-
Notifications
You must be signed in to change notification settings - Fork 2k
Rust: Path resolution improvements #19051
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 11 commits
92528f2
6cf83d3
c5106f7
bd4c85a
c911761
8f8f6f7
7c2bafe
8044b0d
57dfbf4
b2fc7e7
3142dbb
3f1f37f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,7 +1,7 @@ | ||
| use lib::a_module::hello; | ||
| use lib::a_module::hello; // $ item=HELLO | ||
|
|
||
| mod a_module; | ||
|
|
||
| fn main() { | ||
| hello(); | ||
| hello(); // $ item=HELLO | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,3 +1,3 @@ | ||
| pub fn hello() { | ||
| println!("Hello, world!"); | ||
| } | ||
| } // HELLO |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| import utils.test.PathResolutionInlineExpectationsTest |
| Original file line number | Diff line number | Diff line change | ||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -73,7 +73,7 @@ | |||||||||||||||||
| * - https://doc.rust-lang.org/reference/visibility-and-privacy.html | ||||||||||||||||||
| * - https://doc.rust-lang.org/reference/names/namespaces.html | ||||||||||||||||||
| */ | ||||||||||||||||||
| abstract class ItemNode extends AstNode { | ||||||||||||||||||
| abstract class ItemNode extends Locatable { | ||||||||||||||||||
| /** Gets the (original) name of this item. */ | ||||||||||||||||||
| abstract string getName(); | ||||||||||||||||||
|
|
||||||||||||||||||
|
|
@@ -109,7 +109,11 @@ | |||||||||||||||||
|
|
||||||||||||||||||
| /** Gets the immediately enclosing module (or source file) of this item. */ | ||||||||||||||||||
| pragma[nomagic] | ||||||||||||||||||
| ModuleLikeNode getImmediateParentModule() { this = result.getAnItemInScope() } | ||||||||||||||||||
| ModuleLikeNode getImmediateParentModule() { | ||||||||||||||||||
| this = result.getAnItemInScope() | ||||||||||||||||||
| or | ||||||||||||||||||
| result = this.(SourceFileItemNode).getSuper() | ||||||||||||||||||
| } | ||||||||||||||||||
|
|
||||||||||||||||||
| pragma[nomagic] | ||||||||||||||||||
| private ItemNode getASuccessorRec(string name) { | ||||||||||||||||||
|
|
@@ -122,6 +126,10 @@ | |||||||||||||||||
| or | ||||||||||||||||||
| useImportEdge(this, name, result) | ||||||||||||||||||
| or | ||||||||||||||||||
| crateDefEdge(this, name, result) | ||||||||||||||||||
| or | ||||||||||||||||||
| crateDependencyEdge(this, name, result) | ||||||||||||||||||
| or | ||||||||||||||||||
| // items made available through `use` are available to nodes that contain the `use` | ||||||||||||||||||
| exists(UseItemNode use | | ||||||||||||||||||
| use = this.getASuccessorRec(_) and | ||||||||||||||||||
|
|
@@ -168,19 +176,18 @@ | |||||||||||||||||
| result = this.getASuccessorRec(name) | ||||||||||||||||||
| or | ||||||||||||||||||
| name = "super" and | ||||||||||||||||||
| if this instanceof Module | ||||||||||||||||||
| if this instanceof Module or this instanceof SourceFile | ||||||||||||||||||
| then result = this.getImmediateParentModule() | ||||||||||||||||||
| else result = this.getImmediateParentModule().getImmediateParentModule() | ||||||||||||||||||
| or | ||||||||||||||||||
| name = "self" and | ||||||||||||||||||
| not this instanceof Module and | ||||||||||||||||||
| result = this.getImmediateParentModule() | ||||||||||||||||||
| if this instanceof Module then result = this else result = this.getImmediateParentModule() | ||||||||||||||||||
| or | ||||||||||||||||||
| name = "Self" and | ||||||||||||||||||
| this = result.(ImplOrTraitItemNode).getAnItemInSelfScope() | ||||||||||||||||||
| or | ||||||||||||||||||
| name = "crate" and | ||||||||||||||||||
| result.(SourceFileItemNode).getFile() = this.getFile() | ||||||||||||||||||
| this = result.(CrateItemNode).getASourceFile() | ||||||||||||||||||
| } | ||||||||||||||||||
|
|
||||||||||||||||||
| /** Gets the location of this item. */ | ||||||||||||||||||
|
|
@@ -203,6 +210,14 @@ | |||||||||||||||||
| } | ||||||||||||||||||
|
|
||||||||||||||||||
| private class SourceFileItemNode extends ModuleLikeNode, SourceFile { | ||||||||||||||||||
| pragma[nomagic] | ||||||||||||||||||
| ModuleLikeNode getSuper() { | ||||||||||||||||||
| exists(ModuleItemNode mod | | ||||||||||||||||||
| fileImport(mod, this) and | ||||||||||||||||||
| result = mod.getASuccessor("super") | ||||||||||||||||||
| ) | ||||||||||||||||||
| } | ||||||||||||||||||
|
|
||||||||||||||||||
| override string getName() { result = "(source file)" } | ||||||||||||||||||
|
|
||||||||||||||||||
| override Namespace getNamespace() { | ||||||||||||||||||
|
|
@@ -211,6 +226,55 @@ | |||||||||||||||||
|
|
||||||||||||||||||
| override Visibility getVisibility() { none() } | ||||||||||||||||||
|
|
||||||||||||||||||
| override predicate isPublic() { any() } | ||||||||||||||||||
|
|
||||||||||||||||||
| override TypeParam getTypeParam(int i) { none() } | ||||||||||||||||||
| } | ||||||||||||||||||
|
|
||||||||||||||||||
| class CrateItemNode extends ItemNode instanceof Crate { | ||||||||||||||||||
| /** | ||||||||||||||||||
| * Gets the module node that defines this crate. | ||||||||||||||||||
| * | ||||||||||||||||||
| * This is either a source file, when the crate is defined in source code, | ||||||||||||||||||
| * or a module, when the crate is defined in a dependency. | ||||||||||||||||||
| */ | ||||||||||||||||||
| pragma[nomagic] | ||||||||||||||||||
| ModuleLikeNode getModuleNode() { | ||||||||||||||||||
| result = super.getSourceFile() | ||||||||||||||||||
| or | ||||||||||||||||||
| not exists(super.getSourceFile()) and | ||||||||||||||||||
| result = super.getModule() | ||||||||||||||||||
| } | ||||||||||||||||||
|
|
||||||||||||||||||
| /** | ||||||||||||||||||
| * Gets a source file that belongs to this crate, if any. | ||||||||||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not sure if it's a strong convention, but I've noticed that on our AST classes the
Suggested change
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I actually don't like the
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What about this part of the rule:
That seems to conflict with how "if any" is used here?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Right, the |
||||||||||||||||||
| * | ||||||||||||||||||
| * This is calculated as those source files that can be reached from the entry | ||||||||||||||||||
| * file of this crate using zero or more `mod` imports, without going through | ||||||||||||||||||
| * the entry point of some other crate. | ||||||||||||||||||
| */ | ||||||||||||||||||
| pragma[nomagic] | ||||||||||||||||||
| SourceFileItemNode getASourceFile() { | ||||||||||||||||||
| result = super.getSourceFile() | ||||||||||||||||||
| or | ||||||||||||||||||
| exists(SourceFileItemNode mid, Module mod | | ||||||||||||||||||
| mid = this.getASourceFile() and | ||||||||||||||||||
| mod.getFile() = mid.getFile() and | ||||||||||||||||||
| fileImport(mod, result) and | ||||||||||||||||||
| not result = any(Crate other).getSourceFile() | ||||||||||||||||||
| ) | ||||||||||||||||||
| } | ||||||||||||||||||
|
|
||||||||||||||||||
| override string getName() { result = Crate.super.getName() } | ||||||||||||||||||
|
|
||||||||||||||||||
| override Namespace getNamespace() { | ||||||||||||||||||
| result.isType() // can be referenced with `crate` | ||||||||||||||||||
| } | ||||||||||||||||||
|
|
||||||||||||||||||
| override Visibility getVisibility() { none() } | ||||||||||||||||||
|
|
||||||||||||||||||
| override predicate isPublic() { any() } | ||||||||||||||||||
|
|
||||||||||||||||||
| override TypeParam getTypeParam(int i) { none() } | ||||||||||||||||||
| } | ||||||||||||||||||
|
|
||||||||||||||||||
|
|
@@ -460,7 +524,7 @@ | |||||||||||||||||
|
|
||||||||||||||||||
| override Namespace getNamespace() { none() } | ||||||||||||||||||
|
|
||||||||||||||||||
| override Visibility getVisibility() { none() } | ||||||||||||||||||
| override Visibility getVisibility() { result = Use.super.getVisibility() } | ||||||||||||||||||
|
|
||||||||||||||||||
| override TypeParam getTypeParam(int i) { none() } | ||||||||||||||||||
| } | ||||||||||||||||||
|
|
@@ -586,11 +650,33 @@ | |||||||||||||||||
| * Holds if `mod` is a `mod name;` item targeting a file resulting in `item` being | ||||||||||||||||||
| * in scope under the name `name`. | ||||||||||||||||||
| */ | ||||||||||||||||||
| pragma[nomagic] | ||||||||||||||||||
| private predicate fileImportEdge(Module mod, string name, ItemNode item) { | ||||||||||||||||||
| item.isPublic() and | ||||||||||||||||||
| exists(SourceFile f | | ||||||||||||||||||
| exists(SourceFileItemNode f | | ||||||||||||||||||
| fileImport(mod, f) and | ||||||||||||||||||
| sourceFileEdge(f, name, item) | ||||||||||||||||||
| item = f.getASuccessor(name) | ||||||||||||||||||
| ) | ||||||||||||||||||
| } | ||||||||||||||||||
|
|
||||||||||||||||||
| /** | ||||||||||||||||||
| * Holds if crate `c` defines the item `i` named `name`. | ||||||||||||||||||
| */ | ||||||||||||||||||
| pragma[nomagic] | ||||||||||||||||||
| private predicate crateDefEdge(CrateItemNode c, string name, ItemNode i) { | ||||||||||||||||||
| i = c.getModuleNode().getASuccessor(name) and | ||||||||||||||||||
| not i instanceof Crate | ||||||||||||||||||
| } | ||||||||||||||||||
|
|
||||||||||||||||||
| /** | ||||||||||||||||||
| * Holds if `m` depends on crate `dep` named `name`. | ||||||||||||||||||
| */ | ||||||||||||||||||
| private predicate crateDependencyEdge(ModuleLikeNode m, string name, CrateItemNode dep) { | ||||||||||||||||||
| exists(CrateItemNode c | dep = c.(Crate).getDependency(name) | | ||||||||||||||||||
| // entry module/entry source file | ||||||||||||||||||
| m = c.getModuleNode() | ||||||||||||||||||
| or | ||||||||||||||||||
| // entry/transitive source file | ||||||||||||||||||
| m = c.getASourceFile() | ||||||||||||||||||
| ) | ||||||||||||||||||
| } | ||||||||||||||||||
|
|
||||||||||||||||||
|
|
@@ -745,13 +831,54 @@ | |||||||||||||||||
| ) | ||||||||||||||||||
| } | ||||||||||||||||||
|
|
||||||||||||||||||
| /** Gets the item that `path` resolves to, if any. */ | ||||||||||||||||||
| cached | ||||||||||||||||||
| ItemNode resolvePath(RelevantPath path) { | ||||||||||||||||||
| pragma[nomagic] | ||||||||||||||||||
| private ItemNode resolvePath1(RelevantPath path) { | ||||||||||||||||||
| exists(Namespace ns | result = resolvePath0(path, ns) | | ||||||||||||||||||
| pathUsesNamespace(path, ns) | ||||||||||||||||||
| or | ||||||||||||||||||
| not pathUsesNamespace(path, _) | ||||||||||||||||||
| not pathUsesNamespace(path, _) and | ||||||||||||||||||
| not path = any(MacroCall mc).getPath() | ||||||||||||||||||
| ) | ||||||||||||||||||
| } | ||||||||||||||||||
|
|
||||||||||||||||||
| pragma[nomagic] | ||||||||||||||||||
| private ItemNode resolvePathPrivate( | ||||||||||||||||||
| RelevantPath path, ModuleLikeNode itemParent, ModuleLikeNode pathParent | ||||||||||||||||||
| ) { | ||||||||||||||||||
| result = resolvePath1(path) and | ||||||||||||||||||
| itemParent = result.getImmediateParentModule() and | ||||||||||||||||||
| not result.isPublic() and | ||||||||||||||||||
| ( | ||||||||||||||||||
| pathParent.getADescendant() = path | ||||||||||||||||||
| or | ||||||||||||||||||
| pathParent = any(ItemNode mid | path = mid.getADescendant()).getImmediateParentModule() | ||||||||||||||||||
| ) | ||||||||||||||||||
| } | ||||||||||||||||||
|
|
||||||||||||||||||
| /** | ||||||||||||||||||
| * Gets a module that has access to private items defined inside `itemParent`. | ||||||||||||||||||
| * | ||||||||||||||||||
| * According to | ||||||||||||||||||
| * | ||||||||||||||||||
| * https://web.mit.edu/rust-lang_v1.25/arch/amd64_ubuntu1404/share/doc/rust/html/book/second-edition/ch07-02-controlling-visibility-with-pub.html#privacy-rules | ||||||||||||||||||
| * | ||||||||||||||||||
| * this is either `itemParent` itself or any (transitive) child of `itemParent`. | ||||||||||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think I found the same thing in the official Rust reference. We could also use a reference-style Markdown link.
Suggested change
|
||||||||||||||||||
| */ | ||||||||||||||||||
| pragma[nomagic] | ||||||||||||||||||
| private ModuleLikeNode getAPrivateVisibleModule(ModuleLikeNode itemParent) { | ||||||||||||||||||
| exists(resolvePathPrivate(_, itemParent, _)) and | ||||||||||||||||||
| result.getImmediateParentModule*() = itemParent | ||||||||||||||||||
| } | ||||||||||||||||||
|
|
||||||||||||||||||
| /** Gets the item that `path` resolves to, if any. */ | ||||||||||||||||||
| cached | ||||||||||||||||||
| ItemNode resolvePath(RelevantPath path) { | ||||||||||||||||||
| result = resolvePath1(path) and | ||||||||||||||||||
| result.isPublic() | ||||||||||||||||||
| or | ||||||||||||||||||
| exists(ModuleLikeNode itemParent, ModuleLikeNode pathParent | | ||||||||||||||||||
| result = resolvePathPrivate(path, itemParent, pathParent) and | ||||||||||||||||||
| pathParent = getAPrivateVisibleModule(itemParent) | ||||||||||||||||||
| ) | ||||||||||||||||||
| } | ||||||||||||||||||
|
|
||||||||||||||||||
|
|
@@ -762,9 +889,9 @@ | |||||||||||||||||
| } | ||||||||||||||||||
|
|
||||||||||||||||||
| private predicate isUseTreeSubPath(UseTree tree, RelevantPath path) { | ||||||||||||||||||
| path = tree.getPath() | ||||||||||||||||||
| or | ||||||||||||||||||
| exists(RelevantPath mid | | ||||||||||||||||||
| isUseTreeSubPath(tree, mid) and | ||||||||||||||||||
| path = mid.getQualifier() | ||||||||||||||||||
| ) | ||||||||||||||||||
|
|
@@ -831,3 +958,48 @@ | |||||||||||||||||
| name != "_" | ||||||||||||||||||
| ) | ||||||||||||||||||
| } | ||||||||||||||||||
|
|
||||||||||||||||||
| /** Provides predicates for debugging the path resolution implementation. */ | ||||||||||||||||||
| private module Debug { | ||||||||||||||||||
Check warningCode scanning / CodeQL Dead code Warning
This code is never used, and it's not publicly exported.
|
||||||||||||||||||
| private Locatable getRelevantLocatable() { | ||||||||||||||||||
| exists(string filepath, int startline, int startcolumn, int endline, int endcolumn | | ||||||||||||||||||
Check warningCode scanning / CodeQL Omittable 'exists' variable Warning
This exists variable can be omitted by using a don't-care expression
in this argument Error loading related location Loading Check warningCode scanning / CodeQL Omittable 'exists' variable Warning
This exists variable can be omitted by using a don't-care expression
in this argument Error loading related location Loading Check warningCode scanning / CodeQL Omittable 'exists' variable Warning
This exists variable can be omitted by using a don't-care expression
in this argument Error loading related location Loading |
||||||||||||||||||
| result.getLocation().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) and | ||||||||||||||||||
| // filepath.matches("%/compile.rs") and | ||||||||||||||||||
| // startline = 1986 | ||||||||||||||||||
| // filepath.matches("%/build_steps/mod.rs") and | ||||||||||||||||||
| // startline = 17 | ||||||||||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should these be deleted?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sure. |
||||||||||||||||||
| filepath.matches("%/main.rs") and | ||||||||||||||||||
| startline = 1 | ||||||||||||||||||
| ) | ||||||||||||||||||
| } | ||||||||||||||||||
|
|
||||||||||||||||||
| predicate debugUnqualifiedPathLookup(RelevantPath p, string name, Namespace ns, ItemNode encl) { | ||||||||||||||||||
| p = getRelevantLocatable() and | ||||||||||||||||||
| unqualifiedPathLookup(p, name, ns, encl) | ||||||||||||||||||
| } | ||||||||||||||||||
|
|
||||||||||||||||||
| ItemNode debugResolvePath(RelevantPath path) { | ||||||||||||||||||
| path = getRelevantLocatable() and | ||||||||||||||||||
| result = resolvePath(path) | ||||||||||||||||||
| } | ||||||||||||||||||
|
|
||||||||||||||||||
| predicate debugUseImportEdge(Use use, string name, ItemNode item) { | ||||||||||||||||||
| use = getRelevantLocatable() and | ||||||||||||||||||
| useImportEdge(use, name, item) | ||||||||||||||||||
| } | ||||||||||||||||||
|
|
||||||||||||||||||
| ItemNode debugGetASuccessorRec(ItemNode i, string name) { | ||||||||||||||||||
| i = getRelevantLocatable() and | ||||||||||||||||||
| result = i.getASuccessor(name) | ||||||||||||||||||
| } | ||||||||||||||||||
|
|
||||||||||||||||||
| predicate debugFileImportEdge(Module mod, string name, ItemNode item) { | ||||||||||||||||||
| mod = getRelevantLocatable() and | ||||||||||||||||||
| fileImportEdge(mod, name, item) | ||||||||||||||||||
| } | ||||||||||||||||||
|
|
||||||||||||||||||
| predicate debugFileImport(Module m, SourceFile f) { | ||||||||||||||||||
| m = getRelevantLocatable() and | ||||||||||||||||||
| fileImport(m, f) | ||||||||||||||||||
| } | ||||||||||||||||||
| } | ||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.