[clang] [analyzer] Fix misleading 'initialized here' note for uninitialized d… (PR #198345)
Endre Fülöp via cfe-commits
cfe-commits at lists.llvm.org
Wed May 20 07:01:52 PDT 2026
https://github.com/gamesh411 updated https://github.com/llvm/llvm-project/pull/198345
>From 7a868ce932ce706d2bd808a24854199f9288f843 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Endre=20F=C3=BCl=C3=B6p?= <endre.fulop at sigmatechnology.com>
Date: Mon, 18 May 2026 15:59:01 +0200
Subject: [PATCH 1/3] [analyzer] Fix misleading 'initialized here' note for
uninitialized declarations
When a variable is declared without an initializer, the BugReporterVisitor
would emit 'initialized here' as a note, which is confusing because the
variable was never initialized.
Change the note to 'declared without an initial value' for declarations
that have no initializer. Global-storage variables are are also taken
into consideration.
---
.../Core/BugReporterVisitors.cpp | 30 +++++++++----------
.../test/Analysis/cstring-uninitread-notes.c | 2 +-
clang/test/Analysis/placement-new.cpp | 22 +++++++-------
...functions-arg-constraints-tracking-notes.c | 4 +--
.../std-c-library-functions-arg-constraints.c | 8 ++---
clang/test/Analysis/uninit-const.c | 10 +++----
clang/test/Analysis/uninit-vals.m | 8 ++---
7 files changed, 41 insertions(+), 43 deletions(-)
diff --git a/clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp b/clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp
index 5294a0bedf898..2892c12261515 100644
--- a/clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp
+++ b/clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp
@@ -1281,23 +1281,21 @@ static void showBRDiagnostics(llvm::raw_svector_ostream &OS, StoreInfo SI) {
// We don't need to check here, all these conditions were
// checked by StoreSiteFinder, when it figured out that it is
// initialization.
- const auto *DS =
- cast<DeclStmt>(SI.StoreSite->getLocationAs<PostStmt>()->getStmt());
-
- if (SI.Value.isUndef()) {
- if (isa<VarRegion>(SI.Dest)) {
- const auto *VD = cast<VarDecl>(DS->getSingleDecl());
-
- if (VD->getInit()) {
- OS << (HasPrefix ? "initialized" : "Initializing")
- << " to a garbage value";
- } else {
- OS << (HasPrefix ? "declared" : "Declaring")
- << " without an initial value";
- }
+ const auto *VR = dyn_cast<VarRegion>(SI.Dest);
+ if (VR) {
+ const auto *VD = VR->getDecl();
+ bool HasInit = VD->getInit();
+ bool HasGlobalStorage = VD->hasGlobalStorage();
+
+ if (SI.Value.isUndef() && HasInit) {
+ OS << (HasPrefix ? "initialized" : "Initializing")
+ << " to a garbage value";
+ } else if (!HasInit && !HasGlobalStorage) {
+ OS << (HasPrefix ? "declared" : "Declared")
+ << " without an initial value";
+ } else {
+ OS << (HasPrefix ? "initialized" : "Initialized") << " here";
}
- } else {
- OS << (HasPrefix ? "initialized" : "Initialized") << " here";
}
}
}
diff --git a/clang/test/Analysis/cstring-uninitread-notes.c b/clang/test/Analysis/cstring-uninitread-notes.c
index b62519a85c8cc..087ab2ba6260a 100644
--- a/clang/test/Analysis/cstring-uninitread-notes.c
+++ b/clang/test/Analysis/cstring-uninitread-notes.c
@@ -14,7 +14,7 @@ void maybeWrite(const char *src, unsigned size, int *dst) {
} // expected-note{{Returning without writing to '*dst'}}
void returning_without_writing_to_memcpy(const char *src, unsigned size) {
- int block[8 * 8]; // expected-note{{'block' initialized here}}
+ int block[8 * 8]; // expected-note{{'block' declared without an initial value}}
// expected-note at +1{{Calling 'maybeWrite'}}
maybeWrite(src, size, block); // expected-note{{Returning from 'maybeWrite'}}
diff --git a/clang/test/Analysis/placement-new.cpp b/clang/test/Analysis/placement-new.cpp
index 50bbde29cfd59..292ea40b150b8 100644
--- a/clang/test/Analysis/placement-new.cpp
+++ b/clang/test/Analysis/placement-new.cpp
@@ -38,7 +38,7 @@ void f(void *place) {
(void)lp;
}
void g() {
- char buf[2]; // expected-note {{'buf' initialized here}}
+ char buf[2]; // expected-note {{'buf' declared without an initial value}}
f(&buf); // expected-note 2 {{}}
}
} // namespace testArrayBuffer
@@ -78,7 +78,7 @@ void g() {
namespace testPtrToArrayAsPlace {
void f() {
//char *st = new char [8];
- char buf[3]; // expected-note {{'buf' initialized here}}
+ char buf[3]; // expected-note {{'buf' declared without an initial value}}
void *st = buf; // expected-note {{'st' initialized here}}
long *lp = ::new (st) long; // expected-warning{{Storage provided to placement new is only 3 bytes, whereas the allocated type requires 8 bytes}} expected-note 1 {{}}
(void)lp;
@@ -87,7 +87,7 @@ void f() {
namespace testPtrToArrayWithOffsetAsPlace {
void f() {
- int buf[3]; // expected-note {{'buf' initialized here}}
+ int buf[3]; // expected-note {{'buf' declared without an initial value}}
long *lp = ::new (buf + 2) long; // expected-warning{{Storage provided to placement new is only 4 bytes, whereas the allocated type requires 8 bytes}} expected-note 1 {{}}
(void)lp;
}
@@ -95,7 +95,7 @@ void f() {
namespace testZeroSize {
void f() {
- int buf[3]; // expected-note {{'buf' initialized here}}
+ int buf[3]; // expected-note {{'buf' declared without an initial value}}
long *lp = ::new (buf + 3) long; // expected-warning{{Storage provided to placement new is only 0 bytes, whereas the allocated type requires 8 bytes}} expected-note 1 {{}}
(void)lp;
}
@@ -103,7 +103,7 @@ void f() {
namespace testNegativeSize {
void f() {
- int buf[3]; // expected-note {{'buf' initialized here}}
+ int buf[3]; // expected-note {{'buf' declared without an initial value}}
long *lp = ::new (buf + 4) long; // expected-warning{{Storage provided to placement new is only -4 bytes, whereas the allocated type requires 8 bytes}} expected-note 1 {{}}
(void)lp;
}
@@ -123,7 +123,7 @@ void g2() {
namespace testMultiDimensionalArray {
void f() {
- char buf[2][3]; // expected-note {{'buf' initialized here}}
+ char buf[2][3]; // expected-note {{'buf' declared without an initial value}}
long *lp = ::new (buf) long; // expected-warning{{Storage provided to placement new is only 6 bytes, whereas the allocated type requires 8 bytes}} expected-note 1 {{}}
(void)lp;
}
@@ -131,7 +131,7 @@ void f() {
namespace testMultiDimensionalArray2 {
void f() {
- char buf[2][3]; // expected-note {{'buf' initialized here}}
+ char buf[2][3]; // expected-note {{'buf' declared without an initial value}}
long *lp = ::new (buf + 1) long; // expected-warning{{Storage provided to placement new is only 3 bytes, whereas the allocated type requires 8 bytes}} expected-note 1 {{}}
(void)lp;
}
@@ -139,7 +139,7 @@ void f() {
namespace testMultiDimensionalArray3 {
void f() {
- char buf[2][3]; // expected-note {{'buf' initialized here}}
+ char buf[2][3]; // expected-note {{'buf' declared without an initial value}}
long *lp = ::new (&buf[1][1]) long; // expected-warning{{Storage provided to placement new is only 2 bytes, whereas the allocated type requires 8 bytes}} expected-note 1 {{}}
(void)lp;
}
@@ -251,13 +251,13 @@ void f4() {
}
void f5() {
- short b[10]; // expected-note {{'b' initialized here}}
+ short b[10]; // expected-note {{'b' declared without an initial value}}
::new (&b) long; // expected-warning{{Storage type is aligned to 2 bytes but allocated type is aligned to 8 bytes}} expected-note 1 {{}}
}
void f6() {
- short b[10]; // expected-note {{'b' initialized here}}
+ short b[10]; // expected-note {{'b' declared without an initial value}}
// bad (same as previous but checks ElementRegion case)
::new (&b[0]) long; // expected-warning{{Storage type is aligned to 2 bytes but allocated type is aligned to 8 bytes}} expected-note 1 {{}}
@@ -271,7 +271,7 @@ void f7() {
}
void f8() {
- alignas(alignof(long)) short b[10]; // expected-note {{'b' initialized here}}
+ alignas(alignof(long)) short b[10]; // expected-note {{'b' declared without an initial value}}
// ok. aligned to long(ok). offset 3*2(ok)
::new (&b[3]) long; // expected-warning{{Storage type is aligned to 6 bytes but allocated type is aligned to 8 bytes}} expected-note 1 {{}}
diff --git a/clang/test/Analysis/std-c-library-functions-arg-constraints-tracking-notes.c b/clang/test/Analysis/std-c-library-functions-arg-constraints-tracking-notes.c
index 0a66e49be9b2a..f63cd5d9b48f7 100644
--- a/clang/test/Analysis/std-c-library-functions-arg-constraints-tracking-notes.c
+++ b/clang/test/Analysis/std-c-library-functions-arg-constraints-tracking-notes.c
@@ -12,7 +12,7 @@ typedef typeof(sizeof(int)) size_t;
int __buf_size_arg_constraint(const void *, size_t);
void test_buf_size_concrete(void) {
- char buf[3]; // bugpath-note{{'buf' initialized here}}
+ char buf[3]; // bugpath-note{{'buf' declared without an initial value}}
int s = 4; // bugpath-note{{'s' initialized to 4}}
__buf_size_arg_constraint(buf, s); // \
// bugpath-warning{{The 1st argument to '__buf_size_arg_constraint' is a buffer with size 3 but should be a buffer with size equal to or greater than the value of the 2nd argument}} \
@@ -21,7 +21,7 @@ void test_buf_size_concrete(void) {
int __buf_size_arg_constraint_mul(const void *, size_t, size_t);
void test_buf_size_concrete_with_multiplication(void) {
- short buf[3]; // bugpath-note{{'buf' initialized here}}
+ short buf[3]; // bugpath-note{{'buf' declared without an initial value}}
int s1 = 4; // bugpath-note{{'s1' initialized to 4}}
int s2 = sizeof(short); // bugpath-note{{'s2' initialized to}}
__buf_size_arg_constraint_mul(buf, s1, s2); // \
diff --git a/clang/test/Analysis/std-c-library-functions-arg-constraints.c b/clang/test/Analysis/std-c-library-functions-arg-constraints.c
index 0b817dda98c72..2cefa80341fc4 100644
--- a/clang/test/Analysis/std-c-library-functions-arg-constraints.c
+++ b/clang/test/Analysis/std-c-library-functions-arg-constraints.c
@@ -237,7 +237,7 @@ void test_no_node_after_bug(FILE *fp, size_t size, size_t n, void *buf) {
// This is one test case for the ARR38-C SEI-CERT rule.
void ARR38_C_F(FILE *file) {
enum { BUFFER_SIZE = 1024 };
- wchar_t wbuf[BUFFER_SIZE]; // bugpath-note{{'wbuf' initialized here}}
+ wchar_t wbuf[BUFFER_SIZE]; // bugpath-note{{'wbuf' declared without an initial value}}
const size_t size = sizeof(*wbuf); // bugpath-note{{'size' initialized to}}
const size_t nitems = sizeof(wbuf); // bugpath-note{{'nitems' initialized to}}
@@ -287,7 +287,7 @@ void test_arg_constraint_on_variadic_fun(void) {
int __buf_size_arg_constraint(const void *, size_t);
void test_buf_size_concrete(void) {
- char buf[3]; // bugpath-note{{'buf' initialized here}}
+ char buf[3]; // bugpath-note{{'buf' declared without an initial value}}
__buf_size_arg_constraint(buf, 4); // \
// report-warning{{The 1st argument to '__buf_size_arg_constraint' is a buffer with size 3 but should be a buffer with size equal to or greater than the value of the 2nd argument (which is 4)}} \
// bugpath-warning{{The 1st argument to '__buf_size_arg_constraint' is a buffer with size 3 but should be a buffer with size equal to or greater than the value of the 2nd argument (which is 4)}} \
@@ -314,7 +314,7 @@ void test_buf_size_symbolic_and_offset(int s) {
int __buf_size_arg_constraint_mul(const void *, size_t, size_t);
void test_buf_size_concrete_with_multiplication(void) {
- short buf[3]; // bugpath-note{{'buf' initialized here}}
+ short buf[3]; // bugpath-note{{'buf' declared without an initial value}}
__buf_size_arg_constraint_mul(buf, 4, sizeof(short)); // \
// report-warning{{The 1st argument to '__buf_size_arg_constraint_mul' is a buffer with size 6 but should be a buffer with size equal to or greater than the value of the 2nd argument (which is 4) times the 3rd argument (which is 2)}} \
// bugpath-warning{{The 1st argument to '__buf_size_arg_constraint_mul' is a buffer with size 6 but should be a buffer with size equal to or greater than the value of the 2nd argument (which is 4) times the 3rd argument (which is 2)}} \
@@ -340,7 +340,7 @@ void test_buf_size_symbolic_and_offset_with_multiplication(size_t s) {
// The minimum buffer size for this function is set to 10.
int __buf_size_arg_constraint_concrete(const void *);
void test_min_buf_size(void) {
- char buf[9];// bugpath-note{{'buf' initialized here}}
+ char buf[9];// bugpath-note{{'buf' declared without an initial value}}
__buf_size_arg_constraint_concrete(buf); // \
// report-warning{{The 1st argument to '__buf_size_arg_constraint_concrete' is a buffer with size 9 but should be a buffer with size equal to or greater than 10}} \
// bugpath-warning{{The 1st argument to '__buf_size_arg_constraint_concrete' is a buffer with size 9 but should be a buffer with size equal to or greater than 10}} \
diff --git a/clang/test/Analysis/uninit-const.c b/clang/test/Analysis/uninit-const.c
index 1ddf2c89ae59a..f8c89644dd306 100644
--- a/clang/test/Analysis/uninit-const.c
+++ b/clang/test/Analysis/uninit-const.c
@@ -69,14 +69,14 @@ void f_4(void) {
}
void f_5(void) {
- int ta[5]; // expected-note {{'ta' initialized here}}
+ int ta[5]; // expected-note {{'ta' declared without an initial value}}
int *tp = ta; // expected-note {{'tp' initialized here}}
doStuff_pointerToConstInt(tp); // expected-warning {{1st function call argument is a pointer to uninitialized value}}
// expected-note at -1 {{1st function call argument is a pointer to uninitialized value}}
}
void f_5_1(void) {
- int ta[5]; // expected-note {{'ta' initialized here}}
+ int ta[5]; // expected-note {{'ta' declared without an initial value}}
doStuff_pointerToConstInt(ta); // expected-warning {{1st function call argument is a pointer to uninitialized value}}
// expected-note at -1 {{1st function call argument is a pointer to uninitialized value}}
}
@@ -106,20 +106,20 @@ void f_8(void) {
}
void f_9(void) {
- int a[6]; // expected-note {{'a' initialized here}}
+ int a[6]; // expected-note {{'a' declared without an initial value}}
int const *ptau = a; // expected-note {{'ptau' initialized here}}
doStuff_arrayOfConstInt(ptau); // expected-warning {{1st function call argument is a pointer to uninitialized value}}
// expected-note at -1 {{1st function call argument is a pointer to uninitialized value}}
}
void f_10(void) {
- int a[6]; // expected-note {{'a' initialized here}}
+ int a[6]; // expected-note {{'a' declared without an initial value}}
doStuff_arrayOfConstInt(a); // expected-warning {{1st function call argument is a pointer to uninitialized value}}
// expected-note at -1 {{1st function call argument is a pointer to uninitialized value}}
}
void f_11(void) {
- int t[10]; //expected-note {{'t' initialized here}}
+ int t[10]; //expected-note {{'t' declared without an initial value}}
doStuff_constStaticSizedArray(t); // expected-warning {{1st function call argument is a pointer to uninitialized value}}
// expected-note at -1 {{1st function call argument is a pointer to uninitialized value}}
}
diff --git a/clang/test/Analysis/uninit-vals.m b/clang/test/Analysis/uninit-vals.m
index eef2600b97f56..f754f65605d1a 100644
--- a/clang/test/Analysis/uninit-vals.m
+++ b/clang/test/Analysis/uninit-vals.m
@@ -59,7 +59,7 @@ void test_uninit_neg(void) {
extern void test_uninit_struct_arg_aux(struct TestUninit arg);
void test_uninit_struct_arg(void) {
- struct TestUninit x; // expected-note{{'x' initialized here}}
+ struct TestUninit x; // expected-note{{'x' declared without an initial value}}
test_uninit_struct_arg_aux(x); // expected-warning{{Passed-by-value struct argument contains uninitialized data (e.g., field: 'x')}}
// expected-note at -1{{Passed-by-value struct argument contains uninitialized data (e.g., field: 'x')}}
}
@@ -68,7 +68,7 @@ @interface Foo
- (void) passVal:(struct TestUninit)arg;
@end
void testFoo(Foo *o) {
- struct TestUninit x; // expected-note{{'x' initialized here}}
+ struct TestUninit x; // expected-note{{'x' declared without an initial value}}
[o passVal:x]; // expected-warning{{Passed-by-value struct argument contains uninitialized data (e.g., field: 'x')}}
// expected-note at -1{{Passed-by-value struct argument contains uninitialized data (e.g., field: 'x')}}
}
@@ -402,7 +402,7 @@ void testSmallStructBitfieldsFirstUnnamed(void) {
struct {
int : 4;
int y : 4;
- } a, b, c; // expected-note{{'c' initialized here}}
+ } a, b, c; // expected-note{{'c' declared without an initial value}}
a.y = 2;
@@ -419,7 +419,7 @@ void testSmallStructBitfieldsSecondUnnamed(void) {
struct {
int x : 4;
int : 4;
- } a, b, c; // expected-note{{'c' initialized here}}
+ } a, b, c; // expected-note{{'c' declared without an initial value}}
a.x = 1;
>From 56d9db0bfd53f4e694449ab8cc8a155e5b250dbf Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Endre=20F=C3=BCl=C3=B6p?= <endre.fulop at sigmatechnology.com>
Date: Mon, 18 May 2026 19:36:42 +0200
Subject: [PATCH 2/3] fixup: emit 'initialized here' for non-VarRegion
destinations
Without this fallback, field regions (e.g., 'w.a') would get a note
with only the prefix and no message text.
---
clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp | 2 ++
1 file changed, 2 insertions(+)
diff --git a/clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp b/clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp
index 2892c12261515..02a9c21e2945c 100644
--- a/clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp
+++ b/clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp
@@ -1296,6 +1296,8 @@ static void showBRDiagnostics(llvm::raw_svector_ostream &OS, StoreInfo SI) {
} else {
OS << (HasPrefix ? "initialized" : "Initialized") << " here";
}
+ } else {
+ OS << (HasPrefix ? "initialized" : "Initialized") << " here";
}
}
}
>From 0e63ad0a423948f34ea7fe7b02f140aec4138fb2 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Endre=20F=C3=BCl=C3=B6p?= <endre.fulop at sigmatechnology.com>
Date: Wed, 20 May 2026 15:26:29 +0200
Subject: [PATCH 3/3] fixup: address review - spell out type, add garbage-value
test
---
.../StaticAnalyzer/Core/BugReporterVisitors.cpp | 11 ++++-------
clang/test/Analysis/uninit-val-init-from-garbage.c | 14 ++++++++++++++
2 files changed, 18 insertions(+), 7 deletions(-)
create mode 100644 clang/test/Analysis/uninit-val-init-from-garbage.c
diff --git a/clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp b/clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp
index 02a9c21e2945c..4e133a9c81d91 100644
--- a/clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp
+++ b/clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp
@@ -1281,16 +1281,13 @@ static void showBRDiagnostics(llvm::raw_svector_ostream &OS, StoreInfo SI) {
// We don't need to check here, all these conditions were
// checked by StoreSiteFinder, when it figured out that it is
// initialization.
- const auto *VR = dyn_cast<VarRegion>(SI.Dest);
- if (VR) {
- const auto *VD = VR->getDecl();
- bool HasInit = VD->getInit();
- bool HasGlobalStorage = VD->hasGlobalStorage();
+ if (const auto *VR = dyn_cast<VarRegion>(SI.Dest)) {
+ const VarDecl *VD = VR->getDecl();
- if (SI.Value.isUndef() && HasInit) {
+ if (SI.Value.isUndef() && VD->getInit()) {
OS << (HasPrefix ? "initialized" : "Initializing")
<< " to a garbage value";
- } else if (!HasInit && !HasGlobalStorage) {
+ } else if (!VD->getInit() && !VD->hasGlobalStorage()) {
OS << (HasPrefix ? "declared" : "Declared")
<< " without an initial value";
} else {
diff --git a/clang/test/Analysis/uninit-val-init-from-garbage.c b/clang/test/Analysis/uninit-val-init-from-garbage.c
new file mode 100644
index 0000000000000..4ecfb3cd2a38f
--- /dev/null
+++ b/clang/test/Analysis/uninit-val-init-from-garbage.c
@@ -0,0 +1,14 @@
+// RUN: %clang_analyze_cc1 -analyzer-checker=core.uninitialized.Branch \
+// RUN: -analyzer-output=text -verify %s
+
+// Test that 'initialized to a garbage value' note is emitted when a variable
+// is initialized from an uninitialized source and later used in a branch.
+void testInitFromGarbage(int cond) {
+ int y; // expected-note{{'y' declared without an initial value}}
+ if (cond) // expected-note{{Assuming 'cond' is 0}}
+ // expected-note at -1{{Taking false branch}}
+ y = 1;
+ int x = y; // expected-note{{'x' initialized to a garbage value}}
+ if (x > 0) {} // expected-warning{{Branch condition evaluates to a garbage value}}
+ // expected-note at -1{{Branch condition evaluates to a garbage value}}
+}
More information about the cfe-commits
mailing list