diff --git a/src/librustc_resolve/diagnostics.rs b/src/librustc_resolve/diagnostics.rs index 01a4a3c4bb244..9827ce6ef20f8 100644 --- a/src/librustc_resolve/diagnostics.rs +++ b/src/librustc_resolve/diagnostics.rs @@ -20,7 +20,7 @@ use syntax_pos::{BytePos, Span, MultiSpan}; use crate::resolve_imports::{ImportDirective, ImportDirectiveSubclass, ImportResolver}; use crate::{path_names_to_string, KNOWN_TOOLS}; -use crate::{CrateLint, LegacyScope, Module, ModuleOrUniformRoot}; +use crate::{BindingError, CrateLint, LegacyScope, Module, ModuleOrUniformRoot}; use crate::{PathResult, ParentScope, ResolutionError, Resolver, Scope, ScopeSet, Segment}; type Res = def::Res; @@ -207,21 +207,32 @@ impl<'a> Resolver<'a> { err } ResolutionError::VariableNotBoundInPattern(binding_error) => { - let target_sp = binding_error.target.iter().cloned().collect::>(); + 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 msp = MultiSpan::from_spans(target_sp.clone()); - let msg = format!("variable `{}` is not bound in all patterns", binding_error.name); + let msg = format!("variable `{}` is not bound in all patterns", name); let mut err = self.session.struct_span_err_with_code( msp, &msg, DiagnosticId::Error("E0408".into()), ); for sp in target_sp { - err.span_label(sp, format!("pattern doesn't bind `{}`", binding_error.name)); + err.span_label(sp, format!("pattern doesn't bind `{}`", name)); } - let origin_sp = binding_error.origin.iter().cloned(); for sp in origin_sp { err.span_label(sp, "variable not in all patterns"); } + if *could_be_path { + let help_msg = format!( + "if you meant to match on a variant or a `const` item, consider \ + making the path in the pattern qualified: `?::{}`", + name, + ); + err.span_help(span, &help_msg); + } err } ResolutionError::VariableBoundWithDifferentMode(variable_name, diff --git a/src/librustc_resolve/late.rs b/src/librustc_resolve/late.rs index 7cb11195ee02b..358eaae11e712 100644 --- a/src/librustc_resolve/late.rs +++ b/src/librustc_resolve/late.rs @@ -1136,65 +1136,53 @@ impl<'a, 'b> LateResolutionVisitor<'a, '_> { // Checks that all of the arms in an or-pattern have exactly the // same set of bindings, with the same binding modes for each. fn check_consistent_bindings(&mut self, pats: &[P]) { - if pats.is_empty() { - return; - } - let mut missing_vars = FxHashMap::default(); let mut inconsistent_vars = FxHashMap::default(); - for (i, p) in pats.iter().enumerate() { - let map_i = self.binding_mode_map(&p); - - for (j, q) in pats.iter().enumerate() { - if i == j { - continue; - } - let map_j = self.binding_mode_map(&q); - for (&key, &binding_i) in &map_i { - if map_j.is_empty() { // Account for missing bindings when - let binding_error = missing_vars // `map_j` has none. - .entry(key.name) - .or_insert(BindingError { - name: key.name, - origin: BTreeSet::new(), - target: BTreeSet::new(), - }); - binding_error.origin.insert(binding_i.span); - binding_error.target.insert(q.span); - } - for (&key_j, &binding_j) in &map_j { - match map_i.get(&key_j) { - None => { // missing binding - let binding_error = missing_vars - .entry(key_j.name) - .or_insert(BindingError { - name: key_j.name, - origin: BTreeSet::new(), - target: BTreeSet::new(), - }); - binding_error.origin.insert(binding_j.span); - binding_error.target.insert(p.span); - } - Some(binding_i) => { // check consistent binding - if binding_i.binding_mode != binding_j.binding_mode { - inconsistent_vars - .entry(key.name) - .or_insert((binding_j.span, binding_i.span)); - } + for pat_outer in pats.iter() { + let map_outer = self.binding_mode_map(&pat_outer); + + for pat_inner in pats.iter().filter(|pat| pat.id != pat_outer.id) { + let map_inner = self.binding_mode_map(&pat_inner); + + for (&key_inner, &binding_inner) in map_inner.iter() { + match map_outer.get(&key_inner) { + None => { // missing binding + let binding_error = missing_vars + .entry(key_inner.name) + .or_insert(BindingError { + name: key_inner.name, + origin: BTreeSet::new(), + target: BTreeSet::new(), + could_be_path: + key_inner.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) => { // check consistent binding + if binding_outer.binding_mode != binding_inner.binding_mode { + inconsistent_vars + .entry(key_inner.name) + .or_insert((binding_inner.span, binding_outer.span)); } } } } } } - let mut missing_vars = missing_vars.iter().collect::>(); + + let mut missing_vars = missing_vars.iter_mut().collect::>(); missing_vars.sort(); - for (_, v) in missing_vars { + for (name, mut v) in missing_vars { + if inconsistent_vars.contains_key(name) { + v.could_be_path = false; + } self.r.report_error( - *v.origin.iter().next().unwrap(), ResolutionError::VariableNotBoundInPattern(v) - ); + *v.origin.iter().next().unwrap(), + ResolutionError::VariableNotBoundInPattern(v)); } + let mut inconsistent_vars = inconsistent_vars.iter().collect::>(); inconsistent_vars.sort(); for (name, v) in inconsistent_vars { @@ -1222,7 +1210,9 @@ impl<'a, 'b> LateResolutionVisitor<'a, '_> { self.resolve_pattern(pat, source, &mut bindings_list); } // This has to happen *after* we determine which pat_idents are variants - self.check_consistent_bindings(pats); + if pats.len() > 1 { + self.check_consistent_bindings(pats); + } } fn resolve_block(&mut self, block: &Block) { diff --git a/src/librustc_resolve/lib.rs b/src/librustc_resolve/lib.rs index e786e1020026a..98782dfbc7a5f 100644 --- a/src/librustc_resolve/lib.rs +++ b/src/librustc_resolve/lib.rs @@ -135,6 +135,7 @@ struct BindingError { name: Name, origin: BTreeSet, target: BTreeSet, + could_be_path: bool } impl PartialOrd for BindingError { diff --git a/src/test/ui/resolve/resolve-inconsistent-names.rs b/src/test/ui/resolve/resolve-inconsistent-names.rs index 59baa57d91b3d..2fb803c4b2ad4 100644 --- a/src/test/ui/resolve/resolve-inconsistent-names.rs +++ b/src/test/ui/resolve/resolve-inconsistent-names.rs @@ -1,7 +1,36 @@ +#![allow(non_camel_case_types)] + +enum E { A, B, c } + +mod m { + const CONST1: usize = 10; + const Const2: usize = 20; +} + 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 + //~| ERROR variable `b` is not bound in all patterns + } + + let x = (E::A, E::B); + match x { + (A, B) | (ref B, c) | (c, A) => () + //~^ ERROR variable `A` is not bound in all patterns + //~| ERROR variable `B` is not bound in all patterns + //~| ERROR variable `B` is bound in inconsistent ways + //~| ERROR mismatched types + //~| ERROR variable `c` is not bound in all patterns + //~| HELP consider making the path in the pattern qualified: `?::A` + } + + let z = (10, 20); + match z { + (CONST1, _) | (_, Const2) => () + //~^ ERROR variable `CONST1` is not bound in all patterns + //~| HELP consider making the path in the pattern qualified: `?::CONST1` + //~| ERROR variable `Const2` is not bound in all patterns + //~| HELP consider making the path in the pattern qualified: `?::Const2` } } diff --git a/src/test/ui/resolve/resolve-inconsistent-names.stderr b/src/test/ui/resolve/resolve-inconsistent-names.stderr index c75718149fa4e..f02867a0024b5 100644 --- a/src/test/ui/resolve/resolve-inconsistent-names.stderr +++ b/src/test/ui/resolve/resolve-inconsistent-names.stderr @@ -1,5 +1,5 @@ error[E0408]: variable `a` is not bound in all patterns - --> $DIR/resolve-inconsistent-names.rs:4:12 + --> $DIR/resolve-inconsistent-names.rs:13:12 | LL | a | b => {} | - ^ pattern doesn't bind `a` @@ -7,13 +7,92 @@ LL | a | b => {} | variable not in all patterns error[E0408]: variable `b` is not bound in all patterns - --> $DIR/resolve-inconsistent-names.rs:4:8 + --> $DIR/resolve-inconsistent-names.rs:13:8 | LL | a | b => {} | ^ - variable not in all patterns | | | pattern doesn't bind `b` -error: aborting due to 2 previous errors +error[E0408]: variable `A` is not bound in all patterns + --> $DIR/resolve-inconsistent-names.rs:19:18 + | +LL | (A, B) | (ref B, c) | (c, A) => () + | - ^^^^^^^^^^ - variable not in all patterns + | | | + | | pattern doesn't bind `A` + | variable not in all patterns + | +help: if you meant to match on a variant or a `const` item, consider making the path in the pattern qualified: `?::A` + --> $DIR/resolve-inconsistent-names.rs:19:10 + | +LL | (A, B) | (ref B, c) | (c, A) => () + | ^ + +error[E0408]: variable `B` is not bound in all patterns + --> $DIR/resolve-inconsistent-names.rs:19:31 + | +LL | (A, B) | (ref B, c) | (c, A) => () + | - - ^^^^^^ pattern doesn't bind `B` + | | | + | | variable not in all patterns + | variable not in all patterns + +error[E0408]: variable `c` is not bound in all patterns + --> $DIR/resolve-inconsistent-names.rs:19:9 + | +LL | (A, B) | (ref B, c) | (c, A) => () + | ^^^^^^ - - variable not in all patterns + | | | + | | variable not in all patterns + | pattern doesn't bind `c` + +error[E0409]: variable `B` is bound in inconsistent ways within the same match arm + --> $DIR/resolve-inconsistent-names.rs:19:23 + | +LL | (A, B) | (ref B, c) | (c, A) => () + | - ^ bound in different ways + | | + | first binding + +error[E0408]: variable `CONST1` is not bound in all patterns + --> $DIR/resolve-inconsistent-names.rs:30:23 + | +LL | (CONST1, _) | (_, Const2) => () + | ------ ^^^^^^^^^^^ pattern doesn't bind `CONST1` + | | + | variable not in all patterns + | +help: if you meant to match on a variant or a `const` item, consider making the path in the pattern qualified: `?::CONST1` + --> $DIR/resolve-inconsistent-names.rs:30:10 + | +LL | (CONST1, _) | (_, Const2) => () + | ^^^^^^ + +error[E0408]: variable `Const2` is not bound in all patterns + --> $DIR/resolve-inconsistent-names.rs:30:9 + | +LL | (CONST1, _) | (_, Const2) => () + | ^^^^^^^^^^^ ------ variable not in all patterns + | | + | pattern doesn't bind `Const2` + | +help: if you meant to match on a variant or a `const` item, consider making the path in the pattern qualified: `?::Const2` + --> $DIR/resolve-inconsistent-names.rs:30:27 + | +LL | (CONST1, _) | (_, Const2) => () + | ^^^^^^ + +error[E0308]: mismatched types + --> $DIR/resolve-inconsistent-names.rs:19:19 + | +LL | (A, B) | (ref B, c) | (c, A) => () + | ^^^^^ expected enum `E`, found &E + | + = note: expected type `E` + found type `&E` + +error: aborting due to 9 previous errors -For more information about this error, try `rustc --explain E0408`. +Some errors have detailed explanations: E0308, E0408, E0409. +For more information about an error, try `rustc --explain E0308`.