Updated to Clang 3.5a.
Change-Id: I8127eb568f674c2e72635b639a3295381fe8af82
diff --git a/lib/Analysis/AnalysisDeclContext.cpp b/lib/Analysis/AnalysisDeclContext.cpp
index 465f0c3..c90d947 100644
--- a/lib/Analysis/AnalysisDeclContext.cpp
+++ b/lib/Analysis/AnalysisDeclContext.cpp
@@ -28,8 +28,8 @@
#include "clang/Analysis/Support/BumpVector.h"
#include "llvm/ADT/SmallPtrSet.h"
#include "llvm/Support/ErrorHandling.h"
-#include "llvm/Support/raw_ostream.h"
#include "llvm/Support/SaveAndRestore.h"
+#include "llvm/Support/raw_ostream.h"
using namespace clang;
@@ -68,7 +68,8 @@
bool addInitializers,
bool addTemporaryDtors,
bool synthesizeBodies,
- bool addStaticInitBranch)
+ bool addStaticInitBranch,
+ bool addCXXNewAllocator)
: SynthesizeBodies(synthesizeBodies)
{
cfgBuildOptions.PruneTriviallyFalseEdges = !useUnoptimizedCFG;
@@ -76,12 +77,11 @@
cfgBuildOptions.AddInitializers = addInitializers;
cfgBuildOptions.AddTemporaryDtors = addTemporaryDtors;
cfgBuildOptions.AddStaticInitBranches = addStaticInitBranch;
+ cfgBuildOptions.AddCXXNewAllocator = addCXXNewAllocator;
}
void AnalysisDeclContextManager::clear() {
- for (ContextMap::iterator I = Contexts.begin(), E = Contexts.end(); I!=E; ++I)
- delete I->second;
- Contexts.clear();
+ llvm::DeleteContainerSeconds(Contexts);
}
static BodyFarm &getBodyFarm(ASTContext &C) {
@@ -94,14 +94,21 @@
if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(D)) {
Stmt *Body = FD->getBody();
if (!Body && Manager && Manager->synthesizeBodies()) {
- IsAutosynthesized = true;
- return getBodyFarm(getASTContext()).getBody(FD);
+ Body = getBodyFarm(getASTContext()).getBody(FD);
+ if (Body)
+ IsAutosynthesized = true;
}
return Body;
}
- else if (const ObjCMethodDecl *MD = dyn_cast<ObjCMethodDecl>(D))
- return MD->getBody();
- else if (const BlockDecl *BD = dyn_cast<BlockDecl>(D))
+ else if (const ObjCMethodDecl *MD = dyn_cast<ObjCMethodDecl>(D)) {
+ Stmt *Body = MD->getBody();
+ if (!Body && Manager && Manager->synthesizeBodies()) {
+ Body = getBodyFarm(getASTContext()).getBody(MD);
+ if (Body)
+ IsAutosynthesized = true;
+ }
+ return Body;
+ } else if (const BlockDecl *BD = dyn_cast<BlockDecl>(D))
return BD->getBody();
else if (const FunctionTemplateDecl *FunTmpl
= dyn_cast_or_null<FunctionTemplateDecl>(D))
@@ -126,9 +133,8 @@
return MD->getSelfDecl();
if (const BlockDecl *BD = dyn_cast<BlockDecl>(D)) {
// See if 'self' was captured by the block.
- for (BlockDecl::capture_const_iterator it = BD->capture_begin(),
- et = BD->capture_end(); it != et; ++it) {
- const VarDecl *VD = it->getVariable();
+ for (const auto &I : BD->captures()) {
+ const VarDecl *VD = I.getVariable();
if (VD->getName() == "self")
return dyn_cast<ImplicitParamDecl>(VD);
}
@@ -235,10 +241,8 @@
if (!PM) {
PM.reset(new ParentMap(getBody()));
if (const CXXConstructorDecl *C = dyn_cast<CXXConstructorDecl>(getDecl())) {
- for (CXXConstructorDecl::init_const_iterator I = C->init_begin(),
- E = C->init_end();
- I != E; ++I) {
- PM->addStmt((*I)->getInit());
+ for (const auto *I : C->inits()) {
+ PM->addStmt(I->getInit());
}
}
if (builtCFG)
@@ -435,7 +439,7 @@
}
}
-void LocationContext::dumpStack() const {
+LLVM_DUMP_METHOD void LocationContext::dumpStack() const {
dumpStack(llvm::errs());
}
@@ -454,11 +458,6 @@
BumpVectorContext &bc)
: BEVals(bevals), BC(bc) {}
- bool IsTrackedDecl(const VarDecl *VD) {
- const DeclContext *DC = VD->getDeclContext();
- return IgnoredContexts.count(DC) == 0;
- }
-
void VisitStmt(Stmt *S) {
for (Stmt::child_range I = S->children(); I; ++I)
if (Stmt *child = *I)
@@ -506,9 +505,8 @@
new (BV) DeclVec(BC, 10);
// Go through the capture list.
- for (BlockDecl::capture_const_iterator CI = BD->capture_begin(),
- CE = BD->capture_end(); CI != CE; ++CI) {
- BV->push_back(CI->getVariable(), BC);
+ for (const auto &CI : BD->captures()) {
+ BV->push_back(CI.getVariable(), BC);
}
// Find the referenced global/static variables.
@@ -548,15 +546,13 @@
// Release the managed analyses.
if (ManagedAnalyses) {
ManagedAnalysisMap *M = (ManagedAnalysisMap*) ManagedAnalyses;
- for (ManagedAnalysisMap::iterator I = M->begin(), E = M->end(); I!=E; ++I)
- delete I->second;
+ llvm::DeleteContainerSeconds(*M);
delete M;
}
}
AnalysisDeclContextManager::~AnalysisDeclContextManager() {
- for (ContextMap::iterator I = Contexts.begin(), E = Contexts.end(); I!=E; ++I)
- delete I->second;
+ llvm::DeleteContainerSeconds(Contexts);
}
LocationContext::~LocationContext() {}
diff --git a/lib/Analysis/Android.mk b/lib/Analysis/Android.mk
index 8a73690..8ccbf3e 100644
--- a/lib/Analysis/Android.mk
+++ b/lib/Analysis/Android.mk
@@ -8,6 +8,7 @@
TBLGEN_TABLES := \
AttrList.inc \
Attrs.inc \
+ AttrVisitor.inc \
CommentCommandList.inc \
CommentNodes.inc \
DeclNodes.inc \
diff --git a/lib/Analysis/BodyFarm.cpp b/lib/Analysis/BodyFarm.cpp
index 4d5c2ee..039911e 100644
--- a/lib/Analysis/BodyFarm.cpp
+++ b/lib/Analysis/BodyFarm.cpp
@@ -35,8 +35,7 @@
// returns void.
const FunctionProtoType *FT =
BPT->getPointeeType()->getAs<FunctionProtoType>();
- if (!FT || !FT->getResultType()->isVoidType() ||
- FT->getNumArgs() != 0)
+ if (!FT || !FT->getReturnType()->isVoidType() || FT->getNumParams() != 0)
return false;
return true;
@@ -74,6 +73,9 @@
/// Create an Objective-C bool literal.
ObjCBoolLiteralExpr *makeObjCBool(bool Val);
+
+ /// Create an Objective-C ivar reference.
+ ObjCIvarRefExpr *makeObjCIvarRef(const Expr *Base, const ObjCIvarDecl *IVar);
/// Create a Return statement.
ReturnStmt *makeReturn(const Expr *RetVal);
@@ -147,6 +149,15 @@
return new (C) ObjCBoolLiteralExpr(Val, Ty, SourceLocation());
}
+ObjCIvarRefExpr *ASTMaker::makeObjCIvarRef(const Expr *Base,
+ const ObjCIvarDecl *IVar) {
+ return new (C) ObjCIvarRefExpr(const_cast<ObjCIvarDecl*>(IVar),
+ IVar->getType(), SourceLocation(),
+ SourceLocation(), const_cast<Expr*>(Base),
+ /*arrow=*/true, /*free=*/false);
+}
+
+
ReturnStmt *ASTMaker::makeReturn(const Expr *RetVal) {
return new (C) ReturnStmt(SourceLocation(), const_cast<Expr*>(RetVal), 0);
}
@@ -278,8 +289,8 @@
// return YES;
// }
// else return NO;
-
- QualType ResultTy = D->getResultType();
+
+ QualType ResultTy = D->getReturnType();
bool isBoolean = ResultTy->isBooleanType();
if (!isBoolean && !ResultTy->isIntegralType(C))
return 0;
@@ -374,3 +385,87 @@
return Val.getValue();
}
+static Stmt *createObjCPropertyGetter(ASTContext &Ctx,
+ const ObjCPropertyDecl *Prop) {
+ // First, find the backing ivar.
+ const ObjCIvarDecl *IVar = Prop->getPropertyIvarDecl();
+ if (!IVar)
+ return 0;
+
+ // Ignore weak variables, which have special behavior.
+ if (Prop->getPropertyAttributes() & ObjCPropertyDecl::OBJC_PR_weak)
+ return 0;
+
+ // Look to see if Sema has synthesized a body for us. This happens in
+ // Objective-C++ because the return value may be a C++ class type with a
+ // non-trivial copy constructor. We can only do this if we can find the
+ // @synthesize for this property, though (or if we know it's been auto-
+ // synthesized).
+ const ObjCImplementationDecl *ImplDecl =
+ IVar->getContainingInterface()->getImplementation();
+ if (ImplDecl) {
+ for (const auto *I : ImplDecl->property_impls()) {
+ if (I->getPropertyDecl() != Prop)
+ continue;
+
+ if (I->getGetterCXXConstructor()) {
+ ASTMaker M(Ctx);
+ return M.makeReturn(I->getGetterCXXConstructor());
+ }
+ }
+ }
+
+ // Sanity check that the property is the same type as the ivar, or a
+ // reference to it, and that it is either an object pointer or trivially
+ // copyable.
+ if (!Ctx.hasSameUnqualifiedType(IVar->getType(),
+ Prop->getType().getNonReferenceType()))
+ return 0;
+ if (!IVar->getType()->isObjCLifetimeType() &&
+ !IVar->getType().isTriviallyCopyableType(Ctx))
+ return 0;
+
+ // Generate our body:
+ // return self->_ivar;
+ ASTMaker M(Ctx);
+
+ const VarDecl *selfVar = Prop->getGetterMethodDecl()->getSelfDecl();
+
+ Expr *loadedIVar =
+ M.makeObjCIvarRef(
+ M.makeLvalueToRvalue(
+ M.makeDeclRefExpr(selfVar),
+ selfVar->getType()),
+ IVar);
+
+ if (!Prop->getType()->isReferenceType())
+ loadedIVar = M.makeLvalueToRvalue(loadedIVar, IVar->getType());
+
+ return M.makeReturn(loadedIVar);
+}
+
+Stmt *BodyFarm::getBody(const ObjCMethodDecl *D) {
+ // We currently only know how to synthesize property accessors.
+ if (!D->isPropertyAccessor())
+ return 0;
+
+ D = D->getCanonicalDecl();
+
+ Optional<Stmt *> &Val = Bodies[D];
+ if (Val.hasValue())
+ return Val.getValue();
+ Val = 0;
+
+ const ObjCPropertyDecl *Prop = D->findPropertyDecl();
+ if (!Prop)
+ return 0;
+
+ // For now, we only synthesize getters.
+ if (D->param_size() != 0)
+ return 0;
+
+ Val = createObjCPropertyGetter(C, Prop);
+
+ return Val.getValue();
+}
+
diff --git a/lib/Analysis/BodyFarm.h b/lib/Analysis/BodyFarm.h
index 96f61df..2d200fb 100644
--- a/lib/Analysis/BodyFarm.h
+++ b/lib/Analysis/BodyFarm.h
@@ -24,6 +24,8 @@
class ASTContext;
class Decl;
class FunctionDecl;
+class ObjCMethodDecl;
+class ObjCPropertyDecl;
class Stmt;
class BodyFarm {
@@ -32,7 +34,10 @@
/// Factory method for creating bodies for ordinary functions.
Stmt *getBody(const FunctionDecl *D);
-
+
+ /// Factory method for creating bodies for Objective-C properties.
+ Stmt *getBody(const ObjCMethodDecl *D);
+
private:
typedef llvm::DenseMap<const Decl *, Optional<Stmt *> > BodyMap;
diff --git a/lib/Analysis/CFG.cpp b/lib/Analysis/CFG.cpp
index 8b8c573..400c202 100644
--- a/lib/Analysis/CFG.cpp
+++ b/lib/Analysis/CFG.cpp
@@ -21,7 +21,7 @@
#include "clang/AST/StmtVisitor.h"
#include "clang/Basic/Builtins.h"
#include "llvm/ADT/DenseMap.h"
-#include "llvm/ADT/OwningPtr.h"
+#include <memory>
#include "llvm/ADT/SmallPtrSet.h"
#include "llvm/Support/Allocator.h"
#include "llvm/Support/Format.h"
@@ -291,7 +291,7 @@
typedef BlockScopePosPair JumpSource;
ASTContext *Context;
- OwningPtr<CFG> cfg;
+ std::unique_ptr<CFG> cfg;
CFGBlock *Block;
CFGBlock *Succ;
@@ -363,6 +363,7 @@
AddStmtChoice asc);
CFGBlock *VisitCXXCatchStmt(CXXCatchStmt *S);
CFGBlock *VisitCXXConstructExpr(CXXConstructExpr *C, AddStmtChoice asc);
+ CFGBlock *VisitCXXNewExpr(CXXNewExpr *DE, AddStmtChoice asc);
CFGBlock *VisitCXXDeleteExpr(CXXDeleteExpr *DE, AddStmtChoice asc);
CFGBlock *VisitCXXForRangeStmt(CXXForRangeStmt *S);
CFGBlock *VisitCXXFunctionalCastExpr(CXXFunctionalCastExpr *E,
@@ -459,6 +460,9 @@
void appendInitializer(CFGBlock *B, CXXCtorInitializer *I) {
B->appendInitializer(I, cfg->getBumpVectorContext());
}
+ void appendNewAllocator(CFGBlock *B, CXXNewExpr *NE) {
+ B->appendNewAllocator(NE, cfg->getBumpVectorContext());
+ }
void appendBaseDtor(CFGBlock *B, const CXXBaseSpecifier *BS) {
B->appendBaseDtor(BS, cfg->getBumpVectorContext());
}
@@ -479,8 +483,16 @@
void prependAutomaticObjDtorsWithTerminator(CFGBlock *Blk,
LocalScope::const_iterator B, LocalScope::const_iterator E);
- void addSuccessor(CFGBlock *B, CFGBlock *S) {
- B->addSuccessor(S, cfg->getBumpVectorContext());
+ void addSuccessor(CFGBlock *B, CFGBlock *S, bool IsReachable = true) {
+ B->addSuccessor(CFGBlock::AdjacentBlock(S, IsReachable),
+ cfg->getBumpVectorContext());
+ }
+
+ /// Add a reachable successor to a block, with the alternate variant that is
+ /// unreachable.
+ void addSuccessor(CFGBlock *B, CFGBlock *ReachableBlock, CFGBlock *AltBlock) {
+ B->addSuccessor(CFGBlock::AdjacentBlock(ReachableBlock, AltBlock),
+ cfg->getBumpVectorContext());
}
/// Try and evaluate an expression to an integer constant.
@@ -712,7 +724,7 @@
// Create an empty entry block that has no predecessors.
cfg->setEntry(createBlock());
- return cfg.take();
+ return cfg.release();
}
/// createBlock - Used to lazily create blocks that are connected
@@ -730,7 +742,7 @@
CFGBlock *CFGBuilder::createNoReturnBlock() {
CFGBlock *B = createBlock(false);
B->setHasNoReturnElement();
- addSuccessor(B, &cfg->getExit());
+ addSuccessor(B, &cfg->getExit(), Succ);
return B;
}
@@ -868,30 +880,27 @@
const CXXRecordDecl *RD = DD->getParent();
// At the end destroy virtual base objects.
- for (CXXRecordDecl::base_class_const_iterator VI = RD->vbases_begin(),
- VE = RD->vbases_end(); VI != VE; ++VI) {
- const CXXRecordDecl *CD = VI->getType()->getAsCXXRecordDecl();
+ for (const auto &VI : RD->vbases()) {
+ const CXXRecordDecl *CD = VI.getType()->getAsCXXRecordDecl();
if (!CD->hasTrivialDestructor()) {
autoCreateBlock();
- appendBaseDtor(Block, VI);
+ appendBaseDtor(Block, &VI);
}
}
// Before virtual bases destroy direct base objects.
- for (CXXRecordDecl::base_class_const_iterator BI = RD->bases_begin(),
- BE = RD->bases_end(); BI != BE; ++BI) {
- if (!BI->isVirtual()) {
- const CXXRecordDecl *CD = BI->getType()->getAsCXXRecordDecl();
+ for (const auto &BI : RD->bases()) {
+ if (!BI.isVirtual()) {
+ const CXXRecordDecl *CD = BI.getType()->getAsCXXRecordDecl();
if (!CD->hasTrivialDestructor()) {
autoCreateBlock();
- appendBaseDtor(Block, BI);
+ appendBaseDtor(Block, &BI);
}
}
}
// First destroy member objects.
- for (CXXRecordDecl::field_iterator FI = RD->field_begin(),
- FE = RD->field_end(); FI != FE; ++FI) {
+ for (auto *FI : RD->fields()) {
// Check for constant size array. Set type to array element type.
QualType QT = FI->getType();
if (const ConstantArrayType *AT = Context->getAsConstantArrayType(QT)) {
@@ -903,7 +912,7 @@
if (const CXXRecordDecl *CD = QT->getAsCXXRecordDecl())
if (!CD->hasTrivialDestructor()) {
autoCreateBlock();
- appendMemberDtor(Block, *FI);
+ appendMemberDtor(Block, FI);
}
}
}
@@ -930,9 +939,8 @@
// For compound statement we will be creating explicit scope.
if (CompoundStmt *CS = dyn_cast<CompoundStmt>(S)) {
- for (CompoundStmt::body_iterator BI = CS->body_begin(), BE = CS->body_end()
- ; BI != BE; ++BI) {
- Stmt *SI = (*BI)->stripLabelLikeStatements();
+ for (auto *BI : CS->body()) {
+ Stmt *SI = BI->stripLabelLikeStatements();
if (DeclStmt *DS = dyn_cast<DeclStmt>(SI))
Scope = addLocalScopeForDeclStmt(DS, Scope);
}
@@ -952,11 +960,9 @@
if (!BuildOpts.AddImplicitDtors)
return Scope;
- for (DeclStmt::decl_iterator DI = DS->decl_begin(), DE = DS->decl_end()
- ; DI != DE; ++DI) {
- if (VarDecl *VD = dyn_cast<VarDecl>(*DI))
+ for (auto *DI : DS->decls())
+ if (VarDecl *VD = dyn_cast<VarDecl>(DI))
Scope = addLocalScopeForVarDecl(VD, Scope);
- }
return Scope;
}
@@ -1122,6 +1128,9 @@
case Stmt::CXXConstructExprClass:
return VisitCXXConstructExpr(cast<CXXConstructExpr>(S), asc);
+ case Stmt::CXXNewExprClass:
+ return VisitCXXNewExpr(cast<CXXNewExpr>(S), asc);
+
case Stmt::CXXDeleteExprClass:
return VisitCXXDeleteExpr(cast<CXXDeleteExpr>(S), asc);
@@ -1293,7 +1302,7 @@
do {
if (BinaryOperator *B_RHS = dyn_cast<BinaryOperator>(RHS))
if (B_RHS->isLogicalOp()) {
- llvm::tie(RHSBlock, ExitBlock) =
+ std::tie(RHSBlock, ExitBlock) =
VisitLogicalOperator(B_RHS, Term, TrueBlock, FalseBlock);
break;
}
@@ -1311,8 +1320,8 @@
else {
RHSBlock->setTerminator(Term);
TryResult KnownVal = tryEvaluateBool(RHS);
- addSuccessor(RHSBlock, KnownVal.isFalse() ? NULL : TrueBlock);
- addSuccessor(RHSBlock, KnownVal.isTrue() ? NULL : FalseBlock);
+ addSuccessor(RHSBlock, TrueBlock, !KnownVal.isFalse());
+ addSuccessor(RHSBlock, FalseBlock, !KnownVal.isTrue());
}
Block = RHSBlock;
@@ -1355,12 +1364,12 @@
// Now link the LHSBlock with RHSBlock.
if (B->getOpcode() == BO_LOr) {
- addSuccessor(LHSBlock, KnownVal.isFalse() ? NULL : TrueBlock);
- addSuccessor(LHSBlock, KnownVal.isTrue() ? NULL : RHSBlock);
+ addSuccessor(LHSBlock, TrueBlock, !KnownVal.isFalse());
+ addSuccessor(LHSBlock, RHSBlock, !KnownVal.isTrue());
} else {
assert(B->getOpcode() == BO_LAnd);
- addSuccessor(LHSBlock, KnownVal.isFalse() ? NULL : RHSBlock);
- addSuccessor(LHSBlock, KnownVal.isTrue() ? NULL : FalseBlock);
+ addSuccessor(LHSBlock, RHSBlock, !KnownVal.isFalse());
+ addSuccessor(LHSBlock, FalseBlock, !KnownVal.isTrue());
}
return std::make_pair(EntryLHSBlock, ExitBlock);
@@ -1619,8 +1628,8 @@
// See if this is a known constant.
const TryResult& KnownVal = tryEvaluateBool(C->getCond());
- addSuccessor(Block, KnownVal.isFalse() ? NULL : LHSBlock);
- addSuccessor(Block, KnownVal.isTrue() ? NULL : RHSBlock);
+ addSuccessor(Block, LHSBlock, !KnownVal.isFalse());
+ addSuccessor(Block, RHSBlock, !KnownVal.isTrue());
Block->setTerminator(C);
Expr *condExpr = C->getCond();
@@ -1865,9 +1874,10 @@
// See if this is a known constant.
const TryResult &KnownVal = tryEvaluateBool(I->getCond());
- // Now add the successors.
- addSuccessor(Block, KnownVal.isFalse() ? NULL : ThenBlock);
- addSuccessor(Block, KnownVal.isTrue()? NULL : ElseBlock);
+ // Add the successors. If we know that specific branches are
+ // unreachable, inform addSuccessor() of that knowledge.
+ addSuccessor(Block, ThenBlock, /* isReachable = */ !KnownVal.isFalse());
+ addSuccessor(Block, ElseBlock, /* isReachable = */ !KnownVal.isTrue());
// Add the condition as the last statement in the new block. This may create
// new blocks as the condition may contain control-flow. Any newly created
@@ -2078,7 +2088,7 @@
if (BinaryOperator *Cond =
dyn_cast_or_null<BinaryOperator>(C ? C->IgnoreParens() : 0))
if (Cond->isLogicalOp()) {
- llvm::tie(EntryConditionBlock, ExitConditionBlock) =
+ std::tie(EntryConditionBlock, ExitConditionBlock) =
VisitLogicalOperator(Cond, F, BodyBlock, LoopSuccessor);
break;
}
@@ -2394,9 +2404,8 @@
// more optimal CFG representation.
if (BinaryOperator *Cond = dyn_cast<BinaryOperator>(C->IgnoreParens()))
if (Cond->isLogicalOp()) {
- llvm::tie(EntryConditionBlock, ExitConditionBlock) =
- VisitLogicalOperator(Cond, W, BodyBlock,
- LoopSuccessor);
+ std::tie(EntryConditionBlock, ExitConditionBlock) =
+ VisitLogicalOperator(Cond, W, BodyBlock, LoopSuccessor);
break;
}
@@ -2726,8 +2735,8 @@
SwitchAlwaysHasSuccessor |= switchExclusivelyCovered;
SwitchAlwaysHasSuccessor |= Terminator->isAllEnumCasesCovered() &&
Terminator->getSwitchCaseList();
- addSuccessor(SwitchTerminatedBlock,
- SwitchAlwaysHasSuccessor ? 0 : DefaultCaseBlock);
+ addSuccessor(SwitchTerminatedBlock, DefaultCaseBlock,
+ !SwitchAlwaysHasSuccessor);
// Add the terminator and condition in the switch block.
SwitchTerminatedBlock->setTerminator(Terminator);
@@ -2828,10 +2837,9 @@
// Add this block to the list of successors for the block with the switch
// statement.
assert(SwitchTerminatedBlock);
- addSuccessor(SwitchTerminatedBlock,
+ addSuccessor(SwitchTerminatedBlock, CaseBlock,
shouldAddCase(switchExclusivelyCovered, switchCond,
- CS, *Context)
- ? CaseBlock : 0);
+ CS, *Context));
// We set Block to NULL to allow lazy creation of a new block (if necessary)
Block = NULL;
@@ -3124,6 +3132,23 @@
return VisitChildren(C);
}
+CFGBlock *CFGBuilder::VisitCXXNewExpr(CXXNewExpr *NE,
+ AddStmtChoice asc) {
+
+ autoCreateBlock();
+ appendStmt(Block, NE);
+
+ if (NE->getInitializer())
+ Block = Visit(NE->getInitializer());
+ if (BuildOpts.AddCXXNewAllocator)
+ appendNewAllocator(Block, NE);
+ if (NE->isArray())
+ Block = Visit(NE->getArraySize());
+ for (CXXNewExpr::arg_iterator I = NE->placement_arg_begin(),
+ E = NE->placement_arg_end(); I != E; ++I)
+ Block = Visit(*I);
+ return Block;
+}
CFGBlock *CFGBuilder::VisitCXXDeleteExpr(CXXDeleteExpr *DE,
AddStmtChoice asc) {
@@ -3320,10 +3345,12 @@
// a new block for the destructor which does not have as a successor
// anything built thus far. Control won't flow out of this block.
const CXXDestructorDecl *Dtor = E->getTemporary()->getDestructor();
- if (Dtor->isNoReturn())
+ if (Dtor->isNoReturn()) {
+ Succ = B;
Block = createNoReturnBlock();
- else
+ } else {
autoCreateBlock();
+ }
appendTemporaryDtor(Block, E);
B = Block;
@@ -3372,12 +3399,13 @@
Block = createBlock(false);
Block->setTerminator(CFGTerminator(E, true));
+ assert(Block->getTerminator().isTemporaryDtorsBranch());
// See if this is a known constant.
const TryResult &KnownVal = tryEvaluateBool(E->getCond());
if (LHSBlock) {
- addSuccessor(Block, KnownVal.isFalse() ? NULL : LHSBlock);
+ addSuccessor(Block, LHSBlock, !KnownVal.isFalse());
} else if (KnownVal.isFalse()) {
addSuccessor(Block, NULL);
} else {
@@ -3387,7 +3415,8 @@
if (!RHSBlock)
RHSBlock = ConfluenceBlock;
- addSuccessor(Block, KnownVal.isTrue() ? NULL : RHSBlock);
+
+ addSuccessor(Block, RHSBlock, !KnownVal.isTrue());
return Block;
}
@@ -3426,6 +3455,7 @@
switch (getKind()) {
case CFGElement::Statement:
case CFGElement::Initializer:
+ case CFGElement::NewAllocator:
llvm_unreachable("getDestructorDecl should only be used with "
"ImplicitDtors");
case CFGElement::AutomaticObjectDtor: {
@@ -3470,13 +3500,37 @@
}
//===----------------------------------------------------------------------===//
-// Filtered walking of the CFG.
+// CFGBlock operations.
//===----------------------------------------------------------------------===//
+CFGBlock::AdjacentBlock::AdjacentBlock(CFGBlock *B, bool IsReachable)
+ : ReachableBlock(IsReachable ? B : 0),
+ UnreachableBlock(!IsReachable ? B : 0,
+ B && IsReachable ? AB_Normal : AB_Unreachable) {}
+
+CFGBlock::AdjacentBlock::AdjacentBlock(CFGBlock *B, CFGBlock *AlternateBlock)
+ : ReachableBlock(B),
+ UnreachableBlock(B == AlternateBlock ? 0 : AlternateBlock,
+ B == AlternateBlock ? AB_Alternate : AB_Normal) {}
+
+void CFGBlock::addSuccessor(AdjacentBlock Succ,
+ BumpVectorContext &C) {
+ if (CFGBlock *B = Succ.getReachableBlock())
+ B->Preds.push_back(AdjacentBlock(this, Succ.isReachable()), C);
+
+ if (CFGBlock *UnreachableB = Succ.getPossiblyUnreachableBlock())
+ UnreachableB->Preds.push_back(AdjacentBlock(this, false), C);
+
+ Succs.push_back(Succ, C);
+}
+
bool CFGBlock::FilterEdge(const CFGBlock::FilterOptions &F,
const CFGBlock *From, const CFGBlock *To) {
- if (To && F.IgnoreDefaultsWithCoveredEnums) {
+ if (F.IgnoreNullPredecessors && !From)
+ return true;
+
+ if (To && From && F.IgnoreDefaultsWithCoveredEnums) {
// If the 'To' has no label or is labeled but the label isn't a
// CaseStmt then filter this edge.
if (const SwitchStmt *S =
@@ -3572,7 +3626,7 @@
void setBlockID(signed i) { currentBlock = i; }
void setStmtID(unsigned i) { currStmt = i; }
- virtual bool handledStmt(Stmt *S, raw_ostream &OS) {
+ bool handledStmt(Stmt *S, raw_ostream &OS) override {
StmtMapTy::iterator I = StmtMap.find(S);
if (I == StmtMap.end())
@@ -3615,7 +3669,9 @@
public:
CFGBlockTerminatorPrint(raw_ostream &os, StmtPrinterHelper* helper,
const PrintingPolicy &Policy)
- : OS(os), Helper(helper), Policy(Policy) {}
+ : OS(os), Helper(helper), Policy(Policy) {
+ this->Policy.IncludeNewlines = false;
+ }
void VisitIfStmt(IfStmt *I) {
OS << "if ";
@@ -3705,6 +3761,13 @@
void VisitExpr(Expr *E) {
E->printPretty(OS, Helper, Policy);
}
+
+public:
+ void print(CFGTerminator T) {
+ if (T.isTemporaryDtorsBranch())
+ OS << "(Temp Dtor) ";
+ Visit(T.getStmt());
+ }
};
} // end anonymous namespace
@@ -3787,6 +3850,11 @@
OS << ".~" << T->getAsCXXRecordDecl()->getName().str() << "()";
OS << " (Implicit destructor)\n";
+ } else if (Optional<CFGNewAllocator> NE = E.getAs<CFGNewAllocator>()) {
+ OS << "CFGNewAllocator(";
+ if (const CXXNewExpr *AllocExpr = NE->getAllocatorExpr())
+ AllocExpr->getType().print(OS, PrintingPolicy(Helper.getLangOpts()));
+ OS << ")\n";
} else if (Optional<CFGDeleteDtor> DE = E.getAs<CFGDeleteDtor>()) {
const CXXRecordDecl *RD = DE->getCXXRecordDecl();
if (!RD)
@@ -3835,6 +3903,8 @@
OS << " (EXIT)]\n";
else if (&B == cfg->getIndirectGotoBlock())
OS << " (INDIRECT GOTO DISPATCH)]\n";
+ else if (B.hasNoReturnElement())
+ OS << " (NORETURN)]\n";
else
OS << "]\n";
@@ -3903,7 +3973,7 @@
PrintingPolicy PP(Helper.getLangOpts());
CFGBlockTerminatorPrint TPrinter(OS, &Helper, PP);
- TPrinter.Visit(const_cast<Stmt*>(B.getTerminator().getStmt()));
+ TPrinter.print(B.getTerminator());
OS << '\n';
if (ShowColors)
@@ -3931,7 +4001,16 @@
if (i % 10 == 8)
OS << "\n ";
- OS << " B" << (*I)->getBlockID();
+ CFGBlock *B = *I;
+ bool Reachable = true;
+ if (!B) {
+ Reachable = false;
+ B = I->getPossiblyUnreachableBlock();
+ }
+
+ OS << " B" << B->getBlockID();
+ if (!Reachable)
+ OS << "(Unreachable)";
}
if (ShowColors)
@@ -3960,12 +4039,24 @@
if (i % 10 == 8)
OS << "\n ";
- if (*I)
- OS << " B" << (*I)->getBlockID();
- else
- OS << " NULL";
+ CFGBlock *B = *I;
+
+ bool Reachable = true;
+ if (!B) {
+ Reachable = false;
+ B = I->getPossiblyUnreachableBlock();
+ }
+
+ if (B) {
+ OS << " B" << B->getBlockID();
+ if (!Reachable)
+ OS << "(Unreachable)";
+ }
+ else {
+ OS << " NULL";
+ }
}
-
+
if (ShowColors)
OS.resetColor();
OS << '\n';
@@ -4020,10 +4111,10 @@
void CFGBlock::printTerminator(raw_ostream &OS,
const LangOptions &LO) const {
CFGBlockTerminatorPrint TPrinter(OS, NULL, PrintingPolicy(LO));
- TPrinter.Visit(const_cast<Stmt*>(getTerminator().getStmt()));
+ TPrinter.print(getTerminator());
}
-Stmt *CFGBlock::getTerminatorCondition() {
+Stmt *CFGBlock::getTerminatorCondition(bool StripParens) {
Stmt *Terminator = this->Terminator;
if (!Terminator)
return NULL;
@@ -4082,6 +4173,9 @@
return Terminator;
}
+ if (!StripParens)
+ return E;
+
return E ? E->IgnoreParens() : NULL;
}
diff --git a/lib/Analysis/CFGReachabilityAnalysis.cpp b/lib/Analysis/CFGReachabilityAnalysis.cpp
index 492e66f..4ae135f 100644
--- a/lib/Analysis/CFGReachabilityAnalysis.cpp
+++ b/lib/Analysis/CFGReachabilityAnalysis.cpp
@@ -69,7 +69,8 @@
// Add the predecessors to the worklist.
for (CFGBlock::const_pred_iterator i = block->pred_begin(),
e = block->pred_end(); i != e; ++i) {
- worklist.push_back(*i);
+ if (*i)
+ worklist.push_back(*i);
}
}
}
diff --git a/lib/Analysis/CMakeLists.txt b/lib/Analysis/CMakeLists.txt
index deab8f1..9630bc0 100644
--- a/lib/Analysis/CMakeLists.txt
+++ b/lib/Analysis/CMakeLists.txt
@@ -1,3 +1,8 @@
+set(LLVM_LINK_COMPONENTS
+ MC
+ Support
+ )
+
add_clang_library(clangAnalysis
AnalysisDeclContext.cpp
BodyFarm.cpp
@@ -19,19 +24,8 @@
ScanfFormatString.cpp
ThreadSafety.cpp
UninitializedValues.cpp
- )
-add_dependencies(clangAnalysis
- ClangAttrClasses
- ClangAttrList
- ClangCommentNodes
- ClangDiagnosticCommon
- ClangDeclNodes
- ClangDiagnosticAnalysis
- ClangStmtNodes
- )
-
-target_link_libraries(clangAnalysis
+ LINK_LIBS
clangBasic
clangAST
)
diff --git a/lib/Analysis/CallGraph.cpp b/lib/Analysis/CallGraph.cpp
index 3387015..649ba57 100644
--- a/lib/Analysis/CallGraph.cpp
+++ b/lib/Analysis/CallGraph.cpp
@@ -95,9 +95,8 @@
if (BlockDecl *BD = dyn_cast<BlockDecl>(D))
addNodeForDecl(BD, true);
- for (DeclContext::decl_iterator I = D->decls_begin(), E = D->decls_end();
- I!=E; ++I)
- if (DeclContext *DC = dyn_cast<DeclContext>(*I))
+ for (auto *I : D->decls())
+ if (auto *DC = dyn_cast<DeclContext>(I))
addNodesForBlocks(DC);
}
@@ -106,12 +105,7 @@
}
CallGraph::~CallGraph() {
- if (!FunctionMap.empty()) {
- for (FunctionMapTy::iterator I = FunctionMap.begin(), E = FunctionMap.end();
- I != E; ++I)
- delete I->second;
- FunctionMap.clear();
- }
+ llvm::DeleteContainerSeconds(FunctionMap);
}
bool CallGraph::includeInGraph(const Decl *D) {
diff --git a/lib/Analysis/Consumed.cpp b/lib/Analysis/Consumed.cpp
index b33c8d8..7b6ad57 100644
--- a/lib/Analysis/Consumed.cpp
+++ b/lib/Analysis/Consumed.cpp
@@ -17,20 +17,20 @@
#include "clang/AST/DeclCXX.h"
#include "clang/AST/ExprCXX.h"
#include "clang/AST/RecursiveASTVisitor.h"
-#include "clang/AST/StmtVisitor.h"
#include "clang/AST/StmtCXX.h"
+#include "clang/AST/StmtVisitor.h"
#include "clang/AST/Type.h"
+#include "clang/Analysis/Analyses/Consumed.h"
#include "clang/Analysis/Analyses/PostOrderCFGView.h"
#include "clang/Analysis/AnalysisContext.h"
#include "clang/Analysis/CFG.h"
-#include "clang/Analysis/Analyses/Consumed.h"
#include "clang/Basic/OperatorKinds.h"
#include "clang/Basic/SourceLocation.h"
#include "llvm/ADT/DenseMap.h"
-#include "llvm/ADT/OwningPtr.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/Support/Compiler.h"
#include "llvm/Support/raw_ostream.h"
+#include <memory>
// TODO: Adjust states of args to constructors in the same way that arguments to
// function calls are handled.
@@ -143,6 +143,7 @@
return false;
}
+
static bool isConsumableType(const QualType &QT) {
if (QT->isPointerType() || QT->isReferenceType())
return false;
@@ -153,6 +154,23 @@
return false;
}
+static bool isAutoCastType(const QualType &QT) {
+ if (QT->isPointerType() || QT->isReferenceType())
+ return false;
+
+ if (const CXXRecordDecl *RD = QT->getAsCXXRecordDecl())
+ return RD->hasAttr<ConsumableAutoCastAttr>();
+
+ return false;
+}
+
+static bool isSetOnReadPtrType(const QualType &QT) {
+ if (const CXXRecordDecl *RD = QT->getPointeeCXXRecordDecl())
+ return RD->hasAttr<ConsumableSetOnReadAttr>();
+ return false;
+}
+
+
static bool isKnownState(ConsumedState State) {
switch (State) {
case CS_Unconsumed:
@@ -165,19 +183,16 @@
llvm_unreachable("invalid enum");
}
-static bool isRValueRefish(QualType ParamType) {
- return ParamType->isRValueReferenceType() ||
- (ParamType->isLValueReferenceType() &&
- !cast<LValueReferenceType>(
- ParamType.getCanonicalType())->isSpelledAsLValue());
+static bool isRValueRef(QualType ParamType) {
+ return ParamType->isRValueReferenceType();
}
static bool isTestingFunction(const FunctionDecl *FunDecl) {
return FunDecl->hasAttr<TestTypestateAttr>();
}
-static bool isValueType(QualType ParamType) {
- return !(ParamType->isPointerType() || ParamType->isReferenceType());
+static bool isPointerOrRef(QualType ParamType) {
+ return ParamType->isPointerType() || ParamType->isReferenceType();
}
static ConsumedState mapConsumableAttrState(const QualType QT) {
@@ -455,15 +470,29 @@
ConsumedAnalyzer &Analyzer;
ConsumedStateMap *StateMap;
MapType PropagationMap;
- void forwardInfo(const Stmt *From, const Stmt *To);
- bool isLikeMoveAssignment(const CXXMethodDecl *MethodDecl);
- void propagateReturnType(const Stmt *Call, const FunctionDecl *Fun,
- QualType ReturnType);
+
+ InfoEntry findInfo(const Expr *E) {
+ return PropagationMap.find(E->IgnoreParens());
+ }
+ ConstInfoEntry findInfo(const Expr *E) const {
+ return PropagationMap.find(E->IgnoreParens());
+ }
+ void insertInfo(const Expr *E, const PropagationInfo &PI) {
+ PropagationMap.insert(PairType(E->IgnoreParens(), PI));
+ }
+
+ void forwardInfo(const Expr *From, const Expr *To);
+ void copyInfo(const Expr *From, const Expr *To, ConsumedState CS);
+ ConsumedState getInfo(const Expr *From);
+ void setInfo(const Expr *To, ConsumedState NS);
+ void propagateReturnType(const Expr *Call, const FunctionDecl *Fun);
public:
void checkCallability(const PropagationInfo &PInfo,
const FunctionDecl *FunDecl,
SourceLocation BlameLoc);
+ bool handleCall(const CallExpr *Call, const Expr *ObjArg,
+ const FunctionDecl *FunD);
void VisitBinaryOperator(const BinaryOperator *BinOp);
void VisitCallExpr(const CallExpr *Call);
@@ -485,8 +514,8 @@
ConsumedStateMap *StateMap)
: AC(AC), Analyzer(Analyzer), StateMap(StateMap) {}
- PropagationInfo getInfo(const Stmt *StmtNode) const {
- ConstInfoEntry Entry = PropagationMap.find(StmtNode);
+ PropagationInfo getInfo(const Expr *StmtNode) const {
+ ConstInfoEntry Entry = findInfo(StmtNode);
if (Entry != PropagationMap.end())
return Entry->second;
@@ -499,76 +528,187 @@
}
};
+
+void ConsumedStmtVisitor::forwardInfo(const Expr *From, const Expr *To) {
+ InfoEntry Entry = findInfo(From);
+ if (Entry != PropagationMap.end())
+ insertInfo(To, Entry->second);
+}
+
+
+// Create a new state for To, which is initialized to the state of From.
+// If NS is not CS_None, sets the state of From to NS.
+void ConsumedStmtVisitor::copyInfo(const Expr *From, const Expr *To,
+ ConsumedState NS) {
+ InfoEntry Entry = findInfo(From);
+ if (Entry != PropagationMap.end()) {
+ PropagationInfo& PInfo = Entry->second;
+ ConsumedState CS = PInfo.getAsState(StateMap);
+ if (CS != CS_None)
+ insertInfo(To, PropagationInfo(CS));
+ if (NS != CS_None && PInfo.isPointerToValue())
+ setStateForVarOrTmp(StateMap, PInfo, NS);
+ }
+}
+
+
+// Get the ConsumedState for From
+ConsumedState ConsumedStmtVisitor::getInfo(const Expr *From) {
+ InfoEntry Entry = findInfo(From);
+ if (Entry != PropagationMap.end()) {
+ PropagationInfo& PInfo = Entry->second;
+ return PInfo.getAsState(StateMap);
+ }
+ return CS_None;
+}
+
+
+// If we already have info for To then update it, otherwise create a new entry.
+void ConsumedStmtVisitor::setInfo(const Expr *To, ConsumedState NS) {
+ InfoEntry Entry = findInfo(To);
+ if (Entry != PropagationMap.end()) {
+ PropagationInfo& PInfo = Entry->second;
+ if (PInfo.isPointerToValue())
+ setStateForVarOrTmp(StateMap, PInfo, NS);
+ } else if (NS != CS_None) {
+ insertInfo(To, PropagationInfo(NS));
+ }
+}
+
+
+
void ConsumedStmtVisitor::checkCallability(const PropagationInfo &PInfo,
const FunctionDecl *FunDecl,
SourceLocation BlameLoc) {
assert(!PInfo.isTest());
-
- if (!FunDecl->hasAttr<CallableWhenAttr>())
- return;
-
+
const CallableWhenAttr *CWAttr = FunDecl->getAttr<CallableWhenAttr>();
-
+ if (!CWAttr)
+ return;
+
if (PInfo.isVar()) {
ConsumedState VarState = StateMap->getState(PInfo.getVar());
-
+
if (VarState == CS_None || isCallableInState(CWAttr, VarState))
return;
-
+
Analyzer.WarningsHandler.warnUseInInvalidState(
FunDecl->getNameAsString(), PInfo.getVar()->getNameAsString(),
stateToString(VarState), BlameLoc);
-
+
} else {
ConsumedState TmpState = PInfo.getAsState(StateMap);
-
+
if (TmpState == CS_None || isCallableInState(CWAttr, TmpState))
return;
-
+
Analyzer.WarningsHandler.warnUseOfTempInInvalidState(
FunDecl->getNameAsString(), stateToString(TmpState), BlameLoc);
}
}
-void ConsumedStmtVisitor::forwardInfo(const Stmt *From, const Stmt *To) {
- InfoEntry Entry = PropagationMap.find(From);
-
- if (Entry != PropagationMap.end())
- PropagationMap.insert(PairType(To, Entry->second));
+
+// Factors out common behavior for function, method, and operator calls.
+// Check parameters and set parameter state if necessary.
+// Returns true if the state of ObjArg is set, or false otherwise.
+bool ConsumedStmtVisitor::handleCall(const CallExpr *Call, const Expr *ObjArg,
+ const FunctionDecl *FunD) {
+ unsigned Offset = 0;
+ if (isa<CXXOperatorCallExpr>(Call) && isa<CXXMethodDecl>(FunD))
+ Offset = 1; // first argument is 'this'
+
+ // check explicit parameters
+ for (unsigned Index = Offset; Index < Call->getNumArgs(); ++Index) {
+ // Skip variable argument lists.
+ if (Index - Offset >= FunD->getNumParams())
+ break;
+
+ const ParmVarDecl *Param = FunD->getParamDecl(Index - Offset);
+ QualType ParamType = Param->getType();
+
+ InfoEntry Entry = findInfo(Call->getArg(Index));
+
+ if (Entry == PropagationMap.end() || Entry->second.isTest())
+ continue;
+ PropagationInfo PInfo = Entry->second;
+
+ // Check that the parameter is in the correct state.
+ if (ParamTypestateAttr *PTA = Param->getAttr<ParamTypestateAttr>()) {
+ ConsumedState ParamState = PInfo.getAsState(StateMap);
+ ConsumedState ExpectedState = mapParamTypestateAttrState(PTA);
+
+ if (ParamState != ExpectedState)
+ Analyzer.WarningsHandler.warnParamTypestateMismatch(
+ Call->getArg(Index)->getExprLoc(),
+ stateToString(ExpectedState), stateToString(ParamState));
+ }
+
+ if (!(Entry->second.isVar() || Entry->second.isTmp()))
+ continue;
+
+ // Adjust state on the caller side.
+ if (isRValueRef(ParamType))
+ setStateForVarOrTmp(StateMap, PInfo, consumed::CS_Consumed);
+ else if (ReturnTypestateAttr *RT = Param->getAttr<ReturnTypestateAttr>())
+ setStateForVarOrTmp(StateMap, PInfo, mapReturnTypestateAttrState(RT));
+ else if (isPointerOrRef(ParamType) &&
+ (!ParamType->getPointeeType().isConstQualified() ||
+ isSetOnReadPtrType(ParamType)))
+ setStateForVarOrTmp(StateMap, PInfo, consumed::CS_Unknown);
+ }
+
+ if (!ObjArg)
+ return false;
+
+ // check implicit 'self' parameter, if present
+ InfoEntry Entry = findInfo(ObjArg);
+ if (Entry != PropagationMap.end()) {
+ PropagationInfo PInfo = Entry->second;
+ checkCallability(PInfo, FunD, Call->getExprLoc());
+
+ if (SetTypestateAttr *STA = FunD->getAttr<SetTypestateAttr>()) {
+ if (PInfo.isVar()) {
+ StateMap->setState(PInfo.getVar(), mapSetTypestateAttrState(STA));
+ return true;
+ }
+ else if (PInfo.isTmp()) {
+ StateMap->setState(PInfo.getTmp(), mapSetTypestateAttrState(STA));
+ return true;
+ }
+ }
+ else if (isTestingFunction(FunD) && PInfo.isVar()) {
+ PropagationMap.insert(PairType(Call,
+ PropagationInfo(PInfo.getVar(), testsFor(FunD))));
+ }
+ }
+ return false;
}
-bool ConsumedStmtVisitor::isLikeMoveAssignment(
- const CXXMethodDecl *MethodDecl) {
-
- return MethodDecl->isMoveAssignmentOperator() ||
- (MethodDecl->getOverloadedOperator() == OO_Equal &&
- MethodDecl->getNumParams() == 1 &&
- MethodDecl->getParamDecl(0)->getType()->isRValueReferenceType());
-}
-void ConsumedStmtVisitor::propagateReturnType(const Stmt *Call,
- const FunctionDecl *Fun,
- QualType ReturnType) {
- if (isConsumableType(ReturnType)) {
-
+void ConsumedStmtVisitor::propagateReturnType(const Expr *Call,
+ const FunctionDecl *Fun) {
+ QualType RetType = Fun->getCallResultType();
+ if (RetType->isReferenceType())
+ RetType = RetType->getPointeeType();
+
+ if (isConsumableType(RetType)) {
ConsumedState ReturnState;
-
- if (Fun->hasAttr<ReturnTypestateAttr>())
- ReturnState = mapReturnTypestateAttrState(
- Fun->getAttr<ReturnTypestateAttr>());
+ if (ReturnTypestateAttr *RTA = Fun->getAttr<ReturnTypestateAttr>())
+ ReturnState = mapReturnTypestateAttrState(RTA);
else
- ReturnState = mapConsumableAttrState(ReturnType);
+ ReturnState = mapConsumableAttrState(RetType);
PropagationMap.insert(PairType(Call, PropagationInfo(ReturnState)));
}
}
+
void ConsumedStmtVisitor::VisitBinaryOperator(const BinaryOperator *BinOp) {
switch (BinOp->getOpcode()) {
case BO_LAnd:
case BO_LOr : {
- InfoEntry LEntry = PropagationMap.find(BinOp->getLHS()),
- REntry = PropagationMap.find(BinOp->getRHS());
+ InfoEntry LEntry = findInfo(BinOp->getLHS()),
+ REntry = findInfo(BinOp->getRHS());
VarTestResult LTest, RTest;
@@ -605,69 +745,32 @@
}
}
+static bool isStdNamespace(const DeclContext *DC) {
+ if (!DC->isNamespace()) return false;
+ while (DC->getParent()->isNamespace())
+ DC = DC->getParent();
+ const NamespaceDecl *ND = dyn_cast<NamespaceDecl>(DC);
+
+ return ND && ND->getName() == "std" &&
+ ND->getDeclContext()->isTranslationUnit();
+}
+
void ConsumedStmtVisitor::VisitCallExpr(const CallExpr *Call) {
- if (const FunctionDecl *FunDecl =
- dyn_cast_or_null<FunctionDecl>(Call->getDirectCallee())) {
-
- // Special case for the std::move function.
- // TODO: Make this more specific. (Deferred)
- if (FunDecl->getNameAsString() == "move") {
- forwardInfo(Call->getArg(0), Call);
- return;
- }
-
- unsigned Offset = Call->getNumArgs() - FunDecl->getNumParams();
-
- for (unsigned Index = Offset; Index < Call->getNumArgs(); ++Index) {
- const ParmVarDecl *Param = FunDecl->getParamDecl(Index - Offset);
- QualType ParamType = Param->getType();
-
- InfoEntry Entry = PropagationMap.find(Call->getArg(Index));
-
- if (Entry == PropagationMap.end() || Entry->second.isTest())
- continue;
-
- PropagationInfo PInfo = Entry->second;
-
- // Check that the parameter is in the correct state.
-
- if (Param->hasAttr<ParamTypestateAttr>()) {
- ConsumedState ParamState = PInfo.getAsState(StateMap);
-
- ConsumedState ExpectedState =
- mapParamTypestateAttrState(Param->getAttr<ParamTypestateAttr>());
-
- if (ParamState != ExpectedState)
- Analyzer.WarningsHandler.warnParamTypestateMismatch(
- Call->getArg(Index - Offset)->getExprLoc(),
- stateToString(ExpectedState), stateToString(ParamState));
- }
-
- if (!(Entry->second.isVar() || Entry->second.isTmp()))
- continue;
-
- // Adjust state on the caller side.
-
- if (isRValueRefish(ParamType)) {
- setStateForVarOrTmp(StateMap, PInfo, consumed::CS_Consumed);
-
- } else if (Param->hasAttr<ReturnTypestateAttr>()) {
- setStateForVarOrTmp(StateMap, PInfo,
- mapReturnTypestateAttrState(Param->getAttr<ReturnTypestateAttr>()));
-
- } else if (!isValueType(ParamType) &&
- !ParamType->getPointeeType().isConstQualified()) {
-
- setStateForVarOrTmp(StateMap, PInfo, consumed::CS_Unknown);
- }
- }
-
- QualType RetType = FunDecl->getCallResultType();
- if (RetType->isReferenceType())
- RetType = RetType->getPointeeType();
-
- propagateReturnType(Call, FunDecl, RetType);
+ const FunctionDecl *FunDecl = Call->getDirectCallee();
+ if (!FunDecl)
+ return;
+
+ // Special case for the std::move function.
+ // TODO: Make this more specific. (Deferred)
+ if (Call->getNumArgs() == 1 &&
+ FunDecl->getNameAsString() == "move" &&
+ isStdNamespace(FunDecl->getDeclContext())) {
+ copyInfo(Call->getArg(0), Call, CS_Consumed);
+ return;
}
+
+ handleCall(Call, 0, FunDecl);
+ propagateReturnType(Call, FunDecl);
}
void ConsumedStmtVisitor::VisitCastExpr(const CastExpr *Cast) {
@@ -677,7 +780,7 @@
void ConsumedStmtVisitor::VisitCXXBindTemporaryExpr(
const CXXBindTemporaryExpr *Temp) {
- InfoEntry Entry = PropagationMap.find(Temp->getSubExpr());
+ InfoEntry Entry = findInfo(Temp->getSubExpr());
if (Entry != PropagationMap.end() && !Entry->second.isTest()) {
StateMap->setState(Temp, Entry->second.getAsState(StateMap));
@@ -695,168 +798,60 @@
return;
// FIXME: What should happen if someone annotates the move constructor?
- if (Constructor->hasAttr<ReturnTypestateAttr>()) {
- // TODO: Adjust state of args appropriately.
-
- ReturnTypestateAttr *RTAttr = Constructor->getAttr<ReturnTypestateAttr>();
- ConsumedState RetState = mapReturnTypestateAttrState(RTAttr);
+ if (ReturnTypestateAttr *RTA = Constructor->getAttr<ReturnTypestateAttr>()) {
+ // TODO: Adjust state of args appropriately.
+ ConsumedState RetState = mapReturnTypestateAttrState(RTA);
PropagationMap.insert(PairType(Call, PropagationInfo(RetState)));
-
} else if (Constructor->isDefaultConstructor()) {
-
PropagationMap.insert(PairType(Call,
PropagationInfo(consumed::CS_Consumed)));
-
} else if (Constructor->isMoveConstructor()) {
-
- InfoEntry Entry = PropagationMap.find(Call->getArg(0));
-
- if (Entry != PropagationMap.end()) {
- PropagationInfo PInfo = Entry->second;
-
- if (PInfo.isVar()) {
- const VarDecl* Var = PInfo.getVar();
-
- PropagationMap.insert(PairType(Call,
- PropagationInfo(StateMap->getState(Var))));
-
- StateMap->setState(Var, consumed::CS_Consumed);
-
- } else if (PInfo.isTmp()) {
- const CXXBindTemporaryExpr *Tmp = PInfo.getTmp();
-
- PropagationMap.insert(PairType(Call,
- PropagationInfo(StateMap->getState(Tmp))));
-
- StateMap->setState(Tmp, consumed::CS_Consumed);
-
- } else {
- PropagationMap.insert(PairType(Call, PInfo));
- }
- }
+ copyInfo(Call->getArg(0), Call, CS_Consumed);
} else if (Constructor->isCopyConstructor()) {
- forwardInfo(Call->getArg(0), Call);
-
+ // Copy state from arg. If setStateOnRead then set arg to CS_Unknown.
+ ConsumedState NS =
+ isSetOnReadPtrType(Constructor->getThisType(CurrContext)) ?
+ CS_Unknown : CS_None;
+ copyInfo(Call->getArg(0), Call, NS);
} else {
// TODO: Adjust state of args appropriately.
-
ConsumedState RetState = mapConsumableAttrState(ThisType);
PropagationMap.insert(PairType(Call, PropagationInfo(RetState)));
}
}
+
void ConsumedStmtVisitor::VisitCXXMemberCallExpr(
- const CXXMemberCallExpr *Call) {
-
- VisitCallExpr(Call);
-
- InfoEntry Entry = PropagationMap.find(Call->getCallee()->IgnoreParens());
-
- if (Entry != PropagationMap.end()) {
- PropagationInfo PInfo = Entry->second;
- const CXXMethodDecl *MethodDecl = Call->getMethodDecl();
-
- checkCallability(PInfo, MethodDecl, Call->getExprLoc());
-
- if (PInfo.isVar()) {
- if (isTestingFunction(MethodDecl))
- PropagationMap.insert(PairType(Call,
- PropagationInfo(PInfo.getVar(), testsFor(MethodDecl))));
- else if (MethodDecl->hasAttr<SetTypestateAttr>())
- StateMap->setState(PInfo.getVar(),
- mapSetTypestateAttrState(MethodDecl->getAttr<SetTypestateAttr>()));
- } else if (PInfo.isTmp() && MethodDecl->hasAttr<SetTypestateAttr>()) {
- StateMap->setState(PInfo.getTmp(),
- mapSetTypestateAttrState(MethodDecl->getAttr<SetTypestateAttr>()));
- }
- }
+ const CXXMemberCallExpr *Call) {
+ CXXMethodDecl* MD = Call->getMethodDecl();
+ if (!MD)
+ return;
+
+ handleCall(Call, Call->getImplicitObjectArgument(), MD);
+ propagateReturnType(Call, MD);
}
+
void ConsumedStmtVisitor::VisitCXXOperatorCallExpr(
- const CXXOperatorCallExpr *Call) {
-
+ const CXXOperatorCallExpr *Call) {
+
const FunctionDecl *FunDecl =
dyn_cast_or_null<FunctionDecl>(Call->getDirectCallee());
-
if (!FunDecl) return;
-
- if (isa<CXXMethodDecl>(FunDecl) &&
- isLikeMoveAssignment(cast<CXXMethodDecl>(FunDecl))) {
-
- InfoEntry LEntry = PropagationMap.find(Call->getArg(0));
- InfoEntry REntry = PropagationMap.find(Call->getArg(1));
-
- PropagationInfo LPInfo, RPInfo;
-
- if (LEntry != PropagationMap.end() &&
- REntry != PropagationMap.end()) {
-
- LPInfo = LEntry->second;
- RPInfo = REntry->second;
-
- if (LPInfo.isPointerToValue() && RPInfo.isPointerToValue()) {
- setStateForVarOrTmp(StateMap, LPInfo, RPInfo.getAsState(StateMap));
- PropagationMap.insert(PairType(Call, LPInfo));
- setStateForVarOrTmp(StateMap, RPInfo, consumed::CS_Consumed);
-
- } else if (RPInfo.isState()) {
- setStateForVarOrTmp(StateMap, LPInfo, RPInfo.getState());
- PropagationMap.insert(PairType(Call, LPInfo));
-
- } else {
- setStateForVarOrTmp(StateMap, RPInfo, consumed::CS_Consumed);
- }
-
- } else if (LEntry != PropagationMap.end() &&
- REntry == PropagationMap.end()) {
-
- LPInfo = LEntry->second;
-
- assert(!LPInfo.isTest());
-
- if (LPInfo.isPointerToValue()) {
- setStateForVarOrTmp(StateMap, LPInfo, consumed::CS_Unknown);
- PropagationMap.insert(PairType(Call, LPInfo));
-
- } else {
- PropagationMap.insert(PairType(Call,
- PropagationInfo(consumed::CS_Unknown)));
- }
-
- } else if (LEntry == PropagationMap.end() &&
- REntry != PropagationMap.end()) {
-
- RPInfo = REntry->second;
-
- if (RPInfo.isPointerToValue())
- setStateForVarOrTmp(StateMap, RPInfo, consumed::CS_Consumed);
- }
-
- } else {
-
- VisitCallExpr(Call);
-
- InfoEntry Entry = PropagationMap.find(Call->getArg(0));
-
- if (Entry != PropagationMap.end()) {
- PropagationInfo PInfo = Entry->second;
-
- checkCallability(PInfo, FunDecl, Call->getExprLoc());
-
- if (PInfo.isVar()) {
- if (isTestingFunction(FunDecl))
- PropagationMap.insert(PairType(Call,
- PropagationInfo(PInfo.getVar(), testsFor(FunDecl))));
- else if (FunDecl->hasAttr<SetTypestateAttr>())
- StateMap->setState(PInfo.getVar(),
- mapSetTypestateAttrState(FunDecl->getAttr<SetTypestateAttr>()));
-
- } else if (PInfo.isTmp() && FunDecl->hasAttr<SetTypestateAttr>()) {
- StateMap->setState(PInfo.getTmp(),
- mapSetTypestateAttrState(FunDecl->getAttr<SetTypestateAttr>()));
- }
- }
+
+ if (Call->getOperator() == OO_Equal) {
+ ConsumedState CS = getInfo(Call->getArg(1));
+ if (!handleCall(Call, Call->getArg(0), FunDecl))
+ setInfo(Call->getArg(0), CS);
+ return;
}
+
+ if (const CXXMemberCallExpr *MCall = dyn_cast<CXXMemberCallExpr>(Call))
+ handleCall(MCall, MCall->getImplicitObjectArgument(), FunDecl);
+ else
+ handleCall(Call, Call->getArg(0), FunDecl);
+
+ propagateReturnType(Call, FunDecl);
}
void ConsumedStmtVisitor::VisitDeclRefExpr(const DeclRefExpr *DeclRef) {
@@ -866,11 +861,9 @@
}
void ConsumedStmtVisitor::VisitDeclStmt(const DeclStmt *DeclS) {
- for (DeclStmt::const_decl_iterator DI = DeclS->decl_begin(),
- DE = DeclS->decl_end(); DI != DE; ++DI) {
-
- if (isa<VarDecl>(*DI)) VisitVarDecl(cast<VarDecl>(*DI));
- }
+ for (const auto *DI : DeclS->decls())
+ if (isa<VarDecl>(DI))
+ VisitVarDecl(cast<VarDecl>(DI));
if (DeclS->isSingleDecl())
if (const VarDecl *Var = dyn_cast_or_null<VarDecl>(DeclS->getSingleDecl()))
@@ -892,22 +885,16 @@
QualType ParamType = Param->getType();
ConsumedState ParamState = consumed::CS_None;
- if (Param->hasAttr<ParamTypestateAttr>()) {
- const ParamTypestateAttr *PTAttr = Param->getAttr<ParamTypestateAttr>();
- ParamState = mapParamTypestateAttrState(PTAttr);
-
- } else if (isConsumableType(ParamType)) {
- ParamState = mapConsumableAttrState(ParamType);
-
- } else if (isRValueRefish(ParamType) &&
- isConsumableType(ParamType->getPointeeType())) {
-
- ParamState = mapConsumableAttrState(ParamType->getPointeeType());
-
- } else if (ParamType->isReferenceType() &&
- isConsumableType(ParamType->getPointeeType())) {
+ if (const ParamTypestateAttr *PTA = Param->getAttr<ParamTypestateAttr>())
+ ParamState = mapParamTypestateAttrState(PTA);
+ else if (isConsumableType(ParamType))
+ ParamState = mapConsumableAttrState(ParamType);
+ else if (isRValueRef(ParamType) &&
+ isConsumableType(ParamType->getPointeeType()))
+ ParamState = mapConsumableAttrState(ParamType->getPointeeType());
+ else if (ParamType->isReferenceType() &&
+ isConsumableType(ParamType->getPointeeType()))
ParamState = consumed::CS_Unknown;
- }
if (ParamState != CS_None)
StateMap->setState(Param, ParamState);
@@ -917,7 +904,7 @@
ConsumedState ExpectedState = Analyzer.getExpectedReturnState();
if (ExpectedState != CS_None) {
- InfoEntry Entry = PropagationMap.find(Ret->getRetValue());
+ InfoEntry Entry = findInfo(Ret->getRetValue());
if (Entry != PropagationMap.end()) {
ConsumedState RetState = Entry->second.getAsState(StateMap);
@@ -934,7 +921,7 @@
}
void ConsumedStmtVisitor::VisitUnaryOperator(const UnaryOperator *UOp) {
- InfoEntry Entry = PropagationMap.find(UOp->getSubExpr()->IgnoreParens());
+ InfoEntry Entry = findInfo(UOp->getSubExpr());
if (Entry == PropagationMap.end()) return;
switch (UOp->getOpcode()) {
@@ -956,8 +943,7 @@
void ConsumedStmtVisitor::VisitVarDecl(const VarDecl *Var) {
if (isConsumableType(Var->getType())) {
if (Var->hasInit()) {
- MapType::iterator VIT = PropagationMap.find(
- Var->getInit()->IgnoreImplicit());
+ MapType::iterator VIT = findInfo(Var->getInit()->IgnoreImplicit());
if (VIT != PropagationMap.end()) {
PropagationInfo PInfo = VIT->second;
ConsumedState St = PInfo.getAsState(StateMap);
@@ -1158,24 +1144,21 @@
void ConsumedStateMap::checkParamsForReturnTypestate(SourceLocation BlameLoc,
ConsumedWarningsHandlerBase &WarningsHandler) const {
- ConsumedState ExpectedState;
-
for (VarMapType::const_iterator DMI = VarMap.begin(), DME = VarMap.end();
DMI != DME; ++DMI) {
if (isa<ParmVarDecl>(DMI->first)) {
const ParmVarDecl *Param = cast<ParmVarDecl>(DMI->first);
+ const ReturnTypestateAttr *RTA = Param->getAttr<ReturnTypestateAttr>();
- if (!Param->hasAttr<ReturnTypestateAttr>()) continue;
+ if (!RTA)
+ continue;
- ExpectedState =
- mapReturnTypestateAttrState(Param->getAttr<ReturnTypestateAttr>());
-
- if (DMI->second != ExpectedState) {
+ ConsumedState ExpectedState = mapReturnTypestateAttrState(RTA);
+ if (DMI->second != ExpectedState)
WarningsHandler.warnParamReturnTypestateMismatch(BlameLoc,
Param->getNameAsString(), stateToString(ExpectedState),
stateToString(DMI->second));
- }
}
}
}
@@ -1286,9 +1269,7 @@
} else
ReturnType = D->getCallResultType();
- if (D->hasAttr<ReturnTypestateAttr>()) {
- const ReturnTypestateAttr *RTSAttr = D->getAttr<ReturnTypestateAttr>();
-
+ if (const ReturnTypestateAttr *RTSAttr = D->getAttr<ReturnTypestateAttr>()) {
const CXXRecordDecl *RD = ReturnType->getAsCXXRecordDecl();
if (!RD || !RD->hasAttr<ConsumableAttr>()) {
// FIXME: This should be removed when template instantiation propagates
@@ -1300,22 +1281,27 @@
ExpectedReturnState = CS_None;
} else
ExpectedReturnState = mapReturnTypestateAttrState(RTSAttr);
- } else if (isConsumableType(ReturnType))
- ExpectedReturnState = mapConsumableAttrState(ReturnType);
+ } else if (isConsumableType(ReturnType)) {
+ if (isAutoCastType(ReturnType)) // We can auto-cast the state to the
+ ExpectedReturnState = CS_None; // expected state.
+ else
+ ExpectedReturnState = mapConsumableAttrState(ReturnType);
+ }
else
ExpectedReturnState = CS_None;
}
bool ConsumedAnalyzer::splitState(const CFGBlock *CurrBlock,
const ConsumedStmtVisitor &Visitor) {
-
- OwningPtr<ConsumedStateMap> FalseStates(new ConsumedStateMap(*CurrStates));
+
+ std::unique_ptr<ConsumedStateMap> FalseStates(
+ new ConsumedStateMap(*CurrStates));
PropagationInfo PInfo;
if (const IfStmt *IfNode =
dyn_cast_or_null<IfStmt>(CurrBlock->getTerminator().getStmt())) {
- const Stmt *Cond = IfNode->getCond();
+ const Expr *Cond = IfNode->getCond();
PInfo = Visitor.getInfo(Cond);
if (!PInfo.isValid() && isa<BinaryOperator>(Cond))
@@ -1384,8 +1370,8 @@
delete CurrStates;
if (*++SI)
- BlockInfo.addInfo(*SI, FalseStates.take());
-
+ BlockInfo.addInfo(*SI, FalseStates.release());
+
CurrStates = NULL;
return true;
}
@@ -1410,9 +1396,8 @@
ConsumedStmtVisitor Visitor(AC, *this, CurrStates);
// Add all trackable parameters to the state map.
- for (FunctionDecl::param_const_iterator PI = D->param_begin(),
- PE = D->param_end(); PI != PE; ++PI) {
- Visitor.VisitParmVarDecl(*PI);
+ for (auto PI : D->params()) {
+ Visitor.VisitParmVarDecl(PI);
}
// Visit all of the function's basic blocks.
diff --git a/lib/Analysis/LiveVariables.cpp b/lib/Analysis/LiveVariables.cpp
index 9e5ec55..b26d4fb 100644
--- a/lib/Analysis/LiveVariables.cpp
+++ b/lib/Analysis/LiveVariables.cpp
@@ -37,7 +37,6 @@
POV(Ctx.getAnalysis<PostOrderCFGView>()) {}
void enqueueBlock(const CFGBlock *block);
- void enqueueSuccessors(const CFGBlock *block);
void enqueuePredecessors(const CFGBlock *block);
const CFGBlock *dequeue();
@@ -53,19 +52,6 @@
worklist.push_back(block);
}
}
-
-void DataflowWorklist::enqueueSuccessors(const clang::CFGBlock *block) {
- const unsigned OldWorklistSize = worklist.size();
- for (CFGBlock::const_succ_iterator I = block->succ_begin(),
- E = block->succ_end(); I != E; ++I) {
- enqueueBlock(*I);
- }
-
- if (OldWorklistSize == 0 || OldWorklistSize == worklist.size())
- return;
-
- sortWorklist();
-}
void DataflowWorklist::enqueuePredecessors(const clang::CFGBlock *block) {
const unsigned OldWorklistSize = worklist.size();
@@ -372,7 +358,7 @@
void TransferFunctions::VisitBlockExpr(BlockExpr *BE) {
AnalysisDeclContext::referenced_decls_iterator I, E;
- llvm::tie(I, E) =
+ std::tie(I, E) =
LV.analysisContext.getReferencedBlockVars(BE->getBlockDecl());
for ( ; I != E ; ++I) {
const VarDecl *VD = *I;
@@ -389,9 +375,8 @@
}
void TransferFunctions::VisitDeclStmt(DeclStmt *DS) {
- for (DeclStmt::decl_iterator DI=DS->decl_begin(), DE = DS->decl_end();
- DI != DE; ++DI)
- if (VarDecl *VD = dyn_cast<VarDecl>(*DI)) {
+ for (const auto *DI : DS->decls())
+ if (const auto *VD = dyn_cast<VarDecl>(DI)) {
if (!isAlwaysAlive(VD))
val.liveDecls = LV.DSetFact.remove(val.liveDecls, VD);
}
@@ -581,16 +566,6 @@
return new LiveVariables(LV);
}
-static bool compare_entries(const CFGBlock *A, const CFGBlock *B) {
- return A->getBlockID() < B->getBlockID();
-}
-
-static bool compare_vd_entries(const Decl *A, const Decl *B) {
- SourceLocation ALoc = A->getLocStart();
- SourceLocation BLoc = B->getLocStart();
- return ALoc.getRawEncoding() < BLoc.getRawEncoding();
-}
-
void LiveVariables::dumpBlockLiveness(const SourceManager &M) {
getImpl(impl).dumpBlockLiveness(M);
}
@@ -602,7 +577,9 @@
it != ei; ++it) {
vec.push_back(it->first);
}
- std::sort(vec.begin(), vec.end(), compare_entries);
+ std::sort(vec.begin(), vec.end(), [](const CFGBlock *A, const CFGBlock *B) {
+ return A->getBlockID() < B->getBlockID();
+ });
std::vector<const VarDecl*> declVec;
@@ -619,9 +596,11 @@
se = vals.liveDecls.end(); si != se; ++si) {
declVec.push_back(*si);
}
-
- std::sort(declVec.begin(), declVec.end(), compare_vd_entries);
-
+
+ std::sort(declVec.begin(), declVec.end(), [](const Decl *A, const Decl *B) {
+ return A->getLocStart() < B->getLocStart();
+ });
+
for (std::vector<const VarDecl*>::iterator di = declVec.begin(),
de = declVec.end(); di != de; ++di) {
llvm::errs() << " " << (*di)->getDeclName().getAsString()
diff --git a/lib/Analysis/NOTICE b/lib/Analysis/NOTICE
deleted file mode 100644
index 369204e..0000000
--- a/lib/Analysis/NOTICE
+++ /dev/null
@@ -1,62 +0,0 @@
-==============================================================================
-LLVM Release License
-==============================================================================
-University of Illinois/NCSA
-Open Source License
-
-Copyright (c) 2007-2011 University of Illinois at Urbana-Champaign.
-All rights reserved.
-
-Developed by:
-
- LLVM Team
-
- University of Illinois at Urbana-Champaign
-
- http://llvm.org
-
-Permission is hereby granted, free of charge, to any person obtaining a copy of
-this software and associated documentation files (the "Software"), to deal with
-the Software without restriction, including without limitation the rights to
-use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
-of the Software, and to permit persons to whom the Software is furnished to do
-so, subject to the following conditions:
-
- * Redistributions of source code must retain the above copyright notice,
- this list of conditions and the following disclaimers.
-
- * Redistributions in binary form must reproduce the above copyright notice,
- this list of conditions and the following disclaimers in the
- documentation and/or other materials provided with the distribution.
-
- * Neither the names of the LLVM Team, University of Illinois at
- Urbana-Champaign, nor the names of its contributors may be used to
- endorse or promote products derived from this Software without specific
- prior written permission.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
-FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE
-SOFTWARE.
-
-==============================================================================
-The LLVM software contains code written by third parties. Such software will
-have its own individual LICENSE.TXT file in the directory in which it appears.
-This file will describe the copyrights, license, and restrictions which apply
-to that code.
-
-The disclaimer of warranty in the University of Illinois Open Source License
-applies to all code in the LLVM Distribution, and nothing in any of the
-other licenses gives permission to use the names of the LLVM Team or the
-University of Illinois to endorse or promote products derived from this
-Software.
-
-The following pieces of software have additional or alternate copyrights,
-licenses, and/or restrictions:
-
-Program Directory
-------- ---------
-<none yet>
diff --git a/lib/Analysis/ProgramPoint.cpp b/lib/Analysis/ProgramPoint.cpp
index 7d67e8a..26b59bb 100644
--- a/lib/Analysis/ProgramPoint.cpp
+++ b/lib/Analysis/ProgramPoint.cpp
@@ -43,9 +43,10 @@
}
}
-SimpleProgramPointTag::SimpleProgramPointTag(StringRef description)
- : desc(description) {}
+SimpleProgramPointTag::SimpleProgramPointTag(StringRef MsgProvider,
+ StringRef Msg)
+ : Desc((MsgProvider + " : " + Msg).str()) {}
StringRef SimpleProgramPointTag::getTagDescription() const {
- return desc;
+ return Desc;
}
diff --git a/lib/Analysis/PseudoConstantAnalysis.cpp b/lib/Analysis/PseudoConstantAnalysis.cpp
index 5d659ce..314ce7c 100644
--- a/lib/Analysis/PseudoConstantAnalysis.cpp
+++ b/lib/Analysis/PseudoConstantAnalysis.cpp
@@ -171,10 +171,9 @@
case Stmt::DeclStmtClass: {
const DeclStmt *DS = cast<DeclStmt>(Head);
// Iterate over each decl and see if any of them contain reference decls
- for (DeclStmt::const_decl_iterator I = DS->decl_begin(),
- E = DS->decl_end(); I != E; ++I) {
+ for (const auto *I : DS->decls()) {
// We only care about VarDecls
- const VarDecl *VD = dyn_cast<VarDecl>(*I);
+ const VarDecl *VD = dyn_cast<VarDecl>(I);
if (!VD)
continue;
diff --git a/lib/Analysis/ReachableCode.cpp b/lib/Analysis/ReachableCode.cpp
index a2d19c0..3cc8ae4 100644
--- a/lib/Analysis/ReachableCode.cpp
+++ b/lib/Analysis/ReachableCode.cpp
@@ -13,10 +13,12 @@
//===----------------------------------------------------------------------===//
#include "clang/Analysis/Analyses/ReachableCode.h"
+#include "clang/Lex/Preprocessor.h"
#include "clang/AST/Expr.h"
#include "clang/AST/ExprCXX.h"
#include "clang/AST/ExprObjC.h"
#include "clang/AST/StmtCXX.h"
+#include "clang/AST/ParentMap.h"
#include "clang/Analysis/AnalysisContext.h"
#include "clang/Analysis/CFG.h"
#include "clang/Basic/SourceManager.h"
@@ -25,36 +27,324 @@
using namespace clang;
-namespace {
-class DeadCodeScan {
- llvm::BitVector Visited;
- llvm::BitVector &Reachable;
- SmallVector<const CFGBlock *, 10> WorkList;
-
- typedef SmallVector<std::pair<const CFGBlock *, const Stmt *>, 12>
- DeferredLocsTy;
-
- DeferredLocsTy DeferredLocs;
-
-public:
- DeadCodeScan(llvm::BitVector &reachable)
- : Visited(reachable.size()),
- Reachable(reachable) {}
-
- void enqueue(const CFGBlock *block);
- unsigned scanBackwards(const CFGBlock *Start,
- clang::reachable_code::Callback &CB);
-
- bool isDeadCodeRoot(const CFGBlock *Block);
-
- const Stmt *findDeadCode(const CFGBlock *Block);
-
- void reportDeadCode(const Stmt *S,
- clang::reachable_code::Callback &CB);
-};
+//===----------------------------------------------------------------------===//
+// Core Reachability Analysis routines.
+//===----------------------------------------------------------------------===//
+
+static bool isEnumConstant(const Expr *Ex) {
+ const DeclRefExpr *DR = dyn_cast<DeclRefExpr>(Ex);
+ if (!DR)
+ return false;
+ return isa<EnumConstantDecl>(DR->getDecl());
}
-void DeadCodeScan::enqueue(const CFGBlock *block) {
+static bool isTrivialExpression(const Expr *Ex) {
+ Ex = Ex->IgnoreParenCasts();
+ return isa<IntegerLiteral>(Ex) || isa<StringLiteral>(Ex) ||
+ isa<CXXBoolLiteralExpr>(Ex) || isa<ObjCBoolLiteralExpr>(Ex) ||
+ isa<CharacterLiteral>(Ex) ||
+ isEnumConstant(Ex);
+}
+
+static bool isTrivialDoWhile(const CFGBlock *B, const Stmt *S) {
+ // Check if the block ends with a do...while() and see if 'S' is the
+ // condition.
+ if (const Stmt *Term = B->getTerminator()) {
+ if (const DoStmt *DS = dyn_cast<DoStmt>(Term)) {
+ const Expr *Cond = DS->getCond()->IgnoreParenCasts();
+ return Cond == S && isTrivialExpression(Cond);
+ }
+ }
+ return false;
+}
+
+static bool isDeadReturn(const CFGBlock *B, const Stmt *S) {
+ // Look to see if the block ends with a 'return', and see if 'S'
+ // is a substatement. The 'return' may not be the last element in
+ // the block because of destructors.
+ for (CFGBlock::const_reverse_iterator I = B->rbegin(), E = B->rend();
+ I != E; ++I) {
+ if (Optional<CFGStmt> CS = I->getAs<CFGStmt>()) {
+ if (const ReturnStmt *RS = dyn_cast<ReturnStmt>(CS->getStmt())) {
+ if (RS == S)
+ return true;
+ if (const Expr *RE = RS->getRetValue()) {
+ RE = RE->IgnoreParenCasts();
+ if (RE == S)
+ return true;
+ ParentMap PM(const_cast<Expr*>(RE));
+ // If 'S' is in the ParentMap, it is a subexpression of
+ // the return statement. Note also that we are restricting
+ // to looking at return statements in the same CFGBlock,
+ // so this will intentionally not catch cases where the
+ // return statement contains nested control-flow.
+ return PM.getParent(S);
+ }
+ }
+ break;
+ }
+ }
+ return false;
+}
+
+static SourceLocation getTopMostMacro(SourceLocation Loc, SourceManager &SM) {
+ assert(Loc.isMacroID());
+ SourceLocation Last;
+ while (Loc.isMacroID()) {
+ Last = Loc;
+ Loc = SM.getImmediateMacroCallerLoc(Loc);
+ }
+ return Last;
+}
+
+/// Returns true if the statement is expanded from a configuration macro.
+static bool isExpandedFromConfigurationMacro(const Stmt *S,
+ Preprocessor &PP,
+ bool IgnoreYES_NO = false) {
+ // FIXME: This is not very precise. Here we just check to see if the
+ // value comes from a macro, but we can do much better. This is likely
+ // to be over conservative. This logic is factored into a separate function
+ // so that we can refine it later.
+ SourceLocation L = S->getLocStart();
+ if (L.isMacroID()) {
+ if (IgnoreYES_NO) {
+ // The Objective-C constant 'YES' and 'NO'
+ // are defined as macros. Do not treat them
+ // as configuration values.
+ SourceManager &SM = PP.getSourceManager();
+ SourceLocation TopL = getTopMostMacro(L, SM);
+ StringRef MacroName = PP.getImmediateMacroName(TopL);
+ if (MacroName == "YES" || MacroName == "NO")
+ return false;
+ }
+ return true;
+ }
+ return false;
+}
+
+static bool isConfigurationValue(const ValueDecl *D, Preprocessor &PP);
+
+/// Returns true if the statement represents a configuration value.
+///
+/// A configuration value is something usually determined at compile-time
+/// to conditionally always execute some branch. Such guards are for
+/// "sometimes unreachable" code. Such code is usually not interesting
+/// to report as unreachable, and may mask truly unreachable code within
+/// those blocks.
+static bool isConfigurationValue(const Stmt *S,
+ Preprocessor &PP,
+ SourceRange *SilenceableCondVal = nullptr,
+ bool IncludeIntegers = true,
+ bool WrappedInParens = false) {
+ if (!S)
+ return false;
+
+ // Special case looking for the sigil '()' around an integer literal.
+ if (const ParenExpr *PE = dyn_cast<ParenExpr>(S))
+ if (!PE->getLocStart().isMacroID())
+ return isConfigurationValue(PE->getSubExpr(), PP, SilenceableCondVal,
+ IncludeIntegers, true);
+
+ if (const Expr *Ex = dyn_cast<Expr>(S))
+ S = Ex->IgnoreParenCasts();
+
+ bool IgnoreYES_NO = false;
+
+ switch (S->getStmtClass()) {
+ case Stmt::CallExprClass: {
+ const FunctionDecl *Callee =
+ dyn_cast_or_null<FunctionDecl>(cast<CallExpr>(S)->getCalleeDecl());
+ return Callee ? Callee->isConstexpr() : false;
+ }
+ case Stmt::DeclRefExprClass:
+ return isConfigurationValue(cast<DeclRefExpr>(S)->getDecl(), PP);
+ case Stmt::ObjCBoolLiteralExprClass:
+ IgnoreYES_NO = true;
+ // Fallthrough.
+ case Stmt::CXXBoolLiteralExprClass:
+ case Stmt::IntegerLiteralClass: {
+ const Expr *E = cast<Expr>(S);
+ if (IncludeIntegers) {
+ if (SilenceableCondVal && !SilenceableCondVal->getBegin().isValid())
+ *SilenceableCondVal = E->getSourceRange();
+ return WrappedInParens || isExpandedFromConfigurationMacro(E, PP, IgnoreYES_NO);
+ }
+ return false;
+ }
+ case Stmt::MemberExprClass:
+ return isConfigurationValue(cast<MemberExpr>(S)->getMemberDecl(), PP);
+ case Stmt::UnaryExprOrTypeTraitExprClass:
+ return true;
+ case Stmt::BinaryOperatorClass: {
+ const BinaryOperator *B = cast<BinaryOperator>(S);
+ // Only include raw integers (not enums) as configuration
+ // values if they are used in a logical or comparison operator
+ // (not arithmetic).
+ IncludeIntegers &= (B->isLogicalOp() || B->isComparisonOp());
+ return isConfigurationValue(B->getLHS(), PP, SilenceableCondVal,
+ IncludeIntegers) ||
+ isConfigurationValue(B->getRHS(), PP, SilenceableCondVal,
+ IncludeIntegers);
+ }
+ case Stmt::UnaryOperatorClass: {
+ const UnaryOperator *UO = cast<UnaryOperator>(S);
+ if (SilenceableCondVal)
+ *SilenceableCondVal = UO->getSourceRange();
+ return UO->getOpcode() == UO_LNot &&
+ isConfigurationValue(UO->getSubExpr(), PP, SilenceableCondVal,
+ IncludeIntegers, WrappedInParens);
+ }
+ default:
+ return false;
+ }
+}
+
+static bool isConfigurationValue(const ValueDecl *D, Preprocessor &PP) {
+ if (const EnumConstantDecl *ED = dyn_cast<EnumConstantDecl>(D))
+ return isConfigurationValue(ED->getInitExpr(), PP);
+ if (const VarDecl *VD = dyn_cast<VarDecl>(D)) {
+ // As a heuristic, treat globals as configuration values. Note
+ // that we only will get here if Sema evaluated this
+ // condition to a constant expression, which means the global
+ // had to be declared in a way to be a truly constant value.
+ // We could generalize this to local variables, but it isn't
+ // clear if those truly represent configuration values that
+ // gate unreachable code.
+ if (!VD->hasLocalStorage())
+ return true;
+
+ // As a heuristic, locals that have been marked 'const' explicitly
+ // can be treated as configuration values as well.
+ return VD->getType().isLocalConstQualified();
+ }
+ return false;
+}
+
+/// Returns true if we should always explore all successors of a block.
+static bool shouldTreatSuccessorsAsReachable(const CFGBlock *B,
+ Preprocessor &PP) {
+ if (const Stmt *Term = B->getTerminator()) {
+ if (isa<SwitchStmt>(Term))
+ return true;
+ // Specially handle '||' and '&&'.
+ if (isa<BinaryOperator>(Term)) {
+ return isConfigurationValue(Term, PP);
+ }
+ }
+
+ const Stmt *Cond = B->getTerminatorCondition(/* stripParens */ false);
+ return isConfigurationValue(Cond, PP);
+}
+
+static unsigned scanFromBlock(const CFGBlock *Start,
+ llvm::BitVector &Reachable,
+ Preprocessor *PP,
+ bool IncludeSometimesUnreachableEdges) {
+ unsigned count = 0;
+
+ // Prep work queue
+ SmallVector<const CFGBlock*, 32> WL;
+
+ // The entry block may have already been marked reachable
+ // by the caller.
+ if (!Reachable[Start->getBlockID()]) {
+ ++count;
+ Reachable[Start->getBlockID()] = true;
+ }
+
+ WL.push_back(Start);
+
+ // Find the reachable blocks from 'Start'.
+ while (!WL.empty()) {
+ const CFGBlock *item = WL.pop_back_val();
+
+ // There are cases where we want to treat all successors as reachable.
+ // The idea is that some "sometimes unreachable" code is not interesting,
+ // and that we should forge ahead and explore those branches anyway.
+ // This allows us to potentially uncover some "always unreachable" code
+ // within the "sometimes unreachable" code.
+ // Look at the successors and mark then reachable.
+ Optional<bool> TreatAllSuccessorsAsReachable;
+ if (!IncludeSometimesUnreachableEdges)
+ TreatAllSuccessorsAsReachable = false;
+
+ for (CFGBlock::const_succ_iterator I = item->succ_begin(),
+ E = item->succ_end(); I != E; ++I) {
+ const CFGBlock *B = *I;
+ if (!B) do {
+ const CFGBlock *UB = I->getPossiblyUnreachableBlock();
+ if (!UB)
+ break;
+
+ if (!TreatAllSuccessorsAsReachable.hasValue()) {
+ assert(PP);
+ TreatAllSuccessorsAsReachable =
+ shouldTreatSuccessorsAsReachable(item, *PP);
+ }
+
+ if (TreatAllSuccessorsAsReachable.getValue()) {
+ B = UB;
+ break;
+ }
+ }
+ while (false);
+
+ if (B) {
+ unsigned blockID = B->getBlockID();
+ if (!Reachable[blockID]) {
+ Reachable.set(blockID);
+ WL.push_back(B);
+ ++count;
+ }
+ }
+ }
+ }
+ return count;
+}
+
+static unsigned scanMaybeReachableFromBlock(const CFGBlock *Start,
+ Preprocessor &PP,
+ llvm::BitVector &Reachable) {
+ return scanFromBlock(Start, Reachable, &PP, true);
+}
+
+//===----------------------------------------------------------------------===//
+// Dead Code Scanner.
+//===----------------------------------------------------------------------===//
+
+namespace {
+ class DeadCodeScan {
+ llvm::BitVector Visited;
+ llvm::BitVector &Reachable;
+ SmallVector<const CFGBlock *, 10> WorkList;
+ Preprocessor &PP;
+
+ typedef SmallVector<std::pair<const CFGBlock *, const Stmt *>, 12>
+ DeferredLocsTy;
+
+ DeferredLocsTy DeferredLocs;
+
+ public:
+ DeadCodeScan(llvm::BitVector &reachable, Preprocessor &PP)
+ : Visited(reachable.size()),
+ Reachable(reachable),
+ PP(PP) {}
+
+ void enqueue(const CFGBlock *block);
+ unsigned scanBackwards(const CFGBlock *Start,
+ clang::reachable_code::Callback &CB);
+
+ bool isDeadCodeRoot(const CFGBlock *Block);
+
+ const Stmt *findDeadCode(const CFGBlock *Block);
+
+ void reportDeadCode(const CFGBlock *B,
+ const Stmt *S,
+ clang::reachable_code::Callback &CB);
+ };
+}
+
+void DeadCodeScan::enqueue(const CFGBlock *block) {
unsigned blockID = block->getBlockID();
if (Reachable[blockID] || Visited[blockID])
return;
@@ -64,9 +354,9 @@
bool DeadCodeScan::isDeadCodeRoot(const clang::CFGBlock *Block) {
bool isDeadRoot = true;
-
+
for (CFGBlock::const_pred_iterator I = Block->pred_begin(),
- E = Block->pred_end(); I != E; ++I) {
+ E = Block->pred_end(); I != E; ++I) {
if (const CFGBlock *PredBlock = *I) {
unsigned blockID = PredBlock->getBlockID();
if (Visited[blockID]) {
@@ -81,7 +371,7 @@
}
}
}
-
+
return isDeadRoot;
}
@@ -100,11 +390,13 @@
if (isValidDeadStmt(S))
return S;
}
-
+
if (CFGTerminator T = Block->getTerminator()) {
- const Stmt *S = T.getStmt();
- if (isValidDeadStmt(S))
- return S;
+ if (!T.isTemporaryDtorsBranch()) {
+ const Stmt *S = T.getStmt();
+ if (isValidDeadStmt(S))
+ return S;
+ }
}
return 0;
@@ -124,7 +416,7 @@
unsigned count = 0;
enqueue(Start);
-
+
while (!WorkList.empty()) {
const CFGBlock *Block = WorkList.pop_back_val();
@@ -135,7 +427,7 @@
// Look for any dead code within the block.
const Stmt *S = findDeadCode(Block);
-
+
if (!S) {
// No dead code. Possibly an empty block. Look at dead predecessors.
for (CFGBlock::const_pred_iterator I = Block->pred_begin(),
@@ -145,16 +437,16 @@
}
continue;
}
-
+
// Specially handle macro-expanded code.
if (S->getLocStart().isMacroID()) {
- count += clang::reachable_code::ScanReachableFromBlock(Block, Reachable);
+ count += scanMaybeReachableFromBlock(Block, PP, Reachable);
continue;
}
if (isDeadCodeRoot(Block)) {
- reportDeadCode(S, CB);
- count += clang::reachable_code::ScanReachableFromBlock(Block, Reachable);
+ reportDeadCode(Block, S, CB);
+ count += scanMaybeReachableFromBlock(Block, PP, Reachable);
}
else {
// Record this statement as the possibly best location in a
@@ -169,15 +461,15 @@
if (!DeferredLocs.empty()) {
llvm::array_pod_sort(DeferredLocs.begin(), DeferredLocs.end(), SrcCmp);
for (DeferredLocsTy::iterator I = DeferredLocs.begin(),
- E = DeferredLocs.end(); I != E; ++I) {
- const CFGBlock *block = I->first;
- if (Reachable[block->getBlockID()])
+ E = DeferredLocs.end(); I != E; ++I) {
+ const CFGBlock *Block = I->first;
+ if (Reachable[Block->getBlockID()])
continue;
- reportDeadCode(I->second, CB);
- count += clang::reachable_code::ScanReachableFromBlock(block, Reachable);
+ reportDeadCode(Block, I->second, CB);
+ count += scanMaybeReachableFromBlock(Block, PP, Reachable);
}
}
-
+
return count;
}
@@ -208,7 +500,7 @@
case Expr::BinaryConditionalOperatorClass:
case Expr::ConditionalOperatorClass: {
const AbstractConditionalOperator *CO =
- cast<AbstractConditionalOperator>(S);
+ cast<AbstractConditionalOperator>(S);
return CO->getQuestionLoc();
}
case Expr::MemberExprClass: {
@@ -246,61 +538,86 @@
return S->getLocStart();
}
-void DeadCodeScan::reportDeadCode(const Stmt *S,
+void DeadCodeScan::reportDeadCode(const CFGBlock *B,
+ const Stmt *S,
clang::reachable_code::Callback &CB) {
+ // Classify the unreachable code found, or suppress it in some cases.
+ reachable_code::UnreachableKind UK = reachable_code::UK_Other;
+
+ if (isa<BreakStmt>(S)) {
+ UK = reachable_code::UK_Break;
+ }
+ else if (isTrivialDoWhile(B, S)) {
+ return;
+ }
+ else if (isDeadReturn(B, S)) {
+ UK = reachable_code::UK_Return;
+ }
+
+ SourceRange SilenceableCondVal;
+
+ if (UK == reachable_code::UK_Other) {
+ // Check if the dead code is part of the "loop target" of
+ // a for/for-range loop. This is the block that contains
+ // the increment code.
+ if (const Stmt *LoopTarget = B->getLoopTarget()) {
+ SourceLocation Loc = LoopTarget->getLocStart();
+ SourceRange R1(Loc, Loc), R2;
+
+ if (const ForStmt *FS = dyn_cast<ForStmt>(LoopTarget)) {
+ const Expr *Inc = FS->getInc();
+ Loc = Inc->getLocStart();
+ R2 = Inc->getSourceRange();
+ }
+
+ CB.HandleUnreachable(reachable_code::UK_Loop_Increment,
+ Loc, SourceRange(), SourceRange(Loc, Loc), R2);
+ return;
+ }
+
+ // Check if the dead block has a predecessor whose branch has
+ // a configuration value that *could* be modified to
+ // silence the warning.
+ CFGBlock::const_pred_iterator PI = B->pred_begin();
+ if (PI != B->pred_end()) {
+ if (const CFGBlock *PredBlock = PI->getPossiblyUnreachableBlock()) {
+ const Stmt *TermCond =
+ PredBlock->getTerminatorCondition(/* strip parens */ false);
+ isConfigurationValue(TermCond, PP, &SilenceableCondVal);
+ }
+ }
+ }
+
SourceRange R1, R2;
SourceLocation Loc = GetUnreachableLoc(S, R1, R2);
- CB.HandleUnreachable(Loc, R1, R2);
+ CB.HandleUnreachable(UK, Loc, SilenceableCondVal, R1, R2);
}
+//===----------------------------------------------------------------------===//
+// Reachability APIs.
+//===----------------------------------------------------------------------===//
+
namespace clang { namespace reachable_code {
-void Callback::anchor() { }
+void Callback::anchor() { }
unsigned ScanReachableFromBlock(const CFGBlock *Start,
llvm::BitVector &Reachable) {
- unsigned count = 0;
-
- // Prep work queue
- SmallVector<const CFGBlock*, 32> WL;
-
- // The entry block may have already been marked reachable
- // by the caller.
- if (!Reachable[Start->getBlockID()]) {
- ++count;
- Reachable[Start->getBlockID()] = true;
- }
-
- WL.push_back(Start);
-
- // Find the reachable blocks from 'Start'.
- while (!WL.empty()) {
- const CFGBlock *item = WL.pop_back_val();
-
- // Look at the successors and mark then reachable.
- for (CFGBlock::const_succ_iterator I = item->succ_begin(),
- E = item->succ_end(); I != E; ++I)
- if (const CFGBlock *B = *I) {
- unsigned blockID = B->getBlockID();
- if (!Reachable[blockID]) {
- Reachable.set(blockID);
- WL.push_back(B);
- ++count;
- }
- }
- }
- return count;
+ return scanFromBlock(Start, Reachable, /* SourceManager* */ 0, false);
}
-
-void FindUnreachableCode(AnalysisDeclContext &AC, Callback &CB) {
+
+void FindUnreachableCode(AnalysisDeclContext &AC, Preprocessor &PP,
+ Callback &CB) {
+
CFG *cfg = AC.getCFG();
if (!cfg)
return;
- // Scan for reachable blocks from the entrance of the CFG.
+ // Scan for reachable blocks from the entrance of the CFG.
// If there are no unreachable blocks, we're done.
llvm::BitVector reachable(cfg->getNumBlockIDs());
- unsigned numReachable = ScanReachableFromBlock(&cfg->getEntry(), reachable);
+ unsigned numReachable =
+ scanMaybeReachableFromBlock(&cfg->getEntry(), PP, reachable);
if (numReachable == cfg->getNumBlockIDs())
return;
@@ -309,7 +626,7 @@
if (!AC.getCFGBuildOptions().AddEHEdges) {
for (CFG::try_block_iterator I = cfg->try_blocks_begin(),
E = cfg->try_blocks_end() ; I != E; ++I) {
- numReachable += ScanReachableFromBlock(*I, reachable);
+ numReachable += scanMaybeReachableFromBlock(*I, PP, reachable);
}
if (numReachable == cfg->getNumBlockIDs())
return;
@@ -323,7 +640,7 @@
if (reachable[block->getBlockID()])
continue;
- DeadCodeScan DS(reachable);
+ DeadCodeScan DS(reachable, PP);
numReachable += DS.scanBackwards(block, CB);
if (numReachable == cfg->getNumBlockIDs())
diff --git a/lib/Analysis/ScanfFormatString.cpp b/lib/Analysis/ScanfFormatString.cpp
index f5ce84f..3ff7f0a 100644
--- a/lib/Analysis/ScanfFormatString.cpp
+++ b/lib/Analysis/ScanfFormatString.cpp
@@ -379,21 +379,23 @@
return ArgType();
}
-bool ScanfSpecifier::fixType(QualType QT, const LangOptions &LangOpt,
+bool ScanfSpecifier::fixType(QualType QT, QualType RawQT,
+ const LangOptions &LangOpt,
ASTContext &Ctx) {
- if (!QT->isPointerType())
- return false;
// %n is different from other conversion specifiers; don't try to fix it.
if (CS.getKind() == ConversionSpecifier::nArg)
return false;
+ if (!QT->isPointerType())
+ return false;
+
QualType PT = QT->getPointeeType();
// If it's an enum, get its underlying type.
- if (const EnumType *ETy = QT->getAs<EnumType>())
- QT = ETy->getDecl()->getIntegerType();
-
+ if (const EnumType *ETy = PT->getAs<EnumType>())
+ PT = ETy->getDecl()->getIntegerType();
+
const BuiltinType *BT = PT->getAs<BuiltinType>();
if (!BT)
return false;
@@ -405,6 +407,15 @@
LM.setKind(LengthModifier::AsWideChar);
else
LM.setKind(LengthModifier::None);
+
+ // If we know the target array length, we can use it as a field width.
+ if (const ConstantArrayType *CAT = Ctx.getAsConstantArrayType(RawQT)) {
+ if (CAT->getSizeModifier() == ArrayType::Normal)
+ FieldWidth = OptionalAmount(OptionalAmount::Constant,
+ CAT->getSize().getZExtValue() - 1,
+ "", 0, false);
+
+ }
return true;
}
diff --git a/lib/Analysis/ThreadSafety.cpp b/lib/Analysis/ThreadSafety.cpp
index 6e0e173..fd804eb 100644
--- a/lib/Analysis/ThreadSafety.cpp
+++ b/lib/Analysis/ThreadSafety.cpp
@@ -172,12 +172,9 @@
const Expr* const* FunArgs; // Function arguments
CallingContext* PrevCtx; // The previous context; or 0 if none.
- CallingContext(const NamedDecl *D = 0, const Expr *S = 0,
- unsigned N = 0, const Expr* const *A = 0,
- CallingContext *P = 0)
- : AttrDecl(D), SelfArg(S), SelfArrow(false),
- NumArgs(N), FunArgs(A), PrevCtx(P)
- { }
+ CallingContext(const NamedDecl *D)
+ : AttrDecl(D), SelfArg(0), SelfArrow(false), NumArgs(0), FunArgs(0),
+ PrevCtx(0) {}
};
typedef SmallVector<SExprNode, 4> NodeVector;
@@ -188,44 +185,41 @@
NodeVector NodeVec;
private:
+ unsigned make(ExprOp O, unsigned F = 0, const void *D = 0) {
+ NodeVec.push_back(SExprNode(O, F, D));
+ return NodeVec.size() - 1;
+ }
+
unsigned makeNop() {
- NodeVec.push_back(SExprNode(EOP_Nop, 0, 0));
- return NodeVec.size()-1;
+ return make(EOP_Nop);
}
unsigned makeWildcard() {
- NodeVec.push_back(SExprNode(EOP_Wildcard, 0, 0));
- return NodeVec.size()-1;
+ return make(EOP_Wildcard);
}
unsigned makeUniversal() {
- NodeVec.push_back(SExprNode(EOP_Universal, 0, 0));
- return NodeVec.size()-1;
+ return make(EOP_Universal);
}
unsigned makeNamedVar(const NamedDecl *D) {
- NodeVec.push_back(SExprNode(EOP_NVar, 0, D));
- return NodeVec.size()-1;
+ return make(EOP_NVar, 0, D);
}
unsigned makeLocalVar(const NamedDecl *D) {
- NodeVec.push_back(SExprNode(EOP_LVar, 0, D));
- return NodeVec.size()-1;
+ return make(EOP_LVar, 0, D);
}
unsigned makeThis() {
- NodeVec.push_back(SExprNode(EOP_This, 0, 0));
- return NodeVec.size()-1;
+ return make(EOP_This);
}
unsigned makeDot(const NamedDecl *D, bool Arrow) {
- NodeVec.push_back(SExprNode(EOP_Dot, Arrow ? 1 : 0, D));
- return NodeVec.size()-1;
+ return make(EOP_Dot, Arrow ? 1 : 0, D);
}
unsigned makeCall(unsigned NumArgs, const NamedDecl *D) {
- NodeVec.push_back(SExprNode(EOP_Call, NumArgs, D));
- return NodeVec.size()-1;
+ return make(EOP_Call, NumArgs, D);
}
// Grab the very first declaration of virtual method D
@@ -242,28 +236,23 @@
}
unsigned makeMCall(unsigned NumArgs, const CXXMethodDecl *D) {
- NodeVec.push_back(SExprNode(EOP_MCall, NumArgs, getFirstVirtualDecl(D)));
- return NodeVec.size()-1;
+ return make(EOP_MCall, NumArgs, getFirstVirtualDecl(D));
}
unsigned makeIndex() {
- NodeVec.push_back(SExprNode(EOP_Index, 0, 0));
- return NodeVec.size()-1;
+ return make(EOP_Index);
}
unsigned makeUnary() {
- NodeVec.push_back(SExprNode(EOP_Unary, 0, 0));
- return NodeVec.size()-1;
+ return make(EOP_Unary);
}
unsigned makeBinary() {
- NodeVec.push_back(SExprNode(EOP_Binary, 0, 0));
- return NodeVec.size()-1;
+ return make(EOP_Binary);
}
unsigned makeUnknown(unsigned Arity) {
- NodeVec.push_back(SExprNode(EOP_Unknown, Arity, 0));
- return NodeVec.size()-1;
+ return make(EOP_Unknown, Arity);
}
inline bool isCalleeArrow(const Expr *E) {
@@ -277,7 +266,7 @@
/// ensure that the original expression is a valid mutex expression.
///
/// NDeref returns the number of Derefence and AddressOf operations
- /// preceeding the Expr; this is used to decide whether to pretty-print
+ /// preceding the Expr; this is used to decide whether to pretty-print
/// SExprs with . or ->.
unsigned buildSExpr(const Expr *Exp, CallingContext* CallCtx,
int* NDeref = 0) {
@@ -575,15 +564,15 @@
/// Issue a warning about an invalid lock expression
static void warnInvalidLock(ThreadSafetyHandler &Handler,
- const Expr *MutexExp,
- const Expr *DeclExp, const NamedDecl* D) {
+ const Expr *MutexExp, const Expr *DeclExp,
+ const NamedDecl *D, StringRef Kind) {
SourceLocation Loc;
if (DeclExp)
Loc = DeclExp->getExprLoc();
// FIXME: add a note about the attribute location in MutexExp or D
if (Loc.isValid())
- Handler.handleInvalidLockExp(Loc);
+ Handler.handleInvalidLockExp(Kind, Loc);
}
bool operator==(const SExpr &other) const {
@@ -716,27 +705,16 @@
}
};
-
-
/// \brief A short list of SExprs
class MutexIDList : public SmallVector<SExpr, 3> {
public:
- /// \brief Return true if the list contains the specified SExpr
- /// Performs a linear search, because these lists are almost always very small.
- bool contains(const SExpr& M) {
- for (iterator I=begin(),E=end(); I != E; ++I)
- if ((*I) == M) return true;
- return false;
- }
-
- /// \brief Push M onto list, bud discard duplicates
+ /// \brief Push M onto list, but discard duplicates.
void push_back_nodup(const SExpr& M) {
- if (!contains(M)) push_back(M);
+ if (end() == std::find(begin(), end(), M))
+ push_back(M);
}
};
-
-
/// \brief This is a helper class that stores info about the most recent
/// accquire of a Lock.
///
@@ -1450,9 +1428,10 @@
public:
ThreadSafetyAnalyzer(ThreadSafetyHandler &H) : Handler(H) {}
- void addLock(FactSet &FSet, const SExpr &Mutex, const LockData &LDat);
- void removeLock(FactSet &FSet, const SExpr &Mutex,
- SourceLocation UnlockLoc, bool FullyRemove=false);
+ void addLock(FactSet &FSet, const SExpr &Mutex, const LockData &LDat,
+ StringRef DiagKind);
+ void removeLock(FactSet &FSet, const SExpr &Mutex, SourceLocation UnlockLoc,
+ bool FullyRemove, LockKind Kind, StringRef DiagKind);
template <typename AttrType>
void getMutexIDs(MutexIDList &Mtxs, AttrType *Attr, Expr *Exp,
@@ -1485,12 +1464,89 @@
void runAnalysis(AnalysisDeclContext &AC);
};
+/// \brief Gets the value decl pointer from DeclRefExprs or MemberExprs.
+static const ValueDecl *getValueDecl(const Expr *Exp) {
+ if (const auto *CE = dyn_cast<ImplicitCastExpr>(Exp))
+ return getValueDecl(CE->getSubExpr());
+
+ if (const auto *DR = dyn_cast<DeclRefExpr>(Exp))
+ return DR->getDecl();
+
+ if (const auto *ME = dyn_cast<MemberExpr>(Exp))
+ return ME->getMemberDecl();
+
+ return nullptr;
+}
+
+template <typename Ty>
+class has_arg_iterator {
+ typedef char yes[1];
+ typedef char no[2];
+
+ template <typename Inner>
+ static yes& test(Inner *I, decltype(I->args_begin()) * = nullptr);
+
+ template <typename>
+ static no& test(...);
+
+public:
+ static const bool value = sizeof(test<Ty>(nullptr)) == sizeof(yes);
+};
+
+static StringRef ClassifyDiagnostic(const CapabilityAttr *A) {
+ return A->getName();
+}
+
+static StringRef ClassifyDiagnostic(QualType VDT) {
+ // We need to look at the declaration of the type of the value to determine
+ // which it is. The type should either be a record or a typedef, or a pointer
+ // or reference thereof.
+ if (const auto *RT = VDT->getAs<RecordType>()) {
+ if (const auto *RD = RT->getDecl())
+ if (const auto *CA = RD->getAttr<CapabilityAttr>())
+ return ClassifyDiagnostic(CA);
+ } else if (const auto *TT = VDT->getAs<TypedefType>()) {
+ if (const auto *TD = TT->getDecl())
+ if (const auto *CA = TD->getAttr<CapabilityAttr>())
+ return ClassifyDiagnostic(CA);
+ } else if (VDT->isPointerType() || VDT->isReferenceType())
+ return ClassifyDiagnostic(VDT->getPointeeType());
+
+ return "mutex";
+}
+
+static StringRef ClassifyDiagnostic(const ValueDecl *VD) {
+ assert(VD && "No ValueDecl passed");
+
+ // The ValueDecl is the declaration of a mutex or role (hopefully).
+ return ClassifyDiagnostic(VD->getType());
+}
+
+template <typename AttrTy>
+static typename std::enable_if<!has_arg_iterator<AttrTy>::value,
+ StringRef>::type
+ClassifyDiagnostic(const AttrTy *A) {
+ if (const ValueDecl *VD = getValueDecl(A->getArg()))
+ return ClassifyDiagnostic(VD);
+ return "mutex";
+}
+
+template <typename AttrTy>
+static typename std::enable_if<has_arg_iterator<AttrTy>::value,
+ StringRef>::type
+ClassifyDiagnostic(const AttrTy *A) {
+ for (auto I = A->args_begin(), E = A->args_end(); I != E; ++I) {
+ if (const ValueDecl *VD = getValueDecl(*I))
+ return ClassifyDiagnostic(VD);
+ }
+ return "mutex";
+}
/// \brief Add a new lock to the lockset, warning if the lock is already there.
/// \param Mutex -- the Mutex expression for the lock
/// \param LDat -- the LockData for the lock
void ThreadSafetyAnalyzer::addLock(FactSet &FSet, const SExpr &Mutex,
- const LockData &LDat) {
+ const LockData &LDat, StringRef DiagKind) {
// FIXME: deal with acquired before/after annotations.
// FIXME: Don't always warn when we have support for reentrant locks.
if (Mutex.shouldIgnore())
@@ -1498,7 +1554,7 @@
if (FSet.findLock(FactMan, Mutex)) {
if (!LDat.Asserted)
- Handler.handleDoubleLock(Mutex.toString(), LDat.AcquireLoc);
+ Handler.handleDoubleLock(DiagKind, Mutex.toString(), LDat.AcquireLoc);
} else {
FSet.addLock(FactMan, Mutex, LDat);
}
@@ -1508,16 +1564,24 @@
/// \brief Remove a lock from the lockset, warning if the lock is not there.
/// \param Mutex The lock expression corresponding to the lock to be removed
/// \param UnlockLoc The source location of the unlock (only used in error msg)
-void ThreadSafetyAnalyzer::removeLock(FactSet &FSet,
- const SExpr &Mutex,
+void ThreadSafetyAnalyzer::removeLock(FactSet &FSet, const SExpr &Mutex,
SourceLocation UnlockLoc,
- bool FullyRemove) {
+ bool FullyRemove, LockKind ReceivedKind,
+ StringRef DiagKind) {
if (Mutex.shouldIgnore())
return;
const LockData *LDat = FSet.findLock(FactMan, Mutex);
if (!LDat) {
- Handler.handleUnmatchedUnlock(Mutex.toString(), UnlockLoc);
+ Handler.handleUnmatchedUnlock(DiagKind, Mutex.toString(), UnlockLoc);
+ return;
+ }
+
+ // Generic lock removal doesn't care about lock kind mismatches, but
+ // otherwise diagnose when the lock kinds are mismatched.
+ if (ReceivedKind != LK_Generic && LDat->LKind != ReceivedKind) {
+ Handler.handleIncorrectUnlockKind(DiagKind, Mutex.toString(), LDat->LKind,
+ ReceivedKind, UnlockLoc);
return;
}
@@ -1532,8 +1596,8 @@
// We're releasing the underlying mutex, but not destroying the
// managing object. Warn on dual release.
if (!FSet.findLock(FactMan, LDat->UnderlyingMutex)) {
- Handler.handleUnmatchedUnlock(LDat->UnderlyingMutex.toString(),
- UnlockLoc);
+ Handler.handleUnmatchedUnlock(
+ DiagKind, LDat->UnderlyingMutex.toString(), UnlockLoc);
}
FSet.removeLock(FactMan, LDat->UnderlyingMutex);
return;
@@ -1555,7 +1619,7 @@
// The mutex held is the "this" object.
SExpr Mu(0, Exp, D, SelfDecl);
if (!Mu.isValid())
- SExpr::warnInvalidLock(Handler, 0, Exp, D);
+ SExpr::warnInvalidLock(Handler, 0, Exp, D, ClassifyDiagnostic(Attr));
else
Mtxs.push_back_nodup(Mu);
return;
@@ -1564,7 +1628,7 @@
for (iterator_type I=Attr->args_begin(), E=Attr->args_end(); I != E; ++I) {
SExpr Mu(*I, Exp, D, SelfDecl);
if (!Mu.isValid())
- SExpr::warnInvalidLock(Handler, *I, Exp, D);
+ SExpr::warnInvalidLock(Handler, *I, Exp, D, ClassifyDiagnostic(Attr));
else
Mtxs.push_back_nodup(Mu);
}
@@ -1697,6 +1761,7 @@
bool Negate = false;
const CFGBlockInfo *PredBlockInfo = &BlockInfo[PredBlock->getBlockID()];
const LocalVarContext &LVarCtx = PredBlockInfo->ExitContext;
+ StringRef CapDiagKind = "mutex";
CallExpr *Exp =
const_cast<CallExpr*>(getTrylockCallExpr(Cond, LVarCtx, Negate));
@@ -1720,6 +1785,7 @@
cast<ExclusiveTrylockFunctionAttr>(Attr);
getMutexIDs(ExclusiveLocksToAdd, A, Exp, FunDecl,
PredBlock, CurrBlock, A->getSuccessValue(), Negate);
+ CapDiagKind = ClassifyDiagnostic(A);
break;
}
case attr::SharedTrylockFunction: {
@@ -1727,6 +1793,7 @@
cast<SharedTrylockFunctionAttr>(Attr);
getMutexIDs(SharedLocksToAdd, A, Exp, FunDecl,
PredBlock, CurrBlock, A->getSuccessValue(), Negate);
+ CapDiagKind = ClassifyDiagnostic(A);
break;
}
default:
@@ -1736,17 +1803,13 @@
// Add and remove locks.
SourceLocation Loc = Exp->getExprLoc();
- for (unsigned i=0,n=ExclusiveLocksToAdd.size(); i<n; ++i) {
- addLock(Result, ExclusiveLocksToAdd[i],
- LockData(Loc, LK_Exclusive));
- }
- for (unsigned i=0,n=SharedLocksToAdd.size(); i<n; ++i) {
- addLock(Result, SharedLocksToAdd[i],
- LockData(Loc, LK_Shared));
- }
+ for (const auto &ExclusiveLockToAdd : ExclusiveLocksToAdd)
+ addLock(Result, ExclusiveLockToAdd, LockData(Loc, LK_Exclusive),
+ CapDiagKind);
+ for (const auto &SharedLockToAdd : SharedLocksToAdd)
+ addLock(Result, SharedLockToAdd, LockData(Loc, LK_Shared), CapDiagKind);
}
-
/// \brief We use this class to visit different types of expressions in
/// CFGBlocks, and build up the lockset.
/// An expression may cause us to add or remove locks from the lockset, or else
@@ -1761,11 +1824,12 @@
unsigned CtxIndex;
// Helper functions
- const ValueDecl *getValueDecl(const Expr *Exp);
void warnIfMutexNotHeld(const NamedDecl *D, const Expr *Exp, AccessKind AK,
- Expr *MutexExp, ProtectedOperationKind POK);
- void warnIfMutexHeld(const NamedDecl *D, const Expr *Exp, Expr *MutexExp);
+ Expr *MutexExp, ProtectedOperationKind POK,
+ StringRef DiagKind);
+ void warnIfMutexHeld(const NamedDecl *D, const Expr *Exp, Expr *MutexExp,
+ StringRef DiagKind);
void checkAccess(const Expr *Exp, AccessKind AK);
void checkPtAccess(const Expr *Exp, AccessKind AK);
@@ -1789,31 +1853,17 @@
void VisitDeclStmt(DeclStmt *S);
};
-
-/// \brief Gets the value decl pointer from DeclRefExprs or MemberExprs
-const ValueDecl *BuildLockset::getValueDecl(const Expr *Exp) {
- if (const ImplicitCastExpr *CE = dyn_cast<ImplicitCastExpr>(Exp))
- return getValueDecl(CE->getSubExpr());
-
- if (const DeclRefExpr *DR = dyn_cast<DeclRefExpr>(Exp))
- return DR->getDecl();
-
- if (const MemberExpr *ME = dyn_cast<MemberExpr>(Exp))
- return ME->getMemberDecl();
-
- return 0;
-}
-
/// \brief Warn if the LSet does not contain a lock sufficient to protect access
/// of at least the passed in AccessKind.
void BuildLockset::warnIfMutexNotHeld(const NamedDecl *D, const Expr *Exp,
AccessKind AK, Expr *MutexExp,
- ProtectedOperationKind POK) {
+ ProtectedOperationKind POK,
+ StringRef DiagKind) {
LockKind LK = getLockKindFromAccessKind(AK);
SExpr Mutex(MutexExp, Exp, D);
if (!Mutex.isValid()) {
- SExpr::warnInvalidLock(Analyzer->Handler, MutexExp, Exp, D);
+ SExpr::warnInvalidLock(Analyzer->Handler, MutexExp, Exp, D, DiagKind);
return;
} else if (Mutex.shouldIgnore()) {
return;
@@ -1829,40 +1879,38 @@
LDat = &FEntry->LDat;
std::string PartMatchStr = FEntry->MutID.toString();
StringRef PartMatchName(PartMatchStr);
- Analyzer->Handler.handleMutexNotHeld(D, POK, Mutex.toString(), LK,
- Exp->getExprLoc(), &PartMatchName);
+ Analyzer->Handler.handleMutexNotHeld(DiagKind, D, POK, Mutex.toString(),
+ LK, Exp->getExprLoc(),
+ &PartMatchName);
} else {
// Warn that there's no match at all.
- Analyzer->Handler.handleMutexNotHeld(D, POK, Mutex.toString(), LK,
- Exp->getExprLoc());
+ Analyzer->Handler.handleMutexNotHeld(DiagKind, D, POK, Mutex.toString(),
+ LK, Exp->getExprLoc());
}
NoError = false;
}
// Make sure the mutex we found is the right kind.
if (NoError && LDat && !LDat->isAtLeast(LK))
- Analyzer->Handler.handleMutexNotHeld(D, POK, Mutex.toString(), LK,
+ Analyzer->Handler.handleMutexNotHeld(DiagKind, D, POK, Mutex.toString(), LK,
Exp->getExprLoc());
}
/// \brief Warn if the LSet contains the given lock.
-void BuildLockset::warnIfMutexHeld(const NamedDecl *D, const Expr* Exp,
- Expr *MutexExp) {
+void BuildLockset::warnIfMutexHeld(const NamedDecl *D, const Expr *Exp,
+ Expr *MutexExp,
+ StringRef DiagKind) {
SExpr Mutex(MutexExp, Exp, D);
if (!Mutex.isValid()) {
- SExpr::warnInvalidLock(Analyzer->Handler, MutexExp, Exp, D);
+ SExpr::warnInvalidLock(Analyzer->Handler, MutexExp, Exp, D, DiagKind);
return;
}
LockData* LDat = FSet.findLock(Analyzer->FactMan, Mutex);
- if (LDat) {
- std::string DeclName = D->getNameAsString();
- StringRef DeclNameSR (DeclName);
- Analyzer->Handler.handleFunExcludesLock(DeclNameSR, Mutex.toString(),
- Exp->getExprLoc());
- }
+ if (LDat)
+ Analyzer->Handler.handleFunExcludesLock(
+ DiagKind, D->getNameAsString(), Mutex.toString(), Exp->getExprLoc());
}
-
/// \brief Checks guarded_by and pt_guarded_by attributes.
/// Whenever we identify an access (read or write) to a DeclRefExpr that is
/// marked with guarded_by, we must ensure the appropriate mutexes are held.
@@ -1879,10 +1927,8 @@
}
if (const ArraySubscriptExpr *AE = dyn_cast<ArraySubscriptExpr>(Exp)) {
- if (Analyzer->Handler.issueBetaWarnings()) {
- checkPtAccess(AE->getLHS(), AK);
- return;
- }
+ checkPtAccess(AE->getLHS(), AK);
+ return;
}
if (const MemberExpr *ME = dyn_cast<MemberExpr>(Exp)) {
@@ -1896,55 +1942,48 @@
if (!D || !D->hasAttrs())
return;
- if (D->getAttr<GuardedVarAttr>() && FSet.isEmpty())
- Analyzer->Handler.handleNoMutexHeld(D, POK_VarAccess, AK,
+ if (D->hasAttr<GuardedVarAttr>() && FSet.isEmpty())
+ Analyzer->Handler.handleNoMutexHeld("mutex", D, POK_VarAccess, AK,
Exp->getExprLoc());
- const AttrVec &ArgAttrs = D->getAttrs();
- for (unsigned i = 0, Size = ArgAttrs.size(); i < Size; ++i)
- if (GuardedByAttr *GBAttr = dyn_cast<GuardedByAttr>(ArgAttrs[i]))
- warnIfMutexNotHeld(D, Exp, AK, GBAttr->getArg(), POK_VarAccess);
+ for (const auto *I : D->specific_attrs<GuardedByAttr>())
+ warnIfMutexNotHeld(D, Exp, AK, I->getArg(), POK_VarAccess,
+ ClassifyDiagnostic(I));
}
/// \brief Checks pt_guarded_by and pt_guarded_var attributes.
void BuildLockset::checkPtAccess(const Expr *Exp, AccessKind AK) {
- if (Analyzer->Handler.issueBetaWarnings()) {
- while (true) {
- if (const ParenExpr *PE = dyn_cast<ParenExpr>(Exp)) {
- Exp = PE->getSubExpr();
- continue;
- }
- if (const CastExpr *CE = dyn_cast<CastExpr>(Exp)) {
- if (CE->getCastKind() == CK_ArrayToPointerDecay) {
- // If it's an actual array, and not a pointer, then it's elements
- // are protected by GUARDED_BY, not PT_GUARDED_BY;
- checkAccess(CE->getSubExpr(), AK);
- return;
- }
- Exp = CE->getSubExpr();
- continue;
- }
- break;
+ while (true) {
+ if (const ParenExpr *PE = dyn_cast<ParenExpr>(Exp)) {
+ Exp = PE->getSubExpr();
+ continue;
}
+ if (const CastExpr *CE = dyn_cast<CastExpr>(Exp)) {
+ if (CE->getCastKind() == CK_ArrayToPointerDecay) {
+ // If it's an actual array, and not a pointer, then it's elements
+ // are protected by GUARDED_BY, not PT_GUARDED_BY;
+ checkAccess(CE->getSubExpr(), AK);
+ return;
+ }
+ Exp = CE->getSubExpr();
+ continue;
+ }
+ break;
}
- else
- Exp = Exp->IgnoreParenCasts();
const ValueDecl *D = getValueDecl(Exp);
if (!D || !D->hasAttrs())
return;
- if (D->getAttr<PtGuardedVarAttr>() && FSet.isEmpty())
- Analyzer->Handler.handleNoMutexHeld(D, POK_VarDereference, AK,
+ if (D->hasAttr<PtGuardedVarAttr>() && FSet.isEmpty())
+ Analyzer->Handler.handleNoMutexHeld("mutex", D, POK_VarDereference, AK,
Exp->getExprLoc());
- const AttrVec &ArgAttrs = D->getAttrs();
- for (unsigned i = 0, Size = ArgAttrs.size(); i < Size; ++i)
- if (PtGuardedByAttr *GBAttr = dyn_cast<PtGuardedByAttr>(ArgAttrs[i]))
- warnIfMutexNotHeld(D, Exp, AK, GBAttr->getArg(), POK_VarDereference);
+ for (auto const *I : D->specific_attrs<PtGuardedByAttr>())
+ warnIfMutexNotHeld(D, Exp, AK, I->getArg(), POK_VarDereference,
+ ClassifyDiagnostic(I));
}
-
/// \brief Process a function call, method call, constructor call,
/// or destructor call. This involves looking at the attributes on the
/// corresponding function/method/constructor/destructor, issuing warnings,
@@ -1958,26 +1997,22 @@
void BuildLockset::handleCall(Expr *Exp, const NamedDecl *D, VarDecl *VD) {
SourceLocation Loc = Exp->getExprLoc();
const AttrVec &ArgAttrs = D->getAttrs();
- MutexIDList ExclusiveLocksToAdd;
- MutexIDList SharedLocksToAdd;
- MutexIDList LocksToRemove;
+ MutexIDList ExclusiveLocksToAdd, SharedLocksToAdd;
+ MutexIDList ExclusiveLocksToRemove, SharedLocksToRemove, GenericLocksToRemove;
+ StringRef CapDiagKind = "mutex";
for(unsigned i = 0; i < ArgAttrs.size(); ++i) {
Attr *At = const_cast<Attr*>(ArgAttrs[i]);
switch (At->getKind()) {
- // When we encounter an exclusive lock function, we need to add the lock
- // to our lockset with kind exclusive.
- case attr::ExclusiveLockFunction: {
- ExclusiveLockFunctionAttr *A = cast<ExclusiveLockFunctionAttr>(At);
- Analyzer->getMutexIDs(ExclusiveLocksToAdd, A, Exp, D, VD);
- break;
- }
+ // When we encounter a lock function, we need to add the lock to our
+ // lockset.
+ case attr::AcquireCapability: {
+ auto *A = cast<AcquireCapabilityAttr>(At);
+ Analyzer->getMutexIDs(A->isShared() ? SharedLocksToAdd
+ : ExclusiveLocksToAdd,
+ A, Exp, D, VD);
- // When we encounter a shared lock function, we need to add the lock
- // to our lockset with kind shared.
- case attr::SharedLockFunction: {
- SharedLockFunctionAttr *A = cast<SharedLockFunctionAttr>(At);
- Analyzer->getMutexIDs(SharedLocksToAdd, A, Exp, D, VD);
+ CapDiagKind = ClassifyDiagnostic(A);
break;
}
@@ -1989,10 +2024,10 @@
MutexIDList AssertLocks;
Analyzer->getMutexIDs(AssertLocks, A, Exp, D, VD);
- for (unsigned i=0,n=AssertLocks.size(); i<n; ++i) {
- Analyzer->addLock(FSet, AssertLocks[i],
- LockData(Loc, LK_Exclusive, false, true));
- }
+ for (const auto &AssertLock : AssertLocks)
+ Analyzer->addLock(FSet, AssertLock,
+ LockData(Loc, LK_Exclusive, false, true),
+ ClassifyDiagnostic(A));
break;
}
case attr::AssertSharedLock: {
@@ -2000,36 +2035,35 @@
MutexIDList AssertLocks;
Analyzer->getMutexIDs(AssertLocks, A, Exp, D, VD);
- for (unsigned i=0,n=AssertLocks.size(); i<n; ++i) {
- Analyzer->addLock(FSet, AssertLocks[i],
- LockData(Loc, LK_Shared, false, true));
- }
+ for (const auto &AssertLock : AssertLocks)
+ Analyzer->addLock(FSet, AssertLock,
+ LockData(Loc, LK_Shared, false, true),
+ ClassifyDiagnostic(A));
break;
}
// When we encounter an unlock function, we need to remove unlocked
// mutexes from the lockset, and flag a warning if they are not there.
- case attr::UnlockFunction: {
- UnlockFunctionAttr *A = cast<UnlockFunctionAttr>(At);
- Analyzer->getMutexIDs(LocksToRemove, A, Exp, D, VD);
+ case attr::ReleaseCapability: {
+ auto *A = cast<ReleaseCapabilityAttr>(At);
+ if (A->isGeneric())
+ Analyzer->getMutexIDs(GenericLocksToRemove, A, Exp, D, VD);
+ else if (A->isShared())
+ Analyzer->getMutexIDs(SharedLocksToRemove, A, Exp, D, VD);
+ else
+ Analyzer->getMutexIDs(ExclusiveLocksToRemove, A, Exp, D, VD);
+
+ CapDiagKind = ClassifyDiagnostic(A);
break;
}
- case attr::ExclusiveLocksRequired: {
- ExclusiveLocksRequiredAttr *A = cast<ExclusiveLocksRequiredAttr>(At);
+ case attr::RequiresCapability: {
+ RequiresCapabilityAttr *A = cast<RequiresCapabilityAttr>(At);
- for (ExclusiveLocksRequiredAttr::args_iterator
- I = A->args_begin(), E = A->args_end(); I != E; ++I)
- warnIfMutexNotHeld(D, Exp, AK_Written, *I, POK_FunctionCall);
- break;
- }
-
- case attr::SharedLocksRequired: {
- SharedLocksRequiredAttr *A = cast<SharedLocksRequiredAttr>(At);
-
- for (SharedLocksRequiredAttr::args_iterator I = A->args_begin(),
+ for (RequiresCapabilityAttr::args_iterator I = A->args_begin(),
E = A->args_end(); I != E; ++I)
- warnIfMutexNotHeld(D, Exp, AK_Read, *I, POK_FunctionCall);
+ warnIfMutexNotHeld(D, Exp, A->isShared() ? AK_Read : AK_Written, *I,
+ POK_FunctionCall, ClassifyDiagnostic(A));
break;
}
@@ -2038,12 +2072,12 @@
for (LocksExcludedAttr::args_iterator I = A->args_begin(),
E = A->args_end(); I != E; ++I) {
- warnIfMutexHeld(D, Exp, *I);
+ warnIfMutexHeld(D, Exp, *I, ClassifyDiagnostic(A));
}
break;
}
- // Ignore other (non thread-safety) attributes
+ // Ignore attributes unrelated to thread-safety
default:
break;
}
@@ -2054,20 +2088,18 @@
if (VD) {
if (const CXXConstructorDecl *CD = dyn_cast<const CXXConstructorDecl>(D)) {
const CXXRecordDecl* PD = CD->getParent();
- if (PD && PD->getAttr<ScopedLockableAttr>())
+ if (PD && PD->hasAttr<ScopedLockableAttr>())
isScopedVar = true;
}
}
// Add locks.
- for (unsigned i=0,n=ExclusiveLocksToAdd.size(); i<n; ++i) {
- Analyzer->addLock(FSet, ExclusiveLocksToAdd[i],
- LockData(Loc, LK_Exclusive, isScopedVar));
- }
- for (unsigned i=0,n=SharedLocksToAdd.size(); i<n; ++i) {
- Analyzer->addLock(FSet, SharedLocksToAdd[i],
- LockData(Loc, LK_Shared, isScopedVar));
- }
+ for (const auto &M : ExclusiveLocksToAdd)
+ Analyzer->addLock(FSet, M, LockData(Loc, LK_Exclusive, isScopedVar),
+ CapDiagKind);
+ for (const auto &M : SharedLocksToAdd)
+ Analyzer->addLock(FSet, M, LockData(Loc, LK_Shared, isScopedVar),
+ CapDiagKind);
// Add the managing object as a dummy mutex, mapped to the underlying mutex.
// FIXME -- this doesn't work if we acquire multiple locks.
@@ -2076,22 +2108,23 @@
DeclRefExpr DRE(VD, false, VD->getType(), VK_LValue, VD->getLocation());
SExpr SMutex(&DRE, 0, 0);
- for (unsigned i=0,n=ExclusiveLocksToAdd.size(); i<n; ++i) {
- Analyzer->addLock(FSet, SMutex, LockData(MLoc, LK_Exclusive,
- ExclusiveLocksToAdd[i]));
- }
- for (unsigned i=0,n=SharedLocksToAdd.size(); i<n; ++i) {
- Analyzer->addLock(FSet, SMutex, LockData(MLoc, LK_Shared,
- SharedLocksToAdd[i]));
- }
+ for (const auto &M : ExclusiveLocksToAdd)
+ Analyzer->addLock(FSet, SMutex, LockData(MLoc, LK_Exclusive, M),
+ CapDiagKind);
+ for (const auto &M : SharedLocksToAdd)
+ Analyzer->addLock(FSet, SMutex, LockData(MLoc, LK_Shared, M),
+ CapDiagKind);
}
// Remove locks.
// FIXME -- should only fully remove if the attribute refers to 'this'.
bool Dtor = isa<CXXDestructorDecl>(D);
- for (unsigned i=0,n=LocksToRemove.size(); i<n; ++i) {
- Analyzer->removeLock(FSet, LocksToRemove[i], Loc, Dtor);
- }
+ for (const auto &M : ExclusiveLocksToRemove)
+ Analyzer->removeLock(FSet, M, Loc, Dtor, LK_Exclusive, CapDiagKind);
+ for (const auto &M : SharedLocksToRemove)
+ Analyzer->removeLock(FSet, M, Loc, Dtor, LK_Shared, CapDiagKind);
+ for (const auto &M : GenericLocksToRemove)
+ Analyzer->removeLock(FSet, M, Loc, Dtor, LK_Generic, CapDiagKind);
}
@@ -2168,11 +2201,9 @@
case OO_Star:
case OO_Arrow:
case OO_Subscript: {
- if (Analyzer->Handler.issueBetaWarnings()) {
- const Expr *Obj = OE->getArg(0);
- checkAccess(Obj, AK_Read);
- checkPtAccess(Obj, AK_Read);
- }
+ const Expr *Obj = OE->getArg(0);
+ checkAccess(Obj, AK_Read);
+ checkPtAccess(Obj, AK_Read);
break;
}
default: {
@@ -2254,9 +2285,8 @@
if (I1 != FSet1.end()) {
const LockData* LDat1 = &FactMan[*I1].LDat;
if (LDat1->LKind != LDat2.LKind) {
- Handler.handleExclusiveAndShared(FSet2Mutex.toString(),
- LDat2.AcquireLoc,
- LDat1->AcquireLoc);
+ Handler.handleExclusiveAndShared("mutex", FSet2Mutex.toString(),
+ LDat2.AcquireLoc, LDat1->AcquireLoc);
if (Modify && LDat1->LKind != LK_Exclusive) {
// Take the exclusive lock, which is the one in FSet2.
*I1 = *I;
@@ -2272,15 +2302,14 @@
// If this is a scoped lock that manages another mutex, and if the
// underlying mutex is still held, then warn about the underlying
// mutex.
- Handler.handleMutexHeldEndOfScope(LDat2.UnderlyingMutex.toString(),
- LDat2.AcquireLoc,
- JoinLoc, LEK1);
+ Handler.handleMutexHeldEndOfScope("mutex",
+ LDat2.UnderlyingMutex.toString(),
+ LDat2.AcquireLoc, JoinLoc, LEK1);
}
}
else if (!LDat2.Managed && !FSet2Mutex.isUniversal() && !LDat2.Asserted)
- Handler.handleMutexHeldEndOfScope(FSet2Mutex.toString(),
- LDat2.AcquireLoc,
- JoinLoc, LEK1);
+ Handler.handleMutexHeldEndOfScope("mutex", FSet2Mutex.toString(),
+ LDat2.AcquireLoc, JoinLoc, LEK1);
}
}
@@ -2296,15 +2325,14 @@
// If this is a scoped lock that manages another mutex, and if the
// underlying mutex is still held, then warn about the underlying
// mutex.
- Handler.handleMutexHeldEndOfScope(LDat1.UnderlyingMutex.toString(),
- LDat1.AcquireLoc,
- JoinLoc, LEK1);
+ Handler.handleMutexHeldEndOfScope("mutex",
+ LDat1.UnderlyingMutex.toString(),
+ LDat1.AcquireLoc, JoinLoc, LEK1);
}
}
else if (!LDat1.Managed && !FSet1Mutex.isUniversal() && !LDat1.Asserted)
- Handler.handleMutexHeldEndOfScope(FSet1Mutex.toString(),
- LDat1.AcquireLoc,
- JoinLoc, LEK2);
+ Handler.handleMutexHeldEndOfScope("mutex", FSet1Mutex.toString(),
+ LDat1.AcquireLoc, JoinLoc, LEK2);
if (Modify)
FSet1.removeLock(FactMan, FSet1Mutex);
}
@@ -2342,7 +2370,7 @@
if (!D)
return; // Ignore anonymous functions for now.
- if (D->getAttr<NoThreadSafetyAnalysisAttr>())
+ if (D->hasAttr<NoThreadSafetyAnalysisAttr>())
return;
// FIXME: Do something a bit more intelligent inside constructor and
// destructor code. Constructors and destructors must assume unique access
@@ -2385,18 +2413,17 @@
MutexIDList ExclusiveLocksToAdd;
MutexIDList SharedLocksToAdd;
+ StringRef CapDiagKind = "mutex";
SourceLocation Loc = D->getLocation();
for (unsigned i = 0; i < ArgAttrs.size(); ++i) {
Attr *Attr = ArgAttrs[i];
Loc = Attr->getLocation();
- if (ExclusiveLocksRequiredAttr *A
- = dyn_cast<ExclusiveLocksRequiredAttr>(Attr)) {
- getMutexIDs(ExclusiveLocksToAdd, A, (Expr*) 0, D);
- } else if (SharedLocksRequiredAttr *A
- = dyn_cast<SharedLocksRequiredAttr>(Attr)) {
- getMutexIDs(SharedLocksToAdd, A, (Expr*) 0, D);
- } else if (UnlockFunctionAttr *A = dyn_cast<UnlockFunctionAttr>(Attr)) {
+ if (RequiresCapabilityAttr *A = dyn_cast<RequiresCapabilityAttr>(Attr)) {
+ getMutexIDs(A->isShared() ? SharedLocksToAdd : ExclusiveLocksToAdd, A,
+ 0, D);
+ CapDiagKind = ClassifyDiagnostic(A);
+ } else if (auto *A = dyn_cast<ReleaseCapabilityAttr>(Attr)) {
// UNLOCK_FUNCTION() is used to hide the underlying lock implementation.
// We must ignore such methods.
if (A->args_size() == 0)
@@ -2404,16 +2431,14 @@
// FIXME -- deal with exclusive vs. shared unlock functions?
getMutexIDs(ExclusiveLocksToAdd, A, (Expr*) 0, D);
getMutexIDs(LocksReleased, A, (Expr*) 0, D);
- } else if (ExclusiveLockFunctionAttr *A
- = dyn_cast<ExclusiveLockFunctionAttr>(Attr)) {
+ CapDiagKind = ClassifyDiagnostic(A);
+ } else if (auto *A = dyn_cast<AcquireCapabilityAttr>(Attr)) {
if (A->args_size() == 0)
return;
- getMutexIDs(ExclusiveLocksAcquired, A, (Expr*) 0, D);
- } else if (SharedLockFunctionAttr *A
- = dyn_cast<SharedLockFunctionAttr>(Attr)) {
- if (A->args_size() == 0)
- return;
- getMutexIDs(SharedLocksAcquired, A, (Expr*) 0, D);
+ getMutexIDs(A->isShared() ? SharedLocksAcquired
+ : ExclusiveLocksAcquired,
+ A, nullptr, D);
+ CapDiagKind = ClassifyDiagnostic(A);
} else if (isa<ExclusiveTrylockFunctionAttr>(Attr)) {
// Don't try to check trylock functions for now
return;
@@ -2424,14 +2449,12 @@
}
// FIXME -- Loc can be wrong here.
- for (unsigned i=0,n=ExclusiveLocksToAdd.size(); i<n; ++i) {
- addLock(InitialLockset, ExclusiveLocksToAdd[i],
- LockData(Loc, LK_Exclusive));
- }
- for (unsigned i=0,n=SharedLocksToAdd.size(); i<n; ++i) {
- addLock(InitialLockset, SharedLocksToAdd[i],
- LockData(Loc, LK_Shared));
- }
+ for (const auto &ExclusiveLockToAdd : ExclusiveLocksToAdd)
+ addLock(InitialLockset, ExclusiveLockToAdd, LockData(Loc, LK_Exclusive),
+ CapDiagKind);
+ for (const auto &SharedLockToAdd : SharedLocksToAdd)
+ addLock(InitialLockset, SharedLockToAdd, LockData(Loc, LK_Shared),
+ CapDiagKind);
}
for (PostOrderCFGView::iterator I = SortedGraph->begin(),
diff --git a/lib/Analysis/UninitializedValues.cpp b/lib/Analysis/UninitializedValues.cpp
index 332c02c..29c17c3 100644
--- a/lib/Analysis/UninitializedValues.cpp
+++ b/lib/Analysis/UninitializedValues.cpp
@@ -373,9 +373,8 @@
}
void ClassifyRefs::VisitDeclStmt(DeclStmt *DS) {
- for (DeclStmt::decl_iterator DI = DS->decl_begin(), DE = DS->decl_end();
- DI != DE; ++DI) {
- VarDecl *VD = dyn_cast<VarDecl>(*DI);
+ for (auto *DI : DS->decls()) {
+ VarDecl *VD = dyn_cast<VarDecl>(DI);
if (VD && isTrackedVar(VD))
if (const DeclRefExpr *DRE = getSelfInitExpr(VD))
Classification[DRE] = SelfInit;
@@ -535,6 +534,9 @@
for (CFGBlock::const_pred_iterator I = B->pred_begin(), E = B->pred_end();
I != E; ++I) {
const CFGBlock *Pred = *I;
+ if (!Pred)
+ continue;
+
Value AtPredExit = vals.getValue(Pred, B, vd);
if (AtPredExit == Initialized)
// This block initializes the variable.
@@ -630,12 +632,11 @@
void TransferFunctions::VisitBlockExpr(BlockExpr *be) {
const BlockDecl *bd = be->getBlockDecl();
- for (BlockDecl::capture_const_iterator i = bd->capture_begin(),
- e = bd->capture_end() ; i != e; ++i) {
- const VarDecl *vd = i->getVariable();
+ for (const auto &I : bd->captures()) {
+ const VarDecl *vd = I.getVariable();
if (!isTrackedVar(vd))
continue;
- if (i->isByRef()) {
+ if (I.isByRef()) {
vals[vd] = Initialized;
continue;
}
@@ -691,9 +692,8 @@
}
void TransferFunctions::VisitDeclStmt(DeclStmt *DS) {
- for (DeclStmt::decl_iterator DI = DS->decl_begin(), DE = DS->decl_end();
- DI != DE; ++DI) {
- VarDecl *VD = dyn_cast<VarDecl>(*DI);
+ for (auto *DI : DS->decls()) {
+ VarDecl *VD = dyn_cast<VarDecl>(DI);
if (VD && isTrackedVar(VD)) {
if (getSelfInitExpr(VD)) {
// If the initializer consists solely of a reference to itself, we
@@ -751,6 +751,8 @@
for (CFGBlock::const_pred_iterator I = block->pred_begin(),
E = block->pred_end(); I != E; ++I) {
const CFGBlock *pred = *I;
+ if (!pred)
+ continue;
if (wasAnalyzed[pred->getBlockID()]) {
vals.mergeIntoScratch(vals.getValueVector(pred), isFirst);
isFirst = false;
@@ -787,8 +789,8 @@
/// The current block to scribble use information.
unsigned currentBlock;
- virtual void handleUseOfUninitVariable(const VarDecl *vd,
- const UninitUse &use) {
+ void handleUseOfUninitVariable(const VarDecl *vd,
+ const UninitUse &use) override {
hadUse[currentBlock] = true;
hadAnyUse = true;
}
@@ -796,7 +798,7 @@
/// Called when the uninitialized variable analysis detects the
/// idiom 'int x = x'. All other uses of 'x' within the initializer
/// are handled by handleUseOfUninitVariable.
- virtual void handleSelfInit(const VarDecl *vd) {
+ void handleSelfInit(const VarDecl *vd) override {
hadUse[currentBlock] = true;
hadAnyUse = true;
}