https://github.com/gamesh411 updated 
https://github.com/llvm/llvm-project/pull/189361

From 91f222f8f9b26eaee34416f90033819421a76d94 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Endre=20F=C3=BCl=C3=B6p?= <[email protected]>
Date: Fri, 27 Mar 2026 12:18:03 +0100
Subject: [PATCH 01/12] [analyzer] Resolve initializers for fields of struct
 array elements in RegionStore
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Extend getBindingForField() to handle the FieldRegion(ElementRegion(VarRegion))
hierarchy when accessing a field of a struct inside an array.
Previously, only FieldRegion(VarRegion) was handled (i.e. fields of a single
struct variable), so the initializer of struct array elements was never
considered.

The same trust conditions apply: const-qualified types are always trusted,
and non-const globals are trusted when analyzing main(). For C++ structs
with in-class (default member) initializers, we conservatively fall through
to the symbolic path to avoid incorrectly returning zero. Actually resolving
default member initializer values is left to a separate change.

This resolves false positives from security.ArrayBound on sentinel-terminated
struct arrays.

Co-authored-by: Donát Nagy <[email protected]>
---
 clang/lib/StaticAnalyzer/Core/RegionStore.cpp | 40 ++++++++++++++++
 ...-structs-initializer-not-visible-in-main.c | 47 +++++++++++++++++++
 ...tructs-initializer-not-visible-in-main.cpp | 36 ++++++++++++++
 3 files changed, 123 insertions(+)
 create mode 100644 
clang/test/Analysis/array-of-structs-initializer-not-visible-in-main.c
 create mode 100644 
clang/test/Analysis/array-of-structs-initializer-not-visible-in-main.cpp

diff --git a/clang/lib/StaticAnalyzer/Core/RegionStore.cpp 
b/clang/lib/StaticAnalyzer/Core/RegionStore.cpp
index 6ec66298e8c45..eac87355f8401 100644
--- a/clang/lib/StaticAnalyzer/Core/RegionStore.cpp
+++ b/clang/lib/StaticAnalyzer/Core/RegionStore.cpp
@@ -2145,6 +2145,46 @@ SVal 
RegionStoreManager::getBindingForField(RegionBindingsConstRef B,
         }
   }
 
+  // In case of array of structs, the super-region is not directly a VarRegion,
+  // instead there is another layer of ElementRegion in between them, i.e.:
+  // FieldRegion(ElementRegion(VarRegion)).
+  if (const auto *ER = dyn_cast<ElementRegion>(superR)) {
+    if (const auto *VR = dyn_cast<VarRegion>(ER->getSuperRegion())) {
+      const VarDecl *VD = VR->getDecl();
+      QualType ArrayTy = VD->getType();
+      unsigned FieldIdx = FD->getFieldIndex();
+
+      if (ArrayTy.isConstQualified() || Ty.isConstQualified() ||
+          (B.isMainAnalysis() && VD->hasGlobalStorage())) {
+        if (const Expr *Init = VD->getAnyInitializer())
+          if (const auto *InitList = dyn_cast<InitListExpr>(Init))
+            if (const auto CI = ER->getIndex().getAs<nonloc::ConcreteInt>()) {
+              uint64_t ElemIdx = CI->getValue()->getZExtValue();
+
+              const InitListExpr *StructILE = nullptr;
+              if (ElemIdx < InitList->getNumInits()) {
+                StructILE = dyn_cast<InitListExpr>(InitList->getInit(ElemIdx));
+              } else if (!FD->hasInClassInitializer()) {
+                // This is zero initialization, because there is no explicit
+                // initializer for this index and there is no default member
+                // initializer in-class either.
+                return svalBuilder.makeZeroVal(Ty);
+              }
+              if (StructILE) {
+                if (FieldIdx < StructILE->getNumInits()) {
+                  if (const Expr *FieldInit = StructILE->getInit(FieldIdx))
+                    if (std::optional<SVal> V =
+                            svalBuilder.getConstantVal(FieldInit))
+                      return *V;
+                } else if (!FD->hasInClassInitializer()) {
+                  return svalBuilder.makeZeroVal(Ty);
+                }
+              }
+            }
+      }
+    }
+  }
+
   // Handle the case where we are accessing into a larger scalar object.
   // For example, this handles:
   //   struct header {
diff --git 
a/clang/test/Analysis/array-of-structs-initializer-not-visible-in-main.c 
b/clang/test/Analysis/array-of-structs-initializer-not-visible-in-main.c
new file mode 100644
index 0000000000000..652a44d2815e9
--- /dev/null
+++ b/clang/test/Analysis/array-of-structs-initializer-not-visible-in-main.c
@@ -0,0 +1,47 @@
+// RUN: %clang_analyze_cc1 -analyzer-checker=core,debug.ExprInspection -verify 
%s
+
+// This test verifies that the analyzer can 'see' the initializer of an
+// array of structs, covering const, non-const, and main() vs non-main cases.
+
+void clang_analyzer_warnIfReached(void);
+
+struct S {
+  int a;
+};
+
+// Non-const struct array: initializer should be visible in main().
+struct S struct_array[1] = {
+  {11},
+};
+
+int main(int argc, char **argv) {
+  if (struct_array->a == 11) {
+    clang_analyzer_warnIfReached(); // expected-warning {{REACHABLE}}
+  } else {
+    clang_analyzer_warnIfReached(); // unreachable
+  }
+}
+
+// Const struct array: initializer should be visible in any function.
+const struct S struct_array_const[1] = { {44} };
+
+void use_struct_array_const(void) {
+  if (struct_array_const->a == 44) {
+    clang_analyzer_warnIfReached(); // expected-warning {{REACHABLE}}
+  } else {
+    clang_analyzer_warnIfReached(); // unreachable
+  }
+}
+
+// Non-const struct array in non-main: initializer must NOT be trusted.
+struct S struct_array_nonconst[1] = { {55} };
+
+void use_struct_array_nonconst(void) {
+  if (struct_array_nonconst->a == 55) {
+    clang_analyzer_warnIfReached(); // expected-warning {{REACHABLE}}
+  } else {
+    // This is intentionally reachable, because this is a non-const array which
+    // may have been changed before the call to this function.
+    clang_analyzer_warnIfReached(); // expected-warning {{REACHABLE}}
+  }
+}
diff --git 
a/clang/test/Analysis/array-of-structs-initializer-not-visible-in-main.cpp 
b/clang/test/Analysis/array-of-structs-initializer-not-visible-in-main.cpp
new file mode 100644
index 0000000000000..6053a24a63f48
--- /dev/null
+++ b/clang/test/Analysis/array-of-structs-initializer-not-visible-in-main.cpp
@@ -0,0 +1,36 @@
+// RUN: %clang_analyze_cc1 -std=c++14 
-analyzer-checker=core,debug.ExprInspection -verify %s
+
+// This test verifies that the analyzer does not incorrectly assume zero
+// for fields with in-class (default member) initializers when accessing
+// elements of a struct array.
+
+void clang_analyzer_warnIfReached(void);
+
+struct S {
+  int a = 3;
+};
+
+// Non-const array in main: the analyzer must not assume zero for 'a',
+// because it has a default member initializer.
+S sarr_nonconst[2] = {};
+
+int main(int argc, char **argv) {
+  // FIXME: Should recognize that it is 3 (from the default member 
initializer).
+  if (sarr_nonconst[0].a == 3) {
+    clang_analyzer_warnIfReached(); // expected-warning {{REACHABLE}}
+  } else {
+    clang_analyzer_warnIfReached(); // expected-warning {{REACHABLE}}
+  }
+}
+
+// Const array in non-main: the analyzer resolves the default member
+// initializer correctly through the lazy binding path.
+const S sarr_const[2] = {};
+
+void use_const(void) {
+  if (sarr_const[0].a == 3) {
+    clang_analyzer_warnIfReached(); // expected-warning {{REACHABLE}}
+  } else {
+    clang_analyzer_warnIfReached(); // unreachable
+  }
+}

From 6e992acf79af456a5c8c1c9b7acb1f8a05b7cadc Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Endre=20F=C3=BCl=C3=B6p?= <[email protected]>
Date: Tue, 31 Mar 2026 14:11:14 +0200
Subject: [PATCH 02/12] reorganize test cases

move into one file with 2 runlines to test c and cpp
use clang_analyzer_value instead of warnIfReached
use 2 prefixes
---
 ...-structs-initializer-not-visible-in-main.c | 47 ------------
 ...tructs-initializer-not-visible-in-main.cpp | 36 ----------
 .../Analysis/array-of-structs-initializer.cpp | 71 +++++++++++++++++++
 3 files changed, 71 insertions(+), 83 deletions(-)
 delete mode 100644 
clang/test/Analysis/array-of-structs-initializer-not-visible-in-main.c
 delete mode 100644 
clang/test/Analysis/array-of-structs-initializer-not-visible-in-main.cpp
 create mode 100644 clang/test/Analysis/array-of-structs-initializer.cpp

diff --git 
a/clang/test/Analysis/array-of-structs-initializer-not-visible-in-main.c 
b/clang/test/Analysis/array-of-structs-initializer-not-visible-in-main.c
deleted file mode 100644
index 652a44d2815e9..0000000000000
--- a/clang/test/Analysis/array-of-structs-initializer-not-visible-in-main.c
+++ /dev/null
@@ -1,47 +0,0 @@
-// RUN: %clang_analyze_cc1 -analyzer-checker=core,debug.ExprInspection -verify 
%s
-
-// This test verifies that the analyzer can 'see' the initializer of an
-// array of structs, covering const, non-const, and main() vs non-main cases.
-
-void clang_analyzer_warnIfReached(void);
-
-struct S {
-  int a;
-};
-
-// Non-const struct array: initializer should be visible in main().
-struct S struct_array[1] = {
-  {11},
-};
-
-int main(int argc, char **argv) {
-  if (struct_array->a == 11) {
-    clang_analyzer_warnIfReached(); // expected-warning {{REACHABLE}}
-  } else {
-    clang_analyzer_warnIfReached(); // unreachable
-  }
-}
-
-// Const struct array: initializer should be visible in any function.
-const struct S struct_array_const[1] = { {44} };
-
-void use_struct_array_const(void) {
-  if (struct_array_const->a == 44) {
-    clang_analyzer_warnIfReached(); // expected-warning {{REACHABLE}}
-  } else {
-    clang_analyzer_warnIfReached(); // unreachable
-  }
-}
-
-// Non-const struct array in non-main: initializer must NOT be trusted.
-struct S struct_array_nonconst[1] = { {55} };
-
-void use_struct_array_nonconst(void) {
-  if (struct_array_nonconst->a == 55) {
-    clang_analyzer_warnIfReached(); // expected-warning {{REACHABLE}}
-  } else {
-    // This is intentionally reachable, because this is a non-const array which
-    // may have been changed before the call to this function.
-    clang_analyzer_warnIfReached(); // expected-warning {{REACHABLE}}
-  }
-}
diff --git 
a/clang/test/Analysis/array-of-structs-initializer-not-visible-in-main.cpp 
b/clang/test/Analysis/array-of-structs-initializer-not-visible-in-main.cpp
deleted file mode 100644
index 6053a24a63f48..0000000000000
--- a/clang/test/Analysis/array-of-structs-initializer-not-visible-in-main.cpp
+++ /dev/null
@@ -1,36 +0,0 @@
-// RUN: %clang_analyze_cc1 -std=c++14 
-analyzer-checker=core,debug.ExprInspection -verify %s
-
-// This test verifies that the analyzer does not incorrectly assume zero
-// for fields with in-class (default member) initializers when accessing
-// elements of a struct array.
-
-void clang_analyzer_warnIfReached(void);
-
-struct S {
-  int a = 3;
-};
-
-// Non-const array in main: the analyzer must not assume zero for 'a',
-// because it has a default member initializer.
-S sarr_nonconst[2] = {};
-
-int main(int argc, char **argv) {
-  // FIXME: Should recognize that it is 3 (from the default member 
initializer).
-  if (sarr_nonconst[0].a == 3) {
-    clang_analyzer_warnIfReached(); // expected-warning {{REACHABLE}}
-  } else {
-    clang_analyzer_warnIfReached(); // expected-warning {{REACHABLE}}
-  }
-}
-
-// Const array in non-main: the analyzer resolves the default member
-// initializer correctly through the lazy binding path.
-const S sarr_const[2] = {};
-
-void use_const(void) {
-  if (sarr_const[0].a == 3) {
-    clang_analyzer_warnIfReached(); // expected-warning {{REACHABLE}}
-  } else {
-    clang_analyzer_warnIfReached(); // unreachable
-  }
-}
diff --git a/clang/test/Analysis/array-of-structs-initializer.cpp 
b/clang/test/Analysis/array-of-structs-initializer.cpp
new file mode 100644
index 0000000000000..1e048ecb63ee9
--- /dev/null
+++ b/clang/test/Analysis/array-of-structs-initializer.cpp
@@ -0,0 +1,71 @@
+// RUN: %clang_analyze_cc1 -xc -analyzer-checker=core,debug.ExprInspection 
-verify=expected,c %s
+// RUN: %clang_analyze_cc1 -xc++ -DCPP -std=c++14 
-analyzer-checker=core,debug.ExprInspection -verify=expected,cpp %s
+
+void clang_analyzer_value(int);
+
+struct CStruct {
+  int a;
+};
+
+struct CStruct nonconst_c_struct_array[1] = {
+  {11},
+};
+
+void use_nonconst_struct_array_c(void) {
+  clang_analyzer_value(nonconst_c_struct_array->a); // expected-warning 
{{32s:{ [-2147483648, 2147483647] }}}
+}
+
+const struct CStruct const_c_struct_array[1] = { {22} };
+
+void use_const_struct_array_c(void) {
+  clang_analyzer_value(const_c_struct_array->a); // expected-warning {{22}}
+}
+
+#ifdef CPP
+struct CPPStruct {
+  int a = 33;
+};
+
+CPPStruct nonconst_cpp_struct_array[1] = {};
+const CPPStruct const_cpp_struct_array[1] = {};
+
+struct CPPStructWithUserCtor {
+  int a = 44;
+  CPPStructWithUserCtor(): a(55) {}
+};
+
+CPPStructWithUserCtor nonconst_cpp_struct_wctor_array[1] = {};
+
+void use_nonconst_struct_array_cpp(void) {
+  clang_analyzer_value(nonconst_cpp_struct_array->a); // expected-warning 
{{32s:{ [-2147483648, 2147483647] }}}
+}
+
+const CPPStructWithUserCtor const_cpp_struct_wctor_array[1] = {};
+#endif
+
+int main(int argc, char **argv) {
+  // FIXME: In C++ mode, IsMainAnalysis is false because global constructors
+  // may run before main(), so the initializer for non-const globals are not
+  // considered. In C mode this correctly resolves to 11.
+  clang_analyzer_value(nonconst_c_struct_array->a); // c-warning {{11}} 
cpp-warning {{32s:{ [-2147483648, 2147483647] }}}
+
+#ifdef CPP
+  // FIXME: Once we model default member initialization, this should be 33.
+  clang_analyzer_value(const_cpp_struct_array->a); // cpp-warning {{32s:{ 
[-2147483648, 2147483647] }}}
+
+  // FIXME: Even if we modeled default member initialization, because of C++
+  // mode, initializers of non-const globals are not considered. If they were,
+  // this should be 33.
+  clang_analyzer_value(nonconst_cpp_struct_array->a); // cpp-warning {{32s:{ 
[-2147483648, 2147483647] }}}
+
+  // FIXME: Once we model constructors for global arrays, this should be 55.
+  clang_analyzer_value(const_cpp_struct_wctor_array->a); // cpp-warning 
{{32s:{ [-2147483648, 2147483647] }}}
+
+  // FIXME: Even if we modeled default member initialization, because of C++
+  // mode, non-const globals' initializers are not considered. If they were,
+  // the ctor's initializer list has precedence over the default member
+  // initializer, so the correct value should be 55.
+  clang_analyzer_value(nonconst_cpp_struct_wctor_array->a); // cpp-warning 
{{32s:{ [-2147483648, 2147483647] }}}
+#endif
+}
+

From 7f48e4a38c910cf6d30d65ff6e1a765426acd724 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Endre=20F=C3=BCl=C3=B6p?= <[email protected]>
Date: Tue, 31 Mar 2026 15:26:42 +0200
Subject: [PATCH 03/12] rename one partially misleading expectation

---
 clang/test/Analysis/array-of-structs-initializer.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/clang/test/Analysis/array-of-structs-initializer.cpp 
b/clang/test/Analysis/array-of-structs-initializer.cpp
index 1e048ecb63ee9..519f2029800d3 100644
--- a/clang/test/Analysis/array-of-structs-initializer.cpp
+++ b/clang/test/Analysis/array-of-structs-initializer.cpp
@@ -37,7 +37,7 @@ struct CPPStructWithUserCtor {
 CPPStructWithUserCtor nonconst_cpp_struct_wctor_array[1] = {};
 
 void use_nonconst_struct_array_cpp(void) {
-  clang_analyzer_value(nonconst_cpp_struct_array->a); // expected-warning 
{{32s:{ [-2147483648, 2147483647] }}}
+  clang_analyzer_value(nonconst_cpp_struct_array->a); // cpp-warning {{32s:{ 
[-2147483648, 2147483647] }}}
 }
 
 const CPPStructWithUserCtor const_cpp_struct_wctor_array[1] = {};

From 2bb082c994d3fc5e670ae4c3088fd6d985a33015 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Endre=20F=C3=BCl=C3=B6p?= <[email protected]>
Date: Tue, 26 May 2026 19:58:06 +0200
Subject: [PATCH 04/12] Revert original implementation in favor of generalized
 approach

Reverts the ad-hoc FieldRegion(ElementRegion(VarRegion)) handling
in getBindingForField, to be replaced by a single helper that
handles arbitrary nesting depth.
---
 clang/lib/StaticAnalyzer/Core/RegionStore.cpp | 40 -----------
 .../Analysis/array-of-structs-initializer.cpp | 71 -------------------
 2 files changed, 111 deletions(-)
 delete mode 100644 clang/test/Analysis/array-of-structs-initializer.cpp

diff --git a/clang/lib/StaticAnalyzer/Core/RegionStore.cpp 
b/clang/lib/StaticAnalyzer/Core/RegionStore.cpp
index eac87355f8401..6ec66298e8c45 100644
--- a/clang/lib/StaticAnalyzer/Core/RegionStore.cpp
+++ b/clang/lib/StaticAnalyzer/Core/RegionStore.cpp
@@ -2145,46 +2145,6 @@ SVal 
RegionStoreManager::getBindingForField(RegionBindingsConstRef B,
         }
   }
 
-  // In case of array of structs, the super-region is not directly a VarRegion,
-  // instead there is another layer of ElementRegion in between them, i.e.:
-  // FieldRegion(ElementRegion(VarRegion)).
-  if (const auto *ER = dyn_cast<ElementRegion>(superR)) {
-    if (const auto *VR = dyn_cast<VarRegion>(ER->getSuperRegion())) {
-      const VarDecl *VD = VR->getDecl();
-      QualType ArrayTy = VD->getType();
-      unsigned FieldIdx = FD->getFieldIndex();
-
-      if (ArrayTy.isConstQualified() || Ty.isConstQualified() ||
-          (B.isMainAnalysis() && VD->hasGlobalStorage())) {
-        if (const Expr *Init = VD->getAnyInitializer())
-          if (const auto *InitList = dyn_cast<InitListExpr>(Init))
-            if (const auto CI = ER->getIndex().getAs<nonloc::ConcreteInt>()) {
-              uint64_t ElemIdx = CI->getValue()->getZExtValue();
-
-              const InitListExpr *StructILE = nullptr;
-              if (ElemIdx < InitList->getNumInits()) {
-                StructILE = dyn_cast<InitListExpr>(InitList->getInit(ElemIdx));
-              } else if (!FD->hasInClassInitializer()) {
-                // This is zero initialization, because there is no explicit
-                // initializer for this index and there is no default member
-                // initializer in-class either.
-                return svalBuilder.makeZeroVal(Ty);
-              }
-              if (StructILE) {
-                if (FieldIdx < StructILE->getNumInits()) {
-                  if (const Expr *FieldInit = StructILE->getInit(FieldIdx))
-                    if (std::optional<SVal> V =
-                            svalBuilder.getConstantVal(FieldInit))
-                      return *V;
-                } else if (!FD->hasInClassInitializer()) {
-                  return svalBuilder.makeZeroVal(Ty);
-                }
-              }
-            }
-      }
-    }
-  }
-
   // Handle the case where we are accessing into a larger scalar object.
   // For example, this handles:
   //   struct header {
diff --git a/clang/test/Analysis/array-of-structs-initializer.cpp 
b/clang/test/Analysis/array-of-structs-initializer.cpp
deleted file mode 100644
index 519f2029800d3..0000000000000
--- a/clang/test/Analysis/array-of-structs-initializer.cpp
+++ /dev/null
@@ -1,71 +0,0 @@
-// RUN: %clang_analyze_cc1 -xc -analyzer-checker=core,debug.ExprInspection 
-verify=expected,c %s
-// RUN: %clang_analyze_cc1 -xc++ -DCPP -std=c++14 
-analyzer-checker=core,debug.ExprInspection -verify=expected,cpp %s
-
-void clang_analyzer_value(int);
-
-struct CStruct {
-  int a;
-};
-
-struct CStruct nonconst_c_struct_array[1] = {
-  {11},
-};
-
-void use_nonconst_struct_array_c(void) {
-  clang_analyzer_value(nonconst_c_struct_array->a); // expected-warning 
{{32s:{ [-2147483648, 2147483647] }}}
-}
-
-const struct CStruct const_c_struct_array[1] = { {22} };
-
-void use_const_struct_array_c(void) {
-  clang_analyzer_value(const_c_struct_array->a); // expected-warning {{22}}
-}
-
-#ifdef CPP
-struct CPPStruct {
-  int a = 33;
-};
-
-CPPStruct nonconst_cpp_struct_array[1] = {};
-const CPPStruct const_cpp_struct_array[1] = {};
-
-struct CPPStructWithUserCtor {
-  int a = 44;
-  CPPStructWithUserCtor(): a(55) {}
-};
-
-CPPStructWithUserCtor nonconst_cpp_struct_wctor_array[1] = {};
-
-void use_nonconst_struct_array_cpp(void) {
-  clang_analyzer_value(nonconst_cpp_struct_array->a); // cpp-warning {{32s:{ 
[-2147483648, 2147483647] }}}
-}
-
-const CPPStructWithUserCtor const_cpp_struct_wctor_array[1] = {};
-#endif
-
-int main(int argc, char **argv) {
-  // FIXME: In C++ mode, IsMainAnalysis is false because global constructors
-  // may run before main(), so the initializer for non-const globals are not
-  // considered. In C mode this correctly resolves to 11.
-  clang_analyzer_value(nonconst_c_struct_array->a); // c-warning {{11}} 
cpp-warning {{32s:{ [-2147483648, 2147483647] }}}
-
-#ifdef CPP
-  // FIXME: Once we model default member initialization, this should be 33.
-  clang_analyzer_value(const_cpp_struct_array->a); // cpp-warning {{32s:{ 
[-2147483648, 2147483647] }}}
-
-  // FIXME: Even if we modeled default member initialization, because of C++
-  // mode, initializers of non-const globals are not considered. If they were,
-  // this should be 33.
-  clang_analyzer_value(nonconst_cpp_struct_array->a); // cpp-warning {{32s:{ 
[-2147483648, 2147483647] }}}
-
-  // FIXME: Once we model constructors for global arrays, this should be 55.
-  clang_analyzer_value(const_cpp_struct_wctor_array->a); // cpp-warning 
{{32s:{ [-2147483648, 2147483647] }}}
-
-  // FIXME: Even if we modeled default member initialization, because of C++
-  // mode, non-const globals' initializers are not considered. If they were,
-  // the ctor's initializer list has precedence over the default member
-  // initializer, so the correct value should be 55.
-  clang_analyzer_value(nonconst_cpp_struct_wctor_array->a); // cpp-warning 
{{32s:{ [-2147483648, 2147483647] }}}
-#endif
-}
-

From d852879c12dbf2e2223d0902b438073c9569611b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Endre=20F=C3=BCl=C3=B6p?= <[email protected]>
Date: Tue, 26 May 2026 19:58:23 +0200
Subject: [PATCH 05/12] [analyzer] Generalize field initializer resolution in
 RegionStore

Replace the ad-hoc blocks in getBindingForField with a single helper
getConstantValFromInitializer that walks up the region chain to a
VarRegion, then walks down the InitListExpr (semantic form) by field
and element indices. Handles arbitrary nesting depth.

Also resolves two FIXMEs where CXXDefaultInitExpr in const arrays
was not recognized.
---
 clang/lib/StaticAnalyzer/Core/RegionStore.cpp | 100 ++++++++++++----
 .../Analysis/array-of-structs-initializer.cpp | 107 ++++++++++++++++++
 clang/test/Analysis/initialization.cpp        |   3 +-
 3 files changed, 184 insertions(+), 26 deletions(-)
 create mode 100644 clang/test/Analysis/array-of-structs-initializer.cpp

diff --git a/clang/lib/StaticAnalyzer/Core/RegionStore.cpp 
b/clang/lib/StaticAnalyzer/Core/RegionStore.cpp
index 6ec66298e8c45..3ebce9746f502 100644
--- a/clang/lib/StaticAnalyzer/Core/RegionStore.cpp
+++ b/clang/lib/StaticAnalyzer/Core/RegionStore.cpp
@@ -585,6 +585,8 @@ class RegionStoreManager : public StoreManager {
   std::optional<SVal>
   getConstantValFromConstArrayInitializer(RegionBindingsConstRef B,
                                           const ElementRegion *R);
+  std::optional<SVal> getConstantValFromInitializer(const FieldRegion *R,
+                                                    bool isMainAnalysis);
   std::optional<SVal>
   getSValFromInitListExpr(const InitListExpr *ILE,
                           const SmallVector<uint64_t, 2> &ConcreteOffsets,
@@ -2113,6 +2115,74 @@ SVal 
RegionStoreManager::getBindingForElement(RegionBindingsConstRef B,
   return getBindingForFieldOrElementCommon(B, R, R->getElementType());
 }
 
+std::optional<SVal>
+RegionStoreManager::getConstantValFromInitializer(const FieldRegion *R,
+                                                  bool IsMainAnalysis) {
+  SmallVector<const SubRegion *, 4> Path;
+  const MemRegion *Cur = R;
+  while (Cur && !isa<VarRegion>(Cur)) {
+    if (isa<FieldRegion, ElementRegion>(Cur)) {
+      Path.push_back(cast<SubRegion>(Cur));
+      Cur = cast<SubRegion>(Cur)->getSuperRegion();
+    } else {
+      return std::nullopt;
+    }
+  }
+
+  const auto *VR = dyn_cast_or_null<VarRegion>(Cur);
+  if (!VR)
+    return std::nullopt;
+
+  const VarDecl *VD = VR->getDecl();
+  QualType LeafTy = R->getDecl()->getType();
+
+  // We trust initializers of constants and globals when analyzing main().
+  if (!VD->getType().isConstQualified() && !LeafTy.isConstQualified() &&
+      (!IsMainAnalysis || !VD->hasGlobalStorage()))
+    return std::nullopt;
+
+  const Expr *Init = VD->getAnyInitializer();
+  if (!Init)
+    return std::nullopt;
+
+  const Expr *E = Init;
+  for (const SubRegion *SR : llvm::reverse(Path)) {
+    const auto *ILE = dyn_cast<InitListExpr>(E);
+    if (!ILE)
+      return isa<ImplicitValueInitExpr>(E)
+                 ? std::optional<SVal>(svalBuilder.makeZeroVal(LeafTy))
+                 : std::nullopt;
+
+    if (const auto *FR = dyn_cast<FieldRegion>(SR)) {
+      // FIXME: For unions, getFieldIndex() does not correspond to the
+      // InitListExpr index. Maybe we should check getInitializedFieldInUnion()
+      // and return nullopt if the accessed field is not the active member.
+      // Currently we may resolve the wrong value (but this is the pre-existing
+      // behavior).
+      unsigned Idx = FR->getDecl()->getFieldIndex();
+      if (Idx < ILE->getNumInits())
+        E = ILE->getInit(Idx);
+      else
+        return std::nullopt;
+    } else if (const auto *ER = dyn_cast<ElementRegion>(SR)) {
+      auto CI = ER->getIndex().getAs<nonloc::ConcreteInt>();
+      if (!CI)
+        return std::nullopt;
+      uint64_t Idx = CI->getValue()->getZExtValue();
+      if (Idx < ILE->getNumInits())
+        E = ILE->getInit(Idx);
+      else if (const Expr *Filler = ILE->getArrayFiller())
+        E = Filler;
+      else
+        return std::nullopt;
+    } else {
+      return std::nullopt;
+    }
+  }
+
+  return svalBuilder.getConstantVal(E);
+}
+
 SVal RegionStoreManager::getBindingForField(RegionBindingsConstRef B,
                                             const FieldRegion* R) {
 
@@ -2120,30 +2190,12 @@ SVal 
RegionStoreManager::getBindingForField(RegionBindingsConstRef B,
   if (const std::optional<SVal> &V = B.getDirectBinding(R))
     return *V;
 
-  // If the containing record was initialized, try to get its constant value.
-  const FieldDecl *FD = R->getDecl();
-  QualType Ty = FD->getType();
-  const MemRegion* superR = R->getSuperRegion();
-  if (const auto *VR = dyn_cast<VarRegion>(superR)) {
-    const VarDecl *VD = VR->getDecl();
-    QualType RecordVarTy = VD->getType();
-    unsigned Index = FD->getFieldIndex();
-    // Either the record variable or the field has an initializer that we can
-    // trust. We trust initializers of constants and, additionally, respect
-    // initializers of globals when analyzing main().
-    if (RecordVarTy.isConstQualified() || Ty.isConstQualified() ||
-        (B.isMainAnalysis() && VD->hasGlobalStorage()))
-      if (const Expr *Init = VD->getAnyInitializer())
-        if (const auto *InitList = dyn_cast<InitListExpr>(Init)) {
-          if (Index < InitList->getNumInits()) {
-            if (const Expr *FieldInit = InitList->getInit(Index))
-              if (std::optional<SVal> V = 
svalBuilder.getConstantVal(FieldInit))
-                return *V;
-          } else {
-            return svalBuilder.makeZeroVal(Ty);
-          }
-        }
-  }
+  // Try to resolve the field value from the variable's initializer.
+  if (std::optional<SVal> V =
+          getConstantValFromInitializer(R, B.isMainAnalysis()))
+    return *V;
+
+  QualType Ty = R->getDecl()->getType();
 
   // Handle the case where we are accessing into a larger scalar object.
   // For example, this handles:
diff --git a/clang/test/Analysis/array-of-structs-initializer.cpp 
b/clang/test/Analysis/array-of-structs-initializer.cpp
new file mode 100644
index 0000000000000..7de84ea3f9fd9
--- /dev/null
+++ b/clang/test/Analysis/array-of-structs-initializer.cpp
@@ -0,0 +1,107 @@
+// RUN: %clang_analyze_cc1 -xc -analyzer-checker=core,debug.ExprInspection 
-verify=expected,c %s
+// RUN: %clang_analyze_cc1 -xc++ -DCPP -std=c++14 
-analyzer-checker=core,debug.ExprInspection -verify=expected,cpp %s
+
+void clang_analyzer_value(int);
+
+struct CStruct {
+  int a;
+};
+
+struct CStruct nonconst_c_struct_array[1] = {
+  {11},
+};
+
+void use_nonconst_struct_array_c(void) {
+  clang_analyzer_value(nonconst_c_struct_array->a); // expected-warning 
{{32s:{ [-2147483648, 2147483647] }}}
+}
+
+const struct CStruct const_c_struct_array[1] = { {22} };
+
+void use_const_struct_array_c(void) {
+  clang_analyzer_value(const_c_struct_array->a); // expected-warning {{22}}
+}
+
+#ifdef CPP
+struct CPPStruct {
+  int a = 33;
+};
+
+CPPStruct nonconst_cpp_struct_array[1] = {};
+const CPPStruct const_cpp_struct_array[1] = {};
+
+struct CPPStructWithUserCtor {
+  int a = 44;
+  CPPStructWithUserCtor(): a(55) {}
+};
+
+CPPStructWithUserCtor nonconst_cpp_struct_wctor_array[1] = {};
+
+void use_nonconst_struct_array_cpp(void) {
+  clang_analyzer_value(nonconst_cpp_struct_array->a); // cpp-warning {{32s:{ 
[-2147483648, 2147483647] }}}
+}
+
+const CPPStructWithUserCtor const_cpp_struct_wctor_array[1] = {};
+#endif
+
+int main(int argc, char **argv) {
+  // FIXME: In C++ mode, IsMainAnalysis is false because global constructors
+  // may run before main(), so the initializer for non-const globals are not
+  // considered. In C mode this correctly resolves to 11.
+  clang_analyzer_value(nonconst_c_struct_array->a); // c-warning {{11}} 
cpp-warning {{32s:{ [-2147483648, 2147483647] }}}
+
+#ifdef CPP
+  // Default member initialization is resolved from the initializer.
+  clang_analyzer_value(const_cpp_struct_array->a); // cpp-warning {{33}}
+
+  // FIXME: In C++ mode, non-const globals are not trusted because global
+  // constructors may run before main(). This should be 33.
+  clang_analyzer_value(nonconst_cpp_struct_array->a); // cpp-warning {{32s:{ 
[-2147483648, 2147483647] }}}
+
+  // FIXME: We do not model constructor calls in initializers. This should
+  // be 55 (from the constructor's initializer list).
+  clang_analyzer_value(const_cpp_struct_wctor_array->a); // cpp-warning 
{{32s:{ [-2147483648, 2147483647] }}}
+
+  // FIXME: Non-const global in C++ mode, and also requires modeling
+  // constructor calls. This should be 55.
+  clang_analyzer_value(nonconst_cpp_struct_wctor_array->a); // cpp-warning 
{{32s:{ [-2147483648, 2147483647] }}}
+#endif
+}
+
+struct Inner {
+  int x;
+  int y;
+};
+
+struct Outer {
+  struct Inner arr[2];
+  int z;
+};
+
+const struct Outer nested = {{{10, 20}, {30, 40}}, 50};
+
+void test_nested_struct_array_field(void) {
+  clang_analyzer_value(nested.arr[0].x); // expected-warning {{10}}
+  clang_analyzer_value(nested.arr[0].y); // expected-warning {{20}}
+  clang_analyzer_value(nested.arr[1].x); // expected-warning {{30}}
+  clang_analyzer_value(nested.arr[1].y); // expected-warning {{40}}
+  clang_analyzer_value(nested.z);        // expected-warning {{50}}
+}
+
+const struct CStruct matrix[2][2] = {{{1}, {2}}, {{3}, {4}}};
+
+void test_2d_array_of_structs(void) {
+  clang_analyzer_value(matrix[0][0].a); // expected-warning {{1}}
+  clang_analyzer_value(matrix[0][1].a); // expected-warning {{2}}
+  clang_analyzer_value(matrix[1][0].a); // expected-warning {{3}}
+  clang_analyzer_value(matrix[1][1].a); // expected-warning {{4}}
+}
+
+const struct Inner partial_arr[3] = {{100, 200}};
+
+void test_array_filler_zero_init(void) {
+  clang_analyzer_value(partial_arr[0].x); // expected-warning {{100}}
+  clang_analyzer_value(partial_arr[0].y); // expected-warning {{200}}
+  clang_analyzer_value(partial_arr[1].x); // expected-warning {{0}}
+  clang_analyzer_value(partial_arr[2].y); // expected-warning {{0}}
+}
+
diff --git a/clang/test/Analysis/initialization.cpp 
b/clang/test/Analysis/initialization.cpp
index d064dd6172d63..1cd19768018de 100644
--- a/clang/test/Analysis/initialization.cpp
+++ b/clang/test/Analysis/initialization.cpp
@@ -10,8 +10,7 @@ struct S {
 S const sarr[2] = {};
 void definit() {
   int i = 1;
-  // FIXME: Should recognize that it is 3.
-  clang_analyzer_eval(sarr[i].a); // expected-warning{{UNKNOWN}}
+  clang_analyzer_eval(sarr[i].a); // expected-warning{{TRUE}}
 }
 
 int const glob_arr1[3] = {};

From 3aa27aada4ab462a65a71138a5f1f8a369385ab7 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Endre=20F=C3=BCl=C3=B6p?= <[email protected]>
Date: Wed, 27 May 2026 01:13:33 +0200
Subject: [PATCH 06/12] [analyzer] Handle unions in
 getConstantValFromInitializer

A union InitListExpr has one init for one member. Use
getInitializedFieldInUnion() to resolve only the initialized field
and return nullopt for other members.

Handle empty initializers (getNumInits() == 0) by returning zero
for the designated member.

Fixes a pre-existing bug where designated init of a non-first union
member would incorrectly resolve accesses to other members.
---
 clang/lib/StaticAnalyzer/Core/RegionStore.cpp | 29 ++++---
 .../Analysis/array-of-structs-initializer.cpp | 76 +++++++++++++++++++
 2 files changed, 95 insertions(+), 10 deletions(-)

diff --git a/clang/lib/StaticAnalyzer/Core/RegionStore.cpp 
b/clang/lib/StaticAnalyzer/Core/RegionStore.cpp
index 3ebce9746f502..d0fd6b0851d7f 100644
--- a/clang/lib/StaticAnalyzer/Core/RegionStore.cpp
+++ b/clang/lib/StaticAnalyzer/Core/RegionStore.cpp
@@ -2154,16 +2154,25 @@ RegionStoreManager::getConstantValFromInitializer(const 
FieldRegion *R,
                  : std::nullopt;
 
     if (const auto *FR = dyn_cast<FieldRegion>(SR)) {
-      // FIXME: For unions, getFieldIndex() does not correspond to the
-      // InitListExpr index. Maybe we should check getInitializedFieldInUnion()
-      // and return nullopt if the accessed field is not the active member.
-      // Currently we may resolve the wrong value (but this is the pre-existing
-      // behavior).
-      unsigned Idx = FR->getDecl()->getFieldIndex();
-      if (Idx < ILE->getNumInits())
-        E = ILE->getInit(Idx);
-      else
-        return std::nullopt;
+      if (ILE->getType()->isUnionType()) {
+        // A union InitListExpr has one init for one member. We can only
+        // resolve if the accessed field matches the initialized member.
+        const FieldDecl *InitField = ILE->getInitializedFieldInUnion();
+        if (InitField && InitField == FR->getDecl()) {
+          if (ILE->getNumInits() > 0)
+            E = ILE->getInit(0);
+          else
+            return svalBuilder.makeZeroVal(LeafTy);
+        } else {
+          return std::nullopt;
+        }
+      } else {
+        unsigned Idx = FR->getDecl()->getFieldIndex();
+        if (Idx < ILE->getNumInits())
+          E = ILE->getInit(Idx);
+        else
+          return std::nullopt;
+      }
     } else if (const auto *ER = dyn_cast<ElementRegion>(SR)) {
       auto CI = ER->getIndex().getAs<nonloc::ConcreteInt>();
       if (!CI)
diff --git a/clang/test/Analysis/array-of-structs-initializer.cpp 
b/clang/test/Analysis/array-of-structs-initializer.cpp
index 7de84ea3f9fd9..6f2a011fbac82 100644
--- a/clang/test/Analysis/array-of-structs-initializer.cpp
+++ b/clang/test/Analysis/array-of-structs-initializer.cpp
@@ -105,3 +105,79 @@ void test_array_filler_zero_init(void) {
   clang_analyzer_value(partial_arr[2].y); // expected-warning {{0}}
 }
 
+union IntOrChar {
+  int i;
+  char c;
+};
+
+const union IntOrChar u_int = {42};
+
+void test_union_active_member(void) {
+  clang_analyzer_value(u_int.i); // expected-warning {{42}}
+}
+
+void test_union_inactive_member(void) {
+  clang_analyzer_value(u_int.c); // expected-warning {{8s:{ [-128, 127] }}}
+}
+
+const union IntOrChar u_char = {.c = 'x'};
+
+void test_union_designated_active(void) {
+  clang_analyzer_value(u_char.c); // expected-warning {{120}}
+}
+
+void test_union_designated_inactive(void) {
+  clang_analyzer_value(u_char.i); // expected-warning {{32s:{ [-2147483648, 
2147483647] }}}
+}
+
+struct HasUnion {
+  union IntOrChar u;
+  int after;
+};
+
+const struct HasUnion hu = {{99}, 7};
+
+void test_union_in_struct(void) {
+  clang_analyzer_value(hu.u.i); // expected-warning {{99}}
+  clang_analyzer_value(hu.u.c); // expected-warning {{8s:{ [-128, 127] }}}
+  clang_analyzer_value(hu.after); // expected-warning {{7}}
+}
+
+// Empty initializer zero-initializes the first member.
+const union IntOrChar u_empty = {};
+
+void test_union_empty_init(void) {
+  clang_analyzer_value(u_empty.i); // expected-warning {{0}}
+  clang_analyzer_value(u_empty.c); // expected-warning {{8s:{ [-128, 127] }}}
+}
+
+const union IntOrChar u_arr[2] = {{10}, {.c = 'y'}};
+
+void test_union_array(void) {
+  clang_analyzer_value(u_arr[0].i); // expected-warning {{10}}
+  clang_analyzer_value(u_arr[0].c); // expected-warning {{8s:{ [-128, 127] }}}
+  clang_analyzer_value(u_arr[1].c); // expected-warning {{121}}
+  clang_analyzer_value(u_arr[1].i); // expected-warning {{32s:{ [-2147483648, 
2147483647] }}}
+}
+
+struct Tagged {
+  int tag;
+  union {
+    int ival;
+    char cval;
+  } payload;
+};
+
+const struct Tagged tagged_arr[2] = {
+  {1, {.ival = 100}},
+  {2, {.cval = 'Z'}},
+};
+
+void test_struct_union_array(void) {
+  clang_analyzer_value(tagged_arr[0].tag);          // expected-warning {{1}}
+  clang_analyzer_value(tagged_arr[0].payload.ival); // expected-warning {{100}}
+  clang_analyzer_value(tagged_arr[0].payload.cval); // expected-warning {{8s:{ 
[-128, 127] }}}
+  clang_analyzer_value(tagged_arr[1].tag);          // expected-warning {{2}}
+  clang_analyzer_value(tagged_arr[1].payload.cval); // expected-warning {{90}}
+  clang_analyzer_value(tagged_arr[1].payload.ival); // expected-warning 
{{32s:{ [-2147483648, 2147483647] }}}
+}

From b912a11f2786e544c09cddb07d6afadfa3427d34 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Endre=20F=C3=BCl=C3=B6p?= <[email protected]>
Date: Thu, 28 May 2026 19:17:45 +0200
Subject: [PATCH 07/12] Update clang/lib/StaticAnalyzer/Core/RegionStore.cpp
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Co-authored-by: Donát Nagy <[email protected]>
---
 clang/lib/StaticAnalyzer/Core/RegionStore.cpp | 7 ++++---
 1 file changed, 4 insertions(+), 3 deletions(-)

diff --git a/clang/lib/StaticAnalyzer/Core/RegionStore.cpp 
b/clang/lib/StaticAnalyzer/Core/RegionStore.cpp
index d0fd6b0851d7f..b09673d9d03ec 100644
--- a/clang/lib/StaticAnalyzer/Core/RegionStore.cpp
+++ b/clang/lib/StaticAnalyzer/Core/RegionStore.cpp
@@ -2136,9 +2136,10 @@ RegionStoreManager::getConstantValFromInitializer(const 
FieldRegion *R,
   const VarDecl *VD = VR->getDecl();
   QualType LeafTy = R->getDecl()->getType();
 
-  // We trust initializers of constants and globals when analyzing main().
-  if (!VD->getType().isConstQualified() && !LeafTy.isConstQualified() &&
-      (!IsMainAnalysis || !VD->hasGlobalStorage()))
+  bool TrustInit = (VD->getType().isConstQualified() ||
+                    LeafTy.isConstQualified() ||
+                    (IsMainAnalysis && VD->hasGlobalStorage()));
+  if (!TrustInit)
     return std::nullopt;
 
   const Expr *Init = VD->getAnyInitializer();

From d2fb75c25a6d5d23e834bc104d438865de0b3221 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Endre=20F=C3=BCl=C3=B6p?= <[email protected]>
Date: Thu, 28 May 2026 19:21:46 +0200
Subject: [PATCH 08/12] fix clang-format error

---
 clang/lib/StaticAnalyzer/Core/RegionStore.cpp | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/clang/lib/StaticAnalyzer/Core/RegionStore.cpp 
b/clang/lib/StaticAnalyzer/Core/RegionStore.cpp
index b09673d9d03ec..b5b76c2a9c031 100644
--- a/clang/lib/StaticAnalyzer/Core/RegionStore.cpp
+++ b/clang/lib/StaticAnalyzer/Core/RegionStore.cpp
@@ -2136,9 +2136,9 @@ RegionStoreManager::getConstantValFromInitializer(const 
FieldRegion *R,
   const VarDecl *VD = VR->getDecl();
   QualType LeafTy = R->getDecl()->getType();
 
-  bool TrustInit = (VD->getType().isConstQualified() ||
-                    LeafTy.isConstQualified() ||
-                    (IsMainAnalysis && VD->hasGlobalStorage()));
+  bool TrustInit =
+      (VD->getType().isConstQualified() || LeafTy.isConstQualified() ||
+       (IsMainAnalysis && VD->hasGlobalStorage()));
   if (!TrustInit)
     return std::nullopt;
 

From 92d7e8d74df4a32b04473238ec11bd8597031cb8 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Endre=20F=C3=BCl=C3=B6p?= <[email protected]>
Date: Tue, 2 Jun 2026 04:31:15 +0200
Subject: [PATCH 09/12] apply review suggestions of @steakhal

---
 clang/lib/StaticAnalyzer/Core/RegionStore.cpp | 16 ++++++-----
 .../Analysis/array-of-structs-initializer.cpp | 27 +++++++++++++++++--
 2 files changed, 34 insertions(+), 9 deletions(-)

diff --git a/clang/lib/StaticAnalyzer/Core/RegionStore.cpp 
b/clang/lib/StaticAnalyzer/Core/RegionStore.cpp
index b5b76c2a9c031..c35fcc4d8a3a4 100644
--- a/clang/lib/StaticAnalyzer/Core/RegionStore.cpp
+++ b/clang/lib/StaticAnalyzer/Core/RegionStore.cpp
@@ -586,7 +586,7 @@ class RegionStoreManager : public StoreManager {
   getConstantValFromConstArrayInitializer(RegionBindingsConstRef B,
                                           const ElementRegion *R);
   std::optional<SVal> getConstantValFromInitializer(const FieldRegion *R,
-                                                    bool isMainAnalysis);
+                                                    bool IsMainAnalysis);
   std::optional<SVal>
   getSValFromInitListExpr(const InitListExpr *ILE,
                           const SmallVector<uint64_t, 2> &ConcreteOffsets,
@@ -2129,7 +2129,7 @@ RegionStoreManager::getConstantValFromInitializer(const 
FieldRegion *R,
     }
   }
 
-  const auto *VR = dyn_cast_or_null<VarRegion>(Cur);
+  const auto *VR = dyn_cast<VarRegion>(Cur);
   if (!VR)
     return std::nullopt;
 
@@ -2149,21 +2149,23 @@ RegionStoreManager::getConstantValFromInitializer(const 
FieldRegion *R,
   const Expr *E = Init;
   for (const SubRegion *SR : llvm::reverse(Path)) {
     const auto *ILE = dyn_cast<InitListExpr>(E);
-    if (!ILE)
+    // If E is not an InitListExpr, it may be an ImplicitValueInitExpr
+    // representing zero-initialization of this aggregate element.
+    if (!ILE) {
       return isa<ImplicitValueInitExpr>(E)
                  ? std::optional<SVal>(svalBuilder.makeZeroVal(LeafTy))
                  : std::nullopt;
+    }
 
     if (const auto *FR = dyn_cast<FieldRegion>(SR)) {
       if (ILE->getType()->isUnionType()) {
         // A union InitListExpr has one init for one member. We can only
         // resolve if the accessed field matches the initialized member.
         const FieldDecl *InitField = ILE->getInitializedFieldInUnion();
-        if (InitField && InitField == FR->getDecl()) {
-          if (ILE->getNumInits() > 0)
-            E = ILE->getInit(0);
-          else
+        if (InitField == FR->getDecl()) {
+          if (ILE->getNumInits() == 0)
             return svalBuilder.makeZeroVal(LeafTy);
+          E = ILE->getInit(0);
         } else {
           return std::nullopt;
         }
diff --git a/clang/test/Analysis/array-of-structs-initializer.cpp 
b/clang/test/Analysis/array-of-structs-initializer.cpp
index 6f2a011fbac82..fe77623310023 100644
--- a/clang/test/Analysis/array-of-structs-initializer.cpp
+++ b/clang/test/Analysis/array-of-structs-initializer.cpp
@@ -1,5 +1,5 @@
-// RUN: %clang_analyze_cc1 -xc -analyzer-checker=core,debug.ExprInspection 
-verify=expected,c %s
-// RUN: %clang_analyze_cc1 -xc++ -DCPP -std=c++14 
-analyzer-checker=core,debug.ExprInspection -verify=expected,cpp %s
+// RUN: %clang_analyze_cc1 -triple x86_64-unknown-linux-gnu 
-analyzer-checker=core,debug.ExprInspection -verify=expected,c -xc %s
+// RUN: %clang_analyze_cc1 -triple x86_64-unknown-linux-gnu 
-analyzer-checker=core,debug.ExprInspection -verify=expected,cpp -xc++ -DCPP 
-std=c++14 %s
 
 void clang_analyzer_value(int);
 
@@ -87,6 +87,19 @@ void test_nested_struct_array_field(void) {
   clang_analyzer_value(nested.z);        // expected-warning {{50}}
 }
 
+// Elided braces: see [dcl.init.aggr] p15.
+
+const struct Outer nested_elided = {10, 20, 30, 40, 50};
+
+void test_nested_elided_braces(void) {
+  clang_analyzer_value(nested_elided.arr[0].x); // expected-warning {{10}}
+  clang_analyzer_value(nested_elided.arr[0].y); // expected-warning {{20}}
+  clang_analyzer_value(nested_elided.arr[1].x); // expected-warning {{30}}
+  clang_analyzer_value(nested_elided.arr[1].y); // expected-warning {{40}}
+  clang_analyzer_value(nested_elided.z);        // expected-warning {{50}}
+}
+
+
 const struct CStruct matrix[2][2] = {{{1}, {2}}, {{3}, {4}}};
 
 void test_2d_array_of_structs(void) {
@@ -96,6 +109,16 @@ void test_2d_array_of_structs(void) {
   clang_analyzer_value(matrix[1][1].a); // expected-warning {{4}}
 }
 
+const struct CStruct matrix_elided[2][2] = {1, 2, 3, 4};
+
+void test_matrix_elided_braces(void) {
+  clang_analyzer_value(matrix_elided[0][0].a); // expected-warning {{1}}
+  clang_analyzer_value(matrix_elided[0][1].a); // expected-warning {{2}}
+  clang_analyzer_value(matrix_elided[1][0].a); // expected-warning {{3}}
+  clang_analyzer_value(matrix_elided[1][1].a); // expected-warning {{4}}
+}
+
+
 const struct Inner partial_arr[3] = {{100, 200}};
 
 void test_array_filler_zero_init(void) {

From d05ac08ee003c51529a98e5084275298c4f8e1d7 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Endre=20F=C3=BCl=C3=B6p?= <[email protected]>
Date: Tue, 2 Jun 2026 05:11:02 +0200
Subject: [PATCH 10/12] refactor loop body: no fall-through between main cases

---
 clang/lib/StaticAnalyzer/Core/RegionStore.cpp | 25 ++++++++++---------
 1 file changed, 13 insertions(+), 12 deletions(-)

diff --git a/clang/lib/StaticAnalyzer/Core/RegionStore.cpp 
b/clang/lib/StaticAnalyzer/Core/RegionStore.cpp
index c35fcc4d8a3a4..5ea97679d9d62 100644
--- a/clang/lib/StaticAnalyzer/Core/RegionStore.cpp
+++ b/clang/lib/StaticAnalyzer/Core/RegionStore.cpp
@@ -2162,21 +2162,21 @@ RegionStoreManager::getConstantValFromInitializer(const 
FieldRegion *R,
         // A union InitListExpr has one init for one member. We can only
         // resolve if the accessed field matches the initialized member.
         const FieldDecl *InitField = ILE->getInitializedFieldInUnion();
-        if (InitField == FR->getDecl()) {
-          if (ILE->getNumInits() == 0)
-            return svalBuilder.makeZeroVal(LeafTy);
-          E = ILE->getInit(0);
-        } else {
+        if (InitField != FR->getDecl())
           return std::nullopt;
-        }
+        if (ILE->getNumInits() == 0)
+          return svalBuilder.makeZeroVal(LeafTy);
+        E = ILE->getInit(0);
       } else {
         unsigned Idx = FR->getDecl()->getFieldIndex();
-        if (Idx < ILE->getNumInits())
-          E = ILE->getInit(Idx);
-        else
+        if (Idx >= ILE->getNumInits())
           return std::nullopt;
+        E = ILE->getInit(Idx);
       }
-    } else if (const auto *ER = dyn_cast<ElementRegion>(SR)) {
+      continue;
+    }
+
+    if (const auto *ER = dyn_cast<ElementRegion>(SR)) {
       auto CI = ER->getIndex().getAs<nonloc::ConcreteInt>();
       if (!CI)
         return std::nullopt;
@@ -2187,9 +2187,10 @@ RegionStoreManager::getConstantValFromInitializer(const 
FieldRegion *R,
         E = Filler;
       else
         return std::nullopt;
-    } else {
-      return std::nullopt;
+      continue;
     }
+
+    return std::nullopt;
   }
 
   return svalBuilder.getConstantVal(E);

From 1080616f7c1f03f29a0d825abce445dfebbb03f2 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Endre=20F=C3=BCl=C3=B6p?= <[email protected]>
Date: Thu, 4 Jun 2026 13:42:50 +0200
Subject: [PATCH 11/12] untangle the 2 InitList sub-cases

---
 clang/lib/StaticAnalyzer/Core/RegionStore.cpp | 12 ++++++------
 1 file changed, 6 insertions(+), 6 deletions(-)

diff --git a/clang/lib/StaticAnalyzer/Core/RegionStore.cpp 
b/clang/lib/StaticAnalyzer/Core/RegionStore.cpp
index 5ea97679d9d62..a8eb5691affdd 100644
--- a/clang/lib/StaticAnalyzer/Core/RegionStore.cpp
+++ b/clang/lib/StaticAnalyzer/Core/RegionStore.cpp
@@ -2148,14 +2148,14 @@ RegionStoreManager::getConstantValFromInitializer(const 
FieldRegion *R,
 
   const Expr *E = Init;
   for (const SubRegion *SR : llvm::reverse(Path)) {
-    const auto *ILE = dyn_cast<InitListExpr>(E);
     // If E is not an InitListExpr, it may be an ImplicitValueInitExpr
     // representing zero-initialization of this aggregate element.
-    if (!ILE) {
-      return isa<ImplicitValueInitExpr>(E)
-                 ? std::optional<SVal>(svalBuilder.makeZeroVal(LeafTy))
-                 : std::nullopt;
-    }
+    if (isa<ImplicitValueInitExpr>(E))
+      return svalBuilder.makeZeroVal(LeafTy);
+
+    const auto *ILE = dyn_cast<InitListExpr>(E);
+    if (!ILE)
+      return std::nullopt;
 
     if (const auto *FR = dyn_cast<FieldRegion>(SR)) {
       if (ILE->getType()->isUnionType()) {

From 82dd4cfede94b721bb3e6c90ececc273b67fbc77 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Endre=20F=C3=BCl=C3=B6p?= <[email protected]>
Date: Thu, 4 Jun 2026 14:25:41 +0200
Subject: [PATCH 12/12] sharpen the test case

---
 clang/test/Analysis/initialization.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/clang/test/Analysis/initialization.cpp 
b/clang/test/Analysis/initialization.cpp
index 1cd19768018de..0fba3a278524c 100644
--- a/clang/test/Analysis/initialization.cpp
+++ b/clang/test/Analysis/initialization.cpp
@@ -10,7 +10,7 @@ struct S {
 S const sarr[2] = {};
 void definit() {
   int i = 1;
-  clang_analyzer_eval(sarr[i].a); // expected-warning{{TRUE}}
+  clang_analyzer_dump(sarr[i].a); // expected-warning{{3 S32b}}
 }
 
 int const glob_arr1[3] = {};

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to