diff options
author | Ted Kremenek <kremenek@apple.com> | 2012-09-21 00:09:11 +0000 |
---|---|---|
committer | Ted Kremenek <kremenek@apple.com> | 2012-09-21 00:09:11 +0000 |
commit | a43df9539644bf1c258e12710cd69d79b0b078cd (patch) | |
tree | 1c77ba3f0325a80410c1568dc939c1306e80491a /lib/StaticAnalyzer/Core | |
parent | 445895a97ae3f1d7bad3480839d31ed3ebcc9c83 (diff) |
Implement faux-body-synthesis of well-known functions in the static analyzer when
their implementations are unavailable. Start by simulating dispatch_sync().
This change is largely a bunch of plumbing around something very simple. We
use AnalysisDeclContext to conjure up a fake function body (using the
current ASTContext) when one does not exist. This is controlled
under the analyzer-config option "faux-bodies", which is off by default.
The plumbing in this patch is largely to pass the necessary machinery
around. CallEvent needs the AnalysisDeclContextManager to get
the function definition, as one may get conjured up lazily.
BugReporter and PathDiagnosticLocation needed to be relaxed to handle
invalid locations, as the conjured body has no real source locations.
We do some primitive recovery in diagnostic generation to generate
some reasonable locations (for arrows and events), but it can be
improved.
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@164339 91177308-0d34-0410-b5e6-96231b3b80d8
Diffstat (limited to 'lib/StaticAnalyzer/Core')
-rw-r--r-- | lib/StaticAnalyzer/Core/AnalysisManager.cpp | 3 | ||||
-rw-r--r-- | lib/StaticAnalyzer/Core/AnalyzerOptions.cpp | 4 | ||||
-rw-r--r-- | lib/StaticAnalyzer/Core/BugReporter.cpp | 25 | ||||
-rw-r--r-- | lib/StaticAnalyzer/Core/CallEvent.cpp | 18 | ||||
-rw-r--r-- | lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp | 2 |
5 files changed, 39 insertions, 13 deletions
diff --git a/lib/StaticAnalyzer/Core/AnalysisManager.cpp b/lib/StaticAnalyzer/Core/AnalysisManager.cpp index ebd2336080..09a0debaac 100644 --- a/lib/StaticAnalyzer/Core/AnalysisManager.cpp +++ b/lib/StaticAnalyzer/Core/AnalysisManager.cpp @@ -24,7 +24,8 @@ AnalysisManager::AnalysisManager(ASTContext &ctx, DiagnosticsEngine &diags, : AnaCtxMgr(Options.UnoptimizedCFG, /*AddImplicitDtors=*/true, /*AddInitializers=*/true, - Options.includeTemporaryDtorsInCFG()), + Options.includeTemporaryDtorsInCFG(), + Options.shouldSynthesizeBodies()), Ctx(ctx), Diags(diags), LangOpts(lang), diff --git a/lib/StaticAnalyzer/Core/AnalyzerOptions.cpp b/lib/StaticAnalyzer/Core/AnalyzerOptions.cpp index a96dfe1e48..cb7df6b3fd 100644 --- a/lib/StaticAnalyzer/Core/AnalyzerOptions.cpp +++ b/lib/StaticAnalyzer/Core/AnalyzerOptions.cpp @@ -111,3 +111,7 @@ unsigned AnalyzerOptions::getAlwaysInlineSize() const { return AlwaysInlineSize.getValue(); } + +bool AnalyzerOptions::shouldSynthesizeBodies() const { + return getBooleanOption("faux-bodies", false); +} diff --git a/lib/StaticAnalyzer/Core/BugReporter.cpp b/lib/StaticAnalyzer/Core/BugReporter.cpp index 6cbed952c9..e95f31c0d6 100644 --- a/lib/StaticAnalyzer/Core/BugReporter.cpp +++ b/lib/StaticAnalyzer/Core/BugReporter.cpp @@ -121,7 +121,8 @@ GetCurrentOrNextStmt(const ExplodedNode *N) { /// 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) { +bool BugReporter::RemoveUneededCalls(PathPieces &pieces, BugReport *R, + PathDiagnosticCallPiece *CallWithLoc) { bool containsSomethingInteresting = false; const unsigned N = pieces.size(); @@ -131,6 +132,11 @@ bool BugReporter::RemoveUneededCalls(PathPieces &pieces, BugReport *R) { IntrusiveRefCntPtr<PathDiagnosticPiece> piece(pieces.front()); pieces.pop_front(); + // Throw away pieces with invalid locations. + if (piece->getKind() != PathDiagnosticPiece::Call && + piece->getLocation().asLocation().isInvalid()) + continue; + switch (piece->getKind()) { case PathDiagnosticPiece::Call: { PathDiagnosticCallPiece *call = cast<PathDiagnosticCallPiece>(piece); @@ -142,8 +148,17 @@ bool BugReporter::RemoveUneededCalls(PathPieces &pieces, BugReport *R) { } // Recursively clean out the subclass. Keep this call around if // it contains any informative diagnostics. - if (!RemoveUneededCalls(call->path, R)) + PathDiagnosticCallPiece *NewCallWithLoc = + call->getLocation().asLocation().isValid() + ? call : CallWithLoc; + + if (!RemoveUneededCalls(call->path, R, NewCallWithLoc)) continue; + + if (NewCallWithLoc == CallWithLoc && CallWithLoc) { + call->callEnter = CallWithLoc->callEnter; + } + containsSomethingInteresting = true; break; } @@ -156,6 +171,7 @@ bool BugReporter::RemoveUneededCalls(PathPieces &pieces, BugReport *R) { } case PathDiagnosticPiece::Event: { PathDiagnosticEventPiece *event = cast<PathDiagnosticEventPiece>(piece); + // We never throw away an event, but we do throw it away wholesale // as part of a path if we throw the entire path away. containsSomethingInteresting |= !event->isPrunable(); @@ -954,6 +970,11 @@ void EdgeBuilder::rawAddEdge(PathDiagnosticLocation NewLoc) { const PathDiagnosticLocation &NewLocClean = cleanUpLocation(NewLoc); const PathDiagnosticLocation &PrevLocClean = cleanUpLocation(PrevLoc); + if (PrevLocClean.asLocation().isInvalid()) { + PrevLoc = NewLoc; + return; + } + if (NewLocClean.asLocation() == PrevLocClean.asLocation()) return; diff --git a/lib/StaticAnalyzer/Core/CallEvent.cpp b/lib/StaticAnalyzer/Core/CallEvent.cpp index 3b4c13471b..3743871361 100644 --- a/lib/StaticAnalyzer/Core/CallEvent.cpp +++ b/lib/StaticAnalyzer/Core/CallEvent.cpp @@ -414,7 +414,7 @@ SVal CXXInstanceCall::getCXXThisVal() const { } -RuntimeDefinition CXXInstanceCall::getRuntimeDefinition() const { +RuntimeDefinition CXXInstanceCall::getRuntimeDefinition(AnalysisDeclContextManager &M) const { // Do we have a decl at all? const Decl *D = getDecl(); if (!D) @@ -423,7 +423,7 @@ RuntimeDefinition CXXInstanceCall::getRuntimeDefinition() const { // If the method is non-virtual, we know we can inline it. const CXXMethodDecl *MD = cast<CXXMethodDecl>(D); if (!MD->isVirtual()) - return AnyFunctionCall::getRuntimeDefinition(); + return AnyFunctionCall::getRuntimeDefinition(M); // Do we know the implicit 'this' object being called? const MemRegion *R = getCXXThisVal().getAsRegion(); @@ -513,16 +513,16 @@ const Expr *CXXMemberCall::getCXXThisExpr() const { return getOriginExpr()->getImplicitObjectArgument(); } -RuntimeDefinition CXXMemberCall::getRuntimeDefinition() const { +RuntimeDefinition CXXMemberCall::getRuntimeDefinition(AnalysisDeclContextManager &M) const { // C++11 [expr.call]p1: ...If the selected function is non-virtual, or if the // id-expression in the class member access expression is a qualified-id, // that function is called. Otherwise, its final overrider in the dynamic type // of the object expression is called. if (const MemberExpr *ME = dyn_cast<MemberExpr>(getOriginExpr()->getCallee())) if (ME->hasQualifier()) - return AnyFunctionCall::getRuntimeDefinition(); + return AnyFunctionCall::getRuntimeDefinition(M); - return CXXInstanceCall::getRuntimeDefinition(); + return CXXInstanceCall::getRuntimeDefinition(M); } @@ -600,13 +600,13 @@ SVal CXXDestructorCall::getCXXThisVal() const { return UnknownVal(); } -RuntimeDefinition CXXDestructorCall::getRuntimeDefinition() const { +RuntimeDefinition CXXDestructorCall::getRuntimeDefinition(AnalysisDeclContextManager &M) const { // Base destructors are always called non-virtually. // Skip CXXInstanceCall's devirtualization logic in this case. if (isBaseDestructor()) - return AnyFunctionCall::getRuntimeDefinition(); + return AnyFunctionCall::getRuntimeDefinition(M); - return CXXInstanceCall::getRuntimeDefinition(); + return CXXInstanceCall::getRuntimeDefinition(M); } @@ -790,7 +790,7 @@ bool ObjCMethodCall::canBeOverridenInSubclass(ObjCInterfaceDecl *IDecl, llvm_unreachable("The while loop should always terminate."); } -RuntimeDefinition ObjCMethodCall::getRuntimeDefinition() const { +RuntimeDefinition ObjCMethodCall::getRuntimeDefinition(AnalysisDeclContextManager &M) const { const ObjCMessageExpr *E = getOriginExpr(); assert(E); Selector Sel = E->getSelector(); diff --git a/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp b/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp index eb5395e93c..9d4d16c6cd 100644 --- a/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp +++ b/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp @@ -640,7 +640,7 @@ void ExprEngine::defaultEvalCall(NodeBuilder &Bldr, ExplodedNode *Pred, // If we already tried once and failed, make sure we don't retry later. State = InlinedFailedState; } else { - RuntimeDefinition RD = Call->getRuntimeDefinition(); + RuntimeDefinition RD = Call->getRuntimeDefinition(AnalysisDeclContexts); const Decl *D = RD.getDecl(); if (D) { if (RD.mayHaveOtherDefinitions()) { |