| //===--- 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_CLANGTIDY_H |
| #define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CLANGTIDY_H |
| |
| #include "ClangTidyDiagnosticConsumer.h" |
| #include "ClangTidyOptions.h" |
| #include "clang/ASTMatchers/ASTMatchFinder.h" |
| #include "clang/Basic/Diagnostic.h" |
| #include "clang/Basic/SourceManager.h" |
| #include "clang/Tooling/Refactoring.h" |
| #include "llvm/ADT/StringExtras.h" |
| #include "llvm/Support/raw_ostream.h" |
| #include <memory> |
| #include <type_traits> |
| #include <vector> |
| |
| namespace clang { |
| |
| class CompilerInstance; |
| namespace tooling { |
| class CompilationDatabase; |
| } |
| |
| namespace tidy { |
| |
| /// \brief Provides access to the ``ClangTidyCheck`` options via check-local |
| /// names. |
| /// |
| /// Methods of this class prepend ``CheckName + "."`` 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 ``Context``. |
| /// |
| /// Reads the option with the check-local name \p LocalName from the |
| /// ``CheckOptions``. If the corresponding key is not present, returns |
| /// \p Default. |
| std::string get(StringRef LocalName, StringRef Default) const; |
| |
| /// \brief Read a named option from the ``Context``. |
| /// |
| /// Reads the option with the check-local name \p LocalName from local or |
| /// global ``CheckOptions``. Gets local option first. If local is not present, |
| /// falls back to get global option. If global option is not present either, |
| /// returns Default. |
| std::string getLocalOrGlobal(StringRef LocalName, StringRef Default) const; |
| |
| /// \brief Read a named option from the ``Context`` and parse it as an |
| /// integral type ``T``. |
| /// |
| /// Reads the option with the check-local name \p LocalName from the |
| /// ``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 Read a named option from the ``Context`` and parse it as an |
| /// integral type ``T``. |
| /// |
| /// Reads the option with the check-local name \p LocalName from local or |
| /// global ``CheckOptions``. Gets local option first. If local is not present, |
| /// falls back to get global option. If global option is not present either, |
| /// returns Default. |
| template <typename T> |
| typename std::enable_if<std::is_integral<T>::value, T>::type |
| getLocalOrGlobal(StringRef LocalName, T Default) const { |
| std::string Value = getLocalOrGlobal(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 |
| /// ``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. |
| /// |
| /// To implement a ``ClangTidyCheck``, write a subclass and override some of the |
| /// base class's methods. E.g. to implement a check that validates namespace |
| /// declarations, override ``registerMatchers``: |
| /// |
| /// ~~~{.cpp} |
| /// void registerMatchers(ast_matchers::MatchFinder *Finder) override { |
| /// Finder->addMatcher(namespaceDecl().bind("namespace"), this); |
| /// } |
| /// ~~~ |
| /// |
| /// and then override ``check(const MatchResult &Result)`` to do the actual |
| /// check for each match. |
| /// |
| /// A new ``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: |
| /// \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()); |
| } |
| |
| /// \brief Override this to register ``PPCallbacks`` with ``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 Override this to register AST matchers 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 ``ClangTidyChecks`` that register ASTMatchers should do the actual |
| /// work in here. |
| virtual void check(const ast_matchers::MatchFinder::MatchResult &Result) {} |
| |
| /// \brief Add a diagnostic with the check's name. |
| DiagnosticBuilder diag(SourceLocation Loc, StringRef Description, |
| DiagnosticIDs::Level Level = DiagnosticIDs::Warning); |
| |
| /// \brief Should store all options supported by this check with their |
| /// current values or default values for options that haven't been overridden. |
| /// |
| /// The check should use ``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: |
| void run(const ast_matchers::MatchFinder::MatchResult &Result) override; |
| StringRef getID() const override { return CheckName; } |
| std::string CheckName; |
| ClangTidyContext *Context; |
| |
| protected: |
| OptionsView Options; |
| /// \brief Returns the main file name of the current translation unit. |
| StringRef getCurrentMainFile() const { return Context->getCurrentFile(); } |
| /// \brief Returns the language options from the context. |
| LangOptions getLangOpts() const { return Context->getLangOpts(); } |
| }; |
| |
| class ClangTidyCheckFactories; |
| |
| class ClangTidyASTConsumerFactory { |
| public: |
| ClangTidyASTConsumerFactory(ClangTidyContext &Context); |
| |
| /// \brief Returns an ASTConsumer that runs the specified clang-tidy checks. |
| std::unique_ptr<clang::ASTConsumer> |
| CreateASTConsumer(clang::CompilerInstance &Compiler, StringRef File); |
| |
| /// \brief Get the list of enabled checks. |
| std::vector<std::string> getCheckNames(); |
| |
| /// \brief Get the union of options from all checks. |
| ClangTidyOptions::OptionMap getCheckOptions(); |
| |
| private: |
| ClangTidyContext &Context; |
| std::unique_ptr<ClangTidyCheckFactories> CheckFactories; |
| }; |
| |
| /// \brief Fills the list of check names that are enabled when the provided |
| /// filters are applied. |
| std::vector<std::string> getCheckNames(const ClangTidyOptions &Options, |
| bool AllowEnablingAnalyzerAlphaCheckers); |
| |
| /// \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, |
| bool AllowEnablingAnalyzerAlphaCheckers); |
| |
| /// \brief Run a set of clang-tidy checks on a set of files. |
| /// |
| /// \param EnableCheckProfile If provided, it enables check profile collection |
| /// in MatchFinder, and will contain the result of the profile. |
| /// \param StoreCheckProfile If provided, and EnableCheckProfile is true, |
| /// the profile will not be output to stderr, but will instead be stored |
| /// as a JSON file in the specified directory. |
| void runClangTidy(clang::tidy::ClangTidyContext &Context, |
| const tooling::CompilationDatabase &Compilations, |
| ArrayRef<std::string> InputFiles, |
| llvm::IntrusiveRefCntPtr<vfs::FileSystem> BaseFS, |
| bool EnableCheckProfile = false, |
| llvm::StringRef StoreCheckProfile = StringRef()); |
| |
| // 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 and reformatted. If no |
| /// clang-format configuration file is found, the given \P FormatStyle is used. |
| void handleErrors(ClangTidyContext &Context, bool Fix, |
| unsigned &WarningsAsErrorsCount, |
| llvm::IntrusiveRefCntPtr<vfs::FileSystem> BaseFS); |
| |
| /// \brief Serializes replacements into YAML and writes them to the specified |
| /// output stream. |
| void exportReplacements(StringRef MainFilePath, |
| const std::vector<ClangTidyError> &Errors, |
| raw_ostream &OS); |
| |
| } // end namespace tidy |
| } // end namespace clang |
| |
| #endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CLANGTIDY_H |