Skip to content

Commit 565e960

Browse files
committed
WIP 2
1 parent e41b970 commit 565e960

File tree

10 files changed

+59
-44
lines changed

10 files changed

+59
-44
lines changed

mypy/build.py

Lines changed: 23 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
SymbolTableNode, MODULE_REF)
3030
from mypy.semanal import FirstPass, SemanticAnalyzer, ThirdPass
3131
from mypy.checker import TypeChecker
32+
from mypy.indirection import IndirectionDetector
3233
from mypy.errors import Errors, CompileError, DecodeError, report_internal_error
3334
from mypy import fixup
3435
from mypy.report import Reports
@@ -352,6 +353,7 @@ def __init__(self, data_dir: str,
352353
self.modules = self.semantic_analyzer.modules
353354
self.semantic_analyzer_pass3 = ThirdPass(self.modules, self.errors)
354355
self.type_checker = TypeChecker(self.errors, self.modules, options=options)
356+
self.indirection_detector = IndirectionDetector()
355357
self.missing_modules = set() # type: Set[str]
356358
self.stale_modules = set() # type: Set[str]
357359
self.rechecked_modules = set() # type: Set[str]
@@ -1269,7 +1271,7 @@ def load_tree(self) -> None:
12691271
self.manager.modules[self.id] = self.tree
12701272

12711273
def fix_cross_refs(self) -> None:
1272-
fixup_module_pass_one(self.tree, self.manager.modules, self.valid_references())
1274+
fixup_module_pass_one(self.tree, self.manager.modules)
12731275

12741276
def calculate_mros(self) -> None:
12751277
fixup_module_pass_two(self.tree, self.manager.modules)
@@ -1418,25 +1420,38 @@ def type_check(self) -> None:
14181420
if manager.options.semantic_analysis_only:
14191421
return
14201422
with self.wrap_context():
1421-
manager.type_checker.visit_file(self.tree, self.xpath, self.valid_references())
1422-
encountered = manager.type_checker.get_encountered()
1423-
#for dep in encountered - self.valid_references():
1424-
# self.dependencies.append(dep)
1425-
# self.priorities[dep] = PRI_INDIRECT
1423+
manager.type_checker.type_map = {}
1424+
manager.type_checker.visit_file(self.tree, self.xpath, set())
1425+
# encountered = manager.type_checker.get_encountered()
1426+
# print(manager.type_checker.type_map)
14261427

14271428
if manager.options.dump_inference_stats:
14281429
dump_type_stats(self.tree, self.xpath, inferred=True,
14291430
typemap=manager.type_checker.type_map)
14301431
manager.report_file(self.tree)
14311432

1433+
def detect_indirection(self) -> None:
1434+
self.manager.indirection_detector.modules = set()
1435+
# self.manager.indirection_detector.visit_mypy_file(self.tree)
1436+
for item, val in self.manager.type_checker.type_map.items():
1437+
self.manager.indirection_detector._visit(item)
1438+
self.manager.indirection_detector._visit_types(val)
1439+
1440+
encountered = self.manager.indirection_detector.modules
1441+
valid = self.valid_references()
1442+
extra = encountered - valid
1443+
for dep in extra:
1444+
self.dependencies.append(dep)
1445+
self.priorities[dep] = PRI_INDIRECT
1446+
14321447
def valid_references(self) -> Set[str]:
14331448
valid_refs = set(self.dependencies + self.suppressed + self.ancestors)
14341449
valid_refs .add(self.id)
14351450

14361451
if "os" in valid_refs:
14371452
valid_refs.add("os.path")
14381453

1439-
return valid_refs
1454+
return valid_refs
14401455

14411456
def write_cache(self) -> None:
14421457
if self.path and self.manager.options.incremental and not self.manager.errors.is_errors():
@@ -1691,6 +1706,7 @@ def process_stale_scc(graph: Graph, scc: List[str]) -> None:
16911706
graph[id].semantic_analysis_pass_three()
16921707
for id in scc:
16931708
graph[id].type_check()
1709+
graph[id].detect_indirection()
16941710
graph[id].write_cache()
16951711
graph[id].mark_as_rechecked()
16961712

mypy/checkexpr.py

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -75,9 +75,9 @@ def visit_name_expr(self, e: NameExpr) -> Type:
7575
7676
It can be of any kind: local, member or global.
7777
"""
78-
if e.kind == MODULE_REF and e.fullname not in self.chk.valid_module_references:
79-
self.chk.encountered.add(e.fullname)
80-
self.chk.msg.indirect_import(e.fullname, e)
78+
# if e.kind == MODULE_REF and e.fullname not in self.chk.valid_module_references:
79+
# self.chk.encountered.add(e.fullname)
80+
# self.chk.msg.indirect_import(e.fullname, e)
8181
result = self.analyze_ref_expr(e)
8282
return self.chk.narrow_type_from_binder(e, result)
8383

@@ -861,14 +861,15 @@ def apply_generic_arguments2(self, overload: Overloaded, types: List[Type],
861861

862862
def visit_member_expr(self, e: MemberExpr) -> Type:
863863
"""Visit member expression (of form e.id)."""
864-
base = e.expr
865-
if isinstance(base, RefExpr) and base.kind == MODULE_REF:
866-
self.chk.encountered.add(base.fullname)
867-
if base.fullname not in self.chk.valid_module_references:
868-
pass
869-
#self.chk.msg.indirect_import(base.fullname, e)
864+
# base = e.expr
865+
# if isinstance(base, RefExpr) and base.kind == MODULE_REF:
866+
# self.chk.encountered.add(base.fullname)
867+
# if base.fullname not in self.chk.valid_module_references:
868+
# pass
869+
# self.chk.msg.indirect_import(base.fullname, e)
870870
result = self.analyze_ordinary_member_access(e, False)
871-
return self.chk.narrow_type_from_binder(e, result)
871+
out = self.chk.narrow_type_from_binder(e, result)
872+
return out
872873

873874
def analyze_ordinary_member_access(self, e: MemberExpr,
874875
is_lvalue: bool) -> Type:

mypy/fixup.py

Lines changed: 5 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,8 @@
1212
from mypy.visitor import NodeVisitor
1313

1414

15-
def fixup_module_pass_one(tree: MypyFile, modules: Dict[str, MypyFile], valid_references: Optional[Set[str]] = None) -> None:
16-
node_fixer = NodeFixer(modules, valid_refs=valid_references, name=tree.fullname())
15+
def fixup_module_pass_one(tree: MypyFile, modules: Dict[str, MypyFile]) -> None:
16+
node_fixer = NodeFixer(modules)
1717
node_fixer.visit_symbol_table(tree.names)
1818

1919

@@ -34,12 +34,10 @@ def compute_all_mros(symtab: SymbolTable, modules: Dict[str, MypyFile]) -> None:
3434
class NodeFixer(NodeVisitor[None]):
3535
current_info = None # type: Optional[TypeInfo]
3636

37-
def __init__(self, modules: Dict[str, MypyFile], type_fixer: 'TypeFixer' = None, valid_refs: Set[str] = None, name: str = None) -> None:
37+
def __init__(self, modules: Dict[str, MypyFile], type_fixer: 'TypeFixer' = None) -> None:
3838
self.modules = modules
39-
self.valid_references = valid_refs
40-
self.name = name
4139
if type_fixer is None:
42-
type_fixer = TypeFixer(self.modules, self.valid_references, self.name)
40+
type_fixer = TypeFixer(self.modules)
4341
self.type_fixer = type_fixer
4442

4543
# NOTE: This method isn't (yet) part of the NodeVisitor API.
@@ -70,12 +68,6 @@ def visit_symbol_table(self, symtab: SymbolTable) -> None:
7068
for key, value in list(symtab.items()):
7169
cross_ref = value.cross_ref
7270
if cross_ref is not None: # Fix up cross-reference.
73-
original_module = value.cross_ref.rsplit(".", 1)[0]
74-
if original_module not in self.valid_references:
75-
if self.name is not None:
76-
print(self.name)
77-
self.name = None
78-
print(' ', cross_ref)
7971
del value.cross_ref
8072
if cross_ref in self.modules:
8173
value.node = self.modules[cross_ref]
@@ -139,22 +131,14 @@ def visit_var(self, v: Var) -> None:
139131

140132

141133
class TypeFixer(TypeVisitor[None]):
142-
def __init__(self, modules: Dict[str, MypyFile], valid_refs: Set[str] = None, name: str = None) -> None:
134+
def __init__(self, modules: Dict[str, MypyFile]) -> None:
143135
self.modules = modules
144-
self.valid_references = valid_refs
145-
self.name = name
146136

147137
def visit_instance(self, inst: Instance) -> None:
148138
# TODO: Combine Instances that are exactly the same?
149139
type_ref = inst.type_ref
150140
if type_ref is None:
151141
return # We've already been here.
152-
original = type_ref.rsplit('.', 1)[0]
153-
if original not in self.valid_references:
154-
if self.name is not None:
155-
print(self.name + ' (type ref)')
156-
self.name = None
157-
print(' ', type_ref + ' (type ref)')
158142
del inst.type_ref
159143
node = lookup_qualified(self.modules, type_ref)
160144
if isinstance(node, TypeInfo):

mypy/nodes.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,7 @@ def get_line(self) -> int:
114114
return self.line
115115

116116
def accept(self, visitor: NodeVisitor[T]) -> T:
117-
raise RuntimeError('Not implemented')
117+
raise RuntimeError('Not implemented: {}'.format(self.__class__))
118118

119119

120120
# These are placeholders for a future refactoring; see #1783.
@@ -1771,6 +1771,7 @@ class is generic then it will be a type constructor of higher kind.
17711771
"""
17721772

17731773
_fullname = None # type: str # Fully qualified name
1774+
module_name = None # type: str
17741775
defn = None # type: ClassDef # Corresponding ClassDef
17751776
# Method Resolution Order: the order of looking up attributes. The first
17761777
# value always to refers to this class.
@@ -1979,6 +1980,7 @@ def __str__(self) -> str:
19791980
def serialize(self) -> Union[str, JsonDict]:
19801981
# NOTE: This is where all ClassDefs originate, so there shouldn't be duplicates.
19811982
data = {'.class': 'TypeInfo',
1983+
'module_name': self.module_name,
19821984
'fullname': self.fullname(),
19831985
'alt_fullname': self.alt_fullname,
19841986
'names': self.names.serialize(self.alt_fullname or self.fullname()),
@@ -2003,6 +2005,7 @@ def deserialize(cls, data: JsonDict) -> 'TypeInfo':
20032005
ti = TypeInfo(names, defn)
20042006
ti._fullname = data['fullname']
20052007
ti.alt_fullname = data['alt_fullname']
2008+
ti.module_name = data['module_name']
20062009
# TODO: Is there a reason to reconstruct ti.subtypes?
20072010
ti.is_abstract = data['is_abstract']
20082011
ti.abstract_attributes = data['abstract_attributes']

mypy/semanal.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -736,6 +736,7 @@ def setup_class_def_analysis(self, defn: ClassDef) -> None:
736736
if not defn.info:
737737
defn.info = TypeInfo(SymbolTable(), defn)
738738
defn.info._fullname = defn.info.name()
739+
defn.info.module_name = self.cur_mod_id
739740
if self.is_func_scope() or self.type:
740741
kind = MDEF
741742
if self.is_func_scope():
@@ -1384,6 +1385,7 @@ def build_newtype_typeinfo(self, name: str, old_type: Type, base_type: Instance)
13841385

13851386
symbols = SymbolTable()
13861387
info = TypeInfo(symbols, class_def)
1388+
info.module_name = self.cur_mod_id
13871389
info.mro = [info] + base_type.type.mro
13881390
info.bases = [base_type]
13891391
info.is_newtype = True
@@ -1657,6 +1659,7 @@ def build_namedtuple_typeinfo(self, name: str, items: List[str],
16571659
class_def = ClassDef(name, Block([]))
16581660
class_def.fullname = self.qualified_name(name)
16591661
info = TypeInfo(symbols, class_def)
1662+
info.module_name = self.cur_mod_id
16601663
# Add named tuple items as attributes.
16611664
# TODO: Make them read-only.
16621665
for item, typ in zip(items, types):
@@ -2577,6 +2580,7 @@ def visit_class_def(self, cdef: ClassDef) -> None:
25772580
self.sem.check_no_global(cdef.name, cdef)
25782581
cdef.fullname = self.sem.qualified_name(cdef.name)
25792582
info = TypeInfo(SymbolTable(), cdef)
2583+
info.module_name = self.sem.cur_mod_id
25802584
info.set_line(cdef.line)
25812585
cdef.info = info
25822586
self.sem.globals[cdef.name] = SymbolTableNode(GDEF, info,
@@ -2591,6 +2595,8 @@ def process_nested_classes(self, outer_def: ClassDef) -> None:
25912595
node.info._fullname = outer_def.fullname + '.' + node.info.name()
25922596
else:
25932597
node.info._fullname = node.info.name()
2598+
node.info.module_name = self.sem.cur_mod_id
2599+
node.fullname = node.info._fullname
25942600
symbol = SymbolTableNode(MDEF, node.info)
25952601
outer_def.info.names[node.name] = symbol
25962602
self.process_nested_classes(node)

mypy/test/testcheck.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@ def run_case(self, testcase: DataDrivenTestCase) -> None:
8888
# We briefly sleep to make sure file timestamps are distinct.
8989
self.clear_cache()
9090
self.run_case_once(testcase, 1)
91+
print('\n----\n')
9192
self.run_case_once(testcase, 2)
9293
elif optional:
9394
try:

test-data/unit/check-incremental.test

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -298,7 +298,7 @@ const = 3
298298
# Import to mod4 is gone!
299299

300300
[rechecked mod1, mod2, mod3]
301-
[stale mod1, mod2, mod3]
301+
[stale mod3]
302302
[builtins fixtures/module.pyi]
303303
[out]
304304
main:1: note: In module imported here:
@@ -324,7 +324,8 @@ const = 3
324324
[file mod4.py.next]
325325
const = "foo"
326326

327-
[stale mod1, mod2, mod3, mod4]
327+
[rechecked mod1, mod3, mod4]
328+
[stale mod4]
328329
[out]
329330
main:1: note: In module imported here:
330331
tmp/mod1.py:3: error: Argument 1 to "accepts_int" has incompatible type "str"; expected "int"
@@ -375,7 +376,7 @@ def func2() -> str:
375376
return "foo"
376377

377378
[rechecked mod0, mod1, mod2]
378-
[stale mod0, mod2]
379+
[stale mod2]
379380
[out]
380381
tmp/mod0.py:1: note: In module imported here,
381382
main:1: note: ... from here:
@@ -878,7 +879,7 @@ tmp/main.py:4: error: "Class" has no attribute "some_attribute"
878879
[case testIncrementalWithIgnores]
879880
import foo # type: ignore
880881

881-
[builtins fixtures/args.pyi]
882+
[builtins fixtures/module.pyi]
882883
[stale]
883884
[out]
884885

@@ -930,7 +931,7 @@ class A:
930931
class A:
931932
pass
932933
[rechecked m, n]
933-
[stale m, n]
934+
[stale n]
934935
[out]
935936

936937
[case testIncrementalReplacingImports]

test-data/unit/check-modules.test

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -277,6 +277,7 @@ main:1: note: (Perhaps setting MYPYPATH or using the "--silent-imports" flag wou
277277
import xyz, bar
278278
xyz.foo()
279279
bar()
280+
[builtins fixtures/args.pyi]
280281
[out]
281282
main:1: error: Cannot find module named 'xyz'
282283
main:1: note: (Perhaps setting MYPYPATH or using the "--silent-imports" flag would help)

test-data/unit/fixtures/args.pyi

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,3 +26,4 @@ class int:
2626
class str: pass
2727
class bool: pass
2828
class function: pass
29+
class module: pass

test-data/unit/fixtures/module.pyi

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,4 @@ class type: pass
55
class function: pass
66
class int: pass
77
class str: pass
8+
class tuple: pass

0 commit comments

Comments
 (0)