From 8dbdb1760b23112f87aedad37e4dad97559bc750 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Sun, 24 Aug 2025 19:22:51 +0000 Subject: [PATCH 1/6] On binding not present in all patterns, suggest potential typo ``` error[E0408]: variable `Ban` is not bound in all patterns --> f12.rs:9:9 | 9 | (Foo,Bar)|(Ban,Foo) => {} | ^^^^^^^^^ --- variable not in all patterns | | | pattern doesn't bind `Ban` | help: you might have meant to use the similarly named previously used binding `Bar` | 9 - (Foo,Bar)|(Ban,Foo) => {} 9 + (Foo,Bar)|(Bar,Foo) => {} | ``` --- compiler/rustc_resolve/messages.ftl | 2 + compiler/rustc_resolve/src/diagnostics.rs | 53 ++++++++++-- compiler/rustc_resolve/src/errors.rs | 12 +++ compiler/rustc_resolve/src/late.rs | 52 ++++++------ compiler/rustc_resolve/src/lib.rs | 4 +- .../mismatched-bindings-async-fn.stderr | 24 ++++++ tests/ui/or-patterns/missing-bindings.stderr | 81 +++++++++++++++++++ .../nested-undelimited-precedence.stderr | 6 ++ .../name-resolution.stderr | 12 +++ .../ui/resolve/resolve-inconsistent-names.rs | 6 +- .../resolve/resolve-inconsistent-names.stderr | 46 +++++++---- tests/ui/span/issue-39698.stderr | 24 ++++++ 12 files changed, 268 insertions(+), 54 deletions(-) diff --git a/compiler/rustc_resolve/messages.ftl b/compiler/rustc_resolve/messages.ftl index 47280a936779f..0e566e20a124d 100644 --- a/compiler/rustc_resolve/messages.ftl +++ b/compiler/rustc_resolve/messages.ftl @@ -470,6 +470,8 @@ resolve_variable_bound_with_different_mode = .label = bound in different ways .first_binding_span = first binding +resolve_variable_is_a_typo = you might have meant to use the similarly named previously used binding `{$typo}` + resolve_variable_is_not_bound_in_all_patterns = variable `{$name}` is not bound in all patterns diff --git a/compiler/rustc_resolve/src/diagnostics.rs b/compiler/rustc_resolve/src/diagnostics.rs index 337e7d2dd8639..324310ff48b1d 100644 --- a/compiler/rustc_resolve/src/diagnostics.rs +++ b/compiler/rustc_resolve/src/diagnostics.rs @@ -661,8 +661,12 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { ResolutionError::VariableNotBoundInPattern(binding_error, parent_scope) => { let BindingError { name, target, origin, could_be_path } = binding_error; - let target_sp = target.iter().copied().collect::>(); - let origin_sp = origin.iter().copied().collect::>(); + let mut target_sp = target.iter().map(|pat| pat.span).collect::>(); + target_sp.sort(); + target_sp.dedup(); + let mut origin_sp = origin.iter().map(|(span, _)| *span).collect::>(); + origin_sp.sort(); + origin_sp.dedup(); let msp = MultiSpan::from_spans(target_sp.clone()); let mut err = self @@ -671,8 +675,29 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { for sp in target_sp { err.subdiagnostic(errors::PatternDoesntBindName { span: sp, name }); } - for sp in origin_sp { - err.subdiagnostic(errors::VariableNotInAllPatterns { span: sp }); + for sp in &origin_sp { + err.subdiagnostic(errors::VariableNotInAllPatterns { span: *sp }); + } + let mut target_visitor = BindingVisitor::default(); + for pat in &target { + target_visitor.visit_pat(pat); + } + target_visitor.identifiers.sort(); + target_visitor.identifiers.dedup(); + let mut origin_visitor = BindingVisitor::default(); + for (_, pat) in &origin { + origin_visitor.visit_pat(pat); + } + origin_visitor.identifiers.sort(); + origin_visitor.identifiers.dedup(); + // Find if the binding could have been a typo + let mut suggested_typo = false; + if let Some(typo) = + find_best_match_for_name(&target_visitor.identifiers, name.name, None) + && !origin_visitor.identifiers.contains(&typo) + { + err.subdiagnostic(errors::PatternBindingTypo { spans: origin_sp, typo }); + suggested_typo = true; } if could_be_path { let import_suggestions = self.lookup_import_candidates( @@ -693,7 +718,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { }, ); - if import_suggestions.is_empty() { + if import_suggestions.is_empty() && !suggested_typo { let help_msg = format!( "if you meant to match on a variant or a `const` item, consider \ making the path in the pattern qualified: `path::to::ModOrType::{name}`", @@ -3395,7 +3420,7 @@ impl UsePlacementFinder { } } -impl<'tcx> visit::Visitor<'tcx> for UsePlacementFinder { +impl<'tcx> Visitor<'tcx> for UsePlacementFinder { fn visit_crate(&mut self, c: &Crate) { if self.target_module == CRATE_NODE_ID { let inject = c.spans.inject_use_span; @@ -3423,6 +3448,22 @@ impl<'tcx> visit::Visitor<'tcx> for UsePlacementFinder { } } +#[derive(Default)] +struct BindingVisitor { + identifiers: Vec, + spans: FxHashMap>, +} + +impl<'tcx> Visitor<'tcx> for BindingVisitor { + fn visit_pat(&mut self, pat: &ast::Pat) { + if let ast::PatKind::Ident(_, ident, _) = pat.kind { + self.identifiers.push(ident.name); + self.spans.entry(ident.name).or_default().push(ident.span); + } + visit::walk_pat(self, pat); + } +} + fn search_for_any_use_in_items(items: &[Box]) -> Option { for item in items { if let ItemKind::Use(..) = item.kind diff --git a/compiler/rustc_resolve/src/errors.rs b/compiler/rustc_resolve/src/errors.rs index 63d6fa23a148d..72be94e58db93 100644 --- a/compiler/rustc_resolve/src/errors.rs +++ b/compiler/rustc_resolve/src/errors.rs @@ -986,6 +986,18 @@ pub(crate) struct VariableNotInAllPatterns { pub(crate) span: Span, } +#[derive(Subdiagnostic)] +#[multipart_suggestion( + resolve_variable_is_a_typo, + applicability = "maybe-incorrect", + style = "verbose" +)] +pub(crate) struct PatternBindingTypo { + #[suggestion_part(code = "{typo}")] + pub(crate) spans: Vec, + pub(crate) typo: Symbol, +} + #[derive(Diagnostic)] #[diag(resolve_name_defined_multiple_time)] #[note] diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index 679e663f88614..6f1bd4572dd28 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -8,7 +8,6 @@ use std::assert_matches::debug_assert_matches; use std::borrow::Cow; -use std::collections::BTreeSet; use std::collections::hash_map::Entry; use std::mem::{replace, swap, take}; @@ -3682,31 +3681,30 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { // 2) Record any missing bindings or binding mode inconsistencies. for (map_outer, pat_outer) in not_never_pats.iter() { // Check against all arms except for the same pattern which is always self-consistent. - let inners = not_never_pats - .iter() - .filter(|(_, pat)| pat.id != pat_outer.id) - .flat_map(|(map, _)| map); - - for (&name, binding_inner) in inners { - match map_outer.get(&name) { - None => { - // The inner binding is missing in the outer. - let binding_error = - missing_vars.entry(name).or_insert_with(|| BindingError { - name, - origin: BTreeSet::new(), - target: BTreeSet::new(), - could_be_path: name.as_str().starts_with(char::is_uppercase), - }); - binding_error.origin.insert(binding_inner.span); - binding_error.target.insert(pat_outer.span); - } - Some(binding_outer) => { - if binding_outer.annotation != binding_inner.annotation { - // The binding modes in the outer and inner bindings differ. - inconsistent_vars - .entry(name) - .or_insert((binding_inner.span, binding_outer.span)); + let inners = not_never_pats.iter().filter(|(_, pat)| pat.id != pat_outer.id); + + for (map, pat) in inners { + for (&name, binding_inner) in map { + match map_outer.get(&name) { + None => { + // The inner binding is missing in the outer. + let binding_error = + missing_vars.entry(name).or_insert_with(|| BindingError { + name, + origin: Default::default(), + target: Default::default(), + could_be_path: name.as_str().starts_with(char::is_uppercase), + }); + binding_error.origin.push((binding_inner.span, (***pat).clone())); + binding_error.target.push((***pat_outer).clone()); + } + Some(binding_outer) => { + if binding_outer.annotation != binding_inner.annotation { + // The binding modes in the outer and inner bindings differ. + inconsistent_vars + .entry(name) + .or_insert((binding_inner.span, binding_outer.span)); + } } } } @@ -3719,7 +3717,7 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { v.could_be_path = false; } self.report_error( - *v.origin.iter().next().unwrap(), + v.origin.iter().next().unwrap().0, ResolutionError::VariableNotBoundInPattern(v, self.parent_scope), ); } diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs index 2afb52ef4d4be..c36a688330097 100644 --- a/compiler/rustc_resolve/src/lib.rs +++ b/compiler/rustc_resolve/src/lib.rs @@ -230,8 +230,8 @@ enum Used { #[derive(Debug)] struct BindingError { name: Ident, - origin: BTreeSet, - target: BTreeSet, + origin: Vec<(Span, ast::Pat)>, + target: Vec, could_be_path: bool, } diff --git a/tests/ui/or-patterns/mismatched-bindings-async-fn.stderr b/tests/ui/or-patterns/mismatched-bindings-async-fn.stderr index 81602fffa8d07..523cdf959e782 100644 --- a/tests/ui/or-patterns/mismatched-bindings-async-fn.stderr +++ b/tests/ui/or-patterns/mismatched-bindings-async-fn.stderr @@ -5,6 +5,12 @@ LL | async fn a((x | s): String) {} | ^ - variable not in all patterns | | | pattern doesn't bind `s` + | +help: you might have meant to use the similarly named previously used binding `x` + | +LL - async fn a((x | s): String) {} +LL + async fn a((x | x): String) {} + | error[E0408]: variable `x` is not bound in all patterns --> $DIR/mismatched-bindings-async-fn.rs:4:17 @@ -13,6 +19,12 @@ LL | async fn a((x | s): String) {} | - ^ pattern doesn't bind `x` | | | variable not in all patterns + | +help: you might have meant to use the similarly named previously used binding `s` + | +LL - async fn a((x | s): String) {} +LL + async fn a((s | s): String) {} + | error[E0408]: variable `s` is not bound in all patterns --> $DIR/mismatched-bindings-async-fn.rs:9:10 @@ -21,6 +33,12 @@ LL | let (x | s) = String::new(); | ^ - variable not in all patterns | | | pattern doesn't bind `s` + | +help: you might have meant to use the similarly named previously used binding `x` + | +LL - let (x | s) = String::new(); +LL + let (x | x) = String::new(); + | error[E0408]: variable `x` is not bound in all patterns --> $DIR/mismatched-bindings-async-fn.rs:9:14 @@ -29,6 +47,12 @@ LL | let (x | s) = String::new(); | - ^ pattern doesn't bind `x` | | | variable not in all patterns + | +help: you might have meant to use the similarly named previously used binding `s` + | +LL - let (x | s) = String::new(); +LL + let (s | s) = String::new(); + | error: aborting due to 4 previous errors diff --git a/tests/ui/or-patterns/missing-bindings.stderr b/tests/ui/or-patterns/missing-bindings.stderr index 6288cc589131f..636f701664727 100644 --- a/tests/ui/or-patterns/missing-bindings.stderr +++ b/tests/ui/or-patterns/missing-bindings.stderr @@ -86,6 +86,12 @@ LL | let (A(A(a, b) | B(c), d) | B(e)) = Y; | ^^^^^^^ - variable not in all patterns | | | pattern doesn't bind `c` + | +help: you might have meant to use the similarly named previously used binding `a` + | +LL - let (A(A(a, b) | B(c), d) | B(e)) = Y; +LL + let (A(A(a, b) | B(a), d) | B(e)) = Y; + | error[E0408]: variable `a` is not bound in all patterns --> $DIR/missing-bindings.rs:47:22 @@ -94,6 +100,12 @@ LL | let (A(A(a, b) | B(c), d) | B(e)) = Y; | - ^^^^ pattern doesn't bind `a` | | | variable not in all patterns + | +help: you might have meant to use the similarly named previously used binding `c` + | +LL - let (A(A(a, b) | B(c), d) | B(e)) = Y; +LL + let (A(A(c, b) | B(c), d) | B(e)) = Y; + | error[E0408]: variable `b` is not bound in all patterns --> $DIR/missing-bindings.rs:47:22 @@ -102,6 +114,12 @@ LL | let (A(A(a, b) | B(c), d) | B(e)) = Y; | - ^^^^ pattern doesn't bind `b` | | | variable not in all patterns + | +help: you might have meant to use the similarly named previously used binding `c` + | +LL - let (A(A(a, b) | B(c), d) | B(e)) = Y; +LL + let (A(A(a, c) | B(c), d) | B(e)) = Y; + | error[E0408]: variable `e` is not bound in all patterns --> $DIR/missing-bindings.rs:47:10 @@ -110,6 +128,12 @@ LL | let (A(A(a, b) | B(c), d) | B(e)) = Y; | ^^^^^^^^^^^^^^^^^^^^ - variable not in all patterns | | | pattern doesn't bind `e` + | +help: you might have meant to use the similarly named previously used binding `c` + | +LL - let (A(A(a, b) | B(c), d) | B(e)) = Y; +LL + let (A(A(a, b) | B(c), d) | B(c)) = Y; + | error[E0408]: variable `a` is not bound in all patterns --> $DIR/missing-bindings.rs:47:33 @@ -118,6 +142,12 @@ LL | let (A(A(a, b) | B(c), d) | B(e)) = Y; | - ^^^^ pattern doesn't bind `a` | | | variable not in all patterns + | +help: you might have meant to use the similarly named previously used binding `e` + | +LL - let (A(A(a, b) | B(c), d) | B(e)) = Y; +LL + let (A(A(e, b) | B(c), d) | B(e)) = Y; + | error[E0408]: variable `b` is not bound in all patterns --> $DIR/missing-bindings.rs:47:33 @@ -126,6 +156,12 @@ LL | let (A(A(a, b) | B(c), d) | B(e)) = Y; | - ^^^^ pattern doesn't bind `b` | | | variable not in all patterns + | +help: you might have meant to use the similarly named previously used binding `e` + | +LL - let (A(A(a, b) | B(c), d) | B(e)) = Y; +LL + let (A(A(a, e) | B(c), d) | B(e)) = Y; + | error[E0408]: variable `c` is not bound in all patterns --> $DIR/missing-bindings.rs:47:33 @@ -134,6 +170,12 @@ LL | let (A(A(a, b) | B(c), d) | B(e)) = Y; | - ^^^^ pattern doesn't bind `c` | | | variable not in all patterns + | +help: you might have meant to use the similarly named previously used binding `e` + | +LL - let (A(A(a, b) | B(c), d) | B(e)) = Y; +LL + let (A(A(a, b) | B(e), d) | B(e)) = Y; + | error[E0408]: variable `d` is not bound in all patterns --> $DIR/missing-bindings.rs:47:33 @@ -142,6 +184,12 @@ LL | let (A(A(a, b) | B(c), d) | B(e)) = Y; | - ^^^^ pattern doesn't bind `d` | | | variable not in all patterns + | +help: you might have meant to use the similarly named previously used binding `e` + | +LL - let (A(A(a, b) | B(c), d) | B(e)) = Y; +LL + let (A(A(a, b) | B(c), e) | B(e)) = Y; + | error[E0408]: variable `a` is not bound in all patterns --> $DIR/missing-bindings.rs:63:29 @@ -158,6 +206,12 @@ LL | A(_, a) | | ^^^^^^^ pattern doesn't bind `b` LL | B(b), | - variable not in all patterns + | +help: you might have meant to use the similarly named previously used binding `a` + | +LL - B(b), +LL + B(a), + | error[E0408]: variable `a` is not bound in all patterns --> $DIR/missing-bindings.rs:71:21 @@ -166,6 +220,12 @@ LL | A(_, a) | | - variable not in all patterns LL | B(b), | ^^^^ pattern doesn't bind `a` + | +help: you might have meant to use the similarly named previously used binding `b` + | +LL - A(_, a) | +LL + A(_, b) | + | error[E0408]: variable `a` is not bound in all patterns --> $DIR/missing-bindings.rs:74:17 @@ -202,6 +262,12 @@ LL | B(b), ... LL | V3(c), | ^^^^^ pattern doesn't bind `b` + | +help: you might have meant to use the similarly named previously used binding `c` + | +LL - B(b), +LL + B(c), + | error[E0408]: variable `c` is not bound in all patterns --> $DIR/missing-bindings.rs:59:13 @@ -223,6 +289,12 @@ LL | | ) | | |_____________^ pattern doesn't bind `c` LL | V3(c), | - variable not in all patterns + | +help: you might have meant to use the similarly named previously used binding `a` + | +LL - V3(c), +LL + V3(a), + | error[E0408]: variable `a` is not bound in all patterns --> $DIR/missing-bindings.rs:78:13 @@ -235,6 +307,15 @@ LL | A(_, a) | ... LL | V3(c), | ^^^^^ pattern doesn't bind `a` + | +help: you might have meant to use the similarly named previously used binding `c` + | +LL ~ B(Ok(a) | Err(c)) +LL | ) | +LL | V2( +LL | A( +LL ~ A(_, c) | + | error[E0170]: pattern binding `beta` is named the same as one of the variants of the type `check_handling_of_paths::bar::foo` --> $DIR/missing-bindings.rs:19:18 diff --git a/tests/ui/or-patterns/nested-undelimited-precedence.stderr b/tests/ui/or-patterns/nested-undelimited-precedence.stderr index 0835ca1929f15..10dbc9d16c0e2 100644 --- a/tests/ui/or-patterns/nested-undelimited-precedence.stderr +++ b/tests/ui/or-patterns/nested-undelimited-precedence.stderr @@ -60,6 +60,12 @@ LL | let b @ A | B: E = A; | - ^ pattern doesn't bind `b` | | | variable not in all patterns + | +help: you might have meant to use the similarly named previously used binding `B` + | +LL - let b @ A | B: E = A; +LL + let B @ A | B: E = A; + | error[E0308]: mismatched types --> $DIR/nested-undelimited-precedence.rs:34:9 diff --git a/tests/ui/pattern/rfc-3637-guard-patterns/name-resolution.stderr b/tests/ui/pattern/rfc-3637-guard-patterns/name-resolution.stderr index d76e60478a146..a5d9fd2b1a6ec 100644 --- a/tests/ui/pattern/rfc-3637-guard-patterns/name-resolution.stderr +++ b/tests/ui/pattern/rfc-3637-guard-patterns/name-resolution.stderr @@ -5,6 +5,12 @@ LL | ((Ok(x) if y) | (Err(y) if x),) => x && y, | ^^^^^^^^^^^^ - variable not in all patterns | | | pattern doesn't bind `y` + | +help: you might have meant to use the similarly named previously used binding `x` + | +LL - ((Ok(x) if y) | (Err(y) if x),) => x && y, +LL + ((Ok(x) if y) | (Err(x) if x),) => x && y, + | error[E0408]: variable `x` is not bound in all patterns --> $DIR/name-resolution.rs:37:25 @@ -13,6 +19,12 @@ LL | ((Ok(x) if y) | (Err(y) if x),) => x && y, | - ^^^^^^^^^^^^^ pattern doesn't bind `x` | | | variable not in all patterns + | +help: you might have meant to use the similarly named previously used binding `y` + | +LL - ((Ok(x) if y) | (Err(y) if x),) => x && y, +LL + ((Ok(y) if y) | (Err(y) if x),) => x && y, + | error[E0408]: variable `x` is not bound in all patterns --> $DIR/name-resolution.rs:63:28 diff --git a/tests/ui/resolve/resolve-inconsistent-names.rs b/tests/ui/resolve/resolve-inconsistent-names.rs index 9a40b20346c95..bf74a4ba0f342 100644 --- a/tests/ui/resolve/resolve-inconsistent-names.rs +++ b/tests/ui/resolve/resolve-inconsistent-names.rs @@ -10,8 +10,10 @@ pub mod m { fn main() { let y = 1; match y { - a | b => {} //~ ERROR variable `a` is not bound in all patterns - //~| ERROR variable `b` is not bound in all patterns + a | b => {} //~ ERROR variable `a` is not bound in all patterns + //~| ERROR variable `b` is not bound in all patterns + //~| HELP you might have meant to use the similarly named previously used binding `a` + //~| HELP you might have meant to use the similarly named previously used binding `b` } let x = (E::A, E::B); diff --git a/tests/ui/resolve/resolve-inconsistent-names.stderr b/tests/ui/resolve/resolve-inconsistent-names.stderr index 5fac622eef263..152c16ad404ef 100644 --- a/tests/ui/resolve/resolve-inconsistent-names.stderr +++ b/tests/ui/resolve/resolve-inconsistent-names.stderr @@ -1,21 +1,33 @@ error[E0408]: variable `b` is not bound in all patterns - --> $DIR/resolve-inconsistent-names.rs:13:8 + --> $DIR/resolve-inconsistent-names.rs:13:9 + | +LL | a | b => {} + | ^ - variable not in all patterns + | | + | pattern doesn't bind `b` + | +help: you might have meant to use the similarly named previously used binding `a` + | +LL - a | b => {} +LL + a | a => {} | -LL | a | b => {} - | ^ - variable not in all patterns - | | - | pattern doesn't bind `b` error[E0408]: variable `a` is not bound in all patterns - --> $DIR/resolve-inconsistent-names.rs:13:12 + --> $DIR/resolve-inconsistent-names.rs:13:13 + | +LL | a | b => {} + | - ^ pattern doesn't bind `a` + | | + | variable not in all patterns + | +help: you might have meant to use the similarly named previously used binding `b` + | +LL - a | b => {} +LL + b | b => {} | -LL | a | b => {} - | - ^ pattern doesn't bind `a` - | | - | variable not in all patterns error[E0408]: variable `c` is not bound in all patterns - --> $DIR/resolve-inconsistent-names.rs:19:9 + --> $DIR/resolve-inconsistent-names.rs:21:9 | LL | (A, B) | (ref B, c) | (c, A) => () | ^^^^^^ - - variable not in all patterns @@ -24,7 +36,7 @@ LL | (A, B) | (ref B, c) | (c, A) => () | pattern doesn't bind `c` error[E0408]: variable `A` is not bound in all patterns - --> $DIR/resolve-inconsistent-names.rs:19:18 + --> $DIR/resolve-inconsistent-names.rs:21:18 | LL | (A, B) | (ref B, c) | (c, A) => () | - ^^^^^^^^^^ - variable not in all patterns @@ -38,7 +50,7 @@ LL | (E::A, B) | (ref B, c) | (c, A) => () | +++ error[E0408]: variable `B` is not bound in all patterns - --> $DIR/resolve-inconsistent-names.rs:19:31 + --> $DIR/resolve-inconsistent-names.rs:21:31 | LL | (A, B) | (ref B, c) | (c, A) => () | - - ^^^^^^ pattern doesn't bind `B` @@ -47,7 +59,7 @@ LL | (A, B) | (ref B, c) | (c, A) => () | variable not in all patterns error[E0409]: variable `B` is bound inconsistently across alternatives separated by `|` - --> $DIR/resolve-inconsistent-names.rs:19:23 + --> $DIR/resolve-inconsistent-names.rs:21:23 | LL | (A, B) | (ref B, c) | (c, A) => () | - ^ bound in different ways @@ -55,7 +67,7 @@ LL | (A, B) | (ref B, c) | (c, A) => () | first binding error[E0408]: variable `Const2` is not bound in all patterns - --> $DIR/resolve-inconsistent-names.rs:31:9 + --> $DIR/resolve-inconsistent-names.rs:33:9 | LL | (CONST1, _) | (_, Const2) => () | ^^^^^^^^^^^ ------ variable not in all patterns @@ -68,7 +80,7 @@ LL | (CONST1, _) | (_, m::Const2) => () | +++ error[E0408]: variable `CONST1` is not bound in all patterns - --> $DIR/resolve-inconsistent-names.rs:31:23 + --> $DIR/resolve-inconsistent-names.rs:33:23 | LL | (CONST1, _) | (_, Const2) => () | ------ ^^^^^^^^^^^ pattern doesn't bind `CONST1` @@ -82,7 +94,7 @@ LL | const CONST1: usize = 10; | ^^^^^^^^^^^^^^^^^^^^^^^^^ not accessible error[E0308]: mismatched types - --> $DIR/resolve-inconsistent-names.rs:19:19 + --> $DIR/resolve-inconsistent-names.rs:21:19 | LL | match x { | - this expression has type `(E, E)` diff --git a/tests/ui/span/issue-39698.stderr b/tests/ui/span/issue-39698.stderr index 73fcc5c847755..eb18969c3c0db 100644 --- a/tests/ui/span/issue-39698.stderr +++ b/tests/ui/span/issue-39698.stderr @@ -7,6 +7,12 @@ LL | T::T1(a, d) | T::T2(d, b) | T::T3(c) | T::T4(a) => { println!("{:?} | | | pattern doesn't bind `b` | | variable not in all patterns | pattern doesn't bind `b` + | +help: you might have meant to use the similarly named previously used binding `c` + | +LL - T::T1(a, d) | T::T2(d, b) | T::T3(c) | T::T4(a) => { println!("{:?}", a); } +LL + T::T1(a, d) | T::T2(d, c) | T::T3(c) | T::T4(a) => { println!("{:?}", a); } + | error[E0408]: variable `c` is not bound in all patterns --> $DIR/issue-39698.rs:10:9 @@ -17,6 +23,12 @@ LL | T::T1(a, d) | T::T2(d, b) | T::T3(c) | T::T4(a) => { println!("{:?} | | | variable not in all patterns | | pattern doesn't bind `c` | pattern doesn't bind `c` + | +help: you might have meant to use the similarly named previously used binding `d` + | +LL - T::T1(a, d) | T::T2(d, b) | T::T3(c) | T::T4(a) => { println!("{:?}", a); } +LL + T::T1(a, d) | T::T2(d, b) | T::T3(d) | T::T4(a) => { println!("{:?}", a); } + | error[E0408]: variable `a` is not bound in all patterns --> $DIR/issue-39698.rs:10:23 @@ -27,6 +39,12 @@ LL | T::T1(a, d) | T::T2(d, b) | T::T3(c) | T::T4(a) => { println!("{:?} | | | pattern doesn't bind `a` | | pattern doesn't bind `a` | variable not in all patterns + | +help: you might have meant to use the similarly named previously used binding `c` + | +LL - T::T1(a, d) | T::T2(d, b) | T::T3(c) | T::T4(a) => { println!("{:?}", a); } +LL + T::T1(c, d) | T::T2(d, b) | T::T3(c) | T::T4(c) => { println!("{:?}", a); } + | error[E0408]: variable `d` is not bound in all patterns --> $DIR/issue-39698.rs:10:37 @@ -37,6 +55,12 @@ LL | T::T1(a, d) | T::T2(d, b) | T::T3(c) | T::T4(a) => { println!("{:?} | | | pattern doesn't bind `d` | | variable not in all patterns | variable not in all patterns + | +help: you might have meant to use the similarly named previously used binding `c` + | +LL - T::T1(a, d) | T::T2(d, b) | T::T3(c) | T::T4(a) => { println!("{:?}", a); } +LL + T::T1(a, c) | T::T2(c, b) | T::T3(c) | T::T4(a) => { println!("{:?}", a); } + | error[E0381]: used binding `a` is possibly-uninitialized --> $DIR/issue-39698.rs:10:79 From bd0ea615f73e433c43457374e76a16793b73bc9c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Fri, 29 Aug 2025 20:39:36 +0000 Subject: [PATCH 2/6] Add test for typo in or-pattern binding --- tests/ui/or-patterns/binding-typo.fixed | 26 ++++++++++++++++++++ tests/ui/or-patterns/binding-typo.rs | 26 ++++++++++++++++++++ tests/ui/or-patterns/binding-typo.stderr | 30 ++++++++++++++++++++++++ 3 files changed, 82 insertions(+) create mode 100644 tests/ui/or-patterns/binding-typo.fixed create mode 100644 tests/ui/or-patterns/binding-typo.rs create mode 100644 tests/ui/or-patterns/binding-typo.stderr diff --git a/tests/ui/or-patterns/binding-typo.fixed b/tests/ui/or-patterns/binding-typo.fixed new file mode 100644 index 0000000000000..84637862484a0 --- /dev/null +++ b/tests/ui/or-patterns/binding-typo.fixed @@ -0,0 +1,26 @@ +// Issue #51976 +//@ run-rustfix +#![deny(unused_variables)] //~ NOTE: the lint level is defined here +enum Lol { + Foo, + Bar, +} + +fn foo(x: (Lol, Lol)) { + use Lol::*; + match x { + (Foo, Bar) | (Bar, Foo) => {} + //~^ ERROR: variable `Ban` is not bound in all patterns + //~| HELP: you might have meant to use the similarly named previously used binding `Bar` + //~| NOTE: pattern doesn't bind `Ban` + //~| NOTE: variable not in all patterns + //~| ERROR: variable `Ban` is assigned to, but never used + //~| NOTE: consider using `_Ban` instead + _ => {} + } +} + +fn main() { + use Lol::*; + foo((Foo, Bar)); +} diff --git a/tests/ui/or-patterns/binding-typo.rs b/tests/ui/or-patterns/binding-typo.rs new file mode 100644 index 0000000000000..1c5f4d876190b --- /dev/null +++ b/tests/ui/or-patterns/binding-typo.rs @@ -0,0 +1,26 @@ +// Issue #51976 +//@ run-rustfix +#![deny(unused_variables)] //~ NOTE: the lint level is defined here +enum Lol { + Foo, + Bar, +} + +fn foo(x: (Lol, Lol)) { + use Lol::*; + match x { + (Foo, Bar) | (Ban, Foo) => {} + //~^ ERROR: variable `Ban` is not bound in all patterns + //~| HELP: you might have meant to use the similarly named previously used binding `Bar` + //~| NOTE: pattern doesn't bind `Ban` + //~| NOTE: variable not in all patterns + //~| ERROR: variable `Ban` is assigned to, but never used + //~| NOTE: consider using `_Ban` instead + _ => {} + } +} + +fn main() { + use Lol::*; + foo((Foo, Bar)); +} diff --git a/tests/ui/or-patterns/binding-typo.stderr b/tests/ui/or-patterns/binding-typo.stderr new file mode 100644 index 0000000000000..31a409bbc5c22 --- /dev/null +++ b/tests/ui/or-patterns/binding-typo.stderr @@ -0,0 +1,30 @@ +error[E0408]: variable `Ban` is not bound in all patterns + --> $DIR/binding-typo.rs:12:9 + | +LL | (Foo, Bar) | (Ban, Foo) => {} + | ^^^^^^^^^^ --- variable not in all patterns + | | + | pattern doesn't bind `Ban` + | +help: you might have meant to use the similarly named previously used binding `Bar` + | +LL - (Foo, Bar) | (Ban, Foo) => {} +LL + (Foo, Bar) | (Bar, Foo) => {} + | + +error: variable `Ban` is assigned to, but never used + --> $DIR/binding-typo.rs:12:23 + | +LL | (Foo, Bar) | (Ban, Foo) => {} + | ^^^ + | + = note: consider using `_Ban` instead +note: the lint level is defined here + --> $DIR/binding-typo.rs:3:9 + | +LL | #![deny(unused_variables)] + | ^^^^^^^^^^^^^^^^ + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0408`. From cb9cd8f8306e6e1b16661704aed8d6f09aa5db73 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Sat, 30 Aug 2025 01:44:40 +0000 Subject: [PATCH 3/6] On binding not present in all patterns, look at consts and unit structs/variants for suggestions When encountering an or-pattern with a binding not available in all patterns, look for consts and unit struct/variants that have similar names as the binding to detect typos. ``` error[E0408]: variable `Ban` is not bound in all patterns --> $DIR/binding-typo.rs:22:9 | LL | (Foo, _) | (Ban, Foo) => {} | ^^^^^^^^ --- variable not in all patterns | | | pattern doesn't bind `Ban` | help: you might have meant to use the similarly named unit variant `Bar` | LL - (Foo, _) | (Ban, Foo) => {} LL + (Foo, _) | (Bar, Foo) => {} | ``` For items that are not in the immedate scope, suggest the full path for them: ``` error[E0408]: variable `Non` is not bound in all patterns --> $DIR/binding-typo-2.rs:51:16 | LL | (Non | Some(_))=> {} | --- ^^^^^^^ pattern doesn't bind `Non` | | | variable not in all patterns | help: you might have meant to use the similarly named unit variant `None` | LL - (Non | Some(_))=> {} LL + (core::option::Option::None | Some(_))=> {} | ``` --- compiler/rustc_resolve/src/diagnostics.rs | 113 ++++++++++++++++++++- tests/ui/or-patterns/binding-typo-2.rs | 98 ++++++++++++++++++ tests/ui/or-patterns/binding-typo-2.stderr | 107 +++++++++++++++++++ tests/ui/or-patterns/binding-typo.fixed | 12 ++- tests/ui/or-patterns/binding-typo.rs | 12 ++- tests/ui/or-patterns/binding-typo.stderr | 24 ++++- 6 files changed, 361 insertions(+), 5 deletions(-) create mode 100644 tests/ui/or-patterns/binding-typo-2.rs create mode 100644 tests/ui/or-patterns/binding-typo-2.stderr diff --git a/compiler/rustc_resolve/src/diagnostics.rs b/compiler/rustc_resolve/src/diagnostics.rs index 324310ff48b1d..beb882a325f5a 100644 --- a/compiler/rustc_resolve/src/diagnostics.rs +++ b/compiler/rustc_resolve/src/diagnostics.rs @@ -718,10 +718,86 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { }, ); + if import_suggestions.is_empty() && !suggested_typo { + let kinds = [ + DefKind::Ctor(CtorOf::Variant, CtorKind::Const), + DefKind::Ctor(CtorOf::Struct, CtorKind::Const), + DefKind::Const, + DefKind::AssocConst, + ]; + let mut local_names = vec![]; + self.add_module_candidates( + parent_scope.module, + &mut local_names, + &|res| matches!(res, Res::Def(_, _)), + None, + ); + let local_names: FxHashSet<_> = local_names + .into_iter() + .filter_map(|s| match s.res { + Res::Def(_, def_id) => Some(def_id), + _ => None, + }) + .collect(); + + let mut local_suggestions = vec![]; + let mut suggestions = vec![]; + for kind in kinds { + if let Some(suggestion) = self.early_lookup_typo_candidate( + ScopeSet::All(Namespace::ValueNS), + &parent_scope, + name, + &|res: Res| match res { + Res::Def(k, _) => k == kind, + _ => false, + }, + ) && let Res::Def(kind, mut def_id) = suggestion.res + { + if let DefKind::Ctor(_, _) = kind { + def_id = self.tcx.parent(def_id); + } + let kind = kind.descr(def_id); + if local_names.contains(&def_id) { + // The item is available in the current scope. Very likely to + // be a typo. Don't use the full path. + local_suggestions.push(( + suggestion.candidate, + suggestion.candidate.to_string(), + kind, + )); + } else { + suggestions.push(( + suggestion.candidate, + self.def_path_str(def_id), + kind, + )); + } + } + } + let suggestions = if !local_suggestions.is_empty() { + // There is at least one item available in the current scope that is a + // likely typo. We only show those. + local_suggestions + } else { + suggestions + }; + for (name, sugg, kind) in suggestions { + err.span_suggestion_verbose( + span, + format!( + "you might have meant to use the similarly named {kind} `{name}`", + ), + sugg, + Applicability::MaybeIncorrect, + ); + suggested_typo = true; + } + } if import_suggestions.is_empty() && !suggested_typo { let help_msg = format!( - "if you meant to match on a variant or a `const` item, consider \ - making the path in the pattern qualified: `path::to::ModOrType::{name}`", + "if you meant to match on a unit struct, unit variant or a `const` \ + item, consider making the path in the pattern qualified: \ + `path::to::ModOrType::{name}`", ); err.span_help(span, help_msg); } @@ -1041,6 +1117,39 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { .emit() } + fn def_path_str(&self, mut def_id: DefId) -> String { + // We can't use `def_path_str` in resolve. + let mut path = vec![def_id]; + while let Some(parent) = self.tcx.opt_parent(def_id) { + def_id = parent; + path.push(def_id); + if def_id.is_top_level_module() { + break; + } + } + // We will only suggest importing directly if it is accessible through that path. + path.into_iter() + .rev() + .map(|def_id| { + self.tcx + .opt_item_name(def_id) + .map(|name| { + match ( + def_id.is_top_level_module(), + def_id.is_local(), + self.tcx.sess.edition(), + ) { + (true, true, Edition::Edition2015) => String::new(), + (true, true, _) => kw::Crate.to_string(), + (true, false, _) | (false, _, _) => name.to_string(), + } + }) + .unwrap_or_else(|| "_".to_string()) + }) + .collect::>() + .join("::") + } + pub(crate) fn add_scope_set_candidates( &mut self, suggestions: &mut Vec, diff --git a/tests/ui/or-patterns/binding-typo-2.rs b/tests/ui/or-patterns/binding-typo-2.rs new file mode 100644 index 0000000000000..4dfb84d4f0cd3 --- /dev/null +++ b/tests/ui/or-patterns/binding-typo-2.rs @@ -0,0 +1,98 @@ +// Issue #51976 +#![deny(unused_variables)] //~ NOTE: the lint level is defined here +enum Lol { + Foo, + Bar, +} +const Bat: () = (); +struct Bay; + +fn foo(x: (Lol, Lol)) { + use Lol::*; + match &x { + (Foo, Bar) | (Ban, Foo) => {} + //~^ ERROR: variable `Ban` is not bound in all patterns + //~| HELP: you might have meant to use the similarly named previously used binding `Bar` + //~| NOTE: pattern doesn't bind `Ban` + //~| NOTE: variable not in all patterns + //~| ERROR: variable `Ban` is assigned to, but never used + //~| NOTE: consider using `_Ban` instead + _ => {} + } + match &x { + (Foo, _) | (Ban, Foo) => {} + //~^ ERROR: variable `Ban` is not bound in all patterns + //~| HELP: you might have meant to use the similarly named unit variant `Bar` + //~| NOTE: pattern doesn't bind `Ban` + //~| NOTE: variable not in all patterns + //~| ERROR: variable `Ban` is assigned to, but never used + //~| NOTE: consider using `_Ban` instead + _ => {} + } + match Some(42) { + Non => {} + //~^ ERROR: unused variable: `Non` + //~| HELP: if this is intentional, prefix it with an underscore + _ => {} + } + match Some(42) { + Non | None => {} + //~^ ERROR: unused variable: `Non` + //~| HELP: if this is intentional, prefix it with an underscore + //~| ERROR: variable `Non` is not bound in all patterns [E0408] + //~| NOTE: pattern doesn't bind `Non` + //~| NOTE: variable not in all patterns + //~| HELP: you might have meant to use the similarly named previously used binding `None` + _ => {} + } + match Some(42) { + Non | Some(_) => {} + //~^ ERROR: unused variable: `Non` + //~| HELP: if this is intentional, prefix it with an underscore + //~| ERROR: variable `Non` is not bound in all patterns [E0408] + //~| NOTE: pattern doesn't bind `Non` + //~| NOTE: variable not in all patterns + //~| HELP: you might have meant to use the similarly named unit variant `None` + _ => {} + } +} +fn bar(x: (Lol, Lol)) { + use Lol::*; + use Bat; + use Bay; + use std::option::Option::None; + match &x { + (Foo, _) | (Ban, Foo) => {} + //~^ ERROR: variable `Ban` is not bound in all patterns + //~| HELP: you might have meant to use the similarly named unit variant `Bar` + //~| HELP: you might have meant to use the similarly named unit struct `Bay` + //~| HELP: you might have meant to use the similarly named constant `Bat` + //~| NOTE: pattern doesn't bind `Ban` + //~| NOTE: variable not in all patterns + //~| ERROR: variable `Ban` is assigned to, but never used + //~| NOTE: consider using `_Ban` instead + _ => {} + } +} +fn baz(x: (Lol, Lol)) { + use Lol::*; + use Bat; + match &x { + (Foo, _) | (Ban, Foo) => {} + //~^ ERROR: variable `Ban` is not bound in all patterns + //~| HELP: you might have meant to use the similarly named unit variant `Bar` + //~| HELP: you might have meant to use the similarly named constant `Bat` + //~| NOTE: pattern doesn't bind `Ban` + //~| NOTE: variable not in all patterns + //~| ERROR: variable `Ban` is assigned to, but never used + //~| NOTE: consider using `_Ban` instead + _ => {} + } +} + +fn main() { + use Lol::*; + foo((Foo, Bar)); + bar((Foo, Bar)); + baz((Foo, Bar)); +} diff --git a/tests/ui/or-patterns/binding-typo-2.stderr b/tests/ui/or-patterns/binding-typo-2.stderr new file mode 100644 index 0000000000000..a2099572485b7 --- /dev/null +++ b/tests/ui/or-patterns/binding-typo-2.stderr @@ -0,0 +1,107 @@ +error[E0408]: variable `Ban` is not bound in all patterns + --> $DIR/binding-typo-2.rs:13:9 + | +LL | (Foo, Bar) | (Ban, Foo) => {} + | ^^^^^^^^^^ --- variable not in all patterns + | | + | pattern doesn't bind `Ban` + | +help: you might have meant to use the similarly named previously used binding `Bar` + | +LL - (Foo, Bar) | (Ban, Foo) => {} +LL + (Foo, Bar) | (Bar, Foo) => {} + | + +error[E0408]: variable `Ban` is not bound in all patterns + --> $DIR/binding-typo-2.rs:23:9 + | +LL | (Foo, _) | (Ban, Foo) => {} + | ^^^^^^^^ --- variable not in all patterns + | | + | pattern doesn't bind `Ban` + | +help: you might have meant to use the similarly named unit variant `Bar` + | +LL - (Foo, _) | (Ban, Foo) => {} +LL + (Foo, _) | (Bar, Foo) => {} + | +help: you might have meant to use the similarly named unit struct `Bay` + | +LL - (Foo, _) | (Ban, Foo) => {} +LL + (Foo, _) | (::Bay, Foo) => {} + | +help: you might have meant to use the similarly named constant `Bat` + | +LL - (Foo, _) | (Ban, Foo) => {} +LL + (Foo, _) | (::Bat, Foo) => {} + | + +error[E0408]: variable `Non` is not bound in all patterns + --> $DIR/binding-typo-2.rs:41:16 + | +LL | (Non | None)=> {} + | --- ^^^^ pattern doesn't bind `Non` + | | + | variable not in all patterns + | +help: you might have meant to use the similarly named previously used binding `None` + | +LL | (None | None)=> {} + | + + +error[E0408]: variable `Non` is not bound in all patterns + --> $DIR/binding-typo-2.rs:51:16 + | +LL | (Non | Some(_))=> {} + | --- ^^^^^^^ pattern doesn't bind `Non` + | | + | variable not in all patterns + | +help: you might have meant to use the similarly named unit variant `None` + | +LL - (Non | Some(_))=> {} +LL + (core::option::Option::None | Some(_))=> {} + | + +error: variable `Ban` is assigned to, but never used + --> $DIR/binding-typo-2.rs:13:23 + | +LL | (Foo, Bar) | (Ban, Foo) => {} + | ^^^ + | + = note: consider using `_Ban` instead +note: the lint level is defined here + --> $DIR/binding-typo-2.rs:2:9 + | +LL | #![deny(unused_variables)] + | ^^^^^^^^^^^^^^^^ + +error: variable `Ban` is assigned to, but never used + --> $DIR/binding-typo-2.rs:23:21 + | +LL | (Foo, _) | (Ban, Foo) => {} + | ^^^ + | + = note: consider using `_Ban` instead + +error: unused variable: `Non` + --> $DIR/binding-typo-2.rs:35:9 + | +LL | Non => {} + | ^^^ help: if this is intentional, prefix it with an underscore: `_Non` + +error: unused variable: `Non` + --> $DIR/binding-typo-2.rs:41:10 + | +LL | (Non | None)=> {} + | ^^^ help: if this is intentional, prefix it with an underscore: `_Non` + +error: unused variable: `Non` + --> $DIR/binding-typo-2.rs:51:10 + | +LL | (Non | Some(_))=> {} + | ^^^ help: if this is intentional, prefix it with an underscore: `_Non` + +error: aborting due to 9 previous errors + +For more information about this error, try `rustc --explain E0408`. diff --git a/tests/ui/or-patterns/binding-typo.fixed b/tests/ui/or-patterns/binding-typo.fixed index 84637862484a0..2902ca303931b 100644 --- a/tests/ui/or-patterns/binding-typo.fixed +++ b/tests/ui/or-patterns/binding-typo.fixed @@ -8,7 +8,7 @@ enum Lol { fn foo(x: (Lol, Lol)) { use Lol::*; - match x { + match &x { (Foo, Bar) | (Bar, Foo) => {} //~^ ERROR: variable `Ban` is not bound in all patterns //~| HELP: you might have meant to use the similarly named previously used binding `Bar` @@ -18,6 +18,16 @@ fn foo(x: (Lol, Lol)) { //~| NOTE: consider using `_Ban` instead _ => {} } + match &x { + (Foo, _) | (Bar, Foo) => {} + //~^ ERROR: variable `Ban` is not bound in all patterns + //~| HELP: you might have meant to use the similarly named unit variant `Bar` + //~| NOTE: pattern doesn't bind `Ban` + //~| NOTE: variable not in all patterns + //~| ERROR: variable `Ban` is assigned to, but never used + //~| NOTE: consider using `_Ban` instead + _ => {} + } } fn main() { diff --git a/tests/ui/or-patterns/binding-typo.rs b/tests/ui/or-patterns/binding-typo.rs index 1c5f4d876190b..2ffe0e45c83b7 100644 --- a/tests/ui/or-patterns/binding-typo.rs +++ b/tests/ui/or-patterns/binding-typo.rs @@ -8,7 +8,7 @@ enum Lol { fn foo(x: (Lol, Lol)) { use Lol::*; - match x { + match &x { (Foo, Bar) | (Ban, Foo) => {} //~^ ERROR: variable `Ban` is not bound in all patterns //~| HELP: you might have meant to use the similarly named previously used binding `Bar` @@ -18,6 +18,16 @@ fn foo(x: (Lol, Lol)) { //~| NOTE: consider using `_Ban` instead _ => {} } + match &x { + (Foo, _) | (Ban, Foo) => {} + //~^ ERROR: variable `Ban` is not bound in all patterns + //~| HELP: you might have meant to use the similarly named unit variant `Bar` + //~| NOTE: pattern doesn't bind `Ban` + //~| NOTE: variable not in all patterns + //~| ERROR: variable `Ban` is assigned to, but never used + //~| NOTE: consider using `_Ban` instead + _ => {} + } } fn main() { diff --git a/tests/ui/or-patterns/binding-typo.stderr b/tests/ui/or-patterns/binding-typo.stderr index 31a409bbc5c22..f90301d2ceee1 100644 --- a/tests/ui/or-patterns/binding-typo.stderr +++ b/tests/ui/or-patterns/binding-typo.stderr @@ -12,6 +12,20 @@ LL - (Foo, Bar) | (Ban, Foo) => {} LL + (Foo, Bar) | (Bar, Foo) => {} | +error[E0408]: variable `Ban` is not bound in all patterns + --> $DIR/binding-typo.rs:22:9 + | +LL | (Foo, _) | (Ban, Foo) => {} + | ^^^^^^^^ --- variable not in all patterns + | | + | pattern doesn't bind `Ban` + | +help: you might have meant to use the similarly named unit variant `Bar` + | +LL - (Foo, _) | (Ban, Foo) => {} +LL + (Foo, _) | (Bar, Foo) => {} + | + error: variable `Ban` is assigned to, but never used --> $DIR/binding-typo.rs:12:23 | @@ -25,6 +39,14 @@ note: the lint level is defined here LL | #![deny(unused_variables)] | ^^^^^^^^^^^^^^^^ -error: aborting due to 2 previous errors +error: variable `Ban` is assigned to, but never used + --> $DIR/binding-typo.rs:22:21 + | +LL | (Foo, _) | (Ban, Foo) => {} + | ^^^ + | + = note: consider using `_Ban` instead + +error: aborting due to 4 previous errors For more information about this error, try `rustc --explain E0408`. From 00f6fe1b6cbb211c4396b306566816f91a4ed371 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Sat, 30 Aug 2025 16:24:26 +0000 Subject: [PATCH 4/6] On unused binding in pattern, suggest unit struct/variant with similar name When encountering a typo in a pattern that gets interpreted as an unused binding, look for unit struct/variant of the same type as the binding: ``` error: unused variable: `Non` --> $DIR/binding-typo-2.rs:36:9 | LL | Non => {} | ^^^ | help: if this is intentional, prefix it with an underscore | LL | _Non => {} | + help: you might have meant to pattern match on the similarly named variant `None` | LL - Non => {} LL + std::prelude::v1::None => {} | ``` --- compiler/rustc_passes/messages.ftl | 1 + compiler/rustc_passes/src/errors.rs | 18 ++ compiler/rustc_passes/src/liveness.rs | 33 ++- ...d-type-meant-to-be-arg-of-mut-borrow.fixed | 8 +- ...owed-type-meant-to-be-arg-of-mut-borrow.rs | 8 +- ...-type-meant-to-be-arg-of-mut-borrow.stderr | 25 +-- .../lint-uppercase-variables.stderr | 32 ++- tests/ui/or-patterns/binding-typo-2.rs | 25 ++- tests/ui/or-patterns/binding-typo-2.stderr | 197 +++++++++++++++--- tests/ui/or-patterns/binding-typo.fixed | 6 +- tests/ui/or-patterns/binding-typo.rs | 6 +- tests/ui/or-patterns/binding-typo.stderr | 25 +-- 12 files changed, 279 insertions(+), 105 deletions(-) diff --git a/compiler/rustc_passes/messages.ftl b/compiler/rustc_passes/messages.ftl index 00a41e31a0234..99b1aae8b74ca 100644 --- a/compiler/rustc_passes/messages.ftl +++ b/compiler/rustc_passes/messages.ftl @@ -681,6 +681,7 @@ passes_unused_var_maybe_capture_ref = unused variable: `{$name}` passes_unused_var_remove_field = unused variable: `{$name}` passes_unused_var_remove_field_suggestion = try removing the field +passes_unused_var_typo = you might have meant to pattern match on the similarly named {$kind} `{$item_name}` passes_unused_variable_args_in_macro = `{$name}` is captured in macro and introduced a unused variable diff --git a/compiler/rustc_passes/src/errors.rs b/compiler/rustc_passes/src/errors.rs index 4fec6b0798a55..ebd8f6fc8202d 100644 --- a/compiler/rustc_passes/src/errors.rs +++ b/compiler/rustc_passes/src/errors.rs @@ -1346,6 +1346,22 @@ pub(crate) struct UnusedVarRemoveFieldSugg { #[note] pub(crate) struct UnusedVarAssignedOnly { pub name: String, + #[subdiagnostic] + pub typo: Option, +} + +#[derive(Subdiagnostic)] +#[multipart_suggestion( + passes_unused_var_typo, + style = "verbose", + applicability = "machine-applicable" +)] +pub(crate) struct PatternTypo { + #[suggestion_part(code = "{code}")] + pub span: Span, + pub code: String, + pub item_name: String, + pub kind: String, } #[derive(LintDiagnostic)] @@ -1413,6 +1429,8 @@ pub(crate) struct UnusedVariableTryPrefix { #[subdiagnostic] pub sugg: UnusedVariableSugg, pub name: String, + #[subdiagnostic] + pub typo: Option, } #[derive(Subdiagnostic)] diff --git a/compiler/rustc_passes/src/liveness.rs b/compiler/rustc_passes/src/liveness.rs index 801a533c94338..2ae68c445f8e8 100644 --- a/compiler/rustc_passes/src/liveness.rs +++ b/compiler/rustc_passes/src/liveness.rs @@ -95,8 +95,10 @@ use rustc_hir::{Expr, HirId, HirIdMap, HirIdSet, find_attr}; use rustc_index::IndexVec; use rustc_middle::query::Providers; use rustc_middle::span_bug; +use rustc_middle::ty::print::with_no_trimmed_paths; use rustc_middle::ty::{self, RootVariableMinCaptureList, Ty, TyCtxt}; use rustc_session::lint; +use rustc_span::edit_distance::find_best_match_for_name; use rustc_span::{BytePos, Span, Symbol}; use tracing::{debug, instrument}; @@ -1688,6 +1690,34 @@ impl<'tcx> Liveness<'_, 'tcx> { let is_assigned = if ln == self.exit_ln { false } else { self.assigned_on_exit(ln, var) }; + let mut typo = None; + for (hir_id, _, span) in &hir_ids_and_spans { + let ty = self.typeck_results.node_type(*hir_id); + if let ty::Adt(adt, _) = ty.peel_refs().kind() { + let name = Symbol::intern(&name); + let adt_def = self.ir.tcx.adt_def(adt.did()); + let variant_names: Vec<_> = adt_def + .variants() + .iter() + .filter(|v| matches!(v.ctor, Some((CtorKind::Const, _)))) + .map(|v| v.name) + .collect(); + if let Some(name) = find_best_match_for_name(&variant_names, name, None) + && let Some(variant) = adt_def.variants().iter().find(|v| { + v.name == name && matches!(v.ctor, Some((CtorKind::Const, _))) + }) + { + typo = Some(errors::PatternTypo { + span: *span, + code: with_no_trimmed_paths!(self.ir.tcx.def_path_str(variant.def_id)), + kind: self.ir.tcx.def_descr(variant.def_id).to_string(), + item_name: variant.name.to_string(), + }); + } + } + } + // FIXME(estebank): look for consts of the same type with similar names as well, not + // just unit structs and variants. if is_assigned { self.ir.tcx.emit_node_span_lint( lint::builtin::UNUSED_VARIABLES, @@ -1696,7 +1726,7 @@ impl<'tcx> Liveness<'_, 'tcx> { .into_iter() .map(|(_, _, ident_span)| ident_span) .collect::>(), - errors::UnusedVarAssignedOnly { name }, + errors::UnusedVarAssignedOnly { name, typo }, ) } else if can_remove { let spans = hir_ids_and_spans @@ -1788,6 +1818,7 @@ impl<'tcx> Liveness<'_, 'tcx> { name, sugg, string_interp: suggestions, + typo, }, ); } diff --git a/tests/ui/fn/mut-arg-of-borrowed-type-meant-to-be-arg-of-mut-borrow.fixed b/tests/ui/fn/mut-arg-of-borrowed-type-meant-to-be-arg-of-mut-borrow.fixed index b58c3a6720d00..14d5de0261d1e 100644 --- a/tests/ui/fn/mut-arg-of-borrowed-type-meant-to-be-arg-of-mut-borrow.fixed +++ b/tests/ui/fn/mut-arg-of-borrowed-type-meant-to-be-arg-of-mut-borrow.fixed @@ -1,5 +1,5 @@ //@ run-rustfix -#![deny(unused_assignments, unused_variables)] +#![deny(unused_assignments)] #![allow(unused_mut)] struct Object; @@ -8,15 +8,15 @@ fn change_object(object: &mut Object) { //~ HELP you might have meant to mutate *object = object2; //~ ERROR mismatched types } -fn change_object2(object: &mut Object) { //~ ERROR variable `object` is assigned to, but never used - //~^ HELP you might have meant to mutate +fn change_object2(object: &mut Object) { + //~^ HELP you might have meant to mutate let object2 = Object; *object = object2; //~^ ERROR `object2` does not live long enough //~| ERROR value assigned to `object` is never read } -fn change_object3(object: &mut Object) { //~ ERROR variable `object` is assigned to, but never used +fn change_object3(object: &mut Object) { //~^ HELP you might have meant to mutate let mut object2 = Object; //~ HELP consider changing this to be mutable *object = object2; diff --git a/tests/ui/fn/mut-arg-of-borrowed-type-meant-to-be-arg-of-mut-borrow.rs b/tests/ui/fn/mut-arg-of-borrowed-type-meant-to-be-arg-of-mut-borrow.rs index 1fd222e0db17f..5805afb3369b1 100644 --- a/tests/ui/fn/mut-arg-of-borrowed-type-meant-to-be-arg-of-mut-borrow.rs +++ b/tests/ui/fn/mut-arg-of-borrowed-type-meant-to-be-arg-of-mut-borrow.rs @@ -1,5 +1,5 @@ //@ run-rustfix -#![deny(unused_assignments, unused_variables)] +#![deny(unused_assignments)] #![allow(unused_mut)] struct Object; @@ -8,15 +8,15 @@ fn change_object(mut object: &Object) { //~ HELP you might have meant to mutate object = object2; //~ ERROR mismatched types } -fn change_object2(mut object: &Object) { //~ ERROR variable `object` is assigned to, but never used - //~^ HELP you might have meant to mutate +fn change_object2(mut object: &Object) { + //~^ HELP you might have meant to mutate let object2 = Object; object = &object2; //~^ ERROR `object2` does not live long enough //~| ERROR value assigned to `object` is never read } -fn change_object3(mut object: &mut Object) { //~ ERROR variable `object` is assigned to, but never used +fn change_object3(mut object: &mut Object) { //~^ HELP you might have meant to mutate let object2 = Object; //~ HELP consider changing this to be mutable object = &mut object2; diff --git a/tests/ui/fn/mut-arg-of-borrowed-type-meant-to-be-arg-of-mut-borrow.stderr b/tests/ui/fn/mut-arg-of-borrowed-type-meant-to-be-arg-of-mut-borrow.stderr index 0330853d922fe..c2c7378f07a20 100644 --- a/tests/ui/fn/mut-arg-of-borrowed-type-meant-to-be-arg-of-mut-borrow.stderr +++ b/tests/ui/fn/mut-arg-of-borrowed-type-meant-to-be-arg-of-mut-borrow.stderr @@ -23,7 +23,7 @@ LL | object = &object2; note: the lint level is defined here --> $DIR/mut-arg-of-borrowed-type-meant-to-be-arg-of-mut-borrow.rs:2:9 | -LL | #![deny(unused_assignments, unused_variables)] +LL | #![deny(unused_assignments)] | ^^^^^^^^^^^^^^^^^^ help: you might have meant to mutate the pointed at value being passed in, instead of changing the reference in the local binding | @@ -33,19 +33,6 @@ LL | let object2 = Object; LL ~ *object = object2; | -error: variable `object` is assigned to, but never used - --> $DIR/mut-arg-of-borrowed-type-meant-to-be-arg-of-mut-borrow.rs:11:23 - | -LL | fn change_object2(mut object: &Object) { - | ^^^^^^ - | - = note: consider using `_object` instead -note: the lint level is defined here - --> $DIR/mut-arg-of-borrowed-type-meant-to-be-arg-of-mut-borrow.rs:2:29 - | -LL | #![deny(unused_assignments, unused_variables)] - | ^^^^^^^^^^^^^^^^ - error[E0597]: `object2` does not live long enough --> $DIR/mut-arg-of-borrowed-type-meant-to-be-arg-of-mut-borrow.rs:14:13 | @@ -77,14 +64,6 @@ LL | let object2 = Object; LL ~ *object = object2; | -error: variable `object` is assigned to, but never used - --> $DIR/mut-arg-of-borrowed-type-meant-to-be-arg-of-mut-borrow.rs:19:23 - | -LL | fn change_object3(mut object: &mut Object) { - | ^^^^^^ - | - = note: consider using `_object` instead - error[E0596]: cannot borrow `object2` as mutable, as it is not declared as mutable --> $DIR/mut-arg-of-borrowed-type-meant-to-be-arg-of-mut-borrow.rs:22:14 | @@ -96,7 +75,7 @@ help: consider changing this to be mutable LL | let mut object2 = Object; | +++ -error: aborting due to 7 previous errors +error: aborting due to 5 previous errors Some errors have detailed explanations: E0308, E0596, E0597. For more information about an error, try `rustc --explain E0308`. diff --git a/tests/ui/lint/non-snake-case/lint-uppercase-variables.stderr b/tests/ui/lint/non-snake-case/lint-uppercase-variables.stderr index b0c56003957c9..e5f2e65fd91d3 100644 --- a/tests/ui/lint/non-snake-case/lint-uppercase-variables.stderr +++ b/tests/ui/lint/non-snake-case/lint-uppercase-variables.stderr @@ -16,7 +16,7 @@ warning: unused variable: `Foo` --> $DIR/lint-uppercase-variables.rs:22:9 | LL | Foo => {} - | ^^^ help: if this is intentional, prefix it with an underscore: `_Foo` + | ^^^ | note: the lint level is defined here --> $DIR/lint-uppercase-variables.rs:1:9 @@ -24,12 +24,29 @@ note: the lint level is defined here LL | #![warn(unused)] | ^^^^^^ = note: `#[warn(unused_variables)]` implied by `#[warn(unused)]` +help: if this is intentional, prefix it with an underscore + | +LL | _Foo => {} + | + +help: you might have meant to pattern match on the similarly named variant `Foo` + | +LL | foo::Foo::Foo => {} + | ++++++++++ warning: unused variable: `Foo` --> $DIR/lint-uppercase-variables.rs:28:9 | LL | let Foo = foo::Foo::Foo; - | ^^^ help: if this is intentional, prefix it with an underscore: `_Foo` + | ^^^ + | +help: if this is intentional, prefix it with an underscore + | +LL | let _Foo = foo::Foo::Foo; + | + +help: you might have meant to pattern match on the similarly named variant `Foo` + | +LL | let foo::Foo::Foo = foo::Foo::Foo; + | ++++++++++ error[E0170]: pattern binding `Foo` is named the same as one of the variants of the type `foo::Foo` --> $DIR/lint-uppercase-variables.rs:33:17 @@ -41,7 +58,16 @@ warning: unused variable: `Foo` --> $DIR/lint-uppercase-variables.rs:33:17 | LL | fn in_param(Foo: foo::Foo) {} - | ^^^ help: if this is intentional, prefix it with an underscore: `_Foo` + | ^^^ + | +help: if this is intentional, prefix it with an underscore + | +LL | fn in_param(_Foo: foo::Foo) {} + | + +help: you might have meant to pattern match on the similarly named variant `Foo` + | +LL | fn in_param(foo::Foo::Foo: foo::Foo) {} + | ++++++++++ error: structure field `X` should have a snake case name --> $DIR/lint-uppercase-variables.rs:10:5 diff --git a/tests/ui/or-patterns/binding-typo-2.rs b/tests/ui/or-patterns/binding-typo-2.rs index 4dfb84d4f0cd3..d763ce60ce967 100644 --- a/tests/ui/or-patterns/binding-typo-2.rs +++ b/tests/ui/or-patterns/binding-typo-2.rs @@ -17,6 +17,7 @@ fn foo(x: (Lol, Lol)) { //~| NOTE: variable not in all patterns //~| ERROR: variable `Ban` is assigned to, but never used //~| NOTE: consider using `_Ban` instead + //~| HELP: you might have meant to pattern match on the similarly named _ => {} } match &x { @@ -27,15 +28,18 @@ fn foo(x: (Lol, Lol)) { //~| NOTE: variable not in all patterns //~| ERROR: variable `Ban` is assigned to, but never used //~| NOTE: consider using `_Ban` instead + //~| HELP: you might have meant to pattern match on the similarly named _ => {} } match Some(42) { + Some(_) => {} Non => {} //~^ ERROR: unused variable: `Non` //~| HELP: if this is intentional, prefix it with an underscore - _ => {} + //~| HELP: you might have meant to pattern match on the similarly named } match Some(42) { + Some(_) => {} Non | None => {} //~^ ERROR: unused variable: `Non` //~| HELP: if this is intentional, prefix it with an underscore @@ -43,7 +47,7 @@ fn foo(x: (Lol, Lol)) { //~| NOTE: pattern doesn't bind `Non` //~| NOTE: variable not in all patterns //~| HELP: you might have meant to use the similarly named previously used binding `None` - _ => {} + //~| HELP: you might have meant to pattern match on the similarly named } match Some(42) { Non | Some(_) => {} @@ -53,14 +57,13 @@ fn foo(x: (Lol, Lol)) { //~| NOTE: pattern doesn't bind `Non` //~| NOTE: variable not in all patterns //~| HELP: you might have meant to use the similarly named unit variant `None` - _ => {} + //~| HELP: you might have meant to pattern match on the similarly named } } fn bar(x: (Lol, Lol)) { use Lol::*; use Bat; use Bay; - use std::option::Option::None; match &x { (Foo, _) | (Ban, Foo) => {} //~^ ERROR: variable `Ban` is not bound in all patterns @@ -71,6 +74,7 @@ fn bar(x: (Lol, Lol)) { //~| NOTE: variable not in all patterns //~| ERROR: variable `Ban` is assigned to, but never used //~| NOTE: consider using `_Ban` instead + //~| HELP: you might have meant to pattern match on the similarly named _ => {} } } @@ -86,8 +90,21 @@ fn baz(x: (Lol, Lol)) { //~| NOTE: variable not in all patterns //~| ERROR: variable `Ban` is assigned to, but never used //~| NOTE: consider using `_Ban` instead + //~| HELP: you might have meant to pattern match on the similarly named _ => {} } + match &x { + (Ban, _) => {} + //~^ ERROR: unused variable: `Ban` + //~| HELP: if this is intentional, prefix it with an underscore + //~| HELP: you might have meant to pattern match on the similarly named + } + match Bay { + Ban => {} + //~^ ERROR: unused variable: `Ban` + //~| HELP: if this is intentional, prefix it with an underscore + //~| HELP: you might have meant to pattern match on the similarly named + } } fn main() { diff --git a/tests/ui/or-patterns/binding-typo-2.stderr b/tests/ui/or-patterns/binding-typo-2.stderr index a2099572485b7..d683cb2b121cc 100644 --- a/tests/ui/or-patterns/binding-typo-2.stderr +++ b/tests/ui/or-patterns/binding-typo-2.stderr @@ -13,7 +13,48 @@ LL + (Foo, Bar) | (Bar, Foo) => {} | error[E0408]: variable `Ban` is not bound in all patterns - --> $DIR/binding-typo-2.rs:23:9 + --> $DIR/binding-typo-2.rs:24:9 + | +LL | (Foo, _) | (Ban, Foo) => {} + | ^^^^^^^^ --- variable not in all patterns + | | + | pattern doesn't bind `Ban` + | +help: you might have meant to use the similarly named unit variant `Bar` + | +LL - (Foo, _) | (Ban, Foo) => {} +LL + (Foo, _) | (Bar, Foo) => {} + | + +error[E0408]: variable `Non` is not bound in all patterns + --> $DIR/binding-typo-2.rs:43:15 + | +LL | Non | None => {} + | --- ^^^^ pattern doesn't bind `Non` + | | + | variable not in all patterns + | +help: you might have meant to use the similarly named previously used binding `None` + | +LL | None | None => {} + | + + +error[E0408]: variable `Non` is not bound in all patterns + --> $DIR/binding-typo-2.rs:53:15 + | +LL | Non | Some(_) => {} + | --- ^^^^^^^ pattern doesn't bind `Non` + | | + | variable not in all patterns + | +help: you might have meant to use the similarly named unit variant `None` + | +LL - Non | Some(_) => {} +LL + core::option::Option::None | Some(_) => {} + | + +error[E0408]: variable `Ban` is not bound in all patterns + --> $DIR/binding-typo-2.rs:68:9 | LL | (Foo, _) | (Ban, Foo) => {} | ^^^^^^^^ --- variable not in all patterns @@ -28,39 +69,31 @@ LL + (Foo, _) | (Bar, Foo) => {} help: you might have meant to use the similarly named unit struct `Bay` | LL - (Foo, _) | (Ban, Foo) => {} -LL + (Foo, _) | (::Bay, Foo) => {} +LL + (Foo, _) | (Bay, Foo) => {} | help: you might have meant to use the similarly named constant `Bat` | LL - (Foo, _) | (Ban, Foo) => {} -LL + (Foo, _) | (::Bat, Foo) => {} +LL + (Foo, _) | (Bat, Foo) => {} | -error[E0408]: variable `Non` is not bound in all patterns - --> $DIR/binding-typo-2.rs:41:16 - | -LL | (Non | None)=> {} - | --- ^^^^ pattern doesn't bind `Non` - | | - | variable not in all patterns +error[E0408]: variable `Ban` is not bound in all patterns + --> $DIR/binding-typo-2.rs:85:9 | -help: you might have meant to use the similarly named previously used binding `None` +LL | (Foo, _) | (Ban, Foo) => {} + | ^^^^^^^^ --- variable not in all patterns + | | + | pattern doesn't bind `Ban` | -LL | (None | None)=> {} - | + - -error[E0408]: variable `Non` is not bound in all patterns - --> $DIR/binding-typo-2.rs:51:16 +help: you might have meant to use the similarly named unit variant `Bar` | -LL | (Non | Some(_))=> {} - | --- ^^^^^^^ pattern doesn't bind `Non` - | | - | variable not in all patterns +LL - (Foo, _) | (Ban, Foo) => {} +LL + (Foo, _) | (Bar, Foo) => {} | -help: you might have meant to use the similarly named unit variant `None` +help: you might have meant to use the similarly named constant `Bat` | -LL - (Non | Some(_))=> {} -LL + (core::option::Option::None | Some(_))=> {} +LL - (Foo, _) | (Ban, Foo) => {} +LL + (Foo, _) | (Bat, Foo) => {} | error: variable `Ban` is assigned to, but never used @@ -75,33 +108,131 @@ note: the lint level is defined here | LL | #![deny(unused_variables)] | ^^^^^^^^^^^^^^^^ +help: you might have meant to pattern match on the similarly named variant `Bar` + | +LL - (Foo, Bar) | (Ban, Foo) => {} +LL + (Foo, Bar) | (Lol::Bar, Foo) => {} + | error: variable `Ban` is assigned to, but never used - --> $DIR/binding-typo-2.rs:23:21 + --> $DIR/binding-typo-2.rs:24:21 | LL | (Foo, _) | (Ban, Foo) => {} | ^^^ | = note: consider using `_Ban` instead +help: you might have meant to pattern match on the similarly named variant `Bar` + | +LL - (Foo, _) | (Ban, Foo) => {} +LL + (Foo, _) | (Lol::Bar, Foo) => {} + | error: unused variable: `Non` - --> $DIR/binding-typo-2.rs:35:9 + --> $DIR/binding-typo-2.rs:36:9 | LL | Non => {} - | ^^^ help: if this is intentional, prefix it with an underscore: `_Non` + | ^^^ + | +help: if this is intentional, prefix it with an underscore + | +LL | _Non => {} + | + +help: you might have meant to pattern match on the similarly named variant `None` + | +LL - Non => {} +LL + std::prelude::v1::None => {} + | error: unused variable: `Non` - --> $DIR/binding-typo-2.rs:41:10 + --> $DIR/binding-typo-2.rs:43:9 + | +LL | Non | None => {} + | ^^^ + | +help: if this is intentional, prefix it with an underscore + | +LL | _Non | None => {} + | + +help: you might have meant to pattern match on the similarly named variant `None` + | +LL - Non | None => {} +LL + std::prelude::v1::None | None => {} | -LL | (Non | None)=> {} - | ^^^ help: if this is intentional, prefix it with an underscore: `_Non` error: unused variable: `Non` - --> $DIR/binding-typo-2.rs:51:10 + --> $DIR/binding-typo-2.rs:53:9 + | +LL | Non | Some(_) => {} + | ^^^ + | +help: if this is intentional, prefix it with an underscore + | +LL | _Non | Some(_) => {} + | + +help: you might have meant to pattern match on the similarly named variant `None` + | +LL - Non | Some(_) => {} +LL + std::prelude::v1::None | Some(_) => {} + | + +error: variable `Ban` is assigned to, but never used + --> $DIR/binding-typo-2.rs:68:21 + | +LL | (Foo, _) | (Ban, Foo) => {} + | ^^^ + | + = note: consider using `_Ban` instead +help: you might have meant to pattern match on the similarly named variant `Bar` + | +LL - (Foo, _) | (Ban, Foo) => {} +LL + (Foo, _) | (Lol::Bar, Foo) => {} + | + +error: variable `Ban` is assigned to, but never used + --> $DIR/binding-typo-2.rs:85:21 + | +LL | (Foo, _) | (Ban, Foo) => {} + | ^^^ + | + = note: consider using `_Ban` instead +help: you might have meant to pattern match on the similarly named variant `Bar` + | +LL - (Foo, _) | (Ban, Foo) => {} +LL + (Foo, _) | (Lol::Bar, Foo) => {} + | + +error: unused variable: `Ban` + --> $DIR/binding-typo-2.rs:97:10 + | +LL | (Ban, _) => {} + | ^^^ + | +help: if this is intentional, prefix it with an underscore + | +LL | (_Ban, _) => {} + | + +help: you might have meant to pattern match on the similarly named variant `Bar` + | +LL - (Ban, _) => {} +LL + (Lol::Bar, _) => {} + | + +error: unused variable: `Ban` + --> $DIR/binding-typo-2.rs:103:9 + | +LL | Ban => {} + | ^^^ + | +help: if this is intentional, prefix it with an underscore + | +LL | _Ban => {} + | + +help: you might have meant to pattern match on the similarly named struct `Bay` + | +LL - Ban => {} +LL + Bay => {} | -LL | (Non | Some(_))=> {} - | ^^^ help: if this is intentional, prefix it with an underscore: `_Non` -error: aborting due to 9 previous errors +error: aborting due to 15 previous errors For more information about this error, try `rustc --explain E0408`. diff --git a/tests/ui/or-patterns/binding-typo.fixed b/tests/ui/or-patterns/binding-typo.fixed index 2902ca303931b..f209ad644db97 100644 --- a/tests/ui/or-patterns/binding-typo.fixed +++ b/tests/ui/or-patterns/binding-typo.fixed @@ -1,6 +1,6 @@ // Issue #51976 //@ run-rustfix -#![deny(unused_variables)] //~ NOTE: the lint level is defined here +#![allow(unused_variables)] // allowed so we don't get overlapping suggestions enum Lol { Foo, Bar, @@ -14,8 +14,6 @@ fn foo(x: (Lol, Lol)) { //~| HELP: you might have meant to use the similarly named previously used binding `Bar` //~| NOTE: pattern doesn't bind `Ban` //~| NOTE: variable not in all patterns - //~| ERROR: variable `Ban` is assigned to, but never used - //~| NOTE: consider using `_Ban` instead _ => {} } match &x { @@ -24,8 +22,6 @@ fn foo(x: (Lol, Lol)) { //~| HELP: you might have meant to use the similarly named unit variant `Bar` //~| NOTE: pattern doesn't bind `Ban` //~| NOTE: variable not in all patterns - //~| ERROR: variable `Ban` is assigned to, but never used - //~| NOTE: consider using `_Ban` instead _ => {} } } diff --git a/tests/ui/or-patterns/binding-typo.rs b/tests/ui/or-patterns/binding-typo.rs index 2ffe0e45c83b7..6be9b801a0d79 100644 --- a/tests/ui/or-patterns/binding-typo.rs +++ b/tests/ui/or-patterns/binding-typo.rs @@ -1,6 +1,6 @@ // Issue #51976 //@ run-rustfix -#![deny(unused_variables)] //~ NOTE: the lint level is defined here +#![allow(unused_variables)] // allowed so we don't get overlapping suggestions enum Lol { Foo, Bar, @@ -14,8 +14,6 @@ fn foo(x: (Lol, Lol)) { //~| HELP: you might have meant to use the similarly named previously used binding `Bar` //~| NOTE: pattern doesn't bind `Ban` //~| NOTE: variable not in all patterns - //~| ERROR: variable `Ban` is assigned to, but never used - //~| NOTE: consider using `_Ban` instead _ => {} } match &x { @@ -24,8 +22,6 @@ fn foo(x: (Lol, Lol)) { //~| HELP: you might have meant to use the similarly named unit variant `Bar` //~| NOTE: pattern doesn't bind `Ban` //~| NOTE: variable not in all patterns - //~| ERROR: variable `Ban` is assigned to, but never used - //~| NOTE: consider using `_Ban` instead _ => {} } } diff --git a/tests/ui/or-patterns/binding-typo.stderr b/tests/ui/or-patterns/binding-typo.stderr index f90301d2ceee1..fb6d5f71209a6 100644 --- a/tests/ui/or-patterns/binding-typo.stderr +++ b/tests/ui/or-patterns/binding-typo.stderr @@ -13,7 +13,7 @@ LL + (Foo, Bar) | (Bar, Foo) => {} | error[E0408]: variable `Ban` is not bound in all patterns - --> $DIR/binding-typo.rs:22:9 + --> $DIR/binding-typo.rs:20:9 | LL | (Foo, _) | (Ban, Foo) => {} | ^^^^^^^^ --- variable not in all patterns @@ -26,27 +26,6 @@ LL - (Foo, _) | (Ban, Foo) => {} LL + (Foo, _) | (Bar, Foo) => {} | -error: variable `Ban` is assigned to, but never used - --> $DIR/binding-typo.rs:12:23 - | -LL | (Foo, Bar) | (Ban, Foo) => {} - | ^^^ - | - = note: consider using `_Ban` instead -note: the lint level is defined here - --> $DIR/binding-typo.rs:3:9 - | -LL | #![deny(unused_variables)] - | ^^^^^^^^^^^^^^^^ - -error: variable `Ban` is assigned to, but never used - --> $DIR/binding-typo.rs:22:21 - | -LL | (Foo, _) | (Ban, Foo) => {} - | ^^^ - | - = note: consider using `_Ban` instead - -error: aborting due to 4 previous errors +error: aborting due to 2 previous errors For more information about this error, try `rustc --explain E0408`. From 3355f4bced511aba46c5a92ea82241b6713331ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Sat, 30 Aug 2025 17:02:44 +0000 Subject: [PATCH 5/6] Suggest constant on unused binding in a pattern ``` error: unused variable: `Batery` --> $DIR/binding-typo-2.rs:110:9 | LL | Batery => {} | ^^^^^^ | help: if this is intentional, prefix it with an underscore | LL | _Batery => {} | + help: you might have meant to pattern match on the similarly named constant `Battery` | LL | Battery => {} | + ``` --- compiler/rustc_passes/src/liveness.rs | 21 +++++++++- tests/ui/or-patterns/binding-typo-2.rs | 11 ++++- tests/ui/or-patterns/binding-typo-2.stderr | 47 ++++++++++++++-------- 3 files changed, 59 insertions(+), 20 deletions(-) diff --git a/compiler/rustc_passes/src/liveness.rs b/compiler/rustc_passes/src/liveness.rs index 2ae68c445f8e8..b14482f65dc4f 100644 --- a/compiler/rustc_passes/src/liveness.rs +++ b/compiler/rustc_passes/src/liveness.rs @@ -1716,8 +1716,25 @@ impl<'tcx> Liveness<'_, 'tcx> { } } } - // FIXME(estebank): look for consts of the same type with similar names as well, not - // just unit structs and variants. + if typo.is_none() { + for (hir_id, _, span) in &hir_ids_and_spans { + let ty = self.typeck_results.node_type(*hir_id); + // Look for consts of the same type with similar names as well, not just unit + // structs and variants. + for def_id in self.ir.tcx.hir_body_owners() { + if let DefKind::Const = self.ir.tcx.def_kind(def_id) + && self.ir.tcx.type_of(def_id).instantiate_identity() == ty + { + typo = Some(errors::PatternTypo { + span: *span, + code: with_no_trimmed_paths!(self.ir.tcx.def_path_str(def_id)), + kind: "constant".to_string(), + item_name: self.ir.tcx.item_name(def_id).to_string(), + }); + } + } + } + } if is_assigned { self.ir.tcx.emit_node_span_lint( lint::builtin::UNUSED_VARIABLES, diff --git a/tests/ui/or-patterns/binding-typo-2.rs b/tests/ui/or-patterns/binding-typo-2.rs index d763ce60ce967..fe4a20536d790 100644 --- a/tests/ui/or-patterns/binding-typo-2.rs +++ b/tests/ui/or-patterns/binding-typo-2.rs @@ -5,6 +5,7 @@ enum Lol { Bar, } const Bat: () = (); +const Battery: () = (); struct Bay; fn foo(x: (Lol, Lol)) { @@ -62,8 +63,8 @@ fn foo(x: (Lol, Lol)) { } fn bar(x: (Lol, Lol)) { use Lol::*; - use Bat; - use Bay; + use ::Bat; + use ::Bay; match &x { (Foo, _) | (Ban, Foo) => {} //~^ ERROR: variable `Ban` is not bound in all patterns @@ -105,6 +106,12 @@ fn baz(x: (Lol, Lol)) { //~| HELP: if this is intentional, prefix it with an underscore //~| HELP: you might have meant to pattern match on the similarly named } + match () { + Batery => {} + //~^ ERROR: unused variable: `Batery` + //~| HELP: if this is intentional, prefix it with an underscore + //~| HELP: you might have meant to pattern match on the similarly named constant + } } fn main() { diff --git a/tests/ui/or-patterns/binding-typo-2.stderr b/tests/ui/or-patterns/binding-typo-2.stderr index d683cb2b121cc..fc6d02d65a4e9 100644 --- a/tests/ui/or-patterns/binding-typo-2.stderr +++ b/tests/ui/or-patterns/binding-typo-2.stderr @@ -1,5 +1,5 @@ error[E0408]: variable `Ban` is not bound in all patterns - --> $DIR/binding-typo-2.rs:13:9 + --> $DIR/binding-typo-2.rs:14:9 | LL | (Foo, Bar) | (Ban, Foo) => {} | ^^^^^^^^^^ --- variable not in all patterns @@ -13,7 +13,7 @@ LL + (Foo, Bar) | (Bar, Foo) => {} | error[E0408]: variable `Ban` is not bound in all patterns - --> $DIR/binding-typo-2.rs:24:9 + --> $DIR/binding-typo-2.rs:25:9 | LL | (Foo, _) | (Ban, Foo) => {} | ^^^^^^^^ --- variable not in all patterns @@ -27,7 +27,7 @@ LL + (Foo, _) | (Bar, Foo) => {} | error[E0408]: variable `Non` is not bound in all patterns - --> $DIR/binding-typo-2.rs:43:15 + --> $DIR/binding-typo-2.rs:44:15 | LL | Non | None => {} | --- ^^^^ pattern doesn't bind `Non` @@ -40,7 +40,7 @@ LL | None | None => {} | + error[E0408]: variable `Non` is not bound in all patterns - --> $DIR/binding-typo-2.rs:53:15 + --> $DIR/binding-typo-2.rs:54:15 | LL | Non | Some(_) => {} | --- ^^^^^^^ pattern doesn't bind `Non` @@ -54,7 +54,7 @@ LL + core::option::Option::None | Some(_) => {} | error[E0408]: variable `Ban` is not bound in all patterns - --> $DIR/binding-typo-2.rs:68:9 + --> $DIR/binding-typo-2.rs:69:9 | LL | (Foo, _) | (Ban, Foo) => {} | ^^^^^^^^ --- variable not in all patterns @@ -78,7 +78,7 @@ LL + (Foo, _) | (Bat, Foo) => {} | error[E0408]: variable `Ban` is not bound in all patterns - --> $DIR/binding-typo-2.rs:85:9 + --> $DIR/binding-typo-2.rs:86:9 | LL | (Foo, _) | (Ban, Foo) => {} | ^^^^^^^^ --- variable not in all patterns @@ -97,7 +97,7 @@ LL + (Foo, _) | (Bat, Foo) => {} | error: variable `Ban` is assigned to, but never used - --> $DIR/binding-typo-2.rs:13:23 + --> $DIR/binding-typo-2.rs:14:23 | LL | (Foo, Bar) | (Ban, Foo) => {} | ^^^ @@ -115,7 +115,7 @@ LL + (Foo, Bar) | (Lol::Bar, Foo) => {} | error: variable `Ban` is assigned to, but never used - --> $DIR/binding-typo-2.rs:24:21 + --> $DIR/binding-typo-2.rs:25:21 | LL | (Foo, _) | (Ban, Foo) => {} | ^^^ @@ -128,7 +128,7 @@ LL + (Foo, _) | (Lol::Bar, Foo) => {} | error: unused variable: `Non` - --> $DIR/binding-typo-2.rs:36:9 + --> $DIR/binding-typo-2.rs:37:9 | LL | Non => {} | ^^^ @@ -144,7 +144,7 @@ LL + std::prelude::v1::None => {} | error: unused variable: `Non` - --> $DIR/binding-typo-2.rs:43:9 + --> $DIR/binding-typo-2.rs:44:9 | LL | Non | None => {} | ^^^ @@ -160,7 +160,7 @@ LL + std::prelude::v1::None | None => {} | error: unused variable: `Non` - --> $DIR/binding-typo-2.rs:53:9 + --> $DIR/binding-typo-2.rs:54:9 | LL | Non | Some(_) => {} | ^^^ @@ -176,7 +176,7 @@ LL + std::prelude::v1::None | Some(_) => {} | error: variable `Ban` is assigned to, but never used - --> $DIR/binding-typo-2.rs:68:21 + --> $DIR/binding-typo-2.rs:69:21 | LL | (Foo, _) | (Ban, Foo) => {} | ^^^ @@ -189,7 +189,7 @@ LL + (Foo, _) | (Lol::Bar, Foo) => {} | error: variable `Ban` is assigned to, but never used - --> $DIR/binding-typo-2.rs:85:21 + --> $DIR/binding-typo-2.rs:86:21 | LL | (Foo, _) | (Ban, Foo) => {} | ^^^ @@ -202,7 +202,7 @@ LL + (Foo, _) | (Lol::Bar, Foo) => {} | error: unused variable: `Ban` - --> $DIR/binding-typo-2.rs:97:10 + --> $DIR/binding-typo-2.rs:98:10 | LL | (Ban, _) => {} | ^^^ @@ -218,7 +218,7 @@ LL + (Lol::Bar, _) => {} | error: unused variable: `Ban` - --> $DIR/binding-typo-2.rs:103:9 + --> $DIR/binding-typo-2.rs:104:9 | LL | Ban => {} | ^^^ @@ -233,6 +233,21 @@ LL - Ban => {} LL + Bay => {} | -error: aborting due to 15 previous errors +error: unused variable: `Batery` + --> $DIR/binding-typo-2.rs:110:9 + | +LL | Batery => {} + | ^^^^^^ + | +help: if this is intentional, prefix it with an underscore + | +LL | _Batery => {} + | + +help: you might have meant to pattern match on the similarly named constant `Battery` + | +LL | Battery => {} + | + + +error: aborting due to 16 previous errors For more information about this error, try `rustc --explain E0408`. From 86085b4f65fae0ebc456202584e7e04b58a871f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Sat, 30 Aug 2025 17:44:23 +0000 Subject: [PATCH 6/6] Avoid unnecessary suggestion in or-pattern --- compiler/rustc_resolve/src/diagnostics.rs | 42 +++++++++++-------- tests/ui/or-patterns/binding-typo-2.rs | 2 +- tests/ui/or-patterns/binding-typo-2.stderr | 7 ++-- .../mismatched-bindings-async-fn.stderr | 24 ----------- .../nested-undelimited-precedence.stderr | 6 --- .../ui/resolve/resolve-inconsistent-names.rs | 2 - .../resolve/resolve-inconsistent-names.stderr | 26 ++++-------- 7 files changed, 36 insertions(+), 73 deletions(-) diff --git a/compiler/rustc_resolve/src/diagnostics.rs b/compiler/rustc_resolve/src/diagnostics.rs index beb882a325f5a..b7042045242f7 100644 --- a/compiler/rustc_resolve/src/diagnostics.rs +++ b/compiler/rustc_resolve/src/diagnostics.rs @@ -678,26 +678,32 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { for sp in &origin_sp { err.subdiagnostic(errors::VariableNotInAllPatterns { span: *sp }); } - let mut target_visitor = BindingVisitor::default(); - for pat in &target { - target_visitor.visit_pat(pat); - } - target_visitor.identifiers.sort(); - target_visitor.identifiers.dedup(); - let mut origin_visitor = BindingVisitor::default(); - for (_, pat) in &origin { - origin_visitor.visit_pat(pat); - } - origin_visitor.identifiers.sort(); - origin_visitor.identifiers.dedup(); - // Find if the binding could have been a typo let mut suggested_typo = false; - if let Some(typo) = - find_best_match_for_name(&target_visitor.identifiers, name.name, None) - && !origin_visitor.identifiers.contains(&typo) + if !target.iter().all(|pat| matches!(pat.kind, ast::PatKind::Ident(..))) + && !origin.iter().all(|(_, pat)| matches!(pat.kind, ast::PatKind::Ident(..))) { - err.subdiagnostic(errors::PatternBindingTypo { spans: origin_sp, typo }); - suggested_typo = true; + // The check above is so that when we encounter `match foo { (a | b) => {} }`, + // we don't suggest `(a | a) => {}`, which would never be what the user wants. + let mut target_visitor = BindingVisitor::default(); + for pat in &target { + target_visitor.visit_pat(pat); + } + target_visitor.identifiers.sort(); + target_visitor.identifiers.dedup(); + let mut origin_visitor = BindingVisitor::default(); + for (_, pat) in &origin { + origin_visitor.visit_pat(pat); + } + origin_visitor.identifiers.sort(); + origin_visitor.identifiers.dedup(); + // Find if the binding could have been a typo + if let Some(typo) = + find_best_match_for_name(&target_visitor.identifiers, name.name, None) + && !origin_visitor.identifiers.contains(&typo) + { + err.subdiagnostic(errors::PatternBindingTypo { spans: origin_sp, typo }); + suggested_typo = true; + } } if could_be_path { let import_suggestions = self.lookup_import_candidates( diff --git a/tests/ui/or-patterns/binding-typo-2.rs b/tests/ui/or-patterns/binding-typo-2.rs index fe4a20536d790..275b68a22e22f 100644 --- a/tests/ui/or-patterns/binding-typo-2.rs +++ b/tests/ui/or-patterns/binding-typo-2.rs @@ -47,7 +47,7 @@ fn foo(x: (Lol, Lol)) { //~| ERROR: variable `Non` is not bound in all patterns [E0408] //~| NOTE: pattern doesn't bind `Non` //~| NOTE: variable not in all patterns - //~| HELP: you might have meant to use the similarly named previously used binding `None` + //~| HELP: you might have meant to use the similarly named unit variant `None` //~| HELP: you might have meant to pattern match on the similarly named } match Some(42) { diff --git a/tests/ui/or-patterns/binding-typo-2.stderr b/tests/ui/or-patterns/binding-typo-2.stderr index fc6d02d65a4e9..81ea6e6b1bc08 100644 --- a/tests/ui/or-patterns/binding-typo-2.stderr +++ b/tests/ui/or-patterns/binding-typo-2.stderr @@ -34,10 +34,11 @@ LL | Non | None => {} | | | variable not in all patterns | -help: you might have meant to use the similarly named previously used binding `None` +help: you might have meant to use the similarly named unit variant `None` + | +LL - Non | None => {} +LL + core::option::Option::None | None => {} | -LL | None | None => {} - | + error[E0408]: variable `Non` is not bound in all patterns --> $DIR/binding-typo-2.rs:54:15 diff --git a/tests/ui/or-patterns/mismatched-bindings-async-fn.stderr b/tests/ui/or-patterns/mismatched-bindings-async-fn.stderr index 523cdf959e782..81602fffa8d07 100644 --- a/tests/ui/or-patterns/mismatched-bindings-async-fn.stderr +++ b/tests/ui/or-patterns/mismatched-bindings-async-fn.stderr @@ -5,12 +5,6 @@ LL | async fn a((x | s): String) {} | ^ - variable not in all patterns | | | pattern doesn't bind `s` - | -help: you might have meant to use the similarly named previously used binding `x` - | -LL - async fn a((x | s): String) {} -LL + async fn a((x | x): String) {} - | error[E0408]: variable `x` is not bound in all patterns --> $DIR/mismatched-bindings-async-fn.rs:4:17 @@ -19,12 +13,6 @@ LL | async fn a((x | s): String) {} | - ^ pattern doesn't bind `x` | | | variable not in all patterns - | -help: you might have meant to use the similarly named previously used binding `s` - | -LL - async fn a((x | s): String) {} -LL + async fn a((s | s): String) {} - | error[E0408]: variable `s` is not bound in all patterns --> $DIR/mismatched-bindings-async-fn.rs:9:10 @@ -33,12 +21,6 @@ LL | let (x | s) = String::new(); | ^ - variable not in all patterns | | | pattern doesn't bind `s` - | -help: you might have meant to use the similarly named previously used binding `x` - | -LL - let (x | s) = String::new(); -LL + let (x | x) = String::new(); - | error[E0408]: variable `x` is not bound in all patterns --> $DIR/mismatched-bindings-async-fn.rs:9:14 @@ -47,12 +29,6 @@ LL | let (x | s) = String::new(); | - ^ pattern doesn't bind `x` | | | variable not in all patterns - | -help: you might have meant to use the similarly named previously used binding `s` - | -LL - let (x | s) = String::new(); -LL + let (s | s) = String::new(); - | error: aborting due to 4 previous errors diff --git a/tests/ui/or-patterns/nested-undelimited-precedence.stderr b/tests/ui/or-patterns/nested-undelimited-precedence.stderr index 10dbc9d16c0e2..0835ca1929f15 100644 --- a/tests/ui/or-patterns/nested-undelimited-precedence.stderr +++ b/tests/ui/or-patterns/nested-undelimited-precedence.stderr @@ -60,12 +60,6 @@ LL | let b @ A | B: E = A; | - ^ pattern doesn't bind `b` | | | variable not in all patterns - | -help: you might have meant to use the similarly named previously used binding `B` - | -LL - let b @ A | B: E = A; -LL + let B @ A | B: E = A; - | error[E0308]: mismatched types --> $DIR/nested-undelimited-precedence.rs:34:9 diff --git a/tests/ui/resolve/resolve-inconsistent-names.rs b/tests/ui/resolve/resolve-inconsistent-names.rs index bf74a4ba0f342..96504720e8395 100644 --- a/tests/ui/resolve/resolve-inconsistent-names.rs +++ b/tests/ui/resolve/resolve-inconsistent-names.rs @@ -12,8 +12,6 @@ fn main() { match y { a | b => {} //~ ERROR variable `a` is not bound in all patterns //~| ERROR variable `b` is not bound in all patterns - //~| HELP you might have meant to use the similarly named previously used binding `a` - //~| HELP you might have meant to use the similarly named previously used binding `b` } let x = (E::A, E::B); diff --git a/tests/ui/resolve/resolve-inconsistent-names.stderr b/tests/ui/resolve/resolve-inconsistent-names.stderr index 152c16ad404ef..d2333150961ac 100644 --- a/tests/ui/resolve/resolve-inconsistent-names.stderr +++ b/tests/ui/resolve/resolve-inconsistent-names.stderr @@ -5,12 +5,6 @@ LL | a | b => {} | ^ - variable not in all patterns | | | pattern doesn't bind `b` - | -help: you might have meant to use the similarly named previously used binding `a` - | -LL - a | b => {} -LL + a | a => {} - | error[E0408]: variable `a` is not bound in all patterns --> $DIR/resolve-inconsistent-names.rs:13:13 @@ -19,15 +13,9 @@ LL | a | b => {} | - ^ pattern doesn't bind `a` | | | variable not in all patterns - | -help: you might have meant to use the similarly named previously used binding `b` - | -LL - a | b => {} -LL + b | b => {} - | error[E0408]: variable `c` is not bound in all patterns - --> $DIR/resolve-inconsistent-names.rs:21:9 + --> $DIR/resolve-inconsistent-names.rs:19:9 | LL | (A, B) | (ref B, c) | (c, A) => () | ^^^^^^ - - variable not in all patterns @@ -36,7 +24,7 @@ LL | (A, B) | (ref B, c) | (c, A) => () | pattern doesn't bind `c` error[E0408]: variable `A` is not bound in all patterns - --> $DIR/resolve-inconsistent-names.rs:21:18 + --> $DIR/resolve-inconsistent-names.rs:19:18 | LL | (A, B) | (ref B, c) | (c, A) => () | - ^^^^^^^^^^ - variable not in all patterns @@ -50,7 +38,7 @@ LL | (E::A, B) | (ref B, c) | (c, A) => () | +++ error[E0408]: variable `B` is not bound in all patterns - --> $DIR/resolve-inconsistent-names.rs:21:31 + --> $DIR/resolve-inconsistent-names.rs:19:31 | LL | (A, B) | (ref B, c) | (c, A) => () | - - ^^^^^^ pattern doesn't bind `B` @@ -59,7 +47,7 @@ LL | (A, B) | (ref B, c) | (c, A) => () | variable not in all patterns error[E0409]: variable `B` is bound inconsistently across alternatives separated by `|` - --> $DIR/resolve-inconsistent-names.rs:21:23 + --> $DIR/resolve-inconsistent-names.rs:19:23 | LL | (A, B) | (ref B, c) | (c, A) => () | - ^ bound in different ways @@ -67,7 +55,7 @@ LL | (A, B) | (ref B, c) | (c, A) => () | first binding error[E0408]: variable `Const2` is not bound in all patterns - --> $DIR/resolve-inconsistent-names.rs:33:9 + --> $DIR/resolve-inconsistent-names.rs:31:9 | LL | (CONST1, _) | (_, Const2) => () | ^^^^^^^^^^^ ------ variable not in all patterns @@ -80,7 +68,7 @@ LL | (CONST1, _) | (_, m::Const2) => () | +++ error[E0408]: variable `CONST1` is not bound in all patterns - --> $DIR/resolve-inconsistent-names.rs:33:23 + --> $DIR/resolve-inconsistent-names.rs:31:23 | LL | (CONST1, _) | (_, Const2) => () | ------ ^^^^^^^^^^^ pattern doesn't bind `CONST1` @@ -94,7 +82,7 @@ LL | const CONST1: usize = 10; | ^^^^^^^^^^^^^^^^^^^^^^^^^ not accessible error[E0308]: mismatched types - --> $DIR/resolve-inconsistent-names.rs:21:19 + --> $DIR/resolve-inconsistent-names.rs:19:19 | LL | match x { | - this expression has type `(E, E)`