llvm-project

Форк
0
/
ReservedIdentifierCheck.cpp 
208 строк · 7.5 Кб
1
//===--- ReservedIdentifierCheck.cpp - clang-tidy -------------------------===//
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 "ReservedIdentifierCheck.h"
10
#include "../utils/Matchers.h"
11
#include "../utils/OptionsUtils.h"
12
#include "clang/AST/ASTContext.h"
13
#include "clang/ASTMatchers/ASTMatchFinder.h"
14
#include "clang/Lex/Token.h"
15
#include <algorithm>
16
#include <cctype>
17
#include <optional>
18

19
// FixItHint
20

21
using namespace clang::ast_matchers;
22

23
namespace clang::tidy::bugprone {
24

25
static const char DoubleUnderscoreTag[] = "du";
26
static const char UnderscoreCapitalTag[] = "uc";
27
static const char GlobalUnderscoreTag[] = "global-under";
28
static const char NonReservedTag[] = "non-reserved";
29

30
static const char Message[] =
31
    "declaration uses identifier '%0', which is %select{a reserved "
32
    "identifier|not a reserved identifier|reserved in the global namespace}1";
33

34
static int getMessageSelectIndex(StringRef Tag) {
35
  if (Tag == NonReservedTag)
36
    return 1;
37
  if (Tag == GlobalUnderscoreTag)
38
    return 2;
39
  return 0;
40
}
41

42
llvm::SmallVector<llvm::Regex>
43
ReservedIdentifierCheck::parseAllowedIdentifiers() const {
44
  llvm::SmallVector<llvm::Regex> AllowedIdentifiers;
45
  AllowedIdentifiers.reserve(AllowedIdentifiersRaw.size());
46

47
  for (const auto &Identifier : AllowedIdentifiersRaw) {
48
    AllowedIdentifiers.emplace_back(Identifier.str());
49
    if (!AllowedIdentifiers.back().isValid()) {
50
      configurationDiag("Invalid allowed identifier regex '%0'") << Identifier;
51
      AllowedIdentifiers.pop_back();
52
    }
53
  }
54

55
  return AllowedIdentifiers;
56
}
57

58
ReservedIdentifierCheck::ReservedIdentifierCheck(StringRef Name,
59
                                                 ClangTidyContext *Context)
60
    : RenamerClangTidyCheck(Name, Context),
61
      Invert(Options.get("Invert", false)),
62
      AllowedIdentifiersRaw(utils::options::parseStringList(
63
          Options.get("AllowedIdentifiers", ""))),
64
      AllowedIdentifiers(parseAllowedIdentifiers()) {}
65

66
void ReservedIdentifierCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
67
  RenamerClangTidyCheck::storeOptions(Opts);
68
  Options.store(Opts, "Invert", Invert);
69
  Options.store(Opts, "AllowedIdentifiers",
70
                utils::options::serializeStringList(AllowedIdentifiersRaw));
71
}
72

73
static std::string collapseConsecutive(StringRef Str, char C) {
74
  std::string Result;
75
  std::unique_copy(Str.begin(), Str.end(), std::back_inserter(Result),
76
                   [C](char A, char B) { return A == C && B == C; });
77
  return Result;
78
}
79

80
static bool hasReservedDoubleUnderscore(StringRef Name,
81
                                        const LangOptions &LangOpts) {
82
  if (LangOpts.CPlusPlus)
83
    return Name.contains("__");
84
  return Name.starts_with("__");
85
}
86

87
static std::optional<std::string>
88
getDoubleUnderscoreFixup(StringRef Name, const LangOptions &LangOpts) {
89
  if (hasReservedDoubleUnderscore(Name, LangOpts))
90
    return collapseConsecutive(Name, '_');
91
  return std::nullopt;
92
}
93

94
static bool startsWithUnderscoreCapital(StringRef Name) {
95
  return Name.size() >= 2 && Name[0] == '_' && std::isupper(Name[1]);
96
}
97

98
static std::optional<std::string> getUnderscoreCapitalFixup(StringRef Name) {
99
  if (startsWithUnderscoreCapital(Name))
100
    return std::string(Name.drop_front(1));
101
  return std::nullopt;
102
}
103

104
static bool startsWithUnderscoreInGlobalNamespace(StringRef Name,
105
                                                  bool IsInGlobalNamespace,
106
                                                  bool IsMacro) {
107
  return !IsMacro && IsInGlobalNamespace && Name.starts_with("_");
108
}
109

110
static std::optional<std::string>
111
getUnderscoreGlobalNamespaceFixup(StringRef Name, bool IsInGlobalNamespace,
112
                                  bool IsMacro) {
113
  if (startsWithUnderscoreInGlobalNamespace(Name, IsInGlobalNamespace, IsMacro))
114
    return std::string(Name.drop_front(1));
115
  return std::nullopt;
116
}
117

118
static std::string getNonReservedFixup(std::string Name) {
119
  assert(!Name.empty());
120
  if (Name[0] == '_' || std::isupper(Name[0]))
121
    Name.insert(Name.begin(), '_');
122
  else
123
    Name.insert(Name.begin(), 2, '_');
124
  return Name;
125
}
126

127
static std::optional<RenamerClangTidyCheck::FailureInfo>
128
getFailureInfoImpl(StringRef Name, bool IsInGlobalNamespace, bool IsMacro,
129
                   const LangOptions &LangOpts, bool Invert,
130
                   ArrayRef<llvm::Regex> AllowedIdentifiers) {
131
  assert(!Name.empty());
132

133
  if (llvm::any_of(AllowedIdentifiers, [&](const llvm::Regex &Regex) {
134
        return Regex.match(Name);
135
      })) {
136
    return std::nullopt;
137
  }
138
  // TODO: Check for names identical to language keywords, and other names
139
  // specifically reserved by language standards, e.g. C++ 'zombie names' and C
140
  // future library directions
141

142
  using FailureInfo = RenamerClangTidyCheck::FailureInfo;
143
  if (!Invert) {
144
    std::optional<FailureInfo> Info;
145
    auto AppendFailure = [&](StringRef Kind, std::string &&Fixup) {
146
      if (!Info) {
147
        Info = FailureInfo{std::string(Kind), std::move(Fixup)};
148
      } else {
149
        Info->KindName += Kind;
150
        Info->Fixup = std::move(Fixup);
151
      }
152
    };
153
    auto InProgressFixup = [&] {
154
      return llvm::transformOptional(
155
                 Info,
156
                 [](const FailureInfo &Info) { return StringRef(Info.Fixup); })
157
          .value_or(Name);
158
    };
159
    if (auto Fixup = getDoubleUnderscoreFixup(InProgressFixup(), LangOpts))
160
      AppendFailure(DoubleUnderscoreTag, std::move(*Fixup));
161
    if (auto Fixup = getUnderscoreCapitalFixup(InProgressFixup()))
162
      AppendFailure(UnderscoreCapitalTag, std::move(*Fixup));
163
    if (auto Fixup = getUnderscoreGlobalNamespaceFixup(
164
            InProgressFixup(), IsInGlobalNamespace, IsMacro))
165
      AppendFailure(GlobalUnderscoreTag, std::move(*Fixup));
166

167
    return Info;
168
  }
169
  if (!(hasReservedDoubleUnderscore(Name, LangOpts) ||
170
        startsWithUnderscoreCapital(Name) ||
171
        startsWithUnderscoreInGlobalNamespace(Name, IsInGlobalNamespace,
172
                                              IsMacro)))
173
    return FailureInfo{NonReservedTag, getNonReservedFixup(std::string(Name))};
174
  return std::nullopt;
175
}
176

177
std::optional<RenamerClangTidyCheck::FailureInfo>
178
ReservedIdentifierCheck::getDeclFailureInfo(const NamedDecl *Decl,
179
                                            const SourceManager &) const {
180
  assert(Decl && Decl->getIdentifier() && !Decl->getName().empty() &&
181
         "Decl must be an explicit identifier with a name.");
182
  // Implicit identifiers cannot fail.
183
  if (Decl->isImplicit())
184
    return std::nullopt;
185

186
  return getFailureInfoImpl(
187
      Decl->getName(), isa<TranslationUnitDecl>(Decl->getDeclContext()),
188
      /*IsMacro = */ false, getLangOpts(), Invert, AllowedIdentifiers);
189
}
190

191
std::optional<RenamerClangTidyCheck::FailureInfo>
192
ReservedIdentifierCheck::getMacroFailureInfo(const Token &MacroNameTok,
193
                                             const SourceManager &) const {
194
  return getFailureInfoImpl(MacroNameTok.getIdentifierInfo()->getName(), true,
195
                            /*IsMacro = */ true, getLangOpts(), Invert,
196
                            AllowedIdentifiers);
197
}
198

199
RenamerClangTidyCheck::DiagInfo
200
ReservedIdentifierCheck::getDiagInfo(const NamingCheckId &ID,
201
                                     const NamingCheckFailure &Failure) const {
202
  return DiagInfo{Message, [&](DiagnosticBuilder &Diag) {
203
                    Diag << ID.second
204
                         << getMessageSelectIndex(Failure.Info.KindName);
205
                  }};
206
}
207

208
} // namespace clang::tidy::bugprone
209

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.