llvmbot wrote:

<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-clang-static-analyzer-1

Author: Ryosuke Niwa (rniwa)

<details>
<summary>Changes</summary>

This PR adds alpha.webkit.UnretainedCallArgsChecker by generalizing 
RawPtrRefLocalVarsChecker. It checks call arguments of NS or CF types are 
backed by a RetainPtr or not. The new checker is effective for NS and CF types 
in Objective-C++ code without ARC, and it's effective for CF types in code with 
ARC.

---

Patch is 26.90 KiB, truncated to 20.00 KiB below, full version: 
https://github.com/llvm/llvm-project/pull/128586.diff


8 Files Affected:

- (modified) clang/docs/analyzer/checkers.rst (+6) 
- (modified) clang/include/clang/StaticAnalyzer/Checkers/Checkers.td (+4) 
- (modified) clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp (+13) 
- (modified) clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp 
(+4-2) 
- (modified) 
clang/lib/StaticAnalyzer/Checkers/WebKit/RawPtrRefCallArgsChecker.cpp (+110-4) 
- (modified) clang/test/Analysis/Checkers/WebKit/objc-mock-types.h (+75-14) 
- (added) clang/test/Analysis/Checkers/WebKit/unretained-call-args-arc.mm (+30) 
- (added) clang/test/Analysis/Checkers/WebKit/unretained-call-args.mm (+342) 


``````````diff
diff --git a/clang/docs/analyzer/checkers.rst b/clang/docs/analyzer/checkers.rst
index c1eedb33e74d2..c82d4114b38e2 100644
--- a/clang/docs/analyzer/checkers.rst
+++ b/clang/docs/analyzer/checkers.rst
@@ -3582,6 +3582,12 @@ The goal of this rule is to make sure that lifetime of 
any dynamically allocated
 
 The rules of when to use and not to use CheckedPtr / CheckedRef are same as 
alpha.webkit.UncountedCallArgsChecker for ref-counted objects.
 
+alpha.webkit.UnretainedCallArgsChecker
+"""""""""""""""""""""""""""""""""""""
+The goal of this rule is to make sure that lifetime of any dynamically 
allocated NS or CF objects passed as a call argument keeps its memory region 
past the end of the call. This applies to call to any function, method, lambda, 
function pointer or functor. NS or CF objects aren't supposed to be allocated 
on stack so we check arguments for parameters of raw pointers and references to 
unretained types.
+
+The rules of when to use and not to use RetainPtr are same as 
alpha.webkit.UncountedCallArgsChecker for ref-counted objects.
+
 alpha.webkit.UncountedLocalVarsChecker
 """"""""""""""""""""""""""""""""""""""
 The goal of this rule is to make sure that any uncounted local variable is 
backed by a ref-counted object with lifetime that is strictly larger than the 
scope of the uncounted local variable. To be on the safe side we require the 
scope of an uncounted variable to be embedded in the scope of ref-counted 
object that backs it.
diff --git a/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td 
b/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td
index 410f841630660..6aa006e705f80 100644
--- a/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td
+++ b/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td
@@ -1774,6 +1774,10 @@ def UncheckedCallArgsChecker : 
Checker<"UncheckedCallArgsChecker">,
   HelpText<"Check unchecked call arguments.">,
   Documentation<HasDocumentation>;
 
+def UnretainedCallArgsChecker : Checker<"UnretainedCallArgsChecker">,
+  HelpText<"Check unretained call arguments.">,
+  Documentation<HasDocumentation>;
+
 def UncountedLocalVarsChecker : Checker<"UncountedLocalVarsChecker">,
   HelpText<"Check uncounted local variables.">,
   Documentation<HasDocumentation>;
diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp 
b/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp
index dc86c4fcc64b1..8de94ca9d4d91 100644
--- a/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp
@@ -8,6 +8,7 @@
 
 #include "ASTUtils.h"
 #include "PtrTypesSemantics.h"
+#include "clang/AST/Attr.h"
 #include "clang/AST/CXXInheritance.h"
 #include "clang/AST/Decl.h"
 #include "clang/AST/DeclCXX.h"
@@ -28,6 +29,15 @@ bool tryToFindPtrOrigin(
     std::function<bool(const clang::QualType)> isSafePtrType,
     std::function<bool(const clang::Expr *, bool)> callback) {
   while (E) {
+    if (auto *DRE = dyn_cast<DeclRefExpr>(E)) {
+      auto *ValDecl = DRE->getDecl();
+      auto QT = ValDecl->getType();
+      auto ValName = ValDecl->getName();
+      if (ValDecl && (ValName.starts_with('k') || ValName.starts_with("_k")) &&
+          QT.isConstQualified()) {
+        return callback(E, true);
+      }
+    }
     if (auto *tempExpr = dyn_cast<MaterializeTemporaryExpr>(E)) {
       E = tempExpr->getSubExpr();
       continue;
@@ -117,6 +127,9 @@ bool tryToFindPtrOrigin(
           E = call->getArg(0);
           continue;
         }
+
+        if (safeGetName(callee) == "__builtin___CFStringMakeConstantString")
+          return callback(E, true);
       }
     }
     if (auto *ObjCMsgExpr = dyn_cast<ObjCMessageExpr>(E)) {
diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp 
b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
index 7899b19854806..5edc244349a75 100644
--- a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
@@ -371,7 +371,8 @@ std::optional<bool> isGetterOfSafePtr(const CXXMethodDecl 
*M) {
       if (auto *maybeRefToRawOperator = dyn_cast<CXXConversionDecl>(M)) {
         auto QT = maybeRefToRawOperator->getConversionType();
         auto *T = QT.getTypePtrOrNull();
-        return T && (T->isPointerType() || T->isReferenceType());
+        return T && (T->isPointerType() || T->isReferenceType() ||
+          T->isObjCObjectPointerType());
       }
     }
   }
@@ -414,7 +415,8 @@ bool isPtrConversion(const FunctionDecl *F) {
   if (FunctionName == "getPtr" || FunctionName == "WeakPtr" ||
       FunctionName == "dynamicDowncast" || FunctionName == "downcast" ||
       FunctionName == "checkedDowncast" ||
-      FunctionName == "uncheckedDowncast" || FunctionName == "bitwise_cast")
+      FunctionName == "uncheckedDowncast" || FunctionName == "bitwise_cast" ||
+      FunctionName == "bridge_cast")
     return true;
 
   return false;
diff --git 
a/clang/lib/StaticAnalyzer/Checkers/WebKit/RawPtrRefCallArgsChecker.cpp 
b/clang/lib/StaticAnalyzer/Checkers/WebKit/RawPtrRefCallArgsChecker.cpp
index d633fbcbd798b..c6069ffa96696 100644
--- a/clang/lib/StaticAnalyzer/Checkers/WebKit/RawPtrRefCallArgsChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/RawPtrRefCallArgsChecker.cpp
@@ -13,6 +13,7 @@
 #include "clang/AST/Decl.h"
 #include "clang/AST/DeclCXX.h"
 #include "clang/AST/DynamicRecursiveASTVisitor.h"
+#include "clang/Analysis/DomainSpecific/CocoaConventions.h"
 #include "clang/Basic/SourceLocation.h"
 #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
 #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
@@ -35,6 +36,9 @@ class RawPtrRefCallArgsChecker
   TrivialFunctionAnalysis TFA;
   EnsureFunctionAnalysis EFA;
 
+protected:
+  mutable std::optional<RetainTypeChecker> RTC;
+
 public:
   RawPtrRefCallArgsChecker(const char *description)
       : Bug(this, description, "WebKit coding guidelines") {}
@@ -80,9 +84,22 @@ class RawPtrRefCallArgsChecker
         Checker->visitCallExpr(CE, DeclWithIssue);
         return true;
       }
+
+      bool VisitTypedefDecl(TypedefDecl *TD) override {
+        if (Checker->RTC)
+          Checker->RTC->visitTypedef(TD);
+        return true;
+      }
+
+      bool VisitObjCMessageExpr(ObjCMessageExpr *ObjCMsgExpr) override {
+        Checker->visitObjCMessageExpr(ObjCMsgExpr, DeclWithIssue);
+        return true;
+      }
     };
 
     LocalVisitor visitor(this);
+    if (RTC)
+      RTC->visitTranslationUnitDecl(TUD);
     visitor.TraverseDecl(const_cast<TranslationUnitDecl *>(TUD));
   }
 
@@ -122,7 +139,7 @@ class RawPtrRefCallArgsChecker
         // if ((*P)->hasAttr<SafeRefCntblRawPtrAttr>())
         //  continue;
 
-        QualType ArgType = (*P)->getType().getCanonicalType();
+        QualType ArgType = (*P)->getType();
         // FIXME: more complex types (arrays, references to raw pointers, etc)
         std::optional<bool> IsUncounted = isUnsafePtr(ArgType);
         if (!IsUncounted || !(*IsUncounted))
@@ -141,6 +158,42 @@ class RawPtrRefCallArgsChecker
     }
   }
 
+  void visitObjCMessageExpr(const ObjCMessageExpr *E, const Decl *D) const {
+    if (BR->getSourceManager().isInSystemHeader(E->getExprLoc()))
+      return;
+
+    auto Selector = E->getSelector();
+    if (auto *Receiver = E->getInstanceReceiver()->IgnoreParenCasts()) {
+      std::optional<bool> IsUnsafe = isUnsafePtr(E->getReceiverType());
+      if (IsUnsafe && *IsUnsafe && !isPtrOriginSafe(Receiver)) {
+        if (auto *InnerMsg = dyn_cast<ObjCMessageExpr>(Receiver)) {
+          auto InnerSelector = InnerMsg->getSelector();
+          if (InnerSelector.getNameForSlot(0) == "alloc"
+            && Selector.getNameForSlot(0).starts_with("init"))
+          return;
+        }
+        reportBugOnReceiver(Receiver, D);
+      }
+    }
+
+    auto *MethodDecl = E->getMethodDecl();
+    if (!MethodDecl)
+      return;
+
+    auto ArgCount = E->getNumArgs();
+    for (unsigned i = 0; i < ArgCount && i < MethodDecl->param_size(); ++i) {
+      auto *Arg = E->getArg(i);
+      auto *Param = MethodDecl->getParamDecl(i);
+      auto ArgType = Param->getType();
+      std::optional<bool> IsUnsafe = isUnsafePtr(ArgType);
+      if (!IsUnsafe || !(*IsUnsafe))
+        continue;
+      if (isPtrOriginSafe(Arg))
+        continue;
+      reportBug(Arg, Param, D);
+    }
+  }
+
   bool isPtrOriginSafe(const Expr *Arg) const {
     return tryToFindPtrOrigin(
         Arg, /*StopAtFirstRefCountedObj=*/true,
@@ -158,6 +211,8 @@ class RawPtrRefCallArgsChecker
             // foo(NULL)
             return true;
           }
+          if (isa<ObjCStringLiteral>(ArgOrigin))
+            return true;
           if (isASafeCallArg(ArgOrigin))
             return true;
           if (EFA.isACallToEnsureFn(ArgOrigin))
@@ -212,7 +267,7 @@ class RawPtrRefCallArgsChecker
         overloadedOperatorType == OO_PipePipe)
       return true;
 
-    if (isCtorOfRefCounted(Callee))
+    if (isCtorOfSafePtr(Callee))
       return true;
 
     auto name = safeGetName(Callee);
@@ -304,6 +359,22 @@ class RawPtrRefCallArgsChecker
     Report->setDeclWithIssue(DeclWithIssue);
     BR->emitReport(std::move(Report));
   }
+
+  void reportBugOnReceiver(const Expr *CallArg, const Decl *DeclWithIssue) 
const {
+    assert(CallArg);
+
+    const SourceLocation SrcLocToReport = CallArg->getSourceRange().getBegin();
+
+    SmallString<100> Buf;
+    llvm::raw_svector_ostream Os(Buf);
+    Os << "Reciever is " << ptrKind() << " and unsafe.";
+
+    PathDiagnosticLocation BSLoc(SrcLocToReport, BR->getSourceManager());
+    auto Report = std::make_unique<BasicBugReport>(Bug, Os.str(), BSLoc);
+    Report->addRange(CallArg->getSourceRange());
+    Report->setDeclWithIssue(DeclWithIssue);
+    BR->emitReport(std::move(Report));
+  }
 };
 
 class UncountedCallArgsChecker final : public RawPtrRefCallArgsChecker {
@@ -317,7 +388,7 @@ class UncountedCallArgsChecker final : public 
RawPtrRefCallArgsChecker {
   }
 
   std::optional<bool> isUnsafePtr(QualType QT) const final {
-    return isUncountedPtr(QT);
+    return isUncountedPtr(QT.getCanonicalType());
   }
 
   bool isSafePtr(const CXXRecordDecl *Record) const final {
@@ -342,7 +413,7 @@ class UncheckedCallArgsChecker final : public 
RawPtrRefCallArgsChecker {
   }
 
   std::optional<bool> isUnsafePtr(QualType QT) const final {
-    return isUncheckedPtr(QT);
+    return isUncheckedPtr(QT.getCanonicalType());
   }
 
   bool isSafePtr(const CXXRecordDecl *Record) const final {
@@ -356,6 +427,33 @@ class UncheckedCallArgsChecker final : public 
RawPtrRefCallArgsChecker {
   const char *ptrKind() const final { return "unchecked"; }
 };
 
+class UnretainedCallArgsChecker final : public RawPtrRefCallArgsChecker {
+public:
+  UnretainedCallArgsChecker()
+      : RawPtrRefCallArgsChecker("Unretained call argument for a raw "
+                                 "pointer/reference parameter") {
+    RTC = RetainTypeChecker();
+  }
+
+  std::optional<bool> isUnsafeType(QualType QT) const final {
+    return RTC->isUnretained(QT);
+  }
+
+  std::optional<bool> isUnsafePtr(QualType QT) const final {
+    return RTC->isUnretained(QT);
+  }
+
+  bool isSafePtr(const CXXRecordDecl *Record) const final {
+    return isRetainPtr(Record);
+  }
+
+  bool isSafePtrType(const QualType type) const final {
+    return isRetainPtrType(type);
+  }
+
+  const char *ptrKind() const final { return "unretained"; }
+};
+
 } // namespace
 
 void ento::registerUncountedCallArgsChecker(CheckerManager &Mgr) {
@@ -373,3 +471,11 @@ void ento::registerUncheckedCallArgsChecker(CheckerManager 
&Mgr) {
 bool ento::shouldRegisterUncheckedCallArgsChecker(const CheckerManager &) {
   return true;
 }
+
+void ento::registerUnretainedCallArgsChecker(CheckerManager &Mgr) {
+  Mgr.registerChecker<UnretainedCallArgsChecker>();
+}
+
+bool ento::shouldRegisterUnretainedCallArgsChecker(const CheckerManager &) {
+  return true;
+}
diff --git a/clang/test/Analysis/Checkers/WebKit/objc-mock-types.h 
b/clang/test/Analysis/Checkers/WebKit/objc-mock-types.h
index 7bb33bcb6cf44..179003d563f4a 100644
--- a/clang/test/Analysis/Checkers/WebKit/objc-mock-types.h
+++ b/clang/test/Analysis/Checkers/WebKit/objc-mock-types.h
@@ -2,19 +2,24 @@
 @class NSArray;
 @class NSMutableArray;
 #define CF_BRIDGED_TYPE(T) __attribute__((objc_bridge(T)))
+#define CF_BRIDGED_MUTABLE_TYPE(T) __attribute__((objc_bridge_mutable(T)))
 typedef CF_BRIDGED_TYPE(id) void * CFTypeRef;
 typedef signed long CFIndex;
-typedef const struct __CFAllocator * CFAllocatorRef;
+typedef const struct CF_BRIDGED_TYPE(id) __CFAllocator * CFAllocatorRef;
 typedef const struct CF_BRIDGED_TYPE(NSString) __CFString * CFStringRef;
 typedef const struct CF_BRIDGED_TYPE(NSArray) __CFArray * CFArrayRef;
-typedef struct CF_BRIDGED_TYPE(NSMutableArray) __CFArray * CFMutableArrayRef;
+typedef struct CF_BRIDGED_MUTABLE_TYPE(NSMutableArray) __CFArray * 
CFMutableArrayRef;
+typedef struct CF_BRIDGED_MUTABLE_TYPE(CFRunLoopRef) __CFRunLoop * 
CFRunLoopRef;
 extern const CFAllocatorRef kCFAllocatorDefault;
 CFMutableArrayRef CFArrayCreateMutable(CFAllocatorRef allocator, CFIndex 
capacity);
 extern void CFArrayAppendValue(CFMutableArrayRef theArray, const void *value);
 CFArrayRef CFArrayCreate(CFAllocatorRef allocator, const void **values, 
CFIndex numValues);
 CFIndex CFArrayGetCount(CFArrayRef theArray);
+CFRunLoopRef CFRunLoopGetCurrent(void);
+CFRunLoopRef CFRunLoopGetMain(void);
 extern CFTypeRef CFRetain(CFTypeRef cf);
 extern void CFRelease(CFTypeRef cf);
+#define CFSTR(cStr) ((CFStringRef) __builtin___CFStringMakeConstantString ("" 
cStr ""))
 
 __attribute__((objc_root_class))
 @interface NSObject
@@ -25,6 +30,8 @@ __attribute__((objc_root_class))
 @end
 
 @interface SomeObj : NSObject
+- (SomeObj *)mutableCopy;
+- (SomeObj *)copyWithValue:(int)value;
 - (void)doWork;
 - (SomeObj *)other;
 - (SomeObj *)next;
@@ -57,28 +64,34 @@ template <typename T> struct RetainPtr {
   PtrType t;
 
   RetainPtr() : t(nullptr) { }
-
   RetainPtr(PtrType t)
     : t(t) {
     if (t)
-      CFRetain(t);
-  }
-  RetainPtr(RetainPtr&& o)
-    : RetainPtr(o.t)
-  {
-    o.t = nullptr;
+      CFRetain(toCFTypeRef(t));
   }
-  RetainPtr(const RetainPtr& o)
+  RetainPtr(RetainPtr&&);
+  RetainPtr(const RetainPtr&);
+  template <typename U>
+  RetainPtr(const RetainPtr<U>& o)
     : RetainPtr(o.t)
+  {}
+  RetainPtr operator=(const RetainPtr& o)
   {
+    if (t)
+      CFRelease(toCFTypeRef(t));
+    t = o.t;
+    if (t)
+      CFRetain(toCFTypeRef(t));
+    return *this;
   }
-  RetainPtr operator=(const RetainPtr& o)
+  template <typename U>
+  RetainPtr operator=(const RetainPtr<U>& o)
   {
     if (t)
-      CFRelease(t);
+      CFRelease(toCFTypeRef(t));
     t = o.t;
     if (t)
-      CFRetain(t);
+      CFRetain(toCFTypeRef(t));
     return *this;
   }
   ~RetainPtr() {
@@ -86,7 +99,7 @@ template <typename T> struct RetainPtr {
   }
   void clear() {
     if (t)
-      CFRelease(t);
+      CFRelease(toCFTypeRef(t));
     t = nullptr;
   }
   void swap(RetainPtr& o) {
@@ -102,10 +115,19 @@ template <typename T> struct RetainPtr {
     swap(o);
     return *this;
   }
+  PtrType leakRef()
+  {
+    PtrType s = t;
+    t = nullptr;
+    return s;
+  }
   operator PtrType() const { return t; }
   operator bool() const { return t; }
 
 private:
+  CFTypeRef toCFTypeRef(id ptr) { return (__bridge CFTypeRef)ptr; }
+  CFTypeRef toCFTypeRef(const void* ptr) { return (CFTypeRef)ptr; }
+
   template <typename U> friend RetainPtr<U> adoptNS(U*);
   template <typename U> friend RetainPtr<U> adoptCF(U);
 
@@ -113,9 +135,26 @@ template <typename T> struct RetainPtr {
   RetainPtr(PtrType t, AdoptTag) : t(t) { }
 };
 
+template <typename T>
+RetainPtr<T>::RetainPtr(RetainPtr<T>&& o)
+  : RetainPtr(o.t)
+{
+  o.t = nullptr;
+}
+
+template <typename T>
+RetainPtr<T>::RetainPtr(const RetainPtr<T>& o)
+  : RetainPtr(o.t)
+{
+}
+
 template <typename T>
 RetainPtr<T> adoptNS(T* t) {
+#if __has_feature(objc_arc)
+  return t;
+#else
   return RetainPtr<T>(t, RetainPtr<T>::Adopt);
+#endif
 }
 
 template <typename T>
@@ -123,9 +162,31 @@ RetainPtr<T> adoptCF(T t) {
   return RetainPtr<T>(t, RetainPtr<T>::Adopt);
 }
 
+template<typename T> inline RetainPtr<T> retainPtr(T ptr)
+{
+  return ptr;
+}
+
+template<typename T> inline RetainPtr<T> retainPtr(T* ptr)
+{
+  return ptr;
+}
+
+inline NSObject *bridge_cast(CFTypeRef object)
+{
+    return (__bridge NSObject *)object;
+}
+
+inline CFTypeRef bridge_cast(NSObject *object)
+{
+    return (__bridge CFTypeRef)object;
+}
+
 }
 
 using WTF::RetainPtr;
 using WTF::adoptNS;
 using WTF::adoptCF;
+using WTF::retainPtr;
 using WTF::downcast;
+using WTF::bridge_cast;
diff --git a/clang/test/Analysis/Checkers/WebKit/unretained-call-args-arc.mm 
b/clang/test/Analysis/Checkers/WebKit/unretained-call-args-arc.mm
new file mode 100644
index 0000000000000..eb4735da60a05
--- /dev/null
+++ b/clang/test/Analysis/Checkers/WebKit/unretained-call-args-arc.mm
@@ -0,0 +1,30 @@
+// RUN: %clang_analyze_cc1 
-analyzer-checker=alpha.webkit.UnretainedCallArgsChecker -fobjc-arc -verify %s
+
+#import "objc-mock-types.h"
+
+SomeObj *provide();
+CFMutableArrayRef provide_cf();
+void someFunction();
+
+namespace raw_ptr {
+
+void foo() {
+  [provide() doWork];
+  CFArrayAppendValue(provide_cf(), nullptr);
+  // expected-warning@-1{{Call argument for parameter 'theArray' is unretained 
and unsafe [alpha.webkit.UnretainedCallArgsChecker]}}
+}
+
+} // namespace raw_ptr
+
+@interface AnotherObj : NSObject
+- (void)foo:(SomeObj *)obj;
+@end
+
+@implementation AnotherObj
+- (void)foo:(SomeObj*)obj {
+  [obj doWork];
+  [provide() doWork];
+  CFArrayAppendValue(provide_cf(), nullptr);
+  // expected-warning@-1{{Call argument for parameter 'theArray' is unretained 
and unsafe [alpha.webkit.UnretainedCallArgsChecker]}}
+}
+@end
diff --git a/clang/test/Analysis/Checkers/WebKit/unretained-call-args.mm 
b/clang/test/Analysis/Checkers/WebKit/unretained-call-args.mm
new file mode 100644
index 0000000000000..5d9f7c873a8f9
--- /dev/null
+++ b/clang/test/Analysis/Checkers/WebKit/unretained-call-args.mm
@@ -0,0 +1,342 @@
+// RUN: %clang_analyze_cc1 
-analyzer-checker=alpha.webkit.UnretainedCallArgsChecker -verify %s
+
+#include "objc-mock-types.h"
+
+SomeObj *provide();
+void consume_obj(SomeObj*);
+
+CFMutableArrayRef provide_cf();
+void consume_cf(CFMutableArrayRef);
+
+void some_function();
+
+namespace simple {
+  void foo() {
+    consume_obj(provide());
+    // expected-warning@-1{{Call argument is unretained and unsafe}}
+    consume_cf(provide_cf());
+    // expected-warning@-1{{Call argument is unretained and unsafe}}
+  }
+
+  // Test that the checker works with [[clang::suppress]].
+  void foo_suppressed() {
+    [[clang::suppress]] consume_obj(provide()); // no-warning
+    [[clang::suppress]] consume_cf(provide_cf()); // no-warning
+  }
+
+}
+
+namespace multi_arg {
+  void consume_refcntbl(int, SomeObj* foo, CFMutableArrayRef bar, bool);
+  void foo() {
+    consume_refcntbl(42, provide(), provide_cf(), true);
+    // expected-warning@-1{{Call argument for parameter 'foo' is unretained 
and unsafe}}
+    // expected-warning@-2{{Call argument for parameter 'bar' is unretained 
and unsafe}}
+  }
+}
+
+namespace retained {
+  RetainPtr<SomeObj> provide_obj() { return RetainPtr<SomeObj>{}; }
+  void consume_obj(RetainPtr<SomeObj>) {}
+
+  RetainPtr<CFMutableArrayRef> provide_cf() { return CFMutableArrayRef{}; }
+  void consume_cf(RetainPtr<CFMutableArrayRef>) {}
+
+  void foo() {
+    consume_obj(provide_obj().get()); // no warning
+    consume_cf(provide_cf().get()); // no warning
+  }
+}
+
+namespace methods {
+  struct Consumer {
+    void consume_obj(SomeObj* ptr);
+    void consume_cf(CFMutableArrayRef ref);
+  };
+
+  void foo() {
+    Consumer c;
+
+    c.consume_obj(provide());
+    // expected-warning@-1{{Call argument for parameter 'ptr' is unretained 
and unsafe}}
+    c.consume_cf(provide_cf());
+    // expected-warning@-1{{Call argument for parameter 'ref' is unretained 
and unsafe}}
+  }
+
+  void foo2() {
+    struct Consumer {
+      void consume(SomeObj*) { some_function(); }
+      void whatever() {
+        consume(provide());
+        // expected-warning@-1{{Call argument is unretained and unsafe}}
+      }
+
+      void consume_cf(CFMutableArrayRef) { some_function(); }
+      void something() {
+        consume_cf(provide_cf());
+        // expected-warni...
[truncated]

``````````

</details>


https://github.com/llvm/llvm-project/pull/128586
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to