diff --git a/mypy/checker.py b/mypy/checker.py index 109a3b1f15d2..1b36ef9d99e2 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -30,7 +30,10 @@ from mypy import nodes from mypy import operators from mypy.literals import literal, literal_hash, Key -from mypy.typeanal import has_any_from_unimported_type, check_for_explicit_any, make_optional_type +from mypy.typeanal import ( + has_any_from_unimported_type, check_for_explicit_any, make_optional_type, + maybe_expand_unimported_type_becomes_any, +) from mypy.types import ( Type, AnyType, CallableType, FunctionLike, Overloaded, TupleType, TypedDictType, Instance, NoneType, strip_type, TypeType, TypeOfAny, @@ -892,11 +895,15 @@ def check_func_def(self, defn: FuncItem, typ: CallableType, name: Optional[str]) if fdef.type and isinstance(fdef.type, CallableType): ret_type = fdef.type.ret_type if has_any_from_unimported_type(ret_type): - self.msg.unimported_type_becomes_any("Return type", ret_type, fdef) + maybe_expand_unimported_type_becomes_any( + "Return type", ret_type, fdef, self.msg + ) for idx, arg_type in enumerate(fdef.type.arg_types): if has_any_from_unimported_type(arg_type): prefix = f'Argument {idx + 1} to "{fdef.name}"' - self.msg.unimported_type_becomes_any(prefix, arg_type, fdef) + maybe_expand_unimported_type_becomes_any( + prefix, arg_type, fdef, self.msg + ) check_for_explicit_any(fdef.type, self.options, self.is_typeshed_stub, self.msg, context=fdef) @@ -2227,10 +2234,10 @@ def visit_assignment_stmt(self, s: AssignmentStmt) -> None: if isinstance(s.lvalues[-1], TupleExpr): # This is a multiple assignment. Instead of figuring out which type is problematic, # give a generic error message. - self.msg.unimported_type_becomes_any("A type on this line", - AnyType(TypeOfAny.special_form), s) + maybe_expand_unimported_type_becomes_any("A type on this line", + AnyType(TypeOfAny.special_form), s, self.msg) else: - self.msg.unimported_type_becomes_any("Type of variable", s.type, s) + maybe_expand_unimported_type_becomes_any("Type of variable", s.type, s, self.msg) check_for_explicit_any(s.type, self.options, self.is_typeshed_stub, self.msg, context=s) if len(s.lvalues) > 1: @@ -3301,6 +3308,7 @@ def check_simple_assignment(self, lvalue_type: Optional[Type], rvalue: Expressio # '...' is always a valid initializer in a stub. return AnyType(TypeOfAny.special_form) else: + orig_lvalue_type = lvalue_type lvalue_type = get_proper_type(lvalue_type) always_allow_any = lvalue_type is not None and not isinstance(lvalue_type, AnyType) rvalue_type = self.expr_checker.accept(rvalue, lvalue_type, @@ -3310,8 +3318,8 @@ def check_simple_assignment(self, lvalue_type: Optional[Type], rvalue: Expressio self.msg.deleted_as_rvalue(rvalue_type, context) if isinstance(lvalue_type, DeletedType): self.msg.deleted_as_lvalue(lvalue_type, context) - elif lvalue_type: - self.check_subtype(rvalue_type, lvalue_type, context, msg, + elif orig_lvalue_type: + self.check_subtype(rvalue_type, orig_lvalue_type, context, msg, f'{rvalue_name} has type', f'{lvalue_name} has type', code=code) return rvalue_type @@ -5243,6 +5251,7 @@ def check_subtype(self, else: msg_text = msg subtype = get_proper_type(subtype) + orig_supertype = supertype supertype = get_proper_type(supertype) if self.msg.try_report_long_tuple_assignment_error(subtype, supertype, context, msg_text, subtype_label, supertype_label, code=code): @@ -5253,13 +5262,13 @@ def check_subtype(self, note_msg = '' notes: List[str] = [] if subtype_label is not None or supertype_label is not None: - subtype_str, supertype_str = format_type_distinctly(subtype, supertype) + subtype_str, supertype_str = format_type_distinctly(subtype, orig_supertype) if subtype_label is not None: extra_info.append(subtype_label + ' ' + subtype_str) if supertype_label is not None: extra_info.append(supertype_label + ' ' + supertype_str) note_msg = make_inferred_type_note(outer_context or context, subtype, - supertype, supertype_str) + orig_supertype, supertype_str) if isinstance(subtype, Instance) and isinstance(supertype, Instance): notes = append_invariance_notes([], subtype, supertype) if extra_info: @@ -5282,7 +5291,7 @@ def check_subtype(self, if supertype.type.is_protocol and supertype.type.protocol_members == ['__call__']: call = find_member('__call__', supertype, subtype, is_operator=True) assert call is not None - self.msg.note_call(supertype, call, context, code=code) + self.msg.note_call(orig_supertype, call, context, code=code) return False def contains_none(self, t: Type) -> bool: diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py index bfbe961adc7a..5264ea063202 100644 --- a/mypy/checkexpr.py +++ b/mypy/checkexpr.py @@ -11,7 +11,7 @@ from mypy.errors import report_internal_error, ErrorWatcher from mypy.typeanal import ( has_any_from_unimported_type, check_for_explicit_any, set_any_tvars, expand_type_alias, - make_optional_type, + make_optional_type, maybe_expand_unimported_type_becomes_any, ) from mypy.semanal_enum import ENUM_BASES from mypy.types import ( @@ -1539,7 +1539,6 @@ def check_arg(self, outer_context: Context) -> None: """Check the type of a single argument in a call.""" caller_type = get_proper_type(caller_type) - original_caller_type = get_proper_type(original_caller_type) callee_type = get_proper_type(callee_type) if isinstance(caller_type, DeletedType): @@ -1562,6 +1561,7 @@ def check_arg(self, object_type=object_type, context=context, outer_context=outer_context) + original_caller_type = get_proper_type(original_caller_type) self.msg.incompatible_argument_note(original_caller_type, callee_type, context, code=code) @@ -3110,7 +3110,9 @@ def visit_cast_expr(self, expr: CastExpr) -> Type: and is_same_type(source_type, target_type)): self.msg.redundant_cast(target_type, expr) if options.disallow_any_unimported and has_any_from_unimported_type(target_type): - self.msg.unimported_type_becomes_any("Target type of cast", target_type, expr) + maybe_expand_unimported_type_becomes_any( + "Target type of cast", target_type, expr, self.msg + ) check_for_explicit_any(target_type, self.chk.options, self.chk.is_typeshed_stub, self.msg, context=expr) return target_type @@ -4167,7 +4169,9 @@ def visit_namedtuple_expr(self, e: NamedTupleExpr) -> Type: if tuple_type: if (self.chk.options.disallow_any_unimported and has_any_from_unimported_type(tuple_type)): - self.msg.unimported_type_becomes_any("NamedTuple type", tuple_type, e) + maybe_expand_unimported_type_becomes_any( + "NamedTuple type", tuple_type, e, self.msg + ) check_for_explicit_any(tuple_type, self.chk.options, self.chk.is_typeshed_stub, self.msg, context=e) return AnyType(TypeOfAny.special_form) diff --git a/mypy/messages.py b/mypy/messages.py index 70d79384c1a9..3b2988ea603a 100644 --- a/mypy/messages.py +++ b/mypy/messages.py @@ -26,7 +26,7 @@ Type, CallableType, Instance, TypeVarType, TupleType, TypedDictType, LiteralType, UnionType, NoneType, AnyType, Overloaded, FunctionLike, DeletedType, TypeType, UninhabitedType, TypeOfAny, UnboundType, PartialType, get_proper_type, ProperType, - ParamSpecType, Parameters, get_proper_types + ParamSpecType, Parameters, get_proper_types, TypeAliasType ) from mypy.typetraverser import TypeTraverserVisitor from mypy.nodes import ( @@ -405,7 +405,6 @@ def incompatible_argument(self, Return the error code that used for the argument (multiple error codes are possible). """ - arg_type = get_proper_type(arg_type) target = '' callee_name = callable_name(callee) @@ -469,6 +468,7 @@ def incompatible_argument(self, elif callee_name == '': name = callee_name[1:-1] n -= 1 + arg_type = get_proper_type(arg_type) key_type, value_type = cast(TupleType, arg_type).items expected_key_type, expected_value_type = cast(TupleType, callee.arg_types[0]).items @@ -534,6 +534,7 @@ def incompatible_argument(self, arg_name = outer_context.arg_names[n - 1] if arg_name is not None: arg_label = f'"{arg_name}"' + arg_type = get_proper_type(arg_type) if (arg_kind == ARG_STAR2 and isinstance(arg_type, TypedDictType) and m <= len(callee.arg_names) @@ -1186,6 +1187,10 @@ def assert_type_fail(self, source_type: Type, target_type: Type, context: Contex code=codes.ASSERT_TYPE) def unimported_type_becomes_any(self, prefix: str, typ: Type, ctx: Context) -> None: + """Print an error about an unfollowed import turning a type into Any + + Using typeanal.maybe_expand_unimported_type_becomes_any is preferred because + it will expand type aliases to make errors clearer.""" self.fail(f"{prefix} becomes {format_type(typ)} due to an unfollowed import", ctx, code=codes.NO_ANY_UNIMPORTED) @@ -1696,32 +1701,42 @@ def format_literal_value(typ: LiteralType) -> str: else: return typ.value_repr() - # TODO: show type alias names in errors. - typ = get_proper_type(typ) + def format_from_names(name: str, fullname: str, args: Sequence[Type]) -> str: + """Format a type given its short name, full name, and type arguments""" - if isinstance(typ, Instance): - itype = typ - # Get the short name of the type. - if itype.type.fullname in ('types.ModuleType', '_importlib_modulespec.ModuleType'): + # Some of this only really makes sense for instances, but it's easier + # to include it here than try to only use that code when formatting + # an Instance + if fullname in ('types.ModuleType', '_importlib_modulespec.ModuleType'): # Make some common error messages simpler and tidier. return 'Module' - if verbosity >= 2 or (fullnames and itype.type.fullname in fullnames): - base_str = itype.type.fullname + if verbosity >= 2 or (fullnames and fullname in fullnames): + base_str = fullname else: - base_str = itype.type.name - if not itype.args: + base_str = name + if not args: # No type arguments, just return the type name return base_str - elif itype.type.fullname == 'builtins.tuple': - item_type_str = format(itype.args[0]) + elif fullname == 'builtins.tuple': + item_type_str = format(args[0]) return f'Tuple[{item_type_str}, ...]' - elif itype.type.fullname in reverse_builtin_aliases: - alias = reverse_builtin_aliases[itype.type.fullname] + elif fullname in reverse_builtin_aliases: + alias = reverse_builtin_aliases[fullname] alias = alias.split('.')[-1] - return f'{alias}[{format_list(itype.args)}]' + return f'{alias}[{format_list(args)}]' else: # There are type arguments. Convert the arguments to strings. - return f'{base_str}[{format_list(itype.args)}]' + return f'{base_str}[{format_list(args)}]' + + if isinstance(typ, TypeAliasType): + # typ.alias should only ever be None during creation + assert typ.alias is not None + return format_from_names(typ.alias.name, typ.alias.fullname, typ.args) + # get_proper_type doesn't do anything here but we need it to make mypy happy + typ = get_proper_type(typ) + + if isinstance(typ, Instance): + return format_from_names(typ.type.name, typ.type.fullname, typ.args) elif isinstance(typ, TypeVarType): # This is similar to non-generic instance types. return typ.name @@ -1842,25 +1857,32 @@ def format_literal_value(typ: LiteralType) -> str: return 'object' -def collect_all_instances(t: Type) -> List[Instance]: - """Return all instances that `t` contains (including `t`). +def collect_all_names(t: Type) -> List[Tuple[str, str]]: + """Return all (shortname, fullname) pairs for all instances and + type aliases that show up when printing `t` in an error message. - This is similar to collect_all_inner_types from typeanal but only - returns instances and will recurse into fallbacks. + TODO: extend this to include all name pairs that will show up in + the error message """ - visitor = CollectAllInstancesQuery() + visitor = CollectAllNamesQuery() t.accept(visitor) - return visitor.instances + return visitor.names -class CollectAllInstancesQuery(TypeTraverserVisitor): +class CollectAllNamesQuery(TypeTraverserVisitor): def __init__(self) -> None: - self.instances: List[Instance] = [] + # list of (shortname, fullname) pairs + self.names: List[Tuple[str, str]] = [] def visit_instance(self, t: Instance) -> None: - self.instances.append(t) + self.names.append((t.type.name, t.type.fullname)) super().visit_instance(t) + def visit_type_alias_type(self, t: TypeAliasType) -> None: + assert t.alias is not None + self.names.append((t.alias.name, t.alias.fullname)) + super().visit_type_alias_type(t) + def find_type_overlaps(*types: Type) -> Set[str]: """Return a set of fullnames that share a short name and appear in either type. @@ -1870,8 +1892,8 @@ def find_type_overlaps(*types: Type) -> Set[str]: """ d: Dict[str, Set[str]] = {} for type in types: - for inst in collect_all_instances(type): - d.setdefault(inst.type.name, set()).add(inst.type.fullname) + for name, fullname in collect_all_names(type): + d.setdefault(name, set()).add(fullname) for shortname in d.keys(): if f'typing.{shortname}' in TYPES_FOR_UNIMPORTED_HINTS: d[shortname].add(f'typing.{shortname}') diff --git a/mypy/semanal.py b/mypy/semanal.py index a49e7c23edf5..7d0d3221cefa 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -106,7 +106,8 @@ from mypy.typeanal import ( TypeAnalyser, analyze_type_alias, no_subscript_builtin_alias, TypeVarLikeQuery, TypeVarLikeList, remove_dups, has_any_from_unimported_type, - check_for_explicit_any, type_constructors, fix_instance_types + check_for_explicit_any, type_constructors, fix_instance_types, + maybe_expand_unimported_type_becomes_any, ) from mypy.exprtotype import expr_to_unanalyzed_type, TypeTranslationError from mypy.options import Options @@ -1593,7 +1594,7 @@ def configure_base_classes(self, prefix = f"Base type {base_expr.name}" else: prefix = "Base type" - self.msg.unimported_type_becomes_any(prefix, base, base_expr) + maybe_expand_unimported_type_becomes_any(prefix, base, base_expr, self.msg) check_for_explicit_any(base, self.options, self.is_typeshed_stub_file, self.msg, context=base_expr) @@ -3133,11 +3134,11 @@ def process_typevar_declaration(self, s: AssignmentStmt) -> bool: for idx, constraint in enumerate(values, start=1): if has_any_from_unimported_type(constraint): prefix = f"Constraint {idx}" - self.msg.unimported_type_becomes_any(prefix, constraint, s) + maybe_expand_unimported_type_becomes_any(prefix, constraint, s, self.msg) if has_any_from_unimported_type(upper_bound): prefix = "Upper bound of type variable" - self.msg.unimported_type_becomes_any(prefix, upper_bound, s) + maybe_expand_unimported_type_becomes_any(prefix, upper_bound, s, self.msg) for t in values + [upper_bound]: check_for_explicit_any(t, self.options, self.is_typeshed_stub_file, self.msg, diff --git a/mypy/semanal_newtype.py b/mypy/semanal_newtype.py index 948c5b36052f..e9a922b8abad 100644 --- a/mypy/semanal_newtype.py +++ b/mypy/semanal_newtype.py @@ -17,7 +17,9 @@ from mypy.semanal_shared import SemanticAnalyzerInterface from mypy.options import Options from mypy.exprtotype import expr_to_unanalyzed_type, TypeTranslationError -from mypy.typeanal import check_for_explicit_any, has_any_from_unimported_type +from mypy.typeanal import ( + check_for_explicit_any, has_any_from_unimported_type, maybe_expand_unimported_type_becomes_any, +) from mypy.messages import MessageBuilder, format_type from mypy.errorcodes import ErrorCode from mypy import errorcodes as codes @@ -92,7 +94,9 @@ def process_newtype_declaration(self, s: AssignmentStmt) -> bool: context=s) if self.options.disallow_any_unimported and has_any_from_unimported_type(old_type): - self.msg.unimported_type_becomes_any("Argument 2 to NewType(...)", old_type, s) + maybe_expand_unimported_type_becomes_any( + "Argument 2 to NewType(...)", old_type, s, self.msg + ) # If so, add it to the symbol table. assert isinstance(call.analyzed, NewTypeExpr) diff --git a/mypy/semanal_typeddict.py b/mypy/semanal_typeddict.py index 4087f477c597..a190e9f9071c 100644 --- a/mypy/semanal_typeddict.py +++ b/mypy/semanal_typeddict.py @@ -15,7 +15,9 @@ from mypy.semanal_shared import SemanticAnalyzerInterface from mypy.exprtotype import expr_to_unanalyzed_type, TypeTranslationError from mypy.options import Options -from mypy.typeanal import check_for_explicit_any, has_any_from_unimported_type +from mypy.typeanal import ( + check_for_explicit_any, has_any_from_unimported_type, maybe_expand_unimported_type_becomes_any, +) from mypy.messages import MessageBuilder from mypy.errorcodes import ErrorCode from mypy import errorcodes as codes @@ -308,7 +310,9 @@ def parse_typeddict_args( if self.options.disallow_any_unimported: for t in types: if has_any_from_unimported_type(t): - self.msg.unimported_type_becomes_any("Type of a TypedDict key", t, dictexpr) + maybe_expand_unimported_type_becomes_any( + "Type of a TypedDict key", t, dictexpr, self.msg + ) assert total is not None return args[0].value, items, types, total, ok diff --git a/mypy/typeanal.py b/mypy/typeanal.py index 98e37bd0aa40..bbcceec71f40 100644 --- a/mypy/typeanal.py +++ b/mypy/typeanal.py @@ -1475,6 +1475,23 @@ def visit_callable_type(self, t: CallableType) -> TypeVarLikeList: return [] +def maybe_expand_unimported_type_becomes_any( + prefix: str, typ: Type, ctx: Context, msg: MessageBuilder, +) -> None: + """Show an error about `typ` turning into Any, possibly expanding any type aliases + if necessary to make the error message clearer + """ + if isinstance(typ, TypeAliasType): + assert typ.alias is not None + if has_any_from_unimported_type(typ.alias.target): + # The underlying type that this type alias points to has parts that turn + # into Anys, so printing the unexpanded alias doesn't really make it + # obvious where the Any type is + # Avoid that by expanding the alias before printing in that case + typ = get_proper_type(typ) + msg.unimported_type_becomes_any(prefix, typ, ctx) + + def check_for_explicit_any(typ: Optional[Type], options: Options, is_typeshed_stub: bool, diff --git a/test-data/unit/check-generic-alias.test b/test-data/unit/check-generic-alias.test index 574a57607d11..a95d32643ca2 100644 --- a/test-data/unit/check-generic-alias.test +++ b/test-data/unit/check-generic-alias.test @@ -238,11 +238,11 @@ t10: Tuple[int, ...] = t09 A = tuple[int, ...] a: A = () b: A = (1, 2, 3) -c: A = ('x', 'y') # E: Incompatible types in assignment (expression has type "Tuple[str, str]", variable has type "Tuple[int, ...]") +c: A = ('x', 'y') # E: Incompatible types in assignment (expression has type "Tuple[str, str]", variable has type "A") B = tuple[int, str] x: B = (1, 'x') -y: B = ('x', 1) # E: Incompatible types in assignment (expression has type "Tuple[str, int]", variable has type "Tuple[int, str]") +y: B = ('x', 1) # E: Incompatible types in assignment (expression has type "Tuple[str, int]", variable has type "B") reveal_type(tuple[int, ...]()) # N: Revealed type is "builtins.tuple[builtins.int, ...]" [builtins fixtures/tuple.pyi] diff --git a/test-data/unit/check-generics.test b/test-data/unit/check-generics.test index b228e76a32d1..2fd1e3f0a31f 100644 --- a/test-data/unit/check-generics.test +++ b/test-data/unit/check-generics.test @@ -563,7 +563,7 @@ def func(x: IntNode[T]) -> IntNode[T]: return x reveal_type(func) # N: Revealed type is "def [T] (x: __main__.Node[builtins.int, T`-1]) -> __main__.Node[builtins.int, T`-1]" -func(1) # E: Argument 1 to "func" has incompatible type "int"; expected "Node[int, ]" +func(1) # E: Argument 1 to "func" has incompatible type "int"; expected "IntNode[]" func(Node('x', 1)) # E: Argument 1 to "Node" has incompatible type "str"; expected "int" reveal_type(func(Node(1, 'x'))) # N: Revealed type is "__main__.Node[builtins.int, builtins.str]" @@ -772,7 +772,7 @@ def f(x: T) -> UNode[T]: reveal_type(f(1)) # N: Revealed type is "Union[builtins.int, __main__.Node[builtins.int]]" TNode = Union[T, Node[int]] -s = 1 # type: TNode[str] # E: Incompatible types in assignment (expression has type "int", variable has type "Union[str, Node[int]]") +s = 1 # type: TNode[str] # E: Incompatible types in assignment (expression has type "int", variable has type "TNode[str]") if not isinstance(s, str): s.x = 1 @@ -800,7 +800,7 @@ reveal_type(x) # N: Revealed type is "builtins.int" def f2(x: IntTP[T]) -> IntTP[T]: return x -f2((1, 2, 3)) # E: Argument 1 to "f2" has incompatible type "Tuple[int, int, int]"; expected "Tuple[int, ]" +f2((1, 2, 3)) # E: Argument 1 to "f2" has incompatible type "Tuple[int, int, int]"; expected "IntTP[]" reveal_type(f2((1, 'x'))) # N: Revealed type is "Tuple[builtins.int, builtins.str]" [builtins fixtures/for.pyi] @@ -825,9 +825,9 @@ reveal_type(make_cb(1)) # N: Revealed type is "def (*Any, **Any) -> builtins.int def use_cb(arg: T, cb: C2[T]) -> Node[T]: return cb(arg, arg) -use_cb(1, 1) # E: Argument 2 to "use_cb" has incompatible type "int"; expected "Callable[[int, int], Node[int]]" +use_cb(1, 1) # E: Argument 2 to "use_cb" has incompatible type "int"; expected "C2[int]" my_cb = None # type: C2[int] -use_cb('x', my_cb) # E: Argument 2 to "use_cb" has incompatible type "Callable[[int, int], Node[int]]"; expected "Callable[[str, str], Node[str]]" +use_cb('x', my_cb) # E: Argument 2 to "use_cb" has incompatible type "C2[int]"; expected "C2[str]" reveal_type(use_cb(1, my_cb)) # N: Revealed type is "__main__.Node[builtins.int]" [builtins fixtures/tuple.pyi] @@ -849,7 +849,7 @@ def fun2(v: Vec[T], scale: T) -> Vec[T]: return v reveal_type(fun1([(1, 1)])) # N: Revealed type is "builtins.int" -fun1(1) # E: Argument 1 to "fun1" has incompatible type "int"; expected "List[Tuple[bool, bool]]" +fun1(1) # E: Argument 1 to "fun1" has incompatible type "int"; expected "Vec[bool]" fun1([(1, 'x')]) # E: Cannot infer type argument 1 of "fun1" reveal_type(fun2([(1, 1)], 1)) # N: Revealed type is "builtins.list[Tuple[builtins.int, builtins.int]]" diff --git a/test-data/unit/check-literal.test b/test-data/unit/check-literal.test index ab6154428343..424c7322c2d8 100644 --- a/test-data/unit/check-literal.test +++ b/test-data/unit/check-literal.test @@ -269,7 +269,7 @@ accepts_str_1(b_hint) accepts_str_1(c_hint) # E: Argument 1 to "accepts_str_1" has incompatible type "Literal[b'foo']"; expected "Literal['foo']" accepts_str_1(a_alias) accepts_str_1(b_alias) -accepts_str_1(c_alias) # E: Argument 1 to "accepts_str_1" has incompatible type "Literal[b'foo']"; expected "Literal['foo']" +accepts_str_1(c_alias) # E: Argument 1 to "accepts_str_1" has incompatible type "CAlias"; expected "Literal['foo']" accepts_str_2(a_ann) accepts_str_2(b_ann) @@ -279,7 +279,7 @@ accepts_str_2(b_hint) accepts_str_2(c_hint) # E: Argument 1 to "accepts_str_2" has incompatible type "Literal[b'foo']"; expected "Literal['foo']" accepts_str_2(a_alias) accepts_str_2(b_alias) -accepts_str_2(c_alias) # E: Argument 1 to "accepts_str_2" has incompatible type "Literal[b'foo']"; expected "Literal['foo']" +accepts_str_2(c_alias) # E: Argument 1 to "accepts_str_2" has incompatible type "CAlias"; expected "Literal['foo']" accepts_bytes(a_ann) # E: Argument 1 to "accepts_bytes" has incompatible type "Literal['foo']"; expected "Literal[b'foo']" accepts_bytes(b_ann) # E: Argument 1 to "accepts_bytes" has incompatible type "Literal['foo']"; expected "Literal[b'foo']" @@ -287,8 +287,8 @@ accepts_bytes(c_ann) accepts_bytes(a_hint) # E: Argument 1 to "accepts_bytes" has incompatible type "Literal['foo']"; expected "Literal[b'foo']" accepts_bytes(b_hint) # E: Argument 1 to "accepts_bytes" has incompatible type "Literal['foo']"; expected "Literal[b'foo']" accepts_bytes(c_hint) -accepts_bytes(a_alias) # E: Argument 1 to "accepts_bytes" has incompatible type "Literal['foo']"; expected "Literal[b'foo']" -accepts_bytes(b_alias) # E: Argument 1 to "accepts_bytes" has incompatible type "Literal['foo']"; expected "Literal[b'foo']" +accepts_bytes(a_alias) # E: Argument 1 to "accepts_bytes" has incompatible type "AAlias"; expected "Literal[b'foo']" +accepts_bytes(b_alias) # E: Argument 1 to "accepts_bytes" has incompatible type "BAlias"; expected "Literal[b'foo']" accepts_bytes(c_alias) [builtins fixtures/tuple.pyi] [out] @@ -329,20 +329,20 @@ accepts_unicode(a_hint) accepts_unicode(b_hint) # E: Argument 1 to "accepts_unicode" has incompatible type "Literal['foo']"; expected "Literal[u'foo']" accepts_unicode(c_hint) # E: Argument 1 to "accepts_unicode" has incompatible type "Literal['foo']"; expected "Literal[u'foo']" accepts_unicode(a_alias) -accepts_unicode(b_alias) # E: Argument 1 to "accepts_unicode" has incompatible type "Literal['foo']"; expected "Literal[u'foo']" -accepts_unicode(c_alias) # E: Argument 1 to "accepts_unicode" has incompatible type "Literal['foo']"; expected "Literal[u'foo']" +accepts_unicode(b_alias) # E: Argument 1 to "accepts_unicode" has incompatible type "BAlias"; expected "Literal[u'foo']" +accepts_unicode(c_alias) # E: Argument 1 to "accepts_unicode" has incompatible type "CAlias"; expected "Literal[u'foo']" accepts_bytes_1(a_hint) # E: Argument 1 to "accepts_bytes_1" has incompatible type "Literal[u'foo']"; expected "Literal['foo']" accepts_bytes_1(b_hint) accepts_bytes_1(c_hint) -accepts_bytes_1(a_alias) # E: Argument 1 to "accepts_bytes_1" has incompatible type "Literal[u'foo']"; expected "Literal['foo']" +accepts_bytes_1(a_alias) # E: Argument 1 to "accepts_bytes_1" has incompatible type "AAlias"; expected "Literal['foo']" accepts_bytes_1(b_alias) accepts_bytes_1(c_alias) accepts_bytes_2(a_hint) # E: Argument 1 to "accepts_bytes_2" has incompatible type "Literal[u'foo']"; expected "Literal['foo']" accepts_bytes_2(b_hint) accepts_bytes_2(c_hint) -accepts_bytes_2(a_alias) # E: Argument 1 to "accepts_bytes_2" has incompatible type "Literal[u'foo']"; expected "Literal['foo']" +accepts_bytes_2(a_alias) # E: Argument 1 to "accepts_bytes_2" has incompatible type "AAlias"; expected "Literal['foo']" accepts_bytes_2(b_alias) accepts_bytes_2(c_alias) [builtins fixtures/primitives.pyi] @@ -386,20 +386,20 @@ accepts_unicode_1(b_hint) accepts_unicode_1(c_hint) # E: Argument 1 to "accepts_unicode_1" has incompatible type "Literal['foo']"; expected "Literal[u'foo']" accepts_unicode_1(a_alias) accepts_unicode_1(b_alias) -accepts_unicode_1(c_alias) # E: Argument 1 to "accepts_unicode_1" has incompatible type "Literal['foo']"; expected "Literal[u'foo']" +accepts_unicode_1(c_alias) # E: Argument 1 to "accepts_unicode_1" has incompatible type "CAlias"; expected "Literal[u'foo']" accepts_unicode_2(a_hint) accepts_unicode_2(b_hint) accepts_unicode_2(c_hint) # E: Argument 1 to "accepts_unicode_2" has incompatible type "Literal['foo']"; expected "Literal[u'foo']" accepts_unicode_2(a_alias) accepts_unicode_2(b_alias) -accepts_unicode_2(c_alias) # E: Argument 1 to "accepts_unicode_2" has incompatible type "Literal['foo']"; expected "Literal[u'foo']" +accepts_unicode_2(c_alias) # E: Argument 1 to "accepts_unicode_2" has incompatible type "CAlias"; expected "Literal[u'foo']" accepts_bytes(a_hint) # E: Argument 1 to "accepts_bytes" has incompatible type "Literal[u'foo']"; expected "Literal['foo']" accepts_bytes(b_hint) # E: Argument 1 to "accepts_bytes" has incompatible type "Literal[u'foo']"; expected "Literal['foo']" accepts_bytes(c_hint) -accepts_bytes(a_alias) # E: Argument 1 to "accepts_bytes" has incompatible type "Literal[u'foo']"; expected "Literal['foo']" -accepts_bytes(b_alias) # E: Argument 1 to "accepts_bytes" has incompatible type "Literal[u'foo']"; expected "Literal['foo']" +accepts_bytes(a_alias) # E: Argument 1 to "accepts_bytes" has incompatible type "AAlias"; expected "Literal['foo']" +accepts_bytes(b_alias) # E: Argument 1 to "accepts_bytes" has incompatible type "BAlias"; expected "Literal['foo']" accepts_bytes(c_alias) [builtins fixtures/primitives.pyi] [out] @@ -579,11 +579,11 @@ from_b = "foo" # type: b.Alias u.func(u.var) u.func(from_u) -u.func(b.var) # E: Argument 1 to "func" has incompatible type "Literal['foo']"; expected "Literal[u'foo']" -u.func(from_b) # E: Argument 1 to "func" has incompatible type "Literal['foo']"; expected "Literal[u'foo']" +u.func(b.var) # E: Argument 1 to "func" has incompatible type "Alias"; expected "Literal[u'foo']" +u.func(from_b) # E: Argument 1 to "func" has incompatible type "Alias"; expected "Literal[u'foo']" -b.func(u.var) # E: Argument 1 to "func" has incompatible type "Literal[u'foo']"; expected "Literal['foo']" -b.func(from_u) # E: Argument 1 to "func" has incompatible type "Literal[u'foo']"; expected "Literal['foo']" +b.func(u.var) # E: Argument 1 to "func" has incompatible type "Alias"; expected "Literal['foo']" +b.func(from_u) # E: Argument 1 to "func" has incompatible type "Alias"; expected "Literal['foo']" b.func(b.var) b.func(from_b) @@ -1293,7 +1293,7 @@ def func(x: Foo[15]) -> None: pass a: Bar1 b: Bar2 func(a) -func(b) # E: Argument 1 to "func" has incompatible type "Literal[14]"; expected "Literal[15]" +func(b) # E: Argument 1 to "func" has incompatible type "Bar2"; expected "Literal[15]" func(c) [file other_module.py] diff --git a/test-data/unit/check-modules.test b/test-data/unit/check-modules.test index 67767a9114e1..7319b45ae077 100644 --- a/test-data/unit/check-modules.test +++ b/test-data/unit/check-modules.test @@ -518,7 +518,7 @@ Both = Union[int, str] def foo(x: int, y: int = ...) -> int: ... @overload def foo(x: str, y: str = ...) -> str: ... -def foo(x: Both, y: Both = ...) -> Both: # E: Incompatible default for argument "y" (default has type "ellipsis", argument has type "Union[int, str]") +def foo(x: Both, y: Both = ...) -> Both: # E: Incompatible default for argument "y" (default has type "ellipsis", argument has type "Both") return x @overload diff --git a/test-data/unit/check-type-aliases.test b/test-data/unit/check-type-aliases.test index 111743e9235e..eef327d844b1 100644 --- a/test-data/unit/check-type-aliases.test +++ b/test-data/unit/check-type-aliases.test @@ -12,7 +12,7 @@ U = Union[int, str] def f(x: U) -> None: pass f(1) f('') -f(()) # E: Argument 1 to "f" has incompatible type "Tuple[]"; expected "Union[int, str]" +f(()) # E: Argument 1 to "f" has incompatible type "Tuple[]"; expected "U" [targets __main__, __main__.f] [builtins fixtures/tuple.pyi] @@ -21,7 +21,7 @@ from typing import Tuple T = Tuple[int, str] def f(x: T) -> None: pass f((1, 'x')) -f(1) # E: Argument 1 to "f" has incompatible type "int"; expected "Tuple[int, str]" +f(1) # E: Argument 1 to "f" has incompatible type "int"; expected "T" [targets __main__, __main__.f] [builtins fixtures/tuple.pyi] @@ -57,14 +57,14 @@ Never = NoReturn a: Never # Used to be an error here def f(a: Never): ... -f(5) # E: Argument 1 to "f" has incompatible type "int"; expected "NoReturn" +f(5) # E: Argument 1 to "f" has incompatible type "int"; expected "Never" [case testImportUnionAlias] import typing from _m import U def f(x: U) -> None: pass f(1) f('x') -f(()) # E: Argument 1 to "f" has incompatible type "Tuple[]"; expected "Union[int, str]" +f(()) # E: Argument 1 to "f" has incompatible type "Tuple[]"; expected "U" [file _m.py] from typing import Union U = Union[int, str] @@ -131,18 +131,18 @@ class X(Generic[T]): a: X[T] b: A = a c: A[T] = a - d: A[int] = a # E: Incompatible types in assignment (expression has type "X[T]", variable has type "X[int]") + d: A[int] = a # E: Incompatible types in assignment (expression has type "X[T]", variable has type "A[int]") def g(self) -> None: a: X[T] b: X.A = a c: X.A[T] = a - d: X.A[int] = a # E: Incompatible types in assignment (expression has type "X[T]", variable has type "X[int]") + d: X.A[int] = a # E: Incompatible types in assignment (expression has type "X[T]", variable has type "A[int]") def g(arg: X[int]) -> None: p: X[int] = arg.f() q: X.A = arg.f() - r: X.A[str] = arg.f() # E: Incompatible types in assignment (expression has type "X[int]", variable has type "X[str]") + r: X.A[str] = arg.f() # E: Incompatible types in assignment (expression has type "X[int]", variable has type "A[str]") [out] [case testProhibitBoundTypeVariableReuseForAliases] @@ -770,3 +770,71 @@ f(string, string) [builtins fixtures/tuple.pyi] [typing fixtures/typing-medium.pyi] + +[case testShowTypeAliasInErrorMessage] +from typing import Tuple + +Point = Tuple[float, ...] + +def dist(q: Point) -> None: + pass + +dist('abc') # E: Argument 1 to "dist" has incompatible type "str"; expected "Point" + +[builtins fixtures/tuple.pyi] + +# See https://github.com/python/mypy/issues/2968#issuecomment-1133768276 +# We need to disable the no_args optimization to get this to work +[case testSimpleAliasesShownInErrorMessages-xfail] +Alias = int + +a: Alias = 'a' # E: Incompatible types in assignment (expression has type "str", variable has type "Alias") + +def g(a: Alias) -> None: + pass + +g('b') # E: Argument 1 to "g" has incompatible type "str"; expected "Alias" +[case testOverlappingTypeAliasNamesDistinguishedInErrorMessage] +from other_file import f +from typing import List +Alias = List[str] + +a: Alias +f(a) # E: Argument 1 to "f" has incompatible type "__main__.Alias"; expected "other_file.Alias" + +[file other_file.py] +from typing import Union +Alias = Union[int, str] + +def f(x: Alias) -> None: + pass + +[builtins fixtures/tuple.pyi] + +[case testGenericAliasesShownInErrorMessages] +from typing import TypeVar, List + +T = TypeVar('T') + +Alias = List[T] + +def f(x: Alias[int]) -> None: + pass + +a: List[str] +f(a) # E: Argument 1 to "f" has incompatible type "List[str]"; expected "Alias[int]" + +b: Alias[str] +f(b) # E: Argument 1 to "f" has incompatible type "Alias[str]"; expected "Alias[int]" + +[case testGenericAliasWithArgumentConvertedToAny] +# flags: --ignore-missing-imports --disallow-any-unimported +from missing import Unchecked +from typing import List, TypeVar + +T = TypeVar('T') + +X = List[T] + +def f(x: X[Unchecked]) -> None: # E: Argument 1 to "f" becomes "X[Any]" due to an unfollowed import + pass diff --git a/test-data/unit/fine-grained.test b/test-data/unit/fine-grained.test index fa6dc52262dd..fd6bcfc84825 100644 --- a/test-data/unit/fine-grained.test +++ b/test-data/unit/fine-grained.test @@ -8650,7 +8650,7 @@ Alias = Literal[2] [out] == == -main:2: error: Incompatible types in assignment (expression has type "Literal[1]", variable has type "Literal[2]") +main:2: error: Incompatible types in assignment (expression has type "Literal[1]", variable has type "Alias") [case testLiteralFineGrainedOverload] from mod import foo @@ -8721,7 +8721,7 @@ Alias3 = Literal[4] [builtins fixtures/tuple.pyi] [out] == -main:5: error: Argument 1 to "expect_3" has incompatible type "Literal[4]"; expected "Literal[3]" +main:5: error: Argument 1 to "expect_3" has incompatible type "Alias1"; expected "Literal[3]" [case testLiteralFineGrainedChainedFunctionDefinitions] from mod1 import func1 diff --git a/test-data/unit/pythoneval.test b/test-data/unit/pythoneval.test index b59d50feb986..452267b2b977 100644 --- a/test-data/unit/pythoneval.test +++ b/test-data/unit/pythoneval.test @@ -1007,8 +1007,8 @@ re.subn(bpat, lambda m: b'', b'')[0] + b'' [out] _testReModuleBytes.py:7: error: No overload variant of "search" matches argument types "bytes", "str" _testReModuleBytes.py:7: note: Possible overload variants: -_testReModuleBytes.py:7: note: def search(pattern: Union[str, Pattern[str]], string: str, flags: Union[int, RegexFlag] = ...) -> Optional[Match[str]] -_testReModuleBytes.py:7: note: def search(pattern: Union[bytes, Pattern[bytes]], string: Union[bytes, Union[bytearray, memoryview, array[Any], mmap, _CData]], flags: Union[int, RegexFlag] = ...) -> Optional[Match[bytes]] +_testReModuleBytes.py:7: note: def search(pattern: Union[str, Pattern[str]], string: str, flags: _FlagsType = ...) -> Optional[Match[str]] +_testReModuleBytes.py:7: note: def search(pattern: Union[bytes, Pattern[bytes]], string: ReadableBuffer, flags: _FlagsType = ...) -> Optional[Match[bytes]] _testReModuleBytes.py:9: error: Argument 1 to "search" has incompatible type "Pattern[bytes]"; expected "Union[str, Pattern[str]]" [case testReModuleString] @@ -1034,8 +1034,8 @@ re.subn(spat, lambda m: '', '')[0] + '' [out] _testReModuleString.py:7: error: No overload variant of "search" matches argument types "str", "bytes" _testReModuleString.py:7: note: Possible overload variants: -_testReModuleString.py:7: note: def search(pattern: Union[str, Pattern[str]], string: str, flags: Union[int, RegexFlag] = ...) -> Optional[Match[str]] -_testReModuleString.py:7: note: def search(pattern: Union[bytes, Pattern[bytes]], string: Union[bytes, Union[bytearray, memoryview, array[Any], mmap, _CData]], flags: Union[int, RegexFlag] = ...) -> Optional[Match[bytes]] +_testReModuleString.py:7: note: def search(pattern: Union[str, Pattern[str]], string: str, flags: _FlagsType = ...) -> Optional[Match[str]] +_testReModuleString.py:7: note: def search(pattern: Union[bytes, Pattern[bytes]], string: ReadableBuffer, flags: _FlagsType = ...) -> Optional[Match[bytes]] _testReModuleString.py:9: error: Argument 1 to "search" has incompatible type "Pattern[str]"; expected "Union[bytes, Pattern[bytes]]" [case testListSetitemTuple]