Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 36 additions & 0 deletions docs/source/components/features.rst
Original file line number Diff line number Diff line change
Expand Up @@ -266,6 +266,42 @@ Features
.. fault:: Sphinx-codelinks hallucinates traceability objects in Bash
:id: FAULT_BASH_2

.. feature:: Preprocessor-Aware C/C++ Extraction
:id: FE_PREPROC

Extract traceability objects only from the C/C++ preprocessor branches that are
actually compiled.

The default tree-sitter parser sees every comment in a file, regardless of conditional
compilation, so a project using ``#ifdef`` variants yields traceability objects from
*all* branches — including branches that are never built. The optional libclang engine
parses each file as a real translation unit, evaluates the preprocessor, and drops
comments inside inactive regions. Objects in active branches keep their original line
numbers, so source links stay accurate without any source transformation.

Key capabilities:

* Evaluation of ``#if`` / ``#ifdef`` / ``#else`` regions to determine active branches
* Per-file compiler flag resolution from a ``compile_commands.json`` compilation database
* Walk-up discovery of the compilation database from the source file
* Standalone parsing of headers, which a compilation database never lists, using
configured ``defines``, ``includes`` and ``std``
* Opt-in via the :ref:`analyse.preprocessor <preprocessor_config>` configuration table
* Graceful behavior when the optional ``libclang`` dependency is absent

See :ref:`preprocessor_engine` for the conceptual overview and header handling.

.. fault:: Traceability objects in inactive preprocessor branches are extracted
:id: FAULT_PREPROC_1

.. fault:: Traceability objects in active preprocessor branches are dropped
:id: FAULT_PREPROC_2

.. fault:: Compiler flags are resolved from the wrong compilation database entry
:id: FAULT_PREPROC_3

The wrong set of ``-D`` macros makes libclang evaluate the wrong branches as active.

.. feature:: Customized comment styles
:id: FE_CMT

Expand Down
1 change: 1 addition & 0 deletions src/sphinx_codelinks/analyse/analyse.py
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,7 @@ def _resolve_preproc_args(self, src_path: Path) -> list[str] | None:
preproc.defines, preproc.includes, preproc.std
)

# @Extract traceability objects with the preprocessor-aware libclang engine, IMPL_PREPROC_1, impl, [FE_PREPROC]
def create_src_objects_libclang(self) -> None:
from sphinx_codelinks.analyse.preproc import ( # noqa: PLC0415
libclang_parser,
Expand Down
3 changes: 3 additions & 0 deletions src/sphinx_codelinks/analyse/preproc/compile_db.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
TU_SOURCE_SUFFIXES = {".c", ".cpp", ".cc", ".cxx"}


# @Discover compile_commands.json by walking up from the source file, IMPL_PREPROC_3, impl, [FE_PREPROC]
def find_compile_db(start: Path, project_root: Path | None = None) -> Path | None:
"""Walk up from ``start`` looking for compile_commands.json.

Expand Down Expand Up @@ -103,6 +104,7 @@ def filter_args(argv: list[str], input_file: str) -> list[str]:
return out


# @Resolve per-file compiler flags from the compilation database, IMPL_PREPROC_4, impl, [FE_PREPROC]
def load_flags_map(db_path: Path) -> dict[Path, list[str]]:
"""Parse compile_commands.json -> {absolute file path: filtered args}."""
entries = json.loads(db_path.read_text())
Expand All @@ -123,6 +125,7 @@ def load_flags_map(db_path: Path) -> dict[Path, list[str]]:
return flags


# @Parse headers standalone from configured defines and includes, IMPL_PREPROC_5, impl, [FE_PREPROC]
def defines_to_args(
defines: list[str], includes: list[Path], std: str = DEFAULT_CPP_STD
) -> list[str]:
Expand Down
1 change: 1 addition & 0 deletions src/sphinx_codelinks/analyse/preproc/libclang_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ def _is_in_skipped(
)


# @Drop comments inside inactive preprocessor branches, IMPL_PREPROC_2, impl, [FE_PREPROC]
def extract_active_comments(file_path: Path, args: list[str]) -> list[LibclangComment]:
"""Return one LibclangComment per ACTIVE comment token in ``file_path``.

Expand Down
1 change: 1 addition & 0 deletions src/sphinx_codelinks/analyse/preproc/loader.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
)


# @Guard the optional libclang dependency with an install hint, IMPL_PREPROC_6, impl, [FE_PREPROC]
def load_clang_cindex() -> Any: # type: ignore[explicit-any]
"""Return the clang.cindex module or raise a clear install error."""
try:
Expand Down
Loading