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 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
39 changes: 31 additions & 8 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::{AlwaysFixableViolation, Diagnostic, Edit, Fix};
use ruff_macros::{derive_message_formats, violation};
use ruff_python_codegen::Quote;
use ruff_text_size::Ranged;
Expand Down Expand Up @@ -36,7 +36,7 @@ pub struct TripleSingleQuotes {
expected_quote: Quote,
}

impl Violation for TripleSingleQuotes {
impl AlwaysFixableViolation for TripleSingleQuotes {
#[derive_message_formats]
fn message(&self) -> String {
let TripleSingleQuotes { expected_quote } = self;
Expand All @@ -45,12 +45,25 @@ impl Violation for TripleSingleQuotes {
Quote::Single => format!(r#"Use triple single quotes `'''`"#),
}
}

fn fix_title(&self) -> String {
let TripleSingleQuotes { expected_quote } = self;
match expected_quote {
Quote::Double => format!(r#"Use triple double quotes `"""`"#),
Quote::Single => format!(r#"Use 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 +73,28 @@ 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 },
let mut diagnostic =
Diagnostic::new(TripleSingleQuotes { expected_quote }, docstring.range());

diagnostic.set_fix(Fix::safe_edit(Edit::range_replacement(
prefixes + &format!("'''{}'''", docstring.body().as_str()),
docstring.range(),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this always safe? Could this ever change the inner contents of the string?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ran it on the cpython source with --fix, looks fine, but I trust your judgement!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I stand corrected, it breaks when confronted with this condition:

'''Ensure that the given "actual" string ends with "exp_end"'''

));
)));

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

diagnostic.set_fix(Fix::safe_edit(Edit::range_replacement(
prefixes + &format!("\"\"\"{}\"\"\"", docstring.body().as_str()),
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: Use 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: 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 `"""`
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: 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 `"""`
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: Use 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: 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 `"""`
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: Use 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: 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 `"""`
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: 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 `"""`
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: Use 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: Use 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
@@ -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: Use triple double quotes `"""`

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