[clang] be9ca85 - [alpha.webkit.webkit.RetainPtrCtorAdoptChecker] Add a new WebKit checker for correct use of RetainPtr, adoptNS, and adoptCF (#128679)
via cfe-commits
cfe-commits at lists.llvm.org
Wed Mar 12 19:09:08 PDT 2025
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 at -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 at -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 at -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 at -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 at -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 at -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 at -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 at -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 at -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 at -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());
+}
More information about the cfe-commits
mailing list