ClangTidyCheck.cpp 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227
  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. #include "ClangTidyCheck.h"
  9. #include "llvm/ADT/SmallString.h"
  10. #include "llvm/ADT/StringRef.h"
  11. #include "llvm/Support/Error.h"
  12. #include "llvm/Support/YAMLParser.h"
  13. #include <optional>
  14. namespace clang::tidy {
  15. ClangTidyCheck::ClangTidyCheck(StringRef CheckName, ClangTidyContext *Context)
  16. : CheckName(CheckName), Context(Context),
  17. Options(CheckName, Context->getOptions().CheckOptions, Context) {
  18. assert(Context != nullptr);
  19. assert(!CheckName.empty());
  20. }
  21. DiagnosticBuilder ClangTidyCheck::diag(SourceLocation Loc, StringRef Message,
  22. DiagnosticIDs::Level Level) {
  23. return Context->diag(CheckName, Loc, Message, Level);
  24. }
  25. DiagnosticBuilder ClangTidyCheck::diag(StringRef Message,
  26. DiagnosticIDs::Level Level) {
  27. return Context->diag(CheckName, Message, Level);
  28. }
  29. DiagnosticBuilder
  30. ClangTidyCheck::configurationDiag(StringRef Description,
  31. DiagnosticIDs::Level Level) const {
  32. return Context->configurationDiag(Description, Level);
  33. }
  34. void ClangTidyCheck::run(const ast_matchers::MatchFinder::MatchResult &Result) {
  35. // For historical reasons, checks don't implement the MatchFinder run()
  36. // callback directly. We keep the run()/check() distinction to avoid interface
  37. // churn, and to allow us to add cross-cutting logic in the future.
  38. check(Result);
  39. }
  40. ClangTidyCheck::OptionsView::OptionsView(
  41. StringRef CheckName, const ClangTidyOptions::OptionMap &CheckOptions,
  42. ClangTidyContext *Context)
  43. : NamePrefix((CheckName + ".").str()), CheckOptions(CheckOptions),
  44. Context(Context) {}
  45. std::optional<StringRef>
  46. ClangTidyCheck::OptionsView::get(StringRef LocalName) const {
  47. if (Context->getOptionsCollector())
  48. Context->getOptionsCollector()->insert((NamePrefix + LocalName).str());
  49. const auto &Iter = CheckOptions.find((NamePrefix + LocalName).str());
  50. if (Iter != CheckOptions.end())
  51. return StringRef(Iter->getValue().Value);
  52. return std::nullopt;
  53. }
  54. static ClangTidyOptions::OptionMap::const_iterator
  55. findPriorityOption(const ClangTidyOptions::OptionMap &Options,
  56. StringRef NamePrefix, StringRef LocalName,
  57. llvm::StringSet<> *Collector) {
  58. if (Collector) {
  59. Collector->insert((NamePrefix + LocalName).str());
  60. Collector->insert(LocalName);
  61. }
  62. auto IterLocal = Options.find((NamePrefix + LocalName).str());
  63. auto IterGlobal = Options.find(LocalName);
  64. if (IterLocal == Options.end())
  65. return IterGlobal;
  66. if (IterGlobal == Options.end())
  67. return IterLocal;
  68. if (IterLocal->getValue().Priority >= IterGlobal->getValue().Priority)
  69. return IterLocal;
  70. return IterGlobal;
  71. }
  72. std::optional<StringRef>
  73. ClangTidyCheck::OptionsView::getLocalOrGlobal(StringRef LocalName) const {
  74. auto Iter = findPriorityOption(CheckOptions, NamePrefix, LocalName,
  75. Context->getOptionsCollector());
  76. if (Iter != CheckOptions.end())
  77. return StringRef(Iter->getValue().Value);
  78. return std::nullopt;
  79. }
  80. static std::optional<bool> getAsBool(StringRef Value,
  81. const llvm::Twine &LookupName) {
  82. if (std::optional<bool> Parsed = llvm::yaml::parseBool(Value))
  83. return *Parsed;
  84. // To maintain backwards compatability, we support parsing numbers as
  85. // booleans, even though its not supported in YAML.
  86. long long Number;
  87. if (!Value.getAsInteger(10, Number))
  88. return Number != 0;
  89. return std::nullopt;
  90. }
  91. template <>
  92. std::optional<bool>
  93. ClangTidyCheck::OptionsView::get<bool>(StringRef LocalName) const {
  94. if (std::optional<StringRef> ValueOr = get(LocalName)) {
  95. if (auto Result = getAsBool(*ValueOr, NamePrefix + LocalName))
  96. return Result;
  97. diagnoseBadBooleanOption(NamePrefix + LocalName, *ValueOr);
  98. }
  99. return std::nullopt;
  100. }
  101. template <>
  102. std::optional<bool>
  103. ClangTidyCheck::OptionsView::getLocalOrGlobal<bool>(StringRef LocalName) const {
  104. auto Iter = findPriorityOption(CheckOptions, NamePrefix, LocalName,
  105. Context->getOptionsCollector());
  106. if (Iter != CheckOptions.end()) {
  107. if (auto Result = getAsBool(Iter->getValue().Value, Iter->getKey()))
  108. return Result;
  109. diagnoseBadBooleanOption(Iter->getKey(), Iter->getValue().Value);
  110. }
  111. return std::nullopt;
  112. }
  113. void ClangTidyCheck::OptionsView::store(ClangTidyOptions::OptionMap &Options,
  114. StringRef LocalName,
  115. StringRef Value) const {
  116. Options[(NamePrefix + LocalName).str()] = Value;
  117. }
  118. void ClangTidyCheck::OptionsView::storeInt(ClangTidyOptions::OptionMap &Options,
  119. StringRef LocalName,
  120. int64_t Value) const {
  121. store(Options, LocalName, llvm::itostr(Value));
  122. }
  123. template <>
  124. void ClangTidyCheck::OptionsView::store<bool>(
  125. ClangTidyOptions::OptionMap &Options, StringRef LocalName,
  126. bool Value) const {
  127. store(Options, LocalName, Value ? StringRef("true") : StringRef("false"));
  128. }
  129. std::optional<int64_t> ClangTidyCheck::OptionsView::getEnumInt(
  130. StringRef LocalName, ArrayRef<NameAndValue> Mapping, bool CheckGlobal,
  131. bool IgnoreCase) const {
  132. if (!CheckGlobal && Context->getOptionsCollector())
  133. Context->getOptionsCollector()->insert((NamePrefix + LocalName).str());
  134. auto Iter = CheckGlobal
  135. ? findPriorityOption(CheckOptions, NamePrefix, LocalName,
  136. Context->getOptionsCollector())
  137. : CheckOptions.find((NamePrefix + LocalName).str());
  138. if (Iter == CheckOptions.end())
  139. return std::nullopt;
  140. StringRef Value = Iter->getValue().Value;
  141. StringRef Closest;
  142. unsigned EditDistance = 3;
  143. for (const auto &NameAndEnum : Mapping) {
  144. if (IgnoreCase) {
  145. if (Value.equals_insensitive(NameAndEnum.second))
  146. return NameAndEnum.first;
  147. } else if (Value.equals(NameAndEnum.second)) {
  148. return NameAndEnum.first;
  149. } else if (Value.equals_insensitive(NameAndEnum.second)) {
  150. Closest = NameAndEnum.second;
  151. EditDistance = 0;
  152. continue;
  153. }
  154. unsigned Distance =
  155. Value.edit_distance(NameAndEnum.second, true, EditDistance);
  156. if (Distance < EditDistance) {
  157. EditDistance = Distance;
  158. Closest = NameAndEnum.second;
  159. }
  160. }
  161. if (EditDistance < 3)
  162. diagnoseBadEnumOption(Iter->getKey(), Iter->getValue().Value, Closest);
  163. else
  164. diagnoseBadEnumOption(Iter->getKey(), Iter->getValue().Value);
  165. return std::nullopt;
  166. }
  167. static constexpr llvm::StringLiteral ConfigWarning(
  168. "invalid configuration value '%0' for option '%1'%select{|; expected a "
  169. "bool|; expected an integer|; did you mean '%3'?}2");
  170. void ClangTidyCheck::OptionsView::diagnoseBadBooleanOption(
  171. const Twine &Lookup, StringRef Unparsed) const {
  172. SmallString<64> Buffer;
  173. Context->configurationDiag(ConfigWarning)
  174. << Unparsed << Lookup.toStringRef(Buffer) << 1;
  175. }
  176. void ClangTidyCheck::OptionsView::diagnoseBadIntegerOption(
  177. const Twine &Lookup, StringRef Unparsed) const {
  178. SmallString<64> Buffer;
  179. Context->configurationDiag(ConfigWarning)
  180. << Unparsed << Lookup.toStringRef(Buffer) << 2;
  181. }
  182. void ClangTidyCheck::OptionsView::diagnoseBadEnumOption(
  183. const Twine &Lookup, StringRef Unparsed, StringRef Suggestion) const {
  184. SmallString<64> Buffer;
  185. auto Diag = Context->configurationDiag(ConfigWarning)
  186. << Unparsed << Lookup.toStringRef(Buffer);
  187. if (Suggestion.empty())
  188. Diag << 0;
  189. else
  190. Diag << 3 << Suggestion;
  191. }
  192. StringRef ClangTidyCheck::OptionsView::get(StringRef LocalName,
  193. StringRef Default) const {
  194. return get(LocalName).value_or(Default);
  195. }
  196. StringRef
  197. ClangTidyCheck::OptionsView::getLocalOrGlobal(StringRef LocalName,
  198. StringRef Default) const {
  199. return getLocalOrGlobal(LocalName).value_or(Default);
  200. }
  201. } // namespace clang::tidy