[clang] [alpha.webkit.UnretainedCallArgsChecker] Add a checker for NS or CF type call arguments. (PR #128586)
Ryosuke Niwa via cfe-commits
cfe-commits at lists.llvm.org
Mon Feb 24 17:24:16 PST 2025
https://github.com/rniwa updated https://github.com/llvm/llvm-project/pull/128586
>From 90403f77295c0b3e4415263754e22b3c66f31876 Mon Sep 17 00:00:00 2001
From: Ryosuke Niwa <rniwa at webkit.org>
Date: Mon, 24 Feb 2025 13:43:04 -0800
Subject: [PATCH 1/5] [alpha.webkit.UnretainedCallArgsChecker] Add a checker
for NS or CF type call arguments.
This PR adds alpha.webkit.UnretainedCallArgsChecker by generalizing RawPtrRefLocalVarsChecker.
It checks call arguments of NS or CF types are backed by a RetainPtr or not. The new checker
is effective for NS and CF types in Objective-C++ code without ARC, and it's effective for
CF types in code with ARC.
---
clang/docs/analyzer/checkers.rst | 6 +
.../clang/StaticAnalyzer/Checkers/Checkers.td | 4 +
.../Checkers/WebKit/ASTUtils.cpp | 13 +
.../Checkers/WebKit/PtrTypesSemantics.cpp | 6 +-
.../WebKit/RawPtrRefCallArgsChecker.cpp | 114 +++++-
.../Checkers/WebKit/objc-mock-types.h | 89 ++++-
.../WebKit/unretained-call-args-arc.mm | 30 ++
.../Checkers/WebKit/unretained-call-args.mm | 342 ++++++++++++++++++
8 files changed, 584 insertions(+), 20 deletions(-)
create mode 100644 clang/test/Analysis/Checkers/WebKit/unretained-call-args-arc.mm
create mode 100644 clang/test/Analysis/Checkers/WebKit/unretained-call-args.mm
diff --git a/clang/docs/analyzer/checkers.rst b/clang/docs/analyzer/checkers.rst
index c1eedb33e74d2..c82d4114b38e2 100644
--- a/clang/docs/analyzer/checkers.rst
+++ b/clang/docs/analyzer/checkers.rst
@@ -3582,6 +3582,12 @@ The goal of this rule is to make sure that lifetime of any dynamically allocated
The rules of when to use and not to use CheckedPtr / CheckedRef are same as alpha.webkit.UncountedCallArgsChecker for ref-counted objects.
+alpha.webkit.UnretainedCallArgsChecker
+"""""""""""""""""""""""""""""""""""""
+The goal of this rule is to make sure that lifetime of any dynamically allocated NS or CF objects passed as a call argument keeps its memory region past the end of the call. This applies to call to any function, method, lambda, function pointer or functor. NS or CF objects aren't supposed to be allocated on stack so we check arguments for parameters of raw pointers and references to unretained types.
+
+The rules of when to use and not to use RetainPtr are same as alpha.webkit.UncountedCallArgsChecker for ref-counted objects.
+
alpha.webkit.UncountedLocalVarsChecker
""""""""""""""""""""""""""""""""""""""
The goal of this rule is to make sure that any uncounted local variable is backed by a ref-counted object with lifetime that is strictly larger than the scope of the uncounted local variable. To be on the safe side we require the scope of an uncounted variable to be embedded in the scope of ref-counted object that backs it.
diff --git a/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td b/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td
index 410f841630660..6aa006e705f80 100644
--- a/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td
+++ b/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td
@@ -1774,6 +1774,10 @@ def UncheckedCallArgsChecker : Checker<"UncheckedCallArgsChecker">,
HelpText<"Check unchecked call arguments.">,
Documentation<HasDocumentation>;
+def UnretainedCallArgsChecker : Checker<"UnretainedCallArgsChecker">,
+ HelpText<"Check unretained call arguments.">,
+ Documentation<HasDocumentation>;
+
def UncountedLocalVarsChecker : Checker<"UncountedLocalVarsChecker">,
HelpText<"Check uncounted local variables.">,
Documentation<HasDocumentation>;
diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp
index dc86c4fcc64b1..8de94ca9d4d91 100644
--- a/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp
@@ -8,6 +8,7 @@
#include "ASTUtils.h"
#include "PtrTypesSemantics.h"
+#include "clang/AST/Attr.h"
#include "clang/AST/CXXInheritance.h"
#include "clang/AST/Decl.h"
#include "clang/AST/DeclCXX.h"
@@ -28,6 +29,15 @@ bool tryToFindPtrOrigin(
std::function<bool(const clang::QualType)> isSafePtrType,
std::function<bool(const clang::Expr *, bool)> callback) {
while (E) {
+ if (auto *DRE = dyn_cast<DeclRefExpr>(E)) {
+ auto *ValDecl = DRE->getDecl();
+ auto QT = ValDecl->getType();
+ auto ValName = ValDecl->getName();
+ if (ValDecl && (ValName.starts_with('k') || ValName.starts_with("_k")) &&
+ QT.isConstQualified()) {
+ return callback(E, true);
+ }
+ }
if (auto *tempExpr = dyn_cast<MaterializeTemporaryExpr>(E)) {
E = tempExpr->getSubExpr();
continue;
@@ -117,6 +127,9 @@ bool tryToFindPtrOrigin(
E = call->getArg(0);
continue;
}
+
+ if (safeGetName(callee) == "__builtin___CFStringMakeConstantString")
+ return callback(E, true);
}
}
if (auto *ObjCMsgExpr = dyn_cast<ObjCMessageExpr>(E)) {
diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
index 7899b19854806..5edc244349a75 100644
--- a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
@@ -371,7 +371,8 @@ std::optional<bool> isGetterOfSafePtr(const CXXMethodDecl *M) {
if (auto *maybeRefToRawOperator = dyn_cast<CXXConversionDecl>(M)) {
auto QT = maybeRefToRawOperator->getConversionType();
auto *T = QT.getTypePtrOrNull();
- return T && (T->isPointerType() || T->isReferenceType());
+ return T && (T->isPointerType() || T->isReferenceType() ||
+ T->isObjCObjectPointerType());
}
}
}
@@ -414,7 +415,8 @@ bool isPtrConversion(const FunctionDecl *F) {
if (FunctionName == "getPtr" || FunctionName == "WeakPtr" ||
FunctionName == "dynamicDowncast" || FunctionName == "downcast" ||
FunctionName == "checkedDowncast" ||
- FunctionName == "uncheckedDowncast" || FunctionName == "bitwise_cast")
+ FunctionName == "uncheckedDowncast" || FunctionName == "bitwise_cast" ||
+ FunctionName == "bridge_cast")
return true;
return false;
diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/RawPtrRefCallArgsChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/RawPtrRefCallArgsChecker.cpp
index d633fbcbd798b..c6069ffa96696 100644
--- a/clang/lib/StaticAnalyzer/Checkers/WebKit/RawPtrRefCallArgsChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/RawPtrRefCallArgsChecker.cpp
@@ -13,6 +13,7 @@
#include "clang/AST/Decl.h"
#include "clang/AST/DeclCXX.h"
#include "clang/AST/DynamicRecursiveASTVisitor.h"
+#include "clang/Analysis/DomainSpecific/CocoaConventions.h"
#include "clang/Basic/SourceLocation.h"
#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
@@ -35,6 +36,9 @@ class RawPtrRefCallArgsChecker
TrivialFunctionAnalysis TFA;
EnsureFunctionAnalysis EFA;
+protected:
+ mutable std::optional<RetainTypeChecker> RTC;
+
public:
RawPtrRefCallArgsChecker(const char *description)
: Bug(this, description, "WebKit coding guidelines") {}
@@ -80,9 +84,22 @@ class RawPtrRefCallArgsChecker
Checker->visitCallExpr(CE, DeclWithIssue);
return true;
}
+
+ bool VisitTypedefDecl(TypedefDecl *TD) override {
+ if (Checker->RTC)
+ Checker->RTC->visitTypedef(TD);
+ return true;
+ }
+
+ bool VisitObjCMessageExpr(ObjCMessageExpr *ObjCMsgExpr) override {
+ Checker->visitObjCMessageExpr(ObjCMsgExpr, DeclWithIssue);
+ return true;
+ }
};
LocalVisitor visitor(this);
+ if (RTC)
+ RTC->visitTranslationUnitDecl(TUD);
visitor.TraverseDecl(const_cast<TranslationUnitDecl *>(TUD));
}
@@ -122,7 +139,7 @@ class RawPtrRefCallArgsChecker
// if ((*P)->hasAttr<SafeRefCntblRawPtrAttr>())
// continue;
- QualType ArgType = (*P)->getType().getCanonicalType();
+ QualType ArgType = (*P)->getType();
// FIXME: more complex types (arrays, references to raw pointers, etc)
std::optional<bool> IsUncounted = isUnsafePtr(ArgType);
if (!IsUncounted || !(*IsUncounted))
@@ -141,6 +158,42 @@ class RawPtrRefCallArgsChecker
}
}
+ void visitObjCMessageExpr(const ObjCMessageExpr *E, const Decl *D) const {
+ if (BR->getSourceManager().isInSystemHeader(E->getExprLoc()))
+ return;
+
+ auto Selector = E->getSelector();
+ if (auto *Receiver = E->getInstanceReceiver()->IgnoreParenCasts()) {
+ std::optional<bool> IsUnsafe = isUnsafePtr(E->getReceiverType());
+ if (IsUnsafe && *IsUnsafe && !isPtrOriginSafe(Receiver)) {
+ if (auto *InnerMsg = dyn_cast<ObjCMessageExpr>(Receiver)) {
+ auto InnerSelector = InnerMsg->getSelector();
+ if (InnerSelector.getNameForSlot(0) == "alloc"
+ && Selector.getNameForSlot(0).starts_with("init"))
+ return;
+ }
+ reportBugOnReceiver(Receiver, D);
+ }
+ }
+
+ auto *MethodDecl = E->getMethodDecl();
+ if (!MethodDecl)
+ return;
+
+ auto ArgCount = E->getNumArgs();
+ for (unsigned i = 0; i < ArgCount && i < MethodDecl->param_size(); ++i) {
+ auto *Arg = E->getArg(i);
+ auto *Param = MethodDecl->getParamDecl(i);
+ auto ArgType = Param->getType();
+ std::optional<bool> IsUnsafe = isUnsafePtr(ArgType);
+ if (!IsUnsafe || !(*IsUnsafe))
+ continue;
+ if (isPtrOriginSafe(Arg))
+ continue;
+ reportBug(Arg, Param, D);
+ }
+ }
+
bool isPtrOriginSafe(const Expr *Arg) const {
return tryToFindPtrOrigin(
Arg, /*StopAtFirstRefCountedObj=*/true,
@@ -158,6 +211,8 @@ class RawPtrRefCallArgsChecker
// foo(NULL)
return true;
}
+ if (isa<ObjCStringLiteral>(ArgOrigin))
+ return true;
if (isASafeCallArg(ArgOrigin))
return true;
if (EFA.isACallToEnsureFn(ArgOrigin))
@@ -212,7 +267,7 @@ class RawPtrRefCallArgsChecker
overloadedOperatorType == OO_PipePipe)
return true;
- if (isCtorOfRefCounted(Callee))
+ if (isCtorOfSafePtr(Callee))
return true;
auto name = safeGetName(Callee);
@@ -304,6 +359,22 @@ class RawPtrRefCallArgsChecker
Report->setDeclWithIssue(DeclWithIssue);
BR->emitReport(std::move(Report));
}
+
+ void reportBugOnReceiver(const Expr *CallArg, const Decl *DeclWithIssue) const {
+ assert(CallArg);
+
+ const SourceLocation SrcLocToReport = CallArg->getSourceRange().getBegin();
+
+ SmallString<100> Buf;
+ llvm::raw_svector_ostream Os(Buf);
+ Os << "Reciever is " << ptrKind() << " and unsafe.";
+
+ PathDiagnosticLocation BSLoc(SrcLocToReport, BR->getSourceManager());
+ auto Report = std::make_unique<BasicBugReport>(Bug, Os.str(), BSLoc);
+ Report->addRange(CallArg->getSourceRange());
+ Report->setDeclWithIssue(DeclWithIssue);
+ BR->emitReport(std::move(Report));
+ }
};
class UncountedCallArgsChecker final : public RawPtrRefCallArgsChecker {
@@ -317,7 +388,7 @@ class UncountedCallArgsChecker final : public RawPtrRefCallArgsChecker {
}
std::optional<bool> isUnsafePtr(QualType QT) const final {
- return isUncountedPtr(QT);
+ return isUncountedPtr(QT.getCanonicalType());
}
bool isSafePtr(const CXXRecordDecl *Record) const final {
@@ -342,7 +413,7 @@ class UncheckedCallArgsChecker final : public RawPtrRefCallArgsChecker {
}
std::optional<bool> isUnsafePtr(QualType QT) const final {
- return isUncheckedPtr(QT);
+ return isUncheckedPtr(QT.getCanonicalType());
}
bool isSafePtr(const CXXRecordDecl *Record) const final {
@@ -356,6 +427,33 @@ class UncheckedCallArgsChecker final : public RawPtrRefCallArgsChecker {
const char *ptrKind() const final { return "unchecked"; }
};
+class UnretainedCallArgsChecker final : public RawPtrRefCallArgsChecker {
+public:
+ UnretainedCallArgsChecker()
+ : RawPtrRefCallArgsChecker("Unretained call argument for a raw "
+ "pointer/reference parameter") {
+ RTC = RetainTypeChecker();
+ }
+
+ std::optional<bool> isUnsafeType(QualType QT) const final {
+ return RTC->isUnretained(QT);
+ }
+
+ std::optional<bool> isUnsafePtr(QualType QT) const final {
+ return RTC->isUnretained(QT);
+ }
+
+ bool isSafePtr(const CXXRecordDecl *Record) const final {
+ return isRetainPtr(Record);
+ }
+
+ bool isSafePtrType(const QualType type) const final {
+ return isRetainPtrType(type);
+ }
+
+ const char *ptrKind() const final { return "unretained"; }
+};
+
} // namespace
void ento::registerUncountedCallArgsChecker(CheckerManager &Mgr) {
@@ -373,3 +471,11 @@ void ento::registerUncheckedCallArgsChecker(CheckerManager &Mgr) {
bool ento::shouldRegisterUncheckedCallArgsChecker(const CheckerManager &) {
return true;
}
+
+void ento::registerUnretainedCallArgsChecker(CheckerManager &Mgr) {
+ Mgr.registerChecker<UnretainedCallArgsChecker>();
+}
+
+bool ento::shouldRegisterUnretainedCallArgsChecker(const CheckerManager &) {
+ 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..179003d563f4a 100644
--- a/clang/test/Analysis/Checkers/WebKit/objc-mock-types.h
+++ b/clang/test/Analysis/Checkers/WebKit/objc-mock-types.h
@@ -2,19 +2,24 @@
@class NSArray;
@class NSMutableArray;
#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 long CFIndex;
-typedef const struct __CFAllocator * CFAllocatorRef;
+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;
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 ""))
__attribute__((objc_root_class))
@interface NSObject
@@ -25,6 +30,8 @@ __attribute__((objc_root_class))
@end
@interface SomeObj : NSObject
+- (SomeObj *)mutableCopy;
+- (SomeObj *)copyWithValue:(int)value;
- (void)doWork;
- (SomeObj *)other;
- (SomeObj *)next;
@@ -57,28 +64,34 @@ template <typename T> struct RetainPtr {
PtrType t;
RetainPtr() : t(nullptr) { }
-
RetainPtr(PtrType t)
: t(t) {
if (t)
- CFRetain(t);
- }
- RetainPtr(RetainPtr&& o)
- : RetainPtr(o.t)
- {
- o.t = nullptr;
+ CFRetain(toCFTypeRef(t));
}
- RetainPtr(const RetainPtr& o)
+ RetainPtr(RetainPtr&&);
+ RetainPtr(const RetainPtr&);
+ template <typename U>
+ RetainPtr(const RetainPtr<U>& o)
: RetainPtr(o.t)
+ {}
+ RetainPtr operator=(const RetainPtr& o)
{
+ if (t)
+ CFRelease(toCFTypeRef(t));
+ t = o.t;
+ if (t)
+ CFRetain(toCFTypeRef(t));
+ return *this;
}
- RetainPtr operator=(const RetainPtr& o)
+ template <typename U>
+ RetainPtr operator=(const RetainPtr<U>& o)
{
if (t)
- CFRelease(t);
+ CFRelease(toCFTypeRef(t));
t = o.t;
if (t)
- CFRetain(t);
+ CFRetain(toCFTypeRef(t));
return *this;
}
~RetainPtr() {
@@ -86,7 +99,7 @@ template <typename T> struct RetainPtr {
}
void clear() {
if (t)
- CFRelease(t);
+ CFRelease(toCFTypeRef(t));
t = nullptr;
}
void swap(RetainPtr& o) {
@@ -102,10 +115,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 +135,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 +162,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;
diff --git a/clang/test/Analysis/Checkers/WebKit/unretained-call-args-arc.mm b/clang/test/Analysis/Checkers/WebKit/unretained-call-args-arc.mm
new file mode 100644
index 0000000000000..eb4735da60a05
--- /dev/null
+++ b/clang/test/Analysis/Checkers/WebKit/unretained-call-args-arc.mm
@@ -0,0 +1,30 @@
+// RUN: %clang_analyze_cc1 -analyzer-checker=alpha.webkit.UnretainedCallArgsChecker -fobjc-arc -verify %s
+
+#import "objc-mock-types.h"
+
+SomeObj *provide();
+CFMutableArrayRef provide_cf();
+void someFunction();
+
+namespace raw_ptr {
+
+void foo() {
+ [provide() doWork];
+ CFArrayAppendValue(provide_cf(), nullptr);
+ // expected-warning at -1{{Call argument for parameter 'theArray' is unretained and unsafe [alpha.webkit.UnretainedCallArgsChecker]}}
+}
+
+} // namespace raw_ptr
+
+ at interface AnotherObj : NSObject
+- (void)foo:(SomeObj *)obj;
+ at end
+
+ at implementation AnotherObj
+- (void)foo:(SomeObj*)obj {
+ [obj doWork];
+ [provide() doWork];
+ CFArrayAppendValue(provide_cf(), nullptr);
+ // expected-warning at -1{{Call argument for parameter 'theArray' is unretained and unsafe [alpha.webkit.UnretainedCallArgsChecker]}}
+}
+ at end
diff --git a/clang/test/Analysis/Checkers/WebKit/unretained-call-args.mm b/clang/test/Analysis/Checkers/WebKit/unretained-call-args.mm
new file mode 100644
index 0000000000000..5d9f7c873a8f9
--- /dev/null
+++ b/clang/test/Analysis/Checkers/WebKit/unretained-call-args.mm
@@ -0,0 +1,342 @@
+// RUN: %clang_analyze_cc1 -analyzer-checker=alpha.webkit.UnretainedCallArgsChecker -verify %s
+
+#include "objc-mock-types.h"
+
+SomeObj *provide();
+void consume_obj(SomeObj*);
+
+CFMutableArrayRef provide_cf();
+void consume_cf(CFMutableArrayRef);
+
+void some_function();
+
+namespace simple {
+ void foo() {
+ consume_obj(provide());
+ // expected-warning at -1{{Call argument is unretained and unsafe}}
+ consume_cf(provide_cf());
+ // expected-warning at -1{{Call argument is unretained and unsafe}}
+ }
+
+ // Test that the checker works with [[clang::suppress]].
+ void foo_suppressed() {
+ [[clang::suppress]] consume_obj(provide()); // no-warning
+ [[clang::suppress]] consume_cf(provide_cf()); // no-warning
+ }
+
+}
+
+namespace multi_arg {
+ void consume_refcntbl(int, SomeObj* foo, CFMutableArrayRef bar, bool);
+ void foo() {
+ consume_refcntbl(42, provide(), provide_cf(), true);
+ // expected-warning at -1{{Call argument for parameter 'foo' is unretained and unsafe}}
+ // expected-warning at -2{{Call argument for parameter 'bar' is unretained and unsafe}}
+ }
+}
+
+namespace retained {
+ RetainPtr<SomeObj> provide_obj() { return RetainPtr<SomeObj>{}; }
+ void consume_obj(RetainPtr<SomeObj>) {}
+
+ RetainPtr<CFMutableArrayRef> provide_cf() { return CFMutableArrayRef{}; }
+ void consume_cf(RetainPtr<CFMutableArrayRef>) {}
+
+ void foo() {
+ consume_obj(provide_obj().get()); // no warning
+ consume_cf(provide_cf().get()); // no warning
+ }
+}
+
+namespace methods {
+ struct Consumer {
+ void consume_obj(SomeObj* ptr);
+ void consume_cf(CFMutableArrayRef ref);
+ };
+
+ void foo() {
+ Consumer c;
+
+ c.consume_obj(provide());
+ // expected-warning at -1{{Call argument for parameter 'ptr' is unretained and unsafe}}
+ c.consume_cf(provide_cf());
+ // expected-warning at -1{{Call argument for parameter 'ref' is unretained and unsafe}}
+ }
+
+ void foo2() {
+ struct Consumer {
+ void consume(SomeObj*) { some_function(); }
+ void whatever() {
+ consume(provide());
+ // expected-warning at -1{{Call argument is unretained and unsafe}}
+ }
+
+ void consume_cf(CFMutableArrayRef) { some_function(); }
+ void something() {
+ consume_cf(provide_cf());
+ // expected-warning at -1{{Call argument is unretained and unsafe}}
+ }
+ };
+ }
+
+ void foo3() {
+ struct Consumer {
+ void consume(SomeObj*) { some_function(); }
+ void whatever() {
+ this->consume(provide());
+ // expected-warning at -1{{Call argument is unretained and unsafe}}
+ }
+
+ void consume_cf(CFMutableArrayRef) { some_function(); }
+ void something() {
+ this->consume_cf(provide_cf());
+ // expected-warning at -1{{Call argument is unretained and unsafe}}
+ }
+ };
+ }
+
+}
+
+namespace casts {
+ void foo() {
+ consume_obj(provide());
+ // expected-warning at -1{{Call argument is unretained and unsafe}}
+
+ consume_obj(static_cast<OtherObj*>(provide()));
+ // expected-warning at -1{{Call argument is unretained and unsafe}}
+
+ consume_obj(reinterpret_cast<OtherObj*>(provide()));
+ // expected-warning at -1{{Call argument is unretained and unsafe}}
+
+ consume_obj(downcast<OtherObj>(provide()));
+ // expected-warning at -1{{Call argument is unretained and unsafe}}
+ }
+}
+
+namespace null_ptr {
+ void foo_ref() {
+ consume_obj(nullptr);
+ consume_obj(0);
+ consume_cf(nullptr);
+ consume_cf(0);
+ }
+}
+
+namespace retain_ptr_lookalike {
+ struct Decoy {
+ SomeObj* get();
+ };
+
+ void foo() {
+ Decoy D;
+
+ consume_obj(D.get());
+ // expected-warning at -1{{Call argument is unretained and unsafe}}
+ }
+
+ struct Decoy2 {
+ CFMutableArrayRef get();
+ };
+
+ void bar() {
+ Decoy2 D;
+
+ consume_cf(D.get());
+ // expected-warning at -1{{Call argument is unretained and unsafe}}
+ }
+}
+
+namespace param_formarding_function {
+ void consume_more_obj(OtherObj*);
+ void consume_more_cf(CFMutableArrayRef);
+
+ namespace objc {
+ void foo(SomeObj* param) {
+ consume_more_obj(downcast<OtherObj>(param));
+ }
+ }
+
+ namespace cf {
+ void foo(CFMutableArrayRef param) {
+ consume_more_cf(param);
+ }
+ }
+}
+
+namespace param_formarding_lambda {
+ auto consume_more_obj = [](OtherObj*) { some_function(); };
+ auto consume_more_cf = [](CFMutableArrayRef) { some_function(); };
+
+ namespace objc {
+ void foo(SomeObj* param) {
+ consume_more_obj(downcast<OtherObj>(param));
+ }
+ }
+
+ namespace cf {
+ void foo(CFMutableArrayRef param) {
+ consume_more_cf(param);
+ }
+ }
+}
+
+namespace param_forwarding_method {
+ struct Consumer {
+ void consume_obj(SomeObj*);
+ static void consume_obj_s(SomeObj*);
+ void consume_cf(CFMutableArrayRef);
+ static void consume_cf_s(CFMutableArrayRef);
+ };
+
+ void bar(Consumer* consumer, SomeObj* param) {
+ consumer->consume_obj(param);
+ }
+
+ void foo(SomeObj* param) {
+ Consumer::consume_obj_s(param);
+ }
+
+ void baz(Consumer* consumer, CFMutableArrayRef param) {
+ consumer->consume_cf(param);
+ Consumer::consume_cf_s(param);
+ }
+}
+
+
+namespace default_arg {
+ SomeObj* global;
+ CFMutableArrayRef global_cf;
+
+ void function_with_default_arg1(SomeObj* param = global);
+ // expected-warning at -1{{Call argument for parameter 'param' is unretained and unsafe}}
+
+ void function_with_default_arg2(CFMutableArrayRef param = global_cf);
+ // expected-warning at -1{{Call argument for parameter 'param' is unretained and unsafe}}
+
+ void foo() {
+ function_with_default_arg1();
+ function_with_default_arg2();
+ }
+}
+
+namespace cxx_member_func {
+ RetainPtr<SomeObj> protectedProvide();
+ RetainPtr<CFMutableArrayRef> protectedProvideCF();
+
+ void foo() {
+ [provide() doWork];
+ // expected-warning at -1{{Reciever is unretained and unsafe}}
+ [protectedProvide().get() doWork];
+
+ CFArrayAppendValue(provide_cf(), nullptr);
+ // expected-warning at -1{{Call argument for parameter 'theArray' is unretained and unsafe}}
+ CFArrayAppendValue(protectedProvideCF(), nullptr);
+ };
+
+ void bar() {
+ [downcast<OtherObj>(protectedProvide().get()) doMoreWork:downcast<OtherObj>(provide())];
+ // expected-warning at -1{{Call argument for parameter 'other' is unretained and unsafe}}
+ [protectedProvide().get() doWork];
+ };
+
+}
+
+namespace cxx_member_operator_call {
+ // The hidden this-pointer argument without a corresponding parameter caused couple bugs in parameter <-> argument attribution.
+ struct Foo {
+ Foo& operator+(SomeObj* bad);
+ friend Foo& operator-(Foo& lhs, SomeObj* bad);
+ void operator()(SomeObj* bad);
+ };
+
+ SomeObj* global;
+
+ void foo() {
+ Foo f;
+ f + global;
+ // expected-warning at -1{{Call argument for parameter 'bad' is unretained and unsafe}}
+ f - global;
+ // expected-warning at -1{{Call argument for parameter 'bad' is unretained and unsafe}}
+ f(global);
+ // expected-warning at -1{{Call argument for parameter 'bad' is unretained and unsafe}}
+ }
+}
+
+namespace call_with_ptr_on_ref {
+ RetainPtr<SomeObj> provideProtected();
+ RetainPtr<CFMutableArrayRef> provideProtectedCF();
+ void bar(SomeObj* bad);
+ void bar_cf(CFMutableArrayRef bad);
+ bool baz();
+ void foo(bool v) {
+ bar(v ? nullptr : provideProtected().get());
+ bar(baz() ? provideProtected().get() : nullptr);
+ bar(v ? provide() : provideProtected().get());
+ // expected-warning at -1{{Call argument for parameter 'bad' is unretained and unsafe}}
+ bar(v ? provideProtected().get() : provide());
+ // expected-warning at -1{{Call argument for parameter 'bad' is unretained and unsafe}}
+
+ bar_cf(v ? nullptr : provideProtectedCF().get());
+ bar_cf(baz() ? provideProtectedCF().get() : nullptr);
+ bar_cf(v ? provide_cf() : provideProtectedCF().get());
+ // expected-warning at -1{{Call argument for parameter 'bad' is unretained and unsafe}}
+ bar_cf(v ? provideProtectedCF().get() : provide_cf());
+ // expected-warning at -1{{Call argument for parameter 'bad' is unretained and unsafe}}
+ }
+}
+
+namespace call_with_explicit_temporary_obj {
+ void foo() {
+ [RetainPtr<SomeObj>(provide()).get() doWork];
+ CFArrayAppendValue(RetainPtr<CFMutableArrayRef> { provide_cf() }.get(), nullptr);
+ }
+ template <typename T>
+ void bar() {
+ [RetainPtr<SomeObj>(provide()).get() doWork];
+ CFArrayAppendValue(RetainPtr<CFMutableArrayRef> { provide_cf() }.get(), nullptr);
+ }
+ void baz() {
+ bar<int>();
+ }
+}
+
+namespace call_with_adopt_ref {
+ void foo() {
+ [adoptNS(provide()).get() doWork];
+ CFArrayAppendValue(adoptCF(provide_cf()).get(), nullptr);
+ }
+}
+
+namespace call_with_cf_constant {
+ void foo() {
+ CFArrayCreateMutable(kCFAllocatorDefault, 10);
+ }
+}
+
+namespace call_with_cf_string {
+ void bar(CFStringRef);
+ void foo() {
+ bar(CFSTR("hello"));
+ }
+}
+
+namespace call_with_ns_string {
+ void bar(NSString *);
+ void foo() {
+ bar(@"world");
+ }
+}
+
+namespace bridge_cast_arg {
+ void bar(NSString *);
+ void foo(CFStringRef arg) {
+ bar((NSString *)bridge_cast((CFTypeRef)arg));
+ }
+}
+
+namespace alloc_init_pair {
+ void foo() {
+ auto obj = adoptNS([[SomeObj alloc] init]);
+ [obj doWork];
+ }
+}
>From ceeba5796fa04af1a170521dc26e3925576b4430 Mon Sep 17 00:00:00 2001
From: Ryosuke Niwa <rniwa at webkit.org>
Date: Mon, 24 Feb 2025 14:06:43 -0800
Subject: [PATCH 2/5] Fix formatting
---
.../StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp | 2 +-
.../Checkers/WebKit/RawPtrRefCallArgsChecker.cpp | 9 +++++----
2 files changed, 6 insertions(+), 5 deletions(-)
diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
index 5edc244349a75..bc26b63bad421 100644
--- a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
@@ -372,7 +372,7 @@ std::optional<bool> isGetterOfSafePtr(const CXXMethodDecl *M) {
auto QT = maybeRefToRawOperator->getConversionType();
auto *T = QT.getTypePtrOrNull();
return T && (T->isPointerType() || T->isReferenceType() ||
- T->isObjCObjectPointerType());
+ T->isObjCObjectPointerType());
}
}
}
diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/RawPtrRefCallArgsChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/RawPtrRefCallArgsChecker.cpp
index c6069ffa96696..21ef9799a68b3 100644
--- a/clang/lib/StaticAnalyzer/Checkers/WebKit/RawPtrRefCallArgsChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/RawPtrRefCallArgsChecker.cpp
@@ -168,9 +168,9 @@ class RawPtrRefCallArgsChecker
if (IsUnsafe && *IsUnsafe && !isPtrOriginSafe(Receiver)) {
if (auto *InnerMsg = dyn_cast<ObjCMessageExpr>(Receiver)) {
auto InnerSelector = InnerMsg->getSelector();
- if (InnerSelector.getNameForSlot(0) == "alloc"
- && Selector.getNameForSlot(0).starts_with("init"))
- return;
+ if (InnerSelector.getNameForSlot(0) == "alloc" &&
+ Selector.getNameForSlot(0).starts_with("init"))
+ return;
}
reportBugOnReceiver(Receiver, D);
}
@@ -360,7 +360,8 @@ class RawPtrRefCallArgsChecker
BR->emitReport(std::move(Report));
}
- void reportBugOnReceiver(const Expr *CallArg, const Decl *DeclWithIssue) const {
+ void reportBugOnReceiver(const Expr *CallArg,
+ const Decl *DeclWithIssue) const {
assert(CallArg);
const SourceLocation SrcLocToReport = CallArg->getSourceRange().getBegin();
>From ab640f75d293a95575e91480456b2302ddc56d9e Mon Sep 17 00:00:00 2001
From: Ryosuke Niwa <rniwa at webkit.org>
Date: Mon, 24 Feb 2025 14:23:58 -0800
Subject: [PATCH 3/5] Fix documentation
---
clang/docs/analyzer/checkers.rst | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/clang/docs/analyzer/checkers.rst b/clang/docs/analyzer/checkers.rst
index c82d4114b38e2..0ec2690adc9cd 100644
--- a/clang/docs/analyzer/checkers.rst
+++ b/clang/docs/analyzer/checkers.rst
@@ -3583,7 +3583,7 @@ The goal of this rule is to make sure that lifetime of any dynamically allocated
The rules of when to use and not to use CheckedPtr / CheckedRef are same as alpha.webkit.UncountedCallArgsChecker for ref-counted objects.
alpha.webkit.UnretainedCallArgsChecker
-"""""""""""""""""""""""""""""""""""""
+""""""""""""""""""""""""""""""""""""""
The goal of this rule is to make sure that lifetime of any dynamically allocated NS or CF objects passed as a call argument keeps its memory region past the end of the call. This applies to call to any function, method, lambda, function pointer or functor. NS or CF objects aren't supposed to be allocated on stack so we check arguments for parameters of raw pointers and references to unretained types.
The rules of when to use and not to use RetainPtr are same as alpha.webkit.UncountedCallArgsChecker for ref-counted objects.
>From 5d6b018f9ab41d8117fc2617af35631408de3141 Mon Sep 17 00:00:00 2001
From: Ryosuke Niwa <rniwa at webkit.org>
Date: Mon, 24 Feb 2025 16:43:15 -0800
Subject: [PATCH 4/5] Add the support for recognizing a few more edge cases as
safe. Namely, class and superclass properties on class and constant
dictionary or array literals.
---
.../Checkers/WebKit/ASTUtils.cpp | 19 ++++++
.../Checkers/WebKit/objc-mock-types.h | 58 ++++++++++++++++++-
.../Checkers/WebKit/unretained-call-args.mm | 31 +++++++++-
3 files changed, 105 insertions(+), 3 deletions(-)
diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp
index 8de94ca9d4d91..56d9003196f1b 100644
--- a/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp
@@ -37,6 +37,10 @@ bool tryToFindPtrOrigin(
QT.isConstQualified()) {
return callback(E, true);
}
+ if (auto *VD = dyn_cast<VarDecl>(ValDecl)) {
+ if (auto *Init = VD->getInit())
+ E = Init->IgnoreParenCasts();
+ }
}
if (auto *tempExpr = dyn_cast<MaterializeTemporaryExpr>(E)) {
E = tempExpr->getSubExpr();
@@ -63,6 +67,10 @@ bool tryToFindPtrOrigin(
E = tempExpr->getSubExpr();
continue;
}
+ if (auto *OpaqueValue = dyn_cast<OpaqueValueExpr>(E)) {
+ E = OpaqueValue->getSourceExpr();
+ continue;
+ }
if (auto *Expr = dyn_cast<ConditionalOperator>(E)) {
return tryToFindPtrOrigin(Expr->getTrueExpr(), StopAtFirstRefCountedObj,
isSafePtr, isSafePtrType, callback) &&
@@ -137,7 +145,18 @@ bool tryToFindPtrOrigin(
if (isSafePtrType(Method->getReturnType()))
return callback(E, true);
}
+ auto Selector = ObjCMsgExpr->getSelector();
+ auto NameForFirstSlot = Selector.getNameForSlot(0);
+ if ((NameForFirstSlot == "class" || NameForFirstSlot == "superclass") &&
+ !Selector.getNumArgs())
+ return callback(E, true);
}
+ if (auto *ObjCDict = dyn_cast<ObjCDictionaryLiteral>(E))
+ return callback(ObjCDict, true);
+ if (auto *ObjCArray = dyn_cast<ObjCArrayLiteral>(E))
+ return callback(ObjCArray, true);
+ if (auto *ObjCStr = dyn_cast<ObjCStringLiteral>(E))
+ return callback(ObjCStr, true);
if (auto *unaryOp = dyn_cast<UnaryOperator>(E)) {
// FIXME: Currently accepts ANY unary operator. Is it OK?
E = unaryOp->getSubExpr();
diff --git a/clang/test/Analysis/Checkers/WebKit/objc-mock-types.h b/clang/test/Analysis/Checkers/WebKit/objc-mock-types.h
index 179003d563f4a..04c85d978a541 100644
--- a/clang/test/Analysis/Checkers/WebKit/objc-mock-types.h
+++ b/clang/test/Analysis/Checkers/WebKit/objc-mock-types.h
@@ -1,16 +1,20 @@
@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 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_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);
@@ -24,9 +28,61 @@ extern void CFRelease(CFTypeRef cf);
__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
@@ -189,4 +245,4 @@ using WTF::adoptNS;
using WTF::adoptCF;
using WTF::retainPtr;
using WTF::downcast;
-using WTF::bridge_cast;
+using WTF::bridge_cast;
\ No newline at end of file
diff --git a/clang/test/Analysis/Checkers/WebKit/unretained-call-args.mm b/clang/test/Analysis/Checkers/WebKit/unretained-call-args.mm
index 5d9f7c873a8f9..1ccf87f9302e8 100644
--- a/clang/test/Analysis/Checkers/WebKit/unretained-call-args.mm
+++ b/clang/test/Analysis/Checkers/WebKit/unretained-call-args.mm
@@ -308,8 +308,14 @@ void foo() {
}
namespace call_with_cf_constant {
+ static const NSArray *array = @[@"hello"];
+ static const NSDictionary *dict = @{@"hello": @3};
+ void bar(const NSArray *);
+ void baz(const NSDictionary *);
void foo() {
- CFArrayCreateMutable(kCFAllocatorDefault, 10);
+ CFArrayCreateMutable(kCFAllocatorDefault, 10);
+ bar(array);
+ baz(dict);
}
}
@@ -329,8 +335,15 @@ void foo() {
namespace bridge_cast_arg {
void bar(NSString *);
- void foo(CFStringRef arg) {
+ void baz(NSString *);
+ extern const CFStringRef kCFBundleNameKey;
+
+ NSObject *foo(CFStringRef arg) {
bar((NSString *)bridge_cast((CFTypeRef)arg));
+ auto dict = @{
+ @"hello": @1,
+ };
+ return dict[(__bridge NSString *)kCFBundleNameKey];
}
}
@@ -340,3 +353,17 @@ void foo() {
[obj doWork];
}
}
+
+namespace alloc_class {
+ bool foo(NSObject *obj) {
+ return [obj isKindOfClass:SomeObj.class];
+ }
+
+ bool bar(NSObject *obj) {
+ return [obj isKindOfClass:[SomeObj class]];
+ }
+
+ bool baz(NSObject *obj) {
+ return [obj isKindOfClass:[SomeObj superclass]];
+ }
+}
>From 6d5a42ef56e19b94c39324636a633c5da5c515e5 Mon Sep 17 00:00:00 2001
From: Ryosuke Niwa <rniwa at webkit.org>
Date: Mon, 24 Feb 2025 17:23:58 -0800
Subject: [PATCH 5/5] Fix a regression from the last change. We shouldn't be
traversing through variable initializations.
---
clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp | 4 ----
clang/test/Analysis/Checkers/WebKit/unretained-call-args.mm | 6 ++----
2 files changed, 2 insertions(+), 8 deletions(-)
diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp
index 56d9003196f1b..7f735c28a53e7 100644
--- a/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp
@@ -37,10 +37,6 @@ bool tryToFindPtrOrigin(
QT.isConstQualified()) {
return callback(E, true);
}
- if (auto *VD = dyn_cast<VarDecl>(ValDecl)) {
- if (auto *Init = VD->getInit())
- E = Init->IgnoreParenCasts();
- }
}
if (auto *tempExpr = dyn_cast<MaterializeTemporaryExpr>(E)) {
E = tempExpr->getSubExpr();
diff --git a/clang/test/Analysis/Checkers/WebKit/unretained-call-args.mm b/clang/test/Analysis/Checkers/WebKit/unretained-call-args.mm
index 1ccf87f9302e8..b54f1ffb7b7c7 100644
--- a/clang/test/Analysis/Checkers/WebKit/unretained-call-args.mm
+++ b/clang/test/Analysis/Checkers/WebKit/unretained-call-args.mm
@@ -308,14 +308,12 @@ void foo() {
}
namespace call_with_cf_constant {
- static const NSArray *array = @[@"hello"];
- static const NSDictionary *dict = @{@"hello": @3};
void bar(const NSArray *);
void baz(const NSDictionary *);
void foo() {
CFArrayCreateMutable(kCFAllocatorDefault, 10);
- bar(array);
- baz(dict);
+ bar(@[@"hello"]);
+ baz(@{@"hello": @3});
}
}
More information about the cfe-commits
mailing list