[clang] [WebKit checkers] Treat a weak property / variable as safe (PR #163689)
Ryosuke Niwa via cfe-commits
cfe-commits at lists.llvm.org
Wed Oct 15 20:14:25 PDT 2025
https://github.com/rniwa created https://github.com/llvm/llvm-project/pull/163689
Treat a weak Objective-C property, ivar, member variable, and local variable as safe.
>From e2a5d2866304d4a43ae343fa784bbfa15f7ab053 Mon Sep 17 00:00:00 2001
From: Ryosuke Niwa <rniwa at webkit.org>
Date: Wed, 15 Oct 2025 20:12:46 -0700
Subject: [PATCH] [WebKit checkers] Treat a weak property / variable as safe
Treat a weak Objective-C property, ivar, member variable, and local variable as safe.
---
.../WebKit/RawPtrRefLambdaCapturesChecker.cpp | 2 ++
.../WebKit/RawPtrRefLocalVarsChecker.cpp | 2 ++
.../WebKit/RawPtrRefMemberChecker.cpp | 6 +++-
.../unretained-lambda-captures-weak-arc.mm | 22 +++++++++++++
.../WebKit/unretained-lambda-captures-weak.mm | 22 +++++++++++++
.../WebKit/unretained-local-vars-weak-arc.mm | 13 ++++++++
.../WebKit/unretained-local-vars-weak.mm | 13 ++++++++
.../WebKit/unretained-members-weak-arc.mm | 29 +++++++++++++++++
.../WebKit/unretained-members-weak.mm | 31 +++++++++++++++++++
9 files changed, 139 insertions(+), 1 deletion(-)
create mode 100644 clang/test/Analysis/Checkers/WebKit/unretained-lambda-captures-weak-arc.mm
create mode 100644 clang/test/Analysis/Checkers/WebKit/unretained-lambda-captures-weak.mm
create mode 100644 clang/test/Analysis/Checkers/WebKit/unretained-local-vars-weak-arc.mm
create mode 100644 clang/test/Analysis/Checkers/WebKit/unretained-local-vars-weak.mm
create mode 100644 clang/test/Analysis/Checkers/WebKit/unretained-members-weak-arc.mm
create mode 100644 clang/test/Analysis/Checkers/WebKit/unretained-members-weak.mm
diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/RawPtrRefLambdaCapturesChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/RawPtrRefLambdaCapturesChecker.cpp
index 033eb8cc299b0..9e5742e442af9 100644
--- a/clang/lib/StaticAnalyzer/Checkers/WebKit/RawPtrRefLambdaCapturesChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/RawPtrRefLambdaCapturesChecker.cpp
@@ -496,6 +496,8 @@ class UnretainedLambdaCapturesChecker : public RawPtrRefLambdaCapturesChecker {
}
std::optional<bool> isUnsafePtr(QualType QT) const final {
+ if (QT.hasStrongOrWeakObjCLifetime())
+ return false;
return RTC->isUnretained(QT);
}
diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/RawPtrRefLocalVarsChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/RawPtrRefLocalVarsChecker.cpp
index c13df47920f72..f2235e7c25ab2 100644
--- a/clang/lib/StaticAnalyzer/Checkers/WebKit/RawPtrRefLocalVarsChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/RawPtrRefLocalVarsChecker.cpp
@@ -433,6 +433,8 @@ class UnretainedLocalVarsChecker final : public RawPtrRefLocalVarsChecker {
RTC = RetainTypeChecker();
}
std::optional<bool> isUnsafePtr(const QualType T) const final {
+ if (T.hasStrongOrWeakObjCLifetime())
+ return false;
return RTC->isUnretained(T);
}
bool isSafePtr(const CXXRecordDecl *Record) const final {
diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/RawPtrRefMemberChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/RawPtrRefMemberChecker.cpp
index ace639ce7ab18..e4cbd290127bb 100644
--- a/clang/lib/StaticAnalyzer/Checkers/WebKit/RawPtrRefMemberChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/RawPtrRefMemberChecker.cpp
@@ -231,8 +231,10 @@ class RawPtrRefMemberChecker
// "assign" property doesn't retain even under ARC so treat it as unsafe.
bool ignoreARC =
!PD->isReadOnly() && PD->getSetterKind() == ObjCPropertyDecl::Assign;
+ bool IsWeak = PD->getPropertyAttributes() & ObjCPropertyAttribute::kind_weak;
+ bool HasSafeAttr = PD->isRetaining() || IsWeak;
auto IsUnsafePtr = isUnsafePtr(QT, ignoreARC);
- return {IsUnsafePtr && *IsUnsafePtr && !PD->isRetaining(), PropType};
+ return {IsUnsafePtr && *IsUnsafePtr && !HasSafeAttr, PropType};
}
bool shouldSkipDecl(const RecordDecl *RD) const {
@@ -363,6 +365,8 @@ class NoUnretainedMemberChecker final : public RawPtrRefMemberChecker {
}
std::optional<bool> isUnsafePtr(QualType QT, bool ignoreARC) const final {
+ if (QT.hasStrongOrWeakObjCLifetime())
+ return false;
return RTC->isUnretained(QT, ignoreARC);
}
diff --git a/clang/test/Analysis/Checkers/WebKit/unretained-lambda-captures-weak-arc.mm b/clang/test/Analysis/Checkers/WebKit/unretained-lambda-captures-weak-arc.mm
new file mode 100644
index 0000000000000..a52bc7c9a5572
--- /dev/null
+++ b/clang/test/Analysis/Checkers/WebKit/unretained-lambda-captures-weak-arc.mm
@@ -0,0 +1,22 @@
+// RUN: %clang_analyze_cc1 -analyzer-checker=alpha.webkit.UnretainedLambdaCapturesChecker -fobjc-runtime-has-weak -fobjc-weak -fobjc-arc -verify %s
+// expected-no-diagnostics
+
+#include "objc-mock-types.h"
+
+void someFunction();
+template <typename Callback> void call(Callback callback) {
+ someFunction();
+ callback();
+}
+
+NSString *provideStr();
+SomeObj *provideSomeObj();
+
+void foo() {
+ __weak NSString *weakStr = provideStr();
+ __weak SomeObj *weakObj = provideSomeObj();
+ auto lambda = [weakStr, weakObj]() {
+ return [weakStr length] + [weakObj value];
+ };
+ call(lambda);
+}
diff --git a/clang/test/Analysis/Checkers/WebKit/unretained-lambda-captures-weak.mm b/clang/test/Analysis/Checkers/WebKit/unretained-lambda-captures-weak.mm
new file mode 100644
index 0000000000000..7439d7f8bb93b
--- /dev/null
+++ b/clang/test/Analysis/Checkers/WebKit/unretained-lambda-captures-weak.mm
@@ -0,0 +1,22 @@
+// RUN: %clang_analyze_cc1 -analyzer-checker=alpha.webkit.UnretainedLambdaCapturesChecker -fobjc-runtime-has-weak -fobjc-weak -verify %s
+// expected-no-diagnostics
+
+#include "objc-mock-types.h"
+
+void someFunction();
+template <typename Callback> void call(Callback callback) {
+ someFunction();
+ callback();
+}
+
+NSString *provideStr();
+SomeObj *provideSomeObj();
+
+void foo() {
+ __weak NSString *weakStr = provideStr();
+ __weak SomeObj *weakObj = provideSomeObj();
+ auto lambda = [weakStr, weakObj]() {
+ return [weakStr length] + [weakObj value];
+ };
+ call(lambda);
+}
diff --git a/clang/test/Analysis/Checkers/WebKit/unretained-local-vars-weak-arc.mm b/clang/test/Analysis/Checkers/WebKit/unretained-local-vars-weak-arc.mm
new file mode 100644
index 0000000000000..8c709b5921227
--- /dev/null
+++ b/clang/test/Analysis/Checkers/WebKit/unretained-local-vars-weak-arc.mm
@@ -0,0 +1,13 @@
+// RUN: %clang_analyze_cc1 -analyzer-checker=alpha.webkit.UnretainedLocalVarsChecker -fobjc-runtime-has-weak -fobjc-weak -fobjc-arc -verify %s
+// expected-no-diagnostics
+
+#include "objc-mock-types.h"
+
+NSString *provideStr();
+SomeObj *provideSomeObj();
+
+int foo() {
+ __weak NSString *weakStr = provideStr();
+ __weak SomeObj *weakObj = provideSomeObj();
+ return [weakStr length] + [weakObj value];
+}
diff --git a/clang/test/Analysis/Checkers/WebKit/unretained-local-vars-weak.mm b/clang/test/Analysis/Checkers/WebKit/unretained-local-vars-weak.mm
new file mode 100644
index 0000000000000..3ac4ff9d1e4cb
--- /dev/null
+++ b/clang/test/Analysis/Checkers/WebKit/unretained-local-vars-weak.mm
@@ -0,0 +1,13 @@
+// RUN: %clang_analyze_cc1 -analyzer-checker=alpha.webkit.UnretainedLocalVarsChecker -fobjc-runtime-has-weak -fobjc-weak -verify %s
+// expected-no-diagnostics
+
+#include "objc-mock-types.h"
+
+NSString *provideStr();
+SomeObj *provideSomeObj();
+
+int foo() {
+ __weak NSString *weakStr = provideStr();
+ __weak SomeObj *weakObj = provideSomeObj();
+ return [weakStr length] + [weakObj value];
+}
diff --git a/clang/test/Analysis/Checkers/WebKit/unretained-members-weak-arc.mm b/clang/test/Analysis/Checkers/WebKit/unretained-members-weak-arc.mm
new file mode 100644
index 0000000000000..c0aaac09e68d8
--- /dev/null
+++ b/clang/test/Analysis/Checkers/WebKit/unretained-members-weak-arc.mm
@@ -0,0 +1,29 @@
+// RUN: %clang_analyze_cc1 -analyzer-checker=alpha.webkit.NoUnretainedMemberChecker -fobjc-runtime-has-weak -fobjc-weak -fobjc-arc -verify %s
+// expected-no-diagnostics
+
+#include "objc-mock-types.h"
+
+struct Foo {
+ __weak NSString *weakPtr = nullptr;
+ Foo();
+ ~Foo();
+ void bar();
+};
+
+ at interface ObjectWithWeakProperty : NSObject
+ at property(nonatomic, weak) NSString *weak_prop;
+ at end
+
+ at implementation ObjectWithWeakProperty
+ at end
+
+NS_REQUIRES_PROPERTY_DEFINITIONS
+ at interface NoSynthesisObjectWithWeakProperty : NSObject
+ at property(nonatomic, readonly, weak) NSString *weak_prop;
+ at end
+
+ at implementation NoSynthesisObjectWithWeakProperty
+- (NSString *)weak_prop {
+ return nil;
+}
+ at end
diff --git a/clang/test/Analysis/Checkers/WebKit/unretained-members-weak.mm b/clang/test/Analysis/Checkers/WebKit/unretained-members-weak.mm
new file mode 100644
index 0000000000000..422cf6189446d
--- /dev/null
+++ b/clang/test/Analysis/Checkers/WebKit/unretained-members-weak.mm
@@ -0,0 +1,31 @@
+// RUN: %clang_analyze_cc1 -analyzer-checker=alpha.webkit.NoUnretainedMemberChecker -fobjc-runtime-has-weak -fobjc-weak -verify %s
+// expected-no-diagnostics
+
+#include "objc-mock-types.h"
+
+struct Foo {
+ __weak NSString *weakPtr = nullptr;
+ Foo();
+ ~Foo();
+ void bar();
+};
+
+ at interface ObjectWithWeakProperty : NSObject
+ at property(nonatomic, weak) NSString *weak_prop;
+ at end
+
+ at implementation ObjectWithWeakProperty
+ at end
+
+NS_REQUIRES_PROPERTY_DEFINITIONS
+ at interface NoSynthesisObjectWithWeakProperty : NSObject
+ at property(nonatomic, readonly, weak) NSString *weak_prop;
+ at end
+
+ at implementation NoSynthesisObjectWithWeakProperty {
+ __weak NSNumber *weak_ivar;
+}
+- (NSString *)weak_prop {
+ return nil;
+}
+ at end
More information about the cfe-commits
mailing list