System Header Mutation Safety¶
Background¶
tests/nonsmoke/functional/roseTests/astInterfaceTests/interfaceFunctionCoverage.C
walks the entire AST and repeatedly calls SageInterface helpers. After the
parent/symbol-table repair work, those helpers now run on every declaration,
including the libstdc++ nodes that Clang/GCC synthesize inside headers such as
/usr/include/c++/14/bits/basic_string.h. Several of the helpers
(removeStatement, movePreprocessingInfo, etc.) remove the target statement
from its scope before re‑inserting it, assuming the scope “owns” the node. When
the visitor mutates a declaration that actually belongs to a system header, the
scope’s StatementListInsertChild() no longer finds the original node and the
test aborts.
Short-Term Fix¶
We hardened the code in two places:
- The coverage visitor now bails out immediately for nodes whose
Sg_File_Infopoints at/usr/include/.... This keeps the test focused on user-owned code while still exercising the SageInterface entry points on everything else. StatementListInsertChild()logs the missing-target details and simply returns when the target originated in a system header. That prevents the new parent/symbol cleanup from aborting the entire run when a third-party header (libstdc++, libc, etc.) contains local classes or helper declarations.
The combination restores CI parity between GCC 12 (ARM dev box) and GCC 14 (Ubuntu 24.04 CI) without touching the actual system headers.
Long-Term Direction¶
The sustainable fix is to clone any declaration that a SageInterface mutator is about to move/remove when that declaration lives outside the project. Implementing that cleanly requires:
- a reusable “is this node user-owned?” predicate based on
Sg_File_Infoand theSgProjectfile list (no hard-coded/usr/includechecks), - a helper that deep-copies the node, relocates it into a project-owned scope, and rewires symbols/parents before the mutator runs, and
- an opt-out for advanced users who intentionally edit system headers.
That work is larger (roughly a week of design, implementation, and regression testing) and will increase compile-time/memory because of the extra copies, but once it lands we can drop the “skip system headers” guard entirely.
Known Trade-offs¶
- Coverage gap: we no longer exercise SageInterface helpers on libstdc++ nodes. The tests still provide the same coverage for user-facing code, but they intentionally avoid mutating system headers.
- Intentional mutations: projects that want to edit
/usr/includedeclarations can still do so via their own translators. The guard only affects the generic coverage visitor and the low-level insert helper—it doesn’t stop user code from calling the APIs directly.