[clang] [WebKit checkers] Treat Objective-C message send return value as safe (PR #133605)
Ryosuke Niwa via cfe-commits
cfe-commits at lists.llvm.org
Sat Mar 29 16:41:11 PDT 2025
https://github.com/rniwa created https://github.com/llvm/llvm-project/pull/133605
Objective-C selectors are supposed to return autoreleased object. Treat these return values as safe.
>From 02340f3d1f51ae69598a8725403d4835f29f17cd Mon Sep 17 00:00:00 2001
From: Ryosuke Niwa <rniwa at webkit.org>
Date: Sat, 29 Mar 2025 16:21:45 -0700
Subject: [PATCH] [WebKit checkers] Treat Objective-C message send return value
as safe
Objective-C selectors are supposed to return autoreleased object.
Treat these return values as safe.
---
.../Checkers/WebKit/RawPtrRefCallArgsChecker.cpp | 8 ++++++++
.../Checkers/WebKit/RawPtrRefLocalVarsChecker.cpp | 8 ++++++++
.../Analysis/Checkers/WebKit/objc-mock-types.h | 5 +++++
.../Checkers/WebKit/unretained-call-args-arc.mm | 9 +++++++++
.../Checkers/WebKit/unretained-call-args.mm | 9 +++++++++
.../Checkers/WebKit/unretained-local-vars.mm | 15 +++++++++++++--
6 files changed, 52 insertions(+), 2 deletions(-)
diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/RawPtrRefCallArgsChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/RawPtrRefCallArgsChecker.cpp
index ce8f0df697b06..13088920cfa19 100644
--- a/clang/lib/StaticAnalyzer/Checkers/WebKit/RawPtrRefCallArgsChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/RawPtrRefCallArgsChecker.cpp
@@ -47,6 +47,7 @@ class RawPtrRefCallArgsChecker
virtual std::optional<bool> isUnsafePtr(QualType) const = 0;
virtual bool isSafePtr(const CXXRecordDecl *Record) const = 0;
virtual bool isSafePtrType(const QualType type) const = 0;
+ virtual bool isSafeExpr(const Expr *) const { return false; }
virtual const char *ptrKind() const = 0;
void checkASTDecl(const TranslationUnitDecl *TUD, AnalysisManager &MGR,
@@ -233,6 +234,8 @@ class RawPtrRefCallArgsChecker
return true;
if (EFA.isACallToEnsureFn(ArgOrigin))
return true;
+ if (isSafeExpr(ArgOrigin))
+ return true;
return false;
});
}
@@ -469,6 +472,11 @@ class UnretainedCallArgsChecker final : public RawPtrRefCallArgsChecker {
return isRetainPtrType(type);
}
+ bool isSafeExpr(const Expr *E) const final {
+ return ento::cocoa::isCocoaObjectRef(E->getType()) &&
+ isa<ObjCMessageExpr>(E);
+ }
+
const char *ptrKind() const final { return "unretained"; }
};
diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/RawPtrRefLocalVarsChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/RawPtrRefLocalVarsChecker.cpp
index d413e33a490c5..9975d1a91b681 100644
--- a/clang/lib/StaticAnalyzer/Checkers/WebKit/RawPtrRefLocalVarsChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/RawPtrRefLocalVarsChecker.cpp
@@ -179,6 +179,7 @@ class RawPtrRefLocalVarsChecker
virtual std::optional<bool> isUnsafePtr(const QualType T) const = 0;
virtual bool isSafePtr(const CXXRecordDecl *) const = 0;
virtual bool isSafePtrType(const QualType) const = 0;
+ virtual bool isSafeExpr(const Expr *) const { return false; }
virtual const char *ptrKind() const = 0;
void checkASTDecl(const TranslationUnitDecl *TUD, AnalysisManager &MGR,
@@ -300,6 +301,9 @@ class RawPtrRefLocalVarsChecker
if (EFA.isACallToEnsureFn(InitArgOrigin))
return true;
+ if (isSafeExpr(InitArgOrigin))
+ return true;
+
if (auto *Ref = llvm::dyn_cast<DeclRefExpr>(InitArgOrigin)) {
if (auto *MaybeGuardian =
dyn_cast_or_null<VarDecl>(Ref->getFoundDecl())) {
@@ -426,6 +430,10 @@ class UnretainedLocalVarsChecker final : public RawPtrRefLocalVarsChecker {
bool isSafePtrType(const QualType type) const final {
return isRetainPtrType(type);
}
+ bool isSafeExpr(const Expr *E) const final {
+ return ento::cocoa::isCocoaObjectRef(E->getType()) &&
+ isa<ObjCMessageExpr>(E);
+ }
const char *ptrKind() const final { return "unretained"; }
};
diff --git a/clang/test/Analysis/Checkers/WebKit/objc-mock-types.h b/clang/test/Analysis/Checkers/WebKit/objc-mock-types.h
index ef46a7c0a2925..059a203e3b0d1 100644
--- a/clang/test/Analysis/Checkers/WebKit/objc-mock-types.h
+++ b/clang/test/Analysis/Checkers/WebKit/objc-mock-types.h
@@ -71,6 +71,7 @@ __attribute__((objc_root_class))
+ (Class) superclass;
- (instancetype) init;
- (instancetype)retain;
+- (instancetype)autorelease;
- (void)release;
- (BOOL)isKindOfClass:(Class)aClass;
@end
@@ -221,6 +222,10 @@ template <typename T> struct RetainPtr {
operator PtrType() const { return t; }
operator bool() const { return t; }
+#if !__has_feature(objc_arc)
+ PtrType autorelease() { [[clang::suppress]] return [t autorelease]; }
+#endif
+
private:
CFTypeRef toCFTypeRef(id ptr) { return (__bridge CFTypeRef)ptr; }
CFTypeRef toCFTypeRef(const void* ptr) { return (CFTypeRef)ptr; }
diff --git a/clang/test/Analysis/Checkers/WebKit/unretained-call-args-arc.mm b/clang/test/Analysis/Checkers/WebKit/unretained-call-args-arc.mm
index eb4735da60a05..f1f4d912663aa 100644
--- a/clang/test/Analysis/Checkers/WebKit/unretained-call-args-arc.mm
+++ b/clang/test/Analysis/Checkers/WebKit/unretained-call-args-arc.mm
@@ -18,6 +18,7 @@ void foo() {
@interface AnotherObj : NSObject
- (void)foo:(SomeObj *)obj;
+- (SomeObj *)getSomeObj;
@end
@implementation AnotherObj
@@ -27,4 +28,12 @@ - (void)foo:(SomeObj*)obj {
CFArrayAppendValue(provide_cf(), nullptr);
// expected-warning at -1{{Call argument for parameter 'theArray' is unretained and unsafe [alpha.webkit.UnretainedCallArgsChecker]}}
}
+
+- (SomeObj *)getSomeObj {
+ return provide();
+}
+
+- (void)doWorkOnSomeObj {
+ [[self getSomeObj] doWork];
+}
@end
diff --git a/clang/test/Analysis/Checkers/WebKit/unretained-call-args.mm b/clang/test/Analysis/Checkers/WebKit/unretained-call-args.mm
index 55e795ee9a598..dd21864300387 100644
--- a/clang/test/Analysis/Checkers/WebKit/unretained-call-args.mm
+++ b/clang/test/Analysis/Checkers/WebKit/unretained-call-args.mm
@@ -405,6 +405,7 @@ void idcf(CFTypeRef obj) {
@interface TestObject : NSObject
- (void)doWork:(NSString *)msg, ...;
- (void)doWorkOnSelf;
+- (SomeObj *)getSomeObj;
@end
@implementation TestObject
@@ -421,4 +422,12 @@ - (void)doWorkOnSelf {
[self doWork:@"hello", RetainPtr<SomeObj> { provide() }.get(), RetainPtr<CFMutableArrayRef> { provide_cf() }.get()];
}
+- (SomeObj *)getSomeObj {
+ return RetainPtr<SomeObj *>(provide()).autorelease();
+}
+
+- (void)doWorkOnSomeObj {
+ [[self getSomeObj] doWork];
+}
+
@end
diff --git a/clang/test/Analysis/Checkers/WebKit/unretained-local-vars.mm b/clang/test/Analysis/Checkers/WebKit/unretained-local-vars.mm
index 0a3d9e54fa024..a71a80ea3d647 100644
--- a/clang/test/Analysis/Checkers/WebKit/unretained-local-vars.mm
+++ b/clang/test/Analysis/Checkers/WebKit/unretained-local-vars.mm
@@ -14,8 +14,9 @@ void bar(SomeObj *) {}
} // namespace raw_ptr
namespace pointer {
+SomeObj *provide();
void foo_ref() {
- SomeObj *bar = [[SomeObj alloc] init];
+ SomeObj *bar = provide();
// expected-warning at -1{{Local variable 'bar' is unretained and unsafe [alpha.webkit.UnretainedLocalVarsChecker]}}
[bar doWork];
}
@@ -387,6 +388,7 @@ unsigned ccf(CFTypeRef obj) {
} // ptr_conversion
bool doMoreWorkOpaque(OtherObj*);
+SomeObj* provide();
@implementation OtherObj
- (instancetype)init {
@@ -397,4 +399,13 @@ - (instancetype)init {
- (void)doMoreWork:(OtherObj *)other {
doMoreWorkOpaque(other);
}
- at end
\ No newline at end of file
+
+- (SomeObj*)getSomeObj {
+ return RetainPtr<SomeObj *>(provide()).autorelease();
+}
+
+- (void)storeSomeObj {
+ auto *obj = [self getSomeObj];
+ [obj doWork];
+}
+ at end
More information about the cfe-commits
mailing list