diff options
Diffstat (limited to 'lib/StaticAnalyzer/Core')
38 files changed, 1739 insertions, 717 deletions
diff --git a/lib/StaticAnalyzer/Core/AnalysisManager.cpp b/lib/StaticAnalyzer/Core/AnalysisManager.cpp index 5798f01370de8..54634fdffeb5f 100644 --- a/lib/StaticAnalyzer/Core/AnalysisManager.cpp +++ b/lib/StaticAnalyzer/Core/AnalysisManager.cpp @@ -18,7 +18,7 @@ AnalysisManager::AnalysisManager(ASTContext &ctx, DiagnosticsEngine &diags, const LangOptions &lang, const PathDiagnosticConsumers &PDC, StoreManagerCreator storemgr, - ConstraintManagerCreator constraintmgr, + ConstraintManagerCreator constraintmgr, CheckerManager *checkerMgr, AnalyzerOptions &Options, CodeInjector *injector) diff --git a/lib/StaticAnalyzer/Core/AnalyzerOptions.cpp b/lib/StaticAnalyzer/Core/AnalyzerOptions.cpp index 1696bcfac9c19..54c668cd2d6f9 100644 --- a/lib/StaticAnalyzer/Core/AnalyzerOptions.cpp +++ b/lib/StaticAnalyzer/Core/AnalyzerOptions.cpp @@ -65,7 +65,7 @@ IPAKind AnalyzerOptions::getIPAMode() { // Set the member variable. IPAMode = IPAConfig; } - + return IPAMode; } @@ -295,6 +295,13 @@ unsigned AnalyzerOptions::getMaxTimesInlineLarge() { return MaxTimesInlineLarge.getValue(); } +unsigned AnalyzerOptions::getMinCFGSizeTreatFunctionsAsLarge() { + if (!MinCFGSizeTreatFunctionsAsLarge.hasValue()) + MinCFGSizeTreatFunctionsAsLarge = getOptionAsInteger( + "min-cfg-size-treat-functions-as-large", 14); + return MinCFGSizeTreatFunctionsAsLarge.getValue(); +} + unsigned AnalyzerOptions::getMaxNodesPerTopLevelFunction() { if (!MaxNodesPerTopLevelFunction.hasValue()) { int DefaultValue = 0; @@ -325,3 +332,15 @@ bool AnalyzerOptions::shouldPrunePaths() { bool AnalyzerOptions::shouldConditionalizeStaticInitializers() { return getBooleanOption("cfg-conditional-static-initializers", true); } + +bool AnalyzerOptions::shouldInlineLambdas() { + if (!InlineLambdas.hasValue()) + InlineLambdas = getBooleanOption("inline-lambdas", /*Default=*/true); + return InlineLambdas.getValue(); +} + +bool AnalyzerOptions::shouldWidenLoops() { + if (!WidenLoops.hasValue()) + WidenLoops = getBooleanOption("widen-loops", /*Default=*/false); + return WidenLoops.getValue(); +} diff --git a/lib/StaticAnalyzer/Core/BlockCounter.cpp b/lib/StaticAnalyzer/Core/BlockCounter.cpp index c1ac03d5ab97d..8c99bd8084947 100644 --- a/lib/StaticAnalyzer/Core/BlockCounter.cpp +++ b/lib/StaticAnalyzer/Core/BlockCounter.cpp @@ -26,7 +26,7 @@ class CountKey { unsigned BlockID; public: - CountKey(const StackFrameContext *CS, unsigned ID) + CountKey(const StackFrameContext *CS, unsigned ID) : CallSite(CS), BlockID(ID) {} bool operator==(const CountKey &RHS) const { @@ -55,7 +55,7 @@ static inline CountMap::Factory& GetFactory(void *F) { return *static_cast<CountMap::Factory*>(F); } -unsigned BlockCounter::getNumVisited(const StackFrameContext *CallSite, +unsigned BlockCounter::getNumVisited(const StackFrameContext *CallSite, unsigned BlockID) const { CountMap M = GetMap(Data); CountMap::data_type* T = M.lookup(CountKey(CallSite, BlockID)); @@ -71,10 +71,10 @@ BlockCounter::Factory::~Factory() { } BlockCounter -BlockCounter::Factory::IncrementCount(BlockCounter BC, +BlockCounter::Factory::IncrementCount(BlockCounter BC, const StackFrameContext *CallSite, unsigned BlockID) { - return BlockCounter(GetFactory(F).add(GetMap(BC.Data), + return BlockCounter(GetFactory(F).add(GetMap(BC.Data), CountKey(CallSite, BlockID), BC.getNumVisited(CallSite, BlockID)+1).getRoot()); } diff --git a/lib/StaticAnalyzer/Core/BugReporter.cpp b/lib/StaticAnalyzer/Core/BugReporter.cpp index e4db64fe34e02..11be764633cf8 100644 --- a/lib/StaticAnalyzer/Core/BugReporter.cpp +++ b/lib/StaticAnalyzer/Core/BugReporter.cpp @@ -81,13 +81,13 @@ eventsDescribeSameCondition(PathDiagnosticEventPiece *X, // those that came from TrackConstraintBRVisitor. const void *tagPreferred = ConditionBRVisitor::getTag(); const void *tagLesser = TrackConstraintBRVisitor::getTag(); - + if (X->getLocation() != Y->getLocation()) return nullptr; if (X->getTag() == tagPreferred && Y->getTag() == tagLesser) return X; - + if (Y->getTag() == tagPreferred && X->getTag() == tagLesser) return Y; @@ -110,7 +110,7 @@ static void removeRedundantMsgs(PathPieces &path) { for (unsigned i = 0; i < N; ++i) { IntrusiveRefCntPtr<PathDiagnosticPiece> piece(path.front()); path.pop_front(); - + switch (piece->getKind()) { case clang::ento::PathDiagnosticPiece::Call: removeRedundantMsgs(cast<PathDiagnosticCallPiece>(piece)->path); @@ -123,7 +123,7 @@ static void removeRedundantMsgs(PathPieces &path) { case clang::ento::PathDiagnosticPiece::Event: { if (i == N-1) break; - + if (PathDiagnosticEventPiece *nextEvent = dyn_cast<PathDiagnosticEventPiece>(path.front().get())) { PathDiagnosticEventPiece *event = @@ -157,13 +157,13 @@ static bool removeUnneededCalls(PathPieces &pieces, BugReport *R, LocationContextMap &LCM) { bool containsSomethingInteresting = false; const unsigned N = pieces.size(); - + for (unsigned i = 0 ; i < N ; ++i) { // Remove the front piece from the path. If it is still something we // want to keep once we are done, we will push it back on the end. IntrusiveRefCntPtr<PathDiagnosticPiece> piece(pieces.front()); pieces.pop_front(); - + switch (piece->getKind()) { case PathDiagnosticPiece::Call: { PathDiagnosticCallPiece *call = cast<PathDiagnosticCallPiece>(piece); @@ -176,7 +176,7 @@ static bool removeUnneededCalls(PathPieces &pieces, BugReport *R, if (!removeUnneededCalls(call->path, R, LCM)) continue; - + containsSomethingInteresting = true; break; } @@ -189,7 +189,7 @@ static bool removeUnneededCalls(PathPieces &pieces, BugReport *R, } case PathDiagnosticPiece::Event: { PathDiagnosticEventPiece *event = cast<PathDiagnosticEventPiece>(piece); - + // We never throw away an event, but we do throw it away wholesale // as part of a path if we throw the entire path away. containsSomethingInteresting |= !event->isPrunable(); @@ -198,10 +198,10 @@ static bool removeUnneededCalls(PathPieces &pieces, BugReport *R, case PathDiagnosticPiece::ControlFlow: break; } - + pieces.push_back(piece); } - + return containsSomethingInteresting; } @@ -213,7 +213,7 @@ static bool hasImplicitBody(const Decl *D) { } /// Recursively scan through a path and make sure that all call pieces have -/// valid locations. +/// valid locations. static void adjustCallLocations(PathPieces &Pieces, PathDiagnosticLocation *LastCallLocation = nullptr) { @@ -323,7 +323,7 @@ class PathDiagnosticBuilder : public BugReporterContext { NodeMapClosure NMC; public: const LocationContext *LC; - + PathDiagnosticBuilder(GRBugReporter &br, BugReport *r, InterExplodedGraphMap &Backmap, PathDiagnosticConsumer *pdc) @@ -339,7 +339,7 @@ public: BugReport *getBugReport() { return R; } Decl const &getCodeDecl() { return R->getErrorNode()->getCodeDecl(); } - + ParentMap& getParentMap() { return LC->getParentMap(); } const Stmt *getParent(const Stmt *S) { @@ -957,7 +957,7 @@ static PathDiagnosticLocation cleanUpLocation(PathDiagnosticLocation L, if (firstCharOnly) L = PathDiagnosticLocation::createSingleLocation(L); - + return L; } @@ -1001,7 +1001,7 @@ public: ~EdgeBuilder() { while (!CLocs.empty()) popLocation(); - + // Finally, add an initial edge from the start location of the first // statement (if it doesn't already exist). PathDiagnosticLocation L = PathDiagnosticLocation::createDeclBegin( @@ -1016,7 +1016,7 @@ public: popLocation(); PrevLoc = PathDiagnosticLocation(); } - + void addEdge(PathDiagnosticLocation NewLoc, bool alwaysAdd = false, bool IsPostJump = false); @@ -1101,7 +1101,7 @@ void EdgeBuilder::rawAddEdge(PathDiagnosticLocation NewLoc) { PrevLoc = NewLoc; return; } - + if (NewLocClean.asLocation() == PrevLocClean.asLocation()) return; @@ -1242,7 +1242,7 @@ static void reversePropagateIntererstingSymbols(BugReport &R, SVal V = State->getSVal(Ex, LCtx); if (!(R.isInteresting(V) || IE.count(Ex))) return; - + switch (Ex->getStmtClass()) { default: if (!isa<CastExpr>(Ex)) @@ -1260,7 +1260,7 @@ static void reversePropagateIntererstingSymbols(BugReport &R, break; } } - + R.markInteresting(V); } @@ -1275,7 +1275,7 @@ static void reversePropagateInterestingSymbols(BugReport &R, const Stmt *CallSite = Callee->getCallSite(); if (const CallExpr *CE = dyn_cast_or_null<CallExpr>(CallSite)) { if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(CalleeCtx->getDecl())) { - FunctionDecl::param_const_iterator PI = FD->param_begin(), + FunctionDecl::param_const_iterator PI = FD->param_begin(), PE = FD->param_end(); CallExpr::const_arg_iterator AI = CE->arg_begin(), AE = CE->arg_end(); for (; AI != AE && PI != PE; ++AI, ++PI) { @@ -1406,7 +1406,7 @@ static bool GenerateExtensivePathDiagnostic( N->getState().get(), Ex, N->getLocationContext()); } - + if (Optional<CallExitEnd> CE = P.getAs<CallExitEnd>()) { const Stmt *S = CE->getCalleeContext()->getCallSite(); if (const Expr *Ex = dyn_cast_or_null<Expr>(S)) { @@ -1414,7 +1414,7 @@ static bool GenerateExtensivePathDiagnostic( N->getState().get(), Ex, N->getLocationContext()); } - + PathDiagnosticCallPiece *C = PathDiagnosticCallPiece::construct(N, *CE, SM); LCM[&C->path] = CE->getCalleeContext(); @@ -1427,7 +1427,7 @@ static bool GenerateExtensivePathDiagnostic( CallStack.push_back(StackDiagPair(C, N)); break; } - + // Pop the call hierarchy if we are done walking the contents // of a function call. if (Optional<CallEnter> CE = P.getAs<CallEnter>()) { @@ -1436,7 +1436,7 @@ static bool GenerateExtensivePathDiagnostic( PathDiagnosticLocation pos = PathDiagnosticLocation::createBegin(D, SM); EB.addEdge(pos); - + // Flush all locations, and pop the active path. bool VisitedEntireCall = PD.isWithinCall(); EB.flushLocations(); @@ -1466,7 +1466,7 @@ static bool GenerateExtensivePathDiagnostic( } break; } - + // Note that is important that we update the LocationContext // after looking at CallExits. CallExit basically adds an // edge in the *caller*, so we don't want to update the LocationContext @@ -1486,7 +1486,7 @@ static bool GenerateExtensivePathDiagnostic( CalleeCtx, CallerCtx); } } - + // Are we jumping to the head of a loop? Add a special diagnostic. if (const Stmt *Loop = BE->getSrc()->getLoopTarget()) { PathDiagnosticLocation L(Loop, SM, PDB.LC); @@ -1552,11 +1552,11 @@ static bool GenerateExtensivePathDiagnostic( else EB.addExtendedContext(PDB.getEnclosingStmtLocation(stmt).asStmt()); } - + break; } - - + + } while (0); if (!NextNode) @@ -2410,7 +2410,7 @@ static bool optimizeEdges(PathPieces &path, SourceManager &SM, // Trim edges on expressions that are consumed by // the parent expression. if (isa<Expr>(s1End) && PM.isConsumedExpr(cast<Expr>(s1End))) { - removeEdge = true; + removeEdge = true; } // Trim edges where a lexical containment doesn't exist. // For example: @@ -2557,7 +2557,7 @@ BugReport::~BugReport() { const Decl *BugReport::getDeclWithIssue() const { if (DeclWithIssue) return DeclWithIssue; - + const ExplodedNode *N = getErrorNode(); if (!N) return nullptr; @@ -2579,9 +2579,7 @@ void BugReport::Profile(llvm::FoldingSetNodeID& hash) const { hash.AddPointer(GetCurrentOrPreviousStmt(ErrorNode)); } - for (SmallVectorImpl<SourceRange>::const_iterator I = - Ranges.begin(), E = Ranges.end(); I != E; ++I) { - const SourceRange range = *I; + for (SourceRange range : Ranges) { if (!range.isValid()) continue; hash.AddInteger(range.getBegin().getRawEncoding()); @@ -2714,8 +2712,7 @@ llvm::iterator_range<BugReport::ranges_iterator> BugReport::getRanges() { if (Ranges.size() == 1 && !Ranges.begin()->isValid()) return llvm::make_range(ranges_iterator(), ranges_iterator()); - return llvm::iterator_range<BugReport::ranges_iterator>(Ranges.begin(), - Ranges.end()); + return llvm::make_range(Ranges.begin(), Ranges.end()); } PathDiagnosticLocation BugReport::getLocation(const SourceManager &SM) const { @@ -2973,14 +2970,14 @@ static void CompactPathDiagnostic(PathPieces &path, const SourceManager& SM) { for (PathPieces::const_iterator I = path.begin(), E = path.end(); I!=E; ++I) { - + PathDiagnosticPiece *piece = I->get(); // Recursively compact calls. if (PathDiagnosticCallPiece *call=dyn_cast<PathDiagnosticCallPiece>(piece)){ CompactPathDiagnostic(call->path, SM); } - + // Get the location of the PathDiagnosticPiece. const FullSourceLoc Loc = piece->getLocation().asLocation(); @@ -3126,7 +3123,7 @@ bool GRBugReporter::generatePathDiagnostic(PathDiagnostic& PD, PD.resetPath(); origReportConfigToken = R->getConfigurationChangeToken(); - // Generate the very last diagnostic piece - the piece is visible before + // Generate the very last diagnostic piece - the piece is visible before // the trace is expanded. std::unique_ptr<PathDiagnosticPiece> LastPiece; for (BugReport::visitor_iterator I = visitors.begin(), E = visitors.end(); @@ -3224,6 +3221,11 @@ void BugReporter::Register(BugType *BT) { void BugReporter::emitReport(std::unique_ptr<BugReport> R) { if (const ExplodedNode *E = R->getErrorNode()) { + // An error node must either be a sink or have a tag, otherwise + // it could get reclaimed before the path diagnostic is created. + assert((E->isSink() || E->getLocation().getTag()) && + "Error node must either be a sink or have a tag"); + const AnalysisDeclContext *DeclCtx = E->getLocationContext()->getAnalysisDeclContext(); // The source of autosynthesized body can be handcrafted AST or a model @@ -3234,7 +3236,7 @@ void BugReporter::emitReport(std::unique_ptr<BugReport> R) { !DeclCtx->isBodyAutosynthesizedFromModelFile()) return; } - + bool ValidSourceLoc = R->getLocation(getSourceManager()).isValid(); assert(ValidSourceLoc); // If we mess up in a release build, we'd still prefer to just drop the bug @@ -3269,10 +3271,10 @@ namespace { struct FRIEC_WLItem { const ExplodedNode *N; ExplodedNode::const_succ_iterator I, E; - + FRIEC_WLItem(const ExplodedNode *n) : N(n), I(N->succ_begin()), E(N->succ_end()) {} -}; +}; } static BugReport * @@ -3287,11 +3289,11 @@ FindReportInEquivalenceClass(BugReportEquivClass& EQ, // post-dominated by a sink, simply add all the nodes in the equivalence class // to 'Nodes'. Any of the reports will serve as a "representative" report. if (!BT.isSuppressOnSink()) { - BugReport *R = I; + BugReport *R = &*I; for (BugReportEquivClass::iterator I=EQ.begin(), E=EQ.end(); I!=E; ++I) { const ExplodedNode *N = I->getErrorNode(); if (N) { - R = I; + R = &*I; bugReports.push_back(R); } } @@ -3317,35 +3319,35 @@ FindReportInEquivalenceClass(BugReportEquivClass& EQ, } // No successors? By definition this nodes isn't post-dominated by a sink. if (errorNode->succ_empty()) { - bugReports.push_back(I); + bugReports.push_back(&*I); if (!exampleReport) - exampleReport = I; + exampleReport = &*I; continue; } // At this point we know that 'N' is not a sink and it has at least one - // successor. Use a DFS worklist to find a non-sink end-of-path node. + // successor. Use a DFS worklist to find a non-sink end-of-path node. typedef FRIEC_WLItem WLItem; typedef SmallVector<WLItem, 10> DFSWorkList; llvm::DenseMap<const ExplodedNode *, unsigned> Visited; - + DFSWorkList WL; WL.push_back(errorNode); Visited[errorNode] = 1; - + while (!WL.empty()) { WLItem &WI = WL.back(); assert(!WI.N->succ_empty()); - + for (; WI.I != WI.E; ++WI.I) { - const ExplodedNode *Succ = *WI.I; + const ExplodedNode *Succ = *WI.I; // End-of-path node? if (Succ->succ_empty()) { // If we found an end-of-path node that is not a sink. if (!Succ->isSink()) { - bugReports.push_back(I); + bugReports.push_back(&*I); if (!exampleReport) - exampleReport = I; + exampleReport = &*I; WL.clear(); break; } @@ -3426,7 +3428,7 @@ void BugReporter::FlushReport(BugReport *exampleReport, PathDiagnosticLocation L = exampleReport->getLocation(getSourceManager()); auto piece = llvm::make_unique<PathDiagnosticEventPiece>( L, exampleReport->getDescription()); - for (const SourceRange &Range : exampleReport->getRanges()) + for (SourceRange Range : exampleReport->getRanges()) piece->addRange(Range); D->setEndOfPath(std::move(piece)); } diff --git a/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp b/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp index f2915ed818b28..ec1310d91814c 100644 --- a/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp +++ b/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp @@ -117,7 +117,7 @@ std::unique_ptr<PathDiagnosticPiece> BugReporterVisitor::getDefaultEndPath( // special ranges for this report. auto P = llvm::make_unique<PathDiagnosticEventPiece>( L, BR.getDescription(), Ranges.begin() == Ranges.end()); - for (const SourceRange &Range : Ranges) + for (SourceRange Range : Ranges) P->addRange(Range); return std::move(P); @@ -169,7 +169,7 @@ public: bool InEnableNullFPSuppression) { if (!CallEvent::isCallStmt(S)) return; - + // First, find when we processed the statement. do { if (Optional<CallExitEnd> CEE = Node->getLocationAs<CallExitEnd>()) @@ -192,11 +192,11 @@ public: Optional<CallExitEnd> CEE = Node->getLocationAs<CallExitEnd>(); if (!CEE) return; - + const StackFrameContext *CalleeContext = CEE->getCalleeContext(); if (CalleeContext->getCallSite() != S) return; - + // Check the return value. ProgramStateRef State = Node->getState(); SVal RetVal = State->getSVal(S, Node->getLocationContext()); @@ -281,7 +281,7 @@ public: EnableNullFPSuppression); return nullptr; } - + // If we're returning 0, we should track where that 0 came from. bugreporter::trackNullOrUndefValue(N, RetE, BR, /*IsArg*/ false, EnableNullFPSuppression); @@ -472,7 +472,7 @@ PathDiagnosticPiece *FindLastStoreBRVisitor::VisitNode(const ExplodedNode *Succ, InitE = PIP->getInitializer()->getInit(); } } - + // Otherwise, see if this is the store site: // (1) Succ has this binding and Pred does not, i.e. this is // where the binding first occurred. @@ -504,7 +504,7 @@ PathDiagnosticPiece *FindLastStoreBRVisitor::VisitNode(const ExplodedNode *Succ, if (Optional<CallEnter> CE = Succ->getLocationAs<CallEnter>()) { if (const VarRegion *VR = dyn_cast<VarRegion>(R)) { const ParmVarDecl *Param = cast<ParmVarDecl>(VR->getDecl()); - + ProgramStateManager &StateMgr = BRC.getStateManager(); CallEventManager &CallMgr = StateMgr.getCallEventManager(); @@ -681,7 +681,7 @@ PathDiagnosticPiece *FindLastStoreBRVisitor::VisitNode(const ExplodedNode *Succ, else os << "Assigning value"; } - + if (R->canPrintPretty()) { os << " to "; R->printPretty(os); @@ -931,7 +931,7 @@ bool bugreporter::trackNullOrUndefValue(const ExplodedNode *N, if (!N) return false; } - + ProgramStateRef state = N->getState(); // The message send could be nil due to the receiver being nil. @@ -959,7 +959,7 @@ bool bugreporter::trackNullOrUndefValue(const ExplodedNode *N, assert(LVNode && "Unable to find the lvalue node."); ProgramStateRef LVState = LVNode->getState(); SVal LVal = LVState->getSVal(Inner, LVNode->getLocationContext()); - + if (LVState->isNull(LVal).isConstrainedTrue()) { // In case of C++ references, we want to differentiate between a null // reference and reference to null pointer. @@ -1162,11 +1162,11 @@ PathDiagnosticPiece *ConditionBRVisitor::VisitNodeImpl(const ExplodedNode *N, const ExplodedNode *Prev, BugReporterContext &BRC, BugReport &BR) { - + ProgramPoint progPoint = N->getLocation(); ProgramStateRef CurrentState = N->getState(); ProgramStateRef PrevState = Prev->getState(); - + // Compare the GDMs of the state, because that is where constraints // are managed. Note that ensure that we only look at nodes that // were generated by the analyzer engine proper, not checkers. @@ -1177,16 +1177,16 @@ PathDiagnosticPiece *ConditionBRVisitor::VisitNodeImpl(const ExplodedNode *N, // If an assumption was made on a branch, it should be caught // here by looking at the state transition. if (Optional<BlockEdge> BE = progPoint.getAs<BlockEdge>()) { - const CFGBlock *srcBlk = BE->getSrc(); + const CFGBlock *srcBlk = BE->getSrc(); if (const Stmt *term = srcBlk->getTerminator()) return VisitTerminator(term, N, srcBlk, BE->getDst(), BR, BRC); return nullptr; } - + if (Optional<PostStmt> PS = progPoint.getAs<PostStmt>()) { // FIXME: Assuming that BugReporter is a GRBugReporter is a layering // violation. - const std::pair<const ProgramPointTag *, const ProgramPointTag *> &tags = + const std::pair<const ProgramPointTag *, const ProgramPointTag *> &tags = cast<GRBugReporter>(BRC.getBugReporter()). getEngine().geteagerlyAssumeBinOpBifurcationTags(); @@ -1222,7 +1222,7 @@ ConditionBRVisitor::VisitTerminator(const Stmt *Term, case Stmt::ConditionalOperatorClass: Cond = cast<ConditionalOperator>(Term)->getCond(); break; - } + } assert(Cond); assert(srcBlk->succ_size() == 2); @@ -1236,9 +1236,9 @@ ConditionBRVisitor::VisitTrueTest(const Expr *Cond, BugReporterContext &BRC, BugReport &R, const ExplodedNode *N) { - + const Expr *Ex = Cond; - + while (true) { Ex = Ex->IgnoreParenCasts(); switch (Ex->getStmtClass()) { @@ -1294,7 +1294,7 @@ bool ConditionBRVisitor::patternMatch(const Expr *Ex, raw_ostream &Out, Out << '\''; return quotes; } - + if (const IntegerLiteral *IL = dyn_cast<IntegerLiteral>(Ex)) { QualType OriginalTy = OriginalExpr->getType(); if (OriginalTy->isPointerType()) { @@ -1309,11 +1309,11 @@ bool ConditionBRVisitor::patternMatch(const Expr *Ex, raw_ostream &Out, return false; } } - + Out << IL->getValue(); return false; } - + return false; } @@ -1324,10 +1324,10 @@ ConditionBRVisitor::VisitTrueTest(const Expr *Cond, BugReporterContext &BRC, BugReport &R, const ExplodedNode *N) { - + bool shouldInvert = false; Optional<bool> shouldPrune; - + SmallString<128> LhsString, RhsString; { llvm::raw_svector_ostream OutLHS(LhsString), OutRHS(RhsString); @@ -1335,10 +1335,10 @@ ConditionBRVisitor::VisitTrueTest(const Expr *Cond, shouldPrune); const bool isVarRHS = patternMatch(BExpr->getRHS(), OutRHS, BRC, R, N, shouldPrune); - - shouldInvert = !isVarLHS && isVarRHS; + + shouldInvert = !isVarLHS && isVarRHS; } - + BinaryOperator::Opcode Op = BExpr->getOpcode(); if (BinaryOperator::isAssignmentOp(Op)) { @@ -1380,7 +1380,7 @@ ConditionBRVisitor::VisitTrueTest(const Expr *Cond, default: return nullptr; } - + switch (Op) { case BO_EQ: Out << "equal to "; @@ -1392,7 +1392,7 @@ ConditionBRVisitor::VisitTrueTest(const Expr *Cond, Out << BinaryOperator::getOpcodeStr(Op) << ' '; break; } - + Out << (shouldInvert ? LhsString : RhsString); const LocationContext *LCtx = N->getLocationContext(); PathDiagnosticLocation Loc(Cond, BRC.getSourceManager(), LCtx); @@ -1416,7 +1416,7 @@ ConditionBRVisitor::VisitConditionVariable(StringRef LhsString, SmallString<256> buf; llvm::raw_svector_ostream Out(buf); Out << "Assuming " << LhsString << " is "; - + QualType Ty = CondVarExpr->getType(); if (Ty->isPointerType()) @@ -1444,10 +1444,10 @@ ConditionBRVisitor::VisitConditionVariable(StringRef LhsString, } } } - + return event; } - + PathDiagnosticPiece * ConditionBRVisitor::VisitTrueTest(const Expr *Cond, const DeclRefExpr *DR, @@ -1462,11 +1462,11 @@ ConditionBRVisitor::VisitTrueTest(const Expr *Cond, SmallString<256> Buf; llvm::raw_svector_ostream Out(Buf); - + Out << "Assuming '" << VD->getDeclName() << "' is "; - + QualType VDTy = VD->getType(); - + if (VDTy->isPointerType()) Out << (tookTrue ? "non-null" : "null"); else if (VDTy->isObjCObjectPointerType()) @@ -1480,7 +1480,7 @@ ConditionBRVisitor::VisitTrueTest(const Expr *Cond, PathDiagnosticLocation Loc(Cond, BRC.getSourceManager(), LCtx); PathDiagnosticEventPiece *event = new PathDiagnosticEventPiece(Loc, Out.str()); - + const ProgramState *state = N->getState().get(); if (const MemRegion *R = state->getLValue(VD, LCtx).getAsRegion()) { if (report.isInteresting(R)) @@ -1615,13 +1615,13 @@ UndefOrNullArgVisitor::VisitNode(const ExplodedNode *N, // Function can only change the value passed in by address. continue; } - + // If it is a const pointer value, the function does not intend to // change the value. if (T->getPointeeType().isConstQualified()) continue; - // Mark the call site (LocationContext) as interesting if the value of the + // Mark the call site (LocationContext) as interesting if the value of the // argument is undefined or '0'/'NULL'. SVal BoundVal = State->getSVal(R); if (BoundVal.isUndef() || BoundVal.isZeroConstant()) { diff --git a/lib/StaticAnalyzer/Core/CMakeLists.txt b/lib/StaticAnalyzer/Core/CMakeLists.txt index 59a6b6fbc5951..aaffb0b82ce00 100644 --- a/lib/StaticAnalyzer/Core/CMakeLists.txt +++ b/lib/StaticAnalyzer/Core/CMakeLists.txt @@ -6,6 +6,7 @@ add_clang_library(clangStaticAnalyzerCore AnalyzerOptions.cpp BasicValueFactory.cpp BlockCounter.cpp + IssueHash.cpp BugReporter.cpp BugReporterVisitors.cpp CallEvent.cpp @@ -17,6 +18,7 @@ add_clang_library(clangStaticAnalyzerCore CommonBugCategories.cpp ConstraintManager.cpp CoreEngine.cpp + DynamicTypeMap.cpp Environment.cpp ExplodedGraph.cpp ExprEngine.cpp @@ -26,6 +28,7 @@ add_clang_library(clangStaticAnalyzerCore ExprEngineObjC.cpp FunctionSummary.cpp HTMLDiagnostics.cpp + LoopWidening.cpp MemRegion.cpp PathDiagnostic.cpp PlistDiagnostics.cpp diff --git a/lib/StaticAnalyzer/Core/CallEvent.cpp b/lib/StaticAnalyzer/Core/CallEvent.cpp index 8542f7d6a86b5..69af09b25b6e2 100644 --- a/lib/StaticAnalyzer/Core/CallEvent.cpp +++ b/lib/StaticAnalyzer/Core/CallEvent.cpp @@ -17,6 +17,7 @@ #include "clang/AST/ParentMap.h" #include "clang/Analysis/ProgramPoint.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/DynamicTypeMap.h" #include "llvm/ADT/SmallSet.h" #include "llvm/ADT/StringExtras.h" #include "llvm/Support/raw_ostream.h" @@ -49,11 +50,7 @@ QualType CallEvent::getResultType() const { return ResultTy; } -static bool isCallbackArg(SVal V, QualType T) { - // If the parameter is 0, it's harmless. - if (V.isZeroConstant()) - return false; - +static bool isCallback(QualType T) { // If a parameter is a block or a callback, assume it can modify pointer. if (T->isBlockPointerType() || T->isFunctionPointerType() || @@ -74,32 +71,53 @@ static bool isCallbackArg(SVal V, QualType T) { return true; } } - return false; } -bool CallEvent::hasNonZeroCallbackArg() const { +static bool isVoidPointerToNonConst(QualType T) { + if (const PointerType *PT = T->getAs<PointerType>()) { + QualType PointeeTy = PT->getPointeeType(); + if (PointeeTy.isConstQualified()) + return false; + return PointeeTy->isVoidType(); + } else + return false; +} + +bool CallEvent::hasNonNullArgumentsWithType(bool (*Condition)(QualType)) const { unsigned NumOfArgs = getNumArgs(); // If calling using a function pointer, assume the function does not - // have a callback. TODO: We could check the types of the arguments here. + // satisfy the callback. + // TODO: We could check the types of the arguments here. if (!getDecl()) return false; unsigned Idx = 0; for (CallEvent::param_type_iterator I = param_type_begin(), - E = param_type_end(); + E = param_type_end(); I != E && Idx < NumOfArgs; ++I, ++Idx) { if (NumOfArgs <= Idx) break; - if (isCallbackArg(getArgSVal(Idx), *I)) + // If the parameter is 0, it's harmless. + if (getArgSVal(Idx).isZeroConstant()) + continue; + + if (Condition(*I)) return true; } - return false; } +bool CallEvent::hasNonZeroCallbackArg() const { + return hasNonNullArgumentsWithType(isCallback); +} + +bool CallEvent::hasVoidPointerToNonConstArg() const { + return hasNonNullArgumentsWithType(isVoidPointerToNonConst); +} + bool CallEvent::isGlobalCFunction(StringRef FunctionName) const { const FunctionDecl *FD = dyn_cast_or_null<FunctionDecl>(getDecl()); if (!FD) @@ -147,7 +165,7 @@ ProgramStateRef CallEvent::invalidateRegions(unsigned BlockCount, SmallVector<SVal, 8> ValuesToInvalidate; RegionAndSymbolInvalidationTraits ETraits; - getExtraInvalidatedValues(ValuesToInvalidate); + getExtraInvalidatedValues(ValuesToInvalidate, &ETraits); // Indexes of arguments whose values will be preserved by the call. llvm::SmallSet<unsigned, 4> PreserveArgs; @@ -159,7 +177,7 @@ ProgramStateRef CallEvent::invalidateRegions(unsigned BlockCount, // below for efficiency. if (PreserveArgs.count(Idx)) if (const MemRegion *MR = getArgSVal(Idx).getAsRegion()) - ETraits.setTrait(MR->StripCasts(), + ETraits.setTrait(MR->StripCasts(), RegionAndSymbolInvalidationTraits::TK_PreserveContents); // TODO: Factor this out + handle the lower level const pointers. @@ -184,7 +202,7 @@ ProgramPoint CallEvent::getProgramPoint(bool IsPreVisit, } const Decl *D = getDecl(); - assert(D && "Cannot get a program point without a statement or decl"); + assert(D && "Cannot get a program point without a statement or decl"); SourceLocation Loc = getSourceRange().getBegin(); if (IsPreVisit) @@ -265,7 +283,7 @@ QualType CallEvent::getDeclaredResultType(const Decl *D) { return QualType(); } - + llvm_unreachable("unknown callable kind"); } @@ -325,7 +343,7 @@ void AnyFunctionCall::getInitialStackFrameContents( } bool AnyFunctionCall::argumentsMayEscape() const { - if (hasNonZeroCallbackArg()) + if (CallEvent::argumentsMayEscape() || hasVoidPointerToNonConstArg()) return true; const FunctionDecl *D = getDecl(); @@ -336,7 +354,7 @@ bool AnyFunctionCall::argumentsMayEscape() const { if (!II) return false; - // This set of "escaping" APIs is + // This set of "escaping" APIs is // - 'int pthread_setspecific(ptheread_key k, const void *)' stores a // value into thread local storage. The value can later be retrieved with @@ -402,8 +420,30 @@ const FunctionDecl *CXXInstanceCall::getDecl() const { return getSVal(CE->getCallee()).getAsFunctionDecl(); } -void CXXInstanceCall::getExtraInvalidatedValues(ValueList &Values) const { - Values.push_back(getCXXThisVal()); +void CXXInstanceCall::getExtraInvalidatedValues( + ValueList &Values, RegionAndSymbolInvalidationTraits *ETraits) const { + SVal ThisVal = getCXXThisVal(); + Values.push_back(ThisVal); + + // Don't invalidate if the method is const and there are no mutable fields. + if (const CXXMethodDecl *D = cast_or_null<CXXMethodDecl>(getDecl())) { + if (!D->isConst()) + return; + // Get the record decl for the class of 'This'. D->getParent() may return a + // base class decl, rather than the class of the instance which needs to be + // checked for mutable fields. + const Expr *Ex = getCXXThisExpr()->ignoreParenBaseCasts(); + const CXXRecordDecl *ParentRecord = Ex->getType()->getAsCXXRecordDecl(); + if (!ParentRecord || ParentRecord->hasMutableFields()) + return; + // Preserve CXXThis. + const MemRegion *ThisRegion = ThisVal.getAsRegion(); + if (!ThisRegion) + return; + + ETraits->setTrait(ThisRegion->getBaseRegion(), + RegionAndSymbolInvalidationTraits::TK_PreserveContents); + } } SVal CXXInstanceCall::getCXXThisVal() const { @@ -435,7 +475,7 @@ RuntimeDefinition CXXInstanceCall::getRuntimeDefinition() const { return RuntimeDefinition(); // Do we know anything about the type of 'this'? - DynamicTypeInfo DynType = getState()->getDynamicTypeInfo(R); + DynamicTypeInfo DynType = getDynamicTypeInfo(getState(), R); if (!DynType.isValid()) return RuntimeDefinition(); @@ -455,7 +495,7 @@ RuntimeDefinition CXXInstanceCall::getRuntimeDefinition() const { // However, we should at least be able to search up and down our own class // hierarchy, and some real bugs have been caught by checking this. assert(!RD->isDerivedFrom(MD->getParent()) && "Couldn't find known method"); - + // FIXME: This is checking that our DynamicTypeInfo is at least as good as // the static type. However, because we currently don't update // DynamicTypeInfo when an object is cast, we can't actually be sure the @@ -525,7 +565,7 @@ RuntimeDefinition CXXMemberCall::getRuntimeDefinition() const { if (const MemberExpr *ME = dyn_cast<MemberExpr>(getOriginExpr()->getCallee())) if (ME->hasQualifier()) return AnyFunctionCall::getRuntimeDefinition(); - + return CXXInstanceCall::getRuntimeDefinition(); } @@ -549,7 +589,8 @@ ArrayRef<ParmVarDecl*> BlockCall::parameters() const { return D->parameters(); } -void BlockCall::getExtraInvalidatedValues(ValueList &Values) const { +void BlockCall::getExtraInvalidatedValues(ValueList &Values, + RegionAndSymbolInvalidationTraits *ETraits) const { // FIXME: This also needs to invalidate captured globals. if (const MemRegion *R = getBlockRegion()) Values.push_back(loc::MemRegionVal(R)); @@ -557,10 +598,25 @@ void BlockCall::getExtraInvalidatedValues(ValueList &Values) const { void BlockCall::getInitialStackFrameContents(const StackFrameContext *CalleeCtx, BindingsTy &Bindings) const { - const BlockDecl *D = cast<BlockDecl>(CalleeCtx->getDecl()); SValBuilder &SVB = getState()->getStateManager().getSValBuilder(); + ArrayRef<ParmVarDecl*> Params; + if (isConversionFromLambda()) { + auto *LambdaOperatorDecl = cast<CXXMethodDecl>(CalleeCtx->getDecl()); + Params = LambdaOperatorDecl->parameters(); + + // For blocks converted from a C++ lambda, the callee declaration is the + // operator() method on the lambda so we bind "this" to + // the lambda captured by the block. + const VarRegion *CapturedLambdaRegion = getRegionStoringCapturedLambda(); + SVal ThisVal = loc::MemRegionVal(CapturedLambdaRegion); + Loc ThisLoc = SVB.getCXXThis(LambdaOperatorDecl, CalleeCtx); + Bindings.push_back(std::make_pair(ThisLoc, ThisVal)); + } else { + Params = cast<BlockDecl>(CalleeCtx->getDecl())->parameters(); + } + addParameterValuesToBindings(CalleeCtx, Bindings, SVB, *this, - D->parameters()); + Params); } @@ -570,7 +626,8 @@ SVal CXXConstructorCall::getCXXThisVal() const { return UnknownVal(); } -void CXXConstructorCall::getExtraInvalidatedValues(ValueList &Values) const { +void CXXConstructorCall::getExtraInvalidatedValues(ValueList &Values, + RegionAndSymbolInvalidationTraits *ETraits) const { if (Data) Values.push_back(loc::MemRegionVal(static_cast<const MemRegion *>(Data))); } @@ -612,7 +669,8 @@ ArrayRef<ParmVarDecl*> ObjCMethodCall::parameters() const { } void -ObjCMethodCall::getExtraInvalidatedValues(ValueList &Values) const { +ObjCMethodCall::getExtraInvalidatedValues(ValueList &Values, + RegionAndSymbolInvalidationTraits *ETraits) const { Values.push_back(getReceiverSVal()); } @@ -628,7 +686,7 @@ SVal ObjCMethodCall::getReceiverSVal() const { // FIXME: Is this the best way to handle class receivers? if (!isInstanceMessage()) return UnknownVal(); - + if (const Expr *RecE = getOriginExpr()->getInstanceReceiver()) return getSVal(RecE); @@ -709,7 +767,7 @@ ObjCMessageKind ObjCMethodCall::getMessageKind() const { return K; } } - + const_cast<ObjCMethodCall *>(this)->Data = ObjCMessageDataTy(nullptr, 1).getOpaqueValue(); assert(getMessageKind() == OCM_Message); @@ -730,7 +788,7 @@ bool ObjCMethodCall::canBeOverridenInSubclass(ObjCInterfaceDecl *IDecl, getState()->getStateManager().getContext().getSourceManager(); // If the class interface is declared inside the main file, assume it is not - // subcassed. + // subcassed. // TODO: It could actually be subclassed if the subclass is private as well. // This is probably very rare. SourceLocation InterfLoc = IDecl->getEndOfDefinitionLoc(); @@ -800,7 +858,7 @@ RuntimeDefinition ObjCMethodCall::getRuntimeDefinition() const { if (!Receiver) return RuntimeDefinition(); - DynamicTypeInfo DTI = getState()->getDynamicTypeInfo(Receiver); + DynamicTypeInfo DTI = getDynamicTypeInfo(getState(), Receiver); QualType DynType = DTI.getType(); CanBeSubClassed = DTI.canBeASubClass(); ReceiverT = dyn_cast<ObjCObjectPointerType>(DynType); diff --git a/lib/StaticAnalyzer/Core/Checker.cpp b/lib/StaticAnalyzer/Core/Checker.cpp index 22352111d14a8..b422a8871983b 100644 --- a/lib/StaticAnalyzer/Core/Checker.cpp +++ b/lib/StaticAnalyzer/Core/Checker.cpp @@ -23,7 +23,7 @@ StringRef CheckerBase::getTagDescription() const { CheckName CheckerBase::getCheckName() const { return Name; } -CheckerProgramPointTag::CheckerProgramPointTag(StringRef CheckerName, +CheckerProgramPointTag::CheckerProgramPointTag(StringRef CheckerName, StringRef Msg) : SimpleProgramPointTag(CheckerName, Msg) {} diff --git a/lib/StaticAnalyzer/Core/CheckerContext.cpp b/lib/StaticAnalyzer/Core/CheckerContext.cpp index 6b22bf411c293..5ec8bfa800744 100644 --- a/lib/StaticAnalyzer/Core/CheckerContext.cpp +++ b/lib/StaticAnalyzer/Core/CheckerContext.cpp @@ -45,7 +45,7 @@ bool CheckerContext::isCLibraryFunction(const FunctionDecl *FD, if (BId != 0) { if (Name.empty()) return true; - StringRef BName = FD->getASTContext().BuiltinInfo.GetName(BId); + StringRef BName = FD->getASTContext().BuiltinInfo.getName(BId); if (BName.find(Name) != StringRef::npos) return true; } @@ -57,12 +57,8 @@ bool CheckerContext::isCLibraryFunction(const FunctionDecl *FD, return false; // Look through 'extern "C"' and anything similar invented in the future. - const DeclContext *DC = FD->getDeclContext(); - while (DC->isTransparentContext()) - DC = DC->getParent(); - - // If this function is in a namespace, it is not a C library function. - if (!DC->isTranslationUnit()) + // If this function is not in TU directly, it is not a C library function. + if (!FD->getDeclContext()->getRedeclContext()->isTranslationUnit()) return false; // If this function is not externally visible, it is not a C library function. diff --git a/lib/StaticAnalyzer/Core/CheckerHelpers.cpp b/lib/StaticAnalyzer/Core/CheckerHelpers.cpp index 3d9a81581549d..d6aeceb1457db 100644 --- a/lib/StaticAnalyzer/Core/CheckerHelpers.cpp +++ b/lib/StaticAnalyzer/Core/CheckerHelpers.cpp @@ -12,6 +12,7 @@ //===----------------------------------------------------------------------===// #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerHelpers.h" +#include "clang/AST/Decl.h" #include "clang/AST/Expr.h" // Recursively find any substatements containing macros @@ -70,3 +71,26 @@ bool clang::ento::containsBuiltinOffsetOf(const Stmt *S) { return false; } + +// Extract lhs and rhs from assignment statement +std::pair<const clang::VarDecl *, const clang::Expr *> +clang::ento::parseAssignment(const Stmt *S) { + const VarDecl *VD = 0; + const Expr *RHS = 0; + + if (auto Assign = dyn_cast_or_null<BinaryOperator>(S)) { + if (Assign->isAssignmentOp()) { + // Ordinary assignment + RHS = Assign->getRHS(); + if (auto DE = dyn_cast_or_null<DeclRefExpr>(Assign->getLHS())) + VD = dyn_cast_or_null<VarDecl>(DE->getDecl()); + } + } else if (auto PD = dyn_cast_or_null<DeclStmt>(S)) { + // Initialization + assert(PD->isSingleDecl() && "We process decls one by one"); + VD = dyn_cast_or_null<VarDecl>(PD->getSingleDecl()); + RHS = VD->getAnyInitializer(); + } + + return std::make_pair(VD, RHS); +} diff --git a/lib/StaticAnalyzer/Core/CheckerManager.cpp b/lib/StaticAnalyzer/Core/CheckerManager.cpp index 2684cc78be750..008e8ef31cda7 100644 --- a/lib/StaticAnalyzer/Core/CheckerManager.cpp +++ b/lib/StaticAnalyzer/Core/CheckerManager.cpp @@ -177,7 +177,9 @@ void CheckerManager::runCheckersForStmt(bool isPreVisit, namespace { struct CheckObjCMessageContext { typedef std::vector<CheckerManager::CheckObjCMessageFunc> CheckersTy; - bool IsPreVisit, WasInlined; + + ObjCMessageVisitKind Kind; + bool WasInlined; const CheckersTy &Checkers; const ObjCMethodCall &Msg; ExprEngine &Eng; @@ -185,14 +187,28 @@ namespace { CheckersTy::const_iterator checkers_begin() { return Checkers.begin(); } CheckersTy::const_iterator checkers_end() { return Checkers.end(); } - CheckObjCMessageContext(bool isPreVisit, const CheckersTy &checkers, + CheckObjCMessageContext(ObjCMessageVisitKind visitKind, + const CheckersTy &checkers, const ObjCMethodCall &msg, ExprEngine &eng, bool wasInlined) - : IsPreVisit(isPreVisit), WasInlined(wasInlined), Checkers(checkers), + : Kind(visitKind), WasInlined(wasInlined), Checkers(checkers), Msg(msg), Eng(eng) { } void runChecker(CheckerManager::CheckObjCMessageFunc checkFn, NodeBuilder &Bldr, ExplodedNode *Pred) { + + bool IsPreVisit; + + switch (Kind) { + case ObjCMessageVisitKind::Pre: + IsPreVisit = true; + break; + case ObjCMessageVisitKind::MessageNil: + case ObjCMessageVisitKind::Post: + IsPreVisit = false; + break; + } + const ProgramPoint &L = Msg.getProgramPoint(IsPreVisit,checkFn.Checker); CheckerContext C(Bldr, Eng, Pred, L, WasInlined); @@ -202,19 +218,30 @@ namespace { } /// \brief Run checkers for visiting obj-c messages. -void CheckerManager::runCheckersForObjCMessage(bool isPreVisit, +void CheckerManager::runCheckersForObjCMessage(ObjCMessageVisitKind visitKind, ExplodedNodeSet &Dst, const ExplodedNodeSet &Src, const ObjCMethodCall &msg, ExprEngine &Eng, bool WasInlined) { - CheckObjCMessageContext C(isPreVisit, - isPreVisit ? PreObjCMessageCheckers - : PostObjCMessageCheckers, - msg, Eng, WasInlined); + auto &checkers = getObjCMessageCheckers(visitKind); + CheckObjCMessageContext C(visitKind, checkers, msg, Eng, WasInlined); expandGraphWithCheckers(C, Dst, Src); } +const std::vector<CheckerManager::CheckObjCMessageFunc> & +CheckerManager::getObjCMessageCheckers(ObjCMessageVisitKind Kind) { + switch (Kind) { + case ObjCMessageVisitKind::Pre: + return PreObjCMessageCheckers; + break; + case ObjCMessageVisitKind::Post: + return PostObjCMessageCheckers; + case ObjCMessageVisitKind::MessageNil: + return ObjCMessageNilCheckers; + } + llvm_unreachable("Unknown Kind"); +} namespace { // FIXME: This has all the same signatures as CheckObjCMessageContext. // Is there a way we can merge the two? @@ -357,9 +384,9 @@ void CheckerManager::runCheckersForEndFunction(NodeBuilderContext &BC, ExplodedNodeSet &Dst, ExplodedNode *Pred, ExprEngine &Eng) { - + // We define the builder outside of the loop bacause if at least one checkers - // creates a sucsessor for Pred, we do not need to generate an + // creates a sucsessor for Pred, we do not need to generate an // autotransition for it. NodeBuilder Bldr(Pred, Dst, BC); for (unsigned i = 0, e = EndFunctionCheckers.size(); i != e; ++i) { @@ -467,7 +494,7 @@ bool CheckerManager::wantsRegionChangeUpdate(ProgramStateRef state) { } /// \brief Run checkers for region changes. -ProgramStateRef +ProgramStateRef CheckerManager::runCheckersForRegionChanges(ProgramStateRef state, const InvalidatedSymbols *invalidated, ArrayRef<const MemRegion *> ExplicitRegions, @@ -478,7 +505,7 @@ CheckerManager::runCheckersForRegionChanges(ProgramStateRef state, // bail out. if (!state) return nullptr; - state = RegionChangesCheckers[i].CheckFn(state, invalidated, + state = RegionChangesCheckers[i].CheckFn(state, invalidated, ExplicitRegions, Regions, Call); } return state; @@ -506,7 +533,7 @@ CheckerManager::runCheckersForPointerEscape(ProgramStateRef State, } /// \brief Run checkers for handling assumptions on symbolic values. -ProgramStateRef +ProgramStateRef CheckerManager::runCheckersForEvalAssume(ProgramStateRef state, SVal Cond, bool Assumption) { for (unsigned i = 0, e = EvalAssumeCheckers.size(); i != e; ++i) { @@ -558,7 +585,7 @@ void CheckerManager::runCheckersForEvalCall(ExplodedNodeSet &Dst, #endif } } - + // If none of the checkers evaluated the call, ask ExprEngine to handle it. if (!anyEvaluated) { NodeBuilder B(Pred, Dst, Eng.getBuilderContext()); @@ -616,6 +643,11 @@ void CheckerManager::_registerForPostStmt(CheckStmtFunc checkfn, void CheckerManager::_registerForPreObjCMessage(CheckObjCMessageFunc checkfn) { PreObjCMessageCheckers.push_back(checkfn); } + +void CheckerManager::_registerForObjCMessageNil(CheckObjCMessageFunc checkfn) { + ObjCMessageNilCheckers.push_back(checkfn); +} + void CheckerManager::_registerForPostObjCMessage(CheckObjCMessageFunc checkfn) { PostObjCMessageCheckers.push_back(checkfn); } diff --git a/lib/StaticAnalyzer/Core/CheckerRegistry.cpp b/lib/StaticAnalyzer/Core/CheckerRegistry.cpp index 6ba64f52d1837..a15e1573e228b 100644 --- a/lib/StaticAnalyzer/Core/CheckerRegistry.cpp +++ b/lib/StaticAnalyzer/Core/CheckerRegistry.cpp @@ -94,7 +94,7 @@ void CheckerRegistry::addChecker(InitializationFunction fn, StringRef name, } } -void CheckerRegistry::initializeManager(CheckerManager &checkerMgr, +void CheckerRegistry::initializeManager(CheckerManager &checkerMgr, SmallVectorImpl<CheckerOptInfo> &opts) const { // Sort checkers for efficient collection. std::sort(Checkers.begin(), Checkers.end(), checkerNameLT); diff --git a/lib/StaticAnalyzer/Core/ConstraintManager.cpp b/lib/StaticAnalyzer/Core/ConstraintManager.cpp index 4dec526005183..b7db8333aaac6 100644 --- a/lib/StaticAnalyzer/Core/ConstraintManager.cpp +++ b/lib/StaticAnalyzer/Core/ConstraintManager.cpp @@ -26,7 +26,7 @@ static DefinedSVal getLocFromSymbol(const ProgramStateRef &State, } ConditionTruthVal ConstraintManager::checkNull(ProgramStateRef State, - SymbolRef Sym) { + SymbolRef Sym) { QualType Ty = Sym->getType(); DefinedSVal V = Loc::isLocType(Ty) ? getLocFromSymbol(State, Sym) : nonloc::SymbolVal(Sym); diff --git a/lib/StaticAnalyzer/Core/CoreEngine.cpp b/lib/StaticAnalyzer/Core/CoreEngine.cpp index 7844ad4a9c043..39cf7e771755d 100644 --- a/lib/StaticAnalyzer/Core/CoreEngine.cpp +++ b/lib/StaticAnalyzer/Core/CoreEngine.cpp @@ -271,7 +271,7 @@ void CoreEngine::dispatchWorkItem(ExplodedNode* Pred, ProgramPoint Loc, bool CoreEngine::ExecuteWorkListWithInitialState(const LocationContext *L, unsigned Steps, - ProgramStateRef InitState, + ProgramStateRef InitState, ExplodedNodeSet &Dst) { bool DidNotFinish = ExecuteWorkList(L, Steps, InitState); for (ExplodedGraph::eop_iterator I = G.eop_begin(), E = G.eop_end(); I != E; @@ -386,7 +386,7 @@ void CoreEngine::HandleBlockExit(const CFGBlock * B, ExplodedNode *Pred) { } return; } - + case Stmt::DoStmtClass: HandleBranch(cast<DoStmt>(Term)->getCond(), Term, B, Pred); return; @@ -456,7 +456,7 @@ void CoreEngine::HandleBlockExit(const CFGBlock * B, ExplodedNode *Pred) { Pred->State, Pred); } -void CoreEngine::HandleBranch(const Stmt *Cond, const Stmt *Term, +void CoreEngine::HandleBranch(const Stmt *Cond, const Stmt *Term, const CFGBlock * B, ExplodedNode *Pred) { assert(B->succ_size() == 2); NodeBuilderContext Ctx(*this, B, Pred); @@ -491,7 +491,7 @@ void CoreEngine::HandleStaticInit(const DeclStmt *DS, const CFGBlock *B, } -void CoreEngine::HandlePostStmt(const CFGBlock *B, unsigned StmtIdx, +void CoreEngine::HandlePostStmt(const CFGBlock *B, unsigned StmtIdx, ExplodedNode *Pred) { assert(B); assert(!B->empty()); diff --git a/lib/StaticAnalyzer/Core/DynamicTypeMap.cpp b/lib/StaticAnalyzer/Core/DynamicTypeMap.cpp new file mode 100644 index 0000000000000..fd35b664a9127 --- /dev/null +++ b/lib/StaticAnalyzer/Core/DynamicTypeMap.cpp @@ -0,0 +1,51 @@ +//==- DynamicTypeMap.cpp - Dynamic Type Info related APIs ----------*- C++ -*-// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines APIs that track and query dynamic type information. This +// information can be used to devirtualize calls during the symbolic exection +// or do type checking. +// +//===----------------------------------------------------------------------===// + +#include "clang/StaticAnalyzer/Core/PathSensitive/DynamicTypeMap.h" + +namespace clang { +namespace ento { + +DynamicTypeInfo getDynamicTypeInfo(ProgramStateRef State, + const MemRegion *Reg) { + Reg = Reg->StripCasts(); + + // Look up the dynamic type in the GDM. + const DynamicTypeInfo *GDMType = State->get<DynamicTypeMap>(Reg); + if (GDMType) + return *GDMType; + + // Otherwise, fall back to what we know about the region. + if (const TypedRegion *TR = dyn_cast<TypedRegion>(Reg)) + return DynamicTypeInfo(TR->getLocationType(), /*CanBeSubclass=*/false); + + if (const SymbolicRegion *SR = dyn_cast<SymbolicRegion>(Reg)) { + SymbolRef Sym = SR->getSymbol(); + return DynamicTypeInfo(Sym->getType()); + } + + return DynamicTypeInfo(); +} + +ProgramStateRef setDynamicTypeInfo(ProgramStateRef State, const MemRegion *Reg, + DynamicTypeInfo NewTy) { + Reg = Reg->StripCasts(); + ProgramStateRef NewState = State->set<DynamicTypeMap>(Reg, NewTy); + assert(NewState); + return NewState; +} + +} // namespace ento +} // namespace clang diff --git a/lib/StaticAnalyzer/Core/Environment.cpp b/lib/StaticAnalyzer/Core/Environment.cpp index ae5a4cc8b4aad..e2cb52cb417e2 100644 --- a/lib/StaticAnalyzer/Core/Environment.cpp +++ b/lib/StaticAnalyzer/Core/Environment.cpp @@ -90,6 +90,7 @@ SVal Environment::getSVal(const EnvironmentEntry &Entry, case Stmt::CXXNullPtrLiteralExprClass: case Stmt::ObjCStringLiteralClass: case Stmt::StringLiteralClass: + case Stmt::TypeTraitExprClass: // Known constants; defer to SValBuilder. return svalBuilder.getConstantVal(cast<Expr>(S)).getValue(); @@ -97,9 +98,9 @@ SVal Environment::getSVal(const EnvironmentEntry &Entry, const ReturnStmt *RS = cast<ReturnStmt>(S); if (const Expr *RE = RS->getRetValue()) return getSVal(EnvironmentEntry(RE, LCtx), svalBuilder); - return UndefinedVal(); + return UndefinedVal(); } - + // Handle all other Stmt* using a lookup. default: return lookupExpr(EnvironmentEntry(S, LCtx)); @@ -120,7 +121,7 @@ Environment EnvironmentManager::bindExpr(Environment Env, } namespace { -class MarkLiveCallback : public SymbolVisitor { +class MarkLiveCallback final : public SymbolVisitor { SymbolReaper &SymReaper; public: MarkLiveCallback(SymbolReaper &symreaper) : SymReaper(symreaper) {} @@ -170,10 +171,6 @@ EnvironmentManager::removeDeadBindings(Environment Env, // Copy the binding to the new map. EBMapRef = EBMapRef.add(BlkExpr, X); - // If the block expr's value is a memory region, then mark that region. - if (Optional<loc::MemRegionVal> R = X.getAs<loc::MemRegionVal>()) - SymReaper.markLive(R->getRegion()); - // Mark all symbols in the block expr's value live. RSScaner.scan(X); continue; @@ -194,16 +191,16 @@ void Environment::print(raw_ostream &Out, const char *NL, for (Environment::iterator I = begin(), E = end(); I != E; ++I) { const EnvironmentEntry &En = I.getKey(); - + if (isFirst) { Out << NL << NL << "Expressions:" - << NL; + << NL; isFirst = false; } else { Out << NL; } - + const Stmt *S = En.getStmt(); assert(S != nullptr && "Expected non-null Stmt"); diff --git a/lib/StaticAnalyzer/Core/ExplodedGraph.cpp b/lib/StaticAnalyzer/Core/ExplodedGraph.cpp index 010d26e48e1df..8a09720b2a196 100644 --- a/lib/StaticAnalyzer/Core/ExplodedGraph.cpp +++ b/lib/StaticAnalyzer/Core/ExplodedGraph.cpp @@ -90,8 +90,8 @@ bool ExplodedGraph::shouldCollect(const ExplodedNode *node) { // (7) The LocationContext is the same as the predecessor. // (8) Expressions that are *not* lvalue expressions. // (9) The PostStmt isn't for a non-consumed Stmt or Expr. - // (10) The successor is neither a CallExpr StmtPoint nor a CallEnter or - // PreImplicitCall (so that we would be able to find it when retrying a + // (10) The successor is neither a CallExpr StmtPoint nor a CallEnter or + // PreImplicitCall (so that we would be able to find it when retrying a // call with no inlining). // FIXME: It may be safe to reclaim PreCall and PostCall nodes as well. @@ -102,7 +102,7 @@ bool ExplodedGraph::shouldCollect(const ExplodedNode *node) { const ExplodedNode *pred = *(node->pred_begin()); if (pred->succ_size() != 1) return false; - + const ExplodedNode *succ = *(node->succ_begin()); if (succ->pred_size() != 1) return false; @@ -123,7 +123,7 @@ bool ExplodedGraph::shouldCollect(const ExplodedNode *node) { // Conditions 5, 6, and 7. ProgramStateRef state = node->getState(); - ProgramStateRef pred_state = pred->getState(); + ProgramStateRef pred_state = pred->getState(); if (state->store != pred_state->store || state->GDM != pred_state->GDM || progPoint.getLocationContext() != pred->getLocationContext()) return false; @@ -174,7 +174,7 @@ void ExplodedGraph::collectNode(ExplodedNode *node) { FreeNodes.push_back(node); Nodes.RemoveNode(node); --NumNodes; - node->~ExplodedNode(); + node->~ExplodedNode(); } void ExplodedGraph::reclaimRecentlyAllocatedNodes() { diff --git a/lib/StaticAnalyzer/Core/ExprEngine.cpp b/lib/StaticAnalyzer/Core/ExprEngine.cpp index a3239f591a38b..662b0a2dd7989 100644 --- a/lib/StaticAnalyzer/Core/ExprEngine.cpp +++ b/lib/StaticAnalyzer/Core/ExprEngine.cpp @@ -26,6 +26,7 @@ #include "clang/StaticAnalyzer/Core/CheckerManager.h" #include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/LoopWidening.h" #include "llvm/ADT/ImmutableList.h" #include "llvm/ADT/Statistic.h" #include "llvm/Support/raw_ostream.h" @@ -174,7 +175,7 @@ ProgramStateRef ExprEngine::getInitialState(const LocationContext *InitLoc) { } } } - + return state; } @@ -265,7 +266,7 @@ bool ExprEngine::wantsRegionChangeUpdate(ProgramStateRef state) { return getCheckerManager().wantsRegionChangeUpdate(state); } -ProgramStateRef +ProgramStateRef ExprEngine::processRegionChanges(ProgramStateRef state, const InvalidatedSymbols *invalidated, ArrayRef<const MemRegion *> Explicits, @@ -315,7 +316,7 @@ static bool shouldRemoveDeadBindings(AnalysisManager &AMgr, const CFGStmt S, const ExplodedNode *Pred, const LocationContext *LC) { - + // Are we never purging state values? if (AMgr.options.AnalysisPurgeOpt == PurgeNone) return false; @@ -327,7 +328,7 @@ static bool shouldRemoveDeadBindings(AnalysisManager &AMgr, // Is this on a non-expression? if (!isa<Expr>(S.getStmt())) return true; - + // Run before processing a call. if (CallEvent::isCallStmt(S.getStmt())) return true; @@ -475,8 +476,12 @@ void ExprEngine::ProcessInitializer(const CFGInitializer Init, if (BMI->isAnyMemberInitializer()) { // Constructors build the object directly in the field, // but non-objects must be copied in from the initializer. - const Expr *Init = BMI->getInit()->IgnoreImplicit(); - if (!isa<CXXConstructExpr>(Init)) { + if (auto *CtorExpr = findDirectConstructorForCurrentCFGElement()) { + assert(BMI->getInit()->IgnoreImplicit() == CtorExpr); + (void)CtorExpr; + // The field was directly constructed, so there is no need to bind. + } else { + const Expr *Init = BMI->getInit()->IgnoreImplicit(); const ValueDecl *Field; if (BMI->isIndirectMemberInitializer()) { Field = BMI->getIndirectMember(); @@ -512,7 +517,7 @@ void ExprEngine::ProcessInitializer(const CFGInitializer Init, assert(Tmp.size() == 1 && "have not generated any new nodes yet"); assert(*Tmp.begin() == Pred && "have not generated any new nodes yet"); Tmp.clear(); - + PostInitializer PP(BMI, FieldLoc.getAsRegion(), stackFrame); evalBind(Tmp, Init, Pred, FieldLoc, InitVal, /*isInit=*/true, &PP); } @@ -754,9 +759,9 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, case Stmt::CXXUuidofExprClass: case Stmt::CXXFoldExprClass: case Stmt::MSPropertyRefExprClass: + case Stmt::MSPropertySubscriptExprClass: case Stmt::CXXUnresolvedConstructExprClass: case Stmt::DependentScopeDeclRefExprClass: - case Stmt::TypeTraitExprClass: case Stmt::ArrayTypeTraitExprClass: case Stmt::ExpressionTraitExprClass: case Stmt::UnresolvedLookupExprClass: @@ -766,16 +771,19 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, case Stmt::PackExpansionExprClass: case Stmt::SubstNonTypeTemplateParmPackExprClass: case Stmt::FunctionParmPackExprClass: + case Stmt::CoroutineBodyStmtClass: + case Stmt::CoawaitExprClass: + case Stmt::CoreturnStmtClass: + case Stmt::CoyieldExprClass: case Stmt::SEHTryStmtClass: case Stmt::SEHExceptStmtClass: case Stmt::SEHLeaveStmtClass: - case Stmt::LambdaExprClass: case Stmt::SEHFinallyStmtClass: { const ExplodedNode *node = Bldr.generateSink(S, Pred, Pred->getState()); Engine.addAbortedBlock(node, currBldrCtx->getBlock()); break; } - + case Stmt::ParenExprClass: llvm_unreachable("ParenExprs already handled."); case Stmt::GenericSelectionExprClass: @@ -821,9 +829,13 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, case Stmt::OMPOrderedDirectiveClass: case Stmt::OMPAtomicDirectiveClass: case Stmt::OMPTargetDirectiveClass: + case Stmt::OMPTargetDataDirectiveClass: case Stmt::OMPTeamsDirectiveClass: case Stmt::OMPCancellationPointDirectiveClass: case Stmt::OMPCancelDirectiveClass: + case Stmt::OMPTaskLoopDirectiveClass: + case Stmt::OMPTaskLoopSimdDirectiveClass: + case Stmt::OMPDistributeDirectiveClass: llvm_unreachable("Stmt should not be in analyzer evaluation loop"); case Stmt::ObjCSubscriptRefExprClass: @@ -901,7 +913,9 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, case Stmt::ObjCStringLiteralClass: case Stmt::CXXPseudoDestructorExprClass: case Stmt::SubstNonTypeTemplateParmExprClass: - case Stmt::CXXNullPtrLiteralExprClass: { + case Stmt::CXXNullPtrLiteralExprClass: + case Stmt::OMPArraySectionExprClass: + case Stmt::TypeTraitExprClass: { Bldr.takeNodes(Pred); ExplodedNodeSet preVisit; getCheckerManager().runCheckersForPreStmt(preVisit, Pred, S, *this); @@ -964,7 +978,7 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, ExplodedNodeSet preVisit; getCheckerManager().runCheckersForPreStmt(preVisit, Pred, S, *this); - + ExplodedNodeSet Tmp; StmtNodeBuilder Bldr2(preVisit, Tmp, *currBldrCtx); @@ -972,7 +986,7 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, QualType resultType = Ex->getType(); for (ExplodedNodeSet::iterator it = preVisit.begin(), et = preVisit.end(); - it != et; ++it) { + it != et; ++it) { ExplodedNode *N = *it; const LocationContext *LCtx = N->getLocationContext(); SVal result = svalBuilder.conjureSymbolVal(nullptr, Ex, LCtx, @@ -981,10 +995,10 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, ProgramStateRef state = N->getState()->BindExpr(Ex, LCtx, result); Bldr2.generateNode(S, N, state); } - + getCheckerManager().runCheckersForPostStmt(Dst, Tmp, S, *this); Bldr.addNodes(Dst); - break; + break; } case Stmt::ArraySubscriptExprClass: @@ -1011,6 +1025,17 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, Bldr.addNodes(Dst); break; + case Stmt::LambdaExprClass: + if (AMgr.options.shouldInlineLambdas()) { + Bldr.takeNodes(Pred); + VisitLambdaExpr(cast<LambdaExpr>(S), Pred, Dst); + Bldr.addNodes(Dst); + } else { + const ExplodedNode *node = Bldr.generateSink(S, Pred, Pred->getState()); + Engine.addAbortedBlock(node, currBldrCtx->getBlock()); + } + break; + case Stmt::BinaryOperatorClass: { const BinaryOperator* B = cast<BinaryOperator>(S); if (B->isLogicalOp()) { @@ -1029,7 +1054,7 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, } Bldr.takeNodes(Pred); - + if (AMgr.options.eagerlyAssumeBinOpBifurcation && (B->isRelationalOp() || B->isEqualityOp())) { ExplodedNodeSet Tmp; @@ -1074,7 +1099,7 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, Bldr.addNodes(Dst); break; } - + case Stmt::CXXCatchStmtClass: { Bldr.takeNodes(Pred); VisitCXXCatchStmt(cast<CXXCatchStmt>(S), Pred, Dst); @@ -1083,7 +1108,7 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, } case Stmt::CXXTemporaryObjectExprClass: - case Stmt::CXXConstructExprClass: { + case Stmt::CXXConstructExprClass: { Bldr.takeNodes(Pred); VisitCXXConstructExpr(cast<CXXConstructExpr>(S), Pred, Dst); Bldr.addNodes(Dst); @@ -1105,7 +1130,7 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, const CXXDeleteExpr *CDE = cast<CXXDeleteExpr>(S); getCheckerManager().runCheckersForPreStmt(PreVisit, Pred, S, *this); - for (ExplodedNodeSet::iterator i = PreVisit.begin(), + for (ExplodedNodeSet::iterator i = PreVisit.begin(), e = PreVisit.end(); i != e ; ++i) VisitCXXDeleteExpr(CDE, *i, Dst); @@ -1171,18 +1196,18 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, case Stmt::CXXDynamicCastExprClass: case Stmt::CXXReinterpretCastExprClass: case Stmt::CXXConstCastExprClass: - case Stmt::CXXFunctionalCastExprClass: + case Stmt::CXXFunctionalCastExprClass: case Stmt::ObjCBridgedCastExprClass: { Bldr.takeNodes(Pred); const CastExpr *C = cast<CastExpr>(S); // Handle the previsit checks. ExplodedNodeSet dstPrevisit; getCheckerManager().runCheckersForPreStmt(dstPrevisit, Pred, C, *this); - + // Handle the expression itself. ExplodedNodeSet dstExpr; for (ExplodedNodeSet::iterator i = dstPrevisit.begin(), - e = dstPrevisit.end(); i != e ; ++i) { + e = dstPrevisit.end(); i != e ; ++i) { VisitCast(C, C->getSubExpr(), *i, dstExpr); } @@ -1199,7 +1224,7 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, Bldr.addNodes(Dst); break; } - + case Stmt::InitListExprClass: Bldr.takeNodes(Pred); VisitInitListExpr(cast<InitListExpr>(S), Pred, Dst); @@ -1294,7 +1319,7 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, Bldr.takeNodes(Pred); ProgramStateRef state = Pred->getState(); const PseudoObjectExpr *PE = cast<PseudoObjectExpr>(S); - if (const Expr *Result = PE->getResultExpr()) { + if (const Expr *Result = PE->getResultExpr()) { SVal V = state->getSVal(Result, Pred->getLocationContext()); Bldr.generateNode(S, Pred, state->BindExpr(S, Pred->getLocationContext(), V)); @@ -1375,12 +1400,29 @@ bool ExprEngine::replayWithoutInlining(ExplodedNode *N, /// Block entrance. (Update counters). void ExprEngine::processCFGBlockEntrance(const BlockEdge &L, - NodeBuilderWithSinks &nodeBuilder, + NodeBuilderWithSinks &nodeBuilder, ExplodedNode *Pred) { PrettyStackTraceLocationContext CrashInfo(Pred->getLocationContext()); + // If this block is terminated by a loop and it has already been visited the + // maximum number of times, widen the loop. + unsigned int BlockCount = nodeBuilder.getContext().blockCount(); + if (BlockCount == AMgr.options.maxBlockVisitOnPath - 1 && + AMgr.options.shouldWidenLoops()) { + const Stmt *Term = nodeBuilder.getContext().getBlock()->getTerminator(); + if (!(Term && + (isa<ForStmt>(Term) || isa<WhileStmt>(Term) || isa<DoStmt>(Term)))) + return; + // Widen. + const LocationContext *LCtx = Pred->getLocationContext(); + ProgramStateRef WidenedState = + getWidenedLoopState(Pred->getState(), LCtx, BlockCount, Term); + nodeBuilder.generateNode(WidenedState, Pred); + return; + } + // FIXME: Refactor this into a checker. - if (nodeBuilder.getContext().blockCount() >= AMgr.options.maxBlockVisitOnPath) { + if (BlockCount >= AMgr.options.maxBlockVisitOnPath) { static SimpleProgramPointTag tag(TagProviderName, "Block count exceeded"); const ExplodedNode *Sink = nodeBuilder.generateSink(Pred->getState(), Pred, &tag); @@ -1537,7 +1579,6 @@ void ExprEngine::processBranch(const Stmt *Condition, const Stmt *Term, return; } - if (const Expr *Ex = dyn_cast<Expr>(Condition)) Condition = Ex->IgnoreParens(); @@ -1583,7 +1624,7 @@ void ExprEngine::processBranch(const Stmt *Condition, const Stmt *Term, } } } - + // If the condition is still unknown, give up. if (X.isUnknownOrUndef()) { builder.generateNode(PrevState, true, PredI); @@ -1750,7 +1791,7 @@ void ExprEngine::processSwitch(SwitchNodeBuilder& builder) { DefinedOrUnknownSVal CondV = CondV_untested.castAs<DefinedOrUnknownSVal>(); ProgramStateRef DefaultSt = state; - + iterator I = builder.begin(), EI = builder.end(); bool defaultIsFeasible = I == EI; @@ -1758,7 +1799,7 @@ void ExprEngine::processSwitch(SwitchNodeBuilder& builder) { // Successor may be pruned out during CFG construction. if (!I.getBlock()) continue; - + const CaseStmt *Case = I.getCase(); // Evaluate the LHS of the case value. @@ -1772,47 +1813,24 @@ void ExprEngine::processSwitch(SwitchNodeBuilder& builder) { else V2 = V1; - // FIXME: Eventually we should replace the logic below with a range - // comparison, rather than concretize the values within the range. - // This should be easy once we have "ranges" for NonLVals. - - do { - nonloc::ConcreteInt CaseVal(getBasicVals().getValue(V1)); - DefinedOrUnknownSVal Res = svalBuilder.evalEQ(DefaultSt ? DefaultSt : state, - CondV, CaseVal); - - // Now "assume" that the case matches. - if (ProgramStateRef stateNew = state->assume(Res, true)) { - builder.generateCaseStmtNode(I, stateNew); - - // If CondV evaluates to a constant, then we know that this - // is the *only* case that we can take, so stop evaluating the - // others. - if (CondV.getAs<nonloc::ConcreteInt>()) - return; - } - - // Now "assume" that the case doesn't match. Add this state - // to the default state (if it is feasible). - if (DefaultSt) { - if (ProgramStateRef stateNew = DefaultSt->assume(Res, false)) { - defaultIsFeasible = true; - DefaultSt = stateNew; - } - else { - defaultIsFeasible = false; - DefaultSt = nullptr; - } - } - - // Concretize the next value in the range. - if (V1 == V2) - break; - - ++V1; - assert (V1 <= V2); - - } while (true); + ProgramStateRef StateCase; + if (Optional<NonLoc> NL = CondV.getAs<NonLoc>()) + std::tie(StateCase, DefaultSt) = + DefaultSt->assumeWithinInclusiveRange(*NL, V1, V2); + else // UnknownVal + StateCase = DefaultSt; + + if (StateCase) + builder.generateCaseStmtNode(I, StateCase); + + // Now "assume" that the case doesn't match. Add this state + // to the default state (if it is feasible). + if (DefaultSt) + defaultIsFeasible = true; + else { + defaultIsFeasible = false; + break; + } } if (!defaultIsFeasible) @@ -1849,13 +1867,44 @@ void ExprEngine::VisitCommonDeclRefExpr(const Expr *Ex, const NamedDecl *D, if (const VarDecl *VD = dyn_cast<VarDecl>(D)) { // C permits "extern void v", and if you cast the address to a valid type, - // you can even do things with it. We simply pretend + // you can even do things with it. We simply pretend assert(Ex->isGLValue() || VD->getType()->isVoidType()); - SVal V = state->getLValue(VD, Pred->getLocationContext()); + const LocationContext *LocCtxt = Pred->getLocationContext(); + const Decl *D = LocCtxt->getDecl(); + const auto *MD = D ? dyn_cast<CXXMethodDecl>(D) : nullptr; + const auto *DeclRefEx = dyn_cast<DeclRefExpr>(Ex); + SVal V; + bool IsReference; + if (AMgr.options.shouldInlineLambdas() && DeclRefEx && + DeclRefEx->refersToEnclosingVariableOrCapture() && MD && + MD->getParent()->isLambda()) { + // Lookup the field of the lambda. + const CXXRecordDecl *CXXRec = MD->getParent(); + llvm::DenseMap<const VarDecl *, FieldDecl *> LambdaCaptureFields; + FieldDecl *LambdaThisCaptureField; + CXXRec->getCaptureFields(LambdaCaptureFields, LambdaThisCaptureField); + const FieldDecl *FD = LambdaCaptureFields[VD]; + if (!FD) { + // When a constant is captured, sometimes no corresponding field is + // created in the lambda object. + assert(VD->getType().isConstQualified()); + V = state->getLValue(VD, LocCtxt); + IsReference = false; + } else { + Loc CXXThis = + svalBuilder.getCXXThis(MD, LocCtxt->getCurrentStackFrame()); + SVal CXXThisVal = state->getSVal(CXXThis); + V = state->getLValue(FD, CXXThisVal); + IsReference = FD->getType()->isReferenceType(); + } + } else { + V = state->getLValue(VD, LocCtxt); + IsReference = VD->getType()->isReferenceType(); + } // For references, the 'lvalue' is the pointer address stored in the // reference region. - if (VD->getType()->isReferenceType()) { + if (IsReference) { if (const MemRegion *R = V.getAsRegion()) V = state->getSVal(R); else @@ -1900,7 +1949,6 @@ void ExprEngine::VisitLvalArraySubscriptExpr(const ArraySubscriptExpr *A, const Expr *Base = A->getBase()->IgnoreParens(); const Expr *Idx = A->getIdx()->IgnoreParens(); - ExplodedNodeSet checkerPreStmt; getCheckerManager().runCheckersForPreStmt(checkerPreStmt, Pred, A, *this); @@ -2005,8 +2053,9 @@ void ExprEngine::VisitMemberExpr(const MemberExpr *M, ExplodedNode *Pred, } namespace { -class CollectReachableSymbolsCallback : public SymbolVisitor { +class CollectReachableSymbolsCallback final : public SymbolVisitor { InvalidatedSymbols Symbols; + public: CollectReachableSymbolsCallback(ProgramStateRef State) {} const InvalidatedSymbols &getSymbols() const { return Symbols; } @@ -2064,14 +2113,14 @@ ProgramStateRef ExprEngine::processPointerEscapedOnBind(ProgramStateRef State, return State; } -ProgramStateRef +ProgramStateRef ExprEngine::notifyCheckersOfPointerEscape(ProgramStateRef State, const InvalidatedSymbols *Invalidated, ArrayRef<const MemRegion *> ExplicitRegions, ArrayRef<const MemRegion *> Regions, const CallEvent *Call, RegionAndSymbolInvalidationTraits &ITraits) { - + if (!Invalidated || Invalidated->empty()) return State; @@ -2082,7 +2131,7 @@ ExprEngine::notifyCheckersOfPointerEscape(ProgramStateRef State, PSK_EscapeOther, &ITraits); - // If the symbols were invalidated by a call, we want to find out which ones + // If the symbols were invalidated by a call, we want to find out which ones // were invalidated directly due to being arguments to the call. InvalidatedSymbols SymbolsDirectlyInvalidated; for (ArrayRef<const MemRegion *>::iterator I = ExplicitRegions.begin(), @@ -2129,7 +2178,6 @@ void ExprEngine::evalBind(ExplodedNodeSet &Dst, const Stmt *StoreE, getCheckerManager().runCheckersForBind(CheckedSet, Pred, location, Val, StoreE, *this, *PP); - StmtNodeBuilder Bldr(CheckedSet, Dst, *currBldrCtx); // If the location is not a 'Loc', it will already be handled by @@ -2142,13 +2190,12 @@ void ExprEngine::evalBind(ExplodedNodeSet &Dst, const Stmt *StoreE, Bldr.generateNode(L, state, Pred); return; } - for (ExplodedNodeSet::iterator I = CheckedSet.begin(), E = CheckedSet.end(); I!=E; ++I) { ExplodedNode *PredI = *I; ProgramStateRef state = PredI->getState(); - + state = processPointerEscapedOnBind(state, location, Val); // When binding the value, pass on the hint that this is a initialization. @@ -2301,7 +2348,7 @@ void ExprEngine::evalLocation(ExplodedNodeSet &Dst, // "p = 0" is not noted as "Null pointer value stored to 'p'" but // instead "int *p" is noted as // "Variable 'p' initialized to a null pointer value" - + static SimpleProgramPointTag tag(TagProviderName, "Location"); Bldr.generateNode(NodeEx, Pred, state, &tag); } @@ -2326,7 +2373,7 @@ void ExprEngine::evalEagerlyAssumeBinOpBifurcation(ExplodedNodeSet &Dst, ExplodedNodeSet &Src, const Expr *Ex) { StmtNodeBuilder Bldr(Src, Dst, *currBldrCtx); - + for (ExplodedNodeSet::iterator I=Src.begin(), E=Src.end(); I!=E; ++I) { ExplodedNode *Pred = *I; // Test if the previous node was as the same expression. This can happen @@ -2349,7 +2396,7 @@ void ExprEngine::evalEagerlyAssumeBinOpBifurcation(ExplodedNodeSet &Dst, // First assume that the condition is true. if (StateTrue) { - SVal Val = svalBuilder.makeIntVal(1U, Ex->getType()); + SVal Val = svalBuilder.makeIntVal(1U, Ex->getType()); StateTrue = StateTrue->BindExpr(Ex, Pred->getLocationContext(), Val); Bldr.generateNode(Ex, Pred, StateTrue, tags.first); } @@ -2641,10 +2688,10 @@ struct DOTGraphTraits<ExplodedNode*> : << " NodeID: " << (const void*) N << "\\|"; state->printDOT(Out); - Out << "\\l"; + Out << "\\l"; if (const ProgramPointTag *tag = Loc.getTag()) { - Out << "\\|Tag: " << tag->getTagDescription(); + Out << "\\|Tag: " << tag->getTagDescription(); Out << "\\l"; } return Out.str(); diff --git a/lib/StaticAnalyzer/Core/ExprEngineC.cpp b/lib/StaticAnalyzer/Core/ExprEngineC.cpp index 1777ea97a4029..a5b58710b215a 100644 --- a/lib/StaticAnalyzer/Core/ExprEngineC.cpp +++ b/lib/StaticAnalyzer/Core/ExprEngineC.cpp @@ -25,23 +25,23 @@ void ExprEngine::VisitBinaryOperator(const BinaryOperator* B, Expr *LHS = B->getLHS()->IgnoreParens(); Expr *RHS = B->getRHS()->IgnoreParens(); - + // FIXME: Prechecks eventually go in ::Visit(). ExplodedNodeSet CheckedSet; ExplodedNodeSet Tmp2; getCheckerManager().runCheckersForPreStmt(CheckedSet, Pred, B, *this); - + // With both the LHS and RHS evaluated, process the operation itself. for (ExplodedNodeSet::iterator it=CheckedSet.begin(), ei=CheckedSet.end(); it != ei; ++it) { - + ProgramStateRef state = (*it)->getState(); const LocationContext *LCtx = (*it)->getLocationContext(); SVal LeftV = state->getSVal(LHS, LCtx); SVal RightV = state->getSVal(RHS, LCtx); - + BinaryOperator::Opcode Op = B->getOpcode(); - + if (Op == BO_Assign) { // EXPERIMENTAL: "Conjured" symbols. // FIXME: Handle structs. @@ -57,7 +57,7 @@ void ExprEngine::VisitBinaryOperator(const BinaryOperator* B, LeftV, RightV); continue; } - + if (!B->isAssignmentOp()) { StmtNodeBuilder Bldr(*it, Tmp2, *currBldrCtx); @@ -90,19 +90,19 @@ void ExprEngine::VisitBinaryOperator(const BinaryOperator* B, // Process non-assignments except commas or short-circuited // logical expressions (LAnd and LOr). - SVal Result = evalBinOp(state, Op, LeftV, RightV, B->getType()); + SVal Result = evalBinOp(state, Op, LeftV, RightV, B->getType()); if (Result.isUnknown()) { Bldr.generateNode(B, *it, state); continue; - } + } - state = state->BindExpr(B, LCtx, Result); + state = state->BindExpr(B, LCtx, Result); Bldr.generateNode(B, *it, state); continue; } - + assert (B->isCompoundAssignmentOp()); - + switch (Op) { default: llvm_unreachable("Invalid opcode for compound assignment."); @@ -117,43 +117,43 @@ void ExprEngine::VisitBinaryOperator(const BinaryOperator* B, case BO_XorAssign: Op = BO_Xor; break; case BO_OrAssign: Op = BO_Or; break; } - + // Perform a load (the LHS). This performs the checks for // null dereferences, and so on. ExplodedNodeSet Tmp; SVal location = LeftV; evalLoad(Tmp, B, LHS, *it, state, location); - + for (ExplodedNodeSet::iterator I = Tmp.begin(), E = Tmp.end(); I != E; ++I) { state = (*I)->getState(); const LocationContext *LCtx = (*I)->getLocationContext(); SVal V = state->getSVal(LHS, LCtx); - + // Get the computation type. QualType CTy = cast<CompoundAssignOperator>(B)->getComputationResultType(); CTy = getContext().getCanonicalType(CTy); - + QualType CLHSTy = cast<CompoundAssignOperator>(B)->getComputationLHSType(); CLHSTy = getContext().getCanonicalType(CLHSTy); - + QualType LTy = getContext().getCanonicalType(LHS->getType()); - + // Promote LHS. V = svalBuilder.evalCast(V, CLHSTy, LTy); - + // Compute the result of the operation. SVal Result = svalBuilder.evalCast(evalBinOp(state, Op, V, RightV, CTy), B->getType(), CTy); - + // EXPERIMENTAL: "Conjured" symbols. // FIXME: Handle structs. - + SVal LHSVal; - + if (Result.isUnknown()) { // The symbolic value is actually for the type of the left-hand side // expression, not the computation type, as this is the value the @@ -168,52 +168,74 @@ void ExprEngine::VisitBinaryOperator(const BinaryOperator* B, // computation type. LHSVal = svalBuilder.evalCast(Result, LTy, CTy); } - - // In C++, assignment and compound assignment operators return an + + // In C++, assignment and compound assignment operators return an // lvalue. if (B->isGLValue()) state = state->BindExpr(B, LCtx, location); else state = state->BindExpr(B, LCtx, Result); - + evalStore(Tmp2, B, LHS, *I, state, location, LHSVal); } } - + // FIXME: postvisits eventually go in ::Visit() getCheckerManager().runCheckersForPostStmt(Dst, Tmp2, B, *this); } void ExprEngine::VisitBlockExpr(const BlockExpr *BE, ExplodedNode *Pred, ExplodedNodeSet &Dst) { - + CanQualType T = getContext().getCanonicalType(BE->getType()); + const BlockDecl *BD = BE->getBlockDecl(); // Get the value of the block itself. - SVal V = svalBuilder.getBlockPointer(BE->getBlockDecl(), T, + SVal V = svalBuilder.getBlockPointer(BD, T, Pred->getLocationContext(), currBldrCtx->blockCount()); - + ProgramStateRef State = Pred->getState(); - + // If we created a new MemRegion for the block, we should explicitly bind // the captured variables. if (const BlockDataRegion *BDR = dyn_cast_or_null<BlockDataRegion>(V.getAsRegion())) { - + BlockDataRegion::referenced_vars_iterator I = BDR->referenced_vars_begin(), E = BDR->referenced_vars_end(); - + + auto CI = BD->capture_begin(); + auto CE = BD->capture_end(); for (; I != E; ++I) { - const MemRegion *capturedR = I.getCapturedRegion(); - const MemRegion *originalR = I.getOriginalRegion(); + const VarRegion *capturedR = I.getCapturedRegion(); + const VarRegion *originalR = I.getOriginalRegion(); + + // If the capture had a copy expression, use the result of evaluating + // that expression, otherwise use the original value. + // We rely on the invariant that the block declaration's capture variables + // are a prefix of the BlockDataRegion's referenced vars (which may include + // referenced globals, etc.) to enable fast lookup of the capture for a + // given referenced var. + const Expr *copyExpr = nullptr; + if (CI != CE) { + assert(CI->getVariable() == capturedR->getDecl()); + copyExpr = CI->getCopyExpr(); + CI++; + } + if (capturedR != originalR) { - SVal originalV = State->getSVal(loc::MemRegionVal(originalR)); + SVal originalV; + if (copyExpr) { + originalV = State->getSVal(copyExpr, Pred->getLocationContext()); + } else { + originalV = State->getSVal(loc::MemRegionVal(originalR)); + } State = State->bindLoc(loc::MemRegionVal(capturedR), originalV); } } } - + ExplodedNodeSet Tmp; StmtNodeBuilder Bldr(Pred, Tmp, *currBldrCtx); Bldr.generateNode(BE, Pred, @@ -224,12 +246,12 @@ void ExprEngine::VisitBlockExpr(const BlockExpr *BE, ExplodedNode *Pred, getCheckerManager().runCheckersForPostStmt(Dst, Tmp, BE, *this); } -void ExprEngine::VisitCast(const CastExpr *CastE, const Expr *Ex, +void ExprEngine::VisitCast(const CastExpr *CastE, const Expr *Ex, ExplodedNode *Pred, ExplodedNodeSet &Dst) { - + ExplodedNodeSet dstPreStmt; getCheckerManager().runCheckersForPreStmt(dstPreStmt, Pred, CastE, *this); - + if (CastE->getCastKind() == CK_LValueToRValue) { for (ExplodedNodeSet::iterator I = dstPreStmt.begin(), E = dstPreStmt.end(); I!=E; ++I) { @@ -240,18 +262,18 @@ void ExprEngine::VisitCast(const CastExpr *CastE, const Expr *Ex, } return; } - - // All other casts. + + // All other casts. QualType T = CastE->getType(); QualType ExTy = Ex->getType(); - + if (const ExplicitCastExpr *ExCast=dyn_cast_or_null<ExplicitCastExpr>(CastE)) T = ExCast->getTypeAsWritten(); - + StmtNodeBuilder Bldr(dstPreStmt, Dst, *currBldrCtx); for (ExplodedNodeSet::iterator I = dstPreStmt.begin(), E = dstPreStmt.end(); I != E; ++I) { - + Pred = *I; ProgramStateRef state = Pred->getState(); const LocationContext *LCtx = Pred->getLocationContext(); @@ -316,8 +338,8 @@ void ExprEngine::VisitCast(const CastExpr *CastE, const Expr *Ex, case CK_IntegralComplexToFloatingComplex: case CK_CPointerToObjCPointerCast: case CK_BlockPointerToObjCPointerCast: - case CK_AnyPointerToBlockPointerCast: - case CK_ObjCObjectLValueCast: + case CK_AnyPointerToBlockPointerCast: + case CK_ObjCObjectLValueCast: case CK_ZeroToOCLEvent: case CK_LValueBitCast: { // Delegate to SValBuilder to process. @@ -371,7 +393,7 @@ void ExprEngine::VisitCast(const CastExpr *CastE, const Expr *Ex, svalBuilder.conjureSymbolVal(nullptr, CastE, LCtx, resultType, currBldrCtx->blockCount()); state = state->BindExpr(CastE, LCtx, NewSym); - } else + } else // Else, bind to the derived region value. state = state->BindExpr(CastE, LCtx, val); } @@ -417,7 +439,7 @@ void ExprEngine::VisitCompoundLiteralExpr(const CompoundLiteralExpr *CL, const Expr *Init = CL->getInitializer(); SVal V = State->getSVal(CL->getInitializer(), LCtx); - + if (isa<CXXConstructExpr>(Init)) { // No work needed. Just pass the value up to this expression. } else { @@ -450,11 +472,11 @@ void ExprEngine::VisitDeclStmt(const DeclStmt *DS, ExplodedNode *Pred, Dst.insert(Pred); return; } - + // FIXME: all pre/post visits should eventually be handled by ::Visit(). ExplodedNodeSet dstPreVisit; getCheckerManager().runCheckersForPreStmt(dstPreVisit, Pred, DS, *this); - + ExplodedNodeSet dstEvaluated; StmtNodeBuilder B(dstPreVisit, dstEvaluated, *currBldrCtx); for (ExplodedNodeSet::iterator I = dstPreVisit.begin(), E = dstPreVisit.end(); @@ -470,7 +492,10 @@ void ExprEngine::VisitDeclStmt(const DeclStmt *DS, ExplodedNode *Pred, ExplodedNode *UpdatedN = N; SVal InitVal = state->getSVal(InitEx, LC); - if (isa<CXXConstructExpr>(InitEx->IgnoreImplicit())) { + assert(DS->isSingleDecl()); + if (auto *CtorExpr = findDirectConstructorForCurrentCFGElement()) { + assert(InitEx->IgnoreImplicit() == CtorExpr); + (void)CtorExpr; // We constructed the object directly in the variable. // No need to bind anything. B.generateNode(DS, UpdatedN, state); @@ -485,7 +510,7 @@ void ExprEngine::VisitDeclStmt(const DeclStmt *DS, ExplodedNode *Pred, assert(InitVal.getAs<nonloc::LazyCompoundVal>()); } } - + // Recover some path-sensitivity if a scalar value evaluated to // UnknownVal. if (InitVal.isUnknown()) { @@ -596,7 +621,7 @@ void ExprEngine::VisitInitListExpr(const InitListExpr *IE, (T->isArrayType() || T->isRecordType() || T->isVectorType() || T->isAnyComplexType())) { llvm::ImmutableList<SVal> vals = getBasicVals().getEmptySValList(); - + // Handle base case where the initializer has no elements. // e.g: static int* myArray[] = {}; if (NumInitElements == 0) { @@ -604,13 +629,13 @@ void ExprEngine::VisitInitListExpr(const InitListExpr *IE, B.generateNode(IE, Pred, state->BindExpr(IE, LCtx, V)); return; } - + for (InitListExpr::const_reverse_iterator it = IE->rbegin(), ei = IE->rend(); it != ei; ++it) { SVal V = state->getSVal(cast<Expr>(*it), LCtx); vals = getBasicVals().consVals(V, vals); } - + B.generateNode(IE, Pred, state->BindExpr(IE, LCtx, svalBuilder.makeCompoundVal(T, vals))); @@ -632,7 +657,7 @@ void ExprEngine::VisitInitListExpr(const InitListExpr *IE, } void ExprEngine::VisitGuardedExpr(const Expr *Ex, - const Expr *L, + const Expr *L, const Expr *R, ExplodedNode *Pred, ExplodedNodeSet &Dst) { @@ -663,9 +688,7 @@ void ExprEngine::VisitGuardedExpr(const Expr *Ex, bool hasValue = false; SVal V; - for (CFGBlock::const_reverse_iterator I = SrcBlock->rbegin(), - E = SrcBlock->rend(); I != E; ++I) { - CFGElement CE = *I; + for (CFGElement CE : llvm::reverse(*SrcBlock)) { if (Optional<CFGStmt> CS = CE.getAs<CFGStmt>()) { const Expr *ValEx = cast<Expr>(CS->getStmt()); ValEx = ValEx->IgnoreParens(); @@ -694,7 +717,7 @@ void ExprEngine::VisitGuardedExpr(const Expr *Ex, } void ExprEngine:: -VisitOffsetOfExpr(const OffsetOfExpr *OOE, +VisitOffsetOfExpr(const OffsetOfExpr *OOE, ExplodedNode *Pred, ExplodedNodeSet &Dst) { StmtNodeBuilder B(Pred, Dst, *currBldrCtx); APSInt IV; @@ -730,7 +753,7 @@ VisitUnaryExprOrTypeTraitExpr(const UnaryExprOrTypeTraitExpr *Ex, if (Ex->getKind() == UETT_SizeOf) { if (!T->isIncompleteType() && !T->isConstantSizeType()) { assert(T->isVariableArrayType() && "Unknown non-constant-sized type."); - + // FIXME: Add support for VLA type arguments and VLA expressions. // When that happens, we should probably refactor VLASizeChecker's code. continue; @@ -741,10 +764,10 @@ VisitUnaryExprOrTypeTraitExpr(const UnaryExprOrTypeTraitExpr *Ex, continue; } } - + APSInt Value = Ex->EvaluateKnownConstInt(getContext()); CharUnits amt = CharUnits::fromQuantity(Value.getZExtValue()); - + ProgramStateRef state = (*I)->getState(); state = state->BindExpr(Ex, (*I)->getLocationContext(), svalBuilder.makeIntVal(amt.getQuantity(), @@ -755,7 +778,7 @@ VisitUnaryExprOrTypeTraitExpr(const UnaryExprOrTypeTraitExpr *Ex, getCheckerManager().runCheckersForPostStmt(Dst, EvalSet, Ex, *this); } -void ExprEngine::VisitUnaryOperator(const UnaryOperator* U, +void ExprEngine::VisitUnaryOperator(const UnaryOperator* U, ExplodedNode *Pred, ExplodedNodeSet &Dst) { // FIXME: Prechecks eventually go in ::Visit(). @@ -777,13 +800,13 @@ void ExprEngine::VisitUnaryOperator(const UnaryOperator* U, } case UO_Real: { const Expr *Ex = U->getSubExpr()->IgnoreParens(); - + // FIXME: We don't have complex SValues yet. if (Ex->getType()->isAnyComplexType()) { // Just report "Unknown." break; } - + // For all other types, UO_Real is an identity operation. assert (U->getType() == Ex->getType()); ProgramStateRef state = (*I)->getState(); @@ -792,8 +815,8 @@ void ExprEngine::VisitUnaryOperator(const UnaryOperator* U, state->getSVal(Ex, LCtx))); break; } - - case UO_Imag: { + + case UO_Imag: { const Expr *Ex = U->getSubExpr()->IgnoreParens(); // FIXME: We don't have complex SValues yet. if (Ex->getType()->isAnyComplexType()) { @@ -807,7 +830,7 @@ void ExprEngine::VisitUnaryOperator(const UnaryOperator* U, Bldr.generateNode(U, *I, state->BindExpr(U, LCtx, X)); break; } - + case UO_Plus: assert(!U->isGLValue()); // FALL-THROUGH. @@ -820,7 +843,7 @@ void ExprEngine::VisitUnaryOperator(const UnaryOperator* U, // Unary "+" is a no-op, similar to a parentheses. We still have places // where it may be a block-level expression, so we need to // generate an extra node that just propagates the value of the - // subexpression. + // subexpression. const Expr *Ex = U->getSubExpr()->IgnoreParens(); ProgramStateRef state = (*I)->getState(); const LocationContext *LCtx = (*I)->getLocationContext(); @@ -828,7 +851,7 @@ void ExprEngine::VisitUnaryOperator(const UnaryOperator* U, state->getSVal(Ex, LCtx))); break; } - + case UO_LNot: case UO_Minus: case UO_Not: { @@ -836,15 +859,15 @@ void ExprEngine::VisitUnaryOperator(const UnaryOperator* U, const Expr *Ex = U->getSubExpr()->IgnoreParens(); ProgramStateRef state = (*I)->getState(); const LocationContext *LCtx = (*I)->getLocationContext(); - + // Get the value of the subexpression. SVal V = state->getSVal(Ex, LCtx); - + if (V.isUnknownOrUndef()) { Bldr.generateNode(U, *I, state->BindExpr(U, LCtx, V)); break; } - + switch (U->getOpcode()) { default: llvm_unreachable("Invalid Opcode."); @@ -861,7 +884,7 @@ void ExprEngine::VisitUnaryOperator(const UnaryOperator* U, // // Note: technically we do "E == 0", but this is the same in the // transfer functions as "0 == E". - SVal Result; + SVal Result; if (Optional<Loc> LV = V.getAs<Loc>()) { Loc X = svalBuilder.makeNull(); Result = evalBinOp(state, BO_EQ, *LV, X, U->getType()); @@ -874,8 +897,8 @@ void ExprEngine::VisitUnaryOperator(const UnaryOperator* U, Result = evalBinOp(state, BO_EQ, V.castAs<NonLoc>(), X, U->getType()); } - - state = state->BindExpr(U, LCtx, Result); + + state = state->BindExpr(U, LCtx, Result); break; } Bldr.generateNode(U, *I, state); @@ -893,81 +916,81 @@ void ExprEngine::VisitIncrementDecrementOperator(const UnaryOperator* U, // Handle ++ and -- (both pre- and post-increment). assert (U->isIncrementDecrementOp()); const Expr *Ex = U->getSubExpr()->IgnoreParens(); - + const LocationContext *LCtx = Pred->getLocationContext(); ProgramStateRef state = Pred->getState(); SVal loc = state->getSVal(Ex, LCtx); - + // Perform a load. ExplodedNodeSet Tmp; evalLoad(Tmp, U, Ex, Pred, state, loc); - + ExplodedNodeSet Dst2; StmtNodeBuilder Bldr(Tmp, Dst2, *currBldrCtx); for (ExplodedNodeSet::iterator I=Tmp.begin(), E=Tmp.end();I!=E;++I) { - + state = (*I)->getState(); assert(LCtx == (*I)->getLocationContext()); SVal V2_untested = state->getSVal(Ex, LCtx); - + // Propagate unknown and undefined values. if (V2_untested.isUnknownOrUndef()) { Bldr.generateNode(U, *I, state->BindExpr(U, LCtx, V2_untested)); continue; } DefinedSVal V2 = V2_untested.castAs<DefinedSVal>(); - + // Handle all other values. BinaryOperator::Opcode Op = U->isIncrementOp() ? BO_Add : BO_Sub; - + // If the UnaryOperator has non-location type, use its type to create the // constant value. If the UnaryOperator has location type, create the // constant with int type and pointer width. SVal RHS; - + if (U->getType()->isAnyPointerType()) RHS = svalBuilder.makeArrayIndex(1); else if (U->getType()->isIntegralOrEnumerationType()) RHS = svalBuilder.makeIntVal(1, U->getType()); else RHS = UnknownVal(); - + SVal Result = evalBinOp(state, Op, V2, RHS, U->getType()); - + // Conjure a new symbol if necessary to recover precision. if (Result.isUnknown()){ DefinedOrUnknownSVal SymVal = svalBuilder.conjureSymbolVal(nullptr, Ex, LCtx, currBldrCtx->blockCount()); Result = SymVal; - + // If the value is a location, ++/-- should always preserve // non-nullness. Check if the original value was non-null, and if so // propagate that constraint. if (Loc::isLocType(U->getType())) { DefinedOrUnknownSVal Constraint = svalBuilder.evalEQ(state, V2,svalBuilder.makeZeroVal(U->getType())); - + if (!state->assume(Constraint, true)) { // It isn't feasible for the original value to be null. // Propagate this constraint. Constraint = svalBuilder.evalEQ(state, SymVal, svalBuilder.makeZeroVal(U->getType())); - - + + state = state->assume(Constraint, false); assert(state); } } } - + // Since the lvalue-to-rvalue conversion is explicit in the AST, // we bind an l-value if the operator is prefix and an lvalue (in C++). if (U->isGLValue()) state = state->BindExpr(U, LCtx, loc); else state = state->BindExpr(U, LCtx, U->isPostfix() ? V2 : Result); - + // Perform the store. Bldr.takeNodes(*I); ExplodedNodeSet Dst3; diff --git a/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp b/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp index 2a766218aaeb1..556e2239abfb5 100644 --- a/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp +++ b/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp @@ -103,49 +103,32 @@ static SVal makeZeroElementRegion(ProgramStateRef State, SVal LValue, } -static const MemRegion *getRegionForConstructedObject( - const CXXConstructExpr *CE, ExplodedNode *Pred, ExprEngine &Eng, - unsigned int CurrStmtIdx) { +const MemRegion * +ExprEngine::getRegionForConstructedObject(const CXXConstructExpr *CE, + ExplodedNode *Pred) { const LocationContext *LCtx = Pred->getLocationContext(); ProgramStateRef State = Pred->getState(); - const NodeBuilderContext &CurrBldrCtx = Eng.getBuilderContext(); // See if we're constructing an existing region by looking at the next // element in the CFG. - const CFGBlock *B = CurrBldrCtx.getBlock(); - unsigned int NextStmtIdx = CurrStmtIdx + 1; - if (NextStmtIdx < B->size()) { - CFGElement Next = (*B)[NextStmtIdx]; - - // Is this a destructor? If so, we might be in the middle of an assignment - // to a local or member: look ahead one more element to see what we find. - while (Next.getAs<CFGImplicitDtor>() && NextStmtIdx + 1 < B->size()) { - ++NextStmtIdx; - Next = (*B)[NextStmtIdx]; - } - // Is this a constructor for a local variable? - if (Optional<CFGStmt> StmtElem = Next.getAs<CFGStmt>()) { - if (const DeclStmt *DS = dyn_cast<DeclStmt>(StmtElem->getStmt())) { - if (const VarDecl *Var = dyn_cast<VarDecl>(DS->getSingleDecl())) { - if (Var->getInit() && Var->getInit()->IgnoreImplicit() == CE) { - SVal LValue = State->getLValue(Var, LCtx); - QualType Ty = Var->getType(); - LValue = makeZeroElementRegion(State, LValue, Ty); - return LValue.getAsRegion(); - } + if (auto Elem = findElementDirectlyInitializedByCurrentConstructor()) { + if (Optional<CFGStmt> StmtElem = Elem->getAs<CFGStmt>()) { + auto *DS = cast<DeclStmt>(StmtElem->getStmt()); + if (const auto *Var = dyn_cast<VarDecl>(DS->getSingleDecl())) { + if (Var->getInit() && Var->getInit()->IgnoreImplicit() == CE) { + SVal LValue = State->getLValue(Var, LCtx); + QualType Ty = Var->getType(); + LValue = makeZeroElementRegion(State, LValue, Ty); + return LValue.getAsRegion(); } } - } - - // Is this a constructor for a member? - if (Optional<CFGInitializer> InitElem = Next.getAs<CFGInitializer>()) { + } else if (Optional<CFGInitializer> InitElem = Elem->getAs<CFGInitializer>()) { const CXXCtorInitializer *Init = InitElem->getInitializer(); assert(Init->isAnyMemberInitializer()); - const CXXMethodDecl *CurCtor = cast<CXXMethodDecl>(LCtx->getDecl()); - Loc ThisPtr = Eng.getSValBuilder().getCXXThis(CurCtor, - LCtx->getCurrentStackFrame()); + Loc ThisPtr = + getSValBuilder().getCXXThis(CurCtor, LCtx->getCurrentStackFrame()); SVal ThisVal = State->getSVal(ThisPtr); const ValueDecl *Field; @@ -167,13 +150,86 @@ static const MemRegion *getRegionForConstructedObject( // Don't forget to update the pre-constructor initialization code in // ExprEngine::VisitCXXConstructExpr. } - // If we couldn't find an existing region to construct into, assume we're // constructing a temporary. - MemRegionManager &MRMgr = Eng.getSValBuilder().getRegionManager(); + MemRegionManager &MRMgr = getSValBuilder().getRegionManager(); return MRMgr.getCXXTempObjectRegion(CE, LCtx); } +/// Returns true if the initializer for \Elem can be a direct +/// constructor. +static bool canHaveDirectConstructor(CFGElement Elem){ + // DeclStmts and CXXCtorInitializers for fields can be directly constructed. + + if (Optional<CFGStmt> StmtElem = Elem.getAs<CFGStmt>()) { + if (isa<DeclStmt>(StmtElem->getStmt())) { + return true; + } + } + + if (Elem.getKind() == CFGElement::Initializer) { + return true; + } + + return false; +} + +Optional<CFGElement> +ExprEngine::findElementDirectlyInitializedByCurrentConstructor() { + const NodeBuilderContext &CurrBldrCtx = getBuilderContext(); + // See if we're constructing an existing region by looking at the next + // element in the CFG. + const CFGBlock *B = CurrBldrCtx.getBlock(); + assert(isa<CXXConstructExpr>(((*B)[currStmtIdx]).castAs<CFGStmt>().getStmt())); + unsigned int NextStmtIdx = currStmtIdx + 1; + if (NextStmtIdx >= B->size()) + return None; + + CFGElement Next = (*B)[NextStmtIdx]; + + // Is this a destructor? If so, we might be in the middle of an assignment + // to a local or member: look ahead one more element to see what we find. + while (Next.getAs<CFGImplicitDtor>() && NextStmtIdx + 1 < B->size()) { + ++NextStmtIdx; + Next = (*B)[NextStmtIdx]; + } + + if (canHaveDirectConstructor(Next)) + return Next; + + return None; +} + +const CXXConstructExpr * +ExprEngine::findDirectConstructorForCurrentCFGElement() { + // Go backward in the CFG to see if the previous element (ignoring + // destructors) was a CXXConstructExpr. If so, that constructor + // was constructed directly into an existing region. + // This process is essentially the inverse of that performed in + // findElementDirectlyInitializedByCurrentConstructor(). + if (currStmtIdx == 0) + return nullptr; + + const CFGBlock *B = getBuilderContext().getBlock(); + assert(canHaveDirectConstructor((*B)[currStmtIdx])); + + unsigned int PreviousStmtIdx = currStmtIdx - 1; + CFGElement Previous = (*B)[PreviousStmtIdx]; + + while (Previous.getAs<CFGImplicitDtor>() && PreviousStmtIdx > 0) { + --PreviousStmtIdx; + Previous = (*B)[PreviousStmtIdx]; + } + + if (Optional<CFGStmt> PrevStmtElem = Previous.getAs<CFGStmt>()) { + if (auto *CtorExpr = dyn_cast<CXXConstructExpr>(PrevStmtElem->getStmt())) { + return CtorExpr; + } + } + + return nullptr; +} + void ExprEngine::VisitCXXConstructExpr(const CXXConstructExpr *CE, ExplodedNode *Pred, ExplodedNodeSet &destNodes) { @@ -188,7 +244,7 @@ void ExprEngine::VisitCXXConstructExpr(const CXXConstructExpr *CE, switch (CE->getConstructionKind()) { case CXXConstructExpr::CK_Complete: { - Target = getRegionForConstructedObject(CE, Pred, *this, currStmtIdx); + Target = getRegionForConstructedObject(CE, Pred); break; } case CXXConstructExpr::CK_VirtualBase: @@ -300,7 +356,7 @@ void ExprEngine::VisitCXXDestructor(QualType ObjectType, const MemRegion *Dest, const Stmt *S, bool IsBaseDtor, - ExplodedNode *Pred, + ExplodedNode *Pred, ExplodedNodeSet &Dst) { const LocationContext *LCtx = Pred->getLocationContext(); ProgramStateRef State = Pred->getState(); @@ -373,7 +429,7 @@ void ExprEngine::VisitCXXNewExpr(const CXXNewExpr *CNE, ExplodedNode *Pred, // Also, we need to decide how allocators actually work -- they're not // really part of the CXXNewExpr because they happen BEFORE the // CXXConstructExpr subexpression. See PR12014 for some discussion. - + unsigned blockCount = currBldrCtx->blockCount(); const LocationContext *LCtx = Pred->getLocationContext(); DefinedOrUnknownSVal symVal = UnknownVal(); @@ -392,8 +448,8 @@ void ExprEngine::VisitCXXNewExpr(const CXXNewExpr *CNE, ExplodedNode *Pred, IsStandardGlobalOpNewFunction = (FD->getNumParams() == 1); } - // We assume all standard global 'operator new' functions allocate memory in - // heap. We realize this is an approximation that might not correctly model + // We assume all standard global 'operator new' functions allocate memory in + // heap. We realize this is an approximation that might not correctly model // a custom global allocator. if (IsStandardGlobalOpNewFunction) symVal = svalBuilder.getConjuredHeapSymbolVal(CNE, LCtx, blockCount); @@ -472,7 +528,7 @@ void ExprEngine::VisitCXXNewExpr(const CXXNewExpr *CNE, ExplodedNode *Pred, } } -void ExprEngine::VisitCXXDeleteExpr(const CXXDeleteExpr *CDE, +void ExprEngine::VisitCXXDeleteExpr(const CXXDeleteExpr *CDE, ExplodedNode *Pred, ExplodedNodeSet &Dst) { StmtNodeBuilder Bldr(Pred, Dst, *currBldrCtx); ProgramStateRef state = Pred->getState(); @@ -513,3 +569,55 @@ void ExprEngine::VisitCXXThisExpr(const CXXThisExpr *TE, ExplodedNode *Pred, SVal V = state->getSVal(loc::MemRegionVal(R)); Bldr.generateNode(TE, Pred, state->BindExpr(TE, LCtx, V)); } + +void ExprEngine::VisitLambdaExpr(const LambdaExpr *LE, ExplodedNode *Pred, + ExplodedNodeSet &Dst) { + const LocationContext *LocCtxt = Pred->getLocationContext(); + + // Get the region of the lambda itself. + const MemRegion *R = svalBuilder.getRegionManager().getCXXTempObjectRegion( + LE, LocCtxt); + SVal V = loc::MemRegionVal(R); + + ProgramStateRef State = Pred->getState(); + + // If we created a new MemRegion for the lambda, we should explicitly bind + // the captures. + CXXRecordDecl::field_iterator CurField = LE->getLambdaClass()->field_begin(); + for (LambdaExpr::const_capture_init_iterator i = LE->capture_init_begin(), + e = LE->capture_init_end(); + i != e; ++i, ++CurField) { + FieldDecl *FieldForCapture = *CurField; + SVal FieldLoc = State->getLValue(FieldForCapture, V); + + SVal InitVal; + if (!FieldForCapture->hasCapturedVLAType()) { + Expr *InitExpr = *i; + assert(InitExpr && "Capture missing initialization expression"); + InitVal = State->getSVal(InitExpr, LocCtxt); + } else { + // The field stores the length of a captured variable-length array. + // These captures don't have initialization expressions; instead we + // get the length from the VLAType size expression. + Expr *SizeExpr = FieldForCapture->getCapturedVLAType()->getSizeExpr(); + InitVal = State->getSVal(SizeExpr, LocCtxt); + } + + State = State->bindLoc(FieldLoc, InitVal); + } + + // Decay the Loc into an RValue, because there might be a + // MaterializeTemporaryExpr node above this one which expects the bound value + // to be an RValue. + SVal LambdaRVal = State->getSVal(R); + + ExplodedNodeSet Tmp; + StmtNodeBuilder Bldr(Pred, Tmp, *currBldrCtx); + // FIXME: is this the right program point kind? + Bldr.generateNode(LE, Pred, + State->BindExpr(LE, LocCtxt, LambdaRVal), + nullptr, ProgramPoint::PostLValueKind); + + // FIXME: Move all post/pre visits to ::Visit(). + getCheckerManager().runCheckersForPostStmt(Dst, Tmp, LE, *this); +} diff --git a/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp b/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp index 3f608ba79ebc3..74cc8d2ccbc5b 100644 --- a/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp +++ b/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp @@ -44,19 +44,19 @@ void ExprEngine::processCallEnter(CallEnter CE, ExplodedNode *Pred) { const CFG *CalleeCFG = calleeCtx->getCFG(); const CFGBlock *Entry = &(CalleeCFG->getEntry()); - + // Validate the CFG. assert(Entry->empty()); assert(Entry->succ_size() == 1); - + // Get the solitary successor. const CFGBlock *Succ = *(Entry->succ_begin()); - + // Construct an edge representing the starting location in the callee. BlockEdge Loc(Entry, Succ, calleeCtx); ProgramStateRef state = Pred->getState(); - + // Construct a new node and add it to the worklist. bool isNew; ExplodedNode *Node = G.getNode(Loc, state, false, &isNew); @@ -207,8 +207,8 @@ static bool isTemporaryPRValue(const CXXConstructExpr *E, SVal V) { return isa<CXXTempObjectRegion>(MR); } -/// The call exit is simulated with a sequence of nodes, which occur between -/// CallExitBegin and CallExitEnd. The following operations occur between the +/// The call exit is simulated with a sequence of nodes, which occur between +/// CallExitBegin and CallExitEnd. The following operations occur between the /// two program points: /// 1. CallExitBegin (triggers the start of call exit sequence) /// 2. Bind the return value @@ -220,12 +220,12 @@ void ExprEngine::processCallExit(ExplodedNode *CEBNode) { PrettyStackTraceLocationContext CrashInfo(CEBNode->getLocationContext()); const StackFrameContext *calleeCtx = CEBNode->getLocationContext()->getCurrentStackFrame(); - + // The parent context might not be a stack frame, so make sure we // look up the first enclosing stack frame. const StackFrameContext *callerCtx = calleeCtx->getParent()->getCurrentStackFrame(); - + const Stmt *CE = calleeCtx->getCallSite(); ProgramStateRef state = CEBNode->getState(); // Find the last statement in the function and the corresponding basic block. @@ -421,7 +421,8 @@ bool ExprEngine::inlineCall(const CallEvent &Call, const Decl *D, const LocationContext *CurLC = Pred->getLocationContext(); const StackFrameContext *CallerSFC = CurLC->getCurrentStackFrame(); const LocationContext *ParentOfCallee = CallerSFC; - if (Call.getKind() == CE_Block) { + if (Call.getKind() == CE_Block && + !cast<BlockCall>(Call).isConversionFromLambda()) { const BlockDataRegion *BR = cast<BlockCall>(Call).getBlockRegion(); assert(BR && "If we have the block definition we should have its region"); AnalysisDeclContext *BlockCtx = AMgr.getAnalysisDeclContext(D); @@ -429,7 +430,7 @@ bool ExprEngine::inlineCall(const CallEvent &Call, const Decl *D, cast<BlockDecl>(D), BR); } - + // This may be NULL, but that's fine. const Expr *CallE = Call.getOriginExpr(); @@ -439,8 +440,8 @@ bool ExprEngine::inlineCall(const CallEvent &Call, const Decl *D, CalleeADC->getStackFrame(ParentOfCallee, CallE, currBldrCtx->getBlock(), currStmtIdx); - - + + CallEnter Loc(CallE, CalleeSFC, CurLC); // Construct a new state which contains the mapping from actual to @@ -690,9 +691,11 @@ static bool hasMember(const ASTContext &Ctx, const CXXRecordDecl *RD, return true; CXXBasePaths Paths(false, false, false); - if (RD->lookupInBases(&CXXRecordDecl::FindOrdinaryMember, - DeclName.getAsOpaquePtr(), - Paths)) + if (RD->lookupInBases( + [DeclName](const CXXBaseSpecifier *Specifier, CXXBasePath &Path) { + return CXXRecordDecl::FindOrdinaryMember(Specifier, Path, DeclName); + }, + Paths)) return true; return false; @@ -767,7 +770,7 @@ static bool mayInlineDecl(AnalysisDeclContext *CalleeADC, if (!Ctx.getSourceManager().isInMainFile(FD->getLocation())) if (isContainerMethod(Ctx, FD)) return false; - + // Conditionally control the inlining of the destructor of C++ shared_ptr. // We don't currently do a good job modeling shared_ptr because we can't // see the reference count, so treating as opaque is probably the best @@ -868,7 +871,8 @@ bool ExprEngine::shouldInlineCall(const CallEvent &Call, const Decl *D, // Do not inline large functions too many times. if ((Engine.FunctionSummaries->getNumTimesInlined(D) > Opts.getMaxTimesInlineLarge()) && - CalleeCFG->getNumBlockIDs() > 13) { + CalleeCFG->getNumBlockIDs() >= + Opts.getMinCFGSizeTreatFunctionsAsLarge()) { NumReachedInlineCountMax++; return false; } @@ -990,12 +994,12 @@ void ExprEngine::BifurcateCall(const MemRegion *BifurReg, void ExprEngine::VisitReturnStmt(const ReturnStmt *RS, ExplodedNode *Pred, ExplodedNodeSet &Dst) { - + ExplodedNodeSet dstPreVisit; getCheckerManager().runCheckersForPreStmt(dstPreVisit, Pred, RS, *this); StmtNodeBuilder B(dstPreVisit, Dst, *currBldrCtx); - + if (RS->getRetValue()) { for (ExplodedNodeSet::iterator it = dstPreVisit.begin(), ei = dstPreVisit.end(); it != ei; ++it) { diff --git a/lib/StaticAnalyzer/Core/ExprEngineObjC.cpp b/lib/StaticAnalyzer/Core/ExprEngineObjC.cpp index a6611e050dc9b..92c5fe6b6f1a3 100644 --- a/lib/StaticAnalyzer/Core/ExprEngineObjC.cpp +++ b/lib/StaticAnalyzer/Core/ExprEngineObjC.cpp @@ -19,18 +19,18 @@ using namespace clang; using namespace ento; -void ExprEngine::VisitLvalObjCIvarRefExpr(const ObjCIvarRefExpr *Ex, +void ExprEngine::VisitLvalObjCIvarRefExpr(const ObjCIvarRefExpr *Ex, ExplodedNode *Pred, ExplodedNodeSet &Dst) { ProgramStateRef state = Pred->getState(); const LocationContext *LCtx = Pred->getLocationContext(); SVal baseVal = state->getSVal(Ex->getBase(), LCtx); SVal location = state->getLValue(Ex->getDecl(), baseVal); - + ExplodedNodeSet dstIvar; StmtNodeBuilder Bldr(Pred, dstIvar, *currBldrCtx); Bldr.generateNode(Ex, Pred, state->BindExpr(Ex, LCtx, location)); - + // Perform the post-condition check of the ObjCIvarRefExpr and store // the created nodes in 'Dst'. getCheckerManager().runCheckersForPostStmt(Dst, dstIvar, Ex, *this); @@ -45,7 +45,7 @@ void ExprEngine::VisitObjCAtSynchronizedStmt(const ObjCAtSynchronizedStmt *S, void ExprEngine::VisitObjCForCollectionStmt(const ObjCForCollectionStmt *S, ExplodedNode *Pred, ExplodedNodeSet &Dst) { - + // ObjCForCollectionStmts are processed in two places. This method // handles the case where an ObjCForCollectionStmt* occurs as one of the // statements within a basic block. This transfer function does two things: @@ -74,7 +74,7 @@ void ExprEngine::VisitObjCForCollectionStmt(const ObjCForCollectionStmt *S, const Stmt *elem = S->getElement(); ProgramStateRef state = Pred->getState(); SVal elementV; - + if (const DeclStmt *DS = dyn_cast<DeclStmt>(elem)) { const VarDecl *elemD = cast<VarDecl>(DS->getSingleDecl()); assert(elemD->getInit() == nullptr); @@ -83,7 +83,7 @@ void ExprEngine::VisitObjCForCollectionStmt(const ObjCForCollectionStmt *S, else { elementV = state->getSVal(elem, Pred->getLocationContext()); } - + ExplodedNodeSet dstLocation; evalLocation(dstLocation, S, elem, Pred, state, elementV, nullptr, false); @@ -95,17 +95,17 @@ void ExprEngine::VisitObjCForCollectionStmt(const ObjCForCollectionStmt *S, Pred = *NI; ProgramStateRef state = Pred->getState(); const LocationContext *LCtx = Pred->getLocationContext(); - + // Handle the case where the container still has elements. SVal TrueV = svalBuilder.makeTruthVal(1); ProgramStateRef hasElems = state->BindExpr(S, LCtx, TrueV); - + // Handle the case where the container has no elements. SVal FalseV = svalBuilder.makeTruthVal(0); ProgramStateRef noElems = state->BindExpr(S, LCtx, FalseV); if (Optional<loc::MemRegionVal> MV = elementV.getAs<loc::MemRegionVal>()) - if (const TypedValueRegion *R = + if (const TypedValueRegion *R = dyn_cast<TypedValueRegion>(MV->getRegion())) { // FIXME: The proper thing to do is to really iterate over the // container. We will do this with dispatch logic to the store. @@ -116,12 +116,12 @@ void ExprEngine::VisitObjCForCollectionStmt(const ObjCForCollectionStmt *S, currBldrCtx->blockCount()); SVal V = svalBuilder.makeLoc(Sym); hasElems = hasElems->bindLoc(elementV, V); - + // Bind the location to 'nil' on the false branch. SVal nilV = svalBuilder.makeIntVal(0, T); noElems = noElems->bindLoc(elementV, nilV); } - + // Create the new nodes. Bldr.generateNode(S, Pred, hasElems); Bldr.generateNode(S, Pred, noElems); @@ -139,6 +139,76 @@ void ExprEngine::VisitObjCMessage(const ObjCMessageExpr *ME, CallEventRef<ObjCMethodCall> Msg = CEMgr.getObjCMethodCall(ME, Pred->getState(), Pred->getLocationContext()); + // There are three cases for the receiver: + // (1) it is definitely nil, + // (2) it is definitely non-nil, and + // (3) we don't know. + // + // If the receiver is definitely nil, we skip the pre/post callbacks and + // instead call the ObjCMessageNil callbacks and return. + // + // If the receiver is definitely non-nil, we call the pre- callbacks, + // evaluate the call, and call the post- callbacks. + // + // If we don't know, we drop the potential nil flow and instead + // continue from the assumed non-nil state as in (2). This approach + // intentionally drops coverage in order to prevent false alarms + // in the following scenario: + // + // id result = [o someMethod] + // if (result) { + // if (!o) { + // // <-- This program point should be unreachable because if o is nil + // // it must the case that result is nil as well. + // } + // } + // + // We could avoid dropping coverage by performing an explicit case split + // on each method call -- but this would get very expensive. An alternative + // would be to introduce lazy constraints. + // FIXME: This ignores many potential bugs (<rdar://problem/11733396>). + // Revisit once we have lazier constraints. + if (Msg->isInstanceMessage()) { + SVal recVal = Msg->getReceiverSVal(); + if (!recVal.isUndef()) { + // Bifurcate the state into nil and non-nil ones. + DefinedOrUnknownSVal receiverVal = + recVal.castAs<DefinedOrUnknownSVal>(); + ProgramStateRef State = Pred->getState(); + + ProgramStateRef notNilState, nilState; + std::tie(notNilState, nilState) = State->assume(receiverVal); + + // Receiver is definitely nil, so run ObjCMessageNil callbacks and return. + if (nilState && !notNilState) { + StmtNodeBuilder Bldr(Pred, Dst, *currBldrCtx); + bool HasTag = Pred->getLocation().getTag(); + Pred = Bldr.generateNode(ME, Pred, nilState, nullptr, + ProgramPoint::PreStmtKind); + assert((Pred || HasTag) && "Should have cached out already!"); + (void)HasTag; + if (!Pred) + return; + getCheckerManager().runCheckersForObjCMessageNil(Dst, Pred, + *Msg, *this); + return; + } + + ExplodedNodeSet dstNonNil; + StmtNodeBuilder Bldr(Pred, dstNonNil, *currBldrCtx); + // Generate a transition to the non-nil state, dropping any potential + // nil flow. + if (notNilState != State) { + bool HasTag = Pred->getLocation().getTag(); + Pred = Bldr.generateNode(ME, Pred, notNilState); + assert((Pred || HasTag) && "Should have cached out already!"); + (void)HasTag; + if (!Pred) + return; + } + } + } + // Handle the previsits checks. ExplodedNodeSet dstPrevisit; getCheckerManager().runCheckersForPreObjCMessage(dstPrevisit, Pred, @@ -156,39 +226,16 @@ void ExprEngine::VisitObjCMessage(const ObjCMessageExpr *ME, ExplodedNode *Pred = *DI; ProgramStateRef State = Pred->getState(); CallEventRef<ObjCMethodCall> UpdatedMsg = Msg.cloneWithState(State); - + if (UpdatedMsg->isInstanceMessage()) { SVal recVal = UpdatedMsg->getReceiverSVal(); if (!recVal.isUndef()) { - // Bifurcate the state into nil and non-nil ones. - DefinedOrUnknownSVal receiverVal = - recVal.castAs<DefinedOrUnknownSVal>(); - - ProgramStateRef notNilState, nilState; - std::tie(notNilState, nilState) = State->assume(receiverVal); - - // There are three cases: can be nil or non-nil, must be nil, must be - // non-nil. We ignore must be nil, and merge the rest two into non-nil. - // FIXME: This ignores many potential bugs (<rdar://problem/11733396>). - // Revisit once we have lazier constraints. - if (nilState && !notNilState) { - continue; - } - - // Check if the "raise" message was sent. - assert(notNilState); if (ObjCNoRet.isImplicitNoReturn(ME)) { // If we raise an exception, for now treat it as a sink. // Eventually we will want to handle exceptions properly. Bldr.generateSink(ME, Pred, State); continue; } - - // Generate a transition to non-Nil state. - if (notNilState != State) { - Pred = Bldr.generateNode(ME, Pred, notNilState); - assert(Pred && "Should have cached out already!"); - } } } else { // Check for special class methods that are known to not return @@ -203,7 +250,7 @@ void ExprEngine::VisitObjCMessage(const ObjCMessageExpr *ME, defaultEvalCall(Bldr, Pred, *UpdatedMsg); } - + ExplodedNodeSet dstPostvisit; getCheckerManager().runCheckersForPostCall(dstPostvisit, dstEval, *Msg, *this); diff --git a/lib/StaticAnalyzer/Core/HTMLDiagnostics.cpp b/lib/StaticAnalyzer/Core/HTMLDiagnostics.cpp index cfcf7c6a990b4..b3edb8569bd6c 100644 --- a/lib/StaticAnalyzer/Core/HTMLDiagnostics.cpp +++ b/lib/StaticAnalyzer/Core/HTMLDiagnostics.cpp @@ -11,7 +11,6 @@ // //===----------------------------------------------------------------------===// -#include "clang/StaticAnalyzer/Core/PathDiagnosticConsumers.h" #include "clang/AST/ASTContext.h" #include "clang/AST/Decl.h" #include "clang/Basic/FileManager.h" @@ -22,6 +21,8 @@ #include "clang/Rewrite/Core/Rewriter.h" #include "clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h" #include "clang/StaticAnalyzer/Core/CheckerManager.h" +#include "clang/StaticAnalyzer/Core/IssueHash.h" +#include "clang/StaticAnalyzer/Core/PathDiagnosticConsumers.h" #include "llvm/Support/Errc.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/MemoryBuffer.h" @@ -122,11 +123,11 @@ void HTMLDiagnostics::ReportDiag(const PathDiagnostic& D, // The path as already been prechecked that all parts of the path are // from the same file and that it is non-empty. - const SourceManager &SMgr = (*path.begin())->getLocation().getManager(); + const SourceManager &SMgr = path.front()->getLocation().getManager(); assert(!path.empty()); FileID FID = - (*path.begin())->getLocation().asLocation().getExpansionLoc().getFileID(); - assert(!FID.isInvalid()); + path.front()->getLocation().asLocation().getExpansionLoc().getFileID(); + assert(FID.isValid()); // Create a new rewriter to generate HTML. Rewriter R(const_cast<SourceManager&>(SMgr), PP.getLangOpts()); @@ -143,7 +144,7 @@ void HTMLDiagnostics::ReportDiag(const PathDiagnostic& D, // Retrieve the relative position of the declaration which will be used // for the file name FullSourceLoc L( - SMgr.getExpansionLoc((*path.rbegin())->getLocation().asLocation()), + SMgr.getExpansionLoc(path.back()->getLocation().asLocation()), SMgr); FullSourceLoc FunL(SMgr.getExpansionLoc(Body->getLocStart()), SMgr); offsetDecl = L.getExpansionLineNumber() - FunL.getExpansionLineNumber(); @@ -187,8 +188,8 @@ void HTMLDiagnostics::ReportDiag(const PathDiagnostic& D, DirName += '/'; } - int LineNumber = (*path.rbegin())->getLocation().asLocation().getExpansionLineNumber(); - int ColumnNumber = (*path.rbegin())->getLocation().asLocation().getExpansionColumnNumber(); + int LineNumber = path.back()->getLocation().asLocation().getExpansionLineNumber(); + int ColumnNumber = path.back()->getLocation().asLocation().getExpansionColumnNumber(); // Add the name of the file as an <h1> tag. @@ -236,6 +237,13 @@ void HTMLDiagnostics::ReportDiag(const PathDiagnostic& D, if (!BugType.empty()) os << "\n<!-- BUGTYPE " << BugType << " -->\n"; + PathDiagnosticLocation UPDLoc = D.getUniqueingLoc(); + FullSourceLoc L(SMgr.getExpansionLoc(UPDLoc.isValid() + ? UPDLoc.asLocation() + : D.getLocation().asLocation()), + SMgr); + const Decl *DeclWithIssue = D.getDeclWithIssue(); + StringRef BugCategory = D.getCategory(); if (!BugCategory.empty()) os << "\n<!-- BUGCATEGORY " << BugCategory << " -->\n"; @@ -246,6 +254,10 @@ void HTMLDiagnostics::ReportDiag(const PathDiagnostic& D, os << "\n<!-- FUNCTIONNAME " << declName << " -->\n"; + os << "\n<!-- ISSUEHASHCONTENTOFLINEINCONTEXT " + << GetIssueHash(SMgr, L, D.getCheckName(), D.getBugType(), DeclWithIssue, + PP.getLangOpts()) << " -->\n"; + os << "\n<!-- BUGLINE " << LineNumber << " -->\n"; @@ -281,7 +293,12 @@ void HTMLDiagnostics::ReportDiag(const PathDiagnostic& D, if (!AnalyzerOpts.shouldWriteStableReportFilename()) { llvm::sys::path::append(Model, Directory, "report-%%%%%%.html"); - + if (std::error_code EC = + llvm::sys::fs::make_absolute(Model)) { + llvm::errs() << "warning: could not make '" << Model + << "' absolute: " << EC.message() << '\n'; + return; + } if (std::error_code EC = llvm::sys::fs::createUniqueFile(Model, FD, ResultPath)) { llvm::errs() << "warning: could not create file in '" << Directory diff --git a/lib/StaticAnalyzer/Core/IssueHash.cpp b/lib/StaticAnalyzer/Core/IssueHash.cpp new file mode 100644 index 0000000000000..0a3af3dcc7e94 --- /dev/null +++ b/lib/StaticAnalyzer/Core/IssueHash.cpp @@ -0,0 +1,196 @@ +//===---------- IssueHash.cpp - Generate identification hashes --*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +#include "clang/StaticAnalyzer/Core/IssueHash.h" +#include "clang/AST/ASTContext.h" +#include "clang/AST/Decl.h" +#include "clang/AST/DeclCXX.h" +#include "clang/Basic/SourceManager.h" +#include "clang/Basic/Specifiers.h" +#include "clang/Lex/Lexer.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/StringExtras.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/ADT/Twine.h" +#include "llvm/Support/LineIterator.h" +#include "llvm/Support/MD5.h" +#include "llvm/Support/Path.h" + +#include <functional> +#include <sstream> +#include <string> + +using namespace clang; + +// Get a string representation of the parts of the signature that can be +// overloaded on. +static std::string GetSignature(const FunctionDecl *Target) { + if (!Target) + return ""; + std::string Signature; + + if (!isa<CXXConstructorDecl>(Target) && !isa<CXXDestructorDecl>(Target) && + !isa<CXXConversionDecl>(Target)) + Signature.append(Target->getReturnType().getAsString()).append(" "); + Signature.append(Target->getQualifiedNameAsString()).append("("); + + for (int i = 0, paramsCount = Target->getNumParams(); i < paramsCount; ++i) { + if (i) + Signature.append(", "); + Signature.append(Target->getParamDecl(i)->getType().getAsString()); + } + + if (Target->isVariadic()) + Signature.append(", ..."); + Signature.append(")"); + + const auto *TargetT = + llvm::dyn_cast_or_null<FunctionType>(Target->getType().getTypePtr()); + + if (!TargetT || !isa<CXXMethodDecl>(Target)) + return Signature; + + if (TargetT->isConst()) + Signature.append(" const"); + if (TargetT->isVolatile()) + Signature.append(" volatile"); + if (TargetT->isRestrict()) + Signature.append(" restrict"); + + if (const auto *TargetPT = + dyn_cast_or_null<FunctionProtoType>(Target->getType().getTypePtr())) { + switch (TargetPT->getRefQualifier()) { + case RQ_LValue: + Signature.append(" &"); + break; + case RQ_RValue: + Signature.append(" &&"); + break; + default: + break; + } + } + + return Signature; +} + +static std::string GetEnclosingDeclContextSignature(const Decl *D) { + if (!D) + return ""; + + if (const auto *ND = dyn_cast<NamedDecl>(D)) { + std::string DeclName; + + switch (ND->getKind()) { + case Decl::Namespace: + case Decl::Record: + case Decl::CXXRecord: + case Decl::Enum: + DeclName = ND->getQualifiedNameAsString(); + break; + case Decl::CXXConstructor: + case Decl::CXXDestructor: + case Decl::CXXConversion: + case Decl::CXXMethod: + case Decl::Function: + DeclName = GetSignature(dyn_cast_or_null<FunctionDecl>(ND)); + break; + case Decl::ObjCMethod: + // ObjC Methods can not be overloaded, qualified name uniquely identifies + // the method. + DeclName = ND->getQualifiedNameAsString(); + break; + default: + break; + } + + return DeclName; + } + + return ""; +} + +static StringRef GetNthLineOfFile(llvm::MemoryBuffer *Buffer, int Line) { + if (!Buffer) + return ""; + + llvm::line_iterator LI(*Buffer, false); + for (; !LI.is_at_eof() && LI.line_number() != Line; ++LI) + ; + + return *LI; +} + +static std::string NormalizeLine(const SourceManager &SM, FullSourceLoc &L, + const LangOptions &LangOpts) { + static StringRef Whitespaces = " \t\n"; + + StringRef Str = GetNthLineOfFile(SM.getBuffer(L.getFileID(), L), + L.getExpansionLineNumber()); + unsigned col = Str.find_first_not_of(Whitespaces); + col++; + SourceLocation StartOfLine = + SM.translateLineCol(SM.getFileID(L), L.getExpansionLineNumber(), col); + llvm::MemoryBuffer *Buffer = + SM.getBuffer(SM.getFileID(StartOfLine), StartOfLine); + if (!Buffer) + return {}; + + const char *BufferPos = SM.getCharacterData(StartOfLine); + + Token Token; + Lexer Lexer(SM.getLocForStartOfFile(SM.getFileID(StartOfLine)), LangOpts, + Buffer->getBufferStart(), BufferPos, Buffer->getBufferEnd()); + + size_t NextStart = 0; + std::ostringstream LineBuff; + while (!Lexer.LexFromRawLexer(Token) && NextStart < 2) { + if (Token.isAtStartOfLine() && NextStart++ > 0) + continue; + LineBuff << std::string(SM.getCharacterData(Token.getLocation()), + Token.getLength()); + } + + return LineBuff.str(); +} + +static llvm::SmallString<32> GetHashOfContent(StringRef Content) { + llvm::MD5 Hash; + llvm::MD5::MD5Result MD5Res; + SmallString<32> Res; + + Hash.update(Content); + Hash.final(MD5Res); + llvm::MD5::stringifyResult(MD5Res, Res); + + return Res; +} + +std::string clang::GetIssueString(const SourceManager &SM, + FullSourceLoc &IssueLoc, + StringRef CheckerName, StringRef BugType, + const Decl *D, + const LangOptions &LangOpts) { + static StringRef Delimiter = "$"; + + return (llvm::Twine(CheckerName) + Delimiter + + GetEnclosingDeclContextSignature(D) + Delimiter + + llvm::utostr(IssueLoc.getExpansionColumnNumber()) + Delimiter + + NormalizeLine(SM, IssueLoc, LangOpts) + Delimiter + BugType) + .str(); +} + +SmallString<32> clang::GetIssueHash(const SourceManager &SM, + FullSourceLoc &IssueLoc, + StringRef CheckerName, StringRef BugType, + const Decl *D, + const LangOptions &LangOpts) { + + return GetHashOfContent( + GetIssueString(SM, IssueLoc, CheckerName, BugType, D, LangOpts)); +} diff --git a/lib/StaticAnalyzer/Core/LoopWidening.cpp b/lib/StaticAnalyzer/Core/LoopWidening.cpp new file mode 100644 index 0000000000000..05865c294cb70 --- /dev/null +++ b/lib/StaticAnalyzer/Core/LoopWidening.cpp @@ -0,0 +1,68 @@ +//===--- LoopWidening.cpp - Widen loops -------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// +/// This file contains functions which are used to widen loops. A loop may be +/// widened to approximate the exit state(s), without analyzing every +/// iteration. The widening is done by invalidating anything which might be +/// modified by the body of the loop. +/// +//===----------------------------------------------------------------------===// + +#include "clang/StaticAnalyzer/Core/PathSensitive/LoopWidening.h" + +using namespace clang; +using namespace ento; + +/// Return the loops condition Stmt or NULL if LoopStmt is not a loop +static const Expr *getLoopCondition(const Stmt *LoopStmt) { + switch (LoopStmt->getStmtClass()) { + default: + return nullptr; + case Stmt::ForStmtClass: + return cast<ForStmt>(LoopStmt)->getCond(); + case Stmt::WhileStmtClass: + return cast<WhileStmt>(LoopStmt)->getCond(); + case Stmt::DoStmtClass: + return cast<DoStmt>(LoopStmt)->getCond(); + } +} + +namespace clang { +namespace ento { + +ProgramStateRef getWidenedLoopState(ProgramStateRef PrevState, + const LocationContext *LCtx, + unsigned BlockCount, const Stmt *LoopStmt) { + + assert(isa<ForStmt>(LoopStmt) || isa<WhileStmt>(LoopStmt) || + isa<DoStmt>(LoopStmt)); + + // Invalidate values in the current state. + // TODO Make this more conservative by only invalidating values that might + // be modified by the body of the loop. + // TODO Nested loops are currently widened as a result of the invalidation + // being so inprecise. When the invalidation is improved, the handling + // of nested loops will also need to be improved. + const StackFrameContext *STC = LCtx->getCurrentStackFrame(); + MemRegionManager &MRMgr = PrevState->getStateManager().getRegionManager(); + const MemRegion *Regions[] = {MRMgr.getStackLocalsRegion(STC), + MRMgr.getStackArgumentsRegion(STC), + MRMgr.getGlobalsRegion()}; + RegionAndSymbolInvalidationTraits ITraits; + for (auto *Region : Regions) { + ITraits.setTrait(Region, + RegionAndSymbolInvalidationTraits::TK_EntireMemSpace); + } + return PrevState->invalidateRegions(Regions, getLoopCondition(LoopStmt), + BlockCount, LCtx, true, nullptr, nullptr, + &ITraits); +} + +} // end namespace ento +} // end namespace clang diff --git a/lib/StaticAnalyzer/Core/Makefile b/lib/StaticAnalyzer/Core/Makefile index 4aebc163dddc2..c3e00fa36825e 100644 --- a/lib/StaticAnalyzer/Core/Makefile +++ b/lib/StaticAnalyzer/Core/Makefile @@ -1,13 +1,13 @@ ##===- clang/lib/StaticAnalyzer/Core/Makefile --------------*- Makefile -*-===## -# +# # The LLVM Compiler Infrastructure # # This file is distributed under the University of Illinois Open Source # License. See LICENSE.TXT for details. -# +# ##===----------------------------------------------------------------------===## # -# This implements analyses built on top of source-level CFGs. +# This implements analyses built on top of source-level CFGs. # ##===----------------------------------------------------------------------===## diff --git a/lib/StaticAnalyzer/Core/MemRegion.cpp b/lib/StaticAnalyzer/Core/MemRegion.cpp index 5ac845825c8dd..ad3f396e39a1f 100644 --- a/lib/StaticAnalyzer/Core/MemRegion.cpp +++ b/lib/StaticAnalyzer/Core/MemRegion.cpp @@ -756,7 +756,7 @@ getStackOrCaptureRegionForDeclContext(const LocationContext *LC, return cast<VarRegion>(I.getCapturedRegion()); } } - + LC = LC->getParent(); } return (const StackFrameContext *)nullptr; @@ -788,18 +788,18 @@ const VarRegion* MemRegionManager::getVarRegion(const VarDecl *D, else sReg = getGlobalsRegion(); } - - // Finally handle static locals. + + // Finally handle static locals. } else { // FIXME: Once we implement scope handling, we will need to properly lookup // 'D' to the proper LocationContext. const DeclContext *DC = D->getDeclContext(); llvm::PointerUnion<const StackFrameContext *, const VarRegion *> V = getStackOrCaptureRegionForDeclContext(LC, DC, D); - + if (V.is<const VarRegion*>()) return V.get<const VarRegion*>(); - + const StackFrameContext *STC = V.get<const StackFrameContext*>(); if (!STC) @@ -1013,10 +1013,22 @@ MemRegionManager::getCXXBaseObjectRegion(const CXXRecordDecl *RD, const CXXThisRegion* MemRegionManager::getCXXThisRegion(QualType thisPointerTy, const LocationContext *LC) { - const StackFrameContext *STC = LC->getCurrentStackFrame(); - assert(STC); const PointerType *PT = thisPointerTy->getAs<PointerType>(); assert(PT); + // Inside the body of the operator() of a lambda a this expr might refer to an + // object in one of the parent location contexts. + const auto *D = dyn_cast<CXXMethodDecl>(LC->getDecl()); + // FIXME: when operator() of lambda is analyzed as a top level function and + // 'this' refers to a this to the enclosing scope, there is no right region to + // return. + while (!LC->inTopFrame() && + (!D || D->isStatic() || + PT != D->getThisType(getContext())->getAs<PointerType>())) { + LC = LC->getParent(); + D = dyn_cast<CXXMethodDecl>(LC->getDecl()); + } + const StackFrameContext *STC = LC->getCurrentStackFrame(); + assert(STC); return getSubRegion<CXXThisRegion>(PT, getStackArgumentsRegion(STC)); } @@ -1165,6 +1177,7 @@ RegionRawOffset ElementRegion::getAsArrayOffset() const { /// Returns true if \p Base is an immediate base class of \p Child static bool isImmediateBase(const CXXRecordDecl *Child, const CXXRecordDecl *Base) { + assert(Child && "Child must not be null"); // Note that we do NOT canonicalize the base class here, because // ASTRecordLayout doesn't either. If that leads us down the wrong path, // so be it; at least we won't crash. @@ -1239,23 +1252,23 @@ RegionOffset MemRegion::getAsOffset() const { Ty = SR->getSymbol()->getType()->getPointeeType(); RootIsSymbolic = true; } - + const CXXRecordDecl *Child = Ty->getAsCXXRecordDecl(); if (!Child) { // We cannot compute the offset of the base class. SymbolicOffsetBase = R; - } - - if (RootIsSymbolic) { - // Base layers on symbolic regions may not be type-correct. - // Double-check the inheritance here, and revert to a symbolic offset - // if it's invalid (e.g. due to a reinterpret_cast). - if (BOR->isVirtual()) { - if (!Child->isVirtuallyDerivedFrom(BOR->getDecl())) - SymbolicOffsetBase = R; - } else { - if (!isImmediateBase(Child, BOR->getDecl())) - SymbolicOffsetBase = R; + } else { + if (RootIsSymbolic) { + // Base layers on symbolic regions may not be type-correct. + // Double-check the inheritance here, and revert to a symbolic offset + // if it's invalid (e.g. due to a reinterpret_cast). + if (BOR->isVirtual()) { + if (!Child->isVirtuallyDerivedFrom(BOR->getDecl())) + SymbolicOffsetBase = R; + } else { + if (!isImmediateBase(Child, BOR->getDecl())) + SymbolicOffsetBase = R; + } } } @@ -1290,7 +1303,7 @@ RegionOffset MemRegion::getAsOffset() const { if (Optional<nonloc::ConcreteInt> CI = Index.getAs<nonloc::ConcreteInt>()) { // Don't bother calculating precise offsets if we already have a - // symbolic offset somewhere in the chain. + // symbolic offset somewhere in the chain. if (SymbolicOffsetBase) continue; @@ -1324,7 +1337,7 @@ RegionOffset MemRegion::getAsOffset() const { // Get the field number. unsigned idx = 0; - for (RecordDecl::field_iterator FI = RD->field_begin(), + for (RecordDecl::field_iterator FI = RD->field_begin(), FE = RD->field_end(); FI != FE; ++FI, ++idx) if (FR->getDecl() == *FI) break; @@ -1420,7 +1433,7 @@ BlockDataRegion::referenced_vars_begin() const { BumpVector<const MemRegion*> *VecOriginal = static_cast<BumpVector<const MemRegion*>*>(OriginalVars); - + return BlockDataRegion::referenced_vars_iterator(Vec->begin(), VecOriginal->begin()); } @@ -1456,12 +1469,12 @@ const VarRegion *BlockDataRegion::getOriginalRegion(const VarRegion *R) const { // RegionAndSymbolInvalidationTraits //===----------------------------------------------------------------------===// -void RegionAndSymbolInvalidationTraits::setTrait(SymbolRef Sym, +void RegionAndSymbolInvalidationTraits::setTrait(SymbolRef Sym, InvalidationKinds IK) { SymTraitsMap[Sym] |= IK; } -void RegionAndSymbolInvalidationTraits::setTrait(const MemRegion *MR, +void RegionAndSymbolInvalidationTraits::setTrait(const MemRegion *MR, InvalidationKinds IK) { assert(MR); if (const SymbolicRegion *SR = dyn_cast<SymbolicRegion>(MR)) @@ -1470,13 +1483,13 @@ void RegionAndSymbolInvalidationTraits::setTrait(const MemRegion *MR, MRTraitsMap[MR] |= IK; } -bool RegionAndSymbolInvalidationTraits::hasTrait(SymbolRef Sym, +bool RegionAndSymbolInvalidationTraits::hasTrait(SymbolRef Sym, InvalidationKinds IK) { const_symbol_iterator I = SymTraitsMap.find(Sym); if (I != SymTraitsMap.end()) return I->second & IK; - return false; + return false; } bool RegionAndSymbolInvalidationTraits::hasTrait(const MemRegion *MR, diff --git a/lib/StaticAnalyzer/Core/PathDiagnostic.cpp b/lib/StaticAnalyzer/Core/PathDiagnostic.cpp index c4900313cad45..504df30de834c 100644 --- a/lib/StaticAnalyzer/Core/PathDiagnostic.cpp +++ b/lib/StaticAnalyzer/Core/PathDiagnostic.cpp @@ -62,8 +62,6 @@ PathDiagnosticControlFlowPiece::~PathDiagnosticControlFlowPiece() {} PathDiagnosticMacroPiece::~PathDiagnosticMacroPiece() {} -PathPieces::~PathPieces() {} - void PathPieces::flattenTo(PathPieces &Primary, PathPieces &Current, bool ShouldFlattenMacros) const { for (PathPieces::const_iterator I = begin(), E = end(); I != E; ++I) { @@ -181,7 +179,7 @@ void PathDiagnostic::resetDiagnosticLocationToMainFile() { // Reset the report containing declaration and location. DeclWithIssue = CP->getCaller(); Loc = CP->getLocation(); - + return; } } @@ -201,7 +199,7 @@ void PathDiagnosticConsumer::HandlePathDiagnostic( std::unique_ptr<PathDiagnostic> D) { if (!D || D->path.empty()) return; - + // We need to flatten the locations (convert Stmt* to locations) because // the referenced statements may be freed by the time the diagnostics // are emitted. @@ -223,12 +221,12 @@ void PathDiagnosticConsumer::HandlePathDiagnostic( ++I) { const PathDiagnosticPiece *piece = I->get(); FullSourceLoc L = piece->getLocation().asLocation().getExpansionLoc(); - + if (FID.isInvalid()) { FID = SMgr.getFileID(L); } else if (SMgr.getFileID(L) != FID) return; // FIXME: Emit a warning? - + // Check the source ranges. ArrayRef<SourceRange> Ranges = piece->getRanges(); for (ArrayRef<SourceRange>::iterator I = Ranges.begin(), @@ -240,7 +238,7 @@ void PathDiagnosticConsumer::HandlePathDiagnostic( if (!L.isFileID() || SMgr.getFileID(L) != FID) return; // FIXME: Emit a warning? } - + if (const PathDiagnosticCallPiece *call = dyn_cast<PathDiagnosticCallPiece>(piece)) { WorkList.push_back(&call->path); @@ -251,10 +249,10 @@ void PathDiagnosticConsumer::HandlePathDiagnostic( } } } - + if (FID.isInvalid()) return; // FIXME: Emit a warning? - } + } // Profile the node to see if we already have something matching it llvm::FoldingSetNodeID profile; @@ -320,7 +318,7 @@ static Optional<bool> comparePiece(const PathDiagnosticPiece &X, const PathDiagnosticPiece &Y) { if (X.getKind() != Y.getKind()) return X.getKind() < Y.getKind(); - + FullSourceLoc XL = X.getLocation().asLocation(); FullSourceLoc YL = Y.getLocation().asLocation(); if (XL != YL) @@ -333,7 +331,7 @@ static Optional<bool> comparePiece(const PathDiagnosticPiece &X, return X.getRanges().size() < Y.getRanges().size(); const SourceManager &SM = XL.getManager(); - + for (unsigned i = 0, n = X.getRanges().size(); i < n; ++i) { SourceRange XR = X.getRanges()[i]; SourceRange YR = Y.getRanges()[i]; @@ -343,7 +341,7 @@ static Optional<bool> comparePiece(const PathDiagnosticPiece &X, return SM.isBeforeInTranslationUnit(XR.getEnd(), YR.getEnd()); } } - + switch (X.getKind()) { case clang::ento::PathDiagnosticPiece::ControlFlow: return compareControlFlow(cast<PathDiagnosticControlFlowPiece>(X), @@ -420,9 +418,9 @@ void PathDiagnosticConsumer::FlushDiagnostics( PathDiagnosticConsumer::FilesMade *Files) { if (flushed) return; - + flushed = true; - + std::vector<const PathDiagnostic *> BatchDiags; for (llvm::FoldingSet<PathDiagnostic>::iterator it = Diags.begin(), et = Diags.end(); it != et; ++it) { @@ -450,7 +448,7 @@ void PathDiagnosticConsumer::FlushDiagnostics( const PathDiagnostic *D = *it; delete D; } - + // Clear out the FoldingSet. Diags.clear(); } @@ -472,7 +470,7 @@ void PathDiagnosticConsumer::FilesMade::addDiagnostic(const PathDiagnostic &PD, Entry = new (Entry) PDFileEntry(NodeID); Set.InsertNode(Entry, InsertPos); } - + // Allocate persistent storage for the file name. char *FileName_cstr = (char*) Alloc.Allocate(FileName.size(), 1); memcpy(FileName_cstr, FileName.data(), FileName.size()); @@ -847,7 +845,7 @@ PathDiagnosticRange SourceRange R = S->getSourceRange(); if (R.isValid()) return R; - break; + break; } case DeclK: if (const ObjCMethodDecl *MD = dyn_cast<ObjCMethodDecl>(D)) @@ -949,7 +947,7 @@ static bool describeCodeDecl(raw_ostream &Out, const Decl *D, Out << "constructor"; describeClass(Out, MD->getParent(), " for "); - + } else if (isa<CXXDestructorDecl>(MD)) { if (!MD->isUserProvided()) { Out << "destructor"; @@ -1041,7 +1039,7 @@ static void compute_path_size(const PathPieces &pieces, unsigned &size) { for (PathPieces::const_iterator it = pieces.begin(), et = pieces.end(); it != et; ++it) { const PathDiagnosticPiece *piece = it->get(); - if (const PathDiagnosticCallPiece *cp = + if (const PathDiagnosticCallPiece *cp = dyn_cast<PathDiagnosticCallPiece>(piece)) { compute_path_size(cp->path, size); } @@ -1077,12 +1075,12 @@ void PathDiagnosticPiece::Profile(llvm::FoldingSetNodeID &ID) const { I != E; ++I) { ID.AddInteger(I->getBegin().getRawEncoding()); ID.AddInteger(I->getEnd().getRawEncoding()); - } + } } void PathDiagnosticCallPiece::Profile(llvm::FoldingSetNodeID &ID) const { PathDiagnosticPiece::Profile(ID); - for (PathPieces::const_iterator it = path.begin(), + for (PathPieces::const_iterator it = path.begin(), et = path.end(); it != et; ++it) { ID.Add(**it); } diff --git a/lib/StaticAnalyzer/Core/PlistDiagnostics.cpp b/lib/StaticAnalyzer/Core/PlistDiagnostics.cpp index e0aff589e0533..55e1222e0ac6c 100644 --- a/lib/StaticAnalyzer/Core/PlistDiagnostics.cpp +++ b/lib/StaticAnalyzer/Core/PlistDiagnostics.cpp @@ -11,13 +11,13 @@ // //===----------------------------------------------------------------------===// -#include "clang/StaticAnalyzer/Core/AnalyzerOptions.h" #include "clang/Basic/FileManager.h" #include "clang/Basic/PlistSupport.h" #include "clang/Basic/SourceManager.h" #include "clang/Basic/Version.h" #include "clang/Lex/Preprocessor.h" #include "clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h" +#include "clang/StaticAnalyzer/Core/IssueHash.h" #include "clang/StaticAnalyzer/Core/PathDiagnosticConsumers.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/SmallVector.h" @@ -171,7 +171,7 @@ static void ReportEvent(raw_ostream &o, const PathDiagnosticPiece& P, --indent; Indent(o, indent) << "</array>\n"; } - + // Output the call depth. Indent(o, indent) << "<key>depth</key>"; EmitInteger(o, depth) << '\n'; @@ -187,7 +187,7 @@ static void ReportEvent(raw_ostream &o, const PathDiagnosticPiece& P, Indent(o, indent) << "<key>message</key>\n"; Indent(o, indent); EmitString(o, P.getString()) << '\n'; - + // Finish up. --indent; Indent(o, indent); o << "</dict>\n"; @@ -208,9 +208,9 @@ static void ReportCall(raw_ostream &o, const LangOptions &LangOpts, unsigned indent, unsigned depth) { - + IntrusiveRefCntPtr<PathDiagnosticEventPiece> callEnter = - P.getCallEnterEvent(); + P.getCallEnterEvent(); if (callEnter) ReportPiece(o, *callEnter, FM, SM, LangOpts, indent, depth, true, @@ -218,18 +218,18 @@ static void ReportCall(raw_ostream &o, IntrusiveRefCntPtr<PathDiagnosticEventPiece> callEnterWithinCaller = P.getCallEnterWithinCallerEvent(); - + ++depth; - + if (callEnterWithinCaller) ReportPiece(o, *callEnterWithinCaller, FM, SM, LangOpts, indent, depth, true); - + for (PathPieces::const_iterator I = P.path.begin(), E = P.path.end();I!=E;++I) ReportPiece(o, **I, FM, SM, LangOpts, indent, depth, true); --depth; - + IntrusiveRefCntPtr<PathDiagnosticEventPiece> callExit = P.getCallExitEvent(); @@ -295,9 +295,9 @@ void PlistDiagnostics::FlushDiagnosticsImpl( const SourceManager* SM = nullptr; if (!Diags.empty()) - SM = &(*(*Diags.begin())->path.begin())->getLocation().getManager(); + SM = &Diags.front()->path.front()->getLocation().getManager(); + - for (std::vector<const PathDiagnostic*>::iterator DI = Diags.begin(), DE = Diags.end(); DI != DE; ++DI) { @@ -374,7 +374,7 @@ void PlistDiagnostics::FlushDiagnosticsImpl( o << " <array>\n"; - for (PathPieces::const_iterator I = D->path.begin(), E = D->path.end(); + for (PathPieces::const_iterator I = D->path.begin(), E = D->path.end(); I != E; ++I) ReportDiag(o, **I, FM, *SM, LangOpts); @@ -389,7 +389,19 @@ void PlistDiagnostics::FlushDiagnosticsImpl( EmitString(o, D->getBugType()) << '\n'; o << " <key>check_name</key>"; EmitString(o, D->getCheckName()) << '\n'; - + + o << " <!-- This hash is experimental and going to change! -->\n"; + o << " <key>issue_hash_content_of_line_in_context</key>"; + PathDiagnosticLocation UPDLoc = D->getUniqueingLoc(); + FullSourceLoc L(SM->getExpansionLoc(UPDLoc.isValid() + ? UPDLoc.asLocation() + : D->getLocation().asLocation()), + *SM); + const Decl *DeclWithIssue = D->getDeclWithIssue(); + EmitString(o, GetIssueHash(*SM, L, D->getCheckName(), D->getBugType(), + DeclWithIssue, LangOpts)) + << '\n'; + // Output information about the semantic context where // the issue occurred. if (const Decl *DeclWithIssue = D->getDeclWithIssue()) { @@ -423,28 +435,23 @@ void PlistDiagnostics::FlushDiagnosticsImpl( // Output the bug hash for issue unique-ing. Currently, it's just an // offset from the beginning of the function. if (const Stmt *Body = DeclWithIssue->getBody()) { - + // If the bug uniqueing location exists, use it for the hash. // For example, this ensures that two leaks reported on the same line // will have different issue_hashes and that the hash will identify // the leak location even after code is added between the allocation // site and the end of scope (leak report location). - PathDiagnosticLocation UPDLoc = D->getUniqueingLoc(); if (UPDLoc.isValid()) { - FullSourceLoc UL(SM->getExpansionLoc(UPDLoc.asLocation()), - *SM); FullSourceLoc UFunL(SM->getExpansionLoc( D->getUniqueingDecl()->getBody()->getLocStart()), *SM); - o << " <key>issue_hash</key><string>" - << UL.getExpansionLineNumber() - UFunL.getExpansionLineNumber() + o << " <key>issue_hash_function_offset</key><string>" + << L.getExpansionLineNumber() - UFunL.getExpansionLineNumber() << "</string>\n"; // Otherwise, use the location on which the bug is reported. } else { - FullSourceLoc L(SM->getExpansionLoc(D->getLocation().asLocation()), - *SM); FullSourceLoc FunL(SM->getExpansionLoc(Body->getLocStart()), *SM); - o << " <key>issue_hash</key><string>" + o << " <key>issue_hash_function_offset</key><string>" << L.getExpansionLineNumber() - FunL.getExpansionLineNumber() << "</string>\n"; } @@ -486,5 +493,5 @@ void PlistDiagnostics::FlushDiagnosticsImpl( o << " </array>\n"; // Finish. - o << "</dict>\n</plist>"; + o << "</dict>\n</plist>"; } diff --git a/lib/StaticAnalyzer/Core/ProgramState.cpp b/lib/StaticAnalyzer/Core/ProgramState.cpp index 60b32c722ebf8..4f9ad9ebccd9d 100644 --- a/lib/StaticAnalyzer/Core/ProgramState.cpp +++ b/lib/StaticAnalyzer/Core/ProgramState.cpp @@ -36,7 +36,7 @@ void ProgramStateRelease(const ProgramState *state) { if (--s->refCount == 0) { ProgramStateManager &Mgr = s->getStateManager(); Mgr.StateSet.RemoveNode(s); - s->~ProgramState(); + s->~ProgramState(); Mgr.freeStates.push_back(s); } } @@ -86,7 +86,7 @@ ProgramStateManager::~ProgramStateManager() { I->second.second(I->second.first); } -ProgramStateRef +ProgramStateRef ProgramStateManager::removeDeadBindings(ProgramStateRef state, const StackFrameContext *LCtx, SymbolReaper& SymReaper) { @@ -113,7 +113,7 @@ ProgramStateManager::removeDeadBindings(ProgramStateRef state, ProgramStateRef ProgramState::bindLoc(Loc LV, SVal V, bool notifyChanges) const { ProgramStateManager &Mgr = getStateManager(); - ProgramStateRef newState = makeWithStore(Mgr.StoreMgr->Bind(getStore(), + ProgramStateRef newState = makeWithStore(Mgr.StoreMgr->Bind(getStore(), LV, V)); const MemRegion *MR = LV.getAsRegion(); if (MR && Mgr.getOwningEngine() && notifyChanges) @@ -127,15 +127,15 @@ ProgramStateRef ProgramState::bindDefault(SVal loc, SVal V) const { const MemRegion *R = loc.castAs<loc::MemRegionVal>().getRegion(); const StoreRef &newStore = Mgr.StoreMgr->BindDefault(getStore(), R, V); ProgramStateRef new_state = makeWithStore(newStore); - return Mgr.getOwningEngine() ? - Mgr.getOwningEngine()->processRegionChange(new_state, R) : + return Mgr.getOwningEngine() ? + Mgr.getOwningEngine()->processRegionChange(new_state, R) : new_state; } typedef ArrayRef<const MemRegion *> RegionList; typedef ArrayRef<SVal> ValueList; -ProgramStateRef +ProgramStateRef ProgramState::invalidateRegions(RegionList Regions, const Expr *E, unsigned Count, const LocationContext *LCtx, @@ -197,11 +197,11 @@ ProgramState::invalidateRegionsImpl(ValueList Values, if (CausedByPointerEscape) { newState = Eng->notifyCheckersOfPointerEscape(newState, IS, TopLevelInvalidated, - Invalidated, Call, + Invalidated, Call, *ITraits); } - return Eng->processRegionChanges(newState, IS, TopLevelInvalidated, + return Eng->processRegionChanges(newState, IS, TopLevelInvalidated, Invalidated, Call); } @@ -224,7 +224,7 @@ ProgramStateRef ProgramState::killBinding(Loc LV) const { return makeWithStore(newStore); } -ProgramStateRef +ProgramStateRef ProgramState::enterStackFrame(const CallEvent &Call, const StackFrameContext *CalleeCtx) const { const StoreRef &NewStore = @@ -275,7 +275,7 @@ SVal ProgramState::getSVal(Loc location, QualType T) const { // symbol for the call to foo(); the type of that symbol is 'char', // not unsigned. const llvm::APSInt &NewV = getBasicVals().Convert(T, *Int); - + if (V.getAs<Loc>()) return loc::ConcreteInt(NewV); else @@ -283,7 +283,7 @@ SVal ProgramState::getSVal(Loc location, QualType T) const { } } } - + return V; } @@ -353,11 +353,11 @@ ConditionTruthVal ProgramState::isNull(SVal V) const { if (V.isConstant()) return false; - + SymbolRef Sym = V.getAsSymbol(/* IncludeBaseRegion */ true); if (!Sym) return ConditionTruthVal(); - + return getStateManager().ConstraintMgr->isNull(this, Sym); } @@ -390,7 +390,7 @@ ProgramStateRef ProgramStateManager::getPersistentState(ProgramState &State) { ProgramState *newState = nullptr; if (!freeStates.empty()) { newState = freeStates.back(); - freeStates.pop_back(); + freeStates.pop_back(); } else { newState = (ProgramState*) Alloc.Allocate<ProgramState>(); @@ -530,10 +530,10 @@ bool ScanReachableSymbols::scan(const SymExpr *sym) { bool wasVisited = !visited.insert(sym).second; if (wasVisited) return true; - + if (!visitor.VisitSymbol(sym)) return false; - + // TODO: should be rewritten using SymExpr::symbol_iterator. switch (sym->getKind()) { case SymExpr::RegionValueKind: @@ -582,11 +582,11 @@ bool ScanReachableSymbols::scan(SVal val) { bool ScanReachableSymbols::scan(const MemRegion *R) { if (isa<MemSpaceRegion>(R)) return true; - + bool wasVisited = !visited.insert(R).second; if (wasVisited) return true; - + if (!visitor.VisitMemRegion(R)) return false; @@ -722,14 +722,14 @@ bool ProgramState::isTainted(const MemRegion *Reg, TaintTagType K) const { bool ProgramState::isTainted(SymbolRef Sym, TaintTagType Kind) const { if (!Sym) return false; - + // Traverse all the symbols this symbol depends on to see if any are tainted. bool Tainted = false; for (SymExpr::symbol_iterator SI = Sym->symbol_begin(), SE =Sym->symbol_end(); SI != SE; ++SI) { if (!isa<SymbolData>(*SI)) continue; - + const TaintTagType *Tag = get<TaintMap>(*SI); Tainted = (Tag && *Tag == Kind); @@ -748,40 +748,7 @@ bool ProgramState::isTainted(SymbolRef Sym, TaintTagType Kind) const { if (Tainted) return true; } - - return Tainted; -} - -/// The GDM component containing the dynamic type info. This is a map from a -/// symbol to its most likely type. -REGISTER_TRAIT_WITH_PROGRAMSTATE(DynamicTypeMap, - CLANG_ENTO_PROGRAMSTATE_MAP(const MemRegion *, - DynamicTypeInfo)) - -DynamicTypeInfo ProgramState::getDynamicTypeInfo(const MemRegion *Reg) const { - Reg = Reg->StripCasts(); - - // Look up the dynamic type in the GDM. - const DynamicTypeInfo *GDMType = get<DynamicTypeMap>(Reg); - if (GDMType) - return *GDMType; - - // Otherwise, fall back to what we know about the region. - if (const TypedRegion *TR = dyn_cast<TypedRegion>(Reg)) - return DynamicTypeInfo(TR->getLocationType(), /*CanBeSubclass=*/false); - if (const SymbolicRegion *SR = dyn_cast<SymbolicRegion>(Reg)) { - SymbolRef Sym = SR->getSymbol(); - return DynamicTypeInfo(Sym->getType()); - } - - return DynamicTypeInfo(); + return Tainted; } -ProgramStateRef ProgramState::setDynamicTypeInfo(const MemRegion *Reg, - DynamicTypeInfo NewTy) const { - Reg = Reg->StripCasts(); - ProgramStateRef NewState = set<DynamicTypeMap>(Reg, NewTy); - assert(NewState); - return NewState; -} diff --git a/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp b/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp index 170f7c02b8820..0a2b2e64a142a 100644 --- a/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp +++ b/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp @@ -81,6 +81,15 @@ public: RangeSet(PrimRangeSet RS) : ranges(RS) {} + /// Create a new set with all ranges of this set and RS. + /// Possible intersections are not checked here. + RangeSet addRange(Factory &F, const RangeSet &RS) { + PrimRangeSet Ranges(RS.ranges); + for (const auto &range : ranges) + Ranges = F.add(Ranges, range); + return RangeSet(Ranges); + } + iterator begin() const { return ranges.begin(); } iterator end() const { return ranges.end(); } @@ -312,6 +321,14 @@ public: const llvm::APSInt& Int, const llvm::APSInt& Adjustment) override; + ProgramStateRef assumeSymbolWithinInclusiveRange( + ProgramStateRef State, SymbolRef Sym, const llvm::APSInt &From, + const llvm::APSInt &To, const llvm::APSInt &Adjustment) override; + + ProgramStateRef assumeSymbolOutOfInclusiveRange( + ProgramStateRef State, SymbolRef Sym, const llvm::APSInt &From, + const llvm::APSInt &To, const llvm::APSInt &Adjustment) override; + const llvm::APSInt* getSymVal(ProgramStateRef St, SymbolRef sym) const override; ConditionTruthVal checkNull(ProgramStateRef State, SymbolRef Sym) override; @@ -324,6 +341,20 @@ public: private: RangeSet::Factory F; + RangeSet getSymLTRange(ProgramStateRef St, SymbolRef Sym, + const llvm::APSInt &Int, + const llvm::APSInt &Adjustment); + RangeSet getSymGTRange(ProgramStateRef St, SymbolRef Sym, + const llvm::APSInt &Int, + const llvm::APSInt &Adjustment); + RangeSet getSymLERange(ProgramStateRef St, SymbolRef Sym, + const llvm::APSInt &Int, + const llvm::APSInt &Adjustment); + RangeSet getSymLERange(const RangeSet &RS, const llvm::APSInt &Int, + const llvm::APSInt &Adjustment); + RangeSet getSymGERange(ProgramStateRef St, SymbolRef Sym, + const llvm::APSInt &Int, + const llvm::APSInt &Adjustment); }; } // end anonymous namespace @@ -365,7 +396,7 @@ ConditionTruthVal RangeConstraintManager::checkNull(ProgramStateRef State, /// Scan all symbols referenced by the constraints. If the symbol is not alive /// as marked in LSymbols, mark it as dead in DSymbols. -ProgramStateRef +ProgramStateRef RangeConstraintManager::removeDeadBindings(ProgramStateRef state, SymbolReaper& SymReaper) { @@ -415,7 +446,7 @@ RangeConstraintManager::GetRange(ProgramStateRef state, SymbolRef sym) { // As an example, the range [UINT_MAX-1, 3) contains five values: UINT_MAX-1, // UINT_MAX, 0, 1, and 2. -ProgramStateRef +ProgramStateRef RangeConstraintManager::assumeSymNE(ProgramStateRef St, SymbolRef Sym, const llvm::APSInt &Int, const llvm::APSInt &Adjustment) { @@ -435,7 +466,7 @@ RangeConstraintManager::assumeSymNE(ProgramStateRef St, SymbolRef Sym, return New.isEmpty() ? nullptr : St->set<ConstraintRange>(Sym, New); } -ProgramStateRef +ProgramStateRef RangeConstraintManager::assumeSymEQ(ProgramStateRef St, SymbolRef Sym, const llvm::APSInt &Int, const llvm::APSInt &Adjustment) { @@ -450,122 +481,199 @@ RangeConstraintManager::assumeSymEQ(ProgramStateRef St, SymbolRef Sym, return New.isEmpty() ? nullptr : St->set<ConstraintRange>(Sym, New); } -ProgramStateRef -RangeConstraintManager::assumeSymLT(ProgramStateRef St, SymbolRef Sym, - const llvm::APSInt &Int, - const llvm::APSInt &Adjustment) { +RangeSet RangeConstraintManager::getSymLTRange(ProgramStateRef St, + SymbolRef Sym, + const llvm::APSInt &Int, + const llvm::APSInt &Adjustment) { // Before we do any real work, see if the value can even show up. APSIntType AdjustmentType(Adjustment); switch (AdjustmentType.testInRange(Int, true)) { case APSIntType::RTR_Below: - return nullptr; + return F.getEmptySet(); case APSIntType::RTR_Within: break; case APSIntType::RTR_Above: - return St; + return GetRange(St, Sym); } // Special case for Int == Min. This is always false. llvm::APSInt ComparisonVal = AdjustmentType.convert(Int); llvm::APSInt Min = AdjustmentType.getMinValue(); if (ComparisonVal == Min) - return nullptr; + return F.getEmptySet(); - llvm::APSInt Lower = Min-Adjustment; - llvm::APSInt Upper = ComparisonVal-Adjustment; + llvm::APSInt Lower = Min - Adjustment; + llvm::APSInt Upper = ComparisonVal - Adjustment; --Upper; - RangeSet New = GetRange(St, Sym).Intersect(getBasicVals(), F, Lower, Upper); - return New.isEmpty() ? nullptr : St->set<ConstraintRange>(Sym, New); + return GetRange(St, Sym).Intersect(getBasicVals(), F, Lower, Upper); } -ProgramStateRef -RangeConstraintManager::assumeSymGT(ProgramStateRef St, SymbolRef Sym, +ProgramStateRef +RangeConstraintManager::assumeSymLT(ProgramStateRef St, SymbolRef Sym, const llvm::APSInt &Int, const llvm::APSInt &Adjustment) { + RangeSet New = getSymLTRange(St, Sym, Int, Adjustment); + return New.isEmpty() ? nullptr : St->set<ConstraintRange>(Sym, New); +} + +RangeSet +RangeConstraintManager::getSymGTRange(ProgramStateRef St, SymbolRef Sym, + const llvm::APSInt &Int, + const llvm::APSInt &Adjustment) { // Before we do any real work, see if the value can even show up. APSIntType AdjustmentType(Adjustment); switch (AdjustmentType.testInRange(Int, true)) { case APSIntType::RTR_Below: - return St; + return GetRange(St, Sym); case APSIntType::RTR_Within: break; case APSIntType::RTR_Above: - return nullptr; + return F.getEmptySet(); } // Special case for Int == Max. This is always false. llvm::APSInt ComparisonVal = AdjustmentType.convert(Int); llvm::APSInt Max = AdjustmentType.getMaxValue(); if (ComparisonVal == Max) - return nullptr; + return F.getEmptySet(); - llvm::APSInt Lower = ComparisonVal-Adjustment; - llvm::APSInt Upper = Max-Adjustment; + llvm::APSInt Lower = ComparisonVal - Adjustment; + llvm::APSInt Upper = Max - Adjustment; ++Lower; - RangeSet New = GetRange(St, Sym).Intersect(getBasicVals(), F, Lower, Upper); - return New.isEmpty() ? nullptr : St->set<ConstraintRange>(Sym, New); + return GetRange(St, Sym).Intersect(getBasicVals(), F, Lower, Upper); } -ProgramStateRef -RangeConstraintManager::assumeSymGE(ProgramStateRef St, SymbolRef Sym, +ProgramStateRef +RangeConstraintManager::assumeSymGT(ProgramStateRef St, SymbolRef Sym, const llvm::APSInt &Int, const llvm::APSInt &Adjustment) { + RangeSet New = getSymGTRange(St, Sym, Int, Adjustment); + return New.isEmpty() ? nullptr : St->set<ConstraintRange>(Sym, New); +} + +RangeSet +RangeConstraintManager::getSymGERange(ProgramStateRef St, SymbolRef Sym, + const llvm::APSInt &Int, + const llvm::APSInt &Adjustment) { // Before we do any real work, see if the value can even show up. APSIntType AdjustmentType(Adjustment); switch (AdjustmentType.testInRange(Int, true)) { case APSIntType::RTR_Below: - return St; + return GetRange(St, Sym); case APSIntType::RTR_Within: break; case APSIntType::RTR_Above: - return nullptr; + return F.getEmptySet(); } // Special case for Int == Min. This is always feasible. llvm::APSInt ComparisonVal = AdjustmentType.convert(Int); llvm::APSInt Min = AdjustmentType.getMinValue(); if (ComparisonVal == Min) - return St; + return GetRange(St, Sym); llvm::APSInt Max = AdjustmentType.getMaxValue(); - llvm::APSInt Lower = ComparisonVal-Adjustment; - llvm::APSInt Upper = Max-Adjustment; + llvm::APSInt Lower = ComparisonVal - Adjustment; + llvm::APSInt Upper = Max - Adjustment; - RangeSet New = GetRange(St, Sym).Intersect(getBasicVals(), F, Lower, Upper); - return New.isEmpty() ? nullptr : St->set<ConstraintRange>(Sym, New); + return GetRange(St, Sym).Intersect(getBasicVals(), F, Lower, Upper); } -ProgramStateRef -RangeConstraintManager::assumeSymLE(ProgramStateRef St, SymbolRef Sym, +ProgramStateRef +RangeConstraintManager::assumeSymGE(ProgramStateRef St, SymbolRef Sym, const llvm::APSInt &Int, const llvm::APSInt &Adjustment) { + RangeSet New = getSymGERange(St, Sym, Int, Adjustment); + return New.isEmpty() ? nullptr : St->set<ConstraintRange>(Sym, New); +} + +RangeSet +RangeConstraintManager::getSymLERange(const RangeSet &RS, + const llvm::APSInt &Int, + const llvm::APSInt &Adjustment) { // Before we do any real work, see if the value can even show up. APSIntType AdjustmentType(Adjustment); switch (AdjustmentType.testInRange(Int, true)) { case APSIntType::RTR_Below: - return nullptr; + return F.getEmptySet(); case APSIntType::RTR_Within: break; case APSIntType::RTR_Above: - return St; + return RS; } // Special case for Int == Max. This is always feasible. llvm::APSInt ComparisonVal = AdjustmentType.convert(Int); llvm::APSInt Max = AdjustmentType.getMaxValue(); if (ComparisonVal == Max) - return St; + return RS; + + llvm::APSInt Min = AdjustmentType.getMinValue(); + llvm::APSInt Lower = Min - Adjustment; + llvm::APSInt Upper = ComparisonVal - Adjustment; + + return RS.Intersect(getBasicVals(), F, Lower, Upper); +} + +RangeSet +RangeConstraintManager::getSymLERange(ProgramStateRef St, SymbolRef Sym, + const llvm::APSInt &Int, + const llvm::APSInt &Adjustment) { + // Before we do any real work, see if the value can even show up. + APSIntType AdjustmentType(Adjustment); + switch (AdjustmentType.testInRange(Int, true)) { + case APSIntType::RTR_Below: + return F.getEmptySet(); + case APSIntType::RTR_Within: + break; + case APSIntType::RTR_Above: + return GetRange(St, Sym); + } + + // Special case for Int == Max. This is always feasible. + llvm::APSInt ComparisonVal = AdjustmentType.convert(Int); + llvm::APSInt Max = AdjustmentType.getMaxValue(); + if (ComparisonVal == Max) + return GetRange(St, Sym); llvm::APSInt Min = AdjustmentType.getMinValue(); - llvm::APSInt Lower = Min-Adjustment; - llvm::APSInt Upper = ComparisonVal-Adjustment; + llvm::APSInt Lower = Min - Adjustment; + llvm::APSInt Upper = ComparisonVal - Adjustment; + + return GetRange(St, Sym).Intersect(getBasicVals(), F, Lower, Upper); +} - RangeSet New = GetRange(St, Sym).Intersect(getBasicVals(), F, Lower, Upper); +ProgramStateRef +RangeConstraintManager::assumeSymLE(ProgramStateRef St, SymbolRef Sym, + const llvm::APSInt &Int, + const llvm::APSInt &Adjustment) { + RangeSet New = getSymLERange(St, Sym, Int, Adjustment); return New.isEmpty() ? nullptr : St->set<ConstraintRange>(Sym, New); } +ProgramStateRef +RangeConstraintManager::assumeSymbolWithinInclusiveRange( + ProgramStateRef State, SymbolRef Sym, const llvm::APSInt &From, + const llvm::APSInt &To, const llvm::APSInt &Adjustment) { + RangeSet New = getSymGERange(State, Sym, From, Adjustment); + if (New.isEmpty()) + return nullptr; + New = getSymLERange(New, To, Adjustment); + return New.isEmpty() ? nullptr : State->set<ConstraintRange>(Sym, New); +} + +ProgramStateRef +RangeConstraintManager::assumeSymbolOutOfInclusiveRange( + ProgramStateRef State, SymbolRef Sym, const llvm::APSInt &From, + const llvm::APSInt &To, const llvm::APSInt &Adjustment) { + RangeSet RangeLT = getSymLTRange(State, Sym, From, Adjustment); + RangeSet RangeGT = getSymGTRange(State, Sym, To, Adjustment); + RangeSet New(RangeLT.addRange(F, RangeGT)); + return New.isEmpty() ? nullptr : State->set<ConstraintRange>(Sym, New); +} + //===------------------------------------------------------------------------=== // Pretty-printing. //===------------------------------------------------------------------------===/ diff --git a/lib/StaticAnalyzer/Core/RegionStore.cpp b/lib/StaticAnalyzer/Core/RegionStore.cpp index 6d41fc2146fe4..a63f6e4962723 100644 --- a/lib/StaticAnalyzer/Core/RegionStore.cpp +++ b/lib/StaticAnalyzer/Core/RegionStore.cpp @@ -149,7 +149,8 @@ typedef llvm::ImmutableMap<const MemRegion *, ClusterBindings> namespace { class RegionBindingsRef : public llvm::ImmutableMapRef<const MemRegion *, ClusterBindings> { - ClusterBindings::Factory &CBFactory; + ClusterBindings::Factory *CBFactory; + public: typedef llvm::ImmutableMapRef<const MemRegion *, ClusterBindings> ParentTy; @@ -157,21 +158,21 @@ public: RegionBindingsRef(ClusterBindings::Factory &CBFactory, const RegionBindings::TreeTy *T, RegionBindings::TreeTy::Factory *F) - : llvm::ImmutableMapRef<const MemRegion *, ClusterBindings>(T, F), - CBFactory(CBFactory) {} + : llvm::ImmutableMapRef<const MemRegion *, ClusterBindings>(T, F), + CBFactory(&CBFactory) {} RegionBindingsRef(const ParentTy &P, ClusterBindings::Factory &CBFactory) - : llvm::ImmutableMapRef<const MemRegion *, ClusterBindings>(P), - CBFactory(CBFactory) {} + : llvm::ImmutableMapRef<const MemRegion *, ClusterBindings>(P), + CBFactory(&CBFactory) {} RegionBindingsRef add(key_type_ref K, data_type_ref D) const { - return RegionBindingsRef(static_cast<const ParentTy*>(this)->add(K, D), - CBFactory); + return RegionBindingsRef(static_cast<const ParentTy *>(this)->add(K, D), + *CBFactory); } RegionBindingsRef remove(key_type_ref K) const { - return RegionBindingsRef(static_cast<const ParentTy*>(this)->remove(K), - CBFactory); + return RegionBindingsRef(static_cast<const ParentTy *>(this)->remove(K), + *CBFactory); } RegionBindingsRef addBinding(BindingKey K, SVal V) const; @@ -179,16 +180,9 @@ public: RegionBindingsRef addBinding(const MemRegion *R, BindingKey::Kind k, SVal V) const; - RegionBindingsRef &operator=(const RegionBindingsRef &X) { - *static_cast<ParentTy*>(this) = X; - return *this; - } - const SVal *lookup(BindingKey K) const; const SVal *lookup(const MemRegion *R, BindingKey::Kind k) const; - const ClusterBindings *lookup(const MemRegion *R) const { - return static_cast<const ParentTy*>(this)->lookup(R); - } + using llvm::ImmutableMapRef<const MemRegion *, ClusterBindings>::lookup; RegionBindingsRef removeBinding(BindingKey K); @@ -245,10 +239,10 @@ RegionBindingsRef RegionBindingsRef::addBinding(BindingKey K, SVal V) const { const MemRegion *Base = K.getBaseRegion(); const ClusterBindings *ExistingCluster = lookup(Base); - ClusterBindings Cluster = (ExistingCluster ? *ExistingCluster - : CBFactory.getEmptyMap()); + ClusterBindings Cluster = + (ExistingCluster ? *ExistingCluster : CBFactory->getEmptyMap()); - ClusterBindings NewCluster = CBFactory.add(Cluster, K, V); + ClusterBindings NewCluster = CBFactory->add(Cluster, K, V); return add(Base, NewCluster); } @@ -277,7 +271,7 @@ RegionBindingsRef RegionBindingsRef::removeBinding(BindingKey K) { if (!Cluster) return *this; - ClusterBindings NewCluster = CBFactory.remove(*Cluster, K); + ClusterBindings NewCluster = CBFactory->remove(*Cluster, K); if (NewCluster.isEmpty()) return remove(Base); return add(Base, NewCluster); @@ -470,9 +464,9 @@ public: // Part of public interface to class. StoreRef killBinding(Store ST, Loc L) override; void incrementReferenceCount(Store store) override { - getRegionBindings(store).manualRetain(); + getRegionBindings(store).manualRetain(); } - + /// If the StoreManager supports it, decrement the reference count of /// the specified Store object. If the reference count hits 0, the memory /// associated with the object is recycled. @@ -514,7 +508,7 @@ public: // Part of public interface to class. SVal getBindingForFieldOrElementCommon(RegionBindingsConstRef B, const TypedValueRegion *R, QualType Ty); - + SVal getLazyBinding(const SubRegion *LazyBindingRegion, RegionBindingsRef LazyBinding); @@ -656,35 +650,25 @@ protected: RegionBindingsRef B; -private: - GlobalsFilterKind GlobalsFilter; protected: const ClusterBindings *getCluster(const MemRegion *R) { return B.lookup(R); } - /// Returns true if the memory space of the given region is one of the global - /// regions specially included at the start of analysis. - bool isInitiallyIncludedGlobalRegion(const MemRegion *R) { - switch (GlobalsFilter) { - case GFK_None: - return false; - case GFK_SystemOnly: - return isa<GlobalSystemSpaceRegion>(R->getMemorySpace()); - case GFK_All: - return isa<NonStaticGlobalSpaceRegion>(R->getMemorySpace()); - } - - llvm_unreachable("unknown globals filter"); + /// Returns true if all clusters in the given memspace should be initially + /// included in the cluster analysis. Subclasses may provide their + /// own implementation. + bool includeEntireMemorySpace(const MemRegion *Base) { + return false; } public: ClusterAnalysis(RegionStoreManager &rm, ProgramStateManager &StateMgr, - RegionBindingsRef b, GlobalsFilterKind GFK) + RegionBindingsRef b ) : RM(rm), Ctx(StateMgr.getContext()), svalBuilder(StateMgr.getSValBuilder()), - B(b), GlobalsFilter(GFK) {} + B(b) {} RegionBindingsRef getRegionBindings() const { return B; } @@ -702,8 +686,9 @@ public: assert(!Cluster.isEmpty() && "Empty clusters should be removed"); static_cast<DERIVED*>(this)->VisitAddedToCluster(Base, Cluster); - // If this is an interesting global region, add it the work list up front. - if (isInitiallyIncludedGlobalRegion(Base)) + // If the base's memspace should be entirely invalidated, add the cluster + // to the workspace up front. + if (static_cast<DERIVED*>(this)->includeEntireMemorySpace(Base)) AddToWorkList(WorkListElement(Base), &Cluster); } } @@ -716,8 +701,7 @@ public: } bool AddToWorkList(const MemRegion *R) { - const MemRegion *BaseR = R->getBaseRegion(); - return AddToWorkList(WorkListElement(BaseR), getCluster(BaseR)); + return static_cast<DERIVED*>(this)->AddToWorkList(R); } void RunWorkList() { @@ -947,6 +931,7 @@ class invalidateRegionsWorker : public ClusterAnalysis<invalidateRegionsWorker> InvalidatedSymbols &IS; RegionAndSymbolInvalidationTraits &ITraits; StoreManager::InvalidatedRegions *Regions; + GlobalsFilterKind GlobalsFilter; public: invalidateRegionsWorker(RegionStoreManager &rm, ProgramStateManager &stateMgr, @@ -957,14 +942,34 @@ public: RegionAndSymbolInvalidationTraits &ITraitsIn, StoreManager::InvalidatedRegions *r, GlobalsFilterKind GFK) - : ClusterAnalysis<invalidateRegionsWorker>(rm, stateMgr, b, GFK), - Ex(ex), Count(count), LCtx(lctx), IS(is), ITraits(ITraitsIn), Regions(r){} + : ClusterAnalysis<invalidateRegionsWorker>(rm, stateMgr, b), + Ex(ex), Count(count), LCtx(lctx), IS(is), ITraits(ITraitsIn), Regions(r), + GlobalsFilter(GFK) {} void VisitCluster(const MemRegion *baseR, const ClusterBindings *C); void VisitBinding(SVal V); + + using ClusterAnalysis::AddToWorkList; + + bool AddToWorkList(const MemRegion *R); + + /// Returns true if all clusters in the memory space for \p Base should be + /// be invalidated. + bool includeEntireMemorySpace(const MemRegion *Base); + + /// Returns true if the memory space of the given region is one of the global + /// regions specially included at the start of invalidation. + bool isInitiallyIncludedGlobalRegion(const MemRegion *R); }; } +bool invalidateRegionsWorker::AddToWorkList(const MemRegion *R) { + bool doNotInvalidateSuperRegion = ITraits.hasTrait( + R, RegionAndSymbolInvalidationTraits::TK_DoNotInvalidateSuperRegion); + const MemRegion *BaseR = doNotInvalidateSuperRegion ? R : R->getBaseRegion(); + return AddToWorkList(WorkListElement(BaseR), getCluster(BaseR)); +} + void invalidateRegionsWorker::VisitBinding(SVal V) { // A symbol? Mark it touched by the invalidation. if (SymbolRef Sym = V.getAsSymbol()) @@ -993,8 +998,8 @@ void invalidateRegionsWorker::VisitBinding(SVal V) { void invalidateRegionsWorker::VisitCluster(const MemRegion *baseR, const ClusterBindings *C) { - bool PreserveRegionsContents = - ITraits.hasTrait(baseR, + bool PreserveRegionsContents = + ITraits.hasTrait(baseR, RegionAndSymbolInvalidationTraits::TK_PreserveContents); if (C) { @@ -1077,6 +1082,70 @@ void invalidateRegionsWorker::VisitCluster(const MemRegion *baseR, } if (const ArrayType *AT = Ctx.getAsArrayType(T)) { + bool doNotInvalidateSuperRegion = ITraits.hasTrait( + baseR, + RegionAndSymbolInvalidationTraits::TK_DoNotInvalidateSuperRegion); + + if (doNotInvalidateSuperRegion) { + // We are not doing blank invalidation of the whole array region so we + // have to manually invalidate each elements. + Optional<uint64_t> NumElements; + + // Compute lower and upper offsets for region within array. + if (const ConstantArrayType *CAT = dyn_cast<ConstantArrayType>(AT)) + NumElements = CAT->getSize().getZExtValue(); + if (!NumElements) // We are not dealing with a constant size array + goto conjure_default; + QualType ElementTy = AT->getElementType(); + uint64_t ElemSize = Ctx.getTypeSize(ElementTy); + const RegionOffset &RO = baseR->getAsOffset(); + const MemRegion *SuperR = baseR->getBaseRegion(); + if (RO.hasSymbolicOffset()) { + // If base region has a symbolic offset, + // we revert to invalidating the super region. + if (SuperR) + AddToWorkList(SuperR); + goto conjure_default; + } + + uint64_t LowerOffset = RO.getOffset(); + uint64_t UpperOffset = LowerOffset + *NumElements * ElemSize; + bool UpperOverflow = UpperOffset < LowerOffset; + + // Invalidate regions which are within array boundaries, + // or have a symbolic offset. + if (!SuperR) + goto conjure_default; + + const ClusterBindings *C = B.lookup(SuperR); + if (!C) + goto conjure_default; + + for (ClusterBindings::iterator I = C->begin(), E = C->end(); I != E; + ++I) { + const BindingKey &BK = I.getKey(); + Optional<uint64_t> ROffset = + BK.hasSymbolicOffset() ? Optional<uint64_t>() : BK.getOffset(); + + // Check offset is not symbolic and within array's boundaries. + // Handles arrays of 0 elements and of 0-sized elements as well. + if (!ROffset || + (ROffset && + ((*ROffset >= LowerOffset && *ROffset < UpperOffset) || + (UpperOverflow && + (*ROffset >= LowerOffset || *ROffset < UpperOffset)) || + (LowerOffset == UpperOffset && *ROffset == LowerOffset)))) { + B = B.removeBinding(I.getKey()); + // Bound symbolic regions need to be invalidated for dead symbol + // detection. + SVal V = I.getData(); + const MemRegion *R = V.getAsRegion(); + if (R && isa<SymbolicRegion>(R)) + VisitBinding(V); + } + } + } + conjure_default: // Set the default value of the array to conjured symbol. DefinedOrUnknownSVal V = svalBuilder.conjureSymbolVal(baseR, Ex, LCtx, @@ -1091,6 +1160,29 @@ void invalidateRegionsWorker::VisitCluster(const MemRegion *baseR, B = B.addBinding(baseR, BindingKey::Direct, V); } +bool invalidateRegionsWorker::isInitiallyIncludedGlobalRegion( + const MemRegion *R) { + switch (GlobalsFilter) { + case GFK_None: + return false; + case GFK_SystemOnly: + return isa<GlobalSystemSpaceRegion>(R->getMemorySpace()); + case GFK_All: + return isa<NonStaticGlobalSpaceRegion>(R->getMemorySpace()); + } + + llvm_unreachable("unknown globals filter"); +} + +bool invalidateRegionsWorker::includeEntireMemorySpace(const MemRegion *Base) { + if (isInitiallyIncludedGlobalRegion(Base)) + return true; + + const MemSpaceRegion *MemSpace = Base->getMemorySpace(); + return ITraits.hasTrait(MemSpace, + RegionAndSymbolInvalidationTraits::TK_EntireMemSpace); +} + RegionBindingsRef RegionStoreManager::invalidateGlobalRegion(MemRegion::Kind K, const Expr *Ex, @@ -1273,6 +1365,10 @@ SVal RegionStoreManager::getBinding(RegionBindingsConstRef B, Loc L, QualType T) const MemRegion *MR = L.castAs<loc::MemRegionVal>().getRegion(); + if (isa<BlockDataRegion>(MR)) { + return UnknownVal(); + } + if (isa<AllocaRegion>(MR) || isa<SymbolicRegion>(MR) || isa<CodeTextRegion>(MR)) { @@ -1462,7 +1558,7 @@ RegionStoreManager::findLazyBinding(RegionBindingsConstRef B, // through to look for lazy compound value. It is like a field region. Result = findLazyBinding(B, cast<SubRegion>(BaseReg->getSuperRegion()), originalRegion); - + if (Result.second) Result.second = MRMgr.getCXXBaseObjectRegionWithSuper(BaseReg, Result.second); @@ -1508,7 +1604,7 @@ SVal RegionStoreManager::getBindingForElement(RegionBindingsConstRef B, return svalBuilder.makeIntVal(c, T); } } - + // Check for loads from a code text region. For such loads, just give up. if (isa<CodeTextRegion>(superR)) return UnknownVal(); @@ -1520,12 +1616,12 @@ SVal RegionStoreManager::getBindingForElement(RegionBindingsConstRef B, // return *y; // FIXME: This is a hack, and doesn't do anything really intelligent yet. const RegionRawOffset &O = R->getAsArrayOffset(); - + // If we cannot reason about the offset, return an unknown value. if (!O.getRegion()) return UnknownVal(); - - if (const TypedValueRegion *baseR = + + if (const TypedValueRegion *baseR = dyn_cast_or_null<TypedValueRegion>(O.getRegion())) { QualType baseT = baseR->getValueType(); if (baseT->isScalarType()) { @@ -1616,7 +1712,7 @@ SVal RegionStoreManager::getLazyBinding(const SubRegion *LazyBindingRegion, return Result; } - + SVal RegionStoreManager::getBindingForFieldOrElementCommon(RegionBindingsConstRef B, const TypedValueRegion *R, @@ -1670,7 +1766,7 @@ RegionStoreManager::getBindingForFieldOrElementCommon(RegionBindingsConstRef B, if (!index.isConstant()) hasSymbolicIndex = true; } - + // If our super region is a field or element itself, walk up the region // hierarchy to see if there is a default value installed in an ancestor. SR = dyn_cast<SubRegion>(Base); @@ -1680,7 +1776,7 @@ RegionStoreManager::getBindingForFieldOrElementCommon(RegionBindingsConstRef B, if (isa<ElementRegion>(R)) { // Currently we don't reason specially about Clang-style vectors. Check // if superR is a vector and if so return Unknown. - if (const TypedValueRegion *typedSuperR = + if (const TypedValueRegion *typedSuperR = dyn_cast<TypedValueRegion>(R->getSuperRegion())) { if (typedSuperR->getValueType()->isVectorType()) return UnknownVal(); @@ -1807,7 +1903,7 @@ RegionStoreManager::getInterestingValues(nonloc::LazyCompoundVal LCV) { List.insert(List.end(), InnerList.begin(), InnerList.end()); continue; } - + List.push_back(V); } @@ -1844,7 +1940,7 @@ SVal RegionStoreManager::getBindingForArray(RegionBindingsConstRef B, const TypedValueRegion *R) { assert(Ctx.getAsConstantArrayType(R->getValueType()) && "Only constant array types can have compound bindings."); - + return createLazyBinding(B, R); } @@ -2018,11 +2114,11 @@ RegionBindingsRef RegionStoreManager::bindVector(RegionBindingsConstRef B, QualType T = R->getValueType(); assert(T->isVectorType()); const VectorType *VT = T->getAs<VectorType>(); // Use getAs for typedefs. - + // Handle lazy compound values and symbolic values. if (V.getAs<nonloc::LazyCompoundVal>() || V.getAs<nonloc::SymbolVal>()) return bindAggregate(B, R, V); - + // We may get non-CompoundVal accidentally due to imprecise cast logic or // that we are binding symbolic struct value. Kill the field values, and if // the value is symbolic go and bind it as a "default" binding. @@ -2039,7 +2135,7 @@ RegionBindingsRef RegionStoreManager::bindVector(RegionBindingsConstRef B, for ( ; index != numElements ; ++index) { if (VI == VE) break; - + NonLoc Idx = svalBuilder.makeArrayIndex(index); const ElementRegion *ER = MRMgr.getElementRegion(ElemType, Idx, R, Ctx); @@ -2081,7 +2177,7 @@ RegionStoreManager::tryBindSmallStruct(RegionBindingsConstRef B, } RegionBindingsRef NewB = B; - + for (FieldVector::iterator I = Fields.begin(), E = Fields.end(); I != E; ++I){ const FieldRegion *SourceFR = MRMgr.getFieldRegion(*I, LCV.getRegion()); SVal V = getBindingForField(getRegionBindings(LCV.getStore()), SourceFR); @@ -2185,7 +2281,7 @@ public: ProgramStateManager &stateMgr, RegionBindingsRef b, SymbolReaper &symReaper, const StackFrameContext *LCtx) - : ClusterAnalysis<removeDeadBindingsWorker>(rm, stateMgr, b, GFK_None), + : ClusterAnalysis<removeDeadBindingsWorker>(rm, stateMgr, b), SymReaper(symReaper), CurrentLCtx(LCtx) {} // Called by ClusterAnalysis. @@ -2193,11 +2289,20 @@ public: void VisitCluster(const MemRegion *baseR, const ClusterBindings *C); using ClusterAnalysis<removeDeadBindingsWorker>::VisitCluster; + using ClusterAnalysis::AddToWorkList; + + bool AddToWorkList(const MemRegion *R); + bool UpdatePostponed(); void VisitBinding(SVal V); }; } +bool removeDeadBindingsWorker::AddToWorkList(const MemRegion *R) { + const MemRegion *BaseR = R->getBaseRegion(); + return AddToWorkList(WorkListElement(BaseR), getCluster(BaseR)); +} + void removeDeadBindingsWorker::VisitAddedToCluster(const MemRegion *baseR, const ClusterBindings &C) { @@ -2243,8 +2348,12 @@ void removeDeadBindingsWorker::VisitCluster(const MemRegion *baseR, if (const SymbolicRegion *SymR = dyn_cast<SymbolicRegion>(baseR)) SymReaper.markLive(SymR->getSymbol()); - for (ClusterBindings::iterator I = C->begin(), E = C->end(); I != E; ++I) + for (ClusterBindings::iterator I = C->begin(), E = C->end(); I != E; ++I) { + // Element index of a binding key is live. + SymReaper.markElementIndicesLive(I.getKey().getRegion()); + VisitBinding(I.getData()); + } } void removeDeadBindingsWorker::VisitBinding(SVal V) { @@ -2265,7 +2374,8 @@ void removeDeadBindingsWorker::VisitBinding(SVal V) { // If V is a region, then add it to the worklist. if (const MemRegion *R = V.getAsRegion()) { AddToWorkList(R); - + SymReaper.markLive(R); + // All regions captured by a block are also live. if (const BlockDataRegion *BR = dyn_cast<BlockDataRegion>(R)) { BlockDataRegion::referenced_vars_iterator I = BR->referenced_vars_begin(), @@ -2274,7 +2384,7 @@ void removeDeadBindingsWorker::VisitBinding(SVal V) { AddToWorkList(I.getCapturedRegion()); } } - + // Update the set of live symbols. for (SymExpr::symbol_iterator SI = V.symbol_begin(), SE = V.symbol_end(); diff --git a/lib/StaticAnalyzer/Core/SValBuilder.cpp b/lib/StaticAnalyzer/Core/SValBuilder.cpp index 3ed2bde1e4f84..cdae04068e1d1 100644 --- a/lib/StaticAnalyzer/Core/SValBuilder.cpp +++ b/lib/StaticAnalyzer/Core/SValBuilder.cpp @@ -91,10 +91,13 @@ nonloc::ConcreteInt SValBuilder::makeBoolVal(const CXXBoolLiteralExpr *boolean){ return makeTruthVal(boolean->getValue()); } -DefinedOrUnknownSVal +DefinedOrUnknownSVal SValBuilder::getRegionValueSymbolVal(const TypedValueRegion* region) { QualType T = region->getValueType(); + if (T->isNullPtrType()) + return makeZeroVal(T); + if (!SymbolManager::canSymbolicate(T)) return UnknownVal(); @@ -112,6 +115,9 @@ DefinedOrUnknownSVal SValBuilder::conjureSymbolVal(const void *SymbolTag, unsigned Count) { QualType T = Ex->getType(); + if (T->isNullPtrType()) + return makeZeroVal(T); + // Compute the type of the result. If the expression is not an R-value, the // result should be a location. QualType ExType = Ex->getType(); @@ -126,6 +132,9 @@ DefinedOrUnknownSVal SValBuilder::conjureSymbolVal(const void *symbolTag, const LocationContext *LCtx, QualType type, unsigned count) { + if (type->isNullPtrType()) + return makeZeroVal(type); + if (!SymbolManager::canSymbolicate(type)) return UnknownVal(); @@ -142,14 +151,17 @@ DefinedOrUnknownSVal SValBuilder::conjureSymbolVal(const Stmt *stmt, const LocationContext *LCtx, QualType type, unsigned visitCount) { + if (type->isNullPtrType()) + return makeZeroVal(type); + if (!SymbolManager::canSymbolicate(type)) return UnknownVal(); SymbolRef sym = SymMgr.conjureSymbol(stmt, LCtx, type, visitCount); - + if (Loc::isLocType(type)) return loc::MemRegionVal(MemMgr.getSymbolicRegion(sym)); - + return nonloc::SymbolVal(sym); } @@ -160,6 +172,8 @@ SValBuilder::getConjuredHeapSymbolVal(const Expr *E, QualType T = E->getType(); assert(Loc::isLocType(T)); assert(SymbolManager::canSymbolicate(T)); + if (T->isNullPtrType()) + return makeZeroVal(T); SymbolRef sym = SymMgr.conjureSymbol(E, LCtx, T, VisitCount); return loc::MemRegionVal(MemMgr.getSymbolicHeapRegion(sym)); @@ -185,6 +199,9 @@ SValBuilder::getDerivedRegionValueSymbolVal(SymbolRef parentSymbol, const TypedValueRegion *region) { QualType T = region->getValueType(); + if (T->isNullPtrType()) + return makeZeroVal(T); + if (!SymbolManager::canSymbolicate(T)) return UnknownVal(); @@ -259,6 +276,11 @@ Optional<SVal> SValBuilder::getConstantVal(const Expr *E) { case Stmt::CXXBoolLiteralExprClass: return makeBoolVal(cast<CXXBoolLiteralExpr>(E)); + case Stmt::TypeTraitExprClass: { + const TypeTraitExpr *TE = cast<TypeTraitExpr>(E); + return makeTruthVal(TE->getValue(), TE->getType()); + } + case Stmt::IntegerLiteralClass: return makeIntVal(cast<IntegerLiteral>(E)); @@ -270,11 +292,17 @@ Optional<SVal> SValBuilder::getConstantVal(const Expr *E) { case Stmt::ImplicitCastExprClass: { const CastExpr *CE = cast<CastExpr>(E); - if (CE->getCastKind() == CK_ArrayToPointerDecay) { - Optional<SVal> ArrayVal = getConstantVal(CE->getSubExpr()); - if (!ArrayVal) + switch (CE->getCastKind()) { + default: + break; + case CK_ArrayToPointerDecay: + case CK_BitCast: { + const Expr *SE = CE->getSubExpr(); + Optional<SVal> Val = getConstantVal(SE); + if (!Val) return None; - return evalCast(*ArrayVal, CE->getType(), CE->getSubExpr()->getType()); + return evalCast(*Val, CE->getType(), SE->getType()); + } } // FALLTHROUGH } @@ -307,7 +335,7 @@ SVal SValBuilder::makeSymExprValNN(ProgramStateRef State, QualType ResultTy) { if (!State->isTainted(RHS) && !State->isTainted(LHS)) return UnknownVal(); - + const SymExpr *symLHS = LHS.getAsSymExpr(); const SymExpr *symRHS = RHS.getAsSymExpr(); // TODO: When the Max Complexity is reached, we should conjure a symbol @@ -430,7 +458,7 @@ SVal SValBuilder::evalCast(SVal val, QualType castTy, QualType originalTy) { if (shouldBeModeledWithNoOp(Context, Context.getPointerType(castTy), Context.getPointerType(originalTy))) return val; - + // Check for casts from pointers to integers. if (castTy->isIntegralOrEnumerationType() && Loc::isLocType(originalTy)) return evalCastFromLoc(val.castAs<Loc>(), castTy); diff --git a/lib/StaticAnalyzer/Core/SimpleConstraintManager.cpp b/lib/StaticAnalyzer/Core/SimpleConstraintManager.cpp index 35930e47f82a0..4051242434ec9 100644 --- a/lib/StaticAnalyzer/Core/SimpleConstraintManager.cpp +++ b/lib/StaticAnalyzer/Core/SimpleConstraintManager.cpp @@ -190,6 +190,42 @@ ProgramStateRef SimpleConstraintManager::assumeAux(ProgramStateRef state, } // end switch } +ProgramStateRef SimpleConstraintManager::assumeWithinInclusiveRange( + ProgramStateRef State, NonLoc Value, const llvm::APSInt &From, + const llvm::APSInt &To, bool InRange) { + + assert(From.isUnsigned() == To.isUnsigned() && + From.getBitWidth() == To.getBitWidth() && + "Values should have same types!"); + + if (!canReasonAbout(Value)) { + // Just add the constraint to the expression without trying to simplify. + SymbolRef Sym = Value.getAsSymExpr(); + assert(Sym); + return assumeSymWithinInclusiveRange(State, Sym, From, To, InRange); + } + + switch (Value.getSubKind()) { + default: + llvm_unreachable("'assumeWithinInclusiveRange' is not implemented" + "for this NonLoc"); + + case nonloc::LocAsIntegerKind: + case nonloc::SymbolValKind: { + if (SymbolRef Sym = Value.getAsSymbol()) + return assumeSymWithinInclusiveRange(State, Sym, From, To, InRange); + return State; + } // end switch + + case nonloc::ConcreteIntKind: { + const llvm::APSInt &IntVal = Value.castAs<nonloc::ConcreteInt>().getValue(); + bool IsInRange = IntVal >= From && IntVal <= To; + bool isFeasible = (IsInRange == InRange); + return isFeasible ? State : nullptr; + } + } // end switch +} + static void computeAdjustment(SymbolRef &Sym, llvm::APSInt &Adjustment) { // Is it a "($sym+constant1)" expression? if (const SymIntExpr *SE = dyn_cast<SymIntExpr>(Sym)) { @@ -262,6 +298,37 @@ ProgramStateRef SimpleConstraintManager::assumeSymRel(ProgramStateRef state, } // end switch } +ProgramStateRef +SimpleConstraintManager::assumeSymWithinInclusiveRange(ProgramStateRef State, + SymbolRef Sym, + const llvm::APSInt &From, + const llvm::APSInt &To, + bool InRange) { + // Get the type used for calculating wraparound. + BasicValueFactory &BVF = getBasicVals(); + APSIntType WraparoundType = BVF.getAPSIntType(Sym->getType()); + + llvm::APSInt Adjustment = WraparoundType.getZeroValue(); + SymbolRef AdjustedSym = Sym; + computeAdjustment(AdjustedSym, Adjustment); + + // Convert the right-hand side integer as necessary. + APSIntType ComparisonType = std::max(WraparoundType, APSIntType(From)); + llvm::APSInt ConvertedFrom = ComparisonType.convert(From); + llvm::APSInt ConvertedTo = ComparisonType.convert(To); + + // Prefer unsigned comparisons. + if (ComparisonType.getBitWidth() == WraparoundType.getBitWidth() && + ComparisonType.isUnsigned() && !WraparoundType.isUnsigned()) + Adjustment.setIsSigned(false); + + if (InRange) + return assumeSymbolWithinInclusiveRange(State, AdjustedSym, ConvertedFrom, + ConvertedTo, Adjustment); + return assumeSymbolOutOfInclusiveRange(State, AdjustedSym, ConvertedFrom, + ConvertedTo, Adjustment); +} + } // end of namespace ento } // end of namespace clang diff --git a/lib/StaticAnalyzer/Core/SimpleConstraintManager.h b/lib/StaticAnalyzer/Core/SimpleConstraintManager.h index 135cd4ef86492..b26bc9486110f 100644 --- a/lib/StaticAnalyzer/Core/SimpleConstraintManager.h +++ b/lib/StaticAnalyzer/Core/SimpleConstraintManager.h @@ -38,11 +38,24 @@ public: ProgramStateRef assume(ProgramStateRef state, NonLoc Cond, bool Assumption); + ProgramStateRef assumeWithinInclusiveRange(ProgramStateRef State, + NonLoc Value, + const llvm::APSInt &From, + const llvm::APSInt &To, + bool InRange) override; + ProgramStateRef assumeSymRel(ProgramStateRef state, const SymExpr *LHS, BinaryOperator::Opcode op, const llvm::APSInt& Int); + ProgramStateRef assumeSymWithinInclusiveRange(ProgramStateRef State, + SymbolRef Sym, + const llvm::APSInt &From, + const llvm::APSInt &To, + bool InRange); + + protected: //===------------------------------------------------------------------===// @@ -75,6 +88,14 @@ protected: const llvm::APSInt& V, const llvm::APSInt& Adjustment) = 0; + + virtual ProgramStateRef assumeSymbolWithinInclusiveRange( + ProgramStateRef State, SymbolRef Sym, const llvm::APSInt &From, + const llvm::APSInt &To, const llvm::APSInt &Adjustment) = 0; + + virtual ProgramStateRef assumeSymbolOutOfInclusiveRange( + ProgramStateRef state, SymbolRef Sym, const llvm::APSInt &From, + const llvm::APSInt &To, const llvm::APSInt &Adjustment) = 0; //===------------------------------------------------------------------===// // Internal implementation. //===------------------------------------------------------------------===// diff --git a/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp b/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp index b3cab87c80806..a704ce224554f 100644 --- a/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp +++ b/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp @@ -638,7 +638,7 @@ SVal SimpleSValBuilder::evalBinOpLL(ProgramStateRef state, // on the ABI). // FIXME: we can probably do a comparison against other MemRegions, though. // FIXME: is there a way to tell if two labels refer to the same location? - return UnknownVal(); + return UnknownVal(); case loc::ConcreteIntKind: { // If one of the operands is a symbol and the other is a constant, @@ -863,7 +863,7 @@ SVal SimpleSValBuilder::evalBinOpLN(ProgramStateRef state, // Special case: rhs is a zero constant. if (rhs.isZeroConstant()) return lhs; - + // We are dealing with pointer arithmetic. // Handle pointer arithmetic on constant values. @@ -880,7 +880,7 @@ SVal SimpleSValBuilder::evalBinOpLN(ProgramStateRef state, // Offset the increment by the pointer size. llvm::APSInt Multiplicand(rightI.getBitWidth(), /* isUnsigned */ true); rightI *= Multiplicand; - + // Compute the adjusted pointer. switch (op) { case BO_Add: @@ -911,8 +911,9 @@ SVal SimpleSValBuilder::evalBinOpLN(ProgramStateRef state, elementType = elemReg->getElementType(); } else if (isa<SubRegion>(region)) { + assert(op == BO_Add || op == BO_Sub); + index = (op == BO_Add) ? rhs : evalMinus(rhs); superR = region; - index = rhs; if (resultTy->isAnyPointerType()) elementType = resultTy->getPointeeType(); } @@ -922,7 +923,7 @@ SVal SimpleSValBuilder::evalBinOpLN(ProgramStateRef state, superR, getContext())); } } - return UnknownVal(); + return UnknownVal(); } const llvm::APSInt *SimpleSValBuilder::getKnownValue(ProgramStateRef state, diff --git a/lib/StaticAnalyzer/Core/Store.cpp b/lib/StaticAnalyzer/Core/Store.cpp index 99ec1e7043406..7cdb55a597821 100644 --- a/lib/StaticAnalyzer/Core/Store.cpp +++ b/lib/StaticAnalyzer/Core/Store.cpp @@ -52,7 +52,7 @@ StoreRef StoreManager::BindDefault(Store store, const MemRegion *R, SVal V) { return StoreRef(store, *this); } -const ElementRegion *StoreManager::GetElementZeroRegion(const MemRegion *R, +const ElementRegion *StoreManager::GetElementZeroRegion(const MemRegion *R, QualType T) { NonLoc idx = svalBuilder.makeZeroArrayIndex(); assert(!T.isNull()); @@ -366,22 +366,22 @@ SVal StoreManager::evalDynamicCast(SVal Base, QualType TargetType, /// as another region. SVal StoreManager::CastRetrievedVal(SVal V, const TypedValueRegion *R, QualType castTy, bool performTestOnly) { - + if (castTy.isNull() || V.isUnknownOrUndef()) return V; - + ASTContext &Ctx = svalBuilder.getContext(); - if (performTestOnly) { + if (performTestOnly) { // Automatically translate references to pointers. QualType T = R->getValueType(); if (const ReferenceType *RT = T->getAs<ReferenceType>()) T = Ctx.getPointerType(RT->getPointeeType()); - + assert(svalBuilder.getContext().hasSameUnqualifiedType(castTy, T)); return V; } - + return svalBuilder.dispatchCast(V, castTy); } @@ -424,7 +424,7 @@ SVal StoreManager::getLValueIvar(const ObjCIvarDecl *decl, SVal base) { return getLValueFieldOrIvar(decl, base); } -SVal StoreManager::getLValueElement(QualType elementType, NonLoc Offset, +SVal StoreManager::getLValueElement(QualType elementType, NonLoc Offset, SVal Base) { // If the base is an unknown or undefined value, just return it back. diff --git a/lib/StaticAnalyzer/Core/SymbolManager.cpp b/lib/StaticAnalyzer/Core/SymbolManager.cpp index cca0461a47486..99b2e147cb494 100644 --- a/lib/StaticAnalyzer/Core/SymbolManager.cpp +++ b/lib/StaticAnalyzer/Core/SymbolManager.cpp @@ -391,6 +391,18 @@ void SymbolReaper::markLive(SymbolRef sym) { void SymbolReaper::markLive(const MemRegion *region) { RegionRoots.insert(region); + markElementIndicesLive(region); +} + +void SymbolReaper::markElementIndicesLive(const MemRegion *region) { + for (auto SR = dyn_cast<SubRegion>(region); SR; + SR = dyn_cast<SubRegion>(SR->getSuperRegion())) { + if (auto ER = dyn_cast<ElementRegion>(SR)) { + SVal Idx = ER->getIndex(); + for (auto SI = Idx.symbol_begin(), SE = Idx.symbol_end(); SI != SE; ++SI) + markLive(*SI); + } + } } void SymbolReaper::markInUse(SymbolRef sym) { @@ -409,7 +421,7 @@ bool SymbolReaper::maybeDead(SymbolRef sym) { bool SymbolReaper::isLiveRegion(const MemRegion *MR) { if (RegionRoots.count(MR)) return true; - + MR = MR->getBaseRegion(); if (const SymbolicRegion *SR = dyn_cast<SymbolicRegion>(MR)) @@ -442,9 +454,9 @@ bool SymbolReaper::isLive(SymbolRef sym) { markDependentsLive(sym); return true; } - + bool KnownLive; - + switch (sym->getKind()) { case SymExpr::RegionValueKind: KnownLive = isLiveRegion(cast<SymbolRegionValue>(sym)->getRegion()); @@ -525,7 +537,7 @@ bool SymbolReaper::isLive(const VarRegion *VR, bool includeStoreBindings) const{ if (!includeStoreBindings) return false; - + unsigned &cachedQuery = const_cast<SymbolReaper*>(this)->includedRegionCache[VR]; @@ -535,16 +547,14 @@ bool SymbolReaper::isLive(const VarRegion *VR, bool includeStoreBindings) const{ // Query the store to see if the region occurs in any live bindings. if (Store store = reapedStore.getStore()) { - bool hasRegion = + bool hasRegion = reapedStore.getStoreManager().includedInBindings(store, VR); cachedQuery = hasRegion ? 1 : 2; return hasRegion; } - + return false; } return VarContext->isParentOf(CurrentContext); } - -SymbolVisitor::~SymbolVisitor() {} |