Skip to content

Static Analysis Tools — Infer, Clang SA and SonarQube Guide

DodaTech Updated 2026-06-23 5 min read

In this tutorial, you'll learn about Static Analysis Tools. We cover key concepts, practical examples, and best practices to help you understand and apply this topic effectively.

Static analysis tools examine source code without executing it, finding bugs, security vulnerabilities, and code quality issues at compile time using techniques from Formal Verification and Abstract Interpretation.

Learning Path

flowchart LR
  A["Abstract Interpretation"] --> B["Static Analysis Tools
Infer, Clang SA"] B --> C["SAT Solvers"] B --> D["Industrial Formal Verification"] style B fill:#f90,color:#fff,stroke-width:2px
â„šī¸ Info

What you'll learn: How to use Infer, Clang Static Analyzer, and SonarQube for automated code review, their underlying analysis engines, and how to integrate them into CI pipelines.

Why it matters: Static analysis catches defects early in development when they are cheapest to fix. {{< ilink="Rust" "Rust's" >}} borrow checker is a static analyzer that prevents memory bugs at compile time.

Real-world use: Doda Browser runs Infer on every pull request to detect null pointer dereferences, resource leaks, and concurrency bugs before they reach production.

Prerequisites

Basic compiler concepts and familiarity with C, Java, or Python.

What Are Static Analysis Tools?

Static analysis tools inspect program code without executing it. They detect potential defects by building an abstract model of the program and checking it against a set of rules. The techniques range from simple pattern matching to full-blown Abstract Interpretation and Symbolic Execution.

Production static analyzers fall into three categories: linters (surface-level), bug finders (deep inter-procedural analysis), and sound analyzers (mathematically guarantee the absence of certain bugs).

Step-by-Step: Using Infer

Infer is Facebook's static analyzer that uses separation logic to reason about heap-allocated data.

Step 1: Install Infer

# Install Infer on Ubuntu
curl -sSL https://github.com/facebook/infer/releases/download/v1.2.0/infer-linux-x86_64-v1.2.0.tar.xz | tar xJ
export PATH=$PATH:$(pwd)/infer-linux-x86_64-v1.2.0/bin

Step 2: Run Infer on a C File

// example.c
#include <stdlib.h>

void leak_example() {
    int *p = (int *)malloc(sizeof(int));
    // p is never freed - Infer catches this
    *p = 42;
}

void null_deref(int *p) {
    if (p != NULL) {
        *p = 10;
    }
    // safe: null check present
}
infer -- clang -c example.c

Expected output:

example.c:5: error: Resource Leak
  memory allocated by malloc() is not deallocated.
  3.   void leak_example() {
  4.       int *p = (int *)malloc(sizeof(int));
  5.       *p = 42;
          ^
  6.   }

Step 3: Run Infer on a Java Project

cd my-java-project
infer -- javac src/*.java

Step-by-Step: Using Clang Static Analyzer

Step 1: Run scan-build

scan-build clang -c program.c

Step 2: Trigger a Bug

#include <stdlib.h>

void use_after_free() {
    int *p = (int *)malloc(sizeof(int));
    *p = 1;
    free(p);
    *p = 2;  // Use-after-free bug
}
scan-build clang -c use_after_free.c

Expected output:

use_after_free.c:7:5: warning: Use of memory after it is freed
    *p = 2;
    ^   ~
1 warning generated.

Step 3: Programmatic Analysis with clang-tidy

import subprocess

def run_clang_tidy(filepath):
    result = subprocess.run(
        ["clang-tidy", filepath, "--", "-std=c11"],
        capture_output=True, text=True
    )
    return result.stdout

warnings = run_clang_tidy("buggy.c")
print(f"Found {len(warnings.split(chr(10)))} warnings")

Expected output:

Found 3 warnings

Step-by-Step: Building a Simple Analyzer

Step 1: Define Rules

class SimpleAnalyzer:
    def __init__(self):
        self.rules = []

    def add_rule(self, name, check_fn):
        self.rules.append((name, check_fn))

    def analyze(self, source_lines):
        findings = []
        for name, check in self.rules:
            result = check(source_lines)
            if result:
                findings.append((name, result))
        return findings

def check_malloc_without_free(source):
    malloc_lines = []
    free_lines = []
    for i, line in enumerate(source):
        if "malloc" in line:
            malloc_lines.append(i)
        if "free" in line:
            free_lines.append(i)
    issues = []
    for ml in malloc_lines:
        if not any(abs(ml - fl) < 20 for fl in free_lines):
            issues.append(f"Line {ml+1}: malloc without matching free")
    return issues

source = [
    "void f() {",
    "    int *p = malloc(4);",
    "    *p = 42;",
    "}]
]
analyzer = SimpleAnalyzer()
analyzer.add_rule("MallocWithoutFree", check_malloc_without_free)
print(analyzer.analyze(source))

Expected output:

[('MallocWithoutFree', ['Line 2: malloc without matching free'])]

Common Errors

1. False Positives Overwhelm Developers

Too many false positives cause alert fatigue. Tune analyzer sensitivity and suppress known false patterns with inline annotations.

2. Not Running in CI

Running analyzers only locally means bugs ship to production. Integrate Infer or Clang SA into CI with blocking on critical findings.

3. Ignoring Inter-Procedural Analysis

Single-function analysis misses bugs that cross function boundaries. Infer and Clang SA perform inter-procedural analysis by default.

4. Over-reliance on Default Configurations

Default configurations detect common patterns but may miss project-specific issues. Customize rules for your codebase's conventions.

5. Confusing Linting with Deep Static Analysis

Linters check surface-level style and simple patterns. Deep analyzers like Infer and Clang SA find complex bugs using Abstract Interpretation and Symbolic Execution.

Practice Questions

Q1: What is the difference between a linter and a static analyzer?

A linter checks style and surface-level patterns. A static analyzer performs deep inter-procedural analysis using formal methods like Abstract Interpretation.

Q2: How does Infer find resource leaks?

Infer uses separation logic to track ownership of heap-allocated memory. If a pointer goes out of scope without being freed or transferred, Infer reports a leak.

Q3: What types of bugs does Clang SA detect?

Use-after-free, memory leaks, null pointer dereferences, division by zero, and dead stores.

Q4: How do static analyzers handle function pointers?

They use pointer analysis to compute a superset of possible targets, then analyze each target's effects on the abstract state.

Q5: Can static analysis replace testing?

No. Static analysis finds different classes of bugs than testing. Testing finds logic errors in concrete executions; static analysis finds potential defects in all executions.

Challenge

Write a Python-based static analyzer that detects unreachable code after a return statement inside a function. Handle conditionals and loops. Test it on a sample C function with multiple return paths.

FAQ

### What is the most widely used static analyzer?

SonarQube is the most widely deployed platform, supporting 30+ languages with thousands of rules for code quality and security.

### How does Infer handle multi-threaded code?

Infer includes a thread-safety analysis that detects data races and deadlocks using a capture-avoiding representation of lock acquisitions.

### Can static analysis find security vulnerabilities?

Yes. Infer detects injection flaws, command injections, and privacy leaks. clang-tidy detects buffer overflows and format string vulnerabilities.

### What is a false positive in static analysis?

A warning reported by the analyzer that does not correspond to an actual defect in the program. All static analyzers produce false positives.

### Does static analysis work on interpreted languages?

Yes. Python has Pyright and Pylance. JavaScript has ESLint and TypeScript's type checker. Ruby has RuboCop. Each uses language-specific analysis techniques.


Built by the developers of Doda Browser, DodaZIP, and Durga Antivirus Pro.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro