fix(native): align edge builder kind filters with JS parity#541
fix(native): align edge builder kind filters with JS parity#541carlos-alm merged 4 commits intomainfrom
Conversation
The Rust edge builder only matched `kind == "class"` when looking up source nodes and targets for extends/implements edges. This caused all `impl Trait for Struct` relationships (and any non-class hierarchy) to be silently dropped — producing 0 implements edges for Rust sources while WASM correctly found 9. Align the three kind filter sets with the JS-side constants: - Source: class, struct, record, enum (was: class only) - Extends targets: class, struct, trait, record (was: class only) - Implements targets: interface, class, trait (was: interface, class) Fixes #530 (partial — implements parity gap) Impact: 1 functions changed, 0 affected
|
Claude finished @carlos-alm's task —— View job PR Review: fix(native): align edge builder kind filters with JS parityRoot Cause AnalysisProblem Being Solved: Native Rust edge builder was using hardcoded Why This Approach: The fix correctly aligns the native Rust implementation with the established JS fallback constants ( Risk Assessment: Low risk - this is purely fixing a bug where native was more restrictive than JS, no existing behavior is broken Code Quality Assessment✅ Excellent execution:
✅ Technical correctness:
✅ Well-documented:
Test Coverage Concerns
Backlog Compliance
Critical ConcernsNone identified. This is a well-executed bug fix. Final Recommendation
Suggestion for completion: Once the native binary is rebuilt and the implements edge count verification passes, this will be a perfect 5-star PR. |
Greptile SummaryThis PR completes a two-step fix for native/WASM parity in hierarchy edge resolution. The actual bug fix (expanding the Key observations:
Confidence Score: 5/5
Important Files Changed
Flowchart%%{init: {'theme': 'neutral'}}%%
flowchart TD
A[ClassInfo entry] --> B{source_row found?\nHIERARCHY_SOURCE_KINDS\nclass · struct · record · enum}
B -- No --> Z[skip]
B -- Yes --> C{cls.extends?}
C -- Yes --> D[Filter target nodes\nEXTENDS_TARGET_KINDS\nclass · struct · trait · record]
D --> E[emit 'extends' edge\nconfidence 1.0]
C -- No --> F{cls.implements?}
E --> F
F -- Yes --> G[Filter target nodes\nIMPLEMENTS_TARGET_KINDS\ninterface · trait · class]
G --> H[emit 'implements' edge\nconfidence 1.0]
F -- No --> Z
Last reviewed commit: "fix: resolve merge c..." |
| .and_then(|v| v.iter().find(|n| { | ||
| n.kind == "class" || n.kind == "struct" || n.kind == "record" || n.kind == "enum" | ||
| })); |
There was a problem hiding this comment.
Consider extracting kind sets as named constants
The three inline kind-filter closures (source kinds, extends-target kinds, implements-target kinds) are now correct, but they're buried in logic with no named anchor. The JS side has this exact problem solved with HIERARCHY_SOURCE_KINDS, EXTENDS_TARGET_KINDS, and IMPLEMENTS_TARGET_KINDS. If the set ever grows (e.g. adding "protocol" for Swift), a developer will need to hunt down all three locations in Rust by hand.
A const slice or a small helper like is_hierarchy_source_kind(kind: &str) -> bool at the top of the file would mirror the JS convention and prevent the drift that caused this bug in the first place.
// At module level (or in a shared constants module)
const HIERARCHY_SOURCE_KINDS: &[&str] = &["class", "struct", "record", "enum"];
const EXTENDS_TARGET_KINDS: &[&str] = &["class", "struct", "trait", "record"];
const IMPLEMENTS_TARGET_KINDS: &[&str] = &["interface", "class", "trait"];Then the filter closures become:
.and_then(|v| v.iter().find(|n| HIERARCHY_SOURCE_KINDS.contains(&n.kind.as_str())));
// ...
.filter(|n| EXTENDS_TARGET_KINDS.contains(&n.kind.as_str()))
// ...
.filter(|n| IMPLEMENTS_TARGET_KINDS.contains(&n.kind.as_str()))This is not blocking for the current fix, just a suggestion to prevent the same regression in the future.
Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!
There was a problem hiding this comment.
Done in c0e1b68. Extracted all three inline kind-filter closures into module-level const slices (HIERARCHY_SOURCE_KINDS, EXTENDS_TARGET_KINDS, IMPLEMENTS_TARGET_KINDS) at the top of edge_builder.rs, mirroring the JS-side convention in build-edges.js. The filter closures now reference these constants instead of duplicating the kind strings inline.
Extract inline kind-filter closures into named constants (HIERARCHY_SOURCE_KINDS, EXTENDS_TARGET_KINDS, IMPLEMENTS_TARGET_KINDS) at module level, mirroring the JS-side convention in build-edges.js. This prevents future native/WASM drift by giving each set a single source of truth.
|
Addressed Greptile P2 feedback: extracted the three inline kind-filter closures into named See c0e1b68. |
Both sides expanded the same kind filters in edge_builder.rs — main used inline comparisons, this branch used named constants. Kept the named constants (HIERARCHY_SOURCE_KINDS, EXTENDS_TARGET_KINDS, IMPLEMENTS_TARGET_KINDS) as they mirror the JS-side convention and prevent future drift.
Summary
kind == "class"when looking up source nodes and targets forextends/implementsedges, causing all non-class hierarchy relationships to be silently droppedimplementsedges missing in v3.3.0 dogfood report (allimpl SymbolExtractor for XxxExtractorfrom Rust source files)build-edges.js:class→class, struct, record, enumclass→class, struct, trait, recordinterface, class→interface, class, traitRoot cause
The Rust
build_call_edges()inedge_builder.rshardcodedn.kind == "class"for source lookups (line 342) and extends targets (line 348), and omitted"trait"from implements targets (line 365). The JS fallback (build-edges.js:403-405) correctly usesHIERARCHY_SOURCE_KINDS,EXTENDS_TARGET_KINDS, andIMPLEMENTS_TARGET_KINDSwhich includestruct,record,enum, andtrait. Since Rust extractors emitstruct(notclass) for Rust structs, the source lookup always failed and both extends and implements edges were silently dropped.Test plan
ClassRelation({name: "JsExtractor", implements: "SymbolExtractor"})buildCallEdges()produces correct implements edge when given proper node kindsimpl Trait for Struct)