summaryrefslogtreecommitdiffstats
path: root/clang/lib
diff options
context:
space:
mode:
authorTed Kremenek <kremenek@apple.com>2012-09-21 00:09:11 +0000
committerTed Kremenek <kremenek@apple.com>2012-09-21 00:09:11 +0000
commit14f779c4d69098df876c7983549c0e44d9b12aa7 (patch)
treef2e449a96009109e0e6103cf15a517eee1e50d37 /clang/lib
parent96ea28046c7ea912a722a6d0b19a20266001e1f1 (diff)
downloadbcm5719-llvm-14f779c4d69098df876c7983549c0e44d9b12aa7.tar.gz
bcm5719-llvm-14f779c4d69098df876c7983549c0e44d9b12aa7.zip
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. llvm-svn: 164339
Diffstat (limited to 'clang/lib')
-rw-r--r--clang/lib/Analysis/AnalysisDeclContext.cpp31
-rw-r--r--clang/lib/Analysis/BodyFarm.cpp91
-rw-r--r--clang/lib/Analysis/BodyFarm.h43
-rw-r--r--clang/lib/Analysis/CMakeLists.txt1
-rw-r--r--clang/lib/StaticAnalyzer/Core/AnalysisManager.cpp3
-rw-r--r--clang/lib/StaticAnalyzer/Core/AnalyzerOptions.cpp4
-rw-r--r--clang/lib/StaticAnalyzer/Core/BugReporter.cpp25
-rw-r--r--clang/lib/StaticAnalyzer/Core/CallEvent.cpp18
-rw-r--r--clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp2
9 files changed, 199 insertions, 19 deletions
diff --git a/clang/lib/Analysis/AnalysisDeclContext.cpp b/clang/lib/Analysis/AnalysisDeclContext.cpp
index f2ef0defd77..a4bf8d19b17 100644
--- a/clang/lib/Analysis/AnalysisDeclContext.cpp
+++ b/clang/lib/Analysis/AnalysisDeclContext.cpp
@@ -29,13 +29,15 @@
#include "llvm/ADT/SmallPtrSet.h"
#include "llvm/Support/ErrorHandling.h"
+#include "BodyFarm.h"
+
using namespace clang;
typedef llvm::DenseMap<const void *, ManagedAnalysis *> ManagedAnalysisMap;
AnalysisDeclContext::AnalysisDeclContext(AnalysisDeclContextManager *Mgr,
- const Decl *d,
- const CFG::BuildOptions &buildOptions)
+ const Decl *d,
+ const CFG::BuildOptions &buildOptions)
: Manager(Mgr),
D(d),
cfgBuildOptions(buildOptions),
@@ -49,7 +51,7 @@ AnalysisDeclContext::AnalysisDeclContext(AnalysisDeclContextManager *Mgr,
}
AnalysisDeclContext::AnalysisDeclContext(AnalysisDeclContextManager *Mgr,
- const Decl *d)
+ const Decl *d)
: Manager(Mgr),
D(d),
forcedBlkExprs(0),
@@ -64,7 +66,10 @@ AnalysisDeclContext::AnalysisDeclContext(AnalysisDeclContextManager *Mgr,
AnalysisDeclContextManager::AnalysisDeclContextManager(bool useUnoptimizedCFG,
bool addImplicitDtors,
bool addInitializers,
- bool addTemporaryDtors) {
+ bool addTemporaryDtors,
+ bool synthesizeBodies)
+ : SynthesizeBodies(synthesizeBodies)
+{
cfgBuildOptions.PruneTriviallyFalseEdges = !useUnoptimizedCFG;
cfgBuildOptions.AddImplicitDtors = addImplicitDtors;
cfgBuildOptions.AddInitializers = addInitializers;
@@ -77,9 +82,18 @@ void AnalysisDeclContextManager::clear() {
Contexts.clear();
}
+static BodyFarm &getBodyFarm(ASTContext &C) {
+ static BodyFarm *BF = new BodyFarm(C);
+ return *BF;
+}
+
Stmt *AnalysisDeclContext::getBody() const {
- if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(D))
- return FD->getBody();
+ if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(D)) {
+ Stmt *Body = FD->getBody();
+ if (!Body && Manager && Manager->synthesizeBodies())
+ return getBodyFarm(getASTContext()).getBody(FD);
+ return Body;
+ }
else if (const ObjCMethodDecl *MD = dyn_cast<ObjCMethodDecl>(D))
return MD->getBody();
else if (const BlockDecl *BD = dyn_cast<BlockDecl>(D))
@@ -203,6 +217,11 @@ PseudoConstantAnalysis *AnalysisDeclContext::getPseudoConstantAnalysis() {
}
AnalysisDeclContext *AnalysisDeclContextManager::getContext(const Decl *D) {
+ if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(D)) {
+ FD->hasBody(FD);
+ D = FD;
+ }
+
AnalysisDeclContext *&AC = Contexts[D];
if (!AC)
AC = new AnalysisDeclContext(this, D, cfgBuildOptions);
diff --git a/clang/lib/Analysis/BodyFarm.cpp b/clang/lib/Analysis/BodyFarm.cpp
new file mode 100644
index 00000000000..217f607d56f
--- /dev/null
+++ b/clang/lib/Analysis/BodyFarm.cpp
@@ -0,0 +1,91 @@
+//== BodyFarm.cpp - Factory for conjuring up fake bodies ----------*- C++ -*-//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// BodyFarm is a factory for creating faux implementations for functions/methods
+// for analysis purposes.
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/ADT/StringSwitch.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/Expr.h"
+#include "clang/AST/Decl.h"
+#include "BodyFarm.h"
+
+using namespace clang;
+
+typedef Stmt *(*FunctionFarmer)(ASTContext &C, const FunctionDecl *D);
+
+
+/// Create a fake body for dispatch_sync.
+static Stmt *create_dispatch_sync(ASTContext &C, const FunctionDecl *D) {
+ // Check if we have at least two parameters.
+ if (D->param_size() != 2)
+ return 0;
+
+ // Check if the second parameter is a block.
+ const ParmVarDecl *PV = D->getParamDecl(1);
+ QualType Ty = PV->getType();
+ const BlockPointerType *BPT = Ty->getAs<BlockPointerType>();
+ if (!BPT)
+ return 0;
+
+ // Check if the block pointer type takes no arguments and
+ // returns void.
+ const FunctionProtoType *FT =
+ BPT->getPointeeType()->getAs<FunctionProtoType>();
+ if (!FT || !FT->getResultType()->isVoidType() ||
+ FT->getNumArgs() != 0)
+ return 0;
+
+ // Everything checks out. Create a fake body that just calls the block.
+ // This is basically just an AST dump of:
+ //
+ // void dispatch_sync(dispatch_queue_t queue, void (^block)(void)) {
+ // block();
+ // }
+ //
+ DeclRefExpr *DR = DeclRefExpr::CreateEmpty(C, false, false, false, false);
+ DR->setDecl(const_cast<ParmVarDecl*>(PV));
+ DR->setValueKind(VK_LValue);
+ ImplicitCastExpr *ICE = ImplicitCastExpr::Create(C, Ty, CK_LValueToRValue,
+ DR, 0, VK_RValue);
+ CallExpr *CE = new (C) CallExpr(C, ICE, ArrayRef<Expr*>(), C.VoidTy,
+ VK_RValue, SourceLocation());
+ return CE;
+}
+
+Stmt *BodyFarm::getBody(const FunctionDecl *D) {
+ D = D->getCanonicalDecl();
+
+ llvm::Optional<Stmt *> &Val = Bodies[D];
+ if (Val.hasValue())
+ return Val.getValue();
+
+ Val = 0;
+
+ if (D->getIdentifier() == 0)
+ return 0;
+
+ StringRef Name = D->getName();
+ if (Name.empty())
+ return 0;
+
+ FunctionFarmer FF =
+ llvm::StringSwitch<FunctionFarmer>(Name)
+ .Case("dispatch_sync", create_dispatch_sync)
+ .Default(NULL);
+
+ if (FF) {
+ Val = FF(C, D);
+ }
+
+ return Val.getValue();
+}
+
diff --git a/clang/lib/Analysis/BodyFarm.h b/clang/lib/Analysis/BodyFarm.h
new file mode 100644
index 00000000000..d503cc1bcd0
--- /dev/null
+++ b/clang/lib/Analysis/BodyFarm.h
@@ -0,0 +1,43 @@
+//== BodyFarm.h - Factory for conjuring up fake bodies -------------*- C++ -*-//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// BodyFarm is a factory for creating faux implementations for functions/methods
+// for analysis purposes.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_ANALYSIS_BODYFARM_H
+#define LLVM_CLANG_ANALYSIS_BODYFARM_H
+
+#include "llvm/ADT/Optional.h"
+#include "llvm/ADT/DenseMap.h"
+
+namespace clang {
+
+class ASTContext;
+class Decl;
+class FunctionDecl;
+class Stmt;
+
+class BodyFarm {
+public:
+ BodyFarm(ASTContext &C) : C(C) {}
+
+ /// Factory method for creating bodies for ordinary functions.
+ Stmt *getBody(const FunctionDecl *D);
+
+private:
+ typedef llvm::DenseMap<const Decl *, llvm::Optional<Stmt *> > BodyMap;
+
+ ASTContext &C;
+ BodyMap Bodies;
+};
+}
+
+#endif
diff --git a/clang/lib/Analysis/CMakeLists.txt b/clang/lib/Analysis/CMakeLists.txt
index 7c33d737148..ca166669fc8 100644
--- a/clang/lib/Analysis/CMakeLists.txt
+++ b/clang/lib/Analysis/CMakeLists.txt
@@ -1,5 +1,6 @@
add_clang_library(clangAnalysis
AnalysisDeclContext.cpp
+ BodyFarm.cpp
CFG.cpp
CFGReachabilityAnalysis.cpp
CFGStmtMap.cpp
diff --git a/clang/lib/StaticAnalyzer/Core/AnalysisManager.cpp b/clang/lib/StaticAnalyzer/Core/AnalysisManager.cpp
index ebd2336080a..09a0debaace 100644
--- a/clang/lib/StaticAnalyzer/Core/AnalysisManager.cpp
+++ b/clang/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/clang/lib/StaticAnalyzer/Core/AnalyzerOptions.cpp b/clang/lib/StaticAnalyzer/Core/AnalyzerOptions.cpp
index a96dfe1e48b..cb7df6b3fd0 100644
--- a/clang/lib/StaticAnalyzer/Core/AnalyzerOptions.cpp
+++ b/clang/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/clang/lib/StaticAnalyzer/Core/BugReporter.cpp b/clang/lib/StaticAnalyzer/Core/BugReporter.cpp
index 6cbed952c9f..e95f31c0d6b 100644
--- a/clang/lib/StaticAnalyzer/Core/BugReporter.cpp
+++ b/clang/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/clang/lib/StaticAnalyzer/Core/CallEvent.cpp b/clang/lib/StaticAnalyzer/Core/CallEvent.cpp
index 3b4c13471b1..3743871361c 100644
--- a/clang/lib/StaticAnalyzer/Core/CallEvent.cpp
+++ b/clang/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/clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp
index eb5395e93c7..9d4d16c6cd4 100644
--- a/clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp
+++ b/clang/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()) {
OpenPOWER on IntegriCloud