diff --git a/crates/ruff_linter/resources/test/fixtures/pydocstyle/D417.py b/crates/ruff_linter/resources/test/fixtures/pydocstyle/D417.py index 510bf77bf8588f..abbfaeb4def3d9 100644 --- a/crates/ruff_linter/resources/test/fixtures/pydocstyle/D417.py +++ b/crates/ruff_linter/resources/test/fixtures/pydocstyle/D417.py @@ -241,3 +241,57 @@ def function_with_unpack_and_missing_arg_doc_should_fail(query: str, **kwargs: U Args: **kwargs: keyword arguments """ + + +# regression test for https://github.com/astral-sh/ruff/issues/29535. +# reStructuredText requires `*` to be escaped with a backslash. +def escaped_stars_numpy(x, *args, **kwargs): + r"""Do something. + + Parameters + ---------- + x : int + The value. + \*args : tuple + Variable arguments. + \*\*kwargs : dict + Keyword arguments. + """ + + +def escaped_stars_numpy_missing(x, *args, **kwargs): + r"""Do something. + + Parameters + ---------- + \*\*kwargs : dict + Keyword arguments. + """ + + +def escaped_stars_google(x, *args, **kwargs): + r"""Do something. + + Args: + x: the value + \*args: variable arguments + \*\*kwargs: keyword arguments + """ + + +def escaped_stars_google_missing(x, *args, **kwargs): + r"""Do something. + + Args: + \*\*kwargs: keyword arguments + """ + + +def escaped_stars_non_raw(x, *args, **kwargs): + """Do something. + + Args: + x: the value + \\*args: variable arguments + \\*\\*kwargs: keyword arguments + """ diff --git a/crates/ruff_linter/src/rules/pydocstyle/mod.rs b/crates/ruff_linter/src/rules/pydocstyle/mod.rs index ff22b1f05928b2..f028a786396732 100644 --- a/crates/ruff_linter/src/rules/pydocstyle/mod.rs +++ b/crates/ruff_linter/src/rules/pydocstyle/mod.rs @@ -198,7 +198,7 @@ mod tests { let diagnostics = test_path( Path::new("pydocstyle/D417.py"), &settings::LinterSettings { - // With explicit numpy convention, we shouldn't flag anything. + // With explicit numpy convention, we should only flag the NumPy-style docstrings. pydocstyle: Settings { convention: Some(Convention::Numpy), ..Settings::default() diff --git a/crates/ruff_linter/src/rules/pydocstyle/rules/sections.rs b/crates/ruff_linter/src/rules/pydocstyle/rules/sections.rs index b31d35b15856e8..e5d54805218d72 100644 --- a/crates/ruff_linter/src/rules/pydocstyle/rules/sections.rs +++ b/crates/ruff_linter/src/rules/pydocstyle/rules/sections.rs @@ -1967,8 +1967,15 @@ fn has_unpack_annotation(checker: &Checker, parameter: &Parameter) -> bool { } // See: `GOOGLE_ARGS_REGEX` in `pydocstyle/checker.py`. -static GOOGLE_ARGS_REGEX: LazyLock = - LazyLock::new(|| Regex::new(r"^\s*(\*?\*?\w+)\s*(\(.*?\))?\s*:(\r\n|\n)?\s*.+").unwrap()); +static GOOGLE_ARGS_REGEX: LazyLock = LazyLock::new(|| { + Regex::new(r"^\s*((?:\\*\*){0,2}\w+)\s*(\(.*?\))?\s*:(\r\n|\n)?\s*.+").unwrap() +}); + +/// Removes the backslashes that escape `*` in reStructuredText, so that `\*\*kwargs` (or +/// `\\*\\*kwargs` in a non-raw docstring) is treated as `**kwargs`. +fn unescape_parameter_name(name: &str) -> String { + name.replace('\\', "") +} fn args_section(context: &SectionContext) -> FxHashSet { let mut following_lines = context.following_lines(); @@ -2018,7 +2025,11 @@ fn args_section(context: &SectionContext) -> FxHashSet { matches .iter() - .filter_map(|captures| captures.get(1).map(|arg_name| arg_name.as_str().to_owned())) + .filter_map(|captures| { + captures + .get(1) + .map(|arg_name| unescape_parameter_name(arg_name.as_str())) + }) .collect::>() } @@ -2051,7 +2062,7 @@ fn parameters_section(checker: &Checker, docstring: &Docstring, context: &Sectio // Notably, NumPy lets you put multiple parameters of the same type on the same // line. for parameter in parameters.split(',') { - docstring_args.insert(parameter.trim().to_owned()); + docstring_args.insert(unescape_parameter_name(parameter.trim())); } } diff --git a/crates/ruff_linter/src/rules/pydocstyle/snapshots/ruff_linter__rules__pydocstyle__tests__d417_google.snap b/crates/ruff_linter/src/rules/pydocstyle/snapshots/ruff_linter__rules__pydocstyle__tests__d417_google.snap index 44b20a8c6b0b1a..a6770bebbe709e 100644 --- a/crates/ruff_linter/src/rules/pydocstyle/snapshots/ruff_linter__rules__pydocstyle__tests__d417_google.snap +++ b/crates/ruff_linter/src/rules/pydocstyle/snapshots/ruff_linter__rules__pydocstyle__tests__d417_google.snap @@ -111,3 +111,11 @@ D417 Missing argument description in the docstring for `function_with_unpack_and | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 239 | """Function with Unpack kwargs but missing query arg documentation. | + +D417 Missing argument descriptions in the docstring for `escaped_stars_google_missing`: `*args`, `x` + --> D417.py:282:5 + | +282 | def escaped_stars_google_missing(x, *args, **kwargs): + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +283 | r"""Do something. + | diff --git a/crates/ruff_linter/src/rules/pydocstyle/snapshots/ruff_linter__rules__pydocstyle__tests__d417_google_ignore_var_parameters.snap b/crates/ruff_linter/src/rules/pydocstyle/snapshots/ruff_linter__rules__pydocstyle__tests__d417_google_ignore_var_parameters.snap index 7ff0f72bf03ba1..13abd495720793 100644 --- a/crates/ruff_linter/src/rules/pydocstyle/snapshots/ruff_linter__rules__pydocstyle__tests__d417_google_ignore_var_parameters.snap +++ b/crates/ruff_linter/src/rules/pydocstyle/snapshots/ruff_linter__rules__pydocstyle__tests__d417_google_ignore_var_parameters.snap @@ -93,3 +93,11 @@ D417 Missing argument description in the docstring for `function_with_unpack_and | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 239 | """Function with Unpack kwargs but missing query arg documentation. | + +D417 Missing argument description in the docstring for `escaped_stars_google_missing`: `x` + --> D417.py:282:5 + | +282 | def escaped_stars_google_missing(x, *args, **kwargs): + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +283 | r"""Do something. + | diff --git a/crates/ruff_linter/src/rules/pydocstyle/snapshots/ruff_linter__rules__pydocstyle__tests__d417_numpy.snap b/crates/ruff_linter/src/rules/pydocstyle/snapshots/ruff_linter__rules__pydocstyle__tests__d417_numpy.snap index 724d6e7d20ae35..9fb828b7301df9 100644 --- a/crates/ruff_linter/src/rules/pydocstyle/snapshots/ruff_linter__rules__pydocstyle__tests__d417_numpy.snap +++ b/crates/ruff_linter/src/rules/pydocstyle/snapshots/ruff_linter__rules__pydocstyle__tests__d417_numpy.snap @@ -1,4 +1,10 @@ --- source: crates/ruff_linter/src/rules/pydocstyle/mod.rs --- - +D417 Missing argument descriptions in the docstring for `escaped_stars_numpy_missing`: `*args`, `x` + --> D417.py:262:5 + | +262 | def escaped_stars_numpy_missing(x, *args, **kwargs): + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ +263 | r"""Do something. + | diff --git a/crates/ruff_linter/src/rules/pydocstyle/snapshots/ruff_linter__rules__pydocstyle__tests__d417_unspecified.snap b/crates/ruff_linter/src/rules/pydocstyle/snapshots/ruff_linter__rules__pydocstyle__tests__d417_unspecified.snap index 44b20a8c6b0b1a..9f945d6d88a4e4 100644 --- a/crates/ruff_linter/src/rules/pydocstyle/snapshots/ruff_linter__rules__pydocstyle__tests__d417_unspecified.snap +++ b/crates/ruff_linter/src/rules/pydocstyle/snapshots/ruff_linter__rules__pydocstyle__tests__d417_unspecified.snap @@ -111,3 +111,19 @@ D417 Missing argument description in the docstring for `function_with_unpack_and | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 239 | """Function with Unpack kwargs but missing query arg documentation. | + +D417 Missing argument descriptions in the docstring for `escaped_stars_numpy_missing`: `*args`, `x` + --> D417.py:262:5 + | +262 | def escaped_stars_numpy_missing(x, *args, **kwargs): + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ +263 | r"""Do something. + | + +D417 Missing argument descriptions in the docstring for `escaped_stars_google_missing`: `*args`, `x` + --> D417.py:282:5 + | +282 | def escaped_stars_google_missing(x, *args, **kwargs): + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +283 | r"""Do something. + | diff --git a/crates/ruff_linter/src/rules/pydocstyle/snapshots/ruff_linter__rules__pydocstyle__tests__d417_unspecified_ignore_var_parameters.snap b/crates/ruff_linter/src/rules/pydocstyle/snapshots/ruff_linter__rules__pydocstyle__tests__d417_unspecified_ignore_var_parameters.snap index 44b20a8c6b0b1a..9f945d6d88a4e4 100644 --- a/crates/ruff_linter/src/rules/pydocstyle/snapshots/ruff_linter__rules__pydocstyle__tests__d417_unspecified_ignore_var_parameters.snap +++ b/crates/ruff_linter/src/rules/pydocstyle/snapshots/ruff_linter__rules__pydocstyle__tests__d417_unspecified_ignore_var_parameters.snap @@ -111,3 +111,19 @@ D417 Missing argument description in the docstring for `function_with_unpack_and | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 239 | """Function with Unpack kwargs but missing query arg documentation. | + +D417 Missing argument descriptions in the docstring for `escaped_stars_numpy_missing`: `*args`, `x` + --> D417.py:262:5 + | +262 | def escaped_stars_numpy_missing(x, *args, **kwargs): + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ +263 | r"""Do something. + | + +D417 Missing argument descriptions in the docstring for `escaped_stars_google_missing`: `*args`, `x` + --> D417.py:282:5 + | +282 | def escaped_stars_google_missing(x, *args, **kwargs): + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +283 | r"""Do something. + |