Special case for f-string, which is translated into str.join() in mypy AST. This specializer optimizes simplest f-strings which don't contain any format operation.
(builder: IRBuilder, expr: CallExpr, callee: RefExpr)
| 870 | |
| 871 | @specialize_function("join", str_rprimitive) |
| 872 | def translate_fstring(builder: IRBuilder, expr: CallExpr, callee: RefExpr) -> Value | None: |
| 873 | """Special case for f-string, which is translated into str.join() |
| 874 | in mypy AST. |
| 875 | |
| 876 | This specializer optimizes simplest f-strings which don't contain |
| 877 | any format operation. |
| 878 | """ |
| 879 | if ( |
| 880 | isinstance(callee, MemberExpr) |
| 881 | and isinstance(callee.expr, StrExpr) |
| 882 | and callee.expr.value == "" |
| 883 | and expr.arg_kinds == [ARG_POS] |
| 884 | and isinstance(expr.args[0], ListExpr) |
| 885 | ): |
| 886 | for item in expr.args[0].items: |
| 887 | if isinstance(item, StrExpr): |
| 888 | continue |
| 889 | elif isinstance(item, CallExpr): |
| 890 | if not isinstance(item.callee, MemberExpr) or item.callee.name != "format": |
| 891 | return None |
| 892 | elif ( |
| 893 | not isinstance(item.callee.expr, StrExpr) or item.callee.expr.value != "{:{}}" |
| 894 | ): |
| 895 | return None |
| 896 | |
| 897 | if not isinstance(item.args[1], StrExpr) or item.args[1].value != "": |
| 898 | return None |
| 899 | else: |
| 900 | return None |
| 901 | |
| 902 | format_ops = [] |
| 903 | exprs: list[Expression] = [] |
| 904 | |
| 905 | for item in expr.args[0].items: |
| 906 | if isinstance(item, StrExpr) and item.value != "": |
| 907 | format_ops.append(FormatOp.STR) |
| 908 | exprs.append(item) |
| 909 | elif isinstance(item, CallExpr): |
| 910 | format_ops.append(FormatOp.STR) |
| 911 | exprs.append(item.args[0]) |
| 912 | |
| 913 | def get_literal_str(expr: Expression) -> str | None: |
| 914 | if isinstance(expr, StrExpr): |
| 915 | return expr.value |
| 916 | elif isinstance(expr, RefExpr) and isinstance(expr.node, Var) and expr.node.is_final: |
| 917 | final_value = expr.node.final_value |
| 918 | if final_value is not None: |
| 919 | return str(final_value) |
| 920 | return None |
| 921 | |
| 922 | for i in range(len(exprs) - 1): |
| 923 | while ( |
| 924 | len(exprs) >= i + 2 |
| 925 | and (first := get_literal_str(exprs[i])) is not None |
| 926 | and (second := get_literal_str(exprs[i + 1])) is not None |
| 927 | ): |
| 928 | exprs = [*exprs[:i], StrExpr(first + second), *exprs[i + 2 :]] |
| 929 | format_ops = [*format_ops[:i], FormatOp.STR, *format_ops[i + 2 :]] |
nothing calls this directly
no test coverage detected
searching dependent graphs…