ASDenysPetrov updated this revision to Diff 366209.
ASDenysPetrov added a comment.
Fixed //smell// code: from `isa'n'cast` to `dyn-cast`.
CHANGES SINCE LAST ACTION
https://reviews.llvm.org/D104285/new/
https://reviews.llvm.org/D104285
Files:
clang/include/clang/AST/Expr.h
clang/include/clang/AST/Type.h
clang/lib/AST/Expr.cpp
clang/lib/AST/Type.cpp
clang/lib/StaticAnalyzer/Core/RegionStore.cpp
clang/test/Analysis/initialization.cpp
Index: clang/test/Analysis/initialization.cpp
===================================================================
--- clang/test/Analysis/initialization.cpp
+++ clang/test/Analysis/initialization.cpp
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -std=c++14 -triple i386-apple-darwin10 -analyze -analyzer-checker=core.builtin,debug.ExprInspection -verify %s
+// RUN: %clang_cc1 -std=c++14 -triple i386-apple-darwin10 -analyze -analyzer-checker=core.uninitialized.Assign,core.builtin,debug.ExprInspection,core.uninitialized.UndefReturn -verify %s
void clang_analyzer_eval(int);
@@ -18,3 +18,114 @@
// FIXME: Should recognize that it is 0.
clang_analyzer_eval(arr[i][0]); // expected-warning{{UNKNOWN}}
}
+
+void direct_index1() {
+ int const arr[2][2][3] = {};
+ int const *ptr = (int const *)arr;
+ clang_analyzer_eval(ptr[0] == 0); // expected-warning{{TRUE}}
+ clang_analyzer_eval(ptr[1] == 0); // 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] == 0); // expected-warning{{TRUE}}
+ clang_analyzer_eval(ptr[7] == 0); // expected-warning{{TRUE}}
+ clang_analyzer_eval(ptr[8] == 0); // expected-warning{{TRUE}}
+ clang_analyzer_eval(ptr[9] == 0); // expected-warning{{TRUE}}
+ clang_analyzer_eval(ptr[10] == 0); // expected-warning{{TRUE}}
+ clang_analyzer_eval(ptr[11] == 0); // expected-warning{{TRUE}}
+}
+
+void direct_index2() {
+ int const arr[2][2][3] = {{{1, 2, 3}, {4, 5, 6}}, {{7, 8, 9}, {10, 11, 12}}};
+ int const *ptr = arr[0][0];
+ 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] == 5); // expected-warning{{TRUE}}
+ clang_analyzer_eval(ptr[5] == 6); // 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] == 9); // 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 direct_index3() {
+ int const arr[2][2][3] = {{{}, {}}, {{}, {}}};
+ int const *ptr = &(arr[0][0][0]);
+ clang_analyzer_eval(ptr[0] == 0); // expected-warning{{TRUE}}
+ clang_analyzer_eval(ptr[1] == 0); // 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] == 0); // expected-warning{{TRUE}}
+ clang_analyzer_eval(ptr[7] == 0); // expected-warning{{TRUE}}
+ clang_analyzer_eval(ptr[8] == 0); // expected-warning{{TRUE}}
+ clang_analyzer_eval(ptr[9] == 0); // expected-warning{{TRUE}}
+ clang_analyzer_eval(ptr[10] == 0); // expected-warning{{TRUE}}
+ clang_analyzer_eval(ptr[11] == 0); // expected-warning{{TRUE}}
+}
+
+void direct_index4() {
+ int const arr[2][2][3] = {{{1, 2}, {}}, {{7, 8}, {10, 11, 12}}};
+ int const *ptr = (int const *)arr[0];
+ 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 direct_index5() {
+ int arr[2][2][3] = {{{1, 2}}, {{7}}};
+ int *ptr = (int *)arr;
+ 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] == 0); // expected-warning{{TRUE}}
+ clang_analyzer_eval(ptr[8] == 0); // expected-warning{{TRUE}}
+ clang_analyzer_eval(ptr[9] == 0); // expected-warning{{TRUE}}
+ clang_analyzer_eval(ptr[10] == 0); // expected-warning{{TRUE}}
+ clang_analyzer_eval(ptr[11] == 0); // expected-warning{{TRUE}}
+}
+
+void direct_invalid_index1() {
+ const int arr[2][2][3] = {};
+ const int *ptr = (const int *)arr;
+ int idx = -1;
+ auto x = ptr[idx]; // expected-warning{{garbage or undefined}}
+}
+
+void direct_invalid_index2() {
+ const int arr[2][2][3] = {};
+ const int *ptr = (const int *)arr;
+ int idx = 42;
+ auto x = ptr[idx]; // expected-warning{{garbage or undefined}}
+}
+
+static const unsigned RV[1][5] = {{1, 2, 3, 4, 5}};
+void PR50604() {
+ const unsigned *rvp = &(RV[0][0]);
+ auto buf_p = rvp[4]; // no-warning (garbage or undefined)
+}
+
+const float floats[] = {
+ 0.0000, 0.0235, 0.0470, 0.0706, 0.0941, 0.1176};
+float no_warn_garbage_value() {
+ return floats[0]; // no-warning (garbage or undefined)
+}
Index: clang/lib/StaticAnalyzer/Core/RegionStore.cpp
===================================================================
--- clang/lib/StaticAnalyzer/Core/RegionStore.cpp
+++ clang/lib/StaticAnalyzer/Core/RegionStore.cpp
@@ -1668,23 +1668,31 @@
if (const auto *InitList = dyn_cast<InitListExpr>(Init)) {
// The array index has to be known.
if (auto CI = R->getIndex().getAs<nonloc::ConcreteInt>()) {
- int64_t i = CI->getValue().getSExtValue();
- // If it is known that the index is out of bounds, we can return
- // an undefined value.
- if (i < 0)
+ const llvm::APSInt &Int = CI->getValue();
+ int64_t Idx = Int.getExtValue();
+
+ // We shall care about signed negative indexes according to
+ // `getExprForConstArrayByRawIndex` contract.
+ // If index is out of bounds. Return Undef.
+ if (Int.isSigned() && Idx < 0)
return UndefinedVal();
- if (auto CAT = Ctx.getAsConstantArrayType(VD->getType()))
- if (CAT->getSize().sle(i))
- return UndefinedVal();
+ // Get an actual Expr by index.
+ const Expr *E = InitList->getExprForConstArrayByRawIndex(
+ Ctx, static_cast<uint64_t>(Idx));
+
+ // If E is null, then the index is out of bounds. Return Undef.
+ if (!E)
+ return UndefinedVal();
- // If there is a list, but no init, it must be zero.
- if (i >= InitList->getNumInits())
+ // If E is the same as InitList, then there is no value specified
+ // in the list and we shall return a zero value.
+ if (E == InitList)
return svalBuilder.makeZeroVal(R->getElementType());
- if (const Expr *ElemInit = InitList->getInit(i))
- if (Optional<SVal> V = svalBuilder.getConstantVal(ElemInit))
- return *V;
+ // Return a constant value.
+ if (Optional<SVal> V = svalBuilder.getConstantVal(E))
+ return *V;
}
}
}
Index: clang/lib/AST/Type.cpp
===================================================================
--- clang/lib/AST/Type.cpp
+++ clang/lib/AST/Type.cpp
@@ -138,6 +138,18 @@
ArrayTypeBits.SizeModifier = sm;
}
+/// Return an array with extents of the declared array type.
+///
+/// E.g. for `const int x[1][2][3];` returns {1,2,3}.
+SmallVector<uint64_t, 2> ConstantArrayType::getAllExtents() const {
+ SmallVector<uint64_t, 2> Extents;
+ const ConstantArrayType *CAT = this;
+ do {
+ Extents.push_back(*CAT->getSize().getRawData());
+ } while ((CAT = dyn_cast<ConstantArrayType>(CAT->getElementType())));
+ return Extents;
+}
+
unsigned ConstantArrayType::getNumAddressingBits(const ASTContext &Context,
QualType ElementType,
const llvm::APInt &NumElements) {
Index: clang/lib/AST/Expr.cpp
===================================================================
--- clang/lib/AST/Expr.cpp
+++ clang/lib/AST/Expr.cpp
@@ -36,6 +36,7 @@
#include "llvm/Support/raw_ostream.h"
#include <algorithm>
#include <cstring>
+#include <numeric>
using namespace clang;
const Expr *Expr::getBestDynamicClassTypeExpr() const {
@@ -2346,6 +2347,51 @@
return Lit && Lit->getValue() == 0;
}
+const Expr *InitListExpr::getExprForConstArrayByRawIndex(ASTContext &Ctx,
+ uint64_t Idx) const {
+ // Make sure this is a list initialization for const array.
+ QualType T = getType();
+ const ConstantArrayType *CAT = Ctx.getAsConstantArrayType(T);
+ if (!CAT)
+ return nullptr;
+
+ SmallVector<uint64_t, 2> Extents = CAT->getAllExtents();
+ // Calculate array total size.
+ uint64_t Size = std::accumulate(Extents.begin(), Extents.end(), uint64_t(1),
+ std::multiplies<uint64_t>());
+
+ // Return null if index is out of array bounds.
+ if (Idx >= Size)
+ return nullptr;
+
+ // We can iterate through the multi-dimensional array the same as through
+ // one-dimensional array, because of the solid memory allocation.
+ const InitListExpr *InitList = this;
+ uint64_t I = 0;
+ for (uint64_t Ext : Extents) {
+ Size /= Ext;
+ I = Idx / Size;
+ Idx -= I * Size;
+
+ // If there is a list but no value, it must be zero.
+ // Return `this` to notify the user of this particular case.
+ // This should be handled by a caller.
+ if (I >= InitList->InitExprs.size())
+ return this;
+
+ const Stmt *Stmt = InitList->InitExprs[I];
+
+ // If it is not an InitListExpr, then we've reached the actual values.
+ // Return this Expr.
+ if (!(InitList = dyn_cast<InitListExpr>(Stmt)))
+ return cast<Expr>(Stmt);
+ }
+
+ // We shouldn't reach here unless we missed some legal syntax while parsing.
+ // Otherwise, fix parsing logic above.
+ llvm_unreachable("List initialization is formed in an unusual way.");
+}
+
SourceLocation InitListExpr::getBeginLoc() const {
if (InitListExpr *SyntacticForm = getSyntacticForm())
return SyntacticForm->getBeginLoc();
Index: clang/include/clang/AST/Type.h
===================================================================
--- clang/include/clang/AST/Type.h
+++ clang/include/clang/AST/Type.h
@@ -2949,6 +2949,7 @@
public:
const llvm::APInt &getSize() const { return Size; }
+ SmallVector<uint64_t, 2> getAllExtents() const;
const Expr *getSizeExpr() const {
return ConstantArrayTypeBits.HasStoredSizeExpr
? *getTrailingObjects<const Expr *>()
Index: clang/include/clang/AST/Expr.h
===================================================================
--- clang/include/clang/AST/Expr.h
+++ clang/include/clang/AST/Expr.h
@@ -4962,6 +4962,24 @@
InitListExprBits.HadArrayRangeDesignator = ARD;
}
+ /// Return a value-expression under the given index.
+ ///
+ /// \param Idx Direct index beginning from the first value of the list.
+ /// It's a direct index as it would be in a one-dimentional array.
+ /// For instance for `Idx = 3`:
+ /// - `const T x[2][2] = {{1,2},{3,4}}` returns '4';
+ /// - `const T x[2][2] = {{1,},{3,4}}` returns '4';
+ /// - `const T x[3][2] = {{1,},{},{5,6}}` returns '0'.
+ /// \returns
+ /// - value-expression for the valid index;
+ /// - `this` if there's no explicit expression for the valid index;
+ /// - `nullptr` if `Idx >= array_size` or Expr type is not
+ /// `ConstantArrayType`.
+ /// NOTE: Caller shall care about signed negative indexes to avoid unwanted
+ /// conversion.
+ const Expr *getExprForConstArrayByRawIndex(ASTContext &Ctx,
+ uint64_t Idx) const;
+
SourceLocation getBeginLoc() const LLVM_READONLY;
SourceLocation getEndLoc() const LLVM_READONLY;
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits