clang-tools 17.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#include <optional>
18
19// FixItHint
20
21using namespace clang::ast_matchers;
22
23namespace clang::tidy::bugprone {
24
25static const char DoubleUnderscoreTag[] = "du";
26static const char UnderscoreCapitalTag[] = "uc";
27static const char GlobalUnderscoreTag[] = "global-under";
28static const char NonReservedTag[] = "non-reserved";
29
30static 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
34static int getMessageSelectIndex(StringRef Tag) {
35 if (Tag == NonReservedTag)
36 return 1;
38 return 2;
39 return 0;
40}
41
43 ClangTidyContext *Context)
44 : RenamerClangTidyCheck(Name, Context),
45 Invert(Options.get("Invert", false)),
46 AllowedIdentifiers(utils::options::parseStringList(
47 Options.get("AllowedIdentifiers", ""))) {}
48
51 Options.store(Opts, "Invert", Invert);
52 Options.store(Opts, "AllowedIdentifiers",
53 utils::options::serializeStringList(AllowedIdentifiers));
54}
55
56static std::string collapseConsecutive(StringRef Str, char C) {
57 std::string Result;
58 std::unique_copy(Str.begin(), Str.end(), std::back_inserter(Result),
59 [C](char A, char B) { return A == C && B == C; });
60 return Result;
61}
62
63static bool hasReservedDoubleUnderscore(StringRef Name,
64 const LangOptions &LangOpts) {
65 if (LangOpts.CPlusPlus)
66 return Name.contains("__");
67 return Name.startswith("__");
68}
69
70static std::optional<std::string>
71getDoubleUnderscoreFixup(StringRef Name, const LangOptions &LangOpts) {
72 if (hasReservedDoubleUnderscore(Name, LangOpts))
73 return collapseConsecutive(Name, '_');
74 return std::nullopt;
75}
76
77static bool startsWithUnderscoreCapital(StringRef Name) {
78 return Name.size() >= 2 && Name[0] == '_' && std::isupper(Name[1]);
79}
80
81static std::optional<std::string> getUnderscoreCapitalFixup(StringRef Name) {
83 return std::string(Name.drop_front(1));
84 return std::nullopt;
85}
86
88 bool IsInGlobalNamespace) {
89 return IsInGlobalNamespace && Name.size() >= 1 && Name[0] == '_';
90}
91
92static std::optional<std::string>
93getUnderscoreGlobalNamespaceFixup(StringRef Name, bool IsInGlobalNamespace) {
94 if (startsWithUnderscoreInGlobalNamespace(Name, IsInGlobalNamespace))
95 return std::string(Name.drop_front(1));
96 return std::nullopt;
97}
98
99static std::string getNonReservedFixup(std::string Name) {
100 assert(!Name.empty());
101 if (Name[0] == '_' || std::isupper(Name[0]))
102 Name.insert(Name.begin(), '_');
103 else
104 Name.insert(Name.begin(), 2, '_');
105 return Name;
106}
107
108static std::optional<RenamerClangTidyCheck::FailureInfo>
109getFailureInfoImpl(StringRef Name, bool IsInGlobalNamespace,
110 const LangOptions &LangOpts, bool Invert,
111 ArrayRef<StringRef> AllowedIdentifiers) {
112 assert(!Name.empty());
113 if (llvm::is_contained(AllowedIdentifiers, Name))
114 return std::nullopt;
115
116 // TODO: Check for names identical to language keywords, and other names
117 // specifically reserved by language standards, e.g. C++ 'zombie names' and C
118 // future library directions
119
120 using FailureInfo = RenamerClangTidyCheck::FailureInfo;
121 if (!Invert) {
122 std::optional<FailureInfo> Info;
123 auto AppendFailure = [&](StringRef Kind, std::string &&Fixup) {
124 if (!Info) {
125 Info = FailureInfo{std::string(Kind), std::move(Fixup)};
126 } else {
127 Info->KindName += Kind;
128 Info->Fixup = std::move(Fixup);
129 }
130 };
131 auto InProgressFixup = [&] {
132 return llvm::transformOptional(
133 Info,
134 [](const FailureInfo &Info) { return StringRef(Info.Fixup); })
135 .value_or(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 std::nullopt;
152}
153
154std::optional<RenamerClangTidyCheck::FailureInfo>
155ReservedIdentifierCheck::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
165std::optional<RenamerClangTidyCheck::FailureInfo>
166ReservedIdentifierCheck::getMacroFailureInfo(const Token &MacroNameTok,
167 const SourceManager &) const {
168 return getFailureInfoImpl(MacroNameTok.getIdentifierInfo()->getName(), true,
169 getLangOpts(), Invert, AllowedIdentifiers);
170}
171
172RenamerClangTidyCheck::DiagInfo
173ReservedIdentifierCheck::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 clang::tidy::bugprone
const FunctionDecl * Decl
BindArgumentKind Kind
const Criteria C
FunctionInfo Info
HTMLTag Tag
Token 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.
const LangOptions & getLangOpts() const
Returns the language options from the context.
Every ClangTidyCheck reports errors through a DiagnosticsEngine provided by this context.
Base class for clang-tidy checks that want to flag declarations and/or macros for renaming based on c...
void storeOptions(ClangTidyOptions::OptionMap &Opts) override
Derived classes that override this function should call this method from the overridden method.
ReservedIdentifierCheck(StringRef Name, ClangTidyContext *Context)
void storeOptions(ClangTidyOptions::OptionMap &Opts) override
Should store all options supported by this check with their current values or default values for opti...
static std::optional< std::string > getDoubleUnderscoreFixup(StringRef Name, const LangOptions &LangOpts)
static const char DoubleUnderscoreTag[]
static const char GlobalUnderscoreTag[]
static std::optional< std::string > getUnderscoreGlobalNamespaceFixup(StringRef Name, bool IsInGlobalNamespace)
static const char UnderscoreCapitalTag[]
static std::string getNonReservedFixup(std::string Name)
static bool startsWithUnderscoreInGlobalNamespace(StringRef Name, bool IsInGlobalNamespace)
static std::optional< RenamerClangTidyCheck::FailureInfo > getFailureInfoImpl(StringRef Name, bool IsInGlobalNamespace, const LangOptions &LangOpts, bool Invert, ArrayRef< StringRef > AllowedIdentifiers)
static const char NonReservedTag[]
static std::string collapseConsecutive(StringRef Str, char C)
static bool hasReservedDoubleUnderscore(StringRef Name, const LangOptions &LangOpts)
static bool startsWithUnderscoreCapital(StringRef Name)
static std::optional< std::string > getUnderscoreCapitalFixup(StringRef Name)
static int getMessageSelectIndex(StringRef Tag)
std::string serializeStringList(ArrayRef< StringRef > Strings)
Serialize a sequence of names that can be parsed by parseStringList.
llvm::StringMap< ClangTidyValue > OptionMap
Information describing a failed check.