[clang] e4bb68b - [analyzer] Model constructor initializer for an array member (#107537)

via cfe-commits cfe-commits at lists.llvm.org
Fri Sep 6 06:55:19 PDT 2024


Author: Arseniy Zaostrovnykh
Date: 2024-09-06T15:55:16+02:00
New Revision: e4bb68b8717a20c5828b479f83c8648c2596e598

URL: https://github.com/llvm/llvm-project/commit/e4bb68b8717a20c5828b479f83c8648c2596e598
DIFF: https://github.com/llvm/llvm-project/commit/e4bb68b8717a20c5828b479f83c8648c2596e598.diff

LOG: [analyzer] Model constructor initializer for an array member (#107537)

Bind the array member to the compound region associated with the
initializer list, e.g.:

    class C {
      int arr[2];
      C() : arr{1, 2} {}
    };
    C c;

This change enables correct values in `c.arr[0]` and `c.arr[1]`

CPP-5647

Added: 
    

Modified: 
    clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
    clang/test/Analysis/ctor-array.cpp
    clang/test/Analysis/nullptr.cpp

Removed: 
    


################################################################################
diff  --git a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
index dfb7111b512552..315d85319a85a9 100644
--- a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
+++ b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
@@ -1207,9 +1207,14 @@ void ExprEngine::ProcessInitializer(const CFGInitializer CFGInit,
           Init = ASE->getBase()->IgnoreImplicit();
 
         SVal LValue = State->getSVal(Init, stackFrame);
-        if (!Field->getType()->isReferenceType())
-          if (std::optional<Loc> LValueLoc = LValue.getAs<Loc>())
+        if (!Field->getType()->isReferenceType()) {
+          if (std::optional<Loc> LValueLoc = LValue.getAs<Loc>()) {
             InitVal = State->getSVal(*LValueLoc);
+          } else if (auto CV = LValue.getAs<nonloc::CompoundVal>()) {
+            // Initializer list for an array.
+            InitVal = *CV;
+          }
+        }
 
         // If we fail to get the value for some reason, use a symbolic value.
         if (InitVal.isUnknownOrUndef()) {

diff  --git a/clang/test/Analysis/ctor-array.cpp b/clang/test/Analysis/ctor-array.cpp
index 49412ee5a68c70..52600b314b010f 100644
--- a/clang/test/Analysis/ctor-array.cpp
+++ b/clang/test/Analysis/ctor-array.cpp
@@ -234,16 +234,58 @@ struct Parent {
 void member() {
   Parent arr[2];
 
-  // FIXME: Ideally these are TRUE, but at the moment InitListExpr has no
-  // knowledge about where the initializer list is used, so we can't bind
-  // the initializer list to the required region.
-  clang_analyzer_eval(arr[0].arr[0].x == 1); // expected-warning{{UNKNOWN}}
-  clang_analyzer_eval(arr[0].arr[0].y == 2); // expected-warning{{UNKNOWN}}
-  clang_analyzer_eval(arr[0].arr[1].x == 3); // expected-warning{{UNKNOWN}}
-  clang_analyzer_eval(arr[0].arr[1].y == 4); // expected-warning{{UNKNOWN}}
-
-  clang_analyzer_eval(arr[1].arr[0].x == 1); // expected-warning{{UNKNOWN}}
-  clang_analyzer_eval(arr[1].arr[0].y == 2); // expected-warning{{UNKNOWN}}
-  clang_analyzer_eval(arr[1].arr[1].x == 3); // expected-warning{{UNKNOWN}}
-  clang_analyzer_eval(arr[1].arr[1].y == 4); // expected-warning{{UNKNOWN}}
+  clang_analyzer_eval(arr[0].arr[0].x == 1); // expected-warning{{TRUE}}
+  clang_analyzer_eval(arr[0].arr[0].y == 2); // expected-warning{{TRUE}}
+  clang_analyzer_eval(arr[0].arr[1].x == 3); // expected-warning{{TRUE}}
+  clang_analyzer_eval(arr[0].arr[1].y == 4); // expected-warning{{TRUE}}
+
+  clang_analyzer_eval(arr[1].arr[0].x == 1); // expected-warning{{TRUE}}
+  clang_analyzer_eval(arr[1].arr[0].y == 2); // expected-warning{{TRUE}}
+  clang_analyzer_eval(arr[1].arr[1].x == 3); // expected-warning{{TRUE}}
+  clang_analyzer_eval(arr[1].arr[1].y == 4); // expected-warning{{TRUE}}
+}
+
+struct HasArr {
+  int arrDefault[2] = {1, 2};
+  int arr[2];
+  HasArr(int x, int y) : arr{x, y} {}
+};
+
+struct ArrCombination : public HasArr {
+    HasArr membDefault = {5, 6};
+    HasArr memb;
+    ArrCombination(int x) : HasArr(3, 4), memb{7, x} {}
+};
+
+void derived_and_member() {
+  ArrCombination a{8};
+  // FIXME: Default initializers for array members are not modeled.
+  clang_analyzer_eval(a.arrDefault[0] == 1); // expected-warning{{UNKNOWN}}
+  clang_analyzer_eval(a.arrDefault[1] == 2); // expected-warning{{UNKNOWN}}
+  clang_analyzer_eval(a.arr[0] == 3); // expected-warning{{TRUE}}
+  clang_analyzer_eval(a.arr[1] == 4); // expected-warning{{TRUE}}
+  clang_analyzer_eval(a.membDefault.arrDefault[0] == 1); // expected-warning{{UNKNOWN}}
+  clang_analyzer_eval(a.membDefault.arrDefault[1] == 2); // expected-warning{{UNKNOWN}}
+  clang_analyzer_eval(a.membDefault.arr[0] == 5); // expected-warning{{UNKNOWN}}
+  clang_analyzer_eval(a.membDefault.arr[1] == 6); // expected-warning{{UNKNOWN}}
+  clang_analyzer_eval(a.memb.arrDefault[0] == 1); // expected-warning{{UNKNOWN}}
+  clang_analyzer_eval(a.memb.arrDefault[1] == 2); // expected-warning{{UNKNOWN}}
+  clang_analyzer_eval(a.memb.arr[0] == 7); // expected-warning{{TRUE}}
+  clang_analyzer_eval(a.memb.arr[1] == 8); // expected-warning{{TRUE}}
+
+}
+
+struct IncompleteArrInit {
+  int arr[2];
+  int arrDefault[3] = {1, 2, 3};
+  IncompleteArrInit() : arr{1}, arrDefault{2, 3} {}
+};
+
+void incomplete_array_init() {
+  IncompleteArrInit a;
+  clang_analyzer_eval(a.arr[0] == 1); // expected-warning{{TRUE}}
+  clang_analyzer_eval(a.arr[1] == 0); // expected-warning{{TRUE}}
+  clang_analyzer_eval(a.arrDefault[0] == 2); // expected-warning{{TRUE}}
+  clang_analyzer_eval(a.arrDefault[1] == 3); // expected-warning{{TRUE}}
+  clang_analyzer_eval(a.arrDefault[2] == 0); // expected-warning{{TRUE}}
 }

diff  --git a/clang/test/Analysis/nullptr.cpp b/clang/test/Analysis/nullptr.cpp
index 825f6570af591d..73f10a08d96c89 100644
--- a/clang/test/Analysis/nullptr.cpp
+++ b/clang/test/Analysis/nullptr.cpp
@@ -173,3 +173,19 @@ void test_address_space_bind() {
   AS1 AS_ATTRIBUTE &r = *pa;
   r.x = 0; // no-warning
 }
+
+namespace ArrMemWithCtorInitializer {
+struct ArrayMem {
+  int* ptrArr[1];
+  int* memPtr;
+  ArrayMem() : ptrArr{nullptr}, memPtr{nullptr} {}
+  // expected-note at -1{{Storing null pointer value}}
+};
+
+void tp() {
+  ArrayMem obj; // expected-note{{Calling default constructor for 'ArrayMem'}}
+                // expected-note at -1{{Returning from default constructor for 'ArrayMem'}}
+  *obj.ptrArr[0] = 0; // expected-warning{{Dereference of null pointer}}
+                       // expected-note at -1{{Dereference of null pointer}}
+}
+} // namespace ArrMemWithCtorInitializer


        


More information about the cfe-commits mailing list