Skip to content

Conversation

@hvitved
Copy link
Contributor

@hvitved hvitved commented Oct 29, 2025

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:

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/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:

fn foo() {}        // (1)

fn bar() {
    let f = foo;   // `foo` here refers to (1) via path resolution
    let foo = f(); // (2)
    foo            // `foo` here refers to (2) via variable 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

  • First computing global path resolution, where variable shadowing is ignored, exposed as the internal predicate resolvePathIgnoreVariableShadowing.
  • resolvePathIgnoreVariableShadowing is 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 predicate identPatIsResolvable.
  • Variable resolution can then be computed as a local property, using only the global information from identPatIsResolvable.
  • Finally, path resolution can be computed by restricting resolvePathIgnoreVariableShadowing to 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 for rust/unused-variable. I spot checked some of the new alerts on rust and rust-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).

@github-actions github-actions bot added the Rust Pull requests that update Rust code label Oct 29, 2025
@hvitved hvitved force-pushed the rust/path-resolution-variable-impl branch 3 times, most recently from f41c74b to fa48969 Compare October 29, 2025 19:28
@hvitved hvitved force-pushed the rust/path-resolution-variable-impl branch 6 times, most recently from b74d8c6 to e845ff0 Compare November 10, 2025 10:02
@hvitved hvitved force-pushed the rust/path-resolution-variable-impl branch 3 times, most recently from 530a7a8 to 5410d17 Compare November 13, 2025 14:19
@hvitved hvitved force-pushed the rust/path-resolution-variable-impl branch 5 times, most recently from 07703ae to fbb1541 Compare November 17, 2025 13:12
@hvitved hvitved force-pushed the rust/path-resolution-variable-impl branch from fbb1541 to da174c3 Compare November 19, 2025 07:55
@hvitved hvitved force-pushed the rust/path-resolution-variable-impl branch from da174c3 to ec3b2c6 Compare November 19, 2025 08:07
@hvitved hvitved added the no-change-note-required This PR does not need a change note label Nov 19, 2025
@hvitved hvitved marked this pull request as ready for review November 19, 2025 18:45
@hvitved hvitved requested a review from a team as a code owner November 19, 2025 18:45
Copilot AI review requested due to automatic review settings November 19, 2025 18:45
Copilot finished reviewing on behalf of hvitved November 19, 2025 18:48
Copy link
Contributor

Copilot AI left a 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 identPatIsResolvable predicate determines if identifier patterns resolve to constructors/constants
  • Variable resolution now uses path resolution information instead of relying on naming conventions
  • The PathExt abstraction unifies handling of both Path and IdentPat nodes

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
Copy link

Copilot AI Nov 19, 2025

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".

Suggested change
* separate from the inherently local variable resolution logic. We acheive this by
* separate from the inherently local variable resolution logic. We achieve this by

Copilot uses AI. Check for mistakes.

#[rustfmt::skip]
const z: i32 // $ item=i32
= 0; // constz
Copy link

Copilot AI Nov 19, 2025

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.

Suggested change
= 0; // constz
= 0; // constant z

Copilot uses AI. Check for mistakes.
* 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
Copy link

Copilot AI Nov 19, 2025

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.

Suggested change
* 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

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

no-change-note-required This PR does not need a change note Rust Pull requests that update Rust code

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant