1 //===--- ClangTidyCheck.cpp - clang-tidy ------------------------*- C++ -*-===//
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
7 //===----------------------------------------------------------------------===//
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"
16 namespace clang::tidy
{
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());
25 DiagnosticBuilder
ClangTidyCheck::diag(SourceLocation Loc
, StringRef Message
,
26 DiagnosticIDs::Level Level
) {
27 return Context
->diag(CheckName
, Loc
, Message
, Level
);
30 DiagnosticBuilder
ClangTidyCheck::diag(StringRef Message
,
31 DiagnosticIDs::Level Level
) {
32 return Context
->diag(CheckName
, Message
, Level
);
36 ClangTidyCheck::configurationDiag(StringRef Description
,
37 DiagnosticIDs::Level Level
) const {
38 return Context
->configurationDiag(Description
, Level
);
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.
48 ClangTidyCheck::OptionsView::OptionsView(
49 StringRef CheckName
, const ClangTidyOptions::OptionMap
&CheckOptions
,
50 ClangTidyContext
*Context
)
51 : NamePrefix((CheckName
+ ".").str()), CheckOptions(CheckOptions
),
54 std::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
);
64 static ClangTidyOptions::OptionMap::const_iterator
65 findPriorityOption(const ClangTidyOptions::OptionMap
&Options
,
66 StringRef NamePrefix
, StringRef LocalName
,
67 llvm::StringSet
<> *Collector
) {
69 Collector
->insert((NamePrefix
+ LocalName
).str());
70 Collector
->insert(LocalName
);
72 auto IterLocal
= Options
.find((NamePrefix
+ LocalName
).str());
73 auto IterGlobal
= Options
.find(LocalName
);
74 if (IterLocal
== Options
.end())
76 if (IterGlobal
== Options
.end())
78 if (IterLocal
->getValue().Priority
>= IterGlobal
->getValue().Priority
)
83 std::optional
<StringRef
>
84 ClangTidyCheck::OptionsView::getLocalOrGlobal(StringRef LocalName
) const {
85 auto Iter
= findPriorityOption(CheckOptions
, NamePrefix
, LocalName
,
86 Context
->getOptionsCollector());
87 if (Iter
!= CheckOptions
.end())
88 return StringRef(Iter
->getValue().Value
);
92 static std::optional
<bool> getAsBool(StringRef Value
,
93 const llvm::Twine
&LookupName
) {
95 if (std::optional
<bool> Parsed
= llvm::yaml::parseBool(Value
))
97 // To maintain backwards compatability, we support parsing numbers as
98 // booleans, even though its not supported in YAML.
100 if (!Value
.getAsInteger(10, Number
))
107 ClangTidyCheck::OptionsView::get
<bool>(StringRef LocalName
) const {
108 if (std::optional
<StringRef
> ValueOr
= get(LocalName
)) {
109 if (auto Result
= getAsBool(*ValueOr
, NamePrefix
+ LocalName
))
111 diagnoseBadBooleanOption(NamePrefix
+ LocalName
, *ValueOr
);
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()))
124 diagnoseBadBooleanOption(Iter
->getKey(), Iter
->getValue().Value
);
129 void ClangTidyCheck::OptionsView::store(ClangTidyOptions::OptionMap
&Options
,
131 StringRef Value
) const {
132 Options
[(NamePrefix
+ LocalName
).str()] = Value
;
135 void ClangTidyCheck::OptionsView::storeInt(ClangTidyOptions::OptionMap
&Options
,
137 int64_t Value
) const {
138 store(Options
, LocalName
, llvm::itostr(Value
));
142 void ClangTidyCheck::OptionsView::store
<bool>(
143 ClangTidyOptions::OptionMap
&Options
, StringRef LocalName
,
145 store(Options
, LocalName
, Value
? StringRef("true") : StringRef("false"));
148 std::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())
160 StringRef Value
= Iter
->getValue().Value
;
162 unsigned EditDistance
= 3;
163 for (const auto &NameAndEnum
: Mapping
) {
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
;
175 Value
.edit_distance(NameAndEnum
.second
, true, EditDistance
);
176 if (Distance
< EditDistance
) {
177 EditDistance
= Distance
;
178 Closest
= NameAndEnum
.second
;
181 if (EditDistance
< 3)
182 diagnoseBadEnumOption(Iter
->getKey(), Iter
->getValue().Value
, Closest
);
184 diagnoseBadEnumOption(Iter
->getKey(), Iter
->getValue().Value
);
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");
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;
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;
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())
214 Diag
<< 3 << Suggestion
;
217 StringRef
ClangTidyCheck::OptionsView::get(StringRef LocalName
,
218 StringRef Default
) const {
219 return get(LocalName
).value_or(Default
);
223 ClangTidyCheck::OptionsView::getLocalOrGlobal(StringRef LocalName
,
224 StringRef Default
) const {
225 return getLocalOrGlobal(LocalName
).value_or(Default
);
227 } // namespace clang::tidy