150
|
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"
|
173
|
10 #include "llvm/ADT/SmallString.h"
|
|
11 #include "llvm/ADT/StringRef.h"
|
|
12 #include "llvm/Support/Error.h"
|
221
|
13 #include "llvm/Support/YAMLParser.h"
|
252
|
14 #include <optional>
|
150
|
15
|
252
|
16 namespace clang::tidy {
|
150
|
17
|
|
18 ClangTidyCheck::ClangTidyCheck(StringRef CheckName, ClangTidyContext *Context)
|
|
19 : CheckName(CheckName), Context(Context),
|
221
|
20 Options(CheckName, Context->getOptions().CheckOptions, Context) {
|
150
|
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
|
221
|
30 DiagnosticBuilder ClangTidyCheck::diag(StringRef Message,
|
|
31 DiagnosticIDs::Level Level) {
|
|
32 return Context->diag(CheckName, Message, Level);
|
|
33 }
|
|
34
|
|
35 DiagnosticBuilder
|
|
36 ClangTidyCheck::configurationDiag(StringRef Description,
|
236
|
37 DiagnosticIDs::Level Level) const {
|
221
|
38 return Context->configurationDiag(Description, Level);
|
|
39 }
|
|
40
|
150
|
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
|
221
|
48 ClangTidyCheck::OptionsView::OptionsView(
|
|
49 StringRef CheckName, const ClangTidyOptions::OptionMap &CheckOptions,
|
|
50 ClangTidyContext *Context)
|
236
|
51 : NamePrefix((CheckName + ".").str()), CheckOptions(CheckOptions),
|
221
|
52 Context(Context) {}
|
150
|
53
|
252
|
54 std::optional<StringRef>
|
173
|
55 ClangTidyCheck::OptionsView::get(StringRef LocalName) const {
|
236
|
56 if (Context->getOptionsCollector())
|
|
57 Context->getOptionsCollector()->insert((NamePrefix + LocalName).str());
|
|
58 const auto &Iter = CheckOptions.find((NamePrefix + LocalName).str());
|
150
|
59 if (Iter != CheckOptions.end())
|
236
|
60 return StringRef(Iter->getValue().Value);
|
252
|
61 return std::nullopt;
|
221
|
62 }
|
|
63
|
|
64 static ClangTidyOptions::OptionMap::const_iterator
|
236
|
65 findPriorityOption(const ClangTidyOptions::OptionMap &Options,
|
|
66 StringRef NamePrefix, StringRef LocalName,
|
|
67 llvm::StringSet<> *Collector) {
|
|
68 if (Collector) {
|
|
69 Collector->insert((NamePrefix + LocalName).str());
|
|
70 Collector->insert(LocalName);
|
|
71 }
|
221
|
72 auto IterLocal = Options.find((NamePrefix + LocalName).str());
|
236
|
73 auto IterGlobal = Options.find(LocalName);
|
221
|
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;
|
173
|
81 }
|
|
82
|
252
|
83 std::optional<StringRef>
|
173
|
84 ClangTidyCheck::OptionsView::getLocalOrGlobal(StringRef LocalName) const {
|
236
|
85 auto Iter = findPriorityOption(CheckOptions, NamePrefix, LocalName,
|
|
86 Context->getOptionsCollector());
|
221
|
87 if (Iter != CheckOptions.end())
|
236
|
88 return StringRef(Iter->getValue().Value);
|
252
|
89 return std::nullopt;
|
173
|
90 }
|
|
91
|
252
|
92 static std::optional<bool> getAsBool(StringRef Value,
|
|
93 const llvm::Twine &LookupName) {
|
221
|
94
|
252
|
95 if (std::optional<bool> Parsed = llvm::yaml::parseBool(Value))
|
221
|
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;
|
252
|
102 return std::nullopt;
|
150
|
103 }
|
|
104
|
173
|
105 template <>
|
252
|
106 std::optional<bool>
|
173
|
107 ClangTidyCheck::OptionsView::get<bool>(StringRef LocalName) const {
|
252
|
108 if (std::optional<StringRef> ValueOr = get(LocalName)) {
|
221
|
109 if (auto Result = getAsBool(*ValueOr, NamePrefix + LocalName))
|
|
110 return Result;
|
|
111 diagnoseBadBooleanOption(NamePrefix + LocalName, *ValueOr);
|
|
112 }
|
252
|
113 return std::nullopt;
|
173
|
114 }
|
|
115
|
|
116 template <>
|
252
|
117 std::optional<bool>
|
173
|
118 ClangTidyCheck::OptionsView::getLocalOrGlobal<bool>(StringRef LocalName) const {
|
236
|
119 auto Iter = findPriorityOption(CheckOptions, NamePrefix, LocalName,
|
|
120 Context->getOptionsCollector());
|
221
|
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 }
|
252
|
126 return std::nullopt;
|
150
|
127 }
|
|
128
|
|
129 void ClangTidyCheck::OptionsView::store(ClangTidyOptions::OptionMap &Options,
|
|
130 StringRef LocalName,
|
|
131 StringRef Value) const {
|
236
|
132 Options[(NamePrefix + LocalName).str()] = Value;
|
150
|
133 }
|
|
134
|
221
|
135 void ClangTidyCheck::OptionsView::storeInt(ClangTidyOptions::OptionMap &Options,
|
|
136 StringRef LocalName,
|
|
137 int64_t Value) const {
|
150
|
138 store(Options, LocalName, llvm::itostr(Value));
|
|
139 }
|
|
140
|
221
|
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
|
252
|
148 std::optional<int64_t> ClangTidyCheck::OptionsView::getEnumInt(
|
221
|
149 StringRef LocalName, ArrayRef<NameAndValue> Mapping, bool CheckGlobal,
|
|
150 bool IgnoreCase) const {
|
236
|
151 if (!CheckGlobal && Context->getOptionsCollector())
|
|
152 Context->getOptionsCollector()->insert((NamePrefix + LocalName).str());
|
221
|
153 auto Iter = CheckGlobal
|
236
|
154 ? findPriorityOption(CheckOptions, NamePrefix, LocalName,
|
|
155 Context->getOptionsCollector())
|
221
|
156 : CheckOptions.find((NamePrefix + LocalName).str());
|
173
|
157 if (Iter == CheckOptions.end())
|
252
|
158 return std::nullopt;
|
173
|
159
|
221
|
160 StringRef Value = Iter->getValue().Value;
|
173
|
161 StringRef Closest;
|
221
|
162 unsigned EditDistance = 3;
|
173
|
163 for (const auto &NameAndEnum : Mapping) {
|
|
164 if (IgnoreCase) {
|
223
|
165 if (Value.equals_insensitive(NameAndEnum.second))
|
221
|
166 return NameAndEnum.first;
|
|
167 } else if (Value.equals(NameAndEnum.second)) {
|
|
168 return NameAndEnum.first;
|
223
|
169 } else if (Value.equals_insensitive(NameAndEnum.second)) {
|
221
|
170 Closest = NameAndEnum.second;
|
173
|
171 EditDistance = 0;
|
|
172 continue;
|
|
173 }
|
221
|
174 unsigned Distance =
|
|
175 Value.edit_distance(NameAndEnum.second, true, EditDistance);
|
173
|
176 if (Distance < EditDistance) {
|
|
177 EditDistance = Distance;
|
221
|
178 Closest = NameAndEnum.second;
|
173
|
179 }
|
|
180 }
|
|
181 if (EditDistance < 3)
|
236
|
182 diagnoseBadEnumOption(Iter->getKey(), Iter->getValue().Value, Closest);
|
221
|
183 else
|
236
|
184 diagnoseBadEnumOption(Iter->getKey(), Iter->getValue().Value);
|
252
|
185 return std::nullopt;
|
221
|
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;
|
|
195 Context->configurationDiag(ConfigWarning)
|
|
196 << Unparsed << Lookup.toStringRef(Buffer) << 1;
|
173
|
197 }
|
|
198
|
221
|
199 void ClangTidyCheck::OptionsView::diagnoseBadIntegerOption(
|
|
200 const Twine &Lookup, StringRef Unparsed) const {
|
|
201 SmallString<64> Buffer;
|
|
202 Context->configurationDiag(ConfigWarning)
|
|
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
|
236
|
217 StringRef ClangTidyCheck::OptionsView::get(StringRef LocalName,
|
|
218 StringRef Default) const {
|
|
219 return get(LocalName).value_or(Default);
|
221
|
220 }
|
236
|
221
|
|
222 StringRef
|
221
|
223 ClangTidyCheck::OptionsView::getLocalOrGlobal(StringRef LocalName,
|
|
224 StringRef Default) const {
|
236
|
225 return getLocalOrGlobal(LocalName).value_or(Default);
|
173
|
226 }
|
252
|
227 } // namespace clang::tidy
|