diff --git a/crates/ruff_linter/resources/test/fixtures/pydoclint/DOC202_google.py b/crates/ruff_linter/resources/test/fixtures/pydoclint/DOC202_google.py index 416c833e28ca00..671a031937a06a 100644 --- a/crates/ruff_linter/resources/test/fixtures/pydoclint/DOC202_google.py +++ b/crates/ruff_linter/resources/test/fixtures/pydoclint/DOC202_google.py @@ -48,3 +48,14 @@ def bar(self) -> str: num (int): A number """ print('test') + + +# See: https://github.com/astral-sh/ruff/issues/12650 +class C: + def foo(self) -> int: + """Calculate x. + + Returns: + x + """ + raise NotImplementedError 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 5e85018b76d9ac..a971bee4f47bd6 100644 --- a/crates/ruff_linter/src/rules/pydoclint/rules/check_docstring.rs +++ b/crates/ruff_linter/src/rules/pydoclint/rules/check_docstring.rs @@ -6,6 +6,7 @@ use ruff_python_ast::helpers::map_callable; use ruff_python_ast::name::QualifiedName; use ruff_python_ast::visitor::Visitor; use ruff_python_ast::{self as ast, visitor, Expr, Stmt}; +use ruff_python_semantic::analyze::function_type; use ruff_python_semantic::{Definition, MemberKind, SemanticModel}; use ruff_text_size::{Ranged, TextRange}; @@ -645,15 +646,14 @@ pub(crate) fn check_docstring( convention: Option, ) { let mut diagnostics = Vec::new(); - let Definition::Member(member) = definition else { + + // Only check function docstrings. + let Some(function_def) = definition.as_function_def() else { return; }; - // Only check function docstrings. - if matches!( - member.kind, - MemberKind::Class(_) | MemberKind::NestedClass(_) - ) { + // Ignore stubs. + if function_type::is_stub(function_def, checker.semantic()) { return; } @@ -670,17 +670,19 @@ pub(crate) fn check_docstring( let body_entries = { let mut visitor = BodyVisitor::new(checker.semantic()); - visitor.visit_body(member.body()); + visitor.visit_body(&function_def.body); visitor.finish() }; // DOC201 - if checker.enabled(Rule::DocstringMissingReturns) && docstring_sections.returns.is_none() { - let extra_property_decorators = checker.settings.pydocstyle.property_decorators(); - if !definition.is_property(extra_property_decorators, checker.semantic()) { - if let Some(body_return) = body_entries.returns.first() { - let diagnostic = Diagnostic::new(DocstringMissingReturns, body_return.range()); - diagnostics.push(diagnostic); + if checker.enabled(Rule::DocstringMissingReturns) { + if docstring_sections.returns.is_none() { + let extra_property_decorators = checker.settings.pydocstyle.property_decorators(); + if !definition.is_property(extra_property_decorators, checker.semantic()) { + if let Some(body_return) = body_entries.returns.first() { + let diagnostic = Diagnostic::new(DocstringMissingReturns, body_return.range()); + diagnostics.push(diagnostic); + } } } }