about summary refs log tree commit diff
diff options
context:
space:
mode:
authorNadrieril <nadrieril+git@gmail.com>2023-12-12 13:33:39 +0100
committerNadrieril <nadrieril+git@gmail.com>2023-12-12 14:42:04 +0100
commite274372689928972e4e78a24d615f6c4d375f7d4 (patch)
tree714874e1b0e365fd84bb3d2d86edc2ebb986ae76
parent21cce21d8c012f14cf74d5afddd795d324600dac (diff)
downloadrust-e274372689928972e4e78a24d615f6c4d375f7d4.tar.gz
rust-e274372689928972e4e78a24d615f6c4d375f7d4.zip
Correctly gate the parsing of match arms without body
-rw-r--r--compiler/rustc_ast_lowering/src/expr.rs7
-rw-r--r--compiler/rustc_ast_passes/messages.ftl4
-rw-r--r--compiler/rustc_ast_passes/src/errors.rs9
-rw-r--r--compiler/rustc_ast_passes/src/feature_gate.rs29
-rw-r--r--compiler/rustc_parse/src/parser/expr.rs7
-rw-r--r--tests/ui/feature-gates/feature-gate-never_patterns.rs65
-rw-r--r--tests/ui/feature-gates/feature-gate-never_patterns.stderr114
-rw-r--r--tests/ui/parser/match-arm-without-body.rs2
-rw-r--r--tests/ui/parser/match-arm-without-body.stderr16
9 files changed, 227 insertions, 26 deletions
diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs
index e9f88d50937..a44b408feec 100644
--- a/compiler/rustc_ast_lowering/src/expr.rs
+++ b/compiler/rustc_ast_lowering/src/expr.rs
@@ -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;
diff --git a/compiler/rustc_ast_passes/messages.ftl b/compiler/rustc_ast_passes/messages.ftl
index 876126b02ea..28bd6c2111b 100644
--- a/compiler/rustc_ast_passes/messages.ftl
+++ b/compiler/rustc_ast_passes/messages.ftl
@@ -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
 
diff --git a/compiler/rustc_ast_passes/src/errors.rs b/compiler/rustc_ast_passes/src/errors.rs
index 7f6fcb49317..928bf19759a 100644
--- a/compiler/rustc_ast_passes/src/errors.rs
+++ b/compiler/rustc_ast_passes/src/errors.rs
@@ -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,
+}
diff --git a/compiler/rustc_ast_passes/src/feature_gate.rs b/compiler/rustc_ast_passes/src/feature_gate.rs
index 897f35a5c4a..ac55c6cabd0 100644
--- a/compiler/rustc_ast_passes/src/feature_gate.rs
+++ b/compiler/rustc_ast_passes/src/feature_gate.rs
@@ -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() {
diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs
index 3c0627526be..bdaf8db1311 100644
--- a/compiler/rustc_parse/src/parser/expr.rs
+++ b/compiler/rustc_parse/src/parser/expr.rs
@@ -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).
diff --git a/tests/ui/feature-gates/feature-gate-never_patterns.rs b/tests/ui/feature-gates/feature-gate-never_patterns.rs
index ca5ce3b9489..97c68e460ea 100644
--- a/tests/ui/feature-gates/feature-gate-never_patterns.rs
+++ b/tests/ui/feature-gates/feature-gate-never_patterns.rs
@@ -12,16 +12,67 @@ 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 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 `match` arm with no body
+        //~| ERROR `!` patterns are experimental
+    }
+    match res {
+        Err(!) if false,
+        //~^ ERROR `match` arm with no body
+        //~| ERROR a guard on a never pattern will never be run
+        //~| 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
+    }
+    match Some(0) {
+        _ => {}
+        #[cfg(FALSE)]
+        Some(_) if false
+        //~^ ERROR `match` arm with no body
     }
 }
diff --git a/tests/ui/feature-gates/feature-gate-never_patterns.stderr b/tests/ui/feature-gates/feature-gate-never_patterns.stderr
index 2354a3b0476..4ba80603d9f 100644
--- a/tests/ui/feature-gates/feature-gate-never_patterns.stderr
+++ b/tests/ui/feature-gates/feature-gate-never_patterns.stderr
@@ -1,3 +1,18 @@
+error: unexpected `,` in pattern
+  --> $DIR/feature-gate-never_patterns.rs:36: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
    |
@@ -25,7 +40,36 @@ 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 |             ! => {}
    |             ^
@@ -33,7 +77,73 @@ 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:41:9
+   |
+LL |         Some(_)
+   |         ^^^^^^^- 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: `match` arm with no body
+  --> $DIR/feature-gate-never_patterns.rs:48:9
+   |
+LL |         Some(_) if false
+   |         ^^^^^^^- help: add a body after the pattern: `=> todo!(),`
+
+error[E0658]: `!` patterns are experimental
+  --> $DIR/feature-gate-never_patterns.rs:53: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:53:9
+   |
+LL |         Err(!),
+   |         ^^^^^^- help: add a body after the pattern: `=> todo!(),`
+
+error[E0658]: `!` patterns are experimental
+  --> $DIR/feature-gate-never_patterns.rs:58: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:58:9
+   |
+LL |         Err(!) if false,
+   |         ^^^^^^- help: add a body after the pattern: `=> todo!(),`
+
+error: `match` arm with no body
+  --> $DIR/feature-gate-never_patterns.rs:69:9
+   |
+LL |         Some(_)
+   |         ^^^^^^^- help: add a body after the pattern: `=> todo!(),`
+
+error: `match` arm with no body
+  --> $DIR/feature-gate-never_patterns.rs:75: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:58:19
+   |
+LL |         Err(!) if false,
+   |                   ^^^^^ help: remove this guard
+
+error: aborting due to 18 previous errors
 
 Some errors have detailed explanations: E0408, E0658.
 For more information about an error, try `rustc --explain E0408`.
diff --git a/tests/ui/parser/match-arm-without-body.rs b/tests/ui/parser/match-arm-without-body.rs
index c3487c2c658..4723abff8b6 100644
--- a/tests/ui/parser/match-arm-without-body.rs
+++ b/tests/ui/parser/match-arm-without-body.rs
@@ -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) {
diff --git a/tests/ui/parser/match-arm-without-body.stderr b/tests/ui/parser/match-arm-without-body.stderr
index 3a06ed050b5..d98c7ec2826 100644
--- a/tests/ui/parser/match-arm-without-body.stderr
+++ b/tests/ui/parser/match-arm-without-body.stderr
@@ -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
@@ -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