diff options
author | Daniel Jasper <djasper@google.com> | 2013-07-29 08:19:24 +0000 |
---|---|---|
committer | Daniel Jasper <djasper@google.com> | 2013-07-29 08:19:24 +0000 |
commit | d07c840e6a125e954bfe015062c861369aae870e (patch) | |
tree | 23f7638f25cb4755ed1bfc24aeb2c3b2cc418d67 | |
parent | baca389e28985d57f59e42d745696bdf7ed56af2 (diff) | |
download | bcm5719-llvm-d07c840e6a125e954bfe015062c861369aae870e.tar.gz bcm5719-llvm-d07c840e6a125e954bfe015062c861369aae870e.zip |
Initial architecture for clang-tidy.
This is the first version of a possible clang-tidy architecture. The
purpose of clang-tidy is to detect errors in adhering to common coding
patterns, e.g. described in the LLVM Coding Standards.
This is still heavily in flux.
Review: http://llvm-reviews.chandlerc.com/D884
llvm-svn: 187345
32 files changed, 1195 insertions, 3 deletions
diff --git a/clang-tools-extra/CMakeLists.txt b/clang-tools-extra/CMakeLists.txt index 6a4d3793215..fdd2426ffb7 100644 --- a/clang-tools-extra/CMakeLists.txt +++ b/clang-tools-extra/CMakeLists.txt @@ -2,6 +2,7 @@ add_subdirectory(remove-cstr-calls) add_subdirectory(tool-template) add_subdirectory(cpp11-migrate) add_subdirectory(modularize) +add_subdirectory(clang-tidy) # Add the common testsuite after all the tools. add_subdirectory(test) diff --git a/clang-tools-extra/Makefile b/clang-tools-extra/Makefile index 967c7a34055..33897d64a03 100644 --- a/clang-tools-extra/Makefile +++ b/clang-tools-extra/Makefile @@ -12,7 +12,7 @@ CLANG_LEVEL := ../.. include $(CLANG_LEVEL)/../../Makefile.config PARALLEL_DIRS := remove-cstr-calls tool-template modularize -DIRS := cpp11-migrate unittests +DIRS := cpp11-migrate clang-tidy unittests include $(CLANG_LEVEL)/Makefile diff --git a/clang-tools-extra/clang-tidy/CMakeLists.txt b/clang-tools-extra/clang-tidy/CMakeLists.txt new file mode 100644 index 00000000000..5bcf70bbac4 --- /dev/null +++ b/clang-tools-extra/clang-tidy/CMakeLists.txt @@ -0,0 +1,23 @@ +set(LLVM_LINK_COMPONENTS + ${LLVM_TARGETS_TO_BUILD} + asmparser + bitreader + support + mc + ) + +add_clang_library(clangTidy + ClangTidy.cpp + ClangTidyModule.cpp + ) +target_link_libraries(clangTidy + clangTooling + clangBasic + clangRewriteFrontend + clangTidyLLVMModule + clangTidyGoogleModule + ) + +add_subdirectory(tool) +add_subdirectory(llvm) +add_subdirectory(google) diff --git a/clang-tools-extra/clang-tidy/ClangTidy.cpp b/clang-tools-extra/clang-tidy/ClangTidy.cpp new file mode 100644 index 00000000000..f5569bc934c --- /dev/null +++ b/clang-tools-extra/clang-tidy/ClangTidy.cpp @@ -0,0 +1,184 @@ +//===--- tools/extra/clang-tidy/ClangTidy.cpp - Clang tidy tool -----------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// +/// \file This file implements a clang-tidy tool. +/// +/// This tool uses the Clang Tooling infrastructure, see +/// http://clang.llvm.org/docs/HowToSetupToolingForLLVM.html +/// for details on setting it up with LLVM source tree. +/// +//===----------------------------------------------------------------------===// + +#include "ClangTidy.h" +#include "ClangTidyDiagnosticConsumer.h" +#include "ClangTidyModuleRegistry.h" +#include "clang/AST/ASTConsumer.h" +#include "clang/AST/ASTContext.h" +#include "clang/AST/Decl.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" +#include "clang/Lex/PPCallbacks.h" +#include "clang/Lex/Preprocessor.h" +#include "clang/Frontend/ASTConsumers.h" +#include "clang/Frontend/CompilerInstance.h" +#include "clang/Frontend/FrontendActions.h" +#include "clang/Frontend/TextDiagnosticPrinter.h" +#include "clang/Rewrite/Frontend/FixItRewriter.h" +#include "clang/Rewrite/Frontend/FrontendActions.h" +#include "clang/Tooling/Tooling.h" +#include "clang/Tooling/Refactoring.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/Signals.h" +#include <vector> + +using namespace clang::ast_matchers; +using namespace clang::driver; +using namespace clang::tooling; +using namespace llvm; + +namespace clang { +namespace tidy { +namespace { + +class ClangTidyPPAction : public PreprocessOnlyAction { +public: + ClangTidyPPAction(SmallVectorImpl<ClangTidyCheck *> &Checks, + ClangTidyContext &Context) + : Checks(Checks), Context(Context) {} + +private: + virtual bool BeginSourceFileAction(CompilerInstance &Compiler, + llvm::StringRef file_name) { + Context.setSourceManager(&Compiler.getSourceManager()); + for (SmallVectorImpl<ClangTidyCheck *>::iterator I = Checks.begin(), + E = Checks.end(); + I != E; ++I) + (*I)->registerPPCallbacks(Compiler); + return true; + } + + SmallVectorImpl<ClangTidyCheck *> &Checks; + ClangTidyContext &Context; +}; + +class ClangTidyPPActionFactory : public FrontendActionFactory { +public: + ClangTidyPPActionFactory(SmallVectorImpl<ClangTidyCheck *> &Checks, + ClangTidyContext &Context) + : Checks(Checks), Context(Context) {} + + virtual FrontendAction *create() { + return new ClangTidyPPAction(Checks, Context); + } + +private: + SmallVectorImpl<ClangTidyCheck *> &Checks; + ClangTidyContext &Context; +}; + +} // namespace + +ClangTidyError::ClangTidyError(const SourceManager &Sources, SourceLocation Loc, + StringRef Message, + const tooling::Replacements &Fix) + : Message(Message), Fix(Fix) { + FilePath = Sources.getFilename(Loc); + FileOffset = Sources.getFileOffset(Loc); +} + +DiagnosticBuilder ClangTidyContext::Diag(SourceLocation Loc, + StringRef Message) { + return DiagEngine->Report( + Loc, DiagEngine->getCustomDiagID(DiagnosticsEngine::Warning, Message)); +} + +void ClangTidyContext::setDiagnosticsEngine(DiagnosticsEngine *Engine) { + DiagEngine = Engine; +} + +void ClangTidyContext::setSourceManager(SourceManager *SourceMgr) { + DiagEngine->setSourceManager(SourceMgr); +} + +/// \brief Store a \c ClangTidyError. +void ClangTidyContext::storeError(const ClangTidyError &Error) { + Errors->push_back(Error); +} + +void ClangTidyCheck::run(const ast_matchers::MatchFinder::MatchResult &Result) { + Context->setSourceManager(Result.SourceManager); + check(Result); +} + +void runClangTidy(StringRef CheckRegexString, + const tooling::CompilationDatabase &Compilations, + ArrayRef<std::string> Ranges, + SmallVectorImpl<ClangTidyError> *Errors) { + // FIXME: Ranges are currently full files. Support selecting specific + // (line-)ranges. + 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); + + MatchFinder Finder; + for (SmallVectorImpl<ClangTidyCheck *>::iterator I = Checks.begin(), + E = Checks.end(); + I != E; ++I) { + (*I)->setContext(&Context); + (*I)->registerMatchers(&Finder); + } + + Tool.run(new ClangTidyPPActionFactory(Checks, Context)); + Tool.run(newFrontendActionFactory(&Finder)); +} + +void handleErrors(SmallVectorImpl<ClangTidyError> &Errors, bool Fix) { + FileManager Files((FileSystemOptions())); + IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts = new DiagnosticOptions(); + DiagnosticConsumer *DiagPrinter = + new TextDiagnosticPrinter(llvm::outs(), &*DiagOpts); + DiagnosticsEngine Diags(IntrusiveRefCntPtr<DiagnosticIDs>(new DiagnosticIDs), + &*DiagOpts, DiagPrinter); + DiagPrinter->BeginSourceFile(LangOptions()); + SourceManager SourceMgr(Diags, Files); + Rewriter Rewrite(SourceMgr, LangOptions()); + 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)); + tooling::applyAllReplacements(I->Fix, Rewrite); + } + // FIXME: Run clang-format on changes. + if (Fix) + Rewrite.overwriteChangedFiles(); +} + +// This anchor is used to force the linker to link the LLVMModule. +extern volatile int LLVMModuleAnchorSource; +static int LLVMModuleAnchorDestination = LLVMModuleAnchorSource; + +// This anchor is used to force the linker to link the GoogleModule. +extern volatile int GoogleModuleAnchorSource; +static int GoogleModuleAnchorDestination = GoogleModuleAnchorSource; + +} // namespace tidy +} // namespace clang diff --git a/clang-tools-extra/clang-tidy/ClangTidy.h b/clang-tools-extra/clang-tidy/ClangTidy.h new file mode 100644 index 00000000000..3e17c2983ef --- /dev/null +++ b/clang-tools-extra/clang-tidy/ClangTidy.h @@ -0,0 +1,161 @@ +//===--- ClangTidy.h - clang-tidy -------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CLANG_TIDY_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CLANG_TIDY_H + +#include "clang/ASTMatchers/ASTMatchFinder.h" +#include "clang/Basic/Diagnostic.h" +#include "clang/Basic/SourceManager.h" +#include "clang/Tooling/Refactoring.h" + +namespace clang { + +class CompilerInstance; +namespace ast_matchers { +class MatchFinder; +} +namespace tooling { +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 +/// base class's methods. E.g. to implement a check that validates namespace +/// declarations, overwrite \c registerMatchers: +/// +/// \code +/// registerMatchers(ast_matchers::MatchFinder *Finder) { +/// Finder->addMatcher(namespaceDecl().bind("namespace"), this); +/// } +/// \endcode +/// +/// and then overwrite \c check(const MatchResult &Result) to do the actual +/// check for each match. +/// +/// A new \c ClangTidyCheck instance is created per translation unit. +/// +/// FIXME: Figure out whether carrying information from one TU to another is +/// useful/necessary. +class ClangTidyCheck : public ast_matchers::MatchFinder::MatchCallback { +public: + virtual ~ClangTidyCheck() {} + + /// \brief Overwrite this to register \c PPCallbacks with \c Compiler. + /// + /// This should be used for clang-tidy checks that analyze preprocessor- + /// dependent properties, e.g. the order of include directives. + virtual void registerPPCallbacks(CompilerInstance &Compiler) {} + + /// \brief Overwrite this to register ASTMatchers with \p Finder. + /// + /// This should be used by clang-tidy checks that analyze code properties that + /// dependent on AST knowledge. + /// + /// You can register as many matchers as necessary with \p Finder. Usually, + /// "this" will be used as callback, but you can also specify other callback + /// classes. Thereby, different matchers can trigger different callbacks. + /// + /// If you need to merge information between the different matchers, you can + /// store these as members of the derived class. However, note that all + /// matches occur in the order of the AST traversal. + virtual void registerMatchers(ast_matchers::MatchFinder *Finder) {} + + /// \brief \c ClangTidyChecks that register ASTMatchers should do the actual + /// work in here. + virtual void check(const ast_matchers::MatchFinder::MatchResult &Result) {} + + /// \brief The infrastructure sets the context to \p Ctx with this function. + void setContext(ClangTidyContext *Ctx) { Context = Ctx; } + +protected: + ClangTidyContext *Context; + +private: + virtual void run(const ast_matchers::MatchFinder::MatchResult &Result); +}; + +/// \brief Run a set of clang-tidy checks on a set of files. +void runClangTidy(StringRef CheckRegexString, + const tooling::CompilationDatabase &Compilations, + ArrayRef<std::string> Ranges, + SmallVectorImpl<ClangTidyError> *Errors); + +// FIXME: This interface will need to be significantly extended to be useful. +// FIXME: Implement confidence levels for displaying/fixing errors. +// +/// \brief Displays the found \p Errors to the users. If \p Fix is true, \p +/// Errors containing fixes are automatically applied. +void handleErrors(SmallVectorImpl<ClangTidyError> &Errors, bool Fix); + +} // end namespace tidy +} // end namespace clang + +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CLANG_TIDY_H diff --git a/clang-tools-extra/clang-tidy/ClangTidyDiagnosticConsumer.h b/clang-tools-extra/clang-tidy/ClangTidyDiagnosticConsumer.h new file mode 100644 index 00000000000..97519210615 --- /dev/null +++ b/clang-tools-extra/clang-tidy/ClangTidyDiagnosticConsumer.h @@ -0,0 +1,71 @@ +//===--- ClangTidyDiagnosticConsumer.h - clang-tidy -------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CLANG_TIDY_DIAGNOSTIC_CONSUMER_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CLANG_TIDY_DIAGNOSTIC_CONSUMER_H + +#include "clang/Basic/Diagnostic.h" +#include "clang/Basic/SourceManager.h" +#include "clang/Tooling/Refactoring.h" +#include "llvm/ADT/SmallString.h" + +namespace clang { + +class CompilerInstance; +namespace ast_matchers { +class MatchFinder; +} +namespace tooling { +class CompilationDatabase; +} + +namespace tidy { + +/// \brief A diagnostic consumer that turns each \c Diagnostic into a +/// \c SourceManager-independent \c ClangTidyError. +// +// FIXME: If we move away from unit-tests, this can be moved to a private +// implementation file. +class ClangTidyDiagnosticConsumer : public DiagnosticConsumer { +public: + ClangTidyDiagnosticConsumer(ClangTidyContext &Ctx) : Context(Ctx) { + IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts = new DiagnosticOptions(); + Diags.reset(new DiagnosticsEngine( + IntrusiveRefCntPtr<DiagnosticIDs>(new DiagnosticIDs), &*DiagOpts, this, + /*ShouldOwnClient=*/false)); + Context.setDiagnosticsEngine(Diags.get()); + } + + // FIXME: The concept of converting between FixItHints and Replacements is + // more generic and should be pulled out into a more useful Diagnostics + // library. + virtual void HandleDiagnostic(DiagnosticsEngine::Level DiagLevel, + const Diagnostic &Info) { + tooling::Replacements Replacements; + SourceManager &SourceMgr = Info.getSourceManager(); + for (unsigned i = 0, e = Info.getNumFixItHints(); i != e; ++i) { + Replacements.insert(tooling::Replacement( + SourceMgr, Info.getFixItHint(i).RemoveRange.getBegin(), 0, + Info.getFixItHint(i).CodeToInsert)); + } + SmallString<100> Buf; + Info.FormatDiagnostic(Buf); + Context.storeError( + ClangTidyError(SourceMgr, Info.getLocation(), Buf.str(), Replacements)); + } + +private: + ClangTidyContext &Context; + OwningPtr<DiagnosticsEngine> Diags; +}; + +} // end namespace tidy +} // end namespace clang + +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CLANG_TIDY_DIAGNOSTIC_CONSUMER_H diff --git a/clang-tools-extra/clang-tidy/ClangTidyModule.cpp b/clang-tools-extra/clang-tidy/ClangTidyModule.cpp new file mode 100644 index 00000000000..a81213cee4e --- /dev/null +++ b/clang-tools-extra/clang-tidy/ClangTidyModule.cpp @@ -0,0 +1,49 @@ +//===--- tools/extra/clang-tidy/ClangTidyModule.cpp - Clang tidy tool -----===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// +/// \file Implements classes required to build clang-tidy modules. +/// +//===----------------------------------------------------------------------===// + +#include "ClangTidyModule.h" +#include "llvm/Support/Regex.h" + +namespace clang { +namespace tidy { + +CheckFactoryBase::~CheckFactoryBase() {} + +ClangTidyCheckFactories::~ClangTidyCheckFactories() { + for (std::map<std::string, CheckFactoryBase *>::iterator + I = Factories.begin(), + E = Factories.end(); + I != E; ++I) { + delete I->second; + } +} +void ClangTidyCheckFactories::addCheckFactory(StringRef Name, + CheckFactoryBase *Factory) { + + Factories[Name] = Factory; +} + +void ClangTidyCheckFactories::createChecks( + StringRef CheckRegexString, SmallVectorImpl<ClangTidyCheck *> &Checks) { + llvm::Regex CheckRegex(CheckRegexString); + for (std::map<std::string, CheckFactoryBase *>::iterator + I = Factories.begin(), + E = Factories.end(); + I != E; ++I) { + if (CheckRegex.match(I->first)) + Checks.push_back(I->second->createCheck()); + } +} + +} // namespace tidy +} // namespace clang diff --git a/clang-tools-extra/clang-tidy/ClangTidyModule.h b/clang-tools-extra/clang-tidy/ClangTidyModule.h new file mode 100644 index 00000000000..7008863b31a --- /dev/null +++ b/clang-tools-extra/clang-tidy/ClangTidyModule.h @@ -0,0 +1,98 @@ +//===--- ClangTidyModule.h - clang-tidy -------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CLANG_TIDY_MODULE_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CLANG_TIDY_MODULE_H + +#include "ClangTidy.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/StringRef.h" +#include <utility> + +namespace clang { +namespace tidy { + +/// \brief A factory, that can instantiate a specific clang-tidy check for +/// processing a translation unit. +/// +/// In order to register your check with the \c ClangTidyModule, create a +/// subclass of \c CheckFactoryBase and implement \c createCheck(). Then, use +/// this subclass in \c ClangTidyModule::addCheckFactories(). +class CheckFactoryBase { +public: + virtual ~CheckFactoryBase(); + virtual ClangTidyCheck *createCheck() = 0; +}; + +/// \brief A subclass of \c CheckFactoryBase that should be used for all +/// \c ClangTidyChecks that don't require constructor parameters. +/// +/// For example, if have a clang-tidy check like: +/// \code +/// class MyTidyCheck : public ClangTidyCheck { +/// virtual void registerMatchers(ast_matchers::MatchFinder *Finder) { .. } +/// }; +/// \endcode +/// you can register it with: +/// \code +/// class MyModule : public ClangTidyModule { +/// virtual void addCheckFactories(ClangTidyCheckFactories &CheckFactories) { +/// CheckFactories.addCheckFactory( +/// "myproject-my-check", new ClangTidyCheckFactory<MyTidyCheck>()); +/// } +/// }; +/// \endcode +template <typename T> class ClangTidyCheckFactory : public CheckFactoryBase { +public: + virtual ClangTidyCheck *createCheck() { return new T; } +}; + +class ClangTidyCheckFactories; + +/// \brief A clang-tidy module groups a number of \c ClangTidyChecks and gives +/// them a prefixed name. +class ClangTidyModule { +public: + virtual ~ClangTidyModule() {} + + /// \brief Implement this function in order to register all \c CheckFactories + /// belonging to this module. + virtual void addCheckFactories(ClangTidyCheckFactories &CheckFactories) = 0; +}; + +/// \brief A collection of \c ClangTidyCheckFactory instances. +/// +/// All clang-tidy modules register their check factories with an instance of +/// this object. +class ClangTidyCheckFactories { +public: + ClangTidyCheckFactories() {} + ~ClangTidyCheckFactories(); + + /// \brief Register \p Factory with the name \p Name. + /// + /// The \c ClangTidyCheckFactories object takes ownership of the \p Factory. + void addCheckFactory(StringRef Name, CheckFactoryBase *Factory); + + /// \brief Create instances of all checks matching \p CheckRegexString and + /// store them in \p Checks. + /// + /// The caller takes ownership of the return \c ClangTidyChecks. + void createChecks(StringRef CheckRegexString, + SmallVectorImpl<ClangTidyCheck *> &Checks); + +private: + StringRef FilterRegex; + std::map<std::string, CheckFactoryBase *> Factories; +}; + +} // end namespace tidy +} // end namespace clang + +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CLANG_TIDY_MODULE_H diff --git a/clang-tools-extra/clang-tidy/ClangTidyModuleRegistry.h b/clang-tools-extra/clang-tidy/ClangTidyModuleRegistry.h new file mode 100644 index 00000000000..d23809ffdd5 --- /dev/null +++ b/clang-tools-extra/clang-tidy/ClangTidyModuleRegistry.h @@ -0,0 +1,24 @@ +//===--- ClangTidyModuleRegistry.h - clang-tidy -----------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CLANG_TIDY_MODULE_REGISTRY_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CLANG_TIDY_MODULE_REGISTRY_H + +#include "ClangTidyModule.h" +#include "llvm/Support/Registry.h" + +namespace clang { +namespace tidy { + +typedef llvm::Registry<ClangTidyModule> ClangTidyModuleRegistry; + +} // end namespace tidy +} // end namespace clang + +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CLANG_TIDY_MODULE_REGISTRY_H diff --git a/clang-tools-extra/clang-tidy/Makefile b/clang-tools-extra/clang-tidy/Makefile new file mode 100644 index 00000000000..67d32b0f0e6 --- /dev/null +++ b/clang-tools-extra/clang-tidy/Makefile @@ -0,0 +1,16 @@ +##===- tools/extra/clang-tidy/Makefile ----sssss------------*- Makefile -*-===## +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +##===----------------------------------------------------------------------===## + +CLANG_LEVEL := ../../.. +LIBRARYNAME := clangTidy +include $(CLANG_LEVEL)/../../Makefile.config + +DIRS = llvm google tool + +include $(CLANG_LEVEL)/Makefile diff --git a/clang-tools-extra/clang-tidy/google/CMakeLists.txt b/clang-tools-extra/clang-tidy/google/CMakeLists.txt new file mode 100644 index 00000000000..a6bba75b7f0 --- /dev/null +++ b/clang-tools-extra/clang-tidy/google/CMakeLists.txt @@ -0,0 +1,10 @@ +set(LLVM_LINK_COMPONENTS support) + +add_clang_library(clangTidyGoogleModule + GoogleTidyModule.cpp + ) +target_link_libraries(clangTidyGoogleModule + clangTooling + clangBasic + clangASTMatchers + ) diff --git a/clang-tools-extra/clang-tidy/google/GoogleTidyModule.cpp b/clang-tools-extra/clang-tidy/google/GoogleTidyModule.cpp new file mode 100644 index 00000000000..0839a06df55 --- /dev/null +++ b/clang-tools-extra/clang-tidy/google/GoogleTidyModule.cpp @@ -0,0 +1,61 @@ +//===--- GoogleTidyModule.cpp - clang-tidy --------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "GoogleTidyModule.h" +#include "../ClangTidy.h" +#include "../ClangTidyModule.h" +#include "../ClangTidyModuleRegistry.h" +#include "clang/AST/ASTContext.h" +#include "clang/ASTMatchers/ASTMatchers.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" +#include "clang/Frontend/CompilerInstance.h" +#include "clang/Lex/Preprocessor.h" +#include "clang/Lex/PPCallbacks.h" +#include "llvm/Support/raw_ostream.h" + +using namespace clang::ast_matchers; + +namespace clang { +namespace tidy { + +void +ExplicitConstructorCheck::registerMatchers(ast_matchers::MatchFinder *Finder) { + Finder->addMatcher(constructorDecl().bind("construct"), this); +} + +void ExplicitConstructorCheck::check(const MatchFinder::MatchResult &Result) { + const CXXConstructorDecl *Ctor = + Result.Nodes.getNodeAs<CXXConstructorDecl>("construct"); + if (!Ctor->isExplicit() && !Ctor->isImplicit() && Ctor->getNumParams() >= 1 && + Ctor->getMinRequiredArguments() <= 1) { + SourceLocation Loc = Ctor->getLocation(); + Context->Diag(Loc, "Single-argument constructors must be explicit") + << FixItHint::CreateInsertion(Loc, "explicit "); + } +} + +class GoogleModule : public ClangTidyModule { +public: + virtual void addCheckFactories(ClangTidyCheckFactories &CheckFactories) { + CheckFactories.addCheckFactory( + "google-explicit-constructor", + new ClangTidyCheckFactory<ExplicitConstructorCheck>()); + } +}; + +// Register the GoogleTidyModule using this statically initialized variable. +static ClangTidyModuleRegistry::Add<GoogleModule> X("google-module", + "Adds Google lint checks."); + +// This anchor is used to force the linker to link in the generated object file +// and thus register the GoogleModule. +volatile int GoogleModuleAnchorSource = 0; + +} // namespace tidy +} // namespace clang diff --git a/clang-tools-extra/clang-tidy/google/GoogleTidyModule.h b/clang-tools-extra/clang-tidy/google/GoogleTidyModule.h new file mode 100644 index 00000000000..aed9b5f7ef0 --- /dev/null +++ b/clang-tools-extra/clang-tidy/google/GoogleTidyModule.h @@ -0,0 +1,31 @@ +//===--- GoogleTidyModule.h - clang-tidy ------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_GOOGLE_GOOGLE_TIDY_MODULE_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_GOOGLE_GOOGLE_TIDY_MODULE_H + +#include "../ClangTidy.h" + +namespace clang { +namespace tidy { + +/// \brief Checks that all single-argument constructors are explicit. +/// +/// see: +/// http://google-styleguide.googlecode.com/svn/trunk/cppguide.xml#Explicit_Constructors +class ExplicitConstructorCheck : public ClangTidyCheck { +public: + virtual void registerMatchers(ast_matchers::MatchFinder *Finder); + virtual void check(const ast_matchers::MatchFinder::MatchResult &Result); +}; + +} // namespace tidy +} // namespace clang + +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_GOOGLE_GOOGLE_TIDY_MODULE_H diff --git a/clang-tools-extra/clang-tidy/google/Makefile b/clang-tools-extra/clang-tidy/google/Makefile new file mode 100644 index 00000000000..4a39fddf51c --- /dev/null +++ b/clang-tools-extra/clang-tidy/google/Makefile @@ -0,0 +1,12 @@ +##===- clang-tidy/google/Makefile --------------------------*- Makefile -*-===## +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +##===----------------------------------------------------------------------===## +CLANG_LEVEL := ../../../.. +LIBRARYNAME := clangTidyGoogleModule + +include $(CLANG_LEVEL)/Makefile diff --git a/clang-tools-extra/clang-tidy/llvm/CMakeLists.txt b/clang-tools-extra/clang-tidy/llvm/CMakeLists.txt new file mode 100644 index 00000000000..58ceb06f2d5 --- /dev/null +++ b/clang-tools-extra/clang-tidy/llvm/CMakeLists.txt @@ -0,0 +1,10 @@ +set(LLVM_LINK_COMPONENTS support) + +add_clang_library(clangTidyLLVMModule + LLVMTidyModule.cpp + ) +target_link_libraries(clangTidyLLVMModule + clangTooling + clangBasic + clangASTMatchers + ) diff --git a/clang-tools-extra/clang-tidy/llvm/LLVMTidyModule.cpp b/clang-tools-extra/clang-tidy/llvm/LLVMTidyModule.cpp new file mode 100644 index 00000000000..cd33a51705e --- /dev/null +++ b/clang-tools-extra/clang-tidy/llvm/LLVMTidyModule.cpp @@ -0,0 +1,104 @@ +//===--- LLVMTidyModule.cpp - clang-tidy ----------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "LLVMTidyModule.h" +#include "../ClangTidy.h" +#include "../ClangTidyModule.h" +#include "../ClangTidyModuleRegistry.h" +#include "clang/AST/ASTContext.h" +#include "clang/ASTMatchers/ASTMatchers.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" +#include "clang/Frontend/CompilerInstance.h" +#include "clang/Lex/Preprocessor.h" +#include "clang/Lex/PPCallbacks.h" +#include "llvm/Support/raw_ostream.h" + +using namespace clang::ast_matchers; + +namespace clang { +namespace tidy { + +void +NamespaceCommentCheck::registerMatchers(ast_matchers::MatchFinder *Finder) { + Finder->addMatcher(namespaceDecl().bind("namespace"), this); +} + +void NamespaceCommentCheck::check(const MatchFinder::MatchResult &Result) { + const NamespaceDecl *ND = Result.Nodes.getNodeAs<NamespaceDecl>("namespace"); + Token Tok; + SourceLocation Loc = ND->getRBraceLoc().getLocWithOffset(1); + while (Lexer::getRawToken(Loc, Tok, *Result.SourceManager, + Result.Context->getLangOpts())) { + Loc = Loc.getLocWithOffset(1); + } + // FIXME: Check that this namespace is "long". + if (Tok.is(tok::comment)) { + // FIXME: Check comment content. + return; + } + std::string Fix = " // namespace"; + if (!ND->isAnonymousNamespace()) + Fix = Fix.append(" ").append(ND->getNameAsString()); + + Context->Diag(ND->getLocation(), + "namespace not terminated with a closing comment") + << FixItHint::CreateInsertion(ND->getRBraceLoc().getLocWithOffset(1), + Fix); +} + +namespace { +class IncludeOrderPPCallbacks : public PPCallbacks { +public: + explicit IncludeOrderPPCallbacks(ClangTidyContext &Context) + : Context(Context) {} + + virtual void InclusionDirective(SourceLocation HashLoc, + const Token &IncludeTok, StringRef FileName, + bool IsAngled, CharSourceRange FilenameRange, + const FileEntry *File, StringRef SearchPath, + StringRef RelativePath, + const Module *Imported) { + // FIXME: This is a dummy implementation to show how to get at preprocessor + // information. Implement a real include order check. + Context.Diag(HashLoc, "This is an include"); + } + +private: + ClangTidyContext &Context; +}; +} // namespace + +void IncludeOrderCheck::registerPPCallbacks(CompilerInstance &Compiler) { + Compiler.getPreprocessor() + .addPPCallbacks(new IncludeOrderPPCallbacks(*Context)); +} + +class LLVMModule : public ClangTidyModule { +public: + virtual ~LLVMModule() {} + + virtual void addCheckFactories(ClangTidyCheckFactories &CheckFactories) { + CheckFactories.addCheckFactory( + "llvm-include-order", new ClangTidyCheckFactory<IncludeOrderCheck>()); + CheckFactories.addCheckFactory( + "llvm-namespace-comment", + new ClangTidyCheckFactory<NamespaceCommentCheck>()); + } +}; + +// Register the LLVMTidyModule using this statically initialized variable. +static ClangTidyModuleRegistry::Add<LLVMModule> X("llvm-module", + "Adds LLVM lint checks."); + +// This anchor is used to force the linker to link in the generated object file +// and thus register the LLVMModule. +volatile int LLVMModuleAnchorSource = 0; + +} // namespace tidy +} // namespace clang diff --git a/clang-tools-extra/clang-tidy/llvm/LLVMTidyModule.h b/clang-tools-extra/clang-tidy/llvm/LLVMTidyModule.h new file mode 100644 index 00000000000..691ff485503 --- /dev/null +++ b/clang-tools-extra/clang-tidy/llvm/LLVMTidyModule.h @@ -0,0 +1,38 @@ +//===--- LLVMTidyModule.h - clang-tidy --------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_LLVM_LLVM_TIDY_MODULE_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_LLVM_LLVM_TIDY_MODULE_H + +#include "../ClangTidy.h" + +namespace clang { +namespace tidy { + +/// \brief Checks the correct order of \c #includes. +/// +/// see: http://llvm.org/docs/CodingStandards.html#include-style +class IncludeOrderCheck : public ClangTidyCheck { +public: + virtual void registerPPCallbacks(CompilerInstance &Compiler); +}; + +/// \brief Checks that long namespaces have a closing comment. +/// +/// see: http://llvm.org/docs/CodingStandards.html#namespace-indentation +class NamespaceCommentCheck : public ClangTidyCheck { +public: + virtual void registerMatchers(ast_matchers::MatchFinder *Finder); + virtual void check(const ast_matchers::MatchFinder::MatchResult &Result); +}; + +} // namespace tidy +} // namespace clang + +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_LLVM_TIDY_LLVM_MODULE_H diff --git a/clang-tools-extra/clang-tidy/llvm/Makefile b/clang-tools-extra/clang-tidy/llvm/Makefile new file mode 100644 index 00000000000..15ece2e2ca3 --- /dev/null +++ b/clang-tools-extra/clang-tidy/llvm/Makefile @@ -0,0 +1,12 @@ +##===- clang-tidy/llvm/Makefile ----------------------------*- Makefile -*-===## +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +##===----------------------------------------------------------------------===## +CLANG_LEVEL := ../../../.. +LIBRARYNAME := clangTidyLLVMModule + +include $(CLANG_LEVEL)/Makefile diff --git a/clang-tools-extra/clang-tidy/tool/CMakeLists.txt b/clang-tools-extra/clang-tidy/tool/CMakeLists.txt new file mode 100644 index 00000000000..27d511fdb23 --- /dev/null +++ b/clang-tools-extra/clang-tidy/tool/CMakeLists.txt @@ -0,0 +1,18 @@ +set(LLVM_LINK_COMPONENTS + ${LLVM_TARGETS_TO_BUILD} + asmparser + bitreader + support + mc + ) + +add_clang_executable(clang-tidy + ClangTidyMain.cpp + ) +target_link_libraries(clang-tidy + clangTidy + ) + +install(TARGETS clang-tidy + RUNTIME DESTINATION bin) + diff --git a/clang-tools-extra/clang-tidy/tool/ClangTidyMain.cpp b/clang-tools-extra/clang-tidy/tool/ClangTidyMain.cpp new file mode 100644 index 00000000000..aed545697fb --- /dev/null +++ b/clang-tools-extra/clang-tidy/tool/ClangTidyMain.cpp @@ -0,0 +1,56 @@ +//===--- tools/extra/clang-tidy/ClangTidyMain.cpp - Clang tidy tool -------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// +/// \file This file implements a clang-tidy tool. +/// +/// This tool uses the Clang Tooling infrastructure, see +/// http://clang.llvm.org/docs/HowToSetupToolingForLLVM.html +/// for details on setting it up with LLVM source tree. +/// +//===----------------------------------------------------------------------===// + +#include "../ClangTidy.h" +#include "clang/Driver/OptTable.h" +#include "clang/Driver/Options.h" +#include "llvm/Support/CommandLine.h" +#include <vector> + +using namespace clang::ast_matchers; +using namespace clang::driver; +using namespace clang::tooling; +using namespace llvm; + +cl::OptionCategory ClangTidyCategory("clang-tidy options"); + +cl::list<std::string> +Ranges(cl::Positional, cl::desc("<range0> [... <rangeN>]"), cl::OneOrMore); + +static cl::opt<std::string> Checks( + "checks", + cl::desc("Regular expression matching the names of the checks to be run."), + cl::init(".*"), cl::cat(ClangTidyCategory)); +static cl::opt<bool> Fix("fix", cl::desc("Fix detected errors if possible."), + cl::init(false), cl::cat(ClangTidyCategory)); + +// 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. + + SmallVector<clang::tidy::ClangTidyError, 16> Errors; + clang::tidy::runClangTidy(Checks, *Compilations, Ranges, &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 new file mode 100644 index 00000000000..31b8766c8ea --- /dev/null +++ b/clang-tools-extra/clang-tidy/tool/Makefile @@ -0,0 +1,25 @@ +##===- clang-tidy/tool/Makefile ----------------------------*- Makefile -*-===## +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +##===----------------------------------------------------------------------===## + +CLANG_LEVEL := ../../../.. + +TOOLNAME = clang-tidy + +# No plugins, optimize startup time. +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 \ + clangFormat.a clangASTMatchers.a clangTooling.a clangFrontend.a \ + clangSerialization.a clangDriver.a clangParse.a clangSema.a \ + clangAnalysis.a clangRewriteFrontend.a clangRewriteCore.a \ + clangEdit.a clangAST.a clangLex.a clangBasic.a + +include $(CLANG_LEVEL)/Makefile diff --git a/clang-tools-extra/test/CMakeLists.txt b/clang-tools-extra/test/CMakeLists.txt index 3baa7d18a97..4aa6105b933 100644 --- a/clang-tools-extra/test/CMakeLists.txt +++ b/clang-tools-extra/test/CMakeLists.txt @@ -27,7 +27,7 @@ set(CLANG_TOOLS_TEST_DEPS clang clang-headers FileCheck count not # Individual tools we test. - remove-cstr-calls cpp11-migrate modularize + remove-cstr-calls cpp11-migrate modularize clang-tidy # Unit tests ExtraToolsUnitTests diff --git a/clang-tools-extra/test/clang-tidy/basic.cpp b/clang-tools-extra/test/clang-tidy/basic.cpp new file mode 100644 index 00000000000..e61b8ebfb13 --- /dev/null +++ b/clang-tools-extra/test/clang-tidy/basic.cpp @@ -0,0 +1,7 @@ +// RUN: grep -Ev "// *[A-Z-]+:" %s > %t.cpp +// RUN: clang-tidy %t.cpp -- > %t2.cpp +// RUN: FileCheck -input-file=%t2.cpp %s + +namespace i { +} +// CHECK: warning: namespace not terminated with a closing comment diff --git a/clang-tools-extra/test/clang-tidy/fix.cpp b/clang-tools-extra/test/clang-tidy/fix.cpp new file mode 100644 index 00000000000..3af299db8fa --- /dev/null +++ b/clang-tools-extra/test/clang-tidy/fix.cpp @@ -0,0 +1,10 @@ +// RUN: grep -Ev "// *[A-Z-]+:" %s > %t.cpp +// RUN: clang-tidy %t.cpp -fix -- +// RUN: FileCheck -input-file=%t.cpp %s + +namespace i { +} +// CHECK: } // namespace i + +class A { A(int i); }; +// CHECK: class A { explicit A(int i); }; diff --git a/clang-tools-extra/test/clang-tidy/select-checks.cpp b/clang-tools-extra/test/clang-tidy/select-checks.cpp new file mode 100644 index 00000000000..a30717a9922 --- /dev/null +++ b/clang-tools-extra/test/clang-tidy/select-checks.cpp @@ -0,0 +1,10 @@ +// RUN: grep -Ev "// *[A-Z-]+:" %s > %t.cpp +// RUN: clang-tidy %t.cpp -fix -checks=llvm.* -- +// RUN: FileCheck -input-file=%t.cpp %s + +namespace i { +} +// CHECK: } // namespace i + +class A { A(int i); }; // Not fixing this, because the check is in google-. +// CHECK: class A { A(int i); }; diff --git a/clang-tools-extra/unittests/CMakeLists.txt b/clang-tools-extra/unittests/CMakeLists.txt index 444f9496750..8ab39b85288 100644 --- a/clang-tools-extra/unittests/CMakeLists.txt +++ b/clang-tools-extra/unittests/CMakeLists.txt @@ -6,3 +6,4 @@ function(add_extra_unittest test_dirname) endfunction() add_subdirectory(cpp11-migrate) +add_subdirectory(clang-tidy) diff --git a/clang-tools-extra/unittests/Makefile b/clang-tools-extra/unittests/Makefile index 39e155afa8e..5b17ea1e0a3 100644 --- a/clang-tools-extra/unittests/Makefile +++ b/clang-tools-extra/unittests/Makefile @@ -10,6 +10,6 @@ CLANG_LEVEL := ../../.. include $(CLANG_LEVEL)/../../Makefile.config -PARALLEL_DIRS := cpp11-migrate +PARALLEL_DIRS := cpp11-migrate clang-tidy include $(CLANG_LEVEL)/Makefile diff --git a/clang-tools-extra/unittests/clang-tidy/CMakeLists.txt b/clang-tools-extra/unittests/clang-tidy/CMakeLists.txt new file mode 100644 index 00000000000..d0500428a20 --- /dev/null +++ b/clang-tools-extra/unittests/clang-tidy/CMakeLists.txt @@ -0,0 +1,20 @@ +set(LLVM_LINK_COMPONENTS + support + ) + +get_filename_component(CLANG_LINT_SOURCE_DIR + ${CMAKE_CURRENT_SOURCE_DIR}/../../clang-tidy REALPATH) +include_directories(${CLANG_LINT_SOURCE_DIR}) + +add_extra_unittest(ClangTidyTests + LLVMModuleTest.cpp + GoogleModuleTest.cpp) + +target_link_libraries(ClangTidyTests + gtest + gtest_main + clangTidy + clangTooling + clangBasic + clangASTMatchers + ) diff --git a/clang-tools-extra/unittests/clang-tidy/ClangTidyTest.h b/clang-tools-extra/unittests/clang-tidy/ClangTidyTest.h new file mode 100644 index 00000000000..bd230285c43 --- /dev/null +++ b/clang-tools-extra/unittests/clang-tidy/ClangTidyTest.h @@ -0,0 +1,75 @@ +//===--- ClangTidyTest.h - clang-tidy ---------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLS_EXTRA_UNITTESTS_CLANG_TIDY_CLANG_TIDY_TEST_H +#define LLVM_CLANG_TOOLS_EXTRA_UNITTESTS_CLANG_TIDY_CLANG_TIDY_TEST_H + +#include "ClangTidy.h" +#include "ClangTidyDiagnosticConsumer.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" +#include "clang/Frontend/CompilerInstance.h" +#include "clang/Frontend/FrontendActions.h" +#include "clang/Tooling/Refactoring.h" +#include "clang/Tooling/Tooling.h" +#include "gtest/gtest.h" + +namespace clang { +namespace tidy { + +template <typename T> class ClangTidyTest : public ::testing::Test { +protected: + ClangTidyTest() : Check(new T), Context(&Errors) {} + + std::string runCheckOn(StringRef Code) { + ClangTidyDiagnosticConsumer DiagConsumer(Context); + Check->setContext(&Context); + EXPECT_TRUE( + tooling::runToolOnCode(new TestPPAction(*Check, &Context), Code)); + ast_matchers::MatchFinder Finder; + Check->registerMatchers(&Finder); + OwningPtr<tooling::FrontendActionFactory> Factory( + tooling::newFrontendActionFactory(&Finder)); + EXPECT_TRUE(tooling::runToolOnCode(Factory->create(), Code)); + tooling::Replacements Fixes; + for (SmallVector<ClangTidyError, 16>::const_iterator I = Errors.begin(), + E = Errors.end(); + I != E; ++I) + Fixes.insert(I->Fix.begin(), I->Fix.end()); + return tooling::applyAllReplacements(Code, Fixes); + } + + void expectNoChanges(StringRef Code) { EXPECT_EQ(Code, runCheckOn(Code)); } + +private: + class TestPPAction : public PreprocessOnlyAction { + public: + TestPPAction(ClangTidyCheck &Check, ClangTidyContext *Context) + : Check(Check), Context(Context) {} + + private: + virtual bool BeginSourceFileAction(CompilerInstance &Compiler, + llvm::StringRef file_name) { + Context->setSourceManager(&Compiler.getSourceManager()); + Check.registerPPCallbacks(Compiler); + return true; + } + + ClangTidyCheck &Check; + ClangTidyContext *Context; + }; + + OwningPtr<ClangTidyCheck> Check; + SmallVector<ClangTidyError, 16> Errors; + ClangTidyContext Context; +}; + +} // namespace tidy +} // namespace clang + +#endif // LLVM_CLANG_TOOLS_EXTRA_UNITTESTS_CLANG_TIDY_CLANG_TIDY_TEST_H diff --git a/clang-tools-extra/unittests/clang-tidy/GoogleModuleTest.cpp b/clang-tools-extra/unittests/clang-tidy/GoogleModuleTest.cpp new file mode 100644 index 00000000000..26b85b929d6 --- /dev/null +++ b/clang-tools-extra/unittests/clang-tidy/GoogleModuleTest.cpp @@ -0,0 +1,26 @@ +#include "ClangTidyTest.h" + +#include "google/GoogleTidyModule.h" + +namespace clang { +namespace tidy { + +typedef ClangTidyTest<ExplicitConstructorCheck> ExplicitConstructorCheckTest; + +TEST_F(ExplicitConstructorCheckTest, SingleArgumentConstructorsOnly) { + expectNoChanges("class C { C(); };"); + expectNoChanges("class C { C(int i, int j); };"); +} + +TEST_F(ExplicitConstructorCheckTest, Basic) { + EXPECT_EQ("class C { explicit C(int i); };", + runCheckOn("class C { C(int i); };")); +} + +TEST_F(ExplicitConstructorCheckTest, DefaultParameters) { + EXPECT_EQ("class C { explicit C(int i, int j = 0); };", + runCheckOn("class C { C(int i, int j = 0); };")); +} + +} // namespace tidy +} // namespace clang diff --git a/clang-tools-extra/unittests/clang-tidy/LLVMModuleTest.cpp b/clang-tools-extra/unittests/clang-tidy/LLVMModuleTest.cpp new file mode 100644 index 00000000000..da1d9e0db91 --- /dev/null +++ b/clang-tools-extra/unittests/clang-tidy/LLVMModuleTest.cpp @@ -0,0 +1,15 @@ +#include "ClangTidyTest.h" + +#include "llvm/LLVMTidyModule.h" + +namespace clang { +namespace tidy { + +typedef ClangTidyTest<NamespaceCommentCheck> NamespaceCommentCheckTest; + +TEST_F(NamespaceCommentCheckTest, Basic) { + EXPECT_EQ("namespace i {\n} // namespace i", runCheckOn("namespace i {\n}")); +} + +} // namespace tidy +} // namespace clang diff --git a/clang-tools-extra/unittests/clang-tidy/Makefile b/clang-tools-extra/unittests/clang-tidy/Makefile new file mode 100644 index 00000000000..2c617ef3f51 --- /dev/null +++ b/clang-tools-extra/unittests/clang-tidy/Makefile @@ -0,0 +1,24 @@ +##===- unittests/clang-tidy/Makefile -----------------------*- Makefile -*-===## +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +##===----------------------------------------------------------------------===## + +CLANG_LEVEL = ../../../.. +include $(CLANG_LEVEL)/../../Makefile.config + +TESTNAME = ClangTidy +LINK_COMPONENTS := asmparser bitreader support MC MCParser option +USEDLIBS = clangTidy.a clangTidyLLVMModule.a clangTidyGoogleModule.a \ + clangFormat.a clangTooling.a clangFrontend.a clangSerialization.a \ + clangDriver.a clangRewriteFrontend.a clangRewriteCore.a \ + clangParse.a clangSema.a clangAnalysis.a clangAST.a \ + clangASTMatchers.a clangEdit.a clangLex.a clangBasic.a + +include $(CLANG_LEVEL)/Makefile +MAKEFILE_UNITTEST_NO_INCLUDE_COMMON := 1 +CPP.Flags += -I$(PROJ_SRC_DIR)/../../clang-tidy +include $(LLVM_SRC_ROOT)/unittests/Makefile.unittest |