diff options
Diffstat (limited to 'lib/StaticAnalyzer')
-rw-r--r-- | lib/StaticAnalyzer/Checkers/CallAndMessageChecker.cpp | 12 | ||||
-rw-r--r-- | lib/StaticAnalyzer/Checkers/DynamicTypePropagation.cpp | 87 | ||||
-rw-r--r-- | lib/StaticAnalyzer/Checkers/RetainCountChecker.cpp | 151 | ||||
-rw-r--r-- | lib/StaticAnalyzer/Core/AnalysisManager.cpp | 45 | ||||
-rw-r--r-- | lib/StaticAnalyzer/Core/BugReporter.cpp | 189 | ||||
-rw-r--r-- | lib/StaticAnalyzer/Core/BugReporterVisitors.cpp | 2 | ||||
-rw-r--r-- | lib/StaticAnalyzer/Core/CallEvent.cpp | 86 | ||||
-rw-r--r-- | lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp | 42 | ||||
-rw-r--r-- | lib/StaticAnalyzer/Core/HTMLDiagnostics.cpp | 48 | ||||
-rw-r--r-- | lib/StaticAnalyzer/Core/PathDiagnostic.cpp | 18 | ||||
-rw-r--r-- | lib/StaticAnalyzer/Core/PlistDiagnostics.cpp | 92 | ||||
-rw-r--r-- | lib/StaticAnalyzer/Core/ProgramState.cpp | 7 | ||||
-rw-r--r-- | lib/StaticAnalyzer/Core/TextPathDiagnostics.cpp | 12 | ||||
-rw-r--r-- | lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp | 74 |
14 files changed, 411 insertions, 454 deletions
diff --git a/lib/StaticAnalyzer/Checkers/CallAndMessageChecker.cpp b/lib/StaticAnalyzer/Checkers/CallAndMessageChecker.cpp index 30f45c7..5edcf09 100644 --- a/lib/StaticAnalyzer/Checkers/CallAndMessageChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/CallAndMessageChecker.cpp @@ -235,17 +235,20 @@ void CallAndMessageChecker::checkPreStmt(const CallExpr *CE, ProgramStateRef StNonNull, StNull; llvm::tie(StNonNull, StNull) = State->assume(cast<DefinedOrUnknownSVal>(L)); - // FIXME: Do we want to record the non-null assumption here? if (StNull && !StNonNull) { if (!BT_call_null) BT_call_null.reset( new BuiltinBug("Called function pointer is null (null dereference)")); emitBadCall(BT_call_null.get(), C, Callee); } + + C.addTransition(StNonNull); } void CallAndMessageChecker::checkPreCall(const CallEvent &Call, CheckerContext &C) const { + ProgramStateRef State = C.getState(); + // If this is a call to a C++ method, check if the callee is null or // undefined. if (const CXXInstanceCall *CC = dyn_cast<CXXInstanceCall>(&Call)) { @@ -258,11 +261,9 @@ void CallAndMessageChecker::checkPreCall(const CallEvent &Call, return; } - ProgramStateRef State = C.getState(); ProgramStateRef StNonNull, StNull; llvm::tie(StNonNull, StNull) = State->assume(cast<DefinedOrUnknownSVal>(V)); - // FIXME: Do we want to record the non-null assumption here? if (StNull && !StNonNull) { if (!BT_cxx_call_null) BT_cxx_call_null.reset(new BuiltinBug("Called C++ object pointer " @@ -270,6 +271,8 @@ void CallAndMessageChecker::checkPreCall(const CallEvent &Call, emitBadCall(BT_cxx_call_null.get(), C, CC->getCXXThisExpr()); return; } + + State = StNonNull; } // Don't check for uninitialized field values in arguments if the @@ -291,6 +294,9 @@ void CallAndMessageChecker::checkPreCall(const CallEvent &Call, Call.getArgExpr(i), /*IsFirstArgument=*/i == 0, checkUninitFields, Call, *BT)) return; + + // If we make it here, record our assumptions about the callee. + C.addTransition(State); } void CallAndMessageChecker::checkPreObjCMessage(const ObjCMethodCall &msg, diff --git a/lib/StaticAnalyzer/Checkers/DynamicTypePropagation.cpp b/lib/StaticAnalyzer/Checkers/DynamicTypePropagation.cpp index fea5733..b636efb 100644 --- a/lib/StaticAnalyzer/Checkers/DynamicTypePropagation.cpp +++ b/lib/StaticAnalyzer/Checkers/DynamicTypePropagation.cpp @@ -25,7 +25,8 @@ using namespace ento; namespace { class DynamicTypePropagation: - public Checker< check::PostCall, + public Checker< check::PreCall, + check::PostCall, check::PostStmt<ImplicitCastExpr> > { const ObjCObjectType *getObjectTypeForAllocAndNew(const ObjCMessageExpr *MsgE, CheckerContext &C) const; @@ -34,11 +35,70 @@ class DynamicTypePropagation: const ObjCObjectPointerType *getBetterObjCType(const Expr *CastE, CheckerContext &C) const; public: + void checkPreCall(const CallEvent &Call, CheckerContext &C) const; void checkPostCall(const CallEvent &Call, CheckerContext &C) const; void checkPostStmt(const ImplicitCastExpr *CastE, CheckerContext &C) const; }; } +static void recordFixedType(const MemRegion *Region, const CXXMethodDecl *MD, + CheckerContext &C) { + assert(Region); + assert(MD); + + ASTContext &Ctx = C.getASTContext(); + QualType Ty = Ctx.getPointerType(Ctx.getRecordType(MD->getParent())); + + ProgramStateRef State = C.getState(); + State = State->setDynamicTypeInfo(Region, Ty, /*CanBeSubclass=*/false); + C.addTransition(State); + return; +} + +void DynamicTypePropagation::checkPreCall(const CallEvent &Call, + CheckerContext &C) const { + if (const CXXConstructorCall *Ctor = dyn_cast<CXXConstructorCall>(&Call)) { + // C++11 [class.cdtor]p4: When a virtual function is called directly or + // indirectly from a constructor or from a destructor, including during + // the construction or destruction of the class’s non-static data members, + // and the object to which the call applies is the object under + // construction or destruction, the function called is the final overrider + // in the constructor's or destructor's class and not one overriding it in + // a more-derived class. + + switch (Ctor->getOriginExpr()->getConstructionKind()) { + case CXXConstructExpr::CK_Complete: + case CXXConstructExpr::CK_Delegating: + // No additional type info necessary. + return; + case CXXConstructExpr::CK_NonVirtualBase: + case CXXConstructExpr::CK_VirtualBase: + if (const MemRegion *Target = Ctor->getCXXThisVal().getAsRegion()) + recordFixedType(Target, Ctor->getDecl(), C); + return; + } + + return; + } + + if (const CXXDestructorCall *Dtor = dyn_cast<CXXDestructorCall>(&Call)) { + // C++11 [class.cdtor]p4 (see above) + + const MemRegion *Target = Dtor->getCXXThisVal().getAsRegion(); + if (!Target) + return; + + // FIXME: getRuntimeDefinition() can be expensive. It would be better to do + // this when we are entering the stack frame for the destructor. + const Decl *D = Dtor->getRuntimeDefinition().getDecl(); + if (!D) + return; + + recordFixedType(Target, cast<CXXDestructorDecl>(D), C); + return; + } +} + void DynamicTypePropagation::checkPostCall(const CallEvent &Call, CheckerContext &C) const { // We can obtain perfect type info for return values from some calls. @@ -82,6 +142,31 @@ void DynamicTypePropagation::checkPostCall(const CallEvent &Call, break; } } + + return; + } + + if (const CXXConstructorCall *Ctor = dyn_cast<CXXConstructorCall>(&Call)) { + // We may need to undo the effects of our pre-call check. + switch (Ctor->getOriginExpr()->getConstructionKind()) { + case CXXConstructExpr::CK_Complete: + case CXXConstructExpr::CK_Delegating: + // No additional work necessary. + // Note: This will leave behind the actual type of the object for + // complete constructors, but arguably that's a good thing, since it + // means the dynamic type info will be correct even for objects + // constructed with operator new. + return; + case CXXConstructExpr::CK_NonVirtualBase: + case CXXConstructExpr::CK_VirtualBase: + if (const MemRegion *Target = Ctor->getCXXThisVal().getAsRegion()) { + // We just finished a base constructor. Now we can use the subclass's + // type when resolving virtual calls. + const Decl *D = C.getLocationContext()->getDecl(); + recordFixedType(Target, cast<CXXConstructorDecl>(D), C); + } + return; + } } } diff --git a/lib/StaticAnalyzer/Checkers/RetainCountChecker.cpp b/lib/StaticAnalyzer/Checkers/RetainCountChecker.cpp index 5503b23..3c00d99 100644 --- a/lib/StaticAnalyzer/Checkers/RetainCountChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/RetainCountChecker.cpp @@ -1107,18 +1107,6 @@ RetainSummaryManager::getFunctionSummary(const FunctionDecl *FD) { if (S) break; - // Enable this code once the semantics of NSDeallocateObject are resolved - // for GC. <rdar://problem/6619988> -#if 0 - // Handle: NSDeallocateObject(id anObject); - // This method does allow 'nil' (although we don't check it now). - if (strcmp(FName, "NSDeallocateObject") == 0) { - return RetTy == Ctx.VoidTy - ? getPersistentSummary(RetEffect::MakeNoRet(), DoNothing, Dealloc) - : getPersistentStopSummary(); - } -#endif - if (RetTy->isPointerType()) { // For CoreFoundation ('CF') types. if (cocoa::isRefType(RetTy, "CF", FName)) { @@ -1591,28 +1579,12 @@ void RetainSummaryManager::InitializeMethodSummaries() { addClassMethSummary("NSWindow", "alloc", NoTrackYet); -#if 0 - addInstMethSummary("NSWindow", NoTrackYet, "initWithContentRect", - "styleMask", "backing", "defer", NULL); - - addInstMethSummary("NSWindow", NoTrackYet, "initWithContentRect", - "styleMask", "backing", "defer", "screen", NULL); -#endif - // For NSPanel (which subclasses NSWindow), allocated objects are not // self-owned. // FIXME: For now we don't track NSPanels. object for the same reason // as for NSWindow objects. addClassMethSummary("NSPanel", "alloc", NoTrackYet); -#if 0 - addInstMethSummary("NSPanel", NoTrackYet, "initWithContentRect", - "styleMask", "backing", "defer", NULL); - - addInstMethSummary("NSPanel", NoTrackYet, "initWithContentRect", - "styleMask", "backing", "defer", "screen", NULL); -#endif - // Don't track allocated autorelease pools yet, as it is okay to prematurely // exit a method. addClassMethSummary("NSAutoreleasePool", "alloc", NoTrackYet); @@ -1636,62 +1608,6 @@ void RetainSummaryManager::InitializeMethodSummaries() { } //===----------------------------------------------------------------------===// -// AutoreleaseBindings - State used to track objects in autorelease pools. -//===----------------------------------------------------------------------===// -#define AUTORELEASE_POOL_MODELING (0) -// We do not currently have complete modeling of autorelease pools. - -#if AUTORELEASE_POOL_MODELING - -typedef llvm::ImmutableMap<SymbolRef, unsigned> ARCounts; -typedef llvm::ImmutableMap<SymbolRef, ARCounts> ARPoolContents; -typedef llvm::ImmutableList<SymbolRef> ARStack; - -static int AutoRCIndex = 0; -static int AutoRBIndex = 0; - -namespace { class AutoreleasePoolContents {}; } -namespace { class AutoreleaseStack {}; } - -namespace clang { -namespace ento { -template<> struct ProgramStateTrait<AutoreleaseStack> - : public ProgramStatePartialTrait<ARStack> { - static inline void *GDMIndex() { return &AutoRBIndex; } -}; - -template<> struct ProgramStateTrait<AutoreleasePoolContents> - : public ProgramStatePartialTrait<ARPoolContents> { - static inline void *GDMIndex() { return &AutoRCIndex; } -}; -} // end GR namespace -} // end clang namespace - -static SymbolRef GetCurrentAutoreleasePool(ProgramStateRef state) { - ARStack stack = state->get<AutoreleaseStack>(); - return stack.isEmpty() ? SymbolRef() : stack.getHead(); -} - -static ProgramStateRef -SendAutorelease(ProgramStateRef state, - ARCounts::Factory &F, - SymbolRef sym) { - SymbolRef pool = GetCurrentAutoreleasePool(state); - const ARCounts *cnts = state->get<AutoreleasePoolContents>(pool); - ARCounts newCnts(0); - - if (cnts) { - const unsigned *cnt = (*cnts).lookup(sym); - newCnts = F.add(*cnts, sym, cnt ? *cnt + 1 : 1); - } - else - newCnts = F.add(F.getEmptyMap(), sym, 1); - - return state->set<AutoreleasePoolContents>(pool, newCnts); -} -#endif - -//===----------------------------------------------------------------------===// // Error reporting. //===----------------------------------------------------------------------===// namespace { @@ -2431,11 +2347,6 @@ class RetainCountChecker mutable OwningPtr<RetainSummaryManager> Summaries; mutable OwningPtr<RetainSummaryManager> SummariesGC; - -#if AUTORELEASE_POOL_MODELING - mutable ARCounts::Factory ARCountFactory; -#endif - mutable SummaryLogTy SummaryLog; mutable bool ShouldResetSummaryLog; @@ -2892,15 +2803,7 @@ void RetainCountChecker::checkSummary(const RetainSummary &Summ, ResultTy)); // FIXME: Add a flag to the checker where allocations are assumed to - // *not* fail. (The code below is out-of-date, though.) -#if 0 - if (RE.getKind() == RetEffect::OwnedAllocatedSymbol) { - bool isFeasible; - state = state.assume(loc::SymbolVal(Sym), true, isFeasible); - assert(isFeasible && "Cannot assume fresh symbol is non-null."); - } -#endif - + // *not* fail. break; } @@ -3011,9 +2914,6 @@ RetainCountChecker::updateSymbol(ProgramStateRef state, SymbolRef sym, case NewAutoreleasePool: assert(!C.isObjCGCEnabled()); -#if AUTORELEASE_POOL_MODELING - state = state->add<AutoreleaseStack>(sym); -#endif return state; case MayEscape: @@ -3030,13 +2930,7 @@ RetainCountChecker::updateSymbol(ProgramStateRef state, SymbolRef sym, case Autorelease: if (C.isObjCGCEnabled()) return state; - // Update the autorelease counts. - // TODO: AutoreleasePoolContents are not currently used. We will need to - // call SendAutorelease after it's wired up. -#if AUTORELEASE_POOL_MODELING - state = SendAutorelease(state, ARCountFactory, sym); -#endif V = V.autorelease(); break; @@ -3719,35 +3613,6 @@ void RetainCountChecker::checkDeadSymbols(SymbolReaper &SymReaper, C.addTransition(state, Pred); } -//===----------------------------------------------------------------------===// -// Debug printing of refcount bindings and autorelease pools. -//===----------------------------------------------------------------------===// - -#if AUTORELEASE_POOL_MODELING -static void PrintPool(raw_ostream &Out, SymbolRef Sym, - ProgramStateRef State) { - Out << ' '; - if (Sym) - Sym->dumpToStream(Out); - else - Out << "<pool>"; - Out << ":{"; - - // Get the contents of the pool. - if (const ARCounts *Cnts = State->get<AutoreleasePoolContents>(Sym)) - for (ARCounts::iterator I = Cnts->begin(), E = Cnts->end(); I != E; ++I) - Out << '(' << I.getKey() << ',' << I.getData() << ')'; - Out << '}'; -} - -static bool UsesAutorelease(ProgramStateRef state) { - // A state uses autorelease if it allocated an autorelease pool or if it has - // objects in the caller's autorelease pool. - return !state->get<AutoreleaseStack>().isEmpty() || - state->get<AutoreleasePoolContents>(SymbolRef()); -} -#endif - void RetainCountChecker::printState(raw_ostream &Out, ProgramStateRef State, const char *NL, const char *Sep) const { @@ -3761,20 +3626,6 @@ void RetainCountChecker::printState(raw_ostream &Out, ProgramStateRef State, I->second.print(Out); Out << NL; } - -#if AUTORELEASE_POOL_MODELING - // Print the autorelease stack. - if (UsesAutorelease(State)) { - Out << Sep << NL << "AR pool stack:"; - ARStack Stack = State->get<AutoreleaseStack>(); - - PrintPool(Out, SymbolRef(), State); // Print the caller's pool. - for (ARStack::iterator I = Stack.begin(), E = Stack.end(); I != E; ++I) - PrintPool(Out, *I, State); - - Out << NL; - } -#endif } //===----------------------------------------------------------------------===// diff --git a/lib/StaticAnalyzer/Core/AnalysisManager.cpp b/lib/StaticAnalyzer/Core/AnalysisManager.cpp index 5aac640..efeba17 100644 --- a/lib/StaticAnalyzer/Core/AnalysisManager.cpp +++ b/lib/StaticAnalyzer/Core/AnalysisManager.cpp @@ -16,7 +16,7 @@ void AnalysisManager::anchor() { } AnalysisManager::AnalysisManager(ASTContext &ctx, DiagnosticsEngine &diags, const LangOptions &lang, - PathDiagnosticConsumer *pd, + const PathDiagnosticConsumers &PDC, StoreManagerCreator storemgr, ConstraintManagerCreator constraintmgr, CheckerManager *checkerMgr, @@ -33,7 +33,8 @@ AnalysisManager::AnalysisManager(ASTContext &ctx, DiagnosticsEngine &diags, AnalysisInliningMode IMode, bool NoRetry) : AnaCtxMgr(useUnoptimizedCFG, addImplicitDtors, /*addInitializers=*/true), - Ctx(ctx), Diags(diags), LangOpts(lang), PD(pd), + Ctx(ctx), Diags(diags), LangOpts(lang), + PathConsumers(PDC), CreateStoreMgr(storemgr), CreateConstraintMgr(constraintmgr), CheckerMgr(checkerMgr), MaxNodes(maxnodes), MaxVisit(maxvisit), @@ -49,29 +50,19 @@ AnalysisManager::AnalysisManager(ASTContext &ctx, DiagnosticsEngine &diags, AnaCtxMgr.getCFGBuildOptions().setAllAlwaysAdd(); } -AnalysisManager::AnalysisManager(ASTContext &ctx, DiagnosticsEngine &diags, - AnalysisManager &ParentAM) - : AnaCtxMgr(ParentAM.AnaCtxMgr.getUseUnoptimizedCFG(), - ParentAM.AnaCtxMgr.getCFGBuildOptions().AddImplicitDtors, - ParentAM.AnaCtxMgr.getCFGBuildOptions().AddInitializers), - Ctx(ctx), Diags(diags), - LangOpts(ParentAM.LangOpts), PD(ParentAM.getPathDiagnosticConsumer()), - CreateStoreMgr(ParentAM.CreateStoreMgr), - CreateConstraintMgr(ParentAM.CreateConstraintMgr), - CheckerMgr(ParentAM.CheckerMgr), - MaxNodes(ParentAM.MaxNodes), - MaxVisit(ParentAM.MaxVisit), - VisualizeEGDot(ParentAM.VisualizeEGDot), - VisualizeEGUbi(ParentAM.VisualizeEGUbi), - PurgeDead(ParentAM.PurgeDead), - EagerlyAssume(ParentAM.EagerlyAssume), - TrimGraph(ParentAM.TrimGraph), - EagerlyTrimEGraph(ParentAM.EagerlyTrimEGraph), - IPAMode(ParentAM.IPAMode), - InlineMaxStackDepth(ParentAM.InlineMaxStackDepth), - InlineMaxFunctionSize(ParentAM.InlineMaxFunctionSize), - InliningMode(ParentAM.InliningMode), - NoRetryExhausted(ParentAM.NoRetryExhausted) -{ - AnaCtxMgr.getCFGBuildOptions().setAllAlwaysAdd(); +AnalysisManager::~AnalysisManager() { + FlushDiagnostics(); + for (PathDiagnosticConsumers::iterator I = PathConsumers.begin(), + E = PathConsumers.end(); I != E; ++I) { + delete *I; + } +} + +void AnalysisManager::FlushDiagnostics() { + PathDiagnosticConsumer::FilesMade filesMade; + for (PathDiagnosticConsumers::iterator I = PathConsumers.begin(), + E = PathConsumers.end(); + I != E; ++I) { + (*I)->FlushDiagnostics(&filesMade); + } } diff --git a/lib/StaticAnalyzer/Core/BugReporter.cpp b/lib/StaticAnalyzer/Core/BugReporter.cpp index 7ba2fa7..571baec 100644 --- a/lib/StaticAnalyzer/Core/BugReporter.cpp +++ b/lib/StaticAnalyzer/Core/BugReporter.cpp @@ -1345,6 +1345,9 @@ BugReport::~BugReport() { for (visitor_iterator I = visitor_begin(), E = visitor_end(); I != E; ++I) { delete *I; } + while (!interestingSymbols.empty()) { + popInterestingSymbolsAndRegions(); + } } const Decl *BugReport::getDeclWithIssue() const { @@ -1386,11 +1389,11 @@ void BugReport::markInteresting(SymbolRef sym) { return; // If the symbol wasn't already in our set, note a configuration change. - if (interestingSymbols.insert(sym).second) + if (getInterestingSymbols().insert(sym).second) ++ConfigurationChangeToken; if (const SymbolMetadata *meta = dyn_cast<SymbolMetadata>(sym)) - interestingRegions.insert(meta->getRegion()); + getInterestingRegions().insert(meta->getRegion()); } void BugReport::markInteresting(const MemRegion *R) { @@ -1399,11 +1402,11 @@ void BugReport::markInteresting(const MemRegion *R) { // If the base region wasn't already in our set, note a configuration change. R = R->getBaseRegion(); - if (interestingRegions.insert(R).second) + if (getInterestingRegions().insert(R).second) ++ConfigurationChangeToken; if (const SymbolicRegion *SR = dyn_cast<SymbolicRegion>(R)) - interestingSymbols.insert(SR->getSymbol()); + getInterestingSymbols().insert(SR->getSymbol()); } void BugReport::markInteresting(SVal V) { @@ -1411,30 +1414,58 @@ void BugReport::markInteresting(SVal V) { markInteresting(V.getAsSymbol()); } -bool BugReport::isInteresting(SVal V) const { +bool BugReport::isInteresting(SVal V) { return isInteresting(V.getAsRegion()) || isInteresting(V.getAsSymbol()); } -bool BugReport::isInteresting(SymbolRef sym) const { +bool BugReport::isInteresting(SymbolRef sym) { if (!sym) return false; // We don't currently consider metadata symbols to be interesting // even if we know their region is interesting. Is that correct behavior? - return interestingSymbols.count(sym); + return getInterestingSymbols().count(sym); } -bool BugReport::isInteresting(const MemRegion *R) const { +bool BugReport::isInteresting(const MemRegion *R) { if (!R) return false; R = R->getBaseRegion(); - bool b = interestingRegions.count(R); + bool b = getInterestingRegions().count(R); if (b) return true; if (const SymbolicRegion *SR = dyn_cast<SymbolicRegion>(R)) - return interestingSymbols.count(SR->getSymbol()); + return getInterestingSymbols().count(SR->getSymbol()); return false; } - + +void BugReport::lazyInitializeInterestingSets() { + if (interestingSymbols.empty()) { + interestingSymbols.push_back(new Symbols()); + interestingRegions.push_back(new Regions()); + } +} + +BugReport::Symbols &BugReport::getInterestingSymbols() { + lazyInitializeInterestingSets(); + return *interestingSymbols.back(); +} + +BugReport::Regions &BugReport::getInterestingRegions() { + lazyInitializeInterestingSets(); + return *interestingRegions.back(); +} + +void BugReport::pushInterestingSymbolsAndRegions() { + interestingSymbols.push_back(new Symbols(getInterestingSymbols())); + interestingRegions.push_back(new Regions(getInterestingRegions())); +} + +void BugReport::popInterestingSymbolsAndRegions() { + delete interestingSymbols.back(); + interestingSymbols.pop_back(); + delete interestingRegions.back(); + interestingRegions.pop_back(); +} const Stmt *BugReport::getStmt() const { if (!ErrorNode) @@ -1793,12 +1824,13 @@ static void CompactPathDiagnostic(PathPieces &path, const SourceManager& SM) { } void GRBugReporter::GeneratePathDiagnostic(PathDiagnostic& PD, - SmallVectorImpl<BugReport *> &bugReports) { + PathDiagnosticConsumer &PC, + ArrayRef<BugReport *> &bugReports) { assert(!bugReports.empty()); SmallVector<const ExplodedNode *, 10> errorNodes; - for (SmallVectorImpl<BugReport*>::iterator I = bugReports.begin(), - E = bugReports.end(); I != E; ++I) { + for (ArrayRef<BugReport*>::iterator I = bugReports.begin(), + E = bugReports.end(); I != E; ++I) { errorNodes.push_back((*I)->getErrorNode()); } @@ -1818,8 +1850,7 @@ void GRBugReporter::GeneratePathDiagnostic(PathDiagnostic& PD, const ExplodedNode *N = GPair.second.first; // Start building the path diagnostic... - PathDiagnosticBuilder PDB(*this, R, BackMap.get(), - getPathDiagnosticConsumer()); + PathDiagnosticBuilder PDB(*this, R, BackMap.get(), &PC); // Register additional node visitors. R->addVisitor(new NilReceiverBRVisitor()); @@ -1867,6 +1898,8 @@ void GRBugReporter::GeneratePathDiagnostic(PathDiagnostic& PD, case PathDiagnosticConsumer::Minimal: GenerateMinimalPathDiagnostic(PD, PDB, N, visitors); break; + case PathDiagnosticConsumer::None: + llvm_unreachable("PathDiagnosticConsumer::None should never appear here"); } // Clean up the visitors we used. @@ -2022,53 +2055,21 @@ FindReportInEquivalenceClass(BugReportEquivClass& EQ, return exampleReport; } -//===----------------------------------------------------------------------===// -// DiagnosticCache. This is a hack to cache analyzer diagnostics. It -// uses global state, which eventually should go elsewhere. -//===----------------------------------------------------------------------===// -namespace { -class DiagCacheItem : public llvm::FoldingSetNode { - llvm::FoldingSetNodeID ID; -public: - DiagCacheItem(BugReport *R, PathDiagnostic *PD) { - R->Profile(ID); - PD->Profile(ID); - } - - void Profile(llvm::FoldingSetNodeID &id) { - id = ID; - } - - llvm::FoldingSetNodeID &getID() { return ID; } -}; -} - -static bool IsCachedDiagnostic(BugReport *R, PathDiagnostic *PD) { - // FIXME: Eventually this diagnostic cache should reside in something - // like AnalysisManager instead of being a static variable. This is - // really unsafe in the long term. - typedef llvm::FoldingSet<DiagCacheItem> DiagnosticCache; - static DiagnosticCache DC; - - void *InsertPos; - DiagCacheItem *Item = new DiagCacheItem(R, PD); - - if (DC.FindNodeOrInsertPos(Item->getID(), InsertPos)) { - delete Item; - return true; - } - - DC.InsertNode(Item, InsertPos); - return false; -} - void BugReporter::FlushReport(BugReportEquivClass& EQ) { SmallVector<BugReport*, 10> bugReports; BugReport *exampleReport = FindReportInEquivalenceClass(EQ, bugReports); - if (!exampleReport) - return; - - PathDiagnosticConsumer* PD = getPathDiagnosticConsumer(); + if (exampleReport) { + const PathDiagnosticConsumers &C = getPathDiagnosticConsumers(); + for (PathDiagnosticConsumers::const_iterator I=C.begin(), + E=C.end(); I != E; ++I) { + FlushReport(exampleReport, **I, bugReports); + } + } +} + +void BugReporter::FlushReport(BugReport *exampleReport, + PathDiagnosticConsumer &PD, + ArrayRef<BugReport*> bugReports) { // FIXME: Make sure we use the 'R' for the path that was actually used. // Probably doesn't make a difference in practice. @@ -2077,65 +2078,39 @@ void BugReporter::FlushReport(BugReportEquivClass& EQ) { OwningPtr<PathDiagnostic> D(new PathDiagnostic(exampleReport->getDeclWithIssue(), exampleReport->getBugType().getName(), - !PD || PD->useVerboseDescription() + PD.useVerboseDescription() ? exampleReport->getDescription() : exampleReport->getShortDescription(), BT.getCategory())); - if (!bugReports.empty()) - GeneratePathDiagnostic(*D.get(), bugReports); - - // Get the meta data. - const BugReport::ExtraTextList &Meta = - exampleReport->getExtraText(); - for (BugReport::ExtraTextList::const_iterator i = Meta.begin(), - e = Meta.end(); i != e; ++i) { - D->addMeta(*i); - } - - // Emit a summary diagnostic to the regular Diagnostics engine. - BugReport::ranges_iterator Beg, End; - llvm::tie(Beg, End) = exampleReport->getRanges(); - DiagnosticsEngine &Diag = getDiagnostic(); - - if (!IsCachedDiagnostic(exampleReport, D.get())) { - // Search the description for '%', as that will be interpretted as a - // format character by FormatDiagnostics. - StringRef desc = exampleReport->getShortDescription(); - - SmallString<512> TmpStr; - llvm::raw_svector_ostream Out(TmpStr); - for (StringRef::iterator I=desc.begin(), E=desc.end(); I!=E; ++I) { - if (*I == '%') - Out << "%%"; - else - Out << *I; - } - - Out.flush(); - unsigned ErrorDiag = Diag.getCustomDiagID(DiagnosticsEngine::Warning, TmpStr); - - DiagnosticBuilder diagBuilder = Diag.Report( - exampleReport->getLocation(getSourceManager()).asLocation(), ErrorDiag); - for (BugReport::ranges_iterator I = Beg; I != End; ++I) - diagBuilder << *I; + // Generate the full path diagnostic, using the generation scheme + // specified by the PathDiagnosticConsumer. + if (PD.getGenerationScheme() != PathDiagnosticConsumer::None) { + if (!bugReports.empty()) + GeneratePathDiagnostic(*D.get(), PD, bugReports); } - // Emit a full diagnostic for the path if we have a PathDiagnosticConsumer. - if (!PD) - return; - + // If the path is empty, generate a single step path with the location + // of the issue. if (D->path.empty()) { - PathDiagnosticPiece *piece = new PathDiagnosticEventPiece( - exampleReport->getLocation(getSourceManager()), - exampleReport->getDescription()); + PathDiagnosticLocation L = exampleReport->getLocation(getSourceManager()); + PathDiagnosticPiece *piece = + new PathDiagnosticEventPiece(L, exampleReport->getDescription()); + BugReport::ranges_iterator Beg, End; + llvm::tie(Beg, End) = exampleReport->getRanges(); for ( ; Beg != End; ++Beg) piece->addRange(*Beg); - D->getActivePath().push_back(piece); } - PD->HandlePathDiagnostic(D.take()); + // Get the meta data. + const BugReport::ExtraTextList &Meta = exampleReport->getExtraText(); + for (BugReport::ExtraTextList::const_iterator i = Meta.begin(), + e = Meta.end(); i != e; ++i) { + D->addMeta(*i); + } + + PD.HandlePathDiagnostic(D.take()); } void BugReporter::EmitBasicReport(const Decl *DeclWithIssue, diff --git a/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp b/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp index 46aa9e2..e729587 100644 --- a/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp +++ b/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp @@ -323,7 +323,7 @@ void bugreporter::addTrackNullOrUndefValueVisitor(const ExplodedNode *N, // Walk through lvalue-to-rvalue conversions. const Expr *Ex = dyn_cast<Expr>(S); if (Ex) { - Ex = Ex->IgnoreParenLValueCasts(); + Ex = Ex->IgnoreParenCasts(); if (const DeclRefExpr *DR = dyn_cast<DeclRefExpr>(Ex)) { if (const VarDecl *VD = dyn_cast<VarDecl>(DR->getDecl())) { const VarRegion *R = diff --git a/lib/StaticAnalyzer/Core/CallEvent.cpp b/lib/StaticAnalyzer/Core/CallEvent.cpp index e3f4c61..5345bd5 100644 --- a/lib/StaticAnalyzer/Core/CallEvent.cpp +++ b/lib/StaticAnalyzer/Core/CallEvent.cpp @@ -207,10 +207,14 @@ SourceRange CallEvent::getArgSourceRange(unsigned Index) const { return ArgE->getSourceRange(); } +void CallEvent::dump() const { + dump(llvm::errs()); +} + void CallEvent::dump(raw_ostream &Out) const { ASTContext &Ctx = getState()->getStateManager().getContext(); if (const Expr *E = getOriginExpr()) { - E->printPretty(Out, Ctx, 0, Ctx.getPrintingPolicy()); + E->printPretty(Out, 0, Ctx.getPrintingPolicy()); Out << "\n"; return; } @@ -372,47 +376,49 @@ void CXXInstanceCall::getExtraInvalidatedRegions(RegionList &Regions) const { Regions.push_back(R); } -static const CXXMethodDecl *devirtualize(const CXXMethodDecl *MD, SVal ThisVal){ - const MemRegion *R = ThisVal.getAsRegion(); - if (!R) - return 0; - - const TypedValueRegion *TR = dyn_cast<TypedValueRegion>(R->StripCasts()); - if (!TR) - return 0; - - const CXXRecordDecl *RD = TR->getValueType()->getAsCXXRecordDecl(); - if (!RD) - return 0; - - const CXXMethodDecl *Result = MD->getCorrespondingMethodInClass(RD); - const FunctionDecl *Definition; - if (!Result->hasBody(Definition)) - return 0; - - return cast<CXXMethodDecl>(Definition); -} - RuntimeDefinition CXXInstanceCall::getRuntimeDefinition() const { + // Do we have a decl at all? const Decl *D = getDecl(); if (!D) return RuntimeDefinition(); + // If the method is non-virtual, we know we can inline it. const CXXMethodDecl *MD = cast<CXXMethodDecl>(D); if (!MD->isVirtual()) return AnyFunctionCall::getRuntimeDefinition(); - // If the method is virtual, see if we can find the actual implementation - // based on context-sensitivity. - // FIXME: Virtual method calls behave differently when an object is being - // constructed or destructed. It's not as simple as "no devirtualization" - // because a /partially/ constructed object can be referred to through a - // base pointer. We'll eventually want to use DynamicTypeInfo here. - if (const CXXMethodDecl *Devirtualized = devirtualize(MD, getCXXThisVal())) - return RuntimeDefinition(Devirtualized); + // Do we know the implicit 'this' object being called? + const MemRegion *R = getCXXThisVal().getAsRegion(); + if (!R) + return RuntimeDefinition(); - return RuntimeDefinition(); + // Do we know anything about the type of 'this'? + DynamicTypeInfo DynType = getState()->getDynamicTypeInfo(R); + if (!DynType.isValid()) + return RuntimeDefinition(); + + // Is the type a C++ class? (This is mostly a defensive check.) + QualType RegionType = DynType.getType()->getPointeeType(); + const CXXRecordDecl *RD = RegionType->getAsCXXRecordDecl(); + if (!RD || !RD->hasDefinition()) + return RuntimeDefinition(); + + // Find the decl for this method in that class. + const CXXMethodDecl *Result = MD->getCorrespondingMethodInClass(RD, true); + assert(Result && "At the very least the static decl should show up."); + + // Does the decl that we found have an implementation? + const FunctionDecl *Definition; + if (!Result->hasBody(Definition)) + return RuntimeDefinition(); + + // We found a definition. If we're not sure that this devirtualization is + // actually what will happen at runtime, make sure to provide the region so + // that ExprEngine can decide what to do with it. + if (DynType.canBeASubClass()) + return RuntimeDefinition(Definition, R->StripCasts()); + return RuntimeDefinition(Definition, /*DispatchRegion=*/0); } void CXXInstanceCall::getInitialStackFrameContents( @@ -421,16 +427,17 @@ void CXXInstanceCall::getInitialStackFrameContents( AnyFunctionCall::getInitialStackFrameContents(CalleeCtx, Bindings); // Handle the binding of 'this' in the new stack frame. - // We need to make sure we have the proper layering of CXXBaseObjectRegions. SVal ThisVal = getCXXThisVal(); if (!ThisVal.isUnknown()) { ProgramStateManager &StateMgr = getState()->getStateManager(); SValBuilder &SVB = StateMgr.getSValBuilder(); - + const CXXMethodDecl *MD = cast<CXXMethodDecl>(CalleeCtx->getDecl()); Loc ThisLoc = SVB.getCXXThis(MD, CalleeCtx); - if (const MemRegion *ThisReg = ThisVal.getAsRegion()) { + // If we devirtualized to a different member function, we need to make sure + // we have the proper layering of CXXBaseObjectRegions. + if (MD->getCanonicalDecl() != getDecl()->getCanonicalDecl()) { ASTContext &Ctx = SVB.getContext(); const CXXRecordDecl *Class = MD->getParent(); QualType Ty = Ctx.getPointerType(Ctx.getRecordType(Class)); @@ -439,13 +446,10 @@ void CXXInstanceCall::getInitialStackFrameContents( bool Failed; ThisVal = StateMgr.getStoreManager().evalDynamicCast(ThisVal, Ty, Failed); assert(!Failed && "Calling an incorrectly devirtualized method"); - - // If we couldn't build the correct cast, just strip off all casts. - if (ThisVal.isUnknown()) - ThisVal = loc::MemRegionVal(ThisReg->StripCasts()); } - Bindings.push_back(std::make_pair(ThisLoc, ThisVal)); + if (!ThisVal.isUnknown()) + Bindings.push_back(std::make_pair(ThisLoc, ThisVal)); } } @@ -666,6 +670,9 @@ bool ObjCMethodCall::canBeOverridenInSubclass(ObjCInterfaceDecl *IDecl, if (InterfLoc.isValid() && SM.isFromMainFile(InterfLoc)) return false; + // Assume that property accessors are not overridden. + if (getMessageKind() == OCM_PropertyAccess) + return false; // We assume that if the method is public (declared outside of main file) or // has a parent which publicly declares the method, the method could be @@ -853,4 +860,3 @@ CallEventManager::getCaller(const StackFrameContext *CalleeCtx, return getCXXDestructorCall(Dtor, Trigger, ThisVal.getAsRegion(), State, CallerCtx); } - diff --git a/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp b/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp index 8ee6723..3b2e4ec 100644 --- a/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp +++ b/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp @@ -154,6 +154,11 @@ void ExprEngine::processCallExit(ExplodedNode *CEBNode) { } } + // Generate a CallEvent /before/ cleaning the state, so that we can get the + // correct value for 'this' (if necessary). + CallEventManager &CEMgr = getStateManager().getCallEventManager(); + CallEventRef<> Call = CEMgr.getCaller(calleeCtx, state); + // Step 3: BindedRetNode -> CleanedNodes // If we can find a statement and a block in the inlined function, run remove // dead bindings before returning from the call. This is important to ensure @@ -203,21 +208,21 @@ void ExprEngine::processCallExit(ExplodedNode *CEBNode) { &Ctx); SaveAndRestore<unsigned> CBISave(currentStmtIdx, calleeCtx->getIndex()); - CallEventManager &CEMgr = getStateManager().getCallEventManager(); - CallEventRef<> Call = CEMgr.getCaller(calleeCtx, CEEState); + CallEventRef<> UpdatedCall = Call.cloneWithState(CEEState); ExplodedNodeSet DstPostCall; - getCheckerManager().runCheckersForPostCall(DstPostCall, CEENode, *Call, - *this, true); + getCheckerManager().runCheckersForPostCall(DstPostCall, CEENode, + *UpdatedCall, *this, + /*WasInlined=*/true); ExplodedNodeSet Dst; - if (isa<ObjCMethodCall>(Call)) { - getCheckerManager().runCheckersForPostObjCMessage(Dst, DstPostCall, - cast<ObjCMethodCall>(*Call), - *this, true); + if (const ObjCMethodCall *Msg = dyn_cast<ObjCMethodCall>(Call)) { + getCheckerManager().runCheckersForPostObjCMessage(Dst, DstPostCall, *Msg, + *this, + /*WasInlined=*/true); } else if (CE) { getCheckerManager().runCheckersForPostStmt(Dst, DstPostCall, CE, - *this, true); + *this, /*WasInlined=*/true); } else { Dst.insert(DstPostCall); } @@ -555,12 +560,20 @@ void ExprEngine::defaultEvalCall(NodeBuilder &Bldr, ExplodedNode *Pred, RuntimeDefinition RD = Call->getRuntimeDefinition(); const Decl *D = RD.getDecl(); if (D) { - // Explore with and without inlining the call. - if (RD.mayHaveOtherDefinitions() && - getAnalysisManager().IPAMode == DynamicDispatchBifurcate) { - BifurcateCall(RD.getDispatchRegion(), *Call, D, Bldr, Pred); - return; + if (RD.mayHaveOtherDefinitions()) { + // Explore with and without inlining the call. + if (getAnalysisManager().IPAMode == DynamicDispatchBifurcate) { + BifurcateCall(RD.getDispatchRegion(), *Call, D, Bldr, Pred); + return; + } + + // Don't inline if we're not in any dynamic dispatch mode. + if (getAnalysisManager().IPAMode != DynamicDispatch) { + conservativeEvalCall(*Call, Bldr, Pred, State); + return; + } } + // We are not bifurcating and we do have a Decl, so just inline. if (inlineCall(*Call, D, Bldr, Pred, State)) return; @@ -575,6 +588,7 @@ void ExprEngine::BifurcateCall(const MemRegion *BifurReg, const CallEvent &Call, const Decl *D, NodeBuilder &Bldr, ExplodedNode *Pred) { assert(BifurReg); + BifurReg = BifurReg->StripCasts(); // Check if we've performed the split already - note, we only want // to split the path once per memory region. diff --git a/lib/StaticAnalyzer/Core/HTMLDiagnostics.cpp b/lib/StaticAnalyzer/Core/HTMLDiagnostics.cpp index 0152e32..982bcbf 100644 --- a/lib/StaticAnalyzer/Core/HTMLDiagnostics.cpp +++ b/lib/StaticAnalyzer/Core/HTMLDiagnostics.cpp @@ -45,7 +45,7 @@ public: virtual ~HTMLDiagnostics() { FlushDiagnostics(NULL); } virtual void FlushDiagnosticsImpl(std::vector<const PathDiagnostic *> &Diags, - SmallVectorImpl<std::string> *FilesMade); + FilesMade *filesMade); virtual StringRef getName() const { return "HTMLDiagnostics"; @@ -63,7 +63,7 @@ public: const char *HighlightEnd = "</span>"); void ReportDiag(const PathDiagnostic& D, - SmallVectorImpl<std::string> *FilesMade); + FilesMade *filesMade); }; } // end anonymous namespace @@ -76,10 +76,10 @@ HTMLDiagnostics::HTMLDiagnostics(const std::string& prefix, FilePrefix.appendComponent("report"); } -PathDiagnosticConsumer* -ento::createHTMLDiagnosticConsumer(const std::string& prefix, - const Preprocessor &PP) { - return new HTMLDiagnostics(prefix, PP); +void ento::createHTMLDiagnosticConsumer(PathDiagnosticConsumers &C, + const std::string& prefix, + const Preprocessor &PP) { + C.push_back(new HTMLDiagnostics(prefix, PP)); } //===----------------------------------------------------------------------===// @@ -88,15 +88,15 @@ ento::createHTMLDiagnosticConsumer(const std::string& prefix, void HTMLDiagnostics::FlushDiagnosticsImpl( std::vector<const PathDiagnostic *> &Diags, - SmallVectorImpl<std::string> *FilesMade) { + FilesMade *filesMade) { for (std::vector<const PathDiagnostic *>::iterator it = Diags.begin(), et = Diags.end(); it != et; ++it) { - ReportDiag(**it, FilesMade); + ReportDiag(**it, filesMade); } } void HTMLDiagnostics::ReportDiag(const PathDiagnostic& D, - SmallVectorImpl<std::string> *FilesMade) { + FilesMade *filesMade) { // Create the HTML directory if it is missing. if (!createdDir) { @@ -266,8 +266,10 @@ void HTMLDiagnostics::ReportDiag(const PathDiagnostic& D, return; } - if (FilesMade) - FilesMade->push_back(llvm::sys::path::filename(H.str())); + if (filesMade) { + filesMade->push_back(std::make_pair(StringRef(getName()), + llvm::sys::path::filename(H.str()))); + } // Emit the HTML to disk. for (RewriteBuffer::iterator I = Buf->begin(), E = Buf->end(); I!=E; ++I) @@ -480,29 +482,11 @@ void HTMLDiagnostics::HandlePiece(Rewriter& R, FileID BugFileID, R.InsertTextBefore(Loc, os.str()); // Now highlight the ranges. - for (const SourceRange *I = P.ranges_begin(), *E = P.ranges_end(); - I != E; ++I) + ArrayRef<SourceRange> Ranges = P.getRanges(); + for (ArrayRef<SourceRange>::iterator I = Ranges.begin(), + E = Ranges.end(); I != E; ++I) { HighlightRange(R, LPosInfo.first, *I); - -#if 0 - // If there is a code insertion hint, insert that code. - // FIXME: This code is disabled because it seems to mangle the HTML - // output. I'm leaving it here because it's generally the right idea, - // but needs some help from someone more familiar with the rewriter. - for (const FixItHint *Hint = P.fixit_begin(), *HintEnd = P.fixit_end(); - Hint != HintEnd; ++Hint) { - if (Hint->RemoveRange.isValid()) { - HighlightRange(R, LPosInfo.first, Hint->RemoveRange, - "<span class=\"CodeRemovalHint\">", "</span>"); - } - if (Hint->InsertionLoc.isValid()) { - std::string EscapedCode = html::EscapeText(Hint->CodeToInsert, true); - EscapedCode = "<span class=\"CodeInsertionHint\">" + EscapedCode - + "</span>"; - R.InsertTextBefore(Hint->InsertionLoc, EscapedCode); - } } -#endif } static void EmitAlphaCounter(raw_ostream &os, unsigned n) { diff --git a/lib/StaticAnalyzer/Core/PathDiagnostic.cpp b/lib/StaticAnalyzer/Core/PathDiagnostic.cpp index 7d52aac..c849778 100644 --- a/lib/StaticAnalyzer/Core/PathDiagnostic.cpp +++ b/lib/StaticAnalyzer/Core/PathDiagnostic.cpp @@ -157,13 +157,13 @@ void PathDiagnosticConsumer::HandlePathDiagnostic(PathDiagnostic *D) { return; // FIXME: Emit a warning? // Check the source ranges. - for (PathDiagnosticPiece::range_iterator RI = piece->ranges_begin(), - RE = piece->ranges_end(); - RI != RE; ++RI) { - SourceLocation L = SMgr.getExpansionLoc(RI->getBegin()); + ArrayRef<SourceRange> Ranges = piece->getRanges(); + for (ArrayRef<SourceRange>::iterator I = Ranges.begin(), + E = Ranges.end(); I != E; ++I) { + SourceLocation L = SMgr.getExpansionLoc(I->getBegin()); if (!L.isFileID() || SMgr.getFileID(L) != FID) return; // FIXME: Emit a warning? - L = SMgr.getExpansionLoc(RI->getEnd()); + L = SMgr.getExpansionLoc(I->getEnd()); if (!L.isFileID() || SMgr.getFileID(L) != FID) return; // FIXME: Emit a warning? } @@ -240,8 +240,8 @@ struct CompareDiagnostics { }; } -void -PathDiagnosticConsumer::FlushDiagnostics(SmallVectorImpl<std::string> *Files) { +void PathDiagnosticConsumer::FlushDiagnostics( + PathDiagnosticConsumer::FilesMade *Files) { if (flushed) return; @@ -718,7 +718,9 @@ void PathDiagnosticPiece::Profile(llvm::FoldingSetNodeID &ID) const { ID.AddString(str); // FIXME: Add profiling support for code hints. ID.AddInteger((unsigned) getDisplayHint()); - for (range_iterator I = ranges_begin(), E = ranges_end(); I != E; ++I) { + ArrayRef<SourceRange> Ranges = getRanges(); + for (ArrayRef<SourceRange>::iterator I = Ranges.begin(), E = Ranges.end(); + I != E; ++I) { ID.AddInteger(I->getBegin().getRawEncoding()); ID.AddInteger(I->getEnd().getRawEncoding()); } diff --git a/lib/StaticAnalyzer/Core/PlistDiagnostics.cpp b/lib/StaticAnalyzer/Core/PlistDiagnostics.cpp index 58a4bba..d5fdd9d 100644 --- a/lib/StaticAnalyzer/Core/PlistDiagnostics.cpp +++ b/lib/StaticAnalyzer/Core/PlistDiagnostics.cpp @@ -30,23 +30,21 @@ namespace { class PlistDiagnostics : public PathDiagnosticConsumer { const std::string OutputFile; const LangOptions &LangOpts; - OwningPtr<PathDiagnosticConsumer> SubPD; const bool SupportsCrossFileDiagnostics; public: PlistDiagnostics(const std::string& prefix, const LangOptions &LangOpts, - bool supportsMultipleFiles, - PathDiagnosticConsumer *subPD); + bool supportsMultipleFiles); virtual ~PlistDiagnostics() {} void FlushDiagnosticsImpl(std::vector<const PathDiagnostic *> &Diags, - SmallVectorImpl<std::string> *FilesMade); + FilesMade *filesMade); virtual StringRef getName() const { return "PlistDiagnostics"; } - PathGenerationScheme getGenerationScheme() const; + PathGenerationScheme getGenerationScheme() const { return Extensive; } bool supportsLogicalOpControlFlow() const { return true; } bool supportsAllBlockEdges() const { return true; } virtual bool useVerboseDescription() const { return false; } @@ -58,29 +56,20 @@ namespace { PlistDiagnostics::PlistDiagnostics(const std::string& output, const LangOptions &LO, - bool supportsMultipleFiles, - PathDiagnosticConsumer *subPD) - : OutputFile(output), LangOpts(LO), SubPD(subPD), + bool supportsMultipleFiles) + : OutputFile(output), LangOpts(LO), SupportsCrossFileDiagnostics(supportsMultipleFiles) {} -PathDiagnosticConsumer* -ento::createPlistDiagnosticConsumer(const std::string& s, const Preprocessor &PP, - PathDiagnosticConsumer *subPD) { - return new PlistDiagnostics(s, PP.getLangOpts(), false, subPD); +void ento::createPlistDiagnosticConsumer(PathDiagnosticConsumers &C, + const std::string& s, + const Preprocessor &PP) { + C.push_back(new PlistDiagnostics(s, PP.getLangOpts(), false)); } -PathDiagnosticConsumer* -ento::createPlistMultiFileDiagnosticConsumer(const std::string &s, - const Preprocessor &PP) { - return new PlistDiagnostics(s, PP.getLangOpts(), true, 0); -} - -PathDiagnosticConsumer::PathGenerationScheme -PlistDiagnostics::getGenerationScheme() const { - if (const PathDiagnosticConsumer *PD = SubPD.get()) - return PD->getGenerationScheme(); - - return Extensive; +void ento::createPlistMultiFileDiagnosticConsumer(PathDiagnosticConsumers &C, + const std::string &s, + const Preprocessor &PP) { + C.push_back(new PlistDiagnostics(s, PP.getLangOpts(), true)); } static void AddFID(FIDMap &FIDs, SmallVectorImpl<FileID> &V, @@ -231,15 +220,16 @@ static void ReportEvent(raw_ostream &o, const PathDiagnosticPiece& P, EmitLocation(o, SM, LangOpts, L, FM, indent); // Output the ranges (if any). - PathDiagnosticPiece::range_iterator RI = P.ranges_begin(), - RE = P.ranges_end(); + ArrayRef<SourceRange> Ranges = P.getRanges(); - if (RI != RE) { + if (!Ranges.empty()) { Indent(o, indent) << "<key>ranges</key>\n"; Indent(o, indent) << "<array>\n"; ++indent; - for (; RI != RE; ++RI) - EmitRange(o, SM, LangOpts, *RI, FM, indent+1); + for (ArrayRef<SourceRange>::iterator I = Ranges.begin(), E = Ranges.end(); + I != E; ++I) { + EmitRange(o, SM, LangOpts, *I, FM, indent+1); + } --indent; Indent(o, indent) << "</array>\n"; } @@ -353,7 +343,7 @@ static void ReportPiece(raw_ostream &o, void PlistDiagnostics::FlushDiagnosticsImpl( std::vector<const PathDiagnostic *> &Diags, - SmallVectorImpl<std::string> *FilesMade) { + FilesMade *filesMade) { // Build up a set of FIDs that we use by scanning the locations and // ranges of the diagnostics. FIDMap FM; @@ -380,11 +370,11 @@ void PlistDiagnostics::FlushDiagnosticsImpl( I!=E; ++I) { const PathDiagnosticPiece *piece = I->getPtr(); AddFID(FM, Fids, SM, piece->getLocation().asLocation()); - - for (PathDiagnosticPiece::range_iterator RI = piece->ranges_begin(), - RE= piece->ranges_end(); RI != RE; ++RI) { - AddFID(FM, Fids, SM, RI->getBegin()); - AddFID(FM, Fids, SM, RI->getEnd()); + ArrayRef<SourceRange> Ranges = piece->getRanges(); + for (ArrayRef<SourceRange>::iterator I = Ranges.begin(), + E = Ranges.end(); I != E; ++I) { + AddFID(FM, Fids, SM, I->getBegin()); + AddFID(FM, Fids, SM, I->getEnd()); } if (const PathDiagnosticCallPiece *call = @@ -507,19 +497,21 @@ void PlistDiagnostics::FlushDiagnosticsImpl( EmitLocation(o, *SM, LangOpts, D->getLocation(), FM, 2); // Output the diagnostic to the sub-diagnostic client, if any. - if (SubPD) { - std::vector<const PathDiagnostic *> SubDiags; - SubDiags.push_back(D); - SmallVector<std::string, 1> SubFilesMade; - SubPD->FlushDiagnosticsImpl(SubDiags, &SubFilesMade); - - if (!SubFilesMade.empty()) { - o << " <key>" << SubPD->getName() << "_files</key>\n"; - o << " <array>\n"; - for (size_t i = 0, n = SubFilesMade.size(); i < n ; ++i) - o << " <string>" << SubFilesMade[i] << "</string>\n"; - o << " </array>\n"; + if (!filesMade->empty()) { + StringRef lastName; + for (FilesMade::iterator I = filesMade->begin(), E = filesMade->end(); + I != E; ++I) { + StringRef newName = I->first; + if (newName != lastName) { + if (!lastName.empty()) + o << " </array>\n"; + lastName = newName; + o << " <key>" << lastName << "_files</key>\n"; + o << " <array>\n"; + } + o << " <string>" << I->second << "</string>\n"; } + o << " </array>\n"; } // Close up the entry. @@ -531,6 +523,8 @@ void PlistDiagnostics::FlushDiagnosticsImpl( // Finish. o << "</dict>\n</plist>"; - if (FilesMade) - FilesMade->push_back(OutputFile); + if (filesMade) { + StringRef Name(getName()); + filesMade->push_back(std::make_pair(Name, OutputFile)); + } } diff --git a/lib/StaticAnalyzer/Core/ProgramState.cpp b/lib/StaticAnalyzer/Core/ProgramState.cpp index dc988cc..2000338 100644 --- a/lib/StaticAnalyzer/Core/ProgramState.cpp +++ b/lib/StaticAnalyzer/Core/ProgramState.cpp @@ -745,14 +745,16 @@ template<> struct ProgramStateTrait<DynamicTypeMap> }} DynamicTypeInfo ProgramState::getDynamicTypeInfo(const MemRegion *Reg) const { + Reg = Reg->StripCasts(); + // Look up the dynamic type in the GDM. const DynamicTypeInfo *GDMType = get<DynamicTypeMap>(Reg); if (GDMType) return *GDMType; // Otherwise, fall back to what we know about the region. - if (const TypedValueRegion *TR = dyn_cast<TypedValueRegion>(Reg)) - return DynamicTypeInfo(TR->getValueType()); + if (const TypedRegion *TR = dyn_cast<TypedRegion>(Reg)) + return DynamicTypeInfo(TR->getLocationType(), /*CanBeSubclass=*/false); if (const SymbolicRegion *SR = dyn_cast<SymbolicRegion>(Reg)) { SymbolRef Sym = SR->getSymbol(); @@ -764,6 +766,7 @@ DynamicTypeInfo ProgramState::getDynamicTypeInfo(const MemRegion *Reg) const { ProgramStateRef ProgramState::setDynamicTypeInfo(const MemRegion *Reg, DynamicTypeInfo NewTy) const { + Reg = Reg->StripCasts(); ProgramStateRef NewState = set<DynamicTypeMap>(Reg, NewTy); assert(NewState); return NewState; diff --git a/lib/StaticAnalyzer/Core/TextPathDiagnostics.cpp b/lib/StaticAnalyzer/Core/TextPathDiagnostics.cpp index e5b8553..66bf4bb 100644 --- a/lib/StaticAnalyzer/Core/TextPathDiagnostics.cpp +++ b/lib/StaticAnalyzer/Core/TextPathDiagnostics.cpp @@ -32,7 +32,7 @@ public: : OutputFile(output), Diag(diag) {} void FlushDiagnosticsImpl(std::vector<const PathDiagnostic *> &Diags, - SmallVectorImpl<std::string> *FilesMade); + FilesMade *filesMade); virtual StringRef getName() const { return "TextPathDiagnostics"; @@ -47,15 +47,15 @@ public: } // end anonymous namespace -PathDiagnosticConsumer* -ento::createTextPathDiagnosticConsumer(const std::string& out, - const Preprocessor &PP) { - return new TextPathDiagnostics(out, PP.getDiagnostics()); +void ento::createTextPathDiagnosticConsumer(PathDiagnosticConsumers &C, + const std::string& out, + const Preprocessor &PP) { + C.push_back(new TextPathDiagnostics(out, PP.getDiagnostics())); } void TextPathDiagnostics::FlushDiagnosticsImpl( std::vector<const PathDiagnostic *> &Diags, - SmallVectorImpl<std::string> *FilesMade) { + FilesMade *) { for (std::vector<const PathDiagnostic *>::iterator it = Diags.begin(), et = Diags.end(); it != et; ++it) { const PathDiagnostic *D = *it; diff --git a/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp b/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp index fcdaaea..34b5266 100644 --- a/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp +++ b/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp @@ -64,14 +64,55 @@ STATISTIC(MaxCFGSize, "The maximum number of basic blocks in a function."); // Special PathDiagnosticConsumers. //===----------------------------------------------------------------------===// -static PathDiagnosticConsumer* -createPlistHTMLDiagnosticConsumer(const std::string& prefix, - const Preprocessor &PP) { - PathDiagnosticConsumer *PD = - createHTMLDiagnosticConsumer(llvm::sys::path::parent_path(prefix), PP); - return createPlistDiagnosticConsumer(prefix, PP, PD); +static void createPlistHTMLDiagnosticConsumer(PathDiagnosticConsumers &C, + const std::string &prefix, + const Preprocessor &PP) { + createHTMLDiagnosticConsumer(C, llvm::sys::path::parent_path(prefix), PP); + createPlistDiagnosticConsumer(C, prefix, PP); } +namespace { +class ClangDiagPathDiagConsumer : public PathDiagnosticConsumer { + DiagnosticsEngine &Diag; +public: + ClangDiagPathDiagConsumer(DiagnosticsEngine &Diag) : Diag(Diag) {} + virtual ~ClangDiagPathDiagConsumer() {} + virtual StringRef getName() const { return "ClangDiags"; } + virtual bool useVerboseDescription() const { return false; } + virtual PathGenerationScheme getGenerationScheme() const { return None; } + + void FlushDiagnosticsImpl(std::vector<const PathDiagnostic *> &Diags, + FilesMade *filesMade) { + for (std::vector<const PathDiagnostic*>::iterator I = Diags.begin(), + E = Diags.end(); I != E; ++I) { + const PathDiagnostic *PD = *I; + StringRef desc = PD->getDescription(); + SmallString<512> TmpStr; + llvm::raw_svector_ostream Out(TmpStr); + for (StringRef::iterator I=desc.begin(), E=desc.end(); I!=E; ++I) { + if (*I == '%') + Out << "%%"; + else + Out << *I; + } + Out.flush(); + unsigned ErrorDiag = Diag.getCustomDiagID(DiagnosticsEngine::Warning, + TmpStr); + SourceLocation L = PD->getLocation().asLocation(); + DiagnosticBuilder diagBuilder = Diag.Report(L, ErrorDiag); + + // Get the ranges from the last point in the path. + ArrayRef<SourceRange> Ranges = PD->path.back()->getRanges(); + + for (ArrayRef<SourceRange>::iterator I = Ranges.begin(), + E = Ranges.end(); I != E; ++I) { + diagBuilder << *I; + } + } + } +}; +} // end anonymous namespace + //===----------------------------------------------------------------------===// // AnalysisConsumer declaration. //===----------------------------------------------------------------------===// @@ -105,8 +146,8 @@ public: /// working with a PCH file. SetOfDecls LocalTUDecls; - // PD is owned by AnalysisManager. - PathDiagnosticConsumer *PD; + // Set of PathDiagnosticConsumers. Owned by AnalysisManager. + PathDiagnosticConsumers PathConsumers; StoreManagerCreator CreateStoreMgr; ConstraintManagerCreator CreateConstraintMgr; @@ -126,7 +167,7 @@ public: const AnalyzerOptions& opts, ArrayRef<std::string> plugins) : RecVisitorMode(ANALYSIS_ALL), RecVisitorBR(0), - Ctx(0), PP(pp), OutDir(outdir), Opts(opts), Plugins(plugins), PD(0) { + Ctx(0), PP(pp), OutDir(outdir), Opts(opts), Plugins(plugins) { DigestAnalyzerOptions(); if (Opts.PrintStats) { llvm::EnableStatistics(); @@ -141,17 +182,19 @@ public: void DigestAnalyzerOptions() { // Create the PathDiagnosticConsumer. + PathConsumers.push_back(new ClangDiagPathDiagConsumer(PP.getDiagnostics())); + if (!OutDir.empty()) { switch (Opts.AnalysisDiagOpt) { default: #define ANALYSIS_DIAGNOSTICS(NAME, CMDFLAG, DESC, CREATEFN, AUTOCREATE) \ - case PD_##NAME: PD = CREATEFN(OutDir, PP); break; + case PD_##NAME: CREATEFN(PathConsumers, OutDir, PP); break; #include "clang/Frontend/Analyses.def" } } else if (Opts.AnalysisDiagOpt == PD_TEXT) { // Create the text client even without a specified output file since // it just uses diagnostic notes. - PD = createTextPathDiagnosticConsumer("", PP); + createTextPathDiagnosticConsumer(PathConsumers, "", PP); } // Create the analyzer component creators. @@ -205,9 +248,12 @@ public: Ctx = &Context; checkerMgr.reset(createCheckerManager(Opts, PP.getLangOpts(), Plugins, PP.getDiagnostics())); - Mgr.reset(new AnalysisManager(*Ctx, PP.getDiagnostics(), - PP.getLangOpts(), PD, - CreateStoreMgr, CreateConstraintMgr, + Mgr.reset(new AnalysisManager(*Ctx, + PP.getDiagnostics(), + PP.getLangOpts(), + PathConsumers, + CreateStoreMgr, + CreateConstraintMgr, checkerMgr.get(), Opts.MaxNodes, Opts.MaxLoop, Opts.VisualizeEGDot, Opts.VisualizeEGUbi, |