mirror of
https://github.com/astral-sh/ruff.git
synced 2025-11-20 04:00:09 +00:00
[pydoclint] Fix SyntaxError from fixes with line continuations (D201, D202) (#19246)
<!-- Thank you for contributing to Ruff/ty! To help us out with reviewing, please consider the following: - Does this pull request include a summary of the change? (See below.) - Does this pull request include a descriptive title? (Please prefix with `[ty]` for ty pull requests.) - Does this pull request include references to any relevant issues? --> ## Summary <!-- What's the purpose of the change? What does it do, and why? --> This PR fixes #7172 by suppressing the fixes for [docstring-missing-returns (DOC201)](https://docs.astral.sh/ruff/rules/docstring-missing-returns/#docstring-missing-returns-doc201) / [docstring-extraneous-returns (DOC202)](https://docs.astral.sh/ruff/rules/docstring-extraneous-returns/#docstring-extraneous-returns-doc202) if there is a surrounding line continuation character `\` that would make the fix cause a syntax error. To do this, the lints are changed from `AlwaysFixableViolation` to `Violation` with `FixAvailability::Sometimes`. In the case of `DOC201`, the fix is not given if the non-break line ends in a line continuation character `\`. Note that lines are iterated in reverse from the docstring to the function definition. In the case of `DOC202`, the fix is not given if the docstring ends with a line continuation character `\`. ## Test Plan <!-- How was it tested? --> Added a test case.
This commit is contained in:
parent
4f60f0e925
commit
966fd6f57a
5 changed files with 64 additions and 19 deletions
|
|
@ -10,7 +10,7 @@ use ruff_text_size::TextRange;
|
|||
use crate::checkers::ast::Checker;
|
||||
use crate::docstrings::Docstring;
|
||||
use crate::registry::Rule;
|
||||
use crate::{AlwaysFixableViolation, Edit, Fix};
|
||||
use crate::{Edit, Fix, FixAvailability, Violation};
|
||||
|
||||
/// ## What it does
|
||||
/// Checks for docstrings on functions that are separated by one or more blank
|
||||
|
|
@ -42,15 +42,17 @@ pub(crate) struct BlankLineBeforeFunction {
|
|||
num_lines: usize,
|
||||
}
|
||||
|
||||
impl AlwaysFixableViolation for BlankLineBeforeFunction {
|
||||
impl Violation for BlankLineBeforeFunction {
|
||||
const FIX_AVAILABILITY: FixAvailability = FixAvailability::Sometimes;
|
||||
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
let BlankLineBeforeFunction { num_lines } = self;
|
||||
format!("No blank lines allowed before function docstring (found {num_lines})")
|
||||
}
|
||||
|
||||
fn fix_title(&self) -> String {
|
||||
"Remove blank line(s) before function docstring".to_string()
|
||||
fn fix_title(&self) -> Option<String> {
|
||||
Some("Remove blank line(s) before function docstring".to_string())
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -86,15 +88,17 @@ pub(crate) struct BlankLineAfterFunction {
|
|||
num_lines: usize,
|
||||
}
|
||||
|
||||
impl AlwaysFixableViolation for BlankLineAfterFunction {
|
||||
impl Violation for BlankLineAfterFunction {
|
||||
const FIX_AVAILABILITY: FixAvailability = FixAvailability::Sometimes;
|
||||
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
let BlankLineAfterFunction { num_lines } = self;
|
||||
format!("No blank lines allowed after function docstring (found {num_lines})")
|
||||
}
|
||||
|
||||
fn fix_title(&self) -> String {
|
||||
"Remove blank line(s) after function docstring".to_string()
|
||||
fn fix_title(&self) -> Option<String> {
|
||||
Some("Remove blank line(s) after function docstring".to_string())
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -115,12 +119,14 @@ pub(crate) fn blank_before_after_function(checker: &Checker, docstring: &Docstri
|
|||
let mut lines = UniversalNewlineIterator::with_offset(before, function.start()).rev();
|
||||
let mut blank_lines_before = 0usize;
|
||||
let mut blank_lines_start = lines.next().map(|l| l.end()).unwrap_or_default();
|
||||
let mut start_is_line_continuation = false;
|
||||
|
||||
for line in lines {
|
||||
if line.trim().is_empty() {
|
||||
blank_lines_before += 1;
|
||||
blank_lines_start = line.start();
|
||||
} else {
|
||||
start_is_line_continuation = line.ends_with('\\');
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
@ -132,11 +138,14 @@ pub(crate) fn blank_before_after_function(checker: &Checker, docstring: &Docstri
|
|||
},
|
||||
docstring.range(),
|
||||
);
|
||||
// Delete the blank line before the docstring.
|
||||
diagnostic.set_fix(Fix::safe_edit(Edit::deletion(
|
||||
blank_lines_start,
|
||||
docstring.line_start(),
|
||||
)));
|
||||
// Do not offer fix if a \ would cause it to be a syntax error
|
||||
if !start_is_line_continuation {
|
||||
// Delete the blank line before the docstring.
|
||||
diagnostic.set_fix(Fix::safe_edit(Edit::deletion(
|
||||
blank_lines_start,
|
||||
docstring.line_start(),
|
||||
)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -156,7 +165,9 @@ pub(crate) fn blank_before_after_function(checker: &Checker, docstring: &Docstri
|
|||
// Count the number of blank lines after the docstring.
|
||||
let mut blank_lines_after = 0usize;
|
||||
let mut lines = UniversalNewlineIterator::with_offset(after, docstring.end()).peekable();
|
||||
let first_line_end = lines.next().map(|l| l.end()).unwrap_or_default();
|
||||
let first_line = lines.next();
|
||||
let first_line_line_continuation = first_line.as_ref().is_some_and(|l| l.ends_with('\\'));
|
||||
let first_line_end = first_line.map(|l| l.end()).unwrap_or_default();
|
||||
let mut blank_lines_end = first_line_end;
|
||||
|
||||
while let Some(line) = lines.peek() {
|
||||
|
|
@ -185,11 +196,14 @@ pub(crate) fn blank_before_after_function(checker: &Checker, docstring: &Docstri
|
|||
},
|
||||
docstring.range(),
|
||||
);
|
||||
// Delete the blank line after the docstring.
|
||||
diagnostic.set_fix(Fix::safe_edit(Edit::deletion(
|
||||
first_line_end,
|
||||
blank_lines_end,
|
||||
)));
|
||||
// Do not offer fix if a \ would cause it to be a syntax error
|
||||
if !first_line_line_continuation {
|
||||
// Delete the blank line after the docstring.
|
||||
diagnostic.set_fix(Fix::safe_edit(Edit::deletion(
|
||||
first_line_end,
|
||||
blank_lines_end,
|
||||
)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -82,3 +82,14 @@ D.py:568:5: D201 [*] No blank lines allowed before function docstring (found 1)
|
|||
568 567 | """Trailing and leading space.
|
||||
569 568 |
|
||||
570 569 | More content.
|
||||
|
||||
D.py:729:5: D201 No blank lines allowed before function docstring (found 1)
|
||||
|
|
||||
727 | def line_continuation_chars():\
|
||||
728 |
|
||||
729 | """No fix should be offered for D201/D202 because of the line continuation chars."""\
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ D201
|
||||
730 |
|
||||
731 | ...
|
||||
|
|
||||
= help: Remove blank line(s) before function docstring
|
||||
|
|
|
|||
|
|
@ -85,4 +85,15 @@ D.py:568:5: D202 [*] No blank lines allowed after function docstring (found 1)
|
|||
572 |-
|
||||
573 572 | pass
|
||||
574 573 |
|
||||
575 574 |
|
||||
575 574 |
|
||||
|
||||
D.py:729:5: D202 No blank lines allowed after function docstring (found 1)
|
||||
|
|
||||
727 | def line_continuation_chars():\
|
||||
728 |
|
||||
729 | """No fix should be offered for D201/D202 because of the line continuation chars."""\
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ D202
|
||||
730 |
|
||||
731 | ...
|
||||
|
|
||||
= help: Remove blank line(s) after function docstring
|
||||
|
|
|
|||
|
|
@ -428,3 +428,5 @@ D.py:723:1: D208 [*] Docstring is over-indented
|
|||
723 |- Returns:
|
||||
723 |+ Returns:
|
||||
724 724 | """
|
||||
725 725 |
|
||||
726 726 |
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue