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