ASDenysPetrov created this revision.
ASDenysPetrov added reviewers: NoQ, vsavchenko, steakhal, martong.
ASDenysPetrov added a project: clang.
Herald added subscribers: manas, dkrupp, donat.nagy, Szelethus, 
mikhail.ramalho, a.sidorin, rnkovacs, szepet, baloghadamsoftware, xazax.hun.
ASDenysPetrov requested review of this revision.
Herald added a subscriber: cfe-commits.

Add support of handling `CompoundLiteralExpr` in 
`RegionStoreManager::getConstantValFromConstArrayInitializer`. Retrieve a value 
from `CompoundLiteralExpr` which is an initializer of constant arrays in global 
and local scopes. This patch also disables direct binding a compound literal 
for constant arrays of local storage duration.

Example:

  const int arr[8] = (const int[8]){1, 2, 3, 4}; // compound literal


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D108032

Files:
  clang/lib/AST/Expr.cpp
  clang/lib/StaticAnalyzer/Core/RegionStore.cpp
  clang/test/Analysis/compound-literals.c

Index: clang/test/Analysis/compound-literals.c
===================================================================
--- clang/test/Analysis/compound-literals.c
+++ clang/test/Analysis/compound-literals.c
@@ -21,3 +21,139 @@
   clang_analyzer_eval(pointers[0] == NULL); // expected-warning{{FALSE}}
   clang_analyzer_eval(pointers[1] == NULL); // expected-warning{{TRUE}}
 }
+
+void local_arr_index2() {
+  const int local_arr[8] = (const int[8]){[2] = 3, [0] = 1, [1] = 2, [3] = 4};
+  clang_analyzer_eval(local_arr[0] == 1); // expected-warning{{TRUE}}
+  clang_analyzer_eval(local_arr[1] == 2); // expected-warning{{TRUE}}
+  clang_analyzer_eval(local_arr[2] == 3); // expected-warning{{TRUE}}
+  clang_analyzer_eval(local_arr[3] == 4); // expected-warning{{TRUE}}
+  clang_analyzer_eval(local_arr[4] == 0); // expected-warning{{TRUE}}
+  clang_analyzer_eval(local_arr[5] == 0); // expected-warning{{TRUE}}
+  clang_analyzer_eval(local_arr[6] == 0); // expected-warning{{TRUE}}
+  clang_analyzer_eval(local_arr[7] == 0); // expected-warning{{TRUE}}
+}
+
+void local_arr_out_of_bound_index3() {
+  const int local_arr[8] = (const int[8]){[2] = 3, [0] = 1, [1] = 2, [3] = 4};
+  int x = -42;
+  int res = local_arr[x]; // expected-warning{{garbage or undefined}}
+}
+
+void local_arr_out_of_bound_index4() {
+  const int local_arr[8] = (const int[8]){[2] = 3, [0] = 1, [1] = 2, [3] = 4};
+  int x = 42;
+  int res = local_arr[x]; // expected-warning{{garbage or undefined}}
+}
+
+void local_arr_index3() {
+  const int local_arr[8] = (const int[8]){1, 2, 3, 4};
+  clang_analyzer_eval(local_arr[0] == 1); // expected-warning{{TRUE}}
+  clang_analyzer_eval(local_arr[1] == 2); // expected-warning{{TRUE}}
+  clang_analyzer_eval(local_arr[2] == 3); // expected-warning{{TRUE}}
+  clang_analyzer_eval(local_arr[3] == 4); // expected-warning{{TRUE}}
+  clang_analyzer_eval(local_arr[4] == 0); // expected-warning{{TRUE}}
+  clang_analyzer_eval(local_arr[5] == 0); // expected-warning{{TRUE}}
+  clang_analyzer_eval(local_arr[6] == 0); // expected-warning{{TRUE}}
+  clang_analyzer_eval(local_arr[7] == 0); // expected-warning{{TRUE}}
+}
+
+void local_arr_index4() {
+  int const local_arr[][2][3] = (int const[2][2][3]){{{1, 2}, {}}, {{7, 8}, {10, 11, 12}}};
+  clang_analyzer_eval(local_arr[0][0][0] == 1);  // expected-warning{{TRUE}}
+  clang_analyzer_eval(local_arr[0][0][1] == 2);  // expected-warning{{TRUE}}
+  clang_analyzer_eval(local_arr[0][0][2] == 0);  // expected-warning{{TRUE}}
+  clang_analyzer_eval(local_arr[0][1][0] == 0);  // expected-warning{{TRUE}}
+  clang_analyzer_eval(local_arr[0][1][1] == 0);  // expected-warning{{TRUE}}
+  clang_analyzer_eval(local_arr[0][1][2] == 0);  // expected-warning{{TRUE}}
+  clang_analyzer_eval(local_arr[1][0][0] == 7);  // expected-warning{{TRUE}}
+  clang_analyzer_eval(local_arr[1][0][1] == 8);  // expected-warning{{TRUE}}
+  clang_analyzer_eval(local_arr[1][0][2] == 0);  // expected-warning{{TRUE}}
+  clang_analyzer_eval(local_arr[1][1][0] == 10); // expected-warning{{TRUE}}
+  clang_analyzer_eval(local_arr[1][1][1] == 11); // expected-warning{{TRUE}}
+  clang_analyzer_eval(local_arr[1][1][2] == 12); // expected-warning{{TRUE}}
+}
+
+void local_arr_index5() {
+  int const(*ptr_arr)[2][3] = (int const[2][2][3]){{{1, 2}, {}}, {{7, 8}, {10, 11, 12}}};
+  clang_analyzer_eval(ptr_arr[0][0][0] == 1);  // expected-warning{{TRUE}}
+  clang_analyzer_eval(ptr_arr[0][0][1] == 2);  // expected-warning{{TRUE}}
+  clang_analyzer_eval(ptr_arr[0][0][2] == 0);  // expected-warning{{TRUE}}
+  clang_analyzer_eval(ptr_arr[0][1][0] == 0);  // expected-warning{{TRUE}}
+  clang_analyzer_eval(ptr_arr[0][1][1] == 0);  // expected-warning{{TRUE}}
+  clang_analyzer_eval(ptr_arr[0][1][2] == 0);  // expected-warning{{TRUE}}
+  clang_analyzer_eval(ptr_arr[1][0][0] == 7);  // expected-warning{{TRUE}}
+  clang_analyzer_eval(ptr_arr[1][0][1] == 8);  // expected-warning{{TRUE}}
+  clang_analyzer_eval(ptr_arr[1][0][2] == 0);  // expected-warning{{TRUE}}
+  clang_analyzer_eval(ptr_arr[1][1][0] == 10); // expected-warning{{TRUE}}
+  clang_analyzer_eval(ptr_arr[1][1][1] == 11); // expected-warning{{TRUE}}
+  clang_analyzer_eval(ptr_arr[1][1][2] == 12); // expected-warning{{TRUE}}
+}
+
+void local_arr_out_of_bound_index5() {
+  int const(*ptr_arr)[2][3] = (int const[2][2][3]){{{1, 2}, {}}, {{7, 8}, {10, 11, 12}}};
+  int idx = -42;
+  int res = ptr_arr[2][1][idx]; // expected-warning{{garbage or undefined}}
+}
+
+void local_arr_out_of_bound_index6() {
+  int const(*ptr_arr)[2][3] = (int const[2][2][3]){{{1, 2}, {}}, {{7, 8}, {10, 11, 12}}};
+  int idx = 42;
+  int res = ptr_arr[1][1][idx]; // expected-warning{{garbage or undefined}}
+}
+
+void local_direct_index1() {
+  int const *ptr = (int const *)(int const[2][2][3]){{{1, 2}, {}}, {{7, 8}, {10, 11, 12}}};
+  clang_analyzer_eval(ptr[0] == 1);   // expected-warning{{TRUE}}
+  clang_analyzer_eval(ptr[1] == 2);   // expected-warning{{TRUE}}
+  clang_analyzer_eval(ptr[2] == 0);   // expected-warning{{TRUE}}
+  clang_analyzer_eval(ptr[3] == 0);   // expected-warning{{TRUE}}
+  clang_analyzer_eval(ptr[4] == 0);   // expected-warning{{TRUE}}
+  clang_analyzer_eval(ptr[5] == 0);   // expected-warning{{TRUE}}
+  clang_analyzer_eval(ptr[6] == 7);   // expected-warning{{TRUE}}
+  clang_analyzer_eval(ptr[7] == 8);   // expected-warning{{TRUE}}
+  clang_analyzer_eval(ptr[8] == 0);   // expected-warning{{TRUE}}
+  clang_analyzer_eval(ptr[9] == 10);  // expected-warning{{TRUE}}
+  clang_analyzer_eval(ptr[10] == 11); // expected-warning{{TRUE}}
+  clang_analyzer_eval(ptr[11] == 12); // expected-warning{{TRUE}}
+}
+
+void local_direct_invalid_index1() {
+  int const *ptr = (int const *)(int const[2][2][3]){{{1, 2}, {}}, {{7, 8}, {10, 11, 12}}};
+  int idx = -42;
+  int res = ptr[idx]; // expected-warning{{garbage or undefined}}
+}
+
+void local_direct_invalid_index2() {
+  int const *ptr = (int const *)(int const[2][2][3]){{{1, 2}, {}}, {{7, 8}, {10, 11, 12}}};
+  int idx = 42;
+  int res = ptr[idx]; // expected-warning{{garbage or undefined}}
+}
+
+const int glob_arr2[8] = (const int[8]){1, 2, 3, 4};
+void glob_arr_index2() {
+  clang_analyzer_eval(glob_arr2[0] == 1); // expected-warning{{TRUE}}
+  clang_analyzer_eval(glob_arr2[1] == 2); // expected-warning{{TRUE}}
+  clang_analyzer_eval(glob_arr2[2] == 3); // expected-warning{{TRUE}}
+  clang_analyzer_eval(glob_arr2[3] == 4); // expected-warning{{TRUE}}
+  clang_analyzer_eval(glob_arr2[4] == 0); // expected-warning{{TRUE}}
+  clang_analyzer_eval(glob_arr2[5] == 0); // expected-warning{{TRUE}}
+  clang_analyzer_eval(glob_arr2[6] == 0); // expected-warning{{TRUE}}
+  clang_analyzer_eval(glob_arr2[7] == 0); // expected-warning{{TRUE}}
+}
+
+int const glob_arr3[][2][3] = (int const[2][2][3]){{{1, 2}, {}}, {{7, 8}, {10, 11, 12}}};
+void glob_arr_index3() {
+  clang_analyzer_eval(glob_arr3[0][0][0] == 1);  // expected-warning{{TRUE}}
+  clang_analyzer_eval(glob_arr3[0][0][1] == 2);  // expected-warning{{TRUE}}
+  clang_analyzer_eval(glob_arr3[0][0][2] == 0);  // expected-warning{{TRUE}}
+  clang_analyzer_eval(glob_arr3[0][1][0] == 0);  // expected-warning{{TRUE}}
+  clang_analyzer_eval(glob_arr3[0][1][1] == 0);  // expected-warning{{TRUE}}
+  clang_analyzer_eval(glob_arr3[0][1][2] == 0);  // expected-warning{{TRUE}}
+  clang_analyzer_eval(glob_arr3[1][0][0] == 7);  // expected-warning{{TRUE}}
+  clang_analyzer_eval(glob_arr3[1][0][1] == 8);  // expected-warning{{TRUE}}
+  clang_analyzer_eval(glob_arr3[1][0][2] == 0);  // expected-warning{{TRUE}}
+  clang_analyzer_eval(glob_arr3[1][1][0] == 10); // expected-warning{{TRUE}}
+  clang_analyzer_eval(glob_arr3[1][1][1] == 11); // expected-warning{{TRUE}}
+  clang_analyzer_eval(glob_arr3[1][1][2] == 12); // expected-warning{{TRUE}}
+}
Index: clang/lib/StaticAnalyzer/Core/RegionStore.cpp
===================================================================
--- clang/lib/StaticAnalyzer/Core/RegionStore.cpp
+++ clang/lib/StaticAnalyzer/Core/RegionStore.cpp
@@ -438,13 +438,15 @@
   RegionBindingsRef removeSubRegionBindings(RegionBindingsConstRef B,
                                             const SubRegion *R);
   Optional<SVal> getConstantValFromConstArrayInitializer(
-      RegionBindingsConstRef B, const VarRegion *VR, const llvm::APSInt &Idx,
-      QualType ElemT);
+      RegionBindingsConstRef B, const TypedValueRegion *TVR,
+      const llvm::APSInt &Idx, QualType ElemT);
   Optional<SVal> getSValFromInitListExprByIndex(const InitListExpr *ILE,
                                                 const llvm::APSInt &Idx,
                                                 QualType ElemT);
   SVal getSValFromStringLiteralByIndex(const StringLiteral *SL,
                                        const llvm::APSInt &Idx, QualType ElemT);
+  Optional<SVal> getSValFromCompoundLiteralExprByIndex(
+      const CompoundLiteralExpr *CLE, const llvm::APSInt &Idx, QualType ElemT);
 
 public: // Part of public interface to class.
 
@@ -1633,17 +1635,22 @@
   return Result;
 }
 Optional<SVal> RegionStoreManager::getConstantValFromConstArrayInitializer(
-    RegionBindingsConstRef B, const VarRegion *VR, const llvm::APSInt &Idx,
-    QualType ElemT) {
-  // Array should be immutable.
-  const VarDecl *VD = VR->getDecl();
-  if (!VD->getType().isConstQualified() && !ElemT.isConstQualified() &&
-      (!B.isMainAnalysis() || !VD->hasGlobalStorage()))
-    return None;
+    RegionBindingsConstRef B, const TypedValueRegion *TVR,
+    const llvm::APSInt &Idx, QualType ElemT) {
+  const Expr *Init = nullptr;
+  // Caller garantees `TVR` to be either `VarRegion` or `CompoundLiteralRegion`.
+  if (const auto *VR = dyn_cast<VarRegion>(TVR)) {
+    // Array should be immutable.
+    const VarDecl *VD = VR->getDecl();
+    if (!VD->getType().isConstQualified() && !ElemT.isConstQualified() &&
+        (!B.isMainAnalysis() || !VD->hasGlobalStorage()))
+      return None;
+    Init = VD->getAnyInitializer();
+  } else {
+    Init = cast<CompoundLiteralRegion>(TVR)->getLiteralExpr();
+  }
 
   // Array should have an initializer.
-  const Expr *Init = VD->getAnyInitializer();
-
   if (!Init)
     return None;
 
@@ -1655,7 +1662,9 @@
   if (const auto *SL = dyn_cast<StringLiteral>(Init))
     return getSValFromStringLiteralByIndex(SL, Idx, ElemT);
 
-  // FIXME: Handle CompoundLiteralExpr.
+  // Handle CompoundLiteralExpr.
+  if (const auto *CLE = dyn_cast<CompoundLiteralExpr>(Init))
+    return getSValFromCompoundLiteralExprByIndex(CLE, Idx, ElemT);
 
   return None;
 }
@@ -1699,6 +1708,15 @@
   return svalBuilder.makeIntVal(Code, ElemT);
 }
 
+Optional<SVal> RegionStoreManager::getSValFromCompoundLiteralExprByIndex(
+    const CompoundLiteralExpr *CLE, const llvm::APSInt &Idx, QualType ElemT) {
+  assert(CLE && "CompoundLiteralExpr should not be null");
+  const Expr *Init = CLE->getInitializer();
+  if (const auto *ILE = dyn_cast<InitListExpr>(Init))
+    return getSValFromInitListExprByIndex(ILE, Idx, ElemT);
+  return None;
+}
+
 SVal RegionStoreManager::getBindingForElement(RegionBindingsConstRef B,
                                               const ElementRegion* R) {
   // Check if the region has a binding.
@@ -1720,15 +1738,18 @@
       const StringLiteral *Str = StrR->getStringLiteral();
       return getSValFromStringLiteralByIndex(Str, CI->getValue(), T);
     }
-  } else if (isa<ElementRegion>(superR) || isa<VarRegion>(superR)) {
-    const VarRegion *VR = nullptr;
+  } else if (isa<ElementRegion>(superR) || isa<VarRegion>(superR) ||
+             isa<CompoundLiteralRegion>(superR)) {
+    const TypedValueRegion *TVR = nullptr;
     // Prepare APSInt as int64_t.
     llvm::APSInt Idx(64, false);
     if (isa<ElementRegion>(superR)) {
       // Check if it is a mutli-dimensional array.
       const RegionRawOffset &RRO = R->getAsArrayOffset();
-      VR = dyn_cast_or_null<VarRegion>(RRO.getRegion());
-      if (VR) {
+      TVR = dyn_cast_or_null<VarRegion>(RRO.getRegion());
+      if (!TVR)
+        TVR = dyn_cast<CompoundLiteralRegion>(RRO.getRegion());
+      if (TVR) {
         // FIXME: We are limited here due to RegionRawOffset only contains
         // offset as signed integer. Adjust the code when
         // ElementRegion::getAsArrayOffset supports unsigned offsets.
@@ -1737,15 +1758,15 @@
       }
     } else if (auto CI = R->getIndex().getAs<nonloc::ConcreteInt>()) {
       // Check if it is a single-dimensional array.
-      VR = cast<VarRegion>(superR);
+      TVR = cast<TypedValueRegion>(superR);
       const llvm::APSInt Int = CI->getValue();
       Idx = Int.getExtValue();
       Idx.setIsUnsigned(Int.isUnsigned());
     }
 
-    if (VR) {
+    if (TVR) {
       if (Optional<SVal> V = getConstantValFromConstArrayInitializer(
-              B, VR, Idx, R->getElementType()))
+              B, TVR, Idx, R->getElementType()))
         return *V;
     }
   }
@@ -2251,6 +2272,7 @@
   // `getConstantValFromConstArrayInitializer`.For example:
   //   const int arr[42] = { 1, 2, 3 };
   //   const char arr[42] = "123";
+  //   const int arr[42] = (const int[42]){ 1, 2, 3 };
   // The init values of this array will never change, so we don't have to
   // store them additionally in the RegionStore.
   if (const auto *VR = dyn_cast<VarRegion>(R)) {
Index: clang/lib/AST/Expr.cpp
===================================================================
--- clang/lib/AST/Expr.cpp
+++ clang/lib/AST/Expr.cpp
@@ -2381,6 +2381,10 @@
 
     const Stmt *Stmt = InitList->InitExprs[I];
 
+    // Ignore wrappers.
+    if (const auto *CE = dyn_cast<ConstantExpr>(Stmt))
+      Stmt = CE->getSubExpr();
+
     // If it is not an InitListExpr, then we've reached the actual values.
     // Return this Expr.
     if (!(InitList = dyn_cast<InitListExpr>(Stmt)))
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to