brintos

brintos / llvm-project-archived public Read only

0
0
Text · 8.1 KiB · b747657 Raw
230 lines · cpp
1//===----------------------------------------------------------------------===//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-exception6//7//===----------------------------------------------------------------------===//8 9#include "ClangTidyCheck.h"10#include "llvm/ADT/StringRef.h"11#include "llvm/ADT/StringSet.h"12#include "llvm/Support/YAMLParser.h"13#include <optional>14#include <string>15 16namespace clang::tidy {17 18ClangTidyCheck::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());23}24 25DiagnosticBuilder ClangTidyCheck::diag(SourceLocation Loc,26                                       StringRef Description,27                                       DiagnosticIDs::Level Level) {28  return Context->diag(CheckName, Loc, Description, Level);29}30 31DiagnosticBuilder ClangTidyCheck::diag(StringRef Description,32                                       DiagnosticIDs::Level Level) {33  return Context->diag(CheckName, Description, Level);34}35 36DiagnosticBuilder37ClangTidyCheck::configurationDiag(StringRef Description,38                                  DiagnosticIDs::Level Level) const {39  return Context->configurationDiag(Description, Level);40}41 42void 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 interface45  // churn, and to allow us to add cross-cutting logic in the future.46  check(Result);47}48 49ClangTidyCheck::OptionsView::OptionsView(50    StringRef CheckName, const ClangTidyOptions::OptionMap &CheckOptions,51    ClangTidyContext *Context)52    : NamePrefix((CheckName + ".").str()), CheckOptions(CheckOptions),53      Context(Context) {}54 55std::optional<StringRef>56ClangTidyCheck::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;63}64 65static ClangTidyOptions::OptionMap::const_iterator66findPriorityOption(const ClangTidyOptions::OptionMap &Options,67                   StringRef NamePrefix, StringRef LocalName,68                   ClangTidyContext *Context) {69  llvm::StringSet<> *Collector = Context->getOptionsCollector();70  if (Collector) {71    Collector->insert((NamePrefix + LocalName).str());72    Collector->insert(LocalName);73  }74  auto IterLocal = Options.find((NamePrefix + LocalName).str());75  auto IterGlobal = Options.find(LocalName);76  if (IterLocal == Options.end())77    return IterGlobal;78  if (IterGlobal == Options.end())79    return IterLocal;80  if (IterLocal->getValue().Priority >= IterGlobal->getValue().Priority)81    return IterLocal;82  return IterGlobal;83}84 85std::optional<StringRef>86ClangTidyCheck::OptionsView::getLocalOrGlobal(StringRef LocalName) const {87  auto Iter = findPriorityOption(CheckOptions, NamePrefix, LocalName, Context);88  if (Iter != CheckOptions.end())89    return StringRef(Iter->getValue().Value);90  return std::nullopt;91}92 93static std::optional<bool> getAsBool(StringRef Value) {94  if (std::optional<bool> Parsed = llvm::yaml::parseBool(Value))95    return Parsed;96  // To maintain backwards compatibility, we support parsing numbers as97  // booleans, even though its not supported in YAML.98  long long Number = 0;99  if (!Value.getAsInteger(10, Number))100    return Number != 0;101  return std::nullopt;102}103 104template <>105std::optional<bool>106ClangTidyCheck::OptionsView::get<bool>(StringRef LocalName) const {107  if (std::optional<StringRef> ValueOr = get(LocalName)) {108    if (auto Result = getAsBool(*ValueOr))109      return Result;110    diagnoseBadBooleanOption(NamePrefix + LocalName, *ValueOr);111  }112  return std::nullopt;113}114 115template <>116std::optional<bool>117ClangTidyCheck::OptionsView::getLocalOrGlobal<bool>(StringRef LocalName) const {118  auto Iter = findPriorityOption(CheckOptions, NamePrefix, LocalName, Context);119  if (Iter != CheckOptions.end()) {120    if (auto Result = getAsBool(Iter->getValue().Value))121      return Result;122    diagnoseBadBooleanOption(Iter->getKey(), Iter->getValue().Value);123  }124  return std::nullopt;125}126 127void ClangTidyCheck::OptionsView::store(ClangTidyOptions::OptionMap &Options,128                                        StringRef LocalName,129                                        StringRef Value) const {130  Options[(NamePrefix + LocalName).str()] = Value;131}132 133void ClangTidyCheck::OptionsView::storeInt(ClangTidyOptions::OptionMap &Options,134                                           StringRef LocalName,135                                           int64_t Value) const {136  store(Options, LocalName, llvm::itostr(Value));137}138 139void ClangTidyCheck::OptionsView::storeUnsigned(140    ClangTidyOptions::OptionMap &Options, StringRef LocalName,141    uint64_t Value) const {142  store(Options, LocalName, llvm::utostr(Value));143}144 145template <>146void ClangTidyCheck::OptionsView::store<bool>(147    ClangTidyOptions::OptionMap &Options, StringRef LocalName,148    bool Value) const {149  store(Options, LocalName, Value ? StringRef("true") : StringRef("false"));150}151 152std::optional<int64_t>153ClangTidyCheck::OptionsView::getEnumInt(StringRef LocalName,154                                        ArrayRef<NameAndValue> Mapping,155                                        bool CheckGlobal) const {156  if (!CheckGlobal && Context->getOptionsCollector())157    Context->getOptionsCollector()->insert((NamePrefix + LocalName).str());158  auto Iter = CheckGlobal ? findPriorityOption(CheckOptions, NamePrefix,159                                               LocalName, Context)160                          : CheckOptions.find((NamePrefix + LocalName).str());161  if (Iter == CheckOptions.end())162    return std::nullopt;163 164  const StringRef Value = Iter->getValue().Value;165  StringRef Closest;166  unsigned EditDistance = 3;167  for (const auto &NameAndEnum : Mapping) {168    if (Value == NameAndEnum.second) {169      return NameAndEnum.first;170    }171    if (Value.equals_insensitive(NameAndEnum.second)) {172      Closest = NameAndEnum.second;173      EditDistance = 0;174      continue;175    }176    const unsigned Distance =177        Value.edit_distance(NameAndEnum.second, true, EditDistance);178    if (Distance < EditDistance) {179      EditDistance = Distance;180      Closest = NameAndEnum.second;181    }182  }183  if (EditDistance < 3)184    diagnoseBadEnumOption(Iter->getKey(), Iter->getValue().Value, Closest);185  else186    diagnoseBadEnumOption(Iter->getKey(), Iter->getValue().Value);187  return std::nullopt;188}189 190static constexpr llvm::StringLiteral ConfigWarning(191    "invalid configuration value '%0' for option '%1'%select{|; expected a "192    "bool|; expected an integer|; did you mean '%3'?}2");193 194void ClangTidyCheck::OptionsView::diagnoseBadBooleanOption(195    const Twine &Lookup, StringRef Unparsed) const {196  SmallString<64> Buffer;197  Context->configurationDiag(ConfigWarning)198      << Unparsed << Lookup.toStringRef(Buffer) << 1;199}200 201void ClangTidyCheck::OptionsView::diagnoseBadIntegerOption(202    const Twine &Lookup, StringRef Unparsed) const {203  SmallString<64> Buffer;204  Context->configurationDiag(ConfigWarning)205      << Unparsed << Lookup.toStringRef(Buffer) << 2;206}207 208void ClangTidyCheck::OptionsView::diagnoseBadEnumOption(209    const Twine &Lookup, StringRef Unparsed, StringRef Suggestion) const {210  SmallString<64> Buffer;211  auto Diag = Context->configurationDiag(ConfigWarning)212              << Unparsed << Lookup.toStringRef(Buffer);213  if (Suggestion.empty())214    Diag << 0;215  else216    Diag << 3 << Suggestion;217}218 219StringRef ClangTidyCheck::OptionsView::get(StringRef LocalName,220                                           StringRef Default) const {221  return get(LocalName).value_or(Default);222}223 224StringRef225ClangTidyCheck::OptionsView::getLocalOrGlobal(StringRef LocalName,226                                              StringRef Default) const {227  return getLocalOrGlobal(LocalName).value_or(Default);228}229} // namespace clang::tidy230