[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