https://github.com/rniwa created
https://github.com/llvm/llvm-project/pull/200599
Emit a warning for a member variable which is a raw pointer or raw reference to
a smart pointer as such a variable is not memory safe as the underlying smart
pointer might get free'ed elsewhere.
Also add a support for [[clang::annotate_type("webkit.unsafeptr")]], which
explicitly annotate and allow such a member variable in container classes such
as Vector and HashTable.
>From d56a62ab277c3171351216d9c6b441e8f23e6a03 Mon Sep 17 00:00:00 2001
From: Ryosuke Niwa <[email protected]>
Date: Sat, 30 May 2026 12:30:29 -0700
Subject: [PATCH] [alpha.webkit.NoUncheckedPtrMemberChecker] Emit a warning for
a pointer to smart pointers
Emit a warning for a member variable which is a raw pointer or raw reference to
a smart pointer
as such a variable is not memory safe as the underlying smart pointer might get
free'ed elsewhere.
Also add a support for [[clang::annotate_type("webkit.unsafeptr")]], which
explicitly annotate
and allow such a member variable in container classes such as Vector and
HashTable.
---
.../Checkers/WebKit/PtrTypesSemantics.cpp | 32 ++++-
.../Checkers/WebKit/PtrTypesSemantics.h | 10 ++
.../WebKit/RawPtrRefMemberChecker.cpp | 112 ++++++++++++------
.../Checkers/WebKit/unchecked-members.cpp | 28 ++++-
.../Checkers/WebKit/uncounted-members-objc.mm | 24 ++++
.../Checkers/WebKit/uncounted-members.cpp | 27 ++++-
.../Checkers/WebKit/unretained-members-arc.mm | 42 ++++++-
.../Checkers/WebKit/unretained-members.mm | 63 +++++++++-
8 files changed, 288 insertions(+), 50 deletions(-)
diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
index 2ca34ff0587e1..0565c7540f845 100644
--- a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
@@ -225,6 +225,14 @@ static bool isPtrOfType(const clang::QualType T, Predicate
Pred) {
return false;
}
+bool isRefPtrType(const clang::QualType T) {
+ return isPtrOfType(T, [](auto Name) { return isRefType(Name); });
+}
+
+bool isCheckedPtrType(const clang::QualType T) {
+ return isPtrOfType(T, [](auto Name) { return isCheckedPtr(Name); });
+}
+
bool isRefOrCheckedPtrType(const clang::QualType T) {
return isPtrOfType(
T, [](auto Name) { return isRefType(Name) || isCheckedPtr(Name); });
@@ -427,14 +435,15 @@ enum class WebKitAnnotation : uint8_t {
None,
PointerConversion,
NoDelete,
+ UnsafePtr,
};
-static WebKitAnnotation typeAnnotationForReturnType(const FunctionDecl *FD) {
- auto RetType = FD->getReturnType();
- auto *Type = RetType.getTypePtrOrNull();
- if (auto *MacroQualified = dyn_cast_or_null<MacroQualifiedType>(Type))
- Type = MacroQualified->desugar().getTypePtrOrNull();
- auto *Attr = dyn_cast_or_null<AttributedType>(Type);
+static WebKitAnnotation typeAnnotation(const Type *T) {
+ if (!T)
+ return WebKitAnnotation::None;
+ if (auto *MacroQualified = dyn_cast_or_null<MacroQualifiedType>(T))
+ T = MacroQualified->desugar().getTypePtrOrNull();
+ auto *Attr = dyn_cast_or_null<AttributedType>(T);
if (!Attr)
return WebKitAnnotation::None;
auto *AnnotateType = dyn_cast_or_null<AnnotateTypeAttr>(Attr->getAttr());
@@ -445,9 +454,16 @@ static WebKitAnnotation typeAnnotationForReturnType(const
FunctionDecl *FD) {
return WebKitAnnotation::PointerConversion;
if (Annotation == "webkit.nodelete")
return WebKitAnnotation::NoDelete;
+ if (Annotation == "webkit.unsafeptr")
+ return WebKitAnnotation::UnsafePtr;
return WebKitAnnotation::None;
}
+static WebKitAnnotation typeAnnotationForReturnType(const FunctionDecl *FD) {
+ auto RetType = FD->getReturnType();
+ return typeAnnotation(RetType.getTypePtrOrNull());
+}
+
bool isPtrConversion(const FunctionDecl *F) {
assert(F);
if (isCtorOfRefCounted(F))
@@ -494,6 +510,10 @@ bool isNoDeleteFunction(const FunctionDecl *F) {
return false;
}
+bool isExplicitlyAllowedUnsafePtr(const Type *T) {
+ return typeAnnotation(T) == WebKitAnnotation::UnsafePtr;
+}
+
bool isTrivialBuiltinFunction(const FunctionDecl *F) {
if (!F || !F->getDeclName().isIdentifier())
return false;
diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.h
b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.h
index a2fd12656d391..13e78e0beb0ae 100644
--- a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.h
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.h
@@ -109,6 +109,12 @@ std::optional<bool> isUncountedPtr(const clang::QualType
T);
/// class, false if not, std::nullopt if inconclusive.
std::optional<bool> isUncheckedPtr(const clang::QualType T);
+/// \returns true if \p T is a RefPtr or Ref or its variant, false if not.
+bool isRefPtrType(const clang::QualType T);
+
+/// \returns true if \p T is a RefPtr or Ref or its variant, false if not.
+bool isCheckedPtrType(const clang::QualType T);
+
/// \returns true if \p T is a RefPtr, Ref, CheckedPtr, CheckedRef, or its
/// variant, false if not.
bool isRefOrCheckedPtrType(const clang::QualType T);
@@ -162,6 +168,10 @@ bool isPtrConversion(const FunctionDecl *F);
/// [[clang::annotate_type("webkit.nodelete")]].
bool isNoDeleteFunction(const FunctionDecl *F);
+/// \returns true if \p T is annotated with
+/// [[clang::annotate_type("webkit.unsafeptr")]].
+bool isExplicitlyAllowedUnsafePtr(const Type *T);
+
/// \returns true if \p F is a builtin function which is considered trivial.
bool isTrivialBuiltinFunction(const FunctionDecl *F);
diff --git
a/clang/lib/StaticAnalyzer/Checkers/WebKit/RawPtrRefMemberChecker.cpp
b/clang/lib/StaticAnalyzer/Checkers/WebKit/RawPtrRefMemberChecker.cpp
index 0e23ae34ea212..1f0bbb09da61b 100644
--- a/clang/lib/StaticAnalyzer/Checkers/WebKit/RawPtrRefMemberChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/RawPtrRefMemberChecker.cpp
@@ -39,6 +39,7 @@ class RawPtrRefMemberChecker
virtual std::optional<bool> isUnsafePtr(QualType,
bool ignoreARC = false) const = 0;
+ virtual bool isSafePtr(QualType) const = 0;
virtual const char *typeName() const = 0;
virtual const char *invariant() const = 0;
@@ -90,26 +91,39 @@ class RawPtrRefMemberChecker
}
void visitMember(const FieldDecl *Member, const RecordDecl *RD) const {
+ visitMemberDecl(Member, RD);
+ }
+
+ template <typename DeclType, typename ParentDeclType>
+ bool visitMemberDecl(DeclType *Member, const ParentDeclType *D) const {
auto QT = Member->getType();
const Type *MemberType = QT.getTypePtrOrNull();
+ bool IsPtrToSafePtr = false;
while (MemberType) {
auto IsUnsafePtr = isUnsafePtr(QT);
if (IsUnsafePtr && *IsUnsafePtr)
break;
- if (!MemberType->isPointerType())
- return;
+ if (!MemberType->isPointerType() && !MemberType->isReferenceType())
+ return false;
QT = MemberType->getPointeeType();
+ if (isSafePtr(QT) && !isExplicitlyAllowedUnsafePtr(MemberType)) {
+ IsPtrToSafePtr = true;
+ break;
+ }
MemberType = QT.getTypePtrOrNull();
}
if (!MemberType)
- return;
+ return false;
if (auto *MemberCXXRD = MemberType->getPointeeCXXRecordDecl())
- reportBug(Member, MemberType, MemberCXXRD, RD);
+ reportBug(Member, MemberType, MemberCXXRD, D, IsPtrToSafePtr);
else if (auto *ObjCDecl = getObjCDecl(MemberType))
- reportBug(Member, MemberType, ObjCDecl, RD);
+ reportBug(Member, MemberType, ObjCDecl, D, IsPtrToSafePtr);
+ else
+ return false;
+ return true;
}
ObjCInterfaceDecl *getObjCDecl(const Type *TypePtr) const {
@@ -156,21 +170,8 @@ class RawPtrRefMemberChecker
if (IvarDeclsToIgnore.contains(Ivar))
return;
- auto QT = Ivar->getType();
- const Type *IvarType = QT.getTypePtrOrNull();
- if (!IvarType)
- return;
-
- auto IsUnsafePtr = isUnsafePtr(QT);
- if (!IsUnsafePtr || !*IsUnsafePtr)
- return;
-
- IvarDeclsToIgnore.insert(Ivar);
-
- if (auto *MemberCXXRD = IvarType->getPointeeCXXRecordDecl())
- reportBug(Ivar, IvarType, MemberCXXRD, CD);
- else if (auto *ObjCDecl = getObjCDecl(IvarType))
- reportBug(Ivar, IvarType, ObjCDecl, CD);
+ if (visitMemberDecl(Ivar, CD))
+ IvarDeclsToIgnore.insert(Ivar);
}
void visitObjCPropertyDecl(const ObjCContainerDecl *CD,
@@ -184,14 +185,14 @@ class RawPtrRefMemberChecker
return;
}
- auto [IsUnsafe, PropType] = isPropImplUnsafePtr(PD);
+ auto [IsUnsafe, PropType, IsPtrToSafePtr] = isPropImplUnsafePtr(PD);
if (!IsUnsafe)
return;
if (auto *MemberCXXRD = PropType->getPointeeCXXRecordDecl())
- reportBug(PD, PropType, MemberCXXRD, CD);
+ reportBug(PD, PropType, MemberCXXRD, CD, IsPtrToSafePtr);
else if (auto *ObjCDecl = getObjCDecl(PropType))
- reportBug(PD, PropType, ObjCDecl, CD);
+ reportBug(PD, PropType, ObjCDecl, CD, IsPtrToSafePtr);
}
void visitPropImpl(const ObjCContainerDecl *CD,
@@ -208,25 +209,25 @@ class RawPtrRefMemberChecker
return;
IvarDeclsToIgnore.insert(IvarDecl);
}
- auto [IsUnsafe, PropType] = isPropImplUnsafePtr(PropDecl);
+ auto [IsUnsafe, PropType, IsPtrToSafePtr] = isPropImplUnsafePtr(PropDecl);
if (!IsUnsafe)
return;
if (auto *MemberCXXRD = PropType->getPointeeCXXRecordDecl())
- reportBug(PropDecl, PropType, MemberCXXRD, CD);
+ reportBug(PropDecl, PropType, MemberCXXRD, CD, IsPtrToSafePtr);
else if (auto *ObjCDecl = getObjCDecl(PropType))
- reportBug(PropDecl, PropType, ObjCDecl, CD);
+ reportBug(PropDecl, PropType, ObjCDecl, CD, IsPtrToSafePtr);
}
- std::pair<bool, const Type *>
+ std::tuple<bool, const Type *, bool>
isPropImplUnsafePtr(const ObjCPropertyDecl *PD) const {
if (!PD)
- return {false, nullptr};
+ return {false, nullptr, false};
auto QT = PD->getType();
const Type *PropType = QT.getTypePtrOrNull();
if (!PropType)
- return {false, nullptr};
+ return {false, nullptr, false};
// "assign" property doesn't retain even under ARC so treat it as unsafe.
bool ignoreARC =
@@ -235,7 +236,22 @@ class RawPtrRefMemberChecker
PD->getPropertyAttributes() & ObjCPropertyAttribute::kind_weak;
bool HasSafeAttr = PD->isRetaining() || IsWeak;
auto IsUnsafePtr = isUnsafePtr(QT, ignoreARC);
- return {IsUnsafePtr && *IsUnsafePtr && !HasSafeAttr, PropType};
+ if (IsUnsafePtr && *IsUnsafePtr)
+ return {!HasSafeAttr, PropType, false};
+
+ while (PropType->isPointerType() || PropType->isReferenceType()) {
+ auto PointeeQT = PropType->getPointeeType();
+ if (isSafePtr(PointeeQT))
+ return {true, PropType, true};
+ PropType = PointeeQT.getTypePtrOrNull();
+ if (!PropType)
+ break;
+ auto IsUnsafePtr = isUnsafePtr(PointeeQT);
+ if (IsUnsafePtr && *IsUnsafePtr)
+ return {true, PropType, false};
+ }
+
+ return {false, nullptr, false};
}
bool shouldSkipDecl(const RecordDecl *RD) const {
@@ -275,7 +291,8 @@ class RawPtrRefMemberChecker
template <typename DeclType, typename PointeeType, typename ParentDeclType>
void reportBug(const DeclType *Member, const Type *MemberType,
const PointeeType *Pointee,
- const ParentDeclType *ClassCXXRD) const {
+ const ParentDeclType *ClassCXXRD,
+ bool IsPtrToSafe = false) const {
assert(Member);
assert(MemberType);
assert(Pointee);
@@ -297,7 +314,7 @@ class RawPtrRefMemberChecker
Os << " is a ";
else
Os << " contains a ";
- if (printPointer(Os, MemberType) == PrintDeclKind::Pointer) {
+ if (printPointer(Os, MemberType, IsPtrToSafe) == PrintDeclKind::Pointer) {
auto Typedef = MemberType->getAs<TypedefType>();
assert(Typedef);
printQuotedQualifiedName(Os, Typedef->getDecl());
@@ -314,12 +331,22 @@ class RawPtrRefMemberChecker
enum class PrintDeclKind { Pointee, Pointer };
virtual PrintDeclKind printPointer(llvm::raw_svector_ostream &Os,
- const Type *T) const {
+ const Type *T, bool IsPtrToSafe) const {
T = T->getUnqualifiedDesugaredType();
bool IsPtr = isa<PointerType>(T) || isa<ObjCObjectPointerType>(T);
- Os << (IsPtr ? "raw pointer" : "reference") << " to " << typeName() << " ";
+ Os << "raw " << (IsPtr ? "pointer" : "reference") << " to ";
+ if (!IsPtrToSafe)
+ Os << typeName() << " ";
return PrintDeclKind::Pointee;
}
+
+ void printTypeName(llvm::raw_ostream &Os, const Type *T) const {
+ if (auto *RD = T->getAsRecordDecl())
+ RD->getNameForDiagnostic(Os, RD->getASTContext().getPrintingPolicy(),
+ /*Qualified=*/true);
+ else
+ Os << typeName();
+ }
};
class NoUncountedMemberChecker final : public RawPtrRefMemberChecker {
@@ -332,6 +359,10 @@ class NoUncountedMemberChecker final : public
RawPtrRefMemberChecker {
return isUncountedPtr(QT.getCanonicalType());
}
+ bool isSafePtr(QualType QT) const final {
+ return isRefPtrType(QT);
+ }
+
const char *typeName() const final { return "ref-countable type"; }
const char *invariant() const final {
@@ -349,6 +380,10 @@ class NoUncheckedPtrMemberChecker final : public
RawPtrRefMemberChecker {
return isUncheckedPtr(QT.getCanonicalType());
}
+ bool isSafePtr(QualType QT) const final {
+ return isCheckedPtrType(QT);
+ }
+
const char *typeName() const final { return "CheckedPtr capable type"; }
const char *invariant() const final {
@@ -371,6 +406,10 @@ class NoUnretainedMemberChecker final : public
RawPtrRefMemberChecker {
return RTC->isUnretained(QT, ignoreARC);
}
+ bool isSafePtr(QualType QT) const final {
+ return isRetainPtrOrOSPtrType(QT);
+ }
+
const char *typeName() const final { return "retainable type"; }
const char *invariant() const final {
@@ -378,12 +417,13 @@ class NoUnretainedMemberChecker final : public
RawPtrRefMemberChecker {
}
PrintDeclKind printPointer(llvm::raw_svector_ostream &Os,
- const Type *T) const final {
+ const Type *T, bool IsPtrToSafe) const final {
+ // FIXME: Support IsPtrToSafe.
if (!isa<ObjCObjectPointerType>(T) && T->getAs<TypedefType>()) {
Os << typeName() << " ";
return PrintDeclKind::Pointer;
}
- return RawPtrRefMemberChecker::printPointer(Os, T);
+ return RawPtrRefMemberChecker::printPointer(Os, T, IsPtrToSafe);
}
};
diff --git a/clang/test/Analysis/Checkers/WebKit/unchecked-members.cpp
b/clang/test/Analysis/Checkers/WebKit/unchecked-members.cpp
index 3fe15d88ff312..57ca3f98a14da 100644
--- a/clang/test/Analysis/Checkers/WebKit/unchecked-members.cpp
+++ b/clang/test/Analysis/Checkers/WebKit/unchecked-members.cpp
@@ -9,7 +9,7 @@ namespace members {
CheckedObj* a = nullptr;
// expected-warning@-1{{Member variable 'a' in 'members::Foo' is a raw pointer
to CheckedPtr capable type 'CheckedObj'}}
CheckedObj& b;
-// expected-warning@-1{{Member variable 'b' in 'members::Foo' is a reference
to CheckedPtr capable type 'CheckedObj'}}
+// expected-warning@-1{{Member variable 'b' in 'members::Foo' is a raw
reference to CheckedPtr capable type 'CheckedObj'}}
[[clang::suppress]]
CheckedObj* a_suppressed = nullptr;
@@ -19,6 +19,14 @@ namespace members {
CheckedPtr<CheckedObj> c;
CheckedRef<CheckedObj> d;
+ CheckedRef<RefCountable>* e;
+// expected-warning@-1{{Member variable 'e' in 'members::Foo' is a raw pointer
to 'CheckedRef<RefCountable>'}}
+ CheckedRef<RefCountable>& f;
+// expected-warning@-1{{Member variable 'f' in 'members::Foo' is a raw
reference to 'CheckedRef<RefCountable>'}}
+ CheckedRef<RefCountable>** g;
+// expected-warning@-1{{Member variable 'g' in 'members::Foo' contains a raw
pointer to 'CheckedRef<RefCountable>'}}
+ CheckedRef<RefCountable>* h;
+// expected-warning@-1{{Member variable 'h' in 'members::Foo' is a raw pointer
to 'CheckedRef<RefCountable>'}}
public:
Foo();
@@ -39,8 +47,12 @@ namespace unions {
union Foo {
CheckedObj* a;
// expected-warning@-1{{Member variable 'a' in 'unions::Foo' is a raw
pointer to CheckedPtr capable type 'CheckedObj'}}
- CheckedPtr<CheckedObj> c;
- CheckedRef<CheckedObj> d;
+ CheckedPtr<CheckedObj> b;
+ CheckedRef<CheckedObj> c;
+ CheckedObj** d;
+ // expected-warning@-1{{Member variable 'd' in 'unions::Foo' contains a
raw pointer to CheckedPtr capable type 'CheckedObj'}}
+ CheckedPtr<CheckedObj>* e;
+ // expected-warning@-1{{Member variable 'e' in 'unions::Foo' is a raw
pointer to 'CheckedPtr<CheckedObj>'}}
};
template<class T>
@@ -76,8 +88,16 @@ namespace ptr_to_ptr_to_checked_ptr_capable {
};
TemplateList<CheckedObj> list;
- struct SafeList {
+ struct FormerlySafeList {
CheckedPtr<CheckedObj>* elements;
+ // expected-warning@-1{{Member variable 'elements' in
'ptr_to_ptr_to_checked_ptr_capable::FormerlySafeList' is a raw pointer to
'CheckedPtr<CheckedObj>'}}
+ };
+
+ struct Container {
+ CheckedPtr<CheckedObj>* [[clang::annotate_type("webkit.unsafeptr")]]
elements1;
+ CheckedPtr<CheckedObj>** [[clang::annotate_type("webkit.unsafeptr")]]
elements2;
+ // expected-warning@-1{{Member variable 'elements2' in
'ptr_to_ptr_to_checked_ptr_capable::Container' contains a raw pointer to
'CheckedPtr<CheckedObj>'}}
+ CheckedRef<CheckedObj>* [[clang::annotate_type("webkit.unsafeptr")]]*
[[clang::annotate_type("webkit.unsafeptr")]] elements3;
};
} // namespace ptr_to_ptr_to_checked_ptr_capable
diff --git a/clang/test/Analysis/Checkers/WebKit/uncounted-members-objc.mm
b/clang/test/Analysis/Checkers/WebKit/uncounted-members-objc.mm
index 83b08a6841d26..dcbc6e74eee9b 100644
--- a/clang/test/Analysis/Checkers/WebKit/uncounted-members-objc.mm
+++ b/clang/test/Analysis/Checkers/WebKit/uncounted-members-objc.mm
@@ -17,7 +17,25 @@ @interface SomeObjC : NSObject {
// expected-warning@-1{{Instance variable '_uncounted1' in 'SomeObjC' is a raw
pointer to ref-countable type 'RefCountable'}}
RefPtr<RefCountable> _counted1;
[[clang::suppress]] RefCountable* _uncounted2;
+ RefCountable** _ptr_to_ptr_to_uncounted;
+// expected-warning@-1{{Instance variable '_ptr_to_ptr_to_uncounted' in
'SomeObjC' contains a raw pointer to ref-countable type 'RefCountable'}}
+ RefPtr<RefCountable>* ptr_to_refptr1;
+// expected-warning@-1{{Instance variable 'ptr_to_refptr1' in 'SomeObjC' is a
raw pointer to 'RefPtr<RefCountable>'}}
+ RefPtr<RefCountable>* [[clang::annotate_type("webkit.unsafeptr")]]
_ptr_to_refptr2;
+ RefPtr<RefCountable>** _ptr_to_refptr3;
+// expected-warning@-1{{Instance variable '_ptr_to_refptr3' in 'SomeObjC'
contains a raw pointer to 'RefPtr<RefCountable>'}}
+ Ref<RefCountable>* [[clang::annotate_type("webkit.unsafeptr")]] _ptr_to_ref1;
+ Ref<RefCountable>** _ptr_to_ref2;
+// expected-warning@-1{{Instance variable '_ptr_to_ref2' in 'SomeObjC'
contains a raw pointer to 'Ref<RefCountable>'}}
}
+@property (nonatomic) RefCountable **obj1;
+// expected-warning@-1{{Property 'obj1' in 'SomeObjC' contains a raw pointer
to ref-countable type 'RefCountable'}}
+@property (nonatomic) RefPtr<RefCountable> *obj2;
+// expected-warning@-1{{Property 'obj2' in 'SomeObjC' is a raw pointer to
'RefPtr<RefCountable>'}}
+@property (nonatomic) RefPtr<RefCountable> **obj3;
+// expected-warning@-1{{Property 'obj3' in 'SomeObjC' contains a raw pointer
to 'RefPtr<RefCountable>'}}
+@property(nonatomic, readonly) Ref<RefCountable> *syn_prop;
+// expected-warning@-1{{Property 'syn_prop' in 'SomeObjC' is a raw pointer to
'Ref<RefCountable>'}}
- (void)doWork;
@end
@@ -26,8 +44,14 @@ @implementation SomeObjC {
// expected-warning@-1{{Instance variable '_uncounted3' in 'SomeObjC' is a raw
pointer to ref-countable type 'RefCountable'}}
RefPtr<RefCountable> _counted2;
[[clang::suppress]] RefCountable* _uncounted4;
+ RefCountable** _uncounted5;
+// expected-warning@-1{{Instance variable '_uncounted5' in 'SomeObjC' contains
a raw pointer to ref-countable type 'RefCountable'}}
+ RefPtr<RefCountable>* _ptr_to_refptr1;
+// expected-warning@-1{{Instance variable '_ptr_to_refptr1' in 'SomeObjC' is a
raw pointer to 'RefPtr<RefCountable>'}}
}
+@synthesize syn_prop;
+
- (void)doWork {
doSomeWork();
}
diff --git a/clang/test/Analysis/Checkers/WebKit/uncounted-members.cpp
b/clang/test/Analysis/Checkers/WebKit/uncounted-members.cpp
index b8c443cda4f8e..37464d0ddb0ac 100644
--- a/clang/test/Analysis/Checkers/WebKit/uncounted-members.cpp
+++ b/clang/test/Analysis/Checkers/WebKit/uncounted-members.cpp
@@ -16,10 +16,20 @@ namespace members {
RefPtr<RefCountable> b;
public:
+ Foo();
+
RefCountable silenceWarningAboutInit;
RefCountable& c = silenceWarningAboutInit;
-// expected-warning@-1{{Member variable 'c' in 'members::Foo' is a reference
to ref-countable type 'RefCountable'}}
+// expected-warning@-1{{Member variable 'c' in 'members::Foo' is a raw
reference to ref-countable type 'RefCountable'}}
Ref<RefCountable> d;
+ Ref<RefCountable>* e;
+// expected-warning@-1{{Member variable 'e' in 'members::Foo' is a raw pointer
to 'Ref<RefCountable>'}}
+ Ref<RefCountable>& f;
+// expected-warning@-1{{Member variable 'f' in 'members::Foo' is a raw
reference to 'Ref<RefCountable>'}}
+ Ref<RefCountable>** g;
+// expected-warning@-1{{Member variable 'g' in 'members::Foo' contains a raw
pointer to 'Ref<RefCountable>'}}
+ RefPtr<RefCountable>* h;
+// expected-warning@-1{{Member variable 'h' in 'members::Foo' is a raw pointer
to 'RefPtr<RefCountable>'}}
};
template<class T>
@@ -34,6 +44,7 @@ namespace members {
private:
RefCountable* a = nullptr;
};
+
} // members
namespace unions {
@@ -42,6 +53,10 @@ namespace unions {
// expected-warning@-1{{Member variable 'a' in 'unions::Foo' is a raw
pointer to ref-countable type 'RefCountable'}}
RefPtr<RefCountable> b;
Ref<RefCountable> c;
+ RefCountable** d;
+ // expected-warning@-1{{Member variable 'd' in 'unions::Foo' contains a
raw pointer to ref-countable type 'RefCountable'}}
+ Ref<RefCountable>* e;
+ // expected-warning@-1{{Member variable 'e' in 'unions::Foo' is a raw
pointer to 'Ref<RefCountable>'}}
};
template<class T>
@@ -94,8 +109,16 @@ namespace ptr_to_ptr_to_ref_counted {
};
TemplateList<RefCountable> list;
- struct SafeList {
+ struct FormerlySafeList {
RefPtr<RefCountable>* elements;
+ // expected-warning@-1{{Member variable 'elements' in
'ptr_to_ptr_to_ref_counted::FormerlySafeList' is a raw pointer to
'RefPtr<RefCountable>'}}
+ };
+
+ struct Container {
+ RefPtr<CheckedObj>* [[clang::annotate_type("webkit.unsafeptr")]] elements1;
+ RefPtr<CheckedObj>** [[clang::annotate_type("webkit.unsafeptr")]]
elements2;
+ // expected-warning@-1{{Member variable 'elements2' in
'ptr_to_ptr_to_ref_counted::Container' contains a raw pointer to
'RefPtr<CheckedObj>'}}
+ Ref<CheckedObj>* [[clang::annotate_type("webkit.unsafeptr")]]*
[[clang::annotate_type("webkit.unsafeptr")]] elements3;
};
} // namespace ptr_to_ptr_to_ref_counted
diff --git a/clang/test/Analysis/Checkers/WebKit/unretained-members-arc.mm
b/clang/test/Analysis/Checkers/WebKit/unretained-members-arc.mm
index 4eef372d26480..aa5793fac9b3c 100644
--- a/clang/test/Analysis/Checkers/WebKit/unretained-members-arc.mm
+++ b/clang/test/Analysis/Checkers/WebKit/unretained-members-arc.mm
@@ -22,6 +22,14 @@
// expected-warning@-1{{Member variable 'e' in 'members::Foo' is a retainable
type 'CFMutableArrayRef'}}
dispatch_queue_t f = nullptr;
+
+ RetainPtr<SomeObj>* g = nullptr;
+// expected-warning@-1{{Member variable 'g' in 'members::Foo' is a raw pointer
to 'WTF::RetainPtrArc<SomeObj>'}}
+ RetainPtr<SomeObj>** h = nullptr;
+// expected-warning@-1{{Member variable 'h' in 'members::Foo' contains a raw
pointer to 'WTF::RetainPtrArc<SomeObj>'}}
+ RetainPtr<SomeObj>* [[clang::annotate_type("webkit.unsafeptr")]] i =
nullptr;
+ RetainPtr<SomeObj>** [[clang::annotate_type("webkit.unsafeptr")]] j =
nullptr;
+// expected-warning@-1{{Member variable 'j' in 'members::Foo' contains a raw
pointer to 'WTF::RetainPtrArc<SomeObj>'}}
};
union FooUnion {
@@ -37,6 +45,12 @@
S y;
// expected-warning@-1{{Member variable 'y' in 'members::FooTmpl<SomeObj,
__CFArray *, NSObject<OS_dispatch_queue> *>' is a raw pointer to retainable
type}}
R z;
+ RetainPtr<T>* t;
+// expected-warning@-1{{Member variable 't' in 'members::FooTmpl<SomeObj,
__CFArray *, NSObject<OS_dispatch_queue> *>' is a raw pointer to
'WTF::RetainPtrArc<SomeObj>'}}
+ S* u;
+// expected-warning@-1{{Member variable 'u' in 'members::FooTmpl<SomeObj,
__CFArray *, NSObject<OS_dispatch_queue> *>' contains a raw pointer to
retainable type}}
+ RetainPtr<S>* v;
+// expected-warning@-1{{Member variable 'v' in 'members::FooTmpl<SomeObj,
__CFArray *, NSObject<OS_dispatch_queue> *>' is a raw pointer to
'WTF::RetainPtrArc<__CFArray *>'}}
};
void forceTmplToInstantiate(FooTmpl<SomeObj, CFMutableArrayRef,
dispatch_queue_t>) {}
@@ -64,12 +78,21 @@ void forceTmplToInstantiate(FooTmpl<SomeObj,
CFMutableArrayRef, dispatch_queue_t
};
TemplateList<SomeObj, CFMutableArrayRef, dispatch_queue_t> list;
- struct SafeList {
+ struct FormerlySafeList {
RetainPtr<SomeObj>* elements1;
+ // expected-warning@-1{{Member variable 'elements1' in
'ptr_to_ptr_to_retained::FormerlySafeList' is a raw pointer to
'WTF::RetainPtrArc<SomeObj>'}}
RetainPtr<CFMutableArrayRef>* elements2;
+ // expected-warning@-1{{Member variable 'elements2' in
'ptr_to_ptr_to_retained::FormerlySafeList' is a raw pointer to
'WTF::RetainPtrArc<__CFArray *>'}}
OSObjectPtr<dispatch_queue_t> elements3;
};
+ struct Container {
+ RetainPtr<SomeObj>* [[clang::annotate_type("webkit.unsafeptr")]] elements1;
+ RetainPtr<CFMutableArrayRef>**
[[clang::annotate_type("webkit.unsafeptr")]] elements2;
+ // expected-warning@-1{{Member variable 'elements2' in
'ptr_to_ptr_to_retained::Container' contains a raw pointer to
'WTF::RetainPtrArc<__CFArray *>'}}
+ RetainPtr<SomeObj>* [[clang::annotate_type("webkit.unsafeptr")]]*
[[clang::annotate_type("webkit.unsafeptr")]] elements3;
+ };
+
} // namespace ptr_to_ptr_to_retained
@interface AnotherObject : NSObject {
@@ -77,6 +100,10 @@ @interface AnotherObject : NSObject {
CFStringRef cf_string;
// expected-warning@-1{{Instance variable 'cf_string' in 'AnotherObject' is
a retainable type 'CFStringRef'; member variables must be a RetainPtr}}
dispatch_queue_t queue;
+ RetainPtr<SomeObj>* retainptr_ptr;
+ // expected-warning@-1{{Instance variable 'retainptr_ptr' in 'AnotherObject'
is a raw pointer to 'WTF::RetainPtrArc<SomeObj>'}}
+ RetainPtr<SomeObj>** retainptr_ptr_ptr;
+ // expected-warning@-1{{Instance variable 'retainptr_ptr_ptr' in
'AnotherObject' contains a raw pointer to 'WTF::RetainPtrArc<SomeObj>'}}
}
@property(nonatomic, strong) NSString *prop_string1;
@property(nonatomic, assign) NSString *prop_string2;
@@ -85,6 +112,8 @@ @interface AnotherObject : NSObject {
// expected-warning@-1{{Property 'prop_string3' in 'AnotherObject' is a raw
pointer to retainable type 'NSString'; member variables must be a RetainPtr}}
@property(nonatomic, readonly) NSString *prop_string4;
@property(nonatomic, readonly) NSString *prop_safe;
+@property(nonatomic, readonly) RetainPtr<NSString> *prop_retainptr_ptr;
+// expected-warning@-1{{Property 'prop_retainptr_ptr' in 'AnotherObject' is a
raw pointer to 'WTF::RetainPtrArc<NSString>'}}
@end
@implementation AnotherObject
@@ -107,6 +136,10 @@ @interface NoSynthObject : NSObject {
NSString *ns_string;
CFStringRef cf_string;
// expected-warning@-1{{Instance variable 'cf_string' in 'NoSynthObject' is
a retainable type 'CFStringRef'; member variables must be a RetainPtr}}
+ RetainPtr<NSString> *ns_string_retainptr_ptr;
+ // expected-warning@-1{{Instance variable 'ns_string_retainptr_ptr' in
'NoSynthObject' is a raw pointer to 'WTF::RetainPtrArc<NSString>'}}
+ RetainPtr<NSString> **ns_string_retainptr_ptr_ptr;
+ // expected-warning@-1{{Instance variable 'ns_string_retainptr_ptr_ptr' in
'NoSynthObject' contains a raw pointer to 'WTF::RetainPtrArc<NSString>'}}
}
@property(nonatomic, readonly, strong) NSString *prop_string1;
@property(nonatomic, readonly, strong) NSString *prop_string2;
@@ -116,6 +149,9 @@ @interface NoSynthObject : NSObject {
// expected-warning@-1{{Property 'prop_string4' in 'NoSynthObject' is a raw
pointer to retainable type 'NSString'; member variables must be a RetainPtr}}
@property(nonatomic, unsafe_unretained) dispatch_queue_t prop_string5;
// expected-warning@-1{{Property 'prop_string5' in 'NoSynthObject' is a
retainable type 'dispatch_queue_t'}}
+@property(nonatomic, readonly, strong) dispatch_queue_t dispatch;
+@property(nonatomic, readonly) RetainPtr<SomeObj> *prop_retainptr_ptr;
+// expected-warning@-1{{Property 'prop_retainptr_ptr' in 'NoSynthObject' is a
raw pointer to 'WTF::RetainPtrArc<SomeObj>'}}
@end
@implementation NoSynthObject
@@ -126,4 +162,8 @@ - (NSString *)prop_string1 {
@synthesize prop_string3;
@synthesize prop_string4;
@synthesize prop_string5;
+- (dispatch_queue_t)dispatch {
+ return nil;
+}
+@synthesize prop_retainptr_ptr;
@end
diff --git a/clang/test/Analysis/Checkers/WebKit/unretained-members.mm
b/clang/test/Analysis/Checkers/WebKit/unretained-members.mm
index 2b120b9b1385c..aaf08022d5f9f 100644
--- a/clang/test/Analysis/Checkers/WebKit/unretained-members.mm
+++ b/clang/test/Analysis/Checkers/WebKit/unretained-members.mm
@@ -33,6 +33,16 @@
CFMutableArrayRef e = nullptr;
// expected-warning@-1{{Member variable 'e' in 'members::Foo' is a retainable
type 'CFMutableArrayRef'}}
+ SomeObj** f = nullptr;
+// expected-warning@-1{{Member variable 'f' in 'members::Foo' contains a raw
pointer to retainable type}}
+
+ RetainPtr<SomeObj>* g = nullptr;
+// expected-warning@-1{{Member variable 'g' in 'members::Foo' is a raw pointer
to 'WTF::RetainPtr<SomeObj>'}}
+ RetainPtr<SomeObj>** h = nullptr;
+// expected-warning@-1{{Member variable 'h' in 'members::Foo' contains a raw
pointer to 'WTF::RetainPtr<SomeObj>'}}
+ RetainPtr<SomeObj>* [[clang::annotate_type("webkit.unsafeptr")]] i =
nullptr;
+ RetainPtr<SomeObj>** [[clang::annotate_type("webkit.unsafeptr")]] j =
nullptr;
+// expected-warning@-1{{Member variable 'j' in 'members::Foo' contains a raw
pointer to 'WTF::RetainPtr<SomeObj>'}}
};
template<class T, class S, class R>
@@ -43,6 +53,14 @@
// expected-warning@-1{{Member variable 'b' in 'members::FooTmpl<SomeObj,
__CFArray *, NSObject<OS_dispatch_queue> *>' is a raw pointer to retainable
type}}
R c;
// expected-warning@-1{{Member variable 'c' in 'members::FooTmpl<SomeObj,
__CFArray *, NSObject<OS_dispatch_queue> *>' is a raw pointer to retainable
type}}
+ T** d;
+// expected-warning@-1{{Member variable 'd' in 'members::FooTmpl<SomeObj,
__CFArray *, NSObject<OS_dispatch_queue> *>' contains a raw pointer to
retainable type}}
+ RetainPtr<T>* e;
+// expected-warning@-1{{Member variable 'e' in 'members::FooTmpl<SomeObj,
__CFArray *, NSObject<OS_dispatch_queue> *>' is a raw pointer to
'WTF::RetainPtr<SomeObj>'}}
+ S* f;
+// expected-warning@-1{{Member variable 'f' in 'members::FooTmpl<SomeObj,
__CFArray *, NSObject<OS_dispatch_queue> *>' contains a raw pointer to
retainable type}}
+ RetainPtr<S>* g;
+// expected-warning@-1{{Member variable 'g' in 'members::FooTmpl<SomeObj,
__CFArray *, NSObject<OS_dispatch_queue> *>' is a raw pointer to
'WTF::RetainPtr<__CFArray *>'}}
};
void forceTmplToInstantiate(FooTmpl<SomeObj, CFMutableArrayRef,
dispatch_queue_t>) {}
@@ -64,6 +82,10 @@ void forceTmplToInstantiate(FooTmpl<SomeObj,
CFMutableArrayRef, dispatch_queue_t
// expected-warning@-1{{Member variable 'c' in 'unions::Foo' is a
retainable type 'CFMutableArrayRef'}}
dispatch_queue_t d;
// expected-warning@-1{{Member variable 'd' in 'unions::Foo' is a
retainable type 'dispatch_queue_t'}}
+ SomeObj** e;
+ // expected-warning@-1{{Member variable 'e' in 'unions::Foo' contains a
raw pointer to retainable type 'SomeObj'}}
+ RetainPtr<SomeObj>* f;
+ // expected-warning@-1{{Member variable 'f' in 'unions::Foo' is a raw
pointer to 'WTF::RetainPtr<SomeObj>'}}
};
template<class T>
@@ -97,9 +119,18 @@ void forceTmplToInstantiate(FooTempl<SomeObj>) {}
};
TemplateList<SomeObj, CFMutableArrayRef, dispatch_queue_t> list;
- struct SafeList {
+ struct FormerlySafeList {
RetainPtr<SomeObj>* elements1;
+ // expected-warning@-1{{Member variable 'elements1' in
'ptr_to_ptr_to_retained::FormerlySafeList' is a raw pointer to
'WTF::RetainPtr<SomeObj>'}}
RetainPtr<CFMutableArrayRef>* elements2;
+ // expected-warning@-1{{Member variable 'elements2' in
'ptr_to_ptr_to_retained::FormerlySafeList' is a raw pointer to
'WTF::RetainPtr<__CFArray *>'}}
+ };
+
+ struct Container {
+ RetainPtr<SomeObj>* [[clang::annotate_type("webkit.unsafeptr")]] elements1;
+ RetainPtr<CFMutableArrayRef>**
[[clang::annotate_type("webkit.unsafeptr")]] elements2;
+ // expected-warning@-1{{Member variable 'elements2' in
'ptr_to_ptr_to_retained::Container' contains a raw pointer to
'WTF::RetainPtr<__CFArray *>'}}
+ RetainPtr<SomeObj>* [[clang::annotate_type("webkit.unsafeptr")]]*
[[clang::annotate_type("webkit.unsafeptr")]] elements3;
};
} // namespace ptr_to_ptr_to_retained
@@ -111,9 +142,19 @@ @interface AnotherObject : NSObject {
// expected-warning@-1{{Instance variable 'cf_string' in 'AnotherObject' is
a retainable type 'CFStringRef'}}
dispatch_queue_t dispatch;
// expected-warning@-1{{Instance variable 'dispatch' in 'AnotherObject' is a
retainable type 'dispatch_queue_t'}}
+ NSString **ns_string_ptr;
+ // expected-warning@-1{{Instance variable 'ns_string_ptr' in 'AnotherObject'
contains a raw pointer to retainable type 'NSString'}}
+ RetainPtr<SomeObj>* retainptr_ptr;
+ // expected-warning@-1{{Instance variable 'retainptr_ptr' in 'AnotherObject'
is a raw pointer to 'WTF::RetainPtr<SomeObj>'}}
+ RetainPtr<SomeObj>** retainptr_ptr_ptr;
+ // expected-warning@-1{{Instance variable 'retainptr_ptr_ptr' in
'AnotherObject' contains a raw pointer to 'WTF::RetainPtr<SomeObj>'}}
}
@property(nonatomic, readonly, strong) NSString *prop_string;
@property(nonatomic, readonly) NSString *prop_safe;
+@property(nonatomic, readonly) NSString **prop_unsafe;
+// expected-warning@-1{{Property 'prop_unsafe' in 'AnotherObject' contains a
raw pointer to retainable type 'NSString'}}
+@property(nonatomic, readonly) RetainPtr<NSString> *prop_retainptr_ptr;
+// expected-warning@-1{{Property 'prop_retainptr_ptr' in 'AnotherObject' is a
raw pointer to 'WTF::RetainPtr<NSString>'}}
@end
@implementation AnotherObject
@@ -129,6 +170,12 @@ @interface DerivedObject : AnotherObject {
// expected-warning@-1{{Instance variable 'cg_image' in 'DerivedObject' is a
retainable type 'CGImageRef'}}
dispatch_queue_t os_dispatch;
// expected-warning@-1{{Instance variable 'os_dispatch' in 'DerivedObject'
is a retainable type 'dispatch_queue_t'}}
+ NSNumber **ns_number_ptr;
+ // expected-warning@-1{{Instance variable 'ns_number_ptr' in 'DerivedObject'
contains a raw pointer to retainable type 'NSNumber'}}
+ RetainPtr<CGImageRef>* cg_image_retainptr_ptr;
+ // expected-warning@-1{{Instance variable 'cg_image_retainptr_ptr' in
'DerivedObject' is a raw pointer to 'WTF::RetainPtr<CGImage *>'}}
+ RetainPtr<CGImageRef>** cg_image_retainptr_ptr_ptr;
+ // expected-warning@-1{{Instance variable 'cg_image_retainptr_ptr_ptr' in
'DerivedObject' contains a raw pointer to 'WTF::RetainPtr<CGImage *>'}}
}
@property(nonatomic, strong) NSNumber *prop_number;
@property(nonatomic, readonly) NSString *prop_string;
@@ -173,6 +220,12 @@ @interface NoSynthObject : NSObject {
// expected-warning@-1{{Instance variable 'cf_string' in 'NoSynthObject' is
a retainable type 'CFStringRef'}}
dispatch_queue_t dispatch;
// expected-warning@-1{{Instance variable 'dispatch' in 'NoSynthObject' is a
retainable type 'dispatch_queue_t'}}
+ NSString **ns_string_ptr;
+ // expected-warning@-1{{Instance variable 'ns_string_ptr' in 'NoSynthObject'
contains a raw pointer to retainable type 'NSString'}}
+ RetainPtr<NSString> *ns_string_retainptr_ptr;
+ // expected-warning@-1{{Instance variable 'ns_string_retainptr_ptr' in
'NoSynthObject' is a raw pointer to 'WTF::RetainPtr<NSString>'}}
+ RetainPtr<NSString> **ns_string_retainptr_ptr_ptr;
+ // expected-warning@-1{{Instance variable 'ns_string_retainptr_ptr_ptr' in
'NoSynthObject' contains a raw pointer to 'WTF::RetainPtr<NSString>'}}
}
@property(nonatomic, readonly, strong) NSString *prop_string1;
@property(nonatomic, readonly, strong) NSString *prop_string2;
@@ -182,6 +235,9 @@ @interface NoSynthObject : NSObject {
// expected-warning@-1{{Property 'prop_string4' in 'NoSynthObject' is a raw
pointer to retainable type 'NSString'; member variables must be a RetainPtr}}
@property(nonatomic, copy) NSString *prop_string5;
@property(nonatomic, readonly, strong) dispatch_queue_t dispatch;
+@property(nonatomic, readonly) NSString **ns_string_ptr;
+@property(nonatomic, readonly) RetainPtr<SomeObj> *prop_retainptr_ptr;
+// expected-warning@-1{{Property 'prop_retainptr_ptr' in 'NoSynthObject' is a
raw pointer to 'WTF::RetainPtr<SomeObj>'}}
@end
@implementation NoSynthObject
@@ -195,4 +251,9 @@ - (NSString *)prop_string1 {
- (dispatch_queue_t)dispatch {
return nil;
}
+- (NSString **)ns_string_ptr {
+ return nullptr;
+}
+@synthesize prop_retainptr_ptr;
+
@end
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits