Rollup merge of #118868 - Nadrieril:correctly-gate-never_patterns-parsing, r=petrochenkov
Correctly gate the parsing of match arms without body https://github.com/rust-lang/rust/pull/118527 accidentally allowed the following to parse on stable: ```rust match Some(0) { None => { foo(); } #[cfg(FALSE)] Some(_) } ``` This fixes that oversight. The way I choose which error to emit is the best I could think of, I'm open if you know a better way. r? `@petrochenkov` since you're the one who noticed
This commit is contained in:
commit
d661974017
@ -676,6 +676,19 @@ impl Pat {
|
|||||||
});
|
});
|
||||||
could_be_never_pattern
|
could_be_never_pattern
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Whether this contains a `!` pattern. This in particular means that a feature gate error will
|
||||||
|
/// be raised if the feature is off. Used to avoid gating the feature twice.
|
||||||
|
pub fn contains_never_pattern(&self) -> bool {
|
||||||
|
let mut contains_never_pattern = false;
|
||||||
|
self.walk(&mut |pat| {
|
||||||
|
if matches!(pat.kind, PatKind::Never) {
|
||||||
|
contains_never_pattern = true;
|
||||||
|
}
|
||||||
|
true
|
||||||
|
});
|
||||||
|
contains_never_pattern
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A single field in a struct pattern.
|
/// A single field in a struct pattern.
|
||||||
|
@ -581,8 +581,11 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
|||||||
} else {
|
} else {
|
||||||
// Either `body.is_none()` or `is_never_pattern` here.
|
// Either `body.is_none()` or `is_never_pattern` here.
|
||||||
if !is_never_pattern {
|
if !is_never_pattern {
|
||||||
let suggestion = span.shrink_to_hi();
|
if self.tcx.features().never_patterns {
|
||||||
self.tcx.sess.emit_err(MatchArmWithNoBody { span, suggestion });
|
// If the feature is off we already emitted the error after parsing.
|
||||||
|
let suggestion = span.shrink_to_hi();
|
||||||
|
self.tcx.sess.emit_err(MatchArmWithNoBody { span, suggestion });
|
||||||
|
}
|
||||||
} else if let Some(body) = &arm.body {
|
} else if let Some(body) = &arm.body {
|
||||||
self.tcx.sess.emit_err(NeverPatternWithBody { span: body.span });
|
self.tcx.sess.emit_err(NeverPatternWithBody { span: body.span });
|
||||||
guard = None;
|
guard = None;
|
||||||
|
@ -174,6 +174,10 @@ ast_passes_item_underscore = `{$kind}` items in this context need a name
|
|||||||
ast_passes_keyword_lifetime =
|
ast_passes_keyword_lifetime =
|
||||||
lifetimes cannot use keyword names
|
lifetimes cannot use keyword names
|
||||||
|
|
||||||
|
ast_passes_match_arm_with_no_body =
|
||||||
|
`match` arm with no body
|
||||||
|
.suggestion = add a body after the pattern
|
||||||
|
|
||||||
ast_passes_module_nonascii = trying to load file for module `{$name}` with non-ascii identifier name
|
ast_passes_module_nonascii = trying to load file for module `{$name}` with non-ascii identifier name
|
||||||
.help = consider using the `#[path]` attribute to specify filesystem path
|
.help = consider using the `#[path]` attribute to specify filesystem path
|
||||||
|
|
||||||
|
@ -758,3 +758,12 @@ pub struct AnonStructOrUnionNotAllowed {
|
|||||||
pub span: Span,
|
pub span: Span,
|
||||||
pub struct_or_union: &'static str,
|
pub struct_or_union: &'static str,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Diagnostic)]
|
||||||
|
#[diag(ast_passes_match_arm_with_no_body)]
|
||||||
|
pub struct MatchArmWithNoBody {
|
||||||
|
#[primary_span]
|
||||||
|
pub span: Span,
|
||||||
|
#[suggestion(code = " => todo!(),", applicability = "has-placeholders")]
|
||||||
|
pub suggestion: Span,
|
||||||
|
}
|
||||||
|
@ -554,7 +554,34 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session, features: &Features) {
|
|||||||
gate_all!(explicit_tail_calls, "`become` expression is experimental");
|
gate_all!(explicit_tail_calls, "`become` expression is experimental");
|
||||||
gate_all!(generic_const_items, "generic const items are experimental");
|
gate_all!(generic_const_items, "generic const items are experimental");
|
||||||
gate_all!(unnamed_fields, "unnamed fields are not yet fully implemented");
|
gate_all!(unnamed_fields, "unnamed fields are not yet fully implemented");
|
||||||
gate_all!(never_patterns, "`!` patterns are experimental");
|
|
||||||
|
if !visitor.features.never_patterns {
|
||||||
|
if let Some(spans) = spans.get(&sym::never_patterns) {
|
||||||
|
for &span in spans {
|
||||||
|
if span.allows_unstable(sym::never_patterns) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let sm = sess.source_map();
|
||||||
|
// We gate two types of spans: the span of a `!` pattern, and the span of a
|
||||||
|
// match arm without a body. For the latter we want to give the user a normal
|
||||||
|
// error.
|
||||||
|
if let Ok(snippet) = sm.span_to_snippet(span)
|
||||||
|
&& snippet == "!"
|
||||||
|
{
|
||||||
|
feature_err(
|
||||||
|
&sess.parse_sess,
|
||||||
|
sym::never_patterns,
|
||||||
|
span,
|
||||||
|
"`!` patterns are experimental",
|
||||||
|
)
|
||||||
|
.emit();
|
||||||
|
} else {
|
||||||
|
let suggestion = span.shrink_to_hi();
|
||||||
|
sess.emit_err(errors::MatchArmWithNoBody { span, suggestion });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if !visitor.features.negative_bounds {
|
if !visitor.features.negative_bounds {
|
||||||
for &span in spans.get(&sym::negative_bounds).iter().copied().flatten() {
|
for &span in spans.get(&sym::negative_bounds).iter().copied().flatten() {
|
||||||
|
@ -2918,7 +2918,15 @@ impl<'a> Parser<'a> {
|
|||||||
let mut result = if !is_fat_arrow && !is_almost_fat_arrow {
|
let mut result = if !is_fat_arrow && !is_almost_fat_arrow {
|
||||||
// A pattern without a body, allowed for never patterns.
|
// A pattern without a body, allowed for never patterns.
|
||||||
arm_body = None;
|
arm_body = None;
|
||||||
this.expect_one_of(&[token::Comma], &[token::CloseDelim(Delimiter::Brace)])
|
this.expect_one_of(&[token::Comma], &[token::CloseDelim(Delimiter::Brace)]).map(
|
||||||
|
|x| {
|
||||||
|
// Don't gate twice
|
||||||
|
if !pat.contains_never_pattern() {
|
||||||
|
this.sess.gated_spans.gate(sym::never_patterns, pat.span);
|
||||||
|
}
|
||||||
|
x
|
||||||
|
},
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
if let Err(mut err) = this.expect(&token::FatArrow) {
|
if let Err(mut err) = this.expect(&token::FatArrow) {
|
||||||
// We might have a `=>` -> `=` or `->` typo (issue #89396).
|
// We might have a `=>` -> `=` or `->` typo (issue #89396).
|
||||||
|
@ -12,16 +12,63 @@ 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
|
||||||
|
}
|
||||||
|
// Check that the gate operates even behind `cfg`.
|
||||||
|
#[cfg(FALSE)]
|
||||||
|
match *ptr {
|
||||||
|
!
|
||||||
|
//~^ ERROR `!` patterns are experimental
|
||||||
|
}
|
||||||
|
#[cfg(FALSE)]
|
||||||
|
match *ptr {
|
||||||
|
! => {}
|
||||||
|
//~^ ERROR `!` patterns are experimental
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Correctly gate match arms with no body.
|
||||||
|
match Some(0) {
|
||||||
|
None => {}
|
||||||
|
Some(_),
|
||||||
|
//~^ ERROR unexpected `,` in pattern
|
||||||
|
}
|
||||||
|
match Some(0) {
|
||||||
|
None => {}
|
||||||
|
Some(_)
|
||||||
|
//~^ ERROR `match` arm with no body
|
||||||
|
}
|
||||||
|
match Some(0) {
|
||||||
|
_ => {}
|
||||||
|
Some(_) if false,
|
||||||
|
//~^ ERROR `match` arm with no body
|
||||||
|
Some(_) if false
|
||||||
|
//~^ ERROR `match` arm with no body
|
||||||
|
}
|
||||||
|
match res {
|
||||||
|
Ok(_) => {}
|
||||||
|
Err(!),
|
||||||
|
//~^ ERROR `!` patterns are experimental
|
||||||
|
}
|
||||||
|
match res {
|
||||||
|
Err(!) if false,
|
||||||
|
//~^ ERROR `!` patterns are experimental
|
||||||
|
//~| ERROR a guard on a never pattern will never be run
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
|
||||||
// Check that the gate operates even behind `cfg`.
|
// Check that the gate operates even behind `cfg`.
|
||||||
#[cfg(FALSE)]
|
match Some(0) {
|
||||||
unsafe {
|
None => {}
|
||||||
let ptr: *const Void = NonNull::dangling().as_ptr();
|
#[cfg(FALSE)]
|
||||||
match *ptr {
|
Some(_)
|
||||||
! => {} //~ ERROR `!` patterns are experimental
|
//~^ ERROR `match` arm with no body
|
||||||
}
|
}
|
||||||
|
match Some(0) {
|
||||||
|
_ => {}
|
||||||
|
#[cfg(FALSE)]
|
||||||
|
Some(_) if false
|
||||||
|
//~^ ERROR `match` arm with no body
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,3 +1,18 @@
|
|||||||
|
error: unexpected `,` in pattern
|
||||||
|
--> $DIR/feature-gate-never_patterns.rs:34: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[E0408]: variable `_x` is not bound in all patterns
|
error[E0408]: variable `_x` is not bound in all patterns
|
||||||
--> $DIR/feature-gate-never_patterns.rs:8:19
|
--> $DIR/feature-gate-never_patterns.rs:8:19
|
||||||
|
|
|
|
||||||
@ -25,7 +40,16 @@ LL | !
|
|||||||
= help: add `#![feature(never_patterns)]` to the crate attributes to enable
|
= help: add `#![feature(never_patterns)]` to the crate attributes to enable
|
||||||
|
|
||||||
error[E0658]: `!` patterns are experimental
|
error[E0658]: `!` patterns are experimental
|
||||||
--> $DIR/feature-gate-never_patterns.rs:24:13
|
--> $DIR/feature-gate-never_patterns.rs:21:13
|
||||||
|
|
|
||||||
|
LL | !
|
||||||
|
| ^
|
||||||
|
|
|
||||||
|
= note: see issue #118155 <https://github.com/rust-lang/rust/issues/118155> for more information
|
||||||
|
= help: add `#![feature(never_patterns)]` to the crate attributes to enable
|
||||||
|
|
||||||
|
error[E0658]: `!` patterns are experimental
|
||||||
|
--> $DIR/feature-gate-never_patterns.rs:26:13
|
||||||
|
|
|
|
||||||
LL | ! => {}
|
LL | ! => {}
|
||||||
| ^
|
| ^
|
||||||
@ -33,7 +57,61 @@ 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
|
||||||
= help: add `#![feature(never_patterns)]` to the crate attributes to enable
|
= help: add `#![feature(never_patterns)]` to the crate attributes to enable
|
||||||
|
|
||||||
error: aborting due to 4 previous errors
|
error: `match` arm with no body
|
||||||
|
--> $DIR/feature-gate-never_patterns.rs:39:9
|
||||||
|
|
|
||||||
|
LL | Some(_)
|
||||||
|
| ^^^^^^^- help: add a body after the pattern: `=> todo!(),`
|
||||||
|
|
||||||
|
error: `match` arm with no body
|
||||||
|
--> $DIR/feature-gate-never_patterns.rs:44:9
|
||||||
|
|
|
||||||
|
LL | Some(_) if false,
|
||||||
|
| ^^^^^^^- help: add a body after the pattern: `=> todo!(),`
|
||||||
|
|
||||||
|
error: `match` arm with no body
|
||||||
|
--> $DIR/feature-gate-never_patterns.rs:46:9
|
||||||
|
|
|
||||||
|
LL | Some(_) if false
|
||||||
|
| ^^^^^^^- help: add a body after the pattern: `=> todo!(),`
|
||||||
|
|
||||||
|
error[E0658]: `!` patterns are experimental
|
||||||
|
--> $DIR/feature-gate-never_patterns.rs:51:13
|
||||||
|
|
|
||||||
|
LL | Err(!),
|
||||||
|
| ^
|
||||||
|
|
|
||||||
|
= note: see issue #118155 <https://github.com/rust-lang/rust/issues/118155> for more information
|
||||||
|
= help: add `#![feature(never_patterns)]` to the crate attributes to enable
|
||||||
|
|
||||||
|
error[E0658]: `!` patterns are experimental
|
||||||
|
--> $DIR/feature-gate-never_patterns.rs:55:13
|
||||||
|
|
|
||||||
|
LL | Err(!) if false,
|
||||||
|
| ^
|
||||||
|
|
|
||||||
|
= note: see issue #118155 <https://github.com/rust-lang/rust/issues/118155> for more information
|
||||||
|
= help: add `#![feature(never_patterns)]` to the crate attributes to enable
|
||||||
|
|
||||||
|
error: `match` arm with no body
|
||||||
|
--> $DIR/feature-gate-never_patterns.rs:65:9
|
||||||
|
|
|
||||||
|
LL | Some(_)
|
||||||
|
| ^^^^^^^- help: add a body after the pattern: `=> todo!(),`
|
||||||
|
|
||||||
|
error: `match` arm with no body
|
||||||
|
--> $DIR/feature-gate-never_patterns.rs:71:9
|
||||||
|
|
|
||||||
|
LL | Some(_) if false
|
||||||
|
| ^^^^^^^- help: add a body after the pattern: `=> todo!(),`
|
||||||
|
|
||||||
|
error: a guard on a never pattern will never be run
|
||||||
|
--> $DIR/feature-gate-never_patterns.rs:55:19
|
||||||
|
|
|
||||||
|
LL | Err(!) if false,
|
||||||
|
| ^^^^^ help: remove this guard
|
||||||
|
|
||||||
|
error: aborting due to 14 previous errors
|
||||||
|
|
||||||
Some errors have detailed explanations: E0408, E0658.
|
Some errors have detailed explanations: E0408, E0658.
|
||||||
For more information about an error, try `rustc --explain E0408`.
|
For more information about an error, try `rustc --explain E0408`.
|
||||||
|
@ -66,8 +66,6 @@ fn main() {
|
|||||||
pat!()
|
pat!()
|
||||||
//~^ ERROR expected `,` following `match` arm
|
//~^ ERROR expected `,` following `match` arm
|
||||||
//~| HELP missing a comma here
|
//~| HELP missing a comma here
|
||||||
//~| ERROR `match` arm with no body
|
|
||||||
//~| HELP add a body after the pattern
|
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
match Some(false) {
|
match Some(false) {
|
||||||
|
@ -68,19 +68,19 @@ error: `match` arm with no body
|
|||||||
--> $DIR/match-arm-without-body.rs:30:9
|
--> $DIR/match-arm-without-body.rs:30:9
|
||||||
|
|
|
|
||||||
LL | Some(_) if true
|
LL | Some(_) if true
|
||||||
| ^^^^^^^^^^^^^^^- help: add a body after the pattern: `=> todo!(),`
|
| ^^^^^^^- help: add a body after the pattern: `=> todo!(),`
|
||||||
|
|
||||||
error: `match` arm with no body
|
error: `match` arm with no body
|
||||||
--> $DIR/match-arm-without-body.rs:40:9
|
--> $DIR/match-arm-without-body.rs:40:9
|
||||||
|
|
|
|
||||||
LL | Some(_) if true,
|
LL | Some(_) if true,
|
||||||
| ^^^^^^^^^^^^^^^- help: add a body after the pattern: `=> todo!(),`
|
| ^^^^^^^- help: add a body after the pattern: `=> todo!(),`
|
||||||
|
|
||||||
error: `match` arm with no body
|
error: `match` arm with no body
|
||||||
--> $DIR/match-arm-without-body.rs:45:9
|
--> $DIR/match-arm-without-body.rs:45:9
|
||||||
|
|
|
|
||||||
LL | Some(_) if true,
|
LL | Some(_) if true,
|
||||||
| ^^^^^^^^^^^^^^^- help: add a body after the pattern: `=> todo!(),`
|
| ^^^^^^^- help: add a body after the pattern: `=> todo!(),`
|
||||||
|
|
||||||
error: `match` arm with no body
|
error: `match` arm with no body
|
||||||
--> $DIR/match-arm-without-body.rs:51:9
|
--> $DIR/match-arm-without-body.rs:51:9
|
||||||
@ -98,19 +98,13 @@ error: `match` arm with no body
|
|||||||
--> $DIR/match-arm-without-body.rs:61:9
|
--> $DIR/match-arm-without-body.rs:61:9
|
||||||
|
|
|
|
||||||
LL | pat!() if true,
|
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!(),`
|
| ^^^^^^- help: add a body after the pattern: `=> todo!(),`
|
||||||
|
|
||||||
error: `match` arm with no body
|
error: `match` arm with no body
|
||||||
--> $DIR/match-arm-without-body.rs:74:9
|
--> $DIR/match-arm-without-body.rs:72:9
|
||||||
|
|
|
|
||||||
LL | pat!(),
|
LL | pat!(),
|
||||||
| ^^^^^^- help: add a body after the pattern: `=> todo!(),`
|
| ^^^^^^- help: add a body after the pattern: `=> todo!(),`
|
||||||
|
|
||||||
error: aborting due to 14 previous errors
|
error: aborting due to 13 previous errors
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user