diff --git a/mypy/checkstrformat.py b/mypy/checkstrformat.py index 45075bd37552..4cbbc86d1236 100644 --- a/mypy/checkstrformat.py +++ b/mypy/checkstrformat.py @@ -20,6 +20,7 @@ import mypy.errorcodes as codes from mypy import message_registry from mypy.checker_shared import TypeCheckerSharedApi +from mypy.constant_fold import constant_fold_expr from mypy.errors import Errors from mypy.maptype import map_instance_to_supertype from mypy.messages import MessageBuilder @@ -1005,8 +1006,12 @@ def check_expr(expr: Expression) -> None: and len(expr.value) != 1 ): self.msg.requires_int_or_single_byte(context) - elif isinstance(expr, (StrExpr, BytesExpr)) and len(expr.value) != 1: - self.msg.requires_int_or_char(context) + elif isinstance(folded := constant_fold_expr(expr, ""), str): + if len(folded) != 1: + self.msg.requires_int_or_char(context) + elif isinstance(expr, BytesExpr): + if len(expr.value) != 1: + self.msg.requires_int_or_char(context) return check_expr, check_type diff --git a/test-data/unit/check-formatting.test b/test-data/unit/check-formatting.test index b5b37f8d2976..374f0dcc1a52 100644 --- a/test-data/unit/check-formatting.test +++ b/test-data/unit/check-formatting.test @@ -103,14 +103,27 @@ a = None # type: Any [typing fixtures/typing-medium.pyi] [case testStringInterpolationC] +from typing import Final + +final_int: Final = 1 +final_float: Final = 1.0 +final_char_string: Final = 's' +final_empty_string: Final = '' +final_string: Final = 'ab' + '%c' % 1 -'%c' % 1.0 # E: "%c" requires int or char (expression has type "float") +'%c' % final_int +'%c' % 1.0 # E: "%c" requires int or char (expression has type "float") +'%c' % final_float # E: "%c" requires int or char (expression has type "float") '%c' % 's' -'%c' % '' # E: "%c" requires int or char -'%c' % 'ab' # E: "%c" requires int or char -'%c' % b'a' # E: "%c" requires int or char (expression has type "bytes") -'%c' % b'' # E: "%c" requires int or char (expression has type "bytes") -'%c' % b'ab' # E: "%c" requires int or char (expression has type "bytes") +'%c' % final_char_string +'%c' % '' # E: "%c" requires int or char +'%c' % final_empty_string # E: "%c" requires int or char +'%c' % 'ab' # E: "%c" requires int or char +'%c' % final_string # E: "%c" requires int or char +'%c' % b'a' # E: "%c" requires int or char (expression has type "bytes") +'%c' % b'' # E: "%c" requires int or char (expression has type "bytes") +'%c' % b'ab' # E: "%c" requires int or char (expression has type "bytes") [builtins fixtures/primitives.pyi] [case testStringInterpolationMappingTypes]