Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2026-05-10 08:36:22

0001 //===--- ClangTidyCheck.h - clang-tidy --------------------------*- C++ -*-===//
0002 //
0003 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
0004 // See https://llvm.org/LICENSE.txt for license information.
0005 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
0006 //
0007 //===----------------------------------------------------------------------===//
0008 
0009 #ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CLANGTIDYCHECK_H
0010 #define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CLANGTIDYCHECK_H
0011 
0012 #include "ClangTidyDiagnosticConsumer.h"
0013 #include "ClangTidyOptions.h"
0014 #include "clang/ASTMatchers/ASTMatchFinder.h"
0015 #include "clang/Basic/Diagnostic.h"
0016 #include <optional>
0017 #include <type_traits>
0018 #include <utility>
0019 #include <vector>
0020 
0021 namespace clang {
0022 
0023 class SourceManager;
0024 
0025 namespace tidy {
0026 
0027 /// This class should be specialized by any enum type that needs to be converted
0028 /// to and from an \ref llvm::StringRef.
0029 template <class T> struct OptionEnumMapping {
0030   // Specializations of this struct must implement this function.
0031   static ArrayRef<std::pair<T, StringRef>> getEnumMapping() = delete;
0032 };
0033 
0034 /// Base class for all clang-tidy checks.
0035 ///
0036 /// To implement a ``ClangTidyCheck``, write a subclass and override some of the
0037 /// base class's methods. E.g. to implement a check that validates namespace
0038 /// declarations, override ``registerMatchers``:
0039 ///
0040 /// ~~~{.cpp}
0041 /// void registerMatchers(ast_matchers::MatchFinder *Finder) override {
0042 ///   Finder->addMatcher(namespaceDecl().bind("namespace"), this);
0043 /// }
0044 /// ~~~
0045 ///
0046 /// and then override ``check(const MatchResult &Result)`` to do the actual
0047 /// check for each match.
0048 ///
0049 /// A new ``ClangTidyCheck`` instance is created per translation unit.
0050 ///
0051 /// FIXME: Figure out whether carrying information from one TU to another is
0052 /// useful/necessary.
0053 class ClangTidyCheck : public ast_matchers::MatchFinder::MatchCallback {
0054 public:
0055   /// Initializes the check with \p CheckName and \p Context.
0056   ///
0057   /// Derived classes must implement the constructor with this signature or
0058   /// delegate it. If a check needs to read options, it can do this in the
0059   /// constructor using the Options.get() methods below.
0060   ClangTidyCheck(StringRef CheckName, ClangTidyContext *Context);
0061 
0062   /// Override this to disable registering matchers and PP callbacks if an
0063   /// invalid language version is being used.
0064   ///
0065   /// For example if a check is examining overloaded functions then this should
0066   /// be overridden to return false when the CPlusPlus flag is not set in
0067   /// \p LangOpts.
0068   virtual bool isLanguageVersionSupported(const LangOptions &LangOpts) const {
0069     return true;
0070   }
0071 
0072   /// Override this to register ``PPCallbacks`` in the preprocessor.
0073   ///
0074   /// This should be used for clang-tidy checks that analyze preprocessor-
0075   /// dependent properties, e.g. include directives and macro definitions.
0076   ///
0077   /// This will only be executed if the function isLanguageVersionSupported
0078   /// returns true.
0079   ///
0080   /// There are two Preprocessors to choose from that differ in how they handle
0081   /// modular #includes:
0082   ///  - PP is the real Preprocessor. It doesn't walk into modular #includes and
0083   ///    thus doesn't generate PPCallbacks for their contents.
0084   ///  - ModuleExpanderPP preprocesses the whole translation unit in the
0085   ///    non-modular mode, which allows it to generate PPCallbacks not only for
0086   ///    the main file and textual headers, but also for all transitively
0087   ///    included modular headers when the analysis runs with modules enabled.
0088   ///    When modules are not enabled ModuleExpanderPP just points to the real
0089   ///    preprocessor.
0090   virtual void registerPPCallbacks(const SourceManager &SM, Preprocessor *PP,
0091                                    Preprocessor *ModuleExpanderPP) {}
0092 
0093   /// Override this to register AST matchers with \p Finder.
0094   ///
0095   /// This should be used by clang-tidy checks that analyze code properties that
0096   /// dependent on AST knowledge.
0097   ///
0098   /// You can register as many matchers as necessary with \p Finder. Usually,
0099   /// "this" will be used as callback, but you can also specify other callback
0100   /// classes. Thereby, different matchers can trigger different callbacks.
0101   ///
0102   /// This will only be executed if the function isLanguageVersionSupported
0103   /// returns true.
0104   ///
0105   /// If you need to merge information between the different matchers, you can
0106   /// store these as members of the derived class. However, note that all
0107   /// matches occur in the order of the AST traversal.
0108   virtual void registerMatchers(ast_matchers::MatchFinder *Finder) {}
0109 
0110   /// ``ClangTidyChecks`` that register ASTMatchers should do the actual
0111   /// work in here.
0112   virtual void check(const ast_matchers::MatchFinder::MatchResult &Result) {}
0113 
0114   /// Add a diagnostic with the check's name.
0115   DiagnosticBuilder diag(SourceLocation Loc, StringRef Description,
0116                          DiagnosticIDs::Level Level = DiagnosticIDs::Warning);
0117 
0118   /// Add a diagnostic with the check's name.
0119   DiagnosticBuilder diag(StringRef Description,
0120                          DiagnosticIDs::Level Level = DiagnosticIDs::Warning);
0121 
0122   /// Adds a diagnostic to report errors in the check's configuration.
0123   DiagnosticBuilder
0124   configurationDiag(StringRef Description,
0125                     DiagnosticIDs::Level Level = DiagnosticIDs::Warning) const;
0126 
0127   /// Should store all options supported by this check with their
0128   /// current values or default values for options that haven't been overridden.
0129   ///
0130   /// The check should use ``Options.store()`` to store each option it supports
0131   /// whether it has the default value or it has been overridden.
0132   virtual void storeOptions(ClangTidyOptions::OptionMap &Options) {}
0133 
0134   /// Provides access to the ``ClangTidyCheck`` options via check-local
0135   /// names.
0136   ///
0137   /// Methods of this class prepend ``CheckName + "."`` to translate check-local
0138   /// option names to global option names.
0139   class OptionsView {
0140     void diagnoseBadIntegerOption(const Twine &Lookup,
0141                                   StringRef Unparsed) const;
0142     void diagnoseBadBooleanOption(const Twine &Lookup,
0143                                   StringRef Unparsed) const;
0144     void diagnoseBadEnumOption(const Twine &Lookup, StringRef Unparsed,
0145                                StringRef Suggestion = StringRef()) const;
0146 
0147   public:
0148     /// Initializes the instance using \p CheckName + "." as a prefix.
0149     OptionsView(StringRef CheckName,
0150                 const ClangTidyOptions::OptionMap &CheckOptions,
0151                 ClangTidyContext *Context);
0152 
0153     /// Read a named option from the ``Context``.
0154     ///
0155     /// Reads the option with the check-local name \p LocalName from the
0156     /// ``CheckOptions``. If the corresponding key is not present, return
0157     /// ``std::nullopt``.
0158     std::optional<StringRef> get(StringRef LocalName) const;
0159 
0160     /// Read a named option from the ``Context``.
0161     ///
0162     /// Reads the option with the check-local name \p LocalName from the
0163     /// ``CheckOptions``. If the corresponding key is not present, returns
0164     /// \p Default.
0165     StringRef get(StringRef LocalName, StringRef Default) const;
0166 
0167     /// Read a named option from the ``Context``.
0168     ///
0169     /// Reads the option with the check-local name \p LocalName from local or
0170     /// global ``CheckOptions``. Gets local option first. If local is not
0171     /// present, falls back to get global option. If global option is not
0172     /// present either, return ``std::nullopt``.
0173     std::optional<StringRef> getLocalOrGlobal(StringRef LocalName) const;
0174 
0175     /// Read a named option from the ``Context``.
0176     ///
0177     /// Reads the option with the check-local name \p LocalName from local or
0178     /// global ``CheckOptions``. Gets local option first. If local is not
0179     /// present, falls back to get global option. If global option is not
0180     /// present either, returns \p Default.
0181     StringRef getLocalOrGlobal(StringRef LocalName, StringRef Default) const;
0182 
0183     /// Read a named option from the ``Context`` and parse it as an
0184     /// integral type ``T``.
0185     ///
0186     /// Reads the option with the check-local name \p LocalName from the
0187     /// ``CheckOptions``. If the corresponding key is not present,
0188     ///  return ``std::nullopt``.
0189     ///
0190     /// If the corresponding key can't be parsed as a ``T``, emit a
0191     /// diagnostic and return ``std::nullopt``.
0192     template <typename T>
0193     std::enable_if_t<std::is_integral_v<T>, std::optional<T>>
0194     get(StringRef LocalName) const {
0195       if (std::optional<StringRef> Value = get(LocalName)) {
0196         T Result{};
0197         if (!StringRef(*Value).getAsInteger(10, Result))
0198           return Result;
0199         diagnoseBadIntegerOption(NamePrefix + LocalName, *Value);
0200       }
0201       return std::nullopt;
0202     }
0203 
0204     /// Read a named option from the ``Context`` and parse it as an
0205     /// integral type ``T``.
0206     ///
0207     /// Reads the option with the check-local name \p LocalName from the
0208     /// ``CheckOptions``. If the corresponding key is `none`, `null`,
0209     /// `-1` or empty, return ``std::nullopt``. If the corresponding
0210     /// key is not present, return \p Default.
0211     ///
0212     /// If the corresponding key can't be parsed as a ``T``, emit a
0213     /// diagnostic and return \p Default.
0214     template <typename T>
0215     std::enable_if_t<std::is_integral_v<T>, std::optional<T>>
0216     get(StringRef LocalName, std::optional<T> Default) const {
0217       if (std::optional<StringRef> Value = get(LocalName)) {
0218         if (Value == "" || Value == "none" || Value == "null" ||
0219             (std::is_unsigned_v<T> && Value == "-1"))
0220           return std::nullopt;
0221         T Result{};
0222         if (!StringRef(*Value).getAsInteger(10, Result))
0223           return Result;
0224         diagnoseBadIntegerOption(NamePrefix + LocalName, *Value);
0225       }
0226       return Default;
0227     }
0228 
0229     /// Read a named option from the ``Context`` and parse it as an
0230     /// integral type ``T``.
0231     ///
0232     /// Reads the option with the check-local name \p LocalName from the
0233     /// ``CheckOptions``. If the corresponding key is not present, return
0234     /// \p Default.
0235     ///
0236     /// If the corresponding key can't be parsed as a ``T``, emit a
0237     /// diagnostic and return \p Default.
0238     template <typename T>
0239     std::enable_if_t<std::is_integral_v<T>, T> get(StringRef LocalName,
0240                                                    T Default) const {
0241       return get<T>(LocalName).value_or(Default);
0242     }
0243 
0244     /// Read a named option from the ``Context`` and parse it as an
0245     /// integral type ``T``.
0246     ///
0247     /// Reads the option with the check-local name \p LocalName from local or
0248     /// global ``CheckOptions``. Gets local option first. If local is not
0249     /// present, falls back to get global option. If global option is not
0250     /// present either, return ``std::nullopt``.
0251     ///
0252     /// If the corresponding key can't be parsed as a ``T``, emit a
0253     /// diagnostic and return ``std::nullopt``.
0254     template <typename T>
0255     std::enable_if_t<std::is_integral_v<T>, std::optional<T>>
0256     getLocalOrGlobal(StringRef LocalName) const {
0257       std::optional<StringRef> ValueOr = get(LocalName);
0258       bool IsGlobal = false;
0259       if (!ValueOr) {
0260         IsGlobal = true;
0261         ValueOr = getLocalOrGlobal(LocalName);
0262         if (!ValueOr)
0263           return std::nullopt;
0264       }
0265       T Result{};
0266       if (!StringRef(*ValueOr).getAsInteger(10, Result))
0267         return Result;
0268       diagnoseBadIntegerOption(
0269           IsGlobal ? Twine(LocalName) : NamePrefix + LocalName, *ValueOr);
0270       return std::nullopt;
0271     }
0272 
0273     /// Read a named option from the ``Context`` and parse it as an
0274     /// integral type ``T``.
0275     ///
0276     /// Reads the option with the check-local name \p LocalName from local or
0277     /// global ``CheckOptions``. Gets local option first. If local is not
0278     /// present, falls back to get global option. If global option is not
0279     /// present either, return \p Default. If the value value was found
0280     /// and equals ``none``, ``null``, ``-1`` or empty, return ``std::nullopt``.
0281     ///
0282     /// If the corresponding key can't be parsed as a ``T``, emit a
0283     /// diagnostic and return \p Default.
0284     template <typename T>
0285     std::enable_if_t<std::is_integral_v<T>, std::optional<T>>
0286     getLocalOrGlobal(StringRef LocalName, std::optional<T> Default) const {
0287       std::optional<StringRef> ValueOr = get(LocalName);
0288       bool IsGlobal = false;
0289       if (!ValueOr) {
0290         IsGlobal = true;
0291         ValueOr = getLocalOrGlobal(LocalName);
0292         if (!ValueOr)
0293           return Default;
0294       }
0295       T Result{};
0296       if (ValueOr == "" || ValueOr == "none" || ValueOr == "null" ||
0297           (std::is_unsigned_v<T> && ValueOr == "-1"))
0298         return std::nullopt;
0299       if (!StringRef(*ValueOr).getAsInteger(10, Result))
0300         return Result;
0301       diagnoseBadIntegerOption(
0302           IsGlobal ? Twine(LocalName) : NamePrefix + LocalName, *ValueOr);
0303       return Default;
0304     }
0305 
0306     /// Read a named option from the ``Context`` and parse it as an
0307     /// integral type ``T``.
0308     ///
0309     /// Reads the option with the check-local name \p LocalName from local or
0310     /// global ``CheckOptions``. Gets local option first. If local is not
0311     /// present, falls back to get global option. If global option is not
0312     /// present either, return \p Default.
0313     ///
0314     /// If the corresponding key can't be parsed as a ``T``, emit a
0315     /// diagnostic and return \p Default.
0316     template <typename T>
0317     std::enable_if_t<std::is_integral_v<T>, T>
0318     getLocalOrGlobal(StringRef LocalName, T Default) const {
0319       return getLocalOrGlobal<T>(LocalName).value_or(Default);
0320     }
0321 
0322     /// Read a named option from the ``Context`` and parse it as an
0323     /// enum type ``T``.
0324     ///
0325     /// Reads the option with the check-local name \p LocalName from the
0326     /// ``CheckOptions``. If the corresponding key is not present, return
0327     /// ``std::nullopt``.
0328     ///
0329     /// If the corresponding key can't be parsed as a ``T``, emit a
0330     /// diagnostic and return ``std::nullopt``.
0331     ///
0332     /// \ref clang::tidy::OptionEnumMapping must be specialized for ``T`` to
0333     /// supply the mapping required to convert between ``T`` and a string.
0334     template <typename T>
0335     std::enable_if_t<std::is_enum_v<T>, std::optional<T>>
0336     get(StringRef LocalName) const {
0337       if (std::optional<int64_t> ValueOr =
0338               getEnumInt(LocalName, typeEraseMapping<T>(), false))
0339         return static_cast<T>(*ValueOr);
0340       return std::nullopt;
0341     }
0342 
0343     /// Read a named option from the ``Context`` and parse it as an
0344     /// enum type ``T``.
0345     ///
0346     /// Reads the option with the check-local name \p LocalName from the
0347     /// ``CheckOptions``. If the corresponding key is not present,
0348     /// return \p Default.
0349     ///
0350     /// If the corresponding key can't be parsed as a ``T``, emit a
0351     /// diagnostic and return \p Default.
0352     ///
0353     /// \ref clang::tidy::OptionEnumMapping must be specialized for ``T`` to
0354     /// supply the mapping required to convert between ``T`` and a string.
0355     template <typename T>
0356     std::enable_if_t<std::is_enum_v<T>, T> get(StringRef LocalName,
0357                                                T Default) const {
0358       return get<T>(LocalName).value_or(Default);
0359     }
0360 
0361     /// Read a named option from the ``Context`` and parse it as an
0362     /// enum type ``T``.
0363     ///
0364     /// Reads the option with the check-local name \p LocalName from local or
0365     /// global ``CheckOptions``. Gets local option first. If local is not
0366     /// present, falls back to get global option. If global option is not
0367     /// present either, returns ``std::nullopt``.
0368     ///
0369     /// If the corresponding key can't be parsed as a ``T``, emit a
0370     /// diagnostic and return ``std::nullopt``.
0371     ///
0372     /// \ref clang::tidy::OptionEnumMapping must be specialized for ``T`` to
0373     /// supply the mapping required to convert between ``T`` and a string.
0374     template <typename T>
0375     std::enable_if_t<std::is_enum_v<T>, std::optional<T>>
0376     getLocalOrGlobal(StringRef LocalName) const {
0377       if (std::optional<int64_t> ValueOr =
0378               getEnumInt(LocalName, typeEraseMapping<T>(), true))
0379         return static_cast<T>(*ValueOr);
0380       return std::nullopt;
0381     }
0382 
0383     /// Read a named option from the ``Context`` and parse it as an
0384     /// enum type ``T``.
0385     ///
0386     /// Reads the option with the check-local name \p LocalName from local or
0387     /// global ``CheckOptions``. Gets local option first. If local is not
0388     /// present, falls back to get global option. If global option is not
0389     /// present either return \p Default.
0390     ///
0391     /// If the corresponding key can't be parsed as a ``T``, emit a
0392     /// diagnostic and return \p Default.
0393     ///
0394     /// \ref clang::tidy::OptionEnumMapping must be specialized for ``T`` to
0395     /// supply the mapping required to convert between ``T`` and a string.
0396     template <typename T>
0397     std::enable_if_t<std::is_enum_v<T>, T> getLocalOrGlobal(StringRef LocalName,
0398                                                             T Default) const {
0399       return getLocalOrGlobal<T>(LocalName).value_or(Default);
0400     }
0401 
0402     /// Stores an option with the check-local name \p LocalName with
0403     /// string value \p Value to \p Options.
0404     void store(ClangTidyOptions::OptionMap &Options, StringRef LocalName,
0405                StringRef Value) const;
0406 
0407     /// Stores an option with the check-local name \p LocalName with
0408     /// integer value \p Value to \p Options.
0409     template <typename T>
0410     std::enable_if_t<std::is_integral_v<T>>
0411     store(ClangTidyOptions::OptionMap &Options, StringRef LocalName,
0412           T Value) const {
0413       if constexpr (std::is_signed_v<T>)
0414         storeInt(Options, LocalName, Value);
0415       else
0416         storeUnsigned(Options, LocalName, Value);
0417     }
0418 
0419     /// Stores an option with the check-local name \p LocalName with
0420     /// integer value \p Value to \p Options. If the value is empty
0421     /// stores ``
0422     template <typename T>
0423     std::enable_if_t<std::is_integral_v<T>>
0424     store(ClangTidyOptions::OptionMap &Options, StringRef LocalName,
0425           std::optional<T> Value) const {
0426       if (Value)
0427         store(Options, LocalName, *Value);
0428       else
0429         store(Options, LocalName, "none");
0430     }
0431 
0432     /// Stores an option with the check-local name \p LocalName as the string
0433     /// representation of the Enum \p Value to \p Options.
0434     ///
0435     /// \ref clang::tidy::OptionEnumMapping must be specialized for ``T`` to
0436     /// supply the mapping required to convert between ``T`` and a string.
0437     template <typename T>
0438     std::enable_if_t<std::is_enum_v<T>>
0439     store(ClangTidyOptions::OptionMap &Options, StringRef LocalName,
0440           T Value) const {
0441       ArrayRef<std::pair<T, StringRef>> Mapping =
0442           OptionEnumMapping<T>::getEnumMapping();
0443       auto Iter = llvm::find_if(
0444           Mapping, [&](const std::pair<T, StringRef> &NameAndEnum) {
0445             return NameAndEnum.first == Value;
0446           });
0447       assert(Iter != Mapping.end() && "Unknown Case Value");
0448       store(Options, LocalName, Iter->second);
0449     }
0450 
0451   private:
0452     using NameAndValue = std::pair<int64_t, StringRef>;
0453 
0454     std::optional<int64_t> getEnumInt(StringRef LocalName,
0455                                       ArrayRef<NameAndValue> Mapping,
0456                                       bool CheckGlobal) const;
0457 
0458     template <typename T>
0459     std::enable_if_t<std::is_enum_v<T>, std::vector<NameAndValue>>
0460     typeEraseMapping() const {
0461       ArrayRef<std::pair<T, StringRef>> Mapping =
0462           OptionEnumMapping<T>::getEnumMapping();
0463       std::vector<NameAndValue> Result;
0464       Result.reserve(Mapping.size());
0465       for (auto &MappedItem : Mapping) {
0466         Result.emplace_back(static_cast<int64_t>(MappedItem.first),
0467                             MappedItem.second);
0468       }
0469       return Result;
0470     }
0471 
0472     void storeInt(ClangTidyOptions::OptionMap &Options, StringRef LocalName,
0473                   int64_t Value) const;
0474 
0475     void storeUnsigned(ClangTidyOptions::OptionMap &Options,
0476                        StringRef LocalName, uint64_t Value) const;
0477 
0478     std::string NamePrefix;
0479     const ClangTidyOptions::OptionMap &CheckOptions;
0480     ClangTidyContext *Context;
0481   };
0482 
0483 private:
0484   void run(const ast_matchers::MatchFinder::MatchResult &Result) override;
0485   std::string CheckName;
0486   ClangTidyContext *Context;
0487 
0488 protected:
0489   OptionsView Options;
0490   /// Returns the main file name of the current translation unit.
0491   StringRef getCurrentMainFile() const { return Context->getCurrentFile(); }
0492   /// Returns the language options from the context.
0493   const LangOptions &getLangOpts() const { return Context->getLangOpts(); }
0494   /// Returns true when the check is run in a use case when only 1 fix will be
0495   /// applied at a time.
0496   bool areDiagsSelfContained() const {
0497     return Context->areDiagsSelfContained();
0498   }
0499   StringRef getID() const override { return CheckName; }
0500 };
0501 
0502 /// Read a named option from the ``Context`` and parse it as a bool.
0503 ///
0504 /// Reads the option with the check-local name \p LocalName from the
0505 /// ``CheckOptions``. If the corresponding key is not present, return
0506 /// ``std::nullopt``.
0507 ///
0508 /// If the corresponding key can't be parsed as a bool, emit a
0509 /// diagnostic and return ``std::nullopt``.
0510 template <>
0511 std::optional<bool>
0512 ClangTidyCheck::OptionsView::get<bool>(StringRef LocalName) const;
0513 
0514 /// Read a named option from the ``Context`` and parse it as a bool.
0515 ///
0516 /// Reads the option with the check-local name \p LocalName from the
0517 /// ``CheckOptions``. If the corresponding key is not present, return
0518 /// \p Default.
0519 ///
0520 /// If the corresponding key can't be parsed as a bool, emit a
0521 /// diagnostic and return \p Default.
0522 template <>
0523 std::optional<bool>
0524 ClangTidyCheck::OptionsView::getLocalOrGlobal<bool>(StringRef LocalName) const;
0525 
0526 /// Stores an option with the check-local name \p LocalName with
0527 /// bool value \p Value to \p Options.
0528 template <>
0529 void ClangTidyCheck::OptionsView::store<bool>(
0530     ClangTidyOptions::OptionMap &Options, StringRef LocalName,
0531     bool Value) const;
0532 
0533 
0534 } // namespace tidy
0535 } // namespace clang
0536 
0537 #endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CLANGTIDYCHECK_H