-
Notifications
You must be signed in to change notification settings - Fork 1.8k
Rust: Path resolution before variable resolution #20716
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
f41c74b to
fa48969
Compare
b74d8c6 to
e845ff0
Compare
530a7a8 to
5410d17
Compare
rust/ql/lib/codeql/rust/elements/internal/FormatTemplateVariableAccessImpl.qll
Fixed
Show fixed
Hide fixed
07703ae to
fbb1541
Compare
fbb1541 to
da174c3
Compare
da174c3 to
ec3b2c6
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 refactors the Rust path resolution logic to happen before variable resolution, addressing a fundamental ordering issue in the analysis. The change allows the CodeQL analyzer to correctly distinguish between identifier patterns that bind variables versus those that reference constructors or constants.
- Path resolution is now computed globally while ignoring variable shadowing, exposed via
resolvePathIgnoreVariableShadowing - A new
identPatIsResolvablepredicate determines if identifier patterns resolve to constructors/constants - Variable resolution now uses path resolution information instead of relying on naming conventions
- The
PathExtabstraction unifies handling of bothPathandIdentPatnodes
Reviewed Changes
Copilot reviewed 18 out of 18 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| rust/ql/lib/codeql/rust/internal/PathResolution.qll | Core refactoring: introduces PathExt abstraction, resolvePathIgnoreVariableShadowing, and identPatIsResolvable predicates; replaces RelevantPath with PathExt |
| rust/ql/lib/codeql/rust/elements/internal/VariableImpl.qll | Replaces uppercase naming heuristic with identPatIsResolvable check for determining variables |
| rust/ql/lib/codeql/rust/elements/internal/ConstImpl.qll | Adds IdentPatConstAccess to support constant references in identifier patterns |
| rust/ql/lib/codeql/rust/elements/internal/PathExprImpl.qll | Updates primary QL class handling for variable access distinction |
| rust/ql/lib/codeql/rust/controlflow/internal/Completion.qll | Updates pattern matching logic to use variable declaration check instead of naming heuristic |
| rust/ql/lib/codeql/rust/elements/internal/FormatTemplateVariableAccessImpl.qll | Adds primary QL class identifier |
| rust/ql/lib/codeql/rust/internal/Definitions.qll | Minor optimization to macro call location check |
| rust/ql/lib/codeql/rust/internal/CachedStages.qll | Updates stage dependencies, removes unused trait bound check |
| rust/ql/lib/codeql/rust/elements/internal/CallImpl.qll | Updates type reference from RelevantPath to PathExt |
| rust/ql/test/library-tests/path-resolution/path-resolution.ql | Updates query signature to use PathExt |
| rust/ql/test/library-tests/path-resolution/main.rs | Adds comprehensive test cases for pattern resolution scenarios |
| rust/ql/test/library-tests/variables/main.rs | Adds test cases for variable shadowing and pattern matching |
| rust/ql/integration-tests/hello-workspace/path-resolution.ql | Updates query signature to use PathExt |
| Multiple .expected files | Updated test expectations reflecting improved path and variable resolution |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| * | ||
| * So it may seem that path resolution and variable resolution must happen in mutual | ||
| * recursion, but we would like to keep the inherently global path resolution logic | ||
| * separate from the inherently local variable resolution logic. We acheive this by |
Copilot
AI
Nov 19, 2025
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.
Spelling error: "acheive" should be "achieve".
| * separate from the inherently local variable resolution logic. We acheive this by | |
| * separate from the inherently local variable resolution logic. We achieve this by |
|
|
||
| #[rustfmt::skip] | ||
| const z: i32 // $ item=i32 | ||
| = 0; // constz |
Copilot
AI
Nov 19, 2025
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.
[nitpick] The comment "constz" appears to be unclear. Consider using a more descriptive comment like "constant z" to clarify the reference.
| = 0; // constz | |
| = 0; // constant z |
| * let x = ...; // `x` is only a variable if it does not resolve to a constructor/constant | ||
| * ``` | ||
| * | ||
| * Even though variable names typically start with a lowercase letter and constructors |
Copilot
AI
Nov 19, 2025
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.
[nitpick] The comment says "constructors with an uppercase letter" but should say "constructors/constants with an uppercase letter" for consistency with the full description in lines 4-13 which mentions both constructors and constants.
| * Even though variable names typically start with a lowercase letter and constructors | |
| * Even though variable names typically start with a lowercase letter and constructors/constants |
Path resolution needs to happen before variable resolution, because otherwise we cannot know whether an identifier pattern binds a new variable or whether it refers to a constructor or constant:
Even though variable names typically start with a lowercase letter and constructors/constants with an uppercase letter, this is not enforced by the Rust language.
Variables may shadow declarations, so variable resolution also needs to affect path resolution:
So it may seem that path resolution and variable resolution must happen in mutual recursion, but we would like to keep the inherently global path resolution logic separate from the inherently local variable resolution logic. We acheive this by
resolvePathIgnoreVariableShadowing.resolvePathIgnoreVariableShadowingis sufficient to determine whether an identifier pattern resolves to a constructor/constant, since if it does, it cannot be shadowed by a variable. We expose this as the predicateidentPatIsResolvable.identPatIsResolvable.resolvePathIgnoreVariableShadowingto paths that are not resolvable via variable resolution.DCA looks good: We get a (somewhat surprisingly) large increase in
Percentage of calls with call target, and a (less surprisingly) increase in results forrust/unused-variable. I spot checked some of the new alerts onrustandrust-ffmpeg, and while they are FPs, opening those files in VS Code with Rust analyzer also results in warnings. So instead of attempting to filter away the results in the query, I would like to keep them in, as they are generally very useful for finding bugs/shortcomings in path resolution (such as this one).