diff options
Diffstat (limited to 'lib/StaticAnalyzer/Core')
32 files changed, 4125 insertions, 2374 deletions
diff --git a/lib/StaticAnalyzer/Core/APSIntType.cpp b/lib/StaticAnalyzer/Core/APSIntType.cpp index 884b0fa..c7e9526 100644 --- a/lib/StaticAnalyzer/Core/APSIntType.cpp +++ b/lib/StaticAnalyzer/Core/APSIntType.cpp @@ -13,20 +13,31 @@ using namespace clang; using namespace ento; APSIntType::RangeTestResultKind -APSIntType::testInRange(const llvm::APSInt &Value) const { +APSIntType::testInRange(const llvm::APSInt &Value, + bool AllowSignConversions) const { + // Negative numbers cannot be losslessly converted to unsigned type. - if (IsUnsigned && Value.isSigned() && Value.isNegative()) + if (IsUnsigned && !AllowSignConversions && + Value.isSigned() && Value.isNegative()) return RTR_Below; - // Signed integers can be converted to signed integers of the same width - // or (if positive) unsigned integers with one fewer bit. - // Unsigned integers can be converted to unsigned integers of the same width - // or signed integers with one more bit. unsigned MinBits; - if (Value.isSigned()) - MinBits = Value.getMinSignedBits() - IsUnsigned; - else - MinBits = Value.getActiveBits() + !IsUnsigned; + if (AllowSignConversions) { + if (Value.isSigned() && !IsUnsigned) + MinBits = Value.getMinSignedBits(); + else + MinBits = Value.getActiveBits(); + + } else { + // Signed integers can be converted to signed integers of the same width + // or (if positive) unsigned integers with one fewer bit. + // Unsigned integers can be converted to unsigned integers of the same width + // or signed integers with one more bit. + if (Value.isSigned()) + MinBits = Value.getMinSignedBits() - IsUnsigned; + else + MinBits = Value.getActiveBits() + !IsUnsigned; + } if (MinBits <= BitWidth) return RTR_Within; diff --git a/lib/StaticAnalyzer/Core/AnalysisManager.cpp b/lib/StaticAnalyzer/Core/AnalysisManager.cpp index 011d4c09..747b73c 100644 --- a/lib/StaticAnalyzer/Core/AnalysisManager.cpp +++ b/lib/StaticAnalyzer/Core/AnalysisManager.cpp @@ -25,7 +25,8 @@ AnalysisManager::AnalysisManager(ASTContext &ctx, DiagnosticsEngine &diags, /*AddImplicitDtors=*/true, /*AddInitializers=*/true, Options.includeTemporaryDtorsInCFG(), - Options.shouldSynthesizeBodies()), + Options.shouldSynthesizeBodies(), + Options.shouldConditionalizeStaticInitializers()), Ctx(ctx), Diags(diags), LangOpts(lang), diff --git a/lib/StaticAnalyzer/Core/AnalyzerOptions.cpp b/lib/StaticAnalyzer/Core/AnalyzerOptions.cpp index da88589..ae70739 100644 --- a/lib/StaticAnalyzer/Core/AnalyzerOptions.cpp +++ b/lib/StaticAnalyzer/Core/AnalyzerOptions.cpp @@ -13,23 +13,68 @@ //===----------------------------------------------------------------------===// #include "clang/StaticAnalyzer/Core/AnalyzerOptions.h" -#include "llvm/ADT/StringSwitch.h" #include "llvm/ADT/SmallString.h" +#include "llvm/ADT/StringSwitch.h" +#include "llvm/Support/ErrorHandling.h" #include "llvm/Support/raw_ostream.h" using namespace clang; using namespace llvm; +AnalyzerOptions::UserModeKind AnalyzerOptions::getUserMode() { + if (UserMode == UMK_NotSet) { + StringRef ModeStr(Config.GetOrCreateValue("mode", "deep").getValue()); + UserMode = llvm::StringSwitch<UserModeKind>(ModeStr) + .Case("shallow", UMK_Shallow) + .Case("deep", UMK_Deep) + .Default(UMK_NotSet); + assert(UserMode != UMK_NotSet && "User mode is invalid."); + } + return UserMode; +} + +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. + const char *DefaultIPA = 0; + UserModeKind HighLevelMode = getUserMode(); + if (HighLevelMode == UMK_Shallow) + DefaultIPA = "inlining"; + else if (HighLevelMode == UMK_Deep) + DefaultIPA = "dynamic-bifurcate"; + assert(DefaultIPA); + + // Lookup the ipa configuration option, use the default from User Mode. + StringRef ModeStr(Config.GetOrCreateValue("ipa", DefaultIPA).getValue()); + IPAKind IPAConfig = llvm::StringSwitch<IPAKind>(ModeStr) + .Case("none", IPAK_None) + .Case("basic-inlining", IPAK_BasicInlining) + .Case("inlining", IPAK_Inlining) + .Case("dynamic", IPAK_DynamicDispatch) + .Case("dynamic-bifurcate", IPAK_DynamicDispatchBifurcate) + .Default(IPAK_NotSet); + assert(IPAConfig != IPAK_NotSet && "IPA Mode is invalid."); + + // Set the member variable. + IPAMode = IPAConfig; + } + + return IPAMode; +} + bool AnalyzerOptions::mayInlineCXXMemberFunction(CXXInlineableMemberKind K) { - if (IPAMode < Inlining) + if (getIPAMode() < IPAK_Inlining) return false; if (!CXXMemberInliningMode) { static const char *ModeKey = "c++-inlining"; StringRef ModeStr(Config.GetOrCreateValue(ModeKey, - "methods").getValue()); + "destructors").getValue()); CXXInlineableMemberKind &MutableMode = const_cast<CXXInlineableMemberKind &>(CXXMemberInliningMode); @@ -64,8 +109,7 @@ bool AnalyzerOptions::getBooleanOption(StringRef Name, bool DefaultVal) { .Default(DefaultVal); } -bool AnalyzerOptions::getBooleanOption(llvm::Optional<bool> &V, - StringRef Name, +bool AnalyzerOptions::getBooleanOption(Optional<bool> &V, StringRef Name, bool DefaultVal) { if (!V.hasValue()) V = getBooleanOption(Name, DefaultVal); @@ -90,14 +134,21 @@ bool AnalyzerOptions::mayInlineTemplateFunctions() { /*Default=*/true); } +bool AnalyzerOptions::mayInlineCXXContainerCtorsAndDtors() { + return getBooleanOption(InlineCXXContainerCtorsAndDtors, + "c++-container-inlining", + /*Default=*/false); +} + + bool AnalyzerOptions::mayInlineObjCMethod() { return getBooleanOption(ObjCInliningMode, "objc-inlining", /* Default = */ true); } -bool AnalyzerOptions::shouldPruneNullReturnPaths() { - return getBooleanOption(PruneNullReturnPaths, +bool AnalyzerOptions::shouldSuppressNullReturnPaths() { + return getBooleanOption(SuppressNullReturnPaths, "suppress-null-return-paths", /* Default = */ true); } @@ -108,8 +159,20 @@ bool AnalyzerOptions::shouldAvoidSuppressingNullArgumentPaths() { /* Default = */ false); } +bool AnalyzerOptions::shouldSuppressInlinedDefensiveChecks() { + return getBooleanOption(SuppressInlinedDefensiveChecks, + "suppress-inlined-defensive-checks", + /* Default = */ true); +} + +bool AnalyzerOptions::shouldSuppressFromCXXStandardLibrary() { + return getBooleanOption(SuppressFromCXXStandardLibrary, + "suppress-c++-stdlib", + /* Default = */ false); +} + int AnalyzerOptions::getOptionAsInteger(StringRef Name, int DefaultVal) { - llvm::SmallString<10> StrBuf; + SmallString<10> StrBuf; llvm::raw_svector_ostream OS(StrBuf); OS << DefaultVal; @@ -127,12 +190,67 @@ unsigned AnalyzerOptions::getAlwaysInlineSize() { return AlwaysInlineSize.getValue(); } +unsigned AnalyzerOptions::getMaxInlinableSize() { + if (!MaxInlinableSize.hasValue()) { + + int DefaultValue = 0; + UserModeKind HighLevelMode = getUserMode(); + switch (HighLevelMode) { + default: + llvm_unreachable("Invalid mode."); + case UMK_Shallow: + DefaultValue = 4; + break; + case UMK_Deep: + DefaultValue = 50; + break; + } + + MaxInlinableSize = getOptionAsInteger("max-inlinable-size", DefaultValue); + } + return MaxInlinableSize.getValue(); +} + unsigned AnalyzerOptions::getGraphTrimInterval() { if (!GraphTrimInterval.hasValue()) GraphTrimInterval = getOptionAsInteger("graph-trim-interval", 1000); return GraphTrimInterval.getValue(); } +unsigned AnalyzerOptions::getMaxTimesInlineLarge() { + if (!MaxTimesInlineLarge.hasValue()) + MaxTimesInlineLarge = getOptionAsInteger("max-times-inline-large", 32); + return MaxTimesInlineLarge.getValue(); +} + +unsigned AnalyzerOptions::getMaxNodesPerTopLevelFunction() { + if (!MaxNodesPerTopLevelFunction.hasValue()) { + int DefaultValue = 0; + UserModeKind HighLevelMode = getUserMode(); + switch (HighLevelMode) { + default: + llvm_unreachable("Invalid mode."); + case UMK_Shallow: + DefaultValue = 75000; + break; + case UMK_Deep: + DefaultValue = 150000; + break; + } + MaxNodesPerTopLevelFunction = getOptionAsInteger("max-nodes", DefaultValue); + } + return MaxNodesPerTopLevelFunction.getValue(); +} + bool AnalyzerOptions::shouldSynthesizeBodies() { return getBooleanOption("faux-bodies", true); } + +bool AnalyzerOptions::shouldPrunePaths() { + return getBooleanOption("prune-paths", true); +} + +bool AnalyzerOptions::shouldConditionalizeStaticInitializers() { + return getBooleanOption("cfg-conditional-static-initializers", true); +} + diff --git a/lib/StaticAnalyzer/Core/BugReporter.cpp b/lib/StaticAnalyzer/Core/BugReporter.cpp index c898d65..8f8eb3b 100644 --- a/lib/StaticAnalyzer/Core/BugReporter.cpp +++ b/lib/StaticAnalyzer/Core/BugReporter.cpp @@ -12,29 +12,38 @@ // //===----------------------------------------------------------------------===// +#define DEBUG_TYPE "BugReporter" + #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" -#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h" #include "clang/AST/ASTContext.h" -#include "clang/Analysis/CFG.h" #include "clang/AST/DeclObjC.h" #include "clang/AST/Expr.h" #include "clang/AST/ParentMap.h" #include "clang/AST/StmtObjC.h" -#include "clang/Basic/SourceManager.h" +#include "clang/Analysis/CFG.h" #include "clang/Analysis/ProgramPoint.h" +#include "clang/Basic/SourceManager.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" #include "clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h" -#include "llvm/Support/raw_ostream.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h" #include "llvm/ADT/DenseMap.h" -#include "llvm/ADT/SmallString.h" -#include "llvm/ADT/STLExtras.h" -#include "llvm/ADT/OwningPtr.h" #include "llvm/ADT/IntrusiveRefCntPtr.h" +#include "llvm/ADT/OwningPtr.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/ADT/Statistic.h" +#include "llvm/Support/raw_ostream.h" #include <queue> using namespace clang; using namespace ento; +STATISTIC(MaxBugClassSize, + "The maximum number of bug reports in the same equivalence class"); +STATISTIC(MaxValidBugClassSize, + "The maximum number of bug reports in the same equivalence class " + "where at least one report is valid (not suppressed)"); + BugReporterVisitor::~BugReporterVisitor() {} void BugReporterContext::anchor() {} @@ -44,13 +53,13 @@ void BugReporterContext::anchor() {} //===----------------------------------------------------------------------===// static inline const Stmt *GetStmt(const ProgramPoint &P) { - if (const StmtPoint* SP = dyn_cast<StmtPoint>(&P)) + if (Optional<StmtPoint> SP = P.getAs<StmtPoint>()) return SP->getStmt(); - else if (const BlockEdge *BE = dyn_cast<BlockEdge>(&P)) + if (Optional<BlockEdge> BE = P.getAs<BlockEdge>()) return BE->getSrc()->getTerminator(); - else if (const CallEnter *CE = dyn_cast<CallEnter>(&P)) + if (Optional<CallEnter> CE = P.getAs<CallEnter>()) return CE->getCallExpr(); - else if (const CallExitEnd *CEE = dyn_cast<CallExitEnd>(&P)) + if (Optional<CallExitEnd> CEE = P.getAs<CallExitEnd>()) return CEE->getCalleeContext()->getCallSite(); return 0; @@ -191,9 +200,8 @@ static void removeRedundantMsgs(PathPieces &path) { /// 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 should be pruned from the parent path. -bool BugReporter::RemoveUneededCalls(PathPieces &pieces, BugReport *R, - PathDiagnosticCallPiece *CallWithLoc) { +/// "interesting stuff" which means it shouldn't be pruned from the parent path. +bool BugReporter::RemoveUnneededCalls(PathPieces &pieces, BugReport *R) { bool containsSomethingInteresting = false; const unsigned N = pieces.size(); @@ -203,7 +211,9 @@ bool BugReporter::RemoveUneededCalls(PathPieces &pieces, BugReport *R, IntrusiveRefCntPtr<PathDiagnosticPiece> piece(pieces.front()); pieces.pop_front(); - // Throw away pieces with invalid locations. + // Throw away pieces with invalid locations. Note that we can't throw away + // calls just yet because they might have something interesting inside them. + // If so, their locations will be adjusted as necessary later. if (piece->getKind() != PathDiagnosticPiece::Call && piece->getLocation().asLocation().isInvalid()) continue; @@ -217,25 +227,16 @@ bool BugReporter::RemoveUneededCalls(PathPieces &pieces, BugReport *R, containsSomethingInteresting = true; break; } - // Recursively clean out the subclass. Keep this call around if - // it contains any informative diagnostics. - PathDiagnosticCallPiece *NewCallWithLoc = - call->getLocation().asLocation().isValid() - ? call : CallWithLoc; - - if (!RemoveUneededCalls(call->path, R, NewCallWithLoc)) - continue; - if (NewCallWithLoc == CallWithLoc && CallWithLoc) { - call->callEnter = CallWithLoc->callEnter; - } + if (!RemoveUnneededCalls(call->path, R)) + continue; containsSomethingInteresting = true; break; } case PathDiagnosticPiece::Macro: { PathDiagnosticMacroPiece *macro = cast<PathDiagnosticMacroPiece>(piece); - if (!RemoveUneededCalls(macro->subPieces, R)) + if (!RemoveUnneededCalls(macro->subPieces, R)) continue; containsSomethingInteresting = true; break; @@ -258,36 +259,66 @@ bool BugReporter::RemoveUneededCalls(PathPieces &pieces, BugReport *R, return containsSomethingInteresting; } +/// Recursively scan through a path and make sure that all call pieces have +/// valid locations. Note that all other pieces with invalid locations should +/// have already been pruned out. +static void adjustCallLocations(PathPieces &Pieces, + PathDiagnosticLocation *LastCallLocation = 0) { + for (PathPieces::iterator I = Pieces.begin(), E = Pieces.end(); I != E; ++I) { + PathDiagnosticCallPiece *Call = dyn_cast<PathDiagnosticCallPiece>(*I); + + if (!Call) { + assert((*I)->getLocation().asLocation().isValid()); + continue; + } + + if (LastCallLocation) { + if (!Call->callEnter.asLocation().isValid() || + Call->getCaller()->isImplicit()) + Call->callEnter = *LastCallLocation; + if (!Call->callReturn.asLocation().isValid() || + Call->getCaller()->isImplicit()) + Call->callReturn = *LastCallLocation; + } + + // Recursively clean out the subclass. Keep this call around if + // it contains any informative diagnostics. + PathDiagnosticLocation *ThisCallLocation; + if (Call->callEnterWithin.asLocation().isValid() && + !Call->getCallee()->isImplicit()) + ThisCallLocation = &Call->callEnterWithin; + else + ThisCallLocation = &Call->callEnter; + + assert(ThisCallLocation && "Outermost call has an invalid location"); + adjustCallLocations(Call->path, ThisCallLocation); + } +} + //===----------------------------------------------------------------------===// // PathDiagnosticBuilder and its associated routines and helper objects. //===----------------------------------------------------------------------===// -typedef llvm::DenseMap<const ExplodedNode*, -const ExplodedNode*> NodeBackMap; - namespace { class NodeMapClosure : public BugReport::NodeResolver { - NodeBackMap& M; + InterExplodedGraphMap &M; public: - NodeMapClosure(NodeBackMap *m) : M(*m) {} - ~NodeMapClosure() {} + NodeMapClosure(InterExplodedGraphMap &m) : M(m) {} const ExplodedNode *getOriginalNode(const ExplodedNode *N) { - NodeBackMap::iterator I = M.find(N); - return I == M.end() ? 0 : I->second; + return M.lookup(N); } }; class PathDiagnosticBuilder : public BugReporterContext { BugReport *R; PathDiagnosticConsumer *PDC; - OwningPtr<ParentMap> PM; NodeMapClosure NMC; public: const LocationContext *LC; PathDiagnosticBuilder(GRBugReporter &br, - BugReport *r, NodeBackMap *Backmap, + BugReport *r, InterExplodedGraphMap &Backmap, PathDiagnosticConsumer *pdc) : BugReporterContext(br), R(r), PDC(pdc), NMC(Backmap), LC(r->getErrorNode()->getLocationContext()) @@ -552,7 +583,7 @@ static bool GenerateMinimalPathDiagnostic(PathDiagnostic& PD, ProgramPoint P = N->getLocation(); do { - if (const CallExitEnd *CE = dyn_cast<CallExitEnd>(&P)) { + if (Optional<CallExitEnd> CE = P.getAs<CallExitEnd>()) { PathDiagnosticCallPiece *C = PathDiagnosticCallPiece::construct(N, *CE, SMgr); GRBugReporter& BR = PDB.getBugReporter(); @@ -563,7 +594,7 @@ static bool GenerateMinimalPathDiagnostic(PathDiagnostic& PD, break; } - if (const CallEnter *CE = dyn_cast<CallEnter>(&P)) { + if (Optional<CallEnter> CE = P.getAs<CallEnter>()) { // Flush all locations, and pop the active path. bool VisitedEntireCall = PD.isWithinCall(); PD.popActivePath(); @@ -591,7 +622,7 @@ static bool GenerateMinimalPathDiagnostic(PathDiagnostic& PD, break; } - if (const BlockEdge *BE = dyn_cast<BlockEdge>(&P)) { + if (Optional<BlockEdge> BE = P.getAs<BlockEdge>()) { const CFGBlock *Src = BE->getSrc(); const CFGBlock *Dst = BE->getDst(); const Stmt *T = Src->getTerminator(); @@ -1267,7 +1298,81 @@ static void reversePropagateInterestingSymbols(BugReport &R, } } } - + +//===----------------------------------------------------------------------===// +// Functions for determining if a loop was executed 0 times. +//===----------------------------------------------------------------------===// + +/// Return true if the terminator is a loop and the destination is the +/// false branch. +static bool isLoopJumpPastBody(const Stmt *Term, const BlockEdge *BE) { + switch (Term->getStmtClass()) { + case Stmt::ForStmtClass: + case Stmt::WhileStmtClass: + case Stmt::ObjCForCollectionStmtClass: + break; + default: + // Note that we intentionally do not include do..while here. + return false; + } + + // Did we take the false branch? + const CFGBlock *Src = BE->getSrc(); + assert(Src->succ_size() == 2); + return (*(Src->succ_begin()+1) == BE->getDst()); +} + +static bool isContainedByStmt(ParentMap &PM, const Stmt *S, const Stmt *SubS) { + while (SubS) { + if (SubS == S) + return true; + SubS = PM.getParent(SubS); + } + return false; +} + +static const Stmt *getStmtBeforeCond(ParentMap &PM, const Stmt *Term, + const ExplodedNode *N) { + while (N) { + Optional<StmtPoint> SP = N->getLocation().getAs<StmtPoint>(); + if (SP) { + const Stmt *S = SP->getStmt(); + if (!isContainedByStmt(PM, Term, S)) + return S; + } + N = GetPredecessorNode(N); + } + return 0; +} + +static bool isInLoopBody(ParentMap &PM, const Stmt *S, const Stmt *Term) { + const Stmt *LoopBody = 0; + switch (Term->getStmtClass()) { + case Stmt::ForStmtClass: { + const ForStmt *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); + LoopBody = FC->getBody(); + break; + } + case Stmt::WhileStmtClass: + LoopBody = cast<WhileStmt>(Term)->getBody(); + break; + default: + return false; + } + return isContainedByStmt(PM, LoopBody, S); +} + +//===----------------------------------------------------------------------===// +// Top-level logic for generating extensive path diagnostics. +//===----------------------------------------------------------------------===// + static bool GenerateExtensivePathDiagnostic(PathDiagnostic& PD, PathDiagnosticBuilder &PDB, const ExplodedNode *N, @@ -1284,14 +1389,14 @@ static bool GenerateExtensivePathDiagnostic(PathDiagnostic& PD, ProgramPoint P = N->getLocation(); do { - if (const PostStmt *PS = dyn_cast<PostStmt>(&P)) { + if (Optional<PostStmt> PS = P.getAs<PostStmt>()) { if (const Expr *Ex = PS->getStmtAs<Expr>()) reversePropagateIntererstingSymbols(*PDB.getBugReport(), IE, N->getState().getPtr(), Ex, N->getLocationContext()); } - if (const CallExitEnd *CE = dyn_cast<CallExitEnd>(&P)) { + 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, @@ -1315,7 +1420,7 @@ static bool GenerateExtensivePathDiagnostic(PathDiagnostic& PD, // Pop the call hierarchy if we are done walking the contents // of a function call. - if (const CallEnter *CE = dyn_cast<CallEnter>(&P)) { + if (Optional<CallEnter> CE = P.getAs<CallEnter>()) { // Add an edge to the start of the function. const Decl *D = CE->getCalleeContext()->getDecl(); PathDiagnosticLocation pos = @@ -1360,7 +1465,7 @@ static bool GenerateExtensivePathDiagnostic(PathDiagnostic& PD, PDB.LC = N->getLocationContext(); // Block edges. - if (const BlockEdge *BE = dyn_cast<BlockEdge>(&P)) { + 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) { @@ -1397,16 +1502,39 @@ static bool GenerateExtensivePathDiagnostic(PathDiagnostic& PD, EB.addEdge(BL); } } - - if (const Stmt *Term = BE->getSrc()->getTerminator()) + + 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); + PathDiagnosticEventPiece *PE = + new PathDiagnosticEventPiece(L, "Loop body executed 0 times"); + PE->setPrunable(true); + + EB.addEdge(PE->getLocation(), true); + PD.getActivePath().push_front(PE); + } + + // In any case, add the terminator as the current statement + // context for control edges. EB.addContext(Term); + } break; } - if (const BlockEntrance *BE = dyn_cast<BlockEntrance>(&P)) { - CFGElement First = BE->getFirstElement(); - if (const CFGStmt *S = First.getAs<CFGStmt>()) { + 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 '?'. @@ -1502,8 +1630,9 @@ const Decl *BugReport::getDeclWithIssue() const { void BugReport::Profile(llvm::FoldingSetNodeID& hash) const { hash.AddPointer(&BT); hash.AddString(Description); - if (UniqueingLocation.isValid()) { - UniqueingLocation.Profile(hash); + PathDiagnosticLocation UL = getUniqueingLocation(); + if (UL.isValid()) { + UL.Profile(hash); } else if (Location.isValid()) { Location.Profile(hash); } else { @@ -1623,7 +1752,7 @@ const Stmt *BugReport::getStmt() const { ProgramPoint ProgP = ErrorNode->getLocation(); const Stmt *S = NULL; - if (BlockEntrance *BE = dyn_cast<BlockEntrance>(&ProgP)) { + if (Optional<BlockEntrance> BE = ProgP.getAs<BlockEntrance>()) { CFGBlock &Exit = ProgP.getLocationContext()->getCFG()->getExit(); if (BE->getBlock() == &Exit) S = GetPreviousStmt(ErrorNode); @@ -1667,6 +1796,9 @@ PathDiagnosticLocation BugReport::getLocation(const SourceManager &SM) const { if (const BinaryOperator *B = dyn_cast<BinaryOperator>(S)) return PathDiagnosticLocation::createOperatorLoc(B, SM); + if (ErrorNode->getLocation().getAs<PostStmtPurgeDeadSymbols>()) + return PathDiagnosticLocation::createEnd(S, SM, LC); + return PathDiagnosticLocation::createBegin(S, SM, LC); } } else { @@ -1741,141 +1873,174 @@ void BugReporter::FlushReports() { // PathDiagnostics generation. //===----------------------------------------------------------------------===// -static std::pair<std::pair<ExplodedGraph*, NodeBackMap*>, - std::pair<ExplodedNode*, unsigned> > -MakeReportGraph(const ExplodedGraph* G, - SmallVectorImpl<const ExplodedNode*> &nodes) { +namespace { +/// A wrapper around a report graph, which contains only a single path, and its +/// node maps. +class ReportGraph { +public: + InterExplodedGraphMap BackMap; + OwningPtr<ExplodedGraph> Graph; + const ExplodedNode *ErrorNode; + size_t Index; +}; + +/// A wrapper around a trimmed graph and its node maps. +class TrimmedGraph { + InterExplodedGraphMap InverseMap; + + typedef llvm::DenseMap<const ExplodedNode *, unsigned> PriorityMapTy; + PriorityMapTy PriorityMap; + + typedef std::pair<const ExplodedNode *, size_t> NodeIndexPair; + SmallVector<NodeIndexPair, 32> ReportNodes; - // Create the trimmed graph. It will contain the shortest paths from the - // error nodes to the root. In the new graph we should only have one - // error node unless there are two or more error nodes with the same minimum - // path length. - ExplodedGraph* GTrim; - InterExplodedGraphMap* NMap; + OwningPtr<ExplodedGraph> G; - llvm::DenseMap<const void*, const void*> InverseMap; - llvm::tie(GTrim, NMap) = G->Trim(nodes.data(), nodes.data() + nodes.size(), - &InverseMap); + /// A helper class for sorting ExplodedNodes by priority. + template <bool Descending> + class PriorityCompare { + const PriorityMapTy &PriorityMap; - // Create owning pointers for GTrim and NMap just to ensure that they are - // released when this function exists. - OwningPtr<ExplodedGraph> AutoReleaseGTrim(GTrim); - OwningPtr<InterExplodedGraphMap> AutoReleaseNMap(NMap); + public: + PriorityCompare(const PriorityMapTy &M) : PriorityMap(M) {} + + bool operator()(const ExplodedNode *LHS, const ExplodedNode *RHS) const { + PriorityMapTy::const_iterator LI = PriorityMap.find(LHS); + PriorityMapTy::const_iterator RI = PriorityMap.find(RHS); + PriorityMapTy::const_iterator E = PriorityMap.end(); + + if (LI == E) + return Descending; + if (RI == E) + return !Descending; + + return Descending ? LI->second > RI->second + : LI->second < RI->second; + } + + bool operator()(const NodeIndexPair &LHS, const NodeIndexPair &RHS) const { + return (*this)(LHS.first, RHS.first); + } + }; + +public: + TrimmedGraph(const ExplodedGraph *OriginalGraph, + ArrayRef<const ExplodedNode *> Nodes); + + bool popNextReportGraph(ReportGraph &GraphWrapper); +}; +} + +TrimmedGraph::TrimmedGraph(const ExplodedGraph *OriginalGraph, + ArrayRef<const ExplodedNode *> Nodes) { + // The trimmed graph is created in the body of the constructor to ensure + // that the DenseMaps have been initialized already. + InterExplodedGraphMap ForwardMap; + G.reset(OriginalGraph->trim(Nodes, &ForwardMap, &InverseMap)); // Find the (first) error node in the trimmed graph. We just need to consult - // the node map (NMap) which maps from nodes in the original graph to nodes + // the node map which maps from nodes in the original graph to nodes // in the new graph. + llvm::SmallPtrSet<const ExplodedNode *, 32> RemainingNodes; - std::queue<const ExplodedNode*> WS; - typedef llvm::DenseMap<const ExplodedNode*, unsigned> IndexMapTy; - IndexMapTy IndexMap; - - for (unsigned nodeIndex = 0 ; nodeIndex < nodes.size(); ++nodeIndex) { - const ExplodedNode *originalNode = nodes[nodeIndex]; - if (const ExplodedNode *N = NMap->getMappedNode(originalNode)) { - WS.push(N); - IndexMap[originalNode] = nodeIndex; + for (unsigned i = 0, count = Nodes.size(); i < count; ++i) { + if (const ExplodedNode *NewNode = ForwardMap.lookup(Nodes[i])) { + ReportNodes.push_back(std::make_pair(NewNode, i)); + RemainingNodes.insert(NewNode); } } - assert(!WS.empty() && "No error node found in the trimmed graph."); - - // Create a new (third!) graph with a single path. This is the graph - // that will be returned to the caller. - ExplodedGraph *GNew = new ExplodedGraph(); + assert(!RemainingNodes.empty() && "No error node found in the trimmed graph"); - // Sometimes the trimmed graph can contain a cycle. Perform a reverse BFS - // to the root node, and then construct a new graph that contains only - // a single path. - llvm::DenseMap<const void*,unsigned> Visited; + // Perform a forward BFS to find all the shortest paths. + std::queue<const ExplodedNode *> WS; - unsigned cnt = 0; - const ExplodedNode *Root = 0; + assert(G->num_roots() == 1); + WS.push(*G->roots_begin()); + unsigned Priority = 0; while (!WS.empty()) { const ExplodedNode *Node = WS.front(); WS.pop(); - if (Visited.find(Node) != Visited.end()) - continue; + PriorityMapTy::iterator PriorityEntry; + bool IsNew; + llvm::tie(PriorityEntry, IsNew) = + PriorityMap.insert(std::make_pair(Node, Priority)); + ++Priority; - Visited[Node] = cnt++; - - if (Node->pred_empty()) { - Root = Node; - break; + if (!IsNew) { + assert(PriorityEntry->second <= Priority); + continue; } - for (ExplodedNode::const_pred_iterator I=Node->pred_begin(), - E=Node->pred_end(); I!=E; ++I) + if (RemainingNodes.erase(Node)) + if (RemainingNodes.empty()) + break; + + for (ExplodedNode::const_pred_iterator I = Node->succ_begin(), + E = Node->succ_end(); + I != E; ++I) WS.push(*I); } - assert(Root); + // Sort the error paths from longest to shortest. + std::sort(ReportNodes.begin(), ReportNodes.end(), + PriorityCompare<true>(PriorityMap)); +} - // Now walk from the root down the BFS path, always taking the successor - // with the lowest number. - ExplodedNode *Last = 0, *First = 0; - NodeBackMap *BM = new NodeBackMap(); - unsigned NodeIndex = 0; +bool TrimmedGraph::popNextReportGraph(ReportGraph &GraphWrapper) { + if (ReportNodes.empty()) + return false; - for ( const ExplodedNode *N = Root ;;) { - // Lookup the number associated with the current node. - llvm::DenseMap<const void*,unsigned>::iterator I = Visited.find(N); - assert(I != Visited.end()); + const ExplodedNode *OrigN; + llvm::tie(OrigN, GraphWrapper.Index) = ReportNodes.pop_back_val(); + assert(PriorityMap.find(OrigN) != PriorityMap.end() && + "error node not accessible from root"); + // Create a new graph with a single path. This is the graph + // that will be returned to the caller. + ExplodedGraph *GNew = new ExplodedGraph(); + GraphWrapper.Graph.reset(GNew); + GraphWrapper.BackMap.clear(); + + // Now walk from the error node up the BFS path, always taking the + // predeccessor with the lowest number. + ExplodedNode *Succ = 0; + while (true) { // Create the equivalent node in the new graph with the same state // and location. - ExplodedNode *NewN = GNew->getNode(N->getLocation(), N->getState()); + ExplodedNode *NewN = GNew->getNode(OrigN->getLocation(), OrigN->getState()); // Store the mapping to the original node. - llvm::DenseMap<const void*, const void*>::iterator IMitr=InverseMap.find(N); + InterExplodedGraphMap::const_iterator IMitr = InverseMap.find(OrigN); assert(IMitr != InverseMap.end() && "No mapping to original node."); - (*BM)[NewN] = (const ExplodedNode*) IMitr->second; + GraphWrapper.BackMap[NewN] = IMitr->second; // Link up the new node with the previous node. - if (Last) - NewN->addPredecessor(Last, *GNew); + if (Succ) + Succ->addPredecessor(NewN, *GNew); + else + GraphWrapper.ErrorNode = NewN; - Last = NewN; + Succ = NewN; // Are we at the final node? - IndexMapTy::iterator IMI = - IndexMap.find((const ExplodedNode*)(IMitr->second)); - if (IMI != IndexMap.end()) { - First = NewN; - NodeIndex = IMI->second; + if (OrigN->pred_empty()) { + GNew->addRoot(NewN); break; } - // Find the next successor node. We choose the node that is marked - // with the lowest DFS number. - ExplodedNode::const_succ_iterator SI = N->succ_begin(); - ExplodedNode::const_succ_iterator SE = N->succ_end(); - N = 0; - - for (unsigned MinVal = 0; SI != SE; ++SI) { - - I = Visited.find(*SI); - - if (I == Visited.end()) - continue; - - if (!N || I->second < MinVal) { - N = *SI; - MinVal = I->second; - } - } - - assert(N); + // Find the next predeccessor node. We choose the node that is marked + // with the lowest BFS number. + OrigN = *std::min_element(OrigN->pred_begin(), OrigN->pred_end(), + PriorityCompare<false>(PriorityMap)); } - assert(First); - - return std::make_pair(std::make_pair(GNew, BM), - std::make_pair(First, NodeIndex)); + 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) { @@ -1978,128 +2143,128 @@ bool GRBugReporter::generatePathDiagnostic(PathDiagnostic& PD, assert(!bugReports.empty()); bool HasValid = false; - SmallVector<const ExplodedNode *, 10> errorNodes; + 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(0); } } - // If all the reports have been marked invalid, we're done. + // If all the reports have been marked invalid by a previous path generation, + // we're done. if (!HasValid) return false; - // Construct a new graph that contains only a single path from the error - // node to a root. - const std::pair<std::pair<ExplodedGraph*, NodeBackMap*>, - std::pair<ExplodedNode*, unsigned> >& - GPair = MakeReportGraph(&getGraph(), errorNodes); - - // Find the BugReport with the original location. - assert(GPair.second.second < bugReports.size()); - BugReport *R = bugReports[GPair.second.second]; - assert(R && "No original report found for sliced graph."); - assert(R->isValid() && "Report selected from trimmed graph marked invalid."); - - OwningPtr<ExplodedGraph> ReportGraph(GPair.first.first); - OwningPtr<NodeBackMap> BackMap(GPair.first.second); - const ExplodedNode *N = GPair.second.first; - - // Start building the path diagnostic... - PathDiagnosticBuilder PDB(*this, R, BackMap.get(), &PC); - - // Register additional node visitors. - R->addVisitor(new NilReceiverBRVisitor()); - R->addVisitor(new ConditionBRVisitor()); - - BugReport::VisitorList visitors; - unsigned originalReportConfigToken, finalReportConfigToken; - - // 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(); - originalReportConfigToken = R->getConfigurationChangeToken(); - - // Generate the very last diagnostic piece - the piece is visible before - // the trace is expanded. - if (PDB.getGenerationScheme() != PathDiagnosticConsumer::None) { + typedef PathDiagnosticConsumer::PathGenerationScheme PathGenerationScheme; + PathGenerationScheme ActiveScheme = PC.getGenerationScheme(); + + TrimmedGraph TrimG(&getGraph(), errorNodes); + ReportGraph ErrorGraph; + + while (TrimG.popNextReportGraph(ErrorGraph)) { + // Find the BugReport with the original location. + assert(ErrorGraph.Index < bugReports.size()); + BugReport *R = bugReports[ErrorGraph.Index]; + assert(R && "No original report found for sliced graph."); + assert(R->isValid() && "Report selected by trimmed graph marked invalid."); + + // Start building the path diagnostic... + PathDiagnosticBuilder PDB(*this, R, ErrorGraph.BackMap, &PC); + const ExplodedNode *N = ErrorGraph.ErrorNode; + + // Register additional node visitors. + R->addVisitor(new NilReceiverBRVisitor()); + R->addVisitor(new ConditionBRVisitor()); + R->addVisitor(new LikelyFalsePositiveSuppressionBRVisitor()); + + BugReport::VisitorList visitors; + unsigned origReportConfigToken, finalReportConfigToken; + + // 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. PathDiagnosticPiece *LastPiece = 0; for (BugReport::visitor_iterator I = visitors.begin(), E = visitors.end(); - I != E; ++I) { + I != E; ++I) { if (PathDiagnosticPiece *Piece = (*I)->getEndPath(PDB, N, *R)) { assert (!LastPiece && - "There can only be one final piece in a diagnostic."); + "There can only be one final piece in a diagnostic."); LastPiece = Piece; } } - if (!LastPiece) - LastPiece = BugReporterVisitor::getDefaultEndPath(PDB, N, *R); - if (LastPiece) - PD.setEndOfPath(LastPiece); - else - return false; - } - switch (PDB.getGenerationScheme()) { - case PathDiagnosticConsumer::Extensive: - if (!GenerateExtensivePathDiagnostic(PD, PDB, N, visitors)) { - assert(!R->isValid() && "Failed on valid report"); - // Try again. We'll filter out the bad report when we trim the graph. - // FIXME: It would be more efficient to use the same intermediate - // trimmed graph, and just repeat the shortest-path search. - return generatePathDiagnostic(PD, PC, bugReports); - } - break; - case PathDiagnosticConsumer::Minimal: - if (!GenerateMinimalPathDiagnostic(PD, PDB, N, visitors)) { - assert(!R->isValid() && "Failed on valid report"); - // Try again. We'll filter out the bad report when we trim the graph. - return generatePathDiagnostic(PD, PC, bugReports); + if (ActiveScheme != PathDiagnosticConsumer::None) { + if (!LastPiece) + LastPiece = BugReporterVisitor::getDefaultEndPath(PDB, N, *R); + assert(LastPiece); + PD.setEndOfPath(LastPiece); } - break; - case PathDiagnosticConsumer::None: - if (!GenerateVisitorsOnlyPathDiagnostic(PD, PDB, N, visitors)) { - assert(!R->isValid() && "Failed on valid report"); - // Try again. We'll filter out the bad report when we trim the graph. - return generatePathDiagnostic(PD, PC, bugReports); + + switch (ActiveScheme) { + case PathDiagnosticConsumer::Extensive: + GenerateExtensivePathDiagnostic(PD, PDB, N, visitors); + break; + case PathDiagnosticConsumer::Minimal: + GenerateMinimalPathDiagnostic(PD, PDB, N, visitors); + break; + case PathDiagnosticConsumer::None: + GenerateVisitorsOnlyPathDiagnostic(PD, PDB, N, visitors); + break; } - break; - } - // Clean up the visitors we used. - llvm::DeleteContainerPointers(visitors); + // Clean up the visitors we used. + llvm::DeleteContainerPointers(visitors); - // Did anything change while generating this path? - finalReportConfigToken = R->getConfigurationChangeToken(); - } while(finalReportConfigToken != originalReportConfigToken); + // Did anything change while generating this path? + finalReportConfigToken = R->getConfigurationChangeToken(); + } while (finalReportConfigToken != origReportConfigToken); - // Finally, prune the diagnostic path of uninteresting stuff. - if (!PD.path.empty()) { - // Remove messages that are basically the same. - removeRedundantMsgs(PD.getMutablePieces()); + if (!R->isValid()) + continue; - if (R->shouldPrunePath()) { - bool hasSomethingInteresting = RemoveUneededCalls(PD.getMutablePieces(), - R); - assert(hasSomethingInteresting); - (void) hasSomethingInteresting; + // Finally, prune the diagnostic path of uninteresting stuff. + if (!PD.path.empty()) { + // Remove messages that are basically the same. + removeRedundantMsgs(PD.getMutablePieces()); + + if (R->shouldPrunePath() && + getEngine().getAnalysisManager().options.shouldPrunePaths()) { + bool stillHasNotes = RemoveUnneededCalls(PD.getMutablePieces(), R); + assert(stillHasNotes); + (void)stillHasNotes; + } + + adjustCallLocations(PD.getMutablePieces()); } + + // We found a report and didn't suppress it. + return true; } - return true; + // We suppressed all the reports in this equivalence class. + assert(!HasInvalid && "Inconsistent suppression"); + (void)HasInvalid; + return false; } void BugReporter::Register(BugType *BT) { @@ -2265,7 +2430,12 @@ void BugReporter::FlushReport(BugReport *exampleReport, exampleReport->getBugType().getName(), exampleReport->getDescription(), exampleReport->getShortDescription(/*Fallback=*/false), - BT.getCategory())); + BT.getCategory(), + exampleReport->getUniqueingLocation(), + exampleReport->getUniqueingDecl())); + + MaxBugClassSize = std::max(bugReports.size(), + static_cast<size_t>(MaxBugClassSize)); // Generate the full path diagnostic, using the generation scheme // specified by the PathDiagnosticConsumer. Note that we have to generate @@ -2275,6 +2445,9 @@ void BugReporter::FlushReport(BugReport *exampleReport, if (!generatePathDiagnostic(*D.get(), PD, bugReports)) return; + MaxValidBugClassSize = std::max(bugReports.size(), + static_cast<size_t>(MaxValidBugClassSize)); + // If the path is empty, generate a single step path with the location // of the issue. if (D->path.empty()) { diff --git a/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp b/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp index 328e8a6..f600362 100644 --- a/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp +++ b/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp @@ -12,22 +12,23 @@ // //===----------------------------------------------------------------------===// #include "clang/StaticAnalyzer/Core/BugReporter/BugReporterVisitor.h" - #include "clang/AST/Expr.h" #include "clang/AST/ExprObjC.h" #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" #include "clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" #include "llvm/ADT/SmallString.h" #include "llvm/ADT/StringExtras.h" +#include "llvm/Support/raw_ostream.h" using namespace clang; using namespace ento; +using llvm::FoldingSetNodeID; + //===----------------------------------------------------------------------===// // Utility functions. //===----------------------------------------------------------------------===// @@ -39,37 +40,33 @@ bool bugreporter::isDeclRefExprToReference(const Expr *E) { return false; } -const Stmt *bugreporter::GetDerefExpr(const ExplodedNode *N) { +const Expr *bugreporter::getDerefExpr(const Stmt *S) { // Pattern match for a few useful cases (do something smarter later): // a[0], p->f, *p - const PostStmt *Loc = N->getLocationAs<PostStmt>(); - if (!Loc) - return 0; - - const Expr *S = dyn_cast<Expr>(Loc->getStmt()); - if (!S) + const Expr *E = dyn_cast<Expr>(S); + if (!E) return 0; - S = S->IgnoreParenCasts(); + E = E->IgnoreParenCasts(); while (true) { - if (const BinaryOperator *B = dyn_cast<BinaryOperator>(S)) { + if (const BinaryOperator *B = dyn_cast<BinaryOperator>(E)) { assert(B->isAssignmentOp()); - S = B->getLHS()->IgnoreParenCasts(); + E = B->getLHS()->IgnoreParenCasts(); continue; } - else if (const UnaryOperator *U = dyn_cast<UnaryOperator>(S)) { + else if (const UnaryOperator *U = dyn_cast<UnaryOperator>(E)) { if (U->getOpcode() == UO_Deref) return U->getSubExpr()->IgnoreParenCasts(); } - else if (const MemberExpr *ME = dyn_cast<MemberExpr>(S)) { + else if (const MemberExpr *ME = dyn_cast<MemberExpr>(E)) { if (ME->isArrow() || isDeclRefExprToReference(ME->getBase())) { return ME->getBase()->IgnoreParenCasts(); } } - else if (const ObjCIvarRefExpr *IvarRef = dyn_cast<ObjCIvarRefExpr>(S)) { + else if (const ObjCIvarRefExpr *IvarRef = dyn_cast<ObjCIvarRefExpr>(E)) { return IvarRef->getBase()->IgnoreParenCasts(); } - else if (const ArraySubscriptExpr *AE = dyn_cast<ArraySubscriptExpr>(S)) { + else if (const ArraySubscriptExpr *AE = dyn_cast<ArraySubscriptExpr>(E)) { return AE->getBase(); } break; @@ -137,13 +134,15 @@ class ReturnVisitor : public BugReporterVisitorImpl<ReturnVisitor> { const StackFrameContext *StackFrame; enum { Initial, - MaybeSuppress, + MaybeUnsuppress, Satisfied } Mode; + bool EnableNullFPSuppression; + public: - ReturnVisitor(const StackFrameContext *Frame) - : StackFrame(Frame), Mode(Initial) {} + ReturnVisitor(const StackFrameContext *Frame, bool Suppressed) + : StackFrame(Frame), Mode(Initial), EnableNullFPSuppression(Suppressed) {} static void *getTag() { static int Tag = 0; @@ -153,6 +152,7 @@ public: virtual void Profile(llvm::FoldingSetNodeID &ID) const { ID.AddPointer(ReturnVisitor::getTag()); ID.AddPointer(StackFrame); + ID.AddBoolean(EnableNullFPSuppression); } /// Adds a ReturnVisitor if the given statement represents a call that was @@ -163,16 +163,17 @@ public: /// the statement is a call that was inlined, we add the visitor to the /// bug report, so it can print a note later. static void addVisitorIfNecessary(const ExplodedNode *Node, const Stmt *S, - BugReport &BR) { + BugReport &BR, + bool InEnableNullFPSuppression) { if (!CallEvent::isCallStmt(S)) return; // First, find when we processed the statement. do { - if (const CallExitEnd *CEE = Node->getLocationAs<CallExitEnd>()) + if (Optional<CallExitEnd> CEE = Node->getLocationAs<CallExitEnd>()) if (CEE->getCalleeContext()->getCallSite() == S) break; - if (const StmtPoint *SP = Node->getLocationAs<StmtPoint>()) + if (Optional<StmtPoint> SP = Node->getLocationAs<StmtPoint>()) if (SP->getStmt() == S) break; @@ -180,19 +181,41 @@ public: } while (Node); // Next, step over any post-statement checks. - while (Node && isa<PostStmt>(Node->getLocation())) + while (Node && Node->getLocation().getAs<PostStmt>()) Node = Node->getFirstPred(); + if (!Node) + return; // Finally, see if we inlined the call. - if (Node) { - if (const CallExitEnd *CEE = Node->getLocationAs<CallExitEnd>()) { - const StackFrameContext *CalleeContext = CEE->getCalleeContext(); - if (CalleeContext->getCallSite() == S) { - BR.markInteresting(CalleeContext); - BR.addVisitor(new ReturnVisitor(CalleeContext)); - } - } - } + Optional<CallExitEnd> CEE = Node->getLocationAs<CallExitEnd>(); + if (!CEE) + return; + + const StackFrameContext *CalleeContext = CEE->getCalleeContext(); + if (CalleeContext->getCallSite() != S) + return; + + // Check the return value. + ProgramStateRef State = Node->getState(); + SVal RetVal = State->getSVal(S, Node->getLocationContext()); + + // Handle cases where a reference is returned and then immediately used. + if (cast<Expr>(S)->isGLValue()) + if (Optional<Loc> LValue = RetVal.getAs<Loc>()) + RetVal = State->getSVal(*LValue); + + // See if the return value is NULL. If so, suppress the report. + SubEngine *Eng = State->getStateManager().getOwningEngine(); + assert(Eng && "Cannot file a bug report without an owning engine"); + AnalyzerOptions &Options = Eng->getAnalysisManager().options; + + bool EnableNullFPSuppression = false; + if (InEnableNullFPSuppression && Options.shouldSuppressNullReturnPaths()) + if (Optional<Loc> RetLoc = RetVal.getAs<Loc>()) + EnableNullFPSuppression = State->isNull(*RetLoc).isConstrainedTrue(); + + BR.markInteresting(CalleeContext); + BR.addVisitor(new ReturnVisitor(CalleeContext, EnableNullFPSuppression)); } /// Returns true if any counter-suppression heuristics are enabled for @@ -209,7 +232,7 @@ public: if (N->getLocationContext() != StackFrame) return 0; - const StmtPoint *SP = N->getLocationAs<StmtPoint>(); + Optional<StmtPoint> SP = N->getLocationAs<StmtPoint>(); if (!SP) return 0; @@ -229,35 +252,49 @@ public: const Expr *RetE = Ret->getRetValue(); assert(RetE && "Tracking a return value for a void function"); + + // Handle cases where a reference is returned and then immediately used. + Optional<Loc> LValue; + if (RetE->isGLValue()) { + if ((LValue = V.getAs<Loc>())) { + SVal RValue = State->getRawSVal(*LValue, RetE->getType()); + if (RValue.getAs<DefinedSVal>()) + V = RValue; + } + } + + // Ignore aggregate rvalues. + if (V.getAs<nonloc::LazyCompoundVal>() || + V.getAs<nonloc::CompoundVal>()) + return 0; + RetE = RetE->IgnoreParenCasts(); // If we can't prove the return value is 0, just mark it interesting, and // make sure to track it into any further inner functions. - if (State->assume(cast<DefinedSVal>(V), true)) { + if (!State->isNull(V).isConstrainedTrue()) { BR.markInteresting(V); - ReturnVisitor::addVisitorIfNecessary(N, RetE, BR); + ReturnVisitor::addVisitorIfNecessary(N, RetE, BR, + EnableNullFPSuppression); return 0; } // If we're returning 0, we should track where that 0 came from. - bugreporter::trackNullOrUndefValue(N, RetE, BR); + bugreporter::trackNullOrUndefValue(N, RetE, BR, /*IsArg*/ false, + EnableNullFPSuppression); // Build an appropriate message based on the return value. SmallString<64> Msg; llvm::raw_svector_ostream Out(Msg); - if (isa<Loc>(V)) { - // If we are pruning null-return paths as unlikely error paths, mark the - // report invalid. We still want to emit a path note, however, in case + if (V.getAs<Loc>()) { + // 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; - if (Options.shouldPruneNullReturnPaths()) { - if (hasCounterSuppression(Options)) - Mode = MaybeSuppress; - else - BR.markInvalid(ReturnVisitor::getTag(), StackFrame); - } + if (EnableNullFPSuppression && hasCounterSuppression(Options)) + Mode = MaybeUnsuppress; if (RetE->getType()->isObjCObjectPointerType()) Out << "Returning nil"; @@ -267,21 +304,37 @@ public: Out << "Returning zero"; } - // 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())) - Out << " (loaded from '" << *DD << "')"; + if (LValue) { + if (const MemRegion *MR = LValue->getAsRegion()) { + if (MR->canPrintPretty()) { + Out << " (reference to '"; + MR->printPretty(Out); + Out << "')"; + } + } + } 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())) + Out << " (loaded from '" << *DD << "')"; + } PathDiagnosticLocation L(Ret, BRC.getSourceManager(), StackFrame); return new PathDiagnosticEventPiece(L, Out.str()); } - PathDiagnosticPiece *visitNodeMaybeSuppress(const ExplodedNode *N, - const ExplodedNode *PrevN, - BugReporterContext &BRC, - BugReport &BR) { + PathDiagnosticPiece *visitNodeMaybeUnsuppress(const ExplodedNode *N, + const ExplodedNode *PrevN, + BugReporterContext &BRC, + BugReport &BR) { +#ifndef NDEBUG + ExprEngine &Eng = BRC.getBugReporter().getEngine(); + AnalyzerOptions &Options = Eng.getAnalysisManager().options; + assert(hasCounterSuppression(Options)); +#endif + // Are we at the entry node for this call? - const CallEnter *CE = N->getLocationAs<CallEnter>(); + Optional<CallEnter> CE = N->getLocationAs<CallEnter>(); if (!CE) return 0; @@ -290,41 +343,36 @@ public: Mode = Satisfied; - ExprEngine &Eng = BRC.getBugReporter().getEngine(); - AnalyzerOptions &Options = Eng.getAnalysisManager().options; - if (Options.shouldAvoidSuppressingNullArgumentPaths()) { - // Don't automatically suppress a report if one of the arguments is - // known to be a null pointer. Instead, start tracking /that/ null - // value back to its origin. - ProgramStateManager &StateMgr = BRC.getStateManager(); - CallEventManager &CallMgr = StateMgr.getCallEventManager(); - - ProgramStateRef State = N->getState(); - CallEventRef<> Call = CallMgr.getCaller(StackFrame, State); - for (unsigned I = 0, E = Call->getNumArgs(); I != E; ++I) { - SVal ArgV = Call->getArgSVal(I); - if (!isa<Loc>(ArgV)) - continue; + // Don't automatically suppress a report if one of the arguments is + // known to be a null pointer. Instead, start tracking /that/ null + // value back to its origin. + ProgramStateManager &StateMgr = BRC.getStateManager(); + CallEventManager &CallMgr = StateMgr.getCallEventManager(); - const Expr *ArgE = Call->getArgExpr(I); - if (!ArgE) - continue; - - // Is it possible for this argument to be non-null? - if (State->assume(cast<Loc>(ArgV), true)) - continue; - - if (bugreporter::trackNullOrUndefValue(N, ArgE, BR, /*IsArg=*/true)) - return 0; - - // If we /can't/ track the null pointer, we should err on the side of - // false negatives, and continue towards marking this report invalid. - // (We will still look at the other arguments, though.) - } + ProgramStateRef State = N->getState(); + CallEventRef<> Call = CallMgr.getCaller(StackFrame, State); + for (unsigned I = 0, E = Call->getNumArgs(); I != E; ++I) { + Optional<Loc> ArgV = Call->getArgSVal(I).getAs<Loc>(); + if (!ArgV) + continue; + + const Expr *ArgE = Call->getArgExpr(I); + if (!ArgE) + continue; + + // Is it possible for this argument to be non-null? + if (!State->isNull(*ArgV).isConstrainedTrue()) + continue; + + if (bugreporter::trackNullOrUndefValue(N, ArgE, BR, /*IsArg=*/true, + EnableNullFPSuppression)) + BR.removeInvalidation(ReturnVisitor::getTag(), StackFrame); + + // If we /can't/ track the null pointer, we should err on the side of + // false negatives, and continue towards marking this report invalid. + // (We will still look at the other arguments, though.) } - // There is no reason not to suppress this report; go ahead and do it. - BR.markInvalid(ReturnVisitor::getTag(), StackFrame); return 0; } @@ -335,14 +383,22 @@ public: switch (Mode) { case Initial: return visitNodeInitial(N, PrevN, BRC, BR); - case MaybeSuppress: - return visitNodeMaybeSuppress(N, PrevN, BRC, BR); + case MaybeUnsuppress: + return visitNodeMaybeUnsuppress(N, PrevN, BRC, BR); case Satisfied: return 0; } llvm_unreachable("Invalid visit mode!"); } + + PathDiagnosticPiece *getEndPath(BugReporterContext &BRC, + const ExplodedNode *N, + BugReport &BR) { + if (EnableNullFPSuppression) + BR.markInvalid(ReturnVisitor::getTag(), StackFrame); + return 0; + } }; } // end anonymous namespace @@ -352,6 +408,7 @@ void FindLastStoreBRVisitor ::Profile(llvm::FoldingSetNodeID &ID) const { ID.AddPointer(&tag); ID.AddPointer(R); ID.Add(V); + ID.AddBoolean(EnableNullFPSuppression); } PathDiagnosticPiece *FindLastStoreBRVisitor::VisitNode(const ExplodedNode *Succ, @@ -359,7 +416,7 @@ PathDiagnosticPiece *FindLastStoreBRVisitor::VisitNode(const ExplodedNode *Succ, BugReporterContext &BRC, BugReport &BR) { - if (satisfied) + if (Satisfied) return NULL; const ExplodedNode *StoreSite = 0; @@ -368,7 +425,7 @@ PathDiagnosticPiece *FindLastStoreBRVisitor::VisitNode(const ExplodedNode *Succ, // First see if we reached the declaration of the region. if (const VarRegion *VR = dyn_cast<VarRegion>(R)) { - if (const PostStmt *P = Pred->getLocationAs<PostStmt>()) { + if (Optional<PostStmt> P = Pred->getLocationAs<PostStmt>()) { if (const DeclStmt *DS = P->getStmtAs<DeclStmt>()) { if (DS->getSingleDecl() == VR->getDecl()) { StoreSite = Pred; @@ -378,19 +435,36 @@ PathDiagnosticPiece *FindLastStoreBRVisitor::VisitNode(const ExplodedNode *Succ, } } - // Otherwise, check that Succ has this binding and Pred does not, i.e. this is - // where the binding first occurred. + // If this is a post initializer expression, initializing the region, we + // should track the initializer expression. + if (Optional<PostInitializer> PIP = Pred->getLocationAs<PostInitializer>()) { + const MemRegion *FieldReg = (const MemRegion *)PIP->getLocationValue(); + if (FieldReg && FieldReg == R) { + StoreSite = Pred; + InitE = PIP->getInitializer()->getInit(); + } + } + + // Otherwise, see if this is the store site: + // (1) Succ has this binding and Pred does not, i.e. this is + // where the binding first occurred. + // (2) Succ has this binding and is a PostStore node for this region, i.e. + // the same binding was re-assigned here. if (!StoreSite) { if (Succ->getState()->getSVal(R) != V) return NULL; - if (Pred->getState()->getSVal(R) == V) - return NULL; + + if (Pred->getState()->getSVal(R) == V) { + Optional<PostStore> PS = Succ->getLocationAs<PostStore>(); + if (!PS || PS->getLocationValue() != R) + return NULL; + } StoreSite = Succ; // If this is an assignment expression, we can track the value // being assigned. - if (const PostStmt *P = Succ->getLocationAs<PostStmt>()) + if (Optional<PostStmt> P = Succ->getLocationAs<PostStmt>()) if (const BinaryOperator *BO = P->getStmtAs<BinaryOperator>()) if (BO->isAssignmentOp()) InitE = BO->getRHS(); @@ -399,34 +473,41 @@ PathDiagnosticPiece *FindLastStoreBRVisitor::VisitNode(const ExplodedNode *Succ, // FIXME: Handle CXXThisRegion as well. (This is not a priority because // 'this' should never be NULL, but this visitor isn't just for NULL and // UndefinedVal.) - if (const CallEnter *CE = Succ->getLocationAs<CallEnter>()) { - const VarRegion *VR = cast<VarRegion>(R); - const ParmVarDecl *Param = cast<ParmVarDecl>(VR->getDecl()); - - ProgramStateManager &StateMgr = BRC.getStateManager(); - CallEventManager &CallMgr = StateMgr.getCallEventManager(); - - CallEventRef<> Call = CallMgr.getCaller(CE->getCalleeContext(), - Succ->getState()); - InitE = Call->getArgExpr(Param->getFunctionScopeIndex()); - IsParam = true; + if (Optional<CallEnter> CE = Succ->getLocationAs<CallEnter>()) { + if (const VarRegion *VR = dyn_cast<VarRegion>(R)) { + const ParmVarDecl *Param = cast<ParmVarDecl>(VR->getDecl()); + + ProgramStateManager &StateMgr = BRC.getStateManager(); + CallEventManager &CallMgr = StateMgr.getCallEventManager(); + + CallEventRef<> Call = CallMgr.getCaller(CE->getCalleeContext(), + Succ->getState()); + InitE = Call->getArgExpr(Param->getFunctionScopeIndex()); + IsParam = true; + } } + + // If this is a CXXTempObjectRegion, the Expr responsible for its creation + // is wrapped inside of it. + if (const CXXTempObjectRegion *TmpR = dyn_cast<CXXTempObjectRegion>(R)) + InitE = TmpR->getExpr(); } if (!StoreSite) return NULL; - satisfied = true; + Satisfied = true; // If we have an expression that provided the value, try to track where it // came from. if (InitE) { - if (V.isUndef() || isa<loc::ConcreteInt>(V)) { + if (V.isUndef() || V.getAs<loc::ConcreteInt>()) { if (!IsParam) InitE = InitE->IgnoreParenCasts(); - bugreporter::trackNullOrUndefValue(StoreSite, InitE, BR, IsParam); + bugreporter::trackNullOrUndefValue(StoreSite, InitE, BR, IsParam, + EnableNullFPSuppression); } else { ReturnVisitor::addVisitorIfNecessary(StoreSite, InitE->IgnoreParenCasts(), - BR); + BR, EnableNullFPSuppression); } } @@ -437,73 +518,103 @@ PathDiagnosticPiece *FindLastStoreBRVisitor::VisitNode(const ExplodedNode *Succ, SmallString<256> sbuf; llvm::raw_svector_ostream os(sbuf); - if (const PostStmt *PS = StoreSite->getLocationAs<PostStmt>()) { - if (const DeclStmt *DS = PS->getStmtAs<DeclStmt>()) { - - if (const VarRegion *VR = dyn_cast<VarRegion>(R)) { - os << "Variable '" << *VR->getDecl() << "' "; + if (Optional<PostStmt> PS = StoreSite->getLocationAs<PostStmt>()) { + const Stmt *S = PS->getStmt(); + const char *action = 0; + const DeclStmt *DS = dyn_cast<DeclStmt>(S); + const VarRegion *VR = dyn_cast<VarRegion>(R); + + if (DS) { + action = "initialized to "; + } else if (isa<BlockExpr>(S)) { + action = "captured by block as "; + if (VR) { + // See if we can get the BlockVarRegion. + ProgramStateRef State = StoreSite->getState(); + SVal V = State->getSVal(S, PS->getLocationContext()); + if (const BlockDataRegion *BDR = + dyn_cast_or_null<BlockDataRegion>(V.getAsRegion())) { + if (const VarRegion *OriginalR = BDR->getOriginalRegion(VR)) { + if (Optional<KnownSVal> KV = + State->getSVal(OriginalR).getAs<KnownSVal>()) + BR.addVisitor(new FindLastStoreBRVisitor(*KV, OriginalR, + EnableNullFPSuppression)); + } + } } - else - return NULL; + } + + if (action) { + if (!R) + return 0; - if (isa<loc::ConcreteInt>(V)) { + os << '\''; + 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 << "initialized to nil"; + os << action << "nil"; b = true; } } } if (!b) - os << "initialized to a null pointer value"; - } - else if (isa<nonloc::ConcreteInt>(V)) { - os << "initialized to " << cast<nonloc::ConcreteInt>(V).getValue(); + os << action << "a null pointer value"; + } else if (Optional<nonloc::ConcreteInt> CVal = + V.getAs<nonloc::ConcreteInt>()) { + os << action << CVal->getValue(); } - else if (V.isUndef()) { - if (isa<VarRegion>(R)) { - const VarDecl *VD = cast<VarDecl>(DS->getSingleDecl()); - if (VD->getInit()) - os << "initialized to a garbage value"; - else - os << "declared without an initial value"; + else if (DS) { + if (V.isUndef()) { + if (isa<VarRegion>(R)) { + const VarDecl *VD = cast<VarDecl>(DS->getSingleDecl()); + if (VD->getInit()) + os << "initialized to a garbage value"; + else + os << "declared without an initial value"; + } + } + else { + os << "initialized here"; } - } - else { - os << "initialized here"; } } - } else if (isa<CallEnter>(StoreSite->getLocation())) { - const ParmVarDecl *Param = cast<ParmVarDecl>(cast<VarRegion>(R)->getDecl()); + } else if (StoreSite->getLocation().getAs<CallEnter>()) { + if (const VarRegion *VR = dyn_cast<VarRegion>(R)) { + const ParmVarDecl *Param = cast<ParmVarDecl>(VR->getDecl()); - os << "Passing "; + os << "Passing "; - if (isa<loc::ConcreteInt>(V)) { - if (Param->getType()->isObjCObjectPointerType()) - os << "nil object reference"; - else - os << "null pointer value"; - } else if (V.isUndef()) { - os << "uninitialized value"; - } else if (isa<nonloc::ConcreteInt>(V)) { - os << "the value " << cast<nonloc::ConcreteInt>(V).getValue(); - } else { - os << "value"; - } + 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 '"; + // Printed parameter indexes are 1-based, not 0-based. + unsigned Idx = Param->getFunctionScopeIndex() + 1; + os << " via " << Idx << llvm::getOrdinalSuffix(Idx) << " parameter '"; - R->printPretty(os); - os << '\''; + R->printPretty(os); + os << '\''; + } } if (os.str().empty()) { - if (isa<loc::ConcreteInt>(V)) { + if (V.getAs<loc::ConcreteInt>()) { bool b = false; if (R->isBoundable()) { if (const TypedValueRegion *TR = dyn_cast<TypedValueRegion>(R)) { @@ -519,10 +630,9 @@ PathDiagnosticPiece *FindLastStoreBRVisitor::VisitNode(const ExplodedNode *Succ, } else if (V.isUndef()) { os << "Uninitialized value stored to "; - } - else if (isa<nonloc::ConcreteInt>(V)) { - os << "The value " << cast<nonloc::ConcreteInt>(V).getValue() - << " is assigned to "; + } else if (Optional<nonloc::ConcreteInt> CV = + V.getAs<nonloc::ConcreteInt>()) { + os << "The value " << CV->getValue() << " is assigned to "; } else os << "Value assigned to "; @@ -535,7 +645,7 @@ PathDiagnosticPiece *FindLastStoreBRVisitor::VisitNode(const ExplodedNode *Succ, // Construct a new PathDiagnosticPiece. ProgramPoint P = StoreSite->getLocation(); PathDiagnosticLocation L; - if (isa<CallEnter>(P)) + if (P.getAs<CallEnter>() && InitE) L = PathDiagnosticLocation(InitE, BRC.getSourceManager(), P.getLocationContext()); else @@ -558,32 +668,38 @@ const char *TrackConstraintBRVisitor::getTag() { return "TrackConstraintBRVisitor"; } +bool TrackConstraintBRVisitor::isUnderconstrained(const ExplodedNode *N) const { + if (IsZeroCheck) + return N->getState()->isNull(Constraint).isUnderconstrained(); + return N->getState()->assume(Constraint, !Assumption); +} + PathDiagnosticPiece * TrackConstraintBRVisitor::VisitNode(const ExplodedNode *N, const ExplodedNode *PrevN, BugReporterContext &BRC, BugReport &BR) { - if (isSatisfied) + if (IsSatisfied) return NULL; // Check if in the previous state it was feasible for this constraint // to *not* be true. - if (PrevN->getState()->assume(Constraint, !Assumption)) { + if (isUnderconstrained(PrevN)) { - isSatisfied = true; + IsSatisfied = true; // As a sanity check, make sure that the negation of the constraint // was infeasible in the current state. If it is feasible, we somehow // missed the transition point. - if (N->getState()->assume(Constraint, !Assumption)) + if (isUnderconstrained(N)) return NULL; // We found the transition point for the constraint. We now need to // pretty-print the constraint. (work-in-progress) - std::string sbuf; - llvm::raw_string_ostream os(sbuf); + SmallString<64> sbuf; + llvm::raw_svector_ostream os(sbuf); - if (isa<Loc>(Constraint)) { + if (Constraint.getAs<Loc>()) { os << "Assuming pointer value is "; os << (Assumption ? "non-null" : "null"); } @@ -606,25 +722,151 @@ TrackConstraintBRVisitor::VisitNode(const ExplodedNode *N, return NULL; } -bool bugreporter::trackNullOrUndefValue(const ExplodedNode *N, const Stmt *S, - BugReport &report, bool IsArg) { +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; + + assert(N->getState()->isNull(V).isConstrainedTrue() && + "The visitor only tracks the cases where V is constrained to 0"); +} + +void SuppressInlineDefensiveChecksVisitor::Profile(FoldingSetNodeID &ID) const { + static int id = 0; + ID.AddPointer(&id); + ID.Add(V); +} + +const char *SuppressInlineDefensiveChecksVisitor::getTag() { + return "IDCVisitor"; +} + +PathDiagnosticPiece * +SuppressInlineDefensiveChecksVisitor::VisitNode(const ExplodedNode *Succ, + const ExplodedNode *Pred, + BugReporterContext &BRC, + BugReport &BR) { + if (IsSatisfied) + return 0; + + // Start tracking after we see the first state in which the value is null. + if (!IsTrackingTurnedOn) + if (Succ->getState()->isNull(V).isConstrainedTrue()) + IsTrackingTurnedOn = true; + if (!IsTrackingTurnedOn) + return 0; + + // Check if in the previous state it was feasible for this value + // to *not* be null. + if (!Pred->getState()->isNull(V).isConstrainedTrue()) { + IsSatisfied = true; + + assert(Succ->getState()->isNull(V).isConstrainedTrue()); + + // Check if this is inlined defensive checks. + const LocationContext *CurLC =Succ->getLocationContext(); + const LocationContext *ReportLC = BR.getErrorNode()->getLocationContext(); + if (CurLC != ReportLC && !CurLC->isParentOf(ReportLC)) + BR.markInvalid("Suppress IDC", CurLC); + } + return 0; +} + +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 (!VD->getType()->isReferenceType()) + return 0; + ProgramStateManager &StateMgr = N->getState()->getStateManager(); + MemRegionManager &MRMgr = StateMgr.getRegionManager(); + return MRMgr.getVarRegion(VD, N->getLocationContext()); + } + } + + // FIXME: This does not handle other kinds of null references, + // for example, references from FieldRegions: + // struct Wrapper { int &ref; }; + // Wrapper w = { *(int *)0 }; + // w.ref = 1; + + return 0; +} + +static const Expr *peelOffOuterExpr(const Expr *Ex, + const ExplodedNode *N) { + Ex = Ex->IgnoreParenCasts(); + if (const ExprWithCleanups *EWC = dyn_cast<ExprWithCleanups>(Ex)) + return peelOffOuterExpr(EWC->getSubExpr(), N); + if (const OpaqueValueExpr *OVE = dyn_cast<OpaqueValueExpr>(Ex)) + return peelOffOuterExpr(OVE->getSourceExpr(), N); + + // Peel off the ternary operator. + if (const ConditionalOperator *CO = dyn_cast<ConditionalOperator>(Ex)) { + // Find a node where the branching occured and find out which branch + // we took (true/false) by looking at the ExplodedGraph. + const ExplodedNode *NI = N; + do { + ProgramPoint ProgPoint = NI->getLocation(); + if (Optional<BlockEdge> BE = ProgPoint.getAs<BlockEdge>()) { + const CFGBlock *srcBlk = BE->getSrc(); + if (const Stmt *term = srcBlk->getTerminator()) { + if (term == CO) { + bool TookTrueBranch = (*(srcBlk->succ_begin()) == BE->getDst()); + if (TookTrueBranch) + return peelOffOuterExpr(CO->getTrueExpr(), N); + else + return peelOffOuterExpr(CO->getFalseExpr(), N); + } + } + } + NI = NI->getFirstPred(); + } while (NI); + } + return Ex; +} + +bool bugreporter::trackNullOrUndefValue(const ExplodedNode *N, + const Stmt *S, + BugReport &report, bool IsArg, + bool EnableNullFPSuppression) { if (!S || !N) return false; - if (const OpaqueValueExpr *OVE = dyn_cast<OpaqueValueExpr>(S)) - S = OVE->getSourceExpr(); + if (const Expr *Ex = dyn_cast<Expr>(S)) { + Ex = Ex->IgnoreParenCasts(); + const Expr *PeeledEx = peelOffOuterExpr(Ex, N); + if (Ex != PeeledEx) + S = PeeledEx; + } + + const Expr *Inner = 0; + if (const Expr *Ex = dyn_cast<Expr>(S)) { + Ex = Ex->IgnoreParenCasts(); + if (ExplodedGraph::isInterestingLValueExpr(Ex) || CallEvent::isCallStmt(Ex)) + Inner = Ex; + } if (IsArg) { - assert(isa<CallEnter>(N->getLocation()) && "Tracking arg but not at call"); + 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 (const PostStmt *ps = dyn_cast<PostStmt>(&pp)) { - if (ps->getStmt() == S) + if (Optional<StmtPoint> ps = pp.getAs<StmtPoint>()) { + if (ps->getStmt() == S || ps->getStmt() == Inner) break; - } else if (const CallExitEnd *CEE = dyn_cast<CallExitEnd>(&pp)) { - if (CEE->getCalleeContext()->getCallSite() == S) + } else if (Optional<CallExitEnd> CEE = pp.getAs<CallExitEnd>()) { + if (CEE->getCalleeContext()->getCallSite() == S || + CEE->getCalleeContext()->getCallSite() == Inner) break; } N = N->getFirstPred(); @@ -636,129 +878,167 @@ bool bugreporter::trackNullOrUndefValue(const ExplodedNode *N, const Stmt *S, ProgramStateRef state = N->getState(); - // See if the expression we're interested refers to a variable. + // The message send could be nil due to the receiver being nil. + // At this point in the path, the receiver should be live since we are at the + // message send expr. If it is nil, start tracking it. + if (const Expr *Receiver = NilReceiverBRVisitor::getNilReceiver(S, N)) + trackNullOrUndefValue(N, Receiver, report, IsArg, 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 (const Expr *Ex = dyn_cast<Expr>(S)) { - // Strip off parens and casts. Note that this will never have issues with - // C++ user-defined implicit conversions, because those have a constructor - // or function call inside. - Ex = Ex->IgnoreParenCasts(); - if (const DeclRefExpr *DR = dyn_cast<DeclRefExpr>(Ex)) { - // FIXME: Right now we only track VarDecls because it's non-trivial to - // get a MemRegion for any other DeclRefExprs. <rdar://problem/12114812> - if (const VarDecl *VD = dyn_cast<VarDecl>(DR->getDecl())) { - ProgramStateManager &StateMgr = state->getStateManager(); - MemRegionManager &MRMgr = StateMgr.getRegionManager(); - const VarRegion *R = MRMgr.getVarRegion(VD, N->getLocationContext()); - - // Mark both the variable region and its contents as interesting. - SVal V = state->getRawSVal(loc::MemRegionVal(R)); - - // If the value matches the default for the variable region, that - // might mean that it's been cleared out of the state. Fall back to - // the full argument expression (with casts and such intact). - if (IsArg) { - bool UseArgValue = V.isUnknownOrUndef() || V.isZeroConstant(); - if (!UseArgValue) { - const SymbolRegionValue *SRV = - dyn_cast_or_null<SymbolRegionValue>(V.getAsLocSymbol()); - if (SRV) - UseArgValue = (SRV->getRegion() == R); - } - if (UseArgValue) - V = state->getSValAsScalarOrLoc(S, N->getLocationContext()); + if (Inner && ExplodedGraph::isInterestingLValueExpr(Inner)) { + const MemRegion *R = 0; + + // 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."); + 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 additon, 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(new FindLastStoreBRVisitor(*KV, RR, + EnableNullFPSuppression)); + } + } + + if (R) { + // Mark both the variable region and its contents as interesting. + SVal V = state->getRawSVal(loc::MemRegionVal(R)); + + // If the value matches the default for the variable region, that + // might mean that it's been cleared out of the state. Fall back to + // the full argument expression (with casts and such intact). + if (IsArg) { + bool UseArgValue = V.isUnknownOrUndef() || V.isZeroConstant(); + if (!UseArgValue) { + const SymbolRegionValue *SRV = + dyn_cast_or_null<SymbolRegionValue>(V.getAsLocSymbol()); + if (SRV) + UseArgValue = (SRV->getRegion() == R); } + if (UseArgValue) + V = state->getSValAsScalarOrLoc(S, N->getLocationContext()); + } - report.markInteresting(R); - report.markInteresting(V); - report.addVisitor(new UndefOrNullArgVisitor(R)); + report.markInteresting(R); + report.markInteresting(V); + report.addVisitor(new UndefOrNullArgVisitor(R)); - // If the contents are symbolic, find out when they became null. - if (V.getAsLocSymbol()) { - BugReporterVisitor *ConstraintTracker - = new TrackConstraintBRVisitor(cast<DefinedSVal>(V), false); - report.addVisitor(ConstraintTracker); - } + if (isa<SymbolicRegion>(R)) { + TrackConstraintBRVisitor *VI = + new TrackConstraintBRVisitor(loc::MemRegionVal(R), false); + report.addVisitor(VI); + } - report.addVisitor(new FindLastStoreBRVisitor(V, R)); - return true; + // If the contents are symbolic, find out when they became null. + if (V.getAsLocSymbol()) { + BugReporterVisitor *ConstraintTracker = + new TrackConstraintBRVisitor(V.castAs<DefinedSVal>(), false); + report.addVisitor(ConstraintTracker); + + // Add visitor, which will suppress inline defensive checks. + if (N->getState()->isNull(V).isConstrainedTrue() && + EnableNullFPSuppression) { + BugReporterVisitor *IDCSuppressor = + new SuppressInlineDefensiveChecksVisitor(V.castAs<DefinedSVal>(), + N); + report.addVisitor(IDCSuppressor); + } } + + if (Optional<KnownSVal> KV = V.getAs<KnownSVal>()) + report.addVisitor(new FindLastStoreBRVisitor(*KV, R, + EnableNullFPSuppression)); + return true; } } - // If the expression does NOT refer to a variable, we can still track - // constraints on its contents. + // If the expression is not an "lvalue expression", we can still + // track the constraints on its contents. SVal V = state->getSValAsScalarOrLoc(S, N->getLocationContext()); + // 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)) + S = E->IgnoreParenCasts(); + + ReturnVisitor::addVisitorIfNecessary(N, S, report, EnableNullFPSuppression); + // Uncomment this to find cases where we aren't properly getting the // base value that was dereferenced. // assert(!V.isUnknownOrUndef()); - // Is it a symbolic value? - if (loc::MemRegionVal *L = dyn_cast<loc::MemRegionVal>(&V)) { + if (Optional<loc::MemRegionVal> L = V.getAs<loc::MemRegionVal>()) { // 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. SVal RVal = state->getSVal(L->getRegion()); const MemRegion *RegionRVal = RVal.getAsRegion(); report.addVisitor(new UndefOrNullArgVisitor(L->getRegion())); - if (RegionRVal && isa<SymbolicRegion>(RegionRVal)) { report.markInteresting(RegionRVal); report.addVisitor(new TrackConstraintBRVisitor( loc::MemRegionVal(RegionRVal), false)); } - } else { - // Otherwise, 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)) - S = E->IgnoreParenCasts(); - ReturnVisitor::addVisitorIfNecessary(N, S, report); } return true; } -BugReporterVisitor * -FindLastStoreBRVisitor::createVisitorObject(const ExplodedNode *N, - const MemRegion *R) { - assert(R && "The memory region is null."); - - ProgramStateRef state = N->getState(); - SVal V = state->getSVal(R); - if (V.isUnknown()) +const Expr *NilReceiverBRVisitor::getNilReceiver(const Stmt *S, + const ExplodedNode *N) { + const ObjCMessageExpr *ME = dyn_cast<ObjCMessageExpr>(S); + if (!ME) return 0; - - return new FindLastStoreBRVisitor(V, R); + if (const Expr *Receiver = ME->getInstanceReceiver()) { + ProgramStateRef state = N->getState(); + SVal V = state->getSVal(Receiver, N->getLocationContext()); + if (state->isNull(V).isConstrainedTrue()) + return Receiver; + } + return 0; } - PathDiagnosticPiece *NilReceiverBRVisitor::VisitNode(const ExplodedNode *N, const ExplodedNode *PrevN, BugReporterContext &BRC, BugReport &BR) { - const PostStmt *P = N->getLocationAs<PostStmt>(); + Optional<PreStmt> P = N->getLocationAs<PreStmt>(); if (!P) return 0; - const ObjCMessageExpr *ME = P->getStmtAs<ObjCMessageExpr>(); - if (!ME) - return 0; - const Expr *Receiver = ME->getInstanceReceiver(); + + const Expr *Receiver = getNilReceiver(P->getStmt(), N); if (!Receiver) return 0; - ProgramStateRef state = N->getState(); - const SVal &V = state->getSVal(Receiver, N->getLocationContext()); - const DefinedOrUnknownSVal *DV = dyn_cast<DefinedOrUnknownSVal>(&V); - if (!DV) - return 0; - state = state->assume(*DV, true); - if (state) - return 0; // The receiver was nil, and hence the method was skipped. // Register a BugReporterVisitor to issue a message telling us how // the receiver was null. - bugreporter::trackNullOrUndefValue(N, Receiver, BR); + bugreporter::trackNullOrUndefValue(N, Receiver, BR, /*IsArg*/ false, + /*EnableNullFPSuppression*/ false); // Issue a message saying that the method was skipped. PathDiagnosticLocation L(Receiver, BRC.getSourceManager(), N->getLocationContext()); @@ -768,7 +1048,8 @@ PathDiagnosticPiece *NilReceiverBRVisitor::VisitNode(const ExplodedNode *N, // Registers every VarDecl inside a Stmt with a last store visitor. void FindLastStoreBRVisitor::registerStatementVarDecls(BugReport &BR, - const Stmt *S) { + const Stmt *S, + bool EnableNullFPSuppression) { const ExplodedNode *N = BR.getErrorNode(); std::deque<const Stmt *> WorkList; WorkList.push_back(S); @@ -788,9 +1069,10 @@ void FindLastStoreBRVisitor::registerStatementVarDecls(BugReport &BR, // What did we load? SVal V = state->getSVal(S, N->getLocationContext()); - if (isa<loc::ConcreteInt>(V) || isa<nonloc::ConcreteInt>(V)) { + if (V.getAs<loc::ConcreteInt>() || V.getAs<nonloc::ConcreteInt>()) { // Register a new visitor with the BugReport. - BR.addVisitor(new FindLastStoreBRVisitor(V, R)); + BR.addVisitor(new FindLastStoreBRVisitor(V.castAs<KnownSVal>(), R, + EnableNullFPSuppression)); } } } @@ -842,14 +1124,14 @@ PathDiagnosticPiece *ConditionBRVisitor::VisitNodeImpl(const ExplodedNode *N, // If an assumption was made on a branch, it should be caught // here by looking at the state transition. - if (const BlockEdge *BE = dyn_cast<BlockEdge>(&progPoint)) { + if (Optional<BlockEdge> BE = progPoint.getAs<BlockEdge>()) { const CFGBlock *srcBlk = BE->getSrc(); if (const Stmt *term = srcBlk->getTerminator()) return VisitTerminator(term, N, srcBlk, BE->getDst(), BR, BRC); return 0; } - if (const PostStmt *PS = dyn_cast<PostStmt>(&progPoint)) { + 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 = @@ -929,11 +1211,11 @@ ConditionBRVisitor::VisitTrueTest(const Expr *Cond, } } -bool ConditionBRVisitor::patternMatch(const Expr *Ex, llvm::raw_ostream &Out, +bool ConditionBRVisitor::patternMatch(const Expr *Ex, raw_ostream &Out, BugReporterContext &BRC, BugReport &report, const ExplodedNode *N, - llvm::Optional<bool> &prunable) { + Optional<bool> &prunable) { const Expr *OriginalExpr = Ex; Ex = Ex->IgnoreParenCasts(); @@ -992,7 +1274,7 @@ ConditionBRVisitor::VisitTrueTest(const Expr *Cond, const ExplodedNode *N) { bool shouldInvert = false; - llvm::Optional<bool> shouldPrune; + Optional<bool> shouldPrune; SmallString<128> LhsString, RhsString; { @@ -1161,6 +1443,58 @@ ConditionBRVisitor::VisitTrueTest(const Expr *Cond, return event; } + +// FIXME: Copied from ExprEngineCallAndReturn.cpp. +static bool isInStdNamespace(const Decl *D) { + const DeclContext *DC = D->getDeclContext()->getEnclosingNamespaceContext(); + const NamespaceDecl *ND = dyn_cast<NamespaceDecl>(DC); + if (!ND) + return false; + + while (const NamespaceDecl *Parent = dyn_cast<NamespaceDecl>(ND->getParent())) + ND = Parent; + + return ND->getName() == "std"; +} + + +PathDiagnosticPiece * +LikelyFalsePositiveSuppressionBRVisitor::getEndPath(BugReporterContext &BRC, + const ExplodedNode *N, + BugReport &BR) { + // Here we suppress false positives coming from system headers. This list is + // based on known issues. + + // Skip reports within the 'std' namespace. Although these can sometimes be + // the user's fault, we currently don't report them very well, and + // Note that this will not help for any other data structure libraries, like + // TR1, Boost, or llvm/ADT. + ExprEngine &Eng = BRC.getBugReporter().getEngine(); + AnalyzerOptions &Options = Eng.getAnalysisManager().options; + if (Options.shouldSuppressFromCXXStandardLibrary()) { + const LocationContext *LCtx = N->getLocationContext(); + if (isInStdNamespace(LCtx->getDecl())) { + BR.markInvalid(getTag(), 0); + return 0; + } + } + + // Skip reports within the sys/queue.h macros as we do not have the ability to + // reason about data structure shapes. + SourceManager &SM = BRC.getSourceManager(); + FullSourceLoc Loc = BR.getLocation(SM).asLocation(); + while (Loc.isMacroID()) { + if (SM.isInSystemMacro(Loc) && + (SM.getFilename(SM.getSpellingLoc(Loc)).endswith("sys/queue.h"))) { + BR.markInvalid(getTag(), 0); + return 0; + } + Loc = Loc.getSpellingLoc(); + } + + return 0; +} + PathDiagnosticPiece * UndefOrNullArgVisitor::VisitNode(const ExplodedNode *N, const ExplodedNode *PrevN, @@ -1171,7 +1505,7 @@ UndefOrNullArgVisitor::VisitNode(const ExplodedNode *N, ProgramPoint ProgLoc = N->getLocation(); // We are only interested in visiting CallEnter nodes. - CallEnter *CEnter = dyn_cast<CallEnter>(&ProgLoc); + Optional<CallEnter> CEnter = ProgLoc.getAs<CallEnter>(); if (!CEnter) return 0; diff --git a/lib/StaticAnalyzer/Core/CallEvent.cpp b/lib/StaticAnalyzer/Core/CallEvent.cpp index c5cb317..45b2e21 100644 --- a/lib/StaticAnalyzer/Core/CallEvent.cpp +++ b/lib/StaticAnalyzer/Core/CallEvent.cpp @@ -14,11 +14,12 @@ //===----------------------------------------------------------------------===// #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" -#include "clang/Analysis/ProgramPoint.h" #include "clang/AST/ParentMap.h" +#include "clang/Analysis/ProgramPoint.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" #include "llvm/ADT/SmallSet.h" #include "llvm/ADT/StringExtras.h" +#include "llvm/Support/raw_ostream.h" using namespace clang; using namespace ento; @@ -124,7 +125,7 @@ static bool isPointerToConst(QualType Ty) { // Try to retrieve the function declaration and find the function parameter // types which are pointers/references to a non-pointer const. // We will not invalidate the corresponding argument regions. -static void findPtrToConstParams(llvm::SmallSet<unsigned, 1> &PreserveArgs, +static void findPtrToConstParams(llvm::SmallSet<unsigned, 4> &PreserveArgs, const CallEvent &Call) { unsigned Idx = 0; for (CallEvent::param_type_iterator I = Call.param_type_begin(), @@ -136,69 +137,35 @@ static void findPtrToConstParams(llvm::SmallSet<unsigned, 1> &PreserveArgs, } ProgramStateRef CallEvent::invalidateRegions(unsigned BlockCount, - ProgramStateRef Orig) const { + ProgramStateRef Orig) const { ProgramStateRef Result = (Orig ? Orig : getState()); - SmallVector<const MemRegion *, 8> RegionsToInvalidate; - getExtraInvalidatedRegions(RegionsToInvalidate); + SmallVector<SVal, 8> ConstValues; + SmallVector<SVal, 8> ValuesToInvalidate; + + getExtraInvalidatedValues(ValuesToInvalidate); // Indexes of arguments whose values will be preserved by the call. - llvm::SmallSet<unsigned, 1> PreserveArgs; + llvm::SmallSet<unsigned, 4> PreserveArgs; if (!argumentsMayEscape()) findPtrToConstParams(PreserveArgs, *this); for (unsigned Idx = 0, Count = getNumArgs(); Idx != Count; ++Idx) { + // Mark this region for invalidation. We batch invalidate regions + // below for efficiency. if (PreserveArgs.count(Idx)) - continue; - - SVal V = getArgSVal(Idx); - - // If we are passing a location wrapped as an integer, unwrap it and - // invalidate the values referred by the location. - if (nonloc::LocAsInteger *Wrapped = dyn_cast<nonloc::LocAsInteger>(&V)) - V = Wrapped->getLoc(); - else if (!isa<Loc>(V)) - continue; - - if (const MemRegion *R = V.getAsRegion()) { - // Invalidate the value of the variable passed by reference. - - // Are we dealing with an ElementRegion? If the element type is - // a basic integer type (e.g., char, int) and the underlying region - // is a variable region then strip off the ElementRegion. - // FIXME: We really need to think about this for the general case - // as sometimes we are reasoning about arrays and other times - // about (char*), etc., is just a form of passing raw bytes. - // e.g., void *p = alloca(); foo((char*)p); - if (const ElementRegion *ER = dyn_cast<ElementRegion>(R)) { - // Checking for 'integral type' is probably too promiscuous, but - // we'll leave it in for now until we have a systematic way of - // handling all of these cases. Eventually we need to come up - // with an interface to StoreManager so that this logic can be - // appropriately delegated to the respective StoreManagers while - // still allowing us to do checker-specific logic (e.g., - // invalidating reference counts), probably via callbacks. - if (ER->getElementType()->isIntegralOrEnumerationType()) { - const MemRegion *superReg = ER->getSuperRegion(); - if (isa<VarRegion>(superReg) || isa<FieldRegion>(superReg) || - isa<ObjCIvarRegion>(superReg)) - R = cast<TypedRegion>(superReg); - } - // FIXME: What about layers of ElementRegions? - } - - // Mark this region for invalidation. We batch invalidate regions - // below for efficiency. - RegionsToInvalidate.push_back(R); - } + ConstValues.push_back(getArgSVal(Idx)); + else + ValuesToInvalidate.push_back(getArgSVal(Idx)); } // Invalidate designated regions using the batch invalidation API. // NOTE: Even if RegionsToInvalidate is empty, we may still invalidate // global variables. - return Result->invalidateRegions(RegionsToInvalidate, getOriginExpr(), + return Result->invalidateRegions(ValuesToInvalidate, getOriginExpr(), BlockCount, getLocationContext(), - /*Symbols=*/0, this); + /*CausedByPointerEscape*/ true, + /*Symbols=*/0, this, ConstValues); } ProgramPoint CallEvent::getProgramPoint(bool IsPreVisit, @@ -268,7 +235,6 @@ bool CallEvent::isCallStmt(const Stmt *S) { || isa<CXXNewExpr>(S); } -/// \brief Returns the result type, adjusted for references. QualType CallEvent::getDeclaredResultType(const Decl *D) { assert(D); if (const FunctionDecl* FD = dyn_cast<FunctionDecl>(D)) @@ -405,9 +371,8 @@ const FunctionDecl *CXXInstanceCall::getDecl() const { return getSVal(CE->getCallee()).getAsFunctionDecl(); } -void CXXInstanceCall::getExtraInvalidatedRegions(RegionList &Regions) const { - if (const MemRegion *R = getCXXThisVal().getAsRegion()) - Regions.push_back(R); +void CXXInstanceCall::getExtraInvalidatedValues(ValueList &Values) const { + Values.push_back(getCXXThisVal()); } SVal CXXInstanceCall::getCXXThisVal() const { @@ -417,7 +382,7 @@ SVal CXXInstanceCall::getCXXThisVal() const { return UnknownVal(); SVal ThisVal = getSVal(Base); - assert(ThisVal.isUnknownOrUndef() || isa<Loc>(ThisVal)); + assert(ThisVal.isUnknownOrUndef() || ThisVal.getAs<Loc>()); return ThisVal; } @@ -560,10 +525,10 @@ CallEvent::param_iterator BlockCall::param_end() const { return D->param_end(); } -void BlockCall::getExtraInvalidatedRegions(RegionList &Regions) const { +void BlockCall::getExtraInvalidatedValues(ValueList &Values) const { // FIXME: This also needs to invalidate captured globals. if (const MemRegion *R = getBlockRegion()) - Regions.push_back(R); + Values.push_back(loc::MemRegionVal(R)); } void BlockCall::getInitialStackFrameContents(const StackFrameContext *CalleeCtx, @@ -581,9 +546,9 @@ SVal CXXConstructorCall::getCXXThisVal() const { return UnknownVal(); } -void CXXConstructorCall::getExtraInvalidatedRegions(RegionList &Regions) const { +void CXXConstructorCall::getExtraInvalidatedValues(ValueList &Values) const { if (Data) - Regions.push_back(static_cast<const MemRegion *>(Data)); + Values.push_back(loc::MemRegionVal(static_cast<const MemRegion *>(Data))); } void CXXConstructorCall::getInitialStackFrameContents( @@ -635,9 +600,8 @@ CallEvent::param_iterator ObjCMethodCall::param_end() const { } void -ObjCMethodCall::getExtraInvalidatedRegions(RegionList &Regions) const { - if (const MemRegion *R = getReceiverSVal().getAsRegion()) - Regions.push_back(R); +ObjCMethodCall::getExtraInvalidatedValues(ValueList &Values) const { + Values.push_back(getReceiverSVal()); } SVal ObjCMethodCall::getSelfSVal() const { @@ -834,7 +798,34 @@ RuntimeDefinition ObjCMethodCall::getRuntimeDefinition() const { // Lookup the method implementation. if (ReceiverT) if (ObjCInterfaceDecl *IDecl = ReceiverT->getInterfaceDecl()) { - const ObjCMethodDecl *MD = IDecl->lookupPrivateMethod(Sel); + // Repeatedly calling lookupPrivateMethod() is expensive, especially + // when in many cases it returns null. We cache the results so + // that repeated queries on the same ObjCIntefaceDecl and Selector + // don't incur the same cost. On some test cases, we can see the + // same query being issued thousands of times. + // + // NOTE: This cache is essentially a "global" variable, but it + // only gets lazily created when we get here. The value of the + // cache probably comes from it being global across ExprEngines, + // where the same queries may get issued. If we are worried about + // concurrency, or possibly loading/unloading ASTs, etc., we may + // 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; + + static PrivateMethodCache PMC; + Optional<const ObjCMethodDecl *> &Val = PMC[std::make_pair(IDecl, Sel)]; + + // Query lookupPrivateMethod() if the cache does not hit. + if (!Val.hasValue()) + Val = IDecl->lookupPrivateMethod(Sel); + + const ObjCMethodDecl *MD = Val.getValue(); if (CanBeSubClassed) return RuntimeDefinition(MD, Receiver); else @@ -931,8 +922,9 @@ CallEventManager::getCaller(const StackFrameContext *CalleeCtx, // destructors, though this could change in the future. const CFGBlock *B = CalleeCtx->getCallSiteBlock(); CFGElement E = (*B)[CalleeCtx->getIndex()]; - assert(isa<CFGImplicitDtor>(E) && "All other CFG elements should have exprs"); - assert(!isa<CFGTemporaryDtor>(E) && "We don't handle temporaries yet"); + assert(E.getAs<CFGImplicitDtor>() && + "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()); @@ -940,11 +932,12 @@ CallEventManager::getCaller(const StackFrameContext *CalleeCtx, SVal ThisVal = State->getSVal(ThisPtr); const Stmt *Trigger; - if (const CFGAutomaticObjDtor *AutoDtor = dyn_cast<CFGAutomaticObjDtor>(&E)) + if (Optional<CFGAutomaticObjDtor> AutoDtor = E.getAs<CFGAutomaticObjDtor>()) Trigger = AutoDtor->getTriggerStmt(); else Trigger = Dtor->getBody(); return getCXXDestructorCall(Dtor, Trigger, ThisVal.getAsRegion(), - isa<CFGBaseDtor>(E), State, CallerCtx); + E.getAs<CFGBaseDtor>().hasValue(), State, + CallerCtx); } diff --git a/lib/StaticAnalyzer/Core/CheckerManager.cpp b/lib/StaticAnalyzer/Core/CheckerManager.cpp index 3672952..8adf326 100644 --- a/lib/StaticAnalyzer/Core/CheckerManager.cpp +++ b/lib/StaticAnalyzer/Core/CheckerManager.cpp @@ -12,11 +12,11 @@ //===----------------------------------------------------------------------===// #include "clang/StaticAnalyzer/Core/CheckerManager.h" +#include "clang/AST/DeclBase.h" +#include "clang/Analysis/ProgramPoint.h" #include "clang/StaticAnalyzer/Core/Checker.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" -#include "clang/Analysis/ProgramPoint.h" -#include "clang/AST/DeclBase.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" using namespace clang; using namespace ento; @@ -30,7 +30,7 @@ bool CheckerManager::hasPathSensitiveCheckers() const { !LocationCheckers.empty() || !BindCheckers.empty() || !EndAnalysisCheckers.empty() || - !EndPathCheckers.empty() || + !EndFunctionCheckers.empty() || !BranchConditionCheckers.empty() || !LiveSymbolsCheckers.empty() || !DeadSymbolsCheckers.empty() || @@ -353,17 +353,17 @@ void CheckerManager::runCheckersForEndAnalysis(ExplodedGraph &G, /// \brief 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::runCheckersForEndPath(NodeBuilderContext &BC, - ExplodedNodeSet &Dst, - ExplodedNode *Pred, - ExprEngine &Eng) { +void CheckerManager::runCheckersForEndFunction(NodeBuilderContext &BC, + ExplodedNodeSet &Dst, + ExplodedNode *Pred, + ExprEngine &Eng) { // 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 = EndPathCheckers.size(); i != e; ++i) { - CheckEndPathFunc checkFn = EndPathCheckers[i]; + for (unsigned i = 0, e = EndFunctionCheckers.size(); i != e; ++i) { + CheckEndFunctionFunc checkFn = EndFunctionCheckers[i]; const ProgramPoint &L = BlockEntrance(BC.Block, Pred->getLocationContext(), @@ -469,10 +469,10 @@ bool CheckerManager::wantsRegionChangeUpdate(ProgramStateRef state) { /// \brief Run checkers for region changes. ProgramStateRef CheckerManager::runCheckersForRegionChanges(ProgramStateRef state, - const StoreManager::InvalidatedSymbols *invalidated, + const InvalidatedSymbols *invalidated, ArrayRef<const MemRegion *> ExplicitRegions, - ArrayRef<const MemRegion *> Regions, - const CallEvent *Call) { + ArrayRef<const MemRegion *> Regions, + const CallEvent *Call) { for (unsigned i = 0, e = RegionChangesCheckers.size(); i != e; ++i) { // If any checker declares the state infeasible (or if it starts that way), // bail out. @@ -484,6 +484,27 @@ CheckerManager::runCheckersForRegionChanges(ProgramStateRef state, return state; } +/// \brief Run checkers to process symbol escape event. +ProgramStateRef +CheckerManager::runCheckersForPointerEscape(ProgramStateRef State, + const InvalidatedSymbols &Escaped, + const CallEvent *Call, + PointerEscapeKind Kind, + bool IsConst) { + assert((Call != NULL || + (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 NULL; + State = PointerEscapeCheckers[i](State, Escaped, Call, Kind, IsConst); + } + return State; +} + /// \brief Run checkers for handling assumptions on symbolic values. ProgramStateRef CheckerManager::runCheckersForEvalAssume(ProgramStateRef state, @@ -618,8 +639,8 @@ void CheckerManager::_registerForEndAnalysis(CheckEndAnalysisFunc checkfn) { EndAnalysisCheckers.push_back(checkfn); } -void CheckerManager::_registerForEndPath(CheckEndPathFunc checkfn) { - EndPathCheckers.push_back(checkfn); +void CheckerManager::_registerForEndFunction(CheckEndFunctionFunc checkfn) { + EndFunctionCheckers.push_back(checkfn); } void CheckerManager::_registerForBranchCondition( @@ -641,6 +662,15 @@ void CheckerManager::_registerForRegionChanges(CheckRegionChangesFunc checkfn, RegionChangesCheckers.push_back(info); } +void CheckerManager::_registerForPointerEscape(CheckPointerEscapeFunc checkfn){ + PointerEscapeCheckers.push_back(checkfn); +} + +void CheckerManager::_registerForConstPointerEscape( + CheckPointerEscapeFunc checkfn) { + PointerEscapeCheckers.push_back(checkfn); +} + void CheckerManager::_registerForEvalAssume(EvalAssumeFunc checkfn) { EvalAssumeCheckers.push_back(checkfn); } diff --git a/lib/StaticAnalyzer/Core/CheckerRegistry.cpp b/lib/StaticAnalyzer/Core/CheckerRegistry.cpp index 9791e2ec..4729903 100644 --- a/lib/StaticAnalyzer/Core/CheckerRegistry.cpp +++ b/lib/StaticAnalyzer/Core/CheckerRegistry.cpp @@ -10,6 +10,7 @@ #include "clang/StaticAnalyzer/Core/CheckerRegistry.h" #include "clang/StaticAnalyzer/Core/CheckerOptInfo.h" #include "llvm/ADT/SetVector.h" +#include "llvm/Support/raw_ostream.h" using namespace clang; using namespace ento; @@ -109,7 +110,7 @@ void CheckerRegistry::initializeManager(CheckerManager &checkerMgr, } } -void CheckerRegistry::printHelp(llvm::raw_ostream &out, +void CheckerRegistry::printHelp(raw_ostream &out, size_t maxNameChars) const { // FIXME: Alphabetical sort puts 'experimental' in the middle. // Would it be better to name it '~experimental' or something else diff --git a/lib/StaticAnalyzer/Core/CoreEngine.cpp b/lib/StaticAnalyzer/Core/CoreEngine.cpp index ec23792..b09b2c2 100644 --- a/lib/StaticAnalyzer/Core/CoreEngine.cpp +++ b/lib/StaticAnalyzer/Core/CoreEngine.cpp @@ -14,14 +14,14 @@ #define DEBUG_TYPE "CoreEngine" -#include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CoreEngine.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h" #include "clang/AST/Expr.h" #include "clang/AST/StmtCXX.h" -#include "llvm/Support/Casting.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/Statistic.h" +#include "llvm/Support/Casting.h" using namespace clang; using namespace ento; @@ -114,7 +114,7 @@ namespace { } virtual void enqueue(const WorkListUnit& U) { - if (isa<BlockEntrance>(U.getNode()->getLocation())) + if (U.getNode()->getLocation().getAs<BlockEntrance>()) Queue.push_front(U); else Stack.push_back(U); @@ -230,11 +230,11 @@ void CoreEngine::dispatchWorkItem(ExplodedNode* Pred, ProgramPoint Loc, // Dispatch on the location type. switch (Loc.getKind()) { case ProgramPoint::BlockEdgeKind: - HandleBlockEdge(cast<BlockEdge>(Loc), Pred); + HandleBlockEdge(Loc.castAs<BlockEdge>(), Pred); break; case ProgramPoint::BlockEntranceKind: - HandleBlockEntrance(cast<BlockEntrance>(Loc), Pred); + HandleBlockEntrance(Loc.castAs<BlockEntrance>(), Pred); break; case ProgramPoint::BlockExitKind: @@ -242,7 +242,7 @@ void CoreEngine::dispatchWorkItem(ExplodedNode* Pred, ProgramPoint Loc, break; case ProgramPoint::CallEnterKind: { - CallEnter CEnter = cast<CallEnter>(Loc); + CallEnter CEnter = Loc.castAs<CallEnter>(); SubEng.processCallEnter(CEnter, Pred); break; } @@ -259,10 +259,10 @@ void CoreEngine::dispatchWorkItem(ExplodedNode* Pred, ProgramPoint Loc, break; } default: - assert(isa<PostStmt>(Loc) || - isa<PostInitializer>(Loc) || - isa<PostImplicitCall>(Loc) || - isa<CallExitEnd>(Loc)); + assert(Loc.getAs<PostStmt>() || + Loc.getAs<PostInitializer>() || + Loc.getAs<PostImplicitCall>() || + Loc.getAs<CallExitEnd>()); HandlePostStmt(WU.getBlock(), WU.getIndex(), Pred); break; } @@ -331,9 +331,9 @@ void CoreEngine::HandleBlockEntrance(const BlockEntrance &L, WList->setBlockCounter(Counter); // Process the entrance of the block. - if (CFGElement E = L.getFirstElement()) { + if (Optional<CFGElement> E = L.getFirstElement()) { NodeBuilderContext Ctx(*this, L.getBlock(), Pred); - SubEng.processCFGElement(E, Pred, 0, &Ctx); + SubEng.processCFGElement(*E, Pred, 0, &Ctx); } else HandleBlockExit(L.getBlock(), Pred); @@ -346,6 +346,11 @@ void CoreEngine::HandleBlockExit(const CFGBlock * B, ExplodedNode *Pred) { default: llvm_unreachable("Analysis for this terminator not implemented."); + // Model static initializers. + case Stmt::DeclStmtClass: + HandleStaticInit(cast<DeclStmt>(Term), B, Pred); + return; + case Stmt::BinaryOperatorClass: // '&&' and '||' HandleBranch(cast<BinaryOperator>(Term)->getLHS(), Term, B, Pred); return; @@ -456,6 +461,19 @@ void CoreEngine::HandleBranch(const Stmt *Cond, const Stmt *Term, enqueue(Dst); } + +void CoreEngine::HandleStaticInit(const DeclStmt *DS, const CFGBlock *B, + ExplodedNode *Pred) { + assert(B->succ_size() == 2); + NodeBuilderContext Ctx(*this, B, Pred); + ExplodedNodeSet Dst; + SubEng.processStaticInitializer(DS, Ctx, Pred, Dst, + *(B->succ_begin()), *(B->succ_begin()+1)); + // Enqueue the new frontier onto the worklist. + enqueue(Dst); +} + + void CoreEngine::HandlePostStmt(const CFGBlock *B, unsigned StmtIdx, ExplodedNode *Pred) { assert(B); @@ -495,7 +513,7 @@ void CoreEngine::enqueueStmtNode(ExplodedNode *N, assert (!N->isSink()); // Check if this node entered a callee. - if (isa<CallEnter>(N->getLocation())) { + if (N->getLocation().getAs<CallEnter>()) { // Still use the index of the CallExpr. It's needed to create the callee // StackFrameContext. WList->enqueue(N, Block, Idx); @@ -503,19 +521,19 @@ void CoreEngine::enqueueStmtNode(ExplodedNode *N, } // Do not create extra nodes. Move to the next CFG element. - if (isa<PostInitializer>(N->getLocation()) || - isa<PostImplicitCall>(N->getLocation())) { + if (N->getLocation().getAs<PostInitializer>() || + N->getLocation().getAs<PostImplicitCall>()) { WList->enqueue(N, Block, Idx+1); return; } - if (isa<EpsilonPoint>(N->getLocation())) { + if (N->getLocation().getAs<EpsilonPoint>()) { WList->enqueue(N, Block, Idx); return; } // At this point, we know we're processing a normal statement. - CFGStmt CS = cast<CFGStmt>((*Block)[Idx]); + CFGStmt CS = (*Block)[Idx].castAs<CFGStmt>(); PostStmt Loc(CS.getStmt(), N->getLocationContext()); if (Loc == N->getLocation()) { diff --git a/lib/StaticAnalyzer/Core/Environment.cpp b/lib/StaticAnalyzer/Core/Environment.cpp index bab89c5..fe352aa 100644 --- a/lib/StaticAnalyzer/Core/Environment.cpp +++ b/lib/StaticAnalyzer/Core/Environment.cpp @@ -16,6 +16,7 @@ #include "clang/Analysis/AnalysisContext.h" #include "clang/Analysis/CFG.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" +#include "llvm/Support/raw_ostream.h" using namespace clang; using namespace ento; @@ -36,9 +37,6 @@ static const Expr *ignoreTransparentExprs(const Expr *E) { case Stmt::SubstNonTypeTemplateParmExprClass: E = cast<SubstNonTypeTemplateParmExpr>(E)->getReplacement(); break; - case Stmt::CXXDefaultArgExprClass: - E = cast<CXXDefaultArgExpr>(E)->getExpr(); - break; default: // This is the base case: we can't look through more than we already have. return E; @@ -74,7 +72,6 @@ SVal Environment::getSVal(const EnvironmentEntry &Entry, switch (S->getStmtClass()) { case Stmt::CXXBindTemporaryExprClass: - case Stmt::CXXDefaultArgExprClass: case Stmt::ExprWithCleanupsClass: case Stmt::GenericSelectionExprClass: case Stmt::OpaqueValueExprClass: @@ -149,19 +146,6 @@ Environment EnvironmentManager::bindExpr(Environment Env, return Environment(F.add(Env.ExprBindings, E, V)); } -EnvironmentEntry EnvironmentEntry::makeLocation() const { - EnvironmentEntry Result = *this; - reinterpret_cast<uintptr_t &>(Result.first) |= 0x1; - return Result; -} - -Environment EnvironmentManager::bindExprAndLocation(Environment Env, - const EnvironmentEntry &E, - SVal location, SVal V) { - return Environment(F.add(F.add(Env.ExprBindings, E.makeLocation(), location), - E, V)); -} - namespace { class MarkLiveCallback : public SymbolVisitor { SymbolReaper &SymReaper; @@ -178,14 +162,6 @@ public: }; } // end anonymous namespace -// In addition to mapping from EnvironmentEntry - > SVals in the Environment, -// we also maintain a mapping from EnvironmentEntry -> SVals (locations) -// that were used during a load and store. -static inline bool IsLocation(const EnvironmentEntry &E) { - const Stmt *S = E.getStmt(); - return (bool) (((uintptr_t) S) & 0x1); -} - // removeDeadBindings: // - Remove subexpression bindings. // - Remove dead block expression bindings. @@ -202,8 +178,6 @@ EnvironmentManager::removeDeadBindings(Environment Env, // individually removing all the subexpression bindings (which will greatly // outnumber block-level expression bindings). Environment NewEnv = getInitialEnvironment(); - - SmallVector<std::pair<EnvironmentEntry, SVal>, 10> deferredLocations; MarkLiveCallback CB(SymReaper); ScanReachableSymbols RSScaner(ST, CB); @@ -217,15 +191,6 @@ EnvironmentManager::removeDeadBindings(Environment Env, I != E; ++I) { const EnvironmentEntry &BlkExpr = I.getKey(); - // For recorded locations (used when evaluating loads and stores), we - // consider them live only when their associated normal expression is - // also live. - // NOTE: This assumes that loads/stores that evaluated to UnknownVal - // still have an entry in the map. - if (IsLocation(BlkExpr)) { - deferredLocations.push_back(std::make_pair(BlkExpr, I.getData())); - continue; - } const SVal &X = I.getData(); if (SymReaper.isLive(BlkExpr.getStmt(), BlkExpr.getLocationContext())) { @@ -233,26 +198,18 @@ EnvironmentManager::removeDeadBindings(Environment Env, EBMapRef = EBMapRef.add(BlkExpr, X); // If the block expr's value is a memory region, then mark that region. - if (isa<loc::MemRegionVal>(X)) { - const MemRegion *R = cast<loc::MemRegionVal>(X).getRegion(); - SymReaper.markLive(R); - } + if (Optional<loc::MemRegionVal> R = X.getAs<loc::MemRegionVal>()) + SymReaper.markLive(R->getRegion()); // Mark all symbols in the block expr's value live. RSScaner.scan(X); continue; + } else { + SymExpr::symbol_iterator SI = X.symbol_begin(), SE = X.symbol_end(); + for (; SI != SE; ++SI) + SymReaper.maybeDead(*SI); } } - - // Go through he deferred locations and add them to the new environment if - // the correspond Stmt* is in the map as well. - for (SmallVectorImpl<std::pair<EnvironmentEntry, SVal> >::iterator - I = deferredLocations.begin(), E = deferredLocations.end(); I != E; ++I) { - const EnvironmentEntry &En = I->first; - const Stmt *S = (Stmt*) (((uintptr_t) En.getStmt()) & (uintptr_t) ~0x1); - if (EBMapRef.lookup(EnvironmentEntry(S, En.getLocationContext()))) - EBMapRef = EBMapRef.add(En, I->second); - } NewEnv.ExprBindings = EBMapRef.asImmutableMap(); return NewEnv; @@ -260,30 +217,14 @@ EnvironmentManager::removeDeadBindings(Environment Env, void Environment::print(raw_ostream &Out, const char *NL, const char *Sep) const { - printAux(Out, false, NL, Sep); - printAux(Out, true, NL, Sep); -} - -void Environment::printAux(raw_ostream &Out, bool printLocations, - const char *NL, - const char *Sep) const{ - bool isFirst = true; for (Environment::iterator I = begin(), E = end(); I != E; ++I) { const EnvironmentEntry &En = I.getKey(); - if (IsLocation(En)) { - if (!printLocations) - continue; - } - else { - if (printLocations) - continue; - } if (isFirst) { Out << NL << NL - << (printLocations ? "Load/Store locations:" : "Expressions:") + << "Expressions:" << NL; isFirst = false; } else { @@ -291,9 +232,6 @@ void Environment::printAux(raw_ostream &Out, bool printLocations, } const Stmt *S = En.getStmt(); - if (printLocations) { - S = (Stmt*) (((uintptr_t) S) & ((uintptr_t) ~0x1)); - } Out << " (" << (const void*) En.getLocationContext() << ',' << (const void*) S << ") "; diff --git a/lib/StaticAnalyzer/Core/ExplodedGraph.cpp b/lib/StaticAnalyzer/Core/ExplodedGraph.cpp index c284bd7..af9518a 100644 --- a/lib/StaticAnalyzer/Core/ExplodedGraph.cpp +++ b/lib/StaticAnalyzer/Core/ExplodedGraph.cpp @@ -13,12 +13,12 @@ //===----------------------------------------------------------------------===// #include "clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h" +#include "clang/AST/ParentMap.h" +#include "clang/AST/Stmt.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" -#include "clang/AST/Stmt.h" -#include "clang/AST/ParentMap.h" -#include "llvm/ADT/DenseSet.h" #include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/DenseSet.h" #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/Statistic.h" #include <vector> @@ -56,19 +56,42 @@ ExplodedGraph::~ExplodedGraph() {} // Node reclamation. //===----------------------------------------------------------------------===// +bool ExplodedGraph::isInterestingLValueExpr(const Expr *Ex) { + if (!Ex->isLValue()) + return false; + return isa<DeclRefExpr>(Ex) || + isa<MemberExpr>(Ex) || + isa<ObjCIvarRefExpr>(Ex); +} + bool ExplodedGraph::shouldCollect(const ExplodedNode *node) { - // Reclaim all nodes that match *all* the following criteria: + // First, we only consider nodes for reclamation of the following + // conditions apply: // // (1) 1 predecessor (that has one successor) // (2) 1 successor (that has one predecessor) + // + // If a node has no successor it is on the "frontier", while a node + // with no predecessor is a root. + // + // After these prerequisites, we discard all "filler" nodes that + // are used only for intermediate processing, and are not essential + // for analyzer history: + // + // (a) PreStmtPurgeDeadSymbols + // + // We then discard all other nodes where *all* of the following conditions + // apply: + // // (3) The ProgramPoint is for a PostStmt, but not a PostStore. // (4) There is no 'tag' for the ProgramPoint. // (5) The 'store' is the same as the predecessor. // (6) The 'GDM' is the same as the predecessor. // (7) The LocationContext is the same as the predecessor. - // (8) The PostStmt isn't for a non-consumed Stmt or Expr. - // (9) The successor is not a CallExpr StmtPoint (so that we would be able to - // find it when retrying a call with no inlining). + // (8) Expressions that are *not* lvalue expressions. + // (9) The PostStmt isn't for a non-consumed Stmt or Expr. + // (10) The successor is not a CallExpr StmtPoint (so that we would + // be able to find it when retrying a call with no inlining). // FIXME: It may be safe to reclaim PreCall and PostCall nodes as well. // Conditions 1 and 2. @@ -83,14 +106,18 @@ bool ExplodedGraph::shouldCollect(const ExplodedNode *node) { if (succ->pred_size() != 1) return false; - // Condition 3. + // Now reclaim any nodes that are (by definition) not essential to + // analysis history and are not consulted by any client code. ProgramPoint progPoint = node->getLocation(); - if (!isa<PostStmt>(progPoint) || isa<PostStore>(progPoint)) + if (progPoint.getAs<PreStmtPurgeDeadSymbols>()) + return !progPoint.getTag(); + + // Condition 3. + if (!progPoint.getAs<PostStmt>() || progPoint.getAs<PostStore>()) return false; // Condition 4. - PostStmt ps = cast<PostStmt>(progPoint); - if (ps.getTag()) + if (progPoint.getTag()) return false; // Conditions 5, 6, and 7. @@ -99,23 +126,30 @@ bool ExplodedGraph::shouldCollect(const ExplodedNode *node) { if (state->store != pred_state->store || state->GDM != pred_state->GDM || progPoint.getLocationContext() != pred->getLocationContext()) return false; - + + // All further checks require expressions. As per #3, we know that we have + // a PostStmt. + const Expr *Ex = dyn_cast<Expr>(progPoint.castAs<PostStmt>().getStmt()); + if (!Ex) + return false; + // Condition 8. + // Do not collect nodes for "interesting" lvalue expressions since they are + // used extensively for generating path diagnostics. + if (isInterestingLValueExpr(Ex)) + return false; + + // Condition 9. // Do not collect nodes for non-consumed Stmt or Expr to ensure precise // diagnostic generation; specifically, so that we could anchor arrows // pointing to the beginning of statements (as written in code). - if (!isa<Expr>(ps.getStmt())) + ParentMap &PM = progPoint.getLocationContext()->getParentMap(); + if (!PM.isConsumedExpr(Ex)) return false; - - if (const Expr *Ex = dyn_cast<Expr>(ps.getStmt())) { - ParentMap &PM = progPoint.getLocationContext()->getParentMap(); - if (!PM.isConsumedExpr(Ex)) - return false; - } - - // Condition 9. + + // Condition 10. const ProgramPoint SuccLoc = succ->getLocation(); - if (const StmtPoint *SP = dyn_cast<StmtPoint>(&SuccLoc)) + if (Optional<StmtPoint> SP = SuccLoc.getAs<StmtPoint>()) if (CallEvent::isCallStmt(SP->getStmt())) return false; @@ -297,45 +331,31 @@ ExplodedNode *ExplodedGraph::getNode(const ProgramPoint &L, return V; } -std::pair<ExplodedGraph*, InterExplodedGraphMap*> -ExplodedGraph::Trim(const NodeTy* const* NBeg, const NodeTy* const* NEnd, - llvm::DenseMap<const void*, const void*> *InverseMap) const { - - if (NBeg == NEnd) - return std::make_pair((ExplodedGraph*) 0, - (InterExplodedGraphMap*) 0); +ExplodedGraph * +ExplodedGraph::trim(ArrayRef<const NodeTy *> Sinks, + InterExplodedGraphMap *ForwardMap, + InterExplodedGraphMap *InverseMap) const{ - assert (NBeg < NEnd); - - OwningPtr<InterExplodedGraphMap> M(new InterExplodedGraphMap()); - - ExplodedGraph* G = TrimInternal(NBeg, NEnd, M.get(), InverseMap); - - return std::make_pair(static_cast<ExplodedGraph*>(G), M.take()); -} - -ExplodedGraph* -ExplodedGraph::TrimInternal(const ExplodedNode* const* BeginSources, - const ExplodedNode* const* EndSources, - InterExplodedGraphMap* M, - llvm::DenseMap<const void*, const void*> *InverseMap) const { + if (Nodes.empty()) + return 0; typedef llvm::DenseSet<const ExplodedNode*> Pass1Ty; Pass1Ty Pass1; - typedef llvm::DenseMap<const ExplodedNode*, ExplodedNode*> Pass2Ty; - Pass2Ty& Pass2 = M->M; + typedef InterExplodedGraphMap Pass2Ty; + InterExplodedGraphMap Pass2Scratch; + Pass2Ty &Pass2 = ForwardMap ? *ForwardMap : Pass2Scratch; SmallVector<const ExplodedNode*, 10> WL1, WL2; // ===- Pass 1 (reverse DFS) -=== - for (const ExplodedNode* const* I = BeginSources; I != EndSources; ++I) { + for (ArrayRef<const NodeTy *>::iterator I = Sinks.begin(), E = Sinks.end(); + I != E; ++I) { if (*I) WL1.push_back(*I); } - // Process the first worklist until it is empty. Because it is a std::list - // it acts like a FIFO queue. + // Process the first worklist until it is empty. while (!WL1.empty()) { const ExplodedNode *N = WL1.back(); WL1.pop_back(); @@ -398,7 +418,7 @@ ExplodedGraph::TrimInternal(const ExplodedNode* const* BeginSources, if (PI == Pass2.end()) continue; - NewN->addPredecessor(PI->second, *G); + NewN->addPredecessor(const_cast<ExplodedNode *>(PI->second), *G); } // In the case that some of the intended successors of NewN have already @@ -409,7 +429,7 @@ ExplodedGraph::TrimInternal(const ExplodedNode* const* BeginSources, I != E; ++I) { Pass2Ty::iterator PI = Pass2.find(*I); if (PI != Pass2.end()) { - PI->second->addPredecessor(NewN, *G); + const_cast<ExplodedNode *>(PI->second)->addPredecessor(NewN, *G); continue; } @@ -422,13 +442,3 @@ ExplodedGraph::TrimInternal(const ExplodedNode* const* BeginSources, return G; } -void InterExplodedGraphMap::anchor() { } - -ExplodedNode* -InterExplodedGraphMap::getMappedNode(const ExplodedNode *N) const { - llvm::DenseMap<const ExplodedNode*, ExplodedNode*>::const_iterator I = - M.find(N); - - return I == M.end() ? 0 : I->second; -} - diff --git a/lib/StaticAnalyzer/Core/ExprEngine.cpp b/lib/StaticAnalyzer/Core/ExprEngine.cpp index 045591c..ab4dbd7 100644 --- a/lib/StaticAnalyzer/Core/ExprEngine.cpp +++ b/lib/StaticAnalyzer/Core/ExprEngine.cpp @@ -15,21 +15,21 @@ #define DEBUG_TYPE "ExprEngine" -#include "clang/StaticAnalyzer/Core/CheckerManager.h" -#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h" #include "clang/AST/CharUnits.h" #include "clang/AST/ParentMap.h" -#include "clang/AST/StmtObjC.h" #include "clang/AST/StmtCXX.h" +#include "clang/AST/StmtObjC.h" #include "clang/Basic/Builtins.h" -#include "clang/Basic/SourceManager.h" #include "clang/Basic/PrettyStackTrace.h" -#include "llvm/Support/raw_ostream.h" +#include "clang/Basic/SourceManager.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 "llvm/ADT/ImmutableList.h" #include "llvm/ADT/Statistic.h" +#include "llvm/Support/raw_ostream.h" #ifndef NDEBUG #include "llvm/Support/GraphWriter.h" @@ -56,7 +56,8 @@ STATISTIC(NumTimesRetriedWithoutInlining, ExprEngine::ExprEngine(AnalysisManager &mgr, bool gcEnabled, SetOfConstDecls *VisitedCalleesIn, - FunctionSummariesTy *FS) + FunctionSummariesTy *FS, + InliningModes HowToInlineIn) : AMgr(mgr), AnalysisDeclContexts(mgr.getAnalysisDeclContextManager()), Engine(*this, FS), @@ -66,11 +67,11 @@ ExprEngine::ExprEngine(AnalysisManager &mgr, bool gcEnabled, this), SymMgr(StateMgr.getSymbolManager()), svalBuilder(StateMgr.getSValBuilder()), - EntryNode(NULL), - currStmt(NULL), currStmtIdx(0), currBldrCtx(0), + currStmtIdx(0), currBldrCtx(0), ObjCNoRet(mgr.getASTContext()), ObjCGCEnabled(gcEnabled), BR(mgr, *this), - VisitedCallees(VisitedCalleesIn) + VisitedCallees(VisitedCalleesIn), + HowToInline(HowToInlineIn) { unsigned TrimInterval = mgr.options.getGraphTrimInterval(); if (TrimInterval != 0) { @@ -117,8 +118,8 @@ ProgramStateRef ExprEngine::getInitialState(const LocationContext *InitLoc) { svalBuilder.makeZeroVal(T), getContext().IntTy); - DefinedOrUnknownSVal *Constraint = - dyn_cast<DefinedOrUnknownSVal>(&Constraint_untested); + Optional<DefinedOrUnknownSVal> Constraint = + Constraint_untested.getAs<DefinedOrUnknownSVal>(); if (!Constraint) break; @@ -137,7 +138,7 @@ ProgramStateRef ExprEngine::getInitialState(const LocationContext *InitLoc) { const MemRegion *R = state->getRegion(SelfD, InitLoc); SVal V = state->getSVal(loc::MemRegionVal(R)); - if (const Loc *LV = dyn_cast<Loc>(&V)) { + if (Optional<Loc> LV = V.getAs<Loc>()) { // Assume that the pointer value in 'self' is non-null. state = state->assume(*LV, true); assert(state && "'self' cannot be null"); @@ -153,7 +154,7 @@ ProgramStateRef ExprEngine::getInitialState(const LocationContext *InitLoc) { if (SFC->getParent() == 0) { loc::MemRegionVal L = svalBuilder.getCXXThis(MD, SFC); SVal V = state->getSVal(L); - if (const Loc *LV = dyn_cast<Loc>(&V)) { + if (Optional<Loc> LV = V.getAs<Loc>()) { state = state->assume(*LV, true); assert(state && "'this' cannot be null"); } @@ -164,20 +165,63 @@ ProgramStateRef ExprEngine::getInitialState(const LocationContext *InitLoc) { return state; } -/// If the value of the given expression is a NonLoc, copy it into a new -/// temporary region, and replace the value of the expression with that. -static ProgramStateRef createTemporaryRegionIfNeeded(ProgramStateRef State, - const LocationContext *LC, - const Expr *E) { - SVal V = State->getSVal(E, LC); - - if (isa<NonLoc>(V)) { - MemRegionManager &MRMgr = State->getStateManager().getRegionManager(); - const MemRegion *R = MRMgr.getCXXTempObjectRegion(E, LC); - State = State->bindLoc(loc::MemRegionVal(R), V); - State = State->BindExpr(E, LC, loc::MemRegionVal(R)); +ProgramStateRef +ExprEngine::createTemporaryRegionIfNeeded(ProgramStateRef State, + const LocationContext *LC, + const Expr *Ex, + const Expr *Result) { + SVal V = State->getSVal(Ex, LC); + if (!Result) { + // If we don't have an explicit result expression, we're in "if needed" + // mode. Only create a region if the current value is a NonLoc. + if (!V.getAs<NonLoc>()) + return State; + Result = Ex; + } else { + // We need to create a region no matter what. For sanity, make sure we don't + // try to stuff a Loc into a non-pointer temporary region. + assert(!V.getAs<Loc>() || Loc::isLocType(Result->getType())); } + ProgramStateManager &StateMgr = State->getStateManager(); + MemRegionManager &MRMgr = StateMgr.getRegionManager(); + StoreManager &StoreMgr = StateMgr.getStoreManager(); + + // We need to be careful about treating a derived type's value as + // bindings for a base type. Unless we're creating a temporary pointer region, + // start by stripping and recording base casts. + SmallVector<const CastExpr *, 4> Casts; + const Expr *Inner = Ex->IgnoreParens(); + if (!Loc::isLocType(Result->getType())) { + while (const CastExpr *CE = dyn_cast<CastExpr>(Inner)) { + if (CE->getCastKind() == CK_DerivedToBase || + CE->getCastKind() == CK_UncheckedDerivedToBase) + Casts.push_back(CE); + else if (CE->getCastKind() != CK_NoOp) + break; + + Inner = CE->getSubExpr()->IgnoreParens(); + } + } + + // Create a temporary object region for the inner expression (which may have + // a more derived type) and bind the value into it. + const TypedValueRegion *TR = MRMgr.getCXXTempObjectRegion(Inner, LC); + SVal Reg = loc::MemRegionVal(TR); + + if (V.isUnknown()) + V = getSValBuilder().conjureSymbolVal(Result, LC, TR->getValueType(), + currBldrCtx->blockCount()); + State = State->bindLoc(Reg, V); + + // Re-apply the casts (from innermost to outermost) for type sanity. + for (SmallVectorImpl<const CastExpr *>::reverse_iterator I = Casts.rbegin(), + E = Casts.rend(); + I != E; ++I) { + Reg = StoreMgr.evalDerivedToBase(Reg, *I); + } + + State = State->BindExpr(Result, LC, Reg); return State; } @@ -198,7 +242,7 @@ bool ExprEngine::wantsRegionChangeUpdate(ProgramStateRef state) { ProgramStateRef ExprEngine::processRegionChanges(ProgramStateRef state, - const StoreManager::InvalidatedSymbols *invalidated, + const InvalidatedSymbols *invalidated, ArrayRef<const MemRegion *> Explicits, ArrayRef<const MemRegion *> Regions, const CallEvent *Call) { @@ -221,19 +265,17 @@ void ExprEngine::processCFGElement(const CFGElement E, ExplodedNode *Pred, currBldrCtx = Ctx; switch (E.getKind()) { - case CFGElement::Invalid: - llvm_unreachable("Unexpected CFGElement kind."); case CFGElement::Statement: - ProcessStmt(const_cast<Stmt*>(E.getAs<CFGStmt>()->getStmt()), Pred); + ProcessStmt(const_cast<Stmt*>(E.castAs<CFGStmt>().getStmt()), Pred); return; case CFGElement::Initializer: - ProcessInitializer(E.getAs<CFGInitializer>()->getInitializer(), Pred); + ProcessInitializer(E.castAs<CFGInitializer>().getInitializer(), Pred); return; case CFGElement::AutomaticObjectDtor: case CFGElement::BaseDtor: case CFGElement::MemberDtor: case CFGElement::TemporaryDtor: - ProcessImplicitDtor(*E.getAs<CFGImplicitDtor>(), Pred); + ProcessImplicitDtor(E.castAs<CFGImplicitDtor>(), Pred); return; } currBldrCtx = 0; @@ -249,7 +291,7 @@ static bool shouldRemoveDeadBindings(AnalysisManager &AMgr, return false; // Is this the beginning of a basic block? - if (isa<BlockEntrance>(Pred->getLocation())) + if (Pred->getLocation().getAs<BlockEntrance>()) return true; // Is this on a non-expression? @@ -268,22 +310,39 @@ static bool shouldRemoveDeadBindings(AnalysisManager &AMgr, void ExprEngine::removeDead(ExplodedNode *Pred, ExplodedNodeSet &Out, const Stmt *ReferenceStmt, - const StackFrameContext *LC, + const LocationContext *LC, const Stmt *DiagnosticStmt, ProgramPoint::Kind K) { assert((K == ProgramPoint::PreStmtPurgeDeadSymbolsKind || - ReferenceStmt == 0) + ReferenceStmt == 0 || isa<ReturnStmt>(ReferenceStmt)) && "PostStmt is not generally supported by the SymbolReaper yet"); + assert(LC && "Must pass the current (or expiring) LocationContext"); + + if (!DiagnosticStmt) { + DiagnosticStmt = ReferenceStmt; + assert(DiagnosticStmt && "Required for clearing a LocationContext"); + } + NumRemoveDeadBindings++; - CleanedState = Pred->getState(); - SymbolReaper SymReaper(LC, ReferenceStmt, SymMgr, getStoreManager()); + ProgramStateRef CleanedState = Pred->getState(); + + // LC is the location context being destroyed, but SymbolReaper wants a + // location context that is still live. (If this is the top-level stack + // frame, this will be null.) + if (!ReferenceStmt) { + assert(K == ProgramPoint::PostStmtPurgeDeadSymbolsKind && + "Use PostStmtPurgeDeadSymbolsKind for clearing a LocationContext"); + LC = LC->getParent(); + } + + const StackFrameContext *SFC = LC ? LC->getCurrentStackFrame() : 0; + SymbolReaper SymReaper(SFC, ReferenceStmt, SymMgr, getStoreManager()); getCheckerManager().runCheckersForLiveSymbols(CleanedState, SymReaper); // Create a state in which dead bindings are removed from the environment // and the store. TODO: The function should just return new env and store, // not a new state. - const StackFrameContext *SFC = LC->getCurrentStackFrame(); CleanedState = StateMgr.removeDeadBindings(CleanedState, SFC, SymReaper); // Process any special transfer function for dead symbols. @@ -336,19 +395,17 @@ void ExprEngine::ProcessStmt(const CFGStmt S, // Reclaim any unnecessary nodes in the ExplodedGraph. G.reclaimRecentlyAllocatedNodes(); - currStmt = S.getStmt(); + const Stmt *currStmt = S.getStmt(); PrettyStackTraceLoc CrashInfo(getContext().getSourceManager(), currStmt->getLocStart(), "Error evaluating statement"); // Remove dead bindings and symbols. - EntryNode = Pred; ExplodedNodeSet CleanedStates; - if (shouldRemoveDeadBindings(AMgr, S, Pred, EntryNode->getLocationContext())){ - removeDead(EntryNode, CleanedStates, currStmt, - Pred->getStackFrame(), currStmt); + if (shouldRemoveDeadBindings(AMgr, S, Pred, Pred->getLocationContext())){ + removeDead(Pred, CleanedStates, currStmt, Pred->getLocationContext()); } else - CleanedStates.Add(EntryNode); + CleanedStates.Add(Pred); // Visit the statement. ExplodedNodeSet Dst; @@ -362,11 +419,6 @@ void ExprEngine::ProcessStmt(const CFGStmt S, // Enqueue the new nodes onto the work list. Engine.enqueue(Dst, currBldrCtx->getBlock(), currStmtIdx); - - // NULL out these variables to cleanup. - CleanedState = NULL; - EntryNode = NULL; - currStmt = 0; } void ExprEngine::ProcessInitializer(const CFGInitializer Init, @@ -377,7 +429,7 @@ void ExprEngine::ProcessInitializer(const CFGInitializer Init, BMI->getSourceLocation(), "Error evaluating initializer"); - // We don't set EntryNode and currStmt. And we don't clean up state. + // We don't clean up dead bindings here. const StackFrameContext *stackFrame = cast<StackFrameContext>(Pred->getLocationContext()); const CXXConstructorDecl *decl = @@ -386,24 +438,52 @@ void ExprEngine::ProcessInitializer(const CFGInitializer Init, ProgramStateRef State = Pred->getState(); SVal thisVal = State->getSVal(svalBuilder.getCXXThis(decl, stackFrame)); - PostInitializer PP(BMI, stackFrame); ExplodedNodeSet Tmp(Pred); + 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. - const Expr *Init = BMI->getInit(); + const Expr *Init = BMI->getInit()->IgnoreImplicit(); if (!isa<CXXConstructExpr>(Init)) { - SVal FieldLoc; - if (BMI->isIndirectMemberInitializer()) + const ValueDecl *Field; + if (BMI->isIndirectMemberInitializer()) { + Field = BMI->getIndirectMember(); FieldLoc = State->getLValue(BMI->getIndirectMember(), thisVal); - else + } else { + Field = BMI->getMember(); FieldLoc = State->getLValue(BMI->getMember(), thisVal); + } - SVal InitVal = State->getSVal(BMI->getInit(), stackFrame); + SVal InitVal; + if (BMI->getNumArrayIndices() > 0) { + // Handle arrays of trivial type. We can represent this with a + // primitive load/copy from the base array region. + const ArraySubscriptExpr *ASE; + while ((ASE = dyn_cast<ArraySubscriptExpr>(Init))) + Init = ASE->getBase()->IgnoreImplicit(); + + SVal LValue = State->getSVal(Init, stackFrame); + if (Optional<Loc> LValueLoc = LValue.getAs<Loc>()) + InitVal = State->getSVal(*LValueLoc); + + // If we fail to get the value for some reason, use a symbolic value. + if (InitVal.isUnknownOrUndef()) { + SValBuilder &SVB = getSValBuilder(); + InitVal = SVB.conjureSymbolVal(BMI->getInit(), stackFrame, + Field->getType(), + currBldrCtx->blockCount()); + } + } else { + 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 { @@ -413,6 +493,7 @@ void ExprEngine::ProcessInitializer(const CFGInitializer Init, // Construct PostInitializer nodes whether the state changed or not, // so that the diagnostics don't get confused. + 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) { @@ -429,16 +510,16 @@ void ExprEngine::ProcessImplicitDtor(const CFGImplicitDtor D, ExplodedNodeSet Dst; switch (D.getKind()) { case CFGElement::AutomaticObjectDtor: - ProcessAutomaticObjDtor(cast<CFGAutomaticObjDtor>(D), Pred, Dst); + ProcessAutomaticObjDtor(D.castAs<CFGAutomaticObjDtor>(), Pred, Dst); break; case CFGElement::BaseDtor: - ProcessBaseDtor(cast<CFGBaseDtor>(D), Pred, Dst); + ProcessBaseDtor(D.castAs<CFGBaseDtor>(), Pred, Dst); break; case CFGElement::MemberDtor: - ProcessMemberDtor(cast<CFGMemberDtor>(D), Pred, Dst); + ProcessMemberDtor(D.castAs<CFGMemberDtor>(), Pred, Dst); break; case CFGElement::TemporaryDtor: - ProcessTemporaryDtor(cast<CFGTemporaryDtor>(D), Pred, Dst); + ProcessTemporaryDtor(D.castAs<CFGTemporaryDtor>(), Pred, Dst); break; default: llvm_unreachable("Unexpected dtor kind."); @@ -451,18 +532,20 @@ void ExprEngine::ProcessImplicitDtor(const CFGImplicitDtor D, void ExprEngine::ProcessAutomaticObjDtor(const CFGAutomaticObjDtor Dtor, ExplodedNode *Pred, ExplodedNodeSet &Dst) { - ProgramStateRef state = Pred->getState(); const VarDecl *varDecl = Dtor.getVarDecl(); - QualType varType = varDecl->getType(); - if (const ReferenceType *refType = varType->getAs<ReferenceType>()) - varType = refType->getPointeeType(); + ProgramStateRef state = Pred->getState(); + SVal dest = state->getLValue(varDecl, Pred->getLocationContext()); + const MemRegion *Region = dest.castAs<loc::MemRegionVal>().getRegion(); - Loc dest = state->getLValue(varDecl, Pred->getLocationContext()); + if (const ReferenceType *refType = varType->getAs<ReferenceType>()) { + varType = refType->getPointeeType(); + Region = state->getSVal(Region).getAsRegion(); + } - VisitCXXDestructor(varType, cast<loc::MemRegionVal>(dest).getRegion(), - Dtor.getTriggerStmt(), /*IsBase=*/false, Pred, Dst); + VisitCXXDestructor(varType, Region, Dtor.getTriggerStmt(), /*IsBase=*/ false, + Pred, Dst); } void ExprEngine::ProcessBaseDtor(const CFGBaseDtor D, @@ -476,11 +559,13 @@ void ExprEngine::ProcessBaseDtor(const CFGBaseDtor D, SVal ThisVal = Pred->getState()->getSVal(ThisPtr); // Create the base object region. - QualType BaseTy = D.getBaseSpecifier()->getType(); - SVal BaseVal = getStoreManager().evalDerivedToBase(ThisVal, BaseTy); + const CXXBaseSpecifier *Base = D.getBaseSpecifier(); + QualType BaseTy = Base->getType(); + SVal BaseVal = getStoreManager().evalDerivedToBase(ThisVal, BaseTy, + Base->isVirtual()); - VisitCXXDestructor(BaseTy, cast<loc::MemRegionVal>(BaseVal).getRegion(), - CurDtor->getBody(), /*IsBase=*/true, Pred, Dst); + VisitCXXDestructor(BaseTy, BaseVal.castAs<loc::MemRegionVal>().getRegion(), + CurDtor->getBody(), /*IsBase=*/ true, Pred, Dst); } void ExprEngine::ProcessMemberDtor(const CFGMemberDtor D, @@ -492,10 +577,11 @@ void ExprEngine::ProcessMemberDtor(const CFGMemberDtor D, const CXXDestructorDecl *CurDtor = cast<CXXDestructorDecl>(LCtx->getDecl()); Loc ThisVal = getSValBuilder().getCXXThis(CurDtor, LCtx->getCurrentStackFrame()); - SVal FieldVal = State->getLValue(Member, cast<Loc>(State->getSVal(ThisVal))); + SVal FieldVal = + State->getLValue(Member, State->getSVal(ThisVal).castAs<Loc>()); VisitCXXDestructor(Member->getType(), - cast<loc::MemRegionVal>(FieldVal).getRegion(), + FieldVal.castAs<loc::MemRegionVal>().getRegion(), CurDtor->getBody(), /*IsBase=*/false, Pred, Dst); } @@ -511,16 +597,7 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, ExplodedNodeSet Dst; StmtNodeBuilder Bldr(Pred, DstTop, *currBldrCtx); - // Expressions to ignore. - if (const Expr *Ex = dyn_cast<Expr>(S)) - S = Ex->IgnoreParens(); - - // FIXME: add metadata to the CFG so that we can disable - // this check when we KNOW that there is no block-level subexpression. - // The motivation is that this check requires a hashtable lookup. - - if (S != currStmt && Pred->getLocationContext()->getCFG()->isBlkExpr(S)) - return; + assert(!isa<Expr>(S) || S == cast<Expr>(S)->IgnoreParens()); switch (S->getStmtClass()) { // C++ and ARC stuff we don't support yet. @@ -637,7 +714,6 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, case Stmt::StringLiteralClass: case Stmt::ObjCStringLiteralClass: case Stmt::CXXBindTemporaryExprClass: - case Stmt::CXXDefaultArgExprClass: case Stmt::SubstNonTypeTemplateParmExprClass: case Stmt::CXXNullPtrLiteralExprClass: { Bldr.takeNodes(Pred); @@ -648,6 +724,43 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, break; } + case Stmt::CXXDefaultArgExprClass: { + Bldr.takeNodes(Pred); + ExplodedNodeSet PreVisit; + getCheckerManager().runCheckersForPreStmt(PreVisit, Pred, S, *this); + + ExplodedNodeSet Tmp; + StmtNodeBuilder Bldr2(PreVisit, Tmp, *currBldrCtx); + + const LocationContext *LCtx = Pred->getLocationContext(); + const CXXDefaultArgExpr *DefaultE = cast<CXXDefaultArgExpr>(S); + const Expr *ArgE = DefaultE->getExpr(); + + // Avoid creating and destroying a lot of APSInts. + SVal V; + llvm::APSInt Result; + + for (ExplodedNodeSet::iterator I = PreVisit.begin(), E = PreVisit.end(); + I != E; ++I) { + ProgramStateRef State = (*I)->getState(); + + if (ArgE->EvaluateAsInt(Result, getContext())) + V = svalBuilder.makeIntVal(Result); + else + V = State->getSVal(ArgE, LCtx); + + State = State->BindExpr(DefaultE, LCtx, V); + if (DefaultE->isGLValue()) + State = createTemporaryRegionIfNeeded(State, LCtx, DefaultE, + DefaultE); + Bldr2.generateNode(S, *I, State); + } + + getCheckerManager().runCheckersForPostStmt(Dst, Tmp, S, *this); + Bldr.addNodes(Dst); + break; + } + case Expr::ObjCArrayLiteralClass: case Expr::ObjCDictionaryLiteralClass: // FIXME: explicitly model with a region and the actual contents @@ -780,16 +893,23 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, case Stmt::CXXNewExprClass: { Bldr.takeNodes(Pred); - const CXXNewExpr *NE = cast<CXXNewExpr>(S); - VisitCXXNewExpr(NE, Pred, Dst); + ExplodedNodeSet PostVisit; + VisitCXXNewExpr(cast<CXXNewExpr>(S), Pred, PostVisit); + getCheckerManager().runCheckersForPostStmt(Dst, PostVisit, S, *this); Bldr.addNodes(Dst); break; } case Stmt::CXXDeleteExprClass: { Bldr.takeNodes(Pred); + ExplodedNodeSet PreVisit; const CXXDeleteExpr *CDE = cast<CXXDeleteExpr>(S); - VisitCXXDeleteExpr(CDE, Pred, Dst); + getCheckerManager().runCheckersForPreStmt(PreVisit, Pred, S, *this); + + for (ExplodedNodeSet::iterator i = PreVisit.begin(), + e = PreVisit.end(); i != e ; ++i) + VisitCXXDeleteExpr(CDE, *i, Dst); + Bldr.addNodes(Dst); break; } @@ -1012,11 +1132,11 @@ bool ExprEngine::replayWithoutInlining(ExplodedNode *N, // processing the call. if (L.isPurgeKind()) continue; - if (isa<PreImplicitCall>(&L)) + if (L.getAs<PreImplicitCall>()) continue; - if (isa<CallEnter>(&L)) + if (L.getAs<CallEnter>()) continue; - if (const StmtPoint *SP = dyn_cast<StmtPoint>(&L)) + if (Optional<StmtPoint> SP = L.getAs<StmtPoint>()) if (SP->getStmt() == CE) continue; break; @@ -1034,7 +1154,8 @@ bool ExprEngine::replayWithoutInlining(ExplodedNode *N, // Add the special flag to GDM to signal retrying with no inlining. // Note, changing the state ensures that we are not going to cache out. ProgramStateRef NewNodeState = BeforeProcessingCall->getState(); - NewNodeState = NewNodeState->set<ReplayWithoutInlining>((void*)CE); + NewNodeState = + NewNodeState->set<ReplayWithoutInlining>(const_cast<Stmt *>(CE)); // Make the new node a successor of BeforeProcessingCall. bool IsNew = false; @@ -1155,7 +1276,7 @@ static const Stmt *ResolveCondition(const Stmt *Condition, CFGBlock::const_reverse_iterator I = B->rbegin(), E = B->rend(); for (; I != E; ++I) { CFGElement Elem = *I; - CFGStmt *CS = dyn_cast<CFGStmt>(&Elem); + Optional<CFGStmt> CS = Elem.getAs<CFGStmt>(); if (!CS) continue; if (CS->getStmt() != Condition) @@ -1215,8 +1336,8 @@ void ExprEngine::processBranch(const Stmt *Condition, const Stmt *Term, if (PredI->isSink()) continue; - ProgramStateRef PrevState = Pred->getState(); - SVal X = PrevState->getSVal(Condition, Pred->getLocationContext()); + ProgramStateRef PrevState = PredI->getState(); + SVal X = PrevState->getSVal(Condition, PredI->getLocationContext()); if (X.isUnknownOrUndef()) { // Give it a chance to recover from unknown. @@ -1228,7 +1349,7 @@ void ExprEngine::processBranch(const Stmt *Condition, const Stmt *Term, // underlying value and use that instead. SVal recovered = RecoverCastedSymbol(getStateManager(), PrevState, Condition, - Pred->getLocationContext(), + PredI->getLocationContext(), getContext()); if (!recovered.isUnknown()) { @@ -1245,20 +1366,23 @@ void ExprEngine::processBranch(const Stmt *Condition, const Stmt *Term, continue; } - DefinedSVal V = cast<DefinedSVal>(X); + DefinedSVal V = X.castAs<DefinedSVal>(); + + ProgramStateRef StTrue, StFalse; + tie(StTrue, StFalse) = PrevState->assume(V); // Process the true branch. if (builder.isFeasible(true)) { - if (ProgramStateRef state = PrevState->assume(V, true)) - builder.generateNode(state, true, PredI); + if (StTrue) + builder.generateNode(StTrue, true, PredI); else builder.markInfeasible(true); } // Process the false branch. if (builder.isFeasible(false)) { - if (ProgramStateRef state = PrevState->assume(V, false)) - builder.generateNode(state, false, PredI); + if (StFalse) + builder.generateNode(StFalse, false, PredI); else builder.markInfeasible(false); } @@ -1266,6 +1390,34 @@ void ExprEngine::processBranch(const Stmt *Condition, const Stmt *Term, currBldrCtx = 0; } +/// The GDM component containing the set of global variables which have been +/// previously initialized with explicit initializers. +REGISTER_TRAIT_WITH_PROGRAMSTATE(InitializedGlobalsSet, + llvm::ImmutableSet<const VarDecl *>) + +void ExprEngine::processStaticInitializer(const DeclStmt *DS, + NodeBuilderContext &BuilderCtx, + ExplodedNode *Pred, + clang::ento::ExplodedNodeSet &Dst, + const CFGBlock *DstT, + const CFGBlock *DstF) { + currBldrCtx = &BuilderCtx; + + const VarDecl *VD = cast<VarDecl>(DS->getSingleDecl()); + ProgramStateRef state = Pred->getState(); + bool initHasRun = state->contains<InitializedGlobalsSet>(VD); + BranchNodeBuilder builder(Pred, Dst, BuilderCtx, DstT, DstF); + + if (!initHasRun) { + state = state->add<InitializedGlobalsSet>(VD); + } + + builder.generateNode(state, initHasRun, Pred); + builder.markInfeasible(!initHasRun); + + currBldrCtx = 0; +} + /// processIndirectGoto - Called by CoreEngine. Used to generate successor /// nodes by processing the 'effects' of a computed goto jump. void ExprEngine::processIndirectGoto(IndirectGotoNodeBuilder &builder) { @@ -1282,8 +1434,8 @@ void ExprEngine::processIndirectGoto(IndirectGotoNodeBuilder &builder) { typedef IndirectGotoNodeBuilder::iterator iterator; - if (isa<loc::GotoLabel>(V)) { - const LabelDecl *L = cast<loc::GotoLabel>(V).getLabel(); + if (Optional<loc::GotoLabel> LV = V.getAs<loc::GotoLabel>()) { + const LabelDecl *L = LV->getLabel(); for (iterator I = builder.begin(), E = builder.end(); I != E; ++I) { if (I.getLabel() == L) { @@ -1295,7 +1447,7 @@ void ExprEngine::processIndirectGoto(IndirectGotoNodeBuilder &builder) { llvm_unreachable("No block with label."); } - if (isa<loc::ConcreteInt>(V) || isa<UndefinedVal>(V)) { + if (V.getAs<loc::ConcreteInt>() || V.getAs<UndefinedVal>()) { // Dispatch to the first target and mark it as a sink. //ExplodedNode* N = builder.generateNode(builder.begin(), state, true); // FIXME: add checker visit. @@ -1325,10 +1477,10 @@ void ExprEngine::processEndOfFunction(NodeBuilderContext& BC, // Notify checkers. for (ExplodedNodeSet::iterator I = AfterRemovedDead.begin(), E = AfterRemovedDead.end(); I != E; ++I) { - getCheckerManager().runCheckersForEndPath(BC, Dst, *I, *this); + getCheckerManager().runCheckersForEndFunction(BC, Dst, *I, *this); } } else { - getCheckerManager().runCheckersForEndPath(BC, Dst, Pred, *this); + getCheckerManager().runCheckersForEndFunction(BC, Dst, Pred, *this); } Engine.enqueueEndOfFunction(Dst); @@ -1349,7 +1501,7 @@ void ExprEngine::processSwitch(SwitchNodeBuilder& builder) { return; } - DefinedOrUnknownSVal CondV = cast<DefinedOrUnknownSVal>(CondV_untested); + DefinedOrUnknownSVal CondV = CondV_untested.castAs<DefinedOrUnknownSVal>(); ProgramStateRef DefaultSt = state; @@ -1390,7 +1542,7 @@ void ExprEngine::processSwitch(SwitchNodeBuilder& builder) { // If CondV evaluates to a constant, then we know that this // is the *only* case that we can take, so stop evaluating the // others. - if (isa<nonloc::ConcreteInt>(CondV)) + if (CondV.getAs<nonloc::ConcreteInt>()) return; } @@ -1484,7 +1636,7 @@ void ExprEngine::VisitCommonDeclRefExpr(const Expr *Ex, const NamedDecl *D, // results in boolean contexts. SVal V = svalBuilder.conjureSymbolVal(Ex, LCtx, getContext().VoidPtrTy, currBldrCtx->blockCount()); - state = state->assume(cast<DefinedOrUnknownSVal>(V), true); + state = state->assume(V.castAs<DefinedOrUnknownSVal>(), true); Bldr.generateNode(Ex, Pred, state->BindExpr(Ex, LCtx, V), 0, ProgramPoint::PostLValueKind); return; @@ -1576,6 +1728,122 @@ void ExprEngine::VisitMemberExpr(const MemberExpr *M, ExplodedNode *Pred, } } +namespace { +class CollectReachableSymbolsCallback : public SymbolVisitor { + InvalidatedSymbols Symbols; +public: + CollectReachableSymbolsCallback(ProgramStateRef State) {} + const InvalidatedSymbols &getSymbols() const { return Symbols; } + + bool VisitSymbol(SymbolRef Sym) { + Symbols.insert(Sym); + return true; + } +}; +} // end anonymous namespace + +// A value escapes in three possible cases: +// (1) We are binding to something that is not a memory region. +// (2) We are binding to a MemrRegion that does not have stack storage. +// (3) We are binding to a MemRegion with stack storage that the store +// does not understand. +ProgramStateRef ExprEngine::processPointerEscapedOnBind(ProgramStateRef State, + SVal Loc, SVal Val) { + // Are we storing to something that causes the value to "escape"? + bool escapes = true; + + // TODO: Move to StoreManager. + if (Optional<loc::MemRegionVal> regionLoc = Loc.getAs<loc::MemRegionVal>()) { + escapes = !regionLoc->getRegion()->hasStackStorage(); + + if (!escapes) { + // To test (3), generate a new state with the binding added. If it is + // the same state, then it escapes (since the store cannot represent + // the binding). + // Do this only if we know that the store is not supposed to generate the + // same state. + SVal StoredVal = State->getSVal(regionLoc->getRegion()); + if (StoredVal != Val) + escapes = (State == (State->bindLoc(*regionLoc, Val))); + } + } + + // If our store can represent the binding and we aren't storing to something + // that doesn't have local storage then just return and have the simulation + // state continue as is. + if (!escapes) + return State; + + // 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*/ 0, + PSK_EscapeOnBind); + + return State; +} + +ProgramStateRef +ExprEngine::notifyCheckersOfPointerEscape(ProgramStateRef State, + const InvalidatedSymbols *Invalidated, + ArrayRef<const MemRegion *> ExplicitRegions, + ArrayRef<const MemRegion *> Regions, + const CallEvent *Call, + bool IsConst) { + + if (!Invalidated || Invalidated->empty()) + return State; + + if (!Call) + return getCheckerManager().runCheckersForPointerEscape(State, + *Invalidated, + 0, + PSK_EscapeOther); + + // Note: Due to current limitations of RegionStore, we only process the top + // level const pointers correctly. The lower level const pointers are + // currently treated as non-const. + if (IsConst) + return getCheckerManager().runCheckersForPointerEscape(State, + *Invalidated, + Call, + PSK_DirectEscapeOnCall, + true); + + // 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>()) + SymbolsDirectlyInvalidated.insert(R->getSymbol()); + } + + InvalidatedSymbols SymbolsIndirectlyInvalidated; + for (InvalidatedSymbols::const_iterator I=Invalidated->begin(), + E = Invalidated->end(); I!=E; ++I) { + SymbolRef sym = *I; + if (SymbolsDirectlyInvalidated.count(sym)) + continue; + SymbolsIndirectlyInvalidated.insert(sym); + } + + if (!SymbolsDirectlyInvalidated.empty()) + State = getCheckerManager().runCheckersForPointerEscape(State, + SymbolsDirectlyInvalidated, Call, PSK_DirectEscapeOnCall); + + // Notify about the symbols that get indirectly invalidated by the call. + if (!SymbolsIndirectlyInvalidated.empty()) + State = getCheckerManager().runCheckersForPointerEscape(State, + SymbolsIndirectlyInvalidated, Call, PSK_IndirectEscapeOnCall); + + return State; +} + /// evalBind - Handle the semantics of binding a value to a specific location. /// This method is used by evalStore and (soon) VisitDeclStmt, and others. void ExprEngine::evalBind(ExplodedNodeSet &Dst, const Stmt *StoreE, @@ -1593,36 +1861,42 @@ void ExprEngine::evalBind(ExplodedNodeSet &Dst, const Stmt *StoreE, getCheckerManager().runCheckersForBind(CheckedSet, Pred, location, Val, StoreE, *this, *PP); + + StmtNodeBuilder Bldr(CheckedSet, Dst, *currBldrCtx); + // If the location is not a 'Loc', it will already be handled by // the checkers. There is nothing left to do. - if (!isa<Loc>(location)) { - Dst = CheckedSet; + if (!location.getAs<Loc>()) { + const ProgramPoint L = PostStore(StoreE, LC, /*Loc*/0, /*tag*/0); + ProgramStateRef state = Pred->getState(); + state = processPointerEscapedOnBind(state, location, Val); + Bldr.generateNode(L, state, Pred); return; } - ExplodedNodeSet TmpDst; - StmtNodeBuilder Bldr(CheckedSet, TmpDst, *currBldrCtx); for (ExplodedNodeSet::iterator I = CheckedSet.begin(), E = CheckedSet.end(); I!=E; ++I) { ExplodedNode *PredI = *I; ProgramStateRef state = PredI->getState(); + state = processPointerEscapedOnBind(state, location, Val); + // When binding the value, pass on the hint that this is a initialization. // For initializations, we do not need to inform clients of region // changes. - state = state->bindLoc(cast<Loc>(location), + state = state->bindLoc(location.castAs<Loc>(), Val, /* notifyChanges = */ !atDeclInit); - + const MemRegion *LocReg = 0; - if (loc::MemRegionVal *LocRegVal = dyn_cast<loc::MemRegionVal>(&location)) { + if (Optional<loc::MemRegionVal> LocRegVal = + location.getAs<loc::MemRegionVal>()) { LocReg = LocRegVal->getRegion(); } const ProgramPoint L = PostStore(StoreE, LC, LocReg, 0); Bldr.generateNode(L, state, PredI); } - Dst.insert(TmpDst); } /// evalStore - Handle the semantics of a store via an assignment. @@ -1665,7 +1939,7 @@ void ExprEngine::evalLoad(ExplodedNodeSet &Dst, const ProgramPointTag *tag, QualType LoadTy) { - assert(!isa<NonLoc>(location) && "location cannot be a NonLoc."); + 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 @@ -1720,20 +1994,15 @@ void ExprEngine::evalLoadCommon(ExplodedNodeSet &Dst, state = (*NI)->getState(); const LocationContext *LCtx = (*NI)->getLocationContext(); - if (location.isUnknown()) { - // This is important. We must nuke the old binding. - Bldr.generateNode(NodeEx, *NI, - state->BindExpr(BoundEx, LCtx, UnknownVal()), - tag, ProgramPoint::PostLoadKind); - } - else { + SVal V = UnknownVal(); + if (location.isValid()) { if (LoadTy.isNull()) LoadTy = BoundEx->getType(); - SVal V = state->getSVal(cast<Loc>(location), LoadTy); - Bldr.generateNode(NodeEx, *NI, - state->bindExprAndLocation(BoundEx, LCtx, location, V), - tag, ProgramPoint::PostLoadKind); + V = state->getSVal(location.castAs<Loc>(), LoadTy); } + + Bldr.generateNode(NodeEx, *NI, state->BindExpr(BoundEx, LCtx, V), tag, + ProgramPoint::PostLoadKind); } } @@ -1793,26 +2062,29 @@ void ExprEngine::evalEagerlyAssumeBinOpBifurcation(ExplodedNodeSet &Dst, // when the expression fails to evaluate to anything meaningful and // (as an optimization) we don't generate a node. ProgramPoint P = Pred->getLocation(); - if (!isa<PostStmt>(P) || cast<PostStmt>(P).getStmt() != Ex) { + if (!P.getAs<PostStmt>() || P.castAs<PostStmt>().getStmt() != Ex) { continue; } ProgramStateRef state = Pred->getState(); SVal V = state->getSVal(Ex, Pred->getLocationContext()); - nonloc::SymbolVal *SEV = dyn_cast<nonloc::SymbolVal>(&V); + Optional<nonloc::SymbolVal> SEV = V.getAs<nonloc::SymbolVal>(); if (SEV && SEV->isExpression()) { const std::pair<const ProgramPointTag *, const ProgramPointTag*> &tags = geteagerlyAssumeBinOpBifurcationTags(); + ProgramStateRef StateTrue, StateFalse; + tie(StateTrue, StateFalse) = state->assume(*SEV); + // First assume that the condition is true. - if (ProgramStateRef StateTrue = state->assume(*SEV, true)) { + if (StateTrue) { SVal Val = svalBuilder.makeIntVal(1U, Ex->getType()); StateTrue = StateTrue->BindExpr(Ex, Pred->getLocationContext(), Val); Bldr.generateNode(Ex, Pred, StateTrue, tags.first); } // Next, assume that the condition is false. - if (ProgramStateRef StateFalse = state->assume(*SEV, false)) { + if (StateFalse) { SVal Val = svalBuilder.makeIntVal(0U, Ex->getType()); StateFalse = StateFalse->BindExpr(Ex, Pred->getLocationContext(), Val); Bldr.generateNode(Ex, Pred, StateFalse, tags.second); @@ -1836,10 +2108,10 @@ void ExprEngine::VisitGCCAsmStmt(const GCCAsmStmt *A, ExplodedNode *Pred, for (GCCAsmStmt::const_outputs_iterator OI = A->begin_outputs(), OE = A->end_outputs(); OI != OE; ++OI) { SVal X = state->getSVal(*OI, Pred->getLocationContext()); - assert (!isa<NonLoc>(X)); // Should be an Lval, or unknown, undef. + assert (!X.getAs<NonLoc>()); // Should be an Lval, or unknown, undef. - if (isa<Loc>(X)) - state = state->bindLoc(cast<Loc>(X), UnknownVal()); + if (Optional<Loc> LV = X.getAs<Loc>()) + state = state->bindLoc(*LV, UnknownVal()); } Bldr.generateNode(A, Pred, state); @@ -1889,7 +2161,7 @@ struct DOTGraphTraits<ExplodedNode*> : return ""; } - static void printLocation(llvm::raw_ostream &Out, SourceLocation SLoc) { + static void printLocation(raw_ostream &Out, SourceLocation SLoc) { if (SLoc.isFileID()) { Out << "\\lline=" << GraphPrintSourceManager->getExpansionLineNumber(SLoc) @@ -1910,7 +2182,7 @@ struct DOTGraphTraits<ExplodedNode*> : switch (Loc.getKind()) { case ProgramPoint::BlockEntranceKind: { Out << "Block Entrance: B" - << cast<BlockEntrance>(Loc).getBlock()->getBlockID(); + << Loc.castAs<BlockEntrance>().getBlock()->getBlockID(); if (const NamedDecl *ND = dyn_cast<NamedDecl>(Loc.getLocationContext()->getDecl())) { Out << " ("; @@ -1949,73 +2221,46 @@ struct DOTGraphTraits<ExplodedNode*> : break; case ProgramPoint::PreImplicitCallKind: { - ImplicitCallPoint *PC = cast<ImplicitCallPoint>(&Loc); + ImplicitCallPoint PC = Loc.castAs<ImplicitCallPoint>(); Out << "PreCall: "; // FIXME: Get proper printing options. - PC->getDecl()->print(Out, LangOptions()); - printLocation(Out, PC->getLocation()); + PC.getDecl()->print(Out, LangOptions()); + printLocation(Out, PC.getLocation()); break; } case ProgramPoint::PostImplicitCallKind: { - ImplicitCallPoint *PC = cast<ImplicitCallPoint>(&Loc); + ImplicitCallPoint PC = Loc.castAs<ImplicitCallPoint>(); Out << "PostCall: "; // FIXME: Get proper printing options. - PC->getDecl()->print(Out, LangOptions()); - printLocation(Out, PC->getLocation()); + PC.getDecl()->print(Out, LangOptions()); + printLocation(Out, PC.getLocation()); break; } - default: { - if (StmtPoint *L = dyn_cast<StmtPoint>(&Loc)) { - const Stmt *S = L->getStmt(); - - Out << S->getStmtClassName() << ' ' << (const void*) S << ' '; + case ProgramPoint::PostInitializerKind: { + Out << "PostInitializer: "; + const CXXCtorInitializer *Init = + Loc.castAs<PostInitializer>().getInitializer(); + if (const FieldDecl *FD = Init->getAnyMember()) + Out << *FD; + else { + QualType Ty = Init->getTypeSourceInfo()->getType(); + Ty = Ty.getLocalUnqualifiedType(); LangOptions LO; // FIXME. - S->printPretty(Out, 0, PrintingPolicy(LO)); - printLocation(Out, S->getLocStart()); - - if (isa<PreStmt>(Loc)) - Out << "\\lPreStmt\\l;"; - else if (isa<PostLoad>(Loc)) - Out << "\\lPostLoad\\l;"; - else if (isa<PostStore>(Loc)) - Out << "\\lPostStore\\l"; - else if (isa<PostLValue>(Loc)) - Out << "\\lPostLValue\\l"; - -#if 0 - // FIXME: Replace with a general scheme to determine - // the name of the check. - if (GraphPrintCheckerState->isImplicitNullDeref(N)) - Out << "\\|Implicit-Null Dereference.\\l"; - else if (GraphPrintCheckerState->isExplicitNullDeref(N)) - Out << "\\|Explicit-Null Dereference.\\l"; - else if (GraphPrintCheckerState->isUndefDeref(N)) - Out << "\\|Dereference of undefialied value.\\l"; - else if (GraphPrintCheckerState->isUndefStore(N)) - Out << "\\|Store to Undefined Loc."; - else if (GraphPrintCheckerState->isUndefResult(N)) - Out << "\\|Result of operation is undefined."; - else if (GraphPrintCheckerState->isNoReturnCall(N)) - Out << "\\|Call to function marked \"noreturn\"."; - else if (GraphPrintCheckerState->isBadCall(N)) - Out << "\\|Call to NULL/Undefined."; - else if (GraphPrintCheckerState->isUndefArg(N)) - Out << "\\|Argument in call is undefined"; -#endif - - break; + Ty.print(Out, LO); } + break; + } - const BlockEdge &E = cast<BlockEdge>(Loc); + case ProgramPoint::BlockEdgeKind: { + const BlockEdge &E = Loc.castAs<BlockEdge>(); Out << "Edge: (B" << E.getSrc()->getBlockID() << ", B" << E.getDst()->getBlockID() << ')'; if (const Stmt *T = E.getSrc()->getTerminator()) { - SourceLocation SLoc = T->getLocStart(); Out << "\\|Terminator: "; @@ -2074,6 +2319,48 @@ struct DOTGraphTraits<ExplodedNode*> : Out << "\\|Control-flow based on\\lUndefined value.\\l"; } #endif + break; + } + + default: { + const Stmt *S = Loc.castAs<StmtPoint>().getStmt(); + + Out << S->getStmtClassName() << ' ' << (const void*) S << ' '; + LangOptions LO; // FIXME. + S->printPretty(Out, 0, PrintingPolicy(LO)); + printLocation(Out, S->getLocStart()); + + if (Loc.getAs<PreStmt>()) + Out << "\\lPreStmt\\l;"; + else if (Loc.getAs<PostLoad>()) + Out << "\\lPostLoad\\l;"; + else if (Loc.getAs<PostStore>()) + Out << "\\lPostStore\\l"; + else if (Loc.getAs<PostLValue>()) + Out << "\\lPostLValue\\l"; + +#if 0 + // FIXME: Replace with a general scheme to determine + // the name of the check. + if (GraphPrintCheckerState->isImplicitNullDeref(N)) + Out << "\\|Implicit-Null Dereference.\\l"; + else if (GraphPrintCheckerState->isExplicitNullDeref(N)) + Out << "\\|Explicit-Null Dereference.\\l"; + else if (GraphPrintCheckerState->isUndefDeref(N)) + Out << "\\|Dereference of undefialied value.\\l"; + else if (GraphPrintCheckerState->isUndefStore(N)) + Out << "\\|Store to Undefined Loc."; + else if (GraphPrintCheckerState->isUndefResult(N)) + Out << "\\|Result of operation is undefined."; + else if (GraphPrintCheckerState->isNoReturnCall(N)) + Out << "\\|Call to function marked \"noreturn\"."; + else if (GraphPrintCheckerState->isBadCall(N)) + Out << "\\|Call to NULL/Undefined."; + else if (GraphPrintCheckerState->isUndefArg(N)) + Out << "\\|Argument in call is undefined"; +#endif + + break; } } @@ -2108,7 +2395,7 @@ GetGraphNode<llvm::DenseMap<ExplodedNode*, Expr*>::iterator> void ExprEngine::ViewGraph(bool trim) { #ifndef NDEBUG if (trim) { - std::vector<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. @@ -2122,7 +2409,7 @@ void ExprEngine::ViewGraph(bool trim) { if (N) Src.push_back(N); } - ViewGraph(&Src[0], &Src[0]+Src.size()); + ViewGraph(Src); } else { GraphPrintCheckerState = this; @@ -2136,12 +2423,12 @@ void ExprEngine::ViewGraph(bool trim) { #endif } -void ExprEngine::ViewGraph(ExplodedNode** Beg, ExplodedNode** End) { +void ExprEngine::ViewGraph(ArrayRef<const ExplodedNode*> Nodes) { #ifndef NDEBUG GraphPrintCheckerState = this; GraphPrintSourceManager = &getContext().getSourceManager(); - std::auto_ptr<ExplodedGraph> TrimmedG(G.Trim(Beg, End).first); + OwningPtr<ExplodedGraph> TrimmedG(G.trim(Nodes)); if (!TrimmedG.get()) llvm::errs() << "warning: Trimmed ExplodedGraph is empty.\n"; diff --git a/lib/StaticAnalyzer/Core/ExprEngineC.cpp b/lib/StaticAnalyzer/Core/ExprEngineC.cpp index 00b2f4a..3a3c971 100644 --- a/lib/StaticAnalyzer/Core/ExprEngineC.cpp +++ b/lib/StaticAnalyzer/Core/ExprEngineC.cpp @@ -11,6 +11,7 @@ // //===----------------------------------------------------------------------===// +#include "clang/AST/ExprCXX.h" #include "clang/StaticAnalyzer/Core/CheckerManager.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h" @@ -66,12 +67,12 @@ void ExprEngine::VisitBinaryOperator(const BinaryOperator* B, // TODO: This can be removed after we enable history tracking with // SymSymExpr. unsigned Count = currBldrCtx->blockCount(); - if (isa<Loc>(LeftV) && + if (LeftV.getAs<Loc>() && RHS->getType()->isIntegerType() && RightV.isUnknown()) { RightV = svalBuilder.conjureSymbolVal(RHS, LCtx, RHS->getType(), Count); } - if (isa<Loc>(RightV) && + if (RightV.getAs<Loc>() && LHS->getType()->isIntegerType() && LeftV.isUnknown()) { LeftV = svalBuilder.conjureSymbolVal(LHS, LCtx, LHS->getType(), Count); @@ -305,7 +306,8 @@ void ExprEngine::VisitCast(const CastExpr *CastE, const Expr *Ex, case CK_CPointerToObjCPointerCast: case CK_BlockPointerToObjCPointerCast: case CK_AnyPointerToBlockPointerCast: - case CK_ObjCObjectLValueCast: { + case CK_ObjCObjectLValueCast: + case CK_ZeroToOCLEvent: { // Delegate to SValBuilder to process. SVal V = state->getSVal(Ex, LCtx); V = svalBuilder.evalCast(V, T, ExTy); @@ -423,15 +425,10 @@ void ExprEngine::VisitCompoundLiteralExpr(const CompoundLiteralExpr *CL, void ExprEngine::VisitDeclStmt(const DeclStmt *DS, ExplodedNode *Pred, ExplodedNodeSet &Dst) { - - // FIXME: static variables may have an initializer, but the second - // time a function is called those values may not be current. - // This may need to be reflected in the CFG. - // Assumption: The CFG has one DeclStmt per Decl. - const Decl *D = *DS->decl_begin(); - - if (!D || !isa<VarDecl>(D)) { + const VarDecl *VD = dyn_cast_or_null<VarDecl>(*DS->decl_begin()); + + if (!VD) { //TODO:AZ: remove explicit insertion after refactoring is done. Dst.insert(Pred); return; @@ -442,31 +439,33 @@ void ExprEngine::VisitDeclStmt(const DeclStmt *DS, ExplodedNode *Pred, getCheckerManager().runCheckersForPreStmt(dstPreVisit, Pred, DS, *this); StmtNodeBuilder B(dstPreVisit, Dst, *currBldrCtx); - const VarDecl *VD = dyn_cast<VarDecl>(D); for (ExplodedNodeSet::iterator I = dstPreVisit.begin(), E = dstPreVisit.end(); I!=E; ++I) { ExplodedNode *N = *I; ProgramStateRef state = N->getState(); - - // Decls without InitExpr are not initialized explicitly. const LocationContext *LC = N->getLocationContext(); - + + // Decls without InitExpr are not initialized explicitly. if (const Expr *InitEx = VD->getInit()) { + + // Note in the state that the initialization has occurred. + ExplodedNode *UpdatedN = N; SVal InitVal = state->getSVal(InitEx, LC); - if (InitVal == state->getLValue(VD, LC) || - (VD->getType()->isArrayType() && - isa<CXXConstructExpr>(InitEx->IgnoreImplicit()))) { + if (isa<CXXConstructExpr>(InitEx->IgnoreImplicit())) { // We constructed the object directly in the variable. // No need to bind anything. - B.generateNode(DS, N, state); + 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() && isa<loc::MemRegionVal>(InitVal)){ - InitVal = state->getSVal(cast<loc::MemRegionVal>(InitVal).getRegion()); - assert(isa<nonloc::LazyCompoundVal>(InitVal)); + 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 @@ -480,9 +479,11 @@ void ExprEngine::VisitDeclStmt(const DeclStmt *DS, ExplodedNode *Pred, InitVal = svalBuilder.conjureSymbolVal(0, InitEx, LC, Ty, currBldrCtx->blockCount()); } - B.takeNodes(N); + + + B.takeNodes(UpdatedN); ExplodedNodeSet Dst2; - evalBind(Dst2, DS, N, state->getLValue(VD, LC), InitVal, true); + evalBind(Dst2, DS, UpdatedN, state->getLValue(VD, LC), InitVal, true); B.addNodes(Dst2); } } @@ -501,16 +502,16 @@ void ExprEngine::VisitLogicalExpr(const BinaryOperator* B, ExplodedNode *Pred, ProgramStateRef state = Pred->getState(); ExplodedNode *N = Pred; - while (!isa<BlockEntrance>(N->getLocation())) { + while (!N->getLocation().getAs<BlockEntrance>()) { ProgramPoint P = N->getLocation(); - assert(isa<PreStmt>(P)|| isa<PreStmtPurgeDeadSymbols>(P)); + assert(P.getAs<PreStmt>()|| P.getAs<PreStmtPurgeDeadSymbols>()); (void) P; assert(N->pred_size() == 1); N = *N->pred_begin(); } assert(N->pred_size() == 1); N = *N->pred_begin(); - BlockEdge BE = cast<BlockEdge>(N->getLocation()); + BlockEdge BE = N->getLocation().castAs<BlockEdge>(); SVal X; // Determine the value of the expression by introspecting how we @@ -532,28 +533,32 @@ void ExprEngine::VisitLogicalExpr(const BinaryOperator* B, ExplodedNode *Pred, // in SrcBlock is the value of the enclosing expression. // However, we still need to constrain that value to be 0 or 1. assert(!SrcBlock->empty()); - CFGStmt Elem = cast<CFGStmt>(*SrcBlock->rbegin()); + CFGStmt Elem = SrcBlock->rbegin()->castAs<CFGStmt>(); const Expr *RHS = cast<Expr>(Elem.getStmt()); SVal RHSVal = N->getState()->getSVal(RHS, Pred->getLocationContext()); - DefinedOrUnknownSVal DefinedRHS = cast<DefinedOrUnknownSVal>(RHSVal); - ProgramStateRef StTrue, StFalse; - llvm::tie(StTrue, StFalse) = N->getState()->assume(DefinedRHS); - if (StTrue) { - if (StFalse) { - // We can't constrain the value to 0 or 1; the best we can do is a cast. - X = getSValBuilder().evalCast(RHSVal, B->getType(), RHS->getType()); + if (RHSVal.isUndef()) { + X = RHSVal; + } else { + DefinedOrUnknownSVal DefinedRHS = RHSVal.castAs<DefinedOrUnknownSVal>(); + ProgramStateRef StTrue, StFalse; + llvm::tie(StTrue, StFalse) = N->getState()->assume(DefinedRHS); + if (StTrue) { + if (StFalse) { + // We can't constrain the value to 0 or 1. + // The best we can do is a cast. + X = getSValBuilder().evalCast(RHSVal, B->getType(), RHS->getType()); + } else { + // The value is known to be true. + X = getSValBuilder().makeIntVal(1, B->getType()); + } } else { - // The value is known to be true. - X = getSValBuilder().makeIntVal(1, B->getType()); + // The value is known to be false. + assert(StFalse && "Infeasible path!"); + X = getSValBuilder().makeIntVal(0, B->getType()); } - } else { - // The value is known to be false. - assert(StFalse && "Infeasible path!"); - X = getSValBuilder().makeIntVal(0, B->getType()); } } - Bldr.generateNode(B, Pred, state->BindExpr(B, Pred->getLocationContext(), X)); } @@ -581,8 +586,10 @@ void ExprEngine::VisitInitListExpr(const InitListExpr *IE, for (InitListExpr::const_reverse_iterator it = IE->rbegin(), ei = IE->rend(); it != ei; ++it) { - vals = getBasicVals().consVals(state->getSVal(cast<Expr>(*it), LCtx), - vals); + SVal V = state->getSVal(cast<Expr>(*it), LCtx); + if (dyn_cast_or_null<CXXTempObjectRegion>(V.getAsRegion())) + V = UnknownVal(); + vals = getBasicVals().consVals(V, vals); } B.generateNode(IE, Pred, @@ -615,14 +622,16 @@ void ExprEngine::VisitGuardedExpr(const Expr *Ex, for (const ExplodedNode *N = Pred ; N ; N = *N->pred_begin()) { ProgramPoint PP = N->getLocation(); - if (isa<PreStmtPurgeDeadSymbols>(PP) || isa<BlockEntrance>(PP)) { + if (PP.getAs<PreStmtPurgeDeadSymbols>() || PP.getAs<BlockEntrance>()) { assert(N->pred_size() == 1); continue; } - SrcBlock = cast<BlockEdge>(&PP)->getSrc(); + SrcBlock = PP.castAs<BlockEdge>().getSrc(); break; } + assert(SrcBlock && "missing function entry"); + // Find the last expression in the predecessor block. That is the // expression that is used for the value of the ternary expression. bool hasValue = false; @@ -631,7 +640,7 @@ void ExprEngine::VisitGuardedExpr(const Expr *Ex, for (CFGBlock::const_reverse_iterator I = SrcBlock->rbegin(), E = SrcBlock->rend(); I != E; ++I) { CFGElement CE = *I; - if (CFGStmt *CS = dyn_cast<CFGStmt>(&CE)) { + if (Optional<CFGStmt> CS = CE.getAs<CFGStmt>()) { const Expr *ValEx = cast<Expr>(CS->getStmt()); hasValue = true; V = state->getSVal(ValEx, LCtx); @@ -785,11 +794,11 @@ void ExprEngine::VisitUnaryOperator(const UnaryOperator* U, llvm_unreachable("Invalid Opcode."); case UO_Not: // FIXME: Do we need to handle promotions? - state = state->BindExpr(U, LCtx, evalComplement(cast<NonLoc>(V))); + state = state->BindExpr(U, LCtx, evalComplement(V.castAs<NonLoc>())); break; case UO_Minus: // FIXME: Do we need to handle promotions? - state = state->BindExpr(U, LCtx, evalMinus(cast<NonLoc>(V))); + state = state->BindExpr(U, LCtx, evalMinus(V.castAs<NonLoc>())); break; case UO_LNot: // C99 6.5.3.3: "The expression !E is equivalent to (0==E)." @@ -797,14 +806,16 @@ void ExprEngine::VisitUnaryOperator(const UnaryOperator* U, // Note: technically we do "E == 0", but this is the same in the // transfer functions as "0 == E". SVal Result; - if (isa<Loc>(V)) { + if (Optional<Loc> LV = V.getAs<Loc>()) { Loc X = svalBuilder.makeNull(); - Result = evalBinOp(state, BO_EQ, cast<Loc>(V), X, - U->getType()); + Result = evalBinOp(state, BO_EQ, *LV, X, U->getType()); } - else { + else if (Ex->getType()->isFloatingType()) { + // FIXME: handle floating point types. + Result = UnknownVal(); + } else { nonloc::ConcreteInt X(getBasicVals().getValue(0, Ex->getType())); - Result = evalBinOp(state, BO_EQ, cast<NonLoc>(V), X, + Result = evalBinOp(state, BO_EQ, V.castAs<NonLoc>(), X, U->getType()); } @@ -846,7 +857,7 @@ void ExprEngine::VisitIncrementDecrementOperator(const UnaryOperator* U, Bldr.generateNode(U, *I, state->BindExpr(U, LCtx, V2_untested)); continue; } - DefinedSVal V2 = cast<DefinedSVal>(V2_untested); + DefinedSVal V2 = V2_untested.castAs<DefinedSVal>(); // Handle all other values. BinaryOperator::Opcode Op = U->isIncrementOp() ? BO_Add : BO_Sub; diff --git a/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp b/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp index b3baa79..ed90dc5 100644 --- a/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp +++ b/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp @@ -11,13 +11,13 @@ // //===----------------------------------------------------------------------===// -#include "clang/StaticAnalyzer/Core/CheckerManager.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" #include "clang/AST/DeclCXX.h" #include "clang/AST/StmtCXX.h" #include "clang/Basic/PrettyStackTrace.h" +#include "clang/StaticAnalyzer/Core/CheckerManager.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" using namespace clang; using namespace ento; @@ -30,23 +30,90 @@ void ExprEngine::CreateCXXTemporaryObject(const MaterializeTemporaryExpr *ME, ProgramStateRef state = Pred->getState(); const LocationContext *LCtx = Pred->getLocationContext(); - // Bind the temporary object to the value of the expression. Then bind - // the expression to the location of the object. SVal V = state->getSVal(tempExpr, LCtx); // If the value is already a CXXTempObjectRegion, it is fine as it is. // Otherwise, create a new CXXTempObjectRegion, and copy the value into it. + // This is an optimization for when an rvalue is constructed and then + // immediately materialized. const MemRegion *MR = V.getAsRegion(); - if (!MR || !isa<CXXTempObjectRegion>(MR)) { - const MemRegion *R = - svalBuilder.getRegionManager().getCXXTempObjectRegion(ME, LCtx); + if (const CXXTempObjectRegion *TR = + dyn_cast_or_null<CXXTempObjectRegion>(MR)) { + if (getContext().hasSameUnqualifiedType(TR->getValueType(), ME->getType())) + state = state->BindExpr(ME, LCtx, V); + } + + if (state == Pred->getState()) + state = createTemporaryRegionIfNeeded(state, LCtx, tempExpr, ME); + Bldr.generateNode(ME, Pred, state); +} + +// FIXME: This is the sort of code that should eventually live in a Core +// checker rather than as a special case in ExprEngine. +void ExprEngine::performTrivialCopy(NodeBuilder &Bldr, ExplodedNode *Pred, + const CallEvent &Call) { + SVal ThisVal; + bool AlwaysReturnsLValue; + if (const CXXConstructorCall *Ctor = dyn_cast<CXXConstructorCall>(&Call)) { + assert(Ctor->getDecl()->isTrivial()); + assert(Ctor->getDecl()->isCopyOrMoveConstructor()); + ThisVal = Ctor->getCXXThisVal(); + AlwaysReturnsLValue = false; + } else { + assert(cast<CXXMethodDecl>(Call.getDecl())->isTrivial()); + assert(cast<CXXMethodDecl>(Call.getDecl())->getOverloadedOperator() == + OO_Equal); + ThisVal = cast<CXXInstanceCall>(Call).getCXXThisVal(); + AlwaysReturnsLValue = true; + } - SVal L = loc::MemRegionVal(R); - state = state->bindLoc(L, V); - V = L; + const LocationContext *LCtx = Pred->getLocationContext(); + + ExplodedNodeSet Dst; + Bldr.takeNodes(Pred); + + SVal V = Call.getArgSVal(0); + + // If the value being copied is not unknown, load from its location to get + // an aggregate rvalue. + if (Optional<Loc> L = V.getAs<Loc>()) + V = Pred->getState()->getSVal(*L); + else + assert(V.isUnknown()); + + const Expr *CallExpr = Call.getOriginExpr(); + evalBind(Dst, CallExpr, Pred, ThisVal, V, true); + + PostStmt PS(CallExpr, LCtx); + for (ExplodedNodeSet::iterator I = Dst.begin(), E = Dst.end(); + I != E; ++I) { + ProgramStateRef State = (*I)->getState(); + if (AlwaysReturnsLValue) + State = State->BindExpr(CallExpr, LCtx, ThisVal); + else + State = bindReturnValue(Call, LCtx, State); + Bldr.generateNode(PS, State, *I); + } +} + + +/// 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) { + 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); } - Bldr.generateNode(ME, Pred, state->BindExpr(ME, LCtx, V)); + return LValue; } void ExprEngine::VisitCXXConstructExpr(const CXXConstructExpr *CE, @@ -57,6 +124,10 @@ void ExprEngine::VisitCXXConstructExpr(const CXXConstructExpr *CE, const MemRegion *Target = 0; + // 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). + switch (CE->getConstructionKind()) { case CXXConstructExpr::CK_Complete: { // See if we're constructing an existing region by looking at the next @@ -66,29 +137,21 @@ void ExprEngine::VisitCXXConstructExpr(const CXXConstructExpr *CE, CFGElement Next = (*B)[currStmtIdx+1]; // Is this a constructor for a local variable? - if (const CFGStmt *StmtElem = dyn_cast<CFGStmt>(&Next)) { + if (Optional<CFGStmt> StmtElem = Next.getAs<CFGStmt>()) { if (const DeclStmt *DS = dyn_cast<DeclStmt>(StmtElem->getStmt())) { if (const VarDecl *Var = dyn_cast<VarDecl>(DS->getSingleDecl())) { if (Var->getInit()->IgnoreImplicit() == CE) { + SVal LValue = State->getLValue(Var, LCtx); QualType Ty = Var->getType(); - if (const ArrayType *AT = getContext().getAsArrayType(Ty)) { - // FIXME: Handle arrays, which run the same constructor for - // every element. This workaround will just run the first - // constructor (which should still invalidate the entire array). - SVal Base = State->getLValue(Var, LCtx); - Target = State->getLValue(AT->getElementType(), - getSValBuilder().makeZeroArrayIndex(), - Base).getAsRegion(); - } else { - Target = State->getLValue(Var, LCtx).getAsRegion(); - } + LValue = makeZeroElementRegion(State, LValue, Ty); + Target = LValue.getAsRegion(); } } } } // Is this a constructor for a member? - if (const CFGInitializer *InitElem = dyn_cast<CFGInitializer>(&Next)) { + if (Optional<CFGInitializer> InitElem = Next.getAs<CFGInitializer>()) { const CXXCtorInitializer *Init = InitElem->getInitializer(); assert(Init->isAnyMemberInitializer()); @@ -97,13 +160,19 @@ void ExprEngine::VisitCXXConstructExpr(const CXXConstructExpr *CE, LCtx->getCurrentStackFrame()); SVal ThisVal = State->getSVal(ThisPtr); + const ValueDecl *Field; + SVal FieldVal; if (Init->isIndirectMemberInitializer()) { - SVal Field = State->getLValue(Init->getIndirectMember(), ThisVal); - Target = Field.getAsRegion(); + Field = Init->getIndirectMember(); + FieldVal = State->getLValue(Init->getIndirectMember(), ThisVal); } else { - SVal Field = State->getLValue(Init->getMember(), ThisVal); - Target = Field.getAsRegion(); + Field = Init->getMember(); + FieldVal = State->getLValue(Init->getMember(), ThisVal); } + + QualType Ty = Field->getType(); + FieldVal = makeZeroElementRegion(State, FieldVal, Ty); + Target = FieldVal.getAsRegion(); } // FIXME: This will eventually need to handle new-expressions as well. @@ -130,8 +199,10 @@ void ExprEngine::VisitCXXConstructExpr(const CXXConstructExpr *CE, Target = ThisVal.getAsRegion(); } else { // Cast to the base type. - QualType BaseTy = CE->getType(); - SVal BaseVal = getStoreManager().evalDerivedToBase(ThisVal, BaseTy); + bool IsVirtual = + (CE->getConstructionKind() == CXXConstructExpr::CK_VirtualBase); + SVal BaseVal = getStoreManager().evalDerivedToBase(ThisVal, CE->getType(), + IsVirtual); Target = BaseVal.getAsRegion(); } break; @@ -148,14 +219,26 @@ void ExprEngine::VisitCXXConstructExpr(const CXXConstructExpr *CE, getCheckerManager().runCheckersForPreCall(DstPreCall, DstPreVisit, *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); + ExplodedNodeSet DstEvaluated; + StmtNodeBuilder Bldr(DstPreCall, DstEvaluated, *currBldrCtx); + + bool IsArray = isa<ElementRegion>(Target); + if (CE->getConstructor()->isTrivial() && + CE->getConstructor()->isCopyOrMoveConstructor() && + !IsArray) { + // FIXME: Handle other kinds of trivial constructors as well. + for (ExplodedNodeSet::iterator I = DstPreCall.begin(), E = DstPreCall.end(); + I != E; ++I) + performTrivialCopy(Bldr, *I, *Call); + + } else { + for (ExplodedNodeSet::iterator I = DstPreCall.begin(), E = DstPreCall.end(); + I != E; ++I) + defaultEvalCall(Bldr, *I, *Call); + } ExplodedNodeSet DstPostCall; - getCheckerManager().runCheckersForPostCall(DstPostCall, DstInvalidated, + getCheckerManager().runCheckersForPostCall(DstPostCall, DstEvaluated, *Call, *this); getCheckerManager().runCheckersForPostStmt(destNodes, DstPostCall, CE, *this); } @@ -172,11 +255,9 @@ void ExprEngine::VisitCXXDestructor(QualType ObjectType, // 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). - if (const ArrayType *AT = getContext().getAsArrayType(ObjectType)) { - ObjectType = AT->getElementType(); - Dest = State->getLValue(ObjectType, getSValBuilder().makeZeroArrayIndex(), - loc::MemRegionVal(Dest)).getAsRegion(); - } + SVal DestVal = loc::MemRegionVal(Dest); + DestVal = makeZeroElementRegion(State, DestVal, ObjectType); + Dest = DestVal.getAsRegion(); const CXXRecordDecl *RecordDecl = ObjectType->getAsCXXRecordDecl(); assert(RecordDecl && "Only CXXRecordDecls should have destructors"); @@ -211,15 +292,35 @@ void ExprEngine::VisitCXXNewExpr(const CXXNewExpr *CNE, ExplodedNode *Pred, // Also, we need to decide how allocators actually work -- they're not // really part of the CXXNewExpr because they happen BEFORE the // CXXConstructExpr subexpression. See PR12014 for some discussion. - StmtNodeBuilder Bldr(Pred, Dst, *currBldrCtx); unsigned blockCount = currBldrCtx->blockCount(); const LocationContext *LCtx = Pred->getLocationContext(); - DefinedOrUnknownSVal symVal = svalBuilder.conjureSymbolVal(0, CNE, LCtx, - CNE->getType(), - blockCount); - ProgramStateRef State = Pred->getState(); + DefinedOrUnknownSVal 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); + } + // 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(0, CNE, LCtx, CNE->getType(), + blockCount); + + ProgramStateRef State = Pred->getState(); CallEventManager &CEMgr = getStateManager().getCallEventManager(); CallEventRef<CXXAllocatorCall> Call = CEMgr.getCXXAllocatorCall(CNE, State, LCtx); @@ -228,12 +329,13 @@ void ExprEngine::VisitCXXNewExpr(const CXXNewExpr *CNE, ExplodedNode *Pred, // 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 we're compiling with exceptions enabled, and this allocation function // is not declared as non-throwing, failures /must/ be signalled by // exceptions, and thus the return value will never be NULL. // C++11 [basic.stc.dynamic.allocation]p3. - FunctionDecl *FD = CNE->getOperatorNew(); if (FD && getContext().getLangOpts().CXXExceptions) { QualType Ty = FD->getType(); if (const FunctionProtoType *ProtoType = Ty->getAs<FunctionProtoType>()) @@ -241,10 +343,12 @@ void ExprEngine::VisitCXXNewExpr(const CXXNewExpr *CNE, ExplodedNode *Pred, State = State->assume(symVal, true); } + StmtNodeBuilder Bldr(Pred, Dst, *currBldrCtx); + if (CNE->isArray()) { // FIXME: allocating an array requires simulating the constructors. // For now, just return a symbolicated region. - const MemRegion *NewReg = cast<loc::MemRegionVal>(symVal).getRegion(); + const MemRegion *NewReg = symVal.castAs<loc::MemRegionVal>().getRegion(); QualType ObjTy = CNE->getType()->getAs<PointerType>()->getPointeeType(); const ElementRegion *EleReg = getStoreManager().GetElementZeroRegion(NewReg, ObjTy); @@ -258,30 +362,32 @@ 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); - SVal Result = svalBuilder.evalCast(PlacementLoc, CNE->getType(), - CNE->getPlacementArg(0)->getType()); - State = State->BindExpr(CNE, LCtx, Result); - } else { - State = State->BindExpr(CNE, LCtx, symVal); + Result = svalBuilder.evalCast(PlacementLoc, CNE->getType(), + CNE->getPlacementArg(0)->getType()); } + // Bind the address of the object, then check to see if we cached out. + State = State->BindExpr(CNE, LCtx, Result); + ExplodedNode *NewN = Bldr.generateNode(CNE, Pred, State); + if (!NewN) + return; + // If the type is not a record, we won't have a CXXConstructExpr as an // initializer. Copy the value over. if (const Expr *Init = CNE->getInitializer()) { if (!isa<CXXConstructExpr>(Init)) { - QualType ObjTy = CNE->getType()->getAs<PointerType>()->getPointeeType(); - (void)ObjTy; - assert(!ObjTy->isRecordType()); - SVal Location = State->getSVal(CNE, LCtx); - if (isa<Loc>(Location)) - State = State->bindLoc(cast<Loc>(Location), State->getSVal(Init, LCtx)); + assert(Bldr.getResults().size() == 1); + Bldr.takeNodes(NewN); + + assert(!CNE->getType()->getPointeeCXXRecordDecl()); + evalBind(Dst, CNE, NewN, Result, State->getSVal(Init, LCtx), + /*FirstInit=*/IsStandardGlobalOpNewFunction); } } - - Bldr.generateNode(CNE, Pred, State); } void ExprEngine::VisitCXXDeleteExpr(const CXXDeleteExpr *CDE, diff --git a/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp b/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp index 3ead081..f01e4e7 100644 --- a/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp +++ b/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp @@ -13,13 +13,13 @@ #define DEBUG_TYPE "ExprEngine" -#include "clang/Analysis/Analyses/LiveVariables.h" -#include "clang/StaticAnalyzer/Core/CheckerManager.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" #include "clang/AST/CXXInheritance.h" #include "clang/AST/DeclCXX.h" #include "clang/AST/ParentMap.h" +#include "clang/Analysis/Analyses/LiveVariables.h" +#include "clang/StaticAnalyzer/Core/CheckerManager.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" #include "llvm/ADT/SmallSet.h" #include "llvm/ADT/Statistic.h" #include "llvm/Support/SaveAndRestore.h" @@ -33,6 +33,9 @@ STATISTIC(NumOfDynamicDispatchPathSplits, STATISTIC(NumInlinedCalls, "The # of times we inlined a call"); +STATISTIC(NumReachedInlineCountMax, + "The # of times we reached inline count maximum"); + void ExprEngine::processCallEnter(CallEnter CE, ExplodedNode *Pred) { // Get the entry block in the CFG of the callee. const StackFrameContext *calleeCtx = CE.getCalleeContext(); @@ -64,6 +67,7 @@ void ExprEngine::processCallEnter(CallEnter CE, ExplodedNode *Pred) { static std::pair<const Stmt*, const CFGBlock*> getLastStmt(const ExplodedNode *Node) { const Stmt *S = 0; + const CFGBlock *Blk = 0; const StackFrameContext *SF = Node->getLocation().getLocationContext()->getCurrentStackFrame(); @@ -73,10 +77,10 @@ static std::pair<const Stmt*, const ProgramPoint &PP = Node->getLocation(); if (PP.getLocationContext()->getCurrentStackFrame() == SF) { - if (const StmtPoint *SP = dyn_cast<StmtPoint>(&PP)) { + if (Optional<StmtPoint> SP = PP.getAs<StmtPoint>()) { S = SP->getStmt(); break; - } else if (const CallExitEnd *CEE = dyn_cast<CallExitEnd>(&PP)) { + } else if (Optional<CallExitEnd> CEE = PP.getAs<CallExitEnd>()) { S = CEE->getCalleeContext()->getCallSite(); if (S) break; @@ -84,15 +88,17 @@ static std::pair<const Stmt*, // If there is no statement, this is an implicitly-generated call. // We'll walk backwards over it and then continue the loop to find // an actual statement. - const CallEnter *CE; + Optional<CallEnter> CE; do { Node = Node->getFirstPred(); CE = Node->getLocationAs<CallEnter>(); } while (!CE || CE->getCalleeContext() != CEE->getCalleeContext()); // Continue searching the graph. + } else if (Optional<BlockEdge> BE = PP.getAs<BlockEdge>()) { + Blk = BE->getSrc(); } - } else if (const CallEnter *CE = dyn_cast<CallEnter>(&PP)) { + } else if (Optional<CallEnter> CE = PP.getAs<CallEnter>()) { // If we reached the CallEnter for this function, it has no statements. if (CE->getCalleeContext() == SF) break; @@ -104,24 +110,6 @@ static std::pair<const Stmt*, Node = *Node->pred_begin(); } - const CFGBlock *Blk = 0; - if (S) { - // Now, get the enclosing basic block. - while (Node) { - const ProgramPoint &PP = Node->getLocation(); - if (isa<BlockEdge>(PP) && - (PP.getLocationContext()->getCurrentStackFrame() == SF)) { - BlockEdge &EPP = cast<BlockEdge>(PP); - Blk = EPP.getDst(); - break; - } - if (Node->pred_empty()) - return std::pair<const Stmt*, const CFGBlock*>(S, (CFGBlock*)0); - - Node = *Node->pred_begin(); - } - } - return std::pair<const Stmt*, const CFGBlock*>(S, Blk); } @@ -133,7 +121,7 @@ static std::pair<const Stmt*, static SVal adjustReturnValue(SVal V, QualType ExpectedTy, QualType ActualTy, StoreManager &StoreMgr) { // For now, the only adjustments we handle apply only to locations. - if (!isa<Loc>(V)) + if (!V.getAs<Loc>()) return V; // If the types already match, don't do any unnecessary work. @@ -168,27 +156,25 @@ static SVal adjustReturnValue(SVal V, QualType ExpectedTy, QualType ActualTy, void ExprEngine::removeDeadOnEndOfFunction(NodeBuilderContext& BC, ExplodedNode *Pred, ExplodedNodeSet &Dst) { - NodeBuilder Bldr(Pred, Dst, BC); - // Find the last statement in the function and the corresponding basic block. const Stmt *LastSt = 0; const CFGBlock *Blk = 0; llvm::tie(LastSt, Blk) = getLastStmt(Pred); if (!Blk || !LastSt) { + Dst.Add(Pred); return; } - - // If the last statement is return, everything it references should stay live. - if (isa<ReturnStmt>(LastSt)) - return; - // Here, we call the Symbol Reaper with 0 stack context telling it to clean up - // everything on the stack. We use LastStmt as a diagnostic statement, with - // which the PreStmtPurgeDead point will be associated. - currBldrCtx = &BC; - removeDead(Pred, Dst, 0, 0, LastSt, + // Here, we destroy the current location context. We use the current + // function's entire body as a diagnostic statement, with which the program + // point will be associated. However, we only want to use LastStmt as a + // reference for what to clean up if it's a ReturnStmt; otherwise, everything + // is dead. + SaveAndRestore<const NodeBuilderContext *> NodeContextRAII(currBldrCtx, &BC); + const LocationContext *LCtx = Pred->getLocationContext(); + removeDead(Pred, Dst, dyn_cast<ReturnStmt>(LastSt), LCtx, + LCtx->getAnalysisDeclContext()->getBody(), ProgramPoint::PostStmtPurgeDeadSymbolsKind); - currBldrCtx = 0; } static bool wasDifferentDeclUsedForInlining(CallEventRef<> Call, @@ -201,6 +187,23 @@ 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: @@ -261,13 +264,9 @@ void ExprEngine::processCallExit(ExplodedNode *CEBNode) { svalBuilder.getCXXThis(CCE->getConstructor()->getParent(), calleeCtx); SVal ThisV = state->getSVal(This); - // If the constructed object is a prvalue, get its bindings. - // Note that we have to be careful here because constructors embedded - // in DeclStmts are not marked as lvalues. - if (!CCE->isGLValue()) - if (const MemRegion *MR = ThisV.getAsRegion()) - if (isa<CXXTempObjectRegion>(MR)) - ThisV = state->getSVal(cast<Loc>(ThisV)); + // If the constructed object is a temporary prvalue, get its bindings. + if (isTemporaryPRValue(CCE, ThisV)) + ThisV = state->getSVal(ThisV.castAs<Loc>()); state = state->BindExpr(CCE, callerCtx, ThisV); } @@ -290,11 +289,12 @@ void ExprEngine::processCallExit(ExplodedNode *CEBNode) { NodeBuilderContext Ctx(getCoreEngine(), Blk, BindedRetNode); currBldrCtx = &Ctx; - // Here, we call the Symbol Reaper with 0 statement and caller location + // Here, we call the Symbol Reaper with 0 statement and callee location // context, telling it to clean up everything in the callee's context - // (and it's children). We use LastStmt as a diagnostic statement, which - // which the PreStmtPurge Dead point will be associated. - removeDead(BindedRetNode, CleanedNodes, 0, callerCtx, LastSt, + // (and its children). We use the callee's function body as a diagnostic + // statement, with which the program point will be associated. + removeDead(BindedRetNode, CleanedNodes, 0, calleeCtx, + calleeCtx->getAnalysisDeclContext()->getBody(), ProgramPoint::PostStmtPurgeDeadSymbolsKind); currBldrCtx = 0; } else { @@ -394,63 +394,6 @@ static bool IsInStdNamespace(const FunctionDecl *FD) { return ND->getName() == "std"; } -// Determine if we should inline the call. -bool ExprEngine::shouldInlineDecl(const Decl *D, ExplodedNode *Pred) { - AnalysisDeclContext *CalleeADC = AMgr.getAnalysisDeclContext(D); - const CFG *CalleeCFG = CalleeADC->getCFG(); - - // It is possible that the CFG cannot be constructed. - // Be safe, and check if the CalleeCFG is valid. - if (!CalleeCFG) - return false; - - bool IsRecursive = false; - unsigned StackDepth = 0; - examineStackFrames(D, Pred->getLocationContext(), IsRecursive, StackDepth); - if ((StackDepth >= AMgr.options.InlineMaxStackDepth) && - ((CalleeCFG->getNumBlockIDs() > AMgr.options.getAlwaysInlineSize()) - || IsRecursive)) - return false; - - if (Engine.FunctionSummaries->hasReachedMaxBlockCount(D)) - return false; - - if (CalleeCFG->getNumBlockIDs() > AMgr.options.InlineMaxFunctionSize) - return false; - - // Do not inline variadic calls (for now). - if (const BlockDecl *BD = dyn_cast<BlockDecl>(D)) { - if (BD->isVariadic()) - return false; - } - else if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(D)) { - if (FD->isVariadic()) - return false; - } - - if (getContext().getLangOpts().CPlusPlus) { - if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(D)) { - // Conditionally allow the inlining of template functions. - if (!getAnalysisManager().options.mayInlineTemplateFunctions()) - if (FD->getTemplatedKind() != FunctionDecl::TK_NonTemplate) - return false; - - // Conditionally allow the inlining of C++ standard library functions. - if (!getAnalysisManager().options.mayInlineCXXStandardLibrary()) - if (getContext().getSourceManager().isInSystemHeader(FD->getLocation())) - if (IsInStdNamespace(FD)) - return false; - } - } - - // It is possible that the live variables analysis cannot be - // run. If so, bail out. - if (!CalleeADC->getAnalysis<RelaxedLiveVariables>()) - return false; - - return true; -} - // The GDM component containing the dynamic dispatch bifurcation info. When // the exact type of the receiver is not known, we want to explore both paths - // one on which we do inline it and the other one on which we don't. This is @@ -474,107 +417,16 @@ bool ExprEngine::inlineCall(const CallEvent &Call, const Decl *D, const LocationContext *CurLC = Pred->getLocationContext(); const StackFrameContext *CallerSFC = CurLC->getCurrentStackFrame(); - const LocationContext *ParentOfCallee = 0; - - AnalyzerOptions &Opts = getAnalysisManager().options; - - // FIXME: Refactor this check into a hypothetical CallEvent::canInline. - switch (Call.getKind()) { - case CE_Function: - break; - case CE_CXXMember: - case CE_CXXMemberOperator: - if (!Opts.mayInlineCXXMemberFunction(CIMK_MemberFunctions)) - return false; - break; - case CE_CXXConstructor: { - if (!Opts.mayInlineCXXMemberFunction(CIMK_Constructors)) - return false; - - const CXXConstructorCall &Ctor = cast<CXXConstructorCall>(Call); - - // FIXME: We don't handle constructors or destructors for arrays properly. - const MemRegion *Target = Ctor.getCXXThisVal().getAsRegion(); - if (Target && isa<ElementRegion>(Target)) - return false; - - // 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 false; - - // Inlining constructors requires including initializers in the CFG. - const AnalysisDeclContext *ADC = CallerSFC->getAnalysisDeclContext(); - assert(ADC->getCFGBuildOptions().AddInitializers && "No CFG initializers"); - (void)ADC; - - // If the destructor is trivial, it's always safe to inline the constructor. - if (Ctor.getDecl()->getParent()->hasTrivialDestructor()) - break; - - // For other types, only inline constructors if destructor inlining is - // also enabled. - if (!Opts.mayInlineCXXMemberFunction(CIMK_Destructors)) - return false; - - // 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)) - return false; - - break; - } - case CE_CXXDestructor: { - if (!Opts.mayInlineCXXMemberFunction(CIMK_Destructors)) - return false; - - // Inlining destructors requires building the CFG correctly. - const AnalysisDeclContext *ADC = CallerSFC->getAnalysisDeclContext(); - 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)) - return false; - - break; - } - case CE_CXXAllocator: - // Do not inline allocators until we model deallocators. - // This is unfortunate, but basically necessary for smart pointers and such. - return false; - case CE_Block: { + const LocationContext *ParentOfCallee = CallerSFC; + if (Call.getKind() == CE_Block) { const BlockDataRegion *BR = cast<BlockCall>(Call).getBlockRegion(); assert(BR && "If we have the block definition we should have its region"); AnalysisDeclContext *BlockCtx = AMgr.getAnalysisDeclContext(D); ParentOfCallee = BlockCtx->getBlockInvocationContext(CallerSFC, cast<BlockDecl>(D), BR); - break; - } - case CE_ObjCMessage: - if (!Opts.mayInlineObjCMethod()) - return false; - if (!(getAnalysisManager().options.IPAMode == DynamicDispatch || - getAnalysisManager().options.IPAMode == DynamicDispatchBifurcate)) - return false; - break; } - - if (!shouldInlineDecl(D, Pred)) - return false; - if (!ParentOfCallee) - ParentOfCallee = CallerSFC; - // This may be NULL, but that's fine. const Expr *CallE = Call.getOriginExpr(); @@ -585,6 +437,7 @@ bool ExprEngine::inlineCall(const CallEvent &Call, const Decl *D, currBldrCtx->getBlock(), currStmtIdx); + CallEnter Loc(CallE, CalleeSFC, CurLC); // Construct a new state which contains the mapping from actual to @@ -613,11 +466,11 @@ bool ExprEngine::inlineCall(const CallEvent &Call, const Decl *D, static ProgramStateRef getInlineFailedState(ProgramStateRef State, const Stmt *CallE) { - void *ReplayState = State->get<ReplayWithoutInlining>(); + const void *ReplayState = State->get<ReplayWithoutInlining>(); if (!ReplayState) return 0; - assert(ReplayState == (const void*)CallE && "Backtracked to the wrong call."); + assert(ReplayState == CallE && "Backtracked to the wrong call."); (void)CallE; return State->remove<ReplayWithoutInlining>(); @@ -696,7 +549,13 @@ ProgramStateRef ExprEngine::bindReturnValue(const CallEvent &Call, } } } else if (const CXXConstructorCall *C = dyn_cast<CXXConstructorCall>(&Call)){ - return State->BindExpr(E, LCtx, C->getCXXThisVal()); + 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>()); + + return State->BindExpr(E, LCtx, ThisV); } // Conjure a symbol if the return value is unknown. @@ -710,7 +569,8 @@ ProgramStateRef ExprEngine::bindReturnValue(const CallEvent &Call, // Conservatively evaluate call by invalidating regions and binding // a conjured return value. void ExprEngine::conservativeEvalCall(const CallEvent &Call, NodeBuilder &Bldr, - ExplodedNode *Pred, ProgramStateRef State) { + ExplodedNode *Pred, + ProgramStateRef State) { State = Call.invalidateRegions(currBldrCtx->blockCount(), State); State = bindReturnValue(Call, Pred->getLocationContext(), State); @@ -718,38 +578,332 @@ 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) { + const LocationContext *CurLC = Pred->getLocationContext(); + const StackFrameContext *CallerSFC = CurLC->getCurrentStackFrame(); + switch (Call.getKind()) { + case CE_Function: + case CE_Block: + break; + case CE_CXXMember: + case CE_CXXMemberOperator: + if (!Opts.mayInlineCXXMemberFunction(CIMK_MemberFunctions)) + return CIP_DisallowedAlways; + break; + case CE_CXXConstructor: { + if (!Opts.mayInlineCXXMemberFunction(CIMK_Constructors)) + return CIP_DisallowedAlways; + + const CXXConstructorCall &Ctor = cast<CXXConstructorCall>(Call); + + // 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)) + 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"); + (void)ADC; + + // If the destructor is trivial, it's always safe to inline the constructor. + if (Ctor.getDecl()->getParent()->hasTrivialDestructor()) + break; + + // For other types, only inline constructors if destructor inlining is + // also enabled. + 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)) + return CIP_DisallowedOnce; + + break; + } + case CE_CXXDestructor: { + if (!Opts.mayInlineCXXMemberFunction(CIMK_Destructors)) + return CIP_DisallowedAlways; + + // Inlining destructors requires building the CFG correctly. + const AnalysisDeclContext *ADC = CallerSFC->getAnalysisDeclContext(); + 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)) + return CIP_DisallowedOnce; + + break; + } + case CE_CXXAllocator: + // Do not inline allocators until we model deallocators. + // This is unfortunate, but basically necessary for smart pointers and such. + return CIP_DisallowedAlways; + case CE_ObjCMessage: + if (!Opts.mayInlineObjCMethod()) + return CIP_DisallowedAlways; + if (!(Opts.getIPAMode() == IPAK_DynamicDispatch || + Opts.getIPAMode() == IPAK_DynamicDispatchBifurcate)) + return CIP_DisallowedAlways; + break; + } + + return CIP_Allowed; +} + +/// Returns true if the given C++ class is a container. +/// +/// Our heuristic for this is whether it contains a method named 'begin()' or a +/// nested type named 'iterator'. +static bool isContainerClass(const ASTContext &Ctx, const CXXRecordDecl *RD) { + // Don't record any path information. + CXXBasePaths Paths(false, false, false); + + const IdentifierInfo &BeginII = Ctx.Idents.get("begin"); + DeclarationName BeginName = Ctx.DeclarationNames.getIdentifier(&BeginII); + DeclContext::lookup_const_result BeginDecls = RD->lookup(BeginName); + if (!BeginDecls.empty()) + return true; + if (RD->lookupInBases(&CXXRecordDecl::FindOrdinaryMember, + BeginName.getAsOpaquePtr(), + Paths)) + return true; + + const IdentifierInfo &IterII = Ctx.Idents.get("iterator"); + DeclarationName IteratorName = Ctx.DeclarationNames.getIdentifier(&IterII); + DeclContext::lookup_const_result IterDecls = RD->lookup(IteratorName); + if (!IterDecls.empty()) + return true; + if (RD->lookupInBases(&CXXRecordDecl::FindOrdinaryMember, + IteratorName.getAsOpaquePtr(), + Paths)) + return true; + + return false; +} + +/// Returns true if the given function refers to a constructor or destructor of +/// a C++ container. +/// +/// We generally do a poor job modeling most containers right now, and would +/// prefer not to inline their methods. +static bool isContainerCtorOrDtor(const ASTContext &Ctx, + const FunctionDecl *FD) { + // Heuristic: a type is a container if it contains a "begin()" method + // or a type named "iterator". + if (!(isa<CXXConstructorDecl>(FD) || isa<CXXDestructorDecl>(FD))) + return false; + + const CXXRecordDecl *RD = cast<CXXMethodDecl>(FD)->getParent(); + return isContainerClass(Ctx, RD); +} + +/// Returns true if the function in \p CalleeADC may be inlined in general. +/// +/// 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(const CallEvent &Call, AnalysisDeclContext *CalleeADC, + AnalyzerOptions &Opts) { + // FIXME: Do not inline variadic calls. + if (Call.isVariadic()) + return false; + + // Check certain C++-related inlining policies. + ASTContext &Ctx = CalleeADC->getASTContext(); + if (Ctx.getLangOpts().CPlusPlus) { + if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(CalleeADC->getDecl())) { + // Conditionally control the inlining of template functions. + if (!Opts.mayInlineTemplateFunctions()) + if (FD->getTemplatedKind() != FunctionDecl::TK_NonTemplate) + return false; + + // Conditionally control the inlining of C++ standard library functions. + if (!Opts.mayInlineCXXStandardLibrary()) + if (Ctx.getSourceManager().isInSystemHeader(FD->getLocation())) + if (IsInStdNamespace(FD)) + return false; + + // Conditionally control the inlining of methods on objects that look + // like C++ containers. + if (!Opts.mayInlineCXXContainerCtorsAndDtors()) + if (!Ctx.getSourceManager().isFromMainFile(FD->getLocation())) + if (isContainerCtorOrDtor(Ctx, FD)) + return false; + } + } + + // It is possible that the CFG cannot be constructed. + // Be safe, and check if the CalleeCFG is valid. + const CFG *CalleeCFG = CalleeADC->getCFG(); + if (!CalleeCFG) + return false; + + // Do not inline large functions. + if (CalleeCFG->getNumBlockIDs() > Opts.getMaxInlinableSize()) + return false; + + // It is possible that the live variables analysis cannot be + // run. If so, bail out. + if (!CalleeADC->getAnalysis<RelaxedLiveVariables>()) + return false; + + return true; +} + +bool ExprEngine::shouldInlineCall(const CallEvent &Call, const Decl *D, + const ExplodedNode *Pred) { + if (!D) + return false; + + AnalysisManager &AMgr = getAnalysisManager(); + AnalyzerOptions &Opts = AMgr.options; + AnalysisDeclContextManager &ADCMgr = AMgr.getAnalysisDeclContextManager(); + AnalysisDeclContext *CalleeADC = ADCMgr.getContext(D); + + // 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. + if (CalleeADC->isBodyAutosynthesized()) + return true; + + if (!AMgr.shouldInlineCall()) + return false; + + // Check if this function has been marked as non-inlinable. + Optional<bool> MayInline = Engine.FunctionSummaries->mayInline(D); + if (MayInline.hasValue()) { + if (!MayInline.getValue()) + return false; + + } 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(Call, CalleeADC, Opts)) { + Engine.FunctionSummaries->markMayInline(D); + } else { + Engine.FunctionSummaries->markShouldNotInline(D); + return false; + } + } + + // Check if we should inline a call based on its kind. + // 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); + if (CIP != CIP_Allowed) { + if (CIP == CIP_DisallowedAlways) { + assert(!MayInline.hasValue() || MayInline.getValue()); + Engine.FunctionSummaries->markShouldNotInline(D); + } + return false; + } + + const CFG *CalleeCFG = CalleeADC->getCFG(); + + // Do not inline if recursive or we've reached max stack frame count. + bool IsRecursive = false; + unsigned StackDepth = 0; + examineStackFrames(D, Pred->getLocationContext(), IsRecursive, StackDepth); + if ((StackDepth >= Opts.InlineMaxStackDepth) && + ((CalleeCFG->getNumBlockIDs() > Opts.getAlwaysInlineSize()) + || IsRecursive)) + return false; + + // Do not inline large functions too many times. + if ((Engine.FunctionSummaries->getNumTimesInlined(D) > + Opts.getMaxTimesInlineLarge()) && + CalleeCFG->getNumBlockIDs() > 13) { + NumReachedInlineCountMax++; + return false; + } + + if (HowToInline == Inline_Minimal && + (CalleeCFG->getNumBlockIDs() > Opts.getAlwaysInlineSize() + || IsRecursive)) + return false; + + Engine.FunctionSummaries->bumpNumTimesInlined(D); + + return true; +} + +static bool isTrivialObjectAssignment(const CallEvent &Call) { + const CXXInstanceCall *ICall = dyn_cast<CXXInstanceCall>(&Call); + if (!ICall) + return false; + + const CXXMethodDecl *MD = dyn_cast_or_null<CXXMethodDecl>(ICall->getDecl()); + if (!MD) + return false; + if (!(MD->isCopyAssignmentOperator() || MD->isMoveAssignmentOperator())) + return false; + + return MD->isTrivial(); +} + void ExprEngine::defaultEvalCall(NodeBuilder &Bldr, ExplodedNode *Pred, const CallEvent &CallTemplate) { // Make sure we have the most recent state attached to the call. ProgramStateRef State = Pred->getState(); CallEventRef<> Call = CallTemplate.cloneWithState(State); - if (!getAnalysisManager().shouldInlineCall()) { - conservativeEvalCall(*Call, Bldr, Pred, State); + // Special-case trivial assignment operators. + if (isTrivialObjectAssignment(*Call)) { + performTrivialCopy(Bldr, Pred, *Call); return; } + // Try to inline the call. // The origin expression here is just used as a kind of checksum; // this should still be safe even for CallEvents that don't come from exprs. const Expr *E = Call->getOriginExpr(); - ProgramStateRef InlinedFailedState = getInlineFailedState(State, E); + ProgramStateRef InlinedFailedState = getInlineFailedState(State, E); if (InlinedFailedState) { // If we already tried once and failed, make sure we don't retry later. State = InlinedFailedState; } else { RuntimeDefinition RD = Call->getRuntimeDefinition(); const Decl *D = RD.getDecl(); - if (D) { + if (shouldInlineCall(*Call, D, Pred)) { if (RD.mayHaveOtherDefinitions()) { + AnalyzerOptions &Options = getAnalysisManager().options; + // Explore with and without inlining the call. - if (getAnalysisManager().options.IPAMode == DynamicDispatchBifurcate) { + if (Options.getIPAMode() == IPAK_DynamicDispatchBifurcate) { BifurcateCall(RD.getDispatchRegion(), *Call, D, Bldr, Pred); return; } // Don't inline if we're not in any dynamic dispatch mode. - if (getAnalysisManager().options.IPAMode != DynamicDispatch) { + if (Options.getIPAMode() != IPAK_DynamicDispatch) { conservativeEvalCall(*Call, Bldr, Pred, State); return; } diff --git a/lib/StaticAnalyzer/Core/ExprEngineObjC.cpp b/lib/StaticAnalyzer/Core/ExprEngineObjC.cpp index 51dda19b..d276d92 100644 --- a/lib/StaticAnalyzer/Core/ExprEngineObjC.cpp +++ b/lib/StaticAnalyzer/Core/ExprEngineObjC.cpp @@ -103,8 +103,8 @@ void ExprEngine::VisitObjCForCollectionStmt(const ObjCForCollectionStmt *S, // Handle the case where the container has no elements. SVal FalseV = svalBuilder.makeTruthVal(0); ProgramStateRef noElems = state->BindExpr(S, LCtx, FalseV); - - if (loc::MemRegionVal *MV = dyn_cast<loc::MemRegionVal>(&elementV)) + + 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 @@ -161,8 +161,9 @@ void ExprEngine::VisitObjCMessage(const ObjCMessageExpr *ME, SVal recVal = UpdatedMsg->getReceiverSVal(); if (!recVal.isUndef()) { // Bifurcate the state into nil and non-nil ones. - DefinedOrUnknownSVal receiverVal = cast<DefinedOrUnknownSVal>(recVal); - + DefinedOrUnknownSVal receiverVal = + recVal.castAs<DefinedOrUnknownSVal>(); + ProgramStateRef notNilState, nilState; llvm::tie(notNilState, nilState) = State->assume(receiverVal); @@ -179,13 +180,13 @@ void ExprEngine::VisitObjCMessage(const ObjCMessageExpr *ME, if (ObjCNoRet.isImplicitNoReturn(ME)) { // If we raise an exception, for now treat it as a sink. // Eventually we will want to handle exceptions properly. - Bldr.generateSink(currStmt, Pred, State); + Bldr.generateSink(ME, Pred, State); continue; } // Generate a transition to non-Nil state. if (notNilState != State) { - Pred = Bldr.generateNode(currStmt, Pred, notNilState); + Pred = Bldr.generateNode(ME, Pred, notNilState); assert(Pred && "Should have cached out already!"); } } @@ -195,7 +196,7 @@ void ExprEngine::VisitObjCMessage(const ObjCMessageExpr *ME, if (ObjCNoRet.isImplicitNoReturn(ME)) { // If we raise an exception, for now treat it as a sink. // Eventually we will want to handle exceptions properly. - Bldr.generateSink(currStmt, Pred, Pred->getState()); + Bldr.generateSink(ME, Pred, Pred->getState()); continue; } } diff --git a/lib/StaticAnalyzer/Core/FunctionSummary.cpp b/lib/StaticAnalyzer/Core/FunctionSummary.cpp index c227aac..c21735b 100644 --- a/lib/StaticAnalyzer/Core/FunctionSummary.cpp +++ b/lib/StaticAnalyzer/Core/FunctionSummary.cpp @@ -1,4 +1,4 @@ -//== FunctionSummary.h - Stores summaries of functions. ------------*- C++ -*-// +//== FunctionSummary.cpp - Stores summaries of functions. ----------*- C++ -*-// // // The LLVM Compiler Infrastructure // @@ -7,7 +7,7 @@ // //===----------------------------------------------------------------------===// // -// This file defines a summary of a function gathered/used by static analyzes. +// This file defines a summary of a function gathered/used by static analysis. // //===----------------------------------------------------------------------===// @@ -15,16 +15,10 @@ using namespace clang; using namespace ento; -FunctionSummariesTy::~FunctionSummariesTy() { - for (MapTy::iterator I = Map.begin(), E = Map.end(); I != E; ++I) { - delete(I->second); - } -} - unsigned FunctionSummariesTy::getTotalNumBasicBlocks() { unsigned Total = 0; for (MapTy::iterator I = Map.begin(), E = Map.end(); I != E; ++I) { - Total += I->second->TotalBasicBlocks; + Total += I->second.TotalBasicBlocks; } return Total; } @@ -32,7 +26,7 @@ unsigned FunctionSummariesTy::getTotalNumBasicBlocks() { unsigned FunctionSummariesTy::getTotalNumVisitedBasicBlocks() { unsigned Total = 0; for (MapTy::iterator I = Map.begin(), E = Map.end(); I != E; ++I) { - Total += I->second->VisitedBasicBlocks.count(); + Total += I->second.VisitedBasicBlocks.count(); } return Total; } diff --git a/lib/StaticAnalyzer/Core/HTMLDiagnostics.cpp b/lib/StaticAnalyzer/Core/HTMLDiagnostics.cpp index fd875f6..73426da 100644 --- a/lib/StaticAnalyzer/Core/HTMLDiagnostics.cpp +++ b/lib/StaticAnalyzer/Core/HTMLDiagnostics.cpp @@ -12,19 +12,19 @@ //===----------------------------------------------------------------------===// #include "clang/StaticAnalyzer/Core/PathDiagnosticConsumers.h" -#include "clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h" #include "clang/AST/ASTContext.h" #include "clang/AST/Decl.h" -#include "clang/Basic/SourceManager.h" #include "clang/Basic/FileManager.h" -#include "clang/Rewrite/Core/Rewriter.h" -#include "clang/Rewrite/Core/HTMLRewrite.h" +#include "clang/Basic/SourceManager.h" #include "clang/Lex/Lexer.h" #include "clang/Lex/Preprocessor.h" +#include "clang/Rewrite/Core/HTMLRewrite.h" +#include "clang/Rewrite/Core/Rewriter.h" +#include "clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/MemoryBuffer.h" -#include "llvm/Support/raw_ostream.h" #include "llvm/Support/Path.h" +#include "llvm/Support/raw_ostream.h" using namespace clang; using namespace ento; @@ -76,7 +76,8 @@ HTMLDiagnostics::HTMLDiagnostics(const std::string& prefix, FilePrefix.appendComponent("report"); } -void ento::createHTMLDiagnosticConsumer(PathDiagnosticConsumers &C, +void ento::createHTMLDiagnosticConsumer(AnalyzerOptions &AnalyzerOpts, + PathDiagnosticConsumers &C, const std::string& prefix, const Preprocessor &PP) { C.push_back(new HTMLDiagnostics(prefix, PP)); diff --git a/lib/StaticAnalyzer/Core/MemRegion.cpp b/lib/StaticAnalyzer/Core/MemRegion.cpp index fab10cf..b3a1e65 100644 --- a/lib/StaticAnalyzer/Core/MemRegion.cpp +++ b/lib/StaticAnalyzer/Core/MemRegion.cpp @@ -14,13 +14,14 @@ //===----------------------------------------------------------------------===// #include "clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h" -#include "clang/Analysis/AnalysisContext.h" -#include "clang/Analysis/Support/BumpVector.h" +#include "clang/AST/Attr.h" #include "clang/AST/CharUnits.h" #include "clang/AST/DeclObjC.h" #include "clang/AST/RecordLayout.h" +#include "clang/Analysis/AnalysisContext.h" +#include "clang/Analysis/Support/BumpVector.h" #include "clang/Basic/SourceManager.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h" #include "llvm/Support/raw_ostream.h" using namespace clang; @@ -194,6 +195,10 @@ DefinedOrUnknownSVal TypedValueRegion::getExtent(SValBuilder &svalBuilder) const } DefinedOrUnknownSVal FieldRegion::getExtent(SValBuilder &svalBuilder) const { + // Force callers to deal with bitfields explicitly. + if (getDecl()->isBitField()) + return UnknownVal(); + DefinedOrUnknownSVal Extent = DeclRegion::getExtent(svalBuilder); // A zero-length array at the end of a struct often stands for dynamically- @@ -233,7 +238,7 @@ QualType ObjCIvarRegion::getValueType() const { } QualType CXXBaseObjectRegion::getValueType() const { - return QualType(decl->getTypeForDecl(), 0); + return QualType(getDecl()->getTypeForDecl(), 0); } //===----------------------------------------------------------------------===// @@ -272,10 +277,11 @@ void ObjCStringRegion::ProfileRegion(llvm::FoldingSetNodeID& ID, void AllocaRegion::ProfileRegion(llvm::FoldingSetNodeID& ID, const Expr *Ex, unsigned cnt, - const MemRegion *) { + const MemRegion *superRegion) { ID.AddInteger((unsigned) AllocaRegionKind); ID.AddPointer(Ex); ID.AddInteger(cnt); + ID.AddPointer(superRegion); } void AllocaRegion::Profile(llvm::FoldingSetNodeID& ID) const { @@ -400,14 +406,16 @@ void CXXTempObjectRegion::Profile(llvm::FoldingSetNodeID &ID) const { } void CXXBaseObjectRegion::ProfileRegion(llvm::FoldingSetNodeID &ID, - const CXXRecordDecl *decl, - const MemRegion *sReg) { - ID.AddPointer(decl); - ID.AddPointer(sReg); + const CXXRecordDecl *RD, + bool IsVirtual, + const MemRegion *SReg) { + ID.AddPointer(RD); + ID.AddBoolean(IsVirtual); + ID.AddPointer(SReg); } void CXXBaseObjectRegion::Profile(llvm::FoldingSetNodeID &ID) const { - ProfileRegion(ID, decl, superRegion); + ProfileRegion(ID, getDecl(), isVirtual(), superRegion); } //===----------------------------------------------------------------------===// @@ -470,7 +478,7 @@ void CXXTempObjectRegion::dumpToStream(raw_ostream &os) const { } void CXXBaseObjectRegion::dumpToStream(raw_ostream &os) const { - os << "base{" << superRegion << ',' << decl->getName() << '}'; + os << "base{" << superRegion << ',' << getDecl()->getName() << '}'; } void CXXThisRegion::dumpToStream(raw_ostream &os) const { @@ -562,6 +570,14 @@ void VarRegion::printPretty(raw_ostream &os) const { os << getDecl()->getName(); } +bool ObjCIvarRegion::canPrintPretty() const { + return true; +} + +void ObjCIvarRegion::printPretty(raw_ostream &os) const { + os << getDecl()->getName(); +} + bool FieldRegion::canPrintPretty() const { return superRegion->canPrintPretty(); } @@ -883,41 +899,50 @@ MemRegionManager::getCXXTempObjectRegion(Expr const *E, return getSubRegion<CXXTempObjectRegion>(E, getStackLocalsRegion(SFC)); } -const CXXBaseObjectRegion * -MemRegionManager::getCXXBaseObjectRegion(const CXXRecordDecl *decl, - const MemRegion *superRegion) { - // Check that the base class is actually a direct base of this region. - if (const TypedValueRegion *TVR = dyn_cast<TypedValueRegion>(superRegion)) { - if (const CXXRecordDecl *Class = TVR->getValueType()->getAsCXXRecordDecl()){ - if (Class->isVirtuallyDerivedFrom(decl)) { - // Virtual base regions should not be layered, since the layout rules - // are different. - while (const CXXBaseObjectRegion *Base = - dyn_cast<CXXBaseObjectRegion>(superRegion)) { - superRegion = Base->getSuperRegion(); - } - assert(superRegion && !isa<MemSpaceRegion>(superRegion)); +/// Checks whether \p BaseClass is a valid virtual or direct non-virtual base +/// class of the type of \p Super. +static bool isValidBaseClass(const CXXRecordDecl *BaseClass, + const TypedValueRegion *Super, + bool IsVirtual) { + BaseClass = BaseClass->getCanonicalDecl(); - } else { - // Non-virtual bases should always be direct bases. -#ifndef NDEBUG - bool FoundBase = false; - for (CXXRecordDecl::base_class_const_iterator I = Class->bases_begin(), - E = Class->bases_end(); - I != E; ++I) { - if (I->getType()->getAsCXXRecordDecl() == decl) { - FoundBase = true; - break; - } - } + const CXXRecordDecl *Class = Super->getValueType()->getAsCXXRecordDecl(); + if (!Class) + return true; + + if (IsVirtual) + return Class->isVirtuallyDerivedFrom(BaseClass); + + for (CXXRecordDecl::base_class_const_iterator I = Class->bases_begin(), + E = Class->bases_end(); + I != E; ++I) { + if (I->getType()->getAsCXXRecordDecl()->getCanonicalDecl() == BaseClass) + return true; + } + + return false; +} - assert(FoundBase && "Not a direct base class of this region"); -#endif +const CXXBaseObjectRegion * +MemRegionManager::getCXXBaseObjectRegion(const CXXRecordDecl *RD, + const MemRegion *Super, + bool IsVirtual) { + if (isa<TypedValueRegion>(Super)) { + assert(isValidBaseClass(RD, dyn_cast<TypedValueRegion>(Super), IsVirtual)); + (void)isValidBaseClass; + + if (IsVirtual) { + // Virtual base regions should not be layered, since the layout rules + // are different. + while (const CXXBaseObjectRegion *Base = + dyn_cast<CXXBaseObjectRegion>(Super)) { + Super = Base->getSuperRegion(); } + assert(Super && !isa<MemSpaceRegion>(Super)); } } - return getSubRegion<CXXBaseObjectRegion>(decl, superRegion); + return getSubRegion<CXXBaseObjectRegion>(RD, IsVirtual, Super); } const CXXThisRegion* @@ -1042,7 +1067,7 @@ RegionRawOffset ElementRegion::getAsArrayOffset() const { // FIXME: generalize to symbolic offsets. SVal index = ER->getIndex(); - if (nonloc::ConcreteInt *CI = dyn_cast<nonloc::ConcreteInt>(&index)) { + if (Optional<nonloc::ConcreteInt> CI = index.getAs<nonloc::ConcreteInt>()) { // Update the offset. int64_t i = CI->getValue().getSExtValue(); @@ -1071,6 +1096,23 @@ RegionRawOffset ElementRegion::getAsArrayOffset() const { return RegionRawOffset(superR, offset); } + +/// Returns true if \p Base is an immediate base class of \p Child +static bool isImmediateBase(const CXXRecordDecl *Child, + const CXXRecordDecl *Base) { + // Note that we do NOT canonicalize the base class here, because + // ASTRecordLayout doesn't either. If that leads us down the wrong path, + // so be it; at least we won't crash. + for (CXXRecordDecl::base_class_const_iterator I = Child->bases_begin(), + E = Child->bases_end(); + I != E; ++I) { + if (I->getType()->getAsCXXRecordDecl() == Base) + return true; + } + + return false; +} + RegionOffset MemRegion::getAsOffset() const { const MemRegion *R = this; const MemRegion *SymbolicOffsetBase = 0; @@ -1078,16 +1120,37 @@ RegionOffset MemRegion::getAsOffset() const { while (1) { switch (R->getKind()) { - default: - return RegionOffset(R, RegionOffset::Symbolic); + case GenericMemSpaceRegionKind: + case StackLocalsSpaceRegionKind: + case StackArgumentsSpaceRegionKind: + case HeapSpaceRegionKind: + case UnknownSpaceRegionKind: + case StaticGlobalSpaceRegionKind: + case GlobalInternalSpaceRegionKind: + case GlobalSystemSpaceRegionKind: + case GlobalImmutableSpaceRegionKind: + // Stores can bind directly to a region space to set a default value. + assert(Offset == 0 && !SymbolicOffsetBase); + goto Finish; + + case FunctionTextRegionKind: + case BlockTextRegionKind: + case 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: + // Usual base regions. goto Finish; case ObjCIvarRegionKind: @@ -1103,6 +1166,7 @@ RegionOffset MemRegion::getAsOffset() const { 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)) { @@ -1110,6 +1174,7 @@ RegionOffset MemRegion::getAsOffset() const { // Pretend the type of the symbol is the true dynamic type. // (This will at least be self-consistent for the life of the symbol.) Ty = SR->getSymbol()->getType()->getPointeeType(); + RootIsSymbolic = true; } const CXXRecordDecl *Child = Ty->getAsCXXRecordDecl(); @@ -1118,19 +1183,30 @@ RegionOffset MemRegion::getAsOffset() const { SymbolicOffsetBase = R; } + if (RootIsSymbolic) { + // Base layers on symbolic regions may not be type-correct. + // Double-check the inheritance here, and revert to a symbolic offset + // if it's invalid (e.g. due to a reinterpret_cast). + if (BOR->isVirtual()) { + if (!Child->isVirtuallyDerivedFrom(BOR->getDecl())) + SymbolicOffsetBase = R; + } else { + if (!isImmediateBase(Child, BOR->getDecl())) + SymbolicOffsetBase = R; + } + } + // Don't bother calculating precise offsets if we already have a // symbolic offset somewhere in the chain. if (SymbolicOffsetBase) continue; - const ASTRecordLayout &Layout = getContext().getASTRecordLayout(Child); - CharUnits BaseOffset; - const CXXRecordDecl *Base = BOR->getDecl(); - if (Child->isVirtuallyDerivedFrom(Base)) - BaseOffset = Layout.getVBaseClassOffset(Base); + const ASTRecordLayout &Layout = getContext().getASTRecordLayout(Child); + if (BOR->isVirtual()) + BaseOffset = Layout.getVBaseClassOffset(BOR->getDecl()); else - BaseOffset = Layout.getBaseClassOffset(Base); + BaseOffset = Layout.getBaseClassOffset(BOR->getDecl()); // The base offset is in chars, not in bits. Offset += BaseOffset.getQuantity() * getContext().getCharWidth(); @@ -1148,7 +1224,8 @@ RegionOffset MemRegion::getAsOffset() const { } SVal Index = ER->getIndex(); - if (const nonloc::ConcreteInt *CI=dyn_cast<nonloc::ConcreteInt>(&Index)) { + if (Optional<nonloc::ConcreteInt> CI = + Index.getAs<nonloc::ConcreteInt>()) { // Don't bother calculating precise offsets if we already have a // symbolic offset somewhere in the chain. if (SymbolicOffsetBase) @@ -1207,6 +1284,29 @@ RegionOffset MemRegion::getAsOffset() const { // BlockDataRegion //===----------------------------------------------------------------------===// +std::pair<const VarRegion *, const VarRegion *> +BlockDataRegion::getCaptureRegions(const VarDecl *VD) { + MemRegionManager &MemMgr = *getMemRegionManager(); + const VarRegion *VR = 0; + const VarRegion *OriginalVR = 0; + + if (!VD->getAttr<BlocksAttr>() && VD->hasLocalStorage()) { + VR = MemMgr.getVarRegion(VD, this); + OriginalVR = MemMgr.getVarRegion(VD, LC); + } + else { + if (LC) { + VR = MemMgr.getVarRegion(VD, LC); + OriginalVR = VR; + } + else { + VR = MemMgr.getVarRegion(VD, MemMgr.getUnknownRegion()); + OriginalVR = MemMgr.getVarRegion(VD, LC); + } + } + return std::make_pair(VR, OriginalVR); +} + void BlockDataRegion::LazyInitializeReferencedVars() { if (ReferencedVars) return; @@ -1231,25 +1331,9 @@ void BlockDataRegion::LazyInitializeReferencedVars() { new (BVOriginal) VarVec(BC, E - I); for ( ; I != E; ++I) { - const VarDecl *VD = *I; const VarRegion *VR = 0; const VarRegion *OriginalVR = 0; - - if (!VD->getAttr<BlocksAttr>() && VD->hasLocalStorage()) { - VR = MemMgr.getVarRegion(VD, this); - OriginalVR = MemMgr.getVarRegion(VD, LC); - } - else { - if (LC) { - VR = MemMgr.getVarRegion(VD, LC); - OriginalVR = VR; - } - else { - VR = MemMgr.getVarRegion(VD, MemMgr.getUnknownRegion()); - OriginalVR = MemMgr.getVarRegion(VD, LC); - } - } - + llvm::tie(VR, OriginalVR) = getCaptureRegions(*I); assert(VR); assert(OriginalVR); BV->push_back(VR, BC); @@ -1293,3 +1377,13 @@ BlockDataRegion::referenced_vars_end() const { return BlockDataRegion::referenced_vars_iterator(Vec->end(), VecOriginal->end()); } + +const VarRegion *BlockDataRegion::getOriginalRegion(const VarRegion *R) const { + for (referenced_vars_iterator I = referenced_vars_begin(), + E = referenced_vars_end(); + I != E; ++I) { + if (I.getCapturedRegion() == R) + return I.getOriginalRegion(); + } + return 0; +} diff --git a/lib/StaticAnalyzer/Core/PathDiagnostic.cpp b/lib/StaticAnalyzer/Core/PathDiagnostic.cpp index 0f48d1e..7c0fb14 100644 --- a/lib/StaticAnalyzer/Core/PathDiagnostic.cpp +++ b/lib/StaticAnalyzer/Core/PathDiagnostic.cpp @@ -12,16 +12,17 @@ //===----------------------------------------------------------------------===// #include "clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h" -#include "clang/Basic/SourceManager.h" -#include "clang/AST/Expr.h" #include "clang/AST/Decl.h" #include "clang/AST/DeclCXX.h" #include "clang/AST/DeclObjC.h" +#include "clang/AST/Expr.h" #include "clang/AST/ParentMap.h" #include "clang/AST/StmtCXX.h" +#include "clang/Basic/SourceManager.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h" #include "llvm/ADT/SmallString.h" #include "llvm/ADT/StringExtras.h" +#include "llvm/Support/raw_ostream.h" using namespace clang; using namespace ento; @@ -106,12 +107,16 @@ PathDiagnostic::~PathDiagnostic() {} PathDiagnostic::PathDiagnostic(const Decl *declWithIssue, StringRef bugtype, StringRef verboseDesc, - StringRef shortDesc, StringRef category) + StringRef shortDesc, StringRef category, + PathDiagnosticLocation LocationToUnique, + const Decl *DeclToUnique) : DeclWithIssue(declWithIssue), BugType(StripTrailingDots(bugtype)), VerboseDesc(StripTrailingDots(verboseDesc)), ShortDesc(StripTrailingDots(shortDesc)), Category(StripTrailingDots(category)), + UniqueingLoc(LocationToUnique), + UniqueingDecl(DeclToUnique), path(pathImpl) {} void PathDiagnosticConsumer::anchor() { } @@ -125,7 +130,7 @@ PathDiagnosticConsumer::~PathDiagnosticConsumer() { } void PathDiagnosticConsumer::HandlePathDiagnostic(PathDiagnostic *D) { - llvm::OwningPtr<PathDiagnostic> OwningD(D); + OwningPtr<PathDiagnostic> OwningD(D); if (!D || D->path.empty()) return; @@ -141,7 +146,7 @@ void PathDiagnosticConsumer::HandlePathDiagnostic(PathDiagnostic *D) { // Verify that the entire path is from the same FileID. FileID FID; const SourceManager &SMgr = (*D->path.begin())->getLocation().getManager(); - llvm::SmallVector<const PathPieces *, 5> WorkList; + SmallVector<const PathPieces *, 5> WorkList; WorkList.push_back(&D->path); while (!WorkList.empty()) { @@ -208,9 +213,8 @@ void PathDiagnosticConsumer::HandlePathDiagnostic(PathDiagnostic *D) { Diags.InsertNode(OwningD.take()); } -static llvm::Optional<bool> comparePath(const PathPieces &X, - const PathPieces &Y); -static llvm::Optional<bool> +static Optional<bool> comparePath(const PathPieces &X, const PathPieces &Y); +static Optional<bool> compareControlFlow(const PathDiagnosticControlFlowPiece &X, const PathDiagnosticControlFlowPiece &Y) { FullSourceLoc XSL = X.getStartLocation().asLocation(); @@ -221,18 +225,16 @@ compareControlFlow(const PathDiagnosticControlFlowPiece &X, FullSourceLoc YEL = Y.getEndLocation().asLocation(); if (XEL != YEL) return XEL.isBeforeInTranslationUnitThan(YEL); - return llvm::Optional<bool>(); + return None; } -static llvm::Optional<bool> -compareMacro(const PathDiagnosticMacroPiece &X, - const PathDiagnosticMacroPiece &Y) { +static Optional<bool> compareMacro(const PathDiagnosticMacroPiece &X, + const PathDiagnosticMacroPiece &Y) { return comparePath(X.subPieces, Y.subPieces); } -static llvm::Optional<bool> -compareCall(const PathDiagnosticCallPiece &X, - const PathDiagnosticCallPiece &Y) { +static Optional<bool> compareCall(const PathDiagnosticCallPiece &X, + const PathDiagnosticCallPiece &Y) { FullSourceLoc X_CEL = X.callEnter.asLocation(); FullSourceLoc Y_CEL = Y.callEnter.asLocation(); if (X_CEL != Y_CEL) @@ -248,8 +250,8 @@ compareCall(const PathDiagnosticCallPiece &X, return comparePath(X.path, Y.path); } -static llvm::Optional<bool> comparePiece(const PathDiagnosticPiece &X, - const PathDiagnosticPiece &Y) { +static Optional<bool> comparePiece(const PathDiagnosticPiece &X, + const PathDiagnosticPiece &Y) { if (X.getKind() != Y.getKind()) return X.getKind() < Y.getKind(); @@ -281,7 +283,7 @@ static llvm::Optional<bool> comparePiece(const PathDiagnosticPiece &X, return compareControlFlow(cast<PathDiagnosticControlFlowPiece>(X), cast<PathDiagnosticControlFlowPiece>(Y)); case clang::ento::PathDiagnosticPiece::Event: - return llvm::Optional<bool>(); + return None; case clang::ento::PathDiagnosticPiece::Macro: return compareMacro(cast<PathDiagnosticMacroPiece>(X), cast<PathDiagnosticMacroPiece>(Y)); @@ -292,16 +294,15 @@ static llvm::Optional<bool> comparePiece(const PathDiagnosticPiece &X, llvm_unreachable("all cases handled"); } -static llvm::Optional<bool> comparePath(const PathPieces &X, - const PathPieces &Y) { +static Optional<bool> comparePath(const PathPieces &X, const PathPieces &Y) { if (X.size() != Y.size()) return X.size() < Y.size(); for (unsigned i = 0, n = X.size(); i != n; ++i) { - llvm::Optional<bool> b = comparePiece(*X[i], *Y[i]); + Optional<bool> b = comparePiece(*X[i], *Y[i]); if (b.hasValue()) return b.getValue(); } - return llvm::Optional<bool>(); + return None; } static bool compare(const PathDiagnostic &X, const PathDiagnostic &Y) { @@ -339,7 +340,7 @@ static bool compare(const PathDiagnostic &X, const PathDiagnostic &Y) { if (*XI != *YI) return (*XI) < (*YI); } - llvm::Optional<bool> b = comparePath(X.path, Y.path); + Optional<bool> b = comparePath(X.path, Y.path); assert(b.hasValue()); return b.getValue(); } @@ -475,18 +476,16 @@ getLocationForCaller(const StackFrameContext *SFC, CFGElement Source = Block[SFC->getIndex()]; switch (Source.getKind()) { - case CFGElement::Invalid: - llvm_unreachable("Invalid CFGElement"); case CFGElement::Statement: - return PathDiagnosticLocation(cast<CFGStmt>(Source).getStmt(), + return PathDiagnosticLocation(Source.castAs<CFGStmt>().getStmt(), SM, CallerCtx); case CFGElement::Initializer: { - const CFGInitializer &Init = cast<CFGInitializer>(Source); + const CFGInitializer &Init = Source.castAs<CFGInitializer>(); return PathDiagnosticLocation(Init.getInitializer()->getInit(), SM, CallerCtx); } case CFGElement::AutomaticObjectDtor: { - const CFGAutomaticObjDtor &Dtor = cast<CFGAutomaticObjDtor>(Source); + const CFGAutomaticObjDtor &Dtor = Source.castAs<CFGAutomaticObjDtor>(); return PathDiagnosticLocation::createEnd(Dtor.getTriggerStmt(), SM, CallerCtx); } @@ -582,27 +581,27 @@ PathDiagnosticLocation const SourceManager &SMng) { const Stmt* S = 0; - if (const BlockEdge *BE = dyn_cast<BlockEdge>(&P)) { + if (Optional<BlockEdge> BE = P.getAs<BlockEdge>()) { const CFGBlock *BSrc = BE->getSrc(); S = BSrc->getTerminatorCondition(); - } - else if (const StmtPoint *SP = dyn_cast<StmtPoint>(&P)) { + } else if (Optional<StmtPoint> SP = P.getAs<StmtPoint>()) { S = SP->getStmt(); - } - else if (const PostImplicitCall *PIE = dyn_cast<PostImplicitCall>(&P)) { + if (P.getAs<PostStmtPurgeDeadSymbols>()) + return PathDiagnosticLocation::createEnd(S, SMng, P.getLocationContext()); + } else if (Optional<PostInitializer> PIP = P.getAs<PostInitializer>()) { + return PathDiagnosticLocation(PIP->getInitializer()->getSourceLocation(), + SMng); + } else if (Optional<PostImplicitCall> PIE = P.getAs<PostImplicitCall>()) { return PathDiagnosticLocation(PIE->getLocation(), SMng); - } - else if (const CallEnter *CE = dyn_cast<CallEnter>(&P)) { + } else if (Optional<CallEnter> CE = P.getAs<CallEnter>()) { return getLocationForCaller(CE->getCalleeContext(), CE->getLocationContext(), SMng); - } - else if (const CallExitEnd *CEE = dyn_cast<CallExitEnd>(&P)) { + } else if (Optional<CallExitEnd> CEE = P.getAs<CallExitEnd>()) { return getLocationForCaller(CEE->getCalleeContext(), CEE->getLocationContext(), SMng); - } - else { + } else { llvm_unreachable("Unexpected ProgramPoint"); } @@ -619,12 +618,16 @@ PathDiagnosticLocation while (NI) { ProgramPoint P = NI->getLocation(); - if (const StmtPoint *PS = dyn_cast<StmtPoint>(&P)) + if (Optional<StmtPoint> PS = P.getAs<StmtPoint>()) { S = PS->getStmt(); - else if (const BlockEdge *BE = dyn_cast<BlockEdge>(&P)) + if (P.getAs<PostStmtPurgeDeadSymbols>()) + return PathDiagnosticLocation::createEnd(S, SM, + NI->getLocationContext()); + break; + } else if (Optional<BlockEdge> BE = P.getAs<BlockEdge>()) { S = BE->getSrc()->getTerminator(); - if (S) break; + } NI = NI->succ_empty() ? 0 : *(NI->succ_begin()); } @@ -777,48 +780,129 @@ void PathDiagnosticCallPiece::setCallee(const CallEnter &CE, callEnter = getLocationForCaller(CalleeCtx, CE.getLocationContext(), SM); } +static inline void describeClass(raw_ostream &Out, const CXXRecordDecl *D, + StringRef Prefix = StringRef()) { + if (!D->getIdentifier()) + return; + Out << Prefix << '\'' << *D << '\''; +} + +static bool describeCodeDecl(raw_ostream &Out, const Decl *D, + bool ExtendedDescription, + StringRef Prefix = StringRef()) { + if (!D) + return false; + + if (isa<BlockDecl>(D)) { + if (ExtendedDescription) + Out << Prefix << "anonymous block"; + return ExtendedDescription; + } + + if (const CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(D)) { + Out << Prefix; + if (ExtendedDescription && !MD->isUserProvided()) { + if (MD->isExplicitlyDefaulted()) + Out << "defaulted "; + else + Out << "implicit "; + } + + if (const CXXConstructorDecl *CD = dyn_cast<CXXConstructorDecl>(MD)) { + if (CD->isDefaultConstructor()) + Out << "default "; + else if (CD->isCopyConstructor()) + Out << "copy "; + else if (CD->isMoveConstructor()) + Out << "move "; + + Out << "constructor"; + describeClass(Out, MD->getParent(), " for "); + + } else if (isa<CXXDestructorDecl>(MD)) { + if (!MD->isUserProvided()) { + Out << "destructor"; + describeClass(Out, MD->getParent(), " for "); + } else { + // Use ~Foo for explicitly-written destructors. + Out << "'" << *MD << "'"; + } + + } else if (MD->isCopyAssignmentOperator()) { + Out << "copy assignment operator"; + describeClass(Out, MD->getParent(), " for "); + + } else if (MD->isMoveAssignmentOperator()) { + Out << "move assignment operator"; + describeClass(Out, MD->getParent(), " for "); + + } else { + if (MD->getParent()->getIdentifier()) + Out << "'" << *MD->getParent() << "::" << *MD << "'"; + else + Out << "'" << *MD << "'"; + } + + return true; + } + + Out << Prefix << '\'' << cast<NamedDecl>(*D) << '\''; + return true; +} + IntrusiveRefCntPtr<PathDiagnosticEventPiece> PathDiagnosticCallPiece::getCallEnterEvent() const { if (!Callee) return 0; + SmallString<256> buf; llvm::raw_svector_ostream Out(buf); - if (isa<BlockDecl>(Callee)) - Out << "Calling anonymous block"; - else if (const NamedDecl *ND = dyn_cast<NamedDecl>(Callee)) - Out << "Calling '" << *ND << "'"; - StringRef msg = Out.str(); - if (msg.empty()) - return 0; - return new PathDiagnosticEventPiece(callEnter, msg); + + Out << "Calling "; + describeCodeDecl(Out, Callee, /*ExtendedDescription=*/true); + + assert(callEnter.asLocation().isValid()); + return new PathDiagnosticEventPiece(callEnter, Out.str()); } IntrusiveRefCntPtr<PathDiagnosticEventPiece> PathDiagnosticCallPiece::getCallEnterWithinCallerEvent() const { + if (!callEnterWithin.asLocation().isValid()) + return 0; + if (Callee->isImplicit()) + return 0; + if (const CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(Callee)) + if (MD->isDefaulted()) + return 0; + SmallString<256> buf; llvm::raw_svector_ostream Out(buf); - if (const NamedDecl *ND = dyn_cast_or_null<NamedDecl>(Caller)) - Out << "Entered call from '" << *ND << "'"; - else - Out << "Entered call"; - StringRef msg = Out.str(); - if (msg.empty()) - return 0; - return new PathDiagnosticEventPiece(callEnterWithin, msg); + + Out << "Entered call"; + describeCodeDecl(Out, Caller, /*ExtendedDescription=*/false, " from "); + + return new PathDiagnosticEventPiece(callEnterWithin, Out.str()); } IntrusiveRefCntPtr<PathDiagnosticEventPiece> PathDiagnosticCallPiece::getCallExitEvent() const { if (NoExit) return 0; + SmallString<256> buf; llvm::raw_svector_ostream Out(buf); - if (!CallStackMessage.empty()) + + if (!CallStackMessage.empty()) { Out << CallStackMessage; - else if (const NamedDecl *ND = dyn_cast_or_null<NamedDecl>(Callee)) - Out << "Returning from '" << *ND << "'"; - else - Out << "Returning to caller"; + } else { + bool DidDescribe = describeCodeDecl(Out, Callee, + /*ExtendedDescription=*/false, + "Returning from "); + if (!DidDescribe) + Out << "Returning to caller"; + } + + assert(callReturn.asLocation().isValid()); return new PathDiagnosticEventPiece(callReturn, Out.str()); } @@ -910,11 +994,10 @@ StackHintGenerator::~StackHintGenerator() {} std::string StackHintGeneratorForSymbol::getMessage(const ExplodedNode *N){ ProgramPoint P = N->getLocation(); - const CallExitEnd *CExit = dyn_cast<CallExitEnd>(&P); - assert(CExit && "Stack Hints should be constructed at CallExitEnd points."); + CallExitEnd CExit = P.castAs<CallExitEnd>(); // FIXME: Use CallEvent to abstract this over all calls. - const Stmt *CallSite = CExit->getCalleeContext()->getCallSite(); + const Stmt *CallSite = CExit.getCalleeContext()->getCallSite(); const CallExpr *CE = dyn_cast_or_null<CallExpr>(CallSite); if (!CE) return ""; @@ -937,7 +1020,7 @@ std::string StackHintGeneratorForSymbol::getMessage(const ExplodedNode *N){ } // Check if the parameter is a pointer to the symbol. - if (const loc::MemRegionVal *Reg = dyn_cast<loc::MemRegionVal>(&SV)) { + if (Optional<loc::MemRegionVal> Reg = SV.getAs<loc::MemRegionVal>()) { SVal PSV = State->getSVal(Reg->getRegion()); SymbolRef AS = PSV.getAsLocSymbol(); if (AS == Sym) { diff --git a/lib/StaticAnalyzer/Core/PlistDiagnostics.cpp b/lib/StaticAnalyzer/Core/PlistDiagnostics.cpp index 17ef4cf..7dcc088 100644 --- a/lib/StaticAnalyzer/Core/PlistDiagnostics.cpp +++ b/lib/StaticAnalyzer/Core/PlistDiagnostics.cpp @@ -11,16 +11,17 @@ // //===----------------------------------------------------------------------===// -#include "clang/StaticAnalyzer/Core/PathDiagnosticConsumers.h" -#include "clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h" +#include "clang/StaticAnalyzer/Core/AnalyzerOptions.h" #include "clang/Basic/FileManager.h" #include "clang/Basic/SourceManager.h" #include "clang/Basic/Version.h" #include "clang/Lex/Preprocessor.h" -#include "llvm/Support/raw_ostream.h" -#include "llvm/Support/Casting.h" +#include "clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h" +#include "clang/StaticAnalyzer/Core/PathDiagnosticConsumers.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/SmallVector.h" +#include "llvm/Support/Casting.h" +#include "llvm/Support/raw_ostream.h" using namespace clang; using namespace ento; @@ -33,7 +34,9 @@ namespace { const LangOptions &LangOpts; const bool SupportsCrossFileDiagnostics; public: - PlistDiagnostics(const std::string& prefix, const LangOptions &LangOpts, + PlistDiagnostics(AnalyzerOptions &AnalyzerOpts, + const std::string& prefix, + const LangOptions &LangOpts, bool supportsMultipleFiles); virtual ~PlistDiagnostics() {} @@ -54,22 +57,28 @@ namespace { }; } // end anonymous namespace -PlistDiagnostics::PlistDiagnostics(const std::string& output, +PlistDiagnostics::PlistDiagnostics(AnalyzerOptions &AnalyzerOpts, + const std::string& output, const LangOptions &LO, bool supportsMultipleFiles) - : OutputFile(output), LangOpts(LO), + : OutputFile(output), + LangOpts(LO), SupportsCrossFileDiagnostics(supportsMultipleFiles) {} -void ento::createPlistDiagnosticConsumer(PathDiagnosticConsumers &C, +void ento::createPlistDiagnosticConsumer(AnalyzerOptions &AnalyzerOpts, + PathDiagnosticConsumers &C, const std::string& s, const Preprocessor &PP) { - C.push_back(new PlistDiagnostics(s, PP.getLangOpts(), false)); + C.push_back(new PlistDiagnostics(AnalyzerOpts, s, + PP.getLangOpts(), false)); } -void ento::createPlistMultiFileDiagnosticConsumer(PathDiagnosticConsumers &C, +void ento::createPlistMultiFileDiagnosticConsumer(AnalyzerOptions &AnalyzerOpts, + PathDiagnosticConsumers &C, const std::string &s, const Preprocessor &PP) { - C.push_back(new PlistDiagnostics(s, PP.getLangOpts(), true)); + C.push_back(new PlistDiagnostics(AnalyzerOpts, s, + PP.getLangOpts(), true)); } static void AddFID(FIDMap &FIDs, SmallVectorImpl<FileID> &V, @@ -360,7 +369,7 @@ void PlistDiagnostics::FlushDiagnosticsImpl( const PathDiagnostic *D = *DI; - llvm::SmallVector<const PathPieces *, 5> WorkList; + SmallVector<const PathPieces *, 5> WorkList; WorkList.push_back(&D->path); while (!WorkList.empty()) { @@ -486,12 +495,32 @@ void PlistDiagnostics::FlushDiagnosticsImpl( // Output the bug hash for issue unique-ing. Currently, it's just an // offset from the beginning of the function. if (const Stmt *Body = DeclWithIssue->getBody()) { - FullSourceLoc Loc(SM->getExpansionLoc(D->getLocation().asLocation()), + + // If the bug uniqueing location exists, use it for the hash. + // For example, this ensures that two leaks reported on the same line + // will have different issue_hashes and that the hash will identify + // the leak location even after code is added between the allocation + // site and the end of scope (leak report location). + PathDiagnosticLocation UPDLoc = D->getUniqueingLoc(); + if (UPDLoc.isValid()) { + FullSourceLoc UL(SM->getExpansionLoc(UPDLoc.asLocation()), + *SM); + FullSourceLoc UFunL(SM->getExpansionLoc( + D->getUniqueingDecl()->getBody()->getLocStart()), *SM); + o << " <key>issue_hash</key><string>" + << UL.getExpansionLineNumber() - UFunL.getExpansionLineNumber() + << "</string>\n"; + + // Otherwise, use the location on which the bug is reported. + } else { + FullSourceLoc L(SM->getExpansionLoc(D->getLocation().asLocation()), *SM); - FullSourceLoc FunLoc(SM->getExpansionLoc(Body->getLocStart()), *SM); - o << " <key>issue_hash</key><integer>" - << Loc.getExpansionLineNumber() - FunLoc.getExpansionLineNumber() - << "</integer>\n"; + FullSourceLoc FunL(SM->getExpansionLoc(Body->getLocStart()), *SM); + o << " <key>issue_hash</key><string>" + << L.getExpansionLineNumber() - FunL.getExpansionLineNumber() + << "</string>\n"; + } + } } } diff --git a/lib/StaticAnalyzer/Core/ProgramState.cpp b/lib/StaticAnalyzer/Core/ProgramState.cpp index b49a11e..bff2242 100644 --- a/lib/StaticAnalyzer/Core/ProgramState.cpp +++ b/lib/StaticAnalyzer/Core/ProgramState.cpp @@ -11,10 +11,10 @@ // //===----------------------------------------------------------------------===// +#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" #include "clang/Analysis/CFG.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" #include "clang/StaticAnalyzer/Core/PathSensitive/SubEngine.h" #include "clang/StaticAnalyzer/Core/PathSensitive/TaintManager.h" #include "llvm/Support/raw_ostream.h" @@ -132,7 +132,7 @@ ProgramStateRef ProgramState::bindLoc(Loc LV, SVal V, bool notifyChanges) const ProgramStateRef ProgramState::bindDefault(SVal loc, SVal V) const { ProgramStateManager &Mgr = getStateManager(); - const MemRegion *R = cast<loc::MemRegionVal>(loc).getRegion(); + const MemRegion *R = loc.castAs<loc::MemRegionVal>().getRegion(); const StoreRef &newStore = Mgr.StoreMgr->BindDefault(getStore(), R, V); ProgramStateRef new_state = makeWithStore(newStore); return Mgr.getOwningEngine() ? @@ -140,46 +140,108 @@ ProgramStateRef ProgramState::bindDefault(SVal loc, SVal V) const { new_state; } +typedef ArrayRef<const MemRegion *> RegionList; +typedef ArrayRef<SVal> ValueList; + ProgramStateRef -ProgramState::invalidateRegions(ArrayRef<const MemRegion *> Regions, +ProgramState::invalidateRegions(RegionList Regions, const Expr *E, unsigned Count, const LocationContext *LCtx, - StoreManager::InvalidatedSymbols *IS, - const CallEvent *Call) const { + bool CausedByPointerEscape, + InvalidatedSymbols *IS, + const CallEvent *Call, + RegionList ConstRegions) const { + SmallVector<SVal, 8> Values; + for (RegionList::const_iterator I = Regions.begin(), + End = Regions.end(); I != End; ++I) + Values.push_back(loc::MemRegionVal(*I)); + + SmallVector<SVal, 8> ConstValues; + for (RegionList::const_iterator I = ConstRegions.begin(), + End = ConstRegions.end(); I != End; ++I) + ConstValues.push_back(loc::MemRegionVal(*I)); + if (!IS) { - StoreManager::InvalidatedSymbols invalidated; - return invalidateRegionsImpl(Regions, E, Count, LCtx, - invalidated, Call); + InvalidatedSymbols invalidated; + return invalidateRegionsImpl(Values, E, Count, LCtx, + CausedByPointerEscape, + invalidated, Call, ConstValues); } - return invalidateRegionsImpl(Regions, E, Count, LCtx, *IS, Call); + return invalidateRegionsImpl(Values, E, Count, LCtx, CausedByPointerEscape, + *IS, Call, ConstValues); } -ProgramStateRef -ProgramState::invalidateRegionsImpl(ArrayRef<const MemRegion *> Regions, +ProgramStateRef +ProgramState::invalidateRegions(ValueList Values, + const Expr *E, unsigned Count, + const LocationContext *LCtx, + bool CausedByPointerEscape, + InvalidatedSymbols *IS, + const CallEvent *Call, + ValueList ConstValues) const { + if (!IS) { + InvalidatedSymbols invalidated; + return invalidateRegionsImpl(Values, E, Count, LCtx, + CausedByPointerEscape, + invalidated, Call, ConstValues); + } + return invalidateRegionsImpl(Values, E, Count, LCtx, CausedByPointerEscape, + *IS, Call, ConstValues); +} + +ProgramStateRef +ProgramState::invalidateRegionsImpl(ValueList Values, const Expr *E, unsigned Count, const LocationContext *LCtx, - StoreManager::InvalidatedSymbols &IS, - const CallEvent *Call) const { + bool CausedByPointerEscape, + InvalidatedSymbols &IS, + const CallEvent *Call, + ValueList ConstValues) const { ProgramStateManager &Mgr = getStateManager(); SubEngine* Eng = Mgr.getOwningEngine(); - - if (Eng && Eng->wantsRegionChangeUpdate(this)) { + InvalidatedSymbols ConstIS; + + if (Eng) { + StoreManager::InvalidatedRegions TopLevelInvalidated; + StoreManager::InvalidatedRegions TopLevelConstInvalidated; StoreManager::InvalidatedRegions Invalidated; const StoreRef &newStore - = Mgr.StoreMgr->invalidateRegions(getStore(), Regions, E, Count, LCtx, IS, - Call, &Invalidated); + = Mgr.StoreMgr->invalidateRegions(getStore(), Values, ConstValues, + E, Count, LCtx, Call, + IS, ConstIS, + &TopLevelInvalidated, + &TopLevelConstInvalidated, + &Invalidated); + ProgramStateRef newState = makeWithStore(newStore); - return Eng->processRegionChanges(newState, &IS, Regions, Invalidated, Call); + + if (CausedByPointerEscape) { + newState = Eng->notifyCheckersOfPointerEscape(newState, &IS, + TopLevelInvalidated, + Invalidated, Call); + if (!ConstValues.empty()) { + StoreManager::InvalidatedRegions Empty; + newState = Eng->notifyCheckersOfPointerEscape(newState, &ConstIS, + TopLevelConstInvalidated, + Empty, Call, + true); + } + } + + return Eng->processRegionChanges(newState, &IS, + TopLevelInvalidated, Invalidated, + Call); } const StoreRef &newStore = - Mgr.StoreMgr->invalidateRegions(getStore(), Regions, E, Count, LCtx, IS, - Call, NULL); + Mgr.StoreMgr->invalidateRegions(getStore(), Values, ConstValues, + E, Count, LCtx, Call, + IS, ConstIS, NULL, NULL, NULL); return makeWithStore(newStore); } ProgramStateRef ProgramState::killBinding(Loc LV) const { - assert(!isa<loc::MemRegionVal>(LV) && "Use invalidateRegion instead."); + assert(!LV.getAs<loc::MemRegionVal>() && "Use invalidateRegion instead."); Store OldStore = getStore(); const StoreRef &newStore = @@ -243,7 +305,7 @@ SVal ProgramState::getSVal(Loc location, QualType T) const { // not unsigned. const llvm::APSInt &NewV = getBasicVals().Convert(T, *Int); - if (isa<Loc>(V)) + if (V.getAs<Loc>()) return loc::ConcreteInt(NewV); else return nonloc::ConcreteInt(NewV); @@ -268,23 +330,6 @@ ProgramStateRef ProgramState::BindExpr(const Stmt *S, return getStateManager().getPersistentState(NewSt); } -ProgramStateRef -ProgramState::bindExprAndLocation(const Stmt *S, const LocationContext *LCtx, - SVal location, - SVal V) const { - Environment NewEnv = - getStateManager().EnvMgr.bindExprAndLocation(Env, - EnvironmentEntry(S, LCtx), - location, V); - - if (NewEnv == Env) - return this; - - ProgramState NewSt = *this; - NewSt.Env = NewEnv; - return getStateManager().getPersistentState(NewSt); -} - ProgramStateRef ProgramState::assumeInBound(DefinedOrUnknownSVal Idx, DefinedOrUnknownSVal UpperBound, bool Assumption, @@ -308,28 +353,41 @@ ProgramStateRef ProgramState::assumeInBound(DefinedOrUnknownSVal Idx, // Adjust the index. SVal newIdx = svalBuilder.evalBinOpNN(this, BO_Add, - cast<NonLoc>(Idx), Min, indexTy); + Idx.castAs<NonLoc>(), Min, indexTy); if (newIdx.isUnknownOrUndef()) return this; // Adjust the upper bound. SVal newBound = - svalBuilder.evalBinOpNN(this, BO_Add, cast<NonLoc>(UpperBound), + svalBuilder.evalBinOpNN(this, BO_Add, UpperBound.castAs<NonLoc>(), Min, indexTy); if (newBound.isUnknownOrUndef()) return this; // Build the actual comparison. - SVal inBound = svalBuilder.evalBinOpNN(this, BO_LT, - cast<NonLoc>(newIdx), cast<NonLoc>(newBound), - Ctx.IntTy); + SVal inBound = svalBuilder.evalBinOpNN(this, BO_LT, newIdx.castAs<NonLoc>(), + newBound.castAs<NonLoc>(), Ctx.IntTy); if (inBound.isUnknownOrUndef()) return this; // Finally, let the constraint manager take care of it. ConstraintManager &CM = SM.getConstraintManager(); - return CM.assume(this, cast<DefinedSVal>(inBound), Assumption); + return CM.assume(this, inBound.castAs<DefinedSVal>(), Assumption); +} + +ConditionTruthVal ProgramState::isNull(SVal V) const { + if (V.isZeroConstant()) + return true; + + if (V.isConstant()) + return false; + + SymbolRef Sym = V.getAsSymbol(); + if (!Sym) + return ConditionTruthVal(); + + return getStateManager().ConstraintMgr->isNull(this, Sym); } ProgramStateRef ProgramStateManager::getInitialState(const LocationContext *InitLoc) { @@ -516,13 +574,22 @@ bool ScanReachableSymbols::scan(const SymExpr *sym) { } bool ScanReachableSymbols::scan(SVal val) { - if (loc::MemRegionVal *X = dyn_cast<loc::MemRegionVal>(&val)) + if (Optional<loc::MemRegionVal> X = val.getAs<loc::MemRegionVal>()) return scan(X->getRegion()); - if (nonloc::LazyCompoundVal *X = dyn_cast<nonloc::LazyCompoundVal>(&val)) - return scan(X->getRegion()); + if (Optional<nonloc::LazyCompoundVal> X = + val.getAs<nonloc::LazyCompoundVal>()) { + StoreManager &StoreMgr = state->getStateManager().getStoreManager(); + // FIXME: We don't really want to use getBaseRegion() here because pointer + // arithmetic doesn't apply, but scanReachableSymbols only accepts base + // regions right now. + if (!StoreMgr.scanReachableSymbols(X->getStore(), + X->getRegion()->getBaseRegion(), + *this)) + return false; + } - if (nonloc::LocAsInteger *X = dyn_cast<nonloc::LocAsInteger>(&val)) + if (Optional<nonloc::LocAsInteger> X = val.getAs<nonloc::LocAsInteger>()) return scan(X->getLoc()); if (SymbolRef Sym = val.getAsSymbol()) @@ -531,7 +598,7 @@ bool ScanReachableSymbols::scan(SVal val) { if (const SymExpr *Sym = val.getAsSymbolicExpression()) return scan(Sym); - if (nonloc::CompoundVal *X = dyn_cast<nonloc::CompoundVal>(&val)) + if (Optional<nonloc::CompoundVal> X = val.getAs<nonloc::CompoundVal>()) return scan(*X); return true; diff --git a/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp b/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp index 411094b..3606e09 100644 --- a/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp +++ b/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp @@ -16,9 +16,9 @@ #include "clang/StaticAnalyzer/Core/PathSensitive/APSIntType.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h" -#include "llvm/Support/Debug.h" #include "llvm/ADT/FoldingSet.h" #include "llvm/ADT/ImmutableSet.h" +#include "llvm/Support/Debug.h" #include "llvm/Support/raw_ostream.h" using namespace clang; @@ -153,8 +153,8 @@ private: // 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); - APSIntType::RangeTestResultKind UpperTest = Type.testInRange(Upper); + APSIntType::RangeTestResultKind LowerTest = Type.testInRange(Lower, true); + APSIntType::RangeTestResultKind UpperTest = Type.testInRange(Upper, true); switch (LowerTest) { case APSIntType::RTR_Below: @@ -285,8 +285,8 @@ namespace { class RangeConstraintManager : public SimpleConstraintManager{ RangeSet GetRange(ProgramStateRef state, SymbolRef sym); public: - RangeConstraintManager(SubEngine *subengine, BasicValueFactory &BVF) - : SimpleConstraintManager(subengine, BVF) {} + RangeConstraintManager(SubEngine *subengine, SValBuilder &SVB) + : SimpleConstraintManager(subengine, SVB) {} ProgramStateRef assumeSymNE(ProgramStateRef state, SymbolRef sym, const llvm::APSInt& Int, @@ -328,7 +328,7 @@ private: ConstraintManager * ento::CreateRangeConstraintManager(ProgramStateManager &StMgr, SubEngine *Eng) { - return new RangeConstraintManager(Eng, StMgr.getBasicVals()); + return new RangeConstraintManager(Eng, StMgr.getSValBuilder()); } const llvm::APSInt* RangeConstraintManager::getSymVal(ProgramStateRef St, @@ -419,7 +419,7 @@ RangeConstraintManager::assumeSymNE(ProgramStateRef St, SymbolRef Sym, const llvm::APSInt &Adjustment) { // Before we do any real work, see if the value can even show up. APSIntType AdjustmentType(Adjustment); - if (AdjustmentType.testInRange(Int) != APSIntType::RTR_Within) + if (AdjustmentType.testInRange(Int, true) != APSIntType::RTR_Within) return St; llvm::APSInt Lower = AdjustmentType.convert(Int) - Adjustment; @@ -439,7 +439,7 @@ RangeConstraintManager::assumeSymEQ(ProgramStateRef St, SymbolRef Sym, const llvm::APSInt &Adjustment) { // Before we do any real work, see if the value can even show up. APSIntType AdjustmentType(Adjustment); - if (AdjustmentType.testInRange(Int) != APSIntType::RTR_Within) + if (AdjustmentType.testInRange(Int, true) != APSIntType::RTR_Within) return NULL; // [Int-Adjustment, Int-Adjustment] @@ -454,7 +454,7 @@ RangeConstraintManager::assumeSymLT(ProgramStateRef St, SymbolRef Sym, const llvm::APSInt &Adjustment) { // Before we do any real work, see if the value can even show up. APSIntType AdjustmentType(Adjustment); - switch (AdjustmentType.testInRange(Int)) { + switch (AdjustmentType.testInRange(Int, true)) { case APSIntType::RTR_Below: return NULL; case APSIntType::RTR_Within: @@ -483,7 +483,7 @@ RangeConstraintManager::assumeSymGT(ProgramStateRef St, SymbolRef Sym, const llvm::APSInt &Adjustment) { // Before we do any real work, see if the value can even show up. APSIntType AdjustmentType(Adjustment); - switch (AdjustmentType.testInRange(Int)) { + switch (AdjustmentType.testInRange(Int, true)) { case APSIntType::RTR_Below: return St; case APSIntType::RTR_Within: @@ -512,7 +512,7 @@ RangeConstraintManager::assumeSymGE(ProgramStateRef St, SymbolRef Sym, const llvm::APSInt &Adjustment) { // Before we do any real work, see if the value can even show up. APSIntType AdjustmentType(Adjustment); - switch (AdjustmentType.testInRange(Int)) { + switch (AdjustmentType.testInRange(Int, true)) { case APSIntType::RTR_Below: return St; case APSIntType::RTR_Within: @@ -541,7 +541,7 @@ RangeConstraintManager::assumeSymLE(ProgramStateRef St, SymbolRef Sym, const llvm::APSInt &Adjustment) { // Before we do any real work, see if the value can even show up. APSIntType AdjustmentType(Adjustment); - switch (AdjustmentType.testInRange(Int)) { + switch (AdjustmentType.testInRange(Int, true)) { case APSIntType::RTR_Below: return NULL; case APSIntType::RTR_Within: diff --git a/lib/StaticAnalyzer/Core/RegionStore.cpp b/lib/StaticAnalyzer/Core/RegionStore.cpp index aed994d..0f4a682 100644 --- a/lib/StaticAnalyzer/Core/RegionStore.cpp +++ b/lib/StaticAnalyzer/Core/RegionStore.cpp @@ -14,14 +14,15 @@ // parameters are created lazily. // //===----------------------------------------------------------------------===// +#include "clang/AST/Attr.h" #include "clang/AST/CharUnits.h" #include "clang/Analysis/Analyses/LiveVariables.h" #include "clang/Analysis/AnalysisContext.h" #include "clang/Basic/TargetInfo.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/ProgramStateTrait.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h" #include "llvm/ADT/ImmutableList.h" #include "llvm/ADT/ImmutableMap.h" #include "llvm/ADT/Optional.h" @@ -29,7 +30,6 @@ using namespace clang; using namespace ento; -using llvm::Optional; //===----------------------------------------------------------------------===// // Representation of binding keys. @@ -45,11 +45,15 @@ private: llvm::PointerIntPair<const MemRegion *, 2> P; uint64_t Data; - explicit BindingKey(const MemRegion *r, const MemRegion *Base, Kind k) + /// Create a key for a binding to region \p r, which has a symbolic offset + /// from region \p Base. + explicit BindingKey(const SubRegion *r, const SubRegion *Base, Kind k) : P(r, k | Symbolic), Data(reinterpret_cast<uintptr_t>(Base)) { assert(r && Base && "Must have known regions."); assert(getConcreteOffsetRegion() == Base && "Failed to store base region"); } + + /// Create a key for a binding at \p offset from base region \p r. explicit BindingKey(const MemRegion *r, uint64_t offset, Kind k) : P(r, k), Data(offset) { assert(r && "Must have known regions."); @@ -67,9 +71,9 @@ public: return Data; } - const MemRegion *getConcreteOffsetRegion() const { + const SubRegion *getConcreteOffsetRegion() const { assert(hasSymbolicOffset()); - return reinterpret_cast<const MemRegion *>(static_cast<uintptr_t>(Data)); + return reinterpret_cast<const SubRegion *>(static_cast<uintptr_t>(Data)); } const MemRegion *getBaseRegion() const { @@ -105,7 +109,7 @@ public: BindingKey BindingKey::Make(const MemRegion *R, Kind k) { const RegionOffset &RO = R->getAsOffset(); if (RO.hasSymbolicOffset()) - return BindingKey(R, RO.getRegion(), k); + return BindingKey(cast<SubRegion>(R), cast<SubRegion>(RO.getRegion()), k); return BindingKey(RO.getRegion(), RO.getOffset(), k); } @@ -120,6 +124,11 @@ namespace llvm { << ')'; return os; } + + template <typename T> struct isPodLike; + template <> struct isPodLike<BindingKey> { + static const bool value = true; + }; } // end llvm namespace void BindingKey::dump() const { @@ -130,8 +139,156 @@ void BindingKey::dump() const { // Actual Store type. //===----------------------------------------------------------------------===// -typedef llvm::ImmutableMap<BindingKey, SVal> ClusterBindings; -typedef llvm::ImmutableMap<const MemRegion *, ClusterBindings> RegionBindings; +typedef llvm::ImmutableMap<BindingKey, SVal> ClusterBindings; +typedef llvm::ImmutableMapRef<BindingKey, SVal> ClusterBindingsRef; +typedef std::pair<BindingKey, SVal> BindingPair; + +typedef llvm::ImmutableMap<const MemRegion *, ClusterBindings> + RegionBindings; + +namespace { +class RegionBindingsRef : public llvm::ImmutableMapRef<const MemRegion *, + ClusterBindings> { + ClusterBindings::Factory &CBFactory; +public: + typedef llvm::ImmutableMapRef<const MemRegion *, ClusterBindings> + ParentTy; + + RegionBindingsRef(ClusterBindings::Factory &CBFactory, + const RegionBindings::TreeTy *T, + RegionBindings::TreeTy::Factory *F) + : llvm::ImmutableMapRef<const MemRegion *, ClusterBindings>(T, F), + CBFactory(CBFactory) {} + + RegionBindingsRef(const ParentTy &P, ClusterBindings::Factory &CBFactory) + : llvm::ImmutableMapRef<const MemRegion *, ClusterBindings>(P), + CBFactory(CBFactory) {} + + RegionBindingsRef add(key_type_ref K, data_type_ref D) const { + return RegionBindingsRef(static_cast<const ParentTy*>(this)->add(K, D), + CBFactory); + } + + RegionBindingsRef remove(key_type_ref K) const { + return RegionBindingsRef(static_cast<const ParentTy*>(this)->remove(K), + CBFactory); + } + + RegionBindingsRef addBinding(BindingKey K, SVal V) const; + + RegionBindingsRef addBinding(const MemRegion *R, + BindingKey::Kind k, SVal V) const; + + RegionBindingsRef &operator=(const RegionBindingsRef &X) { + *static_cast<ParentTy*>(this) = X; + return *this; + } + + const SVal *lookup(BindingKey K) const; + const SVal *lookup(const MemRegion *R, BindingKey::Kind k) const; + const ClusterBindings *lookup(const MemRegion *R) const { + return static_cast<const ParentTy*>(this)->lookup(R); + } + + RegionBindingsRef removeBinding(BindingKey K); + + RegionBindingsRef removeBinding(const MemRegion *R, + BindingKey::Kind k); + + RegionBindingsRef removeBinding(const MemRegion *R) { + return removeBinding(R, BindingKey::Direct). + removeBinding(R, BindingKey::Default); + } + + Optional<SVal> getDirectBinding(const MemRegion *R) const; + + /// getDefaultBinding - Returns an SVal* representing an optional default + /// binding associated with a region and its subregions. + Optional<SVal> getDefaultBinding(const MemRegion *R) const; + + /// Return the internal tree as a Store. + Store asStore() const { + return asImmutableMap().getRootWithoutRetain(); + } + + void dump(raw_ostream &OS, const char *nl) const { + for (iterator I = begin(), E = end(); I != E; ++I) { + const ClusterBindings &Cluster = I.getData(); + for (ClusterBindings::iterator CI = Cluster.begin(), CE = Cluster.end(); + CI != CE; ++CI) { + OS << ' ' << CI.getKey() << " : " << CI.getData() << nl; + } + OS << nl; + } + } + + LLVM_ATTRIBUTE_USED void dump() const { + dump(llvm::errs(), "\n"); + } +}; +} // end anonymous namespace + +typedef const RegionBindingsRef& RegionBindingsConstRef; + +Optional<SVal> RegionBindingsRef::getDirectBinding(const MemRegion *R) const { + return Optional<SVal>::create(lookup(R, BindingKey::Direct)); +} + +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)); +} + +RegionBindingsRef RegionBindingsRef::addBinding(BindingKey K, SVal V) const { + const MemRegion *Base = K.getBaseRegion(); + + const ClusterBindings *ExistingCluster = lookup(Base); + ClusterBindings Cluster = (ExistingCluster ? *ExistingCluster + : CBFactory.getEmptyMap()); + + ClusterBindings NewCluster = CBFactory.add(Cluster, K, V); + return add(Base, NewCluster); +} + + +RegionBindingsRef RegionBindingsRef::addBinding(const MemRegion *R, + BindingKey::Kind k, + SVal V) const { + return addBinding(BindingKey::Make(R, k), V); +} + +const SVal *RegionBindingsRef::lookup(BindingKey K) const { + const ClusterBindings *Cluster = lookup(K.getBaseRegion()); + if (!Cluster) + return 0; + return Cluster->lookup(K); +} + +const SVal *RegionBindingsRef::lookup(const MemRegion *R, + BindingKey::Kind k) const { + return lookup(BindingKey::Make(R, k)); +} + +RegionBindingsRef RegionBindingsRef::removeBinding(BindingKey K) { + const MemRegion *Base = K.getBaseRegion(); + const ClusterBindings *Cluster = lookup(Base); + if (!Cluster) + return *this; + + ClusterBindings NewCluster = CBFactory.remove(*Cluster, K); + if (NewCluster.isEmpty()) + return remove(Base); + return add(Base, NewCluster); +} + +RegionBindingsRef RegionBindingsRef::removeBinding(const MemRegion *R, + BindingKey::Kind k){ + return removeBinding(BindingKey::Make(R, k)); +} //===----------------------------------------------------------------------===// // Fine-grained control of RegionStoreManager. @@ -161,26 +318,38 @@ public: //===----------------------------------------------------------------------===// namespace { +class invalidateRegionsWorker; class RegionStoreManager : public StoreManager { +public: const RegionStoreFeatures Features; RegionBindings::Factory RBFactory; - ClusterBindings::Factory CBFactory; + mutable ClusterBindings::Factory CBFactory; + + typedef std::vector<SVal> SValListTy; +private: + typedef llvm::DenseMap<const LazyCompoundValData *, + SValListTy> LazyBindingsMapTy; + LazyBindingsMapTy LazyBindingsMap; + + /// \brief A helper used to populate the work list with the given set of + /// regions. + void populateWorkList(invalidateRegionsWorker &W, + ArrayRef<SVal> Values, + bool IsArrayOfConstRegions, + InvalidatedRegions *TopLevelRegions); public: RegionStoreManager(ProgramStateManager& mgr, const RegionStoreFeatures &f) : StoreManager(mgr), Features(f), RBFactory(mgr.getAllocator()), CBFactory(mgr.getAllocator()) {} - Optional<SVal> getDirectBinding(RegionBindings B, const MemRegion *R); - /// getDefaultBinding - Returns an SVal* representing an optional default - /// binding associated with a region and its subregions. - Optional<SVal> getDefaultBinding(RegionBindings B, const MemRegion *R); /// setImplicitDefaultValue - Set the default binding for the provided /// MemRegion to the value implicitly defined for compound literals when /// the value is not specified. - StoreRef setImplicitDefaultValue(Store store, const MemRegion *R, QualType T); + RegionBindingsRef setImplicitDefaultValue(RegionBindingsConstRef B, + const MemRegion *R, QualType T); /// ArrayToPointer - Emulates the "decay" of an array to a pointer /// type. 'Array' represents the lvalue of the array being decayed @@ -197,57 +366,47 @@ public: //===-------------------------------------------------------------------===// // Binding values to regions. //===-------------------------------------------------------------------===// - RegionBindings invalidateGlobalRegion(MemRegion::Kind K, - const Expr *Ex, - unsigned Count, - const LocationContext *LCtx, - RegionBindings B, - InvalidatedRegions *Invalidated); - - StoreRef invalidateRegions(Store store, ArrayRef<const MemRegion *> Regions, + RegionBindingsRef invalidateGlobalRegion(MemRegion::Kind K, + const Expr *Ex, + unsigned Count, + const LocationContext *LCtx, + RegionBindingsRef B, + InvalidatedRegions *Invalidated); + + StoreRef invalidateRegions(Store store, + ArrayRef<SVal> Values, + ArrayRef<SVal> ConstValues, const Expr *E, unsigned Count, const LocationContext *LCtx, - InvalidatedSymbols &IS, const CallEvent *Call, - InvalidatedRegions *Invalidated); + InvalidatedSymbols &IS, + InvalidatedSymbols &ConstIS, + InvalidatedRegions *Invalidated, + InvalidatedRegions *InvalidatedTopLevel, + InvalidatedRegions *InvalidatedTopLevelConst); bool scanReachableSymbols(Store S, const MemRegion *R, ScanReachableSymbols &Callbacks); -public: // Made public for helper classes. - - RegionBindings removeSubRegionBindings(RegionBindings B, const SubRegion *R); - - RegionBindings addBinding(RegionBindings B, BindingKey K, SVal V); - - RegionBindings addBinding(RegionBindings B, const MemRegion *R, - BindingKey::Kind k, SVal V); - - const SVal *lookup(RegionBindings B, BindingKey K); - const SVal *lookup(RegionBindings B, const MemRegion *R, BindingKey::Kind k); + RegionBindingsRef removeSubRegionBindings(RegionBindingsConstRef B, + const SubRegion *R); - RegionBindings removeBinding(RegionBindings B, BindingKey K); - RegionBindings removeBinding(RegionBindings B, const MemRegion *R, - BindingKey::Kind k); +public: // Part of public interface to class. - RegionBindings removeBinding(RegionBindings B, const MemRegion *R) { - return removeBinding(removeBinding(B, R, BindingKey::Direct), R, - BindingKey::Default); + virtual StoreRef Bind(Store store, Loc LV, SVal V) { + return StoreRef(bind(getRegionBindings(store), LV, V).asStore(), *this); } - RegionBindings removeCluster(RegionBindings B, const MemRegion *R); - -public: // Part of public interface to class. - - StoreRef Bind(Store store, Loc LV, SVal V); + 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) { - RegionBindings B = GetRegionBindings(store); - assert(!lookup(B, R, BindingKey::Default)); - assert(!lookup(B, R, BindingKey::Direct)); - return StoreRef(addBinding(B, R, BindingKey::Default, V) - .getRootWithoutRetain(), *this); + RegionBindingsRef B = getRegionBindings(store); + assert(!B.lookup(R, BindingKey::Default)); + assert(!B.lookup(R, BindingKey::Direct)); + return StoreRef(B.addBinding(R, BindingKey::Default, V) + .asImmutableMap() + .getRootWithoutRetain(), *this); } /// \brief Create a new store that binds a value to a compound literal. @@ -265,31 +424,37 @@ public: // Part of public interface to class. const LocationContext *LC, SVal V); /// BindStruct - Bind a compound value to a structure. - StoreRef BindStruct(Store store, const TypedValueRegion* R, SVal V); + RegionBindingsRef bindStruct(RegionBindingsConstRef B, + const TypedValueRegion* R, SVal V); /// BindVector - Bind a compound value to a vector. - StoreRef BindVector(Store store, const TypedValueRegion* R, SVal V); + RegionBindingsRef bindVector(RegionBindingsConstRef B, + const TypedValueRegion* R, SVal V); - StoreRef BindArray(Store store, const TypedValueRegion* R, SVal V); + RegionBindingsRef bindArray(RegionBindingsConstRef B, + const TypedValueRegion* R, + SVal V); /// Clears out all bindings in the given region and assigns a new value /// as a Default binding. - StoreRef BindAggregate(Store store, const TypedRegion *R, SVal DefaultVal); + RegionBindingsRef bindAggregate(RegionBindingsConstRef B, + const TypedRegion *R, + SVal DefaultVal); /// \brief 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); + virtual StoreRef killBinding(Store ST, Loc L); void incrementReferenceCount(Store store) { - GetRegionBindings(store).manualRetain(); + getRegionBindings(store).manualRetain(); } /// If the StoreManager supports it, decrement the reference count of /// the specified Store object. If the reference count hits 0, the memory /// associated with the object is recycled. void decrementReferenceCount(Store store) { - GetRegionBindings(store).manualRelease(); + getRegionBindings(store).manualRelease(); } bool includedInBindings(Store store, const MemRegion *region) const; @@ -307,45 +472,64 @@ public: // Part of public interface to class. /// return undefined /// else /// return symbolic - SVal getBinding(Store store, Loc L, QualType T = QualType()); + virtual SVal getBinding(Store S, Loc L, QualType T) { + return getBinding(getRegionBindings(S), L, T); + } - SVal getBindingForElement(Store store, const ElementRegion *R); + SVal getBinding(RegionBindingsConstRef B, Loc L, QualType T = QualType()); - SVal getBindingForField(Store store, const FieldRegion *R); + SVal getBindingForElement(RegionBindingsConstRef B, const ElementRegion *R); - SVal getBindingForObjCIvar(Store store, const ObjCIvarRegion *R); + SVal getBindingForField(RegionBindingsConstRef B, const FieldRegion *R); - SVal getBindingForVar(Store store, const VarRegion *R); + SVal getBindingForObjCIvar(RegionBindingsConstRef B, const ObjCIvarRegion *R); + + SVal getBindingForVar(RegionBindingsConstRef B, const VarRegion *R); SVal getBindingForLazySymbol(const TypedValueRegion *R); - SVal getBindingForFieldOrElementCommon(Store store, const TypedValueRegion *R, - QualType Ty, const MemRegion *superR); + SVal getBindingForFieldOrElementCommon(RegionBindingsConstRef B, + const TypedValueRegion *R, + QualType Ty, + const MemRegion *superR); - SVal getLazyBinding(const MemRegion *lazyBindingRegion, - Store lazyBindingStore); + SVal getLazyBinding(const SubRegion *LazyBindingRegion, + RegionBindingsRef LazyBinding); /// Get bindings for the values in a struct and return a CompoundVal, used /// when doing struct copy: /// struct s x, y; /// x = y; /// y's value is retrieved by this method. - SVal getBindingForStruct(Store store, const TypedValueRegion* R); - - SVal getBindingForArray(Store store, const TypedValueRegion* R); + SVal getBindingForStruct(RegionBindingsConstRef B, const TypedValueRegion *R); + SVal getBindingForArray(RegionBindingsConstRef B, const TypedValueRegion *R); + NonLoc createLazyBinding(RegionBindingsConstRef B, const TypedValueRegion *R); /// Used to lazily generate derived symbols for bindings that are defined - /// implicitly by default bindings in a super region. - Optional<SVal> getBindingForDerivedDefaultValue(RegionBindings B, + /// implicitly by default bindings in a super region. + /// + /// Note that callers may need to specially handle LazyCompoundVals, which + /// are returned as is in case the caller needs to treat them differently. + Optional<SVal> getBindingForDerivedDefaultValue(RegionBindingsConstRef B, const MemRegion *superR, const TypedValueRegion *R, QualType Ty); - /// Get the state and region whose binding this region R corresponds to. - std::pair<Store, const MemRegion*> - GetLazyBinding(RegionBindings B, const MemRegion *R, - const MemRegion *originalRegion, - bool includeSuffix = false); + /// Get the state and region whose binding this region \p R corresponds to. + /// + /// If there is no lazy binding for \p R, the returned value will have a null + /// \c second. Note that a null pointer can represents a valid Store. + std::pair<Store, const SubRegion *> + findLazyBinding(RegionBindingsConstRef B, const SubRegion *R, + const SubRegion *originalRegion); + + /// Returns the cached set of interesting SVals contained within a lazy + /// binding. + /// + /// The precise value of "interesting" is determined for the purposes of + /// RegionStore's internal analysis. It must always contain all regions and + /// symbols, but may omit constants and other kinds of SVal. + const SValListTy &getInterestingValues(nonloc::LazyCompoundVal LCV); //===------------------------------------------------------------------===// // State pruning. @@ -368,16 +552,18 @@ public: // Part of public interface to class. // Utility methods. //===------------------------------------------------------------------===// - static inline RegionBindings GetRegionBindings(Store store) { - return RegionBindings(static_cast<const RegionBindings::TreeTy*>(store)); + RegionBindingsRef getRegionBindings(Store store) const { + return RegionBindingsRef(CBFactory, + static_cast<const RegionBindings::TreeTy*>(store), + RBFactory.getTreeFactory()); } void print(Store store, raw_ostream &Out, const char* nl, const char *sep); void iterBindings(Store store, BindingsHandler& f) { - RegionBindings B = GetRegionBindings(store); - for (RegionBindings::iterator I = B.begin(), E = B.end(); I != E; ++I) { + RegionBindingsRef B = getRegionBindings(store); + for (RegionBindingsRef::iterator I = B.begin(), E = B.end(); I != E; ++I) { const ClusterBindings &Cluster = I.getData(); for (ClusterBindings::iterator CI = Cluster.begin(), CE = Cluster.end(); CI != CE; ++CI) { @@ -422,7 +608,8 @@ template <typename DERIVED> class ClusterAnalysis { protected: typedef llvm::DenseMap<const MemRegion *, const ClusterBindings *> ClusterMap; - typedef SmallVector<const MemRegion *, 10> WorkList; + typedef llvm::PointerIntPair<const MemRegion *, 1, bool> WorkListElement; + typedef SmallVector<WorkListElement, 10> WorkList; llvm::SmallPtrSet<const ClusterBindings *, 16> Visited; @@ -432,7 +619,7 @@ protected: ASTContext &Ctx; SValBuilder &svalBuilder; - RegionBindings B; + RegionBindingsRef B; const bool includeGlobals; @@ -442,12 +629,12 @@ protected: public: ClusterAnalysis(RegionStoreManager &rm, ProgramStateManager &StateMgr, - RegionBindings b, const bool includeGlobals) + RegionBindingsRef b, const bool includeGlobals) : RM(rm), Ctx(StateMgr.getContext()), svalBuilder(StateMgr.getSValBuilder()), B(b), includeGlobals(includeGlobals) {} - RegionBindings getRegionBindings() const { return B; } + RegionBindingsRef getRegionBindings() const { return B; } bool isVisited(const MemRegion *R) { return Visited.count(getCluster(R)); @@ -455,7 +642,8 @@ public: void GenerateClusters() { // Scan the entire set of bindings and record the region clusters. - for (RegionBindings::iterator RI = B.begin(), RE = B.end(); RI != RE; ++RI){ + for (RegionBindingsRef::iterator RI = B.begin(), RE = B.end(); + RI != RE; ++RI){ const MemRegion *Base = RI.getKey(); const ClusterBindings &Cluster = RI.getData(); @@ -468,35 +656,35 @@ public: } } - bool AddToWorkList(const MemRegion *R, const ClusterBindings *C) { + bool AddToWorkList(WorkListElement E, const ClusterBindings *C) { if (C && !Visited.insert(C)) return false; - WL.push_back(R); + WL.push_back(E); return true; } - bool AddToWorkList(const MemRegion *R) { - const MemRegion *baseR = R->getBaseRegion(); - return AddToWorkList(baseR, getCluster(baseR)); + bool AddToWorkList(const MemRegion *R, bool Flag = false) { + const MemRegion *BaseR = R->getBaseRegion(); + return AddToWorkList(WorkListElement(BaseR, Flag), getCluster(BaseR)); } void RunWorkList() { while (!WL.empty()) { - const MemRegion *baseR = WL.pop_back_val(); + WorkListElement E = WL.pop_back_val(); + const MemRegion *BaseR = E.getPointer(); - // First visit the cluster. - if (const ClusterBindings *Cluster = getCluster(baseR)) - static_cast<DERIVED*>(this)->VisitCluster(baseR, *Cluster); - - // Next, visit the base region. - static_cast<DERIVED*>(this)->VisitBaseRegion(baseR); + static_cast<DERIVED*>(this)->VisitCluster(BaseR, getCluster(BaseR), + E.getInt()); } } -public: void VisitAddedToCluster(const MemRegion *baseR, const ClusterBindings &C) {} - void VisitCluster(const MemRegion *baseR, const ClusterBindings &C) {} - void VisitBaseRegion(const MemRegion *baseR) {} + void VisitCluster(const MemRegion *baseR, const ClusterBindings *C) {} + + void VisitCluster(const MemRegion *BaseR, const ClusterBindings *C, + bool Flag) { + static_cast<DERIVED*>(this)->VisitCluster(BaseR, C); + } }; } @@ -507,7 +695,7 @@ public: bool RegionStoreManager::scanReachableSymbols(Store S, const MemRegion *R, ScanReachableSymbols &Callbacks) { assert(R == R->getBaseRegion() && "Should only be called for base regions"); - RegionBindings B = GetRegionBindings(S); + RegionBindingsRef B = getRegionBindings(S); const ClusterBindings *Cluster = B.lookup(R); if (!Cluster) @@ -562,98 +750,141 @@ static bool isCompatibleWithFields(BindingKey K, const FieldVector &Fields) { Fields.begin() - Delta); } -RegionBindings RegionStoreManager::removeSubRegionBindings(RegionBindings B, - const SubRegion *R) { - BindingKey SRKey = BindingKey::Make(R, BindingKey::Default); - const MemRegion *ClusterHead = SRKey.getBaseRegion(); - if (R == ClusterHead) { - // We can remove an entire cluster's bindings all in one go. - return RBFactory.remove(B, R); - } - +/// Collects all bindings in \p Cluster that may refer to bindings within +/// \p Top. +/// +/// Each binding is a pair whose \c first is the key (a BindingKey) and whose +/// \c second is the value (an SVal). +/// +/// The \p IncludeAllDefaultBindings parameter specifies whether to include +/// default bindings that may extend beyond \p Top itself, e.g. if \p Top is +/// an aggregate within a larger aggregate with a default binding. +static void +collectSubRegionBindings(SmallVectorImpl<BindingPair> &Bindings, + SValBuilder &SVB, const ClusterBindings &Cluster, + const SubRegion *Top, BindingKey TopKey, + bool IncludeAllDefaultBindings) { FieldVector FieldsInSymbolicSubregions; - bool HasSymbolicOffset = SRKey.hasSymbolicOffset(); - if (HasSymbolicOffset) { - getSymbolicOffsetFields(SRKey, FieldsInSymbolicSubregions); - R = cast<SubRegion>(SRKey.getConcreteOffsetRegion()); - SRKey = BindingKey::Make(R, BindingKey::Default); + if (TopKey.hasSymbolicOffset()) { + getSymbolicOffsetFields(TopKey, FieldsInSymbolicSubregions); + Top = cast<SubRegion>(TopKey.getConcreteOffsetRegion()); + TopKey = BindingKey::Make(Top, BindingKey::Default); } - // This assumes the region being invalidated is char-aligned. This isn't - // true for bitfields, but since bitfields have no subregions they shouldn't - // be using this function anyway. + // Find the length (in bits) of the region being invalidated. uint64_t Length = UINT64_MAX; - - SVal Extent = R->getExtent(svalBuilder); - if (nonloc::ConcreteInt *ExtentCI = dyn_cast<nonloc::ConcreteInt>(&Extent)) { + SVal Extent = Top->getExtent(SVB); + if (Optional<nonloc::ConcreteInt> ExtentCI = + Extent.getAs<nonloc::ConcreteInt>()) { const llvm::APSInt &ExtentInt = ExtentCI->getValue(); assert(ExtentInt.isNonNegative() || ExtentInt.isUnsigned()); // Extents are in bytes but region offsets are in bits. Be careful! - Length = ExtentInt.getLimitedValue() * Ctx.getCharWidth(); + Length = ExtentInt.getLimitedValue() * SVB.getContext().getCharWidth(); + } else if (const FieldRegion *FR = dyn_cast<FieldRegion>(Top)) { + if (FR->getDecl()->isBitField()) + Length = FR->getDecl()->getBitWidthValue(SVB.getContext()); } - const ClusterBindings *Cluster = B.lookup(ClusterHead); - if (!Cluster) - return B; - - ClusterBindings Result = *Cluster; - - // It is safe to iterate over the bindings as they are being changed - // because they are in an ImmutableMap. - for (ClusterBindings::iterator I = Cluster->begin(), E = Cluster->end(); + for (ClusterBindings::iterator I = Cluster.begin(), E = Cluster.end(); I != E; ++I) { BindingKey NextKey = I.getKey(); - if (NextKey.getRegion() == SRKey.getRegion()) { + if (NextKey.getRegion() == TopKey.getRegion()) { // FIXME: This doesn't catch the case where we're really invalidating a // region with a symbolic offset. Example: // R: points[i].y // Next: points[0].x - if (NextKey.getOffset() > SRKey.getOffset() && - NextKey.getOffset() - SRKey.getOffset() < Length) { + if (NextKey.getOffset() > TopKey.getOffset() && + NextKey.getOffset() - TopKey.getOffset() < Length) { // Case 1: The next binding is inside the region we're invalidating. - // Remove it. - Result = CBFactory.remove(Result, NextKey); + // Include it. + Bindings.push_back(*I); - } else if (NextKey.getOffset() == SRKey.getOffset()) { + } else if (NextKey.getOffset() == TopKey.getOffset()) { // Case 2: The next binding is at the same offset as the region we're // invalidating. In this case, we need to leave default bindings alone, // since they may be providing a default value for a regions beyond what // we're invalidating. // FIXME: This is probably incorrect; consider invalidating an outer // struct whose first field is bound to a LazyCompoundVal. - if (NextKey.isDirect()) - Result = CBFactory.remove(Result, NextKey); + if (IncludeAllDefaultBindings || NextKey.isDirect()) + Bindings.push_back(*I); } - + } else if (NextKey.hasSymbolicOffset()) { const MemRegion *Base = NextKey.getConcreteOffsetRegion(); - if (R->isSubRegionOf(Base)) { + if (Top->isSubRegionOf(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 remove it. - if (NextKey.isDirect()) + // we'll be conservative and include it. + if (IncludeAllDefaultBindings || NextKey.isDirect()) if (isCompatibleWithFields(NextKey, FieldsInSymbolicSubregions)) - Result = CBFactory.remove(Result, NextKey); + Bindings.push_back(*I); } 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 no longer valid. - if (R == Base || BaseSR->isSubRegionOf(R)) + // super-region. In this case the binding is certainly included. + if (Top == Base || BaseSR->isSubRegionOf(Top)) if (isCompatibleWithFields(NextKey, FieldsInSymbolicSubregions)) - Result = CBFactory.remove(Result, NextKey); + Bindings.push_back(*I); } } } +} + +static void +collectSubRegionBindings(SmallVectorImpl<BindingPair> &Bindings, + SValBuilder &SVB, const ClusterBindings &Cluster, + const SubRegion *Top, bool IncludeAllDefaultBindings) { + collectSubRegionBindings(Bindings, SVB, Cluster, Top, + BindingKey::Make(Top, BindingKey::Default), + IncludeAllDefaultBindings); +} + +RegionBindingsRef +RegionStoreManager::removeSubRegionBindings(RegionBindingsConstRef B, + const SubRegion *Top) { + BindingKey TopKey = BindingKey::Make(Top, BindingKey::Default); + const MemRegion *ClusterHead = TopKey.getBaseRegion(); + + if (Top == ClusterHead) { + // We can remove an entire cluster's bindings all in one go. + return B.remove(Top); + } + + const ClusterBindings *Cluster = B.lookup(ClusterHead); + if (!Cluster) { + // If we're invalidating a region with a symbolic offset, we need to make + // sure we don't treat the base region as uninitialized anymore. + if (TopKey.hasSymbolicOffset()) { + const SubRegion *Concrete = TopKey.getConcreteOffsetRegion(); + return B.addBinding(Concrete, BindingKey::Default, UnknownVal()); + } + return B; + } + + SmallVector<BindingPair, 32> Bindings; + collectSubRegionBindings(Bindings, svalBuilder, *Cluster, Top, TopKey, + /*IncludeAllDefaultBindings=*/false); + + ClusterBindingsRef Result(*Cluster, CBFactory); + for (SmallVectorImpl<BindingPair>::const_iterator I = Bindings.begin(), + E = Bindings.end(); + I != E; ++I) + Result = Result.remove(I->first); // If we're invalidating a region with a symbolic offset, we need to make sure // we don't treat the base region as uninitialized anymore. - // FIXME: This isn't very precise; see the example in the loop. - if (HasSymbolicOffset) - Result = CBFactory.add(Result, SRKey, UnknownVal()); + // FIXME: This isn't very precise; see the example in + // collectSubRegionBindings. + if (TopKey.hasSymbolicOffset()) { + const SubRegion *Concrete = TopKey.getConcreteOffsetRegion(); + Result = Result.add(BindingKey::Make(Concrete, BindingKey::Default), + UnknownVal()); + } if (Result.isEmpty()) - return RBFactory.remove(B, ClusterHead); - return RBFactory.add(B, ClusterHead, Result); + return B.remove(ClusterHead); + return B.add(ClusterHead, Result.asImmutableMap()); } namespace { @@ -662,24 +893,26 @@ class invalidateRegionsWorker : public ClusterAnalysis<invalidateRegionsWorker> const Expr *Ex; unsigned Count; const LocationContext *LCtx; - StoreManager::InvalidatedSymbols &IS; + InvalidatedSymbols &IS; + InvalidatedSymbols &ConstIS; StoreManager::InvalidatedRegions *Regions; public: invalidateRegionsWorker(RegionStoreManager &rm, ProgramStateManager &stateMgr, - RegionBindings b, + RegionBindingsRef b, const Expr *ex, unsigned count, const LocationContext *lctx, - StoreManager::InvalidatedSymbols &is, + InvalidatedSymbols &is, + InvalidatedSymbols &inConstIS, StoreManager::InvalidatedRegions *r, bool includeGlobals) : ClusterAnalysis<invalidateRegionsWorker>(rm, stateMgr, b, includeGlobals), - Ex(ex), Count(count), LCtx(lctx), IS(is), Regions(r) {} + Ex(ex), Count(count), LCtx(lctx), IS(is), ConstIS(inConstIS), Regions(r){} - void VisitCluster(const MemRegion *baseR, const ClusterBindings &C); - void VisitBaseRegion(const MemRegion *baseR); - -private: + /// \param IsConst Specifies if the region we are invalidating is constant. + /// If it is, we invalidate all subregions, but not the base region itself. + void VisitCluster(const MemRegion *baseR, const ClusterBindings *C, + bool IsConst); void VisitBinding(SVal V); }; } @@ -695,43 +928,30 @@ void invalidateRegionsWorker::VisitBinding(SVal V) { } // Is it a LazyCompoundVal? All references get invalidated as well. - if (const nonloc::LazyCompoundVal *LCS = - dyn_cast<nonloc::LazyCompoundVal>(&V)) { + if (Optional<nonloc::LazyCompoundVal> LCS = + V.getAs<nonloc::LazyCompoundVal>()) { - const MemRegion *LazyR = LCS->getRegion(); - RegionBindings B = RegionStoreManager::GetRegionBindings(LCS->getStore()); + const RegionStoreManager::SValListTy &Vals = RM.getInterestingValues(*LCS); - // FIXME: This should not have to walk all bindings in the old store. - for (RegionBindings::iterator RI = B.begin(), RE = B.end(); RI != RE; ++RI){ - const ClusterBindings &Cluster = RI.getData(); - for (ClusterBindings::iterator CI = Cluster.begin(), CE = Cluster.end(); - CI != CE; ++CI) { - BindingKey K = CI.getKey(); - if (const SubRegion *BaseR = dyn_cast<SubRegion>(K.getRegion())) { - if (BaseR == LazyR) - VisitBinding(CI.getData()); - else if (K.hasSymbolicOffset() && BaseR->isSubRegionOf(LazyR)) - VisitBinding(CI.getData()); - } - } - } + for (RegionStoreManager::SValListTy::const_iterator I = Vals.begin(), + E = Vals.end(); + I != E; ++I) + VisitBinding(*I); return; } } -void invalidateRegionsWorker::VisitCluster(const MemRegion *BaseR, - const ClusterBindings &C) { - for (ClusterBindings::iterator I = C.begin(), E = C.end(); I != E; ++I) - VisitBinding(I.getData()); - - B = RM.removeCluster(B, BaseR); -} +void invalidateRegionsWorker::VisitCluster(const MemRegion *baseR, + const ClusterBindings *C, + bool IsConst) { + if (C) { + for (ClusterBindings::iterator I = C->begin(), E = C->end(); I != E; ++I) + VisitBinding(I.getData()); -void invalidateRegionsWorker::VisitBaseRegion(const MemRegion *baseR) { - // Symbolic region? Mark that symbol touched by the invalidation. - if (const SymbolicRegion *SR = dyn_cast<SymbolicRegion>(baseR)) - IS.insert(SR->getSymbol()); + if (!IsConst) + B = B.remove(baseR); + } // BlockDataRegion? If so, invalidate captured variables that are passed // by reference. @@ -739,7 +959,7 @@ void invalidateRegionsWorker::VisitBaseRegion(const MemRegion *baseR) { for (BlockDataRegion::referenced_vars_iterator BI = BR->referenced_vars_begin(), BE = BR->referenced_vars_end() ; BI != BE; ++BI) { - const VarRegion *VR = *BI; + const VarRegion *VR = BI.getCapturedRegion(); const VarDecl *VD = VR->getDecl(); if (VD->getAttr<BlocksAttr>() || !VD->hasLocalStorage()) { AddToWorkList(VR); @@ -750,9 +970,8 @@ void invalidateRegionsWorker::VisitBaseRegion(const MemRegion *baseR) { // invalidate that region. This is because a block may capture // a pointer value, but the thing pointed by that pointer may // get invalidated. - Store store = B.getRootWithoutRetain(); - SVal V = RM.getBinding(store, loc::MemRegionVal(VR)); - if (const Loc *L = dyn_cast<Loc>(&V)) { + SVal V = RM.getBinding(B, loc::MemRegionVal(VR)); + if (Optional<Loc> L = V.getAs<Loc>()) { if (const MemRegion *LR = L->getAsRegion()) AddToWorkList(LR); } @@ -761,6 +980,20 @@ void invalidateRegionsWorker::VisitBaseRegion(const MemRegion *baseR) { return; } + // Symbolic region? + SymbolRef RegionSym = 0; + if (const SymbolicRegion *SR = dyn_cast<SymbolicRegion>(baseR)) + RegionSym = SR->getSymbol(); + + if (IsConst) { + // Mark that symbol touched by the invalidation. + ConstIS.insert(RegionSym); + return; + } + + // Mark that symbol touched by the invalidation. + IS.insert(RegionSym); + // Otherwise, we have a normal data region. Record that we touched the region. if (Regions) Regions->push_back(baseR); @@ -770,7 +1003,7 @@ void invalidateRegionsWorker::VisitBaseRegion(const MemRegion *baseR) { // conjured symbol. The type of the symbol is irrelavant. DefinedOrUnknownSVal V = svalBuilder.conjureSymbolVal(baseR, Ex, LCtx, Ctx.IntTy, Count); - B = RM.addBinding(B, baseR, BindingKey::Default, V); + B = B.addBinding(baseR, BindingKey::Default, V); return; } @@ -786,7 +1019,7 @@ void invalidateRegionsWorker::VisitBaseRegion(const MemRegion *baseR) { // conjured symbol. The type of the symbol is irrelavant. DefinedOrUnknownSVal V = svalBuilder.conjureSymbolVal(baseR, Ex, LCtx, Ctx.IntTy, Count); - B = RM.addBinding(B, baseR, BindingKey::Default, V); + B = B.addBinding(baseR, BindingKey::Default, V); return; } @@ -795,7 +1028,7 @@ void invalidateRegionsWorker::VisitBaseRegion(const MemRegion *baseR) { DefinedOrUnknownSVal V = svalBuilder.conjureSymbolVal(baseR, Ex, LCtx, AT->getElementType(), Count); - B = RM.addBinding(B, baseR, BindingKey::Default, V); + B = B.addBinding(baseR, BindingKey::Default, V); return; } @@ -804,7 +1037,7 @@ void invalidateRegionsWorker::VisitBaseRegion(const MemRegion *baseR) { // If the region is a global and we are invalidating all globals, // just erase the entry. This causes all globals to be lazily // symbolicated from the same base symbol. - B = RM.removeBinding(B, baseR); + B = B.removeBinding(baseR); return; } @@ -812,15 +1045,16 @@ void invalidateRegionsWorker::VisitBaseRegion(const MemRegion *baseR) { DefinedOrUnknownSVal V = svalBuilder.conjureSymbolVal(baseR, Ex, LCtx, T,Count); assert(SymbolManager::canSymbolicate(T) || V.isUnknown()); - B = RM.addBinding(B, baseR, BindingKey::Direct, V); + B = B.addBinding(baseR, BindingKey::Direct, V); } -RegionBindings RegionStoreManager::invalidateGlobalRegion(MemRegion::Kind K, - const Expr *Ex, - unsigned Count, - const LocationContext *LCtx, - RegionBindings B, - InvalidatedRegions *Invalidated) { +RegionBindingsRef +RegionStoreManager::invalidateGlobalRegion(MemRegion::Kind K, + const Expr *Ex, + unsigned Count, + const LocationContext *LCtx, + RegionBindingsRef B, + InvalidatedRegions *Invalidated) { // Bind the globals memory space to a new symbol that we will use to derive // the bindings for all globals. const GlobalsSpaceRegion *GS = MRMgr.getGlobalsRegion(K); @@ -828,8 +1062,8 @@ RegionBindings RegionStoreManager::invalidateGlobalRegion(MemRegion::Kind K, /* type does not matter */ Ctx.IntTy, Count); - B = removeBinding(B, GS); - B = addBinding(B, BindingKey::Make(GS, BindingKey::Default), V); + B = B.removeBinding(GS) + .addBinding(BindingKey::Make(GS, BindingKey::Default), V); // Even if there are no bindings in the global scope, we still need to // record that we touched it. @@ -839,47 +1073,82 @@ RegionBindings RegionStoreManager::invalidateGlobalRegion(MemRegion::Kind K, return B; } -StoreRef RegionStoreManager::invalidateRegions(Store store, - ArrayRef<const MemRegion *> Regions, - const Expr *Ex, unsigned Count, - const LocationContext *LCtx, - InvalidatedSymbols &IS, - const CallEvent *Call, - InvalidatedRegions *Invalidated) { - invalidateRegionsWorker W(*this, StateMgr, - RegionStoreManager::GetRegionBindings(store), - Ex, Count, LCtx, IS, Invalidated, false); +void RegionStoreManager::populateWorkList(invalidateRegionsWorker &W, + ArrayRef<SVal> Values, + bool IsArrayOfConstRegions, + InvalidatedRegions *TopLevelRegions) { + for (ArrayRef<SVal>::iterator I = Values.begin(), + E = Values.end(); I != E; ++I) { + SVal V = *I; + if (Optional<nonloc::LazyCompoundVal> LCS = + V.getAs<nonloc::LazyCompoundVal>()) { + + const SValListTy &Vals = getInterestingValues(*LCS); + + for (SValListTy::const_iterator I = Vals.begin(), + E = Vals.end(); I != E; ++I) { + // Note: the last argument is false here because these are + // non-top-level regions. + if (const MemRegion *R = (*I).getAsRegion()) + W.AddToWorkList(R, /*IsConst=*/ false); + } + continue; + } + + if (const MemRegion *R = V.getAsRegion()) { + if (TopLevelRegions) + TopLevelRegions->push_back(R); + W.AddToWorkList(R, /*IsConst=*/ IsArrayOfConstRegions); + continue; + } + } +} + +StoreRef +RegionStoreManager::invalidateRegions(Store store, + ArrayRef<SVal> Values, + ArrayRef<SVal> ConstValues, + const Expr *Ex, unsigned Count, + const LocationContext *LCtx, + const CallEvent *Call, + InvalidatedSymbols &IS, + InvalidatedSymbols &ConstIS, + InvalidatedRegions *TopLevelRegions, + InvalidatedRegions *TopLevelConstRegions, + InvalidatedRegions *Invalidated) { + RegionBindingsRef B = RegionStoreManager::getRegionBindings(store); + invalidateRegionsWorker W(*this, StateMgr, B, Ex, Count, LCtx, IS, ConstIS, + Invalidated, false); // Scan the bindings and generate the clusters. W.GenerateClusters(); // Add the regions to the worklist. - for (ArrayRef<const MemRegion *>::iterator - I = Regions.begin(), E = Regions.end(); I != E; ++I) - W.AddToWorkList(*I); + populateWorkList(W, Values, /*IsArrayOfConstRegions*/ false, + TopLevelRegions); + populateWorkList(W, ConstValues, /*IsArrayOfConstRegions*/ true, + TopLevelConstRegions); W.RunWorkList(); // Return the new bindings. - RegionBindings B = W.getRegionBindings(); + B = W.getRegionBindings(); - // For all globals which are not static nor immutable: determine which global - // regions should be invalidated and invalidate them. + // For calls, determine which global regions should be invalidated and + // invalidate them. (Note that function-static and immutable globals are never + // invalidated by this.) // TODO: This could possibly be more precise with modules. - // - // System calls invalidate only system globals. - if (Call && Call->isInSystemHeader()) { + if (Call) { B = invalidateGlobalRegion(MemRegion::GlobalSystemSpaceRegionKind, Ex, Count, LCtx, B, Invalidated); - // Internal calls might invalidate both system and internal globals. - } else { - B = invalidateGlobalRegion(MemRegion::GlobalSystemSpaceRegionKind, - Ex, Count, LCtx, B, Invalidated); - B = invalidateGlobalRegion(MemRegion::GlobalInternalSpaceRegionKind, - Ex, Count, LCtx, B, Invalidated); + + if (!Call->isInSystemHeader()) { + B = invalidateGlobalRegion(MemRegion::GlobalInternalSpaceRegionKind, + Ex, Count, LCtx, B, Invalidated); + } } - return StoreRef(B.getRootWithoutRetain(), *this); + return StoreRef(B.asStore(), *this); } //===----------------------------------------------------------------------===// @@ -923,10 +1192,10 @@ RegionStoreManager::getSizeInElements(ProgramStateRef state, /// the array). This is called by ExprEngine when evaluating casts /// from arrays to pointers. SVal RegionStoreManager::ArrayToPointer(Loc Array) { - if (!isa<loc::MemRegionVal>(Array)) + if (!Array.getAs<loc::MemRegionVal>()) return UnknownVal(); - const MemRegion* R = cast<loc::MemRegionVal>(&Array)->getRegion(); + const MemRegion* R = Array.castAs<loc::MemRegionVal>().getRegion(); const TypedValueRegion* ArrayR = dyn_cast<TypedValueRegion>(R); if (!ArrayR) @@ -945,31 +1214,9 @@ SVal RegionStoreManager::ArrayToPointer(Loc Array) { // Loading values from regions. //===----------------------------------------------------------------------===// -Optional<SVal> RegionStoreManager::getDirectBinding(RegionBindings B, - const MemRegion *R) { - - if (const SVal *V = lookup(B, R, BindingKey::Direct)) - return *V; - - return Optional<SVal>(); -} - -Optional<SVal> RegionStoreManager::getDefaultBinding(RegionBindings B, - const MemRegion *R) { - if (R->isBoundable()) - if (const TypedValueRegion *TR = dyn_cast<TypedValueRegion>(R)) - if (TR->getValueType()->isUnionType()) - return UnknownVal(); - - if (const SVal *V = lookup(B, R, BindingKey::Default)) - return *V; - - return Optional<SVal>(); -} - -SVal RegionStoreManager::getBinding(Store store, Loc L, QualType T) { - assert(!isa<UnknownVal>(L) && "location unknown"); - assert(!isa<UndefinedVal>(L) && "location undefined"); +SVal RegionStoreManager::getBinding(RegionBindingsConstRef B, Loc L, QualType T) { + assert(!L.getAs<UnknownVal>() && "location unknown"); + assert(!L.getAs<UndefinedVal>() && "location undefined"); // For access to concrete addresses, return UnknownVal. Checks // for null dereferences (and similar errors) are done by checkers, not @@ -977,14 +1224,14 @@ SVal RegionStoreManager::getBinding(Store store, Loc L, QualType T) { // FIXME: We can consider lazily symbolicating such memory, but we really // should defer this when we can reason easily about symbolicating arrays // of bytes. - if (isa<loc::ConcreteInt>(L)) { + if (L.getAs<loc::ConcreteInt>()) { return UnknownVal(); } - if (!isa<loc::MemRegionVal>(L)) { + if (!L.getAs<loc::MemRegionVal>()) { return UnknownVal(); } - const MemRegion *MR = cast<loc::MemRegionVal>(L).getRegion(); + const MemRegion *MR = L.castAs<loc::MemRegionVal>().getRegion(); if (isa<AllocaRegion>(MR) || isa<SymbolicRegion>(MR) || @@ -1005,6 +1252,11 @@ SVal RegionStoreManager::getBinding(Store store, Loc L, QualType T) { const TypedValueRegion *R = cast<TypedValueRegion>(MR); QualType RTy = R->getValueType(); + // FIXME: we do not yet model the parts of a complex type, so treat the + // whole thing as "unknown". + if (RTy->isAnyComplexType()) + return UnknownVal(); + // FIXME: We should eventually handle funny addressing. e.g.: // // int x = ...; @@ -1013,9 +1265,8 @@ SVal RegionStoreManager::getBinding(Store store, Loc L, QualType T) { // char c = *q; // returns the first byte of 'x'. // // Such funny addressing will occur due to layering of regions. - if (RTy->isStructureOrClassType()) - return getBindingForStruct(store, R); + return getBindingForStruct(B, R); // FIXME: Handle unions. if (RTy->isUnionType()) @@ -1023,7 +1274,7 @@ SVal RegionStoreManager::getBinding(Store store, Loc L, QualType T) { if (RTy->isArrayType()) { if (RTy->isConstantArrayType()) - return getBindingForArray(store, R); + return getBindingForArray(B, R); else return UnknownVal(); } @@ -1033,7 +1284,7 @@ SVal RegionStoreManager::getBinding(Store store, Loc L, QualType T) { return UnknownVal(); if (const FieldRegion* FR = dyn_cast<FieldRegion>(R)) - return CastRetrievedVal(getBindingForField(store, FR), FR, T, false); + return CastRetrievedVal(getBindingForField(B, FR), FR, T, false); if (const ElementRegion* ER = dyn_cast<ElementRegion>(R)) { // FIXME: Here we actually perform an implicit conversion from the loaded @@ -1041,7 +1292,7 @@ SVal RegionStoreManager::getBinding(Store store, 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(store, ER), ER, T, false); + return CastRetrievedVal(getBindingForElement(B, ER), ER, T, false); } if (const ObjCIvarRegion *IVR = dyn_cast<ObjCIvarRegion>(R)) { @@ -1051,7 +1302,7 @@ SVal RegionStoreManager::getBinding(Store store, 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(store, IVR), IVR, T, false); + return CastRetrievedVal(getBindingForObjCIvar(B, IVR), IVR, T, false); } if (const VarRegion *VR = dyn_cast<VarRegion>(R)) { @@ -1061,11 +1312,10 @@ SVal RegionStoreManager::getBinding(Store store, 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(store, VR), VR, T, false); + return CastRetrievedVal(getBindingForVar(B, VR), VR, T, false); } - RegionBindings B = GetRegionBindings(store); - const SVal *V = lookup(B, R, BindingKey::Direct); + const SVal *V = B.lookup(R, BindingKey::Direct); // Check if the region has a binding. if (V) @@ -1086,69 +1336,109 @@ SVal RegionStoreManager::getBinding(Store store, Loc L, QualType T) { return svalBuilder.getRegionValueSymbolVal(R); } -std::pair<Store, const MemRegion *> -RegionStoreManager::GetLazyBinding(RegionBindings B, const MemRegion *R, - const MemRegion *originalRegion, - bool includeSuffix) { - +static QualType getUnderlyingType(const SubRegion *R) { + QualType RegionTy; + if (const TypedValueRegion *TVR = dyn_cast<TypedValueRegion>(R)) + RegionTy = TVR->getValueType(); + + if (const SymbolicRegion *SR = dyn_cast<SymbolicRegion>(R)) + RegionTy = SR->getSymbol()->getType(); + + return RegionTy; +} + +/// Checks to see if store \p B has a lazy binding for region \p R. +/// +/// If \p AllowSubregionBindings is \c false, a lazy binding will be rejected +/// if there are additional bindings within \p R. +/// +/// Note that unlike RegionStoreManager::findLazyBinding, this will not search +/// for lazy bindings for super-regions of \p R. +static Optional<nonloc::LazyCompoundVal> +getExistingLazyBinding(SValBuilder &SVB, RegionBindingsConstRef B, + const SubRegion *R, bool AllowSubregionBindings) { + Optional<SVal> V = B.getDefaultBinding(R); + if (!V) + return None; + + Optional<nonloc::LazyCompoundVal> LCV = V->getAs<nonloc::LazyCompoundVal>(); + if (!LCV) + return None; + + // If the LCV is for a subregion, the types might not match, and we shouldn't + // reuse the binding. + QualType RegionTy = getUnderlyingType(R); + if (!RegionTy.isNull() && + !RegionTy->isVoidPointerType()) { + QualType SourceRegionTy = LCV->getRegion()->getValueType(); + if (!SVB.getContext().hasSameUnqualifiedType(RegionTy, SourceRegionTy)) + return None; + } + + if (!AllowSubregionBindings) { + // If there are any other bindings within this region, we shouldn't reuse + // the top-level binding. + SmallVector<BindingPair, 16> Bindings; + collectSubRegionBindings(Bindings, SVB, *B.lookup(R->getBaseRegion()), R, + /*IncludeAllDefaultBindings=*/true); + if (Bindings.size() > 1) + return None; + } + + return *LCV; +} + + +std::pair<Store, const SubRegion *> +RegionStoreManager::findLazyBinding(RegionBindingsConstRef B, + const SubRegion *R, + const SubRegion *originalRegion) { if (originalRegion != R) { - if (Optional<SVal> OV = getDefaultBinding(B, R)) { - if (const nonloc::LazyCompoundVal *V = - dyn_cast<nonloc::LazyCompoundVal>(OV.getPointer())) - return std::make_pair(V->getStore(), V->getRegion()); - } + if (Optional<nonloc::LazyCompoundVal> V = + getExistingLazyBinding(svalBuilder, B, R, true)) + return std::make_pair(V->getStore(), V->getRegion()); } - + + typedef std::pair<Store, const SubRegion *> StoreRegionPair; + StoreRegionPair Result = StoreRegionPair(); + if (const ElementRegion *ER = dyn_cast<ElementRegion>(R)) { - const std::pair<Store, const MemRegion *> &X = - GetLazyBinding(B, ER->getSuperRegion(), originalRegion); - - if (X.second) - return std::make_pair(X.first, - MRMgr.getElementRegionWithSuper(ER, X.second)); - } - else if (const FieldRegion *FR = dyn_cast<FieldRegion>(R)) { - const std::pair<Store, const MemRegion *> &X = - GetLazyBinding(B, FR->getSuperRegion(), originalRegion); - - if (X.second) { - if (includeSuffix) - return std::make_pair(X.first, - MRMgr.getFieldRegionWithSuper(FR, X.second)); - return X; - } - - } - // C++ base object region is another kind of region that we should blast - // through to look for lazy compound value. It is like a field region. - else if (const CXXBaseObjectRegion *baseReg = - dyn_cast<CXXBaseObjectRegion>(R)) { - const std::pair<Store, const MemRegion *> &X = - GetLazyBinding(B, baseReg->getSuperRegion(), originalRegion); + Result = findLazyBinding(B, cast<SubRegion>(ER->getSuperRegion()), + originalRegion); + + if (Result.second) + Result.second = MRMgr.getElementRegionWithSuper(ER, Result.second); + + } else if (const FieldRegion *FR = dyn_cast<FieldRegion>(R)) { + Result = findLazyBinding(B, cast<SubRegion>(FR->getSuperRegion()), + originalRegion); + + if (Result.second) + Result.second = MRMgr.getFieldRegionWithSuper(FR, Result.second); + + } else if (const CXXBaseObjectRegion *BaseReg = + dyn_cast<CXXBaseObjectRegion>(R)) { + // C++ base object region is another kind of region that we should blast + // through to look for lazy compound value. It is like a field region. + Result = findLazyBinding(B, cast<SubRegion>(BaseReg->getSuperRegion()), + originalRegion); - if (X.second) { - if (includeSuffix) - return std::make_pair(X.first, - MRMgr.getCXXBaseObjectRegionWithSuper(baseReg, - X.second)); - return X; - } + if (Result.second) + Result.second = MRMgr.getCXXBaseObjectRegionWithSuper(BaseReg, + Result.second); } - // The NULL MemRegion indicates an non-existent lazy binding. A NULL Store is - // possible for a valid lazy binding. - return std::make_pair((Store) 0, (const MemRegion *) 0); + return Result; } -SVal RegionStoreManager::getBindingForElement(Store store, +SVal RegionStoreManager::getBindingForElement(RegionBindingsConstRef B, const ElementRegion* R) { // We do not currently model bindings of the CompoundLiteralregion. if (isa<CompoundLiteralRegion>(R->getBaseRegion())) return UnknownVal(); // Check if the region has a binding. - RegionBindings B = GetRegionBindings(store); - if (const Optional<SVal> &V = getDirectBinding(B, R)) + if (const Optional<SVal> &V = B.getDirectBinding(R)) return *V; const MemRegion* superR = R->getSuperRegion(); @@ -1163,7 +1453,7 @@ SVal RegionStoreManager::getBindingForElement(Store store, const StringLiteral *Str = StrR->getStringLiteral(); SVal Idx = R->getIndex(); - if (nonloc::ConcreteInt *CI = dyn_cast<nonloc::ConcreteInt>(&Idx)) { + if (Optional<nonloc::ConcreteInt> CI = Idx.getAs<nonloc::ConcreteInt>()) { int64_t i = CI->getValue().getSExtValue(); // Abort on string underrun. This can be possible by arbitrary // clients of getBindingForElement(). @@ -1202,7 +1492,7 @@ SVal RegionStoreManager::getBindingForElement(Store store, QualType elemT = R->getElementType(); if (elemT->isScalarType()) { if (Ctx.getTypeSizeInChars(baseT) >= Ctx.getTypeSizeInChars(elemT)) { - if (const Optional<SVal> &V = getDirectBinding(B, superR)) { + if (const Optional<SVal> &V = B.getDirectBinding(superR)) { if (SymbolRef parentSym = V->getAsSymbol()) return svalBuilder.getDerivedRegionValueSymbolVal(parentSym, R); @@ -1216,29 +1506,27 @@ SVal RegionStoreManager::getBindingForElement(Store store, } } } - return getBindingForFieldOrElementCommon(store, R, R->getElementType(), - superR); + return getBindingForFieldOrElementCommon(B, R, R->getElementType(),superR); } -SVal RegionStoreManager::getBindingForField(Store store, - const FieldRegion* R) { +SVal RegionStoreManager::getBindingForField(RegionBindingsConstRef B, + const FieldRegion* R) { // Check if the region has a binding. - RegionBindings B = GetRegionBindings(store); - if (const Optional<SVal> &V = getDirectBinding(B, R)) + if (const Optional<SVal> &V = B.getDirectBinding(R)) return *V; QualType Ty = R->getValueType(); - return getBindingForFieldOrElementCommon(store, R, Ty, R->getSuperRegion()); + return getBindingForFieldOrElementCommon(B, R, Ty, R->getSuperRegion()); } Optional<SVal> -RegionStoreManager::getBindingForDerivedDefaultValue(RegionBindings B, +RegionStoreManager::getBindingForDerivedDefaultValue(RegionBindingsConstRef B, const MemRegion *superR, const TypedValueRegion *R, QualType Ty) { - if (const Optional<SVal> &D = getDefaultBinding(B, superR)) { + if (const Optional<SVal> &D = B.getDefaultBinding(superR)) { const SVal &val = D.getValue(); if (SymbolRef parentSym = val.getAsSymbol()) return svalBuilder.getDerivedRegionValueSymbolVal(parentSym, R); @@ -1249,53 +1537,95 @@ RegionStoreManager::getBindingForDerivedDefaultValue(RegionBindings B, if (val.isUnknownOrUndef()) return val; - // Lazy bindings are handled later. - if (isa<nonloc::LazyCompoundVal>(val)) - return Optional<SVal>(); + // Lazy bindings are usually handled through getExistingLazyBinding(). + // We should unify these two code paths at some point. + if (val.getAs<nonloc::LazyCompoundVal>()) + return val; llvm_unreachable("Unknown default value"); } - return Optional<SVal>(); + return None; } -SVal RegionStoreManager::getLazyBinding(const MemRegion *lazyBindingRegion, - Store lazyBindingStore) { - if (const ElementRegion *ER = dyn_cast<ElementRegion>(lazyBindingRegion)) - return getBindingForElement(lazyBindingStore, ER); - - return getBindingForField(lazyBindingStore, - cast<FieldRegion>(lazyBindingRegion)); +SVal RegionStoreManager::getLazyBinding(const SubRegion *LazyBindingRegion, + RegionBindingsRef LazyBinding) { + SVal Result; + if (const ElementRegion *ER = dyn_cast<ElementRegion>(LazyBindingRegion)) + Result = getBindingForElement(LazyBinding, ER); + else + Result = getBindingForField(LazyBinding, + cast<FieldRegion>(LazyBindingRegion)); + + // FIXME: This is a hack to deal with RegionStore's inability to distinguish a + // default value for /part/ of an aggregate from a default value for the + // /entire/ aggregate. The most common case of this is when struct Outer + // has as its first member a struct Inner, which is copied in from a stack + // variable. In this case, even if the Outer's default value is symbolic, 0, + // or unknown, it gets overridden by the Inner's default value of undefined. + // + // This is a general problem -- if the Inner is zero-initialized, the Outer + // will now look zero-initialized. The proper way to solve this is with a + // new version of RegionStore that tracks the extent of a binding as well + // as the offset. + // + // This hack only takes care of the undefined case because that can very + // quickly result in a warning. + if (Result.isUndef()) + Result = UnknownVal(); + + return Result; } -SVal RegionStoreManager::getBindingForFieldOrElementCommon(Store store, +SVal +RegionStoreManager::getBindingForFieldOrElementCommon(RegionBindingsConstRef B, const TypedValueRegion *R, QualType Ty, const MemRegion *superR) { // At this point we have already checked in either getBindingForElement or // getBindingForField if 'R' has a direct binding. - RegionBindings B = GetRegionBindings(store); // Lazy binding? Store lazyBindingStore = NULL; - const MemRegion *lazyBindingRegion = NULL; - llvm::tie(lazyBindingStore, lazyBindingRegion) = GetLazyBinding(B, R, R, - true); - + const SubRegion *lazyBindingRegion = NULL; + llvm::tie(lazyBindingStore, lazyBindingRegion) = findLazyBinding(B, R, R); if (lazyBindingRegion) - return getLazyBinding(lazyBindingRegion, lazyBindingStore); + return getLazyBinding(lazyBindingRegion, + getRegionBindings(lazyBindingStore)); // Record whether or not we see a symbolic index. That can completely // be out of scope of our lookup. bool hasSymbolicIndex = false; - while (superR) { - if (const Optional<SVal> &D = - getBindingForDerivedDefaultValue(B, superR, R, Ty)) + // FIXME: This is a hack to deal with RegionStore's inability to distinguish a + // default value for /part/ of an aggregate from a default value for the + // /entire/ aggregate. The most common case of this is when struct Outer + // has as its first member a struct Inner, which is copied in from a stack + // variable. In this case, even if the Outer's default value is symbolic, 0, + // or unknown, it gets overridden by the Inner's default value of undefined. + // + // This is a general problem -- if the Inner is zero-initialized, the Outer + // will now look zero-initialized. The proper way to solve this is with a + // new version of RegionStore that tracks the extent of a binding as well + // as the offset. + // + // This hack only takes care of the undefined case because that can very + // quickly result in a warning. + bool hasPartialLazyBinding = false; + + const SubRegion *Base = dyn_cast<SubRegion>(superR); + while (Base) { + if (Optional<SVal> D = getBindingForDerivedDefaultValue(B, Base, R, Ty)) { + if (D->getAs<nonloc::LazyCompoundVal>()) { + hasPartialLazyBinding = true; + break; + } + return *D; + } - if (const ElementRegion *ER = dyn_cast<ElementRegion>(superR)) { + if (const ElementRegion *ER = dyn_cast<ElementRegion>(Base)) { NonLoc index = ER->getIndex(); if (!index.isConstant()) hasSymbolicIndex = true; @@ -1303,11 +1633,7 @@ SVal RegionStoreManager::getBindingForFieldOrElementCommon(Store store, // If our super region is a field or element itself, walk up the region // hierarchy to see if there is a default value installed in an ancestor. - if (const SubRegion *SR = dyn_cast<SubRegion>(superR)) { - superR = SR->getSuperRegion(); - continue; - } - break; + Base = dyn_cast<SubRegion>(Base->getSuperRegion()); } if (R->hasStackNonParametersStorage()) { @@ -1327,27 +1653,25 @@ SVal RegionStoreManager::getBindingForFieldOrElementCommon(Store store, // a symbolic offset. if (hasSymbolicIndex) return UnknownVal(); - - return UndefinedVal(); + + if (!hasPartialLazyBinding) + return UndefinedVal(); } // All other values are symbolic. return svalBuilder.getRegionValueSymbolVal(R); } -SVal RegionStoreManager::getBindingForObjCIvar(Store store, +SVal RegionStoreManager::getBindingForObjCIvar(RegionBindingsConstRef B, const ObjCIvarRegion* R) { - - // Check if the region has a binding. - RegionBindings B = GetRegionBindings(store); - - if (const Optional<SVal> &V = getDirectBinding(B, R)) + // Check if the region has a binding. + if (const Optional<SVal> &V = B.getDirectBinding(R)) return *V; const MemRegion *superR = R->getSuperRegion(); // Check if the super region has a default binding. - if (const Optional<SVal> &V = getDefaultBinding(B, superR)) { + if (const Optional<SVal> &V = B.getDefaultBinding(superR)) { if (SymbolRef parentSym = V->getAsSymbol()) return svalBuilder.getDerivedRegionValueSymbolVal(parentSym, R); @@ -1358,51 +1682,64 @@ SVal RegionStoreManager::getBindingForObjCIvar(Store store, return getBindingForLazySymbol(R); } -SVal RegionStoreManager::getBindingForVar(Store store, const VarRegion *R) { +static Optional<SVal> getConstValue(SValBuilder &SVB, const VarDecl *VD) { + ASTContext &Ctx = SVB.getContext(); + if (!VD->getType().isConstQualified()) + return None; - // Check if the region has a binding. - RegionBindings B = GetRegionBindings(store); + const Expr *Init = VD->getInit(); + if (!Init) + return None; + + llvm::APSInt Result; + if (!Init->isGLValue() && Init->EvaluateAsInt(Result, Ctx)) + return SVB.makeIntVal(Result); + + if (Init->isNullPointerConstant(Ctx, Expr::NPC_ValueDependentIsNotNull)) + return SVB.makeNull(); - if (const Optional<SVal> &V = getDirectBinding(B, R)) + // FIXME: Handle other possible constant expressions. + return None; +} + +SVal RegionStoreManager::getBindingForVar(RegionBindingsConstRef B, + const VarRegion *R) { + + // Check if the region has a binding. + if (const Optional<SVal> &V = B.getDirectBinding(R)) return *V; // Lazily derive a value for the VarRegion. const VarDecl *VD = R->getDecl(); - QualType T = VD->getType(); const MemSpaceRegion *MS = R->getMemorySpace(); - if (isa<UnknownSpaceRegion>(MS) || - isa<StackArgumentsSpaceRegion>(MS)) + // Arguments are always symbolic. + if (isa<StackArgumentsSpaceRegion>(MS)) + return svalBuilder.getRegionValueSymbolVal(R); + + // Is 'VD' declared constant? If so, retrieve the constant value. + if (Optional<SVal> V = getConstValue(svalBuilder, VD)) + return *V; + + // This must come after the check for constants because closure-captured + // constant variables may appear in UnknownSpaceRegion. + if (isa<UnknownSpaceRegion>(MS)) return svalBuilder.getRegionValueSymbolVal(R); if (isa<GlobalsSpaceRegion>(MS)) { - if (isa<NonStaticGlobalSpaceRegion>(MS)) { - // Is 'VD' declared constant? If so, retrieve the constant value. - QualType CT = Ctx.getCanonicalType(T); - if (CT.isConstQualified()) { - const Expr *Init = VD->getInit(); - // Do the null check first, as we want to call 'IgnoreParenCasts'. - if (Init) - if (const IntegerLiteral *IL = - dyn_cast<IntegerLiteral>(Init->IgnoreParenCasts())) { - const nonloc::ConcreteInt &V = svalBuilder.makeIntVal(IL); - return svalBuilder.evalCast(V, Init->getType(), IL->getType()); - } - } + QualType T = VD->getType(); - if (const Optional<SVal> &V - = getBindingForDerivedDefaultValue(B, MS, R, CT)) - return V.getValue(); + // Function-scoped static variables are default-initialized to 0; if they + // have an initializer, it would have been processed by now. + if (isa<StaticGlobalSpaceRegion>(MS)) + return svalBuilder.makeZeroVal(T); - return svalBuilder.getRegionValueSymbolVal(R); + if (Optional<SVal> V = getBindingForDerivedDefaultValue(B, MS, R, T)) { + assert(!V->getAs<nonloc::LazyCompoundVal>()); + return V.getValue(); } - if (T->isIntegerType()) - return svalBuilder.makeIntVal(0, T); - if (T->isPointerType()) - return svalBuilder.makeNull(); - - return UnknownVal(); + return svalBuilder.getRegionValueSymbolVal(R); } return UndefinedVal(); @@ -1413,55 +1750,77 @@ SVal RegionStoreManager::getBindingForLazySymbol(const TypedValueRegion *R) { return svalBuilder.getRegionValueSymbolVal(R); } -static bool mayHaveLazyBinding(QualType Ty) { - return Ty->isArrayType() || Ty->isStructureOrClassType(); +const RegionStoreManager::SValListTy & +RegionStoreManager::getInterestingValues(nonloc::LazyCompoundVal LCV) { + // First, check the cache. + LazyBindingsMapTy::iterator I = LazyBindingsMap.find(LCV.getCVData()); + if (I != LazyBindingsMap.end()) + return I->second; + + // If we don't have a list of values cached, start constructing it. + SValListTy List; + + const SubRegion *LazyR = LCV.getRegion(); + RegionBindingsRef B = getRegionBindings(LCV.getStore()); + + // If this region had /no/ bindings at the time, there are no interesting + // values to return. + const ClusterBindings *Cluster = B.lookup(LazyR->getBaseRegion()); + if (!Cluster) + return (LazyBindingsMap[LCV.getCVData()] = llvm_move(List)); + + SmallVector<BindingPair, 32> Bindings; + collectSubRegionBindings(Bindings, svalBuilder, *Cluster, LazyR, + /*IncludeAllDefaultBindings=*/true); + for (SmallVectorImpl<BindingPair>::const_iterator I = Bindings.begin(), + E = Bindings.end(); + I != E; ++I) { + SVal V = I->second; + if (V.isUnknownOrUndef() || V.isConstant()) + continue; + + if (Optional<nonloc::LazyCompoundVal> InnerLCV = + V.getAs<nonloc::LazyCompoundVal>()) { + const SValListTy &InnerList = getInterestingValues(*InnerLCV); + List.insert(List.end(), InnerList.begin(), InnerList.end()); + continue; + } + + List.push_back(V); + } + + return (LazyBindingsMap[LCV.getCVData()] = llvm_move(List)); +} + +NonLoc RegionStoreManager::createLazyBinding(RegionBindingsConstRef B, + const TypedValueRegion *R) { + if (Optional<nonloc::LazyCompoundVal> V = + getExistingLazyBinding(svalBuilder, B, R, false)) + return *V; + + return svalBuilder.makeLazyCompoundVal(StoreRef(B.asStore(), *this), R); } -SVal RegionStoreManager::getBindingForStruct(Store store, - const TypedValueRegion* R) { +SVal RegionStoreManager::getBindingForStruct(RegionBindingsConstRef B, + const TypedValueRegion *R) { const RecordDecl *RD = R->getValueType()->castAs<RecordType>()->getDecl(); if (RD->field_empty()) return UnknownVal(); - // If we already have a lazy binding, don't create a new one, - // unless the first field might have a lazy binding of its own. - // (Right now we can't tell the difference.) - QualType FirstFieldType = RD->field_begin()->getType(); - if (!mayHaveLazyBinding(FirstFieldType)) { - RegionBindings B = GetRegionBindings(store); - BindingKey K = BindingKey::Make(R, BindingKey::Default); - if (const nonloc::LazyCompoundVal *V = - dyn_cast_or_null<nonloc::LazyCompoundVal>(lookup(B, K))) { - return *V; - } - } - - return svalBuilder.makeLazyCompoundVal(StoreRef(store, *this), R); + return createLazyBinding(B, R); } -SVal RegionStoreManager::getBindingForArray(Store store, - const TypedValueRegion * R) { - const ConstantArrayType *Ty = Ctx.getAsConstantArrayType(R->getValueType()); - assert(Ty && "Only constant array types can have compound bindings."); +SVal RegionStoreManager::getBindingForArray(RegionBindingsConstRef B, + const TypedValueRegion *R) { + assert(Ctx.getAsConstantArrayType(R->getValueType()) && + "Only constant array types can have compound bindings."); - // If we already have a lazy binding, don't create a new one, - // unless the first element might have a lazy binding of its own. - // (Right now we can't tell the difference.) - if (!mayHaveLazyBinding(Ty->getElementType())) { - RegionBindings B = GetRegionBindings(store); - BindingKey K = BindingKey::Make(R, BindingKey::Default); - if (const nonloc::LazyCompoundVal *V = - dyn_cast_or_null<nonloc::LazyCompoundVal>(lookup(B, K))) { - return *V; - } - } - - return svalBuilder.makeLazyCompoundVal(StoreRef(store, *this), R); + return createLazyBinding(B, R); } bool RegionStoreManager::includedInBindings(Store store, const MemRegion *region) const { - RegionBindings B = GetRegionBindings(store); + RegionBindingsRef B = getRegionBindings(store); region = region->getBaseRegion(); // Quick path: if the base is the head of a cluster, the region is live. @@ -1469,7 +1828,7 @@ bool RegionStoreManager::includedInBindings(Store store, return true; // Slow path: if the region is the VALUE of any binding, it is live. - for (RegionBindings::iterator RI = B.begin(), RE = B.end(); RI != RE; ++RI) { + for (RegionBindingsRef::iterator RI = B.begin(), RE = B.end(); RI != RE; ++RI) { const ClusterBindings &Cluster = RI.getData(); for (ClusterBindings::iterator CI = Cluster.begin(), CE = Cluster.end(); CI != CE; ++CI) { @@ -1488,31 +1847,33 @@ bool RegionStoreManager::includedInBindings(Store store, //===----------------------------------------------------------------------===// StoreRef RegionStoreManager::killBinding(Store ST, Loc L) { - if (isa<loc::MemRegionVal>(L)) - if (const MemRegion* R = cast<loc::MemRegionVal>(L).getRegion()) - return StoreRef(removeBinding(GetRegionBindings(ST), - R).getRootWithoutRetain(), + if (Optional<loc::MemRegionVal> LV = L.getAs<loc::MemRegionVal>()) + if (const MemRegion* R = LV->getRegion()) + return StoreRef(getRegionBindings(ST).removeBinding(R) + .asImmutableMap() + .getRootWithoutRetain(), *this); return StoreRef(ST, *this); } -StoreRef RegionStoreManager::Bind(Store store, Loc L, SVal V) { - if (isa<loc::ConcreteInt>(L)) - return StoreRef(store, *this); +RegionBindingsRef +RegionStoreManager::bind(RegionBindingsConstRef B, Loc L, SVal V) { + if (L.getAs<loc::ConcreteInt>()) + return B; // If we get here, the location should be a region. - const MemRegion *R = cast<loc::MemRegionVal>(L).getRegion(); + const MemRegion *R = L.castAs<loc::MemRegionVal>().getRegion(); // Check if the region is a struct region. if (const TypedValueRegion* TR = dyn_cast<TypedValueRegion>(R)) { QualType Ty = TR->getValueType(); if (Ty->isArrayType()) - return BindArray(store, TR, V); + return bindArray(B, TR, V); if (Ty->isStructureOrClassType()) - return BindStruct(store, TR, V); + return bindStruct(B, TR, V); if (Ty->isVectorType()) - return BindVector(store, TR, V); + return bindVector(B, TR, V); } if (const SymbolicRegion *SR = dyn_cast<SymbolicRegion>(R)) { @@ -1526,12 +1887,8 @@ StoreRef RegionStoreManager::Bind(Store store, Loc L, SVal V) { } // Clear out bindings that may overlap with this binding. - - // Perform the binding. - RegionBindings B = GetRegionBindings(store); - B = removeSubRegionBindings(B, cast<SubRegion>(R)); - BindingKey Key = BindingKey::Make(R, BindingKey::Direct); - return StoreRef(addBinding(B, Key, V).getRootWithoutRetain(), *this); + RegionBindingsRef NewB = removeSubRegionBindings(B, cast<SubRegion>(R)); + return NewB.addBinding(BindingKey::Make(R, BindingKey::Direct), V); } // FIXME: this method should be merged into Bind(). @@ -1542,10 +1899,10 @@ StoreRef RegionStoreManager::bindCompoundLiteral(Store ST, return Bind(ST, loc::MemRegionVal(MRMgr.getCompoundLiteralRegion(CL, LC)), V); } -StoreRef RegionStoreManager::setImplicitDefaultValue(Store store, - const MemRegion *R, - QualType T) { - RegionBindings B = GetRegionBindings(store); +RegionBindingsRef +RegionStoreManager::setImplicitDefaultValue(RegionBindingsConstRef B, + const MemRegion *R, + QualType T) { SVal V; if (Loc::isLocType(T)) @@ -1566,12 +1923,13 @@ StoreRef RegionStoreManager::setImplicitDefaultValue(Store store, V = UnknownVal(); } - return StoreRef(addBinding(B, R, BindingKey::Default, - V).getRootWithoutRetain(), *this); + return B.addBinding(R, BindingKey::Default, V); } -StoreRef RegionStoreManager::BindArray(Store store, const TypedValueRegion* R, - SVal Init) { +RegionBindingsRef +RegionStoreManager::bindArray(RegionBindingsConstRef B, + const TypedValueRegion* R, + SVal Init) { const ArrayType *AT =cast<ArrayType>(Ctx.getCanonicalType(R->getValueType())); QualType ElementTy = AT->getElementType(); @@ -1581,30 +1939,31 @@ StoreRef RegionStoreManager::BindArray(Store store, const TypedValueRegion* R, Size = CAT->getSize().getZExtValue(); // Check if the init expr is a string literal. - if (loc::MemRegionVal *MRV = dyn_cast<loc::MemRegionVal>(&Init)) { + if (Optional<loc::MemRegionVal> MRV = Init.getAs<loc::MemRegionVal>()) { const StringRegion *S = cast<StringRegion>(MRV->getRegion()); // Treat the string as a lazy compound value. - nonloc::LazyCompoundVal LCV = - cast<nonloc::LazyCompoundVal>(svalBuilder. - makeLazyCompoundVal(StoreRef(store, *this), S)); - return BindAggregate(store, R, LCV); + StoreRef store(B.asStore(), *this); + nonloc::LazyCompoundVal LCV = svalBuilder.makeLazyCompoundVal(store, S) + .castAs<nonloc::LazyCompoundVal>(); + return bindAggregate(B, R, LCV); } // Handle lazy compound values. - if (isa<nonloc::LazyCompoundVal>(Init)) - return BindAggregate(store, R, Init); + if (Init.getAs<nonloc::LazyCompoundVal>()) + return bindAggregate(B, R, Init); // Remaining case: explicit compound values. if (Init.isUnknown()) - return setImplicitDefaultValue(store, R, ElementTy); + return setImplicitDefaultValue(B, R, ElementTy); - nonloc::CompoundVal& CV = cast<nonloc::CompoundVal>(Init); + const nonloc::CompoundVal& CV = Init.castAs<nonloc::CompoundVal>(); nonloc::CompoundVal::iterator VI = CV.begin(), VE = CV.end(); uint64_t i = 0; - StoreRef newStore(store, *this); + RegionBindingsRef NewB(B); + for (; Size.hasValue() ? i < Size.getValue() : true ; ++i, ++VI) { // The init list might be shorter than the array length. if (VI == VE) @@ -1614,44 +1973,45 @@ StoreRef RegionStoreManager::BindArray(Store store, const TypedValueRegion* R, const ElementRegion *ER = MRMgr.getElementRegion(ElementTy, Idx, R, Ctx); if (ElementTy->isStructureOrClassType()) - newStore = BindStruct(newStore.getStore(), ER, *VI); + NewB = bindStruct(NewB, ER, *VI); else if (ElementTy->isArrayType()) - newStore = BindArray(newStore.getStore(), ER, *VI); + NewB = bindArray(NewB, ER, *VI); else - newStore = Bind(newStore.getStore(), svalBuilder.makeLoc(ER), *VI); + NewB = bind(NewB, svalBuilder.makeLoc(ER), *VI); } // If the init list is shorter than the array length, set the // array default value. if (Size.hasValue() && i < Size.getValue()) - newStore = setImplicitDefaultValue(newStore.getStore(), R, ElementTy); + NewB = setImplicitDefaultValue(NewB, R, ElementTy); - return newStore; + return NewB; } -StoreRef RegionStoreManager::BindVector(Store store, const TypedValueRegion* R, - SVal V) { +RegionBindingsRef RegionStoreManager::bindVector(RegionBindingsConstRef B, + const TypedValueRegion* R, + SVal V) { QualType T = R->getValueType(); assert(T->isVectorType()); const VectorType *VT = T->getAs<VectorType>(); // Use getAs for typedefs. // Handle lazy compound values and symbolic values. - if (isa<nonloc::LazyCompoundVal>(V) || isa<nonloc::SymbolVal>(V)) - return BindAggregate(store, R, V); + if (V.getAs<nonloc::LazyCompoundVal>() || V.getAs<nonloc::SymbolVal>()) + return bindAggregate(B, R, V); // We may get non-CompoundVal accidentally due to imprecise cast logic or // that we are binding symbolic struct value. Kill the field values, and if // the value is symbolic go and bind it as a "default" binding. - if (!isa<nonloc::CompoundVal>(V)) { - return BindAggregate(store, R, UnknownVal()); + if (!V.getAs<nonloc::CompoundVal>()) { + return bindAggregate(B, R, UnknownVal()); } QualType ElemType = VT->getElementType(); - nonloc::CompoundVal& CV = cast<nonloc::CompoundVal>(V); + nonloc::CompoundVal CV = V.castAs<nonloc::CompoundVal>(); nonloc::CompoundVal::iterator VI = CV.begin(), VE = CV.end(); unsigned index = 0, numElements = VT->getNumElements(); - StoreRef newStore(store, *this); - + RegionBindingsRef NewB(B); + for ( ; index != numElements ; ++index) { if (VI == VE) break; @@ -1660,20 +2020,20 @@ StoreRef RegionStoreManager::BindVector(Store store, const TypedValueRegion* R, const ElementRegion *ER = MRMgr.getElementRegion(ElemType, Idx, R, Ctx); if (ElemType->isArrayType()) - newStore = BindArray(newStore.getStore(), ER, *VI); + NewB = bindArray(NewB, ER, *VI); else if (ElemType->isStructureOrClassType()) - newStore = BindStruct(newStore.getStore(), ER, *VI); + NewB = bindStruct(NewB, ER, *VI); else - newStore = Bind(newStore.getStore(), svalBuilder.makeLoc(ER), *VI); + NewB = bind(NewB, svalBuilder.makeLoc(ER), *VI); } - return newStore; + return NewB; } -StoreRef RegionStoreManager::BindStruct(Store store, const TypedValueRegion* R, - SVal V) { - +RegionBindingsRef RegionStoreManager::bindStruct(RegionBindingsConstRef B, + const TypedValueRegion* R, + SVal V) { if (!Features.supportsFields()) - return StoreRef(store, *this); + return B; QualType T = R->getValueType(); assert(T->isStructureOrClassType()); @@ -1682,24 +2042,24 @@ StoreRef RegionStoreManager::BindStruct(Store store, const TypedValueRegion* R, RecordDecl *RD = RT->getDecl(); if (!RD->isCompleteDefinition()) - return StoreRef(store, *this); + return B; // Handle lazy compound values and symbolic values. - if (isa<nonloc::LazyCompoundVal>(V) || isa<nonloc::SymbolVal>(V)) - return BindAggregate(store, R, V); + if (V.getAs<nonloc::LazyCompoundVal>() || V.getAs<nonloc::SymbolVal>()) + return bindAggregate(B, R, V); // We may get non-CompoundVal accidentally due to imprecise cast logic or // that we are binding symbolic struct value. Kill the field values, and if // the value is symbolic go and bind it as a "default" binding. - if (V.isUnknown() || !isa<nonloc::CompoundVal>(V)) - return BindAggregate(store, R, UnknownVal()); + if (V.isUnknown() || !V.getAs<nonloc::CompoundVal>()) + return bindAggregate(B, R, UnknownVal()); - nonloc::CompoundVal& CV = cast<nonloc::CompoundVal>(V); + const nonloc::CompoundVal& CV = V.castAs<nonloc::CompoundVal>(); nonloc::CompoundVal::iterator VI = CV.begin(), VE = CV.end(); RecordDecl::field_iterator FI, FE; - StoreRef newStore(store, *this); - + RegionBindingsRef NewB(B); + for (FI = RD->field_begin(), FE = RD->field_end(); FI != FE; ++FI) { if (VI == VE) @@ -1713,95 +2073,30 @@ StoreRef RegionStoreManager::BindStruct(Store store, const TypedValueRegion* R, const FieldRegion* FR = MRMgr.getFieldRegion(*FI, R); if (FTy->isArrayType()) - newStore = BindArray(newStore.getStore(), FR, *VI); + NewB = bindArray(NewB, FR, *VI); else if (FTy->isStructureOrClassType()) - newStore = BindStruct(newStore.getStore(), FR, *VI); + NewB = bindStruct(NewB, FR, *VI); else - newStore = Bind(newStore.getStore(), svalBuilder.makeLoc(FR), *VI); + NewB = bind(NewB, svalBuilder.makeLoc(FR), *VI); ++VI; } // There may be fewer values in the initialize list than the fields of struct. if (FI != FE) { - RegionBindings B = GetRegionBindings(newStore.getStore()); - B = addBinding(B, R, BindingKey::Default, svalBuilder.makeIntVal(0, false)); - newStore = StoreRef(B.getRootWithoutRetain(), *this); + NewB = NewB.addBinding(R, BindingKey::Default, + svalBuilder.makeIntVal(0, false)); } - return newStore; + return NewB; } -StoreRef RegionStoreManager::BindAggregate(Store store, const TypedRegion *R, - SVal Val) { +RegionBindingsRef +RegionStoreManager::bindAggregate(RegionBindingsConstRef B, + const TypedRegion *R, + SVal Val) { // Remove the old bindings, using 'R' as the root of all regions // we will invalidate. Then add the new binding. - RegionBindings B = GetRegionBindings(store); - - B = removeSubRegionBindings(B, R); - B = addBinding(B, R, BindingKey::Default, Val); - - return StoreRef(B.getRootWithoutRetain(), *this); -} - -//===----------------------------------------------------------------------===// -// "Raw" retrievals and bindings. -//===----------------------------------------------------------------------===// - - -RegionBindings RegionStoreManager::addBinding(RegionBindings B, BindingKey K, - SVal V) { - const MemRegion *Base = K.getBaseRegion(); - - const ClusterBindings *ExistingCluster = B.lookup(Base); - ClusterBindings Cluster = (ExistingCluster ? *ExistingCluster - : CBFactory.getEmptyMap()); - - ClusterBindings NewCluster = CBFactory.add(Cluster, K, V); - return RBFactory.add(B, Base, NewCluster); -} - -RegionBindings RegionStoreManager::addBinding(RegionBindings B, - const MemRegion *R, - BindingKey::Kind k, SVal V) { - return addBinding(B, BindingKey::Make(R, k), V); -} - -const SVal *RegionStoreManager::lookup(RegionBindings B, BindingKey K) { - const ClusterBindings *Cluster = B.lookup(K.getBaseRegion()); - if (!Cluster) - return 0; - - return Cluster->lookup(K); -} - -const SVal *RegionStoreManager::lookup(RegionBindings B, - const MemRegion *R, - BindingKey::Kind k) { - return lookup(B, BindingKey::Make(R, k)); -} - -RegionBindings RegionStoreManager::removeBinding(RegionBindings B, - BindingKey K) { - const MemRegion *Base = K.getBaseRegion(); - const ClusterBindings *Cluster = B.lookup(Base); - if (!Cluster) - return B; - - ClusterBindings NewCluster = CBFactory.remove(*Cluster, K); - if (NewCluster.isEmpty()) - return RBFactory.remove(B, Base); - return RBFactory.add(B, Base, NewCluster); -} - -RegionBindings RegionStoreManager::removeBinding(RegionBindings B, - const MemRegion *R, - BindingKey::Kind k){ - return removeBinding(B, BindingKey::Make(R, k)); -} - -RegionBindings RegionStoreManager::removeCluster(RegionBindings B, - const MemRegion *Base) { - return RBFactory.remove(B, Base); + return removeSubRegionBindings(B, R).addBinding(R, BindingKey::Default, Val); } //===----------------------------------------------------------------------===// @@ -1818,7 +2113,7 @@ class removeDeadBindingsWorker : public: removeDeadBindingsWorker(RegionStoreManager &rm, ProgramStateManager &stateMgr, - RegionBindings b, SymbolReaper &symReaper, + RegionBindingsRef b, SymbolReaper &symReaper, const StackFrameContext *LCtx) : ClusterAnalysis<removeDeadBindingsWorker>(rm, stateMgr, b, /* includeGlobals = */ false), @@ -1826,7 +2121,8 @@ public: // Called by ClusterAnalysis. void VisitAddedToCluster(const MemRegion *baseR, const ClusterBindings &C); - void VisitCluster(const MemRegion *baseR, const ClusterBindings &C); + void VisitCluster(const MemRegion *baseR, const ClusterBindings *C); + using ClusterAnalysis<removeDeadBindingsWorker>::VisitCluster; bool UpdatePostponed(); void VisitBinding(SVal V); @@ -1869,38 +2165,30 @@ void removeDeadBindingsWorker::VisitAddedToCluster(const MemRegion *baseR, } void removeDeadBindingsWorker::VisitCluster(const MemRegion *baseR, - const ClusterBindings &C) { + const ClusterBindings *C) { + if (!C) + return; + // Mark the symbol for any SymbolicRegion with live bindings as live itself. // This means we should continue to track that symbol. if (const SymbolicRegion *SymR = dyn_cast<SymbolicRegion>(baseR)) SymReaper.markLive(SymR->getSymbol()); - for (ClusterBindings::iterator I = C.begin(), E = C.end(); I != E; ++I) + for (ClusterBindings::iterator I = C->begin(), E = C->end(); I != E; ++I) VisitBinding(I.getData()); } void removeDeadBindingsWorker::VisitBinding(SVal V) { // Is it a LazyCompoundVal? All referenced regions are live as well. - if (const nonloc::LazyCompoundVal *LCS = - dyn_cast<nonloc::LazyCompoundVal>(&V)) { + if (Optional<nonloc::LazyCompoundVal> LCS = + V.getAs<nonloc::LazyCompoundVal>()) { - const MemRegion *LazyR = LCS->getRegion(); - RegionBindings B = RegionStoreManager::GetRegionBindings(LCS->getStore()); + const RegionStoreManager::SValListTy &Vals = RM.getInterestingValues(*LCS); - // FIXME: This should not have to walk all bindings in the old store. - for (RegionBindings::iterator RI = B.begin(), RE = B.end(); RI != RE; ++RI){ - const ClusterBindings &Cluster = RI.getData(); - for (ClusterBindings::iterator CI = Cluster.begin(), CE = Cluster.end(); - CI != CE; ++CI) { - BindingKey K = CI.getKey(); - if (const SubRegion *BaseR = dyn_cast<SubRegion>(K.getRegion())) { - if (BaseR == LazyR) - VisitBinding(CI.getData()); - else if (K.hasSymbolicOffset() && BaseR->isSubRegionOf(LazyR)) - VisitBinding(CI.getData()); - } - } - } + for (RegionStoreManager::SValListTy::const_iterator I = Vals.begin(), + E = Vals.end(); + I != E; ++I) + VisitBinding(*I); return; } @@ -1946,7 +2234,7 @@ bool removeDeadBindingsWorker::UpdatePostponed() { StoreRef RegionStoreManager::removeDeadBindings(Store store, const StackFrameContext *LCtx, SymbolReaper& SymReaper) { - RegionBindings B = GetRegionBindings(store); + RegionBindingsRef B = getRegionBindings(store); removeDeadBindingsWorker W(*this, StateMgr, B, SymReaper, LCtx); W.GenerateClusters(); @@ -1961,7 +2249,7 @@ StoreRef RegionStoreManager::removeDeadBindings(Store store, // We have now scanned the store, marking reachable regions and symbols // as live. We now remove all the regions that are dead from the store // as well as update DSymbols with the set symbols that are now dead. - for (RegionBindings::iterator I = B.begin(), E = B.end(); I != E; ++I) { + for (RegionBindingsRef::iterator I = B.begin(), E = B.end(); I != E; ++I) { const MemRegion *Base = I.getKey(); // If the cluster has been visited, we know the region has been marked. @@ -1969,7 +2257,7 @@ StoreRef RegionStoreManager::removeDeadBindings(Store store, continue; // Remove the dead entry. - B = removeCluster(B, Base); + B = B.remove(Base); if (const SymbolicRegion *SymR = dyn_cast<SymbolicRegion>(Base)) SymReaper.maybeDead(SymR->getSymbol()); @@ -1985,7 +2273,7 @@ StoreRef RegionStoreManager::removeDeadBindings(Store store, } } - return StoreRef(B.getRootWithoutRetain(), *this); + return StoreRef(B.asStore(), *this); } //===----------------------------------------------------------------------===// @@ -1994,17 +2282,9 @@ StoreRef RegionStoreManager::removeDeadBindings(Store store, void RegionStoreManager::print(Store store, raw_ostream &OS, const char* nl, const char *sep) { - RegionBindings B = GetRegionBindings(store); + RegionBindingsRef B = getRegionBindings(store); OS << "Store (direct and default bindings), " - << (void*) B.getRootWithoutRetain() + << B.asStore() << " :" << nl; - - for (RegionBindings::iterator I = B.begin(), E = B.end(); I != E; ++I) { - const ClusterBindings &Cluster = I.getData(); - for (ClusterBindings::iterator CI = Cluster.begin(), CE = Cluster.end(); - CI != CE; ++CI) { - OS << ' ' << CI.getKey() << " : " << CI.getData() << nl; - } - OS << nl; - } + B.dump(OS, nl); } diff --git a/lib/StaticAnalyzer/Core/SValBuilder.cpp b/lib/StaticAnalyzer/Core/SValBuilder.cpp index b87169a..c72e780 100644 --- a/lib/StaticAnalyzer/Core/SValBuilder.cpp +++ b/lib/StaticAnalyzer/Core/SValBuilder.cpp @@ -12,13 +12,13 @@ // //===----------------------------------------------------------------------===// -#include "clang/AST/ExprCXX.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h" #include "clang/AST/DeclCXX.h" +#include "clang/AST/ExprCXX.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/BasicValueFactory.h" #include "clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/BasicValueFactory.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h" using namespace clang; using namespace ento; @@ -78,13 +78,13 @@ SVal SValBuilder::convertToArrayIndex(SVal val) { return val; // Common case: we have an appropriately sized integer. - if (nonloc::ConcreteInt* CI = dyn_cast<nonloc::ConcreteInt>(&val)) { + if (Optional<nonloc::ConcreteInt> CI = val.getAs<nonloc::ConcreteInt>()) { const llvm::APSInt& I = CI->getValue(); if (I.getBitWidth() == ArrayIndexWidth && I.isSigned()) return val; } - return evalCastFromNonLoc(cast<NonLoc>(val), ArrayIndexTy); + return evalCastFromNonLoc(val.castAs<NonLoc>(), ArrayIndexTy); } nonloc::ConcreteInt SValBuilder::makeBoolVal(const CXXBoolLiteralExpr *boolean){ @@ -237,11 +237,11 @@ SVal SValBuilder::makeSymExprValNN(ProgramStateRef State, return makeNonLoc(symLHS, Op, symRHS, ResultTy); if (symLHS && symLHS->computeComplexity() < MaxComp) - if (const nonloc::ConcreteInt *rInt = dyn_cast<nonloc::ConcreteInt>(&RHS)) + if (Optional<nonloc::ConcreteInt> rInt = RHS.getAs<nonloc::ConcreteInt>()) return makeNonLoc(symLHS, Op, rInt->getValue(), ResultTy); if (symRHS && symRHS->computeComplexity() < MaxComp) - if (const nonloc::ConcreteInt *lInt = dyn_cast<nonloc::ConcreteInt>(&LHS)) + if (Optional<nonloc::ConcreteInt> lInt = LHS.getAs<nonloc::ConcreteInt>()) return makeNonLoc(lInt->getValue(), Op, symRHS, ResultTy); return UnknownVal(); @@ -257,41 +257,42 @@ SVal SValBuilder::evalBinOp(ProgramStateRef state, BinaryOperator::Opcode op, if (lhs.isUnknown() || rhs.isUnknown()) return UnknownVal(); - if (isa<Loc>(lhs)) { - if (isa<Loc>(rhs)) - return evalBinOpLL(state, op, cast<Loc>(lhs), cast<Loc>(rhs), type); + if (Optional<Loc> LV = lhs.getAs<Loc>()) { + if (Optional<Loc> RV = rhs.getAs<Loc>()) + return evalBinOpLL(state, op, *LV, *RV, type); - return evalBinOpLN(state, op, cast<Loc>(lhs), cast<NonLoc>(rhs), type); + return evalBinOpLN(state, op, *LV, rhs.castAs<NonLoc>(), type); } - if (isa<Loc>(rhs)) { + if (Optional<Loc> RV = rhs.getAs<Loc>()) { // Support pointer arithmetic where the addend is on the left // and the pointer on the right. assert(op == BO_Add); // Commute the operands. - return evalBinOpLN(state, op, cast<Loc>(rhs), cast<NonLoc>(lhs), type); + return evalBinOpLN(state, op, *RV, lhs.castAs<NonLoc>(), type); } - return evalBinOpNN(state, op, cast<NonLoc>(lhs), cast<NonLoc>(rhs), type); + return evalBinOpNN(state, op, lhs.castAs<NonLoc>(), rhs.castAs<NonLoc>(), + type); } DefinedOrUnknownSVal SValBuilder::evalEQ(ProgramStateRef state, DefinedOrUnknownSVal lhs, DefinedOrUnknownSVal rhs) { - return cast<DefinedOrUnknownSVal>(evalBinOp(state, BO_EQ, lhs, rhs, - Context.IntTy)); + return evalBinOp(state, BO_EQ, lhs, rhs, Context.IntTy) + .castAs<DefinedOrUnknownSVal>(); } /// Recursively check if the pointer types are equal modulo const, volatile, -/// and restrict qualifiers. Assumes the input types are canonical. -/// TODO: This is based off of code in SemaCast; can we reuse it. -static bool haveSimilarTypes(ASTContext &Context, QualType T1, - QualType T2) { - while (Context.UnwrapSimilarPointerTypes(T1, T2)) { +/// and restrict qualifiers. Also, assume that all types are similar to 'void'. +/// Assumes the input types are canonical. +static bool shouldBeModeledWithNoOp(ASTContext &Context, QualType ToTy, + QualType FromTy) { + while (Context.UnwrapSimilarPointerTypes(ToTy, FromTy)) { Qualifiers Quals1, Quals2; - T1 = Context.getUnqualifiedArrayType(T1, Quals1); - T2 = Context.getUnqualifiedArrayType(T2, Quals2); + ToTy = Context.getUnqualifiedArrayType(ToTy, Quals1); + FromTy = Context.getUnqualifiedArrayType(FromTy, Quals2); // Make sure that non cvr-qualifiers the other qualifiers (e.g., address // spaces) are identical. @@ -301,7 +302,12 @@ static bool haveSimilarTypes(ASTContext &Context, QualType T1, return false; } - if (T1 != T2) + // If we are casting to void, the 'From' value can be used to represent the + // 'To' value. + if (ToTy->isVoidType()) + return true; + + if (ToTy != FromTy) return false; return true; @@ -314,19 +320,19 @@ SVal SValBuilder::evalCast(SVal val, QualType castTy, QualType originalTy) { if (val.isUnknownOrUndef() || castTy == originalTy) return val; - // For const casts, just propagate the value. + // For const casts, casts to void, just propagate the value. if (!castTy->isVariableArrayType() && !originalTy->isVariableArrayType()) - if (haveSimilarTypes(Context, Context.getPointerType(castTy), - Context.getPointerType(originalTy))) + if (shouldBeModeledWithNoOp(Context, Context.getPointerType(castTy), + Context.getPointerType(originalTy))) return val; // Check for casts from pointers to integers. if (castTy->isIntegerType() && Loc::isLocType(originalTy)) - return evalCastFromLoc(cast<Loc>(val), castTy); + return evalCastFromLoc(val.castAs<Loc>(), castTy); // Check for casts from integers to pointers. if (Loc::isLocType(castTy) && originalTy->isIntegerType()) { - if (nonloc::LocAsInteger *LV = dyn_cast<nonloc::LocAsInteger>(&val)) { + if (Optional<nonloc::LocAsInteger> LV = val.getAs<nonloc::LocAsInteger>()) { if (const MemRegion *R = LV->getLoc().getAsRegion()) { StoreManager &storeMgr = StateMgr.getStoreManager(); R = storeMgr.castRegion(R, castTy); @@ -346,7 +352,7 @@ SVal SValBuilder::evalCast(SVal val, QualType castTy, QualType originalTy) { // Check for casts from array type to another type. if (originalTy->isArrayType()) { // We will always decay to a pointer. - val = StateMgr.ArrayToPointer(cast<Loc>(val)); + val = StateMgr.ArrayToPointer(val.castAs<Loc>()); // Are we casting from an array to a pointer? If so just pass on // the decayed value. @@ -361,7 +367,7 @@ SVal SValBuilder::evalCast(SVal val, QualType castTy, QualType originalTy) { // need the original decayed type. // QualType elemTy = cast<ArrayType>(originalTy)->getElementType(); // QualType pointerTy = C.getPointerType(elemTy); - return evalCastFromLoc(cast<Loc>(val), castTy); + return evalCastFromLoc(val.castAs<Loc>(), castTy); } // Check for casts from a region to a specific type. diff --git a/lib/StaticAnalyzer/Core/SVals.cpp b/lib/StaticAnalyzer/Core/SVals.cpp index e34ab6a..38e216f 100644 --- a/lib/StaticAnalyzer/Core/SVals.cpp +++ b/lib/StaticAnalyzer/Core/SVals.cpp @@ -15,6 +15,7 @@ #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" #include "clang/AST/ExprObjC.h" #include "clang/Basic/IdentifierTable.h" +#include "llvm/Support/raw_ostream.h" using namespace clang; using namespace ento; using llvm::APSInt; @@ -29,13 +30,13 @@ using llvm::APSInt; //===----------------------------------------------------------------------===// bool SVal::hasConjuredSymbol() const { - if (const nonloc::SymbolVal* SV = dyn_cast<nonloc::SymbolVal>(this)) { + if (Optional<nonloc::SymbolVal> SV = getAs<nonloc::SymbolVal>()) { SymbolRef sym = SV->getSymbol(); if (isa<SymbolConjured>(sym)) return true; } - if (const loc::MemRegionVal *RV = dyn_cast<loc::MemRegionVal>(this)) { + if (Optional<loc::MemRegionVal> RV = getAs<loc::MemRegionVal>()) { const MemRegion *R = RV->getRegion(); if (const SymbolicRegion *SR = dyn_cast<SymbolicRegion>(R)) { SymbolRef sym = SR->getSymbol(); @@ -48,7 +49,7 @@ bool SVal::hasConjuredSymbol() const { } const FunctionDecl *SVal::getAsFunctionDecl() const { - if (const loc::MemRegionVal* X = dyn_cast<loc::MemRegionVal>(this)) { + if (Optional<loc::MemRegionVal> X = getAs<loc::MemRegionVal>()) { const MemRegion* R = X->getRegion(); if (const FunctionTextRegion *CTR = R->getAs<FunctionTextRegion>()) if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(CTR->getDecl())) @@ -65,10 +66,10 @@ const FunctionDecl *SVal::getAsFunctionDecl() const { /// region. If that is the case, gets the underlining region. SymbolRef SVal::getAsLocSymbol() const { // FIXME: should we consider SymbolRef wrapped in CodeTextRegion? - if (const nonloc::LocAsInteger *X = dyn_cast<nonloc::LocAsInteger>(this)) + if (Optional<nonloc::LocAsInteger> X = getAs<nonloc::LocAsInteger>()) return X->getLoc().getAsLocSymbol(); - if (const loc::MemRegionVal *X = dyn_cast<loc::MemRegionVal>(this)) { + if (Optional<loc::MemRegionVal> X = getAs<loc::MemRegionVal>()) { const MemRegion *R = X->stripCasts(); if (const SymbolicRegion *SymR = dyn_cast<SymbolicRegion>(R)) return SymR->getSymbol(); @@ -78,7 +79,7 @@ SymbolRef SVal::getAsLocSymbol() const { /// Get the symbol in the SVal or its base region. SymbolRef SVal::getLocSymbolInBase() const { - const loc::MemRegionVal *X = dyn_cast<loc::MemRegionVal>(this); + Optional<loc::MemRegionVal> X = getAs<loc::MemRegionVal>(); if (!X) return 0; @@ -101,7 +102,7 @@ SymbolRef SVal::getLocSymbolInBase() const { /// Otherwise return 0. SymbolRef SVal::getAsSymbol() const { // FIXME: should we consider SymbolRef wrapped in CodeTextRegion? - if (const nonloc::SymbolVal *X = dyn_cast<nonloc::SymbolVal>(this)) + if (Optional<nonloc::SymbolVal> X = getAs<nonloc::SymbolVal>()) return X->getSymbol(); return getAsLocSymbol(); @@ -110,7 +111,7 @@ SymbolRef SVal::getAsSymbol() const { /// getAsSymbolicExpression - If this Sval wraps a symbolic expression then /// return that expression. Otherwise return NULL. const SymExpr *SVal::getAsSymbolicExpression() const { - if (const nonloc::SymbolVal *X = dyn_cast<nonloc::SymbolVal>(this)) + if (Optional<nonloc::SymbolVal> X = getAs<nonloc::SymbolVal>()) return X->getSymbol(); return getAsSymbol(); @@ -124,12 +125,11 @@ const SymExpr* SVal::getAsSymExpr() const { } const MemRegion *SVal::getAsRegion() const { - if (const loc::MemRegionVal *X = dyn_cast<loc::MemRegionVal>(this)) + if (Optional<loc::MemRegionVal> X = getAs<loc::MemRegionVal>()) return X->getRegion(); - if (const nonloc::LocAsInteger *X = dyn_cast<nonloc::LocAsInteger>(this)) { + if (Optional<nonloc::LocAsInteger> X = getAs<nonloc::LocAsInteger>()) return X->getLoc().getAsRegion(); - } return 0; } @@ -143,7 +143,7 @@ const void *nonloc::LazyCompoundVal::getStore() const { return static_cast<const LazyCompoundValData*>(Data)->getStore(); } -const TypedRegion *nonloc::LazyCompoundVal::getRegion() const { +const TypedValueRegion *nonloc::LazyCompoundVal::getRegion() const { return static_cast<const LazyCompoundValData*>(Data)->getRegion(); } @@ -164,16 +164,15 @@ nonloc::CompoundVal::iterator nonloc::CompoundVal::end() const { //===----------------------------------------------------------------------===// bool SVal::isConstant() const { - return isa<nonloc::ConcreteInt>(this) || isa<loc::ConcreteInt>(this); + return getAs<nonloc::ConcreteInt>() || getAs<loc::ConcreteInt>(); } bool SVal::isConstant(int I) const { - if (isa<loc::ConcreteInt>(*this)) - return cast<loc::ConcreteInt>(*this).getValue() == I; - else if (isa<nonloc::ConcreteInt>(*this)) - return cast<nonloc::ConcreteInt>(*this).getValue() == I; - else - return false; + if (Optional<loc::ConcreteInt> LV = getAs<loc::ConcreteInt>()) + return LV->getValue() == I; + if (Optional<nonloc::ConcreteInt> NV = getAs<nonloc::ConcreteInt>()) + return NV->getValue() == I; + return false; } bool SVal::isZeroConstant() const { @@ -215,13 +214,12 @@ SVal loc::ConcreteInt::evalBinOp(BasicValueFactory& BasicVals, BinaryOperator::Opcode Op, const loc::ConcreteInt& R) const { - assert (Op == BO_Add || Op == BO_Sub || - (Op >= BO_LT && Op <= BO_NE)); + assert(BinaryOperator::isComparisonOp(Op) || Op == BO_Sub); - const llvm::APSInt* X = BasicVals.evalAPSInt(Op, getValue(), R.getValue()); + const llvm::APSInt *X = BasicVals.evalAPSInt(Op, getValue(), R.getValue()); if (X) - return loc::ConcreteInt(*X); + return nonloc::ConcreteInt(*X); else return UndefinedVal(); } @@ -238,10 +236,10 @@ void SVal::dumpToStream(raw_ostream &os) const { os << "Unknown"; break; case NonLocKind: - cast<NonLoc>(this)->dumpToStream(os); + castAs<NonLoc>().dumpToStream(os); break; case LocKind: - cast<Loc>(this)->dumpToStream(os); + castAs<Loc>().dumpToStream(os); break; case UndefinedKind: os << "Undefined"; @@ -252,7 +250,7 @@ void SVal::dumpToStream(raw_ostream &os) const { void NonLoc::dumpToStream(raw_ostream &os) const { switch (getSubKind()) { case nonloc::ConcreteIntKind: { - const nonloc::ConcreteInt& C = *cast<nonloc::ConcreteInt>(this); + const nonloc::ConcreteInt& C = castAs<nonloc::ConcreteInt>(); if (C.getValue().isUnsigned()) os << C.getValue().getZExtValue(); else @@ -262,16 +260,16 @@ void NonLoc::dumpToStream(raw_ostream &os) const { break; } case nonloc::SymbolValKind: { - os << cast<nonloc::SymbolVal>(this)->getSymbol(); + os << castAs<nonloc::SymbolVal>().getSymbol(); break; } case nonloc::LocAsIntegerKind: { - const nonloc::LocAsInteger& C = *cast<nonloc::LocAsInteger>(this); + const nonloc::LocAsInteger& C = castAs<nonloc::LocAsInteger>(); os << C.getLoc() << " [as " << C.getNumBits() << " bit integer]"; break; } case nonloc::CompoundValKind: { - const nonloc::CompoundVal& C = *cast<nonloc::CompoundVal>(this); + 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) { @@ -287,7 +285,7 @@ void NonLoc::dumpToStream(raw_ostream &os) const { break; } case nonloc::LazyCompoundValKind: { - const nonloc::LazyCompoundVal &C = *cast<nonloc::LazyCompoundVal>(this); + const nonloc::LazyCompoundVal &C = castAs<nonloc::LazyCompoundVal>(); os << "lazyCompoundVal{" << const_cast<void *>(C.getStore()) << ',' << C.getRegion() << '}'; @@ -302,13 +300,13 @@ void NonLoc::dumpToStream(raw_ostream &os) const { void Loc::dumpToStream(raw_ostream &os) const { switch (getSubKind()) { case loc::ConcreteIntKind: - os << cast<loc::ConcreteInt>(this)->getValue().getZExtValue() << " (Loc)"; + os << castAs<loc::ConcreteInt>().getValue().getZExtValue() << " (Loc)"; break; case loc::GotoLabelKind: - os << "&&" << cast<loc::GotoLabel>(this)->getLabel()->getName(); + os << "&&" << castAs<loc::GotoLabel>().getLabel()->getName(); break; case loc::MemRegionKind: - os << '&' << cast<loc::MemRegionVal>(this)->getRegion()->getString(); + os << '&' << castAs<loc::MemRegionVal>().getRegion()->getString(); break; default: llvm_unreachable("Pretty-printing not implemented for this Loc."); diff --git a/lib/StaticAnalyzer/Core/SimpleConstraintManager.cpp b/lib/StaticAnalyzer/Core/SimpleConstraintManager.cpp index 4236ee4..9b759df 100644 --- a/lib/StaticAnalyzer/Core/SimpleConstraintManager.cpp +++ b/lib/StaticAnalyzer/Core/SimpleConstraintManager.cpp @@ -24,7 +24,7 @@ namespace ento { SimpleConstraintManager::~SimpleConstraintManager() {} bool SimpleConstraintManager::canReasonAbout(SVal X) const { - nonloc::SymbolVal *SymVal = dyn_cast<nonloc::SymbolVal>(&X); + Optional<nonloc::SymbolVal> SymVal = X.getAs<nonloc::SymbolVal>(); if (SymVal && SymVal->isExpression()) { const SymExpr *SE = SymVal->getSymbol(); @@ -49,6 +49,16 @@ bool SimpleConstraintManager::canReasonAbout(SVal X) const { } } + if (const SymSymExpr *SSE = dyn_cast<SymSymExpr>(SE)) { + if (BinaryOperator::isComparisonOp(SSE->getOpcode())) { + // We handle Loc <> Loc comparisons, but not (yet) NonLoc <> NonLoc. + if (Loc::isLocType(SSE->getLHS()->getType())) { + assert(Loc::isLocType(SSE->getRHS()->getType())); + return true; + } + } + } + return false; } @@ -58,10 +68,9 @@ bool SimpleConstraintManager::canReasonAbout(SVal X) const { ProgramStateRef SimpleConstraintManager::assume(ProgramStateRef state, DefinedSVal Cond, bool Assumption) { - if (isa<NonLoc>(Cond)) - return assume(state, cast<NonLoc>(Cond), Assumption); - else - return assume(state, cast<Loc>(Cond), Assumption); + if (Optional<NonLoc> NV = Cond.getAs<NonLoc>()) + return assume(state, *NV, Assumption); + return assume(state, Cond.castAs<Loc>(), Assumption); } ProgramStateRef SimpleConstraintManager::assume(ProgramStateRef state, Loc cond, @@ -82,7 +91,7 @@ ProgramStateRef SimpleConstraintManager::assumeAux(ProgramStateRef state, case loc::MemRegionKind: { // FIXME: Should this go into the storemanager? - const MemRegion *R = cast<loc::MemRegionVal>(Cond).getRegion(); + const MemRegion *R = Cond.castAs<loc::MemRegionVal>().getRegion(); const SubRegion *SubR = dyn_cast<SubRegion>(R); while (SubR) { @@ -104,7 +113,7 @@ ProgramStateRef SimpleConstraintManager::assumeAux(ProgramStateRef state, return Assumption ? state : NULL; case loc::ConcreteIntKind: { - bool b = cast<loc::ConcreteInt>(Cond).getValue() != 0; + bool b = Cond.castAs<loc::ConcreteInt>().getValue() != 0; bool isFeasible = b ? Assumption : !Assumption; return isFeasible ? state : NULL; } @@ -120,21 +129,6 @@ ProgramStateRef SimpleConstraintManager::assume(ProgramStateRef state, return state; } -static BinaryOperator::Opcode NegateComparison(BinaryOperator::Opcode op) { - // FIXME: This should probably be part of BinaryOperator, since this isn't - // the only place it's used. (This code was copied from SimpleSValBuilder.cpp.) - switch (op) { - default: - llvm_unreachable("Invalid opcode."); - case BO_LT: return BO_GE; - case BO_GT: return BO_LE; - case BO_LE: return BO_GT; - case BO_GE: return BO_LT; - case BO_EQ: return BO_NE; - case BO_NE: return BO_EQ; - } -} - ProgramStateRef SimpleConstraintManager::assumeAuxForSymbol(ProgramStateRef State, @@ -165,14 +159,12 @@ ProgramStateRef SimpleConstraintManager::assumeAux(ProgramStateRef state, return assumeAuxForSymbol(state, sym, Assumption); } - BasicValueFactory &BasicVals = getBasicVals(); - switch (Cond.getSubKind()) { default: llvm_unreachable("'Assume' not implemented for this NonLoc"); case nonloc::SymbolValKind: { - nonloc::SymbolVal& SV = cast<nonloc::SymbolVal>(Cond); + nonloc::SymbolVal SV = Cond.castAs<nonloc::SymbolVal>(); SymbolRef sym = SV.getSymbol(); assert(sym); @@ -181,36 +173,55 @@ ProgramStateRef SimpleConstraintManager::assumeAux(ProgramStateRef state, return assumeAuxForSymbol(state, sym, Assumption); // Handle symbolic expression. - } else { + } else if (const SymIntExpr *SE = dyn_cast<SymIntExpr>(sym)) { // We can only simplify expressions whose RHS is an integer. - const SymIntExpr *SE = dyn_cast<SymIntExpr>(sym); - if (!SE) - return assumeAuxForSymbol(state, sym, Assumption); BinaryOperator::Opcode op = SE->getOpcode(); - // Implicitly compare non-comparison expressions to 0. - if (!BinaryOperator::isComparisonOp(op)) { - QualType T = SE->getType(); - const llvm::APSInt &zero = BasicVals.getValue(0, T); - op = (Assumption ? BO_NE : BO_EQ); - return assumeSymRel(state, SE, op, zero); + if (BinaryOperator::isComparisonOp(op)) { + if (!Assumption) + op = BinaryOperator::negateComparisonOp(op); + + return assumeSymRel(state, SE->getLHS(), op, SE->getRHS()); } - // From here on out, op is the real comparison we'll be testing. - if (!Assumption) - op = NegateComparison(op); - return assumeSymRel(state, SE->getLHS(), op, SE->getRHS()); + } else if (const SymSymExpr *SSE = dyn_cast<SymSymExpr>(sym)) { + // Translate "a != b" to "(b - a) != 0". + // We invert the order of the operands as a heuristic for how loop + // conditions are usually written ("begin != end") as compared to length + // calculations ("end - begin"). The more correct thing to do would be to + // canonicalize "a - b" and "b - a", which would allow us to treat + // "a != b" and "b != a" the same. + SymbolManager &SymMgr = getSymbolManager(); + BinaryOperator::Opcode Op = SSE->getOpcode(); + 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 we get here, there's nothing else we can do but treat the symbol as + // opaque. + return assumeAuxForSymbol(state, sym, Assumption); } case nonloc::ConcreteIntKind: { - bool b = cast<nonloc::ConcreteInt>(Cond).getValue() != 0; + bool b = Cond.castAs<nonloc::ConcreteInt>().getValue() != 0; bool isFeasible = b ? Assumption : !Assumption; return isFeasible ? state : NULL; } case nonloc::LocAsIntegerKind: - return assumeAux(state, cast<nonloc::LocAsInteger>(Cond).getLoc(), + return assumeAux(state, Cond.castAs<nonloc::LocAsInteger>().getLoc(), Assumption); } // end switch } @@ -258,10 +269,14 @@ ProgramStateRef SimpleConstraintManager::assumeSymRel(ProgramStateRef state, APSIntType ComparisonType = std::max(WraparoundType, APSIntType(Int)); llvm::APSInt ConvertedInt = ComparisonType.convert(Int); + // Prefer unsigned comparisons. + if (ComparisonType.getBitWidth() == WraparoundType.getBitWidth() && + ComparisonType.isUnsigned() && !WraparoundType.isUnsigned()) + Adjustment.setIsSigned(false); + switch (op) { default: - // No logic yet for other operators. assume the constraint is feasible. - return state; + llvm_unreachable("invalid operation not caught by assertion above"); case BO_EQ: return assumeSymEQ(state, Sym, ConvertedInt, Adjustment); diff --git a/lib/StaticAnalyzer/Core/SimpleConstraintManager.h b/lib/StaticAnalyzer/Core/SimpleConstraintManager.h index 01f0b4e..10ddef1 100644 --- a/lib/StaticAnalyzer/Core/SimpleConstraintManager.h +++ b/lib/StaticAnalyzer/Core/SimpleConstraintManager.h @@ -23,10 +23,10 @@ namespace ento { class SimpleConstraintManager : public ConstraintManager { SubEngine *SU; - BasicValueFactory &BVF; + SValBuilder &SVB; public: - SimpleConstraintManager(SubEngine *subengine, BasicValueFactory &BV) - : SU(subengine), BVF(BV) {} + SimpleConstraintManager(SubEngine *subengine, SValBuilder &SB) + : SU(subengine), SVB(SB) {} virtual ~SimpleConstraintManager(); //===------------------------------------------------------------------===// @@ -81,7 +81,8 @@ protected: // Internal implementation. //===------------------------------------------------------------------===// - BasicValueFactory &getBasicVals() const { return BVF; } + BasicValueFactory &getBasicVals() const { return SVB.getBasicValueFactory(); } + SymbolManager &getSymbolManager() const { return SVB.getSymbolManager(); } bool canReasonAbout(SVal X) const; diff --git a/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp b/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp index fbc6ba0..5cc8926 100644 --- a/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp +++ b/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp @@ -11,8 +11,8 @@ // //===----------------------------------------------------------------------===// -#include "clang/StaticAnalyzer/Core/PathSensitive/APSIntType.h" #include "clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/APSIntType.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" using namespace clang; @@ -60,16 +60,16 @@ SValBuilder *ento::createSimpleSValBuilder(llvm::BumpPtrAllocator &alloc, //===----------------------------------------------------------------------===// SVal SimpleSValBuilder::dispatchCast(SVal Val, QualType CastTy) { - assert(isa<Loc>(&Val) || isa<NonLoc>(&Val)); - return isa<Loc>(Val) ? evalCastFromLoc(cast<Loc>(Val), CastTy) - : evalCastFromNonLoc(cast<NonLoc>(Val), CastTy); + assert(Val.getAs<Loc>() || Val.getAs<NonLoc>()); + return Val.getAs<Loc>() ? evalCastFromLoc(Val.castAs<Loc>(), CastTy) + : evalCastFromNonLoc(Val.castAs<NonLoc>(), CastTy); } SVal SimpleSValBuilder::evalCastFromNonLoc(NonLoc val, QualType castTy) { bool isLocType = Loc::isLocType(castTy); - if (nonloc::LocAsInteger *LI = dyn_cast<nonloc::LocAsInteger>(&val)) { + if (Optional<nonloc::LocAsInteger> LI = val.getAs<nonloc::LocAsInteger>()) { if (isLocType) return LI->getLoc(); @@ -98,15 +98,21 @@ SVal SimpleSValBuilder::evalCastFromNonLoc(NonLoc val, QualType castTy) { } // If value is a non integer constant, produce unknown. - if (!isa<nonloc::ConcreteInt>(val)) + if (!val.getAs<nonloc::ConcreteInt>()) return UnknownVal(); + // Handle casts to a boolean type. + if (castTy->isBooleanType()) { + bool b = val.castAs<nonloc::ConcreteInt>().getValue().getBoolValue(); + return makeTruthVal(b, castTy); + } + // Only handle casts from integers to integers - if val is an integer constant // being cast to a non integer type, produce unknown. if (!isLocType && !castTy->isIntegerType()) return UnknownVal(); - llvm::APSInt i = cast<nonloc::ConcreteInt>(val).getValue(); + llvm::APSInt i = val.castAs<nonloc::ConcreteInt>().getValue(); BasicVals.getAPSIntType(castTy).apply(i); if (isLocType) @@ -134,10 +140,10 @@ SVal SimpleSValBuilder::evalCastFromLoc(Loc val, QualType castTy) { if (castTy->isIntegerType()) { unsigned BitWidth = Context.getTypeSize(castTy); - if (!isa<loc::ConcreteInt>(val)) + if (!val.getAs<loc::ConcreteInt>()) return makeLocAsInteger(val, BitWidth); - llvm::APSInt i = cast<loc::ConcreteInt>(val).getValue(); + llvm::APSInt i = val.castAs<loc::ConcreteInt>().getValue(); BasicVals.getAPSIntType(castTy).apply(i); return makeIntVal(i); } @@ -155,7 +161,7 @@ SVal SimpleSValBuilder::evalCastFromLoc(Loc val, QualType castTy) { SVal SimpleSValBuilder::evalMinus(NonLoc val) { switch (val.getSubKind()) { case nonloc::ConcreteIntKind: - return cast<nonloc::ConcreteInt>(val).evalMinus(*this); + return val.castAs<nonloc::ConcreteInt>().evalMinus(*this); default: return UnknownVal(); } @@ -164,7 +170,7 @@ SVal SimpleSValBuilder::evalMinus(NonLoc val) { SVal SimpleSValBuilder::evalComplement(NonLoc X) { switch (X.getSubKind()) { case nonloc::ConcreteIntKind: - return cast<nonloc::ConcreteInt>(X).evalComplement(*this); + return X.castAs<nonloc::ConcreteInt>().evalComplement(*this); default: return UnknownVal(); } @@ -174,33 +180,6 @@ SVal SimpleSValBuilder::evalComplement(NonLoc X) { // Transfer function for binary operators. //===----------------------------------------------------------------------===// -static BinaryOperator::Opcode NegateComparison(BinaryOperator::Opcode op) { - switch (op) { - default: - llvm_unreachable("Invalid opcode."); - case BO_LT: return BO_GE; - case BO_GT: return BO_LE; - case BO_LE: return BO_GT; - case BO_GE: return BO_LT; - case BO_EQ: return BO_NE; - case BO_NE: return BO_EQ; - } -} - -static BinaryOperator::Opcode ReverseComparison(BinaryOperator::Opcode op) { - switch (op) { - default: - llvm_unreachable("Invalid opcode."); - case BO_LT: return BO_GT; - case BO_GT: return BO_LT; - case BO_LE: return BO_GE; - case BO_GE: return BO_LE; - case BO_EQ: - case BO_NE: - return op; - } -} - SVal SimpleSValBuilder::MakeSymIntVal(const SymExpr *LHS, BinaryOperator::Opcode op, const llvm::APSInt &RHS, @@ -331,15 +310,15 @@ SVal SimpleSValBuilder::evalBinOpNN(ProgramStateRef state, default: return makeSymExprValNN(state, op, lhs, rhs, resultTy); case nonloc::LocAsIntegerKind: { - Loc lhsL = cast<nonloc::LocAsInteger>(lhs).getLoc(); + Loc lhsL = lhs.castAs<nonloc::LocAsInteger>().getLoc(); switch (rhs.getSubKind()) { case nonloc::LocAsIntegerKind: return evalBinOpLL(state, op, lhsL, - cast<nonloc::LocAsInteger>(rhs).getLoc(), + rhs.castAs<nonloc::LocAsInteger>().getLoc(), resultTy); case nonloc::ConcreteIntKind: { // Transform the integer into a location and compare. - llvm::APSInt i = cast<nonloc::ConcreteInt>(rhs).getValue(); + llvm::APSInt i = rhs.castAs<nonloc::ConcreteInt>().getValue(); BasicVals.getAPSIntType(Context.VoidPtrTy).apply(i); return evalBinOpLL(state, op, lhsL, makeLoc(i), resultTy); } @@ -356,7 +335,7 @@ SVal SimpleSValBuilder::evalBinOpNN(ProgramStateRef state, } } case nonloc::ConcreteIntKind: { - llvm::APSInt LHSValue = cast<nonloc::ConcreteInt>(lhs).getValue(); + llvm::APSInt LHSValue = lhs.castAs<nonloc::ConcreteInt>().getValue(); // If we're dealing with two known constants, just perform the operation. if (const llvm::APSInt *KnownRHSValue = getKnownValue(state, rhs)) { @@ -392,7 +371,7 @@ SVal SimpleSValBuilder::evalBinOpNN(ProgramStateRef state, case BO_GT: case BO_LE: case BO_GE: - op = ReverseComparison(op); + op = BinaryOperator::reverseComparisonOp(op); // FALL-THROUGH case BO_EQ: case BO_NE: @@ -419,7 +398,7 @@ SVal SimpleSValBuilder::evalBinOpNN(ProgramStateRef state, } case nonloc::SymbolValKind: { // We only handle LHS as simple symbols or SymIntExprs. - SymbolRef Sym = cast<nonloc::SymbolVal>(lhs).getSymbol(); + SymbolRef Sym = lhs.castAs<nonloc::SymbolVal>().getSymbol(); // LHS is a symbolic expression. if (const SymIntExpr *symIntExpr = dyn_cast<SymIntExpr>(Sym)) { @@ -460,7 +439,7 @@ SVal SimpleSValBuilder::evalBinOpNN(ProgramStateRef state, case BO_EQ: case BO_NE: // Negate the comparison and make a value. - opc = NegateComparison(opc); + opc = BinaryOperator::negateComparisonOp(opc); assert(symIntExpr->getType() == resultTy); return makeNonLoc(symIntExpr->getLHS(), opc, symIntExpr->getRHS(), resultTy); @@ -502,22 +481,21 @@ SVal SimpleSValBuilder::evalBinOpNN(ProgramStateRef state, // Otherwise, make a SymIntExpr out of the expression. return MakeSymIntVal(symIntExpr, op, *RHSValue, resultTy); } + } - - } else if (isa<SymbolData>(Sym)) { - // Does the symbol simplify to a constant? If so, "fold" the constant - // by setting 'lhs' to a ConcreteInt and try again. - if (const llvm::APSInt *Constant = state->getConstraintManager() - .getSymVal(state, Sym)) { - lhs = nonloc::ConcreteInt(*Constant); - continue; - } - - // Is the RHS a constant? - if (const llvm::APSInt *RHSValue = getKnownValue(state, rhs)) - return MakeSymIntVal(Sym, op, *RHSValue, resultTy); + // Does the symbolic expression simplify to a constant? + // If so, "fold" the constant by setting 'lhs' to a ConcreteInt + // and try again. + ConstraintManager &CMgr = state->getConstraintManager(); + if (const llvm::APSInt *Constant = CMgr.getSymVal(state, Sym)) { + lhs = nonloc::ConcreteInt(*Constant); + continue; } + // Is the RHS a constant? + if (const llvm::APSInt *RHSValue = getKnownValue(state, rhs)) + return MakeSymIntVal(Sym, op, *RHSValue, resultTy); + // Give up -- this is not a symbolic expression we can handle. return makeSymExprValNN(state, op, InputLHS, InputRHS, resultTy); } @@ -595,25 +573,27 @@ SVal SimpleSValBuilder::evalBinOpLL(ProgramStateRef state, if (!BinaryOperator::isComparisonOp(op)) return UnknownVal(); - const llvm::APSInt &lVal = cast<loc::ConcreteInt>(lhs).getValue(); - return makeNonLoc(rSym, ReverseComparison(op), lVal, resultTy); + const llvm::APSInt &lVal = lhs.castAs<loc::ConcreteInt>().getValue(); + op = BinaryOperator::reverseComparisonOp(op); + return makeNonLoc(rSym, op, lVal, resultTy); } // If both operands are constants, just perform the operation. - if (loc::ConcreteInt *rInt = dyn_cast<loc::ConcreteInt>(&rhs)) { - SVal ResultVal = cast<loc::ConcreteInt>(lhs).evalBinOp(BasicVals, op, - *rInt); - if (Loc *Result = dyn_cast<Loc>(&ResultVal)) - return evalCastFromLoc(*Result, resultTy); - else - return UnknownVal(); + if (Optional<loc::ConcreteInt> rInt = rhs.getAs<loc::ConcreteInt>()) { + SVal ResultVal = + lhs.castAs<loc::ConcreteInt>().evalBinOp(BasicVals, op, *rInt); + if (Optional<NonLoc> Result = ResultVal.getAs<NonLoc>()) + return evalCastFromNonLoc(*Result, resultTy); + + assert(!ResultVal.getAs<Loc>() && "Loc-Loc ops should not produce Locs"); + return UnknownVal(); } // Special case comparisons against NULL. // This must come after the test if the RHS is a symbol, which is used to // build constraints. The address of any non-symbolic region is guaranteed // to be non-NULL, as is any label. - assert(isa<loc::MemRegionVal>(rhs) || isa<loc::GotoLabel>(rhs)); + assert(rhs.getAs<loc::MemRegionVal>() || rhs.getAs<loc::GotoLabel>()); if (lhs.isZeroConstant()) { switch (op) { default: @@ -634,7 +614,7 @@ SVal SimpleSValBuilder::evalBinOpLL(ProgramStateRef state, return UnknownVal(); } case loc::MemRegionKind: { - if (loc::ConcreteInt *rInt = dyn_cast<loc::ConcreteInt>(&rhs)) { + if (Optional<loc::ConcreteInt> rInt = rhs.getAs<loc::ConcreteInt>()) { // If one of the operands is a symbol and the other is a constant, // build an expression for use by the constraint manager. if (SymbolRef lSym = lhs.getAsLocSymbol()) @@ -676,11 +656,11 @@ SVal SimpleSValBuilder::evalBinOpLL(ProgramStateRef state, // regions, though. return UnknownVal(); - const MemSpaceRegion *LeftMS = LeftMR->getMemorySpace(); - const MemSpaceRegion *RightMS = RightMR->getMemorySpace(); - const MemSpaceRegion *UnknownMS = MemMgr.getUnknownRegion(); const MemRegion *LeftBase = LeftMR->getBaseRegion(); const MemRegion *RightBase = RightMR->getBaseRegion(); + const MemSpaceRegion *LeftMS = LeftBase->getMemorySpace(); + const MemSpaceRegion *RightMS = RightBase->getMemorySpace(); + const MemSpaceRegion *UnknownMS = MemMgr.getUnknownRegion(); // If the two regions are from different known memory spaces they cannot be // equal. Also, assume that no symbolic region (whose memory space is @@ -732,21 +712,21 @@ SVal SimpleSValBuilder::evalBinOpLL(ProgramStateRef state, // Get the left index and cast it to the correct type. // If the index is unknown or undefined, bail out here. SVal LeftIndexVal = LeftER->getIndex(); - NonLoc *LeftIndex = dyn_cast<NonLoc>(&LeftIndexVal); + Optional<NonLoc> LeftIndex = LeftIndexVal.getAs<NonLoc>(); if (!LeftIndex) return UnknownVal(); - LeftIndexVal = evalCastFromNonLoc(*LeftIndex, resultTy); - LeftIndex = dyn_cast<NonLoc>(&LeftIndexVal); + LeftIndexVal = evalCastFromNonLoc(*LeftIndex, ArrayIndexTy); + LeftIndex = LeftIndexVal.getAs<NonLoc>(); if (!LeftIndex) return UnknownVal(); // Do the same for the right index. SVal RightIndexVal = RightER->getIndex(); - NonLoc *RightIndex = dyn_cast<NonLoc>(&RightIndexVal); + Optional<NonLoc> RightIndex = RightIndexVal.getAs<NonLoc>(); if (!RightIndex) return UnknownVal(); - RightIndexVal = evalCastFromNonLoc(*RightIndex, resultTy); - RightIndex = dyn_cast<NonLoc>(&RightIndexVal); + RightIndexVal = evalCastFromNonLoc(*RightIndex, ArrayIndexTy); + RightIndex = RightIndexVal.getAs<NonLoc>(); if (!RightIndex) return UnknownVal(); @@ -783,7 +763,6 @@ SVal SimpleSValBuilder::evalBinOpLL(ProgramStateRef state, } // If we get here, we have no way of comparing the ElementRegions. - return UnknownVal(); } // See if both regions are fields of the same structure. @@ -836,6 +815,13 @@ SVal SimpleSValBuilder::evalBinOpLL(ProgramStateRef state, llvm_unreachable("Fields not found in parent record's definition"); } + // At this point we're not going to get a good answer, but we can try + // conjuring an expression instead. + SymbolRef LHSSym = lhs.getAsLocSymbol(); + SymbolRef RHSSym = rhs.getAsLocSymbol(); + if (LHSSym && RHSSym) + return makeNonLoc(LHSSym, op, RHSSym, resultTy); + // If we get here, we have no way of comparing the regions. return UnknownVal(); } @@ -852,11 +838,12 @@ SVal SimpleSValBuilder::evalBinOpLN(ProgramStateRef state, // Special case: 'rhs' is an integer that has the same width as a pointer and // we are using the integer location in a comparison. Normally this cannot be - // triggered, but transfer functions like those for OSCommpareAndSwapBarrier32 + // triggered, but transfer functions like those for OSCompareAndSwapBarrier32 // can generate comparisons that trigger this code. // FIXME: Are all locations guaranteed to have pointer width? if (BinaryOperator::isComparisonOp(op)) { - if (nonloc::ConcreteInt *rhsInt = dyn_cast<nonloc::ConcreteInt>(&rhs)) { + if (Optional<nonloc::ConcreteInt> rhsInt = + rhs.getAs<nonloc::ConcreteInt>()) { const llvm::APSInt *x = &rhsInt->getValue(); ASTContext &ctx = Context; if (ctx.getTypeSize(ctx.VoidPtrTy) == x->getBitWidth()) { @@ -873,8 +860,8 @@ SVal SimpleSValBuilder::evalBinOpLN(ProgramStateRef state, // We are dealing with pointer arithmetic. // Handle pointer arithmetic on constant values. - if (nonloc::ConcreteInt *rhsInt = dyn_cast<nonloc::ConcreteInt>(&rhs)) { - if (loc::ConcreteInt *lhsInt = dyn_cast<loc::ConcreteInt>(&lhs)) { + if (Optional<nonloc::ConcreteInt> rhsInt = rhs.getAs<nonloc::ConcreteInt>()) { + if (Optional<loc::ConcreteInt> lhsInt = lhs.getAs<loc::ConcreteInt>()) { const llvm::APSInt &leftI = lhsInt->getValue(); assert(leftI.isUnsigned()); llvm::APSInt rightI(rhsInt->getValue(), /* isUnsigned */ true); @@ -904,7 +891,7 @@ SVal SimpleSValBuilder::evalBinOpLN(ProgramStateRef state, // Handle cases where 'lhs' is a region. if (const MemRegion *region = lhs.getAsRegion()) { - rhs = cast<NonLoc>(convertToArrayIndex(rhs)); + rhs = convertToArrayIndex(rhs).castAs<NonLoc>(); SVal index = UnknownVal(); const MemRegion *superR = 0; QualType elementType; @@ -923,7 +910,7 @@ SVal SimpleSValBuilder::evalBinOpLN(ProgramStateRef state, elementType = resultTy->getPointeeType(); } - if (NonLoc *indexV = dyn_cast<NonLoc>(&index)) { + if (Optional<NonLoc> indexV = index.getAs<NonLoc>()) { return loc::MemRegionVal(MemMgr.getElementRegion(elementType, *indexV, superR, getContext())); } @@ -936,10 +923,10 @@ const llvm::APSInt *SimpleSValBuilder::getKnownValue(ProgramStateRef state, if (V.isUnknownOrUndef()) return NULL; - if (loc::ConcreteInt* X = dyn_cast<loc::ConcreteInt>(&V)) + if (Optional<loc::ConcreteInt> X = V.getAs<loc::ConcreteInt>()) return &X->getValue(); - if (nonloc::ConcreteInt* X = dyn_cast<nonloc::ConcreteInt>(&V)) + if (Optional<nonloc::ConcreteInt> X = V.getAs<nonloc::ConcreteInt>()) return &X->getValue(); if (SymbolRef Sym = V.getAsSymbol()) diff --git a/lib/StaticAnalyzer/Core/Store.cpp b/lib/StaticAnalyzer/Core/Store.cpp index 939ae54..a0c24fe 100644 --- a/lib/StaticAnalyzer/Core/Store.cpp +++ b/lib/StaticAnalyzer/Core/Store.cpp @@ -12,11 +12,11 @@ //===----------------------------------------------------------------------===// #include "clang/StaticAnalyzer/Core/PathSensitive/Store.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" -#include "clang/AST/CharUnits.h" #include "clang/AST/CXXInheritance.h" +#include "clang/AST/CharUnits.h" #include "clang/AST/DeclObjC.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" using namespace clang; using namespace ento; @@ -223,13 +223,38 @@ const MemRegion *StoreManager::castRegion(const MemRegion *R, QualType CastToTy) llvm_unreachable("unreachable"); } +static bool regionMatchesCXXRecordType(SVal V, QualType Ty) { + const MemRegion *MR = V.getAsRegion(); + if (!MR) + return true; + + const TypedValueRegion *TVR = dyn_cast<TypedValueRegion>(MR); + if (!TVR) + return true; + + const CXXRecordDecl *RD = TVR->getValueType()->getAsCXXRecordDecl(); + if (!RD) + return true; + + const CXXRecordDecl *Expected = Ty->getPointeeCXXRecordDecl(); + if (!Expected) + Expected = Ty->getAsCXXRecordDecl(); + + return Expected->getCanonicalDecl() == RD->getCanonicalDecl(); +} + SVal StoreManager::evalDerivedToBase(SVal Derived, const CastExpr *Cast) { + // Sanity check to avoid doing the wrong thing in the face of + // reinterpret_cast. + if (!regionMatchesCXXRecordType(Derived, Cast->getSubExpr()->getType())) + return UnknownVal(); + // Walk through the cast path to create nested CXXBaseRegions. SVal Result = Derived; for (CastExpr::path_const_iterator I = Cast->path_begin(), E = Cast->path_end(); I != E; ++I) { - Result = evalDerivedToBase(Result, (*I)->getType()); + Result = evalDerivedToBase(Result, (*I)->getType(), (*I)->isVirtual()); } return Result; } @@ -239,13 +264,16 @@ SVal StoreManager::evalDerivedToBase(SVal Derived, const CXXBasePath &Path) { SVal Result = Derived; for (CXXBasePath::const_iterator I = Path.begin(), E = Path.end(); I != E; ++I) { - Result = evalDerivedToBase(Result, I->Base->getType()); + Result = evalDerivedToBase(Result, I->Base->getType(), + I->Base->isVirtual()); } return Result; } -SVal StoreManager::evalDerivedToBase(SVal Derived, QualType BaseType) { - loc::MemRegionVal *DerivedRegVal = dyn_cast<loc::MemRegionVal>(&Derived); +SVal StoreManager::evalDerivedToBase(SVal Derived, QualType BaseType, + bool IsVirtual) { + Optional<loc::MemRegionVal> DerivedRegVal = + Derived.getAs<loc::MemRegionVal>(); if (!DerivedRegVal) return Derived; @@ -255,7 +283,8 @@ SVal StoreManager::evalDerivedToBase(SVal Derived, QualType BaseType) { assert(BaseDecl && "not a C++ object?"); const MemRegion *BaseReg = - MRMgr.getCXXBaseObjectRegion(BaseDecl, DerivedRegVal->getRegion()); + MRMgr.getCXXBaseObjectRegion(BaseDecl, DerivedRegVal->getRegion(), + IsVirtual); return loc::MemRegionVal(BaseReg); } @@ -264,7 +293,7 @@ SVal StoreManager::evalDynamicCast(SVal Base, QualType DerivedType, bool &Failed) { Failed = false; - loc::MemRegionVal *BaseRegVal = dyn_cast<loc::MemRegionVal>(&Base); + Optional<loc::MemRegionVal> BaseRegVal = Base.getAs<loc::MemRegionVal>(); if (!BaseRegVal) return UnknownVal(); const MemRegion *BaseRegion = BaseRegVal->stripCasts(/*StripBases=*/false); @@ -348,12 +377,12 @@ SVal StoreManager::getLValueFieldOrIvar(const Decl *D, SVal Base) { if (Base.isUnknownOrUndef()) return Base; - Loc BaseL = cast<Loc>(Base); + Loc BaseL = Base.castAs<Loc>(); const MemRegion* BaseR = 0; switch (BaseL.getSubKind()) { case loc::MemRegionKind: - BaseR = cast<loc::MemRegionVal>(BaseL).getRegion(); + BaseR = BaseL.castAs<loc::MemRegionVal>().getRegion(); break; case loc::GotoLabelKind: @@ -390,16 +419,16 @@ SVal StoreManager::getLValueElement(QualType elementType, NonLoc Offset, // FIXME: For absolute pointer addresses, we just return that value back as // well, although in reality we should return the offset added to that // value. - if (Base.isUnknownOrUndef() || isa<loc::ConcreteInt>(Base)) + if (Base.isUnknownOrUndef() || Base.getAs<loc::ConcreteInt>()) return Base; - const MemRegion* BaseRegion = cast<loc::MemRegionVal>(Base).getRegion(); + const MemRegion* BaseRegion = Base.castAs<loc::MemRegionVal>().getRegion(); // Pointer of any type can be cast and used as array base. const ElementRegion *ElemR = dyn_cast<ElementRegion>(BaseRegion); // Convert the offset to the appropriate size and signedness. - Offset = cast<NonLoc>(svalBuilder.convertToArrayIndex(Offset)); + Offset = svalBuilder.convertToArrayIndex(Offset).castAs<NonLoc>(); if (!ElemR) { // @@ -417,15 +446,16 @@ SVal StoreManager::getLValueElement(QualType elementType, NonLoc Offset, SVal BaseIdx = ElemR->getIndex(); - if (!isa<nonloc::ConcreteInt>(BaseIdx)) + if (!BaseIdx.getAs<nonloc::ConcreteInt>()) return UnknownVal(); - const llvm::APSInt& BaseIdxI = cast<nonloc::ConcreteInt>(BaseIdx).getValue(); + const llvm::APSInt &BaseIdxI = + BaseIdx.castAs<nonloc::ConcreteInt>().getValue(); // Only allow non-integer offsets if the base region has no offset itself. // FIXME: This is a somewhat arbitrary restriction. We should be using // SValBuilder here to add the two offsets without checking their types. - if (!isa<nonloc::ConcreteInt>(Offset)) { + if (!Offset.getAs<nonloc::ConcreteInt>()) { if (isa<ElementRegion>(BaseRegion->StripCasts())) return UnknownVal(); @@ -434,7 +464,7 @@ SVal StoreManager::getLValueElement(QualType elementType, NonLoc Offset, Ctx)); } - const llvm::APSInt& OffI = cast<nonloc::ConcreteInt>(Offset).getValue(); + const llvm::APSInt& OffI = Offset.castAs<nonloc::ConcreteInt>().getValue(); assert(BaseIdxI.isSigned()); // Compute the new index. diff --git a/lib/StaticAnalyzer/Core/SymbolManager.cpp b/lib/StaticAnalyzer/Core/SymbolManager.cpp index 0c5098b..de2f5bc 100644 --- a/lib/StaticAnalyzer/Core/SymbolManager.cpp +++ b/lib/StaticAnalyzer/Core/SymbolManager.cpp @@ -27,52 +27,33 @@ void SymExpr::dump() const { dumpToStream(llvm::errs()); } -static void print(raw_ostream &os, BinaryOperator::Opcode Op) { - switch (Op) { - default: - llvm_unreachable("operator printing not implemented"); - case BO_Mul: os << '*' ; break; - case BO_Div: os << '/' ; break; - case BO_Rem: os << '%' ; break; - case BO_Add: os << '+' ; break; - case BO_Sub: os << '-' ; break; - case BO_Shl: os << "<<" ; break; - case BO_Shr: os << ">>" ; break; - case BO_LT: os << "<" ; break; - case BO_GT: os << '>' ; break; - case BO_LE: os << "<=" ; break; - case BO_GE: os << ">=" ; break; - case BO_EQ: os << "==" ; break; - case BO_NE: os << "!=" ; break; - case BO_And: os << '&' ; break; - case BO_Xor: os << '^' ; break; - case BO_Or: os << '|' ; break; - } -} - void SymIntExpr::dumpToStream(raw_ostream &os) const { os << '('; getLHS()->dumpToStream(os); - os << ") "; - print(os, getOpcode()); - os << ' ' << getRHS().getZExtValue(); - if (getRHS().isUnsigned()) os << 'U'; + os << ") " + << BinaryOperator::getOpcodeStr(getOpcode()) << ' ' + << getRHS().getZExtValue(); + if (getRHS().isUnsigned()) + os << 'U'; } void IntSymExpr::dumpToStream(raw_ostream &os) const { - os << ' ' << getLHS().getZExtValue(); - if (getLHS().isUnsigned()) os << 'U'; - print(os, getOpcode()); - os << '('; + os << getLHS().getZExtValue(); + if (getLHS().isUnsigned()) + os << 'U'; + os << ' ' + << BinaryOperator::getOpcodeStr(getOpcode()) + << " ("; getRHS()->dumpToStream(os); - os << ") "; + os << ')'; } void SymSymExpr::dumpToStream(raw_ostream &os) const { os << '('; getLHS()->dumpToStream(os); - os << ") "; - os << '('; + os << ") " + << BinaryOperator::getOpcodeStr(getOpcode()) + << " ("; getRHS()->dumpToStream(os); os << ')'; } @@ -468,9 +449,7 @@ bool SymbolReaper::isLive(SymbolRef sym) { switch (sym->getKind()) { case SymExpr::RegionValueKind: - // FIXME: We should be able to use isLiveRegion here (this behavior - // predates isLiveRegion), but doing so causes test failures. Investigate. - KnownLive = true; + KnownLive = isLiveRegion(cast<SymbolRegionValue>(sym)->getRegion()); break; case SymExpr::ConjuredKind: KnownLive = false; diff --git a/lib/StaticAnalyzer/Core/TextPathDiagnostics.cpp b/lib/StaticAnalyzer/Core/TextPathDiagnostics.cpp index e09f4e3..d5706d6 100644 --- a/lib/StaticAnalyzer/Core/TextPathDiagnostics.cpp +++ b/lib/StaticAnalyzer/Core/TextPathDiagnostics.cpp @@ -12,8 +12,8 @@ //===----------------------------------------------------------------------===// #include "clang/StaticAnalyzer/Core/PathDiagnosticConsumers.h" -#include "clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h" #include "clang/Lex/Preprocessor.h" +#include "clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h" #include "llvm/Support/raw_ostream.h" using namespace clang; using namespace ento; @@ -46,7 +46,8 @@ public: } // end anonymous namespace -void ento::createTextPathDiagnosticConsumer(PathDiagnosticConsumers &C, +void ento::createTextPathDiagnosticConsumer(AnalyzerOptions &AnalyzerOpts, + PathDiagnosticConsumers &C, const std::string& out, const Preprocessor &PP) { C.push_back(new TextPathDiagnostics(out, PP.getDiagnostics())); |