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/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(Message: 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(key: (NamePrefix + LocalName).str());
59 const auto &Iter = CheckOptions.find(Key: (NamePrefix + LocalName).str());
60 if (Iter != CheckOptions.end())
61 return StringRef(Iter->getValue().Value);
62 return std::nullopt;
63}
64
65static const llvm::StringSet<> DeprecatedGlobalOptions{
66 "StrictMode",
67 "IgnoreMacros",
68};
69
70static ClangTidyOptions::OptionMap::const_iterator
71findPriorityOption(const ClangTidyOptions::OptionMap &Options,
72 StringRef NamePrefix, StringRef LocalName,
73 ClangTidyContext *Context) {
74 llvm::StringSet<> *Collector = Context->getOptionsCollector();
75 if (Collector) {
76 Collector->insert(key: (NamePrefix + LocalName).str());
77 Collector->insert(key: LocalName);
78 }
79 auto IterLocal = Options.find(Key: (NamePrefix + LocalName).str());
80 auto IterGlobal = Options.find(Key: LocalName);
81 // FIXME: temporary solution for deprecation warnings, should be removed
82 // after 22.x. Warn configuration deps on deprecation global options.
83 if (IterLocal == Options.end() && IterGlobal != Options.end() &&
84 DeprecatedGlobalOptions.contains(key: LocalName))
85 Context->configurationDiag(
86 Message: "global option '%0' is deprecated, please use '%1%0' instead.")
87 << LocalName << NamePrefix;
88 if (IterLocal == Options.end())
89 return IterGlobal;
90 if (IterGlobal == Options.end())
91 return IterLocal;
92 if (IterLocal->getValue().Priority >= IterGlobal->getValue().Priority)
93 return IterLocal;
94 return IterGlobal;
95}
96
97std::optional<StringRef>
98ClangTidyCheck::OptionsView::getLocalOrGlobal(StringRef LocalName) const {
99 auto Iter = findPriorityOption(Options: CheckOptions, NamePrefix, LocalName, Context);
100 if (Iter != CheckOptions.end())
101 return StringRef(Iter->getValue().Value);
102 return std::nullopt;
103}
104
105static std::optional<bool> getAsBool(StringRef Value,
106 const llvm::Twine &LookupName) {
107
108 if (std::optional<bool> Parsed = llvm::yaml::parseBool(S: Value))
109 return Parsed;
110 // To maintain backwards compatability, we support parsing numbers as
111 // booleans, even though its not supported in YAML.
112 long long Number = 0;
113 if (!Value.getAsInteger(Radix: 10, Result&: Number))
114 return Number != 0;
115 return std::nullopt;
116}
117
118template <>
119std::optional<bool>
120ClangTidyCheck::OptionsView::get<bool>(StringRef LocalName) const {
121 if (std::optional<StringRef> ValueOr = get(LocalName)) {
122 if (auto Result = getAsBool(Value: *ValueOr, LookupName: NamePrefix + LocalName))
123 return Result;
124 diagnoseBadBooleanOption(Lookup: NamePrefix + LocalName, Unparsed: *ValueOr);
125 }
126 return std::nullopt;
127}
128
129template <>
130std::optional<bool>
131ClangTidyCheck::OptionsView::getLocalOrGlobal<bool>(StringRef LocalName) const {
132 auto Iter = findPriorityOption(Options: CheckOptions, NamePrefix, LocalName, Context);
133 if (Iter != CheckOptions.end()) {
134 if (auto Result = getAsBool(Value: Iter->getValue().Value, LookupName: Iter->getKey()))
135 return Result;
136 diagnoseBadBooleanOption(Lookup: Iter->getKey(), Unparsed: Iter->getValue().Value);
137 }
138 return std::nullopt;
139}
140
141void ClangTidyCheck::OptionsView::store(ClangTidyOptions::OptionMap &Options,
142 StringRef LocalName,
143 StringRef Value) const {
144 Options[(NamePrefix + LocalName).str()] = Value;
145}
146
147void ClangTidyCheck::OptionsView::storeInt(ClangTidyOptions::OptionMap &Options,
148 StringRef LocalName,
149 int64_t Value) const {
150 store(Options, LocalName, Value: llvm::itostr(X: Value));
151}
152
153void ClangTidyCheck::OptionsView::storeUnsigned(
154 ClangTidyOptions::OptionMap &Options, StringRef LocalName,
155 uint64_t Value) const {
156 store(Options, LocalName, Value: llvm::utostr(X: Value));
157}
158
159template <>
160void ClangTidyCheck::OptionsView::store<bool>(
161 ClangTidyOptions::OptionMap &Options, StringRef LocalName,
162 bool Value) const {
163 store(Options, LocalName, Value: Value ? StringRef("true") : StringRef("false"));
164}
165
166std::optional<int64_t>
167ClangTidyCheck::OptionsView::getEnumInt(StringRef LocalName,
168 ArrayRef<NameAndValue> Mapping,
169 bool CheckGlobal) const {
170 if (!CheckGlobal && Context->getOptionsCollector())
171 Context->getOptionsCollector()->insert(key: (NamePrefix + LocalName).str());
172 auto Iter = CheckGlobal ? findPriorityOption(Options: CheckOptions, NamePrefix,
173 LocalName, Context)
174 : CheckOptions.find(Key: (NamePrefix + LocalName).str());
175 if (Iter == CheckOptions.end())
176 return std::nullopt;
177
178 StringRef Value = Iter->getValue().Value;
179 StringRef Closest;
180 unsigned EditDistance = 3;
181 for (const auto &NameAndEnum : Mapping) {
182 if (Value == NameAndEnum.second) {
183 return NameAndEnum.first;
184 }
185 if (Value.equals_insensitive(RHS: NameAndEnum.second)) {
186 Closest = NameAndEnum.second;
187 EditDistance = 0;
188 continue;
189 }
190 unsigned Distance =
191 Value.edit_distance(Other: NameAndEnum.second, AllowReplacements: true, MaxEditDistance: EditDistance);
192 if (Distance < EditDistance) {
193 EditDistance = Distance;
194 Closest = NameAndEnum.second;
195 }
196 }
197 if (EditDistance < 3)
198 diagnoseBadEnumOption(Lookup: Iter->getKey(), Unparsed: Iter->getValue().Value, Suggestion: Closest);
199 else
200 diagnoseBadEnumOption(Lookup: Iter->getKey(), Unparsed: Iter->getValue().Value);
201 return std::nullopt;
202}
203
204static constexpr llvm::StringLiteral ConfigWarning(
205 "invalid configuration value '%0' for option '%1'%select{|; expected a "
206 "bool|; expected an integer|; did you mean '%3'?}2");
207
208void ClangTidyCheck::OptionsView::diagnoseBadBooleanOption(
209 const Twine &Lookup, StringRef Unparsed) const {
210 SmallString<64> Buffer;
211 Context->configurationDiag(Message: ConfigWarning)
212 << Unparsed << Lookup.toStringRef(Out&: Buffer) << 1;
213}
214
215void ClangTidyCheck::OptionsView::diagnoseBadIntegerOption(
216 const Twine &Lookup, StringRef Unparsed) const {
217 SmallString<64> Buffer;
218 Context->configurationDiag(Message: ConfigWarning)
219 << Unparsed << Lookup.toStringRef(Out&: Buffer) << 2;
220}
221
222void ClangTidyCheck::OptionsView::diagnoseBadEnumOption(
223 const Twine &Lookup, StringRef Unparsed, StringRef Suggestion) const {
224 SmallString<64> Buffer;
225 auto Diag = Context->configurationDiag(Message: ConfigWarning)
226 << Unparsed << Lookup.toStringRef(Out&: Buffer);
227 if (Suggestion.empty())
228 Diag << 0;
229 else
230 Diag << 3 << Suggestion;
231}
232
233StringRef ClangTidyCheck::OptionsView::get(StringRef LocalName,
234 StringRef Default) const {
235 return get(LocalName).value_or(u&: Default);
236}
237
238StringRef
239ClangTidyCheck::OptionsView::getLocalOrGlobal(StringRef LocalName,
240 StringRef Default) const {
241 return getLocalOrGlobal(LocalName).value_or(u&: Default);
242}
243} // namespace clang::tidy
244

Provided by KDAB

Privacy Policy
Learn to use CMake with our Intro Training
Find out more

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