diff options
Diffstat (limited to 'lib/StaticAnalyzer/Core')
41 files changed, 6669 insertions, 5397 deletions
diff --git a/lib/StaticAnalyzer/Core/AnalysisManager.cpp b/lib/StaticAnalyzer/Core/AnalysisManager.cpp index 1cc08f0d9fe7..dc0d3ec8493a 100644 --- a/lib/StaticAnalyzer/Core/AnalysisManager.cpp +++ b/lib/StaticAnalyzer/Core/AnalysisManager.cpp @@ -26,9 +26,12 @@ AnalysisManager::AnalysisManager( // Adding LoopExit elements to the CFG is a requirement for loop // unrolling. Options.includeLoopExitInCFG() || Options.shouldUnrollLoops(), + Options.includeScopesInCFG(), Options.shouldSynthesizeBodies(), Options.shouldConditionalizeStaticInitializers(), /*addCXXNewAllocator=*/true, + Options.includeRichConstructorsInCFG(), + Options.shouldElideConstructors(), injector), Ctx(ASTCtx), Diags(diags), LangOpts(lang), PathConsumers(PDC), CreateStoreMgr(storemgr), CreateConstraintMgr(constraintmgr), diff --git a/lib/StaticAnalyzer/Core/AnalyzerOptions.cpp b/lib/StaticAnalyzer/Core/AnalyzerOptions.cpp index 48e3e22af04a..9b2dc32e0600 100644 --- a/lib/StaticAnalyzer/Core/AnalyzerOptions.cpp +++ b/lib/StaticAnalyzer/Core/AnalyzerOptions.cpp @@ -1,4 +1,4 @@ -//===-- AnalyzerOptions.cpp - Analysis Engine Options -----------*- C++ -*-===// +//===- AnalyzerOptions.cpp - Analysis Engine Options ----------------------===// // // The LLVM Compiler Infrastructure // @@ -16,8 +16,15 @@ #include "clang/StaticAnalyzer/Core/Checker.h" #include "llvm/ADT/SmallString.h" #include "llvm/ADT/StringSwitch.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/ADT/Twine.h" #include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/FileSystem.h" #include "llvm/Support/raw_ostream.h" +#include <cassert> +#include <cstddef> +#include <utility> +#include <vector> using namespace clang; using namespace ento; @@ -55,9 +62,32 @@ AnalyzerOptions::UserModeKind AnalyzerOptions::getUserMode() { return UserMode; } +AnalyzerOptions::ExplorationStrategyKind +AnalyzerOptions::getExplorationStrategy() { + if (ExplorationStrategy == ExplorationStrategyKind::NotSet) { + StringRef StratStr = + Config + .insert(std::make_pair("exploration_strategy", "unexplored_first_queue")) + .first->second; + ExplorationStrategy = + llvm::StringSwitch<ExplorationStrategyKind>(StratStr) + .Case("dfs", ExplorationStrategyKind::DFS) + .Case("bfs", ExplorationStrategyKind::BFS) + .Case("unexplored_first", + ExplorationStrategyKind::UnexploredFirst) + .Case("unexplored_first_queue", + ExplorationStrategyKind::UnexploredFirstQueue) + .Case("bfs_block_dfs_contents", + ExplorationStrategyKind::BFSBlockDFSContents) + .Default(ExplorationStrategyKind::NotSet); + assert(ExplorationStrategy != ExplorationStrategyKind::NotSet && + "User mode is invalid."); + } + return ExplorationStrategy; +} + IPAKind AnalyzerOptions::getIPAMode() { if (IPAMode == IPAK_NotSet) { - // Use the User Mode to set the default IPA value. // Note, we have to add the string to the Config map for the ConfigDumper // checker to function properly. @@ -169,7 +199,7 @@ bool AnalyzerOptions::getBooleanOption(Optional<bool> &V, StringRef Name, bool AnalyzerOptions::includeTemporaryDtorsInCFG() { return getBooleanOption(IncludeTemporaryDtorsInCFG, "cfg-temporary-dtors", - /* Default = */ false); + /* Default = */ true); } bool AnalyzerOptions::includeImplicitDtorsInCFG() { @@ -185,7 +215,19 @@ bool AnalyzerOptions::includeLifetimeInCFG() { bool AnalyzerOptions::includeLoopExitInCFG() { return getBooleanOption(IncludeLoopExitInCFG, "cfg-loopexit", - /* Default = */ false); + /* Default = */ false); +} + +bool AnalyzerOptions::includeRichConstructorsInCFG() { + return getBooleanOption(IncludeRichConstructorsInCFG, + "cfg-rich-constructors", + /* Default = */ true); +} + +bool AnalyzerOptions::includeScopesInCFG() { + return getBooleanOption(IncludeScopesInCFG, + "cfg-scopes", + /* Default = */ false); } bool AnalyzerOptions::mayInlineCXXStandardLibrary() { @@ -203,7 +245,7 @@ bool AnalyzerOptions::mayInlineTemplateFunctions() { bool AnalyzerOptions::mayInlineCXXAllocator() { return getBooleanOption(InlineCXXAllocator, "c++-allocator-inlining", - /*Default=*/false); + /*Default=*/true); } bool AnalyzerOptions::mayInlineCXXContainerMethods() { @@ -218,6 +260,11 @@ bool AnalyzerOptions::mayInlineCXXSharedPtrDtor() { /*Default=*/false); } +bool AnalyzerOptions::mayInlineCXXTemporaryDtors() { + return getBooleanOption(InlineCXXTemporaryDtors, + "c++-temp-dtor-inlining", + /*Default=*/true); +} bool AnalyzerOptions::mayInlineObjCMethod() { return getBooleanOption(ObjCInliningMode, @@ -249,6 +296,12 @@ bool AnalyzerOptions::shouldSuppressFromCXXStandardLibrary() { /* Default = */ true); } +bool AnalyzerOptions::shouldCrosscheckWithZ3() { + return getBooleanOption(CrosscheckWithZ3, + "crosscheck-with-z3", + /* Default = */ false); +} + bool AnalyzerOptions::shouldReportIssuesInMainSourceFile() { return getBooleanOption(ReportIssuesInMainSourceFile, "report-in-main-source-file", @@ -262,6 +315,18 @@ bool AnalyzerOptions::shouldWriteStableReportFilename() { /* Default = */ false); } +bool AnalyzerOptions::shouldSerializeStats() { + return getBooleanOption(SerializeStats, + "serialize-stats", + /* Default = */ false); +} + +bool AnalyzerOptions::shouldElideConstructors() { + return getBooleanOption(ElideConstructors, + "elide-constructors", + /* Default = */ true); +} + int AnalyzerOptions::getOptionAsInteger(StringRef Name, int DefaultVal, const CheckerBase *C, bool SearchInParents) { @@ -299,7 +364,6 @@ unsigned AnalyzerOptions::getAlwaysInlineSize() { unsigned AnalyzerOptions::getMaxInlinableSize() { if (!MaxInlinableSize.hasValue()) { - int DefaultValue = 0; UserModeKind HighLevelMode = getUserMode(); switch (HighLevelMode) { @@ -324,6 +388,12 @@ unsigned AnalyzerOptions::getGraphTrimInterval() { return GraphTrimInterval.getValue(); } +unsigned AnalyzerOptions::getMaxSymbolComplexity() { + if (!MaxSymbolComplexity.hasValue()) + MaxSymbolComplexity = getOptionAsInteger("max-symbol-complexity", 35); + return MaxSymbolComplexity.getValue(); +} + unsigned AnalyzerOptions::getMaxTimesInlineLarge() { if (!MaxTimesInlineLarge.hasValue()) MaxTimesInlineLarge = getOptionAsInteger("max-times-inline-large", 32); @@ -392,3 +462,34 @@ bool AnalyzerOptions::shouldDisplayNotesAsEvents() { getBooleanOption("notes-as-events", /*Default=*/false); return DisplayNotesAsEvents.getValue(); } + +bool AnalyzerOptions::shouldAggressivelySimplifyBinaryOperation() { + if (!AggressiveBinaryOperationSimplification.hasValue()) + AggressiveBinaryOperationSimplification = + getBooleanOption("aggressive-binary-operation-simplification", + /*Default=*/false); + return AggressiveBinaryOperationSimplification.getValue(); +} + +StringRef AnalyzerOptions::getCTUDir() { + if (!CTUDir.hasValue()) { + CTUDir = getOptionAsString("ctu-dir", ""); + if (!llvm::sys::fs::is_directory(*CTUDir)) + CTUDir = ""; + } + return CTUDir.getValue(); +} + +bool AnalyzerOptions::naiveCTUEnabled() { + if (!NaiveCTU.hasValue()) { + NaiveCTU = getBooleanOption("experimental-enable-naive-ctu-analysis", + /*Default=*/false); + } + return NaiveCTU.getValue(); +} + +StringRef AnalyzerOptions::getCTUIndexName() { + if (!CTUIndexName.hasValue()) + CTUIndexName = getOptionAsString("ctu-index-name", "externalFnMap.txt"); + return CTUIndexName.getValue(); +} diff --git a/lib/StaticAnalyzer/Core/BasicValueFactory.cpp b/lib/StaticAnalyzer/Core/BasicValueFactory.cpp index ec7a7e9e4b1c..db4c1432ccc3 100644 --- a/lib/StaticAnalyzer/Core/BasicValueFactory.cpp +++ b/lib/StaticAnalyzer/Core/BasicValueFactory.cpp @@ -1,4 +1,4 @@ -//=== BasicValueFactory.cpp - Basic values for Path Sens analysis --*- C++ -*-// +//===- BasicValueFactory.cpp - Basic values for Path Sens analysis --------===// // // The LLVM Compiler Infrastructure // @@ -13,9 +13,18 @@ // //===----------------------------------------------------------------------===// -#include "clang/AST/ASTContext.h" #include "clang/StaticAnalyzer/Core/PathSensitive/BasicValueFactory.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/APSIntType.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h" #include "clang/StaticAnalyzer/Core/PathSensitive/Store.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/StoreRef.h" +#include "llvm/ADT/APSInt.h" +#include "llvm/ADT/FoldingSet.h" +#include "llvm/ADT/ImmutableList.h" +#include "llvm/ADT/STLExtras.h" +#include <cassert> +#include <cstdint> +#include <utility> using namespace clang; using namespace ento; @@ -40,10 +49,11 @@ void PointerToMemberData::Profile( ID.AddPointer(L.getInternalPointer()); } -typedef std::pair<SVal, uintptr_t> SValData; -typedef std::pair<SVal, SVal> SValPair; +using SValData = std::pair<SVal, uintptr_t>; +using SValPair = std::pair<SVal, SVal>; namespace llvm { + template<> struct FoldingSetTrait<SValData> { static inline void Profile(const SValData& X, llvm::FoldingSetNodeID& ID) { X.first.Profile(ID); @@ -57,20 +67,21 @@ template<> struct FoldingSetTrait<SValPair> { X.second.Profile(ID); } }; -} -typedef llvm::FoldingSet<llvm::FoldingSetNodeWrapper<SValData> > - PersistentSValsTy; +} // namespace llvm -typedef llvm::FoldingSet<llvm::FoldingSetNodeWrapper<SValPair> > - PersistentSValPairsTy; +using PersistentSValsTy = + llvm::FoldingSet<llvm::FoldingSetNodeWrapper<SValData>>; + +using PersistentSValPairsTy = + llvm::FoldingSet<llvm::FoldingSetNodeWrapper<SValPair>>; BasicValueFactory::~BasicValueFactory() { // Note that the dstor for the contents of APSIntSet will never be called, // so we iterate over the set and invoke the dstor for each APSInt. This // frees an aux. memory allocated to represent very large constants. - for (APSIntSetTy::iterator I=APSIntSet.begin(), E=APSIntSet.end(); I!=E; ++I) - I->getValue().~APSInt(); + for (const auto &I : APSIntSet) + I.getValue().~APSInt(); delete (PersistentSValsTy*) PersistentSVals; delete (PersistentSValPairsTy*) PersistentSValPairs; @@ -79,7 +90,8 @@ BasicValueFactory::~BasicValueFactory() { const llvm::APSInt& BasicValueFactory::getValue(const llvm::APSInt& X) { llvm::FoldingSetNodeID ID; void *InsertPos; - typedef llvm::FoldingSetNodeWrapper<llvm::APSInt> FoldNodeTy; + + using FoldNodeTy = llvm::FoldingSetNodeWrapper<llvm::APSInt>; X.Profile(ID); FoldNodeTy* P = APSIntSet.FindNodeOrInsertPos(ID, InsertPos); @@ -107,14 +119,12 @@ const llvm::APSInt& BasicValueFactory::getValue(uint64_t X, unsigned BitWidth, } const llvm::APSInt& BasicValueFactory::getValue(uint64_t X, QualType T) { - return getValue(getAPSIntType(T).getValue(X)); } const CompoundValData* BasicValueFactory::getCompoundValData(QualType T, llvm::ImmutableList<SVal> Vals) { - llvm::FoldingSetNodeID ID; CompoundValData::Profile(ID, T, Vals); void *InsertPos; @@ -150,7 +160,7 @@ BasicValueFactory::getLazyCompoundValData(const StoreRef &store, } const PointerToMemberData *BasicValueFactory::getPointerToMemberData( - const DeclaratorDecl *DD, llvm::ImmutableList<const CXXBaseSpecifier*> L) { + const DeclaratorDecl *DD, llvm::ImmutableList<const CXXBaseSpecifier *> L) { llvm::FoldingSetNodeID ID; PointerToMemberData::Profile(ID, DD, L); void *InsertPos; @@ -167,7 +177,7 @@ const PointerToMemberData *BasicValueFactory::getPointerToMemberData( return D; } -const clang::ento::PointerToMemberData *BasicValueFactory::accumCXXBase( +const PointerToMemberData *BasicValueFactory::accumCXXBase( llvm::iterator_range<CastExpr::path_const_iterator> PathRange, const nonloc::PointerToMember &PTM) { nonloc::PointerToMember::PTMDataType PTMDT = PTM.getPTMData(); @@ -195,10 +205,9 @@ const clang::ento::PointerToMemberData *BasicValueFactory::accumCXXBase( const llvm::APSInt* BasicValueFactory::evalAPSInt(BinaryOperator::Opcode Op, const llvm::APSInt& V1, const llvm::APSInt& V2) { - switch (Op) { default: - assert (false && "Invalid Opcode."); + assert(false && "Invalid Opcode."); case BO_Mul: return &getValue( V1 * V2 ); @@ -220,11 +229,9 @@ BasicValueFactory::evalAPSInt(BinaryOperator::Opcode Op, return &getValue( V1 - V2 ); case BO_Shl: { - // FIXME: This logic should probably go higher up, where we can // test these conditions symbolically. - // FIXME: Expand these checks to include all undefined behavior. if (V1.isSigned() && V1.isNegative()) return nullptr; @@ -236,16 +243,16 @@ BasicValueFactory::evalAPSInt(BinaryOperator::Opcode Op, if (Amt >= V1.getBitWidth()) return nullptr; + if (V1.isSigned() && Amt > V1.countLeadingZeros()) + return nullptr; + return &getValue( V1.operator<<( (unsigned) Amt )); } case BO_Shr: { - // FIXME: This logic should probably go higher up, where we can // test these conditions symbolically. - // FIXME: Expand these checks to include all undefined behavior. - if (V2.isSigned() && V2.isNegative()) return nullptr; @@ -288,10 +295,8 @@ BasicValueFactory::evalAPSInt(BinaryOperator::Opcode Op, } } - const std::pair<SVal, uintptr_t>& BasicValueFactory::getPersistentSValWithData(const SVal& V, uintptr_t Data) { - // Lazily create the folding set. if (!PersistentSVals) PersistentSVals = new PersistentSValsTy(); @@ -302,7 +307,8 @@ BasicValueFactory::getPersistentSValWithData(const SVal& V, uintptr_t Data) { PersistentSValsTy& Map = *((PersistentSValsTy*) PersistentSVals); - typedef llvm::FoldingSetNodeWrapper<SValData> FoldNodeTy; + using FoldNodeTy = llvm::FoldingSetNodeWrapper<SValData>; + FoldNodeTy* P = Map.FindNodeOrInsertPos(ID, InsertPos); if (!P) { @@ -316,7 +322,6 @@ BasicValueFactory::getPersistentSValWithData(const SVal& V, uintptr_t Data) { const std::pair<SVal, SVal>& BasicValueFactory::getPersistentSValPair(const SVal& V1, const SVal& V2) { - // Lazily create the folding set. if (!PersistentSValPairs) PersistentSValPairs = new PersistentSValPairsTy(); @@ -327,7 +332,8 @@ BasicValueFactory::getPersistentSValPair(const SVal& V1, const SVal& V2) { PersistentSValPairsTy& Map = *((PersistentSValPairsTy*) PersistentSValPairs); - typedef llvm::FoldingSetNodeWrapper<SValPair> FoldNodeTy; + using FoldNodeTy = llvm::FoldingSetNodeWrapper<SValPair>; + FoldNodeTy* P = Map.FindNodeOrInsertPos(ID, InsertPos); if (!P) { diff --git a/lib/StaticAnalyzer/Core/BugReporter.cpp b/lib/StaticAnalyzer/Core/BugReporter.cpp index dc284888eb03..f990eb6a058d 100644 --- a/lib/StaticAnalyzer/Core/BugReporter.cpp +++ b/lib/StaticAnalyzer/Core/BugReporter.cpp @@ -1,4 +1,4 @@ -// BugReporter.cpp - Generate PathDiagnostics for Bugs ------------*- C++ -*--// +//===- BugReporter.cpp - Generate PathDiagnostics for bugs ----------------===// // // The LLVM Compiler Infrastructure // @@ -13,28 +13,62 @@ //===----------------------------------------------------------------------===// #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" -#include "clang/AST/ASTContext.h" +#include "clang/AST/Decl.h" +#include "clang/AST/DeclBase.h" #include "clang/AST/DeclObjC.h" #include "clang/AST/Expr.h" #include "clang/AST/ExprCXX.h" #include "clang/AST/ParentMap.h" +#include "clang/AST/Stmt.h" #include "clang/AST/StmtCXX.h" #include "clang/AST/StmtObjC.h" +#include "clang/Analysis/AnalysisDeclContext.h" #include "clang/Analysis/CFG.h" #include "clang/Analysis/CFGStmtMap.h" #include "clang/Analysis/ProgramPoint.h" +#include "clang/Basic/LLVM.h" +#include "clang/Basic/SourceLocation.h" #include "clang/Basic/SourceManager.h" +#include "clang/StaticAnalyzer/Core/AnalyzerOptions.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugReporterVisitors.h" #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" #include "clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h" +#include "clang/StaticAnalyzer/Core/Checker.h" +#include "clang/StaticAnalyzer/Core/CheckerManager.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h" +#include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/DenseMap.h" -#include "llvm/ADT/IntrusiveRefCntPtr.h" +#include "llvm/ADT/DenseSet.h" +#include "llvm/ADT/FoldingSet.h" +#include "llvm/ADT/None.h" +#include "llvm/ADT/Optional.h" #include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/SmallPtrSet.h" #include "llvm/ADT/SmallString.h" +#include "llvm/ADT/SmallVector.h" #include "llvm/ADT/Statistic.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/ADT/iterator_range.h" +#include "llvm/Support/Casting.h" +#include "llvm/Support/Compiler.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/raw_ostream.h" +#include <algorithm> +#include <cassert> +#include <cstddef> +#include <iterator> #include <memory> #include <queue> +#include <string> +#include <tuple> +#include <utility> +#include <vector> using namespace clang; using namespace ento; @@ -47,7 +81,7 @@ STATISTIC(MaxValidBugClassSize, "The maximum number of bug reports in the same equivalence class " "where at least one report is valid (not suppressed)"); -BugReporterVisitor::~BugReporterVisitor() {} +BugReporterVisitor::~BugReporterVisitor() = default; void BugReporterContext::anchor() {} @@ -127,10 +161,9 @@ static void removeRedundantMsgs(PathPieces &path) { if (i == N-1) break; - if (PathDiagnosticEventPiece *nextEvent = + if (auto *nextEvent = dyn_cast<PathDiagnosticEventPiece>(path.front().get())) { - PathDiagnosticEventPiece *event = - cast<PathDiagnosticEventPiece>(piece.get()); + auto *event = cast<PathDiagnosticEventPiece>(piece.get()); // Check to see if we should keep one of the two pieces. If we // come up with a preference, record which piece to keep, and consume // another piece from the path. @@ -152,15 +185,16 @@ static void removeRedundantMsgs(PathPieces &path) { /// A map from PathDiagnosticPiece to the LocationContext of the inlined /// function call it represents. -typedef llvm::DenseMap<const PathPieces *, const LocationContext *> - LocationContextMap; +using LocationContextMap = + llvm::DenseMap<const PathPieces *, const LocationContext *>; /// Recursively scan through a path and prune out calls and macros pieces /// that aren't needed. Return true if afterwards the path contains /// "interesting stuff" which means it shouldn't be pruned from the parent path. static bool removeUnneededCalls(PathPieces &pieces, BugReport *R, - LocationContextMap &LCM) { - bool containsSomethingInteresting = false; + LocationContextMap &LCM, + bool IsInteresting = false) { + bool containsSomethingInteresting = IsInteresting; const unsigned N = pieces.size(); for (unsigned i = 0 ; i < N ; ++i) { @@ -174,12 +208,8 @@ static bool removeUnneededCalls(PathPieces &pieces, BugReport *R, auto &call = cast<PathDiagnosticCallPiece>(*piece); // Check if the location context is interesting. assert(LCM.count(&call.path)); - if (R->isInteresting(LCM[&call.path])) { - containsSomethingInteresting = true; - break; - } - - if (!removeUnneededCalls(call.path, R, LCM)) + if (!removeUnneededCalls(call.path, R, LCM, + R->isInteresting(LCM[&call.path]))) continue; containsSomethingInteresting = true; @@ -187,7 +217,7 @@ static bool removeUnneededCalls(PathPieces &pieces, BugReport *R, } case PathDiagnosticPiece::Macro: { auto ¯o = cast<PathDiagnosticMacroPiece>(*piece); - if (!removeUnneededCalls(macro.subPieces, R, LCM)) + if (!removeUnneededCalls(macro.subPieces, R, LCM, IsInteresting)) continue; containsSomethingInteresting = true; break; @@ -225,13 +255,11 @@ static bool hasImplicitBody(const Decl *D) { static void adjustCallLocations(PathPieces &Pieces, PathDiagnosticLocation *LastCallLocation = nullptr) { - for (PathPieces::iterator I = Pieces.begin(), E = Pieces.end(); I != E; ++I) { - PathDiagnosticCallPiece *Call = dyn_cast<PathDiagnosticCallPiece>(I->get()); + for (const auto &I : Pieces) { + auto *Call = dyn_cast<PathDiagnosticCallPiece>(I.get()); - if (!Call) { - assert((*I)->getLocation().asLocation().isValid()); + if (!Call) continue; - } if (LastCallLocation) { bool CallerIsImplicit = hasImplicitBody(Call->getCaller()); @@ -314,29 +342,19 @@ static void removePiecesWithInvalidLocations(PathPieces &Pieces) { //===----------------------------------------------------------------------===// namespace { -class NodeMapClosure : public BugReport::NodeResolver { - InterExplodedGraphMap &M; -public: - NodeMapClosure(InterExplodedGraphMap &m) : M(m) {} - - const ExplodedNode *getOriginalNode(const ExplodedNode *N) override { - return M.lookup(N); - } -}; class PathDiagnosticBuilder : public BugReporterContext { BugReport *R; PathDiagnosticConsumer *PDC; - NodeMapClosure NMC; + public: const LocationContext *LC; PathDiagnosticBuilder(GRBugReporter &br, BugReport *r, InterExplodedGraphMap &Backmap, PathDiagnosticConsumer *pdc) - : BugReporterContext(br), - R(r), PDC(pdc), NMC(Backmap), LC(r->getErrorNode()->getLocationContext()) - {} + : BugReporterContext(br, Backmap), R(r), PDC(pdc), + LC(r->getErrorNode()->getLocationContext()) {} PathDiagnosticLocation ExecutionContinues(const ExplodedNode *N); @@ -353,19 +371,18 @@ public: return getParentMap().getParent(S); } - NodeMapClosure& getNodeResolver() override { return NMC; } - PathDiagnosticLocation getEnclosingStmtLocation(const Stmt *S); PathDiagnosticConsumer::PathGenerationScheme getGenerationScheme() const { - return PDC ? PDC->getGenerationScheme() : PathDiagnosticConsumer::Extensive; + return PDC ? PDC->getGenerationScheme() : PathDiagnosticConsumer::Minimal; } bool supportsLogicalOpControlFlow() const { return PDC ? PDC->supportsLogicalOpControlFlow() : true; } }; -} // end anonymous namespace + +} // namespace PathDiagnosticLocation PathDiagnosticBuilder::ExecutionContinues(const ExplodedNode *N) { @@ -379,7 +396,6 @@ PathDiagnosticBuilder::ExecutionContinues(const ExplodedNode *N) { PathDiagnosticLocation PathDiagnosticBuilder::ExecutionContinues(llvm::raw_string_ostream &os, const ExplodedNode *N) { - // Slow, but probably doesn't matter. if (os.str().empty()) os << ' '; @@ -433,12 +449,12 @@ static PathDiagnosticLocation getEnclosingStmtLocation(const Stmt *S, SourceManager &SMgr, const ParentMap &P, const LocationContext *LC, bool allowNestedContexts) { if (!S) - return PathDiagnosticLocation(); + return {}; while (const Stmt *Parent = getEnclosingParent(S, P)) { switch (Parent->getStmtClass()) { case Stmt::BinaryOperatorClass: { - const BinaryOperator *B = cast<BinaryOperator>(Parent); + const auto *B = cast<BinaryOperator>(Parent); if (B->isLogicalOp()) return PathDiagnosticLocation(allowNestedContexts ? B : S, SMgr, LC); break; @@ -504,46 +520,21 @@ PathDiagnosticBuilder::getEnclosingStmtLocation(const Stmt *S) { } //===----------------------------------------------------------------------===// -// "Visitors only" path diagnostic generation algorithm. -//===----------------------------------------------------------------------===// -static bool GenerateVisitorsOnlyPathDiagnostic( - PathDiagnostic &PD, PathDiagnosticBuilder &PDB, const ExplodedNode *N, - ArrayRef<std::unique_ptr<BugReporterVisitor>> visitors) { - // All path generation skips the very first node (the error node). - // This is because there is special handling for the end-of-path note. - N = N->getFirstPred(); - if (!N) - return true; - - BugReport *R = PDB.getBugReport(); - while (const ExplodedNode *Pred = N->getFirstPred()) { - for (auto &V : visitors) - // Visit all the node pairs, but throw the path pieces away. - V->VisitNode(N, Pred, PDB, *R); - - N = Pred; - } - - return R->isValid(); -} - -//===----------------------------------------------------------------------===// // "Minimal" path diagnostic generation algorithm. //===----------------------------------------------------------------------===// -typedef std::pair<PathDiagnosticCallPiece*, const ExplodedNode*> StackDiagPair; -typedef SmallVector<StackDiagPair, 6> StackDiagVector; +using StackDiagPair = + std::pair<PathDiagnosticCallPiece *, const ExplodedNode *>; +using StackDiagVector = SmallVector<StackDiagPair, 6>; static void updateStackPiecesWithMessage(PathDiagnosticPiece &P, StackDiagVector &CallStack) { // If the piece contains a special message, add it to all the call // pieces on the active stack. - if (PathDiagnosticEventPiece *ep = dyn_cast<PathDiagnosticEventPiece>(&P)) { - + if (auto *ep = dyn_cast<PathDiagnosticEventPiece>(&P)) { if (ep->hasCallStackHint()) - for (StackDiagVector::iterator I = CallStack.begin(), - E = CallStack.end(); I != E; ++I) { - PathDiagnosticCallPiece *CP = I->first; - const ExplodedNode *N = I->second; + for (const auto &I : CallStack) { + PathDiagnosticCallPiece *CP = I.first; + const ExplodedNode *N = I.second; std::string stackMsg = ep->getCallStackMessage(N); // The last message on the path to final bug is the most important @@ -557,693 +548,271 @@ static void updateStackPiecesWithMessage(PathDiagnosticPiece &P, static void CompactPathDiagnostic(PathPieces &path, const SourceManager& SM); -static bool GenerateMinimalPathDiagnostic( - PathDiagnostic &PD, PathDiagnosticBuilder &PDB, const ExplodedNode *N, - LocationContextMap &LCM, - ArrayRef<std::unique_ptr<BugReporterVisitor>> visitors) { - - SourceManager& SMgr = PDB.getSourceManager(); - const LocationContext *LC = PDB.LC; - const ExplodedNode *NextNode = N->pred_empty() - ? nullptr : *(N->pred_begin()); - - StackDiagVector CallStack; - - while (NextNode) { - N = NextNode; - PDB.LC = N->getLocationContext(); - NextNode = N->getFirstPred(); - - ProgramPoint P = N->getLocation(); - - do { - if (Optional<CallExitEnd> CE = P.getAs<CallExitEnd>()) { - auto C = PathDiagnosticCallPiece::construct(N, *CE, SMgr); - // Record the mapping from call piece to LocationContext. - LCM[&C->path] = CE->getCalleeContext(); - auto *P = C.get(); - PD.getActivePath().push_front(std::move(C)); - PD.pushActivePath(&P->path); - CallStack.push_back(StackDiagPair(P, N)); - break; - } - - if (Optional<CallEnter> CE = P.getAs<CallEnter>()) { - // Flush all locations, and pop the active path. - bool VisitedEntireCall = PD.isWithinCall(); - PD.popActivePath(); - - // Either we just added a bunch of stuff to the top-level path, or - // we have a previous CallExitEnd. If the former, it means that the - // path terminated within a function call. We must then take the - // current contents of the active path and place it within - // a new PathDiagnosticCallPiece. - PathDiagnosticCallPiece *C; - if (VisitedEntireCall) { - C = cast<PathDiagnosticCallPiece>(PD.getActivePath().front().get()); - } else { - const Decl *Caller = CE->getLocationContext()->getDecl(); - C = PathDiagnosticCallPiece::construct(PD.getActivePath(), Caller); - // Record the mapping from call piece to LocationContext. - LCM[&C->path] = CE->getCalleeContext(); - } - - C->setCallee(*CE, SMgr); - if (!CallStack.empty()) { - assert(CallStack.back().first == C); - CallStack.pop_back(); - } - break; - } - - if (Optional<BlockEdge> BE = P.getAs<BlockEdge>()) { - const CFGBlock *Src = BE->getSrc(); - const CFGBlock *Dst = BE->getDst(); - const Stmt *T = Src->getTerminator(); - - if (!T) - break; - - PathDiagnosticLocation Start = - PathDiagnosticLocation::createBegin(T, SMgr, - N->getLocationContext()); - - switch (T->getStmtClass()) { - default: - break; - - case Stmt::GotoStmtClass: - case Stmt::IndirectGotoStmtClass: { - const Stmt *S = PathDiagnosticLocation::getNextStmt(N); - - if (!S) - break; - - std::string sbuf; - llvm::raw_string_ostream os(sbuf); - const PathDiagnosticLocation &End = PDB.getEnclosingStmtLocation(S); - - os << "Control jumps to line " - << End.asLocation().getExpansionLineNumber(); - PD.getActivePath().push_front( - std::make_shared<PathDiagnosticControlFlowPiece>(Start, End, - os.str())); - break; - } - - case Stmt::SwitchStmtClass: { - // Figure out what case arm we took. - std::string sbuf; - llvm::raw_string_ostream os(sbuf); - - if (const Stmt *S = Dst->getLabel()) { - PathDiagnosticLocation End(S, SMgr, LC); - - switch (S->getStmtClass()) { - default: - os << "No cases match in the switch statement. " - "Control jumps to line " - << End.asLocation().getExpansionLineNumber(); - break; - case Stmt::DefaultStmtClass: - os << "Control jumps to the 'default' case at line " - << End.asLocation().getExpansionLineNumber(); - break; - - case Stmt::CaseStmtClass: { - os << "Control jumps to 'case "; - const CaseStmt *Case = cast<CaseStmt>(S); - const Expr *LHS = Case->getLHS()->IgnoreParenCasts(); - - // Determine if it is an enum. - bool GetRawInt = true; - - if (const DeclRefExpr *DR = dyn_cast<DeclRefExpr>(LHS)) { - // FIXME: Maybe this should be an assertion. Are there cases - // were it is not an EnumConstantDecl? - const EnumConstantDecl *D = - dyn_cast<EnumConstantDecl>(DR->getDecl()); - - if (D) { - GetRawInt = false; - os << *D; - } - } - - if (GetRawInt) - os << LHS->EvaluateKnownConstInt(PDB.getASTContext()); - - os << ":' at line " - << End.asLocation().getExpansionLineNumber(); - break; - } - } - PD.getActivePath().push_front( - std::make_shared<PathDiagnosticControlFlowPiece>(Start, End, - os.str())); - } - else { - os << "'Default' branch taken. "; - const PathDiagnosticLocation &End = PDB.ExecutionContinues(os, N); - PD.getActivePath().push_front( - std::make_shared<PathDiagnosticControlFlowPiece>(Start, End, - os.str())); - } - - break; - } - - case Stmt::BreakStmtClass: - case Stmt::ContinueStmtClass: { - std::string sbuf; - llvm::raw_string_ostream os(sbuf); - PathDiagnosticLocation End = PDB.ExecutionContinues(os, N); - PD.getActivePath().push_front( - std::make_shared<PathDiagnosticControlFlowPiece>(Start, End, - os.str())); - break; - } - - // Determine control-flow for ternary '?'. - case Stmt::BinaryConditionalOperatorClass: - case Stmt::ConditionalOperatorClass: { - std::string sbuf; - llvm::raw_string_ostream os(sbuf); - os << "'?' condition is "; - - if (*(Src->succ_begin()+1) == Dst) - os << "false"; - else - os << "true"; - - PathDiagnosticLocation End = PDB.ExecutionContinues(N); - - if (const Stmt *S = End.asStmt()) - End = PDB.getEnclosingStmtLocation(S); - - PD.getActivePath().push_front( - std::make_shared<PathDiagnosticControlFlowPiece>(Start, End, - os.str())); - break; - } - - // Determine control-flow for short-circuited '&&' and '||'. - case Stmt::BinaryOperatorClass: { - if (!PDB.supportsLogicalOpControlFlow()) - break; - const BinaryOperator *B = cast<BinaryOperator>(T); - std::string sbuf; - llvm::raw_string_ostream os(sbuf); - os << "Left side of '"; - - if (B->getOpcode() == BO_LAnd) { - os << "&&" << "' is "; - - if (*(Src->succ_begin()+1) == Dst) { - os << "false"; - PathDiagnosticLocation End(B->getLHS(), SMgr, LC); - PathDiagnosticLocation Start = - PathDiagnosticLocation::createOperatorLoc(B, SMgr); - PD.getActivePath().push_front( - std::make_shared<PathDiagnosticControlFlowPiece>(Start, End, - os.str())); - } - else { - os << "true"; - PathDiagnosticLocation Start(B->getLHS(), SMgr, LC); - PathDiagnosticLocation End = PDB.ExecutionContinues(N); - PD.getActivePath().push_front( - std::make_shared<PathDiagnosticControlFlowPiece>(Start, End, - os.str())); - } - } - else { - assert(B->getOpcode() == BO_LOr); - os << "||" << "' is "; - - if (*(Src->succ_begin()+1) == Dst) { - os << "false"; - PathDiagnosticLocation Start(B->getLHS(), SMgr, LC); - PathDiagnosticLocation End = PDB.ExecutionContinues(N); - PD.getActivePath().push_front( - std::make_shared<PathDiagnosticControlFlowPiece>(Start, End, - os.str())); - } - else { - os << "true"; - PathDiagnosticLocation End(B->getLHS(), SMgr, LC); - PathDiagnosticLocation Start = - PathDiagnosticLocation::createOperatorLoc(B, SMgr); - PD.getActivePath().push_front( - std::make_shared<PathDiagnosticControlFlowPiece>(Start, End, - os.str())); - } - } - - break; - } +std::shared_ptr<PathDiagnosticControlFlowPiece> generateDiagForSwitchOP( + const ExplodedNode *N, + const CFGBlock *Dst, + const SourceManager &SM, + const LocationContext *LC, + PathDiagnosticBuilder &PDB, + PathDiagnosticLocation &Start + ) { + // Figure out what case arm we took. + std::string sbuf; + llvm::raw_string_ostream os(sbuf); + PathDiagnosticLocation End; - case Stmt::DoStmtClass: { - if (*(Src->succ_begin()) == Dst) { - std::string sbuf; - llvm::raw_string_ostream os(sbuf); - - os << "Loop condition is true. "; - PathDiagnosticLocation End = PDB.ExecutionContinues(os, N); - - if (const Stmt *S = End.asStmt()) - End = PDB.getEnclosingStmtLocation(S); - - PD.getActivePath().push_front( - std::make_shared<PathDiagnosticControlFlowPiece>(Start, End, - os.str())); - } - else { - PathDiagnosticLocation End = PDB.ExecutionContinues(N); - - if (const Stmt *S = End.asStmt()) - End = PDB.getEnclosingStmtLocation(S); - - PD.getActivePath().push_front( - std::make_shared<PathDiagnosticControlFlowPiece>( - Start, End, "Loop condition is false. Exiting loop")); - } - - break; - } - - case Stmt::WhileStmtClass: - case Stmt::ForStmtClass: { - if (*(Src->succ_begin()+1) == Dst) { - std::string sbuf; - llvm::raw_string_ostream os(sbuf); - - os << "Loop condition is false. "; - PathDiagnosticLocation End = PDB.ExecutionContinues(os, N); - if (const Stmt *S = End.asStmt()) - End = PDB.getEnclosingStmtLocation(S); - - PD.getActivePath().push_front( - std::make_shared<PathDiagnosticControlFlowPiece>(Start, End, - os.str())); - } - else { - PathDiagnosticLocation End = PDB.ExecutionContinues(N); - if (const Stmt *S = End.asStmt()) - End = PDB.getEnclosingStmtLocation(S); - - PD.getActivePath().push_front( - std::make_shared<PathDiagnosticControlFlowPiece>( - Start, End, "Loop condition is true. Entering loop body")); - } - - break; - } + if (const Stmt *S = Dst->getLabel()) { + End = PathDiagnosticLocation(S, SM, LC); - case Stmt::IfStmtClass: { - PathDiagnosticLocation End = PDB.ExecutionContinues(N); + switch (S->getStmtClass()) { + default: + os << "No cases match in the switch statement. " + "Control jumps to line " + << End.asLocation().getExpansionLineNumber(); + break; + case Stmt::DefaultStmtClass: + os << "Control jumps to the 'default' case at line " + << End.asLocation().getExpansionLineNumber(); + break; - if (const Stmt *S = End.asStmt()) - End = PDB.getEnclosingStmtLocation(S); + case Stmt::CaseStmtClass: { + os << "Control jumps to 'case "; + const auto *Case = cast<CaseStmt>(S); + const Expr *LHS = Case->getLHS()->IgnoreParenCasts(); - if (*(Src->succ_begin()+1) == Dst) - PD.getActivePath().push_front( - std::make_shared<PathDiagnosticControlFlowPiece>( - Start, End, "Taking false branch")); - else - PD.getActivePath().push_front( - std::make_shared<PathDiagnosticControlFlowPiece>( - Start, End, "Taking true branch")); + // Determine if it is an enum. + bool GetRawInt = true; - break; - } - } - } - } while(0); - - if (NextNode) { - // Add diagnostic pieces from custom visitors. - BugReport *R = PDB.getBugReport(); - llvm::FoldingSet<PathDiagnosticPiece> DeduplicationSet; - for (auto &V : visitors) { - if (auto p = V->VisitNode(N, NextNode, PDB, *R)) { - if (DeduplicationSet.GetOrInsertNode(p.get()) != p.get()) - continue; + if (const auto *DR = dyn_cast<DeclRefExpr>(LHS)) { + // FIXME: Maybe this should be an assertion. Are there cases + // were it is not an EnumConstantDecl? + const auto *D = dyn_cast<EnumConstantDecl>(DR->getDecl()); - updateStackPiecesWithMessage(*p, CallStack); - PD.getActivePath().push_front(std::move(p)); + if (D) { + GetRawInt = false; + os << *D; } } - } - } - - if (!PDB.getBugReport()->isValid()) - return false; - - // After constructing the full PathDiagnostic, do a pass over it to compact - // PathDiagnosticPieces that occur within a macro. - CompactPathDiagnostic(PD.getMutablePieces(), PDB.getSourceManager()); - return true; -} - -//===----------------------------------------------------------------------===// -// "Extensive" PathDiagnostic generation. -//===----------------------------------------------------------------------===// - -static bool IsControlFlowExpr(const Stmt *S) { - const Expr *E = dyn_cast<Expr>(S); - - if (!E) - return false; - - E = E->IgnoreParenCasts(); - - if (isa<AbstractConditionalOperator>(E)) - return true; - - if (const BinaryOperator *B = dyn_cast<BinaryOperator>(E)) - if (B->isLogicalOp()) - return true; - return false; -} - -namespace { -class ContextLocation : public PathDiagnosticLocation { - bool IsDead; -public: - ContextLocation(const PathDiagnosticLocation &L, bool isdead = false) - : PathDiagnosticLocation(L), IsDead(isdead) {} - - void markDead() { IsDead = true; } - bool isDead() const { return IsDead; } -}; - -static PathDiagnosticLocation cleanUpLocation(PathDiagnosticLocation L, - const LocationContext *LC, - bool firstCharOnly = false) { - if (const Stmt *S = L.asStmt()) { - const Stmt *Original = S; - while (1) { - // Adjust the location for some expressions that are best referenced - // by one of their subexpressions. - switch (S->getStmtClass()) { - default: - break; - case Stmt::ParenExprClass: - case Stmt::GenericSelectionExprClass: - S = cast<Expr>(S)->IgnoreParens(); - firstCharOnly = true; - continue; - case Stmt::BinaryConditionalOperatorClass: - case Stmt::ConditionalOperatorClass: - S = cast<AbstractConditionalOperator>(S)->getCond(); - firstCharOnly = true; - continue; - case Stmt::ChooseExprClass: - S = cast<ChooseExpr>(S)->getCond(); - firstCharOnly = true; - continue; - case Stmt::BinaryOperatorClass: - S = cast<BinaryOperator>(S)->getLHS(); - firstCharOnly = true; - continue; - } + if (GetRawInt) + os << LHS->EvaluateKnownConstInt(PDB.getASTContext()); + os << ":' at line " << End.asLocation().getExpansionLineNumber(); break; } - - if (S != Original) - L = PathDiagnosticLocation(S, L.getManager(), LC); - } - - if (firstCharOnly) - L = PathDiagnosticLocation::createSingleLocation(L); - - return L; -} - -class EdgeBuilder { - std::vector<ContextLocation> CLocs; - typedef std::vector<ContextLocation>::iterator iterator; - PathDiagnostic &PD; - PathDiagnosticBuilder &PDB; - PathDiagnosticLocation PrevLoc; - - bool IsConsumedExpr(const PathDiagnosticLocation &L); - - bool containsLocation(const PathDiagnosticLocation &Container, - const PathDiagnosticLocation &Containee); - - PathDiagnosticLocation getContextLocation(const PathDiagnosticLocation &L); - - - - void popLocation() { - if (!CLocs.back().isDead() && CLocs.back().asLocation().isFileID()) { - // For contexts, we only one the first character as the range. - rawAddEdge(cleanUpLocation(CLocs.back(), PDB.LC, true)); } - CLocs.pop_back(); + } else { + os << "'Default' branch taken. "; + End = PDB.ExecutionContinues(os, N); + } + return std::make_shared<PathDiagnosticControlFlowPiece>(Start, End, + os.str()); +} + + +std::shared_ptr<PathDiagnosticControlFlowPiece> generateDiagForGotoOP( + const Stmt *S, + PathDiagnosticBuilder &PDB, + PathDiagnosticLocation &Start) { + std::string sbuf; + llvm::raw_string_ostream os(sbuf); + const PathDiagnosticLocation &End = PDB.getEnclosingStmtLocation(S); + os << "Control jumps to line " << End.asLocation().getExpansionLineNumber(); + return std::make_shared<PathDiagnosticControlFlowPiece>(Start, End, os.str()); + +} + +std::shared_ptr<PathDiagnosticControlFlowPiece> generateDiagForBinaryOP( + const ExplodedNode *N, + const Stmt *T, + const CFGBlock *Src, + const CFGBlock *Dst, + const SourceManager &SM, + PathDiagnosticBuilder &PDB, + const LocationContext *LC) { + const auto *B = cast<BinaryOperator>(T); + std::string sbuf; + llvm::raw_string_ostream os(sbuf); + os << "Left side of '"; + PathDiagnosticLocation Start, End; + + if (B->getOpcode() == BO_LAnd) { + os << "&&" + << "' is "; + + if (*(Src->succ_begin() + 1) == Dst) { + os << "false"; + End = PathDiagnosticLocation(B->getLHS(), SM, LC); + Start = + PathDiagnosticLocation::createOperatorLoc(B, SM); + } else { + os << "true"; + Start = PathDiagnosticLocation(B->getLHS(), SM, LC); + End = PDB.ExecutionContinues(N); + } + } else { + assert(B->getOpcode() == BO_LOr); + os << "||" + << "' is "; + + if (*(Src->succ_begin() + 1) == Dst) { + os << "false"; + Start = PathDiagnosticLocation(B->getLHS(), SM, LC); + End = PDB.ExecutionContinues(N); + } else { + os << "true"; + End = PathDiagnosticLocation(B->getLHS(), SM, LC); + Start = + PathDiagnosticLocation::createOperatorLoc(B, SM); + } } + return std::make_shared<PathDiagnosticControlFlowPiece>(Start, End, + os.str()); +} -public: - EdgeBuilder(PathDiagnostic &pd, PathDiagnosticBuilder &pdb) - : PD(pd), PDB(pdb) { - - // If the PathDiagnostic already has pieces, add the enclosing statement - // of the first piece as a context as well. - if (!PD.path.empty()) { - PrevLoc = (*PD.path.begin())->getLocation(); - - if (const Stmt *S = PrevLoc.asStmt()) - addExtendedContext(PDB.getEnclosingStmtLocation(S).asStmt()); - } - } +void generateMinimalDiagForBlockEdge(const ExplodedNode *N, BlockEdge BE, + const SourceManager &SM, + PathDiagnosticBuilder &PDB, + PathDiagnostic &PD) { + const LocationContext *LC = N->getLocationContext(); + const CFGBlock *Src = BE.getSrc(); + const CFGBlock *Dst = BE.getDst(); + const Stmt *T = Src->getTerminator(); + if (!T) + return; - ~EdgeBuilder() { - while (!CLocs.empty()) popLocation(); + auto Start = PathDiagnosticLocation::createBegin(T, SM, LC); + switch (T->getStmtClass()) { + default: + break; - // Finally, add an initial edge from the start location of the first - // statement (if it doesn't already exist). - PathDiagnosticLocation L = PathDiagnosticLocation::createDeclBegin( - PDB.LC, - PDB.getSourceManager()); - if (L.isValid()) - rawAddEdge(L); + case Stmt::GotoStmtClass: + case Stmt::IndirectGotoStmtClass: { + if (const Stmt *S = PathDiagnosticLocation::getNextStmt(N)) + PD.getActivePath().push_front(generateDiagForGotoOP(S, PDB, Start)); + break; } - void flushLocations() { - while (!CLocs.empty()) - popLocation(); - PrevLoc = PathDiagnosticLocation(); + case Stmt::SwitchStmtClass: { + PD.getActivePath().push_front( + generateDiagForSwitchOP(N, Dst, SM, LC, PDB, Start)); + break; } - void addEdge(PathDiagnosticLocation NewLoc, bool alwaysAdd = false, - bool IsPostJump = false); - - void rawAddEdge(PathDiagnosticLocation NewLoc); - - void addContext(const Stmt *S); - void addContext(const PathDiagnosticLocation &L); - void addExtendedContext(const Stmt *S); -}; -} // end anonymous namespace - - -PathDiagnosticLocation -EdgeBuilder::getContextLocation(const PathDiagnosticLocation &L) { - if (const Stmt *S = L.asStmt()) { - if (IsControlFlowExpr(S)) - return L; - - return PDB.getEnclosingStmtLocation(S); + case Stmt::BreakStmtClass: + case Stmt::ContinueStmtClass: { + std::string sbuf; + llvm::raw_string_ostream os(sbuf); + PathDiagnosticLocation End = PDB.ExecutionContinues(os, N); + PD.getActivePath().push_front( + std::make_shared<PathDiagnosticControlFlowPiece>(Start, End, os.str())); + break; } - return L; -} - -bool EdgeBuilder::containsLocation(const PathDiagnosticLocation &Container, - const PathDiagnosticLocation &Containee) { - - if (Container == Containee) - return true; + // Determine control-flow for ternary '?'. + case Stmt::BinaryConditionalOperatorClass: + case Stmt::ConditionalOperatorClass: { + std::string sbuf; + llvm::raw_string_ostream os(sbuf); + os << "'?' condition is "; - if (Container.asDecl()) - return true; + if (*(Src->succ_begin() + 1) == Dst) + os << "false"; + else + os << "true"; - if (const Stmt *S = Containee.asStmt()) - if (const Stmt *ContainerS = Container.asStmt()) { - while (S) { - if (S == ContainerS) - return true; - S = PDB.getParent(S); - } - return false; - } + PathDiagnosticLocation End = PDB.ExecutionContinues(N); - // Less accurate: compare using source ranges. - SourceRange ContainerR = Container.asRange(); - SourceRange ContaineeR = Containee.asRange(); + if (const Stmt *S = End.asStmt()) + End = PDB.getEnclosingStmtLocation(S); - SourceManager &SM = PDB.getSourceManager(); - SourceLocation ContainerRBeg = SM.getExpansionLoc(ContainerR.getBegin()); - SourceLocation ContainerREnd = SM.getExpansionLoc(ContainerR.getEnd()); - SourceLocation ContaineeRBeg = SM.getExpansionLoc(ContaineeR.getBegin()); - SourceLocation ContaineeREnd = SM.getExpansionLoc(ContaineeR.getEnd()); - - unsigned ContainerBegLine = SM.getExpansionLineNumber(ContainerRBeg); - unsigned ContainerEndLine = SM.getExpansionLineNumber(ContainerREnd); - unsigned ContaineeBegLine = SM.getExpansionLineNumber(ContaineeRBeg); - unsigned ContaineeEndLine = SM.getExpansionLineNumber(ContaineeREnd); - - assert(ContainerBegLine <= ContainerEndLine); - assert(ContaineeBegLine <= ContaineeEndLine); - - return (ContainerBegLine <= ContaineeBegLine && - ContainerEndLine >= ContaineeEndLine && - (ContainerBegLine != ContaineeBegLine || - SM.getExpansionColumnNumber(ContainerRBeg) <= - SM.getExpansionColumnNumber(ContaineeRBeg)) && - (ContainerEndLine != ContaineeEndLine || - SM.getExpansionColumnNumber(ContainerREnd) >= - SM.getExpansionColumnNumber(ContaineeREnd))); -} - -void EdgeBuilder::rawAddEdge(PathDiagnosticLocation NewLoc) { - if (!PrevLoc.isValid()) { - PrevLoc = NewLoc; - return; + PD.getActivePath().push_front( + std::make_shared<PathDiagnosticControlFlowPiece>(Start, End, os.str())); + break; } - const PathDiagnosticLocation &NewLocClean = cleanUpLocation(NewLoc, PDB.LC); - const PathDiagnosticLocation &PrevLocClean = cleanUpLocation(PrevLoc, PDB.LC); + // Determine control-flow for short-circuited '&&' and '||'. + case Stmt::BinaryOperatorClass: { + if (!PDB.supportsLogicalOpControlFlow()) + break; - if (PrevLocClean.asLocation().isInvalid()) { - PrevLoc = NewLoc; - return; + std::shared_ptr<PathDiagnosticControlFlowPiece> Diag = + generateDiagForBinaryOP(N, T, Src, Dst, SM, PDB, LC); + PD.getActivePath().push_front(Diag); + break; } - if (NewLocClean.asLocation() == PrevLocClean.asLocation()) - return; - - // FIXME: Ignore intra-macro edges for now. - if (NewLocClean.asLocation().getExpansionLoc() == - PrevLocClean.asLocation().getExpansionLoc()) - return; - - PD.getActivePath().push_front( - std::make_shared<PathDiagnosticControlFlowPiece>(NewLocClean, - PrevLocClean)); - PrevLoc = NewLoc; -} - -void EdgeBuilder::addEdge(PathDiagnosticLocation NewLoc, bool alwaysAdd, - bool IsPostJump) { - - if (!alwaysAdd && NewLoc.asLocation().isMacroID()) - return; + case Stmt::DoStmtClass: + if (*(Src->succ_begin()) == Dst) { + std::string sbuf; + llvm::raw_string_ostream os(sbuf); - const PathDiagnosticLocation &CLoc = getContextLocation(NewLoc); + os << "Loop condition is true. "; + PathDiagnosticLocation End = PDB.ExecutionContinues(os, N); - while (!CLocs.empty()) { - ContextLocation &TopContextLoc = CLocs.back(); + if (const Stmt *S = End.asStmt()) + End = PDB.getEnclosingStmtLocation(S); - // Is the top location context the same as the one for the new location? - if (TopContextLoc == CLoc) { - if (alwaysAdd) { - if (IsConsumedExpr(TopContextLoc)) - TopContextLoc.markDead(); + PD.getActivePath().push_front( + std::make_shared<PathDiagnosticControlFlowPiece>(Start, End, + os.str())); + } else { + PathDiagnosticLocation End = PDB.ExecutionContinues(N); - rawAddEdge(NewLoc); - } + if (const Stmt *S = End.asStmt()) + End = PDB.getEnclosingStmtLocation(S); - if (IsPostJump) - TopContextLoc.markDead(); - return; + PD.getActivePath().push_front( + std::make_shared<PathDiagnosticControlFlowPiece>( + Start, End, "Loop condition is false. Exiting loop")); } + break; - if (containsLocation(TopContextLoc, CLoc)) { - if (alwaysAdd) { - rawAddEdge(NewLoc); - - if (IsConsumedExpr(CLoc)) { - CLocs.push_back(ContextLocation(CLoc, /*IsDead=*/true)); - return; - } - } + case Stmt::WhileStmtClass: + case Stmt::ForStmtClass: + if (*(Src->succ_begin() + 1) == Dst) { + std::string sbuf; + llvm::raw_string_ostream os(sbuf); + + os << "Loop condition is false. "; + PathDiagnosticLocation End = PDB.ExecutionContinues(os, N); + if (const Stmt *S = End.asStmt()) + End = PDB.getEnclosingStmtLocation(S); + + PD.getActivePath().push_front( + std::make_shared<PathDiagnosticControlFlowPiece>(Start, End, + os.str())); + } else { + PathDiagnosticLocation End = PDB.ExecutionContinues(N); + if (const Stmt *S = End.asStmt()) + End = PDB.getEnclosingStmtLocation(S); - CLocs.push_back(ContextLocation(CLoc, /*IsDead=*/IsPostJump)); - return; + PD.getActivePath().push_front( + std::make_shared<PathDiagnosticControlFlowPiece>( + Start, End, "Loop condition is true. Entering loop body")); } - // Context does not contain the location. Flush it. - popLocation(); - } - - // If we reach here, there is no enclosing context. Just add the edge. - rawAddEdge(NewLoc); -} + break; -bool EdgeBuilder::IsConsumedExpr(const PathDiagnosticLocation &L) { - if (const Expr *X = dyn_cast_or_null<Expr>(L.asStmt())) - return PDB.getParentMap().isConsumedExpr(X) && !IsControlFlowExpr(X); + case Stmt::IfStmtClass: { + PathDiagnosticLocation End = PDB.ExecutionContinues(N); - return false; -} + if (const Stmt *S = End.asStmt()) + End = PDB.getEnclosingStmtLocation(S); -void EdgeBuilder::addExtendedContext(const Stmt *S) { - if (!S) - return; - - const Stmt *Parent = PDB.getParent(S); - while (Parent) { - if (isa<CompoundStmt>(Parent)) - Parent = PDB.getParent(Parent); + if (*(Src->succ_begin() + 1) == Dst) + PD.getActivePath().push_front( + std::make_shared<PathDiagnosticControlFlowPiece>( + Start, End, "Taking false branch")); else - break; - } + PD.getActivePath().push_front( + std::make_shared<PathDiagnosticControlFlowPiece>( + Start, End, "Taking true branch")); - if (Parent) { - switch (Parent->getStmtClass()) { - case Stmt::DoStmtClass: - case Stmt::ObjCAtSynchronizedStmtClass: - addContext(Parent); - default: - break; - } + break; } - - addContext(S); -} - -void EdgeBuilder::addContext(const Stmt *S) { - if (!S) - return; - - PathDiagnosticLocation L(S, PDB.getSourceManager(), PDB.LC); - addContext(L); -} - -void EdgeBuilder::addContext(const PathDiagnosticLocation &L) { - while (!CLocs.empty()) { - const PathDiagnosticLocation &TopContextLoc = CLocs.back(); - - // Is the top location context the same as the one for the new location? - if (TopContextLoc == L) - return; - - if (containsLocation(TopContextLoc, L)) { - CLocs.push_back(L); - return; - } - - // Context does not contain the location. Flush it. - popLocation(); } - - CLocs.push_back(L); } // Cone-of-influence: support the reverse propagation of "interesting" symbols @@ -1257,7 +826,7 @@ void EdgeBuilder::addContext(const PathDiagnosticLocation &L) { // because the constraint solver sometimes simplifies certain symbolic values // into constants when appropriate, and this complicates reasoning about // interesting values. -typedef llvm::DenseSet<const Expr *> InterestingExprs; +using InterestingExprs = llvm::DenseSet<const Expr *>; static void reversePropagateIntererstingSymbols(BugReport &R, InterestingExprs &IE, @@ -1276,7 +845,7 @@ static void reversePropagateIntererstingSymbols(BugReport &R, case Stmt::BinaryOperatorClass: case Stmt::UnaryOperatorClass: { for (const Stmt *SubStmt : Ex->children()) { - if (const Expr *child = dyn_cast_or_null<Expr>(SubStmt)) { + if (const auto *child = dyn_cast_or_null<Expr>(SubStmt)) { IE.insert(child); SVal ChildV = State->getSVal(child, LCtx); R.markInteresting(ChildV); @@ -1296,10 +865,10 @@ static void reversePropagateInterestingSymbols(BugReport &R, const LocationContext *CallerCtx) { // FIXME: Handle non-CallExpr-based CallEvents. - const StackFrameContext *Callee = CalleeCtx->getCurrentStackFrame(); + const StackFrameContext *Callee = CalleeCtx->getStackFrame(); const Stmt *CallSite = Callee->getCallSite(); - if (const CallExpr *CE = dyn_cast_or_null<CallExpr>(CallSite)) { - if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(CalleeCtx->getDecl())) { + if (const auto *CE = dyn_cast_or_null<CallExpr>(CallSite)) { + if (const auto *FD = dyn_cast<FunctionDecl>(CalleeCtx->getDecl())) { FunctionDecl::param_const_iterator PI = FD->param_begin(), PE = FD->param_end(); CallExpr::const_arg_iterator AI = CE->arg_begin(), AE = CE->arg_end(); @@ -1339,16 +908,6 @@ static bool isJumpToFalseBranch(const BlockEdge *BE) { return (*(Src->succ_begin()+1) == BE->getDst()); } -/// Return true if the terminator is a loop and the destination is the -/// false branch. -static bool isLoopJumpPastBody(const Stmt *Term, const BlockEdge *BE) { - if (!isLoop(Term)) - return false; - - // Did we take the false branch? - return isJumpToFalseBranch(BE); -} - static bool isContainedByStmt(ParentMap &PM, const Stmt *S, const Stmt *SubS) { while (SubS) { if (SubS == S) @@ -1376,7 +935,7 @@ static bool isInLoopBody(ParentMap &PM, const Stmt *S, const Stmt *Term) { const Stmt *LoopBody = nullptr; switch (Term->getStmtClass()) { case Stmt::CXXForRangeStmtClass: { - const CXXForRangeStmt *FR = cast<CXXForRangeStmt>(Term); + const auto *FR = cast<CXXForRangeStmt>(Term); if (isContainedByStmt(PM, FR->getInc(), S)) return true; if (isContainedByStmt(PM, FR->getLoopVarStmt(), S)) @@ -1385,14 +944,14 @@ static bool isInLoopBody(ParentMap &PM, const Stmt *S, const Stmt *Term) { break; } case Stmt::ForStmtClass: { - const ForStmt *FS = cast<ForStmt>(Term); + const auto *FS = cast<ForStmt>(Term); if (isContainedByStmt(PM, FS->getInc(), S)) return true; LoopBody = FS->getBody(); break; } case Stmt::ObjCForCollectionStmtClass: { - const ObjCForCollectionStmt *FC = cast<ObjCForCollectionStmt>(Term); + const auto *FC = cast<ObjCForCollectionStmt>(Term); LoopBody = FC->getBody(); break; } @@ -1405,210 +964,7 @@ static bool isInLoopBody(ParentMap &PM, const Stmt *S, const Stmt *Term) { return isContainedByStmt(PM, LoopBody, S); } -//===----------------------------------------------------------------------===// -// Top-level logic for generating extensive path diagnostics. -//===----------------------------------------------------------------------===// - -static bool GenerateExtensivePathDiagnostic( - PathDiagnostic &PD, PathDiagnosticBuilder &PDB, const ExplodedNode *N, - LocationContextMap &LCM, - ArrayRef<std::unique_ptr<BugReporterVisitor>> visitors) { - EdgeBuilder EB(PD, PDB); - const SourceManager& SM = PDB.getSourceManager(); - StackDiagVector CallStack; - InterestingExprs IE; - - const ExplodedNode *NextNode = N->pred_empty() ? nullptr : *(N->pred_begin()); - while (NextNode) { - N = NextNode; - NextNode = N->getFirstPred(); - ProgramPoint P = N->getLocation(); - - do { - if (Optional<PostStmt> PS = P.getAs<PostStmt>()) { - if (const Expr *Ex = PS->getStmtAs<Expr>()) - reversePropagateIntererstingSymbols(*PDB.getBugReport(), IE, - N->getState().get(), Ex, - N->getLocationContext()); - } - - if (Optional<CallExitEnd> CE = P.getAs<CallExitEnd>()) { - const Stmt *S = CE->getCalleeContext()->getCallSite(); - if (const Expr *Ex = dyn_cast_or_null<Expr>(S)) { - reversePropagateIntererstingSymbols(*PDB.getBugReport(), IE, - N->getState().get(), Ex, - N->getLocationContext()); - } - - auto C = PathDiagnosticCallPiece::construct(N, *CE, SM); - LCM[&C->path] = CE->getCalleeContext(); - - EB.addEdge(C->callReturn, /*AlwaysAdd=*/true, /*IsPostJump=*/true); - EB.flushLocations(); - - auto *P = C.get(); - PD.getActivePath().push_front(std::move(C)); - PD.pushActivePath(&P->path); - CallStack.push_back(StackDiagPair(P, N)); - break; - } - - // Pop the call hierarchy if we are done walking the contents - // of a function call. - if (Optional<CallEnter> CE = P.getAs<CallEnter>()) { - // Add an edge to the start of the function. - const Decl *D = CE->getCalleeContext()->getDecl(); - PathDiagnosticLocation pos = - PathDiagnosticLocation::createBegin(D, SM); - EB.addEdge(pos); - - // Flush all locations, and pop the active path. - bool VisitedEntireCall = PD.isWithinCall(); - EB.flushLocations(); - PD.popActivePath(); - PDB.LC = N->getLocationContext(); - - // Either we just added a bunch of stuff to the top-level path, or - // we have a previous CallExitEnd. If the former, it means that the - // path terminated within a function call. We must then take the - // current contents of the active path and place it within - // a new PathDiagnosticCallPiece. - PathDiagnosticCallPiece *C; - if (VisitedEntireCall) { - C = cast<PathDiagnosticCallPiece>(PD.getActivePath().front().get()); - } else { - const Decl *Caller = CE->getLocationContext()->getDecl(); - C = PathDiagnosticCallPiece::construct(PD.getActivePath(), Caller); - LCM[&C->path] = CE->getCalleeContext(); - } - - C->setCallee(*CE, SM); - EB.addContext(C->getLocation()); - - if (!CallStack.empty()) { - assert(CallStack.back().first == C); - CallStack.pop_back(); - } - break; - } - - // Note that is important that we update the LocationContext - // after looking at CallExits. CallExit basically adds an - // edge in the *caller*, so we don't want to update the LocationContext - // too soon. - PDB.LC = N->getLocationContext(); - - // Block edges. - if (Optional<BlockEdge> BE = P.getAs<BlockEdge>()) { - // Does this represent entering a call? If so, look at propagating - // interesting symbols across call boundaries. - if (NextNode) { - const LocationContext *CallerCtx = NextNode->getLocationContext(); - const LocationContext *CalleeCtx = PDB.LC; - if (CallerCtx != CalleeCtx) { - reversePropagateInterestingSymbols(*PDB.getBugReport(), IE, - N->getState().get(), - CalleeCtx, CallerCtx); - } - } - - // Are we jumping to the head of a loop? Add a special diagnostic. - if (const Stmt *Loop = BE->getSrc()->getLoopTarget()) { - PathDiagnosticLocation L(Loop, SM, PDB.LC); - const CompoundStmt *CS = nullptr; - - if (const ForStmt *FS = dyn_cast<ForStmt>(Loop)) - CS = dyn_cast<CompoundStmt>(FS->getBody()); - else if (const WhileStmt *WS = dyn_cast<WhileStmt>(Loop)) - CS = dyn_cast<CompoundStmt>(WS->getBody()); - - auto p = std::make_shared<PathDiagnosticEventPiece>( - L, "Looping back to the head of the loop"); - p->setPrunable(true); - - EB.addEdge(p->getLocation(), true); - PD.getActivePath().push_front(std::move(p)); - - if (CS) { - PathDiagnosticLocation BL = - PathDiagnosticLocation::createEndBrace(CS, SM); - EB.addEdge(BL); - } - } - - const CFGBlock *BSrc = BE->getSrc(); - ParentMap &PM = PDB.getParentMap(); - - if (const Stmt *Term = BSrc->getTerminator()) { - // Are we jumping past the loop body without ever executing the - // loop (because the condition was false)? - if (isLoopJumpPastBody(Term, &*BE) && - !isInLoopBody(PM, - getStmtBeforeCond(PM, - BSrc->getTerminatorCondition(), - N), - Term)) { - PathDiagnosticLocation L(Term, SM, PDB.LC); - auto PE = std::make_shared<PathDiagnosticEventPiece>( - L, "Loop body executed 0 times"); - PE->setPrunable(true); - - EB.addEdge(PE->getLocation(), true); - PD.getActivePath().push_front(std::move(PE)); - } - - // In any case, add the terminator as the current statement - // context for control edges. - EB.addContext(Term); - } - - break; - } - - if (Optional<BlockEntrance> BE = P.getAs<BlockEntrance>()) { - Optional<CFGElement> First = BE->getFirstElement(); - if (Optional<CFGStmt> S = First ? First->getAs<CFGStmt>() : None) { - const Stmt *stmt = S->getStmt(); - if (IsControlFlowExpr(stmt)) { - // Add the proper context for '&&', '||', and '?'. - EB.addContext(stmt); - } - else - EB.addExtendedContext(PDB.getEnclosingStmtLocation(stmt).asStmt()); - } - - break; - } - - - } while (0); - - if (!NextNode) - continue; - - // Add pieces from custom visitors. - BugReport *R = PDB.getBugReport(); - llvm::FoldingSet<PathDiagnosticPiece> DeduplicationSet; - for (auto &V : visitors) { - if (auto p = V->VisitNode(N, NextNode, PDB, *R)) { - if (DeduplicationSet.GetOrInsertNode(p.get()) != p.get()) - continue; - - const PathDiagnosticLocation &Loc = p->getLocation(); - EB.addEdge(Loc, true); - updateStackPiecesWithMessage(*p, CallStack); - PD.getActivePath().push_front(std::move(p)); - - if (const Stmt *S = Loc.asStmt()) - EB.addExtendedContext(PDB.getEnclosingStmtLocation(S).asStmt()); - } - } - } - - return PDB.getBugReport()->isValid(); -} - -/// \brief Adds a sanitized control-flow diagnostic edge to a path. +/// Adds a sanitized control-flow diagnostic edge to a path. static void addEdgeToPath(PathPieces &path, PathDiagnosticLocation &PrevLoc, PathDiagnosticLocation NewLoc, @@ -1639,8 +995,7 @@ static void addEdgeToPath(PathPieces &path, /// which returns the element for ObjCForCollectionStmts. static const Stmt *getTerminatorCondition(const CFGBlock *B) { const Stmt *S = B->getTerminatorCondition(); - if (const ObjCForCollectionStmt *FS = - dyn_cast_or_null<ObjCForCollectionStmt>(S)) + if (const auto *FS = dyn_cast_or_null<ObjCForCollectionStmt>(S)) return FS->getElement(); return S; } @@ -1652,269 +1007,256 @@ static const char StrLoopRangeEmpty[] = static const char StrLoopCollectionEmpty[] = "Loop body skipped when collection is empty"; -static bool GenerateAlternateExtensivePathDiagnostic( - PathDiagnostic &PD, PathDiagnosticBuilder &PDB, const ExplodedNode *N, - LocationContextMap &LCM, - ArrayRef<std::unique_ptr<BugReporterVisitor>> visitors) { - - BugReport *report = PDB.getBugReport(); +static std::unique_ptr<FilesToLineNumsMap> +findExecutedLines(SourceManager &SM, const ExplodedNode *N); + +/// Generate diagnostics for the node \p N, +/// and write it into \p PD. +/// \p AddPathEdges Whether diagnostic consumer can generate path arrows +/// showing both row and column. +static void generatePathDiagnosticsForNode(const ExplodedNode *N, + PathDiagnostic &PD, + PathDiagnosticLocation &PrevLoc, + PathDiagnosticBuilder &PDB, + LocationContextMap &LCM, + StackDiagVector &CallStack, + InterestingExprs &IE, + bool AddPathEdges) { + ProgramPoint P = N->getLocation(); const SourceManager& SM = PDB.getSourceManager(); - StackDiagVector CallStack; - InterestingExprs IE; - PathDiagnosticLocation PrevLoc = PD.getLocation(); + // Have we encountered an entrance to a call? It may be + // the case that we have not encountered a matching + // call exit before this point. This means that the path + // terminated within the call itself. + if (auto CE = P.getAs<CallEnter>()) { + + if (AddPathEdges) { + // Add an edge to the start of the function. + const StackFrameContext *CalleeLC = CE->getCalleeContext(); + const Decl *D = CalleeLC->getDecl(); + // Add the edge only when the callee has body. We jump to the beginning + // of the *declaration*, however we expect it to be followed by the + // body. This isn't the case for autosynthesized property accessors in + // Objective-C. No need for a similar extra check for CallExit points + // because the exit edge comes from a statement (i.e. return), + // not from declaration. + if (D->hasBody()) + addEdgeToPath(PD.getActivePath(), PrevLoc, + PathDiagnosticLocation::createBegin(D, SM), CalleeLC); + } - const ExplodedNode *NextNode = N->getFirstPred(); - while (NextNode) { - N = NextNode; - NextNode = N->getFirstPred(); - ProgramPoint P = N->getLocation(); - - do { - // Have we encountered an entrance to a call? It may be - // the case that we have not encountered a matching - // call exit before this point. This means that the path - // terminated within the call itself. - if (Optional<CallEnter> CE = P.getAs<CallEnter>()) { - // Add an edge to the start of the function. - const StackFrameContext *CalleeLC = CE->getCalleeContext(); - const Decl *D = CalleeLC->getDecl(); - // Add the edge only when the callee has body. We jump to the beginning - // of the *declaration*, however we expect it to be followed by the - // body. This isn't the case for autosynthesized property accessors in - // Objective-C. No need for a similar extra check for CallExit points - // because the exit edge comes from a statement (i.e. return), - // not from declaration. - if (D->hasBody()) - addEdgeToPath(PD.getActivePath(), PrevLoc, - PathDiagnosticLocation::createBegin(D, SM), CalleeLC); + // Did we visit an entire call? + bool VisitedEntireCall = PD.isWithinCall(); + PD.popActivePath(); - // Did we visit an entire call? - bool VisitedEntireCall = PD.isWithinCall(); - PD.popActivePath(); + PathDiagnosticCallPiece *C; + if (VisitedEntireCall) { + C = cast<PathDiagnosticCallPiece>(PD.getActivePath().front().get()); + } else { + const Decl *Caller = CE->getLocationContext()->getDecl(); + C = PathDiagnosticCallPiece::construct(PD.getActivePath(), Caller); + + if (AddPathEdges) { + // Since we just transferred the path over to the call piece, + // reset the mapping from active to location context. + assert(PD.getActivePath().size() == 1 && + PD.getActivePath().front().get() == C); + LCM[&PD.getActivePath()] = nullptr; + } - PathDiagnosticCallPiece *C; - if (VisitedEntireCall) { - PathDiagnosticPiece *P = PD.getActivePath().front().get(); - C = cast<PathDiagnosticCallPiece>(P); - } else { - const Decl *Caller = CE->getLocationContext()->getDecl(); - C = PathDiagnosticCallPiece::construct(PD.getActivePath(), Caller); - - // Since we just transferred the path over to the call piece, - // reset the mapping from active to location context. - assert(PD.getActivePath().size() == 1 && - PD.getActivePath().front().get() == C); - LCM[&PD.getActivePath()] = nullptr; - - // Record the location context mapping for the path within - // the call. - assert(LCM[&C->path] == nullptr || - LCM[&C->path] == CE->getCalleeContext()); - LCM[&C->path] = CE->getCalleeContext(); - - // If this is the first item in the active path, record - // the new mapping from active path to location context. - const LocationContext *&NewLC = LCM[&PD.getActivePath()]; - if (!NewLC) - NewLC = N->getLocationContext(); - - PDB.LC = NewLC; - } - C->setCallee(*CE, SM); + // Record the location context mapping for the path within + // the call. + assert(LCM[&C->path] == nullptr || + LCM[&C->path] == CE->getCalleeContext()); + LCM[&C->path] = CE->getCalleeContext(); - // Update the previous location in the active path. - PrevLoc = C->getLocation(); + // If this is the first item in the active path, record + // the new mapping from active path to location context. + const LocationContext *&NewLC = LCM[&PD.getActivePath()]; + if (!NewLC) + NewLC = N->getLocationContext(); - if (!CallStack.empty()) { - assert(CallStack.back().first == C); - CallStack.pop_back(); - } - break; - } + PDB.LC = NewLC; + } + C->setCallee(*CE, SM); - // Query the location context here and the previous location - // as processing CallEnter may change the active path. - PDB.LC = N->getLocationContext(); + // Update the previous location in the active path. + PrevLoc = C->getLocation(); - // Record the mapping from the active path to the location - // context. - assert(!LCM[&PD.getActivePath()] || - LCM[&PD.getActivePath()] == PDB.LC); - LCM[&PD.getActivePath()] = PDB.LC; - - // Have we encountered an exit from a function call? - if (Optional<CallExitEnd> CE = P.getAs<CallExitEnd>()) { - const Stmt *S = CE->getCalleeContext()->getCallSite(); - // Propagate the interesting symbols accordingly. - if (const Expr *Ex = dyn_cast_or_null<Expr>(S)) { - reversePropagateIntererstingSymbols(*PDB.getBugReport(), IE, - N->getState().get(), Ex, - N->getLocationContext()); - } + if (!CallStack.empty()) { + assert(CallStack.back().first == C); + CallStack.pop_back(); + } + return; + } - // We are descending into a call (backwards). Construct - // a new call piece to contain the path pieces for that call. - auto C = PathDiagnosticCallPiece::construct(N, *CE, SM); - // Record the location context for this call piece. - LCM[&C->path] = CE->getCalleeContext(); + if (AddPathEdges) { + // Query the location context here and the previous location + // as processing CallEnter may change the active path. + PDB.LC = N->getLocationContext(); - // Add the edge to the return site. - addEdgeToPath(PD.getActivePath(), PrevLoc, C->callReturn, PDB.LC); - auto *P = C.get(); - PD.getActivePath().push_front(std::move(C)); - PrevLoc.invalidate(); + // Record the mapping from the active path to the location + // context. + assert(!LCM[&PD.getActivePath()] || LCM[&PD.getActivePath()] == PDB.LC); + LCM[&PD.getActivePath()] = PDB.LC; + } - // Make the contents of the call the active path for now. - PD.pushActivePath(&P->path); - CallStack.push_back(StackDiagPair(P, N)); - break; - } + // Have we encountered an exit from a function call? + if (Optional<CallExitEnd> CE = P.getAs<CallExitEnd>()) { - if (Optional<PostStmt> PS = P.getAs<PostStmt>()) { - // For expressions, make sure we propagate the - // interesting symbols correctly. - if (const Expr *Ex = PS->getStmtAs<Expr>()) - reversePropagateIntererstingSymbols(*PDB.getBugReport(), IE, - N->getState().get(), Ex, - N->getLocationContext()); - - // Add an edge. If this is an ObjCForCollectionStmt do - // not add an edge here as it appears in the CFG both - // as a terminator and as a terminator condition. - if (!isa<ObjCForCollectionStmt>(PS->getStmt())) { - PathDiagnosticLocation L = - PathDiagnosticLocation(PS->getStmt(), SM, PDB.LC); - addEdgeToPath(PD.getActivePath(), PrevLoc, L, PDB.LC); - } - break; + // We are descending into a call (backwards). Construct + // a new call piece to contain the path pieces for that call. + auto C = PathDiagnosticCallPiece::construct(N, *CE, SM); + // Record the mapping from call piece to LocationContext. + LCM[&C->path] = CE->getCalleeContext(); + + if (AddPathEdges) { + const Stmt *S = CE->getCalleeContext()->getCallSite(); + // Propagate the interesting symbols accordingly. + if (const auto *Ex = dyn_cast_or_null<Expr>(S)) { + reversePropagateIntererstingSymbols(*PDB.getBugReport(), IE, + N->getState().get(), Ex, + N->getLocationContext()); } + // Add the edge to the return site. + addEdgeToPath(PD.getActivePath(), PrevLoc, C->callReturn, PDB.LC); + PrevLoc.invalidate(); + } - // Block edges. - if (Optional<BlockEdge> BE = P.getAs<BlockEdge>()) { - // Does this represent entering a call? If so, look at propagating - // interesting symbols across call boundaries. - if (NextNode) { - const LocationContext *CallerCtx = NextNode->getLocationContext(); - const LocationContext *CalleeCtx = PDB.LC; - if (CallerCtx != CalleeCtx) { - reversePropagateInterestingSymbols(*PDB.getBugReport(), IE, - N->getState().get(), - CalleeCtx, CallerCtx); - } - } + auto *P = C.get(); + PD.getActivePath().push_front(std::move(C)); - // Are we jumping to the head of a loop? Add a special diagnostic. - if (const Stmt *Loop = BE->getSrc()->getLoopTarget()) { - PathDiagnosticLocation L(Loop, SM, PDB.LC); - const Stmt *Body = nullptr; - - if (const ForStmt *FS = dyn_cast<ForStmt>(Loop)) - Body = FS->getBody(); - else if (const WhileStmt *WS = dyn_cast<WhileStmt>(Loop)) - Body = WS->getBody(); - else if (const ObjCForCollectionStmt *OFS = - dyn_cast<ObjCForCollectionStmt>(Loop)) { - Body = OFS->getBody(); - } else if (const CXXForRangeStmt *FRS = - dyn_cast<CXXForRangeStmt>(Loop)) { - Body = FRS->getBody(); - } - // do-while statements are explicitly excluded here + // Make the contents of the call the active path for now. + PD.pushActivePath(&P->path); + CallStack.push_back(StackDiagPair(P, N)); + return; + } - auto p = std::make_shared<PathDiagnosticEventPiece>( - L, "Looping back to the head " - "of the loop"); - p->setPrunable(true); + if (auto PS = P.getAs<PostStmt>()) { + if (!AddPathEdges) + return; - addEdgeToPath(PD.getActivePath(), PrevLoc, p->getLocation(), PDB.LC); - PD.getActivePath().push_front(std::move(p)); + // For expressions, make sure we propagate the + // interesting symbols correctly. + if (const Expr *Ex = PS->getStmtAs<Expr>()) + reversePropagateIntererstingSymbols(*PDB.getBugReport(), IE, + N->getState().get(), Ex, + N->getLocationContext()); + + // Add an edge. If this is an ObjCForCollectionStmt do + // not add an edge here as it appears in the CFG both + // as a terminator and as a terminator condition. + if (!isa<ObjCForCollectionStmt>(PS->getStmt())) { + PathDiagnosticLocation L = + PathDiagnosticLocation(PS->getStmt(), SM, PDB.LC); + addEdgeToPath(PD.getActivePath(), PrevLoc, L, PDB.LC); + } - if (const CompoundStmt *CS = dyn_cast_or_null<CompoundStmt>(Body)) { - addEdgeToPath(PD.getActivePath(), PrevLoc, - PathDiagnosticLocation::createEndBrace(CS, SM), - PDB.LC); - } - } + } else if (auto BE = P.getAs<BlockEdge>()) { - const CFGBlock *BSrc = BE->getSrc(); - ParentMap &PM = PDB.getParentMap(); - - if (const Stmt *Term = BSrc->getTerminator()) { - // Are we jumping past the loop body without ever executing the - // loop (because the condition was false)? - if (isLoop(Term)) { - const Stmt *TermCond = getTerminatorCondition(BSrc); - bool IsInLoopBody = - isInLoopBody(PM, getStmtBeforeCond(PM, TermCond, N), Term); - - const char *str = nullptr; - - if (isJumpToFalseBranch(&*BE)) { - if (!IsInLoopBody) { - if (isa<ObjCForCollectionStmt>(Term)) { - str = StrLoopCollectionEmpty; - } else if (isa<CXXForRangeStmt>(Term)) { - str = StrLoopRangeEmpty; - } else { - str = StrLoopBodyZero; - } - } - } else { - str = StrEnteringLoop; - } + if (!AddPathEdges) { + generateMinimalDiagForBlockEdge(N, *BE, SM, PDB, PD); + return; + } - if (str) { - PathDiagnosticLocation L(TermCond ? TermCond : Term, SM, PDB.LC); - auto PE = std::make_shared<PathDiagnosticEventPiece>(L, str); - PE->setPrunable(true); - addEdgeToPath(PD.getActivePath(), PrevLoc, - PE->getLocation(), PDB.LC); - PD.getActivePath().push_front(std::move(PE)); - } - } else if (isa<BreakStmt>(Term) || isa<ContinueStmt>(Term) || - isa<GotoStmt>(Term)) { - PathDiagnosticLocation L(Term, SM, PDB.LC); - addEdgeToPath(PD.getActivePath(), PrevLoc, L, PDB.LC); - } - } - break; + // Does this represent entering a call? If so, look at propagating + // interesting symbols across call boundaries. + if (const ExplodedNode *NextNode = N->getFirstPred()) { + const LocationContext *CallerCtx = NextNode->getLocationContext(); + const LocationContext *CalleeCtx = PDB.LC; + if (CallerCtx != CalleeCtx && AddPathEdges) { + reversePropagateInterestingSymbols(*PDB.getBugReport(), IE, + N->getState().get(), + CalleeCtx, CallerCtx); } - } while (0); + } - if (!NextNode) - continue; + // Are we jumping to the head of a loop? Add a special diagnostic. + if (const Stmt *Loop = BE->getSrc()->getLoopTarget()) { + PathDiagnosticLocation L(Loop, SM, PDB.LC); + const Stmt *Body = nullptr; + + if (const auto *FS = dyn_cast<ForStmt>(Loop)) + Body = FS->getBody(); + else if (const auto *WS = dyn_cast<WhileStmt>(Loop)) + Body = WS->getBody(); + else if (const auto *OFS = dyn_cast<ObjCForCollectionStmt>(Loop)) { + Body = OFS->getBody(); + } else if (const auto *FRS = dyn_cast<CXXForRangeStmt>(Loop)) { + Body = FRS->getBody(); + } + // do-while statements are explicitly excluded here - // Add pieces from custom visitors. - llvm::FoldingSet<PathDiagnosticPiece> DeduplicationSet; - for (auto &V : visitors) { - if (auto p = V->VisitNode(N, NextNode, PDB, *report)) { - if (DeduplicationSet.GetOrInsertNode(p.get()) != p.get()) - continue; + auto p = std::make_shared<PathDiagnosticEventPiece>( + L, "Looping back to the head " + "of the loop"); + p->setPrunable(true); - addEdgeToPath(PD.getActivePath(), PrevLoc, p->getLocation(), PDB.LC); - updateStackPiecesWithMessage(*p, CallStack); - PD.getActivePath().push_front(std::move(p)); + addEdgeToPath(PD.getActivePath(), PrevLoc, p->getLocation(), PDB.LC); + PD.getActivePath().push_front(std::move(p)); + + if (const auto *CS = dyn_cast_or_null<CompoundStmt>(Body)) { + addEdgeToPath(PD.getActivePath(), PrevLoc, + PathDiagnosticLocation::createEndBrace(CS, SM), + PDB.LC); } } - } - // Add an edge to the start of the function. - // We'll prune it out later, but it helps make diagnostics more uniform. - const StackFrameContext *CalleeLC = PDB.LC->getCurrentStackFrame(); - const Decl *D = CalleeLC->getDecl(); - addEdgeToPath(PD.getActivePath(), PrevLoc, - PathDiagnosticLocation::createBegin(D, SM), - CalleeLC); + const CFGBlock *BSrc = BE->getSrc(); + ParentMap &PM = PDB.getParentMap(); + + if (const Stmt *Term = BSrc->getTerminator()) { + // Are we jumping past the loop body without ever executing the + // loop (because the condition was false)? + if (isLoop(Term)) { + const Stmt *TermCond = getTerminatorCondition(BSrc); + bool IsInLoopBody = + isInLoopBody(PM, getStmtBeforeCond(PM, TermCond, N), Term); + + const char *str = nullptr; + + if (isJumpToFalseBranch(&*BE)) { + if (!IsInLoopBody) { + if (isa<ObjCForCollectionStmt>(Term)) { + str = StrLoopCollectionEmpty; + } else if (isa<CXXForRangeStmt>(Term)) { + str = StrLoopRangeEmpty; + } else { + str = StrLoopBodyZero; + } + } + } else { + str = StrEnteringLoop; + } - return report->isValid(); + if (str) { + PathDiagnosticLocation L(TermCond ? TermCond : Term, SM, PDB.LC); + auto PE = std::make_shared<PathDiagnosticEventPiece>(L, str); + PE->setPrunable(true); + addEdgeToPath(PD.getActivePath(), PrevLoc, + PE->getLocation(), PDB.LC); + PD.getActivePath().push_front(std::move(PE)); + } + } else if (isa<BreakStmt>(Term) || isa<ContinueStmt>(Term) || + isa<GotoStmt>(Term)) { + PathDiagnosticLocation L(Term, SM, PDB.LC); + addEdgeToPath(PD.getActivePath(), PrevLoc, L, PDB.LC); + } + } + } } -static const Stmt *getLocStmt(PathDiagnosticLocation L) { - if (!L.isValid()) - return nullptr; - return L.asStmt(); +static std::unique_ptr<PathDiagnostic> +generateEmptyDiagnosticForReport(BugReport *R, SourceManager &SM) { + 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->getUniqueingLocation(), R->getUniqueingDecl(), + findExecutedLines(SM, R->getErrorNode())); } static const Stmt *getStmtParent(const Stmt *S, const ParentMap &PM) { @@ -1941,7 +1283,7 @@ static const Stmt *getStmtParent(const Stmt *S, const ParentMap &PM) { static bool isConditionForTerminator(const Stmt *S, const Stmt *Cond) { switch (S->getStmtClass()) { case Stmt::BinaryOperatorClass: { - const BinaryOperator *BO = cast<BinaryOperator>(S); + const auto *BO = cast<BinaryOperator>(S); if (!BO->isLogicalOp()) return false; return BO->getLHS() == Cond || BO->getRHS() == Cond; @@ -1963,7 +1305,7 @@ static bool isConditionForTerminator(const Stmt *S, const Stmt *Cond) { case Stmt::BinaryConditionalOperatorClass: return cast<BinaryConditionalOperator>(S)->getCond() == Cond; case Stmt::ConditionalOperatorClass: { - const ConditionalOperator *CO = cast<ConditionalOperator>(S); + const auto *CO = cast<ConditionalOperator>(S); return CO->getCond() == Cond || CO->getLHS() == Cond || CO->getRHS() == Cond; @@ -1971,7 +1313,7 @@ static bool isConditionForTerminator(const Stmt *S, const Stmt *Cond) { case Stmt::ObjCForCollectionStmtClass: return cast<ObjCForCollectionStmt>(S)->getElement() == Cond; case Stmt::CXXForRangeStmtClass: { - const CXXForRangeStmt *FRS = cast<CXXForRangeStmt>(S); + const auto *FRS = cast<CXXForRangeStmt>(S); return FRS->getCond() == Cond || FRS->getRangeInit() == Cond; } default: @@ -1980,16 +1322,15 @@ static bool isConditionForTerminator(const Stmt *S, const Stmt *Cond) { } static bool isIncrementOrInitInForLoop(const Stmt *S, const Stmt *FL) { - if (const ForStmt *FS = dyn_cast<ForStmt>(FL)) + if (const auto *FS = dyn_cast<ForStmt>(FL)) return FS->getInc() == S || FS->getInit() == S; - if (const CXXForRangeStmt *FRS = dyn_cast<CXXForRangeStmt>(FL)) + if (const auto *FRS = dyn_cast<CXXForRangeStmt>(FL)) return FRS->getInc() == S || FRS->getRangeStmt() == S || FRS->getLoopVarStmt() || FRS->getRangeInit() == S; return false; } -typedef llvm::DenseSet<const PathDiagnosticCallPiece *> - OptimizedCallsSet; +using OptimizedCallsSet = llvm::DenseSet<const PathDiagnosticCallPiece *>; /// Adds synthetic edges from top-level statements to their subexpressions. /// @@ -2001,8 +1342,7 @@ static void addContextEdges(PathPieces &pieces, SourceManager &SM, PathPieces::iterator Prev = pieces.end(); for (PathPieces::iterator I = pieces.begin(), E = Prev; I != E; Prev = I, ++I) { - PathDiagnosticControlFlowPiece *Piece = - dyn_cast<PathDiagnosticControlFlowPiece>(I->get()); + auto *Piece = dyn_cast<PathDiagnosticControlFlowPiece>(I->get()); if (!Piece) continue; @@ -2023,7 +1363,7 @@ static void addContextEdges(PathPieces &pieces, SourceManager &SM, // This is important for nested logical expressions (||, &&, ?:) where we // want to show all the levels of context. while (true) { - const Stmt *Dst = getLocStmt(Piece->getEndLocation()); + const Stmt *Dst = Piece->getEndLocation().getStmtOrNull(); // We are looking at an edge. Is the destination within a larger // expression? @@ -2046,9 +1386,11 @@ static void addContextEdges(PathPieces &pieces, SourceManager &SM, auto *PrevPiece = dyn_cast<PathDiagnosticControlFlowPiece>(Prev->get()); if (PrevPiece) { - if (const Stmt *PrevSrc = getLocStmt(PrevPiece->getStartLocation())) { + if (const Stmt *PrevSrc = + PrevPiece->getStartLocation().getStmtOrNull()) { const Stmt *PrevSrcParent = getStmtParent(PrevSrc, PM); - if (PrevSrcParent == getStmtParent(getLocStmt(DstContext), PM)) { + if (PrevSrcParent == + getStmtParent(DstContext.getStmtOrNull(), PM)) { PrevPiece->setEndLocation(DstContext); break; } @@ -2067,7 +1409,7 @@ static void addContextEdges(PathPieces &pieces, SourceManager &SM, } } -/// \brief Move edges from a branch condition to a branch target +/// Move edges from a branch condition to a branch target /// when the condition is simple. /// /// This restructures some of the work of addContextEdges. That function @@ -2077,17 +1419,15 @@ static void addContextEdges(PathPieces &pieces, SourceManager &SM, /// the branch to the branch condition, and (3) an edge from the branch /// condition to the branch target. We keep (1), but may wish to remove (2) /// and move the source of (3) to the branch if the branch condition is simple. -/// static void simplifySimpleBranches(PathPieces &pieces) { for (PathPieces::iterator I = pieces.begin(), E = pieces.end(); I != E; ++I) { - - auto *PieceI = dyn_cast<PathDiagnosticControlFlowPiece>(I->get()); + const auto *PieceI = dyn_cast<PathDiagnosticControlFlowPiece>(I->get()); if (!PieceI) continue; - const Stmt *s1Start = getLocStmt(PieceI->getStartLocation()); - const Stmt *s1End = getLocStmt(PieceI->getEndLocation()); + const Stmt *s1Start = PieceI->getStartLocation().getStmtOrNull(); + const Stmt *s1End = PieceI->getEndLocation().getStmtOrNull(); if (!s1Start || !s1End) continue; @@ -2102,7 +1442,7 @@ static void simplifySimpleBranches(PathPieces &pieces) { if (NextI == E) break; - auto *EV = dyn_cast<PathDiagnosticEventPiece>(NextI->get()); + const auto *EV = dyn_cast<PathDiagnosticEventPiece>(NextI->get()); if (EV) { StringRef S = EV->getString(); if (S == StrEnteringLoop || S == StrLoopBodyZero || @@ -2120,8 +1460,8 @@ static void simplifySimpleBranches(PathPieces &pieces) { if (!PieceNextI) continue; - const Stmt *s2Start = getLocStmt(PieceNextI->getStartLocation()); - const Stmt *s2End = getLocStmt(PieceNextI->getEndLocation()); + const Stmt *s2Start = PieceNextI->getStartLocation().getStmtOrNull(); + const Stmt *s2End = PieceNextI->getEndLocation().getStmtOrNull(); if (!s2Start || !s2End || s1End != s2Start) continue; @@ -2152,7 +1492,7 @@ static void simplifySimpleBranches(PathPieces &pieces) { static Optional<size_t> getLengthOnSingleLine(SourceManager &SM, SourceRange Range) { SourceRange ExpansionRange(SM.getExpansionLoc(Range.getBegin()), - SM.getExpansionRange(Range.getEnd()).second); + SM.getExpansionRange(Range.getEnd()).getEnd()); FileID FID = SM.getFileID(ExpansionRange.getBegin()); if (FID != SM.getFileID(ExpansionRange.getEnd())) @@ -2204,22 +1544,21 @@ static void removeContextCycles(PathPieces &Path, SourceManager &SM, ParentMap &PM) { for (PathPieces::iterator I = Path.begin(), E = Path.end(); I != E; ) { // Pattern match the current piece and its successor. - PathDiagnosticControlFlowPiece *PieceI = - dyn_cast<PathDiagnosticControlFlowPiece>(I->get()); + const auto *PieceI = dyn_cast<PathDiagnosticControlFlowPiece>(I->get()); if (!PieceI) { ++I; continue; } - const Stmt *s1Start = getLocStmt(PieceI->getStartLocation()); - const Stmt *s1End = getLocStmt(PieceI->getEndLocation()); + const Stmt *s1Start = PieceI->getStartLocation().getStmtOrNull(); + const Stmt *s1End = PieceI->getEndLocation().getStmtOrNull(); PathPieces::iterator NextI = I; ++NextI; if (NextI == E) break; - PathDiagnosticControlFlowPiece *PieceNextI = + const auto *PieceNextI = dyn_cast<PathDiagnosticControlFlowPiece>(NextI->get()); if (!PieceNextI) { @@ -2236,8 +1575,8 @@ static void removeContextCycles(PathPieces &Path, SourceManager &SM, } } - const Stmt *s2Start = getLocStmt(PieceNextI->getStartLocation()); - const Stmt *s2End = getLocStmt(PieceNextI->getEndLocation()); + const Stmt *s2Start = PieceNextI->getStartLocation().getStmtOrNull(); + const Stmt *s2End = PieceNextI->getEndLocation().getStmtOrNull(); if (s1Start && s2Start && s1Start == s2End && s2Start == s1End) { const size_t MAX_SHORT_LINE_LENGTH = 80; @@ -2256,10 +1595,8 @@ static void removeContextCycles(PathPieces &Path, SourceManager &SM, } } -/// \brief Return true if X is contained by Y. -static bool lexicalContains(ParentMap &PM, - const Stmt *X, - const Stmt *Y) { +/// Return true if X is contained by Y. +static bool lexicalContains(ParentMap &PM, const Stmt *X, const Stmt *Y) { while (X) { if (X == Y) return true; @@ -2269,24 +1606,21 @@ static bool lexicalContains(ParentMap &PM, } // Remove short edges on the same line less than 3 columns in difference. -static void removePunyEdges(PathPieces &path, - SourceManager &SM, +static void removePunyEdges(PathPieces &path, SourceManager &SM, ParentMap &PM) { - bool erased = false; for (PathPieces::iterator I = path.begin(), E = path.end(); I != E; erased ? I : ++I) { - erased = false; - auto *PieceI = dyn_cast<PathDiagnosticControlFlowPiece>(I->get()); + const auto *PieceI = dyn_cast<PathDiagnosticControlFlowPiece>(I->get()); if (!PieceI) continue; - const Stmt *start = getLocStmt(PieceI->getStartLocation()); - const Stmt *end = getLocStmt(PieceI->getEndLocation()); + const Stmt *start = PieceI->getStartLocation().getStmtOrNull(); + const Stmt *end = PieceI->getEndLocation().getStmtOrNull(); if (!start || !end) continue; @@ -2327,7 +1661,7 @@ static void removePunyEdges(PathPieces &path, static void removeIdenticalEvents(PathPieces &path) { for (PathPieces::iterator I = path.begin(), E = path.end(); I != E; ++I) { - auto *PieceI = dyn_cast<PathDiagnosticEventPiece>(I->get()); + const auto *PieceI = dyn_cast<PathDiagnosticEventPiece>(I->get()); if (!PieceI) continue; @@ -2336,7 +1670,7 @@ static void removeIdenticalEvents(PathPieces &path) { if (NextI == E) return; - auto *PieceNextI = dyn_cast<PathDiagnosticEventPiece>(NextI->get()); + const auto *PieceNextI = dyn_cast<PathDiagnosticEventPiece>(NextI->get()); if (!PieceNextI) continue; @@ -2377,8 +1711,8 @@ static bool optimizeEdges(PathPieces &path, SourceManager &SM, continue; } - const Stmt *s1Start = getLocStmt(PieceI->getStartLocation()); - const Stmt *s1End = getLocStmt(PieceI->getEndLocation()); + const Stmt *s1Start = PieceI->getStartLocation().getStmtOrNull(); + const Stmt *s1End = PieceI->getEndLocation().getStmtOrNull(); const Stmt *level1 = getStmtParent(s1Start, PM); const Stmt *level2 = getStmtParent(s1End, PM); @@ -2386,15 +1720,15 @@ static bool optimizeEdges(PathPieces &path, SourceManager &SM, if (NextI == E) break; - auto *PieceNextI = dyn_cast<PathDiagnosticControlFlowPiece>(NextI->get()); + const auto *PieceNextI = dyn_cast<PathDiagnosticControlFlowPiece>(NextI->get()); if (!PieceNextI) { ++I; continue; } - const Stmt *s2Start = getLocStmt(PieceNextI->getStartLocation()); - const Stmt *s2End = getLocStmt(PieceNextI->getEndLocation()); + const Stmt *s2Start = PieceNextI->getStartLocation().getStmtOrNull(); + const Stmt *s2End = PieceNextI->getEndLocation().getStmtOrNull(); const Stmt *level3 = getStmtParent(s2Start, PM); const Stmt *level4 = getStmtParent(s2End, PM); @@ -2412,7 +1746,6 @@ static bool optimizeEdges(PathPieces &path, SourceManager &SM, // // NOTE: this will be limited later in cases where we add barriers // to prevent this optimization. - // if (level1 && level1 == level2 && level1 == level3 && level1 == level4) { PieceI->setEndLocation(PieceNextI->getEndLocation()); path.erase(NextI); @@ -2427,7 +1760,6 @@ static bool optimizeEdges(PathPieces &path, SourceManager &SM, // // NOTE: this will be limited later in cases where we add barriers // to prevent this optimization. - // if (s1End && s1End == s2Start && level2) { bool removeEdge = false; // Remove edges into the increment or initialization of a @@ -2493,8 +1825,7 @@ static bool optimizeEdges(PathPieces &path, SourceManager &SM, // // (X -> element) if (s1End == s2Start) { - const ObjCForCollectionStmt *FS = - dyn_cast_or_null<ObjCForCollectionStmt>(level3); + const auto *FS = dyn_cast_or_null<ObjCForCollectionStmt>(level3); if (FS && FS->getCollection()->IgnoreParens() == s2Start && s2End == FS->getElement()) { PieceI->setEndLocation(PieceNextI->getEndLocation()); @@ -2532,8 +1863,7 @@ static bool optimizeEdges(PathPieces &path, SourceManager &SM, /// statement had an invalid source location), this function does nothing. // FIXME: We should just generate invalid edges anyway and have the optimizer // deal with them. -static void dropFunctionEntryEdge(PathPieces &Path, - LocationContextMap &LCM, +static void dropFunctionEntryEdge(PathPieces &Path, LocationContextMap &LCM, SourceManager &SM) { const auto *FirstEdge = dyn_cast<PathDiagnosticControlFlowPiece>(Path.front().get()); @@ -2548,11 +1878,134 @@ static void dropFunctionEntryEdge(PathPieces &Path, Path.pop_front(); } +using VisitorsDiagnosticsTy = llvm::DenseMap<const ExplodedNode *, + std::vector<std::shared_ptr<PathDiagnosticPiece>>>; + +/// This function is responsible for generating diagnostic pieces that are +/// *not* provided by bug report visitors. +/// These diagnostics may differ depending on the consumer's settings, +/// and are therefore constructed separately for each consumer. +/// +/// There are two path diagnostics generation modes: with adding edges (used +/// for plists) and without (used for HTML and text). +/// When edges are added (\p ActiveScheme is Extensive), +/// the path is modified to insert artificially generated +/// edges. +/// Otherwise, more detailed diagnostics is emitted for block edges, explaining +/// the transitions in words. +static std::unique_ptr<PathDiagnostic> generatePathDiagnosticForConsumer( + PathDiagnosticConsumer::PathGenerationScheme ActiveScheme, + PathDiagnosticBuilder &PDB, + const ExplodedNode *ErrorNode, + const VisitorsDiagnosticsTy &VisitorsDiagnostics) { + + bool GenerateDiagnostics = (ActiveScheme != PathDiagnosticConsumer::None); + bool AddPathEdges = (ActiveScheme == PathDiagnosticConsumer::Extensive); + SourceManager &SM = PDB.getSourceManager(); + BugReport *R = PDB.getBugReport(); + AnalyzerOptions &Opts = PDB.getBugReporter().getAnalyzerOptions(); + StackDiagVector CallStack; + InterestingExprs IE; + LocationContextMap LCM; + std::unique_ptr<PathDiagnostic> PD = generateEmptyDiagnosticForReport(R, SM); + + if (GenerateDiagnostics) { + auto EndNotes = VisitorsDiagnostics.find(ErrorNode); + std::shared_ptr<PathDiagnosticPiece> LastPiece; + if (EndNotes != VisitorsDiagnostics.end()) { + assert(!EndNotes->second.empty()); + LastPiece = EndNotes->second[0]; + } else { + LastPiece = BugReporterVisitor::getDefaultEndPath(PDB, ErrorNode, *R); + } + PD->setEndOfPath(LastPiece); + } + + PathDiagnosticLocation PrevLoc = PD->getLocation(); + const ExplodedNode *NextNode = ErrorNode->getFirstPred(); + while (NextNode) { + if (GenerateDiagnostics) + generatePathDiagnosticsForNode( + NextNode, *PD, PrevLoc, PDB, LCM, CallStack, IE, AddPathEdges); + + auto VisitorNotes = VisitorsDiagnostics.find(NextNode); + NextNode = NextNode->getFirstPred(); + if (!GenerateDiagnostics || VisitorNotes == VisitorsDiagnostics.end()) + continue; + + // This is a workaround due to inability to put shared PathDiagnosticPiece + // into a FoldingSet. + std::set<llvm::FoldingSetNodeID> DeduplicationSet; + + // Add pieces from custom visitors. + for (const auto &Note : VisitorNotes->second) { + llvm::FoldingSetNodeID ID; + Note->Profile(ID); + auto P = DeduplicationSet.insert(ID); + if (!P.second) + continue; + + if (AddPathEdges) + addEdgeToPath(PD->getActivePath(), PrevLoc, Note->getLocation(), + PDB.LC); + updateStackPiecesWithMessage(*Note, CallStack); + PD->getActivePath().push_front(Note); + } + } + + if (AddPathEdges) { + // Add an edge to the start of the function. + // We'll prune it out later, but it helps make diagnostics more uniform. + const StackFrameContext *CalleeLC = PDB.LC->getStackFrame(); + const Decl *D = CalleeLC->getDecl(); + addEdgeToPath(PD->getActivePath(), PrevLoc, + PathDiagnosticLocation::createBegin(D, SM), CalleeLC); + } + + if (!AddPathEdges && GenerateDiagnostics) + CompactPathDiagnostic(PD->getMutablePieces(), SM); + + // Finally, prune the diagnostic path of uninteresting stuff. + if (!PD->path.empty()) { + if (R->shouldPrunePath() && Opts.shouldPrunePaths()) { + bool stillHasNotes = + removeUnneededCalls(PD->getMutablePieces(), R, LCM); + assert(stillHasNotes); + (void)stillHasNotes; + } + + // Redirect all call pieces to have valid locations. + adjustCallLocations(PD->getMutablePieces()); + removePiecesWithInvalidLocations(PD->getMutablePieces()); + + if (AddPathEdges) { + + // Reduce the number of edges from a very conservative set + // to an aesthetically pleasing subset that conveys the + // necessary information. + OptimizedCallsSet OCS; + while (optimizeEdges(PD->getMutablePieces(), SM, OCS, LCM)) {} + + // Drop the very first function-entry edge. It's not really necessary + // for top-level functions. + dropFunctionEntryEdge(PD->getMutablePieces(), LCM, SM); + } + + // Remove messages that are basically the same, and edges that may not + // make sense. + // We have to do this after edge optimization in the Extensive mode. + removeRedundantMsgs(PD->getMutablePieces()); + removeEdgesToDefaultInitializers(PD->getMutablePieces()); + } + return PD; +} + //===----------------------------------------------------------------------===// // Methods for BugType and subclasses. //===----------------------------------------------------------------------===// -void BugType::anchor() { } + +void BugType::anchor() {} void BugType::FlushReports(BugReporter &BR) {} @@ -2570,14 +2023,17 @@ void BugReport::addVisitor(std::unique_ptr<BugReporterVisitor> visitor) { llvm::FoldingSetNodeID ID; visitor->Profile(ID); - void *InsertPos; - if (CallbacksSet.FindNodeOrInsertPos(ID, InsertPos)) + void *InsertPos = nullptr; + if (CallbacksSet.FindNodeOrInsertPos(ID, InsertPos)) { return; + } - CallbacksSet.InsertNode(visitor.get(), InsertPos); Callbacks.push_back(std::move(visitor)); - ++ConfigurationChangeToken; +} + +void BugReport::clearVisitors() { + Callbacks.clear(); } BugReport::~BugReport() { @@ -2595,7 +2051,7 @@ const Decl *BugReport::getDeclWithIssue() const { return nullptr; const LocationContext *LC = N->getLocationContext(); - return LC->getCurrentStackFrame()->getDecl(); + return LC->getStackFrame()->getDecl(); } void BugReport::Profile(llvm::FoldingSetNodeID& hash) const { @@ -2623,11 +2079,9 @@ void BugReport::markInteresting(SymbolRef sym) { if (!sym) return; - // If the symbol wasn't already in our set, note a configuration change. - if (getInterestingSymbols().insert(sym).second) - ++ConfigurationChangeToken; + getInterestingSymbols().insert(sym); - if (const SymbolMetadata *meta = dyn_cast<SymbolMetadata>(sym)) + if (const auto *meta = dyn_cast<SymbolMetadata>(sym)) getInterestingRegions().insert(meta->getRegion()); } @@ -2635,12 +2089,10 @@ void BugReport::markInteresting(const MemRegion *R) { if (!R) return; - // If the base region wasn't already in our set, note a configuration change. R = R->getBaseRegion(); - if (getInterestingRegions().insert(R).second) - ++ConfigurationChangeToken; + getInterestingRegions().insert(R); - if (const SymbolicRegion *SR = dyn_cast<SymbolicRegion>(R)) + if (const auto *SR = dyn_cast<SymbolicRegion>(R)) getInterestingSymbols().insert(SR->getSymbol()); } @@ -2674,7 +2126,7 @@ bool BugReport::isInteresting(const MemRegion *R) { bool b = getInterestingRegions().count(R); if (b) return true; - if (const SymbolicRegion *SR = dyn_cast<SymbolicRegion>(R)) + if (const auto *SR = dyn_cast<SymbolicRegion>(R)) return getInterestingSymbols().count(SR->getSymbol()); return false; } @@ -2734,7 +2186,7 @@ llvm::iterator_range<BugReport::ranges_iterator> BugReport::getRanges() { // If no custom ranges, add the range of the statement corresponding to // the error node. if (Ranges.empty()) { - if (const Expr *E = dyn_cast_or_null<Expr>(getStmt())) + if (const auto *E = dyn_cast_or_null<Expr>(getStmt())) addRange(E->getSourceRange()); else return llvm::make_range(ranges_iterator(), ranges_iterator()); @@ -2762,9 +2214,11 @@ PathDiagnosticLocation BugReport::getLocation(const SourceManager &SM) const { // Methods for BugReporter and subclasses. //===----------------------------------------------------------------------===// -BugReportEquivClass::~BugReportEquivClass() { } -GRBugReporter::~GRBugReporter() { } -BugReporterData::~BugReporterData() {} +BugReportEquivClass::~BugReportEquivClass() = default; + +GRBugReporter::~GRBugReporter() = default; + +BugReporterData::~BugReporterData() = default; ExplodedGraph &GRBugReporter::getGraph() { return Eng.getGraph(); } @@ -2775,11 +2229,8 @@ BugReporter::~BugReporter() { FlushReports(); // Free the bug reports we are tracking. - typedef std::vector<BugReportEquivClass *> ContTy; - for (ContTy::iterator I = EQClassesVector.begin(), E = EQClassesVector.end(); - I != E; ++I) { - delete *I; - } + for (const auto I : EQClassesVector) + delete I; } void BugReporter::FlushReports() { @@ -2791,18 +2242,13 @@ void BugReporter::FlushReports() { // FIXME: Only NSErrorChecker needs BugType's FlushReports. // Turn NSErrorChecker into a proper checker and remove this. SmallVector<const BugType *, 16> bugTypes(BugTypes.begin(), BugTypes.end()); - for (SmallVectorImpl<const BugType *>::iterator - I = bugTypes.begin(), E = bugTypes.end(); I != E; ++I) - const_cast<BugType*>(*I)->FlushReports(*this); + for (const auto I : bugTypes) + const_cast<BugType*>(I)->FlushReports(*this); // We need to flush reports in deterministic order to ensure the order // of the reports is consistent between runs. - typedef std::vector<BugReportEquivClass *> ContVecTy; - for (ContVecTy::iterator EI=EQClassesVector.begin(), EE=EQClassesVector.end(); - EI != EE; ++EI){ - BugReportEquivClass& EQ = **EI; - FlushReport(EQ); - } + for (const auto EQ : EQClassesVector) + FlushReport(*EQ); // BugReporter owns and deletes only BugTypes created implicitly through // EmitBasicReport. @@ -2819,6 +2265,7 @@ void BugReporter::FlushReports() { //===----------------------------------------------------------------------===// namespace { + /// A wrapper around a report graph, which contains only a single path, and its /// node maps. class ReportGraph { @@ -2833,10 +2280,12 @@ public: class TrimmedGraph { InterExplodedGraphMap InverseMap; - typedef llvm::DenseMap<const ExplodedNode *, unsigned> PriorityMapTy; + using PriorityMapTy = llvm::DenseMap<const ExplodedNode *, unsigned>; + PriorityMapTy PriorityMap; - typedef std::pair<const ExplodedNode *, size_t> NodeIndexPair; + using NodeIndexPair = std::pair<const ExplodedNode *, size_t>; + SmallVector<NodeIndexPair, 32> ReportNodes; std::unique_ptr<ExplodedGraph> G; @@ -2874,7 +2323,8 @@ public: bool popNextReportGraph(ReportGraph &GraphWrapper); }; -} + +} // namespace TrimmedGraph::TrimmedGraph(const ExplodedGraph *OriginalGraph, ArrayRef<const ExplodedNode *> Nodes) { @@ -2930,8 +2380,8 @@ TrimmedGraph::TrimmedGraph(const ExplodedGraph *OriginalGraph, } // Sort the error paths from longest to shortest. - std::sort(ReportNodes.begin(), ReportNodes.end(), - PriorityCompare<true>(PriorityMap)); + llvm::sort(ReportNodes.begin(), ReportNodes.end(), + PriorityCompare<true>(PriorityMap)); } bool TrimmedGraph::popNextReportGraph(ReportGraph &GraphWrapper) { @@ -2987,23 +2437,21 @@ bool TrimmedGraph::popNextReportGraph(ReportGraph &GraphWrapper) { return true; } - /// CompactPathDiagnostic - This function postprocesses a PathDiagnostic object /// and collapses PathDiagosticPieces that are expanded by macros. static void CompactPathDiagnostic(PathPieces &path, const SourceManager& SM) { - typedef std::vector< - std::pair<std::shared_ptr<PathDiagnosticMacroPiece>, SourceLocation>> - MacroStackTy; + using MacroStackTy = + std::vector< + std::pair<std::shared_ptr<PathDiagnosticMacroPiece>, SourceLocation>>; - typedef std::vector<std::shared_ptr<PathDiagnosticPiece>> PiecesTy; + using PiecesTy = std::vector<std::shared_ptr<PathDiagnosticPiece>>; MacroStackTy MacroStack; PiecesTy Pieces; for (PathPieces::const_iterator I = path.begin(), E = path.end(); - I!=E; ++I) { - - auto &piece = *I; + I != E; ++I) { + const auto &piece = *I; // Recursively compact calls. if (auto *call = dyn_cast<PathDiagnosticCallPiece>(&*piece)) { @@ -3082,43 +2530,69 @@ static void CompactPathDiagnostic(PathPieces &path, const SourceManager& SM) { path.insert(path.end(), Pieces.begin(), Pieces.end()); } -bool GRBugReporter::generatePathDiagnostic(PathDiagnostic& PD, - PathDiagnosticConsumer &PC, - ArrayRef<BugReport *> &bugReports) { - assert(!bugReports.empty()); +/// Generate notes from all visitors. +/// Notes associated with {@code ErrorNode} are generated using +/// {@code getEndPath}, and the rest are generated with {@code VisitNode}. +static std::unique_ptr<VisitorsDiagnosticsTy> +generateVisitorsDiagnostics(BugReport *R, const ExplodedNode *ErrorNode, + BugReporterContext &BRC) { + auto Notes = llvm::make_unique<VisitorsDiagnosticsTy>(); + BugReport::VisitorList visitors; - bool HasValid = false; - bool HasInvalid = false; - SmallVector<const ExplodedNode *, 32> errorNodes; - for (ArrayRef<BugReport*>::iterator I = bugReports.begin(), - E = bugReports.end(); I != E; ++I) { - if ((*I)->isValid()) { - HasValid = true; - errorNodes.push_back((*I)->getErrorNode()); - } else { - // Keep the errorNodes list in sync with the bugReports list. - HasInvalid = true; - errorNodes.push_back(nullptr); + // Run visitors on all nodes starting from the node *before* the last one. + // The last node is reserved for notes generated with {@code getEndPath}. + const ExplodedNode *NextNode = ErrorNode->getFirstPred(); + while (NextNode) { + + // At each iteration, move all visitors from report to visitor list. + for (BugReport::visitor_iterator I = R->visitor_begin(), + E = R->visitor_end(); + I != E; ++I) { + visitors.push_back(std::move(*I)); } - } + R->clearVisitors(); - // If all the reports have been marked invalid by a previous path generation, - // we're done. - if (!HasValid) - return false; + const ExplodedNode *Pred = NextNode->getFirstPred(); + if (!Pred) { + std::shared_ptr<PathDiagnosticPiece> LastPiece; + for (auto &V : visitors) { + V->finalizeVisitor(BRC, ErrorNode, *R); - typedef PathDiagnosticConsumer::PathGenerationScheme PathGenerationScheme; - PathGenerationScheme ActiveScheme = PC.getGenerationScheme(); + if (auto Piece = V->getEndPath(BRC, ErrorNode, *R)) { + assert(!LastPiece && + "There can only be one final piece in a diagnostic."); + LastPiece = std::move(Piece); + (*Notes)[ErrorNode].push_back(LastPiece); + } + } + break; + } - if (ActiveScheme == PathDiagnosticConsumer::Extensive) { - AnalyzerOptions &options = getAnalyzerOptions(); - if (options.getBooleanOption("path-diagnostics-alternate", true)) { - ActiveScheme = PathDiagnosticConsumer::AlternateExtensive; + for (auto &V : visitors) { + auto P = V->VisitNode(NextNode, Pred, BRC, *R); + if (P) + (*Notes)[NextNode].push_back(std::move(P)); } + + if (!R->isValid()) + break; + + NextNode = Pred; } - TrimmedGraph TrimG(&getGraph(), errorNodes); - ReportGraph ErrorGraph; + return Notes; +} + +/// Find a non-invalidated report for a given equivalence class, +/// and return together with a cache of visitors notes. +/// If none found, return a nullptr paired with an empty cache. +static +std::pair<BugReport*, std::unique_ptr<VisitorsDiagnosticsTy>> findValidReport( + TrimmedGraph &TrimG, + ReportGraph &ErrorGraph, + ArrayRef<BugReport *> &bugReports, + AnalyzerOptions &Opts, + GRBugReporter &Reporter) { while (TrimG.popNextReportGraph(ErrorGraph)) { // Find the BugReport with the original location. @@ -3126,125 +2600,85 @@ bool GRBugReporter::generatePathDiagnostic(PathDiagnostic& PD, BugReport *R = bugReports[ErrorGraph.Index]; assert(R && "No original report found for sliced graph."); assert(R->isValid() && "Report selected by trimmed graph marked invalid."); + const ExplodedNode *ErrorNode = ErrorGraph.ErrorNode; - // Start building the path diagnostic... - PathDiagnosticBuilder PDB(*this, R, ErrorGraph.BackMap, &PC); - const ExplodedNode *N = ErrorGraph.ErrorNode; + // Register refutation visitors first, if they mark the bug invalid no + // further analysis is required + R->addVisitor(llvm::make_unique<LikelyFalsePositiveSuppressionBRVisitor>()); // Register additional node visitors. R->addVisitor(llvm::make_unique<NilReceiverBRVisitor>()); R->addVisitor(llvm::make_unique<ConditionBRVisitor>()); - R->addVisitor(llvm::make_unique<LikelyFalsePositiveSuppressionBRVisitor>()); R->addVisitor(llvm::make_unique<CXXSelfAssignmentBRVisitor>()); - BugReport::VisitorList visitors; - unsigned origReportConfigToken, finalReportConfigToken; - LocationContextMap LCM; - - // While generating diagnostics, it's possible the visitors will decide - // new symbols and regions are interesting, or add other visitors based on - // the information they find. If they do, we need to regenerate the path - // based on our new report configuration. - do { - // Get a clean copy of all the visitors. - for (BugReport::visitor_iterator I = R->visitor_begin(), - E = R->visitor_end(); I != E; ++I) - visitors.push_back((*I)->clone()); - - // Clear out the active path from any previous work. - PD.resetPath(); - origReportConfigToken = R->getConfigurationChangeToken(); - - // Generate the very last diagnostic piece - the piece is visible before - // the trace is expanded. - std::unique_ptr<PathDiagnosticPiece> LastPiece; - for (BugReport::visitor_iterator I = visitors.begin(), E = visitors.end(); - I != E; ++I) { - if (std::unique_ptr<PathDiagnosticPiece> Piece = - (*I)->getEndPath(PDB, N, *R)) { - assert (!LastPiece && - "There can only be one final piece in a diagnostic."); - LastPiece = std::move(Piece); - } - } + BugReporterContext BRC(Reporter, ErrorGraph.BackMap); - if (ActiveScheme != PathDiagnosticConsumer::None) { - if (!LastPiece) - LastPiece = BugReporterVisitor::getDefaultEndPath(PDB, N, *R); - assert(LastPiece); - PD.setEndOfPath(std::move(LastPiece)); - } + // Run all visitors on a given graph, once. + std::unique_ptr<VisitorsDiagnosticsTy> visitorNotes = + generateVisitorsDiagnostics(R, ErrorNode, BRC); - // Make sure we get a clean location context map so we don't - // hold onto old mappings. - LCM.clear(); + if (R->isValid()) { + if (Opts.shouldCrosscheckWithZ3()) { + // If crosscheck is enabled, remove all visitors, add the refutation + // visitor and check again + R->clearVisitors(); + R->addVisitor(llvm::make_unique<FalsePositiveRefutationBRVisitor>()); - switch (ActiveScheme) { - case PathDiagnosticConsumer::AlternateExtensive: - GenerateAlternateExtensivePathDiagnostic(PD, PDB, N, LCM, visitors); - break; - case PathDiagnosticConsumer::Extensive: - GenerateExtensivePathDiagnostic(PD, PDB, N, LCM, visitors); - break; - case PathDiagnosticConsumer::Minimal: - GenerateMinimalPathDiagnostic(PD, PDB, N, LCM, visitors); - break; - case PathDiagnosticConsumer::None: - GenerateVisitorsOnlyPathDiagnostic(PD, PDB, N, visitors); - break; + // We don't overrite the notes inserted by other visitors because the + // refutation manager does not add any new note to the path + generateVisitorsDiagnostics(R, ErrorGraph.ErrorNode, BRC); } - // Clean up the visitors we used. - visitors.clear(); - - // Did anything change while generating this path? - finalReportConfigToken = R->getConfigurationChangeToken(); - } while (finalReportConfigToken != origReportConfigToken); - - if (!R->isValid()) - continue; - - // Finally, prune the diagnostic path of uninteresting stuff. - if (!PD.path.empty()) { - if (R->shouldPrunePath() && getAnalyzerOptions().shouldPrunePaths()) { - bool stillHasNotes = removeUnneededCalls(PD.getMutablePieces(), R, LCM); - assert(stillHasNotes); - (void)stillHasNotes; - } + // Check if the bug is still valid + if (R->isValid()) + return std::make_pair(R, std::move(visitorNotes)); + } + } - // Redirect all call pieces to have valid locations. - adjustCallLocations(PD.getMutablePieces()); - removePiecesWithInvalidLocations(PD.getMutablePieces()); + return std::make_pair(nullptr, llvm::make_unique<VisitorsDiagnosticsTy>()); +} - if (ActiveScheme == PathDiagnosticConsumer::AlternateExtensive) { - SourceManager &SM = getSourceManager(); +std::unique_ptr<DiagnosticForConsumerMapTy> +GRBugReporter::generatePathDiagnostics( + ArrayRef<PathDiagnosticConsumer *> consumers, + ArrayRef<BugReport *> &bugReports) { + assert(!bugReports.empty()); - // Reduce the number of edges from a very conservative set - // to an aesthetically pleasing subset that conveys the - // necessary information. - OptimizedCallsSet OCS; - while (optimizeEdges(PD.getMutablePieces(), SM, OCS, LCM)) {} + auto Out = llvm::make_unique<DiagnosticForConsumerMapTy>(); + bool HasValid = false; + SmallVector<const ExplodedNode *, 32> errorNodes; + for (const auto I : bugReports) { + if (I->isValid()) { + HasValid = true; + errorNodes.push_back(I->getErrorNode()); + } else { + // Keep the errorNodes list in sync with the bugReports list. + errorNodes.push_back(nullptr); + } + } - // Drop the very first function-entry edge. It's not really necessary - // for top-level functions. - dropFunctionEntryEdge(PD.getMutablePieces(), LCM, SM); - } + // If all the reports have been marked invalid by a previous path generation, + // we're done. + if (!HasValid) + return Out; - // Remove messages that are basically the same, and edges that may not - // make sense. - // We have to do this after edge optimization in the Extensive mode. - removeRedundantMsgs(PD.getMutablePieces()); - removeEdgesToDefaultInitializers(PD.getMutablePieces()); + TrimmedGraph TrimG(&getGraph(), errorNodes); + ReportGraph ErrorGraph; + auto ReportInfo = findValidReport(TrimG, ErrorGraph, bugReports, + getAnalyzerOptions(), *this); + BugReport *R = ReportInfo.first; + + if (R && R->isValid()) { + const ExplodedNode *ErrorNode = ErrorGraph.ErrorNode; + for (PathDiagnosticConsumer *PC : consumers) { + PathDiagnosticBuilder PDB(*this, R, ErrorGraph.BackMap, PC); + std::unique_ptr<PathDiagnostic> PD = generatePathDiagnosticForConsumer( + PC->getGenerationScheme(), PDB, ErrorNode, *ReportInfo.second); + (*Out)[PC] = std::move(PD); } - - // We found a report and didn't suppress it. - return true; } - // We suppressed all the reports in this equivalence class. - assert(!HasInvalid && "Inconsistent suppression"); - (void)HasInvalid; - return false; + return Out; } void BugReporter::Register(BugType *BT) { @@ -3294,20 +2728,21 @@ void BugReporter::emitReport(std::unique_ptr<BugReport> R) { EQ->AddReport(std::move(R)); } - //===----------------------------------------------------------------------===// // Emitting reports in equivalence classes. //===----------------------------------------------------------------------===// namespace { + struct FRIEC_WLItem { const ExplodedNode *N; ExplodedNode::const_succ_iterator I, E; FRIEC_WLItem(const ExplodedNode *n) - : N(n), I(N->succ_begin()), E(N->succ_end()) {} + : N(n), I(N->succ_begin()), E(N->succ_end()) {} }; -} + +} // namespace static const CFGBlock *findBlockForNode(const ExplodedNode *N) { ProgramPoint P = N->getLocation(); @@ -3397,7 +2832,6 @@ static bool isInevitablySinking(const ExplodedNode *N) { static BugReport * FindReportInEquivalenceClass(BugReportEquivClass& EQ, SmallVectorImpl<BugReport*> &bugReports) { - BugReportEquivClass::iterator I = EQ.begin(), E = EQ.end(); assert(I != E); BugType& BT = I->getBugType(); @@ -3407,10 +2841,10 @@ FindReportInEquivalenceClass(BugReportEquivClass& EQ, // to 'Nodes'. Any of the reports will serve as a "representative" report. if (!BT.isSuppressOnSink()) { BugReport *R = &*I; - for (BugReportEquivClass::iterator I=EQ.begin(), E=EQ.end(); I!=E; ++I) { - const ExplodedNode *N = I->getErrorNode(); + for (auto &I : EQ) { + const ExplodedNode *N = I.getErrorNode(); if (N) { - R = &*I; + R = &I; bugReports.push_back(R); } } @@ -3451,8 +2885,9 @@ FindReportInEquivalenceClass(BugReportEquivClass& EQ, // At this point we know that 'N' is not a sink and it has at least one // successor. Use a DFS worklist to find a non-sink end-of-path node. - typedef FRIEC_WLItem WLItem; - typedef SmallVector<WLItem, 10> DFSWorkList; + using WLItem = FRIEC_WLItem; + using DFSWorkList = SmallVector<WLItem, 10>; + llvm::DenseMap<const ExplodedNode *, unsigned> Visited; DFSWorkList WL; @@ -3502,90 +2937,166 @@ FindReportInEquivalenceClass(BugReportEquivClass& EQ, void BugReporter::FlushReport(BugReportEquivClass& EQ) { SmallVector<BugReport*, 10> bugReports; - BugReport *exampleReport = FindReportInEquivalenceClass(EQ, bugReports); - if (exampleReport) { - for (PathDiagnosticConsumer *PDC : getPathDiagnosticConsumers()) { - FlushReport(exampleReport, *PDC, bugReports); + BugReport *report = FindReportInEquivalenceClass(EQ, bugReports); + if (!report) + return; + + ArrayRef<PathDiagnosticConsumer*> Consumers = getPathDiagnosticConsumers(); + std::unique_ptr<DiagnosticForConsumerMapTy> Diagnostics = + generateDiagnosticForConsumerMap(report, Consumers, bugReports); + + for (auto &P : *Diagnostics) { + PathDiagnosticConsumer *Consumer = P.first; + std::unique_ptr<PathDiagnostic> &PD = P.second; + + // If the path is empty, generate a single step path with the location + // of the issue. + if (PD->path.empty()) { + PathDiagnosticLocation L = report->getLocation(getSourceManager()); + auto piece = llvm::make_unique<PathDiagnosticEventPiece>( + L, report->getDescription()); + for (SourceRange Range : report->getRanges()) + piece->addRange(Range); + PD->setEndOfPath(std::move(piece)); } - } -} -void BugReporter::FlushReport(BugReport *exampleReport, - PathDiagnosticConsumer &PD, - ArrayRef<BugReport*> bugReports) { + PathPieces &Pieces = PD->getMutablePieces(); + if (getAnalyzerOptions().shouldDisplayNotesAsEvents()) { + // For path diagnostic consumers that don't support extra notes, + // we may optionally convert those to path notes. + for (auto I = report->getNotes().rbegin(), + E = report->getNotes().rend(); I != E; ++I) { + PathDiagnosticNotePiece *Piece = I->get(); + auto ConvertedPiece = std::make_shared<PathDiagnosticEventPiece>( + Piece->getLocation(), Piece->getString()); + for (const auto &R: Piece->getRanges()) + ConvertedPiece->addRange(R); + + Pieces.push_front(std::move(ConvertedPiece)); + } + } else { + for (auto I = report->getNotes().rbegin(), + E = report->getNotes().rend(); I != E; ++I) + Pieces.push_front(*I); + } - // FIXME: Make sure we use the 'R' for the path that was actually used. - // Probably doesn't make a difference in practice. - BugType& BT = exampleReport->getBugType(); + // Get the meta data. + const BugReport::ExtraTextList &Meta = report->getExtraText(); + for (const auto &i : Meta) + PD->addMeta(i); - std::unique_ptr<PathDiagnostic> D(new PathDiagnostic( - exampleReport->getBugType().getCheckName(), - exampleReport->getDeclWithIssue(), exampleReport->getBugType().getName(), - exampleReport->getDescription(), - exampleReport->getShortDescription(/*Fallback=*/false), BT.getCategory(), - exampleReport->getUniqueingLocation(), - exampleReport->getUniqueingDecl())); + Consumer->HandlePathDiagnostic(std::move(PD)); + } +} - if (exampleReport->isPathSensitive()) { - // Generate the full path diagnostic, using the generation scheme - // specified by the PathDiagnosticConsumer. Note that we have to generate - // path diagnostics even for consumers which do not support paths, because - // the BugReporterVisitors may mark this bug as a false positive. - assert(!bugReports.empty()); +/// Insert all lines participating in the function signature \p Signature +/// into \p ExecutedLines. +static void populateExecutedLinesWithFunctionSignature( + const Decl *Signature, SourceManager &SM, + std::unique_ptr<FilesToLineNumsMap> &ExecutedLines) { + SourceRange SignatureSourceRange; + const Stmt* Body = Signature->getBody(); + if (const auto FD = dyn_cast<FunctionDecl>(Signature)) { + SignatureSourceRange = FD->getSourceRange(); + } else if (const auto OD = dyn_cast<ObjCMethodDecl>(Signature)) { + SignatureSourceRange = OD->getSourceRange(); + } else { + return; + } + SourceLocation Start = SignatureSourceRange.getBegin(); + SourceLocation End = Body ? Body->getSourceRange().getBegin() + : SignatureSourceRange.getEnd(); + unsigned StartLine = SM.getExpansionLineNumber(Start); + unsigned EndLine = SM.getExpansionLineNumber(End); - MaxBugClassSize.updateMax(bugReports.size()); + FileID FID = SM.getFileID(SM.getExpansionLoc(Start)); + for (unsigned Line = StartLine; Line <= EndLine; Line++) + ExecutedLines->operator[](FID.getHashValue()).insert(Line); +} - if (!generatePathDiagnostic(*D.get(), PD, bugReports)) - return; +static void populateExecutedLinesWithStmt( + const Stmt *S, SourceManager &SM, + std::unique_ptr<FilesToLineNumsMap> &ExecutedLines) { + SourceLocation Loc = S->getSourceRange().getBegin(); + SourceLocation ExpansionLoc = SM.getExpansionLoc(Loc); + FileID FID = SM.getFileID(ExpansionLoc); + unsigned LineNo = SM.getExpansionLineNumber(ExpansionLoc); + ExecutedLines->operator[](FID.getHashValue()).insert(LineNo); +} - MaxValidBugClassSize.updateMax(bugReports.size()); +/// \return all executed lines including function signatures on the path +/// starting from \p N. +static std::unique_ptr<FilesToLineNumsMap> +findExecutedLines(SourceManager &SM, const ExplodedNode *N) { + auto ExecutedLines = llvm::make_unique<FilesToLineNumsMap>(); - // Examine the report and see if the last piece is in a header. Reset the - // report location to the last piece in the main source file. - AnalyzerOptions &Opts = getAnalyzerOptions(); - if (Opts.shouldReportIssuesInMainSourceFile() && !Opts.AnalyzeAll) - D->resetDiagnosticLocationToMainFile(); - } - - // If the path is empty, generate a single step path with the location - // of the issue. - if (D->path.empty()) { - PathDiagnosticLocation L = exampleReport->getLocation(getSourceManager()); - auto piece = llvm::make_unique<PathDiagnosticEventPiece>( - L, exampleReport->getDescription()); - for (SourceRange Range : exampleReport->getRanges()) - piece->addRange(Range); - D->setEndOfPath(std::move(piece)); - } - - PathPieces &Pieces = D->getMutablePieces(); - if (getAnalyzerOptions().shouldDisplayNotesAsEvents()) { - // For path diagnostic consumers that don't support extra notes, - // we may optionally convert those to path notes. - for (auto I = exampleReport->getNotes().rbegin(), - E = exampleReport->getNotes().rend(); I != E; ++I) { - PathDiagnosticNotePiece *Piece = I->get(); - auto ConvertedPiece = std::make_shared<PathDiagnosticEventPiece>( - Piece->getLocation(), Piece->getString()); - for (const auto &R: Piece->getRanges()) - ConvertedPiece->addRange(R); + while (N) { + if (N->getFirstPred() == nullptr) { + // First node: show signature of the entrance point. + const Decl *D = N->getLocationContext()->getDecl(); + populateExecutedLinesWithFunctionSignature(D, SM, ExecutedLines); + } else if (auto CE = N->getLocationAs<CallEnter>()) { + // Inlined function: show signature. + const Decl* D = CE->getCalleeContext()->getDecl(); + populateExecutedLinesWithFunctionSignature(D, SM, ExecutedLines); + } else if (const Stmt *S = PathDiagnosticLocation::getStmt(N)) { + populateExecutedLinesWithStmt(S, SM, ExecutedLines); + + // Show extra context for some parent kinds. + const Stmt *P = N->getParentMap().getParent(S); + + // The path exploration can die before the node with the associated + // return statement is generated, but we do want to show the whole + // return. + if (const auto *RS = dyn_cast_or_null<ReturnStmt>(P)) { + populateExecutedLinesWithStmt(RS, SM, ExecutedLines); + P = N->getParentMap().getParent(RS); + } - Pieces.push_front(std::move(ConvertedPiece)); + if (P && (isa<SwitchCase>(P) || isa<LabelStmt>(P))) + populateExecutedLinesWithStmt(P, SM, ExecutedLines); } - } else { - for (auto I = exampleReport->getNotes().rbegin(), - E = exampleReport->getNotes().rend(); I != E; ++I) - Pieces.push_front(*I); + + N = N->getFirstPred(); } + return ExecutedLines; +} + +std::unique_ptr<DiagnosticForConsumerMapTy> +BugReporter::generateDiagnosticForConsumerMap( + BugReport *report, ArrayRef<PathDiagnosticConsumer *> consumers, + ArrayRef<BugReport *> bugReports) { - // Get the meta data. - const BugReport::ExtraTextList &Meta = exampleReport->getExtraText(); - for (BugReport::ExtraTextList::const_iterator i = Meta.begin(), - e = Meta.end(); i != e; ++i) { - D->addMeta(*i); + if (!report->isPathSensitive()) { + auto Out = llvm::make_unique<DiagnosticForConsumerMapTy>(); + for (auto *Consumer : consumers) + (*Out)[Consumer] = generateEmptyDiagnosticForReport(report, + getSourceManager()); + return Out; } - PD.HandlePathDiagnostic(std::move(D)); + // Generate the full path sensitive diagnostic, using the generation scheme + // specified by the PathDiagnosticConsumer. Note that we have to generate + // path diagnostics even for consumers which do not support paths, because + // the BugReporterVisitors may mark this bug as a false positive. + assert(!bugReports.empty()); + MaxBugClassSize.updateMax(bugReports.size()); + std::unique_ptr<DiagnosticForConsumerMapTy> Out = + generatePathDiagnostics(consumers, bugReports); + + if (Out->empty()) + return Out; + + MaxValidBugClassSize.updateMax(bugReports.size()); + + // Examine the report and see if the last piece is in a header. Reset the + // report location to the last piece in the main source file. + AnalyzerOptions &Opts = getAnalyzerOptions(); + for (auto const &P : *Out) + if (Opts.shouldReportIssuesInMainSourceFile() && !Opts.AnalyzeAll) + P.second->resetDiagnosticLocationToMainFile(); + + return Out; } void BugReporter::EmitBasicReport(const Decl *DeclWithIssue, @@ -3596,12 +3107,12 @@ void BugReporter::EmitBasicReport(const Decl *DeclWithIssue, EmitBasicReport(DeclWithIssue, Checker->getCheckName(), Name, Category, Str, Loc, Ranges); } + void BugReporter::EmitBasicReport(const Decl *DeclWithIssue, CheckName CheckName, StringRef name, StringRef category, StringRef str, PathDiagnosticLocation Loc, ArrayRef<SourceRange> Ranges) { - // 'BT' is owned by BugReporter. BugType *BT = getBugTypeForName(CheckName, name, category); auto R = llvm::make_unique<BugReport>(*BT, str, Loc); @@ -3622,84 +3133,3 @@ BugType *BugReporter::getBugTypeForName(CheckName CheckName, StringRef name, BT = new BugType(CheckName, name, category); return BT; } - -LLVM_DUMP_METHOD void PathPieces::dump() const { - unsigned index = 0; - for (PathPieces::const_iterator I = begin(), E = end(); I != E; ++I) { - llvm::errs() << "[" << index++ << "] "; - (*I)->dump(); - llvm::errs() << "\n"; - } -} - -LLVM_DUMP_METHOD void PathDiagnosticCallPiece::dump() const { - llvm::errs() << "CALL\n--------------\n"; - - if (const Stmt *SLoc = getLocStmt(getLocation())) - SLoc->dump(); - else if (const NamedDecl *ND = dyn_cast<NamedDecl>(getCallee())) - llvm::errs() << *ND << "\n"; - else - getLocation().dump(); -} - -LLVM_DUMP_METHOD void PathDiagnosticEventPiece::dump() const { - llvm::errs() << "EVENT\n--------------\n"; - llvm::errs() << getString() << "\n"; - llvm::errs() << " ---- at ----\n"; - getLocation().dump(); -} - -LLVM_DUMP_METHOD void PathDiagnosticControlFlowPiece::dump() const { - llvm::errs() << "CONTROL\n--------------\n"; - getStartLocation().dump(); - llvm::errs() << " ---- to ----\n"; - getEndLocation().dump(); -} - -LLVM_DUMP_METHOD void PathDiagnosticMacroPiece::dump() const { - llvm::errs() << "MACRO\n--------------\n"; - // FIXME: Print which macro is being invoked. -} - -LLVM_DUMP_METHOD void PathDiagnosticNotePiece::dump() const { - llvm::errs() << "NOTE\n--------------\n"; - llvm::errs() << getString() << "\n"; - llvm::errs() << " ---- at ----\n"; - getLocation().dump(); -} - -LLVM_DUMP_METHOD void PathDiagnosticLocation::dump() const { - if (!isValid()) { - llvm::errs() << "<INVALID>\n"; - return; - } - - switch (K) { - case RangeK: - // FIXME: actually print the range. - llvm::errs() << "<range>\n"; - break; - case SingleLocK: - asLocation().dump(); - llvm::errs() << "\n"; - break; - case StmtK: - if (S) - S->dump(); - else - llvm::errs() << "<NULL STMT>\n"; - break; - case DeclK: - if (const NamedDecl *ND = dyn_cast_or_null<NamedDecl>(D)) - llvm::errs() << *ND << "\n"; - else if (isa<BlockDecl>(D)) - // FIXME: Make this nicer. - llvm::errs() << "<block>\n"; - else if (D) - llvm::errs() << "<unknown decl>\n"; - else - llvm::errs() << "<NULL DECL>\n"; - break; - } -} diff --git a/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp b/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp index 972f4c5f3da2..c87bc685d8b9 100644 --- a/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp +++ b/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp @@ -1,4 +1,4 @@ -// BugReporterVisitors.cpp - Helpers for reporting bugs -----------*- C++ -*--// +//===- BugReporterVisitors.cpp - Helpers for reporting bugs ---------------===// // // The LLVM Compiler Infrastructure // @@ -11,37 +11,83 @@ // enhance the diagnostics reported for a bug. // //===----------------------------------------------------------------------===// + #include "clang/StaticAnalyzer/Core/BugReporter/BugReporterVisitors.h" +#include "clang/AST/ASTContext.h" +#include "clang/AST/Decl.h" +#include "clang/AST/DeclBase.h" +#include "clang/AST/DeclCXX.h" #include "clang/AST/Expr.h" +#include "clang/AST/ExprCXX.h" #include "clang/AST/ExprObjC.h" +#include "clang/AST/Stmt.h" +#include "clang/AST/Type.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" +#include "clang/Analysis/AnalysisDeclContext.h" +#include "clang/Analysis/CFG.h" #include "clang/Analysis/CFGStmtMap.h" +#include "clang/Analysis/ProgramPoint.h" +#include "clang/Basic/IdentifierTable.h" +#include "clang/Basic/LLVM.h" +#include "clang/Basic/SourceLocation.h" +#include "clang/Basic/SourceManager.h" #include "clang/Lex/Lexer.h" +#include "clang/StaticAnalyzer/Core/AnalyzerOptions.h" #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" #include "clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState_Fwd.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/SubEngine.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/SMTConstraintManager.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/None.h" +#include "llvm/ADT/Optional.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/SmallPtrSet.h" #include "llvm/ADT/SmallString.h" +#include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringExtras.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/Casting.h" +#include "llvm/Support/ErrorHandling.h" #include "llvm/Support/raw_ostream.h" +#include <cassert> +#include <deque> +#include <memory> +#include <string> +#include <utility> using namespace clang; using namespace ento; -using llvm::FoldingSetNodeID; - //===----------------------------------------------------------------------===// // Utility functions. //===----------------------------------------------------------------------===// bool bugreporter::isDeclRefExprToReference(const Expr *E) { - if (const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(E)) { + if (const auto *DRE = dyn_cast<DeclRefExpr>(E)) return DRE->getDecl()->getType()->isReferenceType(); - } return false; } +static const Expr *peelOffPointerArithmetic(const BinaryOperator *B) { + if (B->isAdditiveOp() && B->getType()->isPointerType()) { + if (B->getLHS()->getType()->isPointerType()) { + return B->getLHS(); + } else if (B->getRHS()->getType()->isPointerType()) { + return B->getRHS(); + } + } + return nullptr; +} + /// Given that expression S represents a pointer that would be dereferenced, /// try to find a sub-expression from which the pointer came from. /// This is used for tracking down origins of a null or undefined value: @@ -55,33 +101,27 @@ bool bugreporter::isDeclRefExprToReference(const Expr *E) { /// x->y.z ==> x (lvalue) /// foo()->y.z ==> foo() (rvalue) const Expr *bugreporter::getDerefExpr(const Stmt *S) { - const Expr *E = dyn_cast<Expr>(S); + const auto *E = dyn_cast<Expr>(S); if (!E) return nullptr; while (true) { - if (const CastExpr *CE = dyn_cast<CastExpr>(E)) { + if (const auto *CE = dyn_cast<CastExpr>(E)) { if (CE->getCastKind() == CK_LValueToRValue) { // This cast represents the load we're looking for. break; } E = CE->getSubExpr(); - } else if (const BinaryOperator *B = dyn_cast<BinaryOperator>(E)) { + } else if (const auto *B = dyn_cast<BinaryOperator>(E)) { // Pointer arithmetic: '*(x + 2)' -> 'x') etc. - if (B->getType()->isPointerType()) { - if (B->getLHS()->getType()->isPointerType()) { - E = B->getLHS(); - } else if (B->getRHS()->getType()->isPointerType()) { - E = B->getRHS(); - } else { - break; - } + if (const Expr *Inner = peelOffPointerArithmetic(B)) { + E = Inner; } else { // Probably more arithmetic can be pattern-matched here, // but for now give up. break; } - } else if (const UnaryOperator *U = dyn_cast<UnaryOperator>(E)) { + } else if (const auto *U = dyn_cast<UnaryOperator>(E)) { if (U->getOpcode() == UO_Deref || U->getOpcode() == UO_AddrOf || (U->isIncrementDecrementOp() && U->getType()->isPointerType())) { // Operators '*' and '&' don't actually mean anything. @@ -94,14 +134,16 @@ const Expr *bugreporter::getDerefExpr(const Stmt *S) { } } // Pattern match for a few useful cases: a[0], p->f, *p etc. - else if (const MemberExpr *ME = dyn_cast<MemberExpr>(E)) { + else if (const auto *ME = dyn_cast<MemberExpr>(E)) { E = ME->getBase(); - } else if (const ObjCIvarRefExpr *IvarRef = dyn_cast<ObjCIvarRefExpr>(E)) { + } else if (const auto *IvarRef = dyn_cast<ObjCIvarRefExpr>(E)) { E = IvarRef->getBase(); - } else if (const ArraySubscriptExpr *AE = dyn_cast<ArraySubscriptExpr>(E)) { + } else if (const auto *AE = dyn_cast<ArraySubscriptExpr>(E)) { E = AE->getBase(); - } else if (const ParenExpr *PE = dyn_cast<ParenExpr>(E)) { + } else if (const auto *PE = dyn_cast<ParenExpr>(E)) { E = PE->getSubExpr(); + } else if (const auto *EWC = dyn_cast<ExprWithCleanups>(E)) { + E = EWC->getSubExpr(); } else { // Other arbitrary stuff. break; @@ -111,7 +153,7 @@ const Expr *bugreporter::getDerefExpr(const Stmt *S) { // Special case: remove the final lvalue-to-rvalue cast, but do not recurse // deeper into the sub-expression. This way we return the lvalue from which // our pointer rvalue was loaded. - if (const ImplicitCastExpr *CE = dyn_cast<ImplicitCastExpr>(E)) + if (const auto *CE = dyn_cast<ImplicitCastExpr>(E)) if (CE->getCastKind() == CK_LValueToRValue) E = CE->getSubExpr(); @@ -120,14 +162,14 @@ const Expr *bugreporter::getDerefExpr(const Stmt *S) { const Stmt *bugreporter::GetDenomExpr(const ExplodedNode *N) { const Stmt *S = N->getLocationAs<PreStmt>()->getStmt(); - if (const BinaryOperator *BE = dyn_cast<BinaryOperator>(S)) + if (const auto *BE = dyn_cast<BinaryOperator>(S)) return BE->getRHS(); return nullptr; } const Stmt *bugreporter::GetRetValExpr(const ExplodedNode *N) { const Stmt *S = N->getLocationAs<PostStmt>()->getStmt(); - if (const ReturnStmt *RS = dyn_cast<ReturnStmt>(S)) + if (const auto *RS = dyn_cast<ReturnStmt>(S)) return RS->getRetValue(); return nullptr; } @@ -136,13 +178,18 @@ const Stmt *bugreporter::GetRetValExpr(const ExplodedNode *N) { // Definitions for bug reporter visitors. //===----------------------------------------------------------------------===// -std::unique_ptr<PathDiagnosticPiece> +std::shared_ptr<PathDiagnosticPiece> BugReporterVisitor::getEndPath(BugReporterContext &BRC, const ExplodedNode *EndPathNode, BugReport &BR) { return nullptr; } -std::unique_ptr<PathDiagnosticPiece> BugReporterVisitor::getDefaultEndPath( +void +BugReporterVisitor::finalizeVisitor(BugReporterContext &BRC, + const ExplodedNode *EndPathNode, + BugReport &BR) {} + +std::shared_ptr<PathDiagnosticPiece> BugReporterVisitor::getDefaultEndPath( BugReporterContext &BRC, const ExplodedNode *EndPathNode, BugReport &BR) { PathDiagnosticLocation L = PathDiagnosticLocation::createEndOfPath(EndPathNode,BRC.getSourceManager()); @@ -151,16 +198,465 @@ std::unique_ptr<PathDiagnosticPiece> BugReporterVisitor::getDefaultEndPath( // Only add the statement itself as a range if we didn't specify any // special ranges for this report. - auto P = llvm::make_unique<PathDiagnosticEventPiece>( + auto P = std::make_shared<PathDiagnosticEventPiece>( L, BR.getDescription(), Ranges.begin() == Ranges.end()); for (SourceRange Range : Ranges) P->addRange(Range); - return std::move(P); + return P; +} + +/// \return name of the macro inside the location \p Loc. +static StringRef getMacroName(SourceLocation Loc, + BugReporterContext &BRC) { + return Lexer::getImmediateMacroName( + Loc, + BRC.getSourceManager(), + BRC.getASTContext().getLangOpts()); +} + +/// \return Whether given spelling location corresponds to an expansion +/// of a function-like macro. +static bool isFunctionMacroExpansion(SourceLocation Loc, + const SourceManager &SM) { + if (!Loc.isMacroID()) + return false; + while (SM.isMacroArgExpansion(Loc)) + Loc = SM.getImmediateExpansionRange(Loc).getBegin(); + std::pair<FileID, unsigned> TLInfo = SM.getDecomposedLoc(Loc); + SrcMgr::SLocEntry SE = SM.getSLocEntry(TLInfo.first); + const SrcMgr::ExpansionInfo &EInfo = SE.getExpansion(); + return EInfo.isFunctionMacroExpansion(); +} + +/// \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) { + ProgramStateRef State = N->getState(); + ProgramStateManager &Mgr = N->getState()->getStateManager(); + + 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())) + return true; + + // SVal after the state is possibly different. + SVal ValueAtN = N->getState()->getSVal(RegionOfInterest); + if (!Mgr.getSValBuilder().areEqual(State, ValueAtN, ValueAfter).isConstrainedTrue() && + (!ValueAtN.isUndef() || !ValueAfter.isUndef())) + return true; + + return false; } namespace { + +/// Put a diagnostic on return statement of all inlined functions +/// for which the region of interest \p RegionOfInterest was passed into, +/// but not written inside, and it has caused an undefined read or a null +/// pointer dereference outside. +class NoStoreFuncVisitor final : public BugReporterVisitor { + const SubRegion *RegionOfInterest; + static constexpr const char *DiagnosticsMsg = + "Returning without writing to '"; + + /// Frames writing into \c RegionOfInterest. + /// This visitor generates a note only if a function does not write into + /// a region of interest. This information is not immediately available + /// by looking at the node associated with the exit from the function + /// (usually the return statement). To avoid recomputing the same information + /// many times (going up the path for each node and checking whether the + /// region was written into) we instead lazily compute the + /// stack frames along the path which write into the region of interest. + llvm::SmallPtrSet<const StackFrameContext *, 32> FramesModifyingRegion; + llvm::SmallPtrSet<const StackFrameContext *, 32> FramesModifyingCalculated; + +public: + NoStoreFuncVisitor(const SubRegion *R) : RegionOfInterest(R) {} + + void Profile(llvm::FoldingSetNodeID &ID) const override { + static int Tag = 0; + ID.AddPointer(&Tag); + } + + std::shared_ptr<PathDiagnosticPiece> VisitNode(const ExplodedNode *N, + const ExplodedNode *PrevN, + BugReporterContext &BRC, + BugReport &BR) override { + + const LocationContext *Ctx = N->getLocationContext(); + const StackFrameContext *SCtx = Ctx->getStackFrame(); + ProgramStateRef State = N->getState(); + auto CallExitLoc = N->getLocationAs<CallExitBegin>(); + + // No diagnostic if region was modified inside the frame. + if (!CallExitLoc) + return nullptr; + + CallEventRef<> Call = + BRC.getStateManager().getCallEventManager().getCaller(SCtx, State); + const PrintingPolicy &PP = BRC.getASTContext().getPrintingPolicy(); + const SourceManager &SM = BRC.getSourceManager(); + + // 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)) + if (potentiallyWritesIntoIvar(Call->getRuntimeDefinition().getDecl(), + IvarR->getDecl()) && + !isRegionOfInterestModifiedInFrame(N)) + return notModifiedMemberDiagnostics( + Ctx, SM, PP, *CallExitLoc, Call, + MC->getReceiverSVal().getAsRegion()); + + if (const auto *CCall = dyn_cast<CXXConstructorCall>(Call)) { + const MemRegion *ThisRegion = CCall->getCXXThisVal().getAsRegion(); + if (RegionOfInterest->isSubRegionOf(ThisRegion) + && !CCall->getDecl()->isImplicit() + && !isRegionOfInterestModifiedInFrame(N)) + return notModifiedMemberDiagnostics(Ctx, SM, PP, *CallExitLoc, + CCall, ThisRegion); + } + + 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); + unsigned IndirectionLevel = 1; + QualType T = PVD->getType(); + while (const MemRegion *R = S.getAsRegion()) { + if (RegionOfInterest->isSubRegionOf(R) + && !isPointerToConst(PVD->getType())) { + + if (isRegionOfInterestModifiedInFrame(N)) + return nullptr; + + return notModifiedParameterDiagnostics( + Ctx, SM, PP, *CallExitLoc, Call, PVD, R, IndirectionLevel); + } + QualType PT = T->getPointeeType(); + if (PT.isNull() || PT->isVoidType()) break; + S = State->getSVal(R, PT); + T = PT; + IndirectionLevel++; + } + } + + return nullptr; + } + +private: + + /// \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; + if (!Parent || !Parent->getBody()) + return false; + StatementMatcher WriteIntoIvarM = binaryOperator( + hasOperatorName("="), hasLHS(ignoringParenImpCasts(objcIvarRefExpr( + hasDeclaration(equalsNode(Ivar)))))); + StatementMatcher ParentM = stmt(hasDescendant(WriteIntoIvarM)); + auto Matches = match(ParentM, *Parent->getBody(), Parent->getASTContext()); + return !Matches.empty(); + } + + /// 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); + } + + + /// 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(); + + 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(); + } + } + + // Stop calculation at the call to the current function. + if (auto CE = N->getLocationAs<CallEnter>()) + if (CE->getCalleeContext() == OriginalSCtx) + break; + + N = N->getFirstPred(); + } while (N); + } + + /// 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(); + + return Call->parameters(); + } + + /// \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(); + } + + /// \return Diagnostics piece for the member field not modified + /// in a given function. + std::shared_ptr<PathDiagnosticPiece> notModifiedMemberDiagnostics( + const LocationContext *Ctx, + const SourceManager &SM, + const PrintingPolicy &PP, + CallExitBegin &CallExitLoc, + CallEventRef<> Call, + const MemRegion *ArgRegion) { + const char *TopRegionName = isa<ObjCMethodCall>(Call) ? "self" : "this"; + SmallString<256> sbuf; + llvm::raw_svector_ostream os(sbuf); + os << DiagnosticsMsg; + bool out = prettyPrintRegionName(TopRegionName, "->", /*IsReference=*/true, + /*IndirectionLevel=*/1, ArgRegion, os, PP); + + // Return nothing if we have failed to pretty-print. + if (!out) + return nullptr; + + os << "'"; + PathDiagnosticLocation L = + getPathDiagnosticLocation(CallExitLoc.getReturnStmt(), SM, Ctx, Call); + return std::make_shared<PathDiagnosticEventPiece>(L, os.str()); + } + + /// \return Diagnostics piece for the parameter \p PVD not modified + /// in a given function. + /// \p IndirectionLevel How many times \c ArgRegion has to be dereferenced + /// before we get to the super region of \c RegionOfInterest + std::shared_ptr<PathDiagnosticPiece> + notModifiedParameterDiagnostics(const LocationContext *Ctx, + const SourceManager &SM, + const PrintingPolicy &PP, + CallExitBegin &CallExitLoc, + CallEventRef<> Call, + const ParmVarDecl *PVD, + const MemRegion *ArgRegion, + unsigned IndirectionLevel) { + PathDiagnosticLocation L = getPathDiagnosticLocation( + CallExitLoc.getReturnStmt(), SM, Ctx, Call); + SmallString<256> sbuf; + llvm::raw_svector_ostream os(sbuf); + os << DiagnosticsMsg; + bool IsReference = PVD->getType()->isReferenceType(); + const char *Sep = IsReference && IndirectionLevel == 1 ? "." : "->"; + bool Success = prettyPrintRegionName( + PVD->getQualifiedNameAsString().c_str(), + Sep, IsReference, IndirectionLevel, ArgRegion, os, PP); + + // Print the parameter name if the pretty-printing has failed. + if (!Success) + PVD->printQualifiedName(os); + os << "'"; + return std::make_shared<PathDiagnosticEventPiece>(L, os.str()); + } + + /// \return a path diagnostic location for the optionally + /// present return statement \p RS. + PathDiagnosticLocation getPathDiagnosticLocation(const ReturnStmt *RS, + const SourceManager &SM, + const LocationContext *Ctx, + CallEventRef<> Call) { + if (RS) + return PathDiagnosticLocation::createBegin(RS, SM, Ctx); + return PathDiagnosticLocation( + Call->getRuntimeDefinition().getDecl()->getSourceRange().getEnd(), SM); + } + + /// Pretty-print region \p ArgRegion starting from parent to \p os. + /// \return whether printing has succeeded + bool prettyPrintRegionName(StringRef TopRegionName, + StringRef Sep, + bool IsReference, + int IndirectionLevel, + const MemRegion *ArgRegion, + llvm::raw_svector_ostream &os, + const PrintingPolicy &PP) { + SmallVector<const MemRegion *, 5> Subregions; + const MemRegion *R = RegionOfInterest; + while (R != ArgRegion) { + if (!(isa<FieldRegion>(R) || isa<CXXBaseObjectRegion>(R) || + isa<ObjCIvarRegion>(R))) + return false; // Pattern-matching failed. + Subregions.push_back(R); + R = cast<SubRegion>(R)->getSuperRegion(); + } + bool IndirectReference = !Subregions.empty(); + + if (IndirectReference) + IndirectionLevel--; // Due to "->" symbol. + + if (IsReference) + IndirectionLevel--; // Due to reference semantics. + + bool ShouldSurround = IndirectReference && IndirectionLevel > 0; + + if (ShouldSurround) + os << "("; + for (int i = 0; i < IndirectionLevel; i++) + os << "*"; + os << TopRegionName; + if (ShouldSurround) + os << ")"; + + for (auto I = Subregions.rbegin(), E = Subregions.rend(); I != E; ++I) { + if (const auto *FR = dyn_cast<FieldRegion>(*I)) { + os << Sep; + FR->getDecl()->getDeclName().print(os, PP); + Sep = "."; + } else if (const auto *IR = dyn_cast<ObjCIvarRegion>(*I)) { + os << "->"; + IR->getDecl()->getDeclName().print(os, PP); + Sep = "."; + } else if (isa<CXXBaseObjectRegion>(*I)) { + continue; // Just keep going up to the base region. + } else { + llvm_unreachable("Previous check has missed an unexpected region"); + } + } + return true; + } +}; + +/// Suppress null-pointer-dereference bugs where dereferenced null was returned +/// the macro. +class MacroNullReturnSuppressionVisitor final : public BugReporterVisitor { + const SubRegion *RegionOfInterest; + const SVal ValueAtDereference; + + // Do not invalidate the reports where the value was modified + // after it got assigned to from the macro. + bool WasModified = false; + +public: + MacroNullReturnSuppressionVisitor(const SubRegion *R, + const SVal V) : RegionOfInterest(R), + ValueAtDereference(V) {} + + std::shared_ptr<PathDiagnosticPiece> VisitNode(const ExplodedNode *N, + const ExplodedNode *PrevN, + BugReporterContext &BRC, + BugReport &BR) override { + if (WasModified) + return nullptr; + + auto BugPoint = BR.getErrorNode()->getLocation().getAs<StmtPoint>(); + if (!BugPoint) + return nullptr; + + const SourceManager &SMgr = BRC.getSourceManager(); + if (auto Loc = matchAssignment(N, BRC)) { + if (isFunctionMacroExpansion(*Loc, SMgr)) { + std::string MacroName = getMacroName(*Loc, BRC); + SourceLocation BugLoc = BugPoint->getStmt()->getLocStart(); + if (!BugLoc.isMacroID() || getMacroName(BugLoc, BRC) != MacroName) + BR.markInvalid(getTag(), MacroName.c_str()); + } + } + + if (wasRegionOfInterestModifiedAt(RegionOfInterest, N, ValueAtDereference)) + WasModified = true; + + return nullptr; + } + + static void addMacroVisitorIfNecessary( + const ExplodedNode *N, const MemRegion *R, + bool EnableNullFPSuppression, BugReport &BR, + const SVal V) { + AnalyzerOptions &Options = N->getState()->getStateManager() + .getOwningEngine()->getAnalysisManager().options; + if (EnableNullFPSuppression && Options.shouldSuppressNullReturnPaths() + && V.getAs<Loc>()) + BR.addVisitor(llvm::make_unique<MacroNullReturnSuppressionVisitor>( + R->getAs<SubRegion>(), V)); + } + + void* getTag() const { + static int Tag = 0; + return static_cast<void *>(&Tag); + } + + void Profile(llvm::FoldingSetNodeID &ID) const override { + ID.AddPointer(getTag()); + } + +private: + /// \return Source location of right hand side of an assignment + /// into \c RegionOfInterest, empty optional if none found. + Optional<SourceLocation> matchAssignment(const ExplodedNode *N, + BugReporterContext &BRC) { + const Stmt *S = PathDiagnosticLocation::getStmt(N); + ProgramStateRef State = N->getState(); + auto *LCtx = N->getLocationContext(); + if (!S) + return None; + + if (const auto *DS = dyn_cast<DeclStmt>(S)) { + if (const auto *VD = dyn_cast<VarDecl>(DS->getSingleDecl())) + if (const Expr *RHS = VD->getInit()) + if (RegionOfInterest->isSubRegionOf( + State->getLValue(VD, LCtx).getAsRegion())) + return RHS->getLocStart(); + } else if (const auto *BO = dyn_cast<BinaryOperator>(S)) { + const MemRegion *R = N->getSVal(BO->getLHS()).getAsRegion(); + const Expr *RHS = BO->getRHS(); + if (BO->isAssignmentOp() && RegionOfInterest->isSubRegionOf(R)) { + return RHS->getLocStart(); + } + } + return None; + } +}; + /// 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, @@ -168,19 +664,20 @@ namespace { /// /// This visitor is intended to be used when another visitor discovers that an /// interesting value comes from an inlined function call. -class ReturnVisitor : public BugReporterVisitorImpl<ReturnVisitor> { +class ReturnVisitor : public BugReporterVisitor { const StackFrameContext *StackFrame; enum { Initial, MaybeUnsuppress, Satisfied - } Mode; + } Mode = Initial; bool EnableNullFPSuppression; + bool ShouldInvalidate = true; public: ReturnVisitor(const StackFrameContext *Frame, bool Suppressed) - : StackFrame(Frame), Mode(Initial), EnableNullFPSuppression(Suppressed) {} + : StackFrame(Frame), EnableNullFPSuppression(Suppressed) {} static void *getTag() { static int Tag = 0; @@ -235,7 +732,7 @@ public: // Check the return value. ProgramStateRef State = Node->getState(); - SVal RetVal = State->getSVal(S, Node->getLocationContext()); + SVal RetVal = Node->getSVal(S); // Handle cases where a reference is returned and then immediately used. if (cast<Expr>(S)->isGLValue()) @@ -274,7 +771,7 @@ public: if (!SP) return nullptr; - const ReturnStmt *Ret = dyn_cast<ReturnStmt>(SP->getStmt()); + const auto *Ret = dyn_cast<ReturnStmt>(SP->getStmt()); if (!Ret) return nullptr; @@ -329,8 +826,7 @@ public: // If we have counter-suppression enabled, make sure we keep visiting // future nodes. We want to emit a path note as well, in case // the report is resurrected as valid later on. - ExprEngine &Eng = BRC.getBugReporter().getEngine(); - AnalyzerOptions &Options = Eng.getAnalysisManager().options; + AnalyzerOptions &Options = BRC.getAnalyzerOptions(); if (EnableNullFPSuppression && hasCounterSuppression(Options)) Mode = MaybeUnsuppress; @@ -352,8 +848,8 @@ public: } } else { // FIXME: We should have a more generalized location printing mechanism. - if (const DeclRefExpr *DR = dyn_cast<DeclRefExpr>(RetE)) - if (const DeclaratorDecl *DD = dyn_cast<DeclaratorDecl>(DR->getDecl())) + if (const auto *DR = dyn_cast<DeclRefExpr>(RetE)) + if (const auto *DD = dyn_cast<DeclaratorDecl>(DR->getDecl())) Out << " (loaded from '" << *DD << "')"; } @@ -368,8 +864,7 @@ public: visitNodeMaybeUnsuppress(const ExplodedNode *N, const ExplodedNode *PrevN, BugReporterContext &BRC, BugReport &BR) { #ifndef NDEBUG - ExprEngine &Eng = BRC.getBugReporter().getEngine(); - AnalyzerOptions &Options = Eng.getAnalysisManager().options; + AnalyzerOptions &Options = BRC.getAnalyzerOptions(); assert(hasCounterSuppression(Options)); #endif @@ -406,7 +901,7 @@ public: if (bugreporter::trackNullOrUndefValue(N, ArgE, BR, /*IsArg=*/true, EnableNullFPSuppression)) - BR.removeInvalidation(ReturnVisitor::getTag(), StackFrame); + ShouldInvalidate = false; // If we /can't/ track the null pointer, we should err on the side of // false negatives, and continue towards marking this report invalid. @@ -432,18 +927,16 @@ public: llvm_unreachable("Invalid visit mode!"); } - std::unique_ptr<PathDiagnosticPiece> getEndPath(BugReporterContext &BRC, - const ExplodedNode *N, - BugReport &BR) override { - if (EnableNullFPSuppression) + void finalizeVisitor(BugReporterContext &BRC, const ExplodedNode *N, + BugReport &BR) override { + if (EnableNullFPSuppression && ShouldInvalidate) BR.markInvalid(ReturnVisitor::getTag(), StackFrame); - return nullptr; } }; -} // end anonymous namespace +} // namespace -void FindLastStoreBRVisitor ::Profile(llvm::FoldingSetNodeID &ID) const { +void FindLastStoreBRVisitor::Profile(llvm::FoldingSetNodeID &ID) const { static int tag = 0; ID.AddPointer(&tag); ID.AddPointer(R); @@ -466,7 +959,7 @@ static bool isInitializationOfVar(const ExplodedNode *N, const VarRegion *VR) { return false; const MemSpaceRegion *VarSpace = VR->getMemorySpace(); - const StackSpaceRegion *FrameSpace = dyn_cast<StackSpaceRegion>(VarSpace); + const auto *FrameSpace = dyn_cast<StackSpaceRegion>(VarSpace); if (!FrameSpace) { // If we ever directly evaluate global DeclStmts, this assertion will be // invalid, but this still seems preferable to silently accepting an @@ -477,14 +970,131 @@ static bool isInitializationOfVar(const ExplodedNode *N, const VarRegion *VR) { assert(VR->getDecl()->hasLocalStorage()); const LocationContext *LCtx = N->getLocationContext(); - return FrameSpace->getStackFrame() == LCtx->getCurrentStackFrame(); + return FrameSpace->getStackFrame() == LCtx->getStackFrame(); +} + +/// Show diagnostics for initializing or declaring a region \p R with a bad value. +static void showBRDiagnostics(const char *action, llvm::raw_svector_ostream &os, + const MemRegion *R, SVal V, const DeclStmt *DS) { + if (R->canPrintPretty()) { + R->printPretty(os); + os << " "; + } + + if (V.getAs<loc::ConcreteInt>()) { + bool b = false; + if (R->isBoundable()) { + if (const auto *TR = dyn_cast<TypedValueRegion>(R)) { + if (TR->getValueType()->isObjCObjectPointerType()) { + os << action << "nil"; + b = true; + } + } + } + if (!b) + os << action << "a null pointer value"; + + } else if (auto CVal = V.getAs<nonloc::ConcreteInt>()) { + os << action << CVal->getValue(); + } else if (DS) { + if (V.isUndef()) { + if (isa<VarRegion>(R)) { + const auto *VD = cast<VarDecl>(DS->getSingleDecl()); + if (VD->getInit()) { + os << (R->canPrintPretty() ? "initialized" : "Initializing") + << " to a garbage value"; + } else { + os << (R->canPrintPretty() ? "declared" : "Declaring") + << " without an initial value"; + } + } + } else { + os << (R->canPrintPretty() ? "initialized" : "Initialized") + << " here"; + } + } +} + +/// Display diagnostics for passing bad region as a parameter. +static void showBRParamDiagnostics(llvm::raw_svector_ostream& os, + const VarRegion *VR, + SVal V) { + const auto *Param = cast<ParmVarDecl>(VR->getDecl()); + + os << "Passing "; + + if (V.getAs<loc::ConcreteInt>()) { + if (Param->getType()->isObjCObjectPointerType()) + os << "nil object reference"; + else + os << "null pointer value"; + } else if (V.isUndef()) { + os << "uninitialized value"; + } else if (auto CI = V.getAs<nonloc::ConcreteInt>()) { + os << "the value " << CI->getValue(); + } else { + os << "value"; + } + + // Printed parameter indexes are 1-based, not 0-based. + unsigned Idx = Param->getFunctionScopeIndex() + 1; + os << " via " << Idx << llvm::getOrdinalSuffix(Idx) << " parameter"; + if (VR->canPrintPretty()) { + os << " "; + VR->printPretty(os); + } +} + +/// Show default diagnostics for storing bad region. +static void showBRDefaultDiagnostics(llvm::raw_svector_ostream& os, + const MemRegion *R, + SVal V) { + if (V.getAs<loc::ConcreteInt>()) { + bool b = false; + if (R->isBoundable()) { + if (const auto *TR = dyn_cast<TypedValueRegion>(R)) { + if (TR->getValueType()->isObjCObjectPointerType()) { + os << "nil object reference stored"; + b = true; + } + } + } + if (!b) { + if (R->canPrintPretty()) + os << "Null pointer value stored"; + else + os << "Storing null pointer value"; + } + + } else if (V.isUndef()) { + if (R->canPrintPretty()) + os << "Uninitialized value stored"; + else + os << "Storing uninitialized value"; + + } else if (auto CV = V.getAs<nonloc::ConcreteInt>()) { + if (R->canPrintPretty()) + os << "The value " << CV->getValue() << " is assigned"; + else + os << "Assigning " << CV->getValue(); + + } else { + if (R->canPrintPretty()) + os << "Value assigned"; + else + os << "Assigning value"; + } + + if (R->canPrintPretty()) { + os << " to "; + R->printPretty(os); + } } std::shared_ptr<PathDiagnosticPiece> FindLastStoreBRVisitor::VisitNode(const ExplodedNode *Succ, const ExplodedNode *Pred, BugReporterContext &BRC, BugReport &BR) { - if (Satisfied) return nullptr; @@ -493,7 +1103,7 @@ FindLastStoreBRVisitor::VisitNode(const ExplodedNode *Succ, bool IsParam = false; // First see if we reached the declaration of the region. - if (const VarRegion *VR = dyn_cast<VarRegion>(R)) { + if (const auto *VR = dyn_cast<VarRegion>(R)) { if (isInitializationOfVar(Pred, VR)) { StoreSite = Pred; InitE = VR->getDecl()->getInit(); @@ -539,8 +1149,8 @@ FindLastStoreBRVisitor::VisitNode(const ExplodedNode *Succ, // 'this' should never be NULL, but this visitor isn't just for NULL and // UndefinedVal.) if (Optional<CallEnter> CE = Succ->getLocationAs<CallEnter>()) { - if (const VarRegion *VR = dyn_cast<VarRegion>(R)) { - const ParmVarDecl *Param = cast<ParmVarDecl>(VR->getDecl()); + if (const auto *VR = dyn_cast<VarRegion>(R)) { + const auto *Param = cast<ParmVarDecl>(VR->getDecl()); ProgramStateManager &StateMgr = BRC.getStateManager(); CallEventManager &CallMgr = StateMgr.getCallEventManager(); @@ -554,7 +1164,7 @@ FindLastStoreBRVisitor::VisitNode(const ExplodedNode *Succ, // If this is a CXXTempObjectRegion, the Expr responsible for its creation // is wrapped inside of it. - if (const CXXTempObjectRegion *TmpR = dyn_cast<CXXTempObjectRegion>(R)) + if (const auto *TmpR = dyn_cast<CXXTempObjectRegion>(R)) InitE = TmpR->getExpr(); } @@ -584,8 +1194,8 @@ FindLastStoreBRVisitor::VisitNode(const ExplodedNode *Succ, if (Optional<PostStmt> PS = StoreSite->getLocationAs<PostStmt>()) { const Stmt *S = PS->getStmt(); const char *action = nullptr; - const DeclStmt *DS = dyn_cast<DeclStmt>(S); - const VarRegion *VR = dyn_cast<VarRegion>(R); + const auto *DS = dyn_cast<DeclStmt>(S); + const auto *VR = dyn_cast<VarRegion>(R); if (DS) { action = R->canPrintPretty() ? "initialized to " : @@ -596,8 +1206,8 @@ FindLastStoreBRVisitor::VisitNode(const ExplodedNode *Succ, if (VR) { // See if we can get the BlockVarRegion. ProgramStateRef State = StoreSite->getState(); - SVal V = State->getSVal(S, PS->getLocationContext()); - if (const BlockDataRegion *BDR = + SVal V = StoreSite->getSVal(S); + if (const auto *BDR = dyn_cast_or_null<BlockDataRegion>(V.getAsRegion())) { if (const VarRegion *OriginalR = BDR->getOriginalRegion(VR)) { if (Optional<KnownSVal> KV = @@ -608,122 +1218,16 @@ FindLastStoreBRVisitor::VisitNode(const ExplodedNode *Succ, } } } + if (action) + showBRDiagnostics(action, os, R, V, DS); - if (action) { - if (R->canPrintPretty()) { - R->printPretty(os); - os << " "; - } - - if (V.getAs<loc::ConcreteInt>()) { - bool b = false; - if (R->isBoundable()) { - if (const TypedValueRegion *TR = dyn_cast<TypedValueRegion>(R)) { - if (TR->getValueType()->isObjCObjectPointerType()) { - os << action << "nil"; - b = true; - } - } - } - - if (!b) - os << action << "a null pointer value"; - } else if (Optional<nonloc::ConcreteInt> CVal = - V.getAs<nonloc::ConcreteInt>()) { - os << action << CVal->getValue(); - } - else if (DS) { - if (V.isUndef()) { - if (isa<VarRegion>(R)) { - const VarDecl *VD = cast<VarDecl>(DS->getSingleDecl()); - if (VD->getInit()) { - os << (R->canPrintPretty() ? "initialized" : "Initializing") - << " to a garbage value"; - } else { - os << (R->canPrintPretty() ? "declared" : "Declaring") - << " without an initial value"; - } - } - } - else { - os << (R->canPrintPretty() ? "initialized" : "Initialized") - << " here"; - } - } - } } else if (StoreSite->getLocation().getAs<CallEnter>()) { - if (const VarRegion *VR = dyn_cast<VarRegion>(R)) { - const ParmVarDecl *Param = cast<ParmVarDecl>(VR->getDecl()); - - os << "Passing "; - - if (V.getAs<loc::ConcreteInt>()) { - if (Param->getType()->isObjCObjectPointerType()) - os << "nil object reference"; - else - os << "null pointer value"; - } else if (V.isUndef()) { - os << "uninitialized value"; - } else if (Optional<nonloc::ConcreteInt> CI = - V.getAs<nonloc::ConcreteInt>()) { - os << "the value " << CI->getValue(); - } else { - os << "value"; - } - - // Printed parameter indexes are 1-based, not 0-based. - unsigned Idx = Param->getFunctionScopeIndex() + 1; - os << " via " << Idx << llvm::getOrdinalSuffix(Idx) << " parameter"; - if (R->canPrintPretty()) { - os << " "; - R->printPretty(os); - } - } + if (const auto *VR = dyn_cast<VarRegion>(R)) + showBRParamDiagnostics(os, VR, V); } - if (os.str().empty()) { - if (V.getAs<loc::ConcreteInt>()) { - bool b = false; - if (R->isBoundable()) { - if (const TypedValueRegion *TR = dyn_cast<TypedValueRegion>(R)) { - if (TR->getValueType()->isObjCObjectPointerType()) { - os << "nil object reference stored"; - b = true; - } - } - } - if (!b) { - if (R->canPrintPretty()) - os << "Null pointer value stored"; - else - os << "Storing null pointer value"; - } - - } else if (V.isUndef()) { - if (R->canPrintPretty()) - os << "Uninitialized value stored"; - else - os << "Storing uninitialized value"; - - } else if (Optional<nonloc::ConcreteInt> CV = - V.getAs<nonloc::ConcreteInt>()) { - if (R->canPrintPretty()) - os << "The value " << CV->getValue() << " is assigned"; - else - os << "Assigning " << CV->getValue(); - - } else { - if (R->canPrintPretty()) - os << "Value assigned"; - else - os << "Assigning value"; - } - - if (R->canPrintPretty()) { - os << " to "; - R->printPretty(os); - } - } + if (os.str().empty()) + showBRDefaultDiagnostics(os, R, V); // Construct a new PathDiagnosticPiece. ProgramPoint P = StoreSite->getLocation(); @@ -778,7 +1282,6 @@ TrackConstraintBRVisitor::VisitNode(const ExplodedNode *N, // Check if in the previous state it was feasible for this constraint // to *not* be true. if (isUnderconstrained(PrevN)) { - IsSatisfied = true; // As a sanity check, make sure that the negation of the constraint @@ -816,20 +1319,20 @@ TrackConstraintBRVisitor::VisitNode(const ExplodedNode *N, SuppressInlineDefensiveChecksVisitor:: SuppressInlineDefensiveChecksVisitor(DefinedSVal Value, const ExplodedNode *N) - : V(Value), IsSatisfied(false), IsTrackingTurnedOn(false) { - - // Check if the visitor is disabled. - SubEngine *Eng = N->getState()->getStateManager().getOwningEngine(); - assert(Eng && "Cannot file a bug report without an owning engine"); - AnalyzerOptions &Options = Eng->getAnalysisManager().options; - if (!Options.shouldSuppressInlinedDefensiveChecks()) - IsSatisfied = true; + : V(Value) { + // Check if the visitor is disabled. + SubEngine *Eng = N->getState()->getStateManager().getOwningEngine(); + assert(Eng && "Cannot file a bug report without an owning engine"); + AnalyzerOptions &Options = Eng->getAnalysisManager().options; + if (!Options.shouldSuppressInlinedDefensiveChecks()) + IsSatisfied = true; - assert(N->getState()->isNull(V).isConstrainedTrue() && - "The visitor only tracks the cases where V is constrained to 0"); + assert(N->getState()->isNull(V).isConstrainedTrue() && + "The visitor only tracks the cases where V is constrained to 0"); } -void SuppressInlineDefensiveChecksVisitor::Profile(FoldingSetNodeID &ID) const { +void SuppressInlineDefensiveChecksVisitor::Profile( + llvm::FoldingSetNodeID &ID) const { static int id = 0; ID.AddPointer(&id); ID.Add(V); @@ -878,10 +1381,6 @@ SuppressInlineDefensiveChecksVisitor::VisitNode(const ExplodedNode *Succ, if (!BugPoint) return nullptr; - SourceLocation BugLoc = BugPoint->getStmt()->getLocStart(); - if (BugLoc.isMacroID()) - return nullptr; - ProgramPoint CurPoint = Succ->getLocation(); const Stmt *CurTerminatorStmt = nullptr; if (auto BE = CurPoint.getAs<BlockEdge>()) { @@ -902,14 +1401,14 @@ SuppressInlineDefensiveChecksVisitor::VisitNode(const ExplodedNode *Succ, SourceLocation TerminatorLoc = CurTerminatorStmt->getLocStart(); if (TerminatorLoc.isMacroID()) { - const SourceManager &SMgr = BRC.getSourceManager(); - std::pair<FileID, unsigned> TLInfo = SMgr.getDecomposedLoc(TerminatorLoc); - SrcMgr::SLocEntry SE = SMgr.getSLocEntry(TLInfo.first); - const SrcMgr::ExpansionInfo &EInfo = SE.getExpansion(); - if (EInfo.isFunctionMacroExpansion()) { + SourceLocation BugLoc = BugPoint->getStmt()->getLocStart(); + + // Suppress reports unless we are in that same macro. + if (!BugLoc.isMacroID() || + getMacroName(BugLoc, BRC) != getMacroName(TerminatorLoc, BRC)) { BR.markInvalid("Suppress Macro IDC", CurLC); - return nullptr; } + return nullptr; } } return nullptr; @@ -917,8 +1416,8 @@ SuppressInlineDefensiveChecksVisitor::VisitNode(const ExplodedNode *Succ, static const MemRegion *getLocationRegionIfReference(const Expr *E, const ExplodedNode *N) { - if (const DeclRefExpr *DR = dyn_cast<DeclRefExpr>(E)) { - if (const VarDecl *VD = dyn_cast<VarDecl>(DR->getDecl())) { + if (const auto *DR = dyn_cast<DeclRefExpr>(E)) { + if (const auto *VD = dyn_cast<VarDecl>(DR->getDecl())) { if (!VD->getType()->isReferenceType()) return nullptr; ProgramStateManager &StateMgr = N->getState()->getStateManager(); @@ -939,12 +1438,12 @@ static const MemRegion *getLocationRegionIfReference(const Expr *E, static const Expr *peelOffOuterExpr(const Expr *Ex, const ExplodedNode *N) { Ex = Ex->IgnoreParenCasts(); - if (const ExprWithCleanups *EWC = dyn_cast<ExprWithCleanups>(Ex)) + if (const auto *EWC = dyn_cast<ExprWithCleanups>(Ex)) return peelOffOuterExpr(EWC->getSubExpr(), N); - if (const OpaqueValueExpr *OVE = dyn_cast<OpaqueValueExpr>(Ex)) + if (const auto *OVE = dyn_cast<OpaqueValueExpr>(Ex)) return peelOffOuterExpr(OVE->getSourceExpr(), N); - if (auto *POE = dyn_cast<PseudoObjectExpr>(Ex)) { - auto *PropRef = dyn_cast<ObjCPropertyRefExpr>(POE->getSyntacticForm()); + if (const auto *POE = dyn_cast<PseudoObjectExpr>(Ex)) { + const auto *PropRef = dyn_cast<ObjCPropertyRefExpr>(POE->getSyntacticForm()); if (PropRef && PropRef->isMessagingGetter()) { const Expr *GetterMessageSend = POE->getSemanticExpr(POE->getNumSemanticExprs() - 1); @@ -954,7 +1453,7 @@ static const Expr *peelOffOuterExpr(const Expr *Ex, } // Peel off the ternary operator. - if (const ConditionalOperator *CO = dyn_cast<ConditionalOperator>(Ex)) { + if (const auto *CO = dyn_cast<ConditionalOperator>(Ex)) { // Find a node where the branching occurred and find out which branch // we took (true/false) by looking at the ExplodedGraph. const ExplodedNode *NI = N; @@ -975,6 +1474,68 @@ static const Expr *peelOffOuterExpr(const Expr *Ex, NI = NI->getFirstPred(); } while (NI); } + + if (auto *BO = dyn_cast<BinaryOperator>(Ex)) + if (const Expr *SubEx = peelOffPointerArithmetic(BO)) + return peelOffOuterExpr(SubEx, N); + + return Ex; +} + +/// Walk through nodes until we get one that matches the statement exactly. +/// Alternately, if we hit a known lvalue for the statement, we know we've +/// gone too far (though we can likely track the lvalue better anyway). +static const ExplodedNode* findNodeForStatement(const ExplodedNode *N, + const Stmt *S, + const Expr *Inner) { + do { + const ProgramPoint &pp = N->getLocation(); + if (auto ps = pp.getAs<StmtPoint>()) { + if (ps->getStmt() == S || ps->getStmt() == Inner) + break; + } else if (auto CEE = pp.getAs<CallExitEnd>()) { + if (CEE->getCalleeContext()->getCallSite() == S || + CEE->getCalleeContext()->getCallSite() == Inner) + break; + } + N = N->getFirstPred(); + } while (N); + return N; +} + +/// Find the ExplodedNode where the lvalue (the value of 'Ex') +/// was computed. +static const ExplodedNode* findNodeForExpression(const ExplodedNode *N, + const Expr *Inner) { + while (N) { + if (auto P = N->getLocation().getAs<PostStmt>()) { + if (P->getStmt() == Inner) + break; + } + N = N->getFirstPred(); + } + assert(N && "Unable to find the lvalue node."); + return N; +} + +/// Performing operator `&' on an lvalue expression is essentially a no-op. +/// Then, if we are taking addresses of fields or elements, these are also +/// unlikely to matter. +static const Expr* peelOfOuterAddrOf(const Expr* Ex) { + Ex = Ex->IgnoreParenCasts(); + + // FIXME: There's a hack in our Store implementation that always computes + // field offsets around null pointers as if they are always equal to 0. + // The idea here is to report accesses to fields as null dereferences + // even though the pointer value that's being dereferenced is actually + // the offset of the field rather than exactly 0. + // See the FIXME in StoreManager's getLValueFieldOrIvar() method. + // This code interacts heavily with this hack; otherwise the value + // would not be null at all for most fields, so we'd be unable to track it. + if (const auto *Op = dyn_cast<UnaryOperator>(Ex)) + if (Op->getOpcode() == UO_AddrOf && Op->getSubExpr()->isLValue()) + if (const Expr *DerefEx = bugreporter::getDerefExpr(Op->getSubExpr())) + return DerefEx; return Ex; } @@ -985,52 +1546,23 @@ bool bugreporter::trackNullOrUndefValue(const ExplodedNode *N, if (!S || !N) return false; - if (const Expr *Ex = dyn_cast<Expr>(S)) + if (const auto *Ex = dyn_cast<Expr>(S)) S = peelOffOuterExpr(Ex, N); const Expr *Inner = nullptr; - if (const Expr *Ex = dyn_cast<Expr>(S)) { + if (const auto *Ex = dyn_cast<Expr>(S)) { + Ex = peelOfOuterAddrOf(Ex); Ex = Ex->IgnoreParenCasts(); - // Performing operator `&' on an lvalue expression is essentially a no-op. - // Then, if we are taking addresses of fields or elements, these are also - // unlikely to matter. - // FIXME: There's a hack in our Store implementation that always computes - // field offsets around null pointers as if they are always equal to 0. - // The idea here is to report accesses to fields as null dereferences - // even though the pointer value that's being dereferenced is actually - // the offset of the field rather than exactly 0. - // See the FIXME in StoreManager's getLValueFieldOrIvar() method. - // This code interacts heavily with this hack; otherwise the value - // would not be null at all for most fields, so we'd be unable to track it. - if (const auto *Op = dyn_cast<UnaryOperator>(Ex)) - if (Op->getOpcode() == UO_AddrOf && Op->getSubExpr()->isLValue()) - if (const Expr *DerefEx = getDerefExpr(Op->getSubExpr())) - Ex = DerefEx; - - if (Ex && (ExplodedGraph::isInterestingLValueExpr(Ex) || CallEvent::isCallStmt(Ex))) + if (Ex && (ExplodedGraph::isInterestingLValueExpr(Ex) + || CallEvent::isCallStmt(Ex))) Inner = Ex; } if (IsArg && !Inner) { assert(N->getLocation().getAs<CallEnter>() && "Tracking arg but not at call"); } else { - // Walk through nodes until we get one that matches the statement exactly. - // Alternately, if we hit a known lvalue for the statement, we know we've - // gone too far (though we can likely track the lvalue better anyway). - do { - const ProgramPoint &pp = N->getLocation(); - if (Optional<StmtPoint> ps = pp.getAs<StmtPoint>()) { - if (ps->getStmt() == S || ps->getStmt() == Inner) - break; - } else if (Optional<CallExitEnd> CEE = pp.getAs<CallExitEnd>()) { - if (CEE->getCalleeContext()->getCallSite() == S || - CEE->getCalleeContext()->getCallSite() == Inner) - break; - } - N = N->getFirstPred(); - } while (N); - + N = findNodeForStatement(N, S, Inner); if (!N) return false; } @@ -1041,51 +1573,43 @@ bool bugreporter::trackNullOrUndefValue(const ExplodedNode *N, // 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(S, N)) - trackNullOrUndefValue(N, Receiver, report, false, EnableNullFPSuppression); - + trackNullOrUndefValue(N, Receiver, report, /* IsArg=*/ false, + EnableNullFPSuppression); // 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 (Inner && ExplodedGraph::isInterestingLValueExpr(Inner)) { - const MemRegion *R = nullptr; - - // Find the ExplodedNode where the lvalue (the value of 'Ex') - // was computed. We need this for getting the location value. - const ExplodedNode *LVNode = N; - while (LVNode) { - if (Optional<PostStmt> P = LVNode->getLocation().getAs<PostStmt>()) { - if (P->getStmt() == Inner) - break; - } - LVNode = LVNode->getFirstPred(); - } - assert(LVNode && "Unable to find the lvalue node."); + const ExplodedNode *LVNode = findNodeForExpression(N, Inner); ProgramStateRef LVState = LVNode->getState(); - SVal LVal = LVState->getSVal(Inner, LVNode->getLocationContext()); - - if (LVState->isNull(LVal).isConstrainedTrue()) { - // In case of C++ references, we want to differentiate between a null - // reference and reference to null pointer. - // If the LVal is null, check if we are dealing with null reference. - // For those, we want to track the location of the reference. - if (const MemRegion *RR = getLocationRegionIfReference(Inner, N)) - R = RR; - } else { - R = LVState->getSVal(Inner, LVNode->getLocationContext()).getAsRegion(); - - // If this is a C++ reference to a null pointer, we are tracking the - // pointer. In addition, we should find the store at which the reference - // got initialized. - if (const MemRegion *RR = getLocationRegionIfReference(Inner, N)) { - if (Optional<KnownSVal> KV = LVal.getAs<KnownSVal>()) - report.addVisitor(llvm::make_unique<FindLastStoreBRVisitor>( + SVal LVal = LVNode->getSVal(Inner); + + const MemRegion *RR = getLocationRegionIfReference(Inner, N); + bool LVIsNull = LVState->isNull(LVal).isConstrainedTrue(); + + // If this is a C++ reference to a null pointer, we are tracking the + // pointer. In addition, we should find the store at which the reference + // got initialized. + if (RR && !LVIsNull) { + if (auto KV = LVal.getAs<KnownSVal>()) + report.addVisitor(llvm::make_unique<FindLastStoreBRVisitor>( *KV, RR, EnableNullFPSuppression)); - } } + // In case of C++ references, we want to differentiate between a null + // reference and reference to null pointer. + // If the LVal is null, check if we are dealing with null reference. + // For those, we want to track the location of the reference. + const MemRegion *R = (RR && LVIsNull) ? RR : + LVNode->getSVal(Inner).getAsRegion(); + if (R) { // Mark both the variable region and its contents as interesting. SVal V = LVState->getRawSVal(loc::MemRegionVal(R)); + report.addVisitor( + llvm::make_unique<NoStoreFuncVisitor>(cast<SubRegion>(R))); + + MacroNullReturnSuppressionVisitor::addMacroVisitorIfNecessary( + N, R, EnableNullFPSuppression, report, V); report.markInteresting(R); report.markInteresting(V); @@ -1094,21 +1618,21 @@ bool bugreporter::trackNullOrUndefValue(const ExplodedNode *N, // If the contents are symbolic, find out when they became null. if (V.getAsLocSymbol(/*IncludeBaseRegions*/ true)) report.addVisitor(llvm::make_unique<TrackConstraintBRVisitor>( - V.castAs<DefinedSVal>(), false)); + V.castAs<DefinedSVal>(), false)); // Add visitor, which will suppress inline defensive checks. - if (Optional<DefinedSVal> DV = V.getAs<DefinedSVal>()) { + if (auto DV = V.getAs<DefinedSVal>()) { if (!DV->isZeroConstant() && LVState->isNull(*DV).isConstrainedTrue() && EnableNullFPSuppression) { report.addVisitor( llvm::make_unique<SuppressInlineDefensiveChecksVisitor>(*DV, - LVNode)); + LVNode)); } } - if (Optional<KnownSVal> KV = V.getAs<KnownSVal>()) + if (auto KV = V.getAs<KnownSVal>()) report.addVisitor(llvm::make_unique<FindLastStoreBRVisitor>( - *KV, R, EnableNullFPSuppression)); + *KV, R, EnableNullFPSuppression)); return true; } } @@ -1119,7 +1643,7 @@ bool bugreporter::trackNullOrUndefValue(const ExplodedNode *N, // If the value came from an inlined function call, we should at least make // sure that function isn't pruned in our output. - if (const Expr *E = dyn_cast<Expr>(S)) + if (const auto *E = dyn_cast<Expr>(S)) S = E->IgnoreParenCasts(); ReturnVisitor::addVisitorIfNecessary(N, S, report, EnableNullFPSuppression); @@ -1128,40 +1652,40 @@ bool bugreporter::trackNullOrUndefValue(const ExplodedNode *N, // base value that was dereferenced. // assert(!V.isUnknownOrUndef()); // Is it a symbolic value? - if (Optional<loc::MemRegionVal> L = V.getAs<loc::MemRegionVal>()) { + if (auto L = V.getAs<loc::MemRegionVal>()) { + report.addVisitor(llvm::make_unique<UndefOrNullArgVisitor>(L->getRegion())); + // At this point we are dealing with the region's LValue. // However, if the rvalue is a symbolic region, we should track it as well. // Try to use the correct type when looking up the value. SVal RVal; - if (const Expr *E = dyn_cast<Expr>(S)) + if (const auto *E = dyn_cast<Expr>(S)) RVal = state->getRawSVal(L.getValue(), E->getType()); else RVal = state->getSVal(L->getRegion()); - report.addVisitor(llvm::make_unique<UndefOrNullArgVisitor>(L->getRegion())); - if (Optional<KnownSVal> KV = RVal.getAs<KnownSVal>()) + if (auto KV = RVal.getAs<KnownSVal>()) report.addVisitor(llvm::make_unique<FindLastStoreBRVisitor>( - *KV, L->getRegion(), EnableNullFPSuppression)); + *KV, L->getRegion(), EnableNullFPSuppression)); const MemRegion *RegionRVal = RVal.getAsRegion(); if (RegionRVal && isa<SymbolicRegion>(RegionRVal)) { report.markInteresting(RegionRVal); report.addVisitor(llvm::make_unique<TrackConstraintBRVisitor>( - loc::MemRegionVal(RegionRVal), false)); + loc::MemRegionVal(RegionRVal), false)); } } - return true; } const Expr *NilReceiverBRVisitor::getNilReceiver(const Stmt *S, const ExplodedNode *N) { - const ObjCMessageExpr *ME = dyn_cast<ObjCMessageExpr>(S); + const auto *ME = dyn_cast<ObjCMessageExpr>(S); if (!ME) return nullptr; if (const Expr *Receiver = ME->getInstanceReceiver()) { ProgramStateRef state = N->getState(); - SVal V = state->getSVal(Receiver, N->getLocationContext()); + SVal V = N->getSVal(Receiver); if (state->isNull(V).isConstrainedTrue()) return Receiver; } @@ -1184,7 +1708,7 @@ NilReceiverBRVisitor::VisitNode(const ExplodedNode *N, llvm::SmallString<256> Buf; llvm::raw_svector_ostream OS(Buf); - if (const ObjCMessageExpr *ME = dyn_cast<ObjCMessageExpr>(S)) { + if (const auto *ME = dyn_cast<ObjCMessageExpr>(S)) { OS << "'"; ME->getSelector().print(OS); OS << "' not called"; @@ -1217,16 +1741,15 @@ void FindLastStoreBRVisitor::registerStatementVarDecls(BugReport &BR, const Stmt *Head = WorkList.front(); WorkList.pop_front(); - ProgramStateRef state = N->getState(); - ProgramStateManager &StateMgr = state->getStateManager(); + ProgramStateManager &StateMgr = N->getState()->getStateManager(); - if (const DeclRefExpr *DR = dyn_cast<DeclRefExpr>(Head)) { - if (const VarDecl *VD = dyn_cast<VarDecl>(DR->getDecl())) { + if (const auto *DR = dyn_cast<DeclRefExpr>(Head)) { + if (const auto *VD = dyn_cast<VarDecl>(DR->getDecl())) { const VarRegion *R = StateMgr.getRegionManager().getVarRegion(VD, N->getLocationContext()); // What did we load? - SVal V = state->getSVal(S, N->getLocationContext()); + SVal V = N->getSVal(S); if (V.getAs<loc::ConcreteInt>() || V.getAs<nonloc::ConcreteInt>()) { // Register a new visitor with the BugReport. @@ -1267,7 +1790,6 @@ std::shared_ptr<PathDiagnosticPiece> ConditionBRVisitor::VisitNodeImpl(const ExplodedNode *N, const ExplodedNode *Prev, BugReporterContext &BRC, BugReport &BR) { - ProgramPoint progPoint = N->getLocation(); ProgramStateRef CurrentState = N->getState(); ProgramStateRef PrevState = Prev->getState(); @@ -1289,11 +1811,8 @@ ConditionBRVisitor::VisitNodeImpl(const ExplodedNode *N, } if (Optional<PostStmt> PS = progPoint.getAs<PostStmt>()) { - // FIXME: Assuming that BugReporter is a GRBugReporter is a layering - // violation. const std::pair<const ProgramPointTag *, const ProgramPointTag *> &tags = - cast<GRBugReporter>(BRC.getBugReporter()). - getEngine().geteagerlyAssumeBinOpBifurcationTags(); + ExprEngine::geteagerlyAssumeBinOpBifurcationTags(); const ProgramPointTag *tag = PS->getTag(); if (tag == tags.first) @@ -1389,7 +1908,7 @@ ConditionBRVisitor::VisitTrueTest(const Expr *Cond, bool tookTrue, return P; break; case Stmt::UnaryOperatorClass: { - const UnaryOperator *UO = cast<UnaryOperator>(CondTmp); + const auto *UO = cast<UnaryOperator>(CondTmp); if (UO->getOpcode() == UO_LNot) { tookTrueTmp = !tookTrueTmp; CondTmp = UO->getSubExpr(); @@ -1432,7 +1951,6 @@ bool ConditionBRVisitor::patternMatch(const Expr *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, @@ -1463,7 +1981,7 @@ bool ConditionBRVisitor::patternMatch(const Expr *Ex, } } - if (const DeclRefExpr *DR = dyn_cast<DeclRefExpr>(Ex)) { + if (const auto *DR = dyn_cast<DeclRefExpr>(Ex)) { const bool quotes = isa<VarDecl>(DR->getDecl()); if (quotes) { Out << '\''; @@ -1487,7 +2005,7 @@ bool ConditionBRVisitor::patternMatch(const Expr *Ex, return quotes; } - if (const IntegerLiteral *IL = dyn_cast<IntegerLiteral>(Ex)) { + if (const auto *IL = dyn_cast<IntegerLiteral>(Ex)) { QualType OriginalTy = OriginalExpr->getType(); if (OriginalTy->isPointerType()) { if (IL->getValue() == 0) { @@ -1513,7 +2031,6 @@ std::shared_ptr<PathDiagnosticPiece> ConditionBRVisitor::VisitTrueTest(const Expr *Cond, const BinaryOperator *BExpr, const bool tookTrue, BugReporterContext &BRC, BugReport &R, const ExplodedNode *N) { - bool shouldInvert = false; Optional<bool> shouldPrune; @@ -1618,8 +2135,8 @@ std::shared_ptr<PathDiagnosticPiece> ConditionBRVisitor::VisitConditionVariable( PathDiagnosticLocation Loc(CondVarExpr, BRC.getSourceManager(), LCtx); auto event = std::make_shared<PathDiagnosticEventPiece>(Loc, Out.str()); - if (const DeclRefExpr *DR = dyn_cast<DeclRefExpr>(CondVarExpr)) { - if (const VarDecl *VD = dyn_cast<VarDecl>(DR->getDecl())) { + if (const auto *DR = dyn_cast<DeclRefExpr>(CondVarExpr)) { + if (const auto *VD = dyn_cast<VarDecl>(DR->getDecl())) { const ProgramState *state = N->getState().get(); if (const MemRegion *R = state->getLValue(VD, LCtx).getAsRegion()) { if (report.isInteresting(R)) @@ -1635,8 +2152,7 @@ std::shared_ptr<PathDiagnosticPiece> ConditionBRVisitor::VisitTrueTest(const Expr *Cond, const DeclRefExpr *DR, const bool tookTrue, BugReporterContext &BRC, BugReport &report, const ExplodedNode *N) { - - const VarDecl *VD = dyn_cast<VarDecl>(DR->getDecl()); + const auto *VD = dyn_cast<VarDecl>(DR->getDecl()); if (!VD) return nullptr; @@ -1684,14 +2200,11 @@ bool ConditionBRVisitor::isPieceMessageGeneric( Piece->getString() == GenericFalseMessage; } -std::unique_ptr<PathDiagnosticPiece> -LikelyFalsePositiveSuppressionBRVisitor::getEndPath(BugReporterContext &BRC, - const ExplodedNode *N, - BugReport &BR) { +void LikelyFalsePositiveSuppressionBRVisitor::finalizeVisitor( + BugReporterContext &BRC, const ExplodedNode *N, BugReport &BR) { // Here we suppress false positives coming from system headers. This list is // based on known issues. - ExprEngine &Eng = BRC.getBugReporter().getEngine(); - AnalyzerOptions &Options = Eng.getAnalysisManager().options; + AnalyzerOptions &Options = BRC.getAnalyzerOptions(); const Decl *D = N->getLocationContext()->getDecl(); if (AnalysisDeclContext::isInStdNamespace(D)) { @@ -1701,8 +2214,7 @@ LikelyFalsePositiveSuppressionBRVisitor::getEndPath(BugReporterContext &BRC, // TR1, Boost, or llvm/ADT. if (Options.shouldSuppressFromCXXStandardLibrary()) { BR.markInvalid(getTag(), nullptr); - return nullptr; - + return; } else { // If the complete 'std' suppression is not enabled, suppress reports // from the 'std' namespace that are known to produce false positives. @@ -1710,27 +2222,27 @@ LikelyFalsePositiveSuppressionBRVisitor::getEndPath(BugReporterContext &BRC, // The analyzer issues a false use-after-free when std::list::pop_front // or std::list::pop_back are called multiple times because we cannot // reason about the internal invariants of the data structure. - if (const CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(D)) { + if (const auto *MD = dyn_cast<CXXMethodDecl>(D)) { const CXXRecordDecl *CD = MD->getParent(); if (CD->getName() == "list") { BR.markInvalid(getTag(), nullptr); - return nullptr; + return; } } // The analyzer issues a false positive when the constructor of // std::__independent_bits_engine from algorithms is used. - if (const CXXConstructorDecl *MD = dyn_cast<CXXConstructorDecl>(D)) { + if (const auto *MD = dyn_cast<CXXConstructorDecl>(D)) { const CXXRecordDecl *CD = MD->getParent(); if (CD->getName() == "__independent_bits_engine") { BR.markInvalid(getTag(), nullptr); - return nullptr; + return; } } for (const LocationContext *LCtx = N->getLocationContext(); LCtx; LCtx = LCtx->getParent()) { - const CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(LCtx->getDecl()); + const auto *MD = dyn_cast<CXXMethodDecl>(LCtx->getDecl()); if (!MD) continue; @@ -1743,7 +2255,7 @@ LikelyFalsePositiveSuppressionBRVisitor::getEndPath(BugReporterContext &BRC, // data structure. if (CD->getName() == "basic_string") { BR.markInvalid(getTag(), nullptr); - return nullptr; + return; } // The analyzer issues a false positive on @@ -1751,7 +2263,7 @@ LikelyFalsePositiveSuppressionBRVisitor::getEndPath(BugReporterContext &BRC, // because it does not reason properly about temporary destructors. if (CD->getName() == "shared_ptr") { BR.markInvalid(getTag(), nullptr); - return nullptr; + return; } } } @@ -1765,18 +2277,15 @@ LikelyFalsePositiveSuppressionBRVisitor::getEndPath(BugReporterContext &BRC, Loc = Loc.getSpellingLoc(); if (SM.getFilename(Loc).endswith("sys/queue.h")) { BR.markInvalid(getTag(), nullptr); - return nullptr; + return; } } - - return nullptr; } std::shared_ptr<PathDiagnosticPiece> UndefOrNullArgVisitor::VisitNode(const ExplodedNode *N, const ExplodedNode *PrevN, BugReporterContext &BRC, BugReport &BR) { - ProgramStateRef State = N->getState(); ProgramPoint ProgLoc = N->getLocation(); @@ -1789,18 +2298,17 @@ UndefOrNullArgVisitor::VisitNode(const ExplodedNode *N, CallEventManager &CEMgr = BRC.getStateManager().getCallEventManager(); CallEventRef<> Call = CEMgr.getCaller(CEnter->getCalleeContext(), State); unsigned Idx = 0; - ArrayRef<ParmVarDecl*> parms = Call->parameters(); + ArrayRef<ParmVarDecl *> parms = Call->parameters(); - for (ArrayRef<ParmVarDecl*>::iterator I = parms.begin(), E = parms.end(); - I != E; ++I, ++Idx) { + for (const auto ParamDecl : parms) { const MemRegion *ArgReg = Call->getArgSVal(Idx).getAsRegion(); + ++Idx; // Are we tracking the argument or its subregion? - if ( !ArgReg || (ArgReg != R && !R->isSubRegionOf(ArgReg->StripCasts()))) + if ( !ArgReg || !R->isSubRegionOf(ArgReg->StripCasts())) continue; // Check the function parameter type. - const ParmVarDecl *ParamDecl = *I; assert(ParamDecl && "Formal parameter has no decl?"); QualType T = ParamDecl->getType(); @@ -1832,7 +2340,7 @@ CXXSelfAssignmentBRVisitor::VisitNode(const ExplodedNode *Succ, if (Satisfied) return nullptr; - auto Edge = Succ->getLocation().getAs<BlockEdge>(); + const auto Edge = Succ->getLocation().getAs<BlockEdge>(); if (!Edge.hasValue()) return nullptr; @@ -1859,7 +2367,7 @@ CXXSelfAssignmentBRVisitor::VisitNode(const ExplodedNode *Succ, const auto Param = State->getSVal(State->getRegion(Met->getParamDecl(0), LCtx)); const auto This = - State->getSVal(SVB.getCXXThis(Met, LCtx->getCurrentStackFrame())); + State->getSVal(SVB.getCXXThis(Met, LCtx->getStackFrame())); auto L = PathDiagnosticLocation::create(Met, BRC.getSourceManager()); @@ -1877,3 +2385,82 @@ CXXSelfAssignmentBRVisitor::VisitNode(const ExplodedNode *Succ, return std::move(Piece); } + +std::shared_ptr<PathDiagnosticPiece> +TaintBugVisitor::VisitNode(const ExplodedNode *N, const ExplodedNode *PrevN, + BugReporterContext &BRC, BugReport &BR) { + + // Find the ExplodedNode where the taint was first introduced + if (!N->getState()->isTainted(V) || PrevN->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"); +} + +FalsePositiveRefutationBRVisitor::FalsePositiveRefutationBRVisitor() + : Constraints(ConstraintRangeTy::Factory().getEmptyMap()) {} + +void FalsePositiveRefutationBRVisitor::finalizeVisitor( + BugReporterContext &BRC, const ExplodedNode *EndPathNode, BugReport &BR) { + // Collect new constraints + VisitNode(EndPathNode, nullptr, BRC, BR); + + // Create a refutation manager + std::unique_ptr<SMTSolver> RefutationSolver = CreateZ3Solver(); + ASTContext &Ctx = BRC.getASTContext(); + + // Add constraints to the solver + for (const auto &I : Constraints) { + SymbolRef Sym = I.first; + + SMTExprRef Constraints = RefutationSolver->fromBoolean(false); + for (const auto &Range : I.second) { + Constraints = RefutationSolver->mkOr( + Constraints, + RefutationSolver->getRangeExpr(Ctx, Sym, Range.From(), Range.To(), + /*InRange=*/true)); + } + RefutationSolver->addConstraint(Constraints); + } + + // And check for satisfiability + if (RefutationSolver->check().isConstrainedFalse()) + BR.markInvalid("Infeasible constraints", EndPathNode->getLocationContext()); +} + +std::shared_ptr<PathDiagnosticPiece> +FalsePositiveRefutationBRVisitor::VisitNode(const ExplodedNode *N, + const ExplodedNode *PrevN, + BugReporterContext &BRC, + BugReport &BR) { + // Collect new constraints + const ConstraintRangeTy &NewCs = N->getState()->get<ConstraintRange>(); + ConstraintRangeTy::Factory &CF = + N->getState()->get_context<ConstraintRange>(); + + // Add constraints if we don't have them yet + for (auto const &C : NewCs) { + const SymbolRef &Sym = C.first; + if (!Constraints.contains(Sym)) { + Constraints = CF.add(Constraints, Sym, C.second); + } + } + + return nullptr; +} + +void FalsePositiveRefutationBRVisitor::Profile( + llvm::FoldingSetNodeID &ID) const { + static int Tag = 0; + ID.AddPointer(&Tag); +} diff --git a/lib/StaticAnalyzer/Core/CMakeLists.txt b/lib/StaticAnalyzer/Core/CMakeLists.txt index 5ac4f942f373..de994b598e59 100644 --- a/lib/StaticAnalyzer/Core/CMakeLists.txt +++ b/lib/StaticAnalyzer/Core/CMakeLists.txt @@ -13,7 +13,6 @@ add_clang_library(clangStaticAnalyzerCore AnalyzerOptions.cpp BasicValueFactory.cpp BlockCounter.cpp - IssueHash.cpp BugReporter.cpp BugReporterVisitors.cpp CallEvent.cpp @@ -35,6 +34,7 @@ add_clang_library(clangStaticAnalyzerCore ExprEngineObjC.cpp FunctionSummary.cpp HTMLDiagnostics.cpp + IssueHash.cpp LoopUnrolling.cpp LoopWidening.cpp MemRegion.cpp @@ -48,9 +48,11 @@ add_clang_library(clangStaticAnalyzerCore SVals.cpp SimpleConstraintManager.cpp SimpleSValBuilder.cpp + SMTConstraintManager.cpp Store.cpp SubEngine.cpp SymbolManager.cpp + WorkList.cpp Z3ConstraintManager.cpp LINK_LIBS @@ -58,6 +60,7 @@ add_clang_library(clangStaticAnalyzerCore clangASTMatchers clangAnalysis clangBasic + clangCrossTU clangLex clangRewrite ${Z3_LINK_FILES} diff --git a/lib/StaticAnalyzer/Core/CallEvent.cpp b/lib/StaticAnalyzer/Core/CallEvent.cpp index 776369be9dba..8db7b06f186d 100644 --- a/lib/StaticAnalyzer/Core/CallEvent.cpp +++ b/lib/StaticAnalyzer/Core/CallEvent.cpp @@ -1,4 +1,4 @@ -//===- Calls.cpp - Wrapper for all function and method calls ------*- C++ -*--// +//===- CallEvent.cpp - Wrapper for all function and method calls ----------===// // // The LLVM Compiler Infrastructure // @@ -14,14 +14,52 @@ //===----------------------------------------------------------------------===// #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" +#include "clang/AST/ASTContext.h" +#include "clang/AST/Decl.h" +#include "clang/AST/DeclBase.h" +#include "clang/AST/DeclCXX.h" +#include "clang/AST/DeclObjC.h" +#include "clang/AST/Expr.h" +#include "clang/AST/ExprCXX.h" +#include "clang/AST/ExprObjC.h" #include "clang/AST/ParentMap.h" +#include "clang/AST/Stmt.h" +#include "clang/AST/Type.h" +#include "clang/Analysis/AnalysisDeclContext.h" +#include "clang/Analysis/CFG.h" #include "clang/Analysis/ProgramPoint.h" +#include "clang/CrossTU/CrossTranslationUnit.h" +#include "clang/Basic/IdentifierTable.h" +#include "clang/Basic/LLVM.h" +#include "clang/Basic/SourceLocation.h" +#include "clang/Basic/SourceManager.h" +#include "clang/Basic/Specifiers.h" +#include "clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/DynamicTypeInfo.h" #include "clang/StaticAnalyzer/Core/PathSensitive/DynamicTypeMap.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState_Fwd.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/Store.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/None.h" +#include "llvm/ADT/Optional.h" +#include "llvm/ADT/PointerIntPair.h" #include "llvm/ADT/SmallSet.h" +#include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringExtras.h" -#include "llvm/Support/raw_ostream.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/Casting.h" +#include "llvm/Support/Compiler.h" #include "llvm/Support/Debug.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/raw_ostream.h" +#include <cassert> +#include <utility> #define DEBUG_TYPE "static-analyzer-call-event" @@ -29,11 +67,13 @@ using namespace clang; using namespace ento; QualType CallEvent::getResultType() const { + ASTContext &Ctx = getState()->getStateManager().getContext(); const Expr *E = getOriginExpr(); - assert(E && "Calls without origin expressions do not have results"); - QualType ResultTy = E->getType(); + if (!E) + return Ctx.VoidTy; + assert(E); - ASTContext &Ctx = getState()->getStateManager().getContext(); + QualType ResultTy = E->getType(); // A function that returns a reference to 'int' will have a result type // of simply 'int'. Check the origin expr's value kind to recover the @@ -78,7 +118,7 @@ static bool isCallback(QualType T) { } static bool isVoidPointerToNonConst(QualType T) { - if (const PointerType *PT = T->getAs<PointerType>()) { + if (const auto *PT = T->getAs<PointerType>()) { QualType PointeeTy = PT->getPointeeType(); if (PointeeTy.isConstQualified()) return false; @@ -119,14 +159,14 @@ bool CallEvent::hasVoidPointerToNonConstArg() const { } bool CallEvent::isGlobalCFunction(StringRef FunctionName) const { - const FunctionDecl *FD = dyn_cast_or_null<FunctionDecl>(getDecl()); + const auto *FD = dyn_cast_or_null<FunctionDecl>(getDecl()); if (!FD) return false; return CheckerContext::isCLibraryFunction(FD, FunctionName); } -/// \brief Returns true if a type is a pointer-to-const or reference-to-const +/// Returns true if a type is a pointer-to-const or reference-to-const /// with no further indirection. static bool isPointerToConst(QualType Ty) { QualType PointeeTy = Ty->getPointeeType(); @@ -235,7 +275,7 @@ SVal CallEvent::getArgSVal(unsigned Index) const { SourceRange CallEvent::getArgSourceRange(unsigned Index) const { const Expr *ArgE = getArgExpr(Index); if (!ArgE) - return SourceRange(); + return {}; return ArgE->getSourceRange(); } @@ -266,7 +306,6 @@ void CallEvent::dump(raw_ostream &Out) const { Out << "Unknown call (type " << getKind() << ")"; } - bool CallEvent::isCallStmt(const Stmt *S) { return isa<CallExpr>(S) || isa<ObjCMessageExpr>(S) || isa<CXXConstructExpr>(S) @@ -275,11 +314,11 @@ bool CallEvent::isCallStmt(const Stmt *S) { QualType CallEvent::getDeclaredResultType(const Decl *D) { assert(D); - if (const FunctionDecl* FD = dyn_cast<FunctionDecl>(D)) + if (const auto *FD = dyn_cast<FunctionDecl>(D)) return FD->getReturnType(); - if (const ObjCMethodDecl* MD = dyn_cast<ObjCMethodDecl>(D)) + if (const auto *MD = dyn_cast<ObjCMethodDecl>(D)) return MD->getReturnType(); - if (const BlockDecl *BD = dyn_cast<BlockDecl>(D)) { + if (const auto *BD = dyn_cast<BlockDecl>(D)) { // Blocks are difficult because the return type may not be stored in the // BlockDecl itself. The AST should probably be enhanced, but for now we // just do what we can. @@ -296,7 +335,7 @@ QualType CallEvent::getDeclaredResultType(const Decl *D) { return Ty; } - return QualType(); + return {}; } llvm_unreachable("unknown callable kind"); @@ -305,11 +344,11 @@ QualType CallEvent::getDeclaredResultType(const Decl *D) { bool CallEvent::isVariadic(const Decl *D) { assert(D); - if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(D)) + if (const auto *FD = dyn_cast<FunctionDecl>(D)) return FD->isVariadic(); - if (const ObjCMethodDecl *MD = dyn_cast<ObjCMethodDecl>(D)) + if (const auto *MD = dyn_cast<ObjCMethodDecl>(D)) return MD->isVariadic(); - if (const BlockDecl *BD = dyn_cast<BlockDecl>(D)) + if (const auto *BD = dyn_cast<BlockDecl>(D)) return BD->isVariadic(); llvm_unreachable("unknown callable kind"); @@ -350,32 +389,53 @@ ArrayRef<ParmVarDecl*> AnyFunctionCall::parameters() const { RuntimeDefinition AnyFunctionCall::getRuntimeDefinition() const { const FunctionDecl *FD = getDecl(); + if (!FD) + return {}; + // Note that the AnalysisDeclContext will have the FunctionDecl with // the definition (if one exists). - if (FD) { - AnalysisDeclContext *AD = - getLocationContext()->getAnalysisDeclContext()-> - getManager()->getContext(FD); - bool IsAutosynthesized; - Stmt* Body = AD->getBody(IsAutosynthesized); - DEBUG({ - if (IsAutosynthesized) - llvm::dbgs() << "Using autosynthesized body for " << FD->getName() - << "\n"; - }); - if (Body) { - const Decl* Decl = AD->getDecl(); - return RuntimeDefinition(Decl); - } + AnalysisDeclContext *AD = + getLocationContext()->getAnalysisDeclContext()-> + getManager()->getContext(FD); + bool IsAutosynthesized; + Stmt* Body = AD->getBody(IsAutosynthesized); + LLVM_DEBUG({ + if (IsAutosynthesized) + llvm::dbgs() << "Using autosynthesized body for " << FD->getName() + << "\n"; + }); + if (Body) { + const Decl* Decl = AD->getDecl(); + return RuntimeDefinition(Decl); + } + + SubEngine *Engine = getState()->getStateManager().getOwningEngine(); + AnalyzerOptions &Opts = Engine->getAnalysisManager().options; + + // Try to get CTU definition only if CTUDir is provided. + if (!Opts.naiveCTUEnabled()) + return {}; + + cross_tu::CrossTranslationUnitContext &CTUCtx = + *Engine->getCrossTranslationUnitContext(); + llvm::Expected<const FunctionDecl *> CTUDeclOrError = + CTUCtx.getCrossTUDefinition(FD, Opts.getCTUDir(), Opts.getCTUIndexName()); + + if (!CTUDeclOrError) { + handleAllErrors(CTUDeclOrError.takeError(), + [&](const cross_tu::IndexError &IE) { + CTUCtx.emitCrossTUDiagnostics(IE); + }); + return {}; } - return RuntimeDefinition(); + return RuntimeDefinition(*CTUDeclOrError); } void AnyFunctionCall::getInitialStackFrameContents( const StackFrameContext *CalleeCtx, BindingsTy &Bindings) const { - const FunctionDecl *D = cast<FunctionDecl>(CalleeCtx->getDecl()); + const auto *D = cast<FunctionDecl>(CalleeCtx->getDecl()); SValBuilder &SVB = getState()->getStateManager().getSValBuilder(); addParameterValuesToBindings(CalleeCtx, Bindings, SVB, *this, D->parameters()); @@ -442,7 +502,6 @@ bool AnyFunctionCall::argumentsMayEscape() const { return false; } - const FunctionDecl *SimpleFunctionCall::getDecl() const { const FunctionDecl *D = getOriginExpr()->getDirectCallee(); if (D) @@ -451,9 +510,8 @@ const FunctionDecl *SimpleFunctionCall::getDecl() const { return getSVal(getOriginExpr()->getCallee()).getAsFunctionDecl(); } - const FunctionDecl *CXXInstanceCall::getDecl() const { - const CallExpr *CE = cast_or_null<CallExpr>(getOriginExpr()); + const auto *CE = cast_or_null<CallExpr>(getOriginExpr()); if (!CE) return AnyFunctionCall::getDecl(); @@ -470,15 +528,20 @@ void CXXInstanceCall::getExtraInvalidatedValues( Values.push_back(ThisVal); // Don't invalidate if the method is const and there are no mutable fields. - if (const CXXMethodDecl *D = cast_or_null<CXXMethodDecl>(getDecl())) { + if (const auto *D = cast_or_null<CXXMethodDecl>(getDecl())) { if (!D->isConst()) return; // Get the record decl for the class of 'This'. D->getParent() may return a // base class decl, rather than the class of the instance which needs to be // checked for mutable fields. + // TODO: We might as well look at the dynamic type of the object. const Expr *Ex = getCXXThisExpr()->ignoreParenBaseCasts(); - const CXXRecordDecl *ParentRecord = Ex->getType()->getAsCXXRecordDecl(); - if (!ParentRecord || ParentRecord->hasMutableFields()) + QualType T = Ex->getType(); + if (T->isPointerType()) // Arrow or implicit-this syntax? + T = T->getPointeeType(); + const CXXRecordDecl *ParentRecord = T->getAsCXXRecordDecl(); + assert(ParentRecord); + if (ParentRecord->hasMutableFields()) return; // Preserve CXXThis. const MemRegion *ThisRegion = ThisVal.getAsRegion(); @@ -501,27 +564,26 @@ SVal CXXInstanceCall::getCXXThisVal() const { return ThisVal; } - RuntimeDefinition CXXInstanceCall::getRuntimeDefinition() const { // Do we have a decl at all? const Decl *D = getDecl(); if (!D) - return RuntimeDefinition(); + return {}; // If the method is non-virtual, we know we can inline it. - const CXXMethodDecl *MD = cast<CXXMethodDecl>(D); + const auto *MD = cast<CXXMethodDecl>(D); if (!MD->isVirtual()) return AnyFunctionCall::getRuntimeDefinition(); // Do we know the implicit 'this' object being called? const MemRegion *R = getCXXThisVal().getAsRegion(); if (!R) - return RuntimeDefinition(); + return {}; // Do we know anything about the type of 'this'? DynamicTypeInfo DynType = getDynamicTypeInfo(getState(), R); if (!DynType.isValid()) - return RuntimeDefinition(); + return {}; // Is the type a C++ class? (This is mostly a defensive check.) QualType RegionType = DynType.getType()->getPointeeType(); @@ -529,7 +591,7 @@ RuntimeDefinition CXXInstanceCall::getRuntimeDefinition() const { const CXXRecordDecl *RD = RegionType->getAsCXXRecordDecl(); if (!RD || !RD->hasDefinition()) - return RuntimeDefinition(); + return {}; // Find the decl for this method in that class. const CXXMethodDecl *Result = MD->getCorrespondingMethodInClass(RD, true); @@ -547,13 +609,13 @@ RuntimeDefinition CXXInstanceCall::getRuntimeDefinition() const { // this is fixed. <rdar://problem/12287087> //assert(!MD->getParent()->isDerivedFrom(RD) && "Bad DynamicTypeInfo"); - return RuntimeDefinition(); + return {}; } // Does the decl that we found have an implementation? const FunctionDecl *Definition; if (!Result->hasBody(Definition)) - return RuntimeDefinition(); + 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 @@ -574,7 +636,7 @@ void CXXInstanceCall::getInitialStackFrameContents( ProgramStateManager &StateMgr = getState()->getStateManager(); SValBuilder &SVB = StateMgr.getSValBuilder(); - const CXXMethodDecl *MD = cast<CXXMethodDecl>(CalleeCtx->getDecl()); + const auto *MD = cast<CXXMethodDecl>(CalleeCtx->getDecl()); Loc ThisLoc = SVB.getCXXThis(MD, CalleeCtx); // If we devirtualized to a different member function, we need to make sure @@ -587,7 +649,15 @@ void CXXInstanceCall::getInitialStackFrameContents( // FIXME: CallEvent maybe shouldn't be directly accessing StoreManager. bool Failed; ThisVal = StateMgr.getStoreManager().attemptDownCast(ThisVal, Ty, Failed); - assert(!Failed && "Calling an incorrectly devirtualized method"); + if (Failed) { + // We might have suffered some sort of placement new earlier, so + // we're constructing in a completely unexpected storage. + // Fall back to a generic pointer cast for this-value. + const CXXMethodDecl *StaticMD = cast<CXXMethodDecl>(getDecl()); + const CXXRecordDecl *StaticClass = StaticMD->getParent(); + QualType StaticTy = Ctx.getPointerType(Ctx.getRecordType(StaticClass)); + ThisVal = SVB.evalCast(ThisVal, Ty, StaticTy); + } } if (!ThisVal.isUnknown()) @@ -595,8 +665,6 @@ void CXXInstanceCall::getInitialStackFrameContents( } } - - const Expr *CXXMemberCall::getCXXThisExpr() const { return getOriginExpr()->getImplicitObjectArgument(); } @@ -606,19 +674,17 @@ RuntimeDefinition CXXMemberCall::getRuntimeDefinition() const { // id-expression in the class member access expression is a qualified-id, // that function is called. Otherwise, its final overrider in the dynamic type // of the object expression is called. - if (const MemberExpr *ME = dyn_cast<MemberExpr>(getOriginExpr()->getCallee())) + if (const auto *ME = dyn_cast<MemberExpr>(getOriginExpr()->getCallee())) if (ME->hasQualifier()) return AnyFunctionCall::getRuntimeDefinition(); return CXXInstanceCall::getRuntimeDefinition(); } - const Expr *CXXMemberOperatorCall::getCXXThisExpr() const { return getOriginExpr()->getArg(0); } - const BlockDataRegion *BlockCall::getBlockRegion() const { const Expr *Callee = getOriginExpr()->getCallee(); const MemRegion *DataReg = getSVal(Callee).getAsRegion(); @@ -663,7 +729,6 @@ void BlockCall::getInitialStackFrameContents(const StackFrameContext *CalleeCtx, Params); } - SVal CXXConstructorCall::getCXXThisVal() const { if (Data) return loc::MemRegionVal(static_cast<const MemRegion *>(Data)); @@ -672,8 +737,13 @@ SVal CXXConstructorCall::getCXXThisVal() const { void CXXConstructorCall::getExtraInvalidatedValues(ValueList &Values, RegionAndSymbolInvalidationTraits *ETraits) const { - if (Data) - Values.push_back(loc::MemRegionVal(static_cast<const MemRegion *>(Data))); + if (Data) { + loc::MemRegionVal MV(static_cast<const MemRegion *>(Data)); + if (SymbolRef Sym = MV.getAsSymbol(true)) + ETraits->setTrait(Sym, + RegionAndSymbolInvalidationTraits::TK_SuppressEscape); + Values.push_back(MV); + } } void CXXConstructorCall::getInitialStackFrameContents( @@ -684,7 +754,7 @@ void CXXConstructorCall::getInitialStackFrameContents( SVal ThisVal = getCXXThisVal(); if (!ThisVal.isUnknown()) { SValBuilder &SVB = getState()->getStateManager().getSValBuilder(); - const CXXMethodDecl *MD = cast<CXXMethodDecl>(CalleeCtx->getDecl()); + const auto *MD = cast<CXXMethodDecl>(CalleeCtx->getDecl()); Loc ThisLoc = SVB.getCXXThis(MD, CalleeCtx); Bindings.push_back(std::make_pair(ThisLoc, ThisVal)); } @@ -785,7 +855,7 @@ SourceRange ObjCMethodCall::getSourceRange() const { llvm_unreachable("unknown message kind"); } -typedef llvm::PointerIntPair<const PseudoObjectExpr *, 2> ObjCMessageDataTy; +using ObjCMessageDataTy = llvm::PointerIntPair<const PseudoObjectExpr *, 2>; const PseudoObjectExpr *ObjCMethodCall::getContainingPseudoObjectExpr() const { assert(Data && "Lazy lookup not yet performed."); @@ -799,7 +869,7 @@ getSyntacticFromForPseudoObjectExpr(const PseudoObjectExpr *POE) { // This handles the funny case of assigning to the result of a getter. // This can happen if the getter returns a non-const reference. - if (const BinaryOperator *BO = dyn_cast<BinaryOperator>(Syntactic)) + if (const auto *BO = dyn_cast<BinaryOperator>(Syntactic)) Syntactic = BO->getLHS(); return Syntactic; @@ -807,13 +877,12 @@ getSyntacticFromForPseudoObjectExpr(const PseudoObjectExpr *POE) { ObjCMessageKind ObjCMethodCall::getMessageKind() const { if (!Data) { - // Find the parent, ignoring implicit casts. ParentMap &PM = getLocationContext()->getParentMap(); const Stmt *S = PM.getParentIgnoreParenCasts(getOriginExpr()); // Check if parent is a PseudoObjectExpr. - if (const PseudoObjectExpr *POE = dyn_cast_or_null<PseudoObjectExpr>(S)) { + if (const auto *POE = dyn_cast_or_null<PseudoObjectExpr>(S)) { const Expr *Syntactic = getSyntacticFromForPseudoObjectExpr(POE); ObjCMessageKind K; @@ -875,15 +944,14 @@ const ObjCPropertyDecl *ObjCMethodCall::getAccessedProperty() const { bool ObjCMethodCall::canBeOverridenInSubclass(ObjCInterfaceDecl *IDecl, Selector Sel) const { assert(IDecl); - const SourceManager &SM = - getState()->getStateManager().getContext().getSourceManager(); - + AnalysisManager &AMgr = + getState()->getStateManager().getOwningEngine()->getAnalysisManager(); // If the class interface is declared inside the main file, assume it is not // subcassed. // TODO: It could actually be subclassed if the subclass is private as well. // This is probably very rare. SourceLocation InterfLoc = IDecl->getEndOfDefinitionLoc(); - if (InterfLoc.isValid() && SM.isInMainFile(InterfLoc)) + if (InterfLoc.isValid() && AMgr.isInCodeFile(InterfLoc)) return false; // Assume that property accessors are not overridden. @@ -905,7 +973,7 @@ bool ObjCMethodCall::canBeOverridenInSubclass(ObjCInterfaceDecl *IDecl, return false; // If outside the main file, - if (D->getLocation().isValid() && !SM.isInMainFile(D->getLocation())) + if (D->getLocation().isValid() && !AMgr.isInCodeFile(D->getLocation())) return true; if (D->isOverriding()) { @@ -965,7 +1033,6 @@ RuntimeDefinition ObjCMethodCall::getRuntimeDefinition() const { Selector Sel = E->getSelector(); if (E->isInstanceMessage()) { - // Find the receiver type. const ObjCObjectPointerType *ReceiverT = nullptr; bool CanBeSubClassed = false; @@ -980,13 +1047,13 @@ RuntimeDefinition ObjCMethodCall::getRuntimeDefinition() const { } else { Receiver = getReceiverSVal().getAsRegion(); if (!Receiver) - return RuntimeDefinition(); + return {}; DynamicTypeInfo DTI = getDynamicTypeInfo(getState(), Receiver); if (!DTI.isValid()) { assert(isa<AllocaRegion>(Receiver) && "Unhandled untyped region class!"); - return RuntimeDefinition(); + return {}; } QualType DynType = DTI.getType(); @@ -1041,11 +1108,9 @@ RuntimeDefinition ObjCMethodCall::getRuntimeDefinition() const { // need to revisit this someday. In terms of memory, this table // stays around until clang quits, which also may be bad if we // need to release memory. - typedef std::pair<const ObjCInterfaceDecl*, Selector> - PrivateMethodKey; - typedef llvm::DenseMap<PrivateMethodKey, - Optional<const ObjCMethodDecl *> > - PrivateMethodCache; + using PrivateMethodKey = std::pair<const ObjCInterfaceDecl *, Selector>; + using PrivateMethodCache = + llvm::DenseMap<PrivateMethodKey, Optional<const ObjCMethodDecl *>>; static PrivateMethodCache PMC; Optional<const ObjCMethodDecl *> &Val = PMC[std::make_pair(IDecl, Sel)]; @@ -1090,7 +1155,6 @@ RuntimeDefinition ObjCMethodCall::getRuntimeDefinition() const { else return RuntimeDefinition(MD, nullptr); } - } else { // This is a class method. // If we have type info for the receiver class, we are calling via @@ -1101,7 +1165,7 @@ RuntimeDefinition ObjCMethodCall::getRuntimeDefinition() const { } } - return RuntimeDefinition(); + return {}; } bool ObjCMethodCall::argumentsMayEscape() const { @@ -1118,7 +1182,7 @@ bool ObjCMethodCall::argumentsMayEscape() const { void ObjCMethodCall::getInitialStackFrameContents( const StackFrameContext *CalleeCtx, BindingsTy &Bindings) const { - const ObjCMethodDecl *D = cast<ObjCMethodDecl>(CalleeCtx->getDecl()); + const auto *D = cast<ObjCMethodDecl>(CalleeCtx->getDecl()); SValBuilder &SVB = getState()->getStateManager().getSValBuilder(); addParameterValuesToBindings(CalleeCtx, Bindings, SVB, *this, D->parameters()); @@ -1135,12 +1199,12 @@ void ObjCMethodCall::getInitialStackFrameContents( CallEventRef<> CallEventManager::getSimpleCall(const CallExpr *CE, ProgramStateRef State, const LocationContext *LCtx) { - if (const CXXMemberCallExpr *MCE = dyn_cast<CXXMemberCallExpr>(CE)) + if (const auto *MCE = dyn_cast<CXXMemberCallExpr>(CE)) return create<CXXMemberCall>(MCE, State, LCtx); - if (const CXXOperatorCallExpr *OpCE = dyn_cast<CXXOperatorCallExpr>(CE)) { + if (const auto *OpCE = dyn_cast<CXXOperatorCallExpr>(CE)) { const FunctionDecl *DirectCallee = OpCE->getDirectCallee(); - if (const CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(DirectCallee)) + if (const auto *MD = dyn_cast<CXXMethodDecl>(DirectCallee)) if (MD->isInstance()) return create<CXXMemberOperatorCall>(OpCE, State, LCtx); @@ -1153,12 +1217,11 @@ CallEventManager::getSimpleCall(const CallExpr *CE, ProgramStateRef State, return create<SimpleFunctionCall>(CE, State, LCtx); } - CallEventRef<> CallEventManager::getCaller(const StackFrameContext *CalleeCtx, ProgramStateRef State) { const LocationContext *ParentCtx = CalleeCtx->getParent(); - const LocationContext *CallerCtx = ParentCtx->getCurrentStackFrame(); + const LocationContext *CallerCtx = ParentCtx->getStackFrame(); assert(CallerCtx && "This should not be used for top-level stack frames"); const Stmt *CallSite = CalleeCtx->getCallSite(); @@ -1171,7 +1234,7 @@ CallEventManager::getCaller(const StackFrameContext *CalleeCtx, case Stmt::CXXConstructExprClass: case Stmt::CXXTemporaryObjectExprClass: { SValBuilder &SVB = State->getStateManager().getSValBuilder(); - const CXXMethodDecl *Ctor = cast<CXXMethodDecl>(CalleeCtx->getDecl()); + const auto *Ctor = cast<CXXMethodDecl>(CalleeCtx->getDecl()); Loc ThisPtr = SVB.getCXXThis(Ctor, CalleeCtx); SVal ThisVal = State->getSVal(ThisPtr); @@ -1192,12 +1255,11 @@ CallEventManager::getCaller(const StackFrameContext *CalleeCtx, // destructors, though this could change in the future. const CFGBlock *B = CalleeCtx->getCallSiteBlock(); CFGElement E = (*B)[CalleeCtx->getIndex()]; - assert(E.getAs<CFGImplicitDtor>() && + assert((E.getAs<CFGImplicitDtor>() || E.getAs<CFGTemporaryDtor>()) && "All other CFG elements should have exprs"); - assert(!E.getAs<CFGTemporaryDtor>() && "We don't handle temporaries yet"); SValBuilder &SVB = State->getStateManager().getSValBuilder(); - const CXXDestructorDecl *Dtor = cast<CXXDestructorDecl>(CalleeCtx->getDecl()); + const auto *Dtor = cast<CXXDestructorDecl>(CalleeCtx->getDecl()); Loc ThisPtr = SVB.getCXXThis(Dtor, CalleeCtx); SVal ThisVal = State->getSVal(ThisPtr); @@ -1205,7 +1267,7 @@ CallEventManager::getCaller(const StackFrameContext *CalleeCtx, if (Optional<CFGAutomaticObjDtor> AutoDtor = E.getAs<CFGAutomaticObjDtor>()) Trigger = AutoDtor->getTriggerStmt(); else if (Optional<CFGDeleteDtor> DeleteDtor = E.getAs<CFGDeleteDtor>()) - Trigger = cast<Stmt>(DeleteDtor->getDeleteExpr()); + Trigger = DeleteDtor->getDeleteExpr(); else Trigger = Dtor->getBody(); diff --git a/lib/StaticAnalyzer/Core/CheckerContext.cpp b/lib/StaticAnalyzer/Core/CheckerContext.cpp index 61cbf3854bb2..6cf931abbddd 100644 --- a/lib/StaticAnalyzer/Core/CheckerContext.cpp +++ b/lib/StaticAnalyzer/Core/CheckerContext.cpp @@ -20,9 +20,8 @@ using namespace clang; using namespace ento; const FunctionDecl *CheckerContext::getCalleeDecl(const CallExpr *CE) const { - ProgramStateRef State = getState(); const Expr *Callee = CE->getCallee(); - SVal L = State->getSVal(Callee, Pred->getLocationContext()); + SVal L = Pred->getSVal(Callee); return L.getAsFunctionDecl(); } diff --git a/lib/StaticAnalyzer/Core/CheckerHelpers.cpp b/lib/StaticAnalyzer/Core/CheckerHelpers.cpp index ed41914ebd05..b9facffcc8b5 100644 --- a/lib/StaticAnalyzer/Core/CheckerHelpers.cpp +++ b/lib/StaticAnalyzer/Core/CheckerHelpers.cpp @@ -15,8 +15,12 @@ #include "clang/AST/Decl.h" #include "clang/AST/Expr.h" +namespace clang { + +namespace ento { + // Recursively find any substatements containing macros -bool clang::ento::containsMacro(const Stmt *S) { +bool containsMacro(const Stmt *S) { if (S->getLocStart().isMacroID()) return true; @@ -31,7 +35,7 @@ bool clang::ento::containsMacro(const Stmt *S) { } // Recursively find any substatements containing enum constants -bool clang::ento::containsEnum(const Stmt *S) { +bool containsEnum(const Stmt *S) { const DeclRefExpr *DR = dyn_cast<DeclRefExpr>(S); if (DR && isa<EnumConstantDecl>(DR->getDecl())) @@ -45,7 +49,7 @@ bool clang::ento::containsEnum(const Stmt *S) { } // Recursively find any substatements containing static vars -bool clang::ento::containsStaticLocal(const Stmt *S) { +bool containsStaticLocal(const Stmt *S) { const DeclRefExpr *DR = dyn_cast<DeclRefExpr>(S); if (DR) @@ -61,7 +65,7 @@ bool clang::ento::containsStaticLocal(const Stmt *S) { } // Recursively find any substatements containing __builtin_offsetof -bool clang::ento::containsBuiltinOffsetOf(const Stmt *S) { +bool containsBuiltinOffsetOf(const Stmt *S) { if (isa<OffsetOfExpr>(S)) return true; @@ -74,7 +78,7 @@ bool clang::ento::containsBuiltinOffsetOf(const Stmt *S) { // Extract lhs and rhs from assignment statement std::pair<const clang::VarDecl *, const clang::Expr *> -clang::ento::parseAssignment(const Stmt *S) { +parseAssignment(const Stmt *S) { const VarDecl *VD = nullptr; const Expr *RHS = nullptr; @@ -94,3 +98,18 @@ clang::ento::parseAssignment(const Stmt *S) { return std::make_pair(VD, RHS); } + +Nullability getNullabilityAnnotation(QualType Type) { + const auto *AttrType = Type->getAs<AttributedType>(); + if (!AttrType) + return Nullability::Unspecified; + if (AttrType->getAttrKind() == AttributedType::attr_nullable) + return Nullability::Nullable; + else if (AttrType->getAttrKind() == AttributedType::attr_nonnull) + return Nullability::Nonnull; + return Nullability::Unspecified; +} + + +} // end namespace ento +} // end namespace clang diff --git a/lib/StaticAnalyzer/Core/CheckerManager.cpp b/lib/StaticAnalyzer/Core/CheckerManager.cpp index 49f3edef2a2d..712872a15d8a 100644 --- a/lib/StaticAnalyzer/Core/CheckerManager.cpp +++ b/lib/StaticAnalyzer/Core/CheckerManager.cpp @@ -1,4 +1,4 @@ -//===--- CheckerManager.cpp - Static Analyzer Checker Manager -------------===// +//===- CheckerManager.cpp - Static Analyzer Checker Manager ---------------===// // // The LLVM Compiler Infrastructure // @@ -13,10 +13,20 @@ #include "clang/StaticAnalyzer/Core/CheckerManager.h" #include "clang/AST/DeclBase.h" +#include "clang/AST/Stmt.h" #include "clang/Analysis/ProgramPoint.h" +#include "clang/Basic/LLVM.h" #include "clang/StaticAnalyzer/Core/Checker.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CoreEngine.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/Support/Casting.h" +#include "llvm/Support/ErrorHandling.h" +#include <cassert> +#include <vector> using namespace clang; using namespace ento; @@ -43,9 +53,9 @@ void CheckerManager::finishedCheckerRegistration() { #ifndef NDEBUG // Make sure that for every event that has listeners, there is at least // one dispatcher registered for it. - for (llvm::DenseMap<EventTag, EventInfo>::iterator - I = Events.begin(), E = Events.end(); I != E; ++I) - assert(I->second.HasDispatcher && "No dispatcher registered for an event"); + for (const auto &Event : Events) + assert(Event.second.HasDispatcher && + "No dispatcher registered for an event"); #endif } @@ -65,25 +75,22 @@ void CheckerManager::runCheckersOnASTDecl(const Decl *D, AnalysisManager& mgr, } else { // Find the checkers that should run for this Decl and cache them. checkers = &CachedDeclCheckersMap[DeclKind]; - for (unsigned i = 0, e = DeclCheckers.size(); i != e; ++i) { - DeclCheckerInfo &info = DeclCheckers[i]; + for (const auto &info : DeclCheckers) if (info.IsForDeclFn(D)) checkers->push_back(info.CheckFn); - } } assert(checkers); - for (CachedDeclCheckers::iterator - I = checkers->begin(), E = checkers->end(); I != E; ++I) - (*I)(D, mgr, BR); + for (const auto checker : *checkers) + checker(D, mgr, BR); } void CheckerManager::runCheckersOnASTBody(const Decl *D, AnalysisManager& mgr, BugReporter &BR) { assert(D && D->hasBody()); - for (unsigned i = 0, e = BodyCheckers.size(); i != e; ++i) - BodyCheckers[i](D, mgr, BR); + for (const auto BodyChecker : BodyCheckers) + BodyChecker(D, mgr, BR); } //===----------------------------------------------------------------------===// @@ -118,10 +125,8 @@ static void expandGraphWithCheckers(CHECK_CTX checkCtx, } NodeBuilder B(*PrevSet, *CurrSet, BldrCtx); - for (ExplodedNodeSet::iterator NI = PrevSet->begin(), NE = PrevSet->end(); - NI != NE; ++NI) { - checkCtx.runChecker(*I, B, *NI); - } + for (const auto &NI : *PrevSet) + checkCtx.runChecker(*I, B, NI); // If all the produced transitions are sinks, stop. if (CurrSet->empty()) @@ -133,21 +138,23 @@ static void expandGraphWithCheckers(CHECK_CTX checkCtx, } namespace { + struct CheckStmtContext { - typedef SmallVectorImpl<CheckerManager::CheckStmtFunc> CheckersTy; + using CheckersTy = SmallVectorImpl<CheckerManager::CheckStmtFunc>; + bool IsPreVisit; const CheckersTy &Checkers; const Stmt *S; ExprEngine &Eng; bool WasInlined; - CheckersTy::const_iterator checkers_begin() { return Checkers.begin(); } - CheckersTy::const_iterator checkers_end() { return Checkers.end(); } - CheckStmtContext(bool isPreVisit, const CheckersTy &checkers, const Stmt *s, ExprEngine &eng, bool wasInlined = false) - : IsPreVisit(isPreVisit), Checkers(checkers), S(s), Eng(eng), - WasInlined(wasInlined) {} + : IsPreVisit(isPreVisit), Checkers(checkers), S(s), Eng(eng), + WasInlined(wasInlined) {} + + CheckersTy::const_iterator checkers_begin() { return Checkers.begin(); } + CheckersTy::const_iterator checkers_end() { return Checkers.end(); } void runChecker(CheckerManager::CheckStmtFunc checkFn, NodeBuilder &Bldr, ExplodedNode *Pred) { @@ -160,9 +167,10 @@ namespace { checkFn(S, C); } }; -} -/// \brief Run checkers for visiting Stmts. +} // namespace + +/// Run checkers for visiting Stmts. void CheckerManager::runCheckersForStmt(bool isPreVisit, ExplodedNodeSet &Dst, const ExplodedNodeSet &Src, @@ -175,8 +183,9 @@ void CheckerManager::runCheckersForStmt(bool isPreVisit, } namespace { + struct CheckObjCMessageContext { - typedef std::vector<CheckerManager::CheckObjCMessageFunc> CheckersTy; + using CheckersTy = std::vector<CheckerManager::CheckObjCMessageFunc>; ObjCMessageVisitKind Kind; bool WasInlined; @@ -184,19 +193,18 @@ namespace { const ObjCMethodCall &Msg; ExprEngine &Eng; - CheckersTy::const_iterator checkers_begin() { return Checkers.begin(); } - CheckersTy::const_iterator checkers_end() { return Checkers.end(); } - CheckObjCMessageContext(ObjCMessageVisitKind visitKind, const CheckersTy &checkers, const ObjCMethodCall &msg, ExprEngine &eng, bool wasInlined) - : Kind(visitKind), WasInlined(wasInlined), Checkers(checkers), - Msg(msg), Eng(eng) { } + : Kind(visitKind), WasInlined(wasInlined), Checkers(checkers), Msg(msg), + Eng(eng) {} + + CheckersTy::const_iterator checkers_begin() { return Checkers.begin(); } + CheckersTy::const_iterator checkers_end() { return Checkers.end(); } void runChecker(CheckerManager::CheckObjCMessageFunc checkFn, NodeBuilder &Bldr, ExplodedNode *Pred) { - bool IsPreVisit; switch (Kind) { @@ -215,9 +223,10 @@ namespace { checkFn(*Msg.cloneWithState<ObjCMethodCall>(Pred->getState()), C); } }; -} -/// \brief Run checkers for visiting obj-c messages. +} // namespace + +/// Run checkers for visiting obj-c messages. void CheckerManager::runCheckersForObjCMessage(ObjCMessageVisitKind visitKind, ExplodedNodeSet &Dst, const ExplodedNodeSet &Src, @@ -242,24 +251,27 @@ CheckerManager::getObjCMessageCheckers(ObjCMessageVisitKind Kind) { } llvm_unreachable("Unknown Kind"); } + namespace { + // FIXME: This has all the same signatures as CheckObjCMessageContext. // Is there a way we can merge the two? struct CheckCallContext { - typedef std::vector<CheckerManager::CheckCallFunc> CheckersTy; + using CheckersTy = std::vector<CheckerManager::CheckCallFunc>; + bool IsPreVisit, WasInlined; const CheckersTy &Checkers; const CallEvent &Call; ExprEngine &Eng; - CheckersTy::const_iterator checkers_begin() { return Checkers.begin(); } - CheckersTy::const_iterator checkers_end() { return Checkers.end(); } - CheckCallContext(bool isPreVisit, const CheckersTy &checkers, const CallEvent &call, ExprEngine &eng, bool wasInlined) - : IsPreVisit(isPreVisit), WasInlined(wasInlined), Checkers(checkers), - Call(call), Eng(eng) { } + : IsPreVisit(isPreVisit), WasInlined(wasInlined), Checkers(checkers), + Call(call), Eng(eng) {} + + CheckersTy::const_iterator checkers_begin() { return Checkers.begin(); } + CheckersTy::const_iterator checkers_end() { return Checkers.end(); } void runChecker(CheckerManager::CheckCallFunc checkFn, NodeBuilder &Bldr, ExplodedNode *Pred) { @@ -269,9 +281,10 @@ namespace { checkFn(*Call.cloneWithState(Pred->getState()), C); } }; -} -/// \brief Run checkers for visiting an abstract call event. +} // namespace + +/// Run checkers for visiting an abstract call event. void CheckerManager::runCheckersForCallEvent(bool isPreVisit, ExplodedNodeSet &Dst, const ExplodedNodeSet &Src, @@ -286,8 +299,10 @@ void CheckerManager::runCheckersForCallEvent(bool isPreVisit, } namespace { + struct CheckLocationContext { - typedef std::vector<CheckerManager::CheckLocationFunc> CheckersTy; + using CheckersTy = std::vector<CheckerManager::CheckLocationFunc>; + const CheckersTy &Checkers; SVal Loc; bool IsLoad; @@ -295,15 +310,15 @@ namespace { const Stmt *BoundEx; ExprEngine &Eng; - CheckersTy::const_iterator checkers_begin() { return Checkers.begin(); } - CheckersTy::const_iterator checkers_end() { return Checkers.end(); } - CheckLocationContext(const CheckersTy &checkers, SVal loc, bool isLoad, const Stmt *NodeEx, const Stmt *BoundEx, ExprEngine &eng) - : Checkers(checkers), Loc(loc), IsLoad(isLoad), NodeEx(NodeEx), - BoundEx(BoundEx), Eng(eng) {} + : Checkers(checkers), Loc(loc), IsLoad(isLoad), NodeEx(NodeEx), + BoundEx(BoundEx), Eng(eng) {} + + CheckersTy::const_iterator checkers_begin() { return Checkers.begin(); } + CheckersTy::const_iterator checkers_end() { return Checkers.end(); } void runChecker(CheckerManager::CheckLocationFunc checkFn, NodeBuilder &Bldr, ExplodedNode *Pred) { @@ -317,9 +332,10 @@ namespace { checkFn(Loc, IsLoad, BoundEx, C); } }; -} -/// \brief Run checkers for load/store of a location. +} // namespace + +/// Run checkers for load/store of a location. void CheckerManager::runCheckersForLocation(ExplodedNodeSet &Dst, const ExplodedNodeSet &Src, @@ -333,8 +349,10 @@ void CheckerManager::runCheckersForLocation(ExplodedNodeSet &Dst, } namespace { + struct CheckBindContext { - typedef std::vector<CheckerManager::CheckBindFunc> CheckersTy; + using CheckersTy = std::vector<CheckerManager::CheckBindFunc>; + const CheckersTy &Checkers; SVal Loc; SVal Val; @@ -342,13 +360,13 @@ namespace { ExprEngine &Eng; const ProgramPoint &PP; - CheckersTy::const_iterator checkers_begin() { return Checkers.begin(); } - CheckersTy::const_iterator checkers_end() { return Checkers.end(); } - CheckBindContext(const CheckersTy &checkers, SVal loc, SVal val, const Stmt *s, ExprEngine &eng, const ProgramPoint &pp) - : Checkers(checkers), Loc(loc), Val(val), S(s), Eng(eng), PP(pp) {} + : Checkers(checkers), Loc(loc), Val(val), S(s), Eng(eng), PP(pp) {} + + CheckersTy::const_iterator checkers_begin() { return Checkers.begin(); } + CheckersTy::const_iterator checkers_end() { return Checkers.end(); } void runChecker(CheckerManager::CheckBindFunc checkFn, NodeBuilder &Bldr, ExplodedNode *Pred) { @@ -358,9 +376,10 @@ namespace { checkFn(Loc, Val, S, C); } }; -} -/// \brief Run checkers for binding of a value to a location. +} // namespace + +/// Run checkers for binding of a value to a location. void CheckerManager::runCheckersForBind(ExplodedNodeSet &Dst, const ExplodedNodeSet &Src, SVal location, SVal val, @@ -373,24 +392,26 @@ void CheckerManager::runCheckersForBind(ExplodedNodeSet &Dst, void CheckerManager::runCheckersForEndAnalysis(ExplodedGraph &G, BugReporter &BR, ExprEngine &Eng) { - for (unsigned i = 0, e = EndAnalysisCheckers.size(); i != e; ++i) - EndAnalysisCheckers[i](G, BR, Eng); + for (const auto EndAnalysisChecker : EndAnalysisCheckers) + EndAnalysisChecker(G, BR, Eng); } namespace { + struct CheckBeginFunctionContext { - typedef std::vector<CheckerManager::CheckBeginFunctionFunc> CheckersTy; + using CheckersTy = std::vector<CheckerManager::CheckBeginFunctionFunc>; + const CheckersTy &Checkers; ExprEngine &Eng; const ProgramPoint &PP; - CheckersTy::const_iterator checkers_begin() { return Checkers.begin(); } - CheckersTy::const_iterator checkers_end() { return Checkers.end(); } - CheckBeginFunctionContext(const CheckersTy &Checkers, ExprEngine &Eng, const ProgramPoint &PP) : Checkers(Checkers), Eng(Eng), PP(PP) {} + CheckersTy::const_iterator checkers_begin() { return Checkers.begin(); } + CheckersTy::const_iterator checkers_end() { return Checkers.end(); } + void runChecker(CheckerManager::CheckBeginFunctionFunc checkFn, NodeBuilder &Bldr, ExplodedNode *Pred) { const ProgramPoint &L = PP.withTag(checkFn.Checker); @@ -399,7 +420,8 @@ struct CheckBeginFunctionContext { checkFn(C); } }; -} + +} // namespace void CheckerManager::runCheckersForBeginFunction(ExplodedNodeSet &Dst, const BlockEdge &L, @@ -411,42 +433,42 @@ void CheckerManager::runCheckersForBeginFunction(ExplodedNodeSet &Dst, expandGraphWithCheckers(C, Dst, Src); } -/// \brief Run checkers for end of path. +/// Run checkers for end of path. // Note, We do not chain the checker output (like in expandGraphWithCheckers) // for this callback since end of path nodes are expected to be final. void CheckerManager::runCheckersForEndFunction(NodeBuilderContext &BC, ExplodedNodeSet &Dst, ExplodedNode *Pred, - ExprEngine &Eng) { - + ExprEngine &Eng, + const ReturnStmt *RS) { // We define the builder outside of the loop bacause if at least one checkers // creates a sucsessor for Pred, we do not need to generate an // autotransition for it. NodeBuilder Bldr(Pred, Dst, BC); - for (unsigned i = 0, e = EndFunctionCheckers.size(); i != e; ++i) { - CheckEndFunctionFunc checkFn = EndFunctionCheckers[i]; - + for (const auto checkFn : EndFunctionCheckers) { const ProgramPoint &L = BlockEntrance(BC.Block, Pred->getLocationContext(), checkFn.Checker); CheckerContext C(Bldr, Eng, Pred, L); - checkFn(C); + checkFn(RS, C); } } namespace { + struct CheckBranchConditionContext { - typedef std::vector<CheckerManager::CheckBranchConditionFunc> CheckersTy; + using CheckersTy = std::vector<CheckerManager::CheckBranchConditionFunc>; + const CheckersTy &Checkers; const Stmt *Condition; ExprEngine &Eng; - CheckersTy::const_iterator checkers_begin() { return Checkers.begin(); } - CheckersTy::const_iterator checkers_end() { return Checkers.end(); } - CheckBranchConditionContext(const CheckersTy &checkers, const Stmt *Cond, ExprEngine &eng) - : Checkers(checkers), Condition(Cond), Eng(eng) {} + : Checkers(checkers), Condition(Cond), Eng(eng) {} + + CheckersTy::const_iterator checkers_begin() { return Checkers.begin(); } + CheckersTy::const_iterator checkers_end() { return Checkers.end(); } void runChecker(CheckerManager::CheckBranchConditionFunc checkFn, NodeBuilder &Bldr, ExplodedNode *Pred) { @@ -456,9 +478,10 @@ namespace { checkFn(Condition, C); } }; -} -/// \brief Run checkers for branch condition. +} // namespace + +/// Run checkers for branch condition. void CheckerManager::runCheckersForBranchCondition(const Stmt *Condition, ExplodedNodeSet &Dst, ExplodedNode *Pred, @@ -469,29 +492,69 @@ void CheckerManager::runCheckersForBranchCondition(const Stmt *Condition, expandGraphWithCheckers(C, Dst, Src); } -/// \brief Run checkers for live symbols. +namespace { + + struct CheckNewAllocatorContext { + using CheckersTy = std::vector<CheckerManager::CheckNewAllocatorFunc>; + + const CheckersTy &Checkers; + const CXXNewExpr *NE; + SVal Target; + bool WasInlined; + ExprEngine &Eng; + + CheckNewAllocatorContext(const CheckersTy &Checkers, const CXXNewExpr *NE, + SVal Target, bool WasInlined, ExprEngine &Eng) + : Checkers(Checkers), NE(NE), Target(Target), WasInlined(WasInlined), + Eng(Eng) {} + + CheckersTy::const_iterator checkers_begin() { return Checkers.begin(); } + CheckersTy::const_iterator checkers_end() { return Checkers.end(); } + + void runChecker(CheckerManager::CheckNewAllocatorFunc checkFn, + NodeBuilder &Bldr, ExplodedNode *Pred) { + ProgramPoint L = PostAllocatorCall(NE, Pred->getLocationContext()); + CheckerContext C(Bldr, Eng, Pred, L, WasInlined); + checkFn(NE, Target, C); + } + }; + +} // namespace + +void CheckerManager::runCheckersForNewAllocator( + const CXXNewExpr *NE, SVal Target, ExplodedNodeSet &Dst, ExplodedNode *Pred, + ExprEngine &Eng, bool WasInlined) { + ExplodedNodeSet Src; + Src.insert(Pred); + CheckNewAllocatorContext C(NewAllocatorCheckers, NE, Target, WasInlined, Eng); + expandGraphWithCheckers(C, Dst, Src); +} + +/// Run checkers for live symbols. void CheckerManager::runCheckersForLiveSymbols(ProgramStateRef state, SymbolReaper &SymReaper) { - for (unsigned i = 0, e = LiveSymbolsCheckers.size(); i != e; ++i) - LiveSymbolsCheckers[i](state, SymReaper); + for (const auto LiveSymbolsChecker : LiveSymbolsCheckers) + LiveSymbolsChecker(state, SymReaper); } namespace { + struct CheckDeadSymbolsContext { - typedef std::vector<CheckerManager::CheckDeadSymbolsFunc> CheckersTy; + using CheckersTy = std::vector<CheckerManager::CheckDeadSymbolsFunc>; + const CheckersTy &Checkers; SymbolReaper &SR; const Stmt *S; ExprEngine &Eng; ProgramPoint::Kind ProgarmPointKind; - CheckersTy::const_iterator checkers_begin() { return Checkers.begin(); } - CheckersTy::const_iterator checkers_end() { return Checkers.end(); } - CheckDeadSymbolsContext(const CheckersTy &checkers, SymbolReaper &sr, const Stmt *s, ExprEngine &eng, ProgramPoint::Kind K) - : Checkers(checkers), SR(sr), S(s), Eng(eng), ProgarmPointKind(K) { } + : Checkers(checkers), SR(sr), S(s), Eng(eng), ProgarmPointKind(K) {} + + CheckersTy::const_iterator checkers_begin() { return Checkers.begin(); } + CheckersTy::const_iterator checkers_end() { return Checkers.end(); } void runChecker(CheckerManager::CheckDeadSymbolsFunc checkFn, NodeBuilder &Bldr, ExplodedNode *Pred) { @@ -505,9 +568,10 @@ namespace { checkFn(SR, C); } }; -} -/// \brief Run checkers for dead symbols. +} // namespace + +/// Run checkers for dead symbols. void CheckerManager::runCheckersForDeadSymbols(ExplodedNodeSet &Dst, const ExplodedNodeSet &Src, SymbolReaper &SymReaper, @@ -518,7 +582,7 @@ void CheckerManager::runCheckersForDeadSymbols(ExplodedNodeSet &Dst, expandGraphWithCheckers(C, Dst, Src); } -/// \brief Run checkers for region changes. +/// Run checkers for region changes. ProgramStateRef CheckerManager::runCheckersForRegionChanges(ProgramStateRef state, const InvalidatedSymbols *invalidated, @@ -526,19 +590,18 @@ CheckerManager::runCheckersForRegionChanges(ProgramStateRef state, ArrayRef<const MemRegion *> Regions, const LocationContext *LCtx, const CallEvent *Call) { - for (unsigned i = 0, e = RegionChangesCheckers.size(); i != e; ++i) { + for (const auto RegionChangesChecker : RegionChangesCheckers) { // If any checker declares the state infeasible (or if it starts that way), // bail out. if (!state) return nullptr; - state = RegionChangesCheckers[i](state, invalidated, - ExplicitRegions, Regions, - LCtx, Call); + state = RegionChangesChecker(state, invalidated, ExplicitRegions, Regions, + LCtx, Call); } return state; } -/// \brief Run checkers to process symbol escape event. +/// Run checkers to process symbol escape event. ProgramStateRef CheckerManager::runCheckersForPointerEscape(ProgramStateRef State, const InvalidatedSymbols &Escaped, @@ -549,58 +612,55 @@ CheckerManager::runCheckersForPointerEscape(ProgramStateRef State, (Kind != PSK_DirectEscapeOnCall && Kind != PSK_IndirectEscapeOnCall)) && "Call must not be NULL when escaping on call"); - for (unsigned i = 0, e = PointerEscapeCheckers.size(); i != e; ++i) { - // If any checker declares the state infeasible (or if it starts that - // way), bail out. - if (!State) - return nullptr; - State = PointerEscapeCheckers[i](State, Escaped, Call, Kind, ETraits); - } + for (const auto PointerEscapeChecker : PointerEscapeCheckers) { + // If any checker declares the state infeasible (or if it starts that + // way), bail out. + if (!State) + return nullptr; + State = PointerEscapeChecker(State, Escaped, Call, Kind, ETraits); + } return State; } -/// \brief Run checkers for handling assumptions on symbolic values. +/// Run checkers for handling assumptions on symbolic values. ProgramStateRef CheckerManager::runCheckersForEvalAssume(ProgramStateRef state, SVal Cond, bool Assumption) { - for (unsigned i = 0, e = EvalAssumeCheckers.size(); i != e; ++i) { + for (const auto EvalAssumeChecker : EvalAssumeCheckers) { // If any checker declares the state infeasible (or if it starts that way), // bail out. if (!state) return nullptr; - state = EvalAssumeCheckers[i](state, Cond, Assumption); + state = EvalAssumeChecker(state, Cond, Assumption); } return state; } -/// \brief Run checkers for evaluating a call. +/// Run checkers for evaluating a call. /// Only one checker will evaluate the call. void CheckerManager::runCheckersForEvalCall(ExplodedNodeSet &Dst, const ExplodedNodeSet &Src, const CallEvent &Call, ExprEngine &Eng) { const CallExpr *CE = cast<CallExpr>(Call.getOriginExpr()); - for (ExplodedNodeSet::iterator - NI = Src.begin(), NE = Src.end(); NI != NE; ++NI) { - ExplodedNode *Pred = *NI; + for (const auto Pred : Src) { bool anyEvaluated = false; ExplodedNodeSet checkDst; NodeBuilder B(Pred, checkDst, Eng.getBuilderContext()); // Check if any of the EvalCall callbacks can evaluate the call. - for (std::vector<EvalCallFunc>::iterator - EI = EvalCallCheckers.begin(), EE = EvalCallCheckers.end(); - EI != EE; ++EI) { + for (const auto EvalCallChecker : EvalCallCheckers) { ProgramPoint::Kind K = ProgramPoint::PostStmtKind; - const ProgramPoint &L = ProgramPoint::getProgramPoint(CE, K, - Pred->getLocationContext(), EI->Checker); + const ProgramPoint &L = + ProgramPoint::getProgramPoint(CE, K, 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 = (*EI)(CE, C); + evaluated = EvalCallChecker(CE, C); } assert(!(evaluated && anyEvaluated) && "There are more than one checkers evaluating the call"); @@ -621,21 +681,20 @@ void CheckerManager::runCheckersForEvalCall(ExplodedNodeSet &Dst, } } -/// \brief Run checkers for the entire Translation Unit. +/// Run checkers for the entire Translation Unit. void CheckerManager::runCheckersOnEndOfTranslationUnit( const TranslationUnitDecl *TU, AnalysisManager &mgr, BugReporter &BR) { - for (unsigned i = 0, e = EndOfTranslationUnitCheckers.size(); i != e; ++i) - EndOfTranslationUnitCheckers[i](TU, mgr, BR); + for (const auto EndOfTranslationUnitChecker : EndOfTranslationUnitCheckers) + EndOfTranslationUnitChecker(TU, mgr, BR); } void CheckerManager::runCheckersForPrintState(raw_ostream &Out, ProgramStateRef State, const char *NL, const char *Sep) { - for (llvm::DenseMap<CheckerTag, CheckerRef>::iterator - I = CheckerTags.begin(), E = CheckerTags.end(); I != E; ++I) - I->second->printState(Out, State, NL, Sep); + for (const auto &CheckerTag : CheckerTags) + CheckerTag.second->printState(Out, State, NL, Sep); } //===----------------------------------------------------------------------===// @@ -661,6 +720,7 @@ void CheckerManager::_registerForPreStmt(CheckStmtFunc checkfn, StmtCheckerInfo info = { checkfn, isForStmtFn, /*IsPreVisit*/true }; StmtCheckers.push_back(info); } + void CheckerManager::_registerForPostStmt(CheckStmtFunc checkfn, HandlesStmtFunc isForStmtFn) { StmtCheckerInfo info = { checkfn, isForStmtFn, /*IsPreVisit*/false }; @@ -711,6 +771,10 @@ void CheckerManager::_registerForBranchCondition( BranchConditionCheckers.push_back(checkfn); } +void CheckerManager::_registerForNewAllocator(CheckNewAllocatorFunc checkfn) { + NewAllocatorCheckers.push_back(checkfn); +} + void CheckerManager::_registerForLiveSymbols(CheckLiveSymbolsFunc checkfn) { LiveSymbolsCheckers.push_back(checkfn); } @@ -760,15 +824,13 @@ CheckerManager::getCachedStmtCheckersFor(const Stmt *S, bool isPreVisit) { // Find the checkers that should run for this Stmt and cache them. CachedStmtCheckers &Checkers = CachedStmtCheckersMap[Key]; - for (unsigned i = 0, e = StmtCheckers.size(); i != e; ++i) { - StmtCheckerInfo &Info = StmtCheckers[i]; + for (const auto &Info : StmtCheckers) if (Info.IsPreVisit == isPreVisit && Info.IsForStmtFn(S)) Checkers.push_back(Info.CheckFn); - } return Checkers; } CheckerManager::~CheckerManager() { - for (unsigned i = 0, e = CheckerDtors.size(); i != e; ++i) - CheckerDtors[i](); + for (const auto CheckerDtor : CheckerDtors) + CheckerDtor(); } diff --git a/lib/StaticAnalyzer/Core/CheckerRegistry.cpp b/lib/StaticAnalyzer/Core/CheckerRegistry.cpp index c9cb189a5b72..645845ec2181 100644 --- a/lib/StaticAnalyzer/Core/CheckerRegistry.cpp +++ b/lib/StaticAnalyzer/Core/CheckerRegistry.cpp @@ -1,4 +1,4 @@ -//===--- CheckerRegistry.cpp - Maintains all available checkers -*- C++ -*-===// +//===- CheckerRegistry.cpp - Maintains all available checkers -------------===// // // The LLVM Compiler Infrastructure // @@ -9,18 +9,26 @@ #include "clang/StaticAnalyzer/Core/CheckerRegistry.h" #include "clang/Basic/Diagnostic.h" +#include "clang/Basic/LLVM.h" #include "clang/Frontend/FrontendDiagnostic.h" +#include "clang/StaticAnalyzer/Core/CheckerManager.h" #include "clang/StaticAnalyzer/Core/CheckerOptInfo.h" #include "clang/StaticAnalyzer/Core/AnalyzerOptions.h" +#include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SetVector.h" +#include "llvm/ADT/StringMap.h" +#include "llvm/ADT/StringRef.h" #include "llvm/Support/raw_ostream.h" +#include <algorithm> +#include <cstddef> +#include <tuple> using namespace clang; using namespace ento; static const char PackageSeparator = '.'; -typedef llvm::SetVector<const CheckerRegistry::CheckerInfo *> CheckerInfoSet; +using CheckerInfoSet = llvm::SetVector<const CheckerRegistry::CheckerInfo *>; static bool checkerNameLT(const CheckerRegistry::CheckerInfo &a, const CheckerRegistry::CheckerInfo &b) { @@ -50,8 +58,7 @@ static void collectCheckers(const CheckerRegistry::CheckerInfoList &checkers, // Use a binary search to find the possible start of the package. CheckerRegistry::CheckerInfo packageInfo(nullptr, opt.getName(), ""); auto end = checkers.cend(); - CheckerRegistry::CheckerInfoList::const_iterator i = - std::lower_bound(checkers.cbegin(), end, packageInfo, checkerNameLT); + auto i = std::lower_bound(checkers.cbegin(), end, packageInfo, checkerNameLT); // If we didn't even find a possible package, give up. if (i == end) @@ -73,12 +80,11 @@ static void collectCheckers(const CheckerRegistry::CheckerInfoList &checkers, size = packageSize->getValue(); // Step through all the checkers in the package. - for (auto checkEnd = i+size; i != checkEnd; ++i) { + for (auto checkEnd = i+size; i != checkEnd; ++i) if (opt.isEnabled()) collected.insert(&*i); else collected.remove(&*i); - } } void CheckerRegistry::addChecker(InitializationFunction fn, StringRef name, @@ -97,42 +103,38 @@ void CheckerRegistry::addChecker(InitializationFunction fn, StringRef name, void CheckerRegistry::initializeManager(CheckerManager &checkerMgr, SmallVectorImpl<CheckerOptInfo> &opts) const { // Sort checkers for efficient collection. - std::sort(Checkers.begin(), Checkers.end(), checkerNameLT); + llvm::sort(Checkers.begin(), Checkers.end(), checkerNameLT); // Collect checkers enabled by the options. CheckerInfoSet enabledCheckers; - for (SmallVectorImpl<CheckerOptInfo>::iterator - i = opts.begin(), e = opts.end(); i != e; ++i) { - collectCheckers(Checkers, Packages, *i, enabledCheckers); - } + for (auto &i : opts) + collectCheckers(Checkers, Packages, i, enabledCheckers); // Initialize the CheckerManager with all enabled checkers. - for (CheckerInfoSet::iterator - i = enabledCheckers.begin(), e = enabledCheckers.end(); i != e; ++i) { - checkerMgr.setCurrentCheckName(CheckName((*i)->FullName)); - (*i)->Initialize(checkerMgr); + for (const auto *i :enabledCheckers) { + checkerMgr.setCurrentCheckName(CheckName(i->FullName)); + i->Initialize(checkerMgr); } } void CheckerRegistry::validateCheckerOptions(const AnalyzerOptions &opts, DiagnosticsEngine &diags) const { - for (auto &config : opts.Config) { + for (const auto &config : opts.Config) { size_t pos = config.getKey().find(':'); if (pos == StringRef::npos) continue; bool hasChecker = false; StringRef checkerName = config.getKey().substr(0, pos); - for (auto &checker : Checkers) { + for (const auto &checker : Checkers) { if (checker.FullName.startswith(checkerName) && (checker.FullName.size() == pos || checker.FullName[pos] == '.')) { hasChecker = true; break; } } - if (!hasChecker) { + if (!hasChecker) diags.Report(diag::err_unknown_analyzer_checker) << checkerName; - } } } @@ -141,7 +143,7 @@ void CheckerRegistry::printHelp(raw_ostream &out, // FIXME: Alphabetical sort puts 'experimental' in the middle. // Would it be better to name it '~experimental' or something else // that's ASCIIbetically last? - std::sort(Checkers.begin(), Checkers.end(), checkerNameLT); + llvm::sort(Checkers.begin(), Checkers.end(), checkerNameLT); // FIXME: Print available packages. @@ -149,28 +151,26 @@ void CheckerRegistry::printHelp(raw_ostream &out, // Find the maximum option length. size_t optionFieldWidth = 0; - for (CheckerInfoList::const_iterator i = Checkers.begin(), e = Checkers.end(); - i != e; ++i) { + for (const auto &i : Checkers) { // Limit the amount of padding we are willing to give up for alignment. // Package.Name Description [Hidden] - size_t nameLength = i->FullName.size(); + size_t nameLength = i.FullName.size(); if (nameLength <= maxNameChars) optionFieldWidth = std::max(optionFieldWidth, nameLength); } const size_t initialPad = 2; - for (CheckerInfoList::const_iterator i = Checkers.begin(), e = Checkers.end(); - i != e; ++i) { - out.indent(initialPad) << i->FullName; + for (const auto &i : Checkers) { + out.indent(initialPad) << i.FullName; - int pad = optionFieldWidth - i->FullName.size(); + int pad = optionFieldWidth - i.FullName.size(); // Break on long option names. if (pad < 0) { out << '\n'; pad = optionFieldWidth + initialPad; } - out.indent(pad + 2) << i->Desc; + out.indent(pad + 2) << i.Desc; out << '\n'; } @@ -178,19 +178,13 @@ void CheckerRegistry::printHelp(raw_ostream &out, void CheckerRegistry::printList( raw_ostream &out, SmallVectorImpl<CheckerOptInfo> &opts) const { - std::sort(Checkers.begin(), Checkers.end(), checkerNameLT); + llvm::sort(Checkers.begin(), Checkers.end(), checkerNameLT); // Collect checkers enabled by the options. CheckerInfoSet enabledCheckers; - for (SmallVectorImpl<CheckerOptInfo>::iterator i = opts.begin(), - e = opts.end(); - i != e; ++i) { - collectCheckers(Checkers, Packages, *i, enabledCheckers); - } + for (auto &i : opts) + collectCheckers(Checkers, Packages, i, enabledCheckers); - for (CheckerInfoSet::const_iterator i = enabledCheckers.begin(), - e = enabledCheckers.end(); - i != e; ++i) { - out << (*i)->FullName << '\n'; - } + for (const auto *i : enabledCheckers) + out << i->FullName << '\n'; } diff --git a/lib/StaticAnalyzer/Core/ConstraintManager.cpp b/lib/StaticAnalyzer/Core/ConstraintManager.cpp index 8de2b0e8d271..ef9c44c51be4 100644 --- a/lib/StaticAnalyzer/Core/ConstraintManager.cpp +++ b/lib/StaticAnalyzer/Core/ConstraintManager.cpp @@ -1,4 +1,4 @@ -//== ConstraintManager.cpp - Constraints on symbolic values -----*- C++ -*--==// +//===- ConstraintManager.cpp - Constraints on symbolic values. ------------===// // // The LLVM Compiler Infrastructure // @@ -11,12 +11,17 @@ // //===----------------------------------------------------------------------===// +#include "clang/StaticAnalyzer/Core/PathSensitive/ConstraintManager.h" +#include "clang/AST/Type.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState_Fwd.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h" using namespace clang; using namespace ento; -ConstraintManager::~ConstraintManager() {} +ConstraintManager::~ConstraintManager() = default; static DefinedSVal getLocFromSymbol(const ProgramStateRef &State, SymbolRef Sym) { @@ -35,5 +40,5 @@ ConditionTruthVal ConstraintManager::checkNull(ProgramStateRef State, return ConditionTruthVal(false); if (!P.first && P.second) return ConditionTruthVal(true); - return ConditionTruthVal(); + return {}; } diff --git a/lib/StaticAnalyzer/Core/CoreEngine.cpp b/lib/StaticAnalyzer/Core/CoreEngine.cpp index e2e9ddf5048e..c17b6aae37e2 100644 --- a/lib/StaticAnalyzer/Core/CoreEngine.cpp +++ b/lib/StaticAnalyzer/Core/CoreEngine.cpp @@ -1,4 +1,4 @@ -//==- CoreEngine.cpp - Path-Sensitive Dataflow Engine ------------*- C++ -*-// +//===- CoreEngine.cpp - Path-Sensitive Dataflow Engine --------------------===// // // The LLVM Compiler Infrastructure // @@ -15,11 +15,27 @@ #include "clang/StaticAnalyzer/Core/PathSensitive/CoreEngine.h" #include "clang/AST/Expr.h" #include "clang/AST/ExprCXX.h" +#include "clang/AST/Stmt.h" #include "clang/AST/StmtCXX.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h" +#include "clang/Analysis/AnalysisDeclContext.h" +#include "clang/Analysis/CFG.h" +#include "clang/Analysis/ProgramPoint.h" +#include "clang/Basic/LLVM.h" +#include "clang/StaticAnalyzer/Core/AnalyzerOptions.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/BlockCounter.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/FunctionSummary.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/SubEngine.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/WorkList.h" +#include "llvm/ADT/Optional.h" +#include "llvm/ADT/STLExtras.h" #include "llvm/ADT/Statistic.h" #include "llvm/Support/Casting.h" +#include "llvm/Support/ErrorHandling.h" +#include <algorithm> +#include <cassert> +#include <memory> +#include <utility> using namespace clang; using namespace ento; @@ -34,146 +50,42 @@ STATISTIC(NumPathsExplored, "The # of paths explored by the analyzer."); //===----------------------------------------------------------------------===// -// Worklist classes for exploration of reachable states. +// Core analysis engine. //===----------------------------------------------------------------------===// -WorkList::Visitor::~Visitor() {} - -namespace { -class DFS : public WorkList { - SmallVector<WorkListUnit,20> Stack; -public: - bool hasWork() const override { - return !Stack.empty(); - } - - void enqueue(const WorkListUnit& U) override { - Stack.push_back(U); - } - - WorkListUnit dequeue() override { - assert (!Stack.empty()); - const WorkListUnit& U = Stack.back(); - Stack.pop_back(); // This technically "invalidates" U, but we are fine. - return U; - } - - bool visitItemsInWorkList(Visitor &V) override { - for (SmallVectorImpl<WorkListUnit>::iterator - I = Stack.begin(), E = Stack.end(); I != E; ++I) { - if (V.visit(*I)) - return true; - } - return false; - } -}; - -class BFS : public WorkList { - std::deque<WorkListUnit> Queue; -public: - bool hasWork() const override { - return !Queue.empty(); - } - - void enqueue(const WorkListUnit& U) override { - Queue.push_back(U); - } - - WorkListUnit dequeue() override { - WorkListUnit U = Queue.front(); - Queue.pop_front(); - return U; - } - - bool visitItemsInWorkList(Visitor &V) override { - for (std::deque<WorkListUnit>::iterator - I = Queue.begin(), E = Queue.end(); I != E; ++I) { - if (V.visit(*I)) - return true; - } - return false; +static std::unique_ptr<WorkList> generateWorkList(AnalyzerOptions &Opts) { + switch (Opts.getExplorationStrategy()) { + case AnalyzerOptions::ExplorationStrategyKind::DFS: + return WorkList::makeDFS(); + case AnalyzerOptions::ExplorationStrategyKind::BFS: + return WorkList::makeBFS(); + case AnalyzerOptions::ExplorationStrategyKind::BFSBlockDFSContents: + return WorkList::makeBFSBlockDFSContents(); + case AnalyzerOptions::ExplorationStrategyKind::UnexploredFirst: + return WorkList::makeUnexploredFirst(); + case AnalyzerOptions::ExplorationStrategyKind::UnexploredFirstQueue: + return WorkList::makeUnexploredFirstPriorityQueue(); + default: + llvm_unreachable("Unexpected case"); } -}; - -} // end anonymous namespace - -// Place the dstor for WorkList here because it contains virtual member -// functions, and we the code for the dstor generated in one compilation unit. -WorkList::~WorkList() {} - -WorkList *WorkList::makeDFS() { return new DFS(); } -WorkList *WorkList::makeBFS() { return new BFS(); } - -namespace { - class BFSBlockDFSContents : public WorkList { - std::deque<WorkListUnit> Queue; - SmallVector<WorkListUnit,20> Stack; - public: - bool hasWork() const override { - return !Queue.empty() || !Stack.empty(); - } - - void enqueue(const WorkListUnit& U) override { - if (U.getNode()->getLocation().getAs<BlockEntrance>()) - Queue.push_front(U); - else - Stack.push_back(U); - } - - WorkListUnit dequeue() override { - // Process all basic blocks to completion. - if (!Stack.empty()) { - const WorkListUnit& U = Stack.back(); - Stack.pop_back(); // This technically "invalidates" U, but we are fine. - return U; - } - - assert(!Queue.empty()); - // Don't use const reference. The subsequent pop_back() might make it - // unsafe. - WorkListUnit U = Queue.front(); - Queue.pop_front(); - return U; - } - bool visitItemsInWorkList(Visitor &V) override { - for (SmallVectorImpl<WorkListUnit>::iterator - I = Stack.begin(), E = Stack.end(); I != E; ++I) { - if (V.visit(*I)) - return true; - } - for (std::deque<WorkListUnit>::iterator - I = Queue.begin(), E = Queue.end(); I != E; ++I) { - if (V.visit(*I)) - return true; - } - return false; - } - - }; -} // end anonymous namespace - -WorkList* WorkList::makeBFSBlockDFSContents() { - return new BFSBlockDFSContents(); } -//===----------------------------------------------------------------------===// -// Core analysis engine. -//===----------------------------------------------------------------------===// +CoreEngine::CoreEngine(SubEngine &subengine, FunctionSummariesTy *FS, + AnalyzerOptions &Opts) + : SubEng(subengine), WList(generateWorkList(Opts)), + BCounterFactory(G.getAllocator()), FunctionSummaries(FS) {} /// ExecuteWorkList - Run the worklist algorithm for a maximum number of steps. bool CoreEngine::ExecuteWorkList(const LocationContext *L, unsigned Steps, ProgramStateRef InitState) { - if (G.num_roots() == 0) { // Initialize the analysis by constructing // the root if none exists. const CFGBlock *Entry = &(L->getCFG()->getEntry()); - assert (Entry->empty() && - "Entry block must be empty."); + assert(Entry->empty() && "Entry block must be empty."); - assert (Entry->succ_size() == 1 && - "Entry block must have 1 successor."); + assert(Entry->succ_size() == 1 && "Entry block must have 1 successor."); // Mark the entry block as visited. FunctionSummaries->markVisitedBasicBlock(Entry->getBlockID(), @@ -195,7 +107,7 @@ bool CoreEngine::ExecuteWorkList(const LocationContext *L, unsigned Steps, bool IsNew; ExplodedNode *Node = G.getNode(StartLoc, InitState, false, &IsNew); - assert (IsNew); + assert(IsNew); G.addRoot(Node); NodeBuilderContext BuilderCtx(*this, StartLoc.getDst(), Node); @@ -251,13 +163,12 @@ void CoreEngine::dispatchWorkItem(ExplodedNode* Pred, ProgramPoint Loc, break; case ProgramPoint::BlockExitKind: - assert (false && "BlockExit location never occur in forward analysis."); + assert(false && "BlockExit location never occur in forward analysis."); break; - case ProgramPoint::CallEnterKind: { + case ProgramPoint::CallEnterKind: HandleCallEnter(Loc.castAs<CallEnter>(), Pred); break; - } case ProgramPoint::CallExitBeginKind: SubEng.processCallExit(Pred); @@ -275,7 +186,8 @@ void CoreEngine::dispatchWorkItem(ExplodedNode* Pred, ProgramPoint Loc, Loc.getAs<PostInitializer>() || Loc.getAs<PostImplicitCall>() || Loc.getAs<CallExitEnd>() || - Loc.getAs<LoopExit>()); + Loc.getAs<LoopExit>() || + Loc.getAs<PostAllocatorCall>()); HandlePostStmt(WU.getBlock(), WU.getIndex(), Pred); break; } @@ -294,7 +206,6 @@ bool CoreEngine::ExecuteWorkListWithInitialState(const LocationContext *L, } void CoreEngine::HandleBlockEdge(const BlockEdge &L, ExplodedNode *Pred) { - const CFGBlock *Blk = L.getDst(); NodeBuilderContext BuilderCtx(*this, Blk, Pred); @@ -306,18 +217,14 @@ void CoreEngine::HandleBlockEdge(const BlockEdge &L, ExplodedNode *Pred) { // Check if we are entering the EXIT block. if (Blk == &(L.getLocationContext()->getCFG()->getExit())) { - - assert (L.getLocationContext()->getCFG()->getExit().size() == 0 - && "EXIT block cannot contain Stmts."); + assert(L.getLocationContext()->getCFG()->getExit().empty() && + "EXIT block cannot contain Stmts."); // Get return statement.. const ReturnStmt *RS = nullptr; if (!L.getSrc()->empty()) { if (Optional<CFGStmt> LastStmt = L.getSrc()->back().getAs<CFGStmt>()) { - if ((RS = dyn_cast<ReturnStmt>(LastStmt->getStmt()))) { - if (!RS->getRetValue()) - RS = nullptr; - } + RS = dyn_cast<ReturnStmt>(LastStmt->getStmt()); } } @@ -345,12 +252,11 @@ void CoreEngine::HandleBlockEdge(const BlockEdge &L, ExplodedNode *Pred) { void CoreEngine::HandleBlockEntrance(const BlockEntrance &L, ExplodedNode *Pred) { - // Increment the block counter. const LocationContext *LC = Pred->getLocationContext(); unsigned BlockId = L.getBlock()->getBlockID(); BlockCounter Counter = WList->getBlockCounter(); - Counter = BCounterFactory.IncrementCount(Counter, LC->getCurrentStackFrame(), + Counter = BCounterFactory.IncrementCount(Counter, LC->getStackFrame(), BlockId); WList->setBlockCounter(Counter); @@ -364,7 +270,6 @@ void CoreEngine::HandleBlockEntrance(const BlockEntrance &L, } void CoreEngine::HandleBlockExit(const CFGBlock * B, ExplodedNode *Pred) { - if (const Stmt *Term = B->getTerminator()) { switch (Term->getStmtClass()) { default: @@ -397,7 +302,7 @@ void CoreEngine::HandleBlockExit(const CFGBlock * B, ExplodedNode *Pred) { HandleBranch(cast<ChooseExpr>(Term)->getCond(), Term, B, Pred); return; - case Stmt::CXXTryStmtClass: { + case Stmt::CXXTryStmtClass: // Generate a node for each of the successors. // Our logic for EH analysis can certainly be improved. for (CFGBlock::const_succ_iterator it = B->succ_begin(), @@ -408,7 +313,6 @@ void CoreEngine::HandleBlockExit(const CFGBlock * B, ExplodedNode *Pred) { } } return; - } case Stmt::DoStmtClass: HandleBranch(cast<DoStmt>(Term)->getCond(), Term, B, Pred); @@ -433,7 +337,7 @@ void CoreEngine::HandleBlockExit(const CFGBlock * B, ExplodedNode *Pred) { case Stmt::IndirectGotoStmtClass: { // Only 1 successor: the indirect goto dispatch block. - assert (B->succ_size() == 1); + assert(B->succ_size() == 1); IndirectGotoNodeBuilder builder(Pred, B, cast<IndirectGotoStmt>(Term)->getTarget(), @@ -443,7 +347,7 @@ void CoreEngine::HandleBlockExit(const CFGBlock * B, ExplodedNode *Pred) { return; } - case Stmt::ObjCForCollectionStmtClass: { + case Stmt::ObjCForCollectionStmtClass: // In the case of ObjCForCollectionStmt, it appears twice in a CFG: // // (1) inside a basic block, which represents the binding of the @@ -456,7 +360,6 @@ void CoreEngine::HandleBlockExit(const CFGBlock * B, ExplodedNode *Pred) { // contain nil elements. HandleBranch(Term, Term, B, Pred); return; - } case Stmt::SwitchStmtClass: { SwitchNodeBuilder builder(Pred, B, cast<SwitchStmt>(Term)->getCond(), @@ -472,8 +375,8 @@ void CoreEngine::HandleBlockExit(const CFGBlock * B, ExplodedNode *Pred) { } } - assert (B->succ_size() == 1 && - "Blocks with no terminator should have at most 1 successor."); + assert(B->succ_size() == 1 && + "Blocks with no terminator should have at most 1 successor."); generateNode(BlockEdge(B, *(B->succ_begin()), Pred->getLocationContext()), Pred->State, Pred); @@ -518,9 +421,8 @@ void CoreEngine::HandleStaticInit(const DeclStmt *DS, const CFGBlock *B, enqueue(Dst); } - void CoreEngine::HandlePostStmt(const CFGBlock *B, unsigned StmtIdx, - ExplodedNode *Pred) { + ExplodedNode *Pred) { assert(B); assert(!B->empty()); @@ -537,14 +439,13 @@ void CoreEngine::HandlePostStmt(const CFGBlock *B, unsigned StmtIdx, void CoreEngine::generateNode(const ProgramPoint &Loc, ProgramStateRef State, ExplodedNode *Pred) { - bool IsNew; ExplodedNode *Node = G.getNode(Loc, State, false, &IsNew); if (Pred) Node->addPredecessor(Pred, G); // Link 'Node' with its predecessor. else { - assert (IsNew); + assert(IsNew); G.addRoot(Node); // 'Node' has no predecessor. Make it a root. } @@ -555,7 +456,7 @@ void CoreEngine::generateNode(const ProgramPoint &Loc, void CoreEngine::enqueueStmtNode(ExplodedNode *N, const CFGBlock *Block, unsigned Idx) { assert(Block); - assert (!N->isSink()); + assert(!N->isSink()); // Check if this node entered a callee. if (N->getLocation().getAs<CallEnter>()) { @@ -605,8 +506,7 @@ void CoreEngine::enqueueStmtNode(ExplodedNode *N, ExplodedNode *CoreEngine::generateCallExitBeginNode(ExplodedNode *N, const ReturnStmt *RS) { // Create a CallExitBegin node and enqueue it. - const StackFrameContext *LocCtx - = cast<StackFrameContext>(N->getLocationContext()); + const auto *LocCtx = cast<StackFrameContext>(N->getLocationContext()); // Use the callee location context. CallExitBegin Loc(LocCtx, RS); @@ -617,40 +517,33 @@ ExplodedNode *CoreEngine::generateCallExitBeginNode(ExplodedNode *N, return isNew ? Node : nullptr; } - void CoreEngine::enqueue(ExplodedNodeSet &Set) { - for (ExplodedNodeSet::iterator I = Set.begin(), - E = Set.end(); I != E; ++I) { - WList->enqueue(*I); - } + for (const auto I : Set) + WList->enqueue(I); } void CoreEngine::enqueue(ExplodedNodeSet &Set, const CFGBlock *Block, unsigned Idx) { - for (ExplodedNodeSet::iterator I = Set.begin(), - E = Set.end(); I != E; ++I) { - enqueueStmtNode(*I, Block, Idx); - } + for (const auto I : Set) + enqueueStmtNode(I, Block, Idx); } void CoreEngine::enqueueEndOfFunction(ExplodedNodeSet &Set, const ReturnStmt *RS) { - for (ExplodedNodeSet::iterator I = Set.begin(), E = Set.end(); I != E; ++I) { - ExplodedNode *N = *I; + for (auto I : Set) { // If we are in an inlined call, generate CallExitBegin node. - if (N->getLocationContext()->getParent()) { - N = generateCallExitBeginNode(N, RS); - if (N) - WList->enqueue(N); + if (I->getLocationContext()->getParent()) { + I = generateCallExitBeginNode(I, RS); + if (I) + WList->enqueue(I); } else { // TODO: We should run remove dead bindings here. - G.addEndOfPath(N); + G.addEndOfPath(I); NumPathsExplored++; } } } - -void NodeBuilder::anchor() { } +void NodeBuilder::anchor() {} ExplodedNode* NodeBuilder::generateNodeImpl(const ProgramPoint &Loc, ProgramStateRef State, @@ -671,16 +564,15 @@ ExplodedNode* NodeBuilder::generateNodeImpl(const ProgramPoint &Loc, return N; } -void NodeBuilderWithSinks::anchor() { } +void NodeBuilderWithSinks::anchor() {} StmtNodeBuilder::~StmtNodeBuilder() { if (EnclosingBldr) - for (ExplodedNodeSet::iterator I = Frontier.begin(), - E = Frontier.end(); I != E; ++I ) - EnclosingBldr->addNodes(*I); + for (const auto I : Frontier) + EnclosingBldr->addNodes(I); } -void BranchNodeBuilder::anchor() { } +void BranchNodeBuilder::anchor() {} ExplodedNode *BranchNodeBuilder::generateNode(ProgramStateRef State, bool branch, @@ -714,11 +606,9 @@ IndirectGotoNodeBuilder::generateNode(const iterator &I, return Succ; } - ExplodedNode* SwitchNodeBuilder::generateCaseStmtNode(const iterator &I, ProgramStateRef St) { - bool IsNew; ExplodedNode *Succ = Eng.G.getNode(BlockEdge(Src, I.getBlock(), Pred->getLocationContext()), @@ -731,7 +621,6 @@ SwitchNodeBuilder::generateCaseStmtNode(const iterator &I, return Succ; } - ExplodedNode* SwitchNodeBuilder::generateDefaultCaseNode(ProgramStateRef St, bool IsSink) { diff --git a/lib/StaticAnalyzer/Core/DynamicTypeMap.cpp b/lib/StaticAnalyzer/Core/DynamicTypeMap.cpp index a01ff36a8aae..530933916889 100644 --- a/lib/StaticAnalyzer/Core/DynamicTypeMap.cpp +++ b/lib/StaticAnalyzer/Core/DynamicTypeMap.cpp @@ -1,4 +1,4 @@ -//==- DynamicTypeMap.cpp - Dynamic Type Info related APIs ----------*- C++ -*-// +//===- DynamicTypeMap.cpp - Dynamic Type Info related APIs ----------------===// // // The LLVM Compiler Infrastructure // @@ -14,6 +14,13 @@ //===----------------------------------------------------------------------===// #include "clang/StaticAnalyzer/Core/PathSensitive/DynamicTypeMap.h" +#include "clang/Basic/LLVM.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/SymExpr.h" +#include "llvm/Support/Casting.h" +#include "llvm/Support/raw_ostream.h" +#include <cassert> namespace clang { namespace ento { @@ -28,15 +35,15 @@ DynamicTypeInfo getDynamicTypeInfo(ProgramStateRef State, return *GDMType; // Otherwise, fall back to what we know about the region. - if (const TypedRegion *TR = dyn_cast<TypedRegion>(Reg)) + if (const auto *TR = dyn_cast<TypedRegion>(Reg)) return DynamicTypeInfo(TR->getLocationType(), /*CanBeSubclass=*/false); - if (const SymbolicRegion *SR = dyn_cast<SymbolicRegion>(Reg)) { + if (const auto *SR = dyn_cast<SymbolicRegion>(Reg)) { SymbolRef Sym = SR->getSymbol(); return DynamicTypeInfo(Sym->getType()); } - return DynamicTypeInfo(); + return {}; } ProgramStateRef setDynamicTypeInfo(ProgramStateRef State, const MemRegion *Reg, @@ -47,5 +54,28 @@ 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 << " : "; + if (DTI.isValid()) { + Out << DTI.getType()->getPointeeType().getAsString(); + if (DTI.canBeASubClass()) { + Out << " (or its subclass)"; + } + } else { + Out << "Invalid type info"; + } + Out << NL; + } +} + } // namespace ento } // namespace clang diff --git a/lib/StaticAnalyzer/Core/Environment.cpp b/lib/StaticAnalyzer/Core/Environment.cpp index c6acb9d1851c..eccaee292c40 100644 --- a/lib/StaticAnalyzer/Core/Environment.cpp +++ b/lib/StaticAnalyzer/Core/Environment.cpp @@ -1,4 +1,4 @@ -//== Environment.cpp - Map from Stmt* to Locations/Values -------*- C++ -*--==// +//===- Environment.cpp - Map from Stmt* to Locations/Values ---------------===// // // The LLVM Compiler Infrastructure // @@ -11,12 +11,25 @@ // //===----------------------------------------------------------------------===// +#include "clang/StaticAnalyzer/Core/PathSensitive/Environment.h" +#include "clang/AST/Expr.h" #include "clang/AST/ExprCXX.h" -#include "clang/AST/ExprObjC.h" +#include "clang/AST/PrettyPrinter.h" +#include "clang/AST/Stmt.h" #include "clang/Analysis/AnalysisDeclContext.h" -#include "clang/Analysis/CFG.h" +#include "clang/Basic/LLVM.h" +#include "clang/Basic/LangOptions.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/SymExpr.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h" +#include "llvm/ADT/ImmutableMap.h" +#include "llvm/ADT/SmallPtrSet.h" +#include "llvm/Support/Casting.h" +#include "llvm/Support/ErrorHandling.h" #include "llvm/Support/raw_ostream.h" +#include <cassert> using namespace clang; using namespace ento; @@ -46,16 +59,16 @@ static const Expr *ignoreTransparentExprs(const Expr *E) { } static const Stmt *ignoreTransparentExprs(const Stmt *S) { - if (const Expr *E = dyn_cast<Expr>(S)) + if (const auto *E = dyn_cast<Expr>(S)) return ignoreTransparentExprs(E); return S; } EnvironmentEntry::EnvironmentEntry(const Stmt *S, const LocationContext *L) - : std::pair<const Stmt *, - const StackFrameContext *>(ignoreTransparentExprs(S), - L ? L->getCurrentStackFrame() - : nullptr) {} + : std::pair<const Stmt *, + const StackFrameContext *>(ignoreTransparentExprs(S), + L ? L->getStackFrame() + : nullptr) {} SVal Environment::lookupExpr(const EnvironmentEntry &E) const { const SVal* X = ExprBindings.lookup(E); @@ -95,7 +108,7 @@ SVal Environment::getSVal(const EnvironmentEntry &Entry, return svalBuilder.getConstantVal(cast<Expr>(S)).getValue(); case Stmt::ReturnStmtClass: { - const ReturnStmt *RS = cast<ReturnStmt>(S); + const auto *RS = cast<ReturnStmt>(S); if (const Expr *RE = RS->getRetValue()) return getSVal(EnvironmentEntry(RE, LCtx), svalBuilder); return UndefinedVal(); @@ -121,20 +134,25 @@ Environment EnvironmentManager::bindExpr(Environment Env, } namespace { + class MarkLiveCallback final : public SymbolVisitor { SymbolReaper &SymReaper; + public: MarkLiveCallback(SymbolReaper &symreaper) : SymReaper(symreaper) {} + bool VisitSymbol(SymbolRef sym) override { SymReaper.markLive(sym); return true; } + bool VisitMemRegion(const MemRegion *R) override { SymReaper.markLive(R); return true; } }; -} // end anonymous namespace + +} // namespace // removeDeadBindings: // - Remove subexpression bindings. @@ -147,7 +165,6 @@ Environment EnvironmentManager::removeDeadBindings(Environment Env, SymbolReaper &SymReaper, ProgramStateRef ST) { - // We construct a new Environment object entirely, as this is cheaper than // individually removing all the subexpression bindings (which will greatly // outnumber block-level expression bindings). @@ -156,14 +173,13 @@ EnvironmentManager::removeDeadBindings(Environment Env, MarkLiveCallback CB(SymReaper); ScanReachableSymbols RSScaner(ST, CB); - llvm::ImmutableMapRef<EnvironmentEntry,SVal> + llvm::ImmutableMapRef<EnvironmentEntry, SVal> EBMapRef(NewEnv.ExprBindings.getRootWithoutRetain(), F.getTreeFactory()); // Iterate over the block-expr bindings. for (Environment::iterator I = Env.begin(), E = Env.end(); I != E; ++I) { - const EnvironmentEntry &BlkExpr = I.getKey(); const SVal &X = I.getData(); @@ -186,28 +202,41 @@ EnvironmentManager::removeDeadBindings(Environment Env, } void Environment::print(raw_ostream &Out, const char *NL, - const char *Sep) const { - bool isFirst = true; + const char *Sep, const LocationContext *WithLC) const { + if (ExprBindings.isEmpty()) + return; + + if (!WithLC) { + // Find the freshest location context. + llvm::SmallPtrSet<const LocationContext *, 16> FoundContexts; + for (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; + for (const LocationContext *LCI = LC; LCI; LCI = LCI->getParent()) + FoundContexts.insert(LCI); + } + } + } - for (Environment::iterator I = begin(), E = end(); I != E; ++I) { - const EnvironmentEntry &En = I.getKey(); + assert(WithLC); - if (isFirst) { - Out << NL << NL - << "Expressions:" - << NL; - isFirst = false; - } else { - Out << NL; - } + LangOptions LO; // FIXME. + PrintingPolicy PP(LO); - const Stmt *S = En.getStmt(); - assert(S != nullptr && "Expected non-null Stmt"); + Out << NL << NL << "Expressions by stack frame:" << NL; + WithLC->dumpStack(Out, "", NL, Sep, [&](const LocationContext *LC) { + for (auto I : ExprBindings) { + if (I.first.getLocationContext() != LC) + continue; - Out << " (" << (const void*) En.getLocationContext() << ',' - << (const void*) S << ") "; - LangOptions LO; // FIXME. - S->printPretty(Out, nullptr, PrintingPolicy(LO)); - Out << " : " << I.getData(); - } + const Stmt *S = I.first.getStmt(); + assert(S != nullptr && "Expected non-null Stmt"); + + Out << "(" << (const void *)LC << ',' << (const void *)S << ") "; + S->printPretty(Out, nullptr, PP); + Out << " : " << I.second << NL; + } + }); } diff --git a/lib/StaticAnalyzer/Core/ExplodedGraph.cpp b/lib/StaticAnalyzer/Core/ExplodedGraph.cpp index 3bc8e09333b9..ece103d9d09a 100644 --- a/lib/StaticAnalyzer/Core/ExplodedGraph.cpp +++ b/lib/StaticAnalyzer/Core/ExplodedGraph.cpp @@ -1,4 +1,4 @@ -//=-- ExplodedGraph.cpp - Local, Path-Sens. "Exploded Graph" -*- C++ -*------=// +//===- ExplodedGraph.cpp - Local, Path-Sens. "Exploded Graph" -------------===// // // The LLVM Compiler Infrastructure // @@ -13,13 +13,24 @@ //===----------------------------------------------------------------------===// #include "clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h" +#include "clang/AST/Expr.h" +#include "clang/AST/ExprObjC.h" #include "clang/AST/ParentMap.h" #include "clang/AST/Stmt.h" +#include "clang/Analysis/ProgramPoint.h" +#include "clang/Analysis/Support/BumpVector.h" +#include "clang/Basic/LLVM.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState_Fwd.h" #include "llvm/ADT/DenseSet.h" +#include "llvm/ADT/FoldingSet.h" +#include "llvm/ADT/Optional.h" +#include "llvm/ADT/PointerUnion.h" #include "llvm/ADT/SmallVector.h" -#include "llvm/ADT/Statistic.h" +#include "llvm/Support/Casting.h" +#include <cassert> +#include <memory> using namespace clang; using namespace ento; @@ -29,7 +40,7 @@ using namespace ento; //===----------------------------------------------------------------------===// // An out of line virtual method to provide a home for the class vtable. -ExplodedNode::Auditor::~Auditor() {} +ExplodedNode::Auditor::~Auditor() = default; #ifndef NDEBUG static ExplodedNode::Auditor* NodeAuditor = nullptr; @@ -45,10 +56,9 @@ void ExplodedNode::SetAuditor(ExplodedNode::Auditor* A) { // Cleanup. //===----------------------------------------------------------------------===// -ExplodedGraph::ExplodedGraph() - : NumNodes(0), ReclaimNodeInterval(0) {} +ExplodedGraph::ExplodedGraph() = default; -ExplodedGraph::~ExplodedGraph() {} +ExplodedGraph::~ExplodedGraph() = default; //===----------------------------------------------------------------------===// // Node reclamation. @@ -187,12 +197,9 @@ void ExplodedGraph::reclaimRecentlyAllocatedNodes() { return; ReclaimCounter = ReclaimNodeInterval; - for (NodeVector::iterator it = ChangedNodes.begin(), et = ChangedNodes.end(); - it != et; ++it) { - ExplodedNode *node = *it; + for (const auto node : ChangedNodes) if (shouldCollect(node)) collectNode(node); - } ChangedNodes.clear(); } @@ -210,11 +217,11 @@ void ExplodedGraph::reclaimRecentlyAllocatedNodes() { // 2. The group is empty, in which case the storage value is null. // 3. The group contains a single node. // 4. The group contains more than one node. -typedef BumpVector<ExplodedNode *> ExplodedNodeVector; -typedef llvm::PointerUnion<ExplodedNode *, ExplodedNodeVector *> GroupStorage; +using ExplodedNodeVector = BumpVector<ExplodedNode *>; +using GroupStorage = llvm::PointerUnion<ExplodedNode *, ExplodedNodeVector *>; void ExplodedNode::addPredecessor(ExplodedNode *V, ExplodedGraph &G) { - assert (!V->isSink()); + assert(!V->isSink()); Preds.addNode(V, G); V->Succs.addNode(this, G); #ifndef NDEBUG @@ -346,25 +353,22 @@ std::unique_ptr<ExplodedGraph> ExplodedGraph::trim(ArrayRef<const NodeTy *> Sinks, InterExplodedGraphMap *ForwardMap, InterExplodedGraphMap *InverseMap) const { - if (Nodes.empty()) return nullptr; - typedef llvm::DenseSet<const ExplodedNode*> Pass1Ty; + using Pass1Ty = llvm::DenseSet<const ExplodedNode *>; Pass1Ty Pass1; - typedef InterExplodedGraphMap Pass2Ty; + using Pass2Ty = InterExplodedGraphMap; InterExplodedGraphMap Pass2Scratch; Pass2Ty &Pass2 = ForwardMap ? *ForwardMap : Pass2Scratch; SmallVector<const ExplodedNode*, 10> WL1, WL2; // ===- Pass 1 (reverse DFS) -=== - for (ArrayRef<const NodeTy *>::iterator I = Sinks.begin(), E = Sinks.end(); - I != E; ++I) { - if (*I) - WL1.push_back(*I); - } + for (const auto Sink : Sinks) + if (Sink) + WL1.push_back(Sink); // Process the first worklist until it is empty. while (!WL1.empty()) { @@ -445,4 +449,3 @@ ExplodedGraph::trim(ArrayRef<const NodeTy *> Sinks, return G; } - diff --git a/lib/StaticAnalyzer/Core/ExprEngine.cpp b/lib/StaticAnalyzer/Core/ExprEngine.cpp index 3be37e7ae301..188316c096e3 100644 --- a/lib/StaticAnalyzer/Core/ExprEngine.cpp +++ b/lib/StaticAnalyzer/Core/ExprEngine.cpp @@ -1,4 +1,4 @@ -//=-- ExprEngine.cpp - Path-Sensitive Expression-Level Dataflow ---*- C++ -*-= +//===- ExprEngine.cpp - Path-Sensitive Expression-Level Dataflow ----------===// // // The LLVM Compiler Infrastructure // @@ -15,31 +15,75 @@ #include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h" #include "PrettyStackTraceLocationContext.h" -#include "clang/AST/CharUnits.h" +#include "clang/AST/ASTContext.h" +#include "clang/AST/Decl.h" +#include "clang/AST/DeclBase.h" +#include "clang/AST/DeclCXX.h" +#include "clang/AST/DeclObjC.h" +#include "clang/AST/Expr.h" +#include "clang/AST/ExprCXX.h" +#include "clang/AST/ExprObjC.h" #include "clang/AST/ParentMap.h" -#include "clang/Analysis/CFGStmtMap.h" +#include "clang/AST/PrettyPrinter.h" +#include "clang/AST/Stmt.h" #include "clang/AST/StmtCXX.h" #include "clang/AST/StmtObjC.h" -#include "clang/Basic/Builtins.h" +#include "clang/AST/Type.h" +#include "clang/Analysis/AnalysisDeclContext.h" +#include "clang/Analysis/CFG.h" +#include "clang/Analysis/ConstructionContext.h" +#include "clang/Analysis/ProgramPoint.h" +#include "clang/Basic/IdentifierTable.h" +#include "clang/Basic/LLVM.h" +#include "clang/Basic/LangOptions.h" #include "clang/Basic/PrettyStackTrace.h" +#include "clang/Basic/SourceLocation.h" #include "clang/Basic/SourceManager.h" +#include "clang/Basic/Specifiers.h" +#include "clang/StaticAnalyzer/Core/AnalyzerOptions.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" #include "clang/StaticAnalyzer/Core/CheckerManager.h" #include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/LoopWidening.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ConstraintManager.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CoreEngine.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h" #include "clang/StaticAnalyzer/Core/PathSensitive/LoopUnrolling.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/LoopWidening.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState_Fwd.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/Store.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/SymExpr.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h" +#include "llvm/ADT/APSInt.h" +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/ImmutableMap.h" +#include "llvm/ADT/ImmutableSet.h" +#include "llvm/ADT/Optional.h" +#include "llvm/ADT/SmallVector.h" #include "llvm/ADT/Statistic.h" +#include "llvm/Support/Casting.h" +#include "llvm/Support/Compiler.h" +#include "llvm/Support/DOTGraphTraits.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/GraphWriter.h" #include "llvm/Support/SaveAndRestore.h" #include "llvm/Support/raw_ostream.h" - -#ifndef NDEBUG -#include "llvm/Support/GraphWriter.h" -#endif +#include <cassert> +#include <cstdint> +#include <memory> +#include <string> +#include <tuple> +#include <utility> +#include <vector> using namespace clang; using namespace ento; -using llvm::APSInt; #define DEBUG_TYPE "ExprEngine" @@ -54,14 +98,100 @@ STATISTIC(NumMaxBlockCountReachedInInlined, STATISTIC(NumTimesRetriedWithoutInlining, "The # of times we re-evaluated a call without inlining"); -typedef std::pair<const CXXBindTemporaryExpr *, const StackFrameContext *> - CXXBindTemporaryContext; -// Keeps track of whether CXXBindTemporaryExpr nodes have been evaluated. -// The StackFrameContext assures that nested calls due to inlined recursive -// functions do not interfere. -REGISTER_TRAIT_WITH_PROGRAMSTATE(InitializedTemporariesSet, - llvm::ImmutableSet<CXXBindTemporaryContext>) +//===----------------------------------------------------------------------===// +// Internal program state traits. +//===----------------------------------------------------------------------===// + +// When modeling a C++ constructor, for a variety of reasons we need to track +// the location of the object for the duration of its ConstructionContext. +// ObjectsUnderConstruction maps statements within the construction context +// to the object's location, so that on every such statement the location +// could have been retrieved. + +/// ConstructedObjectKey is used for being able to find the path-sensitive +/// memory region of a freshly constructed object while modeling the AST node +/// that syntactically represents the object that is being constructed. +/// Semantics of such nodes may sometimes require access to the region that's +/// not otherwise present in the program state, or to the very fact that +/// the construction context was present and contained references to these +/// AST nodes. +class ConstructedObjectKey { + typedef std::pair< + llvm::PointerUnion<const Stmt *, const CXXCtorInitializer *>, + const LocationContext *> ConstructedObjectKeyImpl; + + ConstructedObjectKeyImpl Impl; + + const void *getAnyASTNodePtr() const { + if (const Stmt *S = getStmt()) + return S; + else + return getCXXCtorInitializer(); + } + +public: + ConstructedObjectKey( + llvm::PointerUnion<const Stmt *, const CXXCtorInitializer *> P, + const LocationContext *LC) + : Impl(P, LC) { + // This is the full list of statements that require additional actions when + // encountered. This list may be expanded when new actions are implemented. + assert(getCXXCtorInitializer() || isa<DeclStmt>(getStmt()) || + isa<CXXNewExpr>(getStmt()) || isa<CXXBindTemporaryExpr>(getStmt()) || + isa<MaterializeTemporaryExpr>(getStmt()) || + isa<CXXConstructExpr>(getStmt())); + } + + const Stmt *getStmt() const { + return Impl.first.dyn_cast<const Stmt *>(); + } + + const CXXCtorInitializer *getCXXCtorInitializer() const { + return Impl.first.dyn_cast<const CXXCtorInitializer *>(); + } + + const LocationContext *getLocationContext() const { + return Impl.second; + } + + void print(llvm::raw_ostream &OS, PrinterHelper *Helper, PrintingPolicy &PP) { + OS << '(' << getLocationContext() << ',' << getAnyASTNodePtr() << ") "; + if (const Stmt *S = getStmt()) { + S->printPretty(OS, Helper, PP); + } else { + const CXXCtorInitializer *I = getCXXCtorInitializer(); + OS << I->getAnyMember()->getNameAsString(); + } + } + + void Profile(llvm::FoldingSetNodeID &ID) const { + ID.AddPointer(Impl.first.getOpaqueValue()); + ID.AddPointer(Impl.second); + } + + bool operator==(const ConstructedObjectKey &RHS) const { + return Impl == RHS.Impl; + } + + bool operator<(const ConstructedObjectKey &RHS) const { + return Impl < RHS.Impl; + } +}; + +typedef llvm::ImmutableMap<ConstructedObjectKey, SVal> + ObjectsUnderConstructionMap; +REGISTER_TRAIT_WITH_PROGRAMSTATE(ObjectsUnderConstruction, + ObjectsUnderConstructionMap) + +// Additionally, track a set of destructors that correspond to elided +// constructors when copy elision occurs. +typedef std::pair<const CXXBindTemporaryExpr *, const LocationContext *> + ElidedDestructorItem; +typedef llvm::ImmutableSet<ElidedDestructorItem> + ElidedDestructorSet; +REGISTER_TRAIT_WITH_PROGRAMSTATE(ElidedDestructors, + ElidedDestructorSet) //===----------------------------------------------------------------------===// // Engine construction and deletion. @@ -69,25 +199,21 @@ REGISTER_TRAIT_WITH_PROGRAMSTATE(InitializedTemporariesSet, static const char* TagProviderName = "ExprEngine"; -ExprEngine::ExprEngine(AnalysisManager &mgr, bool gcEnabled, +ExprEngine::ExprEngine(cross_tu::CrossTranslationUnitContext &CTU, + AnalysisManager &mgr, bool gcEnabled, SetOfConstDecls *VisitedCalleesIn, FunctionSummariesTy *FS, InliningModes HowToInlineIn) - : AMgr(mgr), - AnalysisDeclContexts(mgr.getAnalysisDeclContextManager()), - Engine(*this, FS), - G(Engine.getGraph()), - StateMgr(getContext(), mgr.getStoreManagerCreator(), - mgr.getConstraintManagerCreator(), G.getAllocator(), - this), - SymMgr(StateMgr.getSymbolManager()), - svalBuilder(StateMgr.getSValBuilder()), - currStmtIdx(0), currBldrCtx(nullptr), - ObjCNoRet(mgr.getASTContext()), - ObjCGCEnabled(gcEnabled), BR(mgr, *this), - VisitedCallees(VisitedCalleesIn), - HowToInline(HowToInlineIn) -{ + : CTU(CTU), AMgr(mgr), + AnalysisDeclContexts(mgr.getAnalysisDeclContextManager()), + Engine(*this, FS, mgr.getAnalyzerOptions()), G(Engine.getGraph()), + StateMgr(getContext(), mgr.getStoreManagerCreator(), + mgr.getConstraintManagerCreator(), G.getAllocator(), + this), + SymMgr(StateMgr.getSymbolManager()), + svalBuilder(StateMgr.getSValBuilder()), ObjCNoRet(mgr.getASTContext()), + ObjCGCEnabled(gcEnabled), BR(mgr, *this), + VisitedCallees(VisitedCalleesIn), HowToInline(HowToInlineIn) { unsigned TrimInterval = mgr.options.getGraphTrimInterval(); if (TrimInterval != 0) { // Enable eager node reclaimation when constructing the ExplodedGraph. @@ -111,8 +237,7 @@ ProgramStateRef ExprEngine::getInitialState(const LocationContext *InitLoc) { // FIXME: It would be nice if we had a more general mechanism to add // such preconditions. Some day. do { - - if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(D)) { + if (const auto *FD = dyn_cast<FunctionDecl>(D)) { // Precondition: the first argument of 'main' is an integer guaranteed // to be > 0. const IdentifierInfo *II = FD->getIdentifier(); @@ -121,7 +246,7 @@ ProgramStateRef ExprEngine::getInitialState(const LocationContext *InitLoc) { const ParmVarDecl *PD = FD->getParamDecl(0); QualType T = PD->getType(); - const BuiltinType *BT = dyn_cast<BuiltinType>(T); + const auto *BT = dyn_cast<BuiltinType>(T); if (!BT || !BT->isInteger()) break; @@ -145,9 +270,9 @@ ProgramStateRef ExprEngine::getInitialState(const LocationContext *InitLoc) { } break; } - while (0); + while (false); - if (const ObjCMethodDecl *MD = dyn_cast<ObjCMethodDecl>(D)) { + if (const auto *MD = dyn_cast<ObjCMethodDecl>(D)) { // Precondition: 'self' is always non-null upon entry to an Objective-C // method. const ImplicitParamDecl *SelfD = MD->getSelfDecl(); @@ -161,12 +286,12 @@ ProgramStateRef ExprEngine::getInitialState(const LocationContext *InitLoc) { } } - if (const CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(D)) { + if (const auto *MD = dyn_cast<CXXMethodDecl>(D)) { if (!MD->isStatic()) { // Precondition: 'this' is always non-null upon entry to the // top-level function. This is our starting assumption for // analyzing an "open" program. - const StackFrameContext *SFC = InitLoc->getCurrentStackFrame(); + const StackFrameContext *SFC = InitLoc->getStackFrame(); if (SFC->getParent() == nullptr) { loc::MemRegionVal L = svalBuilder.getCXXThis(MD, SFC); SVal V = state->getSVal(L); @@ -237,17 +362,30 @@ ExprEngine::createTemporaryRegionIfNeeded(ProgramStateRef State, const Expr *Init = InitWithAdjustments->skipRValueSubobjectAdjustments( CommaLHSs, Adjustments); + // Take the region for Init, i.e. for the whole object. If we do not remember + // the region in which the object originally was constructed, come up with + // a new temporary region out of thin air and copy the contents of the object + // (which are currently present in the Environment, because Init is an rvalue) + // into that region. This is not correct, but it is better than nothing. const TypedValueRegion *TR = nullptr; - if (const MaterializeTemporaryExpr *MT = - dyn_cast<MaterializeTemporaryExpr>(Result)) { - StorageDuration SD = MT->getStorageDuration(); - // If this object is bound to a reference with static storage duration, we - // put it in a different region to prevent "address leakage" warnings. - if (SD == SD_Static || SD == SD_Thread) - TR = MRMgr.getCXXStaticTempObjectRegion(Init); - } - if (!TR) + if (const auto *MT = dyn_cast<MaterializeTemporaryExpr>(Result)) { + if (Optional<SVal> V = getObjectUnderConstruction(State, MT, LC)) { + State = finishObjectConstruction(State, MT, LC); + State = State->BindExpr(Result, LC, *V); + return State; + } else { + StorageDuration SD = MT->getStorageDuration(); + // If this object is bound to a reference with static storage duration, we + // put it in a different region to prevent "address leakage" warnings. + if (SD == SD_Static || SD == SD_Thread) { + TR = MRMgr.getCXXStaticTempObjectRegion(Init); + } else { + TR = MRMgr.getCXXTempObjectRegion(Init, LC); + } + } + } else { TR = MRMgr.getCXXTempObjectRegion(Init, LC); + } SVal Reg = loc::MemRegionVal(TR); SVal BaseReg = Reg; @@ -264,7 +402,9 @@ ExprEngine::createTemporaryRegionIfNeeded(ProgramStateRef State, break; case SubobjectAdjustment::MemberPointerAdjustment: // FIXME: Unimplemented. - State = State->bindDefault(Reg, UnknownVal(), LC); + State = State->invalidateRegions(Reg, InitWithAdjustments, + currBldrCtx->blockCount(), LC, true, + nullptr, nullptr, nullptr); return State; } } @@ -283,7 +423,8 @@ ExprEngine::createTemporaryRegionIfNeeded(ProgramStateRef State, currBldrCtx->blockCount()); State = State->bindLoc(BaseReg.castAs<Loc>(), InitVal, LC, false); - // Then we'd need to take the value that certainly exists and bind it over. + // Then we'd need to take the value that certainly exists and bind it + // over. if (InitValWithAdjustments.isUnknown()) { // Try to recover some path sensitivity in case we couldn't // compute the value. @@ -308,6 +449,79 @@ ExprEngine::createTemporaryRegionIfNeeded(ProgramStateRef State, return State; } +ProgramStateRef ExprEngine::addObjectUnderConstruction( + ProgramStateRef State, + llvm::PointerUnion<const Stmt *, const CXXCtorInitializer *> P, + const LocationContext *LC, SVal V) { + ConstructedObjectKey Key(P, LC->getStackFrame()); + // FIXME: Currently the state might already contain the marker due to + // incorrect handling of temporaries bound to default parameters. + assert(!State->get<ObjectsUnderConstruction>(Key) || + isa<CXXBindTemporaryExpr>(Key.getStmt())); + return State->set<ObjectsUnderConstruction>(Key, V); +} + +Optional<SVal> ExprEngine::getObjectUnderConstruction( + ProgramStateRef State, + llvm::PointerUnion<const Stmt *, const CXXCtorInitializer *> P, + const LocationContext *LC) { + ConstructedObjectKey Key(P, LC->getStackFrame()); + return Optional<SVal>::create(State->get<ObjectsUnderConstruction>(Key)); +} + +ProgramStateRef ExprEngine::finishObjectConstruction( + ProgramStateRef State, + llvm::PointerUnion<const Stmt *, const CXXCtorInitializer *> P, + const LocationContext *LC) { + ConstructedObjectKey Key(P, LC->getStackFrame()); + assert(State->contains<ObjectsUnderConstruction>(Key)); + return State->remove<ObjectsUnderConstruction>(Key); +} + +ProgramStateRef ExprEngine::elideDestructor(ProgramStateRef State, + const CXXBindTemporaryExpr *BTE, + const LocationContext *LC) { + ElidedDestructorItem I(BTE, LC); + assert(!State->contains<ElidedDestructors>(I)); + return State->add<ElidedDestructors>(I); +} + +ProgramStateRef +ExprEngine::cleanupElidedDestructor(ProgramStateRef State, + const CXXBindTemporaryExpr *BTE, + const LocationContext *LC) { + ElidedDestructorItem I(BTE, LC); + assert(State->contains<ElidedDestructors>(I)); + return State->remove<ElidedDestructors>(I); +} + +bool ExprEngine::isDestructorElided(ProgramStateRef State, + const CXXBindTemporaryExpr *BTE, + const LocationContext *LC) { + ElidedDestructorItem I(BTE, LC); + return State->contains<ElidedDestructors>(I); +} + +bool ExprEngine::areAllObjectsFullyConstructed(ProgramStateRef State, + const LocationContext *FromLC, + const LocationContext *ToLC) { + const LocationContext *LC = FromLC; + while (LC != ToLC) { + assert(LC && "ToLC must be a parent of FromLC!"); + for (auto I : State->get<ObjectsUnderConstruction>()) + if (I.first.getLocationContext() == LC) + return false; + + for (auto I: State->get<ElidedDestructors>()) + if (I.second == LC) + return false; + + LC = LC->getParent(); + } + return true; +} + + //===----------------------------------------------------------------------===// // Top-level transfer function logic (Dispatcher). //===----------------------------------------------------------------------===// @@ -331,8 +545,44 @@ ExprEngine::processRegionChanges(ProgramStateRef state, LCtx, Call); } +static void printObjectsUnderConstructionForContext(raw_ostream &Out, + ProgramStateRef State, + const char *NL, + const char *Sep, + const LocationContext *LC) { + PrintingPolicy PP = + LC->getAnalysisDeclContext()->getASTContext().getPrintingPolicy(); + for (auto I : State->get<ObjectsUnderConstruction>()) { + ConstructedObjectKey Key = I.first; + SVal Value = I.second; + if (Key.getLocationContext() != LC) + continue; + Key.print(Out, nullptr, PP); + Out << " : " << Value << NL; + } + + for (auto I : State->get<ElidedDestructors>()) { + if (I.second != LC) + continue; + Out << '(' << I.second << ',' << (const void *)I.first << ") "; + I.first->printPretty(Out, nullptr, PP); + Out << " : (constructor elided)" << NL; + } +} + void ExprEngine::printState(raw_ostream &Out, ProgramStateRef State, - const char *NL, const char *Sep) { + const char *NL, const char *Sep, + const LocationContext *LCtx) { + if (LCtx) { + if (!State->get<ObjectsUnderConstruction>().isEmpty()) { + Out << Sep << "Objects under construction:" << NL; + + LCtx->dumpStack(Out, "", NL, Sep, [&](const LocationContext *LC) { + printObjectsUnderConstructionForContext(Out, State, NL, Sep, LC); + }); + } + } + getCheckerManager().runCheckersForPrintState(Out, State, NL, Sep); } @@ -348,10 +598,12 @@ void ExprEngine::processCFGElement(const CFGElement E, ExplodedNode *Pred, switch (E.getKind()) { case CFGElement::Statement: - ProcessStmt(const_cast<Stmt*>(E.castAs<CFGStmt>().getStmt()), Pred); + case CFGElement::Constructor: + case CFGElement::CXXRecordTypedCall: + ProcessStmt(E.castAs<CFGStmt>().getStmt(), Pred); return; case CFGElement::Initializer: - ProcessInitializer(E.castAs<CFGInitializer>().getInitializer(), Pred); + ProcessInitializer(E.castAs<CFGInitializer>(), Pred); return; case CFGElement::NewAllocator: ProcessNewAllocator(E.castAs<CFGNewAllocator>().getAllocatorExpr(), @@ -368,15 +620,16 @@ void ExprEngine::processCFGElement(const CFGElement E, ExplodedNode *Pred, ProcessLoopExit(E.castAs<CFGLoopExit>().getLoopStmt(), Pred); return; case CFGElement::LifetimeEnds: + case CFGElement::ScopeBegin: + case CFGElement::ScopeEnd: return; } } static bool shouldRemoveDeadBindings(AnalysisManager &AMgr, - const CFGStmt S, + const Stmt *S, const ExplodedNode *Pred, const LocationContext *LC) { - // Are we never purging state values? if (AMgr.options.AnalysisPurgeOpt == PurgeNone) return false; @@ -386,17 +639,17 @@ static bool shouldRemoveDeadBindings(AnalysisManager &AMgr, return true; // Is this on a non-expression? - if (!isa<Expr>(S.getStmt())) + if (!isa<Expr>(S)) return true; // Run before processing a call. - if (CallEvent::isCallStmt(S.getStmt())) + if (CallEvent::isCallStmt(S)) return true; // Is this an expression that is consumed by another expression? If so, // postpone cleaning out the state. ParentMap &PM = LC->getAnalysisDeclContext()->getParentMap(); - return !PM.isConsumedExpr(cast<Expr>(S.getStmt())); + return !PM.isConsumedExpr(cast<Expr>(S)); } void ExprEngine::removeDead(ExplodedNode *Pred, ExplodedNodeSet &Out, @@ -426,9 +679,16 @@ void ExprEngine::removeDead(ExplodedNode *Pred, ExplodedNodeSet &Out, LC = LC->getParent(); } - const StackFrameContext *SFC = LC ? LC->getCurrentStackFrame() : nullptr; + const StackFrameContext *SFC = LC ? LC->getStackFrame() : nullptr; SymbolReaper SymReaper(SFC, ReferenceStmt, SymMgr, getStoreManager()); + for (auto I : CleanedState->get<ObjectsUnderConstruction>()) { + if (SymbolRef Sym = I.second.getAsSymbol()) + SymReaper.markLive(Sym); + if (const MemRegion *MR = I.second.getAsRegion()) + SymReaper.markLive(MR); + } + getCheckerManager().runCheckersForLiveSymbols(CleanedState, SymReaper); // Create a state in which dead bindings are removed from the environment @@ -457,9 +717,8 @@ void ExprEngine::removeDead(ExplodedNode *Pred, ExplodedNodeSet &Out, // environment, the store, and the constraints cleaned up but have the // user-supplied states as the predecessors. StmtNodeBuilder Bldr(CheckedSet, Out, *currBldrCtx); - for (ExplodedNodeSet::const_iterator - I = CheckedSet.begin(), E = CheckedSet.end(); I != E; ++I) { - ProgramStateRef CheckerState = (*I)->getState(); + for (const auto I : CheckedSet) { + ProgramStateRef CheckerState = I->getState(); // The constraint manager has not been cleaned up yet, so clean up now. CheckerState = getConstraintManager().removeDeadBindings(CheckerState, @@ -476,35 +735,34 @@ void ExprEngine::removeDead(ExplodedNode *Pred, ExplodedNodeSet &Out, // generate a transition to that state. ProgramStateRef CleanedCheckerSt = StateMgr.getPersistentStateWithGDM(CleanedState, CheckerState); - Bldr.generateNode(DiagnosticStmt, *I, CleanedCheckerSt, &cleanupTag, K); + Bldr.generateNode(DiagnosticStmt, I, CleanedCheckerSt, &cleanupTag, K); } } } -void ExprEngine::ProcessStmt(const CFGStmt S, - ExplodedNode *Pred) { +void ExprEngine::ProcessStmt(const Stmt *currStmt, ExplodedNode *Pred) { // Reclaim any unnecessary nodes in the ExplodedGraph. G.reclaimRecentlyAllocatedNodes(); - const Stmt *currStmt = S.getStmt(); PrettyStackTraceLoc CrashInfo(getContext().getSourceManager(), currStmt->getLocStart(), "Error evaluating statement"); // Remove dead bindings and symbols. ExplodedNodeSet CleanedStates; - if (shouldRemoveDeadBindings(AMgr, S, Pred, Pred->getLocationContext())){ - removeDead(Pred, CleanedStates, currStmt, Pred->getLocationContext()); + if (shouldRemoveDeadBindings(AMgr, currStmt, Pred, + Pred->getLocationContext())) { + removeDead(Pred, CleanedStates, currStmt, + Pred->getLocationContext()); } else CleanedStates.Add(Pred); // Visit the statement. ExplodedNodeSet Dst; - for (ExplodedNodeSet::iterator I = CleanedStates.begin(), - E = CleanedStates.end(); I != E; ++I) { + for (const auto I : CleanedStates) { ExplodedNodeSet DstI; // Visit the statement. - Visit(currStmt, *I, DstI); + Visit(currStmt, I, DstI); Dst.insert(DstI); } @@ -530,36 +788,38 @@ void ExprEngine::ProcessLoopExit(const Stmt* S, ExplodedNode *Pred) { Engine.enqueue(Dst, currBldrCtx->getBlock(), currStmtIdx); } -void ExprEngine::ProcessInitializer(const CFGInitializer Init, +void ExprEngine::ProcessInitializer(const CFGInitializer CFGInit, ExplodedNode *Pred) { - const CXXCtorInitializer *BMI = Init.getInitializer(); + const CXXCtorInitializer *BMI = CFGInit.getInitializer(); + const Expr *Init = BMI->getInit()->IgnoreImplicit(); + const LocationContext *LC = Pred->getLocationContext(); PrettyStackTraceLoc CrashInfo(getContext().getSourceManager(), BMI->getSourceLocation(), "Error evaluating initializer"); // We don't clean up dead bindings here. - const StackFrameContext *stackFrame = - cast<StackFrameContext>(Pred->getLocationContext()); - const CXXConstructorDecl *decl = - cast<CXXConstructorDecl>(stackFrame->getDecl()); + const auto *stackFrame = cast<StackFrameContext>(Pred->getLocationContext()); + const auto *decl = cast<CXXConstructorDecl>(stackFrame->getDecl()); ProgramStateRef State = Pred->getState(); SVal thisVal = State->getSVal(svalBuilder.getCXXThis(decl, stackFrame)); - ExplodedNodeSet Tmp(Pred); + ExplodedNodeSet Tmp; SVal FieldLoc; // Evaluate the initializer, if necessary if (BMI->isAnyMemberInitializer()) { // Constructors build the object directly in the field, // but non-objects must be copied in from the initializer. - if (auto *CtorExpr = findDirectConstructorForCurrentCFGElement()) { - assert(BMI->getInit()->IgnoreImplicit() == CtorExpr); - (void)CtorExpr; + if (getObjectUnderConstruction(State, BMI, LC)) { // The field was directly constructed, so there is no need to bind. + // But we still need to stop tracking the object under construction. + State = finishObjectConstruction(State, BMI, LC); + NodeBuilder Bldr(Pred, Tmp, *currBldrCtx); + PostStore PS(Init, LC, /*Loc*/ nullptr, /*tag*/ nullptr); + Bldr.generateNode(PS, State, Pred); } else { - const Expr *Init = BMI->getInit()->IgnoreImplicit(); const ValueDecl *Field; if (BMI->isIndirectMemberInitializer()) { Field = BMI->getIndirectMember(); @@ -593,15 +853,12 @@ void ExprEngine::ProcessInitializer(const CFGInitializer Init, InitVal = State->getSVal(BMI->getInit(), stackFrame); } - assert(Tmp.size() == 1 && "have not generated any new nodes yet"); - assert(*Tmp.begin() == Pred && "have not generated any new nodes yet"); - Tmp.clear(); - PostInitializer PP(BMI, FieldLoc.getAsRegion(), stackFrame); evalBind(Tmp, Init, Pred, FieldLoc, InitVal, /*isInit=*/true, &PP); } } else { assert(BMI->isBaseInitializer() || BMI->isDelegatingInitializer()); + Tmp.insert(Pred); // We already did all the work when visiting the CXXConstructExpr. } @@ -610,9 +867,9 @@ void ExprEngine::ProcessInitializer(const CFGInitializer Init, PostInitializer PP(BMI, FieldLoc.getAsRegion(), stackFrame); ExplodedNodeSet Dst; NodeBuilder Bldr(Tmp, Dst, *currBldrCtx); - for (ExplodedNodeSet::iterator I = Tmp.begin(), E = Tmp.end(); I != E; ++I) { - ExplodedNode *N = *I; - Bldr.generateNode(PP, N->getState(), N); + for (const auto I : Tmp) { + ProgramStateRef State = I->getState(); + Bldr.generateNode(PP, State, I); } // Enqueue the new nodes onto the work list. @@ -688,8 +945,15 @@ void ExprEngine::ProcessAutomaticObjDtor(const CFGAutomaticObjDtor Dtor, varType = cast<TypedValueRegion>(Region)->getValueType(); } + // FIXME: We need to run the same destructor on every element of the array. + // This workaround will just run the first destructor (which will still + // invalidate the entire array). + EvalCallOptions CallOpts; + Region = makeZeroElementRegion(state, loc::MemRegionVal(Region), varType, + CallOpts.IsArrayCtorOrDtor).getAsRegion(); + VisitCXXDestructor(varType, Region, Dtor.getTriggerStmt(), /*IsBase=*/ false, - Pred, Dst); + Pred, Dst, CallOpts); } void ExprEngine::ProcessDeleteDtor(const CFGDeleteDtor Dtor, @@ -699,12 +963,12 @@ void ExprEngine::ProcessDeleteDtor(const CFGDeleteDtor Dtor, const LocationContext *LCtx = Pred->getLocationContext(); const CXXDeleteExpr *DE = Dtor.getDeleteExpr(); const Stmt *Arg = DE->getArgument(); + QualType DTy = DE->getDestroyedType(); SVal ArgVal = State->getSVal(Arg, LCtx); // If the argument to delete is known to be a null value, // don't run destructor. if (State->isNull(ArgVal).isConstrainedTrue()) { - QualType DTy = DE->getDestroyedType(); QualType BTy = getContext().getBaseElementType(DTy); const CXXRecordDecl *RD = BTy->getAsCXXRecordDecl(); const CXXDestructorDecl *Dtor = RD->getDestructor(); @@ -715,19 +979,30 @@ void ExprEngine::ProcessDeleteDtor(const CFGDeleteDtor Dtor, return; } - VisitCXXDestructor(DE->getDestroyedType(), - ArgVal.getAsRegion(), - DE, /*IsBase=*/ false, - Pred, Dst); + EvalCallOptions CallOpts; + const MemRegion *ArgR = ArgVal.getAsRegion(); + if (DE->isArrayForm()) { + // FIXME: We need to run the same destructor on every element of the array. + // This workaround will just run the first destructor (which will still + // invalidate the entire array). + CallOpts.IsArrayCtorOrDtor = true; + // Yes, it may even be a multi-dimensional array. + while (const auto *AT = getContext().getAsArrayType(DTy)) + DTy = AT->getElementType(); + if (ArgR) + ArgR = getStoreManager().GetElementZeroRegion(cast<SubRegion>(ArgR), DTy); + } + + VisitCXXDestructor(DTy, ArgR, DE, /*IsBase=*/false, Pred, Dst, CallOpts); } void ExprEngine::ProcessBaseDtor(const CFGBaseDtor D, ExplodedNode *Pred, ExplodedNodeSet &Dst) { const LocationContext *LCtx = Pred->getLocationContext(); - const CXXDestructorDecl *CurDtor = cast<CXXDestructorDecl>(LCtx->getDecl()); + const auto *CurDtor = cast<CXXDestructorDecl>(LCtx->getDecl()); Loc ThisPtr = getSValBuilder().getCXXThis(CurDtor, - LCtx->getCurrentStackFrame()); + LCtx->getStackFrame()); SVal ThisVal = Pred->getState()->getSVal(ThisPtr); // Create the base object region. @@ -737,51 +1012,94 @@ void ExprEngine::ProcessBaseDtor(const CFGBaseDtor D, Base->isVirtual()); VisitCXXDestructor(BaseTy, BaseVal.castAs<loc::MemRegionVal>().getRegion(), - CurDtor->getBody(), /*IsBase=*/ true, Pred, Dst); + CurDtor->getBody(), /*IsBase=*/ true, Pred, Dst, {}); } void ExprEngine::ProcessMemberDtor(const CFGMemberDtor D, ExplodedNode *Pred, ExplodedNodeSet &Dst) { const FieldDecl *Member = D.getFieldDecl(); + QualType T = Member->getType(); ProgramStateRef State = Pred->getState(); const LocationContext *LCtx = Pred->getLocationContext(); - const CXXDestructorDecl *CurDtor = cast<CXXDestructorDecl>(LCtx->getDecl()); + const auto *CurDtor = cast<CXXDestructorDecl>(LCtx->getDecl()); Loc ThisVal = getSValBuilder().getCXXThis(CurDtor, - LCtx->getCurrentStackFrame()); + LCtx->getStackFrame()); SVal FieldVal = State->getLValue(Member, State->getSVal(ThisVal).castAs<Loc>()); - VisitCXXDestructor(Member->getType(), - FieldVal.castAs<loc::MemRegionVal>().getRegion(), - CurDtor->getBody(), /*IsBase=*/false, Pred, Dst); + // FIXME: We need to run the same destructor on every element of the array. + // This workaround will just run the first destructor (which will still + // invalidate the entire array). + EvalCallOptions CallOpts; + FieldVal = makeZeroElementRegion(State, FieldVal, T, + CallOpts.IsArrayCtorOrDtor); + + VisitCXXDestructor(T, FieldVal.castAs<loc::MemRegionVal>().getRegion(), + CurDtor->getBody(), /*IsBase=*/false, Pred, Dst, CallOpts); } void ExprEngine::ProcessTemporaryDtor(const CFGTemporaryDtor D, ExplodedNode *Pred, ExplodedNodeSet &Dst) { - ExplodedNodeSet CleanDtorState; - StmtNodeBuilder StmtBldr(Pred, CleanDtorState, *currBldrCtx); + const CXXBindTemporaryExpr *BTE = D.getBindTemporaryExpr(); ProgramStateRef State = Pred->getState(); - if (State->contains<InitializedTemporariesSet>( - std::make_pair(D.getBindTemporaryExpr(), Pred->getStackFrame()))) { + const LocationContext *LC = Pred->getLocationContext(); + const MemRegion *MR = nullptr; + + if (Optional<SVal> V = + getObjectUnderConstruction(State, D.getBindTemporaryExpr(), + Pred->getLocationContext())) { // FIXME: Currently we insert temporary destructors for default parameters, - // but we don't insert the constructors. - State = State->remove<InitializedTemporariesSet>( - std::make_pair(D.getBindTemporaryExpr(), Pred->getStackFrame())); + // but we don't insert the constructors, so the entry in + // ObjectsUnderConstruction may be missing. + State = finishObjectConstruction(State, D.getBindTemporaryExpr(), + Pred->getLocationContext()); + MR = V->getAsRegion(); } + + // If copy elision has occured, and the constructor corresponding to the + // destructor was elided, we need to skip the destructor as well. + if (isDestructorElided(State, BTE, LC)) { + State = cleanupElidedDestructor(State, BTE, LC); + NodeBuilder Bldr(Pred, Dst, *currBldrCtx); + PostImplicitCall PP(D.getDestructorDecl(getContext()), + D.getBindTemporaryExpr()->getLocStart(), + Pred->getLocationContext()); + Bldr.generateNode(PP, State, Pred); + return; + } + + ExplodedNodeSet CleanDtorState; + StmtNodeBuilder StmtBldr(Pred, CleanDtorState, *currBldrCtx); StmtBldr.generateNode(D.getBindTemporaryExpr(), Pred, State); - QualType varType = D.getBindTemporaryExpr()->getSubExpr()->getType(); + QualType T = D.getBindTemporaryExpr()->getSubExpr()->getType(); // FIXME: Currently CleanDtorState can be empty here due to temporaries being // bound to default parameters. assert(CleanDtorState.size() <= 1); ExplodedNode *CleanPred = CleanDtorState.empty() ? Pred : *CleanDtorState.begin(); - // FIXME: Inlining of temporary destructors is not supported yet anyway, so - // we just put a NULL region for now. This will need to be changed later. - VisitCXXDestructor(varType, nullptr, D.getBindTemporaryExpr(), - /*IsBase=*/false, CleanPred, Dst); + + EvalCallOptions CallOpts; + CallOpts.IsTemporaryCtorOrDtor = true; + if (!MR) { + CallOpts.IsCtorOrDtorWithImproperlyModeledTargetRegion = true; + + // If we have no MR, we still need to unwrap the array to avoid destroying + // the whole array at once. Regardless, we'd eventually need to model array + // destructors properly, element-by-element. + while (const ArrayType *AT = getContext().getAsArrayType(T)) { + T = AT->getElementType(); + CallOpts.IsArrayCtorOrDtor = true; + } + } else { + // We'd eventually need to makeZeroElementRegion() trick here, + // but for now we don't have the respective construction contexts, + // so MR would always be null in this case. Do nothing for now. + } + VisitCXXDestructor(T, MR, D.getBindTemporaryExpr(), + /*IsBase=*/false, CleanPred, Dst, CallOpts); } void ExprEngine::processCleanupTemporaryBranch(const CXXBindTemporaryExpr *BTE, @@ -791,19 +1109,23 @@ void ExprEngine::processCleanupTemporaryBranch(const CXXBindTemporaryExpr *BTE, const CFGBlock *DstT, const CFGBlock *DstF) { BranchNodeBuilder TempDtorBuilder(Pred, Dst, BldCtx, DstT, DstF); - if (Pred->getState()->contains<InitializedTemporariesSet>( - std::make_pair(BTE, Pred->getStackFrame()))) { + ProgramStateRef State = Pred->getState(); + const LocationContext *LC = Pred->getLocationContext(); + if (getObjectUnderConstruction(State, BTE, LC)) { TempDtorBuilder.markInfeasible(false); - TempDtorBuilder.generateNode(Pred->getState(), true, Pred); + TempDtorBuilder.generateNode(State, true, Pred); } else { TempDtorBuilder.markInfeasible(true); - TempDtorBuilder.generateNode(Pred->getState(), false, Pred); + TempDtorBuilder.generateNode(State, false, Pred); } } void ExprEngine::VisitCXXBindTemporaryExpr(const CXXBindTemporaryExpr *BTE, ExplodedNodeSet &PreVisit, ExplodedNodeSet &Dst) { + // This is a fallback solution in case we didn't have a construction + // context when we were constructing the temporary. Otherwise the map should + // have been populated there. if (!getAnalysisManager().options.includeTemporaryDtorsInCFG()) { // In case we don't have temporary destructors in the CFG, do not mark // the initialization - we would otherwise never clean it up. @@ -813,34 +1135,39 @@ void ExprEngine::VisitCXXBindTemporaryExpr(const CXXBindTemporaryExpr *BTE, StmtNodeBuilder StmtBldr(PreVisit, Dst, *currBldrCtx); for (ExplodedNode *Node : PreVisit) { ProgramStateRef State = Node->getState(); - - if (!State->contains<InitializedTemporariesSet>( - std::make_pair(BTE, Node->getStackFrame()))) { - // FIXME: Currently the state might already contain the marker due to + const LocationContext *LC = Node->getLocationContext(); + if (!getObjectUnderConstruction(State, BTE, LC)) { + // FIXME: Currently the state might also already contain the marker due to // incorrect handling of temporaries bound to default parameters; for // those, we currently skip the CXXBindTemporaryExpr but rely on adding // temporary destructor nodes. - State = State->add<InitializedTemporariesSet>( - std::make_pair(BTE, Node->getStackFrame())); + State = addObjectUnderConstruction(State, BTE, LC, UnknownVal()); } StmtBldr.generateNode(BTE, Node, State); } } -namespace { -class CollectReachableSymbolsCallback final : public SymbolVisitor { - InvalidatedSymbols Symbols; +ProgramStateRef ExprEngine::escapeValue(ProgramStateRef State, SVal V, + PointerEscapeKind K) const { + class CollectReachableSymbolsCallback final : public SymbolVisitor { + InvalidatedSymbols Symbols; -public: - explicit CollectReachableSymbolsCallback(ProgramStateRef State) {} - const InvalidatedSymbols &getSymbols() const { return Symbols; } + public: + explicit CollectReachableSymbolsCallback(ProgramStateRef State) {} - bool VisitSymbol(SymbolRef Sym) override { - Symbols.insert(Sym); - return true; - } -}; -} // end anonymous namespace + const InvalidatedSymbols &getSymbols() const { return Symbols; } + + bool VisitSymbol(SymbolRef Sym) override { + Symbols.insert(Sym); + return true; + } + }; + + const CollectReachableSymbolsCallback &Scanner = + State->scanReachableSymbols<CollectReachableSymbolsCallback>(V); + return getCheckerManager().runCheckersForPointerEscape( + State, Scanner.getSymbols(), /*CallEvent*/ nullptr, K, nullptr); +} void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, ExplodedNodeSet &DstTop) { @@ -930,8 +1257,7 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, case Stmt::OMPTargetTeamsDistributeParallelForDirectiveClass: case Stmt::OMPTargetTeamsDistributeParallelForSimdDirectiveClass: case Stmt::OMPTargetTeamsDistributeSimdDirectiveClass: - case Stmt::CapturedStmtClass: - { + case Stmt::CapturedStmtClass: { const ExplodedNode *node = Bldr.generateSink(S, Pred, Pred->getState()); Engine.addAbortedBlock(node, currBldrCtx->getBlock()); break; @@ -1026,6 +1352,7 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, case Stmt::AddrLabelExprClass: case Stmt::AttributedStmtClass: case Stmt::IntegerLiteralClass: + case Stmt::FixedPointLiteralClass: case Stmt::CharacterLiteralClass: case Stmt::ImplicitValueInitExprClass: case Stmt::CXXScalarValueInitExprClass: @@ -1060,16 +1387,15 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, StmtNodeBuilder Bldr2(PreVisit, Tmp, *currBldrCtx); const Expr *ArgE; - if (const CXXDefaultArgExpr *DefE = dyn_cast<CXXDefaultArgExpr>(S)) + if (const auto *DefE = dyn_cast<CXXDefaultArgExpr>(S)) ArgE = DefE->getExpr(); - else if (const CXXDefaultInitExpr *DefE = dyn_cast<CXXDefaultInitExpr>(S)) + else if (const auto *DefE = dyn_cast<CXXDefaultInitExpr>(S)) ArgE = DefE->getExpr(); else llvm_unreachable("unknown constant wrapper kind"); bool IsTemporary = false; - if (const MaterializeTemporaryExpr *MTE = - dyn_cast<MaterializeTemporaryExpr>(ArgE)) { + if (const auto *MTE = dyn_cast<MaterializeTemporaryExpr>(ArgE)) { ArgE = MTE->GetTemporaryExpr(); IsTemporary = true; } @@ -1079,15 +1405,14 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, ConstantVal = UnknownVal(); const LocationContext *LCtx = Pred->getLocationContext(); - for (ExplodedNodeSet::iterator I = PreVisit.begin(), E = PreVisit.end(); - I != E; ++I) { - ProgramStateRef State = (*I)->getState(); + for (const auto I : PreVisit) { + ProgramStateRef State = I->getState(); State = State->BindExpr(S, LCtx, *ConstantVal); if (IsTemporary) State = createTemporaryRegionIfNeeded(State, LCtx, cast<Expr>(S), cast<Expr>(S)); - Bldr2.generateNode(S, *I, State); + Bldr2.generateNode(S, I, State); } getCheckerManager().runCheckersForPostStmt(Dst, Tmp, S, *this); @@ -1108,12 +1433,10 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, ExplodedNodeSet Tmp; StmtNodeBuilder Bldr2(preVisit, Tmp, *currBldrCtx); - const Expr *Ex = cast<Expr>(S); + const auto *Ex = cast<Expr>(S); QualType resultType = Ex->getType(); - for (ExplodedNodeSet::iterator it = preVisit.begin(), et = preVisit.end(); - it != et; ++it) { - ExplodedNode *N = *it; + for (const auto N : preVisit) { const LocationContext *LCtx = N->getLocationContext(); SVal result = svalBuilder.conjureSymbolVal(nullptr, Ex, LCtx, resultType, @@ -1127,17 +1450,8 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, ->getType()->isRecordType())) for (auto Child : Ex->children()) { assert(Child); - SVal Val = State->getSVal(Child, LCtx); - - CollectReachableSymbolsCallback Scanner = - State->scanReachableSymbols<CollectReachableSymbolsCallback>( - Val); - const InvalidatedSymbols &EscapedSymbols = Scanner.getSymbols(); - - State = getCheckerManager().runCheckersForPointerEscape( - State, EscapedSymbols, - /*CallEvent*/ nullptr, PSK_EscapeOther, nullptr); + State = escapeValue(State, Val, PSK_EscapeOther); } Bldr2.generateNode(S, N, State); @@ -1184,7 +1498,7 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, break; case Stmt::BinaryOperatorClass: { - const BinaryOperator* B = cast<BinaryOperator>(S); + const auto *B = cast<BinaryOperator>(S); if (B->isLogicalOp()) { Bldr.takeNodes(Pred); VisitLogicalExpr(B, Pred, Dst); @@ -1216,12 +1530,12 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, } case Stmt::CXXOperatorCallExprClass: { - const CXXOperatorCallExpr *OCE = cast<CXXOperatorCallExpr>(S); + const auto *OCE = cast<CXXOperatorCallExpr>(S); // For instance method operators, make sure the 'this' argument has a // valid region. const Decl *Callee = OCE->getCalleeDecl(); - if (const CXXMethodDecl *MD = dyn_cast_or_null<CXXMethodDecl>(Callee)) { + if (const auto *MD = dyn_cast_or_null<CXXMethodDecl>(Callee)) { if (MD->isInstance()) { ProgramStateRef State = Pred->getState(); const LocationContext *LCtx = Pred->getLocationContext(); @@ -1239,34 +1553,38 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, // FALLTHROUGH LLVM_FALLTHROUGH; } + case Stmt::CallExprClass: case Stmt::CXXMemberCallExprClass: - case Stmt::UserDefinedLiteralClass: { + case Stmt::UserDefinedLiteralClass: Bldr.takeNodes(Pred); VisitCallExpr(cast<CallExpr>(S), Pred, Dst); Bldr.addNodes(Dst); break; - } - case Stmt::CXXCatchStmtClass: { + case Stmt::CXXCatchStmtClass: Bldr.takeNodes(Pred); VisitCXXCatchStmt(cast<CXXCatchStmt>(S), Pred, Dst); Bldr.addNodes(Dst); break; - } case Stmt::CXXTemporaryObjectExprClass: - case Stmt::CXXConstructExprClass: { + case Stmt::CXXConstructExprClass: Bldr.takeNodes(Pred); VisitCXXConstructExpr(cast<CXXConstructExpr>(S), Pred, Dst); Bldr.addNodes(Dst); break; - } case Stmt::CXXNewExprClass: { Bldr.takeNodes(Pred); + + ExplodedNodeSet PreVisit; + getCheckerManager().runCheckersForPreStmt(PreVisit, Pred, S, *this); + ExplodedNodeSet PostVisit; - VisitCXXNewExpr(cast<CXXNewExpr>(S), Pred, PostVisit); + for (const auto i : PreVisit) + VisitCXXNewExpr(cast<CXXNewExpr>(S), i, PostVisit); + getCheckerManager().runCheckersForPostStmt(Dst, PostVisit, S, *this); Bldr.addNodes(Dst); break; @@ -1275,12 +1593,11 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, case Stmt::CXXDeleteExprClass: { Bldr.takeNodes(Pred); ExplodedNodeSet PreVisit; - const CXXDeleteExpr *CDE = cast<CXXDeleteExpr>(S); + const auto *CDE = cast<CXXDeleteExpr>(S); getCheckerManager().runCheckersForPreStmt(PreVisit, Pred, S, *this); - for (ExplodedNodeSet::iterator i = PreVisit.begin(), - e = PreVisit.end(); i != e ; ++i) - VisitCXXDeleteExpr(CDE, *i, Dst); + for (const auto i : PreVisit) + VisitCXXDeleteExpr(CDE, i, Dst); Bldr.addNodes(Dst); break; @@ -1290,7 +1607,7 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, case Stmt::ChooseExprClass: { // __builtin_choose_expr Bldr.takeNodes(Pred); - const ChooseExpr *C = cast<ChooseExpr>(S); + const auto *C = cast<ChooseExpr>(S); VisitGuardedExpr(C, C->getLHS(), C->getRHS(), Pred, Dst); Bldr.addNodes(Dst); break; @@ -1311,8 +1628,7 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, case Stmt::BinaryConditionalOperatorClass: case Stmt::ConditionalOperatorClass: { // '?' operator Bldr.takeNodes(Pred); - const AbstractConditionalOperator *C - = cast<AbstractConditionalOperator>(S); + const auto *C = cast<AbstractConditionalOperator>(S); VisitGuardedExpr(C, C->getTrueExpr(), C->getFalseExpr(), Pred, Dst); Bldr.addNodes(Dst); break; @@ -1326,7 +1642,7 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, case Stmt::DeclRefExprClass: { Bldr.takeNodes(Pred); - const DeclRefExpr *DE = cast<DeclRefExpr>(S); + const auto *DE = cast<DeclRefExpr>(S); VisitCommonDeclRefExpr(DE, DE->getDecl(), Pred, Dst); Bldr.addNodes(Dst); break; @@ -1347,7 +1663,7 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, case Stmt::CXXFunctionalCastExprClass: case Stmt::ObjCBridgedCastExprClass: { Bldr.takeNodes(Pred); - const CastExpr *C = cast<CastExpr>(S); + const auto *C = cast<CastExpr>(S); ExplodedNodeSet dstExpr; VisitCast(C, C->getSubExpr(), Pred, dstExpr); @@ -1359,14 +1675,12 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, case Expr::MaterializeTemporaryExprClass: { Bldr.takeNodes(Pred); - const MaterializeTemporaryExpr *MTE = cast<MaterializeTemporaryExpr>(S); + const auto *MTE = cast<MaterializeTemporaryExpr>(S); ExplodedNodeSet dstPrevisit; getCheckerManager().runCheckersForPreStmt(dstPrevisit, Pred, MTE, *this); ExplodedNodeSet dstExpr; - for (ExplodedNodeSet::iterator i = dstPrevisit.begin(), - e = dstPrevisit.end(); i != e ; ++i) { - CreateCXXTemporaryObject(MTE, *i, dstExpr); - } + for (const auto i : dstPrevisit) + CreateCXXTemporaryObject(MTE, i, dstExpr); getCheckerManager().runCheckersForPostStmt(Dst, dstExpr, MTE, *this); Bldr.addNodes(Dst); break; @@ -1421,11 +1735,19 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, Bldr.addNodes(Dst); break; - case Stmt::OffsetOfExprClass: + case Stmt::OffsetOfExprClass: { Bldr.takeNodes(Pred); - VisitOffsetOfExpr(cast<OffsetOfExpr>(S), Pred, Dst); + ExplodedNodeSet PreVisit; + getCheckerManager().runCheckersForPreStmt(PreVisit, Pred, S, *this); + + ExplodedNodeSet PostVisit; + for (const auto Node : PreVisit) + VisitOffsetOfExpr(cast<OffsetOfExpr>(S), Node, PostVisit); + + getCheckerManager().runCheckersForPostStmt(Dst, PostVisit, S, *this); Bldr.addNodes(Dst); break; + } case Stmt::UnaryExprOrTypeTraitExprClass: Bldr.takeNodes(Pred); @@ -1435,7 +1757,7 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, break; case Stmt::StmtExprClass: { - const StmtExpr *SE = cast<StmtExpr>(S); + const auto *SE = cast<StmtExpr>(S); if (SE->getSubStmt()->body_empty()) { // Empty statement expression. @@ -1444,7 +1766,8 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, break; } - if (Expr *LastExpr = dyn_cast<Expr>(*SE->getSubStmt()->body_rbegin())) { + if (const auto *LastExpr = + dyn_cast<Expr>(*SE->getSubStmt()->body_rbegin())) { ProgramStateRef state = Pred->getState(); Bldr.generateNode(SE, Pred, state->BindExpr(SE, Pred->getLocationContext(), @@ -1456,7 +1779,7 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, case Stmt::UnaryOperatorClass: { Bldr.takeNodes(Pred); - const UnaryOperator *U = cast<UnaryOperator>(S); + const auto *U = cast<UnaryOperator>(S); if (AMgr.options.eagerlyAssumeBinOpBifurcation && (U->getOpcode() == UO_LNot)) { ExplodedNodeSet Tmp; VisitUnaryOperator(U, Pred, Tmp); @@ -1471,7 +1794,7 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, case Stmt::PseudoObjectExprClass: { Bldr.takeNodes(Pred); ProgramStateRef state = Pred->getState(); - const PseudoObjectExpr *PE = cast<PseudoObjectExpr>(S); + const auto *PE = cast<PseudoObjectExpr>(S); if (const Expr *Result = PE->getResultExpr()) { SVal V = state->getSVal(Result, Pred->getLocationContext()); Bldr.generateNode(S, Pred, @@ -1490,8 +1813,8 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, bool ExprEngine::replayWithoutInlining(ExplodedNode *N, const LocationContext *CalleeLC) { - const StackFrameContext *CalleeSF = CalleeLC->getCurrentStackFrame(); - const StackFrameContext *CallerSF = CalleeSF->getParent()->getCurrentStackFrame(); + const StackFrameContext *CalleeSF = CalleeLC->getStackFrame(); + const StackFrameContext *CallerSF = CalleeSF->getParent()->getStackFrame(); assert(CalleeSF && CallerSF); ExplodedNode *BeforeProcessingCall = nullptr; const Stmt *CE = CalleeSF->getCallSite(); @@ -1503,7 +1826,7 @@ bool ExprEngine::replayWithoutInlining(ExplodedNode *N, N = N->pred_empty() ? nullptr : *(N->pred_begin()); // Skip the nodes corresponding to the inlined code. - if (L.getLocationContext()->getCurrentStackFrame() != CallerSF) + if (L.getStackFrame() != CallerSF) continue; // We reached the caller. Find the node right before we started // processing the call. @@ -1602,10 +1925,10 @@ void ExprEngine::processCFGBlockEntrance(const BlockEdge &L, // Check if we stopped at the top level function or not. // Root node should have the location context of the top most function. const LocationContext *CalleeLC = Pred->getLocation().getLocationContext(); - const LocationContext *CalleeSF = CalleeLC->getCurrentStackFrame(); + const LocationContext *CalleeSF = CalleeLC->getStackFrame(); const LocationContext *RootLC = (*G.roots_begin())->getLocation().getLocationContext(); - if (RootLC->getCurrentStackFrame() != CalleeSF) { + if (RootLC->getStackFrame() != CalleeSF) { Engine.FunctionSummaries->markReachedMaxBlockCount(CalleeSF->getDecl()); // Re-run the call evaluation without inlining it, by storing the @@ -1639,14 +1962,14 @@ static SVal RecoverCastedSymbol(ProgramStateManager& StateMgr, const LocationContext *LCtx, ASTContext &Ctx) { - const Expr *Ex = dyn_cast<Expr>(Condition); + const auto *Ex = dyn_cast<Expr>(Condition); if (!Ex) return UnknownVal(); uint64_t bits = 0; bool bitsInit = false; - while (const CastExpr *CE = dyn_cast<CastExpr>(Ex)) { + while (const auto *CE = dyn_cast<CastExpr>(Ex)) { QualType T = CE->getType(); if (!T->isIntegralOrEnumerationType()) @@ -1674,7 +1997,7 @@ static SVal RecoverCastedSymbol(ProgramStateManager& StateMgr, #ifndef NDEBUG static const Stmt *getRightmostLeaf(const Stmt *Condition) { while (Condition) { - const BinaryOperator *BO = dyn_cast<BinaryOperator>(Condition); + const auto *BO = dyn_cast<BinaryOperator>(Condition); if (!BO || !BO->isLogicalOp()) { return Condition; } @@ -1700,10 +2023,10 @@ static const Stmt *getRightmostLeaf(const Stmt *Condition) { // space. static const Stmt *ResolveCondition(const Stmt *Condition, const CFGBlock *B) { - if (const Expr *Ex = dyn_cast<Expr>(Condition)) + if (const auto *Ex = dyn_cast<Expr>(Condition)) Condition = Ex->IgnoreParens(); - const BinaryOperator *BO = dyn_cast<BinaryOperator>(Condition); + const auto *BO = dyn_cast<BinaryOperator>(Condition); if (!BO || !BO->isLogicalOp()) return Condition; @@ -1751,7 +2074,7 @@ void ExprEngine::processBranch(const Stmt *Condition, const Stmt *Term, return; } - if (const Expr *Ex = dyn_cast<Expr>(Condition)) + if (const auto *Ex = dyn_cast<Expr>(Condition)) Condition = Ex->IgnoreParens(); Condition = ResolveCondition(Condition, BldCtx.getBlock()); @@ -1767,10 +2090,7 @@ void ExprEngine::processBranch(const Stmt *Condition, const Stmt *Term, return; BranchNodeBuilder builder(CheckersOutSet, Dst, BldCtx, DstT, DstF); - for (NodeBuilder::iterator I = CheckersOutSet.begin(), - E = CheckersOutSet.end(); E != I; ++I) { - ExplodedNode *PredI = *I; - + for (const auto PredI : CheckersOutSet) { if (PredI->isSink()) continue; @@ -1779,7 +2099,7 @@ void ExprEngine::processBranch(const Stmt *Condition, const Stmt *Term, if (X.isUnknownOrUndef()) { // Give it a chance to recover from unknown. - if (const Expr *Ex = dyn_cast<Expr>(Condition)) { + if (const auto *Ex = dyn_cast<Expr>(Condition)) { if (Ex->getType()->isIntegralOrEnumerationType()) { // Try to recover some path-sensitivity. Right now casts of symbolic // integers that promote their values are currently not tracked well. @@ -1836,13 +2156,13 @@ REGISTER_TRAIT_WITH_PROGRAMSTATE(InitializedGlobalsSet, void ExprEngine::processStaticInitializer(const DeclStmt *DS, NodeBuilderContext &BuilderCtx, ExplodedNode *Pred, - clang::ento::ExplodedNodeSet &Dst, + ExplodedNodeSet &Dst, const CFGBlock *DstT, const CFGBlock *DstF) { PrettyStackTraceLocationContext CrashInfo(Pred->getLocationContext()); currBldrCtx = &BuilderCtx; - const VarDecl *VD = cast<VarDecl>(DS->getSingleDecl()); + const auto *VD = cast<VarDecl>(DS->getSingleDecl()); ProgramStateRef state = Pred->getState(); bool initHasRun = state->contains<InitializedGlobalsSet>(VD); BranchNodeBuilder builder(Pred, Dst, BuilderCtx, DstT, DstF); @@ -1860,7 +2180,6 @@ void ExprEngine::processStaticInitializer(const DeclStmt *DS, /// processIndirectGoto - Called by CoreEngine. Used to generate successor /// nodes by processing the 'effects' of a computed goto jump. void ExprEngine::processIndirectGoto(IndirectGotoNodeBuilder &builder) { - ProgramStateRef state = builder.getState(); SVal V = state->getSVal(builder.getTarget(), builder.getLocationContext()); @@ -1871,7 +2190,7 @@ void ExprEngine::processIndirectGoto(IndirectGotoNodeBuilder &builder) { // (3) We have no clue about the label. Dispatch to all targets. // - typedef IndirectGotoNodeBuilder::iterator iterator; + using iterator = IndirectGotoNodeBuilder::iterator; if (Optional<loc::GotoLabel> LV = V.getAs<loc::GotoLabel>()) { const LabelDecl *L = LV->getLabel(); @@ -1897,26 +2216,10 @@ void ExprEngine::processIndirectGoto(IndirectGotoNodeBuilder &builder) { // This is really a catch-all. We don't support symbolics yet. // FIXME: Implement dispatch for symbolic pointers. - for (iterator I=builder.begin(), E=builder.end(); I != E; ++I) + for (iterator I = builder.begin(), E = builder.end(); I != E; ++I) builder.generateNode(I, state); } -#if 0 -static bool stackFrameDoesNotContainInitializedTemporaries(ExplodedNode &Pred) { - const StackFrameContext* Frame = Pred.getStackFrame(); - const llvm::ImmutableSet<CXXBindTemporaryContext> &Set = - Pred.getState()->get<InitializedTemporariesSet>(); - return std::find_if(Set.begin(), Set.end(), - [&](const CXXBindTemporaryContext &Ctx) { - if (Ctx.second == Frame) { - Ctx.first->dump(); - llvm::errs() << "\n"; - } - return Ctx.second == Frame; - }) == Set.end(); -} -#endif - void ExprEngine::processBeginOfFunction(NodeBuilderContext &BC, ExplodedNode *Pred, ExplodedNodeSet &Dst, @@ -1930,9 +2233,59 @@ void ExprEngine::processBeginOfFunction(NodeBuilderContext &BC, void ExprEngine::processEndOfFunction(NodeBuilderContext& BC, ExplodedNode *Pred, const ReturnStmt *RS) { - // FIXME: Assert that stackFrameDoesNotContainInitializedTemporaries(*Pred)). - // We currently cannot enable this assert, as lifetime extended temporaries - // are not modelled correctly. + // FIXME: We currently cannot assert that temporaries are clear, because + // lifetime extended temporaries are not always modelled correctly. In some + // cases when we materialize the temporary, we do + // createTemporaryRegionIfNeeded(), and the region changes, and also the + // respective destructor becomes automatic from temporary. So for now clean up + // the state manually before asserting. Ideally, the code above the assertion + // should go away, but the assertion should remain. + { + ExplodedNodeSet CleanUpObjects; + NodeBuilder Bldr(Pred, CleanUpObjects, BC); + ProgramStateRef State = Pred->getState(); + const LocationContext *FromLC = Pred->getLocationContext(); + const LocationContext *ToLC = FromLC->getStackFrame()->getParent(); + const LocationContext *LC = FromLC; + while (LC != ToLC) { + assert(LC && "ToLC must be a parent of FromLC!"); + for (auto I : State->get<ObjectsUnderConstruction>()) + if (I.first.getLocationContext() == LC) { + // The comment above only pardons us for not cleaning up a + // CXXBindTemporaryExpr. If any other statements are found here, + // it must be a separate problem. + assert(isa<CXXBindTemporaryExpr>(I.first.getStmt())); + State = State->remove<ObjectsUnderConstruction>(I.first); + // Also cleanup the elided destructor info. + ElidedDestructorItem Item( + cast<CXXBindTemporaryExpr>(I.first.getStmt()), + I.first.getLocationContext()); + State = State->remove<ElidedDestructors>(Item); + } + + // Also suppress the assertion for elided destructors when temporary + // destructors are not provided at all by the CFG, because there's no + // good place to clean them up. + if (!AMgr.getAnalyzerOptions().includeTemporaryDtorsInCFG()) + for (auto I : State->get<ElidedDestructors>()) + if (I.second == LC) + State = State->remove<ElidedDestructors>(I); + + LC = LC->getParent(); + } + if (State != Pred->getState()) { + Pred = Bldr.generateNode(Pred->getLocation(), State, Pred); + if (!Pred) { + // The node with clean temporaries already exists. We might have reached + // it on a path on which we initialize different temporaries. + return; + } + } + } + assert(areAllObjectsFullyConstructed(Pred->getState(), + Pred->getLocationContext(), + Pred->getStackFrame()->getParent())); + PrettyStackTraceLocationContext CrashInfo(Pred->getLocationContext()); StateMgr.EndPath(Pred->getState()); @@ -1943,12 +2296,10 @@ void ExprEngine::processEndOfFunction(NodeBuilderContext& BC, removeDeadOnEndOfFunction(BC, Pred, AfterRemovedDead); // Notify checkers. - for (ExplodedNodeSet::iterator I = AfterRemovedDead.begin(), - E = AfterRemovedDead.end(); I != E; ++I) { - getCheckerManager().runCheckersForEndFunction(BC, Dst, *I, *this); - } + for (const auto I : AfterRemovedDead) + getCheckerManager().runCheckersForEndFunction(BC, Dst, I, *this, RS); } else { - getCheckerManager().runCheckersForEndFunction(BC, Dst, Pred, *this); + getCheckerManager().runCheckersForEndFunction(BC, Dst, Pred, *this, RS); } Engine.enqueueEndOfFunction(Dst, RS); @@ -1957,7 +2308,8 @@ void ExprEngine::processEndOfFunction(NodeBuilderContext& BC, /// ProcessSwitch - Called by CoreEngine. Used to generate successor /// nodes by processing the 'effects' of a switch statement. void ExprEngine::processSwitch(SwitchNodeBuilder& builder) { - typedef SwitchNodeBuilder::iterator iterator; + using iterator = SwitchNodeBuilder::iterator; + ProgramStateRef state = builder.getState(); const Expr *CondE = builder.getCondition(); SVal CondV_untested = state->getSVal(CondE, builder.getLocationContext()); @@ -2046,16 +2398,16 @@ void ExprEngine::VisitCommonDeclRefExpr(const Expr *Ex, const NamedDecl *D, ProgramStateRef state = Pred->getState(); const LocationContext *LCtx = Pred->getLocationContext(); - if (const VarDecl *VD = dyn_cast<VarDecl>(D)) { + if (const auto *VD = dyn_cast<VarDecl>(D)) { // C permits "extern void v", and if you cast the address to a valid type, // you can even do things with it. We simply pretend assert(Ex->isGLValue() || VD->getType()->isVoidType()); const LocationContext *LocCtxt = Pred->getLocationContext(); const Decl *D = LocCtxt->getDecl(); - const auto *MD = D ? dyn_cast<CXXMethodDecl>(D) : nullptr; + const auto *MD = dyn_cast_or_null<CXXMethodDecl>(D); const auto *DeclRefEx = dyn_cast<DeclRefExpr>(Ex); - SVal V; - bool IsReference; + Optional<std::pair<SVal, QualType>> VInfo; + if (AMgr.options.shouldInlineLambdas() && DeclRefEx && DeclRefEx->refersToEnclosingVariableOrCapture() && MD && MD->getParent()->isLambda()) { @@ -2064,25 +2416,23 @@ void ExprEngine::VisitCommonDeclRefExpr(const Expr *Ex, const NamedDecl *D, llvm::DenseMap<const VarDecl *, FieldDecl *> LambdaCaptureFields; FieldDecl *LambdaThisCaptureField; CXXRec->getCaptureFields(LambdaCaptureFields, LambdaThisCaptureField); - const FieldDecl *FD = LambdaCaptureFields[VD]; - if (!FD) { - // When a constant is captured, sometimes no corresponding field is - // created in the lambda object. - assert(VD->getType().isConstQualified()); - V = state->getLValue(VD, LocCtxt); - IsReference = false; - } else { + + // Sema follows a sequence of complex rules to determine whether the + // variable should be captured. + if (const FieldDecl *FD = LambdaCaptureFields[VD]) { Loc CXXThis = - svalBuilder.getCXXThis(MD, LocCtxt->getCurrentStackFrame()); + svalBuilder.getCXXThis(MD, LocCtxt->getStackFrame()); SVal CXXThisVal = state->getSVal(CXXThis); - V = state->getLValue(FD, CXXThisVal); - IsReference = FD->getType()->isReferenceType(); + VInfo = std::make_pair(state->getLValue(FD, CXXThisVal), FD->getType()); } - } else { - V = state->getLValue(VD, LocCtxt); - IsReference = VD->getType()->isReferenceType(); } + if (!VInfo) + VInfo = std::make_pair(state->getLValue(VD, LocCtxt), VD->getType()); + + SVal V = VInfo->first; + bool IsReference = VInfo->second->isReferenceType(); + // For references, the 'lvalue' is the pointer address stored in the // reference region. if (IsReference) { @@ -2096,13 +2446,13 @@ void ExprEngine::VisitCommonDeclRefExpr(const Expr *Ex, const NamedDecl *D, ProgramPoint::PostLValueKind); return; } - if (const EnumConstantDecl *ED = dyn_cast<EnumConstantDecl>(D)) { + if (const auto *ED = dyn_cast<EnumConstantDecl>(D)) { assert(!Ex->isGLValue()); SVal V = svalBuilder.makeIntVal(ED->getInitVal()); Bldr.generateNode(Ex, Pred, state->BindExpr(Ex, LCtx, V)); return; } - if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(D)) { + if (const auto *FD = dyn_cast<FunctionDecl>(D)) { SVal V = svalBuilder.getFunctionPointer(FD); Bldr.generateNode(Ex, Pred, state->BindExpr(Ex, LCtx, V), nullptr, ProgramPoint::PostLValueKind); @@ -2118,7 +2468,12 @@ void ExprEngine::VisitCommonDeclRefExpr(const Expr *Ex, const NamedDecl *D, currBldrCtx->blockCount()); state = state->assume(V.castAs<DefinedOrUnknownSVal>(), true); Bldr.generateNode(Ex, Pred, state->BindExpr(Ex, LCtx, V), nullptr, - ProgramPoint::PostLValueKind); + ProgramPoint::PostLValueKind); + return; + } + if (isa<BindingDecl>(D)) { + // FIXME: proper support for bound declarations. + // For now, let's just prevent crashing. return; } @@ -2151,9 +2506,17 @@ void ExprEngine::VisitArraySubscriptExpr(const ArraySubscriptExpr *A, ProgramStateRef state = Node->getState(); if (IsGLValueLike) { - SVal V = state->getLValue(A->getType(), - state->getSVal(Idx, LCtx), - state->getSVal(Base, LCtx)); + QualType T = A->getType(); + + // One of the forbidden LValue types! We still need to have sensible + // symbolic locations to represent this stuff. Note that arithmetic on + // void pointers is a GCC extension. + if (T->isVoidType()) + T = getContext().CharTy; + + SVal V = state->getLValue(T, + state->getSVal(Idx, LCtx), + state->getSVal(Base, LCtx)); Bldr.generateNode(A, Node, state->BindExpr(A, LCtx, V), nullptr, ProgramPoint::PostLValueKind); } else if (IsVectorType) { @@ -2171,41 +2534,36 @@ a vector and not a forbidden lvalue type"); /// VisitMemberExpr - Transfer function for member expressions. void ExprEngine::VisitMemberExpr(const MemberExpr *M, ExplodedNode *Pred, ExplodedNodeSet &Dst) { - // FIXME: Prechecks eventually go in ::Visit(). ExplodedNodeSet CheckedSet; getCheckerManager().runCheckersForPreStmt(CheckedSet, Pred, M, *this); - ExplodedNodeSet EvalSet; - ValueDecl *Member = M->getMemberDecl(); + ExplodedNodeSet EvalSet; + ValueDecl *Member = M->getMemberDecl(); // Handle static member variables and enum constants accessed via // member syntax. - if (isa<VarDecl>(Member) || isa<EnumConstantDecl>(Member)) { - ExplodedNodeSet Dst; - for (ExplodedNodeSet::iterator I = CheckedSet.begin(), E = CheckedSet.end(); - I != E; ++I) { - VisitCommonDeclRefExpr(M, Member, Pred, EvalSet); - } + if (isa<VarDecl>(Member) || isa<EnumConstantDecl>(Member)) { + for (const auto I : CheckedSet) + VisitCommonDeclRefExpr(M, Member, I, EvalSet); } else { StmtNodeBuilder Bldr(CheckedSet, EvalSet, *currBldrCtx); ExplodedNodeSet Tmp; - for (ExplodedNodeSet::iterator I = CheckedSet.begin(), E = CheckedSet.end(); - I != E; ++I) { - ProgramStateRef state = (*I)->getState(); - const LocationContext *LCtx = (*I)->getLocationContext(); + for (const auto I : CheckedSet) { + ProgramStateRef state = I->getState(); + const LocationContext *LCtx = I->getLocationContext(); Expr *BaseExpr = M->getBase(); // Handle C++ method calls. - if (const CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(Member)) { + if (const auto *MD = dyn_cast<CXXMethodDecl>(Member)) { if (MD->isInstance()) state = createTemporaryRegionIfNeeded(state, LCtx, BaseExpr); SVal MDVal = svalBuilder.getFunctionPointer(MD); state = state->BindExpr(M, LCtx, MDVal); - Bldr.generateNode(M, *I, state); + Bldr.generateNode(M, I, state); continue; } @@ -2213,7 +2571,7 @@ void ExprEngine::VisitMemberExpr(const MemberExpr *M, ExplodedNode *Pred, state = createTemporaryRegionIfNeeded(state, LCtx, BaseExpr); SVal baseExprVal = state->getSVal(BaseExpr, LCtx); - FieldDecl *field = cast<FieldDecl>(Member); + const auto *field = cast<FieldDecl>(Member); SVal L = state->getLValue(field, baseExprVal); if (M->isGLValue() || M->getType()->isArrayType()) { @@ -2223,8 +2581,8 @@ void ExprEngine::VisitMemberExpr(const MemberExpr *M, ExplodedNode *Pred, // pointers as soon as they are used. if (!M->isGLValue()) { assert(M->getType()->isArrayType()); - const ImplicitCastExpr *PE = - dyn_cast<ImplicitCastExpr>((*I)->getParentMap().getParentIgnoreParens(M)); + const auto *PE = + dyn_cast<ImplicitCastExpr>(I->getParentMap().getParentIgnoreParens(M)); if (!PE || PE->getCastKind() != CK_ArrayToPointerDecay) { llvm_unreachable("should always be wrapped in ArrayToPointerDecay"); } @@ -2237,11 +2595,11 @@ void ExprEngine::VisitMemberExpr(const MemberExpr *M, ExplodedNode *Pred, L = UnknownVal(); } - Bldr.generateNode(M, *I, state->BindExpr(M, LCtx, L), nullptr, + Bldr.generateNode(M, I, state->BindExpr(M, LCtx, L), nullptr, ProgramPoint::PostLValueKind); } else { - Bldr.takeNodes(*I); - evalLoad(Tmp, M, M, *I, state, L); + Bldr.takeNodes(I); + evalLoad(Tmp, M, M, I, state, L); Bldr.addNodes(Tmp); } } @@ -2261,10 +2619,9 @@ void ExprEngine::VisitAtomicExpr(const AtomicExpr *AE, ExplodedNode *Pred, ExplodedNodeSet AfterInvalidateSet; StmtNodeBuilder Bldr(AfterPreSet, AfterInvalidateSet, *currBldrCtx); - for (ExplodedNodeSet::iterator I = AfterPreSet.begin(), E = AfterPreSet.end(); - I != E; ++I) { - ProgramStateRef State = (*I)->getState(); - const LocationContext *LCtx = (*I)->getLocationContext(); + for (const auto I : AfterPreSet) { + ProgramStateRef State = I->getState(); + const LocationContext *LCtx = I->getLocationContext(); SmallVector<SVal, 8> ValuesToInvalidate; for (unsigned SI = 0, Count = AE->getNumSubExprs(); SI != Count; SI++) { @@ -2281,7 +2638,7 @@ void ExprEngine::VisitAtomicExpr(const AtomicExpr *AE, ExplodedNode *Pred, SVal ResultVal = UnknownVal(); State = State->BindExpr(AE, LCtx, ResultVal); - Bldr.generateNode(AE, *I, State, nullptr, + Bldr.generateNode(AE, I, State, nullptr, ProgramPoint::PostStmtKind); } @@ -2324,15 +2681,7 @@ ProgramStateRef ExprEngine::processPointerEscapedOnBind(ProgramStateRef State, // Otherwise, find all symbols referenced by 'val' that we are tracking // and stop tracking them. - CollectReachableSymbolsCallback Scanner = - State->scanReachableSymbols<CollectReachableSymbolsCallback>(Val); - const InvalidatedSymbols &EscapedSymbols = Scanner.getSymbols(); - State = getCheckerManager().runCheckersForPointerEscape(State, - EscapedSymbols, - /*CallEvent*/ nullptr, - PSK_EscapeOnBind, - nullptr); - + State = escapeValue(State, Val, PSK_EscapeOnBind); return State; } @@ -2343,7 +2692,6 @@ ExprEngine::notifyCheckersOfPointerEscape(ProgramStateRef State, ArrayRef<const MemRegion *> Regions, const CallEvent *Call, RegionAndSymbolInvalidationTraits &ITraits) { - if (!Invalidated || Invalidated->empty()) return State; @@ -2357,16 +2705,13 @@ ExprEngine::notifyCheckersOfPointerEscape(ProgramStateRef State, // If the symbols were invalidated by a call, we want to find out which ones // were invalidated directly due to being arguments to the call. InvalidatedSymbols SymbolsDirectlyInvalidated; - for (ArrayRef<const MemRegion *>::iterator I = ExplicitRegions.begin(), - E = ExplicitRegions.end(); I != E; ++I) { - if (const SymbolicRegion *R = (*I)->StripCasts()->getAs<SymbolicRegion>()) + for (const auto I : ExplicitRegions) { + if (const SymbolicRegion *R = I->StripCasts()->getAs<SymbolicRegion>()) SymbolsDirectlyInvalidated.insert(R->getSymbol()); } InvalidatedSymbols SymbolsIndirectlyInvalidated; - for (InvalidatedSymbols::const_iterator I=Invalidated->begin(), - E = Invalidated->end(); I!=E; ++I) { - SymbolRef sym = *I; + for (const auto &sym : *Invalidated) { if (SymbolsDirectlyInvalidated.count(sym)) continue; SymbolsIndirectlyInvalidated.insert(sym); @@ -2390,7 +2735,6 @@ void ExprEngine::evalBind(ExplodedNodeSet &Dst, const Stmt *StoreE, ExplodedNode *Pred, SVal location, SVal Val, bool atDeclInit, const ProgramPoint *PP) { - const LocationContext *LC = Pred->getLocationContext(); PostStmt PS(StoreE, LC); if (!PP) @@ -2414,9 +2758,7 @@ void ExprEngine::evalBind(ExplodedNodeSet &Dst, const Stmt *StoreE, return; } - for (ExplodedNodeSet::iterator I = CheckedSet.begin(), E = CheckedSet.end(); - I!=E; ++I) { - ExplodedNode *PredI = *I; + for (const auto PredI : CheckedSet) { ProgramStateRef state = PredI->getState(); state = processPointerEscapedOnBind(state, location, Val, LC); @@ -2465,8 +2807,8 @@ void ExprEngine::evalStore(ExplodedNodeSet &Dst, const Expr *AssignE, if (location.isUndef()) return; - for (ExplodedNodeSet::iterator NI=Tmp.begin(), NE=Tmp.end(); NI!=NE; ++NI) - evalBind(Dst, StoreE, *NI, location, Val, false); + for (const auto I : Tmp) + evalBind(Dst, StoreE, I, location, Val, false); } void ExprEngine::evalLoad(ExplodedNodeSet &Dst, @@ -2476,46 +2818,8 @@ void ExprEngine::evalLoad(ExplodedNodeSet &Dst, ProgramStateRef state, SVal location, const ProgramPointTag *tag, - QualType LoadTy) -{ + QualType LoadTy) { assert(!location.getAs<NonLoc>() && "location cannot be a NonLoc."); - - // Are we loading from a region? This actually results in two loads; one - // to fetch the address of the referenced value and one to fetch the - // referenced value. - if (const TypedValueRegion *TR = - dyn_cast_or_null<TypedValueRegion>(location.getAsRegion())) { - - QualType ValTy = TR->getValueType(); - if (const ReferenceType *RT = ValTy->getAs<ReferenceType>()) { - static SimpleProgramPointTag - loadReferenceTag(TagProviderName, "Load Reference"); - ExplodedNodeSet Tmp; - evalLoadCommon(Tmp, NodeEx, BoundEx, Pred, state, - location, &loadReferenceTag, - getContext().getPointerType(RT->getPointeeType())); - - // Perform the load from the referenced value. - for (ExplodedNodeSet::iterator I=Tmp.begin(), E=Tmp.end() ; I!=E; ++I) { - state = (*I)->getState(); - location = state->getSVal(BoundEx, (*I)->getLocationContext()); - evalLoadCommon(Dst, NodeEx, BoundEx, *I, state, location, tag, LoadTy); - } - return; - } - } - - evalLoadCommon(Dst, NodeEx, BoundEx, Pred, state, location, tag, LoadTy); -} - -void ExprEngine::evalLoadCommon(ExplodedNodeSet &Dst, - const Expr *NodeEx, - const Expr *BoundEx, - ExplodedNode *Pred, - ProgramStateRef state, - SVal location, - const ProgramPointTag *tag, - QualType LoadTy) { assert(NodeEx); assert(BoundEx); // Evaluate the location (checks for bad dereferences). @@ -2529,9 +2833,9 @@ void ExprEngine::evalLoadCommon(ExplodedNodeSet &Dst, return; // Proceed with the load. - for (ExplodedNodeSet::iterator NI=Tmp.begin(), NE=Tmp.end(); NI!=NE; ++NI) { - state = (*NI)->getState(); - const LocationContext *LCtx = (*NI)->getLocationContext(); + for (const auto I : Tmp) { + state = I->getState(); + const LocationContext *LCtx = I->getLocationContext(); SVal V = UnknownVal(); if (location.isValid()) { @@ -2540,7 +2844,7 @@ void ExprEngine::evalLoadCommon(ExplodedNodeSet &Dst, V = state->getSVal(location.castAs<Loc>(), LoadTy); } - Bldr.generateNode(NodeEx, *NI, state->BindExpr(BoundEx, LCtx, V), tag, + Bldr.generateNode(NodeEx, I, state->BindExpr(BoundEx, LCtx, V), tag, ProgramPoint::PostLoadKind); } } @@ -2597,8 +2901,7 @@ void ExprEngine::evalEagerlyAssumeBinOpBifurcation(ExplodedNodeSet &Dst, const Expr *Ex) { StmtNodeBuilder Bldr(Src, Dst, *currBldrCtx); - for (ExplodedNodeSet::iterator I=Src.begin(), E=Src.end(); I!=E; ++I) { - ExplodedNode *Pred = *I; + for (const auto Pred : Src) { // Test if the previous node was as the same expression. This can happen // when the expression fails to evaluate to anything meaningful and // (as an optimization) we don't generate a node. @@ -2648,7 +2951,7 @@ void ExprEngine::VisitGCCAsmStmt(const GCCAsmStmt *A, ExplodedNode *Pred, for (const Expr *O : A->outputs()) { SVal X = state->getSVal(O, Pred->getLocationContext()); - assert (!X.getAs<NonLoc>()); // Should be an Lval, or unknown, undef. + assert(!X.getAs<NonLoc>()); // Should be an Lval, or unknown, undef. if (Optional<Loc> LV = X.getAs<Loc>()) state = state->bindLoc(*LV, UnknownVal(), Pred->getLocationContext()); @@ -2672,16 +2975,15 @@ static ExprEngine* GraphPrintCheckerState; static SourceManager* GraphPrintSourceManager; namespace llvm { -template<> -struct DOTGraphTraits<ExplodedNode*> : - public DefaultDOTGraphTraits { - DOTGraphTraits (bool isSimple=false) : DefaultDOTGraphTraits(isSimple) {} +template<> +struct DOTGraphTraits<ExplodedNode*> : public DefaultDOTGraphTraits { + DOTGraphTraits (bool isSimple = false) : DefaultDOTGraphTraits(isSimple) {} // FIXME: Since we do not cache error nodes in ExprEngine now, this does not // work. static std::string getNodeAttributes(const ExplodedNode *N, void*) { - return ""; + return {}; } // De-duplicate some source location pretty-printing. @@ -2694,15 +2996,8 @@ struct DOTGraphTraits<ExplodedNode*> : << "\\l"; } } - static void printLocation2(raw_ostream &Out, SourceLocation SLoc) { - if (SLoc.isFileID() && GraphPrintSourceManager->isInMainFile(SLoc)) - Out << "line " << GraphPrintSourceManager->getExpansionLineNumber(SLoc); - else - SLoc.print(Out, *GraphPrintSourceManager); - } static std::string getNodeLabel(const ExplodedNode *N, void*){ - std::string sbuf; llvm::raw_string_ostream Out(sbuf); @@ -2710,14 +3005,13 @@ struct DOTGraphTraits<ExplodedNode*> : ProgramPoint Loc = N->getLocation(); switch (Loc.getKind()) { - case ProgramPoint::BlockEntranceKind: { + case ProgramPoint::BlockEntranceKind: Out << "Block Entrance: B" << Loc.castAs<BlockEntrance>().getBlock()->getBlockID(); break; - } case ProgramPoint::BlockExitKind: - assert (false); + assert(false); break; case ProgramPoint::CallEnterKind: @@ -2808,7 +3102,7 @@ struct DOTGraphTraits<ExplodedNode*> : const Stmt *Label = E.getDst()->getLabel(); if (Label) { - if (const CaseStmt *C = dyn_cast<CaseStmt>(Label)) { + if (const auto *C = dyn_cast<CaseStmt>(Label)) { Out << "\\lcase "; LangOptions LO; // FIXME. if (C->getLHS()) @@ -2822,7 +3116,7 @@ struct DOTGraphTraits<ExplodedNode*> : Out << ":"; } else { - assert (isa<DefaultStmt>(Label)); + assert(isa<DefaultStmt>(Label)); Out << "\\ldefault:"; } } @@ -2863,6 +3157,8 @@ struct DOTGraphTraits<ExplodedNode*> : Out << "\\lPostStore\\l"; else if (Loc.getAs<PostLValue>()) Out << "\\lPostLValue\\l"; + else if (Loc.getAs<PostAllocatorCall>()) + Out << "\\lPostAllocatorCall\\l"; break; } @@ -2872,40 +3168,7 @@ struct DOTGraphTraits<ExplodedNode*> : Out << "\\|StateID: " << (const void*) state.get() << " NodeID: " << (const void*) N << "\\|"; - // Analysis stack backtrace. - Out << "Location context stack (from current to outer):\\l"; - const LocationContext *LC = Loc.getLocationContext(); - unsigned Idx = 0; - for (; LC; LC = LC->getParent(), ++Idx) { - Out << Idx << ". (" << (const void *)LC << ") "; - switch (LC->getKind()) { - case LocationContext::StackFrame: - if (const NamedDecl *D = dyn_cast<NamedDecl>(LC->getDecl())) - Out << "Calling " << D->getQualifiedNameAsString(); - else - Out << "Calling anonymous code"; - if (const Stmt *S = cast<StackFrameContext>(LC)->getCallSite()) { - Out << " at "; - printLocation2(Out, S->getLocStart()); - } - break; - case LocationContext::Block: - Out << "Invoking block"; - if (const Decl *D = cast<BlockInvocationContext>(LC)->getBlockDecl()) { - Out << " defined at "; - printLocation2(Out, D->getLocStart()); - } - break; - case LocationContext::Scope: - Out << "Entering scope"; - // FIXME: Add more info once ScopeContext is activated. - break; - } - Out << "\\l"; - } - Out << "\\l"; - - state->printDOT(Out); + state->printDOT(Out, N->getLocationContext()); Out << "\\l"; @@ -2916,23 +3179,24 @@ struct DOTGraphTraits<ExplodedNode*> : return Out.str(); } }; -} // end llvm namespace + +} // namespace llvm #endif void ExprEngine::ViewGraph(bool trim) { #ifndef NDEBUG if (trim) { - std::vector<const ExplodedNode*> Src; + std::vector<const ExplodedNode *> Src; // Flush any outstanding reports to make sure we cover all the nodes. // This does not cause them to get displayed. - for (BugReporter::iterator I=BR.begin(), E=BR.end(); I!=E; ++I) - const_cast<BugType*>(*I)->FlushReports(BR); + for (const auto I : BR) + const_cast<BugType *>(I)->FlushReports(BR); // Iterate through the reports and get their nodes. for (BugReporter::EQClasses_iterator EI = BR.EQClasses_begin(), EE = BR.EQClasses_end(); EI != EE; ++EI) { - ExplodedNode *N = const_cast<ExplodedNode*>(EI->begin()->getErrorNode()); + const auto *N = const_cast<ExplodedNode *>(EI->begin()->getErrorNode()); if (N) Src.push_back(N); } diff --git a/lib/StaticAnalyzer/Core/ExprEngineC.cpp b/lib/StaticAnalyzer/Core/ExprEngineC.cpp index 3e7a50365f50..c7b1a9ac82f0 100644 --- a/lib/StaticAnalyzer/Core/ExprEngineC.cpp +++ b/lib/StaticAnalyzer/Core/ExprEngineC.cpp @@ -20,7 +20,7 @@ using namespace clang; using namespace ento; using llvm::APSInt; -/// \brief Optionally conjure and return a symbol for offset when processing +/// Optionally conjure and return a symbol for offset when processing /// an expression \p Expression. /// If \p Other is a location, conjure a symbol for \p Symbol /// (offset) if it is unknown so that memory arithmetic always @@ -257,13 +257,23 @@ ProgramStateRef ExprEngine::handleLValueBitCast( ProgramStateRef state, const Expr* Ex, const LocationContext* LCtx, QualType T, QualType ExTy, const CastExpr* CastE, StmtNodeBuilder& Bldr, ExplodedNode* Pred) { + if (T->isLValueReferenceType()) { + assert(!CastE->getType()->isLValueReferenceType()); + ExTy = getContext().getLValueReferenceType(ExTy); + } else if (T->isRValueReferenceType()) { + assert(!CastE->getType()->isRValueReferenceType()); + ExTy = getContext().getRValueReferenceType(ExTy); + } // Delegate to SValBuilder to process. - SVal V = state->getSVal(Ex, LCtx); - V = svalBuilder.evalCast(V, T, ExTy); + SVal OrigV = state->getSVal(Ex, LCtx); + SVal V = svalBuilder.evalCast(OrigV, T, ExTy); // Negate the result if we're treating the boolean as a signed i1 if (CastE->getCastKind() == CK_BooleanToSignedIntegral) V = evalMinus(V); state = state->BindExpr(CastE, LCtx, V); + if (V.isUnknown() && !OrigV.isUnknown()) { + state = escapeValue(state, OrigV, PSK_EscapeOther); + } Bldr.generateNode(CastE, Pred, state); return state; @@ -580,24 +590,12 @@ void ExprEngine::VisitDeclStmt(const DeclStmt *DS, ExplodedNode *Pred, SVal InitVal = state->getSVal(InitEx, LC); assert(DS->isSingleDecl()); - if (auto *CtorExpr = findDirectConstructorForCurrentCFGElement()) { - assert(InitEx->IgnoreImplicit() == CtorExpr); - (void)CtorExpr; + if (getObjectUnderConstruction(state, DS, LC)) { + state = finishObjectConstruction(state, DS, LC); // We constructed the object directly in the variable. // No need to bind anything. B.generateNode(DS, UpdatedN, state); } else { - // We bound the temp obj region to the CXXConstructExpr. Now recover - // the lazy compound value when the variable is not a reference. - if (AMgr.getLangOpts().CPlusPlus && VD->getType()->isRecordType() && - !VD->getType()->isReferenceType()) { - if (Optional<loc::MemRegionVal> M = - InitVal.getAs<loc::MemRegionVal>()) { - InitVal = state->getSVal(M->getRegion()); - assert(InitVal.getAs<nonloc::LazyCompoundVal>()); - } - } - // Recover some path-sensitivity if a scalar value evaluated to // UnknownVal. if (InitVal.isUnknown()) { @@ -760,7 +758,11 @@ void ExprEngine::VisitGuardedExpr(const Expr *Ex, for (const ExplodedNode *N = Pred ; N ; N = *N->pred_begin()) { ProgramPoint PP = N->getLocation(); if (PP.getAs<PreStmtPurgeDeadSymbols>() || PP.getAs<BlockEntrance>()) { - assert(N->pred_size() == 1); + // If the state N has multiple predecessors P, it means that successors + // of P are all equivalent. + // In turn, that means that all nodes at P are equivalent in terms + // of observable behavior at N, and we can follow any of them. + // FIXME: a more robust solution which does not walk up the tree. continue; } SrcBlock = PP.castAs<BlockEdge>().getSrc(); @@ -1062,6 +1064,7 @@ void ExprEngine::VisitIncrementDecrementOperator(const UnaryOperator* U, // constant value. If the UnaryOperator has location type, create the // constant with int type and pointer width. SVal RHS; + SVal Result; if (U->getType()->isAnyPointerType()) RHS = svalBuilder.makeArrayIndex(1); @@ -1070,7 +1073,14 @@ void ExprEngine::VisitIncrementDecrementOperator(const UnaryOperator* U, else RHS = UnknownVal(); - SVal Result = evalBinOp(state, Op, V2, RHS, U->getType()); + // The use of an operand of type bool with the ++ operators is deprecated + // but valid until C++17. And if the operand of the ++ operator is of type + // bool, it is set to true until C++17. Note that for '_Bool', it is also + // set to true when it encounters ++ operator. + if (U->getType()->isBooleanType() && U->isIncrementOp()) + Result = svalBuilder.makeTruthVal(true, U->getType()); + else + Result = evalBinOp(state, Op, V2, RHS, U->getType()); // Conjure a new symbol if necessary to recover precision. if (Result.isUnknown()){ @@ -1092,7 +1102,6 @@ void ExprEngine::VisitIncrementDecrementOperator(const UnaryOperator* U, Constraint = svalBuilder.evalEQ(state, SymVal, svalBuilder.makeZeroVal(U->getType())); - state = state->assume(Constraint, false); assert(state); } diff --git a/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp b/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp index dad93111966f..dc124fc3ff2d 100644 --- a/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp +++ b/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp @@ -12,6 +12,7 @@ //===----------------------------------------------------------------------===// #include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h" +#include "clang/Analysis/ConstructionContext.h" #include "clang/AST/DeclCXX.h" #include "clang/AST/StmtCXX.h" #include "clang/AST/ParentMap.h" @@ -41,19 +42,30 @@ void ExprEngine::performTrivialCopy(NodeBuilder &Bldr, ExplodedNode *Pred, const CallEvent &Call) { SVal ThisVal; bool AlwaysReturnsLValue; + const CXXRecordDecl *ThisRD = nullptr; if (const CXXConstructorCall *Ctor = dyn_cast<CXXConstructorCall>(&Call)) { assert(Ctor->getDecl()->isTrivial()); assert(Ctor->getDecl()->isCopyOrMoveConstructor()); ThisVal = Ctor->getCXXThisVal(); + ThisRD = Ctor->getDecl()->getParent(); AlwaysReturnsLValue = false; } else { assert(cast<CXXMethodDecl>(Call.getDecl())->isTrivial()); assert(cast<CXXMethodDecl>(Call.getDecl())->getOverloadedOperator() == OO_Equal); ThisVal = cast<CXXInstanceCall>(Call).getCXXThisVal(); + ThisRD = cast<CXXMethodDecl>(Call.getDecl())->getParent(); AlwaysReturnsLValue = true; } + assert(ThisRD); + if (ThisRD->isEmpty()) { + // Do nothing for empty classes. Otherwise it'd retrieve an UnknownVal + // and bind it and RegionStore would think that the actual value + // in this region at this offset is unknown. + return; + } + const LocationContext *LCtx = Pred->getLocationContext(); ExplodedNodeSet Dst; @@ -84,52 +96,50 @@ void ExprEngine::performTrivialCopy(NodeBuilder &Bldr, ExplodedNode *Pred, } -/// Returns a region representing the first element of a (possibly -/// multi-dimensional) array. -/// -/// On return, \p Ty will be set to the base type of the array. -/// -/// If the type is not an array type at all, the original value is returned. -static SVal makeZeroElementRegion(ProgramStateRef State, SVal LValue, - QualType &Ty) { +SVal ExprEngine::makeZeroElementRegion(ProgramStateRef State, SVal LValue, + QualType &Ty, bool &IsArray) { SValBuilder &SVB = State->getStateManager().getSValBuilder(); ASTContext &Ctx = SVB.getContext(); while (const ArrayType *AT = Ctx.getAsArrayType(Ty)) { Ty = AT->getElementType(); LValue = State->getLValue(Ty, SVB.makeZeroArrayIndex(), LValue); + IsArray = true; } return LValue; } +std::pair<ProgramStateRef, SVal> ExprEngine::prepareForObjectConstruction( + const Expr *E, ProgramStateRef State, const LocationContext *LCtx, + const ConstructionContext *CC, EvalCallOptions &CallOpts) { + MemRegionManager &MRMgr = getSValBuilder().getRegionManager(); -const MemRegion * -ExprEngine::getRegionForConstructedObject(const CXXConstructExpr *CE, - ExplodedNode *Pred) { - const LocationContext *LCtx = Pred->getLocationContext(); - ProgramStateRef State = Pred->getState(); - - // See if we're constructing an existing region by looking at the next - // element in the CFG. - - if (auto Elem = findElementDirectlyInitializedByCurrentConstructor()) { - if (Optional<CFGStmt> StmtElem = Elem->getAs<CFGStmt>()) { - auto *DS = cast<DeclStmt>(StmtElem->getStmt()); - if (const auto *Var = dyn_cast<VarDecl>(DS->getSingleDecl())) { - if (Var->getInit() && Var->getInit()->IgnoreImplicit() == CE) { - SVal LValue = State->getLValue(Var, LCtx); - QualType Ty = Var->getType(); - LValue = makeZeroElementRegion(State, LValue, Ty); - return LValue.getAsRegion(); - } - } - } else if (Optional<CFGInitializer> InitElem = Elem->getAs<CFGInitializer>()) { - const CXXCtorInitializer *Init = InitElem->getInitializer(); + // See if we're constructing an existing region by looking at the + // current construction context. + if (CC) { + switch (CC->getKind()) { + case ConstructionContext::CXX17ElidedCopyVariableKind: + case ConstructionContext::SimpleVariableKind: { + const auto *DSCC = cast<VariableConstructionContext>(CC); + const auto *DS = DSCC->getDeclStmt(); + const auto *Var = cast<VarDecl>(DS->getSingleDecl()); + SVal LValue = State->getLValue(Var, LCtx); + QualType Ty = Var->getType(); + LValue = + makeZeroElementRegion(State, LValue, Ty, CallOpts.IsArrayCtorOrDtor); + State = + addObjectUnderConstruction(State, DSCC->getDeclStmt(), LCtx, LValue); + return std::make_pair(State, LValue); + } + case ConstructionContext::CXX17ElidedCopyConstructorInitializerKind: + case ConstructionContext::SimpleConstructorInitializerKind: { + const auto *ICC = cast<ConstructorInitializerConstructionContext>(CC); + const auto *Init = ICC->getCXXCtorInitializer(); assert(Init->isAnyMemberInitializer()); const CXXMethodDecl *CurCtor = cast<CXXMethodDecl>(LCtx->getDecl()); Loc ThisPtr = - getSValBuilder().getCXXThis(CurCtor, LCtx->getCurrentStackFrame()); + getSValBuilder().getCXXThis(CurCtor, LCtx->getStackFrame()); SVal ThisVal = State->getSVal(ThisPtr); const ValueDecl *Field; @@ -143,92 +153,134 @@ ExprEngine::getRegionForConstructedObject(const CXXConstructExpr *CE, } QualType Ty = Field->getType(); - FieldVal = makeZeroElementRegion(State, FieldVal, Ty); - return FieldVal.getAsRegion(); + FieldVal = makeZeroElementRegion(State, FieldVal, Ty, + CallOpts.IsArrayCtorOrDtor); + State = addObjectUnderConstruction(State, Init, LCtx, FieldVal); + return std::make_pair(State, FieldVal); } - - // FIXME: This will eventually need to handle new-expressions as well. - // Don't forget to update the pre-constructor initialization code in - // ExprEngine::VisitCXXConstructExpr. - } - // If we couldn't find an existing region to construct into, assume we're - // constructing a temporary. - MemRegionManager &MRMgr = getSValBuilder().getRegionManager(); - return MRMgr.getCXXTempObjectRegion(CE, LCtx); -} - -/// Returns true if the initializer for \Elem can be a direct -/// constructor. -static bool canHaveDirectConstructor(CFGElement Elem){ - // DeclStmts and CXXCtorInitializers for fields can be directly constructed. - - if (Optional<CFGStmt> StmtElem = Elem.getAs<CFGStmt>()) { - if (isa<DeclStmt>(StmtElem->getStmt())) { - return true; + case ConstructionContext::NewAllocatedObjectKind: { + if (AMgr.getAnalyzerOptions().mayInlineCXXAllocator()) { + const auto *NECC = cast<NewAllocatedObjectConstructionContext>(CC); + const auto *NE = NECC->getCXXNewExpr(); + SVal V = *getObjectUnderConstruction(State, NE, LCtx); + if (const SubRegion *MR = + dyn_cast_or_null<SubRegion>(V.getAsRegion())) { + if (NE->isArray()) { + // TODO: In fact, we need to call the constructor for every + // allocated element, not just the first one! + CallOpts.IsArrayCtorOrDtor = true; + return std::make_pair( + State, loc::MemRegionVal(getStoreManager().GetElementZeroRegion( + MR, NE->getType()->getPointeeType()))); + } + return std::make_pair(State, V); + } + // TODO: Detect when the allocator returns a null pointer. + // Constructor shall not be called in this case. + } + break; } - } - - if (Elem.getKind() == CFGElement::Initializer) { - return true; - } - - return false; -} + case ConstructionContext::SimpleReturnedValueKind: + case ConstructionContext::CXX17ElidedCopyReturnedValueKind: { + // The temporary is to be managed by the parent stack frame. + // So build it in the parent stack frame if we're not in the + // top frame of the analysis. + const StackFrameContext *SFC = LCtx->getStackFrame(); + if (const LocationContext *CallerLCtx = SFC->getParent()) { + auto RTC = (*SFC->getCallSiteBlock())[SFC->getIndex()] + .getAs<CFGCXXRecordTypedCall>(); + if (!RTC) { + // We were unable to find the correct construction context for the + // call in the parent stack frame. This is equivalent to not being + // able to find construction context at all. + break; + } + return prepareForObjectConstruction( + cast<Expr>(SFC->getCallSite()), State, CallerLCtx, + RTC->getConstructionContext(), CallOpts); + } else { + // We are on the top frame of the analysis. + // TODO: What exactly happens when we are? Does the temporary object + // live long enough in the region store in this case? Would checkers + // think that this object immediately goes out of scope? + CallOpts.IsTemporaryCtorOrDtor = true; + SVal V = loc::MemRegionVal(MRMgr.getCXXTempObjectRegion(E, LCtx)); + return std::make_pair(State, V); + } + llvm_unreachable("Unhandled return value construction context!"); + } + case ConstructionContext::ElidedTemporaryObjectKind: { + assert(AMgr.getAnalyzerOptions().shouldElideConstructors()); + const auto *TCC = cast<ElidedTemporaryObjectConstructionContext>(CC); + const CXXBindTemporaryExpr *BTE = TCC->getCXXBindTemporaryExpr(); + const MaterializeTemporaryExpr *MTE = TCC->getMaterializedTemporaryExpr(); + const CXXConstructExpr *CE = TCC->getConstructorAfterElision(); + + // Support pre-C++17 copy elision. We'll have the elidable copy + // constructor in the AST and in the CFG, but we'll skip it + // and construct directly into the final object. This call + // also sets the CallOpts flags for us. + SVal V; + std::tie(State, V) = prepareForObjectConstruction( + CE, State, LCtx, TCC->getConstructionContextAfterElision(), CallOpts); + + // Remember that we've elided the constructor. + State = addObjectUnderConstruction(State, CE, LCtx, V); + + // Remember that we've elided the destructor. + if (BTE) + State = elideDestructor(State, BTE, LCtx); + + // Instead of materialization, shamelessly return + // the final object destination. + if (MTE) + State = addObjectUnderConstruction(State, MTE, LCtx, V); + + return std::make_pair(State, V); + } + case ConstructionContext::SimpleTemporaryObjectKind: { + const auto *TCC = cast<SimpleTemporaryObjectConstructionContext>(CC); + const CXXBindTemporaryExpr *BTE = TCC->getCXXBindTemporaryExpr(); + const MaterializeTemporaryExpr *MTE = TCC->getMaterializedTemporaryExpr(); + SVal V = UnknownVal(); + + if (MTE) { + if (const ValueDecl *VD = MTE->getExtendingDecl()) { + assert(MTE->getStorageDuration() != SD_FullExpression); + if (!VD->getType()->isReferenceType()) { + // We're lifetime-extended by a surrounding aggregate. + // Automatic destructors aren't quite working in this case + // on the CFG side. We should warn the caller about that. + // FIXME: Is there a better way to retrieve this information from + // the MaterializeTemporaryExpr? + CallOpts.IsTemporaryLifetimeExtendedViaAggregate = true; + } + } -Optional<CFGElement> -ExprEngine::findElementDirectlyInitializedByCurrentConstructor() { - const NodeBuilderContext &CurrBldrCtx = getBuilderContext(); - // See if we're constructing an existing region by looking at the next - // element in the CFG. - const CFGBlock *B = CurrBldrCtx.getBlock(); - assert(isa<CXXConstructExpr>(((*B)[currStmtIdx]).castAs<CFGStmt>().getStmt())); - unsigned int NextStmtIdx = currStmtIdx + 1; - if (NextStmtIdx >= B->size()) - return None; - - CFGElement Next = (*B)[NextStmtIdx]; - - // Is this a destructor? If so, we might be in the middle of an assignment - // to a local or member: look ahead one more element to see what we find. - while (Next.getAs<CFGImplicitDtor>() && NextStmtIdx + 1 < B->size()) { - ++NextStmtIdx; - Next = (*B)[NextStmtIdx]; - } + if (MTE->getStorageDuration() == SD_Static || + MTE->getStorageDuration() == SD_Thread) + V = loc::MemRegionVal(MRMgr.getCXXStaticTempObjectRegion(E)); + } - if (canHaveDirectConstructor(Next)) - return Next; + if (V.isUnknown()) + V = loc::MemRegionVal(MRMgr.getCXXTempObjectRegion(E, LCtx)); - return None; -} + if (BTE) + State = addObjectUnderConstruction(State, BTE, LCtx, V); -const CXXConstructExpr * -ExprEngine::findDirectConstructorForCurrentCFGElement() { - // Go backward in the CFG to see if the previous element (ignoring - // destructors) was a CXXConstructExpr. If so, that constructor - // was constructed directly into an existing region. - // This process is essentially the inverse of that performed in - // findElementDirectlyInitializedByCurrentConstructor(). - if (currStmtIdx == 0) - return nullptr; - - const CFGBlock *B = getBuilderContext().getBlock(); - assert(canHaveDirectConstructor((*B)[currStmtIdx])); - - unsigned int PreviousStmtIdx = currStmtIdx - 1; - CFGElement Previous = (*B)[PreviousStmtIdx]; - - while (Previous.getAs<CFGImplicitDtor>() && PreviousStmtIdx > 0) { - --PreviousStmtIdx; - Previous = (*B)[PreviousStmtIdx]; - } + if (MTE) + State = addObjectUnderConstruction(State, MTE, LCtx, V); - if (Optional<CFGStmt> PrevStmtElem = Previous.getAs<CFGStmt>()) { - if (auto *CtorExpr = dyn_cast<CXXConstructExpr>(PrevStmtElem->getStmt())) { - return CtorExpr; + CallOpts.IsTemporaryCtorOrDtor = true; + return std::make_pair(State, V); + } } } - - return nullptr; + // If we couldn't find an existing region to construct into, assume we're + // constructing a temporary. Notify the caller of our failure. + CallOpts.IsCtorOrDtorWithImproperlyModeledTargetRegion = true; + return std::make_pair( + State, loc::MemRegionVal(MRMgr.getCXXTempObjectRegion(E, LCtx))); } void ExprEngine::VisitCXXConstructExpr(const CXXConstructExpr *CE, @@ -237,21 +289,41 @@ void ExprEngine::VisitCXXConstructExpr(const CXXConstructExpr *CE, const LocationContext *LCtx = Pred->getLocationContext(); ProgramStateRef State = Pred->getState(); - const MemRegion *Target = nullptr; + SVal Target = UnknownVal(); + + if (Optional<SVal> ElidedTarget = + getObjectUnderConstruction(State, CE, LCtx)) { + // We've previously modeled an elidable constructor by pretending that it in + // fact constructs into the correct target. This constructor can therefore + // be skipped. + Target = *ElidedTarget; + StmtNodeBuilder Bldr(Pred, destNodes, *currBldrCtx); + State = finishObjectConstruction(State, CE, LCtx); + if (auto L = Target.getAs<Loc>()) + State = State->BindExpr(CE, LCtx, State->getSVal(*L, CE->getType())); + Bldr.generateNode(CE, Pred, State); + return; + } // FIXME: Handle arrays, which run the same constructor for every element. // For now, we just run the first constructor (which should still invalidate // the entire array). + EvalCallOptions CallOpts; + auto C = getCurrentCFGElement().getAs<CFGConstructor>(); + assert(C || getCurrentCFGElement().getAs<CFGStmt>()); + const ConstructionContext *CC = C ? C->getConstructionContext() : nullptr; + switch (CE->getConstructionKind()) { case CXXConstructExpr::CK_Complete: { - Target = getRegionForConstructedObject(CE, Pred); + std::tie(State, Target) = + prepareForObjectConstruction(CE, State, LCtx, CC, CallOpts); break; } 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->getCurrentStackFrame()->getCallSite()) { + if (const Stmt *Outer = LCtx->getStackFrame()->getCallSite()) { const CXXConstructExpr *OuterCtor = dyn_cast<CXXConstructExpr>(Outer); if (OuterCtor) { switch (OuterCtor->getConstructionKind()) { @@ -281,48 +353,59 @@ void ExprEngine::VisitCXXConstructExpr(const CXXConstructExpr *CE, // otherwise always available during construction. if (dyn_cast_or_null<InitListExpr>(LCtx->getParentMap().getParent(CE))) { MemRegionManager &MRMgr = getSValBuilder().getRegionManager(); - Target = MRMgr.getCXXTempObjectRegion(CE, LCtx); + Target = loc::MemRegionVal(MRMgr.getCXXTempObjectRegion(CE, LCtx)); + CallOpts.IsCtorOrDtorWithImproperlyModeledTargetRegion = true; break; } // FALLTHROUGH case CXXConstructExpr::CK_Delegating: { const CXXMethodDecl *CurCtor = cast<CXXMethodDecl>(LCtx->getDecl()); Loc ThisPtr = getSValBuilder().getCXXThis(CurCtor, - LCtx->getCurrentStackFrame()); + LCtx->getStackFrame()); SVal ThisVal = State->getSVal(ThisPtr); if (CE->getConstructionKind() == CXXConstructExpr::CK_Delegating) { - Target = ThisVal.getAsRegion(); + Target = ThisVal; } else { // Cast to the base type. bool IsVirtual = (CE->getConstructionKind() == CXXConstructExpr::CK_VirtualBase); SVal BaseVal = getStoreManager().evalDerivedToBase(ThisVal, CE->getType(), IsVirtual); - Target = BaseVal.getAsRegion(); + Target = BaseVal; } break; } } + if (State != Pred->getState()) { + static SimpleProgramPointTag T("ExprEngine", + "Prepare for object construction"); + ExplodedNodeSet DstPrepare; + StmtNodeBuilder BldrPrepare(Pred, DstPrepare, *currBldrCtx); + BldrPrepare.generateNode(CE, Pred, State, &T, ProgramPoint::PreStmtKind); + assert(DstPrepare.size() <= 1); + if (DstPrepare.size() == 0) + return; + Pred = *BldrPrepare.begin(); + } + CallEventManager &CEMgr = getStateManager().getCallEventManager(); CallEventRef<CXXConstructorCall> Call = - CEMgr.getCXXConstructorCall(CE, Target, State, LCtx); + CEMgr.getCXXConstructorCall(CE, Target.getAsRegion(), State, LCtx); ExplodedNodeSet DstPreVisit; getCheckerManager().runCheckersForPreStmt(DstPreVisit, Pred, CE, *this); + // FIXME: Is it possible and/or useful to do this before PreStmt? ExplodedNodeSet PreInitialized; { StmtNodeBuilder Bldr(DstPreVisit, PreInitialized, *currBldrCtx); - if (CE->requiresZeroInitialization()) { - // Type of the zero doesn't matter. - SVal ZeroVal = svalBuilder.makeZeroVal(getContext().CharTy); - - for (ExplodedNodeSet::iterator I = DstPreVisit.begin(), - E = DstPreVisit.end(); - I != E; ++I) { - ProgramStateRef State = (*I)->getState(); + for (ExplodedNodeSet::iterator I = DstPreVisit.begin(), + E = DstPreVisit.end(); + I != E; ++I) { + ProgramStateRef State = (*I)->getState(); + if (CE->requiresZeroInitialization()) { // FIXME: Once we properly handle constructors in new-expressions, we'll // need to invalidate the region before setting a default value, to make // sure there aren't any lingering bindings around. This probably needs @@ -335,10 +418,11 @@ void ExprEngine::VisitCXXConstructExpr(const CXXConstructExpr *CE, // actually make things worse. Placement new makes this tricky as well, // since it's then possible to be initializing one part of a multi- // dimensional array. - State = State->bindDefault(loc::MemRegionVal(Target), ZeroVal, LCtx); - Bldr.generateNode(CE, *I, State, /*tag=*/nullptr, - ProgramPoint::PreStmtKind); + State = State->bindDefaultZero(Target, LCtx); } + + Bldr.generateNode(CE, *I, State, /*tag=*/nullptr, + ProgramPoint::PreStmtKind); } } @@ -349,10 +433,9 @@ void ExprEngine::VisitCXXConstructExpr(const CXXConstructExpr *CE, ExplodedNodeSet DstEvaluated; StmtNodeBuilder Bldr(DstPreCall, DstEvaluated, *currBldrCtx); - bool IsArray = isa<ElementRegion>(Target); if (CE->getConstructor()->isTrivial() && CE->getConstructor()->isCopyOrMoveConstructor() && - !IsArray) { + !CallOpts.IsArrayCtorOrDtor) { // FIXME: Handle other kinds of trivial constructors as well. for (ExplodedNodeSet::iterator I = DstPreCall.begin(), E = DstPreCall.end(); I != E; ++I) @@ -361,10 +444,10 @@ void ExprEngine::VisitCXXConstructExpr(const CXXConstructExpr *CE, } else { for (ExplodedNodeSet::iterator I = DstPreCall.begin(), E = DstPreCall.end(); I != E; ++I) - defaultEvalCall(Bldr, *I, *Call); + defaultEvalCall(Bldr, *I, *Call, CallOpts); } - // If the CFG was contructed without elements for temporary destructors + // If the CFG was constructed without elements for temporary destructors // and the just-called constructor created a temporary object then // stop exploration if the temporary object has a noreturn constructor. // This can lose coverage because the destructor, if it were present @@ -373,20 +456,30 @@ void ExprEngine::VisitCXXConstructExpr(const CXXConstructExpr *CE, // paths when no-return temporary destructors are used for assertions. const AnalysisDeclContext *ADC = LCtx->getAnalysisDeclContext(); if (!ADC->getCFGBuildOptions().AddTemporaryDtors) { - const MemRegion *Target = Call->getCXXThisVal().getAsRegion(); - if (Target && isa<CXXTempObjectRegion>(Target) && - Call->getDecl()->getParent()->isAnyDestructorNoReturn()) { + const MemRegion *Target = Call->getCXXThisVal().getAsRegion(); + if (Target && isa<CXXTempObjectRegion>(Target) && + Call->getDecl()->getParent()->isAnyDestructorNoReturn()) { + + // If we've inlined the constructor, then DstEvaluated would be empty. + // In this case we still want a sink, which could be implemented + // in processCallExit. But we don't have that implemented at the moment, + // so if you hit this assertion, see if you can avoid inlining + // the respective constructor when analyzer-config cfg-temporary-dtors + // is set to false. + // Otherwise there's nothing wrong with inlining such constructor. + assert(!DstEvaluated.empty() && + "We should not have inlined this constructor!"); for (ExplodedNode *N : DstEvaluated) { Bldr.generateSink(CE, N, N->getState()); } - // There is no need to run the PostCall and PostStmtchecker + // There is no need to run the PostCall and PostStmt checker // callbacks because we just generated sinks on all nodes in th // frontier. return; } - } + } ExplodedNodeSet DstPostCall; getCheckerManager().runCheckersForPostCall(DstPostCall, DstEvaluated, @@ -399,19 +492,11 @@ void ExprEngine::VisitCXXDestructor(QualType ObjectType, const Stmt *S, bool IsBaseDtor, ExplodedNode *Pred, - ExplodedNodeSet &Dst) { + ExplodedNodeSet &Dst, + const EvalCallOptions &CallOpts) { const LocationContext *LCtx = Pred->getLocationContext(); ProgramStateRef State = Pred->getState(); - // FIXME: We need to run the same destructor on every element of the array. - // This workaround will just run the first destructor (which will still - // invalidate the entire array). - SVal DestVal = UnknownVal(); - if (Dest) - DestVal = loc::MemRegionVal(Dest); - DestVal = makeZeroElementRegion(State, DestVal, ObjectType); - Dest = DestVal.getAsRegion(); - const CXXRecordDecl *RecordDecl = ObjectType->getAsCXXRecordDecl(); assert(RecordDecl && "Only CXXRecordDecls should have destructors"); const CXXDestructorDecl *DtorDecl = RecordDecl->getDestructor(); @@ -432,7 +517,7 @@ void ExprEngine::VisitCXXDestructor(QualType ObjectType, StmtNodeBuilder Bldr(DstPreCall, DstInvalidated, *currBldrCtx); for (ExplodedNodeSet::iterator I = DstPreCall.begin(), E = DstPreCall.end(); I != E; ++I) - defaultEvalCall(Bldr, *I, *Call); + defaultEvalCall(Bldr, *I, *Call, CallOpts); ExplodedNodeSet DstPostCall; getCheckerManager().runCheckersForPostCall(Dst, DstInvalidated, @@ -455,15 +540,58 @@ void ExprEngine::VisitCXXNewAllocatorCall(const CXXNewExpr *CNE, getCheckerManager().runCheckersForPreCall(DstPreCall, Pred, *Call, *this); - ExplodedNodeSet DstInvalidated; - StmtNodeBuilder Bldr(DstPreCall, DstInvalidated, *currBldrCtx); - for (ExplodedNodeSet::iterator I = DstPreCall.begin(), E = DstPreCall.end(); - I != E; ++I) - defaultEvalCall(Bldr, *I, *Call); - getCheckerManager().runCheckersForPostCall(Dst, DstInvalidated, - *Call, *this); -} + ExplodedNodeSet DstPostCall; + StmtNodeBuilder CallBldr(DstPreCall, DstPostCall, *currBldrCtx); + for (auto I : DstPreCall) { + // FIXME: Provide evalCall for checkers? + defaultEvalCall(CallBldr, I, *Call); + } + // If the call is inlined, DstPostCall will be empty and we bail out now. + + // Store return value of operator new() for future use, until the actual + // CXXNewExpr gets processed. + ExplodedNodeSet DstPostValue; + StmtNodeBuilder ValueBldr(DstPostCall, DstPostValue, *currBldrCtx); + for (auto I : DstPostCall) { + // FIXME: Because CNE serves as the "call site" for the allocator (due to + // lack of a better expression in the AST), the conjured return value symbol + // is going to be of the same type (C++ object pointer type). Technically + // this is not correct because the operator new's prototype always says that + // it returns a 'void *'. So we should change the type of the symbol, + // and then evaluate the cast over the symbolic pointer from 'void *' to + // the object pointer type. But without changing the symbol's type it + // is breaking too much to evaluate the no-op symbolic cast over it, so we + // skip it for now. + ProgramStateRef State = I->getState(); + SVal RetVal = State->getSVal(CNE, LCtx); + + // If this allocation function is not declared as non-throwing, failures + // /must/ be signalled by exceptions, and thus the return value will never + // be NULL. -fno-exceptions does not influence this semantics. + // FIXME: GCC has a -fcheck-new option, which forces it to consider the case + // where new can return NULL. If we end up supporting that option, we can + // consider adding a check for it here. + // C++11 [basic.stc.dynamic.allocation]p3. + if (const FunctionDecl *FD = CNE->getOperatorNew()) { + QualType Ty = FD->getType(); + if (const auto *ProtoType = Ty->getAs<FunctionProtoType>()) + if (!ProtoType->isNothrow()) + State = State->assume(RetVal.castAs<DefinedOrUnknownSVal>(), true); + } + ValueBldr.generateNode( + CNE, I, addObjectUnderConstruction(State, CNE, LCtx, RetVal)); + } + + ExplodedNodeSet DstPostPostCallCallback; + getCheckerManager().runCheckersForPostCall(DstPostPostCallCallback, + DstPostValue, *Call, *this); + for (auto I : DstPostPostCallCallback) { + getCheckerManager().runCheckersForNewAllocator( + CNE, *getObjectUnderConstruction(I->getState(), CNE, LCtx), Dst, I, + *this); + } +} void ExprEngine::VisitCXXNewExpr(const CXXNewExpr *CNE, ExplodedNode *Pred, ExplodedNodeSet &Dst) { @@ -474,69 +602,74 @@ void ExprEngine::VisitCXXNewExpr(const CXXNewExpr *CNE, ExplodedNode *Pred, unsigned blockCount = currBldrCtx->blockCount(); const LocationContext *LCtx = Pred->getLocationContext(); - DefinedOrUnknownSVal symVal = UnknownVal(); + SVal symVal = UnknownVal(); FunctionDecl *FD = CNE->getOperatorNew(); - bool IsStandardGlobalOpNewFunction = false; - if (FD && !isa<CXXMethodDecl>(FD) && !FD->isVariadic()) { - if (FD->getNumParams() == 2) { - QualType T = FD->getParamDecl(1)->getType(); - if (const IdentifierInfo *II = T.getBaseTypeIdentifier()) - // NoThrow placement new behaves as a standard new. - IsStandardGlobalOpNewFunction = II->getName().equals("nothrow_t"); - } - else - // Placement forms are considered non-standard. - IsStandardGlobalOpNewFunction = (FD->getNumParams() == 1); + bool IsStandardGlobalOpNewFunction = + FD->isReplaceableGlobalAllocationFunction(); + + ProgramStateRef State = Pred->getState(); + + // Retrieve the stored operator new() return value. + if (AMgr.getAnalyzerOptions().mayInlineCXXAllocator()) { + symVal = *getObjectUnderConstruction(State, CNE, LCtx); + State = finishObjectConstruction(State, CNE, LCtx); } // We assume all standard global 'operator new' functions allocate memory in // heap. We realize this is an approximation that might not correctly model // a custom global allocator. - if (IsStandardGlobalOpNewFunction) - symVal = svalBuilder.getConjuredHeapSymbolVal(CNE, LCtx, blockCount); - else - symVal = svalBuilder.conjureSymbolVal(nullptr, CNE, LCtx, CNE->getType(), - blockCount); + if (symVal.isUnknown()) { + if (IsStandardGlobalOpNewFunction) + symVal = svalBuilder.getConjuredHeapSymbolVal(CNE, LCtx, blockCount); + else + symVal = svalBuilder.conjureSymbolVal(nullptr, CNE, LCtx, CNE->getType(), + blockCount); + } - ProgramStateRef State = Pred->getState(); CallEventManager &CEMgr = getStateManager().getCallEventManager(); CallEventRef<CXXAllocatorCall> Call = CEMgr.getCXXAllocatorCall(CNE, State, LCtx); - // Invalidate placement args. - // FIXME: Once we figure out how we want allocators to work, - // we should be using the usual pre-/(default-)eval-/post-call checks here. - State = Call->invalidateRegions(blockCount); - if (!State) - return; + if (!AMgr.getAnalyzerOptions().mayInlineCXXAllocator()) { + // Invalidate placement args. + // FIXME: Once we figure out how we want allocators to work, + // we should be using the usual pre-/(default-)eval-/post-call checks here. + State = Call->invalidateRegions(blockCount); + if (!State) + return; - // If this allocation function is not declared as non-throwing, failures - // /must/ be signalled by exceptions, and thus the return value will never be - // NULL. -fno-exceptions does not influence this semantics. - // FIXME: GCC has a -fcheck-new option, which forces it to consider the case - // where new can return NULL. If we end up supporting that option, we can - // consider adding a check for it here. - // C++11 [basic.stc.dynamic.allocation]p3. - if (FD) { - QualType Ty = FD->getType(); - if (const FunctionProtoType *ProtoType = Ty->getAs<FunctionProtoType>()) - if (!ProtoType->isNothrow(getContext())) - State = State->assume(symVal, true); + // If this allocation function is not declared as non-throwing, failures + // /must/ be signalled by exceptions, and thus the return value will never + // be NULL. -fno-exceptions does not influence this semantics. + // FIXME: GCC has a -fcheck-new option, which forces it to consider the case + // where new can return NULL. If we end up supporting that option, we can + // consider adding a check for it here. + // C++11 [basic.stc.dynamic.allocation]p3. + if (FD) { + QualType Ty = FD->getType(); + if (const auto *ProtoType = Ty->getAs<FunctionProtoType>()) + if (!ProtoType->isNothrow()) + if (auto dSymVal = symVal.getAs<DefinedOrUnknownSVal>()) + State = State->assume(*dSymVal, true); + } } StmtNodeBuilder Bldr(Pred, Dst, *currBldrCtx); + SVal Result = symVal; + if (CNE->isArray()) { // FIXME: allocating an array requires simulating the constructors. // For now, just return a symbolicated region. - const SubRegion *NewReg = - symVal.castAs<loc::MemRegionVal>().getRegionAs<SubRegion>(); - QualType ObjTy = CNE->getType()->getAs<PointerType>()->getPointeeType(); - const ElementRegion *EleReg = - getStoreManager().GetElementZeroRegion(NewReg, ObjTy); - State = State->BindExpr(CNE, Pred->getLocationContext(), - loc::MemRegionVal(EleReg)); + if (const SubRegion *NewReg = + dyn_cast_or_null<SubRegion>(symVal.getAsRegion())) { + QualType ObjTy = CNE->getType()->getAs<PointerType>()->getPointeeType(); + const ElementRegion *EleReg = + getStoreManager().GetElementZeroRegion(NewReg, ObjTy); + Result = loc::MemRegionVal(EleReg); + } + State = State->BindExpr(CNE, Pred->getLocationContext(), Result); Bldr.generateNode(CNE, Pred, State); return; } @@ -545,7 +678,6 @@ void ExprEngine::VisitCXXNewExpr(const CXXNewExpr *CNE, ExplodedNode *Pred, // CXXNewExpr, we need to make sure that the constructed object is not // immediately invalidated here. (The placement call should happen before // the constructor call anyway.) - SVal Result = symVal; if (FD && FD->isReservedGlobalPlacementOperator()) { // Non-array placement new should always return the placement location. SVal PlacementLoc = State->getSVal(CNE->getPlacementArg(0), LCtx); diff --git a/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp b/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp index caf86b26b66d..3ee67f3d6882 100644 --- a/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp +++ b/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp @@ -15,8 +15,8 @@ #include "PrettyStackTraceLocationContext.h" #include "clang/AST/CXXInheritance.h" #include "clang/AST/DeclCXX.h" -#include "clang/AST/ParentMap.h" #include "clang/Analysis/Analyses/LiveVariables.h" +#include "clang/Analysis/ConstructionContext.h" #include "clang/StaticAnalyzer/Core/CheckerManager.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" #include "llvm/ADT/SmallSet.h" @@ -74,15 +74,14 @@ static std::pair<const Stmt*, const CFGBlock*> getLastStmt(const ExplodedNode *Node) { const Stmt *S = nullptr; const CFGBlock *Blk = nullptr; - const StackFrameContext *SF = - Node->getLocation().getLocationContext()->getCurrentStackFrame(); + const StackFrameContext *SF = Node->getStackFrame(); // Back up through the ExplodedGraph until we reach a statement node in this // stack frame. while (Node) { const ProgramPoint &PP = Node->getLocation(); - if (PP.getLocationContext()->getCurrentStackFrame() == SF) { + if (PP.getStackFrame() == SF) { if (Optional<StmtPoint> SP = PP.getAs<StmtPoint>()) { S = SP->getStmt(); break; @@ -121,7 +120,7 @@ static std::pair<const Stmt*, /// Adjusts a return value when the called function's return type does not /// match the caller's expression type. This can happen when a dynamic call -/// is devirtualized, and the overridding method has a covariant (more specific) +/// is devirtualized, and the overriding method has a covariant (more specific) /// return type than the parent's method. For C++ objects, this means we need /// to add base casts. static SVal adjustReturnValue(SVal V, QualType ExpectedTy, QualType ActualTy, @@ -193,23 +192,6 @@ static bool wasDifferentDeclUsedForInlining(CallEventRef<> Call, return RuntimeCallee->getCanonicalDecl() != StaticDecl->getCanonicalDecl(); } -/// Returns true if the CXXConstructExpr \p E was intended to construct a -/// prvalue for the region in \p V. -/// -/// Note that we can't just test for rvalue vs. glvalue because -/// CXXConstructExprs embedded in DeclStmts and initializers are considered -/// rvalues by the AST, and the analyzer would like to treat them as lvalues. -static bool isTemporaryPRValue(const CXXConstructExpr *E, SVal V) { - if (E->isGLValue()) - return false; - - const MemRegion *MR = V.getAsRegion(); - if (!MR) - return false; - - return isa<CXXTempObjectRegion>(MR); -} - /// The call exit is simulated with a sequence of nodes, which occur between /// CallExitBegin and CallExitEnd. The following operations occur between the /// two program points: @@ -221,13 +203,12 @@ static bool isTemporaryPRValue(const CXXConstructExpr *E, SVal V) { void ExprEngine::processCallExit(ExplodedNode *CEBNode) { // Step 1 CEBNode was generated before the call. PrettyStackTraceLocationContext CrashInfo(CEBNode->getLocationContext()); - const StackFrameContext *calleeCtx = - CEBNode->getLocationContext()->getCurrentStackFrame(); + const StackFrameContext *calleeCtx = CEBNode->getStackFrame(); // The parent context might not be a stack frame, so make sure we // look up the first enclosing stack frame. const StackFrameContext *callerCtx = - calleeCtx->getParent()->getCurrentStackFrame(); + calleeCtx->getParent()->getStackFrame(); const Stmt *CE = calleeCtx->getCallSite(); ProgramStateRef state = CEBNode->getState(); @@ -269,13 +250,24 @@ void ExprEngine::processCallExit(ExplodedNode *CEBNode) { loc::MemRegionVal This = svalBuilder.getCXXThis(CCE->getConstructor()->getParent(), calleeCtx); SVal ThisV = state->getSVal(This); - - // If the constructed object is a temporary prvalue, get its bindings. - if (isTemporaryPRValue(CCE, ThisV)) - ThisV = state->getSVal(ThisV.castAs<Loc>()); - + ThisV = state->getSVal(ThisV.castAs<Loc>()); state = state->BindExpr(CCE, callerCtx, ThisV); } + + if (const auto *CNE = dyn_cast<CXXNewExpr>(CE)) { + // We are currently evaluating a CXXNewAllocator CFGElement. It takes a + // while to reach the actual CXXNewExpr element from here, so keep the + // region for later use. + // Additionally cast the return value of the inlined operator new + // (which is of type 'void *') to the correct object type. + SVal AllocV = state->getSVal(CNE, callerCtx); + AllocV = svalBuilder.evalCast( + AllocV, CNE->getType(), + getContext().getPointerType(getContext().VoidTy)); + + state = addObjectUnderConstruction(state, CNE, calleeCtx->getParent(), + AllocV); + } } // Step 3: BindedRetNode -> CleanedNodes @@ -315,6 +307,7 @@ void ExprEngine::processCallExit(ExplodedNode *CEBNode) { CallExitEnd Loc(calleeCtx, callerCtx); bool isNew; ProgramStateRef CEEState = (*I == CEBNode) ? state : (*I)->getState(); + ExplodedNode *CEENode = G.getNode(Loc, CEEState, false, &isNew); CEENode->addPredecessor(*I, G); if (!isNew) @@ -331,16 +324,32 @@ void ExprEngine::processCallExit(ExplodedNode *CEBNode) { CallEventRef<> UpdatedCall = Call.cloneWithState(CEEState); ExplodedNodeSet DstPostCall; - getCheckerManager().runCheckersForPostCall(DstPostCall, CEENode, - *UpdatedCall, *this, - /*WasInlined=*/true); - + if (const CXXNewExpr *CNE = dyn_cast_or_null<CXXNewExpr>(CE)) { + ExplodedNodeSet DstPostPostCallCallback; + getCheckerManager().runCheckersForPostCall(DstPostPostCallCallback, + CEENode, *UpdatedCall, *this, + /*WasInlined=*/true); + for (auto I : DstPostPostCallCallback) { + getCheckerManager().runCheckersForNewAllocator( + CNE, + *getObjectUnderConstruction(I->getState(), CNE, + calleeCtx->getParent()), + DstPostCall, I, *this, + /*WasInlined=*/true); + } + } else { + getCheckerManager().runCheckersForPostCall(DstPostCall, CEENode, + *UpdatedCall, *this, + /*WasInlined=*/true); + } ExplodedNodeSet Dst; if (const ObjCMethodCall *Msg = dyn_cast<ObjCMethodCall>(Call)) { getCheckerManager().runCheckersForPostObjCMessage(Dst, DstPostCall, *Msg, *this, /*WasInlined=*/true); - } else if (CE) { + } else if (CE && + !(isa<CXXNewExpr>(CE) && // Called when visiting CXXNewExpr. + AMgr.getAnalyzerOptions().mayInlineCXXAllocator())) { getCheckerManager().runCheckersForPostStmt(Dst, DstPostCall, CE, *this, /*WasInlined=*/true); } else { @@ -407,7 +416,7 @@ bool ExprEngine::inlineCall(const CallEvent &Call, const Decl *D, assert(D); const LocationContext *CurLC = Pred->getLocationContext(); - const StackFrameContext *CallerSFC = CurLC->getCurrentStackFrame(); + const StackFrameContext *CallerSFC = CurLC->getStackFrame(); const LocationContext *ParentOfCallee = CallerSFC; if (Call.getKind() == CE_Block && !cast<BlockCall>(Call).isConversionFromLambda()) { @@ -542,19 +551,45 @@ ProgramStateRef ExprEngine::bindReturnValue(const CallEvent &Call, } } else if (const CXXConstructorCall *C = dyn_cast<CXXConstructorCall>(&Call)){ SVal ThisV = C->getCXXThisVal(); - - // If the constructed object is a temporary prvalue, get its bindings. - if (isTemporaryPRValue(cast<CXXConstructExpr>(E), ThisV)) - ThisV = State->getSVal(ThisV.castAs<Loc>()); - + ThisV = State->getSVal(ThisV.castAs<Loc>()); return State->BindExpr(E, LCtx, ThisV); } - // Conjure a symbol if the return value is unknown. + SVal R; QualType ResultTy = Call.getResultType(); - SValBuilder &SVB = getSValBuilder(); unsigned Count = currBldrCtx->blockCount(); - SVal R = SVB.conjureSymbolVal(nullptr, E, LCtx, ResultTy, Count); + if (auto RTC = getCurrentCFGElement().getAs<CFGCXXRecordTypedCall>()) { + // Conjure a temporary if the function returns an object by value. + SVal Target; + assert(RTC->getStmt() == Call.getOriginExpr()); + EvalCallOptions CallOpts; // FIXME: We won't really need those. + 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); + + R = State->getSVal(Target.castAs<Loc>(), E->getType()); + } else { + // Conjure a symbol if the return value is unknown. + + // See if we need to conjure a heap pointer instead of + // a regular unknown pointer. + bool IsHeapPointer = false; + if (const auto *CNE = dyn_cast<CXXNewExpr>(E)) + if (CNE->getOperatorNew()->isReplaceableGlobalAllocationFunction()) { + // FIXME: Delegate this to evalCall in MallocChecker? + IsHeapPointer = true; + } + + R = IsHeapPointer ? svalBuilder.getConjuredHeapSymbolVal(E, LCtx, Count) + : svalBuilder.conjureSymbolVal(nullptr, E, LCtx, ResultTy, + Count); + } return State->BindExpr(E, LCtx, R); } @@ -570,17 +605,12 @@ void ExprEngine::conservativeEvalCall(const CallEvent &Call, NodeBuilder &Bldr, Bldr.generateNode(Call.getProgramPoint(), State, Pred); } -enum CallInlinePolicy { - CIP_Allowed, - CIP_DisallowedOnce, - CIP_DisallowedAlways -}; - -static CallInlinePolicy mayInlineCallKind(const CallEvent &Call, - const ExplodedNode *Pred, - AnalyzerOptions &Opts) { +ExprEngine::CallInlinePolicy +ExprEngine::mayInlineCallKind(const CallEvent &Call, const ExplodedNode *Pred, + AnalyzerOptions &Opts, + const ExprEngine::EvalCallOptions &CallOpts) { const LocationContext *CurLC = Pred->getLocationContext(); - const StackFrameContext *CallerSFC = CurLC->getCurrentStackFrame(); + const StackFrameContext *CallerSFC = CurLC->getStackFrame(); switch (Call.getKind()) { case CE_Function: case CE_Block: @@ -596,22 +626,24 @@ static CallInlinePolicy mayInlineCallKind(const CallEvent &Call, const CXXConstructorCall &Ctor = cast<CXXConstructorCall>(Call); + const CXXConstructExpr *CtorExpr = Ctor.getOriginExpr(); + + auto CCE = getCurrentCFGElement().getAs<CFGConstructor>(); + const ConstructionContext *CC = CCE ? CCE->getConstructionContext() + : nullptr; + + if (CC && isa<NewAllocatedObjectConstructionContext>(CC) && + !Opts.mayInlineCXXAllocator()) + return CIP_DisallowedOnce; + // FIXME: We don't handle constructors or destructors for arrays properly. // Even once we do, we still need to be careful about implicitly-generated // initializers for array fields in default move/copy constructors. - const MemRegion *Target = Ctor.getCXXThisVal().getAsRegion(); - if (Target && isa<ElementRegion>(Target)) + // We still allow construction into ElementRegion targets when they don't + // represent array elements. + if (CallOpts.IsArrayCtorOrDtor) return CIP_DisallowedOnce; - // FIXME: This is a hack. We don't use the correct region for a new - // expression, so if we inline the constructor its result will just be - // thrown away. This short-term hack is tracked in <rdar://problem/12180598> - // and the longer-term possible fix is discussed in PR12014. - const CXXConstructExpr *CtorExpr = Ctor.getOriginExpr(); - if (const Stmt *Parent = CurLC->getParentMap().getParent(CtorExpr)) - if (isa<CXXNewExpr>(Parent)) - return CIP_DisallowedOnce; - // Inlining constructors requires including initializers in the CFG. const AnalysisDeclContext *ADC = CallerSFC->getAnalysisDeclContext(); assert(ADC->getCFGBuildOptions().AddInitializers && "No CFG initializers"); @@ -626,12 +658,25 @@ static CallInlinePolicy mayInlineCallKind(const CallEvent &Call, if (!Opts.mayInlineCXXMemberFunction(CIMK_Destructors)) return CIP_DisallowedAlways; - // FIXME: This is a hack. We don't handle temporary destructors - // right now, so we shouldn't inline their constructors. - if (CtorExpr->getConstructionKind() == CXXConstructExpr::CK_Complete) - if (!Target || !isa<DeclRegion>(Target)) + if (CtorExpr->getConstructionKind() == CXXConstructExpr::CK_Complete) { + // If we don't handle temporary destructors, we shouldn't inline + // their constructors. + if (CallOpts.IsTemporaryCtorOrDtor && + !Opts.includeTemporaryDtorsInCFG()) + return CIP_DisallowedOnce; + + // If we did not find the correct this-region, it would be pointless + // to inline the constructor. Instead we will simply invalidate + // the fake temporary target. + if (CallOpts.IsCtorOrDtorWithImproperlyModeledTargetRegion) return CIP_DisallowedOnce; + // If the temporary is lifetime-extended by binding it to a reference-type + // field within an aggregate, automatic destructors don't work properly. + if (CallOpts.IsTemporaryLifetimeExtendedViaAggregate) + return CIP_DisallowedOnce; + } + break; } case CE_CXXDestructor: { @@ -643,13 +688,19 @@ static CallInlinePolicy mayInlineCallKind(const CallEvent &Call, assert(ADC->getCFGBuildOptions().AddImplicitDtors && "No CFG destructors"); (void)ADC; - const CXXDestructorCall &Dtor = cast<CXXDestructorCall>(Call); - // FIXME: We don't handle constructors or destructors for arrays properly. - const MemRegion *Target = Dtor.getCXXThisVal().getAsRegion(); - if (Target && isa<ElementRegion>(Target)) + if (CallOpts.IsArrayCtorOrDtor) + return CIP_DisallowedOnce; + + // Allow disabling temporary destructor inlining with a separate option. + if (CallOpts.IsTemporaryCtorOrDtor && !Opts.mayInlineCXXTemporaryDtors()) return CIP_DisallowedOnce; + // If we did not find the correct this-region, it would be pointless + // to inline the destructor. Instead we will simply invalidate + // the fake temporary target. + if (CallOpts.IsCtorOrDtorWithImproperlyModeledTargetRegion) + return CIP_DisallowedOnce; break; } case CE_CXXAllocator: @@ -731,8 +782,9 @@ 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(AnalysisDeclContext *CalleeADC, - AnalyzerOptions &Opts) { +static bool mayInlineDecl(AnalysisManager &AMgr, + AnalysisDeclContext *CalleeADC) { + AnalyzerOptions &Opts = AMgr.getAnalyzerOptions(); // FIXME: Do not inline variadic calls. if (CallEvent::isVariadic(CalleeADC->getDecl())) return false; @@ -755,7 +807,7 @@ static bool mayInlineDecl(AnalysisDeclContext *CalleeADC, // Conditionally control the inlining of methods on objects that look // like C++ containers. if (!Opts.mayInlineCXXContainerMethods()) - if (!Ctx.getSourceManager().isInMainFile(FD->getLocation())) + if (!AMgr.isInCodeFile(FD->getLocation())) if (isContainerMethod(Ctx, FD)) return false; @@ -788,7 +840,8 @@ static bool mayInlineDecl(AnalysisDeclContext *CalleeADC, } bool ExprEngine::shouldInlineCall(const CallEvent &Call, const Decl *D, - const ExplodedNode *Pred) { + const ExplodedNode *Pred, + const EvalCallOptions &CallOpts) { if (!D) return false; @@ -797,14 +850,6 @@ bool ExprEngine::shouldInlineCall(const CallEvent &Call, const Decl *D, AnalysisDeclContextManager &ADCMgr = AMgr.getAnalysisDeclContextManager(); AnalysisDeclContext *CalleeADC = ADCMgr.getContext(D); - // Temporary object destructor processing is currently broken, so we never - // inline them. - // FIXME: Remove this once temp destructors are working. - if (isa<CXXDestructorCall>(Call)) { - if ((*currBldrCtx->getBlock())[currStmtIdx].getAs<CFGTemporaryDtor>()) - return false; - } - // The auto-synthesized bodies are essential to inline as they are // usually small and commonly used. Note: we should do this check early on to // ensure we always inline these calls. @@ -823,7 +868,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(CalleeADC, Opts)) { + if (mayInlineDecl(getAnalysisManager(), CalleeADC)) { Engine.FunctionSummaries->markMayInline(D); } else { Engine.FunctionSummaries->markShouldNotInline(D); @@ -835,7 +880,7 @@ bool ExprEngine::shouldInlineCall(const CallEvent &Call, const Decl *D, // FIXME: this checks both static and dynamic properties of the call, which // means we're redoing a bit of work that could be cached in the function // summary. - CallInlinePolicy CIP = mayInlineCallKind(Call, Pred, Opts); + CallInlinePolicy CIP = mayInlineCallKind(Call, Pred, Opts, CallOpts); if (CIP != CIP_Allowed) { if (CIP == CIP_DisallowedAlways) { assert(!MayInline.hasValue() || MayInline.getValue()); @@ -887,7 +932,8 @@ static bool isTrivialObjectAssignment(const CallEvent &Call) { } void ExprEngine::defaultEvalCall(NodeBuilder &Bldr, ExplodedNode *Pred, - const CallEvent &CallTemplate) { + const CallEvent &CallTemplate, + const EvalCallOptions &CallOpts) { // Make sure we have the most recent state attached to the call. ProgramStateRef State = Pred->getState(); CallEventRef<> Call = CallTemplate.cloneWithState(State); @@ -910,7 +956,7 @@ void ExprEngine::defaultEvalCall(NodeBuilder &Bldr, ExplodedNode *Pred, } else { RuntimeDefinition RD = Call->getRuntimeDefinition(); const Decl *D = RD.getDecl(); - if (shouldInlineCall(*Call, D, Pred)) { + if (shouldInlineCall(*Call, D, Pred, CallOpts)) { if (RD.mayHaveOtherDefinitions()) { AnalyzerOptions &Options = getAnalysisManager().options; diff --git a/lib/StaticAnalyzer/Core/ExprEngineObjC.cpp b/lib/StaticAnalyzer/Core/ExprEngineObjC.cpp index f5e64f4a5a8c..d76b9cbcfaca 100644 --- a/lib/StaticAnalyzer/Core/ExprEngineObjC.cpp +++ b/lib/StaticAnalyzer/Core/ExprEngineObjC.cpp @@ -42,6 +42,47 @@ void ExprEngine::VisitObjCAtSynchronizedStmt(const ObjCAtSynchronizedStmt *S, getCheckerManager().runCheckersForPreStmt(Dst, Pred, S, *this); } +/// Generate a node in \p Bldr for an iteration statement using ObjC +/// for-loop iterator. +static void populateObjCForDestinationSet( + ExplodedNodeSet &dstLocation, SValBuilder &svalBuilder, + const ObjCForCollectionStmt *S, const Stmt *elem, SVal elementV, + SymbolManager &SymMgr, const NodeBuilderContext *currBldrCtx, + StmtNodeBuilder &Bldr, bool hasElements) { + + for (ExplodedNode *Pred : dstLocation) { + ProgramStateRef state = Pred->getState(); + const LocationContext *LCtx = Pred->getLocationContext(); + + SVal hasElementsV = svalBuilder.makeTruthVal(hasElements); + + // FIXME: S is not an expression. We should not be binding values to it. + ProgramStateRef nextState = state->BindExpr(S, LCtx, hasElementsV); + + if (auto MV = elementV.getAs<loc::MemRegionVal>()) + if (const auto *R = dyn_cast<TypedValueRegion>(MV->getRegion())) { + // FIXME: The proper thing to do is to really iterate over the + // container. We will do this with dispatch logic to the store. + // For now, just 'conjure' up a symbolic value. + QualType T = R->getValueType(); + assert(Loc::isLocType(T)); + + SVal V; + if (hasElements) { + SymbolRef Sym = SymMgr.conjureSymbol(elem, LCtx, T, + currBldrCtx->blockCount()); + V = svalBuilder.makeLoc(Sym); + } else { + V = svalBuilder.makeIntVal(0, T); + } + + nextState = nextState->bindLoc(elementV, V, LCtx); + } + + Bldr.generateNode(S, Pred, nextState); + } +} + void ExprEngine::VisitObjCForCollectionStmt(const ObjCForCollectionStmt *S, ExplodedNode *Pred, ExplodedNodeSet &Dst) { @@ -72,60 +113,35 @@ void ExprEngine::VisitObjCForCollectionStmt(const ObjCForCollectionStmt *S, // result in state splitting. const Stmt *elem = S->getElement(); + const Stmt *collection = S->getCollection(); ProgramStateRef state = Pred->getState(); - SVal elementV; + SVal collectionV = state->getSVal(collection, Pred->getLocationContext()); - if (const DeclStmt *DS = dyn_cast<DeclStmt>(elem)) { + SVal elementV; + if (const auto *DS = dyn_cast<DeclStmt>(elem)) { const VarDecl *elemD = cast<VarDecl>(DS->getSingleDecl()); assert(elemD->getInit() == nullptr); elementV = state->getLValue(elemD, Pred->getLocationContext()); - } - else { + } else { elementV = state->getSVal(elem, Pred->getLocationContext()); } + bool isContainerNull = state->isNull(collectionV).isConstrainedTrue(); + ExplodedNodeSet dstLocation; evalLocation(dstLocation, S, elem, Pred, state, elementV, nullptr, false); ExplodedNodeSet Tmp; StmtNodeBuilder Bldr(Pred, Tmp, *currBldrCtx); - for (ExplodedNodeSet::iterator NI = dstLocation.begin(), - NE = dstLocation.end(); NI!=NE; ++NI) { - Pred = *NI; - ProgramStateRef state = Pred->getState(); - const LocationContext *LCtx = Pred->getLocationContext(); - - // Handle the case where the container still has elements. - SVal TrueV = svalBuilder.makeTruthVal(1); - ProgramStateRef hasElems = state->BindExpr(S, LCtx, TrueV); - - // Handle the case where the container has no elements. - SVal FalseV = svalBuilder.makeTruthVal(0); - ProgramStateRef noElems = state->BindExpr(S, LCtx, FalseV); + if (!isContainerNull) + populateObjCForDestinationSet(dstLocation, svalBuilder, S, elem, elementV, + SymMgr, currBldrCtx, Bldr, + /*hasElements=*/true); - if (Optional<loc::MemRegionVal> MV = elementV.getAs<loc::MemRegionVal>()) - if (const TypedValueRegion *R = - dyn_cast<TypedValueRegion>(MV->getRegion())) { - // FIXME: The proper thing to do is to really iterate over the - // container. We will do this with dispatch logic to the store. - // For now, just 'conjure' up a symbolic value. - QualType T = R->getValueType(); - assert(Loc::isLocType(T)); - SymbolRef Sym = SymMgr.conjureSymbol(elem, LCtx, T, - currBldrCtx->blockCount()); - SVal V = svalBuilder.makeLoc(Sym); - hasElems = hasElems->bindLoc(elementV, V, LCtx); - - // Bind the location to 'nil' on the false branch. - SVal nilV = svalBuilder.makeIntVal(0, T); - noElems = noElems->bindLoc(elementV, nilV, LCtx); - } - - // Create the new nodes. - Bldr.generateNode(S, Pred, hasElems); - Bldr.generateNode(S, Pred, noElems); - } + populateObjCForDestinationSet(dstLocation, svalBuilder, S, elem, elementV, + SymMgr, currBldrCtx, Bldr, + /*hasElements=*/false); // Finally, run any custom checkers. // FIXME: Eventually all pre- and post-checks should live in VisitStmt. diff --git a/lib/StaticAnalyzer/Core/FunctionSummary.cpp b/lib/StaticAnalyzer/Core/FunctionSummary.cpp index c21735b8b882..94edd84d15d2 100644 --- a/lib/StaticAnalyzer/Core/FunctionSummary.cpp +++ b/lib/StaticAnalyzer/Core/FunctionSummary.cpp @@ -1,4 +1,4 @@ -//== FunctionSummary.cpp - Stores summaries of functions. ----------*- C++ -*-// +//===- FunctionSummary.cpp - Stores summaries of functions. ---------------===// // // The LLVM Compiler Infrastructure // @@ -12,21 +12,20 @@ //===----------------------------------------------------------------------===// #include "clang/StaticAnalyzer/Core/PathSensitive/FunctionSummary.h" + using namespace clang; using namespace ento; unsigned FunctionSummariesTy::getTotalNumBasicBlocks() { unsigned Total = 0; - for (MapTy::iterator I = Map.begin(), E = Map.end(); I != E; ++I) { - Total += I->second.TotalBasicBlocks; - } + for (const auto &I : Map) + Total += I.second.TotalBasicBlocks; return Total; } unsigned FunctionSummariesTy::getTotalNumVisitedBasicBlocks() { unsigned Total = 0; - for (MapTy::iterator I = Map.begin(), E = Map.end(); I != E; ++I) { - Total += I->second.VisitedBasicBlocks.count(); - } + for (const auto &I : Map) + Total += I.second.VisitedBasicBlocks.count(); return Total; } diff --git a/lib/StaticAnalyzer/Core/HTMLDiagnostics.cpp b/lib/StaticAnalyzer/Core/HTMLDiagnostics.cpp index ebf1487d4bfc..d5e5f96dee0f 100644 --- a/lib/StaticAnalyzer/Core/HTMLDiagnostics.cpp +++ b/lib/StaticAnalyzer/Core/HTMLDiagnostics.cpp @@ -1,4 +1,4 @@ -//===--- HTMLDiagnostics.cpp - HTML Diagnostics for Paths ----*- C++ -*-===// +//===- HTMLDiagnostics.cpp - HTML Diagnostics for Paths -------------------===// // // The LLVM Compiler Infrastructure // @@ -11,24 +11,43 @@ // //===----------------------------------------------------------------------===// -#include "clang/AST/ASTContext.h" #include "clang/AST/Decl.h" +#include "clang/AST/DeclBase.h" +#include "clang/AST/Stmt.h" #include "clang/Basic/FileManager.h" +#include "clang/Basic/LLVM.h" +#include "clang/Basic/SourceLocation.h" #include "clang/Basic/SourceManager.h" #include "clang/Lex/Lexer.h" #include "clang/Lex/Preprocessor.h" +#include "clang/Lex/Token.h" #include "clang/Rewrite/Core/HTMLRewrite.h" #include "clang/Rewrite/Core/Rewriter.h" +#include "clang/StaticAnalyzer/Core/AnalyzerOptions.h" #include "clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h" -#include "clang/StaticAnalyzer/Core/CheckerManager.h" #include "clang/StaticAnalyzer/Core/IssueHash.h" #include "clang/StaticAnalyzer/Core/PathDiagnosticConsumers.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/ADT/iterator_range.h" +#include "llvm/Support/Casting.h" #include "llvm/Support/Errc.h" +#include "llvm/Support/ErrorHandling.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/Path.h" #include "llvm/Support/raw_ostream.h" +#include <algorithm> +#include <cassert> +#include <map> +#include <memory> +#include <set> #include <sstream> +#include <string> +#include <system_error> +#include <utility> +#include <vector> using namespace clang; using namespace ento; @@ -41,15 +60,19 @@ namespace { class HTMLDiagnostics : public PathDiagnosticConsumer { std::string Directory; - bool createdDir, noDir; + bool createdDir = false; + bool noDir = false; const Preprocessor &PP; AnalyzerOptions &AnalyzerOpts; const bool SupportsCrossFileDiagnostics; + public: HTMLDiagnostics(AnalyzerOptions &AnalyzerOpts, const std::string& prefix, const Preprocessor &pp, - bool supportsMultipleFiles); + bool supportsMultipleFiles) + : Directory(prefix), PP(pp), AnalyzerOpts(AnalyzerOpts), + SupportsCrossFileDiagnostics(supportsMultipleFiles) {} ~HTMLDiagnostics() override { FlushDiagnostics(nullptr); } @@ -94,20 +117,13 @@ public: /// \return Javascript for navigating the HTML report using j/k keys. std::string generateKeyboardNavigationJavascript(); -}; -} // end anonymous namespace +private: + /// \return Javascript for displaying shortcuts help; + std::string showHelpJavascript(); +}; -HTMLDiagnostics::HTMLDiagnostics(AnalyzerOptions &AnalyzerOpts, - const std::string& prefix, - const Preprocessor &pp, - bool supportsMultipleFiles) - : Directory(prefix), - createdDir(false), - noDir(false), - PP(pp), - AnalyzerOpts(AnalyzerOpts), - SupportsCrossFileDiagnostics(supportsMultipleFiles) {} +} // namespace void ento::createHTMLDiagnosticConsumer(AnalyzerOptions &AnalyzerOpts, PathDiagnosticConsumers &C, @@ -130,24 +146,19 @@ void ento::createHTMLSingleFileDiagnosticConsumer(AnalyzerOptions &AnalyzerOpts, void HTMLDiagnostics::FlushDiagnosticsImpl( std::vector<const PathDiagnostic *> &Diags, FilesMade *filesMade) { - for (std::vector<const PathDiagnostic *>::iterator it = Diags.begin(), - et = Diags.end(); it != et; ++it) { - ReportDiag(**it, filesMade); - } + for (const auto Diag : Diags) + ReportDiag(*Diag, filesMade); } void HTMLDiagnostics::ReportDiag(const PathDiagnostic& D, FilesMade *filesMade) { - // Create the HTML directory if it is missing. if (!createdDir) { createdDir = true; if (std::error_code ec = llvm::sys::fs::create_directories(Directory)) { llvm::errs() << "warning: could not create directory '" << Directory << "': " << ec.message() << '\n'; - noDir = true; - return; } } @@ -174,7 +185,7 @@ void HTMLDiagnostics::ReportDiag(const PathDiagnostic& D, SmallString<128> declName("unknown"); int offsetDecl = 0; if (const Decl *DeclWithIssue = D.getDeclWithIssue()) { - if (const NamedDecl *ND = dyn_cast<NamedDecl>(DeclWithIssue)) + if (const auto *ND = dyn_cast<NamedDecl>(DeclWithIssue)) declName = ND->getDeclName().getAsString(); if (const Stmt *Body = DeclWithIssue->getBody()) { @@ -212,7 +223,6 @@ void HTMLDiagnostics::ReportDiag(const PathDiagnostic& D, << "': " << EC.message() << '\n'; return; } - } else { int i = 1; std::error_code EC; @@ -228,10 +238,8 @@ void HTMLDiagnostics::ReportDiag(const PathDiagnostic& D, << "-" << i << ".html"; llvm::sys::path::append(Model, Directory, filename.str()); - EC = llvm::sys::fs::openFileForWrite(Model, - FD, - llvm::sys::fs::F_RW | - llvm::sys::fs::F_Excl); + EC = llvm::sys::fs::openFileForReadWrite( + Model, FD, llvm::sys::fs::CD_CreateNew, llvm::sys::fs::OF_None); if (EC && EC != llvm::errc::file_exists) { llvm::errs() << "warning: could not create file '" << Model << "': " << EC.message() << '\n'; @@ -253,7 +261,6 @@ void HTMLDiagnostics::ReportDiag(const PathDiagnostic& D, std::string HTMLDiagnostics::GenerateHTML(const PathDiagnostic& D, Rewriter &R, const SourceManager& SMgr, const PathPieces& path, const char *declName) { - // Rewrite source files as HTML for every new file the path crosses std::vector<FileID> FileIDs; for (auto I : path) { @@ -309,10 +316,12 @@ std::string HTMLDiagnostics::GenerateHTML(const PathDiagnostic& D, Rewriter &R, const RewriteBuffer *Buf = R.getRewriteBufferFor(FileIDs[0]); if (!Buf) - return ""; + return {}; // Add CSS, header, and footer. - const FileEntry* Entry = SMgr.getFileEntryForID(FileIDs[0]); + FileID FID = + path.back()->getLocation().asLocation().getExpansionLoc().getFileID(); + const FileEntry* Entry = SMgr.getFileEntryForID(FID); FinalizeHTML(D, R, SMgr, path, FileIDs[0], Entry, declName); std::string file; @@ -323,6 +332,114 @@ std::string HTMLDiagnostics::GenerateHTML(const PathDiagnostic& D, Rewriter &R, return os.str(); } +/// Write executed lines from \p D in JSON format into \p os. +static void serializeExecutedLines( + const PathDiagnostic &D, + const PathPieces &path, + llvm::raw_string_ostream &os) { + // Copy executed lines from path diagnostics. + std::map<unsigned, std::set<unsigned>> ExecutedLines; + for (auto I = D.executedLines_begin(), + E = D.executedLines_end(); I != E; ++I) { + std::set<unsigned> &LinesInFile = ExecutedLines[I->first]; + for (unsigned LineNo : I->second) { + LinesInFile.insert(LineNo); + } + } + + // We need to include all lines for which any kind of diagnostics appears. + for (const auto &P : path) { + FullSourceLoc Loc = P->getLocation().asLocation().getExpansionLoc(); + FileID FID = Loc.getFileID(); + unsigned LineNo = Loc.getLineNumber(); + ExecutedLines[FID.getHashValue()].insert(LineNo); + } + + os << "var relevant_lines = {"; + for (auto I = ExecutedLines.begin(), + E = ExecutedLines.end(); I != E; ++I) { + if (I != ExecutedLines.begin()) + os << ", "; + + os << "\"" << I->first << "\": {"; + for (unsigned LineNo : I->second) { + if (LineNo != *(I->second.begin())) + os << ", "; + + os << "\"" << LineNo << "\": 1"; + } + os << "}"; + } + + os << "};"; +} + +/// \return JavaScript for an option to only show relevant lines. +static std::string showRelevantLinesJavascript( + const PathDiagnostic &D, const PathPieces &path) { + std::string s; + llvm::raw_string_ostream os(s); + os << "<script type='text/javascript'>\n"; + serializeExecutedLines(D, path, os); + os << R"<<<( + +var filterCounterexample = function (hide) { + var tables = document.getElementsByClassName("code"); + for (var t=0; t<tables.length; t++) { + var table = tables[t]; + var file_id = table.getAttribute("data-fileid"); + var lines_in_fid = relevant_lines[file_id]; + if (!lines_in_fid) { + lines_in_fid = {}; + } + var lines = table.getElementsByClassName("codeline"); + for (var i=0; i<lines.length; i++) { + var el = lines[i]; + var lineNo = el.getAttribute("data-linenumber"); + if (!lines_in_fid[lineNo]) { + if (hide) { + el.setAttribute("hidden", ""); + } else { + el.removeAttribute("hidden"); + } + } + } + } +} + +window.addEventListener("keydown", function (event) { + if (event.defaultPrevented) { + return; + } + if (event.key == "S") { + var checked = document.getElementsByName("showCounterexample")[0].checked; + filterCounterexample(!checked); + document.getElementsByName("showCounterexample")[0].checked = !checked; + } else { + return; + } + event.preventDefault(); +}, true); + +document.addEventListener("DOMContentLoaded", function() { + document.querySelector('input[name="showCounterexample"]').onchange= + function (event) { + filterCounterexample(this.checked); + }; +}); +</script> + +<form> + <input type="checkbox" name="showCounterexample" id="showCounterexample" /> + <label for="showCounterexample"> + Show only relevant lines + </label> +</form> +)<<<"; + + return os.str(); +} + void HTMLDiagnostics::FinalizeHTML(const PathDiagnostic& D, Rewriter &R, const SourceManager& SMgr, const PathPieces& path, FileID FID, const FileEntry *Entry, const char *declName) { @@ -340,9 +457,15 @@ void HTMLDiagnostics::FinalizeHTML(const PathDiagnostic& D, Rewriter &R, int LineNumber = path.back()->getLocation().asLocation().getExpansionLineNumber(); int ColumnNumber = path.back()->getLocation().asLocation().getExpansionColumnNumber(); + R.InsertTextBefore(SMgr.getLocForStartOfFile(FID), showHelpJavascript()); + R.InsertTextBefore(SMgr.getLocForStartOfFile(FID), generateKeyboardNavigationJavascript()); + // Checkbox and javascript for filtering the output to the counterexample. + R.InsertTextBefore(SMgr.getLocForStartOfFile(FID), + showRelevantLinesJavascript(D, path)); + // Add the name of the file as an <h1> tag. { std::string s; @@ -379,8 +502,8 @@ void HTMLDiagnostics::FinalizeHTML(const PathDiagnostic& D, Rewriter &R, // Output any other meta data. - for (PathDiagnostic::meta_iterator I=D.meta_begin(), E=D.meta_end(); - I!=E; ++I) { + for (PathDiagnostic::meta_iterator I = D.meta_begin(), E = D.meta_end(); + I != E; ++I) { os << "<tr><td></td><td>" << html::EscapeText(*I) << "</td></tr>\n"; } @@ -388,11 +511,24 @@ void HTMLDiagnostics::FinalizeHTML(const PathDiagnostic& D, Rewriter &R, </table> <!-- REPORTSUMMARYEXTRA --> <h3>Annotated Source Code</h3> -<p><span class='macro'>[?] - <span class='expansion'>Use j/k keys for keyboard navigation</span> -</span></p> +<p>Press <a href="#" onclick="toggleHelp(); return false;">'?'</a> + to see keyboard shortcuts</p> +<input type="checkbox" class="spoilerhider" id="showinvocation" /> +<label for="showinvocation" >Show analyzer invocation</label> +<div class="spoiler">clang -cc1 )<<<"; + os << html::EscapeText(AnalyzerOpts.FullCompilerInvocation); + os << R"<<<( +</div> +<div id='tooltiphint' hidden="true"> + <p>Keyboard shortcuts: </p> + <ul> + <li>Use 'j/k' keys for keyboard navigation</li> + <li>Use 'Shift+S' to show/hide relevant lines</li> + <li>Use '?' to toggle this window</li> + </ul> + <a href="#" onclick="toggleHelp(); return false;">Close</a> +</div> )<<<"; - R.InsertTextBefore(SMgr.getLocForStartOfFile(FID), os.str()); } @@ -450,6 +586,34 @@ void HTMLDiagnostics::FinalizeHTML(const PathDiagnostic& D, Rewriter &R, html::AddHeaderFooterInternalBuiltinCSS(R, FID, Entry->getName()); } +std::string HTMLDiagnostics::showHelpJavascript() { + return R"<<<( +<script type='text/javascript'> + +var toggleHelp = function() { + var hint = document.querySelector("#tooltiphint"); + var attributeName = "hidden"; + if (hint.hasAttribute(attributeName)) { + hint.removeAttribute(attributeName); + } else { + hint.setAttribute("hidden", "true"); + } +}; +window.addEventListener("keydown", function (event) { + if (event.defaultPrevented) { + return; + } + if (event.key == "?") { + toggleHelp(); + } else { + return; + } + event.preventDefault(); +}); +</script> +)<<<"; +} + void HTMLDiagnostics::RewriteFile(Rewriter &R, const SourceManager& SMgr, const PathPieces& path, FileID FID) { // Process the path. @@ -494,7 +658,6 @@ void HTMLDiagnostics::RewriteFile(Rewriter &R, const SourceManager& SMgr, void HTMLDiagnostics::HandlePiece(Rewriter& R, FileID BugFileID, const PathDiagnosticPiece& P, unsigned num, unsigned max) { - // For now, just draw a box above the line in question, and emit the // warning. FullSourceLoc Pos = P.getLocation().asLocation(); @@ -634,9 +797,7 @@ void HTMLDiagnostics::HandlePiece(Rewriter& R, FileID BugFileID, os << "</td><td>"; } - if (const PathDiagnosticMacroPiece *MP = - dyn_cast<PathDiagnosticMacroPiece>(&P)) { - + if (const auto *MP = dyn_cast<PathDiagnosticMacroPiece>(&P)) { os << "Within the expansion of the macro '"; // Get the name of the macro by relexing it. @@ -707,10 +868,8 @@ void HTMLDiagnostics::HandlePiece(Rewriter& R, FileID BugFileID, // Now highlight the ranges. ArrayRef<SourceRange> Ranges = P.getRanges(); - for (ArrayRef<SourceRange>::iterator I = Ranges.begin(), - E = Ranges.end(); I != E; ++I) { - HighlightRange(R, LPosInfo.first, *I); - } + for (const auto &Range : Ranges) + HighlightRange(R, LPosInfo.first, Range); } static void EmitAlphaCounter(raw_ostream &os, unsigned n) { @@ -726,18 +885,13 @@ static void EmitAlphaCounter(raw_ostream &os, unsigned n) { unsigned HTMLDiagnostics::ProcessMacroPiece(raw_ostream &os, const PathDiagnosticMacroPiece& P, unsigned num) { - - for (PathPieces::const_iterator I = P.subPieces.begin(), E=P.subPieces.end(); - I!=E; ++I) { - - if (const PathDiagnosticMacroPiece *MP = - dyn_cast<PathDiagnosticMacroPiece>(I->get())) { + for (const auto &subPiece : P.subPieces) { + if (const auto *MP = dyn_cast<PathDiagnosticMacroPiece>(subPiece.get())) { num = ProcessMacroPiece(os, *MP, num); continue; } - if (PathDiagnosticEventPiece *EP = - dyn_cast<PathDiagnosticEventPiece>(I->get())) { + if (const auto *EP = dyn_cast<PathDiagnosticEventPiece>(subPiece.get())) { os << "<div class=\"msg msgEvent\" style=\"width:94%; " "margin-left:5px\">" "<table class=\"msgT\"><tr>" @@ -862,7 +1016,7 @@ window.addEventListener("keydown", function (event) { navigateTo(/*up=*/true); } else { return; - } + } event.preventDefault(); }, true); </script> diff --git a/lib/StaticAnalyzer/Core/LoopUnrolling.cpp b/lib/StaticAnalyzer/Core/LoopUnrolling.cpp index a8c4b05cea13..da4574c61515 100644 --- a/lib/StaticAnalyzer/Core/LoopUnrolling.cpp +++ b/lib/StaticAnalyzer/Core/LoopUnrolling.cpp @@ -97,9 +97,7 @@ changeIntBoundNode(internal::Matcher<Decl> VarNodeMatcher) { unaryOperator(anyOf(hasOperatorName("--"), hasOperatorName("++")), hasUnaryOperand(ignoringParenImpCasts( declRefExpr(to(varDecl(VarNodeMatcher)))))), - binaryOperator(anyOf(hasOperatorName("="), hasOperatorName("+="), - hasOperatorName("/="), hasOperatorName("*="), - hasOperatorName("-=")), + binaryOperator(isAssignmentOperator(), hasLHS(ignoringParenImpCasts( declRefExpr(to(varDecl(VarNodeMatcher))))))); } @@ -143,13 +141,15 @@ static internal::Matcher<Stmt> forLoopMatcher() { return forStmt( hasCondition(simpleCondition("initVarName")), // Initialization should match the form: 'int i = 6' or 'i = 42'. - hasLoopInit(anyOf( - declStmt(hasSingleDecl(varDecl( - allOf(hasInitializer(integerLiteral().bind("initNum")), - equalsBoundNode("initVarName"))))), - binaryOperator(hasLHS(declRefExpr(to( - varDecl(equalsBoundNode("initVarName"))))), - hasRHS(integerLiteral().bind("initNum"))))), + hasLoopInit( + anyOf(declStmt(hasSingleDecl( + varDecl(allOf(hasInitializer(ignoringParenImpCasts( + integerLiteral().bind("initNum"))), + equalsBoundNode("initVarName"))))), + binaryOperator(hasLHS(declRefExpr(to(varDecl( + equalsBoundNode("initVarName"))))), + hasRHS(ignoringParenImpCasts( + integerLiteral().bind("initNum")))))), // Incrementation should be a simple increment or decrement // operator call. hasIncrement(unaryOperator( diff --git a/lib/StaticAnalyzer/Core/LoopWidening.cpp b/lib/StaticAnalyzer/Core/LoopWidening.cpp index 05865c294cb7..9192f49eac6d 100644 --- a/lib/StaticAnalyzer/Core/LoopWidening.cpp +++ b/lib/StaticAnalyzer/Core/LoopWidening.cpp @@ -14,10 +14,16 @@ /// //===----------------------------------------------------------------------===// +#include "clang/AST/AST.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h" #include "clang/StaticAnalyzer/Core/PathSensitive/LoopWidening.h" using namespace clang; using namespace ento; +using namespace clang::ast_matchers; + +const auto MatchRef = "matchref"; /// Return the loops condition Stmt or NULL if LoopStmt is not a loop static const Expr *getLoopCondition(const Stmt *LoopStmt) { @@ -49,7 +55,8 @@ ProgramStateRef getWidenedLoopState(ProgramStateRef PrevState, // TODO Nested loops are currently widened as a result of the invalidation // being so inprecise. When the invalidation is improved, the handling // of nested loops will also need to be improved. - const StackFrameContext *STC = LCtx->getCurrentStackFrame(); + ASTContext &ASTCtx = LCtx->getAnalysisDeclContext()->getASTContext(); + const StackFrameContext *STC = LCtx->getStackFrame(); MemRegionManager &MRMgr = PrevState->getStateManager().getRegionManager(); const MemRegion *Regions[] = {MRMgr.getStackLocalsRegion(STC), MRMgr.getStackArgumentsRegion(STC), @@ -59,6 +66,30 @@ ProgramStateRef getWidenedLoopState(ProgramStateRef PrevState, ITraits.setTrait(Region, RegionAndSymbolInvalidationTraits::TK_EntireMemSpace); } + + // References should not be invalidated. + auto Matches = match(findAll(stmt(hasDescendant(varDecl(hasType(referenceType())).bind(MatchRef)))), + *LCtx->getDecl()->getBody(), ASTCtx); + for (BoundNodes Match : Matches) { + const VarDecl *VD = Match.getNodeAs<VarDecl>(MatchRef); + assert(VD); + const VarRegion *VarMem = MRMgr.getVarRegion(VD, LCtx); + ITraits.setTrait(VarMem, + RegionAndSymbolInvalidationTraits::TK_PreserveContents); + } + + + // 'this' pointer is not an lvalue, we should not invalidate it. If the loop + // is located in a method, constructor or destructor, the value of 'this' + // pointer shoule remain unchanged. + if (const CXXMethodDecl *CXXMD = dyn_cast<CXXMethodDecl>(STC->getDecl())) { + const CXXThisRegion *ThisR = MRMgr.getCXXThisRegion( + CXXMD->getThisType(STC->getAnalysisDeclContext()->getASTContext()), + STC); + ITraits.setTrait(ThisR, + RegionAndSymbolInvalidationTraits::TK_PreserveContents); + } + return PrevState->invalidateRegions(Regions, getLoopCondition(LoopStmt), BlockCount, LCtx, true, nullptr, nullptr, &ITraits); diff --git a/lib/StaticAnalyzer/Core/MemRegion.cpp b/lib/StaticAnalyzer/Core/MemRegion.cpp index cb8ba6de3626..cb2122c7749e 100644 --- a/lib/StaticAnalyzer/Core/MemRegion.cpp +++ b/lib/StaticAnalyzer/Core/MemRegion.cpp @@ -1,4 +1,4 @@ -//== MemRegion.cpp - Abstract memory regions for static analysis --*- C++ -*--// +//===- MemRegion.cpp - Abstract memory regions for static analysis --------===// // // The LLVM Compiler Infrastructure // @@ -14,19 +14,51 @@ //===----------------------------------------------------------------------===// #include "clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h" +#include "clang/AST/ASTContext.h" #include "clang/AST/Attr.h" #include "clang/AST/CharUnits.h" +#include "clang/AST/Decl.h" +#include "clang/AST/DeclCXX.h" #include "clang/AST/DeclObjC.h" +#include "clang/AST/Expr.h" +#include "clang/AST/PrettyPrinter.h" #include "clang/AST/RecordLayout.h" +#include "clang/AST/Type.h" #include "clang/Analysis/AnalysisDeclContext.h" #include "clang/Analysis/Support/BumpVector.h" +#include "clang/Basic/IdentifierTable.h" +#include "clang/Basic/LLVM.h" #include "clang/Basic/SourceManager.h" #include "clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h" +#include "llvm/ADT/APInt.h" +#include "llvm/ADT/FoldingSet.h" +#include "llvm/ADT/Optional.h" +#include "llvm/ADT/PointerUnion.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/ADT/Twine.h" +#include "llvm/Support/Allocator.h" +#include "llvm/Support/Casting.h" +#include "llvm/Support/CheckedArithmetic.h" +#include "llvm/Support/Compiler.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/ErrorHandling.h" #include "llvm/Support/raw_ostream.h" +#include <cassert> +#include <cstdint> +#include <functional> +#include <iterator> +#include <string> +#include <tuple> +#include <utility> using namespace clang; using namespace ento; +#define DEBUG_TYPE "MemRegion" + //===----------------------------------------------------------------------===// // MemRegion Construction. //===----------------------------------------------------------------------===// @@ -37,8 +69,7 @@ RegionTy* MemRegionManager::getSubRegion(const Arg1Ty arg1, llvm::FoldingSetNodeID ID; RegionTy::ProfileRegion(ID, arg1, superRegion); void *InsertPos; - RegionTy* R = cast_or_null<RegionTy>(Regions.FindNodeOrInsertPos(ID, - InsertPos)); + auto *R = cast_or_null<RegionTy>(Regions.FindNodeOrInsertPos(ID, InsertPos)); if (!R) { R = A.Allocate<RegionTy>(); @@ -55,8 +86,7 @@ RegionTy* MemRegionManager::getSubRegion(const Arg1Ty arg1, const Arg2Ty arg2, llvm::FoldingSetNodeID ID; RegionTy::ProfileRegion(ID, arg1, arg2, superRegion); void *InsertPos; - RegionTy* R = cast_or_null<RegionTy>(Regions.FindNodeOrInsertPos(ID, - InsertPos)); + auto *R = cast_or_null<RegionTy>(Regions.FindNodeOrInsertPos(ID, InsertPos)); if (!R) { R = A.Allocate<RegionTy>(); @@ -75,8 +105,7 @@ RegionTy* MemRegionManager::getSubRegion(const Arg1Ty arg1, const Arg2Ty arg2, llvm::FoldingSetNodeID ID; RegionTy::ProfileRegion(ID, arg1, arg2, arg3, superRegion); void *InsertPos; - RegionTy* R = cast_or_null<RegionTy>(Regions.FindNodeOrInsertPos(ID, - InsertPos)); + auto *R = cast_or_null<RegionTy>(Regions.FindNodeOrInsertPos(ID, InsertPos)); if (!R) { R = A.Allocate<RegionTy>(); @@ -91,27 +120,26 @@ RegionTy* MemRegionManager::getSubRegion(const Arg1Ty arg1, const Arg2Ty arg2, // Object destruction. //===----------------------------------------------------------------------===// -MemRegion::~MemRegion() {} +MemRegion::~MemRegion() = default; -MemRegionManager::~MemRegionManager() { - // All regions and their data are BumpPtrAllocated. No need to call - // their destructors. -} +// All regions and their data are BumpPtrAllocated. No need to call their +// destructors. +MemRegionManager::~MemRegionManager() = default; //===----------------------------------------------------------------------===// // Basic methods. //===----------------------------------------------------------------------===// bool SubRegion::isSubRegionOf(const MemRegion* R) const { - const MemRegion* r = getSuperRegion(); - while (r != nullptr) { + const MemRegion* r = this; + do { if (r == R) return true; - if (const SubRegion* sr = dyn_cast<SubRegion>(r)) + if (const auto *sr = dyn_cast<SubRegion>(r)) r = sr->getSuperRegion(); else break; - } + } while (r != nullptr); return false; } @@ -119,16 +147,16 @@ MemRegionManager* SubRegion::getMemRegionManager() const { const SubRegion* r = this; do { const MemRegion *superRegion = r->getSuperRegion(); - if (const SubRegion *sr = dyn_cast<SubRegion>(superRegion)) { + if (const auto *sr = dyn_cast<SubRegion>(superRegion)) { r = sr; continue; } return superRegion->getMemRegionManager(); - } while (1); + } while (true); } const StackFrameContext *VarRegion::getStackFrame() const { - const StackSpaceRegion *SSR = dyn_cast<StackSpaceRegion>(getMemorySpace()); + const auto *SSR = dyn_cast<StackSpaceRegion>(getMemorySpace()); return SSR ? SSR->getStackFrame() : nullptr; } @@ -215,17 +243,17 @@ void StaticGlobalSpaceRegion::Profile(llvm::FoldingSetNodeID &ID) const { ID.AddPointer(getCodeRegion()); } -void StringRegion::ProfileRegion(llvm::FoldingSetNodeID& ID, - const StringLiteral* Str, - const MemRegion* superRegion) { +void StringRegion::ProfileRegion(llvm::FoldingSetNodeID &ID, + const StringLiteral *Str, + const MemRegion *superRegion) { ID.AddInteger(static_cast<unsigned>(StringRegionKind)); ID.AddPointer(Str); ID.AddPointer(superRegion); } -void ObjCStringRegion::ProfileRegion(llvm::FoldingSetNodeID& ID, - const ObjCStringLiteral* Str, - const MemRegion* superRegion) { +void ObjCStringRegion::ProfileRegion(llvm::FoldingSetNodeID &ID, + const ObjCStringLiteral *Str, + const MemRegion *superRegion) { ID.AddInteger(static_cast<unsigned>(ObjCStringRegionKind)); ID.AddPointer(Str); ID.AddPointer(superRegion); @@ -380,13 +408,19 @@ void CXXBaseObjectRegion::Profile(llvm::FoldingSetNodeID &ID) const { // Region anchors. //===----------------------------------------------------------------------===// -void GlobalsSpaceRegion::anchor() { } -void NonStaticGlobalSpaceRegion::anchor() { } -void StackSpaceRegion::anchor() { } -void TypedRegion::anchor() { } -void TypedValueRegion::anchor() { } -void CodeTextRegion::anchor() { } -void SubRegion::anchor() { } +void GlobalsSpaceRegion::anchor() {} + +void NonStaticGlobalSpaceRegion::anchor() {} + +void StackSpaceRegion::anchor() {} + +void TypedRegion::anchor() {} + +void TypedValueRegion::anchor() {} + +void CodeTextRegion::anchor() {} + +void SubRegion::anchor() {} //===----------------------------------------------------------------------===// // Region pretty-printing. @@ -408,7 +442,7 @@ void MemRegion::dumpToStream(raw_ostream &os) const { } void AllocaRegion::dumpToStream(raw_ostream &os) const { - os << "alloca{" << static_cast<const void*>(Ex) << ',' << Cnt << '}'; + os << "alloca{" << static_cast<const void *>(Ex) << ',' << Cnt << '}'; } void FunctionCodeRegion::dumpToStream(raw_ostream &os) const { @@ -416,7 +450,7 @@ void FunctionCodeRegion::dumpToStream(raw_ostream &os) const { } void BlockCodeRegion::dumpToStream(raw_ostream &os) const { - os << "block_code{" << static_cast<const void*>(this) << '}'; + os << "block_code{" << static_cast<const void *>(this) << '}'; } void BlockDataRegion::dumpToStream(raw_ostream &os) const { @@ -425,19 +459,19 @@ void BlockDataRegion::dumpToStream(raw_ostream &os) const { for (BlockDataRegion::referenced_vars_iterator I = referenced_vars_begin(), E = referenced_vars_end(); I != E; ++I) - os << "(" << I.getCapturedRegion() << "," << + os << "(" << I.getCapturedRegion() << "<-" << I.getOriginalRegion() << ") "; os << '}'; } void CompoundLiteralRegion::dumpToStream(raw_ostream &os) const { // FIXME: More elaborate pretty-printing. - os << "{ " << static_cast<const void*>(CL) << " }"; + os << "{ " << static_cast<const void *>(CL) << " }"; } void CXXTempObjectRegion::dumpToStream(raw_ostream &os) const { os << "temp_object{" << getValueType().getAsString() << ',' - << static_cast<const void*>(Ex) << '}'; + << static_cast<const void *>(Ex) << '}'; } void CXXBaseObjectRegion::dumpToStream(raw_ostream &os) const { @@ -478,7 +512,11 @@ void SymbolicRegion::dumpToStream(raw_ostream &os) const { } void VarRegion::dumpToStream(raw_ostream &os) const { - os << *cast<VarDecl>(D); + const auto *VD = cast<VarDecl>(D); + if (const IdentifierInfo *ID = VD->getIdentifier()) + os << ID->getName(); + else + os << "VarRegion{" << static_cast<const void *>(this) << '}'; } LLVM_DUMP_METHOD void RegionRawOffset::dump() const { @@ -622,19 +660,18 @@ std::string MemRegion::getDescriptiveName(bool UseQuotes) const { // Get variable name. if (R && R->canPrintPrettyAsExpr()) { R->printPrettyAsExpr(os); - if (UseQuotes) { + if (UseQuotes) return (llvm::Twine("'") + os.str() + ArrayIndices + "'").str(); - } else { + else return (llvm::Twine(os.str()) + ArrayIndices).str(); - } } return VariableName; } SourceRange MemRegion::sourceRange() const { - const VarRegion *const VR = dyn_cast<VarRegion>(this->getBaseRegion()); - const FieldRegion *const FR = dyn_cast<FieldRegion>(this); + const auto *const VR = dyn_cast<VarRegion>(this->getBaseRegion()); + const auto *const FR = dyn_cast<FieldRegion>(this); // Check for more specific regions first. // FieldRegion @@ -646,9 +683,8 @@ SourceRange MemRegion::sourceRange() const { return VR->getDecl()->getSourceRange(); } // Return invalid source range (can be checked by client). - else { - return SourceRange{}; - } + else + return {}; } //===----------------------------------------------------------------------===// @@ -738,13 +774,14 @@ const CodeSpaceRegion *MemRegionManager::getCodeRegion() { //===----------------------------------------------------------------------===// // Constructing regions. //===----------------------------------------------------------------------===// -const StringRegion* MemRegionManager::getStringRegion(const StringLiteral* Str){ + +const StringRegion *MemRegionManager::getStringRegion(const StringLiteral *Str){ return getSubRegion<StringRegion>( Str, cast<GlobalInternalSpaceRegion>(getGlobalsRegion())); } const ObjCStringRegion * -MemRegionManager::getObjCStringRegion(const ObjCStringLiteral* Str){ +MemRegionManager::getObjCStringRegion(const ObjCStringLiteral *Str){ return getSubRegion<ObjCStringRegion>( Str, cast<GlobalInternalSpaceRegion>(getGlobalsRegion())); } @@ -757,21 +794,20 @@ getStackOrCaptureRegionForDeclContext(const LocationContext *LC, const DeclContext *DC, const VarDecl *VD) { while (LC) { - if (const StackFrameContext *SFC = dyn_cast<StackFrameContext>(LC)) { + if (const auto *SFC = dyn_cast<StackFrameContext>(LC)) { if (cast<DeclContext>(SFC->getDecl()) == DC) return SFC; } - if (const BlockInvocationContext *BC = - dyn_cast<BlockInvocationContext>(LC)) { - const BlockDataRegion *BR = - static_cast<const BlockDataRegion*>(BC->getContextData()); + if (const auto *BC = dyn_cast<BlockInvocationContext>(LC)) { + const auto *BR = + static_cast<const BlockDataRegion *>(BC->getContextData()); // FIXME: This can be made more efficient. for (BlockDataRegion::referenced_vars_iterator I = BR->referenced_vars_begin(), E = BR->referenced_vars_end(); I != E; ++I) { - if (const VarRegion *VR = dyn_cast<VarRegion>(I.getOriginalRegion())) - if (VR->getDecl() == VD) - return cast<VarRegion>(I.getCapturedRegion()); + const VarRegion *VR = I.getOriginalRegion(); + if (VR->getDecl() == VD) + return cast<VarRegion>(I.getCapturedRegion()); } } @@ -818,7 +854,7 @@ const VarRegion* MemRegionManager::getVarRegion(const VarDecl *D, if (V.is<const VarRegion*>()) return V.get<const VarRegion*>(); - const StackFrameContext *STC = V.get<const StackFrameContext*>(); + const auto *STC = V.get<const StackFrameContext *>(); if (!STC) { // FIXME: Assign a more sensible memory space to static locals @@ -836,7 +872,7 @@ const VarRegion* MemRegionManager::getVarRegion(const VarDecl *D, if (isa<FunctionDecl>(STCD) || isa<ObjCMethodDecl>(STCD)) sReg = getGlobalsRegion(MemRegion::StaticGlobalSpaceRegionKind, getFunctionCodeRegion(cast<NamedDecl>(STCD))); - else if (const BlockDecl *BD = dyn_cast<BlockDecl>(STCD)) { + else if (const auto *BD = dyn_cast<BlockDecl>(STCD)) { // FIXME: The fallback type here is totally bogus -- though it should // never be queried, it will prevent uniquing with the real // BlockCodeRegion. Ideally we'd fix the AST so that we always had a @@ -885,7 +921,7 @@ MemRegionManager::getBlockDataRegion(const BlockCodeRegion *BC, if (LC) { // FIXME: Once we implement scope handling, we want the parent region // to be the scope. - const StackFrameContext *STC = LC->getCurrentStackFrame(); + const StackFrameContext *STC = LC->getStackFrame(); assert(STC); sReg = getStackLocalsRegion(STC); } @@ -913,7 +949,7 @@ MemRegionManager::getCompoundLiteralRegion(const CompoundLiteralExpr *CL, if (CL->isFileScope()) sReg = getGlobalsRegion(); else { - const StackFrameContext *STC = LC->getCurrentStackFrame(); + const StackFrameContext *STC = LC->getStackFrame(); assert(STC); sReg = getStackLocalsRegion(STC); } @@ -932,7 +968,7 @@ MemRegionManager::getElementRegion(QualType elementType, NonLoc Idx, void *InsertPos; MemRegion* data = Regions.FindNodeOrInsertPos(ID, InsertPos); - ElementRegion* R = cast_or_null<ElementRegion>(data); + auto *R = cast_or_null<ElementRegion>(data); if (!R) { R = A.Allocate<ElementRegion>(); @@ -954,7 +990,6 @@ MemRegionManager::getBlockCodeRegion(const BlockDecl *BD, CanQualType locTy, return getSubRegion<BlockCodeRegion>(BD, locTy, AC, getCodeRegion()); } - /// getSymbolicRegion - Retrieve or create a "symbolic" memory region. const SymbolicRegion *MemRegionManager::getSymbolicRegion(SymbolRef sym) { return getSubRegion<SymbolicRegion>(sym, getUnknownRegion()); @@ -979,7 +1014,7 @@ MemRegionManager::getObjCIvarRegion(const ObjCIvarDecl *d, const CXXTempObjectRegion* MemRegionManager::getCXXTempObjectRegion(Expr const *E, LocationContext const *LC) { - const StackFrameContext *SFC = LC->getCurrentStackFrame(); + const StackFrameContext *SFC = LC->getStackFrame(); assert(SFC); return getSubRegion<CXXTempObjectRegion>(E, getStackLocalsRegion(SFC)); } @@ -1017,10 +1052,8 @@ MemRegionManager::getCXXBaseObjectRegion(const CXXRecordDecl *RD, if (IsVirtual) { // Virtual base regions should not be layered, since the layout rules // are different. - while (const CXXBaseObjectRegion *Base = - dyn_cast<CXXBaseObjectRegion>(Super)) { + while (const auto *Base = dyn_cast<CXXBaseObjectRegion>(Super)) Super = cast<SubRegion>(Base->getSuperRegion()); - } assert(Super && !isa<MemSpaceRegion>(Super)); } } @@ -1031,7 +1064,7 @@ MemRegionManager::getCXXBaseObjectRegion(const CXXRecordDecl *RD, const CXXThisRegion* MemRegionManager::getCXXThisRegion(QualType thisPointerTy, const LocationContext *LC) { - const PointerType *PT = thisPointerTy->getAs<PointerType>(); + const auto *PT = thisPointerTy->getAs<PointerType>(); assert(PT); // Inside the body of the operator() of a lambda a this expr might refer to an // object in one of the parent location contexts. @@ -1045,7 +1078,7 @@ MemRegionManager::getCXXThisRegion(QualType thisPointerTy, LC = LC->getParent(); D = dyn_cast<CXXMethodDecl>(LC->getDecl()); } - const StackFrameContext *STC = LC->getCurrentStackFrame(); + const StackFrameContext *STC = LC->getStackFrame(); assert(STC); return getSubRegion<CXXThisRegion>(PT, getStackArgumentsRegion(STC)); } @@ -1053,14 +1086,14 @@ MemRegionManager::getCXXThisRegion(QualType thisPointerTy, const AllocaRegion* MemRegionManager::getAllocaRegion(const Expr *E, unsigned cnt, const LocationContext *LC) { - const StackFrameContext *STC = LC->getCurrentStackFrame(); + const StackFrameContext *STC = LC->getStackFrame(); assert(STC); return getSubRegion<AllocaRegion>(E, cnt, getStackLocalsRegion(STC)); } const MemSpaceRegion *MemRegion::getMemorySpace() const { const MemRegion *R = this; - const SubRegion* SR = dyn_cast<SubRegion>(this); + const auto *SR = dyn_cast<SubRegion>(this); while (SR) { R = SR->getSuperRegion(); @@ -1121,7 +1154,7 @@ const MemRegion *MemRegion::StripCasts(bool StripBaseCasts) const { while (true) { switch (R->getKind()) { case ElementRegionKind: { - const ElementRegion *ER = cast<ElementRegion>(R); + const auto *ER = cast<ElementRegion>(R); if (!ER->getIndex().isZeroConstant()) return R; R = ER->getSuperRegion(); @@ -1139,10 +1172,10 @@ const MemRegion *MemRegion::StripCasts(bool StripBaseCasts) const { } const SymbolicRegion *MemRegion::getSymbolicBase() const { - const SubRegion *SubR = dyn_cast<SubRegion>(this); + const auto *SubR = dyn_cast<SubRegion>(this); while (SubR) { - if (const SymbolicRegion *SymR = dyn_cast<SymbolicRegion>(SubR)) + if (const auto *SymR = dyn_cast<SymbolicRegion>(SubR)) return SymR; SubR = dyn_cast<SubRegion>(SubR->getSuperRegion()); } @@ -1150,7 +1183,7 @@ const SymbolicRegion *MemRegion::getSymbolicBase() const { } RegionRawOffset ElementRegion::getAsArrayOffset() const { - CharUnits offset = CharUnits::Zero(); + int64_t offset = 0; const ElementRegion *ER = this; const MemRegion *superR = nullptr; ASTContext &C = getContext(); @@ -1162,7 +1195,7 @@ RegionRawOffset ElementRegion::getAsArrayOffset() const { // FIXME: generalize to symbolic offsets. SVal index = ER->getIndex(); - if (Optional<nonloc::ConcreteInt> CI = index.getAs<nonloc::ConcreteInt>()) { + if (auto CI = index.getAs<nonloc::ConcreteInt>()) { // Update the offset. int64_t i = CI->getValue().getSExtValue(); @@ -1175,8 +1208,15 @@ RegionRawOffset ElementRegion::getAsArrayOffset() const { break; } - CharUnits size = C.getTypeSizeInChars(elemType); - offset += (i * size); + int64_t size = C.getTypeSizeInChars(elemType).getQuantity(); + if (auto NewOffset = llvm::checkedMulAdd(i, size, offset)) { + offset = *NewOffset; + } else { + LLVM_DEBUG(llvm::dbgs() << "MemRegion::getAsArrayOffset: " + << "offset overflowing, returning unknown\n"); + + return nullptr; + } } // Go to the next ElementRegion (if any). @@ -1188,10 +1228,9 @@ RegionRawOffset ElementRegion::getAsArrayOffset() const { } assert(superR && "super region cannot be NULL"); - return RegionRawOffset(superR, offset); + return RegionRawOffset(superR, CharUnits::fromQuantity(offset)); } - /// Returns true if \p Base is an immediate base class of \p Child static bool isImmediateBase(const CXXRecordDecl *Child, const CXXRecordDecl *Base) { @@ -1207,47 +1246,46 @@ static bool isImmediateBase(const CXXRecordDecl *Child, return false; } -RegionOffset MemRegion::getAsOffset() const { - const MemRegion *R = this; +static RegionOffset calculateOffset(const MemRegion *R) { const MemRegion *SymbolicOffsetBase = nullptr; int64_t Offset = 0; - while (1) { + while (true) { switch (R->getKind()) { - case CodeSpaceRegionKind: - case StackLocalsSpaceRegionKind: - case StackArgumentsSpaceRegionKind: - case HeapSpaceRegionKind: - case UnknownSpaceRegionKind: - case StaticGlobalSpaceRegionKind: - case GlobalInternalSpaceRegionKind: - case GlobalSystemSpaceRegionKind: - case GlobalImmutableSpaceRegionKind: + case MemRegion::CodeSpaceRegionKind: + case MemRegion::StackLocalsSpaceRegionKind: + case MemRegion::StackArgumentsSpaceRegionKind: + case MemRegion::HeapSpaceRegionKind: + case MemRegion::UnknownSpaceRegionKind: + case MemRegion::StaticGlobalSpaceRegionKind: + case MemRegion::GlobalInternalSpaceRegionKind: + case MemRegion::GlobalSystemSpaceRegionKind: + case MemRegion::GlobalImmutableSpaceRegionKind: // Stores can bind directly to a region space to set a default value. assert(Offset == 0 && !SymbolicOffsetBase); goto Finish; - case FunctionCodeRegionKind: - case BlockCodeRegionKind: - case BlockDataRegionKind: + case MemRegion::FunctionCodeRegionKind: + case MemRegion::BlockCodeRegionKind: + case MemRegion::BlockDataRegionKind: // These will never have bindings, but may end up having values requested // if the user does some strange casting. if (Offset != 0) SymbolicOffsetBase = R; goto Finish; - case SymbolicRegionKind: - case AllocaRegionKind: - case CompoundLiteralRegionKind: - case CXXThisRegionKind: - case StringRegionKind: - case ObjCStringRegionKind: - case VarRegionKind: - case CXXTempObjectRegionKind: + case MemRegion::SymbolicRegionKind: + case MemRegion::AllocaRegionKind: + case MemRegion::CompoundLiteralRegionKind: + case MemRegion::CXXThisRegionKind: + case MemRegion::StringRegionKind: + case MemRegion::ObjCStringRegionKind: + case MemRegion::VarRegionKind: + case MemRegion::CXXTempObjectRegionKind: // Usual base regions. goto Finish; - case ObjCIvarRegionKind: + case MemRegion::ObjCIvarRegionKind: // This is a little strange, but it's a compromise between // ObjCIvarRegions having unknown compile-time offsets (when using the // non-fragile runtime) and yet still being distinct, non-overlapping @@ -1255,15 +1293,15 @@ RegionOffset MemRegion::getAsOffset() const { // of computing offsets. goto Finish; - case CXXBaseObjectRegionKind: { - const CXXBaseObjectRegion *BOR = cast<CXXBaseObjectRegion>(R); + case MemRegion::CXXBaseObjectRegionKind: { + const auto *BOR = cast<CXXBaseObjectRegion>(R); R = BOR->getSuperRegion(); QualType Ty; bool RootIsSymbolic = false; - if (const TypedValueRegion *TVR = dyn_cast<TypedValueRegion>(R)) { - Ty = TVR->getDesugaredValueType(getContext()); - } else if (const SymbolicRegion *SR = dyn_cast<SymbolicRegion>(R)) { + if (const auto *TVR = dyn_cast<TypedValueRegion>(R)) { + Ty = TVR->getDesugaredValueType(R->getContext()); + } else if (const auto *SR = dyn_cast<SymbolicRegion>(R)) { // If our base region is symbolic, we don't know what type it really is. // Pretend the type of the symbol is the true dynamic type. // (This will at least be self-consistent for the life of the symbol.) @@ -1296,18 +1334,18 @@ RegionOffset MemRegion::getAsOffset() const { continue; CharUnits BaseOffset; - const ASTRecordLayout &Layout = getContext().getASTRecordLayout(Child); + const ASTRecordLayout &Layout = R->getContext().getASTRecordLayout(Child); if (BOR->isVirtual()) BaseOffset = Layout.getVBaseClassOffset(BOR->getDecl()); else BaseOffset = Layout.getBaseClassOffset(BOR->getDecl()); // The base offset is in chars, not in bits. - Offset += BaseOffset.getQuantity() * getContext().getCharWidth(); + Offset += BaseOffset.getQuantity() * R->getContext().getCharWidth(); break; } - case ElementRegionKind: { - const ElementRegion *ER = cast<ElementRegion>(R); + case MemRegion::ElementRegionKind: { + const auto *ER = cast<ElementRegion>(R); R = ER->getSuperRegion(); QualType EleTy = ER->getValueType(); @@ -1327,15 +1365,15 @@ RegionOffset MemRegion::getAsOffset() const { int64_t i = CI->getValue().getSExtValue(); // This type size is in bits. - Offset += i * getContext().getTypeSize(EleTy); + Offset += i * R->getContext().getTypeSize(EleTy); } else { // We cannot compute offset for non-concrete index. SymbolicOffsetBase = R; } break; } - case FieldRegionKind: { - const FieldRegion *FR = cast<FieldRegion>(R); + case MemRegion::FieldRegionKind: { + const auto *FR = cast<FieldRegion>(R); R = FR->getSuperRegion(); const RecordDecl *RD = FR->getDecl()->getParent(); @@ -1360,7 +1398,7 @@ RegionOffset MemRegion::getAsOffset() const { if (FR->getDecl() == *FI) break; } - const ASTRecordLayout &Layout = getContext().getASTRecordLayout(RD); + const ASTRecordLayout &Layout = R->getContext().getASTRecordLayout(RD); // This is offset in bits. Offset += Layout.getFieldOffset(idx); break; @@ -1374,6 +1412,12 @@ RegionOffset MemRegion::getAsOffset() const { return RegionOffset(R, Offset); } +RegionOffset MemRegion::getAsOffset() const { + if (!cachedOffset) + cachedOffset = calculateOffset(this); + return *cachedOffset; +} + //===----------------------------------------------------------------------===// // BlockDataRegion //===----------------------------------------------------------------------===// @@ -1419,13 +1463,14 @@ void BlockDataRegion::LazyInitializeReferencedVars() { llvm::BumpPtrAllocator &A = MemMgr.getAllocator(); BumpVectorContext BC(A); - typedef BumpVector<const MemRegion*> VarVec; - VarVec *BV = A.Allocate<VarVec>(); + using VarVec = BumpVector<const MemRegion *>; + + auto *BV = A.Allocate<VarVec>(); new (BV) VarVec(BC, NumBlockVars); - VarVec *BVOriginal = A.Allocate<VarVec>(); + auto *BVOriginal = A.Allocate<VarVec>(); new (BVOriginal) VarVec(BC, NumBlockVars); - for (const VarDecl *VD : ReferencedBlockVars) { + for (const auto *VD : ReferencedBlockVars) { const VarRegion *VR = nullptr; const VarRegion *OriginalVR = nullptr; std::tie(VR, OriginalVR) = getCaptureRegions(VD); @@ -1443,14 +1488,13 @@ BlockDataRegion::referenced_vars_iterator BlockDataRegion::referenced_vars_begin() const { const_cast<BlockDataRegion*>(this)->LazyInitializeReferencedVars(); - BumpVector<const MemRegion*> *Vec = - static_cast<BumpVector<const MemRegion*>*>(ReferencedVars); + auto *Vec = static_cast<BumpVector<const MemRegion *> *>(ReferencedVars); if (Vec == (void*) 0x1) return BlockDataRegion::referenced_vars_iterator(nullptr, nullptr); - BumpVector<const MemRegion*> *VecOriginal = - static_cast<BumpVector<const MemRegion*>*>(OriginalVars); + auto *VecOriginal = + static_cast<BumpVector<const MemRegion *> *>(OriginalVars); return BlockDataRegion::referenced_vars_iterator(Vec->begin(), VecOriginal->begin()); @@ -1460,14 +1504,13 @@ BlockDataRegion::referenced_vars_iterator BlockDataRegion::referenced_vars_end() const { const_cast<BlockDataRegion*>(this)->LazyInitializeReferencedVars(); - BumpVector<const MemRegion*> *Vec = - static_cast<BumpVector<const MemRegion*>*>(ReferencedVars); + auto *Vec = static_cast<BumpVector<const MemRegion *> *>(ReferencedVars); if (Vec == (void*) 0x1) return BlockDataRegion::referenced_vars_iterator(nullptr, nullptr); - BumpVector<const MemRegion*> *VecOriginal = - static_cast<BumpVector<const MemRegion*>*>(OriginalVars); + auto *VecOriginal = + static_cast<BumpVector<const MemRegion *> *>(OriginalVars); return BlockDataRegion::referenced_vars_iterator(Vec->end(), VecOriginal->end()); @@ -1495,7 +1538,7 @@ void RegionAndSymbolInvalidationTraits::setTrait(SymbolRef Sym, void RegionAndSymbolInvalidationTraits::setTrait(const MemRegion *MR, InvalidationKinds IK) { assert(MR); - if (const SymbolicRegion *SR = dyn_cast<SymbolicRegion>(MR)) + if (const auto *SR = dyn_cast<SymbolicRegion>(MR)) setTrait(SR->getSymbol(), IK); else MRTraitsMap[MR] |= IK; @@ -1515,7 +1558,7 @@ bool RegionAndSymbolInvalidationTraits::hasTrait(const MemRegion *MR, if (!MR) return false; - if (const SymbolicRegion *SR = dyn_cast<SymbolicRegion>(MR)) + if (const auto *SR = dyn_cast<SymbolicRegion>(MR)) return hasTrait(SR->getSymbol(), IK); const_region_iterator I = MRTraitsMap.find(MR); diff --git a/lib/StaticAnalyzer/Core/PathDiagnostic.cpp b/lib/StaticAnalyzer/Core/PathDiagnostic.cpp index 669748c0127a..1b698ec5c086 100644 --- a/lib/StaticAnalyzer/Core/PathDiagnostic.cpp +++ b/lib/StaticAnalyzer/Core/PathDiagnostic.cpp @@ -1,4 +1,4 @@ -//===--- PathDiagnostic.cpp - Path-Specific Diagnostic Handling -*- C++ -*-===// +//===- PathDiagnostic.cpp - Path-Specific Diagnostic Handling -------------===// // // The LLVM Compiler Infrastructure // @@ -13,26 +13,52 @@ #include "clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h" #include "clang/AST/Decl.h" +#include "clang/AST/DeclBase.h" #include "clang/AST/DeclCXX.h" #include "clang/AST/DeclObjC.h" +#include "clang/AST/DeclTemplate.h" #include "clang/AST/Expr.h" #include "clang/AST/ExprCXX.h" +#include "clang/AST/OperationKinds.h" #include "clang/AST/ParentMap.h" -#include "clang/AST/StmtCXX.h" +#include "clang/AST/Stmt.h" +#include "clang/AST/Type.h" +#include "clang/Analysis/AnalysisDeclContext.h" +#include "clang/Analysis/CFG.h" +#include "clang/Analysis/ProgramPoint.h" +#include "clang/Basic/FileManager.h" +#include "clang/Basic/LLVM.h" +#include "clang/Basic/SourceLocation.h" #include "clang/Basic/SourceManager.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/FoldingSet.h" +#include "llvm/ADT/None.h" +#include "llvm/ADT/Optional.h" +#include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SmallString.h" +#include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringExtras.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/Casting.h" +#include "llvm/Support/ErrorHandling.h" #include "llvm/Support/raw_ostream.h" +#include <cassert> +#include <cstring> +#include <memory> +#include <utility> +#include <vector> using namespace clang; using namespace ento; bool PathDiagnosticMacroPiece::containsEvent() const { - for (auto &P : subPieces) { + for (const auto &P : subPieces) { if (isa<PathDiagnosticEventPiece>(*P)) return true; - if (auto *MP = dyn_cast<PathDiagnosticMacroPiece>(P.get())) + if (const auto *MP = dyn_cast<PathDiagnosticMacroPiece>(P.get())) if (MP->containsEvent()) return true; } @@ -43,23 +69,27 @@ static StringRef StripTrailingDots(StringRef s) { for (StringRef::size_type i = s.size(); i != 0; --i) if (s[i - 1] != '.') return s.substr(0, i); - return ""; + return {}; } PathDiagnosticPiece::PathDiagnosticPiece(StringRef s, Kind k, DisplayHint hint) - : str(StripTrailingDots(s)), kind(k), Hint(hint), - LastInMainSourceFile(false) {} + : str(StripTrailingDots(s)), kind(k), Hint(hint) {} PathDiagnosticPiece::PathDiagnosticPiece(Kind k, DisplayHint hint) - : kind(k), Hint(hint), LastInMainSourceFile(false) {} + : kind(k), Hint(hint) {} -PathDiagnosticPiece::~PathDiagnosticPiece() {} -PathDiagnosticEventPiece::~PathDiagnosticEventPiece() {} -PathDiagnosticCallPiece::~PathDiagnosticCallPiece() {} -PathDiagnosticControlFlowPiece::~PathDiagnosticControlFlowPiece() {} -PathDiagnosticMacroPiece::~PathDiagnosticMacroPiece() {} -PathDiagnosticNotePiece::~PathDiagnosticNotePiece() {} +PathDiagnosticPiece::~PathDiagnosticPiece() = default; + +PathDiagnosticEventPiece::~PathDiagnosticEventPiece() = default; + +PathDiagnosticCallPiece::~PathDiagnosticCallPiece() = default; + +PathDiagnosticControlFlowPiece::~PathDiagnosticControlFlowPiece() = default; + +PathDiagnosticMacroPiece::~PathDiagnosticMacroPiece() = default; + +PathDiagnosticNotePiece::~PathDiagnosticNotePiece() = default; void PathPieces::flattenTo(PathPieces &Primary, PathPieces &Current, bool ShouldFlattenMacros) const { @@ -96,22 +126,20 @@ void PathPieces::flattenTo(PathPieces &Primary, PathPieces &Current, } } -PathDiagnostic::~PathDiagnostic() {} - -PathDiagnostic::PathDiagnostic(StringRef CheckName, const Decl *declWithIssue, - StringRef bugtype, StringRef verboseDesc, - StringRef shortDesc, StringRef category, - PathDiagnosticLocation LocationToUnique, - const Decl *DeclToUnique) - : CheckName(CheckName), - DeclWithIssue(declWithIssue), - BugType(StripTrailingDots(bugtype)), - VerboseDesc(StripTrailingDots(verboseDesc)), - ShortDesc(StripTrailingDots(shortDesc)), - Category(StripTrailingDots(category)), - UniqueingLoc(LocationToUnique), - UniqueingDecl(DeclToUnique), - path(pathImpl) {} +PathDiagnostic::~PathDiagnostic() = default; + +PathDiagnostic::PathDiagnostic( + StringRef CheckName, const Decl *declWithIssue, StringRef bugtype, + StringRef verboseDesc, StringRef shortDesc, StringRef category, + PathDiagnosticLocation LocationToUnique, const Decl *DeclToUnique, + std::unique_ptr<FilesToLineNumsMap> ExecutedLines) + : CheckName(CheckName), DeclWithIssue(declWithIssue), + BugType(StripTrailingDots(bugtype)), + VerboseDesc(StripTrailingDots(verboseDesc)), + ShortDesc(StripTrailingDots(shortDesc)), + Category(StripTrailingDots(category)), UniqueingLoc(LocationToUnique), + UniqueingDecl(DeclToUnique), ExecutedLines(std::move(ExecutedLines)), + path(pathImpl) {} static PathDiagnosticCallPiece * getFirstStackedCallToHeaderFile(PathDiagnosticCallPiece *CP, @@ -122,11 +150,11 @@ getFirstStackedCallToHeaderFile(PathDiagnosticCallPiece *CP, if (CallLoc.isMacroID()) return nullptr; - assert(SMgr.isInMainFile(CallLoc) && - "The call piece should be in the main file."); + assert(AnalysisManager::isInCodeFile(CallLoc, SMgr) && + "The call piece should not be in a header file."); // Check if CP represents a path through a function outside of the main file. - if (!SMgr.isInMainFile(CP->callEnterWithin.asLocation())) + if (!AnalysisManager::isInCodeFile(CP->callEnterWithin.asLocation(), SMgr)) return CP; const PathPieces &Path = CP->path; @@ -135,10 +163,8 @@ getFirstStackedCallToHeaderFile(PathDiagnosticCallPiece *CP, // Check if the last piece in the callee path is a call to a function outside // of the main file. - if (PathDiagnosticCallPiece *CPInner = - dyn_cast<PathDiagnosticCallPiece>(Path.back().get())) { + if (auto *CPInner = dyn_cast<PathDiagnosticCallPiece>(Path.back().get())) return getFirstStackedCallToHeaderFile(CPInner, SMgr); - } // Otherwise, the last piece is in the main file. return nullptr; @@ -154,14 +180,14 @@ void PathDiagnostic::resetDiagnosticLocationToMainFile() { // We only need to check if the report ends inside headers, if the last piece // is a call piece. - if (PathDiagnosticCallPiece *CP = dyn_cast<PathDiagnosticCallPiece>(LastP)) { + if (auto *CP = dyn_cast<PathDiagnosticCallPiece>(LastP)) { CP = getFirstStackedCallToHeaderFile(CP, SMgr); if (CP) { // Mark the piece. CP->setAsLastInMainSourceFile(); // Update the path diagnostic message. - const NamedDecl *ND = dyn_cast<NamedDecl>(CP->getCallee()); + const auto *ND = dyn_cast<NamedDecl>(CP->getCallee()); if (ND) { SmallString<200> buf; llvm::raw_svector_ostream os(buf); @@ -178,14 +204,12 @@ void PathDiagnostic::resetDiagnosticLocationToMainFile() { } } -void PathDiagnosticConsumer::anchor() { } +void PathDiagnosticConsumer::anchor() {} PathDiagnosticConsumer::~PathDiagnosticConsumer() { // Delete the contents of the FoldingSet if it isn't empty already. - for (llvm::FoldingSet<PathDiagnostic>::iterator it = - Diags.begin(), et = Diags.end() ; it != et ; ++it) { - delete &*it; - } + for (auto &Diag : Diags) + delete &Diag; } void PathDiagnosticConsumer::HandlePathDiagnostic( @@ -216,9 +240,8 @@ void PathDiagnosticConsumer::HandlePathDiagnostic( while (!WorkList.empty()) { const PathPieces &path = *WorkList.pop_back_val(); - for (PathPieces::const_iterator I = path.begin(), E = path.end(); I != E; - ++I) { - const PathDiagnosticPiece *piece = I->get(); + for (const auto &I : path) { + const PathDiagnosticPiece *piece = I.get(); FullSourceLoc L = piece->getLocation().asLocation().getExpansionLoc(); if (FID.isInvalid()) { @@ -230,28 +253,23 @@ void PathDiagnosticConsumer::HandlePathDiagnostic( // Check the source ranges. ArrayRef<SourceRange> Ranges = piece->getRanges(); - for (ArrayRef<SourceRange>::iterator I = Ranges.begin(), - E = Ranges.end(); I != E; ++I) { - SourceLocation L = SMgr.getExpansionLoc(I->getBegin()); + for (const auto &I : Ranges) { + SourceLocation L = SMgr.getExpansionLoc(I.getBegin()); if (!L.isFileID() || SMgr.getFileID(L) != FID) { llvm::errs() << warning.str(); return; } - L = SMgr.getExpansionLoc(I->getEnd()); + L = SMgr.getExpansionLoc(I.getEnd()); if (!L.isFileID() || SMgr.getFileID(L) != FID) { llvm::errs() << warning.str(); return; } } - if (const PathDiagnosticCallPiece *call = - dyn_cast<PathDiagnosticCallPiece>(piece)) { + if (const auto *call = dyn_cast<PathDiagnosticCallPiece>(piece)) WorkList.push_back(&call->path); - } - else if (const PathDiagnosticMacroPiece *macro = - dyn_cast<PathDiagnosticMacroPiece>(piece)) { + else if (const auto *macro = dyn_cast<PathDiagnosticMacroPiece>(piece)) WorkList.push_back(¯o->subPieces); - } } } @@ -381,11 +399,29 @@ static Optional<bool> comparePath(const PathPieces &X, const PathPieces &Y) { return None; } +static bool compareCrossTUSourceLocs(FullSourceLoc XL, FullSourceLoc YL) { + std::pair<FileID, unsigned> XOffs = XL.getDecomposedLoc(); + std::pair<FileID, unsigned> YOffs = YL.getDecomposedLoc(); + const SourceManager &SM = XL.getManager(); + std::pair<bool, bool> InSameTU = SM.isInTheSameTranslationUnit(XOffs, YOffs); + if (InSameTU.first) + return XL.isBeforeInTranslationUnitThan(YL); + const FileEntry *XFE = SM.getFileEntryForID(XL.getSpellingLoc().getFileID()); + const FileEntry *YFE = SM.getFileEntryForID(YL.getSpellingLoc().getFileID()); + if (!XFE || !YFE) + return XFE && !YFE; + int NameCmp = XFE->getName().compare(YFE->getName()); + if (NameCmp != 0) + return NameCmp == -1; + // Last resort: Compare raw file IDs that are possibly expansions. + return XL.getFileID() < YL.getFileID(); +} + static bool compare(const PathDiagnostic &X, const PathDiagnostic &Y) { FullSourceLoc XL = X.getLocation().asLocation(); FullSourceLoc YL = Y.getLocation().asLocation(); if (XL != YL) - return XL.isBeforeInTranslationUnitThan(YL); + return compareCrossTUSourceLocs(XL, YL); if (X.getBugType() != Y.getBugType()) return X.getBugType() < Y.getBugType(); if (X.getCategory() != Y.getCategory()) @@ -405,7 +441,8 @@ static bool compare(const PathDiagnostic &X, const PathDiagnostic &Y) { SourceLocation YDL = YD->getLocation(); if (XDL != YDL) { const SourceManager &SM = XL.getManager(); - return SM.isBeforeInTranslationUnit(XDL, YDL); + return compareCrossTUSourceLocs(FullSourceLoc(XDL, SM), + FullSourceLoc(YDL, SM)); } } PathDiagnostic::meta_iterator XI = X.meta_begin(), XE = X.meta_end(); @@ -429,11 +466,8 @@ void PathDiagnosticConsumer::FlushDiagnostics( flushed = true; std::vector<const PathDiagnostic *> BatchDiags; - for (llvm::FoldingSet<PathDiagnostic>::iterator it = Diags.begin(), - et = Diags.end(); it != et; ++it) { - const PathDiagnostic *D = &*it; - BatchDiags.push_back(D); - } + for (const auto &D : Diags) + BatchDiags.push_back(&D); // Sort the diagnostics so that they are always emitted in a deterministic // order. @@ -450,11 +484,8 @@ void PathDiagnosticConsumer::FlushDiagnostics( FlushDiagnosticsImpl(BatchDiags, Files); // Delete the flushed diagnostics. - for (std::vector<const PathDiagnostic *>::iterator it = BatchDiags.begin(), - et = BatchDiags.end(); it != et; ++it) { - const PathDiagnostic *D = *it; + for (const auto D : BatchDiags) delete D; - } // Clear out the FoldingSet. Diags.clear(); @@ -553,6 +584,8 @@ getLocationForCaller(const StackFrameContext *SFC, switch (Source.getKind()) { case CFGElement::Statement: + case CFGElement::Constructor: + case CFGElement::CXXRecordTypedCall: return PathDiagnosticLocation(Source.castAs<CFGStmt>().getStmt(), SM, CallerCtx); case CFGElement::Initializer: { @@ -576,8 +609,20 @@ getLocationForCaller(const StackFrameContext *SFC, return PathDiagnosticLocation::createEnd(CallerBody, SM, CallerCtx); return PathDiagnosticLocation::create(CallerInfo->getDecl(), SM); } - case CFGElement::TemporaryDtor: - case CFGElement::NewAllocator: + case CFGElement::NewAllocator: { + const CFGNewAllocator &Alloc = Source.castAs<CFGNewAllocator>(); + return PathDiagnosticLocation(Alloc.getAllocatorExpr(), SM, CallerCtx); + } + case CFGElement::TemporaryDtor: { + // Temporary destructors are for temporaries. They die immediately at around + // the location of CXXBindTemporaryExpr. If they are lifetime-extended, + // they'd be dealt with via an AutomaticObjectDtor instead. + const auto &Dtor = Source.castAs<CFGTemporaryDtor>(); + return PathDiagnosticLocation::createEnd(Dtor.getBindTemporaryExpr(), SM, + CallerCtx); + } + case CFGElement::ScopeBegin: + case CFGElement::ScopeEnd: llvm_unreachable("not yet implemented!"); case CFGElement::LifetimeEnds: case CFGElement::LoopExit: @@ -605,7 +650,7 @@ PathDiagnosticLocation PathDiagnosticLocation::createEnd(const Stmt *S, const SourceManager &SM, LocationOrAnalysisDeclContext LAC) { - if (const CompoundStmt *CS = dyn_cast<CompoundStmt>(S)) + if (const auto *CS = dyn_cast<CompoundStmt>(S)) return createEndBrace(CS, SM); return PathDiagnosticLocation(getValidSourceLocation(S, LAC, /*End=*/true), SM, SingleLocK); @@ -624,7 +669,6 @@ PathDiagnosticLocation::createConditionalColonLoc( return PathDiagnosticLocation(CO->getColonLoc(), SM, SingleLocK); } - PathDiagnosticLocation PathDiagnosticLocation::createMemberLoc(const MemberExpr *ME, const SourceManager &SM) { @@ -649,8 +693,7 @@ PathDiagnosticLocation PathDiagnosticLocation::createDeclBegin(const LocationContext *LC, const SourceManager &SM) { // FIXME: Should handle CXXTryStmt if analyser starts supporting C++. - if (const CompoundStmt *CS = - dyn_cast_or_null<CompoundStmt>(LC->getDecl()->getBody())) + if (const auto *CS = dyn_cast_or_null<CompoundStmt>(LC->getDecl()->getBody())) if (!CS->body_empty()) { SourceLocation Loc = (*CS->body_begin())->getLocStart(); return PathDiagnosticLocation(Loc, SM, SingleLocK); @@ -741,6 +784,8 @@ const Stmt *PathDiagnosticLocation::getStmt(const ExplodedNode *N) { return CEE->getCalleeContext()->getCallSite(); if (Optional<PostInitializer> PIPP = P.getAs<PostInitializer>()) return PIPP->getInitializer()->getInit(); + if (Optional<CallExitBegin> CEB = P.getAs<CallExitBegin>()) + return CEB->getReturnStmt(); return nullptr; } @@ -790,11 +835,11 @@ PathDiagnosticLocation const LocationContext *LC = N->getLocationContext(); // For member expressions, return the location of the '.' or '->'. - if (const MemberExpr *ME = dyn_cast<MemberExpr>(S)) + if (const auto *ME = dyn_cast<MemberExpr>(S)) return PathDiagnosticLocation::createMemberLoc(ME, SM); // For binary operators, return the location of the operator. - if (const BinaryOperator *B = dyn_cast<BinaryOperator>(S)) + if (const auto *B = dyn_cast<BinaryOperator>(S)) return PathDiagnosticLocation::createOperatorLoc(B, SM); if (P.getAs<PostStmtPurgeDeadSymbols>()) @@ -856,7 +901,7 @@ PathDiagnosticRange default: break; case Stmt::DeclStmtClass: { - const DeclStmt *DS = cast<DeclStmt>(S); + const auto *DS = cast<DeclStmt>(S); if (DS->isSingleDecl()) { // Should always be the case, but we'll be defensive. return SourceRange(DS->getLocStart(), @@ -886,9 +931,9 @@ PathDiagnosticRange break; } case DeclK: - if (const ObjCMethodDecl *MD = dyn_cast<ObjCMethodDecl>(D)) + if (const auto *MD = dyn_cast<ObjCMethodDecl>(D)) return MD->getSourceRange(); - if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(D)) { + if (const auto *FD = dyn_cast<FunctionDecl>(D)) { if (Stmt *Body = FD->getBody()) return Body->getSourceRange(); } @@ -898,7 +943,7 @@ PathDiagnosticRange } } - return SourceRange(Loc,Loc); + return SourceRange(Loc, Loc); } void PathDiagnosticLocation::flatten() { @@ -954,17 +999,55 @@ void PathDiagnosticCallPiece::setCallee(const CallEnter &CE, // non-autosynthesized callbacks. // Unless set here, the IsCalleeAnAutosynthesizedPropertyAccessor flag // defaults to false. - if (const ObjCMethodDecl *MD = dyn_cast<ObjCMethodDecl>(Callee)) + if (const auto *MD = dyn_cast<ObjCMethodDecl>(Callee)) IsCalleeAnAutosynthesizedPropertyAccessor = ( MD->isPropertyAccessor() && CalleeCtx->getAnalysisDeclContext()->isBodyAutosynthesized()); } -static inline void describeClass(raw_ostream &Out, const CXXRecordDecl *D, - StringRef Prefix = StringRef()) { +static void describeTemplateParameters(raw_ostream &Out, + const ArrayRef<TemplateArgument> TAList, + const LangOptions &LO, + StringRef Prefix = StringRef(), + StringRef Postfix = StringRef()); + +static void describeTemplateParameter(raw_ostream &Out, + const TemplateArgument &TArg, + const LangOptions &LO) { + + if (TArg.getKind() == TemplateArgument::ArgKind::Pack) { + describeTemplateParameters(Out, TArg.getPackAsArray(), LO); + } else { + TArg.print(PrintingPolicy(LO), Out); + } +} + +static void describeTemplateParameters(raw_ostream &Out, + const ArrayRef<TemplateArgument> TAList, + const LangOptions &LO, + StringRef Prefix, StringRef Postfix) { + if (TAList.empty()) + return; + + Out << Prefix; + for (int I = 0, Last = TAList.size() - 1; I != Last; ++I) { + describeTemplateParameter(Out, TAList[I], LO); + Out << ", "; + } + describeTemplateParameter(Out, TAList[TAList.size() - 1], LO); + Out << Postfix; +} + +static void describeClass(raw_ostream &Out, const CXXRecordDecl *D, + StringRef Prefix = StringRef()) { if (!D->getIdentifier()) return; - Out << Prefix << '\'' << *D << '\''; + Out << Prefix << '\'' << *D; + if (const auto T = dyn_cast<ClassTemplateSpecializationDecl>(D)) + describeTemplateParameters(Out, T->getTemplateArgs().asArray(), + D->getASTContext().getLangOpts(), "<", ">"); + + Out << '\''; } static bool describeCodeDecl(raw_ostream &Out, const Decl *D, @@ -979,7 +1062,7 @@ static bool describeCodeDecl(raw_ostream &Out, const Decl *D, return ExtendedDescription; } - if (const CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(D)) { + if (const auto *MD = dyn_cast<CXXMethodDecl>(D)) { Out << Prefix; if (ExtendedDescription && !MD->isUserProvided()) { if (MD->isExplicitlyDefaulted()) @@ -988,7 +1071,7 @@ static bool describeCodeDecl(raw_ostream &Out, const Decl *D, Out << "implicit "; } - if (const CXXConstructorDecl *CD = dyn_cast<CXXConstructorDecl>(MD)) { + if (const auto *CD = dyn_cast<CXXConstructorDecl>(MD)) { if (CD->isDefaultConstructor()) Out << "default "; else if (CD->isCopyConstructor()) @@ -998,7 +1081,6 @@ static bool describeCodeDecl(raw_ostream &Out, const Decl *D, Out << "constructor"; describeClass(Out, MD->getParent(), " for "); - } else if (isa<CXXDestructorDecl>(MD)) { if (!MD->isUserProvided()) { Out << "destructor"; @@ -1007,15 +1089,12 @@ static bool describeCodeDecl(raw_ostream &Out, const Decl *D, // Use ~Foo for explicitly-written destructors. Out << "'" << *MD << "'"; } - } else if (MD->isCopyAssignmentOperator()) { Out << "copy assignment operator"; describeClass(Out, MD->getParent(), " for "); - } else if (MD->isMoveAssignmentOperator()) { Out << "move assignment operator"; describeClass(Out, MD->getParent(), " for "); - } else { if (MD->getParent()->getIdentifier()) Out << "'" << *MD->getParent() << "::" << *MD << "'"; @@ -1026,7 +1105,16 @@ static bool describeCodeDecl(raw_ostream &Out, const Decl *D, return true; } - Out << Prefix << '\'' << cast<NamedDecl>(*D) << '\''; + Out << Prefix << '\'' << cast<NamedDecl>(*D); + + // Adding template parameters. + if (const auto FD = dyn_cast<FunctionDecl>(D)) + if (const TemplateArgumentList *TAList = + FD->getTemplateSpecializationArgs()) + describeTemplateParameters(Out, TAList->asArray(), + FD->getASTContext().getLangOpts(), "<", ">"); + + Out << '\''; return true; } @@ -1055,7 +1143,7 @@ PathDiagnosticCallPiece::getCallEnterWithinCallerEvent() const { return nullptr; if (Callee->isImplicit() || !Callee->hasBody()) return nullptr; - if (const CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(Callee)) + if (const auto *MD = dyn_cast<CXXMethodDecl>(Callee)) if (MD->isDefaulted()) return nullptr; @@ -1095,13 +1183,10 @@ PathDiagnosticCallPiece::getCallExitEvent() const { } static void compute_path_size(const PathPieces &pieces, unsigned &size) { - for (PathPieces::const_iterator it = pieces.begin(), - et = pieces.end(); it != et; ++it) { - const PathDiagnosticPiece *piece = it->get(); - if (const PathDiagnosticCallPiece *cp = - dyn_cast<PathDiagnosticCallPiece>(piece)) { + for (const auto &I : pieces) { + const PathDiagnosticPiece *piece = I.get(); + if (const auto *cp = dyn_cast<PathDiagnosticCallPiece>(piece)) compute_path_size(cp->path, size); - } else ++size; } @@ -1129,19 +1214,16 @@ void PathDiagnosticPiece::Profile(llvm::FoldingSetNodeID &ID) const { // FIXME: Add profiling support for code hints. ID.AddInteger((unsigned) getDisplayHint()); ArrayRef<SourceRange> Ranges = getRanges(); - for (ArrayRef<SourceRange>::iterator I = Ranges.begin(), E = Ranges.end(); - I != E; ++I) { - ID.AddInteger(I->getBegin().getRawEncoding()); - ID.AddInteger(I->getEnd().getRawEncoding()); + for (const auto &I : Ranges) { + ID.AddInteger(I.getBegin().getRawEncoding()); + ID.AddInteger(I.getEnd().getRawEncoding()); } } void PathDiagnosticCallPiece::Profile(llvm::FoldingSetNodeID &ID) const { PathDiagnosticPiece::Profile(ID); - for (PathPieces::const_iterator it = path.begin(), - et = path.end(); it != et; ++it) { - ID.Add(**it); - } + for (const auto &I : path) + ID.Add(*I); } void PathDiagnosticSpotPiece::Profile(llvm::FoldingSetNodeID &ID) const { @@ -1151,15 +1233,14 @@ void PathDiagnosticSpotPiece::Profile(llvm::FoldingSetNodeID &ID) const { void PathDiagnosticControlFlowPiece::Profile(llvm::FoldingSetNodeID &ID) const { PathDiagnosticPiece::Profile(ID); - for (const_iterator I = begin(), E = end(); I != E; ++I) - ID.Add(*I); + for (const auto &I : *this) + ID.Add(I); } void PathDiagnosticMacroPiece::Profile(llvm::FoldingSetNodeID &ID) const { PathDiagnosticSpotPiece::Profile(ID); - for (PathPieces::const_iterator I = subPieces.begin(), E = subPieces.end(); - I != E; ++I) - ID.Add(**I); + for (const auto &I : subPieces) + ID.Add(*I); } void PathDiagnosticNotePiece::Profile(llvm::FoldingSetNodeID &ID) const { @@ -1175,34 +1256,32 @@ void PathDiagnostic::Profile(llvm::FoldingSetNodeID &ID) const { void PathDiagnostic::FullProfile(llvm::FoldingSetNodeID &ID) const { Profile(ID); - for (PathPieces::const_iterator I = path.begin(), E = path.end(); I != E; ++I) - ID.Add(**I); + for (const auto &I : path) + ID.Add(*I); for (meta_iterator I = meta_begin(), E = meta_end(); I != E; ++I) ID.AddString(*I); } -StackHintGenerator::~StackHintGenerator() {} +StackHintGenerator::~StackHintGenerator() = default; std::string StackHintGeneratorForSymbol::getMessage(const ExplodedNode *N){ + if (!N) + return getMessageForSymbolNotFound(); + ProgramPoint P = N->getLocation(); CallExitEnd CExit = P.castAs<CallExitEnd>(); // FIXME: Use CallEvent to abstract this over all calls. const Stmt *CallSite = CExit.getCalleeContext()->getCallSite(); - const CallExpr *CE = dyn_cast_or_null<CallExpr>(CallSite); + const auto *CE = dyn_cast_or_null<CallExpr>(CallSite); if (!CE) - return ""; - - if (!N) - return getMessageForSymbolNotFound(); + return {}; // Check if one of the parameters are set to the interesting symbol. - ProgramStateRef State = N->getState(); - const LocationContext *LCtx = N->getLocationContext(); unsigned ArgIndex = 0; for (CallExpr::const_arg_iterator I = CE->arg_begin(), E = CE->arg_end(); I != E; ++I, ++ArgIndex){ - SVal SV = State->getSVal(*I, LCtx); + SVal SV = N->getSVal(*I); // Check if the variable corresponding to the symbol is passed by value. SymbolRef AS = SV.getAsLocSymbol(); @@ -1212,7 +1291,10 @@ std::string StackHintGeneratorForSymbol::getMessage(const ExplodedNode *N){ // Check if the parameter is a pointer to the symbol. if (Optional<loc::MemRegionVal> Reg = SV.getAs<loc::MemRegionVal>()) { - SVal PSV = State->getSVal(Reg->getRegion()); + // Do not attempt to dereference void*. + if ((*I)->getType()->isVoidPointerType()) + continue; + SVal PSV = N->getState()->getSVal(Reg->getRegion()); SymbolRef AS = PSV.getAsLocSymbol(); if (AS == Sym) { return getMessageForArg(*I, ArgIndex); @@ -1221,7 +1303,7 @@ std::string StackHintGeneratorForSymbol::getMessage(const ExplodedNode *N){ } // Check if we are returning the interesting symbol. - SVal SV = State->getSVal(CE, LCtx); + SVal SV = N->getSVal(CE); SymbolRef RetSym = SV.getAsLocSymbol(); if (RetSym == Sym) { return getMessageForReturn(CE); @@ -1243,3 +1325,84 @@ std::string StackHintGeneratorForSymbol::getMessageForArg(const Expr *ArgE, return os.str(); } + +LLVM_DUMP_METHOD void PathPieces::dump() const { + unsigned index = 0; + for (PathPieces::const_iterator I = begin(), E = end(); I != E; ++I) { + llvm::errs() << "[" << index++ << "] "; + (*I)->dump(); + llvm::errs() << "\n"; + } +} + +LLVM_DUMP_METHOD void PathDiagnosticCallPiece::dump() const { + llvm::errs() << "CALL\n--------------\n"; + + if (const Stmt *SLoc = getLocation().getStmtOrNull()) + SLoc->dump(); + else if (const auto *ND = dyn_cast_or_null<NamedDecl>(getCallee())) + llvm::errs() << *ND << "\n"; + else + getLocation().dump(); +} + +LLVM_DUMP_METHOD void PathDiagnosticEventPiece::dump() const { + llvm::errs() << "EVENT\n--------------\n"; + llvm::errs() << getString() << "\n"; + llvm::errs() << " ---- at ----\n"; + getLocation().dump(); +} + +LLVM_DUMP_METHOD void PathDiagnosticControlFlowPiece::dump() const { + llvm::errs() << "CONTROL\n--------------\n"; + getStartLocation().dump(); + llvm::errs() << " ---- to ----\n"; + getEndLocation().dump(); +} + +LLVM_DUMP_METHOD void PathDiagnosticMacroPiece::dump() const { + llvm::errs() << "MACRO\n--------------\n"; + // FIXME: Print which macro is being invoked. +} + +LLVM_DUMP_METHOD void PathDiagnosticNotePiece::dump() const { + llvm::errs() << "NOTE\n--------------\n"; + llvm::errs() << getString() << "\n"; + llvm::errs() << " ---- at ----\n"; + getLocation().dump(); +} + +LLVM_DUMP_METHOD void PathDiagnosticLocation::dump() const { + if (!isValid()) { + llvm::errs() << "<INVALID>\n"; + return; + } + + switch (K) { + case RangeK: + // FIXME: actually print the range. + llvm::errs() << "<range>\n"; + break; + case SingleLocK: + asLocation().dump(); + llvm::errs() << "\n"; + break; + case StmtK: + if (S) + S->dump(); + else + llvm::errs() << "<NULL STMT>\n"; + break; + case DeclK: + if (const auto *ND = dyn_cast_or_null<NamedDecl>(D)) + llvm::errs() << *ND << "\n"; + else if (isa<BlockDecl>(D)) + // FIXME: Make this nicer. + llvm::errs() << "<block>\n"; + else if (D) + llvm::errs() << "<unknown decl>\n"; + else + llvm::errs() << "<NULL DECL>\n"; + break; + } +} diff --git a/lib/StaticAnalyzer/Core/PlistDiagnostics.cpp b/lib/StaticAnalyzer/Core/PlistDiagnostics.cpp index 66812ed8ff5b..cfe780db9ec9 100644 --- a/lib/StaticAnalyzer/Core/PlistDiagnostics.cpp +++ b/lib/StaticAnalyzer/Core/PlistDiagnostics.cpp @@ -16,9 +16,12 @@ #include "clang/Basic/SourceManager.h" #include "clang/Basic/Version.h" #include "clang/Lex/Preprocessor.h" +#include "clang/Rewrite/Core/HTMLRewrite.h" +#include "clang/StaticAnalyzer/Core/AnalyzerOptions.h" #include "clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h" #include "clang/StaticAnalyzer/Core/IssueHash.h" #include "clang/StaticAnalyzer/Core/PathDiagnosticConsumers.h" +#include "llvm/ADT/Statistic.h" #include "llvm/ADT/SmallVector.h" #include "llvm/Support/Casting.h" using namespace clang; @@ -30,6 +33,7 @@ namespace { const std::string OutputFile; const LangOptions &LangOpts; const bool SupportsCrossFileDiagnostics; + const bool SerializeStatistics; public: PlistDiagnostics(AnalyzerOptions &AnalyzerOpts, const std::string& prefix, @@ -61,7 +65,8 @@ PlistDiagnostics::PlistDiagnostics(AnalyzerOptions &AnalyzerOpts, bool supportsMultipleFiles) : OutputFile(output), LangOpts(LO), - SupportsCrossFileDiagnostics(supportsMultipleFiles) {} + SupportsCrossFileDiagnostics(supportsMultipleFiles), + SerializeStatistics(AnalyzerOpts.shouldSerializeStats()) {} void ento::createPlistDiagnosticConsumer(AnalyzerOptions &AnalyzerOpts, PathDiagnosticConsumers &C, @@ -79,6 +84,41 @@ void ento::createPlistMultiFileDiagnosticConsumer(AnalyzerOptions &AnalyzerOpts, PP.getLangOpts(), true)); } +static void EmitRanges(raw_ostream &o, + const ArrayRef<SourceRange> Ranges, + const FIDMap& FM, + const SourceManager &SM, + const LangOptions &LangOpts, + unsigned indent) { + + if (Ranges.empty()) + return; + + Indent(o, indent) << "<key>ranges</key>\n"; + Indent(o, indent) << "<array>\n"; + ++indent; + for (auto &R : Ranges) + EmitRange(o, SM, + Lexer::getAsCharRange(SM.getExpansionRange(R), SM, LangOpts), + FM, indent + 1); + --indent; + Indent(o, indent) << "</array>\n"; +} + +static void EmitMessage(raw_ostream &o, StringRef Message, unsigned indent) { + // Output the text. + assert(!Message.empty()); + Indent(o, indent) << "<key>extended_message</key>\n"; + Indent(o, indent); + EmitString(o, Message) << '\n'; + + // Output the short text. + // FIXME: Really use a short string. + Indent(o, indent) << "<key>message</key>\n"; + Indent(o, indent); + EmitString(o, Message) << '\n'; +} + static void ReportControlFlow(raw_ostream &o, const PathDiagnosticControlFlowPiece& P, const FIDMap& FM, @@ -133,7 +173,7 @@ static void ReportControlFlow(raw_ostream &o, Indent(o, indent) << "</dict>\n"; } -static void ReportEvent(raw_ostream &o, const PathDiagnosticPiece& P, +static void ReportEvent(raw_ostream &o, const PathDiagnosticEventPiece& P, const FIDMap& FM, const SourceManager &SM, const LangOptions &LangOpts, @@ -158,34 +198,14 @@ static void ReportEvent(raw_ostream &o, const PathDiagnosticPiece& P, // Output the ranges (if any). ArrayRef<SourceRange> Ranges = P.getRanges(); - - if (!Ranges.empty()) { - Indent(o, indent) << "<key>ranges</key>\n"; - Indent(o, indent) << "<array>\n"; - ++indent; - for (auto &R : Ranges) - EmitRange(o, SM, - Lexer::getAsCharRange(SM.getExpansionRange(R), SM, LangOpts), - FM, indent + 1); - --indent; - Indent(o, indent) << "</array>\n"; - } + EmitRanges(o, Ranges, FM, SM, LangOpts, indent); // Output the call depth. Indent(o, indent) << "<key>depth</key>"; EmitInteger(o, depth) << '\n'; // Output the text. - assert(!P.getString().empty()); - Indent(o, indent) << "<key>extended_message</key>\n"; - Indent(o, indent); - EmitString(o, P.getString()) << '\n'; - - // Output the short text. - // FIXME: Really use a short string. - Indent(o, indent) << "<key>message</key>\n"; - Indent(o, indent); - EmitString(o, P.getString()) << '\n'; + EmitMessage(o, P.getString(), indent); // Finish up. --indent; @@ -241,6 +261,34 @@ static void ReportMacro(raw_ostream &o, } } +static void ReportNote(raw_ostream &o, const PathDiagnosticNotePiece& P, + const FIDMap& FM, + const SourceManager &SM, + const LangOptions &LangOpts, + unsigned indent, + unsigned depth) { + + Indent(o, indent) << "<dict>\n"; + ++indent; + + // 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, FM, SM, LangOpts, indent); + + // Output the text. + EmitMessage(o, P.getString(), indent); + + // Finish up. + --indent; + Indent(o, indent); o << "</dict>\n"; +} + static void ReportDiag(raw_ostream &o, const PathDiagnosticPiece& P, const FIDMap& FM, const SourceManager &SM, const LangOptions &LangOpts) { @@ -266,7 +314,7 @@ static void ReportPiece(raw_ostream &o, indent, depth); break; case PathDiagnosticPiece::Event: - ReportEvent(o, cast<PathDiagnosticSpotPiece>(P), FM, SM, LangOpts, + ReportEvent(o, cast<PathDiagnosticEventPiece>(P), FM, SM, LangOpts, indent, depth, isKeyEvent); break; case PathDiagnosticPiece::Macro: @@ -274,7 +322,8 @@ static void ReportPiece(raw_ostream &o, indent, depth); break; case PathDiagnosticPiece::Note: - // FIXME: Extend the plist format to support those. + ReportNote(o, cast<PathDiagnosticNotePiece>(P), FM, SM, LangOpts, + indent, depth); break; } } @@ -359,15 +408,39 @@ void PlistDiagnostics::FlushDiagnosticsImpl( for (std::vector<const PathDiagnostic*>::iterator DI=Diags.begin(), DE = Diags.end(); DI!=DE; ++DI) { - o << " <dict>\n" - " <key>path</key>\n"; + o << " <dict>\n"; const PathDiagnostic *D = *DI; + const PathPieces &PP = D->path; + + assert(std::is_partitioned( + PP.begin(), PP.end(), + [](const std::shared_ptr<PathDiagnosticPiece> &E) + { return E->getKind() == PathDiagnosticPiece::Note; }) && + "PathDiagnostic is not partitioned so that notes precede the rest"); + + PathPieces::const_iterator FirstNonNote = std::partition_point( + PP.begin(), PP.end(), + [](const std::shared_ptr<PathDiagnosticPiece> &E) + { return E->getKind() == PathDiagnosticPiece::Note; }); + + PathPieces::const_iterator I = PP.begin(); + + if (FirstNonNote != PP.begin()) { + o << " <key>notes</key>\n" + " <array>\n"; + + for (; I != FirstNonNote; ++I) + ReportDiag(o, **I, FM, *SM, LangOpts); + + o << " </array>\n"; + } + + o << " <key>path</key>\n"; o << " <array>\n"; - for (PathPieces::const_iterator I = D->path.begin(), E = D->path.end(); - I != E; ++I) + for (PathPieces::const_iterator E = PP.end(); I != E; ++I) ReportDiag(o, **I, FM, *SM, LangOpts); o << " </array>\n"; @@ -484,6 +557,15 @@ void PlistDiagnostics::FlushDiagnosticsImpl( o << " </array>\n"; + if (llvm::AreStatisticsEnabled() && SerializeStatistics) { + o << " <key>statistics</key>\n"; + std::string stats; + llvm::raw_string_ostream os(stats); + llvm::PrintStatisticsJSON(os); + os.flush(); + EmitString(o, html::EscapeText(stats)) << '\n'; + } + // Finish. o << "</dict>\n</plist>"; } diff --git a/lib/StaticAnalyzer/Core/ProgramState.cpp b/lib/StaticAnalyzer/Core/ProgramState.cpp index 5b6b7339697f..2b401607293b 100644 --- a/lib/StaticAnalyzer/Core/ProgramState.cpp +++ b/lib/StaticAnalyzer/Core/ProgramState.cpp @@ -17,6 +17,7 @@ #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; @@ -125,16 +126,27 @@ ProgramStateRef ProgramState::bindLoc(Loc LV, return newState; } -ProgramStateRef ProgramState::bindDefault(SVal loc, - SVal V, - const LocationContext *LCtx) const { +ProgramStateRef +ProgramState::bindDefaultInitial(SVal loc, SVal V, + const LocationContext *LCtx) const { + ProgramStateManager &Mgr = getStateManager(); + const MemRegion *R = loc.castAs<loc::MemRegionVal>().getRegion(); + const StoreRef &newStore = Mgr.StoreMgr->BindDefaultInitial(getStore(), R, V); + ProgramStateRef new_state = makeWithStore(newStore); + return Mgr.getOwningEngine() + ? Mgr.getOwningEngine()->processRegionChange(new_state, R, LCtx) + : new_state; +} + +ProgramStateRef +ProgramState::bindDefaultZero(SVal loc, const LocationContext *LCtx) const { ProgramStateManager &Mgr = getStateManager(); const MemRegion *R = loc.castAs<loc::MemRegionVal>().getRegion(); - const StoreRef &newStore = Mgr.StoreMgr->BindDefault(getStore(), R, V); + const StoreRef &newStore = Mgr.StoreMgr->BindDefaultZero(getStore(), R); ProgramStateRef new_state = makeWithStore(newStore); - return Mgr.getOwningEngine() ? - Mgr.getOwningEngine()->processRegionChange(new_state, R, LCtx) : - new_state; + return Mgr.getOwningEngine() + ? Mgr.getOwningEngine()->processRegionChange(new_state, R, LCtx) + : new_state; } typedef ArrayRef<const MemRegion *> RegionList; @@ -254,7 +266,7 @@ SVal ProgramState::getSValAsScalarOrLoc(const MemRegion *R) const { } SVal ProgramState::getSVal(Loc location, QualType T) const { - SVal V = getRawSVal(cast<Loc>(location), T); + SVal V = getRawSVal(location, T); // If 'V' is a symbolic value that is *perfectly* constrained to // be a constant value, use that value instead to lessen the burden @@ -324,9 +336,8 @@ ProgramStateRef ProgramState::assumeInBound(DefinedOrUnknownSVal Idx, // Get the offset: the minimum value of the array index type. BasicValueFactory &BVF = svalBuilder.getBasicValueFactory(); - // FIXME: This should be using ValueManager::ArrayindexTy...somehow. if (indexTy.isNull()) - indexTy = Ctx.IntTy; + indexTy = svalBuilder.getArrayIndexType(); nonloc::ConcreteInt Min(BVF.getMinValue(indexTy)); // Adjust the index. @@ -354,6 +365,17 @@ ProgramStateRef ProgramState::assumeInBound(DefinedOrUnknownSVal Idx, return CM.assume(this, inBound.castAs<DefinedSVal>(), Assumption); } +ConditionTruthVal ProgramState::isNonNull(SVal V) const { + ConditionTruthVal IsNull = isNull(V); + if (IsNull.isUnderconstrained()) + return IsNull; + return ConditionTruthVal(!IsNull.getValue()); +} + +ConditionTruthVal ProgramState::areEqual(SVal Lhs, SVal Rhs) const { + return stateMgr->getSValBuilder().areEqual(this, Lhs, Rhs); +} + ConditionTruthVal ProgramState::isNull(SVal V) const { if (V.isZeroConstant()) return true; @@ -426,24 +448,30 @@ void ProgramState::setStore(const StoreRef &newStore) { // State pretty-printing. //===----------------------------------------------------------------------===// -void ProgramState::print(raw_ostream &Out, - const char *NL, const char *Sep) const { +void ProgramState::print(raw_ostream &Out, const char *NL, const char *Sep, + const LocationContext *LC) const { // Print the store. ProgramStateManager &Mgr = getStateManager(); Mgr.getStoreManager().print(getStore(), Out, NL, Sep); // Print out the environment. - Env.print(Out, NL, Sep); + Env.print(Out, NL, Sep, LC); // Print out the constraints. Mgr.getConstraintManager().print(this, Out, NL, Sep); + // Print out the tracked dynamic types. + printDynamicTypeInfo(this, Out, NL, Sep); + + // Print out tainted symbols. + printTaint(Out, NL, Sep); + // Print checker-specific data. - Mgr.getOwningEngine()->printState(Out, this, NL, Sep); + Mgr.getOwningEngine()->printState(Out, this, NL, Sep, LC); } -void ProgramState::printDOT(raw_ostream &Out) const { - print(Out, "\\l", "\\|"); +void ProgramState::printDOT(raw_ostream &Out, const LocationContext *LC) const { + print(Out, "\\l", "\\|", LC); } LLVM_DUMP_METHOD void ProgramState::dump() const { @@ -455,7 +483,7 @@ void ProgramState::printTaint(raw_ostream &Out, TaintMapImpl TM = get<TaintMap>(); if (!TM.isEmpty()) - Out <<"Tainted Symbols:" << NL; + Out <<"Tainted symbols:" << NL; for (TaintMapImpl::iterator I = TM.begin(), E = TM.end(); I != E; ++I) { Out << I->first << " : " << I->second << NL; @@ -781,8 +809,7 @@ bool ProgramState::isTainted(SymbolRef Sym, TaintTagType Kind) const { // 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 == I.first || R->isSubRegionOf(I.first))) + if (Kind == I.second && R->isSubRegionOf(I.first)) return true; } } diff --git a/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp b/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp index 5a4031c0b4a5..e8c7bdbde385 100644 --- a/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp +++ b/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp @@ -12,10 +12,10 @@ // //===----------------------------------------------------------------------===// -#include "RangedConstraintManager.h" #include "clang/StaticAnalyzer/Core/PathSensitive/APSIntType.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/RangedConstraintManager.h" #include "llvm/ADT/FoldingSet.h" #include "llvm/ADT/ImmutableSet.h" #include "llvm/Support/raw_ostream.h" @@ -23,263 +23,203 @@ using namespace clang; using namespace ento; -/// A Range represents the closed range [from, to]. The caller must -/// guarantee that from <= to. Note that Range is immutable, so as not -/// to subvert RangeSet's immutability. -namespace { -class Range : public std::pair<const llvm::APSInt *, const llvm::APSInt *> { -public: - Range(const llvm::APSInt &from, const llvm::APSInt &to) - : std::pair<const llvm::APSInt *, const llvm::APSInt *>(&from, &to) { - assert(from <= to); - } - bool Includes(const llvm::APSInt &v) const { - return *first <= v && v <= *second; - } - const llvm::APSInt &From() const { return *first; } - const llvm::APSInt &To() const { return *second; } - const llvm::APSInt *getConcreteValue() const { - return &From() == &To() ? &From() : nullptr; - } - - void Profile(llvm::FoldingSetNodeID &ID) const { - ID.AddPointer(&From()); - ID.AddPointer(&To()); - } -}; - -class RangeTrait : public llvm::ImutContainerInfo<Range> { -public: - // When comparing if one Range is less than another, we should compare - // the actual APSInt values instead of their pointers. This keeps the order - // consistent (instead of comparing by pointer values) and can potentially - // be used to speed up some of the operations in RangeSet. - static inline bool isLess(key_type_ref lhs, key_type_ref rhs) { - return *lhs.first < *rhs.first || - (!(*rhs.first < *lhs.first) && *lhs.second < *rhs.second); - } -}; - -/// RangeSet contains a set of ranges. If the set is empty, then -/// there the value of a symbol is overly constrained and there are no -/// possible values for that symbol. -class RangeSet { - typedef llvm::ImmutableSet<Range, RangeTrait> PrimRangeSet; - PrimRangeSet ranges; // no need to make const, since it is an - // ImmutableSet - this allows default operator= - // to work. -public: - typedef PrimRangeSet::Factory Factory; - typedef PrimRangeSet::iterator iterator; - - RangeSet(PrimRangeSet RS) : ranges(RS) {} - - /// Create a new set with all ranges of this set and RS. - /// Possible intersections are not checked here. - RangeSet addRange(Factory &F, const RangeSet &RS) { - PrimRangeSet Ranges(RS.ranges); - for (const auto &range : ranges) - Ranges = F.add(Ranges, range); - return RangeSet(Ranges); - } - - iterator begin() const { return ranges.begin(); } - iterator end() const { return ranges.end(); } - - bool isEmpty() const { return ranges.isEmpty(); } - - /// Construct a new RangeSet representing '{ [from, to] }'. - RangeSet(Factory &F, const llvm::APSInt &from, const llvm::APSInt &to) - : ranges(F.add(F.getEmptySet(), Range(from, to))) {} - - /// Profile - Generates a hash profile of this RangeSet for use - /// by FoldingSet. - void Profile(llvm::FoldingSetNodeID &ID) const { ranges.Profile(ID); } - - /// getConcreteValue - If a symbol is contrained to equal a specific integer - /// constant then this method returns that value. Otherwise, it returns - /// NULL. - const llvm::APSInt *getConcreteValue() const { - return ranges.isSingleton() ? ranges.begin()->getConcreteValue() : nullptr; - } +void RangeSet::IntersectInRange(BasicValueFactory &BV, Factory &F, + const llvm::APSInt &Lower, const llvm::APSInt &Upper, + PrimRangeSet &newRanges, PrimRangeSet::iterator &i, + PrimRangeSet::iterator &e) const { + // There are six cases for each range R in the set: + // 1. R is entirely before the intersection range. + // 2. R is entirely after the intersection range. + // 3. R contains the entire intersection range. + // 4. R starts before the intersection range and ends in the middle. + // 5. R starts in the middle of the intersection range and ends after it. + // 6. R is entirely contained in the intersection range. + // These correspond to each of the conditions below. + for (/* i = begin(), e = end() */; i != e; ++i) { + if (i->To() < Lower) { + continue; + } + if (i->From() > Upper) { + break; + } -private: - void IntersectInRange(BasicValueFactory &BV, Factory &F, - const llvm::APSInt &Lower, const llvm::APSInt &Upper, - PrimRangeSet &newRanges, PrimRangeSet::iterator &i, - PrimRangeSet::iterator &e) const { - // There are six cases for each range R in the set: - // 1. R is entirely before the intersection range. - // 2. R is entirely after the intersection range. - // 3. R contains the entire intersection range. - // 4. R starts before the intersection range and ends in the middle. - // 5. R starts in the middle of the intersection range and ends after it. - // 6. R is entirely contained in the intersection range. - // These correspond to each of the conditions below. - for (/* i = begin(), e = end() */; i != e; ++i) { - if (i->To() < Lower) { - continue; - } - if (i->From() > Upper) { + if (i->Includes(Lower)) { + if (i->Includes(Upper)) { + newRanges = + F.add(newRanges, Range(BV.getValue(Lower), BV.getValue(Upper))); break; - } - - if (i->Includes(Lower)) { - if (i->Includes(Upper)) { - newRanges = - F.add(newRanges, Range(BV.getValue(Lower), BV.getValue(Upper))); - break; - } else - newRanges = F.add(newRanges, Range(BV.getValue(Lower), i->To())); - } else { - if (i->Includes(Upper)) { - newRanges = F.add(newRanges, Range(i->From(), BV.getValue(Upper))); - break; - } else - newRanges = F.add(newRanges, *i); - } + } else + newRanges = F.add(newRanges, Range(BV.getValue(Lower), i->To())); + } else { + if (i->Includes(Upper)) { + newRanges = F.add(newRanges, Range(i->From(), BV.getValue(Upper))); + break; + } else + newRanges = F.add(newRanges, *i); } } +} - const llvm::APSInt &getMinValue() const { - assert(!isEmpty()); - return ranges.begin()->From(); - } +const llvm::APSInt &RangeSet::getMinValue() const { + assert(!isEmpty()); + return ranges.begin()->From(); +} - bool pin(llvm::APSInt &Lower, llvm::APSInt &Upper) const { - // This function has nine cases, the cartesian product of range-testing - // both the upper and lower bounds against the symbol's type. - // Each case requires a different pinning operation. - // The function returns false if the described range is entirely outside - // the range of values for the associated symbol. - APSIntType Type(getMinValue()); - APSIntType::RangeTestResultKind LowerTest = Type.testInRange(Lower, true); - APSIntType::RangeTestResultKind UpperTest = Type.testInRange(Upper, true); - - switch (LowerTest) { +bool RangeSet::pin(llvm::APSInt &Lower, llvm::APSInt &Upper) const { + // This function has nine cases, the cartesian product of range-testing + // both the upper and lower bounds against the symbol's type. + // Each case requires a different pinning operation. + // The function returns false if the described range is entirely outside + // the range of values for the associated symbol. + APSIntType Type(getMinValue()); + APSIntType::RangeTestResultKind LowerTest = Type.testInRange(Lower, true); + APSIntType::RangeTestResultKind UpperTest = Type.testInRange(Upper, true); + + switch (LowerTest) { + case APSIntType::RTR_Below: + switch (UpperTest) { case APSIntType::RTR_Below: - switch (UpperTest) { - case APSIntType::RTR_Below: - // The entire range is outside the symbol's set of possible values. - // If this is a conventionally-ordered range, the state is infeasible. - if (Lower <= Upper) - return false; - - // However, if the range wraps around, it spans all possible values. - Lower = Type.getMinValue(); - Upper = Type.getMaxValue(); - break; - case APSIntType::RTR_Within: - // The range starts below what's possible but ends within it. Pin. - Lower = Type.getMinValue(); - Type.apply(Upper); - break; - case APSIntType::RTR_Above: - // The range spans all possible values for the symbol. Pin. - Lower = Type.getMinValue(); - Upper = Type.getMaxValue(); - break; - } + // The entire range is outside the symbol's set of possible values. + // If this is a conventionally-ordered range, the state is infeasible. + if (Lower <= Upper) + return false; + + // However, if the range wraps around, it spans all possible values. + Lower = Type.getMinValue(); + Upper = Type.getMaxValue(); break; case APSIntType::RTR_Within: - switch (UpperTest) { - case APSIntType::RTR_Below: - // The range wraps around, but all lower values are not possible. - Type.apply(Lower); - Upper = Type.getMaxValue(); - break; - case APSIntType::RTR_Within: - // The range may or may not wrap around, but both limits are valid. - Type.apply(Lower); - Type.apply(Upper); - break; - case APSIntType::RTR_Above: - // The range starts within what's possible but ends above it. Pin. - Type.apply(Lower); - Upper = Type.getMaxValue(); - break; - } + // The range starts below what's possible but ends within it. Pin. + Lower = Type.getMinValue(); + Type.apply(Upper); break; case APSIntType::RTR_Above: - switch (UpperTest) { - case APSIntType::RTR_Below: - // The range wraps but is outside the symbol's set of possible values. - return false; - case APSIntType::RTR_Within: - // The range starts above what's possible but ends within it (wrap). - Lower = Type.getMinValue(); - Type.apply(Upper); - break; - case APSIntType::RTR_Above: - // The entire range is outside the symbol's set of possible values. - // If this is a conventionally-ordered range, the state is infeasible. - if (Lower <= Upper) - return false; - - // However, if the range wraps around, it spans all possible values. - Lower = Type.getMinValue(); - Upper = Type.getMaxValue(); - break; - } + // The range spans all possible values for the symbol. Pin. + Lower = Type.getMinValue(); + Upper = Type.getMaxValue(); + break; + } + break; + case APSIntType::RTR_Within: + switch (UpperTest) { + case APSIntType::RTR_Below: + // The range wraps around, but all lower values are not possible. + Type.apply(Lower); + Upper = Type.getMaxValue(); + break; + case APSIntType::RTR_Within: + // The range may or may not wrap around, but both limits are valid. + Type.apply(Lower); + Type.apply(Upper); + break; + case APSIntType::RTR_Above: + // The range starts within what's possible but ends above it. Pin. + Type.apply(Lower); + Upper = Type.getMaxValue(); break; } + break; + case APSIntType::RTR_Above: + switch (UpperTest) { + case APSIntType::RTR_Below: + // The range wraps but is outside the symbol's set of possible values. + return false; + case APSIntType::RTR_Within: + // The range starts above what's possible but ends within it (wrap). + Lower = Type.getMinValue(); + Type.apply(Upper); + break; + case APSIntType::RTR_Above: + // The entire range is outside the symbol's set of possible values. + // If this is a conventionally-ordered range, the state is infeasible. + if (Lower <= Upper) + return false; - return true; + // However, if the range wraps around, it spans all possible values. + Lower = Type.getMinValue(); + Upper = Type.getMaxValue(); + break; + } + break; } -public: - // Returns a set containing the values in the receiving set, intersected with - // the closed range [Lower, Upper]. Unlike the Range type, this range uses - // modular arithmetic, corresponding to the common treatment of C integer - // overflow. Thus, if the Lower bound is greater than the Upper bound, the - // range is taken to wrap around. This is equivalent to taking the - // intersection with the two ranges [Min, Upper] and [Lower, Max], - // or, alternatively, /removing/ all integers between Upper and Lower. - RangeSet Intersect(BasicValueFactory &BV, Factory &F, llvm::APSInt Lower, - llvm::APSInt Upper) const { - if (!pin(Lower, Upper)) - return F.getEmptySet(); - - PrimRangeSet newRanges = F.getEmptySet(); - - PrimRangeSet::iterator i = begin(), e = end(); - if (Lower <= Upper) - IntersectInRange(BV, F, Lower, Upper, newRanges, i, e); - else { - // The order of the next two statements is important! - // IntersectInRange() does not reset the iteration state for i and e. - // Therefore, the lower range most be handled first. - IntersectInRange(BV, F, BV.getMinValue(Upper), Upper, newRanges, i, e); - IntersectInRange(BV, F, Lower, BV.getMaxValue(Lower), newRanges, i, e); - } + return true; +} + +// Returns a set containing the values in the receiving set, intersected with +// the closed range [Lower, Upper]. Unlike the Range type, this range uses +// modular arithmetic, corresponding to the common treatment of C integer +// overflow. Thus, if the Lower bound is greater than the Upper bound, the +// range is taken to wrap around. This is equivalent to taking the +// intersection with the two ranges [Min, Upper] and [Lower, Max], +// or, alternatively, /removing/ all integers between Upper and Lower. +RangeSet RangeSet::Intersect(BasicValueFactory &BV, Factory &F, + llvm::APSInt Lower, llvm::APSInt Upper) const { + if (!pin(Lower, Upper)) + return F.getEmptySet(); - return newRanges; + PrimRangeSet newRanges = F.getEmptySet(); + + PrimRangeSet::iterator i = begin(), e = end(); + if (Lower <= Upper) + IntersectInRange(BV, F, Lower, Upper, newRanges, i, e); + else { + // The order of the next two statements is important! + // IntersectInRange() does not reset the iteration state for i and e. + // Therefore, the lower range most be handled first. + IntersectInRange(BV, F, BV.getMinValue(Upper), Upper, newRanges, i, e); + IntersectInRange(BV, F, Lower, BV.getMaxValue(Lower), newRanges, i, e); } - void print(raw_ostream &os) const { - bool isFirst = true; - os << "{ "; - for (iterator i = begin(), e = end(); i != e; ++i) { - if (isFirst) - isFirst = false; - else - os << ", "; - - os << '[' << i->From().toString(10) << ", " << i->To().toString(10) - << ']'; + 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. +RangeSet RangeSet::Negate(BasicValueFactory &BV, Factory &F) const { + PrimRangeSet newRanges = F.getEmptySet(); + + for (iterator i = begin(), e = end(); i != e; ++i) { + const llvm::APSInt &from = i->From(), &to = i->To(); + const llvm::APSInt &newTo = (from.isMinSignedValue() ? + BV.getMaxValue(from) : + BV.getValue(- from)); + if (to.isMaxSignedValue() && !newRanges.isEmpty() && + newRanges.begin()->From().isMinSignedValue()) { + assert(newRanges.begin()->To().isMinSignedValue() && + "Ranges should not overlap"); + assert(!from.isMinSignedValue() && "Ranges should not overlap"); + const llvm::APSInt &newFrom = newRanges.begin()->From(); + newRanges = + F.add(F.remove(newRanges, *newRanges.begin()), Range(newFrom, newTo)); + } else if (!to.isMinSignedValue()) { + const llvm::APSInt &newFrom = BV.getValue(- to); + newRanges = F.add(newRanges, Range(newFrom, newTo)); + } + if (from.isMinSignedValue()) { + newRanges = F.add(newRanges, Range(BV.getMinValue(from), + BV.getMinValue(from))); } - os << " }"; } - bool operator==(const RangeSet &other) const { - return ranges == other.ranges; - } -}; -} // end anonymous namespace + return newRanges; +} -REGISTER_TRAIT_WITH_PROGRAMSTATE(ConstraintRange, - CLANG_ENTO_PROGRAMSTATE_MAP(SymbolRef, - RangeSet)) +void RangeSet::print(raw_ostream &os) const { + bool isFirst = true; + os << "{ "; + for (iterator i = begin(), e = end(); i != e; ++i) { + if (isFirst) + isFirst = false; + else + os << ", "; + + os << '[' << i->From().toString(10) << ", " << i->To().toString(10) + << ']'; + } + os << " }"; +} namespace { class RangeConstraintManager : public RangedConstraintManager { @@ -344,6 +284,8 @@ private: RangeSet::Factory F; RangeSet getRange(ProgramStateRef State, SymbolRef Sym); + const RangeSet* getRangeForMinusSymbol(ProgramStateRef State, + SymbolRef Sym); RangeSet getSymLTRange(ProgramStateRef St, SymbolRef Sym, const llvm::APSInt &Int, @@ -360,6 +302,7 @@ private: RangeSet getSymGERange(ProgramStateRef St, SymbolRef Sym, const llvm::APSInt &Int, const llvm::APSInt &Adjustment); + }; } // end anonymous namespace @@ -400,9 +343,11 @@ bool RangeConstraintManager::canReasonAbout(SVal X) const { if (BinaryOperator::isEqualityOp(SSE->getOpcode()) || BinaryOperator::isRelationalOp(SSE->getOpcode())) { // We handle Loc <> Loc comparisons, but not (yet) NonLoc <> NonLoc. + // We've recently started producing Loc <> NonLoc comparisons (that + // result from casts of one of the operands between eg. intptr_t and + // void *), but we can't reason about them yet. if (Loc::isLocType(SSE->getLHS()->getType())) { - assert(Loc::isLocType(SSE->getRHS()->getType())); - return true; + return Loc::isLocType(SSE->getRHS()->getType()); } } } @@ -474,7 +419,7 @@ static RangeSet assumeNonZero( --IntType.getZeroValue()); } -/// \brief Apply implicit constraints for bitwise OR- and AND-. +/// Apply implicit constraints for bitwise OR- and AND-. /// For unsigned types, bitwise OR with a constant always returns /// a value greater-or-equal than the constant, and bitwise AND /// returns a value less-or-equal then the constant. @@ -515,9 +460,15 @@ RangeSet RangeConstraintManager::getRange(ProgramStateRef State, if (ConstraintRangeTy::data_type *V = State->get<ConstraintRange>(Sym)) return *V; + BasicValueFactory &BV = getBasicVals(); + + // 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)) + return R->Negate(BV, F); + // Lazily generate a new RangeSet representing all possible values for the // given symbol type. - BasicValueFactory &BV = getBasicVals(); QualType T = Sym->getType(); RangeSet Result(F, BV.getMinValue(T), BV.getMaxValue(T)); @@ -533,6 +484,32 @@ RangeSet RangeConstraintManager::getRange(ProgramStateRef State, return Result; } +// FIXME: Once SValBuilder supports unary minus, we should use SValBuilder to +// obtain the negated symbolic expression instead of constructing the +// symbol manually. This will allow us to support finding ranges of not +// only negated SymSymExpr-type expressions, but also of other, simpler +// expressions which we currently do not know how to negate. +const RangeSet* +RangeConstraintManager::getRangeForMinusSymbol(ProgramStateRef State, + SymbolRef Sym) { + if (const SymSymExpr *SSE = dyn_cast<SymSymExpr>(Sym)) { + if (SSE->getOpcode() == BO_Sub) { + QualType T = Sym->getType(); + SymbolManager &SymMgr = State->getSymbolManager(); + SymbolRef negSym = SymMgr.getSymSymExpr(SSE->getRHS(), BO_Sub, + SSE->getLHS(), T); + if (const RangeSet *negV = State->get<ConstraintRange>(negSym)) { + // Unsigned range set cannot be negated, unless it is [0, 0]. + if ((negV->getConcreteValue() && + (*negV->getConcreteValue() == 0)) || + T->isSignedIntegerOrEnumerationType()) + return negV; + } + } + } + return nullptr; +} + //===------------------------------------------------------------------------=== // assumeSymX methods: protected interface for RangeConstraintManager. //===------------------------------------------------------------------------===/ diff --git a/lib/StaticAnalyzer/Core/RangedConstraintManager.cpp b/lib/StaticAnalyzer/Core/RangedConstraintManager.cpp index 55ff15806efe..f99853f07073 100644 --- a/lib/StaticAnalyzer/Core/RangedConstraintManager.cpp +++ b/lib/StaticAnalyzer/Core/RangedConstraintManager.cpp @@ -12,8 +12,8 @@ // //===----------------------------------------------------------------------===// -#include "RangedConstraintManager.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/RangedConstraintManager.h" namespace clang { @@ -52,17 +52,18 @@ ProgramStateRef RangedConstraintManager::assumeSym(ProgramStateRef State, assert(BinaryOperator::isComparisonOp(Op)); // For now, we only support comparing pointers. - assert(Loc::isLocType(SSE->getLHS()->getType())); - assert(Loc::isLocType(SSE->getRHS()->getType())); - QualType DiffTy = SymMgr.getContext().getPointerDiffType(); - SymbolRef Subtraction = - SymMgr.getSymSymExpr(SSE->getRHS(), BO_Sub, SSE->getLHS(), DiffTy); - - const llvm::APSInt &Zero = getBasicVals().getValue(0, DiffTy); - Op = BinaryOperator::reverseComparisonOp(Op); - if (!Assumption) - Op = BinaryOperator::negateComparisonOp(Op); - return assumeSymRel(State, Subtraction, Op, Zero); + if (Loc::isLocType(SSE->getLHS()->getType()) && + Loc::isLocType(SSE->getRHS()->getType())) { + QualType DiffTy = SymMgr.getContext().getPointerDiffType(); + SymbolRef Subtraction = + SymMgr.getSymSymExpr(SSE->getRHS(), BO_Sub, SSE->getLHS(), DiffTy); + + const llvm::APSInt &Zero = getBasicVals().getValue(0, DiffTy); + Op = BinaryOperator::reverseComparisonOp(Op); + if (!Assumption) + Op = BinaryOperator::negateComparisonOp(Op); + return assumeSymRel(State, Subtraction, Op, Zero); + } } // If we get here, there's nothing else we can do but treat the symbol as diff --git a/lib/StaticAnalyzer/Core/RangedConstraintManager.h b/lib/StaticAnalyzer/Core/RangedConstraintManager.h deleted file mode 100644 index a4e6062a4f57..000000000000 --- a/lib/StaticAnalyzer/Core/RangedConstraintManager.h +++ /dev/null @@ -1,102 +0,0 @@ -//== RangedConstraintManager.h ----------------------------------*- C++ -*--==// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// Ranged constraint manager, built on SimpleConstraintManager. -// -//===----------------------------------------------------------------------===// - -#ifndef LLVM_CLANG_LIB_STATICANALYZER_CORE_RANGEDCONSTRAINTMANAGER_H -#define LLVM_CLANG_LIB_STATICANALYZER_CORE_RANGEDCONSTRAINTMANAGER_H - -#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/SimpleConstraintManager.h" - -namespace clang { - -namespace ento { - -class RangedConstraintManager : public SimpleConstraintManager { -public: - RangedConstraintManager(SubEngine *SE, SValBuilder &SB) - : SimpleConstraintManager(SE, SB) {} - - ~RangedConstraintManager() override; - - //===------------------------------------------------------------------===// - // Implementation for interface from SimpleConstraintManager. - //===------------------------------------------------------------------===// - - ProgramStateRef assumeSym(ProgramStateRef State, SymbolRef Sym, - bool Assumption) override; - - ProgramStateRef assumeSymInclusiveRange(ProgramStateRef State, SymbolRef Sym, - const llvm::APSInt &From, - const llvm::APSInt &To, - bool InRange) override; - - ProgramStateRef assumeSymUnsupported(ProgramStateRef State, SymbolRef Sym, - bool Assumption) override; - -protected: - /// Assume a constraint between a symbolic expression and a concrete integer. - virtual ProgramStateRef assumeSymRel(ProgramStateRef State, SymbolRef Sym, - BinaryOperator::Opcode op, - const llvm::APSInt &Int); - - //===------------------------------------------------------------------===// - // Interface that subclasses must implement. - //===------------------------------------------------------------------===// - - // Each of these is of the form "$Sym+Adj <> V", where "<>" is the comparison - // operation for the method being invoked. - - virtual ProgramStateRef assumeSymNE(ProgramStateRef State, SymbolRef Sym, - const llvm::APSInt &V, - const llvm::APSInt &Adjustment) = 0; - - virtual ProgramStateRef assumeSymEQ(ProgramStateRef State, SymbolRef Sym, - const llvm::APSInt &V, - const llvm::APSInt &Adjustment) = 0; - - virtual ProgramStateRef assumeSymLT(ProgramStateRef State, SymbolRef Sym, - const llvm::APSInt &V, - const llvm::APSInt &Adjustment) = 0; - - virtual ProgramStateRef assumeSymGT(ProgramStateRef State, SymbolRef Sym, - const llvm::APSInt &V, - const llvm::APSInt &Adjustment) = 0; - - virtual ProgramStateRef assumeSymLE(ProgramStateRef State, SymbolRef Sym, - const llvm::APSInt &V, - const llvm::APSInt &Adjustment) = 0; - - virtual ProgramStateRef assumeSymGE(ProgramStateRef State, SymbolRef Sym, - const llvm::APSInt &V, - const llvm::APSInt &Adjustment) = 0; - - virtual ProgramStateRef assumeSymWithinInclusiveRange( - ProgramStateRef State, SymbolRef Sym, const llvm::APSInt &From, - const llvm::APSInt &To, const llvm::APSInt &Adjustment) = 0; - - virtual ProgramStateRef assumeSymOutsideInclusiveRange( - ProgramStateRef State, SymbolRef Sym, const llvm::APSInt &From, - const llvm::APSInt &To, const llvm::APSInt &Adjustment) = 0; - - //===------------------------------------------------------------------===// - // Internal implementation. - //===------------------------------------------------------------------===// -private: - static void computeAdjustment(SymbolRef &Sym, llvm::APSInt &Adjustment); -}; - -} // end GR namespace - -} // end clang namespace - -#endif diff --git a/lib/StaticAnalyzer/Core/RegionStore.cpp b/lib/StaticAnalyzer/Core/RegionStore.cpp index e2e69bb28ec2..db6449e6d5f3 100644 --- a/lib/StaticAnalyzer/Core/RegionStore.cpp +++ b/lib/StaticAnalyzer/Core/RegionStore.cpp @@ -230,11 +230,6 @@ Optional<SVal> RegionBindingsRef::getDirectBinding(const MemRegion *R) const { } Optional<SVal> RegionBindingsRef::getDefaultBinding(const MemRegion *R) const { - if (R->isBoundable()) - if (const TypedValueRegion *TR = dyn_cast<TypedValueRegion>(R)) - if (TR->getValueType()->isUnionType()) - return UnknownVal(); - return Optional<SVal>::create(lookup(R, BindingKey::Default)); } @@ -338,7 +333,7 @@ private: /// To disable all small-struct-dependent behavior, set the option to "0". unsigned SmallStructLimit; - /// \brief A helper used to populate the work list with the given set of + /// A helper used to populate the work list with the given set of /// regions. void populateWorkList(invalidateRegionsWorker &W, ArrayRef<SVal> Values, @@ -409,8 +404,22 @@ public: // Part of public interface to class. RegionBindingsRef bind(RegionBindingsConstRef B, Loc LV, SVal V); - // BindDefault is only used to initialize a region with a default value. - StoreRef BindDefault(Store store, const MemRegion *R, SVal V) override { + // BindDefaultInitial is only used to initialize a region with + // a default value. + StoreRef BindDefaultInitial(Store store, const MemRegion *R, + SVal V) override { + RegionBindingsRef B = getRegionBindings(store); + // Use other APIs when you have to wipe the region that was initialized + // earlier. + assert(!(B.getDefaultBinding(R) || B.getDirectBinding(R)) && + "Double initialization!"); + B = B.addBinding(BindingKey::Make(R, BindingKey::Default), V); + return StoreRef(B.asImmutableMap().getRootWithoutRetain(), *this); + } + + // BindDefaultZero is used for zeroing constructors that may accidentally + // overwrite existing bindings. + StoreRef BindDefaultZero(Store store, const MemRegion *R) override { // FIXME: The offsets of empty bases can be tricky because of // of the so called "empty base class optimization". // If a base class has been optimized out @@ -420,24 +429,14 @@ public: // Part of public interface to class. // and trying to infer them from offsets/alignments // seems to be error-prone and non-trivial because of the trailing padding. // As a temporary mitigation we don't create bindings for empty bases. - if (R->getKind() == MemRegion::CXXBaseObjectRegionKind && - cast<CXXBaseObjectRegion>(R)->getDecl()->isEmpty()) - return StoreRef(store, *this); + if (const auto *BR = dyn_cast<CXXBaseObjectRegion>(R)) + if (BR->getDecl()->isEmpty()) + return StoreRef(store, *this); RegionBindingsRef B = getRegionBindings(store); - assert(!B.lookup(R, BindingKey::Direct)); - - BindingKey Key = BindingKey::Make(R, BindingKey::Default); - if (B.lookup(Key)) { - const SubRegion *SR = cast<SubRegion>(R); - assert(SR->getAsOffset().getOffset() == - SR->getSuperRegion()->getAsOffset().getOffset() && - "A default value must come from a super-region"); - B = removeSubRegionBindings(B, SR); - } else { - B = B.addBinding(Key, V); - } - + SVal V = svalBuilder.makeZeroVal(Ctx.CharTy); + B = removeSubRegionBindings(B, cast<SubRegion>(R)); + B = B.addBinding(BindingKey::Make(R, BindingKey::Default), V); return StoreRef(B.asImmutableMap().getRootWithoutRetain(), *this); } @@ -474,7 +473,7 @@ public: // Part of public interface to class. const TypedRegion *R, SVal DefaultVal); - /// \brief Create a new store with the specified binding removed. + /// Create a new store with the specified binding removed. /// \param ST the original store, that is the basis for the new store. /// \param L the location whose binding should be removed. StoreRef killBinding(Store ST, Loc L) override; @@ -492,7 +491,7 @@ public: // Part of public interface to class. bool includedInBindings(Store store, const MemRegion *region) const override; - /// \brief Return the value bound to specified location in a given state. + /// Return the value bound to specified location in a given state. /// /// The high level logic for this method is this: /// getBinding (L) @@ -825,7 +824,7 @@ collectSubRegionBindings(SmallVectorImpl<BindingPair> &Bindings, FieldVector FieldsInSymbolicSubregions; if (TopKey.hasSymbolicOffset()) { getSymbolicOffsetFields(TopKey, FieldsInSymbolicSubregions); - Top = cast<SubRegion>(TopKey.getConcreteOffsetRegion()); + Top = TopKey.getConcreteOffsetRegion(); TopKey = BindingKey::Make(Top, BindingKey::Default); } @@ -871,7 +870,7 @@ collectSubRegionBindings(SmallVectorImpl<BindingPair> &Bindings, } else if (NextKey.hasSymbolicOffset()) { const MemRegion *Base = NextKey.getConcreteOffsetRegion(); - if (Top->isSubRegionOf(Base)) { + if (Top->isSubRegionOf(Base) && Top != Base) { // Case 3: The next key is symbolic and we just changed something within // its concrete region. We don't know if the binding is still valid, so // we'll be conservative and include it. @@ -881,7 +880,7 @@ collectSubRegionBindings(SmallVectorImpl<BindingPair> &Bindings, } else if (const SubRegion *BaseSR = dyn_cast<SubRegion>(Base)) { // Case 4: The next key is symbolic, but we changed a known // super-region. In this case the binding is certainly included. - if (Top == Base || BaseSR->isSubRegionOf(Top)) + if (BaseSR->isSubRegionOf(Top)) if (isCompatibleWithFields(NextKey, FieldsInSymbolicSubregions)) Bindings.push_back(*I); } @@ -1095,7 +1094,7 @@ void invalidateRegionsWorker::VisitCluster(const MemRegion *baseR, return; } - if (T->isStructureOrClassType()) { + if (T->isRecordType()) { // Invalidate the region by setting its default value to // conjured symbol. The type of the symbol is irrelevant. DefinedOrUnknownSVal V = svalBuilder.conjureSymbolVal(baseR, Ex, LCtx, @@ -1342,7 +1341,8 @@ RegionStoreManager::getSizeInElements(ProgramStateRef state, // If a variable is reinterpreted as a type that doesn't fit into a larger // type evenly, round it down. // This is a signed value, since it's used in arithmetic with signed indices. - return svalBuilder.makeIntVal(RegionSize / EleSize, false); + return svalBuilder.makeIntVal(RegionSize / EleSize, + svalBuilder.getArrayIndexType()); } //===----------------------------------------------------------------------===// @@ -1401,12 +1401,12 @@ SVal RegionStoreManager::getBinding(RegionBindingsConstRef B, Loc L, QualType T) T = TR->getLocationType()->getPointeeType(); else if (const SymbolicRegion *SR = dyn_cast<SymbolicRegion>(MR)) T = SR->getSymbol()->getType()->getPointeeType(); - else if (isa<AllocaRegion>(MR)) - T = Ctx.VoidTy; } assert(!T.isNull() && "Unable to auto-detect binding type!"); assert(!T->isVoidType() && "Attempting to dereference a void pointer!"); MR = GetElementZeroRegion(cast<SubRegion>(MR), T); + } else { + T = cast<TypedValueRegion>(MR)->getValueType(); } // FIXME: Perhaps this method should just take a 'const MemRegion*' argument @@ -1446,7 +1446,7 @@ SVal RegionStoreManager::getBinding(RegionBindingsConstRef B, Loc L, QualType T) return UnknownVal(); if (const FieldRegion* FR = dyn_cast<FieldRegion>(R)) - return CastRetrievedVal(getBindingForField(B, FR), FR, T, false); + return CastRetrievedVal(getBindingForField(B, FR), FR, T); if (const ElementRegion* ER = dyn_cast<ElementRegion>(R)) { // FIXME: Here we actually perform an implicit conversion from the loaded @@ -1454,7 +1454,7 @@ SVal RegionStoreManager::getBinding(RegionBindingsConstRef B, Loc L, QualType T) // more intelligently. For example, an 'element' can encompass multiple // bound regions (e.g., several bound bytes), or could be a subset of // a larger value. - return CastRetrievedVal(getBindingForElement(B, ER), ER, T, false); + return CastRetrievedVal(getBindingForElement(B, ER), ER, T); } if (const ObjCIvarRegion *IVR = dyn_cast<ObjCIvarRegion>(R)) { @@ -1464,7 +1464,7 @@ SVal RegionStoreManager::getBinding(RegionBindingsConstRef B, Loc L, QualType T) // reinterpretted, it is possible we stored a different value that could // fit within the ivar. Either we need to cast these when storing them // or reinterpret them lazily (as we do here). - return CastRetrievedVal(getBindingForObjCIvar(B, IVR), IVR, T, false); + return CastRetrievedVal(getBindingForObjCIvar(B, IVR), IVR, T); } if (const VarRegion *VR = dyn_cast<VarRegion>(R)) { @@ -1474,7 +1474,7 @@ SVal RegionStoreManager::getBinding(RegionBindingsConstRef B, Loc L, QualType T) // variable is reinterpretted, it is possible we stored a different value // that could fit within the variable. Either we need to cast these when // storing them or reinterpret them lazily (as we do here). - return CastRetrievedVal(getBindingForVar(B, VR), VR, T, false); + return CastRetrievedVal(getBindingForVar(B, VR), VR, T); } const SVal *V = B.lookup(R, BindingKey::Direct); @@ -1606,7 +1606,7 @@ SVal RegionStoreManager::getBindingForElement(RegionBindingsConstRef B, const MemRegion* superR = R->getSuperRegion(); // Check if the region is an element region of a string literal. - if (const StringRegion *StrR=dyn_cast<StringRegion>(superR)) { + if (const StringRegion *StrR = dyn_cast<StringRegion>(superR)) { // FIXME: Handle loads from strings where the literal is treated as // an integer, e.g., *((unsigned int*)"hello") QualType T = Ctx.getAsArrayType(StrR->getValueType())->getElementType(); @@ -1629,6 +1629,36 @@ SVal RegionStoreManager::getBindingForElement(RegionBindingsConstRef B, char c = (i >= length) ? '\0' : Str->getCodeUnit(i); return svalBuilder.makeIntVal(c, T); } + } else if (const VarRegion *VR = dyn_cast<VarRegion>(superR)) { + // Check if the containing array is const and has an initialized value. + 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 auto *InitList = dyn_cast<InitListExpr>(Init)) { + // The array index has to be known. + if (auto CI = R->getIndex().getAs<nonloc::ConcreteInt>()) { + int64_t i = CI->getValue().getSExtValue(); + // If it is known that the index is out of bounds, we can return + // an undefined value. + if (i < 0) + return UndefinedVal(); + + if (auto CAT = Ctx.getAsConstantArrayType(VD->getType())) + if (CAT->getSize().sle(i)) + return UndefinedVal(); + + // If there is a list, but no init, it must be zero. + if (i >= InitList->getNumInits()) + return svalBuilder.makeZeroVal(R->getElementType()); + + if (const Expr *ElemInit = InitList->getInit(i)) + if (Optional<SVal> V = svalBuilder.getConstantVal(ElemInit)) + return *V; + } + } + } + } } // Check for loads from a code text region. For such loads, just give up. @@ -1678,7 +1708,34 @@ SVal RegionStoreManager::getBindingForField(RegionBindingsConstRef B, if (const Optional<SVal> &V = B.getDirectBinding(R)) return *V; - QualType Ty = R->getValueType(); + // Is the field declared constant and has an in-class initializer? + const FieldDecl *FD = R->getDecl(); + QualType Ty = FD->getType(); + if (Ty.isConstQualified()) + if (const Expr *Init = FD->getInClassInitializer()) + if (Optional<SVal> V = svalBuilder.getConstantVal(Init)) + return *V; + + // If the containing record was initialized, try to get its constant value. + const MemRegion* superR = R->getSuperRegion(); + if (const auto *VR = dyn_cast<VarRegion>(superR)) { + const VarDecl *VD = VR->getDecl(); + QualType RecordVarTy = VD->getType(); + unsigned Index = FD->getFieldIndex(); + // Either the record variable or the field has to be const qualified. + if (RecordVarTy.isConstQualified() || Ty.isConstQualified()) + if (const Expr *Init = VD->getInit()) + if (const auto *InitList = dyn_cast<InitListExpr>(Init)) { + if (Index < InitList->getNumInits()) { + if (const Expr *FieldInit = InitList->getInit(Index)) + if (Optional<SVal> V = svalBuilder.getConstantVal(FieldInit)) + return *V; + } else { + return svalBuilder.makeZeroVal(Ty); + } + } + } + return getBindingForFieldOrElementCommon(B, R, Ty); } @@ -1776,7 +1833,7 @@ RegionStoreManager::getBindingForFieldOrElementCommon(RegionBindingsConstRef B, // quickly result in a warning. bool hasPartialLazyBinding = false; - const SubRegion *SR = dyn_cast<SubRegion>(R); + const SubRegion *SR = R; while (SR) { const MemRegion *Base = SR->getSuperRegion(); if (Optional<SVal> D = getBindingForDerivedDefaultValue(B, Base, R, Ty)) { @@ -2050,6 +2107,9 @@ RegionStoreManager::bind(RegionBindingsConstRef B, Loc L, SVal V) { R = GetElementZeroRegion(SR, T); } + assert((!isa<CXXThisRegion>(R) || !B.lookup(R)) && + "'this' pointer is not an l-value and is not assignable"); + // Clear out bindings that may overlap with this binding. RegionBindingsRef NewB = removeSubRegionBindings(B, cast<SubRegion>(R)); return NewB.addBinding(BindingKey::Make(R, BindingKey::Direct), V); diff --git a/lib/StaticAnalyzer/Core/SMTConstraintManager.cpp b/lib/StaticAnalyzer/Core/SMTConstraintManager.cpp new file mode 100644 index 000000000000..d379562bf325 --- /dev/null +++ b/lib/StaticAnalyzer/Core/SMTConstraintManager.cpp @@ -0,0 +1,181 @@ +//== 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" +#include "clang/Basic/TargetInfo.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" + +using namespace clang; +using namespace ento; + +ProgramStateRef SMTConstraintManager::assumeSym(ProgramStateRef State, + SymbolRef Sym, + bool Assumption) { + ASTContext &Ctx = getBasicVals().getContext(); + + QualType RetTy; + bool hasComparison; + + SMTExprRef Exp = Solver->getExpr(Ctx, Sym, &RetTy, &hasComparison); + + // Create zero comparison for implicit boolean cast, with reversed assumption + if (!hasComparison && !RetTy->isBooleanType()) + return assumeExpr(State, Sym, + Solver->getZeroExpr(Ctx, Exp, RetTy, !Assumption)); + + return assumeExpr(State, Sym, Assumption ? Exp : Solver->mkNot(Exp)); +} + +ProgramStateRef SMTConstraintManager::assumeSymInclusiveRange( + ProgramStateRef State, SymbolRef Sym, const llvm::APSInt &From, + const llvm::APSInt &To, bool InRange) { + ASTContext &Ctx = getBasicVals().getContext(); + return assumeExpr(State, Sym, + Solver->getRangeExpr(Ctx, Sym, From, To, InRange)); +} + +ProgramStateRef +SMTConstraintManager::assumeSymUnsupported(ProgramStateRef State, SymbolRef Sym, + bool Assumption) { + // Skip anything that is unsupported + return State; +} + +ConditionTruthVal SMTConstraintManager::checkNull(ProgramStateRef State, + SymbolRef Sym) { + ASTContext &Ctx = getBasicVals().getContext(); + + QualType RetTy; + // The expression may be casted, so we cannot call getZ3DataExpr() directly + SMTExprRef VarExp = Solver->getExpr(Ctx, Sym, &RetTy); + SMTExprRef Exp = Solver->getZeroExpr(Ctx, VarExp, RetTy, /*Assumption=*/true); + + // Negate the constraint + SMTExprRef NotExp = + Solver->getZeroExpr(Ctx, VarExp, RetTy, /*Assumption=*/false); + + Solver->reset(); + addStateConstraints(State); + + Solver->push(); + Solver->addConstraint(Exp); + ConditionTruthVal isSat = Solver->check(); + + Solver->pop(); + Solver->addConstraint(NotExp); + ConditionTruthVal isNotSat = Solver->check(); + + // Zero is the only possible solution + if (isSat.isConstrainedTrue() && isNotSat.isConstrainedFalse()) + return true; + + // Zero is not a solution + if (isSat.isConstrainedFalse() && isNotSat.isConstrainedTrue()) + return false; + + // Zero may be a solution + return ConditionTruthVal(); +} + +const llvm::APSInt *SMTConstraintManager::getSymVal(ProgramStateRef State, + SymbolRef Sym) const { + BasicValueFactory &BVF = getBasicVals(); + ASTContext &Ctx = BVF.getContext(); + + if (const SymbolData *SD = dyn_cast<SymbolData>(Sym)) { + QualType Ty = Sym->getType(); + assert(!Ty->isRealFloatingType()); + llvm::APSInt Value(Ctx.getTypeSize(Ty), + !Ty->isSignedIntegerOrEnumerationType()); + + SMTExprRef Exp = + Solver->fromData(SD->getSymbolID(), Ty, Ctx.getTypeSize(Ty)); + + Solver->reset(); + addStateConstraints(State); + + // Constraints are unsatisfiable + ConditionTruthVal isSat = Solver->check(); + if (!isSat.isConstrainedTrue()) + return nullptr; + + // Model does not assign interpretation + if (!Solver->getInterpretation(Exp, Value)) + return nullptr; + + // A value has been obtained, check if it is the only value + SMTExprRef NotExp = Solver->fromBinOp( + Exp, BO_NE, + Ty->isBooleanType() ? Solver->fromBoolean(Value.getBoolValue()) + : Solver->fromAPSInt(Value), + false); + + Solver->addConstraint(NotExp); + + ConditionTruthVal isNotSat = Solver->check(); + if (isNotSat.isConstrainedTrue()) + return nullptr; + + // This is the only solution, store it + return &BVF.getValue(Value); + } + + if (const SymbolCast *SC = dyn_cast<SymbolCast>(Sym)) { + SymbolRef CastSym = SC->getOperand(); + QualType CastTy = SC->getType(); + // Skip the void type + if (CastTy->isVoidType()) + return nullptr; + + const llvm::APSInt *Value; + if (!(Value = getSymVal(State, CastSym))) + return nullptr; + return &BVF.Convert(SC->getType(), *Value); + } + + if (const BinarySymExpr *BSE = dyn_cast<BinarySymExpr>(Sym)) { + const llvm::APSInt *LHS, *RHS; + if (const SymIntExpr *SIE = dyn_cast<SymIntExpr>(BSE)) { + LHS = getSymVal(State, SIE->getLHS()); + RHS = &SIE->getRHS(); + } else if (const IntSymExpr *ISE = dyn_cast<IntSymExpr>(BSE)) { + LHS = &ISE->getLHS(); + RHS = getSymVal(State, ISE->getRHS()); + } else if (const SymSymExpr *SSM = dyn_cast<SymSymExpr>(BSE)) { + // Early termination to avoid expensive call + LHS = getSymVal(State, SSM->getLHS()); + RHS = LHS ? getSymVal(State, SSM->getRHS()) : nullptr; + } else { + llvm_unreachable("Unsupported binary expression to get symbol value!"); + } + + if (!LHS || !RHS) + return nullptr; + + llvm::APSInt ConvertedLHS, ConvertedRHS; + QualType LTy, RTy; + std::tie(ConvertedLHS, LTy) = Solver->fixAPSInt(Ctx, *LHS); + std::tie(ConvertedRHS, RTy) = Solver->fixAPSInt(Ctx, *RHS); + Solver->doIntTypeConversion<llvm::APSInt, &SMTSolver::castAPSInt>( + Ctx, ConvertedLHS, LTy, ConvertedRHS, RTy); + return BVF.evalAPSInt(BSE->getOpcode(), ConvertedLHS, ConvertedRHS); + } + + llvm_unreachable("Unsupported expression to get symbol value!"); +} + +ConditionTruthVal +SMTConstraintManager::checkModel(ProgramStateRef State, + const SMTExprRef &Exp) const { + Solver->reset(); + Solver->addConstraint(Exp); + addStateConstraints(State); + return Solver->check(); +} diff --git a/lib/StaticAnalyzer/Core/SValBuilder.cpp b/lib/StaticAnalyzer/Core/SValBuilder.cpp index 04452e3e7cc2..f292dca8e99f 100644 --- a/lib/StaticAnalyzer/Core/SValBuilder.cpp +++ b/lib/StaticAnalyzer/Core/SValBuilder.cpp @@ -1,4 +1,4 @@ -// SValBuilder.cpp - Basic class for all SValBuilder implementations -*- C++ -*- +//===- SValBuilder.cpp - Basic class for all SValBuilder implementations --===// // // The LLVM Compiler Infrastructure // @@ -13,12 +13,33 @@ //===----------------------------------------------------------------------===// #include "clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h" +#include "clang/AST/ASTContext.h" +#include "clang/AST/Decl.h" #include "clang/AST/DeclCXX.h" #include "clang/AST/ExprCXX.h" +#include "clang/AST/ExprObjC.h" +#include "clang/AST/Stmt.h" +#include "clang/AST/Type.h" +#include "clang/Basic/LLVM.h" +#include "clang/Analysis/AnalysisDeclContext.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/APSIntType.h" #include "clang/StaticAnalyzer/Core/PathSensitive/BasicValueFactory.h" #include "clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState_Fwd.h" #include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/Store.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/SubEngine.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/SymExpr.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h" +#include "llvm/ADT/APSInt.h" +#include "llvm/ADT/None.h" +#include "llvm/ADT/Optional.h" +#include "llvm/Support/Casting.h" +#include "llvm/Support/Compiler.h" +#include <cassert> +#include <tuple> using namespace clang; using namespace ento; @@ -27,7 +48,7 @@ using namespace ento; // Basic SVal creation. //===----------------------------------------------------------------------===// -void SValBuilder::anchor() { } +void SValBuilder::anchor() {} DefinedOrUnknownSVal SValBuilder::makeZeroVal(QualType type) { if (Loc::isLocType(type)) @@ -95,12 +116,12 @@ nonloc::ConcreteInt SValBuilder::makeBoolVal(const CXXBoolLiteralExpr *boolean){ } DefinedOrUnknownSVal -SValBuilder::getRegionValueSymbolVal(const TypedValueRegion* region) { +SValBuilder::getRegionValueSymbolVal(const TypedValueRegion *region) { QualType T = region->getValueType(); if (T->isNullPtrType()) return makeZeroVal(T); - + if (!SymbolManager::canSymbolicate(T)) return UnknownVal(); @@ -149,7 +170,6 @@ DefinedOrUnknownSVal SValBuilder::conjureSymbolVal(const void *symbolTag, return nonloc::SymbolVal(sym); } - DefinedOrUnknownSVal SValBuilder::conjureSymbolVal(const Stmt *stmt, const LocationContext *LCtx, QualType type, @@ -217,10 +237,10 @@ SValBuilder::getDerivedRegionValueSymbolVal(SymbolRef parentSymbol, return nonloc::SymbolVal(sym); } -DefinedSVal SValBuilder::getMemberPointer(const DeclaratorDecl* DD) { +DefinedSVal SValBuilder::getMemberPointer(const DeclaratorDecl *DD) { assert(!DD || isa<CXXMethodDecl>(DD) || isa<FieldDecl>(DD)); - if (auto *MD = dyn_cast_or_null<CXXMethodDecl>(DD)) { + if (const auto *MD = dyn_cast_or_null<CXXMethodDecl>(DD)) { // Sema treats pointers to static member functions as have function pointer // type, so return a function pointer for the method. // We don't need to play a similar trick for static member fields @@ -277,19 +297,19 @@ Optional<SVal> SValBuilder::getConstantVal(const Expr *E) { return makeZeroVal(E->getType()); case Stmt::ObjCStringLiteralClass: { - const ObjCStringLiteral *SL = cast<ObjCStringLiteral>(E); + const auto *SL = cast<ObjCStringLiteral>(E); return makeLoc(getRegionManager().getObjCStringRegion(SL)); } case Stmt::StringLiteralClass: { - const StringLiteral *SL = cast<StringLiteral>(E); + const auto *SL = cast<StringLiteral>(E); return makeLoc(getRegionManager().getStringRegion(SL)); } // Fast-path some expressions to avoid the overhead of going through the AST's // constant evaluator case Stmt::CharacterLiteralClass: { - const CharacterLiteral *C = cast<CharacterLiteral>(E); + const auto *C = cast<CharacterLiteral>(E); return makeIntVal(C->getValue(), C->getType()); } @@ -297,7 +317,7 @@ Optional<SVal> SValBuilder::getConstantVal(const Expr *E) { return makeBoolVal(cast<CXXBoolLiteralExpr>(E)); case Stmt::TypeTraitExprClass: { - const TypeTraitExpr *TE = cast<TypeTraitExpr>(E); + const auto *TE = cast<TypeTraitExpr>(E); return makeTruthVal(TE->getValue(), TE->getType()); } @@ -310,12 +330,19 @@ Optional<SVal> SValBuilder::getConstantVal(const Expr *E) { case Stmt::CXXNullPtrLiteralExprClass: return makeNull(); + case Stmt::CStyleCastExprClass: + case Stmt::CXXFunctionalCastExprClass: + case Stmt::CXXConstCastExprClass: + case Stmt::CXXReinterpretCastExprClass: + case Stmt::CXXStaticCastExprClass: case Stmt::ImplicitCastExprClass: { - const CastExpr *CE = cast<CastExpr>(E); + const auto *CE = cast<CastExpr>(E); switch (CE->getCastKind()) { default: break; case CK_ArrayToPointerDecay: + case CK_IntegralToPointer: + case CK_NoOp: case CK_BitCast: { const Expr *SE = CE->getSubExpr(); Optional<SVal> Val = getConstantVal(SE); @@ -348,20 +375,18 @@ Optional<SVal> SValBuilder::getConstantVal(const Expr *E) { } } -//===----------------------------------------------------------------------===// - SVal SValBuilder::makeSymExprValNN(ProgramStateRef State, BinaryOperator::Opcode Op, NonLoc LHS, NonLoc RHS, QualType ResultTy) { - if (!State->isTainted(RHS) && !State->isTainted(LHS)) - return UnknownVal(); - const SymExpr *symLHS = LHS.getAsSymExpr(); const SymExpr *symRHS = RHS.getAsSymExpr(); + // TODO: When the Max Complexity is reached, we should conjure a symbol // instead of generating an Unknown value and propagate the taint info to it. - const unsigned MaxComp = 10000; // 100000 28X + const unsigned MaxComp = StateMgr.getOwningEngine() + ->getAnalysisManager() + .options.getMaxSymbolComplexity(); if (symLHS && symRHS && (symLHS->computeComplexity() + symRHS->computeComplexity()) < MaxComp) @@ -378,10 +403,8 @@ SVal SValBuilder::makeSymExprValNN(ProgramStateRef State, return UnknownVal(); } - SVal SValBuilder::evalBinOp(ProgramStateRef state, BinaryOperator::Opcode op, SVal lhs, SVal rhs, QualType type) { - if (lhs.isUndef() || rhs.isUndef()) return UndefinedVal(); @@ -413,10 +436,19 @@ SVal SValBuilder::evalBinOp(ProgramStateRef state, BinaryOperator::Opcode op, type); } +ConditionTruthVal SValBuilder::areEqual(ProgramStateRef state, SVal lhs, + SVal rhs) { + return state->isNonNull(evalEQ(state, lhs, rhs)); +} + +SVal SValBuilder::evalEQ(ProgramStateRef state, SVal lhs, SVal rhs) { + return evalBinOp(state, BO_EQ, lhs, rhs, getConditionType()); +} + DefinedOrUnknownSVal SValBuilder::evalEQ(ProgramStateRef state, DefinedOrUnknownSVal lhs, DefinedOrUnknownSVal rhs) { - return evalBinOp(state, BO_EQ, lhs, rhs, getConditionType()) + return evalEQ(state, static_cast<SVal>(lhs), static_cast<SVal>(rhs)) .castAs<DefinedOrUnknownSVal>(); } @@ -425,7 +457,7 @@ DefinedOrUnknownSVal SValBuilder::evalEQ(ProgramStateRef state, /// Assumes the input types are canonical. static bool shouldBeModeledWithNoOp(ASTContext &Context, QualType ToTy, QualType FromTy) { - while (Context.UnwrapSimilarPointerTypes(ToTy, FromTy)) { + while (Context.UnwrapSimilarTypes(ToTy, FromTy)) { Qualifiers Quals1, Quals2; ToTy = Context.getUnqualifiedArrayType(ToTy, Quals1); FromTy = Context.getUnqualifiedArrayType(FromTy, Quals2); @@ -440,6 +472,10 @@ static bool shouldBeModeledWithNoOp(ASTContext &Context, QualType ToTy, // If we are casting to void, the 'From' value can be used to represent the // 'To' value. + // + // FIXME: Doing this after unwrapping the types doesn't make any sense. A + // cast from 'int**' to 'void**' is not special in the way that a cast from + // 'int*' to 'void*' is. if (ToTy->isVoidType()) return true; @@ -454,7 +490,6 @@ static bool shouldBeModeledWithNoOp(ASTContext &Context, QualType ToTy, // of the original value is known to be greater than the max of the target type. SVal SValBuilder::evalIntegralCast(ProgramStateRef state, SVal val, QualType castTy, QualType originalTy) { - // No truncations if target type is big enough. if (getContext().getTypeSize(castTy) >= getContext().getTypeSize(originalTy)) return evalCast(val, castTy, originalTy); @@ -548,8 +583,8 @@ SVal SValBuilder::evalCast(SVal val, QualType castTy, QualType originalTy) { } // Check for casts from array type to another type. - if (const ArrayType *arrayT = - dyn_cast<ArrayType>(originalTy.getCanonicalType())) { + if (const auto *arrayT = + dyn_cast<ArrayType>(originalTy.getCanonicalType())) { // We will always decay to a pointer. QualType elemTy = arrayT->getElementType(); val = StateMgr.ArrayToPointer(val.castAs<Loc>(), elemTy); diff --git a/lib/StaticAnalyzer/Core/SVals.cpp b/lib/StaticAnalyzer/Core/SVals.cpp index a83421426a13..559ca2c9840d 100644 --- a/lib/StaticAnalyzer/Core/SVals.cpp +++ b/lib/StaticAnalyzer/Core/SVals.cpp @@ -1,4 +1,4 @@ -//= RValues.cpp - Abstract RValues for Path-Sens. Value Tracking -*- C++ -*-==// +//===- RValues.cpp - Abstract RValues for Path-Sens. Value Tracking -------===// // // The LLVM Compiler Infrastructure // @@ -12,20 +12,31 @@ // //===----------------------------------------------------------------------===// -#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" -#include "clang/AST/ExprObjC.h" -#include "clang/Basic/IdentifierTable.h" -#include "llvm/Support/raw_ostream.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h" +#include "clang/AST/Decl.h" #include "clang/AST/DeclCXX.h" +#include "clang/AST/Expr.h" +#include "clang/AST/Type.h" +#include "clang/Basic/LLVM.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/BasicValueFactory.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/SymExpr.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h" +#include "llvm/ADT/Optional.h" +#include "llvm/Support/Casting.h" +#include "llvm/Support/Compiler.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/raw_ostream.h" +#include <cassert> + using namespace clang; using namespace ento; -using llvm::APSInt; //===----------------------------------------------------------------------===// // Symbol iteration within an SVal. //===----------------------------------------------------------------------===// - //===----------------------------------------------------------------------===// // Utility methods. //===----------------------------------------------------------------------===// @@ -39,7 +50,7 @@ bool SVal::hasConjuredSymbol() const { if (Optional<loc::MemRegionVal> RV = getAs<loc::MemRegionVal>()) { const MemRegion *R = RV->getRegion(); - if (const SymbolicRegion *SR = dyn_cast<SymbolicRegion>(R)) { + if (const auto *SR = dyn_cast<SymbolicRegion>(R)) { SymbolRef sym = SR->getSymbol(); if (isa<SymbolConjured>(sym)) return true; @@ -53,18 +64,18 @@ const FunctionDecl *SVal::getAsFunctionDecl() const { if (Optional<loc::MemRegionVal> X = getAs<loc::MemRegionVal>()) { const MemRegion* R = X->getRegion(); if (const FunctionCodeRegion *CTR = R->getAs<FunctionCodeRegion>()) - if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(CTR->getDecl())) + if (const auto *FD = dyn_cast<FunctionDecl>(CTR->getDecl())) return FD; } if (auto X = getAs<nonloc::PointerToMember>()) { - if (const CXXMethodDecl *MD = dyn_cast_or_null<CXXMethodDecl>(X->getDecl())) + if (const auto *MD = dyn_cast_or_null<CXXMethodDecl>(X->getDecl())) return MD; } return nullptr; } -/// \brief If this SVal is a location (subclasses Loc) and wraps a symbol, +/// If this SVal is a location (subclasses Loc) and wraps a symbol, /// return that SymbolRef. Otherwise return 0. /// /// Implicit casts (ex: void* -> char*) can turn Symbolic region into Element @@ -95,8 +106,8 @@ SymbolRef SVal::getLocSymbolInBase() const { const MemRegion *R = X->getRegion(); - while (const SubRegion *SR = dyn_cast<SubRegion>(R)) { - if (const SymbolicRegion *SymR = dyn_cast<SymbolicRegion>(SR)) + while (const auto *SR = dyn_cast<SubRegion>(R)) { + if (const auto *SymR = dyn_cast<SymbolicRegion>(SR)) return SymR->getSymbol(); else R = SR->getSuperRegion(); @@ -107,7 +118,7 @@ SymbolRef SVal::getLocSymbolInBase() const { // TODO: The next 3 functions have to be simplified. -/// \brief If this SVal wraps a symbol return that SymbolRef. +/// If this SVal wraps a symbol return that SymbolRef. /// Otherwise, return 0. /// /// Casts are ignored during lookup. @@ -189,14 +200,14 @@ nonloc::CompoundVal::iterator nonloc::CompoundVal::end() const { nonloc::PointerToMember::iterator nonloc::PointerToMember::begin() const { const PTMDataType PTMD = getPTMData(); if (PTMD.is<const DeclaratorDecl *>()) - return nonloc::PointerToMember::iterator(); + return {}; return PTMD.get<const PointerToMemberData *>()->begin(); } nonloc::PointerToMember::iterator nonloc::PointerToMember::end() const { const PTMDataType PTMD = getPTMData(); if (PTMD.is<const DeclaratorDecl *>()) - return nonloc::PointerToMember::iterator(); + return {}; return PTMD.get<const PointerToMemberData *>()->end(); } @@ -220,7 +231,6 @@ bool SVal::isZeroConstant() const { return isConstant(0); } - //===----------------------------------------------------------------------===// // Transfer function dispatch for Non-Locs. //===----------------------------------------------------------------------===// @@ -254,7 +264,6 @@ nonloc::ConcreteInt::evalMinus(SValBuilder &svalBuilder) const { SVal loc::ConcreteInt::evalBinOp(BasicValueFactory& BasicVals, BinaryOperator::Opcode Op, const loc::ConcreteInt& R) const { - assert(BinaryOperator::isComparisonOp(Op) || Op == BO_Sub); const llvm::APSInt *X = BasicVals.evalAPSInt(Op, getValue(), R.getValue()); @@ -291,19 +300,15 @@ void SVal::dumpToStream(raw_ostream &os) const { void NonLoc::dumpToStream(raw_ostream &os) const { switch (getSubKind()) { case nonloc::ConcreteIntKind: { - const nonloc::ConcreteInt& C = castAs<nonloc::ConcreteInt>(); - if (C.getValue().isUnsigned()) - os << C.getValue().getZExtValue(); - else - os << C.getValue().getSExtValue(); - os << ' ' << (C.getValue().isUnsigned() ? 'U' : 'S') - << C.getValue().getBitWidth() << 'b'; + const auto &Value = castAs<nonloc::ConcreteInt>().getValue(); + os << Value << ' ' << (Value.isSigned() ? 'S' : 'U') + << Value.getBitWidth() << 'b'; break; } - case nonloc::SymbolValKind: { + case nonloc::SymbolValKind: os << castAs<nonloc::SymbolVal>().getSymbol(); break; - } + case nonloc::LocAsIntegerKind: { const nonloc::LocAsInteger& C = castAs<nonloc::LocAsInteger>(); os << C.getLoc() << " [as " << C.getNumBits() << " bit integer]"; @@ -313,14 +318,14 @@ void NonLoc::dumpToStream(raw_ostream &os) const { const nonloc::CompoundVal& C = castAs<nonloc::CompoundVal>(); os << "compoundVal{"; bool first = true; - for (nonloc::CompoundVal::iterator I=C.begin(), E=C.end(); I!=E; ++I) { + for (const auto &I : C) { if (first) { os << ' '; first = false; } else os << ", "; - (*I).dumpToStream(os); + I.dumpToStream(os); } os << "}"; break; @@ -353,7 +358,7 @@ void NonLoc::dumpToStream(raw_ostream &os) const { break; } default: - assert (false && "Pretty-printed not implemented for this NonLoc."); + assert(false && "Pretty-printed not implemented for this NonLoc."); break; } } diff --git a/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp b/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp index 94d29d5a6ba3..beae0dfae289 100644 --- a/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp +++ b/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp @@ -12,8 +12,10 @@ //===----------------------------------------------------------------------===// #include "clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h" #include "clang/StaticAnalyzer/Core/PathSensitive/APSIntType.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/SubEngine.h" #include "clang/StaticAnalyzer/Core/PathSensitive/SValVisitor.h" using namespace clang; @@ -157,7 +159,8 @@ SVal SimpleSValBuilder::evalCastFromLoc(Loc val, QualType castTy) { return nonloc::SymbolVal(SymMgr.getExtentSymbol(FTR)); if (const SymbolicRegion *SymR = R->getSymbolicBase()) - return nonloc::SymbolVal(SymR->getSymbol()); + return makeNonLoc(SymR->getSymbol(), BO_NE, + BasicVals.getZeroWithPtrWidth(), castTy); // FALL-THROUGH LLVM_FALLTHROUGH; @@ -307,6 +310,197 @@ SVal SimpleSValBuilder::MakeSymIntVal(const SymExpr *LHS, return makeNonLoc(LHS, op, *ConvertedRHS, resultTy); } +// See if Sym is known to be a relation Rel with Bound. +static bool isInRelation(BinaryOperator::Opcode Rel, SymbolRef Sym, + llvm::APSInt Bound, ProgramStateRef State) { + SValBuilder &SVB = State->getStateManager().getSValBuilder(); + SVal Result = + SVB.evalBinOpNN(State, Rel, nonloc::SymbolVal(Sym), + nonloc::ConcreteInt(Bound), SVB.getConditionType()); + if (auto DV = Result.getAs<DefinedSVal>()) { + return !State->assume(*DV, false); + } + return false; +} + +// See if Sym is known to be within [min/4, max/4], where min and max +// are the bounds of the symbol's integral type. With such symbols, +// some manipulations can be performed without the risk of overflow. +// assume() doesn't cause infinite recursion because we should be dealing +// with simpler symbols on every recursive call. +static bool isWithinConstantOverflowBounds(SymbolRef Sym, + ProgramStateRef State) { + SValBuilder &SVB = State->getStateManager().getSValBuilder(); + BasicValueFactory &BV = SVB.getBasicValueFactory(); + + QualType T = Sym->getType(); + assert(T->isSignedIntegerOrEnumerationType() && + "This only works with signed integers!"); + APSIntType AT = BV.getAPSIntType(T); + + llvm::APSInt Max = AT.getMaxValue() / AT.getValue(4), Min = -Max; + return isInRelation(BO_LE, Sym, Max, State) && + isInRelation(BO_GE, Sym, Min, State); +} + +// Same for the concrete integers: see if I is within [min/4, max/4]. +static bool isWithinConstantOverflowBounds(llvm::APSInt I) { + APSIntType AT(I); + assert(!AT.isUnsigned() && + "This only works with signed integers!"); + + llvm::APSInt Max = AT.getMaxValue() / AT.getValue(4), Min = -Max; + return (I <= Max) && (I >= -Max); +} + +static std::pair<SymbolRef, llvm::APSInt> +decomposeSymbol(SymbolRef Sym, BasicValueFactory &BV) { + if (const auto *SymInt = dyn_cast<SymIntExpr>(Sym)) + if (BinaryOperator::isAdditiveOp(SymInt->getOpcode())) + return std::make_pair(SymInt->getLHS(), + (SymInt->getOpcode() == BO_Add) ? + (SymInt->getRHS()) : + (-SymInt->getRHS())); + + // Fail to decompose: "reduce" the problem to the "$x + 0" case. + return std::make_pair(Sym, BV.getValue(0, Sym->getType())); +} + +// Simplify "(LSym + LInt) Op (RSym + RInt)" assuming all values are of the +// same signed integral type and no overflows occur (which should be checked +// by the caller). +static NonLoc doRearrangeUnchecked(ProgramStateRef State, + BinaryOperator::Opcode Op, + SymbolRef LSym, llvm::APSInt LInt, + SymbolRef RSym, llvm::APSInt RInt) { + SValBuilder &SVB = State->getStateManager().getSValBuilder(); + BasicValueFactory &BV = SVB.getBasicValueFactory(); + SymbolManager &SymMgr = SVB.getSymbolManager(); + + QualType SymTy = LSym->getType(); + assert(SymTy == RSym->getType() && + "Symbols are not of the same type!"); + assert(APSIntType(LInt) == BV.getAPSIntType(SymTy) && + "Integers are not of the same type as symbols!"); + assert(APSIntType(RInt) == BV.getAPSIntType(SymTy) && + "Integers are not of the same type as symbols!"); + + QualType ResultTy; + if (BinaryOperator::isComparisonOp(Op)) + ResultTy = SVB.getConditionType(); + else if (BinaryOperator::isAdditiveOp(Op)) + ResultTy = SymTy; + else + llvm_unreachable("Operation not suitable for unchecked rearrangement!"); + + // FIXME: Can we use assume() without getting into an infinite recursion? + if (LSym == RSym) + return SVB.evalBinOpNN(State, Op, nonloc::ConcreteInt(LInt), + nonloc::ConcreteInt(RInt), ResultTy) + .castAs<NonLoc>(); + + SymbolRef ResultSym = nullptr; + BinaryOperator::Opcode ResultOp; + llvm::APSInt ResultInt; + if (BinaryOperator::isComparisonOp(Op)) { + // Prefer comparing to a non-negative number. + // FIXME: Maybe it'd be better to have consistency in + // "$x - $y" vs. "$y - $x" because those are solver's keys. + if (LInt > RInt) { + ResultSym = SymMgr.getSymSymExpr(RSym, BO_Sub, LSym, SymTy); + ResultOp = BinaryOperator::reverseComparisonOp(Op); + ResultInt = LInt - RInt; // Opposite order! + } else { + ResultSym = SymMgr.getSymSymExpr(LSym, BO_Sub, RSym, SymTy); + ResultOp = Op; + ResultInt = RInt - LInt; // Opposite order! + } + } else { + ResultSym = SymMgr.getSymSymExpr(LSym, Op, RSym, SymTy); + ResultInt = (Op == BO_Add) ? (LInt + RInt) : (LInt - RInt); + ResultOp = BO_Add; + // Bring back the cosmetic difference. + if (ResultInt < 0) { + ResultInt = -ResultInt; + ResultOp = BO_Sub; + } else if (ResultInt == 0) { + // Shortcut: Simplify "$x + 0" to "$x". + return nonloc::SymbolVal(ResultSym); + } + } + const llvm::APSInt &PersistentResultInt = BV.getValue(ResultInt); + return nonloc::SymbolVal( + SymMgr.getSymIntExpr(ResultSym, ResultOp, PersistentResultInt, ResultTy)); +} + +// Rearrange if symbol type matches the result type and if the operator is a +// comparison operator, both symbol and constant must be within constant +// overflow bounds. +static bool shouldRearrange(ProgramStateRef State, BinaryOperator::Opcode Op, + SymbolRef Sym, llvm::APSInt Int, QualType Ty) { + return Sym->getType() == Ty && + (!BinaryOperator::isComparisonOp(Op) || + (isWithinConstantOverflowBounds(Sym, State) && + isWithinConstantOverflowBounds(Int))); +} + +static Optional<NonLoc> tryRearrange(ProgramStateRef State, + BinaryOperator::Opcode Op, NonLoc Lhs, + NonLoc Rhs, QualType ResultTy) { + ProgramStateManager &StateMgr = State->getStateManager(); + SValBuilder &SVB = StateMgr.getSValBuilder(); + + // We expect everything to be of the same type - this type. + QualType SingleTy; + + auto &Opts = + StateMgr.getOwningEngine()->getAnalysisManager().getAnalyzerOptions(); + + // FIXME: After putting complexity threshold to the symbols we can always + // rearrange additive operations but rearrange comparisons only if + // option is set. + if(!Opts.shouldAggressivelySimplifyBinaryOperation()) + return None; + + SymbolRef LSym = Lhs.getAsSymbol(); + if (!LSym) + return None; + + if (BinaryOperator::isComparisonOp(Op)) { + SingleTy = LSym->getType(); + if (ResultTy != SVB.getConditionType()) + return None; + // Initialize SingleTy later with a symbol's type. + } else if (BinaryOperator::isAdditiveOp(Op)) { + SingleTy = ResultTy; + if (LSym->getType() != SingleTy) + return None; + // Substracting unsigned integers is a nightmare. + if (!SingleTy->isSignedIntegerOrEnumerationType()) + return None; + } else { + // Don't rearrange other operations. + return None; + } + + assert(!SingleTy.isNull() && "We should have figured out the type by now!"); + + SymbolRef RSym = Rhs.getAsSymbol(); + if (!RSym || RSym->getType() != SingleTy) + return None; + + BasicValueFactory &BV = State->getBasicVals(); + llvm::APSInt LInt, RInt; + std::tie(LSym, LInt) = decomposeSymbol(LSym, BV); + std::tie(RSym, RInt) = decomposeSymbol(RSym, BV); + if (!shouldRearrange(State, Op, LSym, LInt, SingleTy) || + !shouldRearrange(State, Op, RSym, RInt, SingleTy)) + return None; + + // We know that no overflows can occur anymore. + return doRearrangeUnchecked(State, Op, LSym, LInt, RSym, RInt); +} + SVal SimpleSValBuilder::evalBinOpNN(ProgramStateRef state, BinaryOperator::Opcode op, NonLoc lhs, NonLoc rhs, @@ -559,6 +753,9 @@ SVal SimpleSValBuilder::evalBinOpNN(ProgramStateRef state, if (const llvm::APSInt *RHSValue = getKnownValue(state, rhs)) return MakeSymIntVal(Sym, op, *RHSValue, resultTy); + if (Optional<NonLoc> V = tryRearrange(state, op, lhs, rhs, resultTy)) + return *V; + // Give up -- this is not a symbolic expression we can handle. return makeSymExprValNN(state, op, InputLHS, InputRHS, resultTy); } @@ -988,6 +1185,12 @@ SVal SimpleSValBuilder::evalBinOpLN(ProgramStateRef state, elementType = resultTy->getPointeeType(); } + // Represent arithmetic on void pointers as arithmetic on char pointers. + // It is fine when a TypedValueRegion of char value type represents + // a void pointer. Note that arithmetic on void pointers is a GCC extension. + if (elementType->isVoidType()) + elementType = getContext().CharTy; + if (Optional<NonLoc> indexV = index.getAs<NonLoc>()) { return loc::MemRegionVal(MemMgr.getElementRegion(elementType, *indexV, superR, getContext())); @@ -1023,24 +1226,42 @@ SVal SimpleSValBuilder::simplifySVal(ProgramStateRef State, SVal V) { ProgramStateRef State; SValBuilder &SVB; + // Cache results for the lifetime of the Simplifier. Results change every + // time new constraints are added to the program state, which is the whole + // point of simplifying, and for that very reason it's pointless to maintain + // the same cache for the duration of the whole analysis. + llvm::DenseMap<SymbolRef, SVal> Cached; + + static bool isUnchanged(SymbolRef Sym, SVal Val) { + return Sym == Val.getAsSymbol(); + } + public: Simplifier(ProgramStateRef State) : State(State), SVB(State->getStateManager().getSValBuilder()) {} SVal VisitSymbolData(const SymbolData *S) { if (const llvm::APSInt *I = - SVB.getKnownValue(State, nonloc::SymbolVal(S))) + SVB.getKnownValue(State, SVB.makeSymbolVal(S))) return Loc::isLocType(S->getType()) ? (SVal)SVB.makeIntLocVal(*I) : (SVal)SVB.makeIntVal(*I); - return Loc::isLocType(S->getType()) ? (SVal)SVB.makeLoc(S) - : nonloc::SymbolVal(S); + return SVB.makeSymbolVal(S); } // TODO: Support SymbolCast. Support IntSymExpr when/if we actually // start producing them. SVal VisitSymIntExpr(const SymIntExpr *S) { + auto I = Cached.find(S); + if (I != Cached.end()) + return I->second; + SVal LHS = Visit(S->getLHS()); + if (isUnchanged(S->getLHS(), LHS)) { + SVal V = SVB.makeSymbolVal(S); + Cached[S] = V; + return V; + } SVal RHS; // By looking at the APSInt in the right-hand side of S, we cannot // figure out if it should be treated as a Loc or as a NonLoc. @@ -1059,13 +1280,27 @@ SVal SimpleSValBuilder::simplifySVal(ProgramStateRef State, SVal V) { } else { RHS = SVB.makeIntVal(S->getRHS()); } - return SVB.evalBinOp(State, S->getOpcode(), LHS, RHS, S->getType()); + + SVal V = SVB.evalBinOp(State, S->getOpcode(), LHS, RHS, S->getType()); + Cached[S] = V; + return V; } SVal VisitSymSymExpr(const SymSymExpr *S) { + auto I = Cached.find(S); + if (I != Cached.end()) + return I->second; + SVal LHS = Visit(S->getLHS()); SVal RHS = Visit(S->getRHS()); - return SVB.evalBinOp(State, S->getOpcode(), LHS, RHS, S->getType()); + if (isUnchanged(S->getLHS(), LHS) && isUnchanged(S->getRHS(), RHS)) { + SVal V = SVB.makeSymbolVal(S); + Cached[S] = V; + return V; + } + SVal V = SVB.evalBinOp(State, S->getOpcode(), LHS, RHS, S->getType()); + Cached[S] = V; + return V; } SVal VisitSymExpr(SymbolRef S) { return nonloc::SymbolVal(S); } @@ -1075,13 +1310,20 @@ SVal SimpleSValBuilder::simplifySVal(ProgramStateRef State, SVal V) { SVal VisitNonLocSymbolVal(nonloc::SymbolVal V) { // Simplification is much more costly than computing complexity. // For high complexity, it may be not worth it. - if (V.getSymbol()->computeComplexity() > 100) - return V; return Visit(V.getSymbol()); } SVal VisitSVal(SVal V) { return V; } }; - return Simplifier(State).Visit(V); + // A crude way of preventing this function from calling itself from evalBinOp. + static bool isReentering = false; + if (isReentering) + return V; + + isReentering = true; + SVal SimplifiedV = Simplifier(State).Visit(V); + isReentering = false; + + return SimplifiedV; } diff --git a/lib/StaticAnalyzer/Core/Store.cpp b/lib/StaticAnalyzer/Core/Store.cpp index 173fdd8d0056..5ab5c082269b 100644 --- a/lib/StaticAnalyzer/Core/Store.cpp +++ b/lib/StaticAnalyzer/Core/Store.cpp @@ -1,4 +1,4 @@ -//== Store.cpp - Interface for maps from Locations to Values ----*- C++ -*--==// +//===- Store.cpp - Interface for maps from Locations to Values ------------===// // // The LLVM Compiler Infrastructure // @@ -12,18 +12,37 @@ //===----------------------------------------------------------------------===// #include "clang/StaticAnalyzer/Core/PathSensitive/Store.h" +#include "clang/AST/ASTContext.h" #include "clang/AST/CXXInheritance.h" #include "clang/AST/CharUnits.h" +#include "clang/AST/Decl.h" +#include "clang/AST/DeclCXX.h" #include "clang/AST/DeclObjC.h" +#include "clang/AST/Expr.h" +#include "clang/AST/Type.h" +#include "clang/Basic/LLVM.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/BasicValueFactory.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/StoreRef.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/SymExpr.h" +#include "llvm/ADT/APSInt.h" +#include "llvm/ADT/Optional.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/Support/Casting.h" +#include "llvm/Support/ErrorHandling.h" +#include <cassert> +#include <cstdint> using namespace clang; using namespace ento; StoreManager::StoreManager(ProgramStateManager &stateMgr) - : svalBuilder(stateMgr.getSValBuilder()), StateMgr(stateMgr), - MRMgr(svalBuilder.getRegionManager()), Ctx(stateMgr.getContext()) {} + : svalBuilder(stateMgr.getSValBuilder()), StateMgr(stateMgr), + MRMgr(svalBuilder.getRegionManager()), Ctx(stateMgr.getContext()) {} StoreRef StoreManager::enterStackFrame(Store OldStore, const CallEvent &Call, @@ -33,11 +52,8 @@ StoreRef StoreManager::enterStackFrame(Store OldStore, SmallVector<CallEvent::FrameBindingTy, 16> InitialBindings; Call.getInitialStackFrameContents(LCtx, InitialBindings); - for (CallEvent::BindingsTy::iterator I = InitialBindings.begin(), - E = InitialBindings.end(); - I != E; ++I) { - Store = Bind(Store.getStore(), I->first, I->second); - } + for (const auto &I : InitialBindings) + Store = Bind(Store.getStore(), I.first, I.second); return Store; } @@ -49,10 +65,6 @@ const ElementRegion *StoreManager::MakeElementRegion(const SubRegion *Base, return MRMgr.getElementRegion(EleTy, idx, Base, svalBuilder.getContext()); } -StoreRef StoreManager::BindDefault(Store store, const MemRegion *R, SVal V) { - return StoreRef(store, *this); -} - const ElementRegion *StoreManager::GetElementZeroRegion(const SubRegion *R, QualType T) { NonLoc idx = svalBuilder.makeZeroArrayIndex(); @@ -61,7 +73,6 @@ const ElementRegion *StoreManager::GetElementZeroRegion(const SubRegion *R, } const MemRegion *StoreManager::castRegion(const MemRegion *R, QualType CastToTy) { - ASTContext &Ctx = StateMgr.getContext(); // Handle casts to Objective-C objects. @@ -92,7 +103,7 @@ const MemRegion *StoreManager::castRegion(const MemRegion *R, QualType CastToTy) // Handle casts from compatible types. if (R->isBoundable()) - if (const TypedValueRegion *TR = dyn_cast<TypedValueRegion>(R)) { + if (const auto *TR = dyn_cast<TypedValueRegion>(R)) { QualType ObjTy = Ctx.getCanonicalType(TR->getValueType()); if (CanonPointeeTy == ObjTy) return R; @@ -164,7 +175,7 @@ const MemRegion *StoreManager::castRegion(const MemRegion *R, QualType CastToTy) // Edge case: we are at 0 bytes off the beginning of baseR. We // check to see if type we are casting to is the same as the base // region. If so, just return the base region. - if (const TypedValueRegion *TR = dyn_cast<TypedValueRegion>(baseR)) { + if (const auto *TR = dyn_cast<TypedValueRegion>(baseR)) { QualType ObjTy = Ctx.getCanonicalType(TR->getValueType()); QualType CanonPointeeTy = Ctx.getCanonicalType(PointeeTy); if (CanonPointeeTy == ObjTy) @@ -219,7 +230,7 @@ static bool regionMatchesCXXRecordType(SVal V, QualType Ty) { if (!MR) return true; - const TypedValueRegion *TVR = dyn_cast<TypedValueRegion>(MR); + const auto *TVR = dyn_cast<TypedValueRegion>(MR); if (!TVR) return true; @@ -253,11 +264,9 @@ SVal StoreManager::evalDerivedToBase(SVal Derived, const CastExpr *Cast) { SVal StoreManager::evalDerivedToBase(SVal Derived, const CXXBasePath &Path) { // Walk through the path to create nested CXXBaseRegions. SVal Result = Derived; - for (CXXBasePath::const_iterator I = Path.begin(), E = Path.end(); - I != E; ++I) { - Result = evalDerivedToBase(Result, I->Base->getType(), - I->Base->isVirtual()); - } + for (const auto &I : Path) + Result = evalDerivedToBase(Result, I.Base->getType(), + I.Base->isVirtual()); return Result; } @@ -286,9 +295,9 @@ SVal StoreManager::evalDerivedToBase(SVal Derived, QualType BaseType, /// symbolic regions, where the dynamic type is merely bounded (and even then, /// only ostensibly!), but does not take advantage of any dynamic type info. static const CXXRecordDecl *getCXXRecordType(const MemRegion *MR) { - if (const TypedValueRegion *TVR = dyn_cast<TypedValueRegion>(MR)) + if (const auto *TVR = dyn_cast<TypedValueRegion>(MR)) return TVR->getValueType()->getAsCXXRecordDecl(); - if (const SymbolicRegion *SR = dyn_cast<SymbolicRegion>(MR)) + if (const auto *SR = dyn_cast<SymbolicRegion>(MR)) return SR->getSymbol()->getType()->getPointeeCXXRecordDecl(); return nullptr; } @@ -327,7 +336,7 @@ SVal StoreManager::attemptDownCast(SVal Base, QualType TargetType, return evalDerivedToBase(loc::MemRegionVal(MR), Paths.front()); } - if (const CXXBaseObjectRegion *BaseR = dyn_cast<CXXBaseObjectRegion>(MR)) { + if (const auto *BaseR = dyn_cast<CXXBaseObjectRegion>(MR)) { // Drill down the chain to get the derived classes. MR = BaseR->getSuperRegion(); continue; @@ -348,7 +357,7 @@ SVal StoreManager::attemptDownCast(SVal Base, QualType TargetType, const MemRegion *Uncasted = MR->StripCasts(/*IncludeBaseCasts=*/false); if (Uncasted == MR) { // We reached the bottom of the hierarchy and did not find the derived - // class. We we must be casting the base to derived, so the cast should + // class. We must be casting the base to derived, so the cast should // fail. break; } @@ -361,27 +370,27 @@ SVal StoreManager::attemptDownCast(SVal Base, QualType TargetType, return UnknownVal(); } - /// CastRetrievedVal - Used by subclasses of StoreManager to implement /// implicit casts that arise from loads from regions that are reinterpreted /// as another region. SVal StoreManager::CastRetrievedVal(SVal V, const TypedValueRegion *R, - QualType castTy, bool performTestOnly) { - + QualType castTy) { if (castTy.isNull() || V.isUnknownOrUndef()) return V; - ASTContext &Ctx = svalBuilder.getContext(); - - if (performTestOnly) { - // Automatically translate references to pointers. - QualType T = R->getValueType(); - if (const ReferenceType *RT = T->getAs<ReferenceType>()) - T = Ctx.getPointerType(RT->getPointeeType()); - - assert(svalBuilder.getContext().hasSameUnqualifiedType(castTy, T)); - return V; - } + // When retrieving symbolic pointer and expecting a non-void pointer, + // wrap them into element regions of the expected type if necessary. + // SValBuilder::dispatchCast() doesn't do that, but it is necessary to + // make sure that the retrieved value makes sense, because there's no other + // cast in the AST that would tell us to cast it to the correct pointer type. + // We might need to do that for non-void pointers as well. + // FIXME: We really need a single good function to perform casts for us + // correctly every time we need it. + if (castTy->isPointerType() && !castTy->isVoidPointerType()) + if (const auto *SR = dyn_cast_or_null<SymbolicRegion>(V.getAsRegion())) + if (SR->getSymbol()->getType().getCanonicalType() != + castTy.getCanonicalType()) + return loc::MemRegionVal(castRegion(SR, castTy)); return svalBuilder.dispatchCast(V, castTy); } @@ -421,7 +430,7 @@ SVal StoreManager::getLValueFieldOrIvar(const Decl *D, SVal Base) { // NOTE: We must have this check first because ObjCIvarDecl is a subclass // of FieldDecl. - if (const ObjCIvarDecl *ID = dyn_cast<ObjCIvarDecl>(D)) + if (const auto *ID = dyn_cast<ObjCIvarDecl>(D)) return loc::MemRegionVal(MRMgr.getObjCIvarRegion(ID, BaseR)); return loc::MemRegionVal(MRMgr.getFieldRegion(cast<FieldDecl>(D), BaseR)); @@ -433,7 +442,6 @@ SVal StoreManager::getLValueIvar(const ObjCIvarDecl *decl, SVal base) { SVal StoreManager::getLValueElement(QualType elementType, NonLoc Offset, SVal Base) { - // If the base is an unknown or undefined value, just return it back. // FIXME: For absolute pointer addresses, we just return that value back as // well, although in reality we should return the offset added to that @@ -448,13 +456,12 @@ SVal StoreManager::getLValueElement(QualType elementType, NonLoc Offset, Base.castAs<loc::MemRegionVal>().getRegionAs<SubRegion>(); // Pointer of any type can be cast and used as array base. - const ElementRegion *ElemR = dyn_cast<ElementRegion>(BaseRegion); + const auto *ElemR = dyn_cast<ElementRegion>(BaseRegion); // Convert the offset to the appropriate size and signedness. Offset = svalBuilder.convertToArrayIndex(Offset).castAs<NonLoc>(); if (!ElemR) { - // // If the base region is not an ElementRegion, create one. // This can happen in the following example: // @@ -462,7 +469,6 @@ SVal StoreManager::getLValueElement(QualType elementType, NonLoc Offset, // p[1] = 8; // // Observe that 'p' binds to an AllocaRegion. - // return loc::MemRegionVal(MRMgr.getElementRegion(elementType, Offset, BaseRegion, Ctx)); } @@ -499,7 +505,7 @@ SVal StoreManager::getLValueElement(QualType elementType, NonLoc Offset, Ctx)); } -StoreManager::BindingsHandler::~BindingsHandler() {} +StoreManager::BindingsHandler::~BindingsHandler() = default; bool StoreManager::FindUniqueBinding::HandleBinding(StoreManager& SMgr, Store store, diff --git a/lib/StaticAnalyzer/Core/SymbolManager.cpp b/lib/StaticAnalyzer/Core/SymbolManager.cpp index f2d5ee83f3cc..ed197010ebb7 100644 --- a/lib/StaticAnalyzer/Core/SymbolManager.cpp +++ b/lib/StaticAnalyzer/Core/SymbolManager.cpp @@ -1,4 +1,4 @@ -//== SymbolManager.h - Management of Symbolic Values ------------*- C++ -*--==// +//===- SymbolManager.h - Management of Symbolic Values --------------------===// // // The LLVM Compiler Infrastructure // @@ -13,15 +13,27 @@ //===----------------------------------------------------------------------===// #include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h" +#include "clang/AST/ASTContext.h" +#include "clang/AST/Expr.h" #include "clang/Analysis/Analyses/LiveVariables.h" +#include "clang/Analysis/AnalysisDeclContext.h" +#include "clang/Basic/LLVM.h" #include "clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h" #include "clang/StaticAnalyzer/Core/PathSensitive/Store.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/SymExpr.h" +#include "llvm/ADT/FoldingSet.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/Support/Casting.h" +#include "llvm/Support/Compiler.h" +#include "llvm/Support/ErrorHandling.h" #include "llvm/Support/raw_ostream.h" +#include <cassert> using namespace clang; using namespace ento; -void SymExpr::anchor() { } +void SymExpr::anchor() {} LLVM_DUMP_METHOD void SymExpr::dump() const { dumpToStream(llvm::errs()); @@ -88,7 +100,7 @@ void SymbolMetadata::dumpToStream(raw_ostream &os) const { << getRegion() << ',' << T.getAsString() << '}'; } -void SymbolData::anchor() { } +void SymbolData::anchor() {} void SymbolRegionValue::dumpToStream(raw_ostream &os) const { os << "reg_$" << getSymbolID() @@ -138,7 +150,7 @@ void SymExpr::symbol_iterator::expand() { itr.push_back(cast<IntSymExpr>(SE)->getRHS()); return; case SymExpr::SymSymExprKind: { - const SymSymExpr *x = cast<SymSymExpr>(SE); + const auto *x = cast<SymSymExpr>(SE); itr.push_back(x->getLHS()); itr.push_back(x->getRHS()); return; @@ -147,13 +159,6 @@ void SymExpr::symbol_iterator::expand() { llvm_unreachable("unhandled expansion case"); } -unsigned SymExpr::computeComplexity() const { - unsigned R = 0; - for (symbol_iterator I = symbol_begin(), E = symbol_end(); I != E; ++I) - R++; - return R; -} - const SymbolRegionValue* SymbolManager::getRegionValueSymbol(const TypedValueRegion* R) { llvm::FoldingSetNodeID profile; @@ -192,7 +197,6 @@ const SymbolConjured* SymbolManager::conjureSymbol(const Stmt *E, const SymbolDerived* SymbolManager::getDerivedSymbol(SymbolRef parentSymbol, const TypedValueRegion *R) { - llvm::FoldingSetNodeID profile; SymbolDerived::Profile(profile, parentSymbol, R); void *InsertPos; @@ -227,7 +231,6 @@ const SymbolMetadata * SymbolManager::getMetadataSymbol(const MemRegion* R, const Stmt *S, QualType T, const LocationContext *LCtx, unsigned Count, const void *SymbolTag) { - llvm::FoldingSetNodeID profile; SymbolMetadata::Profile(profile, R, S, T, LCtx, Count, SymbolTag); void *InsertPos; @@ -382,11 +385,10 @@ void SymbolReaper::markDependentsLive(SymbolRef sym) { LI->second = HaveMarkedDependents; if (const SymbolRefSmallVectorTy *Deps = SymMgr.getDependentSymbols(sym)) { - for (SymbolRefSmallVectorTy::const_iterator I = Deps->begin(), - E = Deps->end(); I != E; ++I) { - if (TheLiving.find(*I) != TheLiving.end()) + for (const auto I : *Deps) { + if (TheLiving.find(I) != TheLiving.end()) continue; - markLive(*I); + markLive(I); } } } @@ -405,7 +407,7 @@ void SymbolReaper::markLive(const MemRegion *region) { void SymbolReaper::markElementIndicesLive(const MemRegion *region) { for (auto SR = dyn_cast<SubRegion>(region); SR; SR = dyn_cast<SubRegion>(SR->getSuperRegion())) { - if (auto ER = dyn_cast<ElementRegion>(SR)) { + if (const auto ER = dyn_cast<ElementRegion>(SR)) { SVal Idx = ER->getIndex(); for (auto SI = Idx.symbol_begin(), SE = Idx.symbol_end(); SI != SE; ++SI) markLive(*SI); @@ -432,10 +434,10 @@ bool SymbolReaper::isLiveRegion(const MemRegion *MR) { MR = MR->getBaseRegion(); - if (const SymbolicRegion *SR = dyn_cast<SymbolicRegion>(MR)) + if (const auto *SR = dyn_cast<SymbolicRegion>(MR)) return isLive(SR->getSymbol()); - if (const VarRegion *VR = dyn_cast<VarRegion>(MR)) + if (const auto *VR = dyn_cast<VarRegion>(MR)) return isLive(VR, true); // FIXME: This is a gross over-approximation. What we really need is a way to @@ -533,7 +535,7 @@ bool SymbolReaper::isLive(const VarRegion *VR, bool includeStoreBindings) const{ if (!LCtx) return false; - const StackFrameContext *CurrentContext = LCtx->getCurrentStackFrame(); + const StackFrameContext *CurrentContext = LCtx->getStackFrame(); if (VarContext == CurrentContext) { // If no statement is provided, everything is live. @@ -547,7 +549,7 @@ bool SymbolReaper::isLive(const VarRegion *VR, bool includeStoreBindings) const{ return false; unsigned &cachedQuery = - const_cast<SymbolReaper*>(this)->includedRegionCache[VR]; + const_cast<SymbolReaper *>(this)->includedRegionCache[VR]; if (cachedQuery) { return cachedQuery == 1; diff --git a/lib/StaticAnalyzer/Core/WorkList.cpp b/lib/StaticAnalyzer/Core/WorkList.cpp new file mode 100644 index 000000000000..4b227375da9b --- /dev/null +++ b/lib/StaticAnalyzer/Core/WorkList.cpp @@ -0,0 +1,254 @@ +//===- 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. +// +//===----------------------------------------------------------------------===// +// +// Defines different worklist implementations for the static analyzer. +// +//===----------------------------------------------------------------------===// + +#include "clang/StaticAnalyzer/Core/PathSensitive/WorkList.h" +#include "llvm/ADT/PriorityQueue.h" +#include "llvm/ADT/DenseSet.h" +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/Statistic.h" +#include <deque> +#include <vector> + +using namespace clang; +using namespace ento; + +#define DEBUG_TYPE "WorkList" + +STATISTIC(MaxQueueSize, "Maximum size of the worklist"); +STATISTIC(MaxReachableSize, "Maximum size of auxiliary worklist set"); + +//===----------------------------------------------------------------------===// +// Worklist classes for exploration of reachable states. +//===----------------------------------------------------------------------===// + +namespace { + +class DFS : public WorkList { + SmallVector<WorkListUnit, 20> Stack; + +public: + bool hasWork() const override { + return !Stack.empty(); + } + + void enqueue(const WorkListUnit& U) override { + Stack.push_back(U); + } + + WorkListUnit dequeue() override { + assert(!Stack.empty()); + const WorkListUnit& U = Stack.back(); + Stack.pop_back(); // This technically "invalidates" U, but we are fine. + return U; + } +}; + +class BFS : public WorkList { + std::deque<WorkListUnit> Queue; + +public: + bool hasWork() const override { + return !Queue.empty(); + } + + void enqueue(const WorkListUnit& U) override { + Queue.push_back(U); + } + + WorkListUnit dequeue() override { + WorkListUnit U = Queue.front(); + Queue.pop_front(); + return U; + } +}; + +} // namespace + +// Place the dstor for WorkList here because it contains virtual member +// functions, and we the code for the dstor generated in one compilation unit. +WorkList::~WorkList() = default; + +std::unique_ptr<WorkList> WorkList::makeDFS() { + return llvm::make_unique<DFS>(); +} + +std::unique_ptr<WorkList> WorkList::makeBFS() { + return llvm::make_unique<BFS>(); +} + +namespace { + + class BFSBlockDFSContents : public WorkList { + std::deque<WorkListUnit> Queue; + SmallVector<WorkListUnit, 20> Stack; + + public: + bool hasWork() const override { + return !Queue.empty() || !Stack.empty(); + } + + void enqueue(const WorkListUnit& U) override { + if (U.getNode()->getLocation().getAs<BlockEntrance>()) + Queue.push_front(U); + else + Stack.push_back(U); + } + + WorkListUnit dequeue() override { + // Process all basic blocks to completion. + if (!Stack.empty()) { + const WorkListUnit& U = Stack.back(); + Stack.pop_back(); // This technically "invalidates" U, but we are fine. + return U; + } + + assert(!Queue.empty()); + // Don't use const reference. The subsequent pop_back() might make it + // unsafe. + WorkListUnit U = Queue.front(); + Queue.pop_front(); + return U; + } + }; + +} // namespace + +std::unique_ptr<WorkList> WorkList::makeBFSBlockDFSContents() { + return llvm::make_unique<BFSBlockDFSContents>(); +} + +namespace { + +class UnexploredFirstStack : public WorkList { + /// Stack of nodes known to have statements we have not traversed yet. + SmallVector<WorkListUnit, 20> StackUnexplored; + + /// Stack of all other nodes. + SmallVector<WorkListUnit, 20> StackOthers; + + using BlockID = unsigned; + using LocIdentifier = std::pair<BlockID, const StackFrameContext *>; + + llvm::DenseSet<LocIdentifier> Reachable; + +public: + bool hasWork() const override { + return !(StackUnexplored.empty() && StackOthers.empty()); + } + + void enqueue(const WorkListUnit &U) override { + const ExplodedNode *N = U.getNode(); + auto BE = N->getLocation().getAs<BlockEntrance>(); + + if (!BE) { + // Assume the choice of the order of the preceeding block entrance was + // correct. + StackUnexplored.push_back(U); + } else { + LocIdentifier LocId = std::make_pair( + BE->getBlock()->getBlockID(), + N->getLocationContext()->getStackFrame()); + auto InsertInfo = Reachable.insert(LocId); + + if (InsertInfo.second) { + StackUnexplored.push_back(U); + } else { + StackOthers.push_back(U); + } + } + MaxReachableSize.updateMax(Reachable.size()); + MaxQueueSize.updateMax(StackUnexplored.size() + StackOthers.size()); + } + + WorkListUnit dequeue() override { + if (!StackUnexplored.empty()) { + WorkListUnit &U = StackUnexplored.back(); + StackUnexplored.pop_back(); + return U; + } else { + WorkListUnit &U = StackOthers.back(); + StackOthers.pop_back(); + return U; + } + } +}; + +} // namespace + +std::unique_ptr<WorkList> WorkList::makeUnexploredFirst() { + return llvm::make_unique<UnexploredFirstStack>(); +} + +namespace { +class UnexploredFirstPriorityQueue : public WorkList { + using BlockID = unsigned; + using LocIdentifier = std::pair<BlockID, const StackFrameContext *>; + + // How many times each location was visited. + // Is signed because we negate it later in order to have a reversed + // comparison. + using VisitedTimesMap = llvm::DenseMap<LocIdentifier, int>; + + // Compare by number of times the location was visited first (negated + // to prefer less often visited locations), then by insertion time (prefer + // expanding nodes inserted sooner first). + using QueuePriority = std::pair<int, unsigned long>; + using QueueItem = std::pair<WorkListUnit, QueuePriority>; + + struct ExplorationComparator { + bool operator() (const QueueItem &LHS, const QueueItem &RHS) { + return LHS.second < RHS.second; + } + }; + + // Number of inserted nodes, used to emulate DFS ordering in the priority + // queue when insertions are equal. + unsigned long Counter = 0; + + // Number of times a current location was reached. + VisitedTimesMap NumReached; + + // The top item is the largest one. + llvm::PriorityQueue<QueueItem, std::vector<QueueItem>, ExplorationComparator> + queue; + +public: + bool hasWork() const override { + return !queue.empty(); + } + + void enqueue(const WorkListUnit &U) override { + const ExplodedNode *N = U.getNode(); + unsigned NumVisited = 0; + if (auto BE = N->getLocation().getAs<BlockEntrance>()) { + LocIdentifier LocId = std::make_pair( + BE->getBlock()->getBlockID(), + N->getLocationContext()->getStackFrame()); + NumVisited = NumReached[LocId]++; + } + + queue.push(std::make_pair(U, std::make_pair(-NumVisited, ++Counter))); + } + + WorkListUnit dequeue() override { + QueueItem U = queue.top(); + queue.pop(); + return U.first; + } +}; +} // namespace + +std::unique_ptr<WorkList> WorkList::makeUnexploredFirstPriorityQueue() { + return llvm::make_unique<UnexploredFirstPriorityQueue>(); +} diff --git a/lib/StaticAnalyzer/Core/Z3ConstraintManager.cpp b/lib/StaticAnalyzer/Core/Z3ConstraintManager.cpp index f9f9057a89cd..7379ded49c80 100644 --- a/lib/StaticAnalyzer/Core/Z3ConstraintManager.cpp +++ b/lib/StaticAnalyzer/Core/Z3ConstraintManager.cpp @@ -10,7 +10,11 @@ #include "clang/Basic/TargetInfo.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/SimpleConstraintManager.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/SMTConstraintManager.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/SMTContext.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/SMTExpr.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/SMTSolver.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/SMTSort.h" #include "clang/Config/config.h" @@ -21,30 +25,9 @@ using namespace ento; #include <z3.h> -// Forward declarations -namespace { -class Z3Expr; -class ConstraintZ3 {}; -} // end anonymous namespace - -typedef llvm::ImmutableSet<std::pair<SymbolRef, Z3Expr>> ConstraintZ3Ty; - -// Expansion of REGISTER_TRAIT_WITH_PROGRAMSTATE(ConstraintZ3, Z3SetPair) -namespace clang { -namespace ento { -template <> -struct ProgramStateTrait<ConstraintZ3> - : public ProgramStatePartialTrait<ConstraintZ3Ty> { - static void *GDMIndex() { - static int Index; - return &Index; - } -}; -} // end namespace ento -} // end namespace clang - namespace { +/// Configuration class for Z3 class Z3Config { friend class Z3Context; @@ -63,45 +46,60 @@ public: ~Z3Config() { Z3_del_config(Config); } }; // end class Z3Config -class Z3Context { - Z3_context ZC_P; +// 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_ex(Context, Error))); +} +/// Wrapper for Z3 context +class Z3Context : public SMTContext { public: - static Z3_context ZC; + Z3_context Context; - Z3Context() : ZC_P(Z3_mk_context_rc(Z3Config().Config)) { ZC = ZC_P; } + Z3Context() : SMTContext() { + 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); + } - ~Z3Context() { - Z3_del_context(ZC); - Z3_finalize_memory(); - ZC_P = nullptr; + virtual ~Z3Context() { + Z3_del_context(Context); + Context = nullptr; } }; // end class Z3Context -class Z3Sort { - friend class Z3Expr; +/// Wrapper for Z3 Sort +class Z3Sort : public SMTSort { + friend class Z3Solver; + + Z3Context &Context; Z3_sort Sort; - Z3Sort() : Sort(nullptr) {} - Z3Sort(Z3_sort ZS) : Sort(ZS) { - Z3_inc_ref(Z3Context::ZC, reinterpret_cast<Z3_ast>(Sort)); +public: + /// Default constructor, mainly used by make_shared + Z3Sort(Z3Context &C, Z3_sort ZS) : SMTSort(), Context(C), Sort(ZS) { + Z3_inc_ref(Context.Context, reinterpret_cast<Z3_ast>(Sort)); } -public: /// Override implicit copy constructor for correct reference counting. - Z3Sort(const Z3Sort &Copy) : Sort(Copy.Sort) { - Z3_inc_ref(Z3Context::ZC, reinterpret_cast<Z3_ast>(Sort)); + Z3Sort(const Z3Sort &Copy) + : SMTSort(), Context(Copy.Context), Sort(Copy.Sort) { + Z3_inc_ref(Context.Context, reinterpret_cast<Z3_ast>(Sort)); } /// Provide move constructor - Z3Sort(Z3Sort &&Move) : Sort(nullptr) { *this = std::move(Move); } + Z3Sort(Z3Sort &&Move) : SMTSort(), Context(Move.Context), Sort(nullptr) { + *this = std::move(Move); + } /// Provide move assignment constructor Z3Sort &operator=(Z3Sort &&Move) { if (this != &Move) { if (Sort) - Z3_dec_ref(Z3Context::ZC, reinterpret_cast<Z3_ast>(Sort)); + Z3_dec_ref(Context.Context, reinterpret_cast<Z3_ast>(Sort)); Sort = Move.Sort; Move.Sort = nullptr; } @@ -110,119 +108,78 @@ public: ~Z3Sort() { if (Sort) - Z3_dec_ref(Z3Context::ZC, reinterpret_cast<Z3_ast>(Sort)); - } - - // Return a boolean sort. - static Z3Sort getBoolSort() { return Z3Sort(Z3_mk_bool_sort(Z3Context::ZC)); } - - // Return an appropriate bitvector sort for the given bitwidth. - static Z3Sort getBitvectorSort(unsigned BitWidth) { - return Z3Sort(Z3_mk_bv_sort(Z3Context::ZC, BitWidth)); - } - - // Return an appropriate floating-point sort for the given bitwidth. - static Z3Sort getFloatSort(unsigned BitWidth) { - Z3_sort Sort; - - switch (BitWidth) { - default: - llvm_unreachable("Unsupported floating-point bitwidth!"); - break; - case 16: - Sort = Z3_mk_fpa_sort_16(Z3Context::ZC); - break; - case 32: - Sort = Z3_mk_fpa_sort_32(Z3Context::ZC); - break; - case 64: - Sort = Z3_mk_fpa_sort_64(Z3Context::ZC); - break; - case 128: - Sort = Z3_mk_fpa_sort_128(Z3Context::ZC); - break; - } - return Z3Sort(Sort); + Z3_dec_ref(Context.Context, reinterpret_cast<Z3_ast>(Sort)); } - // Return an appropriate sort for the given AST. - static Z3Sort getSort(Z3_ast AST) { - return Z3Sort(Z3_get_sort(Z3Context::ZC, AST)); + bool isBitvectorSortImpl() const override { + return (Z3_get_sort_kind(Context.Context, Sort) == Z3_BV_SORT); } - Z3_sort_kind getSortKind() const { - return Z3_get_sort_kind(Z3Context::ZC, Sort); + bool isFloatSortImpl() const override { + return (Z3_get_sort_kind(Context.Context, Sort) == Z3_FLOATING_POINT_SORT); } - unsigned getBitvectorSortSize() const { - assert(getSortKind() == Z3_BV_SORT && "Not a bitvector sort!"); - return Z3_get_bv_sort_size(Z3Context::ZC, Sort); + bool isBooleanSortImpl() const override { + return (Z3_get_sort_kind(Context.Context, Sort) == Z3_BOOL_SORT); } - unsigned getFloatSortSize() const { - assert(getSortKind() == Z3_FLOATING_POINT_SORT && - "Not a floating-point sort!"); - return Z3_fpa_get_ebits(Z3Context::ZC, Sort) + - Z3_fpa_get_sbits(Z3Context::ZC, Sort); + unsigned getBitvectorSortSizeImpl() const override { + return Z3_get_bv_sort_size(Context.Context, Sort); } - bool operator==(const Z3Sort &Other) const { - return Z3_is_eq_sort(Z3Context::ZC, Sort, Other.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); } Z3Sort &operator=(const Z3Sort &Move) { - Z3_inc_ref(Z3Context::ZC, reinterpret_cast<Z3_ast>(Move.Sort)); - Z3_dec_ref(Z3Context::ZC, reinterpret_cast<Z3_ast>(Sort)); + Z3_inc_ref(Context.Context, reinterpret_cast<Z3_ast>(Move.Sort)); + Z3_dec_ref(Context.Context, reinterpret_cast<Z3_ast>(Sort)); Sort = Move.Sort; return *this; } - void print(raw_ostream &OS) const { - OS << Z3_sort_to_string(Z3Context::ZC, Sort); + void print(raw_ostream &OS) const override { + OS << Z3_sort_to_string(Context.Context, Sort); } - - LLVM_DUMP_METHOD void dump() const { print(llvm::errs()); } }; // end class Z3Sort -class Z3Expr { - friend class Z3Model; - friend class Z3Solver; +static const Z3Sort &toZ3Sort(const SMTSort &S) { + return static_cast<const Z3Sort &>(S); +} - Z3_ast AST; +class Z3Expr : public SMTExpr { + friend class Z3Solver; - Z3Expr(Z3_ast ZA) : AST(ZA) { Z3_inc_ref(Z3Context::ZC, AST); } + Z3Context &Context; - // Return an appropriate floating-point rounding mode. - static Z3Expr getFloatRoundingMode() { - // TODO: Don't assume nearest ties to even rounding mode - return Z3Expr(Z3_mk_fpa_rne(Z3Context::ZC)); - } + Z3_ast AST; - // 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)); +public: + Z3Expr(Z3Context &C, Z3_ast ZA) : SMTExpr(), Context(C), AST(ZA) { + Z3_inc_ref(Context.Context, AST); } -public: /// Override implicit copy constructor for correct reference counting. - Z3Expr(const Z3Expr &Copy) : AST(Copy.AST) { Z3_inc_ref(Z3Context::ZC, AST); } + Z3Expr(const Z3Expr &Copy) : SMTExpr(), Context(Copy.Context), AST(Copy.AST) { + Z3_inc_ref(Context.Context, AST); + } /// Provide move constructor - Z3Expr(Z3Expr &&Move) : AST(nullptr) { *this = std::move(Move); } + Z3Expr(Z3Expr &&Move) : SMTExpr(), Context(Move.Context), AST(nullptr) { + *this = std::move(Move); + } /// Provide move assignment constructor Z3Expr &operator=(Z3Expr &&Move) { if (this != &Move) { if (AST) - Z3_dec_ref(Z3Context::ZC, AST); + Z3_dec_ref(Context.Context, AST); AST = Move.AST; Move.AST = nullptr; } @@ -231,1388 +188,854 @@ public: ~Z3Expr() { if (AST) - Z3_dec_ref(Z3Context::ZC, AST); - } - - /// 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(); - } + Z3_dec_ref(Context.Context, AST); } - /// Construct a Z3Expr from a unary operator, given a Z3_context. - static Z3Expr fromUnOp(const UnaryOperator::Opcode Op, const Z3Expr &Exp) { - Z3_ast AST; + void Profile(llvm::FoldingSetNodeID &ID) const override { + ID.AddInteger(Z3_get_ast_hash(Context.Context, AST)); + } - switch (Op) { - default: - llvm_unreachable("Unimplemented opcode"); - break; + /// 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); + } - case UO_Minus: - AST = Z3_mk_bvneg(Z3Context::ZC, Exp.AST); - break; + /// Override implicit move constructor for correct reference counting. + Z3Expr &operator=(const Z3Expr &Move) { + Z3_inc_ref(Context.Context, Move.AST); + Z3_dec_ref(Context.Context, AST); + AST = Move.AST; + return *this; + } - case UO_Not: - AST = Z3_mk_bvnot(Z3Context::ZC, Exp.AST); - break; + void print(raw_ostream &OS) const override { + OS << Z3_ast_to_string(Context.Context, AST); + } +}; // end class Z3Expr - case UO_LNot: - AST = Z3_mk_not(Z3Context::ZC, Exp.AST); - break; - } +static const Z3Expr &toZ3Expr(const SMTExpr &E) { + return static_cast<const Z3Expr &>(E); +} - return Z3Expr(AST); - } +class Z3Model { + friend class Z3Solver; + + Z3Context &Context; + + Z3_model Model; - /// Construct a Z3Expr from a floating-point unary operator, given a - /// Z3_context. - static Z3Expr fromFloatUnOp(const UnaryOperator::Opcode Op, - const Z3Expr &Exp) { - Z3_ast AST; +public: + Z3Model(Z3Context &C, Z3_model ZM) : Context(C), Model(ZM) { + assert(C.Context != nullptr); + Z3_model_inc_ref(Context.Context, Model); + } - switch (Op) { - default: - llvm_unreachable("Unimplemented opcode"); - break; + /// Override implicit copy constructor for correct reference counting. + Z3Model(const Z3Model &Copy) : Context(Copy.Context), Model(Copy.Model) { + Z3_model_inc_ref(Context.Context, Model); + } - case UO_Minus: - AST = Z3_mk_fpa_neg(Z3Context::ZC, Exp.AST); - break; + /// Provide move constructor + Z3Model(Z3Model &&Move) : Context(Move.Context), Model(nullptr) { + *this = std::move(Move); + } - case UO_LNot: - return Z3Expr::fromUnOp(Op, Exp); + /// Provide move assignment constructor + Z3Model &operator=(Z3Model &&Move) { + if (this != &Move) { + if (Model) + Z3_model_dec_ref(Context.Context, Model); + Model = Move.Model; + Move.Model = nullptr; } + return *this; + } - return Z3Expr(AST); + ~Z3Model() { + if (Model) + Z3_model_dec_ref(Context.Context, Model); } - /// Construct a Z3Expr from a n-ary binary operator. - static Z3Expr fromNBinOp(const BinaryOperator::Opcode Op, - const std::vector<Z3_ast> &ASTs) { - Z3_ast AST; + void print(raw_ostream &OS) const { + OS << Z3_model_to_string(Context.Context, Model); + } - switch (Op) { - default: - llvm_unreachable("Unimplemented opcode"); - break; + LLVM_DUMP_METHOD void dump() const { print(llvm::errs()); } +}; // end class Z3Model - case BO_LAnd: - AST = Z3_mk_and(Z3Context::ZC, ASTs.size(), ASTs.data()); - break; +/// 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(); + } +} - case BO_LOr: - AST = Z3_mk_or(Z3Context::ZC, ASTs.size(), ASTs.data()); - break; - } +// 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)); +} - return Z3Expr(AST); - } - - /// Construct a Z3Expr from a binary operator, given a Z3_context. - static Z3Expr fromBinOp(const Z3Expr &LHS, const BinaryOperator::Opcode Op, - const Z3Expr &RHS, bool isSigned) { - Z3_ast AST; - - assert(Z3Sort::getSort(LHS.AST) == Z3Sort::getSort(RHS.AST) && - "AST's must have the same sort!"); - - switch (Op) { - default: - llvm_unreachable("Unimplemented opcode"); - break; - - // Multiplicative operators - case BO_Mul: - AST = Z3_mk_bvmul(Z3Context::ZC, LHS.AST, RHS.AST); - break; - case BO_Div: - AST = isSigned ? Z3_mk_bvsdiv(Z3Context::ZC, LHS.AST, RHS.AST) - : Z3_mk_bvudiv(Z3Context::ZC, LHS.AST, RHS.AST); - break; - case BO_Rem: - AST = isSigned ? Z3_mk_bvsrem(Z3Context::ZC, LHS.AST, RHS.AST) - : Z3_mk_bvurem(Z3Context::ZC, LHS.AST, RHS.AST); - break; - - // Additive operators - case BO_Add: - AST = Z3_mk_bvadd(Z3Context::ZC, LHS.AST, RHS.AST); - break; - case BO_Sub: - AST = Z3_mk_bvsub(Z3Context::ZC, LHS.AST, RHS.AST); - break; - - // Bitwise shift operators - case BO_Shl: - AST = Z3_mk_bvshl(Z3Context::ZC, LHS.AST, RHS.AST); - break; - case BO_Shr: - AST = isSigned ? Z3_mk_bvashr(Z3Context::ZC, LHS.AST, RHS.AST) - : Z3_mk_bvlshr(Z3Context::ZC, LHS.AST, RHS.AST); - break; - - // Relational operators - case BO_LT: - AST = isSigned ? Z3_mk_bvslt(Z3Context::ZC, LHS.AST, RHS.AST) - : Z3_mk_bvult(Z3Context::ZC, LHS.AST, RHS.AST); - break; - case BO_GT: - AST = isSigned ? Z3_mk_bvsgt(Z3Context::ZC, LHS.AST, RHS.AST) - : Z3_mk_bvugt(Z3Context::ZC, LHS.AST, RHS.AST); - break; - case BO_LE: - AST = isSigned ? Z3_mk_bvsle(Z3Context::ZC, LHS.AST, RHS.AST) - : Z3_mk_bvule(Z3Context::ZC, LHS.AST, RHS.AST); - break; - case BO_GE: - AST = isSigned ? Z3_mk_bvsge(Z3Context::ZC, LHS.AST, RHS.AST) - : Z3_mk_bvuge(Z3Context::ZC, LHS.AST, RHS.AST); - break; - - // Equality operators - case BO_EQ: - AST = Z3_mk_eq(Z3Context::ZC, LHS.AST, RHS.AST); - break; - case BO_NE: - return Z3Expr::fromUnOp(UO_LNot, - Z3Expr::fromBinOp(LHS, BO_EQ, RHS, isSigned)); - break; - - // Bitwise operators - case BO_And: - AST = Z3_mk_bvand(Z3Context::ZC, LHS.AST, RHS.AST); - break; - case BO_Xor: - AST = Z3_mk_bvxor(Z3Context::ZC, LHS.AST, RHS.AST); - break; - case BO_Or: - AST = Z3_mk_bvor(Z3Context::ZC, LHS.AST, RHS.AST); - break; - - // Logical operators - case BO_LAnd: - case BO_LOr: { - std::vector<Z3_ast> Args = {LHS.AST, RHS.AST}; - return Z3Expr::fromNBinOp(Op, Args); - } - } +} // end anonymous namespace - return Z3Expr(AST); - } - - /// Construct a Z3Expr from a special floating-point binary operator, given - /// a Z3_context. - static Z3Expr fromFloatSpecialBinOp(const Z3Expr &LHS, - const BinaryOperator::Opcode Op, - const llvm::APFloat::fltCategory &RHS) { - Z3_ast AST; - - switch (Op) { - default: - llvm_unreachable("Unimplemented opcode"); - break; - - // Equality operators - case BO_EQ: - switch (RHS) { - case llvm::APFloat::fcInfinity: - AST = Z3_mk_fpa_is_infinite(Z3Context::ZC, LHS.AST); - break; - case llvm::APFloat::fcNaN: - AST = Z3_mk_fpa_is_nan(Z3Context::ZC, LHS.AST); - break; - case llvm::APFloat::fcNormal: - AST = Z3_mk_fpa_is_normal(Z3Context::ZC, LHS.AST); - break; - case llvm::APFloat::fcZero: - AST = Z3_mk_fpa_is_zero(Z3Context::ZC, LHS.AST); - break; - } - break; - case BO_NE: - return Z3Expr::fromFloatUnOp( - UO_LNot, Z3Expr::fromFloatSpecialBinOp(LHS, BO_EQ, RHS)); - break; - } +typedef llvm::ImmutableSet<std::pair<SymbolRef, Z3Expr>> ConstraintZ3Ty; +REGISTER_TRAIT_WITH_PROGRAMSTATE(ConstraintZ3, ConstraintZ3Ty) - return Z3Expr(AST); - } +namespace { - /// Construct a Z3Expr from a floating-point binary operator, given a - /// Z3_context. - static Z3Expr fromFloatBinOp(const Z3Expr &LHS, - const BinaryOperator::Opcode Op, - const Z3Expr &RHS) { - Z3_ast AST; +class Z3Solver : public SMTSolver { + friend class Z3ConstraintManager; - assert(Z3Sort::getSort(LHS.AST) == Z3Sort::getSort(RHS.AST) && - "AST's must have the same sort!"); + Z3Context Context; - switch (Op) { - default: - llvm_unreachable("Unimplemented opcode"); - break; + Z3_solver Solver; - // Multiplicative operators - case BO_Mul: { - Z3Expr RoundingMode = Z3Expr::getFloatRoundingMode(); - AST = Z3_mk_fpa_mul(Z3Context::ZC, RoundingMode.AST, LHS.AST, RHS.AST); - break; - } - case BO_Div: { - Z3Expr RoundingMode = Z3Expr::getFloatRoundingMode(); - AST = Z3_mk_fpa_div(Z3Context::ZC, RoundingMode.AST, LHS.AST, RHS.AST); - break; - } - case BO_Rem: - AST = Z3_mk_fpa_rem(Z3Context::ZC, LHS.AST, RHS.AST); - break; - - // Additive operators - case BO_Add: { - Z3Expr RoundingMode = Z3Expr::getFloatRoundingMode(); - AST = Z3_mk_fpa_add(Z3Context::ZC, RoundingMode.AST, LHS.AST, RHS.AST); - break; - } - case BO_Sub: { - Z3Expr RoundingMode = Z3Expr::getFloatRoundingMode(); - AST = Z3_mk_fpa_sub(Z3Context::ZC, RoundingMode.AST, LHS.AST, RHS.AST); - break; - } +public: + Z3Solver() : SMTSolver(), Solver(Z3_mk_simple_solver(Context.Context)) { + Z3_solver_inc_ref(Context.Context, Solver); + } + + /// Override implicit copy constructor for correct reference counting. + Z3Solver(const Z3Solver &Copy) + : SMTSolver(), Context(Copy.Context), Solver(Copy.Solver) { + Z3_solver_inc_ref(Context.Context, Solver); + } + + /// Provide move constructor + Z3Solver(Z3Solver &&Move) + : SMTSolver(), Context(Move.Context), Solver(nullptr) { + *this = std::move(Move); + } - // Relational operators - case BO_LT: - AST = Z3_mk_fpa_lt(Z3Context::ZC, LHS.AST, RHS.AST); - break; - case BO_GT: - AST = Z3_mk_fpa_gt(Z3Context::ZC, LHS.AST, RHS.AST); - break; - case BO_LE: - AST = Z3_mk_fpa_leq(Z3Context::ZC, LHS.AST, RHS.AST); - break; - case BO_GE: - AST = Z3_mk_fpa_geq(Z3Context::ZC, LHS.AST, RHS.AST); - break; - - // Equality operators - case BO_EQ: - AST = Z3_mk_fpa_eq(Z3Context::ZC, LHS.AST, RHS.AST); - break; - case BO_NE: - return Z3Expr::fromFloatUnOp(UO_LNot, - Z3Expr::fromFloatBinOp(LHS, BO_EQ, RHS)); - break; - - // Logical operators - case BO_LAnd: - case BO_LOr: - return Z3Expr::fromBinOp(LHS, Op, RHS, false); + /// Provide move assignment constructor + Z3Solver &operator=(Z3Solver &&Move) { + if (this != &Move) { + if (Solver) + Z3_solver_dec_ref(Context.Context, Solver); + Solver = Move.Solver; + Move.Solver = nullptr; } + return *this; + } - return Z3Expr(AST); + ~Z3Solver() { + if (Solver) + Z3_solver_dec_ref(Context.Context, Solver); } - /// Construct a Z3Expr from a SymbolData, given a Z3_context. - static Z3Expr fromData(const SymbolID ID, bool isBool, bool isFloat, - uint64_t BitWidth) { - llvm::Twine Name = "$" + llvm::Twine(ID); + void addConstraint(const SMTExprRef &Exp) const override { + Z3_solver_assert(Context.Context, Solver, toZ3Expr(*Exp).AST); + } - Z3Sort Sort; - if (isBool) - Sort = Z3Sort::getBoolSort(); - else if (isFloat) - Sort = Z3Sort::getFloatSort(BitWidth); - else - Sort = Z3Sort::getBitvectorSort(BitWidth); - - Z3_symbol Symbol = Z3_mk_string_symbol(Z3Context::ZC, Name.str().c_str()); - Z3_ast AST = Z3_mk_const(Z3Context::ZC, Symbol, Sort.Sort); - return Z3Expr(AST); - } - - /// Construct a Z3Expr from a SymbolCast, given a Z3_context. - static Z3Expr fromCast(const Z3Expr &Exp, QualType ToTy, uint64_t ToBitWidth, - QualType FromTy, uint64_t FromBitWidth) { - Z3_ast AST; - - if ((FromTy->isIntegralOrEnumerationType() && - ToTy->isIntegralOrEnumerationType()) || - (FromTy->isAnyPointerType() ^ ToTy->isAnyPointerType()) || - (FromTy->isBlockPointerType() ^ ToTy->isBlockPointerType()) || - (FromTy->isReferenceType() ^ ToTy->isReferenceType())) { - // Special case: Z3 boolean type is distinct from bitvector type, so - // must use if-then-else expression instead of direct cast - if (FromTy->isBooleanType()) { - assert(ToBitWidth > 0 && "BitWidth must be positive!"); - Z3Expr Zero = Z3Expr::fromInt("0", ToBitWidth); - Z3Expr One = Z3Expr::fromInt("1", ToBitWidth); - AST = Z3_mk_ite(Z3Context::ZC, Exp.AST, One.AST, Zero.AST); - } else if (ToBitWidth > FromBitWidth) { - AST = FromTy->isSignedIntegerOrEnumerationType() - ? Z3_mk_sign_ext(Z3Context::ZC, ToBitWidth - FromBitWidth, - Exp.AST) - : Z3_mk_zero_ext(Z3Context::ZC, ToBitWidth - FromBitWidth, - Exp.AST); - } else if (ToBitWidth < FromBitWidth) { - AST = Z3_mk_extract(Z3Context::ZC, ToBitWidth - 1, 0, Exp.AST); - } else { - // Both are bitvectors with the same width, ignore the type cast - return Exp; - } - } else if (FromTy->isRealFloatingType() && ToTy->isRealFloatingType()) { - if (ToBitWidth != FromBitWidth) { - Z3Expr RoundingMode = Z3Expr::getFloatRoundingMode(); - Z3Sort Sort = Z3Sort::getFloatSort(ToBitWidth); - AST = Z3_mk_fpa_to_fp_float(Z3Context::ZC, RoundingMode.AST, Exp.AST, - Sort.Sort); - } else { - return Exp; - } - } else if (FromTy->isIntegralOrEnumerationType() && - ToTy->isRealFloatingType()) { - Z3Expr RoundingMode = Z3Expr::getFloatRoundingMode(); - Z3Sort Sort = Z3Sort::getFloatSort(ToBitWidth); - AST = FromTy->isSignedIntegerOrEnumerationType() - ? Z3_mk_fpa_to_fp_signed(Z3Context::ZC, RoundingMode.AST, - Exp.AST, Sort.Sort) - : Z3_mk_fpa_to_fp_unsigned(Z3Context::ZC, RoundingMode.AST, - Exp.AST, Sort.Sort); - } else if (FromTy->isRealFloatingType() && - ToTy->isIntegralOrEnumerationType()) { - Z3Expr RoundingMode = Z3Expr::getFloatRoundingMode(); - AST = ToTy->isSignedIntegerOrEnumerationType() - ? Z3_mk_fpa_to_sbv(Z3Context::ZC, RoundingMode.AST, Exp.AST, - ToBitWidth) - : Z3_mk_fpa_to_ubv(Z3Context::ZC, RoundingMode.AST, Exp.AST, - ToBitWidth); - } else { - llvm_unreachable("Unsupported explicit type cast!"); - } + SMTSortRef getBoolSort() override { + return std::make_shared<Z3Sort>(Context, Z3_mk_bool_sort(Context.Context)); + } - return Z3Expr(AST); + SMTSortRef getBitvectorSort(unsigned BitWidth) override { + return std::make_shared<Z3Sort>(Context, + Z3_mk_bv_sort(Context.Context, BitWidth)); } - /// Construct a Z3Expr from a boolean, given a Z3_context. - static Z3Expr fromBoolean(const bool Bool) { - Z3_ast AST = Bool ? Z3_mk_true(Z3Context::ZC) : Z3_mk_false(Z3Context::ZC); - return Z3Expr(AST); + SMTSortRef getSort(const SMTExprRef &Exp) override { + return std::make_shared<Z3Sort>( + Context, Z3_get_sort(Context.Context, toZ3Expr(*Exp).AST)); } - /// Construct a Z3Expr from a finite APFloat, given a Z3_context. - static Z3Expr fromAPFloat(const llvm::APFloat &Float) { - Z3_ast AST; - Z3Sort Sort = Z3Sort::getFloatSort( - llvm::APFloat::semanticsSizeInBits(Float.getSemantics())); + SMTSortRef getFloat16Sort() override { + return std::make_shared<Z3Sort>(Context, + Z3_mk_fpa_sort_16(Context.Context)); + } - llvm::APSInt Int = llvm::APSInt(Float.bitcastToAPInt(), true); - Z3Expr Z3Int = Z3Expr::fromAPSInt(Int); - AST = Z3_mk_fpa_to_fp_bv(Z3Context::ZC, Z3Int.AST, Sort.Sort); + SMTSortRef getFloat32Sort() override { + return std::make_shared<Z3Sort>(Context, + Z3_mk_fpa_sort_32(Context.Context)); + } - return Z3Expr(AST); + SMTSortRef getFloat64Sort() override { + return std::make_shared<Z3Sort>(Context, + Z3_mk_fpa_sort_64(Context.Context)); } - /// Construct a Z3Expr from an APSInt, given a Z3_context. - static Z3Expr fromAPSInt(const llvm::APSInt &Int) { - Z3Sort Sort = Z3Sort::getBitvectorSort(Int.getBitWidth()); - Z3_ast AST = - Z3_mk_numeral(Z3Context::ZC, Int.toString(10).c_str(), Sort.Sort); - return Z3Expr(AST); + SMTSortRef getFloat128Sort() override { + return std::make_shared<Z3Sort>(Context, + Z3_mk_fpa_sort_128(Context.Context)); } - /// Construct a Z3Expr from an integer, given a Z3_context. - static Z3Expr fromInt(const char *Int, uint64_t BitWidth) { - Z3Sort Sort = Z3Sort::getBitvectorSort(BitWidth); - Z3_ast AST = Z3_mk_numeral(Z3Context::ZC, Int, Sort.Sort); - return Z3Expr(AST); + SMTExprRef newExprRef(const SMTExpr &E) const override { + return std::make_shared<Z3Expr>(toZ3Expr(E)); } - /// Construct an APFloat from a Z3Expr, given the AST representation - static bool toAPFloat(const Z3Sort &Sort, const Z3_ast &AST, - llvm::APFloat &Float, bool useSemantics = true) { - assert(Sort.getSortKind() == Z3_FLOATING_POINT_SORT && - "Unsupported sort to floating-point!"); + SMTExprRef mkBVNeg(const SMTExprRef &Exp) override { + return newExprRef( + Z3Expr(Context, Z3_mk_bvneg(Context.Context, toZ3Expr(*Exp).AST))); + } - llvm::APSInt Int(Sort.getFloatSortSize(), true); - const llvm::fltSemantics &Semantics = - Z3Expr::getFloatSemantics(Sort.getFloatSortSize()); - Z3Sort BVSort = Z3Sort::getBitvectorSort(Sort.getFloatSortSize()); - if (!Z3Expr::toAPSInt(BVSort, AST, Int, true)) { - return false; - } + SMTExprRef mkBVNot(const SMTExprRef &Exp) override { + return newExprRef( + Z3Expr(Context, Z3_mk_bvnot(Context.Context, toZ3Expr(*Exp).AST))); + } - if (useSemantics && - !Z3Expr::areEquivalent(Float.getSemantics(), Semantics)) { - assert(false && "Floating-point types don't match!"); - return false; - } + SMTExprRef mkNot(const SMTExprRef &Exp) override { + return newExprRef( + Z3Expr(Context, Z3_mk_not(Context.Context, toZ3Expr(*Exp).AST))); + } - Float = llvm::APFloat(Semantics, Int); - return true; + SMTExprRef mkBVAdd(const SMTExprRef &LHS, const SMTExprRef &RHS) override { + return newExprRef( + Z3Expr(Context, Z3_mk_bvadd(Context.Context, toZ3Expr(*LHS).AST, + toZ3Expr(*RHS).AST))); } - /// Construct an APSInt from a Z3Expr, given the AST representation - static bool toAPSInt(const Z3Sort &Sort, const Z3_ast &AST, llvm::APSInt &Int, - bool useSemantics = true) { - switch (Sort.getSortKind()) { - default: - llvm_unreachable("Unsupported sort to integer!"); - case Z3_BV_SORT: { - if (useSemantics && Int.getBitWidth() != Sort.getBitvectorSortSize()) { - assert(false && "Bitvector types don't match!"); - return false; - } + SMTExprRef mkBVSub(const SMTExprRef &LHS, const SMTExprRef &RHS) override { + return newExprRef( + Z3Expr(Context, Z3_mk_bvsub(Context.Context, toZ3Expr(*LHS).AST, + toZ3Expr(*RHS).AST))); + } - uint64_t Value[2]; - // Force cast because Z3 defines __uint64 to be a unsigned long long - // type, which isn't compatible with a unsigned long type, even if they - // are the same size. - Z3_get_numeral_uint64(Z3Context::ZC, AST, - reinterpret_cast<__uint64 *>(&Value[0])); - if (Sort.getBitvectorSortSize() <= 64) { - Int = llvm::APSInt(llvm::APInt(Int.getBitWidth(), Value[0]), true); - } else if (Sort.getBitvectorSortSize() == 128) { - Z3Expr ASTHigh = Z3Expr(Z3_mk_extract(Z3Context::ZC, 127, 64, AST)); - Z3_get_numeral_uint64(Z3Context::ZC, AST, - reinterpret_cast<__uint64 *>(&Value[1])); - Int = llvm::APSInt(llvm::APInt(Int.getBitWidth(), Value), true); - } else { - assert(false && "Bitwidth not supported!"); - return false; - } - return true; - } - case Z3_BOOL_SORT: - if (useSemantics && Int.getBitWidth() < 1) { - assert(false && "Boolean type doesn't match!"); - return false; - } - Int = llvm::APSInt( - llvm::APInt(Int.getBitWidth(), - Z3_get_bool_value(Z3Context::ZC, AST) == Z3_L_TRUE ? 1 - : 0), - true); - return true; - } + SMTExprRef mkBVMul(const SMTExprRef &LHS, const SMTExprRef &RHS) override { + return newExprRef( + Z3Expr(Context, Z3_mk_bvmul(Context.Context, toZ3Expr(*LHS).AST, + toZ3Expr(*RHS).AST))); } - void Profile(llvm::FoldingSetNodeID &ID) const { - ID.AddInteger(Z3_get_ast_hash(Z3Context::ZC, 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))); } - bool operator<(const Z3Expr &Other) const { - llvm::FoldingSetNodeID ID1, ID2; - Profile(ID1); - Other.Profile(ID2); - return ID1 < ID2; + SMTExprRef mkBVURem(const SMTExprRef &LHS, const SMTExprRef &RHS) override { + return newExprRef( + Z3Expr(Context, Z3_mk_bvurem(Context.Context, toZ3Expr(*LHS).AST, + toZ3Expr(*RHS).AST))); } - /// Comparison of AST equality, not model equivalence. - bool operator==(const Z3Expr &Other) const { - assert(Z3_is_eq_sort(Z3Context::ZC, Z3_get_sort(Z3Context::ZC, AST), - Z3_get_sort(Z3Context::ZC, Other.AST)) && - "AST's must have the same sort"); - return Z3_is_eq_ast(Z3Context::ZC, AST, Other.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))); } - /// Override implicit move constructor for correct reference counting. - Z3Expr &operator=(const Z3Expr &Move) { - Z3_inc_ref(Z3Context::ZC, Move.AST); - Z3_dec_ref(Z3Context::ZC, AST); - AST = Move.AST; - return *this; + SMTExprRef mkBVUDiv(const SMTExprRef &LHS, const SMTExprRef &RHS) override { + return newExprRef( + Z3Expr(Context, Z3_mk_bvudiv(Context.Context, toZ3Expr(*LHS).AST, + toZ3Expr(*RHS).AST))); } - void print(raw_ostream &OS) const { - OS << Z3_ast_to_string(Z3Context::ZC, 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))); } - LLVM_DUMP_METHOD void dump() const { print(llvm::errs()); } -}; // end class Z3Expr + SMTExprRef mkBVAshr(const SMTExprRef &LHS, const SMTExprRef &RHS) override { + return newExprRef( + Z3Expr(Context, Z3_mk_bvashr(Context.Context, toZ3Expr(*LHS).AST, + toZ3Expr(*RHS).AST))); + } -class Z3Model { - Z3_model Model; + SMTExprRef mkBVLshr(const SMTExprRef &LHS, const SMTExprRef &RHS) override { + return newExprRef( + Z3Expr(Context, Z3_mk_bvlshr(Context.Context, toZ3Expr(*LHS).AST, + toZ3Expr(*RHS).AST))); + } -public: - Z3Model(Z3_model ZM) : Model(ZM) { Z3_model_inc_ref(Z3Context::ZC, Model); } + SMTExprRef mkBVXor(const SMTExprRef &LHS, const SMTExprRef &RHS) override { + return newExprRef( + Z3Expr(Context, Z3_mk_bvxor(Context.Context, toZ3Expr(*LHS).AST, + toZ3Expr(*RHS).AST))); + } - /// Override implicit copy constructor for correct reference counting. - Z3Model(const Z3Model &Copy) : Model(Copy.Model) { - Z3_model_inc_ref(Z3Context::ZC, Model); + SMTExprRef mkBVOr(const SMTExprRef &LHS, const SMTExprRef &RHS) override { + return newExprRef( + Z3Expr(Context, Z3_mk_bvor(Context.Context, toZ3Expr(*LHS).AST, + toZ3Expr(*RHS).AST))); } - /// Provide move constructor - Z3Model(Z3Model &&Move) : Model(nullptr) { *this = std::move(Move); } + SMTExprRef mkBVAnd(const SMTExprRef &LHS, const SMTExprRef &RHS) override { + return newExprRef( + Z3Expr(Context, Z3_mk_bvand(Context.Context, toZ3Expr(*LHS).AST, + toZ3Expr(*RHS).AST))); + } - /// Provide move assignment constructor - Z3Model &operator=(Z3Model &&Move) { - if (this != &Move) { - if (Model) - Z3_model_dec_ref(Z3Context::ZC, Model); - Model = Move.Model; - Move.Model = nullptr; - } - return *this; + SMTExprRef mkBVUlt(const SMTExprRef &LHS, const SMTExprRef &RHS) override { + return newExprRef( + Z3Expr(Context, Z3_mk_bvult(Context.Context, toZ3Expr(*LHS).AST, + toZ3Expr(*RHS).AST))); } - ~Z3Model() { - if (Model) - Z3_model_dec_ref(Z3Context::ZC, Model); + SMTExprRef mkBVSlt(const SMTExprRef &LHS, const SMTExprRef &RHS) override { + return newExprRef( + Z3Expr(Context, Z3_mk_bvslt(Context.Context, toZ3Expr(*LHS).AST, + toZ3Expr(*RHS).AST))); } - /// Given an expression, extract the value of this operand in the model. - bool getInterpretation(const Z3Expr &Exp, llvm::APSInt &Int) const { - Z3_func_decl Func = - Z3_get_app_decl(Z3Context::ZC, Z3_to_app(Z3Context::ZC, Exp.AST)); - if (Z3_model_has_interp(Z3Context::ZC, Model, Func) != Z3_L_TRUE) - return false; + SMTExprRef mkBVUgt(const SMTExprRef &LHS, const SMTExprRef &RHS) override { + return newExprRef( + Z3Expr(Context, Z3_mk_bvugt(Context.Context, toZ3Expr(*LHS).AST, + toZ3Expr(*RHS).AST))); + } - Z3_ast Assign = Z3_model_get_const_interp(Z3Context::ZC, Model, Func); - Z3Sort Sort = Z3Sort::getSort(Assign); - return Z3Expr::toAPSInt(Sort, Assign, Int, true); + SMTExprRef mkBVSgt(const SMTExprRef &LHS, const SMTExprRef &RHS) override { + return newExprRef( + Z3Expr(Context, Z3_mk_bvsgt(Context.Context, toZ3Expr(*LHS).AST, + toZ3Expr(*RHS).AST))); } - /// Given an expression, extract the value of this operand in the model. - bool getInterpretation(const Z3Expr &Exp, llvm::APFloat &Float) const { - Z3_func_decl Func = - Z3_get_app_decl(Z3Context::ZC, Z3_to_app(Z3Context::ZC, Exp.AST)); - if (Z3_model_has_interp(Z3Context::ZC, Model, Func) != Z3_L_TRUE) - return false; + SMTExprRef mkBVUle(const SMTExprRef &LHS, const SMTExprRef &RHS) override { + return newExprRef( + Z3Expr(Context, Z3_mk_bvule(Context.Context, toZ3Expr(*LHS).AST, + toZ3Expr(*RHS).AST))); + } - Z3_ast Assign = Z3_model_get_const_interp(Z3Context::ZC, Model, Func); - Z3Sort Sort = Z3Sort::getSort(Assign); - return Z3Expr::toAPFloat(Sort, Assign, Float, true); + SMTExprRef mkBVSle(const SMTExprRef &LHS, const SMTExprRef &RHS) override { + return newExprRef( + Z3Expr(Context, Z3_mk_bvsle(Context.Context, toZ3Expr(*LHS).AST, + toZ3Expr(*RHS).AST))); } - void print(raw_ostream &OS) const { - OS << Z3_model_to_string(Z3Context::ZC, Model); + SMTExprRef mkBVUge(const SMTExprRef &LHS, const SMTExprRef &RHS) override { + return newExprRef( + Z3Expr(Context, Z3_mk_bvuge(Context.Context, toZ3Expr(*LHS).AST, + toZ3Expr(*RHS).AST))); } - LLVM_DUMP_METHOD void dump() const { print(llvm::errs()); } -}; // end class Z3Model + SMTExprRef mkBVSge(const SMTExprRef &LHS, const SMTExprRef &RHS) override { + return newExprRef( + Z3Expr(Context, Z3_mk_bvsge(Context.Context, toZ3Expr(*LHS).AST, + toZ3Expr(*RHS).AST))); + } -class Z3Solver { - friend class Z3ConstraintManager; + 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))); + } - Z3_solver Solver; + 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))); + } - Z3Solver(Z3_solver ZS) : Solver(ZS) { - Z3_solver_inc_ref(Z3Context::ZC, Solver); + SMTExprRef mkEqual(const SMTExprRef &LHS, const SMTExprRef &RHS) override { + return newExprRef( + Z3Expr(Context, Z3_mk_eq(Context.Context, toZ3Expr(*LHS).AST, + toZ3Expr(*RHS).AST))); } -public: - /// Override implicit copy constructor for correct reference counting. - Z3Solver(const Z3Solver &Copy) : Solver(Copy.Solver) { - Z3_solver_inc_ref(Z3Context::ZC, Solver); + SMTExprRef mkFPNeg(const SMTExprRef &Exp) override { + return newExprRef( + Z3Expr(Context, Z3_mk_fpa_neg(Context.Context, toZ3Expr(*Exp).AST))); } - /// Provide move constructor - Z3Solver(Z3Solver &&Move) : Solver(nullptr) { *this = std::move(Move); } + SMTExprRef mkFPIsInfinite(const SMTExprRef &Exp) override { + return newExprRef(Z3Expr( + Context, Z3_mk_fpa_is_infinite(Context.Context, toZ3Expr(*Exp).AST))); + } - /// Provide move assignment constructor - Z3Solver &operator=(Z3Solver &&Move) { - if (this != &Move) { - if (Solver) - Z3_solver_dec_ref(Z3Context::ZC, Solver); - Solver = Move.Solver; - Move.Solver = nullptr; - } - return *this; + SMTExprRef mkFPIsNaN(const SMTExprRef &Exp) override { + return newExprRef( + Z3Expr(Context, Z3_mk_fpa_is_nan(Context.Context, toZ3Expr(*Exp).AST))); } - ~Z3Solver() { - if (Solver) - Z3_solver_dec_ref(Z3Context::ZC, Solver); + SMTExprRef mkFPIsNormal(const SMTExprRef &Exp) override { + return newExprRef(Z3Expr( + Context, Z3_mk_fpa_is_normal(Context.Context, toZ3Expr(*Exp).AST))); } - /// Given a constraint, add it to the solver - void addConstraint(const Z3Expr &Exp) { - Z3_solver_assert(Z3Context::ZC, Solver, Exp.AST); + SMTExprRef mkFPIsZero(const SMTExprRef &Exp) override { + return newExprRef(Z3Expr( + Context, Z3_mk_fpa_is_zero(Context.Context, toZ3Expr(*Exp).AST))); } - /// Given a program state, construct the logical conjunction and add it to - /// the solver - void addStateConstraints(ProgramStateRef State) { - // TODO: Don't add all the constraints, only the relevant ones - ConstraintZ3Ty CZ = State->get<ConstraintZ3>(); - ConstraintZ3Ty::iterator I = CZ.begin(), IE = CZ.end(); + 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))); + } - // Construct the logical AND of all the constraints - if (I != IE) { - std::vector<Z3_ast> ASTs; + 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))); + } - while (I != IE) - ASTs.push_back(I++->second.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))); + } - Z3Expr Conj = Z3Expr::fromNBinOp(BO_LAnd, ASTs); - addConstraint(Conj); - } + 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))); } - /// Check if the constraints are satisfiable - Z3_lbool check() { return Z3_solver_check(Z3Context::ZC, Solver); } + 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))); + } - /// Push the current solver state - void push() { return Z3_solver_push(Z3Context::ZC, Solver); } + 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))); + } - /// Pop the previous solver state - void pop(unsigned NumStates = 1) { - assert(Z3_solver_get_num_scopes(Z3Context::ZC, Solver) >= NumStates); - return Z3_solver_pop(Z3Context::ZC, Solver, NumStates); + 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))); } - /// Get a model from the solver. Caller should check the model is - /// satisfiable. - Z3Model getModel() { - return Z3Model(Z3_solver_get_model(Z3Context::ZC, Solver)); + 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))); } - /// Reset the solver and remove all constraints. - void reset() { Z3_solver_reset(Z3Context::ZC, Solver); } -}; // end class Z3Solver + 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))); + } -void Z3ErrorHandler(Z3_context Context, Z3_error_code Error) { - llvm::report_fatal_error("Z3 error: " + - llvm::Twine(Z3_get_error_msg_ex(Context, Error))); -} + 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))); + } -class Z3ConstraintManager : public SimpleConstraintManager { - Z3Context Context; - mutable Z3Solver Solver; + 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))); + } -public: - Z3ConstraintManager(SubEngine *SE, SValBuilder &SB) - : SimpleConstraintManager(SE, SB), - Solver(Z3_mk_simple_solver(Z3Context::ZC)) { - Z3_set_error_handler(Z3Context::ZC, Z3ErrorHandler); + SMTExprRef mkBVSignExt(unsigned i, const SMTExprRef &Exp) override { + return newExprRef(Z3Expr( + Context, Z3_mk_sign_ext(Context.Context, i, toZ3Expr(*Exp).AST))); } - //===------------------------------------------------------------------===// - // Implementation for interface from ConstraintManager. - //===------------------------------------------------------------------===// + SMTExprRef mkBVZeroExt(unsigned i, const SMTExprRef &Exp) override { + return newExprRef(Z3Expr( + Context, Z3_mk_zero_ext(Context.Context, i, toZ3Expr(*Exp).AST))); + } - bool canReasonAbout(SVal X) const override; + SMTExprRef mkBVExtract(unsigned High, unsigned Low, + const SMTExprRef &Exp) override { + return newExprRef(Z3Expr(Context, Z3_mk_extract(Context.Context, High, Low, + toZ3Expr(*Exp).AST))); + } - ConditionTruthVal checkNull(ProgramStateRef State, SymbolRef Sym) override; - - const llvm::APSInt *getSymVal(ProgramStateRef State, - SymbolRef Sym) const override; - - ProgramStateRef removeDeadBindings(ProgramStateRef St, - SymbolReaper &SymReaper) override; - - void print(ProgramStateRef St, raw_ostream &Out, const char *nl, - const char *sep) override; - - //===------------------------------------------------------------------===// - // Implementation for interface from SimpleConstraintManager. - //===------------------------------------------------------------------===// - - ProgramStateRef assumeSym(ProgramStateRef state, SymbolRef Sym, - bool Assumption) override; - - ProgramStateRef assumeSymInclusiveRange(ProgramStateRef State, SymbolRef Sym, - const llvm::APSInt &From, - const llvm::APSInt &To, - bool InRange) override; - - ProgramStateRef assumeSymUnsupported(ProgramStateRef State, SymbolRef Sym, - bool Assumption) override; - -private: - //===------------------------------------------------------------------===// - // Internal implementation. - //===------------------------------------------------------------------===// - - // Check whether a new model is satisfiable, and update the program state. - ProgramStateRef assumeZ3Expr(ProgramStateRef State, SymbolRef Sym, - const Z3Expr &Exp); - - // Generate and check a Z3 model, using the given constraint. - Z3_lbool checkZ3Model(ProgramStateRef State, const Z3Expr &Exp) const; - - // Generate a Z3Expr that represents the given symbolic expression. - // Sets the hasComparison parameter if the expression has a comparison - // operator. - // Sets the RetTy parameter to the final return type after promotions and - // casts. - Z3Expr getZ3Expr(SymbolRef Sym, QualType *RetTy = nullptr, - bool *hasComparison = nullptr) const; - - // Generate a Z3Expr that takes the logical not of an expression. - Z3Expr getZ3NotExpr(const Z3Expr &Exp) const; - - // Generate a Z3Expr that compares the expression to zero. - Z3Expr getZ3ZeroExpr(const Z3Expr &Exp, QualType RetTy, - bool Assumption) const; - - // Recursive implementation to unpack and generate symbolic expression. - // Sets the hasComparison and RetTy parameters. See getZ3Expr(). - Z3Expr getZ3SymExpr(SymbolRef Sym, QualType *RetTy, - bool *hasComparison) const; - - // Wrapper to generate Z3Expr from SymbolData. - Z3Expr getZ3DataExpr(const SymbolID ID, QualType Ty) const; - - // Wrapper to generate Z3Expr from SymbolCast. - Z3Expr getZ3CastExpr(const Z3Expr &Exp, QualType FromTy, QualType Ty) const; - - // Wrapper to generate Z3Expr from BinarySymExpr. - // Sets the hasComparison and RetTy parameters. See getZ3Expr(). - Z3Expr getZ3SymBinExpr(const BinarySymExpr *BSE, bool *hasComparison, - QualType *RetTy) const; - - // Wrapper to generate Z3Expr from unpacked binary symbolic expression. - // Sets the RetTy parameter. See getZ3Expr(). - Z3Expr getZ3BinExpr(const Z3Expr &LHS, QualType LTy, - BinaryOperator::Opcode Op, const Z3Expr &RHS, - QualType RTy, QualType *RetTy) const; - - //===------------------------------------------------------------------===// - // Helper functions. - //===------------------------------------------------------------------===// - - // Recover the QualType of an APSInt. - // TODO: Refactor to put elsewhere - QualType getAPSIntType(const llvm::APSInt &Int) const; - - // Perform implicit type conversion on binary symbolic expressions. - // May modify all input parameters. - // TODO: Refactor to use built-in conversion functions - void doTypeConversion(Z3Expr &LHS, Z3Expr &RHS, QualType <y, - QualType &RTy) const; - - // Perform implicit integer type conversion. - // May modify all input parameters. - // TODO: Refactor to use Sema::handleIntegerConversion() - template <typename T, - T(doCast)(const T &, QualType, uint64_t, QualType, uint64_t)> - void doIntTypeConversion(T &LHS, QualType <y, T &RHS, QualType &RTy) const; - - // Perform implicit floating-point type conversion. - // May modify all input parameters. - // TODO: Refactor to use Sema::handleFloatConversion() - template <typename T, - T(doCast)(const T &, QualType, uint64_t, QualType, uint64_t)> - void doFloatTypeConversion(T &LHS, QualType <y, T &RHS, - QualType &RTy) const; - - // Callback function for doCast parameter on APSInt type. - static llvm::APSInt castAPSInt(const llvm::APSInt &V, QualType ToTy, - uint64_t ToWidth, QualType FromTy, - uint64_t FromWidth); -}; // end class Z3ConstraintManager + SMTExprRef mkBVConcat(const SMTExprRef &LHS, const SMTExprRef &RHS) override { + return newExprRef( + Z3Expr(Context, Z3_mk_concat(Context.Context, toZ3Expr(*LHS).AST, + toZ3Expr(*RHS).AST))); + } -Z3_context Z3Context::ZC; + 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))); + } -} // end anonymous namespace + SMTExprRef mkFPtoSBV(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))); + } -ProgramStateRef Z3ConstraintManager::assumeSym(ProgramStateRef State, - SymbolRef Sym, bool Assumption) { - QualType RetTy; - bool hasComparison; + SMTExprRef mkFPtoUBV(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))); + } - Z3Expr Exp = getZ3Expr(Sym, &RetTy, &hasComparison); - // Create zero comparison for implicit boolean cast, with reversed assumption - if (!hasComparison && !RetTy->isBooleanType()) - return assumeZ3Expr(State, Sym, getZ3ZeroExpr(Exp, RetTy, !Assumption)); + SMTExprRef mkSBVtoFP(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))); + } - return assumeZ3Expr(State, Sym, Assumption ? Exp : getZ3NotExpr(Exp)); -} + SMTExprRef mkUBVtoFP(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))); + } -ProgramStateRef Z3ConstraintManager::assumeSymInclusiveRange( - ProgramStateRef State, SymbolRef Sym, const llvm::APSInt &From, - const llvm::APSInt &To, bool InRange) { - QualType RetTy; - // The expression may be casted, so we cannot call getZ3DataExpr() directly - Z3Expr Exp = getZ3Expr(Sym, &RetTy); - - assert((getAPSIntType(From) == getAPSIntType(To)) && - "Range values have different types!"); - QualType RTy = getAPSIntType(From); - bool isSignedTy = RetTy->isSignedIntegerOrEnumerationType(); - Z3Expr FromExp = Z3Expr::fromAPSInt(From); - Z3Expr ToExp = Z3Expr::fromAPSInt(To); - - // Construct single (in)equality - if (From == To) - return assumeZ3Expr(State, Sym, - getZ3BinExpr(Exp, RetTy, InRange ? BO_EQ : BO_NE, - FromExp, RTy, nullptr)); - - // Construct two (in)equalities, and a logical and/or - Z3Expr LHS = - getZ3BinExpr(Exp, RetTy, InRange ? BO_GE : BO_LT, FromExp, RTy, nullptr); - Z3Expr RHS = - getZ3BinExpr(Exp, RetTy, InRange ? BO_LE : BO_GT, ToExp, RTy, nullptr); - return assumeZ3Expr( - State, Sym, - Z3Expr::fromBinOp(LHS, InRange ? BO_LAnd : BO_LOr, RHS, isSignedTy)); -} + SMTExprRef mkBoolean(const bool b) override { + return newExprRef(Z3Expr(Context, b ? Z3_mk_true(Context.Context) + : Z3_mk_false(Context.Context))); + } -ProgramStateRef Z3ConstraintManager::assumeSymUnsupported(ProgramStateRef State, - SymbolRef Sym, - bool Assumption) { - // Skip anything that is unsupported - return State; -} + 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))); + } -bool Z3ConstraintManager::canReasonAbout(SVal X) const { - const TargetInfo &TI = getBasicVals().getContext().getTargetInfo(); + SMTExprRef mkFloat(const llvm::APFloat Float) override { + SMTSortRef Sort = + getFloatSort(llvm::APFloat::semanticsSizeInBits(Float.getSemantics())); - Optional<nonloc::SymbolVal> SymVal = X.getAs<nonloc::SymbolVal>(); - if (!SymVal) - return true; + 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))); + } - const SymExpr *Sym = SymVal->getSymbol(); - do { - QualType Ty = Sym->getType(); + 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))); + } - // Complex types are not modeled - if (Ty->isComplexType() || Ty->isComplexIntegerType()) - return false; + 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)); + } - // Non-IEEE 754 floating-point types are not modeled - if ((Ty->isSpecificBuiltinType(BuiltinType::LongDouble) && - (&TI.getLongDoubleFormat() == &llvm::APFloat::x87DoubleExtended() || - &TI.getLongDoubleFormat() == &llvm::APFloat::PPCDoubleDouble()))) - return false; + bool getBoolean(const SMTExprRef &Exp) override { + return Z3_get_bool_value(Context.Context, toZ3Expr(*Exp).AST) == Z3_L_TRUE; + } - if (isa<SymbolData>(Sym)) { - break; - } else if (const SymbolCast *SC = dyn_cast<SymbolCast>(Sym)) { - Sym = SC->getOperand(); - } else if (const BinarySymExpr *BSE = dyn_cast<BinarySymExpr>(Sym)) { - if (const SymIntExpr *SIE = dyn_cast<SymIntExpr>(BSE)) { - Sym = SIE->getLHS(); - } else if (const IntSymExpr *ISE = dyn_cast<IntSymExpr>(BSE)) { - Sym = ISE->getRHS(); - } else if (const SymSymExpr *SSM = dyn_cast<SymSymExpr>(BSE)) { - return canReasonAbout(nonloc::SymbolVal(SSM->getLHS())) && - canReasonAbout(nonloc::SymbolVal(SSM->getRHS())); - } else { - llvm_unreachable("Unsupported binary expression to reason about!"); - } - } else { - llvm_unreachable("Unsupported expression to reason about!"); - } - } while (Sym); + SMTExprRef getFloatRoundingMode() override { + // TODO: Don't assume nearest ties to even rounding mode + return newExprRef(Z3Expr(Context, Z3_mk_fpa_rne(Context.Context))); + } - return true; -} + SMTExprRef fromData(const SymbolID ID, const QualType &Ty, + uint64_t BitWidth) override { + llvm::Twine Name = "$" + llvm::Twine(ID); + return mkSymbol(Name.str().c_str(), mkSort(Ty, BitWidth)); + } + + SMTExprRef fromBoolean(const bool Bool) override { + Z3_ast AST = + Bool ? Z3_mk_true(Context.Context) : Z3_mk_false(Context.Context); + return newExprRef(Z3Expr(Context, AST)); + } + + SMTExprRef fromAPFloat(const llvm::APFloat &Float) override { + SMTSortRef Sort = + getFloatSort(llvm::APFloat::semanticsSizeInBits(Float.getSemantics())); -ConditionTruthVal Z3ConstraintManager::checkNull(ProgramStateRef State, - SymbolRef Sym) { - QualType RetTy; - // The expression may be casted, so we cannot call getZ3DataExpr() directly - Z3Expr VarExp = getZ3Expr(Sym, &RetTy); - Z3Expr Exp = getZ3ZeroExpr(VarExp, RetTy, true); - // Negate the constraint - Z3Expr NotExp = getZ3ZeroExpr(VarExp, RetTy, false); + llvm::APSInt Int = llvm::APSInt(Float.bitcastToAPInt(), false); + SMTExprRef Z3Int = fromAPSInt(Int); + return newExprRef(Z3Expr( + Context, Z3_mk_fpa_to_fp_bv(Context.Context, toZ3Expr(*Z3Int).AST, + toZ3Sort(*Sort).Sort))); + } - Solver.reset(); - Solver.addStateConstraints(State); + SMTExprRef fromAPSInt(const llvm::APSInt &Int) override { + SMTSortRef Sort = getBitvectorSort(Int.getBitWidth()); + Z3_ast AST = Z3_mk_numeral(Context.Context, Int.toString(10).c_str(), + toZ3Sort(*Sort).Sort); + return newExprRef(Z3Expr(Context, AST)); + } - Solver.push(); - Solver.addConstraint(Exp); - Z3_lbool isSat = Solver.check(); + SMTExprRef fromInt(const char *Int, uint64_t BitWidth) override { + SMTSortRef Sort = getBitvectorSort(BitWidth); + Z3_ast AST = Z3_mk_numeral(Context.Context, Int, toZ3Sort(*Sort).Sort); + return newExprRef(Z3Expr(Context, AST)); + } - Solver.pop(); - Solver.addConstraint(NotExp); - Z3_lbool isNotSat = Solver.check(); + bool toAPFloat(const SMTSortRef &Sort, const SMTExprRef &AST, + llvm::APFloat &Float, bool useSemantics) { + assert(Sort->isFloatSort() && "Unsupported sort to floating-point!"); - // Zero is the only possible solution - if (isSat == Z3_L_TRUE && isNotSat == Z3_L_FALSE) + 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; - // Zero is not a solution - else if (isSat == Z3_L_FALSE && isNotSat == Z3_L_TRUE) - return false; + } - // Zero may be a solution - return ConditionTruthVal(); -} + 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; + } -const llvm::APSInt *Z3ConstraintManager::getSymVal(ProgramStateRef State, - SymbolRef Sym) const { - BasicValueFactory &BV = getBasicVals(); - ASTContext &Ctx = BV.getContext(); + // 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; + } - if (const SymbolData *SD = dyn_cast<SymbolData>(Sym)) { - QualType Ty = Sym->getType(); - assert(!Ty->isRealFloatingType()); - llvm::APSInt Value(Ctx.getTypeSize(Ty), - !Ty->isSignedIntegerOrEnumerationType()); - - Z3Expr Exp = getZ3DataExpr(SD->getSymbolID(), Ty); - - Solver.reset(); - Solver.addStateConstraints(State); - - // Constraints are unsatisfiable - if (Solver.check() != Z3_L_TRUE) - return nullptr; - - Z3Model Model = Solver.getModel(); - // Model does not assign interpretation - if (!Model.getInterpretation(Exp, Value)) - return nullptr; - - // A value has been obtained, check if it is the only value - Z3Expr NotExp = Z3Expr::fromBinOp( - Exp, BO_NE, - Ty->isBooleanType() ? Z3Expr::fromBoolean(Value.getBoolValue()) - : Z3Expr::fromAPSInt(Value), - false); - - Solver.addConstraint(NotExp); - if (Solver.check() == Z3_L_TRUE) - return nullptr; - - // This is the only solution, store it - return &BV.getValue(Value); - } else if (const SymbolCast *SC = dyn_cast<SymbolCast>(Sym)) { - SymbolRef CastSym = SC->getOperand(); - QualType CastTy = SC->getType(); - // Skip the void type - if (CastTy->isVoidType()) - return nullptr; - - const llvm::APSInt *Value; - if (!(Value = getSymVal(State, CastSym))) - return nullptr; - return &BV.Convert(SC->getType(), *Value); - } else if (const BinarySymExpr *BSE = dyn_cast<BinarySymExpr>(Sym)) { - const llvm::APSInt *LHS, *RHS; - if (const SymIntExpr *SIE = dyn_cast<SymIntExpr>(BSE)) { - LHS = getSymVal(State, SIE->getLHS()); - RHS = &SIE->getRHS(); - } else if (const IntSymExpr *ISE = dyn_cast<IntSymExpr>(BSE)) { - LHS = &ISE->getLHS(); - RHS = getSymVal(State, ISE->getRHS()); - } else if (const SymSymExpr *SSM = dyn_cast<SymSymExpr>(BSE)) { - // Early termination to avoid expensive call - LHS = getSymVal(State, SSM->getLHS()); - RHS = LHS ? getSymVal(State, SSM->getRHS()) : nullptr; - } else { - llvm_unreachable("Unsupported binary expression to get symbol value!"); + assert(false && "Bitwidth not supported!"); + return false; } - if (!LHS || !RHS) - return nullptr; + if (Sort->isBooleanSort()) { + if (useSemantics && Int.getBitWidth() < 1) { + assert(false && "Boolean type doesn't match!"); + return false; + } - llvm::APSInt ConvertedLHS = *LHS, ConvertedRHS = *RHS; - QualType LTy = getAPSIntType(*LHS), RTy = getAPSIntType(*RHS); - doIntTypeConversion<llvm::APSInt, Z3ConstraintManager::castAPSInt>( - ConvertedLHS, LTy, ConvertedRHS, RTy); - return BV.evalAPSInt(BSE->getOpcode(), ConvertedLHS, ConvertedRHS); - } + Int = llvm::APSInt(llvm::APInt(Int.getBitWidth(), getBoolean(AST)), + Int.isUnsigned()); + return true; + } - llvm_unreachable("Unsupported expression to get symbol value!"); -} + llvm_unreachable("Unsupported sort to integer!"); + } -ProgramStateRef -Z3ConstraintManager::removeDeadBindings(ProgramStateRef State, - SymbolReaper &SymReaper) { - ConstraintZ3Ty CZ = State->get<ConstraintZ3>(); - ConstraintZ3Ty::Factory &CZFactory = State->get_context<ConstraintZ3>(); + bool getInterpretation(const SMTExprRef &Exp, llvm::APSInt &Int) override { + Z3Model Model = getModel(); + 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; - for (ConstraintZ3Ty::iterator I = CZ.begin(), E = CZ.end(); I != E; ++I) { - if (SymReaper.maybeDead(I->first)) - CZ = CZFactory.remove(CZ, *I); + 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); } - return State->set<ConstraintZ3>(CZ); -} + bool getInterpretation(const SMTExprRef &Exp, llvm::APFloat &Float) override { + Z3Model Model = getModel(); + 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; -//===------------------------------------------------------------------===// -// Internal implementation. -//===------------------------------------------------------------------===// + 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); + } -ProgramStateRef Z3ConstraintManager::assumeZ3Expr(ProgramStateRef State, - SymbolRef Sym, - const Z3Expr &Exp) { - // Check the model, avoid simplifying AST to save time - if (checkZ3Model(State, Exp) == Z3_L_TRUE) - return State->add<ConstraintZ3>(std::make_pair(Sym, Exp)); + ConditionTruthVal check() const override { + Z3_lbool res = Z3_solver_check(Context.Context, Solver); + if (res == Z3_L_TRUE) + return true; - return nullptr; -} + if (res == Z3_L_FALSE) + return false; -Z3_lbool Z3ConstraintManager::checkZ3Model(ProgramStateRef State, - const Z3Expr &Exp) const { - Solver.reset(); - Solver.addConstraint(Exp); - Solver.addStateConstraints(State); - return Solver.check(); -} + return ConditionTruthVal(); + } -Z3Expr Z3ConstraintManager::getZ3Expr(SymbolRef Sym, QualType *RetTy, - bool *hasComparison) const { - if (hasComparison) { - *hasComparison = false; + 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); } - return getZ3SymExpr(Sym, RetTy, hasComparison); -} + /// Get a model from the solver. Caller should check the model is + /// satisfiable. + Z3Model getModel() { + return Z3Model(Context, Z3_solver_get_model(Context.Context, Solver)); + } -Z3Expr Z3ConstraintManager::getZ3NotExpr(const Z3Expr &Exp) const { - return Z3Expr::fromUnOp(UO_LNot, Exp); -} + /// Reset the solver and remove all constraints. + void reset() const override { Z3_solver_reset(Context.Context, Solver); } -Z3Expr Z3ConstraintManager::getZ3ZeroExpr(const Z3Expr &Exp, QualType Ty, - bool Assumption) const { - ASTContext &Ctx = getBasicVals().getContext(); - if (Ty->isRealFloatingType()) { - llvm::APFloat Zero = llvm::APFloat::getZero(Ctx.getFloatTypeSemantics(Ty)); - return Z3Expr::fromFloatBinOp(Exp, Assumption ? BO_EQ : BO_NE, - Z3Expr::fromAPFloat(Zero)); - } else if (Ty->isIntegralOrEnumerationType() || Ty->isAnyPointerType() || - Ty->isBlockPointerType() || Ty->isReferenceType()) { - bool isSigned = Ty->isSignedIntegerOrEnumerationType(); - // Skip explicit comparison for boolean types - if (Ty->isBooleanType()) - return Assumption ? getZ3NotExpr(Exp) : Exp; - return Z3Expr::fromBinOp(Exp, Assumption ? BO_EQ : BO_NE, - Z3Expr::fromInt("0", Ctx.getTypeSize(Ty)), - isSigned); - } - - llvm_unreachable("Unsupported type for zero value!"); -} + void print(raw_ostream &OS) const override { + OS << Z3_solver_to_string(Context.Context, Solver); + } +}; // end class Z3Solver -Z3Expr Z3ConstraintManager::getZ3SymExpr(SymbolRef Sym, QualType *RetTy, - bool *hasComparison) const { - if (const SymbolData *SD = dyn_cast<SymbolData>(Sym)) { - if (RetTy) - *RetTy = Sym->getType(); - - return getZ3DataExpr(SD->getSymbolID(), Sym->getType()); - } else if (const SymbolCast *SC = dyn_cast<SymbolCast>(Sym)) { - if (RetTy) - *RetTy = Sym->getType(); - - QualType FromTy; - Z3Expr Exp = getZ3SymExpr(SC->getOperand(), &FromTy, hasComparison); - // Casting an expression with a comparison invalidates it. Note that this - // must occur after the recursive call above. - // e.g. (signed char) (x > 0) - if (hasComparison) - *hasComparison = false; - return getZ3CastExpr(Exp, FromTy, Sym->getType()); - } else if (const BinarySymExpr *BSE = dyn_cast<BinarySymExpr>(Sym)) { - Z3Expr Exp = getZ3SymBinExpr(BSE, hasComparison, RetTy); - // Set the hasComparison parameter, in post-order traversal order. - if (hasComparison) - *hasComparison = BinaryOperator::isComparisonOp(BSE->getOpcode()); - return Exp; - } - - llvm_unreachable("Unsupported SymbolRef type!"); -} +class Z3ConstraintManager : public SMTConstraintManager { + SMTSolverRef Solver = CreateZ3Solver(); -Z3Expr Z3ConstraintManager::getZ3DataExpr(const SymbolID ID, - QualType Ty) const { - ASTContext &Ctx = getBasicVals().getContext(); - return Z3Expr::fromData(ID, Ty->isBooleanType(), Ty->isRealFloatingType(), - Ctx.getTypeSize(Ty)); -} +public: + Z3ConstraintManager(SubEngine *SE, SValBuilder &SB) + : SMTConstraintManager(SE, SB, Solver) {} -Z3Expr Z3ConstraintManager::getZ3CastExpr(const Z3Expr &Exp, QualType FromTy, - QualType ToTy) const { - ASTContext &Ctx = getBasicVals().getContext(); - return Z3Expr::fromCast(Exp, ToTy, Ctx.getTypeSize(ToTy), FromTy, - Ctx.getTypeSize(FromTy)); -} + void addStateConstraints(ProgramStateRef State) const override { + // TODO: Don't add all the constraints, only the relevant ones + ConstraintZ3Ty CZ = State->get<ConstraintZ3>(); + ConstraintZ3Ty::iterator I = CZ.begin(), IE = CZ.end(); -Z3Expr Z3ConstraintManager::getZ3SymBinExpr(const BinarySymExpr *BSE, - bool *hasComparison, - QualType *RetTy) const { - QualType LTy, RTy; - BinaryOperator::Opcode Op = BSE->getOpcode(); - - if (const SymIntExpr *SIE = dyn_cast<SymIntExpr>(BSE)) { - RTy = getAPSIntType(SIE->getRHS()); - Z3Expr LHS = getZ3SymExpr(SIE->getLHS(), <y, hasComparison); - Z3Expr RHS = Z3Expr::fromAPSInt(SIE->getRHS()); - return getZ3BinExpr(LHS, LTy, Op, RHS, RTy, RetTy); - } else if (const IntSymExpr *ISE = dyn_cast<IntSymExpr>(BSE)) { - LTy = getAPSIntType(ISE->getLHS()); - Z3Expr LHS = Z3Expr::fromAPSInt(ISE->getLHS()); - Z3Expr RHS = getZ3SymExpr(ISE->getRHS(), &RTy, hasComparison); - return getZ3BinExpr(LHS, LTy, Op, RHS, RTy, RetTy); - } else if (const SymSymExpr *SSM = dyn_cast<SymSymExpr>(BSE)) { - Z3Expr LHS = getZ3SymExpr(SSM->getLHS(), <y, hasComparison); - Z3Expr RHS = getZ3SymExpr(SSM->getRHS(), &RTy, hasComparison); - return getZ3BinExpr(LHS, LTy, Op, RHS, RTy, RetTy); - } else { - llvm_unreachable("Unsupported BinarySymExpr type!"); - } -} + // Construct the logical AND of all the constraints + if (I != IE) { + std::vector<SMTExprRef> ASTs; -Z3Expr Z3ConstraintManager::getZ3BinExpr(const Z3Expr &LHS, QualType LTy, - BinaryOperator::Opcode Op, - const Z3Expr &RHS, QualType RTy, - QualType *RetTy) const { - Z3Expr NewLHS = LHS; - Z3Expr NewRHS = RHS; - doTypeConversion(NewLHS, NewRHS, LTy, RTy); - // Update the return type parameter if the output type has changed. - if (RetTy) { - // A boolean result can be represented as an integer type in C/C++, but at - // this point we only care about the Z3 type. Set it as a boolean type to - // avoid subsequent Z3 errors. - if (BinaryOperator::isComparisonOp(Op) || BinaryOperator::isLogicalOp(Op)) { - ASTContext &Ctx = getBasicVals().getContext(); - *RetTy = Ctx.BoolTy; - } else { - *RetTy = LTy; - } + SMTExprRef Constraint = Solver->newExprRef(I++->second); + while (I != IE) { + Constraint = Solver->mkAnd(Constraint, Solver->newExprRef(I++->second)); + } - // If the two operands are pointers and the operation is a subtraction, the - // result is of type ptrdiff_t, which is signed - if (LTy->isAnyPointerType() && LTy == RTy && Op == BO_Sub) { - ASTContext &Ctx = getBasicVals().getContext(); - *RetTy = Ctx.getIntTypeForBitwidth(Ctx.getTypeSize(LTy), true); + Solver->addConstraint(Constraint); } } - return LTy->isRealFloatingType() - ? Z3Expr::fromFloatBinOp(NewLHS, Op, NewRHS) - : Z3Expr::fromBinOp(NewLHS, Op, NewRHS, - LTy->isSignedIntegerOrEnumerationType()); -} + bool canReasonAbout(SVal X) const override { + const TargetInfo &TI = getBasicVals().getContext().getTargetInfo(); -//===------------------------------------------------------------------===// -// Helper functions. -//===------------------------------------------------------------------===// + Optional<nonloc::SymbolVal> SymVal = X.getAs<nonloc::SymbolVal>(); + if (!SymVal) + return true; -QualType Z3ConstraintManager::getAPSIntType(const llvm::APSInt &Int) const { - ASTContext &Ctx = getBasicVals().getContext(); - return Ctx.getIntTypeForBitwidth(Int.getBitWidth(), Int.isSigned()); -} + const SymExpr *Sym = SymVal->getSymbol(); + QualType Ty = Sym->getType(); -void Z3ConstraintManager::doTypeConversion(Z3Expr &LHS, Z3Expr &RHS, - QualType <y, QualType &RTy) const { - ASTContext &Ctx = getBasicVals().getContext(); - - // Perform type conversion - if (LTy->isIntegralOrEnumerationType() && - RTy->isIntegralOrEnumerationType()) { - if (LTy->isArithmeticType() && RTy->isArithmeticType()) - return doIntTypeConversion<Z3Expr, Z3Expr::fromCast>(LHS, LTy, RHS, RTy); - } else if (LTy->isRealFloatingType() || RTy->isRealFloatingType()) { - return doFloatTypeConversion<Z3Expr, Z3Expr::fromCast>(LHS, LTy, RHS, RTy); - } else if ((LTy->isAnyPointerType() || RTy->isAnyPointerType()) || - (LTy->isBlockPointerType() || RTy->isBlockPointerType()) || - (LTy->isReferenceType() || RTy->isReferenceType())) { - // TODO: Refactor to Sema::FindCompositePointerType(), and - // Sema::CheckCompareOperands(). - - uint64_t LBitWidth = Ctx.getTypeSize(LTy); - uint64_t RBitWidth = Ctx.getTypeSize(RTy); - - // Cast the non-pointer type to the pointer type. - // TODO: Be more strict about this. - if ((LTy->isAnyPointerType() ^ RTy->isAnyPointerType()) || - (LTy->isBlockPointerType() ^ RTy->isBlockPointerType()) || - (LTy->isReferenceType() ^ RTy->isReferenceType())) { - if (LTy->isNullPtrType() || LTy->isBlockPointerType() || - LTy->isReferenceType()) { - LHS = Z3Expr::fromCast(LHS, RTy, RBitWidth, LTy, LBitWidth); - LTy = RTy; - } else { - RHS = Z3Expr::fromCast(RHS, LTy, LBitWidth, RTy, RBitWidth); - RTy = LTy; - } - } + // Complex types are not modeled + if (Ty->isComplexType() || Ty->isComplexIntegerType()) + return false; - // Cast the void pointer type to the non-void pointer type. - // For void types, this assumes that the casted value is equal to the value - // of the original pointer, and does not account for alignment requirements. - if (LTy->isVoidPointerType() ^ RTy->isVoidPointerType()) { - assert((Ctx.getTypeSize(LTy) == Ctx.getTypeSize(RTy)) && - "Pointer types have different bitwidths!"); - if (RTy->isVoidPointerType()) - RTy = LTy; - else - LTy = RTy; - } + // Non-IEEE 754 floating-point types are not modeled + if ((Ty->isSpecificBuiltinType(BuiltinType::LongDouble) && + (&TI.getLongDoubleFormat() == &llvm::APFloat::x87DoubleExtended() || + &TI.getLongDoubleFormat() == &llvm::APFloat::PPCDoubleDouble()))) + return false; - if (LTy == RTy) - return; - } + if (isa<SymbolData>(Sym)) + return true; - // Fallback: for the solver, assume that these types don't really matter - if ((LTy.getCanonicalType() == RTy.getCanonicalType()) || - (LTy->isObjCObjectPointerType() && RTy->isObjCObjectPointerType())) { - LTy = RTy; - return; - } + SValBuilder &SVB = getSValBuilder(); - // TODO: Refine behavior for invalid type casts -} + if (const SymbolCast *SC = dyn_cast<SymbolCast>(Sym)) + return canReasonAbout(SVB.makeSymbolVal(SC->getOperand())); -template <typename T, - T(doCast)(const T &, QualType, uint64_t, QualType, uint64_t)> -void Z3ConstraintManager::doIntTypeConversion(T &LHS, QualType <y, T &RHS, - QualType &RTy) const { - ASTContext &Ctx = getBasicVals().getContext(); - - uint64_t LBitWidth = Ctx.getTypeSize(LTy); - uint64_t RBitWidth = Ctx.getTypeSize(RTy); - - // Always perform integer promotion before checking type equality. - // Otherwise, e.g. (bool) a + (bool) b could trigger a backend assertion - if (LTy->isPromotableIntegerType()) { - QualType NewTy = Ctx.getPromotedIntegerType(LTy); - uint64_t NewBitWidth = Ctx.getTypeSize(NewTy); - LHS = (*doCast)(LHS, NewTy, NewBitWidth, LTy, LBitWidth); - LTy = NewTy; - LBitWidth = NewBitWidth; - } - if (RTy->isPromotableIntegerType()) { - QualType NewTy = Ctx.getPromotedIntegerType(RTy); - uint64_t NewBitWidth = Ctx.getTypeSize(NewTy); - RHS = (*doCast)(RHS, NewTy, NewBitWidth, RTy, RBitWidth); - RTy = NewTy; - RBitWidth = NewBitWidth; - } - - if (LTy == RTy) - return; - - // Perform integer type conversion - // Note: Safe to skip updating bitwidth because this must terminate - bool isLSignedTy = LTy->isSignedIntegerOrEnumerationType(); - bool isRSignedTy = RTy->isSignedIntegerOrEnumerationType(); - - int order = Ctx.getIntegerTypeOrder(LTy, RTy); - if (isLSignedTy == isRSignedTy) { - // Same signedness; use the higher-ranked type - if (order == 1) { - RHS = (*doCast)(RHS, LTy, LBitWidth, RTy, RBitWidth); - RTy = LTy; - } else { - LHS = (*doCast)(LHS, RTy, RBitWidth, LTy, LBitWidth); - LTy = RTy; - } - } else if (order != (isLSignedTy ? 1 : -1)) { - // The unsigned type has greater than or equal rank to the - // signed type, so use the unsigned type - if (isRSignedTy) { - RHS = (*doCast)(RHS, LTy, LBitWidth, RTy, RBitWidth); - RTy = LTy; - } else { - LHS = (*doCast)(LHS, RTy, RBitWidth, LTy, LBitWidth); - LTy = RTy; - } - } else if (LBitWidth != RBitWidth) { - // The two types are different widths; if we are here, that - // means the signed type is larger than the unsigned type, so - // use the signed type. - if (isLSignedTy) { - RHS = (*doCast)(RHS, LTy, LBitWidth, RTy, RBitWidth); - RTy = LTy; - } else { - LHS = (*doCast)(LHS, RTy, RBitWidth, LTy, LBitWidth); - LTy = RTy; + if (const BinarySymExpr *BSE = dyn_cast<BinarySymExpr>(Sym)) { + if (const SymIntExpr *SIE = dyn_cast<SymIntExpr>(BSE)) + return canReasonAbout(SVB.makeSymbolVal(SIE->getLHS())); + + if (const IntSymExpr *ISE = dyn_cast<IntSymExpr>(BSE)) + return canReasonAbout(SVB.makeSymbolVal(ISE->getRHS())); + + if (const SymSymExpr *SSE = dyn_cast<SymSymExpr>(BSE)) + return canReasonAbout(SVB.makeSymbolVal(SSE->getLHS())) && + canReasonAbout(SVB.makeSymbolVal(SSE->getRHS())); } - } else { - // The signed type is higher-ranked than the unsigned type, - // but isn't actually any bigger (like unsigned int and long - // on most 32-bit systems). Use the unsigned type corresponding - // to the signed type. - QualType NewTy = Ctx.getCorrespondingUnsignedType(isLSignedTy ? LTy : RTy); - RHS = (*doCast)(RHS, LTy, LBitWidth, RTy, RBitWidth); - RTy = NewTy; - LHS = (*doCast)(LHS, RTy, RBitWidth, LTy, LBitWidth); - LTy = NewTy; + + llvm_unreachable("Unsupported expression to reason about!"); } -} -template <typename T, - T(doCast)(const T &, QualType, uint64_t, QualType, uint64_t)> -void Z3ConstraintManager::doFloatTypeConversion(T &LHS, QualType <y, T &RHS, - QualType &RTy) const { - ASTContext &Ctx = getBasicVals().getContext(); - - uint64_t LBitWidth = Ctx.getTypeSize(LTy); - uint64_t RBitWidth = Ctx.getTypeSize(RTy); - - // Perform float-point type promotion - if (!LTy->isRealFloatingType()) { - LHS = (*doCast)(LHS, RTy, RBitWidth, LTy, LBitWidth); - LTy = RTy; - LBitWidth = RBitWidth; - } - if (!RTy->isRealFloatingType()) { - RHS = (*doCast)(RHS, LTy, LBitWidth, RTy, RBitWidth); - RTy = LTy; - RBitWidth = LBitWidth; - } - - if (LTy == RTy) - return; - - // If we have two real floating types, convert the smaller operand to the - // bigger result - // Note: Safe to skip updating bitwidth because this must terminate - int order = Ctx.getFloatingTypeOrder(LTy, RTy); - if (order > 0) { - RHS = Z3Expr::fromCast(RHS, LTy, LBitWidth, RTy, RBitWidth); - RTy = LTy; - } else if (order == 0) { - LHS = Z3Expr::fromCast(LHS, RTy, RBitWidth, LTy, LBitWidth); - LTy = RTy; - } else { - llvm_unreachable("Unsupported floating-point type cast!"); + ProgramStateRef removeDeadBindings(ProgramStateRef State, + SymbolReaper &SymReaper) override { + ConstraintZ3Ty CZ = State->get<ConstraintZ3>(); + ConstraintZ3Ty::Factory &CZFactory = State->get_context<ConstraintZ3>(); + + for (ConstraintZ3Ty::iterator I = CZ.begin(), E = CZ.end(); I != E; ++I) { + if (SymReaper.maybeDead(I->first)) + CZ = CZFactory.remove(CZ, *I); + } + + return State->set<ConstraintZ3>(CZ); } -} -llvm::APSInt Z3ConstraintManager::castAPSInt(const llvm::APSInt &V, - QualType ToTy, uint64_t ToWidth, - QualType FromTy, - uint64_t FromWidth) { - APSIntType TargetType(ToWidth, !ToTy->isSignedIntegerOrEnumerationType()); - return TargetType.convert(V); -} + ProgramStateRef assumeExpr(ProgramStateRef State, SymbolRef Sym, + const SMTExprRef &Exp) override { + // Check the model, avoid simplifying AST to save time + if (checkModel(State, Exp).isConstrainedTrue()) + return State->add<ConstraintZ3>(std::make_pair(Sym, toZ3Expr(*Exp))); + + return nullptr; + } -//==------------------------------------------------------------------------==/ -// Pretty-printing. -//==------------------------------------------------------------------------==/ + //==------------------------------------------------------------------------==/ + // Pretty-printing. + //==------------------------------------------------------------------------==/ -void Z3ConstraintManager::print(ProgramStateRef St, raw_ostream &OS, - const char *nl, const char *sep) { + void print(ProgramStateRef St, raw_ostream &OS, const char *nl, + const char *sep) override { - ConstraintZ3Ty CZ = St->get<ConstraintZ3>(); + ConstraintZ3Ty CZ = St->get<ConstraintZ3>(); - OS << nl << sep << "Constraints:"; - for (ConstraintZ3Ty::iterator I = CZ.begin(), E = CZ.end(); I != E; ++I) { - OS << nl << ' ' << I->first << " : "; - I->second.print(OS); + OS << nl << sep << "Constraints:"; + for (ConstraintZ3Ty::iterator I = CZ.begin(), E = CZ.end(); I != E; ++I) { + OS << nl << ' ' << I->first << " : "; + I->second.print(OS); + } + OS << nl; } - OS << nl; -} +}; // end class Z3ConstraintManager + +} // end anonymous namespace #endif +std::unique_ptr<SMTSolver> 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_BUILD_Z3=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!", false); + llvm::report_fatal_error("Clang was not compiled with Z3 support, rebuild " + "with -DCLANG_ANALYZER_BUILD_Z3=ON", + false); return nullptr; #endif } |