Repository: python/mypy

Issue:
Weird error messages when mixing classes with instances
```python
class A: pass
x: Tuple[A]
x = A,  # E: Incompatible types in assignment (expression has type "Tuple[A]", variable has type "Tuple[A]")
y: Tuple[Type[A]]
y = A,  # OK, expected
def f(x: Tuple[A]) -> None: return None
f((A,))  # E: Argument 1 to "f" has incompatible type "Tuple[A]"; expected "Tuple[A]"
reveal_type((A,))  # E: Revealed type is 'Tuple[def () -> x.A]'
z: A
z = A  # E: Incompatible types in assignment (expression has type "A" (type object), variable has type "A")
z = A,  # E: Incompatible types in assignment (expression has type "Tuple[A]", variable has type "A")
```

Looks like mypy formats class type in three ways: `T` (which is wrong), `T (type object)`, and `def () -> T`. In some cases these errors are unclear.


Discussion:
Yes, I remember I noticed this around a year ago, last two formats are OK I think, but the first one is definitely misleading.
So the correct error message should be:
> E: Incompatible types in assignment (expression has type "Tuple[**Type[A]**]", variable has type "Tuple[A]")
Example from #3372:
```python
class A: pass
class B(A): pass
def f(a: A): pass

f(B)  # E: Argument 1 to "f" has incompatible type "B"; expected "A"
a: A = B  # E: Incompatible types in assignment (expression has type "B", variable has type "A")
```

I will submit a PR is there is agreement on the correct error message is.

I am +1 on `Type[A]` as @ambv suggested.
I think the issue becomes even more confusing when dealing with metaclasses.
I am also +1 on ``Type[A]``.

Resolving patch:
diff --git a/mypy/messages.py b/mypy/messages.py
--- a/mypy/messages.py
+++ b/mypy/messages.py
@@ -8,6 +8,7 @@
 
 from typing import cast, List, Dict, Any, Sequence, Iterable, Tuple
 
+from mypy.erasetype import erase_type
 from mypy.errors import Errors
 from mypy.types import (
     Type, CallableType, Instance, TypeVarType, TupleType, TypedDictType,
@@ -190,13 +191,7 @@ def format(self, typ: Type, verbosity: int = 0) -> str:
             if func.is_type_obj():
                 # The type of a type object type can be derived from the
                 # return type (this always works).
-                itype = cast(Instance, func.items()[0].ret_type)
-                result = self.format(itype)
-                if verbosity >= 1:
-                    # In some contexts we want to be explicit about the distinction
-                    # between type X and the type of type object X.
-                    result += ' (type object)'
-                return result
+                return self.format(TypeType(erase_type(func.items()[0].ret_type)), verbosity)
             elif isinstance(func, CallableType):
                 return_type = strip_quotes(self.format(func.ret_type))
                 if func.is_ellipsis_args:


Test patch:
diff --git a/test-data/unit/check-class-namedtuple.test b/test-data/unit/check-class-namedtuple.test
--- a/test-data/unit/check-class-namedtuple.test
+++ b/test-data/unit/check-class-namedtuple.test
@@ -221,7 +221,7 @@ class MyNamedTuple(NamedTuple):
     a: int
     b: str
 
-MyNamedTuple.x # E: "MyNamedTuple" has no attribute "x"
+MyNamedTuple.x # E: Type[MyNamedTuple] has no attribute "x"
 
 [case testNewNamedTupleEmptyItems]
 # flags: --python-version 3.6
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
@@ -514,7 +514,7 @@ b = A.x # type: B # E: Incompatible types in assignment (expression has type "A"
 [case testAccessingUndefinedAttributeViaClass]
 import typing
 class A: pass
-A.x # E: "A" has no attribute "x"
+A.x # E: Type[A] has no attribute "x"
 
 [case testAccessingUndefinedAttributeViaClassWithOverloadedInit]
 from foo import *
@@ -525,7 +525,7 @@ class A:
     def __init__(self): pass
     @overload
     def __init__(self, x): pass
-A.x # E: "A" has no attribute "x"
+A.x # E: Type[A] has no attribute "x"
 
 [case testAccessMethodOfClassWithOverloadedInit]
 from foo import *
@@ -812,7 +812,7 @@ class C:
     cls(1)      # E: Too many arguments for "C"
     cls.bar()
     cls.bar(1)  # E: Too many arguments for "bar" of "C"
-    cls.bozo()  # E: "C" has no attribute "bozo"
+    cls.bozo()  # E: Type[C] has no attribute "bozo"
 [builtins fixtures/classmethod.pyi]
 [out]
 
@@ -823,7 +823,7 @@ class C:
   def foo(cls) -> None: pass
 C.foo()
 C.foo(1)  # E: Too many arguments for "foo" of "C"
-C.bozo()  # E: "C" has no attribute "bozo"
+C.bozo()  # E: Type[C] has no attribute "bozo"
 [builtins fixtures/classmethod.pyi]
 
 [case testClassMethodCalledOnInstance]
@@ -833,7 +833,7 @@ class C:
   def foo(cls) -> None: pass
 C().foo()
 C().foo(1)  # E: Too many arguments for "foo" of "C"
-C.bozo()    # E: "C" has no attribute "bozo"
+C.bozo()    # E: Type[C] has no attribute "bozo"
 [builtins fixtures/classmethod.pyi]
 
 [case testClassMethodMayCallAbstractMethod]
@@ -1722,7 +1722,7 @@ class C:
 def f(x: type) -> None: pass
 def g(x: int) -> None: pass
 f(C)
-g(C) # E: Argument 1 to "g" has incompatible type "C"; expected "int"
+g(C) # E: Argument 1 to "g" has incompatible type Type[C]; expected "int"
 [builtins fixtures/__new__.pyi]
 
 [case testClassWith__new__AndCompatibilityWithType2]
@@ -1733,7 +1733,7 @@ class C:
 def f(x: type) -> None: pass
 def g(x: int) -> None: pass
 f(C)
-g(C) # E: Argument 1 to "g" has incompatible type "C"; expected "int"
+g(C) # E: Argument 1 to "g" has incompatible type Type[C]; expected "int"
 [builtins fixtures/__new__.pyi]
 
 [case testGenericClassWith__new__]
@@ -1813,7 +1813,7 @@ class B:
 [case testClassVsInstanceDisambiguation]
 class A: pass
 def f(x: A) -> None: pass
-f(A) # E: Argument 1 to "f" has incompatible type "A" (type object); expected "A"
+f(A) # E: Argument 1 to "f" has incompatible type Type[A]; expected "A"
 [out]
 
 -- TODO
@@ -2621,7 +2621,7 @@ int.__eq__(3, 4)
 [builtins fixtures/args.pyi]
 [out]
 main:33: error: Too few arguments for "__eq__" of "int"
-main:33: error: Unsupported operand types for == ("int" and "int")
+main:33: error: Unsupported operand types for == ("int" and Type[int])
 
 [case testMroSetAfterError]
 class C(str, str):
@@ -2986,7 +2986,7 @@ class M:
 
 class A(metaclass=M): pass  # E: Metaclasses not inheriting from 'type' are not supported
 
-A.x  # E: "A" has no attribute "x"
+A.x  # E: Type[A] has no attribute "x"
 
 [case testMetaclassTypeReveal]
 from typing import Type
@@ -3085,7 +3085,7 @@ from missing import M
 class A(metaclass=M):
     y = 0
 reveal_type(A.y) # E: Revealed type is 'builtins.int'
-A.x # E: "A" has no attribute "x"
+A.x # E: Type[A] has no attribute "x"
 
 [case testAnyMetaclass]
 from typing import Any
@@ -3093,7 +3093,7 @@ M = None  # type: Any
 class A(metaclass=M):
     y = 0
 reveal_type(A.y) # E: Revealed type is 'builtins.int'
-A.x # E: "A" has no attribute "x"
+A.x # E: Type[A] has no attribute "x"
 
 [case testInvalidVariableAsMetaclass]
 from typing import Any
@@ -3104,7 +3104,7 @@ class A(metaclass=M): # E: Invalid metaclass 'M'
 class B(metaclass=MM): # E: Invalid metaclass 'MM'
     y = 0
 reveal_type(A.y) # E: Revealed type is 'builtins.int'
-A.x # E: "A" has no attribute "x"
+A.x # E: Type[A] has no attribute "x"
 
 [case testAnyAsBaseOfMetaclass]
 from typing import Any, Type
diff --git a/test-data/unit/check-dynamic-typing.test b/test-data/unit/check-dynamic-typing.test
--- a/test-data/unit/check-dynamic-typing.test
+++ b/test-data/unit/check-dynamic-typing.test
@@ -485,7 +485,7 @@ class A:
   def __init__(self, a, b): pass
 [out]
 main:6: error: Too few arguments for "A"
-main:7: error: Incompatible types in assignment (expression has type "A", variable has type Callable[[A], A])
+main:7: error: Incompatible types in assignment (expression has type Type[A], variable has type Callable[[A], A])
 
 [case testUsingImplicitTypeObjectWithIs]
 
diff --git a/test-data/unit/check-enum.test b/test-data/unit/check-enum.test
--- a/test-data/unit/check-enum.test
+++ b/test-data/unit/check-enum.test
@@ -325,7 +325,7 @@ main:17: error: Enum() with dict literal requires string literals
 main:18: error: Unexpected arguments to Enum()
 main:19: error: Unexpected arguments to Enum()
 main:20: error: Unexpected arguments to Enum()
-main:22: error: "W" has no attribute "c"
+main:22: error: Type[W] has no attribute "c"
 
 [case testFunctionalEnumFlag]
 from enum import Flag, IntFlag
diff --git a/test-data/unit/check-functions.test b/test-data/unit/check-functions.test
--- a/test-data/unit/check-functions.test
+++ b/test-data/unit/check-functions.test
@@ -254,7 +254,7 @@ class A: pass
 t = None # type: type
 a = None # type: A
 
-a = A # E: Incompatible types in assignment (expression has type "A" (type object), variable has type "A")
+a = A # E: Incompatible types in assignment (expression has type Type[A], variable has type "A")
 t = f # E: Incompatible types in assignment (expression has type Callable[[], None], variable has type "type")
 t = A
 
@@ -1479,7 +1479,7 @@ L = Callable[[Arg(name='x', type=int)], int] # ok
 # I have commented out the following test because I don't know how to expect the "defined here" note part of the error.
 # M = Callable[[Arg(gnome='x', type=int)], int]   E: Invalid type alias   E: Unexpected keyword argument "gnome" for "Arg"
 N = Callable[[Arg(name=None, type=int)], int] # ok
-O = Callable[[List[Arg(int)]], int]  # E: Invalid type alias  # E: Value of type "int" is not indexable  # E: Type expected within [...] # E: The type List[T] is not generic and not indexable
+O = Callable[[List[Arg(int)]], int]  # E: Invalid type alias  # E: Value of type "int" is not indexable  # E: Type expected within [...] # E: The type Type[List[Any]] is not generic and not indexable
 P = Callable[[mypy_extensions.VarArg(int)], int] # ok
 Q = Callable[[Arg(int, type=int)], int]  # E: Invalid type alias  # E: Value of type "int" is not indexable  # E: "Arg" gets multiple values for keyword argument "type"
 R = Callable[[Arg(int, 'x', name='y')], int]  # E: Invalid type alias  # E: Value of type "int" is not indexable  # E: "Arg" gets multiple values for keyword argument "name"
diff --git a/test-data/unit/check-generics.test b/test-data/unit/check-generics.test
--- a/test-data/unit/check-generics.test
+++ b/test-data/unit/check-generics.test
@@ -449,7 +449,7 @@ A[int, str, int]() # E: Type application has too many types (2 expected)
 a = None # type: A
 class A: pass
 a[A]()  # E: Value of type "A" is not indexable
-A[A]()  # E: The type "A" is not generic and not indexable
+A[A]()  # E: The type Type[A] is not generic and not indexable
 [out]
 
 [case testTypeApplicationArgTypes]
@@ -505,7 +505,7 @@ Alias[int]("a")  # E: Argument 1 to "Node" has incompatible type "str"; expected
 [out]
 
 [case testTypeApplicationCrash]
-type[int] # this was crashing, see #2302 (comment)  # E: The type "type" is not generic and not indexable
+type[int] # this was crashing, see #2302 (comment)  # E: The type Type[type] is not generic and not indexable
 [out]
 
 
@@ -1505,7 +1505,7 @@ T = TypeVar('T')
 class C(Generic[T]):
     def __init__(self) -> None: pass
 x = C # type: Callable[[], C[int]]
-y = C # type: Callable[[], int] # E: Incompatible types in assignment (expression has type C[T], variable has type Callable[[], int])
+y = C # type: Callable[[], int] # E: Incompatible types in assignment (expression has type Type[C[Any]], variable has type Callable[[], int])
 
 
 -- Special cases
diff --git a/test-data/unit/check-modules.test b/test-data/unit/check-modules.test
--- a/test-data/unit/check-modules.test
+++ b/test-data/unit/check-modules.test
@@ -915,7 +915,7 @@ class z: pass
 [out]
 main:2: error: Incompatible import of "x" (imported name has type "str", local name has type "int")
 main:2: error: Incompatible import of "y" (imported name has type Callable[[], str], local name has type Callable[[], int])
-main:2: error: Incompatible import of "z" (imported name has type "z" (type object), local name has type "z" (type object))
+main:2: error: Incompatible import of "z" (imported name has type Type[b.z], local name has type Type[a.z])
 
 -- Misc
 
diff --git a/test-data/unit/check-namedtuple.test b/test-data/unit/check-namedtuple.test
--- a/test-data/unit/check-namedtuple.test
+++ b/test-data/unit/check-namedtuple.test
@@ -221,7 +221,7 @@ a = (1,)  # E: Incompatible types in assignment (expression has type "Tuple[int]
 [case testNamedTupleMissingClassAttribute]
 import collections
 MyNamedTuple = collections.namedtuple('MyNamedTuple', ['spam', 'eggs'])
-MyNamedTuple.x # E: "MyNamedTuple" has no attribute "x"
+MyNamedTuple.x # E: Type[MyNamedTuple] has no attribute "x"
 
 
 [case testNamedTupleEmptyItems]
@@ -420,7 +420,7 @@ class C:
         A = NamedTuple('A', [('x', int)])
     def g(self):
         A = NamedTuple('A', [('y', int)])
-C.A  # E: "C" has no attribute "A"
+C.A  # E: Type[C] has no attribute "A"
 
 [case testNamedTupleInFunction]
 from typing import NamedTuple
diff --git a/test-data/unit/check-overloading.test b/test-data/unit/check-overloading.test
--- a/test-data/unit/check-overloading.test
+++ b/test-data/unit/check-overloading.test
@@ -447,7 +447,7 @@ from foo import *
 from typing import overload
 t, a = None, None # type: (type, A)
 
-a = A # E: Incompatible types in assignment (expression has type "A" (type object), variable has type "A")
+a = A # E: Incompatible types in assignment (expression has type Type[A], variable has type "A")
 t = A
 
 class A:
diff --git a/test-data/unit/check-python2.test b/test-data/unit/check-python2.test
--- a/test-data/unit/check-python2.test
+++ b/test-data/unit/check-python2.test
@@ -291,7 +291,7 @@ class A(object):
     __metaclass__ = M
     y = 0
 reveal_type(A.y) # E: Revealed type is 'builtins.int'
-A.x # E: "A" has no attribute "x"
+A.x # E: Type[A] has no attribute "x"
 
 [case testAnyAsBaseOfMetaclass]
 from typing import Any, Type
diff --git a/test-data/unit/check-typeddict.test b/test-data/unit/check-typeddict.test
--- a/test-data/unit/check-typeddict.test
+++ b/test-data/unit/check-typeddict.test
@@ -639,7 +639,7 @@ class C:
         A = TypedDict('A', {'x': int})
     def g(self):
         A = TypedDict('A', {'y': int})
-C.A  # E: "C" has no attribute "A"
+C.A  # E: Type[C] has no attribute "A"
 [builtins fixtures/dict.pyi]
 
 [case testTypedDictInFunction]
diff --git a/test-data/unit/pythoneval.test b/test-data/unit/pythoneval.test
--- a/test-data/unit/pythoneval.test
+++ b/test-data/unit/pythoneval.test
@@ -1096,7 +1096,7 @@ class MyDDict(t.DefaultDict[int,T], t.Generic[T]):
 MyDDict(dict)['0']
 MyDDict(dict)[0]
 [out]
-_program.py:6: error: Argument 1 to "defaultdict" has incompatible type List[_T]; expected Callable[[], str]
+_program.py:6: error: Argument 1 to "defaultdict" has incompatible type Type[List[Any]]; expected Callable[[], str]
 _program.py:9: error: Invalid index type "str" for defaultdict[int, str]; expected type "int"
 _program.py:9: error: Incompatible types in assignment (expression has type "int", target has type "str")
 _program.py:19: error: Dict entry 0 has incompatible type "str": List[<nothing>]