[clang] [alpha.webkit.NoDeleteChecker] Support nodelete on constructors and destructors (PR #218822)
Ryosuke Niwa via cfe-commits
cfe-commits at lists.llvm.org
Sat Oct 3 00:45:04 PDT 2026
https://github.com/rniwa updated https://github.com/llvm/llvm-project/pull/218822
>From a229bf42500e77be7e3e7f02493495c79754ccc9 Mon Sep 17 00:00:00 2001
From: Ryosuke Niwa <rniwa at webkit.org>
Date: Tue, 25 Aug 2026 18:28:00 -0700
Subject: [PATCH 1/5] [alpha.webkit.NoDeleteChecker] Support nodelete on
constructors and destructors
This PR adds the support for specifying nodelete annotation on C++ constructors and destructors.
To do this, we recognize [[clang::annotate("webkit.nodelete")]] on function declarations instead
of [[clang::annotate_type("webkit.nodelete")]] on the return value.
---
.../Checkers/WebKit/NoDeleteChecker.cpp | 46 ++++++-
.../Checkers/WebKit/PtrTypesSemantics.cpp | 125 +++++++++++++++---
.../Checkers/WebKit/PtrTypesSemantics.h | 10 ++
.../Checkers/WebKit/nodelete-annotation.cpp | 47 +++++++
4 files changed, 204 insertions(+), 24 deletions(-)
diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/NoDeleteChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/NoDeleteChecker.cpp
index b3b43361a25efc..cf4518bb962d78 100644
--- a/clang/lib/StaticAnalyzer/Checkers/WebKit/NoDeleteChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/NoDeleteChecker.cpp
@@ -84,16 +84,50 @@ class NoDeleteChecker : public Checker<check::ASTDecl<TranslationUnitDecl>> {
break;
}
}
+
+ const FieldDecl *Field = nullptr;
const Stmt *OffendingStmt = nullptr;
- if (!ParamDecl && TFA.isTrivial(Body, &OffendingStmt))
+ bool IsCtor = false;
+ bool IsDtor = false;
+ if (auto *Ctor = dyn_cast<CXXConstructorDecl>(FD)) {
+ IsCtor = true;
+ Field = TFA.fieldWithNonTrivialCtor(Ctor->getParent());
+ if (!Field) {
+ for (auto *CtorInit : Ctor->inits()) {
+ if (!TFA.isTrivial(CtorInit->getInit(), &OffendingStmt)) {
+ if (!OffendingStmt)
+ OffendingStmt = CtorInit->getInit();
+ break;
+ }
+ }
+ }
+ } else if (auto *Dtor = dyn_cast<CXXDestructorDecl>(FD)) {
+ IsDtor = true;
+ Field = TFA.fieldWithNonTrivialDtor(Dtor->getParent());
+ }
+
+ if (!ParamDecl && !Field && !OffendingStmt &&
+ TFA.isTrivial(Body, &OffendingStmt))
return;
SmallString<100> Buf;
llvm::raw_svector_ostream Os(Buf);
- Os << "A function ";
+ if (IsCtor)
+ Os << "A constructor ";
+ else if (IsDtor)
+ Os << "A destructor ";
+ else
+ Os << "A function ";
printQuotedName(Os, FD);
- Os << " has [[clang::annotate_type(\"webkit.nodelete\")]] but it contains ";
+ // FIXME: Update this to say clang::annotate("webkit.nodelete").
+ Os << " has [[clang::annotate_type(\"webkit.nodelete\")]] but it ";
+ if (IsCtor && Field)
+ Os << "constructs ";
+ else if (IsDtor && Field)
+ Os << "destructs ";
+ else
+ Os << "contains ";
SourceLocation SrcLocToReport;
SourceRange Range;
if (ParamDecl) {
@@ -102,6 +136,12 @@ class NoDeleteChecker : public Checker<check::ASTDecl<TranslationUnitDecl>> {
Os << " which could destruct an object.";
SrcLocToReport = FD->getBeginLoc();
Range = ParamDecl->getSourceRange();
+ } else if (Field) {
+ Os << "a member variable ";
+ printQuotedName(Os, Field);
+ Os << " that could destruct an object.";
+ SrcLocToReport = FD->getBeginLoc();
+ Range = Field->getSourceRange();
} else {
Os << "code that could destruct an object.";
SrcLocToReport = OffendingStmt->getBeginLoc();
diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
index e8e404c753dcbc..7557449def246c 100644
--- a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
@@ -445,7 +445,26 @@ enum class WebKitAnnotation : uint8_t {
NoDelete,
};
-static WebKitAnnotation typeAnnotationForReturnType(const FunctionDecl *FD) {
+static WebKitAnnotation annotationType(const StringRef &Annotation) {
+ if (Annotation == "webkit.pointerconversion")
+ return WebKitAnnotation::PointerConversion;
+ if (Annotation == "webkit.nodelete")
+ return WebKitAnnotation::NoDelete;
+ return WebKitAnnotation::None;
+}
+
+static WebKitAnnotation annotationForFunction(const FunctionDecl *FD) {
+ if (isa<CXXRecordDecl>(FD->getParent())) { // FIXME: Add support for annotate
+ // on non-C++ functions.
+ for (auto *Attr : FD->attrs()) {
+ auto *AnnoAttr = dyn_cast_or_null<AnnotateAttr>(Attr);
+ if (!AnnoAttr)
+ continue;
+ auto Annotation = annotationType(AnnoAttr->getAnnotation());
+ if (Annotation != WebKitAnnotation::None)
+ return Annotation;
+ }
+ }
auto RetType = FD->getReturnType();
auto *Type = RetType.getTypePtrOrNull();
if (auto *MacroQualified = dyn_cast_or_null<MacroQualifiedType>(Type))
@@ -456,12 +475,7 @@ static WebKitAnnotation typeAnnotationForReturnType(const FunctionDecl *FD) {
auto *AnnotateType = dyn_cast_or_null<AnnotateTypeAttr>(Attr->getAttr());
if (!AnnotateType)
return WebKitAnnotation::None;
- auto Annotation = AnnotateType->getAnnotation();
- if (Annotation == "webkit.pointerconversion")
- return WebKitAnnotation::PointerConversion;
- if (Annotation == "webkit.nodelete")
- return WebKitAnnotation::NoDelete;
- return WebKitAnnotation::None;
+ return annotationType(AnnotateType->getAnnotation());
}
bool isPtrConversion(const FunctionDecl *F) {
@@ -481,14 +495,14 @@ bool isPtrConversion(const FunctionDecl *F) {
FunctionName == "checked_objc_cast")
return true;
- if (typeAnnotationForReturnType(F) == WebKitAnnotation::PointerConversion)
+ if (annotationForFunction(F) == WebKitAnnotation::PointerConversion)
return true;
return false;
}
static bool isNoDeleteFunctionDecl(const FunctionDecl *F) {
- return typeAnnotationForReturnType(F) == WebKitAnnotation::NoDelete;
+ return annotationForFunction(F) == WebKitAnnotation::NoDelete;
}
bool isNoDeleteFunction(const FunctionDecl *F) {
@@ -574,14 +588,22 @@ class TrivialFunctionAnalysisVisitor
return Result;
}
+ static bool isTrivialType(QualType Ty) {
+ // T*, T&, or T&& does not delete.
+ if (Ty->isPointerOrReferenceType())
+ return true;
+
+ // Fundamental types (integral, nullptr, etc...) does not delete.
+ if (Ty->isFundamentalType() || Ty->isIntegralOrEnumerationType())
+ return true;
+
+ return false;
+ }
+
bool CanTriviallyDestruct(QualType Ty) {
if (Ty.isNull())
return false;
- // T*, T& or T&& does not run its destructor.
- if (Ty->isPointerOrReferenceType())
- return true;
-
// FIXME: Handle a case when there is a local autorelease pool.
if (Ty->isObjCObjectPointerType()) {
auto Type = Ty.isDestructedType();
@@ -590,8 +612,7 @@ class TrivialFunctionAnalysisVisitor
// strong lifetime in ARC could dealloc an object.
}
- // Fundamental types (integral, nullptr_t, etc...) don't have destructors.
- if (Ty->isFundamentalType() || Ty->isIntegralOrEnumerationType())
+ if (isTrivialType(Ty))
return true;
if (const auto *R = Ty->getAsCXXRecordDecl()) {
@@ -599,7 +620,12 @@ class TrivialFunctionAnalysisVisitor
if (R->hasDefinition() && R->hasTrivialDestructor())
return true;
- if (HasFieldWithNonTrivialDtor(R))
+ if (auto *Dtor = R->getDestructor()) {
+ if (isNoDeleteFunction(Dtor))
+ return true;
+ }
+
+ if (FieldWithNonTrivialDtor(R))
return false;
// For Webkit, side-effects are fine as long as we don't delete objects,
@@ -620,16 +646,42 @@ class TrivialFunctionAnalysisVisitor
return false; // Otherwise it's likely not trivial.
}
- bool HasFieldWithNonTrivialDtor(const CXXRecordDecl *Cls) {
- auto CacheIt = FieldDtorCache.find(Cls);
- if (CacheIt != FieldDtorCache.end())
+ bool CanTriviallyConstruct(QualType Ty) {
+ if (Ty.isNull())
+ return false;
+
+ if (isTrivialType(Ty))
+ return true;
+
+ if (const auto *R = Ty->getAsCXXRecordDecl()) {
+ // C++ trivially destructible classes are fine.
+ if (R->hasDefinition() && R->hasTrivialDefaultConstructor())
+ return true;
+ for (auto *Ctor : R->ctors()) {
+ if (Ctor->isDefaultConstructor() && IsFunctionTrivial(Ctor))
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ template <typename CacheTy, typename IsTrivialTypeFn>
+ bool hasNonTrivialField(const CXXRecordDecl *Cls,
+ const FieldDecl **OffendingField, CacheTy &Cache,
+ IsTrivialTypeFn IsTrivialType) {
+ auto CacheIt = Cache.find(Cls);
+ if (CacheIt != Cache.end() && !OffendingField)
return CacheIt->second;
bool Result = ([&] {
auto HasNonTrivialField = [&](const CXXRecordDecl *R) {
for (const FieldDecl *F : R->fields()) {
- if (!CanTriviallyDestruct(F->getType()))
+ if (!IsTrivialType(F->getType())) {
+ if (OffendingField)
+ *OffendingField = F;
return true;
+ }
}
return false;
};
@@ -653,7 +705,7 @@ class TrivialFunctionAnalysisVisitor
Paths, /*LookupInDependent =*/true);
})();
- FieldDtorCache[Cls] = Result;
+ Cache[Cls] = Result;
return Result;
}
@@ -722,6 +774,22 @@ class TrivialFunctionAnalysisVisitor
VD, [&] { return CanTriviallyDestruct(VD->getType()); });
}
+ const FieldDecl *FieldWithNonTrivialCtor(const CXXRecordDecl *Cls) {
+ const FieldDecl *OffendingField = nullptr;
+ hasNonTrivialField(
+ Cls, &OffendingField, FieldCtorCache,
+ [&](const QualType Ty) { return CanTriviallyConstruct(Ty); });
+ return OffendingField;
+ }
+
+ const FieldDecl *FieldWithNonTrivialDtor(const CXXRecordDecl *Cls) {
+ const FieldDecl *OffendingField = nullptr;
+ hasNonTrivialField(
+ Cls, &OffendingField, FieldDtorCache,
+ [&](const QualType Ty) { return CanTriviallyDestruct(Ty); });
+ return OffendingField;
+ }
+
bool IsStatementTrivial(const Stmt *S) {
auto CacheIt = Cache.find(S);
if (CacheIt != Cache.end())
@@ -1084,6 +1152,7 @@ class TrivialFunctionAnalysisVisitor
private:
CacheTy &Cache;
+ CacheTy FieldCtorCache;
CacheTy FieldDtorCache;
CacheTy RecursiveFn;
const Stmt **OffendingStmt;
@@ -1109,4 +1178,18 @@ bool TrivialFunctionAnalysis::hasTrivialDtorImpl(const VarDecl *VD,
return V.HasTrivialDestructor(VD);
}
+const FieldDecl *
+TrivialFunctionAnalysis::fieldWithNonTrivialCtorImpl(const CXXRecordDecl *RD,
+ CacheTy &Cache) {
+ TrivialFunctionAnalysisVisitor V(Cache);
+ return V.FieldWithNonTrivialCtor(RD);
+}
+
+const FieldDecl *
+TrivialFunctionAnalysis::fieldWithNonTrivialDtorImpl(const CXXRecordDecl *RD,
+ CacheTy &Cache) {
+ TrivialFunctionAnalysisVisitor V(Cache);
+ return V.FieldWithNonTrivialDtor(RD);
+}
+
} // namespace clang
diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.h b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.h
index 4e548c44c6bb9d..13f102e22661c4 100644
--- a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.h
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.h
@@ -183,6 +183,12 @@ class TrivialFunctionAnalysis {
bool hasTrivialDtor(const VarDecl *VD) const {
return hasTrivialDtorImpl(VD, TheCache);
}
+ const FieldDecl *fieldWithNonTrivialCtor(const CXXRecordDecl *RD) const {
+ return fieldWithNonTrivialCtorImpl(RD, TheCache);
+ }
+ const FieldDecl *fieldWithNonTrivialDtor(const CXXRecordDecl *RD) const {
+ return fieldWithNonTrivialDtorImpl(RD, TheCache);
+ }
private:
friend class TrivialFunctionAnalysisVisitor;
@@ -194,6 +200,10 @@ class TrivialFunctionAnalysis {
static bool isTrivialImpl(const Decl *D, CacheTy &Cache, const Stmt **);
static bool isTrivialImpl(const Stmt *S, CacheTy &Cache, const Stmt **);
static bool hasTrivialDtorImpl(const VarDecl *VD, CacheTy &Cache);
+ static const FieldDecl *fieldWithNonTrivialCtorImpl(const CXXRecordDecl *RD,
+ CacheTy &Cache);
+ static const FieldDecl *fieldWithNonTrivialDtorImpl(const CXXRecordDecl *RD,
+ CacheTy &Cache);
};
} // namespace clang
diff --git a/clang/test/Analysis/Checkers/WebKit/nodelete-annotation.cpp b/clang/test/Analysis/Checkers/WebKit/nodelete-annotation.cpp
index 06ba7c47ae91af..e0161bb3f18c3d 100644
--- a/clang/test/Analysis/Checkers/WebKit/nodelete-annotation.cpp
+++ b/clang/test/Analysis/Checkers/WebKit/nodelete-annotation.cpp
@@ -759,3 +759,50 @@ void [[clang::annotate_type("webkit.nodelete")]] valueInitNew() {
}
} // namespace trivial_implicit_ctor_in_new_expr
+
+namespace nodelete_ctor_dtor {
+
+struct OpaqueObject {
+ OpaqueObject();
+ ~OpaqueObject();
+};
+
+struct RefPtrContainer {
+ [[clang::annotate("webkit.nodelete")]] RefPtrContainer() { }
+ // expected-warning at -1{{A constructor 'RefPtrContainer' has [[clang::annotate_type("webkit.nodelete")]] but it constructs a member variable 'opaqueObject' that could destruct an object}}
+ [[clang::annotate("webkit.nodelete")]] ~RefPtrContainer() { }
+ // expected-warning at -1{{A destructor '~RefPtrContainer' has [[clang::annotate_type("webkit.nodelete")]] but it destructs a member variable 'countable' that could destruct an object}}
+ RefPtr<RefCountable> countable;
+ OpaqueObject opaqueObject;
+};
+
+struct RefPtrContainerWithSuppressedDestructor {
+ [[clang::suppress]] [[clang::annotate("webkit.nodelete")]] ~RefPtrContainerWithSuppressedDestructor() { }
+ RefPtr<RefCountable> countable;
+};
+
+void [[clang::annotate_type("webkit.nodelete")]] foo(const RefPtrContainer& src) {
+ RefPtrContainer container(src);
+}
+
+struct ObjectWithOpaqueCopyConstructor {
+ ObjectWithOpaqueCopyConstructor(const ObjectWithOpaqueCopyConstructor&);
+ ObjectWithOpaqueCopyConstructor() { }
+};
+
+struct CallDefaultConstructor {
+ [[clang::annotate("webkit.nodelete")]] CallDefaultConstructor() { }
+ ObjectWithOpaqueCopyConstructor objectWithOpaqueCopyConstructor;
+};
+
+struct CallCopyConstructor {
+ using InnerObjectType = ObjectWithOpaqueCopyConstructor;
+ [[clang::annotate("webkit.nodelete")]] CallCopyConstructor(const InnerObjectType& obj)
+ : object(obj)
+ // expected-warning at -1{{A constructor 'CallCopyConstructor' has [[clang::annotate_type("webkit.nodelete")]] but it contains code that could destruct an object}}
+ {
+ }
+ InnerObjectType object;
+};
+
+} // namespace nodelete_ctor_dtor
>From 689a5a2cab0374006c4552d3c8b19ba33ffc025c Mon Sep 17 00:00:00 2001
From: Ryosuke Niwa <ryosuke.niwa at gmail.com>
Date: Thu, 1 Oct 2026 16:07:02 -0700
Subject: [PATCH 2/5] Update
clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Co-authored-by: Balázs Benics <benicsbalazs at gmail.com>
---
clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
index 7557449def246c..ba375691986e93 100644
--- a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
@@ -445,7 +445,7 @@ enum class WebKitAnnotation : uint8_t {
NoDelete,
};
-static WebKitAnnotation annotationType(const StringRef &Annotation) {
+static WebKitAnnotation annotationType(StringRef Annotation) {
if (Annotation == "webkit.pointerconversion")
return WebKitAnnotation::PointerConversion;
if (Annotation == "webkit.nodelete")
>From 27b90399c97e97b4d80617fb156ae2a508f0aa00 Mon Sep 17 00:00:00 2001
From: Ryosuke Niwa <ryosuke.niwa at gmail.com>
Date: Thu, 1 Oct 2026 16:07:24 -0700
Subject: [PATCH 3/5] Update
clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Co-authored-by: Balázs Benics <benicsbalazs at gmail.com>
---
.../lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
index ba375691986e93..a1abe638bd789d 100644
--- a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
@@ -454,8 +454,8 @@ static WebKitAnnotation annotationType(StringRef Annotation) {
}
static WebKitAnnotation annotationForFunction(const FunctionDecl *FD) {
- if (isa<CXXRecordDecl>(FD->getParent())) { // FIXME: Add support for annotate
- // on non-C++ functions.
+ // FIXME: Add support for annotate on non-C++ functions.
+ if (isa<CXXMethodDecl>(FD)) {
for (auto *Attr : FD->attrs()) {
auto *AnnoAttr = dyn_cast_or_null<AnnotateAttr>(Attr);
if (!AnnoAttr)
>From 5369ae4b2c40b0ae542f95104f56060169b8ab78 Mon Sep 17 00:00:00 2001
From: Ryosuke Niwa <rniwa at webkit.org>
Date: Thu, 1 Oct 2026 16:39:29 -0700
Subject: [PATCH 4/5] Make the annotation work with a regular C function and
make it possible to specify multiple annotations per function
---
.../Checkers/WebKit/PtrTypesSemantics.cpp | 26 +++++++------------
.../Checkers/WebKit/nodelete-annotation.cpp | 8 ++++++
2 files changed, 18 insertions(+), 16 deletions(-)
diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
index a1abe638bd789d..85c3086a8f6464 100644
--- a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
@@ -453,17 +453,11 @@ static WebKitAnnotation annotationType(StringRef Annotation) {
return WebKitAnnotation::None;
}
-static WebKitAnnotation annotationForFunction(const FunctionDecl *FD) {
- // FIXME: Add support for annotate on non-C++ functions.
- if (isa<CXXMethodDecl>(FD)) {
- for (auto *Attr : FD->attrs()) {
- auto *AnnoAttr = dyn_cast_or_null<AnnotateAttr>(Attr);
- if (!AnnoAttr)
- continue;
- auto Annotation = annotationType(AnnoAttr->getAnnotation());
- if (Annotation != WebKitAnnotation::None)
- return Annotation;
- }
+static bool hasAnnotationForFunction(const FunctionDecl *FD,
+ WebKitAnnotation TargetAnnotation) {
+ for (auto *Attr : FD->specific_attrs<AnnotateAttr>()) {
+ if (annotationType(Attr->getAnnotation()) == TargetAnnotation)
+ return true;
}
auto RetType = FD->getReturnType();
auto *Type = RetType.getTypePtrOrNull();
@@ -471,11 +465,11 @@ static WebKitAnnotation annotationForFunction(const FunctionDecl *FD) {
Type = MacroQualified->desugar().getTypePtrOrNull();
auto *Attr = dyn_cast_or_null<AttributedType>(Type);
if (!Attr)
- return WebKitAnnotation::None;
+ return false;
auto *AnnotateType = dyn_cast_or_null<AnnotateTypeAttr>(Attr->getAttr());
if (!AnnotateType)
- return WebKitAnnotation::None;
- return annotationType(AnnotateType->getAnnotation());
+ return false;
+ return annotationType(AnnotateType->getAnnotation()) == TargetAnnotation;
}
bool isPtrConversion(const FunctionDecl *F) {
@@ -495,14 +489,14 @@ bool isPtrConversion(const FunctionDecl *F) {
FunctionName == "checked_objc_cast")
return true;
- if (annotationForFunction(F) == WebKitAnnotation::PointerConversion)
+ if (hasAnnotationForFunction(F, WebKitAnnotation::PointerConversion))
return true;
return false;
}
static bool isNoDeleteFunctionDecl(const FunctionDecl *F) {
- return annotationForFunction(F) == WebKitAnnotation::NoDelete;
+ return hasAnnotationForFunction(F, WebKitAnnotation::NoDelete);
}
bool isNoDeleteFunction(const FunctionDecl *F) {
diff --git a/clang/test/Analysis/Checkers/WebKit/nodelete-annotation.cpp b/clang/test/Analysis/Checkers/WebKit/nodelete-annotation.cpp
index e0161bb3f18c3d..89f8b7f420fc31 100644
--- a/clang/test/Analysis/Checkers/WebKit/nodelete-annotation.cpp
+++ b/clang/test/Analysis/Checkers/WebKit/nodelete-annotation.cpp
@@ -806,3 +806,11 @@ struct CallCopyConstructor {
};
} // namespace nodelete_ctor_dtor
+
+namespace nodelete_ptrconversion {
+
+ [[clang::annotate("webkit.ptrconversion")]] [[clang::annotate("webkit.nodelete")]] void foo(void* ptr) {
+ someFunction(); // expected-warning{{A function 'foo' has [[clang::annotate_type("webkit.nodelete")]] but it contains code that could destruct an object}}
+ }
+
+} // namespace nodelete_ptrconversion
>From 333b4fc87ccb5a5820ce74fde4874a4477d3b847 Mon Sep 17 00:00:00 2001
From: Ryosuke Niwa <rniwa at webkit.org>
Date: Sat, 3 Oct 2026 00:44:41 -0700
Subject: [PATCH 5/5] Fix builds
---
clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.h | 1 +
1 file changed, 1 insertion(+)
diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.h b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.h
index 13f102e22661c4..9a601c806c1c40 100644
--- a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.h
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.h
@@ -21,6 +21,7 @@ class CXXBaseSpecifier;
class CXXMethodDecl;
class CXXRecordDecl;
class Decl;
+class FieldDecl;
class FunctionDecl;
class NamedDecl;
class QualType;
More information about the cfe-commits
mailing list