diff options
Diffstat (limited to 'lib')
-rw-r--r-- | lib/Analysis/AnalysisDeclContext.cpp | 31 | ||||
-rw-r--r-- | lib/Analysis/BodyFarm.cpp | 91 | ||||
-rw-r--r-- | lib/Analysis/BodyFarm.h | 43 | ||||
-rw-r--r-- | lib/Analysis/CMakeLists.txt | 1 | ||||
-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 |
9 files changed, 199 insertions, 19 deletions
diff --git a/lib/Analysis/AnalysisDeclContext.cpp b/lib/Analysis/AnalysisDeclContext.cpp index f2ef0defd7..a4bf8d19b1 100644 --- a/lib/Analysis/AnalysisDeclContext.cpp +++ b/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/lib/Analysis/BodyFarm.cpp b/lib/Analysis/BodyFarm.cpp new file mode 100644 index 0000000000..217f607d56 --- /dev/null +++ b/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/lib/Analysis/BodyFarm.h b/lib/Analysis/BodyFarm.h new file mode 100644 index 0000000000..d503cc1bcd --- /dev/null +++ b/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/lib/Analysis/CMakeLists.txt b/lib/Analysis/CMakeLists.txt index 7c33d73714..ca166669fc 100644 --- a/lib/Analysis/CMakeLists.txt +++ b/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/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()) { |