diff --git a/crates/ruff_linter/resources/test/fixtures/darglint/DAR401_google.py b/crates/ruff_linter/resources/test/fixtures/darglint/DAR401_google.py index 26b2c883f6718..cf8bd5698fe60 100644 --- a/crates/ruff_linter/resources/test/fixtures/darglint/DAR401_google.py +++ b/crates/ruff_linter/resources/test/fixtures/darglint/DAR401_google.py @@ -1,3 +1,13 @@ +from somewhere import AnotherError + + +class FasterThanLightError(Exception): + ... + + +_some_error = Exception + + # DAR401 def calculate_speed(distance: float, time: float) -> float: """Calculate speed as distance divided by time. @@ -90,6 +100,34 @@ def calculate_speed(distance: float, time: float) -> float: raise exc +# DAR401 +def calculate_speed(distance: float, time: float) -> float: + """Calculate speed as distance divided by time. + + Args: + distance: Distance traveled. + time: Time spent traveling. + + Returns: + Speed as distance divided by time. + """ + raise AnotherError + + +# DAR401, but can't resolve the error +def calculate_speed(distance: float, time: float) -> float: + """Calculate speed as distance divided by time. + + Args: + distance: Distance traveled. + time: Time spent traveling. + + Returns: + Speed as distance divided by time. + """ + raise _some_error + + # OK def calculate_speed(distance: float, time: float) -> float: try: diff --git a/crates/ruff_linter/resources/test/fixtures/darglint/DAR401_numpy.py b/crates/ruff_linter/resources/test/fixtures/darglint/DAR401_numpy.py index 05befb273a94a..81f28211e3585 100644 --- a/crates/ruff_linter/resources/test/fixtures/darglint/DAR401_numpy.py +++ b/crates/ruff_linter/resources/test/fixtures/darglint/DAR401_numpy.py @@ -1,3 +1,7 @@ +class FasterThanLightError(Exception): + ... + + def calculate_speed(distance: float, time: float) -> float: """ Calculate speed as distance divided by time. diff --git a/crates/ruff_linter/resources/test/fixtures/darglint/DAR402_google.py b/crates/ruff_linter/resources/test/fixtures/darglint/DAR402_google.py index d6a6254f51a88..a4af696c6e0e8 100644 --- a/crates/ruff_linter/resources/test/fixtures/darglint/DAR402_google.py +++ b/crates/ruff_linter/resources/test/fixtures/darglint/DAR402_google.py @@ -1,3 +1,7 @@ +class FasterThanLightError(Exception): + ... + + def calculate_speed(distance: float, time: float) -> float: """Calculate speed as distance divided by time. diff --git a/crates/ruff_linter/resources/test/fixtures/darglint/DAR402_numpy.py b/crates/ruff_linter/resources/test/fixtures/darglint/DAR402_numpy.py index 28859fbf0cba5..7cb75a7dd6053 100644 --- a/crates/ruff_linter/resources/test/fixtures/darglint/DAR402_numpy.py +++ b/crates/ruff_linter/resources/test/fixtures/darglint/DAR402_numpy.py @@ -1,3 +1,7 @@ +class FasterThanLightError(Exception): + ... + + def calculate_speed(distance: float, time: float) -> float: """ Calculate speed as distance divided by time. diff --git a/crates/ruff_linter/src/rules/darglint/rules/check_docstring.rs b/crates/ruff_linter/src/rules/darglint/rules/check_docstring.rs index 6ca81358fc8af..8376bf35c5a0c 100644 --- a/crates/ruff_linter/src/rules/darglint/rules/check_docstring.rs +++ b/crates/ruff_linter/src/rules/darglint/rules/check_docstring.rs @@ -1,10 +1,9 @@ -use std::collections::HashSet; - use ruff_diagnostics::Diagnostic; use ruff_diagnostics::Violation; use ruff_macros::{derive_message_formats, violation}; use ruff_python_ast::visitor::{self, Visitor}; -use ruff_python_ast::{self as ast, ExceptHandler, Expr, Stmt}; +use ruff_python_ast::{self as ast, Expr, Stmt}; +use ruff_python_semantic::SemanticModel; use ruff_python_semantic::{Definition, MemberKind}; use ruff_text_size::TextRange; @@ -195,27 +194,37 @@ struct Entry { range: TextRange, } -#[derive(Debug)] struct BodyEntries { raised_exceptions: Vec, - named_exceptions: HashSet, } -impl BodyEntries { - fn new() -> Self { +struct BodyVisitor<'a> { + raised_exceptions: Vec, + semantic: &'a SemanticModel<'a>, +} + +impl<'a> BodyVisitor<'a> { + fn new(semantic: &'a SemanticModel<'a>) -> Self { Self { raised_exceptions: Vec::new(), - named_exceptions: HashSet::new(), + semantic, + } + } + + fn finish(self) -> BodyEntries { + BodyEntries { + raised_exceptions: self.raised_exceptions, } } } -impl Visitor<'_> for BodyEntries { +impl Visitor<'_> for BodyVisitor<'_> { fn visit_stmt(&mut self, stmt: &Stmt) { if let Stmt::Raise(ast::StmtRaise { exc: Some(exc), .. }) = stmt { if let Expr::Name(ast::ExprName { id, range, .. }) = exc.as_ref() { - // Skip variable exceptions for now - if !self.named_exceptions.contains(id) { + // SemanticModel will resolve qualified_name for local Class definitions, + // or imported definitions, but not variables which we want to ignore. + if self.semantic.resolve_qualified_name(exc.as_ref()).is_some() { self.raised_exceptions.push(Entry { id: id.to_string(), range: *range, @@ -225,17 +234,6 @@ impl Visitor<'_> for BodyEntries { } visitor::walk_stmt(self, stmt); } - - fn visit_except_handler(&mut self, except_handler: &ExceptHandler) { - if let ExceptHandler::ExceptHandler(ast::ExceptHandlerExceptHandler { - name: Some(name), - .. - }) = except_handler - { - self.named_exceptions.insert(name.id.to_string()); - } - visitor::walk_except_handler(self, except_handler); - } } /// DAR401, DAR402 @@ -262,8 +260,9 @@ pub(crate) fn check_docstring( _ => DocstringEntries::new(section_contexts, section_contexts.style()), }; - let mut body_entries = BodyEntries::new(); - visitor::walk_body(&mut body_entries, member.body()); + let mut visitor = BodyVisitor::new(checker.semantic()); + visitor::walk_body(&mut visitor, member.body()); + let body_entries = visitor.finish(); // DAR401 if checker.enabled(Rule::DocstringMissingException) { diff --git a/crates/ruff_linter/src/rules/darglint/snapshots/ruff_linter__rules__darglint__tests__docstring-extraneous-exception_DAR402_google.py.snap b/crates/ruff_linter/src/rules/darglint/snapshots/ruff_linter__rules__darglint__tests__docstring-extraneous-exception_DAR402_google.py.snap index 3c6fc1c265687..830b2f9a15c20 100644 --- a/crates/ruff_linter/src/rules/darglint/snapshots/ruff_linter__rules__darglint__tests__docstring-extraneous-exception_DAR402_google.py.snap +++ b/crates/ruff_linter/src/rules/darglint/snapshots/ruff_linter__rules__darglint__tests__docstring-extraneous-exception_DAR402_google.py.snap @@ -1,38 +1,38 @@ --- source: crates/ruff_linter/src/rules/darglint/mod.rs --- -DAR402_google.py:11:1: DAR402 FasterThanLightError not explicitly raised. +DAR402_google.py:15:1: DAR402 FasterThanLightError not explicitly raised. | - 9 | Speed as distance divided by time. -10 | -11 | / Raises: -12 | | FasterThanLightError: If speed is greater than the speed of light. -13 | | """ +13 | Speed as distance divided by time. +14 | +15 | / Raises: +16 | | FasterThanLightError: If speed is greater than the speed of light. +17 | | """ | |____^ DAR402 -14 | return distance / time +18 | return distance / time | -DAR402_google.py:27:1: DAR402 FasterThanLightError, DivisionByZero not explicitly raised. +DAR402_google.py:31:1: DAR402 FasterThanLightError, DivisionByZero not explicitly raised. | -25 | Speed as distance divided by time. -26 | -27 | / Raises: -28 | | FasterThanLightError: If speed is greater than the speed of light. -29 | | DivisionByZero: Divide by zero. -30 | | """ +29 | Speed as distance divided by time. +30 | +31 | / Raises: +32 | | FasterThanLightError: If speed is greater than the speed of light. +33 | | DivisionByZero: Divide by zero. +34 | | """ | |____^ DAR402 -31 | return distance / time +35 | return distance / time | -DAR402_google.py:44:1: DAR402 DivisionByZero not explicitly raised. +DAR402_google.py:48:1: DAR402 DivisionByZero not explicitly raised. | -42 | Speed as distance divided by time. -43 | -44 | / Raises: -45 | | FasterThanLightError: If speed is greater than the speed of light. -46 | | DivisionByZero: Divide by zero. -47 | | """ +46 | Speed as distance divided by time. +47 | +48 | / Raises: +49 | | FasterThanLightError: If speed is greater than the speed of light. +50 | | DivisionByZero: Divide by zero. +51 | | """ | |____^ DAR402 -48 | try: -49 | return distance / time +52 | try: +53 | return distance / time | diff --git a/crates/ruff_linter/src/rules/darglint/snapshots/ruff_linter__rules__darglint__tests__docstring-extraneous-exception_DAR402_numpy.py.snap b/crates/ruff_linter/src/rules/darglint/snapshots/ruff_linter__rules__darglint__tests__docstring-extraneous-exception_DAR402_numpy.py.snap index e4fbe295878b6..5cf4f8089f890 100644 --- a/crates/ruff_linter/src/rules/darglint/snapshots/ruff_linter__rules__darglint__tests__docstring-extraneous-exception_DAR402_numpy.py.snap +++ b/crates/ruff_linter/src/rules/darglint/snapshots/ruff_linter__rules__darglint__tests__docstring-extraneous-exception_DAR402_numpy.py.snap @@ -1,46 +1,46 @@ --- source: crates/ruff_linter/src/rules/darglint/mod.rs --- -DAR402_numpy.py:17:1: DAR402 FasterThanLightError not explicitly raised. +DAR402_numpy.py:21:1: DAR402 FasterThanLightError not explicitly raised. | -15 | Speed as distance divided by time. -16 | -17 | / Raises -18 | | ------ -19 | | FasterThanLightError -20 | | If speed is greater than the speed of light. -21 | | """ +19 | Speed as distance divided by time. +20 | +21 | / Raises +22 | | ------ +23 | | FasterThanLightError +24 | | If speed is greater than the speed of light. +25 | | """ | |____^ DAR402 -22 | return distance / time +26 | return distance / time | -DAR402_numpy.py:41:1: DAR402 FasterThanLightError, DivisionByZero not explicitly raised. +DAR402_numpy.py:45:1: DAR402 FasterThanLightError, DivisionByZero not explicitly raised. | -39 | Speed as distance divided by time. -40 | -41 | / Raises -42 | | ------ -43 | | FasterThanLightError -44 | | If speed is greater than the speed of light. -45 | | DivisionByZero -46 | | If attempting to divide by zero. -47 | | """ +43 | Speed as distance divided by time. +44 | +45 | / Raises +46 | | ------ +47 | | FasterThanLightError +48 | | If speed is greater than the speed of light. +49 | | DivisionByZero +50 | | If attempting to divide by zero. +51 | | """ | |____^ DAR402 -48 | return distance / time +52 | return distance / time | -DAR402_numpy.py:67:1: DAR402 DivisionByZero not explicitly raised. +DAR402_numpy.py:71:1: DAR402 DivisionByZero not explicitly raised. | -65 | Speed as distance divided by time. -66 | -67 | / Raises -68 | | ------ -69 | | FasterThanLightError -70 | | If speed is greater than the speed of light. -71 | | DivisionByZero -72 | | If attempting to divide by zero. -73 | | """ +69 | Speed as distance divided by time. +70 | +71 | / Raises +72 | | ------ +73 | | FasterThanLightError +74 | | If speed is greater than the speed of light. +75 | | DivisionByZero +76 | | If attempting to divide by zero. +77 | | """ | |____^ DAR402 -74 | try: -75 | return distance / time +78 | try: +79 | return distance / time | diff --git a/crates/ruff_linter/src/rules/darglint/snapshots/ruff_linter__rules__darglint__tests__docstring-missing-exception_DAR401_google.py.snap b/crates/ruff_linter/src/rules/darglint/snapshots/ruff_linter__rules__darglint__tests__docstring-missing-exception_DAR401_google.py.snap index d631667aaa88e..fd3a6be24519d 100644 --- a/crates/ruff_linter/src/rules/darglint/snapshots/ruff_linter__rules__darglint__tests__docstring-missing-exception_DAR401_google.py.snap +++ b/crates/ruff_linter/src/rules/darglint/snapshots/ruff_linter__rules__darglint__tests__docstring-missing-exception_DAR401_google.py.snap @@ -1,28 +1,36 @@ --- source: crates/ruff_linter/src/rules/darglint/mod.rs --- -DAR401_google.py:35:15: DAR401 Raised exception `FasterThanLightError` missing from docstring +DAR401_google.py:45:15: DAR401 Raised exception `FasterThanLightError` missing from docstring | -33 | return distance / time -34 | except ZeroDivisionError as exc: -35 | raise FasterThanLightError from exc +43 | return distance / time +44 | except ZeroDivisionError as exc: +45 | raise FasterThanLightError from exc | ^^^^^^^^^^^^^^^^^^^^ DAR401 | -DAR401_google.py:52:15: DAR401 Raised exception `FasterThanLightError` missing from docstring +DAR401_google.py:62:15: DAR401 Raised exception `FasterThanLightError` missing from docstring | -50 | return distance / time -51 | except ZeroDivisionError as exc: -52 | raise FasterThanLightError from exc +60 | return distance / time +61 | except ZeroDivisionError as exc: +62 | raise FasterThanLightError from exc | ^^^^^^^^^^^^^^^^^^^^ DAR401 -53 | except: -54 | raise ValueError +63 | except: +64 | raise ValueError | -DAR401_google.py:54:15: DAR401 Raised exception `ValueError` missing from docstring +DAR401_google.py:64:15: DAR401 Raised exception `ValueError` missing from docstring | -52 | raise FasterThanLightError from exc -53 | except: -54 | raise ValueError +62 | raise FasterThanLightError from exc +63 | except: +64 | raise ValueError | ^^^^^^^^^^ DAR401 | + +DAR401_google.py:114:11: DAR401 Raised exception `AnotherError` missing from docstring + | +112 | Speed as distance divided by time. +113 | """ +114 | raise AnotherError + | ^^^^^^^^^^^^ DAR401 + | diff --git a/crates/ruff_linter/src/rules/darglint/snapshots/ruff_linter__rules__darglint__tests__docstring-missing-exception_DAR401_numpy.py.snap b/crates/ruff_linter/src/rules/darglint/snapshots/ruff_linter__rules__darglint__tests__docstring-missing-exception_DAR401_numpy.py.snap index c219ea4836732..88117860cd4d5 100644 --- a/crates/ruff_linter/src/rules/darglint/snapshots/ruff_linter__rules__darglint__tests__docstring-missing-exception_DAR401_numpy.py.snap +++ b/crates/ruff_linter/src/rules/darglint/snapshots/ruff_linter__rules__darglint__tests__docstring-missing-exception_DAR401_numpy.py.snap @@ -1,28 +1,28 @@ --- source: crates/ruff_linter/src/rules/darglint/mod.rs --- -DAR401_numpy.py:47:15: DAR401 Raised exception `FasterThanLightError` missing from docstring +DAR401_numpy.py:51:15: DAR401 Raised exception `FasterThanLightError` missing from docstring | -45 | return distance / time -46 | except ZeroDivisionError as exc: -47 | raise FasterThanLightError from exc +49 | return distance / time +50 | except ZeroDivisionError as exc: +51 | raise FasterThanLightError from exc | ^^^^^^^^^^^^^^^^^^^^ DAR401 | -DAR401_numpy.py:69:15: DAR401 Raised exception `FasterThanLightError` missing from docstring +DAR401_numpy.py:73:15: DAR401 Raised exception `FasterThanLightError` missing from docstring | -67 | return distance / time -68 | except ZeroDivisionError as exc: -69 | raise FasterThanLightError from exc +71 | return distance / time +72 | except ZeroDivisionError as exc: +73 | raise FasterThanLightError from exc | ^^^^^^^^^^^^^^^^^^^^ DAR401 -70 | except: -71 | raise ValueError +74 | except: +75 | raise ValueError | -DAR401_numpy.py:71:15: DAR401 Raised exception `ValueError` missing from docstring +DAR401_numpy.py:75:15: DAR401 Raised exception `ValueError` missing from docstring | -69 | raise FasterThanLightError from exc -70 | except: -71 | raise ValueError +73 | raise FasterThanLightError from exc +74 | except: +75 | raise ValueError | ^^^^^^^^^^ DAR401 |