Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion crates/infigraph-core/src/vuln/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -421,7 +421,7 @@ pub fn format_table(report: &VulnReport) -> String {
truncate_str(&f.dep_name, 20),
truncate_str(&f.dep_version, 12),
truncate_str(&f.vuln_id, 18),
&f.severity,
f.severity,
summary_truncated,
));
}
Expand Down
14 changes: 14 additions & 0 deletions crates/infigraph-languages/languages/dart/relations.scm
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,17 @@
; Import directives
(import_specification
uri: (uri) @import.module)

; Class extends: class Dog extends Animal
(class_declaration
name: (identifier) @inherit.child
superclass: (superclass
(type
(type_identifier) @inherit.parent)))

; Class implements: class Foo implements Bar
(class_declaration
name: (identifier) @inherit.child
interfaces: (interfaces
(type
(type_identifier) @inherit.parent)))
12 changes: 12 additions & 0 deletions crates/infigraph-languages/languages/go/relations.scm
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,15 @@
(call_expression
function: (selector_expression
field: (field_identifier) @spawns.target))) @spawns.site

; Struct embedding: type Dog struct { Animal } -- Go's closest analog to
; inheritance. An embedded (anonymous) field has no name, only a type.
; 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

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

type: (struct_type
(field_declaration_list
(field_declaration
!name
type: (type_identifier) @inherit.parent))))
10 changes: 10 additions & 0 deletions crates/infigraph-languages/languages/kotlin/relations.scm
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,13 @@
; Import declarations
(import
(identifier) @import.module)

; Class inheritance / interface implementation: class Dog : Animal() or class Foo : Bar
(class_declaration
name: (identifier) @inherit.child
(delegation_specifiers
(delegation_specifier
[
(user_type (identifier) @inherit.parent)
(constructor_invocation (user_type (identifier) @inherit.parent))
])))
13 changes: 8 additions & 5 deletions crates/infigraph-languages/languages/objc/entities.scm
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,19 @@
(identifier) @method.name) @method.def

; Class interface declarations
; class_interface has no "name" field (its grammar rule injects the
; identifier positionally, right after @interface) -- name: (identifier)
; never matched anything, so no class symbols were ever extracted.
(class_interface
name: (identifier) @class.name) @class.def
(identifier) @class.name) @class.def

; Class implementation
; Class implementation (same field-less structure as class_interface)
(class_implementation
name: (identifier) @class.name) @class.def
(identifier) @class.name) @class.def

; Protocol declarations
; Protocol declarations (same field-less structure)
(protocol_declaration
name: (identifier) @class.name) @class.def
(identifier) @class.name) @class.def

; Category interface (ObjC categories use class_interface with category field)
; Captured by the class_interface rule above
5 changes: 5 additions & 0 deletions crates/infigraph-languages/languages/objc/relations.scm
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,8 @@
; Module import (@import)
(module_import
path: (identifier) @import.module)

; Class inheritance: @interface Foo : Bar
(class_interface
(identifier) @inherit.child
superclass: (identifier) @inherit.parent)
14 changes: 14 additions & 0 deletions crates/infigraph-languages/languages/swift/relations.scm
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,17 @@
; Import declarations
(import_declaration
(identifier) @import.module)

; Class/struct/enum/protocol inheritance or protocol conformance:
; class Foo: Bar, protocol Foo: Bar
(class_declaration
name: (type_identifier) @inherit.child
(inheritance_specifier
inherits_from: (user_type
(type_identifier) @inherit.parent)))

(protocol_declaration
name: (type_identifier) @inherit.child
(inheritance_specifier
inherits_from: (user_type
(type_identifier) @inherit.parent)))
163 changes: 163 additions & 0 deletions crates/infigraph-languages/tests/registry_integration.rs
Original file line number Diff line number Diff line change
Expand Up @@ -193,3 +193,166 @@ fn test_extraction_smoke_java() {
);
assert!(names.contains(&"add"), "should extract add: {names:?}");
}

/// Regression test: Go has no `extends`/`implements` keywords, but struct
/// embedding (an anonymous field with no name, just a type) is its closest
/// analog to inheritance and wasn't captured at all. Interface satisfaction
/// in Go is implicit/structural and can't be determined from syntax alone,
/// so it's intentionally not covered here.
#[test]
fn test_extraction_go_struct_embedding_produces_inherits_edge() {
use infigraph_core::model::RelationKind;

let registry = bundled_registry().unwrap();
let pack = registry.for_extension(".go").unwrap();

let source = b"package main\ntype Animal struct {\n\tName string\n}\ntype Dog struct {\n\tAnimal\n\tBreed string\n}\n";
let extraction = infigraph_core::extract::extract_file("test.go", source, pack)
.expect("extraction should succeed");

assert!(
extraction
.relations
.iter()
.any(|r| r.kind == RelationKind::Inherits
&& r.source_id.contains("Dog")
&& r.target_id.contains("Animal")),
"expected an INHERITS edge from Dog to Animal (embedded field), got: {:?}",
extraction.relations
);
}

/// Regression test: kotlin/relations.scm had no inheritance capture at all.
#[test]
fn test_extraction_kotlin_inheritance_produces_edges() {
use infigraph_core::model::RelationKind;

let registry = bundled_registry().unwrap();
let pack = registry.for_extension(".kt").unwrap();

let source =
b"open class Animal\nclass Dog : Animal()\n\ninterface Shape\nclass Circle : Shape\n";
let extraction = infigraph_core::extract::extract_file("Test.kt", source, pack)
.expect("extraction should succeed");

let has_edge = |child: &str, parent: &str| {
extraction.relations.iter().any(|r| {
r.kind == RelationKind::Inherits
&& r.source_id.contains(child)
&& r.target_id.contains(parent)
})
};

assert!(
has_edge("Dog", "Animal"),
"class inheritance: {:?}",
extraction.relations
);
assert!(
has_edge("Circle", "Shape"),
"interface implementation: {:?}",
extraction.relations
);
}

/// Regression test: swift/relations.scm had no inheritance capture at all.
#[test]
fn test_extraction_swift_inheritance_produces_edges() {
use infigraph_core::model::RelationKind;

let registry = bundled_registry().unwrap();
let pack = registry.for_extension(".swift").unwrap();

let source =
b"class Animal {}\nclass Dog: Animal {}\n\nprotocol Shape {}\nprotocol Circle: Shape {}\n";
let extraction = infigraph_core::extract::extract_file("Test.swift", source, pack)
.expect("extraction should succeed");

let has_edge = |child: &str, parent: &str| {
extraction.relations.iter().any(|r| {
r.kind == RelationKind::Inherits
&& r.source_id.contains(child)
&& r.target_id.contains(parent)
})
};

assert!(
has_edge("Dog", "Animal"),
"class inheritance: {:?}",
extraction.relations
);
assert!(
has_edge("Circle", "Shape"),
"protocol inheritance: {:?}",
extraction.relations
);
}

/// Regression test: dart/relations.scm had no inheritance capture at all.
#[test]
fn test_extraction_dart_inheritance_produces_edges() {
use infigraph_core::model::RelationKind;

let registry = bundled_registry().unwrap();
let pack = registry.for_extension(".dart").unwrap();

let source = b"class Animal {}\nclass Dog extends Animal {}\n\nclass Drawable {}\nclass Square implements Drawable {}\n";
let extraction = infigraph_core::extract::extract_file("test.dart", source, pack)
.expect("extraction should succeed");

let has_edge = |child: &str, parent: &str| {
extraction.relations.iter().any(|r| {
r.kind == RelationKind::Inherits
&& r.source_id.contains(child)
&& r.target_id.contains(parent)
})
};

assert!(
has_edge("Dog", "Animal"),
"class extends: {:?}",
extraction.relations
);
assert!(
has_edge("Square", "Drawable"),
"class implements: {:?}",
extraction.relations
);
}

/// Regression test: objc/relations.scm had no inheritance capture, AND
/// objc/entities.scm's class_interface/class_implementation/
/// protocol_declaration patterns used a `name:` field that doesn't exist on
/// those grammar nodes (verified 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.
#[test]
fn test_extraction_objc_produces_symbols_and_inherits_edge() {
use infigraph_core::model::RelationKind;

let registry = bundled_registry().unwrap();
let pack = registry.for_extension(".m").unwrap();

let source = b"@interface Animal\n@end\n@interface Dog : Animal\n@end\n";
let extraction = infigraph_core::extract::extract_file("Test.m", source, pack)
.expect("extraction should succeed");

let names: Vec<&str> = extraction.symbols.iter().map(|s| s.name.as_str()).collect();
assert!(
names.contains(&"Animal"),
"should extract Animal: {names:?}"
);
assert!(names.contains(&"Dog"), "should extract Dog: {names:?}");

assert!(
extraction
.relations
.iter()
.any(|r| r.kind == RelationKind::Inherits
&& r.source_id.contains("Dog")
&& r.target_id.contains("Animal")),
"expected an INHERITS edge from Dog to Animal, got: {:?}",
extraction.relations
);
}
5 changes: 2 additions & 3 deletions crates/lsp-to-scip/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -502,11 +502,10 @@ fn parse_symbol(v: &serde_json::Value) -> Option<LspSymbol> {
.and_then(parse_range)
.unwrap_or(range.clone());
(range, sel)
} else if let Some(loc) = v.get("location") {
} else {
let loc = v.get("location")?;
let range = parse_range(&loc["range"])?;
(range.clone(), range)
} else {
return None;
};

Some(LspSymbol {
Expand Down
Loading