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(Stmt)) + Stmt = CE->getSubExpr(); + // If it is not an InitListExpr, then we've reached the actual values. // Return this Expr. if (!(InitList = dyn_cast(Stmt))) 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 getConstantValFromConstArrayInitializer( - RegionBindingsConstRef B, const VarRegion *VR, const llvm::APSInt &Idx, - QualType ElemT); + RegionBindingsConstRef B, const TypedValueRegion *TVR, + const llvm::APSInt &Idx, QualType ElemT); Optional getSValFromInitListExprByIndex(const InitListExpr *ILE, const llvm::APSInt &Idx, QualType ElemT); SVal getSValFromStringLiteralByIndex(const StringLiteral *SL, const llvm::APSInt &Idx, QualType ElemT); + Optional getSValFromCompoundLiteralExprByIndex( + const CompoundLiteralExpr *CLE, const llvm::APSInt &Idx, QualType ElemT); public: // Part of public interface to class. @@ -1633,17 +1635,22 @@ return Result; } Optional 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(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(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(Init)) return getSValFromStringLiteralByIndex(SL, Idx, ElemT); - // FIXME: Handle CompoundLiteralExpr. + // Handle CompoundLiteralExpr. + if (const auto *CLE = dyn_cast(Init)) + return getSValFromCompoundLiteralExprByIndex(CLE, Idx, ElemT); return None; } @@ -1699,6 +1708,15 @@ return svalBuilder.makeIntVal(Code, ElemT); } +Optional 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(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(superR) || isa(superR)) { - const VarRegion *VR = nullptr; + } else if (isa(superR) || isa(superR) || + isa(superR)) { + const TypedValueRegion *TVR = nullptr; // Prepare APSInt as int64_t. llvm::APSInt Idx(64, false); if (isa(superR)) { // Check if it is a mutli-dimensional array. const RegionRawOffset &RRO = R->getAsArrayOffset(); - VR = dyn_cast_or_null(RRO.getRegion()); - if (VR) { + TVR = dyn_cast_or_null(RRO.getRegion()); + if (!TVR) + TVR = dyn_cast(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()) { // Check if it is a single-dimensional array. - VR = cast(superR); + TVR = cast(superR); const llvm::APSInt Int = CI->getValue(); Idx = Int.getExtValue(); Idx.setIsUnsigned(Int.isUnsigned()); } - if (VR) { + if (TVR) { if (Optional 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(R)) { 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}} +}