https://github.com/rniwa updated https://github.com/llvm/llvm-project/pull/132316
>From 642057c409b1c3b98ee4ecb16e95b5fb5be47a01 Mon Sep 17 00:00:00 2001 From: Ryosuke Niwa <rn...@webkit.org> Date: Thu, 20 Mar 2025 17:54:22 -0700 Subject: [PATCH 1/3] [alpha.webkit.RetainPtrCtorAdoptChecker] Support adopt(cast(copy(~)) This PR adds the support for recognizing calling adoptCF/adoptNS on the result of a cast operation on the return value of a function which creates NS or CF types. It also fixes a bug that we weren't reporting memory leaks when CF types are created without ever calling RetainPtr's constructor, adoptCF, or adoptNS. To do this, this PR adds a new mechanism to report a memory leak whenever create or copy CF functions are invoked unless this CallExpr has already been visited while validating a call to adoptCF. Also added an early exit when isOwned returns IsOwnedResult::Skip due to an unresolved template argument. --- .../WebKit/RetainPtrCtorAdoptChecker.cpp | 70 ++++++++++++++---- .../Checkers/WebKit/objc-mock-types.h | 72 ++++++++++++++++++- .../WebKit/retain-ptr-ctor-adopt-use-arc.mm | 9 ++- .../WebKit/retain-ptr-ctor-adopt-use.mm | 9 ++- 4 files changed, 140 insertions(+), 20 deletions(-) diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/RetainPtrCtorAdoptChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/RetainPtrCtorAdoptChecker.cpp index 4ce3262e90e13..2d9620cc8ee5e 100644 --- a/clang/lib/StaticAnalyzer/Checkers/WebKit/RetainPtrCtorAdoptChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/RetainPtrCtorAdoptChecker.cpp @@ -34,6 +34,7 @@ class RetainPtrCtorAdoptChecker mutable BugReporter *BR; mutable std::unique_ptr<RetainSummaryManager> Summaries; mutable llvm::DenseSet<const ValueDecl *> CreateOrCopyOutArguments; + mutable llvm::DenseSet<const Expr *> CreateOrCopyFnCall; mutable RetainTypeChecker RTC; public: @@ -119,7 +120,7 @@ class RetainPtrCtorAdoptChecker return; if (!isAdoptFn(F) || !CE->getNumArgs()) { - rememberOutArguments(CE, F); + checkCreateOrCopyFunction(CE, F, DeclWithIssue); return; } @@ -128,24 +129,30 @@ class RetainPtrCtorAdoptChecker 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); + if (isAllocInit(Arg) || isCreateOrCopy(Arg)) { + CreateOrCopyFnCall.insert(Arg); // Avoid double reporting. + return; + } + if (Result == IsOwnedResult::Owned || Result == IsOwnedResult::Skip) + return; + + 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 { + void checkCreateOrCopyFunction(const CallExpr *CE, + const FunctionDecl *Callee, + const Decl *DeclWithIssue) const { if (!isCreateOrCopyFunction(Callee)) return; + bool hasOutArgument = false; unsigned ArgCount = CE->getNumArgs(); for (unsigned ArgIndex = 0; ArgIndex < ArgCount; ++ArgIndex) { auto *Arg = CE->getArg(ArgIndex)->IgnoreParenCasts(); @@ -164,7 +171,10 @@ class RetainPtrCtorAdoptChecker if (!Decl) continue; CreateOrCopyOutArguments.insert(Decl); + hasOutArgument = true; } + if (!hasOutArgument && !CreateOrCopyFnCall.contains(CE)) + reportLeak(CE, DeclWithIssue); } void visitConstructExpr(const CXXConstructExpr *CE, @@ -190,6 +200,13 @@ class RetainPtrCtorAdoptChecker std::string Name = "RetainPtr constructor"; auto *Arg = CE->getArg(0)->IgnoreParenCasts(); auto Result = isOwned(Arg); + + if (isCreateOrCopy(Arg)) + CreateOrCopyFnCall.insert(Arg); // Avoid double reporting. + + if (Result == IsOwnedResult::Skip) + return; + if (Result == IsOwnedResult::Unknown) Result = IsOwnedResult::NotOwned; if (Result == IsOwnedResult::Owned) @@ -303,11 +320,22 @@ class RetainPtrCtorAdoptChecker if (auto *Callee = CE->getDirectCallee()) { if (isAdoptFn(Callee)) return IsOwnedResult::NotOwned; - if (safeGetName(Callee) == "__builtin___CFStringMakeConstantString") + auto Name = safeGetName(Callee); + if (Name == "__builtin___CFStringMakeConstantString") return IsOwnedResult::NotOwned; + if ((Name == "checked_cf_cast" || Name == "dynamic_cf_cast" || + Name == "checked_objc_cast" || Name == "dynamic_objc_cast") && + CE->getNumArgs() == 1) { + E = CE->getArg(0)->IgnoreParenCasts(); + continue; + } auto RetType = Callee->getReturnType(); if (isRetainPtrType(RetType)) return IsOwnedResult::NotOwned; + if (isCreateOrCopyFunction(Callee)) { + CreateOrCopyFnCall.insert(CE); + return IsOwnedResult::Owned; + } } else if (auto *CalleeExpr = CE->getCallee()) { if (isa<CXXDependentScopeMemberExpr>(CalleeExpr)) return IsOwnedResult::Skip; // Wait for instantiation. @@ -371,6 +399,20 @@ class RetainPtrCtorAdoptChecker Report->setDeclWithIssue(DeclWithIssue); BR->emitReport(std::move(Report)); } + + void reportLeak(const CallExpr *CE, const Decl *DeclWithIssue) const { + SmallString<100> Buf; + llvm::raw_svector_ostream Os(Buf); + + Os << "The return value is +1 and results in a memory leak."; + + 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 diff --git a/clang/test/Analysis/Checkers/WebKit/objc-mock-types.h b/clang/test/Analysis/Checkers/WebKit/objc-mock-types.h index 5bd265596a0b4..ab5e820b2c636 100644 --- a/clang/test/Analysis/Checkers/WebKit/objc-mock-types.h +++ b/clang/test/Analysis/Checkers/WebKit/objc-mock-types.h @@ -21,6 +21,11 @@ typedef struct CF_BRIDGED_MUTABLE_TYPE(CFRunLoopRef) __CFRunLoop * CFRunLoopRef; extern const CFAllocatorRef kCFAllocatorDefault; typedef struct _NSZone NSZone; +typedef unsigned long CFTypeID; + +CFTypeID CFGetTypeID(CFTypeRef cf); + +CFTypeID CFArrayGetTypeID(void); CFMutableArrayRef CFArrayCreateMutable(CFAllocatorRef allocator, CFIndex capacity); extern void CFArrayAppendValue(CFMutableArrayRef theArray, const void *value); CFArrayRef CFArrayCreate(CFAllocatorRef allocator, const void **values, CFIndex numValues); @@ -29,6 +34,7 @@ CFIndex CFArrayGetCount(CFArrayRef theArray); typedef const struct CF_BRIDGED_TYPE(NSDictionary) __CFDictionary * CFDictionaryRef; typedef struct CF_BRIDGED_MUTABLE_TYPE(NSMutableDictionary) __CFDictionary * CFMutableDictionaryRef; +CFTypeID CFDictionaryGetTypeID(void); 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); @@ -135,6 +141,8 @@ __attribute__((objc_root_class)) namespace WTF { +void WTFCrash(void); + template<typename T> class RetainPtr; template<typename T> RetainPtr<T> adoptNS(T*); template<typename T> RetainPtr<T> adoptCF(T); @@ -265,19 +273,79 @@ template<typename T> inline RetainPtr<T> retainPtr(T* ptr) inline NSObject *bridge_cast(CFTypeRef object) { - return (__bridge NSObject *)object; + return (__bridge NSObject *)object; } inline CFTypeRef bridge_cast(NSObject *object) { - return (__bridge CFTypeRef)object; + return (__bridge CFTypeRef)object; +} + +template <typename> struct CFTypeTrait; + +// Use dynamic_cf_cast<> instead of checked_cf_cast<> when actively checking CF types, +// similar to dynamic_cast<> in C++. Be sure to include a nullptr check. + +template<typename T> T dynamic_cf_cast(CFTypeRef object) +{ + if (!object) + return nullptr; + + if (CFGetTypeID(object) != CFTypeTrait<T>::typeID()) + return nullptr; + + return static_cast<T>(const_cast<CF_BRIDGED_TYPE(id) void*>(object)); +} + +template<typename T, typename U> RetainPtr<T> dynamic_cf_cast(RetainPtr<U>&& object) +{ + if (!object) + return nullptr; + + if (CFGetTypeID(object.get()) != CFTypeTrait<T>::typeID()) + return nullptr; + + return adoptCF(static_cast<T>(const_cast<CF_BRIDGED_TYPE(id) void*>(object.leakRef()))); } +// Use checked_cf_cast<> instead of dynamic_cf_cast<> when a specific CF type is required. + +template<typename T> T checked_cf_cast(CFTypeRef object) +{ + if (!object) + return nullptr; + + if (CFGetTypeID(object) != CFTypeTrait<T>::typeID()) + WTFCrash(); + + return static_cast<T>(const_cast<CF_BRIDGED_TYPE(id) void*>(object)); } +} // namespace WTF + +#define WTF_DECLARE_CF_TYPE_TRAIT(ClassName) \ +template <> \ +struct WTF::CFTypeTrait<ClassName##Ref> { \ + static inline CFTypeID typeID(void) { return ClassName##GetTypeID(); } \ +}; + +WTF_DECLARE_CF_TYPE_TRAIT(CFArray); +WTF_DECLARE_CF_TYPE_TRAIT(CFDictionary); + +#define WTF_DECLARE_CF_MUTABLE_TYPE_TRAIT(ClassName, MutableClassName) \ +template <> \ +struct WTF::CFTypeTrait<MutableClassName##Ref> { \ + static inline CFTypeID typeID(void) { return ClassName##GetTypeID(); } \ +}; + +WTF_DECLARE_CF_MUTABLE_TYPE_TRAIT(CFArray, CFMutableArray); +WTF_DECLARE_CF_MUTABLE_TYPE_TRAIT(CFDictionary, CFMutableDictionary); + using WTF::RetainPtr; using WTF::adoptNS; using WTF::adoptCF; using WTF::retainPtr; using WTF::downcast; using WTF::bridge_cast; +using WTF::dynamic_cf_cast; +using WTF::checked_cf_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 index dd7208a534ea1..c2a93358b4cdd 100644 --- 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 @@ -3,6 +3,8 @@ #include "objc-mock-types.h" +CFTypeRef CFCopyArray(CFArrayRef); + void basic_correct() { auto ns1 = adoptNS([SomeObj alloc]); auto ns2 = adoptNS([[SomeObj alloc] init]); @@ -12,6 +14,7 @@ void basic_correct() { auto ns6 = retainPtr([ns3 next]); CFMutableArrayRef cf1 = adoptCF(CFArrayCreateMutable(kCFAllocatorDefault, 10)); auto cf2 = adoptCF(SecTaskCreateFromSelf(kCFAllocatorDefault)); + auto cf3 = adoptCF(checked_cf_cast<CFArrayRef>(CFCopyArray(cf1))); } CFMutableArrayRef provide_cf(); @@ -27,6 +30,8 @@ void basic_wrong() { // expected-warning@-1{{Incorrect use of adoptCF. The argument is +0 and results in an use-after-free [alpha.webkit.RetainPtrCtorAdoptChecker]}} RetainPtr<CFTypeRef> cf3 = SecTaskCreateFromSelf(kCFAllocatorDefault); // expected-warning@-1{{Incorrect use of RetainPtr constructor. The argument is +1 and results in a memory leak [alpha.webkit.RetainPtrCtorAdoptChecker]}} + CFCopyArray(cf1); + // expected-warning@-1{{The return value is +1 and results in a memory leak [alpha.webkit.RetainPtrCtorAdoptChecker]}} } RetainPtr<CVPixelBufferRef> cf_out_argument() { @@ -68,7 +73,7 @@ void adopt_retainptr() { class MemberInit { public: - MemberInit(CFMutableArrayRef array, NSString *str, CFRunLoopRef runLoop) + MemberInit(RetainPtr<CFMutableArrayRef>&& array, NSString *str, CFRunLoopRef runLoop) : m_array(array) , m_str(str) , m_runLoop(runLoop) @@ -80,7 +85,7 @@ void adopt_retainptr() { RetainPtr<CFRunLoopRef> m_runLoop; }; void create_member_init() { - MemberInit init { CFArrayCreateMutable(kCFAllocatorDefault, 10), @"hello", CFRunLoopGetCurrent() }; + MemberInit init { adoptCF(CFArrayCreateMutable(kCFAllocatorDefault, 10)), @"hello", CFRunLoopGetCurrent() }; } RetainPtr<CFStringRef> cfstr() { 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 index 79e0bdb7c577b..c6d2c1fddb9e8 100644 --- a/clang/test/Analysis/Checkers/WebKit/retain-ptr-ctor-adopt-use.mm +++ b/clang/test/Analysis/Checkers/WebKit/retain-ptr-ctor-adopt-use.mm @@ -3,6 +3,8 @@ #include "objc-mock-types.h" +CFTypeRef CFCopyArray(CFArrayRef); + void basic_correct() { auto ns1 = adoptNS([SomeObj alloc]); auto ns2 = adoptNS([[SomeObj alloc] init]); @@ -12,6 +14,7 @@ void basic_correct() { auto ns6 = retainPtr([ns3 next]); CFMutableArrayRef cf1 = adoptCF(CFArrayCreateMutable(kCFAllocatorDefault, 10)); auto cf2 = adoptCF(SecTaskCreateFromSelf(kCFAllocatorDefault)); + auto cf3 = adoptCF(checked_cf_cast<CFArrayRef>(CFCopyArray(cf1))); } CFMutableArrayRef provide_cf(); @@ -27,6 +30,8 @@ void basic_wrong() { // expected-warning@-1{{Incorrect use of adoptCF. The argument is +0 and results in an use-after-free [alpha.webkit.RetainPtrCtorAdoptChecker]}} RetainPtr<CFTypeRef> cf3 = SecTaskCreateFromSelf(kCFAllocatorDefault); // expected-warning@-1{{Incorrect use of RetainPtr constructor. The argument is +1 and results in a memory leak [alpha.webkit.RetainPtrCtorAdoptChecker]}} + CFCopyArray(cf1); + // expected-warning@-1{{The return value is +1 and results in a memory leak [alpha.webkit.RetainPtrCtorAdoptChecker]}} } RetainPtr<CVPixelBufferRef> cf_out_argument() { @@ -68,7 +73,7 @@ void adopt_retainptr() { class MemberInit { public: - MemberInit(CFMutableArrayRef array, NSString *str, CFRunLoopRef runLoop) + MemberInit(RetainPtr<CFMutableArrayRef>&& array, NSString *str, CFRunLoopRef runLoop) : m_array(array) , m_str(str) , m_runLoop(runLoop) @@ -80,7 +85,7 @@ void adopt_retainptr() { RetainPtr<CFRunLoopRef> m_runLoop; }; void create_member_init() { - MemberInit init { CFArrayCreateMutable(kCFAllocatorDefault, 10), @"hello", CFRunLoopGetCurrent() }; + MemberInit init { adoptCF(CFArrayCreateMutable(kCFAllocatorDefault, 10)), @"hello", CFRunLoopGetCurrent() }; } RetainPtr<CFStringRef> cfstr() { >From 9dde1db04ee04c005e9ee4476d861a07d3c34790 Mon Sep 17 00:00:00 2001 From: Ryosuke Niwa <rn...@webkit.org> Date: Thu, 20 Mar 2025 18:16:03 -0700 Subject: [PATCH 2/3] Fix formatting. --- .../Checkers/WebKit/RetainPtrCtorAdoptChecker.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/RetainPtrCtorAdoptChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/RetainPtrCtorAdoptChecker.cpp index 2d9620cc8ee5e..5f9d7b81f113b 100644 --- a/clang/lib/StaticAnalyzer/Checkers/WebKit/RetainPtrCtorAdoptChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/RetainPtrCtorAdoptChecker.cpp @@ -146,8 +146,7 @@ class RetainPtrCtorAdoptChecker reportUseAfterFree(Name, CE, DeclWithIssue); } - void checkCreateOrCopyFunction(const CallExpr *CE, - const FunctionDecl *Callee, + void checkCreateOrCopyFunction(const CallExpr *CE, const FunctionDecl *Callee, const Decl *DeclWithIssue) const { if (!isCreateOrCopyFunction(Callee)) return; @@ -203,7 +202,7 @@ class RetainPtrCtorAdoptChecker if (isCreateOrCopy(Arg)) CreateOrCopyFnCall.insert(Arg); // Avoid double reporting. - + if (Result == IsOwnedResult::Skip) return; >From 763bb3a390168fa8e13339228a82f00f369590be Mon Sep 17 00:00:00 2001 From: Ryosuke Niwa <rn...@webkit.org> Date: Fri, 21 Mar 2025 20:23:25 -0700 Subject: [PATCH 3/3] Fix a regression that alpha.webkit.RetainPtrCtorAdoptChecker emits a warning for non-retained types. This was caused by the checkCreateOrCopyFunction not checking RTC.isUnretained. Fixed this bug by adding the check. This in turn revealed a bug that we weren't properly recognizing CFTypeRef as the pointee type is "void" and therefore does not have a corresponding Record object. Added a new dense set, RecordlessTypes, to discover these types. We only use as a fallback since looping over ElaboratedType and checking its existence in the set is slower than just checking the presence of the canonical type in the CFPointees set. Finally, skip the analyses of smart pointer template classes in RawPtrRefCallArgsChecker and RawPtrRefLocalVarsChecker now that we properly recognize CFTypeRef as a CF type and this type is used internally inside the mock RetainPtr. --- .../Checkers/WebKit/PtrTypesSemantics.cpp | 28 ++++++++++++++++++- .../Checkers/WebKit/PtrTypesSemantics.h | 4 +++ .../WebKit/RawPtrRefCallArgsChecker.cpp | 2 +- .../WebKit/RawPtrRefLocalVarsChecker.cpp | 6 ++++ .../WebKit/RetainPtrCtorAdoptChecker.cpp | 2 ++ .../WebKit/retain-ptr-ctor-adopt-use.mm | 2 ++ 6 files changed, 42 insertions(+), 2 deletions(-) diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp index b4d2353a03cd2..1e9959371d2a6 100644 --- a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp @@ -125,6 +125,16 @@ bool isCheckedPtr(const std::string &Name) { return Name == "CheckedPtr" || Name == "CheckedRef"; } +bool isSmartPtrClass(const std::string &Name) { + return isRefType(Name) || isCheckedPtr(Name) || isRetainPtr(Name) || + Name == "WeakPtr" || Name == "WeakPtr" || Name == "WeakPtrFactory" || + Name == "WeakPtrFactoryWithBitField" || Name == "WeakPtrImplBase" || + Name == "WeakPtrImplBaseSingleThread" || Name == "ThreadSafeWeakPtr" || + Name == "ThreadSafeWeakOrStrongPtr" || + Name == "ThreadSafeWeakPtrControlBlock" || + Name == "ThreadSafeRefCountedAndCanMakeThreadSafeWeakPtr"; +} + bool isCtorOfRefCounted(const clang::FunctionDecl *F) { assert(F); const std::string &FunctionName = safeGetName(F); @@ -222,8 +232,13 @@ void RetainTypeChecker::visitTypedef(const TypedefDecl *TD) { auto PointeeQT = QT->getPointeeType(); const RecordType *RT = PointeeQT->getAs<RecordType>(); - if (!RT) + if (!RT) { + if (TD->hasAttr<ObjCBridgeAttr>() || TD->hasAttr<ObjCBridgeMutableAttr>()) { + if (auto *Type = TD->getTypeForDecl()) + RecordlessTypes.insert(Type); + } return; + } for (auto *Redecl : RT->getDecl()->getMostRecentDecl()->redecls()) { if (Redecl->getAttr<ObjCBridgeAttr>() || @@ -240,6 +255,17 @@ bool RetainTypeChecker::isUnretained(const QualType QT, bool ignoreARC) { auto CanonicalType = QT.getCanonicalType(); auto PointeeType = CanonicalType->getPointeeType(); auto *RT = dyn_cast_or_null<RecordType>(PointeeType.getTypePtrOrNull()); + if (!RT) { + auto *Type = QT.getTypePtrOrNull(); + while (Type) { + if (RecordlessTypes.contains(Type)) + return true; + auto *ET = dyn_cast_or_null<ElaboratedType>(Type); + if (!ET) + break; + Type = ET->desugar().getTypePtrOrNull(); + } + } return RT && CFPointees.contains(RT); } diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.h b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.h index 096675fb912f2..97c9d0510e67d 100644 --- a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.h +++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.h @@ -70,6 +70,7 @@ std::optional<bool> isUnchecked(const clang::QualType T); /// underlying pointer type. class RetainTypeChecker { llvm::DenseSet<const RecordType *> CFPointees; + llvm::DenseSet<const Type *> RecordlessTypes; bool IsARCEnabled{false}; public: @@ -135,6 +136,9 @@ bool isCheckedPtr(const std::string &Name); /// \returns true if \p Name is RetainPtr or its variant, false if not. bool isRetainPtr(const std::string &Name); +/// \returns true if \p Name is a smart pointer type name, false if not. +bool isSmartPtrClass(const std::string &Name); + /// \returns true if \p M is getter of a ref-counted class, false if not. std::optional<bool> isGetterOfSafePtr(const clang::CXXMethodDecl *Method); diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/RawPtrRefCallArgsChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/RawPtrRefCallArgsChecker.cpp index 39e9cd023d1f7..d21b2eedec8ac 100644 --- a/clang/lib/StaticAnalyzer/Checkers/WebKit/RawPtrRefCallArgsChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/RawPtrRefCallArgsChecker.cpp @@ -68,7 +68,7 @@ class RawPtrRefCallArgsChecker } bool TraverseClassTemplateDecl(ClassTemplateDecl *Decl) override { - if (isRefType(safeGetName(Decl))) + if (isSmartPtrClass(safeGetName(Decl))) return true; return DynamicRecursiveASTVisitor::TraverseClassTemplateDecl(Decl); } diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/RawPtrRefLocalVarsChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/RawPtrRefLocalVarsChecker.cpp index d413e33a490c5..845a7013e6037 100644 --- a/clang/lib/StaticAnalyzer/Checkers/WebKit/RawPtrRefLocalVarsChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/RawPtrRefLocalVarsChecker.cpp @@ -260,6 +260,12 @@ class RawPtrRefLocalVarsChecker return DynamicRecursiveASTVisitor::TraverseCompoundStmt(CS); return true; } + + bool TraverseClassTemplateDecl(ClassTemplateDecl *Decl) override { + if (isSmartPtrClass(safeGetName(Decl))) + return true; + return DynamicRecursiveASTVisitor::TraverseClassTemplateDecl(Decl); + } }; LocalVisitor visitor(this); diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/RetainPtrCtorAdoptChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/RetainPtrCtorAdoptChecker.cpp index 5f9d7b81f113b..bc6a5798c1c63 100644 --- a/clang/lib/StaticAnalyzer/Checkers/WebKit/RetainPtrCtorAdoptChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/RetainPtrCtorAdoptChecker.cpp @@ -172,6 +172,8 @@ class RetainPtrCtorAdoptChecker CreateOrCopyOutArguments.insert(Decl); hasOutArgument = true; } + if (!RTC.isUnretained(Callee->getReturnType())) + return; if (!hasOutArgument && !CreateOrCopyFnCall.contains(CE)) reportLeak(CE, DeclWithIssue); } 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 index c6d2c1fddb9e8..33cf98960f793 100644 --- a/clang/test/Analysis/Checkers/WebKit/retain-ptr-ctor-adopt-use.mm +++ b/clang/test/Analysis/Checkers/WebKit/retain-ptr-ctor-adopt-use.mm @@ -4,6 +4,7 @@ #include "objc-mock-types.h" CFTypeRef CFCopyArray(CFArrayRef); +void* CreateCopy(); void basic_correct() { auto ns1 = adoptNS([SomeObj alloc]); @@ -15,6 +16,7 @@ void basic_correct() { CFMutableArrayRef cf1 = adoptCF(CFArrayCreateMutable(kCFAllocatorDefault, 10)); auto cf2 = adoptCF(SecTaskCreateFromSelf(kCFAllocatorDefault)); auto cf3 = adoptCF(checked_cf_cast<CFArrayRef>(CFCopyArray(cf1))); + CreateCopy(); } CFMutableArrayRef provide_cf(); _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits