Skip to content

Commit

Permalink
Unrolled build for rust-lang#118868
Browse files Browse the repository at this point in the history
Rollup merge of rust-lang#118868 - Nadrieril:correctly-gate-never_patterns-parsing, r=petrochenkov

Correctly gate the parsing of match arms without body

rust-lang#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
  • Loading branch information
rust-timer authored Dec 12, 2023
2 parents 835ed00 + 19e0c98 commit 3485b66
Show file tree
Hide file tree
Showing 10 changed files with 207 additions and 26 deletions.
13 changes: 13 additions & 0 deletions compiler/rustc_ast/src/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -676,6 +676,19 @@ impl Pat {
});
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.
Expand Down
7 changes: 5 additions & 2 deletions compiler/rustc_ast_lowering/src/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -581,8 +581,11 @@ impl<'hir> LoweringContext<'_, 'hir> {
} 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 });
if self.tcx.features().never_patterns {
// 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 {
self.tcx.sess.emit_err(NeverPatternWithBody { span: body.span });
guard = None;
Expand Down
4 changes: 4 additions & 0 deletions compiler/rustc_ast_passes/messages.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,10 @@ ast_passes_item_underscore = `{$kind}` items in this context need a name
ast_passes_keyword_lifetime =
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
.help = consider using the `#[path]` attribute to specify filesystem path
Expand Down
9 changes: 9 additions & 0 deletions compiler/rustc_ast_passes/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -758,3 +758,12 @@ pub struct AnonStructOrUnionNotAllowed {
pub span: Span,
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,
}
29 changes: 28 additions & 1 deletion compiler/rustc_ast_passes/src/feature_gate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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!(generic_const_items, "generic const items are experimental");
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 {
for &span in spans.get(&sym::negative_bounds).iter().copied().flatten() {
Expand Down
10 changes: 9 additions & 1 deletion compiler/rustc_parse/src/parser/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2918,7 +2918,15 @@ impl<'a> Parser<'a> {
let mut result = if !is_fat_arrow && !is_almost_fat_arrow {
// A pattern without a body, allowed for never patterns.
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 {
if let Err(mut err) = this.expect(&token::FatArrow) {
// We might have a `=>` -> `=` or `->` typo (issue #89396).
Expand Down
61 changes: 54 additions & 7 deletions tests/ui/feature-gates/feature-gate-never_patterns.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,16 +12,63 @@ fn main() {
unsafe {
let ptr: *const Void = NonNull::dangling().as_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`.
#[cfg(FALSE)]
unsafe {
let ptr: *const Void = NonNull::dangling().as_ptr();
match *ptr {
! => {} //~ ERROR `!` patterns are experimental
}
match Some(0) {
None => {}
#[cfg(FALSE)]
Some(_)
//~^ ERROR `match` arm with no body
}
match Some(0) {
_ => {}
#[cfg(FALSE)]
Some(_) if false
//~^ ERROR `match` arm with no body
}
}
82 changes: 80 additions & 2 deletions tests/ui/feature-gates/feature-gate-never_patterns.stderr
Original file line number Diff line number Diff line change
@@ -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
--> $DIR/feature-gate-never_patterns.rs:8:19
|
Expand Down Expand Up @@ -25,15 +40,78 @@ LL | !
= help: add `#![feature(never_patterns)]` to the crate attributes to enable

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 | ! => {}
| ^
|
= 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: 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.
For more information about an error, try `rustc --explain E0408`.
2 changes: 0 additions & 2 deletions tests/ui/parser/match-arm-without-body.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,8 +66,6 @@ fn main() {
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) {
Expand Down
16 changes: 5 additions & 11 deletions tests/ui/parser/match-arm-without-body.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -68,19 +68,19 @@ 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!(),`
| ^^^^^^^- 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!(),`
| ^^^^^^^- 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!(),`
| ^^^^^^^- help: add a body after the pattern: `=> todo!(),`

error: `match` arm with no body
--> $DIR/match-arm-without-body.rs:51:9
Expand All @@ -98,19 +98,13 @@ 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
--> $DIR/match-arm-without-body.rs:72:9
|
LL | pat!(),
| ^^^^^^- help: add a body after the pattern: `=> todo!(),`

error: aborting due to 14 previous errors
error: aborting due to 13 previous errors

0 comments on commit 3485b66

Please sign in to comment.