зеркало из https://github.com/microsoft/clang-1.git
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. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@156086 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
Родитель
dd160f3ed5
Коммит
e0d3b4cd2b
|
@ -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>
|
||||
|
|
|
@ -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"];
|
||||
}
|
||||
|
|
|
@ -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">;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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; }
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
Загрузка…
Ссылка в новой задаче