From ea5348e7317937db4c9fea2e1f2e777ce6ff7d40 Mon Sep 17 00:00:00 2001 From: BobTheBuidler Date: Thu, 18 Sep 2025 20:26:07 +0000 Subject: [PATCH 1/8] feat: constant fold f-strings --- mypy/constant_fold.py | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/mypy/constant_fold.py b/mypy/constant_fold.py index 4582b2a7396d..3294f9b8a782 100644 --- a/mypy/constant_fold.py +++ b/mypy/constant_fold.py @@ -17,6 +17,9 @@ StrExpr, UnaryExpr, Var, + CallExpr, + MemberExpr, + ListExpr, ) # All possible result types of constant folding @@ -73,6 +76,23 @@ def constant_fold_expr(expr: Expression, cur_mod_id: str) -> ConstantValue | Non value = constant_fold_expr(expr.expr, cur_mod_id) if value is not None: return constant_fold_unary_op(expr.op, value) + # --- f-string constant folding --- + elif ( + isinstance(expr, CallExpr) + and isinstance(callee := expr.callee, MemberExpr) + and isinstance(callee.expr, StrExpr) + and callee.expr.value == "" + and callee.name == "join" + and len(args := expr.args) == 1 + and isinstance(arg := args[0], ListExpr) + ): + folded_items = [] + for item in arg.items: + val = constant_fold_expr(item, cur_mod_id) + if not isinstance(val, str): + return None + folded_items.append(val) + return "".join(folded_items) return None @@ -185,3 +205,7 @@ def constant_fold_unary_op(op: str, value: ConstantValue) -> int | float | None: elif op == "+" and isinstance(value, (int, float)): return value return None + + +def is_f_string_expr(expr: Expression) -> TypeGuard[CallExpr]: + \ No newline at end of file From e8d6c771e846ca6d3d6631ce7a213712e9d46f02 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Thu, 18 Sep 2025 20:46:16 +0000 Subject: [PATCH 2/8] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- mypy/constant_fold.py | 1 - 1 file changed, 1 deletion(-) diff --git a/mypy/constant_fold.py b/mypy/constant_fold.py index 3294f9b8a782..dd96c610f811 100644 --- a/mypy/constant_fold.py +++ b/mypy/constant_fold.py @@ -208,4 +208,3 @@ def constant_fold_unary_op(op: str, value: ConstantValue) -> int | float | None: def is_f_string_expr(expr: Expression) -> TypeGuard[CallExpr]: - \ No newline at end of file From 5f1162ee50680149e35f08cd6d7997edb48cd203 Mon Sep 17 00:00:00 2001 From: BobTheBuidler Date: Thu, 18 Sep 2025 20:49:01 +0000 Subject: [PATCH 3/8] support TupleExpr --- mypy/constant_fold.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/mypy/constant_fold.py b/mypy/constant_fold.py index 3294f9b8a782..b2e0d91ca62c 100644 --- a/mypy/constant_fold.py +++ b/mypy/constant_fold.py @@ -20,6 +20,7 @@ CallExpr, MemberExpr, ListExpr, + TupleExpr, ) # All possible result types of constant folding @@ -76,15 +77,14 @@ def constant_fold_expr(expr: Expression, cur_mod_id: str) -> ConstantValue | Non value = constant_fold_expr(expr.expr, cur_mod_id) if value is not None: return constant_fold_unary_op(expr.op, value) - # --- f-string constant folding --- + # --- partial str.join support in preparation for f-string constant folding --- elif ( isinstance(expr, CallExpr) and isinstance(callee := expr.callee, MemberExpr) and isinstance(callee.expr, StrExpr) - and callee.expr.value == "" and callee.name == "join" and len(args := expr.args) == 1 - and isinstance(arg := args[0], ListExpr) + and isinstance(arg := args[0], (ListExpr, TupleExpr)) ): folded_items = [] for item in arg.items: @@ -92,7 +92,7 @@ def constant_fold_expr(expr: Expression, cur_mod_id: str) -> ConstantValue | Non if not isinstance(val, str): return None folded_items.append(val) - return "".join(folded_items) + return callee.expr.value.join(folded_items) return None From 91a19076e64f2a767ca1fdee9e57b8baaa22761e Mon Sep 17 00:00:00 2001 From: BobTheBuidler Date: Thu, 18 Sep 2025 20:51:51 +0000 Subject: [PATCH 4/8] fix accident commit --- mypy/constant_fold.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/mypy/constant_fold.py b/mypy/constant_fold.py index b2e0d91ca62c..1176d110260c 100644 --- a/mypy/constant_fold.py +++ b/mypy/constant_fold.py @@ -205,7 +205,3 @@ def constant_fold_unary_op(op: str, value: ConstantValue) -> int | float | None: elif op == "+" and isinstance(value, (int, float)): return value return None - - -def is_f_string_expr(expr: Expression) -> TypeGuard[CallExpr]: - \ No newline at end of file From 96c25748d3895f911ac0295d9c25b8a8d7325e43 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Thu, 18 Sep 2025 20:54:04 +0000 Subject: [PATCH 5/8] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- mypy/constant_fold.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/mypy/constant_fold.py b/mypy/constant_fold.py index 1176d110260c..621cc7b0df63 100644 --- a/mypy/constant_fold.py +++ b/mypy/constant_fold.py @@ -8,19 +8,19 @@ from typing import Final, Union from mypy.nodes import ( + CallExpr, ComplexExpr, Expression, FloatExpr, IntExpr, + ListExpr, + MemberExpr, NameExpr, OpExpr, StrExpr, + TupleExpr, UnaryExpr, Var, - CallExpr, - MemberExpr, - ListExpr, - TupleExpr, ) # All possible result types of constant folding From e84c21c10da6f6d31a1ddf2a9915203a453104bf Mon Sep 17 00:00:00 2001 From: BobTheBuidler Date: Thu, 18 Sep 2025 21:12:17 +0000 Subject: [PATCH 6/8] feat: better folding --- mypy/constant_fold.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mypy/constant_fold.py b/mypy/constant_fold.py index 621cc7b0df63..7a5f207bb820 100644 --- a/mypy/constant_fold.py +++ b/mypy/constant_fold.py @@ -81,7 +81,7 @@ def constant_fold_expr(expr: Expression, cur_mod_id: str) -> ConstantValue | Non elif ( isinstance(expr, CallExpr) and isinstance(callee := expr.callee, MemberExpr) - and isinstance(callee.expr, StrExpr) + and isinstance(folded_callee := constant_fold_expr(callee.expr, cur_mod_id), str) and callee.name == "join" and len(args := expr.args) == 1 and isinstance(arg := args[0], (ListExpr, TupleExpr)) @@ -92,7 +92,7 @@ def constant_fold_expr(expr: Expression, cur_mod_id: str) -> ConstantValue | Non if not isinstance(val, str): return None folded_items.append(val) - return callee.expr.value.join(folded_items) + return folded_callee.join(folded_items) return None From b67f10a858b33f3cadc00eb7e453ff72af366128 Mon Sep 17 00:00:00 2001 From: BobTheBuidler Date: Thu, 18 Sep 2025 21:15:03 +0000 Subject: [PATCH 7/8] feat: implement constant folding for str.format --- mypy/constant_fold.py | 34 +++++++++++++++++++++++----------- 1 file changed, 23 insertions(+), 11 deletions(-) diff --git a/mypy/constant_fold.py b/mypy/constant_fold.py index 7a5f207bb820..0900886bb975 100644 --- a/mypy/constant_fold.py +++ b/mypy/constant_fold.py @@ -77,22 +77,34 @@ def constant_fold_expr(expr: Expression, cur_mod_id: str) -> ConstantValue | Non value = constant_fold_expr(expr.expr, cur_mod_id) if value is not None: return constant_fold_unary_op(expr.op, value) - # --- partial str.join support in preparation for f-string constant folding --- + # --- f-string requires partial support for both str.join and str.format --- elif ( isinstance(expr, CallExpr) and isinstance(callee := expr.callee, MemberExpr) and isinstance(folded_callee := constant_fold_expr(callee.expr, cur_mod_id), str) - and callee.name == "join" - and len(args := expr.args) == 1 - and isinstance(arg := args[0], (ListExpr, TupleExpr)) ): - folded_items = [] - for item in arg.items: - val = constant_fold_expr(item, cur_mod_id) - if not isinstance(val, str): - return None - folded_items.append(val) - return folded_callee.join(folded_items) + # --- partial str.join constant folding --- + if ( + callee.name == "join" + and len(args := expr.args) == 1 + and isinstance(arg := args[0], (ListExpr, TupleExpr)) + ): + folded_items: list[str] = [] + for item in arg.items: + val = constant_fold_expr(item, cur_mod_id) + if not isinstance(val, str): + return None + folded_items.append(val) + return folded_callee.join(folded_items) + # --- str.format constant folding + elif callee.name == "format": + folded_args: list[str] = [] + for arg in expr.args: + arg_val = constant_fold_expr(arg, cur_mod_id) + if arg_val is None: + return None + folded_args.append(arg_val) + return folded_callee.format(*folded_args) return None From 201a683160efd9f3a9c62b1baacc68bf111635c2 Mon Sep 17 00:00:00 2001 From: BobTheBuidler <70677534+BobTheBuidler@users.noreply.github.com> Date: Thu, 18 Sep 2025 18:23:07 -0400 Subject: [PATCH 8/8] fix mypy err --- mypy/constant_fold.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mypy/constant_fold.py b/mypy/constant_fold.py index 0900886bb975..3b621c3b4129 100644 --- a/mypy/constant_fold.py +++ b/mypy/constant_fold.py @@ -98,7 +98,7 @@ def constant_fold_expr(expr: Expression, cur_mod_id: str) -> ConstantValue | Non return folded_callee.join(folded_items) # --- str.format constant folding elif callee.name == "format": - folded_args: list[str] = [] + folded_args: list[ConstantValue] = [] for arg in expr.args: arg_val = constant_fold_expr(arg, cur_mod_id) if arg_val is None: