[RISCV] Add LUI/AUIPC+ADDI fusions to sifive-p450. (#78501)
[llvm-project.git] / clang-tools-extra / clang-tidy / ClangTidyCheck.cpp
blob3e926236adb4517245475d2f7b5f420268373e1a
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 //===----------------------------------------------------------------------===//
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"
14 #include <optional>
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);
36 DiagnosticBuilder
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.
46 check(Result);
49 ClangTidyCheck::OptionsView::OptionsView(
50 StringRef CheckName, const ClangTidyOptions::OptionMap &CheckOptions,
51 ClangTidyContext *Context)
52 : NamePrefix((CheckName + ".").str()), CheckOptions(CheckOptions),
53 Context(Context) {}
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);
62 return std::nullopt;
65 static ClangTidyOptions::OptionMap::const_iterator
66 findPriorityOption(const ClangTidyOptions::OptionMap &Options,
67 StringRef NamePrefix, StringRef LocalName,
68 llvm::StringSet<> *Collector) {
69 if (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())
76 return IterGlobal;
77 if (IterGlobal == Options.end())
78 return IterLocal;
79 if (IterLocal->getValue().Priority >= IterGlobal->getValue().Priority)
80 return IterLocal;
81 return IterGlobal;
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);
90 return std::nullopt;
93 static std::optional<bool> getAsBool(StringRef Value,
94 const llvm::Twine &LookupName) {
96 if (std::optional<bool> Parsed = llvm::yaml::parseBool(Value))
97 return Parsed;
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))
102 return Number != 0;
103 return std::nullopt;
106 template <>
107 std::optional<bool>
108 ClangTidyCheck::OptionsView::get<bool>(StringRef LocalName) const {
109 if (std::optional<StringRef> ValueOr = get(LocalName)) {
110 if (auto Result = getAsBool(*ValueOr, NamePrefix + LocalName))
111 return Result;
112 diagnoseBadBooleanOption(NamePrefix + LocalName, *ValueOr);
114 return std::nullopt;
117 template <>
118 std::optional<bool>
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()))
124 return Result;
125 diagnoseBadBooleanOption(Iter->getKey(), Iter->getValue().Value);
127 return std::nullopt;
130 void ClangTidyCheck::OptionsView::store(ClangTidyOptions::OptionMap &Options,
131 StringRef LocalName,
132 StringRef Value) const {
133 Options[(NamePrefix + LocalName).str()] = Value;
136 void ClangTidyCheck::OptionsView::storeInt(ClangTidyOptions::OptionMap &Options,
137 StringRef LocalName,
138 int64_t Value) const {
139 store(Options, LocalName, llvm::itostr(Value));
142 template <>
143 void ClangTidyCheck::OptionsView::store<bool>(
144 ClangTidyOptions::OptionMap &Options, StringRef LocalName,
145 bool Value) const {
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())
159 return std::nullopt;
161 StringRef Value = Iter->getValue().Value;
162 StringRef Closest;
163 unsigned EditDistance = 3;
164 for (const auto &NameAndEnum : Mapping) {
165 if (IgnoreCase) {
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;
172 EditDistance = 0;
173 continue;
175 unsigned Distance =
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);
184 else
185 diagnoseBadEnumOption(Iter->getKey(), Iter->getValue().Value);
186 return std::nullopt;
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())
213 Diag << 0;
214 else
215 Diag << 3 << Suggestion;
218 StringRef ClangTidyCheck::OptionsView::get(StringRef LocalName,
219 StringRef Default) const {
220 return get(LocalName).value_or(Default);
223 StringRef
224 ClangTidyCheck::OptionsView::getLocalOrGlobal(StringRef LocalName,
225 StringRef Default) const {
226 return getLocalOrGlobal(LocalName).value_or(Default);
228 } // namespace clang::tidy