1 | //===--- RenamerClangTidyCheck.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 "RenamerClangTidyCheck.h" |
10 | #include "ASTUtils.h" |
11 | #include "clang/AST/CXXInheritance.h" |
12 | #include "clang/AST/RecursiveASTVisitor.h" |
13 | #include "clang/ASTMatchers/ASTMatchFinder.h" |
14 | #include "clang/Basic/CharInfo.h" |
15 | #include "clang/Frontend/CompilerInstance.h" |
16 | #include "clang/Lex/PPCallbacks.h" |
17 | #include "clang/Lex/Preprocessor.h" |
18 | #include "llvm/ADT/DenseMapInfo.h" |
19 | #include "llvm/ADT/PointerIntPair.h" |
20 | #include <optional> |
21 | |
22 | #define DEBUG_TYPE "clang-tidy" |
23 | |
24 | using namespace clang::ast_matchers; |
25 | |
26 | namespace llvm { |
27 | |
28 | /// Specialization of DenseMapInfo to allow NamingCheckId objects in DenseMaps |
29 | template <> |
30 | struct DenseMapInfo<clang::tidy::RenamerClangTidyCheck::NamingCheckId> { |
31 | using NamingCheckId = clang::tidy::RenamerClangTidyCheck::NamingCheckId; |
32 | |
33 | static inline NamingCheckId getEmptyKey() { |
34 | return {DenseMapInfo<clang::SourceLocation>::getEmptyKey(), "EMPTY" }; |
35 | } |
36 | |
37 | static inline NamingCheckId getTombstoneKey() { |
38 | return {DenseMapInfo<clang::SourceLocation>::getTombstoneKey(), |
39 | "TOMBSTONE" }; |
40 | } |
41 | |
42 | static unsigned getHashValue(NamingCheckId Val) { |
43 | assert(Val != getEmptyKey() && "Cannot hash the empty key!" ); |
44 | assert(Val != getTombstoneKey() && "Cannot hash the tombstone key!" ); |
45 | |
46 | return DenseMapInfo<clang::SourceLocation>::getHashValue(Loc: Val.first) + |
47 | DenseMapInfo<StringRef>::getHashValue(Val: Val.second); |
48 | } |
49 | |
50 | static bool isEqual(const NamingCheckId &LHS, const NamingCheckId &RHS) { |
51 | if (RHS == getEmptyKey()) |
52 | return LHS == getEmptyKey(); |
53 | if (RHS == getTombstoneKey()) |
54 | return LHS == getTombstoneKey(); |
55 | return LHS == RHS; |
56 | } |
57 | }; |
58 | |
59 | } // namespace llvm |
60 | |
61 | namespace clang::tidy { |
62 | |
63 | namespace { |
64 | class NameLookup { |
65 | llvm::PointerIntPair<const NamedDecl *, 1, bool> Data; |
66 | |
67 | public: |
68 | explicit NameLookup(const NamedDecl *ND) : Data(ND, false) {} |
69 | explicit NameLookup(std::nullopt_t) : Data(nullptr, true) {} |
70 | explicit NameLookup(std::nullptr_t) : Data(nullptr, false) {} |
71 | NameLookup() : NameLookup(nullptr) {} |
72 | |
73 | bool hasMultipleResolutions() const { return Data.getInt(); } |
74 | const NamedDecl *getDecl() const { |
75 | assert(!hasMultipleResolutions() && "Found multiple decls" ); |
76 | return Data.getPointer(); |
77 | } |
78 | operator bool() const { return !hasMultipleResolutions(); } |
79 | const NamedDecl *operator*() const { return getDecl(); } |
80 | }; |
81 | } // namespace |
82 | |
83 | static const NamedDecl *findDecl(const RecordDecl &RecDecl, |
84 | StringRef DeclName) { |
85 | for (const Decl *D : RecDecl.decls()) { |
86 | if (const auto *ND = dyn_cast<NamedDecl>(D)) { |
87 | if (ND->getDeclName().isIdentifier() && ND->getName().equals(DeclName)) |
88 | return ND; |
89 | } |
90 | } |
91 | return nullptr; |
92 | } |
93 | |
94 | /// Returns a decl matching the \p DeclName in \p Parent or one of its base |
95 | /// classes. If \p AggressiveTemplateLookup is `true` then it will check |
96 | /// template dependent base classes as well. |
97 | /// If a matching decl is found in multiple base classes then it will return a |
98 | /// flag indicating the multiple resolutions. |
99 | static NameLookup findDeclInBases(const CXXRecordDecl &Parent, |
100 | StringRef DeclName, |
101 | bool AggressiveTemplateLookup) { |
102 | if (!Parent.hasDefinition()) |
103 | return NameLookup(nullptr); |
104 | if (const NamedDecl *InClassRef = findDecl(Parent, DeclName)) |
105 | return NameLookup(InClassRef); |
106 | const NamedDecl *Found = nullptr; |
107 | |
108 | for (CXXBaseSpecifier Base : Parent.bases()) { |
109 | const auto *Record = Base.getType()->getAsCXXRecordDecl(); |
110 | if (!Record && AggressiveTemplateLookup) { |
111 | if (const auto *TST = |
112 | Base.getType()->getAs<TemplateSpecializationType>()) { |
113 | if (const auto *TD = llvm::dyn_cast_or_null<ClassTemplateDecl>( |
114 | Val: TST->getTemplateName().getAsTemplateDecl())) |
115 | Record = TD->getTemplatedDecl(); |
116 | } |
117 | } |
118 | if (!Record) |
119 | continue; |
120 | if (auto Search = |
121 | findDeclInBases(Parent: *Record, DeclName, AggressiveTemplateLookup)) { |
122 | if (*Search) { |
123 | if (Found) |
124 | return NameLookup( |
125 | std::nullopt); // Multiple decls found in different base classes. |
126 | Found = *Search; |
127 | continue; |
128 | } |
129 | } else |
130 | return NameLookup(std::nullopt); // Propagate multiple resolution back up. |
131 | } |
132 | return NameLookup(Found); // If nullptr, decl wasn't found. |
133 | } |
134 | |
135 | /// Returns the function that \p Method is overridding. If There are none or |
136 | /// multiple overrides it returns nullptr. If the overridden function itself is |
137 | /// overridding then it will recurse up to find the first decl of the function. |
138 | static const CXXMethodDecl *getOverrideMethod(const CXXMethodDecl *Method) { |
139 | if (Method->size_overridden_methods() != 1) |
140 | return nullptr; |
141 | |
142 | while (true) { |
143 | Method = *Method->begin_overridden_methods(); |
144 | assert(Method && "Overridden method shouldn't be null" ); |
145 | unsigned NumOverrides = Method->size_overridden_methods(); |
146 | if (NumOverrides == 0) |
147 | return Method; |
148 | if (NumOverrides > 1) |
149 | return nullptr; |
150 | } |
151 | } |
152 | |
153 | namespace { |
154 | |
155 | /// Callback supplies macros to RenamerClangTidyCheck::checkMacro |
156 | class RenamerClangTidyCheckPPCallbacks : public PPCallbacks { |
157 | public: |
158 | RenamerClangTidyCheckPPCallbacks(const SourceManager &SM, |
159 | RenamerClangTidyCheck *Check) |
160 | : SM(SM), Check(Check) {} |
161 | |
162 | /// MacroDefined calls checkMacro for macros in the main file |
163 | void MacroDefined(const Token &MacroNameTok, |
164 | const MacroDirective *MD) override { |
165 | const MacroInfo *Info = MD->getMacroInfo(); |
166 | if (Info->isBuiltinMacro()) |
167 | return; |
168 | if (SM.isWrittenInBuiltinFile(Loc: MacroNameTok.getLocation())) |
169 | return; |
170 | if (SM.isWrittenInCommandLineFile(Loc: MacroNameTok.getLocation())) |
171 | return; |
172 | Check->checkMacro(MacroNameTok, MI: Info, SourceMgr: SM); |
173 | } |
174 | |
175 | /// MacroExpands calls expandMacro for macros in the main file |
176 | void MacroExpands(const Token &MacroNameTok, const MacroDefinition &MD, |
177 | SourceRange /*Range*/, |
178 | const MacroArgs * /*Args*/) override { |
179 | Check->expandMacro(MacroNameTok, MI: MD.getMacroInfo(), SourceMgr: SM); |
180 | } |
181 | |
182 | private: |
183 | const SourceManager &SM; |
184 | RenamerClangTidyCheck *Check; |
185 | }; |
186 | |
187 | class RenamerClangTidyVisitor |
188 | : public RecursiveASTVisitor<RenamerClangTidyVisitor> { |
189 | public: |
190 | RenamerClangTidyVisitor(RenamerClangTidyCheck *Check, const SourceManager &SM, |
191 | bool AggressiveDependentMemberLookup) |
192 | : Check(Check), SM(SM), |
193 | AggressiveDependentMemberLookup(AggressiveDependentMemberLookup) {} |
194 | |
195 | static bool hasNoName(const NamedDecl *Decl) { |
196 | return !Decl->getIdentifier() || Decl->getName().empty(); |
197 | } |
198 | |
199 | bool shouldVisitTemplateInstantiations() const { return true; } |
200 | |
201 | bool shouldVisitImplicitCode() const { return false; } |
202 | |
203 | bool VisitCXXConstructorDecl(CXXConstructorDecl *Decl) { |
204 | if (Decl->isImplicit()) |
205 | return true; |
206 | Check->addUsage(Decl->getParent(), Decl->getNameInfo().getSourceRange(), |
207 | SM); |
208 | |
209 | for (const auto *Init : Decl->inits()) { |
210 | if (!Init->isWritten() || Init->isInClassMemberInitializer()) |
211 | continue; |
212 | if (const FieldDecl *FD = Init->getAnyMember()) |
213 | Check->addUsage(FD, SourceRange(Init->getMemberLocation()), SM); |
214 | // Note: delegating constructors and base class initializers are handled |
215 | // via the "typeLoc" matcher. |
216 | } |
217 | |
218 | return true; |
219 | } |
220 | |
221 | bool VisitCXXDestructorDecl(CXXDestructorDecl *Decl) { |
222 | if (Decl->isImplicit()) |
223 | return true; |
224 | SourceRange Range = Decl->getNameInfo().getSourceRange(); |
225 | if (Range.getBegin().isInvalid()) |
226 | return true; |
227 | |
228 | // The first token that will be found is the ~ (or the equivalent trigraph), |
229 | // we want instead to replace the next token, that will be the identifier. |
230 | Range.setBegin(CharSourceRange::getTokenRange(R: Range).getEnd()); |
231 | Check->addUsage(Decl->getParent(), Range, SM); |
232 | return true; |
233 | } |
234 | |
235 | bool VisitUsingDecl(UsingDecl *Decl) { |
236 | for (const auto *Shadow : Decl->shadows()) |
237 | Check->addUsage(Shadow->getTargetDecl(), |
238 | Decl->getNameInfo().getSourceRange(), SM); |
239 | return true; |
240 | } |
241 | |
242 | bool VisitUsingDirectiveDecl(UsingDirectiveDecl *Decl) { |
243 | Check->addUsage(Decl: Decl->getNominatedNamespaceAsWritten(), |
244 | Range: Decl->getIdentLocation(), SourceMgr: SM); |
245 | return true; |
246 | } |
247 | |
248 | bool VisitNamedDecl(NamedDecl *Decl) { |
249 | if (hasNoName(Decl)) |
250 | return true; |
251 | |
252 | const auto *Canonical = cast<NamedDecl>(Decl->getCanonicalDecl()); |
253 | if (Canonical != Decl) { |
254 | Check->addUsage(Canonical, Decl->getLocation(), SM); |
255 | return true; |
256 | } |
257 | |
258 | // Fix overridden methods |
259 | if (const auto *Method = dyn_cast<CXXMethodDecl>(Val: Decl)) { |
260 | if (const CXXMethodDecl *Overridden = getOverrideMethod(Method)) { |
261 | Check->addUsage(Overridden, Method->getLocation(), SM); |
262 | return true; // Don't try to add the actual decl as a Failure. |
263 | } |
264 | } |
265 | |
266 | // Ignore ClassTemplateSpecializationDecl which are creating duplicate |
267 | // replacements with CXXRecordDecl. |
268 | if (isa<ClassTemplateSpecializationDecl>(Val: Decl)) |
269 | return true; |
270 | |
271 | Check->checkNamedDecl(Decl, SourceMgr: SM); |
272 | return true; |
273 | } |
274 | |
275 | bool VisitDeclRefExpr(DeclRefExpr *DeclRef) { |
276 | SourceRange Range = DeclRef->getNameInfo().getSourceRange(); |
277 | Check->addUsage(DeclRef->getDecl(), Range, SM); |
278 | return true; |
279 | } |
280 | |
281 | bool TraverseNestedNameSpecifierLoc(NestedNameSpecifierLoc Loc) { |
282 | if (const NestedNameSpecifier *Spec = Loc.getNestedNameSpecifier()) { |
283 | if (const NamespaceDecl *Decl = Spec->getAsNamespace()) |
284 | Check->addUsage(Decl, Loc.getLocalSourceRange(), SM); |
285 | } |
286 | |
287 | using Base = RecursiveASTVisitor<RenamerClangTidyVisitor>; |
288 | return Base::TraverseNestedNameSpecifierLoc(NNS: Loc); |
289 | } |
290 | |
291 | bool VisitMemberExpr(MemberExpr *MemberRef) { |
292 | SourceRange Range = MemberRef->getMemberNameInfo().getSourceRange(); |
293 | Check->addUsage(MemberRef->getMemberDecl(), Range, SM); |
294 | return true; |
295 | } |
296 | |
297 | bool |
298 | VisitCXXDependentScopeMemberExpr(CXXDependentScopeMemberExpr *DepMemberRef) { |
299 | QualType BaseType = DepMemberRef->isArrow() |
300 | ? DepMemberRef->getBaseType()->getPointeeType() |
301 | : DepMemberRef->getBaseType(); |
302 | if (BaseType.isNull()) |
303 | return true; |
304 | const CXXRecordDecl *Base = BaseType.getTypePtr()->getAsCXXRecordDecl(); |
305 | if (!Base) |
306 | return true; |
307 | DeclarationName DeclName = DepMemberRef->getMemberNameInfo().getName(); |
308 | if (!DeclName.isIdentifier()) |
309 | return true; |
310 | StringRef DependentName = DeclName.getAsIdentifierInfo()->getName(); |
311 | |
312 | if (NameLookup Resolved = findDeclInBases( |
313 | Parent: *Base, DeclName: DependentName, AggressiveTemplateLookup: AggressiveDependentMemberLookup)) { |
314 | if (*Resolved) |
315 | Check->addUsage(Decl: *Resolved, |
316 | Range: DepMemberRef->getMemberNameInfo().getSourceRange(), SourceMgr: SM); |
317 | } |
318 | |
319 | return true; |
320 | } |
321 | |
322 | bool VisitTypedefTypeLoc(const TypedefTypeLoc &Loc) { |
323 | Check->addUsage(Loc.getTypedefNameDecl(), Loc.getSourceRange(), SM); |
324 | return true; |
325 | } |
326 | |
327 | bool VisitTagTypeLoc(const TagTypeLoc &Loc) { |
328 | Check->addUsage(Loc.getDecl(), Loc.getSourceRange(), SM); |
329 | return true; |
330 | } |
331 | |
332 | bool VisitInjectedClassNameTypeLoc(const InjectedClassNameTypeLoc &Loc) { |
333 | Check->addUsage(Loc.getDecl(), Loc.getSourceRange(), SM); |
334 | return true; |
335 | } |
336 | |
337 | bool VisitUnresolvedUsingTypeLoc(const UnresolvedUsingTypeLoc &Loc) { |
338 | Check->addUsage(Loc.getDecl(), Loc.getSourceRange(), SM); |
339 | return true; |
340 | } |
341 | |
342 | bool VisitTemplateTypeParmTypeLoc(const TemplateTypeParmTypeLoc &Loc) { |
343 | Check->addUsage(Loc.getDecl(), Loc.getSourceRange(), SM); |
344 | return true; |
345 | } |
346 | |
347 | bool |
348 | VisitTemplateSpecializationTypeLoc(const TemplateSpecializationTypeLoc &Loc) { |
349 | const TemplateDecl *Decl = |
350 | Loc.getTypePtr()->getTemplateName().getAsTemplateDecl(); |
351 | |
352 | SourceRange Range(Loc.getTemplateNameLoc(), Loc.getTemplateNameLoc()); |
353 | if (const auto *ClassDecl = dyn_cast<TemplateDecl>(Decl)) { |
354 | if (const NamedDecl *TemplDecl = ClassDecl->getTemplatedDecl()) |
355 | Check->addUsage(Decl: TemplDecl, Range, SourceMgr: SM); |
356 | } |
357 | |
358 | return true; |
359 | } |
360 | |
361 | bool VisitDependentTemplateSpecializationTypeLoc( |
362 | const DependentTemplateSpecializationTypeLoc &Loc) { |
363 | if (const TagDecl *Decl = Loc.getTypePtr()->getAsTagDecl()) |
364 | Check->addUsage(Decl, Loc.getSourceRange(), SM); |
365 | |
366 | return true; |
367 | } |
368 | |
369 | bool VisitDesignatedInitExpr(DesignatedInitExpr *Expr) { |
370 | for (const DesignatedInitExpr::Designator &D : Expr->designators()) { |
371 | if (!D.isFieldDesignator()) |
372 | continue; |
373 | const FieldDecl *FD = D.getFieldDecl(); |
374 | if (!FD) |
375 | continue; |
376 | const IdentifierInfo *II = FD->getIdentifier(); |
377 | if (!II) |
378 | continue; |
379 | SourceRange FixLocation{D.getFieldLoc(), D.getFieldLoc()}; |
380 | Check->addUsage(FD, FixLocation, SM); |
381 | } |
382 | |
383 | return true; |
384 | } |
385 | |
386 | private: |
387 | RenamerClangTidyCheck *Check; |
388 | const SourceManager &SM; |
389 | const bool AggressiveDependentMemberLookup; |
390 | }; |
391 | |
392 | } // namespace |
393 | |
394 | RenamerClangTidyCheck::RenamerClangTidyCheck(StringRef CheckName, |
395 | ClangTidyContext *Context) |
396 | : ClangTidyCheck(CheckName, Context), |
397 | AggressiveDependentMemberLookup( |
398 | Options.getLocalOrGlobal(LocalName: "AggressiveDependentMemberLookup" , Default: false)) {} |
399 | RenamerClangTidyCheck::~RenamerClangTidyCheck() = default; |
400 | |
401 | void RenamerClangTidyCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) { |
402 | Options.store(Options&: Opts, LocalName: "AggressiveDependentMemberLookup" , |
403 | Value: AggressiveDependentMemberLookup); |
404 | } |
405 | |
406 | void RenamerClangTidyCheck::registerMatchers(MatchFinder *Finder) { |
407 | Finder->addMatcher(NodeMatch: translationUnitDecl(), Action: this); |
408 | } |
409 | |
410 | void RenamerClangTidyCheck::registerPPCallbacks( |
411 | const SourceManager &SM, Preprocessor *PP, Preprocessor *ModuleExpanderPP) { |
412 | ModuleExpanderPP->addPPCallbacks( |
413 | C: std::make_unique<RenamerClangTidyCheckPPCallbacks>(args: SM, args: this)); |
414 | } |
415 | |
416 | void RenamerClangTidyCheck::addUsage( |
417 | const RenamerClangTidyCheck::NamingCheckId &Decl, SourceRange Range, |
418 | const SourceManager &SourceMgr) { |
419 | // Do nothing if the provided range is invalid. |
420 | if (Range.isInvalid()) |
421 | return; |
422 | |
423 | // If we have a source manager, use it to convert to the spelling location for |
424 | // performing the fix. This is necessary because macros can map the same |
425 | // spelling location to different source locations, and we only want to fix |
426 | // the token once, before it is expanded by the macro. |
427 | SourceLocation FixLocation = Range.getBegin(); |
428 | FixLocation = SourceMgr.getSpellingLoc(Loc: FixLocation); |
429 | if (FixLocation.isInvalid()) |
430 | return; |
431 | |
432 | // Try to insert the identifier location in the Usages map, and bail out if it |
433 | // is already in there |
434 | RenamerClangTidyCheck::NamingCheckFailure &Failure = |
435 | NamingCheckFailures[Decl]; |
436 | if (!Failure.RawUsageLocs.insert(V: FixLocation).second) |
437 | return; |
438 | |
439 | if (!Failure.shouldFix()) |
440 | return; |
441 | |
442 | if (SourceMgr.isWrittenInScratchSpace(Loc: FixLocation)) |
443 | Failure.FixStatus = RenamerClangTidyCheck::ShouldFixStatus::InsideMacro; |
444 | |
445 | if (!utils::rangeCanBeFixed(Range, SM: &SourceMgr)) |
446 | Failure.FixStatus = RenamerClangTidyCheck::ShouldFixStatus::InsideMacro; |
447 | } |
448 | |
449 | void RenamerClangTidyCheck::addUsage(const NamedDecl *Decl, SourceRange Range, |
450 | const SourceManager &SourceMgr) { |
451 | // Don't keep track for non-identifier names. |
452 | auto *II = Decl->getIdentifier(); |
453 | if (!II) |
454 | return; |
455 | if (const auto *Method = dyn_cast<CXXMethodDecl>(Val: Decl)) { |
456 | if (const CXXMethodDecl *Overridden = getOverrideMethod(Method)) |
457 | Decl = Overridden; |
458 | } |
459 | Decl = cast<NamedDecl>(Decl->getCanonicalDecl()); |
460 | return addUsage( |
461 | Decl: RenamerClangTidyCheck::NamingCheckId(Decl->getLocation(), II->getName()), |
462 | Range, SourceMgr); |
463 | } |
464 | |
465 | void RenamerClangTidyCheck::checkNamedDecl(const NamedDecl *Decl, |
466 | const SourceManager &SourceMgr) { |
467 | std::optional<FailureInfo> MaybeFailure = getDeclFailureInfo(Decl, SM: SourceMgr); |
468 | if (!MaybeFailure) |
469 | return; |
470 | |
471 | FailureInfo &Info = *MaybeFailure; |
472 | NamingCheckFailure &Failure = |
473 | NamingCheckFailures[NamingCheckId(Decl->getLocation(), Decl->getName())]; |
474 | SourceRange Range = |
475 | DeclarationNameInfo(Decl->getDeclName(), Decl->getLocation()) |
476 | .getSourceRange(); |
477 | |
478 | const IdentifierTable &Idents = Decl->getASTContext().Idents; |
479 | auto CheckNewIdentifier = Idents.find(Info.Fixup); |
480 | if (CheckNewIdentifier != Idents.end()) { |
481 | const IdentifierInfo *Ident = CheckNewIdentifier->second; |
482 | if (Ident->isKeyword(LangOpts: getLangOpts())) |
483 | Failure.FixStatus = ShouldFixStatus::ConflictsWithKeyword; |
484 | else if (Ident->hasMacroDefinition()) |
485 | Failure.FixStatus = ShouldFixStatus::ConflictsWithMacroDefinition; |
486 | } else if (!isValidAsciiIdentifier(S: Info.Fixup)) { |
487 | Failure.FixStatus = ShouldFixStatus::FixInvalidIdentifier; |
488 | } |
489 | |
490 | Failure.Info = std::move(Info); |
491 | addUsage(Decl, Range, SourceMgr); |
492 | } |
493 | |
494 | void RenamerClangTidyCheck::check(const MatchFinder::MatchResult &Result) { |
495 | if (!Result.SourceManager) { |
496 | // In principle SourceManager is not null but going only by the definition |
497 | // of MatchResult it must be handled. Cannot rename anything without a |
498 | // SourceManager. |
499 | return; |
500 | } |
501 | RenamerClangTidyVisitor Visitor(this, *Result.SourceManager, |
502 | AggressiveDependentMemberLookup); |
503 | Visitor.TraverseAST(AST&: *Result.Context); |
504 | } |
505 | |
506 | void RenamerClangTidyCheck::checkMacro(const Token &MacroNameTok, |
507 | const MacroInfo *MI, |
508 | const SourceManager &SourceMgr) { |
509 | std::optional<FailureInfo> MaybeFailure = |
510 | getMacroFailureInfo(MacroNameTok, SM: SourceMgr); |
511 | if (!MaybeFailure) |
512 | return; |
513 | FailureInfo &Info = *MaybeFailure; |
514 | StringRef Name = MacroNameTok.getIdentifierInfo()->getName(); |
515 | NamingCheckId ID(MI->getDefinitionLoc(), Name); |
516 | NamingCheckFailure &Failure = NamingCheckFailures[ID]; |
517 | SourceRange Range(MacroNameTok.getLocation(), MacroNameTok.getEndLoc()); |
518 | |
519 | if (!isValidAsciiIdentifier(S: Info.Fixup)) |
520 | Failure.FixStatus = ShouldFixStatus::FixInvalidIdentifier; |
521 | |
522 | Failure.Info = std::move(Info); |
523 | addUsage(Decl: ID, Range, SourceMgr); |
524 | } |
525 | |
526 | void RenamerClangTidyCheck::expandMacro(const Token &MacroNameTok, |
527 | const MacroInfo *MI, |
528 | const SourceManager &SourceMgr) { |
529 | StringRef Name = MacroNameTok.getIdentifierInfo()->getName(); |
530 | NamingCheckId ID(MI->getDefinitionLoc(), Name); |
531 | |
532 | auto Failure = NamingCheckFailures.find(Val: ID); |
533 | if (Failure == NamingCheckFailures.end()) |
534 | return; |
535 | |
536 | SourceRange Range(MacroNameTok.getLocation(), MacroNameTok.getEndLoc()); |
537 | addUsage(Decl: ID, Range, SourceMgr); |
538 | } |
539 | |
540 | static std::string |
541 | getDiagnosticSuffix(const RenamerClangTidyCheck::ShouldFixStatus FixStatus, |
542 | const std::string &Fixup) { |
543 | if (Fixup.empty() || |
544 | FixStatus == RenamerClangTidyCheck::ShouldFixStatus::FixInvalidIdentifier) |
545 | return "; cannot be fixed automatically" ; |
546 | if (FixStatus == RenamerClangTidyCheck::ShouldFixStatus::ShouldFix) |
547 | return {}; |
548 | if (FixStatus >= |
549 | RenamerClangTidyCheck::ShouldFixStatus::IgnoreFailureThreshold) |
550 | return {}; |
551 | if (FixStatus == RenamerClangTidyCheck::ShouldFixStatus::ConflictsWithKeyword) |
552 | return "; cannot be fixed because '" + Fixup + |
553 | "' would conflict with a keyword" ; |
554 | if (FixStatus == |
555 | RenamerClangTidyCheck::ShouldFixStatus::ConflictsWithMacroDefinition) |
556 | return "; cannot be fixed because '" + Fixup + |
557 | "' would conflict with a macro definition" ; |
558 | llvm_unreachable("invalid ShouldFixStatus" ); |
559 | } |
560 | |
561 | void RenamerClangTidyCheck::onEndOfTranslationUnit() { |
562 | for (const auto &Pair : NamingCheckFailures) { |
563 | const NamingCheckId &Decl = Pair.first; |
564 | const NamingCheckFailure &Failure = Pair.second; |
565 | |
566 | if (Failure.Info.KindName.empty()) |
567 | continue; |
568 | |
569 | if (Failure.shouldNotify()) { |
570 | auto DiagInfo = getDiagInfo(ID: Decl, Failure); |
571 | auto Diag = diag(Loc: Decl.first, |
572 | Description: DiagInfo.Text + getDiagnosticSuffix(FixStatus: Failure.FixStatus, |
573 | Fixup: Failure.Info.Fixup)); |
574 | DiagInfo.ApplyArgs(Diag); |
575 | |
576 | if (Failure.shouldFix()) { |
577 | for (const auto &Loc : Failure.RawUsageLocs) { |
578 | // We assume that the identifier name is made of one token only. This |
579 | // is always the case as we ignore usages in macros that could build |
580 | // identifier names by combining multiple tokens. |
581 | // |
582 | // For destructors, we already take care of it by remembering the |
583 | // location of the start of the identifier and not the start of the |
584 | // tilde. |
585 | // |
586 | // Other multi-token identifiers, such as operators are not checked at |
587 | // all. |
588 | Diag << FixItHint::CreateReplacement(RemoveRange: SourceRange(Loc), |
589 | Code: Failure.Info.Fixup); |
590 | } |
591 | } |
592 | } |
593 | } |
594 | } |
595 | |
596 | } // namespace clang::tidy |
597 | |