Reapply "[lldb][dwarf] Compute fully qualified names on simplified template names...
[llvm-project.git] / clang-tools-extra / clang-tidy / utils / RenamerClangTidyCheck.cpp
blob88e4886cd0df938923ce0e871fb7bb3827471744
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 //===----------------------------------------------------------------------===//
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>
22 #define DEBUG_TYPE "clang-tidy"
24 using namespace clang::ast_matchers;
26 namespace llvm {
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;
33 static inline NamingCheckId getEmptyKey() {
34 return {DenseMapInfo<clang::SourceLocation>::getEmptyKey(), "EMPTY"};
37 static inline NamingCheckId getTombstoneKey() {
38 return {DenseMapInfo<clang::SourceLocation>::getTombstoneKey(),
39 "TOMBSTONE"};
42 static unsigned getHashValue(NamingCheckId Val) {
43 assert(Val != getEmptyKey() && "Cannot hash the empty key!");
44 assert(Val != getTombstoneKey() && "Cannot hash the tombstone key!");
46 return DenseMapInfo<clang::SourceLocation>::getHashValue(Val.first) +
47 DenseMapInfo<StringRef>::getHashValue(Val.second);
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;
59 } // namespace llvm
61 namespace clang::tidy {
63 namespace {
65 class NameLookup {
66 llvm::PointerIntPair<const NamedDecl *, 1, bool> Data;
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) {}
74 bool hasMultipleResolutions() const { return Data.getInt(); }
75 const NamedDecl *getDecl() const {
76 assert(!hasMultipleResolutions() && "Found multiple decls");
77 return Data.getPointer();
79 operator bool() const { return !hasMultipleResolutions(); }
80 const NamedDecl *operator*() const { return getDecl(); }
83 } // namespace
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;
93 return nullptr;
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;
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;
114 static bool hasNoName(const NamedDecl *Decl) {
115 return !Decl->getIdentifier() || Decl->getName().empty();
118 static const NamedDecl *getFailureForNamedDecl(const NamedDecl *ND) {
119 const auto *Canonical = cast<NamedDecl>(ND->getCanonicalDecl());
120 if (Canonical != ND)
121 return Canonical;
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());
130 if (Canonical != ND)
131 return Canonical;
134 return ND;
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;
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();
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;
172 } else
173 return NameLookup(std::nullopt); // Propagate multiple resolution back up.
175 return NameLookup(Found); // If nullptr, decl wasn't found.
178 namespace {
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) {}
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);
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);
207 private:
208 const SourceManager &SM;
209 RenamerClangTidyCheck *Check;
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) {}
220 bool shouldVisitTemplateInstantiations() const { return true; }
222 bool shouldVisitImplicitCode() const { return false; }
224 bool VisitCXXConstructorDecl(CXXConstructorDecl *Decl) {
225 if (Decl->isImplicit())
226 return true;
227 Check->addUsage(Decl->getParent(), Decl->getNameInfo().getSourceRange(),
228 SM);
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.
239 return true;
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;
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;
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;
263 bool VisitUsingDirectiveDecl(UsingDirectiveDecl *Decl) {
264 Check->addUsage(Decl->getNominatedNamespaceAsWritten(),
265 Decl->getIdentLocation(), SM);
266 return true;
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;
277 bool VisitDeclRefExpr(DeclRefExpr *DeclRef) {
278 SourceRange Range = DeclRef->getNameInfo().getSourceRange();
279 Check->addUsage(DeclRef->getDecl(), Range, SM);
280 return true;
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);
289 using Base = RecursiveASTVisitor<RenamerClangTidyVisitor>;
290 return Base::TraverseNestedNameSpecifierLoc(Loc);
293 bool VisitMemberExpr(MemberExpr *MemberRef) {
294 SourceRange Range = MemberRef->getMemberNameInfo().getSourceRange();
295 Check->addUsage(MemberRef->getMemberDecl(), Range, SM);
296 return true;
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();
314 if (NameLookup Resolved = findDeclInBases(
315 *Base, DependentName, AggressiveDependentMemberLookup)) {
316 if (*Resolved)
317 Check->addUsage(*Resolved,
318 DepMemberRef->getMemberNameInfo().getSourceRange(), SM);
321 return true;
324 bool VisitTypedefTypeLoc(const TypedefTypeLoc &Loc) {
325 Check->addUsage(Loc.getTypedefNameDecl(), Loc.getSourceRange(), SM);
326 return true;
329 bool VisitTagTypeLoc(const TagTypeLoc &Loc) {
330 Check->addUsage(Loc.getDecl(), Loc.getSourceRange(), SM);
331 return true;
334 bool VisitInjectedClassNameTypeLoc(const InjectedClassNameTypeLoc &Loc) {
335 Check->addUsage(Loc.getDecl(), Loc.getSourceRange(), SM);
336 return true;
339 bool VisitUnresolvedUsingTypeLoc(const UnresolvedUsingTypeLoc &Loc) {
340 Check->addUsage(Loc.getDecl(), Loc.getSourceRange(), SM);
341 return true;
344 bool VisitTemplateTypeParmTypeLoc(const TemplateTypeParmTypeLoc &Loc) {
345 Check->addUsage(Loc.getDecl(), Loc.getSourceRange(), SM);
346 return true;
349 bool
350 VisitTemplateSpecializationTypeLoc(const TemplateSpecializationTypeLoc &Loc) {
351 const TemplateDecl *Decl =
352 Loc.getTypePtr()->getTemplateName().getAsTemplateDecl();
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);
360 return true;
363 bool VisitDependentTemplateSpecializationTypeLoc(
364 const DependentTemplateSpecializationTypeLoc &Loc) {
365 if (const TagDecl *Decl = Loc.getTypePtr()->getAsTagDecl())
366 Check->addUsage(Decl, Loc.getSourceRange(), SM);
368 return true;
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);
385 return true;
388 private:
389 RenamerClangTidyCheck *Check;
390 const SourceManager &SM;
391 const bool AggressiveDependentMemberLookup;
394 } // namespace
396 RenamerClangTidyCheck::RenamerClangTidyCheck(StringRef CheckName,
397 ClangTidyContext *Context)
398 : ClangTidyCheck(CheckName, Context),
399 AggressiveDependentMemberLookup(
400 Options.getLocalOrGlobal("AggressiveDependentMemberLookup", false)) {}
401 RenamerClangTidyCheck::~RenamerClangTidyCheck() = default;
403 void RenamerClangTidyCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
404 Options.store(Opts, "AggressiveDependentMemberLookup",
405 AggressiveDependentMemberLookup);
408 void RenamerClangTidyCheck::registerMatchers(MatchFinder *Finder) {
409 Finder->addMatcher(translationUnitDecl(), this);
412 void RenamerClangTidyCheck::registerPPCallbacks(
413 const SourceManager &SM, Preprocessor *PP, Preprocessor *ModuleExpanderPP) {
414 ModuleExpanderPP->addPPCallbacks(
415 std::make_unique<RenamerClangTidyCheckPPCallbacks>(SM, this));
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};
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};
434 auto EmplaceResult = NamingCheckFailures.try_emplace(FailureId);
435 NamingCheckFailure &Failure = EmplaceResult.first->second;
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;
442 if (Failure.FixStatus != RenamerClangTidyCheck::ShouldFixStatus::ShouldFix)
443 return EmplaceResult;
445 if (SourceMgr.isWrittenInScratchSpace(FixLocation))
446 Failure.FixStatus = RenamerClangTidyCheck::ShouldFixStatus::InsideMacro;
448 if (!utils::rangeCanBeFixed(UsageRange, &SourceMgr))
449 Failure.FixStatus = RenamerClangTidyCheck::ShouldFixStatus::InsideMacro;
451 return EmplaceResult;
454 void RenamerClangTidyCheck::addUsage(const NamedDecl *Decl,
455 SourceRange UsageRange,
456 const SourceManager &SourceMgr) {
457 if (hasNoName(Decl))
458 return;
460 // Ignore ClassTemplateSpecializationDecl which are creating duplicate
461 // replacements with CXXRecordDecl.
462 if (isa<ClassTemplateSpecializationDecl>(Decl))
463 return;
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);
472 std::optional<FailureInfo> MaybeFailure =
473 getDeclFailureInfo(FailureDecl, SourceMgr);
474 if (!MaybeFailure)
475 return;
477 NamingCheckId FailureId(FailureDecl->getLocation(), FailureDecl->getName());
479 auto [FailureIter, NewFailure] = addUsage(FailureId, UsageRange, SourceMgr);
481 if (FailureIter == NamingCheckFailures.end()) {
482 // Nothing to do if the usage wasn't accepted.
483 return;
485 if (!NewFailure) {
486 // FailureInfo has already been provided.
487 return;
490 // Update the stored failure with info regarding the FailureDecl.
491 NamingCheckFailure &Failure = FailureIter->second;
492 Failure.Info = std::move(*MaybeFailure);
494 // Don't overwritte the failure status if it was already set.
495 if (!Failure.shouldFix()) {
496 return;
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;
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;
518 RenamerClangTidyVisitor Visitor(this, *Result.SourceManager,
519 AggressiveDependentMemberLookup);
520 Visitor.TraverseAST(*Result.Context);
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());
536 if (!isValidAsciiIdentifier(Info.Fixup))
537 Failure.FixStatus = ShouldFixStatus::FixInvalidIdentifier;
539 Failure.Info = std::move(Info);
540 addUsage(ID, Range, SourceMgr);
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);
549 auto Failure = NamingCheckFailures.find(ID);
550 if (Failure == NamingCheckFailures.end())
551 return;
553 SourceRange Range(MacroNameTok.getLocation(), MacroNameTok.getEndLoc());
554 addUsage(ID, Range, SourceMgr);
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");
578 void RenamerClangTidyCheck::onEndOfTranslationUnit() {
579 for (const auto &Pair : NamingCheckFailures) {
580 const NamingCheckId &Decl = Pair.first;
581 const NamingCheckFailure &Failure = Pair.second;
583 if (Failure.Info.KindName.empty())
584 continue;
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);
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.
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.
603 // Other multi-token identifiers, such as operators are not checked at
604 // all.
605 Diag << FixItHint::CreateReplacement(SourceRange(Loc),
606 Failure.Info.Fixup);
613 } // namespace clang::tidy