clang-tools  12.0.0git
ReservedIdentifierCheck.cpp
Go to the documentation of this file.
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 
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 
18 // FixItHint
19 
20 using namespace clang::ast_matchers;
21 
22 namespace clang {
23 namespace tidy {
24 namespace bugprone {
25 
26 static const char DoubleUnderscoreTag[] = "du";
27 static const char UnderscoreCapitalTag[] = "uc";
28 static const char GlobalUnderscoreTag[] = "global-under";
29 static const char NonReservedTag[] = "non-reserved";
30 
31 static const char Message[] =
32  "declaration uses identifier '%0', which is %select{a reserved "
33  "identifier|not a reserved identifier|reserved in the global namespace}1";
34 
35 static int getMessageSelectIndex(StringRef Tag) {
36  if (Tag == NonReservedTag)
37  return 1;
38  if (Tag == GlobalUnderscoreTag)
39  return 2;
40  return 0;
41 }
42 
43 ReservedIdentifierCheck::ReservedIdentifierCheck(StringRef Name,
44  ClangTidyContext *Context)
45  : RenamerClangTidyCheck(Name, Context),
46  Invert(Options.get("Invert", false)),
47  AllowedIdentifiers(utils::options::parseStringList(
48  Options.get("AllowedIdentifiers", ""))) {}
49 
52  Options.store(Opts, "Invert", Invert);
53  Options.store(Opts, "AllowedIdentifiers",
54  utils::options::serializeStringList(AllowedIdentifiers));
55 }
56 
57 static std::string collapseConsecutive(StringRef Str, char C) {
58  std::string Result;
59  std::unique_copy(Str.begin(), Str.end(), std::back_inserter(Result),
60  [C](char A, char B) { return A == C && B == C; });
61  return Result;
62 }
63 
64 static bool hasReservedDoubleUnderscore(StringRef Name,
65  const LangOptions &LangOpts) {
66  if (LangOpts.CPlusPlus)
67  return Name.find("__") != StringRef::npos;
68  return Name.startswith("__");
69 }
70 
71 static Optional<std::string>
72 getDoubleUnderscoreFixup(StringRef Name, const LangOptions &LangOpts) {
73  if (hasReservedDoubleUnderscore(Name, LangOpts))
74  return collapseConsecutive(Name, '_');
75  return None;
76 }
77 
78 static bool startsWithUnderscoreCapital(StringRef Name) {
79  return Name.size() >= 2 && Name[0] == '_' && std::isupper(Name[1]);
80 }
81 
82 static Optional<std::string> getUnderscoreCapitalFixup(StringRef Name) {
84  return std::string(Name.drop_front(1));
85  return None;
86 }
87 
89  bool IsInGlobalNamespace) {
90  return IsInGlobalNamespace && Name.size() >= 1 && Name[0] == '_';
91 }
92 
93 static Optional<std::string>
94 getUnderscoreGlobalNamespaceFixup(StringRef Name, bool IsInGlobalNamespace) {
95  if (startsWithUnderscoreInGlobalNamespace(Name, IsInGlobalNamespace))
96  return std::string(Name.drop_front(1));
97  return None;
98 }
99 
100 static std::string getNonReservedFixup(std::string Name) {
101  assert(!Name.empty());
102  if (Name[0] == '_' || std::isupper(Name[0]))
103  Name.insert(Name.begin(), '_');
104  else
105  Name.insert(Name.begin(), 2, '_');
106  return Name;
107 }
108 
109 static Optional<RenamerClangTidyCheck::FailureInfo>
110 getFailureInfoImpl(StringRef Name, bool IsInGlobalNamespace,
111  const LangOptions &LangOpts, bool Invert,
112  ArrayRef<std::string> AllowedIdentifiers) {
113  assert(!Name.empty());
114  if (llvm::is_contained(AllowedIdentifiers, Name))
115  return None;
116 
117  // TODO: Check for names identical to language keywords, and other names
118  // specifically reserved by language standards, e.g. C++ 'zombie names' and C
119  // future library directions
120 
121  using FailureInfo = RenamerClangTidyCheck::FailureInfo;
122  if (!Invert) {
123  Optional<FailureInfo> Info;
124  auto AppendFailure = [&](StringRef Kind, std::string &&Fixup) {
125  if (!Info) {
126  Info = FailureInfo{std::string(Kind), std::move(Fixup)};
127  } else {
128  Info->KindName += Kind;
129  Info->Fixup = std::move(Fixup);
130  }
131  };
132  auto InProgressFixup = [&] {
133  return Info
134  .map([](const FailureInfo &Info) { return StringRef(Info.Fixup); })
135  .getValueOr(Name);
136  };
137  if (auto Fixup = getDoubleUnderscoreFixup(InProgressFixup(), LangOpts))
138  AppendFailure(DoubleUnderscoreTag, std::move(*Fixup));
139  if (auto Fixup = getUnderscoreCapitalFixup(InProgressFixup()))
140  AppendFailure(UnderscoreCapitalTag, std::move(*Fixup));
141  if (auto Fixup = getUnderscoreGlobalNamespaceFixup(InProgressFixup(),
142  IsInGlobalNamespace))
143  AppendFailure(GlobalUnderscoreTag, std::move(*Fixup));
144 
145  return Info;
146  }
147  if (!(hasReservedDoubleUnderscore(Name, LangOpts) ||
149  startsWithUnderscoreInGlobalNamespace(Name, IsInGlobalNamespace)))
150  return FailureInfo{NonReservedTag, getNonReservedFixup(std::string(Name))};
151  return None;
152 }
153 
154 Optional<RenamerClangTidyCheck::FailureInfo>
155 ReservedIdentifierCheck::GetDeclFailureInfo(const NamedDecl *Decl,
156  const SourceManager &) const {
157  assert(Decl && Decl->getIdentifier() && !Decl->getName().empty() &&
158  !Decl->isImplicit() &&
159  "Decl must be an explicit identifier with a name.");
160  return getFailureInfoImpl(Decl->getName(),
161  isa<TranslationUnitDecl>(Decl->getDeclContext()),
162  getLangOpts(), Invert, AllowedIdentifiers);
163 }
164 
165 Optional<RenamerClangTidyCheck::FailureInfo>
166 ReservedIdentifierCheck::GetMacroFailureInfo(const Token &MacroNameTok,
167  const SourceManager &) const {
168  return getFailureInfoImpl(MacroNameTok.getIdentifierInfo()->getName(), true,
169  getLangOpts(), Invert, AllowedIdentifiers);
170 }
171 
173 ReservedIdentifierCheck::GetDiagInfo(const NamingCheckId &ID,
174  const NamingCheckFailure &Failure) const {
175  return DiagInfo{Message, [&](DiagnosticBuilder &diag) {
176  diag << ID.second
177  << getMessageSelectIndex(Failure.Info.KindName);
178  }};
179 }
180 
181 } // namespace bugprone
182 } // namespace tidy
183 } // namespace clang
const FunctionDecl * Decl
std::string serializeStringList(ArrayRef< std::string > Strings)
Serialize a sequence of names that can be parsed by parseStringList.
HTMLTag Tag
Information describing a failed check.
Represents customized diagnostic text and how arguments should be applied.
std::pair< SourceLocation, std::string > NamingCheckId
static Optional< RenamerClangTidyCheck::FailureInfo > getFailureInfoImpl(StringRef Name, bool IsInGlobalNamespace, const LangOptions &LangOpts, bool Invert, ArrayRef< std::string > AllowedIdentifiers)
static bool startsWithUnderscoreInGlobalNamespace(StringRef Name, bool IsInGlobalNamespace)
static std::string getNonReservedFixup(std::string Name)
const LangOptions & getLangOpts() const
Returns the language options from the context.
static bool startsWithUnderscoreCapital(StringRef Name)
static Optional< std::string > getUnderscoreGlobalNamespaceFixup(StringRef Name, bool IsInGlobalNamespace)
std::vector< std::string > parseStringList(StringRef Option)
Parse a semicolon separated list of strings.
static const char DoubleUnderscoreTag[]
static int getMessageSelectIndex(StringRef Tag)
BindArgumentKind Kind
static Optional< std::string > getDoubleUnderscoreFixup(StringRef Name, const LangOptions &LangOpts)
static Optional< std::string > getUnderscoreCapitalFixup(StringRef Name)
void store(ClangTidyOptions::OptionMap &Options, StringRef LocalName, StringRef Value) const
Stores an option with the check-local name LocalName with string value Value to Options.
static constexpr llvm::StringLiteral Name
void storeOptions(ClangTidyOptions::OptionMap &Opts) override
Derived classes that override this function should call this method from the overridden method...
Base class for clang-tidy checks that want to flag declarations and/or macros for renaming based on c...
FunctionInfo Info
static const char GlobalUnderscoreTag[]
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
void storeOptions(ClangTidyOptions::OptionMap &Opts) override
Should store all options supported by this check with their current values or default values for opti...
Every ClangTidyCheck reports errors through a DiagnosticsEngine provided by this context.
static std::string collapseConsecutive(StringRef Str, char C)
static const char NonReservedTag[]
std::map< std::string, ClangTidyValue > OptionMap
DiagnosticBuilder diag(SourceLocation Loc, StringRef Description, DiagnosticIDs::Level Level=DiagnosticIDs::Warning)
Add a diagnostic with the check&#39;s name.
static const char UnderscoreCapitalTag[]
static bool hasReservedDoubleUnderscore(StringRef Name, const LangOptions &LangOpts)