1 //===--- IdentifierNamingCheck.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 "IdentifierNamingCheck.h"
10 
11 #include "../GlobList.h"
12 #include "clang/AST/CXXInheritance.h"
13 #include "clang/Lex/PPCallbacks.h"
14 #include "clang/Lex/Preprocessor.h"
15 #include "llvm/ADT/ArrayRef.h"
16 #include "llvm/ADT/DenseMapInfo.h"
17 #include "llvm/Support/Debug.h"
18 #include "llvm/Support/Error.h"
19 #include "llvm/Support/FormatVariadic.h"
20 #include "llvm/Support/Path.h"
21 #include "llvm/Support/Regex.h"
22 #include "llvm/Support/YAMLParser.h"
23 
24 #define DEBUG_TYPE "clang-tidy"
25 
26 // FixItHint
27 
28 using namespace clang::ast_matchers;
29 
30 namespace clang {
31 namespace tidy {
32 
33 llvm::ArrayRef<
34     std::pair<readability::IdentifierNamingCheck::CaseType, StringRef>>
35 OptionEnumMapping<
getEnumMapping()36     readability::IdentifierNamingCheck::CaseType>::getEnumMapping() {
37   static constexpr std::pair<readability::IdentifierNamingCheck::CaseType,
38                              StringRef>
39       Mapping[] = {
40           {readability::IdentifierNamingCheck::CT_AnyCase, "aNy_CasE"},
41           {readability::IdentifierNamingCheck::CT_LowerCase, "lower_case"},
42           {readability::IdentifierNamingCheck::CT_UpperCase, "UPPER_CASE"},
43           {readability::IdentifierNamingCheck::CT_CamelBack, "camelBack"},
44           {readability::IdentifierNamingCheck::CT_CamelCase, "CamelCase"},
45           {readability::IdentifierNamingCheck::CT_CamelSnakeCase,
46            "Camel_Snake_Case"},
47           {readability::IdentifierNamingCheck::CT_CamelSnakeBack,
48            "camel_Snake_Back"}};
49   return llvm::makeArrayRef(Mapping);
50 }
51 
52 template <>
53 struct OptionEnumMapping<
54     readability::IdentifierNamingCheck::HungarianPrefixType> {
55   using HungarianPrefixType =
56       readability::IdentifierNamingCheck::HungarianPrefixType;
57   static llvm::ArrayRef<std::pair<HungarianPrefixType, StringRef>>
getEnumMappingclang::tidy::OptionEnumMapping58   getEnumMapping() {
59     static constexpr std::pair<HungarianPrefixType, StringRef> Mapping[] = {
60         {HungarianPrefixType::HPT_Off, "Off"},
61         {HungarianPrefixType::HPT_On, "On"},
62         {HungarianPrefixType::HPT_LowerCase, "LowerCase"},
63         {HungarianPrefixType::HPT_CamelCase, "CamelCase"}};
64     return llvm::makeArrayRef(Mapping);
65   }
66 };
67 
68 namespace readability {
69 
70 // clang-format off
71 #define NAMING_KEYS(m) \
72     m(Namespace) \
73     m(InlineNamespace) \
74     m(EnumConstant) \
75     m(ScopedEnumConstant) \
76     m(ConstexprVariable) \
77     m(ConstantMember) \
78     m(PrivateMember) \
79     m(ProtectedMember) \
80     m(PublicMember) \
81     m(Member) \
82     m(ClassConstant) \
83     m(ClassMember) \
84     m(GlobalConstant) \
85     m(GlobalConstantPointer) \
86     m(GlobalPointer) \
87     m(GlobalVariable) \
88     m(LocalConstant) \
89     m(LocalConstantPointer) \
90     m(LocalPointer) \
91     m(LocalVariable) \
92     m(StaticConstant) \
93     m(StaticVariable) \
94     m(Constant) \
95     m(Variable) \
96     m(ConstantParameter) \
97     m(ParameterPack) \
98     m(Parameter) \
99     m(PointerParameter) \
100     m(ConstantPointerParameter) \
101     m(AbstractClass) \
102     m(Struct) \
103     m(Class) \
104     m(Union) \
105     m(Enum) \
106     m(GlobalFunction) \
107     m(ConstexprFunction) \
108     m(Function) \
109     m(ConstexprMethod) \
110     m(VirtualMethod) \
111     m(ClassMethod) \
112     m(PrivateMethod) \
113     m(ProtectedMethod) \
114     m(PublicMethod) \
115     m(Method) \
116     m(Typedef) \
117     m(TypeTemplateParameter) \
118     m(ValueTemplateParameter) \
119     m(TemplateTemplateParameter) \
120     m(TemplateParameter) \
121     m(TypeAlias) \
122     m(MacroDefinition) \
123     m(ObjcIvar) \
124 
125 enum StyleKind : int {
126 #define ENUMERATE(v) SK_ ## v,
127   NAMING_KEYS(ENUMERATE)
128 #undef ENUMERATE
129   SK_Count,
130   SK_Invalid
131 };
132 
133 static StringRef const StyleNames[] = {
134 #define STRINGIZE(v) #v,
135   NAMING_KEYS(STRINGIZE)
136 #undef STRINGIZE
137 };
138 
139 #define HUNGARIAN_NOTATION_PRIMITIVE_TYPES(m) \
140      m(int8_t) \
141      m(int16_t) \
142      m(int32_t) \
143      m(int64_t) \
144      m(uint8_t) \
145      m(uint16_t) \
146      m(uint32_t) \
147      m(uint64_t) \
148      m(char8_t) \
149      m(char16_t) \
150      m(char32_t) \
151      m(float) \
152      m(double) \
153      m(char) \
154      m(bool) \
155      m(_Bool) \
156      m(int) \
157      m(size_t) \
158      m(wchar_t) \
159      m(short-int) \
160      m(short) \
161      m(signed-int) \
162      m(signed-short) \
163      m(signed-short-int) \
164      m(signed-long-long-int) \
165      m(signed-long-long) \
166      m(signed-long-int) \
167      m(signed-long) \
168      m(signed) \
169      m(unsigned-long-long-int) \
170      m(unsigned-long-long) \
171      m(unsigned-long-int) \
172      m(unsigned-long) \
173      m(unsigned-short-int) \
174      m(unsigned-short) \
175      m(unsigned-int) \
176      m(unsigned) \
177      m(long-long-int) \
178      m(long-double) \
179      m(long-long) \
180      m(long-int) \
181      m(long) \
182      m(ptrdiff_t) \
183 
184 static StringRef const HungarainNotationPrimitiveTypes[] = {
185 #define STRINGIZE(v) #v,
186   HUNGARIAN_NOTATION_PRIMITIVE_TYPES(STRINGIZE)
187 #undef STRINGIZE
188 };
189 
190 #define HUNGARIAN_NOTATION_USER_DEFINED_TYPES(m) \
191      m(BOOL) \
192      m(BOOLEAN) \
193      m(BYTE) \
194      m(CHAR) \
195      m(UCHAR) \
196      m(SHORT) \
197      m(USHORT) \
198      m(WORD) \
199      m(DWORD) \
200      m(DWORD32) \
201      m(DWORD64) \
202      m(LONG) \
203      m(ULONG) \
204      m(ULONG32) \
205      m(ULONG64) \
206      m(ULONGLONG) \
207      m(HANDLE) \
208      m(INT) \
209      m(INT8) \
210      m(INT16) \
211      m(INT32) \
212      m(INT64) \
213      m(UINT) \
214      m(UINT8) \
215      m(UINT16) \
216      m(UINT32) \
217      m(UINT64) \
218      m(PVOID) \
219 
220 static StringRef const HungarainNotationUserDefinedTypes[] = {
221 #define STRINGIZE(v) #v,
222   HUNGARIAN_NOTATION_USER_DEFINED_TYPES(STRINGIZE)
223 #undef STRINGIZE
224 };
225 
226 
227 #undef NAMING_KEYS
228 // clang-format on
229 
NamingStyle(llvm::Optional<IdentifierNamingCheck::CaseType> Case,const std::string & Prefix,const std::string & Suffix,const std::string & IgnoredRegexpStr,HungarianPrefixType HPType)230 IdentifierNamingCheck::NamingStyle::NamingStyle(
231     llvm::Optional<IdentifierNamingCheck::CaseType> Case,
232     const std::string &Prefix, const std::string &Suffix,
233     const std::string &IgnoredRegexpStr, HungarianPrefixType HPType)
234     : Case(Case), Prefix(Prefix), Suffix(Suffix),
235       IgnoredRegexpStr(IgnoredRegexpStr), HPType(HPType) {
236   if (!IgnoredRegexpStr.empty()) {
237     IgnoredRegexp =
238         llvm::Regex(llvm::SmallString<128>({"^", IgnoredRegexpStr, "$"}));
239     if (!IgnoredRegexp.isValid())
240       llvm::errs() << "Invalid IgnoredRegexp regular expression: "
241                    << IgnoredRegexpStr;
242   }
243 }
244 
getFileStyleFromOptions(const ClangTidyCheck::OptionsView & Options) const245 IdentifierNamingCheck::FileStyle IdentifierNamingCheck::getFileStyleFromOptions(
246     const ClangTidyCheck::OptionsView &Options) const {
247   IdentifierNamingCheck::HungarianNotationOption HNOption;
248 
249   HungarianNotation.loadDefaultConfig(HNOption);
250   HungarianNotation.loadFileConfig(Options, HNOption);
251 
252   SmallVector<llvm::Optional<IdentifierNamingCheck::NamingStyle>, 0> Styles;
253   Styles.resize(SK_Count);
254   SmallString<64> StyleString;
255   for (unsigned I = 0; I < SK_Count; ++I) {
256     size_t StyleSize = StyleNames[I].size();
257     StyleString.assign({StyleNames[I], "HungarianPrefix"});
258 
259     auto HPTOpt =
260         Options.get<IdentifierNamingCheck::HungarianPrefixType>(StyleString);
261     if (HPTOpt && !HungarianNotation.checkOptionValid(I))
262       configurationDiag("invalid identifier naming option '%0'") << StyleString;
263 
264     memcpy(&StyleString[StyleSize], "IgnoredRegexp", 13);
265     StyleString.truncate(StyleSize + 13);
266     StringRef IgnoredRegexpStr = Options.get(StyleString, "");
267     memcpy(&StyleString[StyleSize], "Prefix", 6);
268     StyleString.truncate(StyleSize + 6);
269     std::string Prefix(Options.get(StyleString, ""));
270     // Fast replacement of [Pre]fix -> [Suf]fix.
271     memcpy(&StyleString[StyleSize], "Suf", 3);
272     std::string Postfix(Options.get(StyleString, ""));
273     memcpy(&StyleString[StyleSize], "Case", 4);
274     StyleString.pop_back_n(2);
275     auto CaseOptional =
276         Options.get<IdentifierNamingCheck::CaseType>(StyleString);
277 
278     if (CaseOptional || !Prefix.empty() || !Postfix.empty() ||
279         !IgnoredRegexpStr.empty() || HPTOpt)
280       Styles[I].emplace(std::move(CaseOptional), std::move(Prefix),
281                         std::move(Postfix), IgnoredRegexpStr.str(),
282                         HPTOpt.value_or(IdentifierNamingCheck::HPT_Off));
283   }
284   bool IgnoreMainLike = Options.get("IgnoreMainLikeFunctions", false);
285   return {std::move(Styles), std::move(HNOption), IgnoreMainLike};
286 }
287 
getDeclTypeName(const NamedDecl * ND) const288 std::string IdentifierNamingCheck::HungarianNotation::getDeclTypeName(
289     const NamedDecl *ND) const {
290   const auto *VD = dyn_cast<ValueDecl>(ND);
291   if (!VD)
292     return {};
293 
294   if (isa<FunctionDecl, EnumConstantDecl>(ND))
295     return {};
296 
297   // Get type text of variable declarations.
298   auto &SM = VD->getASTContext().getSourceManager();
299   const char *Begin = SM.getCharacterData(VD->getBeginLoc());
300   const char *End = SM.getCharacterData(VD->getEndLoc());
301   intptr_t StrLen = End - Begin;
302 
303   // FIXME: Sometimes the value that returns from ValDecl->getEndLoc()
304   // is wrong(out of location of Decl). This causes `StrLen` will be assigned
305   // an unexpected large value. Current workaround to find the terminated
306   // character instead of the `getEndLoc()` function.
307   const char *EOL = strchr(Begin, '\n');
308   if (!EOL)
309     EOL = Begin + strlen(Begin);
310 
311   const char *PosList[] = {strchr(Begin, '='), strchr(Begin, ';'),
312                            strchr(Begin, ','), strchr(Begin, ')'), EOL};
313   for (const auto &Pos : PosList) {
314     if (Pos > Begin)
315       EOL = std::min(EOL, Pos);
316   }
317 
318   StrLen = EOL - Begin;
319   std::string TypeName;
320   if (StrLen > 0) {
321     std::string Type(Begin, StrLen);
322 
323     static constexpr StringRef Keywords[] = {
324         // Constexpr specifiers
325         "constexpr", "constinit", "consteval",
326         // Qualifier
327         "const", "volatile", "restrict", "mutable",
328         // Storage class specifiers
329         "register", "static", "extern", "thread_local",
330         // Other keywords
331         "virtual"};
332 
333     // Remove keywords
334     for (StringRef Kw : Keywords) {
335       for (size_t Pos = 0;
336            (Pos = Type.find(Kw.data(), Pos)) != std::string::npos;) {
337         Type.replace(Pos, Kw.size(), "");
338       }
339     }
340     TypeName = Type.erase(0, Type.find_first_not_of(" "));
341 
342     // Replace spaces with single space.
343     for (size_t Pos = 0; (Pos = Type.find("  ", Pos)) != std::string::npos;
344          Pos += strlen(" ")) {
345       Type.replace(Pos, strlen("  "), " ");
346     }
347 
348     // Replace " &" with "&".
349     for (size_t Pos = 0; (Pos = Type.find(" &", Pos)) != std::string::npos;
350          Pos += strlen("&")) {
351       Type.replace(Pos, strlen(" &"), "&");
352     }
353 
354     // Replace " *" with "* ".
355     for (size_t Pos = 0; (Pos = Type.find(" *", Pos)) != std::string::npos;
356          Pos += strlen("*")) {
357       Type.replace(Pos, strlen(" *"), "* ");
358     }
359 
360     // Remove redundant tailing.
361     static constexpr StringRef TailsOfMultiWordType[] = {
362         " int", " char", " double", " long", " short"};
363     bool RedundantRemoved = false;
364     for (auto Kw : TailsOfMultiWordType) {
365       size_t Pos = Type.rfind(Kw.data());
366       if (Pos != std::string::npos) {
367         Type = Type.substr(0, Pos + Kw.size());
368         RedundantRemoved = true;
369         break;
370       }
371     }
372     TypeName = Type.erase(0, Type.find_first_not_of(" "));
373     if (!RedundantRemoved) {
374       std::size_t FoundSpace = Type.find(" ");
375       if (FoundSpace != std::string::npos)
376         Type = Type.substr(0, FoundSpace);
377     }
378 
379     TypeName = Type.erase(0, Type.find_first_not_of(" "));
380 
381     QualType QT = VD->getType();
382     if (!QT.isNull() && QT->isArrayType())
383       TypeName.append("[]");
384   }
385 
386   return TypeName;
387 }
388 
IdentifierNamingCheck(StringRef Name,ClangTidyContext * Context)389 IdentifierNamingCheck::IdentifierNamingCheck(StringRef Name,
390                                              ClangTidyContext *Context)
391     : RenamerClangTidyCheck(Name, Context), Context(Context), CheckName(Name),
392       GetConfigPerFile(Options.get("GetConfigPerFile", true)),
393       IgnoreFailedSplit(Options.get("IgnoreFailedSplit", false)) {
394 
395   auto IterAndInserted = NamingStylesCache.try_emplace(
396       llvm::sys::path::parent_path(Context->getCurrentFile()),
397       getFileStyleFromOptions(Options));
398   assert(IterAndInserted.second && "Couldn't insert Style");
399   // Holding a reference to the data in the vector is safe as it should never
400   // move.
401   MainFileStyle = &IterAndInserted.first->getValue();
402 }
403 
404 IdentifierNamingCheck::~IdentifierNamingCheck() = default;
405 
checkOptionValid(int StyleKindIndex) const406 bool IdentifierNamingCheck::HungarianNotation::checkOptionValid(
407     int StyleKindIndex) const {
408   if ((StyleKindIndex >= SK_EnumConstant) &&
409       (StyleKindIndex <= SK_ConstantParameter))
410     return true;
411 
412   if ((StyleKindIndex >= SK_Parameter) && (StyleKindIndex <= SK_Enum))
413     return true;
414 
415   return false;
416 }
417 
isOptionEnabled(StringRef OptionKey,const llvm::StringMap<std::string> & StrMap) const418 bool IdentifierNamingCheck::HungarianNotation::isOptionEnabled(
419     StringRef OptionKey, const llvm::StringMap<std::string> &StrMap) const {
420   if (OptionKey.empty())
421     return false;
422 
423   auto Iter = StrMap.find(OptionKey);
424   if (Iter == StrMap.end())
425     return false;
426 
427   llvm::Optional<bool> Parsed = llvm::yaml::parseBool(Iter->getValue());
428   return *Parsed;
429 }
430 
loadFileConfig(const ClangTidyCheck::OptionsView & Options,IdentifierNamingCheck::HungarianNotationOption & HNOption) const431 void IdentifierNamingCheck::HungarianNotation::loadFileConfig(
432     const ClangTidyCheck::OptionsView &Options,
433     IdentifierNamingCheck::HungarianNotationOption &HNOption) const {
434 
435   static constexpr StringRef HNOpts[] = {"TreatStructAsClass"};
436   static constexpr StringRef HNDerivedTypes[] = {"Array", "Pointer",
437                                                  "FunctionPointer"};
438 
439   StringRef Section = "HungarianNotation.";
440 
441   SmallString<128> Buffer = {Section, "General."};
442   size_t DefSize = Buffer.size();
443   for (const auto &Opt : HNOpts) {
444     Buffer.truncate(DefSize);
445     Buffer.append(Opt);
446     StringRef Val = Options.get(Buffer, "");
447     if (!Val.empty())
448       HNOption.General[Opt] = Val.str();
449   }
450 
451   Buffer = {Section, "DerivedType."};
452   DefSize = Buffer.size();
453   for (const auto &Type : HNDerivedTypes) {
454     Buffer.truncate(DefSize);
455     Buffer.append(Type);
456     StringRef Val = Options.get(Buffer, "");
457     if (!Val.empty())
458       HNOption.DerivedType[Type] = Val.str();
459   }
460 
461   static constexpr std::pair<StringRef, StringRef> HNCStrings[] = {
462       {"CharPrinter", "char*"},
463       {"CharArray", "char[]"},
464       {"WideCharPrinter", "wchar_t*"},
465       {"WideCharArray", "wchar_t[]"}};
466 
467   Buffer = {Section, "CString."};
468   DefSize = Buffer.size();
469   for (const auto &CStr : HNCStrings) {
470     Buffer.truncate(DefSize);
471     Buffer.append(CStr.first);
472     StringRef Val = Options.get(Buffer, "");
473     if (!Val.empty())
474       HNOption.CString[CStr.first] = Val.str();
475   }
476 
477   Buffer = {Section, "PrimitiveType."};
478   DefSize = Buffer.size();
479   for (const auto &PrimType : HungarainNotationPrimitiveTypes) {
480     Buffer.truncate(DefSize);
481     Buffer.append(PrimType);
482     StringRef Val = Options.get(Buffer, "");
483     if (!Val.empty()) {
484       std::string Type = PrimType.str();
485       std::replace(Type.begin(), Type.end(), '-', ' ');
486       HNOption.PrimitiveType[Type] = Val.str();
487     }
488   }
489 
490   Buffer = {Section, "UserDefinedType."};
491   DefSize = Buffer.size();
492   for (const auto &Type : HungarainNotationUserDefinedTypes) {
493     Buffer.truncate(DefSize);
494     Buffer.append(Type);
495     StringRef Val = Options.get(Buffer, "");
496     if (!Val.empty())
497       HNOption.UserDefinedType[Type] = Val.str();
498   }
499 }
500 
getPrefix(const Decl * D,const IdentifierNamingCheck::HungarianNotationOption & HNOption) const501 std::string IdentifierNamingCheck::HungarianNotation::getPrefix(
502     const Decl *D,
503     const IdentifierNamingCheck::HungarianNotationOption &HNOption) const {
504   if (!D)
505     return {};
506   const auto *ND = dyn_cast<NamedDecl>(D);
507   if (!ND)
508     return {};
509 
510   std::string Prefix;
511   if (const auto *ECD = dyn_cast<EnumConstantDecl>(ND)) {
512     Prefix = getEnumPrefix(ECD);
513   } else if (const auto *CRD = dyn_cast<CXXRecordDecl>(ND)) {
514     Prefix = getClassPrefix(CRD, HNOption);
515   } else if (isa<VarDecl, FieldDecl, RecordDecl>(ND)) {
516     std::string TypeName = getDeclTypeName(ND);
517     if (!TypeName.empty())
518       Prefix = getDataTypePrefix(TypeName, ND, HNOption);
519   }
520 
521   return Prefix;
522 }
523 
removeDuplicatedPrefix(SmallVector<StringRef,8> & Words,const IdentifierNamingCheck::HungarianNotationOption & HNOption) const524 bool IdentifierNamingCheck::HungarianNotation::removeDuplicatedPrefix(
525     SmallVector<StringRef, 8> &Words,
526     const IdentifierNamingCheck::HungarianNotationOption &HNOption) const {
527   if (Words.size() <= 1)
528     return true;
529 
530   std::string CorrectName = Words[0].str();
531   std::vector<llvm::StringMap<std::string>> MapList = {
532       HNOption.CString, HNOption.DerivedType, HNOption.PrimitiveType,
533       HNOption.UserDefinedType};
534 
535   for (const auto &Map : MapList) {
536     for (const auto &Str : Map) {
537       if (Str.getValue() == CorrectName) {
538         Words.erase(Words.begin(), Words.begin() + 1);
539         return true;
540       }
541     }
542   }
543 
544   return false;
545 }
546 
getDataTypePrefix(StringRef TypeName,const NamedDecl * ND,const IdentifierNamingCheck::HungarianNotationOption & HNOption) const547 std::string IdentifierNamingCheck::HungarianNotation::getDataTypePrefix(
548     StringRef TypeName, const NamedDecl *ND,
549     const IdentifierNamingCheck::HungarianNotationOption &HNOption) const {
550   if (!ND || TypeName.empty())
551     return TypeName.str();
552 
553   std::string ModifiedTypeName(TypeName);
554 
555   // Derived types
556   std::string PrefixStr;
557   if (const auto *TD = dyn_cast<ValueDecl>(ND)) {
558     QualType QT = TD->getType();
559     if (QT->isFunctionPointerType()) {
560       PrefixStr = HNOption.DerivedType.lookup("FunctionPointer");
561     } else if (QT->isPointerType()) {
562       for (const auto &CStr : HNOption.CString) {
563         std::string Key = CStr.getKey().str();
564         if (ModifiedTypeName.find(Key) == 0) {
565           PrefixStr = CStr.getValue();
566           ModifiedTypeName = ModifiedTypeName.substr(
567               Key.size(), ModifiedTypeName.size() - Key.size());
568           break;
569         }
570       }
571     } else if (QT->isArrayType()) {
572       for (const auto &CStr : HNOption.CString) {
573         std::string Key = CStr.getKey().str();
574         if (ModifiedTypeName.find(Key) == 0) {
575           PrefixStr = CStr.getValue();
576           break;
577         }
578       }
579       if (PrefixStr.empty())
580         PrefixStr = HNOption.DerivedType.lookup("Array");
581     } else if (QT->isReferenceType()) {
582       size_t Pos = ModifiedTypeName.find_last_of("&");
583       if (Pos != std::string::npos)
584         ModifiedTypeName = ModifiedTypeName.substr(0, Pos);
585     }
586   }
587 
588   // Pointers
589   size_t PtrCount = [&](std::string TypeName) -> size_t {
590     size_t Pos = TypeName.find('*');
591     size_t Count = 0;
592     for (; Pos < TypeName.length(); Pos++, Count++) {
593       if ('*' != TypeName[Pos])
594         break;
595     }
596     return Count;
597   }(ModifiedTypeName);
598   if (PtrCount > 0) {
599     ModifiedTypeName = [&](std::string Str, StringRef From, StringRef To) {
600       size_t StartPos = 0;
601       while ((StartPos = Str.find(From.data(), StartPos)) !=
602              std::string::npos) {
603         Str.replace(StartPos, From.size(), To.data());
604         StartPos += To.size();
605       }
606       return Str;
607     }(ModifiedTypeName, "*", "");
608   }
609 
610   // Primitive types
611   if (PrefixStr.empty()) {
612     for (const auto &Type : HNOption.PrimitiveType) {
613       if (ModifiedTypeName == Type.getKey()) {
614         PrefixStr = Type.getValue();
615         break;
616       }
617     }
618   }
619 
620   // User-Defined types
621   if (PrefixStr.empty()) {
622     for (const auto &Type : HNOption.UserDefinedType) {
623       if (ModifiedTypeName == Type.getKey()) {
624         PrefixStr = Type.getValue();
625         break;
626       }
627     }
628   }
629 
630   for (size_t Idx = 0; Idx < PtrCount; Idx++)
631     PrefixStr.insert(0, HNOption.DerivedType.lookup("Pointer"));
632 
633   return PrefixStr;
634 }
635 
getClassPrefix(const CXXRecordDecl * CRD,const IdentifierNamingCheck::HungarianNotationOption & HNOption) const636 std::string IdentifierNamingCheck::HungarianNotation::getClassPrefix(
637     const CXXRecordDecl *CRD,
638     const IdentifierNamingCheck::HungarianNotationOption &HNOption) const {
639 
640   if (CRD->isUnion())
641     return {};
642 
643   if (CRD->isStruct() &&
644       !isOptionEnabled("TreatStructAsClass", HNOption.General))
645     return {};
646 
647   return CRD->isAbstract() ? "I" : "C";
648 }
649 
getEnumPrefix(const EnumConstantDecl * ECD) const650 std::string IdentifierNamingCheck::HungarianNotation::getEnumPrefix(
651     const EnumConstantDecl *ECD) const {
652   std::string Name = ECD->getType().getAsString();
653   if (std::string::npos != Name.find("enum")) {
654     Name = Name.substr(strlen("enum"), Name.length() - strlen("enum"));
655     Name = Name.erase(0, Name.find_first_not_of(" "));
656   }
657 
658   static llvm::Regex Splitter(
659       "([a-z0-9A-Z]*)(_+)|([A-Z]?[a-z0-9]+)([A-Z]|$)|([A-Z]+)([A-Z]|$)");
660 
661   StringRef EnumName(Name);
662   SmallVector<StringRef, 8> Substrs;
663   EnumName.split(Substrs, "_", -1, false);
664 
665   SmallVector<StringRef, 8> Words;
666   SmallVector<StringRef, 8> Groups;
667   for (auto Substr : Substrs) {
668     while (!Substr.empty()) {
669       Groups.clear();
670       if (!Splitter.match(Substr, &Groups))
671         break;
672 
673       if (Groups[2].size() > 0) {
674         Words.push_back(Groups[1]);
675         Substr = Substr.substr(Groups[0].size());
676       } else if (Groups[3].size() > 0) {
677         Words.push_back(Groups[3]);
678         Substr = Substr.substr(Groups[0].size() - Groups[4].size());
679       } else if (Groups[5].size() > 0) {
680         Words.push_back(Groups[5]);
681         Substr = Substr.substr(Groups[0].size() - Groups[6].size());
682       }
683     }
684   }
685 
686   std::string Initial;
687   for (StringRef Word : Words)
688     Initial += tolower(Word[0]);
689 
690   return Initial;
691 }
692 
loadDefaultConfig(IdentifierNamingCheck::HungarianNotationOption & HNOption) const693 void IdentifierNamingCheck::HungarianNotation::loadDefaultConfig(
694     IdentifierNamingCheck::HungarianNotationOption &HNOption) const {
695 
696   // Options
697   static constexpr std::pair<StringRef, StringRef> General[] = {
698       {"TreatStructAsClass", "false"}};
699   for (const auto &G : General)
700     HNOption.General.try_emplace(G.first, G.second);
701 
702   // Derived types
703   static constexpr std::pair<StringRef, StringRef> DerivedTypes[] = {
704       {"Array", "a"}, {"Pointer", "p"}, {"FunctionPointer", "fn"}};
705   for (const auto &DT : DerivedTypes)
706     HNOption.DerivedType.try_emplace(DT.first, DT.second);
707 
708   // C strings
709   static constexpr std::pair<StringRef, StringRef> CStrings[] = {
710       {"char*", "sz"},
711       {"char[]", "sz"},
712       {"wchar_t*", "wsz"},
713       {"wchar_t[]", "wsz"}};
714   for (const auto &CStr : CStrings)
715     HNOption.CString.try_emplace(CStr.first, CStr.second);
716 
717   // clang-format off
718   static constexpr std::pair<StringRef, StringRef> PrimitiveTypes[] = {
719         {"int8_t",                  "i8"  },
720         {"int16_t",                 "i16" },
721         {"int32_t",                 "i32" },
722         {"int64_t",                 "i64" },
723         {"uint8_t",                 "u8"  },
724         {"uint16_t",                "u16" },
725         {"uint32_t",                "u32" },
726         {"uint64_t",                "u64" },
727         {"char8_t",                 "c8"  },
728         {"char16_t",                "c16" },
729         {"char32_t",                "c32" },
730         {"float",                   "f"   },
731         {"double",                  "d"   },
732         {"char",                    "c"   },
733         {"bool",                    "b"   },
734         {"_Bool",                   "b"   },
735         {"int",                     "i"   },
736         {"size_t",                  "n"   },
737         {"wchar_t",                 "wc"  },
738         {"short int",               "si"  },
739         {"short",                   "s"   },
740         {"signed int",              "si"  },
741         {"signed short",            "ss"  },
742         {"signed short int",        "ssi" },
743         {"signed long long int",    "slli"},
744         {"signed long long",        "sll" },
745         {"signed long int",         "sli" },
746         {"signed long",             "sl"  },
747         {"signed",                  "s"   },
748         {"unsigned long long int",  "ulli"},
749         {"unsigned long long",      "ull" },
750         {"unsigned long int",       "uli" },
751         {"unsigned long",           "ul"  },
752         {"unsigned short int",      "usi" },
753         {"unsigned short",          "us"  },
754         {"unsigned int",            "ui"  },
755         {"unsigned",                "u"   },
756         {"long long int",           "lli" },
757         {"long double",             "ld"  },
758         {"long long",               "ll"  },
759         {"long int",                "li"  },
760         {"long",                    "l"   },
761         {"ptrdiff_t",               "p"   }};
762   // clang-format on
763   for (const auto &PT : PrimitiveTypes)
764     HNOption.PrimitiveType.try_emplace(PT.first, PT.second);
765 
766   // clang-format off
767   static constexpr std::pair<StringRef, StringRef> UserDefinedTypes[] = {
768       // Windows data types
769       {"BOOL",                    "b"   },
770       {"BOOLEAN",                 "b"   },
771       {"BYTE",                    "by"  },
772       {"CHAR",                    "c"   },
773       {"UCHAR",                   "uc"  },
774       {"SHORT",                   "s"   },
775       {"USHORT",                  "us"  },
776       {"WORD",                    "w"   },
777       {"DWORD",                   "dw"  },
778       {"DWORD32",                 "dw32"},
779       {"DWORD64",                 "dw64"},
780       {"LONG",                    "l"   },
781       {"ULONG",                   "ul"  },
782       {"ULONG32",                 "ul32"},
783       {"ULONG64",                 "ul64"},
784       {"ULONGLONG",               "ull" },
785       {"HANDLE",                  "h"   },
786       {"INT",                     "i"   },
787       {"INT8",                    "i8"  },
788       {"INT16",                   "i16" },
789       {"INT32",                   "i32" },
790       {"INT64",                   "i64" },
791       {"UINT",                    "ui"  },
792       {"UINT8",                   "u8"  },
793       {"UINT16",                  "u16" },
794       {"UINT32",                  "u32" },
795       {"UINT64",                  "u64" },
796       {"PVOID",                   "p"   } };
797   // clang-format on
798   for (const auto &UDT : UserDefinedTypes)
799     HNOption.UserDefinedType.try_emplace(UDT.first, UDT.second);
800 }
801 
storeOptions(ClangTidyOptions::OptionMap & Opts)802 void IdentifierNamingCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
803   RenamerClangTidyCheck::storeOptions(Opts);
804   SmallString<64> StyleString;
805   ArrayRef<llvm::Optional<NamingStyle>> Styles = MainFileStyle->getStyles();
806   for (size_t I = 0; I < SK_Count; ++I) {
807     if (!Styles[I])
808       continue;
809     size_t StyleSize = StyleNames[I].size();
810     StyleString.assign({StyleNames[I], "HungarianPrefix"});
811 
812     Options.store(Opts, StyleString, Styles[I]->HPType);
813 
814     memcpy(&StyleString[StyleSize], "IgnoredRegexp", 13);
815     StyleString.truncate(StyleSize + 13);
816     Options.store(Opts, StyleString, Styles[I]->IgnoredRegexpStr);
817     memcpy(&StyleString[StyleSize], "Prefix", 6);
818     StyleString.truncate(StyleSize + 6);
819     Options.store(Opts, StyleString, Styles[I]->Prefix);
820     // Fast replacement of [Pre]fix -> [Suf]fix.
821     memcpy(&StyleString[StyleSize], "Suf", 3);
822     Options.store(Opts, StyleString, Styles[I]->Suffix);
823     if (Styles[I]->Case) {
824       memcpy(&StyleString[StyleSize], "Case", 4);
825       StyleString.pop_back_n(2);
826       Options.store(Opts, StyleString, *Styles[I]->Case);
827     }
828   }
829   Options.store(Opts, "GetConfigPerFile", GetConfigPerFile);
830   Options.store(Opts, "IgnoreFailedSplit", IgnoreFailedSplit);
831   Options.store(Opts, "IgnoreMainLikeFunctions",
832                 MainFileStyle->isIgnoringMainLikeFunction());
833 }
834 
matchesStyle(StringRef Type,StringRef Name,const IdentifierNamingCheck::NamingStyle & Style,const IdentifierNamingCheck::HungarianNotationOption & HNOption,const NamedDecl * Decl) const835 bool IdentifierNamingCheck::matchesStyle(
836     StringRef Type, StringRef Name,
837     const IdentifierNamingCheck::NamingStyle &Style,
838     const IdentifierNamingCheck::HungarianNotationOption &HNOption,
839     const NamedDecl *Decl) const {
840   static llvm::Regex Matchers[] = {
841       llvm::Regex("^.*$"),
842       llvm::Regex("^[a-z][a-z0-9_]*$"),
843       llvm::Regex("^[a-z][a-zA-Z0-9]*$"),
844       llvm::Regex("^[A-Z][A-Z0-9_]*$"),
845       llvm::Regex("^[A-Z][a-zA-Z0-9]*$"),
846       llvm::Regex("^[A-Z]([a-z0-9]*(_[A-Z])?)*"),
847       llvm::Regex("^[a-z]([a-z0-9]*(_[A-Z])?)*"),
848   };
849 
850   if (!Name.consume_front(Style.Prefix))
851     return false;
852   if (!Name.consume_back(Style.Suffix))
853     return false;
854   if (IdentifierNamingCheck::HungarianPrefixType::HPT_Off != Style.HPType) {
855     std::string HNPrefix = HungarianNotation.getPrefix(Decl, HNOption);
856     if (!Name.consume_front(HNPrefix))
857       return false;
858   }
859 
860   // Ensure the name doesn't have any extra underscores beyond those specified
861   // in the prefix and suffix.
862   if (Name.startswith("_") || Name.endswith("_"))
863     return false;
864 
865   if (Style.Case && !Matchers[static_cast<size_t>(*Style.Case)].match(Name))
866     return false;
867 
868   return true;
869 }
870 
fixupWithCase(StringRef Type,StringRef Name,const Decl * D,const IdentifierNamingCheck::NamingStyle & Style,const IdentifierNamingCheck::HungarianNotationOption & HNOption,IdentifierNamingCheck::CaseType Case) const871 std::string IdentifierNamingCheck::fixupWithCase(
872     StringRef Type, StringRef Name, const Decl *D,
873     const IdentifierNamingCheck::NamingStyle &Style,
874     const IdentifierNamingCheck::HungarianNotationOption &HNOption,
875     IdentifierNamingCheck::CaseType Case) const {
876   static llvm::Regex Splitter(
877       "([a-z0-9A-Z]*)(_+)|([A-Z]?[a-z0-9]+)([A-Z]|$)|([A-Z]+)([A-Z]|$)");
878 
879   SmallVector<StringRef, 8> Substrs;
880   Name.split(Substrs, "_", -1, false);
881 
882   SmallVector<StringRef, 8> Words;
883   SmallVector<StringRef, 8> Groups;
884   for (auto Substr : Substrs) {
885     while (!Substr.empty()) {
886       Groups.clear();
887       if (!Splitter.match(Substr, &Groups))
888         break;
889 
890       if (Groups[2].size() > 0) {
891         Words.push_back(Groups[1]);
892         Substr = Substr.substr(Groups[0].size());
893       } else if (Groups[3].size() > 0) {
894         Words.push_back(Groups[3]);
895         Substr = Substr.substr(Groups[0].size() - Groups[4].size());
896       } else if (Groups[5].size() > 0) {
897         Words.push_back(Groups[5]);
898         Substr = Substr.substr(Groups[0].size() - Groups[6].size());
899       }
900     }
901   }
902 
903   if (Words.empty())
904     return Name.str();
905 
906   if (IdentifierNamingCheck::HungarianPrefixType::HPT_Off != Style.HPType) {
907     HungarianNotation.removeDuplicatedPrefix(Words, HNOption);
908   }
909 
910   SmallString<128> Fixup;
911   switch (Case) {
912   case IdentifierNamingCheck::CT_AnyCase:
913     return Name.str();
914     break;
915 
916   case IdentifierNamingCheck::CT_LowerCase:
917     for (auto const &Word : Words) {
918       if (&Word != &Words.front())
919         Fixup += "_";
920       Fixup += Word.lower();
921     }
922     break;
923 
924   case IdentifierNamingCheck::CT_UpperCase:
925     for (auto const &Word : Words) {
926       if (&Word != &Words.front())
927         Fixup += "_";
928       Fixup += Word.upper();
929     }
930     break;
931 
932   case IdentifierNamingCheck::CT_CamelCase:
933     for (auto const &Word : Words) {
934       Fixup += toupper(Word.front());
935       Fixup += Word.substr(1).lower();
936     }
937     break;
938 
939   case IdentifierNamingCheck::CT_CamelBack:
940     for (auto const &Word : Words) {
941       if (&Word == &Words.front()) {
942         Fixup += Word.lower();
943       } else {
944         Fixup += toupper(Word.front());
945         Fixup += Word.substr(1).lower();
946       }
947     }
948     break;
949 
950   case IdentifierNamingCheck::CT_CamelSnakeCase:
951     for (auto const &Word : Words) {
952       if (&Word != &Words.front())
953         Fixup += "_";
954       Fixup += toupper(Word.front());
955       Fixup += Word.substr(1).lower();
956     }
957     break;
958 
959   case IdentifierNamingCheck::CT_CamelSnakeBack:
960     for (auto const &Word : Words) {
961       if (&Word != &Words.front()) {
962         Fixup += "_";
963         Fixup += toupper(Word.front());
964       } else {
965         Fixup += tolower(Word.front());
966       }
967       Fixup += Word.substr(1).lower();
968     }
969     break;
970   }
971 
972   return Fixup.str().str();
973 }
974 
isParamInMainLikeFunction(const ParmVarDecl & ParmDecl,bool IncludeMainLike) const975 bool IdentifierNamingCheck::isParamInMainLikeFunction(
976     const ParmVarDecl &ParmDecl, bool IncludeMainLike) const {
977   const auto *FDecl =
978       dyn_cast_or_null<FunctionDecl>(ParmDecl.getParentFunctionOrMethod());
979   if (!FDecl)
980     return false;
981   if (FDecl->isMain())
982     return true;
983   if (!IncludeMainLike)
984     return false;
985   if (FDecl->getAccess() != AS_public && FDecl->getAccess() != AS_none)
986     return false;
987   // If the function doesn't have a name that's an identifier, can occur if the
988   // function is an operator overload, bail out early.
989   if (!FDecl->getDeclName().isIdentifier())
990     return false;
991   enum MainType { None, Main, WMain };
992   auto IsCharPtrPtr = [](QualType QType) -> MainType {
993     if (QType.isNull())
994       return None;
995     if (QType = QType->getPointeeType(), QType.isNull())
996       return None;
997     if (QType = QType->getPointeeType(), QType.isNull())
998       return None;
999     if (QType->isCharType())
1000       return Main;
1001     if (QType->isWideCharType())
1002       return WMain;
1003     return None;
1004   };
1005   auto IsIntType = [](QualType QType) {
1006     if (QType.isNull())
1007       return false;
1008     if (const auto *Builtin =
1009             dyn_cast<BuiltinType>(QType->getUnqualifiedDesugaredType())) {
1010       return Builtin->getKind() == BuiltinType::Int;
1011     }
1012     return false;
1013   };
1014   if (!IsIntType(FDecl->getReturnType()))
1015     return false;
1016   if (FDecl->getNumParams() < 2 || FDecl->getNumParams() > 3)
1017     return false;
1018   if (!IsIntType(FDecl->parameters()[0]->getType()))
1019     return false;
1020   MainType Type = IsCharPtrPtr(FDecl->parameters()[1]->getType());
1021   if (Type == None)
1022     return false;
1023   if (FDecl->getNumParams() == 3 &&
1024       IsCharPtrPtr(FDecl->parameters()[2]->getType()) != Type)
1025     return false;
1026 
1027   if (Type == Main) {
1028     static llvm::Regex Matcher(
1029         "(^[Mm]ain([_A-Z]|$))|([a-z0-9_]Main([_A-Z]|$))|(_main(_|$))");
1030     assert(Matcher.isValid() && "Invalid Matcher for main like functions.");
1031     return Matcher.match(FDecl->getName());
1032   }
1033   static llvm::Regex Matcher("(^((W[Mm])|(wm))ain([_A-Z]|$))|([a-z0-9_]W[Mm]"
1034                              "ain([_A-Z]|$))|(_wmain(_|$))");
1035   assert(Matcher.isValid() && "Invalid Matcher for wmain like functions.");
1036   return Matcher.match(FDecl->getName());
1037 }
1038 
fixupWithStyle(StringRef Type,StringRef Name,const IdentifierNamingCheck::NamingStyle & Style,const IdentifierNamingCheck::HungarianNotationOption & HNOption,const Decl * D) const1039 std::string IdentifierNamingCheck::fixupWithStyle(
1040     StringRef Type, StringRef Name,
1041     const IdentifierNamingCheck::NamingStyle &Style,
1042     const IdentifierNamingCheck::HungarianNotationOption &HNOption,
1043     const Decl *D) const {
1044   Name.consume_front(Style.Prefix);
1045   Name.consume_back(Style.Suffix);
1046   std::string Fixed = fixupWithCase(
1047       Type, Name, D, Style, HNOption,
1048       Style.Case.value_or(IdentifierNamingCheck::CaseType::CT_AnyCase));
1049 
1050   std::string HungarianPrefix;
1051   using HungarianPrefixType = IdentifierNamingCheck::HungarianPrefixType;
1052   if (HungarianPrefixType::HPT_Off != Style.HPType) {
1053     HungarianPrefix = HungarianNotation.getPrefix(D, HNOption);
1054     if (!HungarianPrefix.empty()) {
1055       if (Style.HPType == HungarianPrefixType::HPT_LowerCase)
1056         HungarianPrefix += "_";
1057 
1058       if (Style.HPType == HungarianPrefixType::HPT_CamelCase)
1059         Fixed[0] = toupper(Fixed[0]);
1060     }
1061   }
1062   StringRef Mid = StringRef(Fixed).trim("_");
1063   if (Mid.empty())
1064     Mid = "_";
1065 
1066   return (Style.Prefix + HungarianPrefix + Mid + Style.Suffix).str();
1067 }
1068 
findStyleKind(const NamedDecl * D,ArrayRef<llvm::Optional<IdentifierNamingCheck::NamingStyle>> NamingStyles,bool IgnoreMainLikeFunctions) const1069 StyleKind IdentifierNamingCheck::findStyleKind(
1070     const NamedDecl *D,
1071     ArrayRef<llvm::Optional<IdentifierNamingCheck::NamingStyle>> NamingStyles,
1072     bool IgnoreMainLikeFunctions) const {
1073   assert(D && D->getIdentifier() && !D->getName().empty() && !D->isImplicit() &&
1074          "Decl must be an explicit identifier with a name.");
1075 
1076   if (isa<ObjCIvarDecl>(D) && NamingStyles[SK_ObjcIvar])
1077     return SK_ObjcIvar;
1078 
1079   if (isa<TypedefDecl>(D) && NamingStyles[SK_Typedef])
1080     return SK_Typedef;
1081 
1082   if (isa<TypeAliasDecl>(D) && NamingStyles[SK_TypeAlias])
1083     return SK_TypeAlias;
1084 
1085   if (const auto *Decl = dyn_cast<NamespaceDecl>(D)) {
1086     if (Decl->isAnonymousNamespace())
1087       return SK_Invalid;
1088 
1089     if (Decl->isInline() && NamingStyles[SK_InlineNamespace])
1090       return SK_InlineNamespace;
1091 
1092     if (NamingStyles[SK_Namespace])
1093       return SK_Namespace;
1094   }
1095 
1096   if (isa<EnumDecl>(D) && NamingStyles[SK_Enum])
1097     return SK_Enum;
1098 
1099   if (const auto *EnumConst = dyn_cast<EnumConstantDecl>(D)) {
1100     if (cast<EnumDecl>(EnumConst->getDeclContext())->isScoped() &&
1101         NamingStyles[SK_ScopedEnumConstant])
1102       return SK_ScopedEnumConstant;
1103 
1104     if (NamingStyles[SK_EnumConstant])
1105       return SK_EnumConstant;
1106 
1107     if (NamingStyles[SK_Constant])
1108       return SK_Constant;
1109 
1110     return SK_Invalid;
1111   }
1112 
1113   if (const auto *Decl = dyn_cast<CXXRecordDecl>(D)) {
1114     if (Decl->isAnonymousStructOrUnion())
1115       return SK_Invalid;
1116 
1117     if (!Decl->getCanonicalDecl()->isThisDeclarationADefinition())
1118       return SK_Invalid;
1119 
1120     if (Decl->hasDefinition() && Decl->isAbstract() &&
1121         NamingStyles[SK_AbstractClass])
1122       return SK_AbstractClass;
1123 
1124     if (Decl->isStruct() && NamingStyles[SK_Struct])
1125       return SK_Struct;
1126 
1127     if (Decl->isStruct() && NamingStyles[SK_Class])
1128       return SK_Class;
1129 
1130     if (Decl->isClass() && NamingStyles[SK_Class])
1131       return SK_Class;
1132 
1133     if (Decl->isClass() && NamingStyles[SK_Struct])
1134       return SK_Struct;
1135 
1136     if (Decl->isUnion() && NamingStyles[SK_Union])
1137       return SK_Union;
1138 
1139     if (Decl->isEnum() && NamingStyles[SK_Enum])
1140       return SK_Enum;
1141 
1142     return SK_Invalid;
1143   }
1144 
1145   if (const auto *Decl = dyn_cast<FieldDecl>(D)) {
1146     QualType Type = Decl->getType();
1147 
1148     if (!Type.isNull() && Type.isConstQualified()) {
1149       if (NamingStyles[SK_ConstantMember])
1150         return SK_ConstantMember;
1151 
1152       if (NamingStyles[SK_Constant])
1153         return SK_Constant;
1154     }
1155 
1156     if (Decl->getAccess() == AS_private && NamingStyles[SK_PrivateMember])
1157       return SK_PrivateMember;
1158 
1159     if (Decl->getAccess() == AS_protected && NamingStyles[SK_ProtectedMember])
1160       return SK_ProtectedMember;
1161 
1162     if (Decl->getAccess() == AS_public && NamingStyles[SK_PublicMember])
1163       return SK_PublicMember;
1164 
1165     if (NamingStyles[SK_Member])
1166       return SK_Member;
1167 
1168     return SK_Invalid;
1169   }
1170 
1171   if (const auto *Decl = dyn_cast<ParmVarDecl>(D)) {
1172     if (isParamInMainLikeFunction(*Decl, IgnoreMainLikeFunctions))
1173       return SK_Invalid;
1174     QualType Type = Decl->getType();
1175 
1176     if (Decl->isConstexpr() && NamingStyles[SK_ConstexprVariable])
1177       return SK_ConstexprVariable;
1178 
1179     if (!Type.isNull() && Type.isConstQualified()) {
1180       if (Type.getTypePtr()->isAnyPointerType() &&
1181           NamingStyles[SK_ConstantPointerParameter])
1182         return SK_ConstantPointerParameter;
1183 
1184       if (NamingStyles[SK_ConstantParameter])
1185         return SK_ConstantParameter;
1186 
1187       if (NamingStyles[SK_Constant])
1188         return SK_Constant;
1189     }
1190 
1191     if (Decl->isParameterPack() && NamingStyles[SK_ParameterPack])
1192       return SK_ParameterPack;
1193 
1194     if (!Type.isNull() && Type.getTypePtr()->isAnyPointerType() &&
1195         NamingStyles[SK_PointerParameter])
1196       return SK_PointerParameter;
1197 
1198     if (NamingStyles[SK_Parameter])
1199       return SK_Parameter;
1200 
1201     return SK_Invalid;
1202   }
1203 
1204   if (const auto *Decl = dyn_cast<VarDecl>(D)) {
1205     QualType Type = Decl->getType();
1206 
1207     if (Decl->isConstexpr() && NamingStyles[SK_ConstexprVariable])
1208       return SK_ConstexprVariable;
1209 
1210     if (!Type.isNull() && Type.isConstQualified()) {
1211       if (Decl->isStaticDataMember() && NamingStyles[SK_ClassConstant])
1212         return SK_ClassConstant;
1213 
1214       if (Decl->isFileVarDecl() && Type.getTypePtr()->isAnyPointerType() &&
1215           NamingStyles[SK_GlobalConstantPointer])
1216         return SK_GlobalConstantPointer;
1217 
1218       if (Decl->isFileVarDecl() && NamingStyles[SK_GlobalConstant])
1219         return SK_GlobalConstant;
1220 
1221       if (Decl->isStaticLocal() && NamingStyles[SK_StaticConstant])
1222         return SK_StaticConstant;
1223 
1224       if (Decl->isLocalVarDecl() && Type.getTypePtr()->isAnyPointerType() &&
1225           NamingStyles[SK_LocalConstantPointer])
1226         return SK_LocalConstantPointer;
1227 
1228       if (Decl->isLocalVarDecl() && NamingStyles[SK_LocalConstant])
1229         return SK_LocalConstant;
1230 
1231       if (Decl->isFunctionOrMethodVarDecl() && NamingStyles[SK_LocalConstant])
1232         return SK_LocalConstant;
1233 
1234       if (NamingStyles[SK_Constant])
1235         return SK_Constant;
1236     }
1237 
1238     if (Decl->isStaticDataMember() && NamingStyles[SK_ClassMember])
1239       return SK_ClassMember;
1240 
1241     if (Decl->isFileVarDecl() && Type.getTypePtr()->isAnyPointerType() &&
1242         NamingStyles[SK_GlobalPointer])
1243       return SK_GlobalPointer;
1244 
1245     if (Decl->isFileVarDecl() && NamingStyles[SK_GlobalVariable])
1246       return SK_GlobalVariable;
1247 
1248     if (Decl->isStaticLocal() && NamingStyles[SK_StaticVariable])
1249       return SK_StaticVariable;
1250 
1251     if (Decl->isLocalVarDecl() && Type.getTypePtr()->isAnyPointerType() &&
1252         NamingStyles[SK_LocalPointer])
1253       return SK_LocalPointer;
1254 
1255     if (Decl->isLocalVarDecl() && NamingStyles[SK_LocalVariable])
1256       return SK_LocalVariable;
1257 
1258     if (Decl->isFunctionOrMethodVarDecl() && NamingStyles[SK_LocalVariable])
1259       return SK_LocalVariable;
1260 
1261     if (NamingStyles[SK_Variable])
1262       return SK_Variable;
1263 
1264     return SK_Invalid;
1265   }
1266 
1267   if (const auto *Decl = dyn_cast<CXXMethodDecl>(D)) {
1268     if (Decl->isMain() || !Decl->isUserProvided() ||
1269         Decl->size_overridden_methods() > 0 || Decl->hasAttr<OverrideAttr>())
1270       return SK_Invalid;
1271 
1272     // If this method has the same name as any base method, this is likely
1273     // necessary even if it's not an override. e.g. CRTP.
1274     for (const CXXBaseSpecifier &Base : Decl->getParent()->bases())
1275       if (const auto *RD = Base.getType()->getAsCXXRecordDecl())
1276         if (RD->hasMemberName(Decl->getDeclName()))
1277           return SK_Invalid;
1278 
1279     if (Decl->isConstexpr() && NamingStyles[SK_ConstexprMethod])
1280       return SK_ConstexprMethod;
1281 
1282     if (Decl->isConstexpr() && NamingStyles[SK_ConstexprFunction])
1283       return SK_ConstexprFunction;
1284 
1285     if (Decl->isStatic() && NamingStyles[SK_ClassMethod])
1286       return SK_ClassMethod;
1287 
1288     if (Decl->isVirtual() && NamingStyles[SK_VirtualMethod])
1289       return SK_VirtualMethod;
1290 
1291     if (Decl->getAccess() == AS_private && NamingStyles[SK_PrivateMethod])
1292       return SK_PrivateMethod;
1293 
1294     if (Decl->getAccess() == AS_protected && NamingStyles[SK_ProtectedMethod])
1295       return SK_ProtectedMethod;
1296 
1297     if (Decl->getAccess() == AS_public && NamingStyles[SK_PublicMethod])
1298       return SK_PublicMethod;
1299 
1300     if (NamingStyles[SK_Method])
1301       return SK_Method;
1302 
1303     if (NamingStyles[SK_Function])
1304       return SK_Function;
1305 
1306     return SK_Invalid;
1307   }
1308 
1309   if (const auto *Decl = dyn_cast<FunctionDecl>(D)) {
1310     if (Decl->isMain())
1311       return SK_Invalid;
1312 
1313     if (Decl->isConstexpr() && NamingStyles[SK_ConstexprFunction])
1314       return SK_ConstexprFunction;
1315 
1316     if (Decl->isGlobal() && NamingStyles[SK_GlobalFunction])
1317       return SK_GlobalFunction;
1318 
1319     if (NamingStyles[SK_Function])
1320       return SK_Function;
1321   }
1322 
1323   if (isa<TemplateTypeParmDecl>(D)) {
1324     if (NamingStyles[SK_TypeTemplateParameter])
1325       return SK_TypeTemplateParameter;
1326 
1327     if (NamingStyles[SK_TemplateParameter])
1328       return SK_TemplateParameter;
1329 
1330     return SK_Invalid;
1331   }
1332 
1333   if (isa<NonTypeTemplateParmDecl>(D)) {
1334     if (NamingStyles[SK_ValueTemplateParameter])
1335       return SK_ValueTemplateParameter;
1336 
1337     if (NamingStyles[SK_TemplateParameter])
1338       return SK_TemplateParameter;
1339 
1340     return SK_Invalid;
1341   }
1342 
1343   if (isa<TemplateTemplateParmDecl>(D)) {
1344     if (NamingStyles[SK_TemplateTemplateParameter])
1345       return SK_TemplateTemplateParameter;
1346 
1347     if (NamingStyles[SK_TemplateParameter])
1348       return SK_TemplateParameter;
1349 
1350     return SK_Invalid;
1351   }
1352 
1353   return SK_Invalid;
1354 }
1355 
1356 llvm::Optional<RenamerClangTidyCheck::FailureInfo>
getFailureInfo(StringRef Type,StringRef Name,const NamedDecl * ND,SourceLocation Location,ArrayRef<llvm::Optional<IdentifierNamingCheck::NamingStyle>> NamingStyles,const IdentifierNamingCheck::HungarianNotationOption & HNOption,StyleKind SK,const SourceManager & SM,bool IgnoreFailedSplit) const1357 IdentifierNamingCheck::getFailureInfo(
1358     StringRef Type, StringRef Name, const NamedDecl *ND,
1359     SourceLocation Location,
1360     ArrayRef<llvm::Optional<IdentifierNamingCheck::NamingStyle>> NamingStyles,
1361     const IdentifierNamingCheck::HungarianNotationOption &HNOption,
1362     StyleKind SK, const SourceManager &SM, bool IgnoreFailedSplit) const {
1363   if (SK == SK_Invalid || !NamingStyles[SK])
1364     return None;
1365 
1366   const IdentifierNamingCheck::NamingStyle &Style = *NamingStyles[SK];
1367   if (Style.IgnoredRegexp.isValid() && Style.IgnoredRegexp.match(Name))
1368     return None;
1369 
1370   if (matchesStyle(Type, Name, Style, HNOption, ND))
1371     return None;
1372 
1373   std::string KindName =
1374       fixupWithCase(Type, StyleNames[SK], ND, Style, HNOption,
1375                     IdentifierNamingCheck::CT_LowerCase);
1376   std::replace(KindName.begin(), KindName.end(), '_', ' ');
1377 
1378   std::string Fixup = fixupWithStyle(Type, Name, Style, HNOption, ND);
1379   if (StringRef(Fixup).equals(Name)) {
1380     if (!IgnoreFailedSplit) {
1381       LLVM_DEBUG(Location.print(llvm::dbgs(), SM);
1382                  llvm::dbgs()
1383                  << llvm::formatv(": unable to split words for {0} '{1}'\n",
1384                                   KindName, Name));
1385     }
1386     return None;
1387   }
1388   return RenamerClangTidyCheck::FailureInfo{std::move(KindName),
1389                                             std::move(Fixup)};
1390 }
1391 
1392 llvm::Optional<RenamerClangTidyCheck::FailureInfo>
getDeclFailureInfo(const NamedDecl * Decl,const SourceManager & SM) const1393 IdentifierNamingCheck::getDeclFailureInfo(const NamedDecl *Decl,
1394                                           const SourceManager &SM) const {
1395   SourceLocation Loc = Decl->getLocation();
1396   const FileStyle &FileStyle = getStyleForFile(SM.getFilename(Loc));
1397   if (!FileStyle.isActive())
1398     return llvm::None;
1399 
1400   return getFailureInfo(HungarianNotation.getDeclTypeName(Decl),
1401                         Decl->getName(), Decl, Loc, FileStyle.getStyles(),
1402                         FileStyle.getHNOption(),
1403                         findStyleKind(Decl, FileStyle.getStyles(),
1404                                       FileStyle.isIgnoringMainLikeFunction()),
1405                         SM, IgnoreFailedSplit);
1406 }
1407 
1408 llvm::Optional<RenamerClangTidyCheck::FailureInfo>
getMacroFailureInfo(const Token & MacroNameTok,const SourceManager & SM) const1409 IdentifierNamingCheck::getMacroFailureInfo(const Token &MacroNameTok,
1410                                            const SourceManager &SM) const {
1411   SourceLocation Loc = MacroNameTok.getLocation();
1412   const FileStyle &Style = getStyleForFile(SM.getFilename(Loc));
1413   if (!Style.isActive())
1414     return llvm::None;
1415 
1416   return getFailureInfo("", MacroNameTok.getIdentifierInfo()->getName(),
1417                         nullptr, Loc, Style.getStyles(), Style.getHNOption(),
1418                         SK_MacroDefinition, SM, IgnoreFailedSplit);
1419 }
1420 
1421 RenamerClangTidyCheck::DiagInfo
getDiagInfo(const NamingCheckId & ID,const NamingCheckFailure & Failure) const1422 IdentifierNamingCheck::getDiagInfo(const NamingCheckId &ID,
1423                                    const NamingCheckFailure &Failure) const {
1424   return DiagInfo{"invalid case style for %0 '%1'",
1425                   [&](DiagnosticBuilder &Diag) {
1426                     Diag << Failure.Info.KindName << ID.second;
1427                   }};
1428 }
1429 
1430 const IdentifierNamingCheck::FileStyle &
getStyleForFile(StringRef FileName) const1431 IdentifierNamingCheck::getStyleForFile(StringRef FileName) const {
1432   if (!GetConfigPerFile)
1433     return *MainFileStyle;
1434   StringRef Parent = llvm::sys::path::parent_path(FileName);
1435   auto Iter = NamingStylesCache.find(Parent);
1436   if (Iter != NamingStylesCache.end())
1437     return Iter->getValue();
1438 
1439   ClangTidyOptions Options = Context->getOptionsForFile(FileName);
1440   if (Options.Checks && GlobList(*Options.Checks).contains(CheckName)) {
1441     auto It = NamingStylesCache.try_emplace(
1442         Parent,
1443         getFileStyleFromOptions({CheckName, Options.CheckOptions, Context}));
1444     assert(It.second);
1445     return It.first->getValue();
1446   }
1447   // Default construction gives an empty style.
1448   auto It = NamingStylesCache.try_emplace(Parent);
1449   assert(It.second);
1450   return It.first->getValue();
1451 }
1452 
1453 } // namespace readability
1454 } // namespace tidy
1455 } // namespace clang
1456