about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2022-04-30 02:41:34 +0000
committerbors <bors@rust-lang.org>2022-04-30 02:41:34 +0000
commit0c8e520b3e0d9fbdafb8311f1b18a1f847531051 (patch)
tree4a8fb599f2fefd684dd4925a6fc73416f5cc9688
parent05c07386b45fbc540ec8cdc1bc41ae9c062b453b (diff)
parentbce5ab2c7864aa553aa70115336271586daba21c (diff)
downloadrust-0c8e520b3e0d9fbdafb8311f1b18a1f847531051.tar.gz
rust-0c8e520b3e0d9fbdafb8311f1b18a1f847531051.zip
Auto merge of #90988 - estebank:binding-supposed-to-be-const, r=davidtwco
When encountering a binding that could be a const or unit variant, suggest the right path
-rw-r--r--compiler/rustc_resolve/src/diagnostics.rs134
-rw-r--r--compiler/rustc_resolve/src/late.rs25
-rw-r--r--compiler/rustc_resolve/src/lib.rs8
-rw-r--r--compiler/rustc_typeck/src/check/pat.rs2
-rw-r--r--src/test/ui/or-patterns/missing-bindings.stderr32
-rw-r--r--src/test/ui/resolve/resolve-inconsistent-names.rs9
-rw-r--r--src/test/ui/resolve/resolve-inconsistent-names.stderr22
-rw-r--r--src/test/ui/span/issue-39698.stderr20
8 files changed, 164 insertions, 88 deletions
diff --git a/compiler/rustc_resolve/src/diagnostics.rs b/compiler/rustc_resolve/src/diagnostics.rs
index 9bbecf104e5..e0a83ba8c0d 100644
--- a/compiler/rustc_resolve/src/diagnostics.rs
+++ b/compiler/rustc_resolve/src/diagnostics.rs
@@ -123,7 +123,7 @@ impl<'a> Resolver<'a> {
             let (span, found_use) = if let Some(def_id) = def_id.as_local() {
                 UsePlacementFinder::check(krate, self.def_id_to_node_id[def_id])
             } else {
-                (None, false)
+                (None, FoundUse::No)
             };
             if !candidates.is_empty() {
                 show_candidates(
@@ -132,8 +132,9 @@ impl<'a> Resolver<'a> {
                     &mut err,
                     span,
                     &candidates,
-                    instead,
+                    if instead { Instead::Yes } else { Instead::No },
                     found_use,
+                    IsPattern::No,
                 );
             } else if let Some((span, msg, sugg, appl)) = suggestion {
                 err.span_suggestion(span, msg, sugg, appl);
@@ -493,14 +494,14 @@ impl<'a> Resolver<'a> {
     ///
     /// This takes the error provided, combines it with the span and any additional spans inside the
     /// error and emits it.
-    crate fn report_error(&self, span: Span, resolution_error: ResolutionError<'_>) {
+    crate fn report_error(&mut self, span: Span, resolution_error: ResolutionError<'a>) {
         self.into_struct_error(span, resolution_error).emit();
     }
 
     crate fn into_struct_error(
-        &self,
+        &mut self,
         span: Span,
-        resolution_error: ResolutionError<'_>,
+        resolution_error: ResolutionError<'a>,
     ) -> DiagnosticBuilder<'_, ErrorGuaranteed> {
         match resolution_error {
             ResolutionError::GenericParamsFromOuterFunction(outer_res, has_generic_params) => {
@@ -650,7 +651,7 @@ impl<'a> Resolver<'a> {
                 }
                 err
             }
-            ResolutionError::VariableNotBoundInPattern(binding_error) => {
+            ResolutionError::VariableNotBoundInPattern(binding_error, parent_scope) => {
                 let BindingError { name, target, origin, could_be_path } = binding_error;
 
                 let target_sp = target.iter().copied().collect::<Vec<_>>();
@@ -670,13 +671,41 @@ impl<'a> Resolver<'a> {
                 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,
+                if could_be_path {
+                    let import_suggestions = self.lookup_import_candidates(
+                        Ident::with_dummy_span(name),
+                        Namespace::ValueNS,
+                        &parent_scope,
+                        &|res: Res| match res {
+                            Res::Def(
+                                DefKind::Ctor(CtorOf::Variant, CtorKind::Const)
+                                | DefKind::Ctor(CtorOf::Struct, CtorKind::Const)
+                                | DefKind::Const
+                                | DefKind::AssocConst,
+                                _,
+                            ) => true,
+                            _ => false,
+                        },
+                    );
+
+                    if import_suggestions.is_empty() {
+                        let help_msg = format!(
+                            "if you meant to match on a variant or a `const` item, consider \
+                             making the path in the pattern qualified: `path::to::ModOrType::{}`",
+                            name,
+                        );
+                        err.span_help(span, &help_msg);
+                    }
+                    show_candidates(
+                        &self.definitions,
+                        self.session,
+                        &mut err,
+                        Some(span),
+                        &import_suggestions,
+                        Instead::No,
+                        FoundUse::Yes,
+                        IsPattern::Yes,
                     );
-                    err.span_help(span, &help_msg);
                 }
                 err
             }
@@ -1022,7 +1051,7 @@ impl<'a> Resolver<'a> {
     }
 
     crate fn report_vis_error(
-        &self,
+        &mut self,
         vis_resolution_error: VisResolutionError<'_>,
     ) -> ErrorGuaranteed {
         match vis_resolution_error {
@@ -1453,8 +1482,9 @@ impl<'a> Resolver<'a> {
             err,
             None,
             &import_suggestions,
-            false,
-            true,
+            Instead::No,
+            FoundUse::Yes,
+            IsPattern::No,
         );
 
         if macro_kind == MacroKind::Derive && (ident.name == sym::Send || ident.name == sym::Sync) {
@@ -2390,6 +2420,27 @@ fn find_span_immediately_after_crate_name(
     (next_left_bracket == after_second_colon, from_second_colon)
 }
 
+/// A suggestion has already been emitted, change the wording slightly to clarify that both are
+/// independent options.
+enum Instead {
+    Yes,
+    No,
+}
+
+/// Whether an existing place with an `use` item was found.
+enum FoundUse {
+    Yes,
+    No,
+}
+
+/// Whether a binding is part of a pattern or an expression. Used for diagnostics.
+enum IsPattern {
+    /// The binding is part of a pattern
+    Yes,
+    /// The binding is part of an expression
+    No,
+}
+
 /// When an entity with a given name is not available in scope, we search for
 /// entities with that name in all crates. This method allows outputting the
 /// results of this search in a programmer-friendly way
@@ -2400,8 +2451,9 @@ fn show_candidates(
     // This is `None` if all placement locations are inside expansions
     use_placement_span: Option<Span>,
     candidates: &[ImportSuggestion],
-    instead: bool,
-    found_use: bool,
+    instead: Instead,
+    found_use: FoundUse,
+    is_pattern: IsPattern,
 ) {
     if candidates.is_empty() {
         return;
@@ -2428,24 +2480,38 @@ fn show_candidates(
     }
 
     if !accessible_path_strings.is_empty() {
-        let (determiner, kind) = if accessible_path_strings.len() == 1 {
-            ("this", accessible_path_strings[0].1)
+        let (determiner, kind, name) = if accessible_path_strings.len() == 1 {
+            ("this", accessible_path_strings[0].1, format!(" `{}`", accessible_path_strings[0].0))
         } else {
-            ("one of these", "items")
+            ("one of these", "items", String::new())
         };
 
-        let instead = if instead { " instead" } else { "" };
-        let mut msg = format!("consider importing {} {}{}", determiner, kind, instead);
+        let instead = if let Instead::Yes = instead { " instead" } else { "" };
+        let mut msg = if let IsPattern::Yes = is_pattern {
+            format!(
+                "if you meant to match on {}{}{}, use the full path in the pattern",
+                kind, instead, name
+            )
+        } else {
+            format!("consider importing {} {}{}", determiner, kind, instead)
+        };
 
         for note in accessible_path_strings.iter().flat_map(|cand| cand.3.as_ref()) {
             err.note(note);
         }
 
-        if let Some(span) = use_placement_span {
+        if let (IsPattern::Yes, Some(span)) = (is_pattern, use_placement_span) {
+            err.span_suggestions(
+                span,
+                &msg,
+                accessible_path_strings.into_iter().map(|a| a.0),
+                Applicability::MaybeIncorrect,
+            );
+        } else if let Some(span) = use_placement_span {
             for candidate in &mut accessible_path_strings {
                 // produce an additional newline to separate the new use statement
                 // from the directly following item.
-                let additional_newline = if found_use { "" } else { "\n" };
+                let additional_newline = if let FoundUse::Yes = found_use { "" } else { "\n" };
                 candidate.0 = format!("use {};\n{}", &candidate.0, additional_newline);
             }
 
@@ -2453,7 +2519,7 @@ fn show_candidates(
                 span,
                 &msg,
                 accessible_path_strings.into_iter().map(|a| a.0),
-                Applicability::Unspecified,
+                Applicability::MaybeIncorrect,
             );
         } else {
             msg.push(':');
@@ -2468,9 +2534,17 @@ fn show_candidates(
     } else {
         assert!(!inaccessible_path_strings.is_empty());
 
+        let prefix =
+            if let IsPattern::Yes = is_pattern { "you might have meant to match on " } else { "" };
         if inaccessible_path_strings.len() == 1 {
             let (name, descr, def_id, note) = &inaccessible_path_strings[0];
-            let msg = format!("{} `{}` exists but is inaccessible", descr, name);
+            let msg = format!(
+                "{}{} `{}`{} exists but is inaccessible",
+                prefix,
+                descr,
+                name,
+                if let IsPattern::Yes = is_pattern { ", which" } else { "" }
+            );
 
             if let Some(local_def_id) = def_id.and_then(|did| did.as_local()) {
                 let span = definitions.def_span(local_def_id);
@@ -2496,7 +2570,7 @@ fn show_candidates(
                 "item".to_string()
             };
 
-            let mut msg = format!("these {}s exist but are inaccessible", descr);
+            let mut msg = format!("{}these {}s exist but are inaccessible", prefix, descr);
             let mut has_colon = false;
 
             let mut spans = Vec::new();
@@ -2537,14 +2611,14 @@ struct UsePlacementFinder {
 }
 
 impl UsePlacementFinder {
-    fn check(krate: &Crate, target_module: NodeId) -> (Option<Span>, bool) {
+    fn check(krate: &Crate, target_module: NodeId) -> (Option<Span>, FoundUse) {
         let mut finder =
             UsePlacementFinder { target_module, first_legal_span: None, first_use_span: None };
         finder.visit_crate(krate);
         if let Some(use_span) = finder.first_use_span {
-            (Some(use_span), true)
+            (Some(use_span), FoundUse::Yes)
         } else {
-            (finder.first_legal_span, false)
+            (finder.first_legal_span, FoundUse::No)
         }
     }
 }
diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs
index 21b888c25c2..aafef3e3137 100644
--- a/compiler/rustc_resolve/src/late.rs
+++ b/compiler/rustc_resolve/src/late.rs
@@ -1422,7 +1422,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
 
     /// Searches the current set of local scopes for labels. Returns the `NodeId` of the resolved
     /// label and reports an error if the label is not found or is unreachable.
-    fn resolve_label(&self, mut label: Ident) -> Option<NodeId> {
+    fn resolve_label(&mut self, mut label: Ident) -> Option<NodeId> {
         let mut suggestion = None;
 
         // Preserve the original span so that errors contain "in this macro invocation"
@@ -1442,6 +1442,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
 
             let ident = label.normalize_to_macro_rules();
             if let Some((ident, id)) = rib.bindings.get_key_value(&ident) {
+                let definition_span = ident.span;
                 return if self.is_label_valid_from_rib(i) {
                     Some(*id)
                 } else {
@@ -1449,7 +1450,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
                         original_span,
                         ResolutionError::UnreachableLabel {
                             name: label.name,
-                            definition_span: ident.span,
+                            definition_span,
                             suggestion,
                         },
                     );
@@ -2135,7 +2136,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
         span: Span,
         err: F,
     ) where
-        F: FnOnce(Ident, &str, Option<Symbol>) -> ResolutionError<'_>,
+        F: FnOnce(Ident, String, Option<Symbol>) -> ResolutionError<'a>,
     {
         // If there is a TraitRef in scope for an impl, then the method must be in the trait.
         let Some((module, _)) = &self.current_trait_ref else { return; };
@@ -2159,7 +2160,8 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
             // We could not find the method: report an error.
             let candidate = self.find_similarly_named_assoc_item(ident.name, kind);
             let path = &self.current_trait_ref.as_ref().unwrap().1.path;
-            self.report_error(span, err(ident, &path_names_to_string(path), candidate));
+            let path_names = path_names_to_string(path);
+            self.report_error(span, err(ident, path_names, candidate));
             return;
         };
 
@@ -2183,13 +2185,14 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
             AssocItemKind::TyAlias(..) => (rustc_errors::error_code!(E0325), "type"),
             AssocItemKind::MacCall(..) => span_bug!(span, "unexpanded macro"),
         };
+        let trait_path = path_names_to_string(path);
         self.report_error(
             span,
             ResolutionError::TraitImplMismatch {
                 name: ident.name,
                 kind,
                 code,
-                trait_path: path_names_to_string(path),
+                trait_path,
                 trait_item_span: binding.span,
             },
         );
@@ -2304,16 +2307,16 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
         }
 
         // 3) Report all missing variables we found.
-        let mut missing_vars = missing_vars.iter_mut().collect::<Vec<_>>();
-        missing_vars.sort_by_key(|(sym, _err)| sym.as_str());
+        let mut missing_vars = missing_vars.into_iter().collect::<Vec<_>>();
+        missing_vars.sort_by_key(|&(sym, ref _err)| sym);
 
-        for (name, mut v) in missing_vars {
-            if inconsistent_vars.contains_key(name) {
+        for (name, mut v) in missing_vars.into_iter() {
+            if inconsistent_vars.contains_key(&name) {
                 v.could_be_path = false;
             }
             self.report_error(
                 *v.origin.iter().next().unwrap(),
-                ResolutionError::VariableNotBoundInPattern(v),
+                ResolutionError::VariableNotBoundInPattern(v, self.parent_scope),
             );
         }
 
@@ -2815,7 +2818,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
     /// A wrapper around [`Resolver::report_error`].
     ///
     /// This doesn't emit errors for function bodies if this is rustdoc.
-    fn report_error(&self, span: Span, resolution_error: ResolutionError<'_>) {
+    fn report_error(&mut self, span: Span, resolution_error: ResolutionError<'a>) {
         if self.should_report_errs() {
             self.r.report_error(span, resolution_error);
         }
diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs
index 64e228a88d9..dbc4f337ad3 100644
--- a/compiler/rustc_resolve/src/lib.rs
+++ b/compiler/rustc_resolve/src/lib.rs
@@ -201,13 +201,13 @@ enum ResolutionError<'a> {
     /// parameter list.
     NameAlreadyUsedInParameterList(Symbol, Span),
     /// Error E0407: method is not a member of trait.
-    MethodNotMemberOfTrait(Ident, &'a str, Option<Symbol>),
+    MethodNotMemberOfTrait(Ident, String, Option<Symbol>),
     /// Error E0437: type is not a member of trait.
-    TypeNotMemberOfTrait(Ident, &'a str, Option<Symbol>),
+    TypeNotMemberOfTrait(Ident, String, Option<Symbol>),
     /// Error E0438: const is not a member of trait.
-    ConstNotMemberOfTrait(Ident, &'a str, Option<Symbol>),
+    ConstNotMemberOfTrait(Ident, String, Option<Symbol>),
     /// Error E0408: variable `{}` is not bound in all patterns.
-    VariableNotBoundInPattern(&'a BindingError),
+    VariableNotBoundInPattern(BindingError, ParentScope<'a>),
     /// Error E0409: variable `{}` is bound in inconsistent ways within the same match arm.
     VariableBoundWithDifferentMode(Symbol, Span),
     /// Error E0415: identifier is bound more than once in this parameter list.
diff --git a/compiler/rustc_typeck/src/check/pat.rs b/compiler/rustc_typeck/src/check/pat.rs
index cf0c5703cd0..356763fab5e 100644
--- a/compiler/rustc_typeck/src/check/pat.rs
+++ b/compiler/rustc_typeck/src/check/pat.rs
@@ -643,6 +643,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             });
             let pre = if in_match { "in the same arm, " } else { "" };
             err.note(&format!("{}a binding must have the same type in all alternatives", pre));
+            // FIXME: check if `var_ty` and `ty` can be made the same type by adding or removing
+            // `ref` or `&` to the pattern.
             err.emit();
         }
     }
diff --git a/src/test/ui/or-patterns/missing-bindings.stderr b/src/test/ui/or-patterns/missing-bindings.stderr
index c173a3a9aba..8fafa275b5c 100644
--- a/src/test/ui/or-patterns/missing-bindings.stderr
+++ b/src/test/ui/or-patterns/missing-bindings.stderr
@@ -103,6 +103,22 @@ LL |     let (A(A(a, b) | B(c), d) | B(e)) = Y;
    |            |
    |            pattern doesn't bind `c`
 
+error[E0408]: variable `d` is not bound in all patterns
+  --> $DIR/missing-bindings.rs:45:33
+   |
+LL |     let (A(A(a, b) | B(c), d) | B(e)) = Y;
+   |                            -    ^^^^ pattern doesn't bind `d`
+   |                            |
+   |                            variable not in all patterns
+
+error[E0408]: variable `e` is not bound in all patterns
+  --> $DIR/missing-bindings.rs:45:10
+   |
+LL |     let (A(A(a, b) | B(c), d) | B(e)) = Y;
+   |          ^^^^^^^^^^^^^^^^^^^^     - variable not in all patterns
+   |          |
+   |          pattern doesn't bind `e`
+
 error[E0408]: variable `a` is not bound in all patterns
   --> $DIR/missing-bindings.rs:45:33
    |
@@ -127,22 +143,6 @@ LL |     let (A(A(a, b) | B(c), d) | B(e)) = Y;
    |                        |
    |                        variable not in all patterns
 
-error[E0408]: variable `d` is not bound in all patterns
-  --> $DIR/missing-bindings.rs:45:33
-   |
-LL |     let (A(A(a, b) | B(c), d) | B(e)) = Y;
-   |                            -    ^^^^ pattern doesn't bind `d`
-   |                            |
-   |                            variable not in all patterns
-
-error[E0408]: variable `e` is not bound in all patterns
-  --> $DIR/missing-bindings.rs:45:10
-   |
-LL |     let (A(A(a, b) | B(c), d) | B(e)) = Y;
-   |          ^^^^^^^^^^^^^^^^^^^^     - variable not in all patterns
-   |          |
-   |          pattern doesn't bind `e`
-
 error[E0408]: variable `a` is not bound in all patterns
   --> $DIR/missing-bindings.rs:61:29
    |
diff --git a/src/test/ui/resolve/resolve-inconsistent-names.rs b/src/test/ui/resolve/resolve-inconsistent-names.rs
index b9202f556d1..989d2d45230 100644
--- a/src/test/ui/resolve/resolve-inconsistent-names.rs
+++ b/src/test/ui/resolve/resolve-inconsistent-names.rs
@@ -2,9 +2,9 @@
 
 enum E { A, B, c }
 
-mod m {
+pub mod m {
     const CONST1: usize = 10;
-    const Const2: usize = 20;
+    pub const Const2: usize = 20;
 }
 
 fn main() {
@@ -22,15 +22,14 @@ fn main() {
         //~| ERROR variable `B` is bound inconsistently
         //~| ERROR mismatched types
         //~| ERROR variable `c` is not bound in all patterns
-        //~| HELP consider making the path in the pattern qualified: `?::A`
+        //~| HELP if you meant to match on unit variant `E::A`, use the full path in the pattern
     }
 
     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`
+        //~| HELP if you meant to match on constant `m::Const2`, use the full path in the pattern
     }
 }
diff --git a/src/test/ui/resolve/resolve-inconsistent-names.stderr b/src/test/ui/resolve/resolve-inconsistent-names.stderr
index 70e9c2e5bf5..9de191f7d32 100644
--- a/src/test/ui/resolve/resolve-inconsistent-names.stderr
+++ b/src/test/ui/resolve/resolve-inconsistent-names.stderr
@@ -23,11 +23,10 @@ LL |         (A, B) | (ref B, c) | (c, A) => ()
    |          |       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
+help: if you meant to match on unit variant `E::A`, use the full path in the pattern
    |
-LL |         (A, B) | (ref B, c) | (c, A) => ()
-   |          ^
+LL |         (E::A, B) | (ref B, c) | (c, A) => ()
+   |          ~~~~
 
 error[E0408]: variable `B` is not bound in all patterns
   --> $DIR/resolve-inconsistent-names.rs:19:31
@@ -63,11 +62,11 @@ LL |         (CONST1, _) | (_, Const2) => ()
    |          |
    |          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
+note: you might have meant to match on constant `m::CONST1`, which exists but is inaccessible
+  --> $DIR/resolve-inconsistent-names.rs:6:5
    |
-LL |         (CONST1, _) | (_, Const2) => ()
-   |          ^^^^^^
+LL |     const CONST1: usize = 10;
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^ not accessible
 
 error[E0408]: variable `Const2` is not bound in all patterns
   --> $DIR/resolve-inconsistent-names.rs:30:9
@@ -77,11 +76,10 @@ LL |         (CONST1, _) | (_, Const2) => ()
    |         |
    |         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
+help: if you meant to match on constant `m::Const2`, use the full path in the pattern
    |
-LL |         (CONST1, _) | (_, Const2) => ()
-   |                           ^^^^^^
+LL |         (CONST1, _) | (_, m::Const2) => ()
+   |                           ~~~~~~~~~
 
 error[E0308]: mismatched types
   --> $DIR/resolve-inconsistent-names.rs:19:19
diff --git a/src/test/ui/span/issue-39698.stderr b/src/test/ui/span/issue-39698.stderr
index 445df90d395..25c35fd5479 100644
--- a/src/test/ui/span/issue-39698.stderr
+++ b/src/test/ui/span/issue-39698.stderr
@@ -1,3 +1,13 @@
+error[E0408]: variable `d` is not bound in all patterns
+  --> $DIR/issue-39698.rs:10:37
+   |
+LL |         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 `a` is not bound in all patterns
   --> $DIR/issue-39698.rs:10:23
    |
@@ -28,16 +38,6 @@ LL |         T::T1(a, d) | T::T2(d, b) | T::T3(c) | T::T4(a) => { println!("{:?}
    |         |             pattern doesn't bind `c`
    |         pattern doesn't bind `c`
 
-error[E0408]: variable `d` is not bound in all patterns
-  --> $DIR/issue-39698.rs:10:37
-   |
-LL |         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: aborting due to 4 previous errors
 
 For more information about this error, try `rustc --explain E0408`.