blob: 24aea8c2cca2652acddb595080226c86b2f62238 [file] [log] [blame]
Alexander Kornienko98ba0812016-01-13 14:16:35 +00001//===--- VirtualNearMissCheck.cpp - clang-tidy-----------------------------===//
2//
3// The LLVM Compiler Infrastructure
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9
10#include "VirtualNearMissCheck.h"
11#include "clang/AST/ASTContext.h"
12#include "clang/AST/CXXInheritance.h"
13#include "clang/ASTMatchers/ASTMatchFinder.h"
14#include "clang/Lex/Lexer.h"
15
16using namespace clang::ast_matchers;
17
18namespace clang {
19namespace tidy {
20namespace misc {
21
Alexander Kornienko8ac20a82016-01-29 15:22:10 +000022AST_MATCHER(CXXMethodDecl, isStatic) { return Node.isStatic(); }
23
24AST_MATCHER(CXXMethodDecl, isOverloadedOperator) {
25 return Node.isOverloadedOperator();
26}
27
Alexander Kornienko98ba0812016-01-13 14:16:35 +000028/// Finds out if the given method overrides some method.
29static bool isOverrideMethod(const CXXMethodDecl *MD) {
30 return MD->size_overridden_methods() > 0 || MD->hasAttr<OverrideAttr>();
31}
32
33/// Checks whether the return types are covariant, according to
34/// C++[class.virtual]p7.
35///
36/// Similar with clang::Sema::CheckOverridingFunctionReturnType.
37/// \returns true if the return types of BaseMD and DerivedMD are covariant.
38static bool checkOverridingFunctionReturnType(const ASTContext *Context,
39 const CXXMethodDecl *BaseMD,
40 const CXXMethodDecl *DerivedMD) {
Alexander Kornienko8ac20a82016-01-29 15:22:10 +000041 QualType BaseReturnTy = BaseMD->getType()
42 ->getAs<FunctionType>()
43 ->getReturnType()
44 .getCanonicalType();
45 QualType DerivedReturnTy = DerivedMD->getType()
46 ->getAs<FunctionType>()
47 ->getReturnType()
48 .getCanonicalType();
Alexander Kornienko98ba0812016-01-13 14:16:35 +000049
50 if (DerivedReturnTy->isDependentType() || BaseReturnTy->isDependentType())
51 return false;
52
53 // Check if return types are identical.
54 if (Context->hasSameType(DerivedReturnTy, BaseReturnTy))
55 return true;
56
57 /// Check if the return types are covariant.
Gabor Horvath93bc5762016-01-22 21:45:51 +000058
59 // Both types must be pointers or references to classes.
60 if (!(BaseReturnTy->isPointerType() && DerivedReturnTy->isPointerType()) &&
61 !(BaseReturnTy->isReferenceType() && DerivedReturnTy->isReferenceType()))
62 return false;
63
Alexander Kornienko98ba0812016-01-13 14:16:35 +000064 /// BTy is the class type in return type of BaseMD. For example,
65 /// B* Base::md()
66 /// While BRD is the declaration of B.
Alexander Kornienko8ac20a82016-01-29 15:22:10 +000067 QualType DTy = DerivedReturnTy->getPointeeType().getCanonicalType();
68 QualType BTy = BaseReturnTy->getPointeeType().getCanonicalType();
Alexander Kornienko98ba0812016-01-13 14:16:35 +000069
Gabor Horvath93bc5762016-01-22 21:45:51 +000070 const CXXRecordDecl *DRD = DTy->getAsCXXRecordDecl();
71 const CXXRecordDecl *BRD = BTy->getAsCXXRecordDecl();
Alexander Kornienko98ba0812016-01-13 14:16:35 +000072 if (DRD == nullptr || BRD == nullptr)
73 return false;
74
75 if (DRD == BRD)
76 return true;
77
78 if (!Context->hasSameUnqualifiedType(DTy, BTy)) {
79 // Begin checking whether the conversion from D to B is valid.
80 CXXBasePaths Paths(/*FindAmbiguities=*/true, /*RecordPaths=*/true,
81 /*DetectVirtual=*/false);
82
83 // Check whether D is derived from B, and fill in a CXXBasePaths object.
84 if (!DRD->isDerivedFrom(BRD, Paths))
85 return false;
86
87 // Check ambiguity.
88 if (Paths.isAmbiguous(Context->getCanonicalType(BTy).getUnqualifiedType()))
89 return false;
90
91 // Check accessibility.
92 // FIXME: We currently only support checking if B is accessible base class
93 // of D, or D is the same class which DerivedMD is in.
Alexander Kornienko8ac20a82016-01-29 15:22:10 +000094 bool IsItself =
95 DRD->getCanonicalDecl() == DerivedMD->getParent()->getCanonicalDecl();
Alexander Kornienko98ba0812016-01-13 14:16:35 +000096 bool HasPublicAccess = false;
97 for (const auto &Path : Paths) {
98 if (Path.Access == AS_public)
99 HasPublicAccess = true;
100 }
101 if (!HasPublicAccess && !IsItself)
102 return false;
103 // End checking conversion from D to B.
104 }
105
106 // Both pointers or references should have the same cv-qualification.
107 if (DerivedReturnTy.getLocalCVRQualifiers() !=
108 BaseReturnTy.getLocalCVRQualifiers())
109 return false;
110
111 // The class type D should have the same cv-qualification as or less
112 // cv-qualification than the class type B.
113 if (DTy.isMoreQualifiedThan(BTy))
114 return false;
115
116 return true;
117}
118
Gabor Horvath93bc5762016-01-22 21:45:51 +0000119/// \returns decayed type for arrays and functions.
120static QualType getDecayedType(QualType Type) {
121 if (const auto *Decayed = Type->getAs<DecayedType>())
122 return Decayed->getDecayedType();
123 return Type;
124}
125
Alexander Kornienko98ba0812016-01-13 14:16:35 +0000126/// \returns true if the param types are the same.
127static bool checkParamTypes(const CXXMethodDecl *BaseMD,
128 const CXXMethodDecl *DerivedMD) {
129 unsigned NumParamA = BaseMD->getNumParams();
130 unsigned NumParamB = DerivedMD->getNumParams();
131 if (NumParamA != NumParamB)
132 return false;
133
134 for (unsigned I = 0; I < NumParamA; I++) {
Alexander Kornienko8ac20a82016-01-29 15:22:10 +0000135 if (getDecayedType(BaseMD->getParamDecl(I)->getType().getCanonicalType()) !=
136 getDecayedType(
137 DerivedMD->getParamDecl(I)->getType().getCanonicalType()))
Alexander Kornienko98ba0812016-01-13 14:16:35 +0000138 return false;
139 }
140 return true;
141}
142
143/// \returns true if derived method can override base method except for the
144/// name.
145static bool checkOverrideWithoutName(const ASTContext *Context,
146 const CXXMethodDecl *BaseMD,
147 const CXXMethodDecl *DerivedMD) {
Alexander Kornienko98ba0812016-01-13 14:16:35 +0000148 if (BaseMD->isStatic() != DerivedMD->isStatic())
149 return false;
150
Alexander Kornienko98ba0812016-01-13 14:16:35 +0000151 if (BaseMD->getType() == DerivedMD->getType())
152 return true;
153
154 // Now the function types are not identical. Then check if the return types
155 // are covariant and if the param types are the same.
156 if (!checkOverridingFunctionReturnType(Context, BaseMD, DerivedMD))
157 return false;
158 return checkParamTypes(BaseMD, DerivedMD);
159}
160
161/// Check whether BaseMD overrides DerivedMD.
162///
163/// Prerequisite: the class which BaseMD is in should be a base class of that
164/// DerivedMD is in.
165static bool checkOverrideByDerivedMethod(const CXXMethodDecl *BaseMD,
166 const CXXMethodDecl *DerivedMD) {
Alexander Kornienko8ac20a82016-01-29 15:22:10 +0000167 for (CXXMethodDecl::method_iterator I = DerivedMD->begin_overridden_methods(),
168 E = DerivedMD->end_overridden_methods();
169 I != E; ++I) {
170 const CXXMethodDecl *OverriddenMD = *I;
Alexander Kornienkobfee5f72016-01-29 15:22:20 +0000171 if (BaseMD->getCanonicalDecl() == OverriddenMD->getCanonicalDecl())
Alexander Kornienko8ac20a82016-01-29 15:22:10 +0000172 return true;
Alexander Kornienko8ac20a82016-01-29 15:22:10 +0000173 }
Alexander Kornienko98ba0812016-01-13 14:16:35 +0000174
Alexander Kornienko8ac20a82016-01-29 15:22:10 +0000175 return false;
Alexander Kornienko98ba0812016-01-13 14:16:35 +0000176}
177
178bool VirtualNearMissCheck::isPossibleToBeOverridden(
179 const CXXMethodDecl *BaseMD) {
Alexander Kornienko8ac20a82016-01-29 15:22:10 +0000180 auto Iter = PossibleMap.find(BaseMD);
Alexander Kornienko98ba0812016-01-13 14:16:35 +0000181 if (Iter != PossibleMap.end())
182 return Iter->second;
183
184 bool IsPossible = !BaseMD->isImplicit() && !isa<CXXConstructorDecl>(BaseMD) &&
Gabor Horvath93bc5762016-01-22 21:45:51 +0000185 !isa<CXXDestructorDecl>(BaseMD) && BaseMD->isVirtual() &&
Alexander Kornienkodc841502016-01-26 10:56:27 +0000186 !BaseMD->isOverloadedOperator() &&
187 !isa<CXXConversionDecl>(BaseMD);
Alexander Kornienko8ac20a82016-01-29 15:22:10 +0000188 PossibleMap[BaseMD] = IsPossible;
Alexander Kornienko98ba0812016-01-13 14:16:35 +0000189 return IsPossible;
190}
191
192bool VirtualNearMissCheck::isOverriddenByDerivedClass(
193 const CXXMethodDecl *BaseMD, const CXXRecordDecl *DerivedRD) {
Alexander Kornienko8ac20a82016-01-29 15:22:10 +0000194 auto Key = std::make_pair(BaseMD, DerivedRD);
Alexander Kornienko98ba0812016-01-13 14:16:35 +0000195 auto Iter = OverriddenMap.find(Key);
196 if (Iter != OverriddenMap.end())
197 return Iter->second;
198
199 bool IsOverridden = false;
200 for (const CXXMethodDecl *DerivedMD : DerivedRD->methods()) {
201 if (!isOverrideMethod(DerivedMD))
202 continue;
203
204 if (checkOverrideByDerivedMethod(BaseMD, DerivedMD)) {
205 IsOverridden = true;
206 break;
207 }
208 }
209 OverriddenMap[Key] = IsOverridden;
210 return IsOverridden;
211}
212
213void VirtualNearMissCheck::registerMatchers(MatchFinder *Finder) {
214 if (!getLangOpts().CPlusPlus)
215 return;
216
Gabor Horvath93bc5762016-01-22 21:45:51 +0000217 Finder->addMatcher(
Alexander Kornienkodc841502016-01-26 10:56:27 +0000218 cxxMethodDecl(
219 unless(anyOf(isOverride(), isImplicit(), cxxConstructorDecl(),
Alexander Kornienko8ac20a82016-01-29 15:22:10 +0000220 cxxDestructorDecl(), cxxConversionDecl(), isStatic(),
221 isOverloadedOperator())))
Gabor Horvath93bc5762016-01-22 21:45:51 +0000222 .bind("method"),
223 this);
Alexander Kornienko98ba0812016-01-13 14:16:35 +0000224}
225
226void VirtualNearMissCheck::check(const MatchFinder::MatchResult &Result) {
227 const auto *DerivedMD = Result.Nodes.getNodeAs<CXXMethodDecl>("method");
Gabor Horvath93bc5762016-01-22 21:45:51 +0000228 assert(DerivedMD);
Alexander Kornienko98ba0812016-01-13 14:16:35 +0000229
Alexander Kornienko98ba0812016-01-13 14:16:35 +0000230 const ASTContext *Context = Result.Context;
231
Alexander Kornienko8ac20a82016-01-29 15:22:10 +0000232 const auto *DerivedRD = DerivedMD->getParent()->getDefinition();
233 assert(DerivedRD);
Alexander Kornienko98ba0812016-01-13 14:16:35 +0000234
235 for (const auto &BaseSpec : DerivedRD->bases()) {
236 if (const auto *BaseRD = BaseSpec.getType()->getAsCXXRecordDecl()) {
237 for (const auto *BaseMD : BaseRD->methods()) {
238 if (!isPossibleToBeOverridden(BaseMD))
239 continue;
240
241 if (isOverriddenByDerivedClass(BaseMD, DerivedRD))
242 continue;
243
244 unsigned EditDistance =
245 BaseMD->getName().edit_distance(DerivedMD->getName());
246 if (EditDistance > 0 && EditDistance <= EditDistanceThreshold) {
247 if (checkOverrideWithoutName(Context, BaseMD, DerivedMD)) {
248 // A "virtual near miss" is found.
249 diag(DerivedMD->getLocStart(),
250 "method '%0' has a similar name and the same signature as "
251 "virtual method '%1'; did you mean to override it?")
252 << DerivedMD->getQualifiedNameAsString()
253 << BaseMD->getQualifiedNameAsString();
254 }
255 }
256 }
257 }
258 }
259}
260
261} // namespace misc
262} // namespace tidy
263} // namespace clang