diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index 62bacf97f49a7..815f5fa8368ce 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 91591a71611b4..2a519b418e6c7 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 6e1a9eff5008d..1bcf4a07eb094 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 b688950f5a36f..4a32fa7f929cd 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 81733d8f64e2c..479a0db75b028 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 98d2a27884f79..db38fa0d7bc65 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 f162f7dd47b1b..e3e7ff89c8134 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 c33bd0c303192..c3487c2c658ba 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 77fd91766334e..3a06ed050b562 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