diff --git a/crates/infigraph-core/src/vuln/mod.rs b/crates/infigraph-core/src/vuln/mod.rs index 8133dc9..79c3b86 100644 --- a/crates/infigraph-core/src/vuln/mod.rs +++ b/crates/infigraph-core/src/vuln/mod.rs @@ -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, )); } diff --git a/crates/infigraph-languages/languages/rust/relations.scm b/crates/infigraph-languages/languages/rust/relations.scm index 3170364..a2a3cbc 100644 --- a/crates/infigraph-languages/languages/rust/relations.scm +++ b/crates/infigraph-languages/languages/rust/relations.scm @@ -19,3 +19,6 @@ ; Struct inheritance via trait bounds isn't direct, but impl Trait for Type is ; We capture impl blocks as a relationship +(impl_item + trait: (type_identifier) @inherit.parent + type: (type_identifier) @inherit.child) diff --git a/crates/infigraph-languages/languages/typescript/relations.scm b/crates/infigraph-languages/languages/typescript/relations.scm index 378cb0c..ce00a4d 100644 --- a/crates/infigraph-languages/languages/typescript/relations.scm +++ b/crates/infigraph-languages/languages/typescript/relations.scm @@ -13,3 +13,23 @@ ; Import statements (import_statement source: (string) @import.module) + +; Class inheritance: class Foo extends Bar +(class_declaration + name: (type_identifier) @inherit.child + (class_heritage + (extends_clause + value: (identifier) @inherit.parent))) + +; Interface inheritance: interface Foo extends Bar +(interface_declaration + name: (type_identifier) @inherit.child + (extends_type_clause + type: (type_identifier) @inherit.parent)) + +; Class implements: class Foo implements Bar +(class_declaration + name: (type_identifier) @inherit.child + (class_heritage + (implements_clause + (type_identifier) @inherit.parent))) diff --git a/crates/infigraph-languages/tests/registry_integration.rs b/crates/infigraph-languages/tests/registry_integration.rs index 3c54c4c..f538a2e 100644 --- a/crates/infigraph-languages/tests/registry_integration.rs +++ b/crates/infigraph-languages/tests/registry_integration.rs @@ -142,6 +142,34 @@ fn test_extraction_smoke_rust() { assert!(names.contains(&"main"), "should extract main: {names:?}"); } +/// Regression test: rust/relations.scm had a comment describing intent to +/// capture `impl Trait for Type` as an INHERITS relationship, but no actual +/// query pattern was ever written -- every trait impl in every Rust codebase +/// silently produced zero INHERITS edges (confirmed against infigraph's own +/// `impl GraphBackend for KuzuBackend`, which had no corresponding edge). +#[test] +fn test_extraction_rust_impl_trait_produces_inherits_edge() { + use infigraph_core::model::RelationKind; + + let registry = bundled_registry().unwrap(); + let pack = registry.for_extension(".rs").unwrap(); + + let source = b"trait Greet {\n fn hello(&self);\n}\nstruct Person;\nimpl Greet for Person {\n fn hello(&self) {}\n}\n"; + let extraction = infigraph_core::extract::extract_file("test.rs", source, pack) + .expect("extraction should succeed"); + + assert!( + extraction + .relations + .iter() + .any(|r| r.kind == RelationKind::Inherits + && r.source_id.contains("Person") + && r.target_id.contains("Greet")), + "expected an INHERITS edge from Person to Greet, got: {:?}", + extraction.relations + ); +} + #[test] fn test_extraction_smoke_typescript() { let registry = bundled_registry().unwrap(); @@ -162,6 +190,50 @@ fn test_extraction_smoke_typescript() { ); } +/// Regression test: typescript/relations.scm had no inheritance capture at +/// all (only calls + imports), unlike python/relations.scm and +/// javascript/relations.scm which both have working @inherit.child/ +/// @inherit.parent patterns -- every `class X extends Y`, `interface X +/// extends Y`, and `class X implements Y` in every TypeScript codebase +/// silently produced zero INHERITS edges (confirmed against a real repo's +/// `InputProps extends React.ComponentProps<'input'>`, which had no +/// corresponding edge). +#[test] +fn test_extraction_typescript_inheritance_produces_edges() { + use infigraph_core::model::RelationKind; + + let registry = bundled_registry().unwrap(); + let pack = registry.for_extension(".ts").unwrap(); + + let source = b"class Animal {}\nclass Dog extends Animal {}\n\ninterface Shape {}\ninterface Circle extends Shape {}\n\ninterface Drawable {}\nclass Square implements Drawable {}\n"; + let extraction = infigraph_core::extract::extract_file("test.ts", 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("Circle", "Shape"), + "interface extends: {:?}", + extraction.relations + ); + assert!( + has_edge("Square", "Drawable"), + "class implements: {:?}", + extraction.relations + ); +} + #[test] fn test_extraction_smoke_go() { let registry = bundled_registry().unwrap(); diff --git a/crates/lsp-to-scip/src/main.rs b/crates/lsp-to-scip/src/main.rs index 066e543..3f15dae 100644 --- a/crates/lsp-to-scip/src/main.rs +++ b/crates/lsp-to-scip/src/main.rs @@ -502,11 +502,10 @@ fn parse_symbol(v: &serde_json::Value) -> Option { .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 {