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