forked from OSchip/llvm-project
[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:
parent
b63b4d8966
commit
3285f9a239
|
@ -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>;
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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>
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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:
|
||||
}
|
||||
|
||||
{
|
||||
|
|
|
@ -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 {{';'}}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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>
|
||||
|
|
Loading…
Reference in New Issue