clang-tools 6.0.0

VirtualNearMissCheck.cpp
Go to the documentation of this file.
1 //===--- 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
16 using namespace clang::ast_matchers;
17
18 namespace clang {
19 namespace tidy {
20 namespace bugprone {
21
22 AST_MATCHER(CXXMethodDecl, isStatic) { return Node.isStatic(); }
23
24 AST_MATCHER(CXXMethodDecl, isOverloadedOperator) {
25 return Node.isOverloadedOperator();
26 }
27
28 /// Finds out if the given method overrides some method.
29 static 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.
38 static bool checkOverridingFunctionReturnType(const ASTContext *Context,
39 const CXXMethodDecl *BaseMD,
40 const CXXMethodDecl *DerivedMD) {
41 QualType BaseReturnTy = BaseMD->getType()
42 ->getAs<FunctionType>()
43 ->getReturnType()
44 .getCanonicalType();
45 QualType DerivedReturnTy = DerivedMD->getType()
46 ->getAs<FunctionType>()
47 ->getReturnType()
48 .getCanonicalType();
49
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.
58
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
64 /// BTy is the class type in return type of BaseMD. For example,
65 /// B* Base::md()
66 /// While BRD is the declaration of B.
67 QualType DTy = DerivedReturnTy->getPointeeType().getCanonicalType();
68 QualType BTy = BaseReturnTy->getPointeeType().getCanonicalType();
69
70 const CXXRecordDecl *DRD = DTy->getAsCXXRecordDecl();
71 const CXXRecordDecl *BRD = BTy->getAsCXXRecordDecl();
72 if (DRD == nullptr || BRD == nullptr)
73 return false;
74
75 if (!DRD->hasDefinition() || !BRD->hasDefinition())
76 return false;
77
78 if (DRD == BRD)
79 return true;
80
81 if (!Context->hasSameUnqualifiedType(DTy, BTy)) {
82 // Begin checking whether the conversion from D to B is valid.
83 CXXBasePaths Paths(/*FindAmbiguities=*/true, /*RecordPaths=*/true,
84 /*DetectVirtual=*/false);
85
86 // Check whether D is derived from B, and fill in a CXXBasePaths object.
87 if (!DRD->isDerivedFrom(BRD, Paths))
88 return false;
89
90 // Check ambiguity.
91 if (Paths.isAmbiguous(Context->getCanonicalType(BTy).getUnqualifiedType()))
92 return false;
93
94 // Check accessibility.
95 // FIXME: We currently only support checking if B is accessible base class
96 // of D, or D is the same class which DerivedMD is in.
97 bool IsItself =
98 DRD->getCanonicalDecl() == DerivedMD->getParent()->getCanonicalDecl();
99 bool HasPublicAccess = false;
100 for (const auto &Path : Paths) {
101 if (Path.Access == AS_public)
102 HasPublicAccess = true;
103 }
104 if (!HasPublicAccess && !IsItself)
105 return false;
106 // End checking conversion from D to B.
107 }
108
109 // Both pointers or references should have the same cv-qualification.
110 if (DerivedReturnTy.getLocalCVRQualifiers() !=
111 BaseReturnTy.getLocalCVRQualifiers())
112 return false;
113
114 // The class type D should have the same cv-qualification as or less
115 // cv-qualification than the class type B.
116 if (DTy.isMoreQualifiedThan(BTy))
117 return false;
118
119 return true;
120 }
121
122 /// \returns decayed type for arrays and functions.
123 static QualType getDecayedType(QualType Type) {
124 if (const auto *Decayed = Type->getAs<DecayedType>())
125 return Decayed->getDecayedType();
126 return Type;
127 }
128
129 /// \returns true if the param types are the same.
130 static bool checkParamTypes(const CXXMethodDecl *BaseMD,
131 const CXXMethodDecl *DerivedMD) {
132 unsigned NumParamA = BaseMD->getNumParams();
133 unsigned NumParamB = DerivedMD->getNumParams();
134 if (NumParamA != NumParamB)
135 return false;
136
137 for (unsigned I = 0; I < NumParamA; I++) {
138 if (getDecayedType(BaseMD->getParamDecl(I)->getType().getCanonicalType()) !=
139 getDecayedType(
140 DerivedMD->getParamDecl(I)->getType().getCanonicalType()))
141 return false;
142 }
143 return true;
144 }
145
146 /// \returns true if derived method can override base method except for the
147 /// name.
148 static bool checkOverrideWithoutName(const ASTContext *Context,
149 const CXXMethodDecl *BaseMD,
150 const CXXMethodDecl *DerivedMD) {
151 if (BaseMD->isStatic() != DerivedMD->isStatic())
152 return false;
153
154 if (BaseMD->getType() == DerivedMD->getType())
155 return true;
156
157 // Now the function types are not identical. Then check if the return types
158 // are covariant and if the param types are the same.
159 if (!checkOverridingFunctionReturnType(Context, BaseMD, DerivedMD))
160 return false;
161 return checkParamTypes(BaseMD, DerivedMD);
162 }
163
164 /// Check whether BaseMD overrides DerivedMD.
165 ///
166 /// Prerequisite: the class which BaseMD is in should be a base class of that
167 /// DerivedMD is in.
168 static bool checkOverrideByDerivedMethod(const CXXMethodDecl *BaseMD,
169 const CXXMethodDecl *DerivedMD) {
170 for (CXXMethodDecl::method_iterator I = DerivedMD->begin_overridden_methods(),
171 E = DerivedMD->end_overridden_methods();
172 I != E; ++I) {
173 const CXXMethodDecl *OverriddenMD = *I;
174 if (BaseMD->getCanonicalDecl() == OverriddenMD->getCanonicalDecl())
175 return true;
176 }
177
178 return false;
179 }
180
181 bool VirtualNearMissCheck::isPossibleToBeOverridden(
182 const CXXMethodDecl *BaseMD) {
183 auto Iter = PossibleMap.find(BaseMD);
184 if (Iter != PossibleMap.end())
185 return Iter->second;
186
187 bool IsPossible = !BaseMD->isImplicit() && !isa<CXXConstructorDecl>(BaseMD) &&
188 !isa<CXXDestructorDecl>(BaseMD) && BaseMD->isVirtual() &&
189 !BaseMD->isOverloadedOperator() &&
190 !isa<CXXConversionDecl>(BaseMD);
191 PossibleMap[BaseMD] = IsPossible;
192 return IsPossible;
193 }
194
195 bool VirtualNearMissCheck::isOverriddenByDerivedClass(
196 const CXXMethodDecl *BaseMD, const CXXRecordDecl *DerivedRD) {
197 auto Key = std::make_pair(BaseMD, DerivedRD);
198 auto Iter = OverriddenMap.find(Key);
199 if (Iter != OverriddenMap.end())
200 return Iter->second;
201
202 bool IsOverridden = false;
203 for (const CXXMethodDecl *DerivedMD : DerivedRD->methods()) {
204 if (!isOverrideMethod(DerivedMD))
205 continue;
206
207 if (checkOverrideByDerivedMethod(BaseMD, DerivedMD)) {
208 IsOverridden = true;
209 break;
210 }
211 }
212 OverriddenMap[Key] = IsOverridden;
213 return IsOverridden;
214 }
215
216 void VirtualNearMissCheck::registerMatchers(MatchFinder *Finder) {
217 if (!getLangOpts().CPlusPlus)
218 return;
219
220 Finder->addMatcher(
221 cxxMethodDecl(
222 unless(anyOf(isOverride(), isImplicit(), cxxConstructorDecl(),
223 cxxDestructorDecl(), cxxConversionDecl(), isStatic(),
224 isOverloadedOperator())))
225 .bind("method"),
226 this);
227 }
228
229 void VirtualNearMissCheck::check(const MatchFinder::MatchResult &Result) {
230 const auto *DerivedMD = Result.Nodes.getNodeAs<CXXMethodDecl>("method");
231 assert(DerivedMD);
232
233 const ASTContext *Context = Result.Context;
234
235 const auto *DerivedRD = DerivedMD->getParent()->getDefinition();
236 assert(DerivedRD);
237
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))
242 continue;
243
244 if (isOverriddenByDerivedClass(BaseMD, DerivedRD))
245 continue;
246
247 unsigned EditDistance = BaseMD->getName().edit_distance(
248 DerivedMD->getName(), EditDistanceThreshold);
249 if (EditDistance > 0 && EditDistance <= EditDistanceThreshold) {
250 if (checkOverrideWithoutName(Context, BaseMD, DerivedMD)) {
251 // A "virtual near miss" is found.
252 auto Range = CharSourceRange::getTokenRange(
253 SourceRange(DerivedMD->getLocation()));
254
255 bool ApplyFix = !BaseMD->isTemplateInstantiation() &&
256 !DerivedMD->isTemplateInstantiation();
257 auto Diag =
258 diag(DerivedMD->getLocStart(),
259 "method '%0' has a similar name and the same signature as "
260 "virtual method '%1'; did you mean to override it?")
261 << DerivedMD->getQualifiedNameAsString()
262 << BaseMD->getQualifiedNameAsString();
263 if (ApplyFix)
264 Diag << FixItHint::CreateReplacement(Range, BaseMD->getName());
265 }
266 }
267 }
268 }
269 }
270 }
271
272 } // namespace bugprone
273 } // namespace tidy
274 } // namespace clang
clang::ast_matchers
Definition: ClangTidyDiagnosticConsumer.h:27
clang::tidy::bugprone::isOverrideMethod
static bool isOverrideMethod(const CXXMethodDecl *MD)
Finds out if the given method overrides some method.
Definition: VirtualNearMissCheck.cpp:29
clang::tidy::bugprone::checkOverrideWithoutName
static bool checkOverrideWithoutName(const ASTContext *Context, const CXXMethodDecl *BaseMD, const CXXMethodDecl *DerivedMD)
Definition: VirtualNearMissCheck.cpp:148
clang::tidy::bugprone::getDecayedType
static QualType getDecayedType(QualType Type)
Definition: VirtualNearMissCheck.cpp:123
Path
std::vector< HeaderHandle > Path
Definition: PreprocessorTracker.cpp:538
VirtualNearMissCheck.h
clang::tidy::bugprone::checkParamTypes
static bool checkParamTypes(const CXXMethodDecl *BaseMD, const CXXMethodDecl *DerivedMD)
Definition: VirtualNearMissCheck.cpp:130
clang::tidy::bugprone::checkOverridingFunctionReturnType
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. ...
Definition: VirtualNearMissCheck.cpp:38
clang
Definition: AndroidTidyModule.cpp:28
Range
CharSourceRange Range
SourceRange for the file name.
Definition: IncludeOrderCheck.cpp:37
clang::tidy::bugprone::checkOverrideByDerivedMethod
static bool checkOverrideByDerivedMethod(const CXXMethodDecl *BaseMD, const CXXMethodDecl *DerivedMD)
Check whether BaseMD overrides DerivedMD.
Definition: VirtualNearMissCheck.cpp:168
clang::ast_matchers::AST_MATCHER
AST_MATCHER(VarDecl, isAsm)
Definition: NoAssemblerCheck.cpp:18