You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
C extractor: functions with #ifdef-split braces are dropped from the graph (no Function node) #961
A C function whose body braces are split across #ifdef/#else branches is silently dropped from the code knowledge graph — no Function node is emitted for it, even though its callers remain and reference it as an unresolved name.
Root cause
The extractor parses raw source with tree-sitter without running the preprocessor, so both #ifdef branches are present at once. When a construct like this opens a brace in each branch but shares one close brace:
#ifdef_WIN32if (a() &&b()) { // open brace (branch 1)
...
#elseif (c() &&d()) { // a different open brace (branch 2)#endif
...sharedbody...
} // single shared close brace
tree-sitter sees two if { opens and one } close → unbalanced braces → an ERROR node spanning the function body → no Function node is created. Recovery is localized (neighbouring functions extract fine), so the symptom is one function silently absent.
Reproduction (reproduce-first)
Found via a graph-vs-grep audit: cbm_path_within_root in src/mcp/mcp.c was absent from the graph while its 6 callers were present. Refactoring that specific function to a single unconditional control flow (#960) made it index. The general pattern remains unhandled.
Suggested RED reproduction for the bug-repro suite: a fixture C function with the #ifdef-splits-brace shape, asserting a Function node is produced for it. It should fail on the current extractor and pass once the extractor handles the pattern.
Impact
Low frequency but real: any function using this (uncommon) preprocessor pattern is invisible to search_graph/trace_path/callers, and dead-code / impact analysis will misjudge it. Also a latent correctness gap for graph-based audits.
Possible directions (extractor-side, harder than the per-function workaround)
Detect the ERROR-node-spanning-a-definition case and attempt a light preprocessor pass (pick one #ifdef branch) before re-parsing that region.
Or a targeted brace-rebalancing heuristic for #ifdef/#else/#endif regions inside a definition.
Not urgent; filing so the limitation is tracked rather than rediscovered.
The coverage signal (#963, shipped in #968 / 97c6750) now detects this class automatically. A fresh full self-index flags 44 files as parse_partial; drilling into the two largest flagged regions:
src/mcp/mcp.c [4106-6406]: 49 functions in the source range, 48 recovered into the graph, exactly one missing — cbm_path_within_root (the known instance; its body contains an #ifdef _WIN32 split).
src/ui/http_server.c [574-1868]: 31 functions in range, one missing — handle_process_kill (new instance, same in-function #ifdef _WIN32 platform split).
Smaller recurring classes from the same sweep (containing functions all recovered, only inner declarations lost): static CBM_TLS ... declarations across ~20 pipeline files, and the #ifdef __cplusplus } extern-C close braces in the vendored tree-sitter headers.
Current confirmed loss list for this issue: cbm_path_within_root, handle_process_kill. The full flagged set is queryable any time via index_status or query_graph(graph="missed").
The general pattern is now handled, exactly along the lines the issue suggested: the preprocessed second pass (simplecpp, which already existed for macro-hidden CALLS) evaluates the conditionals so one branch survives and the tree parses clean — and since same-file token lines stay aligned, definitions recovered from it carry their original line numbers. Recovery is deliberately narrow: a def is adopted only when it intersects a raw-tree ERROR region, its name is visible on the corresponding original source line, and its qualified name wasn't already extracted — so clean files never gain phantom or duplicate defs.
Both confirmed instances verified with the fixed binary: handle_process_kill (http_server.c, 580–647) and cbm_path_within_root (mcp.c, 5070–5085) now index with correct ranges — the second no longer only thanks to the #960 refactor. The reproduce-first fixture from this issue (#ifdef-splits-brace shape) is in the suite as the permanent guard, plus an inverse guard that clean #ifdef files gain nothing.
The parse_partial coverage signal (#963) stays the detector for any residual instances of this class — a fresh self-index after this fix is the easiest way to confirm the flagged-region count drops.
Activity
DeusData commentedon Jul 9, 2026
The coverage signal (#963, shipped in #968 / 97c6750) now detects this class automatically. A fresh full self-index flags 44 files as parse_partial; drilling into the two largest flagged regions:
src/mcp/mcp.c[4106-6406]: 49 functions in the source range, 48 recovered into the graph, exactly one missing —cbm_path_within_root(the known instance; its body contains an#ifdef _WIN32split).src/ui/http_server.c[574-1868]: 31 functions in range, one missing —handle_process_kill(new instance, same in-function#ifdef _WIN32platform split).Smaller recurring classes from the same sweep (containing functions all recovered, only inner declarations lost):
static CBM_TLS ...declarations across ~20 pipeline files, and the#ifdef __cplusplus }extern-C close braces in the vendored tree-sitter headers.Current confirmed loss list for this issue:
cbm_path_within_root,handle_process_kill. The full flagged set is queryable any time viaindex_statusorquery_graph(graph="missed").DeusData commentedon Jul 12, 2026
Fixed on main by 65a176e (merged in db330bf, PR #1050), shipping in v0.9.1.
The general pattern is now handled, exactly along the lines the issue suggested: the preprocessed second pass (simplecpp, which already existed for macro-hidden CALLS) evaluates the conditionals so one branch survives and the tree parses clean — and since same-file token lines stay aligned, definitions recovered from it carry their original line numbers. Recovery is deliberately narrow: a def is adopted only when it intersects a raw-tree ERROR region, its name is visible on the corresponding original source line, and its qualified name wasn't already extracted — so clean files never gain phantom or duplicate defs.
Both confirmed instances verified with the fixed binary:
handle_process_kill(http_server.c, 580–647) andcbm_path_within_root(mcp.c, 5070–5085) now index with correct ranges — the second no longer only thanks to the #960 refactor. The reproduce-first fixture from this issue (#ifdef-splits-brace shape) is in the suite as the permanent guard, plus an inverse guard that clean#ifdeffiles gain nothing.The parse_partial coverage signal (#963) stays the detector for any residual instances of this class — a fresh self-index after this fix is the easiest way to confirm the flagged-region count drops.
fix(extract): recover C/C++ defs lost to #ifdef-split brace ERROR reg…
fix(extract): recover C/C++ defs lost to #ifdef-split brace ERROR reg…
fix(extract): one definition per site when the raw and preprocessed p…
fix(cpp): one spelling per template specialization in the preprocesse…