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 |
11 | |
12 | #include "ClangTidyDiagnosticConsumer.h" |
13 | #include "ClangTidyOptions.h" |
14 | #include "clang/ASTMatchers/ASTMatchFinder.h" |
15 | #include "clang/Basic/Diagnostic.h" |
16 | #include "llvm/ADT/Optional.h" |
17 | #include <type_traits> |
18 | #include <utility> |
19 | #include <vector> |
20 | |
21 | namespace clang { |
22 | |
23 | class CompilerInstance; |
24 | class SourceManager; |
25 | |
26 | namespace tidy { |
27 | |
28 | /// This class should be specialized by any enum type that needs to be converted |
29 | /// to and from an \ref llvm::StringRef. |
30 | template <class T> struct OptionEnumMapping { |
31 | // Specializations of this struct must implement this function. |
32 | static ArrayRef<std::pair<T, StringRef>> getEnumMapping() = delete; |
33 | }; |
34 | |
35 | /// Base class for all clang-tidy checks. |
36 | /// |
37 | /// To implement a ``ClangTidyCheck``, write a subclass and override some of the |
38 | /// base class's methods. E.g. to implement a check that validates namespace |
39 | /// declarations, override ``registerMatchers``: |
40 | /// |
41 | /// ~~~{.cpp} |
42 | /// void registerMatchers(ast_matchers::MatchFinder *Finder) override { |
43 | /// Finder->addMatcher(namespaceDecl().bind("namespace"), this); |
44 | /// } |
45 | /// ~~~ |
46 | /// |
47 | /// and then override ``check(const MatchResult &Result)`` to do the actual |
48 | /// check for each match. |
49 | /// |
50 | /// A new ``ClangTidyCheck`` instance is created per translation unit. |
51 | /// |
52 | /// FIXME: Figure out whether carrying information from one TU to another is |
53 | /// useful/necessary. |
54 | class ClangTidyCheck : public ast_matchers::MatchFinder::MatchCallback { |
55 | public: |
56 | /// Initializes the check with \p CheckName and \p Context. |
57 | /// |
58 | /// Derived classes must implement the constructor with this signature or |
59 | /// delegate it. If a check needs to read options, it can do this in the |
60 | /// constructor using the Options.get() methods below. |
61 | ClangTidyCheck(StringRef CheckName, ClangTidyContext *Context); |
62 | |
63 | /// Override this to disable registering matchers and PP callbacks if an |
64 | /// invalid language version is being used. |
65 | /// |
66 | /// For example if a check is examining overloaded functions then this should |
67 | /// be overridden to return false when the CPlusPlus flag is not set in |
68 | /// \p LangOpts. |
69 | virtual bool isLanguageVersionSupported(const LangOptions &LangOpts) const { |
70 | return true; |
71 | } |
72 | |
73 | /// Override this to register ``PPCallbacks`` in the preprocessor. |
74 | /// |
75 | /// This should be used for clang-tidy checks that analyze preprocessor- |
76 | /// dependent properties, e.g. include directives and macro definitions. |
77 | /// |
78 | /// This will only be executed if the function isLanguageVersionSupported |
79 | /// returns true. |
80 | /// |
81 | /// There are two Preprocessors to choose from that differ in how they handle |
82 | /// modular #includes: |
83 | /// - PP is the real Preprocessor. It doesn't walk into modular #includes and |
84 | /// thus doesn't generate PPCallbacks for their contents. |
85 | /// - ModuleExpanderPP preprocesses the whole translation unit in the |
86 | /// non-modular mode, which allows it to generate PPCallbacks not only for |
87 | /// the main file and textual headers, but also for all transitively |
88 | /// included modular headers when the analysis runs with modules enabled. |
89 | /// When modules are not enabled ModuleExpanderPP just points to the real |
90 | /// preprocessor. |
91 | virtual void registerPPCallbacks(const SourceManager &SM, Preprocessor *PP, |
92 | Preprocessor *ModuleExpanderPP) {} |
93 | |
94 | /// Override this to register AST matchers with \p Finder. |
95 | /// |
96 | /// This should be used by clang-tidy checks that analyze code properties that |
97 | /// dependent on AST knowledge. |
98 | /// |
99 | /// You can register as many matchers as necessary with \p Finder. Usually, |
100 | /// "this" will be used as callback, but you can also specify other callback |
101 | /// classes. Thereby, different matchers can trigger different callbacks. |
102 | /// |
103 | /// This will only be executed if the function isLanguageVersionSupported |
104 | /// returns true. |
105 | /// |
106 | /// If you need to merge information between the different matchers, you can |
107 | /// store these as members of the derived class. However, note that all |
108 | /// matches occur in the order of the AST traversal. |
109 | virtual void registerMatchers(ast_matchers::MatchFinder *Finder) {} |
110 | |
111 | /// ``ClangTidyChecks`` that register ASTMatchers should do the actual |
112 | /// work in here. |
113 | virtual void check(const ast_matchers::MatchFinder::MatchResult &Result) {} |
114 | |
115 | /// Add a diagnostic with the check's name. |
116 | DiagnosticBuilder diag(SourceLocation Loc, StringRef Description, |
117 | DiagnosticIDs::Level Level = DiagnosticIDs::Warning); |
118 | |
119 | /// Add a diagnostic with the check's name. |
120 | DiagnosticBuilder diag(StringRef Description, |
121 | DiagnosticIDs::Level Level = DiagnosticIDs::Warning); |
122 | |
123 | /// Adds a diagnostic to report errors in the check's configuration. |
124 | DiagnosticBuilder |
125 | configurationDiag(StringRef Description, |
126 | DiagnosticIDs::Level Level = DiagnosticIDs::Warning); |
127 | |
128 | /// Should store all options supported by this check with their |
129 | /// current values or default values for options that haven't been overridden. |
130 | /// |
131 | /// The check should use ``Options.store()`` to store each option it supports |
132 | /// whether it has the default value or it has been overridden. |
133 | virtual void storeOptions(ClangTidyOptions::OptionMap &Options) {} |
134 | |
135 | /// Provides access to the ``ClangTidyCheck`` options via check-local |
136 | /// names. |
137 | /// |
138 | /// Methods of this class prepend ``CheckName + "."`` to translate check-local |
139 | /// option names to global option names. |
140 | class OptionsView { |
141 | void diagnoseBadIntegerOption(const Twine &Lookup, |
142 | StringRef Unparsed) const; |
143 | void diagnoseBadBooleanOption(const Twine &Lookup, |
144 | StringRef Unparsed) const; |
145 | void diagnoseBadEnumOption(const Twine &Lookup, StringRef Unparsed, |
146 | StringRef Suggestion = StringRef()) const; |
147 | |
148 | public: |
149 | /// Initializes the instance using \p CheckName + "." as a prefix. |
150 | OptionsView(StringRef CheckName, |
151 | const ClangTidyOptions::OptionMap &CheckOptions, |
152 | ClangTidyContext *Context); |
153 | |
154 | /// Read a named option from the ``Context``. |
155 | /// |
156 | /// Reads the option with the check-local name \p LocalName from the |
157 | /// ``CheckOptions``. If the corresponding key is not present, return |
158 | /// ``None``. |
159 | llvm::Optional<std::string> get(StringRef LocalName) const; |
160 | |
161 | /// Read a named option from the ``Context``. |
162 | /// |
163 | /// Reads the option with the check-local name \p LocalName from the |
164 | /// ``CheckOptions``. If the corresponding key is not present, returns |
165 | /// \p Default. |
166 | std::string get(StringRef LocalName, StringRef Default) const; |
167 | |
168 | /// Read a named option from the ``Context``. |
169 | /// |
170 | /// Reads the option with the check-local name \p LocalName from local or |
171 | /// global ``CheckOptions``. Gets local option first. If local is not |
172 | /// present, falls back to get global option. If global option is not |
173 | /// present either, return ``None``. |
174 | llvm::Optional<std::string> getLocalOrGlobal(StringRef LocalName) const; |
175 | |
176 | /// Read a named option from the ``Context``. |
177 | /// |
178 | /// Reads the option with the check-local name \p LocalName from local or |
179 | /// global ``CheckOptions``. Gets local option first. If local is not |
180 | /// present, falls back to get global option. If global option is not |
181 | /// present either, returns \p Default. |
182 | std::string getLocalOrGlobal(StringRef LocalName, StringRef Default) const; |
183 | |
184 | /// Read a named option from the ``Context`` and parse it as an |
185 | /// integral type ``T``. |
186 | /// |
187 | /// Reads the option with the check-local name \p LocalName from the |
188 | /// ``CheckOptions``. If the corresponding key is not present, return |
189 | /// ``None``. |
190 | /// |
191 | /// If the corresponding key can't be parsed as a ``T``, emit a |
192 | /// diagnostic and return ``None``. |
193 | template <typename T> |
194 | std::enable_if_t<std::is_integral<T>::value, llvm::Optional<T>> |
195 | get(StringRef LocalName) const { |
196 | if (llvm::Optional<std::string> Value = get(LocalName)) { |
197 | T Result{}; |
198 | if (!StringRef(*Value).getAsInteger(10, Result)) |
199 | return Result; |
200 | diagnoseBadIntegerOption(NamePrefix + LocalName, *Value); |
201 | } |
202 | return None; |
203 | } |
204 | |
205 | /// Read a named option from the ``Context`` and parse it as an |
206 | /// integral type ``T``. |
207 | /// |
208 | /// Reads the option with the check-local name \p LocalName from the |
209 | /// ``CheckOptions``. If the corresponding key is not present, return |
210 | /// \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<T>::value, T> get(StringRef LocalName, |
216 | T Default) const { |
217 | return get<T>(LocalName).getValueOr(Default); |
218 | } |
219 | |
220 | /// Read a named option from the ``Context`` and parse it as an |
221 | /// integral type ``T``. |
222 | /// |
223 | /// Reads the option with the check-local name \p LocalName from local or |
224 | /// global ``CheckOptions``. Gets local option first. If local is not |
225 | /// present, falls back to get global option. If global option is not |
226 | /// present either, return ``None``. |
227 | /// |
228 | /// If the corresponding key can't be parsed as a ``T``, emit a |
229 | /// diagnostic and return ``None``. |
230 | template <typename T> |
231 | std::enable_if_t<std::is_integral<T>::value, llvm::Optional<T>> |
232 | getLocalOrGlobal(StringRef LocalName) const { |
233 | llvm::Optional<std::string> ValueOr = get(LocalName); |
234 | bool IsGlobal = false; |
235 | if (!ValueOr) { |
236 | IsGlobal = true; |
237 | ValueOr = getLocalOrGlobal(LocalName); |
238 | if (!ValueOr) |
239 | return None; |
240 | } |
241 | T Result{}; |
242 | if (!StringRef(*ValueOr).getAsInteger(10, Result)) |
243 | return Result; |
244 | diagnoseBadIntegerOption( |
245 | IsGlobal ? Twine(LocalName) : NamePrefix + LocalName, *ValueOr); |
246 | return None; |
247 | } |
248 | |
249 | /// Read a named option from the ``Context`` and parse it as an |
250 | /// integral type ``T``. |
251 | /// |
252 | /// Reads the option with the check-local name \p LocalName from local or |
253 | /// global ``CheckOptions``. Gets local option first. If local is not |
254 | /// present, falls back to get global option. If global option is not |
255 | /// present either, return \p Default. |
256 | /// |
257 | /// If the corresponding key can't be parsed as a ``T``, emit a |
258 | /// diagnostic and return \p Default. |
259 | template <typename T> |
260 | std::enable_if_t<std::is_integral<T>::value, T> |
261 | getLocalOrGlobal(StringRef LocalName, T Default) const { |
262 | return getLocalOrGlobal<T>(LocalName).getValueOr(Default); |
263 | } |
264 | |
265 | /// Read a named option from the ``Context`` and parse it as an |
266 | /// enum type ``T``. |
267 | /// |
268 | /// Reads the option with the check-local name \p LocalName from the |
269 | /// ``CheckOptions``. If the corresponding key is not present, return |
270 | /// ``None``. |
271 | /// |
272 | /// If the corresponding key can't be parsed as a ``T``, emit a |
273 | /// diagnostic and return ``None``. |
274 | /// |
275 | /// \ref clang::tidy::OptionEnumMapping must be specialized for ``T`` to |
276 | /// supply the mapping required to convert between ``T`` and a string. |
277 | template <typename T> |
278 | std::enable_if_t<std::is_enum<T>::value, llvm::Optional<T>> |
279 | get(StringRef LocalName, bool IgnoreCase = false) const { |
280 | if (llvm::Optional<int64_t> ValueOr = |
281 | getEnumInt(LocalName, typeEraseMapping<T>(), false, IgnoreCase)) |
282 | return static_cast<T>(*ValueOr); |
283 | return None; |
284 | } |
285 | |
286 | /// Read a named option from the ``Context`` and parse it as an |
287 | /// enum type ``T``. |
288 | /// |
289 | /// Reads the option with the check-local name \p LocalName from the |
290 | /// ``CheckOptions``. If the corresponding key is not present, return |
291 | /// \p Default. |
292 | /// |
293 | /// If the corresponding key can't be parsed as a ``T``, emit a |
294 | /// diagnostic and return \p Default. |
295 | /// |
296 | /// \ref clang::tidy::OptionEnumMapping must be specialized for ``T`` to |
297 | /// supply the mapping required to convert between ``T`` and a string. |
298 | template <typename T> |
299 | std::enable_if_t<std::is_enum<T>::value, T> |
300 | get(StringRef LocalName, T Default, bool IgnoreCase = false) const { |
301 | return get<T>(LocalName, IgnoreCase).getValueOr(Default); |
302 | } |
303 | |
304 | /// Read a named option from the ``Context`` and parse it as an |
305 | /// enum type ``T``. |
306 | /// |
307 | /// Reads the option with the check-local name \p LocalName from local or |
308 | /// global ``CheckOptions``. Gets local option first. If local is not |
309 | /// present, falls back to get global option. If global option is not |
310 | /// present either, returns ``None``. |
311 | /// |
312 | /// If the corresponding key can't be parsed as a ``T``, emit a |
313 | /// diagnostic and return ``None``. |
314 | /// |
315 | /// \ref clang::tidy::OptionEnumMapping must be specialized for ``T`` to |
316 | /// supply the mapping required to convert between ``T`` and a string. |
317 | template <typename T> |
318 | std::enable_if_t<std::is_enum<T>::value, llvm::Optional<T>> |
319 | getLocalOrGlobal(StringRef LocalName, bool IgnoreCase = false) const { |
320 | if (llvm::Optional<int64_t> ValueOr = |
321 | getEnumInt(LocalName, typeEraseMapping<T>(), true, IgnoreCase)) |
322 | return static_cast<T>(*ValueOr); |
323 | return None; |
324 | } |
325 | |
326 | /// Read a named option from the ``Context`` and parse it as an |
327 | /// enum type ``T``. |
328 | /// |
329 | /// Reads the option with the check-local name \p LocalName from local or |
330 | /// global ``CheckOptions``. Gets local option first. If local is not |
331 | /// present, falls back to get global option. If global option is not |
332 | /// present either return \p Default. |
333 | /// |
334 | /// If the corresponding key can't be parsed as a ``T``, emit a |
335 | /// diagnostic and return \p Default. |
336 | /// |
337 | /// \ref clang::tidy::OptionEnumMapping must be specialized for ``T`` to |
338 | /// supply the mapping required to convert between ``T`` and a string. |
339 | template <typename T> |
340 | std::enable_if_t<std::is_enum<T>::value, T> |
341 | getLocalOrGlobal(StringRef LocalName, T Default, |
342 | bool IgnoreCase = false) const { |
343 | return getLocalOrGlobal<T>(LocalName, IgnoreCase).getValueOr(Default); |
344 | } |
345 | |
346 | /// Stores an option with the check-local name \p LocalName with |
347 | /// string value \p Value to \p Options. |
348 | void store(ClangTidyOptions::OptionMap &Options, StringRef LocalName, |
349 | StringRef Value) const; |
350 | |
351 | /// Stores an option with the check-local name \p LocalName with |
352 | /// integer value \p Value to \p Options. |
353 | template <typename T> |
354 | std::enable_if_t<std::is_integral<T>::value> |
355 | store(ClangTidyOptions::OptionMap &Options, StringRef LocalName, |
356 | T Value) const { |
357 | storeInt(Options, LocalName, Value); |
358 | } |
359 | |
360 | /// Stores an option with the check-local name \p LocalName as the string |
361 | /// representation of the Enum \p Value to \p Options. |
362 | /// |
363 | /// \ref clang::tidy::OptionEnumMapping must be specialized for ``T`` to |
364 | /// supply the mapping required to convert between ``T`` and a string. |
365 | template <typename T> |
366 | std::enable_if_t<std::is_enum<T>::value> |
367 | store(ClangTidyOptions::OptionMap &Options, StringRef LocalName, |
368 | T Value) const { |
369 | ArrayRef<std::pair<T, StringRef>> Mapping = |
370 | OptionEnumMapping<T>::getEnumMapping(); |
371 | auto Iter = llvm::find_if( |
372 | Mapping, [&](const std::pair<T, StringRef> &NameAndEnum) { |
373 | return NameAndEnum.first == Value; |
374 | }); |
375 | assert(Iter != Mapping.end() && "Unknown Case Value" ); |
376 | store(Options, LocalName, Iter->second); |
377 | } |
378 | |
379 | private: |
380 | using NameAndValue = std::pair<int64_t, StringRef>; |
381 | |
382 | llvm::Optional<int64_t> getEnumInt(StringRef LocalName, |
383 | ArrayRef<NameAndValue> Mapping, |
384 | bool CheckGlobal, bool IgnoreCase) const; |
385 | |
386 | template <typename T> |
387 | std::enable_if_t<std::is_enum<T>::value, std::vector<NameAndValue>> |
388 | typeEraseMapping() const { |
389 | ArrayRef<std::pair<T, StringRef>> Mapping = |
390 | OptionEnumMapping<T>::getEnumMapping(); |
391 | std::vector<NameAndValue> Result; |
392 | Result.reserve(Mapping.size()); |
393 | for (auto &MappedItem : Mapping) { |
394 | Result.emplace_back(static_cast<int64_t>(MappedItem.first), |
395 | MappedItem.second); |
396 | } |
397 | return Result; |
398 | } |
399 | |
400 | void storeInt(ClangTidyOptions::OptionMap &Options, StringRef LocalName, |
401 | int64_t Value) const; |
402 | |
403 | |
404 | std::string NamePrefix; |
405 | const ClangTidyOptions::OptionMap &CheckOptions; |
406 | ClangTidyContext *Context; |
407 | }; |
408 | |
409 | private: |
410 | void run(const ast_matchers::MatchFinder::MatchResult &Result) override; |
411 | StringRef getID() const override { return CheckName; } |
412 | std::string CheckName; |
413 | ClangTidyContext *Context; |
414 | |
415 | protected: |
416 | OptionsView Options; |
417 | /// Returns the main file name of the current translation unit. |
418 | StringRef getCurrentMainFile() const { return Context->getCurrentFile(); } |
419 | /// Returns the language options from the context. |
420 | const LangOptions &getLangOpts() const { return Context->getLangOpts(); } |
421 | }; |
422 | |
423 | /// Read a named option from the ``Context`` and parse it as a bool. |
424 | /// |
425 | /// Reads the option with the check-local name \p LocalName from the |
426 | /// ``CheckOptions``. If the corresponding key is not present, return |
427 | /// ``None``. |
428 | /// |
429 | /// If the corresponding key can't be parsed as a bool, emit a |
430 | /// diagnostic and return ``None``. |
431 | template <> |
432 | llvm::Optional<bool> |
433 | ClangTidyCheck::OptionsView::get<bool>(StringRef LocalName) const; |
434 | |
435 | /// Read a named option from the ``Context`` and parse it as a bool. |
436 | /// |
437 | /// Reads the option with the check-local name \p LocalName from the |
438 | /// ``CheckOptions``. If the corresponding key is not present, return |
439 | /// \p Default. |
440 | /// |
441 | /// If the corresponding key can't be parsed as a bool, emit a |
442 | /// diagnostic and return \p Default. |
443 | template <> |
444 | llvm::Optional<bool> |
445 | ClangTidyCheck::OptionsView::getLocalOrGlobal<bool>(StringRef LocalName) const; |
446 | |
447 | /// Stores an option with the check-local name \p LocalName with |
448 | /// bool value \p Value to \p Options. |
449 | template <> |
450 | void ClangTidyCheck::OptionsView::store<bool>( |
451 | ClangTidyOptions::OptionMap &Options, StringRef LocalName, |
452 | bool Value) const; |
453 | |
454 | |
455 | } // namespace tidy |
456 | } // namespace clang |
457 | |
458 | #endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CLANGTIDYCHECK_H |
459 | |