[clang-tools-extra] [clang-tidy] New bugprone-method-hiding check (PR #154746)
via cfe-commits
cfe-commits at lists.llvm.org
Wed Aug 27 07:52:01 PDT 2025
================
@@ -0,0 +1,129 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 "MethodHidingCheck.h"
+#include "clang/ASTMatchers/ASTMatchFinder.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;
+ // Templates are not handled yet
+ if (Lhs.isTemplated() || Rhs.isTemplated())
+ return false;
+ if (Lhs.isTemplateInstantiation() || Rhs.isTemplateInstantiation())
+ return false;
+ if (Lhs.isFunctionTemplateSpecialization() ||
+ Rhs.isFunctionTemplateSpecialization())
+ 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
+
+MethodHidingCheck::MethodHidingCheck(StringRef Name, ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context) {}
+
+void MethodHidingCheck::registerMatchers(MatchFinder *Finder) {
+ Finder->addMatcher(
+ cxxMethodDecl(
+ unless(anyOf(isOutOfLine(), isStaticStorageClass(), isImplicit(),
+ cxxConstructorDecl(), isOverride(),
+ // isFinal(), //included with isOverride,
+ isPrivate())),
----------------
t-a-james wrote:
If `Derived::foo()` is private then calling it downstream is a compiler error, which makes it much harder to accidentally call the wrong method
https://github.com/llvm/llvm-project/pull/154746
More information about the cfe-commits
mailing list