Enforce max-doc-length for multi-line docstrings (#4347)

This commit is contained in:
Charlie Marsh 2023-05-10 11:06:07 -04:00 committed by GitHub
parent ddbe5a1243
commit 5f64d2346f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 57 additions and 12 deletions

View file

@ -2,7 +2,7 @@
"""Here's a top-level docstring that's over the limit.""" """Here's a top-level docstring that's over the limit."""
def f(): def f1():
"""Here's a docstring that's also over the limit.""" """Here's a docstring that's also over the limit."""
x = 1 # Here's a comment that's over the limit, but it's not standalone. x = 1 # Here's a comment that's over the limit, but it's not standalone.
@ -16,3 +16,16 @@ def f():
"This is also considered a docstring, and is over the limit." "This is also considered a docstring, and is over the limit."
def f2():
"""Here's a multi-line docstring.
It's over the limit on this line, which isn't the first line in the docstring.
"""
def f3():
"""Here's a multi-line docstring.
It's over the limit on this line, which isn't the first line in the docstring."""

View file

@ -118,7 +118,7 @@ pub fn check_physical_lines(
} }
while doc_lines_iter while doc_lines_iter
.next_if(|doc_line_start| line.range().contains(**doc_line_start)) .next_if(|doc_line_start| line.range().contains_inclusive(**doc_line_start))
.is_some() .is_some()
{ {
if enforce_doc_line_too_long { if enforce_doc_line_too_long {

View file

@ -1,14 +1,15 @@
//! Doc line extraction. In this context, a doc line is a line consisting of a //! Doc line extraction. In this context, a doc line is a line consisting of a
//! standalone comment or a constant string statement. //! standalone comment or a constant string statement.
use ruff_text_size::{TextRange, TextSize};
use std::iter::FusedIterator; use std::iter::FusedIterator;
use ruff_python_ast::source_code::Locator; use ruff_text_size::{TextRange, TextSize};
use rustpython_parser::ast::{Constant, ExprKind, Stmt, StmtKind, Suite}; use rustpython_parser::ast::{Constant, ExprKind, Stmt, StmtKind, Suite};
use rustpython_parser::lexer::LexResult; use rustpython_parser::lexer::LexResult;
use rustpython_parser::Tok; use rustpython_parser::Tok;
use ruff_python_ast::newlines::UniversalNewlineIterator;
use ruff_python_ast::source_code::Locator;
use ruff_python_ast::visitor; use ruff_python_ast::visitor;
use ruff_python_ast::visitor::Visitor; use ruff_python_ast::visitor::Visitor;
@ -69,12 +70,12 @@ impl Iterator for DocLines<'_> {
impl FusedIterator for DocLines<'_> {} impl FusedIterator for DocLines<'_> {}
#[derive(Default)] struct StringLinesVisitor<'a> {
struct StringLinesVisitor {
string_lines: Vec<TextSize>, string_lines: Vec<TextSize>,
locator: &'a Locator<'a>,
} }
impl Visitor<'_> for StringLinesVisitor { impl Visitor<'_> for StringLinesVisitor<'_> {
fn visit_stmt(&mut self, stmt: &Stmt) { fn visit_stmt(&mut self, stmt: &Stmt) {
if let StmtKind::Expr { value } = &stmt.node { if let StmtKind::Expr { value } = &stmt.node {
if let ExprKind::Constant { if let ExprKind::Constant {
@ -82,16 +83,30 @@ impl Visitor<'_> for StringLinesVisitor {
.. ..
} = &value.node } = &value.node
{ {
self.string_lines.push(value.start()); for line in UniversalNewlineIterator::with_offset(
self.locator.slice(value.range()),
value.start(),
) {
self.string_lines.push(line.start());
}
} }
} }
visitor::walk_stmt(self, stmt); visitor::walk_stmt(self, stmt);
} }
} }
impl<'a> StringLinesVisitor<'a> {
fn new(locator: &'a Locator<'a>) -> Self {
Self {
string_lines: Vec::new(),
locator,
}
}
}
/// Extract doc lines (standalone strings) start positions from an AST. /// Extract doc lines (standalone strings) start positions from an AST.
pub fn doc_lines_from_ast(python_ast: &Suite) -> Vec<TextSize> { pub fn doc_lines_from_ast(python_ast: &Suite, locator: &Locator) -> Vec<TextSize> {
let mut visitor = StringLinesVisitor::default(); let mut visitor = StringLinesVisitor::new(locator);
visitor.visit_body(python_ast); visitor.visit_body(python_ast);
visitor.string_lines visitor.string_lines
} }

View file

@ -163,7 +163,7 @@ pub fn check_path(
diagnostics.extend(import_diagnostics); diagnostics.extend(import_diagnostics);
} }
if use_doc_lines { if use_doc_lines {
doc_lines.extend(doc_lines_from_ast(&python_ast)); doc_lines.extend(doc_lines_from_ast(&python_ast, locator));
} }
} }
Err(parse_error) => { Err(parse_error) => {

View file

@ -10,7 +10,7 @@ W505.py:2:51: W505 Doc line too long (57 > 50 characters)
W505.py:6:51: W505 Doc line too long (56 > 50 characters) W505.py:6:51: W505 Doc line too long (56 > 50 characters)
| |
6 | def f(): 6 | def f1():
7 | """Here's a docstring that's also over the limit.""" 7 | """Here's a docstring that's also over the limit."""
| ^^^^^^ W505 | ^^^^^^ W505
8 | 8 |
@ -42,4 +42,21 @@ W505.py:18:51: W505 Doc line too long (61 > 50 characters)
| ^^^^^^^^^^^ W505 | ^^^^^^^^^^^ W505
| |
W505.py:24:51: W505 Doc line too long (82 > 50 characters)
|
24 | """Here's a multi-line docstring.
25 |
26 | It's over the limit on this line, which isn't the first line in the docstring.
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ W505
27 | """
|
W505.py:31:51: W505 Doc line too long (85 > 50 characters)
|
31 | """Here's a multi-line docstring.
32 |
33 | It's over the limit on this line, which isn't the first line in the docstring."""
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ W505
|