Bug Summary

File:clang-tools-extra/clang-tidy/bugprone/VirtualNearMissCheck.cpp
Warning:line 42, column 27
Called C++ object pointer is null

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple x86_64-pc-linux-gnu -analyze -disable-free -clear-ast-before-backend -disable-llvm-verifier -discard-value-names -main-file-name VirtualNearMissCheck.cpp -analyzer-store=region -analyzer-opt-analyze-nested-blocks -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=cplusplus -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -analyzer-config-compatibility-mode=true -mrelocation-model pic -pic-level 2 -mframe-pointer=none -relaxed-aliasing -fmath-errno -ffp-contract=on -fno-rounding-math -mconstructor-aliases -funwind-tables=2 -target-cpu x86-64 -tune-cpu generic -debugger-tuning=gdb -ffunction-sections -fdata-sections -fcoverage-compilation-dir=/build/llvm-toolchain-snapshot-14~++20220119111520+da61cb019eb2/build-llvm/tools/clang/stage2-bins -resource-dir /usr/lib/llvm-14/lib/clang/14.0.0 -D _DEBUG -D _GNU_SOURCE -D __STDC_CONSTANT_MACROS -D __STDC_FORMAT_MACROS -D __STDC_LIMIT_MACROS -I tools/clang/tools/extra/clang-tidy/bugprone -I /build/llvm-toolchain-snapshot-14~++20220119111520+da61cb019eb2/clang-tools-extra/clang-tidy/bugprone -I tools/clang/tools/extra/clang-tidy -I /build/llvm-toolchain-snapshot-14~++20220119111520+da61cb019eb2/clang/include -I tools/clang/include -I include -I /build/llvm-toolchain-snapshot-14~++20220119111520+da61cb019eb2/llvm/include -D _FORTIFY_SOURCE=2 -D NDEBUG -U NDEBUG -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/10/../../../../include/c++/10 -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/10/../../../../include/x86_64-linux-gnu/c++/10 -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/10/../../../../include/c++/10/backward -internal-isystem /usr/lib/llvm-14/lib/clang/14.0.0/include -internal-isystem /usr/local/include -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/10/../../../../x86_64-linux-gnu/include -internal-externc-isystem /usr/include/x86_64-linux-gnu -internal-externc-isystem /include -internal-externc-isystem /usr/include -fmacro-prefix-map=/build/llvm-toolchain-snapshot-14~++20220119111520+da61cb019eb2/build-llvm/tools/clang/stage2-bins=build-llvm/tools/clang/stage2-bins -fmacro-prefix-map=/build/llvm-toolchain-snapshot-14~++20220119111520+da61cb019eb2/= -fcoverage-prefix-map=/build/llvm-toolchain-snapshot-14~++20220119111520+da61cb019eb2/build-llvm/tools/clang/stage2-bins=build-llvm/tools/clang/stage2-bins -fcoverage-prefix-map=/build/llvm-toolchain-snapshot-14~++20220119111520+da61cb019eb2/= -O3 -Wno-unused-command-line-argument -Wno-unused-parameter -Wwrite-strings -Wno-missing-field-initializers -Wno-long-long -Wno-maybe-uninitialized -Wno-class-memaccess -Wno-redundant-move -Wno-pessimizing-move -Wno-noexcept-type -Wno-comment -std=c++14 -fdeprecated-macro -fdebug-compilation-dir=/build/llvm-toolchain-snapshot-14~++20220119111520+da61cb019eb2/build-llvm/tools/clang/stage2-bins -fdebug-prefix-map=/build/llvm-toolchain-snapshot-14~++20220119111520+da61cb019eb2/build-llvm/tools/clang/stage2-bins=build-llvm/tools/clang/stage2-bins -fdebug-prefix-map=/build/llvm-toolchain-snapshot-14~++20220119111520+da61cb019eb2/= -ferror-limit 19 -fvisibility-inlines-hidden -stack-protector 2 -fgnuc-version=4.2.1 -fcolor-diagnostics -vectorize-loops -vectorize-slp -analyzer-output=html -analyzer-config stable-report-filename=true -faddrsig -D__GCC_HAVE_DWARF2_CFI_ASM=1 -o /tmp/scan-build-2022-01-19-134126-35450-1 -x c++ /build/llvm-toolchain-snapshot-14~++20220119111520+da61cb019eb2/clang-tools-extra/clang-tidy/bugprone/VirtualNearMissCheck.cpp
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
15using namespace clang::ast_matchers;
16
17namespace clang {
18namespace tidy {
19namespace bugprone {
20
21namespace {
22AST_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
24AST_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.
30static 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.
39static bool checkOverridingFunctionReturnType(const ASTContext *Context,
40 const CXXMethodDecl *BaseMD,
41 const CXXMethodDecl *DerivedMD) {
42 QualType BaseReturnTy = BaseMD->getType()
19
Assuming the object is not a 'FunctionType'
20
Called C++ object pointer is null
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.
124static 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.
131static 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.
149static bool checkOverrideWithoutName(const ASTContext *Context,
150 const CXXMethodDecl *BaseMD,
151 const CXXMethodDecl *DerivedMD) {
152 if (BaseMD->isStatic() != DerivedMD->isStatic())
15
Assuming the condition is false
16
Taking false branch
153 return false;
154
155 if (BaseMD->getType() == DerivedMD->getType())
17
Taking false branch
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))
18
Calling 'checkOverridingFunctionReturnType'
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.
169static 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
182bool 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
196bool 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
217void 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
227void 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__))
;
1
'?' condition is true
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__))
;
2
Assuming 'DerivedRD' is non-null
3
'?' condition is true
235
236 for (const auto &BaseSpec : DerivedRD->bases()) {
4
Assuming '__begin2' is not equal to '__end2'
237 if (const auto *BaseRD = BaseSpec.getType()->getAsCXXRecordDecl()) {
5
Assuming 'BaseRD' is non-null
6
Taking true branch
238 for (const auto *BaseMD : BaseRD->methods()) {
239 if (!isPossibleToBeOverridden(BaseMD))
7
Assuming the condition is false
8
Taking false branch
240 continue;
241
242 if (isOverriddenByDerivedClass(BaseMD, DerivedRD))
9
Assuming the condition is false
10
Taking false branch
243 continue;
244
245 unsigned EditDistance = BaseMD->getName().edit_distance(
246 DerivedMD->getName(), EditDistanceThreshold);
247 if (EditDistance > 0 && EditDistance <= EditDistanceThreshold) {
11
Assuming 'EditDistance' is > 0
12
Assuming 'EditDistance' is <= field 'EditDistanceThreshold'
13
Taking true branch
248 if (checkOverrideWithoutName(Context, BaseMD, DerivedMD)) {
14
Calling 'checkOverrideWithoutName'
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