From 3a742c17f8a2c0c105df086c71296fb87d591c78 Mon Sep 17 00:00:00 2001 From: Auguste Lalande Date: Sun, 21 Jul 2024 15:30:06 -0400 Subject: [PATCH] [`pydoclint`] Fix `DOC501` panic #12428 (#12435) ## Summary Fix panic reported in #12428. Where a string would sometimes get split within a character boundary. This bypasses the need to split the string. This does not guarantee the correct formatting of the docstring, but neither did the previous implementation. Resolves #12428 ## Test Plan Test case added to fixture --- .../test/fixtures/pydoclint/DOC501.py | 8 ++++++++ crates/ruff_linter/src/rules/pydoclint/mod.rs | 11 +++++++++++ .../rules/pydoclint/rules/check_docstring.rs | 19 ++++++++++--------- ...docstring-missing-exception_DOC501.py.snap | 4 ++++ 4 files changed, 33 insertions(+), 9 deletions(-) create mode 100644 crates/ruff_linter/resources/test/fixtures/pydoclint/DOC501.py create mode 100644 crates/ruff_linter/src/rules/pydoclint/snapshots/ruff_linter__rules__pydoclint__tests__docstring-missing-exception_DOC501.py.snap diff --git a/crates/ruff_linter/resources/test/fixtures/pydoclint/DOC501.py b/crates/ruff_linter/resources/test/fixtures/pydoclint/DOC501.py new file mode 100644 index 00000000000000..fd3a371080a3f4 --- /dev/null +++ b/crates/ruff_linter/resources/test/fixtures/pydoclint/DOC501.py @@ -0,0 +1,8 @@ +# https://github.com/astral-sh/ruff/issues/12428 +def parse_bool(x, default=_parse_bool_sentinel): + """Parse a boolean value + bool or type(default) + Raises + `ValueError` + ê>>> all(parse_bool(x) for x in [True, "yes", "Yes", "true", "True", "on", "ON", "1", 1]) + """ diff --git a/crates/ruff_linter/src/rules/pydoclint/mod.rs b/crates/ruff_linter/src/rules/pydoclint/mod.rs index 539f310b91e51c..99fff1322d304c 100644 --- a/crates/ruff_linter/src/rules/pydoclint/mod.rs +++ b/crates/ruff_linter/src/rules/pydoclint/mod.rs @@ -15,6 +15,17 @@ mod tests { use crate::test::test_path; use crate::{assert_messages, settings}; + #[test_case(Rule::DocstringMissingException, Path::new("DOC501.py"))] + fn rules(rule_code: Rule, path: &Path) -> Result<()> { + let snapshot = format!("{}_{}", rule_code.as_ref(), path.to_string_lossy()); + let diagnostics = test_path( + Path::new("pydoclint").join(path).as_path(), + &settings::LinterSettings::for_rule(rule_code), + )?; + assert_messages!(snapshot, diagnostics); + Ok(()) + } + #[test_case(Rule::DocstringMissingException, Path::new("DOC501_google.py"))] #[test_case(Rule::DocstringExtraneousException, Path::new("DOC502_google.py"))] fn rules_google_style(rule_code: Rule, path: &Path) -> Result<()> { diff --git a/crates/ruff_linter/src/rules/pydoclint/rules/check_docstring.rs b/crates/ruff_linter/src/rules/pydoclint/rules/check_docstring.rs index e85d91fd1cca21..10d486bd3fb5a1 100644 --- a/crates/ruff_linter/src/rules/pydoclint/rules/check_docstring.rs +++ b/crates/ruff_linter/src/rules/pydoclint/rules/check_docstring.rs @@ -180,7 +180,7 @@ fn parse_entries(content: &str, style: SectionStyle) -> Vec { /// ``` fn parse_entries_google(content: &str) -> Vec { let mut entries: Vec = Vec::new(); - for potential in content.split('\n') { + for potential in content.lines() { let Some(colon_idx) = potential.find(':') else { continue; }; @@ -202,16 +202,17 @@ fn parse_entries_google(content: &str) -> Vec { /// ``` fn parse_entries_numpy(content: &str) -> Vec { let mut entries: Vec = Vec::new(); - let mut split = content.split('\n'); - let Some(dashes) = split.next() else { + let mut lines = content.lines(); + let Some(dashes) = lines.next() else { return entries; }; - let indentation = dashes.len() - dashes.trim_start().len(); - for potential in split { - if let Some(first_char) = potential.chars().nth(indentation) { - if !first_char.is_whitespace() { - let entry = potential[indentation..].trim(); - entries.push(QualifiedName::user_defined(entry)); + let indentation = &dashes[..dashes.len() - dashes.trim_start().len()]; + for potential in lines { + if let Some(entry) = potential.strip_prefix(indentation) { + if let Some(first_char) = entry.chars().next() { + if !first_char.is_whitespace() { + entries.push(QualifiedName::user_defined(entry.trim_end())); + } } } } diff --git a/crates/ruff_linter/src/rules/pydoclint/snapshots/ruff_linter__rules__pydoclint__tests__docstring-missing-exception_DOC501.py.snap b/crates/ruff_linter/src/rules/pydoclint/snapshots/ruff_linter__rules__pydoclint__tests__docstring-missing-exception_DOC501.py.snap new file mode 100644 index 00000000000000..d3c56b22a3cc29 --- /dev/null +++ b/crates/ruff_linter/src/rules/pydoclint/snapshots/ruff_linter__rules__pydoclint__tests__docstring-missing-exception_DOC501.py.snap @@ -0,0 +1,4 @@ +--- +source: crates/ruff_linter/src/rules/pydoclint/mod.rs +--- +