• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/SmallString.h"
11 #include "llvm/ADT/StringRef.h"
12 #include "llvm/Support/Error.h"
13 #include "llvm/Support/WithColor.h"
14 #include "llvm/Support/raw_ostream.h"
15 
16 namespace clang {
17 namespace tidy {
18 
19 char MissingOptionError::ID;
20 char UnparseableEnumOptionError::ID;
21 char UnparseableIntegerOptionError::ID;
22 
message() const23 std::string MissingOptionError::message() const {
24   llvm::SmallString<128> Buffer({"option not found '", OptionName, "'"});
25   return std::string(Buffer);
26 }
27 
message() const28 std::string UnparseableEnumOptionError::message() const {
29   llvm::SmallString<256> Buffer({"invalid configuration value '", LookupValue,
30                                  "' for option '", LookupName, "'"});
31   if (SuggestedValue)
32     Buffer.append({"; did you mean '", *SuggestedValue, "'?"});
33   return std::string(Buffer);
34 }
35 
message() const36 std::string UnparseableIntegerOptionError::message() const {
37   llvm::SmallString<256> Buffer({"invalid configuration value '", LookupValue,
38                                  "' for option '", LookupName, "'; expected ",
39                                  (IsBoolean ? "a bool" : "an integer value")});
40   return std::string(Buffer);
41 }
42 
ClangTidyCheck(StringRef CheckName,ClangTidyContext * Context)43 ClangTidyCheck::ClangTidyCheck(StringRef CheckName, ClangTidyContext *Context)
44     : CheckName(CheckName), Context(Context),
45       Options(CheckName, Context->getOptions().CheckOptions, Context) {
46   assert(Context != nullptr);
47   assert(!CheckName.empty());
48 }
49 
diag(SourceLocation Loc,StringRef Message,DiagnosticIDs::Level Level)50 DiagnosticBuilder ClangTidyCheck::diag(SourceLocation Loc, StringRef Message,
51                                        DiagnosticIDs::Level Level) {
52   return Context->diag(CheckName, Loc, Message, Level);
53 }
54 
diag(StringRef Message,DiagnosticIDs::Level Level)55 DiagnosticBuilder ClangTidyCheck::diag(StringRef Message,
56                                        DiagnosticIDs::Level Level) {
57   return Context->diag(CheckName, Message, Level);
58 }
59 
60 DiagnosticBuilder
configurationDiag(StringRef Description,DiagnosticIDs::Level Level)61 ClangTidyCheck::configurationDiag(StringRef Description,
62                                   DiagnosticIDs::Level Level) {
63   return Context->configurationDiag(Description, Level);
64 }
65 
run(const ast_matchers::MatchFinder::MatchResult & Result)66 void ClangTidyCheck::run(const ast_matchers::MatchFinder::MatchResult &Result) {
67   // For historical reasons, checks don't implement the MatchFinder run()
68   // callback directly. We keep the run()/check() distinction to avoid interface
69   // churn, and to allow us to add cross-cutting logic in the future.
70   check(Result);
71 }
72 
OptionsView(StringRef CheckName,const ClangTidyOptions::OptionMap & CheckOptions,ClangTidyContext * Context)73 ClangTidyCheck::OptionsView::OptionsView(
74     StringRef CheckName, const ClangTidyOptions::OptionMap &CheckOptions,
75     ClangTidyContext *Context)
76     : NamePrefix(CheckName.str() + "."), CheckOptions(CheckOptions),
77       Context(Context) {}
78 
79 llvm::Expected<std::string>
get(StringRef LocalName) const80 ClangTidyCheck::OptionsView::get(StringRef LocalName) const {
81   const auto &Iter = CheckOptions.find(NamePrefix + LocalName.str());
82   if (Iter != CheckOptions.end())
83     return Iter->getValue().Value;
84   return llvm::make_error<MissingOptionError>((NamePrefix + LocalName).str());
85 }
86 
87 static ClangTidyOptions::OptionMap::const_iterator
findPriorityOption(const ClangTidyOptions::OptionMap & Options,StringRef NamePrefix,StringRef LocalName)88 findPriorityOption(const ClangTidyOptions::OptionMap &Options, StringRef NamePrefix,
89           StringRef LocalName) {
90   auto IterLocal = Options.find((NamePrefix + LocalName).str());
91   auto IterGlobal = Options.find(LocalName.str());
92   if (IterLocal == Options.end())
93     return IterGlobal;
94   if (IterGlobal == Options.end())
95     return IterLocal;
96   if (IterLocal->getValue().Priority >= IterGlobal->getValue().Priority)
97     return IterLocal;
98   return IterGlobal;
99 }
100 
101 llvm::Expected<std::string>
getLocalOrGlobal(StringRef LocalName) const102 ClangTidyCheck::OptionsView::getLocalOrGlobal(StringRef LocalName) const {
103   auto Iter = findPriorityOption(CheckOptions, NamePrefix, LocalName);
104   if (Iter != CheckOptions.end())
105     return Iter->getValue().Value;
106   return llvm::make_error<MissingOptionError>((NamePrefix + LocalName).str());
107 }
108 
getAsBool(StringRef Value,const llvm::Twine & LookupName)109 static llvm::Expected<bool> getAsBool(StringRef Value,
110                                       const llvm::Twine &LookupName) {
111   if (Value == "true")
112     return true;
113   if (Value == "false")
114     return false;
115   bool Result;
116   if (!Value.getAsInteger(10, Result))
117     return Result;
118   return llvm::make_error<UnparseableIntegerOptionError>(LookupName.str(),
119                                                          Value.str(), true);
120 }
121 
122 template <>
123 llvm::Expected<bool>
get(StringRef LocalName) const124 ClangTidyCheck::OptionsView::get<bool>(StringRef LocalName) const {
125   llvm::Expected<std::string> ValueOr = get(LocalName);
126   if (ValueOr)
127     return getAsBool(*ValueOr, NamePrefix + LocalName);
128   return ValueOr.takeError();
129 }
130 
131 template <>
get(StringRef LocalName,bool Default) const132 bool ClangTidyCheck::OptionsView::get<bool>(StringRef LocalName,
133                                             bool Default) const {
134   llvm::Expected<bool> ValueOr = get<bool>(LocalName);
135   if (ValueOr)
136     return *ValueOr;
137   reportOptionParsingError(ValueOr.takeError());
138   return Default;
139 }
140 
141 template <>
142 llvm::Expected<bool>
getLocalOrGlobal(StringRef LocalName) const143 ClangTidyCheck::OptionsView::getLocalOrGlobal<bool>(StringRef LocalName) const {
144   auto Iter = findPriorityOption(CheckOptions, NamePrefix, LocalName);
145   if (Iter != CheckOptions.end())
146     return getAsBool(Iter->getValue().Value, Iter->getKey());
147   return llvm::make_error<MissingOptionError>((NamePrefix + LocalName).str());
148 }
149 
150 template <>
getLocalOrGlobal(StringRef LocalName,bool Default) const151 bool ClangTidyCheck::OptionsView::getLocalOrGlobal<bool>(StringRef LocalName,
152                                                          bool Default) const {
153   llvm::Expected<bool> ValueOr = getLocalOrGlobal<bool>(LocalName);
154   if (ValueOr)
155     return *ValueOr;
156   reportOptionParsingError(ValueOr.takeError());
157   return Default;
158 }
159 
store(ClangTidyOptions::OptionMap & Options,StringRef LocalName,StringRef Value) const160 void ClangTidyCheck::OptionsView::store(ClangTidyOptions::OptionMap &Options,
161                                         StringRef LocalName,
162                                         StringRef Value) const {
163   Options[NamePrefix + LocalName.str()] = Value;
164 }
165 
storeInt(ClangTidyOptions::OptionMap & Options,StringRef LocalName,int64_t Value) const166 void ClangTidyCheck::OptionsView::storeInt(ClangTidyOptions::OptionMap &Options,
167                                            StringRef LocalName,
168                                            int64_t Value) const {
169   store(Options, LocalName, llvm::itostr(Value));
170 }
171 
172 template <>
store(ClangTidyOptions::OptionMap & Options,StringRef LocalName,bool Value) const173 void ClangTidyCheck::OptionsView::store<bool>(
174     ClangTidyOptions::OptionMap &Options, StringRef LocalName,
175     bool Value) const {
176   store(Options, LocalName, Value ? StringRef("true") : StringRef("false"));
177 }
178 
getEnumInt(StringRef LocalName,ArrayRef<NameAndValue> Mapping,bool CheckGlobal,bool IgnoreCase) const179 llvm::Expected<int64_t> ClangTidyCheck::OptionsView::getEnumInt(
180     StringRef LocalName, ArrayRef<NameAndValue> Mapping, bool CheckGlobal,
181     bool IgnoreCase) const {
182   auto Iter = CheckGlobal
183                   ? findPriorityOption(CheckOptions, NamePrefix, LocalName)
184                   : CheckOptions.find((NamePrefix + LocalName).str());
185   if (Iter == CheckOptions.end())
186     return llvm::make_error<MissingOptionError>((NamePrefix + LocalName).str());
187 
188   StringRef Value = Iter->getValue().Value;
189   StringRef Closest;
190   unsigned EditDistance = -1;
191   for (const auto &NameAndEnum : Mapping) {
192     if (IgnoreCase) {
193       if (Value.equals_lower(NameAndEnum.second))
194         return NameAndEnum.first;
195     } else if (Value.equals(NameAndEnum.second)) {
196       return NameAndEnum.first;
197     } else if (Value.equals_lower(NameAndEnum.second)) {
198       Closest = NameAndEnum.second;
199       EditDistance = 0;
200       continue;
201     }
202     unsigned Distance = Value.edit_distance(NameAndEnum.second);
203     if (Distance < EditDistance) {
204       EditDistance = Distance;
205       Closest = NameAndEnum.second;
206     }
207   }
208   if (EditDistance < 3)
209     return llvm::make_error<UnparseableEnumOptionError>(
210         Iter->getKey().str(), Iter->getValue().Value, Closest.str());
211   return llvm::make_error<UnparseableEnumOptionError>(Iter->getKey().str(),
212                                                       Iter->getValue().Value);
213 }
214 
reportOptionParsingError(llvm::Error && Err) const215 void ClangTidyCheck::OptionsView::reportOptionParsingError(
216     llvm::Error &&Err) const {
217   if (auto RemainingErrors =
218           llvm::handleErrors(std::move(Err), [](const MissingOptionError &) {}))
219     Context->configurationDiag(llvm::toString(std::move(RemainingErrors)));
220 }
221 
222 template <>
getOptional(StringRef LocalName) const223 Optional<std::string> ClangTidyCheck::OptionsView::getOptional<std::string>(
224     StringRef LocalName) const {
225   if (auto ValueOr = get(LocalName))
226     return *ValueOr;
227   else
228     consumeError(ValueOr.takeError());
229   return llvm::None;
230 }
231 
232 template <>
233 Optional<std::string>
getOptionalLocalOrGlobal(StringRef LocalName) const234 ClangTidyCheck::OptionsView::getOptionalLocalOrGlobal<std::string>(
235     StringRef LocalName) const {
236   if (auto ValueOr = getLocalOrGlobal(LocalName))
237     return *ValueOr;
238   else
239     consumeError(ValueOr.takeError());
240   return llvm::None;
241 }
242 
243 } // namespace tidy
244 } // namespace clang
245