about summary refs log tree commit diff
diff options
context:
space:
mode:
authorNadrieril <nadrieril+git@gmail.com>2023-11-27 01:53:05 +0100
committerNadrieril <nadrieril+git@gmail.com>2023-12-03 12:25:46 +0100
commita2dcb3a6d9aead3964b3b1cdf814dc7eb9c5d8ed (patch)
tree6dface89197039eb8fddb71508a4522ac0892eca
parent0bfebc6105ea882d7048057718b2e34d09a5d17e (diff)
downloadrust-a2dcb3a6d9aead3964b3b1cdf814dc7eb9c5d8ed.tar.gz
rust-a2dcb3a6d9aead3964b3b1cdf814dc7eb9c5d8ed.zip
Disallow an arm without a body (except for never patterns)
Parsing now accepts a match arm without a body, so we must make sure to
only accept that if the pattern is a never pattern.
-rw-r--r--compiler/rustc_ast/src/ast.rs4
-rw-r--r--compiler/rustc_ast_lowering/messages.ftl4
-rw-r--r--compiler/rustc_ast_lowering/src/errors.rs9
-rw-r--r--compiler/rustc_ast_lowering/src/expr.rs13
-rw-r--r--compiler/rustc_hir/src/hir.rs17
-rw-r--r--tests/ui/parser/macro/macro-expand-to-match-arm.rs1
-rw-r--r--tests/ui/parser/macro/macro-expand-to-match-arm.stderr8
-rw-r--r--tests/ui/parser/match-arm-without-body.rs18
-rw-r--r--tests/ui/parser/match-arm-without-body.stderr66
9 files changed, 128 insertions, 12 deletions
diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs
index 62bacf97f49..815f5fa8368 100644
--- a/compiler/rustc_ast/src/ast.rs
+++ b/compiler/rustc_ast/src/ast.rs
@@ -659,8 +659,8 @@ impl Pat {
         matches!(self.kind, PatKind::Rest)
     }
 
-    /// Could this be a never pattern? I.e. is it a never pattern modulo macro invocations that
-    /// might return never patterns?
+    /// Whether this could be a never pattern, taking into account that a macro invocation can
+    /// return a never pattern. Used to inform errors during parsing.
     pub fn could_be_never_pattern(&self) -> bool {
         let mut could_be_never_pattern = false;
         self.walk(&mut |pat| match &pat.kind {
diff --git a/compiler/rustc_ast_lowering/messages.ftl b/compiler/rustc_ast_lowering/messages.ftl
index 91591a71611..2a519b418e6 100644
--- a/compiler/rustc_ast_lowering/messages.ftl
+++ b/compiler/rustc_ast_lowering/messages.ftl
@@ -91,6 +91,10 @@ ast_lowering_invalid_register =
 ast_lowering_invalid_register_class =
     invalid register class `{$reg_class}`: {$error}
 
+ast_lowering_match_arm_with_no_body =
+    `match` arm with no body
+    .suggestion = add a body after the pattern
+
 ast_lowering_misplaced_assoc_ty_binding =
     associated type bounds are only allowed in where clauses and function signatures, not in {$position}
 
diff --git a/compiler/rustc_ast_lowering/src/errors.rs b/compiler/rustc_ast_lowering/src/errors.rs
index 6e1a9eff500..1bcf4a07eb0 100644
--- a/compiler/rustc_ast_lowering/src/errors.rs
+++ b/compiler/rustc_ast_lowering/src/errors.rs
@@ -340,6 +340,15 @@ pub struct NotSupportedForLifetimeBinderAsyncClosure {
     pub span: Span,
 }
 
+#[derive(Diagnostic)]
+#[diag(ast_lowering_match_arm_with_no_body)]
+pub struct MatchArmWithNoBody {
+    #[primary_span]
+    pub span: Span,
+    #[suggestion(code = " => todo!(),", applicability = "has-placeholders")]
+    pub suggestion: Span,
+}
+
 #[derive(Diagnostic, Clone, Copy)]
 #[diag(ast_lowering_arbitrary_expression_in_pattern)]
 pub struct ArbitraryExpressionInPattern {
diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs
index b688950f5a3..4a32fa7f929 100644
--- a/compiler/rustc_ast_lowering/src/expr.rs
+++ b/compiler/rustc_ast_lowering/src/expr.rs
@@ -1,7 +1,7 @@
 use super::errors::{
     AsyncCoroutinesNotSupported, AsyncNonMoveClosureNotSupported, AwaitOnlyInAsyncFnAndBlocks,
     BaseExpressionDoubleDot, ClosureCannotBeStatic, CoroutineTooManyParameters,
-    FunctionalRecordUpdateDestructuringAssignment, InclusiveRangeWithNoEnd,
+    FunctionalRecordUpdateDestructuringAssignment, InclusiveRangeWithNoEnd, MatchArmWithNoBody,
     NotSupportedForLifetimeBinderAsyncClosure, UnderscoreExprLhsAssign,
 };
 use super::ResolverAstLoweringExt;
@@ -565,14 +565,21 @@ impl<'hir> LoweringContext<'_, 'hir> {
             }
         });
         let hir_id = self.next_id();
+        let span = self.lower_span(arm.span);
         self.lower_attrs(hir_id, &arm.attrs);
         let body = if let Some(body) = &arm.body {
+            // FIXME(never_patterns): Disallow never pattern with a body or guard
             self.lower_expr(body)
         } else {
+            if !pat.is_never_pattern() {
+                self.tcx
+                    .sess
+                    .emit_err(MatchArmWithNoBody { span, suggestion: span.shrink_to_hi() });
+            }
+
             // An arm without a body, meant for never patterns.
             // We add a fake `loop {}` arm body so that it typecks to `!`.
             // FIXME(never_patterns): Desugar into a call to `unreachable_unchecked`.
-            let span = pat.span;
             let block = self.arena.alloc(hir::Block {
                 stmts: &[],
                 expr: None,
@@ -587,7 +594,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
                 span,
             })
         };
-        hir::Arm { hir_id, pat, guard, body, span: self.lower_span(arm.span) }
+        hir::Arm { hir_id, pat, guard, body, span }
     }
 
     /// Lower an `async` construct to a coroutine that implements `Future`.
diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs
index 81733d8f64e..479a0db75b0 100644
--- a/compiler/rustc_hir/src/hir.rs
+++ b/compiler/rustc_hir/src/hir.rs
@@ -1055,6 +1055,23 @@ impl<'hir> Pat<'hir> {
             true
         })
     }
+
+    /// Whether this a never pattern.
+    pub fn is_never_pattern(&self) -> bool {
+        let mut is_never_pattern = false;
+        self.walk(|pat| match &pat.kind {
+            PatKind::Never => {
+                is_never_pattern = true;
+                false
+            }
+            PatKind::Or(s) => {
+                is_never_pattern = s.iter().all(|p| p.is_never_pattern());
+                false
+            }
+            _ => true,
+        });
+        is_never_pattern
+    }
 }
 
 /// A single field in a struct pattern.
diff --git a/tests/ui/parser/macro/macro-expand-to-match-arm.rs b/tests/ui/parser/macro/macro-expand-to-match-arm.rs
index 98d2a27884f..db38fa0d7bc 100644
--- a/tests/ui/parser/macro/macro-expand-to-match-arm.rs
+++ b/tests/ui/parser/macro/macro-expand-to-match-arm.rs
@@ -13,6 +13,7 @@ fn main() {
         Some(1) => {},
         arm!(None => {}),
         //~^ NOTE caused by the macro expansion here
+        //~| ERROR `match` arm with no body
         Some(2) => {},
         _ => {},
     };
diff --git a/tests/ui/parser/macro/macro-expand-to-match-arm.stderr b/tests/ui/parser/macro/macro-expand-to-match-arm.stderr
index f162f7dd47b..e3e7ff89c81 100644
--- a/tests/ui/parser/macro/macro-expand-to-match-arm.stderr
+++ b/tests/ui/parser/macro/macro-expand-to-match-arm.stderr
@@ -10,5 +10,11 @@ LL |         arm!(None => {}),
    = note: the usage of `arm!` is likely invalid in pattern context
    = note: macros cannot expand to match arms
 
-error: aborting due to 1 previous error
+error: `match` arm with no body
+  --> $DIR/macro-expand-to-match-arm.rs:14:9
+   |
+LL |         arm!(None => {}),
+   |         ^^^^^^^^^^^^^^^^- help: add a body after the pattern: `=> todo!(),`
+
+error: aborting due to 2 previous errors
 
diff --git a/tests/ui/parser/match-arm-without-body.rs b/tests/ui/parser/match-arm-without-body.rs
index c33bd0c3031..c3487c2c658 100644
--- a/tests/ui/parser/match-arm-without-body.rs
+++ b/tests/ui/parser/match-arm-without-body.rs
@@ -5,6 +5,8 @@ macro_rules! pat {
 fn main() {
     match Some(false) {
         Some(_)
+        //~^ ERROR `match` arm with no body
+        //~| HELP add a body after the pattern
     }
     match Some(false) {
         Some(_)
@@ -26,6 +28,8 @@ fn main() {
     }
     match Some(false) {
         Some(_) if true
+        //~^ ERROR `match` arm with no body
+        //~| HELP add a body after the pattern
     }
     match Some(false) {
         Some(_) if true
@@ -34,28 +38,42 @@ fn main() {
     }
     match Some(false) {
         Some(_) if true,
+        //~^ ERROR `match` arm with no body
+        //~| HELP add a body after the pattern
     }
     match Some(false) {
         Some(_) if true,
+        //~^ ERROR `match` arm with no body
+        //~| HELP add a body after the pattern
         _ => {}
     }
     match Some(false) {
         pat!()
+        //~^ ERROR `match` arm with no body
+        //~| HELP add a body after the pattern
     }
     match Some(false) {
         pat!(),
+        //~^ ERROR `match` arm with no body
+        //~| HELP add a body after the pattern
     }
     match Some(false) {
         pat!() if true,
+        //~^ ERROR `match` arm with no body
+        //~| HELP add a body after the pattern
     }
     match Some(false) {
         pat!()
         //~^ ERROR expected `,` following `match` arm
         //~| HELP missing a comma here
+        //~| ERROR `match` arm with no body
+        //~| HELP add a body after the pattern
         _ => {}
     }
     match Some(false) {
         pat!(),
+        //~^ ERROR `match` arm with no body
+        //~| HELP add a body after the pattern
         _ => {}
     }
 }
diff --git a/tests/ui/parser/match-arm-without-body.stderr b/tests/ui/parser/match-arm-without-body.stderr
index 77fd9176633..3a06ed050b5 100644
--- a/tests/ui/parser/match-arm-without-body.stderr
+++ b/tests/ui/parser/match-arm-without-body.stderr
@@ -1,5 +1,5 @@
 error: expected one of `,`, `=>`, `if`, `|`, or `}`, found reserved identifier `_`
-  --> $DIR/match-arm-without-body.rs:11:9
+  --> $DIR/match-arm-without-body.rs:13:9
    |
 LL |         Some(_)
    |                - expected one of `,`, `=>`, `if`, `|`, or `}`
@@ -7,7 +7,7 @@ LL |         _ => {}
    |         ^ unexpected token
 
 error: unexpected `,` in pattern
-  --> $DIR/match-arm-without-body.rs:15:16
+  --> $DIR/match-arm-without-body.rs:17:16
    |
 LL |         Some(_),
    |                ^
@@ -22,7 +22,7 @@ LL |         Some(_) |
    |
 
 error: unexpected `,` in pattern
-  --> $DIR/match-arm-without-body.rs:21:16
+  --> $DIR/match-arm-without-body.rs:23:16
    |
 LL |         Some(_),
    |                ^
@@ -45,7 +45,7 @@ LL ~         _ => {}
    |
 
 error: expected one of `,`, `.`, `=>`, `?`, `}`, or an operator, found reserved identifier `_`
-  --> $DIR/match-arm-without-body.rs:32:9
+  --> $DIR/match-arm-without-body.rs:36:9
    |
 LL |         Some(_) if true
    |                        - expected one of `,`, `.`, `=>`, `?`, `}`, or an operator
@@ -53,10 +53,64 @@ LL |         _ => {}
    |         ^ unexpected token
 
 error: expected `,` following `match` arm
-  --> $DIR/match-arm-without-body.rs:52:15
+  --> $DIR/match-arm-without-body.rs:66:15
    |
 LL |         pat!()
    |               ^ help: missing a comma here to end this `match` arm: `,`
 
-error: aborting due to 5 previous errors
+error: `match` arm with no body
+  --> $DIR/match-arm-without-body.rs:7:9
+   |
+LL |         Some(_)
+   |         ^^^^^^^- help: add a body after the pattern: `=> todo!(),`
+
+error: `match` arm with no body
+  --> $DIR/match-arm-without-body.rs:30:9
+   |
+LL |         Some(_) if true
+   |         ^^^^^^^^^^^^^^^- help: add a body after the pattern: `=> todo!(),`
+
+error: `match` arm with no body
+  --> $DIR/match-arm-without-body.rs:40:9
+   |
+LL |         Some(_) if true,
+   |         ^^^^^^^^^^^^^^^- help: add a body after the pattern: `=> todo!(),`
+
+error: `match` arm with no body
+  --> $DIR/match-arm-without-body.rs:45:9
+   |
+LL |         Some(_) if true,
+   |         ^^^^^^^^^^^^^^^- help: add a body after the pattern: `=> todo!(),`
+
+error: `match` arm with no body
+  --> $DIR/match-arm-without-body.rs:51:9
+   |
+LL |         pat!()
+   |         ^^^^^^- help: add a body after the pattern: `=> todo!(),`
+
+error: `match` arm with no body
+  --> $DIR/match-arm-without-body.rs:56:9
+   |
+LL |         pat!(),
+   |         ^^^^^^- help: add a body after the pattern: `=> todo!(),`
+
+error: `match` arm with no body
+  --> $DIR/match-arm-without-body.rs:61:9
+   |
+LL |         pat!() if true,
+   |         ^^^^^^^^^^^^^^- help: add a body after the pattern: `=> todo!(),`
+
+error: `match` arm with no body
+  --> $DIR/match-arm-without-body.rs:66:9
+   |
+LL |         pat!()
+   |         ^^^^^^- help: add a body after the pattern: `=> todo!(),`
+
+error: `match` arm with no body
+  --> $DIR/match-arm-without-body.rs:74:9
+   |
+LL |         pat!(),
+   |         ^^^^^^- help: add a body after the pattern: `=> todo!(),`
+
+error: aborting due to 14 previous errors