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 <[email protected]> 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@-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@-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@-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 <[email protected]> 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 <[email protected]> --- 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 <[email protected]> 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 <[email protected]> --- .../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 <[email protected]> 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 <[email protected]> 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; _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
