mirror of https://github.com/astral-sh/ruff
Merge dfd3c9a78d into b0bc990cbf
This commit is contained in:
commit
78da98fd22
|
|
@ -81,6 +81,7 @@ pub(crate) fn definitions(checker: &mut Checker) {
|
|||
Rule::UndocumentedPublicPackage,
|
||||
]);
|
||||
let enforce_pydoclint = checker.any_rule_enabled(&[
|
||||
Rule::UndocumentedParam,
|
||||
Rule::DocstringExtraneousParameter,
|
||||
Rule::DocstringMissingReturns,
|
||||
Rule::DocstringExtraneousReturns,
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ mod tests {
|
|||
use crate::registry::Rule;
|
||||
use crate::rules::pydocstyle;
|
||||
use crate::rules::pydocstyle::settings::Convention;
|
||||
use crate::settings::types::PreviewMode;
|
||||
use crate::test::test_path;
|
||||
use crate::{assert_diagnostics, settings};
|
||||
|
||||
|
|
@ -99,4 +100,107 @@ mod tests {
|
|||
assert_diagnostics!(snapshot, diagnostics);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test_case(Rule::UndocumentedParam, Path::new("canonical_google_examples.py"))]
|
||||
#[test_case(Rule::UndocumentedParam, Path::new("canonical_numpy_examples.py"))]
|
||||
#[test_case(Rule::UndocumentedParam, Path::new("sections.py"))]
|
||||
fn undocumented_param(rule_code: Rule, path: &Path) -> Result<()> {
|
||||
let snapshot = format!("{}_{}", rule_code.noqa_code(), path.to_string_lossy());
|
||||
let diagnostics = test_path(
|
||||
Path::new("pydocstyle").join(path).as_path(),
|
||||
&settings::LinterSettings {
|
||||
preview: PreviewMode::Enabled,
|
||||
pydocstyle: pydocstyle::settings::Settings {
|
||||
..pydocstyle::settings::Settings::default()
|
||||
},
|
||||
..settings::LinterSettings::for_rule(rule_code)
|
||||
},
|
||||
)?;
|
||||
assert_diagnostics!(snapshot, diagnostics);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn d417_unspecified() -> Result<()> {
|
||||
let diagnostics = test_path(
|
||||
Path::new("pydocstyle/D417.py"),
|
||||
&settings::LinterSettings {
|
||||
preview: PreviewMode::Enabled,
|
||||
// When inferring the convention, we'll see a few false negatives.
|
||||
// See: https://github.com/PyCQA/pydocstyle/issues/459.
|
||||
pydocstyle: pydocstyle::settings::Settings::default(),
|
||||
..settings::LinterSettings::for_rule(Rule::UndocumentedParam)
|
||||
},
|
||||
)?;
|
||||
assert_diagnostics!(diagnostics);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn d417_unspecified_ignore_var_parameters() -> Result<()> {
|
||||
let diagnostics = test_path(
|
||||
Path::new("pydocstyle/D417.py"),
|
||||
&settings::LinterSettings {
|
||||
preview: PreviewMode::Enabled,
|
||||
pydocstyle: pydocstyle::settings::Settings::default(),
|
||||
..settings::LinterSettings::for_rule(Rule::UndocumentedParam)
|
||||
},
|
||||
)?;
|
||||
assert_diagnostics!(diagnostics);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn d417_google() -> Result<()> {
|
||||
let diagnostics = test_path(
|
||||
Path::new("pydocstyle/D417.py"),
|
||||
&settings::LinterSettings {
|
||||
preview: PreviewMode::Enabled,
|
||||
// With explicit Google convention, we should flag every function.
|
||||
pydocstyle: pydocstyle::settings::Settings {
|
||||
convention: Some(Convention::Google),
|
||||
..pydocstyle::settings::Settings::default()
|
||||
},
|
||||
..settings::LinterSettings::for_rule(Rule::UndocumentedParam)
|
||||
},
|
||||
)?;
|
||||
assert_diagnostics!(diagnostics);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn d417_google_ignore_var_parameters() -> Result<()> {
|
||||
let diagnostics = test_path(
|
||||
Path::new("pydocstyle/D417.py"),
|
||||
&settings::LinterSettings {
|
||||
preview: PreviewMode::Enabled,
|
||||
pydocstyle: pydocstyle::settings::Settings {
|
||||
convention: Some(Convention::Google),
|
||||
ignore_var_parameters: true,
|
||||
..pydocstyle::settings::Settings::default()
|
||||
},
|
||||
..settings::LinterSettings::for_rule(Rule::UndocumentedParam)
|
||||
},
|
||||
)?;
|
||||
assert_diagnostics!(diagnostics);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn d417_numpy() -> Result<()> {
|
||||
let diagnostics = test_path(
|
||||
Path::new("pydocstyle/D417.py"),
|
||||
&settings::LinterSettings {
|
||||
preview: PreviewMode::Enabled,
|
||||
// With explicit numpy convention, we shouldn't flag anything.
|
||||
pydocstyle: pydocstyle::settings::Settings {
|
||||
convention: Some(Convention::Numpy),
|
||||
..pydocstyle::settings::Settings::default()
|
||||
},
|
||||
..settings::LinterSettings::for_rule(Rule::UndocumentedParam)
|
||||
},
|
||||
)?;
|
||||
assert_diagnostics!(diagnostics);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,9 +1,11 @@
|
|||
use itertools::Itertools;
|
||||
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
||||
use ruff_python_ast::helpers::{map_callable, map_subscript};
|
||||
use ruff_python_ast::identifier::Identifier;
|
||||
use ruff_python_ast::name::QualifiedName;
|
||||
use ruff_python_ast::visitor::Visitor;
|
||||
use ruff_python_ast::{self as ast, Expr, Stmt, visitor};
|
||||
use ruff_python_semantic::analyze::visibility::is_staticmethod;
|
||||
use ruff_python_semantic::analyze::{function_type, visibility};
|
||||
use ruff_python_semantic::{Definition, SemanticModel};
|
||||
use ruff_python_stdlib::identifiers::is_identifier;
|
||||
|
|
@ -17,6 +19,7 @@ use crate::docstrings::Docstring;
|
|||
use crate::docstrings::sections::{SectionContext, SectionContexts, SectionKind};
|
||||
use crate::docstrings::styles::SectionStyle;
|
||||
use crate::registry::Rule;
|
||||
use crate::rules::pydocstyle::rules::UndocumentedParam;
|
||||
use crate::rules::pydocstyle::settings::Convention;
|
||||
|
||||
/// ## What it does
|
||||
|
|
@ -464,6 +467,7 @@ impl GenericSection {
|
|||
#[derive(Debug, Clone)]
|
||||
struct ParameterEntry<'a> {
|
||||
name: &'a str,
|
||||
has_definition: bool,
|
||||
range: TextRange,
|
||||
}
|
||||
|
||||
|
|
@ -523,6 +527,16 @@ impl<'a> ParametersSection<'a> {
|
|||
range: section.section_name_range(),
|
||||
}
|
||||
}
|
||||
|
||||
fn extend_from_section(&mut self, section: &SectionContext<'a>, style: Option<SectionStyle>) {
|
||||
let mut new_entries = parse_parameters(
|
||||
section.following_lines_str(),
|
||||
section.following_range().start(),
|
||||
style,
|
||||
);
|
||||
|
||||
self.parameters.append(&mut new_entries);
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
|
|
@ -538,10 +552,22 @@ impl<'a> DocstringSections<'a> {
|
|||
let mut docstring_sections = Self::default();
|
||||
for section in sections {
|
||||
match section.kind() {
|
||||
SectionKind::Args | SectionKind::Arguments | SectionKind::Parameters => {
|
||||
SectionKind::Args
|
||||
| SectionKind::Arguments
|
||||
| SectionKind::Parameters
|
||||
| SectionKind::KeywordArgs
|
||||
| SectionKind::KeywordArguments
|
||||
| SectionKind::OtherArgs
|
||||
| SectionKind::OtherArguments
|
||||
| SectionKind::OtherParams
|
||||
| SectionKind::OtherParameters => {
|
||||
if let Some(ref mut parameters_section) = docstring_sections.parameters {
|
||||
parameters_section.extend_from_section(§ion, style);
|
||||
} else {
|
||||
docstring_sections.parameters =
|
||||
Some(ParametersSection::from_section(§ion, style));
|
||||
}
|
||||
}
|
||||
SectionKind::Raises => {
|
||||
docstring_sections.raises = Some(RaisesSection::from_section(§ion, style));
|
||||
}
|
||||
|
|
@ -569,11 +595,12 @@ fn parse_parameters(
|
|||
) -> Vec<ParameterEntry<'_>> {
|
||||
match style {
|
||||
Some(SectionStyle::Google) => parse_parameters_google(content, content_start),
|
||||
|
||||
Some(SectionStyle::Numpy) => parse_parameters_numpy(content, content_start),
|
||||
None => {
|
||||
let entries = parse_parameters_google(content, content_start);
|
||||
let entries = parse_parameters_numpy(content, content_start);
|
||||
if entries.is_empty() {
|
||||
parse_parameters_numpy(content, content_start)
|
||||
parse_parameters_google(content, content_start)
|
||||
} else {
|
||||
entries
|
||||
}
|
||||
|
|
@ -591,7 +618,11 @@ fn parse_parameters(
|
|||
fn parse_parameters_google(content: &str, content_start: TextSize) -> Vec<ParameterEntry<'_>> {
|
||||
let mut entries: Vec<ParameterEntry> = Vec::new();
|
||||
// Find first entry to determine indentation
|
||||
let Some(first_arg) = content.lines().next() else {
|
||||
let Some((_, first_arg)) = content
|
||||
.lines()
|
||||
.enumerate()
|
||||
.find(|(_, line)| !line.trim().is_empty())
|
||||
else {
|
||||
return entries;
|
||||
};
|
||||
let indentation = &first_arg[..first_arg.len() - first_arg.trim_start().len()];
|
||||
|
|
@ -607,7 +638,7 @@ fn parse_parameters_google(content: &str, content_start: TextSize) -> Vec<Parame
|
|||
.next()
|
||||
.is_some_and(|first_char| !first_char.is_whitespace())
|
||||
{
|
||||
let Some((before_colon, _)) = entry.split_once(':') else {
|
||||
let Some((before_colon, after_colon)) = entry.split_once(':') else {
|
||||
continue;
|
||||
};
|
||||
if let Some(param) = before_colon.split_whitespace().next() {
|
||||
|
|
@ -615,9 +646,11 @@ fn parse_parameters_google(content: &str, content_start: TextSize) -> Vec<Parame
|
|||
if is_identifier(param_name) {
|
||||
let param_start = line_start + indentation.text_len();
|
||||
let param_end = param_start + param.text_len();
|
||||
let has_definition = !after_colon.trim().is_empty();
|
||||
|
||||
entries.push(ParameterEntry {
|
||||
name: param_name,
|
||||
has_definition,
|
||||
range: TextRange::new(
|
||||
content_start + param_start,
|
||||
content_start + param_end,
|
||||
|
|
@ -625,6 +658,13 @@ fn parse_parameters_google(content: &str, content_start: TextSize) -> Vec<Parame
|
|||
});
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// this is a follow up of the previous entry.
|
||||
if !entry.trim().is_empty() {
|
||||
if let Some(last) = entries.last_mut() {
|
||||
last.has_definition = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -644,10 +684,14 @@ fn parse_parameters_google(content: &str, content_start: TextSize) -> Vec<Parame
|
|||
fn parse_parameters_numpy(content: &str, content_start: TextSize) -> Vec<ParameterEntry<'_>> {
|
||||
let mut entries: Vec<ParameterEntry> = Vec::new();
|
||||
let mut lines = content.lines();
|
||||
let Some(dashes) = lines.next() else {
|
||||
let Some(dashes_line) = lines.next() else {
|
||||
return entries;
|
||||
};
|
||||
let indentation = &dashes[..dashes.len() - dashes.trim_start().len()];
|
||||
let dashes = dashes_line.trim_start();
|
||||
if dashes.is_empty() || !dashes.chars().all(|c| c == '-') {
|
||||
return entries;
|
||||
}
|
||||
let indentation = &dashes_line[..dashes_line.len() - dashes.len()];
|
||||
|
||||
let mut current_pos = content.full_line_end(dashes.text_len());
|
||||
for potential in lines {
|
||||
|
|
@ -662,7 +706,6 @@ fn parse_parameters_numpy(content: &str, content_start: TextSize) -> Vec<Paramet
|
|||
{
|
||||
if let Some(before_colon) = entry.split(':').next() {
|
||||
let param_line = before_colon.trim_end();
|
||||
|
||||
// Split on commas to handle comma-separated parameters
|
||||
let mut current_offset = TextSize::from(0);
|
||||
for param_part in param_line.split(',') {
|
||||
|
|
@ -678,6 +721,7 @@ fn parse_parameters_numpy(content: &str, content_start: TextSize) -> Vec<Paramet
|
|||
|
||||
entries.push(ParameterEntry {
|
||||
name: param_name,
|
||||
has_definition: true,
|
||||
range: TextRange::at(
|
||||
content_start + param_start,
|
||||
param_part_trimmed.text_len(),
|
||||
|
|
@ -703,9 +747,9 @@ fn parse_raises(content: &str, style: Option<SectionStyle>) -> Vec<QualifiedName
|
|||
Some(SectionStyle::Google) => parse_raises_google(content),
|
||||
Some(SectionStyle::Numpy) => parse_raises_numpy(content),
|
||||
None => {
|
||||
let entries = parse_raises_google(content);
|
||||
let entries = parse_raises_numpy(content);
|
||||
if entries.is_empty() {
|
||||
parse_raises_numpy(content)
|
||||
parse_raises_google(content)
|
||||
} else {
|
||||
entries
|
||||
}
|
||||
|
|
@ -763,10 +807,14 @@ fn parse_raises_google(content: &str) -> Vec<QualifiedName<'_>> {
|
|||
fn parse_raises_numpy(content: &str) -> Vec<QualifiedName<'_>> {
|
||||
let mut entries: Vec<QualifiedName> = Vec::new();
|
||||
let mut lines = content.lines();
|
||||
let Some(dashes) = lines.next() else {
|
||||
let Some(dashes_line) = lines.next() else {
|
||||
return entries;
|
||||
};
|
||||
let indentation = &dashes[..dashes.len() - dashes.trim_start().len()];
|
||||
let dashes = dashes_line.trim_start();
|
||||
if dashes.is_empty() || !dashes.chars().all(|c| c == '-') {
|
||||
return entries;
|
||||
}
|
||||
let indentation = &dashes_line[..dashes_line.len() - dashes.len()];
|
||||
for potential in lines {
|
||||
if let Some(entry) = potential.strip_prefix(indentation) {
|
||||
// Check for Sphinx directives (lines starting with ..) - these indicate the end of the
|
||||
|
|
@ -1166,14 +1214,41 @@ fn is_generator_function_annotated_as_returning_none(
|
|||
.is_some_and(GeneratorOrIteratorArguments::indicates_none_returned)
|
||||
}
|
||||
|
||||
fn parameters_from_signature<'a>(docstring: &'a Docstring) -> Vec<&'a str> {
|
||||
#[derive(Debug)]
|
||||
struct SignatureParameter<'a> {
|
||||
name: &'a str,
|
||||
is_vararg: bool,
|
||||
is_kwarg: bool,
|
||||
}
|
||||
|
||||
fn parameters_from_signature<'a>(docstring: &'a Docstring) -> Vec<SignatureParameter<'a>> {
|
||||
let mut parameters = Vec::new();
|
||||
let Some(function) = docstring.definition.as_function_def() else {
|
||||
return parameters;
|
||||
};
|
||||
for param in &function.parameters {
|
||||
parameters.push(param.name());
|
||||
for param in function.parameters.iter_non_variadic_params() {
|
||||
parameters.push(SignatureParameter {
|
||||
name: param.name(),
|
||||
is_vararg: false,
|
||||
is_kwarg: false,
|
||||
});
|
||||
}
|
||||
|
||||
if let Some(param) = function.parameters.vararg.as_ref() {
|
||||
parameters.push(SignatureParameter {
|
||||
name: param.name(),
|
||||
is_vararg: true,
|
||||
is_kwarg: false,
|
||||
});
|
||||
}
|
||||
if let Some(param) = function.parameters.kwarg.as_ref() {
|
||||
parameters.push(SignatureParameter {
|
||||
name: param.name(),
|
||||
is_vararg: false,
|
||||
is_kwarg: true,
|
||||
});
|
||||
}
|
||||
|
||||
parameters
|
||||
}
|
||||
|
||||
|
|
@ -1209,10 +1284,6 @@ pub(crate) fn check_docstring(
|
|||
|
||||
let semantic = checker.semantic();
|
||||
|
||||
if function_type::is_stub(function_def, semantic) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Prioritize the specified convention over the determined style.
|
||||
let docstring_sections = match convention {
|
||||
Some(Convention::Google) => {
|
||||
|
|
@ -1232,6 +1303,57 @@ pub(crate) fn check_docstring(
|
|||
|
||||
let signature_parameters = parameters_from_signature(docstring);
|
||||
|
||||
// DOC101
|
||||
if checker.settings().preview.is_enabled() {
|
||||
if checker.is_rule_enabled(Rule::UndocumentedParam) {
|
||||
if let Some(parameters_section) = docstring_sections.parameters.as_ref() {
|
||||
let mut missing_parameters = Vec::new();
|
||||
|
||||
// Here we check if the function is a method (and not a staticmethod)
|
||||
// in which case we skip the first argument which should be `self` or
|
||||
// `cls`, and does not need to be documented.
|
||||
for signature_param in signature_parameters.iter().skip(usize::from(
|
||||
docstring.definition.is_method()
|
||||
&& !is_staticmethod(&function_def.decorator_list, semantic),
|
||||
)) {
|
||||
if !(checker.settings().pydocstyle.ignore_var_parameters()
|
||||
&& (signature_param.is_vararg || signature_param.is_kwarg)
|
||||
|| signature_param.name.starts_with('_')
|
||||
|| parameters_section.parameters.iter().any(|param| {
|
||||
param.name == signature_param.name && param.has_definition
|
||||
}))
|
||||
{
|
||||
let name = signature_param.name;
|
||||
if signature_param.is_vararg {
|
||||
missing_parameters.push(format!("*{name}"));
|
||||
} else if signature_param.is_kwarg {
|
||||
missing_parameters.push(format!("**{name}"));
|
||||
} else {
|
||||
missing_parameters.push(name.to_string());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !missing_parameters.is_empty() {
|
||||
if let Some(definition) = docstring.definition.name() {
|
||||
let names = missing_parameters.into_iter().sorted().collect();
|
||||
checker.report_diagnostic(
|
||||
UndocumentedParam {
|
||||
definition: definition.to_string(),
|
||||
names,
|
||||
},
|
||||
function_def.identifier(),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if function_type::is_stub(function_def, semantic) {
|
||||
return;
|
||||
}
|
||||
|
||||
// DOC201
|
||||
if checker.is_rule_enabled(Rule::DocstringMissingReturns) {
|
||||
if should_document_returns(function_def)
|
||||
|
|
@ -1326,7 +1448,10 @@ pub(crate) fn check_docstring(
|
|||
if function_def.parameters.vararg.is_none() && function_def.parameters.kwarg.is_none() {
|
||||
if let Some(docstring_params) = docstring_sections.parameters {
|
||||
for docstring_param in &docstring_params.parameters {
|
||||
if !signature_parameters.contains(&docstring_param.name) {
|
||||
if !signature_parameters
|
||||
.iter()
|
||||
.any(|param| param.name == docstring_param.name)
|
||||
{
|
||||
checker.report_diagnostic(
|
||||
DocstringExtraneousParameter {
|
||||
id: docstring_param.name.to_string(),
|
||||
|
|
|
|||
|
|
@ -0,0 +1,4 @@
|
|||
---
|
||||
source: crates/ruff_linter/src/rules/pydoclint/mod.rs
|
||||
---
|
||||
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
---
|
||||
source: crates/ruff_linter/src/rules/pydoclint/mod.rs
|
||||
---
|
||||
|
||||
|
|
@ -0,0 +1,102 @@
|
|||
---
|
||||
source: crates/ruff_linter/src/rules/pydoclint/mod.rs
|
||||
---
|
||||
D417 Missing argument description in the docstring for `bar`: `y`
|
||||
--> sections.py:292:9
|
||||
|
|
||||
290 | x = 1
|
||||
291 |
|
||||
292 | def bar(y=2): # noqa: D207, D213, D406, D407
|
||||
| ^^^
|
||||
293 | """Nested function test for docstrings.
|
||||
|
|
||||
|
||||
D417 Missing argument description in the docstring for `test_missing_google_args`: `y`
|
||||
--> sections.py:309:5
|
||||
|
|
||||
307 | "(argument(s) y are missing descriptions in "
|
||||
308 | "'test_missing_google_args' docstring)")
|
||||
309 | def test_missing_google_args(x=1, y=2, _private=3): # noqa: D406, D407
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
310 | """Toggle the gizmo.
|
||||
|
|
||||
|
||||
D417 Missing argument descriptions in the docstring for `test_missing_args`: `test`, `y`, `z`
|
||||
--> sections.py:333:9
|
||||
|
|
||||
331 | "(argument(s) test, y, z are missing descriptions in "
|
||||
332 | "'test_missing_args' docstring)", arg_count=5)
|
||||
333 | def test_missing_args(self, test, x, y, z=3, _private_arg=3): # noqa: D213, D407
|
||||
| ^^^^^^^^^^^^^^^^^
|
||||
334 | """Test a valid args section.
|
||||
|
|
||||
|
||||
D417 Missing argument descriptions in the docstring for `test_missing_args_class_method`: `test`, `y`, `z`
|
||||
--> sections.py:345:9
|
||||
|
|
||||
343 | "(argument(s) test, y, z are missing descriptions in "
|
||||
344 | "'test_missing_args_class_method' docstring)", arg_count=5)
|
||||
345 | def test_missing_args_class_method(cls, test, x, y, _, z=3): # noqa: D213, D407
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
346 | """Test a valid args section.
|
||||
|
|
||||
|
||||
D417 Missing argument descriptions in the docstring for `test_missing_args_static_method`: `a`, `y`, `z`
|
||||
--> sections.py:358:9
|
||||
|
|
||||
356 | "(argument(s) a, y, z are missing descriptions in "
|
||||
357 | "'test_missing_args_static_method' docstring)", arg_count=4)
|
||||
358 | def test_missing_args_static_method(a, x, y, _test, z=3): # noqa: D213, D407
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
359 | """Test a valid args section.
|
||||
|
|
||||
|
||||
D417 Missing argument descriptions in the docstring for `test_missing_docstring`: `a`, `b`
|
||||
--> sections.py:370:9
|
||||
|
|
||||
368 | "(argument(s) a, b are missing descriptions in "
|
||||
369 | "'test_missing_docstring' docstring)", arg_count=2)
|
||||
370 | def test_missing_docstring(a, b): # noqa: D213, D407
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^
|
||||
371 | """Test a valid args section.
|
||||
|
|
||||
|
||||
D417 Missing argument description in the docstring for `test_missing_numpy_args`: `y`
|
||||
--> sections.py:398:5
|
||||
|
|
||||
396 | "(argument(s) y are missing descriptions in "
|
||||
397 | "'test_missing_numpy_args' docstring)")
|
||||
398 | def test_missing_numpy_args(_private_arg=0, x=1, y=2): # noqa: D406, D407
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^
|
||||
399 | """Toggle the gizmo.
|
||||
|
|
||||
|
||||
D417 Missing argument descriptions in the docstring for `test_missing_args`: `test`, `y`, `z`
|
||||
--> sections.py:434:9
|
||||
|
|
||||
432 | "(argument(s) test, y, z are missing descriptions in "
|
||||
433 | "'test_missing_args' docstring)", arg_count=5)
|
||||
434 | def test_missing_args(self, test, x, y, z=3, t=1, _private=0): # noqa: D213, D407
|
||||
| ^^^^^^^^^^^^^^^^^
|
||||
435 | """Test a valid args section.
|
||||
|
|
||||
|
||||
D417 Missing argument descriptions in the docstring for `test_missing_args_static_method`: `a`, `z`
|
||||
--> sections.py:468:9
|
||||
|
|
||||
466 | "(argument(s) a, z are missing descriptions in "
|
||||
467 | "'test_missing_args_static_method' docstring)", arg_count=3)
|
||||
468 | def test_missing_args_static_method(a, x, y, z=3, t=1): # noqa: D213, D407
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
469 | """Test a valid args section.
|
||||
|
|
||||
|
||||
D417 Missing argument description in the docstring for `test_incorrect_indent`: `y`
|
||||
--> sections.py:498:9
|
||||
|
|
||||
496 | "(argument(s) y are missing descriptions in "
|
||||
497 | "'test_incorrect_indent' docstring)", arg_count=3)
|
||||
498 | def test_incorrect_indent(self, x=1, y=2): # noqa: D207, D213, D407
|
||||
| ^^^^^^^^^^^^^^^^^^^^^
|
||||
499 | """Reproducing issue #437.
|
||||
|
|
||||
|
|
@ -0,0 +1,113 @@
|
|||
---
|
||||
source: crates/ruff_linter/src/rules/pydoclint/mod.rs
|
||||
---
|
||||
D417 Missing argument descriptions in the docstring for `f`: `y`, `z`
|
||||
--> D417.py:1:5
|
||||
|
|
||||
1 | def f(x, y, z):
|
||||
| ^
|
||||
2 | """Do something.
|
||||
|
|
||||
|
||||
D417 Missing argument descriptions in the docstring for `f`: `y`, `z`
|
||||
--> D417.py:14:5
|
||||
|
|
||||
14 | def f(x, y, z):
|
||||
| ^
|
||||
15 | """Do something.
|
||||
|
|
||||
|
||||
D417 Missing argument descriptions in the docstring for `f`: `y`, `z`
|
||||
--> D417.py:27:5
|
||||
|
|
||||
27 | def f(x, y, z):
|
||||
| ^
|
||||
28 | """Do something.
|
||||
|
|
||||
|
||||
D417 Missing argument descriptions in the docstring for `f`: `y`, `z`
|
||||
--> D417.py:39:5
|
||||
|
|
||||
39 | def f(x, y, z):
|
||||
| ^
|
||||
40 | """Do something.
|
||||
|
|
||||
|
||||
D417 Missing argument description in the docstring for `f`: `y`
|
||||
--> D417.py:52:5
|
||||
|
|
||||
52 | def f(x, y, z):
|
||||
| ^
|
||||
53 | """Do something.
|
||||
|
|
||||
|
||||
D417 Missing argument description in the docstring for `f`: `y`
|
||||
--> D417.py:65:5
|
||||
|
|
||||
65 | def f(x, y, z):
|
||||
| ^
|
||||
66 | """Do something.
|
||||
|
|
||||
|
||||
D417 Missing argument description in the docstring for `f`: `y`
|
||||
--> D417.py:77:5
|
||||
|
|
||||
77 | def f(x, y, z):
|
||||
| ^
|
||||
78 | """Do something.
|
||||
|
|
||||
|
||||
D417 Missing argument description in the docstring for `f`: `x`
|
||||
--> D417.py:98:5
|
||||
|
|
||||
98 | def f(x, *args, **kwargs):
|
||||
| ^
|
||||
99 | """Do something.
|
||||
|
|
||||
|
||||
D417 Missing argument description in the docstring for `f`: `*args`
|
||||
--> D417.py:108:5
|
||||
|
|
||||
108 | def f(x, *args, **kwargs):
|
||||
| ^
|
||||
109 | """Do something.
|
||||
|
|
||||
|
||||
D417 Missing argument description in the docstring for `f`: `x`
|
||||
--> D417.py:131:5
|
||||
|
|
||||
129 | return x, y, z
|
||||
130 |
|
||||
131 | def f(x):
|
||||
| ^
|
||||
132 | """Do something with valid description.
|
||||
|
|
||||
|
||||
D417 Missing argument description in the docstring for `select_data`: `auto_save`
|
||||
--> D417.py:155:5
|
||||
|
|
||||
155 | def select_data(
|
||||
| ^^^^^^^^^^^
|
||||
156 | query: str,
|
||||
157 | args: tuple,
|
||||
|
|
||||
|
||||
D417 Missing argument description in the docstring for `f`: `**kwargs`
|
||||
--> D417.py:172:5
|
||||
|
|
||||
170 | """
|
||||
171 |
|
||||
172 | def f(x, *args, **kwargs):
|
||||
| ^
|
||||
173 | """Do something.
|
||||
|
|
||||
|
||||
D417 Missing argument description in the docstring for `should_fail`: `Args`
|
||||
--> D417.py:199:5
|
||||
|
|
||||
198 | # undocumented argument with the same name as a section
|
||||
199 | def should_fail(payload, Args):
|
||||
| ^^^^^^^^^^^
|
||||
200 | """
|
||||
201 | Send a message.
|
||||
|
|
||||
|
|
@ -0,0 +1,95 @@
|
|||
---
|
||||
source: crates/ruff_linter/src/rules/pydoclint/mod.rs
|
||||
---
|
||||
D417 Missing argument descriptions in the docstring for `f`: `y`, `z`
|
||||
--> D417.py:1:5
|
||||
|
|
||||
1 | def f(x, y, z):
|
||||
| ^
|
||||
2 | """Do something.
|
||||
|
|
||||
|
||||
D417 Missing argument descriptions in the docstring for `f`: `y`, `z`
|
||||
--> D417.py:14:5
|
||||
|
|
||||
14 | def f(x, y, z):
|
||||
| ^
|
||||
15 | """Do something.
|
||||
|
|
||||
|
||||
D417 Missing argument descriptions in the docstring for `f`: `y`, `z`
|
||||
--> D417.py:27:5
|
||||
|
|
||||
27 | def f(x, y, z):
|
||||
| ^
|
||||
28 | """Do something.
|
||||
|
|
||||
|
||||
D417 Missing argument descriptions in the docstring for `f`: `y`, `z`
|
||||
--> D417.py:39:5
|
||||
|
|
||||
39 | def f(x, y, z):
|
||||
| ^
|
||||
40 | """Do something.
|
||||
|
|
||||
|
||||
D417 Missing argument description in the docstring for `f`: `y`
|
||||
--> D417.py:52:5
|
||||
|
|
||||
52 | def f(x, y, z):
|
||||
| ^
|
||||
53 | """Do something.
|
||||
|
|
||||
|
||||
D417 Missing argument description in the docstring for `f`: `y`
|
||||
--> D417.py:65:5
|
||||
|
|
||||
65 | def f(x, y, z):
|
||||
| ^
|
||||
66 | """Do something.
|
||||
|
|
||||
|
||||
D417 Missing argument description in the docstring for `f`: `y`
|
||||
--> D417.py:77:5
|
||||
|
|
||||
77 | def f(x, y, z):
|
||||
| ^
|
||||
78 | """Do something.
|
||||
|
|
||||
|
||||
D417 Missing argument description in the docstring for `f`: `x`
|
||||
--> D417.py:98:5
|
||||
|
|
||||
98 | def f(x, *args, **kwargs):
|
||||
| ^
|
||||
99 | """Do something.
|
||||
|
|
||||
|
||||
D417 Missing argument description in the docstring for `f`: `x`
|
||||
--> D417.py:131:5
|
||||
|
|
||||
129 | return x, y, z
|
||||
130 |
|
||||
131 | def f(x):
|
||||
| ^
|
||||
132 | """Do something with valid description.
|
||||
|
|
||||
|
||||
D417 Missing argument description in the docstring for `select_data`: `auto_save`
|
||||
--> D417.py:155:5
|
||||
|
|
||||
155 | def select_data(
|
||||
| ^^^^^^^^^^^
|
||||
156 | query: str,
|
||||
157 | args: tuple,
|
||||
|
|
||||
|
||||
D417 Missing argument description in the docstring for `should_fail`: `Args`
|
||||
--> D417.py:199:5
|
||||
|
|
||||
198 | # undocumented argument with the same name as a section
|
||||
199 | def should_fail(payload, Args):
|
||||
| ^^^^^^^^^^^
|
||||
200 | """
|
||||
201 | Send a message.
|
||||
|
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
---
|
||||
source: crates/ruff_linter/src/rules/pydoclint/mod.rs
|
||||
---
|
||||
|
||||
|
|
@ -0,0 +1,113 @@
|
|||
---
|
||||
source: crates/ruff_linter/src/rules/pydoclint/mod.rs
|
||||
---
|
||||
D417 Missing argument descriptions in the docstring for `f`: `y`, `z`
|
||||
--> D417.py:1:5
|
||||
|
|
||||
1 | def f(x, y, z):
|
||||
| ^
|
||||
2 | """Do something.
|
||||
|
|
||||
|
||||
D417 Missing argument descriptions in the docstring for `f`: `y`, `z`
|
||||
--> D417.py:14:5
|
||||
|
|
||||
14 | def f(x, y, z):
|
||||
| ^
|
||||
15 | """Do something.
|
||||
|
|
||||
|
||||
D417 Missing argument descriptions in the docstring for `f`: `y`, `z`
|
||||
--> D417.py:27:5
|
||||
|
|
||||
27 | def f(x, y, z):
|
||||
| ^
|
||||
28 | """Do something.
|
||||
|
|
||||
|
||||
D417 Missing argument descriptions in the docstring for `f`: `y`, `z`
|
||||
--> D417.py:39:5
|
||||
|
|
||||
39 | def f(x, y, z):
|
||||
| ^
|
||||
40 | """Do something.
|
||||
|
|
||||
|
||||
D417 Missing argument description in the docstring for `f`: `y`
|
||||
--> D417.py:52:5
|
||||
|
|
||||
52 | def f(x, y, z):
|
||||
| ^
|
||||
53 | """Do something.
|
||||
|
|
||||
|
||||
D417 Missing argument description in the docstring for `f`: `y`
|
||||
--> D417.py:65:5
|
||||
|
|
||||
65 | def f(x, y, z):
|
||||
| ^
|
||||
66 | """Do something.
|
||||
|
|
||||
|
||||
D417 Missing argument description in the docstring for `f`: `y`
|
||||
--> D417.py:77:5
|
||||
|
|
||||
77 | def f(x, y, z):
|
||||
| ^
|
||||
78 | """Do something.
|
||||
|
|
||||
|
||||
D417 Missing argument description in the docstring for `f`: `x`
|
||||
--> D417.py:98:5
|
||||
|
|
||||
98 | def f(x, *args, **kwargs):
|
||||
| ^
|
||||
99 | """Do something.
|
||||
|
|
||||
|
||||
D417 Missing argument description in the docstring for `f`: `*args`
|
||||
--> D417.py:108:5
|
||||
|
|
||||
108 | def f(x, *args, **kwargs):
|
||||
| ^
|
||||
109 | """Do something.
|
||||
|
|
||||
|
||||
D417 Missing argument description in the docstring for `f`: `x`
|
||||
--> D417.py:131:5
|
||||
|
|
||||
129 | return x, y, z
|
||||
130 |
|
||||
131 | def f(x):
|
||||
| ^
|
||||
132 | """Do something with valid description.
|
||||
|
|
||||
|
||||
D417 Missing argument description in the docstring for `select_data`: `auto_save`
|
||||
--> D417.py:155:5
|
||||
|
|
||||
155 | def select_data(
|
||||
| ^^^^^^^^^^^
|
||||
156 | query: str,
|
||||
157 | args: tuple,
|
||||
|
|
||||
|
||||
D417 Missing argument description in the docstring for `f`: `**kwargs`
|
||||
--> D417.py:172:5
|
||||
|
|
||||
170 | """
|
||||
171 |
|
||||
172 | def f(x, *args, **kwargs):
|
||||
| ^
|
||||
173 | """Do something.
|
||||
|
|
||||
|
||||
D417 Missing argument description in the docstring for `should_fail`: `Args`
|
||||
--> D417.py:199:5
|
||||
|
|
||||
198 | # undocumented argument with the same name as a section
|
||||
199 | def should_fail(payload, Args):
|
||||
| ^^^^^^^^^^^
|
||||
200 | """
|
||||
201 | Send a message.
|
||||
|
|
||||
|
|
@ -0,0 +1,113 @@
|
|||
---
|
||||
source: crates/ruff_linter/src/rules/pydoclint/mod.rs
|
||||
---
|
||||
D417 Missing argument descriptions in the docstring for `f`: `y`, `z`
|
||||
--> D417.py:1:5
|
||||
|
|
||||
1 | def f(x, y, z):
|
||||
| ^
|
||||
2 | """Do something.
|
||||
|
|
||||
|
||||
D417 Missing argument descriptions in the docstring for `f`: `y`, `z`
|
||||
--> D417.py:14:5
|
||||
|
|
||||
14 | def f(x, y, z):
|
||||
| ^
|
||||
15 | """Do something.
|
||||
|
|
||||
|
||||
D417 Missing argument descriptions in the docstring for `f`: `y`, `z`
|
||||
--> D417.py:27:5
|
||||
|
|
||||
27 | def f(x, y, z):
|
||||
| ^
|
||||
28 | """Do something.
|
||||
|
|
||||
|
||||
D417 Missing argument descriptions in the docstring for `f`: `y`, `z`
|
||||
--> D417.py:39:5
|
||||
|
|
||||
39 | def f(x, y, z):
|
||||
| ^
|
||||
40 | """Do something.
|
||||
|
|
||||
|
||||
D417 Missing argument description in the docstring for `f`: `y`
|
||||
--> D417.py:52:5
|
||||
|
|
||||
52 | def f(x, y, z):
|
||||
| ^
|
||||
53 | """Do something.
|
||||
|
|
||||
|
||||
D417 Missing argument description in the docstring for `f`: `y`
|
||||
--> D417.py:65:5
|
||||
|
|
||||
65 | def f(x, y, z):
|
||||
| ^
|
||||
66 | """Do something.
|
||||
|
|
||||
|
||||
D417 Missing argument description in the docstring for `f`: `y`
|
||||
--> D417.py:77:5
|
||||
|
|
||||
77 | def f(x, y, z):
|
||||
| ^
|
||||
78 | """Do something.
|
||||
|
|
||||
|
||||
D417 Missing argument description in the docstring for `f`: `x`
|
||||
--> D417.py:98:5
|
||||
|
|
||||
98 | def f(x, *args, **kwargs):
|
||||
| ^
|
||||
99 | """Do something.
|
||||
|
|
||||
|
||||
D417 Missing argument description in the docstring for `f`: `*args`
|
||||
--> D417.py:108:5
|
||||
|
|
||||
108 | def f(x, *args, **kwargs):
|
||||
| ^
|
||||
109 | """Do something.
|
||||
|
|
||||
|
||||
D417 Missing argument description in the docstring for `f`: `x`
|
||||
--> D417.py:131:5
|
||||
|
|
||||
129 | return x, y, z
|
||||
130 |
|
||||
131 | def f(x):
|
||||
| ^
|
||||
132 | """Do something with valid description.
|
||||
|
|
||||
|
||||
D417 Missing argument description in the docstring for `select_data`: `auto_save`
|
||||
--> D417.py:155:5
|
||||
|
|
||||
155 | def select_data(
|
||||
| ^^^^^^^^^^^
|
||||
156 | query: str,
|
||||
157 | args: tuple,
|
||||
|
|
||||
|
||||
D417 Missing argument description in the docstring for `f`: `**kwargs`
|
||||
--> D417.py:172:5
|
||||
|
|
||||
170 | """
|
||||
171 |
|
||||
172 | def f(x, *args, **kwargs):
|
||||
| ^
|
||||
173 | """Do something.
|
||||
|
|
||||
|
||||
D417 Missing argument description in the docstring for `should_fail`: `Args`
|
||||
--> D417.py:199:5
|
||||
|
|
||||
198 | # undocumented argument with the same name as a section
|
||||
199 | def should_fail(payload, Args):
|
||||
| ^^^^^^^^^^^
|
||||
200 | """
|
||||
201 | Send a message.
|
||||
|
|
||||
|
|
@ -1243,9 +1243,9 @@ impl AlwaysFixableViolation for MissingSectionNameColon {
|
|||
#[violation_metadata(stable_since = "v0.0.73")]
|
||||
pub(crate) struct UndocumentedParam {
|
||||
/// The name of the function being documented.
|
||||
definition: String,
|
||||
pub(crate) definition: String,
|
||||
/// The names of the undocumented parameters.
|
||||
names: Vec<String>,
|
||||
pub(crate) names: Vec<String>,
|
||||
}
|
||||
|
||||
impl Violation for UndocumentedParam {
|
||||
|
|
@ -1828,6 +1828,7 @@ fn missing_args(checker: &Checker, docstring: &Docstring, docstrings_args: &FxHa
|
|||
}
|
||||
}
|
||||
|
||||
if checker.settings().preview.is_disabled() {
|
||||
if !missing_arg_names.is_empty() {
|
||||
if let Some(definition) = docstring.definition.name() {
|
||||
let names = missing_arg_names.into_iter().sorted().collect();
|
||||
|
|
@ -1841,6 +1842,7 @@ fn missing_args(checker: &Checker, docstring: &Docstring, docstrings_args: &FxHa
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns `true` if the parameter is annotated with `typing.Unpack`
|
||||
fn has_unpack_annotation(checker: &Checker, parameter: &Parameter) -> bool {
|
||||
|
|
|
|||
Loading…
Reference in New Issue