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