Skip to content

Commit f91c3f6

Browse files
committed
Auto merge of #39713 - estebank:issue-39698, r=jonathandturner
Clean up "pattern doesn't bind x" messages Group "missing variable bind" spans in `or` matches and clarify wording for the two possible cases: when a variable from the first pattern is not in any of the subsequent patterns, and when a variable in any of the other patterns is not in the first one. Before: ```rust error[E0408]: variable `a` from pattern #1 is not bound in pattern #2 --> file.rs:10:23 | 10 | T::T1(a, d) | T::T2(d, b) | T::T3(c) | T::T4(a) => { println!("{:?}", a); } | ^^^^^^^^^^^ pattern doesn't bind `a` error[E0408]: variable `b` from pattern #2 is not bound in pattern #1 --> file.rs:10:32 | 10 | T::T1(a, d) | T::T2(d, b) | T::T3(c) | T::T4(a) => { println!("{:?}", a); } | ^ pattern doesn't bind `b` error[E0408]: variable `a` from pattern #1 is not bound in pattern #3 --> file.rs:10:37 | 10 | T::T1(a, d) | T::T2(d, b) | T::T3(c) | T::T4(a) => { println!("{:?}", a); } | ^^^^^^^^ pattern doesn't bind `a` error[E0408]: variable `d` from pattern #1 is not bound in pattern #3 --> file.rs:10:37 | 10 | T::T1(a, d) | T::T2(d, b) | T::T3(c) | T::T4(a) => { println!("{:?}", a); } | ^^^^^^^^ pattern doesn't bind `d` error[E0408]: variable `c` from pattern #3 is not bound in pattern #1 --> file.rs:10:43 | 10 | T::T1(a, d) | T::T2(d, b) | T::T3(c) | T::T4(a) => { println!("{:?}", a); } | ^ pattern doesn't bind `c` error[E0408]: variable `d` from pattern #1 is not bound in pattern #4 --> file.rs:10:48 | 10 | T::T1(a, d) | T::T2(d, b) | T::T3(c) | T::T4(a) => { println!("{:?}", a); } | ^^^^^^^^ pattern doesn't bind `d` error: aborting due to 6 previous errors ``` After: ```rust error[E0408]: variable `d` is not bound in all patterns --> $DIR/issue-39698.rs:20:37 | 20 | T::T1(a, d) | T::T2(d, b) | T::T3(c) | T::T4(a) => { println!("{:?}", a); } | - - ^^^^^^^^ ^^^^^^^^ pattern doesn't bind `d` | | | | | | | pattern doesn't bind `d` | | variable not in all patterns | variable not in all patterns error[E0408]: variable `c` is not bound in all patterns --> $DIR/issue-39698.rs:20:48 | 20 | T::T1(a, d) | T::T2(d, b) | T::T3(c) | T::T4(a) => { println!("{:?}", a); } | ^^^^^^^^^^^ ^^^^^^^^^^^ - ^^^^^^^^ pattern doesn't bind `c` | | | | | | | variable not in all patterns | | pattern doesn't bind `c` | pattern doesn't bind `c` error[E0408]: variable `a` is not bound in all patterns --> $DIR/issue-39698.rs:20:37 | 20 | T::T1(a, d) | T::T2(d, b) | T::T3(c) | T::T4(a) => { println!("{:?}", a); } | - ^^^^^^^^^^^ ^^^^^^^^ - variable not in all patterns | | | | | | | pattern doesn't bind `a` | | pattern doesn't bind `a` | variable not in all patterns error[E0408]: variable `b` is not bound in all patterns --> $DIR/issue-39698.rs:20:37 | 20 | T::T1(a, d) | T::T2(d, b) | T::T3(c) | T::T4(a) => { println!("{:?}", a); } | ^^^^^^^^^^^ - ^^^^^^^^ ^^^^^^^^ pattern doesn't bind `b` | | | | | | | pattern doesn't bind `b` | | variable not in all patterns | pattern doesn't bind `b` error: aborting due to 4 previous errors ``` Fixes #39698.
2 parents b04ebef + 3ffa4b5 commit f91c3f6

File tree

9 files changed

+175
-50
lines changed

9 files changed

+175
-50
lines changed

src/librustc_resolve/lib.rs

+98-41
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ use errors::DiagnosticBuilder;
6969

7070
use std::cell::{Cell, RefCell};
7171
use std::cmp;
72+
use std::collections::BTreeSet;
7273
use std::fmt;
7374
use std::mem::replace;
7475
use std::rc::Rc;
@@ -97,6 +98,31 @@ enum AssocSuggestion {
9798
AssocItem,
9899
}
99100

101+
#[derive(Eq)]
102+
struct BindingError {
103+
name: Name,
104+
origin: BTreeSet<Span>,
105+
target: BTreeSet<Span>,
106+
}
107+
108+
impl PartialOrd for BindingError {
109+
fn partial_cmp(&self, other: &BindingError) -> Option<cmp::Ordering> {
110+
Some(self.cmp(other))
111+
}
112+
}
113+
114+
impl PartialEq for BindingError {
115+
fn eq(&self, other: &BindingError) -> bool {
116+
self.name == other.name
117+
}
118+
}
119+
120+
impl Ord for BindingError {
121+
fn cmp(&self, other: &BindingError) -> cmp::Ordering {
122+
self.name.cmp(&other.name)
123+
}
124+
}
125+
100126
enum ResolutionError<'a> {
101127
/// error E0401: can't use type parameters from outer function
102128
TypeParametersFromOuterFunction,
@@ -110,10 +136,10 @@ enum ResolutionError<'a> {
110136
TypeNotMemberOfTrait(Name, &'a str),
111137
/// error E0438: const is not a member of trait
112138
ConstNotMemberOfTrait(Name, &'a str),
113-
/// error E0408: variable `{}` from pattern #{} is not bound in pattern #{}
114-
VariableNotBoundInPattern(Name, usize, usize),
115-
/// error E0409: variable is bound with different mode in pattern #{} than in pattern #1
116-
VariableBoundWithDifferentMode(Name, usize, Span),
139+
/// error E0408: variable `{}` is not bound in all patterns
140+
VariableNotBoundInPattern(&'a BindingError),
141+
/// error E0409: variable `{}` is bound in inconsistent ways within the same match arm
142+
VariableBoundWithDifferentMode(Name, Span),
117143
/// error E0415: identifier is bound more than once in this parameter list
118144
IdentifierBoundMoreThanOnceInParameterList(&'a str),
119145
/// error E0416: identifier is bound more than once in the same pattern
@@ -207,27 +233,28 @@ fn resolve_struct_error<'sess, 'a>(resolver: &'sess Resolver,
207233
err.span_label(span, &format!("not a member of trait `{}`", trait_));
208234
err
209235
}
210-
ResolutionError::VariableNotBoundInPattern(variable_name, from, to) => {
211-
let mut err = struct_span_err!(resolver.session,
212-
span,
213-
E0408,
214-
"variable `{}` from pattern #{} is not bound in pattern #{}",
215-
variable_name,
216-
from,
217-
to);
218-
err.span_label(span, &format!("pattern doesn't bind `{}`", variable_name));
236+
ResolutionError::VariableNotBoundInPattern(binding_error) => {
237+
let target_sp = binding_error.target.iter().map(|x| *x).collect::<Vec<_>>();
238+
let msp = MultiSpan::from_spans(target_sp.clone());
239+
let msg = format!("variable `{}` is not bound in all patterns", binding_error.name);
240+
let mut err = resolver.session.struct_span_err_with_code(msp, &msg, "E0408");
241+
for sp in target_sp {
242+
err.span_label(sp, &format!("pattern doesn't bind `{}`", binding_error.name));
243+
}
244+
let origin_sp = binding_error.origin.iter().map(|x| *x).collect::<Vec<_>>();
245+
for sp in origin_sp {
246+
err.span_label(sp, &"variable not in all patterns");
247+
}
219248
err
220249
}
221250
ResolutionError::VariableBoundWithDifferentMode(variable_name,
222-
pattern_number,
223251
first_binding_span) => {
224252
let mut err = struct_span_err!(resolver.session,
225253
span,
226254
E0409,
227-
"variable `{}` is bound with different mode in pattern #{} than in \
228-
pattern #1",
229-
variable_name,
230-
pattern_number);
255+
"variable `{}` is bound in inconsistent \
256+
ways within the same match arm",
257+
variable_name);
231258
err.span_label(span, &format!("bound in different ways"));
232259
err.span_label(first_binding_span, &format!("first binding"));
233260
err
@@ -335,7 +362,7 @@ fn resolve_struct_error<'sess, 'a>(resolver: &'sess Resolver,
335362
}
336363
}
337364

338-
#[derive(Copy, Clone)]
365+
#[derive(Copy, Clone, Debug)]
339366
struct BindingInfo {
340367
span: Span,
341368
binding_mode: BindingMode,
@@ -1904,36 +1931,66 @@ impl<'a> Resolver<'a> {
19041931
if arm.pats.is_empty() {
19051932
return;
19061933
}
1907-
let map_0 = self.binding_mode_map(&arm.pats[0]);
1934+
1935+
let mut missing_vars = FxHashMap();
1936+
let mut inconsistent_vars = FxHashMap();
19081937
for (i, p) in arm.pats.iter().enumerate() {
19091938
let map_i = self.binding_mode_map(&p);
19101939

1911-
for (&key, &binding_0) in &map_0 {
1912-
match map_i.get(&key) {
1913-
None => {
1914-
let error = ResolutionError::VariableNotBoundInPattern(key.name, 1, i + 1);
1915-
resolve_error(self, p.span, error);
1940+
for (j, q) in arm.pats.iter().enumerate() {
1941+
if i == j {
1942+
continue;
1943+
}
1944+
1945+
let map_j = self.binding_mode_map(&q);
1946+
for (&key, &binding_i) in &map_i {
1947+
if map_j.len() == 0 { // Account for missing bindings when
1948+
let binding_error = missing_vars // map_j has none.
1949+
.entry(key.name)
1950+
.or_insert(BindingError {
1951+
name: key.name,
1952+
origin: BTreeSet::new(),
1953+
target: BTreeSet::new(),
1954+
});
1955+
binding_error.origin.insert(binding_i.span);
1956+
binding_error.target.insert(q.span);
19161957
}
1917-
Some(binding_i) => {
1918-
if binding_0.binding_mode != binding_i.binding_mode {
1919-
resolve_error(self,
1920-
binding_i.span,
1921-
ResolutionError::VariableBoundWithDifferentMode(
1922-
key.name,
1923-
i + 1,
1924-
binding_0.span));
1958+
for (&key_j, &binding_j) in &map_j {
1959+
match map_i.get(&key_j) {
1960+
None => { // missing binding
1961+
let binding_error = missing_vars
1962+
.entry(key_j.name)
1963+
.or_insert(BindingError {
1964+
name: key_j.name,
1965+
origin: BTreeSet::new(),
1966+
target: BTreeSet::new(),
1967+
});
1968+
binding_error.origin.insert(binding_j.span);
1969+
binding_error.target.insert(p.span);
1970+
}
1971+
Some(binding_i) => { // check consistent binding
1972+
if binding_i.binding_mode != binding_j.binding_mode {
1973+
inconsistent_vars
1974+
.entry(key.name)
1975+
.or_insert((binding_j.span, binding_i.span));
1976+
}
1977+
}
19251978
}
19261979
}
19271980
}
19281981
}
1929-
1930-
for (&key, &binding) in &map_i {
1931-
if !map_0.contains_key(&key) {
1932-
resolve_error(self,
1933-
binding.span,
1934-
ResolutionError::VariableNotBoundInPattern(key.name, i + 1, 1));
1935-
}
1936-
}
1982+
}
1983+
let mut missing_vars = missing_vars.iter().collect::<Vec<_>>();
1984+
missing_vars.sort();
1985+
for (_, v) in missing_vars {
1986+
resolve_error(self,
1987+
*v.origin.iter().next().unwrap(),
1988+
ResolutionError::VariableNotBoundInPattern(v));
1989+
}
1990+
let mut inconsistent_vars = inconsistent_vars.iter().collect::<Vec<_>>();
1991+
inconsistent_vars.sort();
1992+
for (name, v) in inconsistent_vars {
1993+
resolve_error(self, v.0, ResolutionError::VariableBoundWithDifferentMode(*name, v.1));
19371994
}
19381995
}
19391996

src/test/compile-fail/E0408.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@ fn main() {
1212
let x = Some(0);
1313

1414
match x {
15-
Some(y) | None => {} //~ ERROR variable `y` from pattern #1 is not bound in pattern #2
15+
Some(y) | None => {} //~ ERROR variable `y` is not bound in all patterns
1616
_ => () //~| NOTE pattern doesn't bind `y`
17+
//~| NOTE variable not in all patterns
1718
}
1819
}

src/test/compile-fail/issue-2848.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,8 @@ mod bar {
1919
fn main() {
2020
use bar::foo::{alpha, charlie};
2121
match alpha {
22-
alpha | beta => {} //~ ERROR variable `beta` from pattern #2 is not bound in pattern #1
22+
alpha | beta => {} //~ ERROR variable `beta` is not bound in all patterns
2323
charlie => {} //~| NOTE pattern doesn't bind `beta`
24+
//~| NOTE variable not in all patterns
2425
}
2526
}

src/test/compile-fail/issue-2849.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,6 @@ enum foo { alpha, beta(isize) }
1313
fn main() {
1414
match foo::alpha {
1515
foo::alpha | foo::beta(i) => {}
16-
//~^ ERROR variable `i` from pattern #2 is not bound in pattern #1
16+
//~^ ERROR variable `i` is not bound in all patterns
1717
}
1818
}

src/test/compile-fail/resolve-inconsistent-binding-mode.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ enum opts {
1515
fn matcher1(x: opts) {
1616
match x {
1717
opts::a(ref i) | opts::b(i) => {}
18-
//~^ ERROR variable `i` is bound with different mode in pattern #2 than in pattern #1
18+
//~^ ERROR variable `i` is bound in inconsistent ways within the same match arm
1919
//~^^ ERROR mismatched types
2020
opts::c(_) => {}
2121
}
@@ -24,7 +24,7 @@ fn matcher1(x: opts) {
2424
fn matcher2(x: opts) {
2525
match x {
2626
opts::a(ref i) | opts::b(i) => {}
27-
//~^ ERROR variable `i` is bound with different mode in pattern #2 than in pattern #1
27+
//~^ ERROR variable `i` is bound in inconsistent ways within the same match arm
2828
//~^^ ERROR mismatched types
2929
opts::c(_) => {}
3030
}
@@ -33,7 +33,7 @@ fn matcher2(x: opts) {
3333
fn matcher4(x: opts) {
3434
match x {
3535
opts::a(ref mut i) | opts::b(ref i) => {}
36-
//~^ ERROR variable `i` is bound with different mode in pattern #2 than in pattern #1
36+
//~^ ERROR variable `i` is bound in inconsistent ways within the same match arm
3737
//~^^ ERROR mismatched types
3838
opts::c(_) => {}
3939
}

src/test/compile-fail/resolve-inconsistent-names.rs

+4-2
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,11 @@
1111
fn main() {
1212
let y = 1;
1313
match y {
14-
a | b => {} //~ ERROR variable `a` from pattern #1 is not bound in pattern #2
15-
//~^ ERROR variable `b` from pattern #2 is not bound in pattern #1
14+
a | b => {} //~ ERROR variable `a` is not bound in all patterns
15+
//~^ ERROR variable `b` is not bound in all patterns
1616
//~| NOTE pattern doesn't bind `a`
1717
//~| NOTE pattern doesn't bind `b`
18+
//~| NOTE variable not in all patterns
19+
//~| NOTE variable not in all patterns
1820
}
1921
}

src/test/ui/mismatched_types/E0409.stderr

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
error[E0409]: variable `y` is bound with different mode in pattern #2 than in pattern #1
1+
error[E0409]: variable `y` is bound in inconsistent ways within the same match arm
22
--> $DIR/E0409.rs:15:23
33
|
44
15 | (0, ref y) | (y, 0) => {} //~ ERROR E0409

src/test/ui/span/issue-39698.rs

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
enum T {
12+
T1(i32, i32),
13+
T2(i32, i32),
14+
T3(i32),
15+
T4(i32),
16+
}
17+
18+
fn main() {
19+
match T::T1(123, 456) {
20+
T::T1(a, d) | T::T2(d, b) | T::T3(c) | T::T4(a) => { println!("{:?}", a); }
21+
}
22+
}

src/test/ui/span/issue-39698.stderr

+42
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
error[E0408]: variable `a` is not bound in all patterns
2+
--> $DIR/issue-39698.rs:20:23
3+
|
4+
20 | T::T1(a, d) | T::T2(d, b) | T::T3(c) | T::T4(a) => { println!("{:?}", a); }
5+
| - ^^^^^^^^^^^ ^^^^^^^^ - variable not in all patterns
6+
| | | |
7+
| | | pattern doesn't bind `a`
8+
| | pattern doesn't bind `a`
9+
| variable not in all patterns
10+
11+
error[E0408]: variable `d` is not bound in all patterns
12+
--> $DIR/issue-39698.rs:20:37
13+
|
14+
20 | T::T1(a, d) | T::T2(d, b) | T::T3(c) | T::T4(a) => { println!("{:?}", a); }
15+
| - - ^^^^^^^^ ^^^^^^^^ pattern doesn't bind `d`
16+
| | | |
17+
| | | pattern doesn't bind `d`
18+
| | variable not in all patterns
19+
| variable not in all patterns
20+
21+
error[E0408]: variable `b` is not bound in all patterns
22+
--> $DIR/issue-39698.rs:20:9
23+
|
24+
20 | T::T1(a, d) | T::T2(d, b) | T::T3(c) | T::T4(a) => { println!("{:?}", a); }
25+
| ^^^^^^^^^^^ - ^^^^^^^^ ^^^^^^^^ pattern doesn't bind `b`
26+
| | | |
27+
| | | pattern doesn't bind `b`
28+
| | variable not in all patterns
29+
| pattern doesn't bind `b`
30+
31+
error[E0408]: variable `c` is not bound in all patterns
32+
--> $DIR/issue-39698.rs:20:9
33+
|
34+
20 | T::T1(a, d) | T::T2(d, b) | T::T3(c) | T::T4(a) => { println!("{:?}", a); }
35+
| ^^^^^^^^^^^ ^^^^^^^^^^^ - ^^^^^^^^ pattern doesn't bind `c`
36+
| | | |
37+
| | | variable not in all patterns
38+
| | pattern doesn't bind `c`
39+
| pattern doesn't bind `c`
40+
41+
error: aborting due to 4 previous errors
42+

0 commit comments

Comments
 (0)