Author: Ryosuke Niwa
Date: 2025-04-04T11:25:24-07:00
New Revision: bf1d27889b5831ed3e072a9223bdac098bf71af3

URL: 
https://github.com/llvm/llvm-project/commit/bf1d27889b5831ed3e072a9223bdac098bf71af3
DIFF: 
https://github.com/llvm/llvm-project/commit/bf1d27889b5831ed3e072a9223bdac098bf71af3.diff

LOG: [WebKit checkers] Treat Objective-C message send return value as safe 
(#133605)

Objective-C selectors are supposed to return autoreleased object. Treat
these return values as safe.

Added: 
    

Modified: 
    clang/lib/StaticAnalyzer/Checkers/WebKit/RawPtrRefCallArgsChecker.cpp
    clang/lib/StaticAnalyzer/Checkers/WebKit/RawPtrRefLocalVarsChecker.cpp
    clang/test/Analysis/Checkers/WebKit/objc-mock-types.h
    clang/test/Analysis/Checkers/WebKit/unretained-call-args-arc.mm
    clang/test/Analysis/Checkers/WebKit/unretained-call-args.mm
    clang/test/Analysis/Checkers/WebKit/unretained-local-vars.mm

Removed: 
    


################################################################################
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@-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@-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);
 }
-@end
\ No newline at end of file
+
+- (SomeObj*)getSomeObj {
+  return RetainPtr<SomeObj *>(provide()).autorelease();
+}
+
+- (void)storeSomeObj {
+  auto *obj = [self getSomeObj];
+  [obj doWork];
+}
+@end


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

Reply via email to