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 1/3] [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 2/3] 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 3/3] 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] = {};

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

Reply via email to