diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py index cc563cf562af..235a9f44c2c5 100644 --- a/mypy/checkexpr.py +++ b/mypy/checkexpr.py @@ -986,6 +986,9 @@ def visit_index_expr_helper(self, e: IndexExpr) -> Type: # Special case for tuples. They support indexing only by integer # literals. (Except in weak type checking mode.) index = e.index + if isinstance(index, SliceExpr): + return self.visit_tuple_slice_helper(left_type, index) + ok = False if isinstance(index, IntExpr): n = index.value @@ -1010,6 +1013,32 @@ def visit_index_expr_helper(self, e: IndexExpr) -> Type: e.method_type = method_type return result + def visit_tuple_slice_helper(self, left_type: TupleType, slic: SliceExpr): + begin = 0 + end = len(left_type.items) + stride = 1 + if slic.begin_index: + if isinstance(slic.begin_index, IntExpr): + begin = slic.begin_index.value + else: + self.chk.fail(messages.TUPLE_SLICE_MUST_BE_AN_INT_LITERAL, slic.begin_index) + return AnyType() + if slic.end_index: + if isinstance(slic.end_index, IntExpr): + end = slic.end_index.value + else: + self.chk.fail(messages.TUPLE_SLICE_MUST_BE_AN_INT_LITERAL, slic.end_index) + return AnyType() + if slic.stride: + if isinstance(slic.stride, IntExpr): + stride = slic.stride.value + else: + self.chk.fail(messages.TUPLE_SLICE_MUST_BE_AN_INT_LITERAL, slic.stride) + return AnyType() + + return TupleType(left_type.items[begin:end:stride], left_type.fallback, + left_type.line, left_type.implicit) + def visit_cast_expr(self, expr: CastExpr) -> Type: """Type check a cast expression.""" source_type = self.accept(expr.expr, context=AnyType()) diff --git a/mypy/messages.py b/mypy/messages.py index 40e040393cf8..93b537bca761 100644 --- a/mypy/messages.py +++ b/mypy/messages.py @@ -41,7 +41,8 @@ INIT_MUST_HAVE_NONE_RETURN_TYPE = 'The return type of "__init__" must be None' GETTER_TYPE_INCOMPATIBLE_WITH_SETTER = \ 'Type of getter incompatible with setter' -TUPLE_INDEX_MUST_BE_AN_INT_LITERAL = 'Tuple index must an integer literal' +TUPLE_INDEX_MUST_BE_AN_INT_LITERAL = 'Tuple index must be an integer literal' +TUPLE_SLICE_MUST_BE_AN_INT_LITERAL = 'Tuple slice must be an integer literal' TUPLE_INDEX_OUT_OF_RANGE = 'Tuple index out of range' TYPE_CONSTANT_EXPECTED = 'Type "Constant" or initializer expected' INCOMPATIBLE_PAIR_ITEM_TYPE = 'Incompatible Pair item type' diff --git a/mypy/test/data/check-tuples.test b/mypy/test/data/check-tuples.test index 07586cfb4916..d8f580e330da 100644 --- a/mypy/test/data/check-tuples.test +++ b/mypy/test/data/check-tuples.test @@ -152,7 +152,10 @@ def f() -> None: pass from typing import Tuple t1 = None # type: Tuple[A, B] t2 = None # type: Tuple[A] +t3 = None # type: Tuple[A, B, C, D, E] a, b = None, None # type: (A, B) +x = None # type: Tuple[A, B, C] +y = None # type: Tuple[A, C, E] n = 0 a = t1[1] # E: Incompatible types in assignment (expression has type "B", variable has type "A") @@ -160,15 +163,21 @@ b = t1[0] # E: Incompatible types in assignment (expression has type "A", variab t1[2] # E: Tuple index out of range t1[3] # E: Tuple index out of range t2[1] # E: Tuple index out of range -t1[n] # E: Tuple index must an integer literal +t1[n] # E: Tuple index must be an integer literal +t3[n:] # E: Tuple slice must be an integer literal b = t1[(0)] # E: Incompatible types in assignment (expression has type "A", variable has type "B") a = t1[0] b = t1[1] a = t1[(0)] +x = t3[0:3] # type (A, B, C) +y = t3[0:5:2] # type (A, C, E) class A: pass class B: pass +class C: pass +class D: pass +class E: pass [builtins fixtures/tuple.py] [case testIndexingTuplesWithNegativeIntegers]