Author: Ryosuke Niwa
Date: 2025-03-12T19:09:05-07:00
New Revision: be9ca85d64eb5b2d7b13d7c6154055ae97092d1e

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

LOG: [alpha.webkit.webkit.RetainPtrCtorAdoptChecker] Add a new WebKit checker 
for correct use of RetainPtr, adoptNS, and adoptCF (#128679)

Add a new WebKit checker to validate the correct use of RetainPtr
constructor as well as adoptNS and adoptCF functions. adoptNS and
adoptCf are used for +1 semantics and RetainPtr constructor is used for
+0 semantics.

Added: 
    clang/lib/StaticAnalyzer/Checkers/WebKit/RetainPtrCtorAdoptChecker.cpp
    clang/test/Analysis/Checkers/WebKit/retain-ptr-ctor-adopt-use-arc.mm
    clang/test/Analysis/Checkers/WebKit/retain-ptr-ctor-adopt-use.mm

Modified: 
    clang/docs/analyzer/checkers.rst
    clang/include/clang/StaticAnalyzer/Checkers/Checkers.td
    clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt
    clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
    clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.h
    clang/test/Analysis/Checkers/WebKit/objc-mock-types.h

Removed: 
    


################################################################################
diff  --git a/clang/docs/analyzer/checkers.rst 
b/clang/docs/analyzer/checkers.rst
index e9df204480643..905c93678ffe1 100644
--- a/clang/docs/analyzer/checkers.rst
+++ b/clang/docs/analyzer/checkers.rst
@@ -3779,6 +3779,26 @@ Here are some examples of situations that we warn about 
as they *might* be poten
       NSObject* unretained = retained.get(); // warn
     }
 
+webkit.RetainPtrCtorAdoptChecker
+""""""""""""""""""""""""""""""""
+The goal of this rule is to make sure the constructor of RetainPtr as well as 
adoptNS and adoptCF are used correctly.
+When creating a RetainPtr with +1 semantics, adoptNS or adoptCF should be 
used, and in +0 semantics, RetainPtr constructor should be used.
+Warn otherwise.
+
+These are examples of cases that we consider correct:
+
+  .. code-block:: cpp
+
+    RetainPtr ptr = adoptNS([[NSObject alloc] init]); // ok
+    RetainPtr ptr = CGImageGetColorSpace(image); // ok
+
+Here are some examples of cases that we consider incorrect use of RetainPtr 
constructor and adoptCF
+
+  .. code-block:: cpp
+
+    RetainPtr ptr = [[NSObject alloc] init]; // warn
+    auto ptr = adoptCF(CGImageGetColorSpace(image)); // warn
+
 Debug Checkers
 ---------------
 

diff  --git a/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td 
b/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td
index 0903e65297f05..deb36561839cd 100644
--- a/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td
+++ b/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td
@@ -1801,4 +1801,8 @@ def UnretainedLocalVarsChecker : 
Checker<"UnretainedLocalVarsChecker">,
   HelpText<"Check unretained local variables.">,
   Documentation<HasDocumentation>;
 
+def RetainPtrCtorAdoptChecker : Checker<"RetainPtrCtorAdoptChecker">,
+  HelpText<"Check for correct use of RetainPtr constructor, adoptNS, and 
adoptCF">,
+  Documentation<HasDocumentation>;
+
 } // end alpha.webkit

diff  --git a/clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt 
b/clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt
index 878b48197cd58..a0b8549ade917 100644
--- a/clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt
+++ b/clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt
@@ -133,6 +133,7 @@ add_clang_library(clangStaticAnalyzerCheckers
   WebKit/MemoryUnsafeCastChecker.cpp
   WebKit/PtrTypesSemantics.cpp
   WebKit/RefCntblBaseVirtualDtorChecker.cpp
+  WebKit/RetainPtrCtorAdoptChecker.cpp
   WebKit/RawPtrRefCallArgsChecker.cpp
   WebKit/RawPtrRefLambdaCapturesChecker.cpp
   WebKit/RawPtrRefLocalVarsChecker.cpp

diff  --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp 
b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
index e780f610eb389..bfa58a11c6199 100644
--- a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
@@ -226,15 +226,16 @@ void RetainTypeChecker::visitTypedef(const TypedefDecl 
*TD) {
     return;
 
   for (auto *Redecl : RT->getDecl()->getMostRecentDecl()->redecls()) {
-    if (Redecl->getAttr<ObjCBridgeAttr>()) {
+    if (Redecl->getAttr<ObjCBridgeAttr>() ||
+        Redecl->getAttr<ObjCBridgeMutableAttr>()) {
       CFPointees.insert(RT);
       return;
     }
   }
 }
 
-bool RetainTypeChecker::isUnretained(const QualType QT) {
-  if (ento::cocoa::isCocoaObjectRef(QT) && !IsARCEnabled)
+bool RetainTypeChecker::isUnretained(const QualType QT, bool ignoreARC) {
+  if (ento::cocoa::isCocoaObjectRef(QT) && (!IsARCEnabled || ignoreARC))
     return true;
   auto CanonicalType = QT.getCanonicalType();
   auto PointeeType = CanonicalType->getPointeeType();

diff  --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.h 
b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.h
index 323d473665888..60bfd1a8dd480 100644
--- a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.h
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.h
@@ -75,7 +75,8 @@ class RetainTypeChecker {
 public:
   void visitTranslationUnitDecl(const TranslationUnitDecl *);
   void visitTypedef(const TypedefDecl *);
-  bool isUnretained(const QualType);
+  bool isUnretained(const QualType, bool ignoreARC = false);
+  bool isARCEnabled() const { return IsARCEnabled; }
 };
 
 /// \returns true if \p Class is NS or CF objects AND not retained, false if

diff  --git 
a/clang/lib/StaticAnalyzer/Checkers/WebKit/RetainPtrCtorAdoptChecker.cpp 
b/clang/lib/StaticAnalyzer/Checkers/WebKit/RetainPtrCtorAdoptChecker.cpp
new file mode 100644
index 0000000000000..4ce3262e90e13
--- /dev/null
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/RetainPtrCtorAdoptChecker.cpp
@@ -0,0 +1,383 @@
+//=======- RetainPtrCtorAdoptChecker.cpp -------------------------*- C++ 
-*-==//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM 
Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "ASTUtils.h"
+#include "DiagOutputUtils.h"
+#include "PtrTypesSemantics.h"
+#include "clang/AST/CXXInheritance.h"
+#include "clang/AST/RecursiveASTVisitor.h"
+#include "clang/AST/StmtVisitor.h"
+#include "clang/Analysis/RetainSummaryManager.h"
+#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
+#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
+#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
+#include "clang/StaticAnalyzer/Core/Checker.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
+#include "llvm/ADT/DenseSet.h"
+#include "llvm/ADT/SetVector.h"
+#include <optional>
+
+using namespace clang;
+using namespace ento;
+
+namespace {
+
+class RetainPtrCtorAdoptChecker
+    : public Checker<check::ASTDecl<TranslationUnitDecl>> {
+private:
+  BugType Bug;
+  mutable BugReporter *BR;
+  mutable std::unique_ptr<RetainSummaryManager> Summaries;
+  mutable llvm::DenseSet<const ValueDecl *> CreateOrCopyOutArguments;
+  mutable RetainTypeChecker RTC;
+
+public:
+  RetainPtrCtorAdoptChecker()
+      : Bug(this, "Correct use of RetainPtr, adoptNS, and adoptCF",
+            "WebKit coding guidelines") {}
+
+  void checkASTDecl(const TranslationUnitDecl *TUD, AnalysisManager &MGR,
+                    BugReporter &BRArg) const {
+    BR = &BRArg;
+
+    // The calls to checkAST* from AnalysisConsumer don't
+    // visit template instantiations or lambda classes. We
+    // want to visit those, so we make our own RecursiveASTVisitor.
+    struct LocalVisitor : public RecursiveASTVisitor<LocalVisitor> {
+      const RetainPtrCtorAdoptChecker *Checker;
+      Decl *DeclWithIssue{nullptr};
+
+      using Base = RecursiveASTVisitor<LocalVisitor>;
+
+      explicit LocalVisitor(const RetainPtrCtorAdoptChecker *Checker)
+          : Checker(Checker) {
+        assert(Checker);
+      }
+
+      bool shouldVisitTemplateInstantiations() const { return true; }
+      bool shouldVisitImplicitCode() const { return false; }
+
+      bool TraverseDecl(Decl *D) {
+        llvm::SaveAndRestore SavedDecl(DeclWithIssue);
+        if (D && (isa<FunctionDecl>(D) || isa<ObjCMethodDecl>(D)))
+          DeclWithIssue = D;
+        return Base::TraverseDecl(D);
+      }
+
+      bool TraverseClassTemplateDecl(ClassTemplateDecl *CTD) {
+        if (safeGetName(CTD) == "RetainPtr")
+          return true; // Skip the contents of RetainPtr.
+        return Base::TraverseClassTemplateDecl(CTD);
+      }
+
+      bool VisitTypedefDecl(TypedefDecl *TD) {
+        Checker->RTC.visitTypedef(TD);
+        return true;
+      }
+
+      bool VisitCallExpr(const CallExpr *CE) {
+        Checker->visitCallExpr(CE, DeclWithIssue);
+        return true;
+      }
+
+      bool VisitCXXConstructExpr(const CXXConstructExpr *CE) {
+        Checker->visitConstructExpr(CE, DeclWithIssue);
+        return true;
+      }
+    };
+
+    LocalVisitor visitor(this);
+    Summaries = std::make_unique<RetainSummaryManager>(
+        TUD->getASTContext(), true /* trackObjCAndCFObjects */,
+        false /* trackOSObjects */);
+    RTC.visitTranslationUnitDecl(TUD);
+    visitor.TraverseDecl(const_cast<TranslationUnitDecl *>(TUD));
+  }
+
+  bool isAdoptFn(const Decl *FnDecl) const {
+    auto Name = safeGetName(FnDecl);
+    return Name == "adoptNS" || Name == "adoptCF" || Name == "adoptNSArc" ||
+           Name == "adoptCFArc";
+  }
+
+  bool isAdoptNS(const Decl *FnDecl) const {
+    auto Name = safeGetName(FnDecl);
+    return Name == "adoptNS" || Name == "adoptNSArc";
+  }
+
+  void visitCallExpr(const CallExpr *CE, const Decl *DeclWithIssue) const {
+    if (BR->getSourceManager().isInSystemHeader(CE->getExprLoc()))
+      return;
+
+    auto *F = CE->getDirectCallee();
+    if (!F)
+      return;
+
+    if (!isAdoptFn(F) || !CE->getNumArgs()) {
+      rememberOutArguments(CE, F);
+      return;
+    }
+
+    auto *Arg = CE->getArg(0)->IgnoreParenCasts();
+    auto Result = isOwned(Arg);
+    auto Name = safeGetName(F);
+    if (Result == IsOwnedResult::Unknown)
+      Result = IsOwnedResult::NotOwned;
+    if (Result == IsOwnedResult::NotOwned && !isAllocInit(Arg) &&
+        !isCreateOrCopy(Arg)) {
+      if (auto *DRE = dyn_cast<DeclRefExpr>(Arg)) {
+        if (CreateOrCopyOutArguments.contains(DRE->getDecl()))
+          return;
+      }
+      if (RTC.isARCEnabled() && isAdoptNS(F))
+        reportUseAfterFree(Name, CE, DeclWithIssue, "when ARC is disabled");
+      else
+        reportUseAfterFree(Name, CE, DeclWithIssue);
+    }
+  }
+
+  void rememberOutArguments(const CallExpr *CE,
+                            const FunctionDecl *Callee) const {
+    if (!isCreateOrCopyFunction(Callee))
+      return;
+
+    unsigned ArgCount = CE->getNumArgs();
+    for (unsigned ArgIndex = 0; ArgIndex < ArgCount; ++ArgIndex) {
+      auto *Arg = CE->getArg(ArgIndex)->IgnoreParenCasts();
+      auto *Unary = dyn_cast<UnaryOperator>(Arg);
+      if (!Unary)
+        continue;
+      if (Unary->getOpcode() != UO_AddrOf)
+        continue;
+      auto *SubExpr = Unary->getSubExpr();
+      if (!SubExpr)
+        continue;
+      auto *DRE = dyn_cast<DeclRefExpr>(SubExpr->IgnoreParenCasts());
+      if (!DRE)
+        continue;
+      auto *Decl = DRE->getDecl();
+      if (!Decl)
+        continue;
+      CreateOrCopyOutArguments.insert(Decl);
+    }
+  }
+
+  void visitConstructExpr(const CXXConstructExpr *CE,
+                          const Decl *DeclWithIssue) const {
+    if (BR->getSourceManager().isInSystemHeader(CE->getExprLoc()))
+      return;
+
+    auto *Ctor = CE->getConstructor();
+    if (!Ctor)
+      return;
+
+    auto *Cls = Ctor->getParent();
+    if (!Cls)
+      return;
+
+    if (safeGetName(Cls) != "RetainPtr" || !CE->getNumArgs())
+      return;
+
+    // Ignore RetainPtr construction inside adoptNS, adoptCF, and retainPtr.
+    if (isAdoptFn(DeclWithIssue) || safeGetName(DeclWithIssue) == "retainPtr")
+      return;
+
+    std::string Name = "RetainPtr constructor";
+    auto *Arg = CE->getArg(0)->IgnoreParenCasts();
+    auto Result = isOwned(Arg);
+    if (Result == IsOwnedResult::Unknown)
+      Result = IsOwnedResult::NotOwned;
+    if (Result == IsOwnedResult::Owned)
+      reportLeak(Name, CE, DeclWithIssue);
+    else if (RTC.isARCEnabled() && isAllocInit(Arg))
+      reportLeak(Name, CE, DeclWithIssue, "when ARC is disabled");
+    else if (isCreateOrCopy(Arg))
+      reportLeak(Name, CE, DeclWithIssue);
+  }
+
+  bool isAllocInit(const Expr *E) const {
+    auto *ObjCMsgExpr = dyn_cast<ObjCMessageExpr>(E);
+    if (!ObjCMsgExpr)
+      return false;
+    auto Selector = ObjCMsgExpr->getSelector();
+    auto NameForFirstSlot = Selector.getNameForSlot(0);
+    if (NameForFirstSlot == "alloc" || NameForFirstSlot.starts_with("copy") ||
+        NameForFirstSlot.starts_with("mutableCopy"))
+      return true;
+    if (!NameForFirstSlot.starts_with("init"))
+      return false;
+    if (!ObjCMsgExpr->isInstanceMessage())
+      return false;
+    auto *Receiver = ObjCMsgExpr->getInstanceReceiver()->IgnoreParenCasts();
+    if (!Receiver)
+      return false;
+    if (auto *InnerObjCMsgExpr = dyn_cast<ObjCMessageExpr>(Receiver)) {
+      auto InnerSelector = InnerObjCMsgExpr->getSelector();
+      return InnerSelector.getNameForSlot(0) == "alloc";
+    } else if (auto *CE = dyn_cast<CallExpr>(Receiver)) {
+      if (auto *Callee = CE->getDirectCallee()) {
+        auto CalleeName = Callee->getName();
+        return CalleeName.starts_with("alloc");
+      }
+    }
+    return false;
+  }
+
+  bool isCreateOrCopy(const Expr *E) const {
+    auto *CE = dyn_cast<CallExpr>(E);
+    if (!CE)
+      return false;
+    auto *Callee = CE->getDirectCallee();
+    if (!Callee)
+      return false;
+    return isCreateOrCopyFunction(Callee);
+  }
+
+  bool isCreateOrCopyFunction(const FunctionDecl *FnDecl) const {
+    auto CalleeName = safeGetName(FnDecl);
+    return CalleeName.find("Create") != std::string::npos ||
+           CalleeName.find("Copy") != std::string::npos;
+  }
+
+  enum class IsOwnedResult { Unknown, Skip, Owned, NotOwned };
+  IsOwnedResult isOwned(const Expr *E) const {
+    while (1) {
+      if (isa<CXXNullPtrLiteralExpr>(E))
+        return IsOwnedResult::NotOwned;
+      if (auto *DRE = dyn_cast<DeclRefExpr>(E)) {
+        auto QT = DRE->getType();
+        if (isRetainPtrType(QT))
+          return IsOwnedResult::NotOwned;
+        QT = QT.getCanonicalType();
+        if (RTC.isUnretained(QT, true /* ignoreARC */))
+          return IsOwnedResult::NotOwned;
+        auto *PointeeType = QT->getPointeeType().getTypePtrOrNull();
+        if (PointeeType && PointeeType->isVoidType())
+          return IsOwnedResult::NotOwned; // Assume reading void* as +0.
+      }
+      if (auto *TE = dyn_cast<CXXBindTemporaryExpr>(E)) {
+        E = TE->getSubExpr();
+        continue;
+      }
+      if (auto *ObjCMsgExpr = dyn_cast<ObjCMessageExpr>(E)) {
+        auto Summary = Summaries->getSummary(AnyCall(ObjCMsgExpr));
+        auto RetEffect = Summary->getRetEffect();
+        switch (RetEffect.getKind()) {
+        case RetEffect::NoRet:
+          return IsOwnedResult::Unknown;
+        case RetEffect::OwnedSymbol:
+          return IsOwnedResult::Owned;
+        case RetEffect::NotOwnedSymbol:
+          return IsOwnedResult::NotOwned;
+        case RetEffect::OwnedWhenTrackedReceiver:
+          if (auto *Receiver = ObjCMsgExpr->getInstanceReceiver()) {
+            E = Receiver->IgnoreParenCasts();
+            continue;
+          }
+          return IsOwnedResult::Unknown;
+        case RetEffect::NoRetHard:
+          return IsOwnedResult::Unknown;
+        }
+      }
+      if (auto *CXXCE = dyn_cast<CXXMemberCallExpr>(E)) {
+        if (auto *MD = CXXCE->getMethodDecl()) {
+          auto *Cls = MD->getParent();
+          if (auto *CD = dyn_cast<CXXConversionDecl>(MD)) {
+            auto QT = CD->getConversionType().getCanonicalType();
+            auto *ResultType = QT.getTypePtrOrNull();
+            if (safeGetName(Cls) == "RetainPtr" && ResultType &&
+                (ResultType->isPointerType() || ResultType->isReferenceType() 
||
+                 ResultType->isObjCObjectPointerType()))
+              return IsOwnedResult::NotOwned;
+          }
+          if (safeGetName(MD) == "leakRef" && safeGetName(Cls) == "RetainPtr")
+            return IsOwnedResult::Owned;
+        }
+      }
+      if (auto *CE = dyn_cast<CallExpr>(E)) {
+        if (auto *Callee = CE->getDirectCallee()) {
+          if (isAdoptFn(Callee))
+            return IsOwnedResult::NotOwned;
+          if (safeGetName(Callee) == "__builtin___CFStringMakeConstantString")
+            return IsOwnedResult::NotOwned;
+          auto RetType = Callee->getReturnType();
+          if (isRetainPtrType(RetType))
+            return IsOwnedResult::NotOwned;
+        } else if (auto *CalleeExpr = CE->getCallee()) {
+          if (isa<CXXDependentScopeMemberExpr>(CalleeExpr))
+            return IsOwnedResult::Skip; // Wait for instantiation.
+        }
+        auto Summary = Summaries->getSummary(AnyCall(CE));
+        auto RetEffect = Summary->getRetEffect();
+        switch (RetEffect.getKind()) {
+        case RetEffect::NoRet:
+          return IsOwnedResult::Unknown;
+        case RetEffect::OwnedSymbol:
+          return IsOwnedResult::Owned;
+        case RetEffect::NotOwnedSymbol:
+          return IsOwnedResult::NotOwned;
+        case RetEffect::OwnedWhenTrackedReceiver:
+          return IsOwnedResult::Unknown;
+        case RetEffect::NoRetHard:
+          return IsOwnedResult::Unknown;
+        }
+      }
+      break;
+    }
+    return IsOwnedResult::Unknown;
+  }
+
+  void reportUseAfterFree(std::string &Name, const CallExpr *CE,
+                          const Decl *DeclWithIssue,
+                          const char *condition = nullptr) const {
+    SmallString<100> Buf;
+    llvm::raw_svector_ostream Os(Buf);
+
+    Os << "Incorrect use of " << Name
+       << ". The argument is +0 and results in an use-after-free";
+    if (condition)
+      Os << " " << condition;
+    Os << ".";
+
+    PathDiagnosticLocation BSLoc(CE->getSourceRange().getBegin(),
+                                 BR->getSourceManager());
+    auto Report = std::make_unique<BasicBugReport>(Bug, Os.str(), BSLoc);
+    Report->addRange(CE->getSourceRange());
+    Report->setDeclWithIssue(DeclWithIssue);
+    BR->emitReport(std::move(Report));
+  }
+
+  void reportLeak(std::string &Name, const CXXConstructExpr *CE,
+                  const Decl *DeclWithIssue,
+                  const char *condition = nullptr) const {
+    SmallString<100> Buf;
+    llvm::raw_svector_ostream Os(Buf);
+
+    Os << "Incorrect use of " << Name
+       << ". The argument is +1 and results in a memory leak";
+    if (condition)
+      Os << " " << condition;
+    Os << ".";
+
+    PathDiagnosticLocation BSLoc(CE->getSourceRange().getBegin(),
+                                 BR->getSourceManager());
+    auto Report = std::make_unique<BasicBugReport>(Bug, Os.str(), BSLoc);
+    Report->addRange(CE->getSourceRange());
+    Report->setDeclWithIssue(DeclWithIssue);
+    BR->emitReport(std::move(Report));
+  }
+};
+} // namespace
+
+void ento::registerRetainPtrCtorAdoptChecker(CheckerManager &Mgr) {
+  Mgr.registerChecker<RetainPtrCtorAdoptChecker>();
+}
+
+bool ento::shouldRegisterRetainPtrCtorAdoptChecker(const CheckerManager &mgr) {
+  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 9b13810d0c5c9..c3925218c6c99 100644
--- a/clang/test/Analysis/Checkers/WebKit/objc-mock-types.h
+++ b/clang/test/Analysis/Checkers/WebKit/objc-mock-types.h
@@ -6,6 +6,7 @@
 #define CF_BRIDGED_MUTABLE_TYPE(T) __attribute__((objc_bridge_mutable(T)))
 typedef CF_BRIDGED_TYPE(id) void * CFTypeRef;
 typedef signed char BOOL;
+typedef unsigned char Boolean;
 typedef signed long CFIndex;
 typedef unsigned long NSUInteger;
 typedef const struct CF_BRIDGED_TYPE(id) __CFAllocator * CFAllocatorRef;
@@ -13,12 +14,43 @@ typedef const struct CF_BRIDGED_TYPE(NSString) __CFString * 
CFStringRef;
 typedef const struct CF_BRIDGED_TYPE(NSArray) __CFArray * CFArrayRef;
 typedef struct CF_BRIDGED_MUTABLE_TYPE(NSMutableArray) __CFArray * 
CFMutableArrayRef;
 typedef struct CF_BRIDGED_MUTABLE_TYPE(CFRunLoopRef) __CFRunLoop * 
CFRunLoopRef;
+#define NS_RETURNS_RETAINED __attribute__((ns_returns_retained))
+#define CF_CONSUMED __attribute__((cf_consumed))
+#define CF_RETURNS_RETAINED __attribute__((cf_returns_retained))
 extern const CFAllocatorRef kCFAllocatorDefault;
 typedef struct _NSZone NSZone;
 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);
+
+typedef const struct CF_BRIDGED_TYPE(NSDictionary) __CFDictionary * 
CFDictionaryRef;
+typedef struct CF_BRIDGED_MUTABLE_TYPE(NSMutableDictionary) __CFDictionary * 
CFMutableDictionaryRef;
+
+CFDictionaryRef CFDictionaryCreate(CFAllocatorRef allocator, const void 
**keys, const void **values, CFIndex numValues);
+CFDictionaryRef CFDictionaryCreateCopy(CFAllocatorRef allocator, 
CFDictionaryRef theDict);
+CFDictionaryRef CFDictionaryCreateMutableCopy(CFAllocatorRef allocator, 
CFIndex capacity, CFDictionaryRef theDict);
+CFIndex CFDictionaryGetCount(CFDictionaryRef theDict);
+Boolean CFDictionaryContainsKey(CFDictionaryRef theDict, const void *key);
+Boolean CFDictionaryContainsValue(CFDictionaryRef theDict, const void *value);
+const void *CFDictionaryGetValue(CFDictionaryRef theDict, const void *key);
+void CFDictionaryAddValue(CFMutableDictionaryRef theDict, const void *key, 
const void *value);
+void CFDictionarySetValue(CFMutableDictionaryRef theDict, const void *key, 
const void *value);
+void CFDictionaryReplaceValue(CFMutableDictionaryRef theDict, const void *key, 
const void *value);
+void CFDictionaryRemoveValue(CFMutableDictionaryRef theDict, const void *key);
+
+typedef struct CF_BRIDGED_TYPE(id) __SecTask * SecTaskRef;
+SecTaskRef SecTaskCreateFromSelf(CFAllocatorRef allocator);
+
+typedef struct CF_BRIDGED_TYPE(id) CF_BRIDGED_MUTABLE_TYPE(IOSurface) 
__IOSurface * IOSurfaceRef;
+IOSurfaceRef IOSurfaceCreate(CFDictionaryRef properties);
+
+typedef struct CF_BRIDGED_TYPE(id) __CVBuffer *CVBufferRef;
+typedef CVBufferRef CVImageBufferRef;
+typedef CVImageBufferRef CVPixelBufferRef;
+typedef signed int CVReturn;
+CVReturn CVPixelBufferCreateWithIOSurface(CFAllocatorRef allocator, 
IOSurfaceRef surface, CFDictionaryRef pixelBufferAttributes, CVPixelBufferRef * 
pixelBufferOut);
+
 CFRunLoopRef CFRunLoopGetCurrent(void);
 CFRunLoopRef CFRunLoopGetMain(void);
 extern CFTypeRef CFRetain(CFTypeRef cf);

diff  --git 
a/clang/test/Analysis/Checkers/WebKit/retain-ptr-ctor-adopt-use-arc.mm 
b/clang/test/Analysis/Checkers/WebKit/retain-ptr-ctor-adopt-use-arc.mm
new file mode 100644
index 0000000000000..99f488b578910
--- /dev/null
+++ b/clang/test/Analysis/Checkers/WebKit/retain-ptr-ctor-adopt-use-arc.mm
@@ -0,0 +1,98 @@
+// RUN: %clang_analyze_cc1 
-analyzer-checker=alpha.webkit.RetainPtrCtorAdoptChecker -fobjc-arc -verify %s
+
+#include "objc-mock-types.h"
+
+void basic_correct() {
+  auto ns1 = adoptNS([SomeObj alloc]);
+  auto ns2 = adoptNS([[SomeObj alloc] init]);
+  RetainPtr<SomeObj> ns3 = [ns1.get() next];
+  auto ns4 = adoptNS([ns3 mutableCopy]);
+  auto ns5 = adoptNS([ns3 copyWithValue:3]);
+  auto ns6 = retainPtr([ns3 next]);
+  CFMutableArrayRef cf1 = adoptCF(CFArrayCreateMutable(kCFAllocatorDefault, 
10));
+  auto cf2 = adoptCF(SecTaskCreateFromSelf(kCFAllocatorDefault));
+}
+
+CFMutableArrayRef provide_cf();
+
+void basic_wrong() {
+  RetainPtr<SomeObj> ns1 = [[SomeObj alloc] init];
+  // expected-warning@-1{{Incorrect use of RetainPtr constructor. The argument 
is +1 and results in a memory leak when ARC is disabled 
[alpha.webkit.RetainPtrCtorAdoptChecker]}}
+  auto ns2 = adoptNS([ns1.get() next]);
+  // expected-warning@-1{{Incorrect use of adoptNS. The argument is +0 and 
results in an use-after-free when ARC is disabled 
[alpha.webkit.RetainPtrCtorAdoptChecker]}}
+  RetainPtr<CFMutableArrayRef> cf1 = CFArrayCreateMutable(kCFAllocatorDefault, 
10);
+  // expected-warning@-1{{Incorrect use of RetainPtr constructor. The argument 
is +1 and results in a memory leak [alpha.webkit.RetainPtrCtorAdoptChecker]}}
+  RetainPtr<CFMutableArrayRef> cf2 = adoptCF(provide_cf());
+  // expected-warning@-1{{Incorrect use of adoptCF. The argument is +0 and 
results in an use-after-free [alpha.webkit.RetainPtrCtorAdoptChecker]}}
+  RetainPtr<CFTypeRef> cf3 = SecTaskCreateFromSelf(kCFAllocatorDefault);
+  // expected-warning@-1{{Incorrect use of RetainPtr constructor. The argument 
is +1 and results in a memory leak [alpha.webkit.RetainPtrCtorAdoptChecker]}}
+}
+
+RetainPtr<CVPixelBufferRef> cf_out_argument() {
+  auto surface = adoptCF(IOSurfaceCreate(nullptr));
+  CVPixelBufferRef rawBuffer = nullptr;
+  auto status = CVPixelBufferCreateWithIOSurface(kCFAllocatorDefault, 
surface.get(), nullptr, &rawBuffer);
+  return adoptCF(rawBuffer);
+}
+
+RetainPtr<SomeObj> return_nullptr() {
+  return nullptr;
+}
+
+RetainPtr<SomeObj> return_retainptr() {
+  RetainPtr<SomeObj> foo;
+  return foo;
+}
+
+CFTypeRef CopyValueForSomething();
+
+void cast_retainptr() {
+  RetainPtr<NSObject> foo;
+  RetainPtr<SomeObj> bar = static_cast<SomeObj*>(foo);
+
+  auto baz = adoptCF(CopyValueForSomething()).get();
+  RetainPtr<CFArrayRef> v = static_cast<CFArrayRef>(baz);
+}
+
+SomeObj* allocSomeObj();
+
+void adopt_retainptr() {
+  RetainPtr<NSObject> foo = adoptNS([[SomeObj alloc] init]);
+  auto bar = adoptNS([allocSomeObj() init]);
+}
+
+RetainPtr<CFArrayRef> return_arg(CFArrayRef arg) {
+  return arg;
+}
+
+class MemberInit {
+public:
+  MemberInit(CFMutableArrayRef array, NSString *str, CFRunLoopRef runLoop)
+    : m_array(array)
+    , m_str(str)
+    , m_runLoop(runLoop)
+  { }
+
+private:
+  RetainPtr<CFMutableArrayRef> m_array;
+  RetainPtr<NSString> m_str;
+  RetainPtr<CFRunLoopRef> m_runLoop;
+};
+void create_member_init() {
+  MemberInit init { CFArrayCreateMutable(kCFAllocatorDefault, 10), @"hello", 
CFRunLoopGetCurrent() };
+}
+
+RetainPtr<CFStringRef> cfstr() {
+  return CFSTR("");
+}
+
+template <typename CF, typename NS>
+static RetainPtr<NS> bridge_cast(RetainPtr<CF>&& ptr)
+{
+  return adoptNS((__bridge NSArray *)(ptr.leakRef()));
+}
+
+RetainPtr<CFArrayRef> create_cf_array();
+RetainPtr<id> return_bridge_cast() {
+  return bridge_cast<CFArrayRef, NSArray>(create_cf_array());
+}

diff  --git a/clang/test/Analysis/Checkers/WebKit/retain-ptr-ctor-adopt-use.mm 
b/clang/test/Analysis/Checkers/WebKit/retain-ptr-ctor-adopt-use.mm
new file mode 100644
index 0000000000000..111342bc0e388
--- /dev/null
+++ b/clang/test/Analysis/Checkers/WebKit/retain-ptr-ctor-adopt-use.mm
@@ -0,0 +1,98 @@
+// RUN: %clang_analyze_cc1 
-analyzer-checker=alpha.webkit.RetainPtrCtorAdoptChecker -verify %s
+
+#include "objc-mock-types.h"
+
+void basic_correct() {
+  auto ns1 = adoptNS([SomeObj alloc]);
+  auto ns2 = adoptNS([[SomeObj alloc] init]);
+  RetainPtr<SomeObj> ns3 = [ns1.get() next];
+  auto ns4 = adoptNS([ns3 mutableCopy]);
+  auto ns5 = adoptNS([ns3 copyWithValue:3]);
+  auto ns6 = retainPtr([ns3 next]);
+  CFMutableArrayRef cf1 = adoptCF(CFArrayCreateMutable(kCFAllocatorDefault, 
10));
+  auto cf2 = adoptCF(SecTaskCreateFromSelf(kCFAllocatorDefault));
+}
+
+CFMutableArrayRef provide_cf();
+
+void basic_wrong() {
+  RetainPtr<SomeObj> ns1 = [[SomeObj alloc] init];
+  // expected-warning@-1{{Incorrect use of RetainPtr constructor. The argument 
is +1 and results in a memory leak [alpha.webkit.RetainPtrCtorAdoptChecker]}}
+  auto ns2 = adoptNS([ns1.get() next]);
+  // expected-warning@-1{{Incorrect use of adoptNS. The argument is +0 and 
results in an use-after-free [alpha.webkit.RetainPtrCtorAdoptChecker]}}
+  RetainPtr<CFMutableArrayRef> cf1 = CFArrayCreateMutable(kCFAllocatorDefault, 
10);
+  // expected-warning@-1{{Incorrect use of RetainPtr constructor. The argument 
is +1 and results in a memory leak [alpha.webkit.RetainPtrCtorAdoptChecker]}}
+  RetainPtr<CFMutableArrayRef> cf2 = adoptCF(provide_cf());
+  // expected-warning@-1{{Incorrect use of adoptCF. The argument is +0 and 
results in an use-after-free [alpha.webkit.RetainPtrCtorAdoptChecker]}}
+  RetainPtr<CFTypeRef> cf3 = SecTaskCreateFromSelf(kCFAllocatorDefault);
+  // expected-warning@-1{{Incorrect use of RetainPtr constructor. The argument 
is +1 and results in a memory leak [alpha.webkit.RetainPtrCtorAdoptChecker]}}
+}
+
+RetainPtr<CVPixelBufferRef> cf_out_argument() {
+  auto surface = adoptCF(IOSurfaceCreate(nullptr));
+  CVPixelBufferRef rawBuffer = nullptr;
+  auto status = CVPixelBufferCreateWithIOSurface(kCFAllocatorDefault, 
surface.get(), nullptr, &rawBuffer);
+  return adoptCF(rawBuffer);
+}
+
+RetainPtr<SomeObj> return_nullptr() {
+  return nullptr;
+}
+
+RetainPtr<SomeObj> return_retainptr() {
+  RetainPtr<SomeObj> foo;
+  return foo;
+}
+
+CFTypeRef CopyValueForSomething();
+
+void cast_retainptr() {
+  RetainPtr<NSObject> foo;
+  RetainPtr<SomeObj> bar = static_cast<SomeObj*>(foo);
+
+  auto baz = adoptCF(CopyValueForSomething()).get();
+  RetainPtr<CFArrayRef> v = static_cast<CFArrayRef>(baz);
+}
+
+SomeObj* allocSomeObj();
+
+void adopt_retainptr() {
+  RetainPtr<NSObject> foo = adoptNS([[SomeObj alloc] init]);
+  auto bar = adoptNS([allocSomeObj() init]);
+}
+
+RetainPtr<CFArrayRef> return_arg(CFArrayRef arg) {
+  return arg;
+}
+
+class MemberInit {
+public:
+  MemberInit(CFMutableArrayRef array, NSString *str, CFRunLoopRef runLoop)
+    : m_array(array)
+    , m_str(str)
+    , m_runLoop(runLoop)
+  { }
+
+private:
+  RetainPtr<CFMutableArrayRef> m_array;
+  RetainPtr<NSString> m_str;
+  RetainPtr<CFRunLoopRef> m_runLoop;
+};
+void create_member_init() {
+  MemberInit init { CFArrayCreateMutable(kCFAllocatorDefault, 10), @"hello", 
CFRunLoopGetCurrent() };
+}
+
+RetainPtr<CFStringRef> cfstr() {
+  return CFSTR("");
+}
+
+template <typename CF, typename NS>
+static RetainPtr<NS> bridge_cast(RetainPtr<CF>&& ptr)
+{
+  return adoptNS((__bridge NSArray *)(ptr.leakRef()));
+}
+
+RetainPtr<CFArrayRef> create_cf_array();
+RetainPtr<id> return_bridge_cast() {
+  return bridge_cast<CFArrayRef, NSArray>(create_cf_array());
+}


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

Reply via email to