Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Suggest using a qualified path in patterns with inconsistent bindings #63406

Merged
merged 2 commits into from
Aug 12, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 16 additions & 5 deletions src/librustc_resolve/diagnostics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<ast::NodeId>;
Expand Down Expand Up @@ -207,21 +207,32 @@ impl<'a> Resolver<'a> {
err
}
ResolutionError::VariableNotBoundInPattern(binding_error) => {
let target_sp = binding_error.target.iter().cloned().collect::<Vec<_>>();
let BindingError { name, target, origin, could_be_path } = binding_error;

let target_sp = target.iter().copied().collect::<Vec<_>>();
let origin_sp = origin.iter().copied().collect::<Vec<_>>();

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,
Expand Down
86 changes: 38 additions & 48 deletions src/librustc_resolve/late.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<Pat>]) {
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::<Vec<_>>();

let mut missing_vars = missing_vars.iter_mut().collect::<Vec<_>>();
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::<Vec<_>>();
inconsistent_vars.sort();
for (name, v) in inconsistent_vars {
Expand Down Expand Up @@ -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) {
Expand Down
1 change: 1 addition & 0 deletions src/librustc_resolve/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,7 @@ struct BindingError {
name: Name,
origin: BTreeSet<Span>,
target: BTreeSet<Span>,
could_be_path: bool
}

impl PartialOrd for BindingError {
Expand Down
31 changes: 30 additions & 1 deletion src/test/ui/resolve/resolve-inconsistent-names.rs
Original file line number Diff line number Diff line change
@@ -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`
}
}
87 changes: 83 additions & 4 deletions src/test/ui/resolve/resolve-inconsistent-names.stderr
Original file line number Diff line number Diff line change
@@ -1,19 +1,98 @@
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`
| |
| 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`.