File: | clang-tools-extra/clang-tidy/bugprone/VirtualNearMissCheck.cpp |
Warning: | line 46, column 30 Called C++ object pointer is null |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
1 | //===--- VirtualNearMissCheck.cpp - clang-tidy-----------------------------===// | |||
2 | // | |||
3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | |||
4 | // See https://llvm.org/LICENSE.txt for license information. | |||
5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | |||
6 | // | |||
7 | //===----------------------------------------------------------------------===// | |||
8 | ||||
9 | #include "VirtualNearMissCheck.h" | |||
10 | #include "clang/AST/ASTContext.h" | |||
11 | #include "clang/AST/CXXInheritance.h" | |||
12 | #include "clang/ASTMatchers/ASTMatchFinder.h" | |||
13 | #include "clang/Lex/Lexer.h" | |||
14 | ||||
15 | using namespace clang::ast_matchers; | |||
16 | ||||
17 | namespace clang { | |||
18 | namespace tidy { | |||
19 | namespace bugprone { | |||
20 | ||||
21 | namespace { | |||
22 | AST_MATCHER(CXXMethodDecl, isStatic)namespace internal { class matcher_isStaticMatcher : public :: clang::ast_matchers::internal::MatcherInterface<CXXMethodDecl > { public: explicit matcher_isStaticMatcher() = default; bool matches(const CXXMethodDecl &Node, ::clang::ast_matchers ::internal::ASTMatchFinder *Finder, ::clang::ast_matchers::internal ::BoundNodesTreeBuilder *Builder) const override; }; } inline ::clang::ast_matchers::internal::Matcher<CXXMethodDecl> isStatic() { return ::clang::ast_matchers::internal::makeMatcher ( new internal::matcher_isStaticMatcher()); } inline bool internal ::matcher_isStaticMatcher::matches( const CXXMethodDecl & Node, ::clang::ast_matchers::internal::ASTMatchFinder *Finder , ::clang::ast_matchers::internal::BoundNodesTreeBuilder *Builder ) const { return Node.isStatic(); } | |||
23 | ||||
24 | AST_MATCHER(CXXMethodDecl, isOverloadedOperator)namespace internal { class matcher_isOverloadedOperatorMatcher : public ::clang::ast_matchers::internal::MatcherInterface< CXXMethodDecl> { public: explicit matcher_isOverloadedOperatorMatcher () = default; bool matches(const CXXMethodDecl &Node, ::clang ::ast_matchers::internal::ASTMatchFinder *Finder, ::clang::ast_matchers ::internal::BoundNodesTreeBuilder *Builder) const override; } ; } inline ::clang::ast_matchers::internal::Matcher<CXXMethodDecl > isOverloadedOperator() { return ::clang::ast_matchers::internal ::makeMatcher( new internal::matcher_isOverloadedOperatorMatcher ()); } inline bool internal::matcher_isOverloadedOperatorMatcher ::matches( const CXXMethodDecl &Node, ::clang::ast_matchers ::internal::ASTMatchFinder *Finder, ::clang::ast_matchers::internal ::BoundNodesTreeBuilder *Builder) const { | |||
25 | return Node.isOverloadedOperator(); | |||
26 | } | |||
27 | } // namespace | |||
28 | ||||
29 | /// Finds out if the given method overrides some method. | |||
30 | static bool isOverrideMethod(const CXXMethodDecl *MD) { | |||
31 | return MD->size_overridden_methods() > 0 || MD->hasAttr<OverrideAttr>(); | |||
32 | } | |||
33 | ||||
34 | /// Checks whether the return types are covariant, according to | |||
35 | /// C++[class.virtual]p7. | |||
36 | /// | |||
37 | /// Similar with clang::Sema::CheckOverridingFunctionReturnType. | |||
38 | /// \returns true if the return types of BaseMD and DerivedMD are covariant. | |||
39 | static bool checkOverridingFunctionReturnType(const ASTContext *Context, | |||
40 | const CXXMethodDecl *BaseMD, | |||
41 | const CXXMethodDecl *DerivedMD) { | |||
42 | QualType BaseReturnTy = BaseMD->getType() | |||
43 | ->getAs<FunctionType>() | |||
44 | ->getReturnType() | |||
45 | .getCanonicalType(); | |||
46 | QualType DerivedReturnTy = DerivedMD->getType() | |||
| ||||
47 | ->getAs<FunctionType>() | |||
48 | ->getReturnType() | |||
49 | .getCanonicalType(); | |||
50 | ||||
51 | if (DerivedReturnTy->isDependentType() || BaseReturnTy->isDependentType()) | |||
52 | return false; | |||
53 | ||||
54 | // Check if return types are identical. | |||
55 | if (Context->hasSameType(DerivedReturnTy, BaseReturnTy)) | |||
56 | return true; | |||
57 | ||||
58 | /// Check if the return types are covariant. | |||
59 | ||||
60 | // Both types must be pointers or references to classes. | |||
61 | if (!(BaseReturnTy->isPointerType() && DerivedReturnTy->isPointerType()) && | |||
62 | !(BaseReturnTy->isReferenceType() && DerivedReturnTy->isReferenceType())) | |||
63 | return false; | |||
64 | ||||
65 | /// BTy is the class type in return type of BaseMD. For example, | |||
66 | /// B* Base::md() | |||
67 | /// While BRD is the declaration of B. | |||
68 | QualType DTy = DerivedReturnTy->getPointeeType().getCanonicalType(); | |||
69 | QualType BTy = BaseReturnTy->getPointeeType().getCanonicalType(); | |||
70 | ||||
71 | const CXXRecordDecl *DRD = DTy->getAsCXXRecordDecl(); | |||
72 | const CXXRecordDecl *BRD = BTy->getAsCXXRecordDecl(); | |||
73 | if (DRD == nullptr || BRD == nullptr) | |||
74 | return false; | |||
75 | ||||
76 | if (!DRD->hasDefinition() || !BRD->hasDefinition()) | |||
77 | return false; | |||
78 | ||||
79 | if (DRD == BRD) | |||
80 | return true; | |||
81 | ||||
82 | if (!Context->hasSameUnqualifiedType(DTy, BTy)) { | |||
83 | // Begin checking whether the conversion from D to B is valid. | |||
84 | CXXBasePaths Paths(/*FindAmbiguities=*/true, /*RecordPaths=*/true, | |||
85 | /*DetectVirtual=*/false); | |||
86 | ||||
87 | // Check whether D is derived from B, and fill in a CXXBasePaths object. | |||
88 | if (!DRD->isDerivedFrom(BRD, Paths)) | |||
89 | return false; | |||
90 | ||||
91 | // Check ambiguity. | |||
92 | if (Paths.isAmbiguous(Context->getCanonicalType(BTy).getUnqualifiedType())) | |||
93 | return false; | |||
94 | ||||
95 | // Check accessibility. | |||
96 | // FIXME: We currently only support checking if B is accessible base class | |||
97 | // of D, or D is the same class which DerivedMD is in. | |||
98 | bool IsItself = | |||
99 | DRD->getCanonicalDecl() == DerivedMD->getParent()->getCanonicalDecl(); | |||
100 | bool HasPublicAccess = false; | |||
101 | for (const auto &Path : Paths) { | |||
102 | if (Path.Access == AS_public) | |||
103 | HasPublicAccess = true; | |||
104 | } | |||
105 | if (!HasPublicAccess && !IsItself) | |||
106 | return false; | |||
107 | // End checking conversion from D to B. | |||
108 | } | |||
109 | ||||
110 | // Both pointers or references should have the same cv-qualification. | |||
111 | if (DerivedReturnTy.getLocalCVRQualifiers() != | |||
112 | BaseReturnTy.getLocalCVRQualifiers()) | |||
113 | return false; | |||
114 | ||||
115 | // The class type D should have the same cv-qualification as or less | |||
116 | // cv-qualification than the class type B. | |||
117 | if (DTy.isMoreQualifiedThan(BTy)) | |||
118 | return false; | |||
119 | ||||
120 | return true; | |||
121 | } | |||
122 | ||||
123 | /// \returns decayed type for arrays and functions. | |||
124 | static QualType getDecayedType(QualType Type) { | |||
125 | if (const auto *Decayed = Type->getAs<DecayedType>()) | |||
126 | return Decayed->getDecayedType(); | |||
127 | return Type; | |||
128 | } | |||
129 | ||||
130 | /// \returns true if the param types are the same. | |||
131 | static bool checkParamTypes(const CXXMethodDecl *BaseMD, | |||
132 | const CXXMethodDecl *DerivedMD) { | |||
133 | unsigned NumParamA = BaseMD->getNumParams(); | |||
134 | unsigned NumParamB = DerivedMD->getNumParams(); | |||
135 | if (NumParamA != NumParamB) | |||
136 | return false; | |||
137 | ||||
138 | for (unsigned I = 0; I < NumParamA; I++) { | |||
139 | if (getDecayedType(BaseMD->getParamDecl(I)->getType().getCanonicalType()) != | |||
140 | getDecayedType( | |||
141 | DerivedMD->getParamDecl(I)->getType().getCanonicalType())) | |||
142 | return false; | |||
143 | } | |||
144 | return true; | |||
145 | } | |||
146 | ||||
147 | /// \returns true if derived method can override base method except for the | |||
148 | /// name. | |||
149 | static bool checkOverrideWithoutName(const ASTContext *Context, | |||
150 | const CXXMethodDecl *BaseMD, | |||
151 | const CXXMethodDecl *DerivedMD) { | |||
152 | if (BaseMD->isStatic() != DerivedMD->isStatic()) | |||
153 | return false; | |||
154 | ||||
155 | if (BaseMD->getType() == DerivedMD->getType()) | |||
156 | return true; | |||
157 | ||||
158 | // Now the function types are not identical. Then check if the return types | |||
159 | // are covariant and if the param types are the same. | |||
160 | if (!checkOverridingFunctionReturnType(Context, BaseMD, DerivedMD)) | |||
161 | return false; | |||
162 | return checkParamTypes(BaseMD, DerivedMD); | |||
163 | } | |||
164 | ||||
165 | /// Check whether BaseMD overrides DerivedMD. | |||
166 | /// | |||
167 | /// Prerequisite: the class which BaseMD is in should be a base class of that | |||
168 | /// DerivedMD is in. | |||
169 | static bool checkOverrideByDerivedMethod(const CXXMethodDecl *BaseMD, | |||
170 | const CXXMethodDecl *DerivedMD) { | |||
171 | for (CXXMethodDecl::method_iterator I = DerivedMD->begin_overridden_methods(), | |||
172 | E = DerivedMD->end_overridden_methods(); | |||
173 | I != E; ++I) { | |||
174 | const CXXMethodDecl *OverriddenMD = *I; | |||
175 | if (BaseMD->getCanonicalDecl() == OverriddenMD->getCanonicalDecl()) | |||
176 | return true; | |||
177 | } | |||
178 | ||||
179 | return false; | |||
180 | } | |||
181 | ||||
182 | bool VirtualNearMissCheck::isPossibleToBeOverridden( | |||
183 | const CXXMethodDecl *BaseMD) { | |||
184 | auto Iter = PossibleMap.find(BaseMD); | |||
185 | if (Iter != PossibleMap.end()) | |||
186 | return Iter->second; | |||
187 | ||||
188 | bool IsPossible = !BaseMD->isImplicit() && !isa<CXXConstructorDecl>(BaseMD) && | |||
189 | !isa<CXXDestructorDecl>(BaseMD) && BaseMD->isVirtual() && | |||
190 | !BaseMD->isOverloadedOperator() && | |||
191 | !isa<CXXConversionDecl>(BaseMD); | |||
192 | PossibleMap[BaseMD] = IsPossible; | |||
193 | return IsPossible; | |||
194 | } | |||
195 | ||||
196 | bool VirtualNearMissCheck::isOverriddenByDerivedClass( | |||
197 | const CXXMethodDecl *BaseMD, const CXXRecordDecl *DerivedRD) { | |||
198 | auto Key = std::make_pair(BaseMD, DerivedRD); | |||
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 | Finder->addMatcher( | |||
219 | cxxMethodDecl( | |||
220 | unless(anyOf(isOverride(), isImplicit(), cxxConstructorDecl(), | |||
221 | cxxDestructorDecl(), cxxConversionDecl(), isStatic(), | |||
222 | isOverloadedOperator()))) | |||
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)(static_cast <bool> (DerivedMD) ? void (0) : __assert_fail ("DerivedMD", "clang-tools-extra/clang-tidy/bugprone/VirtualNearMissCheck.cpp" , 229, __extension__ __PRETTY_FUNCTION__)); | |||
| ||||
230 | ||||
231 | const ASTContext *Context = Result.Context; | |||
232 | ||||
233 | const auto *DerivedRD = DerivedMD->getParent()->getDefinition(); | |||
234 | assert(DerivedRD)(static_cast <bool> (DerivedRD) ? void (0) : __assert_fail ("DerivedRD", "clang-tools-extra/clang-tidy/bugprone/VirtualNearMissCheck.cpp" , 234, __extension__ __PRETTY_FUNCTION__)); | |||
235 | ||||
236 | for (const auto &BaseSpec : DerivedRD->bases()) { | |||
237 | if (const auto *BaseRD = BaseSpec.getType()->getAsCXXRecordDecl()) { | |||
238 | for (const auto *BaseMD : BaseRD->methods()) { | |||
239 | if (!isPossibleToBeOverridden(BaseMD)) | |||
240 | continue; | |||
241 | ||||
242 | if (isOverriddenByDerivedClass(BaseMD, DerivedRD)) | |||
243 | continue; | |||
244 | ||||
245 | unsigned EditDistance = BaseMD->getName().edit_distance( | |||
246 | DerivedMD->getName(), EditDistanceThreshold); | |||
247 | if (EditDistance > 0 && EditDistance <= EditDistanceThreshold) { | |||
248 | if (checkOverrideWithoutName(Context, BaseMD, DerivedMD)) { | |||
249 | // A "virtual near miss" is found. | |||
250 | auto Range = CharSourceRange::getTokenRange( | |||
251 | SourceRange(DerivedMD->getLocation())); | |||
252 | ||||
253 | bool ApplyFix = !BaseMD->isTemplateInstantiation() && | |||
254 | !DerivedMD->isTemplateInstantiation(); | |||
255 | auto Diag = | |||
256 | diag(DerivedMD->getBeginLoc(), | |||
257 | "method '%0' has a similar name and the same signature as " | |||
258 | "virtual method '%1'; did you mean to override it?") | |||
259 | << DerivedMD->getQualifiedNameAsString() | |||
260 | << BaseMD->getQualifiedNameAsString(); | |||
261 | if (ApplyFix) | |||
262 | Diag << FixItHint::CreateReplacement(Range, BaseMD->getName()); | |||
263 | } | |||
264 | } | |||
265 | } | |||
266 | } | |||
267 | } | |||
268 | } | |||
269 | ||||
270 | } // namespace bugprone | |||
271 | } // namespace tidy | |||
272 | } // namespace clang |