summaryrefslogtreecommitdiffstats
path: root/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp')
-rw-r--r--lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp257
1 files changed, 136 insertions, 121 deletions
diff --git a/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp b/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp
index 7dbac3c..d71e528 100644
--- a/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp
+++ b/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp
@@ -20,31 +20,30 @@
#include "clang/AST/DeclObjC.h"
#include "clang/AST/ParentMap.h"
#include "clang/AST/RecursiveASTVisitor.h"
+#include "clang/Analysis/Analyses/LiveVariables.h"
#include "clang/Analysis/CFG.h"
#include "clang/Analysis/CallGraph.h"
-#include "clang/Analysis/Analyses/LiveVariables.h"
-#include "clang/StaticAnalyzer/Frontend/CheckerRegistration.h"
-#include "clang/StaticAnalyzer/Core/CheckerManager.h"
+#include "clang/Basic/FileManager.h"
+#include "clang/Basic/SourceManager.h"
+#include "clang/Lex/Preprocessor.h"
#include "clang/StaticAnalyzer/Checkers/LocalCheckers.h"
+#include "clang/StaticAnalyzer/Core/AnalyzerOptions.h"
+#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
#include "clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h"
+#include "clang/StaticAnalyzer/Core/CheckerManager.h"
+#include "clang/StaticAnalyzer/Core/PathDiagnosticConsumers.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h"
-#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h"
-#include "clang/StaticAnalyzer/Core/PathDiagnosticConsumers.h"
-
-#include "clang/Basic/FileManager.h"
-#include "clang/Basic/SourceManager.h"
-#include "clang/StaticAnalyzer/Core/AnalyzerOptions.h"
-#include "clang/Lex/Preprocessor.h"
-#include "llvm/Support/raw_ostream.h"
-#include "llvm/Support/Path.h"
-#include "llvm/Support/Program.h"
-#include "llvm/Support/Timer.h"
+#include "clang/StaticAnalyzer/Frontend/CheckerRegistration.h"
#include "llvm/ADT/DepthFirstIterator.h"
#include "llvm/ADT/OwningPtr.h"
+#include "llvm/ADT/PostOrderIterator.h"
#include "llvm/ADT/SmallPtrSet.h"
#include "llvm/ADT/Statistic.h"
-
+#include "llvm/Support/Path.h"
+#include "llvm/Support/Program.h"
+#include "llvm/Support/Timer.h"
+#include "llvm/Support/raw_ostream.h"
#include <queue>
using namespace clang;
@@ -54,9 +53,11 @@ using llvm::SmallPtrSet;
static ExplodedNode::Auditor* CreateUbiViz();
STATISTIC(NumFunctionTopLevel, "The # of functions at top level.");
-STATISTIC(NumFunctionsAnalyzed, "The # of functions analysed (as top level).");
+STATISTIC(NumFunctionsAnalyzed,
+ "The # of functions and blocks analyzed (as top level "
+ "with inlining turned on).");
STATISTIC(NumBlocksInAnalyzedFunctions,
- "The # of basic blocks in the analyzed functions.");
+ "The # of basic blocks in the analyzed functions.");
STATISTIC(PercentReachableBlocks, "The % of reachable basic blocks.");
STATISTIC(MaxCFGSize, "The maximum number of basic blocks in a function.");
@@ -64,11 +65,13 @@ STATISTIC(MaxCFGSize, "The maximum number of basic blocks in a function.");
// Special PathDiagnosticConsumers.
//===----------------------------------------------------------------------===//
-static void createPlistHTMLDiagnosticConsumer(PathDiagnosticConsumers &C,
+static void createPlistHTMLDiagnosticConsumer(AnalyzerOptions &AnalyzerOpts,
+ PathDiagnosticConsumers &C,
const std::string &prefix,
const Preprocessor &PP) {
- createHTMLDiagnosticConsumer(C, llvm::sys::path::parent_path(prefix), PP);
- createPlistDiagnosticConsumer(C, prefix, PP);
+ createHTMLDiagnosticConsumer(AnalyzerOpts, C,
+ llvm::sys::path::parent_path(prefix), PP);
+ createPlistDiagnosticConsumer(AnalyzerOpts, C, prefix, PP);
}
namespace {
@@ -188,13 +191,14 @@ public:
switch (Opts->AnalysisDiagOpt) {
default:
#define ANALYSIS_DIAGNOSTICS(NAME, CMDFLAG, DESC, CREATEFN, AUTOCREATE) \
- case PD_##NAME: CREATEFN(PathConsumers, OutDir, PP); break;
+ case PD_##NAME: CREATEFN(*Opts.getPtr(), PathConsumers, OutDir, PP);\
+ break;
#include "clang/StaticAnalyzer/Core/Analyses.def"
}
} else if (Opts->AnalysisDiagOpt == PD_TEXT) {
// Create the text client even without a specified output file since
// it just uses diagnostic notes.
- createTextPathDiagnosticConsumer(PathConsumers, "", PP);
+ createTextPathDiagnosticConsumer(*Opts.getPtr(), PathConsumers, "", PP);
}
// Create the analyzer component creators.
@@ -208,14 +212,15 @@ public:
switch (Opts->AnalysisConstraintsOpt) {
default:
- llvm_unreachable("Unknown store manager.");
+ llvm_unreachable("Unknown constraint manager.");
#define ANALYSIS_CONSTRAINTS(NAME, CMDFLAG, DESC, CREATEFN) \
case NAME##Model: CreateConstraintMgr = CREATEFN; break;
#include "clang/StaticAnalyzer/Core/Analyses.def"
}
}
- void DisplayFunction(const Decl *D, AnalysisMode Mode) {
+ void DisplayFunction(const Decl *D, AnalysisMode Mode,
+ ExprEngine::InliningModes IMode) {
if (!Opts->AnalyzerDisplayProgress)
return;
@@ -226,8 +231,18 @@ public:
if (Mode == AM_Syntax)
llvm::errs() << " (Syntax)";
- else if (Mode == AM_Path)
- llvm::errs() << " (Path)";
+ else if (Mode == AM_Path) {
+ llvm::errs() << " (Path, ";
+ switch (IMode) {
+ case ExprEngine::Inline_Minimal:
+ llvm::errs() << " Inline_Minimal";
+ break;
+ case ExprEngine::Inline_Regular:
+ llvm::errs() << " Inline_Regular";
+ break;
+ }
+ llvm::errs() << ")";
+ }
else
assert(Mode == (AM_Syntax | AM_Path) && "Unexpected mode!");
@@ -268,6 +283,12 @@ public:
virtual void HandleTranslationUnit(ASTContext &C);
+ /// \brief Determine which inlining mode should be used when this function is
+ /// analyzed. This allows to redefine the default inlining policies when
+ /// analyzing a given function.
+ ExprEngine::InliningModes
+ getInliningModeForFunction(const Decl *D, SetOfConstDecls Visited);
+
/// \brief Build the call graph for all the top level decls of this TU and
/// use it to define the order in which the functions should be visited.
void HandleDeclsCallGraph(const unsigned LocalTUDeclsSize);
@@ -279,10 +300,14 @@ public:
/// set of functions which should be considered analyzed after analyzing the
/// given root function.
void HandleCode(Decl *D, AnalysisMode Mode,
+ ExprEngine::InliningModes IMode = ExprEngine::Inline_Minimal,
SetOfConstDecls *VisitedCallees = 0);
- void RunPathSensitiveChecks(Decl *D, SetOfConstDecls *VisitedCallees);
+ void RunPathSensitiveChecks(Decl *D,
+ ExprEngine::InliningModes IMode,
+ SetOfConstDecls *VisitedCallees);
void ActionExprEngine(Decl *D, bool ObjCGCEnabled,
+ ExprEngine::InliningModes IMode,
SetOfConstDecls *VisitedCallees);
/// Visitors for the RecursiveASTVisitor.
@@ -305,14 +330,25 @@ public:
// only determined when they are instantiated.
if (FD->isThisDeclarationADefinition() &&
!FD->isDependentContext()) {
+ assert(RecVisitorMode == AM_Syntax || Mgr->shouldInlineCall() == false);
HandleCode(FD, RecVisitorMode);
}
return true;
}
bool VisitObjCMethodDecl(ObjCMethodDecl *MD) {
- if (MD->isThisDeclarationADefinition())
+ if (MD->isThisDeclarationADefinition()) {
+ assert(RecVisitorMode == AM_Syntax || Mgr->shouldInlineCall() == false);
HandleCode(MD, RecVisitorMode);
+ }
+ return true;
+ }
+
+ bool VisitBlockDecl(BlockDecl *BD) {
+ if (BD->hasBody()) {
+ assert(RecVisitorMode == AM_Syntax || Mgr->shouldInlineCall() == false);
+ HandleCode(BD, RecVisitorMode);
+ }
return true;
}
@@ -352,95 +388,90 @@ void AnalysisConsumer::storeTopLevelDecls(DeclGroupRef DG) {
}
}
-static bool shouldSkipFunction(CallGraphNode *N,
- SmallPtrSet<CallGraphNode*,24> Visited) {
- // We want to re-analyse the functions as top level in several cases:
+static bool shouldSkipFunction(const Decl *D,
+ SetOfConstDecls Visited,
+ SetOfConstDecls VisitedAsTopLevel) {
+ if (VisitedAsTopLevel.count(D))
+ return true;
+
+ // We want to re-analyse the functions as top level in the following cases:
// - The 'init' methods should be reanalyzed because
// ObjCNonNilReturnValueChecker assumes that '[super init]' never returns
- // 'nil' and unless we analyze the 'init' functions as top level, we will not
- // catch errors within defensive code.
+ // 'nil' and unless we analyze the 'init' functions as top level, we will
+ // not catch errors within defensive code.
// - We want to reanalyze all ObjC methods as top level to report Retain
// Count naming convention errors more aggressively.
- if (isa<ObjCMethodDecl>(N->getDecl()))
+ if (isa<ObjCMethodDecl>(D))
return false;
// Otherwise, if we visited the function before, do not reanalyze it.
- return Visited.count(N);
+ return Visited.count(D);
}
-void AnalysisConsumer::HandleDeclsCallGraph(const unsigned LocalTUDeclsSize) {
- // Otherwise, use the Callgraph to derive the order.
- // Build the Call Graph.
- CallGraph CG;
+ExprEngine::InliningModes
+AnalysisConsumer::getInliningModeForFunction(const Decl *D,
+ SetOfConstDecls Visited) {
+ // We want to reanalyze all ObjC methods as top level to report Retain
+ // Count naming convention errors more aggressively. But we should tune down
+ // inlining when reanalyzing an already inlined function.
+ if (Visited.count(D)) {
+ assert(isa<ObjCMethodDecl>(D) &&
+ "We are only reanalyzing ObjCMethods.");
+ const ObjCMethodDecl *ObjCM = cast<ObjCMethodDecl>(D);
+ if (ObjCM->getMethodFamily() != OMF_init)
+ return ExprEngine::Inline_Minimal;
+ }
- // Add all the top level declarations to the graph.
+ return ExprEngine::Inline_Regular;
+}
+
+void AnalysisConsumer::HandleDeclsCallGraph(const unsigned LocalTUDeclsSize) {
+ // Build the Call Graph by adding all the top level declarations to the graph.
// Note: CallGraph can trigger deserialization of more items from a pch
// (though HandleInterestingDecl); triggering additions to LocalTUDecls.
// We rely on random access to add the initially processed Decls to CG.
+ CallGraph CG;
for (unsigned i = 0 ; i < LocalTUDeclsSize ; ++i) {
CG.addToCallGraph(LocalTUDecls[i]);
}
- // Find the top level nodes - children of root + the unreachable (parentless)
- // nodes.
- llvm::SmallVector<CallGraphNode*, 24> TopLevelFunctions;
- for (CallGraph::nodes_iterator TI = CG.parentless_begin(),
- TE = CG.parentless_end(); TI != TE; ++TI) {
- TopLevelFunctions.push_back(*TI);
+ // Walk over all of the call graph nodes in topological order, so that we
+ // analyze parents before the children. Skip the functions inlined into
+ // the previously processed functions. Use external Visited set to identify
+ // inlined functions. The topological order allows the "do not reanalyze
+ // previously inlined function" performance heuristic to be triggered more
+ // often.
+ SetOfConstDecls Visited;
+ SetOfConstDecls VisitedAsTopLevel;
+ llvm::ReversePostOrderTraversal<clang::CallGraph*> RPOT(&CG);
+ for (llvm::ReversePostOrderTraversal<clang::CallGraph*>::rpo_iterator
+ I = RPOT.begin(), E = RPOT.end(); I != E; ++I) {
NumFunctionTopLevel++;
- }
- CallGraphNode *Entry = CG.getRoot();
- for (CallGraphNode::iterator I = Entry->begin(),
- E = Entry->end(); I != E; ++I) {
- TopLevelFunctions.push_back(*I);
- NumFunctionTopLevel++;
- }
- // Make sure the nodes are sorted in order reverse of their definition in the
- // translation unit. This step is very important for performance. It ensures
- // that we analyze the root functions before the externally available
- // subroutines.
- std::deque<CallGraphNode*> BFSQueue;
- for (llvm::SmallVector<CallGraphNode*, 24>::reverse_iterator
- TI = TopLevelFunctions.rbegin(), TE = TopLevelFunctions.rend();
- TI != TE; ++TI)
- BFSQueue.push_back(*TI);
-
- // BFS over all of the functions, while skipping the ones inlined into
- // the previously processed functions. Use external Visited set, which is
- // also modified when we inline a function.
- SmallPtrSet<CallGraphNode*,24> Visited;
- while(!BFSQueue.empty()) {
- CallGraphNode *N = BFSQueue.front();
- BFSQueue.pop_front();
-
- // Push the children into the queue.
- for (CallGraphNode::const_iterator CI = N->begin(),
- CE = N->end(); CI != CE; ++CI) {
- if (!shouldSkipFunction(*CI, Visited))
- BFSQueue.push_back(*CI);
- }
+ CallGraphNode *N = *I;
+ Decl *D = N->getDecl();
+
+ // Skip the abstract root node.
+ if (!D)
+ continue;
// Skip the functions which have been processed already or previously
// inlined.
- if (shouldSkipFunction(N, Visited))
+ if (shouldSkipFunction(D, Visited, VisitedAsTopLevel))
continue;
// Analyze the function.
SetOfConstDecls VisitedCallees;
- Decl *D = N->getDecl();
- assert(D);
- HandleCode(D, AM_Path,
+
+ HandleCode(D, AM_Path, getInliningModeForFunction(D, Visited),
(Mgr->options.InliningMode == All ? 0 : &VisitedCallees));
// Add the visited callees to the global visited set.
for (SetOfConstDecls::iterator I = VisitedCallees.begin(),
E = VisitedCallees.end(); I != E; ++I) {
- CallGraphNode *VN = CG.getNode(*I);
- if (VN)
- Visited.insert(VN);
+ Visited.insert(*I);
}
- Visited.insert(N);
+ VisitedAsTopLevel.insert(D);
}
}
@@ -503,16 +534,6 @@ void AnalysisConsumer::HandleTranslationUnit(ASTContext &C) {
}
-static void FindBlocks(DeclContext *D, SmallVectorImpl<Decl*> &WL) {
- if (BlockDecl *BD = dyn_cast<BlockDecl>(D))
- WL.push_back(BD);
-
- for (DeclContext::decl_iterator I = D->decls_begin(), E = D->decls_end();
- I!=E; ++I)
- if (DeclContext *DC = dyn_cast<DeclContext>(*I))
- FindBlocks(DC, WL);
-}
-
static std::string getFunctionName(const Decl *D) {
if (const ObjCMethodDecl *ID = dyn_cast<ObjCMethodDecl>(D)) {
return ID->getSelector().getAsString();
@@ -548,40 +569,32 @@ AnalysisConsumer::getModeForDecl(Decl *D, AnalysisMode Mode) {
}
void AnalysisConsumer::HandleCode(Decl *D, AnalysisMode Mode,
+ ExprEngine::InliningModes IMode,
SetOfConstDecls *VisitedCallees) {
+ if (!D->hasBody())
+ return;
Mode = getModeForDecl(D, Mode);
if (Mode == AM_None)
return;
- DisplayFunction(D, Mode);
+ DisplayFunction(D, Mode, IMode);
CFG *DeclCFG = Mgr->getCFG(D);
if (DeclCFG) {
unsigned CFGSize = DeclCFG->size();
MaxCFGSize = MaxCFGSize < CFGSize ? CFGSize : MaxCFGSize;
}
-
// Clear the AnalysisManager of old AnalysisDeclContexts.
Mgr->ClearContexts();
-
- // Dispatch on the actions.
- SmallVector<Decl*, 10> WL;
- WL.push_back(D);
-
- if (D->hasBody() && Opts->AnalyzeNestedBlocks)
- FindBlocks(cast<DeclContext>(D), WL);
-
BugReporter BR(*Mgr);
- for (SmallVectorImpl<Decl*>::iterator WI=WL.begin(), WE=WL.end();
- WI != WE; ++WI)
- if ((*WI)->hasBody()) {
- if (Mode & AM_Syntax)
- checkerMgr->runCheckersOnASTBody(*WI, *Mgr, BR);
- if ((Mode & AM_Path) && checkerMgr->hasPathSensitiveCheckers()) {
- RunPathSensitiveChecks(*WI, VisitedCallees);
- NumFunctionsAnalyzed++;
- }
- }
+
+ if (Mode & AM_Syntax)
+ checkerMgr->runCheckersOnASTBody(D, *Mgr, BR);
+ if ((Mode & AM_Path) && checkerMgr->hasPathSensitiveCheckers()) {
+ RunPathSensitiveChecks(D, IMode, VisitedCallees);
+ if (IMode != ExprEngine::Inline_Minimal)
+ NumFunctionsAnalyzed++;
+ }
}
//===----------------------------------------------------------------------===//
@@ -589,6 +602,7 @@ void AnalysisConsumer::HandleCode(Decl *D, AnalysisMode Mode,
//===----------------------------------------------------------------------===//
void AnalysisConsumer::ActionExprEngine(Decl *D, bool ObjCGCEnabled,
+ ExprEngine::InliningModes IMode,
SetOfConstDecls *VisitedCallees) {
// Construct the analysis engine. First check if the CFG is valid.
// FIXME: Inter-procedural analysis will need to handle invalid CFGs.
@@ -599,7 +613,7 @@ void AnalysisConsumer::ActionExprEngine(Decl *D, bool ObjCGCEnabled,
if (!Mgr->getAnalysisDeclContext(D)->getAnalysis<RelaxedLiveVariables>())
return;
- ExprEngine Eng(*Mgr, ObjCGCEnabled, VisitedCallees, &FunctionSummaries);
+ ExprEngine Eng(*Mgr, ObjCGCEnabled, VisitedCallees, &FunctionSummaries,IMode);
// Set the graph auditor.
OwningPtr<ExplodedNode::Auditor> Auditor;
@@ -610,7 +624,7 @@ void AnalysisConsumer::ActionExprEngine(Decl *D, bool ObjCGCEnabled,
// Execute the worklist algorithm.
Eng.ExecuteWorkList(Mgr->getAnalysisDeclContextManager().getStackFrame(D),
- Mgr->options.MaxNodes);
+ Mgr->options.getMaxNodesPerTopLevelFunction());
// Release the auditor (if any) so that it doesn't monitor the graph
// created BugReporter.
@@ -625,20 +639,21 @@ void AnalysisConsumer::ActionExprEngine(Decl *D, bool ObjCGCEnabled,
}
void AnalysisConsumer::RunPathSensitiveChecks(Decl *D,
+ ExprEngine::InliningModes IMode,
SetOfConstDecls *Visited) {
switch (Mgr->getLangOpts().getGC()) {
case LangOptions::NonGC:
- ActionExprEngine(D, false, Visited);
+ ActionExprEngine(D, false, IMode, Visited);
break;
case LangOptions::GCOnly:
- ActionExprEngine(D, true, Visited);
+ ActionExprEngine(D, true, IMode, Visited);
break;
case LangOptions::HybridGC:
- ActionExprEngine(D, false, Visited);
- ActionExprEngine(D, true, Visited);
+ ActionExprEngine(D, false, IMode, Visited);
+ ActionExprEngine(D, true, IMode, Visited);
break;
}
}
OpenPOWER on IntegriCloud