forked from python/mypy
/
checkmember.py
107 lines (91 loc) · 4 KB
/
checkmember.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
"""Type checking of member access"""
from mtypes import Type, Instance, Any, TupleType, Callable
from nodes import TypeInfo, FuncBase, Var, FuncDef, AccessorNode, Context
from messages import MessageBuilder
from subtypes import map_instance_to_supertype
from expandtype import expand_type_by_instance
from nodes import method_type
import messages
Type analyse_member_access(str name, Type typ, Context node, bool is_lvalue,
bool is_super, Type tuple_type, MessageBuilder msg,
TypeInfo override_info=None):
"""Analyse member access.
This is a general operation that supports various different variations:
1. lvalue or non-lvalue access (i.e. setter or getter access)
2. supertype access (when using the super keyword; is_super == True and
override_info should refer to the supertype)
"""
if isinstance(typ, Instance):
if name == '__init__' and not is_super:
# Accessing __init__ in statically typed code would compromise
# type safety unless used via super().
msg.fail(messages.CANNOT_ACCESS_INIT, node)
return Any()
# The base object has an instance type.
itype = (Instance)typ
info = itype.type
if override_info:
info = override_info
# Look up the member. First look up the method dictionary.
FuncBase method = None
if not is_lvalue:
method = info.get_method(name)
if method:
# Found a method. The call below has a unique result for all valid
# programs.
itype = map_instance_to_supertype(itype, method.info)
return expand_type_by_instance(method_type(method), itype)
else:
# Not a method.
return analyse_member_var_access(name, itype, info, node,
is_lvalue, is_super, msg)
elif isinstance(typ, Any):
# The base object has dynamic type.
return Any()
elif isinstance(typ, TupleType):
# Actually look up from the tuple type.
return analyse_member_access(name, tuple_type, node, is_lvalue,
is_super, tuple_type, msg)
elif isinstance(typ, Callable) and ((Callable)typ).is_type_obj():
# Class attribute access.
return msg.not_implemented('class attributes', node)
else:
# The base object has an unsupported type.
return msg.has_no_member(typ, name, node)
Type analyse_member_var_access(str name, Instance itype, TypeInfo info,
Context node, bool is_lvalue, bool is_super,
MessageBuilder msg):
"""Analyse member access that does not target a method.
This is logically part of analyse_member_access and the arguments are
similar.
"""
# It was not a method. Try looking up a variable.
v = lookup_member_var_or_accessor(info, name, is_lvalue)
if isinstance(v, Var):
# Found a member variable.
var = (Var)v
itype = map_instance_to_supertype(itype, var.info)
# FIX what if more than one?
if var.type:
return expand_type_by_instance(var.type, itype)
else:
if not var.is_ready:
msg.cannot_determine_type(var.name(), node)
# Implicit dynamic type.
return Any()
elif isinstance(v, FuncDef):
# Found a getter or a setter.
raise NotImplementedError()
# Could not find the member.
if is_super:
msg.undefined_in_superclass(name, node)
return Any()
else:
return msg.has_no_member(itype, name, node)
AccessorNode lookup_member_var_or_accessor(TypeInfo info, str name,
bool is_lvalue):
"""Find the attribute/accessor node that refers to a member of a type."""
if is_lvalue:
return info.get_var_or_setter(name)
else:
return info.get_var_or_getter(name)