brintos

brintos / llvm-project-archived public Read only

0
0
Text · 21.6 KiB · 905e419 Raw
537 lines · c
1//===----------------------------------------------------------------------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8 9#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CLANGTIDYCHECK_H10#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CLANGTIDYCHECK_H11 12#include "ClangTidyDiagnosticConsumer.h"13#include "ClangTidyOptions.h"14#include "clang/ASTMatchers/ASTMatchFinder.h"15#include "clang/Basic/Diagnostic.h"16#include <optional>17#include <type_traits>18#include <utility>19#include <vector>20 21namespace clang {22 23class SourceManager;24 25namespace tidy {26 27/// This class should be specialized by any enum type that needs to be converted28/// to and from an \ref llvm::StringRef.29template <class T> struct OptionEnumMapping {30  // Specializations of this struct must implement this function.31  static ArrayRef<std::pair<T, StringRef>> getEnumMapping() = delete;32};33 34/// Base class for all clang-tidy checks.35///36/// To implement a ``ClangTidyCheck``, write a subclass and override some of the37/// base class's methods. E.g. to implement a check that validates namespace38/// declarations, override ``registerMatchers``:39///40/// ~~~{.cpp}41/// void registerMatchers(ast_matchers::MatchFinder *Finder) override {42///   Finder->addMatcher(namespaceDecl().bind("namespace"), this);43/// }44/// ~~~45///46/// and then override ``check(const MatchResult &Result)`` to do the actual47/// check for each match.48///49/// A new ``ClangTidyCheck`` instance is created per translation unit.50///51/// FIXME: Figure out whether carrying information from one TU to another is52/// useful/necessary.53class ClangTidyCheck : public ast_matchers::MatchFinder::MatchCallback {54public:55  /// Initializes the check with \p CheckName and \p Context.56  ///57  /// Derived classes must implement the constructor with this signature or58  /// delegate it. If a check needs to read options, it can do this in the59  /// constructor using the Options.get() methods below.60  ClangTidyCheck(StringRef CheckName, ClangTidyContext *Context);61 62  /// Override this to disable registering matchers and PP callbacks if an63  /// invalid language version is being used.64  ///65  /// For example if a check is examining overloaded functions then this should66  /// be overridden to return false when the CPlusPlus flag is not set in67  /// \p LangOpts.68  virtual bool isLanguageVersionSupported(const LangOptions &LangOpts) const {69    return true;70  }71 72  /// Override this to register ``PPCallbacks`` in the preprocessor.73  ///74  /// This should be used for clang-tidy checks that analyze preprocessor-75  /// dependent properties, e.g. include directives and macro definitions.76  ///77  /// This will only be executed if the function isLanguageVersionSupported78  /// returns true.79  ///80  /// There are two Preprocessors to choose from that differ in how they handle81  /// modular #includes:82  ///  - PP is the real Preprocessor. It doesn't walk into modular #includes and83  ///    thus doesn't generate PPCallbacks for their contents.84  ///  - ModuleExpanderPP preprocesses the whole translation unit in the85  ///    non-modular mode, which allows it to generate PPCallbacks not only for86  ///    the main file and textual headers, but also for all transitively87  ///    included modular headers when the analysis runs with modules enabled.88  ///    When modules are not enabled ModuleExpanderPP just points to the real89  ///    preprocessor.90  virtual void registerPPCallbacks(const SourceManager &SM, Preprocessor *PP,91                                   Preprocessor *ModuleExpanderPP) {}92 93  /// Override this to register AST matchers with \p Finder.94  ///95  /// This should be used by clang-tidy checks that analyze code properties that96  /// dependent on AST knowledge.97  ///98  /// You can register as many matchers as necessary with \p Finder. Usually,99  /// "this" will be used as callback, but you can also specify other callback100  /// classes. Thereby, different matchers can trigger different callbacks.101  ///102  /// This will only be executed if the function isLanguageVersionSupported103  /// returns true.104  ///105  /// If you need to merge information between the different matchers, you can106  /// store these as members of the derived class. However, note that all107  /// matches occur in the order of the AST traversal.108  virtual void registerMatchers(ast_matchers::MatchFinder *Finder) {}109 110  /// ``ClangTidyChecks`` that register ASTMatchers should do the actual111  /// work in here.112  virtual void check(const ast_matchers::MatchFinder::MatchResult &Result) {}113 114  /// Add a diagnostic with the check's name.115  DiagnosticBuilder diag(SourceLocation Loc, StringRef Description,116                         DiagnosticIDs::Level Level = DiagnosticIDs::Warning);117 118  /// Add a diagnostic with the check's name.119  DiagnosticBuilder diag(StringRef Description,120                         DiagnosticIDs::Level Level = DiagnosticIDs::Warning);121 122  /// Adds a diagnostic to report errors in the check's configuration.123  DiagnosticBuilder124  configurationDiag(StringRef Description,125                    DiagnosticIDs::Level Level = DiagnosticIDs::Warning) const;126 127  /// Should store all options supported by this check with their128  /// current values or default values for options that haven't been overridden.129  ///130  /// The check should use ``Options.store()`` to store each option it supports131  /// whether it has the default value or it has been overridden.132  virtual void storeOptions(ClangTidyOptions::OptionMap &Options) {}133 134  /// Provides access to the ``ClangTidyCheck`` options via check-local135  /// names.136  ///137  /// Methods of this class prepend ``CheckName + "."`` to translate check-local138  /// option names to global option names.139  class OptionsView {140    void diagnoseBadIntegerOption(const Twine &Lookup,141                                  StringRef Unparsed) const;142    void diagnoseBadBooleanOption(const Twine &Lookup,143                                  StringRef Unparsed) const;144    void diagnoseBadEnumOption(const Twine &Lookup, StringRef Unparsed,145                               StringRef Suggestion = StringRef()) const;146 147  public:148    /// Initializes the instance using \p CheckName + "." as a prefix.149    OptionsView(StringRef CheckName,150                const ClangTidyOptions::OptionMap &CheckOptions,151                ClangTidyContext *Context);152 153    /// Read a named option from the ``Context``.154    ///155    /// Reads the option with the check-local name \p LocalName from the156    /// ``CheckOptions``. If the corresponding key is not present, return157    /// ``std::nullopt``.158    std::optional<StringRef> get(StringRef LocalName) const;159 160    /// Read a named option from the ``Context``.161    ///162    /// Reads the option with the check-local name \p LocalName from the163    /// ``CheckOptions``. If the corresponding key is not present, returns164    /// \p Default.165    StringRef get(StringRef LocalName, StringRef Default) const;166 167    /// Read a named option from the ``Context``.168    ///169    /// Reads the option with the check-local name \p LocalName from local or170    /// global ``CheckOptions``. Gets local option first. If local is not171    /// present, falls back to get global option. If global option is not172    /// present either, return ``std::nullopt``.173    std::optional<StringRef> getLocalOrGlobal(StringRef LocalName) const;174 175    /// Read a named option from the ``Context``.176    ///177    /// Reads the option with the check-local name \p LocalName from local or178    /// global ``CheckOptions``. Gets local option first. If local is not179    /// present, falls back to get global option. If global option is not180    /// present either, returns \p Default.181    StringRef getLocalOrGlobal(StringRef LocalName, StringRef Default) const;182 183    /// Read a named option from the ``Context`` and parse it as an184    /// integral type ``T``.185    ///186    /// Reads the option with the check-local name \p LocalName from the187    /// ``CheckOptions``. If the corresponding key is not present,188    ///  return ``std::nullopt``.189    ///190    /// If the corresponding key can't be parsed as a ``T``, emit a191    /// diagnostic and return ``std::nullopt``.192    template <typename T>193    std::enable_if_t<std::is_integral_v<T>, std::optional<T>>194    get(StringRef LocalName) const {195      if (std::optional<StringRef> Value = get(LocalName)) {196        T Result{};197        if (!StringRef(*Value).getAsInteger(10, Result))198          return Result;199        diagnoseBadIntegerOption(NamePrefix + LocalName, *Value);200      }201      return std::nullopt;202    }203 204    /// Read a named option from the ``Context`` and parse it as an205    /// integral type ``T``.206    ///207    /// Reads the option with the check-local name \p LocalName from the208    /// ``CheckOptions``. If the corresponding key is `none`, `null`,209    /// `-1` or empty, return ``std::nullopt``. If the corresponding210    /// key is not present, return \p Default.211    ///212    /// If the corresponding key can't be parsed as a ``T``, emit a213    /// diagnostic and return \p Default.214    template <typename T>215    std::enable_if_t<std::is_integral_v<T>, std::optional<T>>216    get(StringRef LocalName, std::optional<T> Default) const {217      if (std::optional<StringRef> Value = get(LocalName)) {218        if (Value == "" || Value == "none" || Value == "null" ||219            (std::is_unsigned_v<T> && Value == "-1"))220          return std::nullopt;221        T Result{};222        if (!StringRef(*Value).getAsInteger(10, Result))223          return Result;224        diagnoseBadIntegerOption(NamePrefix + LocalName, *Value);225      }226      return Default;227    }228 229    /// Read a named option from the ``Context`` and parse it as an230    /// integral type ``T``.231    ///232    /// Reads the option with the check-local name \p LocalName from the233    /// ``CheckOptions``. If the corresponding key is not present, return234    /// \p Default.235    ///236    /// If the corresponding key can't be parsed as a ``T``, emit a237    /// diagnostic and return \p Default.238    template <typename T>239    std::enable_if_t<std::is_integral_v<T>, T> get(StringRef LocalName,240                                                   T Default) const {241      return get<T>(LocalName).value_or(Default);242    }243 244    /// Read a named option from the ``Context`` and parse it as an245    /// integral type ``T``.246    ///247    /// Reads the option with the check-local name \p LocalName from local or248    /// global ``CheckOptions``. Gets local option first. If local is not249    /// present, falls back to get global option. If global option is not250    /// present either, return ``std::nullopt``.251    ///252    /// If the corresponding key can't be parsed as a ``T``, emit a253    /// diagnostic and return ``std::nullopt``.254    template <typename T>255    std::enable_if_t<std::is_integral_v<T>, std::optional<T>>256    getLocalOrGlobal(StringRef LocalName) const {257      std::optional<StringRef> ValueOr = get(LocalName);258      bool IsGlobal = false;259      if (!ValueOr) {260        IsGlobal = true;261        ValueOr = getLocalOrGlobal(LocalName);262        if (!ValueOr)263          return std::nullopt;264      }265      T Result{};266      if (!StringRef(*ValueOr).getAsInteger(10, Result))267        return Result;268      diagnoseBadIntegerOption(269          IsGlobal ? Twine(LocalName) : NamePrefix + LocalName, *ValueOr);270      return std::nullopt;271    }272 273    /// Read a named option from the ``Context`` and parse it as an274    /// integral type ``T``.275    ///276    /// Reads the option with the check-local name \p LocalName from local or277    /// global ``CheckOptions``. Gets local option first. If local is not278    /// present, falls back to get global option. If global option is not279    /// present either, return \p Default. If the value value was found280    /// and equals ``none``, ``null``, ``-1`` or empty, return ``std::nullopt``.281    ///282    /// If the corresponding key can't be parsed as a ``T``, emit a283    /// diagnostic and return \p Default.284    template <typename T>285    std::enable_if_t<std::is_integral_v<T>, std::optional<T>>286    getLocalOrGlobal(StringRef LocalName, std::optional<T> Default) const {287      std::optional<StringRef> ValueOr = get(LocalName);288      bool IsGlobal = false;289      if (!ValueOr) {290        IsGlobal = true;291        ValueOr = getLocalOrGlobal(LocalName);292        if (!ValueOr)293          return Default;294      }295      T Result{};296      if (ValueOr == "" || ValueOr == "none" || ValueOr == "null" ||297          (std::is_unsigned_v<T> && ValueOr == "-1"))298        return std::nullopt;299      if (!StringRef(*ValueOr).getAsInteger(10, Result))300        return Result;301      diagnoseBadIntegerOption(302          IsGlobal ? Twine(LocalName) : NamePrefix + LocalName, *ValueOr);303      return Default;304    }305 306    /// Read a named option from the ``Context`` and parse it as an307    /// integral type ``T``.308    ///309    /// Reads the option with the check-local name \p LocalName from local or310    /// global ``CheckOptions``. Gets local option first. If local is not311    /// present, falls back to get global option. If global option is not312    /// present either, return \p Default.313    ///314    /// If the corresponding key can't be parsed as a ``T``, emit a315    /// diagnostic and return \p Default.316    template <typename T>317    std::enable_if_t<std::is_integral_v<T>, T>318    getLocalOrGlobal(StringRef LocalName, T Default) const {319      return getLocalOrGlobal<T>(LocalName).value_or(Default);320    }321 322    /// Read a named option from the ``Context`` and parse it as an323    /// enum type ``T``.324    ///325    /// Reads the option with the check-local name \p LocalName from the326    /// ``CheckOptions``. If the corresponding key is not present, return327    /// ``std::nullopt``.328    ///329    /// If the corresponding key can't be parsed as a ``T``, emit a330    /// diagnostic and return ``std::nullopt``.331    ///332    /// \ref clang::tidy::OptionEnumMapping must be specialized for ``T`` to333    /// supply the mapping required to convert between ``T`` and a string.334    template <typename T>335    std::enable_if_t<std::is_enum_v<T>, std::optional<T>>336    get(StringRef LocalName) const {337      if (std::optional<int64_t> ValueOr =338              getEnumInt(LocalName, typeEraseMapping<T>(), false))339        return static_cast<T>(*ValueOr);340      return std::nullopt;341    }342 343    /// Read a named option from the ``Context`` and parse it as an344    /// enum type ``T``.345    ///346    /// Reads the option with the check-local name \p LocalName from the347    /// ``CheckOptions``. If the corresponding key is not present,348    /// return \p Default.349    ///350    /// If the corresponding key can't be parsed as a ``T``, emit a351    /// diagnostic and return \p Default.352    ///353    /// \ref clang::tidy::OptionEnumMapping must be specialized for ``T`` to354    /// supply the mapping required to convert between ``T`` and a string.355    template <typename T>356    std::enable_if_t<std::is_enum_v<T>, T> get(StringRef LocalName,357                                               T Default) const {358      return get<T>(LocalName).value_or(Default);359    }360 361    /// Read a named option from the ``Context`` and parse it as an362    /// enum type ``T``.363    ///364    /// Reads the option with the check-local name \p LocalName from local or365    /// global ``CheckOptions``. Gets local option first. If local is not366    /// present, falls back to get global option. If global option is not367    /// present either, returns ``std::nullopt``.368    ///369    /// If the corresponding key can't be parsed as a ``T``, emit a370    /// diagnostic and return ``std::nullopt``.371    ///372    /// \ref clang::tidy::OptionEnumMapping must be specialized for ``T`` to373    /// supply the mapping required to convert between ``T`` and a string.374    template <typename T>375    std::enable_if_t<std::is_enum_v<T>, std::optional<T>>376    getLocalOrGlobal(StringRef LocalName) const {377      if (std::optional<int64_t> ValueOr =378              getEnumInt(LocalName, typeEraseMapping<T>(), true))379        return static_cast<T>(*ValueOr);380      return std::nullopt;381    }382 383    /// Read a named option from the ``Context`` and parse it as an384    /// enum type ``T``.385    ///386    /// Reads the option with the check-local name \p LocalName from local or387    /// global ``CheckOptions``. Gets local option first. If local is not388    /// present, falls back to get global option. If global option is not389    /// present either return \p Default.390    ///391    /// If the corresponding key can't be parsed as a ``T``, emit a392    /// diagnostic and return \p Default.393    ///394    /// \ref clang::tidy::OptionEnumMapping must be specialized for ``T`` to395    /// supply the mapping required to convert between ``T`` and a string.396    template <typename T>397    std::enable_if_t<std::is_enum_v<T>, T> getLocalOrGlobal(StringRef LocalName,398                                                            T Default) const {399      return getLocalOrGlobal<T>(LocalName).value_or(Default);400    }401 402    /// Stores an option with the check-local name \p LocalName with403    /// string value \p Value to \p Options.404    void store(ClangTidyOptions::OptionMap &Options, StringRef LocalName,405               StringRef Value) const;406 407    /// Stores an option with the check-local name \p LocalName with408    /// integer value \p Value to \p Options.409    template <typename T>410    std::enable_if_t<std::is_integral_v<T>>411    store(ClangTidyOptions::OptionMap &Options, StringRef LocalName,412          T Value) const {413      if constexpr (std::is_signed_v<T>)414        storeInt(Options, LocalName, Value);415      else416        storeUnsigned(Options, LocalName, Value);417    }418 419    /// Stores an option with the check-local name \p LocalName with420    /// integer value \p Value to \p Options. If the value is empty421    /// stores ``422    template <typename T>423    std::enable_if_t<std::is_integral_v<T>>424    store(ClangTidyOptions::OptionMap &Options, StringRef LocalName,425          std::optional<T> Value) const {426      if (Value)427        store(Options, LocalName, *Value);428      else429        store(Options, LocalName, "none");430    }431 432    /// Stores an option with the check-local name \p LocalName as the string433    /// representation of the Enum \p Value to \p Options.434    ///435    /// \ref clang::tidy::OptionEnumMapping must be specialized for ``T`` to436    /// supply the mapping required to convert between ``T`` and a string.437    template <typename T>438    std::enable_if_t<std::is_enum_v<T>>439    store(ClangTidyOptions::OptionMap &Options, StringRef LocalName,440          T Value) const {441      ArrayRef<std::pair<T, StringRef>> Mapping =442          OptionEnumMapping<T>::getEnumMapping();443      auto Iter = llvm::find_if(444          Mapping, [&](const std::pair<T, StringRef> &NameAndEnum) {445            return NameAndEnum.first == Value;446          });447      assert(Iter != Mapping.end() && "Unknown Case Value");448      store(Options, LocalName, Iter->second);449    }450 451  private:452    using NameAndValue = std::pair<int64_t, StringRef>;453 454    std::optional<int64_t> getEnumInt(StringRef LocalName,455                                      ArrayRef<NameAndValue> Mapping,456                                      bool CheckGlobal) const;457 458    template <typename T>459    std::enable_if_t<std::is_enum_v<T>, std::vector<NameAndValue>>460    typeEraseMapping() const {461      const ArrayRef<std::pair<T, StringRef>> Mapping =462          OptionEnumMapping<T>::getEnumMapping();463      std::vector<NameAndValue> Result;464      Result.reserve(Mapping.size());465      for (auto &MappedItem : Mapping) {466        Result.emplace_back(static_cast<int64_t>(MappedItem.first),467                            MappedItem.second);468      }469      return Result;470    }471 472    void storeInt(ClangTidyOptions::OptionMap &Options, StringRef LocalName,473                  int64_t Value) const;474 475    void storeUnsigned(ClangTidyOptions::OptionMap &Options,476                       StringRef LocalName, uint64_t Value) const;477 478    std::string NamePrefix;479    const ClangTidyOptions::OptionMap &CheckOptions;480    ClangTidyContext *Context;481  };482 483private:484  void run(const ast_matchers::MatchFinder::MatchResult &Result) override;485  std::string CheckName;486  ClangTidyContext *Context;487 488protected:489  OptionsView Options;490  /// Returns the main file name of the current translation unit.491  StringRef getCurrentMainFile() const { return Context->getCurrentFile(); }492  /// Returns the language options from the context.493  const LangOptions &getLangOpts() const { return Context->getLangOpts(); }494  /// Returns true when the check is run in a use case when only 1 fix will be495  /// applied at a time.496  bool areDiagsSelfContained() const {497    return Context->areDiagsSelfContained();498  }499  StringRef getID() const override { return CheckName; }500};501 502/// Read a named option from the ``Context`` and parse it as a bool.503///504/// Reads the option with the check-local name \p LocalName from the505/// ``CheckOptions``. If the corresponding key is not present, return506/// ``std::nullopt``.507///508/// If the corresponding key can't be parsed as a bool, emit a509/// diagnostic and return ``std::nullopt``.510template <>511std::optional<bool>512ClangTidyCheck::OptionsView::get<bool>(StringRef LocalName) const;513 514/// Read a named option from the ``Context`` and parse it as a bool.515///516/// Reads the option with the check-local name \p LocalName from the517/// ``CheckOptions``. If the corresponding key is not present, return518/// \p Default.519///520/// If the corresponding key can't be parsed as a bool, emit a521/// diagnostic and return \p Default.522template <>523std::optional<bool>524ClangTidyCheck::OptionsView::getLocalOrGlobal<bool>(StringRef LocalName) const;525 526/// Stores an option with the check-local name \p LocalName with527/// bool value \p Value to \p Options.528template <>529void ClangTidyCheck::OptionsView::store<bool>(530    ClangTidyOptions::OptionMap &Options, StringRef LocalName,531    bool Value) const;532 533} // namespace tidy534} // namespace clang535 536#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CLANGTIDYCHECK_H537