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