blob: bb9c9b65c9c16367b8d2554e7a97fc3f1aefaf7c [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 {
Alexander Kornienkod4ac4af2017-11-24 14:16:29 +000020namespace bugprone {
Alexander Kornienko98ba0812016-01-13 14:16:35 +000021
Benjamin Kramerf8c99292018-02-18 19:02:35 +000022namespace {
Alexander Kornienko8ac20a82016-01-29 15:22:10 +000023AST_MATCHER(CXXMethodDecl, isStatic) { return Node.isStatic(); }
24
25AST_MATCHER(CXXMethodDecl, isOverloadedOperator) {
26 return Node.isOverloadedOperator();
27}
Benjamin Kramerf8c99292018-02-18 19:02:35 +000028} // namespace
Alexander Kornienko8ac20a82016-01-29 15:22:10 +000029
Alexander Kornienko98ba0812016-01-13 14:16:35 +000030/// Finds out if the given method overrides some method.
31static bool isOverrideMethod(const CXXMethodDecl *MD) {
32 return MD->size_overridden_methods() > 0 || MD->hasAttr<OverrideAttr>();
33}
34
35/// Checks whether the return types are covariant, according to
36/// C++[class.virtual]p7.
37///
38/// Similar with clang::Sema::CheckOverridingFunctionReturnType.
39/// \returns true if the return types of BaseMD and DerivedMD are covariant.
40static bool checkOverridingFunctionReturnType(const ASTContext *Context,
41 const CXXMethodDecl *BaseMD,
42 const CXXMethodDecl *DerivedMD) {
Alexander Kornienko8ac20a82016-01-29 15:22:10 +000043 QualType BaseReturnTy = BaseMD->getType()
44 ->getAs<FunctionType>()
45 ->getReturnType()
46 .getCanonicalType();
47 QualType DerivedReturnTy = DerivedMD->getType()
48 ->getAs<FunctionType>()
49 ->getReturnType()
50 .getCanonicalType();
Alexander Kornienko98ba0812016-01-13 14:16:35 +000051
52 if (DerivedReturnTy->isDependentType() || BaseReturnTy->isDependentType())
53 return false;
54
55 // Check if return types are identical.
56 if (Context->hasSameType(DerivedReturnTy, BaseReturnTy))
57 return true;
58
59 /// Check if the return types are covariant.
Gabor Horvath93bc5762016-01-22 21:45:51 +000060
61 // Both types must be pointers or references to classes.
62 if (!(BaseReturnTy->isPointerType() && DerivedReturnTy->isPointerType()) &&
63 !(BaseReturnTy->isReferenceType() && DerivedReturnTy->isReferenceType()))
64 return false;
65
Alexander Kornienko98ba0812016-01-13 14:16:35 +000066 /// BTy is the class type in return type of BaseMD. For example,
67 /// B* Base::md()
68 /// While BRD is the declaration of B.
Alexander Kornienko8ac20a82016-01-29 15:22:10 +000069 QualType DTy = DerivedReturnTy->getPointeeType().getCanonicalType();
70 QualType BTy = BaseReturnTy->getPointeeType().getCanonicalType();
Alexander Kornienko98ba0812016-01-13 14:16:35 +000071
Gabor Horvath93bc5762016-01-22 21:45:51 +000072 const CXXRecordDecl *DRD = DTy->getAsCXXRecordDecl();
73 const CXXRecordDecl *BRD = BTy->getAsCXXRecordDecl();
Alexander Kornienko98ba0812016-01-13 14:16:35 +000074 if (DRD == nullptr || BRD == nullptr)
75 return false;
76
Haojian Wubbff5382016-02-03 17:21:44 +000077 if (!DRD->hasDefinition() || !BRD->hasDefinition())
78 return false;
79
Alexander Kornienko98ba0812016-01-13 14:16:35 +000080 if (DRD == BRD)
81 return true;
82
83 if (!Context->hasSameUnqualifiedType(DTy, BTy)) {
84 // Begin checking whether the conversion from D to B is valid.
85 CXXBasePaths Paths(/*FindAmbiguities=*/true, /*RecordPaths=*/true,
86 /*DetectVirtual=*/false);
87
88 // Check whether D is derived from B, and fill in a CXXBasePaths object.
89 if (!DRD->isDerivedFrom(BRD, Paths))
90 return false;
91
92 // Check ambiguity.
93 if (Paths.isAmbiguous(Context->getCanonicalType(BTy).getUnqualifiedType()))
94 return false;
95
96 // Check accessibility.
97 // FIXME: We currently only support checking if B is accessible base class
98 // of D, or D is the same class which DerivedMD is in.
Alexander Kornienko8ac20a82016-01-29 15:22:10 +000099 bool IsItself =
100 DRD->getCanonicalDecl() == DerivedMD->getParent()->getCanonicalDecl();
Alexander Kornienko98ba0812016-01-13 14:16:35 +0000101 bool HasPublicAccess = false;
102 for (const auto &Path : Paths) {
103 if (Path.Access == AS_public)
104 HasPublicAccess = true;
105 }
106 if (!HasPublicAccess && !IsItself)
107 return false;
108 // End checking conversion from D to B.
109 }
110
111 // Both pointers or references should have the same cv-qualification.
112 if (DerivedReturnTy.getLocalCVRQualifiers() !=
113 BaseReturnTy.getLocalCVRQualifiers())
114 return false;
115
116 // The class type D should have the same cv-qualification as or less
117 // cv-qualification than the class type B.
118 if (DTy.isMoreQualifiedThan(BTy))
119 return false;
120
121 return true;
122}
123
Gabor Horvath93bc5762016-01-22 21:45:51 +0000124/// \returns decayed type for arrays and functions.
125static QualType getDecayedType(QualType Type) {
126 if (const auto *Decayed = Type->getAs<DecayedType>())
127 return Decayed->getDecayedType();
128 return Type;
129}
130
Alexander Kornienko98ba0812016-01-13 14:16:35 +0000131/// \returns true if the param types are the same.
132static bool checkParamTypes(const CXXMethodDecl *BaseMD,
133 const CXXMethodDecl *DerivedMD) {
134 unsigned NumParamA = BaseMD->getNumParams();
135 unsigned NumParamB = DerivedMD->getNumParams();
136 if (NumParamA != NumParamB)
137 return false;
138
139 for (unsigned I = 0; I < NumParamA; I++) {
Alexander Kornienko8ac20a82016-01-29 15:22:10 +0000140 if (getDecayedType(BaseMD->getParamDecl(I)->getType().getCanonicalType()) !=
141 getDecayedType(
142 DerivedMD->getParamDecl(I)->getType().getCanonicalType()))
Alexander Kornienko98ba0812016-01-13 14:16:35 +0000143 return false;
144 }
145 return true;
146}
147
148/// \returns true if derived method can override base method except for the
149/// name.
150static bool checkOverrideWithoutName(const ASTContext *Context,
151 const CXXMethodDecl *BaseMD,
152 const CXXMethodDecl *DerivedMD) {
Alexander Kornienko98ba0812016-01-13 14:16:35 +0000153 if (BaseMD->isStatic() != DerivedMD->isStatic())
154 return false;
155
Alexander Kornienko98ba0812016-01-13 14:16:35 +0000156 if (BaseMD->getType() == DerivedMD->getType())
157 return true;
158
159 // Now the function types are not identical. Then check if the return types
160 // are covariant and if the param types are the same.
161 if (!checkOverridingFunctionReturnType(Context, BaseMD, DerivedMD))
162 return false;
163 return checkParamTypes(BaseMD, DerivedMD);
164}
165
166/// Check whether BaseMD overrides DerivedMD.
167///
168/// Prerequisite: the class which BaseMD is in should be a base class of that
169/// DerivedMD is in.
170static bool checkOverrideByDerivedMethod(const CXXMethodDecl *BaseMD,
171 const CXXMethodDecl *DerivedMD) {
Alexander Kornienko8ac20a82016-01-29 15:22:10 +0000172 for (CXXMethodDecl::method_iterator I = DerivedMD->begin_overridden_methods(),
173 E = DerivedMD->end_overridden_methods();
174 I != E; ++I) {
175 const CXXMethodDecl *OverriddenMD = *I;
Alexander Kornienkobfee5f72016-01-29 15:22:20 +0000176 if (BaseMD->getCanonicalDecl() == OverriddenMD->getCanonicalDecl())
Alexander Kornienko8ac20a82016-01-29 15:22:10 +0000177 return true;
Alexander Kornienko8ac20a82016-01-29 15:22:10 +0000178 }
Alexander Kornienko98ba0812016-01-13 14:16:35 +0000179
Alexander Kornienko8ac20a82016-01-29 15:22:10 +0000180 return false;
Alexander Kornienko98ba0812016-01-13 14:16:35 +0000181}
182
183bool VirtualNearMissCheck::isPossibleToBeOverridden(
184 const CXXMethodDecl *BaseMD) {
Alexander Kornienko8ac20a82016-01-29 15:22:10 +0000185 auto Iter = PossibleMap.find(BaseMD);
Alexander Kornienko98ba0812016-01-13 14:16:35 +0000186 if (Iter != PossibleMap.end())
187 return Iter->second;
188
189 bool IsPossible = !BaseMD->isImplicit() && !isa<CXXConstructorDecl>(BaseMD) &&
Gabor Horvath93bc5762016-01-22 21:45:51 +0000190 !isa<CXXDestructorDecl>(BaseMD) && BaseMD->isVirtual() &&
Alexander Kornienkodc841502016-01-26 10:56:27 +0000191 !BaseMD->isOverloadedOperator() &&
192 !isa<CXXConversionDecl>(BaseMD);
Alexander Kornienko8ac20a82016-01-29 15:22:10 +0000193 PossibleMap[BaseMD] = IsPossible;
Alexander Kornienko98ba0812016-01-13 14:16:35 +0000194 return IsPossible;
195}
196
197bool VirtualNearMissCheck::isOverriddenByDerivedClass(
198 const CXXMethodDecl *BaseMD, const CXXRecordDecl *DerivedRD) {
Alexander Kornienko8ac20a82016-01-29 15:22:10 +0000199 auto Key = std::make_pair(BaseMD, DerivedRD);
Alexander Kornienko98ba0812016-01-13 14:16:35 +0000200 auto Iter = OverriddenMap.find(Key);
201 if (Iter != OverriddenMap.end())
202 return Iter->second;
203
204 bool IsOverridden = false;
205 for (const CXXMethodDecl *DerivedMD : DerivedRD->methods()) {
206 if (!isOverrideMethod(DerivedMD))
207 continue;
208
209 if (checkOverrideByDerivedMethod(BaseMD, DerivedMD)) {
210 IsOverridden = true;
211 break;
212 }
213 }
214 OverriddenMap[Key] = IsOverridden;
215 return IsOverridden;
216}
217
218void VirtualNearMissCheck::registerMatchers(MatchFinder *Finder) {
219 if (!getLangOpts().CPlusPlus)
220 return;
221
Gabor Horvath93bc5762016-01-22 21:45:51 +0000222 Finder->addMatcher(
Alexander Kornienkodc841502016-01-26 10:56:27 +0000223 cxxMethodDecl(
224 unless(anyOf(isOverride(), isImplicit(), cxxConstructorDecl(),
Alexander Kornienko8ac20a82016-01-29 15:22:10 +0000225 cxxDestructorDecl(), cxxConversionDecl(), isStatic(),
226 isOverloadedOperator())))
Gabor Horvath93bc5762016-01-22 21:45:51 +0000227 .bind("method"),
228 this);
Alexander Kornienko98ba0812016-01-13 14:16:35 +0000229}
230
231void VirtualNearMissCheck::check(const MatchFinder::MatchResult &Result) {
232 const auto *DerivedMD = Result.Nodes.getNodeAs<CXXMethodDecl>("method");
Gabor Horvath93bc5762016-01-22 21:45:51 +0000233 assert(DerivedMD);
Alexander Kornienko98ba0812016-01-13 14:16:35 +0000234
Alexander Kornienko98ba0812016-01-13 14:16:35 +0000235 const ASTContext *Context = Result.Context;
236
Alexander Kornienko8ac20a82016-01-29 15:22:10 +0000237 const auto *DerivedRD = DerivedMD->getParent()->getDefinition();
238 assert(DerivedRD);
Alexander Kornienko98ba0812016-01-13 14:16:35 +0000239
240 for (const auto &BaseSpec : DerivedRD->bases()) {
241 if (const auto *BaseRD = BaseSpec.getType()->getAsCXXRecordDecl()) {
242 for (const auto *BaseMD : BaseRD->methods()) {
243 if (!isPossibleToBeOverridden(BaseMD))
244 continue;
245
246 if (isOverriddenByDerivedClass(BaseMD, DerivedRD))
247 continue;
248
Benjamin Kramer14361922017-08-09 22:09:29 +0000249 unsigned EditDistance = BaseMD->getName().edit_distance(
250 DerivedMD->getName(), EditDistanceThreshold);
Alexander Kornienko98ba0812016-01-13 14:16:35 +0000251 if (EditDistance > 0 && EditDistance <= EditDistanceThreshold) {
252 if (checkOverrideWithoutName(Context, BaseMD, DerivedMD)) {
253 // A "virtual near miss" is found.
Cong Liufbd01762016-02-11 16:03:27 +0000254 auto Range = CharSourceRange::getTokenRange(
255 SourceRange(DerivedMD->getLocation()));
256
257 bool ApplyFix = !BaseMD->isTemplateInstantiation() &&
258 !DerivedMD->isTemplateInstantiation();
259 auto Diag =
260 diag(DerivedMD->getLocStart(),
261 "method '%0' has a similar name and the same signature as "
262 "virtual method '%1'; did you mean to override it?")
Alexander Kornienko98ba0812016-01-13 14:16:35 +0000263 << DerivedMD->getQualifiedNameAsString()
264 << BaseMD->getQualifiedNameAsString();
Cong Liufbd01762016-02-11 16:03:27 +0000265 if (ApplyFix)
266 Diag << FixItHint::CreateReplacement(Range, BaseMD->getName());
Alexander Kornienko98ba0812016-01-13 14:16:35 +0000267 }
268 }
269 }
270 }
271 }
272}
273
Alexander Kornienkod4ac4af2017-11-24 14:16:29 +0000274} // namespace bugprone
Alexander Kornienko98ba0812016-01-13 14:16:35 +0000275} // namespace tidy
276} // namespace clang