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