mirror of https://github.com/astral-sh/ruff
Convert single-argument %-style format calls (#3600)
This commit is contained in:
parent
318c2c80e2
commit
e9f359ac5e
|
|
@ -73,3 +73,13 @@ print("%s \N{snowman}" % (a,))
|
||||||
print("%(foo)s \N{snowman}" % {"foo": 1})
|
print("%(foo)s \N{snowman}" % {"foo": 1})
|
||||||
|
|
||||||
print(("foo %s " "bar %s") % (x, y))
|
print(("foo %s " "bar %s") % (x, y))
|
||||||
|
|
||||||
|
# Single-value expressions
|
||||||
|
print('Hello %s' % "World")
|
||||||
|
print('Hello %s' % f"World")
|
||||||
|
print('Hello %s (%s)' % bar)
|
||||||
|
print('Hello %s (%s)' % bar.baz)
|
||||||
|
print('Hello %s (%s)' % bar['bop'])
|
||||||
|
print('Hello %(arg)s' % bar)
|
||||||
|
print('Hello %(arg)s' % bar.baz)
|
||||||
|
print('Hello %(arg)s' % bar['bop'])
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,4 @@
|
||||||
# OK
|
# OK
|
||||||
"%s" % unknown_type
|
|
||||||
|
|
||||||
b"%s" % (b"bytestring",)
|
b"%s" % (b"bytestring",)
|
||||||
|
|
||||||
"%*s" % (5, "hi")
|
"%*s" % (5, "hi")
|
||||||
|
|
@ -57,3 +55,9 @@ pytest.param('"%8s" % (None,)', id="unsafe width-string conversion"),
|
||||||
"""
|
"""
|
||||||
% (x,)
|
% (x,)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
'Hello %s' % bar
|
||||||
|
|
||||||
|
'Hello %s' % bar.baz
|
||||||
|
|
||||||
|
'Hello %s' % bar['bop']
|
||||||
|
|
|
||||||
|
|
@ -335,7 +335,9 @@ pub(crate) fn printf_string_formatting(
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse each string segment.
|
// Parse each string segment.
|
||||||
let mut format_strings = vec![];
|
let mut num_positional_arguments = 0;
|
||||||
|
let mut num_keyword_arguments = 0;
|
||||||
|
let mut format_strings = Vec::with_capacity(strings.len());
|
||||||
for (start, end) in &strings {
|
for (start, end) in &strings {
|
||||||
let string = checker.locator.slice(Range::new(*start, *end));
|
let string = checker.locator.slice(Range::new(*start, *end));
|
||||||
let (Some(leader), Some(trailer)) = (leading_quote(string), trailing_quote(string)) else {
|
let (Some(leader), Some(trailer)) = (leading_quote(string), trailing_quote(string)) else {
|
||||||
|
|
@ -351,12 +353,52 @@ pub(crate) fn printf_string_formatting(
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Count the number of positional and keyword arguments.
|
||||||
|
for (.., format_part) in format_string.iter() {
|
||||||
|
let CFormatPart::Spec(ref fmt) = format_part else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
if fmt.mapping_key.is_none() {
|
||||||
|
num_positional_arguments += 1;
|
||||||
|
} else {
|
||||||
|
num_keyword_arguments += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert the `%`-format string to a `.format` string.
|
||||||
let format_string = percent_to_format(&format_string);
|
let format_string = percent_to_format(&format_string);
|
||||||
format_strings.push(format!("{leader}{format_string}{trailer}"));
|
format_strings.push(format!("{leader}{format_string}{trailer}"));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse the parameters.
|
// Parse the parameters.
|
||||||
let params_string = match right.node {
|
let params_string = match right.node {
|
||||||
|
ExprKind::Constant { .. } | ExprKind::JoinedStr { .. } => {
|
||||||
|
format!("({})", checker.locator.slice(right))
|
||||||
|
}
|
||||||
|
ExprKind::Name { .. }
|
||||||
|
| ExprKind::Attribute { .. }
|
||||||
|
| ExprKind::Subscript { .. }
|
||||||
|
| ExprKind::Call { .. } => {
|
||||||
|
if num_keyword_arguments > 0 {
|
||||||
|
// If we have _any_ named fields, assume the right-hand side is a mapping.
|
||||||
|
format!("(**{})", checker.locator.slice(right))
|
||||||
|
} else if num_positional_arguments > 1 {
|
||||||
|
// If we have multiple fields, but no named fields, assume the right-hand side is a
|
||||||
|
// tuple.
|
||||||
|
format!("(*{})", checker.locator.slice(right))
|
||||||
|
} else {
|
||||||
|
// Otherwise, if we have a single field, we can't make any assumptions about the
|
||||||
|
// right-hand side. It _could_ be a tuple, but it could also be a single value,
|
||||||
|
// and we can't differentiate between them.
|
||||||
|
// For example:
|
||||||
|
// ```python
|
||||||
|
// x = (1,)
|
||||||
|
// print("%s" % x)
|
||||||
|
// print("{}".format(x))
|
||||||
|
// ```
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
ExprKind::Tuple { .. } => clean_params_tuple(checker, right),
|
ExprKind::Tuple { .. } => clean_params_tuple(checker, right),
|
||||||
ExprKind::Dict { .. } => {
|
ExprKind::Dict { .. } => {
|
||||||
if let Some(params_string) = clean_params_dictionary(checker, right) {
|
if let Some(params_string) = clean_params_dictionary(checker, right) {
|
||||||
|
|
|
||||||
|
|
@ -582,4 +582,164 @@ expression: diagnostics
|
||||||
row: 75
|
row: 75
|
||||||
column: 35
|
column: 35
|
||||||
parent: ~
|
parent: ~
|
||||||
|
- kind:
|
||||||
|
name: PrintfStringFormatting
|
||||||
|
body: Use format specifiers instead of percent format
|
||||||
|
suggestion: Replace with format specifiers
|
||||||
|
fixable: true
|
||||||
|
location:
|
||||||
|
row: 78
|
||||||
|
column: 6
|
||||||
|
end_location:
|
||||||
|
row: 78
|
||||||
|
column: 26
|
||||||
|
fix:
|
||||||
|
content: "'Hello {}'.format(\"World\")"
|
||||||
|
location:
|
||||||
|
row: 78
|
||||||
|
column: 6
|
||||||
|
end_location:
|
||||||
|
row: 78
|
||||||
|
column: 26
|
||||||
|
parent: ~
|
||||||
|
- kind:
|
||||||
|
name: PrintfStringFormatting
|
||||||
|
body: Use format specifiers instead of percent format
|
||||||
|
suggestion: Replace with format specifiers
|
||||||
|
fixable: true
|
||||||
|
location:
|
||||||
|
row: 79
|
||||||
|
column: 6
|
||||||
|
end_location:
|
||||||
|
row: 79
|
||||||
|
column: 27
|
||||||
|
fix:
|
||||||
|
content: "'Hello {}'.format(f\"World\")"
|
||||||
|
location:
|
||||||
|
row: 79
|
||||||
|
column: 6
|
||||||
|
end_location:
|
||||||
|
row: 79
|
||||||
|
column: 27
|
||||||
|
parent: ~
|
||||||
|
- kind:
|
||||||
|
name: PrintfStringFormatting
|
||||||
|
body: Use format specifiers instead of percent format
|
||||||
|
suggestion: Replace with format specifiers
|
||||||
|
fixable: true
|
||||||
|
location:
|
||||||
|
row: 80
|
||||||
|
column: 6
|
||||||
|
end_location:
|
||||||
|
row: 80
|
||||||
|
column: 27
|
||||||
|
fix:
|
||||||
|
content: "'Hello {} ({})'.format(*bar)"
|
||||||
|
location:
|
||||||
|
row: 80
|
||||||
|
column: 6
|
||||||
|
end_location:
|
||||||
|
row: 80
|
||||||
|
column: 27
|
||||||
|
parent: ~
|
||||||
|
- kind:
|
||||||
|
name: PrintfStringFormatting
|
||||||
|
body: Use format specifiers instead of percent format
|
||||||
|
suggestion: Replace with format specifiers
|
||||||
|
fixable: true
|
||||||
|
location:
|
||||||
|
row: 81
|
||||||
|
column: 6
|
||||||
|
end_location:
|
||||||
|
row: 81
|
||||||
|
column: 31
|
||||||
|
fix:
|
||||||
|
content: "'Hello {} ({})'.format(*bar.baz)"
|
||||||
|
location:
|
||||||
|
row: 81
|
||||||
|
column: 6
|
||||||
|
end_location:
|
||||||
|
row: 81
|
||||||
|
column: 31
|
||||||
|
parent: ~
|
||||||
|
- kind:
|
||||||
|
name: PrintfStringFormatting
|
||||||
|
body: Use format specifiers instead of percent format
|
||||||
|
suggestion: Replace with format specifiers
|
||||||
|
fixable: true
|
||||||
|
location:
|
||||||
|
row: 82
|
||||||
|
column: 6
|
||||||
|
end_location:
|
||||||
|
row: 82
|
||||||
|
column: 34
|
||||||
|
fix:
|
||||||
|
content: "'Hello {} ({})'.format(*bar['bop'])"
|
||||||
|
location:
|
||||||
|
row: 82
|
||||||
|
column: 6
|
||||||
|
end_location:
|
||||||
|
row: 82
|
||||||
|
column: 34
|
||||||
|
parent: ~
|
||||||
|
- kind:
|
||||||
|
name: PrintfStringFormatting
|
||||||
|
body: Use format specifiers instead of percent format
|
||||||
|
suggestion: Replace with format specifiers
|
||||||
|
fixable: true
|
||||||
|
location:
|
||||||
|
row: 83
|
||||||
|
column: 6
|
||||||
|
end_location:
|
||||||
|
row: 83
|
||||||
|
column: 27
|
||||||
|
fix:
|
||||||
|
content: "'Hello {arg}'.format(**bar)"
|
||||||
|
location:
|
||||||
|
row: 83
|
||||||
|
column: 6
|
||||||
|
end_location:
|
||||||
|
row: 83
|
||||||
|
column: 27
|
||||||
|
parent: ~
|
||||||
|
- kind:
|
||||||
|
name: PrintfStringFormatting
|
||||||
|
body: Use format specifiers instead of percent format
|
||||||
|
suggestion: Replace with format specifiers
|
||||||
|
fixable: true
|
||||||
|
location:
|
||||||
|
row: 84
|
||||||
|
column: 6
|
||||||
|
end_location:
|
||||||
|
row: 84
|
||||||
|
column: 31
|
||||||
|
fix:
|
||||||
|
content: "'Hello {arg}'.format(**bar.baz)"
|
||||||
|
location:
|
||||||
|
row: 84
|
||||||
|
column: 6
|
||||||
|
end_location:
|
||||||
|
row: 84
|
||||||
|
column: 31
|
||||||
|
parent: ~
|
||||||
|
- kind:
|
||||||
|
name: PrintfStringFormatting
|
||||||
|
body: Use format specifiers instead of percent format
|
||||||
|
suggestion: Replace with format specifiers
|
||||||
|
fixable: true
|
||||||
|
location:
|
||||||
|
row: 85
|
||||||
|
column: 6
|
||||||
|
end_location:
|
||||||
|
row: 85
|
||||||
|
column: 34
|
||||||
|
fix:
|
||||||
|
content: "'Hello {arg}'.format(**bar['bop'])"
|
||||||
|
location:
|
||||||
|
row: 85
|
||||||
|
column: 6
|
||||||
|
end_location:
|
||||||
|
row: 85
|
||||||
|
column: 34
|
||||||
|
parent: ~
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue