Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add autofix for D300 #7967

Merged
merged 4 commits into from
Oct 17, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions crates/ruff_linter/resources/test/fixtures/pydocstyle/D300.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
def with_backslash():
"""Sum\\mary."""


def ends_in_quote():
'Sum\\mary."'


def contains_quote():
'Sum"\\mary.'
1 change: 1 addition & 0 deletions crates/ruff_linter/src/rules/pydocstyle/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ mod tests {
#[test_case(Rule::EscapeSequenceInDocstring, Path::new("D.py"))]
#[test_case(Rule::EscapeSequenceInDocstring, Path::new("D301.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<()> {
let snapshot = format!("{}_{}", rule_code.noqa_code(), path.to_string_lossy());
let diagnostics = test_path(
Expand Down
49 changes: 40 additions & 9 deletions crates/ruff_linter/src/rules/pydocstyle/rules/triple_quotes.rs
Original file line number Diff line number Diff line change
@@ -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_python_codegen::Quote;
use ruff_text_size::Ranged;
Expand Down Expand Up @@ -37,6 +37,8 @@ pub struct TripleSingleQuotes {
}

impl Violation for TripleSingleQuotes {
const FIX_AVAILABILITY: FixAvailability = FixAvailability::Sometimes;

#[derive_message_formats]
fn message(&self) -> String {
let TripleSingleQuotes { expected_quote } = self;
Expand All @@ -45,12 +47,25 @@ impl Violation for TripleSingleQuotes {
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
pub(crate) fn triple_quotes(checker: &mut Checker, docstring: &Docstring) {
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("\"\"\"") {
Quote::Single
} else {
Expand All @@ -60,18 +75,34 @@ pub(crate) fn triple_quotes(checker: &mut Checker, docstring: &Docstring) {
match expected_quote {
Quote::Single => {
if !leading_quote.ends_with("'''") {
checker.diagnostics.push(Diagnostic::new(
TripleSingleQuotes { expected_quote },
docstring.range(),
));
let mut diagnostic =
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(),
)));
}

checker.diagnostics.push(diagnostic);
}
}
Quote::Double => {
if !leading_quote.ends_with("\"\"\"") {
checker.diagnostics.push(Diagnostic::new(
TripleSingleQuotes { expected_quote },
docstring.range(),
));
let mut diagnostic =
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(),
)));
}

checker.diagnostics.push(diagnostic);
}
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,47 +1,102 @@
---
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)')
306 | def triple_single_quotes_raw():
307 | r'''Summary.'''
| ^^^^^^^^^^^^^^^ 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)')
311 | def triple_single_quotes_raw_uppercase():
312 | R'''Summary.'''
| ^^^^^^^^^^^^^^^ D300
|
= help: Convert to 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 `"""`
D.py:317:5: D300 [*] Use triple double quotes `"""`
|
315 | @expect('D300: Use """triple double quotes""" (found \'-quotes)')
316 | def single_quotes_raw():
317 | r'Summary.'
| ^^^^^^^^^^^ D300
|
= help: Convert to 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 `"""`
D.py:322:5: D300 [*] Use triple double quotes `"""`
|
320 | @expect('D300: Use """triple double quotes""" (found \'-quotes)')
321 | def single_quotes_raw_uppercase():
322 | R'Summary.'
| ^^^^^^^^^^^ 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')
327 | def single_quotes_raw_uppercase_backslash():
328 | R'Sum\mary.'
| ^^^^^^^^^^^^ D300
|
= help: Convert to 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 `"""`
D.py:645:5: D300 [*] Use triple double quotes `"""`
|
644 | def single_line_docstring_with_an_escaped_backslash():
645 | "\
Expand All @@ -51,39 +106,95 @@ D.py:645:5: D300 Use triple double quotes `"""`
647 |
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:
649 | "After this docstring there's another statement on the same line separated by a semicolon." ; priorities=1
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ D300
650 | def sort_services(self):
651 | pass
|
= help: Convert to 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 `"""`
D.py:654:5: D300 [*] Use triple double quotes `"""`
|
653 | class StatementOnSameLineAsDocstring:
654 | "After this docstring there's another statement on the same line separated by a semicolon."; priorities=1
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ D300
|
= help: Convert to 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 `"""`
D.py:658:5: D300 [*] Use triple double quotes `"""`
|
657 | class CommentAfterDocstring:
658 | "After this docstring there's a comment." # priorities=1
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ D300
659 | def sort_services(self):
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):
664 | "We enforce a newline after the closing quote for a multi-line docstring \
| _____^
665 | | but continuations shouldn't be considered multi-line"
| |_________________________________________________________^ 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"""


Original file line number Diff line number Diff line change
@@ -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."""


Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
---
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 '''
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ D300
|
= help: Convert to triple double quotes

ℹ Fix
1 |-''' SAM macro definitions '''
1 |+""" SAM macro definitions """


Loading