Repository: python/mypy

Issue:
`type[TypeVar]` not checked properly with upper bound
**Bug Report**

When checking a function with argument of `type[SomeTypeVar]`, class attributes are considered missing.

**To Reproduce**

```python
from typing import ClassVar, Type, TypeVar, cast

class One:
    PATTERN: ClassVar[str] = r"^([a-z]{3})$"

class Two:
    PATTERN: ClassVar[str] = r"^([0-9]{3})$"

_T = TypeVar("_T", bound=One | Two)

def foo(result_type: Type[_T]) -> _T:
    result_type.PATTERN  # E: "Type[_T]" has no attribute "PATTERN"  [attr-defined]
    return cast(_T, result_type())

foo(One)
foo(Two)
```

This doesn't depend on `ClassVar` and works with `Final` or plain `str` as well.

Gist: https://mypy-play.net/?mypy=master&python=3.10&gist=2d769a9767898e71431e59df1c2f8488

**Expected Behavior**

No errors (let's ignore `cast` for now, it's another unrelated issue).

**Actual Behavior**
```
main.py:12: error: "Type[_T]" has no attribute "PATTERN"  [attr-defined]
Found 1 error in 1 file (checked 1 source file)
```

**Your Environment**

- Mypy version used: 1.0.0 and master
- Mypy command-line flags: not related
- Mypy configuration options from `mypy.ini` (and other config files): not related
- Python version used: 3.9-3.11, not related

Discovered in [this SO question](https://stackoverflow.com/questions/75507292/how-to-correctly-declare-use-a-typevar-when-a-function-needs-to-access-a-common)
`type[TypeVar]` not checked properly with upper bound
**Bug Report**

When checking a function with argument of `type[SomeTypeVar]`, class attributes are considered missing.

**To Reproduce**

```python
from typing import ClassVar, Type, TypeVar, cast

class One:
    PATTERN: ClassVar[str] = r"^([a-z]{3})$"

class Two:
    PATTERN: ClassVar[str] = r"^([0-9]{3})$"

_T = TypeVar("_T", bound=One | Two)

def foo(result_type: Type[_T]) -> _T:
    result_type.PATTERN  # E: "Type[_T]" has no attribute "PATTERN"  [attr-defined]
    return cast(_T, result_type())

foo(One)
foo(Two)
```

This doesn't depend on `ClassVar` and works with `Final` or plain `str` as well.

Gist: https://mypy-play.net/?mypy=master&python=3.10&gist=2d769a9767898e71431e59df1c2f8488

**Expected Behavior**

No errors (let's ignore `cast` for now, it's another unrelated issue).

**Actual Behavior**
```
main.py:12: error: "Type[_T]" has no attribute "PATTERN"  [attr-defined]
Found 1 error in 1 file (checked 1 source file)
```

**Your Environment**

- Mypy version used: 1.0.0 and master
- Mypy command-line flags: not related
- Mypy configuration options from `mypy.ini` (and other config files): not related
- Python version used: 3.9-3.11, not related

Discovered in [this SO question](https://stackoverflow.com/questions/75507292/how-to-correctly-declare-use-a-typevar-when-a-function-needs-to-access-a-common)


Discussion:

Resolving patch:
diff --git a/mypy/checkmember.py b/mypy/checkmember.py
--- a/mypy/checkmember.py
+++ b/mypy/checkmember.py
@@ -412,6 +412,13 @@ def analyze_type_type_member_access(
         upper_bound = get_proper_type(typ.item.upper_bound)
         if isinstance(upper_bound, Instance):
             item = upper_bound
+        elif isinstance(upper_bound, UnionType):
+            return _analyze_member_access(
+                name,
+                TypeType.make_normalized(upper_bound, line=typ.line, column=typ.column),
+                mx,
+                override_info,
+            )
         elif isinstance(upper_bound, TupleType):
             item = tuple_fallback(upper_bound)
         elif isinstance(upper_bound, AnyType):
diff --git a/mypy/messages.py b/mypy/messages.py
--- a/mypy/messages.py
+++ b/mypy/messages.py
@@ -516,6 +516,12 @@ def has_no_attr(
                         context,
                         code=codes.UNION_ATTR,
                     )
+            else:
+                self.fail(
+                    '{} has no attribute "{}"{}'.format(format_type(original_type), member, extra),
+                    context,
+                    code=codes.ATTR_DEFINED,
+                )
         return AnyType(TypeOfAny.from_error)
 
     def unsupported_operand_types(


Test patch:
diff --git a/test-data/unit/check-classes.test b/test-data/unit/check-classes.test
--- a/test-data/unit/check-classes.test
+++ b/test-data/unit/check-classes.test
@@ -3464,9 +3464,9 @@ class ProUser(User): pass
 class BasicUser(User): pass
 U = TypeVar('U', bound=Union[ProUser, BasicUser])
 def process(cls: Type[U]):
-    cls.foo()  # E: "Type[U]" has no attribute "foo"
+    cls.foo()
     obj = cls()
-    cls.bar(obj)  # E: "Type[U]" has no attribute "bar"
+    cls.bar(obj)
     cls.mro()  # Defined in class type
     cls.error  # E: "Type[U]" has no attribute "error"
 [builtins fixtures/classmethod.pyi]