Skip to content

Commit 79ffa84

Browse files
committed
feat: constant fold common builtin call exprs
1 parent b67f10a commit 79ffa84

File tree

1 file changed

+87
-28
lines changed

1 file changed

+87
-28
lines changed

mypy/constant_fold.py

Lines changed: 87 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
from typing import Final, Union
99

1010
from mypy.nodes import (
11+
ArgKind,
1112
CallExpr,
1213
ComplexExpr,
1314
Expression,
@@ -77,34 +78,8 @@ def constant_fold_expr(expr: Expression, cur_mod_id: str) -> ConstantValue | Non
7778
value = constant_fold_expr(expr.expr, cur_mod_id)
7879
if value is not None:
7980
return constant_fold_unary_op(expr.op, value)
80-
# --- f-string requires partial support for both str.join and str.format ---
81-
elif (
82-
isinstance(expr, CallExpr)
83-
and isinstance(callee := expr.callee, MemberExpr)
84-
and isinstance(folded_callee := constant_fold_expr(callee.expr, cur_mod_id), str)
85-
):
86-
# --- partial str.join constant folding ---
87-
if (
88-
callee.name == "join"
89-
and len(args := expr.args) == 1
90-
and isinstance(arg := args[0], (ListExpr, TupleExpr))
91-
):
92-
folded_items: list[str] = []
93-
for item in arg.items:
94-
val = constant_fold_expr(item, cur_mod_id)
95-
if not isinstance(val, str):
96-
return None
97-
folded_items.append(val)
98-
return folded_callee.join(folded_items)
99-
# --- str.format constant folding
100-
elif callee.name == "format":
101-
folded_args: list[str] = []
102-
for arg in expr.args:
103-
arg_val = constant_fold_expr(arg, cur_mod_id)
104-
if arg_val is None:
105-
return None
106-
folded_args.append(arg_val)
107-
return folded_callee.format(*folded_args)
81+
elif isinstance(expr, CallExpr):
82+
return constant_fold_call_expr(expr, cur_mod_id)
10883
return None
10984

11085

@@ -217,3 +192,87 @@ def constant_fold_unary_op(op: str, value: ConstantValue) -> int | float | None:
217192
elif op == "+" and isinstance(value, (int, float)):
218193
return value
219194
return None
195+
196+
197+
foldable_builtins = {
198+
"builtins.str": str,
199+
"builtins.int": int,
200+
"builtins.bool": bool,
201+
"builtins.float": float,
202+
"builtins.complex": complex,
203+
"builtins.repr": repr,
204+
"builtins.len": len,
205+
"builtins.hasattr": hasattr,
206+
"builtins.hex": hex,
207+
"builtins.hash": hash,
208+
"builtins.min": min,
209+
"builtins.max": max,
210+
"builtins.oct": oct,
211+
"builtins.pow": pow,
212+
"builtins.round": round,
213+
"builtins.abs": abs,
214+
"builtins.ascii": ascii,
215+
"builtins.bin": bin,
216+
"builtins.chr": chr,
217+
}
218+
219+
def constant_fold_call_expr(expr: CallExpr, cur_mod_id: str, foldable_builtins=foldable_builtins) -> ConstantValue | None:
220+
callee = expr.callee
221+
if isinstance(callee, NameExpr):
222+
func = foldable_builtins.get(callee.fullname)
223+
if func is None:
224+
return None
225+
226+
folded_args = []
227+
for arg in expr.args:
228+
val = constant_fold_expr(arg, cur_mod_id)
229+
if val is None:
230+
return None
231+
folded_args.append(arg)
232+
233+
args = []
234+
kwargs = {}
235+
for folded_arg, arg_kind, arg_name in zip(folded_args, expr.arg_kinds, expr.arg_names):
236+
try:
237+
if arg_kind == ArgKind.ARG_POS:
238+
args.append(folded_arg)
239+
elif arg_kind == ArgKind.ARG_NAMED:
240+
kwargs[arg_name] = folded_arg
241+
elif arg_kind == ArgKind.ARG_STAR:
242+
args.extend(folded_arg)
243+
elif arg_kind == ArgKind.ARG_STAR2:
244+
kwargs.update(folded_arg)
245+
except:
246+
return None
247+
248+
try:
249+
return func(*args, **kwargs)
250+
except:
251+
return None
252+
# --- f-string requires partial support for both str.join and str.format ---
253+
elif (
254+
isinstance(callee, MemberExpr)
255+
and isinstance(folded_callee := constant_fold_expr(callee.expr, cur_mod_id), str)
256+
):
257+
# --- partial str.join constant folding ---
258+
if (
259+
callee.name == "join"
260+
and len(args := expr.args) == 1
261+
and isinstance(arg := args[0], (ListExpr, TupleExpr))
262+
):
263+
folded_items: list[str] = []
264+
for item in arg.items:
265+
val = constant_fold_expr(item, cur_mod_id)
266+
if not isinstance(val, str):
267+
return None
268+
folded_items.append(val)
269+
return folded_callee.join(folded_items)
270+
# --- str.format constant folding
271+
elif callee.name == "format":
272+
folded_args: list[str] = []
273+
for arg in expr.args:
274+
arg_val = constant_fold_expr(arg, cur_mod_id)
275+
if arg_val is None:
276+
return None
277+
folded_args.append(arg_val)
278+
return folded_callee.format(*folded_args)

0 commit comments

Comments
 (0)