fix(csharp): extract members in preprocessor blocks - #2634
Conversation
There was a problem hiding this comment.
Graphify reviewed this change.
Looks safe to merge — no coupling regressions and no blocking issues, checked against the code graph (not a self-assessment).
Formal verification. No changes could be formally verified in this run.
Graphify review — findings
This PR modifies the C# extraction logic in _csharp_extra_walk to handle tree-sitter preproc_* wrapper nodes (from #if/#else/#elif directives) as transparent containers, recursing into their children while preserving the enclosing class's parent_class_nid rather than treating guarded members as file-level. It adds a corresponding test in tests/test_dotnet.py that verifies C# members defined inside preprocessor blocks are extracted and that call edges to them resolve correctly. Note: the changed-symbols list is much broader than the diff shown (which only touches engine.py and test_dotnet.py), so the surface area may extend beyond what's visible here.
No blocking issues surfaced. 3 lower-confidence candidates did not survive cross-model review.
Analysis details — impact, health, verification
Impact & health
Graphify review
Impact — 623 functions depend on the 244 functions this change touches.
Health — grade A; 10 existing hotspot(s) in the area this change touches (pre-existing, not introduced here):
_extract_generic()— 18 callers, 23 callees (high)extract_xaml()— 19 callers, 17 callees (high)extract_objc()— 27 callers, 9 callees (high)extract_js()— 75 callers, 3 callees (high)extract_julia()— 16 callers, 7 callees (high)extract_vue()— 10 callers, 6 callees (high)walk()— 1 callers, 54 callees (high)extract_groovy()— 14 callers, 3 callees (high)- …and 2 more
Verification — 623 functions in the blast radius were not formally verified this run (proofs are advisory here).
Gate & verification
graphify gate
PASS — objectively clean (no health regressions, tests not run — proofs not run this pass (advisory)). Grounded, not self-assessed.
Advisory (not blocking):
- verification_scope: 581 function(s) in the blast radius were not formally verified this run
Formal verification
Could not verify: Could not verify \_csharp\_extra\_walk.
The verifier did not have enough to check \_csharp\_extra\_walk, so it is saying so rather than guessing. No false assurance is the whole point.
Guarantee: No guarantee either way, this is an honest abstention, not a pass.
Note: Reason: not verifiable: all 200 sampled inputs raised on both versions — the function never executed, so 'no divergence' would be vacuous (mostly AttributeError — names the real obstacle, not a sampling gap)
Summary
Fixes #2631.
C# members declared inside
#if,#elif, and#elseblocks were skipped because the generic extractor treated tree-sitter'spreproc_*nodes as scope boundaries. This removed the enclosing class context, so guarded members were not emitted as class methods and call resolution could not target them.Changes
#if/#elseblocks and calls to the active methods.Both conditional branches are represented in the graph. Choosing a build configuration remains outside the extractor's scope.
Validation
uv run pytest tests/test_dotnet.py -q -k preprocessor— passed.uv run pytest tests/test_dotnet.py -q— 42 passed.uv run pytest tests/test_languages.py -q -k csharp— 14 passed.uv run ruff check graphify/extractors/engine.py tests/test_dotnet.py— passed.uv run graphify update .— completed successfully.I also ran
uv run pytest tests/ -q: 4,271 passed and 20 skipped. The 20 failures are unrelated Windows/platform-sensitive tests in Astro IDs, filesystem permissions, gitignore, hooks, image handling, install paths, and watch-path separators; none exercise the C# extractor or this regression.