clang-tools 23.0.0git
DerivedMethodShadowingBaseMethodCheck.cpp
Go to the documentation of this file.
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-exception
6//
7//===----------------------------------------------------------------------===//
8
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()) {
47 Stack.push_back(&Base);
48 while (!Stack.empty()) {
49 const CXXBaseSpecifier *CurrentBaseSpec = Stack.back();
50 Stack.pop_back();
51
52 if (CurrentBaseSpec->getAccessSpecifier() == AccessSpecifier::AS_private)
53 continue;
54
55 const CXXRecordDecl *CurrentRecord =
56 CurrentBaseSpec->getType()->getAsCXXRecordDecl();
57 if (!CurrentRecord)
58 continue;
59
60 // For multiple inheritance, we ignore only the bases that come from the
61 // std:: namespace
62 if (CurrentRecord->isInStdNamespace())
63 continue;
64
65 for (const auto &BaseMethod : CurrentRecord->methods()) {
66 if (namesCollide(*BaseMethod, Node)) {
67 const ast_matchers::internal::BoundNodesTreeBuilder Result(*Builder);
68 Builder->setBinding("base_method", DynTypedNode::create(*BaseMethod));
69 return true;
70 }
71 }
72
73 for (const auto &SubBase : CurrentRecord->bases())
74 Stack.push_back(&SubBase);
75 }
76 }
77 return false;
78}
79
80// Same as clang-tools-extra/clang-tidy/modernize/UseEqualsDefaultCheck.cpp,
81// similar matchers are used elsewhere in LLVM
82AST_MATCHER(CXXMethodDecl, isOutOfLine) { return Node.isOutOfLine(); }
83
84} // namespace
85
89
91 MatchFinder *Finder) {
92 Finder->addMatcher(
93 cxxMethodDecl(
94 unless(anyOf(isOutOfLine(), isStaticStorageClass(), isImplicit(),
95 cxxConstructorDecl(), isOverride(), isPrivate(),
96 // isFinal(), //included with isOverride,
97 // Templates are not handled yet
98 ast_matchers::isTemplateInstantiation(),
99 ast_matchers::isExplicitTemplateSpecialization())),
100 ofClass(cxxRecordDecl(isDerivedFrom(cxxRecordDecl()))
101 .bind("derived_class")),
102 nameCollidesWithMethodInBase())
103 .bind("shadowing_method"),
104 this);
105}
106
108 const MatchFinder::MatchResult &Result) {
109 const auto *ShadowingMethod =
110 Result.Nodes.getNodeAs<CXXMethodDecl>("shadowing_method");
111 const auto *DerivedClass =
112 Result.Nodes.getNodeAs<CXXRecordDecl>("derived_class");
113 const auto *BaseMethod = Result.Nodes.getNodeAs<CXXMethodDecl>("base_method");
114
115 if (!ShadowingMethod || !DerivedClass || !BaseMethod)
116 llvm_unreachable("Required binding not found");
117
118 diag(ShadowingMethod->getBeginLoc(),
119 "'%0' shadows method with the same name in class %1")
120 << ShadowingMethod->getQualifiedNameAsString() << BaseMethod->getParent();
121 diag(BaseMethod->getBeginLoc(), "previous definition of %0 is here",
122 DiagnosticIDs::Note)
123 << ShadowingMethod;
124}
125
126} // namespace clang::tidy::bugprone
Every ClangTidyCheck reports errors through a DiagnosticsEngine provided by this context.
void check(const ast_matchers::MatchFinder::MatchResult &Result) override
static bool namesCollide(const CXXMethodDecl &Lhs, const CXXMethodDecl &Rhs)
static bool sameBasicType(const ParmVarDecl *Lhs, const ParmVarDecl *Rhs)
AST_MATCHER(BinaryOperator, isRelationalOperator)