diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -12310,7 +12310,7 @@ 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 --- a/clang/lib/Sema/SemaChecking.cpp +++ b/clang/lib/Sema/SemaChecking.cpp @@ -14387,62 +14387,63 @@ 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(expr); - CheckArrayAccess(ASE->getBase(), ASE->getIdx(), ASE, - AllowOnePastEnd > 0); - expr = ASE->getBase(); - break; - } - case Stmt::MemberExprClass: { - expr = cast(expr)->getBase(); - break; - } - case Stmt::OMPArraySectionExprClass: { - const OMPArraySectionExpr *ASE = cast(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(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(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(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(expr); + CheckArrayAccess(ASE->getBase(), ASE->getIdx(), ASE, AllowOnePastEnd > 0); + CheckArrayAccess(ASE->getBase(), AllowOnePastEnd); + return; + } + case Stmt::MemberExprClass: { + expr = cast(expr)->getBase(); + CheckArrayAccess(expr, /*AllowOnePastEnd=*/0); + return; + } + case Stmt::OMPArraySectionExprClass: { + const OMPArraySectionExpr *ASE = cast(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(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(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(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 --- a/clang/test/Parser/cxx-ambig-decl-expr.cpp +++ b/clang/test/Parser/cxx-ambig-decl-expr.cpp @@ -24,7 +24,7 @@ // 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 --- a/clang/test/SemaCXX/array-bounds.cpp +++ b/clang/test/SemaCXX/array-bounds.cpp @@ -27,7 +27,7 @@ }; 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_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 @@ } 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 @@ arr[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(array[2]); // expected-warning {{array index 2 is past the end of the array (which contains 2 elements)}} + const int &i3 = reinterpret_cast(array[2]); // expected-warning {{array index 2 is past the end of the array (which contains 2 elements)}} + unsigned int &i4 = const_cast(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(&baseArr[2]); // FIXME: Should actually warn because dynamic_cast accesses the vptr + Derived &d2 = dynamic_cast(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)}} +}