//===----------------------------------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #include "DerivedMethodShadowingBaseMethodCheck.h" #include "clang/ASTMatchers/ASTMatchFinder.h" #include "clang/ASTMatchers/ASTMatchers.h" using namespace clang::ast_matchers; namespace clang::tidy::bugprone { static bool sameBasicType(const ParmVarDecl *Lhs, const ParmVarDecl *Rhs) { return Lhs && Rhs && Lhs->getType() .getCanonicalType() .getNonReferenceType() .getUnqualifiedType() == Rhs->getType() .getCanonicalType() .getNonReferenceType() .getUnqualifiedType(); } static bool namesCollide(const CXXMethodDecl &Lhs, const CXXMethodDecl &Rhs) { if (Lhs.getNameAsString() != Rhs.getNameAsString()) return false; if (Lhs.isConst() != Rhs.isConst()) return false; if (Lhs.getNumParams() != Rhs.getNumParams()) return false; for (unsigned int It = 0; It < Lhs.getNumParams(); ++It) if (!sameBasicType(Lhs.getParamDecl(It), Rhs.getParamDecl(It))) return false; return true; } namespace { AST_MATCHER(CXXMethodDecl, nameCollidesWithMethodInBase) { const CXXRecordDecl *DerivedClass = Node.getParent(); for (const auto &Base : DerivedClass->bases()) { llvm::SmallVector Stack; Stack.push_back(&Base); while (!Stack.empty()) { const CXXBaseSpecifier *CurrentBaseSpec = Stack.back(); Stack.pop_back(); if (CurrentBaseSpec->getAccessSpecifier() == clang::AccessSpecifier::AS_private) continue; const CXXRecordDecl *CurrentRecord = CurrentBaseSpec->getType()->getAsCXXRecordDecl(); if (!CurrentRecord) continue; // For multiple inheritance, we ignore only the bases that come from the // std:: namespace if (CurrentRecord->isInStdNamespace()) continue; for (const auto &BaseMethod : CurrentRecord->methods()) { if (namesCollide(*BaseMethod, Node)) { const ast_matchers::internal::BoundNodesTreeBuilder Result(*Builder); Builder->setBinding("base_method", clang::DynTypedNode::create(*BaseMethod)); return true; } } for (const auto &SubBase : CurrentRecord->bases()) Stack.push_back(&SubBase); } } return false; } // Same as clang-tools-extra/clang-tidy/modernize/UseEqualsDefaultCheck.cpp, // similar matchers are used elsewhere in LLVM AST_MATCHER(CXXMethodDecl, isOutOfLine) { return Node.isOutOfLine(); } } // namespace DerivedMethodShadowingBaseMethodCheck::DerivedMethodShadowingBaseMethodCheck( StringRef Name, ClangTidyContext *Context) : ClangTidyCheck(Name, Context) {} void DerivedMethodShadowingBaseMethodCheck::registerMatchers( MatchFinder *Finder) { Finder->addMatcher( cxxMethodDecl( unless(anyOf(isOutOfLine(), isStaticStorageClass(), isImplicit(), cxxConstructorDecl(), isOverride(), isPrivate(), // isFinal(), //included with isOverride, // Templates are not handled yet ast_matchers::isTemplateInstantiation(), ast_matchers::isExplicitTemplateSpecialization())), ofClass(cxxRecordDecl(isDerivedFrom(cxxRecordDecl())) .bind("derived_class")), nameCollidesWithMethodInBase()) .bind("shadowing_method"), this); } void DerivedMethodShadowingBaseMethodCheck::check( const MatchFinder::MatchResult &Result) { const auto *ShadowingMethod = Result.Nodes.getNodeAs("shadowing_method"); const auto *DerivedClass = Result.Nodes.getNodeAs("derived_class"); const auto *BaseMethod = Result.Nodes.getNodeAs("base_method"); if (!ShadowingMethod || !DerivedClass || !BaseMethod) llvm_unreachable("Required binding not found"); diag(ShadowingMethod->getBeginLoc(), "'%0' shadows method with the same name in class %1") << ShadowingMethod->getQualifiedNameAsString() << BaseMethod->getParent(); diag(BaseMethod->getBeginLoc(), "previous definition of %0 is here", DiagnosticIDs::Note) << ShadowingMethod; } } // namespace clang::tidy::bugprone