diff options
Diffstat (limited to 'lib/Analysis')
| -rw-r--r-- | lib/Analysis/AnalysisDeclContext.cpp | 19 | ||||
| -rw-r--r-- | lib/Analysis/BodyFarm.cpp | 60 | ||||
| -rw-r--r-- | lib/Analysis/CFG.cpp | 83 | ||||
| -rw-r--r-- | lib/Analysis/CMakeLists.txt | 7 | ||||
| -rw-r--r-- | lib/Analysis/CallGraph.cpp | 2 | ||||
| -rw-r--r-- | lib/Analysis/CloneDetection.cpp | 19 | ||||
| -rw-r--r-- | lib/Analysis/Consumed.cpp | 26 | ||||
| -rw-r--r-- | lib/Analysis/ExprMutationAnalyzer.cpp | 445 | ||||
| -rw-r--r-- | lib/Analysis/FormatString.cpp | 953 | ||||
| -rw-r--r-- | lib/Analysis/FormatStringParsing.h | 79 | ||||
| -rw-r--r-- | lib/Analysis/LiveVariables.cpp | 61 | ||||
| -rw-r--r-- | lib/Analysis/OSLog.cpp | 203 | ||||
| -rw-r--r-- | lib/Analysis/PrintfFormatString.cpp | 1029 | ||||
| -rw-r--r-- | lib/Analysis/ProgramPoint.cpp | 175 | ||||
| -rw-r--r-- | lib/Analysis/PseudoConstantAnalysis.cpp | 226 | ||||
| -rw-r--r-- | lib/Analysis/ReachableCode.cpp | 22 | ||||
| -rw-r--r-- | lib/Analysis/ScanfFormatString.cpp | 563 | ||||
| -rw-r--r-- | lib/Analysis/ThreadSafety.cpp | 408 | ||||
| -rw-r--r-- | lib/Analysis/ThreadSafetyCommon.cpp | 59 | ||||
| -rw-r--r-- | lib/Analysis/ThreadSafetyTIL.cpp | 18 |
20 files changed, 1103 insertions, 3354 deletions
diff --git a/lib/Analysis/AnalysisDeclContext.cpp b/lib/Analysis/AnalysisDeclContext.cpp index 9557f68452ff..30160bc239ae 100644 --- a/lib/Analysis/AnalysisDeclContext.cpp +++ b/lib/Analysis/AnalysisDeclContext.cpp @@ -27,7 +27,6 @@ #include "clang/AST/StmtCXX.h" #include "clang/AST/StmtVisitor.h" #include "clang/Analysis/Analyses/CFGReachabilityAnalysis.h" -#include "clang/Analysis/Analyses/PseudoConstantAnalysis.h" #include "clang/Analysis/BodyFarm.h" #include "clang/Analysis/CFG.h" #include "clang/Analysis/CFGStmtMap.h" @@ -137,7 +136,7 @@ bool AnalysisDeclContext::isBodyAutosynthesized() const { bool AnalysisDeclContext::isBodyAutosynthesizedFromModelFile() const { bool Tmp; Stmt *Body = getBody(Tmp); - return Tmp && Body->getLocStart().isValid(); + return Tmp && Body->getBeginLoc().isValid(); } /// Returns true if \param VD is an Objective-C implicit 'self' parameter. @@ -292,12 +291,6 @@ ParentMap &AnalysisDeclContext::getParentMap() { return *PM; } -PseudoConstantAnalysis *AnalysisDeclContext::getPseudoConstantAnalysis() { - if (!PCA) - PCA.reset(new PseudoConstantAnalysis(getBody())); - return PCA.get(); -} - AnalysisDeclContext *AnalysisDeclContextManager::getContext(const Decl *D) { if (const auto *FD = dyn_cast<FunctionDecl>(D)) { // Calling 'hasBody' replaces 'FD' in place with the FunctionDecl @@ -392,7 +385,7 @@ LocationContextManager::getLocationContext(AnalysisDeclContext *ctx, LOC *L = cast_or_null<LOC>(Contexts.FindNodeOrInsertPos(ID, InsertPos)); if (!L) { - L = new LOC(ctx, parent, d); + L = new LOC(ctx, parent, d, ++NewID); Contexts.InsertNode(L, InsertPos); } return L; @@ -409,7 +402,7 @@ LocationContextManager::getStackFrame(AnalysisDeclContext *ctx, auto *L = cast_or_null<StackFrameContext>(Contexts.FindNodeOrInsertPos(ID, InsertPos)); if (!L) { - L = new StackFrameContext(ctx, parent, s, blk, idx); + L = new StackFrameContext(ctx, parent, s, blk, idx, ++NewID); Contexts.InsertNode(L, InsertPos); } return L; @@ -434,7 +427,7 @@ LocationContextManager::getBlockInvocationContext(AnalysisDeclContext *ctx, cast_or_null<BlockInvocationContext>(Contexts.FindNodeOrInsertPos(ID, InsertPos)); if (!L) { - L = new BlockInvocationContext(ctx, parent, BD, ContextData); + L = new BlockInvocationContext(ctx, parent, BD, ContextData, ++NewID); Contexts.InsertNode(L, InsertPos); } return L; @@ -500,7 +493,7 @@ void LocationContext::dumpStack( OS << "Calling anonymous code"; if (const Stmt *S = cast<StackFrameContext>(LCtx)->getCallSite()) { OS << " at "; - printLocation(OS, SM, S->getLocStart()); + printLocation(OS, SM, S->getBeginLoc()); } break; case Scope: @@ -510,7 +503,7 @@ void LocationContext::dumpStack( OS << "Invoking block"; if (const Decl *D = cast<BlockInvocationContext>(LCtx)->getDecl()) { OS << " defined at "; - printLocation(OS, SM, D->getLocStart()); + printLocation(OS, SM, D->getBeginLoc()); } break; } diff --git a/lib/Analysis/BodyFarm.cpp b/lib/Analysis/BodyFarm.cpp index ac8fcdc912a0..35f046406763 100644 --- a/lib/Analysis/BodyFarm.cpp +++ b/lib/Analysis/BodyFarm.cpp @@ -201,10 +201,9 @@ ObjCIvarRefExpr *ASTMaker::makeObjCIvarRef(const Expr *Base, /*arrow=*/true, /*free=*/false); } - ReturnStmt *ASTMaker::makeReturn(const Expr *RetVal) { - return new (C) ReturnStmt(SourceLocation(), const_cast<Expr*>(RetVal), - nullptr); + return ReturnStmt::Create(C, SourceLocation(), const_cast<Expr *>(RetVal), + /* NRVOCandidate=*/nullptr); } IntegerLiteral *ASTMaker::makeIntegerLiteral(uint64_t Value, QualType Ty) { @@ -270,8 +269,8 @@ static CallExpr *create_call_once_funcptr_call(ASTContext &C, ASTMaker M, llvm_unreachable("Unexpected state"); } - return new (C) - CallExpr(C, SubExpr, CallArgs, C.VoidTy, VK_RValue, SourceLocation()); + return CallExpr::Create(C, SubExpr, CallArgs, C.VoidTy, VK_RValue, + SourceLocation()); } static CallExpr *create_call_once_lambda_call(ASTContext &C, ASTMaker M, @@ -293,12 +292,12 @@ static CallExpr *create_call_once_lambda_call(ASTContext &C, ASTMaker M, /* T =*/ callOperatorDecl->getType(), /* VK =*/ VK_LValue); - return new (C) - CXXOperatorCallExpr(/*AstContext=*/C, OO_Call, callOperatorDeclRef, - /*args=*/CallArgs, - /*QualType=*/C.VoidTy, - /*ExprValueType=*/VK_RValue, - /*SourceLocation=*/SourceLocation(), FPOptions()); + return CXXOperatorCallExpr::Create( + /*AstContext=*/C, OO_Call, callOperatorDeclRef, + /*args=*/CallArgs, + /*QualType=*/C.VoidTy, + /*ExprValueType=*/VK_RValue, + /*SourceLocation=*/SourceLocation(), FPOptions()); } /// Create a fake body for std::call_once. @@ -464,13 +463,13 @@ static Stmt *create_call_once(ASTContext &C, const FunctionDecl *D) { Deref, M.makeIntegralCast(M.makeIntegerLiteral(1, C.IntTy), DerefType), DerefType); - IfStmt *Out = new (C) - IfStmt(C, SourceLocation(), - /* IsConstexpr=*/ false, - /* init=*/ nullptr, - /* var=*/ nullptr, - /* cond=*/ FlagCheck, - /* then=*/ M.makeCompound({CallbackCall, FlagAssignment})); + auto *Out = + IfStmt::Create(C, SourceLocation(), + /* IsConstexpr=*/false, + /* init=*/nullptr, + /* var=*/nullptr, + /* cond=*/FlagCheck, + /* then=*/M.makeCompound({CallbackCall, FlagAssignment})); return Out; } @@ -510,7 +509,7 @@ static Stmt *create_dispatch_once(ASTContext &C, const FunctionDecl *D) { ASTMaker M(C); // (1) Create the call. - CallExpr *CE = new (C) CallExpr( + CallExpr *CE = CallExpr::Create( /*ASTContext=*/C, /*StmtClass=*/M.makeLvalueToRvalue(/*Expr=*/Block), /*args=*/None, @@ -549,12 +548,12 @@ static Stmt *create_dispatch_once(ASTContext &C, const FunctionDecl *D) { Expr *GuardCondition = M.makeComparison(LValToRval, DoneValue, BO_NE); // (5) Create the 'if' statement. - IfStmt *If = new (C) IfStmt(C, SourceLocation(), - /* IsConstexpr=*/ false, - /* init=*/ nullptr, - /* var=*/ nullptr, - /* cond=*/ GuardCondition, - /* then=*/ CS); + auto *If = IfStmt::Create(C, SourceLocation(), + /* IsConstexpr=*/false, + /* init=*/nullptr, + /* var=*/nullptr, + /* cond=*/GuardCondition, + /* then=*/CS); return If; } @@ -580,8 +579,8 @@ static Stmt *create_dispatch_sync(ASTContext &C, const FunctionDecl *D) { ASTMaker M(C); DeclRefExpr *DR = M.makeDeclRefExpr(PV); ImplicitCastExpr *ICE = M.makeLvalueToRvalue(DR, Ty); - CallExpr *CE = new (C) CallExpr(C, ICE, None, C.VoidTy, VK_RValue, - SourceLocation()); + CallExpr *CE = + CallExpr::Create(C, ICE, None, C.VoidTy, VK_RValue, SourceLocation()); return CE; } @@ -657,8 +656,11 @@ static Stmt *create_OSAtomicCompareAndSwap(ASTContext &C, const FunctionDecl *D) Stmt *Else = M.makeReturn(RetVal); /// Construct the If. - Stmt *If = new (C) IfStmt(C, SourceLocation(), false, nullptr, nullptr, - Comparison, Body, SourceLocation(), Else); + auto *If = IfStmt::Create(C, SourceLocation(), + /* IsConstexpr=*/false, + /* init=*/nullptr, + /* var=*/nullptr, Comparison, Body, + SourceLocation(), Else); return If; } diff --git a/lib/Analysis/CFG.cpp b/lib/Analysis/CFG.cpp index 97829de7ace3..96130c25be8a 100644 --- a/lib/Analysis/CFG.cpp +++ b/lib/Analysis/CFG.cpp @@ -551,6 +551,7 @@ private: CFGBlock *VisitGotoStmt(GotoStmt *G); CFGBlock *VisitIfStmt(IfStmt *I); CFGBlock *VisitImplicitCastExpr(ImplicitCastExpr *E, AddStmtChoice asc); + CFGBlock *VisitConstantExpr(ConstantExpr *E, AddStmtChoice asc); CFGBlock *VisitIndirectGotoStmt(IndirectGotoStmt *I); CFGBlock *VisitLabelStmt(LabelStmt *L); CFGBlock *VisitBlockExpr(BlockExpr *E, AddStmtChoice asc); @@ -571,7 +572,7 @@ private: CFGBlock *VisitObjCForCollectionStmt(ObjCForCollectionStmt *S); CFGBlock *VisitObjCMessageExpr(ObjCMessageExpr *E, AddStmtChoice asc); CFGBlock *VisitPseudoObjectExpr(PseudoObjectExpr *E); - CFGBlock *VisitReturnStmt(ReturnStmt *R); + CFGBlock *VisitReturnStmt(Stmt *S); CFGBlock *VisitSEHExceptStmt(SEHExceptStmt *S); CFGBlock *VisitSEHFinallyStmt(SEHFinallyStmt *S); CFGBlock *VisitSEHLeaveStmt(SEHLeaveStmt *S); @@ -1038,12 +1039,14 @@ private: if (!areExprTypesCompatible(Expr1, Expr2)) return {}; - llvm::APSInt L1, L2; - - if (!Expr1->EvaluateAsInt(L1, *Context) || - !Expr2->EvaluateAsInt(L2, *Context)) + Expr::EvalResult L1Result, L2Result; + if (!Expr1->EvaluateAsInt(L1Result, *Context) || + !Expr2->EvaluateAsInt(L2Result, *Context)) return {}; + llvm::APSInt L1 = L1Result.Val.getInt(); + llvm::APSInt L2 = L2Result.Val.getInt(); + // Can't compare signed with unsigned or with different bit width. if (L1.isSigned() != L2.isSigned() || L1.getBitWidth() != L2.getBitWidth()) return {}; @@ -1133,13 +1136,16 @@ private: case BO_And: { // If either operand is zero, we know the value // must be false. - llvm::APSInt IntVal; - if (Bop->getLHS()->EvaluateAsInt(IntVal, *Context)) { + Expr::EvalResult LHSResult; + if (Bop->getLHS()->EvaluateAsInt(LHSResult, *Context)) { + llvm::APSInt IntVal = LHSResult.Val.getInt(); if (!IntVal.getBoolValue()) { return TryResult(false); } } - if (Bop->getRHS()->EvaluateAsInt(IntVal, *Context)) { + Expr::EvalResult RHSResult; + if (Bop->getRHS()->EvaluateAsInt(RHSResult, *Context)) { + llvm::APSInt IntVal = RHSResult.Val.getInt(); if (!IntVal.getBoolValue()) { return TryResult(false); } @@ -1334,6 +1340,7 @@ void CFGBuilder::findConstructionContexts( case CK_NoOp: case CK_ConstructorConversion: findConstructionContexts(Layer, Cast->getSubExpr()); + break; default: break; } @@ -2099,6 +2106,9 @@ CFGBlock *CFGBuilder::Visit(Stmt * S, AddStmtChoice asc) { case Stmt::ImplicitCastExprClass: return VisitImplicitCastExpr(cast<ImplicitCastExpr>(S), asc); + case Stmt::ConstantExprClass: + return VisitConstantExpr(cast<ConstantExpr>(S), asc); + case Stmt::IndirectGotoStmtClass: return VisitIndirectGotoStmt(cast<IndirectGotoStmt>(S)); @@ -2146,7 +2156,8 @@ CFGBlock *CFGBuilder::Visit(Stmt * S, AddStmtChoice asc) { return VisitPseudoObjectExpr(cast<PseudoObjectExpr>(S)); case Stmt::ReturnStmtClass: - return VisitReturnStmt(cast<ReturnStmt>(S)); + case Stmt::CoreturnStmtClass: + return VisitReturnStmt(S); case Stmt::SEHExceptStmtClass: return VisitSEHExceptStmt(cast<SEHExceptStmt>(S)); @@ -2421,8 +2432,6 @@ CFGBlock *CFGBuilder::VisitCallExpr(CallExpr *C, AddStmtChoice asc) { if (!boundType.isNull()) calleeType = boundType; } - findConstructionContextsForArguments(C); - // If this is a call to a no-return function, this stops the block here. bool NoReturn = getFunctionExtInfo(*calleeType).getNoReturn(); @@ -2439,6 +2448,13 @@ CFGBlock *CFGBuilder::VisitCallExpr(CallExpr *C, AddStmtChoice asc) { bool OmitArguments = false; if (FunctionDecl *FD = C->getDirectCallee()) { + // TODO: Support construction contexts for variadic function arguments. + // These are a bit problematic and not very useful because passing + // C++ objects as C-style variadic arguments doesn't work in general + // (see [expr.call]). + if (!FD->isVariadic()) + findConstructionContextsForArguments(C); + if (FD->isNoReturn() || C->isBuiltinAssumeFalse(*Context)) NoReturn = true; if (FD->hasAttr<NoThrowAttr>()) @@ -2627,15 +2643,12 @@ CFGBlock *CFGBuilder::VisitDeclStmt(DeclStmt *DS) { for (DeclStmt::reverse_decl_iterator I = DS->decl_rbegin(), E = DS->decl_rend(); I != E; ++I) { - // Get the alignment of the new DeclStmt, padding out to >=8 bytes. - unsigned A = alignof(DeclStmt) < 8 ? 8 : alignof(DeclStmt); // Allocate the DeclStmt using the BumpPtrAllocator. It will get // automatically freed with the CFG. DeclGroupRef DG(*I); Decl *D = *I; - void *Mem = cfg->getAllocator().Allocate(sizeof(DeclStmt), A); - DeclStmt *DSNew = new (Mem) DeclStmt(DG, D->getLocation(), GetEndLoc(D)); + DeclStmt *DSNew = new (Context) DeclStmt(DG, D->getLocation(), GetEndLoc(D)); cfg->addSyntheticDeclStmt(DSNew, DS); // Append the fake DeclStmt to block. @@ -2874,22 +2887,24 @@ CFGBlock *CFGBuilder::VisitIfStmt(IfStmt *I) { return LastBlock; } -CFGBlock *CFGBuilder::VisitReturnStmt(ReturnStmt *R) { +CFGBlock *CFGBuilder::VisitReturnStmt(Stmt *S) { // If we were in the middle of a block we stop processing that block. // - // NOTE: If a "return" appears in the middle of a block, this means that the - // code afterwards is DEAD (unreachable). We still keep a basic block - // for that code; a simple "mark-and-sweep" from the entry block will be - // able to report such dead blocks. + // NOTE: If a "return" or "co_return" appears in the middle of a block, this + // means that the code afterwards is DEAD (unreachable). We still keep + // a basic block for that code; a simple "mark-and-sweep" from the entry + // block will be able to report such dead blocks. + assert(isa<ReturnStmt>(S) || isa<CoreturnStmt>(S)); // Create the new block. Block = createBlock(false); - addAutomaticObjHandling(ScopePos, LocalScope::const_iterator(), R); + addAutomaticObjHandling(ScopePos, LocalScope::const_iterator(), S); - findConstructionContexts( - ConstructionContextLayer::create(cfg->getBumpVectorContext(), R), - R->getRetValue()); + if (auto *R = dyn_cast<ReturnStmt>(S)) + findConstructionContexts( + ConstructionContextLayer::create(cfg->getBumpVectorContext(), R), + R->getRetValue()); // If the one of the destructors does not return, we already have the Exit // block as a successor. @@ -2898,7 +2913,7 @@ CFGBlock *CFGBuilder::VisitReturnStmt(ReturnStmt *R) { // Add the return statement to the block. This may create new blocks if R // contains control-flow (short-circuit operations). - return VisitStmt(R, AddStmtChoice::AlwaysAdd); + return VisitStmt(S, AddStmtChoice::AlwaysAdd); } CFGBlock *CFGBuilder::VisitSEHExceptStmt(SEHExceptStmt *ES) { @@ -4250,7 +4265,10 @@ CFGBlock *CFGBuilder::VisitCXXForRangeStmt(CXXForRangeStmt *S) { Block = createBlock(); addStmt(S->getBeginStmt()); addStmt(S->getEndStmt()); - return addStmt(S->getRangeStmt()); + CFGBlock *Head = addStmt(S->getRangeStmt()); + if (S->getInit()) + Head = addStmt(S->getInit()); + return Head; } CFGBlock *CFGBuilder::VisitExprWithCleanups(ExprWithCleanups *E, @@ -4352,6 +4370,11 @@ CFGBlock *CFGBuilder::VisitCXXFunctionalCastExpr(CXXFunctionalCastExpr *E, CFGBlock *CFGBuilder::VisitCXXTemporaryObjectExpr(CXXTemporaryObjectExpr *C, AddStmtChoice asc) { + // If the constructor takes objects as arguments by value, we need to properly + // construct these objects. Construction contexts we find here aren't for the + // constructor C, they're for its arguments only. + findConstructionContextsForArguments(C); + autoCreateBlock(); appendConstructor(Block, C); return VisitChildren(C); @@ -4366,6 +4389,10 @@ CFGBlock *CFGBuilder::VisitImplicitCastExpr(ImplicitCastExpr *E, return Visit(E->getSubExpr(), AddStmtChoice()); } +CFGBlock *CFGBuilder::VisitConstantExpr(ConstantExpr *E, AddStmtChoice asc) { + return Visit(E->getSubExpr(), AddStmtChoice()); +} + CFGBlock *CFGBuilder::VisitIndirectGotoStmt(IndirectGotoStmt *I) { // Lazily create the indirect-goto dispatch block if there isn't one already. CFGBlock *IBlock = cfg->getIndirectGotoBlock(); @@ -4422,6 +4449,10 @@ tryAgain: E = cast<CXXFunctionalCastExpr>(E)->getSubExpr(); goto tryAgain; + case Stmt::ConstantExprClass: + E = cast<ConstantExpr>(E)->getSubExpr(); + goto tryAgain; + case Stmt::ParenExprClass: E = cast<ParenExpr>(E)->getSubExpr(); goto tryAgain; diff --git a/lib/Analysis/CMakeLists.txt b/lib/Analysis/CMakeLists.txt index 432067d98157..5345a56f2002 100644 --- a/lib/Analysis/CMakeLists.txt +++ b/lib/Analysis/CMakeLists.txt @@ -15,16 +15,12 @@ add_clang_library(clangAnalysis Consumed.cpp CodeInjector.cpp Dominators.cpp - FormatString.cpp + ExprMutationAnalyzer.cpp LiveVariables.cpp - OSLog.cpp ObjCNoReturn.cpp PostOrderCFGView.cpp - PrintfFormatString.cpp ProgramPoint.cpp - PseudoConstantAnalysis.cpp ReachableCode.cpp - ScanfFormatString.cpp ThreadSafety.cpp ThreadSafetyCommon.cpp ThreadSafetyLogical.cpp @@ -33,6 +29,7 @@ add_clang_library(clangAnalysis LINK_LIBS clangAST + clangASTMatchers clangBasic clangLex ) diff --git a/lib/Analysis/CallGraph.cpp b/lib/Analysis/CallGraph.cpp index bac00680ffda..66a6f1a9bcea 100644 --- a/lib/Analysis/CallGraph.cpp +++ b/lib/Analysis/CallGraph.cpp @@ -212,7 +212,7 @@ void CallGraph::viewGraph() const { void CallGraphNode::print(raw_ostream &os) const { if (const NamedDecl *ND = dyn_cast_or_null<NamedDecl>(FD)) - return ND->printName(os); + return ND->printQualifiedName(os); os << "< >"; } diff --git a/lib/Analysis/CloneDetection.cpp b/lib/Analysis/CloneDetection.cpp index 8912b3b76751..88402e2adaa7 100644 --- a/lib/Analysis/CloneDetection.cpp +++ b/lib/Analysis/CloneDetection.cpp @@ -7,7 +7,7 @@ // //===----------------------------------------------------------------------===// /// -/// This file implements classes for searching and anlyzing source code clones. +/// This file implements classes for searching and analyzing source code clones. /// //===----------------------------------------------------------------------===// @@ -45,8 +45,8 @@ bool StmtSequence::contains(const StmtSequence &Other) const { // Otherwise check if the start and end locations of the current sequence // surround the other sequence. bool StartIsInBounds = - SM.isBeforeInTranslationUnit(getStartLoc(), Other.getStartLoc()) || - getStartLoc() == Other.getStartLoc(); + SM.isBeforeInTranslationUnit(getBeginLoc(), Other.getBeginLoc()) || + getBeginLoc() == Other.getBeginLoc(); if (!StartIsInBounds) return false; @@ -77,14 +77,14 @@ ASTContext &StmtSequence::getASTContext() const { return D->getASTContext(); } -SourceLocation StmtSequence::getStartLoc() const { - return front()->getLocStart(); +SourceLocation StmtSequence::getBeginLoc() const { + return front()->getBeginLoc(); } -SourceLocation StmtSequence::getEndLoc() const { return back()->getLocEnd(); } +SourceLocation StmtSequence::getEndLoc() const { return back()->getEndLoc(); } SourceRange StmtSequence::getSourceRange() const { - return SourceRange(getStartLoc(), getEndLoc()); + return SourceRange(getBeginLoc(), getEndLoc()); } void CloneDetector::analyzeCodeBody(const Decl *D) { @@ -433,7 +433,7 @@ size_t MinComplexityConstraint::calculateStmtComplexity( // Look up what macros expanded into the current statement. std::string MacroStack = - data_collection::getMacroStack(Seq.getStartLoc(), Context); + data_collection::getMacroStack(Seq.getBeginLoc(), Context); // First, check if ParentMacroStack is not empty which means we are currently // dealing with a parent statement which was expanded from a macro. @@ -523,8 +523,7 @@ void CloneConstraint::splitCloneGroups( Result.push_back(PotentialGroup); } - assert(std::all_of(Indexes.begin(), Indexes.end(), - [](char c) { return c == 1; })); + assert(llvm::all_of(Indexes, [](char c) { return c == 1; })); } CloneGroups = Result; } diff --git a/lib/Analysis/Consumed.cpp b/lib/Analysis/Consumed.cpp index bc81a71b3d91..16eeaba2f61b 100644 --- a/lib/Analysis/Consumed.cpp +++ b/lib/Analysis/Consumed.cpp @@ -64,7 +64,7 @@ static SourceLocation getFirstStmtLoc(const CFGBlock *Block) { // is not empty. for (const auto &B : *Block) if (Optional<CFGStmt> CS = B.getAs<CFGStmt>()) - return CS->getStmt()->getLocStart(); + return CS->getStmt()->getBeginLoc(); // Block is empty. // If we have one successor, return the first statement in that block @@ -78,12 +78,12 @@ static SourceLocation getLastStmtLoc(const CFGBlock *Block) { // Find the source location of the last statement in the block, if the block // is not empty. if (const Stmt *StmtNode = Block->getTerminator()) { - return StmtNode->getLocStart(); + return StmtNode->getBeginLoc(); } else { for (CFGBlock::const_reverse_iterator BI = Block->rbegin(), BE = Block->rend(); BI != BE; ++BI) { if (Optional<CFGStmt> CS = BI->getAs<CFGStmt>()) - return CS->getStmt()->getLocStart(); + return CS->getStmt()->getBeginLoc(); } } @@ -463,7 +463,6 @@ class ConsumedStmtVisitor : public ConstStmtVisitor<ConsumedStmtVisitor> { using InfoEntry = MapType::iterator; using ConstInfoEntry = MapType::const_iterator; - AnalysisDeclContext &AC; ConsumedAnalyzer &Analyzer; ConsumedStateMap *StateMap; MapType PropagationMap; @@ -515,9 +514,8 @@ public: void VisitUnaryOperator(const UnaryOperator *UOp); void VisitVarDecl(const VarDecl *Var); - ConsumedStmtVisitor(AnalysisDeclContext &AC, ConsumedAnalyzer &Analyzer, - ConsumedStateMap *StateMap) - : AC(AC), Analyzer(Analyzer), StateMap(StateMap) {} + ConsumedStmtVisitor(ConsumedAnalyzer &Analyzer, ConsumedStateMap *StateMap) + : Analyzer(Analyzer), StateMap(StateMap) {} PropagationInfo getInfo(const Expr *StmtNode) const { ConstInfoEntry Entry = findInfo(StmtNode); @@ -774,8 +772,7 @@ void ConsumedStmtVisitor::VisitCXXBindTemporaryExpr( void ConsumedStmtVisitor::VisitCXXConstructExpr(const CXXConstructExpr *Call) { CXXConstructorDecl *Constructor = Call->getConstructor(); - ASTContext &CurrContext = AC.getASTContext(); - QualType ThisType = Constructor->getThisType(CurrContext)->getPointeeType(); + QualType ThisType = Constructor->getThisType()->getPointeeType(); if (!isConsumableType(ThisType)) return; @@ -793,7 +790,7 @@ void ConsumedStmtVisitor::VisitCXXConstructExpr(const CXXConstructExpr *Call) { } else if (Constructor->isCopyConstructor()) { // Copy state from arg. If setStateOnRead then set arg to CS_Unknown. ConsumedState NS = - isSetOnReadPtrType(Constructor->getThisType(CurrContext)) ? + isSetOnReadPtrType(Constructor->getThisType()) ? CS_Unknown : CS_None; copyInfo(Call->getArg(0), Call, NS); } else { @@ -893,7 +890,7 @@ void ConsumedStmtVisitor::VisitReturnStmt(const ReturnStmt *Ret) { } } - StateMap->checkParamsForReturnTypestate(Ret->getLocStart(), + StateMap->checkParamsForReturnTypestate(Ret->getBeginLoc(), Analyzer.WarningsHandler); } @@ -1203,8 +1200,7 @@ void ConsumedAnalyzer::determineExpectedReturnState(AnalysisDeclContext &AC, const FunctionDecl *D) { QualType ReturnType; if (const auto *Constructor = dyn_cast<CXXConstructorDecl>(D)) { - ASTContext &CurrContext = AC.getASTContext(); - ReturnType = Constructor->getThisType(CurrContext)->getPointeeType(); + ReturnType = Constructor->getThisType()->getPointeeType(); } else ReturnType = D->getCallResultType(); @@ -1323,7 +1319,7 @@ void ConsumedAnalyzer::run(AnalysisDeclContext &AC) { BlockInfo = ConsumedBlockInfo(CFGraph->getNumBlockIDs(), SortedGraph); CurrStates = llvm::make_unique<ConsumedStateMap>(); - ConsumedStmtVisitor Visitor(AC, *this, CurrStates.get()); + ConsumedStmtVisitor Visitor(*this, CurrStates.get()); // Add all trackable parameters to the state map. for (const auto *PI : D->parameters()) @@ -1363,7 +1359,7 @@ void ConsumedAnalyzer::run(AnalysisDeclContext &AC) { case CFGElement::AutomaticObjectDtor: { const CFGAutomaticObjDtor &DTor = B.castAs<CFGAutomaticObjDtor>(); - SourceLocation Loc = DTor.getTriggerStmt()->getLocEnd(); + SourceLocation Loc = DTor.getTriggerStmt()->getEndLoc(); const VarDecl *Var = DTor.getVarDecl(); Visitor.checkCallability(PropagationInfo(Var), diff --git a/lib/Analysis/ExprMutationAnalyzer.cpp b/lib/Analysis/ExprMutationAnalyzer.cpp new file mode 100644 index 000000000000..8414cb5c726a --- /dev/null +++ b/lib/Analysis/ExprMutationAnalyzer.cpp @@ -0,0 +1,445 @@ +//===---------- ExprMutationAnalyzer.cpp ----------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +#include "clang/Analysis/Analyses/ExprMutationAnalyzer.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" +#include "llvm/ADT/STLExtras.h" + +namespace clang { +using namespace ast_matchers; + +namespace { + +AST_MATCHER_P(LambdaExpr, hasCaptureInit, const Expr *, E) { + return llvm::is_contained(Node.capture_inits(), E); +} + +AST_MATCHER_P(CXXForRangeStmt, hasRangeStmt, + ast_matchers::internal::Matcher<DeclStmt>, InnerMatcher) { + const DeclStmt *const Range = Node.getRangeStmt(); + return InnerMatcher.matches(*Range, Finder, Builder); +} + +const ast_matchers::internal::VariadicDynCastAllOfMatcher<Stmt, CXXTypeidExpr> + cxxTypeidExpr; + +AST_MATCHER(CXXTypeidExpr, isPotentiallyEvaluated) { + return Node.isPotentiallyEvaluated(); +} + +const ast_matchers::internal::VariadicDynCastAllOfMatcher<Stmt, CXXNoexceptExpr> + cxxNoexceptExpr; + +const ast_matchers::internal::VariadicDynCastAllOfMatcher<Stmt, + GenericSelectionExpr> + genericSelectionExpr; + +AST_MATCHER_P(GenericSelectionExpr, hasControllingExpr, + ast_matchers::internal::Matcher<Expr>, InnerMatcher) { + return InnerMatcher.matches(*Node.getControllingExpr(), Finder, Builder); +} + +const auto nonConstReferenceType = [] { + return hasUnqualifiedDesugaredType( + referenceType(pointee(unless(isConstQualified())))); +}; + +const auto nonConstPointerType = [] { + return hasUnqualifiedDesugaredType( + pointerType(pointee(unless(isConstQualified())))); +}; + +const auto isMoveOnly = [] { + return cxxRecordDecl( + hasMethod(cxxConstructorDecl(isMoveConstructor(), unless(isDeleted()))), + hasMethod(cxxMethodDecl(isMoveAssignmentOperator(), unless(isDeleted()))), + unless(anyOf(hasMethod(cxxConstructorDecl(isCopyConstructor(), + unless(isDeleted()))), + hasMethod(cxxMethodDecl(isCopyAssignmentOperator(), + unless(isDeleted())))))); +}; + +template <class T> struct NodeID; +template <> struct NodeID<Expr> { static const std::string value; }; +template <> struct NodeID<Decl> { static const std::string value; }; +const std::string NodeID<Expr>::value = "expr"; +const std::string NodeID<Decl>::value = "decl"; + +template <class T, class F = const Stmt *(ExprMutationAnalyzer::*)(const T *)> +const Stmt *tryEachMatch(ArrayRef<ast_matchers::BoundNodes> Matches, + ExprMutationAnalyzer *Analyzer, F Finder) { + const StringRef ID = NodeID<T>::value; + for (const auto &Nodes : Matches) { + if (const Stmt *S = (Analyzer->*Finder)(Nodes.getNodeAs<T>(ID))) + return S; + } + return nullptr; +} + +} // namespace + +const Stmt *ExprMutationAnalyzer::findMutation(const Expr *Exp) { + return findMutationMemoized(Exp, + {&ExprMutationAnalyzer::findDirectMutation, + &ExprMutationAnalyzer::findMemberMutation, + &ExprMutationAnalyzer::findArrayElementMutation, + &ExprMutationAnalyzer::findCastMutation, + &ExprMutationAnalyzer::findRangeLoopMutation, + &ExprMutationAnalyzer::findReferenceMutation, + &ExprMutationAnalyzer::findFunctionArgMutation}, + Results); +} + +const Stmt *ExprMutationAnalyzer::findMutation(const Decl *Dec) { + return tryEachDeclRef(Dec, &ExprMutationAnalyzer::findMutation); +} + +const Stmt *ExprMutationAnalyzer::findPointeeMutation(const Expr *Exp) { + return findMutationMemoized(Exp, {/*TODO*/}, PointeeResults); +} + +const Stmt *ExprMutationAnalyzer::findPointeeMutation(const Decl *Dec) { + return tryEachDeclRef(Dec, &ExprMutationAnalyzer::findPointeeMutation); +} + +const Stmt *ExprMutationAnalyzer::findMutationMemoized( + const Expr *Exp, llvm::ArrayRef<MutationFinder> Finders, + ResultMap &MemoizedResults) { + const auto Memoized = MemoizedResults.find(Exp); + if (Memoized != MemoizedResults.end()) + return Memoized->second; + + if (isUnevaluated(Exp)) + return MemoizedResults[Exp] = nullptr; + + for (const auto &Finder : Finders) { + if (const Stmt *S = (this->*Finder)(Exp)) + return MemoizedResults[Exp] = S; + } + + return MemoizedResults[Exp] = nullptr; +} + +const Stmt *ExprMutationAnalyzer::tryEachDeclRef(const Decl *Dec, + MutationFinder Finder) { + const auto Refs = + match(findAll(declRefExpr(to(equalsNode(Dec))).bind(NodeID<Expr>::value)), + Stm, Context); + for (const auto &RefNodes : Refs) { + const auto *E = RefNodes.getNodeAs<Expr>(NodeID<Expr>::value); + if ((this->*Finder)(E)) + return E; + } + return nullptr; +} + +bool ExprMutationAnalyzer::isUnevaluated(const Expr *Exp) { + return selectFirst<Expr>( + NodeID<Expr>::value, + match( + findAll( + expr(equalsNode(Exp), + anyOf( + // `Exp` is part of the underlying expression of + // decltype/typeof if it has an ancestor of + // typeLoc. + hasAncestor(typeLoc(unless( + hasAncestor(unaryExprOrTypeTraitExpr())))), + hasAncestor(expr(anyOf( + // `UnaryExprOrTypeTraitExpr` is unevaluated + // unless it's sizeof on VLA. + unaryExprOrTypeTraitExpr(unless(sizeOfExpr( + hasArgumentOfType(variableArrayType())))), + // `CXXTypeidExpr` is unevaluated unless it's + // applied to an expression of glvalue of + // polymorphic class type. + cxxTypeidExpr( + unless(isPotentiallyEvaluated())), + // The controlling expression of + // `GenericSelectionExpr` is unevaluated. + genericSelectionExpr(hasControllingExpr( + hasDescendant(equalsNode(Exp)))), + cxxNoexceptExpr()))))) + .bind(NodeID<Expr>::value)), + Stm, Context)) != nullptr; +} + +const Stmt * +ExprMutationAnalyzer::findExprMutation(ArrayRef<BoundNodes> Matches) { + return tryEachMatch<Expr>(Matches, this, &ExprMutationAnalyzer::findMutation); +} + +const Stmt * +ExprMutationAnalyzer::findDeclMutation(ArrayRef<BoundNodes> Matches) { + return tryEachMatch<Decl>(Matches, this, &ExprMutationAnalyzer::findMutation); +} + +const Stmt *ExprMutationAnalyzer::findExprPointeeMutation( + ArrayRef<ast_matchers::BoundNodes> Matches) { + return tryEachMatch<Expr>(Matches, this, + &ExprMutationAnalyzer::findPointeeMutation); +} + +const Stmt *ExprMutationAnalyzer::findDeclPointeeMutation( + ArrayRef<ast_matchers::BoundNodes> Matches) { + return tryEachMatch<Decl>(Matches, this, + &ExprMutationAnalyzer::findPointeeMutation); +} + +const Stmt *ExprMutationAnalyzer::findDirectMutation(const Expr *Exp) { + // LHS of any assignment operators. + const auto AsAssignmentLhs = + binaryOperator(isAssignmentOperator(), hasLHS(equalsNode(Exp))); + + // Operand of increment/decrement operators. + const auto AsIncDecOperand = + unaryOperator(anyOf(hasOperatorName("++"), hasOperatorName("--")), + hasUnaryOperand(equalsNode(Exp))); + + // Invoking non-const member function. + // A member function is assumed to be non-const when it is unresolved. + const auto NonConstMethod = cxxMethodDecl(unless(isConst())); + const auto AsNonConstThis = + expr(anyOf(cxxMemberCallExpr(callee(NonConstMethod), on(equalsNode(Exp))), + cxxOperatorCallExpr(callee(NonConstMethod), + hasArgument(0, equalsNode(Exp))), + callExpr(callee(expr(anyOf( + unresolvedMemberExpr(hasObjectExpression(equalsNode(Exp))), + cxxDependentScopeMemberExpr( + hasObjectExpression(equalsNode(Exp))))))))); + + // Taking address of 'Exp'. + // We're assuming 'Exp' is mutated as soon as its address is taken, though in + // theory we can follow the pointer and see whether it escaped `Stm` or is + // dereferenced and then mutated. This is left for future improvements. + const auto AsAmpersandOperand = + unaryOperator(hasOperatorName("&"), + // A NoOp implicit cast is adding const. + unless(hasParent(implicitCastExpr(hasCastKind(CK_NoOp)))), + hasUnaryOperand(equalsNode(Exp))); + const auto AsPointerFromArrayDecay = + castExpr(hasCastKind(CK_ArrayToPointerDecay), + unless(hasParent(arraySubscriptExpr())), has(equalsNode(Exp))); + // Treat calling `operator->()` of move-only classes as taking address. + // These are typically smart pointers with unique ownership so we treat + // mutation of pointee as mutation of the smart pointer itself. + const auto AsOperatorArrowThis = + cxxOperatorCallExpr(hasOverloadedOperatorName("->"), + callee(cxxMethodDecl(ofClass(isMoveOnly()), + returns(nonConstPointerType()))), + argumentCountIs(1), hasArgument(0, equalsNode(Exp))); + + // Used as non-const-ref argument when calling a function. + // An argument is assumed to be non-const-ref when the function is unresolved. + // Instantiated template functions are not handled here but in + // findFunctionArgMutation which has additional smarts for handling forwarding + // references. + const auto NonConstRefParam = forEachArgumentWithParam( + equalsNode(Exp), parmVarDecl(hasType(nonConstReferenceType()))); + const auto NotInstantiated = unless(hasDeclaration(isInstantiated())); + const auto AsNonConstRefArg = anyOf( + callExpr(NonConstRefParam, NotInstantiated), + cxxConstructExpr(NonConstRefParam, NotInstantiated), + callExpr(callee(expr(anyOf(unresolvedLookupExpr(), unresolvedMemberExpr(), + cxxDependentScopeMemberExpr(), + hasType(templateTypeParmType())))), + hasAnyArgument(equalsNode(Exp))), + cxxUnresolvedConstructExpr(hasAnyArgument(equalsNode(Exp)))); + + // Captured by a lambda by reference. + // If we're initializing a capture with 'Exp' directly then we're initializing + // a reference capture. + // For value captures there will be an ImplicitCastExpr <LValueToRValue>. + const auto AsLambdaRefCaptureInit = lambdaExpr(hasCaptureInit(Exp)); + + // Returned as non-const-ref. + // If we're returning 'Exp' directly then it's returned as non-const-ref. + // For returning by value there will be an ImplicitCastExpr <LValueToRValue>. + // For returning by const-ref there will be an ImplicitCastExpr <NoOp> (for + // adding const.) + const auto AsNonConstRefReturn = returnStmt(hasReturnValue(equalsNode(Exp))); + + const auto Matches = + match(findAll(stmt(anyOf(AsAssignmentLhs, AsIncDecOperand, AsNonConstThis, + AsAmpersandOperand, AsPointerFromArrayDecay, + AsOperatorArrowThis, AsNonConstRefArg, + AsLambdaRefCaptureInit, AsNonConstRefReturn)) + .bind("stmt")), + Stm, Context); + return selectFirst<Stmt>("stmt", Matches); +} + +const Stmt *ExprMutationAnalyzer::findMemberMutation(const Expr *Exp) { + // Check whether any member of 'Exp' is mutated. + const auto MemberExprs = + match(findAll(expr(anyOf(memberExpr(hasObjectExpression(equalsNode(Exp))), + cxxDependentScopeMemberExpr( + hasObjectExpression(equalsNode(Exp))))) + .bind(NodeID<Expr>::value)), + Stm, Context); + return findExprMutation(MemberExprs); +} + +const Stmt *ExprMutationAnalyzer::findArrayElementMutation(const Expr *Exp) { + // Check whether any element of an array is mutated. + const auto SubscriptExprs = match( + findAll(arraySubscriptExpr(hasBase(ignoringImpCasts(equalsNode(Exp)))) + .bind(NodeID<Expr>::value)), + Stm, Context); + return findExprMutation(SubscriptExprs); +} + +const Stmt *ExprMutationAnalyzer::findCastMutation(const Expr *Exp) { + // If 'Exp' is casted to any non-const reference type, check the castExpr. + const auto Casts = + match(findAll(castExpr(hasSourceExpression(equalsNode(Exp)), + anyOf(explicitCastExpr(hasDestinationType( + nonConstReferenceType())), + implicitCastExpr(hasImplicitDestinationType( + nonConstReferenceType())))) + .bind(NodeID<Expr>::value)), + Stm, Context); + if (const Stmt *S = findExprMutation(Casts)) + return S; + // Treat std::{move,forward} as cast. + const auto Calls = + match(findAll(callExpr(callee(namedDecl( + hasAnyName("::std::move", "::std::forward"))), + hasArgument(0, equalsNode(Exp))) + .bind("expr")), + Stm, Context); + return findExprMutation(Calls); +} + +const Stmt *ExprMutationAnalyzer::findRangeLoopMutation(const Expr *Exp) { + // If range for looping over 'Exp' with a non-const reference loop variable, + // check all declRefExpr of the loop variable. + const auto LoopVars = + match(findAll(cxxForRangeStmt( + hasLoopVariable(varDecl(hasType(nonConstReferenceType())) + .bind(NodeID<Decl>::value)), + hasRangeInit(equalsNode(Exp)))), + Stm, Context); + return findDeclMutation(LoopVars); +} + +const Stmt *ExprMutationAnalyzer::findReferenceMutation(const Expr *Exp) { + // Follow non-const reference returned by `operator*()` of move-only classes. + // These are typically smart pointers with unique ownership so we treat + // mutation of pointee as mutation of the smart pointer itself. + const auto Ref = + match(findAll(cxxOperatorCallExpr( + hasOverloadedOperatorName("*"), + callee(cxxMethodDecl(ofClass(isMoveOnly()), + returns(nonConstReferenceType()))), + argumentCountIs(1), hasArgument(0, equalsNode(Exp))) + .bind(NodeID<Expr>::value)), + Stm, Context); + if (const Stmt *S = findExprMutation(Ref)) + return S; + + // If 'Exp' is bound to a non-const reference, check all declRefExpr to that. + const auto Refs = match( + stmt(forEachDescendant( + varDecl( + hasType(nonConstReferenceType()), + hasInitializer(anyOf(equalsNode(Exp), + conditionalOperator(anyOf( + hasTrueExpression(equalsNode(Exp)), + hasFalseExpression(equalsNode(Exp)))))), + hasParent(declStmt().bind("stmt")), + // Don't follow the reference in range statement, we've handled + // that separately. + unless(hasParent(declStmt(hasParent( + cxxForRangeStmt(hasRangeStmt(equalsBoundNode("stmt")))))))) + .bind(NodeID<Decl>::value))), + Stm, Context); + return findDeclMutation(Refs); +} + +const Stmt *ExprMutationAnalyzer::findFunctionArgMutation(const Expr *Exp) { + const auto NonConstRefParam = forEachArgumentWithParam( + equalsNode(Exp), + parmVarDecl(hasType(nonConstReferenceType())).bind("parm")); + const auto IsInstantiated = hasDeclaration(isInstantiated()); + const auto FuncDecl = hasDeclaration(functionDecl().bind("func")); + const auto Matches = match( + findAll(expr(anyOf(callExpr(NonConstRefParam, IsInstantiated, FuncDecl, + unless(callee(namedDecl(hasAnyName( + "::std::move", "::std::forward"))))), + cxxConstructExpr(NonConstRefParam, IsInstantiated, + FuncDecl))) + .bind(NodeID<Expr>::value)), + Stm, Context); + for (const auto &Nodes : Matches) { + const auto *Exp = Nodes.getNodeAs<Expr>(NodeID<Expr>::value); + const auto *Func = Nodes.getNodeAs<FunctionDecl>("func"); + if (!Func->getBody() || !Func->getPrimaryTemplate()) + return Exp; + + const auto *Parm = Nodes.getNodeAs<ParmVarDecl>("parm"); + const ArrayRef<ParmVarDecl *> AllParams = + Func->getPrimaryTemplate()->getTemplatedDecl()->parameters(); + QualType ParmType = + AllParams[std::min<size_t>(Parm->getFunctionScopeIndex(), + AllParams.size() - 1)] + ->getType(); + if (const auto *T = ParmType->getAs<PackExpansionType>()) + ParmType = T->getPattern(); + + // If param type is forwarding reference, follow into the function + // definition and see whether the param is mutated inside. + if (const auto *RefType = ParmType->getAs<RValueReferenceType>()) { + if (!RefType->getPointeeType().getQualifiers() && + RefType->getPointeeType()->getAs<TemplateTypeParmType>()) { + std::unique_ptr<FunctionParmMutationAnalyzer> &Analyzer = + FuncParmAnalyzer[Func]; + if (!Analyzer) + Analyzer.reset(new FunctionParmMutationAnalyzer(*Func, Context)); + if (Analyzer->findMutation(Parm)) + return Exp; + continue; + } + } + // Not forwarding reference. + return Exp; + } + return nullptr; +} + +FunctionParmMutationAnalyzer::FunctionParmMutationAnalyzer( + const FunctionDecl &Func, ASTContext &Context) + : BodyAnalyzer(*Func.getBody(), Context) { + if (const auto *Ctor = dyn_cast<CXXConstructorDecl>(&Func)) { + // CXXCtorInitializer might also mutate Param but they're not part of + // function body, check them eagerly here since they're typically trivial. + for (const CXXCtorInitializer *Init : Ctor->inits()) { + ExprMutationAnalyzer InitAnalyzer(*Init->getInit(), Context); + for (const ParmVarDecl *Parm : Ctor->parameters()) { + if (Results.find(Parm) != Results.end()) + continue; + if (const Stmt *S = InitAnalyzer.findMutation(Parm)) + Results[Parm] = S; + } + } + } +} + +const Stmt * +FunctionParmMutationAnalyzer::findMutation(const ParmVarDecl *Parm) { + const auto Memoized = Results.find(Parm); + if (Memoized != Results.end()) + return Memoized->second; + + if (const Stmt *S = BodyAnalyzer.findMutation(Parm)) + return Results[Parm] = S; + + return Results[Parm] = nullptr; +} + +} // namespace clang diff --git a/lib/Analysis/FormatString.cpp b/lib/Analysis/FormatString.cpp deleted file mode 100644 index f37e4affae3f..000000000000 --- a/lib/Analysis/FormatString.cpp +++ /dev/null @@ -1,953 +0,0 @@ -// FormatString.cpp - Common stuff for handling printf/scanf formats -*- C++ -*- -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// Shared details for processing format strings of printf and scanf -// (and friends). -// -//===----------------------------------------------------------------------===// - -#include "FormatStringParsing.h" -#include "clang/Basic/LangOptions.h" -#include "clang/Basic/TargetInfo.h" -#include "llvm/Support/ConvertUTF.h" - -using clang::analyze_format_string::ArgType; -using clang::analyze_format_string::FormatStringHandler; -using clang::analyze_format_string::FormatSpecifier; -using clang::analyze_format_string::LengthModifier; -using clang::analyze_format_string::OptionalAmount; -using clang::analyze_format_string::PositionContext; -using clang::analyze_format_string::ConversionSpecifier; -using namespace clang; - -// Key function to FormatStringHandler. -FormatStringHandler::~FormatStringHandler() {} - -//===----------------------------------------------------------------------===// -// Functions for parsing format strings components in both printf and -// scanf format strings. -//===----------------------------------------------------------------------===// - -OptionalAmount -clang::analyze_format_string::ParseAmount(const char *&Beg, const char *E) { - const char *I = Beg; - UpdateOnReturn <const char*> UpdateBeg(Beg, I); - - unsigned accumulator = 0; - bool hasDigits = false; - - for ( ; I != E; ++I) { - char c = *I; - if (c >= '0' && c <= '9') { - hasDigits = true; - accumulator = (accumulator * 10) + (c - '0'); - continue; - } - - if (hasDigits) - return OptionalAmount(OptionalAmount::Constant, accumulator, Beg, I - Beg, - false); - - break; - } - - return OptionalAmount(); -} - -OptionalAmount -clang::analyze_format_string::ParseNonPositionAmount(const char *&Beg, - const char *E, - unsigned &argIndex) { - if (*Beg == '*') { - ++Beg; - return OptionalAmount(OptionalAmount::Arg, argIndex++, Beg, 0, false); - } - - return ParseAmount(Beg, E); -} - -OptionalAmount -clang::analyze_format_string::ParsePositionAmount(FormatStringHandler &H, - const char *Start, - const char *&Beg, - const char *E, - PositionContext p) { - if (*Beg == '*') { - const char *I = Beg + 1; - const OptionalAmount &Amt = ParseAmount(I, E); - - if (Amt.getHowSpecified() == OptionalAmount::NotSpecified) { - H.HandleInvalidPosition(Beg, I - Beg, p); - return OptionalAmount(false); - } - - if (I == E) { - // No more characters left? - H.HandleIncompleteSpecifier(Start, E - Start); - return OptionalAmount(false); - } - - assert(Amt.getHowSpecified() == OptionalAmount::Constant); - - if (*I == '$') { - // Handle positional arguments - - // Special case: '*0$', since this is an easy mistake. - if (Amt.getConstantAmount() == 0) { - H.HandleZeroPosition(Beg, I - Beg + 1); - return OptionalAmount(false); - } - - const char *Tmp = Beg; - Beg = ++I; - - return OptionalAmount(OptionalAmount::Arg, Amt.getConstantAmount() - 1, - Tmp, 0, true); - } - - H.HandleInvalidPosition(Beg, I - Beg, p); - return OptionalAmount(false); - } - - return ParseAmount(Beg, E); -} - - -bool -clang::analyze_format_string::ParseFieldWidth(FormatStringHandler &H, - FormatSpecifier &CS, - const char *Start, - const char *&Beg, const char *E, - unsigned *argIndex) { - // FIXME: Support negative field widths. - if (argIndex) { - CS.setFieldWidth(ParseNonPositionAmount(Beg, E, *argIndex)); - } - else { - const OptionalAmount Amt = - ParsePositionAmount(H, Start, Beg, E, - analyze_format_string::FieldWidthPos); - - if (Amt.isInvalid()) - return true; - CS.setFieldWidth(Amt); - } - return false; -} - -bool -clang::analyze_format_string::ParseArgPosition(FormatStringHandler &H, - FormatSpecifier &FS, - const char *Start, - const char *&Beg, - const char *E) { - const char *I = Beg; - - const OptionalAmount &Amt = ParseAmount(I, E); - - if (I == E) { - // No more characters left? - H.HandleIncompleteSpecifier(Start, E - Start); - return true; - } - - if (Amt.getHowSpecified() == OptionalAmount::Constant && *(I++) == '$') { - // Warn that positional arguments are non-standard. - H.HandlePosition(Start, I - Start); - - // Special case: '%0$', since this is an easy mistake. - if (Amt.getConstantAmount() == 0) { - H.HandleZeroPosition(Start, I - Start); - return true; - } - - FS.setArgIndex(Amt.getConstantAmount() - 1); - FS.setUsesPositionalArg(); - // Update the caller's pointer if we decided to consume - // these characters. - Beg = I; - return false; - } - - return false; -} - -bool -clang::analyze_format_string::ParseLengthModifier(FormatSpecifier &FS, - const char *&I, - const char *E, - const LangOptions &LO, - bool IsScanf) { - LengthModifier::Kind lmKind = LengthModifier::None; - const char *lmPosition = I; - switch (*I) { - default: - return false; - case 'h': - ++I; - if (I != E && *I == 'h') { - ++I; - lmKind = LengthModifier::AsChar; - } else { - lmKind = LengthModifier::AsShort; - } - break; - case 'l': - ++I; - if (I != E && *I == 'l') { - ++I; - lmKind = LengthModifier::AsLongLong; - } else { - lmKind = LengthModifier::AsLong; - } - break; - case 'j': lmKind = LengthModifier::AsIntMax; ++I; break; - case 'z': lmKind = LengthModifier::AsSizeT; ++I; break; - case 't': lmKind = LengthModifier::AsPtrDiff; ++I; break; - case 'L': lmKind = LengthModifier::AsLongDouble; ++I; break; - case 'q': lmKind = LengthModifier::AsQuad; ++I; break; - case 'a': - 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. - ++I; - if (I != E && (*I == 's' || *I == 'S' || *I == '[')) { - lmKind = LengthModifier::AsAllocate; - break; - } - --I; - } - return false; - case 'm': - if (IsScanf) { - lmKind = LengthModifier::AsMAllocate; - ++I; - break; - } - return false; - // printf: AsInt64, AsInt32, AsInt3264 - // scanf: AsInt64 - case 'I': - if (I + 1 != E && I + 2 != E) { - if (I[1] == '6' && I[2] == '4') { - I += 3; - lmKind = LengthModifier::AsInt64; - break; - } - if (IsScanf) - return false; - - if (I[1] == '3' && I[2] == '2') { - I += 3; - lmKind = LengthModifier::AsInt32; - break; - } - } - ++I; - lmKind = LengthModifier::AsInt3264; - break; - case 'w': - lmKind = LengthModifier::AsWide; ++I; break; - } - LengthModifier lm(lmPosition, lmKind); - FS.setLengthModifier(lm); - return true; -} - -bool clang::analyze_format_string::ParseUTF8InvalidSpecifier( - const char *SpecifierBegin, const char *FmtStrEnd, unsigned &Len) { - if (SpecifierBegin + 1 >= FmtStrEnd) - return false; - - const llvm::UTF8 *SB = - reinterpret_cast<const llvm::UTF8 *>(SpecifierBegin + 1); - const llvm::UTF8 *SE = reinterpret_cast<const llvm::UTF8 *>(FmtStrEnd); - const char FirstByte = *SB; - - // If the invalid specifier is a multibyte UTF-8 string, return the - // total length accordingly so that the conversion specifier can be - // properly updated to reflect a complete UTF-8 specifier. - unsigned NumBytes = llvm::getNumBytesForUTF8(FirstByte); - if (NumBytes == 1) - return false; - if (SB + NumBytes > SE) - return false; - - Len = NumBytes + 1; - return true; -} - -//===----------------------------------------------------------------------===// -// Methods on ArgType. -//===----------------------------------------------------------------------===// - -clang::analyze_format_string::ArgType::MatchKind -ArgType::matchesType(ASTContext &C, QualType argTy) const { - if (Ptr) { - // It has to be a pointer. - const PointerType *PT = argTy->getAs<PointerType>(); - if (!PT) - return NoMatch; - - // We cannot write through a const qualified pointer. - if (PT->getPointeeType().isConstQualified()) - return NoMatch; - - argTy = PT->getPointeeType(); - } - - switch (K) { - case InvalidTy: - llvm_unreachable("ArgType must be valid"); - - case UnknownTy: - return Match; - - case AnyCharTy: { - if (const EnumType *ETy = argTy->getAs<EnumType>()) { - // If the enum is incomplete we know nothing about the underlying type. - // Assume that it's 'int'. - if (!ETy->getDecl()->isComplete()) - return NoMatch; - argTy = ETy->getDecl()->getIntegerType(); - } - - if (const BuiltinType *BT = argTy->getAs<BuiltinType>()) - switch (BT->getKind()) { - default: - break; - case BuiltinType::Char_S: - case BuiltinType::SChar: - case BuiltinType::UChar: - case BuiltinType::Char_U: - return Match; - } - return NoMatch; - } - - case SpecificTy: { - if (const EnumType *ETy = argTy->getAs<EnumType>()) { - // If the enum is incomplete we know nothing about the underlying type. - // Assume that it's 'int'. - if (!ETy->getDecl()->isComplete()) - argTy = C.IntTy; - else - argTy = ETy->getDecl()->getIntegerType(); - } - argTy = C.getCanonicalType(argTy).getUnqualifiedType(); - - if (T == argTy) - return Match; - // Check for "compatible types". - if (const BuiltinType *BT = argTy->getAs<BuiltinType>()) - switch (BT->getKind()) { - default: - break; - case BuiltinType::Char_S: - case BuiltinType::SChar: - case BuiltinType::Char_U: - case BuiltinType::UChar: - return T == C.UnsignedCharTy || T == C.SignedCharTy ? Match - : NoMatch; - case BuiltinType::Short: - return T == C.UnsignedShortTy ? Match : NoMatch; - case BuiltinType::UShort: - return T == C.ShortTy ? Match : NoMatch; - case BuiltinType::Int: - return T == C.UnsignedIntTy ? Match : NoMatch; - case BuiltinType::UInt: - return T == C.IntTy ? Match : NoMatch; - case BuiltinType::Long: - return T == C.UnsignedLongTy ? Match : NoMatch; - case BuiltinType::ULong: - return T == C.LongTy ? Match : NoMatch; - case BuiltinType::LongLong: - return T == C.UnsignedLongLongTy ? Match : NoMatch; - case BuiltinType::ULongLong: - return T == C.LongLongTy ? Match : NoMatch; - } - return NoMatch; - } - - case CStrTy: { - const PointerType *PT = argTy->getAs<PointerType>(); - if (!PT) - return NoMatch; - QualType pointeeTy = PT->getPointeeType(); - if (const BuiltinType *BT = pointeeTy->getAs<BuiltinType>()) - switch (BT->getKind()) { - case BuiltinType::Void: - case BuiltinType::Char_U: - case BuiltinType::UChar: - case BuiltinType::Char_S: - case BuiltinType::SChar: - return Match; - default: - break; - } - - return NoMatch; - } - - case WCStrTy: { - const PointerType *PT = argTy->getAs<PointerType>(); - if (!PT) - return NoMatch; - QualType pointeeTy = - C.getCanonicalType(PT->getPointeeType()).getUnqualifiedType(); - return pointeeTy == C.getWideCharType() ? Match : NoMatch; - } - - case WIntTy: { - - QualType PromoArg = - argTy->isPromotableIntegerType() - ? C.getPromotedIntegerType(argTy) : argTy; - - QualType WInt = C.getCanonicalType(C.getWIntType()).getUnqualifiedType(); - PromoArg = C.getCanonicalType(PromoArg).getUnqualifiedType(); - - // If the promoted argument is the corresponding signed type of the - // wint_t type, then it should match. - if (PromoArg->hasSignedIntegerRepresentation() && - C.getCorrespondingUnsignedType(PromoArg) == WInt) - return Match; - - return WInt == PromoArg ? Match : NoMatch; - } - - case CPointerTy: - if (argTy->isVoidPointerType()) { - return Match; - } if (argTy->isPointerType() || argTy->isObjCObjectPointerType() || - argTy->isBlockPointerType() || argTy->isNullPtrType()) { - return NoMatchPedantic; - } else { - return NoMatch; - } - - case ObjCPointerTy: { - if (argTy->getAs<ObjCObjectPointerType>() || - argTy->getAs<BlockPointerType>()) - return Match; - - // Handle implicit toll-free bridging. - if (const PointerType *PT = argTy->getAs<PointerType>()) { - // Things such as CFTypeRef are really just opaque pointers - // to C structs representing CF types that can often be bridged - // to Objective-C objects. Since the compiler doesn't know which - // structs can be toll-free bridged, we just accept them all. - QualType pointee = PT->getPointeeType(); - if (pointee->getAsStructureType() || pointee->isVoidType()) - return Match; - } - return NoMatch; - } - } - - llvm_unreachable("Invalid ArgType Kind!"); -} - -QualType ArgType::getRepresentativeType(ASTContext &C) const { - QualType Res; - switch (K) { - case InvalidTy: - llvm_unreachable("No representative type for Invalid ArgType"); - case UnknownTy: - llvm_unreachable("No representative type for Unknown ArgType"); - case AnyCharTy: - Res = C.CharTy; - break; - case SpecificTy: - Res = T; - break; - case CStrTy: - Res = C.getPointerType(C.CharTy); - break; - case WCStrTy: - Res = C.getPointerType(C.getWideCharType()); - break; - case ObjCPointerTy: - Res = C.ObjCBuiltinIdTy; - break; - case CPointerTy: - Res = C.VoidPtrTy; - break; - case WIntTy: { - Res = C.getWIntType(); - break; - } - } - - if (Ptr) - Res = C.getPointerType(Res); - return Res; -} - -std::string ArgType::getRepresentativeTypeName(ASTContext &C) const { - std::string S = getRepresentativeType(C).getAsString(); - - std::string Alias; - if (Name) { - // Use a specific name for this type, e.g. "size_t". - Alias = Name; - if (Ptr) { - // If ArgType is actually a pointer to T, append an asterisk. - Alias += (Alias[Alias.size()-1] == '*') ? "*" : " *"; - } - // If Alias is the same as the underlying type, e.g. wchar_t, then drop it. - if (S == Alias) - Alias.clear(); - } - - if (!Alias.empty()) - return std::string("'") + Alias + "' (aka '" + S + "')"; - return std::string("'") + S + "'"; -} - - -//===----------------------------------------------------------------------===// -// Methods on OptionalAmount. -//===----------------------------------------------------------------------===// - -ArgType -analyze_format_string::OptionalAmount::getArgType(ASTContext &Ctx) const { - return Ctx.IntTy; -} - -//===----------------------------------------------------------------------===// -// Methods on LengthModifier. -//===----------------------------------------------------------------------===// - -const char * -analyze_format_string::LengthModifier::toString() const { - switch (kind) { - case AsChar: - return "hh"; - case AsShort: - return "h"; - case AsLong: // or AsWideChar - return "l"; - case AsLongLong: - return "ll"; - case AsQuad: - return "q"; - case AsIntMax: - return "j"; - case AsSizeT: - return "z"; - case AsPtrDiff: - return "t"; - case AsInt32: - return "I32"; - case AsInt3264: - return "I"; - case AsInt64: - return "I64"; - case AsLongDouble: - return "L"; - case AsAllocate: - return "a"; - case AsMAllocate: - return "m"; - case AsWide: - return "w"; - case None: - return ""; - } - return nullptr; -} - -//===----------------------------------------------------------------------===// -// Methods on ConversionSpecifier. -//===----------------------------------------------------------------------===// - -const char *ConversionSpecifier::toString() const { - switch (kind) { - case dArg: return "d"; - case DArg: return "D"; - case iArg: return "i"; - case oArg: return "o"; - case OArg: return "O"; - case uArg: return "u"; - case UArg: return "U"; - case xArg: return "x"; - case XArg: return "X"; - case fArg: return "f"; - case FArg: return "F"; - case eArg: return "e"; - case EArg: return "E"; - case gArg: return "g"; - case GArg: return "G"; - case aArg: return "a"; - case AArg: return "A"; - case cArg: return "c"; - case sArg: return "s"; - case pArg: return "p"; - case PArg: - return "P"; - case nArg: return "n"; - case PercentArg: return "%"; - case ScanListArg: return "["; - case InvalidSpecifier: return nullptr; - - // POSIX unicode extensions. - case CArg: return "C"; - case SArg: return "S"; - - // Objective-C specific specifiers. - case ObjCObjArg: return "@"; - - // FreeBSD kernel specific specifiers. - case FreeBSDbArg: return "b"; - case FreeBSDDArg: return "D"; - case FreeBSDrArg: return "r"; - case FreeBSDyArg: return "y"; - - // GlibC specific specifiers. - case PrintErrno: return "m"; - - // MS specific specifiers. - case ZArg: return "Z"; - } - return nullptr; -} - -Optional<ConversionSpecifier> -ConversionSpecifier::getStandardSpecifier() const { - ConversionSpecifier::Kind NewKind; - - switch (getKind()) { - default: - return None; - case DArg: - NewKind = dArg; - break; - case UArg: - NewKind = uArg; - break; - case OArg: - NewKind = oArg; - break; - } - - ConversionSpecifier FixedCS(*this); - FixedCS.setKind(NewKind); - return FixedCS; -} - -//===----------------------------------------------------------------------===// -// Methods on OptionalAmount. -//===----------------------------------------------------------------------===// - -void OptionalAmount::toString(raw_ostream &os) const { - switch (hs) { - case Invalid: - case NotSpecified: - return; - case Arg: - if (UsesDotPrefix) - os << "."; - if (usesPositionalArg()) - os << "*" << getPositionalArgIndex() << "$"; - else - os << "*"; - break; - case Constant: - if (UsesDotPrefix) - os << "."; - os << amt; - break; - } -} - -bool FormatSpecifier::hasValidLengthModifier(const TargetInfo &Target) const { - switch (LM.getKind()) { - case LengthModifier::None: - return true; - - // Handle most integer flags - case LengthModifier::AsShort: - if (Target.getTriple().isOSMSVCRT()) { - switch (CS.getKind()) { - case ConversionSpecifier::cArg: - case ConversionSpecifier::CArg: - case ConversionSpecifier::sArg: - case ConversionSpecifier::SArg: - case ConversionSpecifier::ZArg: - return true; - default: - break; - } - } - // Fall through. - case LengthModifier::AsChar: - case LengthModifier::AsLongLong: - case LengthModifier::AsQuad: - case LengthModifier::AsIntMax: - case LengthModifier::AsSizeT: - case LengthModifier::AsPtrDiff: - switch (CS.getKind()) { - case ConversionSpecifier::dArg: - case ConversionSpecifier::DArg: - case ConversionSpecifier::iArg: - case ConversionSpecifier::oArg: - case ConversionSpecifier::OArg: - case ConversionSpecifier::uArg: - case ConversionSpecifier::UArg: - case ConversionSpecifier::xArg: - case ConversionSpecifier::XArg: - case ConversionSpecifier::nArg: - return true; - case ConversionSpecifier::FreeBSDrArg: - case ConversionSpecifier::FreeBSDyArg: - return Target.getTriple().isOSFreeBSD() || Target.getTriple().isPS4(); - default: - return false; - } - - // Handle 'l' flag - case LengthModifier::AsLong: // or AsWideChar - switch (CS.getKind()) { - case ConversionSpecifier::dArg: - case ConversionSpecifier::DArg: - case ConversionSpecifier::iArg: - case ConversionSpecifier::oArg: - case ConversionSpecifier::OArg: - case ConversionSpecifier::uArg: - case ConversionSpecifier::UArg: - case ConversionSpecifier::xArg: - case ConversionSpecifier::XArg: - case ConversionSpecifier::aArg: - case ConversionSpecifier::AArg: - case ConversionSpecifier::fArg: - case ConversionSpecifier::FArg: - case ConversionSpecifier::eArg: - case ConversionSpecifier::EArg: - case ConversionSpecifier::gArg: - case ConversionSpecifier::GArg: - case ConversionSpecifier::nArg: - case ConversionSpecifier::cArg: - case ConversionSpecifier::sArg: - case ConversionSpecifier::ScanListArg: - case ConversionSpecifier::ZArg: - return true; - case ConversionSpecifier::FreeBSDrArg: - case ConversionSpecifier::FreeBSDyArg: - return Target.getTriple().isOSFreeBSD() || Target.getTriple().isPS4(); - default: - return false; - } - - case LengthModifier::AsLongDouble: - switch (CS.getKind()) { - case ConversionSpecifier::aArg: - case ConversionSpecifier::AArg: - case ConversionSpecifier::fArg: - case ConversionSpecifier::FArg: - case ConversionSpecifier::eArg: - case ConversionSpecifier::EArg: - case ConversionSpecifier::gArg: - case ConversionSpecifier::GArg: - return true; - // GNU libc extension. - case ConversionSpecifier::dArg: - case ConversionSpecifier::iArg: - case ConversionSpecifier::oArg: - case ConversionSpecifier::uArg: - case ConversionSpecifier::xArg: - case ConversionSpecifier::XArg: - return !Target.getTriple().isOSDarwin() && - !Target.getTriple().isOSWindows(); - default: - return false; - } - - case LengthModifier::AsAllocate: - switch (CS.getKind()) { - case ConversionSpecifier::sArg: - case ConversionSpecifier::SArg: - case ConversionSpecifier::ScanListArg: - return true; - default: - return false; - } - - case LengthModifier::AsMAllocate: - switch (CS.getKind()) { - case ConversionSpecifier::cArg: - case ConversionSpecifier::CArg: - case ConversionSpecifier::sArg: - case ConversionSpecifier::SArg: - case ConversionSpecifier::ScanListArg: - return true; - default: - return false; - } - case LengthModifier::AsInt32: - case LengthModifier::AsInt3264: - case LengthModifier::AsInt64: - switch (CS.getKind()) { - case ConversionSpecifier::dArg: - case ConversionSpecifier::iArg: - case ConversionSpecifier::oArg: - case ConversionSpecifier::uArg: - case ConversionSpecifier::xArg: - case ConversionSpecifier::XArg: - return Target.getTriple().isOSMSVCRT(); - default: - return false; - } - case LengthModifier::AsWide: - switch (CS.getKind()) { - case ConversionSpecifier::cArg: - case ConversionSpecifier::CArg: - case ConversionSpecifier::sArg: - case ConversionSpecifier::SArg: - case ConversionSpecifier::ZArg: - return Target.getTriple().isOSMSVCRT(); - default: - return false; - } - } - llvm_unreachable("Invalid LengthModifier Kind!"); -} - -bool FormatSpecifier::hasStandardLengthModifier() const { - switch (LM.getKind()) { - case LengthModifier::None: - case LengthModifier::AsChar: - case LengthModifier::AsShort: - case LengthModifier::AsLong: - case LengthModifier::AsLongLong: - case LengthModifier::AsIntMax: - case LengthModifier::AsSizeT: - case LengthModifier::AsPtrDiff: - case LengthModifier::AsLongDouble: - return true; - case LengthModifier::AsAllocate: - case LengthModifier::AsMAllocate: - case LengthModifier::AsQuad: - case LengthModifier::AsInt32: - case LengthModifier::AsInt3264: - case LengthModifier::AsInt64: - case LengthModifier::AsWide: - return false; - } - llvm_unreachable("Invalid LengthModifier Kind!"); -} - -bool FormatSpecifier::hasStandardConversionSpecifier( - const LangOptions &LangOpt) const { - switch (CS.getKind()) { - case ConversionSpecifier::cArg: - case ConversionSpecifier::dArg: - case ConversionSpecifier::iArg: - case ConversionSpecifier::oArg: - case ConversionSpecifier::uArg: - case ConversionSpecifier::xArg: - case ConversionSpecifier::XArg: - case ConversionSpecifier::fArg: - case ConversionSpecifier::FArg: - case ConversionSpecifier::eArg: - case ConversionSpecifier::EArg: - case ConversionSpecifier::gArg: - case ConversionSpecifier::GArg: - case ConversionSpecifier::aArg: - case ConversionSpecifier::AArg: - case ConversionSpecifier::sArg: - case ConversionSpecifier::pArg: - case ConversionSpecifier::nArg: - case ConversionSpecifier::ObjCObjArg: - case ConversionSpecifier::ScanListArg: - case ConversionSpecifier::PercentArg: - case ConversionSpecifier::PArg: - return true; - case ConversionSpecifier::CArg: - case ConversionSpecifier::SArg: - return LangOpt.ObjC1 || LangOpt.ObjC2; - case ConversionSpecifier::InvalidSpecifier: - case ConversionSpecifier::FreeBSDbArg: - case ConversionSpecifier::FreeBSDDArg: - case ConversionSpecifier::FreeBSDrArg: - case ConversionSpecifier::FreeBSDyArg: - case ConversionSpecifier::PrintErrno: - case ConversionSpecifier::DArg: - case ConversionSpecifier::OArg: - case ConversionSpecifier::UArg: - case ConversionSpecifier::ZArg: - return false; - } - llvm_unreachable("Invalid ConversionSpecifier Kind!"); -} - -bool FormatSpecifier::hasStandardLengthConversionCombination() const { - if (LM.getKind() == LengthModifier::AsLongDouble) { - switch(CS.getKind()) { - case ConversionSpecifier::dArg: - case ConversionSpecifier::iArg: - case ConversionSpecifier::oArg: - case ConversionSpecifier::uArg: - case ConversionSpecifier::xArg: - case ConversionSpecifier::XArg: - return false; - default: - return true; - } - } - return true; -} - -Optional<LengthModifier> FormatSpecifier::getCorrectedLengthModifier() const { - if (CS.isAnyIntArg() || CS.getKind() == ConversionSpecifier::nArg) { - if (LM.getKind() == LengthModifier::AsLongDouble || - LM.getKind() == LengthModifier::AsQuad) { - LengthModifier FixedLM(LM); - FixedLM.setKind(LengthModifier::AsLongLong); - return FixedLM; - } - } - - return None; -} - -bool FormatSpecifier::namedTypeToLengthModifier(QualType QT, - LengthModifier &LM) { - assert(isa<TypedefType>(QT) && "Expected a TypedefType"); - const TypedefNameDecl *Typedef = cast<TypedefType>(QT)->getDecl(); - - for (;;) { - const IdentifierInfo *Identifier = Typedef->getIdentifier(); - if (Identifier->getName() == "size_t") { - LM.setKind(LengthModifier::AsSizeT); - return true; - } else if (Identifier->getName() == "ssize_t") { - // Not C99, but common in Unix. - LM.setKind(LengthModifier::AsSizeT); - return true; - } else if (Identifier->getName() == "intmax_t") { - LM.setKind(LengthModifier::AsIntMax); - return true; - } else if (Identifier->getName() == "uintmax_t") { - LM.setKind(LengthModifier::AsIntMax); - return true; - } else if (Identifier->getName() == "ptrdiff_t") { - LM.setKind(LengthModifier::AsPtrDiff); - return true; - } - - QualType T = Typedef->getUnderlyingType(); - if (!isa<TypedefType>(T)) - break; - - Typedef = cast<TypedefType>(T)->getDecl(); - } - return false; -} diff --git a/lib/Analysis/FormatStringParsing.h b/lib/Analysis/FormatStringParsing.h deleted file mode 100644 index a63140b366cd..000000000000 --- a/lib/Analysis/FormatStringParsing.h +++ /dev/null @@ -1,79 +0,0 @@ -#ifndef LLVM_CLANG_LIB_ANALYSIS_FORMATSTRINGPARSING_H -#define LLVM_CLANG_LIB_ANALYSIS_FORMATSTRINGPARSING_H - -#include "clang/AST/ASTContext.h" -#include "clang/AST/Type.h" -#include "clang/Analysis/Analyses/FormatString.h" - -namespace clang { - -class LangOptions; - -template <typename T> -class UpdateOnReturn { - T &ValueToUpdate; - const T &ValueToCopy; -public: - UpdateOnReturn(T &valueToUpdate, const T &valueToCopy) - : ValueToUpdate(valueToUpdate), ValueToCopy(valueToCopy) {} - - ~UpdateOnReturn() { - ValueToUpdate = ValueToCopy; - } -}; - -namespace analyze_format_string { - -OptionalAmount ParseAmount(const char *&Beg, const char *E); -OptionalAmount ParseNonPositionAmount(const char *&Beg, const char *E, - unsigned &argIndex); - -OptionalAmount ParsePositionAmount(FormatStringHandler &H, - const char *Start, const char *&Beg, - const char *E, PositionContext p); - -bool ParseFieldWidth(FormatStringHandler &H, - FormatSpecifier &CS, - const char *Start, const char *&Beg, const char *E, - unsigned *argIndex); - -bool ParseArgPosition(FormatStringHandler &H, - FormatSpecifier &CS, const char *Start, - const char *&Beg, const char *E); - -/// Returns true if a LengthModifier was parsed and installed in the -/// FormatSpecifier& argument, and false otherwise. -bool ParseLengthModifier(FormatSpecifier &FS, const char *&Beg, const char *E, - const LangOptions &LO, bool IsScanf = false); - -/// Returns true if the invalid specifier in \p SpecifierBegin is a UTF-8 -/// string; check that it won't go further than \p FmtStrEnd and write -/// up the total size in \p Len. -bool ParseUTF8InvalidSpecifier(const char *SpecifierBegin, - const char *FmtStrEnd, unsigned &Len); - -template <typename T> class SpecifierResult { - T FS; - const char *Start; - bool Stop; -public: - SpecifierResult(bool stop = false) - : Start(nullptr), Stop(stop) {} - SpecifierResult(const char *start, - const T &fs) - : FS(fs), Start(start), Stop(false) {} - - const char *getStart() const { return Start; } - bool shouldStop() const { return Stop; } - bool hasValue() const { return Start != nullptr; } - const T &getValue() const { - assert(hasValue()); - return FS; - } - const T &getValue() { return FS; } -}; - -} // end analyze_format_string namespace -} // end clang namespace - -#endif diff --git a/lib/Analysis/LiveVariables.cpp b/lib/Analysis/LiveVariables.cpp index 05bc1a5d102c..afe2d264907f 100644 --- a/lib/Analysis/LiveVariables.cpp +++ b/lib/Analysis/LiveVariables.cpp @@ -93,6 +93,7 @@ public: LiveVariables::Observer *obs = nullptr); void dumpBlockLiveness(const SourceManager& M); + void dumpStmtLiveness(const SourceManager& M); LiveVariablesImpl(AnalysisDeclContext &ac, bool KillAtAssign) : analysisContext(ac), @@ -237,8 +238,8 @@ static const Stmt *LookThroughStmt(const Stmt *S) { while (S) { if (const Expr *Ex = dyn_cast<Expr>(S)) S = Ex->IgnoreParens(); - if (const ExprWithCleanups *EWC = dyn_cast<ExprWithCleanups>(S)) { - S = EWC->getSubExpr(); + if (const FullExpr *FE = dyn_cast<FullExpr>(S)) { + S = FE->getSubExpr(); continue; } if (const OpaqueValueExpr *OVE = dyn_cast<OpaqueValueExpr>(S)) { @@ -327,6 +328,35 @@ void TransferFunctions::Visit(Stmt *S) { // No need to unconditionally visit subexpressions. return; } + case Stmt::IfStmtClass: { + // If one of the branches is an expression rather than a compound + // statement, it will be bad if we mark it as live at the terminator + // of the if-statement (i.e., immediately after the condition expression). + AddLiveStmt(val.liveStmts, LV.SSetFact, cast<IfStmt>(S)->getCond()); + return; + } + case Stmt::WhileStmtClass: { + // If the loop body is an expression rather than a compound statement, + // it will be bad if we mark it as live at the terminator of the loop + // (i.e., immediately after the condition expression). + AddLiveStmt(val.liveStmts, LV.SSetFact, cast<WhileStmt>(S)->getCond()); + return; + } + case Stmt::DoStmtClass: { + // If the loop body is an expression rather than a compound statement, + // it will be bad if we mark it as live at the terminator of the loop + // (i.e., immediately after the condition expression). + AddLiveStmt(val.liveStmts, LV.SSetFact, cast<DoStmt>(S)->getCond()); + return; + } + case Stmt::ForStmtClass: { + // If the loop body is an expression rather than a compound statement, + // it will be bad if we mark it as live at the terminator of the loop + // (i.e., immediately after the condition expression). + AddLiveStmt(val.liveStmts, LV.SSetFact, cast<ForStmt>(S)->getCond()); + return; + } + } for (Stmt *Child : S->children()) { @@ -597,7 +627,7 @@ void LiveVariablesImpl::dumpBlockLiveness(const SourceManager &M) { it != ei; ++it) { vec.push_back(it->first); } - llvm::sort(vec.begin(), vec.end(), [](const CFGBlock *A, const CFGBlock *B) { + llvm::sort(vec, [](const CFGBlock *A, const CFGBlock *B) { return A->getBlockID() < B->getBlockID(); }); @@ -617,21 +647,38 @@ void LiveVariablesImpl::dumpBlockLiveness(const SourceManager &M) { declVec.push_back(*si); } - llvm::sort(declVec.begin(), declVec.end(), - [](const Decl *A, const Decl *B) { - return A->getLocStart() < B->getLocStart(); + llvm::sort(declVec, [](const Decl *A, const Decl *B) { + return A->getBeginLoc() < B->getBeginLoc(); }); for (std::vector<const VarDecl*>::iterator di = declVec.begin(), de = declVec.end(); di != de; ++di) { llvm::errs() << " " << (*di)->getDeclName().getAsString() << " <"; - (*di)->getLocation().dump(M); + (*di)->getLocation().print(llvm::errs(), M); llvm::errs() << ">\n"; } } llvm::errs() << "\n"; } +void LiveVariables::dumpStmtLiveness(const SourceManager &M) { + getImpl(impl).dumpStmtLiveness(M); +} + +void LiveVariablesImpl::dumpStmtLiveness(const SourceManager &M) { + // Don't iterate over blockEndsToLiveness directly because it's not sorted. + for (auto I : *analysisContext.getCFG()) { + + llvm::errs() << "\n[ B" << I->getBlockID() + << " (live statements at block exit) ]\n"; + for (auto S : blocksEndToLiveness[I].liveStmts) { + llvm::errs() << "\n"; + S->dump(); + } + llvm::errs() << "\n"; + } +} + const void *LiveVariables::getTag() { static int x; return &x; } const void *RelaxedLiveVariables::getTag() { static int x; return &x; } diff --git a/lib/Analysis/OSLog.cpp b/lib/Analysis/OSLog.cpp deleted file mode 100644 index b2983932ea22..000000000000 --- a/lib/Analysis/OSLog.cpp +++ /dev/null @@ -1,203 +0,0 @@ -// TODO: header template - -#include "clang/Analysis/Analyses/OSLog.h" -#include "clang/AST/Attr.h" -#include "clang/AST/Decl.h" -#include "clang/AST/DeclCXX.h" -#include "clang/AST/ExprObjC.h" -#include "clang/Analysis/Analyses/FormatString.h" -#include "clang/Basic/Builtins.h" -#include "llvm/ADT/SmallBitVector.h" - -using namespace clang; - -using clang::analyze_os_log::OSLogBufferItem; -using clang::analyze_os_log::OSLogBufferLayout; - -namespace { -class OSLogFormatStringHandler - : public analyze_format_string::FormatStringHandler { -private: - struct ArgData { - const Expr *E = nullptr; - Optional<OSLogBufferItem::Kind> Kind; - Optional<unsigned> Size; - Optional<const Expr *> Count; - Optional<const Expr *> Precision; - Optional<const Expr *> FieldWidth; - unsigned char Flags = 0; - }; - SmallVector<ArgData, 4> ArgsData; - ArrayRef<const Expr *> Args; - - OSLogBufferItem::Kind - getKind(analyze_format_string::ConversionSpecifier::Kind K) { - switch (K) { - case clang::analyze_format_string::ConversionSpecifier::sArg: // "%s" - return OSLogBufferItem::StringKind; - case clang::analyze_format_string::ConversionSpecifier::SArg: // "%S" - return OSLogBufferItem::WideStringKind; - case clang::analyze_format_string::ConversionSpecifier::PArg: { // "%P" - return OSLogBufferItem::PointerKind; - case clang::analyze_format_string::ConversionSpecifier::ObjCObjArg: // "%@" - return OSLogBufferItem::ObjCObjKind; - case clang::analyze_format_string::ConversionSpecifier::PrintErrno: // "%m" - return OSLogBufferItem::ErrnoKind; - default: - return OSLogBufferItem::ScalarKind; - } - } - } - -public: - OSLogFormatStringHandler(ArrayRef<const Expr *> Args) : Args(Args) { - ArgsData.reserve(Args.size()); - } - - virtual bool HandlePrintfSpecifier(const analyze_printf::PrintfSpecifier &FS, - const char *StartSpecifier, - unsigned SpecifierLen) { - if (!FS.consumesDataArgument() && - FS.getConversionSpecifier().getKind() != - clang::analyze_format_string::ConversionSpecifier::PrintErrno) - return true; - - ArgsData.emplace_back(); - unsigned ArgIndex = FS.getArgIndex(); - if (ArgIndex < Args.size()) - ArgsData.back().E = Args[ArgIndex]; - - // First get the Kind - ArgsData.back().Kind = getKind(FS.getConversionSpecifier().getKind()); - if (ArgsData.back().Kind != OSLogBufferItem::ErrnoKind && - !ArgsData.back().E) { - // missing argument - ArgsData.pop_back(); - return false; - } - - switch (FS.getConversionSpecifier().getKind()) { - case clang::analyze_format_string::ConversionSpecifier::sArg: // "%s" - case clang::analyze_format_string::ConversionSpecifier::SArg: { // "%S" - auto &precision = FS.getPrecision(); - switch (precision.getHowSpecified()) { - case clang::analyze_format_string::OptionalAmount::NotSpecified: // "%s" - break; - case clang::analyze_format_string::OptionalAmount::Constant: // "%.16s" - ArgsData.back().Size = precision.getConstantAmount(); - break; - case clang::analyze_format_string::OptionalAmount::Arg: // "%.*s" - ArgsData.back().Count = Args[precision.getArgIndex()]; - break; - case clang::analyze_format_string::OptionalAmount::Invalid: - return false; - } - break; - } - case clang::analyze_format_string::ConversionSpecifier::PArg: { // "%P" - auto &precision = FS.getPrecision(); - switch (precision.getHowSpecified()) { - case clang::analyze_format_string::OptionalAmount::NotSpecified: // "%P" - return false; // length must be supplied with pointer format specifier - case clang::analyze_format_string::OptionalAmount::Constant: // "%.16P" - ArgsData.back().Size = precision.getConstantAmount(); - break; - case clang::analyze_format_string::OptionalAmount::Arg: // "%.*P" - ArgsData.back().Count = Args[precision.getArgIndex()]; - break; - case clang::analyze_format_string::OptionalAmount::Invalid: - return false; - } - break; - } - default: - if (FS.getPrecision().hasDataArgument()) { - ArgsData.back().Precision = Args[FS.getPrecision().getArgIndex()]; - } - break; - } - if (FS.getFieldWidth().hasDataArgument()) { - ArgsData.back().FieldWidth = Args[FS.getFieldWidth().getArgIndex()]; - } - - if (FS.isPrivate()) { - ArgsData.back().Flags |= OSLogBufferItem::IsPrivate; - } - if (FS.isPublic()) { - ArgsData.back().Flags |= OSLogBufferItem::IsPublic; - } - return true; - } - - void computeLayout(ASTContext &Ctx, OSLogBufferLayout &Layout) const { - Layout.Items.clear(); - for (auto &Data : ArgsData) { - if (Data.FieldWidth) { - CharUnits Size = Ctx.getTypeSizeInChars((*Data.FieldWidth)->getType()); - Layout.Items.emplace_back(OSLogBufferItem::ScalarKind, *Data.FieldWidth, - Size, 0); - } - if (Data.Precision) { - CharUnits Size = Ctx.getTypeSizeInChars((*Data.Precision)->getType()); - Layout.Items.emplace_back(OSLogBufferItem::ScalarKind, *Data.Precision, - Size, 0); - } - if (Data.Count) { - // "%.*P" has an extra "count" that we insert before the argument. - CharUnits Size = Ctx.getTypeSizeInChars((*Data.Count)->getType()); - Layout.Items.emplace_back(OSLogBufferItem::CountKind, *Data.Count, Size, - 0); - } - if (Data.Size) - Layout.Items.emplace_back(Ctx, CharUnits::fromQuantity(*Data.Size), - Data.Flags); - if (Data.Kind) { - CharUnits Size; - if (*Data.Kind == OSLogBufferItem::ErrnoKind) - Size = CharUnits::Zero(); - else - Size = Ctx.getTypeSizeInChars(Data.E->getType()); - Layout.Items.emplace_back(*Data.Kind, Data.E, Size, Data.Flags); - } else { - auto Size = Ctx.getTypeSizeInChars(Data.E->getType()); - Layout.Items.emplace_back(OSLogBufferItem::ScalarKind, Data.E, Size, - Data.Flags); - } - } - } -}; -} // end anonymous namespace - -bool clang::analyze_os_log::computeOSLogBufferLayout( - ASTContext &Ctx, const CallExpr *E, OSLogBufferLayout &Layout) { - ArrayRef<const Expr *> Args(E->getArgs(), E->getArgs() + E->getNumArgs()); - - const Expr *StringArg; - ArrayRef<const Expr *> VarArgs; - switch (E->getBuiltinCallee()) { - case Builtin::BI__builtin_os_log_format_buffer_size: - assert(E->getNumArgs() >= 1 && - "__builtin_os_log_format_buffer_size takes at least 1 argument"); - StringArg = E->getArg(0); - VarArgs = Args.slice(1); - break; - case Builtin::BI__builtin_os_log_format: - assert(E->getNumArgs() >= 2 && - "__builtin_os_log_format takes at least 2 arguments"); - StringArg = E->getArg(1); - VarArgs = Args.slice(2); - break; - default: - llvm_unreachable("non-os_log builtin passed to computeOSLogBufferLayout"); - } - - const StringLiteral *Lit = cast<StringLiteral>(StringArg->IgnoreParenCasts()); - assert(Lit && (Lit->isAscii() || Lit->isUTF8())); - StringRef Data = Lit->getString(); - OSLogFormatStringHandler H(VarArgs); - ParsePrintfString(H, Data.begin(), Data.end(), Ctx.getLangOpts(), - Ctx.getTargetInfo(), /*isFreeBSDKPrintf*/ false); - - H.computeLayout(Ctx, Layout); - return true; -} diff --git a/lib/Analysis/PrintfFormatString.cpp b/lib/Analysis/PrintfFormatString.cpp deleted file mode 100644 index dcb15c5e3758..000000000000 --- a/lib/Analysis/PrintfFormatString.cpp +++ /dev/null @@ -1,1029 +0,0 @@ -//== PrintfFormatString.cpp - Analysis of printf format strings --*- C++ -*-==// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// Handling of format string in printf and friends. The structure of format -// strings for fprintf() are described in C99 7.19.6.1. -// -//===----------------------------------------------------------------------===// - -#include "clang/Analysis/Analyses/FormatString.h" -#include "clang/Analysis/Analyses/OSLog.h" -#include "FormatStringParsing.h" -#include "clang/Basic/TargetInfo.h" - -using clang::analyze_format_string::ArgType; -using clang::analyze_format_string::FormatStringHandler; -using clang::analyze_format_string::LengthModifier; -using clang::analyze_format_string::OptionalAmount; -using clang::analyze_format_string::ConversionSpecifier; -using clang::analyze_printf::PrintfSpecifier; - -using namespace clang; - -typedef clang::analyze_format_string::SpecifierResult<PrintfSpecifier> - PrintfSpecifierResult; - -//===----------------------------------------------------------------------===// -// Methods for parsing format strings. -//===----------------------------------------------------------------------===// - -using analyze_format_string::ParseNonPositionAmount; - -static bool ParsePrecision(FormatStringHandler &H, PrintfSpecifier &FS, - const char *Start, const char *&Beg, const char *E, - unsigned *argIndex) { - if (argIndex) { - FS.setPrecision(ParseNonPositionAmount(Beg, E, *argIndex)); - } else { - const OptionalAmount Amt = ParsePositionAmount(H, Start, Beg, E, - analyze_format_string::PrecisionPos); - if (Amt.isInvalid()) - return true; - FS.setPrecision(Amt); - } - return false; -} - -static bool ParseObjCFlags(FormatStringHandler &H, PrintfSpecifier &FS, - const char *FlagBeg, const char *E, bool Warn) { - StringRef Flag(FlagBeg, E - FlagBeg); - // Currently there is only one flag. - if (Flag == "tt") { - FS.setHasObjCTechnicalTerm(FlagBeg); - return false; - } - // Handle either the case of no flag or an invalid flag. - if (Warn) { - if (Flag == "") - H.HandleEmptyObjCModifierFlag(FlagBeg, E - FlagBeg); - else - H.HandleInvalidObjCModifierFlag(FlagBeg, E - FlagBeg); - } - return true; -} - -static PrintfSpecifierResult ParsePrintfSpecifier(FormatStringHandler &H, - const char *&Beg, - const char *E, - unsigned &argIndex, - const LangOptions &LO, - const TargetInfo &Target, - bool Warn, - bool isFreeBSDKPrintf) { - - using namespace clang::analyze_format_string; - using namespace clang::analyze_printf; - - const char *I = Beg; - const char *Start = nullptr; - UpdateOnReturn <const char*> UpdateBeg(Beg, I); - - // Look for a '%' character that indicates the start of a format specifier. - for ( ; I != E ; ++I) { - char c = *I; - if (c == '\0') { - // Detect spurious null characters, which are likely errors. - H.HandleNullChar(I); - return true; - } - if (c == '%') { - Start = I++; // Record the start of the format specifier. - break; - } - } - - // No format specifier found? - if (!Start) - return false; - - if (I == E) { - // No more characters left? - if (Warn) - H.HandleIncompleteSpecifier(Start, E - Start); - return true; - } - - PrintfSpecifier FS; - if (ParseArgPosition(H, FS, Start, I, E)) - return true; - - if (I == E) { - // No more characters left? - if (Warn) - H.HandleIncompleteSpecifier(Start, E - Start); - return true; - } - - if (*I == '{') { - ++I; - unsigned char PrivacyFlags = 0; - StringRef MatchedStr; - - do { - StringRef Str(I, E - I); - std::string Match = "^[\t\n\v\f\r ]*(private|public)[\t\n\v\f\r ]*(,|})"; - llvm::Regex R(Match); - SmallVector<StringRef, 2> Matches; - - if (R.match(Str, &Matches)) { - MatchedStr = Matches[1]; - I += Matches[0].size(); - - // Set the privacy flag if the privacy annotation in the - // comma-delimited segment is at least as strict as the privacy - // annotations in previous comma-delimited segments. - if (MatchedStr.equals("private")) - PrivacyFlags = clang::analyze_os_log::OSLogBufferItem::IsPrivate; - else if (PrivacyFlags == 0 && MatchedStr.equals("public")) - PrivacyFlags = clang::analyze_os_log::OSLogBufferItem::IsPublic; - } else { - size_t CommaOrBracePos = - Str.find_if([](char c) { return c == ',' || c == '}'; }); - - if (CommaOrBracePos == StringRef::npos) { - // Neither a comma nor the closing brace was found. - if (Warn) - H.HandleIncompleteSpecifier(Start, E - Start); - return true; - } - - I += CommaOrBracePos + 1; - } - // Continue until the closing brace is found. - } while (*(I - 1) == ','); - - // Set the privacy flag. - switch (PrivacyFlags) { - case 0: - break; - case clang::analyze_os_log::OSLogBufferItem::IsPrivate: - FS.setIsPrivate(MatchedStr.data()); - break; - case clang::analyze_os_log::OSLogBufferItem::IsPublic: - FS.setIsPublic(MatchedStr.data()); - break; - default: - llvm_unreachable("Unexpected privacy flag value"); - } - } - - // Look for flags (if any). - bool hasMore = true; - for ( ; I != E; ++I) { - switch (*I) { - default: hasMore = false; break; - case '\'': - // FIXME: POSIX specific. Always accept? - FS.setHasThousandsGrouping(I); - break; - case '-': FS.setIsLeftJustified(I); break; - case '+': FS.setHasPlusPrefix(I); break; - case ' ': FS.setHasSpacePrefix(I); break; - case '#': FS.setHasAlternativeForm(I); break; - case '0': FS.setHasLeadingZeros(I); break; - } - if (!hasMore) - break; - } - - if (I == E) { - // No more characters left? - if (Warn) - H.HandleIncompleteSpecifier(Start, E - Start); - return true; - } - - // Look for the field width (if any). - if (ParseFieldWidth(H, FS, Start, I, E, - FS.usesPositionalArg() ? nullptr : &argIndex)) - return true; - - if (I == E) { - // No more characters left? - if (Warn) - H.HandleIncompleteSpecifier(Start, E - Start); - return true; - } - - // Look for the precision (if any). - if (*I == '.') { - ++I; - if (I == E) { - if (Warn) - H.HandleIncompleteSpecifier(Start, E - Start); - return true; - } - - if (ParsePrecision(H, FS, Start, I, E, - FS.usesPositionalArg() ? nullptr : &argIndex)) - return true; - - if (I == E) { - // No more characters left? - if (Warn) - H.HandleIncompleteSpecifier(Start, E - Start); - return true; - } - } - - // Look for the length modifier. - if (ParseLengthModifier(FS, I, E, LO) && I == E) { - // No more characters left? - if (Warn) - H.HandleIncompleteSpecifier(Start, E - Start); - return true; - } - - // Look for the Objective-C modifier flags, if any. - // We parse these here, even if they don't apply to - // the conversion specifier, and then emit an error - // later if the conversion specifier isn't '@'. This - // enables better recovery, and we don't know if - // these flags are applicable until later. - const char *ObjCModifierFlagsStart = nullptr, - *ObjCModifierFlagsEnd = nullptr; - if (*I == '[') { - ObjCModifierFlagsStart = I; - ++I; - auto flagStart = I; - for (;; ++I) { - ObjCModifierFlagsEnd = I; - if (I == E) { - if (Warn) - H.HandleIncompleteSpecifier(Start, E - Start); - return true; - } - // Did we find the closing ']'? - if (*I == ']') { - if (ParseObjCFlags(H, FS, flagStart, I, Warn)) - return true; - ++I; - break; - } - // There are no separators defined yet for multiple - // Objective-C modifier flags. When those are - // defined, this is the place to check. - } - } - - if (*I == '\0') { - // Detect spurious null characters, which are likely errors. - H.HandleNullChar(I); - return true; - } - - // Finally, look for the conversion specifier. - const char *conversionPosition = I++; - ConversionSpecifier::Kind k = ConversionSpecifier::InvalidSpecifier; - switch (*conversionPosition) { - default: - break; - // C99: 7.19.6.1 (section 8). - case '%': k = ConversionSpecifier::PercentArg; break; - case 'A': k = ConversionSpecifier::AArg; break; - case 'E': k = ConversionSpecifier::EArg; break; - case 'F': k = ConversionSpecifier::FArg; break; - case 'G': k = ConversionSpecifier::GArg; break; - case 'X': k = ConversionSpecifier::XArg; break; - case 'a': k = ConversionSpecifier::aArg; break; - case 'c': k = ConversionSpecifier::cArg; break; - case 'd': k = ConversionSpecifier::dArg; break; - case 'e': k = ConversionSpecifier::eArg; break; - case 'f': k = ConversionSpecifier::fArg; break; - case 'g': k = ConversionSpecifier::gArg; break; - case 'i': k = ConversionSpecifier::iArg; break; - case 'n': k = ConversionSpecifier::nArg; break; - case 'o': k = ConversionSpecifier::oArg; break; - case 'p': k = ConversionSpecifier::pArg; break; - case 's': k = ConversionSpecifier::sArg; break; - case 'u': k = ConversionSpecifier::uArg; break; - case 'x': k = ConversionSpecifier::xArg; break; - // POSIX specific. - case 'C': k = ConversionSpecifier::CArg; break; - case 'S': k = ConversionSpecifier::SArg; break; - // Apple extension for os_log - case 'P': - k = ConversionSpecifier::PArg; - break; - // Objective-C. - case '@': k = ConversionSpecifier::ObjCObjArg; break; - // Glibc specific. - case 'm': k = ConversionSpecifier::PrintErrno; break; - // FreeBSD kernel specific. - case 'b': - if (isFreeBSDKPrintf) - k = ConversionSpecifier::FreeBSDbArg; // int followed by char * - break; - case 'r': - if (isFreeBSDKPrintf) - k = ConversionSpecifier::FreeBSDrArg; // int - break; - case 'y': - if (isFreeBSDKPrintf) - k = ConversionSpecifier::FreeBSDyArg; // int - break; - // Apple-specific. - case 'D': - if (isFreeBSDKPrintf) - k = ConversionSpecifier::FreeBSDDArg; // void * followed by char * - else if (Target.getTriple().isOSDarwin()) - k = ConversionSpecifier::DArg; - break; - case 'O': - if (Target.getTriple().isOSDarwin()) - k = ConversionSpecifier::OArg; - break; - case 'U': - if (Target.getTriple().isOSDarwin()) - k = ConversionSpecifier::UArg; - break; - // MS specific. - case 'Z': - if (Target.getTriple().isOSMSVCRT()) - k = ConversionSpecifier::ZArg; - } - - // Check to see if we used the Objective-C modifier flags with - // a conversion specifier other than '@'. - if (k != ConversionSpecifier::ObjCObjArg && - k != ConversionSpecifier::InvalidSpecifier && - ObjCModifierFlagsStart) { - H.HandleObjCFlagsWithNonObjCConversion(ObjCModifierFlagsStart, - ObjCModifierFlagsEnd + 1, - conversionPosition); - return true; - } - - PrintfConversionSpecifier CS(conversionPosition, k); - FS.setConversionSpecifier(CS); - if (CS.consumesDataArgument() && !FS.usesPositionalArg()) - FS.setArgIndex(argIndex++); - // FreeBSD kernel specific. - if (k == ConversionSpecifier::FreeBSDbArg || - k == ConversionSpecifier::FreeBSDDArg) - argIndex++; - - if (k == ConversionSpecifier::InvalidSpecifier) { - unsigned Len = I - Start; - if (ParseUTF8InvalidSpecifier(Start, E, Len)) { - CS.setEndScanList(Start + Len); - FS.setConversionSpecifier(CS); - } - // Assume the conversion takes one argument. - return !H.HandleInvalidPrintfConversionSpecifier(FS, Start, Len); - } - return PrintfSpecifierResult(Start, FS); -} - -bool clang::analyze_format_string::ParsePrintfString(FormatStringHandler &H, - const char *I, - const char *E, - const LangOptions &LO, - const TargetInfo &Target, - bool isFreeBSDKPrintf) { - - unsigned argIndex = 0; - - // Keep looking for a format specifier until we have exhausted the string. - while (I != E) { - const PrintfSpecifierResult &FSR = ParsePrintfSpecifier(H, I, E, argIndex, - LO, Target, true, - isFreeBSDKPrintf); - // Did a fail-stop error of any kind occur when parsing the specifier? - // If so, don't do any more processing. - if (FSR.shouldStop()) - return true; - // Did we exhaust the string or encounter an error that - // we can recover from? - if (!FSR.hasValue()) - continue; - // We have a format specifier. Pass it to the callback. - if (!H.HandlePrintfSpecifier(FSR.getValue(), FSR.getStart(), - I - FSR.getStart())) - return true; - } - assert(I == E && "Format string not exhausted"); - return false; -} - -bool clang::analyze_format_string::ParseFormatStringHasSArg(const char *I, - const char *E, - const LangOptions &LO, - const TargetInfo &Target) { - - unsigned argIndex = 0; - - // Keep looking for a %s format specifier until we have exhausted the string. - FormatStringHandler H; - while (I != E) { - const PrintfSpecifierResult &FSR = ParsePrintfSpecifier(H, I, E, argIndex, - LO, Target, false, - false); - // Did a fail-stop error of any kind occur when parsing the specifier? - // If so, don't do any more processing. - if (FSR.shouldStop()) - return false; - // Did we exhaust the string or encounter an error that - // we can recover from? - if (!FSR.hasValue()) - continue; - const analyze_printf::PrintfSpecifier &FS = FSR.getValue(); - // Return true if this a %s format specifier. - if (FS.getConversionSpecifier().getKind() == ConversionSpecifier::Kind::sArg) - return true; - } - return false; -} - -//===----------------------------------------------------------------------===// -// Methods on PrintfSpecifier. -//===----------------------------------------------------------------------===// - -ArgType PrintfSpecifier::getArgType(ASTContext &Ctx, - bool IsObjCLiteral) const { - const PrintfConversionSpecifier &CS = getConversionSpecifier(); - - if (!CS.consumesDataArgument()) - return ArgType::Invalid(); - - if (CS.getKind() == ConversionSpecifier::cArg) - switch (LM.getKind()) { - case LengthModifier::None: - return Ctx.IntTy; - case LengthModifier::AsLong: - case LengthModifier::AsWide: - return ArgType(ArgType::WIntTy, "wint_t"); - case LengthModifier::AsShort: - if (Ctx.getTargetInfo().getTriple().isOSMSVCRT()) - return Ctx.IntTy; - LLVM_FALLTHROUGH; - default: - return ArgType::Invalid(); - } - - if (CS.isIntArg()) - switch (LM.getKind()) { - case LengthModifier::AsLongDouble: - // GNU extension. - return Ctx.LongLongTy; - case LengthModifier::None: - return Ctx.IntTy; - case LengthModifier::AsInt32: - return ArgType(Ctx.IntTy, "__int32"); - case LengthModifier::AsChar: return ArgType::AnyCharTy; - case LengthModifier::AsShort: return Ctx.ShortTy; - case LengthModifier::AsLong: return Ctx.LongTy; - case LengthModifier::AsLongLong: - case LengthModifier::AsQuad: - return Ctx.LongLongTy; - case LengthModifier::AsInt64: - return ArgType(Ctx.LongLongTy, "__int64"); - case LengthModifier::AsIntMax: - return ArgType(Ctx.getIntMaxType(), "intmax_t"); - case LengthModifier::AsSizeT: - return ArgType::makeSizeT(ArgType(Ctx.getSignedSizeType(), "ssize_t")); - case LengthModifier::AsInt3264: - return Ctx.getTargetInfo().getTriple().isArch64Bit() - ? ArgType(Ctx.LongLongTy, "__int64") - : ArgType(Ctx.IntTy, "__int32"); - case LengthModifier::AsPtrDiff: - return ArgType::makePtrdiffT( - ArgType(Ctx.getPointerDiffType(), "ptrdiff_t")); - case LengthModifier::AsAllocate: - case LengthModifier::AsMAllocate: - case LengthModifier::AsWide: - return ArgType::Invalid(); - } - - if (CS.isUIntArg()) - switch (LM.getKind()) { - case LengthModifier::AsLongDouble: - // GNU extension. - return Ctx.UnsignedLongLongTy; - case LengthModifier::None: - return Ctx.UnsignedIntTy; - case LengthModifier::AsInt32: - return ArgType(Ctx.UnsignedIntTy, "unsigned __int32"); - case LengthModifier::AsChar: return Ctx.UnsignedCharTy; - case LengthModifier::AsShort: return Ctx.UnsignedShortTy; - case LengthModifier::AsLong: return Ctx.UnsignedLongTy; - case LengthModifier::AsLongLong: - case LengthModifier::AsQuad: - return Ctx.UnsignedLongLongTy; - case LengthModifier::AsInt64: - return ArgType(Ctx.UnsignedLongLongTy, "unsigned __int64"); - case LengthModifier::AsIntMax: - return ArgType(Ctx.getUIntMaxType(), "uintmax_t"); - case LengthModifier::AsSizeT: - return ArgType::makeSizeT(ArgType(Ctx.getSizeType(), "size_t")); - case LengthModifier::AsInt3264: - return Ctx.getTargetInfo().getTriple().isArch64Bit() - ? ArgType(Ctx.UnsignedLongLongTy, "unsigned __int64") - : ArgType(Ctx.UnsignedIntTy, "unsigned __int32"); - case LengthModifier::AsPtrDiff: - return ArgType::makePtrdiffT( - ArgType(Ctx.getUnsignedPointerDiffType(), "unsigned ptrdiff_t")); - case LengthModifier::AsAllocate: - case LengthModifier::AsMAllocate: - case LengthModifier::AsWide: - return ArgType::Invalid(); - } - - if (CS.isDoubleArg()) { - if (LM.getKind() == LengthModifier::AsLongDouble) - return Ctx.LongDoubleTy; - return Ctx.DoubleTy; - } - - if (CS.getKind() == ConversionSpecifier::nArg) { - switch (LM.getKind()) { - case LengthModifier::None: - return ArgType::PtrTo(Ctx.IntTy); - case LengthModifier::AsChar: - return ArgType::PtrTo(Ctx.SignedCharTy); - case LengthModifier::AsShort: - return ArgType::PtrTo(Ctx.ShortTy); - case LengthModifier::AsLong: - return ArgType::PtrTo(Ctx.LongTy); - case LengthModifier::AsLongLong: - case LengthModifier::AsQuad: - return ArgType::PtrTo(Ctx.LongLongTy); - case LengthModifier::AsIntMax: - return ArgType::PtrTo(ArgType(Ctx.getIntMaxType(), "intmax_t")); - case LengthModifier::AsSizeT: - return ArgType::PtrTo(ArgType(Ctx.getSignedSizeType(), "ssize_t")); - case LengthModifier::AsPtrDiff: - return ArgType::PtrTo(ArgType(Ctx.getPointerDiffType(), "ptrdiff_t")); - case LengthModifier::AsLongDouble: - return ArgType(); // FIXME: Is this a known extension? - case LengthModifier::AsAllocate: - case LengthModifier::AsMAllocate: - case LengthModifier::AsInt32: - case LengthModifier::AsInt3264: - case LengthModifier::AsInt64: - case LengthModifier::AsWide: - return ArgType::Invalid(); - } - } - - switch (CS.getKind()) { - case ConversionSpecifier::sArg: - if (LM.getKind() == LengthModifier::AsWideChar) { - if (IsObjCLiteral) - return ArgType(Ctx.getPointerType(Ctx.UnsignedShortTy.withConst()), - "const unichar *"); - return ArgType(ArgType::WCStrTy, "wchar_t *"); - } - if (LM.getKind() == LengthModifier::AsWide) - return ArgType(ArgType::WCStrTy, "wchar_t *"); - return ArgType::CStrTy; - case ConversionSpecifier::SArg: - if (IsObjCLiteral) - return ArgType(Ctx.getPointerType(Ctx.UnsignedShortTy.withConst()), - "const unichar *"); - if (Ctx.getTargetInfo().getTriple().isOSMSVCRT() && - LM.getKind() == LengthModifier::AsShort) - return ArgType::CStrTy; - return ArgType(ArgType::WCStrTy, "wchar_t *"); - case ConversionSpecifier::CArg: - if (IsObjCLiteral) - return ArgType(Ctx.UnsignedShortTy, "unichar"); - if (Ctx.getTargetInfo().getTriple().isOSMSVCRT() && - LM.getKind() == LengthModifier::AsShort) - return Ctx.IntTy; - return ArgType(Ctx.WideCharTy, "wchar_t"); - case ConversionSpecifier::pArg: - case ConversionSpecifier::PArg: - return ArgType::CPointerTy; - case ConversionSpecifier::ObjCObjArg: - return ArgType::ObjCPointerTy; - default: - break; - } - - // FIXME: Handle other cases. - return ArgType(); -} - -bool PrintfSpecifier::fixType(QualType QT, const LangOptions &LangOpt, - ASTContext &Ctx, bool IsObjCLiteral) { - // %n is different from other conversion specifiers; don't try to fix it. - if (CS.getKind() == ConversionSpecifier::nArg) - return false; - - // Handle Objective-C objects first. Note that while the '%@' specifier will - // not warn for structure pointer or void pointer arguments (because that's - // how CoreFoundation objects are implemented), we only show a fixit for '%@' - // if we know it's an object (block, id, class, or __attribute__((NSObject))). - if (QT->isObjCRetainableType()) { - if (!IsObjCLiteral) - return false; - - CS.setKind(ConversionSpecifier::ObjCObjArg); - - // Disable irrelevant flags - HasThousandsGrouping = false; - HasPlusPrefix = false; - HasSpacePrefix = false; - HasAlternativeForm = false; - HasLeadingZeroes = false; - Precision.setHowSpecified(OptionalAmount::NotSpecified); - LM.setKind(LengthModifier::None); - - return true; - } - - // Handle strings next (char *, wchar_t *) - if (QT->isPointerType() && (QT->getPointeeType()->isAnyCharacterType())) { - CS.setKind(ConversionSpecifier::sArg); - - // Disable irrelevant flags - HasAlternativeForm = 0; - HasLeadingZeroes = 0; - - // Set the long length modifier for wide characters - if (QT->getPointeeType()->isWideCharType()) - LM.setKind(LengthModifier::AsWideChar); - else - LM.setKind(LengthModifier::None); - - return true; - } - - // If it's an enum, get its underlying type. - if (const EnumType *ETy = QT->getAs<EnumType>()) - QT = ETy->getDecl()->getIntegerType(); - - // We can only work with builtin types. - const BuiltinType *BT = QT->getAs<BuiltinType>(); - if (!BT) - return false; - - // Set length modifier - switch (BT->getKind()) { - case BuiltinType::Bool: - case BuiltinType::WChar_U: - case BuiltinType::WChar_S: - case BuiltinType::Char8: // FIXME: Treat like 'char'? - case BuiltinType::Char16: - case BuiltinType::Char32: - case BuiltinType::UInt128: - case BuiltinType::Int128: - case BuiltinType::Half: - case BuiltinType::Float16: - case BuiltinType::Float128: - case BuiltinType::ShortAccum: - case BuiltinType::Accum: - case BuiltinType::LongAccum: - case BuiltinType::UShortAccum: - case BuiltinType::UAccum: - case BuiltinType::ULongAccum: - case BuiltinType::ShortFract: - case BuiltinType::Fract: - case BuiltinType::LongFract: - case BuiltinType::UShortFract: - case BuiltinType::UFract: - case BuiltinType::ULongFract: - case BuiltinType::SatShortAccum: - case BuiltinType::SatAccum: - case BuiltinType::SatLongAccum: - case BuiltinType::SatUShortAccum: - case BuiltinType::SatUAccum: - case BuiltinType::SatULongAccum: - case BuiltinType::SatShortFract: - case BuiltinType::SatFract: - case BuiltinType::SatLongFract: - case BuiltinType::SatUShortFract: - case BuiltinType::SatUFract: - case BuiltinType::SatULongFract: - // Various types which are non-trivial to correct. - return false; - -#define IMAGE_TYPE(ImgType, Id, SingletonId, Access, Suffix) \ - case BuiltinType::Id: -#include "clang/Basic/OpenCLImageTypes.def" -#define SIGNED_TYPE(Id, SingletonId) -#define UNSIGNED_TYPE(Id, SingletonId) -#define FLOATING_TYPE(Id, SingletonId) -#define BUILTIN_TYPE(Id, SingletonId) \ - case BuiltinType::Id: -#include "clang/AST/BuiltinTypes.def" - // Misc other stuff which doesn't make sense here. - return false; - - case BuiltinType::UInt: - case BuiltinType::Int: - case BuiltinType::Float: - case BuiltinType::Double: - LM.setKind(LengthModifier::None); - break; - - case BuiltinType::Char_U: - case BuiltinType::UChar: - case BuiltinType::Char_S: - case BuiltinType::SChar: - LM.setKind(LengthModifier::AsChar); - break; - - case BuiltinType::Short: - case BuiltinType::UShort: - LM.setKind(LengthModifier::AsShort); - break; - - case BuiltinType::Long: - case BuiltinType::ULong: - LM.setKind(LengthModifier::AsLong); - break; - - case BuiltinType::LongLong: - case BuiltinType::ULongLong: - LM.setKind(LengthModifier::AsLongLong); - break; - - case BuiltinType::LongDouble: - LM.setKind(LengthModifier::AsLongDouble); - break; - } - - // Handle size_t, ptrdiff_t, etc. that have dedicated length modifiers in C99. - if (isa<TypedefType>(QT) && (LangOpt.C99 || LangOpt.CPlusPlus11)) - namedTypeToLengthModifier(QT, LM); - - // 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; - } - - // 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->isCharType()) { - CS.setKind(ConversionSpecifier::cArg); - LM.setKind(LengthModifier::None); - Precision.setHowSpecified(OptionalAmount::NotSpecified); - HasAlternativeForm = 0; - HasLeadingZeroes = 0; - HasPlusPrefix = 0; - } - // Test for Floating type first as LongDouble can pass isUnsignedIntegerType - else if (QT->isRealFloatingType()) { - CS.setKind(ConversionSpecifier::fArg); - } - else if (QT->isSignedIntegerType()) { - CS.setKind(ConversionSpecifier::dArg); - HasAlternativeForm = 0; - } - else if (QT->isUnsignedIntegerType()) { - CS.setKind(ConversionSpecifier::uArg); - HasAlternativeForm = 0; - HasPlusPrefix = 0; - } else { - llvm_unreachable("Unexpected type"); - } - - return true; -} - -void PrintfSpecifier::toString(raw_ostream &os) const { - // Whilst some features have no defined order, we are using the order - // appearing in the C99 standard (ISO/IEC 9899:1999 (E) 7.19.6.1) - os << "%"; - - // Positional args - if (usesPositionalArg()) { - os << getPositionalArgIndex() << "$"; - } - - // Conversion flags - if (IsLeftJustified) os << "-"; - if (HasPlusPrefix) os << "+"; - if (HasSpacePrefix) os << " "; - if (HasAlternativeForm) os << "#"; - if (HasLeadingZeroes) os << "0"; - - // Minimum field width - FieldWidth.toString(os); - // Precision - Precision.toString(os); - // Length modifier - os << LM.toString(); - // Conversion specifier - os << CS.toString(); -} - -bool PrintfSpecifier::hasValidPlusPrefix() const { - if (!HasPlusPrefix) - return true; - - // The plus prefix only makes sense for signed conversions - switch (CS.getKind()) { - case ConversionSpecifier::dArg: - case ConversionSpecifier::DArg: - case ConversionSpecifier::iArg: - case ConversionSpecifier::fArg: - case ConversionSpecifier::FArg: - case ConversionSpecifier::eArg: - case ConversionSpecifier::EArg: - case ConversionSpecifier::gArg: - case ConversionSpecifier::GArg: - case ConversionSpecifier::aArg: - case ConversionSpecifier::AArg: - case ConversionSpecifier::FreeBSDrArg: - case ConversionSpecifier::FreeBSDyArg: - return true; - - default: - return false; - } -} - -bool PrintfSpecifier::hasValidAlternativeForm() const { - if (!HasAlternativeForm) - return true; - - // Alternate form flag only valid with the oxXaAeEfFgG conversions - switch (CS.getKind()) { - case ConversionSpecifier::oArg: - case ConversionSpecifier::OArg: - case ConversionSpecifier::xArg: - case ConversionSpecifier::XArg: - case ConversionSpecifier::aArg: - case ConversionSpecifier::AArg: - case ConversionSpecifier::eArg: - case ConversionSpecifier::EArg: - case ConversionSpecifier::fArg: - case ConversionSpecifier::FArg: - case ConversionSpecifier::gArg: - case ConversionSpecifier::GArg: - case ConversionSpecifier::FreeBSDrArg: - case ConversionSpecifier::FreeBSDyArg: - return true; - - default: - return false; - } -} - -bool PrintfSpecifier::hasValidLeadingZeros() const { - if (!HasLeadingZeroes) - return true; - - // Leading zeroes flag only valid with the diouxXaAeEfFgG conversions - switch (CS.getKind()) { - case ConversionSpecifier::dArg: - case ConversionSpecifier::DArg: - case ConversionSpecifier::iArg: - case ConversionSpecifier::oArg: - case ConversionSpecifier::OArg: - case ConversionSpecifier::uArg: - case ConversionSpecifier::UArg: - case ConversionSpecifier::xArg: - case ConversionSpecifier::XArg: - case ConversionSpecifier::aArg: - case ConversionSpecifier::AArg: - case ConversionSpecifier::eArg: - case ConversionSpecifier::EArg: - case ConversionSpecifier::fArg: - case ConversionSpecifier::FArg: - case ConversionSpecifier::gArg: - case ConversionSpecifier::GArg: - case ConversionSpecifier::FreeBSDrArg: - case ConversionSpecifier::FreeBSDyArg: - return true; - - default: - return false; - } -} - -bool PrintfSpecifier::hasValidSpacePrefix() const { - if (!HasSpacePrefix) - return true; - - // The space prefix only makes sense for signed conversions - switch (CS.getKind()) { - case ConversionSpecifier::dArg: - case ConversionSpecifier::DArg: - case ConversionSpecifier::iArg: - case ConversionSpecifier::fArg: - case ConversionSpecifier::FArg: - case ConversionSpecifier::eArg: - case ConversionSpecifier::EArg: - case ConversionSpecifier::gArg: - case ConversionSpecifier::GArg: - case ConversionSpecifier::aArg: - case ConversionSpecifier::AArg: - case ConversionSpecifier::FreeBSDrArg: - case ConversionSpecifier::FreeBSDyArg: - return true; - - default: - return false; - } -} - -bool PrintfSpecifier::hasValidLeftJustified() const { - if (!IsLeftJustified) - return true; - - // The left justified flag is valid for all conversions except n - switch (CS.getKind()) { - case ConversionSpecifier::nArg: - return false; - - default: - return true; - } -} - -bool PrintfSpecifier::hasValidThousandsGroupingPrefix() const { - if (!HasThousandsGrouping) - return true; - - switch (CS.getKind()) { - case ConversionSpecifier::dArg: - case ConversionSpecifier::DArg: - case ConversionSpecifier::iArg: - case ConversionSpecifier::uArg: - case ConversionSpecifier::UArg: - case ConversionSpecifier::fArg: - case ConversionSpecifier::FArg: - case ConversionSpecifier::gArg: - case ConversionSpecifier::GArg: - return true; - default: - return false; - } -} - -bool PrintfSpecifier::hasValidPrecision() const { - if (Precision.getHowSpecified() == OptionalAmount::NotSpecified) - return true; - - // Precision is only valid with the diouxXaAeEfFgGsP conversions - switch (CS.getKind()) { - case ConversionSpecifier::dArg: - case ConversionSpecifier::DArg: - case ConversionSpecifier::iArg: - case ConversionSpecifier::oArg: - case ConversionSpecifier::OArg: - case ConversionSpecifier::uArg: - case ConversionSpecifier::UArg: - case ConversionSpecifier::xArg: - case ConversionSpecifier::XArg: - case ConversionSpecifier::aArg: - case ConversionSpecifier::AArg: - case ConversionSpecifier::eArg: - case ConversionSpecifier::EArg: - case ConversionSpecifier::fArg: - case ConversionSpecifier::FArg: - case ConversionSpecifier::gArg: - case ConversionSpecifier::GArg: - case ConversionSpecifier::sArg: - case ConversionSpecifier::FreeBSDrArg: - case ConversionSpecifier::FreeBSDyArg: - case ConversionSpecifier::PArg: - return true; - - default: - return false; - } -} -bool PrintfSpecifier::hasValidFieldWidth() const { - if (FieldWidth.getHowSpecified() == OptionalAmount::NotSpecified) - return true; - - // The field width is valid for all conversions except n - switch (CS.getKind()) { - case ConversionSpecifier::nArg: - return false; - - default: - return true; - } -} diff --git a/lib/Analysis/ProgramPoint.cpp b/lib/Analysis/ProgramPoint.cpp index d9833659d7b3..2d016cb13353 100644 --- a/lib/Analysis/ProgramPoint.cpp +++ b/lib/Analysis/ProgramPoint.cpp @@ -43,6 +43,181 @@ ProgramPoint ProgramPoint::getProgramPoint(const Stmt *S, ProgramPoint::Kind K, } } +LLVM_DUMP_METHOD void ProgramPoint::dump() const { + return print(/*CR=*/"\n", llvm::errs()); +} + +static void printLocation(raw_ostream &Out, SourceLocation SLoc, + const SourceManager &SM, + StringRef CR, + StringRef Postfix) { + if (SLoc.isFileID()) { + Out << CR << "line=" << SM.getExpansionLineNumber(SLoc) + << " col=" << SM.getExpansionColumnNumber(SLoc) << Postfix; + } +} + +void ProgramPoint::print(StringRef CR, llvm::raw_ostream &Out) const { + const ASTContext &Context = + getLocationContext()->getAnalysisDeclContext()->getASTContext(); + const SourceManager &SM = Context.getSourceManager(); + switch (getKind()) { + case ProgramPoint::BlockEntranceKind: + Out << "Block Entrance: B" + << castAs<BlockEntrance>().getBlock()->getBlockID(); + break; + + case ProgramPoint::FunctionExitKind: { + auto FEP = getAs<FunctionExitPoint>(); + Out << "Function Exit: B" << FEP->getBlock()->getBlockID(); + if (const ReturnStmt *RS = FEP->getStmt()) { + Out << CR << " Return: S" << RS->getID(Context) << CR; + RS->printPretty(Out, /*helper=*/nullptr, Context.getPrintingPolicy(), + /*Indentation=*/2, /*NewlineSymbol=*/CR); + } + break; + } + case ProgramPoint::BlockExitKind: + assert(false); + break; + + case ProgramPoint::CallEnterKind: + Out << "CallEnter"; + break; + + case ProgramPoint::CallExitBeginKind: + Out << "CallExitBegin"; + break; + + case ProgramPoint::CallExitEndKind: + Out << "CallExitEnd"; + break; + + case ProgramPoint::PostStmtPurgeDeadSymbolsKind: + Out << "PostStmtPurgeDeadSymbols"; + break; + + case ProgramPoint::PreStmtPurgeDeadSymbolsKind: + Out << "PreStmtPurgeDeadSymbols"; + break; + + case ProgramPoint::EpsilonKind: + Out << "Epsilon Point"; + break; + + case ProgramPoint::LoopExitKind: { + LoopExit LE = castAs<LoopExit>(); + Out << "LoopExit: " << LE.getLoopStmt()->getStmtClassName(); + break; + } + + case ProgramPoint::PreImplicitCallKind: { + ImplicitCallPoint PC = castAs<ImplicitCallPoint>(); + Out << "PreCall: "; + PC.getDecl()->print(Out, Context.getLangOpts()); + printLocation(Out, PC.getLocation(), SM, CR, /*Postfix=*/CR); + break; + } + + case ProgramPoint::PostImplicitCallKind: { + ImplicitCallPoint PC = castAs<ImplicitCallPoint>(); + Out << "PostCall: "; + PC.getDecl()->print(Out, Context.getLangOpts()); + printLocation(Out, PC.getLocation(), SM, CR, /*Postfix=*/CR); + break; + } + + case ProgramPoint::PostInitializerKind: { + Out << "PostInitializer: "; + const CXXCtorInitializer *Init = castAs<PostInitializer>().getInitializer(); + if (const FieldDecl *FD = Init->getAnyMember()) + Out << *FD; + else { + QualType Ty = Init->getTypeSourceInfo()->getType(); + Ty = Ty.getLocalUnqualifiedType(); + Ty.print(Out, Context.getLangOpts()); + } + break; + } + + case ProgramPoint::BlockEdgeKind: { + const BlockEdge &E = castAs<BlockEdge>(); + Out << "Edge: (B" << E.getSrc()->getBlockID() << ", B" + << E.getDst()->getBlockID() << ')'; + + if (const Stmt *T = E.getSrc()->getTerminator()) { + SourceLocation SLoc = T->getBeginLoc(); + + Out << "\\|Terminator: "; + E.getSrc()->printTerminator(Out, Context.getLangOpts()); + printLocation(Out, SLoc, SM, CR, /*Postfix=*/""); + + if (isa<SwitchStmt>(T)) { + const Stmt *Label = E.getDst()->getLabel(); + + if (Label) { + if (const auto *C = dyn_cast<CaseStmt>(Label)) { + Out << CR << "case "; + if (C->getLHS()) + C->getLHS()->printPretty( + Out, nullptr, Context.getPrintingPolicy(), + /*Indentation=*/0, /*NewlineSymbol=*/CR); + + if (const Stmt *RHS = C->getRHS()) { + Out << " .. "; + RHS->printPretty(Out, nullptr, Context.getPrintingPolicy(), + /*Indetation=*/0, /*NewlineSymbol=*/CR); + } + + Out << ":"; + } else { + assert(isa<DefaultStmt>(Label)); + Out << CR << "default:"; + } + } else + Out << CR << "(implicit) default:"; + } else if (isa<IndirectGotoStmt>(T)) { + // FIXME + } else { + Out << CR << "Condition: "; + if (*E.getSrc()->succ_begin() == E.getDst()) + Out << "true"; + else + Out << "false"; + } + + Out << CR; + } + + break; + } + + default: { + const Stmt *S = castAs<StmtPoint>().getStmt(); + assert(S != nullptr && "Expecting non-null Stmt"); + + Out << S->getStmtClassName() << " S" << S->getID(Context) << " <" + << (const void *)S << "> "; + S->printPretty(Out, /*helper=*/nullptr, Context.getPrintingPolicy(), + /*Indentation=*/2, /*NewlineSymbol=*/CR); + printLocation(Out, S->getBeginLoc(), SM, CR, /*Postfix=*/""); + + if (getAs<PreStmt>()) + Out << CR << "PreStmt" << CR; + else if (getAs<PostLoad>()) + Out << CR << "PostLoad" << CR; + else if (getAs<PostStore>()) + Out << CR << "PostStore" << CR; + else if (getAs<PostLValue>()) + Out << CR << "PostLValue" << CR; + else if (getAs<PostAllocatorCall>()) + Out << CR << "PostAllocatorCall" << CR; + + break; + } + } +} + SimpleProgramPointTag::SimpleProgramPointTag(StringRef MsgProvider, StringRef Msg) : Desc((MsgProvider + " : " + Msg).str()) {} diff --git a/lib/Analysis/PseudoConstantAnalysis.cpp b/lib/Analysis/PseudoConstantAnalysis.cpp deleted file mode 100644 index 83b545a7be83..000000000000 --- a/lib/Analysis/PseudoConstantAnalysis.cpp +++ /dev/null @@ -1,226 +0,0 @@ -//== PseudoConstantAnalysis.cpp - Find Pseudoconstants in the AST-*- C++ -*-==// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This file tracks the usage of variables in a Decl body to see if they are -// never written to, implying that they constant. This is useful in static -// analysis to see if a developer might have intended a variable to be const. -// -//===----------------------------------------------------------------------===// - -#include "clang/Analysis/Analyses/PseudoConstantAnalysis.h" -#include "clang/AST/Decl.h" -#include "clang/AST/Expr.h" -#include "clang/AST/Stmt.h" -#include "llvm/ADT/SmallPtrSet.h" -#include <deque> - -using namespace clang; - -typedef llvm::SmallPtrSet<const VarDecl*, 32> VarDeclSet; - -PseudoConstantAnalysis::PseudoConstantAnalysis(const Stmt *DeclBody) : - DeclBody(DeclBody), Analyzed(false) { - NonConstantsImpl = new VarDeclSet; - UsedVarsImpl = new VarDeclSet; -} - -PseudoConstantAnalysis::~PseudoConstantAnalysis() { - delete (VarDeclSet*)NonConstantsImpl; - delete (VarDeclSet*)UsedVarsImpl; -} - -// Returns true if the given ValueDecl is never written to in the given DeclBody -bool PseudoConstantAnalysis::isPseudoConstant(const VarDecl *VD) { - // Only local and static variables can be pseudoconstants - if (!VD->hasLocalStorage() && !VD->isStaticLocal()) - return false; - - if (!Analyzed) { - RunAnalysis(); - Analyzed = true; - } - - VarDeclSet *NonConstants = (VarDeclSet*)NonConstantsImpl; - - return !NonConstants->count(VD); -} - -// Returns true if the variable was used (self assignments don't count) -bool PseudoConstantAnalysis::wasReferenced(const VarDecl *VD) { - if (!Analyzed) { - RunAnalysis(); - Analyzed = true; - } - - VarDeclSet *UsedVars = (VarDeclSet*)UsedVarsImpl; - - return UsedVars->count(VD); -} - -// Returns a Decl from a (Block)DeclRefExpr (if any) -const Decl *PseudoConstantAnalysis::getDecl(const Expr *E) { - if (const DeclRefExpr *DR = dyn_cast<DeclRefExpr>(E)) - return DR->getDecl(); - else - return nullptr; -} - -void PseudoConstantAnalysis::RunAnalysis() { - std::deque<const Stmt *> WorkList; - VarDeclSet *NonConstants = (VarDeclSet*)NonConstantsImpl; - VarDeclSet *UsedVars = (VarDeclSet*)UsedVarsImpl; - - // Start with the top level statement of the function - WorkList.push_back(DeclBody); - - while (!WorkList.empty()) { - const Stmt *Head = WorkList.front(); - WorkList.pop_front(); - - if (const Expr *Ex = dyn_cast<Expr>(Head)) - Head = Ex->IgnoreParenCasts(); - - switch (Head->getStmtClass()) { - // Case 1: Assignment operators modifying VarDecls - case Stmt::BinaryOperatorClass: { - const BinaryOperator *BO = cast<BinaryOperator>(Head); - // Look for a Decl on the LHS - const Decl *LHSDecl = getDecl(BO->getLHS()->IgnoreParenCasts()); - if (!LHSDecl) - break; - - // We found a binary operator with a DeclRefExpr on the LHS. We now check - // for any of the assignment operators, implying that this Decl is being - // written to. - switch (BO->getOpcode()) { - // Self-assignments don't count as use of a variable - case BO_Assign: { - // Look for a DeclRef on the RHS - const Decl *RHSDecl = getDecl(BO->getRHS()->IgnoreParenCasts()); - - // If the Decls match, we have self-assignment - if (LHSDecl == RHSDecl) - // Do not visit the children - continue; - - LLVM_FALLTHROUGH; - } - case BO_AddAssign: - case BO_SubAssign: - case BO_MulAssign: - case BO_DivAssign: - case BO_AndAssign: - case BO_OrAssign: - case BO_XorAssign: - case BO_ShlAssign: - case BO_ShrAssign: { - const VarDecl *VD = dyn_cast<VarDecl>(LHSDecl); - // The DeclRefExpr is being assigned to - mark it as non-constant - if (VD) - NonConstants->insert(VD); - break; - } - - default: - break; - } - break; - } - - // Case 2: Pre/post increment/decrement and address of - case Stmt::UnaryOperatorClass: { - const UnaryOperator *UO = cast<UnaryOperator>(Head); - - // Look for a DeclRef in the subexpression - const Decl *D = getDecl(UO->getSubExpr()->IgnoreParenCasts()); - if (!D) - break; - - // We found a unary operator with a DeclRef as a subexpression. We now - // check for any of the increment/decrement operators, as well as - // addressOf. - switch (UO->getOpcode()) { - case UO_PostDec: - case UO_PostInc: - case UO_PreDec: - case UO_PreInc: - // The DeclRef is being changed - mark it as non-constant - case UO_AddrOf: { - // If we are taking the address of the DeclRefExpr, assume it is - // non-constant. - const VarDecl *VD = dyn_cast<VarDecl>(D); - if (VD) - NonConstants->insert(VD); - break; - } - - default: - break; - } - break; - } - - // Case 3: Reference Declarations - case Stmt::DeclStmtClass: { - const DeclStmt *DS = cast<DeclStmt>(Head); - // Iterate over each decl and see if any of them contain reference decls - for (const auto *I : DS->decls()) { - // We only care about VarDecls - const VarDecl *VD = dyn_cast<VarDecl>(I); - if (!VD) - continue; - - // We found a VarDecl; make sure it is a reference type - if (!VD->getType().getTypePtr()->isReferenceType()) - continue; - - // Try to find a Decl in the initializer - const Decl *D = getDecl(VD->getInit()->IgnoreParenCasts()); - if (!D) - break; - - // If the reference is to another var, add the var to the non-constant - // list - if (const VarDecl *RefVD = dyn_cast<VarDecl>(D)) { - NonConstants->insert(RefVD); - continue; - } - } - break; - } - - // Case 4: Variable references - case Stmt::DeclRefExprClass: { - const DeclRefExpr *DR = cast<DeclRefExpr>(Head); - if (const VarDecl *VD = dyn_cast<VarDecl>(DR->getDecl())) { - // Add the Decl to the used list - UsedVars->insert(VD); - continue; - } - break; - } - - // Case 5: Block expressions - case Stmt::BlockExprClass: { - const BlockExpr *B = cast<BlockExpr>(Head); - // Add the body of the block to the list - WorkList.push_back(B->getBody()); - continue; - } - - default: - break; - } // switch (head->getStmtClass()) - - // Add all substatements to the worklist - for (const Stmt *SubStmt : Head->children()) - if (SubStmt) - WorkList.push_back(SubStmt); - } // while (!WorkList.empty()) -} diff --git a/lib/Analysis/ReachableCode.cpp b/lib/Analysis/ReachableCode.cpp index ed26a94f3d60..87f4f7010f98 100644 --- a/lib/Analysis/ReachableCode.cpp +++ b/lib/Analysis/ReachableCode.cpp @@ -153,7 +153,7 @@ static bool isExpandedFromConfigurationMacro(const Stmt *S, // value comes from a macro, but we can do much better. This is likely // to be over conservative. This logic is factored into a separate function // so that we can refine it later. - SourceLocation L = S->getLocStart(); + SourceLocation L = S->getBeginLoc(); if (L.isMacroID()) { SourceManager &SM = PP.getSourceManager(); if (IgnoreYES_NO) { @@ -200,7 +200,7 @@ static bool isConfigurationValue(const Stmt *S, // Special case looking for the sigil '()' around an integer literal. if (const ParenExpr *PE = dyn_cast<ParenExpr>(S)) - if (!PE->getLocStart().isMacroID()) + if (!PE->getBeginLoc().isMacroID()) return isConfigurationValue(PE->getSubExpr(), PP, SilenceableCondVal, IncludeIntegers, true); @@ -219,7 +219,7 @@ static bool isConfigurationValue(const Stmt *S, return isConfigurationValue(cast<DeclRefExpr>(S)->getDecl(), PP); case Stmt::ObjCBoolLiteralExprClass: IgnoreYES_NO = true; - // Fallthrough. + LLVM_FALLTHROUGH; case Stmt::CXXBoolLiteralExprClass: case Stmt::IntegerLiteralClass: { const Expr *E = cast<Expr>(S); @@ -446,7 +446,7 @@ bool DeadCodeScan::isDeadCodeRoot(const clang::CFGBlock *Block) { } static bool isValidDeadStmt(const Stmt *S) { - if (S->getLocStart().isInvalid()) + if (S->getBeginLoc().isInvalid()) return false; if (const BinaryOperator *BO = dyn_cast<BinaryOperator>(S)) return BO->getOpcode() != BO_Comma; @@ -474,9 +474,9 @@ const Stmt *DeadCodeScan::findDeadCode(const clang::CFGBlock *Block) { static int SrcCmp(const std::pair<const CFGBlock *, const Stmt *> *p1, const std::pair<const CFGBlock *, const Stmt *> *p2) { - if (p1->second->getLocStart() < p2->second->getLocStart()) + if (p1->second->getBeginLoc() < p2->second->getBeginLoc()) return -1; - if (p2->second->getLocStart() < p1->second->getLocStart()) + if (p2->second->getBeginLoc() < p1->second->getBeginLoc()) return 1; return 0; } @@ -509,7 +509,7 @@ unsigned DeadCodeScan::scanBackwards(const clang::CFGBlock *Start, } // Specially handle macro-expanded code. - if (S->getLocStart().isMacroID()) { + if (S->getBeginLoc().isMacroID()) { count += scanMaybeReachableFromBlock(Block, PP, Reachable); continue; } @@ -592,7 +592,7 @@ static SourceLocation GetUnreachableLoc(const Stmt *S, case Expr::CXXFunctionalCastExprClass: { const CXXFunctionalCastExpr *CE = cast <CXXFunctionalCastExpr>(S); R1 = CE->getSubExpr()->getSourceRange(); - return CE->getLocStart(); + return CE->getBeginLoc(); } case Stmt::CXXTryStmtClass: { return cast<CXXTryStmt>(S)->getHandler(0)->getCatchLoc(); @@ -605,7 +605,7 @@ static SourceLocation GetUnreachableLoc(const Stmt *S, default: ; } R1 = S->getSourceRange(); - return S->getLocStart(); + return S->getBeginLoc(); } void DeadCodeScan::reportDeadCode(const CFGBlock *B, @@ -631,12 +631,12 @@ void DeadCodeScan::reportDeadCode(const CFGBlock *B, // a for/for-range loop. This is the block that contains // the increment code. if (const Stmt *LoopTarget = B->getLoopTarget()) { - SourceLocation Loc = LoopTarget->getLocStart(); + SourceLocation Loc = LoopTarget->getBeginLoc(); SourceRange R1(Loc, Loc), R2; if (const ForStmt *FS = dyn_cast<ForStmt>(LoopTarget)) { const Expr *Inc = FS->getInc(); - Loc = Inc->getLocStart(); + Loc = Inc->getBeginLoc(); R2 = Inc->getSourceRange(); } diff --git a/lib/Analysis/ScanfFormatString.cpp b/lib/Analysis/ScanfFormatString.cpp deleted file mode 100644 index a9af0cdfdacd..000000000000 --- a/lib/Analysis/ScanfFormatString.cpp +++ /dev/null @@ -1,563 +0,0 @@ -//= ScanfFormatString.cpp - Analysis of printf format strings --*- C++ -*-===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// Handling of format string in scanf and friends. The structure of format -// strings for fscanf() are described in C99 7.19.6.2. -// -//===----------------------------------------------------------------------===// - -#include "clang/Analysis/Analyses/FormatString.h" -#include "FormatStringParsing.h" -#include "clang/Basic/TargetInfo.h" - -using clang::analyze_format_string::ArgType; -using clang::analyze_format_string::FormatStringHandler; -using clang::analyze_format_string::LengthModifier; -using clang::analyze_format_string::OptionalAmount; -using clang::analyze_format_string::ConversionSpecifier; -using clang::analyze_scanf::ScanfConversionSpecifier; -using clang::analyze_scanf::ScanfSpecifier; -using clang::UpdateOnReturn; -using namespace clang; - -typedef clang::analyze_format_string::SpecifierResult<ScanfSpecifier> - ScanfSpecifierResult; - -static bool ParseScanList(FormatStringHandler &H, - ScanfConversionSpecifier &CS, - const char *&Beg, const char *E) { - const char *I = Beg; - const char *start = I - 1; - UpdateOnReturn <const char*> UpdateBeg(Beg, I); - - // No more characters? - if (I == E) { - H.HandleIncompleteScanList(start, I); - return true; - } - - // Special case: ']' is the first character. - if (*I == ']') { - if (++I == E) { - H.HandleIncompleteScanList(start, I - 1); - return true; - } - } - - // Special case: "^]" are the first characters. - if (I + 1 != E && I[0] == '^' && I[1] == ']') { - I += 2; - if (I == E) { - H.HandleIncompleteScanList(start, I - 1); - return true; - } - } - - // Look for a ']' character which denotes the end of the scan list. - while (*I != ']') { - if (++I == E) { - H.HandleIncompleteScanList(start, I - 1); - return true; - } - } - - CS.setEndScanList(I); - return false; -} - -// FIXME: Much of this is copy-paste from ParsePrintfSpecifier. -// We can possibly refactor. -static ScanfSpecifierResult ParseScanfSpecifier(FormatStringHandler &H, - const char *&Beg, - const char *E, - unsigned &argIndex, - const LangOptions &LO, - const TargetInfo &Target) { - using namespace clang::analyze_format_string; - using namespace clang::analyze_scanf; - const char *I = Beg; - const char *Start = nullptr; - UpdateOnReturn <const char*> UpdateBeg(Beg, I); - - // Look for a '%' character that indicates the start of a format specifier. - for ( ; I != E ; ++I) { - char c = *I; - if (c == '\0') { - // Detect spurious null characters, which are likely errors. - H.HandleNullChar(I); - return true; - } - if (c == '%') { - Start = I++; // Record the start of the format specifier. - break; - } - } - - // No format specifier found? - if (!Start) - return false; - - if (I == E) { - // No more characters left? - H.HandleIncompleteSpecifier(Start, E - Start); - return true; - } - - ScanfSpecifier FS; - if (ParseArgPosition(H, FS, Start, I, E)) - return true; - - if (I == E) { - // No more characters left? - H.HandleIncompleteSpecifier(Start, E - Start); - return true; - } - - // Look for '*' flag if it is present. - if (*I == '*') { - FS.setSuppressAssignment(I); - if (++I == E) { - H.HandleIncompleteSpecifier(Start, E - Start); - return true; - } - } - - // Look for the field width (if any). Unlike printf, this is either - // a fixed integer or isn't present. - const OptionalAmount &Amt = clang::analyze_format_string::ParseAmount(I, E); - if (Amt.getHowSpecified() != OptionalAmount::NotSpecified) { - assert(Amt.getHowSpecified() == OptionalAmount::Constant); - FS.setFieldWidth(Amt); - - if (I == E) { - // No more characters left? - H.HandleIncompleteSpecifier(Start, E - Start); - return true; - } - } - - // Look for the length modifier. - if (ParseLengthModifier(FS, I, E, LO, /*scanf=*/true) && I == E) { - // No more characters left? - H.HandleIncompleteSpecifier(Start, E - Start); - return true; - } - - // Detect spurious null characters, which are likely errors. - if (*I == '\0') { - H.HandleNullChar(I); - return true; - } - - // Finally, look for the conversion specifier. - const char *conversionPosition = I++; - ScanfConversionSpecifier::Kind k = ScanfConversionSpecifier::InvalidSpecifier; - switch (*conversionPosition) { - default: - break; - case '%': k = ConversionSpecifier::PercentArg; break; - case 'A': k = ConversionSpecifier::AArg; break; - case 'E': k = ConversionSpecifier::EArg; break; - case 'F': k = ConversionSpecifier::FArg; break; - case 'G': k = ConversionSpecifier::GArg; break; - case 'X': k = ConversionSpecifier::XArg; break; - case 'a': k = ConversionSpecifier::aArg; break; - case 'd': k = ConversionSpecifier::dArg; break; - case 'e': k = ConversionSpecifier::eArg; break; - case 'f': k = ConversionSpecifier::fArg; break; - case 'g': k = ConversionSpecifier::gArg; break; - case 'i': k = ConversionSpecifier::iArg; break; - case 'n': k = ConversionSpecifier::nArg; break; - case 'c': k = ConversionSpecifier::cArg; break; - case 'C': k = ConversionSpecifier::CArg; break; - case 'S': k = ConversionSpecifier::SArg; break; - case '[': k = ConversionSpecifier::ScanListArg; break; - case 'u': k = ConversionSpecifier::uArg; break; - case 'x': k = ConversionSpecifier::xArg; break; - case 'o': k = ConversionSpecifier::oArg; break; - case 's': k = ConversionSpecifier::sArg; break; - case 'p': k = ConversionSpecifier::pArg; break; - // Apple extensions - // Apple-specific - case 'D': - if (Target.getTriple().isOSDarwin()) - k = ConversionSpecifier::DArg; - break; - case 'O': - if (Target.getTriple().isOSDarwin()) - k = ConversionSpecifier::OArg; - break; - case 'U': - if (Target.getTriple().isOSDarwin()) - k = ConversionSpecifier::UArg; - break; - } - ScanfConversionSpecifier CS(conversionPosition, k); - if (k == ScanfConversionSpecifier::ScanListArg) { - if (ParseScanList(H, CS, I, E)) - return true; - } - FS.setConversionSpecifier(CS); - if (CS.consumesDataArgument() && !FS.getSuppressAssignment() - && !FS.usesPositionalArg()) - FS.setArgIndex(argIndex++); - - // FIXME: '%' and '*' doesn't make sense. Issue a warning. - // FIXME: 'ConsumedSoFar' and '*' doesn't make sense. - - if (k == ScanfConversionSpecifier::InvalidSpecifier) { - unsigned Len = I - Beg; - if (ParseUTF8InvalidSpecifier(Beg, E, Len)) { - CS.setEndScanList(Beg + Len); - FS.setConversionSpecifier(CS); - } - // Assume the conversion takes one argument. - return !H.HandleInvalidScanfConversionSpecifier(FS, Beg, Len); - } - return ScanfSpecifierResult(Start, FS); -} - -ArgType ScanfSpecifier::getArgType(ASTContext &Ctx) const { - const ScanfConversionSpecifier &CS = getConversionSpecifier(); - - if (!CS.consumesDataArgument()) - return ArgType::Invalid(); - - switch(CS.getKind()) { - // Signed int. - case ConversionSpecifier::dArg: - case ConversionSpecifier::DArg: - case ConversionSpecifier::iArg: - switch (LM.getKind()) { - case LengthModifier::None: - return ArgType::PtrTo(Ctx.IntTy); - case LengthModifier::AsChar: - return ArgType::PtrTo(ArgType::AnyCharTy); - case LengthModifier::AsShort: - return ArgType::PtrTo(Ctx.ShortTy); - case LengthModifier::AsLong: - return ArgType::PtrTo(Ctx.LongTy); - case LengthModifier::AsLongLong: - case LengthModifier::AsQuad: - return ArgType::PtrTo(Ctx.LongLongTy); - case LengthModifier::AsInt64: - return ArgType::PtrTo(ArgType(Ctx.LongLongTy, "__int64")); - case LengthModifier::AsIntMax: - return ArgType::PtrTo(ArgType(Ctx.getIntMaxType(), "intmax_t")); - case LengthModifier::AsSizeT: - return ArgType::PtrTo(ArgType(Ctx.getSignedSizeType(), "ssize_t")); - case LengthModifier::AsPtrDiff: - return ArgType::PtrTo(ArgType(Ctx.getPointerDiffType(), "ptrdiff_t")); - case LengthModifier::AsLongDouble: - // GNU extension. - return ArgType::PtrTo(Ctx.LongLongTy); - case LengthModifier::AsAllocate: - case LengthModifier::AsMAllocate: - case LengthModifier::AsInt32: - case LengthModifier::AsInt3264: - case LengthModifier::AsWide: - return ArgType::Invalid(); - } - - // Unsigned int. - case ConversionSpecifier::oArg: - case ConversionSpecifier::OArg: - case ConversionSpecifier::uArg: - case ConversionSpecifier::UArg: - case ConversionSpecifier::xArg: - case ConversionSpecifier::XArg: - switch (LM.getKind()) { - case LengthModifier::None: - return ArgType::PtrTo(Ctx.UnsignedIntTy); - case LengthModifier::AsChar: - return ArgType::PtrTo(Ctx.UnsignedCharTy); - case LengthModifier::AsShort: - return ArgType::PtrTo(Ctx.UnsignedShortTy); - case LengthModifier::AsLong: - return ArgType::PtrTo(Ctx.UnsignedLongTy); - case LengthModifier::AsLongLong: - case LengthModifier::AsQuad: - return ArgType::PtrTo(Ctx.UnsignedLongLongTy); - case LengthModifier::AsInt64: - return ArgType::PtrTo(ArgType(Ctx.UnsignedLongLongTy, "unsigned __int64")); - case LengthModifier::AsIntMax: - return ArgType::PtrTo(ArgType(Ctx.getUIntMaxType(), "uintmax_t")); - case LengthModifier::AsSizeT: - return ArgType::PtrTo(ArgType(Ctx.getSizeType(), "size_t")); - case LengthModifier::AsPtrDiff: - return ArgType::PtrTo( - ArgType(Ctx.getUnsignedPointerDiffType(), "unsigned ptrdiff_t")); - case LengthModifier::AsLongDouble: - // GNU extension. - return ArgType::PtrTo(Ctx.UnsignedLongLongTy); - case LengthModifier::AsAllocate: - case LengthModifier::AsMAllocate: - case LengthModifier::AsInt32: - case LengthModifier::AsInt3264: - case LengthModifier::AsWide: - return ArgType::Invalid(); - } - - // Float. - case ConversionSpecifier::aArg: - case ConversionSpecifier::AArg: - case ConversionSpecifier::eArg: - case ConversionSpecifier::EArg: - case ConversionSpecifier::fArg: - case ConversionSpecifier::FArg: - case ConversionSpecifier::gArg: - case ConversionSpecifier::GArg: - switch (LM.getKind()) { - case LengthModifier::None: - return ArgType::PtrTo(Ctx.FloatTy); - case LengthModifier::AsLong: - return ArgType::PtrTo(Ctx.DoubleTy); - case LengthModifier::AsLongDouble: - return ArgType::PtrTo(Ctx.LongDoubleTy); - default: - return ArgType::Invalid(); - } - - // Char, string and scanlist. - case ConversionSpecifier::cArg: - case ConversionSpecifier::sArg: - case ConversionSpecifier::ScanListArg: - switch (LM.getKind()) { - case LengthModifier::None: - return ArgType::PtrTo(ArgType::AnyCharTy); - case LengthModifier::AsLong: - case LengthModifier::AsWide: - return ArgType::PtrTo(ArgType(Ctx.getWideCharType(), "wchar_t")); - case LengthModifier::AsAllocate: - case LengthModifier::AsMAllocate: - return ArgType::PtrTo(ArgType::CStrTy); - case LengthModifier::AsShort: - if (Ctx.getTargetInfo().getTriple().isOSMSVCRT()) - return ArgType::PtrTo(ArgType::AnyCharTy); - LLVM_FALLTHROUGH; - default: - return ArgType::Invalid(); - } - case ConversionSpecifier::CArg: - case ConversionSpecifier::SArg: - // FIXME: Mac OS X specific? - switch (LM.getKind()) { - case LengthModifier::None: - case LengthModifier::AsWide: - return ArgType::PtrTo(ArgType(Ctx.getWideCharType(), "wchar_t")); - case LengthModifier::AsAllocate: - case LengthModifier::AsMAllocate: - return ArgType::PtrTo(ArgType(ArgType::WCStrTy, "wchar_t *")); - case LengthModifier::AsShort: - if (Ctx.getTargetInfo().getTriple().isOSMSVCRT()) - return ArgType::PtrTo(ArgType::AnyCharTy); - LLVM_FALLTHROUGH; - default: - return ArgType::Invalid(); - } - - // Pointer. - case ConversionSpecifier::pArg: - return ArgType::PtrTo(ArgType::CPointerTy); - - // Write-back. - case ConversionSpecifier::nArg: - switch (LM.getKind()) { - case LengthModifier::None: - return ArgType::PtrTo(Ctx.IntTy); - case LengthModifier::AsChar: - return ArgType::PtrTo(Ctx.SignedCharTy); - case LengthModifier::AsShort: - return ArgType::PtrTo(Ctx.ShortTy); - case LengthModifier::AsLong: - return ArgType::PtrTo(Ctx.LongTy); - case LengthModifier::AsLongLong: - case LengthModifier::AsQuad: - return ArgType::PtrTo(Ctx.LongLongTy); - case LengthModifier::AsInt64: - return ArgType::PtrTo(ArgType(Ctx.LongLongTy, "__int64")); - case LengthModifier::AsIntMax: - return ArgType::PtrTo(ArgType(Ctx.getIntMaxType(), "intmax_t")); - case LengthModifier::AsSizeT: - return ArgType::PtrTo(ArgType(Ctx.getSignedSizeType(), "ssize_t")); - case LengthModifier::AsPtrDiff: - return ArgType::PtrTo(ArgType(Ctx.getPointerDiffType(), "ptrdiff_t")); - case LengthModifier::AsLongDouble: - return ArgType(); // FIXME: Is this a known extension? - case LengthModifier::AsAllocate: - case LengthModifier::AsMAllocate: - case LengthModifier::AsInt32: - case LengthModifier::AsInt3264: - case LengthModifier::AsWide: - return ArgType::Invalid(); - } - - default: - break; - } - - return ArgType(); -} - -bool ScanfSpecifier::fixType(QualType QT, QualType RawQT, - const LangOptions &LangOpt, - ASTContext &Ctx) { - - // %n is different from other conversion specifiers; don't try to fix it. - if (CS.getKind() == ConversionSpecifier::nArg) - return false; - - if (!QT->isPointerType()) - return false; - - QualType PT = QT->getPointeeType(); - - // If it's an enum, get its underlying type. - if (const EnumType *ETy = PT->getAs<EnumType>()) { - // Don't try to fix incomplete enums. - if (!ETy->getDecl()->isComplete()) - return false; - PT = ETy->getDecl()->getIntegerType(); - } - - const BuiltinType *BT = PT->getAs<BuiltinType>(); - if (!BT) - return false; - - // Pointer to a character. - if (PT->isAnyCharacterType()) { - CS.setKind(ConversionSpecifier::sArg); - if (PT->isWideCharType()) - LM.setKind(LengthModifier::AsWideChar); - else - LM.setKind(LengthModifier::None); - - // If we know the target array length, we can use it as a field width. - if (const ConstantArrayType *CAT = Ctx.getAsConstantArrayType(RawQT)) { - if (CAT->getSizeModifier() == ArrayType::Normal) - FieldWidth = OptionalAmount(OptionalAmount::Constant, - CAT->getSize().getZExtValue() - 1, - "", 0, false); - - } - return true; - } - - // Figure out the length modifier. - switch (BT->getKind()) { - // no modifier - case BuiltinType::UInt: - case BuiltinType::Int: - case BuiltinType::Float: - LM.setKind(LengthModifier::None); - break; - - // hh - case BuiltinType::Char_U: - case BuiltinType::UChar: - case BuiltinType::Char_S: - case BuiltinType::SChar: - LM.setKind(LengthModifier::AsChar); - break; - - // h - case BuiltinType::Short: - case BuiltinType::UShort: - LM.setKind(LengthModifier::AsShort); - break; - - // l - case BuiltinType::Long: - case BuiltinType::ULong: - case BuiltinType::Double: - LM.setKind(LengthModifier::AsLong); - break; - - // ll - case BuiltinType::LongLong: - case BuiltinType::ULongLong: - LM.setKind(LengthModifier::AsLongLong); - break; - - // L - case BuiltinType::LongDouble: - LM.setKind(LengthModifier::AsLongDouble); - break; - - // Don't know. - default: - return false; - } - - // Handle size_t, ptrdiff_t, etc. that have dedicated length modifiers in C99. - if (isa<TypedefType>(PT) && (LangOpt.C99 || LangOpt.CPlusPlus11)) - namedTypeToLengthModifier(PT, LM); - - // If fixing the length modifier was enough, we are done. - if (hasValidLengthModifier(Ctx.getTargetInfo())) { - const analyze_scanf::ArgType &AT = getArgType(Ctx); - if (AT.isValid() && AT.matchesType(Ctx, QT)) - return true; - } - - // Figure out the conversion specifier. - if (PT->isRealFloatingType()) - CS.setKind(ConversionSpecifier::fArg); - else if (PT->isSignedIntegerType()) - CS.setKind(ConversionSpecifier::dArg); - else if (PT->isUnsignedIntegerType()) - CS.setKind(ConversionSpecifier::uArg); - else - llvm_unreachable("Unexpected type"); - - return true; -} - -void ScanfSpecifier::toString(raw_ostream &os) const { - os << "%"; - - if (usesPositionalArg()) - os << getPositionalArgIndex() << "$"; - if (SuppressAssignment) - os << "*"; - - FieldWidth.toString(os); - os << LM.toString(); - os << CS.toString(); -} - -bool clang::analyze_format_string::ParseScanfString(FormatStringHandler &H, - const char *I, - const char *E, - const LangOptions &LO, - const TargetInfo &Target) { - - unsigned argIndex = 0; - - // Keep looking for a format specifier until we have exhausted the string. - while (I != E) { - const ScanfSpecifierResult &FSR = ParseScanfSpecifier(H, I, E, argIndex, - LO, Target); - // Did a fail-stop error of any kind occur when parsing the specifier? - // If so, don't do any more processing. - if (FSR.shouldStop()) - return true; - // Did we exhaust the string or encounter an error that - // we can recover from? - if (!FSR.hasValue()) - continue; - // We have a format specifier. Pass it to the callback. - if (!H.HandleScanfSpecifier(FSR.getValue(), FSR.getStart(), - I - FSR.getStart())) { - return true; - } - } - assert(I == E && "Format string not exhausted"); - return false; -} diff --git a/lib/Analysis/ThreadSafety.cpp b/lib/Analysis/ThreadSafety.cpp index 03cc234dce5c..78e1b056e1d7 100644 --- a/lib/Analysis/ThreadSafety.cpp +++ b/lib/Analysis/ThreadSafety.cpp @@ -33,6 +33,7 @@ #include "clang/Analysis/Analyses/ThreadSafetyUtil.h" #include "clang/Analysis/AnalysisDeclContext.h" #include "clang/Analysis/CFG.h" +#include "clang/Basic/Builtins.h" #include "clang/Basic/LLVM.h" #include "clang/Basic/OperatorKinds.h" #include "clang/Basic/SourceLocation.h" @@ -41,6 +42,7 @@ #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/ImmutableMap.h" #include "llvm/ADT/Optional.h" +#include "llvm/ADT/PointerIntPair.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringRef.h" @@ -64,13 +66,6 @@ using namespace threadSafety; // Key method definition ThreadSafetyHandler::~ThreadSafetyHandler() = default; -namespace { - -class TILPrinter : - public til::PrettyPrinter<TILPrinter, llvm::raw_ostream> {}; - -} // namespace - /// Issue a warning about an invalid lock expression static void warnInvalidLock(ThreadSafetyHandler &Handler, const Expr *MutexExp, const NamedDecl *D, @@ -86,8 +81,8 @@ static void warnInvalidLock(ThreadSafetyHandler &Handler, namespace { -/// A set of CapabilityInfo objects, which are compiled from the -/// requires attributes on a function. +/// A set of CapabilityExpr objects, which are compiled from thread safety +/// attributes on a function. class CapExprSet : public SmallVector<CapabilityExpr, 4> { public: /// Push M onto list, but discard duplicates. @@ -142,13 +137,16 @@ public: handleRemovalFromIntersection(const FactSet &FSet, FactManager &FactMan, SourceLocation JoinLoc, LockErrorKind LEK, ThreadSafetyHandler &Handler) const = 0; + virtual void handleLock(FactSet &FSet, FactManager &FactMan, + const FactEntry &entry, ThreadSafetyHandler &Handler, + StringRef DiagKind) const = 0; virtual void handleUnlock(FactSet &FSet, FactManager &FactMan, const CapabilityExpr &Cp, SourceLocation UnlockLoc, bool FullyRemove, ThreadSafetyHandler &Handler, StringRef DiagKind) const = 0; // Return true if LKind >= LK, where exclusive > shared - bool isAtLeast(LockKind LK) { + bool isAtLeast(LockKind LK) const { return (LKind == LK_Exclusive) || (LK == LK_Shared); } }; @@ -159,7 +157,7 @@ using FactID = unsigned short; /// the analysis of a single routine. class FactManager { private: - std::vector<std::unique_ptr<FactEntry>> Facts; + std::vector<std::unique_ptr<const FactEntry>> Facts; public: FactID newFact(std::unique_ptr<FactEntry> Entry) { @@ -168,7 +166,6 @@ public: } const FactEntry &operator[](FactID F) const { return *Facts[F]; } - FactEntry &operator[](FactID F) { return *Facts[F]; } }; /// A FactSet is the set of facts that are known to be true at a @@ -238,22 +235,23 @@ public: }); } - FactEntry *findLock(FactManager &FM, const CapabilityExpr &CapE) const { + const FactEntry *findLock(FactManager &FM, const CapabilityExpr &CapE) const { auto I = std::find_if(begin(), end(), [&](FactID ID) { return FM[ID].matches(CapE); }); return I != end() ? &FM[*I] : nullptr; } - FactEntry *findLockUniv(FactManager &FM, const CapabilityExpr &CapE) const { + const FactEntry *findLockUniv(FactManager &FM, + const CapabilityExpr &CapE) const { auto I = std::find_if(begin(), end(), [&](FactID ID) -> bool { return FM[ID].matchesUniv(CapE); }); return I != end() ? &FM[*I] : nullptr; } - FactEntry *findPartialMatch(FactManager &FM, - const CapabilityExpr &CapE) const { + const FactEntry *findPartialMatch(FactManager &FM, + const CapabilityExpr &CapE) const { auto I = std::find_if(begin(), end(), [&](FactID ID) -> bool { return FM[ID].partiallyMatches(CapE); }); @@ -419,7 +417,7 @@ private: Context::Factory ContextFactory; std::vector<VarDefinition> VarDefinitions; std::vector<unsigned> CtxIndices; - std::vector<std::pair<Stmt *, Context>> SavedContexts; + std::vector<std::pair<const Stmt *, Context>> SavedContexts; public: LocalVariableMap() { @@ -460,7 +458,7 @@ public: /// Return the next context after processing S. This function is used by /// clients of the class to get the appropriate context when traversing the /// CFG. It must be called for every assignment or DeclStmt. - Context getNextContext(unsigned &CtxIndex, Stmt *S, Context C) { + Context getNextContext(unsigned &CtxIndex, const Stmt *S, Context C) { if (SavedContexts[CtxIndex+1].first == S) { CtxIndex++; Context Result = SavedContexts[CtxIndex].second; @@ -522,7 +520,7 @@ protected: unsigned getContextIndex() { return SavedContexts.size()-1; } // Save the current context for later replay - void saveContext(Stmt *S, Context C) { + void saveContext(const Stmt *S, Context C) { SavedContexts.push_back(std::make_pair(S, C)); } @@ -592,7 +590,7 @@ CFGBlockInfo CFGBlockInfo::getEmptyBlockInfo(LocalVariableMap &M) { namespace { /// Visitor which builds a LocalVariableMap -class VarMapBuilder : public StmtVisitor<VarMapBuilder> { +class VarMapBuilder : public ConstStmtVisitor<VarMapBuilder> { public: LocalVariableMap* VMap; LocalVariableMap::Context Ctx; @@ -600,16 +598,16 @@ public: VarMapBuilder(LocalVariableMap *VM, LocalVariableMap::Context C) : VMap(VM), Ctx(C) {} - void VisitDeclStmt(DeclStmt *S); - void VisitBinaryOperator(BinaryOperator *BO); + void VisitDeclStmt(const DeclStmt *S); + void VisitBinaryOperator(const BinaryOperator *BO); }; } // namespace // Add new local variables to the variable map -void VarMapBuilder::VisitDeclStmt(DeclStmt *S) { +void VarMapBuilder::VisitDeclStmt(const DeclStmt *S) { bool modifiedCtx = false; - DeclGroupRef DGrp = S->getDeclGroup(); + const DeclGroupRef DGrp = S->getDeclGroup(); for (const auto *D : DGrp) { if (const auto *VD = dyn_cast_or_null<VarDecl>(D)) { const Expr *E = VD->getInit(); @@ -627,7 +625,7 @@ void VarMapBuilder::VisitDeclStmt(DeclStmt *S) { } // Update local variable definitions in variable map -void VarMapBuilder::VisitBinaryOperator(BinaryOperator *BO) { +void VarMapBuilder::VisitBinaryOperator(const BinaryOperator *BO) { if (!BO->isAssignmentOp()) return; @@ -734,7 +732,7 @@ void LocalVariableMap::traverseCFG(CFG *CFGraph, CtxIndices.resize(CFGraph->getNumBlockIDs()); for (const auto *CurrBlock : *SortedGraph) { - int CurrBlockID = CurrBlock->getBlockID(); + unsigned CurrBlockID = CurrBlock->getBlockID(); CFGBlockInfo *CurrBlockInfo = &BlockInfo[CurrBlockID]; VisitedBlocks.insert(CurrBlock); @@ -750,7 +748,7 @@ void LocalVariableMap::traverseCFG(CFG *CFGraph, continue; } - int PrevBlockID = (*PI)->getBlockID(); + unsigned PrevBlockID = (*PI)->getBlockID(); CFGBlockInfo *PrevBlockInfo = &BlockInfo[PrevBlockID]; if (CtxInit) { @@ -780,7 +778,7 @@ void LocalVariableMap::traverseCFG(CFG *CFGraph, switch (BI.getKind()) { case CFGElement::Statement: { CFGStmt CS = BI.castAs<CFGStmt>(); - VMapBuilder.Visit(const_cast<Stmt *>(CS.getStmt())); + VMapBuilder.Visit(CS.getStmt()); break; } default: @@ -819,13 +817,13 @@ static void findBlockLocations(CFG *CFGraph, // Find the source location of the last statement in the block, if the // block is not empty. if (const Stmt *S = CurrBlock->getTerminator()) { - CurrBlockInfo->EntryLoc = CurrBlockInfo->ExitLoc = S->getLocStart(); + CurrBlockInfo->EntryLoc = CurrBlockInfo->ExitLoc = S->getBeginLoc(); } else { for (CFGBlock::const_reverse_iterator BI = CurrBlock->rbegin(), BE = CurrBlock->rend(); BI != BE; ++BI) { // FIXME: Handle other CFGElement kinds. if (Optional<CFGStmt> CS = BI->getAs<CFGStmt>()) { - CurrBlockInfo->ExitLoc = CS->getStmt()->getLocStart(); + CurrBlockInfo->ExitLoc = CS->getStmt()->getBeginLoc(); break; } } @@ -837,7 +835,7 @@ static void findBlockLocations(CFG *CFGraph, for (const auto &BI : *CurrBlock) { // FIXME: Handle other CFGElement kinds. if (Optional<CFGStmt> CS = BI.getAs<CFGStmt>()) { - CurrBlockInfo->EntryLoc = CS->getStmt()->getLocStart(); + CurrBlockInfo->EntryLoc = CS->getStmt()->getBeginLoc(); break; } } @@ -873,6 +871,12 @@ public: } } + void handleLock(FactSet &FSet, FactManager &FactMan, const FactEntry &entry, + ThreadSafetyHandler &Handler, + StringRef DiagKind) const override { + Handler.handleDoubleLock(DiagKind, entry.toString(), entry.loc()); + } + void handleUnlock(FactSet &FSet, FactManager &FactMan, const CapabilityExpr &Cp, SourceLocation UnlockLoc, bool FullyRemove, ThreadSafetyHandler &Handler, @@ -887,63 +891,117 @@ public: class ScopedLockableFactEntry : public FactEntry { private: - SmallVector<const til::SExpr *, 4> UnderlyingMutexes; + enum UnderlyingCapabilityKind { + UCK_Acquired, ///< Any kind of acquired capability. + UCK_ReleasedShared, ///< Shared capability that was released. + UCK_ReleasedExclusive, ///< Exclusive capability that was released. + }; + + using UnderlyingCapability = + llvm::PointerIntPair<const til::SExpr *, 2, UnderlyingCapabilityKind>; + + SmallVector<UnderlyingCapability, 4> UnderlyingMutexes; public: - ScopedLockableFactEntry(const CapabilityExpr &CE, SourceLocation Loc, - const CapExprSet &Excl, const CapExprSet &Shrd) - : FactEntry(CE, LK_Exclusive, Loc, false) { - for (const auto &M : Excl) - UnderlyingMutexes.push_back(M.sexpr()); - for (const auto &M : Shrd) - UnderlyingMutexes.push_back(M.sexpr()); + ScopedLockableFactEntry(const CapabilityExpr &CE, SourceLocation Loc) + : FactEntry(CE, LK_Exclusive, Loc, false) {} + + void addExclusiveLock(const CapabilityExpr &M) { + UnderlyingMutexes.emplace_back(M.sexpr(), UCK_Acquired); + } + + void addSharedLock(const CapabilityExpr &M) { + UnderlyingMutexes.emplace_back(M.sexpr(), UCK_Acquired); + } + + void addExclusiveUnlock(const CapabilityExpr &M) { + UnderlyingMutexes.emplace_back(M.sexpr(), UCK_ReleasedExclusive); + } + + void addSharedUnlock(const CapabilityExpr &M) { + UnderlyingMutexes.emplace_back(M.sexpr(), UCK_ReleasedShared); } void handleRemovalFromIntersection(const FactSet &FSet, FactManager &FactMan, SourceLocation JoinLoc, LockErrorKind LEK, ThreadSafetyHandler &Handler) const override { - for (const auto *UnderlyingMutex : UnderlyingMutexes) { - if (FSet.findLock(FactMan, CapabilityExpr(UnderlyingMutex, false))) { + for (const auto &UnderlyingMutex : UnderlyingMutexes) { + const auto *Entry = FSet.findLock( + FactMan, CapabilityExpr(UnderlyingMutex.getPointer(), false)); + if ((UnderlyingMutex.getInt() == UCK_Acquired && Entry) || + (UnderlyingMutex.getInt() != UCK_Acquired && !Entry)) { // If this scoped lock manages another mutex, and if the underlying - // mutex is still held, then warn about the underlying mutex. + // mutex is still/not held, then warn about the underlying mutex. Handler.handleMutexHeldEndOfScope( - "mutex", sx::toString(UnderlyingMutex), loc(), JoinLoc, LEK); + "mutex", sx::toString(UnderlyingMutex.getPointer()), loc(), JoinLoc, + LEK); } } } + void handleLock(FactSet &FSet, FactManager &FactMan, const FactEntry &entry, + ThreadSafetyHandler &Handler, + StringRef DiagKind) const override { + for (const auto &UnderlyingMutex : UnderlyingMutexes) { + CapabilityExpr UnderCp(UnderlyingMutex.getPointer(), false); + + if (UnderlyingMutex.getInt() == UCK_Acquired) + lock(FSet, FactMan, UnderCp, entry.kind(), entry.loc(), &Handler, + DiagKind); + else + unlock(FSet, FactMan, UnderCp, entry.loc(), &Handler, DiagKind); + } + } + void handleUnlock(FactSet &FSet, FactManager &FactMan, const CapabilityExpr &Cp, SourceLocation UnlockLoc, bool FullyRemove, ThreadSafetyHandler &Handler, StringRef DiagKind) const override { assert(!Cp.negative() && "Managing object cannot be negative."); - for (const auto *UnderlyingMutex : UnderlyingMutexes) { - CapabilityExpr UnderCp(UnderlyingMutex, false); - auto UnderEntry = llvm::make_unique<LockableFactEntry>( - !UnderCp, LK_Exclusive, UnlockLoc); + for (const auto &UnderlyingMutex : UnderlyingMutexes) { + CapabilityExpr UnderCp(UnderlyingMutex.getPointer(), false); - if (FullyRemove) { - // We're destroying the managing object. - // Remove the underlying mutex if it exists; but don't warn. - if (FSet.findLock(FactMan, UnderCp)) { - FSet.removeLock(FactMan, UnderCp); - FSet.addLock(FactMan, std::move(UnderEntry)); - } + // Remove/lock the underlying mutex if it exists/is still unlocked; warn + // on double unlocking/locking if we're not destroying the scoped object. + ThreadSafetyHandler *TSHandler = FullyRemove ? nullptr : &Handler; + if (UnderlyingMutex.getInt() == UCK_Acquired) { + unlock(FSet, FactMan, UnderCp, UnlockLoc, TSHandler, DiagKind); } else { - // We're releasing the underlying mutex, but not destroying the - // managing object. Warn on dual release. - if (!FSet.findLock(FactMan, UnderCp)) { - Handler.handleUnmatchedUnlock(DiagKind, UnderCp.toString(), - UnlockLoc); - } - FSet.removeLock(FactMan, UnderCp); - FSet.addLock(FactMan, std::move(UnderEntry)); + LockKind kind = UnderlyingMutex.getInt() == UCK_ReleasedShared + ? LK_Shared + : LK_Exclusive; + lock(FSet, FactMan, UnderCp, kind, UnlockLoc, TSHandler, DiagKind); } } if (FullyRemove) FSet.removeLock(FactMan, Cp); } + +private: + void lock(FactSet &FSet, FactManager &FactMan, const CapabilityExpr &Cp, + LockKind kind, SourceLocation loc, ThreadSafetyHandler *Handler, + StringRef DiagKind) const { + if (!FSet.findLock(FactMan, Cp)) { + FSet.removeLock(FactMan, !Cp); + FSet.addLock(FactMan, + llvm::make_unique<LockableFactEntry>(Cp, kind, loc)); + } else if (Handler) { + Handler->handleDoubleLock(DiagKind, Cp.toString(), loc); + } + } + + void unlock(FactSet &FSet, FactManager &FactMan, const CapabilityExpr &Cp, + SourceLocation loc, ThreadSafetyHandler *Handler, + StringRef DiagKind) const { + if (FSet.findLock(FactMan, Cp)) { + FSet.removeLock(FactMan, Cp); + FSet.addLock(FactMan, llvm::make_unique<LockableFactEntry>( + !Cp, LK_Exclusive, loc)); + } else if (Handler) { + Handler->handleUnmatchedUnlock(DiagKind, Cp.toString(), loc); + } + } }; /// Class which implements the core thread safety analysis routines. @@ -976,11 +1034,11 @@ public: StringRef DiagKind); template <typename AttrType> - void getMutexIDs(CapExprSet &Mtxs, AttrType *Attr, Expr *Exp, + void getMutexIDs(CapExprSet &Mtxs, AttrType *Attr, const Expr *Exp, const NamedDecl *D, VarDecl *SelfDecl = nullptr); template <class AttrType> - void getMutexIDs(CapExprSet &Mtxs, AttrType *Attr, Expr *Exp, + void getMutexIDs(CapExprSet &Mtxs, AttrType *Attr, const Expr *Exp, const NamedDecl *D, const CFGBlock *PredBlock, const CFGBlock *CurrBlock, Expr *BrE, bool Neg); @@ -1232,7 +1290,7 @@ void ThreadSafetyAnalyzer::addLock(FactSet &FSet, if (!ReqAttr && !Entry->negative()) { // look for the negative capability, and remove it from the fact set. CapabilityExpr NegC = !*Entry; - FactEntry *Nen = FSet.findLock(FactMan, NegC); + const FactEntry *Nen = FSet.findLock(FactMan, NegC); if (Nen) { FSet.removeLock(FactMan, NegC); } @@ -1251,9 +1309,9 @@ void ThreadSafetyAnalyzer::addLock(FactSet &FSet, } // FIXME: Don't always warn when we have support for reentrant locks. - if (FSet.findLock(FactMan, *Entry)) { + if (const FactEntry *Cp = FSet.findLock(FactMan, *Entry)) { if (!Entry->asserted()) - Handler.handleDoubleLock(DiagKind, Entry->toString(), Entry->loc()); + Cp->handleLock(FSet, FactMan, *Entry, Handler, DiagKind); } else { FSet.addLock(FactMan, std::move(Entry)); } @@ -1289,7 +1347,7 @@ void ThreadSafetyAnalyzer::removeLock(FactSet &FSet, const CapabilityExpr &Cp, /// and push them onto Mtxs, discarding any duplicates. template <typename AttrType> void ThreadSafetyAnalyzer::getMutexIDs(CapExprSet &Mtxs, AttrType *Attr, - Expr *Exp, const NamedDecl *D, + const Expr *Exp, const NamedDecl *D, VarDecl *SelfDecl) { if (Attr->args_size() == 0) { // The mutex held is the "this" object. @@ -1321,7 +1379,7 @@ void ThreadSafetyAnalyzer::getMutexIDs(CapExprSet &Mtxs, AttrType *Attr, /// any duplicates. template <class AttrType> void ThreadSafetyAnalyzer::getMutexIDs(CapExprSet &Mtxs, AttrType *Attr, - Expr *Exp, const NamedDecl *D, + const Expr *Exp, const NamedDecl *D, const CFGBlock *PredBlock, const CFGBlock *CurrBlock, Expr *BrE, bool Neg) { @@ -1369,14 +1427,17 @@ const CallExpr* ThreadSafetyAnalyzer::getTrylockCallExpr(const Stmt *Cond, if (!Cond) return nullptr; - if (const auto *CallExp = dyn_cast<CallExpr>(Cond)) + if (const auto *CallExp = dyn_cast<CallExpr>(Cond)) { + if (CallExp->getBuiltinCallee() == Builtin::BI__builtin_expect) + return getTrylockCallExpr(CallExp->getArg(0), C, Negate); return CallExp; + } else if (const auto *PE = dyn_cast<ParenExpr>(Cond)) return getTrylockCallExpr(PE->getSubExpr(), C, Negate); else if (const auto *CE = dyn_cast<ImplicitCastExpr>(Cond)) return getTrylockCallExpr(CE->getSubExpr(), C, Negate); - else if (const auto *EWC = dyn_cast<ExprWithCleanups>(Cond)) - return getTrylockCallExpr(EWC->getSubExpr(), C, Negate); + else if (const auto *FE = dyn_cast<FullExpr>(Cond)) + return getTrylockCallExpr(FE->getSubExpr(), C, Negate); else if (const auto *DRE = dyn_cast<DeclRefExpr>(Cond)) { const Expr *E = LocalVarMap.lookupExpr(DRE->getDecl(), C); return getTrylockCallExpr(E, C, Negate); @@ -1412,6 +1473,17 @@ const CallExpr* ThreadSafetyAnalyzer::getTrylockCallExpr(const Stmt *Cond, if (BOP->getOpcode() == BO_LOr) return getTrylockCallExpr(BOP->getRHS(), C, Negate); return nullptr; + } else if (const auto *COP = dyn_cast<ConditionalOperator>(Cond)) { + bool TCond, FCond; + if (getStaticBooleanValue(COP->getTrueExpr(), TCond) && + getStaticBooleanValue(COP->getFalseExpr(), FCond)) { + if (TCond && !FCond) + return getTrylockCallExpr(COP->getCond(), C, Negate); + if (!TCond && FCond) { + Negate = !Negate; + return getTrylockCallExpr(COP->getCond(), C, Negate); + } + } } return nullptr; } @@ -1426,7 +1498,8 @@ void ThreadSafetyAnalyzer::getEdgeLockset(FactSet& Result, Result = ExitSet; const Stmt *Cond = PredBlock->getTerminatorCondition(); - if (!Cond) + // We don't acquire try-locks on ?: branches, only when its result is used. + if (!Cond || isa<ConditionalOperator>(PredBlock->getTerminator())) return; bool Negate = false; @@ -1434,7 +1507,7 @@ void ThreadSafetyAnalyzer::getEdgeLockset(FactSet& Result, const LocalVarContext &LVarCtx = PredBlockInfo->ExitContext; StringRef CapDiagKind = "mutex"; - auto *Exp = const_cast<CallExpr *>(getTrylockCallExpr(Cond, LVarCtx, Negate)); + const auto *Exp = getTrylockCallExpr(Cond, LVarCtx, Negate); if (!Exp) return; @@ -1494,7 +1567,7 @@ namespace { /// An expression may cause us to add or remove locks from the lockset, or else /// output error messages related to missing locks. /// FIXME: In future, we may be able to not inherit from a visitor. -class BuildLockset : public StmtVisitor<BuildLockset> { +class BuildLockset : public ConstStmtVisitor<BuildLockset> { friend class ThreadSafetyAnalyzer; ThreadSafetyAnalyzer *Analyzer; @@ -1514,19 +1587,23 @@ class BuildLockset : public StmtVisitor<BuildLockset> { void checkPtAccess(const Expr *Exp, AccessKind AK, ProtectedOperationKind POK = POK_VarAccess); - void handleCall(Expr *Exp, const NamedDecl *D, VarDecl *VD = nullptr); + void handleCall(const Expr *Exp, const NamedDecl *D, VarDecl *VD = nullptr); + void examineArguments(const FunctionDecl *FD, + CallExpr::const_arg_iterator ArgBegin, + CallExpr::const_arg_iterator ArgEnd, + bool SkipFirstParam = false); public: BuildLockset(ThreadSafetyAnalyzer *Anlzr, CFGBlockInfo &Info) - : StmtVisitor<BuildLockset>(), Analyzer(Anlzr), FSet(Info.EntrySet), + : ConstStmtVisitor<BuildLockset>(), Analyzer(Anlzr), FSet(Info.EntrySet), LVarCtx(Info.EntryContext), CtxIndex(Info.EntryIndex) {} - void VisitUnaryOperator(UnaryOperator *UO); - void VisitBinaryOperator(BinaryOperator *BO); - void VisitCastExpr(CastExpr *CE); - void VisitCallExpr(CallExpr *Exp); - void VisitCXXConstructExpr(CXXConstructExpr *Exp); - void VisitDeclStmt(DeclStmt *S); + void VisitUnaryOperator(const UnaryOperator *UO); + void VisitBinaryOperator(const BinaryOperator *BO); + void VisitCastExpr(const CastExpr *CE); + void VisitCallExpr(const CallExpr *Exp); + void VisitCXXConstructExpr(const CXXConstructExpr *Exp); + void VisitDeclStmt(const DeclStmt *S); }; } // namespace @@ -1549,7 +1626,7 @@ void BuildLockset::warnIfMutexNotHeld(const NamedDecl *D, const Expr *Exp, if (Cp.negative()) { // Negative capabilities act like locks excluded - FactEntry *LDat = FSet.findLock(Analyzer->FactMan, !Cp); + const FactEntry *LDat = FSet.findLock(Analyzer->FactMan, !Cp); if (LDat) { Analyzer->Handler.handleFunExcludesLock( DiagKind, D->getNameAsString(), (!Cp).toString(), Loc); @@ -1570,7 +1647,7 @@ void BuildLockset::warnIfMutexNotHeld(const NamedDecl *D, const Expr *Exp, return; } - FactEntry* LDat = FSet.findLockUniv(Analyzer->FactMan, Cp); + const FactEntry *LDat = FSet.findLockUniv(Analyzer->FactMan, Cp); bool NoError = true; if (!LDat) { // No exact match found. Look for a partial match. @@ -1606,7 +1683,7 @@ void BuildLockset::warnIfMutexHeld(const NamedDecl *D, const Expr *Exp, return; } - FactEntry* LDat = FSet.findLock(Analyzer->FactMan, Cp); + const FactEntry *LDat = FSet.findLock(Analyzer->FactMan, Cp); if (LDat) { Analyzer->Handler.handleFunExcludesLock( DiagKind, D->getNameAsString(), Cp.toString(), Exp->getExprLoc()); @@ -1630,6 +1707,9 @@ void BuildLockset::checkAccess(const Expr *Exp, AccessKind AK, const auto *VD = dyn_cast<VarDecl>(DRE->getDecl()->getCanonicalDecl()); if (VD && VD->isLocalVarDecl() && VD->getType()->isReferenceType()) { if (const auto *E = VD->getInit()) { + // Guard against self-initialization. e.g., int &i = i; + if (E == Exp) + break; Exp = E; continue; } @@ -1718,7 +1798,8 @@ void BuildLockset::checkPtAccess(const Expr *Exp, AccessKind AK, /// and check that the appropriate locks are held. Non-const method calls with /// the same signature as const method calls can be also treated as reads. /// -void BuildLockset::handleCall(Expr *Exp, const NamedDecl *D, VarDecl *VD) { +void BuildLockset::handleCall(const Expr *Exp, const NamedDecl *D, + VarDecl *VD) { SourceLocation Loc = Exp->getExprLoc(); CapExprSet ExclusiveLocksToAdd, SharedLocksToAdd; CapExprSet ExclusiveLocksToRemove, SharedLocksToRemove, GenericLocksToRemove; @@ -1858,25 +1939,32 @@ void BuildLockset::handleCall(Expr *Exp, const NamedDecl *D, VarDecl *VD) { if (isScopedVar) { // Add the managing object as a dummy mutex, mapped to the underlying mutex. SourceLocation MLoc = VD->getLocation(); - DeclRefExpr DRE(VD, false, VD->getType(), VK_LValue, VD->getLocation()); + DeclRefExpr DRE(VD->getASTContext(), VD, false, VD->getType(), VK_LValue, + VD->getLocation()); // FIXME: does this store a pointer to DRE? CapabilityExpr Scp = Analyzer->SxBuilder.translateAttrExpr(&DRE, nullptr); - std::copy(ScopedExclusiveReqs.begin(), ScopedExclusiveReqs.end(), - std::back_inserter(ExclusiveLocksToAdd)); - std::copy(ScopedSharedReqs.begin(), ScopedSharedReqs.end(), - std::back_inserter(SharedLocksToAdd)); - Analyzer->addLock(FSet, - llvm::make_unique<ScopedLockableFactEntry>( - Scp, MLoc, ExclusiveLocksToAdd, SharedLocksToAdd), - CapDiagKind); + auto ScopedEntry = llvm::make_unique<ScopedLockableFactEntry>(Scp, MLoc); + for (const auto &M : ExclusiveLocksToAdd) + ScopedEntry->addExclusiveLock(M); + for (const auto &M : ScopedExclusiveReqs) + ScopedEntry->addExclusiveLock(M); + for (const auto &M : SharedLocksToAdd) + ScopedEntry->addSharedLock(M); + for (const auto &M : ScopedSharedReqs) + ScopedEntry->addSharedLock(M); + for (const auto &M : ExclusiveLocksToRemove) + ScopedEntry->addExclusiveUnlock(M); + for (const auto &M : SharedLocksToRemove) + ScopedEntry->addSharedUnlock(M); + Analyzer->addLock(FSet, std::move(ScopedEntry), CapDiagKind); } } /// For unary operations which read and write a variable, we need to /// check whether we hold any required mutexes. Reads are checked in /// VisitCastExpr. -void BuildLockset::VisitUnaryOperator(UnaryOperator *UO) { +void BuildLockset::VisitUnaryOperator(const UnaryOperator *UO) { switch (UO->getOpcode()) { case UO_PostDec: case UO_PostInc: @@ -1892,7 +1980,7 @@ void BuildLockset::VisitUnaryOperator(UnaryOperator *UO) { /// For binary operations which assign to a variable (writes), we need to check /// whether we hold any required mutexes. /// FIXME: Deal with non-primitive types. -void BuildLockset::VisitBinaryOperator(BinaryOperator *BO) { +void BuildLockset::VisitBinaryOperator(const BinaryOperator *BO) { if (!BO->isAssignmentOp()) return; @@ -1905,16 +1993,43 @@ void BuildLockset::VisitBinaryOperator(BinaryOperator *BO) { /// Whenever we do an LValue to Rvalue cast, we are reading a variable and /// need to ensure we hold any required mutexes. /// FIXME: Deal with non-primitive types. -void BuildLockset::VisitCastExpr(CastExpr *CE) { +void BuildLockset::VisitCastExpr(const CastExpr *CE) { if (CE->getCastKind() != CK_LValueToRValue) return; checkAccess(CE->getSubExpr(), AK_Read); } -void BuildLockset::VisitCallExpr(CallExpr *Exp) { - bool ExamineArgs = true; - bool OperatorFun = false; +void BuildLockset::examineArguments(const FunctionDecl *FD, + CallExpr::const_arg_iterator ArgBegin, + CallExpr::const_arg_iterator ArgEnd, + bool SkipFirstParam) { + // Currently we can't do anything if we don't know the function declaration. + if (!FD) + return; + // NO_THREAD_SAFETY_ANALYSIS does double duty here. Normally it + // only turns off checking within the body of a function, but we also + // use it to turn off checking in arguments to the function. This + // could result in some false negatives, but the alternative is to + // create yet another attribute. + if (FD->hasAttr<NoThreadSafetyAnalysisAttr>()) + return; + + const ArrayRef<ParmVarDecl *> Params = FD->parameters(); + auto Param = Params.begin(); + if (SkipFirstParam) + ++Param; + + // There can be default arguments, so we stop when one iterator is at end(). + for (auto Arg = ArgBegin; Param != Params.end() && Arg != ArgEnd; + ++Param, ++Arg) { + QualType Qt = (*Param)->getType(); + if (Qt->isReferenceType()) + checkAccess(*Arg, AK_Read, POK_PassByRef); + } +} + +void BuildLockset::VisitCallExpr(const CallExpr *Exp) { if (const auto *CE = dyn_cast<CXXMemberCallExpr>(Exp)) { const auto *ME = dyn_cast<MemberExpr>(CE->getCallee()); // ME can be null when calling a method pointer @@ -1933,13 +2048,12 @@ void BuildLockset::VisitCallExpr(CallExpr *Exp) { checkAccess(CE->getImplicitObjectArgument(), AK_Read); } } - } else if (const auto *OE = dyn_cast<CXXOperatorCallExpr>(Exp)) { - OperatorFun = true; + examineArguments(CE->getDirectCallee(), CE->arg_begin(), CE->arg_end()); + } else if (const auto *OE = dyn_cast<CXXOperatorCallExpr>(Exp)) { auto OEop = OE->getOperator(); switch (OEop) { case OO_Equal: { - ExamineArgs = false; const Expr *Target = OE->getArg(0); const Expr *Source = OE->getArg(1); checkAccess(Target, AK_Written); @@ -1948,60 +2062,27 @@ void BuildLockset::VisitCallExpr(CallExpr *Exp) { } case OO_Star: case OO_Arrow: - case OO_Subscript: { - const Expr *Obj = OE->getArg(0); - checkAccess(Obj, AK_Read); + case OO_Subscript: if (!(OEop == OO_Star && OE->getNumArgs() > 1)) { // Grrr. operator* can be multiplication... - checkPtAccess(Obj, AK_Read); + checkPtAccess(OE->getArg(0), AK_Read); } - break; - } + LLVM_FALLTHROUGH; default: { // TODO: get rid of this, and rely on pass-by-ref instead. const Expr *Obj = OE->getArg(0); checkAccess(Obj, AK_Read); + // Check the remaining arguments. For method operators, the first + // argument is the implicit self argument, and doesn't appear in the + // FunctionDecl, but for non-methods it does. + const FunctionDecl *FD = OE->getDirectCallee(); + examineArguments(FD, std::next(OE->arg_begin()), OE->arg_end(), + /*SkipFirstParam*/ !isa<CXXMethodDecl>(FD)); break; } } - } - - if (ExamineArgs) { - if (FunctionDecl *FD = Exp->getDirectCallee()) { - // NO_THREAD_SAFETY_ANALYSIS does double duty here. Normally it - // only turns off checking within the body of a function, but we also - // use it to turn off checking in arguments to the function. This - // could result in some false negatives, but the alternative is to - // create yet another attribute. - if (!FD->hasAttr<NoThreadSafetyAnalysisAttr>()) { - unsigned Fn = FD->getNumParams(); - unsigned Cn = Exp->getNumArgs(); - unsigned Skip = 0; - - unsigned i = 0; - if (OperatorFun) { - if (isa<CXXMethodDecl>(FD)) { - // First arg in operator call is implicit self argument, - // and doesn't appear in the FunctionDecl. - Skip = 1; - Cn--; - } else { - // Ignore the first argument of operators; it's been checked above. - i = 1; - } - } - // Ignore default arguments - unsigned n = (Fn < Cn) ? Fn : Cn; - - for (; i < n; ++i) { - ParmVarDecl* Pvd = FD->getParamDecl(i); - Expr* Arg = Exp->getArg(i+Skip); - QualType Qt = Pvd->getType(); - if (Qt->isReferenceType()) - checkAccess(Arg, AK_Read, POK_PassByRef); - } - } - } + } else { + examineArguments(Exp->getDirectCallee(), Exp->arg_begin(), Exp->arg_end()); } auto *D = dyn_cast_or_null<NamedDecl>(Exp->getCalleeDecl()); @@ -2010,13 +2091,14 @@ void BuildLockset::VisitCallExpr(CallExpr *Exp) { handleCall(Exp, D); } -void BuildLockset::VisitCXXConstructExpr(CXXConstructExpr *Exp) { +void BuildLockset::VisitCXXConstructExpr(const CXXConstructExpr *Exp) { const CXXConstructorDecl *D = Exp->getConstructor(); if (D && D->isCopyConstructor()) { const Expr* Source = Exp->getArg(0); checkAccess(Source, AK_Read); + } else { + examineArguments(D, Exp->arg_begin(), Exp->arg_end()); } - // FIXME -- only handles constructors in DeclStmt below. } static CXXConstructorDecl * @@ -2046,7 +2128,7 @@ static Expr *buildFakeCtorCall(CXXConstructorDecl *CD, ArrayRef<Expr *> Args, SourceRange(Loc, Loc)); } -void BuildLockset::VisitDeclStmt(DeclStmt *S) { +void BuildLockset::VisitDeclStmt(const DeclStmt *S) { // adjust the context LVarCtx = Analyzer->LocalVarMap.getNextContext(CtxIndex, S, LVarCtx); @@ -2080,7 +2162,7 @@ void BuildLockset::VisitDeclStmt(DeclStmt *S) { CXXConstructorDecl *CtorD = findConstructorForByValueReturn(RD); if (!CtorD || !CtorD->hasAttrs()) continue; - handleCall(buildFakeCtorCall(CtorD, {E}, E->getLocStart()), CtorD, VD); + handleCall(buildFakeCtorCall(CtorD, {E}, E->getBeginLoc()), CtorD, VD); } } } @@ -2242,8 +2324,8 @@ void ThreadSafetyAnalyzer::runAnalysis(AnalysisDeclContext &AC) { // We must ignore such methods. if (A->args_size() == 0) return; - // FIXME -- deal with exclusive vs. shared unlock functions? - getMutexIDs(ExclusiveLocksToAdd, A, nullptr, D); + getMutexIDs(A->isShared() ? SharedLocksToAdd : ExclusiveLocksToAdd, A, + nullptr, D); getMutexIDs(LocksReleased, A, nullptr, D); CapDiagKind = ClassifyDiagnostic(A); } else if (const auto *A = dyn_cast<AcquireCapabilityAttr>(Attr)) { @@ -2279,7 +2361,7 @@ void ThreadSafetyAnalyzer::runAnalysis(AnalysisDeclContext &AC) { } for (const auto *CurrBlock : *SortedGraph) { - int CurrBlockID = CurrBlock->getBlockID(); + unsigned CurrBlockID = CurrBlock->getBlockID(); CFGBlockInfo *CurrBlockInfo = &BlockInfo[CurrBlockID]; // Use the default initial lockset in case there are no predecessors. @@ -2306,7 +2388,7 @@ void ThreadSafetyAnalyzer::runAnalysis(AnalysisDeclContext &AC) { if (*PI == nullptr || !VisitedBlocks.alreadySet(*PI)) continue; - int PrevBlockID = (*PI)->getBlockID(); + unsigned PrevBlockID = (*PI)->getBlockID(); CFGBlockInfo *PrevBlockInfo = &BlockInfo[PrevBlockID]; // Ignore edges from blocks that can't return. @@ -2347,7 +2429,7 @@ void ThreadSafetyAnalyzer::runAnalysis(AnalysisDeclContext &AC) { // Process continue and break blocks. Assume that the lockset for the // resulting block is unaffected by any discrepancies in them. for (const auto *PrevBlock : SpecialBlocks) { - int PrevBlockID = PrevBlock->getBlockID(); + unsigned PrevBlockID = PrevBlock->getBlockID(); CFGBlockInfo *PrevBlockInfo = &BlockInfo[PrevBlockID]; if (!LocksetInitialized) { @@ -2382,21 +2464,21 @@ void ThreadSafetyAnalyzer::runAnalysis(AnalysisDeclContext &AC) { switch (BI.getKind()) { case CFGElement::Statement: { CFGStmt CS = BI.castAs<CFGStmt>(); - LocksetBuilder.Visit(const_cast<Stmt *>(CS.getStmt())); + LocksetBuilder.Visit(CS.getStmt()); break; } // Ignore BaseDtor, MemberDtor, and TemporaryDtor for now. case CFGElement::AutomaticObjectDtor: { CFGAutomaticObjDtor AD = BI.castAs<CFGAutomaticObjDtor>(); - auto *DD = const_cast<CXXDestructorDecl *>( - AD.getDestructorDecl(AC.getASTContext())); + const auto *DD = AD.getDestructorDecl(AC.getASTContext()); if (!DD->hasAttrs()) break; // Create a dummy expression, auto *VD = const_cast<VarDecl *>(AD.getVarDecl()); - DeclRefExpr DRE(VD, false, VD->getType().getNonReferenceType(), - VK_LValue, AD.getTriggerStmt()->getLocEnd()); + DeclRefExpr DRE(VD->getASTContext(), VD, false, + VD->getType().getNonReferenceType(), VK_LValue, + AD.getTriggerStmt()->getEndLoc()); LocksetBuilder.handleCall(&DRE, DD); break; } diff --git a/lib/Analysis/ThreadSafetyCommon.cpp b/lib/Analysis/ThreadSafetyCommon.cpp index fced17ff9197..14d1d9c7a8f7 100644 --- a/lib/Analysis/ThreadSafetyCommon.cpp +++ b/lib/Analysis/ThreadSafetyCommon.cpp @@ -128,7 +128,8 @@ CapabilityExpr SExprBuilder::translateAttrExpr(const Expr *AttrExp, // Hack to handle constructors, where self cannot be recovered from // the expression. if (SelfDecl && !Ctx.SelfArg) { - DeclRefExpr SelfDRE(SelfDecl, false, SelfDecl->getType(), VK_LValue, + DeclRefExpr SelfDRE(SelfDecl->getASTContext(), SelfDecl, false, + SelfDecl->getType(), VK_LValue, SelfDecl->getLocation()); Ctx.SelfArg = &SelfDRE; @@ -211,6 +212,8 @@ til::SExpr *SExprBuilder::translate(const Stmt *S, CallingContext *Ctx) { return translateCXXThisExpr(cast<CXXThisExpr>(S), Ctx); case Stmt::MemberExprClass: return translateMemberExpr(cast<MemberExpr>(S), Ctx); + case Stmt::ObjCIvarRefExprClass: + return translateObjCIVarRefExpr(cast<ObjCIvarRefExpr>(S), Ctx); case Stmt::CallExprClass: return translateCallExpr(cast<CallExpr>(S), Ctx); case Stmt::CXXMemberCallExprClass: @@ -233,6 +236,8 @@ til::SExpr *SExprBuilder::translate(const Stmt *S, CallingContext *Ctx) { cast<BinaryConditionalOperator>(S), Ctx); // We treat these as no-ops + case Stmt::ConstantExprClass: + return translate(cast<ConstantExpr>(S)->getSubExpr(), Ctx); case Stmt::ParenExprClass: return translate(cast<ParenExpr>(S)->getSubExpr(), Ctx); case Stmt::ExprWithCleanupsClass: @@ -311,9 +316,9 @@ static const ValueDecl *getValueDeclFromSExpr(const til::SExpr *E) { return nullptr; } -static bool hasCppPointerType(const til::SExpr *E) { +static bool hasAnyPointerType(const til::SExpr *E) { auto *VD = getValueDeclFromSExpr(E); - if (VD && VD->getType()->isPointerType()) + if (VD && VD->getType()->isAnyPointerType()) return true; if (const auto *C = dyn_cast<til::Cast>(E)) return C->castOpcode() == til::CAST_objToPtr; @@ -344,7 +349,20 @@ til::SExpr *SExprBuilder::translateMemberExpr(const MemberExpr *ME, D = getFirstVirtualDecl(VD); til::Project *P = new (Arena) til::Project(E, D); - if (hasCppPointerType(BE)) + if (hasAnyPointerType(BE)) + P->setArrow(true); + return P; +} + +til::SExpr *SExprBuilder::translateObjCIVarRefExpr(const ObjCIvarRefExpr *IVRE, + CallingContext *Ctx) { + til::SExpr *BE = translate(IVRE->getBase(), Ctx); + til::SExpr *E = new (Arena) til::SApply(BE); + + const auto *D = cast<ObjCIvarDecl>(IVRE->getDecl()->getCanonicalDecl()); + + til::Project *P = new (Arena) til::Project(E, D); + if (hasAnyPointerType(BE)) P->setArrow(true); return P; } @@ -354,15 +372,17 @@ til::SExpr *SExprBuilder::translateCallExpr(const CallExpr *CE, const Expr *SelfE) { if (CapabilityExprMode) { // Handle LOCK_RETURNED - const FunctionDecl *FD = CE->getDirectCallee()->getMostRecentDecl(); - if (LockReturnedAttr* At = FD->getAttr<LockReturnedAttr>()) { - CallingContext LRCallCtx(Ctx); - LRCallCtx.AttrDecl = CE->getDirectCallee(); - LRCallCtx.SelfArg = SelfE; - LRCallCtx.NumArgs = CE->getNumArgs(); - LRCallCtx.FunArgs = CE->getArgs(); - return const_cast<til::SExpr *>( - translateAttrExpr(At->getArg(), &LRCallCtx).sexpr()); + if (const FunctionDecl *FD = CE->getDirectCallee()) { + FD = FD->getMostRecentDecl(); + if (LockReturnedAttr *At = FD->getAttr<LockReturnedAttr>()) { + CallingContext LRCallCtx(Ctx); + LRCallCtx.AttrDecl = CE->getDirectCallee(); + LRCallCtx.SelfArg = SelfE; + LRCallCtx.NumArgs = CE->getNumArgs(); + LRCallCtx.FunArgs = CE->getArgs(); + return const_cast<til::SExpr *>( + translateAttrExpr(At->getArg(), &LRCallCtx).sexpr()); + } } } @@ -927,6 +947,16 @@ void SExprBuilder::exitCFG(const CFGBlock *Last) { } /* +namespace { + +class TILPrinter : + public til::PrettyPrinter<TILPrinter, llvm::raw_ostream> {}; + +} // namespace + +namespace clang { +namespace threadSafety { + void printSCFG(CFGWalker &Walker) { llvm::BumpPtrAllocator Bpa; til::MemRegionRef Arena(&Bpa); @@ -934,4 +964,7 @@ void printSCFG(CFGWalker &Walker) { til::SCFG *Scfg = SxBuilder.buildCFG(Walker); TILPrinter::print(Scfg, llvm::errs()); } + +} // namespace threadSafety +} // namespace clang */ diff --git a/lib/Analysis/ThreadSafetyTIL.cpp b/lib/Analysis/ThreadSafetyTIL.cpp index 798bbfb29d7b..11f7afbd229c 100644 --- a/lib/Analysis/ThreadSafetyTIL.cpp +++ b/lib/Analysis/ThreadSafetyTIL.cpp @@ -150,7 +150,7 @@ void til::simplifyIncompleteArg(til::Phi *Ph) { } // Renumbers the arguments and instructions to have unique, sequential IDs. -int BasicBlock::renumberInstrs(int ID) { +unsigned BasicBlock::renumberInstrs(unsigned ID) { for (auto *Arg : Args) Arg->setID(this, ID++); for (auto *Instr : Instrs) @@ -163,7 +163,8 @@ int BasicBlock::renumberInstrs(int ID) { // Each block will be written into the Blocks array in order, and its BlockID // will be set to the index in the array. Sorting should start from the entry // block, and ID should be the total number of blocks. -int BasicBlock::topologicalSort(SimpleArray<BasicBlock *> &Blocks, int ID) { +unsigned BasicBlock::topologicalSort(SimpleArray<BasicBlock *> &Blocks, + unsigned ID) { if (Visited) return ID; Visited = true; for (auto *Block : successors()) @@ -186,7 +187,8 @@ int BasicBlock::topologicalSort(SimpleArray<BasicBlock *> &Blocks, int ID) { // critical edges, and (3) the entry block is reachable from the exit block // and no blocks are accessible via traversal of back-edges from the exit that // weren't accessible via forward edges from the entry. -int BasicBlock::topologicalFinalSort(SimpleArray<BasicBlock*>& Blocks, int ID) { +unsigned BasicBlock::topologicalFinalSort(SimpleArray<BasicBlock *> &Blocks, + unsigned ID) { // Visited is assumed to have been set by the topologicalSort. This pass // assumes !Visited means that we've visited this node before. if (!Visited) return ID; @@ -257,7 +259,7 @@ void BasicBlock::computePostDominator() { // Renumber instructions in all blocks void SCFG::renumberInstrs() { - int InstrID = 0; + unsigned InstrID = 0; for (auto *Block : Blocks) InstrID = Block->renumberInstrs(InstrID); } @@ -288,11 +290,11 @@ static inline void computeNodeID(BasicBlock *B, // 3) Topologically sorting the blocks into the "Blocks" array. void SCFG::computeNormalForm() { // Topologically sort the blocks starting from the entry block. - int NumUnreachableBlocks = Entry->topologicalSort(Blocks, Blocks.size()); + unsigned NumUnreachableBlocks = Entry->topologicalSort(Blocks, Blocks.size()); if (NumUnreachableBlocks > 0) { // If there were unreachable blocks shift everything down, and delete them. - for (size_t I = NumUnreachableBlocks, E = Blocks.size(); I < E; ++I) { - size_t NI = I - NumUnreachableBlocks; + for (unsigned I = NumUnreachableBlocks, E = Blocks.size(); I < E; ++I) { + unsigned NI = I - NumUnreachableBlocks; Blocks[NI] = Blocks[I]; Blocks[NI]->BlockID = NI; // FIXME: clean up predecessor pointers to unreachable blocks? @@ -305,7 +307,7 @@ void SCFG::computeNormalForm() { Block->computeDominator(); // Once dominators have been computed, the final sort may be performed. - int NumBlocks = Exit->topologicalFinalSort(Blocks, 0); + unsigned NumBlocks = Exit->topologicalFinalSort(Blocks, 0); assert(static_cast<size_t>(NumBlocks) == Blocks.size()); (void) NumBlocks; |
