clang-tools 3.8.0

- Main Page
- Namespaces
- Classes
- Files

- File List
- File Members

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 misc {
21
22 /// Finds out if the given method overrides some method.
23 static bool isOverrideMethod(const CXXMethodDecl *MD) {
24 return MD->size_overridden_methods() > 0 || MD->hasAttr<OverrideAttr>();
25 }
26
27 /// Checks whether the return types are covariant, according to
28 /// C++[class.virtual]p7.
29 ///
30 /// Similar with clang::Sema::CheckOverridingFunctionReturnType.
31 /// \returns true if the return types of BaseMD and DerivedMD are covariant.
32 static bool checkOverridingFunctionReturnType(const ASTContext *Context,
33 const CXXMethodDecl *BaseMD,
34 const CXXMethodDecl *DerivedMD) {
35 QualType BaseReturnTy =
36 BaseMD->getType()->getAs<FunctionType>()->getReturnType();
37 QualType DerivedReturnTy =
38 DerivedMD->getType()->getAs<FunctionType>()->getReturnType();
39
40 if (DerivedReturnTy->isDependentType() || BaseReturnTy->isDependentType())
41 return false;
42
43 // Check if return types are identical.
44 if (Context->hasSameType(DerivedReturnTy, BaseReturnTy))
45 return true;
46
47 /// Check if the return types are covariant.
48 /// BTy is the class type in return type of BaseMD. For example,
49 /// B* Base::md()
50 /// While BRD is the declaration of B.
51 QualType BTy, DTy;
52 const CXXRecordDecl *BRD, *DRD;
53
54 // Both types must be pointers or references to classes.
55 if (const auto *DerivedPT = DerivedReturnTy->getAs<PointerType>()) {
56 if (const auto *BasePT = BaseReturnTy->getAs<PointerType>()) {
57 DTy = DerivedPT->getPointeeType();
58 BTy = BasePT->getPointeeType();
59 }
60 } else if (const auto *DerivedRT = DerivedReturnTy->getAs<ReferenceType>()) {
61 if (const auto *BaseRT = BaseReturnTy->getAs<ReferenceType>()) {
62 DTy = DerivedRT->getPointeeType();
63 BTy = BaseRT->getPointeeType();
64 }
65 }
66
67 // The return types aren't either both pointers or references to a class type.
68 if (DTy.isNull())
69 return false;
70
71 DRD = DTy->getAsCXXRecordDecl();
72 BRD = BTy->getAsCXXRecordDecl();
73 if (DRD == nullptr || BRD == nullptr)
74 return false;
75
76 if (DRD == BRD)
77 return true;
78
79 if (!Context->hasSameUnqualifiedType(DTy, BTy)) {
80 // Begin checking whether the conversion from D to B is valid.
81 CXXBasePaths Paths(/*FindAmbiguities=*/true, /*RecordPaths=*/true,
82 /*DetectVirtual=*/false);
83
84 // Check whether D is derived from B, and fill in a CXXBasePaths object.
85 if (!DRD->isDerivedFrom(BRD, Paths))
86 return false;
87
88 // Check ambiguity.
89 if (Paths.isAmbiguous(Context->getCanonicalType(BTy).getUnqualifiedType()))
90 return false;
91
92 // Check accessibility.
93 // FIXME: We currently only support checking if B is accessible base class
94 // of D, or D is the same class which DerivedMD is in.
95 bool IsItself = DRD == DerivedMD->getParent();
96 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
119 /// \returns true if the param types are the same.
120 static bool checkParamTypes(const CXXMethodDecl *BaseMD,
121 const CXXMethodDecl *DerivedMD) {
122 unsigned NumParamA = BaseMD->getNumParams();
123 unsigned NumParamB = DerivedMD->getNumParams();
124 if (NumParamA != NumParamB)
125 return false;
126
127 for (unsigned I = 0; I < NumParamA; I++) {
128 if (BaseMD->getParamDecl(I)->getType() !=
129 DerivedMD->getParamDecl(I)->getType())
130 return false;
131 }
132 return true;
133 }
134
135 /// \returns true if derived method can override base method except for the
136 /// name.
137 static bool checkOverrideWithoutName(const ASTContext *Context,
138 const CXXMethodDecl *BaseMD,
139 const CXXMethodDecl *DerivedMD) {
140 if (BaseMD->getTypeQualifiers() != DerivedMD->getTypeQualifiers())
141 return false;
142
143 if (BaseMD->isStatic() != DerivedMD->isStatic())
144 return false;
145
146 if (BaseMD->getAccess() != DerivedMD->getAccess())
147 return false;
148
149 if (BaseMD->getType() == DerivedMD->getType())
150 return true;
151
152 // Now the function types are not identical. Then check if the return types
153 // are covariant and if the param types are the same.
154 if (!checkOverridingFunctionReturnType(Context, BaseMD, DerivedMD))
155 return false;
156 return checkParamTypes(BaseMD, DerivedMD);
157 }
158
159 /// Check whether BaseMD overrides DerivedMD.
160 ///
161 /// Prerequisite: the class which BaseMD is in should be a base class of that
162 /// DerivedMD is in.
163 static bool checkOverrideByDerivedMethod(const CXXMethodDecl *BaseMD,
164 const CXXMethodDecl *DerivedMD) {
165 if (BaseMD->getNameAsString() != DerivedMD->getNameAsString())
166 return false;
167
168 if (!checkParamTypes(BaseMD, DerivedMD))
169 return false;
170
171 return true;
172 }
173
174 /// Generate unique ID for given MethodDecl.
175 ///
176 /// The Id is used as key for 'PossibleMap'.
177 /// Typical Id: "Base::func void (void)"
178 static std::string generateMethodId(const CXXMethodDecl *MD) {
179 return MD->getQualifiedNameAsString() + " " + MD->getType().getAsString();
180 }
181
182 bool VirtualNearMissCheck::isPossibleToBeOverridden(
183 const CXXMethodDecl *BaseMD) {
184 std::string Id = generateMethodId(BaseMD);
185 auto Iter = PossibleMap.find(Id);
186 if (Iter != PossibleMap.end())
187 return Iter->second;
188
189 bool IsPossible = !BaseMD->isImplicit() && !isa<CXXConstructorDecl>(BaseMD) &&
190 BaseMD->isVirtual();
191 PossibleMap[Id] = IsPossible;
192 return IsPossible;
193 }
194
195 bool VirtualNearMissCheck::isOverriddenByDerivedClass(
196 const CXXMethodDecl *BaseMD, const CXXRecordDecl *DerivedRD) {
197 auto Key = std::make_pair(generateMethodId(BaseMD),
198 DerivedRD->getQualifiedNameAsString());
199 auto Iter = OverriddenMap.find(Key);
200 if (Iter != OverriddenMap.end())
201 return Iter->second;
202
203 bool IsOverridden = false;
204 for (const CXXMethodDecl *DerivedMD : DerivedRD->methods()) {
205 if (!isOverrideMethod(DerivedMD))
206 continue;
207
208 if (checkOverrideByDerivedMethod(BaseMD, DerivedMD)) {
209 IsOverridden = true;
210 break;
211 }
212 }
213 OverriddenMap[Key] = IsOverridden;
214 return IsOverridden;
215 }
216
217 void VirtualNearMissCheck::registerMatchers(MatchFinder *Finder) {
218 if (!getLangOpts().CPlusPlus)
219 return;
220
221 Finder->addMatcher(cxxMethodDecl(unless(anyOf(isOverride(), isImplicit(),
222 cxxConstructorDecl())))
223 .bind("method"),
224 this);
225 }
226
227 void VirtualNearMissCheck::check(const MatchFinder::MatchResult &Result) {
228 const auto *DerivedMD = Result.Nodes.getNodeAs<CXXMethodDecl>("method");
229 assert(DerivedMD != nullptr);
230
231 if (DerivedMD->isStatic())
232 return;
233
234 const ASTContext *Context = Result.Context;
235
236 const auto *DerivedRD = DerivedMD->getParent();
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 =
248 BaseMD->getName().edit_distance(DerivedMD->getName());
249 if (EditDistance > 0 && EditDistance <= EditDistanceThreshold) {
250 if (checkOverrideWithoutName(Context, BaseMD, DerivedMD)) {
251 // A "virtual near miss" is found.
252 diag(DerivedMD->getLocStart(),
253 "method '%0' has a similar name and the same signature as "
254 "virtual method '%1'; did you mean to override it?")
255 << DerivedMD->getQualifiedNameAsString()
256 << BaseMD->getQualifiedNameAsString();
257 }
258 }
259 }
260 }
261 }
262 }
263
264 } // namespace misc
265 } // namespace tidy
266 } // namespace clang
Finder
std::unique_ptr< ast_matchers::MatchFinder > Finder
Definition: ClangTidy.cpp:188
clang::tidy::misc::checkParamTypes
static bool checkParamTypes(const CXXMethodDecl *BaseMD, const CXXMethodDecl *DerivedMD)
Definition: VirtualNearMissCheck.cpp:120
Path
std::vector< HeaderHandle > Path
Definition: PreprocessorTracker.cpp:538
VirtualNearMissCheck.h
clang::tidy::misc::isOverrideMethod
static bool isOverrideMethod(const CXXMethodDecl *MD)
Finds out if the given method overrides some method.
Definition: VirtualNearMissCheck.cpp:23
clang::tidy::misc::generateMethodId
static std::string generateMethodId(const CXXMethodDecl *MD)
Generate unique ID for given MethodDecl.
Definition: VirtualNearMissCheck.cpp:178
clang::tidy::misc::checkOverrideByDerivedMethod
static bool checkOverrideByDerivedMethod(const CXXMethodDecl *BaseMD, const CXXMethodDecl *DerivedMD)
Check whether BaseMD overrides DerivedMD.
Definition: VirtualNearMissCheck.cpp:163
clang::tidy::modernize::PointerType
static const char PointerType[]
Definition: MakeUniqueCheck.cpp:21
clang::tidy::misc::checkOverrideWithoutName
static bool checkOverrideWithoutName(const ASTContext *Context, const CXXMethodDecl *BaseMD, const CXXMethodDecl *DerivedMD)
Definition: VirtualNearMissCheck.cpp:137
clang::tidy::misc::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:32
Context
ClangTidyContext & Context
Definition: ClangTidy.cpp:93
Result
const NamedDecl * Result
Definition: USRFinder.cpp:121