From 54f099aa32aace8be33109261b61c6c19f1d4d28 Mon Sep 17 00:00:00 2001 From: dot Date: Sat, 10 Oct 2026 23:21:39 +0900 Subject: [PATCH] Canonicalize nested template format specs in annotations Use the existing f-string body unparser for template interpolation format specs, while preserving the outer interpolation source text. Add compile-only Python annotation-constant coverage. Assisted-by: OpenAI Codex:model identifier unavailable --- crates/codegen/src/unparse.rs | 2 +- extra_tests/snippets/syntax_annotations.py | 12 ++++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/crates/codegen/src/unparse.rs b/crates/codegen/src/unparse.rs index 3ada6644cbb..868f1b1bab1 100644 --- a/crates/codegen/src/unparse.rs +++ b/crates/codegen/src/unparse.rs @@ -814,7 +814,7 @@ impl<'a, 'b, 'c> Unparser<'a, 'b, 'c> { if let Some(format_spec) = &interpolation.format_spec { self.p(":")?; - self.unparse_tstring_body(&format_spec.elements)?; + self.unparse_fstring_body(&format_spec.elements)?; } self.p("}") diff --git a/extra_tests/snippets/syntax_annotations.py b/extra_tests/snippets/syntax_annotations.py index 0f78bdc2be1..9b7d0c0ce89 100644 --- a/extra_tests/snippets/syntax_annotations.py +++ b/extra_tests/snippets/syntax_annotations.py @@ -8,3 +8,15 @@ def func(s: str) -> int: # The order of type hints matters for certain functions # e.g. functools.singledispatch assert list(hints.items()) == [('s', str), ('return', int)] + + +# Template format specs use canonical expression text inside future annotations. +for template, expected in ( + ("t'{value:04}'", "t'{value:04}'"), + ("t'{value:{(width)}}'", "t'{value:{width}}'"), + ("t'{(value):{(width)!r}}'", "t'{(value):{width!r}}'"), +): + annotation_code = compile( + "from __future__ import annotations\nx: " + template, "", "exec" + ) + assert expected in annotation_code.co_consts