diff --git a/crates/ruff_linter/resources/test/fixtures/pydoclint/DOC402_google.py b/crates/ruff_linter/resources/test/fixtures/pydoclint/DOC402_google.py new file mode 100644 index 0000000000000..3c286bf697b9a --- /dev/null +++ b/crates/ruff_linter/resources/test/fixtures/pydoclint/DOC402_google.py @@ -0,0 +1,68 @@ +# DOC402 +def foo(num: int) -> str: + """ + Do something + + Args: + num (int): A number + """ + yield 'test' + + +# OK +def foo(num: int) -> str: + """ + Do something + + Args: + num (int): A number + + Yields: + str: A string + """ + yield 'test' + + +class Bar: + + # OK + def foo(self) -> str: + """ + Do something + + Args: + num (int): A number + + Yields: + str: A string + """ + yield 'test' + + + # DOC402 + def bar(self) -> str: + """ + Do something + + Args: + num (int): A number + """ + yield 'test' + + +# OK +def test(): + """Do something.""" + # DOC402 + def nested(): + """Do something nested.""" + yield 5 + + print("I never yield") + + +# DOC402 +def test(): + """Do something.""" + yield from range(10) + diff --git a/crates/ruff_linter/resources/test/fixtures/pydoclint/DOC402_numpy.py b/crates/ruff_linter/resources/test/fixtures/pydoclint/DOC402_numpy.py new file mode 100644 index 0000000000000..bde7a2afdea8d --- /dev/null +++ b/crates/ruff_linter/resources/test/fixtures/pydoclint/DOC402_numpy.py @@ -0,0 +1,62 @@ +# DOC402 +def foo(num: int) -> str: + """ + Do something + + Parameters + ---------- + num : int + A number + """ + yield 'test' + + +# OK +def foo(num: int) -> str: + """ + Do something + + Parameters + ---------- + num : int + A number + + Yields + ------- + str + A string + """ + yield 'test' + + +class Bar: + + # OK + def foo(self) -> str: + """ + Do something + + Parameters + ---------- + num : int + A number + + Yields + ------- + str + A string + """ + yield 'test' + + + # DOC402 + def bar(self) -> str: + """ + Do something + + Parameters + ---------- + num : int + A number + """ + yield 'test' diff --git a/crates/ruff_linter/resources/test/fixtures/pydoclint/DOC403_google.py b/crates/ruff_linter/resources/test/fixtures/pydoclint/DOC403_google.py new file mode 100644 index 0000000000000..70c9c53112eef --- /dev/null +++ b/crates/ruff_linter/resources/test/fixtures/pydoclint/DOC403_google.py @@ -0,0 +1,50 @@ +# OK +def foo(num: int) -> str: + """ + Do something + + Args: + num (int): A number + """ + print('test') + + +# DOC403 +def foo(num: int) -> str: + """ + Do something + + Args: + num (int): A number + + Yields: + str: A string + """ + print('test') + + +class Bar: + + # DOC403 + def foo(self) -> str: + """ + Do something + + Args: + num (int): A number + + Yields: + str: A string + """ + print('test') + + + # OK + def bar(self) -> str: + """ + Do something + + Args: + num (int): A number + """ + print('test') diff --git a/crates/ruff_linter/resources/test/fixtures/pydoclint/DOC403_numpy.py b/crates/ruff_linter/resources/test/fixtures/pydoclint/DOC403_numpy.py new file mode 100644 index 0000000000000..5d5c646a90f2f --- /dev/null +++ b/crates/ruff_linter/resources/test/fixtures/pydoclint/DOC403_numpy.py @@ -0,0 +1,62 @@ +# OK +def foo(num: int) -> str: + """ + Do something + + Parameters + ---------- + num : int + A number + """ + print('test') + + +# DOC403 +def foo(num: int) -> str: + """ + Do something + + Parameters + ---------- + num : int + A number + + Yields + ------- + str + A string + """ + print('test') + + +class Bar: + + # DOC403 + def foo(self) -> str: + """ + Do something + + Parameters + ---------- + num : int + A number + + Yields + ------- + str + A string + """ + print('test') + + + # OK + def bar(self) -> str: + """ + Do something + + Parameters + ---------- + num : int + A number + """ + print('test') diff --git a/crates/ruff_linter/src/checkers/ast/analyze/definitions.rs b/crates/ruff_linter/src/checkers/ast/analyze/definitions.rs index 9f2995de35486..2f1dcda09e953 100644 --- a/crates/ruff_linter/src/checkers/ast/analyze/definitions.rs +++ b/crates/ruff_linter/src/checkers/ast/analyze/definitions.rs @@ -86,6 +86,8 @@ pub(crate) fn definitions(checker: &mut Checker) { let enforce_pydoclint = checker.any_enabled(&[ Rule::DocstringMissingReturns, Rule::DocstringExtraneousReturns, + Rule::DocstringMissingYields, + Rule::DocstringExtraneousYields, Rule::DocstringMissingException, Rule::DocstringExtraneousException, ]); diff --git a/crates/ruff_linter/src/codes.rs b/crates/ruff_linter/src/codes.rs index e475e680516d8..969c5dc4b7066 100644 --- a/crates/ruff_linter/src/codes.rs +++ b/crates/ruff_linter/src/codes.rs @@ -923,6 +923,8 @@ pub fn code_to_rule(linter: Linter, code: &str) -> Option<(RuleGroup, Rule)> { // pydoclint (Pydoclint, "201") => (RuleGroup::Preview, rules::pydoclint::rules::DocstringMissingReturns), (Pydoclint, "202") => (RuleGroup::Preview, rules::pydoclint::rules::DocstringExtraneousReturns), + (Pydoclint, "402") => (RuleGroup::Preview, rules::pydoclint::rules::DocstringMissingYields), + (Pydoclint, "403") => (RuleGroup::Preview, rules::pydoclint::rules::DocstringExtraneousYields), (Pydoclint, "501") => (RuleGroup::Preview, rules::pydoclint::rules::DocstringMissingException), (Pydoclint, "502") => (RuleGroup::Preview, rules::pydoclint::rules::DocstringExtraneousException), diff --git a/crates/ruff_linter/src/rules/pydoclint/mod.rs b/crates/ruff_linter/src/rules/pydoclint/mod.rs index 6ef019ce5e99f..68565de689e19 100644 --- a/crates/ruff_linter/src/rules/pydoclint/mod.rs +++ b/crates/ruff_linter/src/rules/pydoclint/mod.rs @@ -28,6 +28,8 @@ mod tests { #[test_case(Rule::DocstringMissingReturns, Path::new("DOC201_google.py"))] #[test_case(Rule::DocstringExtraneousReturns, Path::new("DOC202_google.py"))] + #[test_case(Rule::DocstringMissingYields, Path::new("DOC402_google.py"))] + #[test_case(Rule::DocstringExtraneousYields, Path::new("DOC403_google.py"))] #[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<()> { @@ -45,6 +47,8 @@ mod tests { #[test_case(Rule::DocstringMissingReturns, Path::new("DOC201_numpy.py"))] #[test_case(Rule::DocstringExtraneousReturns, Path::new("DOC202_numpy.py"))] + #[test_case(Rule::DocstringMissingYields, Path::new("DOC402_numpy.py"))] + #[test_case(Rule::DocstringExtraneousYields, Path::new("DOC403_numpy.py"))] #[test_case(Rule::DocstringMissingException, Path::new("DOC501_numpy.py"))] #[test_case(Rule::DocstringExtraneousException, Path::new("DOC502_numpy.py"))] fn rules_numpy_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 fbe27104382b5..63a98edc6a686 100644 --- a/crates/ruff_linter/src/rules/pydoclint/rules/check_docstring.rs +++ b/crates/ruff_linter/src/rules/pydoclint/rules/check_docstring.rs @@ -3,8 +3,8 @@ use ruff_diagnostics::Diagnostic; use ruff_diagnostics::Violation; use ruff_macros::{derive_message_formats, violation}; use ruff_python_ast::name::QualifiedName; -use ruff_python_ast::statement_visitor::StatementVisitor; -use ruff_python_ast::{self as ast, statement_visitor, Expr, Stmt}; +use ruff_python_ast::visitor::Visitor; +use ruff_python_ast::{self as ast, visitor, Expr, Stmt}; use ruff_python_semantic::{Definition, MemberKind, SemanticModel}; use ruff_text_size::{Ranged, TextRange}; @@ -15,7 +15,7 @@ use crate::registry::Rule; use crate::rules::pydocstyle::settings::Convention; /// ## What it does -/// Checks for functions with explicit returns missing a returns section in +/// Checks for functions with explicit returns missing a "returns" section in /// their docstring. /// /// ## Why is this bad? @@ -56,10 +56,14 @@ impl Violation for DocstringMissingReturns { fn message(&self) -> String { format!("`return` is not documented in docstring") } + + fn fix_title(&self) -> Option { + Some(format!("Add a \"Returns\" section to the docstring")) + } } /// ## What it does -/// Checks for function docstrings that have a returns section without +/// Checks for function docstrings that have a "returns" section without /// needing one. /// /// ## Why is this bad? @@ -100,11 +104,111 @@ impl Violation for DocstringExtraneousReturns { fn message(&self) -> String { format!("Docstring should not have a returns section because the function doesn't return anything") } + + fn fix_title(&self) -> Option { + Some(format!("Remove the \"Returns\" section")) + } +} + +/// ## What it does +/// Checks for functions with yield statements missing a "yields" section in +/// their docstring. +/// +/// ## Why is this bad? +/// Docstrings missing yields sections are a sign of incomplete documentation +/// or refactors. +/// +/// ## Example +/// ```python +/// def count_to_n(n: int) -> int: +/// """Generate integers up to *n*. +/// +/// Args: +/// n: The number at which to stop counting. +/// """ +/// for i in range(1, n + 1): +/// yield i +/// ``` +/// +/// Use instead: +/// ```python +/// def count_to_n(n: int) -> int: +/// """Generate integers up to *n*. +/// +/// Args: +/// n: The number at which to stop counting. +/// +/// Yields: +/// int: The number we're at in the count. +/// """ +/// for i in range(1, n + 1): +/// yield i +/// ``` +#[violation] +pub struct DocstringMissingYields; + +impl Violation for DocstringMissingYields { + #[derive_message_formats] + fn message(&self) -> String { + format!("`yield` is not documented in docstring") + } + + fn fix_title(&self) -> Option { + Some(format!("Add a \"Yields\" section to the docstring")) + } +} + +/// ## What it does +/// Checks for function docstrings that have a "yields" section without +/// needing one. +/// +/// ## Why is this bad? +/// Functions which don't yield anything should not have a yields section +/// in their docstrings. +/// +/// ## Example +/// ```python +/// def say_hello(n: int) -> None: +/// """Says hello to the user. +/// +/// Args: +/// n: Number of times to say hello. +/// +/// Yields: +/// Doesn't yield anything. +/// """ +/// for _ in range(n): +/// print("Hello!") +/// ``` +/// +/// Use instead: +/// ```python +/// def say_hello(n: int) -> None: +/// """Says hello to the user. +/// +/// Args: +/// n: Number of times to say hello. +/// """ +/// for _ in range(n): +/// print("Hello!") +/// ``` +#[violation] +pub struct DocstringExtraneousYields; + +impl Violation for DocstringExtraneousYields { + #[derive_message_formats] + fn message(&self) -> String { + format!("Docstring has a \"Yields\" section but the function doesn't yield anything") + } + + fn fix_title(&self) -> Option { + Some(format!("Remove the \"Yields\" section")) + } } /// ## What it does /// Checks for function docstrings that do not include documentation for all -/// explicitly-raised exceptions. +/// explicitly raised exceptions. /// /// ## Why is this bad? /// If a function raises an exception without documenting it in its docstring, @@ -160,6 +264,11 @@ impl Violation for DocstringMissingException { let DocstringMissingException { id } = self; format!("Raised exception `{id}` missing from docstring") } + + fn fix_title(&self) -> Option { + let DocstringMissingException { id } = self; + Some(format!("Add `{id}` to the docstring")) + } } /// ## What it does @@ -221,6 +330,14 @@ impl Violation for DocstringExtraneousException { ) } } + + fn fix_title(&self) -> Option { + let DocstringExtraneousException { ids } = self; + Some(format!( + "Remove {} from the docstring", + ids.iter().map(|id| format!("`{id}`")).join(", ") + )) + } } // A generic docstring section. @@ -267,24 +384,31 @@ impl<'a> RaisesSection<'a> { } } -#[derive(Debug)] +#[derive(Debug, Default)] struct DocstringSections<'a> { returns: Option, + yields: Option, raises: Option>, } impl<'a> DocstringSections<'a> { fn from_sections(sections: &'a SectionContexts, style: SectionStyle) -> Self { - let mut returns: Option = None; - let mut raises: Option = None; - for section in sections.iter() { + let mut docstring_sections = Self::default(); + for section in sections { match section.kind() { - SectionKind::Raises => raises = Some(RaisesSection::from_section(§ion, style)), - SectionKind::Returns => returns = Some(GenericSection::from_section(§ion)), + SectionKind::Raises => { + docstring_sections.raises = Some(RaisesSection::from_section(§ion, style)); + } + SectionKind::Returns => { + docstring_sections.returns = Some(GenericSection::from_section(§ion)); + } + SectionKind::Yields => { + docstring_sections.yields = Some(GenericSection::from_section(§ion)); + } _ => continue, } } - Self { returns, raises } + docstring_sections } } @@ -373,12 +497,14 @@ impl Ranged for ExceptionEntry<'_> { #[derive(Debug)] struct BodyEntries<'a> { returns: Vec, + yields: Vec, raised_exceptions: Vec>, } /// An AST visitor to extract a summary of documentable statements from a function body. struct BodyVisitor<'a> { returns: Vec, + yields: Vec, raised_exceptions: Vec>, semantic: &'a SemanticModel<'a>, } @@ -387,6 +513,7 @@ impl<'a> BodyVisitor<'a> { fn new(semantic: &'a SemanticModel) -> Self { Self { returns: Vec::new(), + yields: Vec::new(), raised_exceptions: Vec::new(), semantic, } @@ -395,12 +522,13 @@ impl<'a> BodyVisitor<'a> { fn finish(self) -> BodyEntries<'a> { BodyEntries { returns: self.returns, + yields: self.yields, raised_exceptions: self.raised_exceptions, } } } -impl<'a> StatementVisitor<'a> for BodyVisitor<'a> { +impl<'a> Visitor<'a> for BodyVisitor<'a> { fn visit_stmt(&mut self, stmt: &'a Stmt) { match stmt { Stmt::Raise(ast::StmtRaise { exc: Some(exc), .. }) => { @@ -422,7 +550,24 @@ impl<'a> StatementVisitor<'a> for BodyVisitor<'a> { _ => {} } - statement_visitor::walk_stmt(self, stmt); + visitor::walk_stmt(self, stmt); + } + + fn visit_expr(&mut self, expr: &'a Expr) { + match expr { + Expr::Yield(ast::ExprYield { + range, + value: Some(_), + }) => { + self.yields.push(Entry { range: *range }); + } + Expr::YieldFrom(ast::ExprYieldFrom { range, .. }) => { + self.yields.push(Entry { range: *range }); + } + Expr::Lambda(_) => return, + _ => {} + } + visitor::walk_expr(self, expr); } } @@ -439,7 +584,7 @@ fn extract_raised_exception<'a>( None } -/// DOC201, DOC202, DOC501, DOC502 +/// DOC201, DOC202, DOC402, DOC403, DOC501, DOC502 pub(crate) fn check_docstring( checker: &mut Checker, definition: &Definition, @@ -498,6 +643,27 @@ pub(crate) fn check_docstring( } } + // DOC402 + if checker.enabled(Rule::DocstringMissingYields) { + if docstring_sections.yields.is_none() { + if let Some(body_yield) = body_entries.yields.first() { + let diagnostic = Diagnostic::new(DocstringMissingYields, body_yield.range()); + diagnostics.push(diagnostic); + } + } + } + + // DOC403 + if checker.enabled(Rule::DocstringExtraneousYields) { + if let Some(docstring_yields) = docstring_sections.yields { + if body_entries.yields.is_empty() { + let diagnostic = + Diagnostic::new(DocstringExtraneousYields, docstring_yields.range()); + diagnostics.push(diagnostic); + } + } + } + // DOC501 if checker.enabled(Rule::DocstringMissingException) { for body_raise in &body_entries.raised_exceptions { diff --git a/crates/ruff_linter/src/rules/pydoclint/snapshots/ruff_linter__rules__pydoclint__tests__docstring-extraneous-exception_DOC502_google.py.snap b/crates/ruff_linter/src/rules/pydoclint/snapshots/ruff_linter__rules__pydoclint__tests__docstring-extraneous-exception_DOC502_google.py.snap index 8ef9ed882159f..58a44b6914d46 100644 --- a/crates/ruff_linter/src/rules/pydoclint/snapshots/ruff_linter__rules__pydoclint__tests__docstring-extraneous-exception_DOC502_google.py.snap +++ b/crates/ruff_linter/src/rules/pydoclint/snapshots/ruff_linter__rules__pydoclint__tests__docstring-extraneous-exception_DOC502_google.py.snap @@ -11,6 +11,7 @@ DOC502_google.py:16:1: DOC502 Raised exception is not explicitly raised: `Faster | |____^ DOC502 19 | return distance / time | + = help: Remove `FasterThanLightError` from the docstring DOC502_google.py:33:1: DOC502 Raised exceptions are not explicitly raised: `FasterThanLightError`, `DivisionByZero` | @@ -23,6 +24,7 @@ DOC502_google.py:33:1: DOC502 Raised exceptions are not explicitly raised: `Fast | |____^ DOC502 37 | return distance / time | + = help: Remove `FasterThanLightError`, `DivisionByZero` from the docstring DOC502_google.py:51:1: DOC502 Raised exception is not explicitly raised: `DivisionByZero` | @@ -36,3 +38,4 @@ DOC502_google.py:51:1: DOC502 Raised exception is not explicitly raised: `Divisi 55 | try: 56 | return distance / time | + = help: Remove `DivisionByZero` from the docstring diff --git a/crates/ruff_linter/src/rules/pydoclint/snapshots/ruff_linter__rules__pydoclint__tests__docstring-extraneous-exception_DOC502_numpy.py.snap b/crates/ruff_linter/src/rules/pydoclint/snapshots/ruff_linter__rules__pydoclint__tests__docstring-extraneous-exception_DOC502_numpy.py.snap index 41498f2f6e03b..34a0964a08e31 100644 --- a/crates/ruff_linter/src/rules/pydoclint/snapshots/ruff_linter__rules__pydoclint__tests__docstring-extraneous-exception_DOC502_numpy.py.snap +++ b/crates/ruff_linter/src/rules/pydoclint/snapshots/ruff_linter__rules__pydoclint__tests__docstring-extraneous-exception_DOC502_numpy.py.snap @@ -13,6 +13,7 @@ DOC502_numpy.py:22:1: DOC502 Raised exception is not explicitly raised: `FasterT | |____^ DOC502 27 | return distance / time | + = help: Remove `FasterThanLightError` from the docstring DOC502_numpy.py:47:1: DOC502 Raised exceptions are not explicitly raised: `FasterThanLightError`, `DivisionByZero` | @@ -28,6 +29,7 @@ DOC502_numpy.py:47:1: DOC502 Raised exceptions are not explicitly raised: `Faste | |____^ DOC502 54 | return distance / time | + = help: Remove `FasterThanLightError`, `DivisionByZero` from the docstring DOC502_numpy.py:74:1: DOC502 Raised exception is not explicitly raised: `DivisionByZero` | @@ -44,3 +46,4 @@ DOC502_numpy.py:74:1: DOC502 Raised exception is not explicitly raised: `Divisio 81 | try: 82 | return distance / time | + = help: Remove `DivisionByZero` from the docstring diff --git a/crates/ruff_linter/src/rules/pydoclint/snapshots/ruff_linter__rules__pydoclint__tests__docstring-extraneous-returns_DOC202_google.py.snap b/crates/ruff_linter/src/rules/pydoclint/snapshots/ruff_linter__rules__pydoclint__tests__docstring-extraneous-returns_DOC202_google.py.snap index 452c014484502..16aa8e5fb2c6a 100644 --- a/crates/ruff_linter/src/rules/pydoclint/snapshots/ruff_linter__rules__pydoclint__tests__docstring-extraneous-returns_DOC202_google.py.snap +++ b/crates/ruff_linter/src/rules/pydoclint/snapshots/ruff_linter__rules__pydoclint__tests__docstring-extraneous-returns_DOC202_google.py.snap @@ -11,6 +11,7 @@ DOC202_google.py:20:1: DOC202 Docstring should not have a returns section becaus | |____^ DOC202 23 | print('test') | + = help: Remove the "Returns" section DOC202_google.py:36:1: DOC202 Docstring should not have a returns section because the function doesn't return anything | @@ -22,3 +23,4 @@ DOC202_google.py:36:1: DOC202 Docstring should not have a returns section becaus | |________^ DOC202 39 | print('test') | + = help: Remove the "Returns" section diff --git a/crates/ruff_linter/src/rules/pydoclint/snapshots/ruff_linter__rules__pydoclint__tests__docstring-extraneous-returns_DOC202_numpy.py.snap b/crates/ruff_linter/src/rules/pydoclint/snapshots/ruff_linter__rules__pydoclint__tests__docstring-extraneous-returns_DOC202_numpy.py.snap index efd1bf91b02a0..4c1e3fffe4dd3 100644 --- a/crates/ruff_linter/src/rules/pydoclint/snapshots/ruff_linter__rules__pydoclint__tests__docstring-extraneous-returns_DOC202_numpy.py.snap +++ b/crates/ruff_linter/src/rules/pydoclint/snapshots/ruff_linter__rules__pydoclint__tests__docstring-extraneous-returns_DOC202_numpy.py.snap @@ -13,6 +13,7 @@ DOC202_numpy.py:24:1: DOC202 Docstring should not have a returns section because | |____^ DOC202 29 | print('test') | + = help: Remove the "Returns" section DOC202_numpy.py:44:1: DOC202 Docstring should not have a returns section because the function doesn't return anything | @@ -26,3 +27,4 @@ DOC202_numpy.py:44:1: DOC202 Docstring should not have a returns section because | |________^ DOC202 49 | print('test') | + = help: Remove the "Returns" section diff --git a/crates/ruff_linter/src/rules/pydoclint/snapshots/ruff_linter__rules__pydoclint__tests__docstring-extraneous-yields_DOC403_google.py.snap b/crates/ruff_linter/src/rules/pydoclint/snapshots/ruff_linter__rules__pydoclint__tests__docstring-extraneous-yields_DOC403_google.py.snap new file mode 100644 index 0000000000000..2c3c651b226ff --- /dev/null +++ b/crates/ruff_linter/src/rules/pydoclint/snapshots/ruff_linter__rules__pydoclint__tests__docstring-extraneous-yields_DOC403_google.py.snap @@ -0,0 +1,26 @@ +--- +source: crates/ruff_linter/src/rules/pydoclint/mod.rs +--- +DOC403_google.py:20:1: DOC403 Docstring has a "Yields" section but the function doesn't yield anything + | +18 | num (int): A number +19 | +20 | / Yields: +21 | | str: A string +22 | | """ + | |____^ DOC403 +23 | print('test') + | + = help: Remove the "Yields" section + +DOC403_google.py:36:1: DOC403 Docstring has a "Yields" section but the function doesn't yield anything + | +34 | num (int): A number +35 | +36 | / Yields: +37 | | str: A string +38 | | """ + | |________^ DOC403 +39 | print('test') + | + = help: Remove the "Yields" section diff --git a/crates/ruff_linter/src/rules/pydoclint/snapshots/ruff_linter__rules__pydoclint__tests__docstring-extraneous-yields_DOC403_numpy.py.snap b/crates/ruff_linter/src/rules/pydoclint/snapshots/ruff_linter__rules__pydoclint__tests__docstring-extraneous-yields_DOC403_numpy.py.snap new file mode 100644 index 0000000000000..aff49d2869d95 --- /dev/null +++ b/crates/ruff_linter/src/rules/pydoclint/snapshots/ruff_linter__rules__pydoclint__tests__docstring-extraneous-yields_DOC403_numpy.py.snap @@ -0,0 +1,30 @@ +--- +source: crates/ruff_linter/src/rules/pydoclint/mod.rs +--- +DOC403_numpy.py:24:1: DOC403 Docstring has a "Yields" section but the function doesn't yield anything + | +22 | A number +23 | +24 | / Yields +25 | | ------- +26 | | str +27 | | A string +28 | | """ + | |____^ DOC403 +29 | print('test') + | + = help: Remove the "Yields" section + +DOC403_numpy.py:44:1: DOC403 Docstring has a "Yields" section but the function doesn't yield anything + | +42 | A number +43 | +44 | / Yields +45 | | ------- +46 | | str +47 | | A string +48 | | """ + | |________^ DOC403 +49 | print('test') + | + = help: Remove the "Yields" section 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 8ea9749d5246d..7f1c628eadedb 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 @@ -8,6 +8,7 @@ DOC501_google.py:46:15: DOC501 Raised exception `FasterThanLightError` missing f 46 | raise FasterThanLightError from exc | ^^^^^^^^^^^^^^^^^^^^ DOC501 | + = help: Add `FasterThanLightError` to the docstring DOC501_google.py:63:15: DOC501 Raised exception `FasterThanLightError` missing from docstring | @@ -18,6 +19,7 @@ DOC501_google.py:63:15: DOC501 Raised exception `FasterThanLightError` missing f 64 | except: 65 | raise ValueError | + = help: Add `FasterThanLightError` to the docstring DOC501_google.py:65:15: DOC501 Raised exception `ValueError` missing from docstring | @@ -26,6 +28,7 @@ DOC501_google.py:65:15: DOC501 Raised exception `ValueError` missing from docstr 65 | raise ValueError | ^^^^^^^^^^ DOC501 | + = help: Add `ValueError` to the docstring DOC501_google.py:115:11: DOC501 Raised exception `AnotherError` missing from docstring | @@ -34,6 +37,7 @@ DOC501_google.py:115:11: DOC501 Raised exception `AnotherError` missing from doc 115 | raise AnotherError | ^^^^^^^^^^^^ DOC501 | + = help: Add `AnotherError` to the docstring DOC501_google.py:129:11: DOC501 Raised exception `AnotherError` missing from docstring | @@ -42,6 +46,7 @@ DOC501_google.py:129:11: DOC501 Raised exception `AnotherError` missing from doc 129 | raise AnotherError() | ^^^^^^^^^^^^^^ DOC501 | + = help: Add `AnotherError` to the docstring DOC501_google.py:139:11: DOC501 Raised exception `SomeError` missing from docstring | @@ -50,3 +55,4 @@ DOC501_google.py:139:11: DOC501 Raised exception `SomeError` missing from docstr 139 | raise something.SomeError | ^^^^^^^^^^^^^^^^^^^ DOC501 | + = help: Add `SomeError` 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 f91ec86eb3b1b..96823553213e3 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 @@ -8,6 +8,7 @@ DOC501_numpy.py:53:15: DOC501 Raised exception `FasterThanLightError` missing fr 53 | raise FasterThanLightError from exc | ^^^^^^^^^^^^^^^^^^^^ DOC501 | + = help: Add `FasterThanLightError` to the docstring DOC501_numpy.py:76:15: DOC501 Raised exception `FasterThanLightError` missing from docstring | @@ -18,6 +19,7 @@ DOC501_numpy.py:76:15: DOC501 Raised exception `FasterThanLightError` missing fr 77 | except: 78 | raise ValueError | + = help: Add `FasterThanLightError` to the docstring DOC501_numpy.py:78:15: DOC501 Raised exception `ValueError` missing from docstring | @@ -26,3 +28,4 @@ DOC501_numpy.py:78:15: DOC501 Raised exception `ValueError` missing from docstri 78 | raise ValueError | ^^^^^^^^^^ DOC501 | + = help: Add `ValueError` to the docstring diff --git a/crates/ruff_linter/src/rules/pydoclint/snapshots/ruff_linter__rules__pydoclint__tests__docstring-missing-returns_DOC201_google.py.snap b/crates/ruff_linter/src/rules/pydoclint/snapshots/ruff_linter__rules__pydoclint__tests__docstring-missing-returns_DOC201_google.py.snap index 950bbfd97545c..779d0c4d452eb 100644 --- a/crates/ruff_linter/src/rules/pydoclint/snapshots/ruff_linter__rules__pydoclint__tests__docstring-missing-returns_DOC201_google.py.snap +++ b/crates/ruff_linter/src/rules/pydoclint/snapshots/ruff_linter__rules__pydoclint__tests__docstring-missing-returns_DOC201_google.py.snap @@ -8,6 +8,7 @@ DOC201_google.py:9:5: DOC201 `return` is not documented in docstring 9 | return 'test' | ^^^^^^^^^^^^^ DOC201 | + = help: Add a "Returns" section to the docstring DOC201_google.py:50:9: DOC201 `return` is not documented in docstring | @@ -16,6 +17,7 @@ DOC201_google.py:50:9: DOC201 `return` is not documented in docstring 50 | return 'test' | ^^^^^^^^^^^^^ DOC201 | + = help: Add a "Returns" section to the docstring DOC201_google.py:71:9: DOC201 `return` is not documented in docstring | @@ -26,3 +28,4 @@ DOC201_google.py:71:9: DOC201 `return` is not documented in docstring 72 | 73 | print("I never return") | + = help: Add a "Returns" section to the docstring diff --git a/crates/ruff_linter/src/rules/pydoclint/snapshots/ruff_linter__rules__pydoclint__tests__docstring-missing-returns_DOC201_numpy.py.snap b/crates/ruff_linter/src/rules/pydoclint/snapshots/ruff_linter__rules__pydoclint__tests__docstring-missing-returns_DOC201_numpy.py.snap index 759d261092a32..363f87d07c4cd 100644 --- a/crates/ruff_linter/src/rules/pydoclint/snapshots/ruff_linter__rules__pydoclint__tests__docstring-missing-returns_DOC201_numpy.py.snap +++ b/crates/ruff_linter/src/rules/pydoclint/snapshots/ruff_linter__rules__pydoclint__tests__docstring-missing-returns_DOC201_numpy.py.snap @@ -8,6 +8,7 @@ DOC201_numpy.py:11:5: DOC201 `return` is not documented in docstring 11 | return 'test' | ^^^^^^^^^^^^^ DOC201 | + = help: Add a "Returns" section to the docstring DOC201_numpy.py:62:9: DOC201 `return` is not documented in docstring | @@ -16,3 +17,4 @@ DOC201_numpy.py:62:9: DOC201 `return` is not documented in docstring 62 | return 'test' | ^^^^^^^^^^^^^ DOC201 | + = help: Add a "Returns" section to the docstring diff --git a/crates/ruff_linter/src/rules/pydoclint/snapshots/ruff_linter__rules__pydoclint__tests__docstring-missing-yields_DOC402_google.py.snap b/crates/ruff_linter/src/rules/pydoclint/snapshots/ruff_linter__rules__pydoclint__tests__docstring-missing-yields_DOC402_google.py.snap new file mode 100644 index 0000000000000..c9ebc3f280864 --- /dev/null +++ b/crates/ruff_linter/src/rules/pydoclint/snapshots/ruff_linter__rules__pydoclint__tests__docstring-missing-yields_DOC402_google.py.snap @@ -0,0 +1,40 @@ +--- +source: crates/ruff_linter/src/rules/pydoclint/mod.rs +--- +DOC402_google.py:9:5: DOC402 `yield` is not documented in docstring + | +7 | num (int): A number +8 | """ +9 | yield 'test' + | ^^^^^^^^^^^^ DOC402 + | + = help: Add a "Yields" section to the docstring + +DOC402_google.py:50:9: DOC402 `yield` is not documented in docstring + | +48 | num (int): A number +49 | """ +50 | yield 'test' + | ^^^^^^^^^^^^ DOC402 + | + = help: Add a "Yields" section to the docstring + +DOC402_google.py:59:9: DOC402 `yield` is not documented in docstring + | +57 | def nested(): +58 | """Do something nested.""" +59 | yield 5 + | ^^^^^^^ DOC402 +60 | +61 | print("I never yield") + | + = help: Add a "Yields" section to the docstring + +DOC402_google.py:67:5: DOC402 `yield` is not documented in docstring + | +65 | def test(): +66 | """Do something.""" +67 | yield from range(10) + | ^^^^^^^^^^^^^^^^^^^^ DOC402 + | + = help: Add a "Yields" section to the docstring diff --git a/crates/ruff_linter/src/rules/pydoclint/snapshots/ruff_linter__rules__pydoclint__tests__docstring-missing-yields_DOC402_numpy.py.snap b/crates/ruff_linter/src/rules/pydoclint/snapshots/ruff_linter__rules__pydoclint__tests__docstring-missing-yields_DOC402_numpy.py.snap new file mode 100644 index 0000000000000..4737fe16037ce --- /dev/null +++ b/crates/ruff_linter/src/rules/pydoclint/snapshots/ruff_linter__rules__pydoclint__tests__docstring-missing-yields_DOC402_numpy.py.snap @@ -0,0 +1,20 @@ +--- +source: crates/ruff_linter/src/rules/pydoclint/mod.rs +--- +DOC402_numpy.py:11:5: DOC402 `yield` is not documented in docstring + | + 9 | A number +10 | """ +11 | yield 'test' + | ^^^^^^^^^^^^ DOC402 + | + = help: Add a "Yields" section to the docstring + +DOC402_numpy.py:62:9: DOC402 `yield` is not documented in docstring + | +60 | A number +61 | """ +62 | yield 'test' + | ^^^^^^^^^^^^ DOC402 + | + = help: Add a "Yields" section to the docstring diff --git a/ruff.schema.json b/ruff.schema.json index daf012bc8f759..35f109c649708 100644 --- a/ruff.schema.json +++ b/ruff.schema.json @@ -2895,6 +2895,10 @@ "DOC20", "DOC201", "DOC202", + "DOC4", + "DOC40", + "DOC402", + "DOC403", "DOC5", "DOC50", "DOC501",