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