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
This commit is contained in:
Richard Smith 2012-05-03 18:27:39 +00:00
parent b3b56bb960
commit 84837d5b5a
12 changed files with 635 additions and 31 deletions

View File

@ -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>

View File

@ -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"];
}

View File

@ -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">;

View File

@ -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;

View File

@ -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; }

View File

@ -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);

View File

@ -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

View File

@ -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;

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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;
}

View File

@ -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;
}