diff options
author | Richard Smith <richard-llvm@metafoo.co.uk> | 2012-05-03 18:27:39 +0000 |
---|---|---|
committer | Richard Smith <richard-llvm@metafoo.co.uk> | 2012-05-03 18:27:39 +0000 |
commit | 84837d5b5aa0d9d015aca1f08eb96ee27a47a84b (patch) | |
tree | 138db43cee7b666753b9a02e6595c244d4f3ff66 /clang | |
parent | b3b56bb9600d5626b706cfb3d3b5cc365cafa4dd (diff) | |
download | bcm5719-llvm-84837d5b5aa0d9d015aca1f08eb96ee27a47a84b.tar.gz bcm5719-llvm-84837d5b5aa0d9d015aca1f08eb96ee27a47a84b.zip |
Add -Wimplicit-fallthrough warning flag, which warns on fallthrough between
cases in switch statements. Also add a [[clang::fallthrough]] attribute, which
can be used to suppress the warning in the case of intentional fallthrough.
Patch by Alexander Kornienko!
The handling of C++11 attribute namespaces in this patch is temporary, and will
be replaced with a cleaner mechanism in a subsequent patch.
llvm-svn: 156086
Diffstat (limited to 'clang')
-rw-r--r-- | clang/docs/LanguageExtensions.html | 54 | ||||
-rw-r--r-- | clang/include/clang/Basic/Attr.td | 5 | ||||
-rw-r--r-- | clang/include/clang/Basic/DiagnosticGroups.td | 1 | ||||
-rw-r--r-- | clang/include/clang/Basic/DiagnosticSemaKinds.td | 25 | ||||
-rw-r--r-- | clang/include/clang/Sema/AttributeList.h | 6 | ||||
-rw-r--r-- | clang/lib/Analysis/CFG.cpp | 3 | ||||
-rw-r--r-- | clang/lib/Parse/ParseDeclCXX.cpp | 40 | ||||
-rw-r--r-- | clang/lib/Sema/AnalysisBasedWarnings.cpp | 193 | ||||
-rw-r--r-- | clang/lib/Sema/AttributeList.cpp | 9 | ||||
-rw-r--r-- | clang/lib/Sema/SemaStmtAttr.cpp | 34 | ||||
-rw-r--r-- | clang/test/SemaCXX/switch-implicit-fallthrough-cxx98.cpp | 119 | ||||
-rw-r--r-- | clang/test/SemaCXX/switch-implicit-fallthrough.cpp | 177 |
12 files changed, 635 insertions, 31 deletions
diff --git a/clang/docs/LanguageExtensions.html b/clang/docs/LanguageExtensions.html index ca3ca2a89f2..0952fa0469e 100644 --- a/clang/docs/LanguageExtensions.html +++ b/clang/docs/LanguageExtensions.html @@ -103,6 +103,11 @@ <li><a href="#__sync_swap">__sync_swap</a></li> </ul> </li> +<li><a href="#non-standard-attributes">Non-standard C++11 Attributes</a> +<ul> + <li><a href="#clang__fallthrough">The <tt>clang::fallthrough</tt> attribute</a></li> +</ul> +</li> <li><a href="#targetspecific">Target-Specific Extensions</a> <ul> <li><a href="#x86-specific">X86/X86-64 Language Extensions</a></li> @@ -1497,6 +1502,55 @@ with a <tt>__c11_</tt> prefix. The supported operations are:</p> <li><tt>__c11_atomic_fetch_xor</tt></li> </ul> +<!-- ======================================================================= --> +<h2 id="non-standard-attributes">Non-standard C++11 Attributes</h2> +<!-- ======================================================================= --> + +<p>Clang supports one non-standard C++11 attribute. It resides in <tt>clang</tt> +namespace.</p> + +<!-- ======================================================================= --> +<h3 id="clang__fallthrough">The <tt>clang::fallthrough</tt> attribute</h3> +<!-- ======================================================================= --> + +<p>The <tt>clang::fallthrough</tt> attribute is used along with +<tt>-Wimplicit-fallthrough</tt> diagnostic to annotate intentional fall-through +between switch labels. It can only be applied to a null statement placed in a +point of execution between any statement and the next switch label. It is common +to mark these places with a specific comment, but this attribute is meant to +replace comments with a more strict annotation, which can be checked by the +compiler. This attribute doesn't change semantics of the code and can be used +wherever an intended fall-through occurs, but it is designed to mimic +control-flow statements like <tt>break;</tt> so it can be placed in most places +where <tt>break;</tt> can, but only if there are no statements on execution path +between it and the next switch label.</p> +<p>Here is an example:</p> +<pre> +// compile with -Wimplicit-fallthrough +switch (n) { +case 33: + f(); +case 44: // warning: unannotated fall-through + g(); + <b>[[clang::fallthrough]];</b> +case 55: // no warning + if (x) { + h(); + break; + } + else { + i(); + <b>[[clang::fallthrough]];</b> + } +case 66: // no warning + p(); + <b>[[clang::fallthrough]];</b> // warning: fallthrough annotation does not directly + // preceed case label + q(); +case 77: // warning: unannotated fall-through + r(); +} +</pre> <!-- ======================================================================= --> <h2 id="targetspecific">Target-Specific Extensions</h2> diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td index 3638deaa4ca..3d2bebb6f6d 100644 --- a/clang/include/clang/Basic/Attr.td +++ b/clang/include/clang/Basic/Attr.td @@ -313,6 +313,11 @@ def ExtVectorType : Attr { let ASTNode = 0; } +def FallThrough : Attr { + let Spellings = ["clang___fallthrough"]; + let Subjects = [CaseStmt, DefaultStmt]; +} + def FastCall : InheritableAttr { let Spellings = ["fastcall", "__fastcall"]; } diff --git a/clang/include/clang/Basic/DiagnosticGroups.td b/clang/include/clang/Basic/DiagnosticGroups.td index 234412af926..17852ae1e1c 100644 --- a/clang/include/clang/Basic/DiagnosticGroups.td +++ b/clang/include/clang/Basic/DiagnosticGroups.td @@ -208,6 +208,7 @@ def MethodDuplicate : DiagGroup<"duplicate-method-match">; def CoveredSwitchDefault : DiagGroup<"covered-switch-default">; def SwitchEnum : DiagGroup<"switch-enum">; def Switch : DiagGroup<"switch">; +def ImplicitFallthrough : DiagGroup<"implicit-fallthrough">; def Trigraphs : DiagGroup<"trigraphs">; def : DiagGroup<"type-limits">; diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 31fb03c2e42..4af3af1da85 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -5246,6 +5246,31 @@ def warn_missing_cases : Warning< "%0 enumeration values not handled in switch: %1, %2, %3...">, InGroup<Switch>; +def warn_unannotated_fallthrough : Warning< + "unannotated fall-through between switch labels">, + InGroup<ImplicitFallthrough>, DefaultIgnore; +def note_insert_fallthrough_fixit : Note< + "insert '[[clang::fallthrough]];' to silence this warning">, + InGroup<ImplicitFallthrough>, DefaultIgnore; +def note_insert_break_fixit : Note< + "insert 'break;' to avoid fall-through">, + InGroup<ImplicitFallthrough>, DefaultIgnore; +def err_fallthrough_attr_wrong_target : Error< + "clang::fallthrough attribute is only allowed on empty statements">, + InGroup<IgnoredAttributes>; +def note_fallthrough_insert_semi_fixit : Note< + "did you forget ';'?">, + InGroup<IgnoredAttributes>; +def err_fallthrough_attr_outside_switch : Error< + "fallthrough annotation is outside switch statement">, + InGroup<IgnoredAttributes>; +def warn_fallthrough_attr_invalid_placement : Warning< + "fallthrough annotation does not directly precede switch label">, + InGroup<ImplicitFallthrough>; +def warn_fallthrough_attr_unreachable : Warning< + "fallthrough annotation in unreachable code">, + InGroup<ImplicitFallthrough>; + def warn_unreachable_default : Warning< "default label in switch which covers all enumeration values">, InGroup<CoveredSwitchDefault>, DefaultIgnore; diff --git a/clang/include/clang/Sema/AttributeList.h b/clang/include/clang/Sema/AttributeList.h index 4cd0f75d620..ef6cf1c09ea 100644 --- a/clang/include/clang/Sema/AttributeList.h +++ b/clang/include/clang/Sema/AttributeList.h @@ -131,7 +131,7 @@ private: UsedAsTypeAttr(false), IsAvailability(false), NextInPosition(0), NextInPool(0) { if (numArgs) memcpy(getArgsBuffer(), args, numArgs * sizeof(Expr*)); - AttrKind = getKind(getName()); + AttrKind = getKind(getName(), getScopeName()); } AttributeList(IdentifierInfo *attrName, SourceRange attrRange, @@ -152,7 +152,7 @@ private: new (&getAvailabilitySlot(IntroducedSlot)) AvailabilityChange(introduced); new (&getAvailabilitySlot(DeprecatedSlot)) AvailabilityChange(deprecated); new (&getAvailabilitySlot(ObsoletedSlot)) AvailabilityChange(obsoleted); - AttrKind = getKind(getName()); + AttrKind = getKind(getName(), getScopeName()); } friend class AttributePool; @@ -188,7 +188,7 @@ public: void setUsedAsTypeAttr() { UsedAsTypeAttr = true; } Kind getKind() const { return Kind(AttrKind); } - static Kind getKind(const IdentifierInfo *Name); + static Kind getKind(const IdentifierInfo *Name, const IdentifierInfo *Scope); AttributeList *getNext() const { return NextInPosition; } void setNext(AttributeList *N) { NextInPosition = N; } diff --git a/clang/lib/Analysis/CFG.cpp b/clang/lib/Analysis/CFG.cpp index 61c2a8709d3..9a50578c4fb 100644 --- a/clang/lib/Analysis/CFG.cpp +++ b/clang/lib/Analysis/CFG.cpp @@ -1070,9 +1070,6 @@ CFGBlock *CFGBuilder::Visit(Stmt * S, AddStmtChoice asc) { case Stmt::LambdaExprClass: return VisitLambdaExpr(cast<LambdaExpr>(S), asc); - case Stmt::AttributedStmtClass: - return Visit(cast<AttributedStmt>(S)->getSubStmt(), asc); - case Stmt::MemberExprClass: return VisitMemberExpr(cast<MemberExpr>(S), asc); diff --git a/clang/lib/Parse/ParseDeclCXX.cpp b/clang/lib/Parse/ParseDeclCXX.cpp index 3f06919bf60..2a641413536 100644 --- a/clang/lib/Parse/ParseDeclCXX.cpp +++ b/clang/lib/Parse/ParseDeclCXX.cpp @@ -2870,28 +2870,30 @@ void Parser::ParseCXX11AttributeSpecifier(ParsedAttributes &attrs, } bool AttrParsed = false; - // No scoped names are supported; ideally we could put all non-standard - // attributes into namespaces. - if (!ScopeName) { - switch (AttributeList::getKind(AttrName)) { - // No arguments - case AttributeList::AT_carries_dependency: - case AttributeList::AT_noreturn: { - if (Tok.is(tok::l_paren)) { - Diag(Tok.getLocation(), diag::err_cxx11_attribute_forbids_arguments) - << AttrName->getName(); - break; - } - - attrs.addNew(AttrName, AttrLoc, 0, AttrLoc, 0, - SourceLocation(), 0, 0, false, true); - AttrParsed = true; + switch (AttributeList::getKind(AttrName, ScopeName)) { + // No arguments + case AttributeList::AT_carries_dependency: + // FIXME: implement generic support of attributes with C++11 syntax + // see Parse/ParseDecl.cpp: ParseGNUAttributes + case AttributeList::AT_clang___fallthrough: + case AttributeList::AT_noreturn: { + if (Tok.is(tok::l_paren)) { + Diag(Tok.getLocation(), diag::err_cxx11_attribute_forbids_arguments) + << AttrName->getName(); break; } - // Silence warnings - default: break; - } + attrs.addNew(AttrName, + SourceRange(ScopeLoc.isValid() ? ScopeLoc : AttrLoc, + AttrLoc), + ScopeName, ScopeLoc, 0, + SourceLocation(), 0, 0, false, true); + AttrParsed = true; + break; + } + + // Silence warnings + default: break; } // Skip the entire parameter clause, if any diff --git a/clang/lib/Sema/AnalysisBasedWarnings.cpp b/clang/lib/Sema/AnalysisBasedWarnings.cpp index c3b802e4db4..6e93030382a 100644 --- a/clang/lib/Sema/AnalysisBasedWarnings.cpp +++ b/clang/lib/Sema/AnalysisBasedWarnings.cpp @@ -27,6 +27,7 @@ #include "clang/AST/StmtCXX.h" #include "clang/AST/EvaluatedExprVisitor.h" #include "clang/AST/StmtVisitor.h" +#include "clang/AST/RecursiveASTVisitor.h" #include "clang/Analysis/AnalysisContext.h" #include "clang/Analysis/CFG.h" #include "clang/Analysis/Analyses/ReachableCode.h" @@ -42,7 +43,9 @@ #include "llvm/ADT/StringRef.h" #include "llvm/Support/Casting.h" #include <algorithm> +#include <iterator> #include <vector> +#include <deque> using namespace clang; @@ -522,6 +525,188 @@ static bool DiagnoseUninitializedUse(Sema &S, const VarDecl *VD, return true; } +namespace { + class FallthroughMapper : public RecursiveASTVisitor<FallthroughMapper> { + public: + FallthroughMapper(Sema &S) + : FoundSwitchStatements(false), + S(S) { + } + + bool foundSwitchStatements() const { return FoundSwitchStatements; } + + void markFallthroughVisited(const AttributedStmt *Stmt) { + bool Found = FallthroughStmts.erase(Stmt); + assert(Found); + } + + typedef llvm::SmallPtrSet<const AttributedStmt*, 8> AttrStmts; + + const AttrStmts &getFallthroughStmts() const { + return FallthroughStmts; + } + + bool checkFallThroughIntoBlock(const CFGBlock &B, int &AnnotatedCnt) { + int UnannotatedCnt = 0; + AnnotatedCnt = 0; + + std::deque<const CFGBlock*> BlockQueue; + + std::copy(B.pred_begin(), B.pred_end(), std::back_inserter(BlockQueue)); + + while (!BlockQueue.empty()) { + const CFGBlock *P = BlockQueue.front(); + BlockQueue.pop_front(); + + const Stmt *Term = P->getTerminator(); + if (Term && isa<SwitchStmt>(Term)) + continue; // Switch statement, good. + + const SwitchCase *SW = dyn_cast_or_null<SwitchCase>(P->getLabel()); + if (SW && SW->getSubStmt() == B.getLabel() && P->begin() == P->end()) + continue; // Previous case label has no statements, good. + + if (P->pred_begin() == P->pred_end()) { // The block is unreachable. + // This only catches trivially unreachable blocks. + for (CFGBlock::const_iterator ElIt = P->begin(), ElEnd = P->end(); + ElIt != ElEnd; ++ElIt) { + if (const CFGStmt *CS = ElIt->getAs<CFGStmt>()){ + if (const AttributedStmt *AS = asFallThroughAttr(CS->getStmt())) { + S.Diag(AS->getLocStart(), + diag::warn_fallthrough_attr_unreachable); + markFallthroughVisited(AS); + ++AnnotatedCnt; + } + // Don't care about other unreachable statements. + } + } + // If there are no unreachable statements, this may be a special + // case in CFG: + // case X: { + // A a; // A has a destructor. + // break; + // } + // // <<<< This place is represented by a 'hanging' CFG block. + // case Y: + continue; + } + + const Stmt *LastStmt = getLastStmt(*P); + if (const AttributedStmt *AS = asFallThroughAttr(LastStmt)) { + markFallthroughVisited(AS); + ++AnnotatedCnt; + continue; // Fallthrough annotation, good. + } + + if (!LastStmt) { // This block contains no executable statements. + // Traverse its predecessors. + std::copy(P->pred_begin(), P->pred_end(), + std::back_inserter(BlockQueue)); + continue; + } + + ++UnannotatedCnt; + } + return !!UnannotatedCnt; + } + + // RecursiveASTVisitor setup. + bool shouldWalkTypesOfTypeLocs() const { return false; } + + bool VisitAttributedStmt(AttributedStmt *S) { + if (asFallThroughAttr(S)) + FallthroughStmts.insert(S); + return true; + } + + bool VisitSwitchStmt(SwitchStmt *S) { + FoundSwitchStatements = true; + return true; + } + + private: + + static const AttributedStmt *asFallThroughAttr(const Stmt *S) { + if (const AttributedStmt *AS = dyn_cast_or_null<AttributedStmt>(S)) { + if (hasSpecificAttr<FallThroughAttr>(AS->getAttrs())) + return AS; + } + return 0; + } + + static const Stmt *getLastStmt(const CFGBlock &B) { + if (const Stmt *Term = B.getTerminator()) + return Term; + for (CFGBlock::const_reverse_iterator ElemIt = B.rbegin(), + ElemEnd = B.rend(); + ElemIt != ElemEnd; ++ElemIt) { + if (const CFGStmt *CS = ElemIt->getAs<CFGStmt>()) + return CS->getStmt(); + } + // Workaround to detect a statement thrown out by CFGBuilder: + // case X: {} case Y: + // case X: ; case Y: + if (const SwitchCase *SW = dyn_cast_or_null<SwitchCase>(B.getLabel())) + if (!isa<SwitchCase>(SW->getSubStmt())) + return SW->getSubStmt(); + + return 0; + } + + bool FoundSwitchStatements; + AttrStmts FallthroughStmts; + Sema &S; + }; +} + +static void DiagnoseSwitchLabelsFallthrough(Sema &S, AnalysisDeclContext &AC) { + FallthroughMapper FM(S); + FM.TraverseStmt(AC.getBody()); + + if (!FM.foundSwitchStatements()) + return; + + CFG *Cfg = AC.getCFG(); + + if (!Cfg) + return; + + int AnnotatedCnt; + + for (CFG::reverse_iterator I = Cfg->rbegin(), E = Cfg->rend(); I != E; ++I) { + const CFGBlock &B = **I; + const Stmt *Label = B.getLabel(); + + if (!Label || !isa<SwitchCase>(Label)) + continue; + + if (!FM.checkFallThroughIntoBlock(B, AnnotatedCnt)) + continue; + + S.Diag(Label->getLocStart(), diag::warn_unannotated_fallthrough); + + if (!AnnotatedCnt) { + SourceLocation L = Label->getLocStart(); + if (L.isMacroID()) + continue; + if (S.getLangOpts().CPlusPlus0x) { + S.Diag(L, diag::note_insert_fallthrough_fixit) << + FixItHint::CreateInsertion(L, "[[clang::fallthrough]]; "); + } + S.Diag(L, diag::note_insert_break_fixit) << + FixItHint::CreateInsertion(L, "break; "); + } + } + + const FallthroughMapper::AttrStmts &Fallthroughs = FM.getFallthroughStmts(); + for (FallthroughMapper::AttrStmts::const_iterator I = Fallthroughs.begin(), + E = Fallthroughs.end(); + I != E; ++I) { + S.Diag((*I)->getLocStart(), diag::warn_fallthrough_attr_invalid_placement); + } + +} + typedef std::pair<const Expr*, bool> UninitUse; namespace { @@ -861,7 +1046,8 @@ AnalysisBasedWarnings::IssueWarnings(sema::AnalysisBasedWarnings::Policy P, .setAlwaysAdd(Stmt::CStyleCastExprClass) .setAlwaysAdd(Stmt::DeclRefExprClass) .setAlwaysAdd(Stmt::ImplicitCastExprClass) - .setAlwaysAdd(Stmt::UnaryOperatorClass); + .setAlwaysAdd(Stmt::UnaryOperatorClass) + .setAlwaysAdd(Stmt::AttributedStmtClass); } // Construct the analysis context with the specified CFG build options. @@ -973,6 +1159,11 @@ AnalysisBasedWarnings::IssueWarnings(sema::AnalysisBasedWarnings::Policy P, } } + if (Diags.getDiagnosticLevel(diag::warn_unannotated_fallthrough, + D->getLocStart()) != DiagnosticsEngine::Ignored) { + DiagnoseSwitchLabelsFallthrough(S, AC); + } + // Collect statistics about the CFG if it was built. if (S.CollectStats && AC.isCFGBuilt()) { ++NumFunctionsAnalyzed; diff --git a/clang/lib/Sema/AttributeList.cpp b/clang/lib/Sema/AttributeList.cpp index 101e0384fa7..8e7029350ab 100644 --- a/clang/lib/Sema/AttributeList.cpp +++ b/clang/lib/Sema/AttributeList.cpp @@ -15,6 +15,7 @@ #include "clang/AST/Expr.h" #include "clang/Basic/IdentifierTable.h" #include "llvm/ADT/StringSwitch.h" +#include "llvm/ADT/SmallString.h" using namespace clang; size_t AttributeList::allocated_size() const { @@ -99,7 +100,8 @@ AttributePool::createIntegerAttribute(ASTContext &C, IdentifierInfo *Name, #include "clang/Sema/AttrParsedAttrKinds.inc" -AttributeList::Kind AttributeList::getKind(const IdentifierInfo *Name) { +AttributeList::Kind AttributeList::getKind(const IdentifierInfo *Name, + const IdentifierInfo *ScopeName) { StringRef AttrName = Name->getName(); // Normalize the attribute name, __foo__ becomes foo. @@ -107,5 +109,10 @@ AttributeList::Kind AttributeList::getKind(const IdentifierInfo *Name) { AttrName.size() >= 4) AttrName = AttrName.substr(2, AttrName.size() - 4); + // FIXME: implement attribute namespacing correctly. + SmallString<64> Buf; + if (ScopeName) + AttrName = ((Buf += ScopeName->getName()) += "___") += AttrName; + return ::getAttrKind(AttrName); } diff --git a/clang/lib/Sema/SemaStmtAttr.cpp b/clang/lib/Sema/SemaStmtAttr.cpp index 21c329758eb..912d7c64828 100644 --- a/clang/lib/Sema/SemaStmtAttr.cpp +++ b/clang/lib/Sema/SemaStmtAttr.cpp @@ -15,20 +15,46 @@ #include "TargetAttributesSema.h" #include "clang/AST/ASTContext.h" #include "clang/Basic/SourceManager.h" +#include "clang/Lex/Lexer.h" #include "clang/Sema/DelayedDiagnostic.h" #include "clang/Sema/Lookup.h" +#include "clang/Sema/ScopeInfo.h" #include "llvm/ADT/StringExtras.h" + using namespace clang; using namespace sema; +static Attr *handleFallThroughAttr(Sema &S, Stmt *St, const AttributeList &A, + SourceRange Range) { + if (!isa<NullStmt>(St)) { + S.Diag(A.getRange().getBegin(), diag::err_fallthrough_attr_wrong_target) + << St->getLocStart(); + if (isa<SwitchCase>(St)) { + SourceLocation L = Lexer::getLocForEndOfToken(Range.getEnd(), 0, + S.getSourceManager(), S.getLangOpts()); + S.Diag(L, diag::note_fallthrough_insert_semi_fixit) + << FixItHint::CreateInsertion(L, ";"); + } + return 0; + } + if (S.getCurFunction()->SwitchStack.empty()) { + S.Diag(A.getRange().getBegin(), diag::err_fallthrough_attr_outside_switch); + return 0; + } + return ::new (S.Context) FallThroughAttr(A.getRange(), S.Context); +} + -static Attr *ProcessStmtAttribute(Sema &S, Stmt *St, const AttributeList &A) { +static Attr *ProcessStmtAttribute(Sema &S, Stmt *St, const AttributeList &A, + SourceRange Range) { switch (A.getKind()) { + case AttributeList::AT_clang___fallthrough: + return handleFallThroughAttr(S, St, A, Range); default: // if we're here, then we parsed an attribute, but didn't recognize it as a // statement attribute => it is declaration attribute - S.Diag(A.getRange().getBegin(), diag::warn_attribute_invalid_on_stmt) << - A.getName()->getName(); + S.Diag(A.getRange().getBegin(), diag::warn_attribute_invalid_on_stmt) + << A.getName()->getName() << St->getLocStart(); return 0; } } @@ -37,7 +63,7 @@ StmtResult Sema::ProcessStmtAttributes(Stmt *S, AttributeList *AttrList, SourceRange Range) { AttrVec Attrs; for (const AttributeList* l = AttrList; l; l = l->getNext()) { - if (Attr *a = ProcessStmtAttribute(*this, S, *l)) + if (Attr *a = ProcessStmtAttribute(*this, S, *l, Range)) Attrs.push_back(a); } diff --git a/clang/test/SemaCXX/switch-implicit-fallthrough-cxx98.cpp b/clang/test/SemaCXX/switch-implicit-fallthrough-cxx98.cpp new file mode 100644 index 00000000000..14ffcef704d --- /dev/null +++ b/clang/test/SemaCXX/switch-implicit-fallthrough-cxx98.cpp @@ -0,0 +1,119 @@ +// RUN: %clang_cc1 -fsyntax-only -verify -std=c++98 -Wimplicit-fallthrough %s + + +int fallthrough(int n) { + switch (n / 10) { + if (n - 1) { + n = 100; + } else if (n - 2) { + n = 101; + } else if (n - 3) { + n = 102; + } + case -1: // expected-warning{{unannotated fall-through between switch labels}} expected-note{{insert 'break;' to avoid fall-through}} + ; + case 0: {// expected-warning{{unannotated fall-through between switch labels}} expected-note{{insert 'break;' to avoid fall-through}} + } + case 1: // expected-warning{{unannotated fall-through between switch labels}} expected-note{{insert 'break;' to avoid fall-through}} + n += 100 ; + case 3: // expected-warning{{unannotated fall-through between switch labels}} expected-note{{insert 'break;' to avoid fall-through}} + if (n > 0) + n += 200; + case 4: // expected-warning{{unannotated fall-through between switch labels}} expected-note{{insert 'break;' to avoid fall-through}} + if (n < 0) + ; + case 5: // expected-warning{{unannotated fall-through between switch labels}} expected-note{{insert 'break;' to avoid fall-through}} + switch (n) { + case 111: + break; + case 112: + break; + case 113: + break ; + } + case 6: // expected-warning{{unannotated fall-through between switch labels}} expected-note{{insert 'break;' to avoid fall-through}} + n += 300; + } + switch (n / 30) { + case 11: + case 12: // no warning here, intended fall-through, no statement between labels + n += 1600; + } + switch (n / 40) { + case 13: + if (n % 2 == 0) { + return 1; + } else { + return 2; + } + case 15: // no warning here, there's no fall-through + n += 3200; + } + switch (n / 50) { + case 17: { + if (n % 2 == 0) { + return 1; + } else { + return 2; + } + } + case 19: { // no warning here, there's no fall-through + n += 6400; + return 3; + } + case 21: { // no warning here, there's no fall-through + break; + } + case 23: // no warning here, there's no fall-through + n += 128000; + break; + case 25: // no warning here, there's no fall-through + break; + } + + return n; +} + +class ClassWithDtor { +public: + ~ClassWithDtor() {} +}; + +void fallthrough2(int n) { + switch (n) { + case 0: + { + ClassWithDtor temp; + break; + } + default: // no warning here, there's no fall-through + break; + } +} + +#define MY_SWITCH(X, Y, Z, U, V) switch (X) { case Y: Z; case U: V; } +#define MY_SWITCH2(X, Y, Z) switch (X) { Y; Z; } +#define MY_CASE(X, Y) case X: Y +#define MY_CASE2(X, Y, U, V) case X: Y; case U: V + +int fallthrough_macro1(int n) { + MY_SWITCH(n, 13, n *= 2, 14, break) // expected-warning{{unannotated fall-through between switch labels}} + + switch (n + 1) { + MY_CASE(33, n += 2); + MY_CASE(44, break); // expected-warning{{unannotated fall-through between switch labels}} + MY_CASE(55, n += 3); + } + + switch (n + 3) { + MY_CASE(333, return 333); + MY_CASE2(444, n += 44, 4444, break); // expected-warning{{unannotated fall-through between switch labels}} + MY_CASE(555, n += 33); + } + + MY_SWITCH2(n + 4, MY_CASE(17, n *= 3), MY_CASE(19, break)) // expected-warning{{unannotated fall-through between switch labels}} + + MY_SWITCH2(n + 5, MY_CASE(21, break), MY_CASE2(23, n *= 7, 25, break)) // expected-warning{{unannotated fall-through between switch labels}} + + return n; +} diff --git a/clang/test/SemaCXX/switch-implicit-fallthrough.cpp b/clang/test/SemaCXX/switch-implicit-fallthrough.cpp new file mode 100644 index 00000000000..db5508a1257 --- /dev/null +++ b/clang/test/SemaCXX/switch-implicit-fallthrough.cpp @@ -0,0 +1,177 @@ +// RUN: %clang_cc1 -fsyntax-only -verify -std=c++11 -Wimplicit-fallthrough %s + + +int fallthrough(int n) { + switch (n / 10) { + if (n - 1) { + n = 100; + } else if (n - 2) { + n = 101; + } else if (n - 3) { + n = 102; + } + case -1: // expected-warning{{unannotated fall-through between switch labels}} expected-note{{insert '[[clang::fallthrough]];' to silence this warning}} expected-note{{insert 'break;' to avoid fall-through}} + ; + case 0: {// expected-warning{{unannotated fall-through between switch labels}} expected-note{{insert '[[clang::fallthrough]];' to silence this warning}} expected-note{{insert 'break;' to avoid fall-through}} + } + case 1: // expected-warning{{unannotated fall-through between switch labels}} expected-note{{insert '[[clang::fallthrough]];' to silence this warning}} expected-note{{insert 'break;' to avoid fall-through}} + n += 100 ; + case 3: // expected-warning{{unannotated fall-through between switch labels}} expected-note{{insert '[[clang::fallthrough]];' to silence this warning}} expected-note{{insert 'break;' to avoid fall-through}} + if (n > 0) + n += 200; + case 4: // expected-warning{{unannotated fall-through between switch labels}} expected-note{{insert '[[clang::fallthrough]];' to silence this warning}} expected-note{{insert 'break;' to avoid fall-through}} + if (n < 0) + ; + case 5: // expected-warning{{unannotated fall-through between switch labels}} expected-note{{insert '[[clang::fallthrough]];' to silence this warning}} expected-note{{insert 'break;' to avoid fall-through}} + switch (n) { + case 111: + break; + case 112: + break; + case 113: + break ; + } + case 6: // expected-warning{{unannotated fall-through between switch labels}} expected-note{{insert '[[clang::fallthrough]];' to silence this warning}} expected-note{{insert 'break;' to avoid fall-through}} + n += 300; + } + switch (n / 20) { + case 7: + n += 400; + [[clang::fallthrough]]; + case 9: // no warning here, intended fall-through marked with an attribute + n += 800; + [[clang::fallthrough]]; + default: { // no warning here, intended fall-through marked with an attribute + if (n % 2 == 0) { + return 1; + } else { + [[clang::fallthrough]]; + } + } + case 10: // no warning here, intended fall-through marked with an attribute + if (n % 3 == 0) { + n %= 3; + } else { + [[clang::fallthrough]]; + } + case 110: // expected-warning{{unannotated fall-through between switch labels}} but no fix-it hint as we have one fall-through annotation! + n += 800; + } + switch (n / 30) { + case 11: + case 12: // no warning here, intended fall-through, no statement between labels + n += 1600; + } + switch (n / 40) { + case 13: + if (n % 2 == 0) { + return 1; + } else { + return 2; + } + case 15: // no warning here, there's no fall-through + n += 3200; + } + switch (n / 50) { + case 17: { + if (n % 2 == 0) { + return 1; + } else { + return 2; + } + } + case 19: { // no warning here, there's no fall-through + n += 6400; + return 3; + } + case 21: { // no warning here, there's no fall-through + break; + } + case 23: // no warning here, there's no fall-through + n += 128000; + break; + case 25: // no warning here, there's no fall-through + break; + } + + return n; +} + +class ClassWithDtor { +public: + ~ClassWithDtor() {} +}; + +void fallthrough2(int n) { + switch (n) { + case 0: + { + ClassWithDtor temp; + break; + } + default: // no warning here, there's no fall-through + break; + } +} + +#define MY_SWITCH(X, Y, Z, U, V) switch (X) { case Y: Z; case U: V; } +#define MY_SWITCH2(X, Y, Z) switch (X) { Y; Z; } +#define MY_CASE(X, Y) case X: Y +#define MY_CASE2(X, Y, U, V) case X: Y; case U: V + +int fallthrough_macro1(int n) { + MY_SWITCH(n, 13, n *= 2, 14, break) // expected-warning{{unannotated fall-through between switch labels}} + + switch (n + 1) { + MY_CASE(33, n += 2); + MY_CASE(44, break); // expected-warning{{unannotated fall-through between switch labels}} + MY_CASE(55, n += 3); + } + + switch (n + 3) { + MY_CASE(333, return 333); + MY_CASE2(444, n += 44, 4444, break); // expected-warning{{unannotated fall-through between switch labels}} + MY_CASE(555, n += 33); + } + + MY_SWITCH2(n + 4, MY_CASE(17, n *= 3), MY_CASE(19, break)) // expected-warning{{unannotated fall-through between switch labels}} + + MY_SWITCH2(n + 5, MY_CASE(21, break), MY_CASE2(23, n *= 7, 25, break)) // expected-warning{{unannotated fall-through between switch labels}} + + return n; +} + +int fallthrough_position(int n) { + switch (n) { + [[clang::fallthrough]]; // expected-warning{{fallthrough annotation in unreachable code}} + case 221: + [[clang::fallthrough]]; // expected-warning{{fallthrough annotation does not directly precede switch label}} + return 1; + [[clang::fallthrough]]; // expected-warning{{fallthrough annotation in unreachable code}} + case 222: + [[clang::fallthrough]]; // expected-warning{{fallthrough annotation does not directly precede switch label}} + n += 400; + case 223: // expected-warning{{unannotated fall-through between switch labels}} expected-note{{insert '[[clang::fallthrough]];' to silence this warning}} expected-note{{insert 'break;' to avoid fall-through}} + [[clang::fallthrough]]; // expected-warning{{fallthrough annotation does not directly precede switch label}} + } + return n; +} + +int fallthrough_targets(int n) { + [[clang::fallthrough]]; // expected-error{{fallthrough annotation is outside switch statement}} + + [[clang::fallthrough]] // expected-error{{fallthrough attribute is only allowed on empty statements}} + switch (n) { + case 121: + n += 400; + [[clang::fallthrough]]; // no warning here, correct target + case 123: + [[clang::fallthrough]] // expected-error{{fallthrough attribute is only allowed on empty statements}} + n += 800; + break; + [[clang::fallthrough]] // expected-error{{fallthrough attribute is only allowed on empty statements}} expected-note{{did you forget ';'?}} + case 125: + n += 1600; + } + return n; +} |