1//===--- ClangTidyCheck.h - clang-tidy --------------------------*- C++ -*-===//
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-exception
6//
7//===----------------------------------------------------------------------===//
8
9#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CLANGTIDYCHECK_H
10#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CLANGTIDYCHECK_H
11
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 converted
28/// 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 the
37/// base class's methods. E.g. to implement a check that validates namespace
38/// 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 actual
47/// 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 is
52/// 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 or
58 /// delegate it. If a check needs to read options, it can do this in the
59 /// 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 an
63 /// invalid language version is being used.
64 ///
65 /// For example if a check is examining overloaded functions then this should
66 /// be overridden to return false when the CPlusPlus flag is not set in
67 /// \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 isLanguageVersionSupported
78 /// returns true.
79 ///
80 /// There are two Preprocessors to choose from that differ in how they handle
81 /// modular #includes:
82 /// - PP is the real Preprocessor. It doesn't walk into modular #includes and
83 /// thus doesn't generate PPCallbacks for their contents.
84 /// - ModuleExpanderPP preprocesses the whole translation unit in the
85 /// non-modular mode, which allows it to generate PPCallbacks not only for
86 /// the main file and textual headers, but also for all transitively
87 /// included modular headers when the analysis runs with modules enabled.
88 /// When modules are not enabled ModuleExpanderPP just points to the real
89 /// 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 that
96 /// 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 callback
100 /// classes. Thereby, different matchers can trigger different callbacks.
101 ///
102 /// This will only be executed if the function isLanguageVersionSupported
103 /// returns true.
104 ///
105 /// If you need to merge information between the different matchers, you can
106 /// store these as members of the derived class. However, note that all
107 /// 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 actual
111 /// 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 DiagnosticBuilder
124 configurationDiag(StringRef Description,
125 DiagnosticIDs::Level Level = DiagnosticIDs::Warning) const;
126
127 /// Should store all options supported by this check with their
128 /// 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 supports
131 /// 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-local
135 /// names.
136 ///
137 /// Methods of this class prepend ``CheckName + "."`` to translate check-local
138 /// 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 the
156 /// ``CheckOptions``. If the corresponding key is not present, return
157 /// ``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 the
163 /// ``CheckOptions``. If the corresponding key is not present, returns
164 /// \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 or
170 /// global ``CheckOptions``. Gets local option first. If local is not
171 /// present, falls back to get global option. If global option is not
172 /// 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 or
178 /// global ``CheckOptions``. Gets local option first. If local is not
179 /// present, falls back to get global option. If global option is not
180 /// 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 an
184 /// integral type ``T``.
185 ///
186 /// Reads the option with the check-local name \p LocalName from the
187 /// ``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 a
191 /// 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(Lookup: NamePrefix + LocalName, Unparsed: *Value);
200 }
201 return std::nullopt;
202 }
203
204 /// Read a named option from the ``Context`` and parse it as an
205 /// integral type ``T``.
206 ///
207 /// Reads the option with the check-local name \p LocalName from the
208 /// ``CheckOptions``. If the corresponding key is `none`, `null`,
209 /// `-1` or empty, return ``std::nullopt``. If the corresponding
210 /// key is not present, return \p Default.
211 ///
212 /// If the corresponding key can't be parsed as a ``T``, emit a
213 /// 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(Lookup: NamePrefix + LocalName, Unparsed: *Value);
225 }
226 return Default;
227 }
228
229 /// Read a named option from the ``Context`` and parse it as an
230 /// integral type ``T``.
231 ///
232 /// Reads the option with the check-local name \p LocalName from the
233 /// ``CheckOptions``. If the corresponding key is not present, return
234 /// \p Default.
235 ///
236 /// If the corresponding key can't be parsed as a ``T``, emit a
237 /// 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 an
245 /// integral type ``T``.
246 ///
247 /// Reads the option with the check-local name \p LocalName from local or
248 /// global ``CheckOptions``. Gets local option first. If local is not
249 /// present, falls back to get global option. If global option is not
250 /// present either, return ``std::nullopt``.
251 ///
252 /// If the corresponding key can't be parsed as a ``T``, emit a
253 /// 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 Lookup: IsGlobal ? Twine(LocalName) : NamePrefix + LocalName, Unparsed: *ValueOr);
270 return std::nullopt;
271 }
272
273 /// Read a named option from the ``Context`` and parse it as an
274 /// integral type ``T``.
275 ///
276 /// Reads the option with the check-local name \p LocalName from local or
277 /// global ``CheckOptions``. Gets local option first. If local is not
278 /// present, falls back to get global option. If global option is not
279 /// present either, return \p Default. If the value value was found
280 /// and equals ``none``, ``null``, ``-1`` or empty, return ``std::nullopt``.
281 ///
282 /// If the corresponding key can't be parsed as a ``T``, emit a
283 /// 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 Lookup: IsGlobal ? Twine(LocalName) : NamePrefix + LocalName, Unparsed: *ValueOr);
303 return Default;
304 }
305
306 /// Read a named option from the ``Context`` and parse it as an
307 /// integral type ``T``.
308 ///
309 /// Reads the option with the check-local name \p LocalName from local or
310 /// global ``CheckOptions``. Gets local option first. If local is not
311 /// present, falls back to get global option. If global option is not
312 /// present either, return \p Default.
313 ///
314 /// If the corresponding key can't be parsed as a ``T``, emit a
315 /// 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 an
323 /// enum type ``T``.
324 ///
325 /// Reads the option with the check-local name \p LocalName from the
326 /// ``CheckOptions``. If the corresponding key is not present, return
327 /// ``std::nullopt``.
328 ///
329 /// If the corresponding key can't be parsed as a ``T``, emit a
330 /// diagnostic and return ``std::nullopt``.
331 ///
332 /// \ref clang::tidy::OptionEnumMapping must be specialized for ``T`` to
333 /// 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, bool IgnoreCase = false) const {
337 if (std::optional<int64_t> ValueOr =
338 getEnumInt(LocalName, Mapping: typeEraseMapping<T>(), CheckGlobal: false, IgnoreCase))
339 return static_cast<T>(*ValueOr);
340 return std::nullopt;
341 }
342
343 /// Read a named option from the ``Context`` and parse it as an
344 /// enum type ``T``.
345 ///
346 /// Reads the option with the check-local name \p LocalName from the
347 /// ``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 a
351 /// diagnostic and return \p Default.
352 ///
353 /// \ref clang::tidy::OptionEnumMapping must be specialized for ``T`` to
354 /// 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, T Default,
357 bool IgnoreCase = false) const {
358 return get<T>(LocalName, IgnoreCase).value_or(Default);
359 }
360
361 /// Read a named option from the ``Context`` and parse it as an
362 /// enum type ``T``.
363 ///
364 /// Reads the option with the check-local name \p LocalName from local or
365 /// global ``CheckOptions``. Gets local option first. If local is not
366 /// present, falls back to get global option. If global option is not
367 /// present either, returns ``std::nullopt``.
368 ///
369 /// If the corresponding key can't be parsed as a ``T``, emit a
370 /// diagnostic and return ``std::nullopt``.
371 ///
372 /// \ref clang::tidy::OptionEnumMapping must be specialized for ``T`` to
373 /// 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, bool IgnoreCase = false) const {
377 if (std::optional<int64_t> ValueOr =
378 getEnumInt(LocalName, Mapping: typeEraseMapping<T>(), CheckGlobal: true, IgnoreCase))
379 return static_cast<T>(*ValueOr);
380 return std::nullopt;
381 }
382
383 /// Read a named option from the ``Context`` and parse it as an
384 /// enum type ``T``.
385 ///
386 /// Reads the option with the check-local name \p LocalName from local or
387 /// global ``CheckOptions``. Gets local option first. If local is not
388 /// present, falls back to get global option. If global option is not
389 /// present either return \p Default.
390 ///
391 /// If the corresponding key can't be parsed as a ``T``, emit a
392 /// diagnostic and return \p Default.
393 ///
394 /// \ref clang::tidy::OptionEnumMapping must be specialized for ``T`` to
395 /// 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>
398 getLocalOrGlobal(StringRef LocalName, T Default,
399 bool IgnoreCase = false) const {
400 return getLocalOrGlobal<T>(LocalName, IgnoreCase).value_or(Default);
401 }
402
403 /// Stores an option with the check-local name \p LocalName with
404 /// string value \p Value to \p Options.
405 void store(ClangTidyOptions::OptionMap &Options, StringRef LocalName,
406 StringRef Value) const;
407
408 /// Stores an option with the check-local name \p LocalName with
409 /// integer value \p Value to \p Options.
410 template <typename T>
411 std::enable_if_t<std::is_integral_v<T>>
412 store(ClangTidyOptions::OptionMap &Options, StringRef LocalName,
413 T Value) const {
414 if constexpr (std::is_signed_v<T>)
415 storeInt(Options, LocalName, Value);
416 else
417 storeUnsigned(Options, LocalName, Value);
418 }
419
420 /// Stores an option with the check-local name \p LocalName with
421 /// integer value \p Value to \p Options. If the value is empty
422 /// stores ``
423 template <typename T>
424 std::enable_if_t<std::is_integral_v<T>>
425 store(ClangTidyOptions::OptionMap &Options, StringRef LocalName,
426 std::optional<T> Value) const {
427 if (Value)
428 store(Options, LocalName, *Value);
429 else
430 store(Options, LocalName, Value: "none");
431 }
432
433 /// Stores an option with the check-local name \p LocalName as the string
434 /// representation of the Enum \p Value to \p Options.
435 ///
436 /// \ref clang::tidy::OptionEnumMapping must be specialized for ``T`` to
437 /// supply the mapping required to convert between ``T`` and a string.
438 template <typename T>
439 std::enable_if_t<std::is_enum_v<T>>
440 store(ClangTidyOptions::OptionMap &Options, StringRef LocalName,
441 T Value) const {
442 ArrayRef<std::pair<T, StringRef>> Mapping =
443 OptionEnumMapping<T>::getEnumMapping();
444 auto Iter = llvm::find_if(
445 Mapping, [&](const std::pair<T, StringRef> &NameAndEnum) {
446 return NameAndEnum.first == Value;
447 });
448 assert(Iter != Mapping.end() && "Unknown Case Value");
449 store(Options, LocalName, Iter->second);
450 }
451
452 private:
453 using NameAndValue = std::pair<int64_t, StringRef>;
454
455 std::optional<int64_t> getEnumInt(StringRef LocalName,
456 ArrayRef<NameAndValue> Mapping,
457 bool CheckGlobal, bool IgnoreCase) const;
458
459 template <typename T>
460 std::enable_if_t<std::is_enum_v<T>, std::vector<NameAndValue>>
461 typeEraseMapping() const {
462 ArrayRef<std::pair<T, StringRef>> Mapping =
463 OptionEnumMapping<T>::getEnumMapping();
464 std::vector<NameAndValue> Result;
465 Result.reserve(n: Mapping.size());
466 for (auto &MappedItem : Mapping) {
467 Result.emplace_back(static_cast<int64_t>(MappedItem.first),
468 MappedItem.second);
469 }
470 return Result;
471 }
472
473 void storeInt(ClangTidyOptions::OptionMap &Options, StringRef LocalName,
474 int64_t Value) const;
475
476 void storeUnsigned(ClangTidyOptions::OptionMap &Options,
477 StringRef LocalName, uint64_t Value) const;
478
479 std::string NamePrefix;
480 const ClangTidyOptions::OptionMap &CheckOptions;
481 ClangTidyContext *Context;
482 };
483
484private:
485 void run(const ast_matchers::MatchFinder::MatchResult &Result) override;
486 std::string CheckName;
487 ClangTidyContext *Context;
488
489protected:
490 OptionsView Options;
491 /// Returns the main file name of the current translation unit.
492 StringRef getCurrentMainFile() const { return Context->getCurrentFile(); }
493 /// Returns the language options from the context.
494 const LangOptions &getLangOpts() const { return Context->getLangOpts(); }
495 /// Returns true when the check is run in a use case when only 1 fix will be
496 /// applied at a time.
497 bool areDiagsSelfContained() const {
498 return Context->areDiagsSelfContained();
499 }
500 StringRef getID() const override { return CheckName; }
501};
502
503/// Read a named option from the ``Context`` and parse it as a bool.
504///
505/// Reads the option with the check-local name \p LocalName from the
506/// ``CheckOptions``. If the corresponding key is not present, return
507/// ``std::nullopt``.
508///
509/// If the corresponding key can't be parsed as a bool, emit a
510/// diagnostic and return ``std::nullopt``.
511template <>
512std::optional<bool>
513ClangTidyCheck::OptionsView::get<bool>(StringRef LocalName) const;
514
515/// Read a named option from the ``Context`` and parse it as a bool.
516///
517/// Reads the option with the check-local name \p LocalName from the
518/// ``CheckOptions``. If the corresponding key is not present, return
519/// \p Default.
520///
521/// If the corresponding key can't be parsed as a bool, emit a
522/// diagnostic and return \p Default.
523template <>
524std::optional<bool>
525ClangTidyCheck::OptionsView::getLocalOrGlobal<bool>(StringRef LocalName) const;
526
527/// Stores an option with the check-local name \p LocalName with
528/// bool value \p Value to \p Options.
529template <>
530void ClangTidyCheck::OptionsView::store<bool>(
531 ClangTidyOptions::OptionMap &Options, StringRef LocalName,
532 bool Value) const;
533
534
535} // namespace tidy
536} // namespace clang
537
538#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CLANGTIDYCHECK_H
539

source code of clang-tools-extra/clang-tidy/ClangTidyCheck.h