diff --git a/src/librustc_resolve/diagnostics.rs b/src/librustc_resolve/diagnostics.rs index 01a4a3c4bb24..b3f6252e4aab 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,30 @@ 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_variant } = binding_error; + + let target_sp = target.iter().cloned().collect::>(); + let origin_sp = origin.iter().cloned().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_variant { + let help_msg = format!( + "if you meant to match on a variant or a const, 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 7cb11195ee02..13bae25a69af 100644 --- a/src/librustc_resolve/late.rs +++ b/src/librustc_resolve/late.rs @@ -1136,64 +1136,56 @@ 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() { + if pats.len() <= 1 { return; } let mut missing_vars = FxHashMap::default(); let mut inconsistent_vars = FxHashMap::default(); - for (i, p) in pats.iter().enumerate() { + for p in pats.iter() { let map_i = self.binding_mode_map(&p); - - for (j, q) in pats.iter().enumerate() { - if i == j { + for q in pats.iter() { + if p.id == q.id { 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 + for (&key_j, &binding_j) in map_j.iter() { + 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(), + could_be_variant: + key_j.name.as_str().starts_with(char::is_uppercase) + }); + 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_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)); - } + .or_insert((binding_j.span, binding_i.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_variant = 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(); diff --git a/src/librustc_resolve/lib.rs b/src/librustc_resolve/lib.rs index e786e1020026..1cacc6b7d606 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_variant: 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 59baa57d91b3..2fb803c4b2ad 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 c75718149fa4..5a6ae31411ee 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, consider making the path in the pattern qualified: `?::A` + --> $DIR/resolve-inconsistent-names.rs:19:10 + | +LL | (A, B) | (ref B, c) | (c, A) => () + | ^ -For more information about this error, try `rustc --explain E0408`. +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, 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, 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 + +Some errors have detailed explanations: E0308, E0408, E0409. +For more information about an error, try `rustc --explain E0308`.