Implement autofix for F541 (#1577)

This commit is contained in:
Harutaka Kawamura 2023-01-03 12:28:32 +09:00 committed by GitHub
parent 8aeec35bfb
commit 03a8ece954
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 206 additions and 19 deletions

View file

@ -9,6 +9,14 @@ e = (
f"def" +
"ghi"
)
f = (
f"a"
F"b"
"c"
rf"d"
fr"e"
)
g = f""
# OK
g = f"ghi{123:{45}}"

View file

@ -9,6 +9,7 @@ use rustpython_ast::{
};
use rustpython_parser::lexer;
use rustpython_parser::lexer::Tok;
use rustpython_parser::token::StringKind;
use crate::ast::types::Range;
use crate::SourceCodeLocator;
@ -470,6 +471,43 @@ pub fn except_range(handler: &Excepthandler, locator: &SourceCodeLocator) -> Ran
range
}
/// Find f-strings that don't contain any formatted values in a `JoinedStr`.
pub fn find_useless_f_strings(expr: &Expr, locator: &SourceCodeLocator) -> Vec<(Range, Range)> {
let contents = locator.slice_source_code_range(&Range::from_located(expr));
lexer::make_tokenizer_located(&contents, expr.location)
.flatten()
.filter_map(|(location, tok, end_location)| match tok {
Tok::String {
kind: StringKind::FString | StringKind::RawFString,
..
} => {
let first_char = locator.slice_source_code_range(&Range {
location,
end_location: Location::new(location.row(), location.column() + 1),
});
// f"..." => f_position = 0
// fr"..." => f_position = 0
// rf"..." => f_position = 1
let f_position = usize::from(!(first_char == "f" || first_char == "F"));
Some((
Range {
location: Location::new(location.row(), location.column() + f_position),
end_location: Location::new(
location.row(),
location.column() + f_position + 1,
),
},
Range {
location,
end_location,
},
))
}
_ => None,
})
.collect()
}
/// Return the `Range` of `else` in `For`, `AsyncFor`, and `While` statements.
pub fn else_range(stmt: &Stmt, locator: &SourceCodeLocator) -> Option<Range> {
match &stmt.node {

View file

@ -2280,15 +2280,7 @@ where
}
ExprKind::JoinedStr { values } => {
if self.settings.enabled.contains(&CheckCode::F541) {
if !values
.iter()
.any(|value| matches!(value.node, ExprKind::FormattedValue { .. }))
{
self.add_check(Check::new(
CheckKind::FStringMissingPlaceholders,
Range::from_located(expr),
));
}
pyflakes::plugins::f_string_missing_placeholders(expr, values, self);
}
}
ExprKind::BinOp {

View file

@ -0,0 +1,25 @@
use rustpython_ast::{Expr, ExprKind};
use crate::ast::helpers::find_useless_f_strings;
use crate::autofix::Fix;
use crate::checkers::ast::Checker;
use crate::registry::{Check, CheckCode, CheckKind};
/// F541
pub fn f_string_missing_placeholders(expr: &Expr, values: &[Expr], checker: &mut Checker) {
if !values
.iter()
.any(|value| matches!(value.node, ExprKind::FormattedValue { .. }))
{
for (prefix_range, tok_range) in find_useless_f_strings(expr, checker.locator) {
let mut check = Check::new(CheckKind::FStringMissingPlaceholders, tok_range);
if checker.patch(&CheckCode::F541) {
check.amend(Fix::deletion(
prefix_range.location,
prefix_range.end_location,
));
}
checker.add_check(check);
}
}
}

View file

@ -1,4 +1,5 @@
pub use assert_tuple::assert_tuple;
pub use f_string_missing_placeholders::f_string_missing_placeholders;
pub use if_tuple::if_tuple;
pub use invalid_literal_comparisons::invalid_literal_comparison;
pub use invalid_print_syntax::invalid_print_syntax;
@ -13,6 +14,7 @@ pub(crate) use strings::{
};
mod assert_tuple;
mod f_string_missing_placeholders;
mod if_tuple;
mod invalid_literal_comparisons;
mod invalid_print_syntax;

View file

@ -9,7 +9,14 @@ expression: checks
end_location:
row: 6
column: 10
fix: ~
fix:
content: ""
location:
row: 6
column: 4
end_location:
row: 6
column: 5
parent: ~
- kind: FStringMissingPlaceholders
location:
@ -18,7 +25,14 @@ expression: checks
end_location:
row: 7
column: 10
fix: ~
fix:
content: ""
location:
row: 7
column: 4
end_location:
row: 7
column: 5
parent: ~
- kind: FStringMissingPlaceholders
location:
@ -27,7 +41,62 @@ expression: checks
end_location:
row: 9
column: 10
fix: ~
fix:
content: ""
location:
row: 9
column: 4
end_location:
row: 9
column: 5
parent: ~
- kind: FStringMissingPlaceholders
location:
row: 13
column: 4
end_location:
row: 13
column: 8
fix:
content: ""
location:
row: 13
column: 4
end_location:
row: 13
column: 5
parent: ~
- kind: FStringMissingPlaceholders
location:
row: 14
column: 4
end_location:
row: 14
column: 8
fix:
content: ""
location:
row: 14
column: 4
end_location:
row: 14
column: 5
parent: ~
- kind: FStringMissingPlaceholders
location:
row: 16
column: 4
end_location:
row: 16
column: 9
fix:
content: ""
location:
row: 16
column: 5
end_location:
row: 16
column: 6
parent: ~
- kind: FStringMissingPlaceholders
location:
@ -35,25 +104,78 @@ expression: checks
column: 4
end_location:
row: 17
column: 9
fix:
content: ""
location:
row: 17
column: 4
end_location:
row: 17
column: 5
parent: ~
- kind: FStringMissingPlaceholders
location:
row: 19
column: 4
end_location:
row: 19
column: 7
fix:
content: ""
location:
row: 19
column: 4
end_location:
row: 19
column: 5
parent: ~
- kind: FStringMissingPlaceholders
location:
row: 25
column: 12
end_location:
row: 25
column: 16
fix: ~
fix:
content: ""
location:
row: 25
column: 12
end_location:
row: 25
column: 13
parent: ~
- kind: FStringMissingPlaceholders
location:
row: 26
row: 34
column: 6
end_location:
row: 26
row: 34
column: 13
fix: ~
fix:
content: ""
location:
row: 34
column: 6
end_location:
row: 34
column: 7
parent: ~
- kind: FStringMissingPlaceholders
location:
row: 27
row: 35
column: 3
end_location:
row: 27
row: 35
column: 6
fix: ~
fix:
content: ""
location:
row: 35
column: 3
end_location:
row: 35
column: 4
parent: ~