fix(java): attach enum body members to the enum, not the file - #3674
rajatnagda45 wants to merge 1 commit into
Conversation
A Java enum wraps its fields, constructors and methods in an
enum_body_declarations node, nested under enum_body after the constant
list. The generic walker recurses into a class body keeping the enclosing
type as the parent scope, but an unknown wrapper node resets that scope to
None (an unknown wrapper usually IS a scope boundary). That reset orphaned
every enum method, field and constructor onto the file:
public enum Planet {
EARTH(5.976e+24), MARS(6.421e+23);
private final double mass;
Planet(double mass) { this.mass = mass; }
public double surfaceGravity() { return 6.673e-11 * mass; }
}
Planet became a bare list of constants; surfaceGravity was emitted as a
file-level function and the constructor was dropped entirely.
enum_body_declarations is not a scope of its own - its members belong to
the enum - so recurse through it transparently, preserving the enum as the
parent scope (mirrors the companion_object and ERROR handling already in
the walker). Constructors and methods now hang off the enum via method
edges and their bodies are walked as call-graph scopes.
Adds regression coverage: members attach to the enum and don't leak onto
the file, an inter-method call is captured, and the constant case_of edges
are untouched.
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
Fixes Java enum members being orphaned onto the file: the generic walker now recurses transparently through enum_body_declarations, preserving parent_class_nid so fields, constructors, and methods attach to the enum and calls between enum method bodies land in the call graph. Constant case_of edges are unaffected.
No blocking issues surfaced.
Analysis details — impact, health, verification
Impact & health
Graphify review
Impact — 686 functions depend on the 237 functions this change touches.
Health — this change adds coupling hotspots:
- new:
_extract_generic()— 18 callers, 29 callees - new:
extract_js()— 85 callers, 4 callees - new:
extract_xaml()— 19 callers, 17 callees - new:
extract_objc()— 27 callers, 9 callees - new:
extract_julia()— 17 callers, 7 callees - new:
extract_cpp()— 29 callers, 3 callees - new:
extract_vue()— 10 callers, 7 callees - new:
walk()— 1 callers, 62 callees - …and 9 more — each is listed as a finding
Verification — 686 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: 626 function(s) in the blast radius were not formally verified this run
Test selection
Test selection
23 of 287 test file(s) selected (8%) via static blast radius.
tests/test_astro_extraction.py— impacttests/test_build.py— impacttests/test_cjs_module_extension.py— impacttests/test_cpp_nested_and_cli.py— impacttests/test_dotnet.py— impacttests/test_extract.py— impacttests/test_import_extension_resolution.py— impacttests/test_indirect_dispatch.py— impacttests/test_indirect_dispatch_assign_return.py— impacttests/test_indirect_dispatch_getattr.py— impacttests/test_java_enum_members.py— impact, changed-testtests/test_js_exported_scalar_bindings.py— impacttests/test_languages.py— impacttests/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_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.
Formal verification
Could not verify: Could not verify \_extract\_generic.
The verifier did not have enough to check \_extract\_generic, 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: no capturable inputs from the test suite; property tier: parameter `path` is annotated `Path` — outside the synthesizable primitive/collection set
· 17 more finding(s) on lines outside this diff (see the check run).
|
This one surprised me — enum methods and constructors were landing on the file node instead of the enum. It's the |
|
Shipped in v0.9.65 (on PyPI). Cherry-picked with authorship preserved. Thanks @rajatnagda45! |
Problem
A Java enum wraps its fields, constructors and methods in an
enum_body_declarationsnode, nested underenum_bodyafter the constant list:The generic walker recurses into a class body keeping the enclosing type as the parent scope — but an unrecognised wrapper node resets that scope to
None, because an unknown wrapper usually is a scope boundary.enum_body_declarationstripped exactly that reset, so for:Planetbecame a bare list of constants,surfaceGravity()was emitted as a file-level function, and the constructor was dropped entirely. Enums that carry real behaviour (the classic Java enum-with-methods pattern) lost their whole implementation from the graph, and calls made inside those bodies were mis-attributed.Fix
enum_body_declarationsis not a scope of its own — its members belong to the enum. Recurse through it transparently, preserving the enum as the parent scope. This mirrors the two transparent-wrapper cases already in the walker (companion_objectfor Kotlin, andERRORparse-recovery nodes), which exist for the same reason.Constructors and methods now hang off the enum via
methodedges, and their bodies are walked as call-graph scopes. The node name is Java-grammar-specific, so nothing else changes behaviour.Test
New
tests/test_java_enum_members.py:case_ofedges are untouched.The full test suite (5615 tests) passes.