diff options
Diffstat (limited to 'lib/StaticAnalyzer/Core/ExprEngine.cpp')
-rw-r--r-- | lib/StaticAnalyzer/Core/ExprEngine.cpp | 1088 |
1 files changed, 676 insertions, 412 deletions
diff --git a/lib/StaticAnalyzer/Core/ExprEngine.cpp b/lib/StaticAnalyzer/Core/ExprEngine.cpp index 3be37e7ae301..188316c096e3 100644 --- a/lib/StaticAnalyzer/Core/ExprEngine.cpp +++ b/lib/StaticAnalyzer/Core/ExprEngine.cpp @@ -1,4 +1,4 @@ -//=-- ExprEngine.cpp - Path-Sensitive Expression-Level Dataflow ---*- C++ -*-= +//===- ExprEngine.cpp - Path-Sensitive Expression-Level Dataflow ----------===// // // The LLVM Compiler Infrastructure // @@ -15,31 +15,75 @@ #include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h" #include "PrettyStackTraceLocationContext.h" -#include "clang/AST/CharUnits.h" +#include "clang/AST/ASTContext.h" +#include "clang/AST/Decl.h" +#include "clang/AST/DeclBase.h" +#include "clang/AST/DeclCXX.h" +#include "clang/AST/DeclObjC.h" +#include "clang/AST/Expr.h" +#include "clang/AST/ExprCXX.h" +#include "clang/AST/ExprObjC.h" #include "clang/AST/ParentMap.h" -#include "clang/Analysis/CFGStmtMap.h" +#include "clang/AST/PrettyPrinter.h" +#include "clang/AST/Stmt.h" #include "clang/AST/StmtCXX.h" #include "clang/AST/StmtObjC.h" -#include "clang/Basic/Builtins.h" +#include "clang/AST/Type.h" +#include "clang/Analysis/AnalysisDeclContext.h" +#include "clang/Analysis/CFG.h" +#include "clang/Analysis/ConstructionContext.h" +#include "clang/Analysis/ProgramPoint.h" +#include "clang/Basic/IdentifierTable.h" +#include "clang/Basic/LLVM.h" +#include "clang/Basic/LangOptions.h" #include "clang/Basic/PrettyStackTrace.h" +#include "clang/Basic/SourceLocation.h" #include "clang/Basic/SourceManager.h" +#include "clang/Basic/Specifiers.h" +#include "clang/StaticAnalyzer/Core/AnalyzerOptions.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" #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 "clang/StaticAnalyzer/Core/PathSensitive/ConstraintManager.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CoreEngine.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h" #include "clang/StaticAnalyzer/Core/PathSensitive/LoopUnrolling.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/LoopWidening.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState_Fwd.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/Store.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/SymExpr.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h" +#include "llvm/ADT/APSInt.h" +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/ImmutableMap.h" +#include "llvm/ADT/ImmutableSet.h" +#include "llvm/ADT/Optional.h" +#include "llvm/ADT/SmallVector.h" #include "llvm/ADT/Statistic.h" +#include "llvm/Support/Casting.h" +#include "llvm/Support/Compiler.h" +#include "llvm/Support/DOTGraphTraits.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/GraphWriter.h" #include "llvm/Support/SaveAndRestore.h" #include "llvm/Support/raw_ostream.h" - -#ifndef NDEBUG -#include "llvm/Support/GraphWriter.h" -#endif +#include <cassert> +#include <cstdint> +#include <memory> +#include <string> +#include <tuple> +#include <utility> +#include <vector> using namespace clang; using namespace ento; -using llvm::APSInt; #define DEBUG_TYPE "ExprEngine" @@ -54,14 +98,100 @@ STATISTIC(NumMaxBlockCountReachedInInlined, STATISTIC(NumTimesRetriedWithoutInlining, "The # of times we re-evaluated a call without inlining"); -typedef std::pair<const CXXBindTemporaryExpr *, const StackFrameContext *> - CXXBindTemporaryContext; -// Keeps track of whether CXXBindTemporaryExpr nodes have been evaluated. -// The StackFrameContext assures that nested calls due to inlined recursive -// functions do not interfere. -REGISTER_TRAIT_WITH_PROGRAMSTATE(InitializedTemporariesSet, - llvm::ImmutableSet<CXXBindTemporaryContext>) +//===----------------------------------------------------------------------===// +// Internal program state traits. +//===----------------------------------------------------------------------===// + +// When modeling a C++ constructor, for a variety of reasons we need to track +// the location of the object for the duration of its ConstructionContext. +// ObjectsUnderConstruction maps statements within the construction context +// to the object's location, so that on every such statement the location +// could have been retrieved. + +/// ConstructedObjectKey is used for being able to find the path-sensitive +/// memory region of a freshly constructed object while modeling the AST node +/// that syntactically represents the object that is being constructed. +/// Semantics of such nodes may sometimes require access to the region that's +/// not otherwise present in the program state, or to the very fact that +/// the construction context was present and contained references to these +/// AST nodes. +class ConstructedObjectKey { + typedef std::pair< + llvm::PointerUnion<const Stmt *, const CXXCtorInitializer *>, + const LocationContext *> ConstructedObjectKeyImpl; + + ConstructedObjectKeyImpl Impl; + + const void *getAnyASTNodePtr() const { + if (const Stmt *S = getStmt()) + return S; + else + return getCXXCtorInitializer(); + } + +public: + ConstructedObjectKey( + llvm::PointerUnion<const Stmt *, const CXXCtorInitializer *> P, + const LocationContext *LC) + : Impl(P, LC) { + // This is the full list of statements that require additional actions when + // encountered. This list may be expanded when new actions are implemented. + assert(getCXXCtorInitializer() || isa<DeclStmt>(getStmt()) || + isa<CXXNewExpr>(getStmt()) || isa<CXXBindTemporaryExpr>(getStmt()) || + isa<MaterializeTemporaryExpr>(getStmt()) || + isa<CXXConstructExpr>(getStmt())); + } + + const Stmt *getStmt() const { + return Impl.first.dyn_cast<const Stmt *>(); + } + + const CXXCtorInitializer *getCXXCtorInitializer() const { + return Impl.first.dyn_cast<const CXXCtorInitializer *>(); + } + + const LocationContext *getLocationContext() const { + return Impl.second; + } + + void print(llvm::raw_ostream &OS, PrinterHelper *Helper, PrintingPolicy &PP) { + OS << '(' << getLocationContext() << ',' << getAnyASTNodePtr() << ") "; + if (const Stmt *S = getStmt()) { + S->printPretty(OS, Helper, PP); + } else { + const CXXCtorInitializer *I = getCXXCtorInitializer(); + OS << I->getAnyMember()->getNameAsString(); + } + } + + void Profile(llvm::FoldingSetNodeID &ID) const { + ID.AddPointer(Impl.first.getOpaqueValue()); + ID.AddPointer(Impl.second); + } + + bool operator==(const ConstructedObjectKey &RHS) const { + return Impl == RHS.Impl; + } + + bool operator<(const ConstructedObjectKey &RHS) const { + return Impl < RHS.Impl; + } +}; + +typedef llvm::ImmutableMap<ConstructedObjectKey, SVal> + ObjectsUnderConstructionMap; +REGISTER_TRAIT_WITH_PROGRAMSTATE(ObjectsUnderConstruction, + ObjectsUnderConstructionMap) + +// Additionally, track a set of destructors that correspond to elided +// constructors when copy elision occurs. +typedef std::pair<const CXXBindTemporaryExpr *, const LocationContext *> + ElidedDestructorItem; +typedef llvm::ImmutableSet<ElidedDestructorItem> + ElidedDestructorSet; +REGISTER_TRAIT_WITH_PROGRAMSTATE(ElidedDestructors, + ElidedDestructorSet) //===----------------------------------------------------------------------===// // Engine construction and deletion. @@ -69,25 +199,21 @@ REGISTER_TRAIT_WITH_PROGRAMSTATE(InitializedTemporariesSet, static const char* TagProviderName = "ExprEngine"; -ExprEngine::ExprEngine(AnalysisManager &mgr, bool gcEnabled, +ExprEngine::ExprEngine(cross_tu::CrossTranslationUnitContext &CTU, + AnalysisManager &mgr, bool gcEnabled, SetOfConstDecls *VisitedCalleesIn, FunctionSummariesTy *FS, InliningModes HowToInlineIn) - : AMgr(mgr), - AnalysisDeclContexts(mgr.getAnalysisDeclContextManager()), - Engine(*this, FS), - G(Engine.getGraph()), - StateMgr(getContext(), mgr.getStoreManagerCreator(), - mgr.getConstraintManagerCreator(), G.getAllocator(), - this), - SymMgr(StateMgr.getSymbolManager()), - svalBuilder(StateMgr.getSValBuilder()), - currStmtIdx(0), currBldrCtx(nullptr), - ObjCNoRet(mgr.getASTContext()), - ObjCGCEnabled(gcEnabled), BR(mgr, *this), - VisitedCallees(VisitedCalleesIn), - HowToInline(HowToInlineIn) -{ + : CTU(CTU), AMgr(mgr), + AnalysisDeclContexts(mgr.getAnalysisDeclContextManager()), + Engine(*this, FS, mgr.getAnalyzerOptions()), G(Engine.getGraph()), + StateMgr(getContext(), mgr.getStoreManagerCreator(), + mgr.getConstraintManagerCreator(), G.getAllocator(), + this), + SymMgr(StateMgr.getSymbolManager()), + svalBuilder(StateMgr.getSValBuilder()), ObjCNoRet(mgr.getASTContext()), + ObjCGCEnabled(gcEnabled), BR(mgr, *this), + VisitedCallees(VisitedCalleesIn), HowToInline(HowToInlineIn) { unsigned TrimInterval = mgr.options.getGraphTrimInterval(); if (TrimInterval != 0) { // Enable eager node reclaimation when constructing the ExplodedGraph. @@ -111,8 +237,7 @@ ProgramStateRef ExprEngine::getInitialState(const LocationContext *InitLoc) { // FIXME: It would be nice if we had a more general mechanism to add // such preconditions. Some day. do { - - if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(D)) { + if (const auto *FD = dyn_cast<FunctionDecl>(D)) { // Precondition: the first argument of 'main' is an integer guaranteed // to be > 0. const IdentifierInfo *II = FD->getIdentifier(); @@ -121,7 +246,7 @@ ProgramStateRef ExprEngine::getInitialState(const LocationContext *InitLoc) { const ParmVarDecl *PD = FD->getParamDecl(0); QualType T = PD->getType(); - const BuiltinType *BT = dyn_cast<BuiltinType>(T); + const auto *BT = dyn_cast<BuiltinType>(T); if (!BT || !BT->isInteger()) break; @@ -145,9 +270,9 @@ ProgramStateRef ExprEngine::getInitialState(const LocationContext *InitLoc) { } break; } - while (0); + while (false); - if (const ObjCMethodDecl *MD = dyn_cast<ObjCMethodDecl>(D)) { + if (const auto *MD = dyn_cast<ObjCMethodDecl>(D)) { // Precondition: 'self' is always non-null upon entry to an Objective-C // method. const ImplicitParamDecl *SelfD = MD->getSelfDecl(); @@ -161,12 +286,12 @@ ProgramStateRef ExprEngine::getInitialState(const LocationContext *InitLoc) { } } - if (const CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(D)) { + if (const auto *MD = dyn_cast<CXXMethodDecl>(D)) { if (!MD->isStatic()) { // Precondition: 'this' is always non-null upon entry to the // top-level function. This is our starting assumption for // analyzing an "open" program. - const StackFrameContext *SFC = InitLoc->getCurrentStackFrame(); + const StackFrameContext *SFC = InitLoc->getStackFrame(); if (SFC->getParent() == nullptr) { loc::MemRegionVal L = svalBuilder.getCXXThis(MD, SFC); SVal V = state->getSVal(L); @@ -237,17 +362,30 @@ ExprEngine::createTemporaryRegionIfNeeded(ProgramStateRef State, const Expr *Init = InitWithAdjustments->skipRValueSubobjectAdjustments( CommaLHSs, Adjustments); + // Take the region for Init, i.e. for the whole object. If we do not remember + // the region in which the object originally was constructed, come up with + // a new temporary region out of thin air and copy the contents of the object + // (which are currently present in the Environment, because Init is an rvalue) + // into that region. This is not correct, but it is better than nothing. const TypedValueRegion *TR = nullptr; - if (const MaterializeTemporaryExpr *MT = - dyn_cast<MaterializeTemporaryExpr>(Result)) { - StorageDuration SD = MT->getStorageDuration(); - // If this object is bound to a reference with static storage duration, we - // put it in a different region to prevent "address leakage" warnings. - if (SD == SD_Static || SD == SD_Thread) - TR = MRMgr.getCXXStaticTempObjectRegion(Init); - } - if (!TR) + if (const auto *MT = dyn_cast<MaterializeTemporaryExpr>(Result)) { + if (Optional<SVal> V = getObjectUnderConstruction(State, MT, LC)) { + State = finishObjectConstruction(State, MT, LC); + State = State->BindExpr(Result, LC, *V); + return State; + } else { + StorageDuration SD = MT->getStorageDuration(); + // If this object is bound to a reference with static storage duration, we + // put it in a different region to prevent "address leakage" warnings. + if (SD == SD_Static || SD == SD_Thread) { + TR = MRMgr.getCXXStaticTempObjectRegion(Init); + } else { + TR = MRMgr.getCXXTempObjectRegion(Init, LC); + } + } + } else { TR = MRMgr.getCXXTempObjectRegion(Init, LC); + } SVal Reg = loc::MemRegionVal(TR); SVal BaseReg = Reg; @@ -264,7 +402,9 @@ ExprEngine::createTemporaryRegionIfNeeded(ProgramStateRef State, break; case SubobjectAdjustment::MemberPointerAdjustment: // FIXME: Unimplemented. - State = State->bindDefault(Reg, UnknownVal(), LC); + State = State->invalidateRegions(Reg, InitWithAdjustments, + currBldrCtx->blockCount(), LC, true, + nullptr, nullptr, nullptr); return State; } } @@ -283,7 +423,8 @@ ExprEngine::createTemporaryRegionIfNeeded(ProgramStateRef State, currBldrCtx->blockCount()); State = State->bindLoc(BaseReg.castAs<Loc>(), InitVal, LC, false); - // Then we'd need to take the value that certainly exists and bind it over. + // Then we'd need to take the value that certainly exists and bind it + // over. if (InitValWithAdjustments.isUnknown()) { // Try to recover some path sensitivity in case we couldn't // compute the value. @@ -308,6 +449,79 @@ ExprEngine::createTemporaryRegionIfNeeded(ProgramStateRef State, return State; } +ProgramStateRef ExprEngine::addObjectUnderConstruction( + ProgramStateRef State, + llvm::PointerUnion<const Stmt *, const CXXCtorInitializer *> P, + const LocationContext *LC, SVal V) { + ConstructedObjectKey Key(P, LC->getStackFrame()); + // FIXME: Currently the state might already contain the marker due to + // incorrect handling of temporaries bound to default parameters. + assert(!State->get<ObjectsUnderConstruction>(Key) || + isa<CXXBindTemporaryExpr>(Key.getStmt())); + return State->set<ObjectsUnderConstruction>(Key, V); +} + +Optional<SVal> ExprEngine::getObjectUnderConstruction( + ProgramStateRef State, + llvm::PointerUnion<const Stmt *, const CXXCtorInitializer *> P, + const LocationContext *LC) { + ConstructedObjectKey Key(P, LC->getStackFrame()); + return Optional<SVal>::create(State->get<ObjectsUnderConstruction>(Key)); +} + +ProgramStateRef ExprEngine::finishObjectConstruction( + ProgramStateRef State, + llvm::PointerUnion<const Stmt *, const CXXCtorInitializer *> P, + const LocationContext *LC) { + ConstructedObjectKey Key(P, LC->getStackFrame()); + assert(State->contains<ObjectsUnderConstruction>(Key)); + return State->remove<ObjectsUnderConstruction>(Key); +} + +ProgramStateRef ExprEngine::elideDestructor(ProgramStateRef State, + const CXXBindTemporaryExpr *BTE, + const LocationContext *LC) { + ElidedDestructorItem I(BTE, LC); + assert(!State->contains<ElidedDestructors>(I)); + return State->add<ElidedDestructors>(I); +} + +ProgramStateRef +ExprEngine::cleanupElidedDestructor(ProgramStateRef State, + const CXXBindTemporaryExpr *BTE, + const LocationContext *LC) { + ElidedDestructorItem I(BTE, LC); + assert(State->contains<ElidedDestructors>(I)); + return State->remove<ElidedDestructors>(I); +} + +bool ExprEngine::isDestructorElided(ProgramStateRef State, + const CXXBindTemporaryExpr *BTE, + const LocationContext *LC) { + ElidedDestructorItem I(BTE, LC); + return State->contains<ElidedDestructors>(I); +} + +bool ExprEngine::areAllObjectsFullyConstructed(ProgramStateRef State, + const LocationContext *FromLC, + const LocationContext *ToLC) { + const LocationContext *LC = FromLC; + while (LC != ToLC) { + assert(LC && "ToLC must be a parent of FromLC!"); + for (auto I : State->get<ObjectsUnderConstruction>()) + if (I.first.getLocationContext() == LC) + return false; + + for (auto I: State->get<ElidedDestructors>()) + if (I.second == LC) + return false; + + LC = LC->getParent(); + } + return true; +} + + //===----------------------------------------------------------------------===// // Top-level transfer function logic (Dispatcher). //===----------------------------------------------------------------------===// @@ -331,8 +545,44 @@ ExprEngine::processRegionChanges(ProgramStateRef state, LCtx, Call); } +static void printObjectsUnderConstructionForContext(raw_ostream &Out, + ProgramStateRef State, + const char *NL, + const char *Sep, + const LocationContext *LC) { + PrintingPolicy PP = + LC->getAnalysisDeclContext()->getASTContext().getPrintingPolicy(); + for (auto I : State->get<ObjectsUnderConstruction>()) { + ConstructedObjectKey Key = I.first; + SVal Value = I.second; + if (Key.getLocationContext() != LC) + continue; + Key.print(Out, nullptr, PP); + Out << " : " << Value << NL; + } + + for (auto I : State->get<ElidedDestructors>()) { + if (I.second != LC) + continue; + Out << '(' << I.second << ',' << (const void *)I.first << ") "; + I.first->printPretty(Out, nullptr, PP); + Out << " : (constructor elided)" << NL; + } +} + void ExprEngine::printState(raw_ostream &Out, ProgramStateRef State, - const char *NL, const char *Sep) { + const char *NL, const char *Sep, + const LocationContext *LCtx) { + if (LCtx) { + if (!State->get<ObjectsUnderConstruction>().isEmpty()) { + Out << Sep << "Objects under construction:" << NL; + + LCtx->dumpStack(Out, "", NL, Sep, [&](const LocationContext *LC) { + printObjectsUnderConstructionForContext(Out, State, NL, Sep, LC); + }); + } + } + getCheckerManager().runCheckersForPrintState(Out, State, NL, Sep); } @@ -348,10 +598,12 @@ void ExprEngine::processCFGElement(const CFGElement E, ExplodedNode *Pred, switch (E.getKind()) { case CFGElement::Statement: - ProcessStmt(const_cast<Stmt*>(E.castAs<CFGStmt>().getStmt()), Pred); + case CFGElement::Constructor: + case CFGElement::CXXRecordTypedCall: + ProcessStmt(E.castAs<CFGStmt>().getStmt(), Pred); return; case CFGElement::Initializer: - ProcessInitializer(E.castAs<CFGInitializer>().getInitializer(), Pred); + ProcessInitializer(E.castAs<CFGInitializer>(), Pred); return; case CFGElement::NewAllocator: ProcessNewAllocator(E.castAs<CFGNewAllocator>().getAllocatorExpr(), @@ -368,15 +620,16 @@ void ExprEngine::processCFGElement(const CFGElement E, ExplodedNode *Pred, ProcessLoopExit(E.castAs<CFGLoopExit>().getLoopStmt(), Pred); return; case CFGElement::LifetimeEnds: + case CFGElement::ScopeBegin: + case CFGElement::ScopeEnd: return; } } static bool shouldRemoveDeadBindings(AnalysisManager &AMgr, - const CFGStmt S, + const Stmt *S, const ExplodedNode *Pred, const LocationContext *LC) { - // Are we never purging state values? if (AMgr.options.AnalysisPurgeOpt == PurgeNone) return false; @@ -386,17 +639,17 @@ static bool shouldRemoveDeadBindings(AnalysisManager &AMgr, return true; // Is this on a non-expression? - if (!isa<Expr>(S.getStmt())) + if (!isa<Expr>(S)) return true; // Run before processing a call. - if (CallEvent::isCallStmt(S.getStmt())) + if (CallEvent::isCallStmt(S)) return true; // Is this an expression that is consumed by another expression? If so, // postpone cleaning out the state. ParentMap &PM = LC->getAnalysisDeclContext()->getParentMap(); - return !PM.isConsumedExpr(cast<Expr>(S.getStmt())); + return !PM.isConsumedExpr(cast<Expr>(S)); } void ExprEngine::removeDead(ExplodedNode *Pred, ExplodedNodeSet &Out, @@ -426,9 +679,16 @@ void ExprEngine::removeDead(ExplodedNode *Pred, ExplodedNodeSet &Out, LC = LC->getParent(); } - const StackFrameContext *SFC = LC ? LC->getCurrentStackFrame() : nullptr; + const StackFrameContext *SFC = LC ? LC->getStackFrame() : nullptr; SymbolReaper SymReaper(SFC, ReferenceStmt, SymMgr, getStoreManager()); + for (auto I : CleanedState->get<ObjectsUnderConstruction>()) { + if (SymbolRef Sym = I.second.getAsSymbol()) + SymReaper.markLive(Sym); + if (const MemRegion *MR = I.second.getAsRegion()) + SymReaper.markLive(MR); + } + getCheckerManager().runCheckersForLiveSymbols(CleanedState, SymReaper); // Create a state in which dead bindings are removed from the environment @@ -457,9 +717,8 @@ void ExprEngine::removeDead(ExplodedNode *Pred, ExplodedNodeSet &Out, // environment, the store, and the constraints cleaned up but have the // user-supplied states as the predecessors. StmtNodeBuilder Bldr(CheckedSet, Out, *currBldrCtx); - for (ExplodedNodeSet::const_iterator - I = CheckedSet.begin(), E = CheckedSet.end(); I != E; ++I) { - ProgramStateRef CheckerState = (*I)->getState(); + for (const auto I : CheckedSet) { + ProgramStateRef CheckerState = I->getState(); // The constraint manager has not been cleaned up yet, so clean up now. CheckerState = getConstraintManager().removeDeadBindings(CheckerState, @@ -476,35 +735,34 @@ void ExprEngine::removeDead(ExplodedNode *Pred, ExplodedNodeSet &Out, // generate a transition to that state. ProgramStateRef CleanedCheckerSt = StateMgr.getPersistentStateWithGDM(CleanedState, CheckerState); - Bldr.generateNode(DiagnosticStmt, *I, CleanedCheckerSt, &cleanupTag, K); + Bldr.generateNode(DiagnosticStmt, I, CleanedCheckerSt, &cleanupTag, K); } } } -void ExprEngine::ProcessStmt(const CFGStmt S, - ExplodedNode *Pred) { +void ExprEngine::ProcessStmt(const Stmt *currStmt, ExplodedNode *Pred) { // Reclaim any unnecessary nodes in the ExplodedGraph. G.reclaimRecentlyAllocatedNodes(); - const Stmt *currStmt = S.getStmt(); PrettyStackTraceLoc CrashInfo(getContext().getSourceManager(), currStmt->getLocStart(), "Error evaluating statement"); // Remove dead bindings and symbols. ExplodedNodeSet CleanedStates; - if (shouldRemoveDeadBindings(AMgr, S, Pred, Pred->getLocationContext())){ - removeDead(Pred, CleanedStates, currStmt, Pred->getLocationContext()); + if (shouldRemoveDeadBindings(AMgr, currStmt, Pred, + Pred->getLocationContext())) { + removeDead(Pred, CleanedStates, currStmt, + Pred->getLocationContext()); } else CleanedStates.Add(Pred); // Visit the statement. ExplodedNodeSet Dst; - for (ExplodedNodeSet::iterator I = CleanedStates.begin(), - E = CleanedStates.end(); I != E; ++I) { + for (const auto I : CleanedStates) { ExplodedNodeSet DstI; // Visit the statement. - Visit(currStmt, *I, DstI); + Visit(currStmt, I, DstI); Dst.insert(DstI); } @@ -530,36 +788,38 @@ void ExprEngine::ProcessLoopExit(const Stmt* S, ExplodedNode *Pred) { Engine.enqueue(Dst, currBldrCtx->getBlock(), currStmtIdx); } -void ExprEngine::ProcessInitializer(const CFGInitializer Init, +void ExprEngine::ProcessInitializer(const CFGInitializer CFGInit, ExplodedNode *Pred) { - const CXXCtorInitializer *BMI = Init.getInitializer(); + const CXXCtorInitializer *BMI = CFGInit.getInitializer(); + const Expr *Init = BMI->getInit()->IgnoreImplicit(); + const LocationContext *LC = Pred->getLocationContext(); PrettyStackTraceLoc CrashInfo(getContext().getSourceManager(), BMI->getSourceLocation(), "Error evaluating initializer"); // We don't clean up dead bindings here. - const StackFrameContext *stackFrame = - cast<StackFrameContext>(Pred->getLocationContext()); - const CXXConstructorDecl *decl = - cast<CXXConstructorDecl>(stackFrame->getDecl()); + const auto *stackFrame = cast<StackFrameContext>(Pred->getLocationContext()); + const auto *decl = cast<CXXConstructorDecl>(stackFrame->getDecl()); ProgramStateRef State = Pred->getState(); SVal thisVal = State->getSVal(svalBuilder.getCXXThis(decl, stackFrame)); - ExplodedNodeSet Tmp(Pred); + ExplodedNodeSet Tmp; SVal FieldLoc; // Evaluate the initializer, if necessary if (BMI->isAnyMemberInitializer()) { // Constructors build the object directly in the field, // but non-objects must be copied in from the initializer. - if (auto *CtorExpr = findDirectConstructorForCurrentCFGElement()) { - assert(BMI->getInit()->IgnoreImplicit() == CtorExpr); - (void)CtorExpr; + if (getObjectUnderConstruction(State, BMI, LC)) { // The field was directly constructed, so there is no need to bind. + // But we still need to stop tracking the object under construction. + State = finishObjectConstruction(State, BMI, LC); + NodeBuilder Bldr(Pred, Tmp, *currBldrCtx); + PostStore PS(Init, LC, /*Loc*/ nullptr, /*tag*/ nullptr); + Bldr.generateNode(PS, State, Pred); } else { - const Expr *Init = BMI->getInit()->IgnoreImplicit(); const ValueDecl *Field; if (BMI->isIndirectMemberInitializer()) { Field = BMI->getIndirectMember(); @@ -593,15 +853,12 @@ void ExprEngine::ProcessInitializer(const CFGInitializer Init, InitVal = State->getSVal(BMI->getInit(), stackFrame); } - 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); } } else { assert(BMI->isBaseInitializer() || BMI->isDelegatingInitializer()); + Tmp.insert(Pred); // We already did all the work when visiting the CXXConstructExpr. } @@ -610,9 +867,9 @@ void ExprEngine::ProcessInitializer(const CFGInitializer Init, PostInitializer PP(BMI, FieldLoc.getAsRegion(), stackFrame); ExplodedNodeSet Dst; NodeBuilder Bldr(Tmp, Dst, *currBldrCtx); - for (ExplodedNodeSet::iterator I = Tmp.begin(), E = Tmp.end(); I != E; ++I) { - ExplodedNode *N = *I; - Bldr.generateNode(PP, N->getState(), N); + for (const auto I : Tmp) { + ProgramStateRef State = I->getState(); + Bldr.generateNode(PP, State, I); } // Enqueue the new nodes onto the work list. @@ -688,8 +945,15 @@ void ExprEngine::ProcessAutomaticObjDtor(const CFGAutomaticObjDtor Dtor, varType = cast<TypedValueRegion>(Region)->getValueType(); } + // FIXME: We need to run the same destructor on every element of the array. + // This workaround will just run the first destructor (which will still + // invalidate the entire array). + EvalCallOptions CallOpts; + Region = makeZeroElementRegion(state, loc::MemRegionVal(Region), varType, + CallOpts.IsArrayCtorOrDtor).getAsRegion(); + VisitCXXDestructor(varType, Region, Dtor.getTriggerStmt(), /*IsBase=*/ false, - Pred, Dst); + Pred, Dst, CallOpts); } void ExprEngine::ProcessDeleteDtor(const CFGDeleteDtor Dtor, @@ -699,12 +963,12 @@ void ExprEngine::ProcessDeleteDtor(const CFGDeleteDtor Dtor, const LocationContext *LCtx = Pred->getLocationContext(); const CXXDeleteExpr *DE = Dtor.getDeleteExpr(); const Stmt *Arg = DE->getArgument(); + QualType DTy = DE->getDestroyedType(); SVal ArgVal = State->getSVal(Arg, LCtx); // If the argument to delete is known to be a null value, // don't run destructor. if (State->isNull(ArgVal).isConstrainedTrue()) { - QualType DTy = DE->getDestroyedType(); QualType BTy = getContext().getBaseElementType(DTy); const CXXRecordDecl *RD = BTy->getAsCXXRecordDecl(); const CXXDestructorDecl *Dtor = RD->getDestructor(); @@ -715,19 +979,30 @@ void ExprEngine::ProcessDeleteDtor(const CFGDeleteDtor Dtor, return; } - VisitCXXDestructor(DE->getDestroyedType(), - ArgVal.getAsRegion(), - DE, /*IsBase=*/ false, - Pred, Dst); + EvalCallOptions CallOpts; + const MemRegion *ArgR = ArgVal.getAsRegion(); + if (DE->isArrayForm()) { + // FIXME: We need to run the same destructor on every element of the array. + // This workaround will just run the first destructor (which will still + // invalidate the entire array). + CallOpts.IsArrayCtorOrDtor = true; + // Yes, it may even be a multi-dimensional array. + while (const auto *AT = getContext().getAsArrayType(DTy)) + DTy = AT->getElementType(); + if (ArgR) + ArgR = getStoreManager().GetElementZeroRegion(cast<SubRegion>(ArgR), DTy); + } + + VisitCXXDestructor(DTy, ArgR, DE, /*IsBase=*/false, Pred, Dst, CallOpts); } void ExprEngine::ProcessBaseDtor(const CFGBaseDtor D, ExplodedNode *Pred, ExplodedNodeSet &Dst) { const LocationContext *LCtx = Pred->getLocationContext(); - const CXXDestructorDecl *CurDtor = cast<CXXDestructorDecl>(LCtx->getDecl()); + const auto *CurDtor = cast<CXXDestructorDecl>(LCtx->getDecl()); Loc ThisPtr = getSValBuilder().getCXXThis(CurDtor, - LCtx->getCurrentStackFrame()); + LCtx->getStackFrame()); SVal ThisVal = Pred->getState()->getSVal(ThisPtr); // Create the base object region. @@ -737,51 +1012,94 @@ void ExprEngine::ProcessBaseDtor(const CFGBaseDtor D, Base->isVirtual()); VisitCXXDestructor(BaseTy, BaseVal.castAs<loc::MemRegionVal>().getRegion(), - CurDtor->getBody(), /*IsBase=*/ true, Pred, Dst); + CurDtor->getBody(), /*IsBase=*/ true, Pred, Dst, {}); } void ExprEngine::ProcessMemberDtor(const CFGMemberDtor D, ExplodedNode *Pred, ExplodedNodeSet &Dst) { const FieldDecl *Member = D.getFieldDecl(); + QualType T = Member->getType(); ProgramStateRef State = Pred->getState(); const LocationContext *LCtx = Pred->getLocationContext(); - const CXXDestructorDecl *CurDtor = cast<CXXDestructorDecl>(LCtx->getDecl()); + const auto *CurDtor = cast<CXXDestructorDecl>(LCtx->getDecl()); Loc ThisVal = getSValBuilder().getCXXThis(CurDtor, - LCtx->getCurrentStackFrame()); + LCtx->getStackFrame()); SVal FieldVal = State->getLValue(Member, State->getSVal(ThisVal).castAs<Loc>()); - VisitCXXDestructor(Member->getType(), - FieldVal.castAs<loc::MemRegionVal>().getRegion(), - CurDtor->getBody(), /*IsBase=*/false, Pred, Dst); + // FIXME: We need to run the same destructor on every element of the array. + // This workaround will just run the first destructor (which will still + // invalidate the entire array). + EvalCallOptions CallOpts; + FieldVal = makeZeroElementRegion(State, FieldVal, T, + CallOpts.IsArrayCtorOrDtor); + + VisitCXXDestructor(T, FieldVal.castAs<loc::MemRegionVal>().getRegion(), + CurDtor->getBody(), /*IsBase=*/false, Pred, Dst, CallOpts); } void ExprEngine::ProcessTemporaryDtor(const CFGTemporaryDtor D, ExplodedNode *Pred, ExplodedNodeSet &Dst) { - ExplodedNodeSet CleanDtorState; - StmtNodeBuilder StmtBldr(Pred, CleanDtorState, *currBldrCtx); + const CXXBindTemporaryExpr *BTE = D.getBindTemporaryExpr(); ProgramStateRef State = Pred->getState(); - if (State->contains<InitializedTemporariesSet>( - std::make_pair(D.getBindTemporaryExpr(), Pred->getStackFrame()))) { + const LocationContext *LC = Pred->getLocationContext(); + const MemRegion *MR = nullptr; + + if (Optional<SVal> V = + getObjectUnderConstruction(State, D.getBindTemporaryExpr(), + Pred->getLocationContext())) { // FIXME: Currently we insert temporary destructors for default parameters, - // but we don't insert the constructors. - State = State->remove<InitializedTemporariesSet>( - std::make_pair(D.getBindTemporaryExpr(), Pred->getStackFrame())); + // but we don't insert the constructors, so the entry in + // ObjectsUnderConstruction may be missing. + State = finishObjectConstruction(State, D.getBindTemporaryExpr(), + Pred->getLocationContext()); + MR = V->getAsRegion(); } + + // If copy elision has occured, and the constructor corresponding to the + // destructor was elided, we need to skip the destructor as well. + if (isDestructorElided(State, BTE, LC)) { + State = cleanupElidedDestructor(State, BTE, LC); + NodeBuilder Bldr(Pred, Dst, *currBldrCtx); + PostImplicitCall PP(D.getDestructorDecl(getContext()), + D.getBindTemporaryExpr()->getLocStart(), + Pred->getLocationContext()); + Bldr.generateNode(PP, State, Pred); + return; + } + + ExplodedNodeSet CleanDtorState; + StmtNodeBuilder StmtBldr(Pred, CleanDtorState, *currBldrCtx); StmtBldr.generateNode(D.getBindTemporaryExpr(), Pred, State); - QualType varType = D.getBindTemporaryExpr()->getSubExpr()->getType(); + QualType T = D.getBindTemporaryExpr()->getSubExpr()->getType(); // FIXME: Currently CleanDtorState can be empty here due to temporaries being // bound to default parameters. assert(CleanDtorState.size() <= 1); ExplodedNode *CleanPred = CleanDtorState.empty() ? Pred : *CleanDtorState.begin(); - // FIXME: Inlining of temporary destructors is not supported yet anyway, so - // we just put a NULL region for now. This will need to be changed later. - VisitCXXDestructor(varType, nullptr, D.getBindTemporaryExpr(), - /*IsBase=*/false, CleanPred, Dst); + + EvalCallOptions CallOpts; + CallOpts.IsTemporaryCtorOrDtor = true; + if (!MR) { + CallOpts.IsCtorOrDtorWithImproperlyModeledTargetRegion = true; + + // If we have no MR, we still need to unwrap the array to avoid destroying + // the whole array at once. Regardless, we'd eventually need to model array + // destructors properly, element-by-element. + while (const ArrayType *AT = getContext().getAsArrayType(T)) { + T = AT->getElementType(); + CallOpts.IsArrayCtorOrDtor = true; + } + } else { + // We'd eventually need to makeZeroElementRegion() trick here, + // but for now we don't have the respective construction contexts, + // so MR would always be null in this case. Do nothing for now. + } + VisitCXXDestructor(T, MR, D.getBindTemporaryExpr(), + /*IsBase=*/false, CleanPred, Dst, CallOpts); } void ExprEngine::processCleanupTemporaryBranch(const CXXBindTemporaryExpr *BTE, @@ -791,19 +1109,23 @@ void ExprEngine::processCleanupTemporaryBranch(const CXXBindTemporaryExpr *BTE, const CFGBlock *DstT, const CFGBlock *DstF) { BranchNodeBuilder TempDtorBuilder(Pred, Dst, BldCtx, DstT, DstF); - if (Pred->getState()->contains<InitializedTemporariesSet>( - std::make_pair(BTE, Pred->getStackFrame()))) { + ProgramStateRef State = Pred->getState(); + const LocationContext *LC = Pred->getLocationContext(); + if (getObjectUnderConstruction(State, BTE, LC)) { TempDtorBuilder.markInfeasible(false); - TempDtorBuilder.generateNode(Pred->getState(), true, Pred); + TempDtorBuilder.generateNode(State, true, Pred); } else { TempDtorBuilder.markInfeasible(true); - TempDtorBuilder.generateNode(Pred->getState(), false, Pred); + TempDtorBuilder.generateNode(State, false, Pred); } } void ExprEngine::VisitCXXBindTemporaryExpr(const CXXBindTemporaryExpr *BTE, ExplodedNodeSet &PreVisit, ExplodedNodeSet &Dst) { + // This is a fallback solution in case we didn't have a construction + // context when we were constructing the temporary. Otherwise the map should + // have been populated there. if (!getAnalysisManager().options.includeTemporaryDtorsInCFG()) { // In case we don't have temporary destructors in the CFG, do not mark // the initialization - we would otherwise never clean it up. @@ -813,34 +1135,39 @@ void ExprEngine::VisitCXXBindTemporaryExpr(const CXXBindTemporaryExpr *BTE, StmtNodeBuilder StmtBldr(PreVisit, Dst, *currBldrCtx); for (ExplodedNode *Node : PreVisit) { ProgramStateRef State = Node->getState(); - - if (!State->contains<InitializedTemporariesSet>( - std::make_pair(BTE, Node->getStackFrame()))) { - // FIXME: Currently the state might already contain the marker due to + const LocationContext *LC = Node->getLocationContext(); + if (!getObjectUnderConstruction(State, BTE, LC)) { + // FIXME: Currently the state might also already contain the marker due to // incorrect handling of temporaries bound to default parameters; for // those, we currently skip the CXXBindTemporaryExpr but rely on adding // temporary destructor nodes. - State = State->add<InitializedTemporariesSet>( - std::make_pair(BTE, Node->getStackFrame())); + State = addObjectUnderConstruction(State, BTE, LC, UnknownVal()); } StmtBldr.generateNode(BTE, Node, State); } } -namespace { -class CollectReachableSymbolsCallback final : public SymbolVisitor { - InvalidatedSymbols Symbols; +ProgramStateRef ExprEngine::escapeValue(ProgramStateRef State, SVal V, + PointerEscapeKind K) const { + class CollectReachableSymbolsCallback final : public SymbolVisitor { + InvalidatedSymbols Symbols; -public: - explicit CollectReachableSymbolsCallback(ProgramStateRef State) {} - const InvalidatedSymbols &getSymbols() const { return Symbols; } + public: + explicit CollectReachableSymbolsCallback(ProgramStateRef State) {} - bool VisitSymbol(SymbolRef Sym) override { - Symbols.insert(Sym); - return true; - } -}; -} // end anonymous namespace + const InvalidatedSymbols &getSymbols() const { return Symbols; } + + bool VisitSymbol(SymbolRef Sym) override { + Symbols.insert(Sym); + return true; + } + }; + + const CollectReachableSymbolsCallback &Scanner = + State->scanReachableSymbols<CollectReachableSymbolsCallback>(V); + return getCheckerManager().runCheckersForPointerEscape( + State, Scanner.getSymbols(), /*CallEvent*/ nullptr, K, nullptr); +} void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, ExplodedNodeSet &DstTop) { @@ -930,8 +1257,7 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, case Stmt::OMPTargetTeamsDistributeParallelForDirectiveClass: case Stmt::OMPTargetTeamsDistributeParallelForSimdDirectiveClass: case Stmt::OMPTargetTeamsDistributeSimdDirectiveClass: - case Stmt::CapturedStmtClass: - { + case Stmt::CapturedStmtClass: { const ExplodedNode *node = Bldr.generateSink(S, Pred, Pred->getState()); Engine.addAbortedBlock(node, currBldrCtx->getBlock()); break; @@ -1026,6 +1352,7 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, case Stmt::AddrLabelExprClass: case Stmt::AttributedStmtClass: case Stmt::IntegerLiteralClass: + case Stmt::FixedPointLiteralClass: case Stmt::CharacterLiteralClass: case Stmt::ImplicitValueInitExprClass: case Stmt::CXXScalarValueInitExprClass: @@ -1060,16 +1387,15 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, StmtNodeBuilder Bldr2(PreVisit, Tmp, *currBldrCtx); const Expr *ArgE; - if (const CXXDefaultArgExpr *DefE = dyn_cast<CXXDefaultArgExpr>(S)) + if (const auto *DefE = dyn_cast<CXXDefaultArgExpr>(S)) ArgE = DefE->getExpr(); - else if (const CXXDefaultInitExpr *DefE = dyn_cast<CXXDefaultInitExpr>(S)) + else if (const auto *DefE = dyn_cast<CXXDefaultInitExpr>(S)) ArgE = DefE->getExpr(); else llvm_unreachable("unknown constant wrapper kind"); bool IsTemporary = false; - if (const MaterializeTemporaryExpr *MTE = - dyn_cast<MaterializeTemporaryExpr>(ArgE)) { + if (const auto *MTE = dyn_cast<MaterializeTemporaryExpr>(ArgE)) { ArgE = MTE->GetTemporaryExpr(); IsTemporary = true; } @@ -1079,15 +1405,14 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, ConstantVal = UnknownVal(); const LocationContext *LCtx = Pred->getLocationContext(); - for (ExplodedNodeSet::iterator I = PreVisit.begin(), E = PreVisit.end(); - I != E; ++I) { - ProgramStateRef State = (*I)->getState(); + for (const auto I : PreVisit) { + ProgramStateRef State = I->getState(); State = State->BindExpr(S, LCtx, *ConstantVal); if (IsTemporary) State = createTemporaryRegionIfNeeded(State, LCtx, cast<Expr>(S), cast<Expr>(S)); - Bldr2.generateNode(S, *I, State); + Bldr2.generateNode(S, I, State); } getCheckerManager().runCheckersForPostStmt(Dst, Tmp, S, *this); @@ -1108,12 +1433,10 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, ExplodedNodeSet Tmp; StmtNodeBuilder Bldr2(preVisit, Tmp, *currBldrCtx); - const Expr *Ex = cast<Expr>(S); + const auto *Ex = cast<Expr>(S); QualType resultType = Ex->getType(); - for (ExplodedNodeSet::iterator it = preVisit.begin(), et = preVisit.end(); - it != et; ++it) { - ExplodedNode *N = *it; + for (const auto N : preVisit) { const LocationContext *LCtx = N->getLocationContext(); SVal result = svalBuilder.conjureSymbolVal(nullptr, Ex, LCtx, resultType, @@ -1127,17 +1450,8 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, ->getType()->isRecordType())) for (auto Child : Ex->children()) { assert(Child); - SVal Val = State->getSVal(Child, LCtx); - - CollectReachableSymbolsCallback Scanner = - State->scanReachableSymbols<CollectReachableSymbolsCallback>( - Val); - const InvalidatedSymbols &EscapedSymbols = Scanner.getSymbols(); - - State = getCheckerManager().runCheckersForPointerEscape( - State, EscapedSymbols, - /*CallEvent*/ nullptr, PSK_EscapeOther, nullptr); + State = escapeValue(State, Val, PSK_EscapeOther); } Bldr2.generateNode(S, N, State); @@ -1184,7 +1498,7 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, break; case Stmt::BinaryOperatorClass: { - const BinaryOperator* B = cast<BinaryOperator>(S); + const auto *B = cast<BinaryOperator>(S); if (B->isLogicalOp()) { Bldr.takeNodes(Pred); VisitLogicalExpr(B, Pred, Dst); @@ -1216,12 +1530,12 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, } case Stmt::CXXOperatorCallExprClass: { - const CXXOperatorCallExpr *OCE = cast<CXXOperatorCallExpr>(S); + const auto *OCE = cast<CXXOperatorCallExpr>(S); // For instance method operators, make sure the 'this' argument has a // valid region. const Decl *Callee = OCE->getCalleeDecl(); - if (const CXXMethodDecl *MD = dyn_cast_or_null<CXXMethodDecl>(Callee)) { + if (const auto *MD = dyn_cast_or_null<CXXMethodDecl>(Callee)) { if (MD->isInstance()) { ProgramStateRef State = Pred->getState(); const LocationContext *LCtx = Pred->getLocationContext(); @@ -1239,34 +1553,38 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, // FALLTHROUGH LLVM_FALLTHROUGH; } + case Stmt::CallExprClass: case Stmt::CXXMemberCallExprClass: - case Stmt::UserDefinedLiteralClass: { + case Stmt::UserDefinedLiteralClass: Bldr.takeNodes(Pred); VisitCallExpr(cast<CallExpr>(S), Pred, Dst); Bldr.addNodes(Dst); break; - } - case Stmt::CXXCatchStmtClass: { + case Stmt::CXXCatchStmtClass: Bldr.takeNodes(Pred); VisitCXXCatchStmt(cast<CXXCatchStmt>(S), Pred, Dst); Bldr.addNodes(Dst); break; - } case Stmt::CXXTemporaryObjectExprClass: - case Stmt::CXXConstructExprClass: { + case Stmt::CXXConstructExprClass: Bldr.takeNodes(Pred); VisitCXXConstructExpr(cast<CXXConstructExpr>(S), Pred, Dst); Bldr.addNodes(Dst); break; - } case Stmt::CXXNewExprClass: { Bldr.takeNodes(Pred); + + ExplodedNodeSet PreVisit; + getCheckerManager().runCheckersForPreStmt(PreVisit, Pred, S, *this); + ExplodedNodeSet PostVisit; - VisitCXXNewExpr(cast<CXXNewExpr>(S), Pred, PostVisit); + for (const auto i : PreVisit) + VisitCXXNewExpr(cast<CXXNewExpr>(S), i, PostVisit); + getCheckerManager().runCheckersForPostStmt(Dst, PostVisit, S, *this); Bldr.addNodes(Dst); break; @@ -1275,12 +1593,11 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, case Stmt::CXXDeleteExprClass: { Bldr.takeNodes(Pred); ExplodedNodeSet PreVisit; - const CXXDeleteExpr *CDE = cast<CXXDeleteExpr>(S); + const auto *CDE = cast<CXXDeleteExpr>(S); getCheckerManager().runCheckersForPreStmt(PreVisit, Pred, S, *this); - for (ExplodedNodeSet::iterator i = PreVisit.begin(), - e = PreVisit.end(); i != e ; ++i) - VisitCXXDeleteExpr(CDE, *i, Dst); + for (const auto i : PreVisit) + VisitCXXDeleteExpr(CDE, i, Dst); Bldr.addNodes(Dst); break; @@ -1290,7 +1607,7 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, case Stmt::ChooseExprClass: { // __builtin_choose_expr Bldr.takeNodes(Pred); - const ChooseExpr *C = cast<ChooseExpr>(S); + const auto *C = cast<ChooseExpr>(S); VisitGuardedExpr(C, C->getLHS(), C->getRHS(), Pred, Dst); Bldr.addNodes(Dst); break; @@ -1311,8 +1628,7 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, case Stmt::BinaryConditionalOperatorClass: case Stmt::ConditionalOperatorClass: { // '?' operator Bldr.takeNodes(Pred); - const AbstractConditionalOperator *C - = cast<AbstractConditionalOperator>(S); + const auto *C = cast<AbstractConditionalOperator>(S); VisitGuardedExpr(C, C->getTrueExpr(), C->getFalseExpr(), Pred, Dst); Bldr.addNodes(Dst); break; @@ -1326,7 +1642,7 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, case Stmt::DeclRefExprClass: { Bldr.takeNodes(Pred); - const DeclRefExpr *DE = cast<DeclRefExpr>(S); + const auto *DE = cast<DeclRefExpr>(S); VisitCommonDeclRefExpr(DE, DE->getDecl(), Pred, Dst); Bldr.addNodes(Dst); break; @@ -1347,7 +1663,7 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, case Stmt::CXXFunctionalCastExprClass: case Stmt::ObjCBridgedCastExprClass: { Bldr.takeNodes(Pred); - const CastExpr *C = cast<CastExpr>(S); + const auto *C = cast<CastExpr>(S); ExplodedNodeSet dstExpr; VisitCast(C, C->getSubExpr(), Pred, dstExpr); @@ -1359,14 +1675,12 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, case Expr::MaterializeTemporaryExprClass: { Bldr.takeNodes(Pred); - const MaterializeTemporaryExpr *MTE = cast<MaterializeTemporaryExpr>(S); + const auto *MTE = cast<MaterializeTemporaryExpr>(S); ExplodedNodeSet dstPrevisit; getCheckerManager().runCheckersForPreStmt(dstPrevisit, Pred, MTE, *this); ExplodedNodeSet dstExpr; - for (ExplodedNodeSet::iterator i = dstPrevisit.begin(), - e = dstPrevisit.end(); i != e ; ++i) { - CreateCXXTemporaryObject(MTE, *i, dstExpr); - } + for (const auto i : dstPrevisit) + CreateCXXTemporaryObject(MTE, i, dstExpr); getCheckerManager().runCheckersForPostStmt(Dst, dstExpr, MTE, *this); Bldr.addNodes(Dst); break; @@ -1421,11 +1735,19 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, Bldr.addNodes(Dst); break; - case Stmt::OffsetOfExprClass: + case Stmt::OffsetOfExprClass: { Bldr.takeNodes(Pred); - VisitOffsetOfExpr(cast<OffsetOfExpr>(S), Pred, Dst); + ExplodedNodeSet PreVisit; + getCheckerManager().runCheckersForPreStmt(PreVisit, Pred, S, *this); + + ExplodedNodeSet PostVisit; + for (const auto Node : PreVisit) + VisitOffsetOfExpr(cast<OffsetOfExpr>(S), Node, PostVisit); + + getCheckerManager().runCheckersForPostStmt(Dst, PostVisit, S, *this); Bldr.addNodes(Dst); break; + } case Stmt::UnaryExprOrTypeTraitExprClass: Bldr.takeNodes(Pred); @@ -1435,7 +1757,7 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, break; case Stmt::StmtExprClass: { - const StmtExpr *SE = cast<StmtExpr>(S); + const auto *SE = cast<StmtExpr>(S); if (SE->getSubStmt()->body_empty()) { // Empty statement expression. @@ -1444,7 +1766,8 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, break; } - if (Expr *LastExpr = dyn_cast<Expr>(*SE->getSubStmt()->body_rbegin())) { + if (const auto *LastExpr = + dyn_cast<Expr>(*SE->getSubStmt()->body_rbegin())) { ProgramStateRef state = Pred->getState(); Bldr.generateNode(SE, Pred, state->BindExpr(SE, Pred->getLocationContext(), @@ -1456,7 +1779,7 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, case Stmt::UnaryOperatorClass: { Bldr.takeNodes(Pred); - const UnaryOperator *U = cast<UnaryOperator>(S); + const auto *U = cast<UnaryOperator>(S); if (AMgr.options.eagerlyAssumeBinOpBifurcation && (U->getOpcode() == UO_LNot)) { ExplodedNodeSet Tmp; VisitUnaryOperator(U, Pred, Tmp); @@ -1471,7 +1794,7 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, case Stmt::PseudoObjectExprClass: { Bldr.takeNodes(Pred); ProgramStateRef state = Pred->getState(); - const PseudoObjectExpr *PE = cast<PseudoObjectExpr>(S); + const auto *PE = cast<PseudoObjectExpr>(S); if (const Expr *Result = PE->getResultExpr()) { SVal V = state->getSVal(Result, Pred->getLocationContext()); Bldr.generateNode(S, Pred, @@ -1490,8 +1813,8 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, bool ExprEngine::replayWithoutInlining(ExplodedNode *N, const LocationContext *CalleeLC) { - const StackFrameContext *CalleeSF = CalleeLC->getCurrentStackFrame(); - const StackFrameContext *CallerSF = CalleeSF->getParent()->getCurrentStackFrame(); + const StackFrameContext *CalleeSF = CalleeLC->getStackFrame(); + const StackFrameContext *CallerSF = CalleeSF->getParent()->getStackFrame(); assert(CalleeSF && CallerSF); ExplodedNode *BeforeProcessingCall = nullptr; const Stmt *CE = CalleeSF->getCallSite(); @@ -1503,7 +1826,7 @@ bool ExprEngine::replayWithoutInlining(ExplodedNode *N, N = N->pred_empty() ? nullptr : *(N->pred_begin()); // Skip the nodes corresponding to the inlined code. - if (L.getLocationContext()->getCurrentStackFrame() != CallerSF) + if (L.getStackFrame() != CallerSF) continue; // We reached the caller. Find the node right before we started // processing the call. @@ -1602,10 +1925,10 @@ void ExprEngine::processCFGBlockEntrance(const BlockEdge &L, // Check if we stopped at the top level function or not. // Root node should have the location context of the top most function. const LocationContext *CalleeLC = Pred->getLocation().getLocationContext(); - const LocationContext *CalleeSF = CalleeLC->getCurrentStackFrame(); + const LocationContext *CalleeSF = CalleeLC->getStackFrame(); const LocationContext *RootLC = (*G.roots_begin())->getLocation().getLocationContext(); - if (RootLC->getCurrentStackFrame() != CalleeSF) { + if (RootLC->getStackFrame() != CalleeSF) { Engine.FunctionSummaries->markReachedMaxBlockCount(CalleeSF->getDecl()); // Re-run the call evaluation without inlining it, by storing the @@ -1639,14 +1962,14 @@ static SVal RecoverCastedSymbol(ProgramStateManager& StateMgr, const LocationContext *LCtx, ASTContext &Ctx) { - const Expr *Ex = dyn_cast<Expr>(Condition); + const auto *Ex = dyn_cast<Expr>(Condition); if (!Ex) return UnknownVal(); uint64_t bits = 0; bool bitsInit = false; - while (const CastExpr *CE = dyn_cast<CastExpr>(Ex)) { + while (const auto *CE = dyn_cast<CastExpr>(Ex)) { QualType T = CE->getType(); if (!T->isIntegralOrEnumerationType()) @@ -1674,7 +1997,7 @@ static SVal RecoverCastedSymbol(ProgramStateManager& StateMgr, #ifndef NDEBUG static const Stmt *getRightmostLeaf(const Stmt *Condition) { while (Condition) { - const BinaryOperator *BO = dyn_cast<BinaryOperator>(Condition); + const auto *BO = dyn_cast<BinaryOperator>(Condition); if (!BO || !BO->isLogicalOp()) { return Condition; } @@ -1700,10 +2023,10 @@ static const Stmt *getRightmostLeaf(const Stmt *Condition) { // space. static const Stmt *ResolveCondition(const Stmt *Condition, const CFGBlock *B) { - if (const Expr *Ex = dyn_cast<Expr>(Condition)) + if (const auto *Ex = dyn_cast<Expr>(Condition)) Condition = Ex->IgnoreParens(); - const BinaryOperator *BO = dyn_cast<BinaryOperator>(Condition); + const auto *BO = dyn_cast<BinaryOperator>(Condition); if (!BO || !BO->isLogicalOp()) return Condition; @@ -1751,7 +2074,7 @@ void ExprEngine::processBranch(const Stmt *Condition, const Stmt *Term, return; } - if (const Expr *Ex = dyn_cast<Expr>(Condition)) + if (const auto *Ex = dyn_cast<Expr>(Condition)) Condition = Ex->IgnoreParens(); Condition = ResolveCondition(Condition, BldCtx.getBlock()); @@ -1767,10 +2090,7 @@ void ExprEngine::processBranch(const Stmt *Condition, const Stmt *Term, return; BranchNodeBuilder builder(CheckersOutSet, Dst, BldCtx, DstT, DstF); - for (NodeBuilder::iterator I = CheckersOutSet.begin(), - E = CheckersOutSet.end(); E != I; ++I) { - ExplodedNode *PredI = *I; - + for (const auto PredI : CheckersOutSet) { if (PredI->isSink()) continue; @@ -1779,7 +2099,7 @@ void ExprEngine::processBranch(const Stmt *Condition, const Stmt *Term, if (X.isUnknownOrUndef()) { // Give it a chance to recover from unknown. - if (const Expr *Ex = dyn_cast<Expr>(Condition)) { + if (const auto *Ex = dyn_cast<Expr>(Condition)) { if (Ex->getType()->isIntegralOrEnumerationType()) { // Try to recover some path-sensitivity. Right now casts of symbolic // integers that promote their values are currently not tracked well. @@ -1836,13 +2156,13 @@ REGISTER_TRAIT_WITH_PROGRAMSTATE(InitializedGlobalsSet, void ExprEngine::processStaticInitializer(const DeclStmt *DS, NodeBuilderContext &BuilderCtx, ExplodedNode *Pred, - clang::ento::ExplodedNodeSet &Dst, + ExplodedNodeSet &Dst, const CFGBlock *DstT, const CFGBlock *DstF) { PrettyStackTraceLocationContext CrashInfo(Pred->getLocationContext()); currBldrCtx = &BuilderCtx; - const VarDecl *VD = cast<VarDecl>(DS->getSingleDecl()); + const auto *VD = cast<VarDecl>(DS->getSingleDecl()); ProgramStateRef state = Pred->getState(); bool initHasRun = state->contains<InitializedGlobalsSet>(VD); BranchNodeBuilder builder(Pred, Dst, BuilderCtx, DstT, DstF); @@ -1860,7 +2180,6 @@ void ExprEngine::processStaticInitializer(const DeclStmt *DS, /// processIndirectGoto - Called by CoreEngine. Used to generate successor /// nodes by processing the 'effects' of a computed goto jump. void ExprEngine::processIndirectGoto(IndirectGotoNodeBuilder &builder) { - ProgramStateRef state = builder.getState(); SVal V = state->getSVal(builder.getTarget(), builder.getLocationContext()); @@ -1871,7 +2190,7 @@ void ExprEngine::processIndirectGoto(IndirectGotoNodeBuilder &builder) { // (3) We have no clue about the label. Dispatch to all targets. // - typedef IndirectGotoNodeBuilder::iterator iterator; + using iterator = IndirectGotoNodeBuilder::iterator; if (Optional<loc::GotoLabel> LV = V.getAs<loc::GotoLabel>()) { const LabelDecl *L = LV->getLabel(); @@ -1897,26 +2216,10 @@ void ExprEngine::processIndirectGoto(IndirectGotoNodeBuilder &builder) { // This is really a catch-all. We don't support symbolics yet. // FIXME: Implement dispatch for symbolic pointers. - for (iterator I=builder.begin(), E=builder.end(); I != E; ++I) + for (iterator I = builder.begin(), E = builder.end(); I != E; ++I) builder.generateNode(I, state); } -#if 0 -static bool stackFrameDoesNotContainInitializedTemporaries(ExplodedNode &Pred) { - const StackFrameContext* Frame = Pred.getStackFrame(); - const llvm::ImmutableSet<CXXBindTemporaryContext> &Set = - Pred.getState()->get<InitializedTemporariesSet>(); - return std::find_if(Set.begin(), Set.end(), - [&](const CXXBindTemporaryContext &Ctx) { - if (Ctx.second == Frame) { - Ctx.first->dump(); - llvm::errs() << "\n"; - } - return Ctx.second == Frame; - }) == Set.end(); -} -#endif - void ExprEngine::processBeginOfFunction(NodeBuilderContext &BC, ExplodedNode *Pred, ExplodedNodeSet &Dst, @@ -1930,9 +2233,59 @@ void ExprEngine::processBeginOfFunction(NodeBuilderContext &BC, void ExprEngine::processEndOfFunction(NodeBuilderContext& BC, ExplodedNode *Pred, const ReturnStmt *RS) { - // FIXME: Assert that stackFrameDoesNotContainInitializedTemporaries(*Pred)). - // We currently cannot enable this assert, as lifetime extended temporaries - // are not modelled correctly. + // FIXME: We currently cannot assert that temporaries are clear, because + // lifetime extended temporaries are not always modelled correctly. In some + // cases when we materialize the temporary, we do + // createTemporaryRegionIfNeeded(), and the region changes, and also the + // respective destructor becomes automatic from temporary. So for now clean up + // the state manually before asserting. Ideally, the code above the assertion + // should go away, but the assertion should remain. + { + ExplodedNodeSet CleanUpObjects; + NodeBuilder Bldr(Pred, CleanUpObjects, BC); + ProgramStateRef State = Pred->getState(); + const LocationContext *FromLC = Pred->getLocationContext(); + const LocationContext *ToLC = FromLC->getStackFrame()->getParent(); + const LocationContext *LC = FromLC; + while (LC != ToLC) { + assert(LC && "ToLC must be a parent of FromLC!"); + for (auto I : State->get<ObjectsUnderConstruction>()) + if (I.first.getLocationContext() == LC) { + // The comment above only pardons us for not cleaning up a + // CXXBindTemporaryExpr. If any other statements are found here, + // it must be a separate problem. + assert(isa<CXXBindTemporaryExpr>(I.first.getStmt())); + State = State->remove<ObjectsUnderConstruction>(I.first); + // Also cleanup the elided destructor info. + ElidedDestructorItem Item( + cast<CXXBindTemporaryExpr>(I.first.getStmt()), + I.first.getLocationContext()); + State = State->remove<ElidedDestructors>(Item); + } + + // Also suppress the assertion for elided destructors when temporary + // destructors are not provided at all by the CFG, because there's no + // good place to clean them up. + if (!AMgr.getAnalyzerOptions().includeTemporaryDtorsInCFG()) + for (auto I : State->get<ElidedDestructors>()) + if (I.second == LC) + State = State->remove<ElidedDestructors>(I); + + LC = LC->getParent(); + } + if (State != Pred->getState()) { + Pred = Bldr.generateNode(Pred->getLocation(), State, Pred); + if (!Pred) { + // The node with clean temporaries already exists. We might have reached + // it on a path on which we initialize different temporaries. + return; + } + } + } + assert(areAllObjectsFullyConstructed(Pred->getState(), + Pred->getLocationContext(), + Pred->getStackFrame()->getParent())); + PrettyStackTraceLocationContext CrashInfo(Pred->getLocationContext()); StateMgr.EndPath(Pred->getState()); @@ -1943,12 +2296,10 @@ void ExprEngine::processEndOfFunction(NodeBuilderContext& BC, removeDeadOnEndOfFunction(BC, Pred, AfterRemovedDead); // Notify checkers. - for (ExplodedNodeSet::iterator I = AfterRemovedDead.begin(), - E = AfterRemovedDead.end(); I != E; ++I) { - getCheckerManager().runCheckersForEndFunction(BC, Dst, *I, *this); - } + for (const auto I : AfterRemovedDead) + getCheckerManager().runCheckersForEndFunction(BC, Dst, I, *this, RS); } else { - getCheckerManager().runCheckersForEndFunction(BC, Dst, Pred, *this); + getCheckerManager().runCheckersForEndFunction(BC, Dst, Pred, *this, RS); } Engine.enqueueEndOfFunction(Dst, RS); @@ -1957,7 +2308,8 @@ void ExprEngine::processEndOfFunction(NodeBuilderContext& BC, /// ProcessSwitch - Called by CoreEngine. Used to generate successor /// nodes by processing the 'effects' of a switch statement. void ExprEngine::processSwitch(SwitchNodeBuilder& builder) { - typedef SwitchNodeBuilder::iterator iterator; + using iterator = SwitchNodeBuilder::iterator; + ProgramStateRef state = builder.getState(); const Expr *CondE = builder.getCondition(); SVal CondV_untested = state->getSVal(CondE, builder.getLocationContext()); @@ -2046,16 +2398,16 @@ void ExprEngine::VisitCommonDeclRefExpr(const Expr *Ex, const NamedDecl *D, ProgramStateRef state = Pred->getState(); const LocationContext *LCtx = Pred->getLocationContext(); - if (const VarDecl *VD = dyn_cast<VarDecl>(D)) { + if (const auto *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 assert(Ex->isGLValue() || VD->getType()->isVoidType()); const LocationContext *LocCtxt = Pred->getLocationContext(); const Decl *D = LocCtxt->getDecl(); - const auto *MD = D ? dyn_cast<CXXMethodDecl>(D) : nullptr; + const auto *MD = dyn_cast_or_null<CXXMethodDecl>(D); const auto *DeclRefEx = dyn_cast<DeclRefExpr>(Ex); - SVal V; - bool IsReference; + Optional<std::pair<SVal, QualType>> VInfo; + if (AMgr.options.shouldInlineLambdas() && DeclRefEx && DeclRefEx->refersToEnclosingVariableOrCapture() && MD && MD->getParent()->isLambda()) { @@ -2064,25 +2416,23 @@ void ExprEngine::VisitCommonDeclRefExpr(const Expr *Ex, const NamedDecl *D, 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 { + + // Sema follows a sequence of complex rules to determine whether the + // variable should be captured. + if (const FieldDecl *FD = LambdaCaptureFields[VD]) { Loc CXXThis = - svalBuilder.getCXXThis(MD, LocCtxt->getCurrentStackFrame()); + svalBuilder.getCXXThis(MD, LocCtxt->getStackFrame()); SVal CXXThisVal = state->getSVal(CXXThis); - V = state->getLValue(FD, CXXThisVal); - IsReference = FD->getType()->isReferenceType(); + VInfo = std::make_pair(state->getLValue(FD, CXXThisVal), FD->getType()); } - } else { - V = state->getLValue(VD, LocCtxt); - IsReference = VD->getType()->isReferenceType(); } + if (!VInfo) + VInfo = std::make_pair(state->getLValue(VD, LocCtxt), VD->getType()); + + SVal V = VInfo->first; + bool IsReference = VInfo->second->isReferenceType(); + // For references, the 'lvalue' is the pointer address stored in the // reference region. if (IsReference) { @@ -2096,13 +2446,13 @@ void ExprEngine::VisitCommonDeclRefExpr(const Expr *Ex, const NamedDecl *D, ProgramPoint::PostLValueKind); return; } - if (const EnumConstantDecl *ED = dyn_cast<EnumConstantDecl>(D)) { + if (const auto *ED = dyn_cast<EnumConstantDecl>(D)) { assert(!Ex->isGLValue()); SVal V = svalBuilder.makeIntVal(ED->getInitVal()); Bldr.generateNode(Ex, Pred, state->BindExpr(Ex, LCtx, V)); return; } - if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(D)) { + if (const auto *FD = dyn_cast<FunctionDecl>(D)) { SVal V = svalBuilder.getFunctionPointer(FD); Bldr.generateNode(Ex, Pred, state->BindExpr(Ex, LCtx, V), nullptr, ProgramPoint::PostLValueKind); @@ -2118,7 +2468,12 @@ void ExprEngine::VisitCommonDeclRefExpr(const Expr *Ex, const NamedDecl *D, currBldrCtx->blockCount()); state = state->assume(V.castAs<DefinedOrUnknownSVal>(), true); Bldr.generateNode(Ex, Pred, state->BindExpr(Ex, LCtx, V), nullptr, - ProgramPoint::PostLValueKind); + ProgramPoint::PostLValueKind); + return; + } + if (isa<BindingDecl>(D)) { + // FIXME: proper support for bound declarations. + // For now, let's just prevent crashing. return; } @@ -2151,9 +2506,17 @@ void ExprEngine::VisitArraySubscriptExpr(const ArraySubscriptExpr *A, ProgramStateRef state = Node->getState(); if (IsGLValueLike) { - SVal V = state->getLValue(A->getType(), - state->getSVal(Idx, LCtx), - state->getSVal(Base, LCtx)); + QualType T = A->getType(); + + // One of the forbidden LValue types! We still need to have sensible + // symbolic locations to represent this stuff. Note that arithmetic on + // void pointers is a GCC extension. + if (T->isVoidType()) + T = getContext().CharTy; + + SVal V = state->getLValue(T, + state->getSVal(Idx, LCtx), + state->getSVal(Base, LCtx)); Bldr.generateNode(A, Node, state->BindExpr(A, LCtx, V), nullptr, ProgramPoint::PostLValueKind); } else if (IsVectorType) { @@ -2171,41 +2534,36 @@ a vector and not a forbidden lvalue type"); /// VisitMemberExpr - Transfer function for member expressions. void ExprEngine::VisitMemberExpr(const MemberExpr *M, ExplodedNode *Pred, ExplodedNodeSet &Dst) { - // FIXME: Prechecks eventually go in ::Visit(). ExplodedNodeSet CheckedSet; getCheckerManager().runCheckersForPreStmt(CheckedSet, Pred, M, *this); - ExplodedNodeSet EvalSet; - ValueDecl *Member = M->getMemberDecl(); + ExplodedNodeSet EvalSet; + ValueDecl *Member = M->getMemberDecl(); // Handle static member variables and enum constants accessed via // member syntax. - if (isa<VarDecl>(Member) || isa<EnumConstantDecl>(Member)) { - ExplodedNodeSet Dst; - for (ExplodedNodeSet::iterator I = CheckedSet.begin(), E = CheckedSet.end(); - I != E; ++I) { - VisitCommonDeclRefExpr(M, Member, Pred, EvalSet); - } + if (isa<VarDecl>(Member) || isa<EnumConstantDecl>(Member)) { + for (const auto I : CheckedSet) + VisitCommonDeclRefExpr(M, Member, I, EvalSet); } else { StmtNodeBuilder Bldr(CheckedSet, EvalSet, *currBldrCtx); ExplodedNodeSet Tmp; - for (ExplodedNodeSet::iterator I = CheckedSet.begin(), E = CheckedSet.end(); - I != E; ++I) { - ProgramStateRef state = (*I)->getState(); - const LocationContext *LCtx = (*I)->getLocationContext(); + for (const auto I : CheckedSet) { + ProgramStateRef state = I->getState(); + const LocationContext *LCtx = I->getLocationContext(); Expr *BaseExpr = M->getBase(); // Handle C++ method calls. - if (const CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(Member)) { + if (const auto *MD = dyn_cast<CXXMethodDecl>(Member)) { if (MD->isInstance()) state = createTemporaryRegionIfNeeded(state, LCtx, BaseExpr); SVal MDVal = svalBuilder.getFunctionPointer(MD); state = state->BindExpr(M, LCtx, MDVal); - Bldr.generateNode(M, *I, state); + Bldr.generateNode(M, I, state); continue; } @@ -2213,7 +2571,7 @@ void ExprEngine::VisitMemberExpr(const MemberExpr *M, ExplodedNode *Pred, state = createTemporaryRegionIfNeeded(state, LCtx, BaseExpr); SVal baseExprVal = state->getSVal(BaseExpr, LCtx); - FieldDecl *field = cast<FieldDecl>(Member); + const auto *field = cast<FieldDecl>(Member); SVal L = state->getLValue(field, baseExprVal); if (M->isGLValue() || M->getType()->isArrayType()) { @@ -2223,8 +2581,8 @@ void ExprEngine::VisitMemberExpr(const MemberExpr *M, ExplodedNode *Pred, // pointers as soon as they are used. if (!M->isGLValue()) { assert(M->getType()->isArrayType()); - const ImplicitCastExpr *PE = - dyn_cast<ImplicitCastExpr>((*I)->getParentMap().getParentIgnoreParens(M)); + const auto *PE = + dyn_cast<ImplicitCastExpr>(I->getParentMap().getParentIgnoreParens(M)); if (!PE || PE->getCastKind() != CK_ArrayToPointerDecay) { llvm_unreachable("should always be wrapped in ArrayToPointerDecay"); } @@ -2237,11 +2595,11 @@ void ExprEngine::VisitMemberExpr(const MemberExpr *M, ExplodedNode *Pred, L = UnknownVal(); } - Bldr.generateNode(M, *I, state->BindExpr(M, LCtx, L), nullptr, + Bldr.generateNode(M, I, state->BindExpr(M, LCtx, L), nullptr, ProgramPoint::PostLValueKind); } else { - Bldr.takeNodes(*I); - evalLoad(Tmp, M, M, *I, state, L); + Bldr.takeNodes(I); + evalLoad(Tmp, M, M, I, state, L); Bldr.addNodes(Tmp); } } @@ -2261,10 +2619,9 @@ void ExprEngine::VisitAtomicExpr(const AtomicExpr *AE, ExplodedNode *Pred, ExplodedNodeSet AfterInvalidateSet; StmtNodeBuilder Bldr(AfterPreSet, AfterInvalidateSet, *currBldrCtx); - for (ExplodedNodeSet::iterator I = AfterPreSet.begin(), E = AfterPreSet.end(); - I != E; ++I) { - ProgramStateRef State = (*I)->getState(); - const LocationContext *LCtx = (*I)->getLocationContext(); + for (const auto I : AfterPreSet) { + ProgramStateRef State = I->getState(); + const LocationContext *LCtx = I->getLocationContext(); SmallVector<SVal, 8> ValuesToInvalidate; for (unsigned SI = 0, Count = AE->getNumSubExprs(); SI != Count; SI++) { @@ -2281,7 +2638,7 @@ void ExprEngine::VisitAtomicExpr(const AtomicExpr *AE, ExplodedNode *Pred, SVal ResultVal = UnknownVal(); State = State->BindExpr(AE, LCtx, ResultVal); - Bldr.generateNode(AE, *I, State, nullptr, + Bldr.generateNode(AE, I, State, nullptr, ProgramPoint::PostStmtKind); } @@ -2324,15 +2681,7 @@ ProgramStateRef ExprEngine::processPointerEscapedOnBind(ProgramStateRef State, // Otherwise, find all symbols referenced by 'val' that we are tracking // and stop tracking them. - CollectReachableSymbolsCallback Scanner = - State->scanReachableSymbols<CollectReachableSymbolsCallback>(Val); - const InvalidatedSymbols &EscapedSymbols = Scanner.getSymbols(); - State = getCheckerManager().runCheckersForPointerEscape(State, - EscapedSymbols, - /*CallEvent*/ nullptr, - PSK_EscapeOnBind, - nullptr); - + State = escapeValue(State, Val, PSK_EscapeOnBind); return State; } @@ -2343,7 +2692,6 @@ ExprEngine::notifyCheckersOfPointerEscape(ProgramStateRef State, ArrayRef<const MemRegion *> Regions, const CallEvent *Call, RegionAndSymbolInvalidationTraits &ITraits) { - if (!Invalidated || Invalidated->empty()) return State; @@ -2357,16 +2705,13 @@ ExprEngine::notifyCheckersOfPointerEscape(ProgramStateRef State, // 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(), - E = ExplicitRegions.end(); I != E; ++I) { - if (const SymbolicRegion *R = (*I)->StripCasts()->getAs<SymbolicRegion>()) + for (const auto I : ExplicitRegions) { + if (const SymbolicRegion *R = I->StripCasts()->getAs<SymbolicRegion>()) SymbolsDirectlyInvalidated.insert(R->getSymbol()); } InvalidatedSymbols SymbolsIndirectlyInvalidated; - for (InvalidatedSymbols::const_iterator I=Invalidated->begin(), - E = Invalidated->end(); I!=E; ++I) { - SymbolRef sym = *I; + for (const auto &sym : *Invalidated) { if (SymbolsDirectlyInvalidated.count(sym)) continue; SymbolsIndirectlyInvalidated.insert(sym); @@ -2390,7 +2735,6 @@ void ExprEngine::evalBind(ExplodedNodeSet &Dst, const Stmt *StoreE, ExplodedNode *Pred, SVal location, SVal Val, bool atDeclInit, const ProgramPoint *PP) { - const LocationContext *LC = Pred->getLocationContext(); PostStmt PS(StoreE, LC); if (!PP) @@ -2414,9 +2758,7 @@ void ExprEngine::evalBind(ExplodedNodeSet &Dst, const Stmt *StoreE, return; } - for (ExplodedNodeSet::iterator I = CheckedSet.begin(), E = CheckedSet.end(); - I!=E; ++I) { - ExplodedNode *PredI = *I; + for (const auto PredI : CheckedSet) { ProgramStateRef state = PredI->getState(); state = processPointerEscapedOnBind(state, location, Val, LC); @@ -2465,8 +2807,8 @@ void ExprEngine::evalStore(ExplodedNodeSet &Dst, const Expr *AssignE, if (location.isUndef()) return; - for (ExplodedNodeSet::iterator NI=Tmp.begin(), NE=Tmp.end(); NI!=NE; ++NI) - evalBind(Dst, StoreE, *NI, location, Val, false); + for (const auto I : Tmp) + evalBind(Dst, StoreE, I, location, Val, false); } void ExprEngine::evalLoad(ExplodedNodeSet &Dst, @@ -2476,46 +2818,8 @@ void ExprEngine::evalLoad(ExplodedNodeSet &Dst, ProgramStateRef state, SVal location, const ProgramPointTag *tag, - QualType LoadTy) -{ + QualType LoadTy) { assert(!location.getAs<NonLoc>() && "location cannot be a NonLoc."); - - // Are we loading from a region? This actually results in two loads; one - // to fetch the address of the referenced value and one to fetch the - // referenced value. - if (const TypedValueRegion *TR = - dyn_cast_or_null<TypedValueRegion>(location.getAsRegion())) { - - QualType ValTy = TR->getValueType(); - if (const ReferenceType *RT = ValTy->getAs<ReferenceType>()) { - static SimpleProgramPointTag - loadReferenceTag(TagProviderName, "Load Reference"); - ExplodedNodeSet Tmp; - evalLoadCommon(Tmp, NodeEx, BoundEx, Pred, state, - location, &loadReferenceTag, - getContext().getPointerType(RT->getPointeeType())); - - // Perform the load from the referenced value. - for (ExplodedNodeSet::iterator I=Tmp.begin(), E=Tmp.end() ; I!=E; ++I) { - state = (*I)->getState(); - location = state->getSVal(BoundEx, (*I)->getLocationContext()); - evalLoadCommon(Dst, NodeEx, BoundEx, *I, state, location, tag, LoadTy); - } - return; - } - } - - evalLoadCommon(Dst, NodeEx, BoundEx, Pred, state, location, tag, LoadTy); -} - -void ExprEngine::evalLoadCommon(ExplodedNodeSet &Dst, - const Expr *NodeEx, - const Expr *BoundEx, - ExplodedNode *Pred, - ProgramStateRef state, - SVal location, - const ProgramPointTag *tag, - QualType LoadTy) { assert(NodeEx); assert(BoundEx); // Evaluate the location (checks for bad dereferences). @@ -2529,9 +2833,9 @@ void ExprEngine::evalLoadCommon(ExplodedNodeSet &Dst, return; // Proceed with the load. - for (ExplodedNodeSet::iterator NI=Tmp.begin(), NE=Tmp.end(); NI!=NE; ++NI) { - state = (*NI)->getState(); - const LocationContext *LCtx = (*NI)->getLocationContext(); + for (const auto I : Tmp) { + state = I->getState(); + const LocationContext *LCtx = I->getLocationContext(); SVal V = UnknownVal(); if (location.isValid()) { @@ -2540,7 +2844,7 @@ void ExprEngine::evalLoadCommon(ExplodedNodeSet &Dst, V = state->getSVal(location.castAs<Loc>(), LoadTy); } - Bldr.generateNode(NodeEx, *NI, state->BindExpr(BoundEx, LCtx, V), tag, + Bldr.generateNode(NodeEx, I, state->BindExpr(BoundEx, LCtx, V), tag, ProgramPoint::PostLoadKind); } } @@ -2597,8 +2901,7 @@ void ExprEngine::evalEagerlyAssumeBinOpBifurcation(ExplodedNodeSet &Dst, const Expr *Ex) { StmtNodeBuilder Bldr(Src, Dst, *currBldrCtx); - for (ExplodedNodeSet::iterator I=Src.begin(), E=Src.end(); I!=E; ++I) { - ExplodedNode *Pred = *I; + for (const auto Pred : Src) { // Test if the previous node was as the same expression. This can happen // when the expression fails to evaluate to anything meaningful and // (as an optimization) we don't generate a node. @@ -2648,7 +2951,7 @@ void ExprEngine::VisitGCCAsmStmt(const GCCAsmStmt *A, ExplodedNode *Pred, for (const Expr *O : A->outputs()) { SVal X = state->getSVal(O, Pred->getLocationContext()); - assert (!X.getAs<NonLoc>()); // Should be an Lval, or unknown, undef. + assert(!X.getAs<NonLoc>()); // Should be an Lval, or unknown, undef. if (Optional<Loc> LV = X.getAs<Loc>()) state = state->bindLoc(*LV, UnknownVal(), Pred->getLocationContext()); @@ -2672,16 +2975,15 @@ static ExprEngine* GraphPrintCheckerState; static SourceManager* GraphPrintSourceManager; namespace llvm { -template<> -struct DOTGraphTraits<ExplodedNode*> : - public DefaultDOTGraphTraits { - DOTGraphTraits (bool isSimple=false) : DefaultDOTGraphTraits(isSimple) {} +template<> +struct DOTGraphTraits<ExplodedNode*> : public DefaultDOTGraphTraits { + DOTGraphTraits (bool isSimple = false) : DefaultDOTGraphTraits(isSimple) {} // FIXME: Since we do not cache error nodes in ExprEngine now, this does not // work. static std::string getNodeAttributes(const ExplodedNode *N, void*) { - return ""; + return {}; } // De-duplicate some source location pretty-printing. @@ -2694,15 +2996,8 @@ struct DOTGraphTraits<ExplodedNode*> : << "\\l"; } } - static void printLocation2(raw_ostream &Out, SourceLocation SLoc) { - if (SLoc.isFileID() && GraphPrintSourceManager->isInMainFile(SLoc)) - Out << "line " << GraphPrintSourceManager->getExpansionLineNumber(SLoc); - else - SLoc.print(Out, *GraphPrintSourceManager); - } static std::string getNodeLabel(const ExplodedNode *N, void*){ - std::string sbuf; llvm::raw_string_ostream Out(sbuf); @@ -2710,14 +3005,13 @@ struct DOTGraphTraits<ExplodedNode*> : ProgramPoint Loc = N->getLocation(); switch (Loc.getKind()) { - case ProgramPoint::BlockEntranceKind: { + case ProgramPoint::BlockEntranceKind: Out << "Block Entrance: B" << Loc.castAs<BlockEntrance>().getBlock()->getBlockID(); break; - } case ProgramPoint::BlockExitKind: - assert (false); + assert(false); break; case ProgramPoint::CallEnterKind: @@ -2808,7 +3102,7 @@ struct DOTGraphTraits<ExplodedNode*> : const Stmt *Label = E.getDst()->getLabel(); if (Label) { - if (const CaseStmt *C = dyn_cast<CaseStmt>(Label)) { + if (const auto *C = dyn_cast<CaseStmt>(Label)) { Out << "\\lcase "; LangOptions LO; // FIXME. if (C->getLHS()) @@ -2822,7 +3116,7 @@ struct DOTGraphTraits<ExplodedNode*> : Out << ":"; } else { - assert (isa<DefaultStmt>(Label)); + assert(isa<DefaultStmt>(Label)); Out << "\\ldefault:"; } } @@ -2863,6 +3157,8 @@ struct DOTGraphTraits<ExplodedNode*> : Out << "\\lPostStore\\l"; else if (Loc.getAs<PostLValue>()) Out << "\\lPostLValue\\l"; + else if (Loc.getAs<PostAllocatorCall>()) + Out << "\\lPostAllocatorCall\\l"; break; } @@ -2872,40 +3168,7 @@ struct DOTGraphTraits<ExplodedNode*> : Out << "\\|StateID: " << (const void*) state.get() << " NodeID: " << (const void*) N << "\\|"; - // Analysis stack backtrace. - Out << "Location context stack (from current to outer):\\l"; - const LocationContext *LC = Loc.getLocationContext(); - unsigned Idx = 0; - for (; LC; LC = LC->getParent(), ++Idx) { - Out << Idx << ". (" << (const void *)LC << ") "; - switch (LC->getKind()) { - case LocationContext::StackFrame: - if (const NamedDecl *D = dyn_cast<NamedDecl>(LC->getDecl())) - Out << "Calling " << D->getQualifiedNameAsString(); - else - Out << "Calling anonymous code"; - if (const Stmt *S = cast<StackFrameContext>(LC)->getCallSite()) { - Out << " at "; - printLocation2(Out, S->getLocStart()); - } - break; - case LocationContext::Block: - Out << "Invoking block"; - if (const Decl *D = cast<BlockInvocationContext>(LC)->getBlockDecl()) { - Out << " defined at "; - printLocation2(Out, D->getLocStart()); - } - break; - case LocationContext::Scope: - Out << "Entering scope"; - // FIXME: Add more info once ScopeContext is activated. - break; - } - Out << "\\l"; - } - Out << "\\l"; - - state->printDOT(Out); + state->printDOT(Out, N->getLocationContext()); Out << "\\l"; @@ -2916,23 +3179,24 @@ struct DOTGraphTraits<ExplodedNode*> : return Out.str(); } }; -} // end llvm namespace + +} // namespace llvm #endif void ExprEngine::ViewGraph(bool trim) { #ifndef NDEBUG if (trim) { - std::vector<const ExplodedNode*> Src; + std::vector<const ExplodedNode *> Src; // Flush any outstanding reports to make sure we cover all the nodes. // This does not cause them to get displayed. - for (BugReporter::iterator I=BR.begin(), E=BR.end(); I!=E; ++I) - const_cast<BugType*>(*I)->FlushReports(BR); + for (const auto I : BR) + const_cast<BugType *>(I)->FlushReports(BR); // Iterate through the reports and get their nodes. for (BugReporter::EQClasses_iterator EI = BR.EQClasses_begin(), EE = BR.EQClasses_end(); EI != EE; ++EI) { - ExplodedNode *N = const_cast<ExplodedNode*>(EI->begin()->getErrorNode()); + const auto *N = const_cast<ExplodedNode *>(EI->begin()->getErrorNode()); if (N) Src.push_back(N); } |