@@ -1395,7 +1395,8 @@ def visit_import_from(self, imp: ImportFrom) -> None:
1395
1395
self .cur_mod_id ,
1396
1396
node .type_override ,
1397
1397
module_public = module_public ,
1398
- normalized = node .normalized )
1398
+ normalized = node .normalized ,
1399
+ alias_tvars = node .alias_tvars )
1399
1400
self .add_symbol (imported_id , symbol , imp )
1400
1401
elif module and not missing :
1401
1402
# Missing attribute.
@@ -1447,7 +1448,8 @@ def normalize_type_alias(self, node: SymbolTableNode,
1447
1448
normalized = True
1448
1449
if normalized :
1449
1450
node = SymbolTableNode (node .kind , node .node ,
1450
- node .mod_id , node .type_override , normalized = True )
1451
+ node .mod_id , node .type_override ,
1452
+ normalized = True , alias_tvars = node .alias_tvars )
1451
1453
return node
1452
1454
1453
1455
def add_fixture_note (self , fullname : str , ctx : Context ) -> None :
@@ -1491,7 +1493,8 @@ def visit_import_all(self, i: ImportAll) -> None:
1491
1493
self .add_symbol (name , SymbolTableNode (node .kind , node .node ,
1492
1494
self .cur_mod_id ,
1493
1495
node .type_override ,
1494
- normalized = node .normalized ), i )
1496
+ normalized = node .normalized ,
1497
+ alias_tvars = node .alias_tvars ), i )
1495
1498
else :
1496
1499
# Don't add any dummy symbols for 'from x import *' if 'x' is unknown.
1497
1500
pass
@@ -1563,36 +1566,11 @@ def visit_assignment_stmt(self, s: AssignmentStmt) -> None:
1563
1566
allow_tuple_literal = isinstance (s .lvalues [- 1 ], (TupleExpr , ListExpr ))
1564
1567
s .type = self .anal_type (s .type , allow_tuple_literal = allow_tuple_literal )
1565
1568
else :
1566
- # For simple assignments, allow binding type aliases.
1567
- # Also set the type if the rvalue is a simple literal.
1569
+ # Set the type if the rvalue is a simple literal.
1568
1570
if (s .type is None and len (s .lvalues ) == 1 and
1569
1571
isinstance (s .lvalues [0 ], NameExpr )):
1570
1572
if s .lvalues [0 ].is_def :
1571
1573
s .type = self .analyze_simple_literal_type (s .rvalue )
1572
- res = analyze_type_alias (s .rvalue ,
1573
- self .lookup_qualified ,
1574
- self .lookup_fully_qualified ,
1575
- self .tvar_scope ,
1576
- self .fail ,
1577
- self .plugin ,
1578
- self .options ,
1579
- self .is_typeshed_stub_file ,
1580
- allow_unnormalized = True )
1581
- if res and (not isinstance (res , Instance ) or res .args ):
1582
- # TODO: What if this gets reassigned?
1583
- check_for_explicit_any (res , self .options , self .is_typeshed_stub_file , self .msg ,
1584
- context = s )
1585
- # when this type alias gets "inlined", the Any is not explicit anymore,
1586
- # so we need to replace it with non-explicit Anys
1587
- res = make_any_non_explicit (res )
1588
-
1589
- name = s .lvalues [0 ]
1590
- node = self .lookup (name .name , name )
1591
- node .kind = TYPE_ALIAS
1592
- node .type_override = res
1593
- if isinstance (s .rvalue , IndexExpr ):
1594
- s .rvalue .analyzed = TypeAliasExpr (res ,
1595
- fallback = self .alias_fallback (res ))
1596
1574
if s .type :
1597
1575
# Store type into nodes.
1598
1576
for lvalue in s .lvalues :
@@ -1646,26 +1624,79 @@ def alias_fallback(self, tp: Type) -> Instance:
1646
1624
fb_info .mro = [fb_info , self .object_type ().type ]
1647
1625
return Instance (fb_info , [])
1648
1626
1627
+ def analyze_alias (self , rvalue : Expression ,
1628
+ allow_unnormalized : bool ) -> Tuple [Optional [Type ], List [str ]]:
1629
+ """Check if 'rvalue' represents a valid type allowed for aliasing
1630
+ (e.g. not a type variable). If yes, return the corresponding type and a list of
1631
+ qualified type variable names for generic aliases.
1632
+ If 'allow_unnormalized' is True, allow types like builtins.list[T].
1633
+ """
1634
+ res = analyze_type_alias (rvalue ,
1635
+ self .lookup_qualified ,
1636
+ self .lookup_fully_qualified ,
1637
+ self .tvar_scope ,
1638
+ self .fail ,
1639
+ self .plugin ,
1640
+ self .options ,
1641
+ self .is_typeshed_stub_file ,
1642
+ allow_unnormalized = True )
1643
+ if res :
1644
+ alias_tvars = [name for (name , _ ) in
1645
+ res .accept (TypeVariableQuery (self .lookup_qualified , self .tvar_scope ))]
1646
+ else :
1647
+ alias_tvars = []
1648
+ return res , alias_tvars
1649
+
1649
1650
def check_and_set_up_type_alias (self , s : AssignmentStmt ) -> None :
1650
- """Check if assignment creates a type alias and set it up as needed."""
1651
- # For now, type aliases only work at the top level of a module.
1652
- if (len (s .lvalues ) == 1 and not self .is_func_scope () and not self .type
1651
+ """Check if assignment creates a type alias and set it up as needed.
1652
+ For simple aliases like L = List we use a simpler mechanism, just copying TypeInfo.
1653
+ For subscripted (including generic) aliases the resulting types are stored
1654
+ in rvalue.analyzed.
1655
+ """
1656
+ # Type aliases are created only at module scope and class scope (for subscripted types),
1657
+ # at function scope assignments always create local variables with type object types.
1658
+ lvalue = s .lvalues [0 ]
1659
+ if not isinstance (lvalue , NameExpr ):
1660
+ return
1661
+ if (len (s .lvalues ) == 1 and not self .is_func_scope () and
1662
+ not (self .type and isinstance (s .rvalue , NameExpr ) and lvalue .is_def )
1653
1663
and not s .type ):
1654
- lvalue = s .lvalues [0 ]
1655
- if isinstance (lvalue , NameExpr ):
1656
- if not lvalue .is_def :
1657
- # Only a definition can create a type alias, not regular assignment.
1658
- return
1659
- rvalue = s .rvalue
1664
+ rvalue = s .rvalue
1665
+ res , alias_tvars = self .analyze_alias (rvalue , allow_unnormalized = True )
1666
+ if not res :
1667
+ return
1668
+ node = self .lookup (lvalue .name , lvalue )
1669
+ if not lvalue .is_def :
1670
+ # Only a definition can create a type alias, not regular assignment.
1671
+ if node and node .kind == TYPE_ALIAS or isinstance (node .node , TypeInfo ):
1672
+ self .fail ('Cannot assign multiple types to name "{}"'
1673
+ ' without an explicit "Type[...]" annotation'
1674
+ .format (lvalue .name ), lvalue )
1675
+ return
1676
+ check_for_explicit_any (res , self .options , self .is_typeshed_stub_file , self .msg ,
1677
+ context = s )
1678
+ # when this type alias gets "inlined", the Any is not explicit anymore,
1679
+ # so we need to replace it with non-explicit Anys
1680
+ res = make_any_non_explicit (res )
1681
+ if isinstance (res , Instance ) and not res .args and isinstance (rvalue , RefExpr ):
1682
+ # For simple (on-generic) aliases we use aliasing TypeInfo's
1683
+ # to allow using them in runtime context where it makes sense.
1684
+ node .node = res .type
1660
1685
if isinstance (rvalue , RefExpr ):
1661
- node = rvalue .node
1662
- if isinstance (node , TypeInfo ):
1663
- # TODO: We should record the fact that this is a variable
1664
- # that refers to a type, rather than making this
1665
- # just an alias for the type.
1666
- sym = self .lookup_type_node (rvalue )
1667
- if sym :
1668
- self .globals [lvalue .name ] = sym
1686
+ sym = self .lookup_type_node (rvalue )
1687
+ if sym :
1688
+ node .normalized = sym .normalized
1689
+ return
1690
+ node .kind = TYPE_ALIAS
1691
+ node .type_override = res
1692
+ node .alias_tvars = alias_tvars
1693
+ if isinstance (rvalue , IndexExpr ):
1694
+ # We only need this for subscripted aliases, since simple aliases
1695
+ # are already processed using aliasing TypeInfo's above.
1696
+ rvalue .analyzed = TypeAliasExpr (res , node .alias_tvars ,
1697
+ fallback = self .alias_fallback (res ))
1698
+ rvalue .analyzed .line = rvalue .line
1699
+ rvalue .analyzed .column = rvalue .column
1669
1700
1670
1701
def analyze_lvalue (self , lval : Lvalue , nested : bool = False ,
1671
1702
add_global : bool = False ,
@@ -3196,17 +3227,11 @@ def visit_index_expr(self, expr: IndexExpr) -> None:
3196
3227
elif isinstance (expr .base , RefExpr ) and expr .base .kind == TYPE_ALIAS :
3197
3228
# Special form -- subscripting a generic type alias.
3198
3229
# Perform the type substitution and create a new alias.
3199
- res = analyze_type_alias (expr ,
3200
- self .lookup_qualified ,
3201
- self .lookup_fully_qualified ,
3202
- self .tvar_scope ,
3203
- self .fail ,
3204
- self .plugin ,
3205
- self .options ,
3206
- self .is_typeshed_stub_file ,
3207
- allow_unnormalized = self .is_stub_file )
3208
- expr .analyzed = TypeAliasExpr (res , fallback = self .alias_fallback (res ),
3230
+ res , alias_tvars = self .analyze_alias (expr , allow_unnormalized = self .is_stub_file )
3231
+ expr .analyzed = TypeAliasExpr (res , alias_tvars , fallback = self .alias_fallback (res ),
3209
3232
in_runtime = True )
3233
+ expr .analyzed .line = expr .line
3234
+ expr .analyzed .column = expr .column
3210
3235
elif refers_to_class_or_function (expr .base ):
3211
3236
# Special form -- type application.
3212
3237
# Translate index to an unanalyzed type.
0 commit comments