55
55
YieldFromExpr , NamedTupleExpr , TypedDictExpr , NonlocalDecl , SymbolNode ,
56
56
SetComprehension , DictionaryComprehension , TYPE_ALIAS , TypeAliasExpr ,
57
57
YieldExpr , ExecStmt , Argument , BackquoteExpr , ImportBase , AwaitExpr ,
58
- IntExpr , FloatExpr , UnicodeExpr , EllipsisExpr , TempNode , EnumCallExpr ,
58
+ IntExpr , FloatExpr , UnicodeExpr , EllipsisExpr , TempNode , EnumCallExpr , ImportedName ,
59
59
COVARIANT , CONTRAVARIANT , INVARIANT , UNBOUND_IMPORTED , LITERAL_YES , ARG_OPT , nongen_builtins ,
60
60
collections_type_aliases , get_member_expr_fullname ,
61
61
)
84
84
from mypy .plugin import Plugin , ClassDefContext , SemanticAnalyzerPluginInterface
85
85
from mypy import join
86
86
from mypy .util import get_prefix , correct_relative_import
87
- from mypy .semanal_shared import PRIORITY_FALLBACKS
87
+ from mypy .semanal_shared import SemanticAnalyzerInterface , PRIORITY_FALLBACKS
88
88
from mypy .scope import Scope
89
89
90
90
174
174
}
175
175
176
176
177
- class SemanticAnalyzerPass2 (NodeVisitor [None ], SemanticAnalyzerPluginInterface ):
177
+ class SemanticAnalyzerPass2 (NodeVisitor [None ],
178
+ SemanticAnalyzerInterface ,
179
+ SemanticAnalyzerPluginInterface ):
178
180
"""Semantically analyze parsed mypy files.
179
181
180
182
The analyzer binds names and does various consistency checks for a
@@ -1488,6 +1490,8 @@ def visit_import_from(self, imp: ImportFrom) -> None:
1488
1490
module = self .modules .get (import_id )
1489
1491
for id , as_id in imp .names :
1490
1492
node = module .names .get (id ) if module else None
1493
+ node = self .dereference_module_cross_ref (node )
1494
+
1491
1495
missing = False
1492
1496
possible_module_id = import_id + '.' + id
1493
1497
@@ -1516,11 +1520,14 @@ def visit_import_from(self, imp: ImportFrom) -> None:
1516
1520
ast_node = Var (name , type = typ )
1517
1521
symbol = SymbolTableNode (GDEF , ast_node )
1518
1522
self .add_symbol (name , symbol , imp )
1519
- return
1523
+ continue
1520
1524
if node and node .kind != UNBOUND_IMPORTED and not node .module_hidden :
1521
1525
node = self .normalize_type_alias (node , imp )
1522
1526
if not node :
1523
- return
1527
+ # Normalization failed because target is not defined. Avoid duplicate
1528
+ # error messages by marking the imported name as unknown.
1529
+ self .add_unknown_symbol (as_id or id , imp , is_import = True )
1530
+ continue
1524
1531
imported_id = as_id or id
1525
1532
existing_symbol = self .globals .get (imported_id )
1526
1533
if existing_symbol :
@@ -1550,6 +1557,29 @@ def visit_import_from(self, imp: ImportFrom) -> None:
1550
1557
# Missing module.
1551
1558
self .add_unknown_symbol (as_id or id , imp , is_import = True )
1552
1559
1560
+ def dereference_module_cross_ref (
1561
+ self , node : Optional [SymbolTableNode ]) -> Optional [SymbolTableNode ]:
1562
+ """Dereference cross references to other modules (if any).
1563
+
1564
+ If the node is not a cross reference, return it unmodified.
1565
+ """
1566
+ seen = set () # type: Set[str]
1567
+ # Continue until we reach a node that's nota cross reference (or until we find
1568
+ # nothing).
1569
+ while node and isinstance (node .node , ImportedName ):
1570
+ fullname = node .node .fullname ()
1571
+ if fullname in self .modules :
1572
+ # This is a module reference.
1573
+ return SymbolTableNode (MODULE_REF , self .modules [fullname ])
1574
+ if fullname in seen :
1575
+ # Looks like a reference cycle. Just break it.
1576
+ # TODO: Generate a more specific error message.
1577
+ node = None
1578
+ break
1579
+ node = self .lookup_fully_qualified_or_none (fullname )
1580
+ seen .add (fullname )
1581
+ return node
1582
+
1553
1583
def process_import_over_existing_name (self ,
1554
1584
imported_id : str , existing_symbol : SymbolTableNode ,
1555
1585
module_symbol : SymbolTableNode ,
@@ -1573,6 +1603,14 @@ def process_import_over_existing_name(self,
1573
1603
1574
1604
def normalize_type_alias (self , node : SymbolTableNode ,
1575
1605
ctx : Context ) -> Optional [SymbolTableNode ]:
1606
+ """If node refers to a built-in type alias, normalize it.
1607
+
1608
+ An example normalization is 'typing.List' -> '__builtins__.list'.
1609
+
1610
+ By default, if the node doesn't refer to a built-in type alias, return
1611
+ the original node. If normalization fails because the target isn't
1612
+ defined, return None.
1613
+ """
1576
1614
normalized = False
1577
1615
fullname = node .fullname
1578
1616
if fullname in type_aliases :
@@ -1612,7 +1650,10 @@ def visit_import_all(self, i: ImportAll) -> None:
1612
1650
if i_id in self .modules :
1613
1651
m = self .modules [i_id ]
1614
1652
self .add_submodules_to_parent_modules (i_id , True )
1615
- for name , node in m .names .items ():
1653
+ for name , orig_node in m .names .items ():
1654
+ node = self .dereference_module_cross_ref (orig_node )
1655
+ if node is None :
1656
+ continue
1616
1657
new_node = self .normalize_type_alias (node , i )
1617
1658
# if '__all__' exists, all nodes not included have had module_public set to
1618
1659
# False, and we can skip checking '_' because it's been explicitly included.
@@ -1670,11 +1711,8 @@ def type_analyzer(self, *,
1670
1711
third_pass : bool = False ) -> TypeAnalyser :
1671
1712
if tvar_scope is None :
1672
1713
tvar_scope = self .tvar_scope
1673
- tpan = TypeAnalyser (self .lookup_qualified ,
1674
- self .lookup_fully_qualified ,
1714
+ tpan = TypeAnalyser (self ,
1675
1715
tvar_scope ,
1676
- self .fail ,
1677
- self .note ,
1678
1716
self .plugin ,
1679
1717
self .options ,
1680
1718
self .is_typeshed_stub_file ,
@@ -1803,11 +1841,8 @@ def analyze_alias(self, rvalue: Expression,
1803
1841
dynamic = bool (self .function_stack and self .function_stack [- 1 ].is_dynamic ())
1804
1842
global_scope = not self .type and not self .function_stack
1805
1843
res = analyze_type_alias (rvalue ,
1806
- self .lookup_qualified ,
1807
- self .lookup_fully_qualified ,
1844
+ self ,
1808
1845
self .tvar_scope ,
1809
- self .fail ,
1810
- self .note ,
1811
1846
self .plugin ,
1812
1847
self .options ,
1813
1848
self .is_typeshed_stub_file ,
@@ -3408,6 +3443,7 @@ def visit_member_expr(self, expr: MemberExpr) -> None:
3408
3443
# else:
3409
3444
# names = file.names
3410
3445
n = file .names .get (expr .name , None ) if file is not None else None
3446
+ n = self .dereference_module_cross_ref (n )
3411
3447
if n and not n .module_hidden :
3412
3448
n = self .normalize_type_alias (n , expr )
3413
3449
if not n :
@@ -3813,22 +3849,21 @@ def lookup_fully_qualified(self, name: str) -> SymbolTableNode:
3813
3849
n = next_sym .node
3814
3850
return n .names [parts [- 1 ]]
3815
3851
3816
- def lookup_fully_qualified_or_none (self , name : str ) -> Optional [SymbolTableNode ]:
3817
- """Lookup a fully qualified name.
3852
+ def lookup_fully_qualified_or_none (self , fullname : str ) -> Optional [SymbolTableNode ]:
3853
+ """Lookup a fully qualified name that refers to a module-level definition .
3818
3854
3819
3855
Don't assume that the name is defined. This happens in the global namespace --
3820
- the local module namespace is ignored.
3856
+ the local module namespace is ignored. This does not dereference indirect
3857
+ refs.
3858
+
3859
+ Note that this can't be used for names nested in class namespaces.
3821
3860
"""
3822
- assert '.' in name
3823
- parts = name .split ('.' )
3824
- n = self .modules [parts [0 ]]
3825
- for i in range (1 , len (parts ) - 1 ):
3826
- next_sym = n .names .get (parts [i ])
3827
- if not next_sym :
3828
- return None
3829
- assert isinstance (next_sym .node , MypyFile )
3830
- n = next_sym .node
3831
- return n .names .get (parts [- 1 ])
3861
+ assert '.' in fullname
3862
+ module , name = fullname .rsplit ('.' , maxsplit = 1 )
3863
+ if module not in self .modules :
3864
+ return None
3865
+ filenode = self .modules [module ]
3866
+ return filenode .names .get (name )
3832
3867
3833
3868
def qualified_name (self , n : str ) -> str :
3834
3869
if self .type is not None :
@@ -3861,6 +3896,8 @@ def is_module_scope(self) -> bool:
3861
3896
3862
3897
def add_symbol (self , name : str , node : SymbolTableNode ,
3863
3898
context : Context ) -> None :
3899
+ # NOTE: This logic mostly parallels SemanticAnalyzerPass1.add_symbol. If you change
3900
+ # this, you may have to change the other method as well.
3864
3901
if self .is_func_scope ():
3865
3902
assert self .locals [- 1 ] is not None
3866
3903
if name in self .locals [- 1 ]:
@@ -3873,8 +3910,10 @@ def add_symbol(self, name: str, node: SymbolTableNode,
3873
3910
self .type .names [name ] = node
3874
3911
else :
3875
3912
existing = self .globals .get (name )
3876
- if existing and (not isinstance (node .node , MypyFile ) or
3877
- existing .node != node .node ) and existing .kind != UNBOUND_IMPORTED :
3913
+ if (existing
3914
+ and (not isinstance (node .node , MypyFile ) or existing .node != node .node )
3915
+ and existing .kind != UNBOUND_IMPORTED
3916
+ and not isinstance (existing .node , ImportedName )):
3878
3917
# Modules can be imported multiple times to support import
3879
3918
# of multiple submodules of a package (e.g. a.x and a.y).
3880
3919
ok = False
0 commit comments