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