[clang] [alpha.webkit.webkit.RetainPtrCtorAdoptChecker] Add a new WebKit checker for correct use of RetainPtr, adoptNS, and adoptCF (PR #128679)
Ryosuke Niwa via cfe-commits
cfe-commits at lists.llvm.org
Tue Feb 25 09:59:20 PST 2025
https://github.com/rniwa updated https://github.com/llvm/llvm-project/pull/128679
>From b4001f95cf6d35f59ef8af6df8f2bdbe043da380 Mon Sep 17 00:00:00 2001
From: Ryosuke Niwa <rniwa at webkit.org>
Date: Tue, 25 Feb 2025 00:47:45 -0800
Subject: [PATCH 1/3] [alpha.webkit.webkit.RetainPtrCtorAdoptChecker] Add a new
WebKit checker for correct use of RetainPtr, adoptNS, and adoptCF
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.
---
clang/docs/analyzer/checkers.rst | 20 +
.../clang/StaticAnalyzer/Checkers/Checkers.td | 4 +
.../StaticAnalyzer/Checkers/CMakeLists.txt | 1 +
.../Checkers/WebKit/PtrTypesSemantics.cpp | 7 +-
.../Checkers/WebKit/PtrTypesSemantics.h | 3 +-
.../WebKit/RetainPtrCtorAdoptChecker.cpp | 347 ++++++++++++++++++
.../Checkers/WebKit/objc-mock-types.h | 146 +++++++-
.../WebKit/retain-ptr-ctor-adopt-use-arc.mm | 85 +++++
.../WebKit/retain-ptr-ctor-adopt-use.mm | 85 +++++
9 files changed, 680 insertions(+), 18 deletions(-)
create mode 100644 clang/lib/StaticAnalyzer/Checkers/WebKit/RetainPtrCtorAdoptChecker.cpp
create mode 100644 clang/test/Analysis/Checkers/WebKit/retain-ptr-ctor-adopt-use-arc.mm
create mode 100644 clang/test/Analysis/Checkers/WebKit/retain-ptr-ctor-adopt-use.mm
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;
+- (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 +121,34 @@ template <typename T> struct RetainPtr {
PtrType t;
RetainPtr() : t(nullptr) { }
-
RetainPtr(PtrType t)
: t(t) {
if (t)
- CFRetain(t);
+ CFRetain(toCFTypeRef(t));
}
- RetainPtr(RetainPtr&& o)
- : RetainPtr(o.t)
- {
- o.t = nullptr;
- }
- 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 +156,7 @@ template <typename T> struct RetainPtr {
}
void clear() {
if (t)
- CFRelease(t);
+ CFRelease(toCFTypeRef(t));
t = nullptr;
}
void swap(RetainPtr& o) {
@@ -102,10 +172,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 +192,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 +219,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;
\ No newline at end of file
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..5d0c64e0f44b6
--- /dev/null
+++ b/clang/test/Analysis/Checkers/WebKit/retain-ptr-ctor-adopt-use-arc.mm
@@ -0,0 +1,85 @@
+// 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));
+}
+
+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<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);
+}
+
+void adopt_retainptr() {
+ RetainPtr<NSObject> foo = adoptNS([[SomeObj alloc] 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..20b9e2d485c95
--- /dev/null
+++ b/clang/test/Analysis/Checkers/WebKit/retain-ptr-ctor-adopt-use.mm
@@ -0,0 +1,85 @@
+// 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));
+}
+
+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<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);
+}
+
+void adopt_retainptr() {
+ RetainPtr<NSObject> foo = adoptNS([[SomeObj alloc] 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());
+}
>From 1d24671b5742059b484a4c8f755a1ee645446499 Mon Sep 17 00:00:00 2001
From: Ryosuke Niwa <rniwa at webkit.org>
Date: Tue, 25 Feb 2025 01:11:53 -0800
Subject: [PATCH 2/3] Fix formatting.
---
.../WebKit/RetainPtrCtorAdoptChecker.cpp | 89 +++++++++----------
1 file changed, 44 insertions(+), 45 deletions(-)
diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/RetainPtrCtorAdoptChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/RetainPtrCtorAdoptChecker.cpp
index 8727f89826807..fedf8ea30cf8d 100644
--- a/clang/lib/StaticAnalyzer/Checkers/WebKit/RetainPtrCtorAdoptChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/RetainPtrCtorAdoptChecker.cpp
@@ -1,4 +1,4 @@
-//=======- RefCntblBaseVirtualDtor.cpp ---------------------------*- C++ -*-==//
+//=======- 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.
@@ -12,12 +12,12 @@
#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 "clang/Analysis/RetainSummaryManager.h"
#include "llvm/ADT/DenseSet.h"
#include "llvm/ADT/SetVector.h"
#include <optional>
@@ -37,8 +37,7 @@ class RetainPtrCtorAdoptChecker
public:
RetainPtrCtorAdoptChecker()
- : Bug(this,
- "Correct use of RetainPtr, adoptNS, and adoptCF",
+ : Bug(this, "Correct use of RetainPtr, adoptNS, and adoptCF",
"WebKit coding guidelines") {}
void checkASTDecl(const TranslationUnitDecl *TUD, AnalysisManager &MGR,
@@ -68,7 +67,7 @@ class RetainPtrCtorAdoptChecker
DeclWithIssue = D;
return Base::TraverseDecl(D);
}
-
+
bool TraverseClassTemplateDecl(ClassTemplateDecl *CTD) {
if (safeGetName(CTD) == "RetainPtr")
return true; // Skip the contents of RetainPtr.
@@ -95,8 +94,9 @@ class RetainPtrCtorAdoptChecker
};
LocalVisitor visitor(this);
- Summaries = std::make_unique<RetainSummaryManager>(TUD->getASTContext(),
- true /* trackObjCAndCFObjects */, false /* trackOSObjects */);
+ Summaries = std::make_unique<RetainSummaryManager>(
+ TUD->getASTContext(), true /* trackObjCAndCFObjects */,
+ false /* trackOSObjects */);
RTC.visitTranslationUnitDecl(TUD);
visitor.TraverseDecl(const_cast<TranslationUnitDecl *>(TUD));
}
@@ -112,7 +112,7 @@ class RetainPtrCtorAdoptChecker
return Name == "adoptNS" || Name == "adoptNSArc";
}
- void visitCallExpr(const CallExpr *CE, const Decl* DeclWithIssue) const {
+ void visitCallExpr(const CallExpr *CE, const Decl *DeclWithIssue) const {
if (BR->getSourceManager().isInSystemHeader(CE->getExprLoc()))
return;
@@ -138,7 +138,7 @@ class RetainPtrCtorAdoptChecker
}
void visitConstructExpr(const CXXConstructExpr *CE,
- const Decl* DeclWithIssue) const {
+ const Decl *DeclWithIssue) const {
if (BR->getSourceManager().isInSystemHeader(CE->getExprLoc()))
return;
@@ -215,20 +215,20 @@ class RetainPtrCtorAdoptChecker
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;
+ 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)) {
@@ -262,16 +262,16 @@ class RetainPtrCtorAdoptChecker
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;
+ 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;
@@ -280,8 +280,8 @@ class RetainPtrCtorAdoptChecker
}
template <typename ExprType>
- void reportUnknown(std::string& Name, const ExprType *CE,
- const Decl* DeclWithIssue) const {
+ void reportUnknown(std::string &Name, const ExprType *CE,
+ const Decl *DeclWithIssue) const {
SmallString<100> Buf;
llvm::raw_svector_ostream Os(Buf);
@@ -295,9 +295,9 @@ class RetainPtrCtorAdoptChecker
BR->emitReport(std::move(Report));
}
- void reportUseAfterFree(std::string& Name, const CallExpr *CE,
- const Decl* DeclWithIssue,
- const char* condition = nullptr) const {
+ 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);
@@ -315,14 +315,14 @@ class RetainPtrCtorAdoptChecker
BR->emitReport(std::move(Report));
}
- void reportLeak(std::string& Name, const CXXConstructExpr *CE,
- const Decl* DeclWithIssue,
- const char* condition = nullptr) const {
+ 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";
+ Os << "Incorrect use of " << Name
+ << ". The argument is +1 and results in a memory leak";
if (condition)
Os << " " << condition;
Os << ".";
@@ -341,7 +341,6 @@ void ento::registerRetainPtrCtorAdoptChecker(CheckerManager &Mgr) {
Mgr.registerChecker<RetainPtrCtorAdoptChecker>();
}
-bool ento::shouldRegisterRetainPtrCtorAdoptChecker(
- const CheckerManager &mgr) {
+bool ento::shouldRegisterRetainPtrCtorAdoptChecker(const CheckerManager &mgr) {
return true;
}
>From 8f8b0b3115a48cf063563fd467d98dc88a60d367 Mon Sep 17 00:00:00 2001
From: Ryosuke Niwa <rniwa at webkit.org>
Date: Tue, 25 Feb 2025 09:59:01 -0800
Subject: [PATCH 3/3] Fix formatting 2.
---
.../Checkers/WebKit/RetainPtrCtorAdoptChecker.cpp | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/RetainPtrCtorAdoptChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/RetainPtrCtorAdoptChecker.cpp
index fedf8ea30cf8d..a97e497ad315d 100644
--- a/clang/lib/StaticAnalyzer/Checkers/WebKit/RetainPtrCtorAdoptChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/RetainPtrCtorAdoptChecker.cpp
@@ -301,8 +301,8 @@ class RetainPtrCtorAdoptChecker
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";
+ Os << "Incorrect use of " << Name
+ << ". The argument is +0 and results in an use-after-free";
if (condition)
Os << " " << condition;
Os << ".";
More information about the cfe-commits
mailing list