Skip to content

Commit 09273d4

Browse files
authored
Fix WebKit static analyzers to support ref and deref methods to be defined on different classes. (#69985)
1 parent 9e13d64 commit 09273d4

File tree

5 files changed

+126
-36
lines changed

5 files changed

+126
-36
lines changed

clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp

Lines changed: 34 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -18,24 +18,15 @@ using namespace clang;
1818

1919
namespace {
2020

21-
bool hasPublicRefAndDeref(const CXXRecordDecl *R) {
21+
bool hasPublicMethodInBaseClass(const CXXRecordDecl *R,
22+
const char *NameToMatch) {
2223
assert(R);
2324
assert(R->hasDefinition());
2425

25-
bool hasRef = false;
26-
bool hasDeref = false;
2726
for (const CXXMethodDecl *MD : R->methods()) {
2827
const auto MethodName = safeGetName(MD);
29-
30-
if (MethodName == "ref" && MD->getAccess() == AS_public) {
31-
if (hasDeref)
32-
return true;
33-
hasRef = true;
34-
} else if (MethodName == "deref" && MD->getAccess() == AS_public) {
35-
if (hasRef)
36-
return true;
37-
hasDeref = true;
38-
}
28+
if (MethodName == NameToMatch && MD->getAccess() == AS_public)
29+
return true;
3930
}
4031
return false;
4132
}
@@ -44,9 +35,8 @@ bool hasPublicRefAndDeref(const CXXRecordDecl *R) {
4435

4536
namespace clang {
4637

47-
std::optional<const clang::CXXRecordDecl*>
48-
isRefCountable(const CXXBaseSpecifier* Base)
49-
{
38+
std::optional<const clang::CXXRecordDecl *>
39+
hasPublicMethodInBase(const CXXBaseSpecifier *Base, const char *NameToMatch) {
5040
assert(Base);
5141

5242
const Type *T = Base->getType().getTypePtrOrNull();
@@ -59,7 +49,7 @@ isRefCountable(const CXXBaseSpecifier* Base)
5949
if (!R->hasDefinition())
6050
return std::nullopt;
6151

62-
return hasPublicRefAndDeref(R) ? R : nullptr;
52+
return hasPublicMethodInBaseClass(R, NameToMatch) ? R : nullptr;
6353
}
6454

6555
std::optional<bool> isRefCountable(const CXXRecordDecl* R)
@@ -70,29 +60,45 @@ std::optional<bool> isRefCountable(const CXXRecordDecl* R)
7060
if (!R)
7161
return std::nullopt;
7262

73-
if (hasPublicRefAndDeref(R))
63+
bool hasRef = hasPublicMethodInBaseClass(R, "ref");
64+
bool hasDeref = hasPublicMethodInBaseClass(R, "deref");
65+
if (hasRef && hasDeref)
7466
return true;
7567

7668
CXXBasePaths Paths;
7769
Paths.setOrigin(const_cast<CXXRecordDecl *>(R));
7870

7971
bool AnyInconclusiveBase = false;
80-
const auto isRefCountableBase =
81-
[&AnyInconclusiveBase](const CXXBaseSpecifier* Base, CXXBasePath&) {
82-
std::optional<const clang::CXXRecordDecl*> IsRefCountable = clang::isRefCountable(Base);
83-
if (!IsRefCountable) {
84-
AnyInconclusiveBase = true;
85-
return false;
86-
}
87-
return (*IsRefCountable) != nullptr;
72+
const auto hasPublicRefInBase =
73+
[&AnyInconclusiveBase](const CXXBaseSpecifier *Base, CXXBasePath &) {
74+
auto hasRefInBase = clang::hasPublicMethodInBase(Base, "ref");
75+
if (!hasRefInBase) {
76+
AnyInconclusiveBase = true;
77+
return false;
78+
}
79+
return (*hasRefInBase) != nullptr;
8880
};
8981

90-
bool BasesResult = R->lookupInBases(isRefCountableBase, Paths,
82+
hasRef = hasRef || R->lookupInBases(hasPublicRefInBase, Paths,
9183
/*LookupInDependent =*/true);
9284
if (AnyInconclusiveBase)
9385
return std::nullopt;
9486

95-
return BasesResult;
87+
const auto hasPublicDerefInBase =
88+
[&AnyInconclusiveBase](const CXXBaseSpecifier *Base, CXXBasePath &) {
89+
auto hasDerefInBase = clang::hasPublicMethodInBase(Base, "deref");
90+
if (!hasDerefInBase) {
91+
AnyInconclusiveBase = true;
92+
return false;
93+
}
94+
return (*hasDerefInBase) != nullptr;
95+
};
96+
hasDeref = hasDeref || R->lookupInBases(hasPublicDerefInBase, Paths,
97+
/*LookupInDependent =*/true);
98+
if (AnyInconclusiveBase)
99+
return std::nullopt;
100+
101+
return hasRef && hasDeref;
96102
}
97103

98104
bool isCtorOfRefCounted(const clang::FunctionDecl *F) {

clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.h

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,10 +26,10 @@ class Type;
2626
// In WebKit there are two ref-counted templated smart pointers: RefPtr<T> and
2727
// Ref<T>.
2828

29-
/// \returns CXXRecordDecl of the base if the type is ref-countable, nullptr if
30-
/// not, std::nullopt if inconclusive.
31-
std::optional<const clang::CXXRecordDecl*>
32-
isRefCountable(const clang::CXXBaseSpecifier* Base);
29+
/// \returns CXXRecordDecl of the base if the type has ref as a public method,
30+
/// nullptr if not, std::nullopt if inconclusive.
31+
std::optional<const clang::CXXRecordDecl *>
32+
hasPublicMethodInBase(const CXXBaseSpecifier *Base, const char *NameToMatch);
3333

3434
/// \returns true if \p Class is ref-countable, false if not, std::nullopt if
3535
/// inconclusive.

clang/lib/StaticAnalyzer/Checkers/WebKit/RefCntblBaseVirtualDtorChecker.cpp

Lines changed: 43 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -77,14 +77,53 @@ class RefCntblBaseVirtualDtorChecker
7777
(AccSpec == AS_none && RD->isClass()))
7878
return false;
7979

80-
std::optional<const CXXRecordDecl*> RefCntblBaseRD = isRefCountable(Base);
81-
if (!RefCntblBaseRD || !(*RefCntblBaseRD))
80+
auto hasRefInBase = clang::hasPublicMethodInBase(Base, "ref");
81+
auto hasDerefInBase = clang::hasPublicMethodInBase(Base, "deref");
82+
83+
bool hasRef = hasRefInBase && *hasRefInBase != nullptr;
84+
bool hasDeref = hasDerefInBase && *hasDerefInBase != nullptr;
85+
86+
QualType T = Base->getType();
87+
if (T.isNull())
88+
return false;
89+
90+
const CXXRecordDecl *C = T->getAsCXXRecordDecl();
91+
if (!C)
92+
return false;
93+
bool AnyInconclusiveBase = false;
94+
const auto hasPublicRefInBase =
95+
[&AnyInconclusiveBase](const CXXBaseSpecifier *Base,
96+
CXXBasePath &) {
97+
auto hasRefInBase = clang::hasPublicMethodInBase(Base, "ref");
98+
if (!hasRefInBase) {
99+
AnyInconclusiveBase = true;
100+
return false;
101+
}
102+
return (*hasRefInBase) != nullptr;
103+
};
104+
const auto hasPublicDerefInBase = [&AnyInconclusiveBase](
105+
const CXXBaseSpecifier *Base,
106+
CXXBasePath &) {
107+
auto hasDerefInBase = clang::hasPublicMethodInBase(Base, "deref");
108+
if (!hasDerefInBase) {
109+
AnyInconclusiveBase = true;
110+
return false;
111+
}
112+
return (*hasDerefInBase) != nullptr;
113+
};
114+
CXXBasePaths Paths;
115+
Paths.setOrigin(C);
116+
hasRef = hasRef || C->lookupInBases(hasPublicRefInBase, Paths,
117+
/*LookupInDependent =*/true);
118+
hasDeref = hasDeref || C->lookupInBases(hasPublicDerefInBase, Paths,
119+
/*LookupInDependent =*/true);
120+
if (AnyInconclusiveBase || !hasRef || !hasDeref)
82121
return false;
83122

84-
const auto *Dtor = (*RefCntblBaseRD)->getDestructor();
123+
const auto *Dtor = C->getDestructor();
85124
if (!Dtor || !Dtor->isVirtual()) {
86125
ProblematicBaseSpecifier = Base;
87-
ProblematicBaseClass = *RefCntblBaseRD;
126+
ProblematicBaseClass = C;
88127
return true;
89128
}
90129

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
// RUN: %clang_analyze_cc1 -analyzer-checker=webkit.RefCntblBaseVirtualDtor -verify %s
2+
3+
struct RefCountedBase {
4+
void ref() {}
5+
};
6+
7+
template<typename T> struct RefCounted : RefCountedBase {
8+
public:
9+
void deref() const { }
10+
};
11+
12+
struct Base : RefCounted<Base> {
13+
// expected-warning@-1{{Struct 'RefCounted<Base>' is used as a base of struct 'Base' but doesn't have virtual destructor}}
14+
virtual void foo() { }
15+
};
16+
17+
struct Derived : Base { };
18+
// expected-warning@-1{{Struct 'Base' is used as a base of struct 'Derived' but doesn't have virtual destructor}}
19+
20+
void foo () {
21+
Derived d;
22+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
// RUN: %clang_analyze_cc1 -analyzer-checker=webkit.NoUncountedMemberChecker -verify %s
2+
3+
#include "mock-types.h"
4+
5+
class RefCountedBase {
6+
public:
7+
void ref() const { }
8+
};
9+
10+
template<typename T> class RefCounted : public RefCountedBase {
11+
public:
12+
virtual ~RefCounted() { }
13+
void deref() const { }
14+
};
15+
16+
class TreeNode : public RefCounted<TreeNode> {
17+
public:
18+
void setParent(TreeNode& parent) { m_parent = &parent; }
19+
20+
private:
21+
TreeNode* m_parent;
22+
// expected-warning@-1{{Member variable 'm_parent' in 'TreeNode' is a raw pointer to ref-countable type 'TreeNode'}}
23+
};

0 commit comments

Comments
 (0)