diff options
Diffstat (limited to 'lib/Analysis')
-rw-r--r-- | lib/Analysis/AnalysisDeclContext.cpp | 68 | ||||
-rw-r--r-- | lib/Analysis/BodyFarm.cpp | 18 | ||||
-rw-r--r-- | lib/Analysis/BodyFarm.h | 5 | ||||
-rw-r--r-- | lib/Analysis/CFG.cpp | 166 | ||||
-rw-r--r-- | lib/Analysis/CFGStmtMap.cpp | 2 | ||||
-rw-r--r-- | lib/Analysis/CallGraph.cpp | 112 | ||||
-rw-r--r-- | lib/Analysis/CocoaConventions.cpp | 8 | ||||
-rw-r--r-- | lib/Analysis/FormatString.cpp | 11 | ||||
-rw-r--r-- | lib/Analysis/FormatStringParsing.h | 2 | ||||
-rw-r--r-- | lib/Analysis/LiveVariables.cpp | 38 | ||||
-rw-r--r-- | lib/Analysis/PrintfFormatString.cpp | 34 | ||||
-rw-r--r-- | lib/Analysis/ReachableCode.cpp | 14 | ||||
-rw-r--r-- | lib/Analysis/ScanfFormatString.cpp | 4 | ||||
-rw-r--r-- | lib/Analysis/ThreadSafety.cpp | 290 | ||||
-rw-r--r-- | lib/Analysis/UninitializedValues.cpp | 160 |
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 ∾ 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; } } |