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

[pycodestyle] Fix: Don't autofix if the first line ends in a question mark? (D400) #13399

Merged
merged 9 commits into from
Sep 20, 2024
20 changes: 20 additions & 0 deletions crates/ruff_linter/resources/test/fixtures/pydocstyle/D415.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
def f():
"Here's a line ending in a question mark?"
...


def f():
"""Here's a line ending in an exclamation mark!"""
...

def f():
"""Here's a line ending in a colon:"""
...

def f():
"""Here's a line ending in a semi colon;"""
...

def f():
"""Here's a line ending with a whitespace """
...
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 @@ -30,6 +30,7 @@ mod tests {
#[test_case(Rule::EndsInPeriod, Path::new("D.py"))]
#[test_case(Rule::EndsInPeriod, Path::new("D400.py"))]
#[test_case(Rule::EndsInPunctuation, Path::new("D.py"))]
#[test_case(Rule::EndsInPunctuation, Path::new("D415.py"))]
#[test_case(Rule::FirstLineCapitalized, Path::new("D.py"))]
#[test_case(Rule::FirstLineCapitalized, Path::new("D403.py"))]
#[test_case(Rule::FitsOnOneLine, Path::new("D.py"))]
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use ruff_text_size::TextLen;
use strum::IntoEnumIterator;

use ruff_diagnostics::{AlwaysFixableViolation, Diagnostic, Edit, Fix};
use ruff_diagnostics::{Diagnostic, Edit, Fix, FixAvailability, Violation};
use ruff_macros::{derive_message_formats, violation};
use ruff_source_file::{UniversalNewlineIterator, UniversalNewlines};
use ruff_text_size::Ranged;
Expand Down Expand Up @@ -47,14 +47,18 @@ use crate::rules::pydocstyle::helpers::logical_line;
#[violation]
pub struct EndsInPeriod;

impl AlwaysFixableViolation for EndsInPeriod {
impl Violation for EndsInPeriod {
/// `None` in the case a fix is never available or otherwise Some
/// [`FixAvailability`] describing the available fix.
const FIX_AVAILABILITY: FixAvailability = FixAvailability::Sometimes;

#[derive_message_formats]
fn message(&self) -> String {
format!("First line should end with a period")
}

fn fix_title(&self) -> String {
"Add period".to_string()
fn fix_title(&self) -> Option<String> {
Some("Add period".to_string())
}
}

Expand Down Expand Up @@ -104,7 +108,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 !trimmed.ends_with([':', ';']) {
if !trimmed.ends_with([':', ';', '?', '!']) {
diagnostic.set_fix(Fix::unsafe_edit(Edit::insertion(
".".to_string(),
line.start() + trimmed.text_len(),
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use ruff_text_size::TextLen;
use strum::IntoEnumIterator;

use ruff_diagnostics::{AlwaysFixableViolation, Diagnostic, Edit, Fix};
use ruff_diagnostics::{Diagnostic, Edit, Fix, FixAvailability, Violation};
use ruff_macros::{derive_message_formats, violation};
use ruff_source_file::{UniversalNewlineIterator, UniversalNewlines};
use ruff_text_size::Ranged;
Expand Down Expand Up @@ -46,14 +46,18 @@ use crate::rules::pydocstyle::helpers::logical_line;
#[violation]
pub struct EndsInPunctuation;

impl AlwaysFixableViolation for EndsInPunctuation {
impl Violation for EndsInPunctuation {
Copy link
Member

Choose a reason for hiding this comment

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

Nice find

/// `None` in the case a fix is never available or otherwise Some
/// [`FixAvailability`] describing the available fix.
const FIX_AVAILABILITY: FixAvailability = FixAvailability::Sometimes;

#[derive_message_formats]
fn message(&self) -> String {
format!("First line should end with a period, question mark, or exclamation point")
}

fn fix_title(&self) -> String {
"Add closing punctuation".to_string()
fn fix_title(&self) -> Option<String> {
Some("Add closing punctuation".to_string())
}
}

Expand Down Expand Up @@ -103,7 +107,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 !trimmed.ends_with([':', ';']) {
if !trimmed.ends_with([':', ';', '?', '!']) {
yahayaohinoyi marked this conversation as resolved.
Show resolved Hide resolved
diagnostic.set_fix(Fix::unsafe_edit(Edit::insertion(
".".to_string(),
line.start() + trimmed.text_len(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -194,24 +194,14 @@ D.py:487:5: D400 [*] First line should end with a period
489 489 |
490 490 |

D.py:514:5: D400 [*] First line should end with a period
D.py:514:5: D400 First line should end with a period
|
513 | def valid_google_string(): # noqa: D400
514 | """Test a valid something!"""
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ D400
|
= help: Add period

ℹ Unsafe fix
511 511 |
512 512 |
513 513 | def valid_google_string(): # noqa: D400
514 |- """Test a valid something!"""
514 |+ """Test a valid something!."""
515 515 |
516 516 |
517 517 | @expect("D415: First line should end with a period, question mark, "

D.py:520:5: D400 [*] First line should end with a period
|
518 | "or exclamation point (not 'g')")
Expand Down Expand Up @@ -328,6 +318,4 @@ D.py:664:5: D400 [*] First line should end with a period
665 |+ but continuations shouldn't be considered multi-line."
666 666 |
667 667 |
668 668 |


668 668 |
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
---
source: crates/ruff_linter/src/rules/pydocstyle/mod.rs
---
D415.py:11:5: D415 First line should end with a period, question mark, or exclamation point
|
10 | def f():
11 | """Here's a line ending in a colon:"""
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ D415
12 | ...
|
= help: Add closing punctuation

D415.py:15:5: D415 First line should end with a period, question mark, or exclamation point
|
14 | def f():
15 | """Here's a line ending in a semi colon;"""
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ D415
16 | ...
|
= help: Add closing punctuation

D415.py:19:5: D415 [*] First line should end with a period, question mark, or exclamation point
|
18 | def f():
19 | """Here's a line ending with a whitespace """
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ D415
20 | ...
|
= help: Add closing punctuation

ℹ Unsafe fix
16 16 | ...
17 17 |
18 18 | def f():
19 |- """Here's a line ending with a whitespace """
19 |+ """Here's a line ending with a whitespace. """
20 20 | ...
Loading