diff --git a/clang/include/clang/Analysis/ProgramPoint.h b/clang/include/clang/Analysis/ProgramPoint.h index 57e5fc1028d3..4dcf92e7f265 100644 --- a/clang/include/clang/Analysis/ProgramPoint.h +++ b/clang/include/clang/Analysis/ProgramPoint.h @@ -55,17 +55,21 @@ public: CallExitEndKind, MinPostStmtKind = PostStmtKind, MaxPostStmtKind = CallExitEndKind, + PreImplicitCallKind, + PostImplicitCallKind, + MinImplicitCallKind = PreImplicitCallKind, + MaxImplicitCallKind = PostImplicitCallKind, EpsilonKind}; private: - llvm::PointerIntPair Data1; + const void *Data1; llvm::PointerIntPair Data2; // The LocationContext could be NULL to allow ProgramPoint to be used in // context insensitive analysis. llvm::PointerIntPair L; - const ProgramPointTag *Tag; + llvm::PointerIntPair Tag; ProgramPoint(); @@ -74,10 +78,10 @@ protected: Kind k, const LocationContext *l, const ProgramPointTag *tag = 0) - : Data1(P, ((unsigned) k) & 0x3), - Data2(0, (((unsigned) k) >> 2) & 0x3), - L(l, (((unsigned) k) >> 4) & 0x3), - Tag(tag) { + : Data1(P), + Data2(0, (((unsigned) k) >> 0) & 0x3), + L(l, (((unsigned) k) >> 2) & 0x3), + Tag(tag, (((unsigned) k) >> 4) & 0x3) { assert(getKind() == k); assert(getLocationContext() == l); assert(getData1() == P); @@ -88,13 +92,13 @@ protected: Kind k, const LocationContext *l, const ProgramPointTag *tag = 0) - : Data1(P1, ((unsigned) k) & 0x3), - Data2(P2, (((unsigned) k) >> 2) & 0x3), - L(l, (((unsigned) k) >> 4) & 0x3), - Tag(tag) {} + : Data1(P1), + Data2(P2, (((unsigned) k) >> 0) & 0x3), + L(l, (((unsigned) k) >> 2) & 0x3), + Tag(tag, (((unsigned) k) >> 4) & 0x3) {} protected: - const void *getData1() const { return Data1.getPointer(); } + const void *getData1() const { return Data1; } const void *getData2() const { return Data2.getPointer(); } void setData2(const void *d) { Data2.setPointer(d); } @@ -107,11 +111,11 @@ public: } Kind getKind() const { - unsigned x = L.getInt(); + unsigned x = Tag.getInt(); + x <<= 2; + x |= L.getInt(); x <<= 2; x |= Data2.getInt(); - x <<= 2; - x |= Data1.getInt(); return (Kind) x; } @@ -123,7 +127,7 @@ public: K == PreStmtPurgeDeadSymbolsKind); } - const ProgramPointTag *getTag() const { return Tag; } + const ProgramPointTag *getTag() const { return Tag.getPointer(); } const LocationContext *getLocationContext() const { return L.getPointer(); @@ -157,7 +161,7 @@ public: ID.AddPointer(getData1()); ID.AddPointer(getData2()); ID.AddPointer(getLocationContext()); - ID.AddPointer(Tag); + ID.AddPointer(getTag()); } static ProgramPoint getProgramPoint(const Stmt *S, ProgramPoint::Kind K, @@ -408,6 +412,54 @@ public: } }; +/// Represents an implicit call event. +/// +/// The nearest statement is provided for diagnostic purposes. +class ImplicitCallPoint : public ProgramPoint { +public: + ImplicitCallPoint(const Decl *D, SourceLocation Loc, Kind K, + const LocationContext *L, const ProgramPointTag *Tag) + : ProgramPoint(Loc.getPtrEncoding(), D, K, L, Tag) {} + + const Decl *getDecl() const { return static_cast(getData2()); } + SourceLocation getLocation() const { + return SourceLocation::getFromPtrEncoding(getData1()); + } + + static bool classof(const ProgramPoint *Location) { + return Location->getKind() >= MinImplicitCallKind && + Location->getKind() <= MaxImplicitCallKind; + } +}; + +/// Represents a program point just before an implicit call event. +/// +/// Explicit calls will appear as PreStmt program points. +class PreImplicitCall : public ImplicitCallPoint { +public: + PreImplicitCall(const Decl *D, SourceLocation Loc, + const LocationContext *L, const ProgramPointTag *Tag = 0) + : ImplicitCallPoint(D, Loc, PreImplicitCallKind, L, Tag) {} + + static bool classof(const ProgramPoint *Location) { + return Location->getKind() == PreImplicitCallKind; + } +}; + +/// Represents a program point just after an implicit call event. +/// +/// Explicit calls will appear as PostStmt program points. +class PostImplicitCall : public ImplicitCallPoint { +public: + PostImplicitCall(const Decl *D, SourceLocation Loc, + const LocationContext *L, const ProgramPointTag *Tag = 0) + : ImplicitCallPoint(D, Loc, PostImplicitCallKind, L, Tag) {} + + static bool classof(const ProgramPoint *Location) { + return Location->getKind() == PostImplicitCallKind; + } +}; + /// Represents a point when we begin processing an inlined call. class CallEnter : public StmtPoint { public: diff --git a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/Calls.h b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/Calls.h index 332addce1610..0d830c3ce9a1 100644 --- a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/Calls.h +++ b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/Calls.h @@ -24,6 +24,9 @@ #include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h" namespace clang { +class ProgramPoint; +class ProgramPointTag; + namespace ento { enum CallEventKind { @@ -155,6 +158,10 @@ public: return hasNonZeroCallbackArg(); } + /// \brief Returns an appropriate ProgramPoint for this call. + ProgramPoint getProgramPoint(bool IsPreVisit, + const ProgramPointTag *Tag = 0) const; + /// \brief Returns a new state with all argument regions invalidated. /// /// This accepts an alternate state in case some processing has already diff --git a/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp index 41cd80e01e0b..8c2e5295ee63 100644 --- a/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp @@ -1535,6 +1535,9 @@ MallocChecker::MallocBugVisitor::VisitNode(const ExplodedNode *N, if (!S) return 0; + // FIXME: We will eventually need to handle non-statement-based events + // (__attribute__((cleanup))). + // Find out if this is an interesting point and what is the kind. if (Mode == Normal) { if (isAllocated(RS, RSPrev, S)) { diff --git a/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker.cpp index d0618d0dddfe..32f7706bd94a 100644 --- a/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker.cpp @@ -1928,7 +1928,8 @@ PathDiagnosticPiece *CFRefReportVisitor::VisitNode(const ExplodedNode *N, const ExplodedNode *PrevN, BugReporterContext &BRC, BugReport &BR) { - + // FIXME: We will eventually need to handle non-statement-based events + // (__attribute__((cleanup))). if (!isa(N->getLocation())) return NULL; diff --git a/clang/lib/StaticAnalyzer/Core/Calls.cpp b/clang/lib/StaticAnalyzer/Core/Calls.cpp index 24c5ab12ab12..7b86d442765b 100644 --- a/clang/lib/StaticAnalyzer/Core/Calls.cpp +++ b/clang/lib/StaticAnalyzer/Core/Calls.cpp @@ -14,6 +14,7 @@ //===----------------------------------------------------------------------===// #include "clang/StaticAnalyzer/Core/PathSensitive/Calls.h" +#include "clang/Analysis/ProgramPoint.h" #include "llvm/ADT/SmallSet.h" #include "llvm/ADT/StringExtras.h" @@ -195,6 +196,24 @@ ProgramStateRef CallEvent::invalidateRegions(unsigned BlockCount, BlockCount, LCtx, /*Symbols=*/0, this); } +ProgramPoint CallEvent::getProgramPoint(bool IsPreVisit, + const ProgramPointTag *Tag) const { + if (const Expr *E = getOriginExpr()) { + if (IsPreVisit) + return PreStmt(E, LCtx, Tag); + return PostStmt(E, LCtx, Tag); + } + + const Decl *D = getDecl(); + assert(D && "Cannot get a program point without a statement or decl"); + + SourceLocation Loc = getSourceRange().getBegin(); + if (IsPreVisit) + return PreImplicitCall(D, Loc, LCtx, Tag); + return PostImplicitCall(D, Loc, LCtx, Tag); +} + + bool CallEvent::mayBeInlined(const Stmt *S) { return isa(S); } diff --git a/clang/lib/StaticAnalyzer/Core/CheckerManager.cpp b/clang/lib/StaticAnalyzer/Core/CheckerManager.cpp index 39a362182671..d0d212130ace 100644 --- a/clang/lib/StaticAnalyzer/Core/CheckerManager.cpp +++ b/clang/lib/StaticAnalyzer/Core/CheckerManager.cpp @@ -237,15 +237,7 @@ namespace { void runChecker(CheckerManager::CheckCallFunc checkFn, NodeBuilder &Bldr, ExplodedNode *Pred) { - // FIXME: This will be wrong as soon as we handle any calls without - // associated statements. - ProgramPoint::Kind K = IsPreVisit ? ProgramPoint::PreStmtKind - : ProgramPoint::PostStmtKind; - assert(Call.getOriginExpr() && "Calls without stmts not yet handled"); - const ProgramPoint &L = - ProgramPoint::getProgramPoint(Call.getOriginExpr(), - K, Pred->getLocationContext(), - checkFn.Checker); + const ProgramPoint &L = Call.getProgramPoint(IsPreVisit, checkFn.Checker); CheckerContext C(Bldr, Eng, Pred, L); checkFn(Call, C); diff --git a/clang/lib/StaticAnalyzer/Core/ExplodedGraph.cpp b/clang/lib/StaticAnalyzer/Core/ExplodedGraph.cpp index aeb47a131647..510991262827 100644 --- a/clang/lib/StaticAnalyzer/Core/ExplodedGraph.cpp +++ b/clang/lib/StaticAnalyzer/Core/ExplodedGraph.cpp @@ -71,6 +71,7 @@ bool ExplodedGraph::shouldCollect(const ExplodedNode *node) { // (8) The PostStmt is for a non-consumed Stmt or Expr. // (9) The successor is not a CallExpr StmtPoint (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. // Conditions 1 and 2. if (node->pred_size() != 1 || node->succ_size() != 1) @@ -86,9 +87,7 @@ bool ExplodedGraph::shouldCollect(const ExplodedNode *node) { // Condition 3. ProgramPoint progPoint = node->getLocation(); - if (!isa(progPoint) || - (isa(progPoint) || - isa(progPoint) || isa(progPoint))) + if (!isa(progPoint)) return false; // Condition 4. diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp index 63aa28fa0aab..a9387694eecb 100644 --- a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp +++ b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp @@ -984,6 +984,7 @@ bool ExprEngine::replayWithoutInlining(ExplodedNode *N, const StackFrameContext *CallerSF = CalleeSF->getParent()->getCurrentStackFrame(); assert(CalleeSF && CallerSF); ExplodedNode *BeforeProcessingCall = 0; + const Stmt *CE = CalleeSF->getCallSite(); // Find the first node before we started processing the call expression. while (N) { @@ -995,11 +996,13 @@ bool ExprEngine::replayWithoutInlining(ExplodedNode *N, if (L.getLocationContext()->getCurrentStackFrame() != CallerSF) continue; // We reached the caller. Find the node right before we started - // processing the CallExpr. + // processing the call. if (L.isPurgeKind()) continue; + if (isa(&L)) + continue; if (const StmtPoint *SP = dyn_cast(&L)) - if (SP->getStmt() == CalleeSF->getCallSite()) + if (SP->getStmt() == CE) continue; break; } @@ -1010,7 +1013,7 @@ bool ExprEngine::replayWithoutInlining(ExplodedNode *N, // TODO: Clean up the unneeded nodes. // Build an Epsilon node from which we will restart the analyzes. - const Stmt *CE = CalleeSF->getCallSite(); + // Note that CE is permitted to be NULL! ProgramPoint NewNodeLoc = EpsilonPoint(BeforeProcessingCall->getLocationContext(), CE); // Add the special flag to GDM to signal retrying with no inlining. @@ -1872,6 +1875,16 @@ struct DOTGraphTraits : return ""; } + static void printLocation(llvm::raw_ostream &Out, SourceLocation SLoc) { + if (SLoc.isFileID()) { + Out << "\\lline=" + << GraphPrintSourceManager->getExpansionLineNumber(SLoc) + << " col=" + << GraphPrintSourceManager->getExpansionColumnNumber(SLoc) + << "\\l"; + } + } + static std::string getNodeLabel(const ExplodedNode *N, void*){ std::string sbuf; @@ -1921,22 +1934,34 @@ struct DOTGraphTraits : Out << "Epsilon Point"; break; + case ProgramPoint::PreImplicitCallKind: { + ImplicitCallPoint *PC = cast(&Loc); + Out << "PreCall: "; + + // FIXME: Get proper printing options. + PC->getDecl()->print(Out, LangOptions()); + printLocation(Out, PC->getLocation()); + break; + } + + case ProgramPoint::PostImplicitCallKind: { + ImplicitCallPoint *PC = cast(&Loc); + Out << "PostCall: "; + + // FIXME: Get proper printing options. + PC->getDecl()->print(Out, LangOptions()); + printLocation(Out, PC->getLocation()); + break; + } + default: { if (StmtPoint *L = dyn_cast(&Loc)) { const Stmt *S = L->getStmt(); - SourceLocation SLoc = S->getLocStart(); Out << S->getStmtClassName() << ' ' << (void*) S << ' '; LangOptions LO; // FIXME. S->printPretty(Out, 0, PrintingPolicy(LO)); - - if (SLoc.isFileID()) { - Out << "\\lline=" - << GraphPrintSourceManager->getExpansionLineNumber(SLoc) - << " col=" - << GraphPrintSourceManager->getExpansionColumnNumber(SLoc) - << "\\l"; - } + printLocation(Out, S->getLocStart()); if (isa(Loc)) Out << "\\lPreStmt\\l;"; diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp index 6fb41930d35c..bf55ceb5fd49 100644 --- a/clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp +++ b/clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp @@ -346,11 +346,11 @@ static ProgramStateRef getInlineFailedState(ExplodedNode *&N, void *ReplayState = N->getState()->get(); if (!ReplayState) return 0; - const Stmt *ReplayCallE = reinterpret_cast(ReplayState); - if (CallE == ReplayCallE) { - return N->getState()->remove(); - } - return 0; + + assert(ReplayState == (const void*)CallE && "Backtracked to the wrong call."); + (void)CallE; + + return N->getState()->remove(); } void ExprEngine::VisitCallExpr(const CallExpr *CE, ExplodedNode *Pred, @@ -421,13 +421,13 @@ void ExprEngine::evalCall(ExplodedNodeSet &Dst, ExplodedNode *Pred, void ExprEngine::defaultEvalCall(ExplodedNodeSet &Dst, ExplodedNode *Pred, const CallEvent &Call) { // Try to inline the call. - ProgramStateRef state = 0; + // The origin expression here is just used as a kind of checksum; + // for CallEvents that do not have origin expressions, this should still be + // safe. const Expr *E = Call.getOriginExpr(); - if (E) { - state = getInlineFailedState(Pred, E); - if (state == 0 && inlineCall(Dst, Call, Pred)) - return; - } + ProgramStateRef state = getInlineFailedState(Pred, E); + if (state == 0 && inlineCall(Dst, Call, Pred)) + return; // If we can't inline it, handle the return value and invalidate the regions. StmtNodeBuilder Bldr(Pred, Dst, *currentBuilderContext);