Skip to content

Commit 60614c5

Browse files
committed
Make FuncItem.arguments optional
When FuncDef is deserialized we don't reconstruct the arguments. Previous code was deleting the attribute, leading to #11899; instead, always set the attribute, maybe to None, and mypy can remind us to check for not None before use.
1 parent e04a4b4 commit 60614c5

16 files changed

+46
-17
lines changed

mypy/checker.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1005,6 +1005,7 @@ def check_func_def(self, defn: FuncItem, typ: CallableType, name: Optional[str])
10051005
arg_type = self.named_generic_type('builtins.dict',
10061006
[self.str_type(),
10071007
arg_type])
1008+
assert item.arguments is not None
10081009
item.arguments[i].variable.type = arg_type
10091010

10101011
# Type check initialization expressions.
@@ -1051,6 +1052,7 @@ def check_func_def(self, defn: FuncItem, typ: CallableType, name: Optional[str])
10511052
self.binder = old_binder
10521053

10531054
def check_default_args(self, item: FuncItem, body_is_trivial: bool) -> None:
1055+
assert item.arguments is not None
10541056
for arg in item.arguments:
10551057
if arg.initializer is None:
10561058
continue

mypy/checkexpr.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3618,6 +3618,7 @@ def infer_lambda_type_using_context(self, e: LambdaExpr) -> Tuple[Optional[Calla
36183618
# See https://github.com/python/mypy/issues/9927
36193619
return None, None
36203620

3621+
assert e.arguments is not None
36213622
arg_kinds = [arg.kind for arg in e.arguments]
36223623

36233624
if callable_ctx.is_ellipsis_args or ctx.param_spec() is not None:

mypy/messages.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1950,10 +1950,12 @@ def [T <: int] f(self, x: int, y: T) -> None
19501950
s += ' = ...'
19511951

19521952
# If we got a "special arg" (i.e: self, cls, etc...), prepend it to the arg list
1953-
if isinstance(tp.definition, FuncDef) and tp.definition.name is not None:
1953+
if isinstance(tp.definition, FuncDef) and \
1954+
tp.definition.name is not None and \
1955+
tp.definition.arguments is not None:
19541956
definition_args = [arg.variable.name for arg in tp.definition.arguments]
19551957
if definition_args and tp.arg_names != definition_args \
1956-
and len(definition_args) > 0 and definition_args[0]:
1958+
and len(definition_args) > 0 and definition_args[0]:
19571959
if s:
19581960
s = ', ' + s
19591961
s = definition_args[0] + s

mypy/nodes.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -642,7 +642,7 @@ def set_line(self,
642642
class FuncItem(FuncBase):
643643
"""Base class for nodes usable as overloaded function items."""
644644

645-
__slots__ = ('arguments', # Note that can be None if deserialized (type is a lie!)
645+
__slots__ = ('arguments', # Note that can be None if deserialized
646646
'arg_names', # Names of arguments
647647
'arg_kinds', # Kinds of arguments
648648
'min_args', # Minimum number of arguments
@@ -658,14 +658,14 @@ class FuncItem(FuncBase):
658658
'expanded', # Variants of function with type variables with values expanded
659659
)
660660

661-
__deletable__ = ('arguments', 'max_pos', 'min_args')
661+
__deletable__ = ('max_pos', 'min_args')
662662

663663
def __init__(self,
664664
arguments: List[Argument],
665665
body: 'Block',
666666
typ: 'Optional[mypy.types.FunctionLike]' = None) -> None:
667667
super().__init__()
668-
self.arguments = arguments
668+
self.arguments: Optional[List[Argument]] = arguments
669669
self.arg_names = [None if arg.pos_only else arg.variable.name for arg in arguments]
670670
self.arg_kinds: List[ArgKind] = [arg.kind for arg in self.arguments]
671671
self.max_pos: int = (
@@ -693,6 +693,7 @@ def set_line(self,
693693
column: Optional[int] = None,
694694
end_line: Optional[int] = None) -> None:
695695
super().set_line(target, column, end_line)
696+
assert self.arguments is not None
696697
for arg in self.arguments:
697698
arg.set_line(self.line, self.column, self.end_line)
698699

@@ -772,7 +773,7 @@ def deserialize(cls, data: JsonDict) -> 'FuncDef':
772773
ret.arg_names = data['arg_names']
773774
ret.arg_kinds = [ArgKind(x) for x in data['arg_kinds']]
774775
# Leave these uninitialized so that future uses will trigger an error
775-
del ret.arguments
776+
ret.arguments = None
776777
del ret.max_pos
777778
del ret.min_args
778779
return ret

mypy/renaming.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@ def visit_func_def(self, fdef: FuncDef) -> None:
8686
self.reject_redefinition_of_vars_in_scope()
8787

8888
with self.enter_scope(FUNCTION), self.enter_block():
89+
assert fdef.arguments is not None
8990
for arg in fdef.arguments:
9091
name = arg.variable.name
9192
# 'self' can't be redefined since it's special as it allows definition of
@@ -442,6 +443,7 @@ def visit_mypy_file(self, file_node: MypyFile) -> None:
442443
def visit_func_def(self, fdef: FuncDef) -> None:
443444
self.reject_redefinition_of_vars_in_scope()
444445
with self.enter_scope():
446+
assert fdef.arguments is not None
445447
for arg in fdef.arguments:
446448
self.record_skipped(arg.variable.name)
447449
super().visit_func_def(fdef)

mypy/semanal.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -607,6 +607,7 @@ def visit_func_def(self, defn: FuncDef) -> None:
607607
self.statement = defn
608608

609609
# Visit default values because they may contain assignment expressions.
610+
assert defn.arguments is not None
610611
for arg in defn.arguments:
611612
if arg.initializer:
612613
arg.initializer.accept(self)
@@ -980,6 +981,7 @@ def add_function_to_symbol_table(self, func: Union[FuncDef, OverloadedFuncDef])
980981
def analyze_arg_initializers(self, defn: FuncItem) -> None:
981982
with self.tvar_scope_frame(self.tvar_scope.method_frame()):
982983
# Analyze default arguments
984+
assert defn.arguments is not None
983985
for arg in defn.arguments:
984986
if arg.initializer:
985987
arg.initializer.accept(self)
@@ -993,6 +995,7 @@ def analyze_function_body(self, defn: FuncItem) -> None:
993995
a.bind_function_type_variables(cast(CallableType, defn.type), defn)
994996
self.function_stack.append(defn)
995997
with self.enter(defn):
998+
assert defn.arguments is not None
996999
for arg in defn.arguments:
9971000
self.add_local(arg.variable, defn)
9981001

@@ -1021,6 +1024,7 @@ def check_classvar_in_signature(self, typ: ProperType) -> None:
10211024
def check_function_signature(self, fdef: FuncItem) -> None:
10221025
sig = fdef.type
10231026
assert isinstance(sig, CallableType)
1027+
assert fdef.arguments is not None
10241028
if len(sig.arg_types) < len(fdef.arguments):
10251029
self.fail('Type signature has too few arguments', fdef)
10261030
# Add dummy Any arguments to prevent crashes later.
@@ -1073,6 +1077,7 @@ def visit_decorator(self, dec: Decorator) -> None:
10731077
elif refers_to_fullname(d, 'functools.cached_property'):
10741078
dec.var.is_settable_property = True
10751079
self.check_decorated_function_is_method('property', dec)
1080+
assert dec.func.arguments is not None
10761081
if len(dec.func.arguments) > 1:
10771082
self.fail('Too many arguments', dec.func)
10781083
elif refers_to_fullname(d, 'typing.no_type_check'):

mypy/strconv.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ def func_helper(self, o: 'mypy.nodes.FuncItem') -> List[object]:
6464
"""
6565
args: List[Union[mypy.nodes.Var, Tuple[str, List[mypy.nodes.Node]]]] = []
6666
extra: List[Tuple[str, List[mypy.nodes.Var]]] = []
67+
assert o.arguments is not None
6768
for arg in o.arguments:
6869
kind: mypy.nodes.ArgKind = arg.kind
6970
if kind.is_required():
@@ -131,6 +132,7 @@ def visit_import_all(self, o: 'mypy.nodes.ImportAll') -> str:
131132
def visit_func_def(self, o: 'mypy.nodes.FuncDef') -> str:
132133
a = self.func_helper(o)
133134
a.insert(0, o.name)
135+
assert o.arguments is not None
134136
arg_kinds = {arg.kind for arg in o.arguments}
135137
if len(arg_kinds & {mypy.nodes.ARG_NAMED, mypy.nodes.ARG_NAMED_OPT}) > 0:
136138
a.insert(1, 'MaxPos({})'.format(o.max_pos))

mypy/stubgen.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -633,6 +633,7 @@ def visit_func_def(self, o: FuncDef, is_abstract: bool = False,
633633
self.add("%s%sdef %s(" % (self._indent, 'async ' if o.is_coroutine else '', o.name))
634634
self.record_name(o.name)
635635
args: List[str] = []
636+
assert o.arguments is not None
636637
for i, arg_ in enumerate(o.arguments):
637638
var = arg_.variable
638639
kind = arg_.kind

mypy/stubtest.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -495,6 +495,7 @@ def get_desc(arg: Any) -> str:
495495
@staticmethod
496496
def from_funcitem(stub: nodes.FuncItem) -> "Signature[nodes.Argument]":
497497
stub_sig: Signature[nodes.Argument] = Signature()
498+
assert stub.arguments is not None
498499
stub_args = maybe_strip_cls(stub.name, stub.arguments)
499500
for stub_arg in stub_args:
500501
if stub_arg.kind.is_positional():
@@ -544,6 +545,7 @@ def from_overloadedfuncdef(stub: nodes.OverloadedFuncDef) -> "Signature[nodes.Ar
544545
all_args: Dict[str, List[Tuple[nodes.Argument, int]]] = {}
545546
for func in map(_resolve_funcitem_from_decorator, stub.items):
546547
assert func is not None
548+
assert func.arguments is not None
547549
args = maybe_strip_cls(stub.name, func.arguments)
548550
for index, arg in enumerate(args):
549551
# For positional-only args, we allow overloads to have different names for the same
@@ -916,9 +918,11 @@ def apply_decorator_to_funcitem(
916918
):
917919
return func
918920
if decorator.fullname == "builtins.classmethod":
921+
assert func.arguments is not None
919922
assert func.arguments[0].variable.name in ("cls", "metacls")
920923
ret = copy.copy(func)
921924
# Remove the cls argument, since it's not present in inspect.signature of classmethods
925+
assert ret.arguments
922926
ret.arguments = ret.arguments[1:]
923927
return ret
924928
# Just give up on any other decorators. After excluding properties, we don't run into

mypy/suggestions.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,7 @@ class ArgUseFinder(TraverserVisitor):
144144
"""
145145
def __init__(self, func: FuncDef, typemap: Dict[Expression, Type]) -> None:
146146
self.typemap = typemap
147+
assert func.arguments is not None
147148
self.arg_types: Dict[SymbolNode, List[Type]] = {arg.variable: [] for arg in func.arguments}
148149

149150
def visit_call_expr(self, o: CallExpr) -> None:
@@ -179,6 +180,7 @@ def test(x, y):
179180
"""
180181
finder = ArgUseFinder(func, typemap)
181182
func.body.accept(finder)
183+
assert func.arguments is not None
182184
return [finder.arg_types[arg.variable] for arg in func.arguments]
183185

184186

@@ -345,6 +347,7 @@ def get_args(self, is_method: bool,
345347
return types
346348

347349
def get_default_arg_types(self, fdef: FuncDef) -> List[Optional[Type]]:
350+
assert fdef.arguments is not None
348351
return [
349352
self.manager.all_types[arg.initializer] if arg.initializer else None
350353
for arg in fdef.arguments
@@ -418,6 +421,7 @@ def get_guesses_from_parent(self, node: FuncDef) -> List[CallableType]:
418421
if pnode and isinstance(pnode.node, (FuncDef, Decorator)):
419422
typ = get_proper_type(pnode.node.type)
420423
# FIXME: Doesn't work right with generic tyeps
424+
assert node.arguments is not None
421425
if isinstance(typ, CallableType) and len(typ.arg_types) == len(node.arguments):
422426
# Return the first thing we find, since it probably doesn't make sense
423427
# to grab things further up in the chain if an earlier parent has it.

mypy/treetransform.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,7 @@ def visit_func_def(self, node: FuncDef) -> FuncDef:
110110
for stmt in node.body.body:
111111
stmt.accept(init)
112112

113+
assert node.arguments is not None
113114
new = FuncDef(node.name,
114115
[self.copy_argument(arg) for arg in node.arguments],
115116
self.block(node.body),
@@ -139,6 +140,7 @@ def visit_func_def(self, node: FuncDef) -> FuncDef:
139140
return new
140141

141142
def visit_lambda_expr(self, node: LambdaExpr) -> LambdaExpr:
143+
assert node.arguments is not None
142144
new = LambdaExpr([self.copy_argument(arg) for arg in node.arguments],
143145
self.block(node.body),
144146
cast(Optional[FunctionLike], self.optional_type(node.type)))
@@ -630,6 +632,7 @@ def __init__(self, transformer: TransformVisitor) -> None:
630632
def visit_func_def(self, node: FuncDef) -> None:
631633
if node not in self.transformer.func_placeholder_map:
632634
# Haven't seen this FuncDef before, so create a placeholder node.
635+
assert node.arguments is not None
633636
self.transformer.func_placeholder_map[node] = FuncDef(
634637
node.name, node.arguments, node.body, None)
635638
super().visit_func_def(node)

mypy/types.py

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1262,16 +1262,13 @@ def __init__(self,
12621262
# after serialization, but it is useful in error messages.
12631263
# TODO: decide how to add more info here (file, line, column)
12641264
# without changing interface hash.
1265-
self.def_extras = {
1266-
'first_arg': (
1267-
definition.arguments[0].variable.name
1268-
if (getattr(definition, 'arguments', None)
1269-
and definition.arg_names
1270-
and definition.info
1271-
and not definition.is_static)
1272-
else None
1273-
),
1274-
}
1265+
first_arg: Optional[str] = None
1266+
if definition.info and not definition.is_static:
1267+
if definition.arguments:
1268+
first_arg = definition.arguments[0].variable.name
1269+
elif definition.arg_names:
1270+
first_arg = definition.arg_names[0]
1271+
self.def_extras = {'first_arg': first_arg}
12751272
else:
12761273
self.def_extras = {}
12771274
self.type_guard = type_guard

mypyc/irbuild/builder.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1184,6 +1184,7 @@ def gen_arg_defaults(builder: IRBuilder) -> None:
11841184
value to the argument.
11851185
"""
11861186
fitem = builder.fn_info.fitem
1187+
assert fitem.arguments is not None
11871188
for arg in fitem.arguments:
11881189
if arg.initializer:
11891190
target = builder.lookup(arg.variable)

mypyc/irbuild/env_class.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,7 @@ def add_args_to_env(builder: IRBuilder,
160160
base: Optional[Union[FuncInfo, ImplicitClass]] = None,
161161
reassign: bool = True) -> None:
162162
fn_info = builder.fn_info
163+
assert fn_info.fitem.arguments is not None
163164
if local:
164165
for arg in fn_info.fitem.arguments:
165166
rtype = builder.type_to_rtype(arg.variable.type)

mypyc/irbuild/function.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,7 @@ def transform_lambda_expr(builder: IRBuilder, expr: LambdaExpr) -> Value:
122122
assert isinstance(typ, CallableType)
123123

124124
runtime_args = []
125+
assert expr.arguments is not None
125126
for arg, arg_type in zip(expr.arguments, typ.arg_types):
126127
arg.variable.type = arg_type
127128
runtime_args.append(
@@ -467,6 +468,7 @@ def calculate_arg_defaults(builder: IRBuilder,
467468
still stored computed on demand).
468469
"""
469470
fitem = fn_info.fitem
471+
assert fitem.arguments is not None
470472
for arg in fitem.arguments:
471473
# Constant values don't get stored but just recomputed
472474
if arg.initializer and not is_constant(arg.initializer):

mypyc/irbuild/mapper.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,7 @@ def fdef_to_sig(self, fdef: FuncDef) -> FuncSignature:
128128
ret = self.type_to_rtype(fdef.type.ret_type)
129129
else:
130130
# Handle unannotated functions
131+
assert fdef.arguments is not None
131132
arg_types = [object_rprimitive for arg in fdef.arguments]
132133
arg_pos_onlys = [arg.pos_only for arg in fdef.arguments]
133134
# We at least know the return type for __init__ methods will be None.
@@ -145,7 +146,7 @@ def fdef_to_sig(self, fdef: FuncDef) -> FuncSignature:
145146
# deserialized FuncDef that lacks arguments. We won't ever
146147
# need to use those inside of a FuncIR, so we just make up
147148
# some crap.
148-
if hasattr(fdef, 'arguments'):
149+
if fdef.arguments is not None:
149150
arg_names = [arg.variable.name for arg in fdef.arguments]
150151
else:
151152
arg_names = [name or '' for name in fdef.arg_names]

0 commit comments

Comments
 (0)