Skip to content

Commit 5f8b256

Browse files
authored
Check the type of Objective-C++ instance variables in WebKit member variable checkers. (#127570)
Like a C++ member variable, every Objective-C++ instance variable must be a RefPtr, Ref CheckedPtr, or CheckedRef to an object, not a raw pointer or reference.
1 parent c0c42c8 commit 5f8b256

File tree

3 files changed

+107
-3
lines changed

3 files changed

+107
-3
lines changed

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

Lines changed: 37 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,11 @@ class RawPtrRefMemberChecker
6161
Checker->visitRecordDecl(RD);
6262
return true;
6363
}
64+
65+
bool VisitObjCContainerDecl(const ObjCContainerDecl *CD) override {
66+
Checker->visitObjCDecl(CD);
67+
return true;
68+
}
6469
};
6570

6671
LocalVisitor visitor(this);
@@ -87,6 +92,31 @@ class RawPtrRefMemberChecker
8792
}
8893
}
8994

95+
void visitObjCDecl(const ObjCContainerDecl *CD) const {
96+
if (auto *ID = dyn_cast<ObjCInterfaceDecl>(CD)) {
97+
for (auto *Ivar : ID->ivars())
98+
visitIvarDecl(CD, Ivar);
99+
return;
100+
}
101+
if (auto *ID = dyn_cast<ObjCImplementationDecl>(CD)) {
102+
for (auto *Ivar : ID->ivars())
103+
visitIvarDecl(CD, Ivar);
104+
return;
105+
}
106+
}
107+
108+
void visitIvarDecl(const ObjCContainerDecl *CD,
109+
const ObjCIvarDecl *Ivar) const {
110+
const Type *IvarType = Ivar->getType().getTypePtrOrNull();
111+
if (!IvarType)
112+
return;
113+
if (auto *IvarCXXRD = IvarType->getPointeeCXXRecordDecl()) {
114+
std::optional<bool> IsCompatible = isPtrCompatible(IvarCXXRD);
115+
if (IsCompatible && *IsCompatible)
116+
reportBug(Ivar, IvarType, IvarCXXRD, CD);
117+
}
118+
}
119+
90120
bool shouldSkipDecl(const RecordDecl *RD) const {
91121
if (!RD->isThisDeclarationADefinition())
92122
return true;
@@ -121,17 +151,21 @@ class RawPtrRefMemberChecker
121151
return false;
122152
}
123153

124-
void reportBug(const FieldDecl *Member, const Type *MemberType,
154+
template <typename DeclType, typename ParentDeclType>
155+
void reportBug(const DeclType *Member, const Type *MemberType,
125156
const CXXRecordDecl *MemberCXXRD,
126-
const RecordDecl *ClassCXXRD) const {
157+
const ParentDeclType *ClassCXXRD) const {
127158
assert(Member);
128159
assert(MemberType);
129160
assert(MemberCXXRD);
130161

131162
SmallString<100> Buf;
132163
llvm::raw_svector_ostream Os(Buf);
133164

134-
Os << "Member variable ";
165+
if (isa<ObjCContainerDecl>(ClassCXXRD))
166+
Os << "Instance variable ";
167+
else
168+
Os << "Member variable ";
135169
printQuotedName(Os, Member);
136170
Os << " in ";
137171
printQuotedQualifiedName(Os, ClassCXXRD);
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
// RUN: %clang_analyze_cc1 -analyzer-checker=alpha.webkit.NoUncheckedPtrMemberChecker -verify %s
2+
3+
#include "mock-types.h"
4+
5+
__attribute__((objc_root_class))
6+
@interface NSObject
7+
+ (instancetype) alloc;
8+
- (instancetype) init;
9+
- (instancetype)retain;
10+
- (void)release;
11+
@end
12+
13+
void doSomeWork();
14+
15+
@interface SomeObjC : NSObject {
16+
CheckedObj* _unchecked1;
17+
// expected-warning@-1{{Instance variable '_unchecked1' in 'SomeObjC' is a raw pointer to CheckedPtr capable type 'CheckedObj'}}
18+
CheckedPtr<CheckedObj> _counted1;
19+
[[clang::suppress]] CheckedObj* _unchecked2;
20+
}
21+
- (void)doWork;
22+
@end
23+
24+
@implementation SomeObjC {
25+
CheckedObj* _unchecked3;
26+
// expected-warning@-1{{Instance variable '_unchecked3' in 'SomeObjC' is a raw pointer to CheckedPtr capable type 'CheckedObj'}}
27+
CheckedPtr<CheckedObj> _counted2;
28+
[[clang::suppress]] CheckedObj* _unchecked4;
29+
}
30+
31+
- (void)doWork {
32+
doSomeWork();
33+
}
34+
35+
@end
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
// RUN: %clang_analyze_cc1 -analyzer-checker=webkit.NoUncountedMemberChecker -verify %s
2+
3+
#include "mock-types.h"
4+
5+
__attribute__((objc_root_class))
6+
@interface NSObject
7+
+ (instancetype) alloc;
8+
- (instancetype) init;
9+
- (instancetype)retain;
10+
- (void)release;
11+
@end
12+
13+
void doSomeWork();
14+
15+
@interface SomeObjC : NSObject {
16+
RefCountable* _uncounted1;
17+
// expected-warning@-1{{Instance variable '_uncounted1' in 'SomeObjC' is a raw pointer to ref-countable type 'RefCountable'}}
18+
RefPtr<RefCountable> _counted1;
19+
[[clang::suppress]] RefCountable* _uncounted2;
20+
}
21+
- (void)doWork;
22+
@end
23+
24+
@implementation SomeObjC {
25+
RefCountable* _uncounted3;
26+
// expected-warning@-1{{Instance variable '_uncounted3' in 'SomeObjC' is a raw pointer to ref-countable type 'RefCountable'}}
27+
RefPtr<RefCountable> _counted2;
28+
[[clang::suppress]] RefCountable* _uncounted4;
29+
}
30+
31+
- (void)doWork {
32+
doSomeWork();
33+
}
34+
35+
@end

0 commit comments

Comments
 (0)