Skip to content

Merge Webkit checker fixes #10219

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 42 commits into
base: stable/20240723
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
2cd9739
[alpha.webkit.UnretainedLocalVarsChecker] Add a checker for local var…
rniwa Feb 24, 2025
2570f73
[analyzer] Handle structured bindings in alpha.webkit.UncountedCallAr…
ojhunt Mar 3, 2025
e31b229
[alpha.webkit.UncountedCallArgsChecker] Recognize CXXUnresolvedConstr…
rniwa Mar 7, 2025
7314aa9
[alpha.webkit.UnretainedLambdaCapturesChecker] Add a WebKit checker f…
rniwa Mar 9, 2025
3985c3f
[alpha.webkit.NoUnretainedMemberChecker] Add a new WebKit checker for…
rniwa Mar 10, 2025
a322a7e
[WebKit checkers] Don't treat virtual functions as safe. (#129632)
rniwa Mar 11, 2025
d3dbb05
[webkit.UncountedLambdaCapturesChecker] Recognize std::move(protected…
rniwa Mar 12, 2025
b30d426
Add unretained call args checker (#130901)
rniwa Mar 12, 2025
28c78ae
[alpha.webkit.UncountedCallArgsChecker] Treat an explicit constructio…
rniwa Mar 13, 2025
2fae802
[alpha.webkit.webkit.RetainPtrCtorAdoptChecker] Add a new WebKit chec…
rniwa Mar 13, 2025
63ddb4a
[alpha.webkit.ForwardDeclChecker] Add a new WebKit checker for forwar…
rniwa Mar 13, 2025
fd04eee
[alpha.webkit.UncountedCallArgsChecker] os_log functions should be tr…
rniwa Mar 18, 2025
2a385be
[webkit.NoUncountedMemberChecker] Fix a regression that every class i…
rniwa Mar 18, 2025
50ad6fb
[webkit.RefCntblBaseVirtualDtor] Add support for NoVirtualDestructorB…
rniwa Mar 26, 2025
41ffb47
[WebKit Checkers] Recognize Objective-C and CF pointer conversion fun…
rniwa Mar 27, 2025
1069648
Fix the assertion failure in Analysis/Checkers/WebKit/forward-decl-ch…
rniwa Mar 27, 2025
dc2a681
[alpha.webkit.RawPtrRefMemberChecker] The checker doesn't warn Object…
rniwa Mar 29, 2025
ea79d25
[alpha.webkit.NoUnretainedMemberChecker] Ignore system-header-defined…
rniwa Mar 31, 2025
fd32611
[WebKit checkers] Treat Objective-C message send return value as safe…
rniwa Apr 4, 2025
e250d22
[alpha.webkit.ForwardDeclChecker] Ignore forward declared struct. (#1…
rniwa Apr 4, 2025
fcfed41
[alpha.webkit.RetainPtrCtorAdoptChecker] Recognize mutableCopy from l…
rniwa Apr 9, 2025
81d9f2f
[alpha.webkit.UnretainedLambdaCapturesChecker] Add the support for pr…
rniwa Apr 9, 2025
9ecfa43
[alpha.webkit.RetainPtrCtorAdoptChecker] Support adopt(cast(copy(~)) …
rniwa Apr 10, 2025
0e333cb
[alpha.webkit.ForwardDeclChecker] Recognize a forward declared templa…
rniwa Apr 10, 2025
abc6a12
Remove the redundant check for "WeakPtr" in isSmartPtrClass to fix th…
rniwa Apr 14, 2025
03c2480
[alpha.webkit.UnretainedCallArgsChecker] Don't emit a warning for Ret…
rniwa Apr 14, 2025
e0eb5fd
[alpha.webkit.UnretainedCallArgsChecker] Add the support for RetainPt…
rniwa Apr 16, 2025
3803f3b
[WebKit checkers] Treat global const variables as safe (#136170)
rniwa Apr 22, 2025
daeb3ec
[RawPtrRefMemberChecker] Member variable checker should allow T* in s…
rniwa Apr 23, 2025
5611587
[alpha.webkit.UncheckedCallArgsChecker] Checker fails to recognize Ca…
rniwa Apr 24, 2025
41f92b6
[webkit.UncountedLambdaCapturesChecker] Treat a call to lambda functi…
rniwa Apr 25, 2025
04ec1fb
[alpha.webkit.RetainPtrCtorAdoptChecker] An assortment of small enhan…
rniwa Apr 27, 2025
e8e99e6
[alpha.webkit.RetainPtrCtorAdoptChecker] Check nullity before calling…
rniwa Apr 27, 2025
a8a2a22
[RawPtrRefMemberChecker] Make RawPtrRefMemberChecker consistent with …
rniwa Apr 30, 2025
e76e829
[WebKit checkers] Treat std::bit_cast as a pointer conversion (#137476)
rniwa May 1, 2025
d236bc8
[webkit.UncountedLambdaCapturesChecker] Treat a copy capture of a Che…
rniwa May 7, 2025
bef9d8b
[RawPtrRefMemberChecker] Add the support for union and pointers to un…
rniwa May 10, 2025
73fdce8
[webkit.UncountedLambdaCapturesChecker] Treat every argument of std::…
rniwa May 10, 2025
98829b6
[alpha.webkit.UncheckedCallArgsChecker] Forwarding r-value reference …
rniwa Jun 6, 2025
4121fde
[WebKit checkers] Treat passing of a member variable which is capable…
rniwa Jun 9, 2025
1751397
[WebKit checkers] Add an annotation for pointer conversion. (#141277)
rniwa Jun 9, 2025
0a39858
[alpha.webkit.NoUnretainedMemberChecker] Recognize NS_REQUIRES_PROPER…
rniwa Jun 10, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
[alpha.webkit.NoUnretainedMemberChecker] Add a new WebKit checker for…
… unretained member variables and ivars. (llvm#128641)

Add a new WebKit checker for member variables and instance variables of
NS and CF types. A member variable or instance variable to a CF type
should be RetainPtr regardless of whether ARC is enabled or disabled,
and that of a NS type should be RetainPtr when ARC is disabled.
  • Loading branch information
rniwa authored and Ryosuke Niwa committed Apr 22, 2025
commit 3985c3f3edf75ccb075a65cb4dc60ae99f75a868
13 changes: 13 additions & 0 deletions clang/docs/analyzer/checkers.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3479,6 +3479,19 @@ Raw pointers and references to an object which supports CheckedPtr or CheckedRef

See `WebKit Guidelines for Safer C++ Programming <https://github.com/WebKit/WebKit/wiki/Safer-CPP-Guidelines>`_ for details.

alpha.webkit.NoUnretainedMemberChecker
""""""""""""""""""""""""""""""""""""""""
Raw pointers and references to a NS or CF object can't be used as class members or ivars. Only RetainPtr is allowed for CF types regardless of whether ARC is enabled or disabled. Only RetainPtr is allowed for NS types when ARC is disabled.

.. code-block:: cpp

struct Foo {
NSObject *ptr; // warn
// ...
};

See `WebKit Guidelines for Safer C++ Programming <https://github.com/WebKit/WebKit/wiki/Safer-CPP-Guidelines>`_ for details.

alpha.webkit.UnretainedLambdaCapturesChecker
""""""""""""""""""""""""""""""""""""""""""""
Raw pointers and references to NS or CF types can't be captured in lambdas. Only RetainPtr is allowed for CF types regardless of whether ARC is enabled or disabled, and only RetainPtr is allowed for NS types when ARC is disabled.
Expand Down
4 changes: 4 additions & 0 deletions clang/include/clang/StaticAnalyzer/Checkers/Checkers.td
Original file line number Diff line number Diff line change
Expand Up @@ -1795,6 +1795,10 @@ def NoUncheckedPtrMemberChecker : Checker<"NoUncheckedPtrMemberChecker">,
HelpText<"Check for no unchecked member variables.">,
Documentation<HasDocumentation>;

def NoUnretainedMemberChecker : Checker<"NoUnretainedMemberChecker">,
HelpText<"Check for no unretained member variables.">,
Documentation<HasDocumentation>;

def UnretainedLambdaCapturesChecker : Checker<"UnretainedLambdaCapturesChecker">,
HelpText<"Check unretained lambda captures.">,
Documentation<HasDocumentation>;
Expand Down
115 changes: 95 additions & 20 deletions clang/lib/StaticAnalyzer/Checkers/WebKit/RawPtrRefMemberChecker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,16 @@ class RawPtrRefMemberChecker
BugType Bug;
mutable BugReporter *BR;

protected:
mutable std::optional<RetainTypeChecker> RTC;

public:
RawPtrRefMemberChecker(const char *description)
: Bug(this, description, "WebKit coding guidelines") {}

virtual std::optional<bool>
isPtrCompatible(const clang::CXXRecordDecl *) const = 0;
isPtrCompatible(const clang::QualType,
const clang::CXXRecordDecl *R) const = 0;
virtual bool isPtrCls(const clang::CXXRecordDecl *) const = 0;
virtual const char *typeName() const = 0;
virtual const char *invariant() const = 0;
Expand All @@ -58,6 +62,12 @@ class RawPtrRefMemberChecker
bool shouldVisitTemplateInstantiations() const { return true; }
bool shouldVisitImplicitCode() const { return false; }

bool VisitTypedefDecl(const TypedefDecl *TD) {
if (Checker->RTC)
Checker->RTC->visitTypedef(TD);
return true;
}

bool VisitRecordDecl(const RecordDecl *RD) {
Checker->visitRecordDecl(RD);
return true;
Expand All @@ -70,6 +80,8 @@ class RawPtrRefMemberChecker
};

LocalVisitor visitor(this);
if (RTC)
RTC->visitTranslationUnitDecl(TUD);
visitor.TraverseDecl(const_cast<TranslationUnitDecl *>(TUD));
}

Expand All @@ -78,16 +90,22 @@ class RawPtrRefMemberChecker
return;

for (auto *Member : RD->fields()) {
const Type *MemberType = Member->getType().getTypePtrOrNull();
auto QT = Member->getType();
const Type *MemberType = QT.getTypePtrOrNull();
if (!MemberType)
continue;

if (auto *MemberCXXRD = MemberType->getPointeeCXXRecordDecl()) {
// If we don't see the definition we just don't know.
if (MemberCXXRD->hasDefinition()) {
std::optional<bool> isRCAble = isPtrCompatible(MemberCXXRD);
if (isRCAble && *isRCAble)
reportBug(Member, MemberType, MemberCXXRD, RD);
std::optional<bool> IsCompatible = isPtrCompatible(QT, MemberCXXRD);
if (IsCompatible && *IsCompatible)
reportBug(Member, MemberType, MemberCXXRD, RD);
} else {
std::optional<bool> IsCompatible = isPtrCompatible(QT, nullptr);
auto *PointeeType = MemberType->getPointeeType().getTypePtrOrNull();
if (IsCompatible && *IsCompatible) {
auto *Desugared = PointeeType->getUnqualifiedDesugaredType();
if (auto *ObjCType = dyn_cast_or_null<ObjCInterfaceType>(Desugared))
reportBug(Member, MemberType, ObjCType->getDecl(), RD);
}
}
}
Expand All @@ -108,11 +126,12 @@ class RawPtrRefMemberChecker

void visitIvarDecl(const ObjCContainerDecl *CD,
const ObjCIvarDecl *Ivar) const {
const Type *IvarType = Ivar->getType().getTypePtrOrNull();
auto QT = Ivar->getType();
const Type *IvarType = QT.getTypePtrOrNull();
if (!IvarType)
return;
if (auto *IvarCXXRD = IvarType->getPointeeCXXRecordDecl()) {
std::optional<bool> IsCompatible = isPtrCompatible(IvarCXXRD);
std::optional<bool> IsCompatible = isPtrCompatible(QT, IvarCXXRD);
if (IsCompatible && *IsCompatible)
reportBug(Ivar, IvarType, IvarCXXRD, CD);
}
Expand Down Expand Up @@ -152,13 +171,13 @@ class RawPtrRefMemberChecker
return false;
}

template <typename DeclType, typename ParentDeclType>
template <typename DeclType, typename PointeeType, typename ParentDeclType>
void reportBug(const DeclType *Member, const Type *MemberType,
const CXXRecordDecl *MemberCXXRD,
const PointeeType *Pointee,
const ParentDeclType *ClassCXXRD) const {
assert(Member);
assert(MemberType);
assert(MemberCXXRD);
assert(Pointee);

SmallString<100> Buf;
llvm::raw_svector_ostream Os(Buf);
Expand All @@ -170,10 +189,13 @@ class RawPtrRefMemberChecker
printQuotedName(Os, Member);
Os << " in ";
printQuotedQualifiedName(Os, ClassCXXRD);
Os << " is a "
<< (isa<PointerType>(MemberType) ? "raw pointer" : "reference") << " to "
<< typeName() << " ";
printQuotedQualifiedName(Os, MemberCXXRD);
Os << " is a ";
if (printPointer(Os, MemberType) == PrintDeclKind::Pointer) {
auto Typedef = MemberType->getAs<TypedefType>();
assert(Typedef);
printQuotedQualifiedName(Os, Typedef->getDecl());
} else
printQuotedQualifiedName(Os, Pointee);
Os << "; " << invariant() << ".";

PathDiagnosticLocation BSLoc(Member->getSourceRange().getBegin(),
Expand All @@ -182,6 +204,15 @@ class RawPtrRefMemberChecker
Report->addRange(Member->getSourceRange());
BR->emitReport(std::move(Report));
}

enum class PrintDeclKind { Pointee, Pointer };
virtual PrintDeclKind printPointer(llvm::raw_svector_ostream &Os,
const Type *T) const {
T = T->getUnqualifiedDesugaredType();
bool IsPtr = isa<PointerType>(T) || isa<ObjCObjectPointerType>(T);
Os << (IsPtr ? "raw pointer" : "reference") << " to " << typeName() << " ";
return PrintDeclKind::Pointee;
}
};

class NoUncountedMemberChecker final : public RawPtrRefMemberChecker {
Expand All @@ -191,8 +222,9 @@ class NoUncountedMemberChecker final : public RawPtrRefMemberChecker {
"reference-countable type") {}

std::optional<bool>
isPtrCompatible(const clang::CXXRecordDecl *R) const final {
return isRefCountable(R);
isPtrCompatible(const clang::QualType,
const clang::CXXRecordDecl *R) const final {
return R && isRefCountable(R);
}

bool isPtrCls(const clang::CXXRecordDecl *R) const final {
Expand All @@ -213,8 +245,9 @@ class NoUncheckedPtrMemberChecker final : public RawPtrRefMemberChecker {
"checked-pointer capable type") {}

std::optional<bool>
isPtrCompatible(const clang::CXXRecordDecl *R) const final {
return isCheckedPtrCapable(R);
isPtrCompatible(const clang::QualType,
const clang::CXXRecordDecl *R) const final {
return R && isCheckedPtrCapable(R);
}

bool isPtrCls(const clang::CXXRecordDecl *R) const final {
Expand All @@ -229,6 +262,40 @@ class NoUncheckedPtrMemberChecker final : public RawPtrRefMemberChecker {
}
};

class NoUnretainedMemberChecker final : public RawPtrRefMemberChecker {
public:
NoUnretainedMemberChecker()
: RawPtrRefMemberChecker("Member variable is a raw-pointer/reference to "
"retainable type") {
RTC = RetainTypeChecker();
}

std::optional<bool>
isPtrCompatible(const clang::QualType QT,
const clang::CXXRecordDecl *) const final {
return RTC->isUnretained(QT);
}

bool isPtrCls(const clang::CXXRecordDecl *R) const final {
return isRetainPtr(R);
}

const char *typeName() const final { return "retainable type"; }

const char *invariant() const final {
return "member variables must be a RetainPtr";
}

PrintDeclKind printPointer(llvm::raw_svector_ostream &Os,
const Type *T) const final {
if (!isa<ObjCObjectPointerType>(T) && T->getAs<TypedefType>()) {
Os << typeName() << " ";
return PrintDeclKind::Pointer;
}
return RawPtrRefMemberChecker::printPointer(Os, T);
}
};

} // namespace

void ento::registerNoUncountedMemberChecker(CheckerManager &Mgr) {
Expand All @@ -247,3 +314,11 @@ bool ento::shouldRegisterNoUncheckedPtrMemberChecker(
const CheckerManager &Mgr) {
return true;
}

void ento::registerNoUnretainedMemberChecker(CheckerManager &Mgr) {
Mgr.registerChecker<NoUnretainedMemberChecker>();
}

bool ento::shouldRegisterNoUnretainedMemberChecker(const CheckerManager &Mgr) {
return true;
}
39 changes: 39 additions & 0 deletions clang/test/Analysis/Checkers/WebKit/unretained-members-arc.mm
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
// RUN: %clang_analyze_cc1 -analyzer-checker=alpha.webkit.NoUnretainedMemberChecker -fobjc-arc -verify %s

#include "objc-mock-types.h"

namespace members {

struct Foo {
private:
SomeObj* a = nullptr;

[[clang::suppress]]
SomeObj* a_suppressed = nullptr;

protected:
RetainPtr<SomeObj> b;

public:
SomeObj* c = nullptr;
RetainPtr<SomeObj> d;

CFMutableArrayRef e = nullptr;
// expected-warning@-1{{Member variable 'e' in 'members::Foo' is a retainable type 'CFMutableArrayRef'}}
};

template<class T, class S>
struct FooTmpl {
T* x;
S y;
// expected-warning@-1{{Member variable 'y' in 'members::FooTmpl<SomeObj, __CFArray *>' is a raw pointer to retainable type}}
};

void forceTmplToInstantiate(FooTmpl<SomeObj, CFMutableArrayRef>) {}

struct [[clang::suppress]] FooSuppressed {
private:
SomeObj* a = nullptr;
};

}
60 changes: 60 additions & 0 deletions clang/test/Analysis/Checkers/WebKit/unretained-members.mm
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
// RUN: %clang_analyze_cc1 -analyzer-checker=alpha.webkit.NoUnretainedMemberChecker -verify %s

#include "objc-mock-types.h"

namespace members {

struct Foo {
private:
SomeObj* a = nullptr;
// expected-warning@-1{{Member variable 'a' in 'members::Foo' is a raw pointer to retainable type}}

[[clang::suppress]]
SomeObj* a_suppressed = nullptr;
// No warning.

protected:
RetainPtr<SomeObj> b;
// No warning.

public:
SomeObj* c = nullptr;
// expected-warning@-1{{Member variable 'c' in 'members::Foo' is a raw pointer to retainable type}}
RetainPtr<SomeObj> d;

CFMutableArrayRef e = nullptr;
// expected-warning@-1{{Member variable 'e' in 'members::Foo' is a retainable type 'CFMutableArrayRef'}}
};

template<class T, class S>
struct FooTmpl {
T* a;
// expected-warning@-1{{Member variable 'a' in 'members::FooTmpl<SomeObj, __CFArray *>' is a raw pointer to retainable type}}
S b;
// expected-warning@-1{{Member variable 'b' in 'members::FooTmpl<SomeObj, __CFArray *>' is a raw pointer to retainable type}}
};

void forceTmplToInstantiate(FooTmpl<SomeObj, CFMutableArrayRef>) {}

struct [[clang::suppress]] FooSuppressed {
private:
SomeObj* a = nullptr;
// No warning.
};

}

namespace ignore_unions {
union Foo {
SomeObj* a;
RetainPtr<SomeObj> b;
CFMutableArrayRef c;
};

template<class T>
union RefPtr {
T* a;
};

void forceTmplToInstantiate(RefPtr<SomeObj>) {}
}