diff options
Diffstat (limited to 'lib/StaticAnalyzer/Core')
29 files changed, 2084 insertions, 2990 deletions
diff --git a/lib/StaticAnalyzer/Core/AnalysisManager.cpp b/lib/StaticAnalyzer/Core/AnalysisManager.cpp index 1b1ffff5ade82..73e1a0d0000ff 100644 --- a/lib/StaticAnalyzer/Core/AnalysisManager.cpp +++ b/lib/StaticAnalyzer/Core/AnalysisManager.cpp @@ -13,7 +13,7 @@ using namespace ento; void AnalysisManager::anchor() { } -AnalysisManager::AnalysisManager(ASTContext &ASTCtx, DiagnosticsEngine &diags, +AnalysisManager::AnalysisManager(ASTContext &ASTCtx, const PathDiagnosticConsumers &PDC, StoreManagerCreator storemgr, ConstraintManagerCreator constraintmgr, @@ -38,7 +38,7 @@ AnalysisManager::AnalysisManager(ASTContext &ASTCtx, DiagnosticsEngine &diags, Options.ShouldElideConstructors, /*addVirtualBaseBranches=*/true, injector), - Ctx(ASTCtx), Diags(diags), LangOpts(ASTCtx.getLangOpts()), + Ctx(ASTCtx), LangOpts(ASTCtx.getLangOpts()), PathConsumers(PDC), CreateStoreMgr(storemgr), CreateConstraintMgr(constraintmgr), CheckerMgr(checkerMgr), options(Options) { diff --git a/lib/StaticAnalyzer/Core/AnalyzerOptions.cpp b/lib/StaticAnalyzer/Core/AnalyzerOptions.cpp index 71abe2ae6c0e8..01ac2bc83bb6b 100644 --- a/lib/StaticAnalyzer/Core/AnalyzerOptions.cpp +++ b/lib/StaticAnalyzer/Core/AnalyzerOptions.cpp @@ -30,25 +30,6 @@ using namespace clang; using namespace ento; using namespace llvm; -std::vector<StringRef> -AnalyzerOptions::getRegisteredCheckers(bool IncludeExperimental /* = false */) { - static const StringRef StaticAnalyzerChecks[] = { -#define GET_CHECKERS -#define CHECKER(FULLNAME, CLASS, HELPTEXT, DOC_URI, IS_HIDDEN) \ - FULLNAME, -#include "clang/StaticAnalyzer/Checkers/Checkers.inc" -#undef CHECKER -#undef GET_CHECKERS - }; - std::vector<StringRef> Result; - for (StringRef CheckName : StaticAnalyzerChecks) { - if (!CheckName.startswith("debug.") && - (IncludeExperimental || !CheckName.startswith("alpha."))) - Result.push_back(CheckName); - } - return Result; -} - void AnalyzerOptions::printFormattedEntry( llvm::raw_ostream &Out, std::pair<StringRef, StringRef> EntryDescPair, diff --git a/lib/StaticAnalyzer/Core/BugReporter.cpp b/lib/StaticAnalyzer/Core/BugReporter.cpp index e5a0794f10e2c..1864bcef9b873 100644 --- a/lib/StaticAnalyzer/Core/BugReporter.cpp +++ b/lib/StaticAnalyzer/Core/BugReporter.cpp @@ -24,6 +24,7 @@ #include "clang/Analysis/AnalysisDeclContext.h" #include "clang/Analysis/CFG.h" #include "clang/Analysis/CFGStmtMap.h" +#include "clang/Analysis/PathDiagnostic.h" #include "clang/Analysis/ProgramPoint.h" #include "clang/Basic/LLVM.h" #include "clang/Basic/SourceLocation.h" @@ -31,7 +32,6 @@ #include "clang/StaticAnalyzer/Core/AnalyzerOptions.h" #include "clang/StaticAnalyzer/Core/BugReporter/BugReporterVisitors.h" #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" -#include "clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h" #include "clang/StaticAnalyzer/Core/Checker.h" #include "clang/StaticAnalyzer/Core/CheckerManager.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h" @@ -71,6 +71,7 @@ using namespace clang; using namespace ento; +using namespace llvm; #define DEBUG_TYPE "BugReporter" @@ -85,23 +86,252 @@ BugReporterVisitor::~BugReporterVisitor() = default; void BugReporterContext::anchor() {} //===----------------------------------------------------------------------===// -// Helper routines for walking the ExplodedGraph and fetching statements. +// PathDiagnosticBuilder and its associated routines and helper objects. //===----------------------------------------------------------------------===// -static const Stmt *GetPreviousStmt(const ExplodedNode *N) { - for (N = N->getFirstPred(); N; N = N->getFirstPred()) - if (const Stmt *S = PathDiagnosticLocation::getStmt(N)) - return S; +namespace { - return nullptr; +/// A (CallPiece, node assiciated with its CallEnter) pair. +using CallWithEntry = + std::pair<PathDiagnosticCallPiece *, const ExplodedNode *>; +using CallWithEntryStack = SmallVector<CallWithEntry, 6>; + +/// Map from each node to the diagnostic pieces visitors emit for them. +using VisitorsDiagnosticsTy = + llvm::DenseMap<const ExplodedNode *, std::vector<PathDiagnosticPieceRef>>; + +/// A map from PathDiagnosticPiece to the LocationContext of the inlined +/// function call it represents. +using LocationContextMap = + llvm::DenseMap<const PathPieces *, const LocationContext *>; + +/// A helper class that contains everything needed to construct a +/// PathDiagnostic object. It does no much more then providing convenient +/// getters and some well placed asserts for extra security. +class PathDiagnosticConstruct { + /// The consumer we're constructing the bug report for. + const PathDiagnosticConsumer *Consumer; + /// Our current position in the bug path, which is owned by + /// PathDiagnosticBuilder. + const ExplodedNode *CurrentNode; + /// A mapping from parts of the bug path (for example, a function call, which + /// would span backwards from a CallExit to a CallEnter with the nodes in + /// between them) with the location contexts it is associated with. + LocationContextMap LCM; + const SourceManager &SM; + +public: + /// We keep stack of calls to functions as we're ascending the bug path. + /// TODO: PathDiagnostic has a stack doing the same thing, shouldn't we use + /// that instead? + CallWithEntryStack CallStack; + /// The bug report we're constructing. For ease of use, this field is kept + /// public, though some "shortcut" getters are provided for commonly used + /// methods of PathDiagnostic. + std::unique_ptr<PathDiagnostic> PD; + +public: + PathDiagnosticConstruct(const PathDiagnosticConsumer *PDC, + const ExplodedNode *ErrorNode, + const PathSensitiveBugReport *R); + + /// \returns the location context associated with the current position in the + /// bug path. + const LocationContext *getCurrLocationContext() const { + assert(CurrentNode && "Already reached the root!"); + return CurrentNode->getLocationContext(); + } + + /// Same as getCurrLocationContext (they should always return the same + /// location context), but works after reaching the root of the bug path as + /// well. + const LocationContext *getLocationContextForActivePath() const { + return LCM.find(&PD->getActivePath())->getSecond(); + } + + const ExplodedNode *getCurrentNode() const { return CurrentNode; } + + /// Steps the current node to its predecessor. + /// \returns whether we reached the root of the bug path. + bool ascendToPrevNode() { + CurrentNode = CurrentNode->getFirstPred(); + return static_cast<bool>(CurrentNode); + } + + const ParentMap &getParentMap() const { + return getCurrLocationContext()->getParentMap(); + } + + const SourceManager &getSourceManager() const { return SM; } + + const Stmt *getParent(const Stmt *S) const { + return getParentMap().getParent(S); + } + + void updateLocCtxMap(const PathPieces *Path, const LocationContext *LC) { + assert(Path && LC); + LCM[Path] = LC; + } + + const LocationContext *getLocationContextFor(const PathPieces *Path) const { + assert(LCM.count(Path) && + "Failed to find the context associated with these pieces!"); + return LCM.find(Path)->getSecond(); + } + + bool isInLocCtxMap(const PathPieces *Path) const { return LCM.count(Path); } + + PathPieces &getActivePath() { return PD->getActivePath(); } + PathPieces &getMutablePieces() { return PD->getMutablePieces(); } + + bool shouldAddPathEdges() const { return Consumer->shouldAddPathEdges(); } + bool shouldGenerateDiagnostics() const { + return Consumer->shouldGenerateDiagnostics(); + } + bool supportsLogicalOpControlFlow() const { + return Consumer->supportsLogicalOpControlFlow(); + } +}; + +/// Contains every contextual information needed for constructing a +/// PathDiagnostic object for a given bug report. This class and its fields are +/// immutable, and passes a BugReportConstruct object around during the +/// construction. +class PathDiagnosticBuilder : public BugReporterContext { + /// A linear path from the error node to the root. + std::unique_ptr<const ExplodedGraph> BugPath; + /// The bug report we're describing. Visitors create their diagnostics with + /// them being the last entities being able to modify it (for example, + /// changing interestingness here would cause inconsistencies as to how this + /// file and visitors construct diagnostics), hence its const. + const PathSensitiveBugReport *R; + /// The leaf of the bug path. This isn't the same as the bug reports error + /// node, which refers to the *original* graph, not the bug path. + const ExplodedNode *const ErrorNode; + /// The diagnostic pieces visitors emitted, which is expected to be collected + /// by the time this builder is constructed. + std::unique_ptr<const VisitorsDiagnosticsTy> VisitorsDiagnostics; + +public: + /// Find a non-invalidated report for a given equivalence class, and returns + /// a PathDiagnosticBuilder able to construct bug reports for different + /// consumers. Returns None if no valid report is found. + static Optional<PathDiagnosticBuilder> + findValidReport(ArrayRef<PathSensitiveBugReport *> &bugReports, + PathSensitiveBugReporter &Reporter); + + PathDiagnosticBuilder( + BugReporterContext BRC, std::unique_ptr<ExplodedGraph> BugPath, + PathSensitiveBugReport *r, const ExplodedNode *ErrorNode, + std::unique_ptr<VisitorsDiagnosticsTy> VisitorsDiagnostics); + + /// This function is responsible for generating diagnostic pieces that are + /// *not* provided by bug report visitors. + /// These diagnostics may differ depending on the consumer's settings, + /// and are therefore constructed separately for each consumer. + /// + /// There are two path diagnostics generation modes: with adding edges (used + /// for plists) and without (used for HTML and text). When edges are added, + /// the path is modified to insert artificially generated edges. + /// Otherwise, more detailed diagnostics is emitted for block edges, + /// explaining the transitions in words. + std::unique_ptr<PathDiagnostic> + generate(const PathDiagnosticConsumer *PDC) const; + +private: + void updateStackPiecesWithMessage(PathDiagnosticPieceRef P, + const CallWithEntryStack &CallStack) const; + void generatePathDiagnosticsForNode(PathDiagnosticConstruct &C, + PathDiagnosticLocation &PrevLoc) const; + + void generateMinimalDiagForBlockEdge(PathDiagnosticConstruct &C, + BlockEdge BE) const; + + PathDiagnosticPieceRef + generateDiagForGotoOP(const PathDiagnosticConstruct &C, const Stmt *S, + PathDiagnosticLocation &Start) const; + + PathDiagnosticPieceRef + generateDiagForSwitchOP(const PathDiagnosticConstruct &C, const CFGBlock *Dst, + PathDiagnosticLocation &Start) const; + + PathDiagnosticPieceRef + generateDiagForBinaryOP(const PathDiagnosticConstruct &C, const Stmt *T, + const CFGBlock *Src, const CFGBlock *DstC) const; + + PathDiagnosticLocation + ExecutionContinues(const PathDiagnosticConstruct &C) const; + + PathDiagnosticLocation + ExecutionContinues(llvm::raw_string_ostream &os, + const PathDiagnosticConstruct &C) const; + + const PathSensitiveBugReport *getBugReport() const { return R; } +}; + +} // namespace + +//===----------------------------------------------------------------------===// +// Base implementation of stack hint generators. +//===----------------------------------------------------------------------===// + +StackHintGenerator::~StackHintGenerator() = default; + +std::string StackHintGeneratorForSymbol::getMessage(const ExplodedNode *N){ + if (!N) + return getMessageForSymbolNotFound(); + + ProgramPoint P = N->getLocation(); + CallExitEnd CExit = P.castAs<CallExitEnd>(); + + // FIXME: Use CallEvent to abstract this over all calls. + const Stmt *CallSite = CExit.getCalleeContext()->getCallSite(); + const auto *CE = dyn_cast_or_null<CallExpr>(CallSite); + if (!CE) + return {}; + + // Check if one of the parameters are set to the interesting symbol. + unsigned ArgIndex = 0; + for (CallExpr::const_arg_iterator I = CE->arg_begin(), + E = CE->arg_end(); I != E; ++I, ++ArgIndex){ + SVal SV = N->getSVal(*I); + + // Check if the variable corresponding to the symbol is passed by value. + SymbolRef AS = SV.getAsLocSymbol(); + if (AS == Sym) { + return getMessageForArg(*I, ArgIndex); + } + + // Check if the parameter is a pointer to the symbol. + if (Optional<loc::MemRegionVal> Reg = SV.getAs<loc::MemRegionVal>()) { + // Do not attempt to dereference void*. + if ((*I)->getType()->isVoidPointerType()) + continue; + SVal PSV = N->getState()->getSVal(Reg->getRegion()); + SymbolRef AS = PSV.getAsLocSymbol(); + if (AS == Sym) { + return getMessageForArg(*I, ArgIndex); + } + } + } + + // Check if we are returning the interesting symbol. + SVal SV = N->getSVal(CE); + SymbolRef RetSym = SV.getAsLocSymbol(); + if (RetSym == Sym) { + return getMessageForReturn(CE); + } + + return getMessageForSymbolNotFound(); } -static inline const Stmt* -GetCurrentOrPreviousStmt(const ExplodedNode *N) { - if (const Stmt *S = PathDiagnosticLocation::getStmt(N)) - return S; +std::string StackHintGeneratorForSymbol::getMessageForArg(const Expr *ArgE, + unsigned ArgIndex) { + // Printed parameters start at 1, not 0. + ++ArgIndex; - return GetPreviousStmt(N); + return (llvm::Twine(Msg) + " via " + std::to_string(ArgIndex) + + llvm::getOrdinalSuffix(ArgIndex) + " parameter").str(); } //===----------------------------------------------------------------------===// @@ -182,16 +412,12 @@ static void removeRedundantMsgs(PathPieces &path) { } } -/// A map from PathDiagnosticPiece to the LocationContext of the inlined -/// function call it represents. -using LocationContextMap = - llvm::DenseMap<const PathPieces *, const LocationContext *>; - /// Recursively scan through a path and prune out calls and macros pieces /// that aren't needed. Return true if afterwards the path contains /// "interesting stuff" which means it shouldn't be pruned from the parent path. -static bool removeUnneededCalls(PathPieces &pieces, BugReport *R, - LocationContextMap &LCM, +static bool removeUnneededCalls(const PathDiagnosticConstruct &C, + PathPieces &pieces, + const PathSensitiveBugReport *R, bool IsInteresting = false) { bool containsSomethingInteresting = IsInteresting; const unsigned N = pieces.size(); @@ -206,9 +432,9 @@ static bool removeUnneededCalls(PathPieces &pieces, BugReport *R, case PathDiagnosticPiece::Call: { auto &call = cast<PathDiagnosticCallPiece>(*piece); // Check if the location context is interesting. - assert(LCM.count(&call.path)); - if (!removeUnneededCalls(call.path, R, LCM, - R->isInteresting(LCM[&call.path]))) + if (!removeUnneededCalls( + C, call.path, R, + R->isInteresting(C.getLocationContextFor(&call.path)))) continue; containsSomethingInteresting = true; @@ -216,7 +442,7 @@ static bool removeUnneededCalls(PathPieces &pieces, BugReport *R, } case PathDiagnosticPiece::Macro: { auto ¯o = cast<PathDiagnosticMacroPiece>(*piece); - if (!removeUnneededCalls(macro.subPieces, R, LCM, IsInteresting)) + if (!removeUnneededCalls(C, macro.subPieces, R, IsInteresting)) continue; containsSomethingInteresting = true; break; @@ -345,70 +571,23 @@ static void removePiecesWithInvalidLocations(PathPieces &Pieces) { } } -//===----------------------------------------------------------------------===// -// PathDiagnosticBuilder and its associated routines and helper objects. -//===----------------------------------------------------------------------===// - -namespace { - -class PathDiagnosticBuilder : public BugReporterContext { - BugReport *R; - PathDiagnosticConsumer *PDC; - -public: - const LocationContext *LC; - - PathDiagnosticBuilder(GRBugReporter &br, - BugReport *r, InterExplodedGraphMap &Backmap, - PathDiagnosticConsumer *pdc) - : BugReporterContext(br, Backmap), R(r), PDC(pdc), - LC(r->getErrorNode()->getLocationContext()) {} +PathDiagnosticLocation PathDiagnosticBuilder::ExecutionContinues( + const PathDiagnosticConstruct &C) const { + if (const Stmt *S = C.getCurrentNode()->getNextStmtForDiagnostics()) + return PathDiagnosticLocation(S, getSourceManager(), + C.getCurrLocationContext()); - PathDiagnosticLocation ExecutionContinues(const ExplodedNode *N); - - PathDiagnosticLocation ExecutionContinues(llvm::raw_string_ostream &os, - const ExplodedNode *N); - - BugReport *getBugReport() { return R; } - - Decl const &getCodeDecl() { return R->getErrorNode()->getCodeDecl(); } - - ParentMap& getParentMap() { return LC->getParentMap(); } - - const Stmt *getParent(const Stmt *S) { - return getParentMap().getParent(S); - } - - PathDiagnosticLocation getEnclosingStmtLocation(const Stmt *S); - - PathDiagnosticConsumer::PathGenerationScheme getGenerationScheme() const { - return PDC ? PDC->getGenerationScheme() : PathDiagnosticConsumer::Minimal; - } - - bool supportsLogicalOpControlFlow() const { - return PDC ? PDC->supportsLogicalOpControlFlow() : true; - } -}; - -} // namespace - -PathDiagnosticLocation -PathDiagnosticBuilder::ExecutionContinues(const ExplodedNode *N) { - if (const Stmt *S = PathDiagnosticLocation::getNextStmt(N)) - return PathDiagnosticLocation(S, getSourceManager(), LC); - - return PathDiagnosticLocation::createDeclEnd(N->getLocationContext(), + return PathDiagnosticLocation::createDeclEnd(C.getCurrLocationContext(), getSourceManager()); } -PathDiagnosticLocation -PathDiagnosticBuilder::ExecutionContinues(llvm::raw_string_ostream &os, - const ExplodedNode *N) { +PathDiagnosticLocation PathDiagnosticBuilder::ExecutionContinues( + llvm::raw_string_ostream &os, const PathDiagnosticConstruct &C) const { // Slow, but probably doesn't matter. if (os.str().empty()) os << ' '; - const PathDiagnosticLocation &Loc = ExecutionContinues(N); + const PathDiagnosticLocation &Loc = ExecutionContinues(C); if (Loc.asStmt()) os << "Execution continues on line " @@ -416,7 +595,7 @@ PathDiagnosticBuilder::ExecutionContinues(llvm::raw_string_ostream &os, << '.'; else { os << "Execution jumps to the end of the "; - const Decl *D = N->getLocationContext()->getDecl(); + const Decl *D = C.getCurrLocationContext()->getDecl(); if (isa<ObjCMethodDecl>(D)) os << "method"; else if (isa<FunctionDecl>(D)) @@ -454,12 +633,14 @@ static const Stmt *getEnclosingParent(const Stmt *S, const ParentMap &PM) { } static PathDiagnosticLocation -getEnclosingStmtLocation(const Stmt *S, SourceManager &SMgr, const ParentMap &P, - const LocationContext *LC, bool allowNestedContexts) { +getEnclosingStmtLocation(const Stmt *S, const LocationContext *LC, + bool allowNestedContexts = false) { if (!S) return {}; - while (const Stmt *Parent = getEnclosingParent(S, P)) { + const SourceManager &SMgr = LC->getDecl()->getASTContext().getSourceManager(); + + while (const Stmt *Parent = getEnclosingParent(S, LC->getParentMap())) { switch (Parent->getStmtClass()) { case Stmt::BinaryOperatorClass: { const auto *B = cast<BinaryOperator>(Parent); @@ -520,59 +701,51 @@ getEnclosingStmtLocation(const Stmt *S, SourceManager &SMgr, const ParentMap &P, return PathDiagnosticLocation(S, SMgr, LC); } -PathDiagnosticLocation -PathDiagnosticBuilder::getEnclosingStmtLocation(const Stmt *S) { - assert(S && "Null Stmt passed to getEnclosingStmtLocation"); - return ::getEnclosingStmtLocation(S, getSourceManager(), getParentMap(), LC, - /*allowNestedContexts=*/false); -} - //===----------------------------------------------------------------------===// // "Minimal" path diagnostic generation algorithm. //===----------------------------------------------------------------------===// -using StackDiagPair = - std::pair<PathDiagnosticCallPiece *, const ExplodedNode *>; -using StackDiagVector = SmallVector<StackDiagPair, 6>; - -static void updateStackPiecesWithMessage(PathDiagnosticPiece &P, - StackDiagVector &CallStack) { - // If the piece contains a special message, add it to all the call - // pieces on the active stack. - if (auto *ep = dyn_cast<PathDiagnosticEventPiece>(&P)) { - if (ep->hasCallStackHint()) - for (const auto &I : CallStack) { - PathDiagnosticCallPiece *CP = I.first; - const ExplodedNode *N = I.second; - std::string stackMsg = ep->getCallStackMessage(N); - - // The last message on the path to final bug is the most important - // one. Since we traverse the path backwards, do not add the message - // if one has been previously added. - if (!CP->hasCallStackMessage()) - CP->setCallStackMessage(stackMsg); - } - } + +/// If the piece contains a special message, add it to all the call pieces on +/// the active stack. For example, my_malloc allocated memory, so MallocChecker +/// will construct an event at the call to malloc(), and add a stack hint that +/// an allocated memory was returned. We'll use this hint to construct a message +/// when returning from the call to my_malloc +/// +/// void *my_malloc() { return malloc(sizeof(int)); } +/// void fishy() { +/// void *ptr = my_malloc(); // returned allocated memory +/// } // leak +void PathDiagnosticBuilder::updateStackPiecesWithMessage( + PathDiagnosticPieceRef P, const CallWithEntryStack &CallStack) const { + if (R->hasCallStackHint(P)) + for (const auto &I : CallStack) { + PathDiagnosticCallPiece *CP = I.first; + const ExplodedNode *N = I.second; + std::string stackMsg = R->getCallStackMessage(P, N); + + // The last message on the path to final bug is the most important + // one. Since we traverse the path backwards, do not add the message + // if one has been previously added. + if (!CP->hasCallStackMessage()) + CP->setCallStackMessage(stackMsg); + } } static void CompactMacroExpandedPieces(PathPieces &path, const SourceManager& SM); +PathDiagnosticPieceRef PathDiagnosticBuilder::generateDiagForSwitchOP( + const PathDiagnosticConstruct &C, const CFGBlock *Dst, + PathDiagnosticLocation &Start) const { -std::shared_ptr<PathDiagnosticControlFlowPiece> generateDiagForSwitchOP( - const ExplodedNode *N, - const CFGBlock *Dst, - const SourceManager &SM, - const LocationContext *LC, - PathDiagnosticBuilder &PDB, - PathDiagnosticLocation &Start - ) { + const SourceManager &SM = getSourceManager(); // Figure out what case arm we took. std::string sbuf; llvm::raw_string_ostream os(sbuf); PathDiagnosticLocation End; if (const Stmt *S = Dst->getLabel()) { - End = PathDiagnosticLocation(S, SM, LC); + End = PathDiagnosticLocation(S, SM, C.getCurrLocationContext()); switch (S->getStmtClass()) { default: @@ -605,7 +778,7 @@ std::shared_ptr<PathDiagnosticControlFlowPiece> generateDiagForSwitchOP( } if (GetRawInt) - os << LHS->EvaluateKnownConstInt(PDB.getASTContext()); + os << LHS->EvaluateKnownConstInt(getASTContext()); os << ":' at line " << End.asLocation().getExpansionLineNumber(); break; @@ -613,33 +786,29 @@ std::shared_ptr<PathDiagnosticControlFlowPiece> generateDiagForSwitchOP( } } else { os << "'Default' branch taken. "; - End = PDB.ExecutionContinues(os, N); + End = ExecutionContinues(os, C); } return std::make_shared<PathDiagnosticControlFlowPiece>(Start, End, os.str()); } +PathDiagnosticPieceRef PathDiagnosticBuilder::generateDiagForGotoOP( + const PathDiagnosticConstruct &C, const Stmt *S, + PathDiagnosticLocation &Start) const { + std::string sbuf; + llvm::raw_string_ostream os(sbuf); + const PathDiagnosticLocation &End = + getEnclosingStmtLocation(S, C.getCurrLocationContext()); + os << "Control jumps to line " << End.asLocation().getExpansionLineNumber(); + return std::make_shared<PathDiagnosticControlFlowPiece>(Start, End, os.str()); +} -std::shared_ptr<PathDiagnosticControlFlowPiece> generateDiagForGotoOP( - const Stmt *S, - PathDiagnosticBuilder &PDB, - PathDiagnosticLocation &Start) { - std::string sbuf; - llvm::raw_string_ostream os(sbuf); - const PathDiagnosticLocation &End = PDB.getEnclosingStmtLocation(S); - os << "Control jumps to line " << End.asLocation().getExpansionLineNumber(); - return std::make_shared<PathDiagnosticControlFlowPiece>(Start, End, os.str()); +PathDiagnosticPieceRef PathDiagnosticBuilder::generateDiagForBinaryOP( + const PathDiagnosticConstruct &C, const Stmt *T, const CFGBlock *Src, + const CFGBlock *Dst) const { -} + const SourceManager &SM = getSourceManager(); -std::shared_ptr<PathDiagnosticControlFlowPiece> generateDiagForBinaryOP( - const ExplodedNode *N, - const Stmt *T, - const CFGBlock *Src, - const CFGBlock *Dst, - const SourceManager &SM, - PathDiagnosticBuilder &PDB, - const LocationContext *LC) { const auto *B = cast<BinaryOperator>(T); std::string sbuf; llvm::raw_string_ostream os(sbuf); @@ -652,13 +821,14 @@ std::shared_ptr<PathDiagnosticControlFlowPiece> generateDiagForBinaryOP( if (*(Src->succ_begin() + 1) == Dst) { os << "false"; - End = PathDiagnosticLocation(B->getLHS(), SM, LC); + End = PathDiagnosticLocation(B->getLHS(), SM, C.getCurrLocationContext()); Start = PathDiagnosticLocation::createOperatorLoc(B, SM); } else { os << "true"; - Start = PathDiagnosticLocation(B->getLHS(), SM, LC); - End = PDB.ExecutionContinues(N); + Start = + PathDiagnosticLocation(B->getLHS(), SM, C.getCurrLocationContext()); + End = ExecutionContinues(C); } } else { assert(B->getOpcode() == BO_LOr); @@ -667,11 +837,12 @@ std::shared_ptr<PathDiagnosticControlFlowPiece> generateDiagForBinaryOP( if (*(Src->succ_begin() + 1) == Dst) { os << "false"; - Start = PathDiagnosticLocation(B->getLHS(), SM, LC); - End = PDB.ExecutionContinues(N); + Start = + PathDiagnosticLocation(B->getLHS(), SM, C.getCurrLocationContext()); + End = ExecutionContinues(C); } else { os << "true"; - End = PathDiagnosticLocation(B->getLHS(), SM, LC); + End = PathDiagnosticLocation(B->getLHS(), SM, C.getCurrLocationContext()); Start = PathDiagnosticLocation::createOperatorLoc(B, SM); } @@ -680,11 +851,10 @@ std::shared_ptr<PathDiagnosticControlFlowPiece> generateDiagForBinaryOP( os.str()); } -void generateMinimalDiagForBlockEdge(const ExplodedNode *N, BlockEdge BE, - const SourceManager &SM, - PathDiagnosticBuilder &PDB, - PathDiagnostic &PD) { - const LocationContext *LC = N->getLocationContext(); +void PathDiagnosticBuilder::generateMinimalDiagForBlockEdge( + PathDiagnosticConstruct &C, BlockEdge BE) const { + const SourceManager &SM = getSourceManager(); + const LocationContext *LC = C.getCurrLocationContext(); const CFGBlock *Src = BE.getSrc(); const CFGBlock *Dst = BE.getDst(); const Stmt *T = Src->getTerminatorStmt(); @@ -698,14 +868,13 @@ void generateMinimalDiagForBlockEdge(const ExplodedNode *N, BlockEdge BE, case Stmt::GotoStmtClass: case Stmt::IndirectGotoStmtClass: { - if (const Stmt *S = PathDiagnosticLocation::getNextStmt(N)) - PD.getActivePath().push_front(generateDiagForGotoOP(S, PDB, Start)); + if (const Stmt *S = C.getCurrentNode()->getNextStmtForDiagnostics()) + C.getActivePath().push_front(generateDiagForGotoOP(C, S, Start)); break; } case Stmt::SwitchStmtClass: { - PD.getActivePath().push_front( - generateDiagForSwitchOP(N, Dst, SM, LC, PDB, Start)); + C.getActivePath().push_front(generateDiagForSwitchOP(C, Dst, Start)); break; } @@ -713,8 +882,8 @@ void generateMinimalDiagForBlockEdge(const ExplodedNode *N, BlockEdge BE, case Stmt::ContinueStmtClass: { std::string sbuf; llvm::raw_string_ostream os(sbuf); - PathDiagnosticLocation End = PDB.ExecutionContinues(os, N); - PD.getActivePath().push_front( + PathDiagnosticLocation End = ExecutionContinues(os, C); + C.getActivePath().push_front( std::make_shared<PathDiagnosticControlFlowPiece>(Start, End, os.str())); break; } @@ -731,24 +900,22 @@ void generateMinimalDiagForBlockEdge(const ExplodedNode *N, BlockEdge BE, else os << "true"; - PathDiagnosticLocation End = PDB.ExecutionContinues(N); + PathDiagnosticLocation End = ExecutionContinues(C); if (const Stmt *S = End.asStmt()) - End = PDB.getEnclosingStmtLocation(S); + End = getEnclosingStmtLocation(S, C.getCurrLocationContext()); - PD.getActivePath().push_front( + C.getActivePath().push_front( std::make_shared<PathDiagnosticControlFlowPiece>(Start, End, os.str())); break; } // Determine control-flow for short-circuited '&&' and '||'. case Stmt::BinaryOperatorClass: { - if (!PDB.supportsLogicalOpControlFlow()) + if (!C.supportsLogicalOpControlFlow()) break; - std::shared_ptr<PathDiagnosticControlFlowPiece> Diag = - generateDiagForBinaryOP(N, T, Src, Dst, SM, PDB, LC); - PD.getActivePath().push_front(Diag); + C.getActivePath().push_front(generateDiagForBinaryOP(C, T, Src, Dst)); break; } @@ -758,21 +925,21 @@ void generateMinimalDiagForBlockEdge(const ExplodedNode *N, BlockEdge BE, llvm::raw_string_ostream os(sbuf); os << "Loop condition is true. "; - PathDiagnosticLocation End = PDB.ExecutionContinues(os, N); + PathDiagnosticLocation End = ExecutionContinues(os, C); if (const Stmt *S = End.asStmt()) - End = PDB.getEnclosingStmtLocation(S); + End = getEnclosingStmtLocation(S, C.getCurrLocationContext()); - PD.getActivePath().push_front( + C.getActivePath().push_front( std::make_shared<PathDiagnosticControlFlowPiece>(Start, End, os.str())); } else { - PathDiagnosticLocation End = PDB.ExecutionContinues(N); + PathDiagnosticLocation End = ExecutionContinues(C); if (const Stmt *S = End.asStmt()) - End = PDB.getEnclosingStmtLocation(S); + End = getEnclosingStmtLocation(S, C.getCurrLocationContext()); - PD.getActivePath().push_front( + C.getActivePath().push_front( std::make_shared<PathDiagnosticControlFlowPiece>( Start, End, "Loop condition is false. Exiting loop")); } @@ -785,19 +952,19 @@ void generateMinimalDiagForBlockEdge(const ExplodedNode *N, BlockEdge BE, llvm::raw_string_ostream os(sbuf); os << "Loop condition is false. "; - PathDiagnosticLocation End = PDB.ExecutionContinues(os, N); + PathDiagnosticLocation End = ExecutionContinues(os, C); if (const Stmt *S = End.asStmt()) - End = PDB.getEnclosingStmtLocation(S); + End = getEnclosingStmtLocation(S, C.getCurrLocationContext()); - PD.getActivePath().push_front( + C.getActivePath().push_front( std::make_shared<PathDiagnosticControlFlowPiece>(Start, End, os.str())); } else { - PathDiagnosticLocation End = PDB.ExecutionContinues(N); + PathDiagnosticLocation End = ExecutionContinues(C); if (const Stmt *S = End.asStmt()) - End = PDB.getEnclosingStmtLocation(S); + End = getEnclosingStmtLocation(S, C.getCurrLocationContext()); - PD.getActivePath().push_front( + C.getActivePath().push_front( std::make_shared<PathDiagnosticControlFlowPiece>( Start, End, "Loop condition is true. Entering loop body")); } @@ -805,17 +972,17 @@ void generateMinimalDiagForBlockEdge(const ExplodedNode *N, BlockEdge BE, break; case Stmt::IfStmtClass: { - PathDiagnosticLocation End = PDB.ExecutionContinues(N); + PathDiagnosticLocation End = ExecutionContinues(C); if (const Stmt *S = End.asStmt()) - End = PDB.getEnclosingStmtLocation(S); + End = getEnclosingStmtLocation(S, C.getCurrLocationContext()); if (*(Src->succ_begin() + 1) == Dst) - PD.getActivePath().push_front( + C.getActivePath().push_front( std::make_shared<PathDiagnosticControlFlowPiece>( Start, End, "Taking false branch")); else - PD.getActivePath().push_front( + C.getActivePath().push_front( std::make_shared<PathDiagnosticControlFlowPiece>( Start, End, "Taking true branch")); @@ -824,75 +991,6 @@ void generateMinimalDiagForBlockEdge(const ExplodedNode *N, BlockEdge BE, } } -// Cone-of-influence: support the reverse propagation of "interesting" symbols -// and values by tracing interesting calculations backwards through evaluated -// expressions along a path. This is probably overly complicated, but the idea -// is that if an expression computed an "interesting" value, the child -// expressions are also likely to be "interesting" as well (which then -// propagates to the values they in turn compute). This reverse propagation -// is needed to track interesting correlations across function call boundaries, -// where formal arguments bind to actual arguments, etc. This is also needed -// because the constraint solver sometimes simplifies certain symbolic values -// into constants when appropriate, and this complicates reasoning about -// interesting values. -using InterestingExprs = llvm::DenseSet<const Expr *>; - -static void reversePropagateIntererstingSymbols(BugReport &R, - InterestingExprs &IE, - const ProgramState *State, - const Expr *Ex, - const LocationContext *LCtx) { - SVal V = State->getSVal(Ex, LCtx); - if (!(R.isInteresting(V) || IE.count(Ex))) - return; - - switch (Ex->getStmtClass()) { - default: - if (!isa<CastExpr>(Ex)) - break; - LLVM_FALLTHROUGH; - case Stmt::BinaryOperatorClass: - case Stmt::UnaryOperatorClass: { - for (const Stmt *SubStmt : Ex->children()) { - if (const auto *child = dyn_cast_or_null<Expr>(SubStmt)) { - IE.insert(child); - SVal ChildV = State->getSVal(child, LCtx); - R.markInteresting(ChildV); - } - } - break; - } - } - - R.markInteresting(V); -} - -static void reversePropagateInterestingSymbols(BugReport &R, - InterestingExprs &IE, - const ProgramState *State, - const LocationContext *CalleeCtx) -{ - // FIXME: Handle non-CallExpr-based CallEvents. - const StackFrameContext *Callee = CalleeCtx->getStackFrame(); - const Stmt *CallSite = Callee->getCallSite(); - if (const auto *CE = dyn_cast_or_null<CallExpr>(CallSite)) { - if (const auto *FD = dyn_cast<FunctionDecl>(CalleeCtx->getDecl())) { - 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) { - if (const Expr *ArgE = *AI) { - if (const ParmVarDecl *PD = *PI) { - Loc LV = State->getLValue(PD, CalleeCtx); - if (R.isInteresting(LV) || R.isInteresting(State->getRawSVal(LV))) - IE.insert(ArgE); - } - } - } - } - } -} - //===----------------------------------------------------------------------===// // Functions for determining if a loop was executed 0 times. //===----------------------------------------------------------------------===// @@ -916,7 +1014,8 @@ static bool isJumpToFalseBranch(const BlockEdge *BE) { return (*(Src->succ_begin()+1) == BE->getDst()); } -static bool isContainedByStmt(ParentMap &PM, const Stmt *S, const Stmt *SubS) { +static bool isContainedByStmt(const ParentMap &PM, const Stmt *S, + const Stmt *SubS) { while (SubS) { if (SubS == S) return true; @@ -925,7 +1024,7 @@ static bool isContainedByStmt(ParentMap &PM, const Stmt *S, const Stmt *SubS) { return false; } -static const Stmt *getStmtBeforeCond(ParentMap &PM, const Stmt *Term, +static const Stmt *getStmtBeforeCond(const ParentMap &PM, const Stmt *Term, const ExplodedNode *N) { while (N) { Optional<StmtPoint> SP = N->getLocation().getAs<StmtPoint>(); @@ -939,7 +1038,7 @@ static const Stmt *getStmtBeforeCond(ParentMap &PM, const Stmt *Term, return nullptr; } -static bool isInLoopBody(ParentMap &PM, const Stmt *S, const Stmt *Term) { +static bool isInLoopBody(const ParentMap &PM, const Stmt *S, const Stmt *Term) { const Stmt *LoopBody = nullptr; switch (Term->getStmtClass()) { case Stmt::CXXForRangeStmtClass: { @@ -1007,30 +1106,20 @@ static const Stmt *getTerminatorCondition(const CFGBlock *B) { return S; } -static const char StrEnteringLoop[] = "Entering loop body"; -static const char StrLoopBodyZero[] = "Loop body executed 0 times"; -static const char StrLoopRangeEmpty[] = - "Loop body skipped when range is empty"; -static const char StrLoopCollectionEmpty[] = - "Loop body skipped when collection is empty"; +constexpr llvm::StringLiteral StrEnteringLoop = "Entering loop body"; +constexpr llvm::StringLiteral StrLoopBodyZero = "Loop body executed 0 times"; +constexpr llvm::StringLiteral StrLoopRangeEmpty = + "Loop body skipped when range is empty"; +constexpr llvm::StringLiteral StrLoopCollectionEmpty = + "Loop body skipped when collection is empty"; static std::unique_ptr<FilesToLineNumsMap> -findExecutedLines(SourceManager &SM, const ExplodedNode *N); - -/// Generate diagnostics for the node \p N, -/// and write it into \p PD. -/// \p AddPathEdges Whether diagnostic consumer can generate path arrows -/// showing both row and column. -static void generatePathDiagnosticsForNode(const ExplodedNode *N, - PathDiagnostic &PD, - PathDiagnosticLocation &PrevLoc, - PathDiagnosticBuilder &PDB, - LocationContextMap &LCM, - StackDiagVector &CallStack, - InterestingExprs &IE, - bool AddPathEdges) { - ProgramPoint P = N->getLocation(); - const SourceManager& SM = PDB.getSourceManager(); +findExecutedLines(const SourceManager &SM, const ExplodedNode *N); + +void PathDiagnosticBuilder::generatePathDiagnosticsForNode( + PathDiagnosticConstruct &C, PathDiagnosticLocation &PrevLoc) const { + ProgramPoint P = C.getCurrentNode()->getLocation(); + const SourceManager &SM = getSourceManager(); // Have we encountered an entrance to a call? It may be // the case that we have not encountered a matching @@ -1038,7 +1127,7 @@ static void generatePathDiagnosticsForNode(const ExplodedNode *N, // terminated within the call itself. if (auto CE = P.getAs<CallEnter>()) { - if (AddPathEdges) { + if (C.shouldAddPathEdges()) { // Add an edge to the start of the function. const StackFrameContext *CalleeLC = CE->getCalleeContext(); const Decl *D = CalleeLC->getDecl(); @@ -1049,139 +1138,105 @@ static void generatePathDiagnosticsForNode(const ExplodedNode *N, // because the exit edge comes from a statement (i.e. return), // not from declaration. if (D->hasBody()) - addEdgeToPath(PD.getActivePath(), PrevLoc, - PathDiagnosticLocation::createBegin(D, SM)); + addEdgeToPath(C.getActivePath(), PrevLoc, + PathDiagnosticLocation::createBegin(D, SM)); } // Did we visit an entire call? - bool VisitedEntireCall = PD.isWithinCall(); - PD.popActivePath(); + bool VisitedEntireCall = C.PD->isWithinCall(); + C.PD->popActivePath(); - PathDiagnosticCallPiece *C; + PathDiagnosticCallPiece *Call; if (VisitedEntireCall) { - C = cast<PathDiagnosticCallPiece>(PD.getActivePath().front().get()); + Call = cast<PathDiagnosticCallPiece>(C.getActivePath().front().get()); } else { + // The path terminated within a nested location context, create a new + // call piece to encapsulate the rest of the path pieces. const Decl *Caller = CE->getLocationContext()->getDecl(); - C = PathDiagnosticCallPiece::construct(PD.getActivePath(), Caller); - - if (AddPathEdges) { - // Since we just transferred the path over to the call piece, - // reset the mapping from active to location context. - assert(PD.getActivePath().size() == 1 && - PD.getActivePath().front().get() == C); - LCM[&PD.getActivePath()] = nullptr; - } - - // Record the location context mapping for the path within - // the call. - assert(LCM[&C->path] == nullptr || - LCM[&C->path] == CE->getCalleeContext()); - LCM[&C->path] = CE->getCalleeContext(); - - // If this is the first item in the active path, record - // the new mapping from active path to location context. - const LocationContext *&NewLC = LCM[&PD.getActivePath()]; - if (!NewLC) - NewLC = N->getLocationContext(); - - PDB.LC = NewLC; - } - C->setCallee(*CE, SM); + Call = PathDiagnosticCallPiece::construct(C.getActivePath(), Caller); + assert(C.getActivePath().size() == 1 && + C.getActivePath().front().get() == Call); + + // Since we just transferred the path over to the call piece, reset the + // mapping of the active path to the current location context. + assert(C.isInLocCtxMap(&C.getActivePath()) && + "When we ascend to a previously unvisited call, the active path's " + "address shouldn't change, but rather should be compacted into " + "a single CallEvent!"); + C.updateLocCtxMap(&C.getActivePath(), C.getCurrLocationContext()); + + // Record the location context mapping for the path within the call. + assert(!C.isInLocCtxMap(&Call->path) && + "When we ascend to a previously unvisited call, this must be the " + "first time we encounter the caller context!"); + C.updateLocCtxMap(&Call->path, CE->getCalleeContext()); + } + Call->setCallee(*CE, SM); // Update the previous location in the active path. - PrevLoc = C->getLocation(); + PrevLoc = Call->getLocation(); - if (!CallStack.empty()) { - assert(CallStack.back().first == C); - CallStack.pop_back(); + if (!C.CallStack.empty()) { + assert(C.CallStack.back().first == Call); + C.CallStack.pop_back(); } return; } - - if (AddPathEdges) { - // Query the location context here and the previous location - // as processing CallEnter may change the active path. - PDB.LC = N->getLocationContext(); - - // Record the mapping from the active path to the location - // context. - assert(!LCM[&PD.getActivePath()] || LCM[&PD.getActivePath()] == PDB.LC); - LCM[&PD.getActivePath()] = PDB.LC; - } + assert(C.getCurrLocationContext() == C.getLocationContextForActivePath() && + "The current position in the bug path is out of sync with the " + "location context associated with the active path!"); // Have we encountered an exit from a function call? if (Optional<CallExitEnd> CE = P.getAs<CallExitEnd>()) { // We are descending into a call (backwards). Construct // a new call piece to contain the path pieces for that call. - auto C = PathDiagnosticCallPiece::construct(*CE, SM); + auto Call = PathDiagnosticCallPiece::construct(*CE, SM); // Record the mapping from call piece to LocationContext. - LCM[&C->path] = CE->getCalleeContext(); - - if (AddPathEdges) { - const Stmt *S = CE->getCalleeContext()->getCallSite(); - // Propagate the interesting symbols accordingly. - if (const auto *Ex = dyn_cast_or_null<Expr>(S)) { - reversePropagateIntererstingSymbols(*PDB.getBugReport(), IE, - N->getState().get(), Ex, - N->getLocationContext()); - } + assert(!C.isInLocCtxMap(&Call->path) && + "We just entered a call, this must've been the first time we " + "encounter its context!"); + C.updateLocCtxMap(&Call->path, CE->getCalleeContext()); + + if (C.shouldAddPathEdges()) { // Add the edge to the return site. - addEdgeToPath(PD.getActivePath(), PrevLoc, C->callReturn); + addEdgeToPath(C.getActivePath(), PrevLoc, Call->callReturn); PrevLoc.invalidate(); } - auto *P = C.get(); - PD.getActivePath().push_front(std::move(C)); + auto *P = Call.get(); + C.getActivePath().push_front(std::move(Call)); // Make the contents of the call the active path for now. - PD.pushActivePath(&P->path); - CallStack.push_back(StackDiagPair(P, N)); + C.PD->pushActivePath(&P->path); + C.CallStack.push_back(CallWithEntry(P, C.getCurrentNode())); return; } if (auto PS = P.getAs<PostStmt>()) { - if (!AddPathEdges) + if (!C.shouldAddPathEdges()) return; - // For expressions, make sure we propagate the - // interesting symbols correctly. - if (const Expr *Ex = PS->getStmtAs<Expr>()) - reversePropagateIntererstingSymbols(*PDB.getBugReport(), IE, - N->getState().get(), Ex, - N->getLocationContext()); - // Add an edge. If this is an ObjCForCollectionStmt do // not add an edge here as it appears in the CFG both // as a terminator and as a terminator condition. if (!isa<ObjCForCollectionStmt>(PS->getStmt())) { PathDiagnosticLocation L = - PathDiagnosticLocation(PS->getStmt(), SM, PDB.LC); - addEdgeToPath(PD.getActivePath(), PrevLoc, L); + PathDiagnosticLocation(PS->getStmt(), SM, C.getCurrLocationContext()); + addEdgeToPath(C.getActivePath(), PrevLoc, L); } } else if (auto BE = P.getAs<BlockEdge>()) { - if (!AddPathEdges) { - generateMinimalDiagForBlockEdge(N, *BE, SM, PDB, PD); + if (!C.shouldAddPathEdges()) { + generateMinimalDiagForBlockEdge(C, *BE); return; } - // Does this represent entering a call? If so, look at propagating - // interesting symbols across call boundaries. - if (const ExplodedNode *NextNode = N->getFirstPred()) { - const LocationContext *CallerCtx = NextNode->getLocationContext(); - const LocationContext *CalleeCtx = PDB.LC; - if (CallerCtx != CalleeCtx && AddPathEdges) { - reversePropagateInterestingSymbols(*PDB.getBugReport(), IE, - N->getState().get(), CalleeCtx); - } - } - // 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); + PathDiagnosticLocation L(Loop, SM, C.getCurrLocationContext()); const Stmt *Body = nullptr; if (const auto *FS = dyn_cast<ForStmt>(Loop)) @@ -1200,27 +1255,27 @@ static void generatePathDiagnosticsForNode(const ExplodedNode *N, "of the loop"); p->setPrunable(true); - addEdgeToPath(PD.getActivePath(), PrevLoc, p->getLocation()); - PD.getActivePath().push_front(std::move(p)); + addEdgeToPath(C.getActivePath(), PrevLoc, p->getLocation()); + C.getActivePath().push_front(std::move(p)); if (const auto *CS = dyn_cast_or_null<CompoundStmt>(Body)) { - addEdgeToPath(PD.getActivePath(), PrevLoc, - PathDiagnosticLocation::createEndBrace(CS, SM)); + addEdgeToPath(C.getActivePath(), PrevLoc, + PathDiagnosticLocation::createEndBrace(CS, SM)); } } const CFGBlock *BSrc = BE->getSrc(); - ParentMap &PM = PDB.getParentMap(); + const ParentMap &PM = C.getParentMap(); if (const Stmt *Term = BSrc->getTerminatorStmt()) { // Are we jumping past the loop body without ever executing the // loop (because the condition was false)? if (isLoop(Term)) { const Stmt *TermCond = getTerminatorCondition(BSrc); - bool IsInLoopBody = - isInLoopBody(PM, getStmtBeforeCond(PM, TermCond, N), Term); + bool IsInLoopBody = isInLoopBody( + PM, getStmtBeforeCond(PM, TermCond, C.getCurrentNode()), Term); - const char *str = nullptr; + StringRef str; if (isJumpToFalseBranch(&*BE)) { if (!IsInLoopBody) { @@ -1236,31 +1291,41 @@ static void generatePathDiagnosticsForNode(const ExplodedNode *N, str = StrEnteringLoop; } - if (str) { - PathDiagnosticLocation L(TermCond ? TermCond : Term, SM, PDB.LC); + if (!str.empty()) { + PathDiagnosticLocation L(TermCond ? TermCond : Term, SM, + C.getCurrLocationContext()); auto PE = std::make_shared<PathDiagnosticEventPiece>(L, str); PE->setPrunable(true); - addEdgeToPath(PD.getActivePath(), PrevLoc, - PE->getLocation()); - PD.getActivePath().push_front(std::move(PE)); + addEdgeToPath(C.getActivePath(), PrevLoc, PE->getLocation()); + C.getActivePath().push_front(std::move(PE)); } } else if (isa<BreakStmt>(Term) || isa<ContinueStmt>(Term) || isa<GotoStmt>(Term)) { - PathDiagnosticLocation L(Term, SM, PDB.LC); - addEdgeToPath(PD.getActivePath(), PrevLoc, L); + PathDiagnosticLocation L(Term, SM, C.getCurrLocationContext()); + addEdgeToPath(C.getActivePath(), PrevLoc, L); } } } } static std::unique_ptr<PathDiagnostic> -generateEmptyDiagnosticForReport(BugReport *R, SourceManager &SM) { +generateDiagnosticForBasicReport(const BasicBugReport *R) { const BugType &BT = R->getBugType(); - return llvm::make_unique<PathDiagnostic>( - R->getBugType().getCheckName(), R->getDeclWithIssue(), - R->getBugType().getName(), R->getDescription(), - R->getShortDescription(/*UseFallback=*/false), BT.getCategory(), - R->getUniqueingLocation(), R->getUniqueingDecl(), + return std::make_unique<PathDiagnostic>( + BT.getCheckerName(), R->getDeclWithIssue(), BT.getDescription(), + R->getDescription(), R->getShortDescription(/*UseFallback=*/false), + BT.getCategory(), R->getUniqueingLocation(), R->getUniqueingDecl(), + std::make_unique<FilesToLineNumsMap>()); +} + +static std::unique_ptr<PathDiagnostic> +generateEmptyDiagnosticForReport(const PathSensitiveBugReport *R, + const SourceManager &SM) { + const BugType &BT = R->getBugType(); + return std::make_unique<PathDiagnostic>( + BT.getCheckerName(), R->getDeclWithIssue(), BT.getDescription(), + R->getDescription(), R->getShortDescription(/*UseFallback=*/false), + BT.getCategory(), R->getUniqueingLocation(), R->getUniqueingDecl(), findExecutedLines(SM, R->getErrorNode())); } @@ -1342,8 +1407,8 @@ using OptimizedCallsSet = llvm::DenseSet<const PathDiagnosticCallPiece *>; /// This avoids a "swoosh" effect, where an edge from a top-level statement A /// points to a sub-expression B.1 that's not at the start of B. In these cases, /// we'd like to see an edge from A to B, then another one from B to B.1. -static void addContextEdges(PathPieces &pieces, SourceManager &SM, - const ParentMap &PM, const LocationContext *LCtx) { +static void addContextEdges(PathPieces &pieces, const LocationContext *LC) { + const ParentMap &PM = LC->getParentMap(); PathPieces::iterator Prev = pieces.end(); for (PathPieces::iterator I = pieces.begin(), E = Prev; I != E; Prev = I, ++I) { @@ -1360,7 +1425,7 @@ static void addContextEdges(PathPieces &pieces, SourceManager &SM, while (NextSrcContext.isValid() && NextSrcContext.asStmt() != InnerStmt) { SrcContexts.push_back(NextSrcContext); InnerStmt = NextSrcContext.asStmt(); - NextSrcContext = getEnclosingStmtLocation(InnerStmt, SM, PM, LCtx, + NextSrcContext = getEnclosingStmtLocation(InnerStmt, LC, /*allowNested=*/true); } @@ -1373,7 +1438,7 @@ static void addContextEdges(PathPieces &pieces, SourceManager &SM, // We are looking at an edge. Is the destination within a larger // expression? PathDiagnosticLocation DstContext = - getEnclosingStmtLocation(Dst, SM, PM, LCtx, /*allowNested=*/true); + getEnclosingStmtLocation(Dst, LC, /*allowNested=*/true); if (!DstContext.isValid() || DstContext.asStmt() == Dst) break; @@ -1493,7 +1558,7 @@ static void simplifySimpleBranches(PathPieces &pieces) { /// If the locations in the range are not on the same line, returns None. /// /// Note that this does not do a precise user-visible character or column count. -static Optional<size_t> getLengthOnSingleLine(SourceManager &SM, +static Optional<size_t> getLengthOnSingleLine(const SourceManager &SM, SourceRange Range) { SourceRange ExpansionRange(SM.getExpansionLoc(Range.getBegin()), SM.getExpansionRange(Range.getEnd()).getEnd()); @@ -1523,7 +1588,7 @@ static Optional<size_t> getLengthOnSingleLine(SourceManager &SM, } /// \sa getLengthOnSingleLine(SourceManager, SourceRange) -static Optional<size_t> getLengthOnSingleLine(SourceManager &SM, +static Optional<size_t> getLengthOnSingleLine(const SourceManager &SM, const Stmt *S) { return getLengthOnSingleLine(SM, S->getSourceRange()); } @@ -1544,7 +1609,7 @@ static Optional<size_t> getLengthOnSingleLine(SourceManager &SM, /// - if there is an inlined call between the edges instead of a single event. /// - if the whole statement is large enough that having subexpression arrows /// might be helpful. -static void removeContextCycles(PathPieces &Path, SourceManager &SM) { +static void removeContextCycles(PathPieces &Path, const SourceManager &SM) { for (PathPieces::iterator I = Path.begin(), E = Path.end(); I != E; ) { // Pattern match the current piece and its successor. const auto *PieceI = dyn_cast<PathDiagnosticControlFlowPiece>(I->get()); @@ -1599,7 +1664,7 @@ static void removeContextCycles(PathPieces &Path, SourceManager &SM) { } /// Return true if X is contained by Y. -static bool lexicalContains(ParentMap &PM, const Stmt *X, const Stmt *Y) { +static bool lexicalContains(const ParentMap &PM, const Stmt *X, const Stmt *Y) { while (X) { if (X == Y) return true; @@ -1609,8 +1674,8 @@ static bool lexicalContains(ParentMap &PM, const Stmt *X, const Stmt *Y) { } // Remove short edges on the same line less than 3 columns in difference. -static void removePunyEdges(PathPieces &path, SourceManager &SM, - ParentMap &PM) { +static void removePunyEdges(PathPieces &path, const SourceManager &SM, + const ParentMap &PM) { bool erased = false; for (PathPieces::iterator I = path.begin(), E = path.end(); I != E; @@ -1685,13 +1750,13 @@ static void removeIdenticalEvents(PathPieces &path) { } } -static bool optimizeEdges(PathPieces &path, SourceManager &SM, - OptimizedCallsSet &OCS, - LocationContextMap &LCM) { +static bool optimizeEdges(const PathDiagnosticConstruct &C, PathPieces &path, + OptimizedCallsSet &OCS) { bool hasChanges = false; - const LocationContext *LC = LCM[&path]; + const LocationContext *LC = C.getLocationContextFor(&path); assert(LC); - ParentMap &PM = LC->getParentMap(); + const ParentMap &PM = LC->getParentMap(); + const SourceManager &SM = C.getSourceManager(); for (PathPieces::iterator I = path.begin(), E = path.end(); I != E; ) { // Optimize subpaths. @@ -1699,7 +1764,8 @@ static bool optimizeEdges(PathPieces &path, SourceManager &SM, // Record the fact that a call has been optimized so we only do the // effort once. if (!OCS.count(CallI)) { - while (optimizeEdges(CallI->path, SM, OCS, LCM)) {} + while (optimizeEdges(C, CallI->path, OCS)) { + } OCS.insert(CallI); } ++I; @@ -1845,7 +1911,7 @@ static bool optimizeEdges(PathPieces &path, SourceManager &SM, if (!hasChanges) { // Adjust edges into subexpressions to make them more uniform // and aesthetically pleasing. - addContextEdges(path, SM, PM, LC); + addContextEdges(path, LC); // Remove "cyclical" edges that include one or more context edges. removeContextCycles(path, SM); // Hoist edges originating from branch conditions to branches @@ -1866,27 +1932,24 @@ static bool optimizeEdges(PathPieces &path, SourceManager &SM, /// statement had an invalid source location), this function does nothing. // FIXME: We should just generate invalid edges anyway and have the optimizer // deal with them. -static void dropFunctionEntryEdge(PathPieces &Path, LocationContextMap &LCM, - SourceManager &SM) { +static void dropFunctionEntryEdge(const PathDiagnosticConstruct &C, + PathPieces &Path) { const auto *FirstEdge = dyn_cast<PathDiagnosticControlFlowPiece>(Path.front().get()); if (!FirstEdge) return; - const Decl *D = LCM[&Path]->getDecl(); - PathDiagnosticLocation EntryLoc = PathDiagnosticLocation::createBegin(D, SM); + const Decl *D = C.getLocationContextFor(&Path)->getDecl(); + PathDiagnosticLocation EntryLoc = + PathDiagnosticLocation::createBegin(D, C.getSourceManager()); if (FirstEdge->getStartLocation() != EntryLoc) return; Path.pop_front(); } -using VisitorsDiagnosticsTy = llvm::DenseMap<const ExplodedNode *, - std::vector<std::shared_ptr<PathDiagnosticPiece>>>; - /// Populate executes lines with lines containing at least one diagnostics. -static void updateExecutedLinesWithDiagnosticPieces( - PathDiagnostic &PD) { +static void updateExecutedLinesWithDiagnosticPieces(PathDiagnostic &PD) { PathPieces path = PD.path.flatten(/*ShouldFlattenMacros=*/true); FilesToLineNumsMap &ExecutedLines = PD.getExecutedLines(); @@ -1900,56 +1963,61 @@ static void updateExecutedLinesWithDiagnosticPieces( } } -/// This function is responsible for generating diagnostic pieces that are -/// *not* provided by bug report visitors. -/// These diagnostics may differ depending on the consumer's settings, -/// and are therefore constructed separately for each consumer. -/// -/// There are two path diagnostics generation modes: with adding edges (used -/// for plists) and without (used for HTML and text). -/// When edges are added (\p ActiveScheme is Extensive), -/// the path is modified to insert artificially generated -/// edges. -/// Otherwise, more detailed diagnostics is emitted for block edges, explaining -/// the transitions in words. -static std::unique_ptr<PathDiagnostic> generatePathDiagnosticForConsumer( - PathDiagnosticConsumer::PathGenerationScheme ActiveScheme, - PathDiagnosticBuilder &PDB, - const ExplodedNode *ErrorNode, - const VisitorsDiagnosticsTy &VisitorsDiagnostics) { - - bool GenerateDiagnostics = (ActiveScheme != PathDiagnosticConsumer::None); - bool AddPathEdges = (ActiveScheme == PathDiagnosticConsumer::Extensive); - SourceManager &SM = PDB.getSourceManager(); - BugReport *R = PDB.getBugReport(); - AnalyzerOptions &Opts = PDB.getBugReporter().getAnalyzerOptions(); - StackDiagVector CallStack; - InterestingExprs IE; - LocationContextMap LCM; - std::unique_ptr<PathDiagnostic> PD = generateEmptyDiagnosticForReport(R, SM); - - if (GenerateDiagnostics) { - auto EndNotes = VisitorsDiagnostics.find(ErrorNode); - std::shared_ptr<PathDiagnosticPiece> LastPiece; - if (EndNotes != VisitorsDiagnostics.end()) { - assert(!EndNotes->second.empty()); - LastPiece = EndNotes->second[0]; - } else { - LastPiece = BugReporterVisitor::getDefaultEndPath(PDB, ErrorNode, *R); - } - PD->setEndOfPath(LastPiece); +PathDiagnosticConstruct::PathDiagnosticConstruct( + const PathDiagnosticConsumer *PDC, const ExplodedNode *ErrorNode, + const PathSensitiveBugReport *R) + : Consumer(PDC), CurrentNode(ErrorNode), + SM(CurrentNode->getCodeDecl().getASTContext().getSourceManager()), + PD(generateEmptyDiagnosticForReport(R, getSourceManager())) { + LCM[&PD->getActivePath()] = ErrorNode->getLocationContext(); +} + +PathDiagnosticBuilder::PathDiagnosticBuilder( + BugReporterContext BRC, std::unique_ptr<ExplodedGraph> BugPath, + PathSensitiveBugReport *r, const ExplodedNode *ErrorNode, + std::unique_ptr<VisitorsDiagnosticsTy> VisitorsDiagnostics) + : BugReporterContext(BRC), BugPath(std::move(BugPath)), R(r), + ErrorNode(ErrorNode), + VisitorsDiagnostics(std::move(VisitorsDiagnostics)) {} + +std::unique_ptr<PathDiagnostic> +PathDiagnosticBuilder::generate(const PathDiagnosticConsumer *PDC) const { + PathDiagnosticConstruct Construct(PDC, ErrorNode, R); + + const SourceManager &SM = getSourceManager(); + const AnalyzerOptions &Opts = getAnalyzerOptions(); + StringRef ErrorTag = ErrorNode->getLocation().getTag()->getTagDescription(); + + // See whether we need to silence the checker/package. + // FIXME: This will not work if the report was emitted with an incorrect tag. + for (const std::string &CheckerOrPackage : Opts.SilencedCheckersAndPackages) { + if (ErrorTag.startswith(CheckerOrPackage)) + return nullptr; } - PathDiagnosticLocation PrevLoc = PD->getLocation(); - const ExplodedNode *NextNode = ErrorNode->getFirstPred(); - while (NextNode) { - if (GenerateDiagnostics) - generatePathDiagnosticsForNode( - NextNode, *PD, PrevLoc, PDB, LCM, CallStack, IE, AddPathEdges); + if (!PDC->shouldGenerateDiagnostics()) + return generateEmptyDiagnosticForReport(R, getSourceManager()); + + // Construct the final (warning) event for the bug report. + auto EndNotes = VisitorsDiagnostics->find(ErrorNode); + PathDiagnosticPieceRef LastPiece; + if (EndNotes != VisitorsDiagnostics->end()) { + assert(!EndNotes->second.empty()); + LastPiece = EndNotes->second[0]; + } else { + LastPiece = BugReporterVisitor::getDefaultEndPath(*this, ErrorNode, + *getBugReport()); + } + Construct.PD->setEndOfPath(LastPiece); + + PathDiagnosticLocation PrevLoc = Construct.PD->getLocation(); + // From the error node to the root, ascend the bug path and construct the bug + // report. + while (Construct.ascendToPrevNode()) { + generatePathDiagnosticsForNode(Construct, PrevLoc); - auto VisitorNotes = VisitorsDiagnostics.find(NextNode); - NextNode = NextNode->getFirstPred(); - if (!GenerateDiagnostics || VisitorNotes == VisitorsDiagnostics.end()) + auto VisitorNotes = VisitorsDiagnostics->find(Construct.getCurrentNode()); + if (VisitorNotes == VisitorsDiagnostics->end()) continue; // This is a workaround due to inability to put shared PathDiagnosticPiece @@ -1957,74 +2025,74 @@ static std::unique_ptr<PathDiagnostic> generatePathDiagnosticForConsumer( std::set<llvm::FoldingSetNodeID> DeduplicationSet; // Add pieces from custom visitors. - for (const auto &Note : VisitorNotes->second) { + for (const PathDiagnosticPieceRef &Note : VisitorNotes->second) { llvm::FoldingSetNodeID ID; Note->Profile(ID); - auto P = DeduplicationSet.insert(ID); - if (!P.second) + if (!DeduplicationSet.insert(ID).second) continue; - if (AddPathEdges) - addEdgeToPath(PD->getActivePath(), PrevLoc, Note->getLocation()); - updateStackPiecesWithMessage(*Note, CallStack); - PD->getActivePath().push_front(Note); + if (PDC->shouldAddPathEdges()) + addEdgeToPath(Construct.getActivePath(), PrevLoc, Note->getLocation()); + updateStackPiecesWithMessage(Note, Construct.CallStack); + Construct.getActivePath().push_front(Note); } } - if (AddPathEdges) { + if (PDC->shouldAddPathEdges()) { // Add an edge to the start of the function. // We'll prune it out later, but it helps make diagnostics more uniform. - const StackFrameContext *CalleeLC = PDB.LC->getStackFrame(); + const StackFrameContext *CalleeLC = + Construct.getLocationContextForActivePath()->getStackFrame(); const Decl *D = CalleeLC->getDecl(); - addEdgeToPath(PD->getActivePath(), PrevLoc, + addEdgeToPath(Construct.getActivePath(), PrevLoc, PathDiagnosticLocation::createBegin(D, SM)); } // Finally, prune the diagnostic path of uninteresting stuff. - if (!PD->path.empty()) { + if (!Construct.PD->path.empty()) { if (R->shouldPrunePath() && Opts.ShouldPrunePaths) { bool stillHasNotes = - removeUnneededCalls(PD->getMutablePieces(), R, LCM); + removeUnneededCalls(Construct, Construct.getMutablePieces(), R); assert(stillHasNotes); (void)stillHasNotes; } // Remove pop-up notes if needed. if (!Opts.ShouldAddPopUpNotes) - removePopUpNotes(PD->getMutablePieces()); + removePopUpNotes(Construct.getMutablePieces()); // Redirect all call pieces to have valid locations. - adjustCallLocations(PD->getMutablePieces()); - removePiecesWithInvalidLocations(PD->getMutablePieces()); + adjustCallLocations(Construct.getMutablePieces()); + removePiecesWithInvalidLocations(Construct.getMutablePieces()); - if (AddPathEdges) { + if (PDC->shouldAddPathEdges()) { // Reduce the number of edges from a very conservative set // to an aesthetically pleasing subset that conveys the // necessary information. OptimizedCallsSet OCS; - while (optimizeEdges(PD->getMutablePieces(), SM, OCS, LCM)) {} + while (optimizeEdges(Construct, Construct.getMutablePieces(), OCS)) { + } // Drop the very first function-entry edge. It's not really necessary // for top-level functions. - dropFunctionEntryEdge(PD->getMutablePieces(), LCM, SM); + dropFunctionEntryEdge(Construct, Construct.getMutablePieces()); } // Remove messages that are basically the same, and edges that may not // make sense. // We have to do this after edge optimization in the Extensive mode. - removeRedundantMsgs(PD->getMutablePieces()); - removeEdgesToDefaultInitializers(PD->getMutablePieces()); + removeRedundantMsgs(Construct.getMutablePieces()); + removeEdgesToDefaultInitializers(Construct.getMutablePieces()); } - if (GenerateDiagnostics && Opts.ShouldDisplayMacroExpansions) - CompactMacroExpandedPieces(PD->getMutablePieces(), SM); + if (Opts.ShouldDisplayMacroExpansions) + CompactMacroExpandedPieces(Construct.getMutablePieces(), SM); - return PD; + return std::move(Construct.PD); } - //===----------------------------------------------------------------------===// // Methods for BugType and subclasses. //===----------------------------------------------------------------------===// @@ -2037,9 +2105,8 @@ void BuiltinBug::anchor() {} // Methods for BugReport and subclasses. //===----------------------------------------------------------------------===// -void BugReport::NodeResolver::anchor() {} - -void BugReport::addVisitor(std::unique_ptr<BugReporterVisitor> visitor) { +void PathSensitiveBugReport::addVisitor( + std::unique_ptr<BugReporterVisitor> visitor) { if (!visitor) return; @@ -2054,20 +2121,11 @@ void BugReport::addVisitor(std::unique_ptr<BugReporterVisitor> visitor) { Callbacks.push_back(std::move(visitor)); } -void BugReport::clearVisitors() { +void PathSensitiveBugReport::clearVisitors() { Callbacks.clear(); } -BugReport::~BugReport() { - while (!interestingSymbols.empty()) { - popInterestingSymbolsAndRegions(); - } -} - -const Decl *BugReport::getDeclWithIssue() const { - if (DeclWithIssue) - return DeclWithIssue; - +const Decl *PathSensitiveBugReport::getDeclWithIssue() const { const ExplodedNode *N = getErrorNode(); if (!N) return nullptr; @@ -2076,17 +2134,34 @@ const Decl *BugReport::getDeclWithIssue() const { return LC->getStackFrame()->getDecl(); } -void BugReport::Profile(llvm::FoldingSetNodeID& hash) const { +void BasicBugReport::Profile(llvm::FoldingSetNodeID& hash) const { + hash.AddInteger(static_cast<int>(getKind())); + hash.AddPointer(&BT); + hash.AddString(Description); + assert(Location.isValid()); + Location.Profile(hash); + + for (SourceRange range : Ranges) { + if (!range.isValid()) + continue; + hash.AddInteger(range.getBegin().getRawEncoding()); + hash.AddInteger(range.getEnd().getRawEncoding()); + } +} + +void PathSensitiveBugReport::Profile(llvm::FoldingSetNodeID &hash) const { + hash.AddInteger(static_cast<int>(getKind())); hash.AddPointer(&BT); hash.AddString(Description); PathDiagnosticLocation UL = getUniqueingLocation(); if (UL.isValid()) { UL.Profile(hash); - } else if (Location.isValid()) { - Location.Profile(hash); } else { - assert(ErrorNode); - hash.AddPointer(GetCurrentOrPreviousStmt(ErrorNode)); + // TODO: The statement may be null if the report was emitted before any + // statements were executed. In particular, some checkers by design + // occasionally emit their reports in empty functions (that have no + // statements in their body). Do we profile correctly in this case? + hash.AddPointer(ErrorNode->getCurrentOrPreviousStmtForDiagnostics()); } for (SourceRange range : Ranges) { @@ -2097,96 +2172,141 @@ void BugReport::Profile(llvm::FoldingSetNodeID& hash) const { } } -void BugReport::markInteresting(SymbolRef sym) { +template <class T> +static void insertToInterestingnessMap( + llvm::DenseMap<T, bugreporter::TrackingKind> &InterestingnessMap, T Val, + bugreporter::TrackingKind TKind) { + auto Result = InterestingnessMap.insert({Val, TKind}); + + if (Result.second) + return; + + // Even if this symbol/region was already marked as interesting as a + // condition, if we later mark it as interesting again but with + // thorough tracking, overwrite it. Entities marked with thorough + // interestiness are the most important (or most interesting, if you will), + // and we wouldn't like to downplay their importance. + + switch (TKind) { + case bugreporter::TrackingKind::Thorough: + Result.first->getSecond() = bugreporter::TrackingKind::Thorough; + return; + case bugreporter::TrackingKind::Condition: + return; + } + + llvm_unreachable( + "BugReport::markInteresting currently can only handle 2 different " + "tracking kinds! Please define what tracking kind should this entitiy" + "have, if it was already marked as interesting with a different kind!"); +} + +void PathSensitiveBugReport::markInteresting(SymbolRef sym, + bugreporter::TrackingKind TKind) { if (!sym) return; - getInterestingSymbols().insert(sym); + insertToInterestingnessMap(InterestingSymbols, sym, TKind); if (const auto *meta = dyn_cast<SymbolMetadata>(sym)) - getInterestingRegions().insert(meta->getRegion()); + markInteresting(meta->getRegion(), TKind); } -void BugReport::markInteresting(const MemRegion *R) { +void PathSensitiveBugReport::markInteresting(const MemRegion *R, + bugreporter::TrackingKind TKind) { if (!R) return; R = R->getBaseRegion(); - getInterestingRegions().insert(R); + insertToInterestingnessMap(InterestingRegions, R, TKind); if (const auto *SR = dyn_cast<SymbolicRegion>(R)) - getInterestingSymbols().insert(SR->getSymbol()); + markInteresting(SR->getSymbol(), TKind); } -void BugReport::markInteresting(SVal V) { - markInteresting(V.getAsRegion()); - markInteresting(V.getAsSymbol()); +void PathSensitiveBugReport::markInteresting(SVal V, + bugreporter::TrackingKind TKind) { + markInteresting(V.getAsRegion(), TKind); + markInteresting(V.getAsSymbol(), TKind); } -void BugReport::markInteresting(const LocationContext *LC) { +void PathSensitiveBugReport::markInteresting(const LocationContext *LC) { if (!LC) return; InterestingLocationContexts.insert(LC); } -bool BugReport::isInteresting(SVal V) { - return isInteresting(V.getAsRegion()) || isInteresting(V.getAsSymbol()); +Optional<bugreporter::TrackingKind> +PathSensitiveBugReport::getInterestingnessKind(SVal V) const { + auto RKind = getInterestingnessKind(V.getAsRegion()); + auto SKind = getInterestingnessKind(V.getAsSymbol()); + if (!RKind) + return SKind; + if (!SKind) + return RKind; + + // If either is marked with throrough tracking, return that, we wouldn't like + // to downplay a note's importance by 'only' mentioning it as a condition. + switch(*RKind) { + case bugreporter::TrackingKind::Thorough: + return RKind; + case bugreporter::TrackingKind::Condition: + return SKind; + } + + llvm_unreachable( + "BugReport::getInterestingnessKind currently can only handle 2 different " + "tracking kinds! Please define what tracking kind should we return here " + "when the kind of getAsRegion() and getAsSymbol() is different!"); + return None; } -bool BugReport::isInteresting(SymbolRef sym) { +Optional<bugreporter::TrackingKind> +PathSensitiveBugReport::getInterestingnessKind(SymbolRef sym) const { if (!sym) - return false; + return None; // We don't currently consider metadata symbols to be interesting // even if we know their region is interesting. Is that correct behavior? - return getInterestingSymbols().count(sym); + auto It = InterestingSymbols.find(sym); + if (It == InterestingSymbols.end()) + return None; + return It->getSecond(); } -bool BugReport::isInteresting(const MemRegion *R) { +Optional<bugreporter::TrackingKind> +PathSensitiveBugReport::getInterestingnessKind(const MemRegion *R) const { if (!R) - return false; - R = R->getBaseRegion(); - bool b = getInterestingRegions().count(R); - if (b) - return true; - if (const auto *SR = dyn_cast<SymbolicRegion>(R)) - return getInterestingSymbols().count(SR->getSymbol()); - return false; -} + return None; -bool BugReport::isInteresting(const LocationContext *LC) { - if (!LC) - return false; - return InterestingLocationContexts.count(LC); -} + R = R->getBaseRegion(); + auto It = InterestingRegions.find(R); + if (It != InterestingRegions.end()) + return It->getSecond(); -void BugReport::lazyInitializeInterestingSets() { - if (interestingSymbols.empty()) { - interestingSymbols.push_back(new Symbols()); - interestingRegions.push_back(new Regions()); - } + if (const auto *SR = dyn_cast<SymbolicRegion>(R)) + return getInterestingnessKind(SR->getSymbol()); + return None; } -BugReport::Symbols &BugReport::getInterestingSymbols() { - lazyInitializeInterestingSets(); - return *interestingSymbols.back(); +bool PathSensitiveBugReport::isInteresting(SVal V) const { + return getInterestingnessKind(V).hasValue(); } -BugReport::Regions &BugReport::getInterestingRegions() { - lazyInitializeInterestingSets(); - return *interestingRegions.back(); +bool PathSensitiveBugReport::isInteresting(SymbolRef sym) const { + return getInterestingnessKind(sym).hasValue(); } -void BugReport::pushInterestingSymbolsAndRegions() { - interestingSymbols.push_back(new Symbols(getInterestingSymbols())); - interestingRegions.push_back(new Regions(getInterestingRegions())); +bool PathSensitiveBugReport::isInteresting(const MemRegion *R) const { + return getInterestingnessKind(R).hasValue(); } -void BugReport::popInterestingSymbolsAndRegions() { - delete interestingSymbols.pop_back_val(); - delete interestingRegions.pop_back_val(); +bool PathSensitiveBugReport::isInteresting(const LocationContext *LC) const { + if (!LC) + return false; + return InterestingLocationContexts.count(LC); } -const Stmt *BugReport::getStmt() const { +const Stmt *PathSensitiveBugReport::getStmt() const { if (!ErrorNode) return nullptr; @@ -2196,59 +2316,83 @@ const Stmt *BugReport::getStmt() const { if (Optional<BlockEntrance> BE = ProgP.getAs<BlockEntrance>()) { CFGBlock &Exit = ProgP.getLocationContext()->getCFG()->getExit(); if (BE->getBlock() == &Exit) - S = GetPreviousStmt(ErrorNode); + S = ErrorNode->getPreviousStmtForDiagnostics(); } if (!S) - S = PathDiagnosticLocation::getStmt(ErrorNode); + S = ErrorNode->getStmtForDiagnostics(); return S; } -llvm::iterator_range<BugReport::ranges_iterator> BugReport::getRanges() { +ArrayRef<SourceRange> +PathSensitiveBugReport::getRanges() const { // If no custom ranges, add the range of the statement corresponding to // the error node. - if (Ranges.empty()) { - if (const auto *E = dyn_cast_or_null<Expr>(getStmt())) - addRange(E->getSourceRange()); - else - return llvm::make_range(ranges_iterator(), ranges_iterator()); + if (Ranges.empty() && isa_and_nonnull<Expr>(getStmt())) + return ErrorNodeRange; + + return Ranges; +} + +PathDiagnosticLocation +PathSensitiveBugReport::getLocation() const { + assert(ErrorNode && "Cannot create a location with a null node."); + const Stmt *S = ErrorNode->getStmtForDiagnostics(); + ProgramPoint P = ErrorNode->getLocation(); + const LocationContext *LC = P.getLocationContext(); + SourceManager &SM = + ErrorNode->getState()->getStateManager().getContext().getSourceManager(); + + if (!S) { + // If this is an implicit call, return the implicit call point location. + if (Optional<PreImplicitCall> PIE = P.getAs<PreImplicitCall>()) + return PathDiagnosticLocation(PIE->getLocation(), SM); + if (auto FE = P.getAs<FunctionExitPoint>()) { + if (const ReturnStmt *RS = FE->getStmt()) + return PathDiagnosticLocation::createBegin(RS, SM, LC); + } + S = ErrorNode->getNextStmtForDiagnostics(); } - // User-specified absence of range info. - if (Ranges.size() == 1 && !Ranges.begin()->isValid()) - return llvm::make_range(ranges_iterator(), ranges_iterator()); + if (S) { + // For member expressions, return the location of the '.' or '->'. + if (const auto *ME = dyn_cast<MemberExpr>(S)) + return PathDiagnosticLocation::createMemberLoc(ME, SM); - return llvm::make_range(Ranges.begin(), Ranges.end()); -} + // For binary operators, return the location of the operator. + if (const auto *B = dyn_cast<BinaryOperator>(S)) + return PathDiagnosticLocation::createOperatorLoc(B, SM); + + if (P.getAs<PostStmtPurgeDeadSymbols>()) + return PathDiagnosticLocation::createEnd(S, SM, LC); + + if (S->getBeginLoc().isValid()) + return PathDiagnosticLocation(S, SM, LC); -PathDiagnosticLocation BugReport::getLocation(const SourceManager &SM) const { - if (ErrorNode) { - assert(!Location.isValid() && - "Either Location or ErrorNode should be specified but not both."); - return PathDiagnosticLocation::createEndOfPath(ErrorNode, SM); + return PathDiagnosticLocation( + PathDiagnosticLocation::getValidSourceLocation(S, LC), SM); } - assert(Location.isValid()); - return Location; + return PathDiagnosticLocation::createDeclEnd(ErrorNode->getLocationContext(), + SM); } //===----------------------------------------------------------------------===// // Methods for BugReporter and subclasses. //===----------------------------------------------------------------------===// -BugReportEquivClass::~BugReportEquivClass() = default; - -GRBugReporter::~GRBugReporter() = default; - -BugReporterData::~BugReporterData() = default; - -ExplodedGraph &GRBugReporter::getGraph() { return Eng.getGraph(); } +const ExplodedGraph &PathSensitiveBugReporter::getGraph() const { + return Eng.getGraph(); +} -ProgramStateManager& -GRBugReporter::getStateManager() { return Eng.getStateManager(); } +ProgramStateManager &PathSensitiveBugReporter::getStateManager() const { + return Eng.getStateManager(); +} BugReporter::~BugReporter() { - FlushReports(); + // Make sure reports are flushed. + assert(StrBugTypes.empty() && + "Destroying BugReporter before diagnostics are emitted!"); // Free the bug reports we are tracking. for (const auto I : EQClassesVector) @@ -2256,9 +2400,6 @@ BugReporter::~BugReporter() { } void BugReporter::FlushReports() { - if (BugTypes.isEmpty()) - return; - // We need to flush reports in deterministic order to ensure the order // of the reports is consistent between runs. for (const auto EQ : EQClassesVector) @@ -2269,9 +2410,6 @@ void BugReporter::FlushReports() { // FIXME: There are leaks from checkers that assume that the BugTypes they // create will be destroyed by the BugReporter. llvm::DeleteContainerSeconds(StrBugTypes); - - // Remove all references to the BugType objects. - BugTypes = F.getEmptySet(); } //===----------------------------------------------------------------------===// @@ -2280,29 +2418,32 @@ void BugReporter::FlushReports() { namespace { -/// A wrapper around a report graph, which contains only a single path, and its -/// node maps. -class ReportGraph { +/// A wrapper around an ExplodedGraph that contains a single path from the root +/// to the error node. +class BugPathInfo { public: - InterExplodedGraphMap BackMap; - std::unique_ptr<ExplodedGraph> Graph; + std::unique_ptr<ExplodedGraph> BugPath; + PathSensitiveBugReport *Report; const ExplodedNode *ErrorNode; - size_t Index; }; -/// A wrapper around a trimmed graph and its node maps. -class TrimmedGraph { - InterExplodedGraphMap InverseMap; +/// A wrapper around an ExplodedGraph whose leafs are all error nodes. Can +/// conveniently retrieve bug paths from a single error node to the root. +class BugPathGetter { + std::unique_ptr<ExplodedGraph> TrimmedGraph; using PriorityMapTy = llvm::DenseMap<const ExplodedNode *, unsigned>; + /// Assign each node with its distance from the root. PriorityMapTy PriorityMap; - using NodeIndexPair = std::pair<const ExplodedNode *, size_t>; - - SmallVector<NodeIndexPair, 32> ReportNodes; + /// Since the getErrorNode() or BugReport refers to the original ExplodedGraph, + /// we need to pair it to the error node of the constructed trimmed graph. + using ReportNewNodePair = + std::pair<PathSensitiveBugReport *, const ExplodedNode *>; + SmallVector<ReportNewNodePair, 32> ReportNodes; - std::unique_ptr<ExplodedGraph> G; + BugPathInfo CurrentBugPath; /// A helper class for sorting ExplodedNodes by priority. template <bool Descending> @@ -2326,37 +2467,48 @@ class TrimmedGraph { : LI->second < RI->second; } - bool operator()(const NodeIndexPair &LHS, const NodeIndexPair &RHS) const { - return (*this)(LHS.first, RHS.first); + bool operator()(const ReportNewNodePair &LHS, + const ReportNewNodePair &RHS) const { + return (*this)(LHS.second, RHS.second); } }; public: - TrimmedGraph(const ExplodedGraph *OriginalGraph, - ArrayRef<const ExplodedNode *> Nodes); + BugPathGetter(const ExplodedGraph *OriginalGraph, + ArrayRef<PathSensitiveBugReport *> &bugReports); - bool popNextReportGraph(ReportGraph &GraphWrapper); + BugPathInfo *getNextBugPath(); }; } // namespace -TrimmedGraph::TrimmedGraph(const ExplodedGraph *OriginalGraph, - ArrayRef<const ExplodedNode *> Nodes) { +BugPathGetter::BugPathGetter(const ExplodedGraph *OriginalGraph, + ArrayRef<PathSensitiveBugReport *> &bugReports) { + SmallVector<const ExplodedNode *, 32> Nodes; + for (const auto I : bugReports) { + assert(I->isValid() && + "We only allow BugReporterVisitors and BugReporter itself to " + "invalidate reports!"); + Nodes.emplace_back(I->getErrorNode()); + } + // The trimmed graph is created in the body of the constructor to ensure // that the DenseMaps have been initialized already. InterExplodedGraphMap ForwardMap; - G = OriginalGraph->trim(Nodes, &ForwardMap, &InverseMap); + TrimmedGraph = OriginalGraph->trim(Nodes, &ForwardMap); // Find the (first) error node in the trimmed graph. We just need to consult // the node map which maps from nodes in the original graph to nodes // in the new graph. llvm::SmallPtrSet<const ExplodedNode *, 32> RemainingNodes; - for (unsigned i = 0, count = Nodes.size(); i < count; ++i) { - if (const ExplodedNode *NewNode = ForwardMap.lookup(Nodes[i])) { - ReportNodes.push_back(std::make_pair(NewNode, i)); - RemainingNodes.insert(NewNode); - } + for (PathSensitiveBugReport *Report : bugReports) { + const ExplodedNode *NewNode = ForwardMap.lookup(Report->getErrorNode()); + assert(NewNode && + "Failed to construct a trimmed graph that contains this error " + "node!"); + ReportNodes.emplace_back(Report, NewNode); + RemainingNodes.insert(NewNode); } assert(!RemainingNodes.empty() && "No error node found in the trimmed graph"); @@ -2364,8 +2516,8 @@ TrimmedGraph::TrimmedGraph(const ExplodedGraph *OriginalGraph, // Perform a forward BFS to find all the shortest paths. std::queue<const ExplodedNode *> WS; - assert(G->num_roots() == 1); - WS.push(*G->roots_begin()); + assert(TrimmedGraph->num_roots() == 1); + WS.push(*TrimmedGraph->roots_begin()); unsigned Priority = 0; while (!WS.empty()) { @@ -2374,8 +2526,7 @@ TrimmedGraph::TrimmedGraph(const ExplodedGraph *OriginalGraph, PriorityMapTy::iterator PriorityEntry; bool IsNew; - std::tie(PriorityEntry, IsNew) = - PriorityMap.insert(std::make_pair(Node, Priority)); + std::tie(PriorityEntry, IsNew) = PriorityMap.insert({Node, Priority}); ++Priority; if (!IsNew) { @@ -2387,29 +2538,26 @@ TrimmedGraph::TrimmedGraph(const ExplodedGraph *OriginalGraph, if (RemainingNodes.empty()) break; - for (ExplodedNode::const_pred_iterator I = Node->succ_begin(), - E = Node->succ_end(); - I != E; ++I) - WS.push(*I); + for (const ExplodedNode *Succ : Node->succs()) + WS.push(Succ); } // Sort the error paths from longest to shortest. llvm::sort(ReportNodes, PriorityCompare<true>(PriorityMap)); } -bool TrimmedGraph::popNextReportGraph(ReportGraph &GraphWrapper) { +BugPathInfo *BugPathGetter::getNextBugPath() { if (ReportNodes.empty()) - return false; + return nullptr; const ExplodedNode *OrigN; - std::tie(OrigN, GraphWrapper.Index) = ReportNodes.pop_back_val(); + std::tie(CurrentBugPath.Report, OrigN) = ReportNodes.pop_back_val(); assert(PriorityMap.find(OrigN) != PriorityMap.end() && "error node not accessible from root"); - // Create a new graph with a single path. This is the graph - // that will be returned to the caller. - auto GNew = llvm::make_unique<ExplodedGraph>(); - GraphWrapper.BackMap.clear(); + // Create a new graph with a single path. This is the graph that will be + // returned to the caller. + auto GNew = std::make_unique<ExplodedGraph>(); // Now walk from the error node up the BFS path, always taking the // predeccessor with the lowest number. @@ -2417,19 +2565,15 @@ bool TrimmedGraph::popNextReportGraph(ReportGraph &GraphWrapper) { while (true) { // Create the equivalent node in the new graph with the same state // and location. - ExplodedNode *NewN = GNew->createUncachedNode(OrigN->getLocation(), OrigN->getState(), - OrigN->isSink()); - - // Store the mapping to the original node. - InterExplodedGraphMap::const_iterator IMitr = InverseMap.find(OrigN); - assert(IMitr != InverseMap.end() && "No mapping to original node."); - GraphWrapper.BackMap[NewN] = IMitr->second; + ExplodedNode *NewN = GNew->createUncachedNode( + OrigN->getLocation(), OrigN->getState(), + OrigN->getID(), OrigN->isSink()); // Link up the new node with the previous node. if (Succ) Succ->addPredecessor(NewN, *GNew); else - GraphWrapper.ErrorNode = NewN; + CurrentBugPath.ErrorNode = NewN; Succ = NewN; @@ -2442,23 +2586,22 @@ bool TrimmedGraph::popNextReportGraph(ReportGraph &GraphWrapper) { // Find the next predeccessor node. We choose the node that is marked // with the lowest BFS number. OrigN = *std::min_element(OrigN->pred_begin(), OrigN->pred_end(), - PriorityCompare<false>(PriorityMap)); + PriorityCompare<false>(PriorityMap)); } - GraphWrapper.Graph = std::move(GNew); + CurrentBugPath.BugPath = std::move(GNew); - return true; + return &CurrentBugPath; } /// CompactMacroExpandedPieces - This function postprocesses a PathDiagnostic /// object and collapses PathDiagosticPieces that are expanded by macros. static void CompactMacroExpandedPieces(PathPieces &path, const SourceManager& SM) { - using MacroStackTy = - std::vector< - std::pair<std::shared_ptr<PathDiagnosticMacroPiece>, SourceLocation>>; + using MacroStackTy = std::vector< + std::pair<std::shared_ptr<PathDiagnosticMacroPiece>, SourceLocation>>; - using PiecesTy = std::vector<std::shared_ptr<PathDiagnosticPiece>>; + using PiecesTy = std::vector<PathDiagnosticPieceRef>; MacroStackTy MacroStack; PiecesTy Pieces; @@ -2548,33 +2691,39 @@ static void CompactMacroExpandedPieces(PathPieces &path, /// Notes associated with {@code ErrorNode} are generated using /// {@code getEndPath}, and the rest are generated with {@code VisitNode}. static std::unique_ptr<VisitorsDiagnosticsTy> -generateVisitorsDiagnostics(BugReport *R, const ExplodedNode *ErrorNode, +generateVisitorsDiagnostics(PathSensitiveBugReport *R, + const ExplodedNode *ErrorNode, BugReporterContext &BRC) { - auto Notes = llvm::make_unique<VisitorsDiagnosticsTy>(); - BugReport::VisitorList visitors; + std::unique_ptr<VisitorsDiagnosticsTy> Notes = + std::make_unique<VisitorsDiagnosticsTy>(); + PathSensitiveBugReport::VisitorList visitors; // Run visitors on all nodes starting from the node *before* the last one. // The last node is reserved for notes generated with {@code getEndPath}. const ExplodedNode *NextNode = ErrorNode->getFirstPred(); while (NextNode) { - // At each iteration, move all visitors from report to visitor list. - for (BugReport::visitor_iterator I = R->visitor_begin(), - E = R->visitor_end(); - I != E; ++I) { - visitors.push_back(std::move(*I)); - } + // At each iteration, move all visitors from report to visitor list. This is + // important, because the Profile() functions of the visitors make sure that + // a visitor isn't added multiple times for the same node, but it's fine + // to add the a visitor with Profile() for different nodes (e.g. tracking + // a region at different points of the symbolic execution). + for (std::unique_ptr<BugReporterVisitor> &Visitor : R->visitors()) + visitors.push_back(std::move(Visitor)); + R->clearVisitors(); const ExplodedNode *Pred = NextNode->getFirstPred(); if (!Pred) { - std::shared_ptr<PathDiagnosticPiece> LastPiece; + PathDiagnosticPieceRef LastPiece; for (auto &V : visitors) { V->finalizeVisitor(BRC, ErrorNode, *R); if (auto Piece = V->getEndPath(BRC, ErrorNode, *R)) { assert(!LastPiece && "There can only be one final piece in a diagnostic."); + assert(Piece->getKind() == PathDiagnosticPiece::Kind::Event && + "The final piece must contain a message!"); LastPiece = std::move(Piece); (*Notes)[ErrorNode].push_back(LastPiece); } @@ -2597,127 +2746,81 @@ generateVisitorsDiagnostics(BugReport *R, const ExplodedNode *ErrorNode, return Notes; } -/// Find a non-invalidated report for a given equivalence class, -/// and return together with a cache of visitors notes. -/// If none found, return a nullptr paired with an empty cache. -static -std::pair<BugReport*, std::unique_ptr<VisitorsDiagnosticsTy>> findValidReport( - TrimmedGraph &TrimG, - ReportGraph &ErrorGraph, - ArrayRef<BugReport *> &bugReports, - AnalyzerOptions &Opts, - GRBugReporter &Reporter) { +Optional<PathDiagnosticBuilder> PathDiagnosticBuilder::findValidReport( + ArrayRef<PathSensitiveBugReport *> &bugReports, + PathSensitiveBugReporter &Reporter) { - while (TrimG.popNextReportGraph(ErrorGraph)) { + BugPathGetter BugGraph(&Reporter.getGraph(), bugReports); + + while (BugPathInfo *BugPath = BugGraph.getNextBugPath()) { // Find the BugReport with the original location. - assert(ErrorGraph.Index < bugReports.size()); - BugReport *R = bugReports[ErrorGraph.Index]; + PathSensitiveBugReport *R = BugPath->Report; assert(R && "No original report found for sliced graph."); assert(R->isValid() && "Report selected by trimmed graph marked invalid."); - const ExplodedNode *ErrorNode = ErrorGraph.ErrorNode; + const ExplodedNode *ErrorNode = BugPath->ErrorNode; // Register refutation visitors first, if they mark the bug invalid no // further analysis is required - R->addVisitor(llvm::make_unique<LikelyFalsePositiveSuppressionBRVisitor>()); + R->addVisitor(std::make_unique<LikelyFalsePositiveSuppressionBRVisitor>()); // Register additional node visitors. - R->addVisitor(llvm::make_unique<NilReceiverBRVisitor>()); - R->addVisitor(llvm::make_unique<ConditionBRVisitor>()); - R->addVisitor(llvm::make_unique<TagVisitor>()); + R->addVisitor(std::make_unique<NilReceiverBRVisitor>()); + R->addVisitor(std::make_unique<ConditionBRVisitor>()); + R->addVisitor(std::make_unique<TagVisitor>()); - BugReporterContext BRC(Reporter, ErrorGraph.BackMap); + BugReporterContext BRC(Reporter); // Run all visitors on a given graph, once. std::unique_ptr<VisitorsDiagnosticsTy> visitorNotes = generateVisitorsDiagnostics(R, ErrorNode, BRC); if (R->isValid()) { - if (Opts.ShouldCrosscheckWithZ3) { + if (Reporter.getAnalyzerOptions().ShouldCrosscheckWithZ3) { // If crosscheck is enabled, remove all visitors, add the refutation // visitor and check again R->clearVisitors(); - R->addVisitor(llvm::make_unique<FalsePositiveRefutationBRVisitor>()); + R->addVisitor(std::make_unique<FalsePositiveRefutationBRVisitor>()); // We don't overrite the notes inserted by other visitors because the // refutation manager does not add any new note to the path - generateVisitorsDiagnostics(R, ErrorGraph.ErrorNode, BRC); + generateVisitorsDiagnostics(R, BugPath->ErrorNode, BRC); } // Check if the bug is still valid if (R->isValid()) - return std::make_pair(R, std::move(visitorNotes)); + return PathDiagnosticBuilder( + std::move(BRC), std::move(BugPath->BugPath), BugPath->Report, + BugPath->ErrorNode, std::move(visitorNotes)); } } - return std::make_pair(nullptr, llvm::make_unique<VisitorsDiagnosticsTy>()); + return {}; } std::unique_ptr<DiagnosticForConsumerMapTy> -GRBugReporter::generatePathDiagnostics( +PathSensitiveBugReporter::generatePathDiagnostics( ArrayRef<PathDiagnosticConsumer *> consumers, - ArrayRef<BugReport *> &bugReports) { + ArrayRef<PathSensitiveBugReport *> &bugReports) { assert(!bugReports.empty()); - auto Out = llvm::make_unique<DiagnosticForConsumerMapTy>(); - bool HasValid = false; - SmallVector<const ExplodedNode *, 32> errorNodes; - for (const auto I : bugReports) { - if (I->isValid()) { - HasValid = true; - errorNodes.push_back(I->getErrorNode()); - } else { - // Keep the errorNodes list in sync with the bugReports list. - errorNodes.push_back(nullptr); - } - } - - // If all the reports have been marked invalid by a previous path generation, - // we're done. - if (!HasValid) - return Out; + auto Out = std::make_unique<DiagnosticForConsumerMapTy>(); - TrimmedGraph TrimG(&getGraph(), errorNodes); - ReportGraph ErrorGraph; - auto ReportInfo = findValidReport(TrimG, ErrorGraph, bugReports, - getAnalyzerOptions(), *this); - BugReport *R = ReportInfo.first; + Optional<PathDiagnosticBuilder> PDB = + PathDiagnosticBuilder::findValidReport(bugReports, *this); - if (R && R->isValid()) { - const ExplodedNode *ErrorNode = ErrorGraph.ErrorNode; + if (PDB) { for (PathDiagnosticConsumer *PC : consumers) { - PathDiagnosticBuilder PDB(*this, R, ErrorGraph.BackMap, PC); - std::unique_ptr<PathDiagnostic> PD = generatePathDiagnosticForConsumer( - PC->getGenerationScheme(), PDB, ErrorNode, *ReportInfo.second); - (*Out)[PC] = std::move(PD); + if (std::unique_ptr<PathDiagnostic> PD = PDB->generate(PC)) { + (*Out)[PC] = std::move(PD); + } } } return Out; } -void BugReporter::Register(const BugType *BT) { - BugTypes = F.add(BugTypes, 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 - // file. The locations from handcrafted ASTs have no valid source locations - // and have to be discarded. Locations from model files should be preserved - // for processing and reporting. - if (DeclCtx->isBodyAutosynthesized() && - !DeclCtx->isBodyAutosynthesizedFromModelFile()) - return; - } - - bool ValidSourceLoc = R->getLocation(getSourceManager()).isValid(); + bool ValidSourceLoc = R->getLocation().isValid(); assert(ValidSourceLoc); // If we mess up in a release build, we'd still prefer to just drop the bug // instead of trying to go on. @@ -2729,8 +2832,6 @@ void BugReporter::emitReport(std::unique_ptr<BugReport> R) { R->Profile(ID); // Lookup the equivance class. If there isn't one, create it. - const BugType& BT = R->getBugType(); - Register(&BT); void *InsertPos; BugReportEquivClass* EQ = EQClasses.FindNodeOrInsertPos(ID, InsertPos); @@ -2742,6 +2843,28 @@ void BugReporter::emitReport(std::unique_ptr<BugReport> R) { EQ->AddReport(std::move(R)); } +void PathSensitiveBugReporter::emitReport(std::unique_ptr<BugReport> R) { + if (auto PR = dyn_cast<PathSensitiveBugReport>(R.get())) + if (const ExplodedNode *E = PR->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 + // file. The locations from handcrafted ASTs have no valid source + // locations and have to be discarded. Locations from model files should + // be preserved for processing and reporting. + if (DeclCtx->isBodyAutosynthesized() && + !DeclCtx->isBodyAutosynthesizedFromModelFile()) + return; + } + + BugReporter::emitReport(std::move(R)); +} + //===----------------------------------------------------------------------===// // Emitting reports in equivalence classes. //===----------------------------------------------------------------------===// @@ -2758,107 +2881,19 @@ struct FRIEC_WLItem { } // namespace -static const CFGBlock *findBlockForNode(const ExplodedNode *N) { - ProgramPoint P = N->getLocation(); - if (auto BEP = P.getAs<BlockEntrance>()) - return BEP->getBlock(); - - // Find the node's current statement in the CFG. - if (const Stmt *S = PathDiagnosticLocation::getStmt(N)) - return N->getLocationContext()->getAnalysisDeclContext() - ->getCFGStmtMap()->getBlock(S); - - return nullptr; -} - -// Returns true if by simply looking at the block, we can be sure that it -// results in a sink during analysis. This is useful to know when the analysis -// was interrupted, and we try to figure out if it would sink eventually. -// There may be many more reasons why a sink would appear during analysis -// (eg. checkers may generate sinks arbitrarily), but here we only consider -// sinks that would be obvious by looking at the CFG. -static bool isImmediateSinkBlock(const CFGBlock *Blk) { - if (Blk->hasNoReturnElement()) - return true; - - // FIXME: Throw-expressions are currently generating sinks during analysis: - // they're not supported yet, and also often used for actually terminating - // the program. So we should treat them as sinks in this analysis as well, - // at least for now, but once we have better support for exceptions, - // we'd need to carefully handle the case when the throw is being - // immediately caught. - if (std::any_of(Blk->begin(), Blk->end(), [](const CFGElement &Elm) { - if (Optional<CFGStmt> StmtElm = Elm.getAs<CFGStmt>()) - if (isa<CXXThrowExpr>(StmtElm->getStmt())) - return true; - return false; - })) - return true; - - return false; -} - -// Returns true if by looking at the CFG surrounding the node's program -// point, we can be sure that any analysis starting from this point would -// eventually end with a sink. We scan the child CFG blocks in a depth-first -// manner and see if all paths eventually end up in an immediate sink block. -static bool isInevitablySinking(const ExplodedNode *N) { - const CFG &Cfg = N->getCFG(); - - const CFGBlock *StartBlk = findBlockForNode(N); - if (!StartBlk) - return false; - if (isImmediateSinkBlock(StartBlk)) - return true; - - llvm::SmallVector<const CFGBlock *, 32> DFSWorkList; - llvm::SmallPtrSet<const CFGBlock *, 32> Visited; - - DFSWorkList.push_back(StartBlk); - while (!DFSWorkList.empty()) { - const CFGBlock *Blk = DFSWorkList.back(); - DFSWorkList.pop_back(); - Visited.insert(Blk); - - // If at least one path reaches the CFG exit, it means that control is - // returned to the caller. For now, say that we are not sure what - // happens next. If necessary, this can be improved to analyze - // the parent StackFrameContext's call site in a similar manner. - if (Blk == &Cfg.getExit()) - return false; - - for (const auto &Succ : Blk->succs()) { - if (const CFGBlock *SuccBlk = Succ.getReachableBlock()) { - if (!isImmediateSinkBlock(SuccBlk) && !Visited.count(SuccBlk)) { - // If the block has reachable child blocks that aren't no-return, - // add them to the worklist. - DFSWorkList.push_back(SuccBlk); - } - } - } - } - - // Nothing reached the exit. It can only mean one thing: there's no return. - return true; -} - -static BugReport * -FindReportInEquivalenceClass(BugReportEquivClass& EQ, - SmallVectorImpl<BugReport*> &bugReports) { - BugReportEquivClass::iterator I = EQ.begin(), E = EQ.end(); - assert(I != E); - const BugType& BT = I->getBugType(); - +BugReport *PathSensitiveBugReporter::findReportInEquivalenceClass( + BugReportEquivClass &EQ, SmallVectorImpl<BugReport *> &bugReports) { // If we don't need to suppress any of the nodes because they are // 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. + assert(EQ.getReports().size() > 0); + const BugType& BT = EQ.getReports()[0]->getBugType(); if (!BT.isSuppressOnSink()) { - BugReport *R = &*I; - for (auto &I : EQ) { - const ExplodedNode *N = I.getErrorNode(); - if (N) { - R = &I; - bugReports.push_back(R); + BugReport *R = EQ.getReports()[0].get(); + for (auto &J : EQ.getReports()) { + if (auto *PR = dyn_cast<PathSensitiveBugReport>(J.get())) { + R = PR; + bugReports.push_back(PR); } } return R; @@ -2872,20 +2907,21 @@ FindReportInEquivalenceClass(BugReportEquivClass& EQ, // stack for very long paths. BugReport *exampleReport = nullptr; - for (; I != E; ++I) { - const ExplodedNode *errorNode = I->getErrorNode(); - - if (!errorNode) + for (const auto &I: EQ.getReports()) { + auto *R = dyn_cast<PathSensitiveBugReport>(I.get()); + if (!R) continue; + + const ExplodedNode *errorNode = R->getErrorNode(); if (errorNode->isSink()) { llvm_unreachable( "BugType::isSuppressSink() should not be 'true' for sink end nodes"); } // No successors? By definition this nodes isn't post-dominated by a sink. if (errorNode->succ_empty()) { - bugReports.push_back(&*I); + bugReports.push_back(R); if (!exampleReport) - exampleReport = &*I; + exampleReport = R; continue; } @@ -2893,8 +2929,9 @@ FindReportInEquivalenceClass(BugReportEquivClass& EQ, // to being post-dominated by a sink. This works better when the analysis // is incomplete and we have never reached the no-return function call(s) // that we'd inevitably bump into on this path. - if (isInevitablySinking(errorNode)) - continue; + if (const CFGBlock *ErrorB = errorNode->getCFGBlock()) + if (ErrorB->isInevitablySinking()) + 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. @@ -2917,9 +2954,9 @@ FindReportInEquivalenceClass(BugReportEquivClass& EQ, 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(R); if (!exampleReport) - exampleReport = &*I; + exampleReport = R; WL.clear(); break; } @@ -2950,7 +2987,7 @@ FindReportInEquivalenceClass(BugReportEquivClass& EQ, void BugReporter::FlushReport(BugReportEquivClass& EQ) { SmallVector<BugReport*, 10> bugReports; - BugReport *report = FindReportInEquivalenceClass(EQ, bugReports); + BugReport *report = findReportInEquivalenceClass(EQ, bugReports); if (!report) return; @@ -2965,8 +3002,8 @@ void BugReporter::FlushReport(BugReportEquivClass& EQ) { // If the path is empty, generate a single step path with the location // of the issue. if (PD->path.empty()) { - PathDiagnosticLocation L = report->getLocation(getSourceManager()); - auto piece = llvm::make_unique<PathDiagnosticEventPiece>( + PathDiagnosticLocation L = report->getLocation(); + auto piece = std::make_unique<PathDiagnosticEventPiece>( L, report->getDescription()); for (SourceRange Range : report->getRanges()) piece->addRange(Range); @@ -2993,10 +3030,8 @@ void BugReporter::FlushReport(BugReportEquivClass& EQ) { Pieces.push_front(*I); } - // Get the meta data. - const BugReport::ExtraTextList &Meta = report->getExtraText(); - for (const auto &i : Meta) - PD->addMeta(i); + for (const auto &I : report->getFixits()) + Pieces.back()->addFixit(I); updateExecutedLinesWithDiagnosticPieces(*PD); Consumer->HandlePathDiagnostic(std::move(PD)); @@ -3006,7 +3041,7 @@ void BugReporter::FlushReport(BugReportEquivClass& EQ) { /// Insert all lines participating in the function signature \p Signature /// into \p ExecutedLines. static void populateExecutedLinesWithFunctionSignature( - const Decl *Signature, SourceManager &SM, + const Decl *Signature, const SourceManager &SM, FilesToLineNumsMap &ExecutedLines) { SourceRange SignatureSourceRange; const Stmt* Body = Signature->getBody(); @@ -3031,7 +3066,7 @@ static void populateExecutedLinesWithFunctionSignature( } static void populateExecutedLinesWithStmt( - const Stmt *S, SourceManager &SM, + const Stmt *S, const SourceManager &SM, FilesToLineNumsMap &ExecutedLines) { SourceLocation Loc = S->getSourceRange().getBegin(); if (!Loc.isValid()) @@ -3045,8 +3080,8 @@ static void populateExecutedLinesWithStmt( /// \return all executed lines including function signatures on the path /// starting from \p N. static std::unique_ptr<FilesToLineNumsMap> -findExecutedLines(SourceManager &SM, const ExplodedNode *N) { - auto ExecutedLines = llvm::make_unique<FilesToLineNumsMap>(); +findExecutedLines(const SourceManager &SM, const ExplodedNode *N) { + auto ExecutedLines = std::make_unique<FilesToLineNumsMap>(); while (N) { if (N->getFirstPred() == nullptr) { @@ -3057,7 +3092,7 @@ findExecutedLines(SourceManager &SM, const ExplodedNode *N) { // Inlined function: show signature. const Decl* D = CE->getCalleeContext()->getDecl(); populateExecutedLinesWithFunctionSignature(D, SM, *ExecutedLines); - } else if (const Stmt *S = PathDiagnosticLocation::getStmt(N)) { + } else if (const Stmt *S = N->getStmtForDiagnostics()) { populateExecutedLinesWithStmt(S, SM, *ExecutedLines); // Show extra context for some parent kinds. @@ -3082,16 +3117,89 @@ findExecutedLines(SourceManager &SM, const ExplodedNode *N) { std::unique_ptr<DiagnosticForConsumerMapTy> BugReporter::generateDiagnosticForConsumerMap( - BugReport *report, ArrayRef<PathDiagnosticConsumer *> consumers, + BugReport *exampleReport, ArrayRef<PathDiagnosticConsumer *> consumers, ArrayRef<BugReport *> bugReports) { + auto *basicReport = cast<BasicBugReport>(exampleReport); + auto Out = std::make_unique<DiagnosticForConsumerMapTy>(); + for (auto *Consumer : consumers) + (*Out)[Consumer] = generateDiagnosticForBasicReport(basicReport); + return Out; +} - if (!report->isPathSensitive()) { - auto Out = llvm::make_unique<DiagnosticForConsumerMapTy>(); - for (auto *Consumer : consumers) - (*Out)[Consumer] = generateEmptyDiagnosticForReport(report, - getSourceManager()); - return Out; +static PathDiagnosticCallPiece * +getFirstStackedCallToHeaderFile(PathDiagnosticCallPiece *CP, + const SourceManager &SMgr) { + SourceLocation CallLoc = CP->callEnter.asLocation(); + + // If the call is within a macro, don't do anything (for now). + if (CallLoc.isMacroID()) + return nullptr; + + assert(AnalysisManager::isInCodeFile(CallLoc, SMgr) && + "The call piece should not be in a header file."); + + // Check if CP represents a path through a function outside of the main file. + if (!AnalysisManager::isInCodeFile(CP->callEnterWithin.asLocation(), SMgr)) + return CP; + + const PathPieces &Path = CP->path; + if (Path.empty()) + return nullptr; + + // Check if the last piece in the callee path is a call to a function outside + // of the main file. + if (auto *CPInner = dyn_cast<PathDiagnosticCallPiece>(Path.back().get())) + return getFirstStackedCallToHeaderFile(CPInner, SMgr); + + // Otherwise, the last piece is in the main file. + return nullptr; +} + +static void resetDiagnosticLocationToMainFile(PathDiagnostic &PD) { + if (PD.path.empty()) + return; + + PathDiagnosticPiece *LastP = PD.path.back().get(); + assert(LastP); + const SourceManager &SMgr = LastP->getLocation().getManager(); + + // We only need to check if the report ends inside headers, if the last piece + // is a call piece. + if (auto *CP = dyn_cast<PathDiagnosticCallPiece>(LastP)) { + CP = getFirstStackedCallToHeaderFile(CP, SMgr); + if (CP) { + // Mark the piece. + CP->setAsLastInMainSourceFile(); + + // Update the path diagnostic message. + const auto *ND = dyn_cast<NamedDecl>(CP->getCallee()); + if (ND) { + SmallString<200> buf; + llvm::raw_svector_ostream os(buf); + os << " (within a call to '" << ND->getDeclName() << "')"; + PD.appendToDesc(os.str()); + } + + // Reset the report containing declaration and location. + PD.setDeclWithIssue(CP->getCaller()); + PD.setLocation(CP->getLocation()); + + return; + } } +} + + + +std::unique_ptr<DiagnosticForConsumerMapTy> +PathSensitiveBugReporter::generateDiagnosticForConsumerMap( + BugReport *exampleReport, ArrayRef<PathDiagnosticConsumer *> consumers, + ArrayRef<BugReport *> bugReports) { + std::vector<BasicBugReport *> BasicBugReports; + std::vector<PathSensitiveBugReport *> PathSensitiveBugReports; + if (isa<BasicBugReport>(exampleReport)) + return BugReporter::generateDiagnosticForConsumerMap(exampleReport, + consumers, bugReports); // Generate the full path sensitive diagnostic, using the generation scheme // specified by the PathDiagnosticConsumer. Note that we have to generate @@ -3099,8 +3207,13 @@ BugReporter::generateDiagnosticForConsumerMap( // the BugReporterVisitors may mark this bug as a false positive. assert(!bugReports.empty()); MaxBugClassSize.updateMax(bugReports.size()); - std::unique_ptr<DiagnosticForConsumerMapTy> Out = - generatePathDiagnostics(consumers, bugReports); + + // Avoid copying the whole array because there may be a lot of reports. + ArrayRef<PathSensitiveBugReport *> convertedArrayOfReports( + reinterpret_cast<PathSensitiveBugReport *const *>(&*bugReports.begin()), + reinterpret_cast<PathSensitiveBugReport *const *>(&*bugReports.end())); + std::unique_ptr<DiagnosticForConsumerMapTy> Out = generatePathDiagnostics( + consumers, convertedArrayOfReports); if (Out->empty()) return Out; @@ -3109,40 +3222,43 @@ BugReporter::generateDiagnosticForConsumerMap( // Examine the report and see if the last piece is in a header. Reset the // report location to the last piece in the main source file. - AnalyzerOptions &Opts = getAnalyzerOptions(); + const AnalyzerOptions &Opts = getAnalyzerOptions(); for (auto const &P : *Out) if (Opts.ShouldReportIssuesInMainSourceFile && !Opts.AnalyzeAll) - P.second->resetDiagnosticLocationToMainFile(); + resetDiagnosticLocationToMainFile(*P.second); return Out; } void BugReporter::EmitBasicReport(const Decl *DeclWithIssue, - const CheckerBase *Checker, - StringRef Name, StringRef Category, - StringRef Str, PathDiagnosticLocation Loc, - ArrayRef<SourceRange> Ranges) { - EmitBasicReport(DeclWithIssue, Checker->getCheckName(), Name, Category, Str, - Loc, Ranges); + const CheckerBase *Checker, StringRef Name, + StringRef Category, StringRef Str, + PathDiagnosticLocation Loc, + ArrayRef<SourceRange> Ranges, + ArrayRef<FixItHint> Fixits) { + EmitBasicReport(DeclWithIssue, Checker->getCheckerName(), Name, Category, Str, + Loc, Ranges, Fixits); } void BugReporter::EmitBasicReport(const Decl *DeclWithIssue, - CheckName CheckName, + CheckerNameRef CheckName, StringRef name, StringRef category, StringRef str, PathDiagnosticLocation Loc, - ArrayRef<SourceRange> Ranges) { + ArrayRef<SourceRange> Ranges, + ArrayRef<FixItHint> Fixits) { // 'BT' is owned by BugReporter. BugType *BT = getBugTypeForName(CheckName, name, category); - auto R = llvm::make_unique<BugReport>(*BT, str, Loc); + auto R = std::make_unique<BasicBugReport>(*BT, str, Loc); R->setDeclWithIssue(DeclWithIssue); - for (ArrayRef<SourceRange>::iterator I = Ranges.begin(), E = Ranges.end(); - I != E; ++I) - R->addRange(*I); + for (const auto &SR : Ranges) + R->addRange(SR); + for (const auto &FH : Fixits) + R->addFixItHint(FH); emitReport(std::move(R)); } -BugType *BugReporter::getBugTypeForName(CheckName CheckName, StringRef name, - StringRef category) { +BugType *BugReporter::getBugTypeForName(CheckerNameRef CheckName, + StringRef name, StringRef category) { SmallString<136> fullDesc; llvm::raw_svector_ostream(fullDesc) << CheckName.getName() << ":" << name << ":" << category; diff --git a/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp b/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp index 250793c4baff0..7ba93b858baf5 100644 --- a/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp +++ b/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp @@ -26,6 +26,7 @@ #include "clang/Analysis/AnalysisDeclContext.h" #include "clang/Analysis/CFG.h" #include "clang/Analysis/CFGStmtMap.h" +#include "clang/Analysis/PathDiagnostic.h" #include "clang/Analysis/ProgramPoint.h" #include "clang/Basic/IdentifierTable.h" #include "clang/Basic/LLVM.h" @@ -34,7 +35,6 @@ #include "clang/Lex/Lexer.h" #include "clang/StaticAnalyzer/Core/AnalyzerOptions.h" #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" -#include "clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h" #include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h" @@ -180,21 +180,60 @@ static bool hasVisibleUpdate(const ExplodedNode *LeftNode, SVal LeftVal, RLCV->getStore() == RightNode->getState()->getStore(); } -static Optional<const llvm::APSInt *> -getConcreteIntegerValue(const Expr *CondVarExpr, const ExplodedNode *N) { +static Optional<SVal> getSValForVar(const Expr *CondVarExpr, + const ExplodedNode *N) { ProgramStateRef State = N->getState(); const LocationContext *LCtx = N->getLocationContext(); + assert(CondVarExpr); + CondVarExpr = CondVarExpr->IgnoreImpCasts(); + // The declaration of the value may rely on a pointer so take its l-value. - if (const auto *DRE = dyn_cast_or_null<DeclRefExpr>(CondVarExpr)) { - if (const auto *VD = dyn_cast_or_null<VarDecl>(DRE->getDecl())) { - SVal DeclSVal = State->getSVal(State->getLValue(VD, LCtx)); - if (auto DeclCI = DeclSVal.getAs<nonloc::ConcreteInt>()) - return &DeclCI->getValue(); - } - } + // FIXME: As seen in VisitCommonDeclRefExpr, sometimes DeclRefExpr may + // evaluate to a FieldRegion when it refers to a declaration of a lambda + // capture variable. We most likely need to duplicate that logic here. + if (const auto *DRE = dyn_cast<DeclRefExpr>(CondVarExpr)) + if (const auto *VD = dyn_cast<VarDecl>(DRE->getDecl())) + return State->getSVal(State->getLValue(VD, LCtx)); + + if (const auto *ME = dyn_cast<MemberExpr>(CondVarExpr)) + if (const auto *FD = dyn_cast<FieldDecl>(ME->getMemberDecl())) + if (auto FieldL = State->getSVal(ME, LCtx).getAs<Loc>()) + return State->getRawSVal(*FieldL, FD->getType()); + + return None; +} + +static Optional<const llvm::APSInt *> +getConcreteIntegerValue(const Expr *CondVarExpr, const ExplodedNode *N) { - return {}; + if (Optional<SVal> V = getSValForVar(CondVarExpr, N)) + if (auto CI = V->getAs<nonloc::ConcreteInt>()) + return &CI->getValue(); + return None; +} + +static bool isVarAnInterestingCondition(const Expr *CondVarExpr, + const ExplodedNode *N, + const PathSensitiveBugReport *B) { + // Even if this condition is marked as interesting, it isn't *that* + // interesting if it didn't happen in a nested stackframe, the user could just + // follow the arrows. + if (!B->getErrorNode()->getStackFrame()->isParentOf(N->getStackFrame())) + return false; + + if (Optional<SVal> V = getSValForVar(CondVarExpr, N)) + if (Optional<bugreporter::TrackingKind> K = B->getInterestingnessKind(*V)) + return *K == bugreporter::TrackingKind::Condition; + + return false; +} + +static bool isInterestingExpr(const Expr *E, const ExplodedNode *N, + const PathSensitiveBugReport *B) { + if (Optional<SVal> V = getSValForVar(E, N)) + return B->getInterestingnessKind(*V).hasValue(); + return false; } /// \return name of the macro inside the location \p Loc. @@ -255,21 +294,21 @@ static bool wasRegionOfInterestModifiedAt(const SubRegion *RegionOfInterest, // Implementation of BugReporterVisitor. //===----------------------------------------------------------------------===// -std::shared_ptr<PathDiagnosticPiece> -BugReporterVisitor::getEndPath(BugReporterContext &, - const ExplodedNode *, BugReport &) { +PathDiagnosticPieceRef BugReporterVisitor::getEndPath(BugReporterContext &, + const ExplodedNode *, + PathSensitiveBugReport &) { return nullptr; } -void -BugReporterVisitor::finalizeVisitor(BugReporterContext &, - const ExplodedNode *, BugReport &) {} - -std::shared_ptr<PathDiagnosticPiece> BugReporterVisitor::getDefaultEndPath( - BugReporterContext &BRC, const ExplodedNode *EndPathNode, BugReport &BR) { - PathDiagnosticLocation L = - PathDiagnosticLocation::createEndOfPath(EndPathNode,BRC.getSourceManager()); +void BugReporterVisitor::finalizeVisitor(BugReporterContext &, + const ExplodedNode *, + PathSensitiveBugReport &) {} +PathDiagnosticPieceRef +BugReporterVisitor::getDefaultEndPath(const BugReporterContext &BRC, + const ExplodedNode *EndPathNode, + const PathSensitiveBugReport &BR) { + PathDiagnosticLocation L = BR.getLocation(); const auto &Ranges = BR.getRanges(); // Only add the statement itself as a range if we didn't specify any @@ -297,6 +336,7 @@ class NoStoreFuncVisitor final : public BugReporterVisitor { MemRegionManager &MmrMgr; const SourceManager &SM; const PrintingPolicy &PP; + bugreporter::TrackingKind TKind; /// Recursion limit for dereferencing fields when looking for the /// region of interest. @@ -317,10 +357,10 @@ class NoStoreFuncVisitor final : public BugReporterVisitor { using RegionVector = SmallVector<const MemRegion *, 5>; public: - NoStoreFuncVisitor(const SubRegion *R) + NoStoreFuncVisitor(const SubRegion *R, bugreporter::TrackingKind TKind) : RegionOfInterest(R), MmrMgr(*R->getMemRegionManager()), SM(MmrMgr.getContext().getSourceManager()), - PP(MmrMgr.getContext().getPrintingPolicy()) {} + PP(MmrMgr.getContext().getPrintingPolicy()), TKind(TKind) {} void Profile(llvm::FoldingSetNodeID &ID) const override { static int Tag = 0; @@ -333,9 +373,9 @@ public: return static_cast<void *>(&Tag); } - std::shared_ptr<PathDiagnosticPiece> VisitNode(const ExplodedNode *N, - BugReporterContext &BR, - BugReport &R) override; + PathDiagnosticPieceRef VisitNode(const ExplodedNode *N, + BugReporterContext &BR, + PathSensitiveBugReport &R) override; private: /// Attempts to find the region of interest in a given record decl, @@ -368,11 +408,11 @@ private: /// either emit a note or suppress the report enirely. /// \return Diagnostics piece for region not modified in the current function, /// if it decides to emit one. - std::shared_ptr<PathDiagnosticPiece> - maybeEmitNote(BugReport &R, const CallEvent &Call, const ExplodedNode *N, - const RegionVector &FieldChain, const MemRegion *MatchedRegion, - StringRef FirstElement, bool FirstIsReferenceType, - unsigned IndirectionLevel); + PathDiagnosticPieceRef + maybeEmitNote(PathSensitiveBugReport &R, const CallEvent &Call, + const ExplodedNode *N, const RegionVector &FieldChain, + const MemRegion *MatchedRegion, StringRef FirstElement, + bool FirstIsReferenceType, unsigned IndirectionLevel); /// Pretty-print region \p MatchedRegion to \p os. /// \return Whether printing succeeded. @@ -501,9 +541,9 @@ NoStoreFuncVisitor::findRegionOfInterestInRecord( return None; } -std::shared_ptr<PathDiagnosticPiece> +PathDiagnosticPieceRef NoStoreFuncVisitor::VisitNode(const ExplodedNode *N, BugReporterContext &BR, - BugReport &R) { + PathSensitiveBugReport &R) { const LocationContext *Ctx = N->getLocationContext(); const StackFrameContext *SCtx = Ctx->getStackFrame(); @@ -611,8 +651,11 @@ void NoStoreFuncVisitor::findModifyingFrames(const ExplodedNode *N) { } while (N); } -std::shared_ptr<PathDiagnosticPiece> NoStoreFuncVisitor::maybeEmitNote( - BugReport &R, const CallEvent &Call, const ExplodedNode *N, +static llvm::StringLiteral WillBeUsedForACondition = + ", which participates in a condition later"; + +PathDiagnosticPieceRef NoStoreFuncVisitor::maybeEmitNote( + PathSensitiveBugReport &R, const CallEvent &Call, const ExplodedNode *N, const RegionVector &FieldChain, const MemRegion *MatchedRegion, StringRef FirstElement, bool FirstIsReferenceType, unsigned IndirectionLevel) { @@ -657,6 +700,8 @@ std::shared_ptr<PathDiagnosticPiece> NoStoreFuncVisitor::maybeEmitNote( return nullptr; os << "'"; + if (TKind == bugreporter::TrackingKind::Condition) + os << WillBeUsedForACondition; return std::make_shared<PathDiagnosticEventPiece>(L, os.str()); } @@ -752,13 +797,12 @@ class MacroNullReturnSuppressionVisitor final : public BugReporterVisitor { bool WasModified = false; public: - MacroNullReturnSuppressionVisitor(const SubRegion *R, - const SVal V) : RegionOfInterest(R), - ValueAtDereference(V) {} + MacroNullReturnSuppressionVisitor(const SubRegion *R, const SVal V) + : RegionOfInterest(R), ValueAtDereference(V) {} - std::shared_ptr<PathDiagnosticPiece> VisitNode(const ExplodedNode *N, - BugReporterContext &BRC, - BugReport &BR) override { + PathDiagnosticPieceRef VisitNode(const ExplodedNode *N, + BugReporterContext &BRC, + PathSensitiveBugReport &BR) override { if (WasModified) return nullptr; @@ -784,12 +828,12 @@ public: static void addMacroVisitorIfNecessary( const ExplodedNode *N, const MemRegion *R, - bool EnableNullFPSuppression, BugReport &BR, + bool EnableNullFPSuppression, PathSensitiveBugReport &BR, const SVal V) { AnalyzerOptions &Options = N->getState()->getAnalysisManager().options; if (EnableNullFPSuppression && Options.ShouldSuppressNullReturnPaths && V.getAs<Loc>()) - BR.addVisitor(llvm::make_unique<MacroNullReturnSuppressionVisitor>( + BR.addVisitor(std::make_unique<MacroNullReturnSuppressionVisitor>( R->getAs<SubRegion>(), V)); } @@ -806,7 +850,7 @@ private: /// \return Source location of right hand side of an assignment /// into \c RegionOfInterest, empty optional if none found. Optional<SourceLocation> matchAssignment(const ExplodedNode *N) { - const Stmt *S = PathDiagnosticLocation::getStmt(N); + const Stmt *S = N->getStmtForDiagnostics(); ProgramStateRef State = N->getState(); auto *LCtx = N->getLocationContext(); if (!S) @@ -841,7 +885,7 @@ namespace { /// This visitor is intended to be used when another visitor discovers that an /// interesting value comes from an inlined function call. class ReturnVisitor : public BugReporterVisitor { - const StackFrameContext *StackFrame; + const StackFrameContext *CalleeSFC; enum { Initial, MaybeUnsuppress, @@ -851,13 +895,13 @@ class ReturnVisitor : public BugReporterVisitor { bool EnableNullFPSuppression; bool ShouldInvalidate = true; AnalyzerOptions& Options; + bugreporter::TrackingKind TKind; public: - ReturnVisitor(const StackFrameContext *Frame, - bool Suppressed, - AnalyzerOptions &Options) - : StackFrame(Frame), EnableNullFPSuppression(Suppressed), - Options(Options) {} + ReturnVisitor(const StackFrameContext *Frame, bool Suppressed, + AnalyzerOptions &Options, bugreporter::TrackingKind TKind) + : CalleeSFC(Frame), EnableNullFPSuppression(Suppressed), + Options(Options), TKind(TKind) {} static void *getTag() { static int Tag = 0; @@ -866,7 +910,7 @@ public: void Profile(llvm::FoldingSetNodeID &ID) const override { ID.AddPointer(ReturnVisitor::getTag()); - ID.AddPointer(StackFrame); + ID.AddPointer(CalleeSFC); ID.AddBoolean(EnableNullFPSuppression); } @@ -878,8 +922,9 @@ public: /// the statement is a call that was inlined, we add the visitor to the /// bug report, so it can print a note later. static void addVisitorIfNecessary(const ExplodedNode *Node, const Stmt *S, - BugReport &BR, - bool InEnableNullFPSuppression) { + PathSensitiveBugReport &BR, + bool InEnableNullFPSuppression, + bugreporter::TrackingKind TKind) { if (!CallEvent::isCallStmt(S)) return; @@ -950,17 +995,16 @@ public: if (Optional<Loc> RetLoc = RetVal.getAs<Loc>()) EnableNullFPSuppression = State->isNull(*RetLoc).isConstrainedTrue(); - BR.markInteresting(CalleeContext); - BR.addVisitor(llvm::make_unique<ReturnVisitor>(CalleeContext, + BR.addVisitor(std::make_unique<ReturnVisitor>(CalleeContext, EnableNullFPSuppression, - Options)); + Options, TKind)); } - std::shared_ptr<PathDiagnosticPiece> - visitNodeInitial(const ExplodedNode *N, - BugReporterContext &BRC, BugReport &BR) { + PathDiagnosticPieceRef visitNodeInitial(const ExplodedNode *N, + BugReporterContext &BRC, + PathSensitiveBugReport &BR) { // Only print a message at the interesting return statement. - if (N->getLocationContext() != StackFrame) + if (N->getLocationContext() != CalleeSFC) return nullptr; Optional<StmtPoint> SP = N->getLocationAs<StmtPoint>(); @@ -974,7 +1018,7 @@ public: // Okay, we're at the right return statement, but do we have the return // value available? ProgramStateRef State = N->getState(); - SVal V = State->getSVal(Ret, StackFrame); + SVal V = State->getSVal(Ret, CalleeSFC); if (V.isUnknownOrUndef()) return nullptr; @@ -1001,13 +1045,16 @@ public: RetE = RetE->IgnoreParenCasts(); - // If we're returning 0, we should track where that 0 came from. - bugreporter::trackExpressionValue(N, RetE, BR, EnableNullFPSuppression); + // Let's track the return value. + bugreporter::trackExpressionValue( + N, RetE, BR, TKind, EnableNullFPSuppression); // Build an appropriate message based on the return value. SmallString<64> Msg; llvm::raw_svector_ostream Out(Msg); + bool WouldEventBeMeaningless = false; + if (State->isNull(V).isConstrainedTrue()) { if (V.getAs<Loc>()) { @@ -1030,10 +1077,19 @@ public: } else { if (auto CI = V.getAs<nonloc::ConcreteInt>()) { Out << "Returning the value " << CI->getValue(); - } else if (V.getAs<Loc>()) { - Out << "Returning pointer"; } else { - Out << "Returning value"; + // There is nothing interesting about returning a value, when it is + // plain value without any constraints, and the function is guaranteed + // to return that every time. We could use CFG::isLinear() here, but + // constexpr branches are obvious to the compiler, not necesserily to + // the programmer. + if (N->getCFG().size() == 3) + WouldEventBeMeaningless = true; + + if (V.getAs<Loc>()) + Out << "Returning pointer"; + else + Out << "Returning value"; } } @@ -1052,26 +1108,36 @@ public: Out << " (loaded from '" << *DD << "')"; } - PathDiagnosticLocation L(Ret, BRC.getSourceManager(), StackFrame); + PathDiagnosticLocation L(Ret, BRC.getSourceManager(), CalleeSFC); if (!L.isValid() || !L.asLocation().isValid()) return nullptr; - return std::make_shared<PathDiagnosticEventPiece>(L, Out.str()); + if (TKind == bugreporter::TrackingKind::Condition) + Out << WillBeUsedForACondition; + + auto EventPiece = std::make_shared<PathDiagnosticEventPiece>(L, Out.str()); + + // If we determined that the note is meaningless, make it prunable, and + // don't mark the stackframe interesting. + if (WouldEventBeMeaningless) + EventPiece->setPrunable(true); + else + BR.markInteresting(CalleeSFC); + + return EventPiece; } - std::shared_ptr<PathDiagnosticPiece> - visitNodeMaybeUnsuppress(const ExplodedNode *N, - BugReporterContext &BRC, BugReport &BR) { -#ifndef NDEBUG + PathDiagnosticPieceRef visitNodeMaybeUnsuppress(const ExplodedNode *N, + BugReporterContext &BRC, + PathSensitiveBugReport &BR) { assert(Options.ShouldAvoidSuppressingNullArgumentPaths); -#endif // Are we at the entry node for this call? Optional<CallEnter> CE = N->getLocationAs<CallEnter>(); if (!CE) return nullptr; - if (CE->getCalleeContext() != StackFrame) + if (CE->getCalleeContext() != CalleeSFC) return nullptr; Mode = Satisfied; @@ -1083,7 +1149,7 @@ public: CallEventManager &CallMgr = StateMgr.getCallEventManager(); ProgramStateRef State = N->getState(); - CallEventRef<> Call = CallMgr.getCaller(StackFrame, State); + CallEventRef<> Call = CallMgr.getCaller(CalleeSFC, State); for (unsigned I = 0, E = Call->getNumArgs(); I != E; ++I) { Optional<Loc> ArgV = Call->getArgSVal(I).getAs<Loc>(); if (!ArgV) @@ -1097,7 +1163,7 @@ public: if (!State->isNull(*ArgV).isConstrainedTrue()) continue; - if (bugreporter::trackExpressionValue(N, ArgE, BR, EnableNullFPSuppression)) + if (trackExpressionValue(N, ArgE, BR, TKind, EnableNullFPSuppression)) ShouldInvalidate = false; // If we /can't/ track the null pointer, we should err on the side of @@ -1108,9 +1174,9 @@ public: return nullptr; } - std::shared_ptr<PathDiagnosticPiece> VisitNode(const ExplodedNode *N, - BugReporterContext &BRC, - BugReport &BR) override { + PathDiagnosticPieceRef VisitNode(const ExplodedNode *N, + BugReporterContext &BRC, + PathSensitiveBugReport &BR) override { switch (Mode) { case Initial: return visitNodeInitial(N, BRC, BR); @@ -1124,9 +1190,9 @@ public: } void finalizeVisitor(BugReporterContext &, const ExplodedNode *, - BugReport &BR) override { + PathSensitiveBugReport &BR) override { if (EnableNullFPSuppression && ShouldInvalidate) - BR.markInvalid(ReturnVisitor::getTag(), StackFrame); + BR.markInvalid(ReturnVisitor::getTag(), CalleeSFC); } }; @@ -1141,6 +1207,7 @@ void FindLastStoreBRVisitor::Profile(llvm::FoldingSetNodeID &ID) const { ID.AddPointer(&tag); ID.AddPointer(R); ID.Add(V); + ID.AddInteger(static_cast<int>(TKind)); ID.AddBoolean(EnableNullFPSuppression); } @@ -1246,9 +1313,8 @@ static void showBRParamDiagnostics(llvm::raw_svector_ostream& os, } /// Show default diagnostics for storing bad region. -static void showBRDefaultDiagnostics(llvm::raw_svector_ostream& os, - const MemRegion *R, - SVal V) { +static void showBRDefaultDiagnostics(llvm::raw_svector_ostream &os, + const MemRegion *R, SVal V) { if (V.getAs<loc::ConcreteInt>()) { bool b = false; if (R->isBoundable()) { @@ -1291,9 +1357,10 @@ static void showBRDefaultDiagnostics(llvm::raw_svector_ostream& os, } } -std::shared_ptr<PathDiagnosticPiece> +PathDiagnosticPieceRef FindLastStoreBRVisitor::VisitNode(const ExplodedNode *Succ, - BugReporterContext &BRC, BugReport &BR) { + BugReporterContext &BRC, + PathSensitiveBugReport &BR) { if (Satisfied) return nullptr; @@ -1314,7 +1381,7 @@ FindLastStoreBRVisitor::VisitNode(const ExplodedNode *Succ, // should track the initializer expression. if (Optional<PostInitializer> PIP = Pred->getLocationAs<PostInitializer>()) { const MemRegion *FieldReg = (const MemRegion *)PIP->getLocationValue(); - if (FieldReg && FieldReg == R) { + if (FieldReg == R) { StoreSite = Pred; InitE = PIP->getInitializer()->getInit(); } @@ -1351,14 +1418,19 @@ FindLastStoreBRVisitor::VisitNode(const ExplodedNode *Succ, if (Optional<CallEnter> CE = Succ->getLocationAs<CallEnter>()) { if (const auto *VR = dyn_cast<VarRegion>(R)) { - const auto *Param = cast<ParmVarDecl>(VR->getDecl()); + if (const auto *Param = dyn_cast<ParmVarDecl>(VR->getDecl())) { + ProgramStateManager &StateMgr = BRC.getStateManager(); + CallEventManager &CallMgr = StateMgr.getCallEventManager(); - ProgramStateManager &StateMgr = BRC.getStateManager(); - CallEventManager &CallMgr = StateMgr.getCallEventManager(); - - CallEventRef<> Call = CallMgr.getCaller(CE->getCalleeContext(), - Succ->getState()); - InitE = Call->getArgExpr(Param->getFunctionScopeIndex()); + CallEventRef<> Call = CallMgr.getCaller(CE->getCalleeContext(), + Succ->getState()); + InitE = Call->getArgExpr(Param->getFunctionScopeIndex()); + } else { + // Handle Objective-C 'self'. + assert(isa<ImplicitParamDecl>(VR->getDecl())); + InitE = cast<ObjCMessageExpr>(CE->getCalleeContext()->getCallSite()) + ->getInstanceReceiver()->IgnoreParenCasts(); + } IsParam = true; } } @@ -1371,22 +1443,23 @@ FindLastStoreBRVisitor::VisitNode(const ExplodedNode *Succ, if (!StoreSite) return nullptr; + Satisfied = true; // If we have an expression that provided the value, try to track where it // came from. if (InitE) { - if (V.isUndef() || - V.getAs<loc::ConcreteInt>() || V.getAs<nonloc::ConcreteInt>()) { - if (!IsParam) - InitE = InitE->IgnoreParenCasts(); - bugreporter::trackExpressionValue(StoreSite, InitE, BR, - EnableNullFPSuppression); - } - ReturnVisitor::addVisitorIfNecessary(StoreSite, InitE->IgnoreParenCasts(), - BR, EnableNullFPSuppression); + if (!IsParam) + InitE = InitE->IgnoreParenCasts(); + + bugreporter::trackExpressionValue( + StoreSite, InitE, BR, TKind, EnableNullFPSuppression); } + if (TKind == TrackingKind::Condition && + !OriginSFC->isParentOf(StoreSite->getStackFrame())) + return nullptr; + // Okay, we've found the binding. Emit an appropriate message. SmallString<256> sbuf; llvm::raw_svector_ostream os(sbuf); @@ -1411,8 +1484,8 @@ FindLastStoreBRVisitor::VisitNode(const ExplodedNode *Succ, dyn_cast_or_null<BlockDataRegion>(V.getAsRegion())) { if (const VarRegion *OriginalR = BDR->getOriginalRegion(VR)) { if (auto KV = State->getSVal(OriginalR).getAs<KnownSVal>()) - BR.addVisitor(llvm::make_unique<FindLastStoreBRVisitor>( - *KV, OriginalR, EnableNullFPSuppression)); + BR.addVisitor(std::make_unique<FindLastStoreBRVisitor>( + *KV, OriginalR, EnableNullFPSuppression, TKind, OriginSFC)); } } } @@ -1428,6 +1501,9 @@ FindLastStoreBRVisitor::VisitNode(const ExplodedNode *Succ, if (os.str().empty()) showBRDefaultDiagnostics(os, R, V); + if (TKind == bugreporter::TrackingKind::Condition) + os << WillBeUsedForACondition; + // Construct a new PathDiagnosticPiece. ProgramPoint P = StoreSite->getLocation(); PathDiagnosticLocation L; @@ -1467,9 +1543,8 @@ bool TrackConstraintBRVisitor::isUnderconstrained(const ExplodedNode *N) const { return (bool)N->getState()->assume(Constraint, !Assumption); } -std::shared_ptr<PathDiagnosticPiece> -TrackConstraintBRVisitor::VisitNode(const ExplodedNode *N, - BugReporterContext &BRC, BugReport &) { +PathDiagnosticPieceRef TrackConstraintBRVisitor::VisitNode( + const ExplodedNode *N, BugReporterContext &BRC, PathSensitiveBugReport &) { const ExplodedNode *PrevN = N->getFirstPred(); if (IsSatisfied) return nullptr; @@ -1547,10 +1622,10 @@ const char *SuppressInlineDefensiveChecksVisitor::getTag() { return "IDCVisitor"; } -std::shared_ptr<PathDiagnosticPiece> +PathDiagnosticPieceRef SuppressInlineDefensiveChecksVisitor::VisitNode(const ExplodedNode *Succ, BugReporterContext &BRC, - BugReport &BR) { + PathSensitiveBugReport &BR) { const ExplodedNode *Pred = Succ->getFirstPred(); if (IsSatisfied) return nullptr; @@ -1649,24 +1724,12 @@ public: ID.AddPointer(&x); } - std::shared_ptr<PathDiagnosticPiece> VisitNode(const ExplodedNode *N, - BugReporterContext &BRC, - BugReport &BR) override; + PathDiagnosticPieceRef VisitNode(const ExplodedNode *N, + BugReporterContext &BRC, + PathSensitiveBugReport &BR) override; }; } // end of anonymous namespace -static CFGBlock *GetRelevantBlock(const ExplodedNode *Node) { - if (auto SP = Node->getLocationAs<StmtPoint>()) { - const Stmt *S = SP->getStmt(); - assert(S); - - return const_cast<CFGBlock *>(Node->getLocationContext() - ->getAnalysisDeclContext()->getCFGStmtMap()->getBlock(S)); - } - - return nullptr; -} - static std::shared_ptr<PathDiagnosticEventPiece> constructDebugPieceForTrackedCondition(const Expr *Cond, const ExplodedNode *N, @@ -1687,34 +1750,75 @@ constructDebugPieceForTrackedCondition(const Expr *Cond, (Twine() + "Tracking condition '" + ConditionText + "'").str()); } -std::shared_ptr<PathDiagnosticPiece> +static bool isAssertlikeBlock(const CFGBlock *B, ASTContext &Context) { + if (B->succ_size() != 2) + return false; + + const CFGBlock *Then = B->succ_begin()->getReachableBlock(); + const CFGBlock *Else = (B->succ_begin() + 1)->getReachableBlock(); + + if (!Then || !Else) + return false; + + if (Then->isInevitablySinking() != Else->isInevitablySinking()) + return true; + + // For the following condition the following CFG would be built: + // + // -------------> + // / \ + // [B1] -> [B2] -> [B3] -> [sink] + // assert(A && B || C); \ \ + // -----------> [go on with the execution] + // + // It so happens that CFGBlock::getTerminatorCondition returns 'A' for block + // B1, 'A && B' for B2, and 'A && B || C' for B3. Let's check whether we + // reached the end of the condition! + if (const Stmt *ElseCond = Else->getTerminatorCondition()) + if (const auto *BinOp = dyn_cast<BinaryOperator>(ElseCond)) + if (BinOp->isLogicalOp()) + return isAssertlikeBlock(Else, Context); + + return false; +} + +PathDiagnosticPieceRef TrackControlDependencyCondBRVisitor::VisitNode(const ExplodedNode *N, BugReporterContext &BRC, - BugReport &BR) { + PathSensitiveBugReport &BR) { // We can only reason about control dependencies within the same stack frame. if (Origin->getStackFrame() != N->getStackFrame()) return nullptr; - CFGBlock *NB = GetRelevantBlock(N); + CFGBlock *NB = const_cast<CFGBlock *>(N->getCFGBlock()); // Skip if we already inspected this block. if (!VisitedBlocks.insert(NB).second) return nullptr; - CFGBlock *OriginB = GetRelevantBlock(Origin); + CFGBlock *OriginB = const_cast<CFGBlock *>(Origin->getCFGBlock()); // TODO: Cache CFGBlocks for each ExplodedNode. if (!OriginB || !NB) return nullptr; + if (isAssertlikeBlock(NB, BRC.getASTContext())) + return nullptr; + if (ControlDeps.isControlDependent(OriginB, NB)) { + // We don't really want to explain for range loops. Evidence suggests that + // the only thing that leads to is the addition of calls to operator!=. + if (llvm::isa_and_nonnull<CXXForRangeStmt>(NB->getTerminatorStmt())) + return nullptr; + if (const Expr *Condition = NB->getLastCondition()) { // Keeping track of the already tracked conditions on a visitor level // isn't sufficient, because a new visitor is created for each tracked // expression, hence the BugReport level set. if (BR.addTrackedCondition(N)) { bugreporter::trackExpressionValue( - N, Condition, BR, /*EnableNullFPSuppression=*/false); + N, Condition, BR, bugreporter::TrackingKind::Condition, + /*EnableNullFPSuppression=*/false); return constructDebugPieceForTrackedCondition(Condition, N, BRC); } } @@ -1819,7 +1923,7 @@ static const Expr *peelOffOuterExpr(const Expr *Ex, static const ExplodedNode* findNodeForExpression(const ExplodedNode *N, const Expr *Inner) { while (N) { - if (PathDiagnosticLocation::getStmt(N) == Inner) + if (N->getStmtForDiagnostics() == Inner) return N; N = N->getFirstPred(); } @@ -1827,8 +1931,11 @@ static const ExplodedNode* findNodeForExpression(const ExplodedNode *N, } bool bugreporter::trackExpressionValue(const ExplodedNode *InputNode, - const Expr *E, BugReport &report, + const Expr *E, + PathSensitiveBugReport &report, + bugreporter::TrackingKind TKind, bool EnableNullFPSuppression) { + if (!E || !InputNode) return false; @@ -1838,6 +1945,7 @@ bool bugreporter::trackExpressionValue(const ExplodedNode *InputNode, return false; ProgramStateRef LVState = LVNode->getState(); + const StackFrameContext *SFC = LVNode->getStackFrame(); // We only track expressions if we believe that they are important. Chances // are good that control dependencies to the tracking point are also improtant @@ -1845,19 +1953,20 @@ bool bugreporter::trackExpressionValue(const ExplodedNode *InputNode, // TODO: Shouldn't we track control dependencies of every bug location, rather // than only tracked expressions? if (LVState->getAnalysisManager().getAnalyzerOptions().ShouldTrackConditions) - report.addVisitor(llvm::make_unique<TrackControlDependencyCondBRVisitor>( + report.addVisitor(std::make_unique<TrackControlDependencyCondBRVisitor>( InputNode)); // The message send could be nil due to the receiver being nil. // At this point in the path, the receiver should be live since we are at the // message send expr. If it is nil, start tracking it. if (const Expr *Receiver = NilReceiverBRVisitor::getNilReceiver(Inner, LVNode)) - trackExpressionValue(LVNode, Receiver, report, EnableNullFPSuppression); + trackExpressionValue( + LVNode, Receiver, report, TKind, EnableNullFPSuppression); // Track the index if this is an array subscript. if (const auto *Arr = dyn_cast<ArraySubscriptExpr>(Inner)) trackExpressionValue( - LVNode, Arr->getIdx(), report, /*EnableNullFPSuppression*/ false); + LVNode, Arr->getIdx(), report, TKind, /*EnableNullFPSuppression*/false); // See if the expression we're interested refers to a variable. // If so, we can track both its contents and constraints on its value. @@ -1872,8 +1981,8 @@ bool bugreporter::trackExpressionValue(const ExplodedNode *InputNode, // got initialized. if (RR && !LVIsNull) if (auto KV = LVal.getAs<KnownSVal>()) - report.addVisitor(llvm::make_unique<FindLastStoreBRVisitor>( - *KV, RR, EnableNullFPSuppression)); + report.addVisitor(std::make_unique<FindLastStoreBRVisitor>( + *KV, RR, EnableNullFPSuppression, TKind, SFC)); // In case of C++ references, we want to differentiate between a null // reference and reference to null pointer. @@ -1887,17 +1996,18 @@ bool bugreporter::trackExpressionValue(const ExplodedNode *InputNode, // Mark both the variable region and its contents as interesting. SVal V = LVState->getRawSVal(loc::MemRegionVal(R)); report.addVisitor( - llvm::make_unique<NoStoreFuncVisitor>(cast<SubRegion>(R))); + std::make_unique<NoStoreFuncVisitor>(cast<SubRegion>(R), TKind)); MacroNullReturnSuppressionVisitor::addMacroVisitorIfNecessary( LVNode, R, EnableNullFPSuppression, report, V); - report.markInteresting(V); - report.addVisitor(llvm::make_unique<UndefOrNullArgVisitor>(R)); + report.markInteresting(V, TKind); + report.addVisitor(std::make_unique<UndefOrNullArgVisitor>(R)); - // If the contents are symbolic, find out when they became null. - if (V.getAsLocSymbol(/*IncludeBaseRegions*/ true)) - report.addVisitor(llvm::make_unique<TrackConstraintBRVisitor>( + // If the contents are symbolic and null, find out when they became null. + if (V.getAsLocSymbol(/*IncludeBaseRegions=*/true)) + if (LVState->isNull(V).isConstrainedTrue()) + report.addVisitor(std::make_unique<TrackConstraintBRVisitor>( V.castAs<DefinedSVal>(), false)); // Add visitor, which will suppress inline defensive checks. @@ -1905,12 +2015,12 @@ bool bugreporter::trackExpressionValue(const ExplodedNode *InputNode, if (!DV->isZeroConstant() && LVState->isNull(*DV).isConstrainedTrue() && EnableNullFPSuppression) report.addVisitor( - llvm::make_unique<SuppressInlineDefensiveChecksVisitor>(*DV, + std::make_unique<SuppressInlineDefensiveChecksVisitor>(*DV, LVNode)); if (auto KV = V.getAs<KnownSVal>()) - report.addVisitor(llvm::make_unique<FindLastStoreBRVisitor>( - *KV, R, EnableNullFPSuppression)); + report.addVisitor(std::make_unique<FindLastStoreBRVisitor>( + *KV, R, EnableNullFPSuppression, TKind, SFC)); return true; } } @@ -1920,40 +2030,43 @@ bool bugreporter::trackExpressionValue(const ExplodedNode *InputNode, SVal V = LVState->getSValAsScalarOrLoc(Inner, LVNode->getLocationContext()); ReturnVisitor::addVisitorIfNecessary( - LVNode, Inner, report, EnableNullFPSuppression); + LVNode, Inner, report, EnableNullFPSuppression, TKind); // Is it a symbolic value? if (auto L = V.getAs<loc::MemRegionVal>()) { - report.addVisitor(llvm::make_unique<UndefOrNullArgVisitor>(L->getRegion())); - // FIXME: this is a hack for fixing a later crash when attempting to // dereference a void* pointer. // We should not try to dereference pointers at all when we don't care // what is written inside the pointer. bool CanDereference = true; - if (const auto *SR = dyn_cast<SymbolicRegion>(L->getRegion())) + if (const auto *SR = L->getRegionAs<SymbolicRegion>()) { if (SR->getSymbol()->getType()->getPointeeType()->isVoidType()) CanDereference = false; + } else if (L->getRegionAs<AllocaRegion>()) + CanDereference = false; // At this point we are dealing with the region's LValue. // However, if the rvalue is a symbolic region, we should track it as well. // Try to use the correct type when looking up the value. SVal RVal; - if (ExplodedGraph::isInterestingLValueExpr(Inner)) { + if (ExplodedGraph::isInterestingLValueExpr(Inner)) RVal = LVState->getRawSVal(L.getValue(), Inner->getType()); - } else if (CanDereference) { + else if (CanDereference) RVal = LVState->getSVal(L->getRegion()); - } - if (CanDereference) + if (CanDereference) { + report.addVisitor( + std::make_unique<UndefOrNullArgVisitor>(L->getRegion())); + if (auto KV = RVal.getAs<KnownSVal>()) - report.addVisitor(llvm::make_unique<FindLastStoreBRVisitor>( - *KV, L->getRegion(), EnableNullFPSuppression)); + report.addVisitor(std::make_unique<FindLastStoreBRVisitor>( + *KV, L->getRegion(), EnableNullFPSuppression, TKind, SFC)); + } const MemRegion *RegionRVal = RVal.getAsRegion(); if (RegionRVal && isa<SymbolicRegion>(RegionRVal)) { - report.markInteresting(RegionRVal); - report.addVisitor(llvm::make_unique<TrackConstraintBRVisitor>( + report.markInteresting(RegionRVal, TKind); + report.addVisitor(std::make_unique<TrackConstraintBRVisitor>( loc::MemRegionVal(RegionRVal), /*assumption=*/false)); } } @@ -1978,9 +2091,9 @@ const Expr *NilReceiverBRVisitor::getNilReceiver(const Stmt *S, return nullptr; } -std::shared_ptr<PathDiagnosticPiece> -NilReceiverBRVisitor::VisitNode(const ExplodedNode *N, - BugReporterContext &BRC, BugReport &BR) { +PathDiagnosticPieceRef +NilReceiverBRVisitor::VisitNode(const ExplodedNode *N, BugReporterContext &BRC, + PathSensitiveBugReport &BR) { Optional<PreStmt> P = N->getLocationAs<PreStmt>(); if (!P) return nullptr; @@ -2006,8 +2119,9 @@ NilReceiverBRVisitor::VisitNode(const ExplodedNode *N, // The receiver was nil, and hence the method was skipped. // Register a BugReporterVisitor to issue a message telling us how // the receiver was null. - bugreporter::trackExpressionValue(N, Receiver, BR, - /*EnableNullFPSuppression*/ false); + bugreporter::trackExpressionValue( + N, Receiver, BR, bugreporter::TrackingKind::Thorough, + /*EnableNullFPSuppression*/ false); // Issue a message saying that the method was skipped. PathDiagnosticLocation L(Receiver, BRC.getSourceManager(), N->getLocationContext()); @@ -2015,57 +2129,16 @@ NilReceiverBRVisitor::VisitNode(const ExplodedNode *N, } //===----------------------------------------------------------------------===// -// Implementation of FindLastStoreBRVisitor. -//===----------------------------------------------------------------------===// - -// Registers every VarDecl inside a Stmt with a last store visitor. -void FindLastStoreBRVisitor::registerStatementVarDecls(BugReport &BR, - const Stmt *S, - bool EnableNullFPSuppression) { - const ExplodedNode *N = BR.getErrorNode(); - std::deque<const Stmt *> WorkList; - WorkList.push_back(S); - - while (!WorkList.empty()) { - const Stmt *Head = WorkList.front(); - WorkList.pop_front(); - - ProgramStateManager &StateMgr = N->getState()->getStateManager(); - - if (const auto *DR = dyn_cast<DeclRefExpr>(Head)) { - if (const auto *VD = dyn_cast<VarDecl>(DR->getDecl())) { - const VarRegion *R = - StateMgr.getRegionManager().getVarRegion(VD, N->getLocationContext()); - - // What did we load? - SVal V = N->getSVal(S); - - if (V.getAs<loc::ConcreteInt>() || V.getAs<nonloc::ConcreteInt>()) { - // Register a new visitor with the BugReport. - BR.addVisitor(llvm::make_unique<FindLastStoreBRVisitor>( - V.castAs<KnownSVal>(), R, EnableNullFPSuppression)); - } - } - } - - for (const Stmt *SubStmt : Head->children()) - WorkList.push_back(SubStmt); - } -} - -//===----------------------------------------------------------------------===// // Visitor that tries to report interesting diagnostics from conditions. //===----------------------------------------------------------------------===// /// Return the tag associated with this visitor. This tag will be used /// to make all PathDiagnosticPieces created by this visitor. -const char *ConditionBRVisitor::getTag() { - return "ConditionBRVisitor"; -} +const char *ConditionBRVisitor::getTag() { return "ConditionBRVisitor"; } -std::shared_ptr<PathDiagnosticPiece> -ConditionBRVisitor::VisitNode(const ExplodedNode *N, - BugReporterContext &BRC, BugReport &BR) { +PathDiagnosticPieceRef +ConditionBRVisitor::VisitNode(const ExplodedNode *N, BugReporterContext &BRC, + PathSensitiveBugReport &BR) { auto piece = VisitNodeImpl(N, BRC, BR); if (piece) { piece->setTag(getTag()); @@ -2075,9 +2148,10 @@ ConditionBRVisitor::VisitNode(const ExplodedNode *N, return piece; } -std::shared_ptr<PathDiagnosticPiece> +PathDiagnosticPieceRef ConditionBRVisitor::VisitNodeImpl(const ExplodedNode *N, - BugReporterContext &BRC, BugReport &BR) { + BugReporterContext &BRC, + PathSensitiveBugReport &BR) { ProgramPoint ProgPoint = N->getLocation(); const std::pair<const ProgramPointTag *, const ProgramPointTag *> &Tags = ExprEngine::geteagerlyAssumeBinOpBifurcationTags(); @@ -2113,9 +2187,10 @@ ConditionBRVisitor::VisitNodeImpl(const ExplodedNode *N, return nullptr; } -std::shared_ptr<PathDiagnosticPiece> ConditionBRVisitor::VisitTerminator( +PathDiagnosticPieceRef ConditionBRVisitor::VisitTerminator( const Stmt *Term, const ExplodedNode *N, const CFGBlock *srcBlk, - const CFGBlock *dstBlk, BugReport &R, BugReporterContext &BRC) { + const CFGBlock *dstBlk, PathSensitiveBugReport &R, + BugReporterContext &BRC) { const Expr *Cond = nullptr; // In the code below, Term is a CFG terminator and Cond is a branch condition @@ -2170,10 +2245,10 @@ std::shared_ptr<PathDiagnosticPiece> ConditionBRVisitor::VisitTerminator( return VisitTrueTest(Cond, BRC, R, N, TookTrue); } -std::shared_ptr<PathDiagnosticPiece> +PathDiagnosticPieceRef ConditionBRVisitor::VisitTrueTest(const Expr *Cond, BugReporterContext &BRC, - BugReport &R, const ExplodedNode *N, - bool TookTrue) { + PathSensitiveBugReport &R, + const ExplodedNode *N, bool TookTrue) { ProgramStateRef CurrentState = N->getState(); ProgramStateRef PrevState = N->getFirstPred()->getState(); const LocationContext *LCtx = N->getLocationContext(); @@ -2243,7 +2318,7 @@ bool ConditionBRVisitor::patternMatch(const Expr *Ex, const Expr *ParentEx, raw_ostream &Out, BugReporterContext &BRC, - BugReport &report, + PathSensitiveBugReport &report, const ExplodedNode *N, Optional<bool> &prunable, bool IsSameFieldName) { @@ -2258,7 +2333,7 @@ bool ConditionBRVisitor::patternMatch(const Expr *Ex, SourceLocation BeginLoc = OriginalExpr->getBeginLoc(); SourceLocation EndLoc = OriginalExpr->getEndLoc(); if (BeginLoc.isMacroID() && EndLoc.isMacroID()) { - SourceManager &SM = BRC.getSourceManager(); + const SourceManager &SM = BRC.getSourceManager(); const LangOptions &LO = BRC.getASTContext().getLangOpts(); if (Lexer::isAtStartOfMacroExpansion(BeginLoc, SM, LO) && Lexer::isAtEndOfMacroExpansion(EndLoc, SM, LO)) { @@ -2326,21 +2401,22 @@ bool ConditionBRVisitor::patternMatch(const Expr *Ex, return false; } -std::shared_ptr<PathDiagnosticPiece> ConditionBRVisitor::VisitTrueTest( +PathDiagnosticPieceRef ConditionBRVisitor::VisitTrueTest( const Expr *Cond, const BinaryOperator *BExpr, BugReporterContext &BRC, - BugReport &R, const ExplodedNode *N, bool TookTrue, bool IsAssuming) { + PathSensitiveBugReport &R, const ExplodedNode *N, bool TookTrue, + bool IsAssuming) { bool shouldInvert = false; Optional<bool> shouldPrune; // Check if the field name of the MemberExprs is ambiguous. Example: // " 'a.d' is equal to 'h.d' " in 'test/Analysis/null-deref-path-notes.cpp'. bool IsSameFieldName = false; - if (const auto *LhsME = - dyn_cast<MemberExpr>(BExpr->getLHS()->IgnoreParenCasts())) - if (const auto *RhsME = - dyn_cast<MemberExpr>(BExpr->getRHS()->IgnoreParenCasts())) - IsSameFieldName = LhsME->getMemberDecl()->getName() == - RhsME->getMemberDecl()->getName(); + const auto *LhsME = dyn_cast<MemberExpr>(BExpr->getLHS()->IgnoreParenCasts()); + const auto *RhsME = dyn_cast<MemberExpr>(BExpr->getRHS()->IgnoreParenCasts()); + + if (LhsME && RhsME) + IsSameFieldName = + LhsME->getMemberDecl()->getName() == RhsME->getMemberDecl()->getName(); SmallString<128> LhsString, RhsString; { @@ -2410,25 +2486,44 @@ std::shared_ptr<PathDiagnosticPiece> ConditionBRVisitor::VisitTrueTest( Out << (shouldInvert ? LhsString : RhsString); const LocationContext *LCtx = N->getLocationContext(); - PathDiagnosticLocation Loc(Cond, BRC.getSourceManager(), LCtx); + const SourceManager &SM = BRC.getSourceManager(); + + if (isVarAnInterestingCondition(BExpr->getLHS(), N, &R) || + isVarAnInterestingCondition(BExpr->getRHS(), N, &R)) + Out << WillBeUsedForACondition; // Convert 'field ...' to 'Field ...' if it is a MemberExpr. std::string Message = Out.str(); Message[0] = toupper(Message[0]); - // If we know the value create a pop-up note. - if (!IsAssuming) + // If we know the value create a pop-up note to the value part of 'BExpr'. + if (!IsAssuming) { + PathDiagnosticLocation Loc; + if (!shouldInvert) { + if (LhsME && LhsME->getMemberLoc().isValid()) + Loc = PathDiagnosticLocation(LhsME->getMemberLoc(), SM); + else + Loc = PathDiagnosticLocation(BExpr->getLHS(), SM, LCtx); + } else { + if (RhsME && RhsME->getMemberLoc().isValid()) + Loc = PathDiagnosticLocation(RhsME->getMemberLoc(), SM); + else + Loc = PathDiagnosticLocation(BExpr->getRHS(), SM, LCtx); + } + return std::make_shared<PathDiagnosticPopUpPiece>(Loc, Message); + } + PathDiagnosticLocation Loc(Cond, SM, LCtx); auto event = std::make_shared<PathDiagnosticEventPiece>(Loc, Message); if (shouldPrune.hasValue()) event->setPrunable(shouldPrune.getValue()); return event; } -std::shared_ptr<PathDiagnosticPiece> ConditionBRVisitor::VisitConditionVariable( +PathDiagnosticPieceRef ConditionBRVisitor::VisitConditionVariable( StringRef LhsString, const Expr *CondVarExpr, BugReporterContext &BRC, - BugReport &report, const ExplodedNode *N, bool TookTrue) { + PathSensitiveBugReport &report, const ExplodedNode *N, bool TookTrue) { // FIXME: If there's already a constraint tracker for this variable, // we shouldn't emit anything here (c.f. the double note in // test/Analysis/inlining/path-notes.c) @@ -2441,24 +2536,22 @@ std::shared_ptr<PathDiagnosticPiece> ConditionBRVisitor::VisitConditionVariable( const LocationContext *LCtx = N->getLocationContext(); PathDiagnosticLocation Loc(CondVarExpr, BRC.getSourceManager(), LCtx); + + if (isVarAnInterestingCondition(CondVarExpr, N, &report)) + Out << WillBeUsedForACondition; + auto event = std::make_shared<PathDiagnosticEventPiece>(Loc, Out.str()); - if (const auto *DR = dyn_cast<DeclRefExpr>(CondVarExpr)) { - if (const auto *VD = dyn_cast<VarDecl>(DR->getDecl())) { - const ProgramState *state = N->getState().get(); - if (const MemRegion *R = state->getLValue(VD, LCtx).getAsRegion()) { - if (report.isInteresting(R)) - event->setPrunable(false); - } - } - } + if (isInterestingExpr(CondVarExpr, N, &report)) + event->setPrunable(false); return event; } -std::shared_ptr<PathDiagnosticPiece> ConditionBRVisitor::VisitTrueTest( +PathDiagnosticPieceRef ConditionBRVisitor::VisitTrueTest( const Expr *Cond, const DeclRefExpr *DRE, BugReporterContext &BRC, - BugReport &report, const ExplodedNode *N, bool TookTrue, bool IsAssuming) { + PathSensitiveBugReport &report, const ExplodedNode *N, bool TookTrue, + bool IsAssuming) { const auto *VD = dyn_cast<VarDecl>(DRE->getDecl()); if (!VD) return nullptr; @@ -2472,29 +2565,29 @@ std::shared_ptr<PathDiagnosticPiece> ConditionBRVisitor::VisitTrueTest( return nullptr; const LocationContext *LCtx = N->getLocationContext(); - PathDiagnosticLocation Loc(Cond, BRC.getSourceManager(), LCtx); - // If we know the value create a pop-up note. - if (!IsAssuming) + if (isVarAnInterestingCondition(DRE, N, &report)) + Out << WillBeUsedForACondition; + + // If we know the value create a pop-up note to the 'DRE'. + if (!IsAssuming) { + PathDiagnosticLocation Loc(DRE, BRC.getSourceManager(), LCtx); return std::make_shared<PathDiagnosticPopUpPiece>(Loc, Out.str()); + } + PathDiagnosticLocation Loc(Cond, BRC.getSourceManager(), LCtx); auto event = std::make_shared<PathDiagnosticEventPiece>(Loc, Out.str()); - const ProgramState *state = N->getState().get(); - if (const MemRegion *R = state->getLValue(VD, LCtx).getAsRegion()) { - if (report.isInteresting(R)) - event->setPrunable(false); - else { - SVal V = state->getSVal(R); - if (report.isInteresting(V)) - event->setPrunable(false); - } - } + + if (isInterestingExpr(DRE, N, &report)) + event->setPrunable(false); + return std::move(event); } -std::shared_ptr<PathDiagnosticPiece> ConditionBRVisitor::VisitTrueTest( +PathDiagnosticPieceRef ConditionBRVisitor::VisitTrueTest( const Expr *Cond, const MemberExpr *ME, BugReporterContext &BRC, - BugReport &report, const ExplodedNode *N, bool TookTrue, bool IsAssuming) { + PathSensitiveBugReport &report, const ExplodedNode *N, bool TookTrue, + bool IsAssuming) { SmallString<256> Buf; llvm::raw_svector_ostream Out(Buf); @@ -2505,15 +2598,28 @@ std::shared_ptr<PathDiagnosticPiece> ConditionBRVisitor::VisitTrueTest( return nullptr; const LocationContext *LCtx = N->getLocationContext(); - PathDiagnosticLocation Loc(Cond, BRC.getSourceManager(), LCtx); + PathDiagnosticLocation Loc; + + // If we know the value create a pop-up note to the member of the MemberExpr. + if (!IsAssuming && ME->getMemberLoc().isValid()) + Loc = PathDiagnosticLocation(ME->getMemberLoc(), BRC.getSourceManager()); + else + Loc = PathDiagnosticLocation(Cond, BRC.getSourceManager(), LCtx); + if (!Loc.isValid() || !Loc.asLocation().isValid()) return nullptr; + if (isVarAnInterestingCondition(ME, N, &report)) + Out << WillBeUsedForACondition; + // If we know the value create a pop-up note. if (!IsAssuming) return std::make_shared<PathDiagnosticPopUpPiece>(Loc, Out.str()); - return std::make_shared<PathDiagnosticEventPiece>(Loc, Out.str()); + auto event = std::make_shared<PathDiagnosticEventPiece>(Loc, Out.str()); + if (isInterestingExpr(ME, N, &report)) + event->setPrunable(false); + return event; } bool ConditionBRVisitor::printValue(const Expr *CondVarExpr, raw_ostream &Out, @@ -2553,10 +2659,8 @@ bool ConditionBRVisitor::printValue(const Expr *CondVarExpr, raw_ostream &Out, return true; } -const char *const ConditionBRVisitor::GenericTrueMessage = - "Assuming the condition is true"; -const char *const ConditionBRVisitor::GenericFalseMessage = - "Assuming the condition is false"; +constexpr llvm::StringLiteral ConditionBRVisitor::GenericTrueMessage; +constexpr llvm::StringLiteral ConditionBRVisitor::GenericFalseMessage; bool ConditionBRVisitor::isPieceMessageGeneric( const PathDiagnosticPiece *Piece) { @@ -2569,10 +2673,11 @@ bool ConditionBRVisitor::isPieceMessageGeneric( //===----------------------------------------------------------------------===// void LikelyFalsePositiveSuppressionBRVisitor::finalizeVisitor( - BugReporterContext &BRC, const ExplodedNode *N, BugReport &BR) { + BugReporterContext &BRC, const ExplodedNode *N, + PathSensitiveBugReport &BR) { // Here we suppress false positives coming from system headers. This list is // based on known issues. - AnalyzerOptions &Options = BRC.getAnalyzerOptions(); + const AnalyzerOptions &Options = BRC.getAnalyzerOptions(); const Decl *D = N->getLocationContext()->getDecl(); if (AnalysisDeclContext::isInStdNamespace(D)) { @@ -2639,8 +2744,8 @@ void LikelyFalsePositiveSuppressionBRVisitor::finalizeVisitor( // Skip reports within the sys/queue.h macros as we do not have the ability to // reason about data structure shapes. - SourceManager &SM = BRC.getSourceManager(); - FullSourceLoc Loc = BR.getLocation(SM).asLocation(); + const SourceManager &SM = BRC.getSourceManager(); + FullSourceLoc Loc = BR.getLocation().asLocation(); while (Loc.isMacroID()) { Loc = Loc.getSpellingLoc(); if (SM.getFilename(Loc).endswith("sys/queue.h")) { @@ -2654,9 +2759,9 @@ void LikelyFalsePositiveSuppressionBRVisitor::finalizeVisitor( // Implementation of UndefOrNullArgVisitor. //===----------------------------------------------------------------------===// -std::shared_ptr<PathDiagnosticPiece> -UndefOrNullArgVisitor::VisitNode(const ExplodedNode *N, - BugReporterContext &BRC, BugReport &BR) { +PathDiagnosticPieceRef +UndefOrNullArgVisitor::VisitNode(const ExplodedNode *N, BugReporterContext &BRC, + PathSensitiveBugReport &BR) { ProgramStateRef State = N->getState(); ProgramPoint ProgLoc = N->getLocation(); @@ -2712,7 +2817,8 @@ FalsePositiveRefutationBRVisitor::FalsePositiveRefutationBRVisitor() : Constraints(ConstraintRangeTy::Factory().getEmptyMap()) {} void FalsePositiveRefutationBRVisitor::finalizeVisitor( - BugReporterContext &BRC, const ExplodedNode *EndPathNode, BugReport &BR) { + BugReporterContext &BRC, const ExplodedNode *EndPathNode, + PathSensitiveBugReport &BR) { // Collect new constraints VisitNode(EndPathNode, BRC, BR); @@ -2747,10 +2853,8 @@ void FalsePositiveRefutationBRVisitor::finalizeVisitor( BR.markInvalid("Infeasible constraints", EndPathNode->getLocationContext()); } -std::shared_ptr<PathDiagnosticPiece> -FalsePositiveRefutationBRVisitor::VisitNode(const ExplodedNode *N, - BugReporterContext &, - BugReport &) { +PathDiagnosticPieceRef FalsePositiveRefutationBRVisitor::VisitNode( + const ExplodedNode *N, BugReporterContext &, PathSensitiveBugReport &) { // Collect new constraints const ConstraintRangeTy &NewCs = N->getState()->get<ConstraintRange>(); ConstraintRangeTy::Factory &CF = @@ -2784,9 +2888,9 @@ void TagVisitor::Profile(llvm::FoldingSetNodeID &ID) const { ID.AddPointer(&Tag); } -std::shared_ptr<PathDiagnosticPiece> -TagVisitor::VisitNode(const ExplodedNode *N, BugReporterContext &BRC, - BugReport &R) { +PathDiagnosticPieceRef TagVisitor::VisitNode(const ExplodedNode *N, + BugReporterContext &BRC, + PathSensitiveBugReport &R) { ProgramPoint PP = N->getLocation(); const NoteTag *T = dyn_cast_or_null<NoteTag>(PP.getTag()); if (!T) diff --git a/lib/StaticAnalyzer/Core/CallEvent.cpp b/lib/StaticAnalyzer/Core/CallEvent.cpp index a5f7500e63074..5f04a59ba0551 100644 --- a/lib/StaticAnalyzer/Core/CallEvent.cpp +++ b/lib/StaticAnalyzer/Core/CallEvent.cpp @@ -27,6 +27,7 @@ #include "clang/Analysis/AnalysisDeclContext.h" #include "clang/Analysis/CFG.h" #include "clang/Analysis/CFGStmtMap.h" +#include "clang/Analysis/PathDiagnostic.h" #include "clang/Analysis/ProgramPoint.h" #include "clang/CrossTU/CrossTranslationUnit.h" #include "clang/Basic/IdentifierTable.h" @@ -34,10 +35,9 @@ #include "clang/Basic/SourceLocation.h" #include "clang/Basic/SourceManager.h" #include "clang/Basic/Specifiers.h" -#include "clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" #include "clang/StaticAnalyzer/Core/PathSensitive/DynamicTypeInfo.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/DynamicTypeMap.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/DynamicType.h" #include "clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState_Fwd.h" @@ -191,7 +191,8 @@ AnalysisDeclContext *CallEvent::getCalleeAnalysisDeclContext() const { return ADC; } -const StackFrameContext *CallEvent::getCalleeStackFrame() const { +const StackFrameContext * +CallEvent::getCalleeStackFrame(unsigned BlockCount) const { AnalysisDeclContext *ADC = getCalleeAnalysisDeclContext(); if (!ADC) return nullptr; @@ -217,11 +218,12 @@ const StackFrameContext *CallEvent::getCalleeStackFrame() const { break; assert(Idx < Sz); - return ADC->getManager()->getStackFrame(ADC, LCtx, E, B, Idx); + return ADC->getManager()->getStackFrame(ADC, LCtx, E, B, BlockCount, Idx); } -const VarRegion *CallEvent::getParameterLocation(unsigned Index) const { - const StackFrameContext *SFC = getCalleeStackFrame(); +const VarRegion *CallEvent::getParameterLocation(unsigned Index, + unsigned BlockCount) const { + const StackFrameContext *SFC = getCalleeStackFrame(BlockCount); // We cannot construct a VarRegion without a stack frame. if (!SFC) return nullptr; @@ -322,7 +324,7 @@ ProgramStateRef CallEvent::invalidateRegions(unsigned BlockCount, if (getKind() != CE_CXXAllocator) if (isArgumentConstructedDirectly(Idx)) if (auto AdjIdx = getAdjustedParameterIndex(Idx)) - if (const VarRegion *VR = getParameterLocation(*AdjIdx)) + if (const VarRegion *VR = getParameterLocation(*AdjIdx, BlockCount)) ValuesToInvalidate.push_back(loc::MemRegionVal(VR)); } @@ -366,7 +368,8 @@ bool CallEvent::isCalled(const CallDescription &CD) const { if (CD.Flags & CDF_MaybeBuiltin) { return CheckerContext::isCLibraryFunction(FD, CD.getFunctionName()) && - (!CD.RequiredArgs || CD.RequiredArgs <= getNumArgs()); + (!CD.RequiredArgs || CD.RequiredArgs <= getNumArgs()) && + (!CD.RequiredParams || CD.RequiredParams <= parameters().size()); } if (!CD.IsLookupDone) { @@ -405,7 +408,8 @@ bool CallEvent::isCalled(const CallDescription &CD) const { return false; } - return (!CD.RequiredArgs || CD.RequiredArgs == getNumArgs()); + return (!CD.RequiredArgs || CD.RequiredArgs == getNumArgs()) && + (!CD.RequiredParams || CD.RequiredParams == parameters().size()); } SVal CallEvent::getArgSVal(unsigned Index) const { @@ -1033,7 +1037,7 @@ getSyntacticFromForPseudoObjectExpr(const PseudoObjectExpr *POE) { ObjCMessageKind ObjCMethodCall::getMessageKind() const { if (!Data) { // Find the parent, ignoring implicit casts. - ParentMap &PM = getLocationContext()->getParentMap(); + const ParentMap &PM = getLocationContext()->getParentMap(); const Stmt *S = PM.getParentIgnoreParenCasts(getOriginExpr()); // Check if parent is a PseudoObjectExpr. diff --git a/lib/StaticAnalyzer/Core/Checker.cpp b/lib/StaticAnalyzer/Core/Checker.cpp index f4e6f909d7644..bc1c8964b3ee4 100644 --- a/lib/StaticAnalyzer/Core/Checker.cpp +++ b/lib/StaticAnalyzer/Core/Checker.cpp @@ -19,10 +19,10 @@ using namespace ento; int ImplicitNullDerefEvent::Tag; StringRef CheckerBase::getTagDescription() const { - return getCheckName().getName(); + return getCheckerName().getName(); } -CheckName CheckerBase::getCheckName() const { return Name; } +CheckerNameRef CheckerBase::getCheckerName() const { return Name; } CheckerProgramPointTag::CheckerProgramPointTag(StringRef CheckerName, StringRef Msg) @@ -30,10 +30,10 @@ CheckerProgramPointTag::CheckerProgramPointTag(StringRef CheckerName, CheckerProgramPointTag::CheckerProgramPointTag(const CheckerBase *Checker, StringRef Msg) - : SimpleProgramPointTag(Checker->getCheckName().getName(), Msg) {} + : SimpleProgramPointTag(Checker->getCheckerName().getName(), Msg) {} raw_ostream& clang::ento::operator<<(raw_ostream &Out, const CheckerBase &Checker) { - Out << Checker.getCheckName().getName(); + Out << Checker.getCheckerName().getName(); return Out; } diff --git a/lib/StaticAnalyzer/Core/CheckerHelpers.cpp b/lib/StaticAnalyzer/Core/CheckerHelpers.cpp index 34cdc9db699db..11693132de687 100644 --- a/lib/StaticAnalyzer/Core/CheckerHelpers.cpp +++ b/lib/StaticAnalyzer/Core/CheckerHelpers.cpp @@ -91,7 +91,7 @@ parseAssignment(const Stmt *S) { } 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()); + VD = cast<VarDecl>(PD->getSingleDecl()); RHS = VD->getAnyInitializer(); } diff --git a/lib/StaticAnalyzer/Core/CheckerManager.cpp b/lib/StaticAnalyzer/Core/CheckerManager.cpp index 27d5797b4cbc9..f676bd8952836 100644 --- a/lib/StaticAnalyzer/Core/CheckerManager.cpp +++ b/lib/StaticAnalyzer/Core/CheckerManager.cpp @@ -748,7 +748,7 @@ void CheckerManager::runCheckersForPrintStateJson(raw_ostream &Out, continue; Indent(Out, Space, IsDot) - << "{ \"checker\": \"" << CT.second->getCheckName().getName() + << "{ \"checker\": \"" << CT.second->getCheckerName().getName() << "\", \"messages\": [" << NL; Indent(Out, InnerSpace, IsDot) << '\"' << TempBuf.str().trim() << '\"' << NL; diff --git a/lib/StaticAnalyzer/Core/CommonBugCategories.cpp b/lib/StaticAnalyzer/Core/CommonBugCategories.cpp index 54501314386a9..bdae3e605efff 100644 --- a/lib/StaticAnalyzer/Core/CommonBugCategories.cpp +++ b/lib/StaticAnalyzer/Core/CommonBugCategories.cpp @@ -17,4 +17,5 @@ const char * const MemoryRefCount = "Memory (Core Foundation/Objective-C/OSObject)"; const char * const MemoryError = "Memory error"; const char * const UnixAPI = "Unix API"; +const char * const CXXObjectLifecycle = "C++ object lifecycle"; }}} diff --git a/lib/StaticAnalyzer/Core/DynamicType.cpp b/lib/StaticAnalyzer/Core/DynamicType.cpp new file mode 100644 index 0000000000000..a78e0e05e903b --- /dev/null +++ b/lib/StaticAnalyzer/Core/DynamicType.cpp @@ -0,0 +1,229 @@ +//===- DynamicType.cpp - Dynamic type related APIs --------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file defines APIs that track and query dynamic type information. This +// information can be used to devirtualize calls during the symbolic execution +// or do type checking. +// +//===----------------------------------------------------------------------===// + +#include "clang/StaticAnalyzer/Core/PathSensitive/DynamicType.h" +#include "clang/Basic/JsonSupport.h" +#include "clang/Basic/LLVM.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/SymExpr.h" +#include "llvm/Support/Casting.h" +#include "llvm/Support/raw_ostream.h" +#include <cassert> + +/// The GDM component containing the dynamic type info. This is a map from a +/// symbol to its most likely type. +REGISTER_MAP_WITH_PROGRAMSTATE(DynamicTypeMap, const clang::ento::MemRegion *, + clang::ento::DynamicTypeInfo) + +/// A set factory of dynamic cast informations. +REGISTER_SET_FACTORY_WITH_PROGRAMSTATE(CastSet, clang::ento::DynamicCastInfo) + +/// A map from symbols to cast informations. +REGISTER_MAP_WITH_PROGRAMSTATE(DynamicCastMap, const clang::ento::MemRegion *, + CastSet) + +namespace clang { +namespace ento { + +DynamicTypeInfo getDynamicTypeInfo(ProgramStateRef State, const MemRegion *MR) { + MR = MR->StripCasts(); + + // Look up the dynamic type in the GDM. + if (const DynamicTypeInfo *DTI = State->get<DynamicTypeMap>(MR)) + return *DTI; + + // Otherwise, fall back to what we know about the region. + if (const auto *TR = dyn_cast<TypedRegion>(MR)) + return DynamicTypeInfo(TR->getLocationType(), /*CanBeSub=*/false); + + if (const auto *SR = dyn_cast<SymbolicRegion>(MR)) { + SymbolRef Sym = SR->getSymbol(); + return DynamicTypeInfo(Sym->getType()); + } + + return {}; +} + +const DynamicTypeInfo *getRawDynamicTypeInfo(ProgramStateRef State, + const MemRegion *MR) { + return State->get<DynamicTypeMap>(MR); +} + +const DynamicCastInfo *getDynamicCastInfo(ProgramStateRef State, + const MemRegion *MR, + QualType CastFromTy, + QualType CastToTy) { + const auto *Lookup = State->get<DynamicCastMap>().lookup(MR); + if (!Lookup) + return nullptr; + + for (const DynamicCastInfo &Cast : *Lookup) + if (Cast.equals(CastFromTy, CastToTy)) + return &Cast; + + return nullptr; +} + +ProgramStateRef setDynamicTypeInfo(ProgramStateRef State, const MemRegion *MR, + DynamicTypeInfo NewTy) { + State = State->set<DynamicTypeMap>(MR->StripCasts(), NewTy); + assert(State); + return State; +} + +ProgramStateRef setDynamicTypeInfo(ProgramStateRef State, const MemRegion *MR, + QualType NewTy, bool CanBeSubClassed) { + return setDynamicTypeInfo(State, MR, DynamicTypeInfo(NewTy, CanBeSubClassed)); +} + +ProgramStateRef setDynamicTypeAndCastInfo(ProgramStateRef State, + const MemRegion *MR, + QualType CastFromTy, + QualType CastToTy, + bool CastSucceeds) { + if (!MR) + return State; + + if (CastSucceeds) { + assert((CastToTy->isAnyPointerType() || CastToTy->isReferenceType()) && + "DynamicTypeInfo should always be a pointer."); + State = State->set<DynamicTypeMap>(MR, CastToTy); + } + + DynamicCastInfo::CastResult ResultKind = + CastSucceeds ? DynamicCastInfo::CastResult::Success + : DynamicCastInfo::CastResult::Failure; + + CastSet::Factory &F = State->get_context<CastSet>(); + + const CastSet *TempSet = State->get<DynamicCastMap>(MR); + CastSet Set = TempSet ? *TempSet : F.getEmptySet(); + + Set = F.add(Set, {CastFromTy, CastToTy, ResultKind}); + State = State->set<DynamicCastMap>(MR, Set); + + assert(State); + return State; +} + +template <typename MapTy> +ProgramStateRef removeDead(ProgramStateRef State, const MapTy &Map, + SymbolReaper &SR) { + for (const auto &Elem : Map) + if (!SR.isLiveRegion(Elem.first)) + State = State->remove<DynamicCastMap>(Elem.first); + + return State; +} + +ProgramStateRef removeDeadTypes(ProgramStateRef State, SymbolReaper &SR) { + return removeDead(State, State->get<DynamicTypeMap>(), SR); +} + +ProgramStateRef removeDeadCasts(ProgramStateRef State, SymbolReaper &SR) { + return removeDead(State, State->get<DynamicCastMap>(), SR); +} + +static void printDynamicTypesJson(raw_ostream &Out, ProgramStateRef State, + const char *NL, unsigned int Space, + bool IsDot) { + Indent(Out, Space, IsDot) << "\"dynamic_types\": "; + + const DynamicTypeMapTy &Map = State->get<DynamicTypeMap>(); + if (Map.isEmpty()) { + Out << "null," << NL; + return; + } + + ++Space; + Out << '[' << NL; + for (DynamicTypeMapTy::iterator I = Map.begin(); I != Map.end(); ++I) { + const MemRegion *MR = I->first; + const DynamicTypeInfo &DTI = I->second; + Indent(Out, Space, IsDot) + << "{ \"region\": \"" << MR << "\", \"dyn_type\": "; + if (!DTI.isValid()) { + Out << "null"; + } else { + Out << '\"' << DTI.getType()->getPointeeType().getAsString() + << "\", \"sub_classable\": " + << (DTI.canBeASubClass() ? "true" : "false"); + } + Out << " }"; + + if (std::next(I) != Map.end()) + Out << ','; + Out << NL; + } + + --Space; + Indent(Out, Space, IsDot) << "]," << NL; +} + +static void printDynamicCastsJson(raw_ostream &Out, ProgramStateRef State, + const char *NL, unsigned int Space, + bool IsDot) { + Indent(Out, Space, IsDot) << "\"dynamic_casts\": "; + + const DynamicCastMapTy &Map = State->get<DynamicCastMap>(); + if (Map.isEmpty()) { + Out << "null," << NL; + return; + } + + ++Space; + Out << '[' << NL; + for (DynamicCastMapTy::iterator I = Map.begin(); I != Map.end(); ++I) { + const MemRegion *MR = I->first; + const CastSet &Set = I->second; + + Indent(Out, Space, IsDot) << "{ \"region\": \"" << MR << "\", \"casts\": "; + if (Set.isEmpty()) { + Out << "null "; + } else { + ++Space; + Out << '[' << NL; + for (CastSet::iterator SI = Set.begin(); SI != Set.end(); ++SI) { + Indent(Out, Space, IsDot) + << "{ \"from\": \"" << SI->from().getAsString() << "\", \"to\": \"" + << SI->to().getAsString() << "\", \"kind\": \"" + << (SI->succeeds() ? "success" : "fail") << "\" }"; + + if (std::next(SI) != Set.end()) + Out << ','; + Out << NL; + } + --Space; + Indent(Out, Space, IsDot) << ']'; + } + Out << '}'; + + if (std::next(I) != Map.end()) + Out << ','; + Out << NL; + } + + --Space; + Indent(Out, Space, IsDot) << "]," << NL; +} + +void printDynamicTypeInfoJson(raw_ostream &Out, ProgramStateRef State, + const char *NL, unsigned int Space, bool IsDot) { + printDynamicTypesJson(Out, State, NL, Space, IsDot); + printDynamicCastsJson(Out, State, NL, Space, IsDot); +} + +} // namespace ento +} // namespace clang diff --git a/lib/StaticAnalyzer/Core/DynamicTypeMap.cpp b/lib/StaticAnalyzer/Core/DynamicTypeMap.cpp deleted file mode 100644 index 79424452240d7..0000000000000 --- a/lib/StaticAnalyzer/Core/DynamicTypeMap.cpp +++ /dev/null @@ -1,97 +0,0 @@ -//===- DynamicTypeMap.cpp - Dynamic Type Info related APIs ----------------===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -// -// This file defines APIs that track and query dynamic type information. This -// information can be used to devirtualize calls during the symbolic execution -// or do type checking. -// -//===----------------------------------------------------------------------===// - -#include "clang/StaticAnalyzer/Core/PathSensitive/DynamicTypeMap.h" -#include "clang/Basic/JsonSupport.h" -#include "clang/Basic/LLVM.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/SymExpr.h" -#include "llvm/Support/Casting.h" -#include "llvm/Support/raw_ostream.h" -#include <cassert> - -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 auto *TR = dyn_cast<TypedRegion>(Reg)) - return DynamicTypeInfo(TR->getLocationType(), /*CanBeSub=*/false); - - if (const auto *SR = dyn_cast<SymbolicRegion>(Reg)) { - SymbolRef Sym = SR->getSymbol(); - return DynamicTypeInfo(Sym->getType()); - } - - return {}; -} - -ProgramStateRef setDynamicTypeInfo(ProgramStateRef State, const MemRegion *Reg, - DynamicTypeInfo NewTy) { - Reg = Reg->StripCasts(); - ProgramStateRef NewState = State->set<DynamicTypeMap>(Reg, NewTy); - assert(NewState); - return NewState; -} - -void printDynamicTypeInfoJson(raw_ostream &Out, ProgramStateRef State, - const char *NL, unsigned int Space, bool IsDot) { - Indent(Out, Space, IsDot) << "\"dynamic_types\": "; - - const DynamicTypeMapTy &DTM = State->get<DynamicTypeMap>(); - if (DTM.isEmpty()) { - Out << "null," << NL; - return; - } - - ++Space; - Out << '[' << NL; - for (DynamicTypeMapTy::iterator I = DTM.begin(); I != DTM.end(); ++I) { - const MemRegion *MR = I->first; - const DynamicTypeInfo &DTI = I->second; - Out << "{ \"region\": \"" << MR << "\", \"dyn_type\": "; - if (DTI.isValid()) { - Out << '\"' << DTI.getType()->getPointeeType().getAsString() - << "\", \"sub_classable\": " - << (DTI.canBeASubClass() ? "true" : "false"); - } else { - Out << "null"; // Invalid type info - } - Out << "}"; - - if (std::next(I) != DTM.end()) - Out << ','; - Out << NL; - } - - --Space; - Indent(Out, Space, IsDot) << "]," << NL; -} - -void *ProgramStateTrait<DynamicTypeMap>::GDMIndex() { - static int index = 0; - return &index; -} - -} // namespace ento -} // namespace clang diff --git a/lib/StaticAnalyzer/Core/Environment.cpp b/lib/StaticAnalyzer/Core/Environment.cpp index 551c89b04db4b..1ccf4c6104a65 100644 --- a/lib/StaticAnalyzer/Core/Environment.cpp +++ b/lib/StaticAnalyzer/Core/Environment.cpp @@ -108,6 +108,7 @@ SVal Environment::getSVal(const EnvironmentEntry &Entry, case Stmt::ObjCStringLiteralClass: case Stmt::StringLiteralClass: case Stmt::TypeTraitExprClass: + case Stmt::SizeOfPackExprClass: // Known constants; defer to SValBuilder. return svalBuilder.getConstantVal(cast<Expr>(S)).getValue(); diff --git a/lib/StaticAnalyzer/Core/ExplodedGraph.cpp b/lib/StaticAnalyzer/Core/ExplodedGraph.cpp index c86b1436baabb..c4838492271cd 100644 --- a/lib/StaticAnalyzer/Core/ExplodedGraph.cpp +++ b/lib/StaticAnalyzer/Core/ExplodedGraph.cpp @@ -16,6 +16,7 @@ #include "clang/AST/ExprObjC.h" #include "clang/AST/ParentMap.h" #include "clang/AST/Stmt.h" +#include "clang/Analysis/CFGStmtMap.h" #include "clang/Analysis/ProgramPoint.h" #include "clang/Analysis/Support/BumpVector.h" #include "clang/Basic/LLVM.h" @@ -134,7 +135,7 @@ bool ExplodedGraph::shouldCollect(const ExplodedNode *node) { // Do not collect nodes for non-consumed Stmt or Expr to ensure precise // diagnostic generation; specifically, so that we could anchor arrows // pointing to the beginning of statements (as written in code). - ParentMap &PM = progPoint.getLocationContext()->getParentMap(); + const ParentMap &PM = progPoint.getLocationContext()->getParentMap(); if (!PM.isConsumedExpr(Ex)) return false; @@ -282,16 +283,115 @@ ExplodedNode * const *ExplodedNode::NodeGroup::end() const { return Storage.getAddrOfPtr1() + 1; } -int64_t ExplodedNode::getID(ExplodedGraph *G) const { - return G->getAllocator().identifyKnownAlignedObject<ExplodedNode>(this); -} - bool ExplodedNode::isTrivial() const { return pred_size() == 1 && succ_size() == 1 && getFirstPred()->getState()->getID() == getState()->getID() && getFirstPred()->succ_size() == 1; } +const CFGBlock *ExplodedNode::getCFGBlock() const { + ProgramPoint P = getLocation(); + if (auto BEP = P.getAs<BlockEntrance>()) + return BEP->getBlock(); + + // Find the node's current statement in the CFG. + // FIXME: getStmtForDiagnostics() does nasty things in order to provide + // a valid statement for body farms, do we need this behavior here? + if (const Stmt *S = getStmtForDiagnostics()) + return getLocationContext() + ->getAnalysisDeclContext() + ->getCFGStmtMap() + ->getBlock(S); + + return nullptr; +} + +static const LocationContext * +findTopAutosynthesizedParentContext(const LocationContext *LC) { + assert(LC->getAnalysisDeclContext()->isBodyAutosynthesized()); + const LocationContext *ParentLC = LC->getParent(); + assert(ParentLC && "We don't start analysis from autosynthesized code"); + while (ParentLC->getAnalysisDeclContext()->isBodyAutosynthesized()) { + LC = ParentLC; + ParentLC = LC->getParent(); + assert(ParentLC && "We don't start analysis from autosynthesized code"); + } + return LC; +} + +const Stmt *ExplodedNode::getStmtForDiagnostics() const { + // We cannot place diagnostics on autosynthesized code. + // Put them onto the call site through which we jumped into autosynthesized + // code for the first time. + const LocationContext *LC = getLocationContext(); + if (LC->getAnalysisDeclContext()->isBodyAutosynthesized()) { + // It must be a stack frame because we only autosynthesize functions. + return cast<StackFrameContext>(findTopAutosynthesizedParentContext(LC)) + ->getCallSite(); + } + // Otherwise, see if the node's program point directly points to a statement. + // FIXME: Refactor into a ProgramPoint method? + ProgramPoint P = getLocation(); + if (auto SP = P.getAs<StmtPoint>()) + return SP->getStmt(); + if (auto BE = P.getAs<BlockEdge>()) + return BE->getSrc()->getTerminatorStmt(); + if (auto CE = P.getAs<CallEnter>()) + return CE->getCallExpr(); + if (auto CEE = P.getAs<CallExitEnd>()) + return CEE->getCalleeContext()->getCallSite(); + if (auto PIPP = P.getAs<PostInitializer>()) + return PIPP->getInitializer()->getInit(); + if (auto CEB = P.getAs<CallExitBegin>()) + return CEB->getReturnStmt(); + if (auto FEP = P.getAs<FunctionExitPoint>()) + return FEP->getStmt(); + + return nullptr; +} + +const Stmt *ExplodedNode::getNextStmtForDiagnostics() const { + for (const ExplodedNode *N = getFirstSucc(); N; N = N->getFirstSucc()) { + if (const Stmt *S = N->getStmtForDiagnostics()) { + // Check if the statement is '?' or '&&'/'||'. These are "merges", + // not actual statement points. + switch (S->getStmtClass()) { + case Stmt::ChooseExprClass: + case Stmt::BinaryConditionalOperatorClass: + case Stmt::ConditionalOperatorClass: + continue; + case Stmt::BinaryOperatorClass: { + BinaryOperatorKind Op = cast<BinaryOperator>(S)->getOpcode(); + if (Op == BO_LAnd || Op == BO_LOr) + continue; + break; + } + default: + break; + } + // We found the statement, so return it. + return S; + } + } + + return nullptr; +} + +const Stmt *ExplodedNode::getPreviousStmtForDiagnostics() const { + for (const ExplodedNode *N = getFirstPred(); N; N = N->getFirstPred()) + if (const Stmt *S = N->getStmtForDiagnostics()) + return S; + + return nullptr; +} + +const Stmt *ExplodedNode::getCurrentOrPreviousStmtForDiagnostics() const { + if (const Stmt *S = getStmtForDiagnostics()) + return S; + + return getPreviousStmtForDiagnostics(); +} + ExplodedNode *ExplodedGraph::getNode(const ProgramPoint &L, ProgramStateRef State, bool IsSink, @@ -313,14 +413,14 @@ ExplodedNode *ExplodedGraph::getNode(const ProgramPoint &L, V = (NodeTy*) getAllocator().Allocate<NodeTy>(); } - new (V) NodeTy(L, State, IsSink); + ++NumNodes; + new (V) NodeTy(L, State, NumNodes, IsSink); if (ReclaimNodeInterval) ChangedNodes.push_back(V); // Insert the node into the node set and return it. Nodes.InsertNode(V, InsertPos); - ++NumNodes; if (IsNew) *IsNew = true; } @@ -332,9 +432,10 @@ ExplodedNode *ExplodedGraph::getNode(const ProgramPoint &L, ExplodedNode *ExplodedGraph::createUncachedNode(const ProgramPoint &L, ProgramStateRef State, + int64_t Id, bool IsSink) { NodeTy *V = (NodeTy *) getAllocator().Allocate<NodeTy>(); - new (V) NodeTy(L, State, IsSink); + new (V) NodeTy(L, State, Id, IsSink); return V; } @@ -394,7 +495,8 @@ ExplodedGraph::trim(ArrayRef<const NodeTy *> Sinks, // Create the corresponding node in the new graph and record the mapping // from the old node to the new node. - ExplodedNode *NewN = G->createUncachedNode(N->getLocation(), N->State, N->isSink()); + ExplodedNode *NewN = G->createUncachedNode(N->getLocation(), N->State, + N->getID(), N->isSink()); Pass2[N] = NewN; // Also record the reverse mapping from the new node to the old node. diff --git a/lib/StaticAnalyzer/Core/ExprEngine.cpp b/lib/StaticAnalyzer/Core/ExprEngine.cpp index 1fef5b3c1edd5..e92e95354f5f2 100644 --- a/lib/StaticAnalyzer/Core/ExprEngine.cpp +++ b/lib/StaticAnalyzer/Core/ExprEngine.cpp @@ -977,8 +977,8 @@ void ExprEngine::ProcessAutomaticObjDtor(const CFGAutomaticObjDtor Dtor, Region = makeZeroElementRegion(state, loc::MemRegionVal(Region), varType, CallOpts.IsArrayCtorOrDtor).getAsRegion(); - VisitCXXDestructor(varType, Region, Dtor.getTriggerStmt(), /*IsBase=*/ false, - Pred, Dst, CallOpts); + VisitCXXDestructor(varType, Region, Dtor.getTriggerStmt(), + /*IsBase=*/false, Pred, Dst, CallOpts); } void ExprEngine::ProcessDeleteDtor(const CFGDeleteDtor Dtor, @@ -1036,8 +1036,9 @@ void ExprEngine::ProcessBaseDtor(const CFGBaseDtor D, SVal BaseVal = getStoreManager().evalDerivedToBase(ThisVal, BaseTy, Base->isVirtual()); - VisitCXXDestructor(BaseTy, BaseVal.castAs<loc::MemRegionVal>().getRegion(), - CurDtor->getBody(), /*IsBase=*/ true, Pred, Dst, {}); + EvalCallOptions CallOpts; + VisitCXXDestructor(BaseTy, BaseVal.getAsRegion(), CurDtor->getBody(), + /*IsBase=*/true, Pred, Dst, CallOpts); } void ExprEngine::ProcessMemberDtor(const CFGMemberDtor D, @@ -1048,10 +1049,10 @@ void ExprEngine::ProcessMemberDtor(const CFGMemberDtor D, const LocationContext *LCtx = Pred->getLocationContext(); const auto *CurDtor = cast<CXXDestructorDecl>(LCtx->getDecl()); - Loc ThisVal = getSValBuilder().getCXXThis(CurDtor, - LCtx->getStackFrame()); - SVal FieldVal = - State->getLValue(Member, State->getSVal(ThisVal).castAs<Loc>()); + Loc ThisStorageLoc = + getSValBuilder().getCXXThis(CurDtor, LCtx->getStackFrame()); + Loc ThisLoc = State->getSVal(ThisStorageLoc).castAs<Loc>(); + SVal FieldVal = State->getLValue(Member, ThisLoc); // 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 @@ -1060,8 +1061,8 @@ void ExprEngine::ProcessMemberDtor(const CFGMemberDtor D, FieldVal = makeZeroElementRegion(State, FieldVal, T, CallOpts.IsArrayCtorOrDtor); - VisitCXXDestructor(T, FieldVal.castAs<loc::MemRegionVal>().getRegion(), - CurDtor->getBody(), /*IsBase=*/false, Pred, Dst, CallOpts); + VisitCXXDestructor(T, FieldVal.getAsRegion(), CurDtor->getBody(), + /*IsBase=*/false, Pred, Dst, CallOpts); } void ExprEngine::ProcessTemporaryDtor(const CFGTemporaryDtor D, @@ -1109,8 +1110,6 @@ void ExprEngine::ProcessTemporaryDtor(const CFGTemporaryDtor D, 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. @@ -1266,6 +1265,9 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, case Stmt::OMPCancelDirectiveClass: case Stmt::OMPTaskLoopDirectiveClass: case Stmt::OMPTaskLoopSimdDirectiveClass: + case Stmt::OMPMasterTaskLoopDirectiveClass: + case Stmt::OMPMasterTaskLoopSimdDirectiveClass: + case Stmt::OMPParallelMasterTaskLoopDirectiveClass: case Stmt::OMPDistributeDirectiveClass: case Stmt::OMPDistributeParallelForDirectiveClass: case Stmt::OMPDistributeParallelForSimdDirectiveClass: @@ -1369,6 +1371,8 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, case Stmt::CUDAKernelCallExprClass: case Stmt::OpaqueValueExprClass: case Stmt::AsTypeExprClass: + case Stmt::ConceptSpecializationExprClass: + case Stmt::CXXRewrittenBinaryOperatorClass: // Fall through. // Cases we intentionally don't evaluate, since they don't need @@ -3005,9 +3009,13 @@ struct DOTGraphTraits<ExplodedGraph*> : public DefaultDOTGraphTraits { llvm::make_range(BR.EQClasses_begin(), BR.EQClasses_end()); for (const auto &EQ : EQClasses) { - for (const BugReport &Report : EQ) { - if (Report.getErrorNode()->getState() == N->getState() && - Report.getErrorNode()->getLocation() == N->getLocation()) + for (const auto &I : EQ.getReports()) { + const auto *PR = dyn_cast<PathSensitiveBugReport>(I.get()); + if (!PR) + continue; + const ExplodedNode *EN = PR->getErrorNode(); + if (EN->getState() == N->getState() && + EN->getLocation() == N->getLocation()) return true; } } @@ -3023,22 +3031,16 @@ struct DOTGraphTraits<ExplodedGraph*> : public DefaultDOTGraphTraits { llvm::function_ref<void(const ExplodedNode *)> PreCallback, llvm::function_ref<void(const ExplodedNode *)> PostCallback, llvm::function_ref<bool(const ExplodedNode *)> Stop) { - const ExplodedNode *FirstHiddenNode = N; - while (FirstHiddenNode->pred_size() == 1 && - isNodeHidden(*FirstHiddenNode->pred_begin())) { - FirstHiddenNode = *FirstHiddenNode->pred_begin(); - } - const ExplodedNode *OtherNode = FirstHiddenNode; while (true) { - PreCallback(OtherNode); - if (Stop(OtherNode)) + PreCallback(N); + if (Stop(N)) return true; - if (OtherNode == N) + if (N->succ_size() != 1 || !isNodeHidden(N->getFirstSucc())) break; - PostCallback(OtherNode); + PostCallback(N); - OtherNode = *OtherNode->succ_begin(); + N = N->getFirstSucc(); } return false; } @@ -3055,16 +3057,7 @@ struct DOTGraphTraits<ExplodedGraph*> : public DefaultDOTGraphTraits { const unsigned int Space = 1; ProgramStateRef State = N->getState(); - auto Noop = [](const ExplodedNode*){}; - bool HasReport = traverseHiddenNodes( - N, Noop, Noop, &nodeHasBugReport); - bool IsSink = traverseHiddenNodes( - N, Noop, Noop, [](const ExplodedNode *N) { return N->isSink(); }); - - Out << "{ \"node_id\": " << N->getID(G) << ", \"pointer\": \"" - << (const void *)N << "\", \"state_id\": " << State->getID() - << ", \"has_report\": " << (HasReport ? "true" : "false") - << ", \"is_sink\": " << (IsSink ? "true" : "false") + Out << "{ \"state_id\": " << State->getID() << ",\\l"; Indent(Out, Space, IsDot) << "\"program_points\": [\\l"; @@ -3077,9 +3070,12 @@ struct DOTGraphTraits<ExplodedGraph*> : public DefaultDOTGraphTraits { OtherNode->getLocation().printJson(Out, /*NL=*/"\\l"); Out << ", \"tag\": "; if (const ProgramPointTag *Tag = OtherNode->getLocation().getTag()) - Out << '\"' << Tag->getTagDescription() << "\" }"; + Out << '\"' << Tag->getTagDescription() << "\""; else - Out << "null }"; + Out << "null"; + Out << ", \"node_id\": " << OtherNode->getID() << + ", \"is_sink\": " << OtherNode->isSink() << + ", \"has_report\": " << nodeHasBugReport(OtherNode) << " }"; }, // Adds a comma and a new-line between each program point. [&](const ExplodedNode *) { Out << ",\\l"; }, @@ -3088,16 +3084,7 @@ struct DOTGraphTraits<ExplodedGraph*> : public DefaultDOTGraphTraits { Out << "\\l"; // Adds a new-line to the last program point. Indent(Out, Space, IsDot) << "],\\l"; - bool SameAsAllPredecessors = - std::all_of(N->pred_begin(), N->pred_end(), [&](const ExplodedNode *P) { - return P->getState() == State; - }); - - if (!SameAsAllPredecessors) { - State->printDOT(Out, N->getLocationContext(), Space); - } else { - Indent(Out, Space, IsDot) << "\"program_state\": null"; - } + State->printDOT(Out, N->getLocationContext(), Space); Out << "\\l}\\l"; return Out.str(); @@ -3132,8 +3119,12 @@ std::string ExprEngine::DumpGraph(bool trim, StringRef Filename) { // Iterate through the reports and get their nodes. for (BugReporter::EQClasses_iterator EI = BR.EQClasses_begin(), EE = BR.EQClasses_end(); EI != EE; ++EI) { - const auto *N = const_cast<ExplodedNode *>(EI->begin()->getErrorNode()); - if (N) Src.push_back(N); + const auto *R = + dyn_cast<PathSensitiveBugReport>(EI->getReports()[0].get()); + if (!R) + continue; + const auto *N = const_cast<ExplodedNode *>(R->getErrorNode()); + Src.push_back(N); } return DumpGraph(Src, Filename); } else { diff --git a/lib/StaticAnalyzer/Core/ExprEngineC.cpp b/lib/StaticAnalyzer/Core/ExprEngineC.cpp index f436650fbdd9b..02a398c77ac88 100644 --- a/lib/StaticAnalyzer/Core/ExprEngineC.cpp +++ b/lib/StaticAnalyzer/Core/ExprEngineC.cpp @@ -850,8 +850,7 @@ VisitOffsetOfExpr(const OffsetOfExpr *OOE, if (OOE->EvaluateAsInt(Result, getContext())) { APSInt IV = Result.Val.getInt(); assert(IV.getBitWidth() == getContext().getTypeSize(OOE->getType())); - assert(OOE->getType()->isBuiltinType()); - assert(OOE->getType()->getAs<BuiltinType>()->isInteger()); + assert(OOE->getType()->castAs<BuiltinType>()->isInteger()); assert(IV.isSigned() == OOE->getType()->isSignedIntegerType()); SVal X = svalBuilder.makeIntVal(IV); B.generateNode(OOE, Pred, diff --git a/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp b/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp index 1cbd09ea57932..058be985540d3 100644 --- a/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp +++ b/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp @@ -323,7 +323,8 @@ std::pair<ProgramStateRef, SVal> ExprEngine::prepareForObjectConstruction( CallEventManager &CEMgr = getStateManager().getCallEventManager(); SVal V = UnknownVal(); auto getArgLoc = [&](CallEventRef<> Caller) -> Optional<SVal> { - const LocationContext *FutureSFC = Caller->getCalleeStackFrame(); + const LocationContext *FutureSFC = + Caller->getCalleeStackFrame(currBldrCtx->blockCount()); // Return early if we are unable to reliably foresee // the future stack frame. if (!FutureSFC) @@ -342,7 +343,7 @@ std::pair<ProgramStateRef, SVal> ExprEngine::prepareForObjectConstruction( // because this-argument is implemented as a normal argument in // operator call expressions but not in operator declarations. const VarRegion *VR = Caller->getParameterLocation( - *Caller->getAdjustedParameterIndex(Idx)); + *Caller->getAdjustedParameterIndex(Idx), currBldrCtx->blockCount()); if (!VR) return None; @@ -603,7 +604,7 @@ void ExprEngine::VisitCXXDestructor(QualType ObjectType, bool IsBaseDtor, ExplodedNode *Pred, ExplodedNodeSet &Dst, - const EvalCallOptions &CallOpts) { + EvalCallOptions &CallOpts) { assert(S && "A destructor without a trigger!"); const LocationContext *LCtx = Pred->getLocationContext(); ProgramStateRef State = Pred->getState(); @@ -611,7 +612,6 @@ void ExprEngine::VisitCXXDestructor(QualType ObjectType, const CXXRecordDecl *RecordDecl = ObjectType->getAsCXXRecordDecl(); assert(RecordDecl && "Only CXXRecordDecls should have destructors"); const CXXDestructorDecl *DtorDecl = RecordDecl->getDestructor(); - // FIXME: There should always be a Decl, otherwise the destructor call // shouldn't have been added to the CFG in the first place. if (!DtorDecl) { @@ -625,9 +625,27 @@ void ExprEngine::VisitCXXDestructor(QualType ObjectType, return; } + if (!Dest) { + // We're trying to destroy something that is not a region. This may happen + // for a variety of reasons (unknown target region, concrete integer instead + // of target region, etc.). The current code makes an attempt to recover. + // FIXME: We probably don't really need to recover when we're dealing + // with concrete integers specifically. + CallOpts.IsCtorOrDtorWithImproperlyModeledTargetRegion = true; + if (const Expr *E = dyn_cast_or_null<Expr>(S)) { + Dest = MRMgr.getCXXTempObjectRegion(E, Pred->getLocationContext()); + } else { + static SimpleProgramPointTag T("ExprEngine", "SkipInvalidDestructor"); + NodeBuilder Bldr(Pred, Dst, *currBldrCtx); + Bldr.generateSink(Pred->getLocation().withTag(&T), + Pred->getState(), Pred); + return; + } + } + CallEventManager &CEMgr = getStateManager().getCallEventManager(); CallEventRef<CXXDestructorCall> Call = - CEMgr.getCXXDestructorCall(DtorDecl, S, Dest, IsBaseDtor, State, LCtx); + CEMgr.getCXXDestructorCall(DtorDecl, S, Dest, IsBaseDtor, State, LCtx); PrettyStackTraceLoc CrashInfo(getContext().getSourceManager(), Call->getSourceRange().getBegin(), @@ -757,7 +775,8 @@ void ExprEngine::VisitCXXNewExpr(const CXXNewExpr *CNE, ExplodedNode *Pred, if (!AMgr.getAnalyzerOptions().MayInlineCXXAllocator) { // Invalidate placement args. // FIXME: Once we figure out how we want allocators to work, - // we should be using the usual pre-/(default-)eval-/post-call checks here. + // we should be using the usual pre-/(default-)eval-/post-call checkers + // here. State = Call->invalidateRegions(blockCount); if (!State) return; @@ -785,9 +804,8 @@ void ExprEngine::VisitCXXNewExpr(const CXXNewExpr *CNE, ExplodedNode *Pred, if (CNE->isArray()) { // FIXME: allocating an array requires simulating the constructors. // For now, just return a symbolicated region. - if (const SubRegion *NewReg = - dyn_cast_or_null<SubRegion>(symVal.getAsRegion())) { - QualType ObjTy = CNE->getType()->getAs<PointerType>()->getPointeeType(); + if (const auto *NewReg = cast_or_null<SubRegion>(symVal.getAsRegion())) { + QualType ObjTy = CNE->getType()->getPointeeType(); const ElementRegion *EleReg = getStoreManager().GetElementZeroRegion(NewReg, ObjTy); Result = loc::MemRegionVal(EleReg); diff --git a/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp b/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp index b935e3afe34b2..345d4d817deae 100644 --- a/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp +++ b/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp @@ -451,9 +451,8 @@ bool ExprEngine::inlineCall(const CallEvent &Call, const Decl *D, // Construct a new stack frame for the callee. AnalysisDeclContext *CalleeADC = AMgr.getAnalysisDeclContext(D); const StackFrameContext *CalleeSFC = - CalleeADC->getStackFrame(ParentOfCallee, CallE, - currBldrCtx->getBlock(), - currStmtIdx); + CalleeADC->getStackFrame(ParentOfCallee, CallE, currBldrCtx->getBlock(), + currBldrCtx->blockCount(), currStmtIdx); CallEnter Loc(CallE, CalleeSFC, CurLC); diff --git a/lib/StaticAnalyzer/Core/HTMLDiagnostics.cpp b/lib/StaticAnalyzer/Core/HTMLDiagnostics.cpp index 64c42699fcf3c..a4918d7179ff5 100644 --- a/lib/StaticAnalyzer/Core/HTMLDiagnostics.cpp +++ b/lib/StaticAnalyzer/Core/HTMLDiagnostics.cpp @@ -10,6 +10,7 @@ // //===----------------------------------------------------------------------===// +#include "clang/Analysis/PathDiagnostic.h" #include "clang/AST/Decl.h" #include "clang/AST/DeclBase.h" #include "clang/AST/Stmt.h" @@ -23,7 +24,6 @@ #include "clang/Rewrite/Core/HTMLRewrite.h" #include "clang/Rewrite/Core/Rewriter.h" #include "clang/StaticAnalyzer/Core/AnalyzerOptions.h" -#include "clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h" #include "clang/StaticAnalyzer/Core/IssueHash.h" #include "clang/StaticAnalyzer/Core/PathDiagnosticConsumers.h" #include "llvm/ADT/ArrayRef.h" @@ -134,17 +134,17 @@ private: } // namespace -void ento::createHTMLDiagnosticConsumer(AnalyzerOptions &AnalyzerOpts, - PathDiagnosticConsumers &C, - const std::string& prefix, - const Preprocessor &PP) { +void ento::createHTMLDiagnosticConsumer( + AnalyzerOptions &AnalyzerOpts, PathDiagnosticConsumers &C, + const std::string &prefix, const Preprocessor &PP, + const cross_tu::CrossTranslationUnitContext &) { C.push_back(new HTMLDiagnostics(AnalyzerOpts, prefix, PP, true)); } -void ento::createHTMLSingleFileDiagnosticConsumer(AnalyzerOptions &AnalyzerOpts, - PathDiagnosticConsumers &C, - const std::string& prefix, - const Preprocessor &PP) { +void ento::createHTMLSingleFileDiagnosticConsumer( + AnalyzerOptions &AnalyzerOpts, PathDiagnosticConsumers &C, + const std::string &prefix, const Preprocessor &PP, + const cross_tu::CrossTranslationUnitContext &) { C.push_back(new HTMLDiagnostics(AnalyzerOpts, prefix, PP, false)); } @@ -555,8 +555,9 @@ void HTMLDiagnostics::FinalizeHTML(const PathDiagnostic& D, Rewriter &R, os << "\n<!-- FUNCTIONNAME " << declName << " -->\n"; os << "\n<!-- ISSUEHASHCONTENTOFLINEINCONTEXT " - << GetIssueHash(SMgr, L, D.getCheckName(), D.getBugType(), DeclWithIssue, - PP.getLangOpts()) << " -->\n"; + << GetIssueHash(SMgr, L, D.getCheckerName(), D.getBugType(), + DeclWithIssue, PP.getLangOpts()) + << " -->\n"; os << "\n<!-- BUGLINE " << LineNumber @@ -612,7 +613,7 @@ HandlePopUpPieceStartTag(Rewriter &R, for (const auto &Range : PopUpRanges) { html::HighlightRange(R, Range.getBegin(), Range.getEnd(), "", "<table class='variable_popup'><tbody>", - /*IsTokenRange=*/false); + /*IsTokenRange=*/true); } } @@ -644,12 +645,11 @@ static void HandlePopUpPieceEndTag(Rewriter &R, Out << "</tbody></table></span>"; html::HighlightRange(R, Range.getBegin(), Range.getEnd(), "<span class='variable'>", Buf.c_str(), - /*IsTokenRange=*/false); - - // Otherwise inject just the new row at the end of the range. + /*IsTokenRange=*/true); } else { + // Otherwise inject just the new row at the end of the range. html::HighlightRange(R, Range.getBegin(), Range.getEnd(), "", Buf.c_str(), - /*IsTokenRange=*/false); + /*IsTokenRange=*/true); } } @@ -658,16 +658,14 @@ void HTMLDiagnostics::RewriteFile(Rewriter &R, // Process the path. // Maintain the counts of extra note pieces separately. unsigned TotalPieces = path.size(); - unsigned TotalNotePieces = - std::count_if(path.begin(), path.end(), - [](const std::shared_ptr<PathDiagnosticPiece> &p) { - return isa<PathDiagnosticNotePiece>(*p); - }); - unsigned PopUpPieceCount = - std::count_if(path.begin(), path.end(), - [](const std::shared_ptr<PathDiagnosticPiece> &p) { - return isa<PathDiagnosticPopUpPiece>(*p); - }); + unsigned TotalNotePieces = std::count_if( + path.begin(), path.end(), [](const PathDiagnosticPieceRef &p) { + return isa<PathDiagnosticNotePiece>(*p); + }); + unsigned PopUpPieceCount = std::count_if( + path.begin(), path.end(), [](const PathDiagnosticPieceRef &p) { + return isa<PathDiagnosticPopUpPiece>(*p); + }); unsigned TotalRegularPieces = TotalPieces - TotalNotePieces - PopUpPieceCount; unsigned NumRegularPieces = TotalRegularPieces; diff --git a/lib/StaticAnalyzer/Core/LoopUnrolling.cpp b/lib/StaticAnalyzer/Core/LoopUnrolling.cpp index 9838249ae82ca..1a09a521f1167 100644 --- a/lib/StaticAnalyzer/Core/LoopUnrolling.cpp +++ b/lib/StaticAnalyzer/Core/LoopUnrolling.cpp @@ -165,7 +165,9 @@ static bool isPossiblyEscaped(const VarDecl *VD, ExplodedNode *N) { return true; while (!N->pred_empty()) { - const Stmt *S = PathDiagnosticLocation::getStmt(N); + // FIXME: getStmtForDiagnostics() does nasty things in order to provide + // a valid statement for body farms, do we need this behavior here? + const Stmt *S = N->getStmtForDiagnostics(); if (!S) { N = N->getFirstPred(); continue; diff --git a/lib/StaticAnalyzer/Core/MemRegion.cpp b/lib/StaticAnalyzer/Core/MemRegion.cpp index f763701af7fb1..a10d7e69ad7e7 100644 --- a/lib/StaticAnalyzer/Core/MemRegion.cpp +++ b/lib/StaticAnalyzer/Core/MemRegion.cpp @@ -506,7 +506,7 @@ void ElementRegion::dumpToStream(raw_ostream &os) const { } void FieldRegion::dumpToStream(raw_ostream &os) const { - os << superRegion << "->" << *getDecl(); + os << superRegion << "." << *getDecl(); } void ObjCIvarRegion::dumpToStream(raw_ostream &os) const { @@ -1075,7 +1075,7 @@ MemRegionManager::getCXXBaseObjectRegion(const CXXRecordDecl *RD, const SubRegion *Super, bool IsVirtual) { if (isa<TypedValueRegion>(Super)) { - assert(isValidBaseClass(RD, dyn_cast<TypedValueRegion>(Super), IsVirtual)); + assert(isValidBaseClass(RD, cast<TypedValueRegion>(Super), IsVirtual)); (void)&isValidBaseClass; if (IsVirtual) { @@ -1426,6 +1426,7 @@ static RegionOffset calculateOffset(const MemRegion *R) { case MemRegion::FieldRegionKind: { const auto *FR = cast<FieldRegion>(R); R = FR->getSuperRegion(); + assert(R); const RecordDecl *RD = FR->getDecl()->getParent(); if (RD->isUnion() || !RD->isCompleteDefinition()) { diff --git a/lib/StaticAnalyzer/Core/PathDiagnostic.cpp b/lib/StaticAnalyzer/Core/PathDiagnostic.cpp deleted file mode 100644 index 54fbd6a5bc496..0000000000000 --- a/lib/StaticAnalyzer/Core/PathDiagnostic.cpp +++ /dev/null @@ -1,1466 +0,0 @@ -//===- PathDiagnostic.cpp - Path-Specific Diagnostic Handling -------------===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -// -// This file defines the PathDiagnostic-related interfaces. -// -//===----------------------------------------------------------------------===// - -#include "clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h" -#include "clang/AST/Decl.h" -#include "clang/AST/DeclBase.h" -#include "clang/AST/DeclCXX.h" -#include "clang/AST/DeclObjC.h" -#include "clang/AST/DeclTemplate.h" -#include "clang/AST/Expr.h" -#include "clang/AST/ExprCXX.h" -#include "clang/AST/OperationKinds.h" -#include "clang/AST/ParentMap.h" -#include "clang/AST/Stmt.h" -#include "clang/AST/Type.h" -#include "clang/Analysis/AnalysisDeclContext.h" -#include "clang/Analysis/CFG.h" -#include "clang/Analysis/ProgramPoint.h" -#include "clang/Basic/FileManager.h" -#include "clang/Basic/LLVM.h" -#include "clang/Basic/SourceLocation.h" -#include "clang/Basic/SourceManager.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h" -#include "llvm/ADT/ArrayRef.h" -#include "llvm/ADT/FoldingSet.h" -#include "llvm/ADT/None.h" -#include "llvm/ADT/Optional.h" -#include "llvm/ADT/STLExtras.h" -#include "llvm/ADT/SmallString.h" -#include "llvm/ADT/SmallVector.h" -#include "llvm/ADT/StringExtras.h" -#include "llvm/ADT/StringRef.h" -#include "llvm/Support/Casting.h" -#include "llvm/Support/ErrorHandling.h" -#include "llvm/Support/raw_ostream.h" -#include <cassert> -#include <cstring> -#include <memory> -#include <utility> -#include <vector> - -using namespace clang; -using namespace ento; - -bool PathDiagnosticMacroPiece::containsEvent() const { - for (const auto &P : subPieces) { - if (isa<PathDiagnosticEventPiece>(*P)) - return true; - if (const auto *MP = dyn_cast<PathDiagnosticMacroPiece>(P.get())) - if (MP->containsEvent()) - return true; - } - return false; -} - -static StringRef StripTrailingDots(StringRef s) { - for (StringRef::size_type i = s.size(); i != 0; --i) - if (s[i - 1] != '.') - return s.substr(0, i); - return {}; -} - -PathDiagnosticPiece::PathDiagnosticPiece(StringRef s, - Kind k, DisplayHint hint) - : str(StripTrailingDots(s)), kind(k), Hint(hint) {} - -PathDiagnosticPiece::PathDiagnosticPiece(Kind k, DisplayHint hint) - : kind(k), Hint(hint) {} - -PathDiagnosticPiece::~PathDiagnosticPiece() = default; - -PathDiagnosticEventPiece::~PathDiagnosticEventPiece() = default; - -PathDiagnosticCallPiece::~PathDiagnosticCallPiece() = default; - -PathDiagnosticControlFlowPiece::~PathDiagnosticControlFlowPiece() = default; - -PathDiagnosticMacroPiece::~PathDiagnosticMacroPiece() = default; - -PathDiagnosticNotePiece::~PathDiagnosticNotePiece() = default; - -PathDiagnosticPopUpPiece::~PathDiagnosticPopUpPiece() = default; - -void PathPieces::flattenTo(PathPieces &Primary, PathPieces &Current, - bool ShouldFlattenMacros) const { - for (auto &Piece : *this) { - switch (Piece->getKind()) { - case PathDiagnosticPiece::Call: { - auto &Call = cast<PathDiagnosticCallPiece>(*Piece); - if (auto CallEnter = Call.getCallEnterEvent()) - Current.push_back(std::move(CallEnter)); - Call.path.flattenTo(Primary, Primary, ShouldFlattenMacros); - if (auto callExit = Call.getCallExitEvent()) - Current.push_back(std::move(callExit)); - break; - } - case PathDiagnosticPiece::Macro: { - auto &Macro = cast<PathDiagnosticMacroPiece>(*Piece); - if (ShouldFlattenMacros) { - Macro.subPieces.flattenTo(Primary, Primary, ShouldFlattenMacros); - } else { - Current.push_back(Piece); - PathPieces NewPath; - Macro.subPieces.flattenTo(Primary, NewPath, ShouldFlattenMacros); - // FIXME: This probably shouldn't mutate the original path piece. - Macro.subPieces = NewPath; - } - break; - } - case PathDiagnosticPiece::Event: - case PathDiagnosticPiece::ControlFlow: - case PathDiagnosticPiece::Note: - case PathDiagnosticPiece::PopUp: - Current.push_back(Piece); - break; - } - } -} - -PathDiagnostic::~PathDiagnostic() = default; - -PathDiagnostic::PathDiagnostic( - StringRef CheckName, const Decl *declWithIssue, StringRef bugtype, - StringRef verboseDesc, StringRef shortDesc, StringRef category, - PathDiagnosticLocation LocationToUnique, const Decl *DeclToUnique, - std::unique_ptr<FilesToLineNumsMap> ExecutedLines) - : CheckName(CheckName), DeclWithIssue(declWithIssue), - BugType(StripTrailingDots(bugtype)), - VerboseDesc(StripTrailingDots(verboseDesc)), - ShortDesc(StripTrailingDots(shortDesc)), - Category(StripTrailingDots(category)), UniqueingLoc(LocationToUnique), - UniqueingDecl(DeclToUnique), ExecutedLines(std::move(ExecutedLines)), - path(pathImpl) {} - -static PathDiagnosticCallPiece * -getFirstStackedCallToHeaderFile(PathDiagnosticCallPiece *CP, - const SourceManager &SMgr) { - SourceLocation CallLoc = CP->callEnter.asLocation(); - - // If the call is within a macro, don't do anything (for now). - if (CallLoc.isMacroID()) - return nullptr; - - assert(AnalysisManager::isInCodeFile(CallLoc, SMgr) && - "The call piece should not be in a header file."); - - // Check if CP represents a path through a function outside of the main file. - if (!AnalysisManager::isInCodeFile(CP->callEnterWithin.asLocation(), SMgr)) - return CP; - - const PathPieces &Path = CP->path; - if (Path.empty()) - return nullptr; - - // Check if the last piece in the callee path is a call to a function outside - // of the main file. - if (auto *CPInner = dyn_cast<PathDiagnosticCallPiece>(Path.back().get())) - return getFirstStackedCallToHeaderFile(CPInner, SMgr); - - // Otherwise, the last piece is in the main file. - return nullptr; -} - -void PathDiagnostic::resetDiagnosticLocationToMainFile() { - if (path.empty()) - return; - - PathDiagnosticPiece *LastP = path.back().get(); - assert(LastP); - const SourceManager &SMgr = LastP->getLocation().getManager(); - - // We only need to check if the report ends inside headers, if the last piece - // is a call piece. - if (auto *CP = dyn_cast<PathDiagnosticCallPiece>(LastP)) { - CP = getFirstStackedCallToHeaderFile(CP, SMgr); - if (CP) { - // Mark the piece. - CP->setAsLastInMainSourceFile(); - - // Update the path diagnostic message. - const auto *ND = dyn_cast<NamedDecl>(CP->getCallee()); - if (ND) { - SmallString<200> buf; - llvm::raw_svector_ostream os(buf); - os << " (within a call to '" << ND->getDeclName() << "')"; - appendToDesc(os.str()); - } - - // Reset the report containing declaration and location. - DeclWithIssue = CP->getCaller(); - Loc = CP->getLocation(); - - return; - } - } -} - -void PathDiagnosticConsumer::anchor() {} - -PathDiagnosticConsumer::~PathDiagnosticConsumer() { - // Delete the contents of the FoldingSet if it isn't empty already. - for (auto &Diag : Diags) - delete &Diag; -} - -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. - D->flattenLocations(); - - // If the PathDiagnosticConsumer does not support diagnostics that - // cross file boundaries, prune out such diagnostics now. - if (!supportsCrossFileDiagnostics()) { - // Verify that the entire path is from the same FileID. - FileID FID; - const SourceManager &SMgr = D->path.front()->getLocation().getManager(); - SmallVector<const PathPieces *, 5> WorkList; - WorkList.push_back(&D->path); - SmallString<128> buf; - llvm::raw_svector_ostream warning(buf); - warning << "warning: Path diagnostic report is not generated. Current " - << "output format does not support diagnostics that cross file " - << "boundaries. Refer to --analyzer-output for valid output " - << "formats\n"; - - while (!WorkList.empty()) { - const PathPieces &path = *WorkList.pop_back_val(); - - for (const auto &I : path) { - const PathDiagnosticPiece *piece = I.get(); - FullSourceLoc L = piece->getLocation().asLocation().getExpansionLoc(); - - if (FID.isInvalid()) { - FID = SMgr.getFileID(L); - } else if (SMgr.getFileID(L) != FID) { - llvm::errs() << warning.str(); - return; - } - - // Check the source ranges. - ArrayRef<SourceRange> Ranges = piece->getRanges(); - for (const auto &I : Ranges) { - SourceLocation L = SMgr.getExpansionLoc(I.getBegin()); - if (!L.isFileID() || SMgr.getFileID(L) != FID) { - llvm::errs() << warning.str(); - return; - } - L = SMgr.getExpansionLoc(I.getEnd()); - if (!L.isFileID() || SMgr.getFileID(L) != FID) { - llvm::errs() << warning.str(); - return; - } - } - - if (const auto *call = dyn_cast<PathDiagnosticCallPiece>(piece)) - WorkList.push_back(&call->path); - else if (const auto *macro = dyn_cast<PathDiagnosticMacroPiece>(piece)) - WorkList.push_back(¯o->subPieces); - } - } - - if (FID.isInvalid()) - return; // FIXME: Emit a warning? - } - - // Profile the node to see if we already have something matching it - llvm::FoldingSetNodeID profile; - D->Profile(profile); - void *InsertPos = nullptr; - - if (PathDiagnostic *orig = Diags.FindNodeOrInsertPos(profile, InsertPos)) { - // Keep the PathDiagnostic with the shorter path. - // Note, the enclosing routine is called in deterministic order, so the - // results will be consistent between runs (no reason to break ties if the - // size is the same). - const unsigned orig_size = orig->full_size(); - const unsigned new_size = D->full_size(); - if (orig_size <= new_size) - return; - - assert(orig != D.get()); - Diags.RemoveNode(orig); - delete orig; - } - - Diags.InsertNode(D.release()); -} - -static Optional<bool> comparePath(const PathPieces &X, const PathPieces &Y); - -static Optional<bool> -compareControlFlow(const PathDiagnosticControlFlowPiece &X, - const PathDiagnosticControlFlowPiece &Y) { - FullSourceLoc XSL = X.getStartLocation().asLocation(); - FullSourceLoc YSL = Y.getStartLocation().asLocation(); - if (XSL != YSL) - return XSL.isBeforeInTranslationUnitThan(YSL); - FullSourceLoc XEL = X.getEndLocation().asLocation(); - FullSourceLoc YEL = Y.getEndLocation().asLocation(); - if (XEL != YEL) - return XEL.isBeforeInTranslationUnitThan(YEL); - return None; -} - -static Optional<bool> compareMacro(const PathDiagnosticMacroPiece &X, - const PathDiagnosticMacroPiece &Y) { - return comparePath(X.subPieces, Y.subPieces); -} - -static Optional<bool> compareCall(const PathDiagnosticCallPiece &X, - const PathDiagnosticCallPiece &Y) { - FullSourceLoc X_CEL = X.callEnter.asLocation(); - FullSourceLoc Y_CEL = Y.callEnter.asLocation(); - if (X_CEL != Y_CEL) - return X_CEL.isBeforeInTranslationUnitThan(Y_CEL); - FullSourceLoc X_CEWL = X.callEnterWithin.asLocation(); - FullSourceLoc Y_CEWL = Y.callEnterWithin.asLocation(); - if (X_CEWL != Y_CEWL) - return X_CEWL.isBeforeInTranslationUnitThan(Y_CEWL); - FullSourceLoc X_CRL = X.callReturn.asLocation(); - FullSourceLoc Y_CRL = Y.callReturn.asLocation(); - if (X_CRL != Y_CRL) - return X_CRL.isBeforeInTranslationUnitThan(Y_CRL); - return comparePath(X.path, Y.path); -} - -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) - return XL.isBeforeInTranslationUnitThan(YL); - - if (X.getString() != Y.getString()) - return X.getString() < Y.getString(); - - if (X.getRanges().size() != Y.getRanges().size()) - 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]; - if (XR != YR) { - if (XR.getBegin() != YR.getBegin()) - return SM.isBeforeInTranslationUnit(XR.getBegin(), YR.getBegin()); - return SM.isBeforeInTranslationUnit(XR.getEnd(), YR.getEnd()); - } - } - - switch (X.getKind()) { - case PathDiagnosticPiece::ControlFlow: - return compareControlFlow(cast<PathDiagnosticControlFlowPiece>(X), - cast<PathDiagnosticControlFlowPiece>(Y)); - case PathDiagnosticPiece::Macro: - return compareMacro(cast<PathDiagnosticMacroPiece>(X), - cast<PathDiagnosticMacroPiece>(Y)); - case PathDiagnosticPiece::Call: - return compareCall(cast<PathDiagnosticCallPiece>(X), - cast<PathDiagnosticCallPiece>(Y)); - case PathDiagnosticPiece::Event: - case PathDiagnosticPiece::Note: - case PathDiagnosticPiece::PopUp: - return None; - } - llvm_unreachable("all cases handled"); -} - -static Optional<bool> comparePath(const PathPieces &X, const PathPieces &Y) { - if (X.size() != Y.size()) - return X.size() < Y.size(); - - PathPieces::const_iterator X_I = X.begin(), X_end = X.end(); - PathPieces::const_iterator Y_I = Y.begin(), Y_end = Y.end(); - - for ( ; X_I != X_end && Y_I != Y_end; ++X_I, ++Y_I) { - Optional<bool> b = comparePiece(**X_I, **Y_I); - if (b.hasValue()) - return b.getValue(); - } - - return None; -} - -static bool compareCrossTUSourceLocs(FullSourceLoc XL, FullSourceLoc YL) { - std::pair<FileID, unsigned> XOffs = XL.getDecomposedLoc(); - std::pair<FileID, unsigned> YOffs = YL.getDecomposedLoc(); - const SourceManager &SM = XL.getManager(); - std::pair<bool, bool> InSameTU = SM.isInTheSameTranslationUnit(XOffs, YOffs); - if (InSameTU.first) - return XL.isBeforeInTranslationUnitThan(YL); - const FileEntry *XFE = SM.getFileEntryForID(XL.getSpellingLoc().getFileID()); - const FileEntry *YFE = SM.getFileEntryForID(YL.getSpellingLoc().getFileID()); - if (!XFE || !YFE) - return XFE && !YFE; - int NameCmp = XFE->getName().compare(YFE->getName()); - if (NameCmp != 0) - return NameCmp == -1; - // Last resort: Compare raw file IDs that are possibly expansions. - return XL.getFileID() < YL.getFileID(); -} - -static bool compare(const PathDiagnostic &X, const PathDiagnostic &Y) { - FullSourceLoc XL = X.getLocation().asLocation(); - FullSourceLoc YL = Y.getLocation().asLocation(); - if (XL != YL) - return compareCrossTUSourceLocs(XL, YL); - if (X.getBugType() != Y.getBugType()) - return X.getBugType() < Y.getBugType(); - if (X.getCategory() != Y.getCategory()) - return X.getCategory() < Y.getCategory(); - if (X.getVerboseDescription() != Y.getVerboseDescription()) - return X.getVerboseDescription() < Y.getVerboseDescription(); - if (X.getShortDescription() != Y.getShortDescription()) - return X.getShortDescription() < Y.getShortDescription(); - if (X.getDeclWithIssue() != Y.getDeclWithIssue()) { - const Decl *XD = X.getDeclWithIssue(); - if (!XD) - return true; - const Decl *YD = Y.getDeclWithIssue(); - if (!YD) - return false; - SourceLocation XDL = XD->getLocation(); - SourceLocation YDL = YD->getLocation(); - if (XDL != YDL) { - const SourceManager &SM = XL.getManager(); - return compareCrossTUSourceLocs(FullSourceLoc(XDL, SM), - FullSourceLoc(YDL, SM)); - } - } - PathDiagnostic::meta_iterator XI = X.meta_begin(), XE = X.meta_end(); - PathDiagnostic::meta_iterator YI = Y.meta_begin(), YE = Y.meta_end(); - if (XE - XI != YE - YI) - return (XE - XI) < (YE - YI); - for ( ; XI != XE ; ++XI, ++YI) { - if (*XI != *YI) - return (*XI) < (*YI); - } - Optional<bool> b = comparePath(X.path, Y.path); - assert(b.hasValue()); - return b.getValue(); -} - -void PathDiagnosticConsumer::FlushDiagnostics( - PathDiagnosticConsumer::FilesMade *Files) { - if (flushed) - return; - - flushed = true; - - std::vector<const PathDiagnostic *> BatchDiags; - for (const auto &D : Diags) - BatchDiags.push_back(&D); - - // Sort the diagnostics so that they are always emitted in a deterministic - // order. - int (*Comp)(const PathDiagnostic *const *, const PathDiagnostic *const *) = - [](const PathDiagnostic *const *X, const PathDiagnostic *const *Y) { - assert(*X != *Y && "PathDiagnostics not uniqued!"); - if (compare(**X, **Y)) - return -1; - assert(compare(**Y, **X) && "Not a total order!"); - return 1; - }; - array_pod_sort(BatchDiags.begin(), BatchDiags.end(), Comp); - - FlushDiagnosticsImpl(BatchDiags, Files); - - // Delete the flushed diagnostics. - for (const auto D : BatchDiags) - delete D; - - // Clear out the FoldingSet. - Diags.clear(); -} - -PathDiagnosticConsumer::FilesMade::~FilesMade() { - for (PDFileEntry &Entry : Set) - Entry.~PDFileEntry(); -} - -void PathDiagnosticConsumer::FilesMade::addDiagnostic(const PathDiagnostic &PD, - StringRef ConsumerName, - StringRef FileName) { - llvm::FoldingSetNodeID NodeID; - NodeID.Add(PD); - void *InsertPos; - PDFileEntry *Entry = Set.FindNodeOrInsertPos(NodeID, InsertPos); - if (!Entry) { - Entry = Alloc.Allocate<PDFileEntry>(); - 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()); - - Entry->files.push_back(std::make_pair(ConsumerName, - StringRef(FileName_cstr, - FileName.size()))); -} - -PathDiagnosticConsumer::PDFileEntry::ConsumerFiles * -PathDiagnosticConsumer::FilesMade::getFiles(const PathDiagnostic &PD) { - llvm::FoldingSetNodeID NodeID; - NodeID.Add(PD); - void *InsertPos; - PDFileEntry *Entry = Set.FindNodeOrInsertPos(NodeID, InsertPos); - if (!Entry) - return nullptr; - return &Entry->files; -} - -//===----------------------------------------------------------------------===// -// PathDiagnosticLocation methods. -//===----------------------------------------------------------------------===// - -static SourceLocation getValidSourceLocation(const Stmt* S, - LocationOrAnalysisDeclContext LAC, - bool UseEnd = false) { - SourceLocation L = UseEnd ? S->getEndLoc() : S->getBeginLoc(); - assert(!LAC.isNull() && "A valid LocationContext or AnalysisDeclContext should " - "be passed to PathDiagnosticLocation upon creation."); - - // S might be a temporary statement that does not have a location in the - // source code, so find an enclosing statement and use its location. - if (!L.isValid()) { - AnalysisDeclContext *ADC; - if (LAC.is<const LocationContext*>()) - ADC = LAC.get<const LocationContext*>()->getAnalysisDeclContext(); - else - ADC = LAC.get<AnalysisDeclContext*>(); - - ParentMap &PM = ADC->getParentMap(); - - const Stmt *Parent = S; - do { - Parent = PM.getParent(Parent); - - // In rare cases, we have implicit top-level expressions, - // such as arguments for implicit member initializers. - // In this case, fall back to the start of the body (even if we were - // asked for the statement end location). - if (!Parent) { - const Stmt *Body = ADC->getBody(); - if (Body) - L = Body->getBeginLoc(); - else - L = ADC->getDecl()->getEndLoc(); - break; - } - - L = UseEnd ? Parent->getEndLoc() : Parent->getBeginLoc(); - } while (!L.isValid()); - } - - // FIXME: Ironically, this assert actually fails in some cases. - //assert(L.isValid()); - return L; -} - -static PathDiagnosticLocation -getLocationForCaller(const StackFrameContext *SFC, - const LocationContext *CallerCtx, - const SourceManager &SM) { - const CFGBlock &Block = *SFC->getCallSiteBlock(); - CFGElement Source = Block[SFC->getIndex()]; - - switch (Source.getKind()) { - case CFGElement::Statement: - case CFGElement::Constructor: - case CFGElement::CXXRecordTypedCall: - return PathDiagnosticLocation(Source.castAs<CFGStmt>().getStmt(), - SM, CallerCtx); - case CFGElement::Initializer: { - const CFGInitializer &Init = Source.castAs<CFGInitializer>(); - return PathDiagnosticLocation(Init.getInitializer()->getInit(), - SM, CallerCtx); - } - case CFGElement::AutomaticObjectDtor: { - const CFGAutomaticObjDtor &Dtor = Source.castAs<CFGAutomaticObjDtor>(); - return PathDiagnosticLocation::createEnd(Dtor.getTriggerStmt(), - SM, CallerCtx); - } - case CFGElement::DeleteDtor: { - const CFGDeleteDtor &Dtor = Source.castAs<CFGDeleteDtor>(); - return PathDiagnosticLocation(Dtor.getDeleteExpr(), SM, CallerCtx); - } - case CFGElement::BaseDtor: - case CFGElement::MemberDtor: { - const AnalysisDeclContext *CallerInfo = CallerCtx->getAnalysisDeclContext(); - if (const Stmt *CallerBody = CallerInfo->getBody()) - return PathDiagnosticLocation::createEnd(CallerBody, SM, CallerCtx); - return PathDiagnosticLocation::create(CallerInfo->getDecl(), SM); - } - case CFGElement::NewAllocator: { - const CFGNewAllocator &Alloc = Source.castAs<CFGNewAllocator>(); - return PathDiagnosticLocation(Alloc.getAllocatorExpr(), SM, CallerCtx); - } - case CFGElement::TemporaryDtor: { - // Temporary destructors are for temporaries. They die immediately at around - // the location of CXXBindTemporaryExpr. If they are lifetime-extended, - // they'd be dealt with via an AutomaticObjectDtor instead. - const auto &Dtor = Source.castAs<CFGTemporaryDtor>(); - return PathDiagnosticLocation::createEnd(Dtor.getBindTemporaryExpr(), SM, - CallerCtx); - } - case CFGElement::ScopeBegin: - case CFGElement::ScopeEnd: - llvm_unreachable("not yet implemented!"); - case CFGElement::LifetimeEnds: - case CFGElement::LoopExit: - llvm_unreachable("CFGElement kind should not be on callsite!"); - } - - llvm_unreachable("Unknown CFGElement kind"); -} - -PathDiagnosticLocation -PathDiagnosticLocation::createBegin(const Decl *D, - const SourceManager &SM) { - return PathDiagnosticLocation(D->getBeginLoc(), SM, SingleLocK); -} - -PathDiagnosticLocation -PathDiagnosticLocation::createBegin(const Stmt *S, - const SourceManager &SM, - LocationOrAnalysisDeclContext LAC) { - return PathDiagnosticLocation(getValidSourceLocation(S, LAC), - SM, SingleLocK); -} - -PathDiagnosticLocation -PathDiagnosticLocation::createEnd(const Stmt *S, - const SourceManager &SM, - LocationOrAnalysisDeclContext LAC) { - if (const auto *CS = dyn_cast<CompoundStmt>(S)) - return createEndBrace(CS, SM); - return PathDiagnosticLocation(getValidSourceLocation(S, LAC, /*End=*/true), - SM, SingleLocK); -} - -PathDiagnosticLocation -PathDiagnosticLocation::createOperatorLoc(const BinaryOperator *BO, - const SourceManager &SM) { - return PathDiagnosticLocation(BO->getOperatorLoc(), SM, SingleLocK); -} - -PathDiagnosticLocation -PathDiagnosticLocation::createConditionalColonLoc( - const ConditionalOperator *CO, - const SourceManager &SM) { - return PathDiagnosticLocation(CO->getColonLoc(), SM, SingleLocK); -} - -PathDiagnosticLocation -PathDiagnosticLocation::createMemberLoc(const MemberExpr *ME, - const SourceManager &SM) { - - assert(ME->getMemberLoc().isValid() || ME->getBeginLoc().isValid()); - - // In some cases, getMemberLoc isn't valid -- in this case we'll return with - // some other related valid SourceLocation. - if (ME->getMemberLoc().isValid()) - return PathDiagnosticLocation(ME->getMemberLoc(), SM, SingleLocK); - - return PathDiagnosticLocation(ME->getBeginLoc(), SM, SingleLocK); -} - -PathDiagnosticLocation -PathDiagnosticLocation::createBeginBrace(const CompoundStmt *CS, - const SourceManager &SM) { - SourceLocation L = CS->getLBracLoc(); - return PathDiagnosticLocation(L, SM, SingleLocK); -} - -PathDiagnosticLocation -PathDiagnosticLocation::createEndBrace(const CompoundStmt *CS, - const SourceManager &SM) { - SourceLocation L = CS->getRBracLoc(); - return PathDiagnosticLocation(L, SM, SingleLocK); -} - -PathDiagnosticLocation -PathDiagnosticLocation::createDeclBegin(const LocationContext *LC, - const SourceManager &SM) { - // FIXME: Should handle CXXTryStmt if analyser starts supporting C++. - if (const auto *CS = dyn_cast_or_null<CompoundStmt>(LC->getDecl()->getBody())) - if (!CS->body_empty()) { - SourceLocation Loc = (*CS->body_begin())->getBeginLoc(); - return PathDiagnosticLocation(Loc, SM, SingleLocK); - } - - return PathDiagnosticLocation(); -} - -PathDiagnosticLocation -PathDiagnosticLocation::createDeclEnd(const LocationContext *LC, - const SourceManager &SM) { - SourceLocation L = LC->getDecl()->getBodyRBrace(); - return PathDiagnosticLocation(L, SM, SingleLocK); -} - -PathDiagnosticLocation -PathDiagnosticLocation::create(const ProgramPoint& P, - const SourceManager &SMng) { - const Stmt* S = nullptr; - if (Optional<BlockEdge> BE = P.getAs<BlockEdge>()) { - const CFGBlock *BSrc = BE->getSrc(); - if (BSrc->getTerminator().isVirtualBaseBranch()) { - // TODO: VirtualBaseBranches should also appear for destructors. - // In this case we should put the diagnostic at the end of decl. - return PathDiagnosticLocation::createBegin( - P.getLocationContext()->getDecl(), SMng); - - } else { - S = BSrc->getTerminatorCondition(); - if (!S) { - // If the BlockEdge has no terminator condition statement but its - // source is the entry of the CFG (e.g. a checker crated the branch at - // the beginning of a function), use the function's declaration instead. - assert(BSrc == &BSrc->getParent()->getEntry() && "CFGBlock has no " - "TerminatorCondition and is not the enrty block of the CFG"); - return PathDiagnosticLocation::createBegin( - P.getLocationContext()->getDecl(), SMng); - } - } - } else if (Optional<StmtPoint> SP = P.getAs<StmtPoint>()) { - S = SP->getStmt(); - if (P.getAs<PostStmtPurgeDeadSymbols>()) - return PathDiagnosticLocation::createEnd(S, SMng, P.getLocationContext()); - } else if (Optional<PostInitializer> PIP = P.getAs<PostInitializer>()) { - return PathDiagnosticLocation(PIP->getInitializer()->getSourceLocation(), - SMng); - } else if (Optional<PreImplicitCall> PIC = P.getAs<PreImplicitCall>()) { - return PathDiagnosticLocation(PIC->getLocation(), SMng); - } else if (Optional<PostImplicitCall> PIE = P.getAs<PostImplicitCall>()) { - return PathDiagnosticLocation(PIE->getLocation(), SMng); - } else if (Optional<CallEnter> CE = P.getAs<CallEnter>()) { - return getLocationForCaller(CE->getCalleeContext(), - CE->getLocationContext(), - SMng); - } else if (Optional<CallExitEnd> CEE = P.getAs<CallExitEnd>()) { - return getLocationForCaller(CEE->getCalleeContext(), - CEE->getLocationContext(), - SMng); - } else if (auto CEB = P.getAs<CallExitBegin>()) { - if (const ReturnStmt *RS = CEB->getReturnStmt()) - return PathDiagnosticLocation::createBegin(RS, SMng, - CEB->getLocationContext()); - return PathDiagnosticLocation( - CEB->getLocationContext()->getDecl()->getSourceRange().getEnd(), SMng); - } else if (Optional<BlockEntrance> BE = P.getAs<BlockEntrance>()) { - CFGElement BlockFront = BE->getBlock()->front(); - if (auto StmtElt = BlockFront.getAs<CFGStmt>()) { - return PathDiagnosticLocation(StmtElt->getStmt()->getBeginLoc(), SMng); - } else if (auto NewAllocElt = BlockFront.getAs<CFGNewAllocator>()) { - return PathDiagnosticLocation( - NewAllocElt->getAllocatorExpr()->getBeginLoc(), SMng); - } - llvm_unreachable("Unexpected CFG element at front of block"); - } else if (Optional<FunctionExitPoint> FE = P.getAs<FunctionExitPoint>()) { - return PathDiagnosticLocation(FE->getStmt(), SMng, - FE->getLocationContext()); - } else { - llvm_unreachable("Unexpected ProgramPoint"); - } - - return PathDiagnosticLocation(S, SMng, P.getLocationContext()); -} - -static const LocationContext * -findTopAutosynthesizedParentContext(const LocationContext *LC) { - assert(LC->getAnalysisDeclContext()->isBodyAutosynthesized()); - const LocationContext *ParentLC = LC->getParent(); - assert(ParentLC && "We don't start analysis from autosynthesized code"); - while (ParentLC->getAnalysisDeclContext()->isBodyAutosynthesized()) { - LC = ParentLC; - ParentLC = LC->getParent(); - assert(ParentLC && "We don't start analysis from autosynthesized code"); - } - return LC; -} - -const Stmt *PathDiagnosticLocation::getStmt(const ExplodedNode *N) { - // We cannot place diagnostics on autosynthesized code. - // Put them onto the call site through which we jumped into autosynthesized - // code for the first time. - const LocationContext *LC = N->getLocationContext(); - if (LC->getAnalysisDeclContext()->isBodyAutosynthesized()) { - // It must be a stack frame because we only autosynthesize functions. - return cast<StackFrameContext>(findTopAutosynthesizedParentContext(LC)) - ->getCallSite(); - } - // Otherwise, see if the node's program point directly points to a statement. - ProgramPoint P = N->getLocation(); - if (auto SP = P.getAs<StmtPoint>()) - return SP->getStmt(); - if (auto BE = P.getAs<BlockEdge>()) - return BE->getSrc()->getTerminatorStmt(); - if (auto CE = P.getAs<CallEnter>()) - return CE->getCallExpr(); - if (auto CEE = P.getAs<CallExitEnd>()) - return CEE->getCalleeContext()->getCallSite(); - if (auto PIPP = P.getAs<PostInitializer>()) - return PIPP->getInitializer()->getInit(); - if (auto CEB = P.getAs<CallExitBegin>()) - return CEB->getReturnStmt(); - if (auto FEP = P.getAs<FunctionExitPoint>()) - return FEP->getStmt(); - - return nullptr; -} - -const Stmt *PathDiagnosticLocation::getNextStmt(const ExplodedNode *N) { - for (N = N->getFirstSucc(); N; N = N->getFirstSucc()) { - if (const Stmt *S = getStmt(N)) { - // Check if the statement is '?' or '&&'/'||'. These are "merges", - // not actual statement points. - switch (S->getStmtClass()) { - case Stmt::ChooseExprClass: - case Stmt::BinaryConditionalOperatorClass: - case Stmt::ConditionalOperatorClass: - continue; - case Stmt::BinaryOperatorClass: { - BinaryOperatorKind Op = cast<BinaryOperator>(S)->getOpcode(); - if (Op == BO_LAnd || Op == BO_LOr) - continue; - break; - } - default: - break; - } - // We found the statement, so return it. - return S; - } - } - - return nullptr; -} - -PathDiagnosticLocation - PathDiagnosticLocation::createEndOfPath(const ExplodedNode *N, - const SourceManager &SM) { - assert(N && "Cannot create a location with a null node."); - const Stmt *S = getStmt(N); - const LocationContext *LC = N->getLocationContext(); - - if (!S) { - // If this is an implicit call, return the implicit call point location. - if (Optional<PreImplicitCall> PIE = N->getLocationAs<PreImplicitCall>()) - return PathDiagnosticLocation(PIE->getLocation(), SM); - if (auto FE = N->getLocationAs<FunctionExitPoint>()) { - if (const ReturnStmt *RS = FE->getStmt()) - return PathDiagnosticLocation::createBegin(RS, SM, LC); - } - S = getNextStmt(N); - } - - if (S) { - ProgramPoint P = N->getLocation(); - - // For member expressions, return the location of the '.' or '->'. - if (const auto *ME = dyn_cast<MemberExpr>(S)) - return PathDiagnosticLocation::createMemberLoc(ME, SM); - - // For binary operators, return the location of the operator. - if (const auto *B = dyn_cast<BinaryOperator>(S)) - return PathDiagnosticLocation::createOperatorLoc(B, SM); - - if (P.getAs<PostStmtPurgeDeadSymbols>()) - return PathDiagnosticLocation::createEnd(S, SM, LC); - - if (S->getBeginLoc().isValid()) - return PathDiagnosticLocation(S, SM, LC); - return PathDiagnosticLocation(getValidSourceLocation(S, LC), SM); - } - - return createDeclEnd(N->getLocationContext(), SM); -} - -PathDiagnosticLocation PathDiagnosticLocation::createSingleLocation( - const PathDiagnosticLocation &PDL) { - FullSourceLoc L = PDL.asLocation(); - return PathDiagnosticLocation(L, L.getManager(), SingleLocK); -} - -FullSourceLoc - PathDiagnosticLocation::genLocation(SourceLocation L, - LocationOrAnalysisDeclContext LAC) const { - assert(isValid()); - // Note that we want a 'switch' here so that the compiler can warn us in - // case we add more cases. - switch (K) { - case SingleLocK: - case RangeK: - break; - case StmtK: - // Defensive checking. - if (!S) - break; - return FullSourceLoc(getValidSourceLocation(S, LAC), - const_cast<SourceManager&>(*SM)); - case DeclK: - // Defensive checking. - if (!D) - break; - return FullSourceLoc(D->getLocation(), const_cast<SourceManager&>(*SM)); - } - - return FullSourceLoc(L, const_cast<SourceManager&>(*SM)); -} - -PathDiagnosticRange - PathDiagnosticLocation::genRange(LocationOrAnalysisDeclContext LAC) const { - assert(isValid()); - // Note that we want a 'switch' here so that the compiler can warn us in - // case we add more cases. - switch (K) { - case SingleLocK: - return PathDiagnosticRange(SourceRange(Loc,Loc), true); - case RangeK: - break; - case StmtK: { - const Stmt *S = asStmt(); - switch (S->getStmtClass()) { - default: - break; - case Stmt::DeclStmtClass: { - const auto *DS = cast<DeclStmt>(S); - if (DS->isSingleDecl()) { - // Should always be the case, but we'll be defensive. - return SourceRange(DS->getBeginLoc(), - DS->getSingleDecl()->getLocation()); - } - break; - } - // FIXME: Provide better range information for different - // terminators. - case Stmt::IfStmtClass: - case Stmt::WhileStmtClass: - case Stmt::DoStmtClass: - case Stmt::ForStmtClass: - case Stmt::ChooseExprClass: - case Stmt::IndirectGotoStmtClass: - case Stmt::SwitchStmtClass: - case Stmt::BinaryConditionalOperatorClass: - case Stmt::ConditionalOperatorClass: - case Stmt::ObjCForCollectionStmtClass: { - SourceLocation L = getValidSourceLocation(S, LAC); - return SourceRange(L, L); - } - } - SourceRange R = S->getSourceRange(); - if (R.isValid()) - return R; - break; - } - case DeclK: - if (const auto *MD = dyn_cast<ObjCMethodDecl>(D)) - return MD->getSourceRange(); - if (const auto *FD = dyn_cast<FunctionDecl>(D)) { - if (Stmt *Body = FD->getBody()) - return Body->getSourceRange(); - } - else { - SourceLocation L = D->getLocation(); - return PathDiagnosticRange(SourceRange(L, L), true); - } - } - - return SourceRange(Loc, Loc); -} - -void PathDiagnosticLocation::flatten() { - if (K == StmtK) { - K = RangeK; - S = nullptr; - D = nullptr; - } - else if (K == DeclK) { - K = SingleLocK; - S = nullptr; - D = nullptr; - } -} - -//===----------------------------------------------------------------------===// -// Manipulation of PathDiagnosticCallPieces. -//===----------------------------------------------------------------------===// - -std::shared_ptr<PathDiagnosticCallPiece> -PathDiagnosticCallPiece::construct(const CallExitEnd &CE, - const SourceManager &SM) { - const Decl *caller = CE.getLocationContext()->getDecl(); - PathDiagnosticLocation pos = getLocationForCaller(CE.getCalleeContext(), - CE.getLocationContext(), - SM); - return std::shared_ptr<PathDiagnosticCallPiece>( - new PathDiagnosticCallPiece(caller, pos)); -} - -PathDiagnosticCallPiece * -PathDiagnosticCallPiece::construct(PathPieces &path, - const Decl *caller) { - std::shared_ptr<PathDiagnosticCallPiece> C( - new PathDiagnosticCallPiece(path, caller)); - path.clear(); - auto *R = C.get(); - path.push_front(std::move(C)); - return R; -} - -void PathDiagnosticCallPiece::setCallee(const CallEnter &CE, - const SourceManager &SM) { - const StackFrameContext *CalleeCtx = CE.getCalleeContext(); - Callee = CalleeCtx->getDecl(); - - callEnterWithin = PathDiagnosticLocation::createBegin(Callee, SM); - callEnter = getLocationForCaller(CalleeCtx, CE.getLocationContext(), SM); - - // Autosynthesized property accessors are special because we'd never - // pop back up to non-autosynthesized code until we leave them. - // This is not generally true for autosynthesized callees, which may call - // non-autosynthesized callbacks. - // Unless set here, the IsCalleeAnAutosynthesizedPropertyAccessor flag - // defaults to false. - if (const auto *MD = dyn_cast<ObjCMethodDecl>(Callee)) - IsCalleeAnAutosynthesizedPropertyAccessor = ( - MD->isPropertyAccessor() && - CalleeCtx->getAnalysisDeclContext()->isBodyAutosynthesized()); -} - -static void describeTemplateParameters(raw_ostream &Out, - const ArrayRef<TemplateArgument> TAList, - const LangOptions &LO, - StringRef Prefix = StringRef(), - StringRef Postfix = StringRef()); - -static void describeTemplateParameter(raw_ostream &Out, - const TemplateArgument &TArg, - const LangOptions &LO) { - - if (TArg.getKind() == TemplateArgument::ArgKind::Pack) { - describeTemplateParameters(Out, TArg.getPackAsArray(), LO); - } else { - TArg.print(PrintingPolicy(LO), Out); - } -} - -static void describeTemplateParameters(raw_ostream &Out, - const ArrayRef<TemplateArgument> TAList, - const LangOptions &LO, - StringRef Prefix, StringRef Postfix) { - if (TAList.empty()) - return; - - Out << Prefix; - for (int I = 0, Last = TAList.size() - 1; I != Last; ++I) { - describeTemplateParameter(Out, TAList[I], LO); - Out << ", "; - } - describeTemplateParameter(Out, TAList[TAList.size() - 1], LO); - Out << Postfix; -} - -static void describeClass(raw_ostream &Out, const CXXRecordDecl *D, - StringRef Prefix = StringRef()) { - if (!D->getIdentifier()) - return; - Out << Prefix << '\'' << *D; - if (const auto T = dyn_cast<ClassTemplateSpecializationDecl>(D)) - describeTemplateParameters(Out, T->getTemplateArgs().asArray(), - D->getASTContext().getLangOpts(), "<", ">"); - - Out << '\''; -} - -static bool describeCodeDecl(raw_ostream &Out, const Decl *D, - bool ExtendedDescription, - StringRef Prefix = StringRef()) { - if (!D) - return false; - - if (isa<BlockDecl>(D)) { - if (ExtendedDescription) - Out << Prefix << "anonymous block"; - return ExtendedDescription; - } - - if (const auto *MD = dyn_cast<CXXMethodDecl>(D)) { - Out << Prefix; - if (ExtendedDescription && !MD->isUserProvided()) { - if (MD->isExplicitlyDefaulted()) - Out << "defaulted "; - else - Out << "implicit "; - } - - if (const auto *CD = dyn_cast<CXXConstructorDecl>(MD)) { - if (CD->isDefaultConstructor()) - Out << "default "; - else if (CD->isCopyConstructor()) - Out << "copy "; - else if (CD->isMoveConstructor()) - Out << "move "; - - Out << "constructor"; - describeClass(Out, MD->getParent(), " for "); - } else if (isa<CXXDestructorDecl>(MD)) { - if (!MD->isUserProvided()) { - Out << "destructor"; - describeClass(Out, MD->getParent(), " for "); - } else { - // Use ~Foo for explicitly-written destructors. - Out << "'" << *MD << "'"; - } - } else if (MD->isCopyAssignmentOperator()) { - Out << "copy assignment operator"; - describeClass(Out, MD->getParent(), " for "); - } else if (MD->isMoveAssignmentOperator()) { - Out << "move assignment operator"; - describeClass(Out, MD->getParent(), " for "); - } else { - if (MD->getParent()->getIdentifier()) - Out << "'" << *MD->getParent() << "::" << *MD << "'"; - else - Out << "'" << *MD << "'"; - } - - return true; - } - - Out << Prefix << '\'' << cast<NamedDecl>(*D); - - // Adding template parameters. - if (const auto FD = dyn_cast<FunctionDecl>(D)) - if (const TemplateArgumentList *TAList = - FD->getTemplateSpecializationArgs()) - describeTemplateParameters(Out, TAList->asArray(), - FD->getASTContext().getLangOpts(), "<", ">"); - - Out << '\''; - return true; -} - -std::shared_ptr<PathDiagnosticEventPiece> -PathDiagnosticCallPiece::getCallEnterEvent() const { - // We do not produce call enters and call exits for autosynthesized property - // accessors. We do generally produce them for other functions coming from - // the body farm because they may call callbacks that bring us back into - // visible code. - if (!Callee || IsCalleeAnAutosynthesizedPropertyAccessor) - return nullptr; - - SmallString<256> buf; - llvm::raw_svector_ostream Out(buf); - - Out << "Calling "; - describeCodeDecl(Out, Callee, /*ExtendedDescription=*/true); - - assert(callEnter.asLocation().isValid()); - return std::make_shared<PathDiagnosticEventPiece>(callEnter, Out.str()); -} - -std::shared_ptr<PathDiagnosticEventPiece> -PathDiagnosticCallPiece::getCallEnterWithinCallerEvent() const { - if (!callEnterWithin.asLocation().isValid()) - return nullptr; - if (Callee->isImplicit() || !Callee->hasBody()) - return nullptr; - if (const auto *MD = dyn_cast<CXXMethodDecl>(Callee)) - if (MD->isDefaulted()) - return nullptr; - - SmallString<256> buf; - llvm::raw_svector_ostream Out(buf); - - Out << "Entered call"; - describeCodeDecl(Out, Caller, /*ExtendedDescription=*/false, " from "); - - return std::make_shared<PathDiagnosticEventPiece>(callEnterWithin, Out.str()); -} - -std::shared_ptr<PathDiagnosticEventPiece> -PathDiagnosticCallPiece::getCallExitEvent() const { - // We do not produce call enters and call exits for autosynthesized property - // accessors. We do generally produce them for other functions coming from - // the body farm because they may call callbacks that bring us back into - // visible code. - if (NoExit || IsCalleeAnAutosynthesizedPropertyAccessor) - return nullptr; - - SmallString<256> buf; - llvm::raw_svector_ostream Out(buf); - - if (!CallStackMessage.empty()) { - Out << CallStackMessage; - } else { - bool DidDescribe = describeCodeDecl(Out, Callee, - /*ExtendedDescription=*/false, - "Returning from "); - if (!DidDescribe) - Out << "Returning to caller"; - } - - assert(callReturn.asLocation().isValid()); - return std::make_shared<PathDiagnosticEventPiece>(callReturn, Out.str()); -} - -static void compute_path_size(const PathPieces &pieces, unsigned &size) { - for (const auto &I : pieces) { - const PathDiagnosticPiece *piece = I.get(); - if (const auto *cp = dyn_cast<PathDiagnosticCallPiece>(piece)) - compute_path_size(cp->path, size); - else - ++size; - } -} - -unsigned PathDiagnostic::full_size() { - unsigned size = 0; - compute_path_size(path, size); - return size; -} - -//===----------------------------------------------------------------------===// -// FoldingSet profiling methods. -//===----------------------------------------------------------------------===// - -void PathDiagnosticLocation::Profile(llvm::FoldingSetNodeID &ID) const { - ID.AddInteger(Range.getBegin().getRawEncoding()); - ID.AddInteger(Range.getEnd().getRawEncoding()); - ID.AddInteger(Loc.getRawEncoding()); -} - -void PathDiagnosticPiece::Profile(llvm::FoldingSetNodeID &ID) const { - ID.AddInteger((unsigned) getKind()); - ID.AddString(str); - // FIXME: Add profiling support for code hints. - ID.AddInteger((unsigned) getDisplayHint()); - ArrayRef<SourceRange> Ranges = getRanges(); - for (const auto &I : Ranges) { - ID.AddInteger(I.getBegin().getRawEncoding()); - ID.AddInteger(I.getEnd().getRawEncoding()); - } -} - -void PathDiagnosticCallPiece::Profile(llvm::FoldingSetNodeID &ID) const { - PathDiagnosticPiece::Profile(ID); - for (const auto &I : path) - ID.Add(*I); -} - -void PathDiagnosticSpotPiece::Profile(llvm::FoldingSetNodeID &ID) const { - PathDiagnosticPiece::Profile(ID); - ID.Add(Pos); -} - -void PathDiagnosticControlFlowPiece::Profile(llvm::FoldingSetNodeID &ID) const { - PathDiagnosticPiece::Profile(ID); - for (const auto &I : *this) - ID.Add(I); -} - -void PathDiagnosticMacroPiece::Profile(llvm::FoldingSetNodeID &ID) const { - PathDiagnosticSpotPiece::Profile(ID); - for (const auto &I : subPieces) - ID.Add(*I); -} - -void PathDiagnosticNotePiece::Profile(llvm::FoldingSetNodeID &ID) const { - PathDiagnosticSpotPiece::Profile(ID); -} - -void PathDiagnosticPopUpPiece::Profile(llvm::FoldingSetNodeID &ID) const { - PathDiagnosticSpotPiece::Profile(ID); -} - -void PathDiagnostic::Profile(llvm::FoldingSetNodeID &ID) const { - ID.Add(getLocation()); - ID.AddString(BugType); - ID.AddString(VerboseDesc); - ID.AddString(Category); -} - -void PathDiagnostic::FullProfile(llvm::FoldingSetNodeID &ID) const { - Profile(ID); - for (const auto &I : path) - ID.Add(*I); - for (meta_iterator I = meta_begin(), E = meta_end(); I != E; ++I) - ID.AddString(*I); -} - -StackHintGenerator::~StackHintGenerator() = default; - -std::string StackHintGeneratorForSymbol::getMessage(const ExplodedNode *N){ - if (!N) - return getMessageForSymbolNotFound(); - - ProgramPoint P = N->getLocation(); - CallExitEnd CExit = P.castAs<CallExitEnd>(); - - // FIXME: Use CallEvent to abstract this over all calls. - const Stmt *CallSite = CExit.getCalleeContext()->getCallSite(); - const auto *CE = dyn_cast_or_null<CallExpr>(CallSite); - if (!CE) - return {}; - - // Check if one of the parameters are set to the interesting symbol. - unsigned ArgIndex = 0; - for (CallExpr::const_arg_iterator I = CE->arg_begin(), - E = CE->arg_end(); I != E; ++I, ++ArgIndex){ - SVal SV = N->getSVal(*I); - - // Check if the variable corresponding to the symbol is passed by value. - SymbolRef AS = SV.getAsLocSymbol(); - if (AS == Sym) { - return getMessageForArg(*I, ArgIndex); - } - - // Check if the parameter is a pointer to the symbol. - if (Optional<loc::MemRegionVal> Reg = SV.getAs<loc::MemRegionVal>()) { - // Do not attempt to dereference void*. - if ((*I)->getType()->isVoidPointerType()) - continue; - SVal PSV = N->getState()->getSVal(Reg->getRegion()); - SymbolRef AS = PSV.getAsLocSymbol(); - if (AS == Sym) { - return getMessageForArg(*I, ArgIndex); - } - } - } - - // Check if we are returning the interesting symbol. - SVal SV = N->getSVal(CE); - SymbolRef RetSym = SV.getAsLocSymbol(); - if (RetSym == Sym) { - return getMessageForReturn(CE); - } - - return getMessageForSymbolNotFound(); -} - -std::string StackHintGeneratorForSymbol::getMessageForArg(const Expr *ArgE, - unsigned ArgIndex) { - // Printed parameters start at 1, not 0. - ++ArgIndex; - - SmallString<200> buf; - llvm::raw_svector_ostream os(buf); - - os << Msg << " via " << ArgIndex << llvm::getOrdinalSuffix(ArgIndex) - << " parameter"; - - return os.str(); -} - -LLVM_DUMP_METHOD void PathPieces::dump() const { - unsigned index = 0; - for (PathPieces::const_iterator I = begin(), E = end(); I != E; ++I) { - llvm::errs() << "[" << index++ << "] "; - (*I)->dump(); - llvm::errs() << "\n"; - } -} - -LLVM_DUMP_METHOD void PathDiagnosticCallPiece::dump() const { - llvm::errs() << "CALL\n--------------\n"; - - if (const Stmt *SLoc = getLocation().getStmtOrNull()) - SLoc->dump(); - else if (const auto *ND = dyn_cast_or_null<NamedDecl>(getCallee())) - llvm::errs() << *ND << "\n"; - else - getLocation().dump(); -} - -LLVM_DUMP_METHOD void PathDiagnosticEventPiece::dump() const { - llvm::errs() << "EVENT\n--------------\n"; - llvm::errs() << getString() << "\n"; - llvm::errs() << " ---- at ----\n"; - getLocation().dump(); -} - -LLVM_DUMP_METHOD void PathDiagnosticControlFlowPiece::dump() const { - llvm::errs() << "CONTROL\n--------------\n"; - getStartLocation().dump(); - llvm::errs() << " ---- to ----\n"; - getEndLocation().dump(); -} - -LLVM_DUMP_METHOD void PathDiagnosticMacroPiece::dump() const { - llvm::errs() << "MACRO\n--------------\n"; - // FIXME: Print which macro is being invoked. -} - -LLVM_DUMP_METHOD void PathDiagnosticNotePiece::dump() const { - llvm::errs() << "NOTE\n--------------\n"; - llvm::errs() << getString() << "\n"; - llvm::errs() << " ---- at ----\n"; - getLocation().dump(); -} - -LLVM_DUMP_METHOD void PathDiagnosticPopUpPiece::dump() const { - llvm::errs() << "POP-UP\n--------------\n"; - llvm::errs() << getString() << "\n"; - llvm::errs() << " ---- at ----\n"; - getLocation().dump(); -} - -LLVM_DUMP_METHOD void PathDiagnosticLocation::dump() const { - if (!isValid()) { - llvm::errs() << "<INVALID>\n"; - return; - } - - switch (K) { - case RangeK: - // FIXME: actually print the range. - llvm::errs() << "<range>\n"; - break; - case SingleLocK: - asLocation().dump(); - llvm::errs() << "\n"; - break; - case StmtK: - if (S) - S->dump(); - else - llvm::errs() << "<NULL STMT>\n"; - break; - case DeclK: - if (const auto *ND = dyn_cast_or_null<NamedDecl>(D)) - llvm::errs() << *ND << "\n"; - else if (isa<BlockDecl>(D)) - // FIXME: Make this nicer. - llvm::errs() << "<block>\n"; - else if (D) - llvm::errs() << "<unknown decl>\n"; - else - llvm::errs() << "<NULL DECL>\n"; - break; - } -} diff --git a/lib/StaticAnalyzer/Core/PlistDiagnostics.cpp b/lib/StaticAnalyzer/Core/PlistDiagnostics.cpp index 8387512792974..3a3942a8301bd 100644 --- a/lib/StaticAnalyzer/Core/PlistDiagnostics.cpp +++ b/lib/StaticAnalyzer/Core/PlistDiagnostics.cpp @@ -10,20 +10,22 @@ // //===----------------------------------------------------------------------===// +#include "clang/Analysis/PathDiagnostic.h" #include "clang/Basic/FileManager.h" #include "clang/Basic/PlistSupport.h" #include "clang/Basic/SourceManager.h" #include "clang/Basic/Version.h" +#include "clang/CrossTU/CrossTranslationUnit.h" +#include "clang/Frontend/ASTUnit.h" #include "clang/Lex/Preprocessor.h" #include "clang/Lex/TokenConcatenation.h" #include "clang/Rewrite/Core/HTMLRewrite.h" #include "clang/StaticAnalyzer/Core/AnalyzerOptions.h" -#include "clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h" #include "clang/StaticAnalyzer/Core/IssueHash.h" #include "clang/StaticAnalyzer/Core/PathDiagnosticConsumers.h" -#include "llvm/ADT/Statistic.h" #include "llvm/ADT/SmallPtrSet.h" #include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/Statistic.h" #include "llvm/Support/Casting.h" using namespace clang; @@ -39,12 +41,13 @@ namespace { class PlistDiagnostics : public PathDiagnosticConsumer { const std::string OutputFile; const Preprocessor &PP; + const cross_tu::CrossTranslationUnitContext &CTU; AnalyzerOptions &AnOpts; const bool SupportsCrossFileDiagnostics; public: - PlistDiagnostics(AnalyzerOptions &AnalyzerOpts, - const std::string& prefix, + PlistDiagnostics(AnalyzerOptions &AnalyzerOpts, const std::string &prefix, const Preprocessor &PP, + const cross_tu::CrossTranslationUnitContext &CTU, bool supportsMultipleFiles); ~PlistDiagnostics() override {} @@ -73,12 +76,14 @@ class PlistPrinter { const FIDMap& FM; AnalyzerOptions &AnOpts; const Preprocessor &PP; + const cross_tu::CrossTranslationUnitContext &CTU; llvm::SmallVector<const PathDiagnosticMacroPiece *, 0> MacroPieces; public: PlistPrinter(const FIDMap& FM, AnalyzerOptions &AnOpts, - const Preprocessor &PP) - : FM(FM), AnOpts(AnOpts), PP(PP) { + const Preprocessor &PP, + const cross_tu::CrossTranslationUnitContext &CTU) + : FM(FM), AnOpts(AnOpts), PP(PP), CTU(CTU) { } void ReportDiag(raw_ostream &o, const PathDiagnosticPiece& P) { @@ -129,6 +134,7 @@ private: void EmitRanges(raw_ostream &o, const ArrayRef<SourceRange> Ranges, unsigned indent); void EmitMessage(raw_ostream &o, StringRef Message, unsigned indent); + void EmitFixits(raw_ostream &o, ArrayRef<FixItHint> fixits, unsigned indent); void ReportControlFlow(raw_ostream &o, const PathDiagnosticControlFlowPiece& P, @@ -160,8 +166,8 @@ struct ExpansionInfo { } // end of anonymous namespace static void printBugPath(llvm::raw_ostream &o, const FIDMap& FM, - AnalyzerOptions &AnOpts, - const Preprocessor &PP, + AnalyzerOptions &AnOpts, const Preprocessor &PP, + const cross_tu::CrossTranslationUnitContext &CTU, const PathPieces &Path); /// Print coverage information to output stream {@code o}. @@ -172,8 +178,9 @@ static void printCoverage(const PathDiagnostic *D, FIDMap &FM, llvm::raw_fd_ostream &o); -static ExpansionInfo getExpandedMacro(SourceLocation MacroLoc, - const Preprocessor &PP); +static ExpansionInfo +getExpandedMacro(SourceLocation MacroLoc, const Preprocessor &PP, + const cross_tu::CrossTranslationUnitContext &CTU); //===----------------------------------------------------------------------===// // Methods of PlistPrinter. @@ -216,6 +223,33 @@ void PlistPrinter::EmitMessage(raw_ostream &o, StringRef Message, EmitString(o, Message) << '\n'; } +void PlistPrinter::EmitFixits(raw_ostream &o, ArrayRef<FixItHint> fixits, + unsigned indent) { + if (fixits.size() == 0) + return; + + const SourceManager &SM = PP.getSourceManager(); + const LangOptions &LangOpts = PP.getLangOpts(); + + Indent(o, indent) << "<key>fixits</key>\n"; + Indent(o, indent) << "<array>\n"; + for (const auto &fixit : fixits) { + assert(!fixit.isNull()); + // FIXME: Add support for InsertFromRange and BeforePreviousInsertion. + assert(!fixit.InsertFromRange.isValid() && "Not implemented yet!"); + assert(!fixit.BeforePreviousInsertions && "Not implemented yet!"); + Indent(o, indent) << " <dict>\n"; + Indent(o, indent) << " <key>remove_range</key>\n"; + EmitRange(o, SM, Lexer::getAsCharRange(fixit.RemoveRange, SM, LangOpts), + FM, indent + 2); + Indent(o, indent) << " <key>insert_string</key>"; + EmitString(o, fixit.CodeToInsert); + o << "\n"; + Indent(o, indent) << " </dict>\n"; + } + Indent(o, indent) << "</array>\n"; +} + void PlistPrinter::ReportControlFlow(raw_ostream &o, const PathDiagnosticControlFlowPiece& P, unsigned indent) { @@ -266,6 +300,9 @@ void PlistPrinter::ReportControlFlow(raw_ostream &o, EmitString(o, s) << '\n'; } + assert(P.getFixits().size() == 0 && + "Fixits on constrol flow pieces are not implemented yet!"); + --indent; Indent(o, indent) << "</dict>\n"; } @@ -302,6 +339,9 @@ void PlistPrinter::ReportEvent(raw_ostream &o, const PathDiagnosticEventPiece& P // Output the text. EmitMessage(o, P.getString(), indent); + // Output the fixits. + EmitFixits(o, P.getFixits(), indent); + // Finish up. --indent; Indent(o, indent); o << "</dict>\n"; @@ -329,6 +369,9 @@ void PlistPrinter::ReportCall(raw_ostream &o, const PathDiagnosticCallPiece &P, if (auto callExit = P.getCallExitEvent()) ReportPiece(o, *callExit, indent, depth, /*includeControlFlow*/ true); + + assert(P.getFixits().size() == 0 && + "Fixits on call pieces are not implemented yet!"); } void PlistPrinter::ReportMacroSubPieces(raw_ostream &o, @@ -341,13 +384,16 @@ void PlistPrinter::ReportMacroSubPieces(raw_ostream &o, I != E; ++I) { ReportPiece(o, **I, indent, depth, /*includeControlFlow*/ false); } + + assert(P.getFixits().size() == 0 && + "Fixits on constrol flow pieces are not implemented yet!"); } void PlistPrinter::ReportMacroExpansions(raw_ostream &o, unsigned indent) { for (const PathDiagnosticMacroPiece *P : MacroPieces) { const SourceManager &SM = PP.getSourceManager(); - ExpansionInfo EI = getExpandedMacro(P->getLocation().asLocation(), PP); + ExpansionInfo EI = getExpandedMacro(P->getLocation().asLocation(), PP, CTU); Indent(o, indent) << "<dict>\n"; ++indent; @@ -398,6 +444,9 @@ void PlistPrinter::ReportNote(raw_ostream &o, const PathDiagnosticNotePiece& P, // Output the text. EmitMessage(o, P.getString(), indent); + // Output the fixits. + EmitFixits(o, P.getFixits(), indent); + // Finish up. --indent; Indent(o, indent); o << "</dict>\n"; @@ -426,6 +475,9 @@ void PlistPrinter::ReportPopUp(raw_ostream &o, // Output the text. EmitMessage(o, P.getString(), indent); + assert(P.getFixits().size() == 0 && + "Fixits on pop-up pieces are not implemented yet!"); + // Finish up. --indent; Indent(o, indent) << "</dict>\n"; @@ -469,20 +521,20 @@ static void printCoverage(const PathDiagnostic *D, } static void printBugPath(llvm::raw_ostream &o, const FIDMap& FM, - AnalyzerOptions &AnOpts, - const Preprocessor &PP, + AnalyzerOptions &AnOpts, const Preprocessor &PP, + const cross_tu::CrossTranslationUnitContext &CTU, const PathPieces &Path) { - PlistPrinter Printer(FM, AnOpts, PP); - assert(std::is_partitioned( - Path.begin(), Path.end(), - [](const std::shared_ptr<PathDiagnosticPiece> &E) - { return E->getKind() == PathDiagnosticPiece::Note; }) && + PlistPrinter Printer(FM, AnOpts, PP, CTU); + assert(std::is_partitioned(Path.begin(), Path.end(), + [](const PathDiagnosticPieceRef &E) { + return E->getKind() == PathDiagnosticPiece::Note; + }) && "PathDiagnostic is not partitioned so that notes precede the rest"); PathPieces::const_iterator FirstNonNote = std::partition_point( - Path.begin(), Path.end(), - [](const std::shared_ptr<PathDiagnosticPiece> &E) - { return E->getKind() == PathDiagnosticPiece::Note; }); + Path.begin(), Path.end(), [](const PathDiagnosticPieceRef &E) { + return E->getKind() == PathDiagnosticPiece::Note; + }); PathPieces::const_iterator I = Path.begin(); @@ -518,26 +570,29 @@ static void printBugPath(llvm::raw_ostream &o, const FIDMap& FM, // Methods of PlistDiagnostics. //===----------------------------------------------------------------------===// -PlistDiagnostics::PlistDiagnostics(AnalyzerOptions &AnalyzerOpts, - const std::string& output, - const Preprocessor &PP, - bool supportsMultipleFiles) - : OutputFile(output), PP(PP), AnOpts(AnalyzerOpts), - SupportsCrossFileDiagnostics(supportsMultipleFiles) {} - -void ento::createPlistDiagnosticConsumer(AnalyzerOptions &AnalyzerOpts, - PathDiagnosticConsumers &C, - const std::string& s, - const Preprocessor &PP) { - C.push_back(new PlistDiagnostics(AnalyzerOpts, s, PP, +PlistDiagnostics::PlistDiagnostics( + AnalyzerOptions &AnalyzerOpts, const std::string &output, + const Preprocessor &PP, const cross_tu::CrossTranslationUnitContext &CTU, + bool supportsMultipleFiles) + : OutputFile(output), PP(PP), CTU(CTU), AnOpts(AnalyzerOpts), + SupportsCrossFileDiagnostics(supportsMultipleFiles) { + // FIXME: Will be used by a later planned change. + (void)this->CTU; +} + +void ento::createPlistDiagnosticConsumer( + AnalyzerOptions &AnalyzerOpts, PathDiagnosticConsumers &C, + const std::string &s, const Preprocessor &PP, + const cross_tu::CrossTranslationUnitContext &CTU) { + C.push_back(new PlistDiagnostics(AnalyzerOpts, s, PP, CTU, /*supportsMultipleFiles*/ false)); } -void ento::createPlistMultiFileDiagnosticConsumer(AnalyzerOptions &AnalyzerOpts, - PathDiagnosticConsumers &C, - const std::string &s, - const Preprocessor &PP) { - C.push_back(new PlistDiagnostics(AnalyzerOpts, s, PP, +void ento::createPlistMultiFileDiagnosticConsumer( + AnalyzerOptions &AnalyzerOpts, PathDiagnosticConsumers &C, + const std::string &s, const Preprocessor &PP, + const cross_tu::CrossTranslationUnitContext &CTU) { + C.push_back(new PlistDiagnostics(AnalyzerOpts, s, PP, CTU, /*supportsMultipleFiles*/ true)); } void PlistDiagnostics::FlushDiagnosticsImpl( @@ -590,7 +645,7 @@ void PlistDiagnostics::FlushDiagnosticsImpl( // Open the file. std::error_code EC; - llvm::raw_fd_ostream o(OutputFile, EC, llvm::sys::fs::F_Text); + llvm::raw_fd_ostream o(OutputFile, EC, llvm::sys::fs::OF_Text); if (EC) { llvm::errs() << "warning: could not create file: " << EC.message() << '\n'; return; @@ -614,7 +669,7 @@ void PlistDiagnostics::FlushDiagnosticsImpl( o << " <dict>\n"; const PathDiagnostic *D = *DI; - printBugPath(o, FM, AnOpts, PP, D->path); + printBugPath(o, FM, AnOpts, PP, CTU, D->path); // Output the bug type and bug category. o << " <key>description</key>"; @@ -624,7 +679,7 @@ void PlistDiagnostics::FlushDiagnosticsImpl( o << " <key>type</key>"; EmitString(o, D->getBugType()) << '\n'; o << " <key>check_name</key>"; - EmitString(o, D->getCheckName()) << '\n'; + EmitString(o, D->getCheckerName()) << '\n'; o << " <!-- This hash is experimental and going to change! -->\n"; o << " <key>issue_hash_content_of_line_in_context</key>"; @@ -634,7 +689,7 @@ void PlistDiagnostics::FlushDiagnosticsImpl( : D->getLocation().asLocation()), SM); const Decl *DeclWithIssue = D->getDeclWithIssue(); - EmitString(o, GetIssueHash(SM, L, D->getCheckName(), D->getBugType(), + EmitString(o, GetIssueHash(SM, L, D->getCheckerName(), D->getBugType(), DeclWithIssue, LangOpts)) << '\n'; @@ -867,17 +922,23 @@ static const MacroInfo *getMacroInfoForLocation(const Preprocessor &PP, // Definitions of helper functions and methods for expanding macros. //===----------------------------------------------------------------------===// -static ExpansionInfo getExpandedMacro(SourceLocation MacroLoc, - const Preprocessor &PP) { +static ExpansionInfo +getExpandedMacro(SourceLocation MacroLoc, const Preprocessor &PP, + const cross_tu::CrossTranslationUnitContext &CTU) { + + const Preprocessor *PPToUse = &PP; + if (auto LocAndUnit = CTU.getImportedFromSourceLocation(MacroLoc)) { + MacroLoc = LocAndUnit->first; + PPToUse = &LocAndUnit->second->getPreprocessor(); + } llvm::SmallString<200> ExpansionBuf; llvm::raw_svector_ostream OS(ExpansionBuf); - TokenPrinter Printer(OS, PP); + TokenPrinter Printer(OS, *PPToUse); llvm::SmallPtrSet<IdentifierInfo*, 8> AlreadyProcessedTokens; - std::string MacroName = - getMacroNameAndPrintExpansion(Printer, MacroLoc, PP, MacroArgMap{}, - AlreadyProcessedTokens); + std::string MacroName = getMacroNameAndPrintExpansion( + Printer, MacroLoc, *PPToUse, MacroArgMap{}, AlreadyProcessedTokens); return { MacroName, OS.str() }; } diff --git a/lib/StaticAnalyzer/Core/ProgramState.cpp b/lib/StaticAnalyzer/Core/ProgramState.cpp index a1ca0b1b84bfa..f50d82de3b28d 100644 --- a/lib/StaticAnalyzer/Core/ProgramState.cpp +++ b/lib/StaticAnalyzer/Core/ProgramState.cpp @@ -15,7 +15,7 @@ #include "clang/Basic/JsonSupport.h" #include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/DynamicTypeMap.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/DynamicType.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h" #include "clang/StaticAnalyzer/Core/PathSensitive/SubEngine.h" #include "llvm/Support/raw_ostream.h" diff --git a/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp b/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp index 64724227395d5..9752a0e22832c 100644 --- a/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp +++ b/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp @@ -330,7 +330,7 @@ private: std::unique_ptr<ConstraintManager> ento::CreateRangeConstraintManager(ProgramStateManager &StMgr, SubEngine *Eng) { - return llvm::make_unique<RangeConstraintManager>(Eng, StMgr.getSValBuilder()); + return std::make_unique<RangeConstraintManager>(Eng, StMgr.getSValBuilder()); } bool RangeConstraintManager::canReasonAbout(SVal X) const { diff --git a/lib/StaticAnalyzer/Core/RegionStore.cpp b/lib/StaticAnalyzer/Core/RegionStore.cpp index d2aea1fd92dda..5d2ef59e2d66b 100644 --- a/lib/StaticAnalyzer/Core/RegionStore.cpp +++ b/lib/StaticAnalyzer/Core/RegionStore.cpp @@ -108,7 +108,7 @@ public: Data == X.Data; } - void dump() const; + LLVM_DUMP_METHOD void dump() const; }; } // end anonymous namespace @@ -135,7 +135,9 @@ static inline raw_ostream &operator<<(raw_ostream &Out, BindingKey K) { } // namespace llvm -LLVM_DUMP_METHOD void BindingKey::dump() const { llvm::errs() << *this; } +#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP) +void BindingKey::dump() const { llvm::errs() << *this; } +#endif //===----------------------------------------------------------------------===// // Actual Store type. @@ -153,28 +155,42 @@ class RegionBindingsRef : public llvm::ImmutableMapRef<const MemRegion *, ClusterBindings> { ClusterBindings::Factory *CBFactory; + // This flag indicates whether the current bindings are within the analysis + // that has started from main(). It affects how we perform loads from + // global variables that have initializers: if we have observed the + // program execution from the start and we know that these variables + // have not been overwritten yet, we can be sure that their initializers + // are still relevant. This flag never gets changed when the bindings are + // updated, so it could potentially be moved into RegionStoreManager + // (as if it's the same bindings but a different loading procedure) + // however that would have made the manager needlessly stateful. + bool IsMainAnalysis; + public: typedef llvm::ImmutableMapRef<const MemRegion *, ClusterBindings> ParentTy; RegionBindingsRef(ClusterBindings::Factory &CBFactory, const RegionBindings::TreeTy *T, - RegionBindings::TreeTy::Factory *F) + RegionBindings::TreeTy::Factory *F, + bool IsMainAnalysis) : llvm::ImmutableMapRef<const MemRegion *, ClusterBindings>(T, F), - CBFactory(&CBFactory) {} + CBFactory(&CBFactory), IsMainAnalysis(IsMainAnalysis) {} - RegionBindingsRef(const ParentTy &P, ClusterBindings::Factory &CBFactory) + RegionBindingsRef(const ParentTy &P, + ClusterBindings::Factory &CBFactory, + bool IsMainAnalysis) : llvm::ImmutableMapRef<const MemRegion *, ClusterBindings>(P), - CBFactory(&CBFactory) {} + CBFactory(&CBFactory), IsMainAnalysis(IsMainAnalysis) {} RegionBindingsRef add(key_type_ref K, data_type_ref D) const { return RegionBindingsRef(static_cast<const ParentTy *>(this)->add(K, D), - *CBFactory); + *CBFactory, IsMainAnalysis); } RegionBindingsRef remove(key_type_ref K) const { return RegionBindingsRef(static_cast<const ParentTy *>(this)->remove(K), - *CBFactory); + *CBFactory, IsMainAnalysis); } RegionBindingsRef addBinding(BindingKey K, SVal V) const; @@ -204,7 +220,13 @@ public: /// Return the internal tree as a Store. Store asStore() const { - return asImmutableMap().getRootWithoutRetain(); + llvm::PointerIntPair<Store, 1, bool> Ptr = { + asImmutableMap().getRootWithoutRetain(), IsMainAnalysis}; + return reinterpret_cast<Store>(Ptr.getOpaqueValue()); + } + + bool isMainAnalysis() const { + return IsMainAnalysis; } void printJson(raw_ostream &Out, const char *NL = "\n", @@ -379,8 +401,15 @@ public: /// casts from arrays to pointers. SVal ArrayToPointer(Loc Array, QualType ElementTy) override; + /// Creates the Store that correctly represents memory contents before + /// the beginning of the analysis of the given top-level stack frame. StoreRef getInitialStore(const LocationContext *InitLoc) override { - return StoreRef(RBFactory.getEmptyMap().getRootWithoutRetain(), *this); + bool IsMainAnalysis = false; + if (const auto *FD = dyn_cast<FunctionDecl>(InitLoc->getDecl())) + IsMainAnalysis = FD->isMain() && !Ctx.getLangOpts().CPlusPlus; + return StoreRef(RegionBindingsRef( + RegionBindingsRef::ParentTy(RBFactory.getEmptyMap(), RBFactory), + CBFactory, IsMainAnalysis).asStore(), *this); } //===-------------------------------------------------------------------===// @@ -606,9 +635,13 @@ public: // Part of public interface to class. //===------------------------------------------------------------------===// RegionBindingsRef getRegionBindings(Store store) const { - return RegionBindingsRef(CBFactory, - static_cast<const RegionBindings::TreeTy*>(store), - RBFactory.getTreeFactory()); + llvm::PointerIntPair<Store, 1, bool> Ptr; + Ptr.setFromOpaqueValue(const_cast<void *>(store)); + return RegionBindingsRef( + CBFactory, + static_cast<const RegionBindings::TreeTy *>(Ptr.getPointer()), + RBFactory.getTreeFactory(), + Ptr.getInt()); } void printJson(raw_ostream &Out, Store S, const char *NL = "\n", @@ -642,14 +675,14 @@ public: // Part of public interface to class. std::unique_ptr<StoreManager> ento::CreateRegionStoreManager(ProgramStateManager &StMgr) { RegionStoreFeatures F = maximal_features_tag(); - return llvm::make_unique<RegionStoreManager>(StMgr, F); + return std::make_unique<RegionStoreManager>(StMgr, F); } std::unique_ptr<StoreManager> ento::CreateFieldsOnlyRegionStoreManager(ProgramStateManager &StMgr) { RegionStoreFeatures F = minimal_features_tag(); F.enableFields(true); - return llvm::make_unique<RegionStoreManager>(StMgr, F); + return std::make_unique<RegionStoreManager>(StMgr, F); } @@ -1669,10 +1702,12 @@ SVal RegionStoreManager::getBindingForElement(RegionBindingsConstRef B, return svalBuilder.makeIntVal(c, T); } } else if (const VarRegion *VR = dyn_cast<VarRegion>(superR)) { - // Check if the containing array is const and has an initialized value. + // Check if the containing array has an initialized value that we can trust. + // We can trust a const value or a value of a global initializer in main(). const VarDecl *VD = VR->getDecl(); - // Either the array or the array element has to be const. - if (VD->getType().isConstQualified() || R->getElementType().isConstQualified()) { + if (VD->getType().isConstQualified() || + R->getElementType().isConstQualified() || + (B.isMainAnalysis() && VD->hasGlobalStorage())) { if (const Expr *Init = VD->getAnyInitializer()) { if (const auto *InitList = dyn_cast<InitListExpr>(Init)) { // The array index has to be known. @@ -1761,8 +1796,11 @@ SVal RegionStoreManager::getBindingForField(RegionBindingsConstRef B, const VarDecl *VD = VR->getDecl(); QualType RecordVarTy = VD->getType(); unsigned Index = FD->getFieldIndex(); - // Either the record variable or the field has to be const qualified. - if (RecordVarTy.isConstQualified() || Ty.isConstQualified()) + // Either the record variable or the field has an initializer that we can + // trust. We trust initializers of constants and, additionally, respect + // initializers of globals when analyzing main(). + if (RecordVarTy.isConstQualified() || Ty.isConstQualified() || + (B.isMainAnalysis() && VD->hasGlobalStorage())) if (const Expr *Init = VD->getAnyInitializer()) if (const auto *InitList = dyn_cast<InitListExpr>(Init)) { if (Index < InitList->getNumInits()) { @@ -1980,6 +2018,12 @@ SVal RegionStoreManager::getBindingForVar(RegionBindingsConstRef B, if (isa<GlobalsSpaceRegion>(MS)) { QualType T = VD->getType(); + // If we're in main(), then global initializers have not become stale yet. + if (B.isMainAnalysis()) + if (const Expr *Init = VD->getAnyInitializer()) + if (Optional<SVal> V = svalBuilder.getConstantVal(Init)) + return *V; + // Function-scoped static variables are default-initialized to 0; if they // have an initializer, it would have been processed by now. // FIXME: This is only true when we're starting analysis from main(). @@ -2247,8 +2291,7 @@ RegionBindingsRef RegionStoreManager::bindVector(RegionBindingsConstRef B, const TypedValueRegion* R, SVal V) { QualType T = R->getValueType(); - assert(T->isVectorType()); - const VectorType *VT = T->getAs<VectorType>(); // Use getAs for typedefs. + const VectorType *VT = T->castAs<VectorType>(); // Use castAs for typedefs. // Handle lazy compound values and symbolic values. if (V.getAs<nonloc::LazyCompoundVal>() || V.getAs<nonloc::SymbolVal>()) @@ -2333,7 +2376,7 @@ RegionBindingsRef RegionStoreManager::bindStruct(RegionBindingsConstRef B, QualType T = R->getValueType(); assert(T->isStructureOrClassType()); - const RecordType* RT = T->getAs<RecordType>(); + const RecordType* RT = T->castAs<RecordType>(); const RecordDecl *RD = RT->getDecl(); if (!RD->isCompleteDefinition()) diff --git a/lib/StaticAnalyzer/Core/SMTConstraintManager.cpp b/lib/StaticAnalyzer/Core/SMTConstraintManager.cpp index d5c14351d3302..6ad12ca0a688f 100644 --- a/lib/StaticAnalyzer/Core/SMTConstraintManager.cpp +++ b/lib/StaticAnalyzer/Core/SMTConstraintManager.cpp @@ -14,5 +14,5 @@ using namespace ento; std::unique_ptr<ConstraintManager> ento::CreateZ3ConstraintManager(ProgramStateManager &StMgr, SubEngine *Eng) { - return llvm::make_unique<SMTConstraintManager>(Eng, StMgr.getSValBuilder()); + return std::make_unique<SMTConstraintManager>(Eng, StMgr.getSValBuilder()); } diff --git a/lib/StaticAnalyzer/Core/SarifDiagnostics.cpp b/lib/StaticAnalyzer/Core/SarifDiagnostics.cpp index d1faf3f4dea9d..190ab7e21dbcc 100644 --- a/lib/StaticAnalyzer/Core/SarifDiagnostics.cpp +++ b/lib/StaticAnalyzer/Core/SarifDiagnostics.cpp @@ -10,10 +10,10 @@ // //===----------------------------------------------------------------------===// +#include "clang/Analysis/PathDiagnostic.h" #include "clang/Basic/Version.h" #include "clang/Lex/Preprocessor.h" #include "clang/StaticAnalyzer/Core/AnalyzerOptions.h" -#include "clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h" #include "clang/StaticAnalyzer/Core/PathDiagnosticConsumers.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/StringMap.h" @@ -43,10 +43,10 @@ public: }; } // end anonymous namespace -void ento::createSarifDiagnosticConsumer(AnalyzerOptions &AnalyzerOpts, - PathDiagnosticConsumers &C, - const std::string &Output, - const Preprocessor &) { +void ento::createSarifDiagnosticConsumer( + AnalyzerOptions &AnalyzerOpts, PathDiagnosticConsumers &C, + const std::string &Output, const Preprocessor &, + const cross_tu::CrossTranslationUnitContext &) { C.push_back(new SarifDiagnostics(AnalyzerOpts, Output)); } @@ -106,26 +106,26 @@ static std::string fileNameToURI(StringRef Filename) { return Ret.str().str(); } -static json::Object createFileLocation(const FileEntry &FE) { +static json::Object createArtifactLocation(const FileEntry &FE) { return json::Object{{"uri", fileNameToURI(getFileName(FE))}}; } -static json::Object createFile(const FileEntry &FE) { - return json::Object{{"fileLocation", createFileLocation(FE)}, +static json::Object createArtifact(const FileEntry &FE) { + return json::Object{{"location", createArtifactLocation(FE)}, {"roles", json::Array{"resultFile"}}, {"length", FE.getSize()}, {"mimeType", "text/plain"}}; } -static json::Object createFileLocation(const FileEntry &FE, - json::Array &Files) { +static json::Object createArtifactLocation(const FileEntry &FE, + json::Array &Artifacts) { std::string FileURI = fileNameToURI(getFileName(FE)); - // See if the Files array contains this URI already. If it does not, create - // a new file object to add to the array. - auto I = llvm::find_if(Files, [&](const json::Value &File) { + // See if the Artifacts array contains this URI already. If it does not, + // create a new artifact object to add to the array. + auto I = llvm::find_if(Artifacts, [&](const json::Value &File) { if (const json::Object *Obj = File.getAsObject()) { - if (const json::Object *FileLoc = Obj->getObject("fileLocation")) { + if (const json::Object *FileLoc = Obj->getObject("location")) { Optional<StringRef> URI = FileLoc->getString("uri"); return URI && URI->equals(FileURI); } @@ -133,28 +133,35 @@ static json::Object createFileLocation(const FileEntry &FE, return false; }); - // Calculate the index within the file location array so it can be stored in + // Calculate the index within the artifact array so it can be stored in // the JSON object. - auto Index = static_cast<unsigned>(std::distance(Files.begin(), I)); - if (I == Files.end()) - Files.push_back(createFile(FE)); + auto Index = static_cast<unsigned>(std::distance(Artifacts.begin(), I)); + if (I == Artifacts.end()) + Artifacts.push_back(createArtifact(FE)); - return json::Object{{"uri", FileURI}, {"fileIndex", Index}}; + return json::Object{{"uri", FileURI}, {"index", Index}}; } static json::Object createTextRegion(SourceRange R, const SourceManager &SM) { - return json::Object{ + json::Object Region{ {"startLine", SM.getExpansionLineNumber(R.getBegin())}, - {"endLine", SM.getExpansionLineNumber(R.getEnd())}, {"startColumn", SM.getExpansionColumnNumber(R.getBegin())}, - {"endColumn", SM.getExpansionColumnNumber(R.getEnd())}}; + }; + if (R.getBegin() == R.getEnd()) { + Region["endColumn"] = SM.getExpansionColumnNumber(R.getBegin()); + } else { + Region["endLine"] = SM.getExpansionLineNumber(R.getEnd()); + Region["endColumn"] = SM.getExpansionColumnNumber(R.getEnd()) + 1; + } + return Region; } static json::Object createPhysicalLocation(SourceRange R, const FileEntry &FE, const SourceManager &SMgr, - json::Array &Files) { - return json::Object{{{"fileLocation", createFileLocation(FE, Files)}, - {"region", createTextRegion(R, SMgr)}}}; + json::Array &Artifacts) { + return json::Object{ + {{"artifactLocation", createArtifactLocation(FE, Artifacts)}, + {"region", createTextRegion(R, SMgr)}}}; } enum class Importance { Important, Essential, Unimportant }; @@ -207,15 +214,16 @@ static Importance calculateImportance(const PathDiagnosticPiece &Piece) { } static json::Object createThreadFlow(const PathPieces &Pieces, - json::Array &Files) { + json::Array &Artifacts) { const SourceManager &SMgr = Pieces.front()->getLocation().getManager(); json::Array Locations; for (const auto &Piece : Pieces) { const PathDiagnosticLocation &P = Piece->getLocation(); Locations.push_back(createThreadFlowLocation( - createLocation(createPhysicalLocation(P.asRange(), - *P.asLocation().getFileEntry(), - SMgr, Files), + createLocation(createPhysicalLocation( + P.asRange(), + *P.asLocation().getExpansionLoc().getFileEntry(), + SMgr, Artifacts), Piece->getString()), calculateImportance(*Piece))); } @@ -223,35 +231,30 @@ static json::Object createThreadFlow(const PathPieces &Pieces, } static json::Object createCodeFlow(const PathPieces &Pieces, - json::Array &Files) { + json::Array &Artifacts) { return json::Object{ - {"threadFlows", json::Array{createThreadFlow(Pieces, Files)}}}; + {"threadFlows", json::Array{createThreadFlow(Pieces, Artifacts)}}}; } -static json::Object createTool() { - return json::Object{{"name", "clang"}, - {"fullName", "clang static analyzer"}, - {"language", "en-US"}, - {"version", getClangFullVersion()}}; -} - -static json::Object createResult(const PathDiagnostic &Diag, json::Array &Files, +static json::Object createResult(const PathDiagnostic &Diag, + json::Array &Artifacts, const StringMap<unsigned> &RuleMapping) { const PathPieces &Path = Diag.path.flatten(false); const SourceManager &SMgr = Path.front()->getLocation().getManager(); - auto Iter = RuleMapping.find(Diag.getCheckName()); + auto Iter = RuleMapping.find(Diag.getCheckerName()); assert(Iter != RuleMapping.end() && "Rule ID is not in the array index map?"); return json::Object{ {"message", createMessage(Diag.getVerboseDescription())}, - {"codeFlows", json::Array{createCodeFlow(Path, Files)}}, + {"codeFlows", json::Array{createCodeFlow(Path, Artifacts)}}, {"locations", json::Array{createLocation(createPhysicalLocation( Diag.getLocation().asRange(), - *Diag.getLocation().asLocation().getFileEntry(), SMgr, Files))}}, + *Diag.getLocation().asLocation().getExpansionLoc().getFileEntry(), + SMgr, Artifacts))}}, {"ruleIndex", Iter->getValue()}, - {"ruleId", Diag.getCheckName()}}; + {"ruleId", Diag.getCheckerName()}}; } static StringRef getRuleDescription(StringRef CheckName) { @@ -277,10 +280,10 @@ static StringRef getRuleHelpURIStr(StringRef CheckName) { } static json::Object createRule(const PathDiagnostic &Diag) { - StringRef CheckName = Diag.getCheckName(); + StringRef CheckName = Diag.getCheckerName(); json::Object Ret{ {"fullDescription", createMessage(getRuleDescription(CheckName))}, - {"name", createMessage(CheckName)}, + {"name", CheckName}, {"id", CheckName}}; std::string RuleURI = getRuleHelpURIStr(CheckName); @@ -296,7 +299,7 @@ static json::Array createRules(std::vector<const PathDiagnostic *> &Diags, llvm::StringSet<> Seen; llvm::for_each(Diags, [&](const PathDiagnostic *D) { - StringRef RuleID = D->getCheckName(); + StringRef RuleID = D->getCheckerName(); std::pair<llvm::StringSet<>::iterator, bool> P = Seen.insert(RuleID); if (P.second) { RuleMapping[RuleID] = Rules.size(); // Maps RuleID to an Array Index. @@ -307,24 +310,28 @@ static json::Array createRules(std::vector<const PathDiagnostic *> &Diags, return Rules; } -static json::Object createResources(std::vector<const PathDiagnostic *> &Diags, - StringMap<unsigned> &RuleMapping) { - return json::Object{{"rules", createRules(Diags, RuleMapping)}}; +static json::Object createTool(std::vector<const PathDiagnostic *> &Diags, + StringMap<unsigned> &RuleMapping) { + return json::Object{ + {"driver", json::Object{{"name", "clang"}, + {"fullName", "clang static analyzer"}, + {"language", "en-US"}, + {"version", getClangFullVersion()}, + {"rules", createRules(Diags, RuleMapping)}}}}; } static json::Object createRun(std::vector<const PathDiagnostic *> &Diags) { - json::Array Results, Files; + json::Array Results, Artifacts; StringMap<unsigned> RuleMapping; - json::Object Resources = createResources(Diags, RuleMapping); + json::Object Tool = createTool(Diags, RuleMapping); llvm::for_each(Diags, [&](const PathDiagnostic *D) { - Results.push_back(createResult(*D, Files, RuleMapping)); + Results.push_back(createResult(*D, Artifacts, RuleMapping)); }); - return json::Object{{"tool", createTool()}, - {"resources", std::move(Resources)}, + return json::Object{{"tool", std::move(Tool)}, {"results", std::move(Results)}, - {"files", std::move(Files)}}; + {"artifacts", std::move(Artifacts)}}; } void SarifDiagnostics::FlushDiagnosticsImpl( @@ -335,15 +342,15 @@ void SarifDiagnostics::FlushDiagnosticsImpl( // file can become large very quickly, so decoding into JSON to append a run // may be an expensive operation. std::error_code EC; - llvm::raw_fd_ostream OS(OutputFile, EC, llvm::sys::fs::F_Text); + llvm::raw_fd_ostream OS(OutputFile, EC, llvm::sys::fs::OF_Text); if (EC) { llvm::errs() << "warning: could not create file: " << EC.message() << '\n'; return; } json::Object Sarif{ {"$schema", - "http://json.schemastore.org/sarif-2.0.0-csd.2.beta.2018-11-28"}, - {"version", "2.0.0-csd.2.beta.2018-11-28"}, + "https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema-2.1.0.json"}, + {"version", "2.1.0"}, {"runs", json::Array{createRun(Diags)}}}; OS << llvm::formatv("{0:2}\n", json::Value(std::move(Sarif))); } diff --git a/lib/StaticAnalyzer/Core/Store.cpp b/lib/StaticAnalyzer/Core/Store.cpp index 3cf616161c669..b4ab6877726ce 100644 --- a/lib/StaticAnalyzer/Core/Store.cpp +++ b/lib/StaticAnalyzer/Core/Store.cpp @@ -52,7 +52,7 @@ StoreRef StoreManager::enterStackFrame(Store OldStore, Call.getInitialStackFrameContents(LCtx, InitialBindings); for (const auto &I : InitialBindings) - Store = Bind(Store.getStore(), I.first, I.second); + Store = Bind(Store.getStore(), I.first.castAs<Loc>(), I.second); return Store; } diff --git a/lib/StaticAnalyzer/Core/WorkList.cpp b/lib/StaticAnalyzer/Core/WorkList.cpp index 129d1720395e4..348552ba73a9b 100644 --- a/lib/StaticAnalyzer/Core/WorkList.cpp +++ b/lib/StaticAnalyzer/Core/WorkList.cpp @@ -79,11 +79,11 @@ public: WorkList::~WorkList() = default; std::unique_ptr<WorkList> WorkList::makeDFS() { - return llvm::make_unique<DFS>(); + return std::make_unique<DFS>(); } std::unique_ptr<WorkList> WorkList::makeBFS() { - return llvm::make_unique<BFS>(); + return std::make_unique<BFS>(); } namespace { @@ -124,7 +124,7 @@ namespace { } // namespace std::unique_ptr<WorkList> WorkList::makeBFSBlockDFSContents() { - return llvm::make_unique<BFSBlockDFSContents>(); + return std::make_unique<BFSBlockDFSContents>(); } namespace { @@ -186,7 +186,7 @@ public: } // namespace std::unique_ptr<WorkList> WorkList::makeUnexploredFirst() { - return llvm::make_unique<UnexploredFirstStack>(); + return std::make_unique<UnexploredFirstStack>(); } namespace { @@ -249,7 +249,7 @@ public: } // namespace std::unique_ptr<WorkList> WorkList::makeUnexploredFirstPriorityQueue() { - return llvm::make_unique<UnexploredFirstPriorityQueue>(); + return std::make_unique<UnexploredFirstPriorityQueue>(); } namespace { @@ -309,5 +309,5 @@ public: } std::unique_ptr<WorkList> WorkList::makeUnexploredFirstPriorityLocationQueue() { - return llvm::make_unique<UnexploredFirstPriorityLocationQueue>(); + return std::make_unique<UnexploredFirstPriorityLocationQueue>(); } |