Skip to content

Commit

Permalink
Correctly gate the parsing of match arms without body
Browse files Browse the repository at this point in the history
  • Loading branch information
Nadrieril committed Dec 12, 2023
1 parent 21cce21 commit d40202d
Show file tree
Hide file tree
Showing 7 changed files with 145 additions and 13 deletions.
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,
}
31 changes: 30 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,36 @@ 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(span_lines) = sm.span_to_lines(span)
&& let [line_info] = span_lines.lines.as_slice()
&& let Some(line) = span_lines.file.get_line(line_info.line_index)
&& &line[line_info.start_col.0..line_info.end_col.0] == "!"
{
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
7 changes: 6 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,12 @@ 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| {
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
40 changes: 33 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,42 @@ fn main() {
unsafe {
let ptr: *const Void = NonNull::dangling().as_ptr();
match *ptr {
! //~ ERROR `!` patterns are experimental
!
//~^ ERROR `!` patterns are experimental
//~| ERROR `!` patterns are experimental
}
// Check that the gate operates even behind `cfg`.
#[cfg(FALSE)]
match *ptr {
!
//~^ ERROR `!` patterns are experimental
//~| 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 `match` arm with no body
}
match res {
Ok(_) => {}
Err(!)
//~^ ERROR `match` arm with no body
//~| ERROR `!` patterns are experimental
}

// 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
}
}
60 changes: 58 additions & 2 deletions tests/ui/feature-gates/feature-gate-never_patterns.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -25,15 +25,71 @@ 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:15: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
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`

error[E0658]: `!` patterns are experimental
--> $DIR/feature-gate-never_patterns.rs:22: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:22: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
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`

error[E0658]: `!` patterns are experimental
--> $DIR/feature-gate-never_patterns.rs:28: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:36:9
|
LL | Some(_)
| ^^^^^^^- help: add a body after the pattern: `=> todo!(),`

error[E0658]: `!` patterns are experimental
--> $DIR/feature-gate-never_patterns.rs:41: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: `match` arm with no body
--> $DIR/feature-gate-never_patterns.rs:41:9
|
LL | Err(!)
| ^^^^^^- help: add a body after the pattern: `=> todo!(),`

error: `match` arm with no body
--> $DIR/feature-gate-never_patterns.rs:50:9
|
LL | Some(_)
| ^^^^^^^- help: add a body after the pattern: `=> todo!(),`

error: aborting due to 11 previous errors

Some errors have detailed explanations: E0408, E0658.
For more information about an error, try `rustc --explain E0408`.

0 comments on commit d40202d

Please sign in to comment.