Implemented clang-tidy-check-specific options.

Summary:
Each check can implement readOptions and storeOptions methods to read
and store custom options. Each check's options are stored in a local namespace
to avoid name collisions and provide some sort of context to the user.

Reviewers: bkramer, klimek

Reviewed By: klimek

Subscribers: cfe-commits

Differential Revision: http://reviews.llvm.org/D5296

llvm-svn: 217661
This commit is contained in:
Alexander Kornienko 2014-09-12 08:53:36 +00:00
parent 313f5e2f29
commit 6e0cbc8947
35 changed files with 263 additions and 58 deletions

View File

@ -214,13 +214,11 @@ ClangTidyASTConsumerFactory::CreateASTConsumer(
Context.setASTContext(&Compiler.getASTContext()); Context.setASTContext(&Compiler.getASTContext());
std::vector<std::unique_ptr<ClangTidyCheck>> Checks; std::vector<std::unique_ptr<ClangTidyCheck>> Checks;
GlobList &Filter = Context.getChecksFilter(); CheckFactories->createChecks(&Context, Checks);
CheckFactories->createChecks(Filter, Checks);
std::unique_ptr<ast_matchers::MatchFinder> Finder( std::unique_ptr<ast_matchers::MatchFinder> Finder(
new ast_matchers::MatchFinder); new ast_matchers::MatchFinder);
for (auto &Check : Checks) { for (auto &Check : Checks) {
Check->setContext(&Context);
Check->registerMatchers(&*Finder); Check->registerMatchers(&*Finder);
Check->registerPPCallbacks(Compiler); Check->registerPPCallbacks(Compiler);
} }
@ -235,6 +233,7 @@ ClangTidyASTConsumerFactory::CreateASTConsumer(
AnalyzerOptions->Config["cfg-temporary-dtors"] = AnalyzerOptions->Config["cfg-temporary-dtors"] =
Context.getOptions().AnalyzeTemporaryDtors ? "true" : "false"; Context.getOptions().AnalyzeTemporaryDtors ? "true" : "false";
GlobList &Filter = Context.getChecksFilter();
AnalyzerOptions->CheckersControlList = getCheckersControlList(Filter); AnalyzerOptions->CheckersControlList = getCheckersControlList(Filter);
if (!AnalyzerOptions->CheckersControlList.empty()) { if (!AnalyzerOptions->CheckersControlList.empty()) {
AnalyzerOptions->AnalysisStoreOpt = RegionStoreModel; AnalyzerOptions->AnalysisStoreOpt = RegionStoreModel;
@ -251,9 +250,9 @@ ClangTidyASTConsumerFactory::CreateASTConsumer(
std::move(Consumers), std::move(Finder), std::move(Checks)); std::move(Consumers), std::move(Finder), std::move(Checks));
} }
std::vector<std::string> std::vector<std::string> ClangTidyASTConsumerFactory::getCheckNames() {
ClangTidyASTConsumerFactory::getCheckNames(GlobList &Filter) {
std::vector<std::string> CheckNames; std::vector<std::string> CheckNames;
GlobList &Filter = Context.getChecksFilter();
for (const auto &CheckFactory : *CheckFactories) { for (const auto &CheckFactory : *CheckFactories) {
if (Filter.contains(CheckFactory.first)) if (Filter.contains(CheckFactory.first))
CheckNames.push_back(CheckFactory.first); CheckNames.push_back(CheckFactory.first);
@ -266,6 +265,15 @@ ClangTidyASTConsumerFactory::getCheckNames(GlobList &Filter) {
return CheckNames; return CheckNames;
} }
ClangTidyOptions::OptionMap ClangTidyASTConsumerFactory::getCheckOptions() {
ClangTidyOptions::OptionMap Options;
std::vector<std::unique_ptr<ClangTidyCheck>> Checks;
CheckFactories->createChecks(&Context, Checks);
for (const auto &Check : Checks)
Check->storeOptions(Options);
return Options;
}
ClangTidyASTConsumerFactory::CheckersList ClangTidyASTConsumerFactory::CheckersList
ClangTidyASTConsumerFactory::getCheckersControlList(GlobList &Filter) { ClangTidyASTConsumerFactory::getCheckersControlList(GlobList &Filter) {
CheckersList List; CheckersList List;
@ -307,9 +315,25 @@ void ClangTidyCheck::run(const ast_matchers::MatchFinder::MatchResult &Result) {
check(Result); check(Result);
} }
void ClangTidyCheck::setName(StringRef Name) { OptionsView::OptionsView(StringRef CheckName,
assert(CheckName.empty()); const ClangTidyOptions::OptionMap &CheckOptions)
CheckName = Name.str(); : NamePrefix(CheckName.str() + "."), CheckOptions(CheckOptions) {}
std::string OptionsView::get(StringRef LocalName, std::string Default) const {
const auto &Iter = CheckOptions.find(NamePrefix + LocalName.str());
if (Iter != CheckOptions.end())
return Iter->second;
return Default;
}
void OptionsView::store(ClangTidyOptions::OptionMap &Options,
StringRef LocalName, StringRef Value) const {
Options[NamePrefix + LocalName.str()] = Value;
}
void OptionsView::store(ClangTidyOptions::OptionMap &Options,
StringRef LocalName, int64_t Value) const {
store(Options, LocalName, llvm::itostr(Value));
} }
std::vector<std::string> getCheckNames(const ClangTidyOptions &Options) { std::vector<std::string> getCheckNames(const ClangTidyOptions &Options) {
@ -317,7 +341,15 @@ std::vector<std::string> getCheckNames(const ClangTidyOptions &Options) {
llvm::make_unique<DefaultOptionsProvider>(ClangTidyGlobalOptions(), llvm::make_unique<DefaultOptionsProvider>(ClangTidyGlobalOptions(),
Options)); Options));
ClangTidyASTConsumerFactory Factory(Context); ClangTidyASTConsumerFactory Factory(Context);
return Factory.getCheckNames(Context.getChecksFilter()); return Factory.getCheckNames();
}
ClangTidyOptions::OptionMap getCheckOptions(const ClangTidyOptions &Options) {
clang::tidy::ClangTidyContext Context(
llvm::make_unique<DefaultOptionsProvider>(ClangTidyGlobalOptions(),
Options));
ClangTidyASTConsumerFactory Factory(Context);
return Factory.getCheckOptions();
} }
ClangTidyStats ClangTidyStats

View File

@ -16,7 +16,10 @@
#include "clang/Basic/Diagnostic.h" #include "clang/Basic/Diagnostic.h"
#include "clang/Basic/SourceManager.h" #include "clang/Basic/SourceManager.h"
#include "clang/Tooling/Refactoring.h" #include "clang/Tooling/Refactoring.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/Support/raw_ostream.h"
#include <memory> #include <memory>
#include <type_traits>
#include <vector> #include <vector>
namespace clang { namespace clang {
@ -28,6 +31,55 @@ class CompilationDatabase;
namespace tidy { namespace tidy {
/// \brief Provides access to the \c ClangTidyCheck options via check-local
/// names.
///
/// Methods of this class prepend <tt>CheckName + "."</tt> to translate
/// check-local option names to global option names.
class OptionsView {
public:
/// \brief Initializes the instance using \p CheckName + "." as a prefix.
OptionsView(StringRef CheckName,
const ClangTidyOptions::OptionMap &CheckOptions);
/// \brief Read a named option from the \c Context.
///
/// Reads the option with the check-local name \p LocalName from the
/// \c CheckOptions. If the corresponding key is not present, returns
/// \p Default.
std::string get(StringRef LocalName, std::string Default) const;
/// \brief Read a named option from the \c Context and parse it as an integral
/// type \c T.
///
/// Reads the option with the check-local name \p LocalName from the
/// \c CheckOptions. If the corresponding key is not present, returns
/// \p Default.
template <typename T>
typename std::enable_if<std::is_integral<T>::value, T>::type
get(StringRef LocalName, T Default) const {
std::string Value = get(LocalName, "");
T Result = Default;
if (!Value.empty())
StringRef(Value).getAsInteger(10, Result);
return Result;
}
/// \brief Stores an option with the check-local name \p LocalName with string
/// value \p Value to \p Options.
void store(ClangTidyOptions::OptionMap &Options, StringRef LocalName,
StringRef Value) const;
/// \brief Stores an option with the check-local name \p LocalName with
/// \c int64_t value \p Value to \p Options.
void store(ClangTidyOptions::OptionMap &Options, StringRef LocalName,
int64_t Value) const;
private:
std::string NamePrefix;
const ClangTidyOptions::OptionMap &CheckOptions;
};
/// \brief Base class for all clang-tidy checks. /// \brief Base class for all clang-tidy checks.
/// ///
/// To implement a \c ClangTidyCheck, write a subclass and overwrite some of the /// To implement a \c ClangTidyCheck, write a subclass and overwrite some of the
@ -49,6 +101,18 @@ namespace tidy {
/// useful/necessary. /// useful/necessary.
class ClangTidyCheck : public ast_matchers::MatchFinder::MatchCallback { class ClangTidyCheck : public ast_matchers::MatchFinder::MatchCallback {
public: public:
/// \brief Initializes the check with \p CheckName and \p Context.
///
/// Derived classes must implement the constructor with this signature or
/// delegate it. If a check needs to read options, it can do this in the
/// constructor using the Options.get() methods below.
ClangTidyCheck(StringRef CheckName, ClangTidyContext *Context)
: CheckName(CheckName), Context(Context),
Options(CheckName, Context->getOptions().CheckOptions) {
assert(Context != nullptr);
assert(!CheckName.empty());
}
virtual ~ClangTidyCheck() {} virtual ~ClangTidyCheck() {}
/// \brief Overwrite this to register \c PPCallbacks with \c Compiler. /// \brief Overwrite this to register \c PPCallbacks with \c Compiler.
@ -75,21 +139,24 @@ public:
/// work in here. /// work in here.
virtual void check(const ast_matchers::MatchFinder::MatchResult &Result) {} 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; }
/// \brief Add a diagnostic with the check's name. /// \brief Add a diagnostic with the check's name.
DiagnosticBuilder diag(SourceLocation Loc, StringRef Description, DiagnosticBuilder diag(SourceLocation Loc, StringRef Description,
DiagnosticIDs::Level Level = DiagnosticIDs::Warning); DiagnosticIDs::Level Level = DiagnosticIDs::Warning);
/// \brief Sets the check name. Intended to be used by the clang-tidy /// \brief Should store all options supported by this check with their
/// framework. Can be called only once. /// current values or default values for options that haven't been overridden.
void setName(StringRef Name); ///
/// The check should use \c Options.store() to store each option it supports
/// whether it has the default value or it has been overridden.
virtual void storeOptions(ClangTidyOptions::OptionMap &Options) {}
private: private:
void run(const ast_matchers::MatchFinder::MatchResult &Result) override; void run(const ast_matchers::MatchFinder::MatchResult &Result) override;
ClangTidyContext *Context;
std::string CheckName; std::string CheckName;
ClangTidyContext *Context;
protected:
OptionsView Options;
}; };
class ClangTidyCheckFactories; class ClangTidyCheckFactories;
@ -103,10 +170,13 @@ public:
CreateASTConsumer(clang::CompilerInstance &Compiler, StringRef File); CreateASTConsumer(clang::CompilerInstance &Compiler, StringRef File);
/// \brief Get the list of enabled checks. /// \brief Get the list of enabled checks.
std::vector<std::string> getCheckNames(GlobList &Filter); std::vector<std::string> getCheckNames();
/// \brief Get the union of options from all checks.
ClangTidyOptions::OptionMap getCheckOptions();
private: private:
typedef std::vector<std::pair<std::string, bool> > CheckersList; typedef std::vector<std::pair<std::string, bool>> CheckersList;
CheckersList getCheckersControlList(GlobList &Filter); CheckersList getCheckersControlList(GlobList &Filter);
ClangTidyContext &Context; ClangTidyContext &Context;
@ -117,6 +187,14 @@ private:
/// filters are applied. /// filters are applied.
std::vector<std::string> getCheckNames(const ClangTidyOptions &Options); std::vector<std::string> getCheckNames(const ClangTidyOptions &Options);
/// \brief Returns the effective check-specific options.
///
/// The method configures ClangTidy with the specified \p Options and collects
/// effective options from all created checks. The returned set of options
/// includes default check-specific options for all keys not overridden by \p
/// Options.
ClangTidyOptions::OptionMap getCheckOptions(const ClangTidyOptions &Options);
/// \brief Run a set of clang-tidy checks on a set of files. /// \brief Run a set of clang-tidy checks on a set of files.
ClangTidyStats ClangTidyStats
runClangTidy(std::unique_ptr<ClangTidyOptionsProvider> OptionsProvider, runClangTidy(std::unique_ptr<ClangTidyOptionsProvider> OptionsProvider,

View File

@ -16,19 +16,18 @@
namespace clang { namespace clang {
namespace tidy { namespace tidy {
void ClangTidyCheckFactories::registerCheckFactory( void ClangTidyCheckFactories::registerCheckFactory(StringRef Name,
StringRef Name, std::function<ClangTidyCheck *()> Factory) { CheckFactory Factory) {
Factories[Name] = Factory; Factories[Name] = Factory;
} }
void ClangTidyCheckFactories::createChecks( void ClangTidyCheckFactories::createChecks(
GlobList &Filter, std::vector<std::unique_ptr<ClangTidyCheck>> &Checks) { ClangTidyContext *Context,
std::vector<std::unique_ptr<ClangTidyCheck>> &Checks) {
GlobList &Filter = Context->getChecksFilter();
for (const auto &Factory : Factories) { for (const auto &Factory : Factories) {
if (Filter.contains(Factory.first)) { if (Filter.contains(Factory.first))
ClangTidyCheck *Check = Factory.second(); Checks.emplace_back(Factory.second(Factory.first, Context));
Check->setName(Factory.first);
Checks.emplace_back(Check);
}
} }
} }

View File

@ -26,11 +26,13 @@ namespace tidy {
/// this object. /// this object.
class ClangTidyCheckFactories { class ClangTidyCheckFactories {
public: public:
typedef std::function<ClangTidyCheck *(
StringRef Name, ClangTidyContext *Context)> CheckFactory;
/// \brief Registers check \p Factory with name \p Name. /// \brief Registers check \p Factory with name \p Name.
/// ///
/// For all checks that have default constructors, use \c registerCheck. /// For all checks that have default constructors, use \c registerCheck.
void registerCheckFactory(StringRef Name, void registerCheckFactory(StringRef Name, CheckFactory Factory);
std::function<ClangTidyCheck *()> Factory);
/// \brief Registers the \c CheckType with the name \p Name. /// \brief Registers the \c CheckType with the name \p Name.
/// ///
@ -53,19 +55,21 @@ public:
/// } /// }
/// }; /// };
/// \endcode /// \endcode
template<typename CheckType> template <typename CheckType> void registerCheck(StringRef CheckName) {
void registerCheck(StringRef Name) { registerCheckFactory(CheckName,
registerCheckFactory(Name, []() { return new CheckType(); }); [](StringRef Name, ClangTidyContext *Context) {
return new CheckType(Name, Context);
});
} }
/// \brief Create instances of all checks matching \p CheckRegexString and /// \brief Create instances of all checks matching \p CheckRegexString and
/// store them in \p Checks. /// store them in \p Checks.
/// ///
/// The caller takes ownership of the return \c ClangTidyChecks. /// The caller takes ownership of the return \c ClangTidyChecks.
void createChecks(GlobList &Filter, void createChecks(ClangTidyContext *Context,
std::vector<std::unique_ptr<ClangTidyCheck>> &Checks); std::vector<std::unique_ptr<ClangTidyCheck>> &Checks);
typedef std::map<std::string, std::function<ClangTidyCheck *()>> FactoryMap; typedef std::map<std::string, CheckFactory> FactoryMap;
FactoryMap::const_iterator begin() const { return Factories.begin(); } FactoryMap::const_iterator begin() const { return Factories.begin(); }
FactoryMap::const_iterator end() const { return Factories.end(); } FactoryMap::const_iterator end() const { return Factories.end(); }
bool empty() const { return Factories.empty(); } bool empty() const { return Factories.empty(); }

View File

@ -25,6 +25,7 @@ using clang::tidy::FileFilter;
LLVM_YAML_IS_FLOW_SEQUENCE_VECTOR(FileFilter) LLVM_YAML_IS_FLOW_SEQUENCE_VECTOR(FileFilter)
LLVM_YAML_IS_FLOW_SEQUENCE_VECTOR(FileFilter::LineRange) LLVM_YAML_IS_FLOW_SEQUENCE_VECTOR(FileFilter::LineRange)
LLVM_YAML_IS_SEQUENCE_VECTOR(ClangTidyOptions::StringPair);
namespace llvm { namespace llvm {
namespace yaml { namespace yaml {
@ -57,11 +58,34 @@ template <> struct MappingTraits<FileFilter> {
} }
}; };
template <> struct MappingTraits<ClangTidyOptions::StringPair> {
static void mapping(IO &IO, ClangTidyOptions::StringPair &KeyValue) {
IO.mapRequired("key", KeyValue.first);
IO.mapRequired("value", KeyValue.second);
}
};
struct NOptionMap {
NOptionMap(IO &) {}
NOptionMap(IO &, const ClangTidyOptions::OptionMap &OptionMap)
: Options(OptionMap.begin(), OptionMap.end()) {}
ClangTidyOptions::OptionMap denormalize(IO &) {
ClangTidyOptions::OptionMap Map;
for (const auto &KeyValue : Options)
Map[KeyValue.first] = KeyValue.second;
return Map;
}
std::vector<ClangTidyOptions::StringPair> Options;
};
template <> struct MappingTraits<ClangTidyOptions> { template <> struct MappingTraits<ClangTidyOptions> {
static void mapping(IO &IO, ClangTidyOptions &Options) { static void mapping(IO &IO, ClangTidyOptions &Options) {
MappingNormalization<NOptionMap, ClangTidyOptions::OptionMap> NOpts(
IO, Options.CheckOptions);
IO.mapOptional("Checks", Options.Checks); IO.mapOptional("Checks", Options.Checks);
IO.mapOptional("HeaderFilterRegex", Options.HeaderFilterRegex); IO.mapOptional("HeaderFilterRegex", Options.HeaderFilterRegex);
IO.mapOptional("AnalyzeTemporaryDtors", Options.AnalyzeTemporaryDtors); IO.mapOptional("AnalyzeTemporaryDtors", Options.AnalyzeTemporaryDtors);
IO.mapOptional("CheckOptions", NOpts->Options);
} }
}; };
@ -85,6 +109,10 @@ ClangTidyOptions::mergeWith(const ClangTidyOptions &Other) const {
Result.HeaderFilterRegex = Other.HeaderFilterRegex; Result.HeaderFilterRegex = Other.HeaderFilterRegex;
if (Other.AnalyzeTemporaryDtors) if (Other.AnalyzeTemporaryDtors)
Result.AnalyzeTemporaryDtors = Other.AnalyzeTemporaryDtors; Result.AnalyzeTemporaryDtors = Other.AnalyzeTemporaryDtors;
for (const auto &KeyValue : Other.CheckOptions)
Result.CheckOptions[KeyValue.first] = KeyValue.second;
return Result; return Result;
} }
@ -169,6 +197,10 @@ FileOptionsProvider::TryReadConfigFile(StringRef Directory) {
llvm::MemoryBuffer::getFile(ConfigFile.c_str()); llvm::MemoryBuffer::getFile(ConfigFile.c_str());
if (std::error_code EC = Text.getError()) if (std::error_code EC = Text.getError())
return EC; return EC;
// Skip empty files, e.g. files opened for writing via shell output
// redirection.
if ((*Text)->getBuffer().empty())
return make_error_code(llvm::errc::no_such_file_or_directory);
if (std::error_code EC = parseConfiguration((*Text)->getBuffer(), Options)) if (std::error_code EC = parseConfiguration((*Text)->getBuffer(), Options))
return EC; return EC;
return Options.mergeWith(OverrideOptions); return Options.mergeWith(OverrideOptions);

View File

@ -14,6 +14,7 @@
#include "llvm/ADT/StringMap.h" #include "llvm/ADT/StringMap.h"
#include "llvm/ADT/StringRef.h" #include "llvm/ADT/StringRef.h"
#include "llvm/Support/ErrorOr.h" #include "llvm/Support/ErrorOr.h"
#include <map>
#include <string> #include <string>
#include <system_error> #include <system_error>
#include <utility> #include <utility>
@ -70,6 +71,12 @@ struct ClangTidyOptions {
/// \brief Turns on temporary destructor-based analysis. /// \brief Turns on temporary destructor-based analysis.
llvm::Optional<bool> AnalyzeTemporaryDtors; llvm::Optional<bool> AnalyzeTemporaryDtors;
typedef std::pair<std::string, std::string> StringPair;
typedef std::map<std::string, std::string> OptionMap;
/// \brief Key-value mapping used to store check-specific options.
OptionMap CheckOptions;
}; };
/// \brief Abstract interface for retrieving various ClangTidy options. /// \brief Abstract interface for retrieving various ClangTidy options.

View File

@ -26,6 +26,8 @@ namespace readability {
/// ones generated by -Wold-style-cast. /// ones generated by -Wold-style-cast.
class AvoidCStyleCastsCheck : public ClangTidyCheck { class AvoidCStyleCastsCheck : public ClangTidyCheck {
public: public:
AvoidCStyleCastsCheck(StringRef Name, ClangTidyContext *Context)
: ClangTidyCheck(Name, Context) {}
void registerMatchers(ast_matchers::MatchFinder *Finder) override; void registerMatchers(ast_matchers::MatchFinder *Finder) override;
void check(const ast_matchers::MatchFinder::MatchResult &Result) override; void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
}; };

View File

@ -21,6 +21,8 @@ namespace tidy {
/// http://google-styleguide.googlecode.com/svn/trunk/cppguide.xml#Explicit_Constructors /// http://google-styleguide.googlecode.com/svn/trunk/cppguide.xml#Explicit_Constructors
class ExplicitConstructorCheck : public ClangTidyCheck { class ExplicitConstructorCheck : public ClangTidyCheck {
public: public:
ExplicitConstructorCheck(StringRef Name, ClangTidyContext *Context)
: ClangTidyCheck(Name, Context) {}
void registerMatchers(ast_matchers::MatchFinder *Finder) override; void registerMatchers(ast_matchers::MatchFinder *Finder) override;
void check(const ast_matchers::MatchFinder::MatchResult &Result) override; void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
}; };

View File

@ -24,6 +24,8 @@ namespace build {
/// Corresponding cpplint.py check name: 'build/explicit_make_pair'. /// Corresponding cpplint.py check name: 'build/explicit_make_pair'.
class ExplicitMakePairCheck : public ClangTidyCheck { class ExplicitMakePairCheck : public ClangTidyCheck {
public: public:
ExplicitMakePairCheck(StringRef Name, ClangTidyContext *Context)
: ClangTidyCheck(Name, Context) {}
void registerMatchers(ast_matchers::MatchFinder *Finder) override; void registerMatchers(ast_matchers::MatchFinder *Finder) override;
void check(const ast_matchers::MatchFinder::MatchResult &Result) override; void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
}; };

View File

@ -21,9 +21,9 @@ namespace runtime {
/// Correspondig cpplint.py check: runtime/int. /// Correspondig cpplint.py check: runtime/int.
class IntegerTypesCheck : public ClangTidyCheck { class IntegerTypesCheck : public ClangTidyCheck {
public: public:
IntegerTypesCheck() IntegerTypesCheck(StringRef Name, ClangTidyContext *Context)
: UnsignedTypePrefix("uint"), SignedTypePrefix("int"), : ClangTidyCheck(Name, Context), UnsignedTypePrefix("uint"),
AddUnderscoreT(false) {} SignedTypePrefix("int"), AddUnderscoreT(false) {}
void registerMatchers(ast_matchers::MatchFinder *Finder) override; void registerMatchers(ast_matchers::MatchFinder *Finder) override;
void check(const ast_matchers::MatchFinder::MatchResult &Result) override; void check(const ast_matchers::MatchFinder::MatchResult &Result) override;

View File

@ -24,6 +24,8 @@ namespace runtime {
/// Corresponding cpplint.py check name: 'runtime/memset'. /// Corresponding cpplint.py check name: 'runtime/memset'.
class MemsetZeroLengthCheck : public ClangTidyCheck { class MemsetZeroLengthCheck : public ClangTidyCheck {
public: public:
MemsetZeroLengthCheck(StringRef Name, ClangTidyContext *Context)
: ClangTidyCheck(Name, Context) {}
void registerMatchers(ast_matchers::MatchFinder *Finder) override; void registerMatchers(ast_matchers::MatchFinder *Finder) override;
void check(const ast_matchers::MatchFinder::MatchResult &Result) override; void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
}; };

View File

@ -22,6 +22,8 @@ namespace readability {
/// Corresponding cpplint.py check name: 'readability/function'. /// Corresponding cpplint.py check name: 'readability/function'.
class NamedParameterCheck : public ClangTidyCheck { class NamedParameterCheck : public ClangTidyCheck {
public: public:
NamedParameterCheck(StringRef Name, ClangTidyContext *Context)
: ClangTidyCheck(Name, Context) {}
void registerMatchers(ast_matchers::MatchFinder *Finder) override; void registerMatchers(ast_matchers::MatchFinder *Finder) override;
void check(const ast_matchers::MatchFinder::MatchResult &Result) override; void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
}; };

View File

@ -22,6 +22,8 @@ namespace runtime {
/// Corresponding cpplint.py check name: 'runtime/operator'. /// Corresponding cpplint.py check name: 'runtime/operator'.
class OverloadedUnaryAndCheck : public ClangTidyCheck { class OverloadedUnaryAndCheck : public ClangTidyCheck {
public: public:
OverloadedUnaryAndCheck(StringRef Name, ClangTidyContext *Context)
: ClangTidyCheck(Name, Context) {}
void registerMatchers(ast_matchers::MatchFinder *Finder) override; void registerMatchers(ast_matchers::MatchFinder *Finder) override;
void check(const ast_matchers::MatchFinder::MatchResult &Result) override; void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
}; };

View File

@ -39,6 +39,8 @@ namespace runtime {
/// Corresponding cpplint.py check name: 'runtime/member_string_reference'. /// Corresponding cpplint.py check name: 'runtime/member_string_reference'.
class StringReferenceMemberCheck : public ClangTidyCheck { class StringReferenceMemberCheck : public ClangTidyCheck {
public: public:
StringReferenceMemberCheck(StringRef Name, ClangTidyContext *Context)
: ClangTidyCheck(Name, Context) {}
void registerMatchers(ast_matchers::MatchFinder *Finder) override; void registerMatchers(ast_matchers::MatchFinder *Finder) override;
void check(const ast_matchers::MatchFinder::MatchResult &Result) override; void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
}; };

View File

@ -22,6 +22,8 @@ namespace build {
/// Corresponding cpplint.py check name: 'build/namespaces'. /// Corresponding cpplint.py check name: 'build/namespaces'.
class UnnamedNamespaceInHeaderCheck : public ClangTidyCheck { class UnnamedNamespaceInHeaderCheck : public ClangTidyCheck {
public: public:
UnnamedNamespaceInHeaderCheck(StringRef Name, ClangTidyContext *Context)
: ClangTidyCheck(Name, Context) {}
void registerMatchers(ast_matchers::MatchFinder *Finder) override; void registerMatchers(ast_matchers::MatchFinder *Finder) override;
void check(const ast_matchers::MatchFinder::MatchResult &Result) override; void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
}; };

View File

@ -22,6 +22,8 @@ namespace build {
/// Corresponding cpplint.py check name: 'build/namespaces'. /// Corresponding cpplint.py check name: 'build/namespaces'.
class UsingNamespaceDirectiveCheck : public ClangTidyCheck { class UsingNamespaceDirectiveCheck : public ClangTidyCheck {
public: public:
UsingNamespaceDirectiveCheck(StringRef Name, ClangTidyContext *Context)
: ClangTidyCheck(Name, Context) {}
void registerMatchers(ast_matchers::MatchFinder *Finder) override; void registerMatchers(ast_matchers::MatchFinder *Finder) override;
void check(const ast_matchers::MatchFinder::MatchResult &Result) override; void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
}; };

View File

@ -18,6 +18,8 @@ namespace tidy {
/// Finds and fixes header guards that do not adhere to LLVM style. /// Finds and fixes header guards that do not adhere to LLVM style.
class LLVMHeaderGuardCheck : public HeaderGuardCheck { class LLVMHeaderGuardCheck : public HeaderGuardCheck {
public: public:
LLVMHeaderGuardCheck(StringRef Name, ClangTidyContext *Context)
: HeaderGuardCheck(Name, Context) {}
bool shouldSuggestEndifComment(StringRef Filename) override { return false; } bool shouldSuggestEndifComment(StringRef Filename) override { return false; }
bool shouldFixHeaderGuard(StringRef Filename) override; bool shouldFixHeaderGuard(StringRef Filename) override;
std::string getHeaderGuard(StringRef Filename, StringRef OldGuard) override; std::string getHeaderGuard(StringRef Filename, StringRef OldGuard) override;

View File

@ -20,6 +20,8 @@ namespace tidy {
/// see: http://llvm.org/docs/CodingStandards.html#include-style /// see: http://llvm.org/docs/CodingStandards.html#include-style
class IncludeOrderCheck : public ClangTidyCheck { class IncludeOrderCheck : public ClangTidyCheck {
public: public:
IncludeOrderCheck(StringRef Name, ClangTidyContext *Context)
: ClangTidyCheck(Name, Context) {}
void registerPPCallbacks(CompilerInstance &Compiler) override; void registerPPCallbacks(CompilerInstance &Compiler) override;
}; };

View File

@ -11,20 +11,26 @@
#include "clang/AST/ASTContext.h" #include "clang/AST/ASTContext.h"
#include "clang/ASTMatchers/ASTMatchers.h" #include "clang/ASTMatchers/ASTMatchers.h"
#include "clang/Lex/Lexer.h" #include "clang/Lex/Lexer.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/Support/raw_ostream.h"
using namespace clang::ast_matchers; using namespace clang::ast_matchers;
namespace clang { namespace clang {
namespace tidy { namespace tidy {
NamespaceCommentCheck::NamespaceCommentCheck() NamespaceCommentCheck::NamespaceCommentCheck(StringRef Name,
: NamespaceCommentPattern("^/[/*] *(end (of )?)? *(anonymous|unnamed)? *" ClangTidyContext *Context)
: ClangTidyCheck(Name, Context),
NamespaceCommentPattern("^/[/*] *(end (of )?)? *(anonymous|unnamed)? *"
"namespace( +([a-zA-Z0-9_]+))? *(\\*/)?$", "namespace( +([a-zA-Z0-9_]+))? *(\\*/)?$",
llvm::Regex::IgnoreCase), llvm::Regex::IgnoreCase),
ShortNamespaceLines(1) {} ShortNamespaceLines(Options.get("ShortNamespaceLines", 1u)),
SpacesBeforeComments(Options.get("SpacesBeforeComments", 1u)) {}
void NamespaceCommentCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
Options.store(Opts, "ShortNamespaceLines", ShortNamespaceLines);
Options.store(Opts, "SpacesBeforeComments", SpacesBeforeComments);
}
void NamespaceCommentCheck::registerMatchers(MatchFinder *Finder) { void NamespaceCommentCheck::registerMatchers(MatchFinder *Finder) {
Finder->addMatcher(namespaceDecl().bind("namespace"), this); Finder->addMatcher(namespaceDecl().bind("namespace"), this);
@ -36,10 +42,12 @@ bool locationsInSameFile(const SourceManager &Sources, SourceLocation Loc1,
Sources.getFileID(Loc1) == Sources.getFileID(Loc2); Sources.getFileID(Loc1) == Sources.getFileID(Loc2);
} }
std::string getNamespaceComment(const NamespaceDecl *ND, bool InsertLineBreak) { std::string getNamespaceComment(const NamespaceDecl *ND, bool InsertLineBreak,
unsigned SpacesBeforeComments) {
std::string Fix = "// namespace"; std::string Fix = "// namespace";
if (!ND->isAnonymousNamespace()) if (!ND->isAnonymousNamespace())
Fix.append(" ").append(ND->getNameAsString()); Fix.append(std::string(SpacesBeforeComments, ' '))
.append(ND->getNameAsString());
if (InsertLineBreak) if (InsertLineBreak)
Fix.append("\n"); Fix.append("\n");
return Fix; return Fix;
@ -97,7 +105,8 @@ void NamespaceCommentCheck::check(const MatchFinder::MatchResult &Result) {
diag(Loc, "namespace closing comment refers to a wrong namespace '%0'") diag(Loc, "namespace closing comment refers to a wrong namespace '%0'")
<< NamespaceNameInComment << NamespaceNameInComment
<< FixItHint::CreateReplacement( << FixItHint::CreateReplacement(
OldCommentRange, getNamespaceComment(ND, NeedLineBreak)); OldCommentRange,
getNamespaceComment(ND, NeedLineBreak, SpacesBeforeComments));
return; return;
} }
@ -110,7 +119,8 @@ void NamespaceCommentCheck::check(const MatchFinder::MatchResult &Result) {
diag(ND->getLocation(), "namespace not terminated with a closing comment") diag(ND->getLocation(), "namespace not terminated with a closing comment")
<< FixItHint::CreateInsertion( << FixItHint::CreateInsertion(
AfterRBrace, " " + getNamespaceComment(ND, NeedLineBreak)); AfterRBrace,
" " + getNamespaceComment(ND, NeedLineBreak, SpacesBeforeComments));
} }
} // namespace tidy } // namespace tidy

View File

@ -21,13 +21,16 @@ namespace tidy {
/// see: http://llvm.org/docs/CodingStandards.html#namespace-indentation /// see: http://llvm.org/docs/CodingStandards.html#namespace-indentation
class NamespaceCommentCheck : public ClangTidyCheck { class NamespaceCommentCheck : public ClangTidyCheck {
public: public:
NamespaceCommentCheck(); NamespaceCommentCheck(StringRef Name, ClangTidyContext *Context);
void registerMatchers(ast_matchers::MatchFinder *Finder) override; void registerMatchers(ast_matchers::MatchFinder *Finder) override;
void check(const ast_matchers::MatchFinder::MatchResult &Result) override; void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
private: private:
void storeOptions(ClangTidyOptions::OptionMap &Options) override;
llvm::Regex NamespaceCommentPattern; llvm::Regex NamespaceCommentPattern;
const unsigned ShortNamespaceLines; const unsigned ShortNamespaceLines;
const unsigned SpacesBeforeComments;
}; };
} // namespace tidy } // namespace tidy

View File

@ -17,8 +17,6 @@ using namespace clang::ast_matchers;
namespace clang { namespace clang {
namespace tidy { namespace tidy {
TwineLocalCheck::TwineLocalCheck() {}
void TwineLocalCheck::registerMatchers(MatchFinder *Finder) { void TwineLocalCheck::registerMatchers(MatchFinder *Finder) {
auto TwineType = auto TwineType =
qualType(hasDeclaration(recordDecl(hasName("::llvm::Twine")))); qualType(hasDeclaration(recordDecl(hasName("::llvm::Twine"))));

View File

@ -19,7 +19,8 @@ namespace tidy {
/// and should be generally avoided. /// and should be generally avoided.
class TwineLocalCheck : public ClangTidyCheck { class TwineLocalCheck : public ClangTidyCheck {
public: public:
TwineLocalCheck(); TwineLocalCheck(StringRef Name, ClangTidyContext *Context)
: ClangTidyCheck(Name, Context) {}
void registerMatchers(ast_matchers::MatchFinder *Finder) override; void registerMatchers(ast_matchers::MatchFinder *Finder) override;
void check(const ast_matchers::MatchFinder::MatchResult &Result) override; void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
}; };

View File

@ -18,8 +18,10 @@ using namespace clang::ast_matchers;
namespace clang { namespace clang {
namespace tidy { namespace tidy {
ArgumentCommentCheck::ArgumentCommentCheck() ArgumentCommentCheck::ArgumentCommentCheck(StringRef Name,
: IdentRE("^(/\\* *)([_A-Za-z][_A-Za-z0-9]*)( *= *\\*/)$") {} ClangTidyContext *Context)
: ClangTidyCheck(Name, Context),
IdentRE("^(/\\* *)([_A-Za-z][_A-Za-z0-9]*)( *= *\\*/)$") {}
void ArgumentCommentCheck::registerMatchers(MatchFinder *Finder) { void ArgumentCommentCheck::registerMatchers(MatchFinder *Finder) {
Finder->addMatcher( Finder->addMatcher(

View File

@ -19,7 +19,7 @@ namespace tidy {
/// \brief Checks that argument comments match parameter names. /// \brief Checks that argument comments match parameter names.
class ArgumentCommentCheck : public ClangTidyCheck { class ArgumentCommentCheck : public ClangTidyCheck {
public: public:
ArgumentCommentCheck(); ArgumentCommentCheck(StringRef Name, ClangTidyContext *Context);
void registerMatchers(ast_matchers::MatchFinder *Finder) override; void registerMatchers(ast_matchers::MatchFinder *Finder) override;
void check(const ast_matchers::MatchFinder::MatchResult &Result) override; void check(const ast_matchers::MatchFinder::MatchResult &Result) override;

View File

@ -23,6 +23,8 @@ namespace tidy {
/// } /// }
class BoolPointerImplicitConversion : public ClangTidyCheck { class BoolPointerImplicitConversion : public ClangTidyCheck {
public: public:
BoolPointerImplicitConversion(StringRef Name, ClangTidyContext *Context)
: ClangTidyCheck(Name, Context) {}
void registerMatchers(ast_matchers::MatchFinder *Finder) override; void registerMatchers(ast_matchers::MatchFinder *Finder) override;
void check(const ast_matchers::MatchFinder::MatchResult &Result) override; void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
}; };

View File

@ -23,6 +23,8 @@ namespace tidy {
/// *ptr->get() ==> **ptr /// *ptr->get() ==> **ptr
class RedundantSmartptrGet : public ClangTidyCheck { class RedundantSmartptrGet : public ClangTidyCheck {
public: public:
RedundantSmartptrGet(StringRef Name, ClangTidyContext *Context)
: ClangTidyCheck(Name, Context) {}
void registerMatchers(ast_matchers::MatchFinder *Finder) override; void registerMatchers(ast_matchers::MatchFinder *Finder) override;
void check(const ast_matchers::MatchFinder::MatchResult &Result) override; void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
}; };

View File

@ -19,6 +19,8 @@ namespace tidy {
/// conversions. /// conversions.
class SwappedArgumentsCheck : public ClangTidyCheck { class SwappedArgumentsCheck : public ClangTidyCheck {
public: public:
SwappedArgumentsCheck(StringRef Name, ClangTidyContext *Context)
: ClangTidyCheck(Name, Context) {}
void registerMatchers(ast_matchers::MatchFinder *Finder) override; void registerMatchers(ast_matchers::MatchFinder *Finder) override;
void check(const ast_matchers::MatchFinder::MatchResult &Result) override; void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
}; };

View File

@ -20,6 +20,8 @@ namespace tidy {
/// meant to use a delegating constructor or base class initializer. /// meant to use a delegating constructor or base class initializer.
class UndelegatedConstructorCheck : public ClangTidyCheck { class UndelegatedConstructorCheck : public ClangTidyCheck {
public: public:
UndelegatedConstructorCheck(StringRef Name, ClangTidyContext *Context)
: ClangTidyCheck(Name, Context) {}
void registerMatchers(ast_matchers::MatchFinder *Finder) override; void registerMatchers(ast_matchers::MatchFinder *Finder) override;
void check(const ast_matchers::MatchFinder::MatchResult &Result) override; void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
}; };

View File

@ -37,6 +37,8 @@ namespace tidy {
/// - Ignore objects returned from a call. /// - Ignore objects returned from a call.
class UnusedRAIICheck : public ClangTidyCheck { class UnusedRAIICheck : public ClangTidyCheck {
public: public:
UnusedRAIICheck(StringRef Name, ClangTidyContext *Context)
: ClangTidyCheck(Name, Context) {}
void registerMatchers(ast_matchers::MatchFinder *Finder) override; void registerMatchers(ast_matchers::MatchFinder *Finder) override;
void check(const ast_matchers::MatchFinder::MatchResult &Result) override; void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
}; };

View File

@ -18,6 +18,8 @@ namespace tidy {
/// \brief Use C++11's 'override' and remove 'virtual' where applicable. /// \brief Use C++11's 'override' and remove 'virtual' where applicable.
class UseOverride : public ClangTidyCheck { class UseOverride : public ClangTidyCheck {
public: public:
UseOverride(StringRef Name, ClangTidyContext *Context)
: ClangTidyCheck(Name, Context) {}
void registerMatchers(ast_matchers::MatchFinder *Finder) override; void registerMatchers(ast_matchers::MatchFinder *Finder) override;
void check(const ast_matchers::MatchFinder::MatchResult &Result) override; void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
}; };

View File

@ -173,6 +173,7 @@ int clangTidyMain(int argc, const char **argv) {
} }
if (DumpConfig) { if (DumpConfig) {
EffectiveOptions.CheckOptions = getCheckOptions(EffectiveOptions);
llvm::outs() << configurationAsText(ClangTidyOptions::getDefaults() llvm::outs() << configurationAsText(ClangTidyOptions::getDefaults()
.mergeWith(EffectiveOptions)) .mergeWith(EffectiveOptions))
<< "\n"; << "\n";

View File

@ -18,6 +18,8 @@ namespace tidy {
/// \brief Finds and fixes header guards. /// \brief Finds and fixes header guards.
class HeaderGuardCheck : public ClangTidyCheck { class HeaderGuardCheck : public ClangTidyCheck {
public: public:
HeaderGuardCheck(StringRef Name, ClangTidyContext *Context)
: ClangTidyCheck(Name, Context) {}
void registerPPCallbacks(CompilerInstance &Compiler) override; void registerPPCallbacks(CompilerInstance &Compiler) override;
/// \brief Returns true if the checker should suggest inserting a trailing /// \brief Returns true if the checker should suggest inserting a trailing

View File

@ -8,6 +8,8 @@ namespace test {
class TestCheck : public ClangTidyCheck { class TestCheck : public ClangTidyCheck {
public: public:
TestCheck(StringRef Name, ClangTidyContext *Context)
: ClangTidyCheck(Name, Context) {}
void registerMatchers(ast_matchers::MatchFinder *Finder) override { void registerMatchers(ast_matchers::MatchFinder *Finder) override {
Finder->addMatcher(ast_matchers::varDecl().bind("var"), this); Finder->addMatcher(ast_matchers::varDecl().bind("var"), this);
} }
@ -23,9 +25,8 @@ TEST(ClangTidyDiagnosticConsumer, SortsErrors) {
std::vector<ClangTidyError> Errors; std::vector<ClangTidyError> Errors;
runCheckOnCode<TestCheck>("int a;", &Errors); runCheckOnCode<TestCheck>("int a;", &Errors);
EXPECT_EQ(2ul, Errors.size()); EXPECT_EQ(2ul, Errors.size());
// FIXME: Remove " []" once the check name is removed from the message text. EXPECT_EQ("type specifier", Errors[0].Message.Message);
EXPECT_EQ("type specifier []", Errors[0].Message.Message); EXPECT_EQ("variable", Errors[1].Message.Message);
EXPECT_EQ("variable []", Errors[1].Message.Message);
} }
TEST(GlobList, Empty) { TEST(GlobList, Empty) {

View File

@ -44,13 +44,12 @@ std::string runCheckOnCode(StringRef Code,
std::vector<ClangTidyError> *Errors = nullptr, std::vector<ClangTidyError> *Errors = nullptr,
const Twine &Filename = "input.cc", const Twine &Filename = "input.cc",
ArrayRef<std::string> ExtraArgs = None) { ArrayRef<std::string> ExtraArgs = None) {
T Check;
ClangTidyOptions Options; ClangTidyOptions Options;
Options.Checks = "*"; Options.Checks = "*";
ClangTidyContext Context(llvm::make_unique<DefaultOptionsProvider>( ClangTidyContext Context(llvm::make_unique<DefaultOptionsProvider>(
ClangTidyGlobalOptions(), Options)); ClangTidyGlobalOptions(), Options));
ClangTidyDiagnosticConsumer DiagConsumer(Context); ClangTidyDiagnosticConsumer DiagConsumer(Context);
Check.setContext(&Context); T Check("test-check", &Context);
std::vector<std::string> ArgCXX11(1, "-std=c++11"); std::vector<std::string> ArgCXX11(1, "-std=c++11");
ArgCXX11.insert(ArgCXX11.end(), ExtraArgs.begin(), ExtraArgs.end()); ArgCXX11.insert(ArgCXX11.end(), ExtraArgs.begin(), ExtraArgs.end());

View File

@ -95,6 +95,8 @@ static std::string runHeaderGuardCheck(StringRef Code, const Twine &Filename) {
namespace { namespace {
struct WithEndifComment : public LLVMHeaderGuardCheck { struct WithEndifComment : public LLVMHeaderGuardCheck {
WithEndifComment(StringRef Name, ClangTidyContext *Context)
: LLVMHeaderGuardCheck(Name, Context) {}
bool shouldSuggestEndifComment(StringRef Filename) override { return true; } bool shouldSuggestEndifComment(StringRef Filename) override { return true; }
}; };
} // namespace } // namespace