615 lines · cpp
1//===----------------------------------------------------------------------===//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-exception6//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 DenseMaps29template <>30struct DenseMapInfo<clang::tidy::RenamerClangTidyCheck::NamingCheckId> {31 using NamingCheckId = clang::tidy::RenamerClangTidyCheck::NamingCheckId;32 33 static NamingCheckId getEmptyKey() {34 return {DenseMapInfo<clang::SourceLocation>::getEmptyKey(), "EMPTY"};35 }36 37 static 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 llvm60 61namespace clang::tidy {62 63namespace {64 65class NameLookup {66 llvm::PointerIntPair<const NamedDecl *, 1, bool> Data;67 68public: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} // namespace84 85static 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 overriding. If There are none or97/// multiple overrides it returns nullptr. If the overridden function itself is98/// overriding then it will recurse up to find the first decl of the function.99static 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 const unsigned NumOverrides = Method->size_overridden_methods();107 if (NumOverrides == 0)108 return Method;109 if (NumOverrides > 1)110 return nullptr;111 }112}113 114static bool hasNoName(const NamedDecl *Decl) {115 return !Decl->getIdentifier() || Decl->getName().empty();116}117 118static 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 base138/// classes. If \p AggressiveTemplateLookup is `true` then it will check139/// template dependent base classes as well.140/// If a matching decl is found in multiple base classes then it will return a141/// flag indicating the multiple resolutions.142static 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 (const 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 } else173 return NameLookup(std::nullopt); // Propagate multiple resolution back up.174 }175 return NameLookup(Found); // If nullptr, decl wasn't found.176}177 178namespace {179 180/// Callback supplies macros to RenamerClangTidyCheck::checkMacro181class RenamerClangTidyCheckPPCallbacks : public PPCallbacks {182public:183 RenamerClangTidyCheckPPCallbacks(const SourceManager &SM,184 RenamerClangTidyCheck *Check)185 : SM(SM), Check(Check) {}186 187 /// MacroDefined calls checkMacro for macros in the main file188 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 if (SM.isInSystemHeader(MacroNameTok.getLocation()))198 return;199 Check->checkMacro(MacroNameTok, Info, SM);200 }201 202 /// MacroExpands calls expandMacro for macros in the main file203 void MacroExpands(const Token &MacroNameTok, const MacroDefinition &MD,204 SourceRange /*Range*/,205 const MacroArgs * /*Args*/) override {206 Check->expandMacro(MacroNameTok, MD.getMacroInfo(), SM);207 }208 209private:210 const SourceManager &SM;211 RenamerClangTidyCheck *Check;212};213 214class RenamerClangTidyVisitor215 : public RecursiveASTVisitor<RenamerClangTidyVisitor> {216public:217 RenamerClangTidyVisitor(RenamerClangTidyCheck *Check, const SourceManager &SM,218 bool AggressiveDependentMemberLookup)219 : Check(Check), SM(SM),220 AggressiveDependentMemberLookup(AggressiveDependentMemberLookup) {}221 222 bool shouldVisitTemplateInstantiations() const { return true; }223 224 bool shouldVisitImplicitCode() const { return false; }225 226 bool VisitCXXConstructorDecl(CXXConstructorDecl *Decl) {227 if (Decl->isImplicit())228 return true;229 Check->addUsage(Decl->getParent(), Decl->getNameInfo().getSourceRange(),230 SM);231 232 for (const auto *Init : Decl->inits()) {233 if (!Init->isWritten() || Init->isInClassMemberInitializer())234 continue;235 if (const FieldDecl *FD = Init->getAnyMember())236 Check->addUsage(FD, SourceRange(Init->getMemberLocation()), SM);237 // Note: delegating constructors and base class initializers are handled238 // via the "typeLoc" matcher.239 }240 241 return true;242 }243 244 bool VisitCXXDestructorDecl(CXXDestructorDecl *Decl) {245 if (Decl->isImplicit())246 return true;247 SourceRange Range = Decl->getNameInfo().getSourceRange();248 if (Range.getBegin().isInvalid())249 return true;250 251 // The first token that will be found is the ~ (or the equivalent trigraph),252 // we want instead to replace the next token, that will be the identifier.253 Range.setBegin(CharSourceRange::getTokenRange(Range).getEnd());254 Check->addUsage(Decl->getParent(), Range, SM);255 return true;256 }257 258 bool VisitUsingDecl(UsingDecl *Decl) {259 for (const auto *Shadow : Decl->shadows())260 Check->addUsage(Shadow->getTargetDecl(),261 Decl->getNameInfo().getSourceRange(), SM);262 return true;263 }264 265 bool VisitUsingDirectiveDecl(UsingDirectiveDecl *Decl) {266 Check->addUsage(Decl->getNominatedNamespaceAsWritten(),267 Decl->getIdentLocation(), SM);268 return true;269 }270 271 bool VisitNamedDecl(NamedDecl *Decl) {272 const SourceRange UsageRange =273 DeclarationNameInfo(Decl->getDeclName(), Decl->getLocation())274 .getSourceRange();275 Check->addUsage(Decl, UsageRange, SM);276 return true;277 }278 279 bool VisitDeclRefExpr(DeclRefExpr *DeclRef) {280 const SourceRange Range = DeclRef->getNameInfo().getSourceRange();281 Check->addUsage(DeclRef->getDecl(), Range, SM);282 return true;283 }284 285 bool TraverseNestedNameSpecifierLoc(NestedNameSpecifierLoc Loc) {286 if (const NestedNameSpecifier Spec = Loc.getNestedNameSpecifier();287 Spec.getKind() == NestedNameSpecifier::Kind::Namespace) {288 if (const auto *Decl =289 dyn_cast<NamespaceDecl>(Spec.getAsNamespaceAndPrefix().Namespace))290 Check->addUsage(Decl, Loc.getLocalSourceRange(), SM);291 }292 293 using Base = RecursiveASTVisitor<RenamerClangTidyVisitor>;294 return Base::TraverseNestedNameSpecifierLoc(Loc);295 }296 297 bool VisitMemberExpr(MemberExpr *MemberRef) {298 const SourceRange Range = MemberRef->getMemberNameInfo().getSourceRange();299 Check->addUsage(MemberRef->getMemberDecl(), Range, SM);300 return true;301 }302 303 bool304 VisitCXXDependentScopeMemberExpr(CXXDependentScopeMemberExpr *DepMemberRef) {305 const QualType BaseType =306 DepMemberRef->isArrow() ? DepMemberRef->getBaseType()->getPointeeType()307 : DepMemberRef->getBaseType();308 if (BaseType.isNull())309 return true;310 const CXXRecordDecl *Base = BaseType.getTypePtr()->getAsCXXRecordDecl();311 if (!Base)312 return true;313 const DeclarationName DeclName =314 DepMemberRef->getMemberNameInfo().getName();315 if (!DeclName.isIdentifier())316 return true;317 const StringRef DependentName = DeclName.getAsIdentifierInfo()->getName();318 319 if (const NameLookup Resolved = findDeclInBases(320 *Base, DependentName, AggressiveDependentMemberLookup)) {321 if (*Resolved)322 Check->addUsage(*Resolved,323 DepMemberRef->getMemberNameInfo().getSourceRange(), SM);324 }325 326 return true;327 }328 329 bool VisitTypedefTypeLoc(const TypedefTypeLoc &Loc) {330 Check->addUsage(Loc.getDecl(), Loc.getNameLoc(), SM);331 return true;332 }333 334 bool VisitTagTypeLoc(const TagTypeLoc &Loc) {335 Check->addUsage(Loc.getDecl(), Loc.getNameLoc(), SM);336 return true;337 }338 339 bool VisitUnresolvedUsingTypeLoc(const UnresolvedUsingTypeLoc &Loc) {340 Check->addUsage(Loc.getDecl(), Loc.getNameLoc(), SM);341 return true;342 }343 344 bool VisitTemplateTypeParmTypeLoc(const TemplateTypeParmTypeLoc &Loc) {345 Check->addUsage(Loc.getDecl(), Loc.getNameLoc(), SM);346 return true;347 }348 349 bool350 VisitTemplateSpecializationTypeLoc(const TemplateSpecializationTypeLoc &Loc) {351 const TemplateDecl *Decl =352 Loc.getTypePtr()->getTemplateName().getAsTemplateDecl(353 /*IgnoreDeduced=*/true);354 if (!Decl)355 return true;356 357 if (const auto *ClassDecl = dyn_cast<TemplateDecl>(Decl))358 if (const NamedDecl *TemplDecl = ClassDecl->getTemplatedDecl())359 Check->addUsage(TemplDecl, Loc.getTemplateNameLoc(), SM);360 361 return true;362 }363 364 bool VisitDesignatedInitExpr(DesignatedInitExpr *Expr) {365 for (const DesignatedInitExpr::Designator &D : Expr->designators()) {366 if (!D.isFieldDesignator())367 continue;368 const FieldDecl *FD = D.getFieldDecl();369 if (!FD)370 continue;371 const IdentifierInfo *II = FD->getIdentifier();372 if (!II)373 continue;374 const SourceRange FixLocation{D.getFieldLoc(), D.getFieldLoc()};375 Check->addUsage(FD, FixLocation, SM);376 }377 378 return true;379 }380 381private:382 RenamerClangTidyCheck *Check;383 const SourceManager &SM;384 const bool AggressiveDependentMemberLookup;385};386 387} // namespace388 389RenamerClangTidyCheck::RenamerClangTidyCheck(StringRef CheckName,390 ClangTidyContext *Context)391 : ClangTidyCheck(CheckName, Context),392 AggressiveDependentMemberLookup(393 Options.get("AggressiveDependentMemberLookup", false)) {}394RenamerClangTidyCheck::~RenamerClangTidyCheck() = default;395 396void RenamerClangTidyCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {397 Options.store(Opts, "AggressiveDependentMemberLookup",398 AggressiveDependentMemberLookup);399}400 401void RenamerClangTidyCheck::registerMatchers(MatchFinder *Finder) {402 Finder->addMatcher(translationUnitDecl(), this);403}404 405void RenamerClangTidyCheck::registerPPCallbacks(406 const SourceManager &SM, Preprocessor *PP, Preprocessor *ModuleExpanderPP) {407 ModuleExpanderPP->addPPCallbacks(408 std::make_unique<RenamerClangTidyCheckPPCallbacks>(SM, this));409}410 411std::pair<RenamerClangTidyCheck::NamingCheckFailureMap::iterator, bool>412RenamerClangTidyCheck::addUsage(413 const RenamerClangTidyCheck::NamingCheckId &FailureId,414 SourceRange UsageRange, const SourceManager &SourceMgr) {415 // Do nothing if the provided range is invalid.416 if (UsageRange.isInvalid())417 return {NamingCheckFailures.end(), false};418 419 // Get the spelling location for performing the fix. This is necessary because420 // macros can map the same spelling location to different source locations,421 // and we only want to fix the token once, before it is expanded by the macro.422 SourceLocation FixLocation = UsageRange.getBegin();423 FixLocation = SourceMgr.getSpellingLoc(FixLocation);424 if (FixLocation.isInvalid())425 return {NamingCheckFailures.end(), false};426 427 // Skip if in system system header428 if (SourceMgr.isInSystemHeader(FixLocation))429 return {NamingCheckFailures.end(), false};430 431 auto EmplaceResult = NamingCheckFailures.try_emplace(FailureId);432 NamingCheckFailure &Failure = EmplaceResult.first->second;433 434 // Try to insert the identifier location in the Usages map, and bail out if it435 // is already in there436 if (!Failure.RawUsageLocs.insert(FixLocation).second)437 return EmplaceResult;438 439 if (Failure.FixStatus != RenamerClangTidyCheck::ShouldFixStatus::ShouldFix)440 return EmplaceResult;441 442 if (SourceMgr.isWrittenInScratchSpace(FixLocation))443 Failure.FixStatus = RenamerClangTidyCheck::ShouldFixStatus::InsideMacro;444 445 if (!utils::rangeCanBeFixed(UsageRange, &SourceMgr))446 Failure.FixStatus = RenamerClangTidyCheck::ShouldFixStatus::InsideMacro;447 448 return EmplaceResult;449}450 451void RenamerClangTidyCheck::addUsage(const NamedDecl *Decl,452 SourceRange UsageRange,453 const SourceManager &SourceMgr) {454 if (SourceMgr.isInSystemHeader(Decl->getLocation()))455 return;456 457 if (hasNoName(Decl))458 return;459 460 // Ignore ClassTemplateSpecializationDecl which are creating duplicate461 // 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. Ideally466 // there is just one NamedDecl in every group of "related" NamedDecls that467 // becomes the failure. This NamedDecl and all of its related NamedDecls468 // become usages. E.g. Since NamedDecls are Redeclarable, only the canonical469 // 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 const NamingCheckId FailureId(FailureDecl->getLocation(),478 FailureDecl->getName());479 480 auto [FailureIter, NewFailure] = addUsage(FailureId, UsageRange, SourceMgr);481 482 if (FailureIter == NamingCheckFailures.end()) {483 // Nothing to do if the usage wasn't accepted.484 return;485 }486 if (!NewFailure) {487 // FailureInfo has already been provided.488 return;489 }490 491 // Update the stored failure with info regarding the FailureDecl.492 NamingCheckFailure &Failure = FailureIter->second;493 Failure.Info = std::move(*MaybeFailure);494 495 // Don't overwrite the failure status if it was already set.496 if (!Failure.shouldFix()) {497 return;498 }499 const IdentifierTable &Idents = FailureDecl->getASTContext().Idents;500 auto CheckNewIdentifier = Idents.find(Failure.Info.Fixup);501 if (CheckNewIdentifier != Idents.end()) {502 const IdentifierInfo *Ident = CheckNewIdentifier->second;503 if (Ident->isKeyword(getLangOpts()))504 Failure.FixStatus = ShouldFixStatus::ConflictsWithKeyword;505 else if (Ident->hasMacroDefinition())506 Failure.FixStatus = ShouldFixStatus::ConflictsWithMacroDefinition;507 } else if (!isValidAsciiIdentifier(Failure.Info.Fixup)) {508 Failure.FixStatus = ShouldFixStatus::FixInvalidIdentifier;509 }510}511 512void RenamerClangTidyCheck::check(const MatchFinder::MatchResult &Result) {513 if (!Result.SourceManager) {514 // In principle SourceManager is not null but going only by the definition515 // of MatchResult it must be handled. Cannot rename anything without a516 // SourceManager.517 return;518 }519 RenamerClangTidyVisitor Visitor(this, *Result.SourceManager,520 AggressiveDependentMemberLookup);521 Visitor.TraverseAST(*Result.Context);522}523 524void RenamerClangTidyCheck::checkMacro(const Token &MacroNameTok,525 const MacroInfo *MI,526 const SourceManager &SourceMgr) {527 std::optional<FailureInfo> MaybeFailure =528 getMacroFailureInfo(MacroNameTok, SourceMgr);529 if (!MaybeFailure)530 return;531 FailureInfo &Info = *MaybeFailure;532 const StringRef Name = MacroNameTok.getIdentifierInfo()->getName();533 const NamingCheckId ID(MI->getDefinitionLoc(), Name);534 NamingCheckFailure &Failure = NamingCheckFailures[ID];535 const SourceRange Range(MacroNameTok.getLocation(), MacroNameTok.getEndLoc());536 537 if (!isValidAsciiIdentifier(Info.Fixup))538 Failure.FixStatus = ShouldFixStatus::FixInvalidIdentifier;539 540 Failure.Info = std::move(Info);541 addUsage(ID, Range, SourceMgr);542}543 544void RenamerClangTidyCheck::expandMacro(const Token &MacroNameTok,545 const MacroInfo *MI,546 const SourceManager &SourceMgr) {547 const StringRef Name = MacroNameTok.getIdentifierInfo()->getName();548 const NamingCheckId ID(MI->getDefinitionLoc(), Name);549 550 auto Failure = NamingCheckFailures.find(ID);551 if (Failure == NamingCheckFailures.end())552 return;553 554 const SourceRange Range(MacroNameTok.getLocation(), MacroNameTok.getEndLoc());555 addUsage(ID, Range, SourceMgr);556}557 558static std::string559getDiagnosticSuffix(const RenamerClangTidyCheck::ShouldFixStatus FixStatus,560 const std::string &Fixup) {561 if (Fixup.empty() ||562 FixStatus == RenamerClangTidyCheck::ShouldFixStatus::FixInvalidIdentifier)563 return "; cannot be fixed automatically";564 if (FixStatus == RenamerClangTidyCheck::ShouldFixStatus::ShouldFix)565 return {};566 if (FixStatus >=567 RenamerClangTidyCheck::ShouldFixStatus::IgnoreFailureThreshold)568 return {};569 if (FixStatus == RenamerClangTidyCheck::ShouldFixStatus::ConflictsWithKeyword)570 return "; cannot be fixed because '" + Fixup +571 "' would conflict with a keyword";572 if (FixStatus ==573 RenamerClangTidyCheck::ShouldFixStatus::ConflictsWithMacroDefinition)574 return "; cannot be fixed because '" + Fixup +575 "' would conflict with a macro definition";576 llvm_unreachable("invalid ShouldFixStatus");577}578 579void RenamerClangTidyCheck::onEndOfTranslationUnit() {580 for (const auto &Pair : NamingCheckFailures) {581 const NamingCheckId &Decl = Pair.first;582 const NamingCheckFailure &Failure = Pair.second;583 584 if (Failure.Info.KindName.empty())585 continue;586 587 if (Failure.shouldNotify()) {588 auto DiagInfo = getDiagInfo(Decl, Failure);589 auto Diag = diag(Decl.first,590 DiagInfo.Text + getDiagnosticSuffix(Failure.FixStatus,591 Failure.Info.Fixup));592 DiagInfo.ApplyArgs(Diag);593 594 if (Failure.shouldFix()) {595 for (const auto &Loc : Failure.RawUsageLocs) {596 // We assume that the identifier name is made of one token only. This597 // is always the case as we ignore usages in macros that could build598 // identifier names by combining multiple tokens.599 //600 // For destructors, we already take care of it by remembering the601 // location of the start of the identifier and not the start of the602 // tilde.603 //604 // Other multi-token identifiers, such as operators are not checked at605 // all.606 Diag << FixItHint::CreateReplacement(SourceRange(Loc),607 Failure.Info.Fixup);608 }609 }610 }611 }612}613 614} // namespace clang::tidy615