From 985ee926980acf86327ecc7e958b119116f6b6bc Mon Sep 17 00:00:00 2001 From: Tom Hvitved Date: Mon, 10 Feb 2025 13:49:12 +0100 Subject: [PATCH 1/2] Rust: Add more path resolution tests --- .../CONSISTENCY/AstConsistency.expected | 20 ++++ .../library-tests/path-resolution/main.rs | 69 +++++++++++ .../path-resolution/path-resolution.expected | 113 ++++++++++++------ 3 files changed, 163 insertions(+), 39 deletions(-) diff --git a/rust/ql/test/library-tests/path-resolution/CONSISTENCY/AstConsistency.expected b/rust/ql/test/library-tests/path-resolution/CONSISTENCY/AstConsistency.expected index 9d4e175192b4..48a22466d889 100644 --- a/rust/ql/test/library-tests/path-resolution/CONSISTENCY/AstConsistency.expected +++ b/rust/ql/test/library-tests/path-resolution/CONSISTENCY/AstConsistency.expected @@ -1,3 +1,23 @@ multiplePathResolutions | main.rs:118:9:118:9 | f | main.rs:104:5:106:5 | fn f | | main.rs:118:9:118:9 | f | main.rs:110:5:112:5 | fn f | +| main.rs:221:17:221:19 | Foo | main.rs:216:5:216:21 | struct Foo | +| main.rs:221:17:221:19 | Foo | main.rs:218:5:218:15 | fn Foo | +| main.rs:222:9:222:11 | Foo | main.rs:216:5:216:21 | struct Foo | +| main.rs:222:9:222:11 | Foo | main.rs:218:5:218:15 | fn Foo | +| main.rs:236:13:236:15 | Foo | main.rs:216:5:216:21 | struct Foo | +| main.rs:236:13:236:15 | Foo | main.rs:218:5:218:15 | fn Foo | +| main.rs:237:17:237:22 | FooBar | main.rs:228:9:228:17 | FooBar | +| main.rs:237:17:237:22 | FooBar | main.rs:233:5:233:18 | fn FooBar | +| main.rs:238:17:238:22 | FooBar | main.rs:228:9:228:17 | FooBar | +| main.rs:238:17:238:22 | FooBar | main.rs:233:5:233:18 | fn FooBar | +| main.rs:273:13:273:25 | ...::f | main.rs:269:5:269:17 | fn f | +| main.rs:273:13:273:25 | ...::f | main.rs:269:19:270:19 | struct f | +| main.rs:276:17:276:17 | f | main.rs:269:5:269:17 | fn f | +| main.rs:276:17:276:17 | f | main.rs:269:19:270:19 | struct f | +| main.rs:277:21:277:21 | f | main.rs:269:5:269:17 | fn f | +| main.rs:277:21:277:21 | f | main.rs:269:19:270:19 | struct f | +| main.rs:278:13:278:13 | f | main.rs:269:5:269:17 | fn f | +| main.rs:278:13:278:13 | f | main.rs:269:19:270:19 | struct f | +| main.rs:301:5:301:10 | ...::f | main.rs:220:5:223:5 | fn f | +| main.rs:301:5:301:10 | ...::f | main.rs:225:5:225:12 | mod f | diff --git a/rust/ql/test/library-tests/path-resolution/main.rs b/rust/ql/test/library-tests/path-resolution/main.rs index d302143ad5a4..123c1e55baff 100644 --- a/rust/ql/test/library-tests/path-resolution/main.rs +++ b/rust/ql/test/library-tests/path-resolution/main.rs @@ -212,6 +212,74 @@ mod m10 { } } +mod m11 { + pub struct Foo {} // I61 + + fn Foo() {} // I62 + + pub fn f() { + let _ = Foo {}; // $ item=I61 $ SPURIOUS: item=I62 + Foo(); // $ item=I62 $ SPURIOUS: item=I61 + } // I63 + + mod f {} // I66 + + pub enum Bar { + FooBar {}, // I64 + } // I65 + + use Bar::FooBar; // $ item=I64 + + fn FooBar() {} // I65 + + #[rustfmt::skip] + fn g(x: Foo) { // $ item=I61 $ SPURIOUS: item=I62 + let _ = FooBar {}; // $ item=I64 $ SPURIOUS: item=I65 + let _ = FooBar(); // $ item=I65 $ SPURIOUS: item=I64 + } + + struct S; // I67 + enum E { + C, // I68 + } + + use E::C; // $ item=I68 + + fn h() { + let _ = S; // $ item=I67 + let _ = C; // $ item=I68 + } +} + +mod m12 { + #[rustfmt::skip] + trait MyParamTrait< + T // I69 + > { + type AssociatedType; // I70 + + fn f( + &self, + x: T // $ MISSING: item=I69 + ) -> Self::AssociatedType; // $ MISSING: item=I70 + } +} + +mod m13 { + pub fn f() {} // I71 + pub struct f {} // I72 + + mod m14 { + use crate::m13::f; // $ item=I71 item=I72 + + #[rustfmt::skip] + fn g(x: f) { // $ item=I72 $ SPUROUS: item=I71 + let _ = f {}; // $ item=I72 $ SPUROUS: item=I71 + f(); // $ item=I71 $ SPUROUS: item=I72 + } + } +} + fn main() { my::nested::nested1::nested2::f(); // $ item=I4 my::f(); // $ item=I38 @@ -230,4 +298,5 @@ fn main() { m7::f(); // $ item=I45 m8::g(); // $ item=I55 m9::f(); // $ item=I57 + m11::f(); // $ item=I63 $ SPUROUS: item=I66 } diff --git a/rust/ql/test/library-tests/path-resolution/path-resolution.expected b/rust/ql/test/library-tests/path-resolution/path-resolution.expected index 746a72883756..c2a59f7083bc 100644 --- a/rust/ql/test/library-tests/path-resolution/path-resolution.expected +++ b/rust/ql/test/library-tests/path-resolution/path-resolution.expected @@ -13,6 +13,11 @@ mod | main.rs:139:1:182:1 | mod m8 | | main.rs:184:1:192:1 | mod m9 | | main.rs:194:1:213:1 | mod m10 | +| main.rs:215:1:252:1 | mod m11 | +| main.rs:225:5:225:12 | mod f | +| main.rs:254:1:266:1 | mod m12 | +| main.rs:268:1:281:1 | mod m13 | +| main.rs:272:5:280:5 | mod m14 | | my2/mod.rs:1:1:1:16 | mod nested2 | | my2/nested2.rs:1:1:11:1 | mod nested3 | | my2/nested2.rs:2:5:10:5 | mod nested4 | @@ -38,7 +43,7 @@ resolvePath | main.rs:30:17:30:21 | super | main.rs:18:5:36:5 | mod m2 | | main.rs:30:17:30:24 | ...::f | main.rs:19:9:21:9 | fn f | | main.rs:33:17:33:17 | f | main.rs:19:9:21:9 | fn f | -| main.rs:40:9:40:13 | super | main.rs:1:1:233:2 | SourceFile | +| main.rs:40:9:40:13 | super | main.rs:1:1:302:2 | SourceFile | | main.rs:40:9:40:17 | ...::m1 | main.rs:13:1:37:1 | mod m1 | | main.rs:40:9:40:21 | ...::m2 | main.rs:18:5:36:5 | mod m2 | | main.rs:40:9:40:24 | ...::g | main.rs:23:9:27:9 | fn g | @@ -50,7 +55,7 @@ resolvePath | main.rs:61:17:61:19 | Foo | main.rs:59:9:59:21 | struct Foo | | main.rs:64:13:64:15 | Foo | main.rs:53:5:53:17 | struct Foo | | main.rs:66:5:66:5 | f | main.rs:55:5:62:5 | fn f | -| main.rs:68:5:68:8 | self | main.rs:1:1:233:2 | SourceFile | +| main.rs:68:5:68:8 | self | main.rs:1:1:302:2 | SourceFile | | main.rs:68:5:68:11 | ...::i | main.rs:71:1:83:1 | fn i | | main.rs:74:13:74:15 | Foo | main.rs:48:1:48:13 | struct Foo | | main.rs:81:17:81:19 | Foo | main.rs:77:9:79:9 | struct Foo | @@ -64,7 +69,7 @@ resolvePath | main.rs:87:57:87:66 | ...::g | my2/nested2.rs:7:9:9:9 | fn g | | main.rs:87:80:87:86 | nested4 | my2/nested2.rs:2:5:10:5 | mod nested4 | | main.rs:100:5:100:22 | f_defined_in_macro | main.rs:99:18:99:42 | fn f_defined_in_macro | -| main.rs:117:13:117:17 | super | main.rs:1:1:233:2 | SourceFile | +| main.rs:117:13:117:17 | super | main.rs:1:1:302:2 | SourceFile | | main.rs:117:13:117:21 | ...::m5 | main.rs:103:1:107:1 | mod m5 | | main.rs:118:9:118:9 | f | main.rs:104:5:106:5 | fn f | | main.rs:118:9:118:9 | f | main.rs:110:5:112:5 | fn f | @@ -99,42 +104,72 @@ resolvePath | main.rs:207:7:209:7 | MyStruct::<...> | main.rs:195:5:201:5 | struct MyStruct | | main.rs:208:9:208:9 | T | main.rs:204:14:204:14 | TypeParam | | main.rs:211:9:211:16 | MyStruct | main.rs:195:5:201:5 | struct MyStruct | -| main.rs:216:5:216:6 | my | main.rs:1:1:1:7 | mod my | -| main.rs:216:5:216:14 | ...::nested | my.rs:1:1:1:15 | mod nested | -| main.rs:216:5:216:23 | ...::nested1 | my/nested.rs:1:1:17:1 | mod nested1 | -| main.rs:216:5:216:32 | ...::nested2 | my/nested.rs:2:5:11:5 | mod nested2 | -| main.rs:216:5:216:35 | ...::f | my/nested.rs:3:9:5:9 | fn f | -| main.rs:217:5:217:6 | my | main.rs:1:1:1:7 | mod my | -| main.rs:217:5:217:9 | ...::f | my.rs:5:1:7:1 | fn f | -| main.rs:218:5:218:11 | nested2 | my2/mod.rs:1:1:1:16 | mod nested2 | -| main.rs:218:5:218:20 | ...::nested3 | my2/nested2.rs:1:1:11:1 | mod nested3 | -| main.rs:218:5:218:29 | ...::nested4 | my2/nested2.rs:2:5:10:5 | mod nested4 | -| main.rs:218:5:218:32 | ...::f | my2/nested2.rs:3:9:5:9 | fn f | -| main.rs:219:5:219:5 | f | my2/nested2.rs:3:9:5:9 | fn f | -| main.rs:220:5:220:5 | g | my2/nested2.rs:7:9:9:9 | fn g | -| main.rs:221:5:221:9 | crate | main.rs:1:1:233:2 | SourceFile | -| main.rs:221:5:221:12 | ...::h | main.rs:50:1:69:1 | fn h | -| main.rs:222:5:222:6 | m1 | main.rs:13:1:37:1 | mod m1 | -| main.rs:222:5:222:10 | ...::m2 | main.rs:18:5:36:5 | mod m2 | -| main.rs:222:5:222:13 | ...::g | main.rs:23:9:27:9 | fn g | -| main.rs:223:5:223:6 | m1 | main.rs:13:1:37:1 | mod m1 | -| main.rs:223:5:223:10 | ...::m2 | main.rs:18:5:36:5 | mod m2 | -| main.rs:223:5:223:14 | ...::m3 | main.rs:29:9:35:9 | mod m3 | -| main.rs:223:5:223:17 | ...::h | main.rs:30:27:34:13 | fn h | -| main.rs:224:5:224:6 | m4 | main.rs:39:1:46:1 | mod m4 | -| main.rs:224:5:224:9 | ...::i | main.rs:42:5:45:5 | fn i | -| main.rs:225:5:225:5 | h | main.rs:50:1:69:1 | fn h | -| main.rs:226:5:226:11 | f_alias | my2/nested2.rs:3:9:5:9 | fn f | -| main.rs:227:5:227:11 | g_alias | my2/nested2.rs:7:9:9:9 | fn g | -| main.rs:228:5:228:5 | j | main.rs:97:1:101:1 | fn j | -| main.rs:229:5:229:6 | m6 | main.rs:109:1:120:1 | mod m6 | -| main.rs:229:5:229:9 | ...::g | main.rs:114:5:119:5 | fn g | -| main.rs:230:5:230:6 | m7 | main.rs:122:1:137:1 | mod m7 | -| main.rs:230:5:230:9 | ...::f | main.rs:129:5:136:5 | fn f | -| main.rs:231:5:231:6 | m8 | main.rs:139:1:182:1 | mod m8 | -| main.rs:231:5:231:9 | ...::g | main.rs:169:5:181:5 | fn g | -| main.rs:232:5:232:6 | m9 | main.rs:184:1:192:1 | mod m9 | -| main.rs:232:5:232:9 | ...::f | main.rs:187:5:191:5 | fn f | +| main.rs:221:17:221:19 | Foo | main.rs:216:5:216:21 | struct Foo | +| main.rs:221:17:221:19 | Foo | main.rs:218:5:218:15 | fn Foo | +| main.rs:222:9:222:11 | Foo | main.rs:216:5:216:21 | struct Foo | +| main.rs:222:9:222:11 | Foo | main.rs:218:5:218:15 | fn Foo | +| main.rs:231:9:231:11 | Bar | main.rs:227:5:229:5 | enum Bar | +| main.rs:231:9:231:19 | ...::FooBar | main.rs:228:9:228:17 | FooBar | +| main.rs:236:13:236:15 | Foo | main.rs:216:5:216:21 | struct Foo | +| main.rs:236:13:236:15 | Foo | main.rs:218:5:218:15 | fn Foo | +| main.rs:237:17:237:22 | FooBar | main.rs:228:9:228:17 | FooBar | +| main.rs:237:17:237:22 | FooBar | main.rs:233:5:233:18 | fn FooBar | +| main.rs:238:17:238:22 | FooBar | main.rs:228:9:228:17 | FooBar | +| main.rs:238:17:238:22 | FooBar | main.rs:233:5:233:18 | fn FooBar | +| main.rs:246:9:246:9 | E | main.rs:241:15:244:5 | enum E | +| main.rs:246:9:246:12 | ...::C | main.rs:243:9:243:9 | C | +| main.rs:249:17:249:17 | S | main.rs:241:5:241:13 | struct S | +| main.rs:250:17:250:17 | C | main.rs:243:9:243:9 | C | +| main.rs:264:14:264:17 | Self | main.rs:255:5:265:5 | trait MyParamTrait | +| main.rs:273:13:273:17 | crate | main.rs:1:1:302:2 | SourceFile | +| main.rs:273:13:273:22 | ...::m13 | main.rs:268:1:281:1 | mod m13 | +| main.rs:273:13:273:25 | ...::f | main.rs:269:5:269:17 | fn f | +| main.rs:273:13:273:25 | ...::f | main.rs:269:19:270:19 | struct f | +| main.rs:276:17:276:17 | f | main.rs:269:5:269:17 | fn f | +| main.rs:276:17:276:17 | f | main.rs:269:19:270:19 | struct f | +| main.rs:277:21:277:21 | f | main.rs:269:5:269:17 | fn f | +| main.rs:277:21:277:21 | f | main.rs:269:19:270:19 | struct f | +| main.rs:278:13:278:13 | f | main.rs:269:5:269:17 | fn f | +| main.rs:278:13:278:13 | f | main.rs:269:19:270:19 | struct f | +| main.rs:284:5:284:6 | my | main.rs:1:1:1:7 | mod my | +| main.rs:284:5:284:14 | ...::nested | my.rs:1:1:1:15 | mod nested | +| main.rs:284:5:284:23 | ...::nested1 | my/nested.rs:1:1:17:1 | mod nested1 | +| main.rs:284:5:284:32 | ...::nested2 | my/nested.rs:2:5:11:5 | mod nested2 | +| main.rs:284:5:284:35 | ...::f | my/nested.rs:3:9:5:9 | fn f | +| main.rs:285:5:285:6 | my | main.rs:1:1:1:7 | mod my | +| main.rs:285:5:285:9 | ...::f | my.rs:5:1:7:1 | fn f | +| main.rs:286:5:286:11 | nested2 | my2/mod.rs:1:1:1:16 | mod nested2 | +| main.rs:286:5:286:20 | ...::nested3 | my2/nested2.rs:1:1:11:1 | mod nested3 | +| main.rs:286:5:286:29 | ...::nested4 | my2/nested2.rs:2:5:10:5 | mod nested4 | +| main.rs:286:5:286:32 | ...::f | my2/nested2.rs:3:9:5:9 | fn f | +| main.rs:287:5:287:5 | f | my2/nested2.rs:3:9:5:9 | fn f | +| main.rs:288:5:288:5 | g | my2/nested2.rs:7:9:9:9 | fn g | +| main.rs:289:5:289:9 | crate | main.rs:1:1:302:2 | SourceFile | +| main.rs:289:5:289:12 | ...::h | main.rs:50:1:69:1 | fn h | +| main.rs:290:5:290:6 | m1 | main.rs:13:1:37:1 | mod m1 | +| main.rs:290:5:290:10 | ...::m2 | main.rs:18:5:36:5 | mod m2 | +| main.rs:290:5:290:13 | ...::g | main.rs:23:9:27:9 | fn g | +| main.rs:291:5:291:6 | m1 | main.rs:13:1:37:1 | mod m1 | +| main.rs:291:5:291:10 | ...::m2 | main.rs:18:5:36:5 | mod m2 | +| main.rs:291:5:291:14 | ...::m3 | main.rs:29:9:35:9 | mod m3 | +| main.rs:291:5:291:17 | ...::h | main.rs:30:27:34:13 | fn h | +| main.rs:292:5:292:6 | m4 | main.rs:39:1:46:1 | mod m4 | +| main.rs:292:5:292:9 | ...::i | main.rs:42:5:45:5 | fn i | +| main.rs:293:5:293:5 | h | main.rs:50:1:69:1 | fn h | +| main.rs:294:5:294:11 | f_alias | my2/nested2.rs:3:9:5:9 | fn f | +| main.rs:295:5:295:11 | g_alias | my2/nested2.rs:7:9:9:9 | fn g | +| main.rs:296:5:296:5 | j | main.rs:97:1:101:1 | fn j | +| main.rs:297:5:297:6 | m6 | main.rs:109:1:120:1 | mod m6 | +| main.rs:297:5:297:9 | ...::g | main.rs:114:5:119:5 | fn g | +| main.rs:298:5:298:6 | m7 | main.rs:122:1:137:1 | mod m7 | +| main.rs:298:5:298:9 | ...::f | main.rs:129:5:136:5 | fn f | +| main.rs:299:5:299:6 | m8 | main.rs:139:1:182:1 | mod m8 | +| main.rs:299:5:299:9 | ...::g | main.rs:169:5:181:5 | fn g | +| main.rs:300:5:300:6 | m9 | main.rs:184:1:192:1 | mod m9 | +| main.rs:300:5:300:9 | ...::f | main.rs:187:5:191:5 | fn f | +| main.rs:301:5:301:7 | m11 | main.rs:215:1:252:1 | mod m11 | +| main.rs:301:5:301:10 | ...::f | main.rs:220:5:223:5 | fn f | +| main.rs:301:5:301:10 | ...::f | main.rs:225:5:225:12 | mod f | | my2/mod.rs:5:5:5:11 | nested2 | my2/mod.rs:1:1:1:16 | mod nested2 | | my2/mod.rs:5:5:5:20 | ...::nested3 | my2/nested2.rs:1:1:11:1 | mod nested3 | | my2/mod.rs:5:5:5:29 | ...::nested4 | my2/nested2.rs:2:5:10:5 | mod nested4 | From c7441ab0056c2ab6cfcc211e10bd7963018287ba Mon Sep 17 00:00:00 2001 From: Tom Hvitved Date: Mon, 10 Feb 2025 13:49:28 +0100 Subject: [PATCH 2/2] Rust: Model namespaces in path resolution --- rust/ql/lib/codeql/rust/AstConsistency.qll | 6 + .../rust/elements/internal/PathResolution.qll | 185 +++++++++++++++--- .../CONSISTENCY/AstConsistency.expected | 20 -- .../library-tests/path-resolution/main.rs | 22 +-- .../path-resolution/path-resolution.expected | 11 +- 5 files changed, 180 insertions(+), 64 deletions(-) diff --git a/rust/ql/lib/codeql/rust/AstConsistency.qll b/rust/ql/lib/codeql/rust/AstConsistency.qll index 3ff90e010138..4069a77f4b2b 100644 --- a/rust/ql/lib/codeql/rust/AstConsistency.qll +++ b/rust/ql/lib/codeql/rust/AstConsistency.qll @@ -78,6 +78,12 @@ private import codeql.rust.elements.internal.PathResolution /** Holds if `p` may resolve to multiple items including `i`. */ query predicate multiplePathResolutions(Path p, ItemNode i) { i = resolvePath(p) and + // `use foo::bar` may use both a type `bar` and a value `bar` + not p = + any(UseTree use | + not use.isGlob() and + not use.hasUseTreeList() + ).getPath() and strictcount(resolvePath(p)) > 1 } diff --git a/rust/ql/lib/codeql/rust/elements/internal/PathResolution.qll b/rust/ql/lib/codeql/rust/elements/internal/PathResolution.qll index 0854ddb43427..df1d0fe07188 100644 --- a/rust/ql/lib/codeql/rust/elements/internal/PathResolution.qll +++ b/rust/ql/lib/codeql/rust/elements/internal/PathResolution.qll @@ -5,6 +5,31 @@ private import rust private import codeql.rust.elements.internal.generated.ParentChild +private newtype TNamespace = + TTypeNamespace() or + TValueNamespace() + +/** + * A namespace. + * + * Either the _value_ namespace or the _type_ namespace, see + * https://doc.rust-lang.org/reference/names/namespaces.html. + */ +final class Namespace extends TNamespace { + /** Holds if this is the value namespace. */ + predicate isValue() { this = TValueNamespace() } + + /** Holds if this is the type namespace. */ + predicate isType() { this = TTypeNamespace() } + + /** Gets a textual representation of this namespace. */ + string toString() { + this.isValue() and result = "value" + or + this.isType() and result = "type" + } +} + /** * An item that may be referred to by a path, and which is a node in * the _item graph_. @@ -46,11 +71,15 @@ private import codeql.rust.elements.internal.generated.ParentChild * - https://doc.rust-lang.org/reference/names/scopes.html * - https://doc.rust-lang.org/reference/paths.html * - https://doc.rust-lang.org/reference/visibility-and-privacy.html + * - https://doc.rust-lang.org/reference/names/namespaces.html */ abstract class ItemNode extends AstNode { /** Gets the (original) name of this item. */ abstract string getName(); + /** Gets the namespace that this item belongs to, if any. */ + abstract Namespace getNamespace(); + /** Gets the visibility of this item, if any. */ abstract Visibility getVisibility(); @@ -143,30 +172,44 @@ abstract private class ModuleLikeNode extends ItemNode { private class SourceFileItemNode extends ModuleLikeNode, SourceFile { override string getName() { result = "(source file)" } + override Namespace getNamespace() { + result.isType() // can be referenced with `super` + } + override Visibility getVisibility() { none() } } private class ConstItemNode extends ItemNode instanceof Const { override string getName() { result = Const.super.getName().getText() } + override Namespace getNamespace() { result.isValue() } + override Visibility getVisibility() { result = Const.super.getVisibility() } } private class EnumItemNode extends ItemNode instanceof Enum { override string getName() { result = Enum.super.getName().getText() } + override Namespace getNamespace() { result.isType() } + override Visibility getVisibility() { result = Enum.super.getVisibility() } } private class VariantItemNode extends ItemNode instanceof Variant { override string getName() { result = Variant.super.getName().getText() } + override Namespace getNamespace() { + if super.getFieldList() instanceof RecordFieldList then result.isType() else result.isValue() + } + override Visibility getVisibility() { result = Variant.super.getVisibility() } } private class FunctionItemNode extends ItemNode instanceof Function { override string getName() { result = Function.super.getName().getText() } + override Namespace getNamespace() { result.isValue() } + override Visibility getVisibility() { result = Function.super.getVisibility() } } @@ -184,57 +227,92 @@ abstract private class ImplOrTraitItemNode extends ItemNode { } } -private class ImplItemNode extends ImplOrTraitItemNode instanceof Impl { +class ImplItemNode extends ImplOrTraitItemNode instanceof Impl { + ItemNode resolveSelfTy() { result = resolvePath(super.getSelfTy().(PathTypeRepr).getPath()) } + override string getName() { result = "(impl)" } + override Namespace getNamespace() { + result.isType() // can be referenced with `Self` + } + override Visibility getVisibility() { result = Impl.super.getVisibility() } } private class MacroCallItemNode extends ItemNode instanceof MacroCall { override string getName() { result = "(macro call)" } + override Namespace getNamespace() { none() } + override Visibility getVisibility() { none() } } private class ModuleItemNode extends ModuleLikeNode instanceof Module { override string getName() { result = Module.super.getName().getText() } + override Namespace getNamespace() { result.isType() } + override Visibility getVisibility() { result = Module.super.getVisibility() } } private class StructItemNode extends ItemNode instanceof Struct { override string getName() { result = Struct.super.getName().getText() } + override Namespace getNamespace() { + result.isType() // the struct itself + or + not super.getFieldList() instanceof RecordFieldList and + result.isValue() // the constructor + } + override Visibility getVisibility() { result = Struct.super.getVisibility() } } -private class TraitItemNode extends ImplOrTraitItemNode instanceof Trait { +class TraitItemNode extends ImplOrTraitItemNode instanceof Trait { override string getName() { result = Trait.super.getName().getText() } + override Namespace getNamespace() { result.isType() } + override Visibility getVisibility() { result = Trait.super.getVisibility() } } +class TypeAliasItemNode extends ItemNode instanceof TypeAlias { + override string getName() { result = TypeAlias.super.getName().getText() } + + override Namespace getNamespace() { result.isType() } + + override Visibility getVisibility() { result = TypeAlias.super.getVisibility() } +} + private class UnionItemNode extends ItemNode instanceof Union { override string getName() { result = Union.super.getName().getText() } + override Namespace getNamespace() { result.isType() } + override Visibility getVisibility() { result = Union.super.getVisibility() } } private class UseItemNode extends ItemNode instanceof Use { override string getName() { result = "(use)" } + override Namespace getNamespace() { none() } + override Visibility getVisibility() { none() } } private class BlockExprItemNode extends ItemNode instanceof BlockExpr { override string getName() { result = "(block expr)" } + override Namespace getNamespace() { none() } + override Visibility getVisibility() { none() } } private class TypeParamItemNode extends ItemNode instanceof TypeParam { override string getName() { result = TypeParam.super.getName().getText() } + override Namespace getNamespace() { result.isType() } + override Visibility getVisibility() { none() } } @@ -320,19 +398,22 @@ private predicate useTreeDeclares(UseTree tree, string name) { } /** - * Holds if `item` explicitly declares a sub item named `name`. This includes - * items declared by `use` statements, except for glob imports. + * Holds if `item` explicitly declares a sub item named `name` in the + * namespace `ns`. This includes items declared by `use` statements, + * except for glob imports. */ pragma[nomagic] -private predicate declares(ItemNode item, string name) { +private predicate declares(ItemNode item, Namespace ns, string name) { exists(ItemNode child | child.getImmediateParent() = item | - child.getName() = name + child.getName() = name and + child.getNamespace() = ns or - useTreeDeclares(child.(Use).getUseTree(), name) + useTreeDeclares(child.(Use).getUseTree(), name) and + exists(ns) // `use foo::bar` can refer to both a value and a type ) or exists(MacroCallItemNode call | - declares(call, name) and + declares(call, ns, name) and call.getImmediateParent() = item ) } @@ -351,19 +432,20 @@ private class RelevantPath extends Path { /** * Holds if the unqualified path `p` references an item named `name`, and `name` - * may be looked up inside enclosing item `encl`. + * may be looked up in the `ns` namespace inside enclosing item `encl`. */ pragma[nomagic] -private predicate unqualifiedPathLookup(RelevantPath p, string name, ItemNode encl) { +private predicate unqualifiedPathLookup(RelevantPath p, string name, Namespace ns, ItemNode encl) { exists(ItemNode encl0 | // lookup in the immediately enclosing item p.isUnqualified(name) and - encl0.getADescendant() = p + encl0.getADescendant() = p and + exists(ns) or // lookup in an outer scope, but only if the item is not declared in inner scope exists(ItemNode mid | - unqualifiedPathLookup(p, name, mid) and - not declares(mid, name) + unqualifiedPathLookup(p, name, ns, mid) and + not declares(mid, ns, name) | // nested modules do not have unqualified access to items from outer modules, // except for items declared at top-level in the source file @@ -374,16 +456,30 @@ private predicate unqualifiedPathLookup(RelevantPath p, string name, ItemNode en | // functions in `impl` blocks need to use explicit `Self::` to access other // functions in the `impl` block - if encl0 instanceof ImplOrTraitItemNode then encl = encl0.getImmediateParent() else encl = encl0 + if encl0 instanceof ImplOrTraitItemNode and ns.isValue() + then encl = encl0.getImmediateParent() + else encl = encl0 ) } -/** Gets the item that `path` resolves to, if any. */ -cached -ItemNode resolvePath(RelevantPath path) { - exists(ItemNode encl, string name | - unqualifiedPathLookup(path, name, encl) and - result = encl.getASuccessor(name) +pragma[nomagic] +private ItemNode getASuccessor(ItemNode pred, string name, Namespace ns) { + result = pred.getASuccessor(name) and + ns = result.getNamespace() +} + +pragma[nomagic] +private ItemNode resolvePath0(RelevantPath path) { + exists(ItemNode encl, Namespace ns, string name, ItemNode res | + unqualifiedPathLookup(path, name, ns, encl) and + res = getASuccessor(encl, name, ns) + | + if + not any(RelevantPath parent).getQualifier() = path and + name = "Self" and + res instanceof ImplItemNode + then result = res.(ImplItemNode).resolveSelfTy() + else result = res ) or exists(ItemNode q, string name | @@ -394,6 +490,47 @@ ItemNode resolvePath(RelevantPath path) { result = resolveUseTreeListItem(_, _, path) } +/** Holds if path `p` must be looked up in namespace `n`. */ +private predicate pathUsesNamespace(Path p, Namespace n) { + n.isValue() and + ( + p = any(PathExpr pe).getPath() + or + p = any(TupleStructPat tsp).getPath() + ) + or + n.isType() and + ( + p = any(Visibility v).getPath() + or + p = any(RecordExpr re).getPath() + or + p = any(PathTypeRepr ptr).getPath() + or + p = any(RecordPat rp).getPath() + or + p = + any(UseTree use | + use.isGlob() + or + use.hasUseTreeList() + ).getPath() + or + p = any(Path parent).getQualifier() + ) +} + +/** Gets the item that `path` resolves to, if any. */ +cached +ItemNode resolvePath(RelevantPath path) { + result = resolvePath0(path) and + ( + pathUsesNamespace(path, result.getNamespace()) + or + not pathUsesNamespace(path, _) + ) +} + pragma[nomagic] private ItemNode resolvePathQualifier(RelevantPath path, string name) { result = resolvePath(path.getQualifier()) and @@ -452,14 +589,14 @@ pragma[nomagic] private predicate useImportEdge(Use use, string name, ItemNode item) { exists(UseTree tree, ItemNode used | used = resolveUseTreeListItem(use, tree) and - not exists(tree.getUseTreeList()) and + not tree.hasUseTreeList() and if tree.isGlob() then - exists(ItemNode encl | + exists(ItemNode encl, Namespace ns | encl.getADescendant() = use and - item = used.getASuccessor(name) and + item = getASuccessor(used, name, ns) and // glob imports can be shadowed - not declares(encl, name) + not declares(encl, ns, name) ) else item = used | diff --git a/rust/ql/test/library-tests/path-resolution/CONSISTENCY/AstConsistency.expected b/rust/ql/test/library-tests/path-resolution/CONSISTENCY/AstConsistency.expected index 48a22466d889..9d4e175192b4 100644 --- a/rust/ql/test/library-tests/path-resolution/CONSISTENCY/AstConsistency.expected +++ b/rust/ql/test/library-tests/path-resolution/CONSISTENCY/AstConsistency.expected @@ -1,23 +1,3 @@ multiplePathResolutions | main.rs:118:9:118:9 | f | main.rs:104:5:106:5 | fn f | | main.rs:118:9:118:9 | f | main.rs:110:5:112:5 | fn f | -| main.rs:221:17:221:19 | Foo | main.rs:216:5:216:21 | struct Foo | -| main.rs:221:17:221:19 | Foo | main.rs:218:5:218:15 | fn Foo | -| main.rs:222:9:222:11 | Foo | main.rs:216:5:216:21 | struct Foo | -| main.rs:222:9:222:11 | Foo | main.rs:218:5:218:15 | fn Foo | -| main.rs:236:13:236:15 | Foo | main.rs:216:5:216:21 | struct Foo | -| main.rs:236:13:236:15 | Foo | main.rs:218:5:218:15 | fn Foo | -| main.rs:237:17:237:22 | FooBar | main.rs:228:9:228:17 | FooBar | -| main.rs:237:17:237:22 | FooBar | main.rs:233:5:233:18 | fn FooBar | -| main.rs:238:17:238:22 | FooBar | main.rs:228:9:228:17 | FooBar | -| main.rs:238:17:238:22 | FooBar | main.rs:233:5:233:18 | fn FooBar | -| main.rs:273:13:273:25 | ...::f | main.rs:269:5:269:17 | fn f | -| main.rs:273:13:273:25 | ...::f | main.rs:269:19:270:19 | struct f | -| main.rs:276:17:276:17 | f | main.rs:269:5:269:17 | fn f | -| main.rs:276:17:276:17 | f | main.rs:269:19:270:19 | struct f | -| main.rs:277:21:277:21 | f | main.rs:269:5:269:17 | fn f | -| main.rs:277:21:277:21 | f | main.rs:269:19:270:19 | struct f | -| main.rs:278:13:278:13 | f | main.rs:269:5:269:17 | fn f | -| main.rs:278:13:278:13 | f | main.rs:269:19:270:19 | struct f | -| main.rs:301:5:301:10 | ...::f | main.rs:220:5:223:5 | fn f | -| main.rs:301:5:301:10 | ...::f | main.rs:225:5:225:12 | mod f | diff --git a/rust/ql/test/library-tests/path-resolution/main.rs b/rust/ql/test/library-tests/path-resolution/main.rs index 123c1e55baff..de7b245c478d 100644 --- a/rust/ql/test/library-tests/path-resolution/main.rs +++ b/rust/ql/test/library-tests/path-resolution/main.rs @@ -218,8 +218,8 @@ mod m11 { fn Foo() {} // I62 pub fn f() { - let _ = Foo {}; // $ item=I61 $ SPURIOUS: item=I62 - Foo(); // $ item=I62 $ SPURIOUS: item=I61 + let _ = Foo {}; // $ item=I61 + Foo(); // $ item=I62 } // I63 mod f {} // I66 @@ -233,9 +233,9 @@ mod m11 { fn FooBar() {} // I65 #[rustfmt::skip] - fn g(x: Foo) { // $ item=I61 $ SPURIOUS: item=I62 - let _ = FooBar {}; // $ item=I64 $ SPURIOUS: item=I65 - let _ = FooBar(); // $ item=I65 $ SPURIOUS: item=I64 + fn g(x: Foo) { // $ item=I61 + let _ = FooBar {}; // $ item=I64 + let _ = FooBar(); // $ item=I65 } struct S; // I67 @@ -260,8 +260,8 @@ mod m12 { fn f( &self, - x: T // $ MISSING: item=I69 - ) -> Self::AssociatedType; // $ MISSING: item=I70 + x: T // $ item=I69 + ) -> Self::AssociatedType; // $ item=I70 } } @@ -273,9 +273,9 @@ mod m13 { use crate::m13::f; // $ item=I71 item=I72 #[rustfmt::skip] - fn g(x: f) { // $ item=I72 $ SPUROUS: item=I71 - let _ = f {}; // $ item=I72 $ SPUROUS: item=I71 - f(); // $ item=I71 $ SPUROUS: item=I72 + fn g(x: f) { // $ item=I72 + let _ = f {}; // $ item=I72 + f(); // $ item=I71 } } } @@ -298,5 +298,5 @@ fn main() { m7::f(); // $ item=I45 m8::g(); // $ item=I55 m9::f(); // $ item=I57 - m11::f(); // $ item=I63 $ SPUROUS: item=I66 + m11::f(); // $ item=I63 } diff --git a/rust/ql/test/library-tests/path-resolution/path-resolution.expected b/rust/ql/test/library-tests/path-resolution/path-resolution.expected index c2a59f7083bc..5aa8cbc0768f 100644 --- a/rust/ql/test/library-tests/path-resolution/path-resolution.expected +++ b/rust/ql/test/library-tests/path-resolution/path-resolution.expected @@ -105,32 +105,26 @@ resolvePath | main.rs:208:9:208:9 | T | main.rs:204:14:204:14 | TypeParam | | main.rs:211:9:211:16 | MyStruct | main.rs:195:5:201:5 | struct MyStruct | | main.rs:221:17:221:19 | Foo | main.rs:216:5:216:21 | struct Foo | -| main.rs:221:17:221:19 | Foo | main.rs:218:5:218:15 | fn Foo | -| main.rs:222:9:222:11 | Foo | main.rs:216:5:216:21 | struct Foo | | main.rs:222:9:222:11 | Foo | main.rs:218:5:218:15 | fn Foo | | main.rs:231:9:231:11 | Bar | main.rs:227:5:229:5 | enum Bar | | main.rs:231:9:231:19 | ...::FooBar | main.rs:228:9:228:17 | FooBar | | main.rs:236:13:236:15 | Foo | main.rs:216:5:216:21 | struct Foo | -| main.rs:236:13:236:15 | Foo | main.rs:218:5:218:15 | fn Foo | | main.rs:237:17:237:22 | FooBar | main.rs:228:9:228:17 | FooBar | -| main.rs:237:17:237:22 | FooBar | main.rs:233:5:233:18 | fn FooBar | -| main.rs:238:17:238:22 | FooBar | main.rs:228:9:228:17 | FooBar | | main.rs:238:17:238:22 | FooBar | main.rs:233:5:233:18 | fn FooBar | | main.rs:246:9:246:9 | E | main.rs:241:15:244:5 | enum E | | main.rs:246:9:246:12 | ...::C | main.rs:243:9:243:9 | C | | main.rs:249:17:249:17 | S | main.rs:241:5:241:13 | struct S | | main.rs:250:17:250:17 | C | main.rs:243:9:243:9 | C | +| main.rs:263:16:263:16 | T | main.rs:257:7:257:7 | TypeParam | | main.rs:264:14:264:17 | Self | main.rs:255:5:265:5 | trait MyParamTrait | +| main.rs:264:14:264:33 | ...::AssociatedType | main.rs:259:9:259:28 | TypeAlias | | main.rs:273:13:273:17 | crate | main.rs:1:1:302:2 | SourceFile | | main.rs:273:13:273:22 | ...::m13 | main.rs:268:1:281:1 | mod m13 | | main.rs:273:13:273:25 | ...::f | main.rs:269:5:269:17 | fn f | | main.rs:273:13:273:25 | ...::f | main.rs:269:19:270:19 | struct f | -| main.rs:276:17:276:17 | f | main.rs:269:5:269:17 | fn f | | main.rs:276:17:276:17 | f | main.rs:269:19:270:19 | struct f | -| main.rs:277:21:277:21 | f | main.rs:269:5:269:17 | fn f | | main.rs:277:21:277:21 | f | main.rs:269:19:270:19 | struct f | | main.rs:278:13:278:13 | f | main.rs:269:5:269:17 | fn f | -| main.rs:278:13:278:13 | f | main.rs:269:19:270:19 | struct f | | main.rs:284:5:284:6 | my | main.rs:1:1:1:7 | mod my | | main.rs:284:5:284:14 | ...::nested | my.rs:1:1:1:15 | mod nested | | main.rs:284:5:284:23 | ...::nested1 | my/nested.rs:1:1:17:1 | mod nested1 | @@ -169,7 +163,6 @@ resolvePath | main.rs:300:5:300:9 | ...::f | main.rs:187:5:191:5 | fn f | | main.rs:301:5:301:7 | m11 | main.rs:215:1:252:1 | mod m11 | | main.rs:301:5:301:10 | ...::f | main.rs:220:5:223:5 | fn f | -| main.rs:301:5:301:10 | ...::f | main.rs:225:5:225:12 | mod f | | my2/mod.rs:5:5:5:11 | nested2 | my2/mod.rs:1:1:1:16 | mod nested2 | | my2/mod.rs:5:5:5:20 | ...::nested3 | my2/nested2.rs:1:1:11:1 | mod nested3 | | my2/mod.rs:5:5:5:29 | ...::nested4 | my2/nested2.rs:2:5:10:5 | mod nested4 |