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)}} +} _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits