129 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 "DerivedMethodShadowingBaseMethodCheck.h"10#include "clang/ASTMatchers/ASTMatchFinder.h"11#include "clang/ASTMatchers/ASTMatchers.h"12 13using namespace clang::ast_matchers;14 15namespace clang::tidy::bugprone {16 17static bool sameBasicType(const ParmVarDecl *Lhs, const ParmVarDecl *Rhs) {18 return Lhs && Rhs &&19 Lhs->getType()20 .getCanonicalType()21 .getNonReferenceType()22 .getUnqualifiedType() == Rhs->getType()23 .getCanonicalType()24 .getNonReferenceType()25 .getUnqualifiedType();26}27 28static bool namesCollide(const CXXMethodDecl &Lhs, const CXXMethodDecl &Rhs) {29 if (Lhs.getNameAsString() != Rhs.getNameAsString())30 return false;31 if (Lhs.isConst() != Rhs.isConst())32 return false;33 if (Lhs.getNumParams() != Rhs.getNumParams())34 return false;35 for (unsigned int It = 0; It < Lhs.getNumParams(); ++It)36 if (!sameBasicType(Lhs.getParamDecl(It), Rhs.getParamDecl(It)))37 return false;38 return true;39}40 41namespace {42 43AST_MATCHER(CXXMethodDecl, nameCollidesWithMethodInBase) {44 const CXXRecordDecl *DerivedClass = Node.getParent();45 for (const auto &Base : DerivedClass->bases()) {46 llvm::SmallVector<const CXXBaseSpecifier *, 8> Stack;47 Stack.push_back(&Base);48 while (!Stack.empty()) {49 const CXXBaseSpecifier *CurrentBaseSpec = Stack.back();50 Stack.pop_back();51 52 if (CurrentBaseSpec->getAccessSpecifier() ==53 clang::AccessSpecifier::AS_private)54 continue;55 56 const CXXRecordDecl *CurrentRecord =57 CurrentBaseSpec->getType()->getAsCXXRecordDecl();58 if (!CurrentRecord)59 continue;60 61 // For multiple inheritance, we ignore only the bases that come from the62 // std:: namespace63 if (CurrentRecord->isInStdNamespace())64 continue;65 66 for (const auto &BaseMethod : CurrentRecord->methods()) {67 if (namesCollide(*BaseMethod, Node)) {68 const ast_matchers::internal::BoundNodesTreeBuilder Result(*Builder);69 Builder->setBinding("base_method",70 clang::DynTypedNode::create(*BaseMethod));71 return true;72 }73 }74 75 for (const auto &SubBase : CurrentRecord->bases())76 Stack.push_back(&SubBase);77 }78 }79 return false;80}81 82// Same as clang-tools-extra/clang-tidy/modernize/UseEqualsDefaultCheck.cpp,83// similar matchers are used elsewhere in LLVM84AST_MATCHER(CXXMethodDecl, isOutOfLine) { return Node.isOutOfLine(); }85 86} // namespace87 88DerivedMethodShadowingBaseMethodCheck::DerivedMethodShadowingBaseMethodCheck(89 StringRef Name, ClangTidyContext *Context)90 : ClangTidyCheck(Name, Context) {}91 92void DerivedMethodShadowingBaseMethodCheck::registerMatchers(93 MatchFinder *Finder) {94 Finder->addMatcher(95 cxxMethodDecl(96 unless(anyOf(isOutOfLine(), isStaticStorageClass(), isImplicit(),97 cxxConstructorDecl(), isOverride(), isPrivate(),98 // isFinal(), //included with isOverride,99 // Templates are not handled yet100 ast_matchers::isTemplateInstantiation(),101 ast_matchers::isExplicitTemplateSpecialization())),102 ofClass(cxxRecordDecl(isDerivedFrom(cxxRecordDecl()))103 .bind("derived_class")),104 nameCollidesWithMethodInBase())105 .bind("shadowing_method"),106 this);107}108 109void DerivedMethodShadowingBaseMethodCheck::check(110 const MatchFinder::MatchResult &Result) {111 const auto *ShadowingMethod =112 Result.Nodes.getNodeAs<CXXMethodDecl>("shadowing_method");113 const auto *DerivedClass =114 Result.Nodes.getNodeAs<CXXRecordDecl>("derived_class");115 const auto *BaseMethod = Result.Nodes.getNodeAs<CXXMethodDecl>("base_method");116 117 if (!ShadowingMethod || !DerivedClass || !BaseMethod)118 llvm_unreachable("Required binding not found");119 120 diag(ShadowingMethod->getBeginLoc(),121 "'%0' shadows method with the same name in class %1")122 << ShadowingMethod->getQualifiedNameAsString() << BaseMethod->getParent();123 diag(BaseMethod->getBeginLoc(), "previous definition of %0 is here",124 DiagnosticIDs::Note)125 << ShadowingMethod;126}127 128} // namespace clang::tidy::bugprone129