Preserve quotes in F523 fixer (#4836)

This commit is contained in:
Charlie Marsh 2023-06-03 15:53:57 -04:00 committed by GitHub
parent 42c071d302
commit fcacd3cd95
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 94 additions and 6 deletions

View File

@ -17,3 +17,8 @@
"{0}{1}".format(1, *args) # No issues "{0}{1}".format(1, *args) # No issues
"{0}{1}".format(1, 2, *args) # No issues "{0}{1}".format(1, 2, *args) # No issues
"{0}{1}".format(1, 2, 3, *args) # F523 "{0}{1}".format(1, 2, 3, *args) # F523
# With nested quotes
"{''1:{0}}".format(1, 2, 3) # F523
"{\"\"1:{0}}".format(1, 2, 3) # F523
'{""1:{0}}'.format(1, 2, 3) # F523

View File

@ -1,4 +1,5 @@
use anyhow::{bail, Ok, Result}; use anyhow::{anyhow, bail, Ok, Result};
use itertools::Itertools;
use libcst_native::{Codegen, CodegenState, DictElement, Expression}; use libcst_native::{Codegen, CodegenState, DictElement, Expression};
use ruff_text_size::TextRange; use ruff_text_size::TextRange;
use rustpython_format::{ use rustpython_format::{
@ -9,7 +10,7 @@ use rustpython_parser::{lexer, Mode, Tok};
use ruff_diagnostics::Edit; use ruff_diagnostics::Edit;
use ruff_python_ast::source_code::{Locator, Stylist}; use ruff_python_ast::source_code::{Locator, Stylist};
use ruff_python_ast::str::raw_contents; use ruff_python_ast::str::{leading_quote, raw_contents, trailing_quote};
use crate::cst::matchers::{ use crate::cst::matchers::{
match_attribute, match_call_mut, match_dict, match_expression, match_simple_string, match_attribute, match_call_mut, match_dict, match_expression, match_simple_string,
@ -144,7 +145,7 @@ pub(crate) fn remove_unused_positional_arguments_from_format_call(
}); });
let mut min_unused_index = 0; let mut min_unused_index = 0;
for index in unused_arguments { for index in unused_arguments.iter().sorted() {
if *index == min_unused_index { if *index == min_unused_index {
min_unused_index += 1; min_unused_index += 1;
} else { } else {
@ -152,12 +153,36 @@ pub(crate) fn remove_unused_positional_arguments_from_format_call(
} }
} }
let mut new_format_string; // If we removed an argument, we may need to rewrite the positional themselves.
// Ex) `"{1}{2}".format(a, b, c)` to `"{0}{1}".format(b, c)`
let new_format_string;
if min_unused_index > 0 { if min_unused_index > 0 {
// Extract the format string verbatim.
let func = match_attribute(&mut call.func)?; let func = match_attribute(&mut call.func)?;
let simple_string = match_simple_string(&mut func.value)?; let simple_string = match_simple_string(&mut func.value)?;
new_format_string = update_field_types(format_string, min_unused_index);
new_format_string = format!(r#""{new_format_string}""#); // Extract existing quotes from the format string.
let leading_quote = leading_quote(simple_string.value).ok_or_else(|| {
anyhow!(
"Could not find leading quote for format string: {}",
simple_string.value
)
})?;
let trailing_quote = trailing_quote(simple_string.value).ok_or_else(|| {
anyhow!(
"Could not find trailing quote for format string: {}",
simple_string.value
)
})?;
// Update the format string, preserving the quotes.
new_format_string = format!(
"{}{}{}",
leading_quote,
update_field_types(format_string, min_unused_index),
trailing_quote
);
simple_string.value = new_format_string.as_str(); simple_string.value = new_format_string.as_str();
} }

View File

@ -169,6 +169,8 @@ F523.py:19:1: F523 [*] `.format` call has unused arguments at position(s): 2
20 | "{0}{1}".format(1, 2, *args) # No issues 20 | "{0}{1}".format(1, 2, *args) # No issues
21 | "{0}{1}".format(1, 2, 3, *args) # F523 21 | "{0}{1}".format(1, 2, 3, *args) # F523
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ F523 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ F523
22 |
23 | # With nested quotes
| |
= help: Remove extra positional arguments at position(s): 2 = help: Remove extra positional arguments at position(s): 2
@ -178,5 +180,61 @@ F523.py:19:1: F523 [*] `.format` call has unused arguments at position(s): 2
18 18 | "{0}{1}".format(1, 2, *args) # No issues 18 18 | "{0}{1}".format(1, 2, *args) # No issues
19 |-"{0}{1}".format(1, 2, 3, *args) # F523 19 |-"{0}{1}".format(1, 2, 3, *args) # F523
19 |+"{0}{1}".format(1, 2, *args) # F523 19 |+"{0}{1}".format(1, 2, *args) # F523
20 20 |
21 21 | # With nested quotes
22 22 | "{''1:{0}}".format(1, 2, 3) # F523
F523.py:22:1: F523 [*] `.format` call has unused arguments at position(s): 1, 2
|
22 | # With nested quotes
23 | "{''1:{0}}".format(1, 2, 3) # F523
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ F523
24 | "{\"\"1:{0}}".format(1, 2, 3) # F523
25 | '{""1:{0}}'.format(1, 2, 3) # F523
|
= help: Remove extra positional arguments at position(s): 1, 2
Suggested fix
19 19 | "{0}{1}".format(1, 2, 3, *args) # F523
20 20 |
21 21 | # With nested quotes
22 |-"{''1:{0}}".format(1, 2, 3) # F523
22 |+"{''1:{0}}".format(1, ) # F523
23 23 | "{\"\"1:{0}}".format(1, 2, 3) # F523
24 24 | '{""1:{0}}'.format(1, 2, 3) # F523
F523.py:23:1: F523 [*] `.format` call has unused arguments at position(s): 1, 2
|
23 | # With nested quotes
24 | "{''1:{0}}".format(1, 2, 3) # F523
25 | "{\"\"1:{0}}".format(1, 2, 3) # F523
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ F523
26 | '{""1:{0}}'.format(1, 2, 3) # F523
|
= help: Remove extra positional arguments at position(s): 1, 2
Suggested fix
20 20 |
21 21 | # With nested quotes
22 22 | "{''1:{0}}".format(1, 2, 3) # F523
23 |-"{\"\"1:{0}}".format(1, 2, 3) # F523
23 |+"{\"\"1:{0}}".format(1, ) # F523
24 24 | '{""1:{0}}'.format(1, 2, 3) # F523
F523.py:24:1: F523 [*] `.format` call has unused arguments at position(s): 1, 2
|
24 | "{''1:{0}}".format(1, 2, 3) # F523
25 | "{\"\"1:{0}}".format(1, 2, 3) # F523
26 | '{""1:{0}}'.format(1, 2, 3) # F523
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ F523
|
= help: Remove extra positional arguments at position(s): 1, 2
Suggested fix
21 21 | # With nested quotes
22 22 | "{''1:{0}}".format(1, 2, 3) # F523
23 23 | "{\"\"1:{0}}".format(1, 2, 3) # F523
24 |-'{""1:{0}}'.format(1, 2, 3) # F523
24 |+'{""1:{0}}'.format(1, ) # F523