about summary refs log tree commit diff
diff options
context:
space:
mode:
authorFabian Zaiser <fabian.zaiser@gmail.com>2020-11-11 13:15:15 +0000
committerFabian Zaiser <fabian.zaiser@gmail.com>2020-11-14 13:53:12 +0000
commit8cf35643106bba09b5d6c71ceac74dc58573f371 (patch)
tree3d4cbfbfb737a9092150e94b7fa92713910365f7
parenta38f8fb674e6a0a6fc358655c6ce6069235f621a (diff)
downloadrust-8cf35643106bba09b5d6c71ceac74dc58573f371.tar.gz
rust-8cf35643106bba09b5d6c71ceac74dc58573f371.zip
Add underscore expressions for destructuring assignments
Co-authored-by: varkor <github@varkor.com>
-rw-r--r--compiler/rustc_ast/src/ast.rs3
-rw-r--r--compiler/rustc_ast/src/mut_visit.rs1
-rw-r--r--compiler/rustc_ast/src/visit.rs1
-rw-r--r--compiler/rustc_ast_lowering/src/expr.rs19
-rw-r--r--compiler/rustc_ast_passes/src/feature_gate.rs6
-rw-r--r--compiler/rustc_ast_pretty/src/pprust/state.rs1
-rw-r--r--compiler/rustc_parse/src/parser/expr.rs3
-rw-r--r--src/test/ui/cross/cross-file-errors/main.rs3
-rw-r--r--src/test/ui/cross/cross-file-errors/main.stderr22
-rw-r--r--src/test/ui/destructuring-assignment/nested_destructure.rs3
-rw-r--r--src/test/ui/destructuring-assignment/slice_destructure.rs2
-rw-r--r--src/test/ui/destructuring-assignment/slice_destructure_fail.rs1
-rw-r--r--src/test/ui/destructuring-assignment/slice_destructure_fail.stderr8
-rw-r--r--src/test/ui/destructuring-assignment/struct_destructure.rs6
-rw-r--r--src/test/ui/destructuring-assignment/struct_destructure_fail.rs2
-rw-r--r--src/test/ui/destructuring-assignment/struct_destructure_fail.stderr32
-rw-r--r--src/test/ui/destructuring-assignment/tuple_destructure.rs2
-rw-r--r--src/test/ui/destructuring-assignment/tuple_destructure_fail.rs1
-rw-r--r--src/test/ui/destructuring-assignment/tuple_destructure_fail.stderr13
-rw-r--r--src/test/ui/destructuring-assignment/tuple_struct_destructure.rs4
-rw-r--r--src/test/ui/destructuring-assignment/tuple_struct_destructure_fail.rs4
-rw-r--r--src/test/ui/destructuring-assignment/tuple_struct_destructure_fail.stderr28
-rw-r--r--src/test/ui/destructuring-assignment/underscore-range-expr-gating.rs2
-rw-r--r--src/test/ui/destructuring-assignment/underscore-range-expr-gating.stderr13
-rw-r--r--src/test/ui/suggestions/fn-or-tuple-struct-with-underscore-args.rs18
-rw-r--r--src/test/ui/suggestions/fn-or-tuple-struct-with-underscore-args.stderr89
-rw-r--r--src/tools/clippy/clippy_lints/src/utils/sugg.rs1
27 files changed, 243 insertions, 45 deletions
diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs
index 3e953729aab..328086af183 100644
--- a/compiler/rustc_ast/src/ast.rs
+++ b/compiler/rustc_ast/src/ast.rs
@@ -1192,6 +1192,7 @@ impl Expr {
             ExprKind::Field(..) => ExprPrecedence::Field,
             ExprKind::Index(..) => ExprPrecedence::Index,
             ExprKind::Range(..) => ExprPrecedence::Range,
+            ExprKind::Underscore => ExprPrecedence::Path,
             ExprKind::Path(..) => ExprPrecedence::Path,
             ExprKind::AddrOf(..) => ExprPrecedence::AddrOf,
             ExprKind::Break(..) => ExprPrecedence::Break,
@@ -1324,6 +1325,8 @@ pub enum ExprKind {
     Index(P<Expr>, P<Expr>),
     /// A range (e.g., `1..2`, `1..`, `..2`, `1..=2`, `..=2`; and `..` in destructuring assingment).
     Range(Option<P<Expr>>, Option<P<Expr>>, RangeLimits),
+    /// An underscore, used in destructuring assignment to ignore a value.
+    Underscore,
 
     /// Variable reference, possibly containing `::` and/or type
     /// parameters (e.g., `foo::bar::<baz>`).
diff --git a/compiler/rustc_ast/src/mut_visit.rs b/compiler/rustc_ast/src/mut_visit.rs
index 26097980e8b..ddae0ab03e4 100644
--- a/compiler/rustc_ast/src/mut_visit.rs
+++ b/compiler/rustc_ast/src/mut_visit.rs
@@ -1232,6 +1232,7 @@ pub fn noop_visit_expr<T: MutVisitor>(
             visit_opt(e1, |e1| vis.visit_expr(e1));
             visit_opt(e2, |e2| vis.visit_expr(e2));
         }
+        ExprKind::Underscore => {}
         ExprKind::Path(qself, path) => {
             vis.visit_qself(qself);
             vis.visit_path(path);
diff --git a/compiler/rustc_ast/src/visit.rs b/compiler/rustc_ast/src/visit.rs
index 49b521afcdc..560064182e1 100644
--- a/compiler/rustc_ast/src/visit.rs
+++ b/compiler/rustc_ast/src/visit.rs
@@ -806,6 +806,7 @@ pub fn walk_expr<'a, V: Visitor<'a>>(visitor: &mut V, expression: &'a Expr) {
             walk_list!(visitor, visit_expr, start);
             walk_list!(visitor, visit_expr, end);
         }
+        ExprKind::Underscore => {}
         ExprKind::Path(ref maybe_qself, ref path) => {
             if let Some(ref qself) = *maybe_qself {
                 visitor.visit_ty(&qself.ty);
diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs
index 330776fc8c5..ecbe97bd45a 100644
--- a/compiler/rustc_ast_lowering/src/expr.rs
+++ b/compiler/rustc_ast_lowering/src/expr.rs
@@ -164,6 +164,16 @@ impl<'hir> LoweringContext<'_, 'hir> {
                 ExprKind::Range(ref e1, ref e2, lims) => {
                     self.lower_expr_range(e.span, e1.as_deref(), e2.as_deref(), lims)
                 }
+                ExprKind::Underscore => {
+                    self.sess
+                        .struct_span_err(
+                            e.span,
+                            "in expressions, `_` can only be used on the left-hand side of an assignment",
+                        )
+                        .span_label(e.span, "`_` not allowed here")
+                        .emit();
+                    hir::ExprKind::Err
+                }
                 ExprKind::Path(ref qself, ref path) => {
                     let qpath = self.lower_qpath(
                         e.id,
@@ -863,7 +873,10 @@ impl<'hir> LoweringContext<'_, 'hir> {
         // Return early in case of an ordinary assignment.
         fn is_ordinary(lower_ctx: &mut LoweringContext<'_, '_>, lhs: &Expr) -> bool {
             match &lhs.kind {
-                ExprKind::Array(..) | ExprKind::Struct(..) | ExprKind::Tup(..) => false,
+                ExprKind::Array(..)
+                | ExprKind::Struct(..)
+                | ExprKind::Tup(..)
+                | ExprKind::Underscore => false,
                 // Check for tuple struct constructor.
                 ExprKind::Call(callee, ..) => lower_ctx.extract_tuple_struct_path(callee).is_none(),
                 ExprKind::Paren(e) => {
@@ -943,6 +956,10 @@ impl<'hir> LoweringContext<'_, 'hir> {
         assignments: &mut Vec<hir::Stmt<'hir>>,
     ) -> &'hir hir::Pat<'hir> {
         match &lhs.kind {
+            // Underscore pattern.
+            ExprKind::Underscore => {
+                return self.pat_without_dbm(lhs.span, hir::PatKind::Wild);
+            }
             // Slice patterns.
             ExprKind::Array(elements) => {
                 let (pats, rest) =
diff --git a/compiler/rustc_ast_passes/src/feature_gate.rs b/compiler/rustc_ast_passes/src/feature_gate.rs
index 2831675cb36..181783441f3 100644
--- a/compiler/rustc_ast_passes/src/feature_gate.rs
+++ b/compiler/rustc_ast_passes/src/feature_gate.rs
@@ -630,7 +630,11 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session) {
     gate_all!(const_trait_impl, "const trait impls are experimental");
     gate_all!(half_open_range_patterns, "half-open range patterns are unstable");
     gate_all!(inline_const, "inline-const is experimental");
-    gate_all!(destructuring_assignment, "destructuring assignments are unstable");
+    if sess.parse_sess.span_diagnostic.err_count() == 0 {
+        // Errors for `destructuring_assignment` can get quite noisy, especially where `_` is
+        // involved, so we only emit errors where there are no other parsing errors.
+        gate_all!(destructuring_assignment, "destructuring assignments are unstable");
+    }
 
     // All uses of `gate_all!` below this point were added in #65742,
     // and subsequently disabled (with the non-early gating readded).
diff --git a/compiler/rustc_ast_pretty/src/pprust/state.rs b/compiler/rustc_ast_pretty/src/pprust/state.rs
index a566200c338..887b60f98f7 100644
--- a/compiler/rustc_ast_pretty/src/pprust/state.rs
+++ b/compiler/rustc_ast_pretty/src/pprust/state.rs
@@ -2068,6 +2068,7 @@ impl<'a> State<'a> {
                     self.print_expr_maybe_paren(e, fake_prec);
                 }
             }
+            ast::ExprKind::Underscore => self.s.word("_"),
             ast::ExprKind::Path(None, ref path) => self.print_path(path, true, 0),
             ast::ExprKind::Path(Some(ref qself), ref path) => self.print_qpath(path, qself, true),
             ast::ExprKind::Break(opt_label, ref opt_expr) => {
diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs
index 188bf227c42..ffbf786491d 100644
--- a/compiler/rustc_parse/src/parser/expr.rs
+++ b/compiler/rustc_parse/src/parser/expr.rs
@@ -1089,6 +1089,9 @@ impl<'a> Parser<'a> {
             self.parse_yield_expr(attrs)
         } else if self.eat_keyword(kw::Let) {
             self.parse_let_expr(attrs)
+        } else if self.eat_keyword(kw::Underscore) {
+            self.sess.gated_spans.gate(sym::destructuring_assignment, self.prev_token.span);
+            Ok(self.mk_expr(self.prev_token.span, ExprKind::Underscore, attrs))
         } else if !self.unclosed_delims.is_empty() && self.check(&token::Semi) {
             // Don't complain about bare semicolons after unclosed braces
             // recovery in order to keep the error count down. Fixing the
diff --git a/src/test/ui/cross/cross-file-errors/main.rs b/src/test/ui/cross/cross-file-errors/main.rs
index 74e9461803c..1902ab94d4c 100644
--- a/src/test/ui/cross/cross-file-errors/main.rs
+++ b/src/test/ui/cross/cross-file-errors/main.rs
@@ -3,5 +3,6 @@ mod underscore;
 
 fn main() {
     underscore!();
-    //~^ ERROR expected expression, found reserved identifier `_`
+    //~^ ERROR `_` can only be used on the left-hand side of an assignment
+    //~| ERROR destructuring assignments are unstable
 }
diff --git a/src/test/ui/cross/cross-file-errors/main.stderr b/src/test/ui/cross/cross-file-errors/main.stderr
index f9101d8a583..b8658745060 100644
--- a/src/test/ui/cross/cross-file-errors/main.stderr
+++ b/src/test/ui/cross/cross-file-errors/main.stderr
@@ -1,15 +1,31 @@
-error: expected expression, found reserved identifier `_`
+error[E0658]: destructuring assignments are unstable
   --> $DIR/underscore.rs:8:9
    |
 LL |         _
-   |         ^ expected expression
+   |         ^
    | 
   ::: $DIR/main.rs:5:5
    |
 LL |     underscore!();
    |     -------------- in this macro invocation
    |
+   = note: see issue #71126 <https://github.com/rust-lang/rust/issues/71126> for more information
+   = help: add `#![feature(destructuring_assignment)]` to the crate attributes to enable
    = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
 
-error: aborting due to previous error
+error: in expressions, `_` can only be used on the left-hand side of an assignment
+  --> $DIR/underscore.rs:8:9
+   |
+LL |         _
+   |         ^ `_` not allowed here
+   | 
+  ::: $DIR/main.rs:5:5
+   |
+LL |     underscore!();
+   |     -------------- in this macro invocation
+   |
+   = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: aborting due to 2 previous errors
 
+For more information about this error, try `rustc --explain E0658`.
diff --git a/src/test/ui/destructuring-assignment/nested_destructure.rs b/src/test/ui/destructuring-assignment/nested_destructure.rs
index 393dfc16c0a..0d45ff7da72 100644
--- a/src/test/ui/destructuring-assignment/nested_destructure.rs
+++ b/src/test/ui/destructuring-assignment/nested_destructure.rs
@@ -14,4 +14,7 @@ fn main() {
     Struct { a: TupleStruct((a, b), c), b: [d] } =
         Struct { a: TupleStruct((0, 1), 2), b: [3] };
     assert_eq!((a, b, c, d), (0, 1, 2, 3));
+
+    // unnested underscore: just discard
+    _ = 1;
 }
diff --git a/src/test/ui/destructuring-assignment/slice_destructure.rs b/src/test/ui/destructuring-assignment/slice_destructure.rs
index 3dd10aff19c..76cdc1260fc 100644
--- a/src/test/ui/destructuring-assignment/slice_destructure.rs
+++ b/src/test/ui/destructuring-assignment/slice_destructure.rs
@@ -9,6 +9,8 @@ fn main() {
   let mut c;
   [a, .., b, c] = [1, 2, 3, 4, 5];
   assert_eq!((a, b, c), (1, 4, 5));
+  [_, a, _] = [1, 2, 3];
+  assert_eq!((a, b), (2, 4));
   [..] = [1, 2, 3];
   [c, ..] = [5, 6, 6];
   assert_eq!(c, 5);
diff --git a/src/test/ui/destructuring-assignment/slice_destructure_fail.rs b/src/test/ui/destructuring-assignment/slice_destructure_fail.rs
index f636ea3511c..90d93892f7f 100644
--- a/src/test/ui/destructuring-assignment/slice_destructure_fail.rs
+++ b/src/test/ui/destructuring-assignment/slice_destructure_fail.rs
@@ -4,4 +4,5 @@ fn main() {
   let (mut a, mut b);
   [a, .., b, ..] = [0, 1]; //~ ERROR `..` can only be used once per slice pattern
   [a, a, b] = [1, 2]; //~ ERROR pattern requires 3 elements but array has 2
+  [_] = [1, 2]; //~ ERROR pattern requires 1 element but array has 2
 }
diff --git a/src/test/ui/destructuring-assignment/slice_destructure_fail.stderr b/src/test/ui/destructuring-assignment/slice_destructure_fail.stderr
index 728687deb8b..cc412c72df5 100644
--- a/src/test/ui/destructuring-assignment/slice_destructure_fail.stderr
+++ b/src/test/ui/destructuring-assignment/slice_destructure_fail.stderr
@@ -12,6 +12,12 @@ error[E0527]: pattern requires 3 elements but array has 2
 LL |   [a, a, b] = [1, 2];
    |   ^^^^^^^^^ expected 2 elements
 
-error: aborting due to 2 previous errors
+error[E0527]: pattern requires 1 element but array has 2
+  --> $DIR/slice_destructure_fail.rs:7:3
+   |
+LL |   [_] = [1, 2];
+   |   ^^^ expected 2 elements
+
+error: aborting due to 3 previous errors
 
 For more information about this error, try `rustc --explain E0527`.
diff --git a/src/test/ui/destructuring-assignment/struct_destructure.rs b/src/test/ui/destructuring-assignment/struct_destructure.rs
index b3a96ee1573..2bcbd9d0d74 100644
--- a/src/test/ui/destructuring-assignment/struct_destructure.rs
+++ b/src/test/ui/destructuring-assignment/struct_destructure.rs
@@ -12,8 +12,10 @@ fn main() {
     assert_eq!((a, b), (0, 1));
     Struct { a: b, b: a }  = Struct { a: 1, b: 2 };
     assert_eq!((a,b), (2, 1));
+    Struct { a: _, b } = Struct { a: 1, b: 2 };
+    assert_eq!((a, b), (2, 2));
     Struct { a, .. } = Struct { a: 1, b: 3 };
-    assert_eq!((a, b), (1, 1));
+    assert_eq!((a, b), (1, 2));
     Struct { .. } = Struct { a: 1, b: 4 };
-    assert_eq!((a, b), (1, 1));
+    assert_eq!((a, b), (1, 2));
 }
diff --git a/src/test/ui/destructuring-assignment/struct_destructure_fail.rs b/src/test/ui/destructuring-assignment/struct_destructure_fail.rs
index c22695ed388..4aa327b61f4 100644
--- a/src/test/ui/destructuring-assignment/struct_destructure_fail.rs
+++ b/src/test/ui/destructuring-assignment/struct_destructure_fail.rs
@@ -9,6 +9,8 @@ fn main() {
     let mut c;
     let d = Struct { a: 0, b: 1 };
     Struct { a, b, c } = Struct { a: 0, b: 1 }; //~ ERROR does not have a field named `c`
+    Struct { a, _ } = Struct { a: 1, b: 2 }; //~ ERROR pattern does not mention field `b`
+    //~| ERROR expected identifier, found reserved identifier `_`
     Struct { a, ..d } = Struct { a: 1, b: 2 };
     //~^ ERROR functional record updates are not allowed in destructuring assignments
     Struct { a, .. }; //~ ERROR base expression required after `..`
diff --git a/src/test/ui/destructuring-assignment/struct_destructure_fail.stderr b/src/test/ui/destructuring-assignment/struct_destructure_fail.stderr
index 4da4698804f..81661a357e7 100644
--- a/src/test/ui/destructuring-assignment/struct_destructure_fail.stderr
+++ b/src/test/ui/destructuring-assignment/struct_destructure_fail.stderr
@@ -1,11 +1,19 @@
+error: expected identifier, found reserved identifier `_`
+  --> $DIR/struct_destructure_fail.rs:12:17
+   |
+LL |     Struct { a, _ } = Struct { a: 1, b: 2 };
+   |     ------      ^ expected identifier, found reserved identifier
+   |     |
+   |     while parsing this struct
+
 error: functional record updates are not allowed in destructuring assignments
-  --> $DIR/struct_destructure_fail.rs:12:19
+  --> $DIR/struct_destructure_fail.rs:14:19
    |
 LL |     Struct { a, ..d } = Struct { a: 1, b: 2 };
    |                   ^ help: consider removing the trailing pattern
 
 error: base expression required after `..`
-  --> $DIR/struct_destructure_fail.rs:14:19
+  --> $DIR/struct_destructure_fail.rs:16:19
    |
 LL |     Struct { a, .. };
    |                   ^ add a base expression here
@@ -16,6 +24,22 @@ error[E0026]: struct `Struct` does not have a field named `c`
 LL |     Struct { a, b, c } = Struct { a: 0, b: 1 };
    |                    ^ struct `Struct` does not have this field
 
-error: aborting due to 3 previous errors
+error[E0027]: pattern does not mention field `b`
+  --> $DIR/struct_destructure_fail.rs:12:5
+   |
+LL |     Struct { a, _ } = Struct { a: 1, b: 2 };
+   |     ^^^^^^^^^^^^^^^ missing field `b`
+   |
+help: include the missing field in the pattern
+   |
+LL |     Struct { a, b, _ } = Struct { a: 1, b: 2 };
+   |               ^^^
+help: if you don't care about this missing field, you can explicitly ignore it
+   |
+LL |     Struct { a, .., _ } = Struct { a: 1, b: 2 };
+   |               ^^^^
+
+error: aborting due to 5 previous errors
 
-For more information about this error, try `rustc --explain E0026`.
+Some errors have detailed explanations: E0026, E0027.
+For more information about an error, try `rustc --explain E0026`.
diff --git a/src/test/ui/destructuring-assignment/tuple_destructure.rs b/src/test/ui/destructuring-assignment/tuple_destructure.rs
index 16aafc4693f..2096182d421 100644
--- a/src/test/ui/destructuring-assignment/tuple_destructure.rs
+++ b/src/test/ui/destructuring-assignment/tuple_destructure.rs
@@ -16,6 +16,8 @@ fn main() {
     assert_eq!((a, b), (2, 2));
     (b, ..) = (5, 6, 7);
     assert_eq!(b, 5);
+    (a, _) = (8, 9);
+    assert_eq!(a, 8);
 
     // Test for a non-Copy type (String):
     let (mut c, mut d);
diff --git a/src/test/ui/destructuring-assignment/tuple_destructure_fail.rs b/src/test/ui/destructuring-assignment/tuple_destructure_fail.rs
index b76f4968e62..5524e91dc40 100644
--- a/src/test/ui/destructuring-assignment/tuple_destructure_fail.rs
+++ b/src/test/ui/destructuring-assignment/tuple_destructure_fail.rs
@@ -7,4 +7,5 @@ fn main() {
     (a, .., b, ..) = (0, 1); //~ ERROR `..` can only be used once per tuple pattern
     (a, a, b) = (1, 2); //~ ERROR mismatched types
     (C, ..) = (0,1); //~ ERROR invalid left-hand side of assignment
+    (_,) = (1, 2); //~ ERROR mismatched types
 }
diff --git a/src/test/ui/destructuring-assignment/tuple_destructure_fail.stderr b/src/test/ui/destructuring-assignment/tuple_destructure_fail.stderr
index a60e1cb1eec..1146b88278d 100644
--- a/src/test/ui/destructuring-assignment/tuple_destructure_fail.stderr
+++ b/src/test/ui/destructuring-assignment/tuple_destructure_fail.stderr
@@ -25,7 +25,18 @@ LL |     (C, ..) = (0,1);
    |      |
    |      cannot assign to this expression
 
-error: aborting due to 3 previous errors
+error[E0308]: mismatched types
+  --> $DIR/tuple_destructure_fail.rs:10:5
+   |
+LL |     (_,) = (1, 2);
+   |     ^^^^   ------ this expression has type `({integer}, {integer})`
+   |     |
+   |     expected a tuple with 2 elements, found one with 1 element
+   |
+   = note: expected type `({integer}, {integer})`
+             found tuple `(_,)`
+
+error: aborting due to 4 previous errors
 
 Some errors have detailed explanations: E0070, E0308.
 For more information about an error, try `rustc --explain E0070`.
diff --git a/src/test/ui/destructuring-assignment/tuple_struct_destructure.rs b/src/test/ui/destructuring-assignment/tuple_struct_destructure.rs
index 106a9b16db4..7b5c5ad2bae 100644
--- a/src/test/ui/destructuring-assignment/tuple_struct_destructure.rs
+++ b/src/test/ui/destructuring-assignment/tuple_struct_destructure.rs
@@ -23,8 +23,10 @@ fn main() {
     assert_eq!((a, b), (0, 1));
     TupleStruct(a, .., b) = TupleStruct(1, 2);
     assert_eq!((a, b), (1, 2));
+    TupleStruct(_, a) = TupleStruct(2, 2);
+    assert_eq!((a, b), (2, 2));
     TupleStruct(..) = TupleStruct(3, 4);
-    assert_eq!((a, b), (1, 2));
+    assert_eq!((a, b), (2, 2));
     TupleStruct(5,6).assign(&mut a, &mut b);
     assert_eq!((a, b), (5, 6));
     Enum::SingleVariant(a, b) = Enum::SingleVariant(7, 8);
diff --git a/src/test/ui/destructuring-assignment/tuple_struct_destructure_fail.rs b/src/test/ui/destructuring-assignment/tuple_struct_destructure_fail.rs
index 61ae42a5175..c39db061177 100644
--- a/src/test/ui/destructuring-assignment/tuple_struct_destructure_fail.rs
+++ b/src/test/ui/destructuring-assignment/tuple_struct_destructure_fail.rs
@@ -29,8 +29,12 @@ fn main() {
 
     TupleStruct(a, a, b) = TupleStruct(1, 2);
     //~^ ERROR this pattern has 3 fields, but the corresponding tuple struct has 2 fields
+    TupleStruct(_) = TupleStruct(1, 2);
+    //~^ ERROR this pattern has 1 field, but the corresponding tuple struct has 2 fields
     Enum::SingleVariant(a, a, b) = Enum::SingleVariant(1, 2);
     //~^ ERROR this pattern has 3 fields, but the corresponding tuple variant has 2 fields
+    Enum::SingleVariant(_) = Enum::SingleVariant(1, 2);
+    //~^ ERROR this pattern has 1 field, but the corresponding tuple variant has 2 fields
 
     // Check if `test` is recognized as not a tuple struct but a function call:
     test() = TupleStruct(0, 0);
diff --git a/src/test/ui/destructuring-assignment/tuple_struct_destructure_fail.stderr b/src/test/ui/destructuring-assignment/tuple_struct_destructure_fail.stderr
index 863eedecf76..0e7174e5b19 100644
--- a/src/test/ui/destructuring-assignment/tuple_struct_destructure_fail.stderr
+++ b/src/test/ui/destructuring-assignment/tuple_struct_destructure_fail.stderr
@@ -23,17 +23,35 @@ LL | struct TupleStruct<S, T>(S, T);
 LL |     TupleStruct(a, a, b) = TupleStruct(1, 2);
    |     ^^^^^^^^^^^^^^^^^^^^ expected 2 fields, found 3
 
-error[E0023]: this pattern has 3 fields, but the corresponding tuple variant has 2 fields
+error[E0023]: this pattern has 1 field, but the corresponding tuple struct has 2 fields
   --> $DIR/tuple_struct_destructure_fail.rs:32:5
    |
+LL | struct TupleStruct<S, T>(S, T);
+   | ------------------------------- tuple struct defined here
+...
+LL |     TupleStruct(_) = TupleStruct(1, 2);
+   |     ^^^^^^^^^^^^^^ expected 2 fields, found 1
+
+error[E0023]: this pattern has 3 fields, but the corresponding tuple variant has 2 fields
+  --> $DIR/tuple_struct_destructure_fail.rs:34:5
+   |
 LL |     SingleVariant(S, T)
    |     ------------------- tuple variant defined here
 ...
 LL |     Enum::SingleVariant(a, a, b) = Enum::SingleVariant(1, 2);
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected 2 fields, found 3
 
+error[E0023]: this pattern has 1 field, but the corresponding tuple variant has 2 fields
+  --> $DIR/tuple_struct_destructure_fail.rs:36:5
+   |
+LL |     SingleVariant(S, T)
+   |     ------------------- tuple variant defined here
+...
+LL |     Enum::SingleVariant(_) = Enum::SingleVariant(1, 2);
+   |     ^^^^^^^^^^^^^^^^^^^^^^ expected 2 fields, found 1
+
 error[E0070]: invalid left-hand side of assignment
-  --> $DIR/tuple_struct_destructure_fail.rs:36:12
+  --> $DIR/tuple_struct_destructure_fail.rs:40:12
    |
 LL |     test() = TupleStruct(0, 0);
    |     ------ ^
@@ -41,7 +59,7 @@ LL |     test() = TupleStruct(0, 0);
    |     cannot assign to this expression
 
 error[E0070]: invalid left-hand side of assignment
-  --> $DIR/tuple_struct_destructure_fail.rs:38:14
+  --> $DIR/tuple_struct_destructure_fail.rs:42:14
    |
 LL |     (test)() = TupleStruct(0, 0);
    |     -------- ^
@@ -49,14 +67,14 @@ LL |     (test)() = TupleStruct(0, 0);
    |     cannot assign to this expression
 
 error[E0070]: invalid left-hand side of assignment
-  --> $DIR/tuple_struct_destructure_fail.rs:40:38
+  --> $DIR/tuple_struct_destructure_fail.rs:44:38
    |
 LL |     <Alias::<isize> as Test>::test() = TupleStruct(0, 0);
    |     -------------------------------- ^
    |     |
    |     cannot assign to this expression
 
-error: aborting due to 7 previous errors
+error: aborting due to 9 previous errors
 
 Some errors have detailed explanations: E0023, E0070.
 For more information about an error, try `rustc --explain E0023`.
diff --git a/src/test/ui/destructuring-assignment/underscore-range-expr-gating.rs b/src/test/ui/destructuring-assignment/underscore-range-expr-gating.rs
index b41f2f52a3d..4ed4f56702c 100644
--- a/src/test/ui/destructuring-assignment/underscore-range-expr-gating.rs
+++ b/src/test/ui/destructuring-assignment/underscore-range-expr-gating.rs
@@ -4,5 +4,7 @@ struct S { x : u32 }
 
 #[cfg(FALSE)]
 fn foo() {
+    _; //~ ERROR destructuring assignments are unstable
+
     S { x: 5, .. }; //~ ERROR destructuring assignments are unstable
 }
diff --git a/src/test/ui/destructuring-assignment/underscore-range-expr-gating.stderr b/src/test/ui/destructuring-assignment/underscore-range-expr-gating.stderr
index 442e36cd306..a5ed761a01c 100644
--- a/src/test/ui/destructuring-assignment/underscore-range-expr-gating.stderr
+++ b/src/test/ui/destructuring-assignment/underscore-range-expr-gating.stderr
@@ -1,5 +1,14 @@
 error[E0658]: destructuring assignments are unstable
-  --> $DIR/underscore-range-expr-gating.rs:7:15
+  --> $DIR/underscore-range-expr-gating.rs:7:5
+   |
+LL |     _;
+   |     ^
+   |
+   = note: see issue #71126 <https://github.com/rust-lang/rust/issues/71126> for more information
+   = help: add `#![feature(destructuring_assignment)]` to the crate attributes to enable
+
+error[E0658]: destructuring assignments are unstable
+  --> $DIR/underscore-range-expr-gating.rs:9:15
    |
 LL |     S { x: 5, .. };
    |               ^^
@@ -7,6 +16,6 @@ LL |     S { x: 5, .. };
    = note: see issue #71126 <https://github.com/rust-lang/rust/issues/71126> for more information
    = help: add `#![feature(destructuring_assignment)]` to the crate attributes to enable
 
-error: aborting due to previous error
+error: aborting due to 2 previous errors
 
 For more information about this error, try `rustc --explain E0658`.
diff --git a/src/test/ui/suggestions/fn-or-tuple-struct-with-underscore-args.rs b/src/test/ui/suggestions/fn-or-tuple-struct-with-underscore-args.rs
index a8ea3faefe8..00638e04f5d 100644
--- a/src/test/ui/suggestions/fn-or-tuple-struct-with-underscore-args.rs
+++ b/src/test/ui/suggestions/fn-or-tuple-struct-with-underscore-args.rs
@@ -8,12 +8,18 @@ trait T {
 
 fn main() {
     let _: usize = foo(_, _);
-    //~^ ERROR expected expression
-    //~| ERROR expected expression
+    //~^ ERROR `_` can only be used on the left-hand side of an assignment
+    //~| ERROR `_` can only be used on the left-hand side of an assignment
+    //~| ERROR destructuring assignments are unstable
+    //~| ERROR destructuring assignments are unstable
     let _: S = S(_, _);
-    //~^ ERROR expected expression
-    //~| ERROR expected expression
+    //~^ ERROR `_` can only be used on the left-hand side of an assignment
+    //~| ERROR `_` can only be used on the left-hand side of an assignment
+    //~| ERROR destructuring assignments are unstable
+    //~| ERROR destructuring assignments are unstable
     let _: usize = T::baz(_, _);
-    //~^ ERROR expected expression
-    //~| ERROR expected expression
+    //~^ ERROR `_` can only be used on the left-hand side of an assignment
+    //~| ERROR `_` can only be used on the left-hand side of an assignment
+    //~| ERROR destructuring assignments are unstable
+    //~| ERROR destructuring assignments are unstable
 }
diff --git a/src/test/ui/suggestions/fn-or-tuple-struct-with-underscore-args.stderr b/src/test/ui/suggestions/fn-or-tuple-struct-with-underscore-args.stderr
index a6d1c4b859f..248fa6b9c9c 100644
--- a/src/test/ui/suggestions/fn-or-tuple-struct-with-underscore-args.stderr
+++ b/src/test/ui/suggestions/fn-or-tuple-struct-with-underscore-args.stderr
@@ -1,38 +1,93 @@
-error: expected expression, found reserved identifier `_`
+error[E0658]: destructuring assignments are unstable
   --> $DIR/fn-or-tuple-struct-with-underscore-args.rs:10:24
    |
 LL |     let _: usize = foo(_, _);
-   |                        ^ expected expression
+   |                        ^
+   |
+   = note: see issue #71126 <https://github.com/rust-lang/rust/issues/71126> for more information
+   = help: add `#![feature(destructuring_assignment)]` to the crate attributes to enable
+
+error[E0658]: destructuring assignments are unstable
+  --> $DIR/fn-or-tuple-struct-with-underscore-args.rs:10:27
+   |
+LL |     let _: usize = foo(_, _);
+   |                           ^
+   |
+   = note: see issue #71126 <https://github.com/rust-lang/rust/issues/71126> for more information
+   = help: add `#![feature(destructuring_assignment)]` to the crate attributes to enable
+
+error[E0658]: destructuring assignments are unstable
+  --> $DIR/fn-or-tuple-struct-with-underscore-args.rs:15:18
+   |
+LL |     let _: S = S(_, _);
+   |                  ^
+   |
+   = note: see issue #71126 <https://github.com/rust-lang/rust/issues/71126> for more information
+   = help: add `#![feature(destructuring_assignment)]` to the crate attributes to enable
+
+error[E0658]: destructuring assignments are unstable
+  --> $DIR/fn-or-tuple-struct-with-underscore-args.rs:15:21
+   |
+LL |     let _: S = S(_, _);
+   |                     ^
+   |
+   = note: see issue #71126 <https://github.com/rust-lang/rust/issues/71126> for more information
+   = help: add `#![feature(destructuring_assignment)]` to the crate attributes to enable
+
+error[E0658]: destructuring assignments are unstable
+  --> $DIR/fn-or-tuple-struct-with-underscore-args.rs:20:27
+   |
+LL |     let _: usize = T::baz(_, _);
+   |                           ^
+   |
+   = note: see issue #71126 <https://github.com/rust-lang/rust/issues/71126> for more information
+   = help: add `#![feature(destructuring_assignment)]` to the crate attributes to enable
+
+error[E0658]: destructuring assignments are unstable
+  --> $DIR/fn-or-tuple-struct-with-underscore-args.rs:20:30
+   |
+LL |     let _: usize = T::baz(_, _);
+   |                              ^
+   |
+   = note: see issue #71126 <https://github.com/rust-lang/rust/issues/71126> for more information
+   = help: add `#![feature(destructuring_assignment)]` to the crate attributes to enable
+
+error: in expressions, `_` can only be used on the left-hand side of an assignment
+  --> $DIR/fn-or-tuple-struct-with-underscore-args.rs:10:24
+   |
+LL |     let _: usize = foo(_, _);
+   |                        ^ `_` not allowed here
 
-error: expected expression, found reserved identifier `_`
+error: in expressions, `_` can only be used on the left-hand side of an assignment
   --> $DIR/fn-or-tuple-struct-with-underscore-args.rs:10:27
    |
 LL |     let _: usize = foo(_, _);
-   |                           ^ expected expression
+   |                           ^ `_` not allowed here
 
-error: expected expression, found reserved identifier `_`
-  --> $DIR/fn-or-tuple-struct-with-underscore-args.rs:13:18
+error: in expressions, `_` can only be used on the left-hand side of an assignment
+  --> $DIR/fn-or-tuple-struct-with-underscore-args.rs:15:18
    |
 LL |     let _: S = S(_, _);
-   |                  ^ expected expression
+   |                  ^ `_` not allowed here
 
-error: expected expression, found reserved identifier `_`
-  --> $DIR/fn-or-tuple-struct-with-underscore-args.rs:13:21
+error: in expressions, `_` can only be used on the left-hand side of an assignment
+  --> $DIR/fn-or-tuple-struct-with-underscore-args.rs:15:21
    |
 LL |     let _: S = S(_, _);
-   |                     ^ expected expression
+   |                     ^ `_` not allowed here
 
-error: expected expression, found reserved identifier `_`
-  --> $DIR/fn-or-tuple-struct-with-underscore-args.rs:16:27
+error: in expressions, `_` can only be used on the left-hand side of an assignment
+  --> $DIR/fn-or-tuple-struct-with-underscore-args.rs:20:27
    |
 LL |     let _: usize = T::baz(_, _);
-   |                           ^ expected expression
+   |                           ^ `_` not allowed here
 
-error: expected expression, found reserved identifier `_`
-  --> $DIR/fn-or-tuple-struct-with-underscore-args.rs:16:30
+error: in expressions, `_` can only be used on the left-hand side of an assignment
+  --> $DIR/fn-or-tuple-struct-with-underscore-args.rs:20:30
    |
 LL |     let _: usize = T::baz(_, _);
-   |                              ^ expected expression
+   |                              ^ `_` not allowed here
 
-error: aborting due to 6 previous errors
+error: aborting due to 12 previous errors
 
+For more information about this error, try `rustc --explain E0658`.
diff --git a/src/tools/clippy/clippy_lints/src/utils/sugg.rs b/src/tools/clippy/clippy_lints/src/utils/sugg.rs
index 625120b880e..1fcd41e4dbf 100644
--- a/src/tools/clippy/clippy_lints/src/utils/sugg.rs
+++ b/src/tools/clippy/clippy_lints/src/utils/sugg.rs
@@ -170,6 +170,7 @@ impl<'a> Sugg<'a> {
             | ast::ExprKind::MacCall(..)
             | ast::ExprKind::MethodCall(..)
             | ast::ExprKind::Paren(..)
+            | ast::ExprKind::Underscore
             | ast::ExprKind::Path(..)
             | ast::ExprKind::Repeat(..)
             | ast::ExprKind::Ret(..)