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

source code of clang-tools-extra/clang-tidy/utils/RenamerClangTidyCheck.cpp