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