10 #include "clang/AST/ASTContext.h"
11 #include "clang/AST/CXXInheritance.h"
12 #include "clang/ASTMatchers/ASTMatchFinder.h"
13 #include "clang/Lex/Lexer.h"
22 AST_MATCHER(CXXMethodDecl, isStatic) {
return Node.isStatic(); }
25 return Node.isOverloadedOperator();
31 return MD->size_overridden_methods() > 0 ||
MD->hasAttr<OverrideAttr>();
40 const CXXMethodDecl *BaseMD,
41 const CXXMethodDecl *DerivedMD) {
42 QualType BaseReturnTy = BaseMD->getType()
43 ->getAs<FunctionType>()
46 QualType DerivedReturnTy = DerivedMD->getType()
47 ->getAs<FunctionType>()
51 if (DerivedReturnTy->isDependentType() || BaseReturnTy->isDependentType())
55 if (Context->hasSameType(DerivedReturnTy, BaseReturnTy))
61 if (!(BaseReturnTy->isPointerType() && DerivedReturnTy->isPointerType()) &&
62 !(BaseReturnTy->isReferenceType() && DerivedReturnTy->isReferenceType()))
68 QualType DTy = DerivedReturnTy->getPointeeType().getCanonicalType();
69 QualType BTy = BaseReturnTy->getPointeeType().getCanonicalType();
71 const CXXRecordDecl *DRD = DTy->getAsCXXRecordDecl();
72 const CXXRecordDecl *BRD = BTy->getAsCXXRecordDecl();
73 if (DRD ==
nullptr || BRD ==
nullptr)
76 if (!DRD->hasDefinition() || !BRD->hasDefinition())
82 if (!Context->hasSameUnqualifiedType(DTy, BTy)) {
84 CXXBasePaths Paths(
true,
true,
88 if (!DRD->isDerivedFrom(BRD, Paths))
92 if (Paths.isAmbiguous(Context->getCanonicalType(BTy).getUnqualifiedType()))
99 DRD->getCanonicalDecl() == DerivedMD->getParent()->getCanonicalDecl();
100 bool HasPublicAccess =
false;
101 for (
const auto &
Path : Paths) {
102 if (
Path.Access == AS_public)
103 HasPublicAccess =
true;
105 if (!HasPublicAccess && !IsItself)
111 if (DerivedReturnTy.getLocalCVRQualifiers() !=
112 BaseReturnTy.getLocalCVRQualifiers())
117 if (DTy.isMoreQualifiedThan(BTy))
125 if (
const auto *Decayed =
Type->getAs<DecayedType>())
126 return Decayed->getDecayedType();
132 const CXXMethodDecl *DerivedMD) {
133 unsigned NumParamA = BaseMD->getNumParams();
134 unsigned NumParamB = DerivedMD->getNumParams();
135 if (NumParamA != NumParamB)
138 for (
unsigned I = 0; I < NumParamA; I++) {
139 if (
getDecayedType(BaseMD->getParamDecl(I)->getType().getCanonicalType()) !=
141 DerivedMD->getParamDecl(I)->getType().getCanonicalType()))
150 const CXXMethodDecl *BaseMD,
151 const CXXMethodDecl *DerivedMD) {
152 if (BaseMD->isStatic() != DerivedMD->isStatic())
155 if (BaseMD->getType() == DerivedMD->getType())
170 const CXXMethodDecl *DerivedMD) {
171 for (CXXMethodDecl::method_iterator I = DerivedMD->begin_overridden_methods(),
172 E = DerivedMD->end_overridden_methods();
174 const CXXMethodDecl *OverriddenMD = *I;
175 if (BaseMD->getCanonicalDecl() == OverriddenMD->getCanonicalDecl())
182 bool VirtualNearMissCheck::isPossibleToBeOverridden(
183 const CXXMethodDecl *BaseMD) {
184 auto Iter = PossibleMap.find(BaseMD);
185 if (Iter != PossibleMap.end())
188 bool IsPossible = !BaseMD->isImplicit() && !isa<CXXConstructorDecl>(BaseMD) &&
189 !isa<CXXDestructorDecl>(BaseMD) && BaseMD->isVirtual() &&
190 !BaseMD->isOverloadedOperator() &&
191 !isa<CXXConversionDecl>(BaseMD);
192 PossibleMap[BaseMD] = IsPossible;
196 bool VirtualNearMissCheck::isOverriddenByDerivedClass(
197 const CXXMethodDecl *BaseMD,
const CXXRecordDecl *DerivedRD) {
198 auto Key = std::make_pair(BaseMD, DerivedRD);
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) {
220 unless(anyOf(isOverride(), isImplicit(), cxxConstructorDecl(),
221 cxxDestructorDecl(), cxxConversionDecl(), isStatic(),
222 isOverloadedOperator())))
227 void VirtualNearMissCheck::check(
const MatchFinder::MatchResult &Result) {
228 const auto *DerivedMD = Result.Nodes.getNodeAs<CXXMethodDecl>(
"method");
231 const ASTContext *Context = Result.Context;
233 const auto *DerivedRD = DerivedMD->getParent()->getDefinition();
236 for (
const auto &BaseSpec : DerivedRD->bases()) {
237 if (
const auto *BaseRD = BaseSpec.getType()->getAsCXXRecordDecl()) {
238 for (
const auto *BaseMD : BaseRD->methods()) {
239 if (!isPossibleToBeOverridden(BaseMD))
242 if (isOverriddenByDerivedClass(BaseMD, DerivedRD))
245 unsigned EditDistance = BaseMD->getName().edit_distance(
246 DerivedMD->getName(), EditDistanceThreshold);
247 if (EditDistance > 0 && EditDistance <= EditDistanceThreshold) {
250 auto Range = CharSourceRange::getTokenRange(
251 SourceRange(DerivedMD->getLocation()));
253 bool ApplyFix = !BaseMD->isTemplateInstantiation() &&
254 !DerivedMD->isTemplateInstantiation();
256 diag(DerivedMD->getBeginLoc(),
257 "method '%0' has a similar name and the same signature as "
258 "virtual method '%1'; did you mean to override it?")
259 << DerivedMD->getQualifiedNameAsString()
260 << BaseMD->getQualifiedNameAsString();
262 Diag << FixItHint::CreateReplacement(
Range, BaseMD->getName());