Skip to content

Commit

Permalink
Avoid panics for implicitly-concatenated docstrings
Browse files Browse the repository at this point in the history
  • Loading branch information
charliermarsh committed Mar 17, 2023
1 parent 1dd3cbd commit 684f32d
Show file tree
Hide file tree
Showing 4 changed files with 40 additions and 2 deletions.
9 changes: 9 additions & 0 deletions crates/ruff/resources/test/fixtures/pydocstyle/sections.py
Original file line number Diff line number Diff line change
Expand Up @@ -504,3 +504,12 @@ def test_incorrect_indent(self, x=1, y=2): # noqa: D207, D213, D407
x: Test argument.
"""


def implicit_string_concatenation():
"""Toggle the gizmo.
Returns
A value of some sort.
""""Extra content"
14 changes: 13 additions & 1 deletion crates/ruff/src/checkers/ast/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ use crate::checkers::ast::deferred::Deferred;
use crate::docstrings::definition::{
transition_scope, Definition, DefinitionKind, Docstring, Documentable,
};
use crate::fs::relativize_path;
use crate::registry::{AsRule, Rule};
use crate::rules::{
flake8_2020, flake8_annotations, flake8_bandit, flake8_blind_except, flake8_boolean_trap,
Expand All @@ -50,7 +51,7 @@ use crate::rules::{
};
use crate::settings::types::PythonVersion;
use crate::settings::{flags, Settings};
use crate::{autofix, docstrings, noqa};
use crate::{autofix, docstrings, noqa, warn_user};

mod deferred;

Expand Down Expand Up @@ -5133,6 +5134,7 @@ impl<'a> Checker<'a> {
}
overloaded_name = flake8_annotations::helpers::overloaded_name(self, &definition);
}

if self.is_stub {
if self.settings.rules.enabled(Rule::DocstringInStub) {
flake8_pyi::rules::docstring_in_stubs(self, definition.docstring);
Expand Down Expand Up @@ -5162,6 +5164,16 @@ impl<'a> Checker<'a> {
Location::new(expr.location.row(), expr.location.column()),
));

if pydocstyle::helpers::should_ignore_docstring(contents) {
warn_user!(
"Docstring at {}:{}:{} contains implicit string concatenation; ignoring...",
relativize_path(self.path),
expr.location.row(),
expr.location.column() + 1
);
continue;
}

let body = str::raw_contents(contents);
let docstring = Docstring {
kind: definition.kind,
Expand Down
5 changes: 4 additions & 1 deletion crates/ruff/src/docstrings/extraction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,18 @@

use rustpython_parser::ast::{Constant, Expr, ExprKind, Stmt, StmtKind};

use crate::docstrings::definition::{Definition, DefinitionKind, Documentable};
use ruff_python_ast::visibility::{Modifier, VisibleScope};

use crate::docstrings::definition::{Definition, DefinitionKind, Documentable};

/// Extract a docstring from a function or class body.
pub fn docstring_from(suite: &[Stmt]) -> Option<&Expr> {
let stmt = suite.first()?;
// Require the docstring to be a standalone expression.
let StmtKind::Expr { value } = &stmt.node else {
return None;
};
// Only match strings.
if !matches!(
&value.node,
ExprKind::Constant {
Expand Down
14 changes: 14 additions & 0 deletions crates/ruff/src/rules/pydocstyle/helpers.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
use std::collections::BTreeSet;

use rustpython_parser::{lexer, Mode, Tok};

use ruff_python_ast::cast;
use ruff_python_ast::helpers::{map_callable, to_call_path};
use ruff_python_ast::newlines::StrExt;
Expand Down Expand Up @@ -60,3 +62,15 @@ pub fn should_ignore_definition(
}
false
}

/// Check if a docstring should be ignored.
pub fn should_ignore_docstring(contents: &str) -> bool {
// Avoid analyzing docstrings that contain implicit string concatenations.
// Python does consider these docstrings, but they're almost certainly a
// user error, and supporting them "properly" is extremely difficult.
lexer::lex(contents, Mode::Module)
.flatten()
.filter(|(_, tok, _)| matches!(tok, Tok::String { .. }))
.count()
> 1
}

0 comments on commit 684f32d

Please sign in to comment.