Skip to content

Commit 4f28519

Browse files
authored
[syntax-errors] Star expression in index before Python 3.11 (#16544)
Summary -- This PR detects tuple unpacking expressions in index/subscript expressions before Python 3.11. Test Plan -- New inline tests
1 parent 2cd25ef commit 4f28519

File tree

8 files changed

+933
-0
lines changed

8 files changed

+933
-0
lines changed
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# parse_options: {"target-version": "3.10"}
2+
lst[*index] # simple index
3+
class Array(Generic[DType, *Shape]): ... # motivating example from the PEP
4+
lst[a, *b, c] # different positions
5+
lst[a, b, *c] # different positions
6+
lst[*a, *b] # multiple unpacks
7+
array[3:5, *idxs] # mixed with slices
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
array[*start:*end]
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# parse_options: {"target-version": "3.11"}
2+
lst[*index] # simple index
3+
class Array(Generic[DType, *Shape]): ... # motivating example from the PEP
4+
lst[a, *b, c] # different positions
5+
lst[a, b, *c] # different positions
6+
lst[*a, *b] # multiple unpacks
7+
array[3:5, *idxs] # mixed with slices

crates/ruff_python_parser/src/error.rs

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -617,6 +617,33 @@ pub enum UnsupportedSyntaxErrorKind {
617617
TypeAliasStatement,
618618
TypeParamDefault,
619619

620+
/// Represents the use of a [PEP 646] star expression in an index.
621+
///
622+
/// ## Examples
623+
///
624+
/// Before Python 3.11, star expressions were not allowed in index/subscript operations (within
625+
/// square brackets). This restriction was lifted in [PEP 646] to allow for star-unpacking of
626+
/// `typing.TypeVarTuple`s, also added in Python 3.11. As such, this is the primary motivating
627+
/// example from the PEP:
628+
///
629+
/// ```python
630+
/// from typing import TypeVar, TypeVarTuple
631+
///
632+
/// DType = TypeVar('DType')
633+
/// Shape = TypeVarTuple('Shape')
634+
///
635+
/// class Array(Generic[DType, *Shape]): ...
636+
/// ```
637+
///
638+
/// But it applies to simple indexing as well:
639+
///
640+
/// ```python
641+
/// vector[*x]
642+
/// array[a, *b]
643+
/// ```
644+
///
645+
/// [PEP 646]: https://peps.python.org/pep-0646/#change-1-star-expressions-in-indexes
646+
StarExpressionInIndex,
620647
/// Represents the use of tuple unpacking in a `for` statement iterator clause before Python
621648
/// 3.9.
622649
///
@@ -669,6 +696,9 @@ impl Display for UnsupportedSyntaxError {
669696
UnsupportedSyntaxErrorKind::TypeParamDefault => {
670697
"Cannot set default type for a type parameter"
671698
}
699+
UnsupportedSyntaxErrorKind::StarExpressionInIndex => {
700+
"Cannot use star expression in index"
701+
}
672702
UnsupportedSyntaxErrorKind::UnparenthesizedUnpackInFor => {
673703
"Cannot use iterable unpacking in `for` statements"
674704
}
@@ -717,6 +747,9 @@ impl UnsupportedSyntaxErrorKind {
717747
UnsupportedSyntaxErrorKind::TypeParameterList => Change::Added(PythonVersion::PY312),
718748
UnsupportedSyntaxErrorKind::TypeAliasStatement => Change::Added(PythonVersion::PY312),
719749
UnsupportedSyntaxErrorKind::TypeParamDefault => Change::Added(PythonVersion::PY313),
750+
UnsupportedSyntaxErrorKind::StarExpressionInIndex => {
751+
Change::Added(PythonVersion::PY311)
752+
}
720753
UnsupportedSyntaxErrorKind::UnparenthesizedUnpackInFor => {
721754
Change::Added(PythonVersion::PY39)
722755
}

crates/ruff_python_parser/src/parser/expression.rs

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -853,6 +853,35 @@ impl<'src> Parser<'src> {
853853

854854
self.expect(TokenKind::Rsqb);
855855

856+
// test_ok star_index_py311
857+
// # parse_options: {"target-version": "3.11"}
858+
// lst[*index] # simple index
859+
// class Array(Generic[DType, *Shape]): ... # motivating example from the PEP
860+
// lst[a, *b, c] # different positions
861+
// lst[a, b, *c] # different positions
862+
// lst[*a, *b] # multiple unpacks
863+
// array[3:5, *idxs] # mixed with slices
864+
865+
// test_err star_index_py310
866+
// # parse_options: {"target-version": "3.10"}
867+
// lst[*index] # simple index
868+
// class Array(Generic[DType, *Shape]): ... # motivating example from the PEP
869+
// lst[a, *b, c] # different positions
870+
// lst[a, b, *c] # different positions
871+
// lst[*a, *b] # multiple unpacks
872+
// array[3:5, *idxs] # mixed with slices
873+
874+
// test_err star_slices
875+
// array[*start:*end]
876+
if let Expr::Tuple(ast::ExprTuple { elts, .. }) = &slice {
877+
for elt in elts.iter().filter(|elt| elt.is_starred_expr()) {
878+
self.add_unsupported_syntax_error(
879+
UnsupportedSyntaxErrorKind::StarExpressionInIndex,
880+
elt.range(),
881+
);
882+
}
883+
};
884+
856885
ast::ExprSubscript {
857886
value: Box::new(value),
858887
slice: Box::new(slice),

0 commit comments

Comments
 (0)