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