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