https://github.com/rniwa updated https://github.com/llvm/llvm-project/pull/200481
>From f3c6e2fd76a05aa15c6670811bc1a17d170ace87 Mon Sep 17 00:00:00 2001 From: Ryosuke Niwa <[email protected]> Date: Fri, 29 May 2026 11:48:52 -0700 Subject: [PATCH 1/2] [alpha.webkit.NoDeleteChecker] Returning with copy elision should be considered no-delete. Ignore the destructor of CXXBindTemporaryExpr when returning a value with copy elision. --- .../Checkers/WebKit/PtrTypesSemantics.cpp | 14 +++++++++++++- clang/test/Analysis/Checkers/WebKit/mock-types.h | 1 + .../Checkers/WebKit/nodelete-annotation.cpp | 12 +++++++++++- 3 files changed, 25 insertions(+), 2 deletions(-) diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp index f1515701cc6f3..6ce2954471e6b 100644 --- a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp @@ -706,8 +706,20 @@ class TrivialFunctionAnalysisVisitor bool VisitReturnStmt(const ReturnStmt *RS) { // A return statement is allowed as long as the return value is trivial. - if (auto *RV = RS->getRetValue()) + if (auto *RV = RS->getRetValue()) { + if (auto *ExprWithClean = dyn_cast<ExprWithCleanups>(RV)) { + if (ExprWithClean->isPRValue()) + RV = ExprWithClean->getSubExpr(); + } + if (auto *SubE = RV->IgnoreParenCasts()) { + if (auto *BTE = dyn_cast<CXXBindTemporaryExpr>(SubE)) { + // Ignore the destructor of BTE if copy elision is in effect. + if (RV->getType() == BTE->getType()) + return Visit(BTE->getSubExpr()); + } + } return Visit(RV); + } return true; } diff --git a/clang/test/Analysis/Checkers/WebKit/mock-types.h b/clang/test/Analysis/Checkers/WebKit/mock-types.h index af63268ac9695..de5c2d35f2408 100644 --- a/clang/test/Analysis/Checkers/WebKit/mock-types.h +++ b/clang/test/Analysis/Checkers/WebKit/mock-types.h @@ -202,6 +202,7 @@ template <typename T> struct RefPtr { t = o.t; o.t = tmp; } + operator T*() { return t; } T *get() const { return t; } T *operator->() const { return t; } T &operator*() const { return *t; } diff --git a/clang/test/Analysis/Checkers/WebKit/nodelete-annotation.cpp b/clang/test/Analysis/Checkers/WebKit/nodelete-annotation.cpp index 6906afb7fa0f6..871b0afcb89fd 100644 --- a/clang/test/Analysis/Checkers/WebKit/nodelete-annotation.cpp +++ b/clang/test/Analysis/Checkers/WebKit/nodelete-annotation.cpp @@ -316,10 +316,19 @@ class Derived : public Base<Type> { }; struct Data { - static Ref<Data> create() { + static Ref<Data> [[clang::annotate_type("webkit.nodelete")]] create() { return adoptRef(*new Data); } + static Ref<Data> [[clang::annotate_type("webkit.nodelete")]] create(double) { + return adoptRef(*new Data(RefCountable::create()->next())); + // expected-warning@-1{{A function 'create' has [[clang::annotate_type("webkit.nodelete")]] but it contains code that could destruct an object}} + } + + static Data* [[clang::annotate_type("webkit.nodelete")]] create(int) { + return adoptRef(new Data); // expected-warning{{A function 'create' has [[clang::annotate_type("webkit.nodelete")]] but it contains code that could destruct an object}} + } + void ref() { ++refCount; } @@ -338,6 +347,7 @@ struct Data { protected: Data() = default; + Data(RefCountable*) { } private: unsigned refCount { 0 }; >From 934e9ec9a651a993544f56f0e8157aee924edcd5 Mon Sep 17 00:00:00 2001 From: Ryosuke Niwa <[email protected]> Date: Sat, 30 May 2026 00:46:23 -0700 Subject: [PATCH 2/2] Extend copy elision check to function arguments. When the return value of T g() is given to f(T&&), skip the checking of the destructor of T in ExprWithCleanups / CXXBindTemporaryExpr. i.e. assume no deletion of T happens at the end of expression although f's function body could delete T and we do continue to check that. --- .../Checkers/WebKit/PtrTypesSemantics.cpp | 34 +++++++++--------- .../Analysis/Checkers/WebKit/call-args.cpp | 17 +++++++++ .../WebKit/nodelete-lazy-initialize.cpp | 36 +++++++++++++++++++ 3 files changed, 70 insertions(+), 17 deletions(-) create mode 100644 clang/test/Analysis/Checkers/WebKit/nodelete-lazy-initialize.cpp diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp index 6ce2954471e6b..aaa7ac3e4dd97 100644 --- a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp @@ -705,21 +705,8 @@ class TrivialFunctionAnalysisVisitor } bool VisitReturnStmt(const ReturnStmt *RS) { - // A return statement is allowed as long as the return value is trivial. - if (auto *RV = RS->getRetValue()) { - if (auto *ExprWithClean = dyn_cast<ExprWithCleanups>(RV)) { - if (ExprWithClean->isPRValue()) - RV = ExprWithClean->getSubExpr(); - } - if (auto *SubE = RV->IgnoreParenCasts()) { - if (auto *BTE = dyn_cast<CXXBindTemporaryExpr>(SubE)) { - // Ignore the destructor of BTE if copy elision is in effect. - if (RV->getType() == BTE->getType()) - return Visit(BTE->getSubExpr()); - } - } - return Visit(RV); - } + if (auto *RV = RS->getRetValue()) + return VisitIgnoringTempGivenToRValue(RV); return true; } @@ -899,15 +886,28 @@ class TrivialFunctionAnalysisVisitor bool checkArguments(const CallExpr *CE) { for (const Expr *Arg : CE->arguments()) { - if (Arg && !Visit(Arg)) + if (Arg && !VisitIgnoringTempGivenToRValue(Arg)) return false; } return true; } + bool VisitIgnoringTempGivenToRValue(const Expr* Arg) { + Arg = Arg->IgnoreParenCasts(); + if (!Arg->isPRValue()) + return Visit(Arg); + if (auto *ExprWithClean = dyn_cast<ExprWithCleanups>(Arg)) + Arg = ExprWithClean->getSubExpr()->IgnoreParenCasts(); + if (auto *BTE = dyn_cast<CXXBindTemporaryExpr>(Arg)) { + if (Arg->getType() == BTE->getType()) + return Visit(BTE->getSubExpr()); + } + return Visit(Arg); + } + bool VisitCXXConstructExpr(const CXXConstructExpr *CE) { for (const Expr *Arg : CE->arguments()) { - if (Arg && !Visit(Arg)) + if (Arg && !VisitIgnoringTempGivenToRValue(Arg)) return false; } diff --git a/clang/test/Analysis/Checkers/WebKit/call-args.cpp b/clang/test/Analysis/Checkers/WebKit/call-args.cpp index f15991134c58a..bcd3a9592d55d 100644 --- a/clang/test/Analysis/Checkers/WebKit/call-args.cpp +++ b/clang/test/Analysis/Checkers/WebKit/call-args.cpp @@ -557,4 +557,21 @@ namespace call_with_weak_ptr { weakPtr->method(); // expected-warning@-1{{Call argument for 'this' parameter is uncounted and unsafe}} } + + struct Provider { + RefCountableWithWeakPtr* provide(); + }; + int intValue(); + + struct Container { + Container(Provider& provider) + : m_weakPtr(provider.provide()) + , m_value(intValue()) + { } + + private: + WeakPtr<RefCountableWithWeakPtr> m_weakPtr; + int m_value; + }; + } diff --git a/clang/test/Analysis/Checkers/WebKit/nodelete-lazy-initialize.cpp b/clang/test/Analysis/Checkers/WebKit/nodelete-lazy-initialize.cpp new file mode 100644 index 0000000000000..aabe949aae07d --- /dev/null +++ b/clang/test/Analysis/Checkers/WebKit/nodelete-lazy-initialize.cpp @@ -0,0 +1,36 @@ +// RUN: %clang_analyze_cc1 -analyzer-checker=alpha.webkit.NoDeleteChecker -verify %s + +#include "mock-types.h" + +void crash(); + +template<typename T, typename U> +[[clang::suppress]] inline void [[clang::annotate_type("webkit.nodelete")]] lazyInitialize(const RefPtr<T>& ptr, Ref<U>&& obj) +{ + if (ptr) + crash(); + const_cast<RefPtr<T>&>(ptr) = WTF::move(obj); +} + +struct RefObj { + static Ref<RefObj> [[clang::annotate_type("webkit.nodelete")]] create(int = 0); + void ref() const; + void deref() const; + int value() const; +}; + +struct Container { + + void [[clang::annotate_type("webkit.nodelete")]] foo() { + if (!m_bar) + lazyInitialize(m_bar, RefObj::create()); + } + + void [[clang::annotate_type("webkit.nodelete")]] bar() { + if (!m_bar) + lazyInitialize(m_bar, RefObj::create(RefObj::create()->value())); + // expected-warning@-1{{A function 'bar' has [[clang::annotate_type("webkit.nodelete")]] but it contains code that could destruct an object}} + } + + const RefPtr<RefObj> m_bar; +}; _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
