diff options
author | Ted Kremenek <kremenek@apple.com> | 2008-07-02 00:03:09 +0000 |
---|---|---|
committer | Ted Kremenek <kremenek@apple.com> | 2008-07-02 00:03:09 +0000 |
commit | f4381fddf152a63e1ac97185293c47ec0ac2f1a6 (patch) | |
tree | d801ed4df75b9c65dfa1899a94955e5a211d68a5 | |
parent | 91d1a14be8eabe235fcf27b070bc9c568d5e1e63 (diff) |
Added AnalysisConsumer, a meta-level ASTConsumer class to drive various
analyses. This potentially is the primordial origins of a Clang-equivalent
"PassManager".
The new AnalysisConsumer interface allows multiple analyses to be run from a
single invocation of Clang.
Migrated the logic of "-warn-dead-stores" and "-warn-uninit-values" to use the
new AnalysisConsumer interface. The new interface results in a significant code
reduction to incorporate an analysis into the Driver.
Updated a test case to (correctly) acknowledge that it contains a dead store
(this check wasn't being performed because it was previously masked by
-warn-uninit-values).
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@52996 91177308-0d34-0410-b5e6-96231b3b80d8
-rw-r--r-- | Driver/ASTConsumers.cpp | 52 | ||||
-rw-r--r-- | Driver/ASTConsumers.h | 7 | ||||
-rw-r--r-- | Driver/AnalysisConsumer.cpp | 248 | ||||
-rw-r--r-- | Driver/AnalysisConsumer.h | 34 | ||||
-rw-r--r-- | Driver/clang.cpp | 39 | ||||
-rw-r--r-- | test/Analysis/conditional-op-missing-lhs.c | 2 |
6 files changed, 310 insertions, 72 deletions
diff --git a/Driver/ASTConsumers.cpp b/Driver/ASTConsumers.cpp index 1383a5ed5d..dbdce45d1c 100644 --- a/Driver/ASTConsumers.cpp +++ b/Driver/ASTConsumers.cpp @@ -635,58 +635,6 @@ ASTConsumer *clang::CreateLiveVarAnalyzer(const std::string& fname) { } //===----------------------------------------------------------------------===// -// DeadStores - run checker to locate dead stores in a function - -namespace { - class DeadStoreVisitor : public CFGVisitor { - Diagnostic &Diags; - ASTContext *Ctx; - public: - DeadStoreVisitor(Diagnostic &diags) : Diags(diags) {} - virtual void Initialize(ASTContext &Context) { - Ctx = &Context; - } - - virtual void VisitCFG(CFG& C, Decl& CD) { - llvm::OwningPtr<ParentMap> PM(new ParentMap(CD.getCodeBody())); - CheckDeadStores(C, *Ctx, *PM, Diags); - } - - virtual bool printFuncDeclStart() { return false; } - }; -} // end anonymous namespace - -ASTConsumer *clang::CreateDeadStoreChecker(Diagnostic &Diags) { - return new DeadStoreVisitor(Diags); -} - -//===----------------------------------------------------------------------===// -// Unitialized Values - run checker to flag potential uses of uninitalized -// variables. - -namespace { - class UninitValsVisitor : public CFGVisitor { - Diagnostic &Diags; - ASTContext *Ctx; - public: - UninitValsVisitor(Diagnostic &diags) : Diags(diags) {} - virtual void Initialize(ASTContext &Context) { - Ctx = &Context; - } - - virtual void VisitCFG(CFG& C, Decl&) { - CheckUninitializedValues(C, *Ctx, Diags); - } - - virtual bool printFuncDeclStart() { return false; } - }; -} // end anonymous namespace - -ASTConsumer *clang::CreateUnitValsChecker(Diagnostic &Diags) { - return new UninitValsVisitor(Diags); -} - -//===----------------------------------------------------------------------===// // CheckerConsumer - Generic Driver for running intra-procedural path-sensitive // analyses. diff --git a/Driver/ASTConsumers.h b/Driver/ASTConsumers.h index ca93e0fbf9..5853664832 100644 --- a/Driver/ASTConsumers.h +++ b/Driver/ASTConsumers.h @@ -30,7 +30,6 @@ struct LangOptions; class Preprocessor; class PreprocessorFactory; - ASTConsumer *CreateASTPrinter(std::ostream* OS = NULL); ASTConsumer *CreateASTDumper(); @@ -41,10 +40,6 @@ ASTConsumer *CreateCFGDumper(bool ViewGraphs, const std::string& FName); ASTConsumer *CreateLiveVarAnalyzer(const std::string& fname); -ASTConsumer *CreateDeadStoreChecker(Diagnostic &Diags); - -ASTConsumer *CreateUnitValsChecker(Diagnostic &Diags); - ASTConsumer *CreateGRSimpleVals(Diagnostic &Diags, Preprocessor* PP, PreprocessorFactory* PPF, const std::string& Function, @@ -75,4 +70,6 @@ ASTConsumer *CreateASTSerializer(const std::string& InFile, } // end clang namespace +#include "AnalysisConsumer.h" + #endif diff --git a/Driver/AnalysisConsumer.cpp b/Driver/AnalysisConsumer.cpp new file mode 100644 index 0000000000..11fb4a95c1 --- /dev/null +++ b/Driver/AnalysisConsumer.cpp @@ -0,0 +1,248 @@ +//===--- AnalysisConsumer.cpp - ASTConsumer for running Analyses ----------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// "Meta" ASTConsumer for running different source analyses. +// +//===----------------------------------------------------------------------===// + +#include "ASTConsumers.h" +#include "clang/AST/ASTConsumer.h" +#include "clang/AST/Decl.h" +#include "clang/AST/DeclObjC.h" +#include "llvm/Support/Compiler.h" +#include "llvm/ADT/ImmutableList.h" +#include "llvm/ADT/OwningPtr.h" +#include "clang/AST/CFG.h" +#include "clang/Analysis/Analyses/LiveVariables.h" +#include "clang/Analysis/PathDiagnostic.h" +#include "clang/Basic/SourceManager.h" +#include "clang/Basic/FileManager.h" +#include "clang/AST/ParentMap.h" +#include "clang/Analysis/Analyses/LiveVariables.h" +#include "clang/Analysis/LocalCheckers.h" +#include "clang/Analysis/PathSensitive/GRTransferFuncs.h" +#include "clang/Analysis/PathSensitive/GRExprEngine.h" + +using namespace clang; + + +//===----------------------------------------------------------------------===// +// Basic type definitions. +//===----------------------------------------------------------------------===// + +namespace { + + class AnalysisManager; + typedef void (*CodeAction)(AnalysisManager& Mgr); + +} // end anonymous namespace + +//===----------------------------------------------------------------------===// +// AnalysisConsumer declaration. +//===----------------------------------------------------------------------===// + +namespace { + + class VISIBILITY_HIDDEN AnalysisConsumer : public ASTConsumer { + typedef llvm::ImmutableList<CodeAction> Actions; + Actions FunctionActions; + Actions ObjCMethodActions; + + Actions::Factory F; + + public: + const bool Visualize; + const bool TrimGraph; + const LangOptions& LOpts; + Diagnostic &Diags; + ASTContext* Ctx; + Preprocessor* PP; + PreprocessorFactory* PPF; + const std::string HTMLDir; + const std::string FName; + llvm::OwningPtr<PathDiagnosticClient> PD; + bool AnalyzeAll; + + AnalysisConsumer(Diagnostic &diags, Preprocessor* pp, + PreprocessorFactory* ppf, + const LangOptions& lopts, + const std::string& fname, + const std::string& htmldir, + bool visualize, bool trim, bool analyzeAll) + : FunctionActions(F.GetEmptyList()), ObjCMethodActions(F.GetEmptyList()), + Visualize(visualize), TrimGraph(trim), LOpts(lopts), Diags(diags), + Ctx(0), PP(pp), PPF(ppf), + HTMLDir(htmldir), + FName(fname), + AnalyzeAll(analyzeAll) {} + + void addCodeAction(CodeAction action) { + FunctionActions = F.Concat(action, FunctionActions); + ObjCMethodActions = F.Concat(action, ObjCMethodActions); + } + + virtual void Initialize(ASTContext &Context) { + Ctx = &Context; + } + + virtual void HandleTopLevelDecl(Decl *D); + void HandleCode(Decl* D, Stmt* Body, Actions actions); + }; + + + class VISIBILITY_HIDDEN AnalysisManager { + Decl* D; + Stmt* Body; + AnalysisConsumer& C; + + llvm::OwningPtr<CFG> cfg; + llvm::OwningPtr<LiveVariables> liveness; + llvm::OwningPtr<ParentMap> PM; + + public: + AnalysisManager(AnalysisConsumer& c, Decl* d, Stmt* b) + : D(d), Body(b), C(c) {} + + + Decl* getCodeDecl() const { return D; } + Stmt* getBody() const { return Body; } + + CFG* getCFG() { + if (!cfg) cfg.reset(CFG::buildCFG(getBody())); + return cfg.get(); + } + + ParentMap* getParentMap() { + if (!PM) PM.reset(new ParentMap(getBody())); + return PM.get(); + } + + ASTContext& getContext() { + return *C.Ctx; + } + + Diagnostic& getDiagnostic() { + return C.Diags; + } + + LiveVariables* getLiveVariables() { + if (!liveness) liveness.reset(new LiveVariables(*getCFG())); + return liveness.get(); + } + }; + +} // end anonymous namespace + +namespace llvm { + template <> struct FoldingSetTrait<CodeAction> { + static inline void Profile(CodeAction X, FoldingSetNodeID& ID) { + ID.AddPointer(reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(X))); + } + }; +} + +//===----------------------------------------------------------------------===// +// AnalysisConsumer implementation. +//===----------------------------------------------------------------------===// + +void AnalysisConsumer::HandleTopLevelDecl(Decl *D) { + switch (D->getKind()) { + case Decl::Function: { + FunctionDecl* FD = cast<FunctionDecl>(D); + Stmt* Body = FD->getBody(); + if (Body) HandleCode(FD, Body, FunctionActions); + break; + } + + case Decl::ObjCMethod: { + ObjCMethodDecl* MD = cast<ObjCMethodDecl>(D); + Stmt* Body = MD->getBody(); + if (Body) HandleCode(MD, Body, ObjCMethodActions); + break; + } + + default: + break; + } +} + +void AnalysisConsumer::HandleCode(Decl* D, Stmt* Body, Actions actions) { + + // Don't run the actions if an error has occured with parsing the file. + if (Diags.hasErrorOccurred()) + return; + + SourceLocation Loc = D->getLocation(); + + // Only run actions on declarations defined in actual source. + if (!Loc.isFileID()) + return; + + // Don't run the actions on declarations in header files unless + // otherwise specified. + if (!AnalyzeAll && !Ctx->getSourceManager().isFromMainFile(Loc)) + return; + + // Create an AnalysisManager that will manage the state for analyzing + // this method/function. + AnalysisManager mgr(*this, D, Body); + + // Dispatch on the actions. + for (Actions::iterator I = actions.begin(), + E = actions.end(); I != E; ++I) + ((*I).getHead())(mgr); +} + +//===----------------------------------------------------------------------===// +// Analyses +//===----------------------------------------------------------------------===// + +static void ActionDeadStores(AnalysisManager& mgr) { + CheckDeadStores(*mgr.getCFG(), mgr.getContext(), *mgr.getParentMap(), + mgr.getDiagnostic()); +} + +static void ActionUninitVals(AnalysisManager& mgr) { + CheckUninitializedValues(*mgr.getCFG(), mgr.getContext(), + mgr.getDiagnostic()); +} + +//===----------------------------------------------------------------------===// +// AnalysisConsumer creation. +//===----------------------------------------------------------------------===// + +ASTConsumer* clang::CreateAnalysisConsumer(Analyses* Beg, Analyses* End, + Diagnostic &diags, Preprocessor* pp, + PreprocessorFactory* ppf, + const LangOptions& lopts, + const std::string& fname, + const std::string& htmldir, + bool visualize, bool trim, + bool analyzeAll) { + + llvm::OwningPtr<AnalysisConsumer> + C(new AnalysisConsumer(diags, pp, ppf, lopts, fname, htmldir, + visualize, trim, analyzeAll)); + + for ( ; Beg != End ; ++Beg) + switch (*Beg) { + case WarnDeadStores: + C->addCodeAction(&ActionDeadStores); + break; + + case WarnUninitVals: + C->addCodeAction(&ActionUninitVals); + break; + + default: break; + } + + return C.take(); +} + diff --git a/Driver/AnalysisConsumer.h b/Driver/AnalysisConsumer.h new file mode 100644 index 0000000000..3a2f3b9437 --- /dev/null +++ b/Driver/AnalysisConsumer.h @@ -0,0 +1,34 @@ +//===--- AnalysisConsumer.cpp - ASTConsumer for running Analyses ----------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// "Meta" ASTConsumer for running different source analyses. +// +//===----------------------------------------------------------------------===// + +#ifndef DRIVER_ANALYSISCONSUMER_H +#define DRIVER_ANALYSISCONSUMER_H + +namespace clang { + +enum Analyses { + WarnDeadStores, + WarnUninitVals +}; + +ASTConsumer* CreateAnalysisConsumer(Analyses* Beg, Analyses* End, + Diagnostic &diags, Preprocessor* pp, + PreprocessorFactory* ppf, + const LangOptions& lopts, + const std::string& fname, + const std::string& htmldir, + bool visualize, bool trim, + bool analyzeAll); +} // end clang namespace + +#endif diff --git a/Driver/clang.cpp b/Driver/clang.cpp index c1d4707c5d..eddaca3a88 100644 --- a/Driver/clang.cpp +++ b/Driver/clang.cpp @@ -78,16 +78,14 @@ enum ProgActions { AnalysisGRSimpleVals, // Perform graph-reachability constant prop. AnalysisGRSimpleValsView, // Visualize results of path-sens. analysis. CheckerCFRef, // Run the Core Foundation Ref. Count Checker. - WarnDeadStores, // Run DeadStores checker on parsed ASTs. - WarnDeadStoresCheck, // Check diagnostics for "DeadStores". - WarnUninitVals, // Run UnitializedVariables checker. TestSerialization, // Run experimental serialization code. ParsePrintCallbacks, // Parse and print each callback. ParseSyntaxOnly, // Parse and perform semantic analysis. ParseNoop, // Parse with noop callbacks. RunPreprocessorOnly, // Just lex, no output. PrintPreprocessedInput, // -E mode. - DumpTokens // Token dump mode. + DumpTokens, // Token dump mode. + RunAnalysis // Run one or more source code analyses. }; static llvm::cl::opt<ProgActions> @@ -120,10 +118,6 @@ ProgAction(llvm::cl::desc("Choose output type:"), llvm::cl::ZeroOrMore, "Run parser, then build and view CFGs with Graphviz"), clEnumValN(AnalysisLiveVariables, "dump-live-variables", "Print results of live variable analysis"), - clEnumValN(WarnDeadStores, "warn-dead-stores", - "Flag warnings of stores to dead variables"), - clEnumValN(WarnUninitVals, "warn-uninit-values", - "Flag warnings of uses of unitialized variables"), clEnumValN(AnalysisGRSimpleVals, "checker-simple", "Perform path-sensitive constant propagation"), clEnumValN(CheckerCFRef, "checker-cfref", @@ -181,6 +175,15 @@ AnalyzeAll("checker-opt-analyze-headers", llvm::cl::desc("Force the static analyzer to analyze " "functions defined in header files")); +static llvm::cl::list<Analyses> +AnalysisList(llvm::cl::desc("Available Source Code Analyses:"), +llvm::cl::values( +clEnumValN(WarnDeadStores, "warn-dead-stores", + "Flag warnings of stores to dead variables"), +clEnumValN(WarnUninitVals, "warn-uninit-values", + "Flag warnings of uses of unitialized variables"), +clEnumValEnd)); + //===----------------------------------------------------------------------===// // Language Options //===----------------------------------------------------------------------===// @@ -1199,12 +1202,6 @@ static ASTConsumer* CreateASTConsumer(const std::string& InFile, case AnalysisLiveVariables: return CreateLiveVarAnalyzer(AnalyzeSpecificFunction); - case WarnDeadStores: - return CreateDeadStoreChecker(Diag); - - case WarnUninitVals: - return CreateUnitValsChecker(Diag); - case AnalysisGRSimpleVals: return CreateGRSimpleVals(Diag, PP, PPF, AnalyzeSpecificFunction, OutputFile, VisualizeEG, TrimGraph, AnalyzeAll); @@ -1228,6 +1225,15 @@ static ASTConsumer* CreateASTConsumer(const std::string& InFile, case RewriteObjC: return CreateCodeRewriterTest(InFile, OutputFile, Diag, LangOpts); + + case RunAnalysis: + assert (!AnalysisList.empty()); + return CreateAnalysisConsumer(&AnalysisList[0], + &AnalysisList[0]+AnalysisList.size(), + Diag, PP, PPF, LangOpts, + AnalyzeSpecificFunction, + OutputFile, VisualizeEG, TrimGraph, + AnalyzeAll); } } @@ -1485,6 +1491,11 @@ int main(int argc, char **argv) { exit(1); } + // Are we invoking one or more source analyses? + if (!AnalysisList.empty() && ProgAction == ParseSyntaxOnly) + ProgAction = RunAnalysis; + + llvm::OwningPtr<SourceManager> SourceMgr; for (unsigned i = 0, e = InputFilenames.size(); i != e; ++i) { diff --git a/test/Analysis/conditional-op-missing-lhs.c b/test/Analysis/conditional-op-missing-lhs.c index 917212d387..6296291cdf 100644 --- a/test/Analysis/conditional-op-missing-lhs.c +++ b/test/Analysis/conditional-op-missing-lhs.c @@ -4,7 +4,7 @@ void f1() { int i; - int j = i ? : 1; // expected-warning{{use of uninitialized variable}} + int j = i ? : 1; // expected-warning{{use of uninitialized variable}} //expected-warning{{Value stored to 'j' is never read}} } void *f2(int *i) |