diff --git a/rust/ql/lib/codeql/rust/internal/CachedStages.qll b/rust/ql/lib/codeql/rust/internal/CachedStages.qll index cfd3d6905220..132b9ec8f7e8 100644 --- a/rust/ql/lib/codeql/rust/internal/CachedStages.qll +++ b/rust/ql/lib/codeql/rust/internal/CachedStages.qll @@ -117,7 +117,7 @@ module Stages { or exists(resolvePath(_)) or - exists(any(ItemNode i).getASuccessor(_, _)) + exists(any(ItemNode i).getASuccessor(_, _, _)) or exists(any(ImplOrTraitItemNode i).getASelfPath()) or diff --git a/rust/ql/lib/codeql/rust/internal/PathResolution.qll b/rust/ql/lib/codeql/rust/internal/PathResolution.qll index 44e8b4522554..efd614db63a4 100644 --- a/rust/ql/lib/codeql/rust/internal/PathResolution.qll +++ b/rust/ql/lib/codeql/rust/internal/PathResolution.qll @@ -6,6 +6,7 @@ private import rust private import codeql.rust.elements.internal.generated.ParentChild private import codeql.rust.internal.CachedStages private import codeql.rust.frameworks.stdlib.Builtins as Builtins +private import codeql.util.Option private newtype TNamespace = TTypeNamespace() or @@ -78,6 +79,10 @@ private ItemNode getAChildSuccessor(ItemNode item, string name, SuccessorKind ki ) } +private module UseOption = Option; + +private class UseOption = UseOption::Option; + /** * Holds if `n` is superseded by an attribute macro expansion. That is, `n` is * an item or a transitive child of an item with an attribute macro expansion. @@ -229,40 +234,51 @@ abstract class ItemNode extends Locatable { result = this.(SourceFileItemNode).getSuper() } - /** Gets a successor named `name` of the given `kind`, if any. */ + /** + * Gets a successor named `name` of the given `kind`, if any. + * + * `useOpt` represents the `use` statement that brought the item into scope, + * if any. + */ cached - ItemNode getASuccessor(string name, SuccessorKind kind) { + ItemNode getASuccessor(string name, SuccessorKind kind, UseOption useOpt) { Stages::PathResolutionStage::ref() and sourceFileEdge(this, name, result) and - kind.isBoth() + kind.isBoth() and + useOpt.isNone() or - result = getAChildSuccessor(this, name, kind) + result = getAChildSuccessor(this, name, kind) and + useOpt.isNone() or - fileImportEdge(this, name, result, kind) + fileImportEdge(this, name, result, kind, useOpt) or - useImportEdge(this, name, result, kind) + useImportEdge(this, name, result, kind) and + useOpt.isNone() or - crateDefEdge(this, name, result, kind) + crateDefEdge(this, name, result, kind, useOpt) or crateDependencyEdge(this, name, result) and - kind.isInternal() + kind.isInternal() and + useOpt.isNone() or externCrateEdge(this, name, result) and - kind.isInternal() + kind.isInternal() and + useOpt.isNone() 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, kind) - ) + useOpt.asSome() = + any(UseItemNode use_ | + use_ = this.getASuccessor(_, _, _) and + result = use_.getASuccessor(name, kind, _) + ) or - exists(ExternCrateItemNode ec | result = ec.(ItemNode).getASuccessor(name, kind) | - ec = this.getASuccessor(_, _) + exists(ExternCrateItemNode ec | result = ec.(ItemNode).getASuccessor(name, kind, useOpt) | + ec = this.getASuccessor(_, _, _) or // if the extern crate appears in the crate root, then the crate name is also added // to the 'extern prelude', see https://doc.rust-lang.org/reference/items/extern-crates.html exists(Crate c | - ec = c.getSourceFile().(ItemNode).getASuccessor(_, _) and + ec = c.getSourceFile().(ItemNode).getASuccessor(_, _, _) and this = c.getASourceFile() ) ) @@ -270,20 +286,20 @@ abstract class ItemNode extends Locatable { // a trait has access to the associated items of its supertraits this = any(TraitItemNodeImpl trait | - result = trait.resolveABoundCand().getASuccessor(name, kind) and + result = trait.resolveABoundCand().getASuccessor(name, kind, useOpt) and kind.isExternalOrBoth() and result instanceof AssocItemNode and not trait.hasAssocItem(name) ) or // items made available by an implementation where `this` is the implementing type - typeImplEdge(this, _, name, kind, result) + typeImplEdge(this, _, name, kind, result, useOpt) or // trait items with default implementations made available in an implementation exists(ImplItemNodeImpl impl, ItemNode trait | this = impl and trait = impl.resolveTraitTyCand() and - result = trait.getASuccessor(name, kind) and + result = trait.getASuccessor(name, kind, useOpt) and result.(AssocItemNode).hasImplementation() and kind.isExternalOrBoth() and not impl.hasAssocItem(name) @@ -291,42 +307,52 @@ abstract class ItemNode extends Locatable { or // type parameters have access to the associated items of its bounds result = - this.(TypeParamItemNodeImpl).resolveABoundCand().getASuccessor(name, kind).(AssocItemNode) and + this.(TypeParamItemNodeImpl) + .resolveABoundCand() + .getASuccessor(name, kind, useOpt) + .(AssocItemNode) and kind.isExternalOrBoth() or result = this.(ImplTraitTypeReprItemNodeImpl) .resolveABoundCand() - .getASuccessor(name, kind) + .getASuccessor(name, kind, useOpt) .(AssocItemNode) and kind.isExternalOrBoth() or - result = this.(TypeAliasItemNodeImpl).resolveAliasCand().getASuccessor(name, kind) and + result = this.(TypeAliasItemNodeImpl).resolveAliasCand().getASuccessor(name, kind, useOpt) and kind.isExternalOrBoth() or name = "super" and - if this instanceof Module or this instanceof SourceFile - then ( - kind.isBoth() and result = this.getImmediateParentModule() - ) else ( - kind.isInternal() and result = this.getImmediateParentModule().getImmediateParentModule() + useOpt.isNone() and + ( + if this instanceof Module or this instanceof SourceFile + then ( + kind.isBoth() and result = this.getImmediateParentModule() + ) else ( + kind.isInternal() and result = this.getImmediateParentModule().getImmediateParentModule() + ) ) or name = "self" and - if - this instanceof Module or - this instanceof Enum or - this instanceof Struct or - this instanceof Crate - then ( - kind.isBoth() and - result = this - ) else ( - kind.isInternal() and - result = this.getImmediateParentModule() + useOpt.isNone() and + ( + if + this instanceof Module or + this instanceof Enum or + this instanceof Struct or + this instanceof Crate + then ( + kind.isBoth() and + result = this + ) else ( + kind.isInternal() and + result = this.getImmediateParentModule() + ) ) or kind.isInternal() and + useOpt.isNone() and ( preludeEdge(this, name, result) or @@ -350,7 +376,7 @@ abstract class ItemNode extends Locatable { pragma[nomagic] ItemNode getASuccessor(string name) { exists(SuccessorKind kind | - result = this.getASuccessor(name, kind) and + result = this.getASuccessor(name, kind, _) and kind.isExternalOrBoth() ) } @@ -1266,10 +1292,12 @@ predicate fileImport(Module m, SourceFile f) { * in scope under the name `name`. */ pragma[nomagic] -private predicate fileImportEdge(Module mod, string name, ItemNode item, SuccessorKind kind) { +private predicate fileImportEdge( + Module mod, string name, ItemNode item, SuccessorKind kind, UseOption useOpt +) { exists(SourceFileItemNode f | fileImport(mod, f) and - item = f.getASuccessor(name, kind) + item = f.getASuccessor(name, kind, useOpt) ) } @@ -1277,8 +1305,10 @@ private predicate fileImportEdge(Module mod, string name, ItemNode item, Success * Holds if crate `c` defines the item `i` named `name`. */ pragma[nomagic] -private predicate crateDefEdge(CrateItemNode c, string name, ItemNode i, SuccessorKind kind) { - i = c.getSourceFile().getASuccessor(name, kind) and +private predicate crateDefEdge( + CrateItemNode c, string name, ItemNode i, SuccessorKind kind, UseOption useOpt +) { + i = c.getSourceFile().getASuccessor(name, kind, useOpt) and kind.isExternalOrBoth() } @@ -1291,6 +1321,12 @@ private predicate crateDependency(SourceFileItemNode file, string name, CrateIte exists(CrateItemNode c | dep = c.(Crate).getDependency(name) | file = c.getASourceFile()) } +pragma[nomagic] +private predicate hasDeclOrDep(SourceFileItemNode file, string name) { + declaresDirectly(file, TTypeNamespace(), name) or + crateDependency(file, name, _) +} + /** * Holds if `file` depends on crate `dep` named `name`. */ @@ -1304,8 +1340,7 @@ private predicate crateDependencyEdge(SourceFileItemNode file, string name, Crat // a given file to its crate (for example, if the file is `mod` imported inside a macro that the // extractor is unable to expand). name = dep.getName() and - not declaresDirectly(file, TTypeNamespace(), name) and - not crateDependency(file, name, _) + not hasDeclOrDep(file, name) } private predicate useTreeDeclares(UseTree tree, string name) { @@ -1366,13 +1401,22 @@ class RelevantPath extends Path { name = this.getText() } + /** + * Holds if this is an unqualified path with the textual value `name` and + * enclosing item `encl`. + */ pragma[nomagic] - predicate isCratePath(string name, ItemNode encl) { - name = ["crate", "$crate"] and + predicate isUnqualified(string name, ItemNode encl) { this.isUnqualified(name) and encl.getADescendant() = this } + pragma[nomagic] + predicate isCratePath(string name, ItemNode encl) { + name = ["crate", "$crate"] and + this.isUnqualified(name, encl) + } + pragma[nomagic] predicate isDollarCrateQualifiedPath(string name) { this.getQualifier().(RelevantPath).isCratePath("$crate", _) and @@ -1397,32 +1441,34 @@ private ItemNode getOuterScope(ItemNode i) { } /** - * Holds if the unqualified path `p` references an item named `name`, and `name` - * may be looked up in the `ns` namespace inside enclosing item `encl`. + * Holds if _some_ unqualified path in `encl` references an item named `name`, + * and `name` may be looked up in the `ns` namespace inside `ancestor`. */ pragma[nomagic] -private predicate unqualifiedPathLookup(ItemNode encl, string name, Namespace ns, RelevantPath p) { +private predicate unqualifiedPathLookup(ItemNode ancestor, string name, Namespace ns, ItemNode encl) { // lookup in the immediately enclosing item - p.isUnqualified(name) and - encl.getADescendant() = p and + any(RelevantPath p).isUnqualified(name, encl) and + ancestor = encl and exists(ns) and not name = ["crate", "$crate", "super", "self"] or // lookup in an outer scope, but only if the item is not declared in inner scope exists(ItemNode mid | - unqualifiedPathLookup(mid, name, ns, p) and + unqualifiedPathLookup(mid, name, ns, encl) and not declares(mid, ns, name) and not ( name = "Self" and mid = any(ImplOrTraitItemNode i).getAnItemInSelfScope() ) and - encl = getOuterScope(mid) + ancestor = getOuterScope(mid) ) } pragma[nomagic] -private ItemNode getASuccessor(ItemNode pred, string name, Namespace ns, SuccessorKind kind) { - result = pred.getASuccessor(name, kind) and +private ItemNode getASuccessor( + ItemNode pred, string name, Namespace ns, SuccessorKind kind, UseOption useOpt +) { + result = pred.getASuccessor(name, kind, useOpt) and ns = result.getNamespace() } @@ -1437,32 +1483,34 @@ private predicate sourceFileHasCratePathTc(ItemNode i1, ItemNode i2) = /** * Holds if the unqualified path `p` references a keyword item named `name`, and - * `name` may be looked up inside enclosing item `encl`. + * `name` may be looked up inside `ancestor`. */ pragma[nomagic] -private predicate keywordLookup(ItemNode encl, string name, RelevantPath p) { +private predicate keywordLookup(ItemNode ancestor, string name, RelevantPath p) { // For `($)crate`, jump directly to the root module exists(ItemNode i | p.isCratePath(name, i) | - encl instanceof SourceFile and - encl = i + ancestor instanceof SourceFile and + ancestor = i or - sourceFileHasCratePathTc(encl, i) + sourceFileHasCratePathTc(ancestor, i) ) or name = ["super", "self"] and - p.isUnqualified(name) and - encl.getADescendant() = p + p.isUnqualified(name, ancestor) } pragma[nomagic] private ItemNode unqualifiedPathLookup(RelevantPath p, Namespace ns, SuccessorKind kind) { - exists(ItemNode encl, string name | - result = getASuccessor(encl, name, ns, kind) and + exists(ItemNode ancestor, string name | + result = getASuccessor(ancestor, pragma[only_bind_into](name), ns, kind, _) and kind.isInternalOrBoth() | - unqualifiedPathLookup(encl, name, ns, p) + exists(ItemNode encl | + unqualifiedPathLookup(ancestor, name, ns, encl) and + p.isUnqualified(pragma[only_bind_into](name), encl) + ) or - keywordLookup(encl, name, p) and exists(ns) + keywordLookup(ancestor, name, p) and exists(ns) ) } @@ -1486,7 +1534,7 @@ module TraitIsVisible { // lookup in an outer scope, but only if the trait is not declared in inner scope exists(ItemNode mid | traitLookup(mid, element, trait) and - not trait = mid.getASuccessor(_, _) and + not trait = mid.getASuccessor(_, _, _) and encl = getOuterScope(mid) ) } @@ -1494,7 +1542,9 @@ module TraitIsVisible { /** Holds if the trait `trait` is visible at `element`. */ pragma[nomagic] predicate traitIsVisible(Element element, Trait trait) { - exists(ItemNode encl | traitLookup(encl, element, trait) and trait = encl.getASuccessor(_, _)) + exists(ItemNode encl | + traitLookup(encl, element, trait) and trait = encl.getASuccessor(_, _, _) + ) } } @@ -1523,12 +1573,42 @@ private ItemNode resolvePathCandQualifier(RelevantPath qualifier, RelevantPath p name = path.getText() } -pragma[nomagic] -private Crate getCrate0(Locatable l) { result.getASourceFile().getFile() = l.getFile() } - bindingset[l] pragma[inline_late] -private Crate getCrate(Locatable l) { result = getCrate0(l) } +private ModuleLikeNode getAnAncestorModule(Locatable l) { + exists(ItemNode encl | + encl.getADescendant() = l and + result = encl.getImmediateParentModule*() + ) +} + +bindingset[i] +pragma[inline_late] +private ModuleLikeNode getParent(ItemNode i) { result = i.getImmediateParent() } + +/** + * Holds if resolving a qualified path at `l` to the item `i` with successor kind + * `kind` respects visibility. + * + * This is the case when either `i` is externally visible (e.g. a `pub` function), + * or when `i` (or the `use` statement, `useOpt`, that brought `i` into scope) is + * in an ancestor module of `l`. + */ +bindingset[l, i, kind, useOpt] +pragma[inline_late] +private predicate checkQualifiedVisibility( + Locatable l, ItemNode i, SuccessorKind kind, UseOption useOpt +) { + kind.isExternalOrBoth() + or + exists(AstNode n | getAnAncestorModule(l) = getParent(n) | + n = useOpt.asSome() + or + useOpt.isNone() and + n = i + ) and + not i instanceof TypeParam +} /** * Gets the item that `path` resolves to in `ns` when `qualifier` is the @@ -1538,19 +1618,10 @@ pragma[nomagic] private ItemNode resolvePathCandQualified( RelevantPath qualifier, ItemNode q, RelevantPath path, Namespace ns ) { - exists(string name, SuccessorKind kind | + exists(string name, SuccessorKind kind, UseOption useOpt | q = resolvePathCandQualifier(qualifier, path, name) and - result = getASuccessor(q, name, ns, kind) - | - kind.isExternalOrBoth() - or - // Non-public items are visible to paths in descendant modules of the declaring - // module; the declaration may happen via a `use` statement, where the item - // being used is _not_ itself in an ancestor module, and we currently don't track - // that information in `getASuccessor`. So, for simplicity, we allow for non-public - // items when the path and the item are in the same crate. - getCrate(path) = getCrate(result) and - not result instanceof TypeParam + result = getASuccessor(q, name, ns, kind, useOpt) and + checkQualifiedVisibility(path, result, kind, useOpt) ) } @@ -1561,6 +1632,8 @@ private predicate pathUsesNamespace(Path p, Namespace n) { p = any(PathExpr pe).getPath() or p = any(TupleStructPat tsp).getPath() + or + p = any(Meta m).getPath() ) or n.isType() and @@ -1621,7 +1694,7 @@ private ItemNode resolvePathCand(RelevantPath path) { private Trait getResolvePathTraitUsed(RelevantPath path, AssocItemNode node) { exists(TypeItemNode type, ImplItemNodeImpl impl | node = resolvePathCandQualified(_, type, path, _) and - typeImplEdge(type, impl, _, _, node) and + typeImplEdge(type, impl, _, _, node, _) and result = impl.resolveTraitTyCand() ) } @@ -1668,18 +1741,17 @@ private predicate isUseTreeSubPathUnqualified(UseTree tree, RelevantPath path, s pragma[nomagic] private ItemNode resolveUseTreeListItem(Use use, UseTree tree, RelevantPath path, SuccessorKind kind) { - kind.isExternalOrBoth() and - ( + exists(UseOption useOpt | checkQualifiedVisibility(use, result, kind, useOpt) | 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), kind) + result = mid.getASuccessor(pragma[only_bind_into](name), kind, useOpt) ) or exists(ItemNode q, string name | q = resolveUseTreeListItemQualifier(use, tree, path, name) and - result = q.getASuccessor(name, kind) + result = q.getASuccessor(name, kind, useOpt) ) ) } @@ -1711,10 +1783,10 @@ private predicate useImportEdge(Use use, string name, ItemNode item, SuccessorKi not tree.hasUseTreeList() and if tree.isGlob() then - exists(ItemNode encl, Namespace ns, SuccessorKind kind1 | + exists(ItemNode encl, Namespace ns, SuccessorKind kind1, UseOption useOpt | encl.getADescendant() = use and - item = getASuccessor(used, name, ns, kind1) and - kind1.isExternalOrBoth() and + item = getASuccessor(used, name, ns, kind1, useOpt) and + checkQualifiedVisibility(use, item, kind1, useOpt) and // glob imports can be shadowed not declares(encl, ns, name) and not name = ["super", "self"] @@ -1764,10 +1836,11 @@ private predicate externCrateEdge(ExternCrateItemNode ec, string name, CrateItem * makes `assoc` available as `name` at `kind`. */ private predicate typeImplEdge( - TypeItemNode typeItem, ImplItemNodeImpl impl, string name, SuccessorKind kind, AssocItemNode assoc + TypeItemNode typeItem, ImplItemNodeImpl impl, string name, SuccessorKind kind, + AssocItemNode assoc, UseOption useOpt ) { typeItem = impl.resolveSelfTyCand() and - assoc = impl.getASuccessor(name, kind) and + assoc = impl.getASuccessor(name, kind, useOpt) and kind.isExternalOrBoth() } @@ -1818,10 +1891,13 @@ private module Debug { } predicate debugUnqualifiedPathLookup( - RelevantPath p, string name, Namespace ns, ItemNode encl, string path + RelevantPath p, string name, Namespace ns, ItemNode ancestor, string path ) { p = getRelevantLocatable() and - unqualifiedPathLookup(encl, name, ns, p) and + exists(ItemNode encl | + unqualifiedPathLookup(encl, name, ns, ancestor) and + p.isUnqualified(name, encl) + ) and path = p.toStringDebug() } @@ -1839,12 +1915,12 @@ private module Debug { ItemNode debugGetASuccessor(ItemNode i, string name, SuccessorKind kind) { i = getRelevantLocatable() and - result = i.getASuccessor(name, kind) + result = i.getASuccessor(name, kind, _) } predicate debugFileImportEdge(Module mod, string name, ItemNode item, SuccessorKind kind) { mod = getRelevantLocatable() and - fileImportEdge(mod, name, item, kind) + fileImportEdge(mod, name, item, kind, _) } predicate debugFileImport(Module m, SourceFile f) { diff --git a/rust/ql/lib/utils/test/PathResolutionInlineExpectationsTest.qll b/rust/ql/lib/utils/test/PathResolutionInlineExpectationsTest.qll index 8d2fdb2d2ebf..df668194c075 100644 --- a/rust/ql/lib/utils/test/PathResolutionInlineExpectationsTest.qll +++ b/rust/ql/lib/utils/test/PathResolutionInlineExpectationsTest.qll @@ -25,10 +25,14 @@ private module ResolveTest implements TestSig { private predicate item(ItemNode i, string value) { exists(string filepath, int line, boolean inMacro | itemAt(i, filepath, line, inMacro) | - commmentAt(value, filepath, line) - or - not commmentAt(_, filepath, line) and - value = i.getName() + if i instanceof SourceFile + then value = i.getFile().getBaseName() + else ( + commmentAt(value, filepath, line) + or + not commmentAt(_, filepath, line) and + value = i.getName() + ) ) } 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 85edb6832027..6b86c78237c9 100644 --- a/rust/ql/test/library-tests/path-resolution/my2/mod.rs +++ b/rust/ql/test/library-tests/path-resolution/my2/mod.rs @@ -15,6 +15,8 @@ pub use nested2::nested7::nested8::{ // $ item=I118 use nested2::nested5::nested6::f as nested6_f; // $ item=I116 +use std::ops::Deref; // $ item=Deref + pub mod my3; #[path = "renamed.rs"] diff --git a/rust/ql/test/library-tests/path-resolution/my2/my3/mod.rs b/rust/ql/test/library-tests/path-resolution/my2/my3/mod.rs index b459ca05aa6c..169aeed6b288 100644 --- a/rust/ql/test/library-tests/path-resolution/my2/my3/mod.rs +++ b/rust/ql/test/library-tests/path-resolution/my2/my3/mod.rs @@ -8,3 +8,7 @@ use super::super::h; // $ item=I25 use super::g; // $ item=I9 use super::nested6_f; // $ item=I116 + +use super::*; // $ item=mod.rs + +trait MyTrait: Deref {} // $ item=Deref 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 a908ec1e5c17..9315016fe6a0 100644 --- a/rust/ql/test/library-tests/path-resolution/path-resolution.expected +++ b/rust/ql/test/library-tests/path-resolution/path-resolution.expected @@ -33,8 +33,8 @@ mod | main.rs:712:1:764:1 | mod associated_types | | main.rs:770:1:789:1 | mod impl_with_attribute_macro | | my2/mod.rs:1:1:1:16 | mod nested2 | -| my2/mod.rs:18:1:18:12 | mod my3 | -| my2/mod.rs:20:1:21:10 | mod mymod | +| my2/mod.rs:20:1:20:12 | mod my3 | +| my2/mod.rs:22:1:23: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 | @@ -406,7 +406,7 @@ resolvePath | main.rs:814:5:814:14 | ...::f | my2/nested2.rs:15:9:17:9 | fn f | | main.rs:815:5:815:11 | nested8 | my2/nested2.rs:22:5:26:5 | mod nested8 | | main.rs:815:5:815:14 | ...::f | my2/nested2.rs:23:9:25:9 | fn f | -| main.rs:816:5:816:7 | my3 | my2/mod.rs:18:1:18:12 | mod my3 | +| main.rs:816:5:816:7 | my3 | my2/mod.rs:20:1:20:12 | mod my3 | | main.rs:816:5:816:10 | ...::f | my2/my3/mod.rs:1:1:5:1 | fn f | | main.rs:817:5:817:12 | nested_f | my/my4/my5/mod.rs:1:1:3:1 | fn f | | main.rs:818:5:818:7 | m18 | main.rs:553:1:571:1 | mod m18 | @@ -440,17 +440,22 @@ resolvePath | my2/mod.rs:16:5:16:20 | ...::nested5 | my2/nested2.rs:13:1:19:1 | mod nested5 | | my2/mod.rs:16:5:16:29 | ...::nested6 | my2/nested2.rs:14:5:18:5 | mod nested6 | | my2/mod.rs:16:5:16:32 | ...::f | my2/nested2.rs:15:9:17:9 | fn f | -| my2/mod.rs:23:9:23:13 | mymod | my2/mod.rs:20:1:21:10 | mod mymod | -| my2/mod.rs:23:9:23:16 | ...::f | my2/renamed.rs:1:1:1:13 | fn f | +| my2/mod.rs:18:5:18:7 | std | {EXTERNAL LOCATION} | Crate(std@0.0.0) | +| my2/mod.rs:18:5:18:12 | ...::ops | {EXTERNAL LOCATION} | mod ops | +| my2/mod.rs:18:5:18:19 | ...::Deref | {EXTERNAL LOCATION} | trait Deref | +| my2/mod.rs:25:9:25:13 | mymod | my2/mod.rs:22:1:23:10 | mod mymod | +| my2/mod.rs:25:9:25:16 | ...::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:56:1:75:1 | fn h | -| my2/my3/mod.rs:7:5:7:9 | super | my2/mod.rs:1:1:23:34 | SourceFile | +| my2/my3/mod.rs:7:5:7:9 | super | my2/mod.rs:1:1:25:34 | SourceFile | | my2/my3/mod.rs:7:5:7:16 | ...::super | main.rs:1:1:826:2 | SourceFile | | my2/my3/mod.rs:7:5:7:19 | ...::h | main.rs:56:1:75:1 | fn h | -| my2/my3/mod.rs:8:5:8:9 | super | my2/mod.rs:1:1:23:34 | SourceFile | +| my2/my3/mod.rs:8:5:8:9 | super | my2/mod.rs:1:1:25:34 | SourceFile | | my2/my3/mod.rs:8:5:8:12 | ...::g | my2/mod.rs:3:1:6:1 | fn g | -| my2/my3/mod.rs:10:5:10:9 | super | my2/mod.rs:1:1:23:34 | SourceFile | +| my2/my3/mod.rs:10:5:10:9 | super | my2/mod.rs:1:1:25:34 | SourceFile | | my2/my3/mod.rs:10:5:10:20 | ...::nested6_f | my2/nested2.rs:15:9:17:9 | fn f | +| my2/my3/mod.rs:12:5:12:9 | super | my2/mod.rs:1:1:25:34 | SourceFile | +| my2/my3/mod.rs:14:16:14:20 | Deref | {EXTERNAL LOCATION} | trait Deref | | 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 |