-
-
Notifications
You must be signed in to change notification settings - Fork 2.9k
Generic type aliases #2378
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Generic type aliases #2378
Changes from all commits
5dc0090
f6d862b
b0790d4
7ddd6ab
633e3d1
07baa51
56b7ae7
cbcb2c0
5c92b12
13e1fff
ee1c25c
2edcf48
64fc96e
8c64111
c106204
feb9413
d402a68
8cea156
382c53e
863c576
544ff66
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -6,7 +6,8 @@ | |
Type, AnyType, CallableType, Overloaded, NoneTyp, Void, TypeVarDef, | ||
TupleType, Instance, TypeVarId, TypeVarType, ErasedType, UnionType, | ||
PartialType, DeletedType, UnboundType, UninhabitedType, TypeType, | ||
true_only, false_only, is_named_instance, function_type | ||
true_only, false_only, is_named_instance, function_type, | ||
get_typ_args, set_typ_args, | ||
) | ||
from mypy.nodes import ( | ||
NameExpr, RefExpr, Var, FuncDef, OverloadedFuncDef, TypeInfo, CallExpr, | ||
|
@@ -17,6 +18,7 @@ | |
ConditionalExpr, ComparisonExpr, TempNode, SetComprehension, | ||
DictionaryComprehension, ComplexExpr, EllipsisExpr, StarExpr, | ||
TypeAliasExpr, BackquoteExpr, ARG_POS, ARG_NAMED, ARG_STAR2, MODULE_REF, | ||
UNBOUND_TVAR, BOUND_TVAR, | ||
) | ||
from mypy import nodes | ||
import mypy.checker | ||
|
@@ -1375,8 +1377,55 @@ def visit_type_application(self, tapp: TypeApplication) -> Type: | |
return AnyType() | ||
|
||
def visit_type_alias_expr(self, alias: TypeAliasExpr) -> Type: | ||
"""Get type of a type alias (could be generic) in a runtime expression.""" | ||
item = alias.type | ||
if not alias.in_runtime: | ||
# We don't replace TypeVar's with Any for alias used as Alias[T](42). | ||
item = self.replace_tvars_any(item) | ||
if isinstance(item, Instance): | ||
# Normally we get a callable type (or overloaded) with .is_type_obj() true | ||
# representing the class's constructor | ||
tp = type_object_type(item.type, self.named_type) | ||
else: | ||
# This type is invalid in most runtime contexts | ||
# and corresponding an error will be reported. | ||
return alias.fallback | ||
if isinstance(tp, CallableType): | ||
if len(tp.variables) != len(item.args): | ||
self.msg.incompatible_type_application(len(tp.variables), | ||
len(item.args), item) | ||
return AnyType() | ||
return self.apply_generic_arguments(tp, item.args, item) | ||
elif isinstance(tp, Overloaded): | ||
for it in tp.items(): | ||
if len(it.variables) != len(item.args): | ||
self.msg.incompatible_type_application(len(it.variables), | ||
len(item.args), item) | ||
return AnyType() | ||
return Overloaded([self.apply_generic_arguments(it, item.args, item) | ||
for it in tp.items()]) | ||
return AnyType() | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's not an error because we can only get here when there already was a previous error, right? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @gvanrossum Yes, I think so. At least I don't see a way to get there without previously having an error. |
||
|
||
def replace_tvars_any(self, tp: Type) -> Type: | ||
"""Replace all type variables of a type alias tp with Any. Basically, this function | ||
finishes what could not be done in method TypeAnalyser.visit_unbound_type() | ||
from typeanal.py. | ||
""" | ||
typ_args = get_typ_args(tp) | ||
new_args = typ_args[:] | ||
for i, arg in enumerate(typ_args): | ||
if isinstance(arg, UnboundType): | ||
sym = None | ||
try: | ||
sym = self.chk.lookup_qualified(arg.name) | ||
except KeyError: | ||
pass | ||
if sym and (sym.kind == UNBOUND_TVAR or sym.kind == BOUND_TVAR): | ||
new_args[i] = AnyType() | ||
else: | ||
new_args[i] = self.replace_tvars_any(arg) | ||
return set_typ_args(tp, new_args, tp.line, tp.column) | ||
|
||
def visit_list_expr(self, e: ListExpr) -> Type: | ||
"""Type check a list expression [...].""" | ||
return self.check_lst_expr(e.items, 'builtins.list', '<list>', e) | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd like a comment here explaining that we'll generally get a callable type (or overloaded) with .is_type_obj() true, representing the class's constructor. This would explain that below we really just check for callable and overloaded.