11 #include "clang/AST/ASTContext.h"
12 #include "clang/AST/CXXInheritance.h"
13 #include "clang/ASTMatchers/ASTMatchFinder.h"
14 #include "clang/Lex/Lexer.h"
16 using namespace clang::ast_matchers;
24 return MD->size_overridden_methods() > 0 || MD->hasAttr<OverrideAttr>();
33 const CXXMethodDecl *BaseMD,
34 const CXXMethodDecl *DerivedMD) {
35 QualType BaseReturnTy =
36 BaseMD->getType()->getAs<FunctionType>()->getReturnType();
37 QualType DerivedReturnTy =
38 DerivedMD->getType()->getAs<FunctionType>()->getReturnType();
40 if (DerivedReturnTy->isDependentType() || BaseReturnTy->isDependentType())
44 if (Context->hasSameType(DerivedReturnTy, BaseReturnTy))
52 const CXXRecordDecl *BRD, *DRD;
55 if (
const auto *DerivedPT = DerivedReturnTy->getAs<
PointerType>()) {
56 if (
const auto *BasePT = BaseReturnTy->getAs<
PointerType>()) {
57 DTy = DerivedPT->getPointeeType();
58 BTy = BasePT->getPointeeType();
60 }
else if (
const auto *DerivedRT = DerivedReturnTy->getAs<ReferenceType>()) {
61 if (
const auto *BaseRT = BaseReturnTy->getAs<ReferenceType>()) {
62 DTy = DerivedRT->getPointeeType();
63 BTy = BaseRT->getPointeeType();
71 DRD = DTy->getAsCXXRecordDecl();
72 BRD = BTy->getAsCXXRecordDecl();
73 if (DRD ==
nullptr || BRD ==
nullptr)
79 if (!Context->hasSameUnqualifiedType(DTy, BTy)) {
81 CXXBasePaths Paths(
true,
true,
85 if (!DRD->isDerivedFrom(BRD, Paths))
89 if (Paths.isAmbiguous(Context->getCanonicalType(BTy).getUnqualifiedType()))
95 bool IsItself = DRD == DerivedMD->getParent();
96 bool HasPublicAccess =
false;
97 for (
const auto &
Path : Paths) {
98 if (
Path.Access == AS_public)
99 HasPublicAccess =
true;
101 if (!HasPublicAccess && !IsItself)
107 if (DerivedReturnTy.getLocalCVRQualifiers() !=
108 BaseReturnTy.getLocalCVRQualifiers())
113 if (DTy.isMoreQualifiedThan(BTy))
121 const CXXMethodDecl *DerivedMD) {
122 unsigned NumParamA = BaseMD->getNumParams();
123 unsigned NumParamB = DerivedMD->getNumParams();
124 if (NumParamA != NumParamB)
127 for (
unsigned I = 0; I < NumParamA; I++) {
128 if (BaseMD->getParamDecl(I)->getType() !=
129 DerivedMD->getParamDecl(I)->getType())
138 const CXXMethodDecl *BaseMD,
139 const CXXMethodDecl *DerivedMD) {
140 if (BaseMD->getTypeQualifiers() != DerivedMD->getTypeQualifiers())
143 if (BaseMD->isStatic() != DerivedMD->isStatic())
146 if (BaseMD->getAccess() != DerivedMD->getAccess())
149 if (BaseMD->getType() == DerivedMD->getType())
164 const CXXMethodDecl *DerivedMD) {
165 if (BaseMD->getNameAsString() != DerivedMD->getNameAsString())
179 return MD->getQualifiedNameAsString() +
" " + MD->getType().getAsString();
182 bool VirtualNearMissCheck::isPossibleToBeOverridden(
183 const CXXMethodDecl *BaseMD) {
185 auto Iter = PossibleMap.find(Id);
186 if (Iter != PossibleMap.end())
189 bool IsPossible = !BaseMD->isImplicit() && !isa<CXXConstructorDecl>(BaseMD) &&
191 PossibleMap[Id] = IsPossible;
195 bool VirtualNearMissCheck::isOverriddenByDerivedClass(
196 const CXXMethodDecl *BaseMD,
const CXXRecordDecl *DerivedRD) {
198 DerivedRD->getQualifiedNameAsString());
199 auto Iter = OverriddenMap.find(Key);
200 if (Iter != OverriddenMap.end())
203 bool IsOverridden =
false;
204 for (
const CXXMethodDecl *DerivedMD : DerivedRD->methods()) {
213 OverriddenMap[Key] = IsOverridden;
217 void VirtualNearMissCheck::registerMatchers(MatchFinder *
Finder) {
218 if (!getLangOpts().CPlusPlus)
221 Finder->addMatcher(cxxMethodDecl(unless(anyOf(isOverride(), isImplicit(),
222 cxxConstructorDecl())))
227 void VirtualNearMissCheck::check(
const MatchFinder::MatchResult &
Result) {
228 const auto *DerivedMD = Result.Nodes.getNodeAs<CXXMethodDecl>(
"method");
229 assert(DerivedMD !=
nullptr);
231 if (DerivedMD->isStatic())
234 const ASTContext *
Context = Result.Context;
236 const auto *DerivedRD = DerivedMD->getParent();
238 for (
const auto &BaseSpec : DerivedRD->bases()) {
239 if (
const auto *BaseRD = BaseSpec.getType()->getAsCXXRecordDecl()) {
240 for (
const auto *BaseMD : BaseRD->methods()) {
241 if (!isPossibleToBeOverridden(BaseMD))
244 if (isOverriddenByDerivedClass(BaseMD, DerivedRD))
247 unsigned EditDistance =
248 BaseMD->getName().edit_distance(DerivedMD->getName());
249 if (EditDistance > 0 && EditDistance <= EditDistanceThreshold) {
252 diag(DerivedMD->getLocStart(),
253 "method '%0' has a similar name and the same signature as "
254 "virtual method '%1'; did you mean to override it?")
255 << DerivedMD->getQualifiedNameAsString()
256 << BaseMD->getQualifiedNameAsString();
std::unique_ptr< ast_matchers::MatchFinder > Finder
static bool checkParamTypes(const CXXMethodDecl *BaseMD, const CXXMethodDecl *DerivedMD)
std::vector< HeaderHandle > Path
static bool isOverrideMethod(const CXXMethodDecl *MD)
Finds out if the given method overrides some method.
static std::string generateMethodId(const CXXMethodDecl *MD)
Generate unique ID for given MethodDecl.
static bool checkOverrideByDerivedMethod(const CXXMethodDecl *BaseMD, const CXXMethodDecl *DerivedMD)
Check whether BaseMD overrides DerivedMD.
static const char PointerType[]
static bool checkOverrideWithoutName(const ASTContext *Context, const CXXMethodDecl *BaseMD, const CXXMethodDecl *DerivedMD)
static bool checkOverridingFunctionReturnType(const ASTContext *Context, const CXXMethodDecl *BaseMD, const CXXMethodDecl *DerivedMD)
Checks whether the return types are covariant, according to C++[class.virtual]p7. ...
ClangTidyContext & Context