[clang] [alpha.webkit.webkit.RetainPtrCtorAdoptChecker] Add a new WebKit checker for correct use of RetainPtr, adoptNS, and adoptCF (PR #128679)
via cfe-commits
cfe-commits at lists.llvm.org
Tue Feb 25 01:01:12 PST 2025
llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-clang
Author: Ryosuke Niwa (rniwa)
<details>
<summary>Changes</summary>
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.
---
Patch is 29.52 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/128679.diff
9 Files Affected:
- (modified) clang/docs/analyzer/checkers.rst (+20)
- (modified) clang/include/clang/StaticAnalyzer/Checkers/Checkers.td (+4)
- (modified) clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt (+1)
- (modified) clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp (+4-3)
- (modified) clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.h (+2-1)
- (added) clang/lib/StaticAnalyzer/Checkers/WebKit/RetainPtrCtorAdoptChecker.cpp (+347)
- (modified) clang/test/Analysis/Checkers/WebKit/objc-mock-types.h (+132-14)
- (added) clang/test/Analysis/Checkers/WebKit/retain-ptr-ctor-adopt-use-arc.mm (+85)
- (added) clang/test/Analysis/Checkers/WebKit/retain-ptr-ctor-adopt-use.mm (+85)
``````````diff
diff --git a/clang/docs/analyzer/checkers.rst b/clang/docs/analyzer/checkers.rst
index c1eedb33e74d2..4cbd31f44d3f6 100644
--- a/clang/docs/analyzer/checkers.rst
+++ b/clang/docs/analyzer/checkers.rst
@@ -3713,6 +3713,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 RetinPtr 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 410f841630660..9aa696d8803b1 100644
--- a/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td
+++ b/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td
@@ -1786,4 +1786,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 5910043440987..0b6b169d7b447 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/UncountedLambdaCapturesChecker.cpp
WebKit/RawPtrRefLocalVarsChecker.cpp
diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
index 7899b19854806..7e7bd49ca0bdb 100644
--- a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
@@ -225,15 +225,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 fcc1a41dba78b..f69d374cbbf90 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..8727f89826807
--- /dev/null
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/RetainPtrCtorAdoptChecker.cpp
@@ -0,0 +1,347 @@
+//=======- RefCntblBaseVirtualDtor.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/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 "clang/Analysis/RetainSummaryManager.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 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;
+ }
+
+ llvm::SetVector<const CXXRecordDecl *> Decls;
+ llvm::DenseSet<const CXXRecordDecl *> CRTPs;
+ };
+
+ 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())
+ return;
+
+ auto *Arg = CE->getArg(0)->IgnoreParenCasts();
+ auto Result = isOwned(Arg);
+ auto Name = safeGetName(F);
+ if (Result == IsOwnedResult::Unknown)
+ reportUnknown(Name, CE, DeclWithIssue);
+ else if (Result == IsOwnedResult::NotOwned) {
+ if (RTC.isARCEnabled() && isAdoptNS(F)) {
+ if (!isAllocInit(Arg))
+ reportUseAfterFree(Name, CE, DeclWithIssue, "when ARC is disabled");
+ } else
+ reportUseAfterFree(Name, CE, DeclWithIssue);
+ }
+ }
+
+ 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)
+ reportUnknown(Name, CE, DeclWithIssue);
+ else if (Result == IsOwnedResult::Owned)
+ reportLeak(Name, CE, DeclWithIssue);
+ else if (RTC.isARCEnabled() && isAllocInit(Arg))
+ reportLeak(Name, CE, DeclWithIssue, "when ARC is disabled");
+ }
+
+ 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;
+ auto *InnerObjCMsgExpr = dyn_cast<ObjCMessageExpr>(Receiver);
+ if (!InnerObjCMsgExpr)
+ return false;
+ auto InnerSelector = InnerObjCMsgExpr->getSelector();
+ return InnerSelector.getNameForSlot(0) == "alloc";
+ }
+
+ 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;
+ }
+
+ template <typename ExprType>
+ void reportUnknown(std::string& Name, const ExprType *CE,
+ const Decl* DeclWithIssue) const {
+ SmallString<100> Buf;
+ llvm::raw_svector_ostream Os(Buf);
+
+ Os << Name << " is called on an object in unknown state.";
+
+ 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 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 7bb33bcb6cf44..9b13810d0c5c9 100644
--- a/clang/test/Analysis/Checkers/WebKit/objc-mock-types.h
+++ b/clang/test/Analysis/Checkers/WebKit/objc-mock-types.h
@@ -1,30 +1,94 @@
@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 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;
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);
+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;
+- (NS...
[truncated]
``````````
</details>
https://github.com/llvm/llvm-project/pull/128679
More information about the cfe-commits
mailing list