From 11504a47be6d1736d241fe6ae8099e4738cfcc1b Mon Sep 17 00:00:00 2001 From: Christoph Tyralla Date: Sat, 18 Sep 2021 15:55:36 +0200 Subject: [PATCH 1/3] Enable reporting attribute access errors for type variables bound to unions (fixes #10948). --- mypy/messages.py | 4 +++- test-data/unit/check-generic-subtyping.test | 21 +++++++++++++++++++++ 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/mypy/messages.py b/mypy/messages.py index 820e15eb0581a..82400b2d448bb 100644 --- a/mypy/messages.py +++ b/mypy/messages.py @@ -335,7 +335,9 @@ def has_no_attr(self, format_type(original_type), member, extra), context, code=codes.ATTR_DEFINED) - elif isinstance(original_type, UnionType): + elif isinstance(original_type, UnionType) or ( + isinstance(original_type, TypeVarType) and + isinstance(original_type.upper_bound, UnionType)): # The checker passes "object" in lieu of "None" for attribute # checks, so we manually convert it back. typ_format, orig_type_format = format_type_distinctly(typ, original_type) diff --git a/test-data/unit/check-generic-subtyping.test b/test-data/unit/check-generic-subtyping.test index 5a7c0b8624f73..91582b227faef 100644 --- a/test-data/unit/check-generic-subtyping.test +++ b/test-data/unit/check-generic-subtyping.test @@ -817,4 +817,25 @@ class Y(Generic[T]): def f(self) -> T: return U() # E: Incompatible return value type (got "U", expected "T") + +[case testTypeVarBoundToUnionAttributeAccess] +from typing import Union, TypeVar + +class U: pass +class W: pass + +T = TypeVar("T", bound=Union[U, W]) + +def f(x: T) -> None: + x.a + x.b = 1.0 + del x.c + [out] +main:9: error: Item "U" of "T" has no attribute "a" +main:9: error: Item "W" of "T" has no attribute "a" +main:10: error: Item "U" of "T" has no attribute "b" +main:10: error: Item "W" of "T" has no attribute "b" +main:11: error: Item "U" of "T" has no attribute "c" +main:11: error: Item "W" of "T" has no attribute "c" + From 96178dfbf8db60db361ee781bb42c928c2bd874a Mon Sep 17 00:00:00 2001 From: Christoph Tyralla Date: Sat, 18 Sep 2021 21:46:00 +0200 Subject: [PATCH 2/3] Add true negatives to the test case. --- test-data/unit/check-generic-subtyping.test | 29 ++++++++++++--------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/test-data/unit/check-generic-subtyping.test b/test-data/unit/check-generic-subtyping.test index 91582b227faef..0315ed9381580 100644 --- a/test-data/unit/check-generic-subtyping.test +++ b/test-data/unit/check-generic-subtyping.test @@ -821,21 +821,24 @@ class Y(Generic[T]): [case testTypeVarBoundToUnionAttributeAccess] from typing import Union, TypeVar -class U: pass -class W: pass +class U: + a: float +class V: + b: float +class W: + c: float -T = TypeVar("T", bound=Union[U, W]) +T = TypeVar("T", bound=Union[U, V, W]) def f(x: T) -> None: - x.a - x.b = 1.0 - del x.c + x.a # E + x.b = 1.0 # E + del x.c # E [out] -main:9: error: Item "U" of "T" has no attribute "a" -main:9: error: Item "W" of "T" has no attribute "a" -main:10: error: Item "U" of "T" has no attribute "b" -main:10: error: Item "W" of "T" has no attribute "b" -main:11: error: Item "U" of "T" has no attribute "c" -main:11: error: Item "W" of "T" has no attribute "c" - +main:13: error: Item "V" of "T" has no attribute "a" +main:13: error: Item "W" of "T" has no attribute "a" +main:14: error: Item "U" of "T" has no attribute "b" +main:14: error: Item "W" of "T" has no attribute "b" +main:15: error: Item "U" of "T" has no attribute "c" +main:15: error: Item "V" of "T" has no attribute "c" From 694e5db0919bec7ee4d6593ecf2a8c16d257a9c9 Mon Sep 17 00:00:00 2001 From: Christoph Tyralla Date: Sat, 18 Sep 2021 21:46:44 +0200 Subject: [PATCH 3/3] Fix the code and improve the error message. --- mypy/messages.py | 14 +++++++++++--- test-data/unit/check-generic-subtyping.test | 12 ++++++------ 2 files changed, 17 insertions(+), 9 deletions(-) diff --git a/mypy/messages.py b/mypy/messages.py index 82400b2d448bb..92a5487162fc0 100644 --- a/mypy/messages.py +++ b/mypy/messages.py @@ -335,9 +335,7 @@ def has_no_attr(self, format_type(original_type), member, extra), context, code=codes.ATTR_DEFINED) - elif isinstance(original_type, UnionType) or ( - isinstance(original_type, TypeVarType) and - isinstance(original_type.upper_bound, UnionType)): + elif isinstance(original_type, UnionType): # The checker passes "object" in lieu of "None" for attribute # checks, so we manually convert it back. typ_format, orig_type_format = format_type_distinctly(typ, original_type) @@ -347,6 +345,16 @@ def has_no_attr(self, self.fail('Item {} of {} has no attribute "{}"{}'.format( typ_format, orig_type_format, member, extra), context, code=codes.UNION_ATTR) + elif isinstance(original_type, TypeVarType): + bound = get_proper_type(original_type.upper_bound) + if isinstance(bound, UnionType): + typ_fmt, bound_fmt = format_type_distinctly(typ, bound) + original_type_fmt = format_type(original_type) + self.fail( + 'Item {} of the upper bound {} of type variable {} has no ' + 'attribute "{}"{}'.format( + typ_fmt, bound_fmt, original_type_fmt, member, extra), + context, code=codes.UNION_ATTR) return AnyType(TypeOfAny.from_error) def unsupported_operand_types(self, diff --git a/test-data/unit/check-generic-subtyping.test b/test-data/unit/check-generic-subtyping.test index 0315ed9381580..d1420a43f9e48 100644 --- a/test-data/unit/check-generic-subtyping.test +++ b/test-data/unit/check-generic-subtyping.test @@ -836,9 +836,9 @@ def f(x: T) -> None: del x.c # E [out] -main:13: error: Item "V" of "T" has no attribute "a" -main:13: error: Item "W" of "T" has no attribute "a" -main:14: error: Item "U" of "T" has no attribute "b" -main:14: error: Item "W" of "T" has no attribute "b" -main:15: error: Item "U" of "T" has no attribute "c" -main:15: error: Item "V" of "T" has no attribute "c" +main:13: error: Item "V" of the upper bound "Union[U, V, W]" of type variable "T" has no attribute "a" +main:13: error: Item "W" of the upper bound "Union[U, V, W]" of type variable "T" has no attribute "a" +main:14: error: Item "U" of the upper bound "Union[U, V, W]" of type variable "T" has no attribute "b" +main:14: error: Item "W" of the upper bound "Union[U, V, W]" of type variable "T" has no attribute "b" +main:15: error: Item "U" of the upper bound "Union[U, V, W]" of type variable "T" has no attribute "c" +main:15: error: Item "V" of the upper bound "Union[U, V, W]" of type variable "T" has no attribute "c"