Index: clang/lib/StaticAnalyzer/Core/RegionStore.cpp =================================================================== --- clang/lib/StaticAnalyzer/Core/RegionStore.cpp +++ clang/lib/StaticAnalyzer/Core/RegionStore.cpp @@ -1680,52 +1680,63 @@ QualType ElemT = R->getElementType(); return getSValFromStringLiteralByIndex(SL, Idx, ElemT); } - } else if (const auto *InitList = dyn_cast(Init)) { - // The array index has to be known. - if (auto CI = R->getIndex().getAs()) { - // If it is not an array, return Undef. - QualType T = VD->getType(); - const ConstantArrayType *CAT = Ctx.getAsConstantArrayType(T); - if (!CAT) - return UndefinedVal(); - - // Support one-dimensional array. - // C++20 [expr.add] 7.6.6.4 (excerpt): - // If P points to an array element i of an array object x with n - // elements, where i < 0 or i > n, the behavior is undefined. - // Dereferencing is not allowed on the "one past the last - // element", when i == n. - // Example: - // const int arr[4] = {1, 2}; - // const int *ptr = arr; - // int x0 = ptr[0]; // 1 - // int x1 = ptr[1]; // 2 - // int x2 = ptr[2]; // 0 - // int x3 = ptr[3]; // 0 - // int x4 = ptr[4]; // UB - // TODO: Support multidimensional array. - if (!isa(CAT->getElementType())) { - // One-dimensional array. - const llvm::APSInt &Idx = CI->getValue(); - const auto I = static_cast(Idx.getExtValue()); - // Use `getZExtValue` because array extent can not be negative. - const uint64_t Extent = CAT->getSize().getZExtValue(); - // Check for `Idx < 0`, NOT for `I < 0`, because `Idx` CAN be - // negative, but `I` can NOT. - if (Idx < 0 || I >= Extent) + } else { + const InitListExpr *InitList = dyn_cast(Init); + if (!InitList) { + if (const auto *CLE = dyn_cast(Init)) { + const Expr *Init = CLE->getInitializer(); + if (const auto *ILE = dyn_cast(Init)) + InitList = ILE; + } + } + if (InitList) { + // The array index has to be known. + if (auto CI = R->getIndex().getAs()) { + // If it is not an array, return Undef. + QualType T = VD->getType(); + const ConstantArrayType *CAT = Ctx.getAsConstantArrayType(T); + if (!CAT) return UndefinedVal(); - // C++20 [expr.add] 9.4.17.5 (excerpt): - // i-th array element is value-initialized for each k < i ≤ n, - // where k is an expression-list size and n is an array extent. - if (I >= InitList->getNumInits()) - return svalBuilder.makeZeroVal(R->getElementType()); - - // Return a constant value, if it is presented. - // FIXME: Support other SVals. - const Expr *E = InitList->getInit(I); - if (Optional V = svalBuilder.getConstantVal(E)) - return *V; + // Support one-dimensional array. + // C++20 [expr.add] 7.6.6.4 (excerpt): + // If P points to an array element i of an array object x with n + // elements, where i < 0 or i > n, the behavior is undefined. + // Dereferencing is not allowed on the "one past the last + // element", when i == n. + // Example: + // const int arr[4] = {1, 2}; + // const int *ptr = arr; + // int x0 = ptr[0]; // 1 + // int x1 = ptr[1]; // 2 + // int x2 = ptr[2]; // 0 + // int x3 = ptr[3]; // 0 + // int x4 = ptr[4]; // UB + // TODO: Support multidimensional array. + if (!isa(CAT->getElementType())) { + // One-dimensional array. + const llvm::APSInt &Idx = CI->getValue(); + const auto I = static_cast(Idx.getExtValue()); + // Use `getZExtValue` because array extent can not be negative. + const uint64_t Extent = CAT->getSize().getZExtValue(); + // Check for `Idx < 0`, NOT for `I < 0`, because `Idx` CAN be + // negative, but `I` can NOT. + if (Idx < 0 || I >= Extent) + return UndefinedVal(); + + // C++20 [expr.add] 9.4.17.5 (excerpt): + // i-th array element is value-initialized for each k < i ≤ n, + // where k is an expression-list size and n is an array + // extent. + if (I >= InitList->getNumInits()) + return svalBuilder.makeZeroVal(R->getElementType()); + + // Return a constant value, if it is presented. + // FIXME: Support other SVals. + const Expr *E = InitList->getInit(I); + if (Optional V = svalBuilder.getConstantVal(E)) + return *V; + } } } } Index: clang/test/Analysis/compound-literals.c =================================================================== --- clang/test/Analysis/compound-literals.c +++ clang/test/Analysis/compound-literals.c @@ -1,5 +1,5 @@ // RUN: %clang_cc1 -triple=i386-apple-darwin10 -verify %s -analyze \ -// RUN: -analyzer-checker=debug.ExprInspection +// RUN: -analyzer-checker=debug.ExprInspection,core.uninitialized.Assign #define NULL 0 void clang_analyzer_eval(int); @@ -21,3 +21,63 @@ clang_analyzer_eval(pointers[0] == NULL); // expected-warning{{FALSE}} clang_analyzer_eval(pointers[1] == NULL); // expected-warning{{TRUE}} } + +const int glob_arr1[8] = (const int[8]){[2] = 3, [0] = 1, [1] = 2, [3] = 4}; +void glob_arr_index1() { + clang_analyzer_eval(glob_arr1[0] == 1); // expected-warning{{TRUE}} + clang_analyzer_eval(glob_arr1[1] == 2); // expected-warning{{TRUE}} + clang_analyzer_eval(glob_arr1[2] == 3); // expected-warning{{TRUE}} + clang_analyzer_eval(glob_arr1[3] == 4); // expected-warning{{TRUE}} + clang_analyzer_eval(glob_arr1[4] == 0); // expected-warning{{TRUE}} + clang_analyzer_eval(glob_arr1[5] == 0); // expected-warning{{TRUE}} + clang_analyzer_eval(glob_arr1[6] == 0); // expected-warning{{TRUE}} + clang_analyzer_eval(glob_arr1[7] == 0); // expected-warning{{TRUE}} +} + +void glob_invalid_index1() { + int x = -42; + int res = glob_arr1[x]; // expected-warning{{garbage or undefined}} +} + +void glob_invalid_index2() { + int x = 42; + int res = glob_arr1[x]; // 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}} +} + +void glob_ptr_index1() { + int const *ptr = glob_arr2; + clang_analyzer_eval(ptr[0] == 1); // expected-warning{{TRUE}} + clang_analyzer_eval(ptr[1] == 2); // expected-warning{{TRUE}} + clang_analyzer_eval(ptr[2] == 3); // expected-warning{{TRUE}} + clang_analyzer_eval(ptr[3] == 4); // 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] == 0); // expected-warning{{TRUE}} + clang_analyzer_eval(ptr[7] == 0); // expected-warning{{TRUE}} + clang_analyzer_eval(ptr[8] == 0); // expected-warning{{UNDEFINED}} + clang_analyzer_eval(ptr[9] == 0); // expected-warning{{UNDEFINED}} +} + +void glob_invalid_index3() { + int const *ptr = glob_arr2; + int idx = -42; + int res = ptr[idx]; // expected-warning{{garbage or undefined}} +} + +void glob_invalid_index4() { + int const *ptr = glob_arr2; + int idx = 42; + int res = ptr[idx]; // expected-warning{{garbage or undefined}} +}