Repository: python/mypy

Issue:
Suggested improvement for wording of an error
I recently had trouble understanding an error output along the lines of:

```
Type argument 1 of "<function name>" has incompatible value "<a type that doesn't fit the TypeVar>"
```

Would it be possible to output something more along the lines of:

```
TypeVar "T" cannot be a "<wrong type>" (from first argument to "<function name>")
```


Discussion:
I think that's a great suggestion!
`TypeVar "<typevar name>" cannot be a "<wrong type>" (from argument 1 to "<function name>")`

@KitB are you interested in taking a stab at it yourself? If not, I can work on it.
I don't really have the time, go ahead.

Resolving patch:
diff --git a/mypy/applytype.py b/mypy/applytype.py
--- a/mypy/applytype.py
+++ b/mypy/applytype.py
@@ -38,12 +38,11 @@ def apply_generic_arguments(callable: CallableType, types: List[Type],
                     types[i] = value
                     break
             else:
-                msg.incompatible_typevar_value(callable, i + 1, type, context)
-
+                msg.incompatible_typevar_value(callable, type, callable.variables[i].name, context)
         upper_bound = callable.variables[i].upper_bound
         if (type and not isinstance(type, PartialType) and
                 not mypy.subtypes.is_subtype(type, upper_bound)):
-            msg.incompatible_typevar_value(callable, i + 1, type, context)
+            msg.incompatible_typevar_value(callable, type, callable.variables[i].name, context)
 
     # Create a map from type variable id to target type.
     id_to_type = {}  # type: Dict[TypeVarId, Type]
diff --git a/mypy/messages.py b/mypy/messages.py
--- a/mypy/messages.py
+++ b/mypy/messages.py
@@ -92,6 +92,7 @@
 CANNOT_ISINSTANCE_NEWTYPE = 'Cannot use isinstance() with a NewType type'
 BARE_GENERIC = 'Missing type parameters for generic type'
 IMPLICIT_GENERIC_ANY_BUILTIN = 'Implicit generic "Any". Use \'{}\' and specify generic parameters'
+INCOMPATIBLE_TYPEVAR_VALUE = 'Value of type variable "{}" of {} cannot be {}'
 
 ARG_CONSTRUCTOR_NAMES = {
     ARG_POS: "Arg",
@@ -859,10 +860,13 @@ def read_only_property(self, name: str, type: TypeInfo,
         self.fail('Property "{}" defined in "{}" is read-only'.format(
             name, type.name()), context)
 
-    def incompatible_typevar_value(self, callee: CallableType, index: int,
-                                   type: Type, context: Context) -> None:
-        self.fail('Type argument {} of {} has incompatible value {}'.format(
-            index, callable_name(callee), self.format(type)), context)
+    def incompatible_typevar_value(self,
+                                   callee: CallableType,
+                                   typ: Type,
+                                   typevar_name: str,
+                                   context: Context) -> None:
+        self.fail(INCOMPATIBLE_TYPEVAR_VALUE.format(typevar_name, callable_name(callee),
+                                                    self.format(typ)), context)
 
     def overloaded_signatures_overlap(self, index1: int, index2: int,
                                       context: Context) -> None:
diff --git a/mypy/typeanal.py b/mypy/typeanal.py
--- a/mypy/typeanal.py
+++ b/mypy/typeanal.py
@@ -625,8 +625,7 @@ def visit_instance(self, t: Instance) -> None:
                             continue
                     else:
                         arg_values = [arg]
-                    self.check_type_var_values(info, arg_values,
-                                               tvar.values, i + 1, t)
+                    self.check_type_var_values(info, arg_values, tvar.name, tvar.values, i + 1, t)
                 if not is_subtype(arg, tvar.upper_bound):
                     self.fail('Type argument "{}" of "{}" must be '
                               'a subtype of "{}"'.format(
@@ -637,7 +636,7 @@ def visit_instance(self, t: Instance) -> None:
             for base in info.bases:
                 base.accept(self)
 
-    def check_type_var_values(self, type: TypeInfo, actuals: List[Type],
+    def check_type_var_values(self, type: TypeInfo, actuals: List[Type], arg_name: str,
                               valids: List[Type], arg_number: int, context: Context) -> None:
         for actual in actuals:
             if (not isinstance(actual, AnyType) and
@@ -646,8 +645,10 @@ def check_type_var_values(self, type: TypeInfo, actuals: List[Type],
                     self.fail('Invalid type argument value for "{}"'.format(
                         type.name()), context)
                 else:
-                    self.fail('Type argument {} of "{}" has incompatible value "{}"'.format(
-                        arg_number, type.name(), actual.type.name()), context)
+                    class_name = '"{}"'.format(type.name())
+                    actual_type_name = '"{}"'.format(actual.type.name())
+                    self.fail(messages.INCOMPATIBLE_TYPEVAR_VALUE.format(
+                        arg_name, class_name, actual_type_name), context)
 
     def visit_callable_type(self, t: CallableType) -> None:
         t.ret_type.accept(self)


Test patch:
diff --git a/test-data/unit/check-bound.test b/test-data/unit/check-bound.test
--- a/test-data/unit/check-bound.test
+++ b/test-data/unit/check-bound.test
@@ -24,8 +24,8 @@ b = B()
 b = f(b)
 b = f(C()) # Fail
 [out]
-main:12: error: Type argument 1 of "f" has incompatible value "U"
-main:16: error: Type argument 1 of "f" has incompatible value "D"
+main:12: error: Value of type variable "T" of "f" cannot be "U"
+main:16: error: Value of type variable "T" of "f" cannot be "D"
 main:20: error: Incompatible types in assignment (expression has type "C", variable has type "B")
 
 
@@ -42,7 +42,7 @@ class G(Generic[T]):
 v = None # type: G[A]
 w = None # type: G[B]
 x = None # type: G[str] # E: Type argument "builtins.str" of "G" must be a subtype of "__main__.A"
-y = G('a') # E: Type argument 1 of "G" has incompatible value "str"
+y = G('a') # E: Value of type variable "T" of "G" cannot be "str"
 z = G(A())
 z = G(B())
 
@@ -125,7 +125,7 @@ def j(x: TA) -> A:
 def k(x: TA) -> B:
     return x # Fail
 [out]
-main:16: error: Type argument 1 of "h" has incompatible value "TA"
+main:16: error: Value of type variable "TB" of "h" cannot be "TA"
 main:21: error: Incompatible return value type (got "TA", expected "B")
 
 
@@ -199,5 +199,5 @@ def foo(x: int) -> int:
 a = 1
 b = foo(a)
 b = 'a' # E: Incompatible types in assignment (expression has type "str", variable has type "int")
-twice(a) # E: Type argument 1 of "twice" has incompatible value "int"
+twice(a) # E: Value of type variable "T" of "twice" cannot be "int"
 [builtins fixtures/args.pyi]
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
@@ -2118,7 +2118,7 @@ def error(u_c: Type[U]) -> P:
 [out]
 main:11: error: Revealed type is '__main__.WizUser*'
 main:13: error: Incompatible return value type (got "U", expected "P")
-main:13: error: Type argument 1 of "new_pro" has incompatible value "U"
+main:13: error: Value of type variable "P" of "new_pro" cannot be "U"
 
 [case testTypeUsingTypeCCovariance]
 from typing import Type, TypeVar
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
@@ -852,7 +852,7 @@ fun1(1) # E: Argument 1 to "fun1" has incompatible type "int"; expected List[Tup
 fun1([(1, 'x')]) # E: Cannot infer type argument 1 of "fun1"
 
 reveal_type(fun2([(1, 1)], 1)) # E: Revealed type is 'builtins.list[Tuple[builtins.int*, builtins.int*]]'
-fun2([('x', 'x')], 'x') # E: Type argument 1 of "fun2" has incompatible value "str"
+fun2([('x', 'x')], 'x') # E: Value of type variable "T" of "fun2" cannot be "str"
 
 [builtins fixtures/list.pyi]
 
@@ -1068,9 +1068,9 @@ y = None # type: SameA[str] # Two errors here, for both args of A
 
 [builtins fixtures/list.pyi]
 [out]
-main:9:7: error: Type argument 1 of "A" has incompatible value "str"
-main:13: error: Type argument 1 of "A" has incompatible value "str"
-main:13: error: Type argument 2 of "A" has incompatible value "str"
+main:9:7: error: Value of type variable "T" of "A" cannot be "str"
+main:13: error: Value of type variable "T" of "A" cannot be "str"
+main:13: error: Value of type variable "S" of "A" cannot be "str"
 
 [case testGenericTypeAliasesIgnoredPotentialAlias]
 class A: ...
diff --git a/test-data/unit/check-inference.test b/test-data/unit/check-inference.test
--- a/test-data/unit/check-inference.test
+++ b/test-data/unit/check-inference.test
@@ -750,10 +750,10 @@ AnyStr = TypeVar('AnyStr', bytes, str)
 def f(x: Union[AnyStr, int], *a: AnyStr) -> None: pass
 f('foo')
 f('foo', 'bar')
-f('foo', b'bar') # E: Type argument 1 of "f" has incompatible value "object"
+f('foo', b'bar') # E: Value of type variable "AnyStr" of "f" cannot be "object"
 f(1)
 f(1, 'foo')
-f(1, 'foo', b'bar') # E: Type argument 1 of "f" has incompatible value "object"
+f(1, 'foo', b'bar') # E: Value of type variable "AnyStr" of "f" cannot be "object"
 [builtins fixtures/primitives.pyi]
 
 
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
@@ -957,8 +957,8 @@ def g(x: U, y: V) -> None:
     f(x)() # E: "mystr" not callable
     f(y) # E: No overload variant of "f" matches argument types [V`-2]
     a = f([x]) # E: "f" does not return a value
-    f([y]) # E: Type argument 1 of "f" has incompatible value "V"
-    f([x, y]) # E: Type argument 1 of "f" has incompatible value "object"
+    f([y]) # E: Value of type variable "T" of "f" cannot be "V"
+    f([x, y]) # E: Value of type variable "T" of "f" cannot be "object"
 [builtins fixtures/list.pyi]
 [out]
 
@@ -998,10 +998,10 @@ def g(x: int, *a: AnyStr) -> None: pass
 
 g('foo')
 g('foo', 'bar')
-g('foo', b'bar') # E: Type argument 1 of "g" has incompatible value "object"
+g('foo', b'bar') # E: Value of type variable "AnyStr" of "g" cannot be "object"
 g(1)
 g(1, 'foo')
-g(1, 'foo', b'bar') # E: Type argument 1 of "g" has incompatible value "object"
+g(1, 'foo', b'bar') # E: Value of type variable "AnyStr" of "g" cannot be "object"
 [builtins fixtures/primitives.pyi]
 
 [case testBadOverlapWithTypeVarsWithValues]
diff --git a/test-data/unit/check-typevar-values.test b/test-data/unit/check-typevar-values.test
--- a/test-data/unit/check-typevar-values.test
+++ b/test-data/unit/check-typevar-values.test
@@ -7,7 +7,7 @@ T = TypeVar('T', int, str)
 def f(x: T) -> None: pass
 f(1)
 f('x')
-f(object()) # E: Type argument 1 of "f" has incompatible value "object"
+f(object()) # E: Value of type variable "T" of "f" cannot be "object"
 
 [case testCallGenericFunctionWithTypeVarValueRestrictionUsingContext]
 from typing import TypeVar, List
@@ -18,7 +18,7 @@ s = ['x']
 o = [object()]
 i = f(1)
 s = f('')
-o = f(1) # E: Type argument 1 of "f" has incompatible value "object"
+o = f(1) # E: Value of type variable "T" of "f" cannot be "object"
 [builtins fixtures/list.pyi]
 
 [case testCallGenericFunctionWithTypeVarValueRestrictionAndAnyArgs]
@@ -228,7 +228,7 @@ X = TypeVar('X', int, str)
 class A(Generic[X]): pass
 a = None  # type: A[int]
 b = None  # type: A[str]
-d = None  # type: A[object] # E: Type argument 1 of "A" has incompatible value "object"
+d = None  # type: A[object] # E: Value of type variable "X" of "A" cannot be "object"
 c = None  # type: A[Any]
 
 [case testConstructGenericTypeWithTypevarValuesAndTypeInference]
@@ -239,7 +239,7 @@ class A(Generic[X]):
 A(1)
 A('x')
 A(cast(Any, object()))
-A(object()) # E: Type argument 1 of "A" has incompatible value "object"
+A(object()) # E: Value of type variable "X" of "A" cannot be "object"
 
 [case testGenericTypeWithTypevarValuesAndTypevarArgument]
 from typing import TypeVar, Generic
@@ -390,8 +390,8 @@ Y = TypeVar('Y', int, str)
 class C(Generic[X, Y]): pass
 a = None  # type: C[A, int]
 b = None  # type: C[B, str]
-c = None  # type: C[int, int] # E: Type argument 1 of "C" has incompatible value "int"
-d = None  # type: C[A, A]     # E: Type argument 2 of "C" has incompatible value "A"
+c = None  # type: C[int, int] # E: Value of type variable "X" of "C" cannot be "int"
+d = None  # type: C[A, A]     # E: Value of type variable "Y" of "C" cannot be "A"
 
 [case testCallGenericFunctionUsingMultipleTypevarsWithValues]
 from typing import TypeVar
@@ -402,8 +402,8 @@ Y = TypeVar('Y', int, str)
 def f(x: X, y: Y) -> None: pass
 f(A(), '')
 f(B(), 1)
-f(A(), A()) # E: Type argument 2 of "f" has incompatible value "A"
-f(1, 1) # E: Type argument 1 of "f" has incompatible value "int"
+f(A(), A())  # E: Value of type variable "Y" of "f" cannot be "A"
+f(1, 1)  # E: Value of type variable "X" of "f" cannot be "int"
 
 [case testGenericFunctionWithNormalAndRestrictedTypevar]
 from typing import TypeVar, Generic
@@ -418,7 +418,7 @@ def f(x: X, y: Y, z: int) -> None:
     z = y # Error
     y.foo # Error
 [out]
-main:8: error: Type argument 1 of "C" has incompatible value "X"
+main:8: error: Value of type variable "Y" of "C" cannot be "X"
 main:9: error: Incompatible types in assignment (expression has type "X", variable has type "int")
 main:10: error: Incompatible types in assignment (expression has type "str", variable has type "int")
 main:11: error: "int" has no attribute "foo"
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
@@ -1277,7 +1277,7 @@ re.subn(bpat, b'', b'')[0] + b''
 re.subn(bre, lambda m: b'', b'')[0] + b''
 re.subn(bpat, lambda m: b'', b'')[0] + b''
 [out]
-_program.py:7: error: Type argument 1 of "search" has incompatible value "object"
+_program.py:7: error: Value of type variable "AnyStr" of "search" cannot be "object"
 _program.py:9: error: Cannot infer type argument 1 of "search"
 
 [case testReModuleString]
@@ -1301,7 +1301,7 @@ re.subn(spat, '', '')[0] + ''
 re.subn(sre, lambda m: '', '')[0] + ''
 re.subn(spat, lambda m: '', '')[0] + ''
 [out]
-_program.py:7: error: Type argument 1 of "search" has incompatible value "object"
+_program.py:7: error: Value of type variable "AnyStr" of "search" cannot be "object"
 _program.py:9: error: Cannot infer type argument 1 of "search"
 
 [case testListSetitemTuple]