[clang] e48f444 - [Sema] Fix -Warray-bounds false negative when casting an out-of-bounds array item
via cfe-commits
cfe-commits at lists.llvm.org
Wed Feb 3 05:51:14 PST 2021
Author: Ilya Mirsky
Date: 2021-02-03T07:50:50-06:00
New Revision: e48f444751cf781c42934b242b81f549da77bad0
URL: https://github.com/llvm/llvm-project/commit/e48f444751cf781c42934b242b81f549da77bad0
DIFF: https://github.com/llvm/llvm-project/commit/e48f444751cf781c42934b242b81f549da77bad0.diff
LOG: [Sema] Fix -Warray-bounds false negative when casting an out-of-bounds array item
Patch by Ilya Mirsky!
Fixes: http://llvm.org/PR44343
Reviewed By: rsmith
Differential Revision: https://reviews.llvm.org/D71714
Added:
Modified:
clang/include/clang/Sema/Sema.h
clang/lib/Sema/SemaChecking.cpp
clang/test/Parser/cxx-ambig-decl-expr.cpp
clang/test/SemaCXX/array-bounds.cpp
Removed:
################################################################################
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 7f7c84eb1b1d..2fca81d25345 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -12310,7 +12310,7 @@ class Sema final {
void CheckArrayAccess(const Expr *BaseExpr, const Expr *IndexExpr,
const ArraySubscriptExpr *ASE=nullptr,
bool AllowOnePastEnd=true, bool IndexNegated=false);
- void CheckArrayAccess(const Expr *E);
+ void CheckArrayAccess(const Expr *E, int AllowOnePastEnd = 0);
// Used to grab the relevant information from a FormatAttr and a
// FunctionDeclaration.
struct FormatStringInfo {
diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp
index 2d3d36f4adad..17dd5c9e8d99 100644
--- a/clang/lib/Sema/SemaChecking.cpp
+++ b/clang/lib/Sema/SemaChecking.cpp
@@ -14387,62 +14387,63 @@ void Sema::CheckArrayAccess(const Expr *BaseExpr, const Expr *IndexExpr,
PDiag(diag::note_array_declared_here) << ND);
}
-void Sema::CheckArrayAccess(const Expr *expr) {
- int AllowOnePastEnd = 0;
- while (expr) {
- expr = expr->IgnoreParenImpCasts();
- switch (expr->getStmtClass()) {
- case Stmt::ArraySubscriptExprClass: {
- const ArraySubscriptExpr *ASE = cast<ArraySubscriptExpr>(expr);
- CheckArrayAccess(ASE->getBase(), ASE->getIdx(), ASE,
- AllowOnePastEnd > 0);
- expr = ASE->getBase();
- break;
- }
- case Stmt::MemberExprClass: {
- expr = cast<MemberExpr>(expr)->getBase();
- break;
- }
- case Stmt::OMPArraySectionExprClass: {
- const OMPArraySectionExpr *ASE = cast<OMPArraySectionExpr>(expr);
- if (ASE->getLowerBound())
- CheckArrayAccess(ASE->getBase(), ASE->getLowerBound(),
- /*ASE=*/nullptr, AllowOnePastEnd > 0);
- return;
- }
- case Stmt::UnaryOperatorClass: {
- // Only unwrap the * and & unary operators
- const UnaryOperator *UO = cast<UnaryOperator>(expr);
- expr = UO->getSubExpr();
- switch (UO->getOpcode()) {
- case UO_AddrOf:
- AllowOnePastEnd++;
- break;
- case UO_Deref:
- AllowOnePastEnd--;
- break;
- default:
- return;
- }
- break;
- }
- case Stmt::ConditionalOperatorClass: {
- const ConditionalOperator *cond = cast<ConditionalOperator>(expr);
- if (const Expr *lhs = cond->getLHS())
- CheckArrayAccess(lhs);
- if (const Expr *rhs = cond->getRHS())
- CheckArrayAccess(rhs);
- return;
- }
- case Stmt::CXXOperatorCallExprClass: {
- const auto *OCE = cast<CXXOperatorCallExpr>(expr);
- for (const auto *Arg : OCE->arguments())
- CheckArrayAccess(Arg);
- return;
- }
- default:
- return;
+void Sema::CheckArrayAccess(const Expr *expr, int AllowOnePastEnd) {
+ if (!expr)
+ return;
+
+ expr = expr->IgnoreParenCasts();
+ switch (expr->getStmtClass()) {
+ case Stmt::ArraySubscriptExprClass: {
+ const ArraySubscriptExpr *ASE = cast<ArraySubscriptExpr>(expr);
+ CheckArrayAccess(ASE->getBase(), ASE->getIdx(), ASE, AllowOnePastEnd > 0);
+ CheckArrayAccess(ASE->getBase(), AllowOnePastEnd);
+ return;
+ }
+ case Stmt::MemberExprClass: {
+ expr = cast<MemberExpr>(expr)->getBase();
+ CheckArrayAccess(expr, /*AllowOnePastEnd=*/0);
+ return;
+ }
+ case Stmt::OMPArraySectionExprClass: {
+ const OMPArraySectionExpr *ASE = cast<OMPArraySectionExpr>(expr);
+ if (ASE->getLowerBound())
+ CheckArrayAccess(ASE->getBase(), ASE->getLowerBound(),
+ /*ASE=*/nullptr, AllowOnePastEnd > 0);
+ return;
+ }
+ case Stmt::UnaryOperatorClass: {
+ // Only unwrap the * and & unary operators
+ const UnaryOperator *UO = cast<UnaryOperator>(expr);
+ expr = UO->getSubExpr();
+ switch (UO->getOpcode()) {
+ case UO_AddrOf:
+ AllowOnePastEnd++;
+ break;
+ case UO_Deref:
+ AllowOnePastEnd--;
+ break;
+ default:
+ return;
}
+ CheckArrayAccess(expr, AllowOnePastEnd);
+ return;
+ }
+ case Stmt::ConditionalOperatorClass: {
+ const ConditionalOperator *cond = cast<ConditionalOperator>(expr);
+ if (const Expr *lhs = cond->getLHS())
+ CheckArrayAccess(lhs, AllowOnePastEnd);
+ if (const Expr *rhs = cond->getRHS())
+ CheckArrayAccess(rhs, AllowOnePastEnd);
+ return;
+ }
+ case Stmt::CXXOperatorCallExprClass: {
+ const auto *OCE = cast<CXXOperatorCallExpr>(expr);
+ for (const auto *Arg : OCE->arguments())
+ CheckArrayAccess(Arg);
+ return;
+ }
+ default:
+ return;
}
}
diff --git a/clang/test/Parser/cxx-ambig-decl-expr.cpp b/clang/test/Parser/cxx-ambig-decl-expr.cpp
index 6203db2fbd22..373fe250c6b5 100644
--- a/clang/test/Parser/cxx-ambig-decl-expr.cpp
+++ b/clang/test/Parser/cxx-ambig-decl-expr.cpp
@@ -24,7 +24,7 @@ void arr() {
// This is array indexing not an array declarator because a comma expression
// is not syntactically a constant-expression.
- int(x[1,1]); // expected-warning 2{{unused}}
+ int(x[1,0]); // expected-warning 2{{unused}}
// This is array indexing not an array declaration because a braced-init-list
// is not syntactically a constant-expression.
diff --git a/clang/test/SemaCXX/array-bounds.cpp b/clang/test/SemaCXX/array-bounds.cpp
index 47be6c2423dc..837175014fe5 100644
--- a/clang/test/SemaCXX/array-bounds.cpp
+++ b/clang/test/SemaCXX/array-bounds.cpp
@@ -27,7 +27,7 @@ template <char *sz> class Qux {
};
void f1(int a[1]) {
- int val = a[3]; // no warning for function argumnet
+ int val = a[3]; // no warning for function argument
}
void f2(const int (&a)[2]) { // expected-note {{declared here}}
@@ -133,7 +133,7 @@ int test_pr9296() {
int test_sizeof_as_condition(int flag) {
int arr[2] = { 0, 0 }; // expected-note {{array 'arr' declared here}}
- if (flag)
+ if (flag)
return sizeof(char) != sizeof(char) ? arr[2] : arr[1];
return sizeof(char) == sizeof(char) ? arr[2] : arr[1]; // expected-warning {{array index 2 is past the end of the array (which contains 2 elements)}}
}
@@ -241,7 +241,7 @@ void test_pr10771() {
}
int test_pr11007_aux(const char * restrict, ...);
-
+
// Test checking with varargs.
void test_pr11007() {
double a[5]; // expected-note {{array 'a' declared here}}
@@ -320,3 +320,33 @@ void test() {
arr<float>[1] = 0; // expected-warning {{array index 1 is past the end of the array (which contains 1 element)}}
}
} // namespace var_template_array
+
+namespace PR44343 {
+ const unsigned int array[2] = {0, 1}; // expected-note 5{{array 'array' declared here}}
+
+ const int i1 = (const int)array[2]; // expected-warning {{array index 2 is past the end of the array (which contains 2 elements)}}
+ const int i2 = static_cast<const int>(array[2]); // expected-warning {{array index 2 is past the end of the array (which contains 2 elements)}}
+ const int &i3 = reinterpret_cast<const int&>(array[2]); // expected-warning {{array index 2 is past the end of the array (which contains 2 elements)}}
+ unsigned int &i4 = const_cast<unsigned int&>(array[2]); // expected-warning {{array index 2 is past the end of the array (which contains 2 elements)}}
+ int i5 = int(array[2]); // expected-warning {{array index 2 is past the end of the array (which contains 2 elements)}}
+ const unsigned int *i6 = &(1 > 0 ? array[2] : array[1]); // no warning for one-past-end element's address retrieval
+
+ // Test dynamic cast
+ struct Base {
+ virtual ~Base();
+ };
+ struct Derived : Base {
+ };
+ Base baseArr[2]; // expected-note {{array 'baseArr' declared here}}
+ Derived *d1 = dynamic_cast<Derived *>(&baseArr[2]); // FIXME: Should actually warn because dynamic_cast accesses the vptr
+ Derived &d2 = dynamic_cast<Derived &>(baseArr[2]); // expected-warning {{array index 2 is past the end of the array (which contains 2 elements)}}
+
+ // Test operator `&` in combination with operators `.` and `->`
+ struct A {
+ int n;
+ };
+ A a[2]; // expected-note {{array 'a' declared here}}
+ int *n = &a[2].n; // expected-warning {{array index 2 is past the end of the array (which contains 2 elements)}}
+ A *aPtr[2]; // expected-note {{array 'aPtr' declared here}}
+ int *n2 = &aPtr[2]->n; // expected-warning {{array index 2 is past the end of the array (which contains 2 elements)}}
+}
More information about the cfe-commits
mailing list