diff options
Diffstat (limited to 'lib/StaticAnalyzer/Core')
49 files changed, 2196 insertions, 3411 deletions
diff --git a/lib/StaticAnalyzer/Core/APSIntType.cpp b/lib/StaticAnalyzer/Core/APSIntType.cpp index c7e95268213e..a1de10c89ed9 100644 --- a/lib/StaticAnalyzer/Core/APSIntType.cpp +++ b/lib/StaticAnalyzer/Core/APSIntType.cpp @@ -1,9 +1,8 @@ //===--- APSIntType.cpp - Simple record of the type of APSInts ------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// diff --git a/lib/StaticAnalyzer/Core/AnalysisManager.cpp b/lib/StaticAnalyzer/Core/AnalysisManager.cpp index 7fb1c09ca049..1b1ffff5ade8 100644 --- a/lib/StaticAnalyzer/Core/AnalysisManager.cpp +++ b/lib/StaticAnalyzer/Core/AnalysisManager.cpp @@ -1,9 +1,8 @@ //===-- AnalysisManager.cpp -------------------------------------*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// @@ -24,7 +23,7 @@ AnalysisManager::AnalysisManager(ASTContext &ASTCtx, DiagnosticsEngine &diags, : AnaCtxMgr( ASTCtx, Options.UnoptimizedCFG, Options.ShouldIncludeImplicitDtorsInCFG, - /*AddInitializers=*/true, + /*addInitializers=*/true, Options.ShouldIncludeTemporaryDtorsInCFG, Options.ShouldIncludeLifetimeInCFG, // Adding LoopExit elements to the CFG is a requirement for loop @@ -36,7 +35,9 @@ AnalysisManager::AnalysisManager(ASTContext &ASTCtx, DiagnosticsEngine &diags, Options.ShouldConditionalizeStaticInitializers, /*addCXXNewAllocator=*/true, Options.ShouldIncludeRichConstructorsInCFG, - Options.ShouldElideConstructors, injector), + Options.ShouldElideConstructors, + /*addVirtualBaseBranches=*/true, + injector), Ctx(ASTCtx), Diags(diags), LangOpts(ASTCtx.getLangOpts()), PathConsumers(PDC), CreateStoreMgr(storemgr), CreateConstraintMgr(constraintmgr), CheckerMgr(checkerMgr), diff --git a/lib/StaticAnalyzer/Core/AnalyzerOptions.cpp b/lib/StaticAnalyzer/Core/AnalyzerOptions.cpp index 0588c2bd3d35..71abe2ae6c0e 100644 --- a/lib/StaticAnalyzer/Core/AnalyzerOptions.cpp +++ b/lib/StaticAnalyzer/Core/AnalyzerOptions.cpp @@ -1,9 +1,8 @@ //===- AnalyzerOptions.cpp - Analysis Engine Options ----------------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// // @@ -20,6 +19,7 @@ #include "llvm/ADT/Twine.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/FileSystem.h" +#include "llvm/Support/FormattedStream.h" #include "llvm/Support/raw_ostream.h" #include <cassert> #include <cstddef> @@ -34,7 +34,7 @@ std::vector<StringRef> AnalyzerOptions::getRegisteredCheckers(bool IncludeExperimental /* = false */) { static const StringRef StaticAnalyzerChecks[] = { #define GET_CHECKERS -#define CHECKER(FULLNAME, CLASS, HELPTEXT, DOC_URI) \ +#define CHECKER(FULLNAME, CLASS, HELPTEXT, DOC_URI, IS_HIDDEN) \ FULLNAME, #include "clang/StaticAnalyzer/Checkers/Checkers.inc" #undef CHECKER @@ -49,6 +49,37 @@ AnalyzerOptions::getRegisteredCheckers(bool IncludeExperimental /* = false */) { return Result; } +void AnalyzerOptions::printFormattedEntry( + llvm::raw_ostream &Out, + std::pair<StringRef, StringRef> EntryDescPair, + size_t InitialPad, size_t EntryWidth, size_t MinLineWidth) { + + llvm::formatted_raw_ostream FOut(Out); + + const size_t PadForDesc = InitialPad + EntryWidth; + + FOut.PadToColumn(InitialPad) << EntryDescPair.first; + // If the buffer's length is greater then PadForDesc, print a newline. + if (FOut.getColumn() > PadForDesc) + FOut << '\n'; + + FOut.PadToColumn(PadForDesc); + + if (MinLineWidth == 0) { + FOut << EntryDescPair.second; + return; + } + + for (char C : EntryDescPair.second) { + if (FOut.getColumn() > MinLineWidth && C == ' ') { + FOut << '\n'; + FOut.PadToColumn(PadForDesc); + continue; + } + FOut << C; + } +} + ExplorationStrategyKind AnalyzerOptions::getExplorationStrategy() const { auto K = @@ -102,18 +133,13 @@ AnalyzerOptions::mayInlineCXXMemberFunction( return *K >= Param; } -StringRef AnalyzerOptions::getCheckerStringOption(StringRef OptionName, - StringRef DefaultVal, - const CheckerBase *C, +StringRef AnalyzerOptions::getCheckerStringOption(StringRef CheckerName, + StringRef OptionName, bool SearchInParents) const { - assert(C); - // Search for a package option if the option for the checker is not specified - // and search in parents is enabled. - StringRef CheckerName = C->getTagDescription(); - assert(!CheckerName.empty() && "Empty checker name! Make sure the checker object (including it's " "bases!) if fully initialized before calling this function!"); + ConfigTable::const_iterator E = Config.end(); do { ConfigTable::const_iterator I = @@ -122,35 +148,66 @@ StringRef AnalyzerOptions::getCheckerStringOption(StringRef OptionName, return StringRef(I->getValue()); size_t Pos = CheckerName.rfind('.'); if (Pos == StringRef::npos) - return DefaultVal; + break; + CheckerName = CheckerName.substr(0, Pos); } while (!CheckerName.empty() && SearchInParents); - return DefaultVal; + + llvm_unreachable("Unknown checker option! Did you call getChecker*Option " + "with incorrect parameters? User input must've been " + "verified by CheckerRegistry."); + + return ""; } -bool AnalyzerOptions::getCheckerBooleanOption(StringRef Name, bool DefaultVal, - const CheckerBase *C, +StringRef AnalyzerOptions::getCheckerStringOption(const ento::CheckerBase *C, + StringRef OptionName, + bool SearchInParents) const { + return getCheckerStringOption( + C->getTagDescription(), OptionName, SearchInParents); +} + +bool AnalyzerOptions::getCheckerBooleanOption(StringRef CheckerName, + StringRef OptionName, bool SearchInParents) const { - // FIXME: We should emit a warning here if the value is something other than - // "true", "false", or the empty string (meaning the default value), - // but the AnalyzerOptions doesn't have access to a diagnostic engine. - assert(C); - return llvm::StringSwitch<bool>( - getCheckerStringOption(Name, DefaultVal ? "true" : "false", C, + auto Ret = llvm::StringSwitch<llvm::Optional<bool>>( + getCheckerStringOption(CheckerName, OptionName, SearchInParents)) .Case("true", true) .Case("false", false) - .Default(DefaultVal); + .Default(None); + + assert(Ret && + "This option should be either 'true' or 'false', and should've been " + "validated by CheckerRegistry!"); + + return *Ret; +} + +bool AnalyzerOptions::getCheckerBooleanOption(const ento::CheckerBase *C, + StringRef OptionName, + bool SearchInParents) const { + return getCheckerBooleanOption( + C->getTagDescription(), OptionName, SearchInParents); } -int AnalyzerOptions::getCheckerIntegerOption(StringRef Name, int DefaultVal, - const CheckerBase *C, - bool SearchInParents) const { - int Ret = DefaultVal; - bool HasFailed = getCheckerStringOption(Name, std::to_string(DefaultVal), C, +int AnalyzerOptions::getCheckerIntegerOption(StringRef CheckerName, + StringRef OptionName, + bool SearchInParents) const { + int Ret = 0; + bool HasFailed = getCheckerStringOption(CheckerName, OptionName, SearchInParents) - .getAsInteger(10, Ret); - assert(!HasFailed && "analyzer-config option should be numeric"); + .getAsInteger(0, Ret); + assert(!HasFailed && + "This option should be numeric, and should've been validated by " + "CheckerRegistry!"); (void)HasFailed; return Ret; } + +int AnalyzerOptions::getCheckerIntegerOption(const ento::CheckerBase *C, + StringRef OptionName, + bool SearchInParents) const { + return getCheckerIntegerOption( + C->getTagDescription(), OptionName, SearchInParents); +} diff --git a/lib/StaticAnalyzer/Core/BasicValueFactory.cpp b/lib/StaticAnalyzer/Core/BasicValueFactory.cpp index d8ed6942de81..7cd48bf44374 100644 --- a/lib/StaticAnalyzer/Core/BasicValueFactory.cpp +++ b/lib/StaticAnalyzer/Core/BasicValueFactory.cpp @@ -1,9 +1,8 @@ //===- BasicValueFactory.cpp - Basic values for Path Sens analysis --------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// // @@ -232,9 +231,6 @@ BasicValueFactory::evalAPSInt(BinaryOperator::Opcode Op, // FIXME: This logic should probably go higher up, where we can // test these conditions symbolically. - if (V1.isSigned() && V1.isNegative()) - return nullptr; - if (V2.isSigned() && V2.isNegative()) return nullptr; @@ -243,8 +239,13 @@ BasicValueFactory::evalAPSInt(BinaryOperator::Opcode Op, if (Amt >= V1.getBitWidth()) return nullptr; - if (V1.isSigned() && Amt > V1.countLeadingZeros()) + if (!Ctx.getLangOpts().CPlusPlus2a) { + if (V1.isSigned() && V1.isNegative()) + return nullptr; + + if (V1.isSigned() && Amt > V1.countLeadingZeros()) return nullptr; + } return &getValue( V1.operator<<( (unsigned) Amt )); } diff --git a/lib/StaticAnalyzer/Core/BlockCounter.cpp b/lib/StaticAnalyzer/Core/BlockCounter.cpp index 8c99bd808494..e7ac6f1cfa00 100644 --- a/lib/StaticAnalyzer/Core/BlockCounter.cpp +++ b/lib/StaticAnalyzer/Core/BlockCounter.cpp @@ -1,9 +1,8 @@ //==- BlockCounter.h - ADT for counting block visits -------------*- C++ -*-// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// // diff --git a/lib/StaticAnalyzer/Core/BugReporter.cpp b/lib/StaticAnalyzer/Core/BugReporter.cpp index fd7f53210490..e5a0794f10e2 100644 --- a/lib/StaticAnalyzer/Core/BugReporter.cpp +++ b/lib/StaticAnalyzer/Core/BugReporter.cpp @@ -1,9 +1,8 @@ //===- BugReporter.cpp - Generate PathDiagnostics for bugs ----------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// // @@ -155,8 +154,6 @@ static void removeRedundantMsgs(PathPieces &path) { case PathDiagnosticPiece::Macro: removeRedundantMsgs(cast<PathDiagnosticMacroPiece>(*piece).subPieces); break; - case PathDiagnosticPiece::ControlFlow: - break; case PathDiagnosticPiece::Event: { if (i == N-1) break; @@ -176,7 +173,9 @@ static void removeRedundantMsgs(PathPieces &path) { } break; } + case PathDiagnosticPiece::ControlFlow: case PathDiagnosticPiece::Note: + case PathDiagnosticPiece::PopUp: break; } path.push_back(std::move(piece)); @@ -231,9 +230,8 @@ static bool removeUnneededCalls(PathPieces &pieces, BugReport *R, break; } case PathDiagnosticPiece::ControlFlow: - break; - case PathDiagnosticPiece::Note: + case PathDiagnosticPiece::PopUp: break; } @@ -243,6 +241,16 @@ static bool removeUnneededCalls(PathPieces &pieces, BugReport *R, return containsSomethingInteresting; } +/// Same logic as above to remove extra pieces. +static void removePopUpNotes(PathPieces &Path) { + for (unsigned int i = 0; i < Path.size(); ++i) { + auto Piece = std::move(Path.front()); + Path.pop_front(); + if (!isa<PathDiagnosticPopUpPiece>(*Piece)) + Path.push_back(std::move(Piece)); + } +} + /// Returns true if the given decl has been implicitly given a body, either by /// the analyzer or by the compiler proper. static bool hasImplicitBody(const Decl *D) { @@ -679,7 +687,7 @@ void generateMinimalDiagForBlockEdge(const ExplodedNode *N, BlockEdge BE, const LocationContext *LC = N->getLocationContext(); const CFGBlock *Src = BE.getSrc(); const CFGBlock *Dst = BE.getDst(); - const Stmt *T = Src->getTerminator(); + const Stmt *T = Src->getTerminatorStmt(); if (!T) return; @@ -1204,7 +1212,7 @@ static void generatePathDiagnosticsForNode(const ExplodedNode *N, const CFGBlock *BSrc = BE->getSrc(); ParentMap &PM = PDB.getParentMap(); - if (const Stmt *Term = BSrc->getTerminator()) { + 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)) { @@ -1247,11 +1255,11 @@ static void generatePathDiagnosticsForNode(const ExplodedNode *N, static std::unique_ptr<PathDiagnostic> generateEmptyDiagnosticForReport(BugReport *R, SourceManager &SM) { - BugType &BT = R->getBugType(); + const BugType &BT = R->getBugType(); return llvm::make_unique<PathDiagnostic>( R->getBugType().getCheckName(), R->getDeclWithIssue(), R->getBugType().getName(), R->getDescription(), - R->getShortDescription(/*Fallback=*/false), BT.getCategory(), + R->getShortDescription(/*UseFallback=*/false), BT.getCategory(), R->getUniqueingLocation(), R->getUniqueingDecl(), findExecutedLines(SM, R->getErrorNode())); } @@ -1370,8 +1378,7 @@ static void addContextEdges(PathPieces &pieces, SourceManager &SM, break; // If the source is in the same context, we're already good. - if (std::find(SrcContexts.begin(), SrcContexts.end(), DstContext) != - SrcContexts.end()) + if (llvm::find(SrcContexts, DstContext) != SrcContexts.end()) break; // Update the subexpression node to point to the context edge. @@ -1983,6 +1990,10 @@ static std::unique_ptr<PathDiagnostic> generatePathDiagnosticForConsumer( (void)stillHasNotes; } + // Remove pop-up notes if needed. + if (!Opts.ShouldAddPopUpNotes) + removePopUpNotes(PD->getMutablePieces()); + // Redirect all call pieces to have valid locations. adjustCallLocations(PD->getMutablePieces()); removePiecesWithInvalidLocations(PD->getMutablePieces()); @@ -2612,7 +2623,7 @@ std::pair<BugReport*, std::unique_ptr<VisitorsDiagnosticsTy>> findValidReport( // Register additional node visitors. R->addVisitor(llvm::make_unique<NilReceiverBRVisitor>()); R->addVisitor(llvm::make_unique<ConditionBRVisitor>()); - R->addVisitor(llvm::make_unique<CXXSelfAssignmentBRVisitor>()); + R->addVisitor(llvm::make_unique<TagVisitor>()); BugReporterContext BRC(Reporter, ErrorGraph.BackMap); @@ -2684,7 +2695,7 @@ GRBugReporter::generatePathDiagnostics( return Out; } -void BugReporter::Register(BugType *BT) { +void BugReporter::Register(const BugType *BT) { BugTypes = F.add(BugTypes, BT); } @@ -2718,7 +2729,7 @@ void BugReporter::emitReport(std::unique_ptr<BugReport> R) { R->Profile(ID); // Lookup the equivance class. If there isn't one, create it. - BugType& BT = R->getBugType(); + const BugType& BT = R->getBugType(); Register(&BT); void *InsertPos; BugReportEquivClass* EQ = EQClasses.FindNodeOrInsertPos(ID, InsertPos); @@ -2836,7 +2847,7 @@ FindReportInEquivalenceClass(BugReportEquivClass& EQ, SmallVectorImpl<BugReport*> &bugReports) { BugReportEquivClass::iterator I = EQ.begin(), E = EQ.end(); assert(I != E); - BugType& BT = I->getBugType(); + const BugType& BT = I->getBugType(); // 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 diff --git a/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp b/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp index da94b6eb21e9..250793c4baff 100644 --- a/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp +++ b/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp @@ -1,9 +1,8 @@ //===- BugReporterVisitors.cpp - Helpers for reporting bugs ---------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// // @@ -23,6 +22,7 @@ #include "clang/AST/Stmt.h" #include "clang/AST/Type.h" #include "clang/ASTMatchers/ASTMatchFinder.h" +#include "clang/Analysis/Analyses/Dominators.h" #include "clang/Analysis/AnalysisDeclContext.h" #include "clang/Analysis/CFG.h" #include "clang/Analysis/CFGStmtMap.h" @@ -154,35 +154,47 @@ const Expr *bugreporter::getDerefExpr(const Stmt *S) { return E; } -//===----------------------------------------------------------------------===// -// Definitions for bug reporter visitors. -//===----------------------------------------------------------------------===// +/// Comparing internal representations of symbolic values (via +/// SVal::operator==()) is a valid way to check if the value was updated, +/// unless it's a LazyCompoundVal that may have a different internal +/// representation every time it is loaded from the state. In this function we +/// do an approximate comparison for lazy compound values, checking that they +/// are the immediate snapshots of the tracked region's bindings within the +/// node's respective states but not really checking that these snapshots +/// actually contain the same set of bindings. +static bool hasVisibleUpdate(const ExplodedNode *LeftNode, SVal LeftVal, + const ExplodedNode *RightNode, SVal RightVal) { + if (LeftVal == RightVal) + return true; -std::shared_ptr<PathDiagnosticPiece> -BugReporterVisitor::getEndPath(BugReporterContext &, - const ExplodedNode *, BugReport &) { - return nullptr; -} + const auto LLCV = LeftVal.getAs<nonloc::LazyCompoundVal>(); + if (!LLCV) + return false; -void -BugReporterVisitor::finalizeVisitor(BugReporterContext &, - const ExplodedNode *, BugReport &) {} + const auto RLCV = RightVal.getAs<nonloc::LazyCompoundVal>(); + if (!RLCV) + return false; -std::shared_ptr<PathDiagnosticPiece> BugReporterVisitor::getDefaultEndPath( - BugReporterContext &BRC, const ExplodedNode *EndPathNode, BugReport &BR) { - PathDiagnosticLocation L = - PathDiagnosticLocation::createEndOfPath(EndPathNode,BRC.getSourceManager()); + return LLCV->getRegion() == RLCV->getRegion() && + LLCV->getStore() == LeftNode->getState()->getStore() && + RLCV->getStore() == RightNode->getState()->getStore(); +} - const auto &Ranges = BR.getRanges(); +static Optional<const llvm::APSInt *> +getConcreteIntegerValue(const Expr *CondVarExpr, const ExplodedNode *N) { + ProgramStateRef State = N->getState(); + const LocationContext *LCtx = N->getLocationContext(); - // Only add the statement itself as a range if we didn't specify any - // special ranges for this report. - auto P = std::make_shared<PathDiagnosticEventPiece>( - L, BR.getDescription(), Ranges.begin() == Ranges.end()); - for (SourceRange Range : Ranges) - P->addRange(Range); + // 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(); + } + } - return P; + return {}; } /// \return name of the macro inside the location \p Loc. @@ -209,36 +221,70 @@ static bool isFunctionMacroExpansion(SourceLocation Loc, } /// \return Whether \c RegionOfInterest was modified at \p N, -/// where \p ReturnState is a state associated with the return -/// from the current frame. -static bool wasRegionOfInterestModifiedAt( - const SubRegion *RegionOfInterest, - const ExplodedNode *N, - SVal ValueAfter) { +/// where \p ValueAfter is \c RegionOfInterest's value at the end of the +/// stack frame. +static bool wasRegionOfInterestModifiedAt(const SubRegion *RegionOfInterest, + const ExplodedNode *N, + SVal ValueAfter) { ProgramStateRef State = N->getState(); ProgramStateManager &Mgr = N->getState()->getStateManager(); - if (!N->getLocationAs<PostStore>() - && !N->getLocationAs<PostInitializer>() - && !N->getLocationAs<PostStmt>()) + if (!N->getLocationAs<PostStore>() && !N->getLocationAs<PostInitializer>() && + !N->getLocationAs<PostStmt>()) return false; // Writing into region of interest. if (auto PS = N->getLocationAs<PostStmt>()) if (auto *BO = PS->getStmtAs<BinaryOperator>()) if (BO->isAssignmentOp() && RegionOfInterest->isSubRegionOf( - N->getSVal(BO->getLHS()).getAsRegion())) + N->getSVal(BO->getLHS()).getAsRegion())) return true; // SVal after the state is possibly different. SVal ValueAtN = N->getState()->getSVal(RegionOfInterest); - if (!Mgr.getSValBuilder().areEqual(State, ValueAtN, ValueAfter).isConstrainedTrue() && + if (!Mgr.getSValBuilder() + .areEqual(State, ValueAtN, ValueAfter) + .isConstrainedTrue() && (!ValueAtN.isUndef() || !ValueAfter.isUndef())) return true; return false; } +//===----------------------------------------------------------------------===// +// Implementation of BugReporterVisitor. +//===----------------------------------------------------------------------===// + +std::shared_ptr<PathDiagnosticPiece> +BugReporterVisitor::getEndPath(BugReporterContext &, + const ExplodedNode *, BugReport &) { + 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()); + + const auto &Ranges = BR.getRanges(); + + // Only add the statement itself as a range if we didn't specify any + // special ranges for this report. + auto P = std::make_shared<PathDiagnosticEventPiece>( + L, BR.getDescription(), Ranges.begin() == Ranges.end()); + for (SourceRange Range : Ranges) + P->addRange(Range); + + return P; +} + +//===----------------------------------------------------------------------===// +// Implementation of NoStoreFuncVisitor. +//===----------------------------------------------------------------------===// namespace { @@ -269,6 +315,7 @@ class NoStoreFuncVisitor final : public BugReporterVisitor { llvm::SmallPtrSet<const StackFrameContext *, 32> FramesModifyingCalculated; using RegionVector = SmallVector<const MemRegion *, 5>; + public: NoStoreFuncVisitor(const SubRegion *R) : RegionOfInterest(R), MmrMgr(*R->getMemRegionManager()), @@ -281,354 +328,418 @@ public: ID.AddPointer(RegionOfInterest); } + void *getTag() const { + static int Tag = 0; + return static_cast<void *>(&Tag); + } + std::shared_ptr<PathDiagnosticPiece> VisitNode(const ExplodedNode *N, BugReporterContext &BR, - BugReport &) override { + BugReport &R) override; +private: + /// Attempts to find the region of interest in a given record decl, + /// by either following the base classes or fields. + /// Dereferences fields up to a given recursion limit. + /// Note that \p Vec is passed by value, leading to quadratic copying cost, + /// but it's OK in practice since its length is limited to DEREFERENCE_LIMIT. + /// \return A chain fields leading to the region of interest or None. + const Optional<RegionVector> + findRegionOfInterestInRecord(const RecordDecl *RD, ProgramStateRef State, + const MemRegion *R, const RegionVector &Vec = {}, + int depth = 0); + + /// Check and lazily calculate whether the region of interest is + /// modified in the stack frame to which \p N belongs. + /// The calculation is cached in FramesModifyingRegion. + bool isRegionOfInterestModifiedInFrame(const ExplodedNode *N) { const LocationContext *Ctx = N->getLocationContext(); const StackFrameContext *SCtx = Ctx->getStackFrame(); - ProgramStateRef State = N->getState(); - auto CallExitLoc = N->getLocationAs<CallExitBegin>(); + if (!FramesModifyingCalculated.count(SCtx)) + findModifyingFrames(N); + return FramesModifyingRegion.count(SCtx); + } - // No diagnostic if region was modified inside the frame. - if (!CallExitLoc || isRegionOfInterestModifiedInFrame(N)) - return nullptr; + /// Write to \c FramesModifyingRegion all stack frames along + /// the path in the current stack frame which modify \c RegionOfInterest. + void findModifyingFrames(const ExplodedNode *N); - CallEventRef<> Call = - BR.getStateManager().getCallEventManager().getCaller(SCtx, State); + /// Consume the information on the no-store stack frame in order to + /// 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); - if (SM.isInSystemHeader(Call->getDecl()->getSourceRange().getBegin())) - return nullptr; + /// Pretty-print region \p MatchedRegion to \p os. + /// \return Whether printing succeeded. + bool prettyPrintRegionName(StringRef FirstElement, bool FirstIsReferenceType, + const MemRegion *MatchedRegion, + const RegionVector &FieldChain, + int IndirectionLevel, + llvm::raw_svector_ostream &os); - // Region of interest corresponds to an IVar, exiting a method - // which could have written into that IVar, but did not. - if (const auto *MC = dyn_cast<ObjCMethodCall>(Call)) { - if (const auto *IvarR = dyn_cast<ObjCIvarRegion>(RegionOfInterest)) { - const MemRegion *SelfRegion = MC->getReceiverSVal().getAsRegion(); - if (RegionOfInterest->isSubRegionOf(SelfRegion) && - potentiallyWritesIntoIvar(Call->getRuntimeDefinition().getDecl(), - IvarR->getDecl())) - return notModifiedDiagnostics(Ctx, *CallExitLoc, Call, {}, SelfRegion, - "self", /*FirstIsReferenceType=*/false, - 1); - } - } + /// Print first item in the chain, return new separator. + static StringRef prettyPrintFirstElement(StringRef FirstElement, + bool MoreItemsExpected, + int IndirectionLevel, + llvm::raw_svector_ostream &os); +}; - if (const auto *CCall = dyn_cast<CXXConstructorCall>(Call)) { - const MemRegion *ThisR = CCall->getCXXThisVal().getAsRegion(); - if (RegionOfInterest->isSubRegionOf(ThisR) - && !CCall->getDecl()->isImplicit()) - return notModifiedDiagnostics(Ctx, *CallExitLoc, Call, {}, ThisR, - "this", - /*FirstIsReferenceType=*/false, 1); +} // end of anonymous namespace - // Do not generate diagnostics for not modified parameters in - // constructors. - return nullptr; - } +/// \return Whether the method declaration \p Parent +/// syntactically has a binary operation writing into the ivar \p Ivar. +static bool potentiallyWritesIntoIvar(const Decl *Parent, + const ObjCIvarDecl *Ivar) { + using namespace ast_matchers; + const char *IvarBind = "Ivar"; + if (!Parent || !Parent->hasBody()) + return false; + StatementMatcher WriteIntoIvarM = binaryOperator( + hasOperatorName("="), + hasLHS(ignoringParenImpCasts( + objcIvarRefExpr(hasDeclaration(equalsNode(Ivar))).bind(IvarBind)))); + StatementMatcher ParentM = stmt(hasDescendant(WriteIntoIvarM)); + auto Matches = match(ParentM, *Parent->getBody(), Parent->getASTContext()); + for (BoundNodes &Match : Matches) { + auto IvarRef = Match.getNodeAs<ObjCIvarRefExpr>(IvarBind); + if (IvarRef->isFreeIvar()) + return true; - ArrayRef<ParmVarDecl *> parameters = getCallParameters(Call); - for (unsigned I = 0; I < Call->getNumArgs() && I < parameters.size(); ++I) { - const ParmVarDecl *PVD = parameters[I]; - SVal S = Call->getArgSVal(I); - bool ParamIsReferenceType = PVD->getType()->isReferenceType(); - std::string ParamName = PVD->getNameAsString(); - - int IndirectionLevel = 1; - QualType T = PVD->getType(); - while (const MemRegion *R = S.getAsRegion()) { - if (RegionOfInterest->isSubRegionOf(R) && !isPointerToConst(T)) - return notModifiedDiagnostics(Ctx, *CallExitLoc, Call, {}, R, - ParamName, ParamIsReferenceType, - IndirectionLevel); - - QualType PT = T->getPointeeType(); - if (PT.isNull() || PT->isVoidType()) break; - - if (const RecordDecl *RD = PT->getAsRecordDecl()) - if (auto P = findRegionOfInterestInRecord(RD, State, R)) - return notModifiedDiagnostics( - Ctx, *CallExitLoc, Call, *P, RegionOfInterest, ParamName, - ParamIsReferenceType, IndirectionLevel); - - S = State->getSVal(R, PT); - T = PT; - IndirectionLevel++; - } - } + const Expr *Base = IvarRef->getBase(); + if (const auto *ICE = dyn_cast<ImplicitCastExpr>(Base)) + Base = ICE->getSubExpr(); - return nullptr; + if (const auto *DRE = dyn_cast<DeclRefExpr>(Base)) + if (const auto *ID = dyn_cast<ImplicitParamDecl>(DRE->getDecl())) + if (ID->getParameterKind() == ImplicitParamDecl::ObjCSelf) + return true; + + return false; } + return false; +} -private: - /// Attempts to find the region of interest in a given CXX decl, - /// by either following the base classes or fields. - /// Dereferences fields up to a given recursion limit. - /// Note that \p Vec is passed by value, leading to quadratic copying cost, - /// but it's OK in practice since its length is limited to DEREFERENCE_LIMIT. - /// \return A chain fields leading to the region of interest or None. - const Optional<RegionVector> - findRegionOfInterestInRecord(const RecordDecl *RD, ProgramStateRef State, - const MemRegion *R, - const RegionVector &Vec = {}, - int depth = 0) { +/// Get parameters associated with runtime definition in order +/// to get the correct parameter name. +static ArrayRef<ParmVarDecl *> getCallParameters(CallEventRef<> Call) { + // Use runtime definition, if available. + RuntimeDefinition RD = Call->getRuntimeDefinition(); + if (const auto *FD = dyn_cast_or_null<FunctionDecl>(RD.getDecl())) + return FD->parameters(); + if (const auto *MD = dyn_cast_or_null<ObjCMethodDecl>(RD.getDecl())) + return MD->parameters(); + + return Call->parameters(); +} - if (depth == DEREFERENCE_LIMIT) // Limit the recursion depth. +/// \return whether \p Ty points to a const type, or is a const reference. +static bool isPointerToConst(QualType Ty) { + return !Ty->getPointeeType().isNull() && + Ty->getPointeeType().getCanonicalType().isConstQualified(); +} + +/// Attempts to find the region of interest in a given CXX decl, +/// by either following the base classes or fields. +/// Dereferences fields up to a given recursion limit. +/// Note that \p Vec is passed by value, leading to quadratic copying cost, +/// but it's OK in practice since its length is limited to DEREFERENCE_LIMIT. +/// \return A chain fields leading to the region of interest or None. +const Optional<NoStoreFuncVisitor::RegionVector> +NoStoreFuncVisitor::findRegionOfInterestInRecord( + const RecordDecl *RD, ProgramStateRef State, const MemRegion *R, + const NoStoreFuncVisitor::RegionVector &Vec /* = {} */, + int depth /* = 0 */) { + + if (depth == DEREFERENCE_LIMIT) // Limit the recursion depth. + return None; + + if (const auto *RDX = dyn_cast<CXXRecordDecl>(RD)) + if (!RDX->hasDefinition()) return None; - if (const auto *RDX = dyn_cast<CXXRecordDecl>(RD)) - if (!RDX->hasDefinition()) - return None; - - // Recursively examine the base classes. - // Note that following base classes does not increase the recursion depth. - if (const auto *RDX = dyn_cast<CXXRecordDecl>(RD)) - for (const auto II : RDX->bases()) - if (const RecordDecl *RRD = II.getType()->getAsRecordDecl()) - if (auto Out = findRegionOfInterestInRecord(RRD, State, R, Vec, depth)) - return Out; - - for (const FieldDecl *I : RD->fields()) { - QualType FT = I->getType(); - const FieldRegion *FR = MmrMgr.getFieldRegion(I, cast<SubRegion>(R)); - const SVal V = State->getSVal(FR); - const MemRegion *VR = V.getAsRegion(); - - RegionVector VecF = Vec; - VecF.push_back(FR); - - if (RegionOfInterest == VR) - return VecF; - - if (const RecordDecl *RRD = FT->getAsRecordDecl()) - if (auto Out = - findRegionOfInterestInRecord(RRD, State, FR, VecF, depth + 1)) + // Recursively examine the base classes. + // Note that following base classes does not increase the recursion depth. + if (const auto *RDX = dyn_cast<CXXRecordDecl>(RD)) + for (const auto II : RDX->bases()) + if (const RecordDecl *RRD = II.getType()->getAsRecordDecl()) + if (Optional<RegionVector> Out = + findRegionOfInterestInRecord(RRD, State, R, Vec, depth)) return Out; - QualType PT = FT->getPointeeType(); - if (PT.isNull() || PT->isVoidType() || !VR) continue; + for (const FieldDecl *I : RD->fields()) { + QualType FT = I->getType(); + const FieldRegion *FR = MmrMgr.getFieldRegion(I, cast<SubRegion>(R)); + const SVal V = State->getSVal(FR); + const MemRegion *VR = V.getAsRegion(); - if (const RecordDecl *RRD = PT->getAsRecordDecl()) - if (auto Out = - findRegionOfInterestInRecord(RRD, State, VR, VecF, depth + 1)) - return Out; + RegionVector VecF = Vec; + VecF.push_back(FR); - } + if (RegionOfInterest == VR) + return VecF; - return None; + if (const RecordDecl *RRD = FT->getAsRecordDecl()) + if (auto Out = + findRegionOfInterestInRecord(RRD, State, FR, VecF, depth + 1)) + return Out; + + QualType PT = FT->getPointeeType(); + if (PT.isNull() || PT->isVoidType() || !VR) + continue; + + if (const RecordDecl *RRD = PT->getAsRecordDecl()) + if (Optional<RegionVector> Out = + findRegionOfInterestInRecord(RRD, State, VR, VecF, depth + 1)) + return Out; } - /// \return Whether the method declaration \p Parent - /// syntactically has a binary operation writing into the ivar \p Ivar. - bool potentiallyWritesIntoIvar(const Decl *Parent, - const ObjCIvarDecl *Ivar) { - using namespace ast_matchers; - const char * IvarBind = "Ivar"; - if (!Parent || !Parent->hasBody()) - return false; - StatementMatcher WriteIntoIvarM = binaryOperator( - hasOperatorName("="), - hasLHS(ignoringParenImpCasts( - objcIvarRefExpr(hasDeclaration(equalsNode(Ivar))).bind(IvarBind)))); - StatementMatcher ParentM = stmt(hasDescendant(WriteIntoIvarM)); - auto Matches = match(ParentM, *Parent->getBody(), Parent->getASTContext()); - for (BoundNodes &Match : Matches) { - auto IvarRef = Match.getNodeAs<ObjCIvarRefExpr>(IvarBind); - if (IvarRef->isFreeIvar()) - return true; + return None; +} + +std::shared_ptr<PathDiagnosticPiece> +NoStoreFuncVisitor::VisitNode(const ExplodedNode *N, BugReporterContext &BR, + BugReport &R) { - const Expr *Base = IvarRef->getBase(); - if (const auto *ICE = dyn_cast<ImplicitCastExpr>(Base)) - Base = ICE->getSubExpr(); + const LocationContext *Ctx = N->getLocationContext(); + const StackFrameContext *SCtx = Ctx->getStackFrame(); + ProgramStateRef State = N->getState(); + auto CallExitLoc = N->getLocationAs<CallExitBegin>(); - if (const auto *DRE = dyn_cast<DeclRefExpr>(Base)) - if (const auto *ID = dyn_cast<ImplicitParamDecl>(DRE->getDecl())) - if (ID->getParameterKind() == ImplicitParamDecl::ObjCSelf) - return true; + // No diagnostic if region was modified inside the frame. + if (!CallExitLoc || isRegionOfInterestModifiedInFrame(N)) + return nullptr; - return false; + CallEventRef<> Call = + BR.getStateManager().getCallEventManager().getCaller(SCtx, State); + + // Region of interest corresponds to an IVar, exiting a method + // which could have written into that IVar, but did not. + if (const auto *MC = dyn_cast<ObjCMethodCall>(Call)) { + if (const auto *IvarR = dyn_cast<ObjCIvarRegion>(RegionOfInterest)) { + const MemRegion *SelfRegion = MC->getReceiverSVal().getAsRegion(); + if (RegionOfInterest->isSubRegionOf(SelfRegion) && + potentiallyWritesIntoIvar(Call->getRuntimeDefinition().getDecl(), + IvarR->getDecl())) + return maybeEmitNote(R, *Call, N, {}, SelfRegion, "self", + /*FirstIsReferenceType=*/false, 1); } - return false; } - /// Check and lazily calculate whether the region of interest is - /// modified in the stack frame to which \p N belongs. - /// The calculation is cached in FramesModifyingRegion. - bool isRegionOfInterestModifiedInFrame(const ExplodedNode *N) { - const LocationContext *Ctx = N->getLocationContext(); - const StackFrameContext *SCtx = Ctx->getStackFrame(); - if (!FramesModifyingCalculated.count(SCtx)) - findModifyingFrames(N); - return FramesModifyingRegion.count(SCtx); + if (const auto *CCall = dyn_cast<CXXConstructorCall>(Call)) { + const MemRegion *ThisR = CCall->getCXXThisVal().getAsRegion(); + if (RegionOfInterest->isSubRegionOf(ThisR) && + !CCall->getDecl()->isImplicit()) + return maybeEmitNote(R, *Call, N, {}, ThisR, "this", + /*FirstIsReferenceType=*/false, 1); + + // Do not generate diagnostics for not modified parameters in + // constructors. + return nullptr; } + ArrayRef<ParmVarDecl *> parameters = getCallParameters(Call); + for (unsigned I = 0; I < Call->getNumArgs() && I < parameters.size(); ++I) { + const ParmVarDecl *PVD = parameters[I]; + SVal V = Call->getArgSVal(I); + bool ParamIsReferenceType = PVD->getType()->isReferenceType(); + std::string ParamName = PVD->getNameAsString(); - /// Write to \c FramesModifyingRegion all stack frames along - /// the path in the current stack frame which modify \c RegionOfInterest. - void findModifyingFrames(const ExplodedNode *N) { - assert(N->getLocationAs<CallExitBegin>()); - ProgramStateRef LastReturnState = N->getState(); - SVal ValueAtReturn = LastReturnState->getSVal(RegionOfInterest); - const LocationContext *Ctx = N->getLocationContext(); - const StackFrameContext *OriginalSCtx = Ctx->getStackFrame(); + int IndirectionLevel = 1; + QualType T = PVD->getType(); + while (const MemRegion *MR = V.getAsRegion()) { + if (RegionOfInterest->isSubRegionOf(MR) && !isPointerToConst(T)) + return maybeEmitNote(R, *Call, N, {}, MR, ParamName, + ParamIsReferenceType, IndirectionLevel); - do { - ProgramStateRef State = N->getState(); - auto CallExitLoc = N->getLocationAs<CallExitBegin>(); - if (CallExitLoc) { - LastReturnState = State; - ValueAtReturn = LastReturnState->getSVal(RegionOfInterest); - } - - FramesModifyingCalculated.insert( - N->getLocationContext()->getStackFrame()); - - if (wasRegionOfInterestModifiedAt(RegionOfInterest, N, ValueAtReturn)) { - const StackFrameContext *SCtx = N->getStackFrame(); - while (!SCtx->inTopFrame()) { - auto p = FramesModifyingRegion.insert(SCtx); - if (!p.second) - break; // Frame and all its parents already inserted. - SCtx = SCtx->getParent()->getStackFrame(); - } - } + QualType PT = T->getPointeeType(); + if (PT.isNull() || PT->isVoidType()) + break; - // Stop calculation at the call to the current function. - if (auto CE = N->getLocationAs<CallEnter>()) - if (CE->getCalleeContext() == OriginalSCtx) - break; + if (const RecordDecl *RD = PT->getAsRecordDecl()) + if (Optional<RegionVector> P = + findRegionOfInterestInRecord(RD, State, MR)) + return maybeEmitNote(R, *Call, N, *P, RegionOfInterest, ParamName, + ParamIsReferenceType, IndirectionLevel); - N = N->getFirstPred(); - } while (N); + V = State->getSVal(MR, PT); + T = PT; + IndirectionLevel++; + } } - /// Get parameters associated with runtime definition in order - /// to get the correct parameter name. - ArrayRef<ParmVarDecl *> getCallParameters(CallEventRef<> Call) { - // Use runtime definition, if available. - RuntimeDefinition RD = Call->getRuntimeDefinition(); - if (const auto *FD = dyn_cast_or_null<FunctionDecl>(RD.getDecl())) - return FD->parameters(); - if (const auto *MD = dyn_cast_or_null<ObjCMethodDecl>(RD.getDecl())) - return MD->parameters(); + return nullptr; +} - return Call->parameters(); - } +void NoStoreFuncVisitor::findModifyingFrames(const ExplodedNode *N) { + assert(N->getLocationAs<CallExitBegin>()); + ProgramStateRef LastReturnState = N->getState(); + SVal ValueAtReturn = LastReturnState->getSVal(RegionOfInterest); + const LocationContext *Ctx = N->getLocationContext(); + const StackFrameContext *OriginalSCtx = Ctx->getStackFrame(); - /// \return whether \p Ty points to a const type, or is a const reference. - bool isPointerToConst(QualType Ty) { - return !Ty->getPointeeType().isNull() && - Ty->getPointeeType().getCanonicalType().isConstQualified(); - } + do { + ProgramStateRef State = N->getState(); + auto CallExitLoc = N->getLocationAs<CallExitBegin>(); + if (CallExitLoc) { + LastReturnState = State; + ValueAtReturn = LastReturnState->getSVal(RegionOfInterest); + } - /// \return Diagnostics piece for region not modified in the current function. - std::shared_ptr<PathDiagnosticPiece> - notModifiedDiagnostics(const LocationContext *Ctx, CallExitBegin &CallExitLoc, - CallEventRef<> Call, const RegionVector &FieldChain, - const MemRegion *MatchedRegion, StringRef FirstElement, - bool FirstIsReferenceType, unsigned IndirectionLevel) { - - PathDiagnosticLocation L; - if (const ReturnStmt *RS = CallExitLoc.getReturnStmt()) { - L = PathDiagnosticLocation::createBegin(RS, SM, Ctx); - } else { - L = PathDiagnosticLocation( - Call->getRuntimeDefinition().getDecl()->getSourceRange().getEnd(), - SM); + FramesModifyingCalculated.insert(N->getLocationContext()->getStackFrame()); + + if (wasRegionOfInterestModifiedAt(RegionOfInterest, N, ValueAtReturn)) { + const StackFrameContext *SCtx = N->getStackFrame(); + while (!SCtx->inTopFrame()) { + auto p = FramesModifyingRegion.insert(SCtx); + if (!p.second) + break; // Frame and all its parents already inserted. + SCtx = SCtx->getParent()->getStackFrame(); + } } - SmallString<256> sbuf; - llvm::raw_svector_ostream os(sbuf); - os << "Returning without writing to '"; + // Stop calculation at the call to the current function. + if (auto CE = N->getLocationAs<CallEnter>()) + if (CE->getCalleeContext() == OriginalSCtx) + break; - // Do not generate the note if failed to pretty-print. - if (!prettyPrintRegionName(FirstElement, FirstIsReferenceType, - MatchedRegion, FieldChain, IndirectionLevel, os)) - return nullptr; + N = N->getFirstPred(); + } while (N); +} - os << "'"; - return std::make_shared<PathDiagnosticEventPiece>(L, os.str()); +std::shared_ptr<PathDiagnosticPiece> NoStoreFuncVisitor::maybeEmitNote( + BugReport &R, const CallEvent &Call, const ExplodedNode *N, + const RegionVector &FieldChain, const MemRegion *MatchedRegion, + StringRef FirstElement, bool FirstIsReferenceType, + unsigned IndirectionLevel) { + // Optimistically suppress uninitialized value bugs that result + // from system headers having a chance to initialize the value + // but failing to do so. It's too unlikely a system header's fault. + // It's much more likely a situation in which the function has a failure + // mode that the user decided not to check. If we want to hunt such + // omitted checks, we should provide an explicit function-specific note + // describing the precondition under which the function isn't supposed to + // initialize its out-parameter, and additionally check that such + // precondition can actually be fulfilled on the current path. + if (Call.isInSystemHeader()) { + // We make an exception for system header functions that have no branches. + // Such functions unconditionally fail to initialize the variable. + // If they call other functions that have more paths within them, + // this suppression would still apply when we visit these inner functions. + // One common example of a standard function that doesn't ever initialize + // its out parameter is operator placement new; it's up to the follow-up + // constructor (if any) to initialize the memory. + if (!N->getStackFrame()->getCFG()->isLinear()) + R.markInvalid(getTag(), nullptr); + return nullptr; } - /// Pretty-print region \p MatchedRegion to \p os. - /// \return Whether printing succeeded. - bool prettyPrintRegionName(StringRef FirstElement, bool FirstIsReferenceType, - const MemRegion *MatchedRegion, - const RegionVector &FieldChain, - int IndirectionLevel, - llvm::raw_svector_ostream &os) { + PathDiagnosticLocation L = + PathDiagnosticLocation::create(N->getLocation(), SM); + + // For now this shouldn't trigger, but once it does (as we add more + // functions to the body farm), we'll need to decide if these reports + // are worth suppressing as well. + if (!L.hasValidLocation()) + return nullptr; - if (FirstIsReferenceType) - IndirectionLevel--; + SmallString<256> sbuf; + llvm::raw_svector_ostream os(sbuf); + os << "Returning without writing to '"; - RegionVector RegionSequence; + // Do not generate the note if failed to pretty-print. + if (!prettyPrintRegionName(FirstElement, FirstIsReferenceType, MatchedRegion, + FieldChain, IndirectionLevel, os)) + return nullptr; - // Add the regions in the reverse order, then reverse the resulting array. - assert(RegionOfInterest->isSubRegionOf(MatchedRegion)); - const MemRegion *R = RegionOfInterest; - while (R != MatchedRegion) { - RegionSequence.push_back(R); - R = cast<SubRegion>(R)->getSuperRegion(); - } - std::reverse(RegionSequence.begin(), RegionSequence.end()); - RegionSequence.append(FieldChain.begin(), FieldChain.end()); + os << "'"; + return std::make_shared<PathDiagnosticEventPiece>(L, os.str()); +} - StringRef Sep; - for (const MemRegion *R : RegionSequence) { +bool NoStoreFuncVisitor::prettyPrintRegionName(StringRef FirstElement, + bool FirstIsReferenceType, + const MemRegion *MatchedRegion, + const RegionVector &FieldChain, + int IndirectionLevel, + llvm::raw_svector_ostream &os) { - // Just keep going up to the base region. - // Element regions may appear due to casts. - if (isa<CXXBaseObjectRegion>(R) || isa<CXXTempObjectRegion>(R)) - continue; + if (FirstIsReferenceType) + IndirectionLevel--; - if (Sep.empty()) - Sep = prettyPrintFirstElement(FirstElement, - /*MoreItemsExpected=*/true, - IndirectionLevel, os); + RegionVector RegionSequence; - os << Sep; + // Add the regions in the reverse order, then reverse the resulting array. + assert(RegionOfInterest->isSubRegionOf(MatchedRegion)); + const MemRegion *R = RegionOfInterest; + while (R != MatchedRegion) { + RegionSequence.push_back(R); + R = cast<SubRegion>(R)->getSuperRegion(); + } + std::reverse(RegionSequence.begin(), RegionSequence.end()); + RegionSequence.append(FieldChain.begin(), FieldChain.end()); - // Can only reasonably pretty-print DeclRegions. - if (!isa<DeclRegion>(R)) - return false; + StringRef Sep; + for (const MemRegion *R : RegionSequence) { - const auto *DR = cast<DeclRegion>(R); - Sep = DR->getValueType()->isAnyPointerType() ? "->" : "."; - DR->getDecl()->getDeclName().print(os, PP); - } + // Just keep going up to the base region. + // Element regions may appear due to casts. + if (isa<CXXBaseObjectRegion>(R) || isa<CXXTempObjectRegion>(R)) + continue; if (Sep.empty()) - prettyPrintFirstElement(FirstElement, - /*MoreItemsExpected=*/false, IndirectionLevel, - os); - return true; - } + Sep = prettyPrintFirstElement(FirstElement, + /*MoreItemsExpected=*/true, + IndirectionLevel, os); - /// Print first item in the chain, return new separator. - StringRef prettyPrintFirstElement(StringRef FirstElement, - bool MoreItemsExpected, - int IndirectionLevel, - llvm::raw_svector_ostream &os) { - StringRef Out = "."; - - if (IndirectionLevel > 0 && MoreItemsExpected) { - IndirectionLevel--; - Out = "->"; - } + os << Sep; - if (IndirectionLevel > 0 && MoreItemsExpected) - os << "("; + // Can only reasonably pretty-print DeclRegions. + if (!isa<DeclRegion>(R)) + return false; - for (int i=0; i<IndirectionLevel; i++) - os << "*"; - os << FirstElement; + const auto *DR = cast<DeclRegion>(R); + Sep = DR->getValueType()->isAnyPointerType() ? "->" : "."; + DR->getDecl()->getDeclName().print(os, PP); + } - if (IndirectionLevel > 0 && MoreItemsExpected) - os << ")"; + if (Sep.empty()) + prettyPrintFirstElement(FirstElement, + /*MoreItemsExpected=*/false, IndirectionLevel, os); + return true; +} + +StringRef NoStoreFuncVisitor::prettyPrintFirstElement( + StringRef FirstElement, bool MoreItemsExpected, int IndirectionLevel, + llvm::raw_svector_ostream &os) { + StringRef Out = "."; - return Out; + if (IndirectionLevel > 0 && MoreItemsExpected) { + IndirectionLevel--; + Out = "->"; } -}; + + if (IndirectionLevel > 0 && MoreItemsExpected) + os << "("; + + for (int i = 0; i < IndirectionLevel; i++) + os << "*"; + os << FirstElement; + + if (IndirectionLevel > 0 && MoreItemsExpected) + os << ")"; + + return Out; +} + +//===----------------------------------------------------------------------===// +// Implementation of MacroNullReturnSuppressionVisitor. +//===----------------------------------------------------------------------===// + +namespace { /// Suppress null-pointer-dereference bugs where dereferenced null was returned /// the macro. @@ -718,6 +829,10 @@ private: } }; +} // end of anonymous namespace + +namespace { + /// Emits an extra note at the return statement of an interesting stack frame. /// /// The returned value is marked as an interesting value, and if it's null, @@ -769,16 +884,38 @@ public: return; // First, find when we processed the statement. + // If we work with a 'CXXNewExpr' that is going to be purged away before + // its call take place. We would catch that purge in the last condition + // as a 'StmtPoint' so we have to bypass it. + const bool BypassCXXNewExprEval = isa<CXXNewExpr>(S); + + // This is moving forward when we enter into another context. + const StackFrameContext *CurrentSFC = Node->getStackFrame(); + do { - if (auto CEE = Node->getLocationAs<CallExitEnd>()) + // If that is satisfied we found our statement as an inlined call. + if (Optional<CallExitEnd> CEE = Node->getLocationAs<CallExitEnd>()) if (CEE->getCalleeContext()->getCallSite() == S) break; - if (auto SP = Node->getLocationAs<StmtPoint>()) - if (SP->getStmt() == S) - break; + // Try to move forward to the end of the call-chain. Node = Node->getFirstPred(); - } while (Node); + if (!Node) + break; + + const StackFrameContext *PredSFC = Node->getStackFrame(); + + // If that is satisfied we found our statement. + // FIXME: This code currently bypasses the call site for the + // conservatively evaluated allocator. + if (!BypassCXXNewExprEval) + if (Optional<StmtPoint> SP = Node->getLocationAs<StmtPoint>()) + // See if we do not enter into another context. + if (SP->getStmt() == S && CurrentSFC == PredSFC) + break; + + CurrentSFC = PredSFC; + } while (Node->getStackFrame() == CurrentSFC); // Next, step over any post-statement checks. while (Node && Node->getLocation().getAs<PostStmt>()) @@ -993,7 +1130,11 @@ public: } }; -} // namespace +} // end of anonymous namespace + +//===----------------------------------------------------------------------===// +// Implementation of FindLastStoreBRVisitor. +//===----------------------------------------------------------------------===// void FindLastStoreBRVisitor::Profile(llvm::FoldingSetNodeID &ID) const { static int tag = 0; @@ -1188,7 +1329,7 @@ FindLastStoreBRVisitor::VisitNode(const ExplodedNode *Succ, if (Succ->getState()->getSVal(R) != V) return nullptr; - if (Pred->getState()->getSVal(R) == V) { + if (hasVisibleUpdate(Pred, Pred->getState()->getSVal(R), Succ, V)) { Optional<PostStore> PS = Succ->getLocationAs<PostStore>(); if (!PS || PS->getLocationValue() != R) return nullptr; @@ -1209,6 +1350,7 @@ FindLastStoreBRVisitor::VisitNode(const ExplodedNode *Succ, // UndefinedVal.) if (Optional<CallEnter> CE = Succ->getLocationAs<CallEnter>()) { if (const auto *VR = dyn_cast<VarRegion>(R)) { + const auto *Param = cast<ParmVarDecl>(VR->getDecl()); ProgramStateManager &StateMgr = BRC.getStateManager(); @@ -1302,6 +1444,10 @@ FindLastStoreBRVisitor::VisitNode(const ExplodedNode *Succ, return std::make_shared<PathDiagnosticEventPiece>(L, os.str()); } +//===----------------------------------------------------------------------===// +// Implementation of TrackConstraintBRVisitor. +//===----------------------------------------------------------------------===// + void TrackConstraintBRVisitor::Profile(llvm::FoldingSetNodeID &ID) const { static int tag = 0; ID.AddPointer(&tag); @@ -1374,6 +1520,10 @@ TrackConstraintBRVisitor::VisitNode(const ExplodedNode *N, return nullptr; } +//===----------------------------------------------------------------------===// +// Implementation of SuppressInlineDefensiveChecksVisitor. +//===----------------------------------------------------------------------===// + SuppressInlineDefensiveChecksVisitor:: SuppressInlineDefensiveChecksVisitor(DefinedSVal Value, const ExplodedNode *N) : V(Value) { @@ -1446,7 +1596,7 @@ SuppressInlineDefensiveChecksVisitor::VisitNode(const ExplodedNode *Succ, return nullptr; CFGStmtMap *Map = CurLC->getAnalysisDeclContext()->getCFGStmtMap(); - CurTerminatorStmt = Map->getBlock(CurStmt)->getTerminator(); + CurTerminatorStmt = Map->getBlock(CurStmt)->getTerminatorStmt(); } else { return nullptr; } @@ -1469,6 +1619,114 @@ SuppressInlineDefensiveChecksVisitor::VisitNode(const ExplodedNode *Succ, return nullptr; } +//===----------------------------------------------------------------------===// +// TrackControlDependencyCondBRVisitor. +//===----------------------------------------------------------------------===// + +namespace { +/// Tracks the expressions that are a control dependency of the node that was +/// supplied to the constructor. +/// For example: +/// +/// cond = 1; +/// if (cond) +/// 10 / 0; +/// +/// An error is emitted at line 3. This visitor realizes that the branch +/// on line 2 is a control dependency of line 3, and tracks it's condition via +/// trackExpressionValue(). +class TrackControlDependencyCondBRVisitor final : public BugReporterVisitor { + const ExplodedNode *Origin; + ControlDependencyCalculator ControlDeps; + llvm::SmallSet<const CFGBlock *, 32> VisitedBlocks; + +public: + TrackControlDependencyCondBRVisitor(const ExplodedNode *O) + : Origin(O), ControlDeps(&O->getCFG()) {} + + void Profile(llvm::FoldingSetNodeID &ID) const override { + static int x = 0; + ID.AddPointer(&x); + } + + std::shared_ptr<PathDiagnosticPiece> VisitNode(const ExplodedNode *N, + BugReporterContext &BRC, + BugReport &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, + BugReporterContext &BRC) { + + if (BRC.getAnalyzerOptions().AnalysisDiagOpt == PD_NONE || + !BRC.getAnalyzerOptions().ShouldTrackConditionsDebug) + return nullptr; + + std::string ConditionText = Lexer::getSourceText( + CharSourceRange::getTokenRange(Cond->getSourceRange()), + BRC.getSourceManager(), + BRC.getASTContext().getLangOpts()); + + return std::make_shared<PathDiagnosticEventPiece>( + PathDiagnosticLocation::createBegin( + Cond, BRC.getSourceManager(), N->getLocationContext()), + (Twine() + "Tracking condition '" + ConditionText + "'").str()); +} + +std::shared_ptr<PathDiagnosticPiece> +TrackControlDependencyCondBRVisitor::VisitNode(const ExplodedNode *N, + BugReporterContext &BRC, + BugReport &BR) { + // We can only reason about control dependencies within the same stack frame. + if (Origin->getStackFrame() != N->getStackFrame()) + return nullptr; + + CFGBlock *NB = GetRelevantBlock(N); + + // Skip if we already inspected this block. + if (!VisitedBlocks.insert(NB).second) + return nullptr; + + CFGBlock *OriginB = GetRelevantBlock(Origin); + + // TODO: Cache CFGBlocks for each ExplodedNode. + if (!OriginB || !NB) + return nullptr; + + if (ControlDeps.isControlDependent(OriginB, NB)) { + 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); + return constructDebugPieceForTrackedCondition(Condition, N, BRC); + } + } + } + + return nullptr; +} + +//===----------------------------------------------------------------------===// +// Implementation of trackExpressionValue. +//===----------------------------------------------------------------------===// + static const MemRegion *getLocationRegionIfReference(const Expr *E, const ExplodedNode *N) { if (const auto *DR = dyn_cast<DeclRefExpr>(E)) { @@ -1518,7 +1776,7 @@ static const Expr *peelOffOuterExpr(const Expr *Ex, ProgramPoint ProgPoint = NI->getLocation(); if (Optional<BlockEdge> BE = ProgPoint.getAs<BlockEdge>()) { const CFGBlock *srcBlk = BE->getSrc(); - if (const Stmt *term = srcBlk->getTerminator()) { + if (const Stmt *term = srcBlk->getTerminatorStmt()) { if (term == CO) { bool TookTrueBranch = (*(srcBlk->succ_begin()) == BE->getDst()); if (TookTrueBranch) @@ -1581,12 +1839,26 @@ bool bugreporter::trackExpressionValue(const ExplodedNode *InputNode, ProgramStateRef LVState = LVNode->getState(); + // We only track expressions if we believe that they are important. Chances + // are good that control dependencies to the tracking point are also improtant + // because of this, let's explain why we believe control reached this point. + // 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>( + 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); + // Track the index if this is an array subscript. + if (const auto *Arr = dyn_cast<ArraySubscriptExpr>(Inner)) + trackExpressionValue( + LVNode, Arr->getIdx(), report, /*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. if (ExplodedGraph::isInterestingLValueExpr(Inner)) { @@ -1688,6 +1960,10 @@ bool bugreporter::trackExpressionValue(const ExplodedNode *InputNode, return true; } +//===----------------------------------------------------------------------===// +// Implementation of NulReceiverBRVisitor. +//===----------------------------------------------------------------------===// + const Expr *NilReceiverBRVisitor::getNilReceiver(const Stmt *S, const ExplodedNode *N) { const auto *ME = dyn_cast<ObjCMessageExpr>(S); @@ -1738,6 +2014,10 @@ NilReceiverBRVisitor::VisitNode(const ExplodedNode *N, return std::make_shared<PathDiagnosticEventPiece>(L, OS.str()); } +//===----------------------------------------------------------------------===// +// Implementation of FindLastStoreBRVisitor. +//===----------------------------------------------------------------------===// + // Registers every VarDecl inside a Stmt with a last store visitor. void FindLastStoreBRVisitor::registerStatementVarDecls(BugReport &BR, const Stmt *S, @@ -1798,39 +2078,36 @@ ConditionBRVisitor::VisitNode(const ExplodedNode *N, std::shared_ptr<PathDiagnosticPiece> ConditionBRVisitor::VisitNodeImpl(const ExplodedNode *N, BugReporterContext &BRC, BugReport &BR) { - ProgramPoint progPoint = N->getLocation(); - ProgramStateRef CurrentState = N->getState(); - ProgramStateRef PrevState = N->getFirstPred()->getState(); - - // Compare the GDMs of the state, because that is where constraints - // are managed. Note that ensure that we only look at nodes that - // were generated by the analyzer engine proper, not checkers. - if (CurrentState->getGDM().getRoot() == - PrevState->getGDM().getRoot()) - return nullptr; + ProgramPoint ProgPoint = N->getLocation(); + const std::pair<const ProgramPointTag *, const ProgramPointTag *> &Tags = + ExprEngine::geteagerlyAssumeBinOpBifurcationTags(); // If an assumption was made on a branch, it should be caught // here by looking at the state transition. - if (Optional<BlockEdge> BE = progPoint.getAs<BlockEdge>()) { - const CFGBlock *srcBlk = BE->getSrc(); - if (const Stmt *term = srcBlk->getTerminator()) - return VisitTerminator(term, N, srcBlk, BE->getDst(), BR, BRC); + if (Optional<BlockEdge> BE = ProgPoint.getAs<BlockEdge>()) { + const CFGBlock *SrcBlock = BE->getSrc(); + if (const Stmt *Term = SrcBlock->getTerminatorStmt()) { + // If the tag of the previous node is 'Eagerly Assume...' the current + // 'BlockEdge' has the same constraint information. We do not want to + // report the value as it is just an assumption on the predecessor node + // which will be caught in the next VisitNode() iteration as a 'PostStmt'. + const ProgramPointTag *PreviousNodeTag = + N->getFirstPred()->getLocation().getTag(); + if (PreviousNodeTag == Tags.first || PreviousNodeTag == Tags.second) + return nullptr; + + return VisitTerminator(Term, N, SrcBlock, BE->getDst(), BR, BRC); + } return nullptr; } - if (Optional<PostStmt> PS = progPoint.getAs<PostStmt>()) { - const std::pair<const ProgramPointTag *, const ProgramPointTag *> &tags = - ExprEngine::geteagerlyAssumeBinOpBifurcationTags(); - - const ProgramPointTag *tag = PS->getTag(); - if (tag == tags.first) - return VisitTrueTest(cast<Expr>(PS->getStmt()), true, - BRC, BR, N); - if (tag == tags.second) - return VisitTrueTest(cast<Expr>(PS->getStmt()), false, - BRC, BR, N); + if (Optional<PostStmt> PS = ProgPoint.getAs<PostStmt>()) { + const ProgramPointTag *CurrentNodeTag = PS->getTag(); + if (CurrentNodeTag != Tags.first && CurrentNodeTag != Tags.second) + return nullptr; - return nullptr; + bool TookTrue = CurrentNodeTag == Tags.first; + return VisitTrueTest(cast<Expr>(PS->getStmt()), BRC, BR, N, TookTrue); } return nullptr; @@ -1876,6 +2153,8 @@ std::shared_ptr<PathDiagnosticPiece> ConditionBRVisitor::VisitTerminator( break; } + Cond = Cond->IgnoreParens(); + // However, when we encounter a logical operator as a branch condition, // then the condition is actually its RHS, because LHS would be // the condition for the logical operator terminator. @@ -1887,18 +2166,30 @@ std::shared_ptr<PathDiagnosticPiece> ConditionBRVisitor::VisitTerminator( assert(Cond); assert(srcBlk->succ_size() == 2); - const bool tookTrue = *(srcBlk->succ_begin()) == dstBlk; - return VisitTrueTest(Cond, tookTrue, BRC, R, N); + const bool TookTrue = *(srcBlk->succ_begin()) == dstBlk; + return VisitTrueTest(Cond, BRC, R, N, TookTrue); } std::shared_ptr<PathDiagnosticPiece> -ConditionBRVisitor::VisitTrueTest(const Expr *Cond, bool tookTrue, - BugReporterContext &BRC, BugReport &R, - const ExplodedNode *N) { +ConditionBRVisitor::VisitTrueTest(const Expr *Cond, BugReporterContext &BRC, + BugReport &R, const ExplodedNode *N, + bool TookTrue) { + ProgramStateRef CurrentState = N->getState(); + ProgramStateRef PrevState = N->getFirstPred()->getState(); + const LocationContext *LCtx = N->getLocationContext(); + + // If the constraint information is changed between the current and the + // previous program state we assuming the newly seen constraint information. + // If we cannot evaluate the condition (and the constraints are the same) + // the analyzer has no information about the value and just assuming it. + bool IsAssuming = + !BRC.getStateManager().haveEqualConstraints(CurrentState, PrevState) || + CurrentState->getSVal(Cond, LCtx).isUnknownOrUndef(); + // These will be modified in code below, but we need to preserve the original // values in case we want to throw the generic message. const Expr *CondTmp = Cond; - bool tookTrueTmp = tookTrue; + bool TookTrueTmp = TookTrue; while (true) { CondTmp = CondTmp->IgnoreParenCasts(); @@ -1907,18 +2198,23 @@ ConditionBRVisitor::VisitTrueTest(const Expr *Cond, bool tookTrue, break; case Stmt::BinaryOperatorClass: if (auto P = VisitTrueTest(Cond, cast<BinaryOperator>(CondTmp), - tookTrueTmp, BRC, R, N)) + BRC, R, N, TookTrueTmp, IsAssuming)) return P; break; case Stmt::DeclRefExprClass: if (auto P = VisitTrueTest(Cond, cast<DeclRefExpr>(CondTmp), - tookTrueTmp, BRC, R, N)) + BRC, R, N, TookTrueTmp, IsAssuming)) + return P; + break; + case Stmt::MemberExprClass: + if (auto P = VisitTrueTest(Cond, cast<MemberExpr>(CondTmp), + BRC, R, N, TookTrueTmp, IsAssuming)) return P; break; case Stmt::UnaryOperatorClass: { const auto *UO = cast<UnaryOperator>(CondTmp); if (UO->getOpcode() == UO_LNot) { - tookTrueTmp = !tookTrueTmp; + TookTrueTmp = !TookTrueTmp; CondTmp = UO->getSubExpr(); continue; } @@ -1930,13 +2226,17 @@ ConditionBRVisitor::VisitTrueTest(const Expr *Cond, bool tookTrue, // Condition too complex to explain? Just say something so that the user // knew we've made some path decision at this point. - const LocationContext *LCtx = N->getLocationContext(); + // If it is too complex and we know the evaluation of the condition do not + // repeat the note from 'BugReporter.cpp' + if (!IsAssuming) + return nullptr; + PathDiagnosticLocation Loc(Cond, BRC.getSourceManager(), LCtx); if (!Loc.isValid() || !Loc.asLocation().isValid()) return nullptr; return std::make_shared<PathDiagnosticEventPiece>( - Loc, tookTrue ? GenericTrueMessage : GenericFalseMessage); + Loc, TookTrue ? GenericTrueMessage : GenericFalseMessage); } bool ConditionBRVisitor::patternMatch(const Expr *Ex, @@ -1945,47 +2245,27 @@ bool ConditionBRVisitor::patternMatch(const Expr *Ex, BugReporterContext &BRC, BugReport &report, const ExplodedNode *N, - Optional<bool> &prunable) { + Optional<bool> &prunable, + bool IsSameFieldName) { const Expr *OriginalExpr = Ex; Ex = Ex->IgnoreParenCasts(); - // Use heuristics to determine if Ex is a macro expending to a literal and - // if so, use the macro's name. - SourceLocation LocStart = Ex->getBeginLoc(); - SourceLocation LocEnd = Ex->getEndLoc(); - if (LocStart.isMacroID() && LocEnd.isMacroID() && - (isa<GNUNullExpr>(Ex) || - isa<ObjCBoolLiteralExpr>(Ex) || - isa<CXXBoolLiteralExpr>(Ex) || - isa<IntegerLiteral>(Ex) || - isa<FloatingLiteral>(Ex))) { - StringRef StartName = Lexer::getImmediateMacroNameForDiagnostics(LocStart, - BRC.getSourceManager(), BRC.getASTContext().getLangOpts()); - StringRef EndName = Lexer::getImmediateMacroNameForDiagnostics(LocEnd, - BRC.getSourceManager(), BRC.getASTContext().getLangOpts()); - bool beginAndEndAreTheSameMacro = StartName.equals(EndName); - - bool partOfParentMacro = false; - if (ParentEx->getBeginLoc().isMacroID()) { - StringRef PName = Lexer::getImmediateMacroNameForDiagnostics( - ParentEx->getBeginLoc(), BRC.getSourceManager(), - BRC.getASTContext().getLangOpts()); - partOfParentMacro = PName.equals(StartName); - } - - if (beginAndEndAreTheSameMacro && !partOfParentMacro ) { - // Get the location of the macro name as written by the caller. - SourceLocation Loc = LocStart; - while (LocStart.isMacroID()) { - Loc = LocStart; - LocStart = BRC.getSourceManager().getImmediateMacroCallerLoc(LocStart); + if (isa<GNUNullExpr>(Ex) || isa<ObjCBoolLiteralExpr>(Ex) || + isa<CXXBoolLiteralExpr>(Ex) || isa<IntegerLiteral>(Ex) || + isa<FloatingLiteral>(Ex)) { + // Use heuristics to determine if the expression is a macro + // expanding to a literal and if so, use the macro's name. + SourceLocation BeginLoc = OriginalExpr->getBeginLoc(); + SourceLocation EndLoc = OriginalExpr->getEndLoc(); + if (BeginLoc.isMacroID() && EndLoc.isMacroID()) { + SourceManager &SM = BRC.getSourceManager(); + const LangOptions &LO = BRC.getASTContext().getLangOpts(); + if (Lexer::isAtStartOfMacroExpansion(BeginLoc, SM, LO) && + Lexer::isAtEndOfMacroExpansion(EndLoc, SM, LO)) { + CharSourceRange R = Lexer::getAsCharRange({BeginLoc, EndLoc}, SM, LO); + Out << Lexer::getSourceText(R, SM, LO); + return false; } - StringRef MacroName = Lexer::getImmediateMacroNameForDiagnostics( - Loc, BRC.getSourceManager(), BRC.getASTContext().getLangOpts()); - - // Return the macro name. - Out << MacroName; - return false; } } @@ -2032,23 +2312,43 @@ bool ConditionBRVisitor::patternMatch(const Expr *Ex, return false; } + if (const auto *ME = dyn_cast<MemberExpr>(Ex)) { + if (!IsSameFieldName) + Out << "field '" << ME->getMemberDecl()->getName() << '\''; + else + Out << '\'' + << Lexer::getSourceText( + CharSourceRange::getTokenRange(Ex->getSourceRange()), + BRC.getSourceManager(), BRC.getASTContext().getLangOpts(), 0) + << '\''; + } + return false; } -std::shared_ptr<PathDiagnosticPiece> -ConditionBRVisitor::VisitTrueTest(const Expr *Cond, const BinaryOperator *BExpr, - const bool tookTrue, BugReporterContext &BRC, - BugReport &R, const ExplodedNode *N) { +std::shared_ptr<PathDiagnosticPiece> ConditionBRVisitor::VisitTrueTest( + const Expr *Cond, const BinaryOperator *BExpr, BugReporterContext &BRC, + BugReport &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(); + SmallString<128> LhsString, RhsString; { llvm::raw_svector_ostream OutLHS(LhsString), OutRHS(RhsString); - const bool isVarLHS = patternMatch(BExpr->getLHS(), BExpr, OutLHS, - BRC, R, N, shouldPrune); - const bool isVarRHS = patternMatch(BExpr->getRHS(), BExpr, OutRHS, - BRC, R, N, shouldPrune); + const bool isVarLHS = patternMatch(BExpr->getLHS(), BExpr, OutLHS, BRC, R, + N, shouldPrune, IsSameFieldName); + const bool isVarRHS = patternMatch(BExpr->getRHS(), BExpr, OutRHS, BRC, R, + N, shouldPrune, IsSameFieldName); shouldInvert = !isVarLHS && isVarRHS; } @@ -2058,8 +2358,8 @@ ConditionBRVisitor::VisitTrueTest(const Expr *Cond, const BinaryOperator *BExpr, if (BinaryOperator::isAssignmentOp(Op)) { // For assignment operators, all that we care about is that the LHS // evaluates to "true" or "false". - return VisitConditionVariable(LhsString, BExpr->getLHS(), tookTrue, - BRC, R, N); + return VisitConditionVariable(LhsString, BExpr->getLHS(), BRC, R, N, + TookTrue); } // For non-assignment operations, we require that we can understand @@ -2071,7 +2371,8 @@ ConditionBRVisitor::VisitTrueTest(const Expr *Cond, const BinaryOperator *BExpr, // Should we invert the strings if the LHS is not a variable name? SmallString<256> buf; llvm::raw_svector_ostream Out(buf); - Out << "Assuming " << (shouldInvert ? RhsString : LhsString) << " is "; + Out << (IsAssuming ? "Assuming " : "") + << (shouldInvert ? RhsString : LhsString) << " is "; // Do we need to invert the opcode? if (shouldInvert) @@ -2083,7 +2384,7 @@ ConditionBRVisitor::VisitTrueTest(const Expr *Cond, const BinaryOperator *BExpr, case BO_GE: Op = BO_LE; break; } - if (!tookTrue) + if (!TookTrue) switch (Op) { case BO_EQ: Op = BO_NE; break; case BO_NE: Op = BO_EQ; break; @@ -2110,15 +2411,24 @@ ConditionBRVisitor::VisitTrueTest(const Expr *Cond, const BinaryOperator *BExpr, Out << (shouldInvert ? LhsString : RhsString); const LocationContext *LCtx = N->getLocationContext(); PathDiagnosticLocation Loc(Cond, BRC.getSourceManager(), LCtx); - auto event = std::make_shared<PathDiagnosticEventPiece>(Loc, Out.str()); + + // 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) + return std::make_shared<PathDiagnosticPopUpPiece>(Loc, Message); + + auto event = std::make_shared<PathDiagnosticEventPiece>(Loc, Message); if (shouldPrune.hasValue()) event->setPrunable(shouldPrune.getValue()); return event; } std::shared_ptr<PathDiagnosticPiece> ConditionBRVisitor::VisitConditionVariable( - StringRef LhsString, const Expr *CondVarExpr, const bool tookTrue, - BugReporterContext &BRC, BugReport &report, const ExplodedNode *N) { + StringRef LhsString, const Expr *CondVarExpr, BugReporterContext &BRC, + BugReport &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) @@ -2126,17 +2436,7 @@ std::shared_ptr<PathDiagnosticPiece> ConditionBRVisitor::VisitConditionVariable( llvm::raw_svector_ostream Out(buf); Out << "Assuming " << LhsString << " is "; - QualType Ty = CondVarExpr->getType(); - - if (Ty->isPointerType()) - Out << (tookTrue ? "not null" : "null"); - else if (Ty->isObjCObjectPointerType()) - Out << (tookTrue ? "not nil" : "nil"); - else if (Ty->isBooleanType()) - Out << (tookTrue ? "true" : "false"); - else if (Ty->isIntegralOrEnumerationType()) - Out << (tookTrue ? "non-zero" : "zero"); - else + if (!printValue(CondVarExpr, Out, N, TookTrue, /*IsAssuming=*/true)) return nullptr; const LocationContext *LCtx = N->getLocationContext(); @@ -2156,34 +2456,29 @@ std::shared_ptr<PathDiagnosticPiece> ConditionBRVisitor::VisitConditionVariable( return event; } -std::shared_ptr<PathDiagnosticPiece> -ConditionBRVisitor::VisitTrueTest(const Expr *Cond, const DeclRefExpr *DR, - const bool tookTrue, BugReporterContext &BRC, - BugReport &report, const ExplodedNode *N) { - const auto *VD = dyn_cast<VarDecl>(DR->getDecl()); +std::shared_ptr<PathDiagnosticPiece> ConditionBRVisitor::VisitTrueTest( + const Expr *Cond, const DeclRefExpr *DRE, BugReporterContext &BRC, + BugReport &report, const ExplodedNode *N, bool TookTrue, bool IsAssuming) { + const auto *VD = dyn_cast<VarDecl>(DRE->getDecl()); if (!VD) return nullptr; SmallString<256> Buf; llvm::raw_svector_ostream Out(Buf); - Out << "Assuming '" << VD->getDeclName() << "' is "; + Out << (IsAssuming ? "Assuming '" : "'") << VD->getDeclName() << "' is "; - QualType VDTy = VD->getType(); - - if (VDTy->isPointerType()) - Out << (tookTrue ? "non-null" : "null"); - else if (VDTy->isObjCObjectPointerType()) - Out << (tookTrue ? "non-nil" : "nil"); - else if (VDTy->isScalarType()) - Out << (tookTrue ? "not equal to 0" : "0"); - else + if (!printValue(DRE, Out, N, TookTrue, IsAssuming)) return nullptr; const LocationContext *LCtx = N->getLocationContext(); PathDiagnosticLocation Loc(Cond, BRC.getSourceManager(), LCtx); - auto event = std::make_shared<PathDiagnosticEventPiece>(Loc, Out.str()); + // If we know the value create a pop-up note. + if (!IsAssuming) + return std::make_shared<PathDiagnosticPopUpPiece>(Loc, Out.str()); + + 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)) @@ -2197,6 +2492,67 @@ ConditionBRVisitor::VisitTrueTest(const Expr *Cond, const DeclRefExpr *DR, return std::move(event); } +std::shared_ptr<PathDiagnosticPiece> ConditionBRVisitor::VisitTrueTest( + const Expr *Cond, const MemberExpr *ME, BugReporterContext &BRC, + BugReport &report, const ExplodedNode *N, bool TookTrue, bool IsAssuming) { + SmallString<256> Buf; + llvm::raw_svector_ostream Out(Buf); + + Out << (IsAssuming ? "Assuming field '" : "Field '") + << ME->getMemberDecl()->getName() << "' is "; + + if (!printValue(ME, Out, N, TookTrue, IsAssuming)) + return nullptr; + + const LocationContext *LCtx = N->getLocationContext(); + PathDiagnosticLocation Loc(Cond, BRC.getSourceManager(), LCtx); + if (!Loc.isValid() || !Loc.asLocation().isValid()) + return nullptr; + + // 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()); +} + +bool ConditionBRVisitor::printValue(const Expr *CondVarExpr, raw_ostream &Out, + const ExplodedNode *N, bool TookTrue, + bool IsAssuming) { + QualType Ty = CondVarExpr->getType(); + + if (Ty->isPointerType()) { + Out << (TookTrue ? "non-null" : "null"); + return true; + } + + if (Ty->isObjCObjectPointerType()) { + Out << (TookTrue ? "non-nil" : "nil"); + return true; + } + + if (!Ty->isIntegralOrEnumerationType()) + return false; + + Optional<const llvm::APSInt *> IntValue; + if (!IsAssuming) + IntValue = getConcreteIntegerValue(CondVarExpr, N); + + if (IsAssuming || !IntValue.hasValue()) { + if (Ty->isBooleanType()) + Out << (TookTrue ? "true" : "false"); + else + Out << (TookTrue ? "not equal to 0" : "0"); + } else { + if (Ty->isBooleanType()) + Out << (IntValue.getValue()->getBoolValue() ? "true" : "false"); + else + Out << *IntValue.getValue(); + } + + return true; +} + const char *const ConditionBRVisitor::GenericTrueMessage = "Assuming the condition is true"; const char *const ConditionBRVisitor::GenericFalseMessage = @@ -2208,6 +2564,10 @@ bool ConditionBRVisitor::isPieceMessageGeneric( Piece->getString() == GenericFalseMessage; } +//===----------------------------------------------------------------------===// +// Implementation of LikelyFalsePositiveSuppressionBRVisitor. +//===----------------------------------------------------------------------===// + void LikelyFalsePositiveSuppressionBRVisitor::finalizeVisitor( BugReporterContext &BRC, const ExplodedNode *N, BugReport &BR) { // Here we suppress false positives coming from system headers. This list is @@ -2290,6 +2650,10 @@ void LikelyFalsePositiveSuppressionBRVisitor::finalizeVisitor( } } +//===----------------------------------------------------------------------===// +// Implementation of UndefOrNullArgVisitor. +//===----------------------------------------------------------------------===// + std::shared_ptr<PathDiagnosticPiece> UndefOrNullArgVisitor::VisitNode(const ExplodedNode *N, BugReporterContext &BRC, BugReport &BR) { @@ -2340,78 +2704,9 @@ UndefOrNullArgVisitor::VisitNode(const ExplodedNode *N, return nullptr; } -std::shared_ptr<PathDiagnosticPiece> -CXXSelfAssignmentBRVisitor::VisitNode(const ExplodedNode *Succ, - BugReporterContext &BRC, BugReport &) { - if (Satisfied) - return nullptr; - - const auto Edge = Succ->getLocation().getAs<BlockEdge>(); - if (!Edge.hasValue()) - return nullptr; - - auto Tag = Edge->getTag(); - if (!Tag) - return nullptr; - - if (Tag->getTagDescription() != "cplusplus.SelfAssignment") - return nullptr; - - Satisfied = true; - - const auto *Met = - dyn_cast<CXXMethodDecl>(Succ->getCodeDecl().getAsFunction()); - assert(Met && "Not a C++ method."); - assert((Met->isCopyAssignmentOperator() || Met->isMoveAssignmentOperator()) && - "Not a copy/move assignment operator."); - - const auto *LCtx = Edge->getLocationContext(); - - const auto &State = Succ->getState(); - auto &SVB = State->getStateManager().getSValBuilder(); - - const auto Param = - State->getSVal(State->getRegion(Met->getParamDecl(0), LCtx)); - const auto This = - State->getSVal(SVB.getCXXThis(Met, LCtx->getStackFrame())); - - auto L = PathDiagnosticLocation::create(Met, BRC.getSourceManager()); - - if (!L.isValid() || !L.asLocation().isValid()) - return nullptr; - - SmallString<256> Buf; - llvm::raw_svector_ostream Out(Buf); - - Out << "Assuming " << Met->getParamDecl(0)->getName() << - ((Param == This) ? " == " : " != ") << "*this"; - - auto Piece = std::make_shared<PathDiagnosticEventPiece>(L, Out.str()); - Piece->addRange(Met->getSourceRange()); - - return std::move(Piece); -} - -std::shared_ptr<PathDiagnosticPiece> -TaintBugVisitor::VisitNode(const ExplodedNode *N, - BugReporterContext &BRC, BugReport &) { - - // Find the ExplodedNode where the taint was first introduced - if (!N->getState()->isTainted(V) || N->getFirstPred()->getState()->isTainted(V)) - return nullptr; - - const Stmt *S = PathDiagnosticLocation::getStmt(N); - if (!S) - return nullptr; - - const LocationContext *NCtx = N->getLocationContext(); - PathDiagnosticLocation L = - PathDiagnosticLocation::createBegin(S, BRC.getSourceManager(), NCtx); - if (!L.isValid() || !L.asLocation().isValid()) - return nullptr; - - return std::make_shared<PathDiagnosticEventPiece>(L, "Taint originated here"); -} +//===----------------------------------------------------------------------===// +// Implementation of FalsePositiveRefutationBRVisitor. +//===----------------------------------------------------------------------===// FalsePositiveRefutationBRVisitor::FalsePositiveRefutationBRVisitor() : Constraints(ConstraintRangeTy::Factory().getEmptyMap()) {} @@ -2422,7 +2717,7 @@ void FalsePositiveRefutationBRVisitor::finalizeVisitor( VisitNode(EndPathNode, BRC, BR); // Create a refutation manager - SMTSolverRef RefutationSolver = CreateZ3Solver(); + llvm::SMTSolverRef RefutationSolver = llvm::CreateZ3Solver(); ASTContext &Ctx = BRC.getASTContext(); // Add constraints to the solver @@ -2430,7 +2725,7 @@ void FalsePositiveRefutationBRVisitor::finalizeVisitor( const SymbolRef Sym = I.first; auto RangeIt = I.second.begin(); - SMTExprRef Constraints = SMTConv::getRangeExpr( + llvm::SMTExprRef Constraints = SMTConv::getRangeExpr( RefutationSolver, Ctx, Sym, RangeIt->From(), RangeIt->To(), /*InRange=*/true); while ((++RangeIt) != I.second.end()) { @@ -2477,3 +2772,33 @@ void FalsePositiveRefutationBRVisitor::Profile( static int Tag = 0; ID.AddPointer(&Tag); } + +//===----------------------------------------------------------------------===// +// Implementation of TagVisitor. +//===----------------------------------------------------------------------===// + +int NoteTag::Kind = 0; + +void TagVisitor::Profile(llvm::FoldingSetNodeID &ID) const { + static int Tag = 0; + ID.AddPointer(&Tag); +} + +std::shared_ptr<PathDiagnosticPiece> +TagVisitor::VisitNode(const ExplodedNode *N, BugReporterContext &BRC, + BugReport &R) { + ProgramPoint PP = N->getLocation(); + const NoteTag *T = dyn_cast_or_null<NoteTag>(PP.getTag()); + if (!T) + return nullptr; + + if (Optional<std::string> Msg = T->generateMessage(BRC, R)) { + PathDiagnosticLocation Loc = + PathDiagnosticLocation::create(PP, BRC.getSourceManager()); + auto Piece = std::make_shared<PathDiagnosticEventPiece>(Loc, *Msg); + Piece->setPrunable(T->isPrunable()); + return Piece; + } + + return nullptr; +} diff --git a/lib/StaticAnalyzer/Core/CallEvent.cpp b/lib/StaticAnalyzer/Core/CallEvent.cpp index 0e7f31502e81..a5f7500e6307 100644 --- a/lib/StaticAnalyzer/Core/CallEvent.cpp +++ b/lib/StaticAnalyzer/Core/CallEvent.cpp @@ -1,9 +1,8 @@ //===- CallEvent.cpp - Wrapper for all function and method calls ----------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// // @@ -357,20 +356,32 @@ bool CallEvent::isCalled(const CallDescription &CD) const { // FIXME: Add ObjC Message support. if (getKind() == CE_ObjCMessage) return false; + + const IdentifierInfo *II = getCalleeIdentifier(); + if (!II) + return false; + const FunctionDecl *FD = dyn_cast_or_null<FunctionDecl>(getDecl()); + if (!FD) + return false; + + if (CD.Flags & CDF_MaybeBuiltin) { + return CheckerContext::isCLibraryFunction(FD, CD.getFunctionName()) && + (!CD.RequiredArgs || CD.RequiredArgs <= getNumArgs()); + } + if (!CD.IsLookupDone) { CD.IsLookupDone = true; CD.II = &getState()->getStateManager().getContext().Idents.get( CD.getFunctionName()); } - const IdentifierInfo *II = getCalleeIdentifier(); - if (!II || II != CD.II) + + if (II != CD.II) return false; - const Decl *D = getDecl(); // If CallDescription provides prefix names, use them to improve matching // accuracy. - if (CD.QualifiedName.size() > 1 && D) { - const DeclContext *Ctx = D->getDeclContext(); + if (CD.QualifiedName.size() > 1 && FD) { + const DeclContext *Ctx = FD->getDeclContext(); // See if we'll be able to match them all. size_t NumUnmatched = CD.QualifiedName.size() - 1; for (; Ctx && isa<NamedDecl>(Ctx); Ctx = Ctx->getParent()) { @@ -394,8 +405,7 @@ bool CallEvent::isCalled(const CallDescription &CD) const { return false; } - return (CD.RequiredArgs == CallDescription::NoArgRequirement || - CD.RequiredArgs == getNumArgs()); + return (!CD.RequiredArgs || CD.RequiredArgs == getNumArgs()); } SVal CallEvent::getArgSVal(unsigned Index) const { @@ -756,8 +766,11 @@ RuntimeDefinition CXXInstanceCall::getRuntimeDefinition() const { // Does the decl that we found have an implementation? const FunctionDecl *Definition; - if (!Result->hasBody(Definition)) + if (!Result->hasBody(Definition)) { + if (!DynType.canBeASubClass()) + return AnyFunctionCall::getRuntimeDefinition(); return {}; + } // We found a definition. If we're not sure that this devirtualization is // actually what will happen at runtime, make sure to provide the region so diff --git a/lib/StaticAnalyzer/Core/Checker.cpp b/lib/StaticAnalyzer/Core/Checker.cpp index 72bfd84b40a3..f4e6f909d764 100644 --- a/lib/StaticAnalyzer/Core/Checker.cpp +++ b/lib/StaticAnalyzer/Core/Checker.cpp @@ -1,9 +1,8 @@ //== Checker.cpp - Registration mechanism for checkers -----------*- C++ -*--=// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// // diff --git a/lib/StaticAnalyzer/Core/CheckerContext.cpp b/lib/StaticAnalyzer/Core/CheckerContext.cpp index 6cf931abbddd..725ff1002e29 100644 --- a/lib/StaticAnalyzer/Core/CheckerContext.cpp +++ b/lib/StaticAnalyzer/Core/CheckerContext.cpp @@ -1,9 +1,8 @@ //== CheckerContext.cpp - Context info for path-sensitive checkers-----------=// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// // diff --git a/lib/StaticAnalyzer/Core/CheckerHelpers.cpp b/lib/StaticAnalyzer/Core/CheckerHelpers.cpp index e73a22ae3981..34cdc9db699d 100644 --- a/lib/StaticAnalyzer/Core/CheckerHelpers.cpp +++ b/lib/StaticAnalyzer/Core/CheckerHelpers.cpp @@ -1,9 +1,8 @@ //===---- CheckerHelpers.cpp - Helper functions for checkers ----*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// // diff --git a/lib/StaticAnalyzer/Core/CheckerManager.cpp b/lib/StaticAnalyzer/Core/CheckerManager.cpp index 688c47e984cc..27d5797b4cbc 100644 --- a/lib/StaticAnalyzer/Core/CheckerManager.cpp +++ b/lib/StaticAnalyzer/Core/CheckerManager.cpp @@ -1,9 +1,8 @@ //===- CheckerManager.cpp - Static Analyzer Checker Manager ---------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// // @@ -15,7 +14,9 @@ #include "clang/AST/DeclBase.h" #include "clang/AST/Stmt.h" #include "clang/Analysis/ProgramPoint.h" +#include "clang/Basic/JsonSupport.h" #include "clang/Basic/LLVM.h" +#include "clang/Driver/DriverDiagnostic.h" #include "clang/StaticAnalyzer/Core/Checker.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" @@ -59,6 +60,15 @@ void CheckerManager::finishedCheckerRegistration() { #endif } +void CheckerManager::reportInvalidCheckerOptionValue( + const CheckerBase *C, StringRef OptionName, StringRef ExpectedValueDesc) { + + Context.getDiagnostics() + .Report(diag::err_analyzer_checker_option_invalid_input) + << (llvm::Twine() + C->getTagDescription() + ":" + OptionName).str() + << ExpectedValueDesc; +} + //===----------------------------------------------------------------------===// // Functions for running checkers for AST traversing.. //===----------------------------------------------------------------------===// @@ -641,7 +651,6 @@ void CheckerManager::runCheckersForEvalCall(ExplodedNodeSet &Dst, const ExplodedNodeSet &Src, const CallEvent &Call, ExprEngine &Eng) { - const CallExpr *CE = cast<CallExpr>(Call.getOriginExpr()); for (const auto Pred : Src) { bool anyEvaluated = false; @@ -650,16 +659,19 @@ void CheckerManager::runCheckersForEvalCall(ExplodedNodeSet &Dst, // Check if any of the EvalCall callbacks can evaluate the call. for (const auto EvalCallChecker : EvalCallCheckers) { - ProgramPoint::Kind K = ProgramPoint::PostStmtKind; - const ProgramPoint &L = - ProgramPoint::getProgramPoint(CE, K, Pred->getLocationContext(), - EvalCallChecker.Checker); + // TODO: Support the situation when the call doesn't correspond + // to any Expr. + ProgramPoint L = ProgramPoint::getProgramPoint( + cast<CallExpr>(Call.getOriginExpr()), + ProgramPoint::PostStmtKind, + Pred->getLocationContext(), + EvalCallChecker.Checker); bool evaluated = false; { // CheckerContext generates transitions(populates checkDest) on // destruction, so introduce the scope to make sure it gets properly // populated. CheckerContext C(B, Eng, Pred, L); - evaluated = EvalCallChecker(CE, C); + evaluated = EvalCallChecker(Call, C); } assert(!(evaluated && anyEvaluated) && "There are more than one checkers evaluating the call"); @@ -689,11 +701,73 @@ void CheckerManager::runCheckersOnEndOfTranslationUnit( EndOfTranslationUnitChecker(TU, mgr, BR); } -void CheckerManager::runCheckersForPrintState(raw_ostream &Out, - ProgramStateRef State, - const char *NL, const char *Sep) { - for (const auto &CheckerTag : CheckerTags) - CheckerTag.second->printState(Out, State, NL, Sep); +void CheckerManager::runCheckersForPrintStateJson(raw_ostream &Out, + ProgramStateRef State, + const char *NL, + unsigned int Space, + bool IsDot) const { + Indent(Out, Space, IsDot) << "\"checker_messages\": "; + + // Create a temporary stream to see whether we have any message. + SmallString<1024> TempBuf; + llvm::raw_svector_ostream TempOut(TempBuf); + unsigned int InnerSpace = Space + 2; + + // Create the new-line in JSON with enough space. + SmallString<128> NewLine; + llvm::raw_svector_ostream NLOut(NewLine); + NLOut << "\", " << NL; // Inject the ending and a new line + Indent(NLOut, InnerSpace, IsDot) << "\""; // then begin the next message. + + ++Space; + bool HasMessage = false; + + // Store the last CheckerTag. + const void *LastCT = nullptr; + for (const auto &CT : CheckerTags) { + // See whether the current checker has a message. + CT.second->printState(TempOut, State, /*NL=*/NewLine.c_str(), /*Sep=*/""); + + if (TempBuf.empty()) + continue; + + if (!HasMessage) { + Out << '[' << NL; + HasMessage = true; + } + + LastCT = &CT; + TempBuf.clear(); + } + + for (const auto &CT : CheckerTags) { + // See whether the current checker has a message. + CT.second->printState(TempOut, State, /*NL=*/NewLine.c_str(), /*Sep=*/""); + + if (TempBuf.empty()) + continue; + + Indent(Out, Space, IsDot) + << "{ \"checker\": \"" << CT.second->getCheckName().getName() + << "\", \"messages\": [" << NL; + Indent(Out, InnerSpace, IsDot) + << '\"' << TempBuf.str().trim() << '\"' << NL; + Indent(Out, Space, IsDot) << "]}"; + + if (&CT != LastCT) + Out << ','; + Out << NL; + + TempBuf.clear(); + } + + // It is the last element of the 'program_state' so do not add a comma. + if (HasMessage) + Indent(Out, --Space, IsDot) << "]"; + else + Out << "null"; + + Out << NL; } //===----------------------------------------------------------------------===// diff --git a/lib/StaticAnalyzer/Core/CommonBugCategories.cpp b/lib/StaticAnalyzer/Core/CommonBugCategories.cpp index cdae3ef0116a..54501314386a 100644 --- a/lib/StaticAnalyzer/Core/CommonBugCategories.cpp +++ b/lib/StaticAnalyzer/Core/CommonBugCategories.cpp @@ -1,9 +1,8 @@ //=--- CommonBugCategories.cpp - Provides common issue categories -*- C++ -*-=// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// diff --git a/lib/StaticAnalyzer/Core/ConstraintManager.cpp b/lib/StaticAnalyzer/Core/ConstraintManager.cpp index ef9c44c51be4..d642c3530268 100644 --- a/lib/StaticAnalyzer/Core/ConstraintManager.cpp +++ b/lib/StaticAnalyzer/Core/ConstraintManager.cpp @@ -1,9 +1,8 @@ //===- ConstraintManager.cpp - Constraints on symbolic values. ------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// // diff --git a/lib/StaticAnalyzer/Core/CoreEngine.cpp b/lib/StaticAnalyzer/Core/CoreEngine.cpp index 196854cb09da..94cf74de8293 100644 --- a/lib/StaticAnalyzer/Core/CoreEngine.cpp +++ b/lib/StaticAnalyzer/Core/CoreEngine.cpp @@ -1,9 +1,8 @@ //===- CoreEngine.cpp - Path-Sensitive Dataflow Engine --------------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// // @@ -217,6 +216,25 @@ void CoreEngine::HandleBlockEdge(const BlockEdge &L, ExplodedNode *Pred) { LC->getDecl(), LC->getCFG()->getNumBlockIDs()); + // Display a prunable path note to the user if it's a virtual bases branch + // and we're taking the path that skips virtual base constructors. + if (L.getSrc()->getTerminator().isVirtualBaseBranch() && + L.getDst() == *L.getSrc()->succ_begin()) { + ProgramPoint P = L.withTag(getNoteTags().makeNoteTag( + [](BugReporterContext &, BugReport &) -> std::string { + // TODO: Just call out the name of the most derived class + // when we know it. + return "Virtual base initialization skipped because " + "it has already been handled by the most derived class"; + }, /*IsPrunable=*/true)); + // Perform the transition. + ExplodedNodeSet Dst; + NodeBuilder Bldr(Pred, Dst, BuilderCtx); + Pred = Bldr.generateNode(P, Pred->getState(), Pred); + if (!Pred) + return; + } + // Check if we are entering the EXIT block. if (Blk == &(L.getLocationContext()->getCFG()->getExit())) { assert(L.getLocationContext()->getCFG()->getExit().empty() && @@ -276,14 +294,14 @@ void CoreEngine::HandleBlockEntrance(const BlockEntrance &L, } void CoreEngine::HandleBlockExit(const CFGBlock * B, ExplodedNode *Pred) { - if (const Stmt *Term = B->getTerminator()) { + if (const Stmt *Term = B->getTerminatorStmt()) { switch (Term->getStmtClass()) { default: llvm_unreachable("Analysis for this terminator not implemented."); case Stmt::CXXBindTemporaryExprClass: HandleCleanupTemporaryBranch( - cast<CXXBindTemporaryExpr>(B->getTerminator().getStmt()), B, Pred); + cast<CXXBindTemporaryExpr>(Term), B, Pred); return; // Model static initializers. @@ -378,9 +396,19 @@ void CoreEngine::HandleBlockExit(const CFGBlock * B, ExplodedNode *Pred) { case Stmt::WhileStmtClass: HandleBranch(cast<WhileStmt>(Term)->getCond(), Term, B, Pred); return; + + case Stmt::GCCAsmStmtClass: + assert(cast<GCCAsmStmt>(Term)->isAsmGoto() && "Encountered GCCAsmStmt without labels"); + // TODO: Handle jumping to labels + return; } } + if (B->getTerminator().isVirtualBaseBranch()) { + HandleVirtualBaseBranch(B, Pred); + return; + } + assert(B->succ_size() == 1 && "Blocks with no terminator should have at most 1 successor."); @@ -440,6 +468,29 @@ void CoreEngine::HandlePostStmt(const CFGBlock *B, unsigned StmtIdx, } } +void CoreEngine::HandleVirtualBaseBranch(const CFGBlock *B, + ExplodedNode *Pred) { + const LocationContext *LCtx = Pred->getLocationContext(); + if (const auto *CallerCtor = dyn_cast_or_null<CXXConstructExpr>( + LCtx->getStackFrame()->getCallSite())) { + switch (CallerCtor->getConstructionKind()) { + case CXXConstructExpr::CK_NonVirtualBase: + case CXXConstructExpr::CK_VirtualBase: { + BlockEdge Loc(B, *B->succ_begin(), LCtx); + HandleBlockEdge(Loc, Pred); + return; + } + default: + break; + } + } + + // We either don't see a parent stack frame because we're in the top frame, + // or the parent stack frame doesn't initialize our virtual bases. + BlockEdge Loc(B, *(B->succ_begin() + 1), LCtx); + HandleBlockEdge(Loc, Pred); +} + /// generateNode - Utility method to generate nodes, hook up successors, /// and add nodes to the worklist. void CoreEngine::generateNode(const ProgramPoint &Loc, diff --git a/lib/StaticAnalyzer/Core/DynamicTypeMap.cpp b/lib/StaticAnalyzer/Core/DynamicTypeMap.cpp index da7854df1def..79424452240d 100644 --- a/lib/StaticAnalyzer/Core/DynamicTypeMap.cpp +++ b/lib/StaticAnalyzer/Core/DynamicTypeMap.cpp @@ -1,9 +1,8 @@ //===- DynamicTypeMap.cpp - Dynamic Type Info related APIs ----------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// // @@ -14,6 +13,7 @@ //===----------------------------------------------------------------------===// #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" @@ -36,7 +36,7 @@ DynamicTypeInfo getDynamicTypeInfo(ProgramStateRef State, // Otherwise, fall back to what we know about the region. if (const auto *TR = dyn_cast<TypedRegion>(Reg)) - return DynamicTypeInfo(TR->getLocationType(), /*CanBeSubclass=*/false); + return DynamicTypeInfo(TR->getLocationType(), /*CanBeSub=*/false); if (const auto *SR = dyn_cast<SymbolicRegion>(Reg)) { SymbolRef Sym = SR->getSymbol(); @@ -54,27 +54,38 @@ ProgramStateRef setDynamicTypeInfo(ProgramStateRef State, const MemRegion *Reg, return NewState; } -void printDynamicTypeInfo(ProgramStateRef State, raw_ostream &Out, - const char *NL, const char *Sep) { - bool First = true; - for (const auto &I : State->get<DynamicTypeMap>()) { - if (First) { - Out << NL << "Dynamic types of regions:" << NL; - First = false; - } - const MemRegion *MR = I.first; - const DynamicTypeInfo &DTI = I.second; - Out << MR << " : "; +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(); - if (DTI.canBeASubClass()) { - Out << " (or its subclass)"; - } + Out << '\"' << DTI.getType()->getPointeeType().getAsString() + << "\", \"sub_classable\": " + << (DTI.canBeASubClass() ? "true" : "false"); } else { - Out << "Invalid type info"; + 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() { diff --git a/lib/StaticAnalyzer/Core/Environment.cpp b/lib/StaticAnalyzer/Core/Environment.cpp index b45f93b6dde8..551c89b04db4 100644 --- a/lib/StaticAnalyzer/Core/Environment.cpp +++ b/lib/StaticAnalyzer/Core/Environment.cpp @@ -1,9 +1,8 @@ //===- Environment.cpp - Map from Stmt* to Locations/Values ---------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// // @@ -19,6 +18,7 @@ #include "clang/Analysis/AnalysisDeclContext.h" #include "clang/Basic/LLVM.h" #include "clang/Basic/LangOptions.h" +#include "clang/Basic/JsonSupport.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" #include "clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h" #include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h" @@ -200,43 +200,86 @@ EnvironmentManager::removeDeadBindings(Environment Env, return NewEnv; } -void Environment::print(raw_ostream &Out, const char *NL, - const char *Sep, - const ASTContext &Context, - const LocationContext *WithLC) const { - if (ExprBindings.isEmpty()) +void Environment::printJson(raw_ostream &Out, const ASTContext &Ctx, + const LocationContext *LCtx, const char *NL, + unsigned int Space, bool IsDot) const { + Indent(Out, Space, IsDot) << "\"environment\": "; + + if (ExprBindings.isEmpty()) { + Out << "null," << NL; return; + } - if (!WithLC) { + ++Space; + if (!LCtx) { // Find the freshest location context. llvm::SmallPtrSet<const LocationContext *, 16> FoundContexts; - for (auto I : *this) { + for (const auto &I : *this) { const LocationContext *LC = I.first.getLocationContext(); if (FoundContexts.count(LC) == 0) { // This context is fresher than all other contexts so far. - WithLC = LC; + LCtx = LC; for (const LocationContext *LCI = LC; LCI; LCI = LCI->getParent()) FoundContexts.insert(LCI); } } } - assert(WithLC); + assert(LCtx); + + Out << "{ \"pointer\": \"" << (const void *)LCtx->getStackFrame() + << "\", \"items\": [" << NL; + PrintingPolicy PP = Ctx.getPrintingPolicy(); - PrintingPolicy PP = Context.getPrintingPolicy(); + LCtx->printJson(Out, NL, Space, IsDot, [&](const LocationContext *LC) { + // LCtx items begin + bool HasItem = false; + unsigned int InnerSpace = Space + 1; - Out << NL << "Expressions by stack frame:" << NL; - WithLC->dumpStack(Out, "", NL, Sep, [&](const LocationContext *LC) { - for (auto I : ExprBindings) { - if (I.first.getLocationContext() != LC) + // Store the last ExprBinding which we will print. + BindingsTy::iterator LastI = ExprBindings.end(); + for (BindingsTy::iterator I = ExprBindings.begin(); I != ExprBindings.end(); + ++I) { + if (I->first.getLocationContext() != LC) continue; - const Stmt *S = I.first.getStmt(); + if (!HasItem) { + HasItem = true; + Out << '[' << NL; + } + + const Stmt *S = I->first.getStmt(); + (void)S; assert(S != nullptr && "Expected non-null Stmt"); - Out << "(LC" << LC->getID() << ", S" << S->getID(Context) << ") "; - S->printPretty(Out, /*Helper=*/nullptr, PP); - Out << " : " << I.second << NL; + LastI = I; + } + + for (BindingsTy::iterator I = ExprBindings.begin(); I != ExprBindings.end(); + ++I) { + if (I->first.getLocationContext() != LC) + continue; + + const Stmt *S = I->first.getStmt(); + Indent(Out, InnerSpace, IsDot) + << "{ \"stmt_id\": " << S->getID(Ctx) << ", \"pretty\": "; + S->printJson(Out, nullptr, PP, /*AddQuotes=*/true); + + Out << ", \"value\": "; + I->second.printJson(Out, /*AddQuotes=*/true); + + Out << " }"; + + if (I != LastI) + Out << ','; + Out << NL; } + + if (HasItem) + Indent(Out, --InnerSpace, IsDot) << ']'; + else + Out << "null "; }); + + Indent(Out, --Space, IsDot) << "]}," << NL; } diff --git a/lib/StaticAnalyzer/Core/ExplodedGraph.cpp b/lib/StaticAnalyzer/Core/ExplodedGraph.cpp index d6bcbb96b55f..c86b1436baab 100644 --- a/lib/StaticAnalyzer/Core/ExplodedGraph.cpp +++ b/lib/StaticAnalyzer/Core/ExplodedGraph.cpp @@ -1,9 +1,8 @@ //===- ExplodedGraph.cpp - Local, Path-Sens. "Exploded Graph" -------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// // diff --git a/lib/StaticAnalyzer/Core/ExprEngine.cpp b/lib/StaticAnalyzer/Core/ExprEngine.cpp index 151eef56fece..1fef5b3c1edd 100644 --- a/lib/StaticAnalyzer/Core/ExprEngine.cpp +++ b/lib/StaticAnalyzer/Core/ExprEngine.cpp @@ -1,9 +1,8 @@ //===- ExprEngine.cpp - Path-Sensitive Expression-Level Dataflow ----------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// // @@ -34,6 +33,7 @@ #include "clang/Analysis/ConstructionContext.h" #include "clang/Analysis/ProgramPoint.h" #include "clang/Basic/IdentifierTable.h" +#include "clang/Basic/JsonSupport.h" #include "clang/Basic/LLVM.h" #include "clang/Basic/LangOptions.h" #include "clang/Basic/PrettyStackTrace.h" @@ -142,21 +142,34 @@ public: return getLocationContext()->getDecl()->getASTContext(); } - void print(llvm::raw_ostream &OS, PrinterHelper *Helper, PrintingPolicy &PP) { - OS << "(LC" << getLocationContext()->getID() << ','; - if (const Stmt *S = getItem().getStmtOrNull()) - OS << 'S' << S->getID(getASTContext()); + void printJson(llvm::raw_ostream &Out, PrinterHelper *Helper, + PrintingPolicy &PP) const { + const Stmt *S = getItem().getStmtOrNull(); + const CXXCtorInitializer *I = nullptr; + if (!S) + I = getItem().getCXXCtorInitializer(); + + if (S) + Out << "\"stmt_id\": " << S->getID(getASTContext()); else - OS << 'I' << getItem().getCXXCtorInitializer()->getID(getASTContext()); - OS << ',' << getItem().getKindAsString(); + Out << "\"init_id\": " << I->getID(getASTContext()); + + // Kind + Out << ", \"kind\": \"" << getItem().getKindAsString() + << "\", \"argument_index\": "; + if (getItem().getKind() == ConstructionContextItem::ArgumentKind) - OS << " #" << getItem().getIndex(); - OS << ") "; - if (const Stmt *S = getItem().getStmtOrNull()) { - S->printPretty(OS, Helper, PP); + Out << getItem().getIndex(); + else + Out << "null"; + + // Pretty-print + Out << ", \"pretty\": "; + + if (S) { + S->printJson(Out, Helper, PP, /*AddQuotes=*/true); } else { - const CXXCtorInitializer *I = getItem().getCXXCtorInitializer(); - OS << I->getAnyMember()->getNameAsString(); + Out << '\"' << I->getAnyMember()->getNameAsString() << '\"'; } } @@ -198,9 +211,13 @@ ExprEngine::ExprEngine(cross_tu::CrossTranslationUnitContext &CTU, mgr.getConstraintManagerCreator(), G.getAllocator(), this), SymMgr(StateMgr.getSymbolManager()), - svalBuilder(StateMgr.getSValBuilder()), ObjCNoRet(mgr.getASTContext()), + MRMgr(StateMgr.getRegionManager()), + svalBuilder(StateMgr.getSValBuilder()), + ObjCNoRet(mgr.getASTContext()), BR(mgr, *this), - VisitedCallees(VisitedCalleesIn), HowToInline(HowToInlineIn) { + VisitedCallees(VisitedCalleesIn), + HowToInline(HowToInlineIn) + { unsigned TrimInterval = mgr.options.GraphTrimInterval; if (TrimInterval != 0) { // Enable eager node reclamation when constructing the ExplodedGraph. @@ -208,10 +225,6 @@ ExprEngine::ExprEngine(cross_tu::CrossTranslationUnitContext &CTU, } } -ExprEngine::~ExprEngine() { - BR.FlushReports(); -} - //===----------------------------------------------------------------------===// // Utility methods. //===----------------------------------------------------------------------===// @@ -538,36 +551,73 @@ ExprEngine::processRegionChanges(ProgramStateRef state, LCtx, Call); } -static void printObjectsUnderConstructionForContext(raw_ostream &Out, - ProgramStateRef State, - const char *NL, - const LocationContext *LC) { +static void +printObjectsUnderConstructionJson(raw_ostream &Out, ProgramStateRef State, + const char *NL, const LocationContext *LCtx, + unsigned int Space = 0, bool IsDot = false) { PrintingPolicy PP = - LC->getAnalysisDeclContext()->getASTContext().getPrintingPolicy(); - for (auto I : State->get<ObjectsUnderConstruction>()) { - ConstructedObjectKey Key = I.first; + LCtx->getAnalysisDeclContext()->getASTContext().getPrintingPolicy(); + + ++Space; + bool HasItem = false; + + // Store the last key. + const ConstructedObjectKey *LastKey = nullptr; + for (const auto &I : State->get<ObjectsUnderConstruction>()) { + const ConstructedObjectKey &Key = I.first; + if (Key.getLocationContext() != LCtx) + continue; + + if (!HasItem) { + Out << "[" << NL; + HasItem = true; + } + + LastKey = &Key; + } + + for (const auto &I : State->get<ObjectsUnderConstruction>()) { + const ConstructedObjectKey &Key = I.first; SVal Value = I.second; - if (Key.getLocationContext() != LC) + if (Key.getLocationContext() != LCtx) continue; - Key.print(Out, nullptr, PP); - Out << " : " << Value << NL; + + Indent(Out, Space, IsDot) << "{ "; + Key.printJson(Out, nullptr, PP); + Out << ", \"value\": \"" << Value << "\" }"; + + if (&Key != LastKey) + Out << ','; + Out << NL; + } + + if (HasItem) + Indent(Out, --Space, IsDot) << ']'; // End of "location_context". + else { + Out << "null "; } } -void ExprEngine::printState(raw_ostream &Out, ProgramStateRef State, - const char *NL, const char *Sep, - const LocationContext *LCtx) { - if (LCtx) { - if (!State->get<ObjectsUnderConstruction>().isEmpty()) { - Out << Sep << "Objects under construction:" << NL; +void ExprEngine::printJson(raw_ostream &Out, ProgramStateRef State, + const LocationContext *LCtx, const char *NL, + unsigned int Space, bool IsDot) const { + Indent(Out, Space, IsDot) << "\"constructing_objects\": "; - LCtx->dumpStack(Out, "", NL, Sep, [&](const LocationContext *LC) { - printObjectsUnderConstructionForContext(Out, State, NL, LC); - }); - } + if (LCtx && !State->get<ObjectsUnderConstruction>().isEmpty()) { + ++Space; + Out << '[' << NL; + LCtx->printJson(Out, NL, Space, IsDot, [&](const LocationContext *LC) { + printObjectsUnderConstructionJson(Out, State, NL, LC, Space, IsDot); + }); + + --Space; + Indent(Out, Space, IsDot) << "]," << NL; // End of "constructing_objects". + } else { + Out << "null," << NL; } - getCheckerManager().runCheckersForPrintState(Out, State, NL, Sep); + getCheckerManager().runCheckersForPrintStateJson(Out, State, NL, Space, + IsDot); } void ExprEngine::processEndWorklist() { @@ -1338,6 +1388,7 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, case Stmt::NoInitExprClass: case Stmt::SizeOfPackExprClass: case Stmt::StringLiteralClass: + case Stmt::SourceLocExprClass: case Stmt::ObjCStringLiteralClass: case Stmt::CXXPseudoDestructorExprClass: case Stmt::SubstNonTypeTemplateParmExprClass: @@ -1517,7 +1568,7 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, ProgramStateRef NewState = createTemporaryRegionIfNeeded(State, LCtx, OCE->getArg(0)); if (NewState != State) { - Pred = Bldr.generateNode(OCE, Pred, NewState, /*Tag=*/nullptr, + Pred = Bldr.generateNode(OCE, Pred, NewState, /*tag=*/nullptr, ProgramPoint::PreStmtKind); // Did we cache out? if (!Pred) @@ -1636,6 +1687,7 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, case Stmt::CXXReinterpretCastExprClass: case Stmt::CXXConstCastExprClass: case Stmt::CXXFunctionalCastExprClass: + case Stmt::BuiltinBitCastExprClass: case Stmt::ObjCBridgedCastExprClass: { Bldr.takeNodes(Pred); const auto *C = cast<CastExpr>(S); @@ -1858,7 +1910,7 @@ void ExprEngine::processCFGBlockEntrance(const BlockEdge &L, // other constraints) then consider completely unrolling it. if(AMgr.options.ShouldUnrollLoops) { unsigned maxBlockVisitOnPath = AMgr.options.maxBlockVisitOnPath; - const Stmt *Term = nodeBuilder.getContext().getBlock()->getTerminator(); + const Stmt *Term = nodeBuilder.getContext().getBlock()->getTerminatorStmt(); if (Term) { ProgramStateRef NewState = updateLoopStack(Term, AMgr.getASTContext(), Pred, maxBlockVisitOnPath); @@ -1879,7 +1931,7 @@ void ExprEngine::processCFGBlockEntrance(const BlockEdge &L, unsigned int BlockCount = nodeBuilder.getContext().blockCount(); if (BlockCount == AMgr.options.maxBlockVisitOnPath - 1 && AMgr.options.ShouldWidenLoops) { - const Stmt *Term = nodeBuilder.getContext().getBlock()->getTerminator(); + const Stmt *Term = nodeBuilder.getContext().getBlock()->getTerminatorStmt(); if (!(Term && (isa<ForStmt>(Term) || isa<WhileStmt>(Term) || isa<DoStmt>(Term)))) return; @@ -2004,8 +2056,8 @@ static const Stmt *ResolveCondition(const Stmt *Condition, if (!BO || !BO->isLogicalOp()) return Condition; - assert(!B->getTerminator().isTemporaryDtorsBranch() && - "Temporary destructor branches handled by processBindTemporary."); + assert(B->getTerminator().isStmtBranch() && + "Other kinds of branches are handled separately!"); // For logical operations, we still have the case where some branches // use the traditional "merge" approach and others sink the branch @@ -2258,7 +2310,6 @@ void ExprEngine::processEndOfFunction(NodeBuilderContext& BC, Pred->getStackFrame()->getParent())); PrettyStackTraceLocationContext CrashInfo(Pred->getLocationContext()); - StateMgr.EndPath(Pred->getState()); ExplodedNodeSet Dst; if (Pred->getLocationContext()->inTopFrame()) { @@ -2620,43 +2671,39 @@ void ExprEngine::VisitAtomicExpr(const AtomicExpr *AE, ExplodedNode *Pred, getCheckerManager().runCheckersForPostStmt(Dst, AfterInvalidateSet, AE, *this); } -// A value escapes in three possible cases: +// A value escapes in four possible cases: // (1) We are binding to something that is not a memory region. -// (2) We are binding to a MemrRegion that does not have stack storage. -// (3) We are binding to a MemRegion with stack storage that the store +// (2) We are binding to a MemRegion that does not have stack storage. +// (3) We are binding to a top-level parameter region with a non-trivial +// destructor. We won't see the destructor during analysis, but it's there. +// (4) We are binding to a MemRegion with stack storage that the store // does not understand. -ProgramStateRef ExprEngine::processPointerEscapedOnBind(ProgramStateRef State, - SVal Loc, - SVal Val, - const LocationContext *LCtx) { - // Are we storing to something that causes the value to "escape"? - bool escapes = true; - - // TODO: Move to StoreManager. - if (Optional<loc::MemRegionVal> regionLoc = Loc.getAs<loc::MemRegionVal>()) { - escapes = !regionLoc->getRegion()->hasStackStorage(); - - if (!escapes) { - // To test (3), generate a new state with the binding added. If it is - // the same state, then it escapes (since the store cannot represent - // the binding). - // Do this only if we know that the store is not supposed to generate the - // same state. - SVal StoredVal = State->getSVal(regionLoc->getRegion()); - if (StoredVal != Val) - escapes = (State == (State->bindLoc(*regionLoc, Val, LCtx))); - } - } - - // If our store can represent the binding and we aren't storing to something - // that doesn't have local storage then just return and have the simulation - // state continue as is. - if (!escapes) - return State; +ProgramStateRef +ExprEngine::processPointerEscapedOnBind(ProgramStateRef State, SVal Loc, + SVal Val, const LocationContext *LCtx) { + + // Cases (1) and (2). + const MemRegion *MR = Loc.getAsRegion(); + if (!MR || !MR->hasStackStorage()) + return escapeValue(State, Val, PSK_EscapeOnBind); + + // Case (3). + if (const auto *VR = dyn_cast<VarRegion>(MR->getBaseRegion())) + if (VR->hasStackParametersStorage() && VR->getStackFrame()->inTopFrame()) + if (const auto *RD = VR->getValueType()->getAsCXXRecordDecl()) + if (!RD->hasTrivialDestructor()) + return escapeValue(State, Val, PSK_EscapeOnBind); + + // Case (4): in order to test that, generate a new state with the binding + // added. If it is the same state, then it escapes (since the store cannot + // represent the binding). + // Do this only if we know that the store is not supposed to generate the + // same state. + SVal StoredVal = State->getSVal(MR); + if (StoredVal != Val) + if (State == (State->bindLoc(loc::MemRegionVal(MR), Val, LCtx))) + return escapeValue(State, Val, PSK_EscapeOnBind); - // Otherwise, find all symbols referenced by 'val' that we are tracking - // and stop tracking them. - State = escapeValue(State, Val, PSK_EscapeOnBind); return State; } @@ -2959,7 +3006,8 @@ struct DOTGraphTraits<ExplodedGraph*> : public DefaultDOTGraphTraits { for (const auto &EQ : EQClasses) { for (const BugReport &Report : EQ) { - if (Report.getErrorNode() == N) + if (Report.getErrorNode()->getState() == N->getState() && + Report.getErrorNode()->getLocation() == N->getLocation()) return true; } } @@ -2995,57 +3043,63 @@ struct DOTGraphTraits<ExplodedGraph*> : public DefaultDOTGraphTraits { return false; } - static std::string getNodeAttributes(const ExplodedNode *N, - ExplodedGraph *) { - SmallVector<StringRef, 10> Out; - auto Noop = [](const ExplodedNode*){}; - if (traverseHiddenNodes(N, Noop, Noop, &nodeHasBugReport)) { - Out.push_back("style=filled"); - Out.push_back("fillcolor=red"); - } - - if (traverseHiddenNodes(N, Noop, Noop, - [](const ExplodedNode *C) { return C->isSink(); })) - Out.push_back("color=blue"); - return llvm::join(Out, ","); - } - static bool isNodeHidden(const ExplodedNode *N) { return N->isTrivial(); } static std::string getNodeLabel(const ExplodedNode *N, ExplodedGraph *G){ - std::string sbuf; - llvm::raw_string_ostream Out(sbuf); + std::string Buf; + llvm::raw_string_ostream Out(Buf); + const bool IsDot = true; + 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") + << ",\\l"; + + Indent(Out, Space, IsDot) << "\"program_points\": [\\l"; + // Dump program point for all the previously skipped nodes. traverseHiddenNodes( N, [&](const ExplodedNode *OtherNode) { - OtherNode->getLocation().print(/*CR=*/"\\l", Out); + Indent(Out, Space + 1, IsDot) << "{ "; + OtherNode->getLocation().printJson(Out, /*NL=*/"\\l"); + Out << ", \"tag\": "; if (const ProgramPointTag *Tag = OtherNode->getLocation().getTag()) - Out << "\\lTag:" << Tag->getTagDescription(); - if (N->isSink()) - Out << "\\lNode is sink\\l"; - if (nodeHasBugReport(N)) - Out << "\\lBug report attached\\l"; + Out << '\"' << Tag->getTagDescription() << "\" }"; + else + Out << "null }"; }, - [&](const ExplodedNode *) { Out << "\\l--------\\l"; }, + // Adds a comma and a new-line between each program point. + [&](const ExplodedNode *) { Out << ",\\l"; }, [&](const ExplodedNode *) { return false; }); - Out << "\\l\\|"; - - Out << "StateID: ST" << State->getID() << ", NodeID: N" << N->getID(G) - << " <" << (const void *)N << ">\\|"; + 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()); + + if (!SameAsAllPredecessors) { + State->printDOT(Out, N->getLocationContext(), Space); + } else { + Indent(Out, Space, IsDot) << "\"program_state\": null"; + } + + Out << "\\l}\\l"; return Out.str(); } }; diff --git a/lib/StaticAnalyzer/Core/ExprEngineC.cpp b/lib/StaticAnalyzer/Core/ExprEngineC.cpp index b980628878e9..f436650fbdd9 100644 --- a/lib/StaticAnalyzer/Core/ExprEngineC.cpp +++ b/lib/StaticAnalyzer/Core/ExprEngineC.cpp @@ -1,9 +1,8 @@ //=-- ExprEngineC.cpp - ExprEngine support for C expressions ----*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// // @@ -101,6 +100,10 @@ void ExprEngine::VisitBinaryOperator(const BinaryOperator* B, SVal Result = evalBinOp(state, Op, LeftV, RightV, B->getType()); if (!Result.isUnknown()) { state = state->BindExpr(B, LCtx, Result); + } else { + // If we cannot evaluate the operation escape the operands. + state = escapeValue(state, LeftV, PSK_EscapeOther); + state = escapeValue(state, RightV, PSK_EscapeOther); } Bldr.generateNode(B, *it, state); @@ -377,9 +380,9 @@ void ExprEngine::VisitCast(const CastExpr *CastE, const Expr *Ex, case CK_Dependent: case CK_ArrayToPointerDecay: case CK_BitCast: + case CK_LValueToRValueBitCast: case CK_AddressSpaceConversion: case CK_BooleanToSignedIntegral: - case CK_NullToPointer: case CK_IntegralToPointer: case CK_PointerToIntegral: { SVal V = state->getSVal(Ex, LCtx); @@ -416,7 +419,9 @@ void ExprEngine::VisitCast(const CastExpr *CastE, const Expr *Ex, case CK_IntToOCLSampler: case CK_LValueBitCast: case CK_FixedPointCast: - case CK_FixedPointToBoolean: { + case CK_FixedPointToBoolean: + case CK_FixedPointToIntegral: + case CK_IntegralToFixedPoint: { state = handleLValueBitCast(state, Ex, LCtx, T, ExTy, CastE, Bldr, Pred); continue; @@ -502,6 +507,12 @@ void ExprEngine::VisitCast(const CastExpr *CastE, const Expr *Ex, Bldr.generateNode(CastE, Pred, state); continue; } + case CK_NullToPointer: { + SVal V = svalBuilder.makeNull(); + state = state->BindExpr(CastE, LCtx, V); + Bldr.generateNode(CastE, Pred, state); + continue; + } case CK_NullToMemberPointer: { SVal V = svalBuilder.getMemberPointer(nullptr); state = state->BindExpr(CastE, LCtx, V); @@ -626,6 +637,21 @@ void ExprEngine::VisitDeclStmt(const DeclStmt *DS, ExplodedNode *Pred, void ExprEngine::VisitLogicalExpr(const BinaryOperator* B, ExplodedNode *Pred, ExplodedNodeSet &Dst) { + // This method acts upon CFG elements for logical operators && and || + // and attaches the value (true or false) to them as expressions. + // It doesn't produce any state splits. + // If we made it that far, we're past the point when we modeled the short + // circuit. It means that we should have precise knowledge about whether + // we've short-circuited. If we did, we already know the value we need to + // bind. If we didn't, the value of the RHS (casted to the boolean type) + // is the answer. + // Currently this method tries to figure out whether we've short-circuited + // by looking at the ExplodedGraph. This method is imperfect because there + // could inevitably have been merges that would have resulted in multiple + // potential path traversal histories. We bail out when we fail. + // Due to this ambiguity, a more reliable solution would have been to + // track the short circuit operation history path-sensitively until + // we evaluate the respective logical operator. assert(B->getOpcode() == BO_LAnd || B->getOpcode() == BO_LOr); @@ -647,10 +673,20 @@ void ExprEngine::VisitLogicalExpr(const BinaryOperator* B, ExplodedNode *Pred, ProgramPoint P = N->getLocation(); assert(P.getAs<PreStmt>()|| P.getAs<PreStmtPurgeDeadSymbols>()); (void) P; - assert(N->pred_size() == 1); + if (N->pred_size() != 1) { + // We failed to track back where we came from. + Bldr.generateNode(B, Pred, state); + return; + } N = *N->pred_begin(); } - assert(N->pred_size() == 1); + + if (N->pred_size() != 1) { + // We failed to track back where we came from. + Bldr.generateNode(B, Pred, state); + return; + } + N = *N->pred_begin(); BlockEdge BE = N->getLocation().castAs<BlockEdge>(); SVal X; @@ -703,7 +739,7 @@ void ExprEngine::VisitInitListExpr(const InitListExpr *IE, QualType T = getContext().getCanonicalType(IE->getType()); unsigned NumInitElements = IE->getNumInits(); - if (!IE->isGLValue() && + if (!IE->isGLValue() && !IE->isTransparent() && (T->isArrayType() || T->isRecordType() || T->isVectorType() || T->isAnyComplexType())) { llvm::ImmutableList<SVal> vals = getBasicVals().getEmptySValList(); diff --git a/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp b/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp index 6445b9df5a58..1cbd09ea5793 100644 --- a/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp +++ b/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp @@ -1,9 +1,8 @@ //===- ExprEngineCXX.cpp - ExprEngine support for C++ -----------*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// // @@ -197,6 +196,12 @@ std::pair<ProgramStateRef, SVal> ExprEngine::prepareForObjectConstruction( // able to find construction context at all. break; } + if (isa<BlockInvocationContext>(CallerLCtx)) { + // Unwrap block invocation contexts. They're mostly part of + // the current stack frame. + CallerLCtx = CallerLCtx->getParent(); + assert(!isa<BlockInvocationContext>(CallerLCtx)); + } return prepareForObjectConstruction( cast<Expr>(SFC->getCallSite()), State, CallerLCtx, RTC->getConstructionContext(), CallOpts); @@ -423,25 +428,20 @@ void ExprEngine::VisitCXXConstructExpr(const CXXConstructExpr *CE, prepareForObjectConstruction(CE, State, LCtx, CC, CallOpts); break; } - case CXXConstructExpr::CK_VirtualBase: + case CXXConstructExpr::CK_VirtualBase: { // Make sure we are not calling virtual base class initializers twice. // Only the most-derived object should initialize virtual base classes. - if (const Stmt *Outer = LCtx->getStackFrame()->getCallSite()) { - const CXXConstructExpr *OuterCtor = dyn_cast<CXXConstructExpr>(Outer); - if (OuterCtor) { - switch (OuterCtor->getConstructionKind()) { - case CXXConstructExpr::CK_NonVirtualBase: - case CXXConstructExpr::CK_VirtualBase: - // Bail out! - destNodes.Add(Pred); - return; - case CXXConstructExpr::CK_Complete: - case CXXConstructExpr::CK_Delegating: - break; - } - } - } + const auto *OuterCtor = dyn_cast_or_null<CXXConstructExpr>( + LCtx->getStackFrame()->getCallSite()); + assert( + (!OuterCtor || + OuterCtor->getConstructionKind() == CXXConstructExpr::CK_Complete || + OuterCtor->getConstructionKind() == CXXConstructExpr::CK_Delegating) && + ("This virtual base should have already been initialized by " + "the most derived class!")); + (void)OuterCtor; LLVM_FALLTHROUGH; + } case CXXConstructExpr::CK_NonVirtualBase: // In C++17, classes with non-virtual bases may be aggregates, so they would // be initialized as aggregates without a constructor call, so we may have @@ -604,6 +604,7 @@ void ExprEngine::VisitCXXDestructor(QualType ObjectType, ExplodedNode *Pred, ExplodedNodeSet &Dst, const EvalCallOptions &CallOpts) { + assert(S && "A destructor without a trigger!"); const LocationContext *LCtx = Pred->getLocationContext(); ProgramStateRef State = Pred->getState(); @@ -611,6 +612,19 @@ void ExprEngine::VisitCXXDestructor(QualType ObjectType, 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) { + // Skip the invalid destructor. We cannot simply return because + // it would interrupt the analysis instead. + static SimpleProgramPointTag T("ExprEngine", "SkipInvalidDestructor"); + // FIXME: PostImplicitCall with a null decl may crash elsewhere anyway. + PostImplicitCall PP(/*Decl=*/nullptr, S->getEndLoc(), LCtx, &T); + NodeBuilder Bldr(Pred, Dst, *currBldrCtx); + Bldr.generateNode(PP, Pred->getState(), Pred); + return; + } + CallEventManager &CEMgr = getStateManager().getCallEventManager(); CallEventRef<CXXDestructorCall> Call = CEMgr.getCXXDestructorCall(DtorDecl, S, Dest, IsBaseDtor, State, LCtx); @@ -629,7 +643,6 @@ void ExprEngine::VisitCXXDestructor(QualType ObjectType, I != E; ++I) defaultEvalCall(Bldr, *I, *Call, CallOpts); - ExplodedNodeSet DstPostCall; getCheckerManager().runCheckersForPostCall(Dst, DstInvalidated, *Call, *this); } diff --git a/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp b/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp index 758195d8d911..b935e3afe34b 100644 --- a/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp +++ b/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp @@ -1,9 +1,8 @@ //=-- ExprEngineCallAndReturn.cpp - Support for call/return -----*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// // @@ -328,30 +327,30 @@ void ExprEngine::processCallExit(ExplodedNode *CEBNode) { ExplodedNodeSet DstPostPostCallCallback; getCheckerManager().runCheckersForPostCall(DstPostPostCallCallback, CEENode, *UpdatedCall, *this, - /*WasInlined=*/true); + /*wasInlined=*/true); for (auto I : DstPostPostCallCallback) { getCheckerManager().runCheckersForNewAllocator( CNE, *getObjectUnderConstruction(I->getState(), CNE, calleeCtx->getParent()), DstPostCall, I, *this, - /*WasInlined=*/true); + /*wasInlined=*/true); } } else { getCheckerManager().runCheckersForPostCall(DstPostCall, CEENode, *UpdatedCall, *this, - /*WasInlined=*/true); + /*wasInlined=*/true); } ExplodedNodeSet Dst; if (const ObjCMethodCall *Msg = dyn_cast<ObjCMethodCall>(Call)) { getCheckerManager().runCheckersForPostObjCMessage(Dst, DstPostCall, *Msg, *this, - /*WasInlined=*/true); + /*wasInlined=*/true); } else if (CE && !(isa<CXXNewExpr>(CE) && // Called when visiting CXXNewExpr. AMgr.getAnalyzerOptions().MayInlineCXXAllocator)) { getCheckerManager().runCheckersForPostStmt(Dst, DstPostCall, CE, - *this, /*WasInlined=*/true); + *this, /*wasInlined=*/true); } else { Dst.insert(DstPostCall); } @@ -365,6 +364,26 @@ void ExprEngine::processCallExit(ExplodedNode *CEBNode) { } } +bool ExprEngine::isSmall(AnalysisDeclContext *ADC) const { + // When there are no branches in the function, it means that there's no + // exponential complexity introduced by inlining such function. + // Such functions also don't trigger various fundamental problems + // with our inlining mechanism, such as the problem of + // inlined defensive checks. Hence isLinear(). + const CFG *Cfg = ADC->getCFG(); + return Cfg->isLinear() || Cfg->size() <= AMgr.options.AlwaysInlineSize; +} + +bool ExprEngine::isLarge(AnalysisDeclContext *ADC) const { + const CFG *Cfg = ADC->getCFG(); + return Cfg->size() >= AMgr.options.MinCFGSizeTreatFunctionsAsLarge; +} + +bool ExprEngine::isHuge(AnalysisDeclContext *ADC) const { + const CFG *Cfg = ADC->getCFG(); + return Cfg->getNumBlockIDs() > AMgr.options.MaxInlinableSize; +} + void ExprEngine::examineStackFrames(const Decl *D, const LocationContext *LCtx, bool &IsRecursive, unsigned &StackDepth) { IsRecursive = false; @@ -385,8 +404,7 @@ void ExprEngine::examineStackFrames(const Decl *D, const LocationContext *LCtx, // Do not count the small functions when determining the stack depth. AnalysisDeclContext *CalleeADC = AMgr.getAnalysisDeclContext(DI); - const CFG *CalleeCFG = CalleeADC->getCFG(); - if (CalleeCFG->getNumBlockIDs() > AMgr.options.AlwaysInlineSize) + if (!isSmall(CalleeADC)) ++StackDepth; } LCtx = LCtx->getParent(); @@ -616,12 +634,19 @@ ProgramStateRef ExprEngine::bindReturnValue(const CallEvent &Call, std::tie(State, Target) = prepareForObjectConstruction(Call.getOriginExpr(), State, LCtx, RTC->getConstructionContext(), CallOpts); - assert(Target.getAsRegion()); - // Invalidate the region so that it didn't look uninitialized. Don't notify - // the checkers. - State = State->invalidateRegions(Target.getAsRegion(), E, Count, LCtx, - /* CausedByPointerEscape=*/false, nullptr, - &Call, nullptr); + const MemRegion *TargetR = Target.getAsRegion(); + assert(TargetR); + // Invalidate the region so that it didn't look uninitialized. If this is + // a field or element constructor, we do not want to invalidate + // the whole structure. Pointer escape is meaningless because + // the structure is a product of conservative evaluation + // and therefore contains nothing interesting at this point. + RegionAndSymbolInvalidationTraits ITraits; + ITraits.setTrait(TargetR, + RegionAndSymbolInvalidationTraits::TK_DoNotInvalidateSuperRegion); + State = State->invalidateRegions(TargetR, E, Count, LCtx, + /* CausesPointerEscape=*/false, nullptr, + &Call, &ITraits); R = State->getSVal(Target.castAs<Loc>(), E->getType()); } else { @@ -833,8 +858,7 @@ static bool isCXXSharedPtrDtor(const FunctionDecl *FD) { /// This checks static properties of the function, such as its signature and /// CFG, to determine whether the analyzer should ever consider inlining it, /// in any context. -static bool mayInlineDecl(AnalysisManager &AMgr, - AnalysisDeclContext *CalleeADC) { +bool ExprEngine::mayInlineDecl(AnalysisDeclContext *CalleeADC) const { AnalyzerOptions &Opts = AMgr.getAnalyzerOptions(); // FIXME: Do not inline variadic calls. if (CallEvent::isVariadic(CalleeADC->getDecl())) @@ -879,7 +903,7 @@ static bool mayInlineDecl(AnalysisManager &AMgr, return false; // Do not inline large functions. - if (CalleeCFG->getNumBlockIDs() > Opts.MaxInlinableSize) + if (isHuge(CalleeADC)) return false; // It is possible that the live variables analysis cannot be @@ -919,7 +943,7 @@ bool ExprEngine::shouldInlineCall(const CallEvent &Call, const Decl *D, } else { // We haven't actually checked the static properties of this function yet. // Do that now, and record our decision in the function summaries. - if (mayInlineDecl(getAnalysisManager(), CalleeADC)) { + if (mayInlineDecl(CalleeADC)) { Engine.FunctionSummaries->markMayInline(D); } else { Engine.FunctionSummaries->markShouldNotInline(D); @@ -940,29 +964,23 @@ bool ExprEngine::shouldInlineCall(const CallEvent &Call, const Decl *D, return false; } - const CFG *CalleeCFG = CalleeADC->getCFG(); - // Do not inline if recursive or we've reached max stack frame count. bool IsRecursive = false; unsigned StackDepth = 0; examineStackFrames(D, Pred->getLocationContext(), IsRecursive, StackDepth); if ((StackDepth >= Opts.InlineMaxStackDepth) && - ((CalleeCFG->getNumBlockIDs() > Opts.AlwaysInlineSize) - || IsRecursive)) + (!isSmall(CalleeADC) || IsRecursive)) return false; // Do not inline large functions too many times. if ((Engine.FunctionSummaries->getNumTimesInlined(D) > Opts.MaxTimesInlineLarge) && - CalleeCFG->getNumBlockIDs() >= - Opts.MinCFGSizeTreatFunctionsAsLarge) { + isLarge(CalleeADC)) { NumReachedInlineCountMax++; return false; } - if (HowToInline == Inline_Minimal && - (CalleeCFG->getNumBlockIDs() > Opts.AlwaysInlineSize - || IsRecursive)) + if (HowToInline == Inline_Minimal && (!isSmall(CalleeADC) || IsRecursive)) return false; return true; diff --git a/lib/StaticAnalyzer/Core/ExprEngineObjC.cpp b/lib/StaticAnalyzer/Core/ExprEngineObjC.cpp index 6b8402f621e0..eb9a0be2e5d6 100644 --- a/lib/StaticAnalyzer/Core/ExprEngineObjC.cpp +++ b/lib/StaticAnalyzer/Core/ExprEngineObjC.cpp @@ -1,9 +1,8 @@ //=-- ExprEngineObjC.cpp - ExprEngine support for Objective-C ---*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// // diff --git a/lib/StaticAnalyzer/Core/FunctionSummary.cpp b/lib/StaticAnalyzer/Core/FunctionSummary.cpp index 94edd84d15d2..2b9a45133bba 100644 --- a/lib/StaticAnalyzer/Core/FunctionSummary.cpp +++ b/lib/StaticAnalyzer/Core/FunctionSummary.cpp @@ -1,9 +1,8 @@ //===- FunctionSummary.cpp - Stores summaries of functions. ---------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// // diff --git a/lib/StaticAnalyzer/Core/HTMLDiagnostics.cpp b/lib/StaticAnalyzer/Core/HTMLDiagnostics.cpp index fc82f1176942..64c42699fcf3 100644 --- a/lib/StaticAnalyzer/Core/HTMLDiagnostics.cpp +++ b/lib/StaticAnalyzer/Core/HTMLDiagnostics.cpp @@ -1,9 +1,8 @@ //===- HTMLDiagnostics.cpp - HTML Diagnostics for Paths -------------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// // @@ -91,8 +90,9 @@ public: const PathDiagnosticMacroPiece& P, unsigned num); - void HandlePiece(Rewriter& R, FileID BugFileID, - const PathDiagnosticPiece& P, unsigned num, unsigned max); + void HandlePiece(Rewriter &R, FileID BugFileID, const PathDiagnosticPiece &P, + const std::vector<SourceRange> &PopUpRanges, unsigned num, + unsigned max); void HighlightRange(Rewriter& R, FileID BugFileID, SourceRange Range, const char *HighlightStart = "<span class=\"mrange\">", @@ -274,7 +274,7 @@ std::string HTMLDiagnostics::GenerateHTML(const PathDiagnostic& D, Rewriter &R, std::vector<FileID> FileIDs; for (auto I : path) { FileID FID = I->getLocation().asLocation().getExpansionLoc().getFileID(); - if (std::find(FileIDs.begin(), FileIDs.end(), FID) != FileIDs.end()) + if (llvm::is_contained(FileIDs, FID)) continue; FileIDs.push_back(FID); @@ -606,6 +606,53 @@ window.addEventListener("keydown", function (event) { )<<<"; } +static void +HandlePopUpPieceStartTag(Rewriter &R, + const std::vector<SourceRange> &PopUpRanges) { + for (const auto &Range : PopUpRanges) { + html::HighlightRange(R, Range.getBegin(), Range.getEnd(), "", + "<table class='variable_popup'><tbody>", + /*IsTokenRange=*/false); + } +} + +static void HandlePopUpPieceEndTag(Rewriter &R, + const PathDiagnosticPopUpPiece &Piece, + std::vector<SourceRange> &PopUpRanges, + unsigned int LastReportedPieceIndex, + unsigned int PopUpPieceIndex) { + SmallString<256> Buf; + llvm::raw_svector_ostream Out(Buf); + + SourceRange Range(Piece.getLocation().asRange()); + + // Write out the path indices with a right arrow and the message as a row. + Out << "<tr><td valign='top'><div class='PathIndex PathIndexPopUp'>" + << LastReportedPieceIndex; + + // Also annotate the state transition with extra indices. + Out << '.' << PopUpPieceIndex; + + Out << "</div></td><td>" << Piece.getString() << "</td></tr>"; + + // If no report made at this range mark the variable and add the end tags. + if (std::find(PopUpRanges.begin(), PopUpRanges.end(), Range) == + PopUpRanges.end()) { + // Store that we create a report at this range. + PopUpRanges.push_back(Range); + + 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. + } else { + html::HighlightRange(R, Range.getBegin(), Range.getEnd(), "", Buf.c_str(), + /*IsTokenRange=*/false); + } +} + void HTMLDiagnostics::RewriteFile(Rewriter &R, const PathPieces& path, FileID FID) { // Process the path. @@ -616,39 +663,80 @@ void HTMLDiagnostics::RewriteFile(Rewriter &R, [](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 TotalRegularPieces = TotalPieces - TotalNotePieces; + unsigned TotalRegularPieces = TotalPieces - TotalNotePieces - PopUpPieceCount; unsigned NumRegularPieces = TotalRegularPieces; unsigned NumNotePieces = TotalNotePieces; + // Stores the count of the regular piece indices. + std::map<int, int> IndexMap; + // Stores the different ranges where we have reported something. + std::vector<SourceRange> PopUpRanges; for (auto I = path.rbegin(), E = path.rend(); I != E; ++I) { - if (isa<PathDiagnosticNotePiece>(I->get())) { + const auto &Piece = *I->get(); + + if (isa<PathDiagnosticPopUpPiece>(Piece)) { + ++IndexMap[NumRegularPieces]; + } else if (isa<PathDiagnosticNotePiece>(Piece)) { // This adds diagnostic bubbles, but not navigation. // Navigation through note pieces would be added later, // as a separate pass through the piece list. - HandlePiece(R, FID, **I, NumNotePieces, TotalNotePieces); + HandlePiece(R, FID, Piece, PopUpRanges, NumNotePieces, TotalNotePieces); --NumNotePieces; } else { - HandlePiece(R, FID, **I, NumRegularPieces, TotalRegularPieces); + HandlePiece(R, FID, Piece, PopUpRanges, NumRegularPieces, + TotalRegularPieces); --NumRegularPieces; } } - // Add line numbers, header, footer, etc. + // Secondary indexing if we are having multiple pop-ups between two notes. + // (e.g. [(13) 'a' is 'true']; [(13.1) 'b' is 'false']; [(13.2) 'c' is...) + NumRegularPieces = TotalRegularPieces; + for (auto I = path.rbegin(), E = path.rend(); I != E; ++I) { + const auto &Piece = *I->get(); + + if (const auto *PopUpP = dyn_cast<PathDiagnosticPopUpPiece>(&Piece)) { + int PopUpPieceIndex = IndexMap[NumRegularPieces]; + + // Pop-up pieces needs the index of the last reported piece and its count + // how many times we report to handle multiple reports on the same range. + // This marks the variable, adds the </table> end tag and the message + // (list element) as a row. The <table> start tag will be added after the + // rows has been written out. Note: It stores every different range. + HandlePopUpPieceEndTag(R, *PopUpP, PopUpRanges, NumRegularPieces, + PopUpPieceIndex); + + if (PopUpPieceIndex > 0) + --IndexMap[NumRegularPieces]; + + } else if (!isa<PathDiagnosticNotePiece>(Piece)) { + --NumRegularPieces; + } + } + + // Add the <table> start tag of pop-up pieces based on the stored ranges. + HandlePopUpPieceStartTag(R, PopUpRanges); + // Add line numbers, header, footer, etc. html::EscapeText(R, FID); html::AddLineNumbers(R, FID); // If we have a preprocessor, relex the file and syntax highlight. // We might not have a preprocessor if we come from a deserialized AST file, // for example. - html::SyntaxHighlight(R, FID, PP); html::HighlightMacros(R, FID, PP); } -void HTMLDiagnostics::HandlePiece(Rewriter& R, FileID BugFileID, - const PathDiagnosticPiece& P, +void HTMLDiagnostics::HandlePiece(Rewriter &R, FileID BugFileID, + const PathDiagnosticPiece &P, + const std::vector<SourceRange> &PopUpRanges, unsigned num, unsigned max) { // For now, just draw a box above the line in question, and emit the // warning. @@ -690,9 +778,7 @@ void HTMLDiagnostics::HandlePiece(Rewriter& R, FileID BugFileID, bool IsNote = false; bool SuppressIndex = (max == 1); switch (P.getKind()) { - case PathDiagnosticPiece::Call: - llvm_unreachable("Calls and extra notes should already be handled"); - case PathDiagnosticPiece::Event: Kind = "Event"; break; + case PathDiagnosticPiece::Event: Kind = "Event"; break; case PathDiagnosticPiece::ControlFlow: Kind = "Control"; break; // Setting Kind to "Control" is intentional. case PathDiagnosticPiece::Macro: Kind = "Control"; break; @@ -701,6 +787,9 @@ void HTMLDiagnostics::HandlePiece(Rewriter& R, FileID BugFileID, IsNote = true; SuppressIndex = true; break; + case PathDiagnosticPiece::Call: + case PathDiagnosticPiece::PopUp: + llvm_unreachable("Calls and extra notes should already be handled"); } std::string sbuf; @@ -860,8 +949,14 @@ void HTMLDiagnostics::HandlePiece(Rewriter& R, FileID BugFileID, // Now highlight the ranges. ArrayRef<SourceRange> Ranges = P.getRanges(); - for (const auto &Range : Ranges) + for (const auto &Range : Ranges) { + // If we have already highlighted the range as a pop-up there is no work. + if (std::find(PopUpRanges.begin(), PopUpRanges.end(), Range) != + PopUpRanges.end()) + continue; + HighlightRange(R, LPosInfo.first, Range); + } } static void EmitAlphaCounter(raw_ostream &os, unsigned n) { diff --git a/lib/StaticAnalyzer/Core/IssueHash.cpp b/lib/StaticAnalyzer/Core/IssueHash.cpp index 6c55c61dd399..e7497f3fbdaa 100644 --- a/lib/StaticAnalyzer/Core/IssueHash.cpp +++ b/lib/StaticAnalyzer/Core/IssueHash.cpp @@ -1,9 +1,8 @@ //===---------- IssueHash.cpp - Generate identification hashes --*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// #include "clang/StaticAnalyzer/Core/IssueHash.h" @@ -121,7 +120,7 @@ static std::string GetEnclosingDeclContextSignature(const Decl *D) { return ""; } -static StringRef GetNthLineOfFile(llvm::MemoryBuffer *Buffer, int Line) { +static StringRef GetNthLineOfFile(const llvm::MemoryBuffer *Buffer, int Line) { if (!Buffer) return ""; @@ -145,7 +144,7 @@ static std::string NormalizeLine(const SourceManager &SM, FullSourceLoc &L, col++; SourceLocation StartOfLine = SM.translateLineCol(SM.getFileID(L), L.getExpansionLineNumber(), col); - llvm::MemoryBuffer *Buffer = + const llvm::MemoryBuffer *Buffer = SM.getBuffer(SM.getFileID(StartOfLine), StartOfLine); if (!Buffer) return {}; diff --git a/lib/StaticAnalyzer/Core/LoopUnrolling.cpp b/lib/StaticAnalyzer/Core/LoopUnrolling.cpp index da4574c61515..9838249ae82c 100644 --- a/lib/StaticAnalyzer/Core/LoopUnrolling.cpp +++ b/lib/StaticAnalyzer/Core/LoopUnrolling.cpp @@ -1,9 +1,8 @@ //===--- LoopUnrolling.cpp - Unroll loops -----------------------*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// /// @@ -235,7 +234,7 @@ bool madeNewBranch(ExplodedNode *N, const Stmt *LoopStmt) { ProgramPoint P = N->getLocation(); if (Optional<BlockEntrance> BE = P.getAs<BlockEntrance>()) - S = BE->getBlock()->getTerminator(); + S = BE->getBlock()->getTerminatorStmt(); if (S == LoopStmt) return false; diff --git a/lib/StaticAnalyzer/Core/LoopWidening.cpp b/lib/StaticAnalyzer/Core/LoopWidening.cpp index 8f6cb9a6b09e..9a7b1a24b819 100644 --- a/lib/StaticAnalyzer/Core/LoopWidening.cpp +++ b/lib/StaticAnalyzer/Core/LoopWidening.cpp @@ -1,9 +1,8 @@ //===--- LoopWidening.cpp - Widen loops -------------------------*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// /// diff --git a/lib/StaticAnalyzer/Core/MemRegion.cpp b/lib/StaticAnalyzer/Core/MemRegion.cpp index 9a1d4d73c20b..f763701af7fb 100644 --- a/lib/StaticAnalyzer/Core/MemRegion.cpp +++ b/lib/StaticAnalyzer/Core/MemRegion.cpp @@ -1,9 +1,8 @@ //===- MemRegion.cpp - Abstract memory regions for static analysis --------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// // @@ -845,6 +844,7 @@ getStackOrCaptureRegionForDeclContext(const LocationContext *LC, const VarRegion* MemRegionManager::getVarRegion(const VarDecl *D, const LocationContext *LC) { + D = D->getCanonicalDecl(); const MemRegion *sReg = nullptr; if (D->hasGlobalStorage() && !D->isStaticLocal()) { @@ -931,6 +931,7 @@ const VarRegion* MemRegionManager::getVarRegion(const VarDecl *D, const VarRegion *MemRegionManager::getVarRegion(const VarDecl *D, const MemRegion *superR) { + D = D->getCanonicalDecl(); return getSubRegion<VarRegion>(D, superR); } @@ -1008,6 +1009,7 @@ MemRegionManager::getElementRegion(QualType elementType, NonLoc Idx, const FunctionCodeRegion * MemRegionManager::getFunctionCodeRegion(const NamedDecl *FD) { + // To think: should we canonicalize the declaration here? return getSubRegion<FunctionCodeRegion>(FD, getCodeRegion()); } diff --git a/lib/StaticAnalyzer/Core/PathDiagnostic.cpp b/lib/StaticAnalyzer/Core/PathDiagnostic.cpp index 3e93bb6a7c4f..54fbd6a5bc49 100644 --- a/lib/StaticAnalyzer/Core/PathDiagnostic.cpp +++ b/lib/StaticAnalyzer/Core/PathDiagnostic.cpp @@ -1,9 +1,8 @@ //===- PathDiagnostic.cpp - Path-Specific Diagnostic Handling -------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// // @@ -91,6 +90,8 @@ PathDiagnosticMacroPiece::~PathDiagnosticMacroPiece() = default; PathDiagnosticNotePiece::~PathDiagnosticNotePiece() = default; +PathDiagnosticPopUpPiece::~PathDiagnosticPopUpPiece() = default; + void PathPieces::flattenTo(PathPieces &Primary, PathPieces &Current, bool ShouldFlattenMacros) const { for (auto &Piece : *this) { @@ -120,6 +121,7 @@ void PathPieces::flattenTo(PathPieces &Primary, PathPieces &Current, case PathDiagnosticPiece::Event: case PathDiagnosticPiece::ControlFlow: case PathDiagnosticPiece::Note: + case PathDiagnosticPiece::PopUp: Current.push_back(Piece); break; } @@ -370,15 +372,16 @@ static Optional<bool> comparePiece(const PathDiagnosticPiece &X, case PathDiagnosticPiece::ControlFlow: return compareControlFlow(cast<PathDiagnosticControlFlowPiece>(X), cast<PathDiagnosticControlFlowPiece>(Y)); - case PathDiagnosticPiece::Event: - case PathDiagnosticPiece::Note: - return None; 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"); } @@ -572,6 +575,8 @@ static SourceLocation getValidSourceLocation(const Stmt* S, } while (!L.isValid()); } + // FIXME: Ironically, this assert actually fails in some cases. + //assert(L.isValid()); return L; } @@ -672,7 +677,15 @@ PathDiagnosticLocation::createConditionalColonLoc( PathDiagnosticLocation PathDiagnosticLocation::createMemberLoc(const MemberExpr *ME, const SourceManager &SM) { - return PathDiagnosticLocation(ME->getMemberLoc(), SM, SingleLocK); + + 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 @@ -715,7 +728,24 @@ PathDiagnosticLocation::create(const ProgramPoint& P, const Stmt* S = nullptr; if (Optional<BlockEdge> BE = P.getAs<BlockEdge>()) { const CFGBlock *BSrc = BE->getSrc(); - S = BSrc->getTerminatorCondition(); + 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>()) @@ -735,6 +765,12 @@ PathDiagnosticLocation::create(const ProgramPoint& P, 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>()) { @@ -744,6 +780,9 @@ PathDiagnosticLocation::create(const ProgramPoint& P, 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"); } @@ -779,7 +818,7 @@ const Stmt *PathDiagnosticLocation::getStmt(const ExplodedNode *N) { if (auto SP = P.getAs<StmtPoint>()) return SP->getStmt(); if (auto BE = P.getAs<BlockEdge>()) - return BE->getSrc()->getTerminator(); + return BE->getSrc()->getTerminatorStmt(); if (auto CE = P.getAs<CallEnter>()) return CE->getCallExpr(); if (auto CEE = P.getAs<CallExitEnd>()) @@ -1255,6 +1294,10 @@ 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); @@ -1380,6 +1423,13 @@ LLVM_DUMP_METHOD void PathDiagnosticNotePiece::dump() const { 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"; diff --git a/lib/StaticAnalyzer/Core/PlistDiagnostics.cpp b/lib/StaticAnalyzer/Core/PlistDiagnostics.cpp index db4cf76578d8..838751279297 100644 --- a/lib/StaticAnalyzer/Core/PlistDiagnostics.cpp +++ b/lib/StaticAnalyzer/Core/PlistDiagnostics.cpp @@ -1,9 +1,8 @@ //===--- PlistDiagnostics.cpp - Plist Diagnostics for Paths -----*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// // @@ -23,6 +22,7 @@ #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/Support/Casting.h" @@ -120,6 +120,9 @@ private: case PathDiagnosticPiece::Note: ReportNote(o, cast<PathDiagnosticNotePiece>(P), indent); break; + case PathDiagnosticPiece::PopUp: + ReportPopUp(o, cast<PathDiagnosticPopUpPiece>(P), indent); + break; } } @@ -138,6 +141,9 @@ private: unsigned indent, unsigned depth); void ReportNote(raw_ostream &o, const PathDiagnosticNotePiece& P, unsigned indent); + + void ReportPopUp(raw_ostream &o, const PathDiagnosticPopUpPiece &P, + unsigned indent); }; } // end of anonymous namespace @@ -397,6 +403,34 @@ void PlistPrinter::ReportNote(raw_ostream &o, const PathDiagnosticNotePiece& P, Indent(o, indent); o << "</dict>\n"; } +void PlistPrinter::ReportPopUp(raw_ostream &o, + const PathDiagnosticPopUpPiece &P, + unsigned indent) { + const SourceManager &SM = PP.getSourceManager(); + + Indent(o, indent) << "<dict>\n"; + ++indent; + + Indent(o, indent) << "<key>kind</key><string>pop-up</string>\n"; + + // Output the location. + FullSourceLoc L = P.getLocation().asLocation(); + + Indent(o, indent) << "<key>location</key>\n"; + EmitLocation(o, SM, L, FM, indent); + + // Output the ranges (if any). + ArrayRef<SourceRange> Ranges = P.getRanges(); + EmitRanges(o, Ranges, indent); + + // Output the text. + EmitMessage(o, P.getString(), indent); + + // Finish up. + --indent; + Indent(o, indent) << "</dict>\n"; +} + //===----------------------------------------------------------------------===// // Static function definitions. //===----------------------------------------------------------------------===// @@ -714,7 +748,7 @@ void PlistDiagnostics::FlushDiagnosticsImpl( } // Finish. - o << "</dict>\n</plist>"; + o << "</dict>\n</plist>\n"; } //===----------------------------------------------------------------------===// @@ -777,10 +811,20 @@ public: /// As we expand the last line, we'll immediately replace PRINT(str) with /// print(x). The information that both 'str' and 'x' refers to the same string /// is an information we have to forward, hence the argument \p PrevArgs. -static std::string getMacroNameAndPrintExpansion(TokenPrinter &Printer, - SourceLocation MacroLoc, - const Preprocessor &PP, - const MacroArgMap &PrevArgs); +/// +/// To avoid infinite recursion we maintain the already processed tokens in +/// a set. This is carried as a parameter through the recursive calls. The set +/// is extended with the currently processed token and after processing it, the +/// token is removed. If the token is already in the set, then recursion stops: +/// +/// #define f(y) x +/// #define x f(x) +static std::string getMacroNameAndPrintExpansion( + TokenPrinter &Printer, + SourceLocation MacroLoc, + const Preprocessor &PP, + const MacroArgMap &PrevArgs, + llvm::SmallPtrSet<IdentifierInfo *, 8> &AlreadyProcessedTokens); /// Retrieves the name of the macro and what it's arguments expand into /// at \p ExpanLoc. @@ -829,19 +873,38 @@ static ExpansionInfo getExpandedMacro(SourceLocation MacroLoc, llvm::SmallString<200> ExpansionBuf; llvm::raw_svector_ostream OS(ExpansionBuf); TokenPrinter Printer(OS, PP); + llvm::SmallPtrSet<IdentifierInfo*, 8> AlreadyProcessedTokens; + std::string MacroName = - getMacroNameAndPrintExpansion(Printer, MacroLoc, PP, MacroArgMap{}); + getMacroNameAndPrintExpansion(Printer, MacroLoc, PP, MacroArgMap{}, + AlreadyProcessedTokens); return { MacroName, OS.str() }; } -static std::string getMacroNameAndPrintExpansion(TokenPrinter &Printer, - SourceLocation MacroLoc, - const Preprocessor &PP, - const MacroArgMap &PrevArgs) { +static std::string getMacroNameAndPrintExpansion( + TokenPrinter &Printer, + SourceLocation MacroLoc, + const Preprocessor &PP, + const MacroArgMap &PrevArgs, + llvm::SmallPtrSet<IdentifierInfo *, 8> &AlreadyProcessedTokens) { const SourceManager &SM = PP.getSourceManager(); MacroNameAndArgs Info = getMacroNameAndArgs(SM.getExpansionLoc(MacroLoc), PP); + IdentifierInfo* IDInfo = PP.getIdentifierInfo(Info.Name); + + // TODO: If the macro definition contains another symbol then this function is + // called recursively. In case this symbol is the one being defined, it will + // be an infinite recursion which is stopped by this "if" statement. However, + // in this case we don't get the full expansion text in the Plist file. See + // the test file where "value" is expanded to "garbage_" instead of + // "garbage_value". + if (AlreadyProcessedTokens.find(IDInfo) != AlreadyProcessedTokens.end()) + return Info.Name; + AlreadyProcessedTokens.insert(IDInfo); + + if (!Info.MI) + return Info.Name; // Manually expand its arguments from the previous macro. Info.Args.expandFromPrevMacro(PrevArgs); @@ -863,14 +926,15 @@ static std::string getMacroNameAndPrintExpansion(TokenPrinter &Printer, // If this token is a macro that should be expanded inside the current // macro. - if (const MacroInfo *MI = - getMacroInfoForLocation(PP, SM, II, T.getLocation())) { - getMacroNameAndPrintExpansion(Printer, T.getLocation(), PP, Info.Args); + if (getMacroInfoForLocation(PP, SM, II, T.getLocation())) { + getMacroNameAndPrintExpansion(Printer, T.getLocation(), PP, Info.Args, + AlreadyProcessedTokens); // If this is a function-like macro, skip its arguments, as // getExpandedMacro() already printed them. If this is the case, let's // first jump to the '(' token. - if (MI->getNumParams() != 0) + auto N = std::next(It); + if (N != E && N->is(tok::l_paren)) It = getMatchingRParen(++It, E); continue; } @@ -897,8 +961,17 @@ static std::string getMacroNameAndPrintExpansion(TokenPrinter &Printer, } getMacroNameAndPrintExpansion(Printer, ArgIt->getLocation(), PP, - Info.Args); - if (MI->getNumParams() != 0) + Info.Args, AlreadyProcessedTokens); + // Peek the next token if it is a tok::l_paren. This way we can decide + // if this is the application or just a reference to a function maxro + // symbol: + // + // #define apply(f) ... + // #define func(x) ... + // apply(func) + // apply(func(42)) + auto N = std::next(ArgIt); + if (N != ArgEnd && N->is(tok::l_paren)) ArgIt = getMatchingRParen(++ArgIt, ArgEnd); } continue; @@ -909,6 +982,8 @@ static std::string getMacroNameAndPrintExpansion(TokenPrinter &Printer, Printer.printToken(T); } + AlreadyProcessedTokens.erase(IDInfo); + return Info.Name; } @@ -937,7 +1012,14 @@ static MacroNameAndArgs getMacroNameAndArgs(SourceLocation ExpanLoc, assert(II && "Failed to acquire the IndetifierInfo for the macro!"); const MacroInfo *MI = getMacroInfoForLocation(PP, SM, II, ExpanLoc); - assert(MI && "The macro must've been defined at it's expansion location!"); + // assert(MI && "The macro must've been defined at it's expansion location!"); + // + // We should always be able to obtain the MacroInfo in a given TU, but if + // we're running the analyzer with CTU, the Preprocessor won't contain the + // directive history (or anything for that matter) from another TU. + // TODO: assert when we're not running with CTU. + if (!MI) + return { MacroName, MI, {} }; // Acquire the macro's arguments. // @@ -951,8 +1033,16 @@ static MacroNameAndArgs getMacroNameAndArgs(SourceLocation ExpanLoc, return { MacroName, MI, {} }; RawLexer.LexFromRawLexer(TheTok); - assert(TheTok.is(tok::l_paren) && - "The token after the macro's identifier token should be '('!"); + // When this is a token which expands to another macro function then its + // parentheses are not at its expansion locaiton. For example: + // + // #define foo(x) int bar() { return x; } + // #define apply_zero(f) f(0) + // apply_zero(foo) + // ^ + // This is not a tok::l_paren, but foo is a function. + if (TheTok.isNot(tok::l_paren)) + return { MacroName, MI, {} }; MacroArgMap Args; diff --git a/lib/StaticAnalyzer/Core/PrettyStackTraceLocationContext.h b/lib/StaticAnalyzer/Core/PrettyStackTraceLocationContext.h index 4bb694819c2a..c71ee3bd4286 100644 --- a/lib/StaticAnalyzer/Core/PrettyStackTraceLocationContext.h +++ b/lib/StaticAnalyzer/Core/PrettyStackTraceLocationContext.h @@ -1,9 +1,8 @@ //==- PrettyStackTraceLocationContext.h - show analysis backtrace --*- C++ -*-// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// @@ -33,9 +32,9 @@ public: assert(LCtx); } - void print(raw_ostream &OS) const override { - OS << "While analyzing stack: \n"; - LCtx->dumpStack(OS, "\t"); + void print(raw_ostream &Out) const override { + Out << "While analyzing stack: \n"; + LCtx->dumpStack(Out); } }; diff --git a/lib/StaticAnalyzer/Core/ProgramState.cpp b/lib/StaticAnalyzer/Core/ProgramState.cpp index 2e2e2ec94f39..a1ca0b1b84bf 100644 --- a/lib/StaticAnalyzer/Core/ProgramState.cpp +++ b/lib/StaticAnalyzer/Core/ProgramState.cpp @@ -1,9 +1,8 @@ //= ProgramState.cpp - Path-Sensitive "State" for tracking values --*- C++ -*--= // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// // @@ -11,14 +10,14 @@ // //===----------------------------------------------------------------------===// -#include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" #include "clang/Analysis/CFG.h" +#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/ProgramStateTrait.h" #include "clang/StaticAnalyzer/Core/PathSensitive/SubEngine.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/TaintManager.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/DynamicTypeMap.h" #include "llvm/Support/raw_ostream.h" using namespace clang; @@ -442,53 +441,40 @@ void ProgramState::setStore(const StoreRef &newStore) { // State pretty-printing. //===----------------------------------------------------------------------===// -void ProgramState::print(raw_ostream &Out, - const char *NL, const char *Sep, - const LocationContext *LC) const { - // Print the store. +void ProgramState::printJson(raw_ostream &Out, const LocationContext *LCtx, + const char *NL, unsigned int Space, + bool IsDot) const { + Indent(Out, Space, IsDot) << "\"program_state\": {" << NL; + ++Space; + ProgramStateManager &Mgr = getStateManager(); - const ASTContext &Context = getStateManager().getContext(); - Mgr.getStoreManager().print(getStore(), Out, NL); + + // Print the store. + Mgr.getStoreManager().printJson(Out, getStore(), NL, Space, IsDot); // Print out the environment. - Env.print(Out, NL, Sep, Context, LC); + Env.printJson(Out, Mgr.getContext(), LCtx, NL, Space, IsDot); // Print out the constraints. - Mgr.getConstraintManager().print(this, Out, NL, Sep); + Mgr.getConstraintManager().printJson(Out, this, NL, Space, IsDot); // Print out the tracked dynamic types. - printDynamicTypeInfo(this, Out, NL, Sep); - - // Print out tainted symbols. - printTaint(Out, NL); + printDynamicTypeInfoJson(Out, this, NL, Space, IsDot); // Print checker-specific data. - Mgr.getOwningEngine().printState(Out, this, NL, Sep, LC); -} - -void ProgramState::printDOT(raw_ostream &Out, - const LocationContext *LC) const { - print(Out, "\\l", "\\|", LC); -} + Mgr.getOwningEngine().printJson(Out, this, LCtx, NL, Space, IsDot); -LLVM_DUMP_METHOD void ProgramState::dump() const { - print(llvm::errs()); + --Space; + Indent(Out, Space, IsDot) << '}'; } -void ProgramState::printTaint(raw_ostream &Out, - const char *NL) const { - TaintMapImpl TM = get<TaintMap>(); - - if (!TM.isEmpty()) - Out <<"Tainted symbols:" << NL; - - for (TaintMapImpl::iterator I = TM.begin(), E = TM.end(); I != E; ++I) { - Out << I->first << " : " << I->second << NL; - } +void ProgramState::printDOT(raw_ostream &Out, const LocationContext *LCtx, + unsigned int Space) const { + printJson(Out, LCtx, /*NL=*/"\\l", Space, /*IsDot=*/true); } -void ProgramState::dumpTaint() const { - printTaint(llvm::errs()); +LLVM_DUMP_METHOD void ProgramState::dump() const { + printJson(llvm::errs()); } AnalysisManager& ProgramState::getAnalysisManager() const { @@ -658,166 +644,3 @@ bool ProgramState::scanReachableSymbols( } return true; } - -ProgramStateRef ProgramState::addTaint(const Stmt *S, - const LocationContext *LCtx, - TaintTagType Kind) const { - if (const Expr *E = dyn_cast_or_null<Expr>(S)) - S = E->IgnoreParens(); - - return addTaint(getSVal(S, LCtx), Kind); -} - -ProgramStateRef ProgramState::addTaint(SVal V, - TaintTagType Kind) const { - SymbolRef Sym = V.getAsSymbol(); - if (Sym) - return addTaint(Sym, Kind); - - // If the SVal represents a structure, try to mass-taint all values within the - // structure. For now it only works efficiently on lazy compound values that - // were conjured during a conservative evaluation of a function - either as - // return values of functions that return structures or arrays by value, or as - // values of structures or arrays passed into the function by reference, - // directly or through pointer aliasing. Such lazy compound values are - // characterized by having exactly one binding in their captured store within - // their parent region, which is a conjured symbol default-bound to the base - // region of the parent region. - if (auto LCV = V.getAs<nonloc::LazyCompoundVal>()) { - if (Optional<SVal> binding = getStateManager().StoreMgr->getDefaultBinding(*LCV)) { - if (SymbolRef Sym = binding->getAsSymbol()) - return addPartialTaint(Sym, LCV->getRegion(), Kind); - } - } - - const MemRegion *R = V.getAsRegion(); - return addTaint(R, Kind); -} - -ProgramStateRef ProgramState::addTaint(const MemRegion *R, - TaintTagType Kind) const { - if (const SymbolicRegion *SR = dyn_cast_or_null<SymbolicRegion>(R)) - return addTaint(SR->getSymbol(), Kind); - return this; -} - -ProgramStateRef ProgramState::addTaint(SymbolRef Sym, - TaintTagType Kind) const { - // If this is a symbol cast, remove the cast before adding the taint. Taint - // is cast agnostic. - while (const SymbolCast *SC = dyn_cast<SymbolCast>(Sym)) - Sym = SC->getOperand(); - - ProgramStateRef NewState = set<TaintMap>(Sym, Kind); - assert(NewState); - return NewState; -} - -ProgramStateRef ProgramState::addPartialTaint(SymbolRef ParentSym, - const SubRegion *SubRegion, - TaintTagType Kind) const { - // Ignore partial taint if the entire parent symbol is already tainted. - if (contains<TaintMap>(ParentSym) && *get<TaintMap>(ParentSym) == Kind) - return this; - - // Partial taint applies if only a portion of the symbol is tainted. - if (SubRegion == SubRegion->getBaseRegion()) - return addTaint(ParentSym, Kind); - - const TaintedSubRegions *SavedRegs = get<DerivedSymTaint>(ParentSym); - TaintedSubRegions Regs = - SavedRegs ? *SavedRegs : stateMgr->TSRFactory.getEmptyMap(); - - Regs = stateMgr->TSRFactory.add(Regs, SubRegion, Kind); - ProgramStateRef NewState = set<DerivedSymTaint>(ParentSym, Regs); - assert(NewState); - return NewState; -} - -bool ProgramState::isTainted(const Stmt *S, const LocationContext *LCtx, - TaintTagType Kind) const { - if (const Expr *E = dyn_cast_or_null<Expr>(S)) - S = E->IgnoreParens(); - - SVal val = getSVal(S, LCtx); - return isTainted(val, Kind); -} - -bool ProgramState::isTainted(SVal V, TaintTagType Kind) const { - if (const SymExpr *Sym = V.getAsSymExpr()) - return isTainted(Sym, Kind); - if (const MemRegion *Reg = V.getAsRegion()) - return isTainted(Reg, Kind); - return false; -} - -bool ProgramState::isTainted(const MemRegion *Reg, TaintTagType K) const { - if (!Reg) - return false; - - // Element region (array element) is tainted if either the base or the offset - // are tainted. - if (const ElementRegion *ER = dyn_cast<ElementRegion>(Reg)) - return isTainted(ER->getSuperRegion(), K) || isTainted(ER->getIndex(), K); - - if (const SymbolicRegion *SR = dyn_cast<SymbolicRegion>(Reg)) - return isTainted(SR->getSymbol(), K); - - if (const SubRegion *ER = dyn_cast<SubRegion>(Reg)) - return isTainted(ER->getSuperRegion(), K); - - return false; -} - -bool ProgramState::isTainted(SymbolRef Sym, TaintTagType Kind) const { - if (!Sym) - return false; - - // Traverse all the symbols this symbol depends on to see if any are tainted. - for (SymExpr::symbol_iterator SI = Sym->symbol_begin(), SE =Sym->symbol_end(); - SI != SE; ++SI) { - if (!isa<SymbolData>(*SI)) - continue; - - if (const TaintTagType *Tag = get<TaintMap>(*SI)) { - if (*Tag == Kind) - return true; - } - - if (const SymbolDerived *SD = dyn_cast<SymbolDerived>(*SI)) { - // If this is a SymbolDerived with a tainted parent, it's also tainted. - if (isTainted(SD->getParentSymbol(), Kind)) - return true; - - // If this is a SymbolDerived with the same parent symbol as another - // tainted SymbolDerived and a region that's a sub-region of that tainted - // symbol, it's also tainted. - if (const TaintedSubRegions *Regs = - get<DerivedSymTaint>(SD->getParentSymbol())) { - const TypedValueRegion *R = SD->getRegion(); - for (auto I : *Regs) { - // FIXME: The logic to identify tainted regions could be more - // complete. For example, this would not currently identify - // overlapping fields in a union as tainted. To identify this we can - // check for overlapping/nested byte offsets. - if (Kind == I.second && R->isSubRegionOf(I.first)) - return true; - } - } - } - - // If memory region is tainted, data is also tainted. - if (const SymbolRegionValue *SRV = dyn_cast<SymbolRegionValue>(*SI)) { - if (isTainted(SRV->getRegion(), Kind)) - return true; - } - - // If this is a SymbolCast from a tainted value, it's also tainted. - if (const SymbolCast *SC = dyn_cast<SymbolCast>(*SI)) { - if (isTainted(SC->getOperand(), Kind)) - return true; - } - } - - return false; -} diff --git a/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp b/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp index d9b58d0f5185..64724227395d 100644 --- a/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp +++ b/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp @@ -1,9 +1,8 @@ //== RangeConstraintManager.cpp - Manage range constraints.------*- C++ -*--==// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// // @@ -12,6 +11,7 @@ // //===----------------------------------------------------------------------===// +#include "clang/Basic/JsonSupport.h" #include "clang/StaticAnalyzer/Core/PathSensitive/APSIntType.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h" @@ -174,6 +174,22 @@ RangeSet RangeSet::Intersect(BasicValueFactory &BV, Factory &F, return newRanges; } +// Returns a set containing the values in the receiving set, intersected with +// the range set passed as parameter. +RangeSet RangeSet::Intersect(BasicValueFactory &BV, Factory &F, + const RangeSet &Other) const { + PrimRangeSet newRanges = F.getEmptySet(); + + for (iterator i = Other.begin(), e = Other.end(); i != e; ++i) { + RangeSet newPiece = Intersect(BV, F, i->From(), i->To()); + for (iterator j = newPiece.begin(), ee = newPiece.end(); j != ee; ++j) { + newRanges = F.add(newRanges, *j); + } + } + + return newRanges; +} + // Turn all [A, B] ranges to [-B, -A]. Ranges [MIN, B] are turned to range set // [MIN, MIN] U [-B, MAX], when MIN and MAX are the minimal and the maximal // signed values of the type. @@ -231,6 +247,11 @@ public: // Implementation for interface from ConstraintManager. //===------------------------------------------------------------------===// + bool haveEqualConstraints(ProgramStateRef S1, + ProgramStateRef S2) const override { + return S1->get<ConstraintRange>() == S2->get<ConstraintRange>(); + } + bool canReasonAbout(SVal X) const override; ConditionTruthVal checkNull(ProgramStateRef State, SymbolRef Sym) override; @@ -241,8 +262,8 @@ public: ProgramStateRef removeDeadBindings(ProgramStateRef State, SymbolReaper &SymReaper) override; - void print(ProgramStateRef State, raw_ostream &Out, const char *nl, - const char *sep) override; + void printJson(raw_ostream &Out, ProgramStateRef State, const char *NL = "\n", + unsigned int Space = 0, bool IsDot = false) const override; //===------------------------------------------------------------------===// // Implementation for interface from RangedConstraintManager. @@ -457,14 +478,21 @@ static RangeSet applyBitwiseConstraints( RangeSet RangeConstraintManager::getRange(ProgramStateRef State, SymbolRef Sym) { - if (ConstraintRangeTy::data_type *V = State->get<ConstraintRange>(Sym)) - return *V; - - BasicValueFactory &BV = getBasicVals(); + ConstraintRangeTy::data_type *V = State->get<ConstraintRange>(Sym); // If Sym is a difference of symbols A - B, then maybe we have range set // stored for B - A. - if (const RangeSet *R = getRangeForMinusSymbol(State, Sym)) + BasicValueFactory &BV = getBasicVals(); + const RangeSet *R = getRangeForMinusSymbol(State, Sym); + + // If we have range set stored for both A - B and B - A then calculate the + // effective range set by intersecting the range set for A - B and the + // negated range set of B - A. + if (V && R) + return V->Intersect(BV, F, R->Negate(BV, F)); + if (V) + return *V; + if (R) return R->Negate(BV, F); // Lazily generate a new RangeSet representing all possible values for the @@ -727,25 +755,35 @@ ProgramStateRef RangeConstraintManager::assumeSymOutsideInclusiveRange( return New.isEmpty() ? nullptr : State->set<ConstraintRange>(Sym, New); } -//===------------------------------------------------------------------------=== +//===----------------------------------------------------------------------===// // Pretty-printing. -//===------------------------------------------------------------------------===/ - -void RangeConstraintManager::print(ProgramStateRef St, raw_ostream &Out, - const char *nl, const char *sep) { +//===----------------------------------------------------------------------===// - ConstraintRangeTy Ranges = St->get<ConstraintRange>(); +void RangeConstraintManager::printJson(raw_ostream &Out, ProgramStateRef State, + const char *NL, unsigned int Space, + bool IsDot) const { + ConstraintRangeTy Constraints = State->get<ConstraintRange>(); - if (Ranges.isEmpty()) { - Out << nl << sep << "Ranges are empty." << nl; + Indent(Out, Space, IsDot) << "\"constraints\": "; + if (Constraints.isEmpty()) { + Out << "null," << NL; return; } - Out << nl << sep << "Ranges of symbol values:"; - for (ConstraintRangeTy::iterator I = Ranges.begin(), E = Ranges.end(); I != E; - ++I) { - Out << nl << ' ' << I.getKey() << " : "; + ++Space; + Out << '[' << NL; + for (ConstraintRangeTy::iterator I = Constraints.begin(); + I != Constraints.end(); ++I) { + Indent(Out, Space, IsDot) + << "{ \"symbol\": \"" << I.getKey() << "\", \"range\": \""; I.getData().print(Out); + Out << "\" }"; + + if (std::next(I) != Constraints.end()) + Out << ','; + Out << NL; } - Out << nl; + + --Space; + Indent(Out, Space, IsDot) << "]," << NL; } diff --git a/lib/StaticAnalyzer/Core/RangedConstraintManager.cpp b/lib/StaticAnalyzer/Core/RangedConstraintManager.cpp index 146dc20ad021..4748c106eb55 100644 --- a/lib/StaticAnalyzer/Core/RangedConstraintManager.cpp +++ b/lib/StaticAnalyzer/Core/RangedConstraintManager.cpp @@ -1,9 +1,8 @@ //== RangedConstraintManager.cpp --------------------------------*- C++ -*--==// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// // diff --git a/lib/StaticAnalyzer/Core/RegionStore.cpp b/lib/StaticAnalyzer/Core/RegionStore.cpp index b2339be4f263..d2aea1fd92dd 100644 --- a/lib/StaticAnalyzer/Core/RegionStore.cpp +++ b/lib/StaticAnalyzer/Core/RegionStore.cpp @@ -1,9 +1,8 @@ //== RegionStore.cpp - Field-sensitive store model --------------*- C++ -*--==// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// // @@ -20,6 +19,7 @@ #include "clang/ASTMatchers/ASTMatchFinder.h" #include "clang/Analysis/Analyses/LiveVariables.h" #include "clang/Analysis/AnalysisDeclContext.h" +#include "clang/Basic/JsonSupport.h" #include "clang/Basic/TargetInfo.h" #include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" @@ -121,25 +121,21 @@ BindingKey BindingKey::Make(const MemRegion *R, Kind k) { } namespace llvm { - static inline - raw_ostream &operator<<(raw_ostream &os, BindingKey K) { - os << '(' << K.getRegion(); - if (!K.hasSymbolicOffset()) - os << ',' << K.getOffset(); - os << ',' << (K.isDirect() ? "direct" : "default") - << ')'; - return os; - } - - template <typename T> struct isPodLike; - template <> struct isPodLike<BindingKey> { - static const bool value = true; - }; -} // end llvm namespace - -#ifndef NDEBUG +static inline raw_ostream &operator<<(raw_ostream &Out, BindingKey K) { + Out << "\"kind\": \"" << (K.isDirect() ? "Direct" : "Default") + << "\", \"offset\": "; + + if (!K.hasSymbolicOffset()) + Out << K.getOffset(); + else + Out << "null"; + + return Out; +} + +} // namespace llvm + LLVM_DUMP_METHOD void BindingKey::dump() const { llvm::errs() << *this; } -#endif //===----------------------------------------------------------------------===// // Actual Store type. @@ -211,18 +207,34 @@ public: return asImmutableMap().getRootWithoutRetain(); } - void dump(raw_ostream &OS, const char *nl) const { - for (iterator I = begin(), E = end(); I != E; ++I) { - const ClusterBindings &Cluster = I.getData(); - for (ClusterBindings::iterator CI = Cluster.begin(), CE = Cluster.end(); - CI != CE; ++CI) { - OS << ' ' << CI.getKey() << " : " << CI.getData() << nl; - } - OS << nl; - } + void printJson(raw_ostream &Out, const char *NL = "\n", + unsigned int Space = 0, bool IsDot = false) const { + for (iterator I = begin(); I != end(); ++I) { + // TODO: We might need a .printJson for I.getKey() as well. + Indent(Out, Space, IsDot) + << "{ \"cluster\": \"" << I.getKey() << "\", \"pointer\": \"" + << (const void *)I.getKey() << "\", \"items\": [" << NL; + + ++Space; + const ClusterBindings &CB = I.getData(); + for (ClusterBindings::iterator CI = CB.begin(); CI != CB.end(); ++CI) { + Indent(Out, Space, IsDot) << "{ " << CI.getKey() << ", \"value\": "; + CI.getData().printJson(Out, /*AddQuotes=*/true); + Out << " }"; + if (std::next(CI) != CB.end()) + Out << ','; + Out << NL; + } + + --Space; + Indent(Out, Space, IsDot) << "]}"; + if (std::next(I) != end()) + Out << ','; + Out << NL; + } } - LLVM_DUMP_METHOD void dump() const { dump(llvm::errs(), "\n"); } + LLVM_DUMP_METHOD void dump() const { printJson(llvm::errs()); } }; } // end anonymous namespace @@ -599,7 +611,8 @@ public: // Part of public interface to class. RBFactory.getTreeFactory()); } - void print(Store store, raw_ostream &Out, const char* nl) override; + void printJson(raw_ostream &Out, Store S, const char *NL = "\n", + unsigned int Space = 0, bool IsDot = false) const override; void iterBindings(Store store, BindingsHandler& f) override { RegionBindingsRef B = getRegionBindings(store); @@ -1240,7 +1253,7 @@ RegionStoreManager::invalidateGlobalRegion(MemRegion::Kind K, // Bind the globals memory space to a new symbol that we will use to derive // the bindings for all globals. const GlobalsSpaceRegion *GS = MRMgr.getGlobalsRegion(K); - SVal V = svalBuilder.conjureSymbolVal(/* SymbolTag = */ (const void*) GS, Ex, LCtx, + SVal V = svalBuilder.conjureSymbolVal(/* symbolTag = */ (const void*) GS, Ex, LCtx, /* type does not matter */ Ctx.IntTy, Count); @@ -1660,7 +1673,7 @@ SVal RegionStoreManager::getBindingForElement(RegionBindingsConstRef B, const VarDecl *VD = VR->getDecl(); // Either the array or the array element has to be const. if (VD->getType().isConstQualified() || R->getElementType().isConstQualified()) { - if (const Expr *Init = VD->getInit()) { + if (const Expr *Init = VD->getAnyInitializer()) { if (const auto *InitList = dyn_cast<InitListExpr>(Init)) { // The array index has to be known. if (auto CI = R->getIndex().getAs<nonloc::ConcreteInt>()) { @@ -1750,7 +1763,7 @@ SVal RegionStoreManager::getBindingForField(RegionBindingsConstRef B, unsigned Index = FD->getFieldIndex(); // Either the record variable or the field has to be const qualified. if (RecordVarTy.isConstQualified() || Ty.isConstQualified()) - if (const Expr *Init = VD->getInit()) + if (const Expr *Init = VD->getAnyInitializer()) if (const auto *InitList = dyn_cast<InitListExpr>(Init)) { if (Index < InitList->getNumInits()) { if (const Expr *FieldInit = InitList->getInit(Index)) @@ -1932,7 +1945,10 @@ SVal RegionStoreManager::getBindingForVar(RegionBindingsConstRef B, const VarRegion *R) { // Check if the region has a binding. - if (const Optional<SVal> &V = B.getDirectBinding(R)) + if (Optional<SVal> V = B.getDirectBinding(R)) + return *V; + + if (Optional<SVal> V = B.getDefaultBinding(R)) return *V; // Lazily derive a value for the VarRegion. @@ -1945,7 +1961,7 @@ SVal RegionStoreManager::getBindingForVar(RegionBindingsConstRef B, // Is 'VD' declared constant? If so, retrieve the constant value. if (VD->getType().isConstQualified()) { - if (const Expr *Init = VD->getInit()) { + if (const Expr *Init = VD->getAnyInitializer()) { if (Optional<SVal> V = svalBuilder.getConstantVal(Init)) return *V; @@ -2339,12 +2355,64 @@ RegionBindingsRef RegionStoreManager::bindStruct(RegionBindingsConstRef B, if (V.isUnknown() || !V.getAs<nonloc::CompoundVal>()) return bindAggregate(B, R, UnknownVal()); + // The raw CompoundVal is essentially a symbolic InitListExpr: an (immutable) + // list of other values. It appears pretty much only when there's an actual + // initializer list expression in the program, and the analyzer tries to + // unwrap it as soon as possible. + // This code is where such unwrap happens: when the compound value is put into + // the object that it was supposed to initialize (it's an *initializer* list, + // after all), instead of binding the whole value to the whole object, we bind + // sub-values to sub-objects. Sub-values may themselves be compound values, + // and in this case the procedure becomes recursive. + // FIXME: The annoying part about compound values is that they don't carry + // any sort of information about which value corresponds to which sub-object. + // It's simply a list of values in the middle of nowhere; we expect to match + // them to sub-objects, essentially, "by index": first value binds to + // the first field, second value binds to the second field, etc. + // It would have been much safer to organize non-lazy compound values as + // a mapping from fields/bases to values. const nonloc::CompoundVal& CV = V.castAs<nonloc::CompoundVal>(); nonloc::CompoundVal::iterator VI = CV.begin(), VE = CV.end(); - RecordDecl::field_iterator FI, FE; RegionBindingsRef NewB(B); + // In C++17 aggregates may have base classes, handle those as well. + // They appear before fields in the initializer list / compound value. + if (const auto *CRD = dyn_cast<CXXRecordDecl>(RD)) { + // If the object was constructed with a constructor, its value is a + // LazyCompoundVal. If it's a raw CompoundVal, it means that we're + // performing aggregate initialization. The only exception from this + // rule is sending an Objective-C++ message that returns a C++ object + // to a nil receiver; in this case the semantics is to return a + // zero-initialized object even if it's a C++ object that doesn't have + // this sort of constructor; the CompoundVal is empty in this case. + assert((CRD->isAggregate() || (Ctx.getLangOpts().ObjC && VI == VE)) && + "Non-aggregates are constructed with a constructor!"); + + for (const auto &B : CRD->bases()) { + // (Multiple inheritance is fine though.) + assert(!B.isVirtual() && "Aggregates cannot have virtual base classes!"); + + if (VI == VE) + break; + + QualType BTy = B.getType(); + assert(BTy->isStructureOrClassType() && "Base classes must be classes!"); + + const CXXRecordDecl *BRD = BTy->getAsCXXRecordDecl(); + assert(BRD && "Base classes must be C++ classes!"); + + const CXXBaseObjectRegion *BR = + MRMgr.getCXXBaseObjectRegion(BRD, R, /*IsVirtual=*/false); + + NewB = bindStruct(NewB, BR, *VI); + + ++VI; + } + } + + RecordDecl::field_iterator FI, FE; + for (FI = RD->field_begin(), FE = RD->field_end(); FI != FE; ++FI) { if (VI == VE) @@ -2391,10 +2459,7 @@ RegionStoreManager::bindAggregate(RegionBindingsConstRef B, namespace { class RemoveDeadBindingsWorker : public ClusterAnalysis<RemoveDeadBindingsWorker> { - using ChildrenListTy = SmallVector<const SymbolDerived *, 4>; - using MapParentsToDerivedTy = llvm::DenseMap<SymbolRef, ChildrenListTy>; - - MapParentsToDerivedTy ParentsToDerived; + SmallVector<const SymbolicRegion *, 12> Postponed; SymbolReaper &SymReaper; const StackFrameContext *CurrentLCtx; @@ -2415,10 +2480,8 @@ public: bool AddToWorkList(const MemRegion *R); + bool UpdatePostponed(); void VisitBinding(SVal V); - -private: - void populateWorklistFromSymbol(SymbolRef s); }; } @@ -2438,11 +2501,10 @@ void RemoveDeadBindingsWorker::VisitAddedToCluster(const MemRegion *baseR, } if (const SymbolicRegion *SR = dyn_cast<SymbolicRegion>(baseR)) { - if (SymReaper.isLive(SR->getSymbol())) { + if (SymReaper.isLive(SR->getSymbol())) AddToWorkList(SR, &C); - } else if (const auto *SD = dyn_cast<SymbolDerived>(SR->getSymbol())) { - ParentsToDerived[SD->getParentSymbol()].push_back(SD); - } + else + Postponed.push_back(SR); return; } @@ -2455,7 +2517,7 @@ void RemoveDeadBindingsWorker::VisitAddedToCluster(const MemRegion *baseR, // CXXThisRegion in the current or parent location context is live. if (const CXXThisRegion *TR = dyn_cast<CXXThisRegion>(baseR)) { const auto *StackReg = - cast<StackArgumentsSpaceRegion>(TR->getSuperRegion()); + cast<StackArgumentsSpaceRegion>(TR->getSuperRegion()); const StackFrameContext *RegCtx = StackReg->getStackFrame(); if (CurrentLCtx && (RegCtx == CurrentLCtx || RegCtx->isParentOf(CurrentLCtx))) @@ -2499,15 +2561,6 @@ void RemoveDeadBindingsWorker::VisitBinding(SVal V) { // If V is a region, then add it to the worklist. if (const MemRegion *R = V.getAsRegion()) { AddToWorkList(R); - - if (const auto *TVR = dyn_cast<TypedValueRegion>(R)) { - DefinedOrUnknownSVal RVS = - RM.getSValBuilder().getRegionValueSymbolVal(TVR); - if (const MemRegion *SR = RVS.getAsRegion()) { - AddToWorkList(SR); - } - } - SymReaper.markLive(R); // All regions captured by a block are also live. @@ -2521,30 +2574,25 @@ void RemoveDeadBindingsWorker::VisitBinding(SVal V) { // Update the set of live symbols. - for (auto SI = V.symbol_begin(), SE = V.symbol_end(); SI != SE; ++SI) { - populateWorklistFromSymbol(*SI); - - for (const auto *SD : ParentsToDerived[*SI]) - populateWorklistFromSymbol(SD); - + for (auto SI = V.symbol_begin(), SE = V.symbol_end(); SI!=SE; ++SI) SymReaper.markLive(*SI); - } } -void RemoveDeadBindingsWorker::populateWorklistFromSymbol(SymbolRef S) { - if (const auto *SD = dyn_cast<SymbolData>(S)) { - if (Loc::isLocType(SD->getType()) && !SymReaper.isLive(SD)) { - const SymbolicRegion *SR = RM.getRegionManager().getSymbolicRegion(SD); - - if (B.contains(SR)) - AddToWorkList(SR); +bool RemoveDeadBindingsWorker::UpdatePostponed() { + // See if any postponed SymbolicRegions are actually live now, after + // having done a scan. + bool Changed = false; - const SymbolicRegion *SHR = - RM.getRegionManager().getSymbolicHeapRegion(SD); - if (B.contains(SHR)) - AddToWorkList(SHR); + for (auto I = Postponed.begin(), E = Postponed.end(); I != E; ++I) { + if (const SymbolicRegion *SR = *I) { + if (SymReaper.isLive(SR->getSymbol())) { + Changed |= AddToWorkList(SR); + *I = nullptr; + } } } + + return Changed; } StoreRef RegionStoreManager::removeDeadBindings(Store store, @@ -2560,7 +2608,7 @@ StoreRef RegionStoreManager::removeDeadBindings(Store store, W.AddToWorkList(*I); } - W.RunWorkList(); + do W.RunWorkList(); while (W.UpdatePostponed()); // We have now scanned the store, marking reachable regions and symbols // as live. We now remove all the regions that are dead from the store @@ -2581,11 +2629,18 @@ StoreRef RegionStoreManager::removeDeadBindings(Store store, // Utility methods. //===----------------------------------------------------------------------===// -void RegionStoreManager::print(Store store, raw_ostream &OS, - const char* nl) { - RegionBindingsRef B = getRegionBindings(store); - OS << "Store (direct and default bindings), " - << B.asStore() - << " :" << nl; - B.dump(OS, nl); +void RegionStoreManager::printJson(raw_ostream &Out, Store S, const char *NL, + unsigned int Space, bool IsDot) const { + RegionBindingsRef Bindings = getRegionBindings(S); + + Indent(Out, Space, IsDot) << "\"store\": "; + + if (Bindings.isEmpty()) { + Out << "null," << NL; + return; + } + + Out << "{ \"pointer\": \"" << Bindings.asStore() << "\", \"items\": [" << NL; + Bindings.printJson(Out, NL, Space + 1, IsDot); + Indent(Out, Space, IsDot) << "]}," << NL; } diff --git a/lib/StaticAnalyzer/Core/RetainSummaryManager.cpp b/lib/StaticAnalyzer/Core/RetainSummaryManager.cpp deleted file mode 100644 index 2e40cc33381c..000000000000 --- a/lib/StaticAnalyzer/Core/RetainSummaryManager.cpp +++ /dev/null @@ -1,1229 +0,0 @@ -//== RetainSummaryManager.cpp - Summaries for reference counting --*- C++ -*--// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This file defines summaries implementation for retain counting, which -// implements a reference count checker for Core Foundation, Cocoa -// and OSObject (on Mac OS X). -// -//===----------------------------------------------------------------------===// - -#include "clang/StaticAnalyzer/Core/RetainSummaryManager.h" -#include "clang/Analysis/DomainSpecific/CocoaConventions.h" -#include "clang/AST/Attr.h" -#include "clang/AST/DeclCXX.h" -#include "clang/AST/DeclObjC.h" -#include "clang/AST/ParentMap.h" -#include "clang/ASTMatchers/ASTMatchFinder.h" - -using namespace clang; -using namespace ento; - -template <class T> -constexpr static bool isOneOf() { - return false; -} - -/// Helper function to check whether the class is one of the -/// rest of varargs. -template <class T, class P, class... ToCompare> -constexpr static bool isOneOf() { - return std::is_same<T, P>::value || isOneOf<T, ToCompare...>(); -} - -namespace { - -/// Fake attribute class for RC* attributes. -struct GeneralizedReturnsRetainedAttr { - static bool classof(const Attr *A) { - if (auto AA = dyn_cast<AnnotateAttr>(A)) - return AA->getAnnotation() == "rc_ownership_returns_retained"; - return false; - } -}; - -struct GeneralizedReturnsNotRetainedAttr { - static bool classof(const Attr *A) { - if (auto AA = dyn_cast<AnnotateAttr>(A)) - return AA->getAnnotation() == "rc_ownership_returns_not_retained"; - return false; - } -}; - -struct GeneralizedConsumedAttr { - static bool classof(const Attr *A) { - if (auto AA = dyn_cast<AnnotateAttr>(A)) - return AA->getAnnotation() == "rc_ownership_consumed"; - return false; - } -}; - -} - -template <class T> -Optional<ObjKind> RetainSummaryManager::hasAnyEnabledAttrOf(const Decl *D, - QualType QT) { - ObjKind K; - if (isOneOf<T, CFConsumedAttr, CFReturnsRetainedAttr, - CFReturnsNotRetainedAttr>()) { - if (!TrackObjCAndCFObjects) - return None; - - K = ObjKind::CF; - } else if (isOneOf<T, NSConsumedAttr, NSConsumesSelfAttr, - NSReturnsAutoreleasedAttr, NSReturnsRetainedAttr, - NSReturnsNotRetainedAttr, NSConsumesSelfAttr>()) { - - if (!TrackObjCAndCFObjects) - return None; - - if (isOneOf<T, NSReturnsRetainedAttr, NSReturnsAutoreleasedAttr, - NSReturnsNotRetainedAttr>() && - !cocoa::isCocoaObjectRef(QT)) - return None; - K = ObjKind::ObjC; - } else if (isOneOf<T, OSConsumedAttr, OSConsumesThisAttr, - OSReturnsNotRetainedAttr, OSReturnsRetainedAttr, - OSReturnsRetainedOnZeroAttr, - OSReturnsRetainedOnNonZeroAttr>()) { - if (!TrackOSObjects) - return None; - K = ObjKind::OS; - } else if (isOneOf<T, GeneralizedReturnsNotRetainedAttr, - GeneralizedReturnsRetainedAttr, - GeneralizedConsumedAttr>()) { - K = ObjKind::Generalized; - } else { - llvm_unreachable("Unexpected attribute"); - } - if (D->hasAttr<T>()) - return K; - return None; -} - -template <class T1, class T2, class... Others> -Optional<ObjKind> RetainSummaryManager::hasAnyEnabledAttrOf(const Decl *D, - QualType QT) { - if (auto Out = hasAnyEnabledAttrOf<T1>(D, QT)) - return Out; - return hasAnyEnabledAttrOf<T2, Others...>(D, QT); -} - -const RetainSummary * -RetainSummaryManager::getPersistentSummary(const RetainSummary &OldSumm) { - // Unique "simple" summaries -- those without ArgEffects. - if (OldSumm.isSimple()) { - ::llvm::FoldingSetNodeID ID; - OldSumm.Profile(ID); - - void *Pos; - CachedSummaryNode *N = SimpleSummaries.FindNodeOrInsertPos(ID, Pos); - - if (!N) { - N = (CachedSummaryNode *) BPAlloc.Allocate<CachedSummaryNode>(); - new (N) CachedSummaryNode(OldSumm); - SimpleSummaries.InsertNode(N, Pos); - } - - return &N->getValue(); - } - - RetainSummary *Summ = (RetainSummary *) BPAlloc.Allocate<RetainSummary>(); - new (Summ) RetainSummary(OldSumm); - return Summ; -} - -static bool isSubclass(const Decl *D, - StringRef ClassName) { - using namespace ast_matchers; - DeclarationMatcher SubclassM = cxxRecordDecl(isSameOrDerivedFrom(ClassName)); - return !(match(SubclassM, *D, D->getASTContext()).empty()); -} - -static bool isOSObjectSubclass(const Decl *D) { - return isSubclass(D, "OSObject"); -} - -static bool isOSObjectDynamicCast(StringRef S) { - return S == "safeMetaCast"; -} - -static bool isOSIteratorSubclass(const Decl *D) { - return isSubclass(D, "OSIterator"); -} - -static bool hasRCAnnotation(const Decl *D, StringRef rcAnnotation) { - for (const auto *Ann : D->specific_attrs<AnnotateAttr>()) { - if (Ann->getAnnotation() == rcAnnotation) - return true; - } - return false; -} - -static bool isRetain(const FunctionDecl *FD, StringRef FName) { - return FName.startswith_lower("retain") || FName.endswith_lower("retain"); -} - -static bool isRelease(const FunctionDecl *FD, StringRef FName) { - return FName.startswith_lower("release") || FName.endswith_lower("release"); -} - -static bool isAutorelease(const FunctionDecl *FD, StringRef FName) { - return FName.startswith_lower("autorelease") || - FName.endswith_lower("autorelease"); -} - -static bool isMakeCollectable(StringRef FName) { - return FName.contains_lower("MakeCollectable"); -} - -/// A function is OSObject related if it is declared on a subclass -/// of OSObject, or any of the parameters is a subclass of an OSObject. -static bool isOSObjectRelated(const CXXMethodDecl *MD) { - if (isOSObjectSubclass(MD->getParent())) - return true; - - for (ParmVarDecl *Param : MD->parameters()) { - QualType PT = Param->getType()->getPointeeType(); - if (!PT.isNull()) - if (CXXRecordDecl *RD = PT->getAsCXXRecordDecl()) - if (isOSObjectSubclass(RD)) - return true; - } - - return false; -} - -const RetainSummary * -RetainSummaryManager::getSummaryForOSObject(const FunctionDecl *FD, - StringRef FName, QualType RetTy) { - if (RetTy->isPointerType()) { - const CXXRecordDecl *PD = RetTy->getPointeeType()->getAsCXXRecordDecl(); - if (PD && isOSObjectSubclass(PD)) { - if (const IdentifierInfo *II = FD->getIdentifier()) { - if (isOSObjectDynamicCast(II->getName())) - return getDefaultSummary(); - - // All objects returned with functions *not* starting with - // get, or iterators, are returned at +1. - if ((!II->getName().startswith("get") && - !II->getName().startswith("Get")) || - isOSIteratorSubclass(PD)) { - return getOSSummaryCreateRule(FD); - } else { - return getOSSummaryGetRule(FD); - } - } - } - } - - if (const auto *MD = dyn_cast<CXXMethodDecl>(FD)) { - const CXXRecordDecl *Parent = MD->getParent(); - if (TrackOSObjects && Parent && isOSObjectSubclass(Parent)) { - if (FName == "release") - return getOSSummaryReleaseRule(FD); - - if (FName == "retain") - return getOSSummaryRetainRule(FD); - - if (FName == "free") - return getOSSummaryFreeRule(FD); - - if (MD->getOverloadedOperator() == OO_New) - return getOSSummaryCreateRule(MD); - } - } - - return nullptr; -} - -const RetainSummary *RetainSummaryManager::getSummaryForObjCOrCFObject( - const FunctionDecl *FD, - StringRef FName, - QualType RetTy, - const FunctionType *FT, - bool &AllowAnnotations) { - - ArgEffects ScratchArgs(AF.getEmptyMap()); - - std::string RetTyName = RetTy.getAsString(); - if (FName == "pthread_create" || FName == "pthread_setspecific") { - // Part of: <rdar://problem/7299394> and <rdar://problem/11282706>. - // This will be addressed better with IPA. - return getPersistentStopSummary(); - } else if(FName == "NSMakeCollectable") { - // Handle: id NSMakeCollectable(CFTypeRef) - AllowAnnotations = false; - return RetTy->isObjCIdType() ? getUnarySummary(FT, DoNothing) - : getPersistentStopSummary(); - } else if (FName == "CMBufferQueueDequeueAndRetain" || - FName == "CMBufferQueueDequeueIfDataReadyAndRetain") { - // Part of: <rdar://problem/39390714>. - return getPersistentSummary(RetEffect::MakeOwned(ObjKind::CF), - ScratchArgs, - ArgEffect(DoNothing), - ArgEffect(DoNothing)); - } else if (FName == "CFPlugInInstanceCreate") { - return getPersistentSummary(RetEffect::MakeNoRet(), ScratchArgs); - } else if (FName == "IORegistryEntrySearchCFProperty" || - (RetTyName == "CFMutableDictionaryRef" && - (FName == "IOBSDNameMatching" || FName == "IOServiceMatching" || - FName == "IOServiceNameMatching" || - FName == "IORegistryEntryIDMatching" || - FName == "IOOpenFirmwarePathMatching"))) { - // Part of <rdar://problem/6961230>. (IOKit) - // This should be addressed using a API table. - return getPersistentSummary(RetEffect::MakeOwned(ObjKind::CF), ScratchArgs, - ArgEffect(DoNothing), ArgEffect(DoNothing)); - } else if (FName == "IOServiceGetMatchingService" || - FName == "IOServiceGetMatchingServices") { - // FIXES: <rdar://problem/6326900> - // This should be addressed using a API table. This strcmp is also - // a little gross, but there is no need to super optimize here. - ScratchArgs = AF.add(ScratchArgs, 1, ArgEffect(DecRef, ObjKind::CF)); - return getPersistentSummary(RetEffect::MakeNoRet(), - ScratchArgs, - ArgEffect(DoNothing), ArgEffect(DoNothing)); - } else if (FName == "IOServiceAddNotification" || - FName == "IOServiceAddMatchingNotification") { - // Part of <rdar://problem/6961230>. (IOKit) - // This should be addressed using a API table. - ScratchArgs = AF.add(ScratchArgs, 2, ArgEffect(DecRef, ObjKind::CF)); - return getPersistentSummary(RetEffect::MakeNoRet(), - ScratchArgs, - ArgEffect(DoNothing), ArgEffect(DoNothing)); - } else if (FName == "CVPixelBufferCreateWithBytes") { - // FIXES: <rdar://problem/7283567> - // Eventually this can be improved by recognizing that the pixel - // buffer passed to CVPixelBufferCreateWithBytes is released via - // a callback and doing full IPA to make sure this is done correctly. - // FIXME: This function has an out parameter that returns an - // allocated object. - ScratchArgs = AF.add(ScratchArgs, 7, ArgEffect(StopTracking)); - return getPersistentSummary(RetEffect::MakeNoRet(), - ScratchArgs, - ArgEffect(DoNothing), ArgEffect(DoNothing)); - } else if (FName == "CGBitmapContextCreateWithData") { - // FIXES: <rdar://problem/7358899> - // Eventually this can be improved by recognizing that 'releaseInfo' - // passed to CGBitmapContextCreateWithData is released via - // a callback and doing full IPA to make sure this is done correctly. - ScratchArgs = AF.add(ScratchArgs, 8, ArgEffect(ArgEffect(StopTracking))); - return getPersistentSummary(RetEffect::MakeOwned(ObjKind::CF), ScratchArgs, - ArgEffect(DoNothing), ArgEffect(DoNothing)); - } else if (FName == "CVPixelBufferCreateWithPlanarBytes") { - // FIXES: <rdar://problem/7283567> - // Eventually this can be improved by recognizing that the pixel - // buffer passed to CVPixelBufferCreateWithPlanarBytes is released - // via a callback and doing full IPA to make sure this is done - // correctly. - ScratchArgs = AF.add(ScratchArgs, 12, ArgEffect(StopTracking)); - return getPersistentSummary(RetEffect::MakeNoRet(), - ScratchArgs, - ArgEffect(DoNothing), ArgEffect(DoNothing)); - } else if (FName == "VTCompressionSessionEncodeFrame") { - // The context argument passed to VTCompressionSessionEncodeFrame() - // is passed to the callback specified when creating the session - // (e.g. with VTCompressionSessionCreate()) which can release it. - // To account for this possibility, conservatively stop tracking - // the context. - ScratchArgs = AF.add(ScratchArgs, 5, ArgEffect(StopTracking)); - return getPersistentSummary(RetEffect::MakeNoRet(), - ScratchArgs, - ArgEffect(DoNothing), ArgEffect(DoNothing)); - } else if (FName == "dispatch_set_context" || - FName == "xpc_connection_set_context") { - // <rdar://problem/11059275> - The analyzer currently doesn't have - // a good way to reason about the finalizer function for libdispatch. - // If we pass a context object that is memory managed, stop tracking it. - // <rdar://problem/13783514> - Same problem, but for XPC. - // FIXME: this hack should possibly go away once we can handle - // libdispatch and XPC finalizers. - ScratchArgs = AF.add(ScratchArgs, 1, ArgEffect(StopTracking)); - return getPersistentSummary(RetEffect::MakeNoRet(), - ScratchArgs, - ArgEffect(DoNothing), ArgEffect(DoNothing)); - } else if (FName.startswith("NSLog")) { - return getDoNothingSummary(); - } else if (FName.startswith("NS") && - (FName.find("Insert") != StringRef::npos)) { - // Whitelist NSXXInsertXX, for example NSMapInsertIfAbsent, since they can - // be deallocated by NSMapRemove. (radar://11152419) - ScratchArgs = AF.add(ScratchArgs, 1, ArgEffect(StopTracking)); - ScratchArgs = AF.add(ScratchArgs, 2, ArgEffect(StopTracking)); - return getPersistentSummary(RetEffect::MakeNoRet(), - ScratchArgs, ArgEffect(DoNothing), - ArgEffect(DoNothing)); - } - - if (RetTy->isPointerType()) { - - // For CoreFoundation ('CF') types. - if (cocoa::isRefType(RetTy, "CF", FName)) { - if (isRetain(FD, FName)) { - // CFRetain isn't supposed to be annotated. However, this may as - // well be a user-made "safe" CFRetain function that is incorrectly - // annotated as cf_returns_retained due to lack of better options. - // We want to ignore such annotation. - AllowAnnotations = false; - - return getUnarySummary(FT, IncRef); - } else if (isAutorelease(FD, FName)) { - // The headers use cf_consumed, but we can fully model CFAutorelease - // ourselves. - AllowAnnotations = false; - - return getUnarySummary(FT, Autorelease); - } else if (isMakeCollectable(FName)) { - AllowAnnotations = false; - return getUnarySummary(FT, DoNothing); - } else { - return getCFCreateGetRuleSummary(FD); - } - } - - // For CoreGraphics ('CG') and CoreVideo ('CV') types. - if (cocoa::isRefType(RetTy, "CG", FName) || - cocoa::isRefType(RetTy, "CV", FName)) { - if (isRetain(FD, FName)) - return getUnarySummary(FT, IncRef); - else - return getCFCreateGetRuleSummary(FD); - } - - // For all other CF-style types, use the Create/Get - // rule for summaries but don't support Retain functions - // with framework-specific prefixes. - if (coreFoundation::isCFObjectRef(RetTy)) { - return getCFCreateGetRuleSummary(FD); - } - - if (FD->hasAttr<CFAuditedTransferAttr>()) { - return getCFCreateGetRuleSummary(FD); - } - } - - // Check for release functions, the only kind of functions that we care - // about that don't return a pointer type. - if (FName.startswith("CG") || FName.startswith("CF")) { - // Test for 'CGCF'. - FName = FName.substr(FName.startswith("CGCF") ? 4 : 2); - - if (isRelease(FD, FName)) - return getUnarySummary(FT, DecRef); - else { - assert(ScratchArgs.isEmpty()); - // Remaining CoreFoundation and CoreGraphics functions. - // We use to assume that they all strictly followed the ownership idiom - // and that ownership cannot be transferred. While this is technically - // correct, many methods allow a tracked object to escape. For example: - // - // CFMutableDictionaryRef x = CFDictionaryCreateMutable(...); - // CFDictionaryAddValue(y, key, x); - // CFRelease(x); - // ... it is okay to use 'x' since 'y' has a reference to it - // - // We handle this and similar cases with the follow heuristic. If the - // function name contains "InsertValue", "SetValue", "AddValue", - // "AppendValue", or "SetAttribute", then we assume that arguments may - // "escape." This means that something else holds on to the object, - // allowing it be used even after its local retain count drops to 0. - ArgEffectKind E = - (StrInStrNoCase(FName, "InsertValue") != StringRef::npos || - StrInStrNoCase(FName, "AddValue") != StringRef::npos || - StrInStrNoCase(FName, "SetValue") != StringRef::npos || - StrInStrNoCase(FName, "AppendValue") != StringRef::npos || - StrInStrNoCase(FName, "SetAttribute") != StringRef::npos) - ? MayEscape - : DoNothing; - - return getPersistentSummary(RetEffect::MakeNoRet(), ScratchArgs, - ArgEffect(DoNothing), ArgEffect(E, ObjKind::CF)); - } - } - - return nullptr; -} - -const RetainSummary * -RetainSummaryManager::generateSummary(const FunctionDecl *FD, - bool &AllowAnnotations) { - // We generate "stop" summaries for implicitly defined functions. - if (FD->isImplicit()) - return getPersistentStopSummary(); - - const IdentifierInfo *II = FD->getIdentifier(); - - StringRef FName = II ? II->getName() : ""; - - // Strip away preceding '_'. Doing this here will effect all the checks - // down below. - FName = FName.substr(FName.find_first_not_of('_')); - - // Inspect the result type. Strip away any typedefs. - const auto *FT = FD->getType()->getAs<FunctionType>(); - QualType RetTy = FT->getReturnType(); - - if (TrackOSObjects) - if (const RetainSummary *S = getSummaryForOSObject(FD, FName, RetTy)) - return S; - - if (TrackObjCAndCFObjects) - if (const RetainSummary *S = - getSummaryForObjCOrCFObject(FD, FName, RetTy, FT, AllowAnnotations)) - return S; - - if (const auto *MD = dyn_cast<CXXMethodDecl>(FD)) - if (!(TrackOSObjects && isOSObjectRelated(MD))) - return getPersistentSummary(RetEffect::MakeNoRet(), - ArgEffects(AF.getEmptyMap()), - ArgEffect(DoNothing), - ArgEffect(StopTracking), - ArgEffect(DoNothing)); - - return getDefaultSummary(); -} - -const RetainSummary * -RetainSummaryManager::getFunctionSummary(const FunctionDecl *FD) { - // If we don't know what function we're calling, use our default summary. - if (!FD) - return getDefaultSummary(); - - // Look up a summary in our cache of FunctionDecls -> Summaries. - FuncSummariesTy::iterator I = FuncSummaries.find(FD); - if (I != FuncSummaries.end()) - return I->second; - - // No summary? Generate one. - bool AllowAnnotations = true; - const RetainSummary *S = generateSummary(FD, AllowAnnotations); - - // Annotations override defaults. - if (AllowAnnotations) - updateSummaryFromAnnotations(S, FD); - - FuncSummaries[FD] = S; - return S; -} - -//===----------------------------------------------------------------------===// -// Summary creation for functions (largely uses of Core Foundation). -//===----------------------------------------------------------------------===// - -static ArgEffect getStopTrackingHardEquivalent(ArgEffect E) { - switch (E.getKind()) { - case DoNothing: - case Autorelease: - case DecRefBridgedTransferred: - case IncRef: - case UnretainedOutParameter: - case RetainedOutParameter: - case RetainedOutParameterOnZero: - case RetainedOutParameterOnNonZero: - case MayEscape: - case StopTracking: - case StopTrackingHard: - return E.withKind(StopTrackingHard); - case DecRef: - case DecRefAndStopTrackingHard: - return E.withKind(DecRefAndStopTrackingHard); - case Dealloc: - return E.withKind(Dealloc); - } - - llvm_unreachable("Unknown ArgEffect kind"); -} - -void RetainSummaryManager::updateSummaryForCall(const RetainSummary *&S, - const CallEvent &Call) { - if (Call.hasNonZeroCallbackArg()) { - ArgEffect RecEffect = - getStopTrackingHardEquivalent(S->getReceiverEffect()); - ArgEffect DefEffect = - getStopTrackingHardEquivalent(S->getDefaultArgEffect()); - - ArgEffects ScratchArgs(AF.getEmptyMap()); - ArgEffects CustomArgEffects = S->getArgEffects(); - for (ArgEffects::iterator I = CustomArgEffects.begin(), - E = CustomArgEffects.end(); - I != E; ++I) { - ArgEffect Translated = getStopTrackingHardEquivalent(I->second); - if (Translated.getKind() != DefEffect.getKind()) - ScratchArgs = AF.add(ScratchArgs, I->first, Translated); - } - - RetEffect RE = RetEffect::MakeNoRetHard(); - - // Special cases where the callback argument CANNOT free the return value. - // This can generally only happen if we know that the callback will only be - // called when the return value is already being deallocated. - if (const SimpleFunctionCall *FC = dyn_cast<SimpleFunctionCall>(&Call)) { - if (IdentifierInfo *Name = FC->getDecl()->getIdentifier()) { - // When the CGBitmapContext is deallocated, the callback here will free - // the associated data buffer. - // The callback in dispatch_data_create frees the buffer, but not - // the data object. - if (Name->isStr("CGBitmapContextCreateWithData") || - Name->isStr("dispatch_data_create")) - RE = S->getRetEffect(); - } - } - - S = getPersistentSummary(RE, ScratchArgs, RecEffect, DefEffect); - } - - // Special case '[super init];' and '[self init];' - // - // Even though calling '[super init]' without assigning the result to self - // and checking if the parent returns 'nil' is a bad pattern, it is common. - // Additionally, our Self Init checker already warns about it. To avoid - // overwhelming the user with messages from both checkers, we model the case - // of '[super init]' in cases when it is not consumed by another expression - // as if the call preserves the value of 'self'; essentially, assuming it can - // never fail and return 'nil'. - // Note, we don't want to just stop tracking the value since we want the - // RetainCount checker to report leaks and use-after-free if SelfInit checker - // is turned off. - if (const ObjCMethodCall *MC = dyn_cast<ObjCMethodCall>(&Call)) { - if (MC->getMethodFamily() == OMF_init && MC->isReceiverSelfOrSuper()) { - - // Check if the message is not consumed, we know it will not be used in - // an assignment, ex: "self = [super init]". - const Expr *ME = MC->getOriginExpr(); - const LocationContext *LCtx = MC->getLocationContext(); - ParentMap &PM = LCtx->getAnalysisDeclContext()->getParentMap(); - if (!PM.isConsumedExpr(ME)) { - RetainSummaryTemplate ModifiableSummaryTemplate(S, *this); - ModifiableSummaryTemplate->setReceiverEffect(ArgEffect(DoNothing)); - ModifiableSummaryTemplate->setRetEffect(RetEffect::MakeNoRet()); - } - } - } -} - -const RetainSummary * -RetainSummaryManager::getSummary(const CallEvent &Call, - QualType ReceiverType) { - const RetainSummary *Summ; - switch (Call.getKind()) { - case CE_Function: - case CE_CXXMember: - case CE_CXXMemberOperator: - case CE_CXXConstructor: - case CE_CXXAllocator: - Summ = getFunctionSummary(cast_or_null<FunctionDecl>(Call.getDecl())); - break; - case CE_Block: - case CE_CXXDestructor: - // FIXME: These calls are currently unsupported. - return getPersistentStopSummary(); - case CE_ObjCMessage: { - const ObjCMethodCall &Msg = cast<ObjCMethodCall>(Call); - if (Msg.isInstanceMessage()) - Summ = getInstanceMethodSummary(Msg, ReceiverType); - else - Summ = getClassMethodSummary(Msg); - break; - } - } - - updateSummaryForCall(Summ, Call); - - assert(Summ && "Unknown call type?"); - return Summ; -} - - -const RetainSummary * -RetainSummaryManager::getCFCreateGetRuleSummary(const FunctionDecl *FD) { - if (coreFoundation::followsCreateRule(FD)) - return getCFSummaryCreateRule(FD); - - return getCFSummaryGetRule(FD); -} - -bool RetainSummaryManager::isTrustedReferenceCountImplementation( - const FunctionDecl *FD) { - return hasRCAnnotation(FD, "rc_ownership_trusted_implementation"); -} - -Optional<RetainSummaryManager::BehaviorSummary> -RetainSummaryManager::canEval(const CallExpr *CE, const FunctionDecl *FD, - bool &hasTrustedImplementationAnnotation) { - - IdentifierInfo *II = FD->getIdentifier(); - if (!II) - return None; - - StringRef FName = II->getName(); - FName = FName.substr(FName.find_first_not_of('_')); - - QualType ResultTy = CE->getCallReturnType(Ctx); - if (ResultTy->isObjCIdType()) { - if (II->isStr("NSMakeCollectable")) - return BehaviorSummary::Identity; - } else if (ResultTy->isPointerType()) { - // Handle: (CF|CG|CV)Retain - // CFAutorelease - // It's okay to be a little sloppy here. - if (FName == "CMBufferQueueDequeueAndRetain" || - FName == "CMBufferQueueDequeueIfDataReadyAndRetain") { - // Part of: <rdar://problem/39390714>. - // These are not retain. They just return something and retain it. - return None; - } - if (cocoa::isRefType(ResultTy, "CF", FName) || - cocoa::isRefType(ResultTy, "CG", FName) || - cocoa::isRefType(ResultTy, "CV", FName)) - if (isRetain(FD, FName) || isAutorelease(FD, FName) || - isMakeCollectable(FName)) - return BehaviorSummary::Identity; - - // safeMetaCast is called by OSDynamicCast. - // We assume that OSDynamicCast is either an identity (cast is OK, - // the input was non-zero), - // or that it returns zero (when the cast failed, or the input - // was zero). - if (TrackOSObjects && isOSObjectDynamicCast(FName)) { - return BehaviorSummary::IdentityOrZero; - } - - const FunctionDecl* FDD = FD->getDefinition(); - if (FDD && isTrustedReferenceCountImplementation(FDD)) { - hasTrustedImplementationAnnotation = true; - return BehaviorSummary::Identity; - } - } - - if (const auto *MD = dyn_cast<CXXMethodDecl>(FD)) { - const CXXRecordDecl *Parent = MD->getParent(); - if (TrackOSObjects && Parent && isOSObjectSubclass(Parent)) - if (FName == "release" || FName == "retain") - return BehaviorSummary::NoOp; - } - - return None; -} - -const RetainSummary * -RetainSummaryManager::getUnarySummary(const FunctionType* FT, - ArgEffectKind AE) { - - // Unary functions have no arg effects by definition. - ArgEffects ScratchArgs(AF.getEmptyMap()); - - // Sanity check that this is *really* a unary function. This can - // happen if people do weird things. - const FunctionProtoType* FTP = dyn_cast<FunctionProtoType>(FT); - if (!FTP || FTP->getNumParams() != 1) - return getPersistentStopSummary(); - - ArgEffect Effect(AE, ObjKind::CF); - - ScratchArgs = AF.add(ScratchArgs, 0, Effect); - return getPersistentSummary(RetEffect::MakeNoRet(), - ScratchArgs, - ArgEffect(DoNothing), ArgEffect(DoNothing)); -} - -const RetainSummary * -RetainSummaryManager::getOSSummaryRetainRule(const FunctionDecl *FD) { - return getPersistentSummary(RetEffect::MakeNoRet(), - AF.getEmptyMap(), - /*ReceiverEff=*/ArgEffect(DoNothing), - /*DefaultEff=*/ArgEffect(DoNothing), - /*ThisEff=*/ArgEffect(IncRef, ObjKind::OS)); -} - -const RetainSummary * -RetainSummaryManager::getOSSummaryReleaseRule(const FunctionDecl *FD) { - return getPersistentSummary(RetEffect::MakeNoRet(), - AF.getEmptyMap(), - /*ReceiverEff=*/ArgEffect(DoNothing), - /*DefaultEff=*/ArgEffect(DoNothing), - /*ThisEff=*/ArgEffect(DecRef, ObjKind::OS)); -} - -const RetainSummary * -RetainSummaryManager::getOSSummaryFreeRule(const FunctionDecl *FD) { - return getPersistentSummary(RetEffect::MakeNoRet(), - AF.getEmptyMap(), - /*ReceiverEff=*/ArgEffect(DoNothing), - /*DefaultEff=*/ArgEffect(DoNothing), - /*ThisEff=*/ArgEffect(Dealloc, ObjKind::OS)); -} - -const RetainSummary * -RetainSummaryManager::getOSSummaryCreateRule(const FunctionDecl *FD) { - return getPersistentSummary(RetEffect::MakeOwned(ObjKind::OS), - AF.getEmptyMap()); -} - -const RetainSummary * -RetainSummaryManager::getOSSummaryGetRule(const FunctionDecl *FD) { - return getPersistentSummary(RetEffect::MakeNotOwned(ObjKind::OS), - AF.getEmptyMap()); -} - -const RetainSummary * -RetainSummaryManager::getCFSummaryCreateRule(const FunctionDecl *FD) { - return getPersistentSummary(RetEffect::MakeOwned(ObjKind::CF), - ArgEffects(AF.getEmptyMap())); -} - -const RetainSummary * -RetainSummaryManager::getCFSummaryGetRule(const FunctionDecl *FD) { - return getPersistentSummary(RetEffect::MakeNotOwned(ObjKind::CF), - ArgEffects(AF.getEmptyMap()), - ArgEffect(DoNothing), ArgEffect(DoNothing)); -} - - - - -//===----------------------------------------------------------------------===// -// Summary creation for Selectors. -//===----------------------------------------------------------------------===// - -Optional<RetEffect> -RetainSummaryManager::getRetEffectFromAnnotations(QualType RetTy, - const Decl *D) { - if (hasAnyEnabledAttrOf<NSReturnsRetainedAttr>(D, RetTy)) - return ObjCAllocRetE; - - if (auto K = hasAnyEnabledAttrOf<CFReturnsRetainedAttr, OSReturnsRetainedAttr, - GeneralizedReturnsRetainedAttr>(D, RetTy)) - return RetEffect::MakeOwned(*K); - - if (auto K = hasAnyEnabledAttrOf< - CFReturnsNotRetainedAttr, OSReturnsNotRetainedAttr, - GeneralizedReturnsNotRetainedAttr, NSReturnsNotRetainedAttr, - NSReturnsAutoreleasedAttr>(D, RetTy)) - return RetEffect::MakeNotOwned(*K); - - if (const auto *MD = dyn_cast<CXXMethodDecl>(D)) - for (const auto *PD : MD->overridden_methods()) - if (auto RE = getRetEffectFromAnnotations(RetTy, PD)) - return RE; - - return None; -} - -/// \return Whether the chain of typedefs starting from {@code QT} -/// has a typedef with a given name {@code Name}. -static bool hasTypedefNamed(QualType QT, - StringRef Name) { - while (auto *T = dyn_cast<TypedefType>(QT)) { - const auto &Context = T->getDecl()->getASTContext(); - if (T->getDecl()->getIdentifier() == &Context.Idents.get(Name)) - return true; - QT = T->getDecl()->getUnderlyingType(); - } - return false; -} - -static QualType getCallableReturnType(const NamedDecl *ND) { - if (const auto *FD = dyn_cast<FunctionDecl>(ND)) { - return FD->getReturnType(); - } else if (const auto *MD = dyn_cast<ObjCMethodDecl>(ND)) { - return MD->getReturnType(); - } else { - llvm_unreachable("Unexpected decl"); - } -} - -bool RetainSummaryManager::applyParamAnnotationEffect( - const ParmVarDecl *pd, unsigned parm_idx, const NamedDecl *FD, - RetainSummaryTemplate &Template) { - QualType QT = pd->getType(); - if (auto K = - hasAnyEnabledAttrOf<NSConsumedAttr, CFConsumedAttr, OSConsumedAttr, - GeneralizedConsumedAttr>(pd, QT)) { - Template->addArg(AF, parm_idx, ArgEffect(DecRef, *K)); - return true; - } else if (auto K = hasAnyEnabledAttrOf< - CFReturnsRetainedAttr, OSReturnsRetainedAttr, - OSReturnsRetainedOnNonZeroAttr, OSReturnsRetainedOnZeroAttr, - GeneralizedReturnsRetainedAttr>(pd, QT)) { - - // For OSObjects, we try to guess whether the object is created based - // on the return value. - if (K == ObjKind::OS) { - QualType QT = getCallableReturnType(FD); - - bool HasRetainedOnZero = pd->hasAttr<OSReturnsRetainedOnZeroAttr>(); - bool HasRetainedOnNonZero = pd->hasAttr<OSReturnsRetainedOnNonZeroAttr>(); - - // The usual convention is to create an object on non-zero return, but - // it's reverted if the typedef chain has a typedef kern_return_t, - // because kReturnSuccess constant is defined as zero. - // The convention can be overwritten by custom attributes. - bool SuccessOnZero = - HasRetainedOnZero || - (hasTypedefNamed(QT, "kern_return_t") && !HasRetainedOnNonZero); - bool ShouldSplit = !QT.isNull() && !QT->isVoidType(); - ArgEffectKind AK = RetainedOutParameter; - if (ShouldSplit && SuccessOnZero) { - AK = RetainedOutParameterOnZero; - } else if (ShouldSplit && (!SuccessOnZero || HasRetainedOnNonZero)) { - AK = RetainedOutParameterOnNonZero; - } - Template->addArg(AF, parm_idx, ArgEffect(AK, ObjKind::OS)); - } - - // For others: - // Do nothing. Retained out parameters will either point to a +1 reference - // or NULL, but the way you check for failure differs depending on the - // API. Consequently, we don't have a good way to track them yet. - return true; - } else if (auto K = hasAnyEnabledAttrOf<CFReturnsNotRetainedAttr, - OSReturnsNotRetainedAttr, - GeneralizedReturnsNotRetainedAttr>( - pd, QT)) { - Template->addArg(AF, parm_idx, ArgEffect(UnretainedOutParameter, *K)); - return true; - } - - if (const auto *MD = dyn_cast<CXXMethodDecl>(FD)) { - for (const auto *OD : MD->overridden_methods()) { - const ParmVarDecl *OP = OD->parameters()[parm_idx]; - if (applyParamAnnotationEffect(OP, parm_idx, OD, Template)) - return true; - } - } - - return false; -} - -void -RetainSummaryManager::updateSummaryFromAnnotations(const RetainSummary *&Summ, - const FunctionDecl *FD) { - if (!FD) - return; - - assert(Summ && "Must have a summary to add annotations to."); - RetainSummaryTemplate Template(Summ, *this); - - // Effects on the parameters. - unsigned parm_idx = 0; - for (auto pi = FD->param_begin(), - pe = FD->param_end(); pi != pe; ++pi, ++parm_idx) - applyParamAnnotationEffect(*pi, parm_idx, FD, Template); - - QualType RetTy = FD->getReturnType(); - if (Optional<RetEffect> RetE = getRetEffectFromAnnotations(RetTy, FD)) - Template->setRetEffect(*RetE); - - if (hasAnyEnabledAttrOf<OSConsumesThisAttr>(FD, RetTy)) - Template->setThisEffect(ArgEffect(DecRef, ObjKind::OS)); -} - -void -RetainSummaryManager::updateSummaryFromAnnotations(const RetainSummary *&Summ, - const ObjCMethodDecl *MD) { - if (!MD) - return; - - assert(Summ && "Must have a valid summary to add annotations to"); - RetainSummaryTemplate Template(Summ, *this); - - // Effects on the receiver. - if (hasAnyEnabledAttrOf<NSConsumesSelfAttr>(MD, MD->getReturnType())) - Template->setReceiverEffect(ArgEffect(DecRef, ObjKind::ObjC)); - - // Effects on the parameters. - unsigned parm_idx = 0; - for (auto pi = MD->param_begin(), pe = MD->param_end(); pi != pe; - ++pi, ++parm_idx) - applyParamAnnotationEffect(*pi, parm_idx, MD, Template); - - QualType RetTy = MD->getReturnType(); - if (Optional<RetEffect> RetE = getRetEffectFromAnnotations(RetTy, MD)) - Template->setRetEffect(*RetE); -} - -const RetainSummary * -RetainSummaryManager::getStandardMethodSummary(const ObjCMethodDecl *MD, - Selector S, QualType RetTy) { - // Any special effects? - ArgEffect ReceiverEff = ArgEffect(DoNothing, ObjKind::ObjC); - RetEffect ResultEff = RetEffect::MakeNoRet(); - - // Check the method family, and apply any default annotations. - switch (MD ? MD->getMethodFamily() : S.getMethodFamily()) { - case OMF_None: - case OMF_initialize: - case OMF_performSelector: - // Assume all Objective-C methods follow Cocoa Memory Management rules. - // FIXME: Does the non-threaded performSelector family really belong here? - // The selector could be, say, @selector(copy). - if (cocoa::isCocoaObjectRef(RetTy)) - ResultEff = RetEffect::MakeNotOwned(ObjKind::ObjC); - else if (coreFoundation::isCFObjectRef(RetTy)) { - // ObjCMethodDecl currently doesn't consider CF objects as valid return - // values for alloc, new, copy, or mutableCopy, so we have to - // double-check with the selector. This is ugly, but there aren't that - // many Objective-C methods that return CF objects, right? - if (MD) { - switch (S.getMethodFamily()) { - case OMF_alloc: - case OMF_new: - case OMF_copy: - case OMF_mutableCopy: - ResultEff = RetEffect::MakeOwned(ObjKind::CF); - break; - default: - ResultEff = RetEffect::MakeNotOwned(ObjKind::CF); - break; - } - } else { - ResultEff = RetEffect::MakeNotOwned(ObjKind::CF); - } - } - break; - case OMF_init: - ResultEff = ObjCInitRetE; - ReceiverEff = ArgEffect(DecRef, ObjKind::ObjC); - break; - case OMF_alloc: - case OMF_new: - case OMF_copy: - case OMF_mutableCopy: - if (cocoa::isCocoaObjectRef(RetTy)) - ResultEff = ObjCAllocRetE; - else if (coreFoundation::isCFObjectRef(RetTy)) - ResultEff = RetEffect::MakeOwned(ObjKind::CF); - break; - case OMF_autorelease: - ReceiverEff = ArgEffect(Autorelease, ObjKind::ObjC); - break; - case OMF_retain: - ReceiverEff = ArgEffect(IncRef, ObjKind::ObjC); - break; - case OMF_release: - ReceiverEff = ArgEffect(DecRef, ObjKind::ObjC); - break; - case OMF_dealloc: - ReceiverEff = ArgEffect(Dealloc, ObjKind::ObjC); - break; - case OMF_self: - // -self is handled specially by the ExprEngine to propagate the receiver. - break; - case OMF_retainCount: - case OMF_finalize: - // These methods don't return objects. - break; - } - - // If one of the arguments in the selector has the keyword 'delegate' we - // should stop tracking the reference count for the receiver. This is - // because the reference count is quite possibly handled by a delegate - // method. - if (S.isKeywordSelector()) { - for (unsigned i = 0, e = S.getNumArgs(); i != e; ++i) { - StringRef Slot = S.getNameForSlot(i); - if (Slot.substr(Slot.size() - 8).equals_lower("delegate")) { - if (ResultEff == ObjCInitRetE) - ResultEff = RetEffect::MakeNoRetHard(); - else - ReceiverEff = ArgEffect(StopTrackingHard, ObjKind::ObjC); - } - } - } - - if (ReceiverEff.getKind() == DoNothing && - ResultEff.getKind() == RetEffect::NoRet) - return getDefaultSummary(); - - return getPersistentSummary(ResultEff, ArgEffects(AF.getEmptyMap()), - ArgEffect(ReceiverEff), ArgEffect(MayEscape)); -} - -const RetainSummary *RetainSummaryManager::getInstanceMethodSummary( - const ObjCMethodCall &Msg, - QualType ReceiverType) { - const ObjCInterfaceDecl *ReceiverClass = nullptr; - - // We do better tracking of the type of the object than the core ExprEngine. - // See if we have its type in our private state. - if (!ReceiverType.isNull()) - if (const auto *PT = ReceiverType->getAs<ObjCObjectPointerType>()) - ReceiverClass = PT->getInterfaceDecl(); - - // If we don't know what kind of object this is, fall back to its static type. - if (!ReceiverClass) - ReceiverClass = Msg.getReceiverInterface(); - - // FIXME: The receiver could be a reference to a class, meaning that - // we should use the class method. - // id x = [NSObject class]; - // [x performSelector:... withObject:... afterDelay:...]; - Selector S = Msg.getSelector(); - const ObjCMethodDecl *Method = Msg.getDecl(); - if (!Method && ReceiverClass) - Method = ReceiverClass->getInstanceMethod(S); - - return getMethodSummary(S, ReceiverClass, Method, Msg.getResultType(), - ObjCMethodSummaries); -} - -const RetainSummary * -RetainSummaryManager::getMethodSummary(Selector S, - const ObjCInterfaceDecl *ID, - const ObjCMethodDecl *MD, QualType RetTy, - ObjCMethodSummariesTy &CachedSummaries) { - - // Objective-C method summaries are only applicable to ObjC and CF objects. - if (!TrackObjCAndCFObjects) - return getDefaultSummary(); - - // Look up a summary in our summary cache. - const RetainSummary *Summ = CachedSummaries.find(ID, S); - - if (!Summ) { - Summ = getStandardMethodSummary(MD, S, RetTy); - - // Annotations override defaults. - updateSummaryFromAnnotations(Summ, MD); - - // Memoize the summary. - CachedSummaries[ObjCSummaryKey(ID, S)] = Summ; - } - - return Summ; -} - -void RetainSummaryManager::InitializeClassMethodSummaries() { - ArgEffects ScratchArgs = AF.getEmptyMap(); - - // Create the [NSAssertionHandler currentHander] summary. - addClassMethSummary("NSAssertionHandler", "currentHandler", - getPersistentSummary(RetEffect::MakeNotOwned(ObjKind::ObjC), - ScratchArgs)); - - // Create the [NSAutoreleasePool addObject:] summary. - ScratchArgs = AF.add(ScratchArgs, 0, ArgEffect(Autorelease)); - addClassMethSummary("NSAutoreleasePool", "addObject", - getPersistentSummary(RetEffect::MakeNoRet(), ScratchArgs, - ArgEffect(DoNothing), - ArgEffect(Autorelease))); -} - -void RetainSummaryManager::InitializeMethodSummaries() { - - ArgEffects ScratchArgs = AF.getEmptyMap(); - // Create the "init" selector. It just acts as a pass-through for the - // receiver. - const RetainSummary *InitSumm = getPersistentSummary( - ObjCInitRetE, ScratchArgs, ArgEffect(DecRef, ObjKind::ObjC)); - addNSObjectMethSummary(GetNullarySelector("init", Ctx), InitSumm); - - // awakeAfterUsingCoder: behaves basically like an 'init' method. It - // claims the receiver and returns a retained object. - addNSObjectMethSummary(GetUnarySelector("awakeAfterUsingCoder", Ctx), - InitSumm); - - // The next methods are allocators. - const RetainSummary *AllocSumm = getPersistentSummary(ObjCAllocRetE, - ScratchArgs); - const RetainSummary *CFAllocSumm = - getPersistentSummary(RetEffect::MakeOwned(ObjKind::CF), ScratchArgs); - - // Create the "retain" selector. - RetEffect NoRet = RetEffect::MakeNoRet(); - const RetainSummary *Summ = getPersistentSummary( - NoRet, ScratchArgs, ArgEffect(IncRef, ObjKind::ObjC)); - addNSObjectMethSummary(GetNullarySelector("retain", Ctx), Summ); - - // Create the "release" selector. - Summ = getPersistentSummary(NoRet, ScratchArgs, - ArgEffect(DecRef, ObjKind::ObjC)); - addNSObjectMethSummary(GetNullarySelector("release", Ctx), Summ); - - // Create the -dealloc summary. - Summ = getPersistentSummary(NoRet, ScratchArgs, ArgEffect(Dealloc, - ObjKind::ObjC)); - addNSObjectMethSummary(GetNullarySelector("dealloc", Ctx), Summ); - - // Create the "autorelease" selector. - Summ = getPersistentSummary(NoRet, ScratchArgs, ArgEffect(Autorelease, - ObjKind::ObjC)); - addNSObjectMethSummary(GetNullarySelector("autorelease", Ctx), Summ); - - // For NSWindow, allocated objects are (initially) self-owned. - // FIXME: For now we opt for false negatives with NSWindow, as these objects - // self-own themselves. However, they only do this once they are displayed. - // Thus, we need to track an NSWindow's display status. - // This is tracked in <rdar://problem/6062711>. - // See also http://llvm.org/bugs/show_bug.cgi?id=3714. - const RetainSummary *NoTrackYet = - getPersistentSummary(RetEffect::MakeNoRet(), ScratchArgs, - ArgEffect(StopTracking), ArgEffect(StopTracking)); - - addClassMethSummary("NSWindow", "alloc", NoTrackYet); - - // For NSPanel (which subclasses NSWindow), allocated objects are not - // self-owned. - // FIXME: For now we don't track NSPanels. object for the same reason - // as for NSWindow objects. - addClassMethSummary("NSPanel", "alloc", NoTrackYet); - - // For NSNull, objects returned by +null are singletons that ignore - // retain/release semantics. Just don't track them. - // <rdar://problem/12858915> - addClassMethSummary("NSNull", "null", NoTrackYet); - - // Don't track allocated autorelease pools, as it is okay to prematurely - // exit a method. - addClassMethSummary("NSAutoreleasePool", "alloc", NoTrackYet); - addClassMethSummary("NSAutoreleasePool", "allocWithZone", NoTrackYet, false); - addClassMethSummary("NSAutoreleasePool", "new", NoTrackYet); - - // Create summaries QCRenderer/QCView -createSnapShotImageOfType: - addInstMethSummary("QCRenderer", AllocSumm, "createSnapshotImageOfType"); - addInstMethSummary("QCView", AllocSumm, "createSnapshotImageOfType"); - - // Create summaries for CIContext, 'createCGImage' and - // 'createCGLayerWithSize'. These objects are CF objects, and are not - // automatically garbage collected. - addInstMethSummary("CIContext", CFAllocSumm, "createCGImage", "fromRect"); - addInstMethSummary("CIContext", CFAllocSumm, "createCGImage", "fromRect", - "format", "colorSpace"); - addInstMethSummary("CIContext", CFAllocSumm, "createCGLayerWithSize", "info"); -} - -CallEffects CallEffects::getEffect(const ObjCMethodDecl *MD) { - ASTContext &Ctx = MD->getASTContext(); - LangOptions L = Ctx.getLangOpts(); - RetainSummaryManager M(Ctx, L.ObjCAutoRefCount, - /*TrackNSAndCFObjects=*/true, - /*TrackOSObjects=*/false); - const RetainSummary *S = M.getMethodSummary(MD); - CallEffects CE(S->getRetEffect(), S->getReceiverEffect()); - unsigned N = MD->param_size(); - for (unsigned i = 0; i < N; ++i) { - CE.Args.push_back(S->getArg(i)); - } - return CE; -} - -CallEffects CallEffects::getEffect(const FunctionDecl *FD) { - ASTContext &Ctx = FD->getASTContext(); - LangOptions L = Ctx.getLangOpts(); - RetainSummaryManager M(Ctx, L.ObjCAutoRefCount, - /*TrackNSAndCFObjects=*/true, - /*TrackOSObjects=*/false); - const RetainSummary *S = M.getFunctionSummary(FD); - CallEffects CE(S->getRetEffect()); - unsigned N = FD->param_size(); - for (unsigned i = 0; i < N; ++i) { - CE.Args.push_back(S->getArg(i)); - } - return CE; -} diff --git a/lib/StaticAnalyzer/Core/SMTConstraintManager.cpp b/lib/StaticAnalyzer/Core/SMTConstraintManager.cpp new file mode 100644 index 000000000000..d5c14351d330 --- /dev/null +++ b/lib/StaticAnalyzer/Core/SMTConstraintManager.cpp @@ -0,0 +1,18 @@ +//== SMTConstraintManager.cpp -----------------------------------*- C++ -*--==// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "clang/StaticAnalyzer/Core/PathSensitive/SMTConstraintManager.h" + +using namespace clang; +using namespace ento; + +std::unique_ptr<ConstraintManager> +ento::CreateZ3ConstraintManager(ProgramStateManager &StMgr, SubEngine *Eng) { + return llvm::make_unique<SMTConstraintManager>(Eng, StMgr.getSValBuilder()); +} diff --git a/lib/StaticAnalyzer/Core/SValBuilder.cpp b/lib/StaticAnalyzer/Core/SValBuilder.cpp index 6c0d487c8a87..3a5841137e1a 100644 --- a/lib/StaticAnalyzer/Core/SValBuilder.cpp +++ b/lib/StaticAnalyzer/Core/SValBuilder.cpp @@ -1,9 +1,8 @@ //===- SValBuilder.cpp - Basic class for all SValBuilder implementations --===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// // diff --git a/lib/StaticAnalyzer/Core/SVals.cpp b/lib/StaticAnalyzer/Core/SVals.cpp index 933c5c330072..9b5de6c3eb92 100644 --- a/lib/StaticAnalyzer/Core/SVals.cpp +++ b/lib/StaticAnalyzer/Core/SVals.cpp @@ -1,9 +1,8 @@ -//===- RValues.cpp - Abstract RValues for Path-Sens. Value Tracking -------===// +//===-- SVals.cpp - Abstract RValues for Path-Sens. Value Tracking --------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// // @@ -17,6 +16,7 @@ #include "clang/AST/DeclCXX.h" #include "clang/AST/Expr.h" #include "clang/AST/Type.h" +#include "clang/Basic/JsonSupport.h" #include "clang/Basic/LLVM.h" #include "clang/StaticAnalyzer/Core/PathSensitive/BasicValueFactory.h" #include "clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h" @@ -284,6 +284,15 @@ SVal loc::ConcreteInt::evalBinOp(BasicValueFactory& BasicVals, LLVM_DUMP_METHOD void SVal::dump() const { dumpToStream(llvm::errs()); } +void SVal::printJson(raw_ostream &Out, bool AddQuotes) const { + std::string Buf; + llvm::raw_string_ostream TempOut(Buf); + + dumpToStream(TempOut); + + Out << JsonFormat(TempOut.str(), AddQuotes); +} + void SVal::dumpToStream(raw_ostream &os) const { switch (getBaseKind()) { case UnknownValKind: diff --git a/lib/StaticAnalyzer/Core/SarifDiagnostics.cpp b/lib/StaticAnalyzer/Core/SarifDiagnostics.cpp index fecbc0001079..d1faf3f4dea9 100644 --- a/lib/StaticAnalyzer/Core/SarifDiagnostics.cpp +++ b/lib/StaticAnalyzer/Core/SarifDiagnostics.cpp @@ -1,9 +1,8 @@ //===--- SarifDiagnostics.cpp - Sarif Diagnostics for Paths -----*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// // @@ -192,15 +191,16 @@ static json::Object createLocation(json::Object &&PhysicalLocation, static Importance calculateImportance(const PathDiagnosticPiece &Piece) { switch (Piece.getKind()) { - case PathDiagnosticPiece::Kind::Call: - case PathDiagnosticPiece::Kind::Macro: - case PathDiagnosticPiece::Kind::Note: + case PathDiagnosticPiece::Call: + case PathDiagnosticPiece::Macro: + case PathDiagnosticPiece::Note: + case PathDiagnosticPiece::PopUp: // FIXME: What should be reported here? break; - case PathDiagnosticPiece::Kind::Event: + case PathDiagnosticPiece::Event: return Piece.getTagStr() == "ConditionBRVisitor" ? Importance::Important : Importance::Essential; - case PathDiagnosticPiece::Kind::ControlFlow: + case PathDiagnosticPiece::ControlFlow: return Importance::Unimportant; } return Importance::Unimportant; @@ -257,7 +257,7 @@ static json::Object createResult(const PathDiagnostic &Diag, json::Array &Files, static StringRef getRuleDescription(StringRef CheckName) { return llvm::StringSwitch<StringRef>(CheckName) #define GET_CHECKERS -#define CHECKER(FULLNAME, CLASS, HELPTEXT, DOC_URI) \ +#define CHECKER(FULLNAME, CLASS, HELPTEXT, DOC_URI, IS_HIDDEN) \ .Case(FULLNAME, HELPTEXT) #include "clang/StaticAnalyzer/Checkers/Checkers.inc" #undef CHECKER @@ -268,7 +268,7 @@ static StringRef getRuleDescription(StringRef CheckName) { static StringRef getRuleHelpURIStr(StringRef CheckName) { return llvm::StringSwitch<StringRef>(CheckName) #define GET_CHECKERS -#define CHECKER(FULLNAME, CLASS, HELPTEXT, DOC_URI) \ +#define CHECKER(FULLNAME, CLASS, HELPTEXT, DOC_URI, IS_HIDDEN) \ .Case(FULLNAME, DOC_URI) #include "clang/StaticAnalyzer/Checkers/Checkers.inc" #undef CHECKER @@ -345,5 +345,5 @@ void SarifDiagnostics::FlushDiagnosticsImpl( "http://json.schemastore.org/sarif-2.0.0-csd.2.beta.2018-11-28"}, {"version", "2.0.0-csd.2.beta.2018-11-28"}, {"runs", json::Array{createRun(Diags)}}}; - OS << llvm::formatv("{0:2}", json::Value(std::move(Sarif))); + OS << llvm::formatv("{0:2}\n", json::Value(std::move(Sarif))); } diff --git a/lib/StaticAnalyzer/Core/SimpleConstraintManager.cpp b/lib/StaticAnalyzer/Core/SimpleConstraintManager.cpp index adb40178f5b1..85f60231a276 100644 --- a/lib/StaticAnalyzer/Core/SimpleConstraintManager.cpp +++ b/lib/StaticAnalyzer/Core/SimpleConstraintManager.cpp @@ -1,9 +1,8 @@ //== SimpleConstraintManager.cpp --------------------------------*- C++ -*--==// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// // diff --git a/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp b/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp index fc57cecac9cb..84c52f53ca5e 100644 --- a/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp +++ b/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp @@ -1,9 +1,8 @@ // SimpleSValBuilder.cpp - A basic SValBuilder -----------------------*- C++ -*- // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// // @@ -526,7 +525,7 @@ SVal SimpleSValBuilder::evalBinOpNN(ProgramStateRef state, case BO_Sub: if (resultTy->isIntegralOrEnumerationType()) return makeIntVal(0, resultTy); - return evalCastFromNonLoc(makeIntVal(0, /*Unsigned=*/false), resultTy); + return evalCastFromNonLoc(makeIntVal(0, /*isUnsigned=*/false), resultTy); case BO_Or: case BO_And: return evalCastFromNonLoc(lhs, resultTy); @@ -572,7 +571,15 @@ SVal SimpleSValBuilder::evalBinOpNN(ProgramStateRef state, // add 1 to a LocAsInteger, we'd better unpack the Loc and add to it, // then pack it back into a LocAsInteger. llvm::APSInt i = rhs.castAs<nonloc::ConcreteInt>().getValue(); - BasicVals.getAPSIntType(Context.VoidPtrTy).apply(i); + // If the region has a symbolic base, pay attention to the type; it + // might be coming from a non-default address space. For non-symbolic + // regions it doesn't matter that much because such comparisons would + // most likely evaluate to concrete false anyway. FIXME: We might + // still need to handle the non-comparison case. + if (SymbolRef lSym = lhs.getAsLocSymbol(true)) + BasicVals.getAPSIntType(lSym->getType()).apply(i); + else + BasicVals.getAPSIntType(Context.VoidPtrTy).apply(i); return evalBinOpLL(state, op, lhsL, makeLoc(i), resultTy); } default: diff --git a/lib/StaticAnalyzer/Core/Store.cpp b/lib/StaticAnalyzer/Core/Store.cpp index 4fa937d9658d..3cf616161c66 100644 --- a/lib/StaticAnalyzer/Core/Store.cpp +++ b/lib/StaticAnalyzer/Core/Store.cpp @@ -1,9 +1,8 @@ //===- Store.cpp - Interface for maps from Locations to Values ------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// // diff --git a/lib/StaticAnalyzer/Core/SubEngine.cpp b/lib/StaticAnalyzer/Core/SubEngine.cpp index 350f4b8bb3a2..d7ddd9cf4610 100644 --- a/lib/StaticAnalyzer/Core/SubEngine.cpp +++ b/lib/StaticAnalyzer/Core/SubEngine.cpp @@ -1,9 +1,8 @@ //== SubEngine.cpp - Interface of the subengine of CoreEngine ------*- C++ -*-// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// diff --git a/lib/StaticAnalyzer/Core/SymbolManager.cpp b/lib/StaticAnalyzer/Core/SymbolManager.cpp index 66273f099a38..675209f6fd7e 100644 --- a/lib/StaticAnalyzer/Core/SymbolManager.cpp +++ b/lib/StaticAnalyzer/Core/SymbolManager.cpp @@ -1,9 +1,8 @@ //===- SymbolManager.h - Management of Symbolic Values --------------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// // @@ -405,7 +404,7 @@ void SymbolReaper::markLive(SymbolRef sym) { } void SymbolReaper::markLive(const MemRegion *region) { - RegionRoots.insert(region); + RegionRoots.insert(region->getBaseRegion()); markElementIndicesLive(region); } @@ -426,11 +425,15 @@ void SymbolReaper::markInUse(SymbolRef sym) { } bool SymbolReaper::isLiveRegion(const MemRegion *MR) { + // TODO: For now, liveness of a memory region is equivalent to liveness of its + // base region. In fact we can do a bit better: say, if a particular FieldDecl + // is not used later in the path, we can diagnose a leak of a value within + // that field earlier than, say, the variable that contains the field dies. + MR = MR->getBaseRegion(); + if (RegionRoots.count(MR)) return true; - MR = MR->getBaseRegion(); - if (const auto *SR = dyn_cast<SymbolicRegion>(MR)) return isLive(SR->getSymbol()); diff --git a/lib/StaticAnalyzer/Core/TaintManager.cpp b/lib/StaticAnalyzer/Core/TaintManager.cpp deleted file mode 100644 index c34b0ca1839d..000000000000 --- a/lib/StaticAnalyzer/Core/TaintManager.cpp +++ /dev/null @@ -1,23 +0,0 @@ -//== TaintManager.cpp ------------------------------------------ -*- C++ -*--=// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// - -#include "clang/StaticAnalyzer/Core/PathSensitive/TaintManager.h" - -using namespace clang; -using namespace ento; - -void *ProgramStateTrait<TaintMap>::GDMIndex() { - static int index = 0; - return &index; -} - -void *ProgramStateTrait<DerivedSymTaint>::GDMIndex() { - static int index; - return &index; -} diff --git a/lib/StaticAnalyzer/Core/WorkList.cpp b/lib/StaticAnalyzer/Core/WorkList.cpp index e705393cb83a..129d1720395e 100644 --- a/lib/StaticAnalyzer/Core/WorkList.cpp +++ b/lib/StaticAnalyzer/Core/WorkList.cpp @@ -1,9 +1,8 @@ //===- WorkList.cpp - Analyzer work-list implementation--------------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// // diff --git a/lib/StaticAnalyzer/Core/Z3ConstraintManager.cpp b/lib/StaticAnalyzer/Core/Z3ConstraintManager.cpp deleted file mode 100644 index c4729f969f33..000000000000 --- a/lib/StaticAnalyzer/Core/Z3ConstraintManager.cpp +++ /dev/null @@ -1,841 +0,0 @@ -//== Z3ConstraintManager.cpp --------------------------------*- C++ -*--==// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// - -#include "clang/Basic/TargetInfo.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/SMTConstraintManager.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/SMTConv.h" - -#include "clang/Config/config.h" - -using namespace clang; -using namespace ento; - -#if CLANG_ANALYZER_WITH_Z3 - -#include <z3.h> - -namespace { - -/// Configuration class for Z3 -class Z3Config { - friend class Z3Context; - - Z3_config Config; - -public: - Z3Config() : Config(Z3_mk_config()) { - // Enable model finding - Z3_set_param_value(Config, "model", "true"); - // Disable proof generation - Z3_set_param_value(Config, "proof", "false"); - // Set timeout to 15000ms = 15s - Z3_set_param_value(Config, "timeout", "15000"); - } - - ~Z3Config() { Z3_del_config(Config); } -}; // end class Z3Config - -// Function used to report errors -void Z3ErrorHandler(Z3_context Context, Z3_error_code Error) { - llvm::report_fatal_error("Z3 error: " + - llvm::Twine(Z3_get_error_msg(Context, Error))); -} - -/// Wrapper for Z3 context -class Z3Context { -public: - Z3_context Context; - - Z3Context() { - Context = Z3_mk_context_rc(Z3Config().Config); - // The error function is set here because the context is the first object - // created by the backend - Z3_set_error_handler(Context, Z3ErrorHandler); - } - - virtual ~Z3Context() { - Z3_del_context(Context); - Context = nullptr; - } -}; // end class Z3Context - -/// Wrapper for Z3 Sort -class Z3Sort : public SMTSort { - friend class Z3Solver; - - Z3Context &Context; - - Z3_sort Sort; - -public: - /// Default constructor, mainly used by make_shared - Z3Sort(Z3Context &C, Z3_sort ZS) : Context(C), Sort(ZS) { - Z3_inc_ref(Context.Context, reinterpret_cast<Z3_ast>(Sort)); - } - - /// Override implicit copy constructor for correct reference counting. - Z3Sort(const Z3Sort &Other) : Context(Other.Context), Sort(Other.Sort) { - Z3_inc_ref(Context.Context, reinterpret_cast<Z3_ast>(Sort)); - } - - /// Override implicit copy assignment constructor for correct reference - /// counting. - Z3Sort &operator=(const Z3Sort &Other) { - Z3_inc_ref(Context.Context, reinterpret_cast<Z3_ast>(Other.Sort)); - Z3_dec_ref(Context.Context, reinterpret_cast<Z3_ast>(Sort)); - Sort = Other.Sort; - return *this; - } - - Z3Sort(Z3Sort &&Other) = delete; - Z3Sort &operator=(Z3Sort &&Other) = delete; - - ~Z3Sort() { - if (Sort) - Z3_dec_ref(Context.Context, reinterpret_cast<Z3_ast>(Sort)); - } - - bool isBitvectorSortImpl() const override { - return (Z3_get_sort_kind(Context.Context, Sort) == Z3_BV_SORT); - } - - bool isFloatSortImpl() const override { - return (Z3_get_sort_kind(Context.Context, Sort) == Z3_FLOATING_POINT_SORT); - } - - bool isBooleanSortImpl() const override { - return (Z3_get_sort_kind(Context.Context, Sort) == Z3_BOOL_SORT); - } - - unsigned getBitvectorSortSizeImpl() const override { - return Z3_get_bv_sort_size(Context.Context, Sort); - } - - unsigned getFloatSortSizeImpl() const override { - return Z3_fpa_get_ebits(Context.Context, Sort) + - Z3_fpa_get_sbits(Context.Context, Sort); - } - - bool equal_to(SMTSort const &Other) const override { - return Z3_is_eq_sort(Context.Context, Sort, - static_cast<const Z3Sort &>(Other).Sort); - } - - void print(raw_ostream &OS) const override { - OS << Z3_sort_to_string(Context.Context, Sort); - } -}; // end class Z3Sort - -static const Z3Sort &toZ3Sort(const SMTSort &S) { - return static_cast<const Z3Sort &>(S); -} - -class Z3Expr : public SMTExpr { - friend class Z3Solver; - - Z3Context &Context; - - Z3_ast AST; - -public: - Z3Expr(Z3Context &C, Z3_ast ZA) : SMTExpr(), Context(C), AST(ZA) { - Z3_inc_ref(Context.Context, AST); - } - - /// Override implicit copy constructor for correct reference counting. - Z3Expr(const Z3Expr &Copy) : SMTExpr(), Context(Copy.Context), AST(Copy.AST) { - Z3_inc_ref(Context.Context, AST); - } - - /// Override implicit copy assignment constructor for correct reference - /// counting. - Z3Expr &operator=(const Z3Expr &Other) { - Z3_inc_ref(Context.Context, Other.AST); - Z3_dec_ref(Context.Context, AST); - AST = Other.AST; - return *this; - } - - Z3Expr(Z3Expr &&Other) = delete; - Z3Expr &operator=(Z3Expr &&Other) = delete; - - ~Z3Expr() { - if (AST) - Z3_dec_ref(Context.Context, AST); - } - - void Profile(llvm::FoldingSetNodeID &ID) const override { - ID.AddInteger(Z3_get_ast_hash(Context.Context, AST)); - } - - /// Comparison of AST equality, not model equivalence. - bool equal_to(SMTExpr const &Other) const override { - assert(Z3_is_eq_sort(Context.Context, Z3_get_sort(Context.Context, AST), - Z3_get_sort(Context.Context, - static_cast<const Z3Expr &>(Other).AST)) && - "AST's must have the same sort"); - return Z3_is_eq_ast(Context.Context, AST, - static_cast<const Z3Expr &>(Other).AST); - } - - void print(raw_ostream &OS) const override { - OS << Z3_ast_to_string(Context.Context, AST); - } -}; // end class Z3Expr - -static const Z3Expr &toZ3Expr(const SMTExpr &E) { - return static_cast<const Z3Expr &>(E); -} - -class Z3Model { - friend class Z3Solver; - - Z3Context &Context; - - Z3_model Model; - -public: - Z3Model(Z3Context &C, Z3_model ZM) : Context(C), Model(ZM) { - Z3_model_inc_ref(Context.Context, Model); - } - - Z3Model(const Z3Model &Other) = delete; - Z3Model(Z3Model &&Other) = delete; - Z3Model &operator=(Z3Model &Other) = delete; - Z3Model &operator=(Z3Model &&Other) = delete; - - ~Z3Model() { - if (Model) - Z3_model_dec_ref(Context.Context, Model); - } - - void print(raw_ostream &OS) const { - OS << Z3_model_to_string(Context.Context, Model); - } - - LLVM_DUMP_METHOD void dump() const { print(llvm::errs()); } -}; // end class Z3Model - -/// Get the corresponding IEEE floating-point type for a given bitwidth. -static const llvm::fltSemantics &getFloatSemantics(unsigned BitWidth) { - switch (BitWidth) { - default: - llvm_unreachable("Unsupported floating-point semantics!"); - break; - case 16: - return llvm::APFloat::IEEEhalf(); - case 32: - return llvm::APFloat::IEEEsingle(); - case 64: - return llvm::APFloat::IEEEdouble(); - case 128: - return llvm::APFloat::IEEEquad(); - } -} - -// Determine whether two float semantics are equivalent -static bool areEquivalent(const llvm::fltSemantics &LHS, - const llvm::fltSemantics &RHS) { - return (llvm::APFloat::semanticsPrecision(LHS) == - llvm::APFloat::semanticsPrecision(RHS)) && - (llvm::APFloat::semanticsMinExponent(LHS) == - llvm::APFloat::semanticsMinExponent(RHS)) && - (llvm::APFloat::semanticsMaxExponent(LHS) == - llvm::APFloat::semanticsMaxExponent(RHS)) && - (llvm::APFloat::semanticsSizeInBits(LHS) == - llvm::APFloat::semanticsSizeInBits(RHS)); -} - -} // end anonymous namespace - -typedef llvm::ImmutableSet<std::pair<SymbolRef, Z3Expr>> ConstraintZ3Ty; -REGISTER_TRAIT_WITH_PROGRAMSTATE(ConstraintZ3, ConstraintZ3Ty) - -namespace { - -class Z3Solver : public SMTSolver { - friend class Z3ConstraintManager; - - Z3Context Context; - - Z3_solver Solver; - -public: - Z3Solver() : Solver(Z3_mk_simple_solver(Context.Context)) { - Z3_solver_inc_ref(Context.Context, Solver); - } - - Z3Solver(const Z3Solver &Other) = delete; - Z3Solver(Z3Solver &&Other) = delete; - Z3Solver &operator=(Z3Solver &Other) = delete; - Z3Solver &operator=(Z3Solver &&Other) = delete; - - ~Z3Solver() { - if (Solver) - Z3_solver_dec_ref(Context.Context, Solver); - } - - void addConstraint(const SMTExprRef &Exp) const override { - Z3_solver_assert(Context.Context, Solver, toZ3Expr(*Exp).AST); - } - - SMTSortRef getBoolSort() override { - return std::make_shared<Z3Sort>(Context, Z3_mk_bool_sort(Context.Context)); - } - - SMTSortRef getBitvectorSort(unsigned BitWidth) override { - return std::make_shared<Z3Sort>(Context, - Z3_mk_bv_sort(Context.Context, BitWidth)); - } - - SMTSortRef getSort(const SMTExprRef &Exp) override { - return std::make_shared<Z3Sort>( - Context, Z3_get_sort(Context.Context, toZ3Expr(*Exp).AST)); - } - - SMTSortRef getFloat16Sort() override { - return std::make_shared<Z3Sort>(Context, - Z3_mk_fpa_sort_16(Context.Context)); - } - - SMTSortRef getFloat32Sort() override { - return std::make_shared<Z3Sort>(Context, - Z3_mk_fpa_sort_32(Context.Context)); - } - - SMTSortRef getFloat64Sort() override { - return std::make_shared<Z3Sort>(Context, - Z3_mk_fpa_sort_64(Context.Context)); - } - - SMTSortRef getFloat128Sort() override { - return std::make_shared<Z3Sort>(Context, - Z3_mk_fpa_sort_128(Context.Context)); - } - - SMTExprRef newExprRef(const SMTExpr &E) const override { - return std::make_shared<Z3Expr>(toZ3Expr(E)); - } - - SMTExprRef mkBVNeg(const SMTExprRef &Exp) override { - return newExprRef( - Z3Expr(Context, Z3_mk_bvneg(Context.Context, toZ3Expr(*Exp).AST))); - } - - SMTExprRef mkBVNot(const SMTExprRef &Exp) override { - return newExprRef( - Z3Expr(Context, Z3_mk_bvnot(Context.Context, toZ3Expr(*Exp).AST))); - } - - SMTExprRef mkNot(const SMTExprRef &Exp) override { - return newExprRef( - Z3Expr(Context, Z3_mk_not(Context.Context, toZ3Expr(*Exp).AST))); - } - - SMTExprRef mkBVAdd(const SMTExprRef &LHS, const SMTExprRef &RHS) override { - return newExprRef( - Z3Expr(Context, Z3_mk_bvadd(Context.Context, toZ3Expr(*LHS).AST, - toZ3Expr(*RHS).AST))); - } - - SMTExprRef mkBVSub(const SMTExprRef &LHS, const SMTExprRef &RHS) override { - return newExprRef( - Z3Expr(Context, Z3_mk_bvsub(Context.Context, toZ3Expr(*LHS).AST, - toZ3Expr(*RHS).AST))); - } - - SMTExprRef mkBVMul(const SMTExprRef &LHS, const SMTExprRef &RHS) override { - return newExprRef( - Z3Expr(Context, Z3_mk_bvmul(Context.Context, toZ3Expr(*LHS).AST, - toZ3Expr(*RHS).AST))); - } - - SMTExprRef mkBVSRem(const SMTExprRef &LHS, const SMTExprRef &RHS) override { - return newExprRef( - Z3Expr(Context, Z3_mk_bvsrem(Context.Context, toZ3Expr(*LHS).AST, - toZ3Expr(*RHS).AST))); - } - - SMTExprRef mkBVURem(const SMTExprRef &LHS, const SMTExprRef &RHS) override { - return newExprRef( - Z3Expr(Context, Z3_mk_bvurem(Context.Context, toZ3Expr(*LHS).AST, - toZ3Expr(*RHS).AST))); - } - - SMTExprRef mkBVSDiv(const SMTExprRef &LHS, const SMTExprRef &RHS) override { - return newExprRef( - Z3Expr(Context, Z3_mk_bvsdiv(Context.Context, toZ3Expr(*LHS).AST, - toZ3Expr(*RHS).AST))); - } - - SMTExprRef mkBVUDiv(const SMTExprRef &LHS, const SMTExprRef &RHS) override { - return newExprRef( - Z3Expr(Context, Z3_mk_bvudiv(Context.Context, toZ3Expr(*LHS).AST, - toZ3Expr(*RHS).AST))); - } - - SMTExprRef mkBVShl(const SMTExprRef &LHS, const SMTExprRef &RHS) override { - return newExprRef( - Z3Expr(Context, Z3_mk_bvshl(Context.Context, toZ3Expr(*LHS).AST, - toZ3Expr(*RHS).AST))); - } - - SMTExprRef mkBVAshr(const SMTExprRef &LHS, const SMTExprRef &RHS) override { - return newExprRef( - Z3Expr(Context, Z3_mk_bvashr(Context.Context, toZ3Expr(*LHS).AST, - toZ3Expr(*RHS).AST))); - } - - SMTExprRef mkBVLshr(const SMTExprRef &LHS, const SMTExprRef &RHS) override { - return newExprRef( - Z3Expr(Context, Z3_mk_bvlshr(Context.Context, toZ3Expr(*LHS).AST, - toZ3Expr(*RHS).AST))); - } - - SMTExprRef mkBVXor(const SMTExprRef &LHS, const SMTExprRef &RHS) override { - return newExprRef( - Z3Expr(Context, Z3_mk_bvxor(Context.Context, toZ3Expr(*LHS).AST, - toZ3Expr(*RHS).AST))); - } - - SMTExprRef mkBVOr(const SMTExprRef &LHS, const SMTExprRef &RHS) override { - return newExprRef( - Z3Expr(Context, Z3_mk_bvor(Context.Context, toZ3Expr(*LHS).AST, - toZ3Expr(*RHS).AST))); - } - - SMTExprRef mkBVAnd(const SMTExprRef &LHS, const SMTExprRef &RHS) override { - return newExprRef( - Z3Expr(Context, Z3_mk_bvand(Context.Context, toZ3Expr(*LHS).AST, - toZ3Expr(*RHS).AST))); - } - - SMTExprRef mkBVUlt(const SMTExprRef &LHS, const SMTExprRef &RHS) override { - return newExprRef( - Z3Expr(Context, Z3_mk_bvult(Context.Context, toZ3Expr(*LHS).AST, - toZ3Expr(*RHS).AST))); - } - - SMTExprRef mkBVSlt(const SMTExprRef &LHS, const SMTExprRef &RHS) override { - return newExprRef( - Z3Expr(Context, Z3_mk_bvslt(Context.Context, toZ3Expr(*LHS).AST, - toZ3Expr(*RHS).AST))); - } - - SMTExprRef mkBVUgt(const SMTExprRef &LHS, const SMTExprRef &RHS) override { - return newExprRef( - Z3Expr(Context, Z3_mk_bvugt(Context.Context, toZ3Expr(*LHS).AST, - toZ3Expr(*RHS).AST))); - } - - SMTExprRef mkBVSgt(const SMTExprRef &LHS, const SMTExprRef &RHS) override { - return newExprRef( - Z3Expr(Context, Z3_mk_bvsgt(Context.Context, toZ3Expr(*LHS).AST, - toZ3Expr(*RHS).AST))); - } - - SMTExprRef mkBVUle(const SMTExprRef &LHS, const SMTExprRef &RHS) override { - return newExprRef( - Z3Expr(Context, Z3_mk_bvule(Context.Context, toZ3Expr(*LHS).AST, - toZ3Expr(*RHS).AST))); - } - - SMTExprRef mkBVSle(const SMTExprRef &LHS, const SMTExprRef &RHS) override { - return newExprRef( - Z3Expr(Context, Z3_mk_bvsle(Context.Context, toZ3Expr(*LHS).AST, - toZ3Expr(*RHS).AST))); - } - - SMTExprRef mkBVUge(const SMTExprRef &LHS, const SMTExprRef &RHS) override { - return newExprRef( - Z3Expr(Context, Z3_mk_bvuge(Context.Context, toZ3Expr(*LHS).AST, - toZ3Expr(*RHS).AST))); - } - - SMTExprRef mkBVSge(const SMTExprRef &LHS, const SMTExprRef &RHS) override { - return newExprRef( - Z3Expr(Context, Z3_mk_bvsge(Context.Context, toZ3Expr(*LHS).AST, - toZ3Expr(*RHS).AST))); - } - - SMTExprRef mkAnd(const SMTExprRef &LHS, const SMTExprRef &RHS) override { - Z3_ast Args[2] = {toZ3Expr(*LHS).AST, toZ3Expr(*RHS).AST}; - return newExprRef(Z3Expr(Context, Z3_mk_and(Context.Context, 2, Args))); - } - - SMTExprRef mkOr(const SMTExprRef &LHS, const SMTExprRef &RHS) override { - Z3_ast Args[2] = {toZ3Expr(*LHS).AST, toZ3Expr(*RHS).AST}; - return newExprRef(Z3Expr(Context, Z3_mk_or(Context.Context, 2, Args))); - } - - SMTExprRef mkEqual(const SMTExprRef &LHS, const SMTExprRef &RHS) override { - return newExprRef( - Z3Expr(Context, Z3_mk_eq(Context.Context, toZ3Expr(*LHS).AST, - toZ3Expr(*RHS).AST))); - } - - SMTExprRef mkFPNeg(const SMTExprRef &Exp) override { - return newExprRef( - Z3Expr(Context, Z3_mk_fpa_neg(Context.Context, toZ3Expr(*Exp).AST))); - } - - SMTExprRef mkFPIsInfinite(const SMTExprRef &Exp) override { - return newExprRef(Z3Expr( - Context, Z3_mk_fpa_is_infinite(Context.Context, toZ3Expr(*Exp).AST))); - } - - SMTExprRef mkFPIsNaN(const SMTExprRef &Exp) override { - return newExprRef( - Z3Expr(Context, Z3_mk_fpa_is_nan(Context.Context, toZ3Expr(*Exp).AST))); - } - - SMTExprRef mkFPIsNormal(const SMTExprRef &Exp) override { - return newExprRef(Z3Expr( - Context, Z3_mk_fpa_is_normal(Context.Context, toZ3Expr(*Exp).AST))); - } - - SMTExprRef mkFPIsZero(const SMTExprRef &Exp) override { - return newExprRef(Z3Expr( - Context, Z3_mk_fpa_is_zero(Context.Context, toZ3Expr(*Exp).AST))); - } - - SMTExprRef mkFPMul(const SMTExprRef &LHS, const SMTExprRef &RHS) override { - SMTExprRef RoundingMode = getFloatRoundingMode(); - return newExprRef( - Z3Expr(Context, - Z3_mk_fpa_mul(Context.Context, toZ3Expr(*LHS).AST, - toZ3Expr(*RHS).AST, toZ3Expr(*RoundingMode).AST))); - } - - SMTExprRef mkFPDiv(const SMTExprRef &LHS, const SMTExprRef &RHS) override { - SMTExprRef RoundingMode = getFloatRoundingMode(); - return newExprRef( - Z3Expr(Context, - Z3_mk_fpa_div(Context.Context, toZ3Expr(*LHS).AST, - toZ3Expr(*RHS).AST, toZ3Expr(*RoundingMode).AST))); - } - - SMTExprRef mkFPRem(const SMTExprRef &LHS, const SMTExprRef &RHS) override { - return newExprRef( - Z3Expr(Context, Z3_mk_fpa_rem(Context.Context, toZ3Expr(*LHS).AST, - toZ3Expr(*RHS).AST))); - } - - SMTExprRef mkFPAdd(const SMTExprRef &LHS, const SMTExprRef &RHS) override { - SMTExprRef RoundingMode = getFloatRoundingMode(); - return newExprRef( - Z3Expr(Context, - Z3_mk_fpa_add(Context.Context, toZ3Expr(*LHS).AST, - toZ3Expr(*RHS).AST, toZ3Expr(*RoundingMode).AST))); - } - - SMTExprRef mkFPSub(const SMTExprRef &LHS, const SMTExprRef &RHS) override { - SMTExprRef RoundingMode = getFloatRoundingMode(); - return newExprRef( - Z3Expr(Context, - Z3_mk_fpa_sub(Context.Context, toZ3Expr(*LHS).AST, - toZ3Expr(*RHS).AST, toZ3Expr(*RoundingMode).AST))); - } - - SMTExprRef mkFPLt(const SMTExprRef &LHS, const SMTExprRef &RHS) override { - return newExprRef( - Z3Expr(Context, Z3_mk_fpa_lt(Context.Context, toZ3Expr(*LHS).AST, - toZ3Expr(*RHS).AST))); - } - - SMTExprRef mkFPGt(const SMTExprRef &LHS, const SMTExprRef &RHS) override { - return newExprRef( - Z3Expr(Context, Z3_mk_fpa_gt(Context.Context, toZ3Expr(*LHS).AST, - toZ3Expr(*RHS).AST))); - } - - SMTExprRef mkFPLe(const SMTExprRef &LHS, const SMTExprRef &RHS) override { - return newExprRef( - Z3Expr(Context, Z3_mk_fpa_leq(Context.Context, toZ3Expr(*LHS).AST, - toZ3Expr(*RHS).AST))); - } - - SMTExprRef mkFPGe(const SMTExprRef &LHS, const SMTExprRef &RHS) override { - return newExprRef( - Z3Expr(Context, Z3_mk_fpa_geq(Context.Context, toZ3Expr(*LHS).AST, - toZ3Expr(*RHS).AST))); - } - - SMTExprRef mkFPEqual(const SMTExprRef &LHS, const SMTExprRef &RHS) override { - return newExprRef( - Z3Expr(Context, Z3_mk_fpa_eq(Context.Context, toZ3Expr(*LHS).AST, - toZ3Expr(*RHS).AST))); - } - - SMTExprRef mkIte(const SMTExprRef &Cond, const SMTExprRef &T, - const SMTExprRef &F) override { - return newExprRef( - Z3Expr(Context, Z3_mk_ite(Context.Context, toZ3Expr(*Cond).AST, - toZ3Expr(*T).AST, toZ3Expr(*F).AST))); - } - - SMTExprRef mkBVSignExt(unsigned i, const SMTExprRef &Exp) override { - return newExprRef(Z3Expr( - Context, Z3_mk_sign_ext(Context.Context, i, toZ3Expr(*Exp).AST))); - } - - SMTExprRef mkBVZeroExt(unsigned i, const SMTExprRef &Exp) override { - return newExprRef(Z3Expr( - Context, Z3_mk_zero_ext(Context.Context, i, toZ3Expr(*Exp).AST))); - } - - SMTExprRef mkBVExtract(unsigned High, unsigned Low, - const SMTExprRef &Exp) override { - return newExprRef(Z3Expr(Context, Z3_mk_extract(Context.Context, High, Low, - toZ3Expr(*Exp).AST))); - } - - SMTExprRef mkBVConcat(const SMTExprRef &LHS, const SMTExprRef &RHS) override { - return newExprRef( - Z3Expr(Context, Z3_mk_concat(Context.Context, toZ3Expr(*LHS).AST, - toZ3Expr(*RHS).AST))); - } - - SMTExprRef mkFPtoFP(const SMTExprRef &From, const SMTSortRef &To) override { - SMTExprRef RoundingMode = getFloatRoundingMode(); - return newExprRef(Z3Expr( - Context, - Z3_mk_fpa_to_fp_float(Context.Context, toZ3Expr(*RoundingMode).AST, - toZ3Expr(*From).AST, toZ3Sort(*To).Sort))); - } - - SMTExprRef mkSBVtoFP(const SMTExprRef &From, const SMTSortRef &To) override { - SMTExprRef RoundingMode = getFloatRoundingMode(); - return newExprRef(Z3Expr( - Context, - Z3_mk_fpa_to_fp_signed(Context.Context, toZ3Expr(*RoundingMode).AST, - toZ3Expr(*From).AST, toZ3Sort(*To).Sort))); - } - - SMTExprRef mkUBVtoFP(const SMTExprRef &From, const SMTSortRef &To) override { - SMTExprRef RoundingMode = getFloatRoundingMode(); - return newExprRef(Z3Expr( - Context, - Z3_mk_fpa_to_fp_unsigned(Context.Context, toZ3Expr(*RoundingMode).AST, - toZ3Expr(*From).AST, toZ3Sort(*To).Sort))); - } - - SMTExprRef mkFPtoSBV(const SMTExprRef &From, unsigned ToWidth) override { - SMTExprRef RoundingMode = getFloatRoundingMode(); - return newExprRef(Z3Expr( - Context, Z3_mk_fpa_to_sbv(Context.Context, toZ3Expr(*RoundingMode).AST, - toZ3Expr(*From).AST, ToWidth))); - } - - SMTExprRef mkFPtoUBV(const SMTExprRef &From, unsigned ToWidth) override { - SMTExprRef RoundingMode = getFloatRoundingMode(); - return newExprRef(Z3Expr( - Context, Z3_mk_fpa_to_ubv(Context.Context, toZ3Expr(*RoundingMode).AST, - toZ3Expr(*From).AST, ToWidth))); - } - - SMTExprRef mkBoolean(const bool b) override { - return newExprRef(Z3Expr(Context, b ? Z3_mk_true(Context.Context) - : Z3_mk_false(Context.Context))); - } - - SMTExprRef mkBitvector(const llvm::APSInt Int, unsigned BitWidth) override { - const SMTSortRef Sort = getBitvectorSort(BitWidth); - return newExprRef( - Z3Expr(Context, Z3_mk_numeral(Context.Context, Int.toString(10).c_str(), - toZ3Sort(*Sort).Sort))); - } - - SMTExprRef mkFloat(const llvm::APFloat Float) override { - SMTSortRef Sort = - getFloatSort(llvm::APFloat::semanticsSizeInBits(Float.getSemantics())); - - llvm::APSInt Int = llvm::APSInt(Float.bitcastToAPInt(), false); - SMTExprRef Z3Int = mkBitvector(Int, Int.getBitWidth()); - return newExprRef(Z3Expr( - Context, Z3_mk_fpa_to_fp_bv(Context.Context, toZ3Expr(*Z3Int).AST, - toZ3Sort(*Sort).Sort))); - } - - SMTExprRef mkSymbol(const char *Name, SMTSortRef Sort) override { - return newExprRef( - Z3Expr(Context, Z3_mk_const(Context.Context, - Z3_mk_string_symbol(Context.Context, Name), - toZ3Sort(*Sort).Sort))); - } - - llvm::APSInt getBitvector(const SMTExprRef &Exp, unsigned BitWidth, - bool isUnsigned) override { - return llvm::APSInt( - llvm::APInt(BitWidth, - Z3_get_numeral_string(Context.Context, toZ3Expr(*Exp).AST), - 10), - isUnsigned); - } - - bool getBoolean(const SMTExprRef &Exp) override { - return Z3_get_bool_value(Context.Context, toZ3Expr(*Exp).AST) == Z3_L_TRUE; - } - - SMTExprRef getFloatRoundingMode() override { - // TODO: Don't assume nearest ties to even rounding mode - return newExprRef(Z3Expr(Context, Z3_mk_fpa_rne(Context.Context))); - } - - bool toAPFloat(const SMTSortRef &Sort, const SMTExprRef &AST, - llvm::APFloat &Float, bool useSemantics) { - assert(Sort->isFloatSort() && "Unsupported sort to floating-point!"); - - llvm::APSInt Int(Sort->getFloatSortSize(), true); - const llvm::fltSemantics &Semantics = - getFloatSemantics(Sort->getFloatSortSize()); - SMTSortRef BVSort = getBitvectorSort(Sort->getFloatSortSize()); - if (!toAPSInt(BVSort, AST, Int, true)) { - return false; - } - - if (useSemantics && !areEquivalent(Float.getSemantics(), Semantics)) { - assert(false && "Floating-point types don't match!"); - return false; - } - - Float = llvm::APFloat(Semantics, Int); - return true; - } - - bool toAPSInt(const SMTSortRef &Sort, const SMTExprRef &AST, - llvm::APSInt &Int, bool useSemantics) { - if (Sort->isBitvectorSort()) { - if (useSemantics && Int.getBitWidth() != Sort->getBitvectorSortSize()) { - assert(false && "Bitvector types don't match!"); - return false; - } - - // FIXME: This function is also used to retrieve floating-point values, - // which can be 16, 32, 64 or 128 bits long. Bitvectors can be anything - // between 1 and 64 bits long, which is the reason we have this weird - // guard. In the future, we need proper calls in the backend to retrieve - // floating-points and its special values (NaN, +/-infinity, +/-zero), - // then we can drop this weird condition. - if (Sort->getBitvectorSortSize() <= 64 || - Sort->getBitvectorSortSize() == 128) { - Int = getBitvector(AST, Int.getBitWidth(), Int.isUnsigned()); - return true; - } - - assert(false && "Bitwidth not supported!"); - return false; - } - - if (Sort->isBooleanSort()) { - if (useSemantics && Int.getBitWidth() < 1) { - assert(false && "Boolean type doesn't match!"); - return false; - } - - Int = llvm::APSInt(llvm::APInt(Int.getBitWidth(), getBoolean(AST)), - Int.isUnsigned()); - return true; - } - - llvm_unreachable("Unsupported sort to integer!"); - } - - bool getInterpretation(const SMTExprRef &Exp, llvm::APSInt &Int) override { - Z3Model Model(Context, Z3_solver_get_model(Context.Context, Solver)); - Z3_func_decl Func = Z3_get_app_decl( - Context.Context, Z3_to_app(Context.Context, toZ3Expr(*Exp).AST)); - if (Z3_model_has_interp(Context.Context, Model.Model, Func) != Z3_L_TRUE) - return false; - - SMTExprRef Assign = newExprRef( - Z3Expr(Context, - Z3_model_get_const_interp(Context.Context, Model.Model, Func))); - SMTSortRef Sort = getSort(Assign); - return toAPSInt(Sort, Assign, Int, true); - } - - bool getInterpretation(const SMTExprRef &Exp, llvm::APFloat &Float) override { - Z3Model Model(Context, Z3_solver_get_model(Context.Context, Solver)); - Z3_func_decl Func = Z3_get_app_decl( - Context.Context, Z3_to_app(Context.Context, toZ3Expr(*Exp).AST)); - if (Z3_model_has_interp(Context.Context, Model.Model, Func) != Z3_L_TRUE) - return false; - - SMTExprRef Assign = newExprRef( - Z3Expr(Context, - Z3_model_get_const_interp(Context.Context, Model.Model, Func))); - SMTSortRef Sort = getSort(Assign); - return toAPFloat(Sort, Assign, Float, true); - } - - Optional<bool> check() const override { - Z3_lbool res = Z3_solver_check(Context.Context, Solver); - if (res == Z3_L_TRUE) - return true; - - if (res == Z3_L_FALSE) - return false; - - return Optional<bool>(); - } - - void push() override { return Z3_solver_push(Context.Context, Solver); } - - void pop(unsigned NumStates = 1) override { - assert(Z3_solver_get_num_scopes(Context.Context, Solver) >= NumStates); - return Z3_solver_pop(Context.Context, Solver, NumStates); - } - - bool isFPSupported() override { return true; } - - /// Reset the solver and remove all constraints. - void reset() override { Z3_solver_reset(Context.Context, Solver); } - - void print(raw_ostream &OS) const override { - OS << Z3_solver_to_string(Context.Context, Solver); - } -}; // end class Z3Solver - -class Z3ConstraintManager : public SMTConstraintManager<ConstraintZ3, Z3Expr> { - SMTSolverRef Solver = CreateZ3Solver(); - -public: - Z3ConstraintManager(SubEngine *SE, SValBuilder &SB) - : SMTConstraintManager(SE, SB, Solver) {} -}; // end class Z3ConstraintManager - -} // end anonymous namespace - -#endif - -SMTSolverRef clang::ento::CreateZ3Solver() { -#if CLANG_ANALYZER_WITH_Z3 - return llvm::make_unique<Z3Solver>(); -#else - llvm::report_fatal_error("Clang was not compiled with Z3 support, rebuild " - "with -DCLANG_ANALYZER_ENABLE_Z3_SOLVER=ON", - false); - return nullptr; -#endif -} - -std::unique_ptr<ConstraintManager> -ento::CreateZ3ConstraintManager(ProgramStateManager &StMgr, SubEngine *Eng) { -#if CLANG_ANALYZER_WITH_Z3 - return llvm::make_unique<Z3ConstraintManager>(Eng, StMgr.getSValBuilder()); -#else - llvm::report_fatal_error("Clang was not compiled with Z3 support, rebuild " - "with -DCLANG_ANALYZER_ENABLE_Z3_SOLVER=ON", - false); - return nullptr; -#endif -} |