diff --git a/rust/ql/lib/codeql/rust/AstConsistency.qll b/rust/ql/lib/codeql/rust/AstConsistency.qll index 0c9359d6569ca..91fefd0d80883 100644 --- a/rust/ql/lib/codeql/rust/AstConsistency.qll +++ b/rust/ql/lib/codeql/rust/AstConsistency.qll @@ -73,6 +73,14 @@ query predicate multiplePositions(Element parent, int pos1, int pos2, string acc pos1 != pos2 } +private import codeql.rust.elements.internal.PathResolution + +/** Holds if `p` may resolve to multiple items. */ +query predicate multiplePathResolutions(Path p, Item i) { + i = resolvePath(p) and + strictcount(resolvePath(p)) > 1 +} + /** * Gets counts of abstract syntax tree inconsistencies of each type. */ @@ -98,4 +106,7 @@ int getAstInconsistencyCounts(string type) { or type = "Multiple positions" and result = count(Element e | multiplePositions(_, _, _, _, e) | e) + or + type = "Multiple path resolutions" and + result = count(Path p | multiplePathResolutions(p, _) | p) } diff --git a/rust/ql/lib/codeql/rust/elements/internal/CallExprBaseImpl.qll b/rust/ql/lib/codeql/rust/elements/internal/CallExprBaseImpl.qll index f67c8f2892571..e93abe999d26d 100644 --- a/rust/ql/lib/codeql/rust/elements/internal/CallExprBaseImpl.qll +++ b/rust/ql/lib/codeql/rust/elements/internal/CallExprBaseImpl.qll @@ -16,6 +16,7 @@ module Impl { private import codeql.rust.elements.internal.MethodCallExprImpl::Impl private import codeql.rust.elements.internal.CallExprImpl::Impl private import codeql.rust.elements.internal.PathExprImpl::Impl + private import codeql.rust.elements.internal.PathResolution pragma[nomagic] Resolvable getCallResolvable(CallExprBase call) { @@ -33,6 +34,10 @@ module Impl { * Gets the target callable of this call, if a unique such target can * be statically resolved. */ - Callable getStaticTarget() { getCallResolvable(this).resolvesAsItem(result) } + Callable getStaticTarget() { + getCallResolvable(this).resolvesAsItem(result) + or + result = resolvePath(this.(CallExpr).getFunction().(PathExpr).getPath()) + } } } diff --git a/rust/ql/lib/codeql/rust/elements/internal/PathResolution.qll b/rust/ql/lib/codeql/rust/elements/internal/PathResolution.qll new file mode 100644 index 0000000000000..da6ea47395ccd --- /dev/null +++ b/rust/ql/lib/codeql/rust/elements/internal/PathResolution.qll @@ -0,0 +1,440 @@ +/** Provides functionality for resolving paths. */ + +private import rust +private import codeql.rust.elements.internal.generated.ParentChild + +/** + * An item that may be referred to by a path, and which is a node in + * the _item graph_. + * + * The item graph is a labeled directed graph, where an edge + * `item1 --name--> item2` means that `item2` is available inside the + * scope of `item1` under the name `name`. For example, if we have + * + * ```rust + * mod m1 { + * mod m2 { } + * } + * ``` + * + * then there is an edge `m1 --m2--> m1::m2`. + * + * Source files are also considered nodes in the item graph, and for + * each source file `f` there is an edge `f --name--> item` when `f` + * declares `item` with the name `name`. + * + * For imports like + * + * ```rust + * mod m1 { + * mod m2; + * use m2::foo; + * } + * ``` + * + * we first generate an edge `m1::m2 --name--> f::item`, where `item` is + * any item (named `name`) inside the imported source file `f`. Using this + * edge, `m2::foo` can resolve to `f::foo`, which results in the edge + * `m1::use m2 --foo--> f::foo`. Lastly, all edges out of `use` nodes are + * lifted to predecessors in the graph, so we get an edge `m1 --foo--> f::foo`. + * + * + * References: + * - https://doc.rust-lang.org/reference/items/modules.html + * - 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 + */ +abstract class ItemNode extends AstNode { + /** Gets the (original) name of this item. */ + abstract string getName(); + + /** Gets the visibility of this item, if any. */ + abstract Visibility getVisibility(); + + /** Holds if this item is declared as `pub`. */ + bindingset[this] + pragma[inline_late] + predicate isPublic() { exists(this.getVisibility()) } + + /** Gets an element that has this item as immediately enlcosing item. */ + pragma[nomagic] + Element getADescendant() { + getImmediateParent(result) = this + or + exists(Element mid | + mid = this.getADescendant() and + getImmediateParent(result) = mid and + not mid instanceof ItemNode + ) + } + + /** Gets the immediately enclosing item of this item, if any. */ + pragma[nomagic] + ItemNode getImmediateParent() { this = result.getADescendant() } + + pragma[nomagic] + private ItemNode getItemAncestorNonModule() { + result = this.getImmediateParent() + or + exists(ItemNode mid | + mid = this.getItemAncestorNonModule() and + result = mid.getImmediateParent() and + not mid instanceof ModuleLikeNode + ) + } + + /** Gets the immediately enclosing module (or source file) of this item. */ + pragma[nomagic] + ModuleLikeNode getImmediateParentModule() { result = this.getItemAncestorNonModule() } + + /** Gets a successor named `name` of this item, if any. */ + pragma[nomagic] + ItemNode getASuccessor(string name) { + sourceFileEdge(this, name, result) + or + this = result.getImmediateParent() and + name = result.getName() + or + fileImportEdge(this, name, result) + or + useImportEdge(this, name, result) + or + // items made available through `use` are available to nodes that contain the `use` + exists(UseItemNode use | + use = this.getASuccessor(_) and + result = use.getASuccessor(name) + ) + or + // items made available through macro calls are available to nodes that contain the macro call + exists(MacroCallItemNode call | + call = this.getASuccessor(_) and + result = call.getASuccessor(name) + ) + or + name = "super" and + if this instanceof Module + then result = this.getImmediateParentModule() + else result = this.getImmediateParentModule().getImmediateParentModule() + or + name = "self" and + if this instanceof Module then result = this else result = this.getImmediateParentModule() + or + name = "crate" and + result.(SourceFileItemNode).getFile() = this.getFile() + } +} + +/** A module or a source file. */ +abstract private class ModuleLikeNode extends ItemNode { } + +private class SourceFileItemNode extends ModuleLikeNode, SourceFile { + override string getName() { result = "(source file)" } + + override Visibility getVisibility() { none() } +} + +private class ConstItemNode extends ItemNode instanceof Const { + override string getName() { result = Const.super.getName().getText() } + + override Visibility getVisibility() { result = Const.super.getVisibility() } +} + +private class EnumItemNode extends ItemNode instanceof Enum { + override string getName() { result = Enum.super.getName().getText() } + + override Visibility getVisibility() { result = Enum.super.getVisibility() } +} + +private class VariantItemNode extends ItemNode instanceof Variant { + override string getName() { result = Variant.super.getName().getText() } + + override Visibility getVisibility() { result = Variant.super.getVisibility() } +} + +private class FunctionItemNode extends ItemNode instanceof Function { + override string getName() { result = Function.super.getName().getText() } + + override Visibility getVisibility() { result = Function.super.getVisibility() } +} + +private class ImplItemNode extends ItemNode instanceof Impl { + override string getName() { result = "(impl)" } + + override Visibility getVisibility() { none() } +} + +private class MacroCallItemNode extends ItemNode instanceof MacroCall { + override string getName() { result = "(macro call)" } + + override Visibility getVisibility() { none() } +} + +private class ModuleItemNode extends ModuleLikeNode instanceof Module { + override string getName() { result = Module.super.getName().getText() } + + override Visibility getVisibility() { result = Module.super.getVisibility() } +} + +private class StructItemNode extends ItemNode instanceof Struct { + override string getName() { result = Struct.super.getName().getText() } + + override Visibility getVisibility() { result = Struct.super.getVisibility() } +} + +private class TraitItemNode extends ItemNode instanceof Trait { + override string getName() { result = Trait.super.getName().getText() } + + override Visibility getVisibility() { result = Trait.super.getVisibility() } +} + +private class UnionItemNode extends ItemNode instanceof Union { + override string getName() { result = Union.super.getName().getText() } + + override Visibility getVisibility() { result = Union.super.getVisibility() } +} + +private class UseItemNode extends ItemNode instanceof Use { + override string getName() { result = "(use)" } + + override Visibility getVisibility() { none() } +} + +private class BlockExprItemNode extends ItemNode instanceof BlockExpr { + override string getName() { result = "(block expr)" } + + override Visibility getVisibility() { none() } +} + +private predicate sourceFileEdge(SourceFile f, string name, ItemNode item) { + item = f.getAnItem() and + name = item.getName() +} + +/** Holds if `f` is available as `mod name;` inside `folder`. */ +private predicate fileModule(SourceFile f, string name, Folder folder) { + exists(File file | file = f.getFile() | + file.getBaseName() = name + ".rs" and + folder = file.getParentContainer() + or + exists(Folder encl | + file.getBaseName() = "mod.rs" and + encl = file.getParentContainer() and + name = encl.getBaseName() and + folder = encl.getParentContainer() + ) + ) +} + +/** + * Holds if `m` is a `mod name;` module declaration happening in a file named + * `fileName.rs`, inside the folder `parent`. + */ +private predicate modImport(Module m, string fileName, string name, Folder parent) { + exists(File f | + f = m.getFile() and + not m.hasItemList() and + // TODO: handle + // ``` + // #[path = "foo.rs"] + // mod bar; + // ``` + not m.getAnAttr().getMeta().getPath().getPart().getNameRef().getText() = "path" and + name = m.getName().getText() and + parent = f.getParentContainer() and + fileName = f.getStem() + ) +} + +/** Holds if `m` is a `mod name;` item importing file `f`. */ +private predicate fileImport(Module m, SourceFile f) { + exists(string fileName, string name, Folder parent | modImport(m, fileName, name, parent) | + // sibling import + fileModule(f, name, parent) + or + // child import + fileModule(f, name, parent.getFolder(fileName)) + ) +} + +/** + * Holds if `mod` is a `mod name;` item targeting a file resulting in `item` being + * in scope under the name `name`. + */ +private predicate fileImportEdge(Module mod, string name, ItemNode item) { + item.isPublic() and + exists(SourceFile f | + fileImport(mod, f) and + sourceFileEdge(f, name, item) + ) +} + +pragma[nomagic] +private predicate useTreeIsGlobImport(UseTree use) { + // TODO: the extractor should provide this information + use.getLocation() != use.getPath().getLocation() and + not use.hasUseTreeList() and + not use.hasRename() +} + +private predicate useTreeDeclares(UseTree tree, string name) { + not useTreeIsGlobImport(tree) and + not exists(tree.getUseTreeList()) and + ( + name = tree.getRename().getName().getText() and + name != "_" + or + not tree.hasRename() and + name = tree.getPath().getPart().getNameRef().getText() + ) + or + exists(UseTree mid | + useTreeDeclares(mid, name) and + mid = tree.getUseTreeList().getAUseTree() + ) +} + +/** + * Holds if `item` explicitly declares a sub item named `name`. This includes + * items declared by `use` statements, except for glob imports. + */ +pragma[nomagic] +private predicate declares(ItemNode item, string name) { + exists(ItemNode child | child.getImmediateParent() = item | + child.getName() = name + or + useTreeDeclares(child.(Use).getUseTree(), name) + ) + or + exists(MacroCallItemNode call | + declares(call, name) and + call.getImmediateParent() = item + ) +} + +private class RelevantPath extends Path { + RelevantPath() { not this = any(VariableAccess va).(PathExpr).getPath() } + + pragma[nomagic] + predicate isRoot(string name) { + not exists(this.getQualifier()) and + not this = any(UseTreeList list).getAUseTree().getPath() and + name = this.getPart().getNameRef().getText() + } +} + +/** + * Holds if the root path `root` references an item named `name`, and `name` + * may be looked up inside enclosing item `encl`. + */ +pragma[nomagic] +private predicate rootPathLookup(RelevantPath root, string name, ItemNode encl) { + // lookup in the immediately enclosing item + root.isRoot(name) and + encl.getADescendant() = root + or + // lookup in an outer scope, but only if the item is not declared in inner scope + exists(ItemNode mid | + rootPathLookup(root, name, mid) and + not declares(mid, name) + | + // nested modules do not have unqualified access to items from outer modules, + // except for items declared at top-level in the source file + if mid instanceof Module + then encl.(SourceFileItemNode) = mid.getImmediateParent+() + else encl = mid.getImmediateParent() + ) +} + +/** Gets the item that `path` resolves to, if any. */ +cached +ItemNode resolvePath(RelevantPath path) { + exists(ItemNode encl, string name | + rootPathLookup(path, name, encl) and + result = encl.getASuccessor(name) + ) + or + exists(ItemNode q, string name | + q = resolvePathQualifier(path, name) and + result = q.getASuccessor(name) + ) + or + result = resolveUseTreeListItem(_, _, path) +} + +pragma[nomagic] +private ItemNode resolvePathQualifier(RelevantPath path, string name) { + result = resolvePath(path.getQualifier()) and + name = path.getPart().getNameRef().getText() +} + +private predicate isUseTreeSubPath(UseTree tree, RelevantPath path) { + path = tree.getPath() + or + exists(RelevantPath mid | + isUseTreeSubPath(tree, mid) and + path = mid.getQualifier() + ) +} + +pragma[nomagic] +private predicate isUseTreeSubPathUnqualified(UseTree tree, RelevantPath path, string name) { + isUseTreeSubPath(tree, path) and + not exists(path.getQualifier()) and + name = path.getPart().getNameRef().getText() +} + +pragma[nomagic] +private ItemNode resolveUseTreeListItem(Use use, UseTree tree, RelevantPath path) { + exists(UseTree midTree, ItemNode mid, string name | + mid = resolveUseTreeListItem(use, midTree) and + tree = midTree.getUseTreeList().getAUseTree() and + isUseTreeSubPathUnqualified(tree, path, pragma[only_bind_into](name)) and + result = mid.getASuccessor(pragma[only_bind_into](name)) + ) + or + exists(ItemNode q, string name | + q = resolveUseTreeListItemQualifier(use, tree, path, name) and + result = q.getASuccessor(name) + ) +} + +pragma[nomagic] +private ItemNode resolveUseTreeListItemQualifier( + Use use, UseTree tree, RelevantPath path, string name +) { + result = resolveUseTreeListItem(use, tree, path.getQualifier()) and + name = path.getPart().getNameRef().getText() +} + +pragma[nomagic] +private ItemNode resolveUseTreeListItem(Use use, UseTree tree) { + tree = use.getUseTree() and + result = resolvePath(tree.getPath()) + or + result = resolveUseTreeListItem(use, tree, tree.getPath()) +} + +/** Holds if `use` imports `item` as `name`. */ +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 + if useTreeIsGlobImport(tree) + then + exists(ItemNode encl | + encl.getADescendant() = use and + item = used.getASuccessor(name) and + // glob imports can be shadowed + not declares(encl, name) + ) + else item = used + | + not tree.hasRename() and + name = item.getName() + or + name = tree.getRename().getName().getText() and + name != "_" + ) +} diff --git a/rust/ql/test/library-tests/modules/CONSISTENCY/AstConsistency.expected b/rust/ql/test/library-tests/modules/CONSISTENCY/AstConsistency.expected new file mode 100644 index 0000000000000..9d4e175192b44 --- /dev/null +++ b/rust/ql/test/library-tests/modules/CONSISTENCY/AstConsistency.expected @@ -0,0 +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 | diff --git a/rust/ql/test/library-tests/modules/main.rs b/rust/ql/test/library-tests/modules/main.rs new file mode 100644 index 0000000000000..a73e7ec76cde8 --- /dev/null +++ b/rust/ql/test/library-tests/modules/main.rs @@ -0,0 +1,155 @@ +mod my; // I1 + +use my::*; // $ item=I1 + +use my::nested::nested1::nested2::*; // $ item=I3 + +mod my2; // I14 + +use my2::*; // $ item=I14 + +use my2::nested2::nested3::nested4::{f, g}; // $ item=I11 item=I12 item=I13 + +mod m1 { + fn f() { + println!("main.rs::m1::f"); + } // I16 + + pub mod m2 { + fn f() { + println!("main.rs::m1::m2::f"); + } // I18 + + pub fn g() { + println!("main.rs::m1::m2::g"); + f(); // $ item=I18 + super::f(); // $ item=I16 + } // I19 + + pub mod m3 { + use super::f; // $ item=I18 + pub fn h() { + println!("main.rs::m1::m2::m3::h"); + f(); // $ item=I18 + } // I21 + } // I20 + } // I17 +} // I15 + +mod m4 { + use super::m1::m2::g; // $ item=I19 + + pub fn i() { + println!("main.rs::m4::i"); + g(); // $ item=I19 + } // I23 +} // I22 + +struct Foo {} // I24 + +fn h() { + println!("main.rs::h"); + + struct Foo {} // I26 + + fn f() { + use m1::m2::g; // $ item=I19 + g(); // $ item=I19 + + struct Foo {} // I28 + println!("main.rs::h::f"); + let _ = Foo {}; // $ item=I28 + } // I27 + + let _ = Foo {}; // $ item=I26 + + f(); // $ item=I27 + + self::i(); // $ item=I29 +} // I25 + +fn i() { + println!("main.rs::i"); + + let _ = Foo {}; // $ item=I24 + + { + struct Foo { + x: i32, + } // I30 + + let _ = Foo { x: 0 }; // $ item=I30 + } +} // I29 + +use my2::nested2 as my2_nested2_alias; // $ item=I8 + +use my2_nested2_alias::nested3::{nested4::f as f_alias, nested4::g as g_alias, nested4::*}; // $ item=I10 item=I12 item=I13 item=I11 + +macro_rules! fn_in_macro { + ($e:expr) => { + fn f_defined_in_macro() { + $e + } + }; +} + +fn j() { + println!("main.rs::j"); + fn_in_macro!(println!("main.rs::j::f")); + f_defined_in_macro(); // $ item=f_defined_in_macro +} // I31 + +mod m5 { + pub fn f() { + println!("main.rs::m5::f"); + } // I33 +} // I32 + +mod m6 { + fn f() { + println!("main.rs::m6::f"); + } // I35 + + pub fn g() { + println!("main.rs::m6::g"); + // this import shadows the definition `I35`, which we don't currently handle + use super::m5::*; // $ item=I32 + f(); // $ item=I33 $ SPURIOUS: item=I35 + } // I36 +} // I34 + +mod m7 { + pub enum MyEnum { + A(i32), // I42 + B { x: i32 }, // I43 + C, // I44 + } // I41 + + #[rustfmt::skip] + pub fn f() -> MyEnum // $ item=I41 + { + println!("main.rs::m7::f"); + let _ = MyEnum::A(0); // $ item=I42 + let _ = MyEnum::B { x: 0 }; // $ item=I43 + MyEnum::C // $ item=I44 + } // I45 +} // I40 + +fn main() { + my::nested::nested1::nested2::f(); // $ item=I4 + my::f(); // $ item=I38 + nested2::nested3::nested4::f(); // $ item=I12 + f(); // $ item=I12 + g(); // $ item=I13 + crate::h(); // $ item=I25 + m1::m2::g(); // $ item=I19 + m1::m2::m3::h(); // $ item=I21 + m4::i(); // $ item=I23 + h(); // $ item=I25 + f_alias(); // $ item=I12 + g_alias(); // $ item=I13 + j(); // $ item=I31 + m6::g(); // $ item=I36 + m7::f(); // $ item=I45 +} diff --git a/rust/ql/test/library-tests/modules/modules.expected b/rust/ql/test/library-tests/modules/modules.expected new file mode 100644 index 0000000000000..89343a4318138 --- /dev/null +++ b/rust/ql/test/library-tests/modules/modules.expected @@ -0,0 +1,119 @@ +testFailures +mod +| lib.rs:1:1:1:7 | mod my | +| main.rs:1:1:1:7 | mod my | +| main.rs:7:1:7:8 | mod my2 | +| main.rs:13:1:37:1 | mod m1 | +| main.rs:18:5:36:5 | mod m2 | +| main.rs:29:9:35:9 | mod m3 | +| main.rs:39:1:46:1 | mod m4 | +| main.rs:103:1:107:1 | mod m5 | +| main.rs:109:1:120:1 | mod m6 | +| main.rs:122:1:137:1 | mod m7 | +| 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 | +| my.rs:1:1:1:15 | mod nested | +| my/nested.rs:1:1:17:1 | mod nested1 | +| my/nested.rs:2:5:11:5 | mod nested2 | +resolvePath +| main.rs:3:5:3:6 | my | main.rs:1:1:1:7 | mod my | +| main.rs:5:5:5:6 | my | main.rs:1:1:1:7 | mod my | +| main.rs:5:5:5:14 | ...::nested | my.rs:1:1:1:15 | mod nested | +| main.rs:5:5:5:23 | ...::nested1 | my/nested.rs:1:1:17:1 | mod nested1 | +| main.rs:5:5:5:32 | ...::nested2 | my/nested.rs:2:5:11:5 | mod nested2 | +| main.rs:9:5:9:7 | my2 | main.rs:7:1:7:8 | mod my2 | +| main.rs:11:5:11:7 | my2 | main.rs:7:1:7:8 | mod my2 | +| main.rs:11:5:11:16 | ...::nested2 | my2/mod.rs:1:1:1:16 | mod nested2 | +| main.rs:11:5:11:25 | ...::nested3 | my2/nested2.rs:1:1:11:1 | mod nested3 | +| main.rs:11:5:11:34 | ...::nested4 | my2/nested2.rs:2:5:10:5 | mod nested4 | +| main.rs:11:38:11:38 | f | my2/nested2.rs:3:9:5:9 | fn f | +| main.rs:11:41:11:41 | g | my2/nested2.rs:7:9:9:9 | fn g | +| main.rs:25:13:25:13 | f | main.rs:19:9:21:9 | fn f | +| main.rs:26:13:26:17 | super | main.rs:13:1:37:1 | mod m1 | +| main.rs:26:13:26:20 | ...::f | main.rs:14:5:16:5 | fn f | +| 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:155: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 | +| main.rs:44:9:44:9 | g | main.rs:23:9:27:9 | fn g | +| main.rs:56:13:56:14 | m1 | main.rs:13:1:37:1 | mod m1 | +| main.rs:56:13:56:18 | ...::m2 | main.rs:18:5:36:5 | mod m2 | +| main.rs:56:13:56:21 | ...::g | main.rs:23:9:27:9 | fn g | +| main.rs:57:9:57:9 | g | main.rs:23:9:27:9 | fn g | +| 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:155: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 | +| main.rs:85:5:85:7 | my2 | main.rs:7:1:7:8 | mod my2 | +| main.rs:85:5:85:16 | ...::nested2 | my2/mod.rs:1:1:1:16 | mod nested2 | +| main.rs:87:5:87:21 | my2_nested2_alias | my2/mod.rs:1:1:1:16 | mod nested2 | +| main.rs:87:5:87:30 | ...::nested3 | my2/nested2.rs:1:1:11:1 | mod nested3 | +| main.rs:87:34:87:40 | nested4 | my2/nested2.rs:2:5:10:5 | mod nested4 | +| main.rs:87:34:87:43 | ...::f | my2/nested2.rs:3:9:5:9 | fn f | +| main.rs:87:57:87:63 | nested4 | my2/nested2.rs:2:5:10:5 | mod nested4 | +| 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:155: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 | +| main.rs:130:19:130:24 | MyEnum | main.rs:123:5:127:5 | enum MyEnum | +| main.rs:133:17:133:22 | MyEnum | main.rs:123:5:127:5 | enum MyEnum | +| main.rs:133:17:133:25 | ...::A | main.rs:124:9:124:14 | A | +| main.rs:134:17:134:22 | MyEnum | main.rs:123:5:127:5 | enum MyEnum | +| main.rs:134:17:134:25 | ...::B | main.rs:124:23:125:20 | B | +| main.rs:135:9:135:14 | MyEnum | main.rs:123:5:127:5 | enum MyEnum | +| main.rs:135:9:135:17 | ...::C | main.rs:125:23:126:9 | C | +| main.rs:140:5:140:6 | my | main.rs:1:1:1:7 | mod my | +| main.rs:140:5:140:14 | ...::nested | my.rs:1:1:1:15 | mod nested | +| main.rs:140:5:140:23 | ...::nested1 | my/nested.rs:1:1:17:1 | mod nested1 | +| main.rs:140:5:140:32 | ...::nested2 | my/nested.rs:2:5:11:5 | mod nested2 | +| main.rs:140:5:140:35 | ...::f | my/nested.rs:3:9:5:9 | fn f | +| main.rs:141:5:141:6 | my | main.rs:1:1:1:7 | mod my | +| main.rs:141:5:141:9 | ...::f | my.rs:5:1:7:1 | fn f | +| main.rs:142:5:142:11 | nested2 | my2/mod.rs:1:1:1:16 | mod nested2 | +| main.rs:142:5:142:20 | ...::nested3 | my2/nested2.rs:1:1:11:1 | mod nested3 | +| main.rs:142:5:142:29 | ...::nested4 | my2/nested2.rs:2:5:10:5 | mod nested4 | +| main.rs:142:5:142:32 | ...::f | my2/nested2.rs:3:9:5:9 | fn f | +| main.rs:143:5:143:5 | f | my2/nested2.rs:3:9:5:9 | fn f | +| main.rs:144:5:144:5 | g | my2/nested2.rs:7:9:9:9 | fn g | +| main.rs:145:5:145:9 | crate | main.rs:1:1:155:2 | SourceFile | +| main.rs:145:5:145:12 | ...::h | main.rs:50:1:69:1 | fn h | +| main.rs:146:5:146:6 | m1 | main.rs:13:1:37:1 | mod m1 | +| main.rs:146:5:146:10 | ...::m2 | main.rs:18:5:36:5 | mod m2 | +| main.rs:146:5:146:13 | ...::g | main.rs:23:9:27:9 | fn g | +| main.rs:147:5:147:6 | m1 | main.rs:13:1:37:1 | mod m1 | +| main.rs:147:5:147:10 | ...::m2 | main.rs:18:5:36:5 | mod m2 | +| main.rs:147:5:147:14 | ...::m3 | main.rs:29:9:35:9 | mod m3 | +| main.rs:147:5:147:17 | ...::h | main.rs:30:27:34:13 | fn h | +| main.rs:148:5:148:6 | m4 | main.rs:39:1:46:1 | mod m4 | +| main.rs:148:5:148:9 | ...::i | main.rs:42:5:45:5 | fn i | +| main.rs:149:5:149:5 | h | main.rs:50:1:69:1 | fn h | +| main.rs:150:5:150:11 | f_alias | my2/nested2.rs:3:9:5:9 | fn f | +| main.rs:151:5:151:11 | g_alias | my2/nested2.rs:7:9:9:9 | fn g | +| main.rs:152:5:152:5 | j | main.rs:97:1:101:1 | fn j | +| main.rs:153:5:153:6 | m6 | main.rs:109:1:120:1 | mod m6 | +| main.rs:153:5:153:9 | ...::g | main.rs:114:5:119:5 | fn g | +| main.rs:154:5:154:6 | m7 | main.rs:122:1:137:1 | mod m7 | +| main.rs:154:5:154:9 | ...::f | main.rs:129:5:136:5 | fn 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 | +| my2/mod.rs:5:5:5:32 | ...::f | my2/nested2.rs:3:9:5:9 | fn f | +| 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 | +| my.rs:11:5:11:5 | g | my/nested.rs:19:1:22:1 | fn g | +| my/nested.rs:9:13:9:13 | f | my/nested.rs:3:9:5:9 | fn f | +| my/nested.rs:15:9:15:15 | nested2 | my/nested.rs:2:5:11:5 | mod nested2 | +| my/nested.rs:15:9:15:18 | ...::f | my/nested.rs:3:9:5:9 | fn f | +| my/nested.rs:21:5:21:11 | nested1 | my/nested.rs:1:1:17:1 | mod nested1 | +| my/nested.rs:21:5:21:20 | ...::nested2 | my/nested.rs:2:5:11:5 | mod nested2 | +| my/nested.rs:21:5:21:23 | ...::f | my/nested.rs:3:9:5:9 | fn f | diff --git a/rust/ql/test/library-tests/modules/modules.ql b/rust/ql/test/library-tests/modules/modules.ql new file mode 100644 index 0000000000000..df3828a4e123e --- /dev/null +++ b/rust/ql/test/library-tests/modules/modules.ql @@ -0,0 +1,49 @@ +import rust +import codeql.rust.elements.internal.PathResolution +import utils.test.InlineExpectationsTest + +query predicate mod(Module m) { any() } + +query predicate resolvePath(Path p, ItemNode i) { i = resolvePath(p) } + +module ResolveTest implements TestSig { + string getARelevantTag() { result = "item" } + + private predicate itemAt(ItemNode i, string filepath, int line, boolean inMacro) { + i.getLocation().hasLocationInfo(filepath, _, _, line, _) and + if i.isInMacroExpansion() then inMacro = true else inMacro = false + } + + private predicate commmentAt(string text, string filepath, int line) { + exists(Comment c | + c.getLocation().hasLocationInfo(filepath, line, _, _, _) and + c.getCommentText() = text + ) + } + + private predicate item(ItemNode i, string value) { + exists(string filepath, int line, boolean inMacro | itemAt(i, filepath, line, inMacro) | + commmentAt(value, filepath, line) and + inMacro = false + or + ( + not commmentAt(_, filepath, line) + or + inMacro = true + ) and + value = i.getName() + ) + } + + predicate hasActualResult(Location location, string element, string tag, string value) { + exists(Path p | + not p = any(Path parent).getQualifier() and + location = p.getLocation() and + element = p.toString() and + item(resolvePath(p), value) and + tag = "item" + ) + } +} + +import MakeTest diff --git a/rust/ql/test/library-tests/modules/my.rs b/rust/ql/test/library-tests/modules/my.rs new file mode 100644 index 0000000000000..fd9511a21181a --- /dev/null +++ b/rust/ql/test/library-tests/modules/my.rs @@ -0,0 +1,12 @@ +pub mod nested; // I37 + +use nested::g; // $ item=I7 + +pub fn f() { + println!("my.rs::f"); +} // I38 + +pub fn h() { + println!("my.rs::h"); + g(); // $ item=I7 +} // I39 diff --git a/rust/ql/test/library-tests/modules/my/nested.rs b/rust/ql/test/library-tests/modules/my/nested.rs new file mode 100644 index 0000000000000..639ed241ae3ea --- /dev/null +++ b/rust/ql/test/library-tests/modules/my/nested.rs @@ -0,0 +1,22 @@ +pub mod nested1 { + pub mod nested2 { + pub fn f() { + println!("nested.rs:nested1::nested2::f"); + } // I4 + + fn g() { + println!("nested.rs:nested1::nested2::g"); + f(); // $ item=I4 + } // I5 + } // I3 + + fn g() { + println!("nested.rs:nested1::g"); + nested2::f(); // $ item=I4 + } // I6 +} // I1 + +pub fn g() { + println!("nested.rs::g"); + nested1::nested2::f(); // $ item=I4 +} // I7 diff --git a/rust/ql/test/library-tests/modules/my2/mod.rs b/rust/ql/test/library-tests/modules/my2/mod.rs new file mode 100644 index 0000000000000..fcd82bf02c881 --- /dev/null +++ b/rust/ql/test/library-tests/modules/my2/mod.rs @@ -0,0 +1,6 @@ +pub mod nested2; // I8 + +fn g() { + println!("mod.rs::g"); + nested2::nested3::nested4::f(); // $ item=I12 +} // I9 diff --git a/rust/ql/test/library-tests/modules/my2/nested2.rs b/rust/ql/test/library-tests/modules/my2/nested2.rs new file mode 100644 index 0000000000000..dec8b8a2377e7 --- /dev/null +++ b/rust/ql/test/library-tests/modules/my2/nested2.rs @@ -0,0 +1,11 @@ +pub mod nested3 { + pub mod nested4 { + pub fn f() { + println!("nested2.rs::nested3::nested4::f"); + } // I12 + + pub fn g() { + println!("nested2.rs::nested3::nested4::g"); + } // I13 + } // I11 +} // I10 diff --git a/rust/ql/test/query-tests/diagnostics/AstConsistencyCounts.expected b/rust/ql/test/query-tests/diagnostics/AstConsistencyCounts.expected index 7f8d388fdc509..1a79d92303dc2 100644 --- a/rust/ql/test/query-tests/diagnostics/AstConsistencyCounts.expected +++ b/rust/ql/test/query-tests/diagnostics/AstConsistencyCounts.expected @@ -1,6 +1,7 @@ | Multiple children | 0 | | Multiple locations | 0 | | Multiple parents | 0 | +| Multiple path resolutions | 0 | | Multiple positions | 0 | | Multiple primary QL classes | 0 | | Multiple toStrings | 0 | diff --git a/rust/ql/test/query-tests/security/CWE-696/BadCTorInitialization.expected b/rust/ql/test/query-tests/security/CWE-696/BadCTorInitialization.expected index 311e1828f5350..9d8fc2524718f 100644 --- a/rust/ql/test/query-tests/security/CWE-696/BadCTorInitialization.expected +++ b/rust/ql/test/query-tests/security/CWE-696/BadCTorInitialization.expected @@ -1,44 +1,49 @@ #select -| test.rs:31:9:31:25 | ...::stdout(...) | test.rs:29:1:29:13 | Attr | test.rs:31:9:31:25 | ...::stdout(...) | Call to ...::stdout(...) in a function with the ctor attribute. | -| test.rs:36:9:36:25 | ...::stdout(...) | test.rs:34:1:34:13 | Attr | test.rs:36:9:36:25 | ...::stdout(...) | Call to ...::stdout(...) in a function with the dtor attribute. | -| test.rs:43:9:43:25 | ...::stdout(...) | test.rs:40:1:40:13 | Attr | test.rs:43:9:43:25 | ...::stdout(...) | Call to ...::stdout(...) in a function with the dtor attribute. | -| test.rs:53:9:53:16 | stdout(...) | test.rs:51:1:51:7 | Attr | test.rs:53:9:53:16 | stdout(...) | Call to stdout(...) in a function with the ctor attribute. | -| test.rs:58:9:58:16 | stderr(...) | test.rs:56:1:56:7 | Attr | test.rs:58:9:58:16 | stderr(...) | Call to stderr(...) in a function with the ctor attribute. | -| test.rs:63:14:63:28 | ...::_print(...) | test.rs:61:1:61:7 | Attr | test.rs:63:14:63:28 | ...::_print(...) | Call to ...::_print(...) in a function with the ctor attribute. | -| test.rs:69:9:69:24 | ...::stdin(...) | test.rs:66:1:66:7 | Attr | test.rs:69:9:69:24 | ...::stdin(...) | Call to ...::stdin(...) in a function with the ctor attribute. | -| test.rs:90:5:90:35 | ...::sleep(...) | test.rs:88:1:88:7 | Attr | test.rs:90:5:90:35 | ...::sleep(...) | Call to ...::sleep(...) in a function with the ctor attribute. | -| test.rs:97:5:97:23 | ...::exit(...) | test.rs:95:1:95:7 | Attr | test.rs:97:5:97:23 | ...::exit(...) | Call to ...::exit(...) in a function with the ctor attribute. | -| test.rs:126:9:126:16 | stderr(...) | test.rs:129:1:129:7 | Attr | test.rs:126:9:126:16 | stderr(...) | Call to stderr(...) in a function with the ctor attribute. | -| test.rs:126:9:126:16 | stderr(...) | test.rs:145:1:145:7 | Attr | test.rs:126:9:126:16 | stderr(...) | Call to stderr(...) in a function with the ctor attribute. | -| test.rs:126:9:126:44 | ... .write_all(...) | test.rs:129:1:129:7 | Attr | test.rs:126:9:126:44 | ... .write_all(...) | Call to ... .write_all(...) in a function with the ctor attribute. | -| test.rs:126:9:126:44 | ... .write_all(...) | test.rs:145:1:145:7 | Attr | test.rs:126:9:126:44 | ... .write_all(...) | Call to ... .write_all(...) in a function with the ctor attribute. | -| test.rs:171:5:171:15 | ...::stdout(...) | test.rs:169:1:169:7 | Attr | test.rs:171:5:171:15 | ...::stdout(...) | Call to ...::stdout(...) in a function with the ctor attribute. | +| test.rs:30:9:30:25 | ...::stdout(...) | test.rs:28:1:28:13 | Attr | test.rs:30:9:30:25 | ...::stdout(...) | Call to ...::stdout(...) in a function with the ctor attribute. | +| test.rs:35:9:35:25 | ...::stdout(...) | test.rs:33:1:33:13 | Attr | test.rs:35:9:35:25 | ...::stdout(...) | Call to ...::stdout(...) in a function with the dtor attribute. | +| test.rs:42:9:42:25 | ...::stdout(...) | test.rs:39:1:39:13 | Attr | test.rs:42:9:42:25 | ...::stdout(...) | Call to ...::stdout(...) in a function with the dtor attribute. | +| test.rs:52:9:52:16 | stdout(...) | test.rs:50:1:50:7 | Attr | test.rs:52:9:52:16 | stdout(...) | Call to stdout(...) in a function with the ctor attribute. | +| test.rs:57:9:57:16 | stderr(...) | test.rs:55:1:55:7 | Attr | test.rs:57:9:57:16 | stderr(...) | Call to stderr(...) in a function with the ctor attribute. | +| test.rs:62:14:62:28 | ...::_print(...) | test.rs:60:1:60:7 | Attr | test.rs:62:14:62:28 | ...::_print(...) | Call to ...::_print(...) in a function with the ctor attribute. | +| test.rs:68:9:68:24 | ...::stdin(...) | test.rs:65:1:65:7 | Attr | test.rs:68:9:68:24 | ...::stdin(...) | Call to ...::stdin(...) in a function with the ctor attribute. | +| test.rs:89:5:89:35 | ...::sleep(...) | test.rs:87:1:87:7 | Attr | test.rs:89:5:89:35 | ...::sleep(...) | Call to ...::sleep(...) in a function with the ctor attribute. | +| test.rs:96:5:96:23 | ...::exit(...) | test.rs:94:1:94:7 | Attr | test.rs:96:5:96:23 | ...::exit(...) | Call to ...::exit(...) in a function with the ctor attribute. | +| test.rs:125:9:125:16 | stderr(...) | test.rs:128:1:128:7 | Attr | test.rs:125:9:125:16 | stderr(...) | Call to stderr(...) in a function with the ctor attribute. | +| test.rs:125:9:125:16 | stderr(...) | test.rs:144:1:144:7 | Attr | test.rs:125:9:125:16 | stderr(...) | Call to stderr(...) in a function with the ctor attribute. | +| test.rs:125:9:125:16 | stderr(...) | test.rs:150:1:150:7 | Attr | test.rs:125:9:125:16 | stderr(...) | Call to stderr(...) in a function with the ctor attribute. | +| test.rs:125:9:125:44 | ... .write_all(...) | test.rs:128:1:128:7 | Attr | test.rs:125:9:125:44 | ... .write_all(...) | Call to ... .write_all(...) in a function with the ctor attribute. | +| test.rs:125:9:125:44 | ... .write_all(...) | test.rs:144:1:144:7 | Attr | test.rs:125:9:125:44 | ... .write_all(...) | Call to ... .write_all(...) in a function with the ctor attribute. | +| test.rs:125:9:125:44 | ... .write_all(...) | test.rs:150:1:150:7 | Attr | test.rs:125:9:125:44 | ... .write_all(...) | Call to ... .write_all(...) in a function with the ctor attribute. | +| test.rs:170:5:170:15 | ...::stdout(...) | test.rs:168:1:168:7 | Attr | test.rs:170:5:170:15 | ...::stdout(...) | Call to ...::stdout(...) in a function with the ctor attribute. | edges -| test.rs:29:1:29:13 | Attr | test.rs:29:1:32:1 | fn bad1_1 | -| test.rs:29:1:32:1 | fn bad1_1 | test.rs:31:9:31:25 | ...::stdout(...) | -| test.rs:34:1:34:13 | Attr | test.rs:34:1:37:1 | fn bad1_2 | -| test.rs:34:1:37:1 | fn bad1_2 | test.rs:36:9:36:25 | ...::stdout(...) | -| test.rs:39:1:44:1 | fn bad1_3 | test.rs:43:9:43:25 | ...::stdout(...) | -| test.rs:40:1:40:13 | Attr | test.rs:39:1:44:1 | fn bad1_3 | -| test.rs:51:1:51:7 | Attr | test.rs:51:1:54:1 | fn bad2_1 | -| test.rs:51:1:54:1 | fn bad2_1 | test.rs:53:9:53:16 | stdout(...) | -| test.rs:56:1:56:7 | Attr | test.rs:56:1:59:1 | fn bad2_2 | -| test.rs:56:1:59:1 | fn bad2_2 | test.rs:58:9:58:16 | stderr(...) | -| test.rs:61:1:61:7 | Attr | test.rs:61:1:64:1 | fn bad2_3 | -| test.rs:61:1:64:1 | fn bad2_3 | test.rs:63:14:63:28 | ...::_print(...) | -| test.rs:66:1:66:7 | Attr | test.rs:66:1:70:1 | fn bad2_4 | -| test.rs:66:1:70:1 | fn bad2_4 | test.rs:69:9:69:24 | ...::stdin(...) | -| test.rs:88:1:88:7 | Attr | test.rs:88:1:91:1 | fn bad2_7 | -| test.rs:88:1:91:1 | fn bad2_7 | test.rs:90:5:90:35 | ...::sleep(...) | -| test.rs:95:1:95:7 | Attr | test.rs:95:1:98:1 | fn bad2_8 | -| test.rs:95:1:98:1 | fn bad2_8 | test.rs:97:5:97:23 | ...::exit(...) | -| test.rs:125:1:127:1 | fn call_target3_1 | test.rs:126:9:126:16 | stderr(...) | -| test.rs:125:1:127:1 | fn call_target3_1 | test.rs:126:9:126:44 | ... .write_all(...) | -| test.rs:129:1:129:7 | Attr | test.rs:129:1:132:1 | fn bad3_1 | -| test.rs:129:1:132:1 | fn bad3_1 | test.rs:131:5:131:20 | call_target3_1(...) | -| test.rs:131:5:131:20 | call_target3_1(...) | test.rs:125:1:127:1 | fn call_target3_1 | -| test.rs:145:1:145:7 | Attr | test.rs:145:1:149:1 | fn bad3_3 | -| test.rs:145:1:149:1 | fn bad3_3 | test.rs:147:5:147:20 | call_target3_1(...) | -| test.rs:147:5:147:20 | call_target3_1(...) | test.rs:125:1:127:1 | fn call_target3_1 | -| test.rs:169:1:169:7 | Attr | test.rs:169:1:172:1 | fn bad4_1 | -| test.rs:169:1:172:1 | fn bad4_1 | test.rs:171:5:171:15 | ...::stdout(...) | +| test.rs:28:1:28:13 | Attr | test.rs:28:1:31:1 | fn bad1_1 | +| test.rs:28:1:31:1 | fn bad1_1 | test.rs:30:9:30:25 | ...::stdout(...) | +| test.rs:33:1:33:13 | Attr | test.rs:33:1:36:1 | fn bad1_2 | +| test.rs:33:1:36:1 | fn bad1_2 | test.rs:35:9:35:25 | ...::stdout(...) | +| test.rs:38:1:43:1 | fn bad1_3 | test.rs:42:9:42:25 | ...::stdout(...) | +| test.rs:39:1:39:13 | Attr | test.rs:38:1:43:1 | fn bad1_3 | +| test.rs:50:1:50:7 | Attr | test.rs:50:1:53:1 | fn bad2_1 | +| test.rs:50:1:53:1 | fn bad2_1 | test.rs:52:9:52:16 | stdout(...) | +| test.rs:55:1:55:7 | Attr | test.rs:55:1:58:1 | fn bad2_2 | +| test.rs:55:1:58:1 | fn bad2_2 | test.rs:57:9:57:16 | stderr(...) | +| test.rs:60:1:60:7 | Attr | test.rs:60:1:63:1 | fn bad2_3 | +| test.rs:60:1:63:1 | fn bad2_3 | test.rs:62:14:62:28 | ...::_print(...) | +| test.rs:65:1:65:7 | Attr | test.rs:65:1:69:1 | fn bad2_4 | +| test.rs:65:1:69:1 | fn bad2_4 | test.rs:68:9:68:24 | ...::stdin(...) | +| test.rs:87:1:87:7 | Attr | test.rs:87:1:90:1 | fn bad2_7 | +| test.rs:87:1:90:1 | fn bad2_7 | test.rs:89:5:89:35 | ...::sleep(...) | +| test.rs:94:1:94:7 | Attr | test.rs:94:1:97:1 | fn bad2_8 | +| test.rs:94:1:97:1 | fn bad2_8 | test.rs:96:5:96:23 | ...::exit(...) | +| test.rs:124:1:126:1 | fn call_target3_1 | test.rs:125:9:125:16 | stderr(...) | +| test.rs:124:1:126:1 | fn call_target3_1 | test.rs:125:9:125:44 | ... .write_all(...) | +| test.rs:128:1:128:7 | Attr | test.rs:128:1:131:1 | fn bad3_1 | +| test.rs:128:1:131:1 | fn bad3_1 | test.rs:130:5:130:20 | call_target3_1(...) | +| test.rs:130:5:130:20 | call_target3_1(...) | test.rs:124:1:126:1 | fn call_target3_1 | +| test.rs:144:1:144:7 | Attr | test.rs:144:1:148:1 | fn bad3_3 | +| test.rs:144:1:148:1 | fn bad3_3 | test.rs:146:5:146:20 | call_target3_1(...) | +| test.rs:146:5:146:20 | call_target3_1(...) | test.rs:124:1:126:1 | fn call_target3_1 | +| test.rs:150:1:150:7 | Attr | test.rs:150:1:153:1 | fn bad3_4 | +| test.rs:150:1:153:1 | fn bad3_4 | test.rs:152:5:152:12 | bad3_3(...) | +| test.rs:152:5:152:12 | bad3_3(...) | test.rs:144:1:148:1 | fn bad3_3 | +| test.rs:168:1:168:7 | Attr | test.rs:168:1:171:1 | fn bad4_1 | +| test.rs:168:1:171:1 | fn bad4_1 | test.rs:170:5:170:15 | ...::stdout(...) | diff --git a/rust/ql/test/query-tests/security/CWE-696/test.rs b/rust/ql/test/query-tests/security/CWE-696/test.rs index 5cd7f451f2cb7..b23c06aa6a695 100644 --- a/rust/ql/test/query-tests/security/CWE-696/test.rs +++ b/rust/ql/test/query-tests/security/CWE-696/test.rs @@ -1,4 +1,3 @@ - // --- attribute variants --- use std::io::Write; @@ -123,7 +122,7 @@ unsafe fn harmless2_11() { // --- transitive cases --- fn call_target3_1() { - _ = stderr().write_all(b"Hello, world!"); // $ Alert=source3_1 Alert=source3_3 MISSING: Alert=source3_4 + _ = stderr().write_all(b"Hello, world!"); // $ Alert=source3_1 Alert=source3_3 Alert=source3_4 } #[ctor] // $ Source=source3_1 @@ -148,7 +147,7 @@ fn bad3_3() { call_target3_2(); } -#[ctor] // $ MISSING: Source=source3_4 +#[ctor] // $ Source=source3_4 fn bad3_4() { bad3_3(); }