[docs] Add LICENSE.txt to the root of the mono-repo
[llvm-project.git] / clang-tools-extra / clang-tidy / bugprone / ReservedIdentifierCheck.cpp
blob75326e0ab1d19d08bf29e79f8a86f7744550114c
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 //===----------------------------------------------------------------------===//
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>
18 // FixItHint
20 using namespace clang::ast_matchers;
22 namespace clang {
23 namespace tidy {
24 namespace bugprone {
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";
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";
35 static int getMessageSelectIndex(StringRef Tag) {
36 if (Tag == NonReservedTag)
37 return 1;
38 if (Tag == GlobalUnderscoreTag)
39 return 2;
40 return 0;
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", ""))) {}
50 void ReservedIdentifierCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
51 RenamerClangTidyCheck::storeOptions(Opts);
52 Options.store(Opts, "Invert", Invert);
53 Options.store(Opts, "AllowedIdentifiers",
54 utils::options::serializeStringList(AllowedIdentifiers));
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;
64 static bool hasReservedDoubleUnderscore(StringRef Name,
65 const LangOptions &LangOpts) {
66 if (LangOpts.CPlusPlus)
67 return Name.contains("__");
68 return Name.startswith("__");
71 static Optional<std::string>
72 getDoubleUnderscoreFixup(StringRef Name, const LangOptions &LangOpts) {
73 if (hasReservedDoubleUnderscore(Name, LangOpts))
74 return collapseConsecutive(Name, '_');
75 return None;
78 static bool startsWithUnderscoreCapital(StringRef Name) {
79 return Name.size() >= 2 && Name[0] == '_' && std::isupper(Name[1]);
82 static Optional<std::string> getUnderscoreCapitalFixup(StringRef Name) {
83 if (startsWithUnderscoreCapital(Name))
84 return std::string(Name.drop_front(1));
85 return None;
88 static bool startsWithUnderscoreInGlobalNamespace(StringRef Name,
89 bool IsInGlobalNamespace) {
90 return IsInGlobalNamespace && Name.size() >= 1 && Name[0] == '_';
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;
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;
109 static Optional<RenamerClangTidyCheck::FailureInfo>
110 getFailureInfoImpl(StringRef Name, bool IsInGlobalNamespace,
111 const LangOptions &LangOpts, bool Invert,
112 ArrayRef<StringRef> AllowedIdentifiers) {
113 assert(!Name.empty());
114 if (llvm::is_contained(AllowedIdentifiers, Name))
115 return None;
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
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);
132 auto InProgressFixup = [&] {
133 return Info
134 .transform(
135 [](const FailureInfo &Info) { return StringRef(Info.Fixup); })
136 .value_or(Name);
138 if (auto Fixup = getDoubleUnderscoreFixup(InProgressFixup(), LangOpts))
139 AppendFailure(DoubleUnderscoreTag, std::move(*Fixup));
140 if (auto Fixup = getUnderscoreCapitalFixup(InProgressFixup()))
141 AppendFailure(UnderscoreCapitalTag, std::move(*Fixup));
142 if (auto Fixup = getUnderscoreGlobalNamespaceFixup(InProgressFixup(),
143 IsInGlobalNamespace))
144 AppendFailure(GlobalUnderscoreTag, std::move(*Fixup));
146 return Info;
148 if (!(hasReservedDoubleUnderscore(Name, LangOpts) ||
149 startsWithUnderscoreCapital(Name) ||
150 startsWithUnderscoreInGlobalNamespace(Name, IsInGlobalNamespace)))
151 return FailureInfo{NonReservedTag, getNonReservedFixup(std::string(Name))};
152 return None;
155 Optional<RenamerClangTidyCheck::FailureInfo>
156 ReservedIdentifierCheck::getDeclFailureInfo(const NamedDecl *Decl,
157 const SourceManager &) const {
158 assert(Decl && Decl->getIdentifier() && !Decl->getName().empty() &&
159 !Decl->isImplicit() &&
160 "Decl must be an explicit identifier with a name.");
161 return getFailureInfoImpl(Decl->getName(),
162 isa<TranslationUnitDecl>(Decl->getDeclContext()),
163 getLangOpts(), Invert, AllowedIdentifiers);
166 Optional<RenamerClangTidyCheck::FailureInfo>
167 ReservedIdentifierCheck::getMacroFailureInfo(const Token &MacroNameTok,
168 const SourceManager &) const {
169 return getFailureInfoImpl(MacroNameTok.getIdentifierInfo()->getName(), true,
170 getLangOpts(), Invert, AllowedIdentifiers);
173 RenamerClangTidyCheck::DiagInfo
174 ReservedIdentifierCheck::getDiagInfo(const NamingCheckId &ID,
175 const NamingCheckFailure &Failure) const {
176 return DiagInfo{Message, [&](DiagnosticBuilder &Diag) {
177 Diag << ID.second
178 << getMessageSelectIndex(Failure.Info.KindName);
182 } // namespace bugprone
183 } // namespace tidy
184 } // namespace clang