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
,
26 StringRef Description
,
27 DiagnosticIDs::Level Level
) {
28 return Context
->diag(CheckName
, Loc
, Description
, Level
);
31 DiagnosticBuilder
ClangTidyCheck::diag(StringRef Description
,
32 DiagnosticIDs::Level Level
) {
33 return Context
->diag(CheckName
, Description
, Level
);
37 ClangTidyCheck::configurationDiag(StringRef Description
,
38 DiagnosticIDs::Level Level
) const {
39 return Context
->configurationDiag(Description
, Level
);
42 void 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.
49 ClangTidyCheck::OptionsView::OptionsView(
50 StringRef CheckName
, const ClangTidyOptions::OptionMap
&CheckOptions
,
51 ClangTidyContext
*Context
)
52 : NamePrefix((CheckName
+ ".").str()), CheckOptions(CheckOptions
),
55 std::optional
<StringRef
>
56 ClangTidyCheck::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
);
65 static ClangTidyOptions::OptionMap::const_iterator
66 findPriorityOption(const ClangTidyOptions::OptionMap
&Options
,
67 StringRef NamePrefix
, StringRef LocalName
,
68 llvm::StringSet
<> *Collector
) {
70 Collector
->insert((NamePrefix
+ LocalName
).str());
71 Collector
->insert(LocalName
);
73 auto IterLocal
= Options
.find((NamePrefix
+ LocalName
).str());
74 auto IterGlobal
= Options
.find(LocalName
);
75 if (IterLocal
== Options
.end())
77 if (IterGlobal
== Options
.end())
79 if (IterLocal
->getValue().Priority
>= IterGlobal
->getValue().Priority
)
84 std::optional
<StringRef
>
85 ClangTidyCheck::OptionsView::getLocalOrGlobal(StringRef LocalName
) const {
86 auto Iter
= findPriorityOption(CheckOptions
, NamePrefix
, LocalName
,
87 Context
->getOptionsCollector());
88 if (Iter
!= CheckOptions
.end())
89 return StringRef(Iter
->getValue().Value
);
93 static std::optional
<bool> getAsBool(StringRef Value
,
94 const llvm::Twine
&LookupName
) {
96 if (std::optional
<bool> Parsed
= llvm::yaml::parseBool(Value
))
98 // To maintain backwards compatability, we support parsing numbers as
99 // booleans, even though its not supported in YAML.
100 long long Number
= 0;
101 if (!Value
.getAsInteger(10, Number
))
108 ClangTidyCheck::OptionsView::get
<bool>(StringRef LocalName
) const {
109 if (std::optional
<StringRef
> ValueOr
= get(LocalName
)) {
110 if (auto Result
= getAsBool(*ValueOr
, NamePrefix
+ LocalName
))
112 diagnoseBadBooleanOption(NamePrefix
+ LocalName
, *ValueOr
);
119 ClangTidyCheck::OptionsView::getLocalOrGlobal
<bool>(StringRef LocalName
) const {
120 auto Iter
= findPriorityOption(CheckOptions
, NamePrefix
, LocalName
,
121 Context
->getOptionsCollector());
122 if (Iter
!= CheckOptions
.end()) {
123 if (auto Result
= getAsBool(Iter
->getValue().Value
, Iter
->getKey()))
125 diagnoseBadBooleanOption(Iter
->getKey(), Iter
->getValue().Value
);
130 void ClangTidyCheck::OptionsView::store(ClangTidyOptions::OptionMap
&Options
,
132 StringRef Value
) const {
133 Options
[(NamePrefix
+ LocalName
).str()] = Value
;
136 void ClangTidyCheck::OptionsView::storeInt(ClangTidyOptions::OptionMap
&Options
,
138 int64_t Value
) const {
139 store(Options
, LocalName
, llvm::itostr(Value
));
143 void ClangTidyCheck::OptionsView::store
<bool>(
144 ClangTidyOptions::OptionMap
&Options
, StringRef LocalName
,
146 store(Options
, LocalName
, Value
? StringRef("true") : StringRef("false"));
149 std::optional
<int64_t> ClangTidyCheck::OptionsView::getEnumInt(
150 StringRef LocalName
, ArrayRef
<NameAndValue
> Mapping
, bool CheckGlobal
,
151 bool IgnoreCase
) const {
152 if (!CheckGlobal
&& Context
->getOptionsCollector())
153 Context
->getOptionsCollector()->insert((NamePrefix
+ LocalName
).str());
154 auto Iter
= CheckGlobal
155 ? findPriorityOption(CheckOptions
, NamePrefix
, LocalName
,
156 Context
->getOptionsCollector())
157 : CheckOptions
.find((NamePrefix
+ LocalName
).str());
158 if (Iter
== CheckOptions
.end())
161 StringRef Value
= Iter
->getValue().Value
;
163 unsigned EditDistance
= 3;
164 for (const auto &NameAndEnum
: Mapping
) {
166 if (Value
.equals_insensitive(NameAndEnum
.second
))
167 return NameAndEnum
.first
;
168 } else if (Value
.equals(NameAndEnum
.second
)) {
169 return NameAndEnum
.first
;
170 } else if (Value
.equals_insensitive(NameAndEnum
.second
)) {
171 Closest
= NameAndEnum
.second
;
176 Value
.edit_distance(NameAndEnum
.second
, true, EditDistance
);
177 if (Distance
< EditDistance
) {
178 EditDistance
= Distance
;
179 Closest
= NameAndEnum
.second
;
182 if (EditDistance
< 3)
183 diagnoseBadEnumOption(Iter
->getKey(), Iter
->getValue().Value
, Closest
);
185 diagnoseBadEnumOption(Iter
->getKey(), Iter
->getValue().Value
);
189 static constexpr llvm::StringLiteral
ConfigWarning(
190 "invalid configuration value '%0' for option '%1'%select{|; expected a "
191 "bool|; expected an integer|; did you mean '%3'?}2");
193 void ClangTidyCheck::OptionsView::diagnoseBadBooleanOption(
194 const Twine
&Lookup
, StringRef Unparsed
) const {
195 SmallString
<64> Buffer
;
196 Context
->configurationDiag(ConfigWarning
)
197 << Unparsed
<< Lookup
.toStringRef(Buffer
) << 1;
200 void ClangTidyCheck::OptionsView::diagnoseBadIntegerOption(
201 const Twine
&Lookup
, StringRef Unparsed
) const {
202 SmallString
<64> Buffer
;
203 Context
->configurationDiag(ConfigWarning
)
204 << Unparsed
<< Lookup
.toStringRef(Buffer
) << 2;
207 void ClangTidyCheck::OptionsView::diagnoseBadEnumOption(
208 const Twine
&Lookup
, StringRef Unparsed
, StringRef Suggestion
) const {
209 SmallString
<64> Buffer
;
210 auto Diag
= Context
->configurationDiag(ConfigWarning
)
211 << Unparsed
<< Lookup
.toStringRef(Buffer
);
212 if (Suggestion
.empty())
215 Diag
<< 3 << Suggestion
;
218 StringRef
ClangTidyCheck::OptionsView::get(StringRef LocalName
,
219 StringRef Default
) const {
220 return get(LocalName
).value_or(Default
);
224 ClangTidyCheck::OptionsView::getLocalOrGlobal(StringRef LocalName
,
225 StringRef Default
) const {
226 return getLocalOrGlobal(LocalName
).value_or(Default
);
228 } // namespace clang::tidy