mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-21 14:23:45 +00:00
Auto merge of #118527 - Nadrieril:never_patterns_parse, r=compiler-errors
never_patterns: Parse match arms with no body Never patterns are meant to signal unreachable cases, and thus don't take bodies: ```rust let ptr: *const Option<!> = ...; match *ptr { None => { foo(); } Some(!), } ``` This PR makes rustc accept the above, and enforces that an arm has a body xor is a never pattern. This affects parsing of match arms even with the feature off, so this is delicate. (Plus this is my first non-trivial change to the parser). ~~The last commit is optional; it introduces a bit of churn to allow the new suggestions to be machine-applicable. There may be a better solution? I'm not sure.~~ EDIT: I removed that commit r? `@compiler-errors`
This commit is contained in:
commit
2b399b5275
@ -658,6 +658,24 @@ impl Pat {
|
|||||||
pub fn is_rest(&self) -> bool {
|
pub fn is_rest(&self) -> bool {
|
||||||
matches!(self.kind, PatKind::Rest)
|
matches!(self.kind, PatKind::Rest)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 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 {
|
||||||
|
PatKind::Never | PatKind::MacCall(_) => {
|
||||||
|
could_be_never_pattern = true;
|
||||||
|
false
|
||||||
|
}
|
||||||
|
PatKind::Or(s) => {
|
||||||
|
could_be_never_pattern = s.iter().all(|p| p.could_be_never_pattern());
|
||||||
|
false
|
||||||
|
}
|
||||||
|
_ => true,
|
||||||
|
});
|
||||||
|
could_be_never_pattern
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A single field in a struct pattern.
|
/// A single field in a struct pattern.
|
||||||
@ -1080,8 +1098,8 @@ pub struct Arm {
|
|||||||
pub pat: P<Pat>,
|
pub pat: P<Pat>,
|
||||||
/// Match arm guard, e.g. `n > 10` in `match foo { n if n > 10 => {}, _ => {} }`
|
/// Match arm guard, e.g. `n > 10` in `match foo { n if n > 10 => {}, _ => {} }`
|
||||||
pub guard: Option<P<Expr>>,
|
pub guard: Option<P<Expr>>,
|
||||||
/// Match arm body.
|
/// Match arm body. Omitted if the pattern is a never pattern.
|
||||||
pub body: P<Expr>,
|
pub body: Option<P<Expr>>,
|
||||||
pub span: Span,
|
pub span: Span,
|
||||||
pub id: NodeId,
|
pub id: NodeId,
|
||||||
pub is_placeholder: bool,
|
pub is_placeholder: bool,
|
||||||
|
@ -453,7 +453,7 @@ pub fn noop_flat_map_arm<T: MutVisitor>(mut arm: Arm, vis: &mut T) -> SmallVec<[
|
|||||||
vis.visit_id(id);
|
vis.visit_id(id);
|
||||||
vis.visit_pat(pat);
|
vis.visit_pat(pat);
|
||||||
visit_opt(guard, |guard| vis.visit_expr(guard));
|
visit_opt(guard, |guard| vis.visit_expr(guard));
|
||||||
vis.visit_expr(body);
|
visit_opt(body, |body| vis.visit_expr(body));
|
||||||
vis.visit_span(span);
|
vis.visit_span(span);
|
||||||
smallvec![arm]
|
smallvec![arm]
|
||||||
}
|
}
|
||||||
|
@ -951,7 +951,7 @@ pub fn walk_param<'a, V: Visitor<'a>>(visitor: &mut V, param: &'a Param) {
|
|||||||
pub fn walk_arm<'a, V: Visitor<'a>>(visitor: &mut V, arm: &'a Arm) {
|
pub fn walk_arm<'a, V: Visitor<'a>>(visitor: &mut V, arm: &'a Arm) {
|
||||||
visitor.visit_pat(&arm.pat);
|
visitor.visit_pat(&arm.pat);
|
||||||
walk_list!(visitor, visit_expr, &arm.guard);
|
walk_list!(visitor, visit_expr, &arm.guard);
|
||||||
visitor.visit_expr(&arm.body);
|
walk_list!(visitor, visit_expr, &arm.body);
|
||||||
walk_list!(visitor, visit_attribute, &arm.attrs);
|
walk_list!(visitor, visit_attribute, &arm.attrs);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -91,6 +91,10 @@ ast_lowering_invalid_register =
|
|||||||
ast_lowering_invalid_register_class =
|
ast_lowering_invalid_register_class =
|
||||||
invalid register class `{$reg_class}`: {$error}
|
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 =
|
ast_lowering_misplaced_assoc_ty_binding =
|
||||||
associated type bounds are only allowed in where clauses and function signatures, not in {$position}
|
associated type bounds are only allowed in where clauses and function signatures, not in {$position}
|
||||||
|
|
||||||
@ -104,6 +108,15 @@ ast_lowering_misplaced_impl_trait =
|
|||||||
ast_lowering_misplaced_relax_trait_bound =
|
ast_lowering_misplaced_relax_trait_bound =
|
||||||
`?Trait` bounds are only permitted at the point where a type parameter is declared
|
`?Trait` bounds are only permitted at the point where a type parameter is declared
|
||||||
|
|
||||||
|
ast_lowering_never_pattern_with_body =
|
||||||
|
a never pattern is always unreachable
|
||||||
|
.label = this will never be executed
|
||||||
|
.suggestion = remove this expression
|
||||||
|
|
||||||
|
ast_lowering_never_pattern_with_guard =
|
||||||
|
a guard on a never pattern will never be run
|
||||||
|
.suggestion = remove this guard
|
||||||
|
|
||||||
ast_lowering_not_supported_for_lifetime_binder_async_closure =
|
ast_lowering_not_supported_for_lifetime_binder_async_closure =
|
||||||
`for<...>` binders on `async` closures are not currently supported
|
`for<...>` binders on `async` closures are not currently supported
|
||||||
|
|
||||||
|
@ -340,6 +340,32 @@ pub struct NotSupportedForLifetimeBinderAsyncClosure {
|
|||||||
pub span: Span,
|
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)]
|
||||||
|
#[diag(ast_lowering_never_pattern_with_body)]
|
||||||
|
pub struct NeverPatternWithBody {
|
||||||
|
#[primary_span]
|
||||||
|
#[label]
|
||||||
|
#[suggestion(code = "", applicability = "maybe-incorrect")]
|
||||||
|
pub span: Span,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Diagnostic)]
|
||||||
|
#[diag(ast_lowering_never_pattern_with_guard)]
|
||||||
|
pub struct NeverPatternWithGuard {
|
||||||
|
#[primary_span]
|
||||||
|
#[suggestion(code = "", applicability = "maybe-incorrect")]
|
||||||
|
pub span: Span,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Diagnostic, Clone, Copy)]
|
#[derive(Diagnostic, Clone, Copy)]
|
||||||
#[diag(ast_lowering_arbitrary_expression_in_pattern)]
|
#[diag(ast_lowering_arbitrary_expression_in_pattern)]
|
||||||
pub struct ArbitraryExpressionInPattern {
|
pub struct ArbitraryExpressionInPattern {
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
use super::errors::{
|
use super::errors::{
|
||||||
AsyncCoroutinesNotSupported, AsyncNonMoveClosureNotSupported, AwaitOnlyInAsyncFnAndBlocks,
|
AsyncCoroutinesNotSupported, AsyncNonMoveClosureNotSupported, AwaitOnlyInAsyncFnAndBlocks,
|
||||||
BaseExpressionDoubleDot, ClosureCannotBeStatic, CoroutineTooManyParameters,
|
BaseExpressionDoubleDot, ClosureCannotBeStatic, CoroutineTooManyParameters,
|
||||||
FunctionalRecordUpdateDestructuringAssignment, InclusiveRangeWithNoEnd,
|
FunctionalRecordUpdateDestructuringAssignment, InclusiveRangeWithNoEnd, MatchArmWithNoBody,
|
||||||
NotSupportedForLifetimeBinderAsyncClosure, UnderscoreExprLhsAssign,
|
NeverPatternWithBody, NeverPatternWithGuard, NotSupportedForLifetimeBinderAsyncClosure,
|
||||||
|
UnderscoreExprLhsAssign,
|
||||||
};
|
};
|
||||||
use super::ResolverAstLoweringExt;
|
use super::ResolverAstLoweringExt;
|
||||||
use super::{ImplTraitContext, LoweringContext, ParamMode, ParenthesizedGenericArgs};
|
use super::{ImplTraitContext, LoweringContext, ParamMode, ParenthesizedGenericArgs};
|
||||||
@ -549,7 +550,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
|||||||
|
|
||||||
fn lower_arm(&mut self, arm: &Arm) -> hir::Arm<'hir> {
|
fn lower_arm(&mut self, arm: &Arm) -> hir::Arm<'hir> {
|
||||||
let pat = self.lower_pat(&arm.pat);
|
let pat = self.lower_pat(&arm.pat);
|
||||||
let guard = arm.guard.as_ref().map(|cond| {
|
let mut guard = arm.guard.as_ref().map(|cond| {
|
||||||
if let ExprKind::Let(pat, scrutinee, span, is_recovered) = &cond.kind {
|
if let ExprKind::Let(pat, scrutinee, span, is_recovered) = &cond.kind {
|
||||||
hir::Guard::IfLet(self.arena.alloc(hir::Let {
|
hir::Guard::IfLet(self.arena.alloc(hir::Let {
|
||||||
hir_id: self.next_id(),
|
hir_id: self.next_id(),
|
||||||
@ -564,14 +565,43 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
let hir_id = self.next_id();
|
let hir_id = self.next_id();
|
||||||
|
let span = self.lower_span(arm.span);
|
||||||
self.lower_attrs(hir_id, &arm.attrs);
|
self.lower_attrs(hir_id, &arm.attrs);
|
||||||
hir::Arm {
|
let is_never_pattern = pat.is_never_pattern();
|
||||||
hir_id,
|
let body = if let Some(body) = &arm.body
|
||||||
pat,
|
&& !is_never_pattern
|
||||||
guard,
|
{
|
||||||
body: self.lower_expr(&arm.body),
|
self.lower_expr(body)
|
||||||
span: self.lower_span(arm.span),
|
} else {
|
||||||
}
|
// Either `body.is_none()` or `is_never_pattern` here.
|
||||||
|
if !is_never_pattern {
|
||||||
|
let suggestion = span.shrink_to_hi();
|
||||||
|
self.tcx.sess.emit_err(MatchArmWithNoBody { span, suggestion });
|
||||||
|
} else if let Some(body) = &arm.body {
|
||||||
|
self.tcx.sess.emit_err(NeverPatternWithBody { span: body.span });
|
||||||
|
guard = None;
|
||||||
|
} else if let Some(g) = &arm.guard {
|
||||||
|
self.tcx.sess.emit_err(NeverPatternWithGuard { span: g.span });
|
||||||
|
guard = None;
|
||||||
|
}
|
||||||
|
|
||||||
|
// We add a fake `loop {}` arm body so that it typecks to `!`.
|
||||||
|
// FIXME(never_patterns): Desugar into a call to `unreachable_unchecked`.
|
||||||
|
let block = self.arena.alloc(hir::Block {
|
||||||
|
stmts: &[],
|
||||||
|
expr: None,
|
||||||
|
hir_id: self.next_id(),
|
||||||
|
rules: hir::BlockCheckMode::DefaultBlock,
|
||||||
|
span,
|
||||||
|
targeted_by_break: false,
|
||||||
|
});
|
||||||
|
self.arena.alloc(hir::Expr {
|
||||||
|
hir_id: self.next_id(),
|
||||||
|
kind: hir::ExprKind::Loop(block, None, hir::LoopSource::Loop, span),
|
||||||
|
span,
|
||||||
|
})
|
||||||
|
};
|
||||||
|
hir::Arm { hir_id, pat, guard, body, span }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Lower an `async` construct to a coroutine that implements `Future`.
|
/// Lower an `async` construct to a coroutine that implements `Future`.
|
||||||
|
@ -631,28 +631,33 @@ impl<'a> State<'a> {
|
|||||||
self.print_expr(e);
|
self.print_expr(e);
|
||||||
self.space();
|
self.space();
|
||||||
}
|
}
|
||||||
self.word_space("=>");
|
|
||||||
|
|
||||||
match &arm.body.kind {
|
if let Some(body) = &arm.body {
|
||||||
ast::ExprKind::Block(blk, opt_label) => {
|
self.word_space("=>");
|
||||||
if let Some(label) = opt_label {
|
|
||||||
self.print_ident(label.ident);
|
match &body.kind {
|
||||||
self.word_space(":");
|
ast::ExprKind::Block(blk, opt_label) => {
|
||||||
|
if let Some(label) = opt_label {
|
||||||
|
self.print_ident(label.ident);
|
||||||
|
self.word_space(":");
|
||||||
|
}
|
||||||
|
|
||||||
|
// The block will close the pattern's ibox.
|
||||||
|
self.print_block_unclosed_indent(blk);
|
||||||
|
|
||||||
|
// If it is a user-provided unsafe block, print a comma after it.
|
||||||
|
if let BlockCheckMode::Unsafe(ast::UserProvided) = blk.rules {
|
||||||
|
self.word(",");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
_ => {
|
||||||
// The block will close the pattern's ibox.
|
self.end(); // Close the ibox for the pattern.
|
||||||
self.print_block_unclosed_indent(blk);
|
self.print_expr(body);
|
||||||
|
|
||||||
// If it is a user-provided unsafe block, print a comma after it.
|
|
||||||
if let BlockCheckMode::Unsafe(ast::UserProvided) = blk.rules {
|
|
||||||
self.word(",");
|
self.word(",");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => {
|
} else {
|
||||||
self.end(); // Close the ibox for the pattern.
|
self.word(",");
|
||||||
self.print_expr(&arm.body);
|
|
||||||
self.word(",");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
self.end(); // Close enclosing cbox.
|
self.end(); // Close enclosing cbox.
|
||||||
}
|
}
|
||||||
|
@ -136,7 +136,7 @@ fn cs_partial_cmp(
|
|||||||
&& let Some(last) = arms.last_mut()
|
&& let Some(last) = arms.last_mut()
|
||||||
&& let PatKind::Wild = last.pat.kind
|
&& let PatKind::Wild = last.pat.kind
|
||||||
{
|
{
|
||||||
last.body = expr2;
|
last.body = Some(expr2);
|
||||||
expr1
|
expr1
|
||||||
} else {
|
} else {
|
||||||
let eq_arm = cx.arm(
|
let eq_arm = cx.arm(
|
||||||
|
@ -71,6 +71,8 @@ expand_macro_const_stability =
|
|||||||
.label = invalid const stability attribute
|
.label = invalid const stability attribute
|
||||||
.label2 = const stability attribute affects this macro
|
.label2 = const stability attribute affects this macro
|
||||||
|
|
||||||
|
expand_macro_expands_to_match_arm = macros cannot expand to match arms
|
||||||
|
|
||||||
expand_malformed_feature_attribute =
|
expand_malformed_feature_attribute =
|
||||||
malformed `feature` attribute input
|
malformed `feature` attribute input
|
||||||
.expected = expected just one word
|
.expected = expected just one word
|
||||||
|
@ -505,7 +505,7 @@ impl<'a> ExtCtxt<'a> {
|
|||||||
attrs: AttrVec::new(),
|
attrs: AttrVec::new(),
|
||||||
pat,
|
pat,
|
||||||
guard: None,
|
guard: None,
|
||||||
body: expr,
|
body: Some(expr),
|
||||||
span,
|
span,
|
||||||
id: ast::DUMMY_NODE_ID,
|
id: ast::DUMMY_NODE_ID,
|
||||||
is_placeholder: false,
|
is_placeholder: false,
|
||||||
|
@ -304,6 +304,8 @@ pub(crate) struct IncompleteParse<'a> {
|
|||||||
pub label_span: Span,
|
pub label_span: Span,
|
||||||
pub macro_path: &'a ast::Path,
|
pub macro_path: &'a ast::Path,
|
||||||
pub kind_name: &'a str,
|
pub kind_name: &'a str,
|
||||||
|
#[note(expand_macro_expands_to_match_arm)]
|
||||||
|
pub expands_to_match_arm: Option<()>,
|
||||||
|
|
||||||
#[suggestion(
|
#[suggestion(
|
||||||
expand_suggestion_add_semi,
|
expand_suggestion_add_semi,
|
||||||
|
@ -955,12 +955,15 @@ pub fn ensure_complete_parse<'a>(
|
|||||||
_ => None,
|
_ => None,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let expands_to_match_arm = kind_name == "pattern" && parser.token == token::FatArrow;
|
||||||
|
|
||||||
parser.sess.emit_err(IncompleteParse {
|
parser.sess.emit_err(IncompleteParse {
|
||||||
span: def_site_span,
|
span: def_site_span,
|
||||||
token,
|
token,
|
||||||
label_span: span,
|
label_span: span,
|
||||||
macro_path,
|
macro_path,
|
||||||
kind_name,
|
kind_name,
|
||||||
|
expands_to_match_arm: expands_to_match_arm.then_some(()),
|
||||||
add_semicolon,
|
add_semicolon,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -119,7 +119,7 @@ pub fn placeholder(
|
|||||||
}]),
|
}]),
|
||||||
AstFragmentKind::Arms => AstFragment::Arms(smallvec![ast::Arm {
|
AstFragmentKind::Arms => AstFragment::Arms(smallvec![ast::Arm {
|
||||||
attrs: Default::default(),
|
attrs: Default::default(),
|
||||||
body: expr_placeholder(),
|
body: Some(expr_placeholder()),
|
||||||
guard: None,
|
guard: None,
|
||||||
id,
|
id,
|
||||||
pat: pat(),
|
pat: pat(),
|
||||||
|
@ -1056,6 +1056,23 @@ impl<'hir> Pat<'hir> {
|
|||||||
true
|
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.
|
/// A single field in a struct pattern.
|
||||||
|
@ -1000,8 +1000,10 @@ impl EarlyLintPass for UnusedDocComment {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn check_arm(&mut self, cx: &EarlyContext<'_>, arm: &ast::Arm) {
|
fn check_arm(&mut self, cx: &EarlyContext<'_>, arm: &ast::Arm) {
|
||||||
let arm_span = arm.pat.span.with_hi(arm.body.span.hi());
|
if let Some(body) = &arm.body {
|
||||||
warn_if_doc(cx, arm_span, "match arms", &arm.attrs);
|
let arm_span = arm.pat.span.with_hi(body.span.hi());
|
||||||
|
warn_if_doc(cx, arm_span, "match arms", &arm.attrs);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn check_pat(&mut self, cx: &EarlyContext<'_>, pat: &ast::Pat) {
|
fn check_pat(&mut self, cx: &EarlyContext<'_>, pat: &ast::Pat) {
|
||||||
|
@ -1113,15 +1113,17 @@ impl EarlyLintPass for UnusedParens {
|
|||||||
}
|
}
|
||||||
ExprKind::Match(ref _expr, ref arm) => {
|
ExprKind::Match(ref _expr, ref arm) => {
|
||||||
for a in arm {
|
for a in arm {
|
||||||
self.check_unused_delims_expr(
|
if let Some(body) = &a.body {
|
||||||
cx,
|
self.check_unused_delims_expr(
|
||||||
&a.body,
|
cx,
|
||||||
UnusedDelimsCtx::MatchArmExpr,
|
body,
|
||||||
false,
|
UnusedDelimsCtx::MatchArmExpr,
|
||||||
None,
|
false,
|
||||||
None,
|
None,
|
||||||
true,
|
None,
|
||||||
);
|
true,
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
|
@ -458,8 +458,6 @@ parse_macro_expands_to_adt_field = macros cannot expand to {$adt_ty} fields
|
|||||||
|
|
||||||
parse_macro_expands_to_enum_variant = macros cannot expand to enum variants
|
parse_macro_expands_to_enum_variant = macros cannot expand to enum variants
|
||||||
|
|
||||||
parse_macro_expands_to_match_arm = macros cannot expand to match arms
|
|
||||||
|
|
||||||
parse_macro_invocation_visibility = can't qualify macro invocation with `pub`
|
parse_macro_invocation_visibility = can't qualify macro invocation with `pub`
|
||||||
.suggestion = remove the visibility
|
.suggestion = remove the visibility
|
||||||
.help = try adjusting the macro to put `{$vis}` inside the invocation
|
.help = try adjusting the macro to put `{$vis}` inside the invocation
|
||||||
|
@ -2839,7 +2839,6 @@ impl<'a> Parser<'a> {
|
|||||||
pub(crate) fn maybe_recover_unexpected_comma(
|
pub(crate) fn maybe_recover_unexpected_comma(
|
||||||
&mut self,
|
&mut self,
|
||||||
lo: Span,
|
lo: Span,
|
||||||
is_mac_invoc: bool,
|
|
||||||
rt: CommaRecoveryMode,
|
rt: CommaRecoveryMode,
|
||||||
) -> PResult<'a, ()> {
|
) -> PResult<'a, ()> {
|
||||||
if self.token != token::Comma {
|
if self.token != token::Comma {
|
||||||
@ -2860,28 +2859,24 @@ impl<'a> Parser<'a> {
|
|||||||
let seq_span = lo.to(self.prev_token.span);
|
let seq_span = lo.to(self.prev_token.span);
|
||||||
let mut err = self.struct_span_err(comma_span, "unexpected `,` in pattern");
|
let mut err = self.struct_span_err(comma_span, "unexpected `,` in pattern");
|
||||||
if let Ok(seq_snippet) = self.span_to_snippet(seq_span) {
|
if let Ok(seq_snippet) = self.span_to_snippet(seq_span) {
|
||||||
if is_mac_invoc {
|
err.multipart_suggestion(
|
||||||
err.note(fluent::parse_macro_expands_to_match_arm);
|
format!(
|
||||||
} else {
|
"try adding parentheses to match on a tuple{}",
|
||||||
err.multipart_suggestion(
|
if let CommaRecoveryMode::LikelyTuple = rt { "" } else { "..." },
|
||||||
format!(
|
),
|
||||||
"try adding parentheses to match on a tuple{}",
|
vec![
|
||||||
if let CommaRecoveryMode::LikelyTuple = rt { "" } else { "..." },
|
(seq_span.shrink_to_lo(), "(".to_string()),
|
||||||
),
|
(seq_span.shrink_to_hi(), ")".to_string()),
|
||||||
vec![
|
],
|
||||||
(seq_span.shrink_to_lo(), "(".to_string()),
|
Applicability::MachineApplicable,
|
||||||
(seq_span.shrink_to_hi(), ")".to_string()),
|
);
|
||||||
],
|
if let CommaRecoveryMode::EitherTupleOrPipe = rt {
|
||||||
|
err.span_suggestion(
|
||||||
|
seq_span,
|
||||||
|
"...or a vertical bar to match on multiple alternatives",
|
||||||
|
seq_snippet.replace(',', " |"),
|
||||||
Applicability::MachineApplicable,
|
Applicability::MachineApplicable,
|
||||||
);
|
);
|
||||||
if let CommaRecoveryMode::EitherTupleOrPipe = rt {
|
|
||||||
err.span_suggestion(
|
|
||||||
seq_span,
|
|
||||||
"...or a vertical bar to match on multiple alternatives",
|
|
||||||
seq_snippet.replace(',', " |"),
|
|
||||||
Applicability::MachineApplicable,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(err)
|
Err(err)
|
||||||
|
@ -2899,127 +2899,155 @@ impl<'a> Parser<'a> {
|
|||||||
self.collect_tokens_trailing_token(attrs, ForceCollect::No, |this, attrs| {
|
self.collect_tokens_trailing_token(attrs, ForceCollect::No, |this, attrs| {
|
||||||
let lo = this.token.span;
|
let lo = this.token.span;
|
||||||
let (pat, guard) = this.parse_match_arm_pat_and_guard()?;
|
let (pat, guard) = this.parse_match_arm_pat_and_guard()?;
|
||||||
let arrow_span = this.token.span;
|
|
||||||
if let Err(mut err) = this.expect(&token::FatArrow) {
|
|
||||||
// We might have a `=>` -> `=` or `->` typo (issue #89396).
|
|
||||||
if TokenKind::FatArrow
|
|
||||||
.similar_tokens()
|
|
||||||
.is_some_and(|similar_tokens| similar_tokens.contains(&this.token.kind))
|
|
||||||
{
|
|
||||||
err.span_suggestion(
|
|
||||||
this.token.span,
|
|
||||||
"use a fat arrow to start a match arm",
|
|
||||||
"=>",
|
|
||||||
Applicability::MachineApplicable,
|
|
||||||
);
|
|
||||||
if matches!(
|
|
||||||
(&this.prev_token.kind, &this.token.kind),
|
|
||||||
(token::DotDotEq, token::Gt)
|
|
||||||
) {
|
|
||||||
// `error_inclusive_range_match_arrow` handles cases like `0..=> {}`,
|
|
||||||
// so we suppress the error here
|
|
||||||
err.delay_as_bug();
|
|
||||||
} else {
|
|
||||||
err.emit();
|
|
||||||
}
|
|
||||||
this.bump();
|
|
||||||
} else {
|
|
||||||
return Err(err);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let arm_start_span = this.token.span;
|
|
||||||
|
|
||||||
let expr = this.parse_expr_res(Restrictions::STMT_EXPR, None).map_err(|mut err| {
|
let span_before_body = this.prev_token.span;
|
||||||
err.span_label(arrow_span, "while parsing the `match` arm starting here");
|
let arm_body;
|
||||||
err
|
let is_fat_arrow = this.check(&token::FatArrow);
|
||||||
})?;
|
let is_almost_fat_arrow = TokenKind::FatArrow
|
||||||
|
.similar_tokens()
|
||||||
let require_comma = classify::expr_requires_semi_to_be_stmt(&expr)
|
.is_some_and(|similar_tokens| similar_tokens.contains(&this.token.kind));
|
||||||
&& this.token != token::CloseDelim(Delimiter::Brace);
|
let mut result = if !is_fat_arrow && !is_almost_fat_arrow {
|
||||||
|
// A pattern without a body, allowed for never patterns.
|
||||||
let hi = this.prev_token.span;
|
arm_body = None;
|
||||||
|
|
||||||
if require_comma {
|
|
||||||
let sm = this.sess.source_map();
|
|
||||||
if let Some(body) = this.parse_arm_body_missing_braces(&expr, arrow_span) {
|
|
||||||
let span = body.span;
|
|
||||||
return Ok((
|
|
||||||
ast::Arm {
|
|
||||||
attrs,
|
|
||||||
pat,
|
|
||||||
guard,
|
|
||||||
body,
|
|
||||||
span,
|
|
||||||
id: DUMMY_NODE_ID,
|
|
||||||
is_placeholder: false,
|
|
||||||
},
|
|
||||||
TrailingToken::None,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
this.expect_one_of(&[token::Comma], &[token::CloseDelim(Delimiter::Brace)])
|
this.expect_one_of(&[token::Comma], &[token::CloseDelim(Delimiter::Brace)])
|
||||||
.or_else(|mut err| {
|
|
||||||
if this.token == token::FatArrow {
|
|
||||||
if let Ok(expr_lines) = sm.span_to_lines(expr.span)
|
|
||||||
&& let Ok(arm_start_lines) = sm.span_to_lines(arm_start_span)
|
|
||||||
&& arm_start_lines.lines[0].end_col == expr_lines.lines[0].end_col
|
|
||||||
&& expr_lines.lines.len() == 2
|
|
||||||
{
|
|
||||||
// We check whether there's any trailing code in the parse span,
|
|
||||||
// if there isn't, we very likely have the following:
|
|
||||||
//
|
|
||||||
// X | &Y => "y"
|
|
||||||
// | -- - missing comma
|
|
||||||
// | |
|
|
||||||
// | arrow_span
|
|
||||||
// X | &X => "x"
|
|
||||||
// | - ^^ self.token.span
|
|
||||||
// | |
|
|
||||||
// | parsed until here as `"y" & X`
|
|
||||||
err.span_suggestion_short(
|
|
||||||
arm_start_span.shrink_to_hi(),
|
|
||||||
"missing a comma here to end this `match` arm",
|
|
||||||
",",
|
|
||||||
Applicability::MachineApplicable,
|
|
||||||
);
|
|
||||||
return Err(err);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// FIXME(compiler-errors): We could also recover `; PAT =>` here
|
|
||||||
|
|
||||||
// Try to parse a following `PAT =>`, if successful
|
|
||||||
// then we should recover.
|
|
||||||
let mut snapshot = this.create_snapshot_for_diagnostic();
|
|
||||||
let pattern_follows = snapshot
|
|
||||||
.parse_pat_allow_top_alt(
|
|
||||||
None,
|
|
||||||
RecoverComma::Yes,
|
|
||||||
RecoverColon::Yes,
|
|
||||||
CommaRecoveryMode::EitherTupleOrPipe,
|
|
||||||
)
|
|
||||||
.map_err(|err| err.cancel())
|
|
||||||
.is_ok();
|
|
||||||
if pattern_follows && snapshot.check(&TokenKind::FatArrow) {
|
|
||||||
err.cancel();
|
|
||||||
this.sess.emit_err(errors::MissingCommaAfterMatchArm {
|
|
||||||
span: hi.shrink_to_hi(),
|
|
||||||
});
|
|
||||||
return Ok(true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
err.span_label(arrow_span, "while parsing the `match` arm starting here");
|
|
||||||
Err(err)
|
|
||||||
})?;
|
|
||||||
} else {
|
} else {
|
||||||
this.eat(&token::Comma);
|
if let Err(mut err) = this.expect(&token::FatArrow) {
|
||||||
|
// We might have a `=>` -> `=` or `->` typo (issue #89396).
|
||||||
|
if is_almost_fat_arrow {
|
||||||
|
err.span_suggestion(
|
||||||
|
this.token.span,
|
||||||
|
"use a fat arrow to start a match arm",
|
||||||
|
"=>",
|
||||||
|
Applicability::MachineApplicable,
|
||||||
|
);
|
||||||
|
if matches!(
|
||||||
|
(&this.prev_token.kind, &this.token.kind),
|
||||||
|
(token::DotDotEq, token::Gt)
|
||||||
|
) {
|
||||||
|
// `error_inclusive_range_match_arrow` handles cases like `0..=> {}`,
|
||||||
|
// so we suppress the error here
|
||||||
|
err.delay_as_bug();
|
||||||
|
} else {
|
||||||
|
err.emit();
|
||||||
|
}
|
||||||
|
this.bump();
|
||||||
|
} else {
|
||||||
|
return Err(err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let arrow_span = this.prev_token.span;
|
||||||
|
let arm_start_span = this.token.span;
|
||||||
|
|
||||||
|
let expr =
|
||||||
|
this.parse_expr_res(Restrictions::STMT_EXPR, None).map_err(|mut err| {
|
||||||
|
err.span_label(arrow_span, "while parsing the `match` arm starting here");
|
||||||
|
err
|
||||||
|
})?;
|
||||||
|
|
||||||
|
let require_comma = classify::expr_requires_semi_to_be_stmt(&expr)
|
||||||
|
&& this.token != token::CloseDelim(Delimiter::Brace);
|
||||||
|
|
||||||
|
if !require_comma {
|
||||||
|
arm_body = Some(expr);
|
||||||
|
this.eat(&token::Comma);
|
||||||
|
Ok(false)
|
||||||
|
} else if let Some(body) = this.parse_arm_body_missing_braces(&expr, arrow_span) {
|
||||||
|
arm_body = Some(body);
|
||||||
|
Ok(true)
|
||||||
|
} else {
|
||||||
|
let expr_span = expr.span;
|
||||||
|
arm_body = Some(expr);
|
||||||
|
this.expect_one_of(&[token::Comma], &[token::CloseDelim(Delimiter::Brace)])
|
||||||
|
.map_err(|mut err| {
|
||||||
|
if this.token == token::FatArrow {
|
||||||
|
let sm = this.sess.source_map();
|
||||||
|
if let Ok(expr_lines) = sm.span_to_lines(expr_span)
|
||||||
|
&& let Ok(arm_start_lines) = sm.span_to_lines(arm_start_span)
|
||||||
|
&& arm_start_lines.lines[0].end_col
|
||||||
|
== expr_lines.lines[0].end_col
|
||||||
|
&& expr_lines.lines.len() == 2
|
||||||
|
{
|
||||||
|
// We check whether there's any trailing code in the parse span,
|
||||||
|
// if there isn't, we very likely have the following:
|
||||||
|
//
|
||||||
|
// X | &Y => "y"
|
||||||
|
// | -- - missing comma
|
||||||
|
// | |
|
||||||
|
// | arrow_span
|
||||||
|
// X | &X => "x"
|
||||||
|
// | - ^^ self.token.span
|
||||||
|
// | |
|
||||||
|
// | parsed until here as `"y" & X`
|
||||||
|
err.span_suggestion_short(
|
||||||
|
arm_start_span.shrink_to_hi(),
|
||||||
|
"missing a comma here to end this `match` arm",
|
||||||
|
",",
|
||||||
|
Applicability::MachineApplicable,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
err.span_label(
|
||||||
|
arrow_span,
|
||||||
|
"while parsing the `match` arm starting here",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
err
|
||||||
|
})
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let hi_span = arm_body.as_ref().map_or(span_before_body, |body| body.span);
|
||||||
|
let arm_span = lo.to(hi_span);
|
||||||
|
|
||||||
|
// We want to recover:
|
||||||
|
// X | Some(_) => foo()
|
||||||
|
// | - missing comma
|
||||||
|
// X | None => "x"
|
||||||
|
// | ^^^^ self.token.span
|
||||||
|
// as well as:
|
||||||
|
// X | Some(!)
|
||||||
|
// | - missing comma
|
||||||
|
// X | None => "x"
|
||||||
|
// | ^^^^ self.token.span
|
||||||
|
// But we musn't recover
|
||||||
|
// X | pat[0] => {}
|
||||||
|
// | ^ self.token.span
|
||||||
|
let recover_missing_comma = arm_body.is_some() || pat.could_be_never_pattern();
|
||||||
|
if recover_missing_comma {
|
||||||
|
result = result.or_else(|err| {
|
||||||
|
// FIXME(compiler-errors): We could also recover `; PAT =>` here
|
||||||
|
|
||||||
|
// Try to parse a following `PAT =>`, if successful
|
||||||
|
// then we should recover.
|
||||||
|
let mut snapshot = this.create_snapshot_for_diagnostic();
|
||||||
|
let pattern_follows = snapshot
|
||||||
|
.parse_pat_allow_top_alt(
|
||||||
|
None,
|
||||||
|
RecoverComma::Yes,
|
||||||
|
RecoverColon::Yes,
|
||||||
|
CommaRecoveryMode::EitherTupleOrPipe,
|
||||||
|
)
|
||||||
|
.map_err(|err| err.cancel())
|
||||||
|
.is_ok();
|
||||||
|
if pattern_follows && snapshot.check(&TokenKind::FatArrow) {
|
||||||
|
err.cancel();
|
||||||
|
this.sess.emit_err(errors::MissingCommaAfterMatchArm {
|
||||||
|
span: arm_span.shrink_to_hi(),
|
||||||
|
});
|
||||||
|
return Ok(true);
|
||||||
|
}
|
||||||
|
Err(err)
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
result?;
|
||||||
|
|
||||||
Ok((
|
Ok((
|
||||||
ast::Arm {
|
ast::Arm {
|
||||||
attrs,
|
attrs,
|
||||||
pat,
|
pat,
|
||||||
guard,
|
guard,
|
||||||
body: expr,
|
body: arm_body,
|
||||||
span: lo.to(hi),
|
span: arm_span,
|
||||||
id: DUMMY_NODE_ID,
|
id: DUMMY_NODE_ID,
|
||||||
is_placeholder: false,
|
is_placeholder: false,
|
||||||
},
|
},
|
||||||
|
@ -154,12 +154,8 @@ impl<'a> Parser<'a> {
|
|||||||
}
|
}
|
||||||
Err(err) => return Err(err),
|
Err(err) => return Err(err),
|
||||||
};
|
};
|
||||||
if rc == RecoverComma::Yes {
|
if rc == RecoverComma::Yes && !first_pat.could_be_never_pattern() {
|
||||||
self.maybe_recover_unexpected_comma(
|
self.maybe_recover_unexpected_comma(first_pat.span, rt)?;
|
||||||
first_pat.span,
|
|
||||||
matches!(first_pat.kind, PatKind::MacCall(_)),
|
|
||||||
rt,
|
|
||||||
)?;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the next token is not a `|`,
|
// If the next token is not a `|`,
|
||||||
@ -200,8 +196,8 @@ impl<'a> Parser<'a> {
|
|||||||
err.span_label(lo, WHILE_PARSING_OR_MSG);
|
err.span_label(lo, WHILE_PARSING_OR_MSG);
|
||||||
err
|
err
|
||||||
})?;
|
})?;
|
||||||
if rc == RecoverComma::Yes {
|
if rc == RecoverComma::Yes && !pat.could_be_never_pattern() {
|
||||||
self.maybe_recover_unexpected_comma(pat.span, false, rt)?;
|
self.maybe_recover_unexpected_comma(pat.span, rt)?;
|
||||||
}
|
}
|
||||||
pats.push(pat);
|
pats.push(pat);
|
||||||
}
|
}
|
||||||
|
@ -3297,7 +3297,7 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> {
|
|||||||
self.with_rib(ValueNS, RibKind::Normal, |this| {
|
self.with_rib(ValueNS, RibKind::Normal, |this| {
|
||||||
this.resolve_pattern_top(&arm.pat, PatternSource::Match);
|
this.resolve_pattern_top(&arm.pat, PatternSource::Match);
|
||||||
walk_list!(this, visit_expr, &arm.guard);
|
walk_list!(this, visit_expr, &arm.guard);
|
||||||
this.visit_expr(&arm.body);
|
walk_list!(this, visit_expr, &arm.body);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -341,7 +341,9 @@ impl<'a, 'tcx> Visitor<'tcx> for SimilarNamesLocalVisitor<'a, 'tcx> {
|
|||||||
|
|
||||||
self.apply(|this| {
|
self.apply(|this| {
|
||||||
SimilarNamesNameVisitor(this).visit_pat(&arm.pat);
|
SimilarNamesNameVisitor(this).visit_pat(&arm.pat);
|
||||||
this.apply(|this| walk_expr(this, &arm.body));
|
if let Some(body) = &arm.body {
|
||||||
|
this.apply(|this| walk_expr(this, body));
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
self.check_single_char_names();
|
self.check_single_char_names();
|
||||||
|
@ -105,7 +105,9 @@ impl<'ast> Visitor<'ast> for BreakVisitor {
|
|||||||
fn visit_expr(&mut self, expr: &'ast Expr) {
|
fn visit_expr(&mut self, expr: &'ast Expr) {
|
||||||
self.is_break = match expr.kind {
|
self.is_break = match expr.kind {
|
||||||
ExprKind::Break(..) | ExprKind::Continue(..) | ExprKind::Ret(..) => true,
|
ExprKind::Break(..) | ExprKind::Continue(..) | ExprKind::Ret(..) => true,
|
||||||
ExprKind::Match(_, ref arms) => arms.iter().all(|arm| self.check_expr(&arm.body)),
|
ExprKind::Match(_, ref arms) => arms.iter().all(|arm|
|
||||||
|
arm.body.is_none() || arm.body.as_deref().is_some_and(|body| self.check_expr(body))
|
||||||
|
),
|
||||||
ExprKind::If(_, ref then, Some(ref els)) => self.check_block(then) && self.check_expr(els),
|
ExprKind::If(_, ref then, Some(ref els)) => self.check_block(then) && self.check_expr(els),
|
||||||
ExprKind::If(_, _, None)
|
ExprKind::If(_, _, None)
|
||||||
// ignore loops for simplicity
|
// ignore loops for simplicity
|
||||||
|
@ -236,7 +236,7 @@ pub fn eq_field(l: &ExprField, r: &ExprField) -> bool {
|
|||||||
pub fn eq_arm(l: &Arm, r: &Arm) -> bool {
|
pub fn eq_arm(l: &Arm, r: &Arm) -> bool {
|
||||||
l.is_placeholder == r.is_placeholder
|
l.is_placeholder == r.is_placeholder
|
||||||
&& eq_pat(&l.pat, &r.pat)
|
&& eq_pat(&l.pat, &r.pat)
|
||||||
&& eq_expr(&l.body, &r.body)
|
&& eq_expr_opt(&l.body, &r.body)
|
||||||
&& eq_expr_opt(&l.guard, &r.guard)
|
&& eq_expr_opt(&l.guard, &r.guard)
|
||||||
&& over(&l.attrs, &r.attrs, eq_attr)
|
&& over(&l.attrs, &r.attrs, eq_attr)
|
||||||
}
|
}
|
||||||
|
@ -223,7 +223,7 @@ fn rewrite_match_arm(
|
|||||||
) -> Option<String> {
|
) -> Option<String> {
|
||||||
let (missing_span, attrs_str) = if !arm.attrs.is_empty() {
|
let (missing_span, attrs_str) = if !arm.attrs.is_empty() {
|
||||||
if contains_skip(&arm.attrs) {
|
if contains_skip(&arm.attrs) {
|
||||||
let (_, body) = flatten_arm_body(context, &arm.body, None);
|
let (_, body) = flatten_arm_body(context, arm.body.as_deref()?, None);
|
||||||
// `arm.span()` does not include trailing comma, add it manually.
|
// `arm.span()` does not include trailing comma, add it manually.
|
||||||
return Some(format!(
|
return Some(format!(
|
||||||
"{}{}",
|
"{}{}",
|
||||||
@ -246,7 +246,7 @@ fn rewrite_match_arm(
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Patterns
|
// Patterns
|
||||||
let pat_shape = match &arm.body.kind {
|
let pat_shape = match &arm.body.as_ref()?.kind {
|
||||||
ast::ExprKind::Block(_, Some(label)) => {
|
ast::ExprKind::Block(_, Some(label)) => {
|
||||||
// Some block with a label ` => 'label: {`
|
// Some block with a label ` => 'label: {`
|
||||||
// 7 = ` => : {`
|
// 7 = ` => : {`
|
||||||
@ -280,10 +280,10 @@ fn rewrite_match_arm(
|
|||||||
false,
|
false,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
let arrow_span = mk_sp(arm.pat.span.hi(), arm.body.span().lo());
|
let arrow_span = mk_sp(arm.pat.span.hi(), arm.body.as_ref()?.span().lo());
|
||||||
rewrite_match_body(
|
rewrite_match_body(
|
||||||
context,
|
context,
|
||||||
&arm.body,
|
arm.body.as_ref()?,
|
||||||
&lhs_str,
|
&lhs_str,
|
||||||
shape,
|
shape,
|
||||||
guard_str.contains('\n'),
|
guard_str.contains('\n'),
|
||||||
|
@ -97,7 +97,12 @@ impl Spanned for ast::Arm {
|
|||||||
} else {
|
} else {
|
||||||
self.attrs[0].span.lo()
|
self.attrs[0].span.lo()
|
||||||
};
|
};
|
||||||
span_with_attrs_lo_hi!(self, lo, self.body.span.hi())
|
let hi = if let Some(body) = &self.body {
|
||||||
|
body.span.hi()
|
||||||
|
} else {
|
||||||
|
self.pat.span.hi()
|
||||||
|
};
|
||||||
|
span_with_attrs_lo_hi!(self, lo, hi)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -12,7 +12,7 @@ fn main() {
|
|||||||
unsafe {
|
unsafe {
|
||||||
let ptr: *const Void = NonNull::dangling().as_ptr();
|
let ptr: *const Void = NonNull::dangling().as_ptr();
|
||||||
match *ptr {
|
match *ptr {
|
||||||
! => {} //~ ERROR `!` patterns are experimental
|
! //~ ERROR `!` patterns are experimental
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -18,7 +18,7 @@ LL | let (Ok(_x) | Err(&!)) = res.as_ref();
|
|||||||
error[E0658]: `!` patterns are experimental
|
error[E0658]: `!` patterns are experimental
|
||||||
--> $DIR/feature-gate-never_patterns.rs:15:13
|
--> $DIR/feature-gate-never_patterns.rs:15:13
|
||||||
|
|
|
|
||||||
LL | ! => {}
|
LL | !
|
||||||
| ^
|
| ^
|
||||||
|
|
|
|
||||||
= note: see issue #118155 <https://github.com/rust-lang/rust/issues/118155> for more information
|
= note: see issue #118155 <https://github.com/rust-lang/rust/issues/118155> for more information
|
||||||
|
@ -17,7 +17,7 @@ fn main() {
|
|||||||
}
|
}
|
||||||
match x as i32 {
|
match x as i32 {
|
||||||
0..5+1 => errors_only.push(x),
|
0..5+1 => errors_only.push(x),
|
||||||
//~^ error: expected one of `=>`, `if`, or `|`, found `+`
|
//~^ error: expected one of `,`, `=>`, `if`, `|`, or `}`, found `+`
|
||||||
1 | -3..0 => first_or.push(x),
|
1 | -3..0 => first_or.push(x),
|
||||||
y @ (0..5 | 6) => or_two.push(y),
|
y @ (0..5 | 6) => or_two.push(y),
|
||||||
y @ 0..const { 5 + 1 } => assert_eq!(y, 5),
|
y @ 0..const { 5 + 1 } => assert_eq!(y, 5),
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
error: expected one of `=>`, `if`, or `|`, found `+`
|
error: expected one of `,`, `=>`, `if`, `|`, or `}`, found `+`
|
||||||
--> $DIR/range_pat_interactions1.rs:19:17
|
--> $DIR/range_pat_interactions1.rs:19:17
|
||||||
|
|
|
|
||||||
LL | 0..5+1 => errors_only.push(x),
|
LL | 0..5+1 => errors_only.push(x),
|
||||||
| ^ expected one of `=>`, `if`, or `|`
|
| ^ expected one of `,`, `=>`, `if`, `|`, or `}`
|
||||||
|
|
||||||
error[E0408]: variable `n` is not bound in all patterns
|
error[E0408]: variable `n` is not bound in all patterns
|
||||||
--> $DIR/range_pat_interactions1.rs:10:25
|
--> $DIR/range_pat_interactions1.rs:10:25
|
||||||
|
@ -9,7 +9,7 @@ fn main() {
|
|||||||
match x as i32 {
|
match x as i32 {
|
||||||
0..=(5+1) => errors_only.push(x),
|
0..=(5+1) => errors_only.push(x),
|
||||||
//~^ error: inclusive range with no end
|
//~^ error: inclusive range with no end
|
||||||
//~| error: expected one of `=>`, `if`, or `|`, found `(`
|
//~| error: expected one of `,`, `=>`, `if`, `|`, or `}`, found `(`
|
||||||
1 | -3..0 => first_or.push(x),
|
1 | -3..0 => first_or.push(x),
|
||||||
y @ (0..5 | 6) => or_two.push(y),
|
y @ (0..5 | 6) => or_two.push(y),
|
||||||
y @ 0..const { 5 + 1 } => assert_eq!(y, 5),
|
y @ 0..const { 5 + 1 } => assert_eq!(y, 5),
|
||||||
|
@ -6,11 +6,11 @@ LL | 0..=(5+1) => errors_only.push(x),
|
|||||||
|
|
|
|
||||||
= note: inclusive ranges must be bounded at the end (`..=b` or `a..=b`)
|
= note: inclusive ranges must be bounded at the end (`..=b` or `a..=b`)
|
||||||
|
|
||||||
error: expected one of `=>`, `if`, or `|`, found `(`
|
error: expected one of `,`, `=>`, `if`, `|`, or `}`, found `(`
|
||||||
--> $DIR/range_pat_interactions2.rs:10:17
|
--> $DIR/range_pat_interactions2.rs:10:17
|
||||||
|
|
|
|
||||||
LL | 0..=(5+1) => errors_only.push(x),
|
LL | 0..=(5+1) => errors_only.push(x),
|
||||||
| ^ expected one of `=>`, `if`, or `|`
|
| ^ expected one of `,`, `=>`, `if`, `|`, or `}`
|
||||||
|
|
||||||
error: aborting due to 2 previous errors
|
error: aborting due to 2 previous errors
|
||||||
|
|
||||||
|
@ -84,15 +84,15 @@ fn main() {}
|
|||||||
|
|
||||||
#[cfg(FALSE)] fn e() { match 0 { 0..=#[attr] 10 => () } }
|
#[cfg(FALSE)] fn e() { match 0 { 0..=#[attr] 10 => () } }
|
||||||
//~^ ERROR inclusive range with no end
|
//~^ ERROR inclusive range with no end
|
||||||
//~| ERROR expected one of `=>`, `if`, or `|`, found `#`
|
//~| ERROR expected one of `,`, `=>`, `if`, `|`, or `}`, found `#`
|
||||||
#[cfg(FALSE)] fn e() { match 0 { 0..=#[attr] -10 => () } }
|
#[cfg(FALSE)] fn e() { match 0 { 0..=#[attr] -10 => () } }
|
||||||
//~^ ERROR inclusive range with no end
|
//~^ ERROR inclusive range with no end
|
||||||
//~| ERROR expected one of `=>`, `if`, or `|`, found `#`
|
//~| ERROR expected one of `,`, `=>`, `if`, `|`, or `}`, found `#`
|
||||||
#[cfg(FALSE)] fn e() { match 0 { 0..=-#[attr] 10 => () } }
|
#[cfg(FALSE)] fn e() { match 0 { 0..=-#[attr] 10 => () } }
|
||||||
//~^ ERROR unexpected token: `#`
|
//~^ ERROR unexpected token: `#`
|
||||||
#[cfg(FALSE)] fn e() { match 0 { 0..=#[attr] FOO => () } }
|
#[cfg(FALSE)] fn e() { match 0 { 0..=#[attr] FOO => () } }
|
||||||
//~^ ERROR inclusive range with no end
|
//~^ ERROR inclusive range with no end
|
||||||
//~| ERROR expected one of `=>`, `if`, or `|`, found `#`
|
//~| ERROR expected one of `,`, `=>`, `if`, `|`, or `}`, found `#`
|
||||||
|
|
||||||
#[cfg(FALSE)] fn e() { let _ = x.#![attr]foo(); }
|
#[cfg(FALSE)] fn e() { let _ = x.#![attr]foo(); }
|
||||||
//~^ ERROR unexpected token: `#`
|
//~^ ERROR unexpected token: `#`
|
||||||
|
@ -365,11 +365,11 @@ LL | #[cfg(FALSE)] fn e() { match 0 { 0..=#[attr] 10 => () } }
|
|||||||
|
|
|
|
||||||
= note: inclusive ranges must be bounded at the end (`..=b` or `a..=b`)
|
= note: inclusive ranges must be bounded at the end (`..=b` or `a..=b`)
|
||||||
|
|
||||||
error: expected one of `=>`, `if`, or `|`, found `#`
|
error: expected one of `,`, `=>`, `if`, `|`, or `}`, found `#`
|
||||||
--> $DIR/attr-stmt-expr-attr-bad.rs:85:38
|
--> $DIR/attr-stmt-expr-attr-bad.rs:85:38
|
||||||
|
|
|
|
||||||
LL | #[cfg(FALSE)] fn e() { match 0 { 0..=#[attr] 10 => () } }
|
LL | #[cfg(FALSE)] fn e() { match 0 { 0..=#[attr] 10 => () } }
|
||||||
| ^ expected one of `=>`, `if`, or `|`
|
| ^ expected one of `,`, `=>`, `if`, `|`, or `}`
|
||||||
|
|
||||||
error[E0586]: inclusive range with no end
|
error[E0586]: inclusive range with no end
|
||||||
--> $DIR/attr-stmt-expr-attr-bad.rs:88:35
|
--> $DIR/attr-stmt-expr-attr-bad.rs:88:35
|
||||||
@ -379,11 +379,11 @@ LL | #[cfg(FALSE)] fn e() { match 0 { 0..=#[attr] -10 => () } }
|
|||||||
|
|
|
|
||||||
= note: inclusive ranges must be bounded at the end (`..=b` or `a..=b`)
|
= note: inclusive ranges must be bounded at the end (`..=b` or `a..=b`)
|
||||||
|
|
||||||
error: expected one of `=>`, `if`, or `|`, found `#`
|
error: expected one of `,`, `=>`, `if`, `|`, or `}`, found `#`
|
||||||
--> $DIR/attr-stmt-expr-attr-bad.rs:88:38
|
--> $DIR/attr-stmt-expr-attr-bad.rs:88:38
|
||||||
|
|
|
|
||||||
LL | #[cfg(FALSE)] fn e() { match 0 { 0..=#[attr] -10 => () } }
|
LL | #[cfg(FALSE)] fn e() { match 0 { 0..=#[attr] -10 => () } }
|
||||||
| ^ expected one of `=>`, `if`, or `|`
|
| ^ expected one of `,`, `=>`, `if`, `|`, or `}`
|
||||||
|
|
||||||
error: unexpected token: `#`
|
error: unexpected token: `#`
|
||||||
--> $DIR/attr-stmt-expr-attr-bad.rs:91:39
|
--> $DIR/attr-stmt-expr-attr-bad.rs:91:39
|
||||||
@ -399,11 +399,11 @@ LL | #[cfg(FALSE)] fn e() { match 0 { 0..=#[attr] FOO => () } }
|
|||||||
|
|
|
|
||||||
= note: inclusive ranges must be bounded at the end (`..=b` or `a..=b`)
|
= note: inclusive ranges must be bounded at the end (`..=b` or `a..=b`)
|
||||||
|
|
||||||
error: expected one of `=>`, `if`, or `|`, found `#`
|
error: expected one of `,`, `=>`, `if`, `|`, or `}`, found `#`
|
||||||
--> $DIR/attr-stmt-expr-attr-bad.rs:93:38
|
--> $DIR/attr-stmt-expr-attr-bad.rs:93:38
|
||||||
|
|
|
|
||||||
LL | #[cfg(FALSE)] fn e() { match 0 { 0..=#[attr] FOO => () } }
|
LL | #[cfg(FALSE)] fn e() { match 0 { 0..=#[attr] FOO => () } }
|
||||||
| ^ expected one of `=>`, `if`, or `|`
|
| ^ expected one of `,`, `=>`, `if`, `|`, or `}`
|
||||||
|
|
||||||
error: unexpected token: `#`
|
error: unexpected token: `#`
|
||||||
--> $DIR/attr-stmt-expr-attr-bad.rs:97:34
|
--> $DIR/attr-stmt-expr-attr-bad.rs:97:34
|
||||||
|
@ -3,7 +3,7 @@ static tmp : [&'static str; 2] = ["hello", "he"];
|
|||||||
fn main() {
|
fn main() {
|
||||||
let z = "hello";
|
let z = "hello";
|
||||||
match z {
|
match z {
|
||||||
tmp[0] => {} //~ ERROR expected one of `=>`, `@`, `if`, or `|`, found `[`
|
tmp[0] => {} //~ ERROR expected one of `,`, `=>`, `@`, `if`, `|`, or `}`, found `[`
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
error: expected one of `=>`, `@`, `if`, or `|`, found `[`
|
error: expected one of `,`, `=>`, `@`, `if`, `|`, or `}`, found `[`
|
||||||
--> $DIR/issue-24375.rs:6:12
|
--> $DIR/issue-24375.rs:6:12
|
||||||
|
|
|
|
||||||
LL | tmp[0] => {}
|
LL | tmp[0] => {}
|
||||||
| ^ expected one of `=>`, `@`, `if`, or `|`
|
| ^ expected one of `,`, `=>`, `@`, `if`, `|`, or `}`
|
||||||
|
|
||||||
error: aborting due to 1 previous error
|
error: aborting due to 1 previous error
|
||||||
|
|
||||||
|
@ -1,6 +1,9 @@
|
|||||||
macro_rules! arm {
|
macro_rules! arm {
|
||||||
($pattern:pat => $block:block) => {
|
($pattern:pat => $block:block) => {
|
||||||
$pattern => $block
|
$pattern => $block
|
||||||
|
//~^ ERROR macro expansion ignores token `=>` and any following
|
||||||
|
//~| NOTE the usage of `arm!` is likely invalid in pattern context
|
||||||
|
//~| NOTE macros cannot expand to match arms
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -9,9 +12,8 @@ fn main() {
|
|||||||
match x {
|
match x {
|
||||||
Some(1) => {},
|
Some(1) => {},
|
||||||
arm!(None => {}),
|
arm!(None => {}),
|
||||||
//~^ NOTE macros cannot expand to match arms
|
//~^ NOTE caused by the macro expansion here
|
||||||
//~| ERROR unexpected `,` in pattern
|
//~| ERROR `match` arm with no body
|
||||||
// doesn't recover
|
|
||||||
Some(2) => {},
|
Some(2) => {},
|
||||||
_ => {},
|
_ => {},
|
||||||
};
|
};
|
||||||
|
@ -1,10 +1,20 @@
|
|||||||
error: unexpected `,` in pattern
|
error: macro expansion ignores token `=>` and any following
|
||||||
--> $DIR/macro-expand-to-match-arm.rs:11:25
|
--> $DIR/macro-expand-to-match-arm.rs:3:18
|
||||||
|
|
|
|
||||||
|
LL | $pattern => $block
|
||||||
|
| ^^
|
||||||
|
...
|
||||||
LL | arm!(None => {}),
|
LL | arm!(None => {}),
|
||||||
| ^
|
| ---------------- caused by the macro expansion here
|
||||||
|
|
|
|
||||||
|
= note: the usage of `arm!` is likely invalid in pattern context
|
||||||
= note: macros cannot expand to match arms
|
= 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
|
||||||
|
|
||||||
|
79
tests/ui/parser/match-arm-without-body.rs
Normal file
79
tests/ui/parser/match-arm-without-body.rs
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
macro_rules! pat {
|
||||||
|
() => { Some(_) }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
match Some(false) {
|
||||||
|
Some(_)
|
||||||
|
//~^ ERROR `match` arm with no body
|
||||||
|
//~| HELP add a body after the pattern
|
||||||
|
}
|
||||||
|
match Some(false) {
|
||||||
|
Some(_)
|
||||||
|
_ => {}
|
||||||
|
//~^ ERROR expected one of
|
||||||
|
}
|
||||||
|
match Some(false) {
|
||||||
|
Some(_),
|
||||||
|
//~^ ERROR unexpected `,` in pattern
|
||||||
|
//~| HELP try adding parentheses to match on a tuple
|
||||||
|
//~| HELP or a vertical bar to match on multiple alternatives
|
||||||
|
}
|
||||||
|
match Some(false) {
|
||||||
|
Some(_),
|
||||||
|
//~^ ERROR unexpected `,` in pattern
|
||||||
|
//~| HELP try adding parentheses to match on a tuple
|
||||||
|
//~| HELP or a vertical bar to match on multiple alternatives
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
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 expected one of
|
||||||
|
}
|
||||||
|
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
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
116
tests/ui/parser/match-arm-without-body.stderr
Normal file
116
tests/ui/parser/match-arm-without-body.stderr
Normal file
@ -0,0 +1,116 @@
|
|||||||
|
error: expected one of `,`, `=>`, `if`, `|`, or `}`, found reserved identifier `_`
|
||||||
|
--> $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:17:16
|
||||||
|
|
|
||||||
|
LL | Some(_),
|
||||||
|
| ^
|
||||||
|
|
|
||||||
|
help: try adding parentheses to match on a tuple...
|
||||||
|
|
|
||||||
|
LL | (Some(_),)
|
||||||
|
| + +
|
||||||
|
help: ...or a vertical bar to match on multiple alternatives
|
||||||
|
|
|
||||||
|
LL | Some(_) |
|
||||||
|
|
|
||||||
|
|
||||||
|
error: unexpected `,` in pattern
|
||||||
|
--> $DIR/match-arm-without-body.rs:23:16
|
||||||
|
|
|
||||||
|
LL | Some(_),
|
||||||
|
| ^
|
||||||
|
|
|
||||||
|
help: try adding parentheses to match on a tuple...
|
||||||
|
|
|
||||||
|
LL ~ (Some(_),
|
||||||
|
LL |
|
||||||
|
LL |
|
||||||
|
LL |
|
||||||
|
LL ~ _) => {}
|
||||||
|
|
|
||||||
|
help: ...or a vertical bar to match on multiple alternatives
|
||||||
|
|
|
||||||
|
LL ~ Some(_) |
|
||||||
|
LL +
|
||||||
|
LL +
|
||||||
|
LL +
|
||||||
|
LL ~ _ => {}
|
||||||
|
|
|
||||||
|
|
||||||
|
error: expected one of `,`, `.`, `=>`, `?`, `}`, or an operator, found reserved identifier `_`
|
||||||
|
--> $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:66:15
|
||||||
|
|
|
||||||
|
LL | pat!()
|
||||||
|
| ^ help: missing a comma here to end this `match` arm: `,`
|
||||||
|
|
||||||
|
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
|
||||||
|
|
@ -1,7 +1,7 @@
|
|||||||
fn main() {
|
fn main() {
|
||||||
match 42 {
|
match 42 {
|
||||||
x < 7 => (),
|
x < 7 => (),
|
||||||
//~^ error: expected one of `=>`, `@`, `if`, or `|`, found `<`
|
//~^ error: expected one of `,`, `=>`, `@`, `if`, `|`, or `}`, found `<`
|
||||||
_ => ()
|
_ => ()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
error: expected one of `=>`, `@`, `if`, or `|`, found `<`
|
error: expected one of `,`, `=>`, `@`, `if`, `|`, or `}`, found `<`
|
||||||
--> $DIR/pat-lt-bracket-1.rs:3:7
|
--> $DIR/pat-lt-bracket-1.rs:3:7
|
||||||
|
|
|
|
||||||
LL | x < 7 => (),
|
LL | x < 7 => (),
|
||||||
| ^ expected one of `=>`, `@`, `if`, or `|`
|
| ^ expected one of `,`, `=>`, `@`, `if`, `|`, or `}`
|
||||||
|
|
||||||
error: aborting due to 1 previous error
|
error: aborting due to 1 previous error
|
||||||
|
|
||||||
|
@ -20,58 +20,58 @@ fn never_pattern_location(void: Void) {
|
|||||||
// FIXME(never_patterns): Don't accept on a non-empty type.
|
// FIXME(never_patterns): Don't accept on a non-empty type.
|
||||||
match Some(0) {
|
match Some(0) {
|
||||||
None => {}
|
None => {}
|
||||||
Some(!) => {}
|
Some(!),
|
||||||
}
|
}
|
||||||
// FIXME(never_patterns): Don't accept on an arbitrary type, even if there are no more branches.
|
// FIXME(never_patterns): Don't accept on an arbitrary type, even if there are no more branches.
|
||||||
match () {
|
match () {
|
||||||
() => {}
|
() => {}
|
||||||
! => {}
|
!,
|
||||||
}
|
}
|
||||||
// FIXME(never_patterns): Don't accept even on an empty branch.
|
// FIXME(never_patterns): Don't accept even on an empty branch.
|
||||||
match None::<Void> {
|
match None::<Void> {
|
||||||
None => {}
|
None => {}
|
||||||
! => {}
|
!,
|
||||||
}
|
}
|
||||||
// FIXME(never_patterns): Let alone if the emptiness is behind a reference.
|
// FIXME(never_patterns): Let alone if the emptiness is behind a reference.
|
||||||
match None::<&Void> {
|
match None::<&Void> {
|
||||||
None => {}
|
None => {}
|
||||||
! => {}
|
!,
|
||||||
}
|
}
|
||||||
// Participate in match ergonomics.
|
// Participate in match ergonomics.
|
||||||
match &void {
|
match &void {
|
||||||
! => {}
|
!
|
||||||
}
|
}
|
||||||
match &&void {
|
match &&void {
|
||||||
! => {}
|
!
|
||||||
}
|
}
|
||||||
match &&void {
|
match &&void {
|
||||||
&! => {}
|
&!
|
||||||
}
|
}
|
||||||
match &None::<Void> {
|
match &None::<Void> {
|
||||||
None => {}
|
None => {}
|
||||||
Some(!) => {}
|
Some(!)
|
||||||
}
|
}
|
||||||
match None::<&Void> {
|
match None::<&Void> {
|
||||||
None => {}
|
None => {}
|
||||||
Some(!) => {}
|
Some(!),
|
||||||
}
|
}
|
||||||
// Accept on a composite empty type.
|
// Accept on a composite empty type.
|
||||||
match None::<&(u32, Void)> {
|
match None::<&(u32, Void)> {
|
||||||
None => {}
|
None => {}
|
||||||
Some(&!) => {}
|
Some(&!),
|
||||||
}
|
}
|
||||||
// Accept on an simple empty type.
|
// Accept on an simple empty type.
|
||||||
match None::<Void> {
|
match None::<Void> {
|
||||||
None => {}
|
None => {}
|
||||||
Some(!) => {}
|
Some(!),
|
||||||
}
|
}
|
||||||
match None::<&Void> {
|
match None::<&Void> {
|
||||||
None => {}
|
None => {}
|
||||||
Some(&!) => {}
|
Some(&!),
|
||||||
}
|
}
|
||||||
match None::<&(u32, Void)> {
|
match None::<&(u32, Void)> {
|
||||||
None => {}
|
None => {}
|
||||||
Some(&(_, !)) => {}
|
Some(&(_, !)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -84,12 +84,12 @@ fn never_and_bindings() {
|
|||||||
//~^ ERROR: is not bound in all patterns
|
//~^ ERROR: is not bound in all patterns
|
||||||
}
|
}
|
||||||
let (Ok(_x) | Err(&!)) = x;
|
let (Ok(_x) | Err(&!)) = x;
|
||||||
//~^ ERROR: is not bound in all patterns
|
//~^ ERROR: is not bound in all patterns
|
||||||
|
|
||||||
// FIXME(never_patterns): A never pattern mustn't have bindings.
|
// FIXME(never_patterns): A never pattern mustn't have bindings.
|
||||||
match x {
|
match x {
|
||||||
Ok(_) => {}
|
Ok(_) => {}
|
||||||
Err(&(_b, !)) => {}
|
Err(&(_b, !)),
|
||||||
}
|
}
|
||||||
match x {
|
match x {
|
||||||
Ok(_a) | Err(&(_b, !)) => {}
|
Ok(_a) | Err(&(_b, !)) => {}
|
||||||
|
33
tests/ui/rfcs/rfc-0000-never_patterns/check.rs
Normal file
33
tests/ui/rfcs/rfc-0000-never_patterns/check.rs
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
#![feature(never_patterns)]
|
||||||
|
#![allow(incomplete_features)]
|
||||||
|
|
||||||
|
enum Void {}
|
||||||
|
|
||||||
|
fn main() {}
|
||||||
|
|
||||||
|
macro_rules! never {
|
||||||
|
() => { ! }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn no_arms_or_guards(x: Void) {
|
||||||
|
match None::<Void> {
|
||||||
|
Some(!) => {}
|
||||||
|
//~^ ERROR a never pattern is always unreachable
|
||||||
|
None => {}
|
||||||
|
}
|
||||||
|
match None::<Void> {
|
||||||
|
Some(!) if true,
|
||||||
|
//~^ ERROR guard on a never pattern
|
||||||
|
None => {}
|
||||||
|
}
|
||||||
|
match None::<Void> {
|
||||||
|
Some(!) if true => {}
|
||||||
|
//~^ ERROR a never pattern is always unreachable
|
||||||
|
None => {}
|
||||||
|
}
|
||||||
|
match None::<Void> {
|
||||||
|
Some(never!()) => {},
|
||||||
|
//~^ ERROR a never pattern is always unreachable
|
||||||
|
None => {}
|
||||||
|
}
|
||||||
|
}
|
35
tests/ui/rfcs/rfc-0000-never_patterns/check.stderr
Normal file
35
tests/ui/rfcs/rfc-0000-never_patterns/check.stderr
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
error: a never pattern is always unreachable
|
||||||
|
--> $DIR/check.rs:14:20
|
||||||
|
|
|
||||||
|
LL | Some(!) => {}
|
||||||
|
| ^^
|
||||||
|
| |
|
||||||
|
| this will never be executed
|
||||||
|
| help: remove this expression
|
||||||
|
|
||||||
|
error: a guard on a never pattern will never be run
|
||||||
|
--> $DIR/check.rs:19:20
|
||||||
|
|
|
||||||
|
LL | Some(!) if true,
|
||||||
|
| ^^^^ help: remove this guard
|
||||||
|
|
||||||
|
error: a never pattern is always unreachable
|
||||||
|
--> $DIR/check.rs:24:28
|
||||||
|
|
|
||||||
|
LL | Some(!) if true => {}
|
||||||
|
| ^^
|
||||||
|
| |
|
||||||
|
| this will never be executed
|
||||||
|
| help: remove this expression
|
||||||
|
|
||||||
|
error: a never pattern is always unreachable
|
||||||
|
--> $DIR/check.rs:29:27
|
||||||
|
|
|
||||||
|
LL | Some(never!()) => {},
|
||||||
|
| ^^
|
||||||
|
| |
|
||||||
|
| this will never be executed
|
||||||
|
| help: remove this expression
|
||||||
|
|
||||||
|
error: aborting due to 4 previous errors
|
||||||
|
|
71
tests/ui/rfcs/rfc-0000-never_patterns/parse.rs
Normal file
71
tests/ui/rfcs/rfc-0000-never_patterns/parse.rs
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
#![feature(never_patterns)]
|
||||||
|
#![allow(incomplete_features)]
|
||||||
|
|
||||||
|
enum Void {}
|
||||||
|
|
||||||
|
fn main() {}
|
||||||
|
|
||||||
|
macro_rules! never {
|
||||||
|
() => { ! }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse(x: Void) {
|
||||||
|
match None::<Void> {
|
||||||
|
None => {}
|
||||||
|
Some(!),
|
||||||
|
}
|
||||||
|
match None::<Void> {
|
||||||
|
Some(!),
|
||||||
|
None => {}
|
||||||
|
}
|
||||||
|
match None::<Void> {
|
||||||
|
None => {}
|
||||||
|
Some(!)
|
||||||
|
}
|
||||||
|
match None::<Void> {
|
||||||
|
Some(!)
|
||||||
|
//~^ ERROR expected `,` following `match` arm
|
||||||
|
None => {}
|
||||||
|
}
|
||||||
|
match None::<Void> {
|
||||||
|
Some(!) if true
|
||||||
|
//~^ ERROR expected `,` following `match` arm
|
||||||
|
//~| ERROR guard on a never pattern
|
||||||
|
None => {}
|
||||||
|
}
|
||||||
|
match None::<Void> {
|
||||||
|
Some(!) if true,
|
||||||
|
//~^ ERROR guard on a never pattern
|
||||||
|
None => {}
|
||||||
|
}
|
||||||
|
match None::<Void> {
|
||||||
|
Some(!) <=
|
||||||
|
//~^ ERROR expected one of
|
||||||
|
}
|
||||||
|
match x {
|
||||||
|
never!(),
|
||||||
|
}
|
||||||
|
match x {
|
||||||
|
never!() if true,
|
||||||
|
//~^ ERROR guard on a never pattern
|
||||||
|
}
|
||||||
|
match x {
|
||||||
|
never!()
|
||||||
|
}
|
||||||
|
match &x {
|
||||||
|
&never!(),
|
||||||
|
}
|
||||||
|
match None::<Void> {
|
||||||
|
Some(never!()),
|
||||||
|
None => {}
|
||||||
|
}
|
||||||
|
match x { ! }
|
||||||
|
match &x { &! }
|
||||||
|
|
||||||
|
let res: Result<bool, Void> = Ok(false);
|
||||||
|
let Ok(_) = res;
|
||||||
|
let Ok(_) | Err(!) = &res; // Disallowed; see #82048.
|
||||||
|
//~^ ERROR top-level or-patterns are not allowed in `let` bindings
|
||||||
|
let (Ok(_) | Err(!)) = &res;
|
||||||
|
let (Ok(_) | Err(&!)) = res.as_ref();
|
||||||
|
}
|
44
tests/ui/rfcs/rfc-0000-never_patterns/parse.stderr
Normal file
44
tests/ui/rfcs/rfc-0000-never_patterns/parse.stderr
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
error: expected `,` following `match` arm
|
||||||
|
--> $DIR/parse.rs:26:16
|
||||||
|
|
|
||||||
|
LL | Some(!)
|
||||||
|
| ^ help: missing a comma here to end this `match` arm: `,`
|
||||||
|
|
||||||
|
error: expected `,` following `match` arm
|
||||||
|
--> $DIR/parse.rs:31:24
|
||||||
|
|
|
||||||
|
LL | Some(!) if true
|
||||||
|
| ^ help: missing a comma here to end this `match` arm: `,`
|
||||||
|
|
||||||
|
error: expected one of `,`, `=>`, `if`, `|`, or `}`, found `<=`
|
||||||
|
--> $DIR/parse.rs:42:17
|
||||||
|
|
|
||||||
|
LL | Some(!) <=
|
||||||
|
| ^^ expected one of `,`, `=>`, `if`, `|`, or `}`
|
||||||
|
|
||||||
|
error: top-level or-patterns are not allowed in `let` bindings
|
||||||
|
--> $DIR/parse.rs:67:9
|
||||||
|
|
|
||||||
|
LL | let Ok(_) | Err(!) = &res; // Disallowed; see #82048.
|
||||||
|
| ^^^^^^^^^^^^^^ help: wrap the pattern in parentheses: `(Ok(_) | Err(!))`
|
||||||
|
|
||||||
|
error: a guard on a never pattern will never be run
|
||||||
|
--> $DIR/parse.rs:31:20
|
||||||
|
|
|
||||||
|
LL | Some(!) if true
|
||||||
|
| ^^^^ help: remove this guard
|
||||||
|
|
||||||
|
error: a guard on a never pattern will never be run
|
||||||
|
--> $DIR/parse.rs:37:20
|
||||||
|
|
|
||||||
|
LL | Some(!) if true,
|
||||||
|
| ^^^^ help: remove this guard
|
||||||
|
|
||||||
|
error: a guard on a never pattern will never be run
|
||||||
|
--> $DIR/parse.rs:49:21
|
||||||
|
|
|
||||||
|
LL | never!() if true,
|
||||||
|
| ^^^^ help: remove this guard
|
||||||
|
|
||||||
|
error: aborting due to 7 previous errors
|
||||||
|
|
Loading…
Reference in New Issue
Block a user