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