Skip to content

Lint unused_variables's "typoed pattern" suggestion still suggests invalid and unrelated paths #147595

Description

@fmease

I just stumbled upon this organically. Since PR #145827 rustc suggests paths to underscore (!) constant items which is syntactically invalid. Consider:

mod assertions { const _: () = assert!(true); }

pub fn demo(x: Option<()>) {
    match x {
        Some(x) => {}
        None => {}
    }
}
Compiler Output
warning: unused variable: `x`
 --> src/lib.rs:5:14
  |
5 |         Some(x) => {}
  |              ^
  |
  = note: `#[warn(unused_variables)]` (part of `#[warn(unused)]`) on by default
help: if this is intentional, prefix it with an underscore
  |
5 |         Some(_x) => {}
  |              +
help: you might have meant to pattern match on the similarly named constant `_`
  |
5 -         Some(x) => {}
5 +         Some(assertions::_) => {}
  |

The suggested path assertions::_ is syntactically invalid and should never be a suggestion.


Moreover, while rustc does seem to make sure that the type of the unused binding and the suggested constant / constructor match, it doesn't seem to filter out

  1. private items (meaning: the suggestion will lead to errors) or
  2. items with textually dissimilar names (according to a string metric like Levenshtein distance).

Consider:

mod enclosed { #[allow(dead_code)] const EXTRA_OFFSET: i32 = 1; }

pub fn demo(x: Option<i32>) {
    match x {
        Some(x) => {}
        None => {}
    }
}
Compiler Output
warning: unused variable: `x`
 --> src/lib.rs:5:14
  |
5 |         Some(x) => {}
  |              ^
  |
  = note: `#[warn(unused_variables)]` (part of `#[warn(unused)]`) on by default
help: if this is intentional, prefix it with an underscore
  |
5 |         Some(_x) => {}
  |              +
help: you might have meant to pattern match on the similarly named constant `EXTRA_OFFSET`
  |
5 -         Some(x) => {}
5 +         Some(enclosed::EXTRA_OFFSET) => {}
  |

The suggested path enclosed::EXTRA_OFFSET is (1) inaccessible inside demo due to privacy and (2) looks nothing like the unused x, so it can't possibly be a typo.


Finally, the suggestion doesn't properly qualify the path. Consider

pub mod enclosed { pub const X: i32 = 1; }

pub mod separated {
    pub fn demo(x: Option<i32>) {
        let Some(x) = x else { return };
    }
}
Compiler Output
warning: unused variable: `x`
 --> src/lib.rs:7:18
  |
7 |         let Some(x) = x else { return };
  |                  ^
  |
  = note: `#[warn(unused_variables)]` (part of `#[warn(unused)]`) on by default
help: if this is intentional, prefix it with an underscore
  |
7 |         let Some(_x) = x else { return };
  |                  +
help: you might have meant to pattern match on the similarly named constant `X`
  |
7 -         let Some(x) = x else { return };
7 +         let Some(enclosed::X) = x else { return };
  |

It suggests enclosed::X inside separated::demo while it should suggest super::enclosed::X or crate::enclosed::X otherwise it doesn't resolve.


rustc 1.92.0-nightly (3d8c1c1fc 2025-10-06)
binary: rustc
commit-hash: 3d8c1c1fc077d04658de63261d8ce2903546db13
commit-date: 2025-10-06
host: x86_64-unknown-linux-gnu
release: 1.92.0-nightly
LLVM version: 21.1.2

Activity

  1. added
    A-diagnosticsArea: Messages for errors, warnings, and lints
    T-compilerRelevant to the compiler team, which will review and decide on the PR/issue.
    D-invalid-suggestionDiagnostics: A structured suggestion resulting in incorrect code.
    on Oct 11, 2025
  2. fmease commented on Oct 11, 2025

    @fmease
    MemberAuthor

    I'm aware of the fixed issue #147303. Its linked PR #147320 simply suppressed the diagnostic for function parameters but whether the unused binding is inside a parameter, a match expr, let binding or XY is immaterial, this affects all pattern locations.

    What actually matters for addressing this issue is the following and I summarize:

    1. Never suggest _
    2. Ideally, don't suggest private items
    3. (More aggressively or actually) filter out textually dissimilar items
    4. Properly qualify suggested paths

    I'm also aware of PR #147310 which downgraded the suggestion from machine-applicable to maybe-incorrect.

  3. added
    A-patternsRelating to patterns and pattern matching
    on Oct 11, 2025
  4. estebank commented on Oct 15, 2025

    @estebank
    Contributor

    For the second case above, the suggestion is wrong even if it was public: the lev distance should be too high!

    Ideally, don't suggest private items

    I think that we should still at least point at reasonable private items (in the same way that if we have a binding out of scope, we point at it). There are cases where the item being private is the mistake, specially if the lev distance is 0.

    (More aggressively or actually) filter out textually dissimilar items

    I'm surprised that find_best_match_for_name with a dist: None would pick these names at all, because in that case it should be looking at things with at most 1/3rd distance change :-/

    Properly qualify suggested paths

    IIRC, the problem is that being in resolve we have to duplicate/reimplement the logic for getting the appropriate path to print, this was a best-effort approximation :(

  5. reddevilmidzy commented on Nov 18, 2025

    @reddevilmidzy
    Member

    I've encountered a similar issue with the "typoed pattern" suggestion, but in a different context: simple variable bindings (not pattern matching).

    Reproduction:

    pub mod utils {
        pub fn f() {
            use crate::system;
            let x = system::Y;
      	}
    }
    
    pub mod system {
        pub const Y: u32 = 0;
    }
    
    fn main() {}

    Compiler Output:

    warning: unused variable: `x`
     --> src\main.rs:4:13
      |
    4 |         let x = system::Y;
      |             ^
      |
      = note: `#[warn(unused_variables)]` (part of `#[warn(unused)]`) on by default
    help: you might have meant to pattern match on the similarly named constant `Y`
      |
    4 -         let x = system::Y;
    4 +         let system::Y = system::Y;
      |
    help: if this is intentional, prefix it with an underscore
      |
    4 |         let _x = system::Y;
      |  

    The suggested let system::Y = system::Y; is a pattern match that doesn't bind any variable, so it doesn't solve the original problem (the value is still unused).

    rustc version

    rustc 1.93.0-nightly (843f8ce2e 2025-11-07)
    binary: rustc
    commit-hash: 843f8ce2ebc01d35a30484eadc8a84cdc6130844
    commit-date: 2025-11-07
    host: x86_64-pc-windows-msvc
    release: 1.93.0-nightly
    LLVM version: 21.1.3
    
  6. fmease commented on Sep 19, 2026

    @fmease
    MemberAuthor

    Incidentally, the 2nd case (privacy) was addressed the same day I opened this issue (on main, not nightly): PR RUST-142390 added the necessary is_accessible_from check.

    Ideally, don't suggest private items

    I think that we should still at least point at reasonable private items (in the same way that if we have a binding out of scope, we point at it). There are cases where the item being private is the mistake, specially if the lev distance is 0.

    Of course, it means this suggestion wasn't implemented.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-diagnosticsArea: Messages for errors, warnings, and lintsA-patternsRelating to patterns and pattern matchingD-invalid-suggestionDiagnostics: A structured suggestion resulting in incorrect code.L-unused_variablesLint: unused_variablesT-compilerRelevant to the compiler team, which will review and decide on the PR/issue.

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions