diff --git a/clang/include/clang/Basic/DiagnosticParseKinds.td b/clang/include/clang/Basic/DiagnosticParseKinds.td --- a/clang/include/clang/Basic/DiagnosticParseKinds.td +++ b/clang/include/clang/Basic/DiagnosticParseKinds.td @@ -295,8 +295,6 @@ 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; diff --git a/clang/lib/Parse/ParseStmt.cpp b/clang/lib/Parse/ParseStmt.cpp --- a/clang/lib/Parse/ParseStmt.cpp +++ b/clang/lib/Parse/ParseStmt.cpp @@ -882,18 +882,21 @@ // If we found a non-case statement, start by parsing it. StmtResult SubStmt; - if (Tok.isNot(tok::r_brace)) { - SubStmt = ParseStatement(/*TrailingElseLoc=*/nullptr, StmtCtx); - } 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, " ;"); + if (Tok.is(tok::r_brace)) { + // "switch (X) { case 4: }", is valid and is treated as if label was + // followed by a null statement. + 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); } - SubStmt = StmtError(); + SubStmt = Actions.ActOnNullStmt(ColonLoc); + } else { + SubStmt = ParseStatement(/*TrailingElseLoc=*/nullptr, StmtCtx); } // Install the body into the most deeply-nested case. @@ -939,15 +942,21 @@ 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. + 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); + } + 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. diff --git a/clang/test/AST/ast-dump-stmt.c b/clang/test/AST/ast-dump-stmt.c --- a/clang/test/AST/ast-dump-stmt.c +++ b/clang/test/AST/ast-dump-stmt.c @@ -169,10 +169,10 @@ void TestSwitch(int i) { switch (i) { - // CHECK: SwitchStmt 0x{{[^ ]*}} + // CHECK: SwitchStmt 0x{{[^ ]*}} // CHECK-NEXT: ImplicitCastExpr // CHECK-NEXT: DeclRefExpr 0x{{[^ ]*}} 'int' lvalue ParmVar 0x{{[^ ]*}} 'i' 'int' - // CHECK-NEXT: CompoundStmt 0x{{[^ ]*}} + // CHECK-NEXT: CompoundStmt 0x{{[^ ]*}} case 0: break; // CHECK-NEXT: CaseStmt 0x{{[^ ]*}} @@ -201,6 +201,21 @@ // CHECK-NEXT: ConstantExpr // CHECK-NEXT: IntegerLiteral 0x{{[^ ]*}} 'int' 5 // CHECK-NEXT: BreakStmt 0x{{[^ ]*}} + case 6: + // CHECK-NEXT: CaseStmt 0x{{[^ ]*}} + // CHECK-NEXT: ConstantExpr + // CHECK-NEXT: IntegerLiteral 0x{{[^ ]*}} 'int' 6 + // CHECK-NEXT: NullStmt 0x{{[^ ]*}} + } + + switch(i){ + // CHECK: SwitchStmt 0x{{[^ ]*}} + // CHECK-NEXT: ImplicitCastExpr + // CHECK-NEXT: DeclRefExpr 0x{{[^ ]*}} 'int' lvalue ParmVar 0x{{[^ ]*}} 'i' 'int' + // CHECK-NEXT: CompoundStmt 0x{{[^ ]*}} + default: + // CHECK-NEXT: DefaultStmt 0x{{[^ ]*}} + // CHECK-NEXT: NullStmt 0x{{[^ ]*}} } } diff --git a/clang/test/C/C2x/n2508.c b/clang/test/C/C2x/n2508.c --- 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 @@ } 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 --- a/clang/test/FixIt/fixit.c +++ b/clang/test/FixIt/fixit.c @@ -87,26 +87,6 @@ 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 --- 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 --- 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 @@ 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 --- a/clang/test/Parser/switch-recovery.cpp +++ b/clang/test/Parser/switch-recovery.cpp @@ -160,15 +160,15 @@ 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_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 @@ 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 --- a/clang/www/c_status.html +++ b/clang/www/c_status.html @@ -763,13 +763,7 @@ Free positioning of labels inside compound statements N2508 - -
Partial - Clang supports labels at the end of compound statements but does - not yet support case or default labels at - the end of a switch statement's compound block. -
- + Clang 16 Clarification request for C17 example of undefined behavior