[cfe-commits] r156086 - in /cfe/trunk: docs/LanguageExtensions.html include/clang/Basic/Attr.td include/clang/Basic/DiagnosticGroups.td include/clang/Basic/DiagnosticSemaKinds.td include/clang/Sema/AttributeList.h lib/Analysis/CFG.cpp lib/Parse/ParseDeclCXX.cpp lib/Sema/AnalysisBasedWarnings.cpp lib/Sema/AttributeList.cpp lib/Sema/SemaStmtAttr.cpp test/SemaCXX/switch-implicit-fallthrough-cxx98.cpp test/SemaCXX/switch-implicit-fallthrough.cpp
Richard Smith
richard-llvm at metafoo.co.uk
Thu May 3 11:27:39 PDT 2012
Author: rsmith
Date: Thu May 3 13:27:39 2012
New Revision: 156086
URL: http://llvm.org/viewvc/llvm-project?rev=156086&view=rev
Log:
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.
Added:
cfe/trunk/test/SemaCXX/switch-implicit-fallthrough-cxx98.cpp
cfe/trunk/test/SemaCXX/switch-implicit-fallthrough.cpp
Modified:
cfe/trunk/docs/LanguageExtensions.html
cfe/trunk/include/clang/Basic/Attr.td
cfe/trunk/include/clang/Basic/DiagnosticGroups.td
cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td
cfe/trunk/include/clang/Sema/AttributeList.h
cfe/trunk/lib/Analysis/CFG.cpp
cfe/trunk/lib/Parse/ParseDeclCXX.cpp
cfe/trunk/lib/Sema/AnalysisBasedWarnings.cpp
cfe/trunk/lib/Sema/AttributeList.cpp
cfe/trunk/lib/Sema/SemaStmtAttr.cpp
Modified: cfe/trunk/docs/LanguageExtensions.html
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/docs/LanguageExtensions.html?rev=156086&r1=156085&r2=156086&view=diff
==============================================================================
--- cfe/trunk/docs/LanguageExtensions.html (original)
+++ cfe/trunk/docs/LanguageExtensions.html Thu May 3 13:27:39 2012
@@ -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 @@
<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>
Modified: cfe/trunk/include/clang/Basic/Attr.td
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/Attr.td?rev=156086&r1=156085&r2=156086&view=diff
==============================================================================
--- cfe/trunk/include/clang/Basic/Attr.td (original)
+++ cfe/trunk/include/clang/Basic/Attr.td Thu May 3 13:27:39 2012
@@ -313,6 +313,11 @@
let ASTNode = 0;
}
+def FallThrough : Attr {
+ let Spellings = ["clang___fallthrough"];
+ let Subjects = [CaseStmt, DefaultStmt];
+}
+
def FastCall : InheritableAttr {
let Spellings = ["fastcall", "__fastcall"];
}
Modified: cfe/trunk/include/clang/Basic/DiagnosticGroups.td
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/DiagnosticGroups.td?rev=156086&r1=156085&r2=156086&view=diff
==============================================================================
--- cfe/trunk/include/clang/Basic/DiagnosticGroups.td (original)
+++ cfe/trunk/include/clang/Basic/DiagnosticGroups.td Thu May 3 13:27:39 2012
@@ -208,6 +208,7 @@
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">;
Modified: cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td?rev=156086&r1=156085&r2=156086&view=diff
==============================================================================
--- cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td (original)
+++ cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td Thu May 3 13:27:39 2012
@@ -5246,6 +5246,31 @@
"%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;
Modified: cfe/trunk/include/clang/Sema/AttributeList.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Sema/AttributeList.h?rev=156086&r1=156085&r2=156086&view=diff
==============================================================================
--- cfe/trunk/include/clang/Sema/AttributeList.h (original)
+++ cfe/trunk/include/clang/Sema/AttributeList.h Thu May 3 13:27:39 2012
@@ -131,7 +131,7 @@
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 @@
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 @@
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; }
Modified: cfe/trunk/lib/Analysis/CFG.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Analysis/CFG.cpp?rev=156086&r1=156085&r2=156086&view=diff
==============================================================================
--- cfe/trunk/lib/Analysis/CFG.cpp (original)
+++ cfe/trunk/lib/Analysis/CFG.cpp Thu May 3 13:27:39 2012
@@ -1070,9 +1070,6 @@
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);
Modified: cfe/trunk/lib/Parse/ParseDeclCXX.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Parse/ParseDeclCXX.cpp?rev=156086&r1=156085&r2=156086&view=diff
==============================================================================
--- cfe/trunk/lib/Parse/ParseDeclCXX.cpp (original)
+++ cfe/trunk/lib/Parse/ParseDeclCXX.cpp Thu May 3 13:27:39 2012
@@ -2870,28 +2870,30 @@
}
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
Modified: cfe/trunk/lib/Sema/AnalysisBasedWarnings.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/AnalysisBasedWarnings.cpp?rev=156086&r1=156085&r2=156086&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/AnalysisBasedWarnings.cpp (original)
+++ cfe/trunk/lib/Sema/AnalysisBasedWarnings.cpp Thu May 3 13:27:39 2012
@@ -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 @@
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 @@
.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 @@
}
}
+ 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;
Modified: cfe/trunk/lib/Sema/AttributeList.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/AttributeList.cpp?rev=156086&r1=156085&r2=156086&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/AttributeList.cpp (original)
+++ cfe/trunk/lib/Sema/AttributeList.cpp Thu May 3 13:27:39 2012
@@ -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 @@
#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 @@
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);
}
Modified: cfe/trunk/lib/Sema/SemaStmtAttr.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaStmtAttr.cpp?rev=156086&r1=156085&r2=156086&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaStmtAttr.cpp (original)
+++ cfe/trunk/lib/Sema/SemaStmtAttr.cpp Thu May 3 13:27:39 2012
@@ -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 @@
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);
}
Added: cfe/trunk/test/SemaCXX/switch-implicit-fallthrough-cxx98.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaCXX/switch-implicit-fallthrough-cxx98.cpp?rev=156086&view=auto
==============================================================================
--- cfe/trunk/test/SemaCXX/switch-implicit-fallthrough-cxx98.cpp (added)
+++ cfe/trunk/test/SemaCXX/switch-implicit-fallthrough-cxx98.cpp Thu May 3 13:27:39 2012
@@ -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;
+}
Added: cfe/trunk/test/SemaCXX/switch-implicit-fallthrough.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaCXX/switch-implicit-fallthrough.cpp?rev=156086&view=auto
==============================================================================
--- cfe/trunk/test/SemaCXX/switch-implicit-fallthrough.cpp (added)
+++ cfe/trunk/test/SemaCXX/switch-implicit-fallthrough.cpp Thu May 3 13:27:39 2012
@@ -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;
+}
More information about the cfe-commits
mailing list