Skip to content

Commit

Permalink
Disallow an arm without a body (except for never patterns)
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
Nadrieril committed Dec 3, 2023
1 parent 0bfebc6 commit a2dcb3a
Show file tree
Hide file tree
Showing 9 changed files with 128 additions and 12 deletions.
4 changes: 2 additions & 2 deletions compiler/rustc_ast/src/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
4 changes: 4 additions & 0 deletions compiler/rustc_ast_lowering/messages.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -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}
Expand Down
9 changes: 9 additions & 0 deletions compiler/rustc_ast_lowering/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
13 changes: 10 additions & 3 deletions compiler/rustc_ast_lowering/src/expr.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use super::errors::{
AsyncCoroutinesNotSupported, AsyncNonMoveClosureNotSupported, AwaitOnlyInAsyncFnAndBlocks,
BaseExpressionDoubleDot, ClosureCannotBeStatic, CoroutineTooManyParameters,
FunctionalRecordUpdateDestructuringAssignment, InclusiveRangeWithNoEnd,
FunctionalRecordUpdateDestructuringAssignment, InclusiveRangeWithNoEnd, MatchArmWithNoBody,
NotSupportedForLifetimeBinderAsyncClosure, UnderscoreExprLhsAssign,
};
use super::ResolverAstLoweringExt;
Expand Down Expand Up @@ -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,
Expand All @@ -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`.
Expand Down
17 changes: 17 additions & 0 deletions compiler/rustc_hir/src/hir.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
1 change: 1 addition & 0 deletions tests/ui/parser/macro/macro-expand-to-match-arm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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) => {},
_ => {},
};
Expand Down
8 changes: 7 additions & 1 deletion tests/ui/parser/macro/macro-expand-to-match-arm.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -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

18 changes: 18 additions & 0 deletions tests/ui/parser/match-arm-without-body.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(_)
Expand All @@ -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
Expand All @@ -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
_ => {}
}
}
66 changes: 60 additions & 6 deletions tests/ui/parser/match-arm-without-body.stderr
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
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 `}`
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(_),
| ^
Expand All @@ -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(_),
| ^
Expand All @@ -45,18 +45,72 @@ 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
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

0 comments on commit a2dcb3a

Please sign in to comment.