Author: Ryosuke Niwa
Date: 2025-03-11T12:47:13-07:00
New Revision: 9e64fc6fb7d9a1ea9fe59d82380219508aee649f

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

LOG: [alpha.webkit.UnretainedCallArgsChecker] Add a checker for NS or CF type 
call arguments. (#130729)

This PR adds alpha.webkit.UnretainedCallArgsChecker by generalizing
RawPtrRefCallArgsChecker. It checks call arguments of NS or CF types are
backed by a RetainPtr or not. The new checker emits warnings for CF
types in Objective-C++ code regardless of ARC and it emits warning for
NS types when ARC is disabled.

Hard-code a few cases as safe: class and superclass properties on class
interfaces and constant dictionary or array literals as well as the
return value of NSClassFromString.

Finally, add the support for variadic function calls and selector
invocations.

Added: 
    clang/test/Analysis/Checkers/WebKit/unretained-call-args-arc.mm
    clang/test/Analysis/Checkers/WebKit/unretained-call-args.mm

Modified: 
    clang/docs/analyzer/checkers.rst
    clang/include/clang/StaticAnalyzer/Checkers/Checkers.td
    clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp
    clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
    clang/lib/StaticAnalyzer/Checkers/WebKit/RawPtrRefCallArgsChecker.cpp

Removed: 
    


################################################################################
diff  --git a/clang/docs/analyzer/checkers.rst 
b/clang/docs/analyzer/checkers.rst
index c947f26204159..e9df204480643 100644
--- a/clang/docs/analyzer/checkers.rst
+++ b/clang/docs/analyzer/checkers.rst
@@ -3642,6 +3642,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 7ee2ec1aaf09f..0903e65297f05 100644
--- a/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td
+++ b/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td
@@ -1785,6 +1785,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 5e67cb29d08e4..ae0f8ad09f26e 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()) { // Treat constants such as kCF* as safe.
+        return callback(E, true);
+      }
+    }
     if (auto *tempExpr = dyn_cast<MaterializeTemporaryExpr>(E)) {
       E = tempExpr->getSubExpr();
       continue;
@@ -57,6 +67,10 @@ bool tryToFindPtrOrigin(
       E = tempExpr->getSubExpr();
       continue;
     }
+    if (auto *OpaqueValue = dyn_cast<OpaqueValueExpr>(E)) {
+      E = OpaqueValue->getSourceExpr();
+      continue;
+    }
     if (auto *Expr = dyn_cast<ConditionalOperator>(E)) {
       return tryToFindPtrOrigin(Expr->getTrueExpr(), StopAtFirstRefCountedObj,
                                 isSafePtr, isSafePtrType, callback) &&
@@ -129,6 +143,11 @@ bool tryToFindPtrOrigin(
           E = call->getArg(0);
           continue;
         }
+
+        auto Name = safeGetName(callee);
+        if (Name == "__builtin___CFStringMakeConstantString" ||
+            Name == "NSClassFromString")
+          return callback(E, true);
       }
     }
     if (auto *ObjCMsgExpr = dyn_cast<ObjCMessageExpr>(E)) {
@@ -136,7 +155,18 @@ bool tryToFindPtrOrigin(
         if (isSafePtrType(Method->getReturnType()))
           return callback(E, true);
       }
+      auto Selector = ObjCMsgExpr->getSelector();
+      auto NameForFirstSlot = Selector.getNameForSlot(0);
+      if ((NameForFirstSlot == "class" || NameForFirstSlot == "superclass") &&
+          !Selector.getNumArgs())
+        return callback(E, true);
     }
+    if (auto *ObjCDict = dyn_cast<ObjCDictionaryLiteral>(E))
+      return callback(ObjCDict, true);
+    if (auto *ObjCArray = dyn_cast<ObjCArrayLiteral>(E))
+      return callback(ObjCArray, true);
+    if (auto *ObjCStr = dyn_cast<ObjCStringLiteral>(E))
+      return callback(ObjCStr, true);
     if (auto *unaryOp = dyn_cast<UnaryOperator>(E)) {
       // FIXME: Currently accepts ANY unary operator. Is it OK?
       E = unaryOp->getSubExpr();
@@ -156,6 +186,14 @@ bool isASafeCallArg(const Expr *E) {
     if (auto *D = dyn_cast_or_null<VarDecl>(FoundDecl)) {
       if (isa<ParmVarDecl>(D) || D->isLocalVarDecl())
         return true;
+      if (auto *ImplicitP = dyn_cast<ImplicitParamDecl>(D)) {
+        auto Kind = ImplicitP->getParameterKind();
+        if (Kind == ImplicitParamKind::ObjCSelf ||
+            Kind == ImplicitParamKind::ObjCCmd ||
+            Kind == ImplicitParamKind::CXXThis ||
+            Kind == ImplicitParamKind::CXXVTT)
+          return true;
+      }
     } else if (auto *BD = dyn_cast_or_null<BindingDecl>(FoundDecl)) {
       VarDecl *VD = BD->getHoldingVar();
       if (VD && (isa<ParmVarDecl>(VD) || VD->isLocalVarDecl()))

diff  --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp 
b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
index 419d9c2325412..e780f610eb389 100644
--- a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
@@ -372,7 +372,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());
       }
     }
   }
@@ -415,7 +416,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 9d07c65da88af..2dc17f048f922 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))
@@ -138,6 +155,58 @@ class RawPtrRefCallArgsChecker
 
         reportBug(Arg, *P, D);
       }
+      for (; ArgIdx < CE->getNumArgs(); ++ArgIdx) {
+        const auto *Arg = CE->getArg(ArgIdx);
+        auto ArgType = Arg->getType();
+        std::optional<bool> IsUncounted = isUnsafePtr(ArgType);
+        if (!IsUncounted || !(*IsUncounted))
+          continue;
+
+        if (auto *defaultArg = dyn_cast<CXXDefaultArgExpr>(Arg))
+          Arg = defaultArg->getExpr();
+
+        if (isPtrOriginSafe(Arg))
+          continue;
+
+        reportBug(Arg, nullptr, D);
+      }
+    }
+  }
+
+  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) {
+      auto *Arg = E->getArg(i);
+      bool hasParam = i < MethodDecl->param_size();
+      auto *Param = hasParam ? MethodDecl->getParamDecl(i) : nullptr;
+      auto ArgType = Arg->getType();
+      std::optional<bool> IsUnsafe = isUnsafePtr(ArgType);
+      if (!IsUnsafe || !(*IsUnsafe))
+        continue;
+      if (isPtrOriginSafe(Arg))
+        continue;
+      reportBug(Arg, Param, D);
     }
   }
 
@@ -158,6 +227,8 @@ class RawPtrRefCallArgsChecker
             // foo(NULL)
             return true;
           }
+          if (isa<ObjCStringLiteral>(ArgOrigin))
+            return true;
           if (isASafeCallArg(ArgOrigin))
             return true;
           if (EFA.isACallToEnsureFn(ArgOrigin))
@@ -212,7 +283,7 @@ class RawPtrRefCallArgsChecker
         overloadedOperatorType == OO_PipePipe)
       return true;
 
-    if (isCtorOfRefCounted(Callee))
+    if (isCtorOfSafePtr(Callee))
       return true;
 
     auto name = safeGetName(Callee);
@@ -277,9 +348,10 @@ class RawPtrRefCallArgsChecker
     }
     Os << " is " << ptrKind() << " and unsafe.";
 
+    bool usesDefaultArgValue = isa<CXXDefaultArgExpr>(CallArg) && Param;
     const SourceLocation SrcLocToReport =
-        isa<CXXDefaultArgExpr>(CallArg) ? Param->getDefaultArg()->getExprLoc()
-                                        : CallArg->getSourceRange().getBegin();
+        usesDefaultArgValue ? Param->getDefaultArg()->getExprLoc()
+                            : CallArg->getSourceRange().getBegin();
 
     PathDiagnosticLocation BSLoc(SrcLocToReport, BR->getSourceManager());
     auto Report = std::make_unique<BasicBugReport>(Bug, Os.str(), BSLoc);
@@ -304,6 +376,23 @@ 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 +406,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 +431,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 +445,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 +489,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/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..3411cbdf5aa9b
--- /dev/null
+++ b/clang/test/Analysis/Checkers/WebKit/unretained-call-args.mm
@@ -0,0 +1,396 @@
+// 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_retainable(int, SomeObj* foo, CFMutableArrayRef bar, bool);
+  void foo() {
+    consume_retainable(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}}
+  }
+
+  void consume_retainable(SomeObj* foo, ...);
+  void bar() {
+    consume_retainable(provide(), 1, provide_cf(), 
RetainPtr<CFMutableArrayRef> { provide_cf() }.get());
+    // expected-warning@-1{{Call argument for parameter 'foo' is unretained 
and unsafe}}
+    // expected-warning@-2{{Call argument is unretained and unsafe}}
+     consume_retainable(RetainPtr<SomeObj> { provide() }.get(), 1, 
RetainPtr<CFMutableArrayRef> { provide_cf() }.get());
+  }
+}
+
+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-warning@-1{{Call argument is unretained and unsafe}}
+      }
+    };
+  }
+
+  void foo3() {
+    struct Consumer {
+      void consume(SomeObj*) { some_function(); }
+      void whatever() {
+        this->consume(provide());
+        // expected-warning@-1{{Call argument is unretained and unsafe}}
+      }
+
+      void consume_cf(CFMutableArrayRef) { some_function(); }
+      void something() {
+        this->consume_cf(provide_cf());
+        // expected-warning@-1{{Call argument is unretained and unsafe}}
+      }
+    };
+  }
+
+}
+
+namespace casts {
+  void foo() {
+    consume_obj(provide());
+    // expected-warning@-1{{Call argument is unretained and unsafe}}
+
+    consume_obj(static_cast<OtherObj*>(provide()));
+    // expected-warning@-1{{Call argument is unretained and unsafe}}
+
+    consume_obj(reinterpret_cast<OtherObj*>(provide()));
+    // expected-warning@-1{{Call argument is unretained and unsafe}}
+
+    consume_obj(downcast<OtherObj>(provide()));
+    // expected-warning@-1{{Call argument is unretained and unsafe}}
+  }
+}
+
+namespace null_ptr {
+  void foo_ref() {
+    consume_obj(nullptr);
+    consume_obj(0);
+    consume_cf(nullptr);
+    consume_cf(0);
+  }
+}
+
+namespace retain_ptr_lookalike {
+  struct Decoy {
+    SomeObj* get();
+  };
+
+  void foo() {
+    Decoy D;
+
+    consume_obj(D.get());
+    // expected-warning@-1{{Call argument is unretained and unsafe}}
+  }
+
+  struct Decoy2 {
+    CFMutableArrayRef get();
+  };
+
+  void bar() {
+    Decoy2 D;
+
+    consume_cf(D.get());
+    // expected-warning@-1{{Call argument is unretained and unsafe}}
+  }
+}
+
+namespace param_formarding_function {
+  void consume_more_obj(OtherObj*);
+  void consume_more_cf(CFMutableArrayRef);
+
+  namespace objc {
+    void foo(SomeObj* param) {
+      consume_more_obj(downcast<OtherObj>(param));
+    }
+  }
+
+  namespace cf {
+    void foo(CFMutableArrayRef param) {
+      consume_more_cf(param);
+    }
+  }
+}
+
+namespace param_formarding_lambda {
+  auto consume_more_obj = [](OtherObj*) { some_function(); };
+  auto consume_more_cf = [](CFMutableArrayRef) { some_function(); };
+
+  namespace objc {
+    void foo(SomeObj* param) {
+      consume_more_obj(downcast<OtherObj>(param));
+    }
+  }
+
+  namespace cf {
+    void foo(CFMutableArrayRef param) {
+      consume_more_cf(param);
+    }
+  }
+}
+
+namespace param_forwarding_method {
+  struct Consumer {
+    void consume_obj(SomeObj*);
+    static void consume_obj_s(SomeObj*);
+    void consume_cf(CFMutableArrayRef);
+    static void consume_cf_s(CFMutableArrayRef);
+  };
+
+  void bar(Consumer* consumer, SomeObj* param) {
+    consumer->consume_obj(param);
+  }
+
+  void foo(SomeObj* param) {
+    Consumer::consume_obj_s(param);
+  }
+
+  void baz(Consumer* consumer, CFMutableArrayRef param) {
+    consumer->consume_cf(param);
+    Consumer::consume_cf_s(param);
+  }
+}
+
+
+namespace default_arg {
+  SomeObj* global;
+  CFMutableArrayRef global_cf;
+
+  void function_with_default_arg1(SomeObj* param = global);
+  // expected-warning@-1{{Call argument for parameter 'param' is unretained 
and unsafe}}
+
+  void function_with_default_arg2(CFMutableArrayRef param = global_cf);
+  // expected-warning@-1{{Call argument for parameter 'param' is unretained 
and unsafe}}
+
+  void foo() {
+    function_with_default_arg1();
+    function_with_default_arg2();
+  }
+}
+
+namespace cxx_member_func {
+  RetainPtr<SomeObj> protectedProvide();
+  RetainPtr<CFMutableArrayRef> protectedProvideCF();
+
+  void foo() {
+    [provide() doWork];
+    // expected-warning@-1{{Reciever is unretained and unsafe}}
+    [protectedProvide().get() doWork];
+
+    CFArrayAppendValue(provide_cf(), nullptr);
+    // expected-warning@-1{{Call argument for parameter 'theArray' is 
unretained and unsafe}}
+    CFArrayAppendValue(protectedProvideCF(), nullptr);
+  };
+
+  void bar() {
+    [downcast<OtherObj>(protectedProvide().get()) 
doMoreWork:downcast<OtherObj>(provide())];
+    // expected-warning@-1{{Call argument for parameter 'other' is unretained 
and unsafe}}
+    [protectedProvide().get() doWork];
+  };
+
+}
+
+namespace cxx_member_operator_call {
+  // The hidden this-pointer argument without a corresponding parameter caused 
couple bugs in parameter <-> argument attribution.
+  struct Foo {
+    Foo& operator+(SomeObj* bad);
+    friend Foo& operator-(Foo& lhs, SomeObj* bad);
+    void operator()(SomeObj* bad);
+  };
+
+  SomeObj* global;
+
+  void foo() {
+    Foo f;
+    f + global;
+    // expected-warning@-1{{Call argument for parameter 'bad' is unretained 
and unsafe}}
+    f - global;
+    // expected-warning@-1{{Call argument for parameter 'bad' is unretained 
and unsafe}}
+    f(global);
+    // expected-warning@-1{{Call argument for parameter 'bad' is unretained 
and unsafe}}
+  }
+}
+
+namespace call_with_ptr_on_ref {
+  RetainPtr<SomeObj> provideProtected();
+  RetainPtr<CFMutableArrayRef> provideProtectedCF();
+  void bar(SomeObj* bad);
+  void bar_cf(CFMutableArrayRef bad);
+  bool baz();
+  void foo(bool v) {
+    bar(v ? nullptr : provideProtected().get());
+    bar(baz() ? provideProtected().get() : nullptr);
+    bar(v ? provide() : provideProtected().get());
+    // expected-warning@-1{{Call argument for parameter 'bad' is unretained 
and unsafe}}
+    bar(v ? provideProtected().get() : provide());
+    // expected-warning@-1{{Call argument for parameter 'bad' is unretained 
and unsafe}}
+
+    bar_cf(v ? nullptr : provideProtectedCF().get());
+    bar_cf(baz() ? provideProtectedCF().get() : nullptr);
+    bar_cf(v ? provide_cf() : provideProtectedCF().get());
+    // expected-warning@-1{{Call argument for parameter 'bad' is unretained 
and unsafe}}
+    bar_cf(v ? provideProtectedCF().get() : provide_cf());
+    // expected-warning@-1{{Call argument for parameter 'bad' is unretained 
and unsafe}}
+  }
+}
+
+namespace call_with_explicit_temporary_obj {
+  void foo() {
+    [RetainPtr<SomeObj>(provide()).get() doWork];
+    CFArrayAppendValue(RetainPtr<CFMutableArrayRef> { provide_cf() }.get(), 
nullptr);
+  }
+  template <typename T>
+  void bar() {
+    [RetainPtr<SomeObj>(provide()).get() doWork];
+    CFArrayAppendValue(RetainPtr<CFMutableArrayRef> { provide_cf() }.get(), 
nullptr);
+  }
+  void baz() {
+    bar<int>();
+  }
+}
+
+namespace call_with_adopt_ref {
+  void foo() {
+    [adoptNS(provide()).get() doWork];
+    CFArrayAppendValue(adoptCF(provide_cf()).get(), nullptr);
+  }
+}
+
+namespace call_with_cf_constant {
+  void bar(const NSArray *);
+  void baz(const NSDictionary *);
+  void foo() {
+    CFArrayCreateMutable(kCFAllocatorDefault, 10);
+    bar(@[@"hello"]);
+    baz(@{@"hello": @3});
+  }
+}
+
+namespace call_with_cf_string {
+  void bar(CFStringRef);
+  void foo() {
+    bar(CFSTR("hello"));
+  }
+}
+
+namespace call_with_ns_string {
+  void bar(NSString *);
+  void foo() {
+    bar(@"world");
+  }
+}
+
+namespace bridge_cast_arg {
+  void bar(NSString *);
+  void baz(NSString *);
+  extern const CFStringRef kCFBundleNameKey;
+
+  NSObject *foo(CFStringRef arg) {
+    bar((NSString *)bridge_cast((CFTypeRef)arg));
+    auto dict = @{
+      @"hello": @1,
+    };
+    return dict[(__bridge NSString *)kCFBundleNameKey];
+  }
+}
+
+namespace alloc_init_pair {
+  void foo() {
+    auto obj = adoptNS([[SomeObj alloc] init]);
+    [obj doWork];
+  }
+}
+
+namespace alloc_class {
+  bool foo(NSObject *obj) {
+    return [obj isKindOfClass:SomeObj.class] && [obj 
isKindOfClass:NSClassFromString(@"SomeObj")];
+  }
+
+  bool bar(NSObject *obj) {
+    return [obj isKindOfClass:[SomeObj class]];
+  }
+
+  bool baz(NSObject *obj) {
+    return [obj isKindOfClass:[SomeObj superclass]];
+  }
+}
+
+@interface TestObject : NSObject
+- (void)doWork:(NSString *)msg, ...;
+- (void)doWorkOnSelf;
+@end
+
+@implementation TestObject
+
+- (void)doWork:(NSString *)msg, ... {
+  some_function();
+}
+
+- (void)doWorkOnSelf {
+  [self doWork:nil];
+  [self doWork:@"hello", provide(), provide_cf()];
+  // expected-warning@-1{{Call argument is unretained and unsafe}}
+  // expected-warning@-2{{Call argument is unretained and unsafe}}
+  [self doWork:@"hello", RetainPtr<SomeObj> { provide() }.get(), 
RetainPtr<CFMutableArrayRef> { provide_cf() }.get()];
+}
+
+@end


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

Reply via email to