mirror of
https://github.com/astral-sh/ruff.git
synced 2025-10-01 22:31:23 +00:00
Add autofix for D300
(#7967)
## Summary Add fix for `D300` ## Test Plan `cargo test` and manually
This commit is contained in:
parent
dc6b4ad2b4
commit
8a529925b3
6 changed files with 205 additions and 20 deletions
10
crates/ruff_linter/resources/test/fixtures/pydocstyle/D300.py
vendored
Normal file
10
crates/ruff_linter/resources/test/fixtures/pydocstyle/D300.py
vendored
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
def with_backslash():
|
||||||
|
"""Sum\\mary."""
|
||||||
|
|
||||||
|
|
||||||
|
def ends_in_quote():
|
||||||
|
'Sum\\mary."'
|
||||||
|
|
||||||
|
|
||||||
|
def contains_quote():
|
||||||
|
'Sum"\\mary.'
|
|
@ -87,6 +87,7 @@ mod tests {
|
||||||
#[test_case(Rule::EscapeSequenceInDocstring, Path::new("D.py"))]
|
#[test_case(Rule::EscapeSequenceInDocstring, Path::new("D.py"))]
|
||||||
#[test_case(Rule::EscapeSequenceInDocstring, Path::new("D301.py"))]
|
#[test_case(Rule::EscapeSequenceInDocstring, Path::new("D301.py"))]
|
||||||
#[test_case(Rule::TripleSingleQuotes, Path::new("D.py"))]
|
#[test_case(Rule::TripleSingleQuotes, Path::new("D.py"))]
|
||||||
|
#[test_case(Rule::TripleSingleQuotes, Path::new("D300.py"))]
|
||||||
fn rules(rule_code: Rule, path: &Path) -> Result<()> {
|
fn rules(rule_code: Rule, path: &Path) -> Result<()> {
|
||||||
let snapshot = format!("{}_{}", rule_code.noqa_code(), path.to_string_lossy());
|
let snapshot = format!("{}_{}", rule_code.noqa_code(), path.to_string_lossy());
|
||||||
let diagnostics = test_path(
|
let diagnostics = test_path(
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use ruff_diagnostics::{Diagnostic, Violation};
|
use ruff_diagnostics::{Diagnostic, Edit, Fix, FixAvailability, Violation};
|
||||||
use ruff_macros::{derive_message_formats, violation};
|
use ruff_macros::{derive_message_formats, violation};
|
||||||
use ruff_python_codegen::Quote;
|
use ruff_python_codegen::Quote;
|
||||||
use ruff_text_size::Ranged;
|
use ruff_text_size::Ranged;
|
||||||
|
@ -37,6 +37,8 @@ pub struct TripleSingleQuotes {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Violation for TripleSingleQuotes {
|
impl Violation for TripleSingleQuotes {
|
||||||
|
const FIX_AVAILABILITY: FixAvailability = FixAvailability::Sometimes;
|
||||||
|
|
||||||
#[derive_message_formats]
|
#[derive_message_formats]
|
||||||
fn message(&self) -> String {
|
fn message(&self) -> String {
|
||||||
let TripleSingleQuotes { expected_quote } = self;
|
let TripleSingleQuotes { expected_quote } = self;
|
||||||
|
@ -45,12 +47,25 @@ impl Violation for TripleSingleQuotes {
|
||||||
Quote::Single => format!(r#"Use triple single quotes `'''`"#),
|
Quote::Single => format!(r#"Use triple single quotes `'''`"#),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn fix_title(&self) -> Option<String> {
|
||||||
|
let TripleSingleQuotes { expected_quote } = self;
|
||||||
|
Some(match expected_quote {
|
||||||
|
Quote::Double => format!("Convert to triple double quotes"),
|
||||||
|
Quote::Single => format!("Convert to triple single quotes"),
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// D300
|
/// D300
|
||||||
pub(crate) fn triple_quotes(checker: &mut Checker, docstring: &Docstring) {
|
pub(crate) fn triple_quotes(checker: &mut Checker, docstring: &Docstring) {
|
||||||
let leading_quote = docstring.leading_quote();
|
let leading_quote = docstring.leading_quote();
|
||||||
|
|
||||||
|
let prefixes = docstring
|
||||||
|
.leading_quote()
|
||||||
|
.trim_end_matches(|c| c == '\'' || c == '"')
|
||||||
|
.to_owned();
|
||||||
|
|
||||||
let expected_quote = if docstring.body().contains("\"\"\"") {
|
let expected_quote = if docstring.body().contains("\"\"\"") {
|
||||||
Quote::Single
|
Quote::Single
|
||||||
} else {
|
} else {
|
||||||
|
@ -60,18 +75,34 @@ pub(crate) fn triple_quotes(checker: &mut Checker, docstring: &Docstring) {
|
||||||
match expected_quote {
|
match expected_quote {
|
||||||
Quote::Single => {
|
Quote::Single => {
|
||||||
if !leading_quote.ends_with("'''") {
|
if !leading_quote.ends_with("'''") {
|
||||||
checker.diagnostics.push(Diagnostic::new(
|
let mut diagnostic =
|
||||||
TripleSingleQuotes { expected_quote },
|
Diagnostic::new(TripleSingleQuotes { expected_quote }, docstring.range());
|
||||||
|
|
||||||
|
let body = docstring.body().as_str();
|
||||||
|
if !body.ends_with('\'') {
|
||||||
|
diagnostic.set_fix(Fix::safe_edit(Edit::range_replacement(
|
||||||
|
format!("{prefixes}'''{body}'''"),
|
||||||
docstring.range(),
|
docstring.range(),
|
||||||
));
|
)));
|
||||||
|
}
|
||||||
|
|
||||||
|
checker.diagnostics.push(diagnostic);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Quote::Double => {
|
Quote::Double => {
|
||||||
if !leading_quote.ends_with("\"\"\"") {
|
if !leading_quote.ends_with("\"\"\"") {
|
||||||
checker.diagnostics.push(Diagnostic::new(
|
let mut diagnostic =
|
||||||
TripleSingleQuotes { expected_quote },
|
Diagnostic::new(TripleSingleQuotes { expected_quote }, docstring.range());
|
||||||
|
|
||||||
|
let body = docstring.body().as_str();
|
||||||
|
if !body.ends_with('"') {
|
||||||
|
diagnostic.set_fix(Fix::safe_edit(Edit::range_replacement(
|
||||||
|
format!("{prefixes}\"\"\"{body}\"\"\""),
|
||||||
docstring.range(),
|
docstring.range(),
|
||||||
));
|
)));
|
||||||
|
}
|
||||||
|
|
||||||
|
checker.diagnostics.push(diagnostic);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,47 +1,102 @@
|
||||||
---
|
---
|
||||||
source: crates/ruff_linter/src/rules/pydocstyle/mod.rs
|
source: crates/ruff_linter/src/rules/pydocstyle/mod.rs
|
||||||
---
|
---
|
||||||
D.py:307:5: D300 Use triple double quotes `"""`
|
D.py:307:5: D300 [*] Use triple double quotes `"""`
|
||||||
|
|
|
|
||||||
305 | @expect('D300: Use """triple double quotes""" (found \'\'\'-quotes)')
|
305 | @expect('D300: Use """triple double quotes""" (found \'\'\'-quotes)')
|
||||||
306 | def triple_single_quotes_raw():
|
306 | def triple_single_quotes_raw():
|
||||||
307 | r'''Summary.'''
|
307 | r'''Summary.'''
|
||||||
| ^^^^^^^^^^^^^^^ D300
|
| ^^^^^^^^^^^^^^^ D300
|
||||||
|
|
|
|
||||||
|
= help: Convert to triple double quotes
|
||||||
|
|
||||||
D.py:312:5: D300 Use triple double quotes `"""`
|
ℹ Fix
|
||||||
|
304 304 |
|
||||||
|
305 305 | @expect('D300: Use """triple double quotes""" (found \'\'\'-quotes)')
|
||||||
|
306 306 | def triple_single_quotes_raw():
|
||||||
|
307 |- r'''Summary.'''
|
||||||
|
307 |+ r"""Summary."""
|
||||||
|
308 308 |
|
||||||
|
309 309 |
|
||||||
|
310 310 | @expect('D300: Use """triple double quotes""" (found \'\'\'-quotes)')
|
||||||
|
|
||||||
|
D.py:312:5: D300 [*] Use triple double quotes `"""`
|
||||||
|
|
|
|
||||||
310 | @expect('D300: Use """triple double quotes""" (found \'\'\'-quotes)')
|
310 | @expect('D300: Use """triple double quotes""" (found \'\'\'-quotes)')
|
||||||
311 | def triple_single_quotes_raw_uppercase():
|
311 | def triple_single_quotes_raw_uppercase():
|
||||||
312 | R'''Summary.'''
|
312 | R'''Summary.'''
|
||||||
| ^^^^^^^^^^^^^^^ D300
|
| ^^^^^^^^^^^^^^^ D300
|
||||||
|
|
|
|
||||||
|
= help: Convert to triple double quotes
|
||||||
|
|
||||||
D.py:317:5: D300 Use triple double quotes `"""`
|
ℹ Fix
|
||||||
|
309 309 |
|
||||||
|
310 310 | @expect('D300: Use """triple double quotes""" (found \'\'\'-quotes)')
|
||||||
|
311 311 | def triple_single_quotes_raw_uppercase():
|
||||||
|
312 |- R'''Summary.'''
|
||||||
|
312 |+ R"""Summary."""
|
||||||
|
313 313 |
|
||||||
|
314 314 |
|
||||||
|
315 315 | @expect('D300: Use """triple double quotes""" (found \'-quotes)')
|
||||||
|
|
||||||
|
D.py:317:5: D300 [*] Use triple double quotes `"""`
|
||||||
|
|
|
|
||||||
315 | @expect('D300: Use """triple double quotes""" (found \'-quotes)')
|
315 | @expect('D300: Use """triple double quotes""" (found \'-quotes)')
|
||||||
316 | def single_quotes_raw():
|
316 | def single_quotes_raw():
|
||||||
317 | r'Summary.'
|
317 | r'Summary.'
|
||||||
| ^^^^^^^^^^^ D300
|
| ^^^^^^^^^^^ D300
|
||||||
|
|
|
|
||||||
|
= help: Convert to triple double quotes
|
||||||
|
|
||||||
D.py:322:5: D300 Use triple double quotes `"""`
|
ℹ Fix
|
||||||
|
314 314 |
|
||||||
|
315 315 | @expect('D300: Use """triple double quotes""" (found \'-quotes)')
|
||||||
|
316 316 | def single_quotes_raw():
|
||||||
|
317 |- r'Summary.'
|
||||||
|
317 |+ r"""Summary."""
|
||||||
|
318 318 |
|
||||||
|
319 319 |
|
||||||
|
320 320 | @expect('D300: Use """triple double quotes""" (found \'-quotes)')
|
||||||
|
|
||||||
|
D.py:322:5: D300 [*] Use triple double quotes `"""`
|
||||||
|
|
|
|
||||||
320 | @expect('D300: Use """triple double quotes""" (found \'-quotes)')
|
320 | @expect('D300: Use """triple double quotes""" (found \'-quotes)')
|
||||||
321 | def single_quotes_raw_uppercase():
|
321 | def single_quotes_raw_uppercase():
|
||||||
322 | R'Summary.'
|
322 | R'Summary.'
|
||||||
| ^^^^^^^^^^^ D300
|
| ^^^^^^^^^^^ D300
|
||||||
|
|
|
|
||||||
|
= help: Convert to triple double quotes
|
||||||
|
|
||||||
D.py:328:5: D300 Use triple double quotes `"""`
|
ℹ Fix
|
||||||
|
319 319 |
|
||||||
|
320 320 | @expect('D300: Use """triple double quotes""" (found \'-quotes)')
|
||||||
|
321 321 | def single_quotes_raw_uppercase():
|
||||||
|
322 |- R'Summary.'
|
||||||
|
322 |+ R"""Summary."""
|
||||||
|
323 323 |
|
||||||
|
324 324 |
|
||||||
|
325 325 | @expect('D300: Use """triple double quotes""" (found \'-quotes)')
|
||||||
|
|
||||||
|
D.py:328:5: D300 [*] Use triple double quotes `"""`
|
||||||
|
|
|
|
||||||
326 | @expect('D301: Use r""" if any backslashes in a docstring')
|
326 | @expect('D301: Use r""" if any backslashes in a docstring')
|
||||||
327 | def single_quotes_raw_uppercase_backslash():
|
327 | def single_quotes_raw_uppercase_backslash():
|
||||||
328 | R'Sum\mary.'
|
328 | R'Sum\mary.'
|
||||||
| ^^^^^^^^^^^^ D300
|
| ^^^^^^^^^^^^ D300
|
||||||
|
|
|
|
||||||
|
= help: Convert to triple double quotes
|
||||||
|
|
||||||
D.py:645:5: D300 Use triple double quotes `"""`
|
ℹ Fix
|
||||||
|
325 325 | @expect('D300: Use """triple double quotes""" (found \'-quotes)')
|
||||||
|
326 326 | @expect('D301: Use r""" if any backslashes in a docstring')
|
||||||
|
327 327 | def single_quotes_raw_uppercase_backslash():
|
||||||
|
328 |- R'Sum\mary.'
|
||||||
|
328 |+ R"""Sum\mary."""
|
||||||
|
329 329 |
|
||||||
|
330 330 |
|
||||||
|
331 331 | @expect('D301: Use r""" if any backslashes in a docstring')
|
||||||
|
|
||||||
|
D.py:645:5: D300 [*] Use triple double quotes `"""`
|
||||||
|
|
|
|
||||||
644 | def single_line_docstring_with_an_escaped_backslash():
|
644 | def single_line_docstring_with_an_escaped_backslash():
|
||||||
645 | "\
|
645 | "\
|
||||||
|
@ -51,8 +106,21 @@ D.py:645:5: D300 Use triple double quotes `"""`
|
||||||
647 |
|
647 |
|
||||||
648 | class StatementOnSameLineAsDocstring:
|
648 | class StatementOnSameLineAsDocstring:
|
||||||
|
|
|
|
||||||
|
= help: Convert to triple double quotes
|
||||||
|
|
||||||
D.py:649:5: D300 Use triple double quotes `"""`
|
ℹ Fix
|
||||||
|
642 642 |
|
||||||
|
643 643 |
|
||||||
|
644 644 | def single_line_docstring_with_an_escaped_backslash():
|
||||||
|
645 |- "\
|
||||||
|
646 |- "
|
||||||
|
645 |+ """\
|
||||||
|
646 |+ """
|
||||||
|
647 647 |
|
||||||
|
648 648 | class StatementOnSameLineAsDocstring:
|
||||||
|
649 649 | "After this docstring there's another statement on the same line separated by a semicolon." ; priorities=1
|
||||||
|
|
||||||
|
D.py:649:5: D300 [*] Use triple double quotes `"""`
|
||||||
|
|
|
|
||||||
648 | class StatementOnSameLineAsDocstring:
|
648 | class StatementOnSameLineAsDocstring:
|
||||||
649 | "After this docstring there's another statement on the same line separated by a semicolon." ; priorities=1
|
649 | "After this docstring there's another statement on the same line separated by a semicolon." ; priorities=1
|
||||||
|
@ -60,15 +128,37 @@ D.py:649:5: D300 Use triple double quotes `"""`
|
||||||
650 | def sort_services(self):
|
650 | def sort_services(self):
|
||||||
651 | pass
|
651 | pass
|
||||||
|
|
|
|
||||||
|
= help: Convert to triple double quotes
|
||||||
|
|
||||||
D.py:654:5: D300 Use triple double quotes `"""`
|
ℹ Fix
|
||||||
|
646 646 | "
|
||||||
|
647 647 |
|
||||||
|
648 648 | class StatementOnSameLineAsDocstring:
|
||||||
|
649 |- "After this docstring there's another statement on the same line separated by a semicolon." ; priorities=1
|
||||||
|
649 |+ """After this docstring there's another statement on the same line separated by a semicolon.""" ; priorities=1
|
||||||
|
650 650 | def sort_services(self):
|
||||||
|
651 651 | pass
|
||||||
|
652 652 |
|
||||||
|
|
||||||
|
D.py:654:5: D300 [*] Use triple double quotes `"""`
|
||||||
|
|
|
|
||||||
653 | class StatementOnSameLineAsDocstring:
|
653 | class StatementOnSameLineAsDocstring:
|
||||||
654 | "After this docstring there's another statement on the same line separated by a semicolon."; priorities=1
|
654 | "After this docstring there's another statement on the same line separated by a semicolon."; priorities=1
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ D300
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ D300
|
||||||
|
|
|
|
||||||
|
= help: Convert to triple double quotes
|
||||||
|
|
||||||
D.py:658:5: D300 Use triple double quotes `"""`
|
ℹ Fix
|
||||||
|
651 651 | pass
|
||||||
|
652 652 |
|
||||||
|
653 653 | class StatementOnSameLineAsDocstring:
|
||||||
|
654 |- "After this docstring there's another statement on the same line separated by a semicolon."; priorities=1
|
||||||
|
654 |+ """After this docstring there's another statement on the same line separated by a semicolon."""; priorities=1
|
||||||
|
655 655 |
|
||||||
|
656 656 |
|
||||||
|
657 657 | class CommentAfterDocstring:
|
||||||
|
|
||||||
|
D.py:658:5: D300 [*] Use triple double quotes `"""`
|
||||||
|
|
|
|
||||||
657 | class CommentAfterDocstring:
|
657 | class CommentAfterDocstring:
|
||||||
658 | "After this docstring there's a comment." # priorities=1
|
658 | "After this docstring there's a comment." # priorities=1
|
||||||
|
@ -76,8 +166,19 @@ D.py:658:5: D300 Use triple double quotes `"""`
|
||||||
659 | def sort_services(self):
|
659 | def sort_services(self):
|
||||||
660 | pass
|
660 | pass
|
||||||
|
|
|
|
||||||
|
= help: Convert to triple double quotes
|
||||||
|
|
||||||
D.py:664:5: D300 Use triple double quotes `"""`
|
ℹ Fix
|
||||||
|
655 655 |
|
||||||
|
656 656 |
|
||||||
|
657 657 | class CommentAfterDocstring:
|
||||||
|
658 |- "After this docstring there's a comment." # priorities=1
|
||||||
|
658 |+ """After this docstring there's a comment.""" # priorities=1
|
||||||
|
659 659 | def sort_services(self):
|
||||||
|
660 660 | pass
|
||||||
|
661 661 |
|
||||||
|
|
||||||
|
D.py:664:5: D300 [*] Use triple double quotes `"""`
|
||||||
|
|
|
|
||||||
663 | def newline_after_closing_quote(self):
|
663 | def newline_after_closing_quote(self):
|
||||||
664 | "We enforce a newline after the closing quote for a multi-line docstring \
|
664 | "We enforce a newline after the closing quote for a multi-line docstring \
|
||||||
|
@ -85,5 +186,15 @@ D.py:664:5: D300 Use triple double quotes `"""`
|
||||||
665 | | but continuations shouldn't be considered multi-line"
|
665 | | but continuations shouldn't be considered multi-line"
|
||||||
| |_________________________________________________________^ D300
|
| |_________________________________________________________^ D300
|
||||||
|
|
|
|
||||||
|
= help: Convert to triple double quotes
|
||||||
|
|
||||||
|
ℹ Fix
|
||||||
|
661 661 |
|
||||||
|
662 662 |
|
||||||
|
663 663 | def newline_after_closing_quote(self):
|
||||||
|
664 |- "We enforce a newline after the closing quote for a multi-line docstring \
|
||||||
|
665 |- but continuations shouldn't be considered multi-line"
|
||||||
|
664 |+ """We enforce a newline after the closing quote for a multi-line docstring \
|
||||||
|
665 |+ but continuations shouldn't be considered multi-line"""
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,27 @@
|
||||||
|
---
|
||||||
|
source: crates/ruff_linter/src/rules/pydocstyle/mod.rs
|
||||||
|
---
|
||||||
|
D300.py:6:5: D300 Use triple double quotes `"""`
|
||||||
|
|
|
||||||
|
5 | def ends_in_quote():
|
||||||
|
6 | 'Sum\\mary."'
|
||||||
|
| ^^^^^^^^^^^^^ D300
|
||||||
|
|
|
||||||
|
= help: Convert to triple double quotes
|
||||||
|
|
||||||
|
D300.py:10:5: D300 [*] Use triple double quotes `"""`
|
||||||
|
|
|
||||||
|
9 | def contains_quote():
|
||||||
|
10 | 'Sum"\\mary.'
|
||||||
|
| ^^^^^^^^^^^^^ D300
|
||||||
|
|
|
||||||
|
= help: Convert to triple double quotes
|
||||||
|
|
||||||
|
ℹ Fix
|
||||||
|
7 7 |
|
||||||
|
8 8 |
|
||||||
|
9 9 | def contains_quote():
|
||||||
|
10 |- 'Sum"\\mary.'
|
||||||
|
10 |+ """Sum"\\mary."""
|
||||||
|
|
||||||
|
|
|
@ -1,10 +1,15 @@
|
||||||
---
|
---
|
||||||
source: crates/ruff_linter/src/rules/pydocstyle/mod.rs
|
source: crates/ruff_linter/src/rules/pydocstyle/mod.rs
|
||||||
---
|
---
|
||||||
bom.py:1:1: D300 Use triple double quotes `"""`
|
bom.py:1:1: D300 [*] Use triple double quotes `"""`
|
||||||
|
|
|
|
||||||
1 | ''' SAM macro definitions '''
|
1 | ''' SAM macro definitions '''
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ D300
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ D300
|
||||||
|
|
|
|
||||||
|
= help: Convert to triple double quotes
|
||||||
|
|
||||||
|
ℹ Fix
|
||||||
|
1 |-''' SAM macro definitions '''
|
||||||
|
1 |+""" SAM macro definitions """
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue