From daccb3f4f354cfd01b024da502eb3e1c0313c9d4 Mon Sep 17 00:00:00 2001 From: Alex Waygood Date: Fri, 2 Aug 2024 23:17:06 +0100 Subject: [PATCH] [`pydoclint`] Deduplicate collected exceptions after traversing function bodies (#12642) --- .../test/fixtures/pydoclint/DOC501_google.py | 14 ++++++++++ .../test/fixtures/pydoclint/DOC501_numpy.py | 16 +++++++++++ .../rules/pydoclint/rules/check_docstring.rs | 27 ++++++++++++++++--- ...ng-missing-exception_DOC501_google.py.snap | 21 +++++++++++++++ ...ing-missing-exception_DOC501_numpy.py.snap | 21 +++++++++++++++ 5 files changed, 96 insertions(+), 3 deletions(-) diff --git a/crates/ruff_linter/resources/test/fixtures/pydoclint/DOC501_google.py b/crates/ruff_linter/resources/test/fixtures/pydoclint/DOC501_google.py index ab648696172ef..ff9ac372a6825 100644 --- a/crates/ruff_linter/resources/test/fixtures/pydoclint/DOC501_google.py +++ b/crates/ruff_linter/resources/test/fixtures/pydoclint/DOC501_google.py @@ -232,3 +232,17 @@ def calculate_speed(distance: float, time: float) -> float: except Exception as e: print(f"Oh no, we encountered {e}") raise + + +def foo(): + """Foo. + + Returns: + 42: int. + """ + if True: + raise TypeError # DOC501 + else: + raise TypeError # no DOC501 here because we already emitted a diagnostic for the earlier `raise TypeError` + raise ValueError # DOC501 + return 42 diff --git a/crates/ruff_linter/resources/test/fixtures/pydoclint/DOC501_numpy.py b/crates/ruff_linter/resources/test/fixtures/pydoclint/DOC501_numpy.py index 16c6e74124d08..55695bf88e9ac 100644 --- a/crates/ruff_linter/resources/test/fixtures/pydoclint/DOC501_numpy.py +++ b/crates/ruff_linter/resources/test/fixtures/pydoclint/DOC501_numpy.py @@ -133,3 +133,19 @@ def calculate_speed(distance: float, time: float) -> float: except Exception as e: print(f"Oh no, we encountered {e}") raise + + +def foo(): + """Foo. + + Returns + ------- + int + 42 + """ + if True: + raise TypeError # DOC501 + else: + raise TypeError # no DOC501 here because we already emitted a diagnostic for the earlier `raise TypeError` + raise ValueError # DOC501 + return 42 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 a0698e37476bd..5e85018b76d9a 100644 --- a/crates/ruff_linter/src/rules/pydoclint/rules/check_docstring.rs +++ b/crates/ruff_linter/src/rules/pydoclint/rules/check_docstring.rs @@ -523,10 +523,31 @@ impl<'a> BodyVisitor<'a> { } fn finish(self) -> BodyEntries<'a> { + let BodyVisitor { + returns, + yields, + mut raised_exceptions, + .. + } = self; + + // Deduplicate exceptions collected: + // no need to complain twice about `raise TypeError` not being documented + // just because there are two separate `raise TypeError` statements in the function + raised_exceptions.sort_unstable_by(|left, right| { + left.qualified_name + .segments() + .cmp(right.qualified_name.segments()) + .then_with(|| left.start().cmp(&right.start())) + .then_with(|| left.end().cmp(&right.end())) + }); + raised_exceptions.dedup_by(|left, right| { + left.qualified_name.segments() == right.qualified_name.segments() + }); + BodyEntries { - returns: self.returns, - yields: self.yields, - raised_exceptions: self.raised_exceptions, + returns, + yields, + raised_exceptions, } } } diff --git a/crates/ruff_linter/src/rules/pydoclint/snapshots/ruff_linter__rules__pydoclint__tests__docstring-missing-exception_DOC501_google.py.snap b/crates/ruff_linter/src/rules/pydoclint/snapshots/ruff_linter__rules__pydoclint__tests__docstring-missing-exception_DOC501_google.py.snap index 0976d183c270d..e8fbdff46c0e9 100644 --- a/crates/ruff_linter/src/rules/pydoclint/snapshots/ruff_linter__rules__pydoclint__tests__docstring-missing-exception_DOC501_google.py.snap +++ b/crates/ruff_linter/src/rules/pydoclint/snapshots/ruff_linter__rules__pydoclint__tests__docstring-missing-exception_DOC501_google.py.snap @@ -67,3 +67,24 @@ DOC501_google.py:213:9: DOC501 Raised exception `ZeroDivisionError` missing from 215 | print("Not a number? Shame on you!") | = help: Add `ZeroDivisionError` to the docstring + +DOC501_google.py:244:15: DOC501 Raised exception `TypeError` missing from docstring + | +242 | """ +243 | if True: +244 | raise TypeError # DOC501 + | ^^^^^^^^^ DOC501 +245 | else: +246 | raise TypeError # no DOC501 here because we already emitted a diagnostic for the earlier `raise TypeError` + | + = help: Add `TypeError` to the docstring + +DOC501_google.py:247:11: DOC501 Raised exception `ValueError` missing from docstring + | +245 | else: +246 | raise TypeError # no DOC501 here because we already emitted a diagnostic for the earlier `raise TypeError` +247 | raise ValueError # DOC501 + | ^^^^^^^^^^ DOC501 +248 | return 42 + | + = help: Add `ValueError` to the docstring diff --git a/crates/ruff_linter/src/rules/pydoclint/snapshots/ruff_linter__rules__pydoclint__tests__docstring-missing-exception_DOC501_numpy.py.snap b/crates/ruff_linter/src/rules/pydoclint/snapshots/ruff_linter__rules__pydoclint__tests__docstring-missing-exception_DOC501_numpy.py.snap index 7f08fa44b38d6..3511ea7a6a317 100644 --- a/crates/ruff_linter/src/rules/pydoclint/snapshots/ruff_linter__rules__pydoclint__tests__docstring-missing-exception_DOC501_numpy.py.snap +++ b/crates/ruff_linter/src/rules/pydoclint/snapshots/ruff_linter__rules__pydoclint__tests__docstring-missing-exception_DOC501_numpy.py.snap @@ -38,3 +38,24 @@ DOC501_numpy.py:111:9: DOC501 Raised exception `TypeError` missing from docstrin | ^^^^^ DOC501 | = help: Add `TypeError` to the docstring + +DOC501_numpy.py:147:15: DOC501 Raised exception `TypeError` missing from docstring + | +145 | """ +146 | if True: +147 | raise TypeError # DOC501 + | ^^^^^^^^^ DOC501 +148 | else: +149 | raise TypeError # no DOC501 here because we already emitted a diagnostic for the earlier `raise TypeError` + | + = help: Add `TypeError` to the docstring + +DOC501_numpy.py:150:11: DOC501 Raised exception `ValueError` missing from docstring + | +148 | else: +149 | raise TypeError # no DOC501 here because we already emitted a diagnostic for the earlier `raise TypeError` +150 | raise ValueError # DOC501 + | ^^^^^^^^^^ DOC501 +151 | return 42 + | + = help: Add `ValueError` to the docstring