fix(cpp): treat a member-function declaration as a method, not a field - #3963
rajatnagda45 wants to merge 1 commit into
Conversation
Inside a class body a `virtual double area() = 0;` (pure virtual), a `virtual void draw();` and a plain prototype `int run();` parse as a `field_declaration` whose declarator is a `function_declarator`. The field branch recognised that shape only to skip emitting a type reference — it still emitted a node for every declarator as a DATA MEMBER (a bare `area` label with a `defines`/field edge). So a pure-virtual interface class had no method nodes, only phantom fields; a header-declared method and its out-of-line/overriding definition did not share one node (`area` field vs `.area()` method); and a call to such a method resolved to a field-shaped node. Fix: in the field-declaration branch, emit a function_declarator with the same shape a defined method uses — a `.name()` label, a `method` edge, and callable — so the declaration and its definition collapse to one method node (parity with the Java/TypeScript/Scala abstract-method contract). A genuine data member still gets its `defines` field edge. This corrects the label of every header-declared method, so a few existing cross-file tests that asserted the old bare `bar`/`run` field label now assert `.bar()`/`.run()`; the resolved call edges (source, target, confidence) are unchanged, and the decl/def merge still produces exactly one node carrying the definition site. Added tests/test_cpp_method_declarations.py.
|
Thanks for the pull request, @rajatnagda45. A maintainer will review it soon. Want to talk it through while it is in review? Come join us on our Discord server. For longer-form discussion there is also GitHub Discussions. A couple of things that speed up review: make sure the test suite passes on Python 3.10 and 3.13, and that the change keeps extraction deterministic. |
There was a problem hiding this comment.
Graphify reviewed this change.
Worth a look — the grounded gate found no coupling regressions or blocking issues, but 1 advisory finding(s) below merit a look before merge.
Graphify review — findings
Fixes C++ member-function declarations so that prototypes, virtual f(); and pure-virtual virtual f() = 0; inside a class body (including pointer/reference-returning ones) are emitted as callable .name() nodes with a method edge instead of bare-named defines fields. Pure-virtual interfaces now get real method nodes, and a declaration shares one node with its out-of-line or in-class definition, so calls resolve to .bar() rather than a phantom bar field. Genuine data members still get their defines field edge, and existing cross-file call tests now expect the .name() labels.
Worth a look
- C++ function-pointer data members are classified as methods —
graphify/extractors/engine.py:5149· Escalate · medium · 2 independent checks- agreed by 2 of 2 members but NOT verified (no proof, no reproducing execution) — consensus is not a verdict; needs human review
Analysis details — impact, health, verification
Impact & health
Graphify review
Impact — 1128 functions depend on the 871 functions this change touches.
Health — this change adds coupling hotspots:
- new:
_extract_generic()— 18 callers, 29 callees - new:
extract_js()— 87 callers, 4 callees - new:
extract_xaml()— 19 callers, 17 callees - new:
extract_objc()— 27 callers, 9 callees - new:
extract_julia()— 19 callers, 7 callees - new:
extract_cpp()— 31 callers, 3 callees - new:
extract_vue()— 10 callers, 7 callees - new:
walk()— 1 callers, 66 callees - …and 9 more — each is listed as a finding
Verification — 1128 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: 1082 function(s) in the blast radius were not formally verified this run
Test selection
Test selection
27 of 313 test file(s) selected (9%) via static blast radius.
tests/test_astro_extraction.py— impacttests/test_build.py— impacttests/test_cjs_module_extension.py— impacttests/test_cpp_method_declarations.py— impact, changed-testtests/test_cpp_nested_and_cli.py— impacttests/test_cpp_objc_cross_file_calls.py— impact, changed-testtests/test_dotnet.py— impacttests/test_extract.py— impacttests/test_extract_php_closures.py— impacttests/test_import_extension_resolution.py— impacttests/test_indirect_call_block_scoped_shadow.py— impacttests/test_indirect_dispatch.py— impacttests/test_indirect_dispatch_assign_return.py— impacttests/test_indirect_dispatch_getattr.py— impacttests/test_js_exported_scalar_bindings.py— impacttests/test_languages.py— impact, changed-testtests/test_multilang.py— impacttests/test_python_underscore_resolution.py— impacttests/test_rationale.py— impacttests/test_ruby_resolution.py— impacttests/test_scala_self_type.py— impacttests/test_swift_computed_properties.py— impacttests/test_swift_protocol_requirements.py— impacttests/test_trailing_newline_not_a_syntax_error.py— impacttests/test_ts_new_expression_calls.py— impacttests/test_typescript_module_extensions.py— impacttests/test_vue_extraction.py— impact
Selection is safe under the controlled-regression assumption; always-run tests + a periodic full run are the backstops. Advisory — it never changes the check verdict.
· 17 more finding(s) on lines outside this diff (see the check run).
Problem
Inside a class body a
virtual double area() = 0;(pure virtual), avirtual void draw();and a plain prototypeint run();all parse as afield_declarationwhose declarator is afunction_declarator. The field branch recognised that shape only to skip emitting a type reference — it still emitted a node for every declarator as a data member (a barearealabel with adefines/field edge).Consequences:
areafield vs.area()method);Implementation
In the field-declaration branch, emit a
function_declaratorwith the same shape a defined method uses — a.name()label, amethodedge, and callable — so the declaration and its definition collapse to one method node (parity with the Java/TypeScript/Scala abstract-method contract). A genuine data member still gets itsdefinesfield edge.Verification
tests/test_cpp_method_declarations.py(new): pure-virtual / declared-virtual / prototype members become method nodes; a data member stays a field; the decl/def merge keeps one node and a call through it resolves. All fail onv8, pass with the fix.bar/runfield label now expect.bar()/.run()(test_cpp_objc_cross_file_calls,test_languages). The resolved call edges (source, target, confidence) are unchanged, and the decl/def merge still produces exactly one node carrying the definition site.uv run pytest tests/— full suite green (6175 passed, 15 skipped).uv run ruff check .clean;uv run pyrightadds zero new errors.