diff --git a/mypy/checker.py b/mypy/checker.py index a0a8d00337b5e..e0bfd08260ef3 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -3299,14 +3299,14 @@ def check_assignment_to_multiple_lvalues( rvalues.extend([TempNode(typ) for typ in typs.items]) elif self.type_is_iterable(typs) and isinstance(typs, Instance): if iterable_type is not None and iterable_type != self.iterable_item_type( - typs + typs, rvalue ): self.fail(message_registry.CONTIGUOUS_ITERABLE_EXPECTED, context) else: if last_idx is None or last_idx + 1 == idx_rval: rvalues.append(rval) last_idx = idx_rval - iterable_type = self.iterable_item_type(typs) + iterable_type = self.iterable_item_type(typs, rvalue) else: self.fail(message_registry.CONTIGUOUS_ITERABLE_EXPECTED, context) else: @@ -3635,7 +3635,7 @@ def check_multi_assignment_from_iterable( if self.type_is_iterable(rvalue_type) and isinstance( rvalue_type, (Instance, CallableType, TypeType, Overloaded) ): - item_type = self.iterable_item_type(rvalue_type) + item_type = self.iterable_item_type(rvalue_type, context) for lv in lvalues: if isinstance(lv, StarExpr): items_type = self.named_generic_type("builtins.list", [item_type]) @@ -6392,7 +6392,9 @@ def note( return self.msg.note(msg, context, offset=offset, code=code) - def iterable_item_type(self, it: Instance | CallableType | TypeType | Overloaded) -> Type: + def iterable_item_type( + self, it: Instance | CallableType | TypeType | Overloaded, context: Context + ) -> Type: if isinstance(it, Instance): iterable = map_instance_to_supertype(it, self.lookup_typeinfo("typing.Iterable")) item_type = iterable.args[0] @@ -6401,7 +6403,7 @@ def iterable_item_type(self, it: Instance | CallableType | TypeType | Overloaded # in case there is no explicit base class. return item_type # Try also structural typing. - return self.analyze_iterable_item_type_without_expression(it, it)[1] + return self.analyze_iterable_item_type_without_expression(it, context)[1] def function_type(self, func: FuncBase) -> FunctionLike: return function_type(func, self.named_type("builtins.function")) diff --git a/mypy/checkpattern.py b/mypy/checkpattern.py index 603b392eee293..e60ed8a11711f 100644 --- a/mypy/checkpattern.py +++ b/mypy/checkpattern.py @@ -15,7 +15,7 @@ from mypy.maptype import map_instance_to_supertype from mypy.meet import narrow_declared_type from mypy.messages import MessageBuilder -from mypy.nodes import ARG_POS, Expression, NameExpr, TypeAlias, TypeInfo, Var +from mypy.nodes import ARG_POS, Context, Expression, NameExpr, TypeAlias, TypeInfo, Var from mypy.patterns import ( AsPattern, ClassPattern, @@ -242,7 +242,7 @@ def visit_sequence_pattern(self, o: SequencePattern) -> PatternType: elif size_diff > 0 and star_position is None: return self.early_non_match() else: - inner_type = self.get_sequence_type(current_type) + inner_type = self.get_sequence_type(current_type, o) if inner_type is None: inner_type = self.chk.named_type("builtins.object") inner_types = [inner_type] * len(o.patterns) @@ -309,12 +309,12 @@ def visit_sequence_pattern(self, o: SequencePattern) -> PatternType: new_type = current_type return PatternType(new_type, rest_type, captures) - def get_sequence_type(self, t: Type) -> Type | None: + def get_sequence_type(self, t: Type, context: Context) -> Type | None: t = get_proper_type(t) if isinstance(t, AnyType): return AnyType(TypeOfAny.from_another_any, t) if isinstance(t, UnionType): - items = [self.get_sequence_type(item) for item in t.items] + items = [self.get_sequence_type(item, context) for item in t.items] not_none_items = [item for item in items if item is not None] if len(not_none_items) > 0: return make_simplified_union(not_none_items) @@ -324,7 +324,7 @@ def get_sequence_type(self, t: Type) -> Type | None: if self.chk.type_is_iterable(t) and isinstance(t, (Instance, TupleType)): if isinstance(t, TupleType): t = tuple_fallback(t) - return self.chk.iterable_item_type(t) + return self.chk.iterable_item_type(t, context) else: return None diff --git a/test-data/unit/check-classes.test b/test-data/unit/check-classes.test index da95674ed08f3..ec09c517c92e7 100644 --- a/test-data/unit/check-classes.test +++ b/test-data/unit/check-classes.test @@ -7797,3 +7797,19 @@ class Element(Generic[_T]): class Bar(Foo): ... e: Element[Bar] reveal_type(e.elements) # N: Revealed type is "typing.Sequence[__main__.Element[__main__.Bar]]" + +[case testIterableUnpackingWithGetAttr] +from typing import Union, Tuple + +class C: + def __getattr__(self, name): + pass + +class D: + def f(self) -> C: + return C() + + def g(self) -> None: + # TODO: This is a false positive + a, b = self.f() # E: "C" has no attribute "__iter__" (not iterable) +[builtins fixtures/tuple.pyi]