[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
This commit is contained in:
Evgeny Shulgin 2022-09-19 19:26:03 +00:00
parent b63b4d8966
commit 3285f9a239
10 changed files with 99 additions and 74 deletions

View File

@ -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>;

View File

@ -2094,6 +2094,7 @@ private:
StmtResult ParseCompoundStatement(bool isStmtExpr,
unsigned ScopeFlags);
void ParseCompoundStatementLeadingPragmas();
void DiagnoseLabelAtEndOfCompoundStatement();
bool ConsumeNullStmt(StmtVector &Stmts);
StmtResult ParseCompoundStatementBody(bool isStmtExpr = false);
bool ParseParenExprOrCondition(StmtResult *InitStmt,

View File

@ -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) {

View File

@ -169,10 +169,10 @@ label3:
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>
}
}

View File

@ -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:
}
{

View File

@ -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 {{';'}}

View File

@ -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;
}

View File

@ -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 @@ label2:
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;
}

View File

@ -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 {

View File

@ -763,13 +763,7 @@ conformance.</p>
<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>