]> git.lizzy.rs Git - rust.git/commitdiff
Clean up "pattern doesn't bind x" messages
authorEsteban Küber <esteban@kuber.com.ar>
Fri, 10 Feb 2017 01:54:56 +0000 (17:54 -0800)
committerEsteban Küber <esteban@kuber.com.ar>
Mon, 6 Mar 2017 03:20:26 +0000 (00:20 -0300)
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

src/librustc_resolve/lib.rs
src/test/compile-fail/E0408.rs
src/test/compile-fail/issue-2848.rs
src/test/compile-fail/issue-2849.rs
src/test/compile-fail/resolve-inconsistent-binding-mode.rs
src/test/compile-fail/resolve-inconsistent-names.rs
src/test/ui/mismatched_types/E0409.stderr
src/test/ui/span/issue-39698.rs [new file with mode: 0644]
src/test/ui/span/issue-39698.stderr [new file with mode: 0644]

index 093994ba8257d28cd7fa488994f7f7c9b0da5b11..adb0eee652bd0888e995e0f4184f4cb560f45c7b 100644 (file)
@@ -97,6 +97,31 @@ enum AssocSuggestion {
     AssocItem,
 }
 
+#[derive(Eq)]
+struct BindingError {
+    name: Name,
+    origin: FxHashSet<Span>,
+    target: FxHashSet<Span>,
+}
+
+impl PartialOrd for BindingError {
+    fn partial_cmp(&self, other: &BindingError) -> Option<cmp::Ordering> {
+        Some(self.cmp(other))
+    }
+}
+
+impl PartialEq for BindingError {
+    fn eq(&self, other: &BindingError) -> bool {
+        self.name == other.name
+    }
+}
+
+impl Ord for BindingError {
+    fn cmp(&self, other: &BindingError) -> cmp::Ordering {
+        self.name.cmp(&other.name)
+    }
+}
+
 enum ResolutionError<'a> {
     /// error E0401: can't use type parameters from outer function
     TypeParametersFromOuterFunction,
@@ -110,10 +135,10 @@ enum ResolutionError<'a> {
     TypeNotMemberOfTrait(Name, &'a str),
     /// error E0438: const is not a member of trait
     ConstNotMemberOfTrait(Name, &'a str),
-    /// error E0408: variable `{}` from pattern #{} is not bound in pattern #{}
-    VariableNotBoundInPattern(Name, usize, usize),
-    /// error E0409: variable is bound with different mode in pattern #{} than in pattern #1
-    VariableBoundWithDifferentMode(Name, usize, Span),
+    /// error E0408: variable `{}` is not bound in all patterns
+    VariableNotBoundInPattern(&'a BindingError),
+    /// error E0409: variable `{}` is bound in inconsistent ways within the same match arm
+    VariableBoundWithDifferentMode(Name, Span),
     /// error E0415: identifier is bound more than once in this parameter list
     IdentifierBoundMoreThanOnceInParameterList(&'a str),
     /// 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,
             err.span_label(span, &format!("not a member of trait `{}`", trait_));
             err
         }
-        ResolutionError::VariableNotBoundInPattern(variable_name, from, to) => {
-            let mut err = struct_span_err!(resolver.session,
-                             span,
-                             E0408,
-                             "variable `{}` from pattern #{} is not bound in pattern #{}",
-                             variable_name,
-                             from,
-                             to);
-            err.span_label(span, &format!("pattern doesn't bind `{}`", variable_name));
+        ResolutionError::VariableNotBoundInPattern(binding_error) => {
+            let mut target_sp = binding_error.target.iter().map(|x| *x).collect::<Vec<_>>();
+            target_sp.sort();
+            let msp = MultiSpan::from_spans(target_sp.clone());
+            let msg = format!("variable `{}` is not bound in all patterns", binding_error.name);
+            let mut err = resolver.session.struct_span_err_with_code(msp, &msg, "E0408");
+            for sp in target_sp {
+                err.span_label(sp, &format!("pattern doesn't bind `{}`", binding_error.name));
+            }
+            let mut origin_sp = binding_error.origin.iter().map(|x| *x).collect::<Vec<_>>();
+            origin_sp.sort();
+            for sp in origin_sp {
+                err.span_label(sp, &"variable not in all patterns");
+            }
             err
         }
         ResolutionError::VariableBoundWithDifferentMode(variable_name,
-                                                        pattern_number,
                                                         first_binding_span) => {
             let mut err = struct_span_err!(resolver.session,
                              span,
                              E0409,
-                             "variable `{}` is bound with different mode in pattern #{} than in \
-                              pattern #1",
-                             variable_name,
-                             pattern_number);
+                             "variable `{}` is bound in inconsistent \
+                             ways within the same match arm",
+                             variable_name);
             err.span_label(span, &format!("bound in different ways"));
             err.span_label(first_binding_span, &format!("first binding"));
             err
@@ -335,7 +363,7 @@ fn resolve_struct_error<'sess, 'a>(resolver: &'sess Resolver,
     }
 }
 
-#[derive(Copy, Clone)]
+#[derive(Copy, Clone, Debug)]
 struct BindingInfo {
     span: Span,
     binding_mode: BindingMode,
@@ -1904,36 +1932,66 @@ fn check_consistent_bindings(&mut self, arm: &Arm) {
         if arm.pats.is_empty() {
             return;
         }
-        let map_0 = self.binding_mode_map(&arm.pats[0]);
+
+        let mut missing_vars = FxHashMap();
+        let mut inconsistent_vars = FxHashMap();
         for (i, p) in arm.pats.iter().enumerate() {
             let map_i = self.binding_mode_map(&p);
 
-            for (&key, &binding_0) in &map_0 {
-                match map_i.get(&key) {
-                    None => {
-                        let error = ResolutionError::VariableNotBoundInPattern(key.name, 1, i + 1);
-                        resolve_error(self, p.span, error);
+            for (j, q) in arm.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.len() == 0 {                   // Account for missing bindings when
+                        let binding_error = missing_vars    // map_j has none.
+                            .entry(key.name)
+                            .or_insert(BindingError {
+                                name: key.name,
+                                origin: FxHashSet(),
+                                target: FxHashSet(),
+                            });
+                        binding_error.origin.insert(binding_i.span);
+                        binding_error.target.insert(q.span);
                     }
-                    Some(binding_i) => {
-                        if binding_0.binding_mode != binding_i.binding_mode {
-                            resolve_error(self,
-                                          binding_i.span,
-                                          ResolutionError::VariableBoundWithDifferentMode(
-                                              key.name,
-                                              i + 1,
-                                              binding_0.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: FxHashSet(),
+                                        target: FxHashSet(),
+                                    });
+                                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 (&key, &binding) in &map_i {
-                if !map_0.contains_key(&key) {
-                    resolve_error(self,
-                                  binding.span,
-                                  ResolutionError::VariableNotBoundInPattern(key.name, i + 1, 1));
-                }
-            }
+        }
+        let mut missing_vars = missing_vars.iter().collect::<Vec<_>>();
+        missing_vars.sort();
+        for (_, v) in missing_vars {
+            resolve_error(self,
+                          *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 {
+            resolve_error(self, v.0, ResolutionError::VariableBoundWithDifferentMode(*name, v.1));
         }
     }
 
index d75f6124827726d79cba494eec55509332645490..ce77a537e263ddd220e52ec75b8b74a38a88f99e 100644 (file)
@@ -12,7 +12,8 @@ fn main() {
     let x = Some(0);
 
     match x {
-        Some(y) | None => {} //~  ERROR variable `y` from pattern #1 is not bound in pattern #2
+        Some(y) | None => {} //~  ERROR variable `y` is not bound in all patterns
         _ => ()              //~| NOTE pattern doesn't bind `y`
+                             //~| NOTE variable not in all patterns
     }
 }
index f5e0c545bb52436fa4714d87aa3e59511d7f424c..38bd7adefd91f758c024dbfa41f12ab9c48034a5 100644 (file)
@@ -19,7 +19,8 @@ pub enum foo {
 fn main() {
     use bar::foo::{alpha, charlie};
     match alpha {
-      alpha | beta => {} //~  ERROR variable `beta` from pattern #2 is not bound in pattern #1
+      alpha | beta => {} //~  ERROR variable `beta` is not bound in all patterns
       charlie => {}      //~| NOTE pattern doesn't bind `beta`
+                         //~| NOTE variable not in all patterns
     }
 }
index 48f4cac9711a8d31ebed0ba50a6a646358722e2d..203b28bd5e417afedf09433a232b95291e48ecc7 100644 (file)
@@ -13,6 +13,6 @@ enum foo { alpha, beta(isize) }
 fn main() {
     match foo::alpha {
       foo::alpha | foo::beta(i) => {}
-      //~^ ERROR variable `i` from pattern #2 is not bound in pattern #1
+      //~^ ERROR variable `i` is not bound in all patterns
     }
 }
index 284c08ef09b28fa4c475ab575af71d706fce72db..63d33a9e5fa6f9a6125a5736293b6a4add8240c9 100644 (file)
@@ -15,7 +15,7 @@ enum opts {
 fn matcher1(x: opts) {
     match x {
       opts::a(ref i) | opts::b(i) => {}
-      //~^ ERROR variable `i` is bound with different mode in pattern #2 than in pattern #1
+      //~^ ERROR variable `i` is bound in inconsistent ways within the same match arm
       //~^^ ERROR mismatched types
       opts::c(_) => {}
     }
@@ -24,7 +24,7 @@ fn matcher1(x: opts) {
 fn matcher2(x: opts) {
     match x {
       opts::a(ref i) | opts::b(i) => {}
-      //~^ ERROR variable `i` is bound with different mode in pattern #2 than in pattern #1
+      //~^ ERROR variable `i` is bound in inconsistent ways within the same match arm
       //~^^ ERROR mismatched types
       opts::c(_) => {}
     }
@@ -33,7 +33,7 @@ fn matcher2(x: opts) {
 fn matcher4(x: opts) {
     match x {
       opts::a(ref mut i) | opts::b(ref i) => {}
-      //~^ ERROR variable `i` is bound with different mode in pattern #2 than in pattern #1
+      //~^ ERROR variable `i` is bound in inconsistent ways within the same match arm
       //~^^ ERROR mismatched types
       opts::c(_) => {}
     }
index 1e2541502ace874c04d28188a6557850d45361c9..7fee5aedb06ed33eb1b159b8cfa11c8eab963c79 100644 (file)
 fn main() {
     let y = 1;
     match y {
-       a | b => {} //~  ERROR variable `a` from pattern #1 is not bound in pattern #2
-                   //~^ ERROR variable `b` from pattern #2 is not bound in pattern #1
+       a | b => {} //~  ERROR variable `a` is not bound in all patterns
+                   //~^ ERROR variable `b` is not bound in all patterns
                    //~| NOTE pattern doesn't bind `a`
                    //~| NOTE pattern doesn't bind `b`
+                   //~| NOTE variable not in all patterns
+                   //~| NOTE variable not in all patterns
     }
 }
index 251e247fa28ba623b76aeb0a4162e28ad74567c4..45a42b1c271f8678920ca841580d814aa62f6498 100644 (file)
@@ -1,4 +1,4 @@
-error[E0409]: variable `y` is bound with different mode in pattern #2 than in pattern #1
+error[E0409]: variable `y` is bound in inconsistent ways within the same match arm
   --> $DIR/E0409.rs:15:23
    |
 15 |         (0, ref y) | (y, 0) => {} //~ ERROR E0409
diff --git a/src/test/ui/span/issue-39698.rs b/src/test/ui/span/issue-39698.rs
new file mode 100644 (file)
index 0000000..17b3f1c
--- /dev/null
@@ -0,0 +1,22 @@
+// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+enum T {
+    T1(i32, i32),
+    T2(i32, i32),
+    T3(i32),
+    T4(i32),
+}
+
+fn main() {
+    match T::T1(123, 456) {
+        T::T1(a, d) | T::T2(d, b) | T::T3(c) | T::T4(a) => { println!("{:?}", a); }
+    }
+}
diff --git a/src/test/ui/span/issue-39698.stderr b/src/test/ui/span/issue-39698.stderr
new file mode 100644 (file)
index 0000000..97d802f
--- /dev/null
@@ -0,0 +1,42 @@
+error[E0408]: variable `a` is not bound in all patterns
+  --> $DIR/issue-39698.rs:20:23
+   |
+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 `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 `b` is not bound in all patterns
+  --> $DIR/issue-39698.rs:20:9
+   |
+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[E0408]: variable `c` is not bound in all patterns
+  --> $DIR/issue-39698.rs:20:9
+   |
+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: aborting due to 4 previous errors
+