Rust: Handle unqualified UseTrees in path resolution

This commit is contained in:
Tom Hvitved
2025-11-03 11:25:50 +01:00
parent 39dd3acd76
commit 50552da4d8
3 changed files with 60 additions and 7 deletions

View File

@@ -1457,6 +1457,24 @@ private predicate crateDependencyEdge(SourceFileItemNode file, string name, Crat
not hasDeclOrDep(file, name)
}
/**
* Gets a `UseTree` that is nested under `tree`, and which needs to be resolved
* relative to the path of `tree`.
*
* `tree` is restricted to either having a path or being a direct child of some
* `use` statement without a path.
*/
private UseTree getAUseTreeUseTree(UseTree tree) {
result = tree.getUseTreeList().getAUseTree() and
(if tree.hasPath() then any() else tree = any(Use u).getUseTree())
or
exists(UseTree mid |
mid = getAUseTreeUseTree(tree) and
not mid.hasPath() and
result = mid.getUseTreeList().getAUseTree()
)
}
private predicate useTreeDeclares(UseTree tree, string name) {
not tree.isGlob() and
not exists(tree.getUseTreeList()) and
@@ -1470,7 +1488,7 @@ private predicate useTreeDeclares(UseTree tree, string name) {
or
exists(UseTree mid |
useTreeDeclares(mid, name) and
mid = tree.getUseTreeList().getAUseTree()
mid = getAUseTreeUseTree(tree)
)
}
@@ -1511,7 +1529,10 @@ class RelevantPath extends Path {
pragma[nomagic]
predicate isUnqualified(string name) {
not exists(this.getQualifier()) and
not this = any(UseTreeList list).getAUseTree().getPath().getQualifier*() and
not exists(UseTree tree |
tree.hasPath() and
this = getAUseTreeUseTree(tree).getPath().getQualifier*()
) and
name = this.getText()
}
@@ -1990,7 +2011,7 @@ private ItemNode resolveUseTreeListItem(Use use, UseTree tree, RelevantPath path
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
tree = getAUseTreeUseTree(midTree) and
isUseTreeSubPathUnqualified(tree, path, pragma[only_bind_into](name)) and
result = mid.getASuccessor(pragma[only_bind_into](name), kind, useOpt)
)
@@ -2010,14 +2031,31 @@ private ItemNode resolveUseTreeListItemQualifier(
name = path.getText()
}
private UseTree getAUseUseTree(Use use) {
exists(UseTree root | root = use.getUseTree() |
result = root
or
not root.hasPath() and
result = getAUseTreeUseTree(root)
)
}
pragma[nomagic]
private ItemNode resolveUseTreeListItem(Use use, UseTree tree) {
exists(Path path | path = tree.getPath() |
tree = use.getUseTree() and
tree = getAUseUseTree(use) and
result = resolvePathCand(path)
or
result = resolveUseTreeListItem(use, tree, path, _)
)
or
exists(UseTree midTree |
// `use foo::{bar, *}`; midTree = `foo` and tree = `*`
result = resolveUseTreeListItem(use, midTree) and
tree = getAUseTreeUseTree(midTree) and
tree.isGlob() and
not tree.hasPath()
)
}
/** Holds if `use` imports `item` as `name`. */
@@ -2159,6 +2197,16 @@ private module Debug {
result = resolvePath(path)
}
ItemNode debugResolveUseTreeListItem(Use use, UseTree tree, RelevantPath path, SuccessorKind kind) {
use = getRelevantLocatable() and
result = resolveUseTreeListItem(use, tree, path, kind)
}
ItemNode debugResolveUseTreeListItem(Use use, UseTree tree) {
use = getRelevantLocatable() and
result = resolveUseTreeListItem(use, tree)
}
predicate debugUseImportEdge(Use use, string name, ItemNode item, SuccessorKind kind) {
use = getRelevantLocatable() and
useImportEdge(use, name, item, kind)

View File

@@ -1,7 +1,7 @@
mod my; // I1
#[rustfmt::skip]
use {{{my::{{self as my_alias, *}}}}}; // $ MISSING: item=I1
use {{{my::{{self as my_alias, *}}}}}; // $ item=I1
use my::nested::nested1::nested2::*; // $ item=I3
@@ -816,8 +816,8 @@ fn main() {
nested6::f(); // $ item=I116
nested8::f(); // $ item=I119
my3::f(); // $ item=I200
nested_f(); // $ MISSING: item=I201
my_alias::nested_f(); // $ MISSING: item=I201
nested_f(); // $ item=I201
my_alias::nested_f(); // $ item=I201
m18::m19::m20::g(); // $ item=I103
m23::f(); // $ item=I108
m24::f(); // $ item=I121

View File

@@ -47,6 +47,8 @@ mod
| my/nested.rs:1:1:17:1 | mod nested1 |
| my/nested.rs:2:5:11:5 | mod nested2 |
resolvePath
| main.rs:4:8:4:9 | my | main.rs:1:1:1:7 | mod my |
| main.rs:4:14:4:17 | self | main.rs:1:1:1:7 | mod my |
| main.rs:6:5:6:6 | my | main.rs:1:1:1:7 | mod my |
| main.rs:6:5:6:14 | ...::nested | my.rs:1:1:1:15 | mod nested |
| main.rs:6:5:6:23 | ...::nested1 | my/nested.rs:1:1:17:1 | mod nested1 |
@@ -447,6 +449,9 @@ resolvePath
| main.rs:817:5:817:14 | ...::f | my2/nested2.rs:23:9:25:9 | fn f |
| main.rs:818:5:818:7 | my3 | my2/mod.rs:20:1:20:12 | mod my3 |
| main.rs:818:5:818:10 | ...::f | my2/my3/mod.rs:1:1:5:1 | fn f |
| main.rs:819:5:819:12 | nested_f | my/my4/my5/mod.rs:1:1:3:1 | fn f |
| main.rs:820:5:820:12 | my_alias | main.rs:1:1:1:7 | mod my |
| main.rs:820:5:820:22 | ...::nested_f | my/my4/my5/mod.rs:1:1:3:1 | fn f |
| main.rs:821:5:821:7 | m18 | main.rs:555:1:573:1 | mod m18 |
| main.rs:821:5:821:12 | ...::m19 | main.rs:560:5:572:5 | mod m19 |
| main.rs:821:5:821:17 | ...::m20 | main.rs:565:9:571:9 | mod m20 |