mirror of
https://github.com/astral-sh/ruff.git
synced 2025-09-26 11:59:35 +00:00
Autofixer for ISC001 (#4853)
## Summary This PR adds autofixer for rule ISC001 in cases where both string literals are of the same kind and with same quotes (double / single). Fixes #4829 ## Test Plan I added testcases with different combinations of string literals.
This commit is contained in:
parent
780336db0a
commit
e2130707f5
4 changed files with 332 additions and 9 deletions
|
@ -34,3 +34,19 @@ _ = (
|
|||
b"abc"
|
||||
b"def"
|
||||
)
|
||||
|
||||
_ = """a""" """b"""
|
||||
|
||||
_ = """a
|
||||
b""" """c
|
||||
d"""
|
||||
|
||||
_ = f"""a""" f"""b"""
|
||||
|
||||
_ = f"a" "b"
|
||||
|
||||
_ = """a""" "b"
|
||||
|
||||
_ = 'a' "b"
|
||||
|
||||
_ = rf"a" rf"b"
|
||||
|
|
|
@ -3,9 +3,10 @@ use ruff_text_size::TextRange;
|
|||
use rustpython_parser::lexer::LexResult;
|
||||
use rustpython_parser::Tok;
|
||||
|
||||
use ruff_diagnostics::{Diagnostic, Violation};
|
||||
use ruff_diagnostics::{AutofixKind, Diagnostic, Edit, Fix, Violation};
|
||||
use ruff_macros::{derive_message_formats, violation};
|
||||
use ruff_python_ast::source_code::Locator;
|
||||
use ruff_python_ast::str::{leading_quote, trailing_quote};
|
||||
|
||||
use crate::rules::flake8_implicit_str_concat::settings::Settings;
|
||||
|
||||
|
@ -34,10 +35,16 @@ use crate::rules::flake8_implicit_str_concat::settings::Settings;
|
|||
pub struct SingleLineImplicitStringConcatenation;
|
||||
|
||||
impl Violation for SingleLineImplicitStringConcatenation {
|
||||
const AUTOFIX: AutofixKind = AutofixKind::Sometimes;
|
||||
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
format!("Implicitly concatenated string literals on one line")
|
||||
}
|
||||
|
||||
fn autofix_title(&self) -> Option<String> {
|
||||
Some("Combine string literals".to_string())
|
||||
}
|
||||
}
|
||||
|
||||
/// ## What it does
|
||||
|
@ -106,12 +113,50 @@ pub(crate) fn implicit(
|
|||
TextRange::new(a_range.start(), b_range.end()),
|
||||
));
|
||||
} else {
|
||||
diagnostics.push(Diagnostic::new(
|
||||
let mut diagnostic = Diagnostic::new(
|
||||
SingleLineImplicitStringConcatenation,
|
||||
TextRange::new(a_range.start(), b_range.end()),
|
||||
));
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
if let Some(fix) = concatenate_strings(*a_range, *b_range, locator) {
|
||||
diagnostic.set_fix(fix);
|
||||
}
|
||||
|
||||
diagnostics.push(diagnostic);
|
||||
};
|
||||
};
|
||||
}
|
||||
diagnostics
|
||||
}
|
||||
|
||||
fn concatenate_strings(a_range: TextRange, b_range: TextRange, locator: &Locator) -> Option<Fix> {
|
||||
let a_text = &locator.contents()[a_range];
|
||||
let b_text = &locator.contents()[b_range];
|
||||
|
||||
let a_leading_quote = leading_quote(a_text)?;
|
||||
let b_leading_quote = leading_quote(b_text)?;
|
||||
|
||||
// Require, for now, that the leading quotes are the same.
|
||||
if a_leading_quote != b_leading_quote {
|
||||
return None;
|
||||
}
|
||||
|
||||
let a_trailing_quote = trailing_quote(a_text)?;
|
||||
let b_trailing_quote = trailing_quote(b_text)?;
|
||||
|
||||
// Require, for now, that the trailing quotes are the same.
|
||||
if a_trailing_quote != b_trailing_quote {
|
||||
return None;
|
||||
}
|
||||
|
||||
let a_body = &a_text[a_leading_quote.len()..a_text.len() - a_trailing_quote.len()];
|
||||
let b_body = &b_text[b_leading_quote.len()..b_text.len() - b_trailing_quote.len()];
|
||||
|
||||
let concatenation = format!("{a_leading_quote}{a_body}{b_body}{a_trailing_quote}");
|
||||
let range = TextRange::new(a_range.start(), b_range.end());
|
||||
|
||||
Some(Fix::automatic(Edit::range_replacement(
|
||||
concatenation,
|
||||
range,
|
||||
)))
|
||||
}
|
||||
|
|
|
@ -1,20 +1,151 @@
|
|||
---
|
||||
source: crates/ruff/src/rules/flake8_implicit_str_concat/mod.rs
|
||||
---
|
||||
ISC.py:1:5: ISC001 Implicitly concatenated string literals on one line
|
||||
ISC.py:1:5: ISC001 [*] Implicitly concatenated string literals on one line
|
||||
|
|
||||
1 | _ = "a" "b" "c"
|
||||
| ^^^^^^^ ISC001
|
||||
2 |
|
||||
3 | _ = "abc" + "def"
|
||||
|
|
||||
= help: Combine string literals
|
||||
|
||||
ISC.py:1:9: ISC001 Implicitly concatenated string literals on one line
|
||||
ℹ Fix
|
||||
1 |-_ = "a" "b" "c"
|
||||
1 |+_ = "ab" "c"
|
||||
2 2 |
|
||||
3 3 | _ = "abc" + "def"
|
||||
4 4 |
|
||||
|
||||
ISC.py:1:9: ISC001 [*] Implicitly concatenated string literals on one line
|
||||
|
|
||||
1 | _ = "a" "b" "c"
|
||||
| ^^^^^^^ ISC001
|
||||
2 |
|
||||
3 | _ = "abc" + "def"
|
||||
|
|
||||
= help: Combine string literals
|
||||
|
||||
ℹ Fix
|
||||
1 |-_ = "a" "b" "c"
|
||||
1 |+_ = "a" "bc"
|
||||
2 2 |
|
||||
3 3 | _ = "abc" + "def"
|
||||
4 4 |
|
||||
|
||||
ISC.py:38:5: ISC001 [*] Implicitly concatenated string literals on one line
|
||||
|
|
||||
36 | )
|
||||
37 |
|
||||
38 | _ = """a""" """b"""
|
||||
| ^^^^^^^^^^^^^^^ ISC001
|
||||
39 |
|
||||
40 | _ = """a
|
||||
|
|
||||
= help: Combine string literals
|
||||
|
||||
ℹ Fix
|
||||
35 35 | b"def"
|
||||
36 36 | )
|
||||
37 37 |
|
||||
38 |-_ = """a""" """b"""
|
||||
38 |+_ = """ab"""
|
||||
39 39 |
|
||||
40 40 | _ = """a
|
||||
41 41 | b""" """c
|
||||
|
||||
ISC.py:40:5: ISC001 [*] Implicitly concatenated string literals on one line
|
||||
|
|
||||
38 | _ = """a""" """b"""
|
||||
39 |
|
||||
40 | _ = """a
|
||||
| _____^
|
||||
41 | | b""" """c
|
||||
42 | | d"""
|
||||
| |____^ ISC001
|
||||
43 |
|
||||
44 | _ = f"""a""" f"""b"""
|
||||
|
|
||||
= help: Combine string literals
|
||||
|
||||
ℹ Fix
|
||||
38 38 | _ = """a""" """b"""
|
||||
39 39 |
|
||||
40 40 | _ = """a
|
||||
41 |-b""" """c
|
||||
41 |+bc
|
||||
42 42 | d"""
|
||||
43 43 |
|
||||
44 44 | _ = f"""a""" f"""b"""
|
||||
|
||||
ISC.py:44:5: ISC001 [*] Implicitly concatenated string literals on one line
|
||||
|
|
||||
42 | d"""
|
||||
43 |
|
||||
44 | _ = f"""a""" f"""b"""
|
||||
| ^^^^^^^^^^^^^^^^^ ISC001
|
||||
45 |
|
||||
46 | _ = f"a" "b"
|
||||
|
|
||||
= help: Combine string literals
|
||||
|
||||
ℹ Fix
|
||||
41 41 | b""" """c
|
||||
42 42 | d"""
|
||||
43 43 |
|
||||
44 |-_ = f"""a""" f"""b"""
|
||||
44 |+_ = f"""ab"""
|
||||
45 45 |
|
||||
46 46 | _ = f"a" "b"
|
||||
47 47 |
|
||||
|
||||
ISC.py:46:5: ISC001 Implicitly concatenated string literals on one line
|
||||
|
|
||||
44 | _ = f"""a""" f"""b"""
|
||||
45 |
|
||||
46 | _ = f"a" "b"
|
||||
| ^^^^^^^^ ISC001
|
||||
47 |
|
||||
48 | _ = """a""" "b"
|
||||
|
|
||||
= help: Combine string literals
|
||||
|
||||
ISC.py:48:5: ISC001 Implicitly concatenated string literals on one line
|
||||
|
|
||||
46 | _ = f"a" "b"
|
||||
47 |
|
||||
48 | _ = """a""" "b"
|
||||
| ^^^^^^^^^^^ ISC001
|
||||
49 |
|
||||
50 | _ = 'a' "b"
|
||||
|
|
||||
= help: Combine string literals
|
||||
|
||||
ISC.py:50:5: ISC001 Implicitly concatenated string literals on one line
|
||||
|
|
||||
48 | _ = """a""" "b"
|
||||
49 |
|
||||
50 | _ = 'a' "b"
|
||||
| ^^^^^^^ ISC001
|
||||
51 |
|
||||
52 | _ = rf"a" rf"b"
|
||||
|
|
||||
= help: Combine string literals
|
||||
|
||||
ISC.py:52:5: ISC001 [*] Implicitly concatenated string literals on one line
|
||||
|
|
||||
50 | _ = 'a' "b"
|
||||
51 |
|
||||
52 | _ = rf"a" rf"b"
|
||||
| ^^^^^^^^^^^ ISC001
|
||||
|
|
||||
= help: Combine string literals
|
||||
|
||||
ℹ Fix
|
||||
49 49 |
|
||||
50 50 | _ = 'a' "b"
|
||||
51 51 |
|
||||
52 |-_ = rf"a" rf"b"
|
||||
52 |+_ = rf"ab"
|
||||
|
||||
|
||||
|
|
|
@ -1,20 +1,151 @@
|
|||
---
|
||||
source: crates/ruff/src/rules/flake8_implicit_str_concat/mod.rs
|
||||
---
|
||||
ISC.py:1:5: ISC001 Implicitly concatenated string literals on one line
|
||||
ISC.py:1:5: ISC001 [*] Implicitly concatenated string literals on one line
|
||||
|
|
||||
1 | _ = "a" "b" "c"
|
||||
| ^^^^^^^ ISC001
|
||||
2 |
|
||||
3 | _ = "abc" + "def"
|
||||
|
|
||||
= help: Combine string literals
|
||||
|
||||
ISC.py:1:9: ISC001 Implicitly concatenated string literals on one line
|
||||
ℹ Fix
|
||||
1 |-_ = "a" "b" "c"
|
||||
1 |+_ = "ab" "c"
|
||||
2 2 |
|
||||
3 3 | _ = "abc" + "def"
|
||||
4 4 |
|
||||
|
||||
ISC.py:1:9: ISC001 [*] Implicitly concatenated string literals on one line
|
||||
|
|
||||
1 | _ = "a" "b" "c"
|
||||
| ^^^^^^^ ISC001
|
||||
2 |
|
||||
3 | _ = "abc" + "def"
|
||||
|
|
||||
= help: Combine string literals
|
||||
|
||||
ℹ Fix
|
||||
1 |-_ = "a" "b" "c"
|
||||
1 |+_ = "a" "bc"
|
||||
2 2 |
|
||||
3 3 | _ = "abc" + "def"
|
||||
4 4 |
|
||||
|
||||
ISC.py:38:5: ISC001 [*] Implicitly concatenated string literals on one line
|
||||
|
|
||||
36 | )
|
||||
37 |
|
||||
38 | _ = """a""" """b"""
|
||||
| ^^^^^^^^^^^^^^^ ISC001
|
||||
39 |
|
||||
40 | _ = """a
|
||||
|
|
||||
= help: Combine string literals
|
||||
|
||||
ℹ Fix
|
||||
35 35 | b"def"
|
||||
36 36 | )
|
||||
37 37 |
|
||||
38 |-_ = """a""" """b"""
|
||||
38 |+_ = """ab"""
|
||||
39 39 |
|
||||
40 40 | _ = """a
|
||||
41 41 | b""" """c
|
||||
|
||||
ISC.py:40:5: ISC001 [*] Implicitly concatenated string literals on one line
|
||||
|
|
||||
38 | _ = """a""" """b"""
|
||||
39 |
|
||||
40 | _ = """a
|
||||
| _____^
|
||||
41 | | b""" """c
|
||||
42 | | d"""
|
||||
| |____^ ISC001
|
||||
43 |
|
||||
44 | _ = f"""a""" f"""b"""
|
||||
|
|
||||
= help: Combine string literals
|
||||
|
||||
ℹ Fix
|
||||
38 38 | _ = """a""" """b"""
|
||||
39 39 |
|
||||
40 40 | _ = """a
|
||||
41 |-b""" """c
|
||||
41 |+bc
|
||||
42 42 | d"""
|
||||
43 43 |
|
||||
44 44 | _ = f"""a""" f"""b"""
|
||||
|
||||
ISC.py:44:5: ISC001 [*] Implicitly concatenated string literals on one line
|
||||
|
|
||||
42 | d"""
|
||||
43 |
|
||||
44 | _ = f"""a""" f"""b"""
|
||||
| ^^^^^^^^^^^^^^^^^ ISC001
|
||||
45 |
|
||||
46 | _ = f"a" "b"
|
||||
|
|
||||
= help: Combine string literals
|
||||
|
||||
ℹ Fix
|
||||
41 41 | b""" """c
|
||||
42 42 | d"""
|
||||
43 43 |
|
||||
44 |-_ = f"""a""" f"""b"""
|
||||
44 |+_ = f"""ab"""
|
||||
45 45 |
|
||||
46 46 | _ = f"a" "b"
|
||||
47 47 |
|
||||
|
||||
ISC.py:46:5: ISC001 Implicitly concatenated string literals on one line
|
||||
|
|
||||
44 | _ = f"""a""" f"""b"""
|
||||
45 |
|
||||
46 | _ = f"a" "b"
|
||||
| ^^^^^^^^ ISC001
|
||||
47 |
|
||||
48 | _ = """a""" "b"
|
||||
|
|
||||
= help: Combine string literals
|
||||
|
||||
ISC.py:48:5: ISC001 Implicitly concatenated string literals on one line
|
||||
|
|
||||
46 | _ = f"a" "b"
|
||||
47 |
|
||||
48 | _ = """a""" "b"
|
||||
| ^^^^^^^^^^^ ISC001
|
||||
49 |
|
||||
50 | _ = 'a' "b"
|
||||
|
|
||||
= help: Combine string literals
|
||||
|
||||
ISC.py:50:5: ISC001 Implicitly concatenated string literals on one line
|
||||
|
|
||||
48 | _ = """a""" "b"
|
||||
49 |
|
||||
50 | _ = 'a' "b"
|
||||
| ^^^^^^^ ISC001
|
||||
51 |
|
||||
52 | _ = rf"a" rf"b"
|
||||
|
|
||||
= help: Combine string literals
|
||||
|
||||
ISC.py:52:5: ISC001 [*] Implicitly concatenated string literals on one line
|
||||
|
|
||||
50 | _ = 'a' "b"
|
||||
51 |
|
||||
52 | _ = rf"a" rf"b"
|
||||
| ^^^^^^^^^^^ ISC001
|
||||
|
|
||||
= help: Combine string literals
|
||||
|
||||
ℹ Fix
|
||||
49 49 |
|
||||
50 50 | _ = 'a' "b"
|
||||
51 51 |
|
||||
52 |-_ = rf"a" rf"b"
|
||||
52 |+_ = rf"ab"
|
||||
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue