clang-tools 8.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 namespace {
23 AST_MATCHER(CXXMethodDecl, isStatic) { return Node.isStatic(); }
24
25 AST_MATCHER(CXXMethodDecl, isOverloadedOperator) {
26 return Node.isOverloadedOperator();
27 }
28 } // namespace
29
30 /// Finds out if the given method overrides some method.
31 static 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.
40 static bool checkOverridingFunctionReturnType(const ASTContext *Context,
41 const CXXMethodDecl *BaseMD,
42 const CXXMethodDecl *DerivedMD) {
43 QualType BaseReturnTy = BaseMD->getType()
44 ->getAs<FunctionType>()
45 ->getReturnType()
46 .getCanonicalType();
47 QualType DerivedReturnTy = DerivedMD->getType()
48 ->getAs<FunctionType>()
49 ->getReturnType()
50 .getCanonicalType();
51
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.
60
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
66 /// BTy is the class type in return type of BaseMD. For example,
67 /// B* Base::md()
68 /// While BRD is the declaration of B.
69 QualType DTy = DerivedReturnTy->getPointeeType().getCanonicalType();
70 QualType BTy = BaseReturnTy->getPointeeType().getCanonicalType();
71
72 const CXXRecordDecl *DRD = DTy->getAsCXXRecordDecl();
73 const CXXRecordDecl *BRD = BTy->getAsCXXRecordDecl();
74 if (DRD == nullptr || BRD == nullptr)
75 return false;
76
77 if (!DRD->hasDefinition() || !BRD->hasDefinition())
78 return false;
79
80 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.
99 bool IsItself =
100 DRD->getCanonicalDecl() == DerivedMD->getParent()->getCanonicalDecl();
101 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
124 /// \returns decayed type for arrays and functions.
125 static QualType getDecayedType(QualType Type) {
126 if (const auto *Decayed = Type->getAs<DecayedType>())
127 return Decayed->getDecayedType();
128 return Type;
129 }
130
131 /// \returns true if the param types are the same.
132 static 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++) {
140 if (getDecayedType(BaseMD->getParamDecl(I)->getType().getCanonicalType()) !=
141 getDecayedType(
142 DerivedMD->getParamDecl(I)->getType().getCanonicalType()))
143 return false;
144 }
145 return true;
146 }
147
148 /// \returns true if derived method can override base method except for the
149 /// name.
150 static bool checkOverrideWithoutName(const ASTContext *Context,
151 const CXXMethodDecl *BaseMD,
152 const CXXMethodDecl *DerivedMD) {
153 if (BaseMD->isStatic() != DerivedMD->isStatic())
154 return false;
155
156 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.
170 static bool checkOverrideByDerivedMethod(const CXXMethodDecl *BaseMD,
171 const CXXMethodDecl *DerivedMD) {
172 for (CXXMethodDecl::method_iterator I = DerivedMD->begin_overridden_methods(),
173 E = DerivedMD->end_overridden_methods();
174 I != E; ++I) {
175 const CXXMethodDecl *OverriddenMD = *I;
176 if (BaseMD->getCanonicalDecl() == OverriddenMD->getCanonicalDecl())
177 return true;
178 }
179
180 return false;
181 }
182
183 bool VirtualNearMissCheck::isPossibleToBeOverridden(
184 const CXXMethodDecl *BaseMD) {
185 auto Iter = PossibleMap.find(BaseMD);
186 if (Iter != PossibleMap.end())
187 return Iter->second;
188
189 bool IsPossible = !BaseMD->isImplicit() && !isa<CXXConstructorDecl>(BaseMD) &&
190 !isa<CXXDestructorDecl>(BaseMD) && BaseMD->isVirtual() &&
191 !BaseMD->isOverloadedOperator() &&
192 !isa<CXXConversionDecl>(BaseMD);
193 PossibleMap[BaseMD] = IsPossible;
194 return IsPossible;
195 }
196
197 bool VirtualNearMissCheck::isOverriddenByDerivedClass(
198 const CXXMethodDecl *BaseMD, const CXXRecordDecl *DerivedRD) {
199 auto Key = std::make_pair(BaseMD, DerivedRD);
200 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
218 void VirtualNearMissCheck::registerMatchers(MatchFinder *Finder) {
219 if (!getLangOpts().CPlusPlus)
220 return;
221
222 Finder->addMatcher(
223 cxxMethodDecl(
224 unless(anyOf(isOverride(), isImplicit(), cxxConstructorDecl(),
225 cxxDestructorDecl(), cxxConversionDecl(), isStatic(),
226 isOverloadedOperator())))
227 .bind("method"),
228 this);
229 }
230
231 void VirtualNearMissCheck::check(const MatchFinder::MatchResult &Result) {
232 const auto *DerivedMD = Result.Nodes.getNodeAs<CXXMethodDecl>("method");
233 assert(DerivedMD);
234
235 const ASTContext *Context = Result.Context;
236
237 const auto *DerivedRD = DerivedMD->getParent()->getDefinition();
238 assert(DerivedRD);
239
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
249 unsigned EditDistance = BaseMD->getName().edit_distance(
250 DerivedMD->getName(), EditDistanceThreshold);
251 if (EditDistance > 0 && EditDistance <= EditDistanceThreshold) {
252 if (checkOverrideWithoutName(Context, BaseMD, DerivedMD)) {
253 // A "virtual near miss" is found.
254 auto Range = CharSourceRange::getTokenRange(
255 SourceRange(DerivedMD->getLocation()));
256
257 bool ApplyFix = !BaseMD->isTemplateInstantiation() &&
258 !DerivedMD->isTemplateInstantiation();
259 auto Diag =
260 diag(DerivedMD->getBeginLoc(),
261 "method '%0' has a similar name and the same signature as "
262 "virtual method '%1'; did you mean to override it?")
263 << DerivedMD->getQualifiedNameAsString()
264 << BaseMD->getQualifiedNameAsString();
265 if (ApplyFix)
266 Diag << FixItHint::CreateReplacement(Range, BaseMD->getName());
267 }
268 }
269 }
270 }
271 }
272 }
273
274 } // namespace bugprone
275 } // namespace tidy
276 } // namespace clang
clang::ast_matchers
Definition: AbseilMatcher.h:15
clang::tidy::matchers::AST_MATCHER
AST_MATCHER(BinaryOperator, isAssignmentOperator)
Definition: Matchers.h:20
clang::tidy::bugprone::isOverrideMethod
static bool isOverrideMethod(const CXXMethodDecl *MD)
Finds out if the given method overrides some method.
Definition: VirtualNearMissCheck.cpp:31
clang::tidy::bugprone::checkOverrideWithoutName
static bool checkOverrideWithoutName(const ASTContext *Context, const CXXMethodDecl *BaseMD, const CXXMethodDecl *DerivedMD)
Definition: VirtualNearMissCheck.cpp:150
clang::tidy::bugprone::getDecayedType
static QualType getDecayedType(QualType Type)
Definition: VirtualNearMissCheck.cpp:125
Path
std::vector< HeaderHandle > Path
Definition: PreprocessorTracker.cpp:538
clang::clangd::getReturnType
std::string getReturnType(const CodeCompletionString &CCS)
Gets detail to be used as the detail field in an LSP completion item.
Definition: CodeCompletionStrings.cpp:202
VirtualNearMissCheck.h
clang::tidy::bugprone::checkParamTypes
static bool checkParamTypes(const CXXMethodDecl *BaseMD, const CXXMethodDecl *DerivedMD)
Definition: VirtualNearMissCheck.cpp:132
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:40
Result
llvm::Optional< llvm::Expected< tooling::AtomicChanges > > Result
Definition: ClangdServer.cpp:60
clang
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
Definition: ChangeNamespace.cpp:18
clang::doc::MD
static GeneratorRegistry::Add< MDGenerator > MD(MDGenerator::Format, "Generator for MD output.")
Range
CharSourceRange Range
SourceRange for the file name.
Definition: IncludeOrderCheck.cpp:38
clang::tidy::bugprone::checkOverrideByDerivedMethod
static bool checkOverrideByDerivedMethod(const CXXMethodDecl *BaseMD, const CXXMethodDecl *DerivedMD)
Check whether BaseMD overrides DerivedMD.
Definition: VirtualNearMissCheck.cpp:170