summaryrefslogtreecommitdiffstats
path: root/lib/Analysis
diff options
context:
space:
mode:
Diffstat (limited to 'lib/Analysis')
-rw-r--r--lib/Analysis/AnalysisDeclContext.cpp68
-rw-r--r--lib/Analysis/BodyFarm.cpp18
-rw-r--r--lib/Analysis/BodyFarm.h5
-rw-r--r--lib/Analysis/CFG.cpp166
-rw-r--r--lib/Analysis/CFGStmtMap.cpp2
-rw-r--r--lib/Analysis/CallGraph.cpp112
-rw-r--r--lib/Analysis/CocoaConventions.cpp8
-rw-r--r--lib/Analysis/FormatString.cpp11
-rw-r--r--lib/Analysis/FormatStringParsing.h2
-rw-r--r--lib/Analysis/LiveVariables.cpp38
-rw-r--r--lib/Analysis/PrintfFormatString.cpp34
-rw-r--r--lib/Analysis/ReachableCode.cpp14
-rw-r--r--lib/Analysis/ScanfFormatString.cpp4
-rw-r--r--lib/Analysis/ThreadSafety.cpp290
-rw-r--r--lib/Analysis/UninitializedValues.cpp160
15 files changed, 634 insertions, 298 deletions
diff --git a/lib/Analysis/AnalysisDeclContext.cpp b/lib/Analysis/AnalysisDeclContext.cpp
index e7df0a8..5ff7842 100644
--- a/lib/Analysis/AnalysisDeclContext.cpp
+++ b/lib/Analysis/AnalysisDeclContext.cpp
@@ -12,24 +12,24 @@
//
//===----------------------------------------------------------------------===//
+#include "clang/Analysis/AnalysisContext.h"
+#include "BodyFarm.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/Decl.h"
#include "clang/AST/DeclObjC.h"
#include "clang/AST/DeclTemplate.h"
#include "clang/AST/ParentMap.h"
#include "clang/AST/StmtVisitor.h"
+#include "clang/Analysis/Analyses/CFGReachabilityAnalysis.h"
#include "clang/Analysis/Analyses/LiveVariables.h"
#include "clang/Analysis/Analyses/PseudoConstantAnalysis.h"
-#include "clang/Analysis/Analyses/CFGReachabilityAnalysis.h"
-#include "clang/Analysis/AnalysisContext.h"
#include "clang/Analysis/CFG.h"
#include "clang/Analysis/CFGStmtMap.h"
#include "clang/Analysis/Support/BumpVector.h"
-#include "llvm/Support/SaveAndRestore.h"
#include "llvm/ADT/SmallPtrSet.h"
#include "llvm/Support/ErrorHandling.h"
-
-#include "BodyFarm.h"
+#include "llvm/Support/raw_ostream.h"
+#include "llvm/Support/SaveAndRestore.h"
using namespace clang;
@@ -67,13 +67,15 @@ AnalysisDeclContextManager::AnalysisDeclContextManager(bool useUnoptimizedCFG,
bool addImplicitDtors,
bool addInitializers,
bool addTemporaryDtors,
- bool synthesizeBodies)
+ bool synthesizeBodies,
+ bool addStaticInitBranch)
: SynthesizeBodies(synthesizeBodies)
{
cfgBuildOptions.PruneTriviallyFalseEdges = !useUnoptimizedCFG;
cfgBuildOptions.AddImplicitDtors = addImplicitDtors;
cfgBuildOptions.AddInitializers = addInitializers;
cfgBuildOptions.AddTemporaryDtors = addTemporaryDtors;
+ cfgBuildOptions.AddStaticInitBranches = addStaticInitBranch;
}
void AnalysisDeclContextManager::clear() {
@@ -87,11 +89,14 @@ static BodyFarm &getBodyFarm(ASTContext &C) {
return *BF;
}
-Stmt *AnalysisDeclContext::getBody() const {
+Stmt *AnalysisDeclContext::getBody(bool &IsAutosynthesized) const {
+ IsAutosynthesized = false;
if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(D)) {
Stmt *Body = FD->getBody();
- if (!Body && Manager && Manager->synthesizeBodies())
+ if (!Body && Manager && Manager->synthesizeBodies()) {
+ IsAutosynthesized = true;
return getBodyFarm(getASTContext()).getBody(FD);
+ }
return Body;
}
else if (const ObjCMethodDecl *MD = dyn_cast<ObjCMethodDecl>(D))
@@ -105,6 +110,17 @@ Stmt *AnalysisDeclContext::getBody() const {
llvm_unreachable("unknown code decl");
}
+Stmt *AnalysisDeclContext::getBody() const {
+ bool Tmp;
+ return getBody(Tmp);
+}
+
+bool AnalysisDeclContext::isBodyAutosynthesized() const {
+ bool Tmp;
+ getBody(Tmp);
+ return Tmp;
+}
+
const ImplicitParamDecl *AnalysisDeclContext::getSelfDecl() const {
if (const ObjCMethodDecl *MD = dyn_cast<ObjCMethodDecl>(D))
return MD->getSelfDecl();
@@ -371,6 +387,31 @@ bool LocationContext::isParentOf(const LocationContext *LC) const {
return false;
}
+void LocationContext::dumpStack() const {
+ ASTContext &Ctx = getAnalysisDeclContext()->getASTContext();
+ PrintingPolicy PP(Ctx.getLangOpts());
+ PP.TerseOutput = 1;
+
+ unsigned Frame = 0;
+ for (const LocationContext *LCtx = this; LCtx; LCtx = LCtx->getParent()) {
+ switch (LCtx->getKind()) {
+ case StackFrame:
+ llvm::errs() << '#' << Frame++ << ' ';
+ cast<StackFrameContext>(LCtx)->getDecl()->print(llvm::errs(), PP);
+ llvm::errs() << '\n';
+ break;
+ case Scope:
+ llvm::errs() << " (scope)\n";
+ break;
+ case Block:
+ llvm::errs() << " (block context: "
+ << cast<BlockInvocationContext>(LCtx)->getContextData()
+ << ")\n";
+ break;
+ }
+ }
+}
+
//===----------------------------------------------------------------------===//
// Lazily generated map to query the external variables referenced by a Block.
//===----------------------------------------------------------------------===//
@@ -403,9 +444,6 @@ public:
if (!VD->hasLocalStorage()) {
if (Visited.insert(VD))
BEVals.push_back(VD, BC);
- } else if (DR->refersToEnclosingLocal()) {
- if (Visited.insert(VD) && IsTrackedDecl(VD))
- BEVals.push_back(VD, BC);
}
}
}
@@ -440,7 +478,13 @@ static DeclVec* LazyInitializeReferencedDecls(const BlockDecl *BD,
DeclVec *BV = (DeclVec*) A.Allocate<DeclVec>();
new (BV) DeclVec(BC, 10);
- // Find the referenced variables.
+ // 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);
+ }
+
+ // Find the referenced global/static variables.
FindBlockDeclRefExprsVals F(*BV, BC);
F.Visit(BD->getBody());
diff --git a/lib/Analysis/BodyFarm.cpp b/lib/Analysis/BodyFarm.cpp
index 794ff9c..dda26bf 100644
--- a/lib/Analysis/BodyFarm.cpp
+++ b/lib/Analysis/BodyFarm.cpp
@@ -12,12 +12,12 @@
//
//===----------------------------------------------------------------------===//
-#include "llvm/ADT/StringSwitch.h"
+#include "BodyFarm.h"
#include "clang/AST/ASTContext.h"
-#include "clang/AST/Expr.h"
#include "clang/AST/Decl.h"
+#include "clang/AST/Expr.h"
#include "clang/AST/ExprObjC.h"
-#include "BodyFarm.h"
+#include "llvm/ADT/StringSwitch.h"
using namespace clang;
@@ -103,9 +103,7 @@ BinaryOperator *ASTMaker::makeComparison(const Expr *LHS, const Expr *RHS,
}
CompoundStmt *ASTMaker::makeCompound(ArrayRef<Stmt *> Stmts) {
- return new (C) CompoundStmt(C, const_cast<Stmt**>(Stmts.data()),
- Stmts.size(),
- SourceLocation(), SourceLocation());
+ return new (C) CompoundStmt(C, Stmts, SourceLocation(), SourceLocation());
}
DeclRefExpr *ASTMaker::makeDeclRefExpr(const VarDecl *D) {
@@ -270,7 +268,11 @@ static Stmt *create_OSAtomicCompareAndSwap(ASTContext &C, const FunctionDecl *D)
if (D->param_size() != 3)
return 0;
- // Body for:
+ // Signature:
+ // _Bool OSAtomicCompareAndSwapPtr(void *__oldValue,
+ // void *__newValue,
+ // void * volatile *__theValue)
+ // Generate body:
// if (oldValue == *theValue) {
// *theValue = newValue;
// return YES;
@@ -342,7 +344,7 @@ static Stmt *create_OSAtomicCompareAndSwap(ASTContext &C, const FunctionDecl *D)
Stmt *BodyFarm::getBody(const FunctionDecl *D) {
D = D->getCanonicalDecl();
- llvm::Optional<Stmt *> &Val = Bodies[D];
+ Optional<Stmt *> &Val = Bodies[D];
if (Val.hasValue())
return Val.getValue();
diff --git a/lib/Analysis/BodyFarm.h b/lib/Analysis/BodyFarm.h
index d503cc1..96f61df 100644
--- a/lib/Analysis/BodyFarm.h
+++ b/lib/Analysis/BodyFarm.h
@@ -15,8 +15,9 @@
#ifndef LLVM_CLANG_ANALYSIS_BODYFARM_H
#define LLVM_CLANG_ANALYSIS_BODYFARM_H
-#include "llvm/ADT/Optional.h"
+#include "clang/Basic/LLVM.h"
#include "llvm/ADT/DenseMap.h"
+#include "llvm/ADT/Optional.h"
namespace clang {
@@ -33,7 +34,7 @@ public:
Stmt *getBody(const FunctionDecl *D);
private:
- typedef llvm::DenseMap<const Decl *, llvm::Optional<Stmt *> > BodyMap;
+ typedef llvm::DenseMap<const Decl *, Optional<Stmt *> > BodyMap;
ASTContext &C;
BodyMap Bodies;
diff --git a/lib/Analysis/CFG.cpp b/lib/Analysis/CFG.cpp
index 315e543..1adb8b8 100644
--- a/lib/Analysis/CFG.cpp
+++ b/lib/Analysis/CFG.cpp
@@ -12,20 +12,20 @@
//
//===----------------------------------------------------------------------===//
-#include "llvm/Support/SaveAndRestore.h"
#include "clang/Analysis/CFG.h"
#include "clang/AST/ASTContext.h"
+#include "clang/AST/Attr.h"
+#include "clang/AST/CharUnits.h"
#include "clang/AST/DeclCXX.h"
-#include "clang/AST/StmtVisitor.h"
#include "clang/AST/PrettyPrinter.h"
-#include "clang/AST/CharUnits.h"
-#include "clang/Basic/AttrKinds.h"
-#include "llvm/Support/GraphWriter.h"
-#include "llvm/Support/Allocator.h"
-#include "llvm/Support/Format.h"
+#include "clang/AST/StmtVisitor.h"
#include "llvm/ADT/DenseMap.h"
-#include "llvm/ADT/SmallPtrSet.h"
#include "llvm/ADT/OwningPtr.h"
+#include "llvm/ADT/SmallPtrSet.h"
+#include "llvm/Support/Allocator.h"
+#include "llvm/Support/Format.h"
+#include "llvm/Support/GraphWriter.h"
+#include "llvm/Support/SaveAndRestore.h"
using namespace clang;
@@ -233,6 +233,44 @@ public:
}
};
+class reverse_children {
+ llvm::SmallVector<Stmt *, 12> childrenBuf;
+ ArrayRef<Stmt*> children;
+public:
+ reverse_children(Stmt *S);
+
+ typedef ArrayRef<Stmt*>::reverse_iterator iterator;
+ iterator begin() const { return children.rbegin(); }
+ iterator end() const { return children.rend(); }
+};
+
+
+reverse_children::reverse_children(Stmt *S) {
+ if (CallExpr *CE = dyn_cast<CallExpr>(S)) {
+ children = CE->getRawSubExprs();
+ return;
+ }
+ switch (S->getStmtClass()) {
+ // Note: Fill in this switch with more cases we want to optimize.
+ case Stmt::InitListExprClass: {
+ InitListExpr *IE = cast<InitListExpr>(S);
+ children = llvm::makeArrayRef(reinterpret_cast<Stmt**>(IE->getInits()),
+ IE->getNumInits());
+ return;
+ }
+ default:
+ break;
+ }
+
+ // Default case for all other statements.
+ for (Stmt::child_range I = S->children(); I; ++I) {
+ childrenBuf.push_back(*I);
+ }
+
+ // This needs to be done *after* childrenBuf has been populated.
+ children = childrenBuf;
+}
+
/// CFGBuilder - This class implements CFG construction from an AST.
/// The builder is stateful: an instance of the builder should be used to only
/// construct a single CFG.
@@ -637,7 +675,7 @@ CFG* CFGBuilder::buildCFG(const Decl *D, Stmt *Statement) {
E = BackpatchBlocks.end(); I != E; ++I ) {
CFGBlock *B = I->block;
- GotoStmt *G = cast<GotoStmt>(B->getTerminator());
+ const GotoStmt *G = cast<GotoStmt>(B->getTerminator());
LabelMapTy::iterator LI = LabelMap.find(G->getLabel());
// If there is no target for the goto, then we are looking at an
@@ -807,7 +845,7 @@ void CFGBuilder::addAutomaticObjDtors(LocalScope::const_iterator B,
Ty = Context->getBaseElementType(Ty);
const CXXDestructorDecl *Dtor = Ty->getAsCXXRecordDecl()->getDestructor();
- if (cast<FunctionType>(Dtor->getType())->getNoReturnAttr())
+ if (Dtor->isNoReturn())
Block = createNoReturnBlock();
else
autoCreateBlock();
@@ -1166,14 +1204,19 @@ CFGBlock *CFGBuilder::VisitStmt(Stmt *S, AddStmtChoice asc) {
}
/// VisitChildren - Visit the children of a Stmt.
-CFGBlock *CFGBuilder::VisitChildren(Stmt *Terminator) {
- CFGBlock *lastBlock = Block;
- for (Stmt::child_range I = Terminator->children(); I; ++I)
- if (Stmt *child = *I)
- if (CFGBlock *b = Visit(child))
- lastBlock = b;
+CFGBlock *CFGBuilder::VisitChildren(Stmt *S) {
+ CFGBlock *B = Block;
- return lastBlock;
+ // Visit the children in their reverse order so that they appear in
+ // left-to-right (natural) order in the CFG.
+ reverse_children RChildren(S);
+ for (reverse_children::iterator I = RChildren.begin(), E = RChildren.end();
+ I != E; ++I) {
+ if (Stmt *Child = *I)
+ if (CFGBlock *R = Visit(Child))
+ B = R;
+ }
+ return B;
}
CFGBlock *CFGBuilder::VisitAddrLabelExpr(AddrLabelExpr *A,
@@ -1402,7 +1445,7 @@ CFGBlock *CFGBuilder::VisitCallExpr(CallExpr *C, AddStmtChoice asc) {
}
if (FunctionDecl *FD = C->getDirectCallee()) {
- if (FD->hasAttr<NoReturnAttr>())
+ if (FD->isNoReturn())
NoReturn = true;
if (FD->hasAttr<NoThrowAttr>())
AddEHEdge = false;
@@ -1610,6 +1653,21 @@ CFGBlock *CFGBuilder::VisitDeclSubExpr(DeclStmt *DS) {
bool IsReference = false;
bool HasTemporaries = false;
+ // Guard static initializers under a branch.
+ CFGBlock *blockAfterStaticInit = 0;
+
+ if (BuildOpts.AddStaticInitBranches && VD->isStaticLocal()) {
+ // For static variables, we need to create a branch to track
+ // whether or not they are initialized.
+ if (Block) {
+ Succ = Block;
+ Block = 0;
+ if (badCFG)
+ return 0;
+ }
+ blockAfterStaticInit = Succ;
+ }
+
// Destructors of temporaries in initialization expression should be called
// after initialization finishes.
Expr *Init = VD->getInit();
@@ -1657,7 +1715,17 @@ CFGBlock *CFGBuilder::VisitDeclSubExpr(DeclStmt *DS) {
if (ScopePos && VD == *ScopePos)
++ScopePos;
- return Block ? Block : LastBlock;
+ CFGBlock *B = LastBlock;
+ if (blockAfterStaticInit) {
+ Succ = B;
+ Block = createBlock(false);
+ Block->setTerminator(DS);
+ addSuccessor(Block, blockAfterStaticInit);
+ addSuccessor(Block, B);
+ B = Block;
+ }
+
+ return B;
}
CFGBlock *CFGBuilder::VisitIfStmt(IfStmt *I) {
@@ -3093,19 +3161,14 @@ tryAgain:
CFGBlock *CFGBuilder::VisitChildrenForTemporaryDtors(Stmt *E) {
// When visiting children for destructors we want to visit them in reverse
- // order. Because there's no reverse iterator for children must to reverse
- // them in helper vector.
- typedef SmallVector<Stmt *, 4> ChildrenVect;
- ChildrenVect ChildrenRev;
- for (Stmt::child_range I = E->children(); I; ++I) {
- if (*I) ChildrenRev.push_back(*I);
- }
-
+ // order that they will appear in the CFG. Because the CFG is built
+ // bottom-up, this means we visit them in their natural order, which
+ // reverses them in the CFG.
CFGBlock *B = Block;
- for (ChildrenVect::reverse_iterator I = ChildrenRev.rbegin(),
- L = ChildrenRev.rend(); I != L; ++I) {
- if (CFGBlock *R = VisitForTemporaryDtors(*I))
- B = R;
+ for (Stmt::child_range I = E->children(); I; ++I) {
+ if (Stmt *Child = *I)
+ if (CFGBlock *R = VisitForTemporaryDtors(Child))
+ B = R;
}
return B;
}
@@ -3190,7 +3253,7 @@ CFGBlock *CFGBuilder::VisitCXXBindTemporaryExprForTemporaryDtors(
// 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 (cast<FunctionType>(Dtor->getType())->getNoReturnAttr())
+ if (Dtor->isNoReturn())
Block = createNoReturnBlock();
else
autoCreateBlock();
@@ -3294,13 +3357,12 @@ CFG* CFG::buildCFG(const Decl *D, Stmt *Statement, ASTContext *C,
const CXXDestructorDecl *
CFGImplicitDtor::getDestructorDecl(ASTContext &astContext) const {
switch (getKind()) {
- case CFGElement::Invalid:
case CFGElement::Statement:
case CFGElement::Initializer:
llvm_unreachable("getDestructorDecl should only be used with "
"ImplicitDtors");
case CFGElement::AutomaticObjectDtor: {
- const VarDecl *var = cast<CFGAutomaticObjDtor>(this)->getVarDecl();
+ const VarDecl *var = castAs<CFGAutomaticObjDtor>().getVarDecl();
QualType ty = var->getType();
ty = ty.getNonReferenceType();
while (const ArrayType *arrayType = astContext.getAsArrayType(ty)) {
@@ -3313,7 +3375,7 @@ CFGImplicitDtor::getDestructorDecl(ASTContext &astContext) const {
}
case CFGElement::TemporaryDtor: {
const CXXBindTemporaryExpr *bindExpr =
- cast<CFGTemporaryDtor>(this)->getBindTemporaryExpr();
+ castAs<CFGTemporaryDtor>().getBindTemporaryExpr();
const CXXTemporary *temp = bindExpr->getTemporary();
return temp->getDestructor();
}
@@ -3327,10 +3389,8 @@ CFGImplicitDtor::getDestructorDecl(ASTContext &astContext) const {
}
bool CFGImplicitDtor::isNoReturn(ASTContext &astContext) const {
- if (const CXXDestructorDecl *decl = getDestructorDecl(astContext)) {
- QualType ty = decl->getType();
- return cast<FunctionType>(ty)->getNoReturnAttr();
- }
+ if (const CXXDestructorDecl *DD = getDestructorDecl(astContext))
+ return DD->isNoReturn();
return false;
}
@@ -3370,7 +3430,7 @@ static BlkExprMapTy* PopulateBlkExprMap(CFG& cfg) {
for (CFG::iterator I=cfg.begin(), E=cfg.end(); I != E; ++I)
for (CFGBlock::iterator BI=(*I)->begin(), EI=(*I)->end(); BI != EI; ++BI)
- if (const CFGStmt *S = BI->getAs<CFGStmt>())
+ if (Optional<CFGStmt> S = BI->getAs<CFGStmt>())
FindSubExprAssignments(S->getStmt(), SubExprAssignments);
for (CFG::iterator I=cfg.begin(), E=cfg.end(); I != E; ++I) {
@@ -3379,7 +3439,7 @@ static BlkExprMapTy* PopulateBlkExprMap(CFG& cfg) {
// block-level that are block-level expressions.
for (CFGBlock::iterator BI=(*I)->begin(), EI=(*I)->end(); BI != EI; ++BI) {
- const CFGStmt *CS = BI->getAs<CFGStmt>();
+ Optional<CFGStmt> CS = BI->getAs<CFGStmt>();
if (!CS)
continue;
if (const Expr *Exp = dyn_cast<Expr>(CS->getStmt())) {
@@ -3495,7 +3555,7 @@ public:
unsigned j = 1;
for (CFGBlock::const_iterator BI = (*I)->begin(), BEnd = (*I)->end() ;
BI != BEnd; ++BI, ++j ) {
- if (const CFGStmt *SE = BI->getAs<CFGStmt>()) {
+ if (Optional<CFGStmt> SE = BI->getAs<CFGStmt>()) {
const Stmt *stmt= SE->getStmt();
std::pair<unsigned, unsigned> P((*I)->getBlockID(), j);
StmtMap[stmt] = P;
@@ -3607,6 +3667,11 @@ public:
Terminator->printPretty(OS, Helper, Policy);
}
+ void VisitDeclStmt(DeclStmt *DS) {
+ VarDecl *VD = cast<VarDecl>(DS->getSingleDecl());
+ OS << "static init " << VD->getName();
+ }
+
void VisitForStmt(ForStmt *F) {
OS << "for (" ;
if (F->getInit())
@@ -3685,7 +3750,7 @@ public:
static void print_elem(raw_ostream &OS, StmtPrinterHelper* Helper,
const CFGElement &E) {
- if (const CFGStmt *CS = E.getAs<CFGStmt>()) {
+ if (Optional<CFGStmt> CS = E.getAs<CFGStmt>()) {
const Stmt *S = CS->getStmt();
if (Helper) {
@@ -3733,7 +3798,7 @@ static void print_elem(raw_ostream &OS, StmtPrinterHelper* Helper,
if (isa<Expr>(S))
OS << '\n';
- } else if (const CFGInitializer *IE = E.getAs<CFGInitializer>()) {
+ } else if (Optional<CFGInitializer> IE = E.getAs<CFGInitializer>()) {
const CXXCtorInitializer *I = IE->getInitializer();
if (I->isBaseInitializer())
OS << I->getBaseClass()->getAsCXXRecordDecl()->getName();
@@ -3748,7 +3813,8 @@ static void print_elem(raw_ostream &OS, StmtPrinterHelper* Helper,
OS << " (Base initializer)\n";
else OS << " (Member initializer)\n";
- } else if (const CFGAutomaticObjDtor *DE = E.getAs<CFGAutomaticObjDtor>()){
+ } else if (Optional<CFGAutomaticObjDtor> DE =
+ E.getAs<CFGAutomaticObjDtor>()) {
const VarDecl *VD = DE->getVarDecl();
Helper->handleDecl(VD, OS);
@@ -3760,19 +3826,19 @@ static void print_elem(raw_ostream &OS, StmtPrinterHelper* Helper,
OS << ".~" << T->getAsCXXRecordDecl()->getName().str() << "()";
OS << " (Implicit destructor)\n";
- } else if (const CFGBaseDtor *BE = E.getAs<CFGBaseDtor>()) {
+ } else if (Optional<CFGBaseDtor> BE = E.getAs<CFGBaseDtor>()) {
const CXXBaseSpecifier *BS = BE->getBaseSpecifier();
OS << "~" << BS->getType()->getAsCXXRecordDecl()->getName() << "()";
OS << " (Base object destructor)\n";
- } else if (const CFGMemberDtor *ME = E.getAs<CFGMemberDtor>()) {
+ } else if (Optional<CFGMemberDtor> ME = E.getAs<CFGMemberDtor>()) {
const FieldDecl *FD = ME->getFieldDecl();
const Type *T = FD->getType()->getBaseElementTypeUnsafe();
OS << "this->" << FD->getName();
OS << ".~" << T->getAsCXXRecordDecl()->getName() << "()";
OS << " (Member object destructor)\n";
- } else if (const CFGTemporaryDtor *TE = E.getAs<CFGTemporaryDtor>()) {
+ } else if (Optional<CFGTemporaryDtor> TE = E.getAs<CFGTemporaryDtor>()) {
const CXXBindTemporaryExpr *BT = TE->getBindTemporaryExpr();
OS << "~" << BT->getType()->getAsCXXRecordDecl()->getName() << "()";
OS << " (Temporary object destructor)\n";
@@ -3893,7 +3959,7 @@ static void print_block(raw_ostream &OS, const CFG* cfg,
for (CFGBlock::const_pred_iterator I = B.pred_begin(), E = B.pred_end();
I != E; ++I, ++i) {
- if (i == 8 || (i-8) == 0)
+ if (i % 10 == 8)
OS << "\n ";
OS << " B" << (*I)->getBlockID();
@@ -3922,7 +3988,7 @@ static void print_block(raw_ostream &OS, const CFG* cfg,
for (CFGBlock::const_succ_iterator I = B.succ_begin(), E = B.succ_end();
I != E; ++I, ++i) {
- if (i == 8 || (i-8) % 10 == 0)
+ if (i % 10 == 8)
OS << "\n ";
if (*I)
diff --git a/lib/Analysis/CFGStmtMap.cpp b/lib/Analysis/CFGStmtMap.cpp
index 16df676..87c2f5b 100644
--- a/lib/Analysis/CFGStmtMap.cpp
+++ b/lib/Analysis/CFGStmtMap.cpp
@@ -50,7 +50,7 @@ static void Accumulate(SMap &SM, CFGBlock *B) {
// First walk the block-level expressions.
for (CFGBlock::iterator I = B->begin(), E = B->end(); I != E; ++I) {
const CFGElement &CE = *I;
- const CFGStmt *CS = CE.getAs<CFGStmt>();
+ Optional<CFGStmt> CS = CE.getAs<CFGStmt>();
if (!CS)
continue;
diff --git a/lib/Analysis/CallGraph.cpp b/lib/Analysis/CallGraph.cpp
index 6b75956..3387015 100644
--- a/lib/Analysis/CallGraph.cpp
+++ b/lib/Analysis/CallGraph.cpp
@@ -10,16 +10,21 @@
// This file defines the AST-based CallGraph.
//
//===----------------------------------------------------------------------===//
-#include "clang/Analysis/CallGraph.h"
+#define DEBUG_TYPE "CallGraph"
+#include "clang/Analysis/CallGraph.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/Decl.h"
#include "clang/AST/StmtVisitor.h"
-
+#include "llvm/ADT/PostOrderIterator.h"
+#include "llvm/ADT/Statistic.h"
#include "llvm/Support/GraphWriter.h"
using namespace clang;
+STATISTIC(NumObjCCallEdges, "Number of Objective-C method call edges");
+STATISTIC(NumBlockCallEdges, "Number of block call edges");
+
namespace {
/// A helper class, which walks the AST and locates all the call sites in the
/// given function body.
@@ -33,13 +38,48 @@ public:
void VisitStmt(Stmt *S) { VisitChildren(S); }
- void VisitCallExpr(CallExpr *CE) {
- // TODO: We need to handle ObjC method calls as well.
+ Decl *getDeclFromCall(CallExpr *CE) {
if (FunctionDecl *CalleeDecl = CE->getDirectCallee())
- if (G->includeInGraph(CalleeDecl)) {
- CallGraphNode *CalleeNode = G->getOrInsertNode(CalleeDecl);
- CallerNode->addCallee(CalleeNode, G);
+ return CalleeDecl;
+
+ // Simple detection of a call through a block.
+ Expr *CEE = CE->getCallee()->IgnoreParenImpCasts();
+ if (BlockExpr *Block = dyn_cast<BlockExpr>(CEE)) {
+ NumBlockCallEdges++;
+ return Block->getBlockDecl();
+ }
+
+ return 0;
+ }
+
+ void addCalledDecl(Decl *D) {
+ if (G->includeInGraph(D)) {
+ CallGraphNode *CalleeNode = G->getOrInsertNode(D);
+ CallerNode->addCallee(CalleeNode, G);
+ }
+ }
+
+ void VisitCallExpr(CallExpr *CE) {
+ if (Decl *D = getDeclFromCall(CE))
+ addCalledDecl(D);
+ }
+
+ // Adds may-call edges for the ObjC message sends.
+ void VisitObjCMessageExpr(ObjCMessageExpr *ME) {
+ if (ObjCInterfaceDecl *IDecl = ME->getReceiverInterface()) {
+ Selector Sel = ME->getSelector();
+
+ // Find the callee definition within the same translation unit.
+ Decl *D = 0;
+ if (ME->isInstanceMessage())
+ D = IDecl->lookupPrivateMethod(Sel);
+ else
+ D = IDecl->lookupPrivateClassMethod(Sel);
+ if (D) {
+ addCalledDecl(D);
+ NumObjCCallEdges++;
}
+ }
}
void VisitChildren(Stmt *S) {
@@ -51,6 +91,16 @@ public:
} // end anonymous namespace
+void CallGraph::addNodesForBlocks(DeclContext *D) {
+ 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))
+ addNodesForBlocks(DC);
+}
+
CallGraph::CallGraph() {
Root = getOrInsertNode(0);
}
@@ -65,6 +115,10 @@ CallGraph::~CallGraph() {
}
bool CallGraph::includeInGraph(const Decl *D) {
+ assert(D);
+ if (!D->getBody())
+ return false;
+
if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(D)) {
// We skip function template definitions, as their semantics is
// only determined when they are instantiated.
@@ -88,14 +142,8 @@ bool CallGraph::includeInGraph(const Decl *D) {
void CallGraph::addNodeForDecl(Decl* D, bool IsGlobal) {
assert(D);
- // Do nothing if the node already exists.
- if (FunctionMap.find(D) != FunctionMap.end())
- return;
-
// Allocate a new node, mark it as root, and process it's calls.
CallGraphNode *Node = getOrInsertNode(D);
- if (IsGlobal)
- Root->addCallee(Node, this);
// Process all the calls by this function as well.
CGBuilder builder(this, Node);
@@ -115,23 +163,31 @@ CallGraphNode *CallGraph::getOrInsertNode(Decl *F) {
return Node;
Node = new CallGraphNode(F);
- // If not root, add to the parentless list.
+ // Make Root node a parent of all functions to make sure all are reachable.
if (F != 0)
- ParentlessNodes.insert(Node);
+ Root->addCallee(Node, this);
return Node;
}
void CallGraph::print(raw_ostream &OS) const {
OS << " --- Call graph Dump --- \n";
- for (const_iterator I = begin(), E = end(); I != E; ++I) {
+
+ // We are going to print the graph in reverse post order, partially, to make
+ // sure the output is deterministic.
+ llvm::ReversePostOrderTraversal<const clang::CallGraph*> RPOT(this);
+ for (llvm::ReversePostOrderTraversal<const clang::CallGraph*>::rpo_iterator
+ I = RPOT.begin(), E = RPOT.end(); I != E; ++I) {
+ const CallGraphNode *N = *I;
+
OS << " Function: ";
- if (I->second == Root)
+ if (N == Root)
OS << "< root >";
else
- I->second->print(OS);
+ N->print(OS);
+
OS << " calls: ";
- for (CallGraphNode::iterator CI = I->second->begin(),
- CE = I->second->end(); CI != CE; ++CI) {
+ for (CallGraphNode::const_iterator CI = N->begin(),
+ CE = N->end(); CI != CE; ++CI) {
assert(*CI != Root && "No one can call the root node.");
(*CI)->print(OS);
OS << " ";
@@ -149,15 +205,10 @@ void CallGraph::viewGraph() const {
llvm::ViewGraph(this, "CallGraph");
}
-StringRef CallGraphNode::getName() const {
- if (const FunctionDecl *D = dyn_cast_or_null<FunctionDecl>(FD))
- if (const IdentifierInfo *II = D->getIdentifier())
- return II->getName();
- return "< >";
-}
-
void CallGraphNode::print(raw_ostream &os) const {
- os << getName();
+ if (const NamedDecl *ND = dyn_cast_or_null<NamedDecl>(FD))
+ return ND->printName(os);
+ os << "< >";
}
void CallGraphNode::dump() const {
@@ -176,7 +227,10 @@ struct DOTGraphTraits<const CallGraph*> : public DefaultDOTGraphTraits {
if (CG->getRoot() == Node) {
return "< root >";
}
- return Node->getName();
+ if (const NamedDecl *ND = dyn_cast_or_null<NamedDecl>(Node->getDecl()))
+ return ND->getNameAsString();
+ else
+ return "< >";
}
};
diff --git a/lib/Analysis/CocoaConventions.cpp b/lib/Analysis/CocoaConventions.cpp
index ce973af..0db3cac 100644
--- a/lib/Analysis/CocoaConventions.cpp
+++ b/lib/Analysis/CocoaConventions.cpp
@@ -12,12 +12,12 @@
//===----------------------------------------------------------------------===//
#include "clang/Analysis/DomainSpecific/CocoaConventions.h"
-#include "clang/AST/Type.h"
#include "clang/AST/Decl.h"
#include "clang/AST/DeclObjC.h"
+#include "clang/AST/Type.h"
+#include "clang/Basic/CharInfo.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/Support/ErrorHandling.h"
-#include <cctype>
using namespace clang;
using namespace ento;
@@ -106,7 +106,7 @@ bool coreFoundation::followsCreateRule(const FunctionDecl *fn) {
char ch = *it;
if (ch == 'C' || ch == 'c') {
// Make sure this isn't something like 'recreate' or 'Scopy'.
- if (ch == 'c' && it != start && isalpha(*(it - 1)))
+ if (ch == 'c' && it != start && isLetter(*(it - 1)))
continue;
++it;
@@ -131,7 +131,7 @@ bool coreFoundation::followsCreateRule(const FunctionDecl *fn) {
continue;
}
- if (it == endI || !islower(*it))
+ if (it == endI || !isLowercase(*it))
return true;
// If we matched a lowercase character, it isn't the end of the
diff --git a/lib/Analysis/FormatString.cpp b/lib/Analysis/FormatString.cpp
index 73063b5..ad0dce4 100644
--- a/lib/Analysis/FormatString.cpp
+++ b/lib/Analysis/FormatString.cpp
@@ -204,7 +204,7 @@ clang::analyze_format_string::ParseLengthModifier(FormatSpecifier &FS,
case 'L': lmKind = LengthModifier::AsLongDouble; ++I; break;
case 'q': lmKind = LengthModifier::AsQuad; ++I; break;
case 'a':
- if (IsScanf && !LO.C99 && !LO.CPlusPlus0x) {
+ if (IsScanf && !LO.C99 && !LO.CPlusPlus11) {
// For scanf in C90, look at the next character to see if this should
// be parsed as the GNU extension 'a' length modifier. If not, this
// will be parsed as a conversion specifier.
@@ -527,13 +527,13 @@ const char *ConversionSpecifier::toString() const {
return NULL;
}
-llvm::Optional<ConversionSpecifier>
+Optional<ConversionSpecifier>
ConversionSpecifier::getStandardSpecifier() const {
ConversionSpecifier::Kind NewKind;
switch (getKind()) {
default:
- return llvm::Optional<ConversionSpecifier>();
+ return None;
case DArg:
NewKind = dArg;
break;
@@ -756,8 +756,7 @@ bool FormatSpecifier::hasStandardLengthConversionCombination() const {
return true;
}
-llvm::Optional<LengthModifier>
-FormatSpecifier::getCorrectedLengthModifier() const {
+Optional<LengthModifier> FormatSpecifier::getCorrectedLengthModifier() const {
if (CS.isAnyIntArg() || CS.getKind() == ConversionSpecifier::nArg) {
if (LM.getKind() == LengthModifier::AsLongDouble ||
LM.getKind() == LengthModifier::AsQuad) {
@@ -767,7 +766,7 @@ FormatSpecifier::getCorrectedLengthModifier() const {
}
}
- return llvm::Optional<LengthModifier>();
+ return None;
}
bool FormatSpecifier::namedTypeToLengthModifier(QualType QT,
diff --git a/lib/Analysis/FormatStringParsing.h b/lib/Analysis/FormatStringParsing.h
index f483ec6..6b25123 100644
--- a/lib/Analysis/FormatStringParsing.h
+++ b/lib/Analysis/FormatStringParsing.h
@@ -1,9 +1,9 @@
#ifndef LLVM_CLANG_FORMAT_PARSING_H
#define LLVM_CLANG_FORMAT_PARSING_H
-#include "clang/Analysis/Analyses/FormatString.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/Type.h"
+#include "clang/Analysis/Analyses/FormatString.h"
#include "llvm/Support/raw_ostream.h"
namespace clang {
diff --git a/lib/Analysis/LiveVariables.cpp b/lib/Analysis/LiveVariables.cpp
index 38f8199..b43892a 100644
--- a/lib/Analysis/LiveVariables.cpp
+++ b/lib/Analysis/LiveVariables.cpp
@@ -1,15 +1,25 @@
-#include "clang/Analysis/Analyses/LiveVariables.h"
-#include "clang/Analysis/Analyses/PostOrderCFGView.h"
+//=- LiveVariables.cpp - Live Variable Analysis for Source CFGs ----------*-==//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file implements Live Variables analysis for source-level CFGs.
+//
+//===----------------------------------------------------------------------===//
+#include "clang/Analysis/Analyses/LiveVariables.h"
#include "clang/AST/Stmt.h"
-#include "clang/Analysis/CFG.h"
-#include "clang/Analysis/AnalysisContext.h"
#include "clang/AST/StmtVisitor.h"
-
-#include "llvm/ADT/PostOrderIterator.h"
+#include "clang/Analysis/Analyses/PostOrderCFGView.h"
+#include "clang/Analysis/AnalysisContext.h"
+#include "clang/Analysis/CFG.h"
#include "llvm/ADT/DenseMap.h"
-
-#include <deque>
+#include "llvm/ADT/PostOrderIterator.h"
+#include "llvm/Support/raw_ostream.h"
#include <algorithm>
#include <vector>
@@ -464,15 +474,16 @@ LiveVariablesImpl::runOnBlock(const CFGBlock *block,
ei = block->rend(); it != ei; ++it) {
const CFGElement &elem = *it;
- if (const CFGAutomaticObjDtor *Dtor = dyn_cast<CFGAutomaticObjDtor>(&elem)){
+ if (Optional<CFGAutomaticObjDtor> Dtor =
+ elem.getAs<CFGAutomaticObjDtor>()) {
val.liveDecls = DSetFact.add(val.liveDecls, Dtor->getVarDecl());
continue;
}
- if (!isa<CFGStmt>(elem))
+ if (!elem.getAs<CFGStmt>())
continue;
- const Stmt *S = cast<CFGStmt>(elem).getStmt();
+ const Stmt *S = elem.castAs<CFGStmt>().getStmt();
TF.Visit(const_cast<Stmt*>(S));
stmtsToLiveness[S] = val;
}
@@ -524,8 +535,9 @@ LiveVariables::computeLiveness(AnalysisDeclContext &AC,
if (killAtAssign)
for (CFGBlock::const_iterator bi = block->begin(), be = block->end();
bi != be; ++bi) {
- if (const CFGStmt *cs = bi->getAs<CFGStmt>()) {
- if (const BinaryOperator *BO = dyn_cast<BinaryOperator>(cs->getStmt())) {
+ if (Optional<CFGStmt> cs = bi->getAs<CFGStmt>()) {
+ if (const BinaryOperator *BO =
+ dyn_cast<BinaryOperator>(cs->getStmt())) {
if (BO->getOpcode() == BO_Assign) {
if (const DeclRefExpr *DR =
dyn_cast<DeclRefExpr>(BO->getLHS()->IgnoreParens())) {
diff --git a/lib/Analysis/PrintfFormatString.cpp b/lib/Analysis/PrintfFormatString.cpp
index 2fa5a88..8f151b9 100644
--- a/lib/Analysis/PrintfFormatString.cpp
+++ b/lib/Analysis/PrintfFormatString.cpp
@@ -13,8 +13,8 @@
//===----------------------------------------------------------------------===//
#include "clang/Analysis/Analyses/FormatString.h"
-#include "clang/Basic/TargetInfo.h"
#include "FormatStringParsing.h"
+#include "clang/Basic/TargetInfo.h"
using clang::analyze_format_string::ArgType;
using clang::analyze_format_string::FormatStringHandler;
@@ -359,17 +359,19 @@ ArgType PrintfSpecifier::getArgType(ASTContext &Ctx,
case ConversionSpecifier::sArg:
if (LM.getKind() == LengthModifier::AsWideChar) {
if (IsObjCLiteral)
- return Ctx.getPointerType(Ctx.UnsignedShortTy.withConst());
+ return ArgType(Ctx.getPointerType(Ctx.UnsignedShortTy.withConst()),
+ "const unichar *");
return ArgType(ArgType::WCStrTy, "wchar_t *");
}
return ArgType::CStrTy;
case ConversionSpecifier::SArg:
if (IsObjCLiteral)
- return Ctx.getPointerType(Ctx.UnsignedShortTy.withConst());
+ return ArgType(Ctx.getPointerType(Ctx.UnsignedShortTy.withConst()),
+ "const unichar *");
return ArgType(ArgType::WCStrTy, "wchar_t *");
case ConversionSpecifier::CArg:
if (IsObjCLiteral)
- return Ctx.UnsignedShortTy;
+ return ArgType(Ctx.UnsignedShortTy, "unichar");
return ArgType(Ctx.WCharTy, "wchar_t");
case ConversionSpecifier::pArg:
return ArgType::CPointerTy;
@@ -494,11 +496,29 @@ bool PrintfSpecifier::fixType(QualType QT, const LangOptions &LangOpt,
}
// Handle size_t, ptrdiff_t, etc. that have dedicated length modifiers in C99.
- if (isa<TypedefType>(QT) && (LangOpt.C99 || LangOpt.CPlusPlus0x))
+ if (isa<TypedefType>(QT) && (LangOpt.C99 || LangOpt.CPlusPlus11))
namedTypeToLengthModifier(QT, LM);
- // If fixing the length modifier was enough, we are done.
+ // If fixing the length modifier was enough, we might be done.
if (hasValidLengthModifier(Ctx.getTargetInfo())) {
+ // If we're going to offer a fix anyway, make sure the sign matches.
+ switch (CS.getKind()) {
+ case ConversionSpecifier::uArg:
+ case ConversionSpecifier::UArg:
+ if (QT->isSignedIntegerType())
+ CS.setKind(clang::analyze_format_string::ConversionSpecifier::dArg);
+ break;
+ case ConversionSpecifier::dArg:
+ case ConversionSpecifier::DArg:
+ case ConversionSpecifier::iArg:
+ if (QT->isUnsignedIntegerType() && !HasPlusPrefix)
+ CS.setKind(clang::analyze_format_string::ConversionSpecifier::uArg);
+ break;
+ default:
+ // Other specifiers do not have signed/unsigned variants.
+ break;
+ }
+
const analyze_printf::ArgType &ATR = getArgType(Ctx, IsObjCLiteral);
if (ATR.isValid() && ATR.matchesType(Ctx, QT))
return true;
@@ -506,7 +526,7 @@ bool PrintfSpecifier::fixType(QualType QT, const LangOptions &LangOpt,
// Set conversion specifier and disable any flags which do not apply to it.
// Let typedefs to char fall through to int, as %c is silly for uint8_t.
- if (isa<TypedefType>(QT) && QT->isAnyCharacterType()) {
+ if (!isa<TypedefType>(QT) && QT->isCharType()) {
CS.setKind(ConversionSpecifier::cArg);
LM.setKind(LengthModifier::None);
Precision.setHowSpecified(OptionalAmount::NotSpecified);
diff --git a/lib/Analysis/ReachableCode.cpp b/lib/Analysis/ReachableCode.cpp
index 11f2ebe..a90aebb 100644
--- a/lib/Analysis/ReachableCode.cpp
+++ b/lib/Analysis/ReachableCode.cpp
@@ -12,16 +12,16 @@
//
//===----------------------------------------------------------------------===//
-#include "llvm/ADT/BitVector.h"
-#include "llvm/ADT/SmallVector.h"
+#include "clang/Analysis/Analyses/ReachableCode.h"
#include "clang/AST/Expr.h"
#include "clang/AST/ExprCXX.h"
#include "clang/AST/ExprObjC.h"
#include "clang/AST/StmtCXX.h"
-#include "clang/Analysis/Analyses/ReachableCode.h"
-#include "clang/Analysis/CFG.h"
#include "clang/Analysis/AnalysisContext.h"
+#include "clang/Analysis/CFG.h"
#include "clang/Basic/SourceManager.h"
+#include "llvm/ADT/BitVector.h"
+#include "llvm/ADT/SmallVector.h"
using namespace clang;
@@ -29,9 +29,9 @@ namespace {
class DeadCodeScan {
llvm::BitVector Visited;
llvm::BitVector &Reachable;
- llvm::SmallVector<const CFGBlock *, 10> WorkList;
+ SmallVector<const CFGBlock *, 10> WorkList;
- typedef llvm::SmallVector<std::pair<const CFGBlock *, const Stmt *>, 12>
+ typedef SmallVector<std::pair<const CFGBlock *, const Stmt *>, 12>
DeferredLocsTy;
DeferredLocsTy DeferredLocs;
@@ -95,7 +95,7 @@ static bool isValidDeadStmt(const Stmt *S) {
const Stmt *DeadCodeScan::findDeadCode(const clang::CFGBlock *Block) {
for (CFGBlock::const_iterator I = Block->begin(), E = Block->end(); I!=E; ++I)
- if (const CFGStmt *CS = I->getAs<CFGStmt>()) {
+ if (Optional<CFGStmt> CS = I->getAs<CFGStmt>()) {
const Stmt *S = CS->getStmt();
if (isValidDeadStmt(S))
return S;
diff --git a/lib/Analysis/ScanfFormatString.cpp b/lib/Analysis/ScanfFormatString.cpp
index 574e56a..2dbc9e4 100644
--- a/lib/Analysis/ScanfFormatString.cpp
+++ b/lib/Analysis/ScanfFormatString.cpp
@@ -13,8 +13,8 @@
//===----------------------------------------------------------------------===//
#include "clang/Analysis/Analyses/FormatString.h"
-#include "clang/Basic/TargetInfo.h"
#include "FormatStringParsing.h"
+#include "clang/Basic/TargetInfo.h"
using clang::analyze_format_string::ArgType;
using clang::analyze_format_string::FormatStringHandler;
@@ -445,7 +445,7 @@ bool ScanfSpecifier::fixType(QualType QT, const LangOptions &LangOpt,
}
// Handle size_t, ptrdiff_t, etc. that have dedicated length modifiers in C99.
- if (isa<TypedefType>(PT) && (LangOpt.C99 || LangOpt.CPlusPlus0x))
+ if (isa<TypedefType>(PT) && (LangOpt.C99 || LangOpt.CPlusPlus11))
namedTypeToLengthModifier(PT, LM);
// If fixing the length modifier was enough, we are done.
diff --git a/lib/Analysis/ThreadSafety.cpp b/lib/Analysis/ThreadSafety.cpp
index c7f1f62..4fe342d 100644
--- a/lib/Analysis/ThreadSafety.cpp
+++ b/lib/Analysis/ThreadSafety.cpp
@@ -16,17 +16,18 @@
//===----------------------------------------------------------------------===//
#include "clang/Analysis/Analyses/ThreadSafety.h"
-#include "clang/Analysis/Analyses/PostOrderCFGView.h"
-#include "clang/Analysis/AnalysisContext.h"
-#include "clang/Analysis/CFG.h"
-#include "clang/Analysis/CFGStmtMap.h"
+#include "clang/AST/Attr.h"
#include "clang/AST/DeclCXX.h"
#include "clang/AST/ExprCXX.h"
#include "clang/AST/StmtCXX.h"
#include "clang/AST/StmtVisitor.h"
-#include "clang/Basic/SourceManager.h"
-#include "clang/Basic/SourceLocation.h"
+#include "clang/Analysis/Analyses/PostOrderCFGView.h"
+#include "clang/Analysis/AnalysisContext.h"
+#include "clang/Analysis/CFG.h"
+#include "clang/Analysis/CFGStmtMap.h"
#include "clang/Basic/OperatorKinds.h"
+#include "clang/Basic/SourceLocation.h"
+#include "clang/Basic/SourceManager.h"
#include "llvm/ADT/BitVector.h"
#include "llvm/ADT/FoldingSet.h"
#include "llvm/ADT/ImmutableMap.h"
@@ -164,15 +165,16 @@ private:
/// should be evaluated; multiple calling contexts can be chained together
/// by the lock_returned attribute.
struct CallingContext {
- const NamedDecl* AttrDecl; // The decl to which the attribute is attached.
- Expr* SelfArg; // Implicit object argument -- e.g. 'this'
- bool SelfArrow; // is Self referred to with -> or .?
- unsigned NumArgs; // Number of funArgs
- Expr** FunArgs; // Function arguments
- CallingContext* PrevCtx; // The previous context; or 0 if none.
-
- CallingContext(const NamedDecl *D = 0, Expr *S = 0,
- unsigned N = 0, Expr **A = 0, CallingContext *P = 0)
+ const NamedDecl* AttrDecl; // The decl to which the attribute is attached.
+ const Expr* SelfArg; // Implicit object argument -- e.g. 'this'
+ bool SelfArrow; // is Self referred to with -> or .?
+ unsigned NumArgs; // Number of funArgs
+ 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)
{ }
@@ -272,15 +274,16 @@ private:
/// NDeref returns the number of Derefence and AddressOf operations
/// preceeding the Expr; this is used to decide whether to pretty-print
/// SExprs with . or ->.
- unsigned buildSExpr(Expr *Exp, CallingContext* CallCtx, int* NDeref = 0) {
+ unsigned buildSExpr(const Expr *Exp, CallingContext* CallCtx,
+ int* NDeref = 0) {
if (!Exp)
return 0;
- if (DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(Exp)) {
- NamedDecl *ND = cast<NamedDecl>(DRE->getDecl()->getCanonicalDecl());
- ParmVarDecl *PV = dyn_cast_or_null<ParmVarDecl>(ND);
+ if (const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(Exp)) {
+ const NamedDecl *ND = cast<NamedDecl>(DRE->getDecl()->getCanonicalDecl());
+ const ParmVarDecl *PV = dyn_cast_or_null<ParmVarDecl>(ND);
if (PV) {
- FunctionDecl *FD =
+ const FunctionDecl *FD =
cast<FunctionDecl>(PV->getDeclContext())->getCanonicalDecl();
unsigned i = PV->getFunctionScopeIndex();
@@ -309,18 +312,18 @@ private:
makeThis();
return 1;
}
- } else if (MemberExpr *ME = dyn_cast<MemberExpr>(Exp)) {
- NamedDecl *ND = ME->getMemberDecl();
+ } else if (const MemberExpr *ME = dyn_cast<MemberExpr>(Exp)) {
+ const NamedDecl *ND = ME->getMemberDecl();
int ImplicitDeref = ME->isArrow() ? 1 : 0;
unsigned Root = makeDot(ND, false);
unsigned Sz = buildSExpr(ME->getBase(), CallCtx, &ImplicitDeref);
NodeVec[Root].setArrow(ImplicitDeref > 0);
NodeVec[Root].setSize(Sz + 1);
return Sz + 1;
- } else if (CXXMemberCallExpr *CMCE = dyn_cast<CXXMemberCallExpr>(Exp)) {
+ } else if (const CXXMemberCallExpr *CMCE = dyn_cast<CXXMemberCallExpr>(Exp)) {
// When calling a function with a lock_returned attribute, replace
// the function call with the expression in lock_returned.
- CXXMethodDecl* MD =
+ const CXXMethodDecl* MD =
cast<CXXMethodDecl>(CMCE->getMethodDecl()->getMostRecentDecl());
if (LockReturnedAttr* At = MD->getAttr<LockReturnedAttr>()) {
CallingContext LRCallCtx(CMCE->getMethodDecl());
@@ -343,14 +346,14 @@ private:
unsigned NumCallArgs = CMCE->getNumArgs();
unsigned Root = makeMCall(NumCallArgs, CMCE->getMethodDecl());
unsigned Sz = buildSExpr(CMCE->getImplicitObjectArgument(), CallCtx);
- Expr** CallArgs = CMCE->getArgs();
+ const Expr* const* CallArgs = CMCE->getArgs();
for (unsigned i = 0; i < NumCallArgs; ++i) {
Sz += buildSExpr(CallArgs[i], CallCtx);
}
NodeVec[Root].setSize(Sz + 1);
return Sz + 1;
- } else if (CallExpr *CE = dyn_cast<CallExpr>(Exp)) {
- FunctionDecl* FD =
+ } else if (const CallExpr *CE = dyn_cast<CallExpr>(Exp)) {
+ const FunctionDecl* FD =
cast<FunctionDecl>(CE->getDirectCallee()->getMostRecentDecl());
if (LockReturnedAttr* At = FD->getAttr<LockReturnedAttr>()) {
CallingContext LRCallCtx(CE->getDirectCallee());
@@ -361,7 +364,7 @@ private:
}
// Treat smart pointers and iterators as pointers;
// ignore the * and -> operators.
- if (CXXOperatorCallExpr *OE = dyn_cast<CXXOperatorCallExpr>(CE)) {
+ if (const CXXOperatorCallExpr *OE = dyn_cast<CXXOperatorCallExpr>(CE)) {
OverloadedOperatorKind k = OE->getOperator();
if (k == OO_Star) {
if (NDeref) ++(*NDeref);
@@ -374,19 +377,19 @@ private:
unsigned NumCallArgs = CE->getNumArgs();
unsigned Root = makeCall(NumCallArgs, 0);
unsigned Sz = buildSExpr(CE->getCallee(), CallCtx);
- Expr** CallArgs = CE->getArgs();
+ const Expr* const* CallArgs = CE->getArgs();
for (unsigned i = 0; i < NumCallArgs; ++i) {
Sz += buildSExpr(CallArgs[i], CallCtx);
}
NodeVec[Root].setSize(Sz+1);
return Sz+1;
- } else if (BinaryOperator *BOE = dyn_cast<BinaryOperator>(Exp)) {
+ } else if (const BinaryOperator *BOE = dyn_cast<BinaryOperator>(Exp)) {
unsigned Root = makeBinary();
unsigned Sz = buildSExpr(BOE->getLHS(), CallCtx);
Sz += buildSExpr(BOE->getRHS(), CallCtx);
NodeVec[Root].setSize(Sz);
return Sz;
- } else if (UnaryOperator *UOE = dyn_cast<UnaryOperator>(Exp)) {
+ } else if (const UnaryOperator *UOE = dyn_cast<UnaryOperator>(Exp)) {
// Ignore & and * operators -- they're no-ops.
// However, we try to figure out whether the expression is a pointer,
// so we can use . and -> appropriately in error messages.
@@ -412,13 +415,14 @@ private:
unsigned Sz = buildSExpr(UOE->getSubExpr(), CallCtx);
NodeVec[Root].setSize(Sz);
return Sz;
- } else if (ArraySubscriptExpr *ASE = dyn_cast<ArraySubscriptExpr>(Exp)) {
+ } else if (const ArraySubscriptExpr *ASE =
+ dyn_cast<ArraySubscriptExpr>(Exp)) {
unsigned Root = makeIndex();
unsigned Sz = buildSExpr(ASE->getBase(), CallCtx);
Sz += buildSExpr(ASE->getIdx(), CallCtx);
NodeVec[Root].setSize(Sz);
return Sz;
- } else if (AbstractConditionalOperator *CE =
+ } else if (const AbstractConditionalOperator *CE =
dyn_cast<AbstractConditionalOperator>(Exp)) {
unsigned Root = makeUnknown(3);
unsigned Sz = buildSExpr(CE->getCond(), CallCtx);
@@ -426,20 +430,20 @@ private:
Sz += buildSExpr(CE->getFalseExpr(), CallCtx);
NodeVec[Root].setSize(Sz);
return Sz;
- } else if (ChooseExpr *CE = dyn_cast<ChooseExpr>(Exp)) {
+ } else if (const ChooseExpr *CE = dyn_cast<ChooseExpr>(Exp)) {
unsigned Root = makeUnknown(3);
unsigned Sz = buildSExpr(CE->getCond(), CallCtx);
Sz += buildSExpr(CE->getLHS(), CallCtx);
Sz += buildSExpr(CE->getRHS(), CallCtx);
NodeVec[Root].setSize(Sz);
return Sz;
- } else if (CastExpr *CE = dyn_cast<CastExpr>(Exp)) {
+ } else if (const CastExpr *CE = dyn_cast<CastExpr>(Exp)) {
return buildSExpr(CE->getSubExpr(), CallCtx, NDeref);
- } else if (ParenExpr *PE = dyn_cast<ParenExpr>(Exp)) {
+ } else if (const ParenExpr *PE = dyn_cast<ParenExpr>(Exp)) {
return buildSExpr(PE->getSubExpr(), CallCtx, NDeref);
- } else if (ExprWithCleanups *EWC = dyn_cast<ExprWithCleanups>(Exp)) {
+ } else if (const ExprWithCleanups *EWC = dyn_cast<ExprWithCleanups>(Exp)) {
return buildSExpr(EWC->getSubExpr(), CallCtx, NDeref);
- } else if (CXXBindTemporaryExpr *E = dyn_cast<CXXBindTemporaryExpr>(Exp)) {
+ } else if (const CXXBindTemporaryExpr *E = dyn_cast<CXXBindTemporaryExpr>(Exp)) {
return buildSExpr(E->getSubExpr(), CallCtx, NDeref);
} else if (isa<CharacterLiteral>(Exp) ||
isa<CXXNullPtrLiteralExpr>(Exp) ||
@@ -463,12 +467,12 @@ private:
/// \param DeclExp An expression involving the Decl on which the attribute
/// occurs.
/// \param D The declaration to which the lock/unlock attribute is attached.
- void buildSExprFromExpr(Expr *MutexExp, Expr *DeclExp, const NamedDecl *D,
- VarDecl *SelfDecl = 0) {
+ void buildSExprFromExpr(const Expr *MutexExp, const Expr *DeclExp,
+ const NamedDecl *D, VarDecl *SelfDecl = 0) {
CallingContext CallCtx(D);
if (MutexExp) {
- if (StringLiteral* SLit = dyn_cast<StringLiteral>(MutexExp)) {
+ if (const StringLiteral* SLit = dyn_cast<StringLiteral>(MutexExp)) {
if (SLit->getString() == StringRef("*"))
// The "*" expr is a universal lock, which essentially turns off
// checks until it is removed from the lockset.
@@ -488,18 +492,21 @@ private:
// Examine DeclExp to find SelfArg and FunArgs, which are used to substitute
// for formal parameters when we call buildMutexID later.
- if (MemberExpr *ME = dyn_cast<MemberExpr>(DeclExp)) {
+ if (const MemberExpr *ME = dyn_cast<MemberExpr>(DeclExp)) {
CallCtx.SelfArg = ME->getBase();
CallCtx.SelfArrow = ME->isArrow();
- } else if (CXXMemberCallExpr *CE = dyn_cast<CXXMemberCallExpr>(DeclExp)) {
+ } else if (const CXXMemberCallExpr *CE =
+ dyn_cast<CXXMemberCallExpr>(DeclExp)) {
CallCtx.SelfArg = CE->getImplicitObjectArgument();
CallCtx.SelfArrow = dyn_cast<MemberExpr>(CE->getCallee())->isArrow();
CallCtx.NumArgs = CE->getNumArgs();
CallCtx.FunArgs = CE->getArgs();
- } else if (CallExpr *CE = dyn_cast<CallExpr>(DeclExp)) {
+ } else if (const CallExpr *CE =
+ dyn_cast<CallExpr>(DeclExp)) {
CallCtx.NumArgs = CE->getNumArgs();
CallCtx.FunArgs = CE->getArgs();
- } else if (CXXConstructExpr *CE = dyn_cast<CXXConstructExpr>(DeclExp)) {
+ } else if (const CXXConstructExpr *CE =
+ dyn_cast<CXXConstructExpr>(DeclExp)) {
CallCtx.SelfArg = 0; // Will be set below
CallCtx.NumArgs = CE->getNumArgs();
CallCtx.FunArgs = CE->getArgs();
@@ -543,7 +550,7 @@ public:
/// occurs.
/// \param D The declaration to which the lock/unlock attribute is attached.
/// Caller must check isValid() after construction.
- SExpr(Expr* MutexExp, Expr *DeclExp, const NamedDecl* D,
+ SExpr(const Expr* MutexExp, const Expr *DeclExp, const NamedDecl* D,
VarDecl *SelfDecl=0) {
buildSExprFromExpr(MutexExp, DeclExp, D, SelfDecl);
}
@@ -566,8 +573,9 @@ public:
}
/// Issue a warning about an invalid lock expression
- static void warnInvalidLock(ThreadSafetyHandler &Handler, Expr* MutexExp,
- Expr *DeclExp, const NamedDecl* D) {
+ static void warnInvalidLock(ThreadSafetyHandler &Handler,
+ const Expr *MutexExp,
+ const Expr *DeclExp, const NamedDecl* D) {
SourceLocation Loc;
if (DeclExp)
Loc = DeclExp->getExprLoc();
@@ -776,7 +784,7 @@ struct LockData {
/// \brief A FactEntry stores a single fact that is known at a particular point
/// in the program execution. Currently, this is information regarding a lock
-/// that is held at that point.
+/// that is held at that point.
struct FactEntry {
SExpr MutID;
LockData LDat;
@@ -789,7 +797,7 @@ struct FactEntry {
typedef unsigned short FactID;
-/// \brief FactManager manages the memory for all facts that are created during
+/// \brief FactManager manages the memory for all facts that are created during
/// the analysis of a single routine.
class FactManager {
private:
@@ -807,9 +815,9 @@ public:
/// \brief A FactSet is the set of facts that are known to be true at a
-/// particular program point. FactSets must be small, because they are
+/// particular program point. FactSets must be small, because they are
/// frequently copied, and are thus implemented as a set of indices into a
-/// table maintained by a FactManager. A typical FactSet only holds 1 or 2
+/// table maintained by a FactManager. A typical FactSet only holds 1 or 2
/// locks, so we can get away with doing a linear search for lookup. Note
/// that a hashtable or map is inappropriate in this case, because lookups
/// may involve partial pattern matches, rather than exact matches.
@@ -1342,8 +1350,8 @@ void LocalVariableMap::traverseCFG(CFG *CFGraph,
BE = CurrBlock->end(); BI != BE; ++BI) {
switch (BI->getKind()) {
case CFGElement::Statement: {
- const CFGStmt *CS = cast<CFGStmt>(&*BI);
- VMapBuilder.Visit(const_cast<Stmt*>(CS->getStmt()));
+ CFGStmt CS = BI->castAs<CFGStmt>();
+ VMapBuilder.Visit(const_cast<Stmt*>(CS.getStmt()));
break;
}
default:
@@ -1389,7 +1397,7 @@ static void findBlockLocations(CFG *CFGraph,
for (CFGBlock::const_reverse_iterator BI = CurrBlock->rbegin(),
BE = CurrBlock->rend(); BI != BE; ++BI) {
// FIXME: Handle other CFGElement kinds.
- if (const CFGStmt *CS = dyn_cast<CFGStmt>(&*BI)) {
+ if (Optional<CFGStmt> CS = BI->getAs<CFGStmt>()) {
CurrBlockInfo->ExitLoc = CS->getStmt()->getLocStart();
break;
}
@@ -1402,7 +1410,7 @@ static void findBlockLocations(CFG *CFGraph,
for (CFGBlock::const_iterator BI = CurrBlock->begin(),
BE = CurrBlock->end(); BI != BE; ++BI) {
// FIXME: Handle other CFGElement kinds.
- if (const CFGStmt *CS = dyn_cast<CFGStmt>(&*BI)) {
+ if (Optional<CFGStmt> CS = BI->getAs<CFGStmt>()) {
CurrBlockInfo->EntryLoc = CS->getStmt()->getLocStart();
break;
}
@@ -1733,14 +1741,15 @@ class BuildLockset : public StmtVisitor<BuildLockset> {
unsigned CtxIndex;
// Helper functions
- const ValueDecl *getValueDecl(Expr *Exp);
+ const ValueDecl *getValueDecl(const Expr *Exp);
- void warnIfMutexNotHeld(const NamedDecl *D, Expr *Exp, AccessKind AK,
+ void warnIfMutexNotHeld(const NamedDecl *D, const Expr *Exp, AccessKind AK,
Expr *MutexExp, ProtectedOperationKind POK);
- void warnIfMutexHeld(const NamedDecl *D, Expr *Exp, Expr *MutexExp);
+ void warnIfMutexHeld(const NamedDecl *D, const Expr *Exp, Expr *MutexExp);
+
+ void checkAccess(const Expr *Exp, AccessKind AK);
+ void checkPtAccess(const Expr *Exp, AccessKind AK);
- void checkAccess(Expr *Exp, AccessKind AK);
- void checkDereference(Expr *Exp, AccessKind AK);
void handleCall(Expr *Exp, const NamedDecl *D, VarDecl *VD = 0);
public:
@@ -1762,7 +1771,10 @@ public:
/// \brief Gets the value decl pointer from DeclRefExprs or MemberExprs
-const ValueDecl *BuildLockset::getValueDecl(Expr *Exp) {
+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();
@@ -1774,7 +1786,7 @@ const ValueDecl *BuildLockset::getValueDecl(Expr *Exp) {
/// \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, Expr *Exp,
+void BuildLockset::warnIfMutexNotHeld(const NamedDecl *D, const Expr *Exp,
AccessKind AK, Expr *MutexExp,
ProtectedOperationKind POK) {
LockKind LK = getLockKindFromAccessKind(AK);
@@ -1813,7 +1825,7 @@ void BuildLockset::warnIfMutexNotHeld(const NamedDecl *D, Expr *Exp,
}
/// \brief Warn if the LSet contains the given lock.
-void BuildLockset::warnIfMutexHeld(const NamedDecl *D, Expr* Exp,
+void BuildLockset::warnIfMutexHeld(const NamedDecl *D, const Expr* Exp,
Expr *MutexExp) {
SExpr Mutex(MutexExp, Exp, D);
if (!Mutex.isValid()) {
@@ -1831,51 +1843,61 @@ void BuildLockset::warnIfMutexHeld(const NamedDecl *D, Expr* Exp,
}
-/// \brief This method identifies variable dereferences and checks pt_guarded_by
-/// and pt_guarded_var annotations. Note that we only check these annotations
-/// at the time a pointer is dereferenced.
-/// FIXME: We need to check for other types of pointer dereferences
-/// (e.g. [], ->) and deal with them here.
-/// \param Exp An expression that has been read or written.
-void BuildLockset::checkDereference(Expr *Exp, AccessKind AK) {
- UnaryOperator *UO = dyn_cast<UnaryOperator>(Exp);
- if (!UO || UO->getOpcode() != clang::UO_Deref)
+/// \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.
+/// Similarly, we check if the access is to an expression that dereferences
+/// a pointer marked with pt_guarded_by.
+void BuildLockset::checkAccess(const Expr *Exp, AccessKind AK) {
+ Exp = Exp->IgnoreParenCasts();
+
+ if (const UnaryOperator *UO = dyn_cast<UnaryOperator>(Exp)) {
+ // For dereferences
+ if (UO->getOpcode() == clang::UO_Deref)
+ checkPtAccess(UO->getSubExpr(), AK);
return;
- Exp = UO->getSubExpr()->IgnoreParenCasts();
+ }
+
+ if (const MemberExpr *ME = dyn_cast<MemberExpr>(Exp)) {
+ if (ME->isArrow())
+ checkPtAccess(ME->getBase(), AK);
+ else
+ checkAccess(ME->getBase(), AK);
+ }
const ValueDecl *D = getValueDecl(Exp);
- if(!D || !D->hasAttrs())
+ if (!D || !D->hasAttrs())
return;
- if (D->getAttr<PtGuardedVarAttr>() && FSet.isEmpty())
- Analyzer->Handler.handleNoMutexHeld(D, POK_VarDereference, AK,
+ if (D->getAttr<GuardedVarAttr>() && FSet.isEmpty())
+ Analyzer->Handler.handleNoMutexHeld(D, POK_VarAccess, AK,
Exp->getExprLoc());
const AttrVec &ArgAttrs = D->getAttrs();
- for(unsigned i = 0, Size = ArgAttrs.size(); i < Size; ++i)
- if (PtGuardedByAttr *PGBAttr = dyn_cast<PtGuardedByAttr>(ArgAttrs[i]))
- warnIfMutexNotHeld(D, Exp, AK, PGBAttr->getArg(), POK_VarDereference);
+ 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);
}
-/// \brief Checks guarded_by and guarded_var attributes.
-/// Whenever we identify an access (read or write) of a DeclRefExpr or
-/// MemberExpr, we need to check whether there are any guarded_by or
-/// guarded_var attributes, and make sure we hold the appropriate mutexes.
-void BuildLockset::checkAccess(Expr *Exp, AccessKind AK) {
+/// \brief Checks pt_guarded_by and pt_guarded_var attributes.
+void BuildLockset::checkPtAccess(const Expr *Exp, AccessKind AK) {
+ Exp = Exp->IgnoreParenCasts();
+
const ValueDecl *D = getValueDecl(Exp);
- if(!D || !D->hasAttrs())
+ if (!D || !D->hasAttrs())
return;
- if (D->getAttr<GuardedVarAttr>() && FSet.isEmpty())
- Analyzer->Handler.handleNoMutexHeld(D, POK_VarAccess, AK,
+ if (D->getAttr<PtGuardedVarAttr>() && FSet.isEmpty())
+ Analyzer->Handler.handleNoMutexHeld(D, POK_VarDereference, 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 (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);
}
+
/// \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,
@@ -2009,9 +2031,7 @@ void BuildLockset::VisitUnaryOperator(UnaryOperator *UO) {
case clang::UO_PostInc:
case clang::UO_PreDec:
case clang::UO_PreInc: {
- Expr *SubExp = UO->getSubExpr()->IgnoreParenCasts();
- checkAccess(SubExp, AK_Written);
- checkDereference(SubExp, AK_Written);
+ checkAccess(UO->getSubExpr(), AK_Written);
break;
}
default:
@@ -2029,9 +2049,7 @@ void BuildLockset::VisitBinaryOperator(BinaryOperator *BO) {
// adjust the context
LVarCtx = Analyzer->LocalVarMap.getNextContext(CtxIndex, BO, LVarCtx);
- Expr *LHSExp = BO->getLHS()->IgnoreParenCasts();
- checkAccess(LHSExp, AK_Written);
- checkDereference(LHSExp, AK_Written);
+ checkAccess(BO->getLHS(), AK_Written);
}
/// Whenever we do an LValue to Rvalue cast, we are reading a variable and
@@ -2040,13 +2058,46 @@ void BuildLockset::VisitBinaryOperator(BinaryOperator *BO) {
void BuildLockset::VisitCastExpr(CastExpr *CE) {
if (CE->getCastKind() != CK_LValueToRValue)
return;
- Expr *SubExp = CE->getSubExpr()->IgnoreParenCasts();
- checkAccess(SubExp, AK_Read);
- checkDereference(SubExp, AK_Read);
+ checkAccess(CE->getSubExpr(), AK_Read);
}
void BuildLockset::VisitCallExpr(CallExpr *Exp) {
+ if (CXXMemberCallExpr *CE = dyn_cast<CXXMemberCallExpr>(Exp)) {
+ MemberExpr *ME = dyn_cast<MemberExpr>(CE->getCallee());
+ // ME can be null when calling a method pointer
+ CXXMethodDecl *MD = CE->getMethodDecl();
+
+ if (ME && MD) {
+ if (ME->isArrow()) {
+ if (MD->isConst()) {
+ checkPtAccess(CE->getImplicitObjectArgument(), AK_Read);
+ } else { // FIXME -- should be AK_Written
+ checkPtAccess(CE->getImplicitObjectArgument(), AK_Read);
+ }
+ } else {
+ if (MD->isConst())
+ checkAccess(CE->getImplicitObjectArgument(), AK_Read);
+ else // FIXME -- should be AK_Written
+ checkAccess(CE->getImplicitObjectArgument(), AK_Read);
+ }
+ }
+ } else if (CXXOperatorCallExpr *OE = dyn_cast<CXXOperatorCallExpr>(Exp)) {
+ switch (OE->getOperator()) {
+ case OO_Equal: {
+ const Expr *Target = OE->getArg(0);
+ const Expr *Source = OE->getArg(1);
+ checkAccess(Target, AK_Written);
+ checkAccess(Source, AK_Read);
+ break;
+ }
+ default: {
+ const Expr *Source = OE->getArg(0);
+ checkAccess(Source, AK_Read);
+ break;
+ }
+ }
+ }
NamedDecl *D = dyn_cast_or_null<NamedDecl>(Exp->getCalleeDecl());
if(!D || !D->hasAttrs())
return;
@@ -2054,6 +2105,11 @@ void BuildLockset::VisitCallExpr(CallExpr *Exp) {
}
void BuildLockset::VisitCXXConstructExpr(CXXConstructExpr *Exp) {
+ const CXXConstructorDecl *D = Exp->getConstructor();
+ if (D && D->isCopyConstructor()) {
+ const Expr* Source = Exp->getArg(0);
+ checkAccess(Source, AK_Read);
+ }
// FIXME -- only handles constructors in DeclStmt below.
}
@@ -2164,6 +2220,21 @@ void ThreadSafetyAnalyzer::intersectAndWarn(FactSet &FSet1,
}
+// Return true if block B never continues to its successors.
+inline bool neverReturns(const CFGBlock* B) {
+ if (B->hasNoReturnElement())
+ return true;
+ if (B->empty())
+ return false;
+
+ CFGElement Last = B->back();
+ if (Optional<CFGStmt> S = Last.getAs<CFGStmt>()) {
+ if (isa<CXXThrowExpr>(S->getStmt()))
+ return true;
+ }
+ return false;
+}
+
/// \brief Check a function's CFG for thread-safety violations.
///
@@ -2281,7 +2352,7 @@ void ThreadSafetyAnalyzer::runAnalysis(AnalysisDeclContext &AC) {
// union because the real error is probably that we forgot to unlock M on
// all code paths.
bool LocksetInitialized = false;
- llvm::SmallVector<CFGBlock*, 8> SpecialBlocks;
+ SmallVector<CFGBlock *, 8> SpecialBlocks;
for (CFGBlock::const_pred_iterator PI = CurrBlock->pred_begin(),
PE = CurrBlock->pred_end(); PI != PE; ++PI) {
@@ -2293,7 +2364,7 @@ void ThreadSafetyAnalyzer::runAnalysis(AnalysisDeclContext &AC) {
CFGBlockInfo *PrevBlockInfo = &BlockInfo[PrevBlockID];
// Ignore edges from blocks that can't return.
- if ((*PI)->hasNoReturnElement() || !PrevBlockInfo->Reachable)
+ if (neverReturns(*PI) || !PrevBlockInfo->Reachable)
continue;
// Okay, we can reach this block from the entry.
@@ -2310,7 +2381,6 @@ void ThreadSafetyAnalyzer::runAnalysis(AnalysisDeclContext &AC) {
}
}
-
FactSet PrevLockset;
getEdgeLockset(PrevLockset, PrevBlockInfo->ExitSet, *PI, CurrBlock);
@@ -2368,22 +2438,22 @@ void ThreadSafetyAnalyzer::runAnalysis(AnalysisDeclContext &AC) {
BE = CurrBlock->end(); BI != BE; ++BI) {
switch (BI->getKind()) {
case CFGElement::Statement: {
- const CFGStmt *CS = cast<CFGStmt>(&*BI);
- LocksetBuilder.Visit(const_cast<Stmt*>(CS->getStmt()));
+ CFGStmt CS = BI->castAs<CFGStmt>();
+ LocksetBuilder.Visit(const_cast<Stmt*>(CS.getStmt()));
break;
}
// Ignore BaseDtor, MemberDtor, and TemporaryDtor for now.
case CFGElement::AutomaticObjectDtor: {
- const CFGAutomaticObjDtor *AD = cast<CFGAutomaticObjDtor>(&*BI);
- CXXDestructorDecl *DD = const_cast<CXXDestructorDecl*>(
- AD->getDestructorDecl(AC.getASTContext()));
+ CFGAutomaticObjDtor AD = BI->castAs<CFGAutomaticObjDtor>();
+ CXXDestructorDecl *DD = const_cast<CXXDestructorDecl *>(
+ AD.getDestructorDecl(AC.getASTContext()));
if (!DD->hasAttrs())
break;
// Create a dummy expression,
- VarDecl *VD = const_cast<VarDecl*>(AD->getVarDecl());
+ VarDecl *VD = const_cast<VarDecl*>(AD.getVarDecl());
DeclRefExpr DRE(VD, false, VD->getType(), VK_LValue,
- AD->getTriggerStmt()->getLocEnd());
+ AD.getTriggerStmt()->getLocEnd());
LocksetBuilder.handleCall(&DRE, DD);
break;
}
diff --git a/lib/Analysis/UninitializedValues.cpp b/lib/Analysis/UninitializedValues.cpp
index b2e27ca..730aa6b 100644
--- a/lib/Analysis/UninitializedValues.cpp
+++ b/lib/Analysis/UninitializedValues.cpp
@@ -11,20 +11,22 @@
//
//===----------------------------------------------------------------------===//
-#include <utility>
-#include "llvm/ADT/Optional.h"
-#include "llvm/ADT/SmallBitVector.h"
-#include "llvm/ADT/SmallVector.h"
-#include "llvm/ADT/PackedVector.h"
-#include "llvm/ADT/DenseMap.h"
#include "clang/AST/ASTContext.h"
+#include "clang/AST/Attr.h"
#include "clang/AST/Decl.h"
-#include "clang/Analysis/CFG.h"
-#include "clang/Analysis/AnalysisContext.h"
-#include "clang/Analysis/Visitors/CFGRecStmtDeclVisitor.h"
+#include "clang/Analysis/Analyses/PostOrderCFGView.h"
#include "clang/Analysis/Analyses/UninitializedValues.h"
+#include "clang/Analysis/AnalysisContext.h"
+#include "clang/Analysis/CFG.h"
#include "clang/Analysis/DomainSpecific/ObjCNoReturn.h"
+#include "clang/Analysis/Visitors/CFGRecStmtDeclVisitor.h"
+#include "llvm/ADT/DenseMap.h"
+#include "llvm/ADT/Optional.h"
+#include "llvm/ADT/PackedVector.h"
+#include "llvm/ADT/SmallBitVector.h"
+#include "llvm/ADT/SmallVector.h"
#include "llvm/Support/SaveAndRestore.h"
+#include <utility>
using namespace clang;
@@ -57,7 +59,7 @@ public:
unsigned size() const { return map.size(); }
/// Returns the bit vector index for a given declaration.
- llvm::Optional<unsigned> getValueIndex(const VarDecl *d) const;
+ Optional<unsigned> getValueIndex(const VarDecl *d) const;
};
}
@@ -72,10 +74,10 @@ void DeclToIndex::computeMap(const DeclContext &dc) {
}
}
-llvm::Optional<unsigned> DeclToIndex::getValueIndex(const VarDecl *d) const {
+Optional<unsigned> DeclToIndex::getValueIndex(const VarDecl *d) const {
llvm::DenseMap<const VarDecl *, unsigned>::const_iterator I = map.find(d);
if (I == map.end())
- return llvm::Optional<unsigned>();
+ return None;
return I->second;
}
@@ -130,7 +132,7 @@ public:
Value getValue(const CFGBlock *block, const CFGBlock *dstBlock,
const VarDecl *vd) {
- const llvm::Optional<unsigned> &idx = declToIndex.getValueIndex(vd);
+ const Optional<unsigned> &idx = declToIndex.getValueIndex(vd);
assert(idx.hasValue());
return getValueVector(block)[idx.getValue()];
}
@@ -191,7 +193,7 @@ void CFGBlockValues::resetScratch() {
}
ValueVector::reference CFGBlockValues::operator[](const VarDecl *vd) {
- const llvm::Optional<unsigned> &idx = declToIndex.getValueIndex(vd);
+ const Optional<unsigned> &idx = declToIndex.getValueIndex(vd);
assert(idx.hasValue());
return scratch[idx.getValue()];
}
@@ -202,10 +204,20 @@ ValueVector::reference CFGBlockValues::operator[](const VarDecl *vd) {
namespace {
class DataflowWorklist {
+ PostOrderCFGView::iterator PO_I, PO_E;
SmallVector<const CFGBlock *, 20> worklist;
llvm::BitVector enqueuedBlocks;
public:
- DataflowWorklist(const CFG &cfg) : enqueuedBlocks(cfg.getNumBlockIDs()) {}
+ DataflowWorklist(const CFG &cfg, PostOrderCFGView &view)
+ : PO_I(view.begin()), PO_E(view.end()),
+ enqueuedBlocks(cfg.getNumBlockIDs(), true) {
+ // Treat the first block as already analyzed.
+ if (PO_I != PO_E) {
+ assert(*PO_I == &cfg.getEntry());
+ enqueuedBlocks[(*PO_I)->getBlockID()] = false;
+ ++PO_I;
+ }
+ }
void enqueueSuccessors(const CFGBlock *block);
const CFGBlock *dequeue();
@@ -213,7 +225,6 @@ public:
}
void DataflowWorklist::enqueueSuccessors(const clang::CFGBlock *block) {
- unsigned OldWorklistSize = worklist.size();
for (CFGBlock::const_succ_iterator I = block->succ_begin(),
E = block->succ_end(); I != E; ++I) {
const CFGBlock *Successor = *I;
@@ -222,22 +233,30 @@ void DataflowWorklist::enqueueSuccessors(const clang::CFGBlock *block) {
worklist.push_back(Successor);
enqueuedBlocks[Successor->getBlockID()] = true;
}
- if (OldWorklistSize == 0 || OldWorklistSize == worklist.size())
- return;
-
- // Rotate the newly added blocks to the start of the worklist so that it forms
- // a proper queue when we pop off the end of the worklist.
- std::rotate(worklist.begin(), worklist.begin() + OldWorklistSize,
- worklist.end());
}
const CFGBlock *DataflowWorklist::dequeue() {
- if (worklist.empty())
+ const CFGBlock *B = 0;
+
+ // First dequeue from the worklist. This can represent
+ // updates along backedges that we want propagated as quickly as possible.
+ if (!worklist.empty()) {
+ B = worklist.back();
+ worklist.pop_back();
+ }
+ // Next dequeue from the initial reverse post order. This is the
+ // theoretical ideal in the presence of no back edges.
+ else if (PO_I != PO_E) {
+ B = *PO_I;
+ ++PO_I;
+ }
+ else {
return 0;
- const CFGBlock *b = worklist.back();
- worklist.pop_back();
- enqueuedBlocks[b->getBlockID()] = false;
- return b;
+ }
+
+ assert(enqueuedBlocks[B->getBlockID()] == true);
+ enqueuedBlocks[B->getBlockID()] = false;
+ return B;
}
//------------------------------------------------------------------------====//
@@ -339,6 +358,16 @@ static const DeclRefExpr *getSelfInitExpr(VarDecl *VD) {
}
void ClassifyRefs::classify(const Expr *E, Class C) {
+ // The result of a ?: could also be an lvalue.
+ E = E->IgnoreParens();
+ if (const ConditionalOperator *CO = dyn_cast<ConditionalOperator>(E)) {
+ const Expr *TrueExpr = CO->getTrueExpr();
+ if (!isa<OpaqueValueExpr>(TrueExpr))
+ classify(TrueExpr, C);
+ classify(CO->getFalseExpr(), C);
+ return;
+ }
+
FindVarResult Var = findVar(E, DC);
if (const DeclRefExpr *DRE = Var.getDeclRefExpr())
Classification[DRE] = std::max(Classification[DRE], C);
@@ -408,13 +437,13 @@ class TransferFunctions : public StmtVisitor<TransferFunctions> {
AnalysisDeclContext &ac;
const ClassifyRefs &classification;
ObjCNoReturn objCNoRet;
- UninitVariablesHandler *handler;
+ UninitVariablesHandler &handler;
public:
TransferFunctions(CFGBlockValues &vals, const CFG &cfg,
const CFGBlock *block, AnalysisDeclContext &ac,
const ClassifyRefs &classification,
- UninitVariablesHandler *handler)
+ UninitVariablesHandler &handler)
: vals(vals), cfg(cfg), block(block), ac(ac),
classification(classification), objCNoRet(ac.getASTContext()),
handler(handler) {}
@@ -490,8 +519,8 @@ public:
// 'n' is definitely uninitialized for two edges into block 7 (from blocks 2
// and 4), so we report that any time either of those edges is taken (in
// each case when 'b == false'), 'n' is used uninitialized.
- llvm::SmallVector<const CFGBlock*, 32> Queue;
- llvm::SmallVector<unsigned, 32> SuccsVisited(cfg.getNumBlockIDs(), 0);
+ SmallVector<const CFGBlock*, 32> Queue;
+ SmallVector<unsigned, 32> SuccsVisited(cfg.getNumBlockIDs(), 0);
Queue.push_back(block);
// Specify that we've already visited all successors of the starting block.
// This has the dual purpose of ensuring we never add it to the queue, and
@@ -571,11 +600,9 @@ public:
}
void TransferFunctions::reportUse(const Expr *ex, const VarDecl *vd) {
- if (!handler)
- return;
Value v = vals[vd];
if (isUninitialized(v))
- handler->handleUseOfUninitVariable(vd, getUninitUse(ex, vd, v));
+ handler.handleUseOfUninitVariable(vd, getUninitUse(ex, vd, v));
}
void TransferFunctions::VisitObjCForCollectionStmt(ObjCForCollectionStmt *FS) {
@@ -636,8 +663,7 @@ void TransferFunctions::VisitDeclRefExpr(DeclRefExpr *dr) {
vals[cast<VarDecl>(dr->getDecl())] = Initialized;
break;
case ClassifyRefs::SelfInit:
- if (handler)
- handler->handleSelfInit(cast<VarDecl>(dr->getDecl()));
+ handler.handleSelfInit(cast<VarDecl>(dr->getDecl()));
break;
}
}
@@ -703,7 +729,7 @@ static bool runOnBlock(const CFGBlock *block, const CFG &cfg,
AnalysisDeclContext &ac, CFGBlockValues &vals,
const ClassifyRefs &classification,
llvm::BitVector &wasAnalyzed,
- UninitVariablesHandler *handler = 0) {
+ UninitVariablesHandler &handler) {
wasAnalyzed[block->getBlockID()] = true;
vals.resetScratch();
// Merge in values of predecessor blocks.
@@ -720,13 +746,49 @@ static bool runOnBlock(const CFGBlock *block, const CFG &cfg,
TransferFunctions tf(vals, cfg, block, ac, classification, handler);
for (CFGBlock::const_iterator I = block->begin(), E = block->end();
I != E; ++I) {
- if (const CFGStmt *cs = dyn_cast<CFGStmt>(&*I)) {
+ if (Optional<CFGStmt> cs = I->getAs<CFGStmt>())
tf.Visit(const_cast<Stmt*>(cs->getStmt()));
- }
}
return vals.updateValueVectorWithScratch(block);
}
+/// PruneBlocksHandler is a special UninitVariablesHandler that is used
+/// to detect when a CFGBlock has any *potential* use of an uninitialized
+/// variable. It is mainly used to prune out work during the final
+/// reporting pass.
+namespace {
+struct PruneBlocksHandler : public UninitVariablesHandler {
+ PruneBlocksHandler(unsigned numBlocks)
+ : hadUse(numBlocks, false), hadAnyUse(false),
+ currentBlock(0) {}
+
+ virtual ~PruneBlocksHandler() {}
+
+ /// Records if a CFGBlock had a potential use of an uninitialized variable.
+ llvm::BitVector hadUse;
+
+ /// Records if any CFGBlock had a potential use of an uninitialized variable.
+ bool hadAnyUse;
+
+ /// The current block to scribble use information.
+ unsigned currentBlock;
+
+ virtual void handleUseOfUninitVariable(const VarDecl *vd,
+ const UninitUse &use) {
+ hadUse[currentBlock] = true;
+ hadAnyUse = true;
+ }
+
+ /// 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) {
+ hadUse[currentBlock] = true;
+ hadAnyUse = true;
+ }
+};
+}
+
void clang::runUninitializedVariablesAnalysis(
const DeclContext &dc,
const CFG &cfg,
@@ -753,27 +815,33 @@ void clang::runUninitializedVariablesAnalysis(
}
// Proceed with the workist.
- DataflowWorklist worklist(cfg);
+ DataflowWorklist worklist(cfg, *ac.getAnalysis<PostOrderCFGView>());
llvm::BitVector previouslyVisited(cfg.getNumBlockIDs());
worklist.enqueueSuccessors(&cfg.getEntry());
llvm::BitVector wasAnalyzed(cfg.getNumBlockIDs(), false);
wasAnalyzed[cfg.getEntry().getBlockID()] = true;
+ PruneBlocksHandler PBH(cfg.getNumBlockIDs());
while (const CFGBlock *block = worklist.dequeue()) {
+ PBH.currentBlock = block->getBlockID();
+
// Did the block change?
bool changed = runOnBlock(block, cfg, ac, vals,
- classification, wasAnalyzed);
+ classification, wasAnalyzed, PBH);
++stats.NumBlockVisits;
if (changed || !previouslyVisited[block->getBlockID()])
worklist.enqueueSuccessors(block);
previouslyVisited[block->getBlockID()] = true;
}
-
- // Run through the blocks one more time, and report uninitialized variabes.
+
+ if (!PBH.hadAnyUse)
+ return;
+
+ // Run through the blocks one more time, and report uninitialized variables.
for (CFG::const_iterator BI = cfg.begin(), BE = cfg.end(); BI != BE; ++BI) {
const CFGBlock *block = *BI;
- if (wasAnalyzed[block->getBlockID()]) {
- runOnBlock(block, cfg, ac, vals, classification, wasAnalyzed, &handler);
+ if (PBH.hadUse[block->getBlockID()]) {
+ runOnBlock(block, cfg, ac, vals, classification, wasAnalyzed, handler);
++stats.NumBlockVisits;
}
}
OpenPOWER on IntegriCloud