Skip to content

Commit b0d0272

Browse files
committed
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: ``` 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: ``` error[E0408]: variable `a` is not bound in all patterns --> file.rs:20:37 | 20 | T::T1(a, d) | T::T2(d, b) | T::T3(c) | T::T4(a) => { intln!("{:?}", a); } | - ^^^^^^^^^^^ ^^^^^^^^ - variable t in all patterns | | | | | | | pattern doesn't bind `a` | | pattern doesn't bind `a` | variable not in all patterns error[E0408]: variable `d` is not bound in all patterns --> file.rs:20:37 | 20 | T::T1(a, d) | T::T2(d, b) | T::T3(c) | T::T4(a) => { intln!("{:?}", a); } | - - ^^^^^^^^ ^^^^^^^^ pattern esn't bind `d` | | | | | | | pattern doesn't bind `d` | | variable not in all patterns | variable not in all patterns error[E0408]: variable `b` is not bound in all patterns --> file.rs:20:37 | 20 | T::T1(a, d) | T::T2(d, b) | T::T3(c) | T::T4(a) => { intln!("{:?}", a); } | ^^^^^^^^^^^ - ^^^^^^^^ ^^^^^^^^ pattern esn't bind `b` | | | | | | | pattern doesn't bind `b` | | variable not in all patterns | pattern doesn't bind `b` error[E0408]: variable `c` is not bound in all patterns --> file.rs:20:48 | 20 | T::T1(a, d) | T::T2(d, b) | T::T3(c) | T::T4(a) => { intln!("{:?}", a); } | ^^^^^^^^^^^ ^^^^^^^^^^^ - ^^^^^^^^ pattern esn't bind `c` | | | | | | | variable not in all tterns | | pattern doesn't bind `c` | pattern doesn't bind `c` error: aborting due to 4 previous errors ``` * Have only one presentation for binding consistency errors * Point to same binding in multiple patterns when possible * Check inconsistent bindings in all arms * Simplify wording of diagnostic message * Sort emition and spans of binding errors for deterministic output
1 parent b1e3176 commit b0d0272

File tree

9 files changed

+176
-50
lines changed

9 files changed

+176
-50
lines changed

src/librustc_resolve/lib.rs

+99-41
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,31 @@ enum AssocSuggestion {
9797
AssocItem,
9898
}
9999

100+
#[derive(Eq)]
101+
struct BindingError {
102+
name: Name,
103+
origin: FxHashSet<Span>,
104+
target: FxHashSet<Span>,
105+
}
106+
107+
impl PartialOrd for BindingError {
108+
fn partial_cmp(&self, other: &BindingError) -> Option<cmp::Ordering> {
109+
Some(self.cmp(other))
110+
}
111+
}
112+
113+
impl PartialEq for BindingError {
114+
fn eq(&self, other: &BindingError) -> bool {
115+
self.name == other.name
116+
}
117+
}
118+
119+
impl Ord for BindingError {
120+
fn cmp(&self, other: &BindingError) -> cmp::Ordering {
121+
self.name.cmp(&other.name)
122+
}
123+
}
124+
100125
enum ResolutionError<'a> {
101126
/// error E0401: can't use type parameters from outer function
102127
TypeParametersFromOuterFunction,
@@ -110,10 +135,10 @@ enum ResolutionError<'a> {
110135
TypeNotMemberOfTrait(Name, &'a str),
111136
/// error E0438: const is not a member of trait
112137
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),
138+
/// error E0408: variable `{}` is not bound in all patterns
139+
VariableNotBoundInPattern(&'a BindingError),
140+
/// error E0409: variable `{}` is bound in inconsistent ways within the same match arm
141+
VariableBoundWithDifferentMode(Name, Span),
117142
/// error E0415: identifier is bound more than once in this parameter list
118143
IdentifierBoundMoreThanOnceInParameterList(&'a str),
119144
/// error E0416: identifier is bound more than once in the same pattern
@@ -207,27 +232,30 @@ fn resolve_struct_error<'sess, 'a>(resolver: &'sess Resolver,
207232
err.span_label(span, &format!("not a member of trait `{}`", trait_));
208233
err
209234
}
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));
235+
ResolutionError::VariableNotBoundInPattern(binding_error) => {
236+
let mut target_sp = binding_error.target.iter().map(|x| *x).collect::<Vec<_>>();
237+
target_sp.sort();
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 mut origin_sp = binding_error.origin.iter().map(|x| *x).collect::<Vec<_>>();
245+
origin_sp.sort();
246+
for sp in origin_sp {
247+
err.span_label(sp, &"variable not in all patterns");
248+
}
219249
err
220250
}
221251
ResolutionError::VariableBoundWithDifferentMode(variable_name,
222-
pattern_number,
223252
first_binding_span) => {
224253
let mut err = struct_span_err!(resolver.session,
225254
span,
226255
E0409,
227-
"variable `{}` is bound with different mode in pattern #{} than in \
228-
pattern #1",
229-
variable_name,
230-
pattern_number);
256+
"variable `{}` is bound in inconsistent \
257+
ways within the same match arm",
258+
variable_name);
231259
err.span_label(span, &format!("bound in different ways"));
232260
err.span_label(first_binding_span, &format!("first binding"));
233261
err
@@ -335,7 +363,7 @@ fn resolve_struct_error<'sess, 'a>(resolver: &'sess Resolver,
335363
}
336364
}
337365

338-
#[derive(Copy, Clone)]
366+
#[derive(Copy, Clone, Debug)]
339367
struct BindingInfo {
340368
span: Span,
341369
binding_mode: BindingMode,
@@ -1904,36 +1932,66 @@ impl<'a> Resolver<'a> {
19041932
if arm.pats.is_empty() {
19051933
return;
19061934
}
1907-
let map_0 = self.binding_mode_map(&arm.pats[0]);
1935+
1936+
let mut missing_vars = FxHashMap();
1937+
let mut inconsistent_vars = FxHashMap();
19081938
for (i, p) in arm.pats.iter().enumerate() {
19091939
let map_i = self.binding_mode_map(&p);
19101940

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);
1941+
for (j, q) in arm.pats.iter().enumerate() {
1942+
if i == j {
1943+
continue;
1944+
}
1945+
1946+
let map_j = self.binding_mode_map(&q);
1947+
for (&key, &binding_i) in &map_i {
1948+
if map_j.len() == 0 { // Account for missing bindings when
1949+
let binding_error = missing_vars // map_j has none.
1950+
.entry(key.name)
1951+
.or_insert(BindingError {
1952+
name: key.name,
1953+
origin: FxHashSet(),
1954+
target: FxHashSet(),
1955+
});
1956+
binding_error.origin.insert(binding_i.span);
1957+
binding_error.target.insert(q.span);
19161958
}
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));
1959+
for (&key_j, &binding_j) in &map_j {
1960+
match map_i.get(&key_j) {
1961+
None => { // missing binding
1962+
let binding_error = missing_vars
1963+
.entry(key_j.name)
1964+
.or_insert(BindingError {
1965+
name: key_j.name,
1966+
origin: FxHashSet(),
1967+
target: FxHashSet(),
1968+
});
1969+
binding_error.origin.insert(binding_j.span);
1970+
binding_error.target.insert(p.span);
1971+
}
1972+
Some(binding_i) => { // check consistent binding
1973+
if binding_i.binding_mode != binding_j.binding_mode {
1974+
inconsistent_vars
1975+
.entry(key.name)
1976+
.or_insert((binding_j.span, binding_i.span));
1977+
}
1978+
}
19251979
}
19261980
}
19271981
}
19281982
}
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-
}
1983+
}
1984+
let mut missing_vars = missing_vars.iter().collect::<Vec<_>>();
1985+
missing_vars.sort();
1986+
for (_, v) in missing_vars {
1987+
resolve_error(self,
1988+
*v.origin.iter().next().unwrap(),
1989+
ResolutionError::VariableNotBoundInPattern(v));
1990+
}
1991+
let mut inconsistent_vars = inconsistent_vars.iter().collect::<Vec<_>>();
1992+
inconsistent_vars.sort();
1993+
for (name, v) in inconsistent_vars {
1994+
resolve_error(self, v.0, ResolutionError::VariableBoundWithDifferentMode(*name, v.1));
19371995
}
19381996
}
19391997

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)