[clang] 3285f9a - [Clang] Support case and default labels at end of compound statement

Evgeny Shulgin via cfe-commits cfe-commits at lists.llvm.org
Wed Sep 21 12:37:43 PDT 2022


Author: Evgeny Shulgin
Date: 2022-09-21T19:37:22Z
New Revision: 3285f9a2392fd6bc79241b1e97b124079553e48d

URL: https://github.com/llvm/llvm-project/commit/3285f9a2392fd6bc79241b1e97b124079553e48d
DIFF: https://github.com/llvm/llvm-project/commit/3285f9a2392fd6bc79241b1e97b124079553e48d.diff

LOG: [Clang] Support case and default labels at end of compound statement

Direct continuation of https://reviews.llvm.org/D133887

Reviewed By: #clang-language-wg, aaron.ballman

Differential Revision: https://reviews.llvm.org/D134207

Added: 
    

Modified: 
    clang/include/clang/Basic/DiagnosticParseKinds.td
    clang/include/clang/Parse/Parser.h
    clang/lib/Parse/ParseStmt.cpp
    clang/test/AST/ast-dump-stmt.c
    clang/test/C/C2x/n2508.c
    clang/test/FixIt/fixit.c
    clang/test/Parser/c2x-label.c
    clang/test/Parser/cxx2b-label.cpp
    clang/test/Parser/switch-recovery.cpp
    clang/www/c_status.html

Removed: 
    


################################################################################
diff  --git a/clang/include/clang/Basic/DiagnosticParseKinds.td b/clang/include/clang/Basic/DiagnosticParseKinds.td
index 76d40c338d641..987aa050a0cef 100644
--- a/clang/include/clang/Basic/DiagnosticParseKinds.td
+++ b/clang/include/clang/Basic/DiagnosticParseKinds.td
@@ -295,8 +295,6 @@ def note_missing_selector_name : Note<
 def note_force_empty_selector_name : Note<
   "or insert whitespace before ':' to use %0 as parameter name "
   "and have an empty entry in the selector">;
-def err_switch_label_end_of_compound_statement : Error<
-  "label at end of switch compound statement: expected statement">;
 def ext_c_label_end_of_compound_statement : ExtWarn<
   "label at end of compound statement is a C2x extension">,
    InGroup<C2x>;

diff  --git a/clang/include/clang/Parse/Parser.h b/clang/include/clang/Parse/Parser.h
index 72123993be6b2..e6144ae01c779 100644
--- a/clang/include/clang/Parse/Parser.h
+++ b/clang/include/clang/Parse/Parser.h
@@ -2094,6 +2094,7 @@ class Parser : public CodeCompletionHandler {
   StmtResult ParseCompoundStatement(bool isStmtExpr,
                                     unsigned ScopeFlags);
   void ParseCompoundStatementLeadingPragmas();
+  void DiagnoseLabelAtEndOfCompoundStatement();
   bool ConsumeNullStmt(StmtVector &Stmts);
   StmtResult ParseCompoundStatementBody(bool isStmtExpr = false);
   bool ParseParenExprOrCondition(StmtResult *InitStmt,

diff  --git a/clang/lib/Parse/ParseStmt.cpp b/clang/lib/Parse/ParseStmt.cpp
index 1a3363604a33b..bc2710d7656df 100644
--- a/clang/lib/Parse/ParseStmt.cpp
+++ b/clang/lib/Parse/ParseStmt.cpp
@@ -730,15 +730,7 @@ StmtResult Parser::ParseLabeledStatement(ParsedAttributes &Attrs,
 
   // The label may have no statement following it
   if (SubStmt.isUnset() && Tok.is(tok::r_brace)) {
-    if (getLangOpts().CPlusPlus) {
-      Diag(Tok, getLangOpts().CPlusPlus2b
-                    ? diag::warn_cxx20_compat_label_end_of_compound_statement
-                    : diag::ext_cxx_label_end_of_compound_statement);
-    } else {
-      Diag(Tok, getLangOpts().C2x
-                    ? diag::warn_c2x_compat_label_end_of_compound_statement
-                    : diag::ext_c_label_end_of_compound_statement);
-    }
+    DiagnoseLabelAtEndOfCompoundStatement();
     SubStmt = Actions.ActOnNullStmt(ColonLoc);
   }
 
@@ -882,18 +874,13 @@ StmtResult Parser::ParseCaseStatement(ParsedStmtContext StmtCtx,
   // If we found a non-case statement, start by parsing it.
   StmtResult SubStmt;
 
-  if (Tok.isNot(tok::r_brace)) {
-    SubStmt = ParseStatement(/*TrailingElseLoc=*/nullptr, StmtCtx);
+  if (Tok.is(tok::r_brace)) {
+    // "switch (X) { case 4: }", is valid and is treated as if label was
+    // followed by a null statement.
+    DiagnoseLabelAtEndOfCompoundStatement();
+    SubStmt = Actions.ActOnNullStmt(ColonLoc);
   } else {
-    // Nicely diagnose the common error "switch (X) { case 4: }", which is
-    // not valid.  If ColonLoc doesn't point to a valid text location, there was
-    // another parsing error, so avoid producing extra diagnostics.
-    if (ColonLoc.isValid()) {
-      SourceLocation AfterColonLoc = PP.getLocForEndOfToken(ColonLoc);
-      Diag(AfterColonLoc, diag::err_switch_label_end_of_compound_statement)
-          << FixItHint::CreateInsertion(AfterColonLoc, " ;");
-    }
-    SubStmt = StmtError();
+    SubStmt = ParseStatement(/*TrailingElseLoc=*/nullptr, StmtCtx);
   }
 
   // Install the body into the most deeply-nested case.
@@ -939,15 +926,13 @@ StmtResult Parser::ParseDefaultStatement(ParsedStmtContext StmtCtx) {
 
   StmtResult SubStmt;
 
-  if (Tok.isNot(tok::r_brace)) {
-    SubStmt = ParseStatement(/*TrailingElseLoc=*/nullptr, StmtCtx);
+  if (Tok.is(tok::r_brace)) {
+    // "switch (X) {... default: }", is valid and is treated as if label was
+    // followed by a null statement.
+    DiagnoseLabelAtEndOfCompoundStatement();
+    SubStmt = Actions.ActOnNullStmt(ColonLoc);
   } else {
-    // Diagnose the common error "switch (X) {... default: }", which is
-    // not valid.
-    SourceLocation AfterColonLoc = PP.getLocForEndOfToken(ColonLoc);
-    Diag(AfterColonLoc, diag::err_switch_label_end_of_compound_statement)
-        << FixItHint::CreateInsertion(AfterColonLoc, " ;");
-    SubStmt = true;
+    SubStmt = ParseStatement(/*TrailingElseLoc=*/nullptr, StmtCtx);
   }
 
   // Broken sub-stmt shouldn't prevent forming the case statement properly.
@@ -1064,6 +1049,18 @@ void Parser::ParseCompoundStatementLeadingPragmas() {
 
 }
 
+void Parser::DiagnoseLabelAtEndOfCompoundStatement() {
+  if (getLangOpts().CPlusPlus) {
+    Diag(Tok, getLangOpts().CPlusPlus2b
+                  ? diag::warn_cxx20_compat_label_end_of_compound_statement
+                  : diag::ext_cxx_label_end_of_compound_statement);
+  } else {
+    Diag(Tok, getLangOpts().C2x
+                  ? diag::warn_c2x_compat_label_end_of_compound_statement
+                  : diag::ext_c_label_end_of_compound_statement);
+  }
+}
+
 /// Consume any extra semi-colons resulting in null statements,
 /// returning true if any tok::semi were consumed.
 bool Parser::ConsumeNullStmt(StmtVector &Stmts) {

diff  --git a/clang/test/AST/ast-dump-stmt.c b/clang/test/AST/ast-dump-stmt.c
index ba0c38ab18804..5c44fea2df6e7 100644
--- a/clang/test/AST/ast-dump-stmt.c
+++ b/clang/test/AST/ast-dump-stmt.c
@@ -169,10 +169,10 @@ void TestLabelsAndGoto(void) {
 
 void TestSwitch(int i) {
   switch (i) {
-  // CHECK: SwitchStmt 0x{{[^ ]*}} <line:[[@LINE-1]]:3, line:[[@LINE+32]]:3>
+  // CHECK: SwitchStmt 0x{{[^ ]*}} <line:[[@LINE-1]]:3, line:[[@LINE+37]]:3>
   // CHECK-NEXT: ImplicitCastExpr
   // CHECK-NEXT: DeclRefExpr 0x{{[^ ]*}} <col:11> 'int' lvalue ParmVar 0x{{[^ ]*}} 'i' 'int'
-  // CHECK-NEXT: CompoundStmt 0x{{[^ ]*}} <col:14, line:[[@LINE+29]]:3>
+  // CHECK-NEXT: CompoundStmt 0x{{[^ ]*}} <col:14, line:[[@LINE+34]]:3>
   case 0:
     break;
   // CHECK-NEXT: CaseStmt 0x{{[^ ]*}} <line:[[@LINE-2]]:3, line:[[@LINE-1]]:5>
@@ -201,6 +201,21 @@ void TestSwitch(int i) {
   // CHECK-NEXT: ConstantExpr
   // CHECK-NEXT: IntegerLiteral 0x{{[^ ]*}} <col:14> 'int' 5
   // CHECK-NEXT: BreakStmt 0x{{[^ ]*}} <line:[[@LINE-6]]:5>
+  case 6:
+  // CHECK-NEXT: CaseStmt 0x{{[^ ]*}} <line:[[@LINE-1]]:3, col:9>
+  // CHECK-NEXT: ConstantExpr
+  // CHECK-NEXT: IntegerLiteral 0x{{[^ ]*}} <col:8> 'int' 6
+  // CHECK-NEXT: NullStmt 0x{{[^ ]*}} <col:9>
+  }
+
+  switch(i){
+  // CHECK: SwitchStmt 0x{{[^ ]*}} <line:[[@LINE-1]]:3, line:[[@LINE+7]]:3>
+  // CHECK-NEXT: ImplicitCastExpr
+  // CHECK-NEXT: DeclRefExpr 0x{{[^ ]*}} <col:10> 'int' lvalue ParmVar 0x{{[^ ]*}} 'i' 'int'
+  // CHECK-NEXT: CompoundStmt 0x{{[^ ]*}} <col:12, line:[[@LINE+4]]:3>
+  default:
+  // CHECK-NEXT: DefaultStmt 0x{{[^ ]*}} <line:[[@LINE-1]]:3, col:10>
+  // CHECK-NEXT: NullStmt 0x{{[^ ]*}} <col:10>
   }
 }
 

diff  --git a/clang/test/C/C2x/n2508.c b/clang/test/C/C2x/n2508.c
index 1610d6e17220d..2dee0b668cb4e 100644
--- a/clang/test/C/C2x/n2508.c
+++ b/clang/test/C/C2x/n2508.c
@@ -1,6 +1,8 @@
 // RUN: %clang_cc1 -verify -std=c2x %s
 
-/* WG14 N2508: partial
+// expected-no-diagnostics
+
+/* WG14 N2508: yes
  * Free positioning of labels inside compound statements
  */
 void test() {
@@ -9,8 +11,7 @@ void test() {
   }
 
   switch (1) {
-  // FIXME: this should be accepted per C2x 6.8.2p2.
-  case 1: // expected-error {{label at end of switch compound statement: expected statement}}
+  case 1:
   }
 
   {

diff  --git a/clang/test/FixIt/fixit.c b/clang/test/FixIt/fixit.c
index eda00c35ccbb2..4e1323f35afbf 100644
--- a/clang/test/FixIt/fixit.c
+++ b/clang/test/FixIt/fixit.c
@@ -87,26 +87,6 @@ int commaAtEndOfStatement(void) {
   return 0, // expected-error {{';'}}
 }
 
-int noSemiAfterLabel(int n) {
-  switch (n) {
-    default:
-      return n % 4;
-    case 0:
-    case 1:
-    case 2:
-    // CHECK: /*FOO*/ case 3: ;
-    /*FOO*/ case 3: // expected-error {{expected statement}}
-  }
-  switch (n) {
-    case 1:
-    case 2:
-      return 0;
-    // CHECK: /*BAR*/ default: ;
-    /*BAR*/ default: // expected-error {{expected statement}}
-  }
-  return 1;
-}
-
 struct noSemiAfterStruct // expected-error {{expected ';' after struct}}
 struct noSemiAfterStruct {
   int n // expected-warning {{';'}}

diff  --git a/clang/test/Parser/c2x-label.c b/clang/test/Parser/c2x-label.c
index 6141e5bcb76df..8e276c05254e3 100644
--- a/clang/test/Parser/c2x-label.c
+++ b/clang/test/Parser/c2x-label.c
@@ -1,10 +1,30 @@
 // RUN: %clang_cc1 -fsyntax-only -std=c17 -Wc2x-compat -verify=c17 %s
 // RUN: %clang_cc1 -fsyntax-only -std=c2x -Wpre-c2x-compat -verify=c2x %s
 
-void foo() {
+void test_label_in_func() {
     int x;
 label1:
     x = 1;
 label2: label3: label4:
 } // c17-warning {{label at end of compound statement is a C2x extension}} \
      c2x-warning {{label at end of compound statement is incompatible with C standards before C2x}}
+
+int test_label_in_switch(int v) {
+    switch (v) {
+    case 1:
+        return 1;
+    case 2:
+        return 2;
+    case 3: case 4: case 5:
+    } // c17-warning {{label at end of compound statement is a C2x extension}} \
+         c2x-warning {{label at end of compound statement is incompatible with C standards before C2x}}
+
+    switch (v) {
+    case 6:
+        return 6;
+    default:
+    } // c17-warning {{label at end of compound statement is a C2x extension}} \
+         c2x-warning {{label at end of compound statement is incompatible with C standards before C2x}}
+
+    return 0;
+}

diff  --git a/clang/test/Parser/cxx2b-label.cpp b/clang/test/Parser/cxx2b-label.cpp
index 99b42b5d56316..2560c8d71b982 100644
--- a/clang/test/Parser/cxx2b-label.cpp
+++ b/clang/test/Parser/cxx2b-label.cpp
@@ -1,7 +1,7 @@
 // RUN: %clang_cc1 -fsyntax-only -verify=expected,cxx2b -std=c++2b -Wpre-c++2b-compat %s
 // RUN: %clang_cc1 -fsyntax-only -verify=expected,cxx20 -std=c++20 %s
 
-void foo() {
+void test_label_in_func() {
 label1:
     int x;
 label2:
@@ -9,3 +9,23 @@ void foo() {
 label3: label4: label5:
 } // cxx20-warning {{label at end of compound statement is a C++2b extension}} \
      cxx2b-warning {{label at end of compound statement is incompatible with C++ standards before C++2b}}
+
+int test_label_in_switch(int v) {
+    switch (v) {
+    case 1:
+        return 1;
+    case 2:
+        return 2;
+    case 3: case 4: case 5:
+    } // cxx20-warning {{label at end of compound statement is a C++2b extension}} \
+         cxx2b-warning {{label at end of compound statement is incompatible with C++ standards before C++2b}}
+
+    switch (v) {
+    case 6:
+        return 6;
+    default:
+    } // cxx20-warning {{label at end of compound statement is a C++2b extension}} \
+         cxx2b-warning {{label at end of compound statement is incompatible with C++ standards before C++2b}}
+
+    return 0;
+}

diff  --git a/clang/test/Parser/switch-recovery.cpp b/clang/test/Parser/switch-recovery.cpp
index ba83355c7928c..f422a8757d963 100644
--- a/clang/test/Parser/switch-recovery.cpp
+++ b/clang/test/Parser/switch-recovery.cpp
@@ -160,15 +160,15 @@ void test12(int x) {
 void missing_statement_case(int x) {
   switch (x) {
     case 1:
-    case 0: // expected-error {{label at end of switch compound statement: expected statement}}
-  }
+    case 0:
+  } // expected-warning {{label at end of compound statement is a C++2b extension}}
 }
 
 void missing_statement_default(int x) {
   switch (x) {
     case 0:
-    default: // expected-error {{label at end of switch compound statement: expected statement}}
-  }
+    default:
+  } // expected-warning {{label at end of compound statement is a C++2b extension}}
 }
 
 void pr19022_1() {
@@ -178,9 +178,8 @@ void pr19022_1() {
 
 void pr19022_1a(int x) {
   switch(x) {
-  case 1  // expected-error{{expected ':' after 'case'}} \
-          // expected-error{{label at end of switch compound statement: expected statement}}
-  }
+  case 1  // expected-error{{expected ':' after 'case'}}
+  } // expected-warning {{label at end of compound statement is a C++2b extension}}
 }
 
 void pr19022_1b(int x) {
@@ -210,9 +209,9 @@ int pr19022_4(int x) {
 
 void pr19022_5(int x) {
   switch(x) {
-  case 1: case // expected-error{{expected ':' after 'case'}} \
-               // expected-error{{expected statement}}
-  }  // expected-error{{expected expression}}
+  case 1: case // expected-error{{expected ':' after 'case'}}
+  }  // expected-error{{expected expression}} \
+     // expected-warning {{label at end of compound statement is a C++2b extension}}
 }
 
 namespace pr19022 {

diff  --git a/clang/www/c_status.html b/clang/www/c_status.html
index f3e0ab6171a17..395bdf5d22da9 100644
--- a/clang/www/c_status.html
+++ b/clang/www/c_status.html
@@ -763,13 +763,7 @@ <h2 id="c2x">C2x implementation status</h2>
     <tr>
       <td>Free positioning of labels inside compound statements</td>
       <td><a href="https://www.open-std.org/jtc1/sc22/wg14/www/docs/n2508.pdf">N2508</a></td>
-        <td class="partial" align="center">
-          <details><summary>Partial</summary>
-            Clang supports labels at the end of compound statements but does
-            not yet support <code>case</code> or <code>default</code> labels at
-            the end of a switch statement's compound block.
-          </details>
-        </td>
+      <td class="unreleased" align="center">Clang 16</td>
     </tr>
     <tr>
       <td>Clarification request for C17 example of undefined behavior</td>


        


More information about the cfe-commits mailing list