From fb7e4e4b04a7d1d783e440370f4601c70d5f1f16 Mon Sep 17 00:00:00 2001 From: Zanie Date: Wed, 1 Nov 2023 11:12:34 -0500 Subject: [PATCH 1/3] Fix bug where `PLE1307` was raised when formatting `%c` with characters Closes https://github.com/astral-sh/ruff/issues/8406 --- .../fixtures/pylint/bad_string_format_type.py | 25 +++++++++++++------ .../pylint/rules/bad_string_format_type.rs | 19 +++++++++++--- 2 files changed, 32 insertions(+), 12 deletions(-) diff --git a/crates/ruff_linter/resources/test/fixtures/pylint/bad_string_format_type.py b/crates/ruff_linter/resources/test/fixtures/pylint/bad_string_format_type.py index 3fe6722401507..5502645c66e99 100644 --- a/crates/ruff_linter/resources/test/fixtures/pylint/bad_string_format_type.py +++ b/crates/ruff_linter/resources/test/fixtures/pylint/bad_string_format_type.py @@ -26,9 +26,7 @@ "%d %s %f" % VALUES_TO_FORMAT "%s" % "1" "%s %s %s" % ("1", 2, 3.5) -print("%d %d" - % -(1, 1.1)) +print("%d %d" % (1, 1.1)) "%s" % 1 "%d" % 1 "%f" % 1 @@ -49,11 +47,22 @@ "%d %d" % "1" "%d" "%d" % "1" "-%f" % time.time() -"%r" % (object['dn'],) -r'\%03o' % (ord(c),) -('%02X' % int(_) for _ in o) +"%r" % (object["dn"],) +r"\%03o" % (ord(c),) +("%02X" % int(_) for _ in o) "%s;range=%d-*" % (attr, upper + 1) "%d" % (len(foo),) -'(%r, %r, %r, %r)' % (hostname, address, username, '$PASSWORD') -'%r' % ({'server_school_roles': server_school_roles, 'is_school_multiserver_domain': is_school_multiserver_domain}, ) +"(%r, %r, %r, %r)" % (hostname, address, username, "$PASSWORD") +"%r" % ( + { + "server_school_roles": server_school_roles, + "is_school_multiserver_domain": is_school_multiserver_domain, + }, +) "%d" % (1 if x > 0 else 2) + +# Special cases for %c allowing single character strings +# https://github.com/astral-sh/ruff/issues/8406 +"%c" % ("x",) +"%c" % "x" +"%c" & "œ" diff --git a/crates/ruff_linter/src/rules/pylint/rules/bad_string_format_type.rs b/crates/ruff_linter/src/rules/pylint/rules/bad_string_format_type.rs index 0ea6884da096a..4d62c785de583 100644 --- a/crates/ruff_linter/src/rules/pylint/rules/bad_string_format_type.rs +++ b/crates/ruff_linter/src/rules/pylint/rules/bad_string_format_type.rs @@ -119,12 +119,23 @@ fn collect_specs(formats: &[CFormatStrOrBytes]) -> Vec<&CFormatSpec> { /// Return `true` if the format string is equivalent to the constant type fn equivalent(format: &CFormatSpec, value: &Expr) -> bool { - let format = FormatType::from(format.format_char); + let format_type = FormatType::from(format.format_char); match ResolvedPythonType::from(value) { - ResolvedPythonType::Atom(atom) => format.is_compatible_with(atom), - ResolvedPythonType::Union(atoms) => { - atoms.iter().all(|atom| format.is_compatible_with(*atom)) + ResolvedPythonType::Atom(atom) => { + // Special case where `%c` allows single character strings to be formatted + if format.format_char == 'c' { + if let Expr::StringLiteral(string) = value { + let mut chars = string.chars(); + if chars.next().is_some() && chars.next().is_none() { + return true; + } + } + } + format_type.is_compatible_with(atom) } + ResolvedPythonType::Union(atoms) => atoms + .iter() + .all(|atom| format_type.is_compatible_with(*atom)), ResolvedPythonType::Unknown => true, ResolvedPythonType::TypeError => true, } From 0264c8a4bf9ed2e958d37fc29ca35ab1065bb8b6 Mon Sep 17 00:00:00 2001 From: Zanie Date: Wed, 1 Nov 2023 11:16:13 -0500 Subject: [PATCH 2/3] Revert formatting changes --- .../fixtures/pylint/bad_string_format_type.py | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/crates/ruff_linter/resources/test/fixtures/pylint/bad_string_format_type.py b/crates/ruff_linter/resources/test/fixtures/pylint/bad_string_format_type.py index 5502645c66e99..8fa3071991fed 100644 --- a/crates/ruff_linter/resources/test/fixtures/pylint/bad_string_format_type.py +++ b/crates/ruff_linter/resources/test/fixtures/pylint/bad_string_format_type.py @@ -26,7 +26,9 @@ "%d %s %f" % VALUES_TO_FORMAT "%s" % "1" "%s %s %s" % ("1", 2, 3.5) -print("%d %d" % (1, 1.1)) +print("%d %d" + % +(1, 1.1)) "%s" % 1 "%d" % 1 "%f" % 1 @@ -47,18 +49,13 @@ "%d %d" % "1" "%d" "%d" % "1" "-%f" % time.time() -"%r" % (object["dn"],) -r"\%03o" % (ord(c),) -("%02X" % int(_) for _ in o) +"%r" % (object['dn'],) +r'\%03o' % (ord(c),) +('%02X' % int(_) for _ in o) "%s;range=%d-*" % (attr, upper + 1) "%d" % (len(foo),) -"(%r, %r, %r, %r)" % (hostname, address, username, "$PASSWORD") -"%r" % ( - { - "server_school_roles": server_school_roles, - "is_school_multiserver_domain": is_school_multiserver_domain, - }, -) +'(%r, %r, %r, %r)' % (hostname, address, username, '$PASSWORD') +'%r' % ({'server_school_roles': server_school_roles, 'is_school_multiserver_domain': is_school_multiserver_domain}, ) "%d" % (1 if x > 0 else 2) # Special cases for %c allowing single character strings From d7d3e449fb77f0544e622f7bd73f993d5743d0c3 Mon Sep 17 00:00:00 2001 From: Zanie Blue Date: Wed, 1 Nov 2023 23:28:34 -0500 Subject: [PATCH 3/3] Fix typo Co-authored-by: Dhruv Manilawala --- .../resources/test/fixtures/pylint/bad_string_format_type.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/ruff_linter/resources/test/fixtures/pylint/bad_string_format_type.py b/crates/ruff_linter/resources/test/fixtures/pylint/bad_string_format_type.py index 8fa3071991fed..e95b8ed9a6ded 100644 --- a/crates/ruff_linter/resources/test/fixtures/pylint/bad_string_format_type.py +++ b/crates/ruff_linter/resources/test/fixtures/pylint/bad_string_format_type.py @@ -62,4 +62,4 @@ # https://github.com/astral-sh/ruff/issues/8406 "%c" % ("x",) "%c" % "x" -"%c" & "œ" +"%c" % "œ"