[clang] Guard type size queries on incomplete types in -Wunsafe-buffer-usage (PR #205426)
Phoebe Liang via cfe-commits
cfe-commits at lists.llvm.org
Wed Jun 24 11:48:55 PDT 2026
https://github.com/pl98 updated https://github.com/llvm/llvm-project/pull/205426
>From 2e5a6b69599f26841454880379295104ceb5027f Mon Sep 17 00:00:00 2001
From: Phoebe Liang <phoebeliang at google.com>
Date: Tue, 23 Jun 2026 16:33:23 -0400
Subject: [PATCH 1/2] [Clang][Sema] Guard type size queries on incomplete types
in -Wunsafe-buffer-usage
When evaluating explicit pointer casts of container `.data()` invocations, skip querying pointee size if the source or destination pointee type is incomplete or dependent.
Previously, destination pointee types were checked using `getAsTagDecl()->isCompleteDefinition()`, which missed incomplete array types (`T[]`) and non-C++ tag declarations. Source pointee types were passed directly to `Ctx.getTypeSize()` without any completeness guard.
Querying `getTypeSize()` on incomplete types resulted in assertion
failures in debug builds and segmentation faults in release builds. This
change guards both source and destination types.
---
clang/lib/Sema/AnalysisBasedWarnings.cpp | 22 ++++++++++---------
...e-buffer-usage-warning-data-invocation.cpp | 20 +++++++++++++----
2 files changed, 28 insertions(+), 14 deletions(-)
diff --git a/clang/lib/Sema/AnalysisBasedWarnings.cpp b/clang/lib/Sema/AnalysisBasedWarnings.cpp
index 52fe7765c6c6a..70135d22e724d 100644
--- a/clang/lib/Sema/AnalysisBasedWarnings.cpp
+++ b/clang/lib/Sema/AnalysisBasedWarnings.cpp
@@ -2508,27 +2508,29 @@ class UnsafeBufferUsageReporter : public UnsafeBufferUsageHandler {
MsgParam = 5;
} else if (const auto *ECE = dyn_cast<ExplicitCastExpr>(Operation)) {
QualType destType = ECE->getType();
- bool destTypeComplete = true;
if (!isa<PointerType>(destType))
return;
destType = destType.getTypePtr()->getPointeeType();
- if (const auto *D = destType->getAsTagDecl())
- destTypeComplete = D->isCompleteDefinition();
- // If destination type is incomplete, it is unsafe to cast to anyway, no
- // need to check its type:
- if (destTypeComplete) {
+ // If destination type is incomplete or dependent, it is unsafe to cast
+ // to anyway, no need to check its type:
+ if (!destType->isIncompleteType() && !destType->isDependentType()) {
const uint64_t dSize = Ctx.getTypeSize(destType);
QualType srcType = ECE->getSubExpr()->getType();
assert(srcType->isPointerType());
- const uint64_t sSize =
- Ctx.getTypeSize(srcType.getTypePtr()->getPointeeType());
+ QualType srcPointeeType = srcType.getTypePtr()->getPointeeType();
- if (sSize >= dSize)
- return;
+ // Check if source type is incomplete or dependent as well:
+ if (!srcPointeeType->isIncompleteType() &&
+ !srcPointeeType->isDependentType()) {
+ const uint64_t sSize = Ctx.getTypeSize(srcPointeeType);
+
+ if (sSize >= dSize)
+ return;
+ }
}
if (const auto *CE = dyn_cast<CXXMemberCallExpr>(
ECE->getSubExpr()->IgnoreParens())) {
diff --git a/clang/test/SemaCXX/warn-unsafe-buffer-usage-warning-data-invocation.cpp b/clang/test/SemaCXX/warn-unsafe-buffer-usage-warning-data-invocation.cpp
index a6a334d247023..41ac0f12743bd 100644
--- a/clang/test/SemaCXX/warn-unsafe-buffer-usage-warning-data-invocation.cpp
+++ b/clang/test/SemaCXX/warn-unsafe-buffer-usage-warning-data-invocation.cpp
@@ -174,10 +174,22 @@ A false_negatives(std::span<int> span_pt, span<A> span_A) {
}
-void test_incomplete_type(std::span<char> S) {
- (struct IncompleteStruct *)S.data(); // expected-warning{{unsafe invocation of 'data'}}
- (class IncompleteClass *)S.data(); // expected-warning{{unsafe invocation of 'data'}}
- (union IncompleteUnion *)S.data(); // expected-warning{{unsafe invocation of 'data'}}
+struct IncompleteStruct;
+class IncompleteClass;
+union IncompleteUnion;
+
+void test_incomplete_dest_type(std::span<char> S) {
+ (IncompleteStruct *)S.data(); // expected-warning{{unsafe invocation of 'data'}}
+ (IncompleteClass *)S.data(); // expected-warning{{unsafe invocation of 'data'}}
+ (IncompleteUnion *)S.data(); // expected-warning{{unsafe invocation of 'data'}}
+}
+
+void test_incomplete_source_type(std::span<IncompleteStruct> S) {
+ (float *)S.data(); // expected-warning{{unsafe invocation of 'data'}}
+}
+
+void test_incomplete_array_dest_type(std::span<char> S) {
+ (IncompleteStruct(*)[])S.data(); // expected-warning{{unsafe invocation of 'data'}}
}
void test_complete_type(std::span<long> S) {
>From ecb2156fc02fb3c4ae672358df7995c1cce58a0d Mon Sep 17 00:00:00 2001
From: Phoebe Liang <phoebeliang at google.com>
Date: Wed, 24 Jun 2026 14:34:33 -0400
Subject: [PATCH 2/2] Add dependent test cases.
---
...safe-buffer-usage-warning-data-invocation.cpp | 16 ++++++++++++++++
1 file changed, 16 insertions(+)
diff --git a/clang/test/SemaCXX/warn-unsafe-buffer-usage-warning-data-invocation.cpp b/clang/test/SemaCXX/warn-unsafe-buffer-usage-warning-data-invocation.cpp
index 41ac0f12743bd..107611d461d9d 100644
--- a/clang/test/SemaCXX/warn-unsafe-buffer-usage-warning-data-invocation.cpp
+++ b/clang/test/SemaCXX/warn-unsafe-buffer-usage-warning-data-invocation.cpp
@@ -192,6 +192,22 @@ void test_incomplete_array_dest_type(std::span<char> S) {
(IncompleteStruct(*)[])S.data(); // expected-warning{{unsafe invocation of 'data'}}
}
+template <typename DestType>
+void test_dependent_dest_type(std::span<char> S) {
+ (DestType *)S.data(); // expected-warning{{unsafe invocation of 'data'}}
+}
+
+template <typename SourceType>
+void test_dependent_source_type(std::span<SourceType> S) {
+ (float *)S.data(); // expected-warning{{unsafe invocation of 'data'}}
+}
+
+void instantiate_dependent_tests(std::span<char> char_span,
+ std::span<IncompleteStruct> incomplete_span) {
+ test_dependent_dest_type<IncompleteStruct>(char_span);
+ test_dependent_source_type<IncompleteStruct>(incomplete_span);
+}
+
void test_complete_type(std::span<long> S) {
(struct CompleteStruct *)S.data(); // no warn as the struct size is smaller than long
(class CompleteClass *)S.data(); // no warn as the class size is smaller than long
More information about the cfe-commits
mailing list