Advanced CLI Usage ================== Full Command Reference ---------------------- :: aurora-lint [OPTIONS] [PATH] Arguments: [PATH] Path to the file, directory, or git repository to analyze [default: .] Options: -m, --manifest Path to the rules manifest file [default: rules_templates/rules-all.toml] -i, --interactive Run in interactive terminal UI mode (requires building with `--features tui`) -e, --export Export violations to file (format by extension: .csv, .xlsx, .json, .sarif, .sarif.json) --generate-suppression Generate suppression entry for a specific violation -d, --directories Additional directories to pre-scan for function definitions (repeatable; enables cross-file context) --fail-on-violation Exit with code 1 if any violations are found --fail-on-severity Exit with code 1 if any violation meets or exceeds this severity [Low, Medium, High, Critical] --min-severity Only report violations at or above this severity [Low, Medium, High, Critical] --rules Only report violations from these rules (comma-separated) --exclude Exclude files matching this path glob from analysis (repeatable, e.g. --exclude '**/onelua.c' --exclude 'testes/**') --diff Only analyze modified/new C files (requires git repo) --suppress-file Path to suppress.toml file (auto-detected in project root if not specified; supports [[suppression]] hash entries and [[wildcard]] glob/prefix entries) -I, --include-path Include search paths for resolving #include directives (repeatable; like compiler -I flag) --compile-commands Read include search paths and -D macros from a compile_commands.json (optional; improves cross-file macro/header coverage for projects that already have a compile database) --system-includes Also search the compiler's own built-in system header directories, found by asking it (cc -E -Wp,-v -). Off by default: it spawns a compiler. Usable with or without --compile-commands, which can never contain these paths --report-macro-gaps[=FILE] After the scan, report where the macro-expansion engine was blind: definitions it skipped, #includes it could not resolve, calls it could not attribute. Summary on stdout; =FILE also writes every row as JSON. Never changes a finding -v, --verbose Increase output verbosity (repeat for more detail; -v shows per-rule scanning progress) --save-prescan Save prescan context to a binary cache file (speeds up repeated scans of the same project) --load-prescan Load prescan context from cache instead of re-scanning -d directories -j, --jobs Number of parallel analysis threads (0 = auto-detect, 1 = sequential; default: 0) --detect-relevance Detect categorically-inapplicable rule classes (CON*/WIN*) in PATH and -d directories, then write a relevance-gated manifest with --write-manifest. Does not run an analysis. --write-manifest With --detect-relevance: write the generated manifest here (requires --detect-relevance) -h, --help Print help -V, --version Print version Cross-File Analysis ------------------- Cross-file context comes from a pre-scan that collects function definitions, type declarations, and macro aliases before analysis begins. This significantly reduces false positives from rules like DCL31-C (unused identifiers) and DCL07-C (type mismatches) that would otherwise flag externally-defined symbols. With no ``-d``, the target is pre-scanned itself: every ``.c``/``.h`` file under a directory target, or the file plus the headers beside it for a single-file target. So ``aurora-lint foo.c`` already knows the definitions in ``foo.c``, and ``aurora-lint src/`` knows everything under ``src/``, without naming the target a second time. The ``-d`` / ``--directories`` flag adds context from *outside* the target. Once any ``-d`` is given, the pre-scan covers exactly the ``-d`` directories: name the target too if its own definitions should stay in scope. :: # The target is its own context; nothing more needed aurora-lint /path/to/project # Include additional directories (e.g., shared headers, sibling modules) aurora-lint /path/to/project -d /path/to/project -d /path/to/shared/headers # Multiple -d flags stack — all are pre-scanned before analysis begins aurora-lint src/ -d src/ -d vendor/ -d third_party/ The pre-scan collects: - **Function definitions**: names, parameter counts, return types across all ``.c``/``.h`` files - **Header prototypes**: functions declared in ``.h`` files (public API detection for DCL15-C) - **Function summaries**: null return behavior, freed parameters, no-return annotations, parameter dereferences, return value ranges, parameter pass-through chains - **Call graph**: caller → callee relationships for transitive analysis - **Call-site argument states**: null state of arguments at each call site, aggregated per parameter for inter-procedural null propagation - **Macro constants and aliases**: ``#define`` values for constant evaluation and ``#define SYSTEM system`` patterns for taint tracking - **Struct field types**: struct definitions for type resolution (INT32-C, INT30-C) - **Global constants**: file-scope ``const`` variables for dead-branch elimination - **Global pointer null states**: cross-file ``extern`` pointer tracking (EXP34-C) Using a Compile Database ------------------------ aurora-lint needs no build system: point it at any tree and it works. ``--compile-commands`` is a purely optional upgrade for projects that *already* produce a ``compile_commands.json`` (CMake's ``CMAKE_EXPORT_COMPILE_COMMANDS``, ``bear``, ``compiledb``). Without the flag, nothing changes. :: aurora-lint src/ -d src/ --compile-commands build/compile_commands.json aurora-lint does **not** run a preprocessor. It reads two things out of the database and feeds them to the pre-scan that already exists: - **Include search paths** (``-I``, ``-isystem``, ``-iquote``, ``-idirafter``, resolved against each entry's ``directory``) are appended to any explicit ``-I`` you passed. This lets ``#include`` resolution reach headers the sibling-header scan would never find — notably angle-bracket includes of vendored or out-of-tree headers — so their macros, prototypes and struct types join the cross-file context. - **Command-line macros** (``-D``, minus anything ``-U``'d) are parsed as real ``#define`` directives, so command-line constants fold and function-like ``-D`` macros become expandable exactly like ones written in a header. Because the parse tree is untouched, every finding keeps the source location it always had, and the ``PRE*`` rules still audit macros as written. Reaching the Compiler's Own Headers ----------------------------------- A compile database lists the flags a build *passes*, so it can never contain the compiler's built-in search directories — the compiler already knows them. ``--system-includes`` recovers them by asking the compiler itself (``cc -E -Wp,-v -``) and appending what it reports, lowest priority, after everything you or the database named:: aurora-lint src/ -d src/ --system-includes aurora-lint src/ -d src/ --compile-commands build/compile_commands.json --system-includes With a compile database, each distinct compiler the database names is asked (so a cross-compiled project gets *its* toolchain's directories); without one, the platform default ``cc`` is asked. A compiler that cannot be run — a cross-compiler absent from the analysis host, say — produces a warning and is skipped, never an error. It is off by default for two reasons: it spawns a compiler, which the rest of the analysis pipeline never does, and it is worth being able to measure its effect separately from the database's. What it actually reaches, on a typical Linux host, is the two directories nothing else does. ``/usr/include`` is commonly passed by hand already, but the multiarch directory (``/usr/include/x86_64-linux-gnu``) holds glibc's ``bits/*.h``, so a ```` include from an otherwise-reachable header dead-ends without it; and the gcc internal directory is where ``stdarg.h``, ``stddef.h`` and ``stdbool.h`` *actually* live, since glibc does not ship them. Without the flag, ``NULL``, ``offsetof``, ``va_list`` and ``bool``/``true``/ ``false`` are never harvested from their real definitions. One caveat worth knowing: ``#include`` resolution lets a later header's macro override an earlier one of the same name, so pointing it at the whole system header tree allows a libc macro to win over a project macro that shares its name. That is how any ``-I`` path has always behaved; this flag is simply the first thing to aim it at all of ``/usr/include``. Two properties worth knowing: - **Build flags never override real source.** A ``-D`` only supplies a macro name the scanned tree never defined; a real ``#define`` always wins. - **Paths are absolute and host-specific.** A database generated on another machine or inside a container names directories that may not exist locally. aurora-lint warns when compile-database include paths are missing rather than silently resolving nothing. The compiler's own built-in system header directories are *not* in a compile database (they are implicit), so headers found only in ``/usr/include`` remain out of reach. Seeing Where the Engine Is Blind -------------------------------- Everything above widens what the macro-expansion engine can see; nothing above tells you what it still cannot. Because aurora-lint runs without a preprocessor, the engine makes a handful of silent decisions on every scan — a variadic or ``#``/``##`` macro is skipped, a definition under ``#ifdef _WIN32`` is dropped under the POSIX profile, the first of two ``#ifdef``-selected definitions wins, a header on no search path is never opened, a call to a name nothing defines stays opaque. Each is the right default; each is also a place a defect can hide with no finding saying so. ``--report-macro-gaps`` prints them:: aurora-lint src/ -d src/ -I include/ --report-macro-gaps aurora-lint src/ -d src/ -I include/ --report-macro-gaps=macro-gaps.json The summary comes after the findings, as a per-kind count and up to 25 rows per kind; ``=FILE`` (the ``=`` is required, so the path is never mistaken for the scan target) writes every row as JSON with a ``kind`` from this list: .. list-table:: :header-rows: 1 :widths: 28 72 * - ``kind`` - What it says * - ``variadic-definition``, ``paste-definition``, ``malformed-definition`` - A function-like ``#define`` the collector will never expand, and why. * - ``platform-dead-definition`` - Dropped because its branch never compiles under the assumed POSIX profile (``_MSC_VER``, ``_WIN32``, ``__vxworks``, …). Noise on a POSIX target; the whole story on any other. * - ``ambiguous-definition`` - The same name is defined more than once in one file under conditions the profile cannot settle (``#ifdef WITH_TLS``). The first is used; the row names the others. * - ``conflicting-definition`` - Defined differently in two scanned files — a project ``config.h`` overriding a vendored library's default, say. Scan order decides which one every invocation gets. * - ``unresolved-include`` - An ``#include`` that resolved to no file. One row per header project-wide; a header the ``-d`` pre-scan already walked is not reported even when no ``-I`` places it. * - ``unexpandable-invocation`` - A call to one of the skipped definitions above: known to be a macro, known to be opaque. * - ``arity-mismatch`` - The call's argument count differs from the definition held — usually the sign that a different ``#ifdef`` branch is live at this site. * - ``unknown-callee`` - A call to a name no scanned file defines or declares and that is not a C standard-library function. ``ALL_CAPS`` names are almost certainly macros from an unscanned header; the rest are prototypes it never saw. The rows are the engine's own decisions, not a second opinion, so the report cannot disagree with the analysis it describes; and it is built from a parse-only pass, so it adds no findings and removes none. In CI the useful habit is to keep the JSON as a build artifact and watch the per-kind totals: a jump in ``unknown-callee`` after a dependency bump means a header stopped resolving, and ``conflicting-definition`` rows are worth reading once, since each one is a name whose meaning depends on scan order. File Exclusion -------------- ``--exclude`` drops files matching a path glob from the scan (and from the precision/recall denominator), for checked-in amalgamations, vendored code, test harnesses, or build tooling that isn't part of the shipped product. Repeatable; each occurrence adds one more glob: :: # Drop a single generated/amalgamated file aurora-lint /path/to/project --exclude '**/onelua.c' # Drop a whole subtree aurora-lint /path/to/project --exclude 'tests/**' --exclude 'vendor/**' # Combine multiple globs to scope down to just the shipped product aurora-lint /path/to/repo \ --exclude 'tests/**' --exclude 'docs/**' --exclude 'scripts/**' Globs are matched against each file's path relative to the scan root (same semantics as a project's own ``toolchain.toml`` ``[ignore].paths``, which are merged in automatically if present — ``--exclude`` only needs to name patterns that aren't already covered there). A pattern like ``tests/**`` matches anywhere a ``tests`` directory sits at that depth; use a leading ``**/`` (e.g. ``**/ltests.c``) to match a filename regardless of its directory. .. important:: ``--exclude`` is the only flag that removes files from the scan. ``-d``/``--directories`` does the opposite: it *adds* directories to pre-scan for cross-file context (function summaries, macro aliases, ...) and has no effect on which files are actually analyzed and reported on. Passing ``-d some/dir`` does **not** restrict analysis to ``some/dir`` — if you want a scan restricted to a subset of a larger tree, either point the ``PATH`` argument at that subdirectory or exclude everything else with ``--exclude``. Export Formats -------------- aurora-lint determines the export format from the file extension: =========== =============================================================== Extension Format =========== =============================================================== ``.csv`` Comma-separated values (file, line, column, rule, severity, message) ``.xlsx`` Excel workbook with formatted columns and severity coloring ``.json`` JSON array of violation objects ``.sarif`` `SARIF 2.1.0 `_ for IDE and CI integration =========== =============================================================== :: aurora-lint /path/to/repo --export results.csv aurora-lint /path/to/repo --export results.xlsx aurora-lint /path/to/repo --export results.json aurora-lint /path/to/repo --export results.sarif JSON export produces an array of violation objects, each containing: .. code-block:: json { "file": "src/main.c", "line": 42, "column": 5, "rule_id": "ARR30-C", "severity": "High", "message": "Do not form or use out-of-bounds pointers or array subscripts", "suggestion": "Validate array index before use" } Severity Filtering ------------------ Control which violations are reported and which trigger failure: :: # Only report Medium and above (suppress Low-severity noise) aurora-lint /path/to/repo --min-severity Medium # Fail only on High or Critical (gate CI but still report Medium) aurora-lint /path/to/repo --min-severity Medium --fail-on-severity High # Strict mode: fail on any violation aurora-lint /path/to/repo --fail-on-violation Rule Filtering -------------- Restrict analysis to specific rules: :: # Only check memory and array rules aurora-lint /path/to/repo --rules MEM30-C,MEM31-C,ARR30-C,ARR32-C # Combine with severity and export aurora-lint /path/to/repo --rules STR31-C,STR32-C --min-severity High --export str-results.sarif Diff Mode --------- Analyze only files modified in the current git working tree (staged + unstaged changes vs HEAD): :: # Only analyze changed C files aurora-lint /path/to/repo --diff This is particularly useful in CI pipelines to provide fast feedback on pull requests without scanning the entire codebase. Project-Relevance Detection ---------------------------- For a codebase with no tailored manifest, ``--detect-relevance`` scans PATH (and any ``-d`` directories) for evidence of threading (``pthread``, ````, ``_Atomic``) and Windows API usage, then generates a manifest with the categorically-inapplicable rule classes disabled (``CON*`` if no threading evidence, ``WIN*`` if no Windows API evidence). It never runs an analysis itself — pair it with ``--write-manifest`` to save the generated manifest, then pass that manifest to a normal scan via ``-m``: :: # Generate a relevance-gated manifest for a POSIX-only codebase aurora-lint /path/to/repo --detect-relevance --write-manifest gated-rules.toml # Use it for the actual scan aurora-lint /path/to/repo -m gated-rules.toml Detection is conservative by design: a rule class is disabled only when no evidence of it was found anywhere in the scanned corpus (including resolved ``-I``/``-d`` includes); an unresolved include path leaves the affected rules enabled with an ``# unresolved: ...`` comment rather than guessing. C11/Annex-K-specific rules are detected and annotated but not yet auto-gated (v2 work); see ``docs/design/project-relevance-gating.md`` for the full design and current scope. This is unrelated to the ``conf/realworld/*-rules.toml`` manifests used by the benchmark suite, which remain hand-curated and are never overwritten by this flag. Exit Codes ---------- ====== ========================================================================== Code Meaning ====== ========================================================================== ``0`` Success (no violations, or none meeting the failure threshold) ``1`` Violations found (when ``--fail-on-violation`` or ``--fail-on-severity`` is set) ``2`` Analysis error (invalid path, bad manifest, parse failure) ====== ==========================================================================