diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td --- a/clang/include/clang/Basic/Attr.td +++ b/clang/include/clang/Basic/Attr.td @@ -1170,7 +1170,7 @@ def FallThrough : StmtAttr { let Spellings = [CXX11<"", "fallthrough", 201603>, C2x<"", "fallthrough">, - CXX11<"clang", "fallthrough">]; + CXX11<"clang", "fallthrough">, GCC<"fallthrough">]; // let Subjects = [NullStmt]; let Documentation = [FallthroughDocs]; } diff --git a/clang/include/clang/Parse/Parser.h b/clang/include/clang/Parse/Parser.h --- a/clang/include/clang/Parse/Parser.h +++ b/clang/include/clang/Parse/Parser.h @@ -2326,6 +2326,8 @@ /// a type-specifier other than a cv-qualifier. bool isCXXDeclarationSpecifierAType(); + bool isNullStmtWithAttributes(); + /// Determine whether the current token sequence might be /// '<' template-argument-list '>' /// rather than a less-than expression. @@ -2357,6 +2359,7 @@ TPResult TryParseFunctionDeclarator(); TPResult TryParseBracketDeclarator(); TPResult TryConsumeDeclarationSpecifier(); + TPResult TryParseNullStmtWithAttributes(); public: TypeResult ParseTypeName(SourceRange *Range = nullptr, 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 @@ -100,6 +100,11 @@ ParsedAttributesWithRange Attrs(AttrFactory); MaybeParseCXX11Attributes(Attrs, nullptr, /*MightBeObjCMessageSend*/ true); + + if(isNullStmtWithAttributes()) { + MaybeParseGNUAttributes(Attrs); + } + if (!MaybeParseOpenCLUnrollHintAttribute(Attrs)) return StmtError(); diff --git a/clang/lib/Parse/ParseTentative.cpp b/clang/lib/Parse/ParseTentative.cpp --- a/clang/lib/Parse/ParseTentative.cpp +++ b/clang/lib/Parse/ParseTentative.cpp @@ -2121,3 +2121,28 @@ return TPResult::Ambiguous; return TPResult::False; } + +Parser::TPResult Parser::TryParseNullStmtWithAttributes() { + if(Tok.isNot(tok::kw___attribute)) { + return TPResult::False; + } + ParsedAttributesWithRange attrs(AttrFactory); + ParseGNUAttributes(attrs, nullptr, nullptr); + if(attrs.size() <= 0) { + return TPResult::False; + } + for(auto &attr : attrs) { + if(!attr.isStmtAttr()) { + return TPResult::False; + } + } + if(Tok.isNot(tok::semi)) { + return TPResult::False; + } + return TPResult::True; +} + +bool Parser::isNullStmtWithAttributes() { + RevertingTentativeParsingAction PA(*this); + return TryParseNullStmtWithAttributes() == TPResult::True; +} diff --git a/clang/lib/Sema/AnalysisBasedWarnings.cpp b/clang/lib/Sema/AnalysisBasedWarnings.cpp --- a/clang/lib/Sema/AnalysisBasedWarnings.cpp +++ b/clang/lib/Sema/AnalysisBasedWarnings.cpp @@ -1215,7 +1215,7 @@ tok::r_square, tok::r_square }; - bool PreferClangAttr = !PP.getLangOpts().CPlusPlus17; + bool PreferClangAttr = !PP.getLangOpts().CPlusPlus17 && !PP.getLangOpts().C2x; StringRef MacroName; if (PreferClangAttr) @@ -1224,24 +1224,19 @@ MacroName = PP.getLastMacroWithSpelling(Loc, FallthroughTokens); if (MacroName.empty() && !PreferClangAttr) MacroName = PP.getLastMacroWithSpelling(Loc, ClangFallthroughTokens); - if (MacroName.empty()) - MacroName = PreferClangAttr ? "[[clang::fallthrough]]" : "[[fallthrough]]"; + if (MacroName.empty()) { + if (!PreferClangAttr) + MacroName = "[[fallthrough]]"; + else if (PP.getLangOpts().CPlusPlus) + MacroName = "[[clang::fallthrough]]"; + else + MacroName = "__attribute__((fallthrough))"; + } return MacroName; } static void DiagnoseSwitchLabelsFallthrough(Sema &S, AnalysisDeclContext &AC, bool PerFunction) { - // Only perform this analysis when using [[]] attributes. There is no good - // workflow for this warning when not using C++11. There is no good way to - // silence the warning (no attribute is available) unless we are using - // [[]] attributes. One could use pragmas to silence the warning, but as a - // general solution that is gross and not in the spirit of this warning. - // - // NOTE: This an intermediate solution. There are on-going discussions on - // how to properly support this warning outside of C++11 with an annotation. - if (!AC.getASTContext().getLangOpts().DoubleSquareBracketAttributes) - return; - FallthroughMapper FM(S); FM.TraverseStmt(AC.getBody()); @@ -1281,7 +1276,7 @@ SourceLocation L = Label->getBeginLoc(); if (L.isMacroID()) continue; - if (S.getLangOpts().CPlusPlus11) { + if (S.getLangOpts().CPlusPlus11 || S.getLangOpts().C99) { const Stmt *Term = B->getTerminatorStmt(); // Skip empty cases. while (B->empty() && !Term && B->succ_size() == 1) { diff --git a/clang/test/Sema/fallthrough-attr.c b/clang/test/Sema/fallthrough-attr.c new file mode 100644 --- /dev/null +++ b/clang/test/Sema/fallthrough-attr.c @@ -0,0 +1,48 @@ +// RUN: %clang_cc1 -fsyntax-only -std=gnu89 -verify -Wimplicit-fallthrough %s + +int foo(int x) +{ + int a = 0; + + switch (x) { + case 0: + a++; + case 1: + // expected-warning@-1{{unannotated fall-through between switch labels}} + //expected-note@-2{{insert 'break;' to avoid fall-through}} + a--; + case 2: + // expected-warning@-1{{unannotated fall-through between switch labels}} + // expected-note@-2{{insert 'break;' to avoid fall-through}} + break; + default: + a = 1; + } + + return 0; +} + +int bar(int x) +{ + int a = 0; + + switch (x) { + case 0: + a++; + __attribute__ ((fallthrough)); + case 1: + a--; + __attribute__ ((fallthrough)); + case 2: + break; + default: + a = 1; + } + + return 0; +} + +__attribute__ ((fallthrough)); // expected-warning {{declaration does not declare anything}} +void baz(int x) { + __attribute__ ((fallthrough)); // expected-error {{fallthrough annotation is outside switch statement}} +} diff --git a/clang/test/SemaCXX/switch-implicit-fallthrough.cpp b/clang/test/SemaCXX/switch-implicit-fallthrough.cpp --- a/clang/test/SemaCXX/switch-implicit-fallthrough.cpp +++ b/clang/test/SemaCXX/switch-implicit-fallthrough.cpp @@ -329,3 +329,15 @@ } return n; } + +int fallthrough_attribute_spelling(int n) { + switch (n) { + case 0: + n++; + __attribute__ ((fallthrough)); + case 1: + n++; + break; + } + return n; +}