Remove per-diagnostic check for fixability (#7919)

## Summary

Throughout the codebase, we have this pattern:

```rust
let mut diagnostic = ...
if checker.patch(Rule::UnusedVariable) {
    // Do the fix.
}
diagnostics.push(diagnostic)
```

This was helpful when we computed fixes lazily; however, we now compute
fixes eagerly, and this is _only_ used to ensure that we don't generate
fixes for rules marked as unfixable.

We often forget to add this, and it leads to bugs in enforcing
`--unfixable`.

This PR instead removes all of these checks, moving the responsibility
of enforcing `--unfixable` up to `check_path`. This is similar to how
@zanieb handled the `--extend-unsafe` logic: we post-process the
diagnostics to remove any fixes that should be ignored.
This commit is contained in:
Charlie Marsh 2023-10-11 12:09:47 -04:00 committed by GitHub
parent 1835d7bb45
commit c38617fa27
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
189 changed files with 2433 additions and 3210 deletions

View file

@ -5,7 +5,6 @@ use ruff_text_size::Ranged;
use crate::checkers::ast::Checker;
use crate::docstrings::Docstring;
use crate::registry::AsRule;
/// ## What it does
/// Checks for docstring summary lines that are not separated from the docstring
@ -91,35 +90,33 @@ pub(crate) fn blank_after_summary(checker: &mut Checker, docstring: &Docstring)
},
docstring.range(),
);
if checker.patch(diagnostic.kind.rule()) {
if blanks_count > 1 {
let mut lines = UniversalNewlineIterator::with_offset(&body, body.start());
let mut summary_end = body.start();
if blanks_count > 1 {
let mut lines = UniversalNewlineIterator::with_offset(&body, body.start());
let mut summary_end = body.start();
// Find the "summary" line (defined as the first non-blank line).
for line in lines.by_ref() {
if !line.trim().is_empty() {
summary_end = line.full_end();
break;
}
// Find the "summary" line (defined as the first non-blank line).
for line in lines.by_ref() {
if !line.trim().is_empty() {
summary_end = line.full_end();
break;
}
// Find the last blank line
let mut blank_end = summary_end;
for line in lines {
if !line.trim().is_empty() {
blank_end = line.start();
break;
}
}
// Insert one blank line after the summary (replacing any existing lines).
diagnostic.set_fix(Fix::safe_edit(Edit::replacement(
checker.stylist().line_ending().to_string(),
summary_end,
blank_end,
)));
}
// Find the last blank line
let mut blank_end = summary_end;
for line in lines {
if !line.trim().is_empty() {
blank_end = line.start();
break;
}
}
// Insert one blank line after the summary (replacing any existing lines).
diagnostic.set_fix(Fix::safe_edit(Edit::replacement(
checker.stylist().line_ending().to_string(),
summary_end,
blank_end,
)));
}
checker.diagnostics.push(diagnostic);
}

View file

@ -7,7 +7,7 @@ use ruff_text_size::{TextLen, TextRange};
use crate::checkers::ast::Checker;
use crate::docstrings::Docstring;
use crate::registry::{AsRule, Rule};
use crate::registry::Rule;
/// ## What it does
/// Checks for docstrings on class definitions that are not preceded by a
@ -189,27 +189,23 @@ pub(crate) fn blank_before_after_class(checker: &mut Checker, docstring: &Docstr
if checker.enabled(Rule::BlankLineBeforeClass) {
if blank_lines_before != 0 {
let mut diagnostic = Diagnostic::new(BlankLineBeforeClass, docstring.range());
if checker.patch(diagnostic.kind.rule()) {
// Delete the blank line before the class.
diagnostic.set_fix(Fix::safe_edit(Edit::deletion(
blank_lines_start,
docstring.start() - docstring.indentation.text_len(),
)));
}
// Delete the blank line before the class.
diagnostic.set_fix(Fix::safe_edit(Edit::deletion(
blank_lines_start,
docstring.start() - docstring.indentation.text_len(),
)));
checker.diagnostics.push(diagnostic);
}
}
if checker.enabled(Rule::OneBlankLineBeforeClass) {
if blank_lines_before != 1 {
let mut diagnostic = Diagnostic::new(OneBlankLineBeforeClass, docstring.range());
if checker.patch(diagnostic.kind.rule()) {
// Insert one blank line before the class.
diagnostic.set_fix(Fix::safe_edit(Edit::replacement(
checker.stylist().line_ending().to_string(),
blank_lines_start,
docstring.start() - docstring.indentation.text_len(),
)));
}
// Insert one blank line before the class.
diagnostic.set_fix(Fix::safe_edit(Edit::replacement(
checker.stylist().line_ending().to_string(),
blank_lines_start,
docstring.start() - docstring.indentation.text_len(),
)));
checker.diagnostics.push(diagnostic);
}
}
@ -243,21 +239,19 @@ pub(crate) fn blank_before_after_class(checker: &mut Checker, docstring: &Docstr
let indentation = indentation_at_offset(docstring.start(), checker.locator())
.expect("Own line docstring must have indentation");
let mut diagnostic = Diagnostic::new(OneBlankLineAfterClass, docstring.range());
if checker.patch(diagnostic.kind.rule()) {
let line_ending = checker.stylist().line_ending().as_str();
// We have to trim the whitespace twice, once before the semicolon above and
// once after the semicolon here, or we get invalid indents:
// ```rust
// class Priority:
// """Has priorities""" ; priorities=1
// ```
let next_statement = next_statement.trim_whitespace_start();
diagnostic.set_fix(Fix::safe_edit(Edit::replacement(
line_ending.to_string() + line_ending + indentation + next_statement,
replacement_start,
first_line.end(),
)));
}
let line_ending = checker.stylist().line_ending().as_str();
// We have to trim the whitespace twice, once before the semicolon above and
// once after the semicolon here, or we get invalid indents:
// ```rust
// class Priority:
// """Has priorities""" ; priorities=1
// ```
let next_statement = next_statement.trim_whitespace_start();
diagnostic.set_fix(Fix::safe_edit(Edit::replacement(
line_ending.to_string() + line_ending + indentation + next_statement,
replacement_start,
first_line.end(),
)));
checker.diagnostics.push(diagnostic);
return;
} else if trailing.starts_with('#') {
@ -280,14 +274,12 @@ pub(crate) fn blank_before_after_class(checker: &mut Checker, docstring: &Docstr
if blank_lines_after != 1 {
let mut diagnostic = Diagnostic::new(OneBlankLineAfterClass, docstring.range());
if checker.patch(diagnostic.kind.rule()) {
// Insert a blank line before the class (replacing any existing lines).
diagnostic.set_fix(Fix::safe_edit(Edit::replacement(
checker.stylist().line_ending().to_string(),
replacement_start,
blank_lines_end,
)));
}
// Insert a blank line before the class (replacing any existing lines).
diagnostic.set_fix(Fix::safe_edit(Edit::replacement(
checker.stylist().line_ending().to_string(),
replacement_start,
blank_lines_end,
)));
checker.diagnostics.push(diagnostic);
}
}

View file

@ -10,7 +10,7 @@ use ruff_text_size::{TextLen, TextRange};
use crate::checkers::ast::Checker;
use crate::docstrings::Docstring;
use crate::registry::{AsRule, Rule};
use crate::registry::Rule;
/// ## What it does
/// Checks for docstrings on functions that are separated by one or more blank
@ -132,13 +132,11 @@ pub(crate) fn blank_before_after_function(checker: &mut Checker, docstring: &Doc
},
docstring.range(),
);
if checker.patch(diagnostic.kind.rule()) {
// Delete the blank line before the docstring.
diagnostic.set_fix(Fix::safe_edit(Edit::deletion(
blank_lines_start,
docstring.start() - docstring.indentation.text_len(),
)));
}
// Delete the blank line before the docstring.
diagnostic.set_fix(Fix::safe_edit(Edit::deletion(
blank_lines_start,
docstring.start() - docstring.indentation.text_len(),
)));
checker.diagnostics.push(diagnostic);
}
}
@ -188,13 +186,11 @@ pub(crate) fn blank_before_after_function(checker: &mut Checker, docstring: &Doc
},
docstring.range(),
);
if checker.patch(diagnostic.kind.rule()) {
// Delete the blank line after the docstring.
diagnostic.set_fix(Fix::safe_edit(Edit::deletion(
first_line_end,
blank_lines_end,
)));
}
// Delete the blank line after the docstring.
diagnostic.set_fix(Fix::safe_edit(Edit::deletion(
first_line_end,
blank_lines_end,
)));
checker.diagnostics.push(diagnostic);
}
}

View file

@ -5,7 +5,6 @@ use ruff_text_size::{TextLen, TextRange};
use crate::checkers::ast::Checker;
use crate::docstrings::Docstring;
use crate::registry::AsRule;
/// ## What it does
/// Checks for docstrings that do not start with a capital letter.
@ -90,12 +89,10 @@ pub(crate) fn capitalized(checker: &mut Checker, docstring: &Docstring) {
docstring.expr.range(),
);
if checker.patch(diagnostic.kind.rule()) {
diagnostic.set_fix(Fix::safe_edit(Edit::range_replacement(
capitalized_word,
TextRange::at(body.start(), first_word.text_len()),
)));
}
diagnostic.set_fix(Fix::safe_edit(Edit::range_replacement(
capitalized_word,
TextRange::at(body.start(), first_word.text_len()),
)));
checker.diagnostics.push(diagnostic);
}

View file

@ -9,7 +9,7 @@ use ruff_text_size::Ranged;
use crate::checkers::ast::Checker;
use crate::docstrings::sections::SectionKind;
use crate::docstrings::Docstring;
use crate::registry::AsRule;
use crate::rules::pydocstyle::helpers::logical_line;
/// ## What it does
@ -104,7 +104,7 @@ pub(crate) fn ends_with_period(checker: &mut Checker, docstring: &Docstring) {
if !trimmed.ends_with('.') {
let mut diagnostic = Diagnostic::new(EndsInPeriod, docstring.range());
// Best-effort fix: avoid adding a period after other punctuation marks.
if checker.patch(diagnostic.kind.rule()) && !trimmed.ends_with([':', ';']) {
if !trimmed.ends_with([':', ';']) {
diagnostic.set_fix(Fix::unsafe_edit(Edit::insertion(
".".to_string(),
line.start() + trimmed.text_len(),

View file

@ -9,7 +9,7 @@ use ruff_text_size::Ranged;
use crate::checkers::ast::Checker;
use crate::docstrings::sections::SectionKind;
use crate::docstrings::Docstring;
use crate::registry::AsRule;
use crate::rules::pydocstyle::helpers::logical_line;
/// ## What it does
@ -103,7 +103,7 @@ pub(crate) fn ends_with_punctuation(checker: &mut Checker, docstring: &Docstring
if !trimmed.ends_with(['.', '!', '?']) {
let mut diagnostic = Diagnostic::new(EndsInPunctuation, docstring.range());
// Best-effort fix: avoid adding a period after other punctuation marks.
if checker.patch(diagnostic.kind.rule()) && !trimmed.ends_with([':', ';']) {
if !trimmed.ends_with([':', ';']) {
diagnostic.set_fix(Fix::unsafe_edit(Edit::insertion(
".".to_string(),
line.start() + trimmed.text_len(),

View file

@ -8,7 +8,7 @@ use ruff_text_size::{TextLen, TextRange};
use crate::checkers::ast::Checker;
use crate::docstrings::Docstring;
use crate::registry::{AsRule, Rule};
use crate::registry::Rule;
/// ## What it does
/// Checks for docstrings that are indented with tabs.
@ -188,12 +188,10 @@ pub(crate) fn indent(checker: &mut Checker, docstring: &Docstring) {
{
let mut diagnostic =
Diagnostic::new(UnderIndentation, TextRange::empty(line.start()));
if checker.patch(diagnostic.kind.rule()) {
diagnostic.set_fix(Fix::safe_edit(Edit::range_replacement(
clean_space(docstring.indentation),
TextRange::at(line.start(), line_indent.text_len()),
)));
}
diagnostic.set_fix(Fix::safe_edit(Edit::range_replacement(
clean_space(docstring.indentation),
TextRange::at(line.start(), line_indent.text_len()),
)));
checker.diagnostics.push(diagnostic);
}
}
@ -229,15 +227,13 @@ pub(crate) fn indent(checker: &mut Checker, docstring: &Docstring) {
// enables fix.
let mut diagnostic =
Diagnostic::new(OverIndentation, TextRange::empty(over_indented.start()));
if checker.patch(diagnostic.kind.rule()) {
let indent = clean_space(docstring.indentation);
let edit = if indent.is_empty() {
Edit::range_deletion(over_indented)
} else {
Edit::range_replacement(indent, over_indented)
};
diagnostic.set_fix(Fix::safe_edit(edit));
}
let indent = clean_space(docstring.indentation);
let edit = if indent.is_empty() {
Edit::range_deletion(over_indented)
} else {
Edit::range_replacement(indent, over_indented)
};
diagnostic.set_fix(Fix::safe_edit(edit));
checker.diagnostics.push(diagnostic);
}
}
@ -248,16 +244,14 @@ pub(crate) fn indent(checker: &mut Checker, docstring: &Docstring) {
if line_indent.len() > docstring.indentation.len() {
let mut diagnostic =
Diagnostic::new(OverIndentation, TextRange::empty(last.start()));
if checker.patch(diagnostic.kind.rule()) {
let indent = clean_space(docstring.indentation);
let range = TextRange::at(last.start(), line_indent.text_len());
let edit = if indent.is_empty() {
Edit::range_deletion(range)
} else {
Edit::range_replacement(indent, range)
};
diagnostic.set_fix(Fix::safe_edit(edit));
}
let indent = clean_space(docstring.indentation);
let range = TextRange::at(last.start(), line_indent.text_len());
let edit = if indent.is_empty() {
Edit::range_deletion(range)
} else {
Edit::range_replacement(indent, range)
};
diagnostic.set_fix(Fix::safe_edit(edit));
checker.diagnostics.push(diagnostic);
}
}

View file

@ -7,7 +7,7 @@ use ruff_text_size::{Ranged, TextRange, TextSize};
use crate::checkers::ast::Checker;
use crate::docstrings::Docstring;
use crate::registry::{AsRule, Rule};
use crate::registry::Rule;
/// ## What it does
/// Checks for docstring summary lines that are not positioned on the first
@ -137,16 +137,14 @@ pub(crate) fn multi_line_summary_start(checker: &mut Checker, docstring: &Docstr
if is_triple_quote(&first_line) {
if checker.enabled(Rule::MultiLineSummaryFirstLine) {
let mut diagnostic = Diagnostic::new(MultiLineSummaryFirstLine, docstring.range());
if checker.patch(diagnostic.kind.rule()) {
// Delete until first non-whitespace char.
for line in content_lines {
if let Some(end_column) = line.find(|c: char| !c.is_whitespace()) {
diagnostic.set_fix(Fix::safe_edit(Edit::deletion(
first_line.end(),
line.start() + TextSize::try_from(end_column).unwrap(),
)));
break;
}
// Delete until first non-whitespace char.
for line in content_lines {
if let Some(end_column) = line.find(|c: char| !c.is_whitespace()) {
diagnostic.set_fix(Fix::safe_edit(Edit::deletion(
first_line.end(),
line.start() + TextSize::try_from(end_column).unwrap(),
)));
break;
}
}
checker.diagnostics.push(diagnostic);
@ -163,46 +161,44 @@ pub(crate) fn multi_line_summary_start(checker: &mut Checker, docstring: &Docstr
} else {
if checker.enabled(Rule::MultiLineSummarySecondLine) {
let mut diagnostic = Diagnostic::new(MultiLineSummarySecondLine, docstring.range());
if checker.patch(diagnostic.kind.rule()) {
let mut indentation = String::from(docstring.indentation);
let mut fixable = true;
if !indentation.chars().all(char::is_whitespace) {
fixable = false;
let mut indentation = String::from(docstring.indentation);
let mut fixable = true;
if !indentation.chars().all(char::is_whitespace) {
fixable = false;
// If the docstring isn't on its own line, look at the statement indentation,
// and add the default indentation to get the "right" level.
if let Definition::Member(member) = &docstring.definition {
let stmt_line_start = checker.locator().line_start(member.start());
let stmt_indentation = checker
.locator()
.slice(TextRange::new(stmt_line_start, member.start()));
// If the docstring isn't on its own line, look at the statement indentation,
// and add the default indentation to get the "right" level.
if let Definition::Member(member) = &docstring.definition {
let stmt_line_start = checker.locator().line_start(member.start());
let stmt_indentation = checker
.locator()
.slice(TextRange::new(stmt_line_start, member.start()));
if stmt_indentation.chars().all(char::is_whitespace) {
indentation.clear();
indentation.push_str(stmt_indentation);
indentation.push_str(checker.stylist().indentation());
fixable = true;
}
};
}
if stmt_indentation.chars().all(char::is_whitespace) {
indentation.clear();
indentation.push_str(stmt_indentation);
indentation.push_str(checker.stylist().indentation());
fixable = true;
}
};
}
if fixable {
let prefix = leading_quote(contents).unwrap();
// Use replacement instead of insert to trim possible whitespace between leading
// quote and text.
let repl = format!(
"{}{}{}",
checker.stylist().line_ending().as_str(),
indentation,
first_line.strip_prefix(prefix).unwrap().trim_start()
);
if fixable {
let prefix = leading_quote(contents).unwrap();
// Use replacement instead of insert to trim possible whitespace between leading
// quote and text.
let repl = format!(
"{}{}{}",
checker.stylist().line_ending().as_str(),
indentation,
first_line.strip_prefix(prefix).unwrap().trim_start()
);
diagnostic.set_fix(Fix::safe_edit(Edit::replacement(
repl,
body.start(),
first_line.end(),
)));
}
diagnostic.set_fix(Fix::safe_edit(Edit::replacement(
repl,
body.start(),
first_line.end(),
)));
}
checker.diagnostics.push(diagnostic);
}

View file

@ -8,7 +8,6 @@ use ruff_text_size::Ranged;
use crate::checkers::ast::Checker;
use crate::docstrings::Docstring;
use crate::registry::AsRule;
/// ## What it does
/// Checks for multi-line docstrings whose closing quotes are not on their
@ -81,27 +80,25 @@ pub(crate) fn newline_after_last_paragraph(checker: &mut Checker, docstring: &Do
if last_line != "\"\"\"" && last_line != "'''" {
let mut diagnostic =
Diagnostic::new(NewLineAfterLastParagraph, docstring.range());
if checker.patch(diagnostic.kind.rule()) {
// Insert a newline just before the end-quote(s).
let num_trailing_quotes = "'''".text_len();
let num_trailing_spaces: TextSize = last_line
.chars()
.rev()
.skip(usize::from(num_trailing_quotes))
.take_while(|c| c.is_whitespace())
.map(TextLen::text_len)
.sum();
let content = format!(
"{}{}",
checker.stylist().line_ending().as_str(),
clean_space(docstring.indentation)
);
diagnostic.set_fix(Fix::safe_edit(Edit::replacement(
content,
docstring.expr.end() - num_trailing_quotes - num_trailing_spaces,
docstring.expr.end() - num_trailing_quotes,
)));
}
// Insert a newline just before the end-quote(s).
let num_trailing_quotes = "'''".text_len();
let num_trailing_spaces: TextSize = last_line
.chars()
.rev()
.skip(usize::from(num_trailing_quotes))
.take_while(|c| c.is_whitespace())
.map(TextLen::text_len)
.sum();
let content = format!(
"{}{}",
checker.stylist().line_ending().as_str(),
clean_space(docstring.indentation)
);
diagnostic.set_fix(Fix::safe_edit(Edit::replacement(
content,
docstring.expr.end() - num_trailing_quotes - num_trailing_spaces,
docstring.expr.end() - num_trailing_quotes,
)));
checker.diagnostics.push(diagnostic);
}
}

View file

@ -6,7 +6,7 @@ use ruff_text_size::{TextLen, TextRange};
use crate::checkers::ast::Checker;
use crate::docstrings::Docstring;
use crate::registry::AsRule;
use crate::rules::pydocstyle::helpers::ends_with_backslash;
/// ## What it does
@ -63,17 +63,14 @@ pub(crate) fn no_surrounding_whitespace(checker: &mut Checker, docstring: &Docst
return;
}
let mut diagnostic = Diagnostic::new(SurroundingWhitespace, docstring.range());
if checker.patch(diagnostic.kind.rule()) {
let quote = docstring.contents.chars().last().unwrap();
// If removing whitespace would lead to an invalid string of quote
// characters, avoid applying the fix.
if !trimmed.ends_with(quote) && !trimmed.starts_with(quote) && !ends_with_backslash(trimmed)
{
diagnostic.set_fix(Fix::safe_edit(Edit::range_replacement(
trimmed.to_string(),
TextRange::at(body.start(), line.text_len()),
)));
}
let quote = docstring.contents.chars().last().unwrap();
// If removing whitespace would lead to an invalid string of quote
// characters, avoid applying the fix.
if !trimmed.ends_with(quote) && !trimmed.starts_with(quote) && !ends_with_backslash(trimmed) {
diagnostic.set_fix(Fix::safe_edit(Edit::range_replacement(
trimmed.to_string(),
TextRange::at(body.start(), line.text_len()),
)));
}
checker.diagnostics.push(diagnostic);
}

View file

@ -6,7 +6,6 @@ use ruff_text_size::Ranged;
use crate::checkers::ast::Checker;
use crate::docstrings::Docstring;
use crate::registry::AsRule;
/// ## What it does
/// Checks for single-line docstrings that are broken across multiple lines.
@ -65,24 +64,22 @@ pub(crate) fn one_liner(checker: &mut Checker, docstring: &Docstring) {
if non_empty_line_count == 1 && line_count > 1 {
let mut diagnostic = Diagnostic::new(FitsOnOneLine, docstring.range());
if checker.patch(diagnostic.kind.rule()) {
if let (Some(leading), Some(trailing)) = (
leading_quote(docstring.contents),
trailing_quote(docstring.contents),
) {
// If removing whitespace would lead to an invalid string of quote
// characters, avoid applying the fix.
let body = docstring.body();
let trimmed = body.trim();
if trimmed.chars().rev().take_while(|c| *c == '\\').count() % 2 == 0
&& !trimmed.ends_with(trailing.chars().last().unwrap())
&& !trimmed.starts_with(leading.chars().last().unwrap())
{
diagnostic.set_fix(Fix::unsafe_edit(Edit::range_replacement(
format!("{leading}{trimmed}{trailing}"),
docstring.range(),
)));
}
if let (Some(leading), Some(trailing)) = (
leading_quote(docstring.contents),
trailing_quote(docstring.contents),
) {
// If removing whitespace would lead to an invalid string of quote
// characters, avoid applying the fix.
let body = docstring.body();
let trimmed = body.trim();
if trimmed.chars().rev().take_while(|c| *c == '\\').count() % 2 == 0
&& !trimmed.ends_with(trailing.chars().last().unwrap())
&& !trimmed.starts_with(leading.chars().last().unwrap())
{
diagnostic.set_fix(Fix::unsafe_edit(Edit::range_replacement(
format!("{leading}{trimmed}{trailing}"),
docstring.range(),
)));
}
}
checker.diagnostics.push(diagnostic);

View file

@ -18,7 +18,7 @@ use crate::checkers::ast::Checker;
use crate::docstrings::sections::{SectionContext, SectionContexts, SectionKind};
use crate::docstrings::styles::SectionStyle;
use crate::docstrings::Docstring;
use crate::registry::{AsRule, Rule};
use crate::registry::Rule;
use crate::rules::pydocstyle::settings::Convention;
/// ## What it does
@ -1388,12 +1388,9 @@ fn blanks_and_section_underline(
},
docstring.range(),
);
if checker.patch(diagnostic.kind.rule()) {
let range =
TextRange::new(context.following_range().start(), blank_lines_end);
// Delete any blank lines between the header and the underline.
diagnostic.set_fix(Fix::safe_edit(Edit::range_deletion(range)));
}
let range = TextRange::new(context.following_range().start(), blank_lines_end);
// Delete any blank lines between the header and the underline.
diagnostic.set_fix(Fix::safe_edit(Edit::range_deletion(range)));
checker.diagnostics.push(diagnostic);
}
}
@ -1412,20 +1409,18 @@ fn blanks_and_section_underline(
},
docstring.range(),
);
if checker.patch(diagnostic.kind.rule()) {
// Replace the existing underline with a line of the appropriate length.
let content = format!(
"{}{}{}",
clean_space(docstring.indentation),
"-".repeat(context.section_name().len()),
checker.stylist().line_ending().as_str()
);
diagnostic.set_fix(Fix::safe_edit(Edit::replacement(
content,
blank_lines_end,
non_blank_line.full_end(),
)));
};
// Replace the existing underline with a line of the appropriate length.
let content = format!(
"{}{}{}",
clean_space(docstring.indentation),
"-".repeat(context.section_name().len()),
checker.stylist().line_ending().as_str()
);
diagnostic.set_fix(Fix::safe_edit(Edit::replacement(
content,
blank_lines_end,
non_blank_line.full_end(),
)));
checker.diagnostics.push(diagnostic);
}
}
@ -1439,19 +1434,17 @@ fn blanks_and_section_underline(
},
docstring.range(),
);
if checker.patch(diagnostic.kind.rule()) {
// Replace the existing indentation with whitespace of the appropriate length.
let range = TextRange::at(
blank_lines_end,
leading_space.text_len() + TextSize::from(1),
);
let contents = clean_space(docstring.indentation);
diagnostic.set_fix(Fix::safe_edit(if contents.is_empty() {
Edit::range_deletion(range)
} else {
Edit::range_replacement(contents, range)
}));
};
// Replace the existing indentation with whitespace of the appropriate length.
let range = TextRange::at(
blank_lines_end,
leading_space.text_len() + TextSize::from(1),
);
let contents = clean_space(docstring.indentation);
diagnostic.set_fix(Fix::safe_edit(if contents.is_empty() {
Edit::range_deletion(range)
} else {
Edit::range_replacement(contents, range)
}));
checker.diagnostics.push(diagnostic);
}
}
@ -1484,13 +1477,11 @@ fn blanks_and_section_underline(
},
docstring.range(),
);
if checker.patch(diagnostic.kind.rule()) {
// Delete any blank lines between the header and content.
diagnostic.set_fix(Fix::safe_edit(Edit::deletion(
line_after_dashes.start(),
blank_lines_after_dashes_end,
)));
}
// Delete any blank lines between the header and content.
diagnostic.set_fix(Fix::safe_edit(Edit::deletion(
line_after_dashes.start(),
blank_lines_after_dashes_end,
)));
checker.diagnostics.push(diagnostic);
}
}
@ -1516,31 +1507,29 @@ fn blanks_and_section_underline(
},
docstring.range(),
);
if checker.patch(diagnostic.kind.rule()) {
// Add a dashed line (of the appropriate length) under the section header.
let content = format!(
"{}{}{}",
checker.stylist().line_ending().as_str(),
clean_space(docstring.indentation),
"-".repeat(context.section_name().len()),
);
if equal_line_found
&& non_blank_line.trim_whitespace().len() == context.section_name().len()
{
// If an existing underline is an equal sign line of the appropriate length,
// replace it with a dashed line.
diagnostic.set_fix(Fix::safe_edit(Edit::replacement(
content,
context.summary_range().end(),
non_blank_line.end(),
)));
} else {
// Otherwise, insert a dashed line after the section header.
diagnostic.set_fix(Fix::safe_edit(Edit::insertion(
content,
context.summary_range().end(),
)));
}
// Add a dashed line (of the appropriate length) under the section header.
let content = format!(
"{}{}{}",
checker.stylist().line_ending().as_str(),
clean_space(docstring.indentation),
"-".repeat(context.section_name().len()),
);
if equal_line_found
&& non_blank_line.trim_whitespace().len() == context.section_name().len()
{
// If an existing underline is an equal sign line of the appropriate length,
// replace it with a dashed line.
diagnostic.set_fix(Fix::safe_edit(Edit::replacement(
content,
context.summary_range().end(),
non_blank_line.end(),
)));
} else {
// Otherwise, insert a dashed line after the section header.
diagnostic.set_fix(Fix::safe_edit(Edit::insertion(
content,
context.summary_range().end(),
)));
}
checker.diagnostics.push(diagnostic);
}
@ -1552,12 +1541,9 @@ fn blanks_and_section_underline(
},
docstring.range(),
);
if checker.patch(diagnostic.kind.rule()) {
let range =
TextRange::new(context.following_range().start(), blank_lines_end);
// Delete any blank lines between the header and content.
diagnostic.set_fix(Fix::safe_edit(Edit::range_deletion(range)));
}
let range = TextRange::new(context.following_range().start(), blank_lines_end);
// Delete any blank lines between the header and content.
diagnostic.set_fix(Fix::safe_edit(Edit::range_deletion(range)));
checker.diagnostics.push(diagnostic);
}
}
@ -1572,20 +1558,18 @@ fn blanks_and_section_underline(
},
docstring.range(),
);
if checker.patch(diagnostic.kind.rule()) {
// Add a dashed line (of the appropriate length) under the section header.
let content = format!(
"{}{}{}",
checker.stylist().line_ending().as_str(),
clean_space(docstring.indentation),
"-".repeat(context.section_name().len()),
);
// Add a dashed line (of the appropriate length) under the section header.
let content = format!(
"{}{}{}",
checker.stylist().line_ending().as_str(),
clean_space(docstring.indentation),
"-".repeat(context.section_name().len()),
);
diagnostic.set_fix(Fix::safe_edit(Edit::insertion(
content,
context.summary_range().end(),
)));
}
diagnostic.set_fix(Fix::safe_edit(Edit::insertion(
content,
context.summary_range().end(),
)));
checker.diagnostics.push(diagnostic);
}
if checker.enabled(Rule::EmptyDocstringSection) {
@ -1614,15 +1598,13 @@ fn common_section(
},
docstring.range(),
);
if checker.patch(diagnostic.kind.rule()) {
// Replace the section title with the capitalized variant. This requires
// locating the start and end of the section name.
let section_range = context.section_name_range();
diagnostic.set_fix(Fix::safe_edit(Edit::range_replacement(
capitalized_section_name.to_string(),
section_range,
)));
}
// Replace the section title with the capitalized variant. This requires
// locating the start and end of the section name.
let section_range = context.section_name_range();
diagnostic.set_fix(Fix::safe_edit(Edit::range_replacement(
capitalized_section_name.to_string(),
section_range,
)));
checker.diagnostics.push(diagnostic);
}
}
@ -1636,17 +1618,15 @@ fn common_section(
},
docstring.range(),
);
if checker.patch(diagnostic.kind.rule()) {
// Replace the existing indentation with whitespace of the appropriate length.
let content = clean_space(docstring.indentation);
let fix_range = TextRange::at(context.start(), leading_space.text_len());
// Replace the existing indentation with whitespace of the appropriate length.
let content = clean_space(docstring.indentation);
let fix_range = TextRange::at(context.start(), leading_space.text_len());
diagnostic.set_fix(Fix::safe_edit(if content.is_empty() {
Edit::range_deletion(fix_range)
} else {
Edit::range_replacement(content, fix_range)
}));
};
diagnostic.set_fix(Fix::safe_edit(if content.is_empty() {
Edit::range_deletion(fix_range)
} else {
Edit::range_replacement(content, fix_range)
}));
checker.diagnostics.push(diagnostic);
}
}
@ -1662,13 +1642,11 @@ fn common_section(
},
docstring.range(),
);
if checker.patch(diagnostic.kind.rule()) {
// Add a newline at the beginning of the next section.
diagnostic.set_fix(Fix::safe_edit(Edit::insertion(
line_end.to_string(),
next.start(),
)));
}
// Add a newline at the beginning of the next section.
diagnostic.set_fix(Fix::safe_edit(Edit::insertion(
line_end.to_string(),
next.start(),
)));
checker.diagnostics.push(diagnostic);
}
} else {
@ -1679,13 +1657,11 @@ fn common_section(
},
docstring.range(),
);
if checker.patch(diagnostic.kind.rule()) {
// Add a newline after the section.
diagnostic.set_fix(Fix::safe_edit(Edit::insertion(
format!("{}{}", line_end, docstring.indentation),
context.end(),
)));
}
// Add a newline after the section.
diagnostic.set_fix(Fix::safe_edit(Edit::insertion(
format!("{}{}", line_end, docstring.indentation),
context.end(),
)));
checker.diagnostics.push(diagnostic);
}
}
@ -1702,13 +1678,11 @@ fn common_section(
},
docstring.range(),
);
if checker.patch(diagnostic.kind.rule()) {
// Add a blank line before the section.
diagnostic.set_fix(Fix::safe_edit(Edit::insertion(
line_end.to_string(),
context.start(),
)));
}
// Add a blank line before the section.
diagnostic.set_fix(Fix::safe_edit(Edit::insertion(
line_end.to_string(),
context.start(),
)));
checker.diagnostics.push(diagnostic);
}
}
@ -1898,13 +1872,11 @@ fn numpy_section(
},
docstring.range(),
);
if checker.patch(diagnostic.kind.rule()) {
let section_range = context.section_name_range();
diagnostic.set_fix(Fix::safe_edit(Edit::range_deletion(TextRange::at(
section_range.end(),
suffix.text_len(),
))));
}
let section_range = context.section_name_range();
diagnostic.set_fix(Fix::safe_edit(Edit::range_deletion(TextRange::at(
section_range.end(),
suffix.text_len(),
))));
checker.diagnostics.push(diagnostic);
}
@ -1934,14 +1906,12 @@ fn google_section(
},
docstring.range(),
);
if checker.patch(diagnostic.kind.rule()) {
// Replace the suffix.
let section_name_range = context.section_name_range();
diagnostic.set_fix(Fix::safe_edit(Edit::range_replacement(
":".to_string(),
TextRange::at(section_name_range.end(), suffix.text_len()),
)));
}
// Replace the suffix.
let section_name_range = context.section_name_range();
diagnostic.set_fix(Fix::safe_edit(Edit::range_replacement(
":".to_string(),
TextRange::at(section_name_range.end(), suffix.text_len()),
)));
checker.diagnostics.push(diagnostic);
}
}