-
Notifications
You must be signed in to change notification settings - Fork 1.8k
Rust: Fallback crate resolution #20225
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
base: main
Are you sure you want to change the base?
Conversation
f1e4bb9
to
8b44182
Compare
4ee31bd
to
8a3c3aa
Compare
36a7fe1
to
245b001
Compare
245b001
to
4a32fc0
Compare
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.
Pull Request Overview
This PR introduces fallback crate resolution to handle cases where crate dependency information from the extractor may be incomplete. The key change allows paths to resolve to any crate in the database when the crate name doesn't conflict with known dependencies, and adds consistency improvements for qualified path resolution.
Key changes:
- Adds fallback crate resolution when dependency information is missing
- Implements consistency checks to ensure qualified paths like
q::p
resolve consistently with their qualifiers - Refactors path resolution to separate candidate resolution from final resolution with consistency checks
Reviewed Changes
Copilot reviewed 7 out of 7 changed files in this pull request and generated 2 comments.
File | Description |
---|---|
PathResolutionConsistency.qll | Updates path resolution consistency check to exclude crate conflicts from multiple resolution warnings |
PathResolution.qll | Major refactoring adding fallback crate resolution, consistency checks, and new implementation classes |
TypeParamImpl.qll | Simplifies type bound resolution by delegating to TypeParamItemNode |
*.expected files | Updated test expectations reflecting reduced path resolution inconsistencies |
// As a fallback, give all files access to crates that do not conflict with known dependencies | ||
// and declarations. | ||
name = dep.getName() and | ||
not declaresDirectly(file, TTypeNamespace(), name) and |
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.
The TTypeNamespace()
call should likely be name.getNamespace()
or a similar method to get the appropriate namespace for the crate name, rather than always using the type namespace.
not declaresDirectly(file, TTypeNamespace(), name) and | |
not declaresDirectly(file, dep.getNamespace(), name) and |
Copilot uses AI. Check for mistakes.
@@ -1218,7 +1303,7 @@ class RelevantPath extends Path { | |||
pragma[nomagic] | |||
predicate isUnqualified(string name) { | |||
not exists(this.getQualifier()) and | |||
not this = any(UseTreeList list).getAUseTree().getPath() and | |||
not this = any(UseTreeList list).getAUseTree().getPath().getQualifier*() and | |||
name = this.getText() | |||
} | |||
|
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.
The transitive closure operator *
after getQualifier
creates a potential infinite recursion if there are circular qualifier relationships. Consider using a bounded recursion or a different approach to handle qualifier chains.
not isQualifiedViaUseTree(this) and | |
name = this.getText() | |
} | |
/** | |
* Helper predicate to check if `path` is reachable via qualifiers from any UseTreeList, | |
* using bounded recursion to avoid infinite loops in case of cycles. | |
*/ | |
private predicate isQualifiedViaUseTree(Path path) { | |
exists(UseTreeList list, UseTree ut, Path p | | |
ut = list.getAUseTree() and | |
p = ut.getPath() and | |
isReachableQualifier(p, path, []) | |
) | |
} | |
/** | |
* Bounded recursion: holds if `target` is reachable from `current` via qualifiers, | |
* without revisiting any path in `visited`. | |
*/ | |
private predicate isReachableQualifier(Path current, Path target, Path[] visited) { | |
current = target | |
or | |
not visited.contains(current) and | |
exists(Path next | | |
next = current.getQualifier() and | |
isReachableQualifier(next, target, visited + [current]) | |
) | |
} |
Copilot uses AI. Check for mistakes.
Before this PR, we relied strictly on crate dependency information from the extractor when resolving paths to crates. However, for whatever reason, this information may sometimes be incomplete, so in this PR we relax this condition and allow for a path to resolve to any crate in the DB, as long as the name of that crate does not conflict with the name of a known dependency.
In order to reduce path resolution inconsistencies, this PR also adds a step to make resolution of qualified paths like
q::p
consistent; of all the candidate resolution targets ofq
, only chose the one(s) that are consistent with whatq::p
may resolve to.DCA looks great:
Percentage of calls with call target
increases from 61.52 to 62.44, path resolution inconsistencies are reduced by 41 %, at only a marginal slowdown.