https://github.com/rniwa updated 
https://github.com/llvm/llvm-project/pull/141293

>From bcee06004f24f8488bcc8e84170bf3509daa5fd9 Mon Sep 17 00:00:00 2001
From: Ryosuke Niwa <rn...@webkit.org>
Date: Fri, 23 May 2025 13:53:36 -0700
Subject: [PATCH 1/3] [alpha.webkit.NoUnretainedMemberChecker] Recognize
 NS_REQUIRES_PROPERTY_DEFINITIONS

Allow @property of a raw pointer when NS_REQUIRES_PROPERTY_DEFINITIONS is 
specified on the interface
since such an interface does not automatically synthesize raw pointer ivars.

Also emit a warning for @property(assign) and @property(unsafe_unretained) 
under ARC.
---
 .../Checkers/WebKit/PtrTypesSemantics.cpp     |  1 +
 .../Checkers/WebKit/PtrTypesSemantics.h       |  2 ++
 .../WebKit/RawPtrRefMemberChecker.cpp         | 21 +++++++++----
 .../Checkers/WebKit/objc-mock-types.h         |  1 +
 .../Checkers/WebKit/unretained-members-arc.mm | 30 +++++++++++++++++++
 .../Checkers/WebKit/unretained-members.mm     | 19 ++++++++++++
 6 files changed, 68 insertions(+), 6 deletions(-)

diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp 
b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
index 4ddd11495f534..f547f86f4782f 100644
--- a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
@@ -236,6 +236,7 @@ std::optional<bool> isUnchecked(const QualType T) {
 void RetainTypeChecker::visitTranslationUnitDecl(
     const TranslationUnitDecl *TUD) {
   IsARCEnabled = TUD->getLangOpts().ObjCAutoRefCount;
+  DefaultSynthProperties = TUD->getLangOpts().ObjCDefaultSynthProperties;
 }
 
 void RetainTypeChecker::visitTypedef(const TypedefDecl *TD) {
diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.h 
b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.h
index f9fcfe9878d54..3c9560cb8059b 100644
--- a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.h
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.h
@@ -76,12 +76,14 @@ class RetainTypeChecker {
   llvm::DenseSet<const RecordType *> CFPointees;
   llvm::DenseSet<const Type *> RecordlessTypes;
   bool IsARCEnabled{false};
+  bool DefaultSynthProperties{true};
 
 public:
   void visitTranslationUnitDecl(const TranslationUnitDecl *);
   void visitTypedef(const TypedefDecl *);
   bool isUnretained(const QualType, bool ignoreARC = false);
   bool isARCEnabled() const { return IsARCEnabled; }
+  bool defaultSynthProperties() const { return DefaultSynthProperties; }
 };
 
 /// \returns true if \p Class is NS or CF objects AND not retained, false if
diff --git 
a/clang/lib/StaticAnalyzer/Checkers/WebKit/RawPtrRefMemberChecker.cpp 
b/clang/lib/StaticAnalyzer/Checkers/WebKit/RawPtrRefMemberChecker.cpp
index 4ea6158c4c410..aa707823beb5b 100644
--- a/clang/lib/StaticAnalyzer/Checkers/WebKit/RawPtrRefMemberChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/RawPtrRefMemberChecker.cpp
@@ -38,7 +38,8 @@ class RawPtrRefMemberChecker
   RawPtrRefMemberChecker(const char *description)
       : Bug(this, description, "WebKit coding guidelines") {}
 
-  virtual std::optional<bool> isUnsafePtr(QualType) const = 0;
+  virtual std::optional<bool> isUnsafePtr(QualType,
+                                          bool ignoreARC = false) const = 0;
   virtual const char *typeName() const = 0;
   virtual const char *invariant() const = 0;
 
@@ -174,7 +175,15 @@ class RawPtrRefMemberChecker
     if (!PropType)
       return;
 
-    auto IsUnsafePtr = isUnsafePtr(QT);
+    if (const ObjCInterfaceDecl *ID = dyn_cast<ObjCInterfaceDecl>(CD)) {
+      if (!RTC || !RTC->defaultSynthProperties() ||
+          ID->isObjCRequiresPropertyDefs())
+        return;
+    }
+
+    bool ignoreARC =
+        !PD->isReadOnly() && PD->getSetterKind() == ObjCPropertyDecl::Assign;
+    auto IsUnsafePtr = isUnsafePtr(QT, ignoreARC);
     if (!IsUnsafePtr || !*IsUnsafePtr)
       return;
 
@@ -274,7 +283,7 @@ class NoUncountedMemberChecker final : public 
RawPtrRefMemberChecker {
       : RawPtrRefMemberChecker("Member variable is a raw-pointer/reference to "
                                "reference-countable type") {}
 
-  std::optional<bool> isUnsafePtr(QualType QT) const final {
+  std::optional<bool> isUnsafePtr(QualType QT, bool) const final {
     return isUncountedPtr(QT.getCanonicalType());
   }
 
@@ -291,7 +300,7 @@ class NoUncheckedPtrMemberChecker final : public 
RawPtrRefMemberChecker {
       : RawPtrRefMemberChecker("Member variable is a raw-pointer/reference to "
                                "checked-pointer capable type") {}
 
-  std::optional<bool> isUnsafePtr(QualType QT) const final {
+  std::optional<bool> isUnsafePtr(QualType QT, bool) const final {
     return isUncheckedPtr(QT.getCanonicalType());
   }
 
@@ -311,8 +320,8 @@ class NoUnretainedMemberChecker final : public 
RawPtrRefMemberChecker {
     RTC = RetainTypeChecker();
   }
 
-  std::optional<bool> isUnsafePtr(QualType QT) const final {
-    return RTC->isUnretained(QT);
+  std::optional<bool> isUnsafePtr(QualType QT, bool ignoreARC) const final {
+    return RTC->isUnretained(QT, ignoreARC);
   }
 
   const char *typeName() const final { return "retainable type"; }
diff --git a/clang/test/Analysis/Checkers/WebKit/objc-mock-types.h 
b/clang/test/Analysis/Checkers/WebKit/objc-mock-types.h
index 93e7dfd77b9e9..9e4356a71f1b5 100644
--- a/clang/test/Analysis/Checkers/WebKit/objc-mock-types.h
+++ b/clang/test/Analysis/Checkers/WebKit/objc-mock-types.h
@@ -22,6 +22,7 @@ typedef struct CF_BRIDGED_TYPE(id) CGImage *CGImageRef;
 #define NS_RETURNS_RETAINED __attribute__((ns_returns_retained))
 #define CF_CONSUMED __attribute__((cf_consumed))
 #define CF_RETURNS_RETAINED __attribute__((cf_returns_retained))
+#define NS_REQUIRES_PROPERTY_DEFINITIONS 
__attribute__((objc_requires_property_definitions))
 
 extern const CFAllocatorRef kCFAllocatorDefault;
 typedef struct _NSZone NSZone;
diff --git a/clang/test/Analysis/Checkers/WebKit/unretained-members-arc.mm 
b/clang/test/Analysis/Checkers/WebKit/unretained-members-arc.mm
index 3491bc93ed98a..54d661639da60 100644
--- a/clang/test/Analysis/Checkers/WebKit/unretained-members-arc.mm
+++ b/clang/test/Analysis/Checkers/WebKit/unretained-members-arc.mm
@@ -64,3 +64,33 @@ void forceTmplToInstantiate(FooTmpl<SomeObj, 
CFMutableArrayRef>) {}
   };
 
 } // namespace ptr_to_ptr_to_retained
+
+@interface AnotherObject : NSObject {
+  NSString *ns_string;
+  CFStringRef cf_string;
+  // expected-warning@-1{{Instance variable 'cf_string' in 'AnotherObject' is 
a retainable type 'CFStringRef'; member variables must be a RetainPtr}}
+}
+@property(nonatomic, strong) NSString *prop_string1;
+@property(nonatomic, assign) NSString *prop_string2;
+// expected-warning@-1{{Property 'prop_string2' in 'AnotherObject' is a raw 
pointer to retainable type 'NSString'; member variables must be a RetainPtr}}
+@property(nonatomic, unsafe_unretained) NSString *prop_string3;
+// expected-warning@-1{{Property 'prop_string3' in 'AnotherObject' is a raw 
pointer to retainable type 'NSString'; member variables must be a RetainPtr}}
+@property(nonatomic, readonly) NSString *prop_string4;
+@end
+
+NS_REQUIRES_PROPERTY_DEFINITIONS
+@interface NoSynthObject : NSObject {
+  NSString *ns_string;
+  CFStringRef cf_string;
+  // expected-warning@-1{{Instance variable 'cf_string' in 'NoSynthObject' is 
a retainable type 'CFStringRef'; member variables must be a RetainPtr}}
+}
+@property(nonatomic, readonly, strong) NSString *prop_string1;
+@property(nonatomic, readonly, strong) NSString *prop_string2;
+@end
+
+@implementation NoSynthObject
+- (NSString *)prop_string1 {
+  return nil;
+}
+@synthesize prop_string2;
+@end
diff --git a/clang/test/Analysis/Checkers/WebKit/unretained-members.mm 
b/clang/test/Analysis/Checkers/WebKit/unretained-members.mm
index 0cb4c4ac0f6a0..7b31296378034 100644
--- a/clang/test/Analysis/Checkers/WebKit/unretained-members.mm
+++ b/clang/test/Analysis/Checkers/WebKit/unretained-members.mm
@@ -99,3 +99,22 @@ @interface AnotherObject : NSObject {
 @property(nonatomic, strong) NSString *prop_string;
 // expected-warning@-1{{Property 'prop_string' in 'AnotherObject' is a raw 
pointer to retainable type 'NSString'; member variables must be a RetainPtr}}
 @end
+
+NS_REQUIRES_PROPERTY_DEFINITIONS
+@interface NoSynthObject : NSObject {
+  NSString *ns_string;
+  // expected-warning@-1{{Instance variable 'ns_string' in 'NoSynthObject' is 
a raw pointer to retainable type 'NSString'; member variables must be a 
RetainPtr}}
+  CFStringRef cf_string;
+  // expected-warning@-1{{Instance variable 'cf_string' in 'NoSynthObject' is 
a retainable type 'CFStringRef'; member variables must be a RetainPtr}}
+}
+@property(nonatomic, readonly, strong) NSString *prop_string1;
+@property(nonatomic, readonly, strong) NSString *prop_string2;
+@end
+
+@implementation NoSynthObject
+- (NSString *)prop_string1 {
+  return nil;
+}
+@synthesize prop_string2;
+// expected-warning@-1{{Instance variable 'prop_string2' in 'NoSynthObject' is 
a raw pointer to retainable type 'NSString'}}
+@end

>From c768954f7bfa3df3b02b4411f9c828198fb0516e Mon Sep 17 00:00:00 2001
From: Ryosuke Niwa <rn...@webkit.org>
Date: Fri, 6 Jun 2025 13:16:23 -0600
Subject: [PATCH 2/3] Emit a warning for @synthesize when synthesizing an
 unsafe raw pointer.

Added test cases for synthesizing `assign` and `unsafe_unretained` properties.
---
 .../WebKit/RawPtrRefMemberChecker.cpp         | 59 ++++++++++++++++---
 .../Checkers/WebKit/unretained-members-arc.mm |  6 ++
 .../Checkers/WebKit/unretained-members.mm     |  6 ++
 3 files changed, 63 insertions(+), 8 deletions(-)

diff --git 
a/clang/lib/StaticAnalyzer/Checkers/WebKit/RawPtrRefMemberChecker.cpp 
b/clang/lib/StaticAnalyzer/Checkers/WebKit/RawPtrRefMemberChecker.cpp
index aa707823beb5b..8a31ab9a23c61 100644
--- a/clang/lib/StaticAnalyzer/Checkers/WebKit/RawPtrRefMemberChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/RawPtrRefMemberChecker.cpp
@@ -30,6 +30,7 @@ class RawPtrRefMemberChecker
 private:
   BugType Bug;
   mutable BugReporter *BR;
+  mutable llvm::DenseSet<const ObjCIvarDecl *> IvarDeclsToIgnore;
 
 protected:
   mutable std::optional<RetainTypeChecker> RTC;
@@ -143,6 +144,8 @@ class RawPtrRefMemberChecker
     if (auto *ID = dyn_cast<ObjCImplementationDecl>(CD)) {
       for (auto *Ivar : ID->ivars())
         visitIvarDecl(CD, Ivar);
+      for (auto *PropImpl : ID->property_impls())
+        visitPropImpl(CD, PropImpl);
       return;
     }
   }
@@ -151,6 +154,10 @@ class RawPtrRefMemberChecker
                      const ObjCIvarDecl *Ivar) const {
     if (BR->getSourceManager().isInSystemHeader(Ivar->getLocation()))
       return;
+
+    if (IvarDeclsToIgnore.contains(Ivar))
+      return;
+
     auto QT = Ivar->getType();
     const Type *IvarType = QT.getTypePtrOrNull();
     if (!IvarType)
@@ -160,6 +167,8 @@ class RawPtrRefMemberChecker
     if (!IsUnsafePtr || !*IsUnsafePtr)
       return;
 
+    IvarDeclsToIgnore.insert(Ivar);
+
     if (auto *MemberCXXRD = IvarType->getPointeeCXXRecordDecl())
       reportBug(Ivar, IvarType, MemberCXXRD, CD);
     else if (auto *ObjCDecl = getObjCDecl(IvarType))
@@ -170,10 +179,6 @@ class RawPtrRefMemberChecker
                              const ObjCPropertyDecl *PD) const {
     if (BR->getSourceManager().isInSystemHeader(PD->getLocation()))
       return;
-    auto QT = PD->getType();
-    const Type *PropType = QT.getTypePtrOrNull();
-    if (!PropType)
-      return;
 
     if (const ObjCInterfaceDecl *ID = dyn_cast<ObjCInterfaceDecl>(CD)) {
       if (!RTC || !RTC->defaultSynthProperties() ||
@@ -181,10 +186,8 @@ class RawPtrRefMemberChecker
         return;
     }
 
-    bool ignoreARC =
-        !PD->isReadOnly() && PD->getSetterKind() == ObjCPropertyDecl::Assign;
-    auto IsUnsafePtr = isUnsafePtr(QT, ignoreARC);
-    if (!IsUnsafePtr || !*IsUnsafePtr)
+    auto [IsUnsafe, PropType] = isPropImplUnsafePtr(PD);
+    if (!IsUnsafe)
       return;
 
     if (auto *MemberCXXRD = PropType->getPointeeCXXRecordDecl())
@@ -193,6 +196,46 @@ class RawPtrRefMemberChecker
       reportBug(PD, PropType, ObjCDecl, CD);
   }
 
+  void visitPropImpl(const ObjCContainerDecl *CD,
+                     const ObjCPropertyImplDecl* PID) const {
+   if (BR->getSourceManager().isInSystemHeader(PID->getLocation()))
+     return;
+
+   if (PID->getPropertyImplementation() != ObjCPropertyImplDecl::Synthesize)
+     return;
+
+   auto *PropDecl = PID->getPropertyDecl();
+   if (auto *IvarDecl = PID->getPropertyIvarDecl()) {
+     if (IvarDeclsToIgnore.contains(IvarDecl))
+       return;
+     IvarDeclsToIgnore.insert(IvarDecl);
+   }
+   auto [IsUnsafe, PropType] = isPropImplUnsafePtr(PropDecl);
+   if (!IsUnsafe)
+     return;
+
+   if (auto *MemberCXXRD = PropType->getPointeeCXXRecordDecl())
+     reportBug(PropDecl, PropType, MemberCXXRD, CD);
+   else if (auto *ObjCDecl = getObjCDecl(PropType))
+     reportBug(PropDecl, PropType, ObjCDecl, CD);
+  }
+
+  std::pair<bool, const Type*> isPropImplUnsafePtr(const ObjCPropertyDecl *PD) 
const {
+    if (!PD)
+      return { false, nullptr };
+
+    auto QT = PD->getType();
+    const Type *PropType = QT.getTypePtrOrNull();
+    if (!PropType)
+      return { false, nullptr };
+
+    // "assign" property doesn't retain even under ARC so treat it as unsafe.
+    bool ignoreARC =
+        !PD->isReadOnly() && PD->getSetterKind() == ObjCPropertyDecl::Assign;
+    auto IsUnsafePtr = isUnsafePtr(QT, ignoreARC);
+    return { IsUnsafePtr && *IsUnsafePtr, PropType };
+  }
+
   bool shouldSkipDecl(const RecordDecl *RD) const {
     if (!RD->isThisDeclarationADefinition())
       return true;
diff --git a/clang/test/Analysis/Checkers/WebKit/unretained-members-arc.mm 
b/clang/test/Analysis/Checkers/WebKit/unretained-members-arc.mm
index 54d661639da60..00e6e6ec1dcfa 100644
--- a/clang/test/Analysis/Checkers/WebKit/unretained-members-arc.mm
+++ b/clang/test/Analysis/Checkers/WebKit/unretained-members-arc.mm
@@ -86,6 +86,10 @@ @interface NoSynthObject : NSObject {
 }
 @property(nonatomic, readonly, strong) NSString *prop_string1;
 @property(nonatomic, readonly, strong) NSString *prop_string2;
+@property(nonatomic, assign) NSString *prop_string3;
+// expected-warning@-1{{Property 'prop_string3' in 'NoSynthObject' is a raw 
pointer to retainable type 'NSString'; member variables must be a RetainPtr}}
+@property(nonatomic, unsafe_unretained) NSString *prop_string4;
+// expected-warning@-1{{Property 'prop_string4' in 'NoSynthObject' is a raw 
pointer to retainable type 'NSString'; member variables must be a RetainPtr}}
 @end
 
 @implementation NoSynthObject
@@ -93,4 +97,6 @@ - (NSString *)prop_string1 {
   return nil;
 }
 @synthesize prop_string2;
+@synthesize prop_string3;
+@synthesize prop_string4;
 @end
diff --git a/clang/test/Analysis/Checkers/WebKit/unretained-members.mm 
b/clang/test/Analysis/Checkers/WebKit/unretained-members.mm
index 7b31296378034..d0e489654e7b8 100644
--- a/clang/test/Analysis/Checkers/WebKit/unretained-members.mm
+++ b/clang/test/Analysis/Checkers/WebKit/unretained-members.mm
@@ -109,6 +109,8 @@ @interface NoSynthObject : NSObject {
 }
 @property(nonatomic, readonly, strong) NSString *prop_string1;
 @property(nonatomic, readonly, strong) NSString *prop_string2;
+@property(nonatomic, assign) NSString *prop_string3;
+@property(nonatomic, unsafe_unretained) NSString *prop_string4;
 @end
 
 @implementation NoSynthObject
@@ -117,4 +119,8 @@ - (NSString *)prop_string1 {
 }
 @synthesize prop_string2;
 // expected-warning@-1{{Instance variable 'prop_string2' in 'NoSynthObject' is 
a raw pointer to retainable type 'NSString'}}
+@synthesize prop_string3;
+// expected-warning@-1{{Instance variable 'prop_string3' in 'NoSynthObject' is 
a raw pointer to retainable type 'NSString'; member variables must be a 
RetainPtr}}
+@synthesize prop_string4;
+// expected-warning@-1{{Instance variable 'prop_string4' in 'NoSynthObject' is 
a raw pointer to retainable type 'NSString'; member variables must be a 
RetainPtr}}
 @end

>From 469b56022db915b81449b4f0ca3121363c78de78 Mon Sep 17 00:00:00 2001
From: Ryosuke Niwa <rn...@webkit.org>
Date: Fri, 6 Jun 2025 13:40:41 -0600
Subject: [PATCH 3/3] Fix formatting

---
 .../WebKit/RawPtrRefMemberChecker.cpp         | 51 ++++++++++---------
 1 file changed, 26 insertions(+), 25 deletions(-)

diff --git 
a/clang/lib/StaticAnalyzer/Checkers/WebKit/RawPtrRefMemberChecker.cpp 
b/clang/lib/StaticAnalyzer/Checkers/WebKit/RawPtrRefMemberChecker.cpp
index 8a31ab9a23c61..ccef7570e18b2 100644
--- a/clang/lib/StaticAnalyzer/Checkers/WebKit/RawPtrRefMemberChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/RawPtrRefMemberChecker.cpp
@@ -197,43 +197,44 @@ class RawPtrRefMemberChecker
   }
 
   void visitPropImpl(const ObjCContainerDecl *CD,
-                     const ObjCPropertyImplDecl* PID) const {
-   if (BR->getSourceManager().isInSystemHeader(PID->getLocation()))
-     return;
-
-   if (PID->getPropertyImplementation() != ObjCPropertyImplDecl::Synthesize)
-     return;
-
-   auto *PropDecl = PID->getPropertyDecl();
-   if (auto *IvarDecl = PID->getPropertyIvarDecl()) {
-     if (IvarDeclsToIgnore.contains(IvarDecl))
-       return;
-     IvarDeclsToIgnore.insert(IvarDecl);
-   }
-   auto [IsUnsafe, PropType] = isPropImplUnsafePtr(PropDecl);
-   if (!IsUnsafe)
-     return;
-
-   if (auto *MemberCXXRD = PropType->getPointeeCXXRecordDecl())
-     reportBug(PropDecl, PropType, MemberCXXRD, CD);
-   else if (auto *ObjCDecl = getObjCDecl(PropType))
-     reportBug(PropDecl, PropType, ObjCDecl, CD);
+                     const ObjCPropertyImplDecl *PID) const {
+    if (BR->getSourceManager().isInSystemHeader(PID->getLocation()))
+      return;
+
+    if (PID->getPropertyImplementation() != ObjCPropertyImplDecl::Synthesize)
+      return;
+
+    auto *PropDecl = PID->getPropertyDecl();
+    if (auto *IvarDecl = PID->getPropertyIvarDecl()) {
+      if (IvarDeclsToIgnore.contains(IvarDecl))
+        return;
+      IvarDeclsToIgnore.insert(IvarDecl);
+    }
+    auto [IsUnsafe, PropType] = isPropImplUnsafePtr(PropDecl);
+    if (!IsUnsafe)
+      return;
+
+    if (auto *MemberCXXRD = PropType->getPointeeCXXRecordDecl())
+      reportBug(PropDecl, PropType, MemberCXXRD, CD);
+    else if (auto *ObjCDecl = getObjCDecl(PropType))
+      reportBug(PropDecl, PropType, ObjCDecl, CD);
   }
 
-  std::pair<bool, const Type*> isPropImplUnsafePtr(const ObjCPropertyDecl *PD) 
const {
+  std::pair<bool, const Type *>
+  isPropImplUnsafePtr(const ObjCPropertyDecl *PD) const {
     if (!PD)
-      return { false, nullptr };
+      return {false, nullptr};
 
     auto QT = PD->getType();
     const Type *PropType = QT.getTypePtrOrNull();
     if (!PropType)
-      return { false, nullptr };
+      return {false, nullptr};
 
     // "assign" property doesn't retain even under ARC so treat it as unsafe.
     bool ignoreARC =
         !PD->isReadOnly() && PD->getSetterKind() == ObjCPropertyDecl::Assign;
     auto IsUnsafePtr = isUnsafePtr(QT, ignoreARC);
-    return { IsUnsafePtr && *IsUnsafePtr, PropType };
+    return {IsUnsafePtr && *IsUnsafePtr, PropType};
   }
 
   bool shouldSkipDecl(const RecordDecl *RD) const {

_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to