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 "llvm/ADT/Optional.h"
17#include <type_traits>
18#include <utility>
19#include <vector>
20
21namespace clang {
22
23class CompilerInstance;
24class SourceManager;
25
26namespace 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.
30template <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.
54class ClangTidyCheck : public ast_matchers::MatchFinder::MatchCallback {
55public:
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
409private:
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
415protected:
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``.
431template <>
432llvm::Optional<bool>
433ClangTidyCheck::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.
443template <>
444llvm::Optional<bool>
445ClangTidyCheck::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.
449template <>
450void 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