brintos

brintos / llvm-project-archived public Read only

0
0
Text · 7.5 KiB · 62e2245 Raw
207 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 "ReservedIdentifierCheck.h"10#include "../utils/OptionsUtils.h"11#include "clang/AST/ASTContext.h"12#include "clang/Lex/Token.h"13#include <algorithm>14#include <cctype>15#include <optional>16 17// FixItHint18 19using namespace clang::ast_matchers;20 21namespace clang::tidy::bugprone {22 23static const char DoubleUnderscoreTag[] = "du";24static const char UnderscoreCapitalTag[] = "uc";25static const char GlobalUnderscoreTag[] = "global-under";26static const char NonReservedTag[] = "non-reserved";27 28static const char Message[] =29    "declaration uses identifier '%0', which is %select{a reserved "30    "identifier|not a reserved identifier|reserved in the global namespace}1";31 32static int getMessageSelectIndex(StringRef Tag) {33  if (Tag == NonReservedTag)34    return 1;35  if (Tag == GlobalUnderscoreTag)36    return 2;37  return 0;38}39 40llvm::SmallVector<llvm::Regex>41ReservedIdentifierCheck::parseAllowedIdentifiers() const {42  llvm::SmallVector<llvm::Regex> AllowedIdentifiers;43  AllowedIdentifiers.reserve(AllowedIdentifiersRaw.size());44 45  for (const auto &Identifier : AllowedIdentifiersRaw) {46    AllowedIdentifiers.emplace_back(Identifier.str());47    if (!AllowedIdentifiers.back().isValid()) {48      configurationDiag("Invalid allowed identifier regex '%0'") << Identifier;49      AllowedIdentifiers.pop_back();50    }51  }52 53  return AllowedIdentifiers;54}55 56ReservedIdentifierCheck::ReservedIdentifierCheck(StringRef Name,57                                                 ClangTidyContext *Context)58    : RenamerClangTidyCheck(Name, Context),59      Invert(Options.get("Invert", false)),60      AllowedIdentifiersRaw(utils::options::parseStringList(61          Options.get("AllowedIdentifiers", ""))),62      AllowedIdentifiers(parseAllowedIdentifiers()) {}63 64void ReservedIdentifierCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {65  RenamerClangTidyCheck::storeOptions(Opts);66  Options.store(Opts, "Invert", Invert);67  Options.store(Opts, "AllowedIdentifiers",68                utils::options::serializeStringList(AllowedIdentifiersRaw));69}70 71static std::string collapseConsecutive(StringRef Str, char C) {72  std::string Result;73  std::unique_copy(Str.begin(), Str.end(), std::back_inserter(Result),74                   [C](char A, char B) { return A == C && B == C; });75  return Result;76}77 78static bool hasReservedDoubleUnderscore(StringRef Name,79                                        const LangOptions &LangOpts) {80  if (LangOpts.CPlusPlus)81    return Name.contains("__");82  return Name.starts_with("__");83}84 85static std::optional<std::string>86getDoubleUnderscoreFixup(StringRef Name, const LangOptions &LangOpts) {87  if (hasReservedDoubleUnderscore(Name, LangOpts))88    return collapseConsecutive(Name, '_');89  return std::nullopt;90}91 92static bool startsWithUnderscoreCapital(StringRef Name) {93  return Name.size() >= 2 && Name[0] == '_' && std::isupper(Name[1]);94}95 96static std::optional<std::string> getUnderscoreCapitalFixup(StringRef Name) {97  if (startsWithUnderscoreCapital(Name))98    return std::string(Name.drop_front(1));99  return std::nullopt;100}101 102static bool startsWithUnderscoreInGlobalNamespace(StringRef Name,103                                                  bool IsInGlobalNamespace,104                                                  bool IsMacro) {105  return !IsMacro && IsInGlobalNamespace && Name.starts_with("_");106}107 108static std::optional<std::string>109getUnderscoreGlobalNamespaceFixup(StringRef Name, bool IsInGlobalNamespace,110                                  bool IsMacro) {111  if (startsWithUnderscoreInGlobalNamespace(Name, IsInGlobalNamespace, IsMacro))112    return std::string(Name.drop_front(1));113  return std::nullopt;114}115 116static std::string getNonReservedFixup(std::string Name) {117  assert(!Name.empty());118  if (Name[0] == '_' || std::isupper(Name[0]))119    Name.insert(Name.begin(), '_');120  else121    Name.insert(Name.begin(), 2, '_');122  return Name;123}124 125static std::optional<RenamerClangTidyCheck::FailureInfo>126getFailureInfoImpl(StringRef Name, bool IsInGlobalNamespace, bool IsMacro,127                   const LangOptions &LangOpts, bool Invert,128                   ArrayRef<llvm::Regex> AllowedIdentifiers) {129  assert(!Name.empty());130 131  if (llvm::any_of(AllowedIdentifiers, [&](const llvm::Regex &Regex) {132        return Regex.match(Name);133      })) {134    return std::nullopt;135  }136  // TODO: Check for names identical to language keywords, and other names137  // specifically reserved by language standards, e.g. C++ 'zombie names' and C138  // future library directions139 140  using FailureInfo = RenamerClangTidyCheck::FailureInfo;141  if (!Invert) {142    std::optional<FailureInfo> Info;143    auto AppendFailure = [&](StringRef Kind, std::string &&Fixup) {144      if (!Info) {145        Info = FailureInfo{std::string(Kind), std::move(Fixup)};146      } else {147        Info->KindName += Kind;148        Info->Fixup = std::move(Fixup);149      }150    };151    auto InProgressFixup = [&] {152      return llvm::transformOptional(153                 Info,154                 [](const FailureInfo &Info) { return StringRef(Info.Fixup); })155          .value_or(Name);156    };157    if (auto Fixup = getDoubleUnderscoreFixup(InProgressFixup(), LangOpts))158      AppendFailure(DoubleUnderscoreTag, std::move(*Fixup));159    if (auto Fixup = getUnderscoreCapitalFixup(InProgressFixup()))160      AppendFailure(UnderscoreCapitalTag, std::move(*Fixup));161    if (auto Fixup = getUnderscoreGlobalNamespaceFixup(162            InProgressFixup(), IsInGlobalNamespace, IsMacro))163      AppendFailure(GlobalUnderscoreTag, std::move(*Fixup));164 165    return Info;166  }167  if (!(hasReservedDoubleUnderscore(Name, LangOpts) ||168        startsWithUnderscoreCapital(Name) ||169        startsWithUnderscoreInGlobalNamespace(Name, IsInGlobalNamespace,170                                              IsMacro)))171    return FailureInfo{NonReservedTag, getNonReservedFixup(std::string(Name))};172  return std::nullopt;173}174 175std::optional<RenamerClangTidyCheck::FailureInfo>176ReservedIdentifierCheck::getDeclFailureInfo(const NamedDecl *Decl,177                                            const SourceManager &) const {178  assert(Decl && Decl->getIdentifier() && !Decl->getName().empty() &&179         "Decl must be an explicit identifier with a name.");180  // Implicit identifiers cannot fail.181  if (Decl->isImplicit())182    return std::nullopt;183 184  return getFailureInfoImpl(185      Decl->getName(), isa<TranslationUnitDecl>(Decl->getDeclContext()),186      /*IsMacro = */ false, getLangOpts(), Invert, AllowedIdentifiers);187}188 189std::optional<RenamerClangTidyCheck::FailureInfo>190ReservedIdentifierCheck::getMacroFailureInfo(const Token &MacroNameTok,191                                             const SourceManager &) const {192  return getFailureInfoImpl(MacroNameTok.getIdentifierInfo()->getName(), true,193                            /*IsMacro = */ true, getLangOpts(), Invert,194                            AllowedIdentifiers);195}196 197RenamerClangTidyCheck::DiagInfo198ReservedIdentifierCheck::getDiagInfo(const NamingCheckId &ID,199                                     const NamingCheckFailure &Failure) const {200  return DiagInfo{Message, [&](DiagnosticBuilder &Diag) {201                    Diag << ID.second202                         << getMessageSelectIndex(Failure.Info.KindName);203                  }};204}205 206} // namespace clang::tidy::bugprone207