Merge pull request #20496 from hvitved/rust/path-resolution-use-visibility

Rust: Visibility check for qualified path resolution
This commit is contained in:
Tom Hvitved
2025-09-24 10:19:22 +02:00
committed by GitHub
6 changed files with 203 additions and 112 deletions

View File

@@ -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

View File

@@ -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<Use>;
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,11 +1401,20 @@ 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 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) and
encl.getADescendant() = this
this.isUnqualified(name, encl)
}
pragma[nomagic]
@@ -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<relevantTraitVisibleSig/2 relevantTraitVisible> {
// 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<relevantTraitVisibleSig/2 relevantTraitVisible> {
/** 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) {

View File

@@ -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()
)
)
}

View File

@@ -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"]

View File

@@ -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

View File

@@ -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 |