[clang] [alpha.webkit.ForwardDeclChecker] Add a new WebKit checker for forward declarations (PR #130554)

Ryosuke Niwa via cfe-commits cfe-commits at lists.llvm.org
Sun Mar 9 23:27:00 PDT 2025


https://github.com/rniwa created https://github.com/llvm/llvm-project/pull/130554

Add a new static analyzer which emits warnings for function call arguments, local variables, and member variables that are only forward declared. These forward declaration prevents other WebKit checkers from checking the safety of code.

>From d2edd9b5fd188476e59790c333497516933532bb Mon Sep 17 00:00:00 2001
From: Ryosuke Niwa <rniwa at webkit.org>
Date: Sun, 9 Mar 2025 23:06:33 -0700
Subject: [PATCH] [alpha.webkit.ForwardDeclChecker] Add a new WebKit checker
 for forward declarations

Add a new static analyzer which emits warnings for function call arguments, local variables,
and member variables that are only forward declared. These forward declaration prevents
other WebKit checkers from checking the safety of code.
---
 clang/docs/analyzer/checkers.rst              |  18 +
 .../clang/StaticAnalyzer/Checkers/Checkers.td |   4 +
 .../StaticAnalyzer/Checkers/CMakeLists.txt    |   1 +
 .../Checkers/WebKit/ForwardDeclChecker.cpp    | 337 ++++++++++++++++++
 .../Checkers/WebKit/forward-decl-checker.mm   | 136 +++++++
 .../Checkers/WebKit/mock-system-header.h      |  14 +
 .../Checkers/WebKit/objc-mock-types.h         | 178 ++++++++-
 7 files changed, 674 insertions(+), 14 deletions(-)
 create mode 100644 clang/lib/StaticAnalyzer/Checkers/WebKit/ForwardDeclChecker.cpp
 create mode 100644 clang/test/Analysis/Checkers/WebKit/forward-decl-checker.mm

diff --git a/clang/docs/analyzer/checkers.rst b/clang/docs/analyzer/checkers.rst
index c1eedb33e74d2..d3ea10c46ab83 100644
--- a/clang/docs/analyzer/checkers.rst
+++ b/clang/docs/analyzer/checkers.rst
@@ -3441,6 +3441,24 @@ Limitations:
 alpha.WebKit
 ^^^^^^^^^^^^
 
+alpha.webkit.ForwardDeclChecker
+"""""""""""""""""""""""""""""""
+Check for local variables, member variables, and function arguments that are forward declared.
+
+.. code-block:: cpp
+
+ struct Obj;
+ Obj* provide();
+
+ struct Foo {
+   Obj* ptr; // warn
+ };
+
+  void foo() {
+    Obj* obj = provide(); // warn
+    consume(obj); // warn
+  }
+
 .. _alpha-webkit-NoUncheckedPtrMemberChecker:
 
 alpha.webkit.MemoryUnsafeCastChecker
diff --git a/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td b/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td
index 410f841630660..de4d138cf4718 100644
--- a/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td
+++ b/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td
@@ -1758,6 +1758,10 @@ def UncountedLambdaCapturesChecker : Checker<"UncountedLambdaCapturesChecker">,
 
 let ParentPackage = WebKitAlpha in {
 
+def ForwardDeclChecker : Checker<"ForwardDeclChecker">,
+  HelpText<"Check for forward declared local or member variables and function arguments">,
+  Documentation<HasDocumentation>;
+
 def MemoryUnsafeCastChecker : Checker<"MemoryUnsafeCastChecker">,
   HelpText<"Check for memory unsafe casts from base type to derived type.">,
   Documentation<HasDocumentation>;
diff --git a/clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt b/clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt
index 5910043440987..3085b2031ec90 100644
--- a/clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt
+++ b/clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt
@@ -130,6 +130,7 @@ add_clang_library(clangStaticAnalyzerCheckers
   VirtualCallChecker.cpp
   WebKit/RawPtrRefMemberChecker.cpp
   WebKit/ASTUtils.cpp
+  WebKit/ForwardDeclChecker.cpp
   WebKit/MemoryUnsafeCastChecker.cpp
   WebKit/PtrTypesSemantics.cpp
   WebKit/RefCntblBaseVirtualDtorChecker.cpp
diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/ForwardDeclChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/ForwardDeclChecker.cpp
new file mode 100644
index 0000000000000..9b24a23edd262
--- /dev/null
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/ForwardDeclChecker.cpp
@@ -0,0 +1,337 @@
+//=======- ForwardDeclChecker.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/Decl.h"
+#include "clang/AST/DeclCXX.h"
+#include "clang/AST/RecursiveASTVisitor.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"
+#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
+#include "clang/StaticAnalyzer/Core/Checker.h"
+#include "llvm/ADT/DenseSet.h"
+#include "llvm/Support/SaveAndRestore.h"
+#include <optional>
+
+using namespace clang;
+using namespace ento;
+
+namespace {
+
+class ForwardDeclChecker
+    : public Checker<check::ASTDecl<TranslationUnitDecl>> {
+  BugType Bug;
+  mutable BugReporter *BR;
+  mutable RetainTypeChecker RTC;
+  mutable llvm::DenseSet<const Type *> SystemTypes;
+
+public:
+  ForwardDeclChecker()
+      : Bug(this, "Forward declared member or local variable or parameter",
+            "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> {
+      using Base = RecursiveASTVisitor<LocalVisitor>;
+
+      const ForwardDeclChecker *Checker;
+      Decl *DeclWithIssue{nullptr};
+
+      explicit LocalVisitor(const ForwardDeclChecker *Checker)
+          : Checker(Checker) {
+        assert(Checker);
+      }
+
+      bool shouldVisitTemplateInstantiations() const { return true; }
+      bool shouldVisitImplicitCode() const { return false; }
+
+      bool VisitTypedefDecl(TypedefDecl *TD) {
+        Checker->visitTypedef(TD);
+        return true;
+      }
+
+      bool VisitRecordDecl(const RecordDecl *RD) {
+        Checker->visitRecordDecl(RD, DeclWithIssue);
+        return true;
+      }
+
+      bool TraverseDecl(Decl *D) {
+        llvm::SaveAndRestore SavedDecl(DeclWithIssue);
+        if (D && (isa<FunctionDecl>(D) || isa<ObjCMethodDecl>(D)))
+          DeclWithIssue = D;
+        return Base::TraverseDecl(D);
+      }
+
+      bool VisitVarDecl(VarDecl *V) {
+        if (V->isLocalVarDecl())
+          Checker->visitVarDecl(V, DeclWithIssue);
+        return true;
+      }
+
+      bool VisitCallExpr(const CallExpr *CE) {
+        Checker->visitCallExpr(CE, DeclWithIssue);
+        return true;
+      }
+
+      bool VisitCXXConstructExpr(const CXXConstructExpr *CE) {
+        Checker->visitConstructExpr(CE, DeclWithIssue);
+        return true;
+      }
+
+      bool VisitObjCMessageExpr(const ObjCMessageExpr *ObjCMsgExpr) {
+        Checker->visitObjCMessageExpr(ObjCMsgExpr, DeclWithIssue);
+        return true;
+      }
+    };
+
+    LocalVisitor visitor(this);
+    RTC.visitTranslationUnitDecl(TUD);
+    visitor.TraverseDecl(const_cast<TranslationUnitDecl *>(TUD));
+  }
+  
+  void visitTypedef(const TypedefDecl* TD) const {
+    RTC.visitTypedef(TD);
+    auto QT = TD->getUnderlyingType().getCanonicalType();
+    if (BR->getSourceManager().isInSystemHeader(TD->getBeginLoc())) {
+      if (auto *Type = QT.getTypePtrOrNull(); Type && QT->isPointerType())
+        SystemTypes.insert(Type);
+    }
+  }
+
+  bool isUnknownType(QualType QT) const {
+    auto *Type = QT.getTypePtrOrNull();
+    if (!Type)
+      return false;
+    auto *CanonicalType = QT.getCanonicalType().getTypePtrOrNull();
+    auto PointeeQT = Type->getPointeeType();
+    auto *PointeeType = PointeeQT.getTypePtrOrNull();
+    if (!PointeeType)
+      return false;
+    auto *R = PointeeType->getAsCXXRecordDecl();
+    if (!R) // Forward declaration of a Objective-C interface is safe.
+      return false;
+    auto Name = R->getName();
+    return !R->hasDefinition() && !RTC.isUnretained(QT) &&
+           !SystemTypes.contains(CanonicalType) &&
+           !Name.starts_with("Opaque") &&
+           Name != "_NSZone";
+  }
+
+  void visitRecordDecl(const RecordDecl *RD, const Decl *DeclWithIssue) const {
+    if (!RD->isThisDeclarationADefinition())
+      return;
+
+    if (RD->isImplicit() || RD->isLambda())
+      return;
+
+    const auto RDLocation = RD->getLocation();
+    if (!RDLocation.isValid())
+      return;
+
+    const auto Kind = RD->getTagKind();
+    if (Kind != TagTypeKind::Struct && Kind != TagTypeKind::Class)
+      return;
+
+    if (BR->getSourceManager().isInSystemHeader(RDLocation))
+      return;
+
+    // Ref-counted smartpointers actually have raw-pointer to uncounted type as
+    // a member but we trust them to handle it correctly.
+    auto R = llvm::dyn_cast_or_null<CXXRecordDecl>(RD);
+    if (!R || isRefCounted(R) || isCheckedPtr(R) || isRetainPtr(R))
+      return;
+
+    for (auto *Member : RD->fields()) {
+      auto QT = Member->getType();
+      if (isUnknownType(QT)) {
+        SmallString<100> Buf;
+        llvm::raw_svector_ostream Os(Buf);
+
+        const std::string TypeName = QT.getAsString();
+        Os << "Member variable ";
+        printQuotedName(Os, Member);
+        Os << " uses a forward declared type '" << TypeName << "'";
+
+        const SourceLocation SrcLocToReport = Member->getBeginLoc();
+        PathDiagnosticLocation BSLoc(SrcLocToReport, BR->getSourceManager());
+        auto Report = std::make_unique<BasicBugReport>(Bug, Os.str(), BSLoc);
+        Report->addRange(Member->getSourceRange());
+        Report->setDeclWithIssue(DeclWithIssue);
+        BR->emitReport(std::move(Report));
+      }
+    }
+  }
+
+  void visitVarDecl(const VarDecl *V, const Decl *DeclWithIssue) const {
+    if (BR->getSourceManager().isInSystemHeader(V->getBeginLoc()))
+      return;
+
+    auto QT = V->getType();
+    if (!isUnknownType(QT))
+      return;
+
+    SmallString<100> Buf;
+    llvm::raw_svector_ostream Os(Buf);
+
+    const std::string TypeName = QT.getAsString();
+    Os << "Local variable ";
+    printQuotedQualifiedName(Os, V);
+    Os << " uses a forward declared type '" << TypeName << "'";
+
+    const SourceLocation SrcLocToReport = V->getBeginLoc();
+    PathDiagnosticLocation BSLoc(SrcLocToReport, BR->getSourceManager());
+    auto Report = std::make_unique<BasicBugReport>(Bug, Os.str(), BSLoc);
+    Report->addRange(V->getSourceRange());
+    Report->setDeclWithIssue(DeclWithIssue);
+    BR->emitReport(std::move(Report));
+  }
+
+  void visitCallExpr(const CallExpr *CE, const Decl *DeclWithIssue) const {
+    if (BR->getSourceManager().isInSystemHeader(CE->getExprLoc()))
+      return;
+
+    if (auto *F = CE->getDirectCallee()) {
+      // Skip the first argument for overloaded member operators (e. g. lambda
+      // or std::function call operator).
+      unsigned ArgIdx =
+          isa<CXXOperatorCallExpr>(CE) && isa_and_nonnull<CXXMethodDecl>(F);
+
+      for (auto P = F->param_begin();
+           P < F->param_end() && ArgIdx < CE->getNumArgs(); ++P, ++ArgIdx)
+        visitCallArg(CE->getArg(ArgIdx), *P, DeclWithIssue);
+    }
+  }
+
+  void visitConstructExpr(const CXXConstructExpr *CE,
+                          const Decl *DeclWithIssue) const {
+    if (BR->getSourceManager().isInSystemHeader(CE->getExprLoc()))
+      return;
+
+    if (auto *F = CE->getConstructor()) {
+      // Skip the first argument for overloaded member operators (e. g. lambda
+      // or std::function call operator).
+      unsigned ArgIdx =
+          isa<CXXOperatorCallExpr>(CE) && isa_and_nonnull<CXXMethodDecl>(F);
+
+      for (auto P = F->param_begin();
+           P < F->param_end() && ArgIdx < CE->getNumArgs(); ++P, ++ArgIdx)
+        visitCallArg(CE->getArg(ArgIdx), *P, DeclWithIssue);
+    }
+  }
+
+  void visitObjCMessageExpr(const ObjCMessageExpr *E,
+                            const Decl *DeclWithIssue) const {
+    if (BR->getSourceManager().isInSystemHeader(E->getExprLoc()))
+      return;
+
+    if (auto *Receiver = E->getInstanceReceiver()->IgnoreParenCasts()) {
+      if (isUnknownType(E->getReceiverType()))
+        reportUnknownRecieverType(Receiver, DeclWithIssue);
+    }
+
+    auto *MethodDecl = E->getMethodDecl();
+    if (!MethodDecl)
+      return;
+
+    auto ArgCount = E->getNumArgs();
+    for (unsigned i = 0; i < ArgCount && i < MethodDecl->param_size(); ++i)
+      visitCallArg(E->getArg(i), MethodDecl->getParamDecl(i), DeclWithIssue);
+  }
+
+  void visitCallArg(const Expr* Arg, const ParmVarDecl* Param,
+                    const Decl *DeclWithIssue) const {
+    auto *ArgExpr = Arg->IgnoreParenCasts();
+    if (auto *InnerCE = dyn_cast<CallExpr>(Arg)) {
+      auto *InnerCallee = InnerCE->getDirectCallee();
+      if (InnerCallee && InnerCallee->isInStdNamespace() &&
+          safeGetName(InnerCallee) == "move" && InnerCE->getNumArgs() == 1) {
+        ArgExpr = InnerCE->getArg(0);
+        if (ArgExpr)
+          ArgExpr = ArgExpr->IgnoreParenCasts();
+      }
+    }
+    if (isa<CXXNullPtrLiteralExpr>(ArgExpr) || isa<IntegerLiteral>(ArgExpr) ||
+        isa<CXXDefaultArgExpr>(ArgExpr))
+      return;
+    if (auto *DRE = dyn_cast<DeclRefExpr>(ArgExpr)) {
+      if (auto* ValDecl = DRE->getDecl()) {
+        if (isa<ParmVarDecl>(ValDecl))
+          return;
+      }
+    }
+
+    QualType ArgType = Param->getType();
+    if (!isUnknownType(ArgType))
+      return;
+
+    reportUnknownArgType(Arg, Param, DeclWithIssue);
+  }
+
+  void reportUnknownArgType(const Expr *CallArg, const ParmVarDecl *Param,
+                            const Decl *DeclWithIssue) const {
+    assert(CallArg);
+
+    SmallString<100> Buf;
+    llvm::raw_svector_ostream Os(Buf);
+
+    const std::string paramName = safeGetName(Param);
+    const std::string TypeName = Param->getType().getAsString();
+    Os << "Call argument";
+    if (!paramName.empty()) {
+      Os << " for parameter ";
+      printQuotedQualifiedName(Os, Param);
+    }
+    Os << " uses a forward declared type '" << TypeName << "'";
+
+    const SourceLocation SrcLocToReport = CallArg->getExprLoc();
+    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));
+  }
+
+  void reportUnknownRecieverType(const Expr* Receiver,
+                                 const Decl *DeclWithIssue) const {
+    assert(Receiver);
+
+    SmallString<100> Buf;
+    llvm::raw_svector_ostream Os(Buf);
+
+    const std::string TypeName = Receiver->getType().getAsString();
+    Os << "Receiver uses a forward declared type '" << TypeName << "'";
+
+    const SourceLocation SrcLocToReport = Receiver->getExprLoc();
+    PathDiagnosticLocation BSLoc(SrcLocToReport, BR->getSourceManager());
+    auto Report = std::make_unique<BasicBugReport>(Bug, Os.str(), BSLoc);
+    Report->addRange(Receiver->getSourceRange());
+    Report->setDeclWithIssue(DeclWithIssue);
+    BR->emitReport(std::move(Report));
+  }
+};
+
+} // namespace
+
+void ento::registerForwardDeclChecker(CheckerManager &Mgr) {
+  Mgr.registerChecker<ForwardDeclChecker>();
+}
+
+bool ento::shouldRegisterForwardDeclChecker(const CheckerManager &) {
+  return true;
+}
diff --git a/clang/test/Analysis/Checkers/WebKit/forward-decl-checker.mm b/clang/test/Analysis/Checkers/WebKit/forward-decl-checker.mm
new file mode 100644
index 0000000000000..151cbe2affa92
--- /dev/null
+++ b/clang/test/Analysis/Checkers/WebKit/forward-decl-checker.mm
@@ -0,0 +1,136 @@
+// RUN: %clang_analyze_cc1 -analyzer-checker=alpha.webkit.ForwardDeclChecker -verify %s
+
+#include "mock-types.h"
+#include "objc-mock-types.h"
+#include "mock-system-header.h"
+
+namespace std {
+
+template <typename T> struct remove_reference {
+  typedef T type;
+};
+
+template <typename T> struct remove_reference<T&> {
+  typedef T type;
+};
+
+template<typename T> typename remove_reference<T>::type&& move(T&& t);
+
+} // namespace std
+
+typedef struct OpaqueJSString * JSStringRef;
+
+class Obj;
+ at class ObjCObj;
+
+Obj* provide_obj_ptr();
+void receive_obj_ptr(Obj* p = nullptr);
+
+Obj* ptr(Obj* arg) {
+  receive_obj_ptr(provide_obj_ptr());
+  // expected-warning at -1{{Call argument for parameter 'p' uses a forward declared type 'Obj *'}}
+  auto *obj = provide_obj_ptr();
+  // expected-warning at -1{{Local variable 'obj' uses a forward declared type 'Obj *'}}
+  receive_obj_ptr(arg);
+  receive_obj_ptr(nullptr);
+  receive_obj_ptr();
+  return obj;
+}
+
+Obj& provide_obj_ref();
+void receive_obj_ref(Obj& p);
+
+Obj& ref() {
+  receive_obj_ref(provide_obj_ref());
+  // expected-warning at -1{{Call argument for parameter 'p' uses a forward declared type 'Obj &'}}
+  auto &obj = provide_obj_ref();
+  // expected-warning at -1{{Local variable 'obj' uses a forward declared type 'Obj &'}}
+  return obj;
+}
+
+Obj&& provide_obj_rval();
+void receive_obj_rval(Obj&& p);
+
+void rval(Obj&& arg) {
+  receive_obj_rval(provide_obj_rval());
+  // expected-warning at -1{{Call argument for parameter 'p' uses a forward declared type 'Obj &&'}}
+  auto &&obj = provide_obj_rval();
+  // expected-warning at -1{{Local variable 'obj' uses a forward declared type 'Obj &&'}}
+  receive_obj_rval(std::move(arg));
+}
+
+ObjCObj *provide_objcobj();
+void receive_objcobj(ObjCObj *p);
+ObjCObj *objc_ptr() {
+  receive_objcobj(provide_objcobj());
+  auto *objcobj = provide_objcobj();
+  return objcobj;
+}
+
+struct WrapperObj {
+  Obj* ptr { nullptr };
+  // expected-warning at -1{{Member variable 'ptr' uses a forward declared type 'Obj *'}}
+
+  WrapperObj(Obj* obj);
+  WrapperObj(Obj& obj);
+  WrapperObj(Obj&& obj);
+};
+
+void construct_ptr(Obj&& arg) {
+  WrapperObj wrapper1(provide_obj_ptr());
+  // expected-warning at -1{{Call argument for parameter 'obj' uses a forward declared type 'Obj *'}}
+  WrapperObj wrapper2(provide_obj_ref());
+  // expected-warning at -1{{Call argument for parameter 'obj' uses a forward declared type 'Obj &'}}
+  WrapperObj wrapper3(std::move(arg));
+}
+
+JSStringRef provide_opaque_ptr();
+void receive_opaque_ptr(JSStringRef);
+NSZone *provide_zone();
+
+JSStringRef opaque_ptr() {
+  receive_opaque_ptr(provide_opaque_ptr());
+  auto ref = provide_opaque_ptr();
+  return ref;
+}
+
+ at interface AnotherObj : NSObject
+- (Obj *)ptr;
+- (Obj &)ref;
+- (void)objc;
+- (void)doMoreWork:(ObjCObj *)obj;
+ at end
+
+ at implementation AnotherObj
+- (Obj *)ptr {
+  receive_obj_ptr(provide_obj_ptr());
+  // expected-warning at -1{{Call argument for parameter 'p' uses a forward declared type 'Obj *'}}
+  auto *obj = provide_obj_ptr();
+  // expected-warning at -1{{Local variable 'obj' uses a forward declared type 'Obj *'}}
+  return obj;
+}
+
+- (Obj &)ref {
+  receive_obj_ref(provide_obj_ref());
+  // expected-warning at -1{{Call argument for parameter 'p' uses a forward declared type 'Obj &'}}
+  auto &obj = provide_obj_ref();
+  // expected-warning at -1{{Local variable 'obj' uses a forward declared type 'Obj &'}}
+  return obj;
+}
+
+- (void)objc {
+  auto *obj = provide_objcobj();
+  [obj doWork];
+  [self doMoreWork:provide_objcobj()];
+  [self doMoreWork:nil];
+}
+
+- (void)doMoreWork:(ObjCObj *)obj {
+  auto array = CFArrayCreateMutable(kCFAllocatorDefault, 10);
+  CFArrayAppendValue(array, nullptr);
+  auto log = os_log_create("Foo", "Bar");
+  os_log_msg(log, OS_LOG_TYPE_DEFAULT, "Some Log");
+  auto *zone = provide_zone();
+}
+
+ at end
diff --git a/clang/test/Analysis/Checkers/WebKit/mock-system-header.h b/clang/test/Analysis/Checkers/WebKit/mock-system-header.h
index a1d30957b19cb..73d6e3dbf4643 100644
--- a/clang/test/Analysis/Checkers/WebKit/mock-system-header.h
+++ b/clang/test/Analysis/Checkers/WebKit/mock-system-header.h
@@ -15,3 +15,17 @@ template <typename T>
 struct MemberVariable {
     T* obj { nullptr };
 };
+
+typedef unsigned char uint8_t;
+
+enum os_log_type_t : uint8_t {
+    OS_LOG_TYPE_DEFAULT = 0x00,
+    OS_LOG_TYPE_INFO = 0x01,
+    OS_LOG_TYPE_DEBUG = 0x02,
+    OS_LOG_TYPE_ERROR = 0x10,
+    OS_LOG_TYPE_FAULT = 0x11,
+};
+
+typedef struct os_log_s *os_log_t;
+os_log_t os_log_create(const char *subsystem, const char *category);
+void os_log_msg(os_log_t oslog, os_log_type_t type, const char *msg);
diff --git a/clang/test/Analysis/Checkers/WebKit/objc-mock-types.h b/clang/test/Analysis/Checkers/WebKit/objc-mock-types.h
index 7bb33bcb6cf44..ee165cdf7aa97 100644
--- a/clang/test/Analysis/Checkers/WebKit/objc-mock-types.h
+++ b/clang/test/Analysis/Checkers/WebKit/objc-mock-types.h
@@ -1,30 +1,126 @@
 @class NSString;
 @class NSArray;
 @class NSMutableArray;
+#define nil ((id)0)
 #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 char BOOL;
+typedef unsigned char Boolean;
 typedef signed long CFIndex;
-typedef const struct __CFAllocator * CFAllocatorRef;
+typedef unsigned long NSUInteger;
+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;
+#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);
 extern void CFRelease(CFTypeRef cf);
+#define CFSTR(cStr) ((CFStringRef) __builtin___CFStringMakeConstantString ("" cStr ""))
+extern Class NSClassFromString(NSString *aClassName);
 
 __attribute__((objc_root_class))
 @interface NSObject
 + (instancetype) alloc;
++ (Class) class;
++ (Class) superclass;
 - (instancetype) init;
 - (instancetype)retain;
 - (void)release;
+- (BOOL)isKindOfClass:(Class)aClass;
+ at end
+
+ at protocol NSCopying
+- (id)copyWithZone:(NSZone *)zone;
+ at end
+
+ at protocol NSFastEnumeration
+- (int)countByEnumeratingWithState:(void *)state objects:(id *)objects count:(unsigned)count;
+- (void)protocolMethod;
+ at end
+
+ at interface NSEnumerator <NSFastEnumeration>
+ at end
+
+ at interface NSDictionary : NSObject <NSCopying>
+- (NSUInteger)count;
+- (id)objectForKey:(id)aKey;
+- (id)objectForKeyedSubscript:(id)aKey;
+- (NSEnumerator *)keyEnumerator;
++ (id)dictionary;
++ (id)dictionaryWithObject:(id)object forKey:(id <NSCopying>)key;
++ (instancetype)dictionaryWithObjects:(const id [])objects forKeys:(const id <NSCopying> [])keys count:(NSUInteger)cnt;
+ at end
+
+ at interface NSArray : NSObject <NSCopying, NSFastEnumeration>
+- (NSUInteger)count;
+- (NSEnumerator *)objectEnumerator;
++ (NSArray *)arrayWithObjects:(const id [])objects count:(NSUInteger)count;
+ at end
+
+ at interface NSString : NSObject <NSCopying>
+- (NSUInteger)length;
+- (NSString *)stringByAppendingString:(NSString *)aString;
+- ( const char *)UTF8String;
+- (id)initWithUTF8String:(const char *)nullTerminatedCString;
++ (id)stringWithUTF8String:(const char *)nullTerminatedCString;
+ at end
+
+ at interface NSMutableString : NSString
+ at end
+
+ at interface NSValue : NSObject <NSCopying>
+- (void)getValue:(void *)value;
+ at end
+
+ at interface NSNumber : NSValue
+- (char)charValue;
+- (id)initWithInt:(int)value;
++ (NSNumber *)numberWithInt:(int)value;
 @end
 
 @interface SomeObj : NSObject
+- (SomeObj *)mutableCopy;
+- (SomeObj *)copyWithValue:(int)value;
 - (void)doWork;
 - (SomeObj *)other;
 - (SomeObj *)next;
@@ -57,28 +153,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 +188,7 @@ template <typename T> struct RetainPtr {
   }
   void clear() {
     if (t)
-      CFRelease(t);
+      CFRelease(toCFTypeRef(t));
     t = nullptr;
   }
   void swap(RetainPtr& o) {
@@ -102,10 +204,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 +224,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 +251,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;



More information about the cfe-commits mailing list