clang-tools 24.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 "../utils/Matchers.h"
11#include "clang/ASTMatchers/ASTMatchFinder.h"
12#include "clang/ASTMatchers/ASTMatchers.h"
13
14using namespace clang::ast_matchers;
15using namespace clang::tidy::matchers;
16
17namespace clang::tidy::bugprone {
18
19static bool sameBasicType(const ParmVarDecl *Lhs, const ParmVarDecl *Rhs) {
20 return Lhs && Rhs &&
21 Lhs->getType()
22 .getCanonicalType()
23 .getNonReferenceType()
24 .getUnqualifiedType() == Rhs->getType()
25 .getCanonicalType()
26 .getNonReferenceType()
27 .getUnqualifiedType();
28}
29
30static bool namesCollide(const CXXMethodDecl &Lhs, const CXXMethodDecl &Rhs) {
31 if (Lhs.getNameAsString() != Rhs.getNameAsString())
32 return false;
33 if (Lhs.isConst() != Rhs.isConst())
34 return false;
35 if (Lhs.getNumParams() != Rhs.getNumParams())
36 return false;
37 for (unsigned int It = 0; It < Lhs.getNumParams(); ++It)
38 if (!sameBasicType(Lhs.getParamDecl(It), Rhs.getParamDecl(It)))
39 return false;
40 return true;
41}
42
43namespace {
44
45AST_MATCHER(CXXMethodDecl, nameCollidesWithMethodInBase) {
46 const CXXRecordDecl *DerivedClass = Node.getParent();
47 for (const auto &Base : DerivedClass->bases()) {
49 Stack.push_back(&Base);
50 while (!Stack.empty()) {
51 const CXXBaseSpecifier *CurrentBaseSpec = Stack.back();
52 Stack.pop_back();
53
54 if (CurrentBaseSpec->getAccessSpecifier() == AccessSpecifier::AS_private)
55 continue;
56
57 const CXXRecordDecl *CurrentRecord =
58 CurrentBaseSpec->getType()->getAsCXXRecordDecl();
59 if (!CurrentRecord)
60 continue;
61
62 // For multiple inheritance, we ignore only the bases that come from the
63 // std:: namespace
64 if (CurrentRecord->isInStdNamespace())
65 continue;
66
67 for (const auto &BaseMethod : CurrentRecord->methods()) {
68 if (namesCollide(*BaseMethod, Node)) {
69 const ast_matchers::internal::BoundNodesTreeBuilder Result(*Builder);
70 Builder->setBinding("base_method", 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
82AST_MATCHER(CXXMethodDecl, isTemplate) {
83 return Node.getDescribedFunctionTemplate() != nullptr;
84}
85
86} // namespace
87
91
93 MatchFinder *Finder) {
94 Finder->addMatcher(
95 cxxMethodDecl(
96 unless(anyOf(isOutOfLine(), isStaticStorageClass(), isImplicit(),
97 cxxConstructorDecl(), isOverride(), isPrivate(),
98 // isFinal(), //included with isOverride,
99 // TODO: Templates are not handled yet
100 isTemplate(), 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
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::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)