diff --git a/resources/test/fixtures/D200.py b/resources/test/fixtures/D200.py new file mode 100644 index 0000000000..9c44400e1a --- /dev/null +++ b/resources/test/fixtures/D200.py @@ -0,0 +1,25 @@ +def f(): + """ + Fail. + """ + + +def f(): + """Fail. + """ + + +def f(): + """ + Fail.""" + + +def f(): + """Pass.""" + + +def f(): + """Pass. + + More content here. + """ diff --git a/src/check_ast.rs b/src/check_ast.rs index 36de447568..4d13754b60 100644 --- a/src/check_ast.rs +++ b/src/check_ast.rs @@ -1890,6 +1890,9 @@ impl<'a> Checker<'a> { fn check_docstrings(&mut self) { while let Some(docstring) = self.docstrings.pop() { + if self.settings.enabled.contains(&CheckCode::D200) { + docstrings::one_liner(self, &docstring); + } if self.settings.enabled.contains(&CheckCode::D400) { docstrings::ends_with_period(self, &docstring); } diff --git a/src/checks.rs b/src/checks.rs index 267ae9313e..239f90da30 100644 --- a/src/checks.rs +++ b/src/checks.rs @@ -151,6 +151,7 @@ pub enum CheckCode { U007, U008, // pydocstyle + D200, D400, D419, // Meta @@ -250,6 +251,7 @@ pub enum CheckKind { UsePEP604Annotation, SuperCallWithParameters, // pydocstyle + OneLinerDocstring, EmptyDocstring, DocstringEndsInNonPeriod, // Meta @@ -361,6 +363,7 @@ impl CheckCode { CheckCode::U007 => CheckKind::UsePEP604Annotation, CheckCode::U008 => CheckKind::SuperCallWithParameters, // pydocstyle + CheckCode::D200 => CheckKind::OneLinerDocstring, CheckCode::D400 => CheckKind::DocstringEndsInNonPeriod, CheckCode::D419 => CheckKind::EmptyDocstring, // Meta @@ -451,6 +454,7 @@ impl CheckKind { CheckKind::UselessObjectInheritance(_) => &CheckCode::U004, CheckKind::SuperCallWithParameters => &CheckCode::U008, // pydocstyle + CheckKind::OneLinerDocstring => &CheckCode::D200, CheckKind::DocstringEndsInNonPeriod => &CheckCode::D400, CheckKind::EmptyDocstring => &CheckCode::D419, // Meta @@ -697,6 +701,7 @@ impl CheckKind { "Use `super()` instead of `super(__class__, self)`".to_string() } // pydocstyle + CheckKind::OneLinerDocstring => "One-line docstring should fit on one line".to_string(), CheckKind::DocstringEndsInNonPeriod => { "First line should end with a period".to_string() } diff --git a/src/docstrings.rs b/src/docstrings.rs index 72e519a4ee..fe256ea0a5 100644 --- a/src/docstrings.rs +++ b/src/docstrings.rs @@ -61,6 +61,33 @@ pub fn extract<'a, 'b>( None } +pub fn one_liner(checker: &mut Checker, docstring: &Docstring) { + if let ExprKind::Constant { + value: Constant::Str(string), + .. + } = &docstring.expr.node + { + let mut line_count = 0; + let mut non_empty_line_count = 0; + for line in string.lines() { + line_count += 1; + if !line.trim().is_empty() { + non_empty_line_count += 1; + } + if non_empty_line_count > 1 { + return; + } + } + + if non_empty_line_count == 1 && line_count > 1 { + checker.add_check(Check::new( + CheckKind::OneLinerDocstring, + Range::from_located(docstring.expr), + )); + } + } +} + pub fn not_empty(checker: &mut Checker, docstring: &Docstring) { if let ExprKind::Constant { value: Constant::Str(string), diff --git a/src/linter.rs b/src/linter.rs index e6ee6fe2ad..17d3420760 100644 --- a/src/linter.rs +++ b/src/linter.rs @@ -978,6 +978,18 @@ mod tests { Ok(()) } + #[test] + fn d200() -> Result<()> { + let mut checks = check_path( + Path::new("./resources/test/fixtures/D200.py"), + &settings::Settings::for_rule(CheckCode::D200), + &fixer::Mode::Generate, + )?; + checks.sort_by_key(|check| check.location); + insta::assert_yaml_snapshot!(checks); + Ok(()) + } + #[test] fn d400() -> Result<()> { let mut checks = check_path( diff --git a/src/snapshots/ruff__linter__tests__d200.snap b/src/snapshots/ruff__linter__tests__d200.snap new file mode 100644 index 0000000000..0ca97f562e --- /dev/null +++ b/src/snapshots/ruff__linter__tests__d200.snap @@ -0,0 +1,29 @@ +--- +source: src/linter.rs +expression: checks +--- +- kind: OneLinerDocstring + location: + row: 2 + column: 6 + end_location: + row: 4 + column: 8 + fix: ~ +- kind: OneLinerDocstring + location: + row: 8 + column: 6 + end_location: + row: 9 + column: 8 + fix: ~ +- kind: OneLinerDocstring + location: + row: 13 + column: 6 + end_location: + row: 14 + column: 13 + fix: ~ + diff --git a/src/snapshots/ruff__linter__tests__d400.snap b/src/snapshots/ruff__linter__tests__d400.snap index 79081a77d1..e59a48bc6b 100644 --- a/src/snapshots/ruff__linter__tests__d400.snap +++ b/src/snapshots/ruff__linter__tests__d400.snap @@ -2,7 +2,7 @@ source: src/linter.rs expression: checks --- -- kind: DocstringEndsInPeriod +- kind: DocstringEndsInNonPeriod location: row: 9 column: 6 @@ -10,7 +10,7 @@ expression: checks row: 12 column: 8 fix: ~ -- kind: DocstringEndsInPeriod +- kind: DocstringEndsInNonPeriod location: row: 16 column: 6 diff --git a/src/snapshots/ruff__linter__tests__d400.snap.new b/src/snapshots/ruff__linter__tests__d400.snap.new deleted file mode 100644 index 79f66a7c34..0000000000 --- a/src/snapshots/ruff__linter__tests__d400.snap.new +++ /dev/null @@ -1,22 +0,0 @@ ---- -source: src/linter.rs -assertion_line: 989 -expression: checks ---- -- kind: DocstringEndsInNonPeriod - location: - row: 9 - column: 6 - end_location: - row: 12 - column: 8 - fix: ~ -- kind: DocstringEndsInNonPeriod - location: - row: 16 - column: 6 - end_location: - row: 19 - column: 8 - fix: ~ -