diff --git a/rust/ql/lib/codeql/files/FileSystem.qll b/rust/ql/lib/codeql/files/FileSystem.qll index 175f50c7c9ea..5a60d28418eb 100644 --- a/rust/ql/lib/codeql/files/FileSystem.qll +++ b/rust/ql/lib/codeql/files/FileSystem.qll @@ -34,6 +34,8 @@ class Container = Impl::Container; class Folder = Impl::Folder; +module Folder = Impl::Folder; + /** A file. */ class File extends Container, Impl::File { /** Holds if this file was extracted from ordinary source code. */ diff --git a/rust/ql/lib/codeql/rust/internal/PathResolution.qll b/rust/ql/lib/codeql/rust/internal/PathResolution.qll index 91d7e87704c6..9a73d8d035c7 100644 --- a/rust/ql/lib/codeql/rust/internal/PathResolution.qll +++ b/rust/ql/lib/codeql/rust/internal/PathResolution.qll @@ -655,6 +655,16 @@ private predicate fileModule(SourceFile f, string name, Folder folder) { ) } +/** + * Gets the `Meta` of the module `m`'s [path attribute][1]. + * + * [1]: https://doc.rust-lang.org/reference/items/modules.html#r-items.mod.outlined.path + */ +private Meta getPathAttrMeta(Module m) { + result = m.getAnAttr().getMeta() and + result.getPath().getText() = "path" +} + /** * Holds if `m` is a `mod name;` module declaration, where the corresponding * module file needs to be looked up in `lookup` or one of its descandants. @@ -663,12 +673,7 @@ private predicate modImport0(Module m, string name, Folder lookup) { exists(File f, Folder parent, string fileName | f = m.getFile() and not m.hasItemList() and - // TODO: handle - // ``` - // #[path = "foo.rs"] - // mod bar; - // ``` - not m.getAnAttr().getMeta().getPath().getText() = "path" and + not exists(getPathAttrMeta(m)) and name = m.getName().getText() and parent = f.getParentContainer() and fileName = f.getStem() @@ -717,6 +722,16 @@ private predicate modImportNestedLookup(Module m, ModuleItemNode ancestor, Folde ) } +private predicate pathAttrImport(Folder f, Module m, string relativePath) { + exists(Meta meta | + f = m.getFile().getParentContainer() and + meta = getPathAttrMeta(m) and + relativePath = meta.getExpr().(LiteralExpr).getTextValue().regexpCapture("\"(.+)\"", 1) + ) +} + +private predicate shouldAppend(Folder f, string relativePath) { pathAttrImport(f, _, relativePath) } + /** Holds if `m` is a `mod name;` item importing file `f`. */ private predicate fileImport(Module m, SourceFile f) { exists(string name, Folder parent | @@ -730,6 +745,11 @@ private predicate fileImport(Module m, SourceFile f) { // `m` is inside a nested module modImportNestedLookup(m, m, parent) ) + or + exists(Folder folder, string relativePath | + pathAttrImport(folder, m, relativePath) and + f.getFile() = Folder::Append::append(folder, relativePath) + ) } /** diff --git a/rust/ql/test/library-tests/path-resolution/my2/mod.rs b/rust/ql/test/library-tests/path-resolution/my2/mod.rs index 64291a53af53..43c1a05e91fe 100644 --- a/rust/ql/test/library-tests/path-resolution/my2/mod.rs +++ b/rust/ql/test/library-tests/path-resolution/my2/mod.rs @@ -10,3 +10,8 @@ pub use nested2::nested5::*; // $ item=I114 pub use nested2::nested7::nested8::{self}; // $ item=I118 pub mod my3; + +#[path = "renamed.rs"] +mod mymod; + +use mymod::f; // $ item=I1001 diff --git a/rust/ql/test/library-tests/path-resolution/my2/renamed.rs b/rust/ql/test/library-tests/path-resolution/my2/renamed.rs new file mode 100644 index 000000000000..fa37691c1366 --- /dev/null +++ b/rust/ql/test/library-tests/path-resolution/my2/renamed.rs @@ -0,0 +1 @@ +pub fn f() {} // I1001 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 1d480f2f5ad9..7fbbca66c39c 100644 --- a/rust/ql/test/library-tests/path-resolution/path-resolution.expected +++ b/rust/ql/test/library-tests/path-resolution/path-resolution.expected @@ -30,6 +30,7 @@ mod | main.rs:523:1:548:1 | mod m23 | | my2/mod.rs:1:1:1:16 | mod nested2 | | my2/mod.rs:12:1:12:12 | mod my3 | +| my2/mod.rs:14:1:15:10 | mod mymod | | my2/nested2.rs:1:1:11:1 | mod nested3 | | my2/nested2.rs:2:5:10:5 | mod nested4 | | my2/nested2.rs:13:1:19:1 | mod nested5 | @@ -306,12 +307,14 @@ resolvePath | my2/mod.rs:10:9:10:24 | ...::nested7 | my2/nested2.rs:21:1:27:1 | mod nested7 | | my2/mod.rs:10:9:10:33 | ...::nested8 | my2/nested2.rs:22:5:26:5 | mod nested8 | | my2/mod.rs:10:37:10:40 | self | my2/nested2.rs:22:5:26:5 | mod nested8 | +| my2/mod.rs:17:5:17:9 | mymod | my2/mod.rs:14:1:15:10 | mod mymod | +| my2/mod.rs:17:5:17:12 | ...::f | my2/renamed.rs:1:1:1:13 | fn f | | my2/my3/mod.rs:3:5:3:5 | g | my2/mod.rs:3:1:6:1 | fn g | | my2/my3/mod.rs:4:5:4:5 | h | main.rs:50:1:69:1 | fn h | -| my2/my3/mod.rs:7:5:7:9 | super | my2/mod.rs:1:1:12:13 | SourceFile | +| my2/my3/mod.rs:7:5:7:9 | super | my2/mod.rs:1:1:17:30 | SourceFile | | my2/my3/mod.rs:7:5:7:16 | ...::super | main.rs:1:1:578:2 | SourceFile | | my2/my3/mod.rs:7:5:7:19 | ...::h | main.rs:50:1:69:1 | fn h | -| my2/my3/mod.rs:8:5:8:9 | super | my2/mod.rs:1:1:12:13 | SourceFile | +| my2/my3/mod.rs:8:5:8:9 | super | my2/mod.rs:1:1:17:30 | SourceFile | | my2/my3/mod.rs:8:5:8:12 | ...::g | my2/mod.rs:3:1:6:1 | fn g | | my.rs:3:5:3:10 | nested | my.rs:1:1:1:15 | mod nested | | my.rs:3:5:3:13 | ...::g | my/nested.rs:19:1:22:1 | fn g | diff --git a/shared/util/codeql/util/FileSystem.qll b/shared/util/codeql/util/FileSystem.qll index a9eb21279b63..ea58db929c51 100644 --- a/shared/util/codeql/util/FileSystem.qll +++ b/shared/util/codeql/util/FileSystem.qll @@ -218,6 +218,60 @@ module Make { /** Gets the URL of this file. */ override string getURL() { result = "file://" + this.getAbsolutePath() + ":0:0:0:0" } } + + /** Provides logic related to `Folder`s. */ + module Folder { + /** Holds if `relativePath` needs to be appended to `f`. */ + signature predicate shouldAppendSig(Folder f, string relativePath); + + /** Provides the `append` predicate for appending a relative path onto a folder. */ + module Append { + pragma[nomagic] + private string getComponent(string relativePath, int i) { + shouldAppend(_, relativePath) and + result = relativePath.replaceAll("\\", "/").regexpFind("[^/]+", i, _) + } + + private int getNumberOfComponents(string relativePath) { + result = strictcount(int i | exists(getComponent(relativePath, i)) | i) + or + relativePath = "" and + result = 0 + } + + pragma[nomagic] + private Container appendStep(Folder f, string relativePath, int i) { + i = -1 and + shouldAppend(f, relativePath) and + result = f + or + exists(Container mid, string comp | + mid = appendStep(f, relativePath, i - 1) and + comp = getComponent(relativePath, i) and + if comp = ".." + then result = mid.getParentContainer() + else + if comp = "." + then result = mid + else ( + result = mid.getAChildContainer() and + result.getBaseName() = comp + ) + ) + } + + /** + * Gets the file or folder obtained by appending `relativePath` onto `f`. + */ + pragma[nomagic] + Container append(Folder f, string relativePath) { + exists(int last | + last = getNumberOfComponents(relativePath) - 1 and + result = appendStep(f, relativePath, last) + ) + } + } + } } /** A file. */