diff --git a/mypy/checker.py b/mypy/checker.py index 179ff6e0b4b6..38976d4ce15e 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -2171,7 +2171,9 @@ def bind_and_map_method( def get_op_other_domain(self, tp: FunctionLike) -> Type | None: if isinstance(tp, CallableType): if tp.arg_kinds and tp.arg_kinds[0] == ARG_POS: - return tp.arg_types[0] + # For generic methods, domain comparison is tricky, as a first + # approximation erase all remaining type variables to bounds. + return erase_typevars(tp.arg_types[0], {v.id for v in tp.variables}) return None elif isinstance(tp, Overloaded): raw_items = [self.get_op_other_domain(it) for it in tp.items] diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py index f826d4c11dd3..8e6af0218c32 100644 --- a/mypy/checkexpr.py +++ b/mypy/checkexpr.py @@ -167,6 +167,7 @@ TypedDictType, TypeOfAny, TypeType, + TypeVarId, TypeVarLikeType, TypeVarTupleType, TypeVarType, @@ -4933,7 +4934,7 @@ def check_lst_expr(self, e: ListExpr | SetExpr | TupleExpr, fullname: str, tag: tv = TypeVarType( "T", "T", - id=-1, + id=TypeVarId(-1, namespace=""), values=[], upper_bound=self.object_type(), default=AnyType(TypeOfAny.from_omitted_generics), @@ -5164,7 +5165,7 @@ def visit_dict_expr(self, e: DictExpr) -> Type: kt = TypeVarType( "KT", "KT", - id=-1, + id=TypeVarId(-1, namespace=""), values=[], upper_bound=self.object_type(), default=AnyType(TypeOfAny.from_omitted_generics), @@ -5172,7 +5173,7 @@ def visit_dict_expr(self, e: DictExpr) -> Type: vt = TypeVarType( "VT", "VT", - id=-2, + id=TypeVarId(-2, namespace=""), values=[], upper_bound=self.object_type(), default=AnyType(TypeOfAny.from_omitted_generics), @@ -5564,7 +5565,7 @@ def check_generator_or_comprehension( tv = TypeVarType( "T", "T", - id=-1, + id=TypeVarId(-1, namespace=""), values=[], upper_bound=self.object_type(), default=AnyType(TypeOfAny.from_omitted_generics), @@ -5591,7 +5592,7 @@ def visit_dictionary_comprehension(self, e: DictionaryComprehension) -> Type: ktdef = TypeVarType( "KT", "KT", - id=-1, + id=TypeVarId(-1, namespace=""), values=[], upper_bound=self.object_type(), default=AnyType(TypeOfAny.from_omitted_generics), @@ -5599,7 +5600,7 @@ def visit_dictionary_comprehension(self, e: DictionaryComprehension) -> Type: vtdef = TypeVarType( "VT", "VT", - id=-2, + id=TypeVarId(-2, namespace=""), values=[], upper_bound=self.object_type(), default=AnyType(TypeOfAny.from_omitted_generics), diff --git a/mypy/expandtype.py b/mypy/expandtype.py index f7fa0258f588..86875bc6079a 100644 --- a/mypy/expandtype.py +++ b/mypy/expandtype.py @@ -316,7 +316,7 @@ def interpolate_args_for_unpack(self, t: CallableType, var_arg: UnpackType) -> l new_unpack: Type if isinstance(var_arg_type, Instance): # we have something like Unpack[Tuple[Any, ...]] - new_unpack = var_arg + new_unpack = UnpackType(var_arg.type.accept(self)) elif isinstance(var_arg_type, TupleType): # We have something like Unpack[Tuple[Unpack[Ts], X1, X2]] expanded_tuple = var_arg_type.accept(self) diff --git a/mypy/join.py b/mypy/join.py index c711697ec46d..5284be7dd2a1 100644 --- a/mypy/join.py +++ b/mypy/join.py @@ -5,6 +5,7 @@ from typing import Sequence, overload import mypy.typeops +from mypy.expandtype import expand_type from mypy.maptype import map_instance_to_supertype from mypy.nodes import CONTRAVARIANT, COVARIANT, INVARIANT, VARIANCE_NOT_READY from mypy.state import state @@ -36,6 +37,7 @@ TypedDictType, TypeOfAny, TypeType, + TypeVarId, TypeVarLikeType, TypeVarTupleType, TypeVarType, @@ -718,7 +720,35 @@ def is_similar_callables(t: CallableType, s: CallableType) -> bool: ) +def update_callable_ids(c: CallableType, ids: list[TypeVarId]) -> CallableType: + tv_map = {} + tvs = [] + for tv, new_id in zip(c.variables, ids): + new_tv = tv.copy_modified(id=new_id) + tvs.append(new_tv) + tv_map[tv.id] = new_tv + return expand_type(c, tv_map).copy_modified(variables=tvs) + + +def match_generic_callables(t: CallableType, s: CallableType) -> tuple[CallableType, CallableType]: + # The case where we combine/join/meet similar callables, situation where both are generic + # requires special care. A more principled solution may involve unify_generic_callable(), + # but it would have two problems: + # * This adds risk of infinite recursion: e.g. join -> unification -> solver -> join + # * Using unification is an incorrect thing for meets, as it "widens" the types + # Finally, this effectively falls back to an old behaviour before namespaces were added to + # type variables, and it worked relatively well. + max_len = max(len(t.variables), len(s.variables)) + min_len = min(len(t.variables), len(s.variables)) + if min_len == 0: + return t, s + new_ids = [TypeVarId.new(meta_level=0) for _ in range(max_len)] + # Note: this relies on variables being in order they appear in function definition. + return update_callable_ids(t, new_ids), update_callable_ids(s, new_ids) + + def join_similar_callables(t: CallableType, s: CallableType) -> CallableType: + t, s = match_generic_callables(t, s) arg_types: list[Type] = [] for i in range(len(t.arg_types)): arg_types.append(safe_meet(t.arg_types[i], s.arg_types[i])) @@ -771,6 +801,7 @@ def safe_meet(t: Type, s: Type) -> Type: def combine_similar_callables(t: CallableType, s: CallableType) -> CallableType: + t, s = match_generic_callables(t, s) arg_types: list[Type] = [] for i in range(len(t.arg_types)): arg_types.append(safe_join(t.arg_types[i], s.arg_types[i])) diff --git a/mypy/meet.py b/mypy/meet.py index df8b960cdf3f..2d44cafb23b3 100644 --- a/mypy/meet.py +++ b/mypy/meet.py @@ -1024,8 +1024,9 @@ def default(self, typ: Type) -> ProperType: def meet_similar_callables(t: CallableType, s: CallableType) -> CallableType: - from mypy.join import safe_join + from mypy.join import match_generic_callables, safe_join + t, s = match_generic_callables(t, s) arg_types: list[Type] = [] for i in range(len(t.arg_types)): arg_types.append(safe_join(t.arg_types[i], s.arg_types[i])) diff --git a/mypy/messages.py b/mypy/messages.py index de079feda048..f01b0a726584 100644 --- a/mypy/messages.py +++ b/mypy/messages.py @@ -83,6 +83,7 @@ TypeOfAny, TypeStrVisitor, TypeType, + TypeVarLikeType, TypeVarTupleType, TypeVarType, UnboundType, @@ -2502,14 +2503,16 @@ def format_literal_value(typ: LiteralType) -> str: return typ.value_repr() if isinstance(typ, TypeAliasType) and typ.is_recursive: - # TODO: find balance here, str(typ) doesn't support custom verbosity, and may be - # too verbose for user messages, OTOH it nicely shows structure of recursive types. - if verbosity < 2: - type_str = typ.alias.name if typ.alias else "" + if typ.alias is None: + type_str = "" + else: + if verbosity >= 2 or (fullnames and typ.alias.fullname in fullnames): + type_str = typ.alias.fullname + else: + type_str = typ.alias.name if typ.args: type_str += f"[{format_list(typ.args)}]" - return type_str - return str(typ) + return type_str # TODO: always mention type alias names in errors. typ = get_proper_type(typ) @@ -2550,9 +2553,15 @@ def format_literal_value(typ: LiteralType) -> str: return f"Unpack[{format(typ.type)}]" elif isinstance(typ, TypeVarType): # This is similar to non-generic instance types. + fullname = scoped_type_var_name(typ) + if verbosity >= 2 or (fullnames and fullname in fullnames): + return fullname return typ.name elif isinstance(typ, TypeVarTupleType): # This is similar to non-generic instance types. + fullname = scoped_type_var_name(typ) + if verbosity >= 2 or (fullnames and fullname in fullnames): + return fullname return typ.name elif isinstance(typ, ParamSpecType): # Concatenate[..., P] @@ -2563,6 +2572,7 @@ def format_literal_value(typ: LiteralType) -> str: return f"[{args}, **{typ.name_with_suffix()}]" else: + # TODO: better disambiguate ParamSpec name clashes. return typ.name_with_suffix() elif isinstance(typ, TupleType): # Prefer the name of the fallback class (if not tuple), as it's more informative. @@ -2680,29 +2690,51 @@ 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_named_types(t: Type) -> list[Type]: + """Return all instances/aliases/type variables that `t` contains (including `t`). This is similar to collect_all_inner_types from typeanal but only returns instances and will recurse into fallbacks. """ - visitor = CollectAllInstancesQuery() + visitor = CollectAllNamedTypesQuery() t.accept(visitor) - return visitor.instances + return visitor.types -class CollectAllInstancesQuery(TypeTraverserVisitor): +class CollectAllNamedTypesQuery(TypeTraverserVisitor): def __init__(self) -> None: - self.instances: list[Instance] = [] + self.types: list[Type] = [] def visit_instance(self, t: Instance) -> None: - self.instances.append(t) + self.types.append(t) super().visit_instance(t) def visit_type_alias_type(self, t: TypeAliasType) -> None: if t.alias and not t.is_recursive: - t.alias.target.accept(self) - super().visit_type_alias_type(t) + get_proper_type(t).accept(self) + else: + self.types.append(t) + super().visit_type_alias_type(t) + + def visit_type_var(self, t: TypeVarType) -> None: + self.types.append(t) + super().visit_type_var(t) + + def visit_type_var_tuple(self, t: TypeVarTupleType) -> None: + self.types.append(t) + super().visit_type_var_tuple(t) + + def visit_param_spec(self, t: ParamSpecType) -> None: + self.types.append(t) + super().visit_param_spec(t) + + +def scoped_type_var_name(t: TypeVarLikeType) -> str: + if not t.id.namespace: + return t.name + # TODO: support rare cases when both TypeVar name and namespace suffix coincide. + *_, suffix = t.id.namespace.split(".") + return f"{t.name}@{suffix}" def find_type_overlaps(*types: Type) -> set[str]: @@ -2713,8 +2745,14 @@ 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 t in collect_all_named_types(type): + if isinstance(t, ProperType) and isinstance(t, Instance): + d.setdefault(t.type.name, set()).add(t.type.fullname) + elif isinstance(t, TypeAliasType) and t.alias: + d.setdefault(t.alias.name, set()).add(t.alias.fullname) + else: + assert isinstance(t, TypeVarLikeType) + d.setdefault(t.name, set()).add(scoped_type_var_name(t)) for shortname in d.keys(): if f"typing.{shortname}" in TYPES_FOR_UNIMPORTED_HINTS: d[shortname].add(f"typing.{shortname}") @@ -2732,7 +2770,7 @@ def format_type( """ Convert a type to a relatively short string suitable for error messages. - `verbosity` is a coarse grained control on the verbosity of the type + `verbosity` is a coarse-grained control on the verbosity of the type This function returns a string appropriate for unmodified use in error messages; this means that it will be quoted in most cases. If @@ -2748,7 +2786,7 @@ def format_type_bare( """ Convert a type to a relatively short string suitable for error messages. - `verbosity` is a coarse grained control on the verbosity of the type + `verbosity` is a coarse-grained control on the verbosity of the type `fullnames` specifies a set of names that should be printed in full This function will return an unquoted string. If a caller doesn't need to diff --git a/mypy/plugins/attrs.py b/mypy/plugins/attrs.py index 83f685f57a16..db976385ee56 100644 --- a/mypy/plugins/attrs.py +++ b/mypy/plugins/attrs.py @@ -69,6 +69,7 @@ Type, TypeOfAny, TypeType, + TypeVarId, TypeVarType, UninhabitedType, UnionType, @@ -807,25 +808,25 @@ def _add_order(ctx: mypy.plugin.ClassDefContext, adder: MethodAdder) -> None: # AT = TypeVar('AT') # def __lt__(self: AT, other: AT) -> bool # This way comparisons with subclasses will work correctly. + fullname = f"{ctx.cls.info.fullname}.{SELF_TVAR_NAME}" tvd = TypeVarType( SELF_TVAR_NAME, - ctx.cls.info.fullname + "." + SELF_TVAR_NAME, - id=-1, + fullname, + # Namespace is patched per-method below. + id=TypeVarId(-1, namespace=""), values=[], upper_bound=object_type, default=AnyType(TypeOfAny.from_omitted_generics), ) self_tvar_expr = TypeVarExpr( - SELF_TVAR_NAME, - ctx.cls.info.fullname + "." + SELF_TVAR_NAME, - [], - object_type, - AnyType(TypeOfAny.from_omitted_generics), + SELF_TVAR_NAME, fullname, [], object_type, AnyType(TypeOfAny.from_omitted_generics) ) ctx.cls.info.names[SELF_TVAR_NAME] = SymbolTableNode(MDEF, self_tvar_expr) - args = [Argument(Var("other", tvd), tvd, None, ARG_POS)] for method in ["__lt__", "__le__", "__gt__", "__ge__"]: + namespace = f"{ctx.cls.info.fullname}.{method}" + tvd = tvd.copy_modified(id=TypeVarId(tvd.id.raw_id, namespace=namespace)) + args = [Argument(Var("other", tvd), tvd, None, ARG_POS)] adder.add_method(method, args, bool_type, self_type=tvd, tvd=tvd) diff --git a/mypy/plugins/dataclasses.py b/mypy/plugins/dataclasses.py index dead512a2202..dd2eceab217f 100644 --- a/mypy/plugins/dataclasses.py +++ b/mypy/plugins/dataclasses.py @@ -65,6 +65,7 @@ TupleType, Type, TypeOfAny, + TypeVarId, TypeVarType, UninhabitedType, UnionType, @@ -314,8 +315,8 @@ def transform(self) -> bool: obj_type = self._api.named_type("builtins.object") order_tvar_def = TypeVarType( SELF_TVAR_NAME, - info.fullname + "." + SELF_TVAR_NAME, - id=-1, + f"{info.fullname}.{SELF_TVAR_NAME}", + id=TypeVarId(-1, namespace=f"{info.fullname}.{method_name}"), values=[], upper_bound=obj_type, default=AnyType(TypeOfAny.from_omitted_generics), diff --git a/mypy/plugins/functools.py b/mypy/plugins/functools.py index 81a3b4d96ef3..335123a4a108 100644 --- a/mypy/plugins/functools.py +++ b/mypy/plugins/functools.py @@ -7,7 +7,7 @@ import mypy.checker import mypy.plugin from mypy.argmap import map_actuals_to_formals -from mypy.nodes import ARG_POS, ARG_STAR2, ArgKind, Argument, FuncItem, Var +from mypy.nodes import ARG_POS, ARG_STAR2, ArgKind, Argument, CallExpr, FuncItem, Var from mypy.plugins.common import add_method_to_class from mypy.types import ( AnyType, @@ -151,12 +151,22 @@ def partial_new_callback(ctx: mypy.plugin.FunctionContext) -> Type: actual_arg_names = [a for param in ctx.arg_names[1:] for a in param] actual_types = [a for param in ctx.arg_types[1:] for a in param] + # Create a valid context for various ad-hoc inspections in check_call(). + call_expr = CallExpr( + callee=ctx.args[0][0], + args=actual_args, + arg_kinds=actual_arg_kinds, + arg_names=actual_arg_names, + analyzed=ctx.context.analyzed if isinstance(ctx.context, CallExpr) else None, + ) + call_expr.set_line(ctx.context) + _, bound = ctx.api.expr_checker.check_call( callee=defaulted, args=actual_args, arg_kinds=actual_arg_kinds, arg_names=actual_arg_names, - context=defaulted, + context=call_expr, ) bound = get_proper_type(bound) if not isinstance(bound, CallableType): diff --git a/mypy/semanal.py b/mypy/semanal.py index 2448ea8485f7..8505b3a9ccac 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -279,6 +279,7 @@ TypedDictType, TypeOfAny, TypeType, + TypeVarId, TypeVarLikeType, TypeVarTupleType, TypeVarType, @@ -894,7 +895,7 @@ def analyze_func_def(self, defn: FuncDef) -> None: self.prepare_method_signature(defn, self.type, has_self_type) # Analyze function signature - with self.tvar_scope_frame(self.tvar_scope.method_frame()): + with self.tvar_scope_frame(self.tvar_scope.method_frame(defn.fullname)): if defn.type: self.check_classvar_in_signature(defn.type) assert isinstance(defn.type, CallableType) @@ -902,7 +903,9 @@ def analyze_func_def(self, defn: FuncDef) -> None: # class-level imported names and type variables are in scope. analyzer = self.type_analyzer() tag = self.track_incomplete_refs() - result = analyzer.visit_callable_type(defn.type, nested=False) + result = analyzer.visit_callable_type( + defn.type, nested=False, namespace=defn.fullname + ) # Don't store not ready types (including placeholders). if self.found_incomplete_ref(tag) or has_placeholder(result): self.defer(defn) @@ -1114,7 +1117,7 @@ def update_function_type_variables(self, fun_type: CallableType, defn: FuncItem) if defn is generic. Return True, if the signature contains typing.Self type, or False otherwise. """ - with self.tvar_scope_frame(self.tvar_scope.method_frame()): + with self.tvar_scope_frame(self.tvar_scope.method_frame(defn.fullname)): a = self.type_analyzer() fun_type.variables, has_self_type = a.bind_function_type_variables(fun_type, defn) if has_self_type and self.type is not None: @@ -1152,7 +1155,7 @@ def setup_self_type(self) -> None: info.self_type = TypeVarType( "Self", f"{info.fullname}.Self", - id=0, + id=TypeVarId(0), # 0 is a special value for self-types. values=[], upper_bound=fill_typevars(info), default=AnyType(TypeOfAny.from_omitted_generics), @@ -1441,7 +1444,7 @@ def add_function_to_symbol_table(self, func: FuncDef | OverloadedFuncDef) -> Non self.add_symbol(func.name, func, func) def analyze_arg_initializers(self, defn: FuncItem) -> None: - with self.tvar_scope_frame(self.tvar_scope.method_frame()): + with self.tvar_scope_frame(self.tvar_scope.method_frame(defn.fullname)): # Analyze default arguments for arg in defn.arguments: if arg.initializer: @@ -1449,7 +1452,7 @@ def analyze_arg_initializers(self, defn: FuncItem) -> None: def analyze_function_body(self, defn: FuncItem) -> None: is_method = self.is_class_scope() - with self.tvar_scope_frame(self.tvar_scope.method_frame()): + with self.tvar_scope_frame(self.tvar_scope.method_frame(defn.fullname)): # Bind the type variables again to visit the body. if defn.type: a = self.type_analyzer() @@ -3930,7 +3933,9 @@ def check_and_set_up_type_alias(self, s: AssignmentStmt) -> bool: or (self.options.python_version >= (3, 10) or self.is_stub_file) ): # Note: CallExpr is for "void = type(None)" and OpExpr is for "X | Y" union syntax. - s.rvalue.analyzed = TypeAliasExpr(alias_node) + if not isinstance(s.rvalue.analyzed, TypeAliasExpr): + # Any existing node will be updated in-place below. + s.rvalue.analyzed = TypeAliasExpr(alias_node) s.rvalue.analyzed.line = s.line # we use the column from resulting target, to get better location for errors s.rvalue.analyzed.column = res.column diff --git a/mypy/semanal_namedtuple.py b/mypy/semanal_namedtuple.py index 753deafe103b..768dd265b338 100644 --- a/mypy/semanal_namedtuple.py +++ b/mypy/semanal_namedtuple.py @@ -62,6 +62,7 @@ Type, TypeOfAny, TypeType, + TypeVarId, TypeVarLikeType, TypeVarType, UnboundType, @@ -569,27 +570,33 @@ def add_field( add_field(Var("__match_args__", match_args_type), is_initialized_in_class=True) assert info.tuple_type is not None # Set by update_tuple_type() above. - tvd = TypeVarType( + shared_self_type = TypeVarType( name=SELF_TVAR_NAME, - fullname=info.fullname + "." + SELF_TVAR_NAME, + fullname=f"{info.fullname}.{SELF_TVAR_NAME}", + # Namespace is patched per-method below. id=self.api.tvar_scope.new_unique_func_id(), values=[], upper_bound=info.tuple_type, default=AnyType(TypeOfAny.from_omitted_generics), ) - selftype = tvd def add_method( funcname: str, - ret: Type, + ret: Type | None, # None means use (patched) self-type args: list[Argument], is_classmethod: bool = False, is_new: bool = False, ) -> None: + fullname = f"{info.fullname}.{funcname}" + self_type = shared_self_type.copy_modified( + id=TypeVarId(shared_self_type.id.raw_id, namespace=fullname) + ) + if ret is None: + ret = self_type if is_classmethod or is_new: - first = [Argument(Var("_cls"), TypeType.make_normalized(selftype), None, ARG_POS)] + first = [Argument(Var("_cls"), TypeType.make_normalized(self_type), None, ARG_POS)] else: - first = [Argument(Var("_self"), selftype, None, ARG_POS)] + first = [Argument(Var("_self"), self_type, None, ARG_POS)] args = first + args types = [arg.type_annotation for arg in args] @@ -597,12 +604,12 @@ def add_method( arg_kinds = [arg.kind for arg in args] assert None not in types signature = CallableType(cast(List[Type], types), arg_kinds, items, ret, function_type) - signature.variables = [tvd] + signature.variables = [self_type] func = FuncDef(funcname, args, Block([])) func.info = info func.is_class = is_classmethod func.type = set_callable_name(signature, func) - func._fullname = info.fullname + "." + funcname + func._fullname = fullname func.line = line if is_classmethod: v = Var(funcname, func.type) @@ -620,13 +627,13 @@ def add_method( add_method( "_replace", - ret=selftype, + ret=None, args=[Argument(var, var.type, EllipsisExpr(), ARG_NAMED_OPT) for var in vars], ) if self.options.python_version >= (3, 13): add_method( "__replace__", - ret=selftype, + ret=None, args=[Argument(var, var.type, EllipsisExpr(), ARG_NAMED_OPT) for var in vars], ) @@ -635,11 +642,11 @@ def make_init_arg(var: Var) -> Argument: kind = ARG_POS if default is None else ARG_OPT return Argument(var, var.type, default, kind) - add_method("__new__", ret=selftype, args=[make_init_arg(var) for var in vars], is_new=True) + add_method("__new__", ret=None, args=[make_init_arg(var) for var in vars], is_new=True) add_method("_asdict", args=[], ret=ordereddictype) add_method( "_make", - ret=selftype, + ret=None, is_classmethod=True, args=[Argument(Var("iterable", iterable_type), iterable_type, None, ARG_POS)], ) diff --git a/mypy/semanal_shared.py b/mypy/semanal_shared.py index b5ec2bb52a0d..01d8e9aafffb 100644 --- a/mypy/semanal_shared.py +++ b/mypy/semanal_shared.py @@ -314,7 +314,7 @@ def __call__(self, fully_qualified_name: str, args: list[Type] | None = None) -> def paramspec_args( name: str, fullname: str, - id: TypeVarId | int, + id: TypeVarId, *, named_type_func: _NamedTypeCallback, line: int = -1, @@ -337,7 +337,7 @@ def paramspec_args( def paramspec_kwargs( name: str, fullname: str, - id: TypeVarId | int, + id: TypeVarId, *, named_type_func: _NamedTypeCallback, line: int = -1, diff --git a/mypy/subtypes.py b/mypy/subtypes.py index a5523fbe0d45..971caa3991ae 100644 --- a/mypy/subtypes.py +++ b/mypy/subtypes.py @@ -944,7 +944,7 @@ def visit_overloaded(self, left: Overloaded) -> bool: # When it is the same overload, then the types are equal. return True - # Ensure each overload in the right side (the supertype) is accounted for. + # Ensure each overload on the right side (the supertype) is accounted for. previous_match_left_index = -1 matched_overloads = set() @@ -1792,7 +1792,9 @@ def are_args_compatible( # If both arguments are required allow_partial_overlap has no effect. allow_partial_overlap = False - def is_different(left_item: object | None, right_item: object | None) -> bool: + def is_different( + left_item: object | None, right_item: object | None, allow_overlap: bool + ) -> bool: """Checks if the left and right items are different. If the right item is unspecified (e.g. if the right callable doesn't care @@ -1802,19 +1804,21 @@ def is_different(left_item: object | None, right_item: object | None) -> bool: if the left callable also doesn't care.""" if right_item is None: return False - if allow_partial_overlap and left_item is None: + if allow_overlap and left_item is None: return False return left_item != right_item # If right has a specific name it wants this argument to be, left must # have the same. - if is_different(left.name, right.name): + if is_different(left.name, right.name, allow_partial_overlap): # But pay attention to whether we're ignoring positional arg names if not ignore_pos_arg_names or right.pos is None: return False - # If right is at a specific position, left must have the same: - if is_different(left.pos, right.pos) and not allow_imprecise_kinds: + # If right is at a specific position, left must have the same. + # TODO: partial overlap logic is flawed for positions. + # We disable it to avoid false positives at a cost of few false negatives. + if is_different(left.pos, right.pos, allow_overlap=False) and not allow_imprecise_kinds: return False # If right's argument is optional, left's must also be diff --git a/mypy/test/testtypes.py b/mypy/test/testtypes.py index b3f84905c47e..0218d33cc124 100644 --- a/mypy/test/testtypes.py +++ b/mypy/test/testtypes.py @@ -144,7 +144,11 @@ def test_tuple_type_upper(self) -> None: def test_type_variable_binding(self) -> None: assert_equal( - str(TypeVarType("X", "X", 1, [], self.fx.o, AnyType(TypeOfAny.from_omitted_generics))), + str( + TypeVarType( + "X", "X", TypeVarId(1), [], self.fx.o, AnyType(TypeOfAny.from_omitted_generics) + ) + ), "X`1", ) assert_equal( @@ -152,7 +156,7 @@ def test_type_variable_binding(self) -> None: TypeVarType( "X", "X", - 1, + TypeVarId(1), [self.x, self.y], self.fx.o, AnyType(TypeOfAny.from_omitted_generics), @@ -170,14 +174,25 @@ def test_generic_function_type(self) -> None: self.function, name=None, variables=[ - TypeVarType("X", "X", -1, [], self.fx.o, AnyType(TypeOfAny.from_omitted_generics)) + TypeVarType( + "X", + "X", + TypeVarId(-1), + [], + self.fx.o, + AnyType(TypeOfAny.from_omitted_generics), + ) ], ) assert_equal(str(c), "def [X] (X?, Y?) -> Y?") v = [ - TypeVarType("Y", "Y", -1, [], self.fx.o, AnyType(TypeOfAny.from_omitted_generics)), - TypeVarType("X", "X", -2, [], self.fx.o, AnyType(TypeOfAny.from_omitted_generics)), + TypeVarType( + "Y", "Y", TypeVarId(-1), [], self.fx.o, AnyType(TypeOfAny.from_omitted_generics) + ), + TypeVarType( + "X", "X", TypeVarId(-2), [], self.fx.o, AnyType(TypeOfAny.from_omitted_generics) + ), ] c2 = CallableType([], [], [], NoneType(), self.function, name=None, variables=v) assert_equal(str(c2), "def [Y, X] ()") @@ -205,7 +220,9 @@ def test_type_alias_expand_all(self) -> None: def test_recursive_nested_in_non_recursive(self) -> None: A, _ = self.fx.def_alias_1(self.fx.a) - T = TypeVarType("T", "T", -1, [], self.fx.o, AnyType(TypeOfAny.from_omitted_generics)) + T = TypeVarType( + "T", "T", TypeVarId(-1), [], self.fx.o, AnyType(TypeOfAny.from_omitted_generics) + ) NA = self.fx.non_rec_alias(Instance(self.fx.gi, [T]), [T], [A]) assert not NA.is_recursive assert has_recursive_types(NA) @@ -657,7 +674,9 @@ def callable(self, vars: list[str], *a: Type) -> CallableType: n = -1 for v in vars: tv.append( - TypeVarType(v, v, n, [], self.fx.o, AnyType(TypeOfAny.from_omitted_generics)) + TypeVarType( + v, v, TypeVarId(n), [], self.fx.o, AnyType(TypeOfAny.from_omitted_generics) + ) ) n -= 1 return CallableType( diff --git a/mypy/test/typefixture.py b/mypy/test/typefixture.py index b7bde16e6be2..5a813f70117c 100644 --- a/mypy/test/typefixture.py +++ b/mypy/test/typefixture.py @@ -30,6 +30,7 @@ TypeAliasType, TypeOfAny, TypeType, + TypeVarId, TypeVarLikeType, TypeVarTupleType, TypeVarType, @@ -57,7 +58,7 @@ def make_type_var( return TypeVarType( name, name, - id, + TypeVarId(id), values, upper_bound, AnyType(TypeOfAny.from_omitted_generics), @@ -227,7 +228,7 @@ def make_type_var_tuple(name: str, id: int, upper_bound: Type) -> TypeVarTupleTy return TypeVarTupleType( name, name, - id, + TypeVarId(id), upper_bound, self.std_tuple, AnyType(TypeOfAny.from_omitted_generics), @@ -325,7 +326,7 @@ def make_type_info( TypeVarTupleType( n, n, - id, + TypeVarId(id), self.std_tuple.copy_modified(args=[self.o]), self.std_tuple.copy_modified(args=[self.o]), AnyType(TypeOfAny.from_omitted_generics), @@ -340,7 +341,7 @@ def make_type_info( TypeVarType( n, n, - id, + TypeVarId(id), [], self.o, AnyType(TypeOfAny.from_omitted_generics), diff --git a/mypy/tvar_scope.py b/mypy/tvar_scope.py index 4dc663df0399..fe97a8359287 100644 --- a/mypy/tvar_scope.py +++ b/mypy/tvar_scope.py @@ -85,29 +85,27 @@ def allow_binding(self, fullname: str) -> bool: return False return True - def method_frame(self) -> TypeVarLikeScope: + def method_frame(self, namespace: str) -> TypeVarLikeScope: """A new scope frame for binding a method""" - return TypeVarLikeScope(self, False, None) + return TypeVarLikeScope(self, False, None, namespace=namespace) def class_frame(self, namespace: str) -> TypeVarLikeScope: """A new scope frame for binding a class. Prohibits *this* class's tvars""" return TypeVarLikeScope(self.get_function_scope(), True, self, namespace=namespace) - def new_unique_func_id(self) -> int: + def new_unique_func_id(self) -> TypeVarId: """Used by plugin-like code that needs to make synthetic generic functions.""" self.func_id -= 1 - return self.func_id + return TypeVarId(self.func_id) def bind_new(self, name: str, tvar_expr: TypeVarLikeExpr) -> TypeVarLikeType: if self.is_class_scope: self.class_id += 1 i = self.class_id - namespace = self.namespace else: self.func_id -= 1 i = self.func_id - # TODO: Consider also using namespaces for functions - namespace = "" + namespace = self.namespace tvar_expr.default.accept(TypeVarLikeNamespaceSetter(namespace)) if isinstance(tvar_expr, TypeVarExpr): @@ -124,9 +122,9 @@ def bind_new(self, name: str, tvar_expr: TypeVarLikeExpr) -> TypeVarLikeType: ) elif isinstance(tvar_expr, ParamSpecExpr): tvar_def = ParamSpecType( - name, - tvar_expr.fullname, - i, + name=name, + fullname=tvar_expr.fullname, + id=TypeVarId(i, namespace=namespace), flavor=ParamSpecFlavor.BARE, upper_bound=tvar_expr.upper_bound, default=tvar_expr.default, @@ -135,9 +133,9 @@ def bind_new(self, name: str, tvar_expr: TypeVarLikeExpr) -> TypeVarLikeType: ) elif isinstance(tvar_expr, TypeVarTupleExpr): tvar_def = TypeVarTupleType( - name, - tvar_expr.fullname, - i, + name=name, + fullname=tvar_expr.fullname, + id=TypeVarId(i, namespace=namespace), upper_bound=tvar_expr.upper_bound, tuple_fallback=tvar_expr.tuple_fallback, default=tvar_expr.default, diff --git a/mypy/typeanal.py b/mypy/typeanal.py index ded8b8412a9a..a513b0716a01 100644 --- a/mypy/typeanal.py +++ b/mypy/typeanal.py @@ -1027,9 +1027,12 @@ def visit_unpack_type(self, t: UnpackType) -> Type: def visit_parameters(self, t: Parameters) -> Type: raise NotImplementedError("ParamSpec literals cannot have unbound TypeVars") - def visit_callable_type(self, t: CallableType, nested: bool = True) -> Type: + def visit_callable_type( + self, t: CallableType, nested: bool = True, namespace: str = "" + ) -> Type: # Every Callable can bind its own type variables, if they're not in the outer scope - with self.tvar_scope_frame(): + # TODO: attach namespace for nested free type variables (these appear in return type only). + with self.tvar_scope_frame(namespace=namespace): unpacked_kwargs = t.unpack_kwargs if self.defining_alias: variables = t.variables @@ -1432,7 +1435,7 @@ def analyze_callable_type(self, t: UnboundType) -> Type: ) else: # Callable[P, RET] (where P is ParamSpec) - with self.tvar_scope_frame(): + with self.tvar_scope_frame(namespace=""): # Temporarily bind ParamSpecs to allow code like this: # my_fun: Callable[Q, Foo[Q]] # We usually do this later in visit_callable_type(), but the analysis @@ -1648,9 +1651,9 @@ def note(self, msg: str, ctx: Context, *, code: ErrorCode | None = None) -> None self.note_func(msg, ctx, code=code) @contextmanager - def tvar_scope_frame(self) -> Iterator[None]: + def tvar_scope_frame(self, namespace: str) -> Iterator[None]: old_scope = self.tvar_scope - self.tvar_scope = self.tvar_scope.method_frame() + self.tvar_scope = self.tvar_scope.method_frame(namespace) yield self.tvar_scope = old_scope @@ -1795,7 +1798,7 @@ def anal_var_def(self, var_def: TypeVarLikeType) -> TypeVarLikeType: return TypeVarType( name=var_def.name, fullname=var_def.fullname, - id=var_def.id.raw_id, + id=var_def.id, values=self.anal_array(var_def.values), upper_bound=var_def.upper_bound.accept(self), default=var_def.default.accept(self), diff --git a/mypy/types.py b/mypy/types.py index 2cacc3e44085..cdcb26f435b8 100644 --- a/mypy/types.py +++ b/mypy/types.py @@ -510,9 +510,8 @@ class TypeVarId: # Class variable used for allocating fresh ids for metavariables. next_raw_id: ClassVar[int] = 1 - # Fullname of class (or potentially function in the future) which - # declares this type variable (not the fullname of the TypeVar - # definition!), or '' + # Fullname of class or function/method which declares this type + # variable (not the fullname of the TypeVar definition!), or '' namespace: str def __init__(self, raw_id: int, meta_level: int = 0, *, namespace: str = "") -> None: @@ -560,7 +559,7 @@ def __init__( self, name: str, fullname: str, - id: TypeVarId | int, + id: TypeVarId, upper_bound: Type, default: Type, line: int = -1, @@ -569,8 +568,6 @@ def __init__( super().__init__(line, column) self.name = name self.fullname = fullname - if isinstance(id, int): - id = TypeVarId(id) self.id = id self.upper_bound = upper_bound self.default = default @@ -607,7 +604,7 @@ def __init__( self, name: str, fullname: str, - id: TypeVarId | int, + id: TypeVarId, values: list[Type], upper_bound: Type, default: Type, @@ -626,7 +623,7 @@ def copy_modified( values: Bogus[list[Type]] = _dummy, upper_bound: Bogus[Type] = _dummy, default: Bogus[Type] = _dummy, - id: Bogus[TypeVarId | int] = _dummy, + id: Bogus[TypeVarId] = _dummy, line: int = _dummy_int, column: int = _dummy_int, **kwargs: Any, @@ -722,7 +719,7 @@ def __init__( self, name: str, fullname: str, - id: TypeVarId | int, + id: TypeVarId, flavor: int, upper_bound: Type, default: Type, @@ -749,7 +746,7 @@ def with_flavor(self, flavor: int) -> ParamSpecType: def copy_modified( self, *, - id: Bogus[TypeVarId | int] = _dummy, + id: Bogus[TypeVarId] = _dummy, flavor: int = _dummy_int, prefix: Bogus[Parameters] = _dummy, default: Bogus[Type] = _dummy, @@ -794,6 +791,7 @@ def serialize(self) -> JsonDict: "name": self.name, "fullname": self.fullname, "id": self.id.raw_id, + "namespace": self.id.namespace, "flavor": self.flavor, "upper_bound": self.upper_bound.serialize(), "default": self.default.serialize(), @@ -806,7 +804,7 @@ def deserialize(cls, data: JsonDict) -> ParamSpecType: return ParamSpecType( data["name"], data["fullname"], - data["id"], + TypeVarId(data["id"], namespace=data["namespace"]), data["flavor"], deserialize_type(data["upper_bound"]), deserialize_type(data["default"]), @@ -826,7 +824,7 @@ def __init__( self, name: str, fullname: str, - id: TypeVarId | int, + id: TypeVarId, upper_bound: Type, tuple_fallback: Instance, default: Type, @@ -848,6 +846,7 @@ def serialize(self) -> JsonDict: "name": self.name, "fullname": self.fullname, "id": self.id.raw_id, + "namespace": self.id.namespace, "upper_bound": self.upper_bound.serialize(), "tuple_fallback": self.tuple_fallback.serialize(), "default": self.default.serialize(), @@ -860,7 +859,7 @@ def deserialize(cls, data: JsonDict) -> TypeVarTupleType: return TypeVarTupleType( data["name"], data["fullname"], - data["id"], + TypeVarId(data["id"], namespace=data["namespace"]), deserialize_type(data["upper_bound"]), Instance.deserialize(data["tuple_fallback"]), deserialize_type(data["default"]), @@ -881,7 +880,7 @@ def __eq__(self, other: object) -> bool: def copy_modified( self, *, - id: Bogus[TypeVarId | int] = _dummy, + id: Bogus[TypeVarId] = _dummy, upper_bound: Bogus[Type] = _dummy, default: Bogus[Type] = _dummy, min_len: Bogus[int] = _dummy, @@ -3499,6 +3498,11 @@ def visit_instance(self, typ: Instance) -> None: typ.column = self.column super().visit_instance(typ) + def visit_type_alias_type(self, typ: TypeAliasType) -> None: + typ.line = self.line + typ.column = self.column + super().visit_type_alias_type(typ) + class HasTypeVars(BoolTypeQuery): def __init__(self) -> None: diff --git a/mypyc/test-data/fixtures/testutil.py b/mypyc/test-data/fixtures/testutil.py index 7f00ee5aea00..f210faf71109 100644 --- a/mypyc/test-data/fixtures/testutil.py +++ b/mypyc/test-data/fixtures/testutil.py @@ -5,7 +5,7 @@ import math from typing import ( Any, Iterator, TypeVar, Generator, Optional, List, Tuple, Sequence, - Union, Callable, Awaitable, + Union, Callable, Awaitable, Generic ) from typing import Final @@ -86,7 +86,7 @@ def run_generator(gen: Generator[T, V, U], F = TypeVar('F', bound=Callable) -class async_val(Awaitable[V]): +class async_val(Awaitable[V], Generic[T, V]): def __init__(self, val: T) -> None: self.val = val diff --git a/test-data/unit/check-functions.test b/test-data/unit/check-functions.test index fe01590c6c71..917b74fd2147 100644 --- a/test-data/unit/check-functions.test +++ b/test-data/unit/check-functions.test @@ -3240,3 +3240,65 @@ class Base: class Derived(Base): def foo(self): # E: Cannot override final attribute "foo" (previously declared in base class "Base") pass + +[case testTypeVarIdClashPolymorphic] +from typing import Callable, Generic, TypeVar + +A = TypeVar("A") +B = TypeVar("B") + +class Gen(Generic[A]): ... + +def id_(x: A) -> A: ... +def f(x: Gen[A], y: A) -> Gen[Gen[A]]: ... +def g(x: Gen[A], id_: Callable[[B], B], f: Callable[[A, B], Gen[A]]) -> A: ... + +def test(x: Gen[Gen[A]]) -> Gen[A]: + return g(x, id_, f) # Technically OK + +x: Gen[Gen[int]] +reveal_type(g(x, id_, f)) # N: Revealed type is "__main__.Gen[builtins.int]" + +def h(x: A, y: A) -> A: ... +def gn(id_: Callable[[B], B], step: Callable[[A, B], A]) -> A: ... + +def fn(x: A) -> A: + return gn(id_, h) # Technically OK + +[case testTypeVarIdsNested] +from typing import Callable, TypeVar + +A = TypeVar("A") +B = TypeVar("B") + +def f(x: Callable[[A], A]) -> Callable[[B], B]: + def g(x: B) -> B: ... + return g + +reveal_type(f(f)) # N: Revealed type is "def [B] (B`1) -> B`1" +reveal_type(f(f)(f)) # N: Revealed type is "def [A] (x: def (A`-1) -> A`-1) -> def [B] (B`-2) -> B`-2" + +[case testGenericUnionFunctionJoin] +from typing import TypeVar, Union + +T = TypeVar("T") +S = TypeVar("S") + +def f(x: T, y: S) -> Union[T, S]: ... +def g(x: T, y: S) -> Union[T, S]: ... + +x = [f, g] +reveal_type(x) # N: Revealed type is "builtins.list[def [T, S] (x: T`4, y: S`5) -> Union[T`4, S`5]]" +[builtins fixtures/list.pyi] + +[case testTypeVariableClashErrorMessage] +from typing import TypeVar + +T = TypeVar("T") + +class C: # Note: Generic[T] missing + def bad_idea(self, x: T) -> None: + self.x = x + + def nope(self, x: T) -> None: + self.x = x # E: Incompatible types in assignment (expression has type "T@nope", variable has type "T@bad_idea") diff --git a/test-data/unit/check-functools.test b/test-data/unit/check-functools.test index 38083ad98f21..283500f25a7d 100644 --- a/test-data/unit/check-functools.test +++ b/test-data/unit/check-functools.test @@ -183,8 +183,8 @@ p3(1, 3) # E: Too many positional arguments for "foo" \ # E: Argument 2 to "foo" has incompatible type "int"; expected "str" functools.partial(foo, "a") # E: Argument 1 to "foo" has incompatible type "str"; expected "int" -functools.partial(foo, b=1) # E: Argument 1 to "foo" has incompatible type "int"; expected "str" -functools.partial(foo, a=1, b=2, c=3) # E: Argument 2 to "foo" has incompatible type "int"; expected "str" +functools.partial(foo, b=1) # E: Argument "b" to "foo" has incompatible type "int"; expected "str" +functools.partial(foo, a=1, b=2, c=3) # E: Argument "b" to "foo" has incompatible type "int"; expected "str" functools.partial(1) # E: "int" not callable \ # E: Argument 1 to "partial" has incompatible type "int"; expected "Callable[..., Never]" [builtins fixtures/dict.pyi] diff --git a/test-data/unit/check-generics.test b/test-data/unit/check-generics.test index b1d1ff3f46a1..bd327745e2ed 100644 --- a/test-data/unit/check-generics.test +++ b/test-data/unit/check-generics.test @@ -1608,17 +1608,17 @@ if int(): if int(): y1 = f3 if int(): - y1 = f4 # E: Incompatible types in assignment (expression has type "Callable[[int], A]", variable has type "Callable[[A], A]") + y1 = f4 # E: Incompatible types in assignment (expression has type "Callable[[int], A@f4]", variable has type "Callable[[A@f1], A@f1]") y2 = f2 if int(): y2 = f2 if int(): - y2 = f1 # E: Incompatible types in assignment (expression has type "Callable[[A], A]", variable has type "Callable[[A], B]") + y2 = f1 # E: Incompatible types in assignment (expression has type "Callable[[A@f1], A@f1]", variable has type "Callable[[A@f2], B]") if int(): - y2 = f3 # E: Incompatible types in assignment (expression has type "Callable[[B], B]", variable has type "Callable[[A], B]") + y2 = f3 # E: Incompatible types in assignment (expression has type "Callable[[B@f3], B@f3]", variable has type "Callable[[A], B@f2]") if int(): - y2 = f4 # E: Incompatible types in assignment (expression has type "Callable[[int], A]", variable has type "Callable[[A], B]") + y2 = f4 # E: Incompatible types in assignment (expression has type "Callable[[int], A@f4]", variable has type "Callable[[A@f2], B]") y3 = f3 if int(): @@ -1634,7 +1634,7 @@ y4 = f4 if int(): y4 = f4 if int(): - y4 = f1 # E: Incompatible types in assignment (expression has type "Callable[[A], A]", variable has type "Callable[[int], A]") + y4 = f1 # E: Incompatible types in assignment (expression has type "Callable[[A@f1], A@f1]", variable has type "Callable[[int], A@f4]") if int(): y4 = f2 if int(): @@ -1655,26 +1655,26 @@ def outer(t: T) -> None: y1 = f1 if int(): y1 = f2 - y1 = f3 # E: Incompatible types in assignment (expression has type "Callable[[T], A]", variable has type "Callable[[A], A]") - y1 = f4 # E: Incompatible types in assignment (expression has type "Callable[[A], T]", variable has type "Callable[[A], A]") + y1 = f3 # E: Incompatible types in assignment (expression has type "Callable[[T], A@f3]", variable has type "Callable[[A@f1], A@f1]") + y1 = f4 # E: Incompatible types in assignment (expression has type "Callable[[A@f4], T]", variable has type "Callable[[A@f1], A@f1]") y1 = f5 # E: Incompatible types in assignment (expression has type "Callable[[T], T]", variable has type "Callable[[A], A]") y2 = f2 if int(): - y2 = f1 # E: Incompatible types in assignment (expression has type "Callable[[A], A]", variable has type "Callable[[A], B]") + y2 = f1 # E: Incompatible types in assignment (expression has type "Callable[[A@f1], A@f1]", variable has type "Callable[[A@f2], B]") y3 = f3 if int(): - y3 = f1 # E: Incompatible types in assignment (expression has type "Callable[[A], A]", variable has type "Callable[[T], A]") + y3 = f1 # E: Incompatible types in assignment (expression has type "Callable[[A@f1], A@f1]", variable has type "Callable[[T], A@f3]") y3 = f2 - y3 = f4 # E: Incompatible types in assignment (expression has type "Callable[[A], T]", variable has type "Callable[[T], A]") + y3 = f4 # E: Incompatible types in assignment (expression has type "Callable[[A@f4], T]", variable has type "Callable[[T], A@f3]") y3 = f5 # E: Incompatible types in assignment (expression has type "Callable[[T], T]", variable has type "Callable[[T], A]") y4 = f4 if int(): - y4 = f1 # E: Incompatible types in assignment (expression has type "Callable[[A], A]", variable has type "Callable[[A], T]") + y4 = f1 # E: Incompatible types in assignment (expression has type "Callable[[A@f1], A@f1]", variable has type "Callable[[A@f4], T]") y4 = f2 - y4 = f3 # E: Incompatible types in assignment (expression has type "Callable[[T], A]", variable has type "Callable[[A], T]") + y4 = f3 # E: Incompatible types in assignment (expression has type "Callable[[T], A@f3]", variable has type "Callable[[A@f4], T]") y4 = f5 # E: Incompatible types in assignment (expression has type "Callable[[T], T]", variable has type "Callable[[A], T]") y5 = f5 @@ -1683,7 +1683,6 @@ def outer(t: T) -> None: y5 = f2 y5 = f3 y5 = f4 -[out] [case testSubtypingWithGenericFunctionUsingTypevarWithValues] from typing import TypeVar, Callable @@ -2928,8 +2927,8 @@ def mix(fs: List[Callable[[S], T]]) -> Callable[[S], List[T]]: def id(__x: U) -> U: ... fs = [id, id, id] -reveal_type(mix(fs)) # N: Revealed type is "def [S] (S`3) -> builtins.list[S`3]" -reveal_type(mix([id, id, id])) # N: Revealed type is "def [S] (S`5) -> builtins.list[S`5]" +reveal_type(mix(fs)) # N: Revealed type is "def [S] (S`7) -> builtins.list[S`7]" +reveal_type(mix([id, id, id])) # N: Revealed type is "def [S] (S`9) -> builtins.list[S`9]" [builtins fixtures/list.pyi] [case testInferenceAgainstGenericCurry] diff --git a/test-data/unit/check-inference.test b/test-data/unit/check-inference.test index 08b53ab16972..fcd03f8efe01 100644 --- a/test-data/unit/check-inference.test +++ b/test-data/unit/check-inference.test @@ -988,7 +988,7 @@ a = k2 if int(): a = k2 if int(): - a = k1 # E: Incompatible types in assignment (expression has type "Callable[[int, List[T]], List[Union[T, int]]]", variable has type "Callable[[S, List[T]], List[Union[T, int]]]") + a = k1 # E: Incompatible types in assignment (expression has type "Callable[[int, List[T@k1]], List[Union[T@k1, int]]]", variable has type "Callable[[S, List[T@k2]], List[Union[T@k2, int]]]") b = k1 if int(): b = k1 diff --git a/test-data/unit/check-python311.test b/test-data/unit/check-python311.test index 2d1a09ef3336..28951824999f 100644 --- a/test-data/unit/check-python311.test +++ b/test-data/unit/check-python311.test @@ -171,5 +171,5 @@ reveal_type(x3) # N: Revealed type is "def (*Any) -> builtins.int" IntList = List[int] Alias4 = Callable[[*IntList], int] # E: "List[int]" cannot be unpacked (must be tuple or TypeVarTuple) x4: Alias4[int] # E: Bad number of arguments for type alias, expected 0, given 1 -reveal_type(x4) # N: Revealed type is "def (*Unpack[builtins.tuple[Any, ...]]) -> builtins.int" +reveal_type(x4) # N: Revealed type is "def (*Any) -> builtins.int" [builtins fixtures/tuple.pyi] diff --git a/test-data/unit/check-type-aliases.test b/test-data/unit/check-type-aliases.test index c13331e0a61b..86bd4422003b 100644 --- a/test-data/unit/check-type-aliases.test +++ b/test-data/unit/check-type-aliases.test @@ -1220,3 +1220,14 @@ Ta2 = TypeAliasType("Ta2", None, type_params=(Unpack[Ts],)) # E: Free type vari # N: Don't Unpack type variables in type_params [builtins fixtures/tuple.pyi] + +[case testAliasInstanceNameClash] +from lib import func +class A: ... +func(A()) # E: Argument 1 to "func" has incompatible type "__main__.A"; expected "lib.A" +[file lib.py] +from typing import List, Union + +A = Union[int, List[A]] +def func(x: A) -> int: ... +[builtins fixtures/tuple.pyi] diff --git a/test-data/unit/check-typevar-tuple.test b/test-data/unit/check-typevar-tuple.test index f704e3c5c713..21415abb9c28 100644 --- a/test-data/unit/check-typevar-tuple.test +++ b/test-data/unit/check-typevar-tuple.test @@ -881,7 +881,8 @@ y: B z: C reveal_type(x) # N: Revealed type is "Any" reveal_type(y) # N: Revealed type is "Any" -reveal_type(z) # N: Revealed type is "Tuple[builtins.int, Unpack[Any]]" +reveal_type(z) # N: Revealed type is "Tuple[builtins.int, Unpack[builtins.tuple[Any, ...]]]" + [builtins fixtures/tuple.pyi] [case testInferenceAgainstGenericVariadicWithBadType]