fix: extract INHERITS edges for Go, Kotlin, Swift, Dart, Objective-C - #22
Conversation
Same class of gap as the earlier TypeScript/Rust fix -- none of these five languages captured any inheritance-like relationship at all: - go: struct embedding (Go's closest analog to inheritance -- an anonymous field with no name, just a type). Interface satisfaction is implicit/ structural in Go and can't be determined from syntax alone, so it's intentionally not covered. - kotlin: class inheritance and interface implementation via delegation_specifiers (class X : Y() / class X : Y). - swift: class/struct/enum inheritance and protocol conformance (class X: Y, protocol X: Y). - dart: class extends and implements. - objc: class inheritance (@interface X : Y). Also fixes a separate, deeper bug in objc/entities.scm: class_interface, class_implementation, and protocol_declaration all used a `name:` field that doesn't exist on those grammar nodes (confirmed against tree-sitter-objc's node-types.json -- the class name is an unlabeled positional child, not a field), so every Objective-C class/protocol produced zero symbols at all, not just zero inheritance edges. Without this, the new relations.scm pattern would have nothing to bind to. All five verified against vendored tree-sitter grammar node-types.json rather than guessed. Tested end-to-end against synthetic files covering each pattern -- all five produce correct INHERITS edges (and, for objc, correct symbols too), with no language-pack load warnings. Regression tests added to infigraph-languages/tests/registry_integration.rs for all five languages. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TAXDJyFdnA4BV1U2fxufdC
Unrelated to this PR's INHERITS-extraction change, but the pre-commit hook's fmt/clippy checks block all commits until fixed. - registry_integration.rs: cargo fmt drift - lsp-to-scip/main.rs, vuln/mod.rs: allow question_mark / useless_borrows_in_formatting lints that newly apply because the 'stable' toolchain drifted since this code was written in May, not because the code changed (CI's dtolnay/rust-toolchain@stable also resolves to rustc 1.97.x).
Rewrites the two spots suppressed in the prior commit instead of disabling the lints: - lsp-to-scip/main.rs: replace if-let/else-if-let/else with ? operator - vuln/mod.rs: remove redundant & in format! arg
| ; Interface satisfaction is implicit/structural in Go and can't be | ||
| ; determined from syntax alone, so it isn't captured here. | ||
| (type_spec | ||
| name: (type_identifier) @inherit.child |
There was a problem hiding this comment.
Same issue like PR#21 - Kindly check here also @pradeepmouli
crates/infigraph-languages/languages/go/relations.scm, embedded-field query (type: (type_identifier)):
🔴 bug: field_declaration.type field allows qualified_type (cross-package embed) and generic_type too, per grammar. type Dog struct { pkg.Animal } — very common Go pattern — parses as qualified_type, not type_identifier. Edge silently dropped. Fix: type: (_) @inherit.parent. Add test for package-qualified embedding.
crates/infigraph-languages/languages/dart/relations.scm, L28 and L35 ((type_identifier) inside superclass/interfaces):
🔴 bug: Dart's _type_not_void grammar rule (used by both extends/implements) wraps the type name plus optional type_arguments, and the type name itself can be library-prefixed (pkg.Animal). class Dog extends Animal<T> or extends pkg.Animal won't match a bare type_identifier child. Fix: (type_identifier) → (_) @inherit.parent, or match on the type node directly without descending into a specific child type. Add tests for generic and prefixed base classes.
…Swift/Dart Same shared resolver architecture as the TS/Rust/Python/Java fix (PR #21): extract/relations.rs::resolve_inherit_text recursively decomposes a captured @inherit.parent/@inherit.child node via a per-language decomposition query, threaded through LanguagePack via with_inherit_decompose() on ParserBackend::TreeSitter (boxed from the outset this time to avoid the clippy::large_enum_variant round-trip hit on the first branch). Go: wildcards field_declaration's embedded-field type position (previously narrow type_identifier-only, silently dropping edges for generic/qualified embedded fields e.g. `Base[T]`, `pkg.Animal`); the !name embedded-field- detection guard is untouched. Kotlin/Swift: wildcard the delegation_specifier/ inheritance_specifier capture positions; decomposed via kind+anchor-based queries rather than field-based ones, since both grammars declare no fields at all on their compound user_type node (confirmed empirically, same finding as Java on the other branch). Dart: needs no separate decomposition file at all -- a single fully-anchored pattern (leading anchor picks the first type-field child, skipping the sibling type-arguments blob Dart's grammar produces; trailing anchor requires the identifier to be that node's only child) correctly handles generic, qualified, and plain bases in one shot, verified against all three plus the multi-interface `implements A, B<T>` case. ObjC: confirmed unchanged -- its class_interface.superclass field is grammar-constrained to plain identifier only, no compound shape possible. New regression tests cover the actual compound cases (generic/qualified embedded fields for Go, generic superclasses for Kotlin/Swift, generic extends plus multi-interface implements for Dart). Every pattern verified against the real vendored tree-sitter grammars via a scratch harness before being written here, not assumed from documentation.
# Conflicts: # crates/infigraph-languages/tests/registry_integration.rs
Summary
Follow-up to #21 (TypeScript/Rust
INHERITSextraction). Same class ofgap in five more mainstream languages -- none captured any
inheritance-like relationship at all:
anonymous field with no name, just a type). Interface satisfaction is
implicit/structural in Go and can't be determined from syntax alone, so
it's intentionally not covered.
delegation_specifiers(class X : Y()/class X : Y).(
class X: Y,protocol X: Y).extendsandimplements.@interface X : Y). Also fixes a separate,deeper bug in
objc/entities.scm:class_interface,class_implementation, andprotocol_declarationall used aname:field that doesn't exist on those grammar nodes (confirmed against
tree-sitter-objc'snode-types.json-- the class name is an unlabeledpositional child, not a field), so every Objective-C class/protocol
produced zero symbols at all, not just zero inheritance edges.
Without fixing that too, the new
relations.scmpattern would havenothing to bind to.
Fix
Verified every grammar's exact node structure against the vendored
tree-sitter-*node-types.json(and, for Objective-C, the compiledgrammar.jsonfor positional/unlabeled child structure) rather thanguessing.
Test plan
infigraph-core(268) andinfigraph-languages(16) testsuites pass
infigraph-languages/tests/registry_integration.rs, one perlanguage (the Objective-C test also asserts symbols are extracted,
covering the entities.scm fix)
five produce correct
INHERITSedges (Objective-C additionallyconfirmed to now produce symbols where it previously produced zero)
🤖 Generated with Claude Code
https://claude.ai/code/session_01TAXDJyFdnA4BV1U2fxufdC