1 //===--- RenamerClangTidyCheck.cpp - clang-tidy ---------------------------===//
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
7 //===----------------------------------------------------------------------===//
9 #include "RenamerClangTidyCheck.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"
22 #define DEBUG_TYPE "clang-tidy"
24 using namespace clang::ast_matchers
;
28 /// Specialization of DenseMapInfo to allow NamingCheckId objects in DenseMaps
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(),
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();
61 namespace clang::tidy
{
66 llvm::PointerIntPair
<const NamedDecl
*, 1, bool> Data
;
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(); }
85 static const NamedDecl
*findDecl(const RecordDecl
&RecDecl
,
87 for (const Decl
*D
: RecDecl
.decls()) {
88 if (const auto *ND
= dyn_cast
<NamedDecl
>(D
)) {
89 if (ND
->getDeclName().isIdentifier() && ND
->getName() == DeclName
)
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)
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)
109 if (NumOverrides
> 1)
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());
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());
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
,
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();
164 findDeclInBases(*Record
, DeclName
, AggressiveTemplateLookup
)) {
168 std::nullopt
); // Multiple decls found in different base classes.
173 return NameLookup(std::nullopt
); // Propagate multiple resolution back up.
175 return NameLookup(Found
); // If nullptr, decl wasn't found.
180 /// Callback supplies macros to RenamerClangTidyCheck::checkMacro
181 class RenamerClangTidyCheckPPCallbacks
: public PPCallbacks
{
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())
193 if (SM
.isWrittenInBuiltinFile(MacroNameTok
.getLocation()))
195 if (SM
.isWrittenInCommandLineFile(MacroNameTok
.getLocation()))
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
);
208 const SourceManager
&SM
;
209 RenamerClangTidyCheck
*Check
;
212 class RenamerClangTidyVisitor
213 : public RecursiveASTVisitor
<RenamerClangTidyVisitor
> {
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())
227 Check
->addUsage(Decl
->getParent(), Decl
->getNameInfo().getSourceRange(),
230 for (const auto *Init
: Decl
->inits()) {
231 if (!Init
->isWritten() || Init
->isInClassMemberInitializer())
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.
242 bool VisitCXXDestructorDecl(CXXDestructorDecl
*Decl
) {
243 if (Decl
->isImplicit())
245 SourceRange Range
= Decl
->getNameInfo().getSourceRange();
246 if (Range
.getBegin().isInvalid())
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
);
256 bool VisitUsingDecl(UsingDecl
*Decl
) {
257 for (const auto *Shadow
: Decl
->shadows())
258 Check
->addUsage(Shadow
->getTargetDecl(),
259 Decl
->getNameInfo().getSourceRange(), SM
);
263 bool VisitUsingDirectiveDecl(UsingDirectiveDecl
*Decl
) {
264 Check
->addUsage(Decl
->getNominatedNamespaceAsWritten(),
265 Decl
->getIdentLocation(), SM
);
269 bool VisitNamedDecl(NamedDecl
*Decl
) {
270 SourceRange UsageRange
=
271 DeclarationNameInfo(Decl
->getDeclName(), Decl
->getLocation())
273 Check
->addUsage(Decl
, UsageRange
, SM
);
277 bool VisitDeclRefExpr(DeclRefExpr
*DeclRef
) {
278 SourceRange Range
= DeclRef
->getNameInfo().getSourceRange();
279 Check
->addUsage(DeclRef
->getDecl(), Range
, SM
);
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
);
300 VisitCXXDependentScopeMemberExpr(CXXDependentScopeMemberExpr
*DepMemberRef
) {
301 QualType BaseType
= DepMemberRef
->isArrow()
302 ? DepMemberRef
->getBaseType()->getPointeeType()
303 : DepMemberRef
->getBaseType();
304 if (BaseType
.isNull())
306 const CXXRecordDecl
*Base
= BaseType
.getTypePtr()->getAsCXXRecordDecl();
309 DeclarationName DeclName
= DepMemberRef
->getMemberNameInfo().getName();
310 if (!DeclName
.isIdentifier())
312 StringRef DependentName
= DeclName
.getAsIdentifierInfo()->getName();
314 if (NameLookup Resolved
= findDeclInBases(
315 *Base
, DependentName
, AggressiveDependentMemberLookup
)) {
317 Check
->addUsage(*Resolved
,
318 DepMemberRef
->getMemberNameInfo().getSourceRange(), SM
);
324 bool VisitTypedefTypeLoc(const TypedefTypeLoc
&Loc
) {
325 Check
->addUsage(Loc
.getTypedefNameDecl(), Loc
.getSourceRange(), SM
);
329 bool VisitTagTypeLoc(const TagTypeLoc
&Loc
) {
330 Check
->addUsage(Loc
.getDecl(), Loc
.getSourceRange(), SM
);
334 bool VisitInjectedClassNameTypeLoc(const InjectedClassNameTypeLoc
&Loc
) {
335 Check
->addUsage(Loc
.getDecl(), Loc
.getSourceRange(), SM
);
339 bool VisitUnresolvedUsingTypeLoc(const UnresolvedUsingTypeLoc
&Loc
) {
340 Check
->addUsage(Loc
.getDecl(), Loc
.getSourceRange(), SM
);
344 bool VisitTemplateTypeParmTypeLoc(const TemplateTypeParmTypeLoc
&Loc
) {
345 Check
->addUsage(Loc
.getDecl(), Loc
.getSourceRange(), SM
);
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
);
363 bool VisitDependentTemplateSpecializationTypeLoc(
364 const DependentTemplateSpecializationTypeLoc
&Loc
) {
365 if (const TagDecl
*Decl
= Loc
.getTypePtr()->getAsTagDecl())
366 Check
->addUsage(Decl
, Loc
.getSourceRange(), SM
);
371 bool VisitDesignatedInitExpr(DesignatedInitExpr
*Expr
) {
372 for (const DesignatedInitExpr::Designator
&D
: Expr
->designators()) {
373 if (!D
.isFieldDesignator())
375 const FieldDecl
*FD
= D
.getFieldDecl();
378 const IdentifierInfo
*II
= FD
->getIdentifier();
381 SourceRange FixLocation
{D
.getFieldLoc(), D
.getFieldLoc()};
382 Check
->addUsage(FD
, FixLocation
, SM
);
389 RenamerClangTidyCheck
*Check
;
390 const SourceManager
&SM
;
391 const bool AggressiveDependentMemberLookup
;
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
) {
460 // Ignore ClassTemplateSpecializationDecl which are creating duplicate
461 // replacements with CXXRecordDecl.
462 if (isa
<ClassTemplateSpecializationDecl
>(Decl
))
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
);
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.
486 // FailureInfo has already been provided.
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()) {
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
518 RenamerClangTidyVisitor
Visitor(this, *Result
.SourceManager
,
519 AggressiveDependentMemberLookup
);
520 Visitor
.TraverseAST(*Result
.Context
);
523 void RenamerClangTidyCheck::checkMacro(const Token
&MacroNameTok
,
525 const SourceManager
&SourceMgr
) {
526 std::optional
<FailureInfo
> MaybeFailure
=
527 getMacroFailureInfo(MacroNameTok
, SourceMgr
);
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
,
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())
553 SourceRange
Range(MacroNameTok
.getLocation(), MacroNameTok
.getEndLoc());
554 addUsage(ID
, Range
, SourceMgr
);
558 getDiagnosticSuffix(const RenamerClangTidyCheck::ShouldFixStatus FixStatus
,
559 const std::string
&Fixup
) {
561 FixStatus
== RenamerClangTidyCheck::ShouldFixStatus::FixInvalidIdentifier
)
562 return "; cannot be fixed automatically";
563 if (FixStatus
== RenamerClangTidyCheck::ShouldFixStatus::ShouldFix
)
566 RenamerClangTidyCheck::ShouldFixStatus::IgnoreFailureThreshold
)
568 if (FixStatus
== RenamerClangTidyCheck::ShouldFixStatus::ConflictsWithKeyword
)
569 return "; cannot be fixed because '" + Fixup
+
570 "' would conflict with a keyword";
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())
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
603 // Other multi-token identifiers, such as operators are not checked at
605 Diag
<< FixItHint::CreateReplacement(SourceRange(Loc
),
613 } // namespace clang::tidy