clang-tools  16.0.0git
ClangTidyCheck.cpp
Go to the documentation of this file.
1 //===--- ClangTidyCheck.cpp - 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 #include "ClangTidyCheck.h"
10 #include "llvm/ADT/SmallString.h"
11 #include "llvm/ADT/StringRef.h"
12 #include "llvm/Support/Error.h"
13 #include "llvm/Support/YAMLParser.h"
14 
15 namespace clang {
16 namespace tidy {
17 
18 ClangTidyCheck::ClangTidyCheck(StringRef CheckName, ClangTidyContext *Context)
19  : CheckName(CheckName), Context(Context),
20  Options(CheckName, Context->getOptions().CheckOptions, Context) {
21  assert(Context != nullptr);
22  assert(!CheckName.empty());
23 }
24 
25 DiagnosticBuilder ClangTidyCheck::diag(SourceLocation Loc, StringRef Message,
26  DiagnosticIDs::Level Level) {
27  return Context->diag(CheckName, Loc, Message, Level);
28 }
29 
30 DiagnosticBuilder ClangTidyCheck::diag(StringRef Message,
31  DiagnosticIDs::Level Level) {
32  return Context->diag(CheckName, Message, Level);
33 }
34 
35 DiagnosticBuilder
37  DiagnosticIDs::Level Level) const {
38  return Context->configurationDiag(Description, Level);
39 }
40 
41 void ClangTidyCheck::run(const ast_matchers::MatchFinder::MatchResult &Result) {
42  // For historical reasons, checks don't implement the MatchFinder run()
43  // callback directly. We keep the run()/check() distinction to avoid interface
44  // churn, and to allow us to add cross-cutting logic in the future.
45  check(Result);
46 }
47 
49  StringRef CheckName, const ClangTidyOptions::OptionMap &CheckOptions,
50  ClangTidyContext *Context)
51  : NamePrefix((CheckName + ".").str()), CheckOptions(CheckOptions),
52  Context(Context) {}
53 
54 llvm::Optional<StringRef>
55 ClangTidyCheck::OptionsView::get(StringRef LocalName) const {
56  if (Context->getOptionsCollector())
57  Context->getOptionsCollector()->insert((NamePrefix + LocalName).str());
58  const auto &Iter = CheckOptions.find((NamePrefix + LocalName).str());
59  if (Iter != CheckOptions.end())
60  return StringRef(Iter->getValue().Value);
61  return None;
62 }
63 
64 static ClangTidyOptions::OptionMap::const_iterator
66  StringRef NamePrefix, StringRef LocalName,
67  llvm::StringSet<> *Collector) {
68  if (Collector) {
69  Collector->insert((NamePrefix + LocalName).str());
70  Collector->insert(LocalName);
71  }
72  auto IterLocal = Options.find((NamePrefix + LocalName).str());
73  auto IterGlobal = Options.find(LocalName);
74  if (IterLocal == Options.end())
75  return IterGlobal;
76  if (IterGlobal == Options.end())
77  return IterLocal;
78  if (IterLocal->getValue().Priority >= IterGlobal->getValue().Priority)
79  return IterLocal;
80  return IterGlobal;
81 }
82 
83 llvm::Optional<StringRef>
85  auto Iter = findPriorityOption(CheckOptions, NamePrefix, LocalName,
86  Context->getOptionsCollector());
87  if (Iter != CheckOptions.end())
88  return StringRef(Iter->getValue().Value);
89  return None;
90 }
91 
92 static Optional<bool> getAsBool(StringRef Value,
93  const llvm::Twine &LookupName) {
94 
95  if (llvm::Optional<bool> Parsed = llvm::yaml::parseBool(Value))
96  return *Parsed;
97  // To maintain backwards compatability, we support parsing numbers as
98  // booleans, even though its not supported in YAML.
99  long long Number;
100  if (!Value.getAsInteger(10, Number))
101  return Number != 0;
102  return None;
103 }
104 
105 template <>
106 llvm::Optional<bool>
107 ClangTidyCheck::OptionsView::get<bool>(StringRef LocalName) const {
108  if (llvm::Optional<StringRef> ValueOr = get(LocalName)) {
109  if (auto Result = getAsBool(*ValueOr, NamePrefix + LocalName))
110  return Result;
111  diagnoseBadBooleanOption(NamePrefix + LocalName, *ValueOr);
112  }
113  return None;
114 }
115 
116 template <>
117 llvm::Optional<bool>
118 ClangTidyCheck::OptionsView::getLocalOrGlobal<bool>(StringRef LocalName) const {
119  auto Iter = findPriorityOption(CheckOptions, NamePrefix, LocalName,
120  Context->getOptionsCollector());
121  if (Iter != CheckOptions.end()) {
122  if (auto Result = getAsBool(Iter->getValue().Value, Iter->getKey()))
123  return Result;
124  diagnoseBadBooleanOption(Iter->getKey(), Iter->getValue().Value);
125  }
126  return None;
127 }
128 
130  StringRef LocalName,
131  StringRef Value) const {
132  Options[(NamePrefix + LocalName).str()] = Value;
133 }
134 
135 void ClangTidyCheck::OptionsView::storeInt(ClangTidyOptions::OptionMap &Options,
136  StringRef LocalName,
137  int64_t Value) const {
138  store(Options, LocalName, llvm::itostr(Value));
139 }
140 
141 template <>
142 void ClangTidyCheck::OptionsView::store<bool>(
143  ClangTidyOptions::OptionMap &Options, StringRef LocalName,
144  bool Value) const {
145  store(Options, LocalName, Value ? StringRef("true") : StringRef("false"));
146 }
147 
148 llvm::Optional<int64_t> ClangTidyCheck::OptionsView::getEnumInt(
149  StringRef LocalName, ArrayRef<NameAndValue> Mapping, bool CheckGlobal,
150  bool IgnoreCase) const {
151  if (!CheckGlobal && Context->getOptionsCollector())
152  Context->getOptionsCollector()->insert((NamePrefix + LocalName).str());
153  auto Iter = CheckGlobal
154  ? findPriorityOption(CheckOptions, NamePrefix, LocalName,
155  Context->getOptionsCollector())
156  : CheckOptions.find((NamePrefix + LocalName).str());
157  if (Iter == CheckOptions.end())
158  return None;
159 
160  StringRef Value = Iter->getValue().Value;
161  StringRef Closest;
162  unsigned EditDistance = 3;
163  for (const auto &NameAndEnum : Mapping) {
164  if (IgnoreCase) {
165  if (Value.equals_insensitive(NameAndEnum.second))
166  return NameAndEnum.first;
167  } else if (Value.equals(NameAndEnum.second)) {
168  return NameAndEnum.first;
169  } else if (Value.equals_insensitive(NameAndEnum.second)) {
170  Closest = NameAndEnum.second;
171  EditDistance = 0;
172  continue;
173  }
174  unsigned Distance =
175  Value.edit_distance(NameAndEnum.second, true, EditDistance);
176  if (Distance < EditDistance) {
177  EditDistance = Distance;
178  Closest = NameAndEnum.second;
179  }
180  }
181  if (EditDistance < 3)
182  diagnoseBadEnumOption(Iter->getKey(), Iter->getValue().Value, Closest);
183  else
184  diagnoseBadEnumOption(Iter->getKey(), Iter->getValue().Value);
185  return None;
186 }
187 
188 static constexpr llvm::StringLiteral ConfigWarning(
189  "invalid configuration value '%0' for option '%1'%select{|; expected a "
190  "bool|; expected an integer|; did you mean '%3'?}2");
191 
192 void ClangTidyCheck::OptionsView::diagnoseBadBooleanOption(
193  const Twine &Lookup, StringRef Unparsed) const {
194  SmallString<64> Buffer;
196  << Unparsed << Lookup.toStringRef(Buffer) << 1;
197 }
198 
199 void ClangTidyCheck::OptionsView::diagnoseBadIntegerOption(
200  const Twine &Lookup, StringRef Unparsed) const {
201  SmallString<64> Buffer;
203  << Unparsed << Lookup.toStringRef(Buffer) << 2;
204 }
205 
206 void ClangTidyCheck::OptionsView::diagnoseBadEnumOption(
207  const Twine &Lookup, StringRef Unparsed, StringRef Suggestion) const {
208  SmallString<64> Buffer;
209  auto Diag = Context->configurationDiag(ConfigWarning)
210  << Unparsed << Lookup.toStringRef(Buffer);
211  if (Suggestion.empty())
212  Diag << 0;
213  else
214  Diag << 3 << Suggestion;
215 }
216 
217 StringRef ClangTidyCheck::OptionsView::get(StringRef LocalName,
218  StringRef Default) const {
219  return get(LocalName).value_or(Default);
220 }
221 
222 StringRef
224  StringRef Default) const {
225  return getLocalOrGlobal(LocalName).value_or(Default);
226 }
227 } // namespace tidy
228 } // namespace clang
Loc
SourceLocation Loc
Definition: KernelNameRestrictionCheck.cpp:45
clang::tidy::ClangTidyOptions::OptionMap
llvm::StringMap< ClangTidyValue > OptionMap
Definition: ClangTidyOptions.h:115
clang::tidy::ClangTidyCheck::ClangTidyCheck
ClangTidyCheck(StringRef CheckName, ClangTidyContext *Context)
Initializes the check with CheckName and Context.
Definition: ClangTidyCheck.cpp:18
clang::tidy::bugprone::Message
static const char Message[]
Definition: ReservedIdentifierCheck.cpp:31
clang::tidy::getAsBool
static Optional< bool > getAsBool(StringRef Value, const llvm::Twine &LookupName)
Definition: ClangTidyCheck.cpp:92
clang::tidy::ConfigWarning
static constexpr llvm::StringLiteral ConfigWarning("invalid configuration value '%0' for option '%1'%select{|; expected a " "bool|; expected an integer|; did you mean '%3'?}2")
Number
unsigned Number
Definition: SourceCodeTests.cpp:94
clang::tidy::ClangTidyCheck::Options
OptionsView Options
Definition: ClangTidyCheck.h:415
clang::tidy::ClangTidyContext
Every ClangTidyCheck reports errors through a DiagnosticsEngine provided by this context.
Definition: ClangTidyDiagnosticConsumer.h:67
Description
const char * Description
Definition: Dexp.cpp:361
clang::tidy::ClangTidyCheck::OptionsView::get
llvm::Optional< StringRef > get(StringRef LocalName) const
Read a named option from the Context.
Definition: ClangTidyCheck.cpp:55
clang::tidy::ClangTidyCheck::diag
DiagnosticBuilder diag(SourceLocation Loc, StringRef Description, DiagnosticIDs::Level Level=DiagnosticIDs::Warning)
Add a diagnostic with the check's name.
Definition: ClangTidyCheck.cpp:25
clang::tidy::findPriorityOption
static ClangTidyOptions::OptionMap::const_iterator findPriorityOption(const ClangTidyOptions::OptionMap &Options, StringRef NamePrefix, StringRef LocalName, llvm::StringSet<> *Collector)
Definition: ClangTidyCheck.cpp:65
clang::tidy::ClangTidyCheck::check
virtual void check(const ast_matchers::MatchFinder::MatchResult &Result)
ClangTidyChecks that register ASTMatchers should do the actual work in here.
Definition: ClangTidyCheck.h:112
Collector
std::shared_ptr< SymbolCollector > Collector
Definition: SymbolCollectorTests.cpp:258
clang::tidy::ClangTidyCheck::configurationDiag
DiagnosticBuilder configurationDiag(StringRef Description, DiagnosticIDs::Level Level=DiagnosticIDs::Warning) const
Adds a diagnostic to report errors in the check's configuration.
Definition: ClangTidyCheck.cpp:36
clang::tidy::bugprone::model::MixFlags::None
@ None
Mix between the two parameters is not possible.
clang
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
Definition: ApplyReplacements.h:27
clang::tidy::ClangTidyCheck::OptionsView::store
void store(ClangTidyOptions::OptionMap &Options, StringRef LocalName, StringRef Value) const
Stores an option with the check-local name LocalName with string value Value to Options.
Definition: ClangTidyCheck.cpp:129
clang::tidy::ClangTidyCheck::OptionsView::OptionsView
OptionsView(StringRef CheckName, const ClangTidyOptions::OptionMap &CheckOptions, ClangTidyContext *Context)
Initializes the instance using CheckName + "." as a prefix.
Definition: ClangTidyCheck.cpp:48
clang::tidy::ClangTidyContext::getOptionsCollector
llvm::StringSet * getOptionsCollector() const
Definition: ClangTidyDiagnosticConsumer.h:208
clang::tidy::ClangTidyContext::diag
DiagnosticBuilder diag(StringRef CheckName, SourceLocation Loc, StringRef Message, DiagnosticIDs::Level Level=DiagnosticIDs::Warning)
Report any errors detected using this method.
Definition: ClangTidyDiagnosticConsumer.cpp:174
ClangTidyCheck.h
clang::tidy::ClangTidyContext::configurationDiag
DiagnosticBuilder configurationDiag(StringRef Message, DiagnosticIDs::Level Level=DiagnosticIDs::Warning)
Report any errors to do with reading the configuration using this method.
Definition: ClangTidyDiagnosticConsumer.cpp:205
clang::tidy::ClangTidyCheck::OptionsView::getLocalOrGlobal
llvm::Optional< StringRef > getLocalOrGlobal(StringRef LocalName) const
Read a named option from the Context.
Definition: ClangTidyCheck.cpp:84