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