.. SPDX-License-Identifier: MIT .. _concepts/analysis: =============== Static analysis =============== LIBRA makes static analysis nearly zero-configuration: enable :cmake:variable:`LIBRA_ANALYSIS` and the targets appear automatically for every tool found on ``PATH``. This page explains the design decisions behind that behaviour and documents tool-specific quirks. For the target reference, see :ref:`reference/targets`. How LIBRA configures analysis ============================== LIBRA detects the languages enabled for your CMake project and sets the source files passed to each analysis tool accordingly. This allows tools that only support C or C++ to coexist without causing errors on incompatible source files. An individual target is created per auto-registered source file, giving you per-file warnings and errors — the same granularity as compilation. Analysis Philosophy =================== Analysis is a separate workflow from the usual debug/test cycle and should run in its own preset and build directory. This is not merely a conceptual preference — it is a correctness requirement for the ``fix`` targets (see :ref:`Header files ` below). Running fix targets against a stale build that does not reflect current source can produce incorrect patches. It is (highly) unlikely that a given project will want to require that ALL static analysis checks pass for MR merge, etc. Many of the checks overlap with each other (esp. ``clang-tidy``), so it is up to each project to choose which checks to use. Compilation database ==================== All supported analysis tools use a compilation database (``compile_commands.json``) by default. This is the most reliable source of truth for compiler flags, include paths, and defines, since it reflects exactly what was passed to the compiler for each translation unit. When not using a compilation database, LIBRA walks only the ``INTERFACE_INCLUDE_DIRECTORIES`` and ``INTERFACE_COMPILE_DEFINITIONS`` of the main target — it does not recurse into ``PRIVATE`` dependencies, as doing so would violate CMake's visibility contract. For projects with multiple layers of ``PRIVATE`` deps, this path is unreliable; ``LIBRA_USE_COMPDB=YES`` (the default) is strongly preferred. .. NOTE:: For best results, set the compiler to clang when using clang-based tools such as ``clang-tidy``. If the compiler is not clang, the compilation database may contain flags that clang does not understand, causing analysis to fail even if the project builds cleanly. Using :cmake:variable:`LIBRA_USE_COMPDB` ``=YES`` is strongly recommended if GCC for builds and clang for analysis. .. _concepts/analysis/header-files: Header files ============ Header files are always required to be syntactically self-contained and valid in isolation — they must not rely on a specific include order or on definitions provided by a prior ``#include``. This is enforced directly by LIBRA's analysis machinery. Headers that are included by at least one ``.c/.cpp`` translation unit are covered automatically by a compdb: tools analyze them in the context of the including TU, which has a full compilation database entry and therefore correct flags. Headers that are part of the public API but are *not* included by any ``.c/.cpp`` — for example, in header-only libraries or partially header-only components — would otherwise be invisible to the compilation database. LIBRA handles these by generating a lightweight stub translation unit for each such header at configure time in the build directory: .. code-block:: cpp // Auto-generated by libra -- DO NOT EDIT #include .. note:: .c stubs are generated for .h files and .cpp stubs are generated for .hpp files. Under LIBRA conventions (see :ref:`concepts/project-setup/layout`) .h files are always C code and .hpp files are always C++ code. Each stub is compiled as part of an object library that links privately against the analysis target, giving it a real compilation database entry with the correct include paths and defines. The stub is then registered as an analysis target in the same way as any other source file. If a header fails analysis when checked via its stub, that is a real bug in the header — not a tooling artifact. The fix is to make the header self-contained, not to suppress the error. Stubs are only generated for headers with no real ``.c/.cpp`` coverage. This is a correctness requirement for the ``fix`` analysis variant (e.g., ``fix-clang-tidy``): if a header gains ``.c/.cpp`` coverage after stubs were generated, both the stub TU and the real TU will analyze it and generate identical patches. The second patch application fails because the first has already modified the file, invalidating the expected context. This also enables ``fix`` analysis to be done in parallel. To avoid this, always reconfigure and rebuild before running any ``fix-*`` target. The separate analysis preset makes this natural: ``cmake --preset analyze && cmake --build --preset analyze`` before ``cmake --build --preset analyze --target fix-clang-tidy`` ensures stubs reflect the current source tree. The sequence that produces a conflict: .. tab-set:: .. tab-item:: C #. ``foo.h`` has no ``.c`` coverage → stub generated → stub compiled → compdb entry exists. #. User adds ``#include `` to some ``.c``. #. User runs ``fix-clang-tidy`` **without reconfiguring**. #. Both the stub TU and the real TU analyze ``foo.h`` and generate identical patches. #. Second patch application fails with a conflict error. .. tab-item:: C++ #. ``foo.hpp`` has no ``.cpp`` coverage → stub generated → stub compiled → compdb entry exists. #. User adds ``#include `` to some ``.cpp``. #. User runs ``fix-clang-tidy`` **without reconfiguring**. #. Both the stub TU and the real TU analyze ``foo.hpp`` and generate identical patches. #. Second patch application fails with a conflict error. .. NOTE:: The GNU extension versions of ``-std`` are passed when not using a compilation database, to match LIBRA behavior in standard autodetection.