From becd0024606a5c6636f83add4380c4b19b236674 Mon Sep 17 00:00:00 2001 From: Tom Hvitved Date: Fri, 6 Feb 2026 10:57:56 +0100 Subject: [PATCH] Rust: Distinguish path resolution expectations from type inference expectations --- .../PathResolutionInlineExpectationsTest.qll | 28 +++++++-- .../library-tests/path-resolution/main.rs | 62 +++++++++---------- .../test/library-tests/path-resolution/my.rs | 2 +- 3 files changed, 55 insertions(+), 37 deletions(-) diff --git a/rust/ql/lib/utils/test/PathResolutionInlineExpectationsTest.qll b/rust/ql/lib/utils/test/PathResolutionInlineExpectationsTest.qll index bde96ace1a4a..bb9f981d07c9 100644 --- a/rust/ql/lib/utils/test/PathResolutionInlineExpectationsTest.qll +++ b/rust/ql/lib/utils/test/PathResolutionInlineExpectationsTest.qll @@ -8,7 +8,7 @@ private import codeql.rust.internal.typeinference.TypeInference private import utils.test.InlineExpectationsTest private module ResolveTest implements TestSig { - string getARelevantTag() { result = "item" } + string getARelevantTag() { result = ["item", "target", "item_no_target"] } private predicate itemAt(ItemNode i, string filepath, int line) { i.getLocation().hasLocationInfo(filepath, _, _, line, _) @@ -36,19 +36,37 @@ private module ResolveTest implements TestSig { ) } + private Item getCallExprTarget(Path p) { + exists(CallExpr ce | + p = ce.getFunction().(PathExpr).getPath() and + result = ce.getResolvedTarget() + ) + } + predicate hasActualResult(Location location, string element, string tag, string value) { - exists(AstNode n | + exists(AstNode n, ItemNode i | not n = any(Path parent).getQualifier() and location = n.getLocation() and n.fromSource() and not location.getFile().getAbsolutePath().matches("%proc_macro.rs") and not n.isFromMacroExpansion() and element = n.toString() and - tag = "item" + item(i, value) | - item(resolvePath(n), value) + i = resolvePath(n) and + ( + if exists(getCallExprTarget(n)) and not i = getCallExprTarget(n) + then tag = "item_no_target" + else tag = "item" + ) or - item(n.(MethodCallExpr).getStaticTarget(), value) + tag = "target" and + ( + i = n.(MethodCallExpr).getStaticTarget() + or + i = getCallExprTarget(n) and + not i = resolvePath(n) + ) ) } } diff --git a/rust/ql/test/library-tests/path-resolution/main.rs b/rust/ql/test/library-tests/path-resolution/main.rs index 773b082cd02a..2b12946a89b6 100644 --- a/rust/ql/test/library-tests/path-resolution/main.rs +++ b/rust/ql/test/library-tests/path-resolution/main.rs @@ -189,18 +189,18 @@ mod m8 { #[rustfmt::skip] pub fn g() { let x = MyStruct {}; // $ item=I50 - MyTrait::f(&x); // $ item=I48 + MyTrait::f(&x); // $ item_no_target=I48 target=I53 MyStruct::f(&x); // $ item=I53 // $ MISSING: item=52 - ::f(&x); // $ item=I48 + ::f(&x); // $ item_no_target=I48 target=I53 let x = MyStruct {}; // $ item=I50 - x.f(); // $ item=I53 + x.f(); // $ target=I53 let x = MyStruct {}; // $ item=I50 - x.g(); // $ item=I54 + x.g(); // $ target=I54 MyStruct::h(&x); // $ item=I74 - x.h(); // $ item=I74 + x.h(); // $ target=I74 } // I55 } // I46 @@ -316,7 +316,7 @@ mod m15 { fn f(&self) { println!("m15::Trait2::f"); // $ item=println Self::g(self); // $ item=I80 - self.g(); // $ item=I80 + self.g(); // $ target=I80 } // Trait2::f } // I82 @@ -331,7 +331,7 @@ mod m15 { fn f(&self, tt: TT) { // $ item=ITT Self::g(self); // $ item=I80 TT::g(&tt); // $ item=I80 - self.g(); // $ item=I80 + self.g(); // $ target=I80 } } // ITrait3 @@ -343,7 +343,7 @@ mod m15 { fn f(&self) { println!("m15::::f"); // $ item=println Self::g(self); // $ item=I77 - self.g(); // $ item=I77 + self.g(); // $ target=I77 } // I76 fn g(&self) { @@ -365,12 +365,12 @@ mod m15 { let x = S; // $ item=I81 ::f(&x); // $ item=Trait1::f + >::f(&x); // $ item_no_target=Trait1::f target=I76 ::f(&x); // $ item=Trait2::f + >::f(&x); // $ item_no_target=Trait2::f target=I78 S::g(&x); // $ item=I77 - x.g(); // $ item=I77 + x.g(); // $ target=I77 } // I75 } @@ -383,12 +383,12 @@ mod m16 { ; // Trait1::f fn g(&self) -> T {// $ item=I84 - self.f() // $ item=Trait1::f + self.f() // $ target=Trait1::f } // I85 fn h(&self) -> T { // $ item=I84 Self::g(&self); // $ item=I85 - self.g() // $ item=I85 + self.g() // $ target=I85 } // I96 const c: T // $ item=I84 @@ -405,7 +405,7 @@ mod m16 { fn f(&self) -> T { // $ item=I87 println!("m16::Trait2::f"); // $ item=println Self::g(self); // $ item=I85 - self.g(); // $ item=I85 + self.g(); // $ target=I85 Self::c // $ item=I94 } // Trait2::f } // I89 @@ -420,7 +420,7 @@ mod m16 { fn f(&self) -> S { // $ item=I90 println!("m16::>::f"); // $ item=println Self::g(self); // $ item=I92 - self.g() // $ item=I92 + self.g() // $ target=I92 } // I91 fn g(&self) -> S { // $ item=I90 @@ -452,16 +452,16 @@ mod m16 { as Trait1< S // $ item=I90 > // $ item=I86 - >::f(&x); // $ item=Trait1::f + >::f(&x); // $ item_no_target=Trait1::f target=I91 // $ item=I89 - >::f(&x); // $ item=Trait2::f + >::f(&x); // $ item_no_target=Trait2::f target=I93 S::g(&x); // $ item=I92 - x.g(); // $ item=I92 + x.g(); // $ target=I92 S::h(&x); // $ item=I96 - x.h(); // $ item=I96 + x.h(); // $ target=I96 S::c; // $ item=I95 :: // $ item=i32 - Assoc(); // $ item=S3i32AssocFunc $ SPURIOUS: item=S3boolAssocFunc (the spurious target is later filtered away by type inference) + Assoc(); // $ item=S3i32AssocFunc item_no_target=S3boolAssocFunc S3:::: // $ item=bool - f1(); // $ item=S3boolf1 $ SPURIOUS: item=S3i32f1 (the spurious target is later filtered away by type inference) + f1(); // $ item=S3boolf1 item_no_target=S3i32f1 S3:::: // $ item=i32 - f1(); // $ item=S3i32f1 $ SPURIOUS: item=S3boolf1 (the spurious target is later filtered away by type inference) + f1(); // $ item=S3i32f1 item_no_target=S3boolf1 } } @@ -628,7 +628,7 @@ mod trait_visibility { { // The `Bar` trait is not visible, but we can refer to its method // with a full path. - m::Bar::a_method(&x); // $ item=Bar::a_method + m::Bar::a_method(&x); // $ item_no_target=Bar::a_method target=X_Bar::a_method } } // trait_visibility::f } @@ -652,7 +652,7 @@ mod m17 { fn g(x: T) { // $ item=I5 - x.f(); // $ item=I1 + x.f(); // $ target=I1 T::f(&x); // $ item=I1 MyTrait::f(&x); // $ item=I1 } // I6 @@ -735,7 +735,7 @@ mod m23 { #[rustfmt::skip] pub fn f() { let x = S; // $ item=I4 - x.f(); // $ item=I5 + x.f(); // $ target=I5 } // I108 } @@ -760,7 +760,7 @@ mod m24 { T: TraitA // $ item=I111 item=I1151 { fn call_trait_a(&self) { - self.data.trait_a_method(); // $ item=I110 + self.data.trait_a_method(); // $ target=I110 } // I116 } @@ -772,8 +772,8 @@ mod m24 { T: TraitA, // $ item=I111 item=I1161 { fn call_both(&self) { - self.data.trait_a_method(); // $ item=I110 - self.data.trait_b_method(); // $ item=I112 + self.data.trait_a_method(); // $ target=I110 + self.data.trait_b_method(); // $ target=I112 } // I117 } @@ -798,8 +798,8 @@ mod m24 { let impl_obj = Implementor; // $ item=I118 let generic = GenericStruct { data: impl_obj }; // $ item=I115 - generic.call_trait_a(); // $ item=I116 - generic.call_both(); // $ item=I117 + generic.call_trait_a(); // $ target=I116 + generic.call_both(); // $ target=I117 // Access through where clause type parameter constraint GenericStruct::::call_trait_a(&generic); // $ item=I116 item=I118 @@ -1132,7 +1132,7 @@ fn main() { zelf::h(); // $ item=I25 z_changed(); // $ item=I122 AStruct::z_on_type(); // $ item=I124 - AStruct {}.z_on_instance(); // $ item=I123 item=I125 + AStruct {}.z_on_instance(); // $ item=I123 target=I125 impl_with_attribute_macro::test(); // $ item=impl_with_attribute_macro::test patterns::test(); // $ item=patterns::test } diff --git a/rust/ql/test/library-tests/path-resolution/my.rs b/rust/ql/test/library-tests/path-resolution/my.rs index 612e40d493f9..c506ba3c196a 100644 --- a/rust/ql/test/library-tests/path-resolution/my.rs +++ b/rust/ql/test/library-tests/path-resolution/my.rs @@ -30,7 +30,7 @@ fn int_div( ) -> Result // $ item=my::Result $ item=i32 { if y == 0 { - return Err("Div by zero".to_string()); // $ item=Err item=to_string + return Err("Div by zero".to_string()); // $ item=Err target=to_string } Ok(x / y) // $ item=Ok }