123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462 |
- //===--- ClangTidyCheck.h - clang-tidy --------------------------*- C++ -*-===//
- //
- // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
- // See https://llvm.org/LICENSE.txt for license information.
- // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
- //
- //===----------------------------------------------------------------------===//
- #ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CLANGTIDYCHECK_H
- #define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CLANGTIDYCHECK_H
- #include "ClangTidyDiagnosticConsumer.h"
- #include "ClangTidyOptions.h"
- #include "clang/ASTMatchers/ASTMatchFinder.h"
- #include "clang/Basic/Diagnostic.h"
- #include <optional>
- #include <type_traits>
- #include <utility>
- #include <vector>
- namespace clang {
- class SourceManager;
- namespace tidy {
- /// This class should be specialized by any enum type that needs to be converted
- /// to and from an \ref llvm::StringRef.
- template <class T> struct OptionEnumMapping {
- // Specializations of this struct must implement this function.
- static ArrayRef<std::pair<T, StringRef>> getEnumMapping() = delete;
- };
- /// 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:
- /// 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);
- /// Override this to disable registering matchers and PP callbacks if an
- /// invalid language version is being used.
- ///
- /// For example if a check is examining overloaded functions then this should
- /// be overridden to return false when the CPlusPlus flag is not set in
- /// \p LangOpts.
- virtual bool isLanguageVersionSupported(const LangOptions &LangOpts) const {
- return true;
- }
- /// Override this to register ``PPCallbacks`` in the preprocessor.
- ///
- /// This should be used for clang-tidy checks that analyze preprocessor-
- /// dependent properties, e.g. include directives and macro definitions.
- ///
- /// This will only be executed if the function isLanguageVersionSupported
- /// returns true.
- ///
- /// There are two Preprocessors to choose from that differ in how they handle
- /// modular #includes:
- /// - PP is the real Preprocessor. It doesn't walk into modular #includes and
- /// thus doesn't generate PPCallbacks for their contents.
- /// - ModuleExpanderPP preprocesses the whole translation unit in the
- /// non-modular mode, which allows it to generate PPCallbacks not only for
- /// the main file and textual headers, but also for all transitively
- /// included modular headers when the analysis runs with modules enabled.
- /// When modules are not enabled ModuleExpanderPP just points to the real
- /// preprocessor.
- virtual void registerPPCallbacks(const SourceManager &SM, Preprocessor *PP,
- Preprocessor *ModuleExpanderPP) {}
- /// 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.
- ///
- /// This will only be executed if the function isLanguageVersionSupported
- /// returns true.
- ///
- /// 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) {}
- /// ``ClangTidyChecks`` that register ASTMatchers should do the actual
- /// work in here.
- virtual void check(const ast_matchers::MatchFinder::MatchResult &Result) {}
- /// Add a diagnostic with the check's name.
- DiagnosticBuilder diag(SourceLocation Loc, StringRef Description,
- DiagnosticIDs::Level Level = DiagnosticIDs::Warning);
- /// Add a diagnostic with the check's name.
- DiagnosticBuilder diag(StringRef Description,
- DiagnosticIDs::Level Level = DiagnosticIDs::Warning);
- /// Adds a diagnostic to report errors in the check's configuration.
- DiagnosticBuilder
- configurationDiag(StringRef Description,
- DiagnosticIDs::Level Level = DiagnosticIDs::Warning) const;
- /// 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) {}
- /// 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 {
- void diagnoseBadIntegerOption(const Twine &Lookup,
- StringRef Unparsed) const;
- void diagnoseBadBooleanOption(const Twine &Lookup,
- StringRef Unparsed) const;
- void diagnoseBadEnumOption(const Twine &Lookup, StringRef Unparsed,
- StringRef Suggestion = StringRef()) const;
- public:
- /// Initializes the instance using \p CheckName + "." as a prefix.
- OptionsView(StringRef CheckName,
- const ClangTidyOptions::OptionMap &CheckOptions,
- ClangTidyContext *Context);
- /// 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, return
- /// ``std::nullopt``.
- std::optional<StringRef> get(StringRef LocalName) const;
- /// 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.
- StringRef get(StringRef LocalName, StringRef Default) const;
- /// 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, return ``std::nullopt``.
- std::optional<StringRef> getLocalOrGlobal(StringRef LocalName) const;
- /// 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 \p Default.
- StringRef getLocalOrGlobal(StringRef LocalName, StringRef Default) const;
- /// 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, return
- /// ``std::nullopt``.
- ///
- /// If the corresponding key can't be parsed as a ``T``, emit a
- /// diagnostic and return ``std::nullopt``.
- template <typename T>
- std::enable_if_t<std::is_integral<T>::value, std::optional<T>>
- get(StringRef LocalName) const {
- if (std::optional<StringRef> Value = get(LocalName)) {
- T Result{};
- if (!StringRef(*Value).getAsInteger(10, Result))
- return Result;
- diagnoseBadIntegerOption(NamePrefix + LocalName, *Value);
- }
- return std::nullopt;
- }
- /// 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, return
- /// \p Default.
- ///
- /// If the corresponding key can't be parsed as a ``T``, emit a
- /// diagnostic and return \p Default.
- template <typename T>
- std::enable_if_t<std::is_integral<T>::value, T> get(StringRef LocalName,
- T Default) const {
- return get<T>(LocalName).value_or(Default);
- }
- /// 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, return ``std::nullopt``.
- ///
- /// If the corresponding key can't be parsed as a ``T``, emit a
- /// diagnostic and return ``std::nullopt``.
- template <typename T>
- std::enable_if_t<std::is_integral<T>::value, std::optional<T>>
- getLocalOrGlobal(StringRef LocalName) const {
- std::optional<StringRef> ValueOr = get(LocalName);
- bool IsGlobal = false;
- if (!ValueOr) {
- IsGlobal = true;
- ValueOr = getLocalOrGlobal(LocalName);
- if (!ValueOr)
- return std::nullopt;
- }
- T Result{};
- if (!StringRef(*ValueOr).getAsInteger(10, Result))
- return Result;
- diagnoseBadIntegerOption(
- IsGlobal ? Twine(LocalName) : NamePrefix + LocalName, *ValueOr);
- return std::nullopt;
- }
- /// 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, return \p Default.
- ///
- /// If the corresponding key can't be parsed as a ``T``, emit a
- /// diagnostic and return \p Default.
- template <typename T>
- std::enable_if_t<std::is_integral<T>::value, T>
- getLocalOrGlobal(StringRef LocalName, T Default) const {
- return getLocalOrGlobal<T>(LocalName).value_or(Default);
- }
- /// Read a named option from the ``Context`` and parse it as an
- /// enum type ``T``.
- ///
- /// Reads the option with the check-local name \p LocalName from the
- /// ``CheckOptions``. If the corresponding key is not present, return
- /// ``std::nullopt``.
- ///
- /// If the corresponding key can't be parsed as a ``T``, emit a
- /// diagnostic and return ``std::nullopt``.
- ///
- /// \ref clang::tidy::OptionEnumMapping must be specialized for ``T`` to
- /// supply the mapping required to convert between ``T`` and a string.
- template <typename T>
- std::enable_if_t<std::is_enum<T>::value, std::optional<T>>
- get(StringRef LocalName, bool IgnoreCase = false) const {
- if (std::optional<int64_t> ValueOr =
- getEnumInt(LocalName, typeEraseMapping<T>(), false, IgnoreCase))
- return static_cast<T>(*ValueOr);
- return std::nullopt;
- }
- /// Read a named option from the ``Context`` and parse it as an
- /// enum type ``T``.
- ///
- /// Reads the option with the check-local name \p LocalName from the
- /// ``CheckOptions``. If the corresponding key is not present, return
- /// \p Default.
- ///
- /// If the corresponding key can't be parsed as a ``T``, emit a
- /// diagnostic and return \p Default.
- ///
- /// \ref clang::tidy::OptionEnumMapping must be specialized for ``T`` to
- /// supply the mapping required to convert between ``T`` and a string.
- template <typename T>
- std::enable_if_t<std::is_enum<T>::value, T>
- get(StringRef LocalName, T Default, bool IgnoreCase = false) const {
- return get<T>(LocalName, IgnoreCase).value_or(Default);
- }
- /// Read a named option from the ``Context`` and parse it as an
- /// enum 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 ``std::nullopt``.
- ///
- /// If the corresponding key can't be parsed as a ``T``, emit a
- /// diagnostic and return ``std::nullopt``.
- ///
- /// \ref clang::tidy::OptionEnumMapping must be specialized for ``T`` to
- /// supply the mapping required to convert between ``T`` and a string.
- template <typename T>
- std::enable_if_t<std::is_enum<T>::value, std::optional<T>>
- getLocalOrGlobal(StringRef LocalName, bool IgnoreCase = false) const {
- if (std::optional<int64_t> ValueOr =
- getEnumInt(LocalName, typeEraseMapping<T>(), true, IgnoreCase))
- return static_cast<T>(*ValueOr);
- return std::nullopt;
- }
- /// Read a named option from the ``Context`` and parse it as an
- /// enum 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 return \p Default.
- ///
- /// If the corresponding key can't be parsed as a ``T``, emit a
- /// diagnostic and return \p Default.
- ///
- /// \ref clang::tidy::OptionEnumMapping must be specialized for ``T`` to
- /// supply the mapping required to convert between ``T`` and a string.
- template <typename T>
- std::enable_if_t<std::is_enum<T>::value, T>
- getLocalOrGlobal(StringRef LocalName, T Default,
- bool IgnoreCase = false) const {
- return getLocalOrGlobal<T>(LocalName, IgnoreCase).value_or(Default);
- }
- /// 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;
- /// Stores an option with the check-local name \p LocalName with
- /// integer value \p Value to \p Options.
- template <typename T>
- std::enable_if_t<std::is_integral<T>::value>
- store(ClangTidyOptions::OptionMap &Options, StringRef LocalName,
- T Value) const {
- storeInt(Options, LocalName, Value);
- }
- /// Stores an option with the check-local name \p LocalName as the string
- /// representation of the Enum \p Value to \p Options.
- ///
- /// \ref clang::tidy::OptionEnumMapping must be specialized for ``T`` to
- /// supply the mapping required to convert between ``T`` and a string.
- template <typename T>
- std::enable_if_t<std::is_enum<T>::value>
- store(ClangTidyOptions::OptionMap &Options, StringRef LocalName,
- T Value) const {
- ArrayRef<std::pair<T, StringRef>> Mapping =
- OptionEnumMapping<T>::getEnumMapping();
- auto Iter = llvm::find_if(
- Mapping, [&](const std::pair<T, StringRef> &NameAndEnum) {
- return NameAndEnum.first == Value;
- });
- assert(Iter != Mapping.end() && "Unknown Case Value");
- store(Options, LocalName, Iter->second);
- }
- private:
- using NameAndValue = std::pair<int64_t, StringRef>;
- std::optional<int64_t> getEnumInt(StringRef LocalName,
- ArrayRef<NameAndValue> Mapping,
- bool CheckGlobal, bool IgnoreCase) const;
- template <typename T>
- std::enable_if_t<std::is_enum<T>::value, std::vector<NameAndValue>>
- typeEraseMapping() const {
- ArrayRef<std::pair<T, StringRef>> Mapping =
- OptionEnumMapping<T>::getEnumMapping();
- std::vector<NameAndValue> Result;
- Result.reserve(Mapping.size());
- for (auto &MappedItem : Mapping) {
- Result.emplace_back(static_cast<int64_t>(MappedItem.first),
- MappedItem.second);
- }
- return Result;
- }
- void storeInt(ClangTidyOptions::OptionMap &Options, StringRef LocalName,
- int64_t Value) const;
- std::string NamePrefix;
- const ClangTidyOptions::OptionMap &CheckOptions;
- ClangTidyContext *Context;
- };
- private:
- void run(const ast_matchers::MatchFinder::MatchResult &Result) override;
- StringRef getID() const override { return CheckName; }
- std::string CheckName;
- ClangTidyContext *Context;
- protected:
- OptionsView Options;
- /// Returns the main file name of the current translation unit.
- StringRef getCurrentMainFile() const { return Context->getCurrentFile(); }
- /// Returns the language options from the context.
- const LangOptions &getLangOpts() const { return Context->getLangOpts(); }
- /// Returns true when the check is run in a use case when only 1 fix will be
- /// applied at a time.
- bool areDiagsSelfContained() const {
- return Context->areDiagsSelfContained();
- }
- };
- /// Read a named option from the ``Context`` and parse it as a bool.
- ///
- /// Reads the option with the check-local name \p LocalName from the
- /// ``CheckOptions``. If the corresponding key is not present, return
- /// ``std::nullopt``.
- ///
- /// If the corresponding key can't be parsed as a bool, emit a
- /// diagnostic and return ``std::nullopt``.
- template <>
- std::optional<bool>
- ClangTidyCheck::OptionsView::get<bool>(StringRef LocalName) const;
- /// Read a named option from the ``Context`` and parse it as a bool.
- ///
- /// Reads the option with the check-local name \p LocalName from the
- /// ``CheckOptions``. If the corresponding key is not present, return
- /// \p Default.
- ///
- /// If the corresponding key can't be parsed as a bool, emit a
- /// diagnostic and return \p Default.
- template <>
- std::optional<bool>
- ClangTidyCheck::OptionsView::getLocalOrGlobal<bool>(StringRef LocalName) const;
- /// Stores an option with the check-local name \p LocalName with
- /// bool value \p Value to \p Options.
- template <>
- void ClangTidyCheck::OptionsView::store<bool>(
- ClangTidyOptions::OptionMap &Options, StringRef LocalName,
- bool Value) const;
- } // namespace tidy
- } // namespace clang
- #endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CLANGTIDYCHECK_H
|