[clang-tools-extra] [clang-tidy] New bugprone-derived-method-shadowing-base-method (PR #154746)
via cfe-commits
cfe-commits at lists.llvm.org
Fri Aug 29 04:03:51 PDT 2025
================
@@ -0,0 +1,127 @@
+//===----------------------------------------------------------------------===//
+//
+// 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"
+#include <stack>
+
+using namespace clang::ast_matchers;
+
+namespace clang::tidy::bugprone {
+
+namespace {
+
+bool sameBasicType(const ParmVarDecl *Lhs, const ParmVarDecl *Rhs) {
+ if (Lhs && Rhs) {
+ return Lhs->getType()
+ .getCanonicalType()
+ .getNonReferenceType()
+ .getUnqualifiedType() == Rhs->getType()
+ .getCanonicalType()
+ .getNonReferenceType()
+ .getUnqualifiedType();
+ }
+ return false;
+}
+
+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;
+}
+
+AST_MATCHER(CXXMethodDecl, nameCollidesWithMethodInBase) {
+ const CXXRecordDecl *DerivedClass = Node.getParent();
+ for (const auto &Base : DerivedClass->bases()) {
+ std::stack<const CXXBaseSpecifier *> Stack;
+ Stack.push(&Base);
+ while (!Stack.empty()) {
+ const CXXBaseSpecifier *CurrentBaseSpec = Stack.top();
+ Stack.pop();
+
+ if (CurrentBaseSpec->getAccessSpecifier() ==
+ clang::AccessSpecifier::AS_private)
+ continue;
+
+ const auto *CurrentRecord =
+ CurrentBaseSpec->getType()->getAsCXXRecordDecl();
+ if (!CurrentRecord)
+ continue;
+
+ for (const auto &BaseMethod : CurrentRecord->methods()) {
+ if (namesCollide(*BaseMethod, Node)) {
+ ast_matchers::internal::BoundNodesTreeBuilder Result(*Builder);
+ Builder->setBinding("base_method",
+ clang::DynTypedNode::create(*BaseMethod));
+ return true;
+ }
+ }
+
+ for (const auto &SubBase : CurrentRecord->bases())
+ Stack.push(&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(unless(isInStdNamespace()))))
+ .bind("derived_class")),
----------------
t-a-james wrote:
You're right, good spot - fixed this and added a unit test in my latest push.
Note I got rid of the `unless(isInStdNamespace())` here. It would have provided an early exit for the single inheritance case, but I don't think that buys us much and it's cleaner to have all this logic in `nameCollidesWithMethodInBase()`.
https://github.com/llvm/llvm-project/pull/154746
More information about the cfe-commits
mailing list