10#include "clang/AST/ASTContext.h"
11#include "clang/AST/CXXInheritance.h"
12#include "clang/ASTMatchers/ASTMatchFinder.h"
13#include "clang/Lex/Lexer.h"
23 return Node.isOverloadedOperator();
29 return MD->size_overridden_methods() > 0 || MD->hasAttr<OverrideAttr>();
38 const CXXMethodDecl *BaseMD,
39 const CXXMethodDecl *DerivedMD) {
40 QualType BaseReturnTy = BaseMD->getType()
41 ->castAs<FunctionType>()
44 QualType DerivedReturnTy = DerivedMD->getType()
45 ->castAs<FunctionType>()
49 if (DerivedReturnTy->isDependentType() || BaseReturnTy->isDependentType())
53 if (Context->hasSameType(DerivedReturnTy, BaseReturnTy))
59 if (!(BaseReturnTy->isPointerType() && DerivedReturnTy->isPointerType()) &&
60 !(BaseReturnTy->isReferenceType() && DerivedReturnTy->isReferenceType()))
66 QualType DTy = DerivedReturnTy->getPointeeType().getCanonicalType();
67 QualType BTy = BaseReturnTy->getPointeeType().getCanonicalType();
69 const CXXRecordDecl *DRD = DTy->getAsCXXRecordDecl();
70 const CXXRecordDecl *BRD = BTy->getAsCXXRecordDecl();
71 if (DRD ==
nullptr || BRD ==
nullptr)
74 if (!DRD->hasDefinition() || !BRD->hasDefinition())
80 if (!Context->hasSameUnqualifiedType(DTy, BTy)) {
82 CXXBasePaths Paths(
true,
true,
86 if (!DRD->isDerivedFrom(BRD, Paths))
90 if (Paths.isAmbiguous(Context->getCanonicalType(BTy).getUnqualifiedType()))
97 DRD->getCanonicalDecl() == DerivedMD->getParent()->getCanonicalDecl();
98 bool HasPublicAccess =
false;
99 for (
const auto &
Path : Paths) {
100 if (
Path.Access == AS_public)
101 HasPublicAccess =
true;
103 if (!HasPublicAccess && !IsItself)
109 if (DerivedReturnTy.getLocalCVRQualifiers() !=
110 BaseReturnTy.getLocalCVRQualifiers())
115 if (DTy.isMoreQualifiedThan(BTy))
123 if (
const auto *Decayed =
Type->getAs<DecayedType>())
124 return Decayed->getDecayedType();
130 const CXXMethodDecl *DerivedMD) {
131 unsigned NumParamA = BaseMD->getNumParams();
132 unsigned NumParamB = DerivedMD->getNumParams();
133 if (NumParamA != NumParamB)
136 for (
unsigned I = 0; I < NumParamA; I++) {
137 if (
getDecayedType(BaseMD->getParamDecl(I)->getType().getCanonicalType()) !=
139 DerivedMD->getParamDecl(I)->getType().getCanonicalType()))
148 const CXXMethodDecl *BaseMD,
149 const CXXMethodDecl *DerivedMD) {
150 if (BaseMD->isStatic() != DerivedMD->isStatic())
153 if (BaseMD->getType() == DerivedMD->getType())
168 const CXXMethodDecl *DerivedMD) {
169 for (CXXMethodDecl::method_iterator I = DerivedMD->begin_overridden_methods(),
170 E = DerivedMD->end_overridden_methods();
172 const CXXMethodDecl *OverriddenMD = *I;
173 if (BaseMD->getCanonicalDecl() == OverriddenMD->getCanonicalDecl())
180bool VirtualNearMissCheck::isPossibleToBeOverridden(
181 const CXXMethodDecl *BaseMD) {
182 auto Iter = PossibleMap.find(BaseMD);
183 if (Iter != PossibleMap.end())
186 bool IsPossible = !BaseMD->isImplicit() && !isa<CXXConstructorDecl>(BaseMD) &&
187 !isa<CXXDestructorDecl>(BaseMD) && BaseMD->isVirtual() &&
188 !BaseMD->isOverloadedOperator() &&
189 !isa<CXXConversionDecl>(BaseMD);
190 PossibleMap[BaseMD] = IsPossible;
194bool VirtualNearMissCheck::isOverriddenByDerivedClass(
195 const CXXMethodDecl *BaseMD,
const CXXRecordDecl *DerivedRD) {
196 auto Key = std::make_pair(BaseMD, DerivedRD);
197 auto Iter = OverriddenMap.find(Key);
198 if (Iter != OverriddenMap.end())
201 bool IsOverridden =
false;
202 for (
const CXXMethodDecl *DerivedMD : DerivedRD->methods()) {
211 OverriddenMap[
Key] = IsOverridden;
218 unless(anyOf(isOverride(), isImplicit(), cxxConstructorDecl(),
219 cxxDestructorDecl(), cxxConversionDecl(), isStatic(),
220 isOverloadedOperator())))
226 const auto *DerivedMD = Result.Nodes.getNodeAs<CXXMethodDecl>(
"method");
229 const ASTContext *Context = Result.Context;
231 const auto *DerivedRD = DerivedMD->getParent()->getDefinition();
234 for (
const auto &BaseSpec : DerivedRD->bases()) {
235 if (
const auto *BaseRD = BaseSpec.getType()->getAsCXXRecordDecl()) {
236 for (
const auto *BaseMD : BaseRD->methods()) {
237 if (!isPossibleToBeOverridden(BaseMD))
240 if (isOverriddenByDerivedClass(BaseMD, DerivedRD))
243 unsigned EditDistance = BaseMD->getName().edit_distance(
244 DerivedMD->getName(), EditDistanceThreshold);
245 if (EditDistance > 0 && EditDistance <= EditDistanceThreshold) {
248 auto Range = CharSourceRange::getTokenRange(
249 SourceRange(DerivedMD->getLocation()));
251 bool ApplyFix = !BaseMD->isTemplateInstantiation() &&
252 !DerivedMD->isTemplateInstantiation();
254 diag(DerivedMD->getBeginLoc(),
255 "method '%0' has a similar name and the same signature as "
256 "virtual method '%1'; did you mean to override it?")
257 << DerivedMD->getQualifiedNameAsString()
258 << BaseMD->getQualifiedNameAsString();
260 Diag << FixItHint::CreateReplacement(
Range, BaseMD->getName());
CharSourceRange Range
SourceRange for the file name.
std::vector< HeaderHandle > Path
::clang::DynTypedNode Node
DiagnosticBuilder diag(SourceLocation Loc, StringRef Description, DiagnosticIDs::Level Level=DiagnosticIDs::Warning)
Add a diagnostic with the check's name.
void check(const ast_matchers::MatchFinder::MatchResult &Result) override
ClangTidyChecks that register ASTMatchers should do the actual work in here.
void registerMatchers(ast_matchers::MatchFinder *Finder) override
Override this to register AST matchers with Finder.
static bool checkOverrideByDerivedMethod(const CXXMethodDecl *BaseMD, const CXXMethodDecl *DerivedMD)
Check whether BaseMD overrides DerivedMD.
static bool checkOverrideWithoutName(const ASTContext *Context, const CXXMethodDecl *BaseMD, const CXXMethodDecl *DerivedMD)
static bool isOverrideMethod(const CXXMethodDecl *MD)
Finds out if the given method overrides some method.
AST_MATCHER(clang::VarDecl, hasConstantDeclaration)
static bool checkParamTypes(const CXXMethodDecl *BaseMD, const CXXMethodDecl *DerivedMD)
static QualType getDecayedType(QualType Type)
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.