diff options
author | Manuel Klimek <klimek@google.com> | 2013-11-14 15:49:44 +0000 |
---|---|---|
committer | Manuel Klimek <klimek@google.com> | 2013-11-14 15:49:44 +0000 |
commit | 814f9bd93afff12f61bf351746c7710b8a98fe72 (patch) | |
tree | 4f8313c6c7590bbfe767b3f02106c4a7203742f9 | |
parent | e8a2c18bc7846728bbaa612e9861c018eb2bf0f1 (diff) | |
download | bcm5719-llvm-814f9bd93afff12f61bf351746c7710b8a98fe72.tar.gz bcm5719-llvm-814f9bd93afff12f61bf351746c7710b8a98fe72.zip |
Make clang's static analyzer checks available through clang-tidy.
This is implemented in a way that the current static analyzer
architecture allows, in the future we might want to revisit this.
With this change static analyzer checks are available from clang-tidy
by specifying -checks=clang-analyzer-<name>.
This change also fixes the use of the compilation database to allow
clang-tidy to be used like any other clang tool.
llvm-svn: 194707
-rw-r--r-- | clang-tools-extra/clang-tidy/CMakeLists.txt | 2 | ||||
-rw-r--r-- | clang-tools-extra/clang-tidy/ClangTidy.cpp | 172 | ||||
-rw-r--r-- | clang-tools-extra/clang-tidy/ClangTidy.h | 64 | ||||
-rw-r--r-- | clang-tools-extra/clang-tidy/ClangTidyDiagnosticConsumer.h | 102 | ||||
-rw-r--r-- | clang-tools-extra/clang-tidy/tool/CMakeLists.txt | 1 | ||||
-rw-r--r-- | clang-tools-extra/clang-tidy/tool/ClangTidyMain.cpp | 14 | ||||
-rw-r--r-- | clang-tools-extra/clang-tidy/tool/Makefile | 2 | ||||
-rw-r--r-- | clang-tools-extra/test/clang-tidy/static-analyzer.cpp | 8 | ||||
-rw-r--r-- | clang-tools-extra/unittests/clang-tidy/ClangTidyTest.h | 1 | ||||
-rw-r--r-- | clang-tools-extra/unittests/clang-tidy/Makefile | 2 |
10 files changed, 258 insertions, 110 deletions
diff --git a/clang-tools-extra/clang-tidy/CMakeLists.txt b/clang-tools-extra/clang-tidy/CMakeLists.txt index b5479728dd3..e8c592bdb4b 100644 --- a/clang-tools-extra/clang-tidy/CMakeLists.txt +++ b/clang-tools-extra/clang-tidy/CMakeLists.txt @@ -14,6 +14,8 @@ target_link_libraries(clangTidy clangTooling clangBasic clangRewriteFrontend + clangStaticAnalyzerFrontend + clangStaticAnalyzerCheckers ) add_subdirectory(tool) diff --git a/clang-tools-extra/clang-tidy/ClangTidy.cpp b/clang-tools-extra/clang-tidy/ClangTidy.cpp index b7d38350b34..457011a9fbc 100644 --- a/clang-tools-extra/clang-tidy/ClangTidy.cpp +++ b/clang-tools-extra/clang-tidy/ClangTidy.cpp @@ -30,6 +30,7 @@ #include "clang/Frontend/TextDiagnosticPrinter.h" #include "clang/Rewrite/Frontend/FixItRewriter.h" #include "clang/Rewrite/Frontend/FrontendActions.h" +#include "clang/StaticAnalyzer/Frontend/FrontendActions.h" #include "clang/Tooling/Tooling.h" #include "clang/Tooling/Refactoring.h" #include "llvm/Support/Path.h" @@ -45,15 +46,83 @@ namespace clang { namespace tidy { namespace { -class ClangTidyPPAction : public PreprocessOnlyAction { +/// \brief A combined ASTConsumer that forwards calls to two different +/// consumers. +/// +/// FIXME: This currently forwards just enough methods for the static analyzer +/// and the \c MatchFinder's consumer to work; expand this to all methods of +/// ASTConsumer and put it into a common location. +class CombiningASTConsumer : public ASTConsumer { +public: + CombiningASTConsumer(ASTConsumer *Consumer1, ASTConsumer *Consumer2) + : Consumer1(Consumer1), Consumer2(Consumer2) {} + + virtual void Initialize(ASTContext &Context) LLVM_OVERRIDE { + Consumer1->Initialize(Context); + Consumer2->Initialize(Context); + } + virtual bool HandleTopLevelDecl(DeclGroupRef D) LLVM_OVERRIDE { + return Consumer1->HandleTopLevelDecl(D) && Consumer2->HandleTopLevelDecl(D); + } + virtual void HandleTopLevelDeclInObjCContainer(DeclGroupRef D) LLVM_OVERRIDE { + Consumer1->HandleTopLevelDeclInObjCContainer(D); + Consumer2->HandleTopLevelDeclInObjCContainer(D); + } + virtual void HandleTranslationUnit(ASTContext &Context) LLVM_OVERRIDE { + Consumer1->HandleTranslationUnit(Context); + Consumer2->HandleTranslationUnit(Context); + } + +private: + llvm::OwningPtr<ASTConsumer> Consumer1; + llvm::OwningPtr<ASTConsumer> Consumer2; +}; + +/// \brief Action that runs clang-tidy and static analyzer checks. +/// +/// FIXME: Note that this inherits from \c AnalysisAction as this is the only +/// way we can currently get to AnalysisAction::CreateASTConsumer. Ideally +/// we'd want to build a more generic way to use \c FrontendAction based +/// checkers in clang-tidy, but that needs some preperation work first. +class ClangTidyAction : public ento::AnalysisAction { public: - ClangTidyPPAction(SmallVectorImpl<ClangTidyCheck *> &Checks, - ClangTidyContext &Context) - : Checks(Checks), Context(Context) {} + ClangTidyAction(StringRef CheckRegexString, + SmallVectorImpl<ClangTidyCheck *> &Checks, + ClangTidyContext &Context, MatchFinder &Finder) + : CheckRegexString(CheckRegexString), Checks(Checks), Context(Context), + Finder(Finder) {} private: + clang::ASTConsumer *CreateASTConsumer(clang::CompilerInstance &Compiler, + StringRef File) LLVM_OVERRIDE { + AnalyzerOptionsRef Options = Compiler.getAnalyzerOpts(); + llvm::Regex CheckRegex(CheckRegexString); + +// Run our regex against all possible static analyzer checkers. +// Note that debug checkers print values / run programs to visualize the CFG +// and are thus not applicable to clang-tidy in general. +#define GET_CHECKERS +#define CHECKER(FULLNAME, CLASS, DESCFILE, HELPTEXT, GROUPINDEX, HIDDEN) \ + if (!StringRef(FULLNAME).startswith("debug") && \ + CheckRegex.match("clang-analyzer-" FULLNAME)) \ + Options->CheckersControlList.push_back(std::make_pair(FULLNAME, true)); +#include "../../../lib/StaticAnalyzer/Checkers/Checkers.inc" +#undef CHECKER +#undef GET_CHECKERS + + Options->AnalysisStoreOpt = RegionStoreModel; + Options->AnalysisDiagOpt = PD_TEXT; + Options->AnalyzeNestedBlocks = true; + Options->eagerlyAssumeBinOpBifurcation = true; + return new CombiningASTConsumer( + Finder.newASTConsumer(), + ento::AnalysisAction::CreateASTConsumer(Compiler, File)); + } + virtual bool BeginSourceFileAction(CompilerInstance &Compiler, - llvm::StringRef file_name) { + llvm::StringRef Filename) LLVM_OVERRIDE { + if (!ento::AnalysisAction::BeginSourceFileAction(Compiler, Filename)) + return false; Context.setSourceManager(&Compiler.getSourceManager()); for (SmallVectorImpl<ClangTidyCheck *>::iterator I = Checks.begin(), E = Checks.end(); @@ -62,35 +131,61 @@ private: return true; } + std::string CheckRegexString; SmallVectorImpl<ClangTidyCheck *> &Checks; ClangTidyContext &Context; + MatchFinder &Finder; }; -class ClangTidyPPActionFactory : public FrontendActionFactory { +class ClangTidyActionFactory : public FrontendActionFactory { public: - ClangTidyPPActionFactory(SmallVectorImpl<ClangTidyCheck *> &Checks, - ClangTidyContext &Context) - : Checks(Checks), Context(Context) {} + ClangTidyActionFactory(StringRef CheckRegexString, ClangTidyContext &Context) + : CheckRegexString(CheckRegexString), Context(Context) { + ClangTidyCheckFactories CheckFactories; + for (ClangTidyModuleRegistry::iterator I = ClangTidyModuleRegistry::begin(), + E = ClangTidyModuleRegistry::end(); + I != E; ++I) { + OwningPtr<ClangTidyModule> Module(I->instantiate()); + Module->addCheckFactories(CheckFactories); + } + + SmallVector<ClangTidyCheck *, 16> Checks; + CheckFactories.createChecks(CheckRegexString, Checks); + + for (SmallVectorImpl<ClangTidyCheck *>::iterator I = Checks.begin(), + E = Checks.end(); + I != E; ++I) { + (*I)->setContext(&Context); + (*I)->registerMatchers(&Finder); + } + } virtual FrontendAction *create() { - return new ClangTidyPPAction(Checks, Context); + return new ClangTidyAction(CheckRegexString, Checks, Context, Finder); } private: - SmallVectorImpl<ClangTidyCheck *> &Checks; + std::string CheckRegexString; + SmallVector<ClangTidyCheck *, 8> Checks; ClangTidyContext &Context; + MatchFinder Finder; }; } // namespace -ClangTidyError::ClangTidyError(const SourceManager &Sources, SourceLocation Loc, - StringRef Message, - const tooling::Replacements &Fix) - : Message(Message), Fix(Fix) { +ClangTidyMessage::ClangTidyMessage(StringRef Message) : Message(Message) {} + +ClangTidyMessage::ClangTidyMessage(StringRef Message, + const SourceManager &Sources, + SourceLocation Loc) + : Message(Message) { FilePath = Sources.getFilename(Loc); FileOffset = Sources.getFileOffset(Loc); } +ClangTidyError::ClangTidyError(const ClangTidyMessage &Message) + : Message(Message) {} + DiagnosticBuilder ClangTidyContext::Diag(SourceLocation Loc, StringRef Message) { return DiagEngine->Report( @@ -115,6 +210,11 @@ void ClangTidyCheck::run(const ast_matchers::MatchFinder::MatchResult &Result) { check(Result); } +FrontendActionFactory *createClangTidyActionFactory(StringRef CheckRegexString, + ClangTidyContext &Context) { + return new ClangTidyActionFactory(CheckRegexString, Context); +} + void runClangTidy(StringRef CheckRegexString, const tooling::CompilationDatabase &Compilations, ArrayRef<std::string> Ranges, @@ -124,27 +224,24 @@ void runClangTidy(StringRef CheckRegexString, ClangTool Tool(Compilations, Ranges); clang::tidy::ClangTidyContext Context(Errors); ClangTidyDiagnosticConsumer DiagConsumer(Context); - ClangTidyCheckFactories CheckFactories; - for (ClangTidyModuleRegistry::iterator I = ClangTidyModuleRegistry::begin(), - E = ClangTidyModuleRegistry::end(); - I != E; ++I) { - OwningPtr<ClangTidyModule> Module(I->instantiate()); - Module->addCheckFactories(CheckFactories); - } - SmallVector<ClangTidyCheck *, 16> Checks; - CheckFactories.createChecks(CheckRegexString, Checks); + Tool.setDiagnosticConsumer(&DiagConsumer); + Tool.run(createClangTidyActionFactory(CheckRegexString, Context)); +} - MatchFinder Finder; - for (SmallVectorImpl<ClangTidyCheck *>::iterator I = Checks.begin(), - E = Checks.end(); - I != E; ++I) { - (*I)->setContext(&Context); - (*I)->registerMatchers(&Finder); +static void reportDiagnostic(const ClangTidyMessage &Message, + SourceManager &SourceMgr, + DiagnosticsEngine::Level Level, + DiagnosticsEngine &Diags) { + SourceLocation Loc; + if (!Message.FilePath.empty()) { + const FileEntry *File = + SourceMgr.getFileManager().getFile(Message.FilePath); + FileID ID = SourceMgr.createFileID(File, SourceLocation(), SrcMgr::C_User); + Loc = SourceMgr.getLocForStartOfFile(ID); + Loc = Loc.getLocWithOffset(Message.FileOffset); } - - Tool.run(new ClangTidyPPActionFactory(Checks, Context)); - Tool.run(newFrontendActionFactory(&Finder)); + Diags.Report(Loc, Diags.getCustomDiagID(Level, Message.Message)); } void handleErrors(SmallVectorImpl<ClangTidyError> &Errors, bool Fix) { @@ -160,11 +257,10 @@ void handleErrors(SmallVectorImpl<ClangTidyError> &Errors, bool Fix) { for (SmallVectorImpl<ClangTidyError>::iterator I = Errors.begin(), E = Errors.end(); I != E; ++I) { - const FileEntry *File = Files.getFile(I->FilePath); - FileID ID = SourceMgr.createFileID(File, SourceLocation(), SrcMgr::C_User); - SourceLocation Loc = SourceMgr.getLocForStartOfFile(ID); - Diags.Report(Loc.getLocWithOffset(I->FileOffset), - Diags.getCustomDiagID(DiagnosticsEngine::Warning, I->Message)); + reportDiagnostic(I->Message, SourceMgr, DiagnosticsEngine::Warning, Diags); + for (unsigned i = 0, e = I->Notes.size(); i != e; ++i) { + reportDiagnostic(I->Notes[i], SourceMgr, DiagnosticsEngine::Note, Diags); + } tooling::applyAllReplacements(I->Fix, Rewrite); } // FIXME: Run clang-format on changes. diff --git a/clang-tools-extra/clang-tidy/ClangTidy.h b/clang-tools-extra/clang-tidy/ClangTidy.h index 3e17c2983ef..1916a52459c 100644 --- a/clang-tools-extra/clang-tidy/ClangTidy.h +++ b/clang-tools-extra/clang-tidy/ClangTidy.h @@ -14,6 +14,7 @@ #include "clang/Basic/Diagnostic.h" #include "clang/Basic/SourceManager.h" #include "clang/Tooling/Refactoring.h" +#include "ClangTidyDiagnosticConsumer.h" namespace clang { @@ -27,64 +28,6 @@ class CompilationDatabase; namespace tidy { -/// \brief A detected error complete with information to display diagnostic and -/// automatic fix. -/// -/// This is used as an intermediate format to transport Diagnostics without a -/// dependency on a SourceManager. -/// -/// FIXME: Make Diagnostics flexible enough to support this directly. -struct ClangTidyError { - ClangTidyError(const SourceManager &Sources, SourceLocation Loc, - StringRef Message, const tooling::Replacements &Fix); - - std::string Message; - unsigned FileOffset; - std::string FilePath; - tooling::Replacements Fix; -}; - -/// \brief Every \c ClangTidyCheck reports errors through a \c DiagnosticEngine -/// provided by this context. -/// -/// A \c ClangTidyCheck always has access to the active context to report -/// warnings like: -/// \code -/// Context->Diag(Loc, "Single-argument constructors must be explicit") -/// << FixItHint::CreateInsertion(Loc, "explicit "); -/// \endcode -class ClangTidyContext { -public: - ClangTidyContext(SmallVectorImpl<ClangTidyError> *Errors) : Errors(Errors) {} - - /// \brief Report any errors detected using this method. - /// - /// This is still under heavy development and will likely change towards using - /// tablegen'd diagnostic IDs. - /// FIXME: Figure out a way to manage ID spaces. - DiagnosticBuilder Diag(SourceLocation Loc, StringRef Message); - - /// \brief Sets the \c DiagnosticsEngine so that Diagnostics can be generated - /// correctly. - /// - /// This is called from the \c ClangTidyCheck base class. - void setDiagnosticsEngine(DiagnosticsEngine *Engine); - - /// \brief Sets the \c SourceManager of the used \c DiagnosticsEngine. - /// - /// This is called from the \c ClangTidyCheck base class. - void setSourceManager(SourceManager *SourceMgr); - -private: - friend class ClangTidyDiagnosticConsumer; // Calls storeError(). - - /// \brief Store a \c ClangTidyError. - void storeError(const ClangTidyError &Error); - - SmallVectorImpl<ClangTidyError> *Errors; - DiagnosticsEngine *DiagEngine; -}; - /// \brief Base class for all clang-tidy checks. /// /// To implement a \c ClangTidyCheck, write a subclass and overwrite some of the @@ -142,6 +85,11 @@ private: virtual void run(const ast_matchers::MatchFinder::MatchResult &Result); }; +/// \brief Returns an action factory that runs the specified clang-tidy checks. +tooling::FrontendActionFactory * +createClangTidyActionFactory(StringRef CheckRegexString, + ClangTidyContext &Context); + /// \brief Run a set of clang-tidy checks on a set of files. void runClangTidy(StringRef CheckRegexString, const tooling::CompilationDatabase &Compilations, diff --git a/clang-tools-extra/clang-tidy/ClangTidyDiagnosticConsumer.h b/clang-tools-extra/clang-tidy/ClangTidyDiagnosticConsumer.h index 97519210615..074f3e19ad1 100644 --- a/clang-tools-extra/clang-tidy/ClangTidyDiagnosticConsumer.h +++ b/clang-tools-extra/clang-tidy/ClangTidyDiagnosticConsumer.h @@ -27,6 +27,74 @@ class CompilationDatabase; namespace tidy { +/// \brief A message from a clang-tidy check. +/// +/// Note that this is independent of a \c SourceManager. +struct ClangTidyMessage { + ClangTidyMessage(StringRef Message = ""); + ClangTidyMessage(StringRef Message, const SourceManager &Sources, + SourceLocation Loc); + std::string Message; + std::string FilePath; + unsigned FileOffset; +}; + +/// \brief A detected error complete with information to display diagnostic and +/// automatic fix. +/// +/// This is used as an intermediate format to transport Diagnostics without a +/// dependency on a SourceManager. +/// +/// FIXME: Make Diagnostics flexible enough to support this directly. +struct ClangTidyError { + ClangTidyError(const ClangTidyMessage &Message); + + ClangTidyMessage Message; + tooling::Replacements Fix; + SmallVector<ClangTidyMessage, 1> Notes; +}; + +/// \brief Every \c ClangTidyCheck reports errors through a \c DiagnosticEngine +/// provided by this context. +/// +/// A \c ClangTidyCheck always has access to the active context to report +/// warnings like: +/// \code +/// Context->Diag(Loc, "Single-argument constructors must be explicit") +/// << FixItHint::CreateInsertion(Loc, "explicit "); +/// \endcode +class ClangTidyContext { +public: + ClangTidyContext(SmallVectorImpl<ClangTidyError> *Errors) : Errors(Errors) {} + + /// \brief Report any errors detected using this method. + /// + /// This is still under heavy development and will likely change towards using + /// tablegen'd diagnostic IDs. + /// FIXME: Figure out a way to manage ID spaces. + DiagnosticBuilder Diag(SourceLocation Loc, StringRef Message); + + /// \brief Sets the \c DiagnosticsEngine so that Diagnostics can be generated + /// correctly. + /// + /// This is called from the \c ClangTidyCheck base class. + void setDiagnosticsEngine(DiagnosticsEngine *Engine); + + /// \brief Sets the \c SourceManager of the used \c DiagnosticsEngine. + /// + /// This is called from the \c ClangTidyCheck base class. + void setSourceManager(SourceManager *SourceMgr); + +private: + friend class ClangTidyDiagnosticConsumer; // Calls storeError(). + + /// \brief Store a \c ClangTidyError. + void storeError(const ClangTidyError &Error); + + SmallVectorImpl<ClangTidyError> *Errors; + DiagnosticsEngine *DiagEngine; +}; + /// \brief A diagnostic consumer that turns each \c Diagnostic into a /// \c SourceManager-independent \c ClangTidyError. // @@ -47,22 +115,46 @@ public: // library. virtual void HandleDiagnostic(DiagnosticsEngine::Level DiagLevel, const Diagnostic &Info) { - tooling::Replacements Replacements; + if (DiagLevel != DiagnosticsEngine::Note) { + Errors.push_back(ClangTidyError(getMessage(Info))); + } else { + Errors.back().Notes.push_back(getMessage(Info)); + } + addFixes(Info, Errors.back()); + } + + virtual void finish() { + for (unsigned i = 0, e = Errors.size(); i != e; ++i) { + Context.storeError(Errors[i]); + } + } + +private: + void addFixes(const Diagnostic &Info, ClangTidyError &Error) { + if (!Info.hasSourceManager()) + return; SourceManager &SourceMgr = Info.getSourceManager(); + tooling::Replacements Replacements; for (unsigned i = 0, e = Info.getNumFixItHints(); i != e; ++i) { - Replacements.insert(tooling::Replacement( + Error.Fix.insert(tooling::Replacement( SourceMgr, Info.getFixItHint(i).RemoveRange.getBegin(), 0, Info.getFixItHint(i).CodeToInsert)); } + } + + ClangTidyMessage getMessage(const Diagnostic &Info) const { SmallString<100> Buf; Info.FormatDiagnostic(Buf); - Context.storeError( - ClangTidyError(SourceMgr, Info.getLocation(), Buf.str(), Replacements)); + if (!Info.hasSourceManager()) { + return ClangTidyMessage(Buf.str()); + } + return ClangTidyMessage(Buf.str(), Info.getSourceManager(), + Info.getLocation()); } -private: ClangTidyContext &Context; OwningPtr<DiagnosticsEngine> Diags; + SmallVector<ClangTidyError, 8> Errors; }; } // end namespace tidy diff --git a/clang-tools-extra/clang-tidy/tool/CMakeLists.txt b/clang-tools-extra/clang-tidy/tool/CMakeLists.txt index b966410477c..8fa39fda7dc 100644 --- a/clang-tools-extra/clang-tidy/tool/CMakeLists.txt +++ b/clang-tools-extra/clang-tidy/tool/CMakeLists.txt @@ -13,6 +13,7 @@ target_link_libraries(clang-tidy clangTidy clangTidyLLVMModule clangTidyGoogleModule + clangStaticAnalyzerCheckers ) install(TARGETS clang-tidy diff --git a/clang-tools-extra/clang-tidy/tool/ClangTidyMain.cpp b/clang-tools-extra/clang-tidy/tool/ClangTidyMain.cpp index 47aefe7be0e..b1c1e4d5b9d 100644 --- a/clang-tools-extra/clang-tidy/tool/ClangTidyMain.cpp +++ b/clang-tools-extra/clang-tidy/tool/ClangTidyMain.cpp @@ -16,6 +16,7 @@ //===----------------------------------------------------------------------===// #include "../ClangTidy.h" +#include "clang/Tooling/CommonOptionsParser.h" #include "llvm/Support/CommandLine.h" #include <vector> @@ -26,8 +27,7 @@ using namespace llvm; cl::OptionCategory ClangTidyCategory("clang-tidy options"); -cl::list<std::string> -Ranges(cl::Positional, cl::desc("<range0> [... <rangeN>]"), cl::OneOrMore); +static cl::extrahelp CommonHelp(CommonOptionsParser::HelpMessage); static cl::opt<std::string> Checks( "checks", @@ -39,15 +39,11 @@ static cl::opt<bool> Fix("fix", cl::desc("Fix detected errors if possible."), // FIXME: Add option to list name/description of all checks. int main(int argc, const char **argv) { - cl::ParseCommandLineOptions(argc, argv, "TBD\n"); - OwningPtr<clang::tooling::CompilationDatabase> Compilations( - FixedCompilationDatabase::loadFromCommandLine(argc, argv)); - if (!Compilations) - return 0; - // FIXME: Load other compilation databases. + CommonOptionsParser OptionsParser(argc, argv); SmallVector<clang::tidy::ClangTidyError, 16> Errors; - clang::tidy::runClangTidy(Checks, *Compilations, Ranges, &Errors); + clang::tidy::runClangTidy(Checks, OptionsParser.getCompilations(), + OptionsParser.getSourcePathList(), &Errors); clang::tidy::handleErrors(Errors, Fix); return 0; diff --git a/clang-tools-extra/clang-tidy/tool/Makefile b/clang-tools-extra/clang-tidy/tool/Makefile index 31b8766c8ea..203fcdbf334 100644 --- a/clang-tools-extra/clang-tidy/tool/Makefile +++ b/clang-tools-extra/clang-tidy/tool/Makefile @@ -17,6 +17,8 @@ TOOL_NO_EXPORTS = 1 include $(CLANG_LEVEL)/../../Makefile.config LINK_COMPONENTS := $(TARGETS_TO_BUILD) asmparser bitreader support mc option USEDLIBS = clangTidy.a clangTidyLLVMModule.a clangTidyGoogleModule.a \ + clangStaticAnalyzerFrontend.a clangStaticAnalyzerCheckers.a \ + clangStaticAnalyzerCore.a \ clangFormat.a clangASTMatchers.a clangTooling.a clangFrontend.a \ clangSerialization.a clangDriver.a clangParse.a clangSema.a \ clangAnalysis.a clangRewriteFrontend.a clangRewriteCore.a \ diff --git a/clang-tools-extra/test/clang-tidy/static-analyzer.cpp b/clang-tools-extra/test/clang-tidy/static-analyzer.cpp new file mode 100644 index 00000000000..8e14773847a --- /dev/null +++ b/clang-tools-extra/test/clang-tidy/static-analyzer.cpp @@ -0,0 +1,8 @@ +// RUN: clang-tidy %s -checks='clang-analyzer-cplusplus' -- | FileCheck %s + +void f() { + int *p = new int(42); + delete p; + delete p; + // CHECK: warning: Attempt to free released memory +} diff --git a/clang-tools-extra/unittests/clang-tidy/ClangTidyTest.h b/clang-tools-extra/unittests/clang-tidy/ClangTidyTest.h index bd230285c43..9cd8bcdd85f 100644 --- a/clang-tools-extra/unittests/clang-tidy/ClangTidyTest.h +++ b/clang-tools-extra/unittests/clang-tidy/ClangTidyTest.h @@ -36,6 +36,7 @@ protected: OwningPtr<tooling::FrontendActionFactory> Factory( tooling::newFrontendActionFactory(&Finder)); EXPECT_TRUE(tooling::runToolOnCode(Factory->create(), Code)); + DiagConsumer.finish(); tooling::Replacements Fixes; for (SmallVector<ClangTidyError, 16>::const_iterator I = Errors.begin(), E = Errors.end(); diff --git a/clang-tools-extra/unittests/clang-tidy/Makefile b/clang-tools-extra/unittests/clang-tidy/Makefile index 59fb2bbe25a..4a913eb1ac7 100644 --- a/clang-tools-extra/unittests/clang-tidy/Makefile +++ b/clang-tools-extra/unittests/clang-tidy/Makefile @@ -14,6 +14,8 @@ TESTNAME = ClangTidy LINK_COMPONENTS := asmparser bitreader support MC MCParser option \ TransformUtils USEDLIBS = clangTidy.a clangTidyLLVMModule.a clangTidyGoogleModule.a \ + clangStaticAnalyzerFrontend.a clangStaticAnalyzerCheckers.a \ + clangStaticAnalyzerCore.a \ clangFormat.a clangTooling.a clangFrontend.a clangSerialization.a \ clangDriver.a clangRewriteFrontend.a clangRewriteCore.a \ clangParse.a clangSema.a clangAnalysis.a clangAST.a \ |