mirror of
https://github.com/astral-sh/ruff.git
synced 2025-10-03 23:25:14 +00:00
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:
parent
1835d7bb45
commit
c38617fa27
189 changed files with 2433 additions and 3210 deletions
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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(),
|
||||
|
|
|
@ -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(),
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue