Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Disambiguate argument descriptors from section headers #9427

Merged
merged 1 commit into from
Jan 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions crates/ruff_linter/resources/test/fixtures/pydocstyle/D417.py
Original file line number Diff line number Diff line change
Expand Up @@ -150,3 +150,21 @@ def f(self, /, arg1: int) -> None:
Args:
arg1: some description of arg
"""


def select_data(
query: str,
args: tuple,
database: str,
auto_save: bool,
) -> None:
"""This function has an argument `args`, which shouldn't be mistaken for a section.

Args:
query:
Query template.
args:
A list of arguments.
database:
Which database to connect to ("origin" or "destination").
"""
Original file line number Diff line number Diff line change
Expand Up @@ -536,9 +536,29 @@ def non_empty_blank_line_before_section(): # noqa: D416
"""Toggle the gizmo.

The function's description.

Returns
-------
A value of some sort.

"""


def lowercase_sub_section_header():
"""Below, `returns:` should _not_ be considered a section header.

Args:
Here's a note.

returns:
"""


def titlecase_sub_section_header():
"""Below, `Returns:` should be considered a section header.

Args:
Here's a note.

Returns:
"""
45 changes: 38 additions & 7 deletions crates/ruff_linter/src/docstrings/sections.rs
Original file line number Diff line number Diff line change
Expand Up @@ -153,13 +153,17 @@ impl<'a> SectionContexts<'a> {
while let Some(line) = lines.next() {
if let Some(section_kind) = suspected_as_section(&line, style) {
let indent = leading_space(&line);
let section_name = leading_words(&line);
let indent_size = indent.text_len();

let section_name_range = TextRange::at(indent.text_len(), section_name.text_len());
let section_name = leading_words(&line);
let section_name_size = section_name.text_len();

if is_docstring_section(
&line,
section_name_range,
indent_size,
section_name_size,
section_kind,
last.as_ref(),
previous_line.as_ref(),
lines.peek(),
) {
Expand All @@ -170,7 +174,8 @@ impl<'a> SectionContexts<'a> {

last = Some(SectionContextData {
kind: section_kind,
name_range: section_name_range + line.start(),
indent_size: indent.text_len(),
name_range: TextRange::at(line.start() + indent_size, section_name_size),
range: TextRange::empty(line.start()),
summary_full_end: line.full_end(),
});
Expand Down Expand Up @@ -204,8 +209,8 @@ impl<'a> SectionContexts<'a> {
}

impl<'a> IntoIterator for &'a SectionContexts<'a> {
type IntoIter = SectionContextsIter<'a>;
type Item = SectionContext<'a>;
type IntoIter = SectionContextsIter<'a>;

fn into_iter(self) -> Self::IntoIter {
self.iter()
Expand Down Expand Up @@ -257,6 +262,9 @@ impl ExactSizeIterator for SectionContextsIter<'_> {}
struct SectionContextData {
kind: SectionKind,

/// The size of the indentation of the section name.
indent_size: TextSize,

/// Range of the section name, relative to the [`Docstring::body`]
name_range: TextRange,

Expand Down Expand Up @@ -401,12 +409,15 @@ fn suspected_as_section(line: &str, style: SectionStyle) -> Option<SectionKind>
/// Check if the suspected context is really a section header.
fn is_docstring_section(
line: &Line,
section_name_range: TextRange,
indent_size: TextSize,
section_name_size: TextSize,
section_kind: SectionKind,
previous_section: Option<&SectionContextData>,
previous_line: Option<&Line>,
next_line: Option<&Line>,
) -> bool {
// Determine whether the current line looks like a section header, e.g., "Args:".
let section_name_suffix = line[usize::from(section_name_range.end())..].trim();
let section_name_suffix = line[usize::from(indent_size + section_name_size)..].trim();
let this_looks_like_a_section_name =
section_name_suffix == ":" || section_name_suffix.is_empty();
if !this_looks_like_a_section_name {
Expand Down Expand Up @@ -439,5 +450,25 @@ fn is_docstring_section(
return false;
}

// Determine if this is a sub-section within another section, like `args` in:
// ```python
// def func(args: tuple[int]):
// """Toggle the gizmo.
//
// Args:
// args: The arguments to the function.
// """
// ```
// However, if the header is an _exact_ match (like `Returns:`, as opposed to `returns:`), then
// continue to treat it as a section header.
if let Some(previous_section) = previous_section {
if previous_section.indent_size < indent_size {
let verbatim = &line[TextRange::at(indent_size, section_name_size)];
if section_kind.as_str() != verbatim {
return false;
}
}
}

true
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,4 +27,27 @@ sections.py:144:5: D214 [*] Section is over-indented ("Returns")
148 148 | A value of some sort.
149 149 |

sections.py:558:5: D214 [*] Section is over-indented ("Returns")
|
557 | def titlecase_sub_section_header():
558 | """Below, `Returns:` should be considered a section header.
| _____^
559 | |
560 | | Args:
561 | | Here's a note.
562 | |
563 | | Returns:
564 | | """
| |_______^ D214
|
= help: Remove over-indentation from "Returns"

ℹ Safe fix
560 560 | Args:
561 561 | Here's a note.
562 562 |
563 |- Returns:
563 |+ Returns:
564 564 | """


Original file line number Diff line number Diff line change
Expand Up @@ -498,4 +498,74 @@ sections.py:527:5: D407 [*] Missing dashed underline after section ("Parameters"
531 532 | """
532 533 |

sections.py:548:5: D407 [*] Missing dashed underline after section ("Args")
|
547 | def lowercase_sub_section_header():
548 | """Below, `returns:` should _not_ be considered a section header.
| _____^
549 | |
550 | | Args:
551 | | Here's a note.
552 | |
553 | | returns:
554 | | """
| |_______^ D407
|
= help: Add dashed line under "Args"

ℹ Safe fix
548 548 | """Below, `returns:` should _not_ be considered a section header.
549 549 |
550 550 | Args:
551 |+ ----
551 552 | Here's a note.
552 553 |
553 554 | returns:

sections.py:558:5: D407 [*] Missing dashed underline after section ("Args")
|
557 | def titlecase_sub_section_header():
558 | """Below, `Returns:` should be considered a section header.
| _____^
559 | |
560 | | Args:
561 | | Here's a note.
562 | |
563 | | Returns:
564 | | """
| |_______^ D407
|
= help: Add dashed line under "Args"

ℹ Safe fix
558 558 | """Below, `Returns:` should be considered a section header.
559 559 |
560 560 | Args:
561 |+ ----
561 562 | Here's a note.
562 563 |
563 564 | Returns:

sections.py:558:5: D407 [*] Missing dashed underline after section ("Returns")
|
557 | def titlecase_sub_section_header():
558 | """Below, `Returns:` should be considered a section header.
| _____^
559 | |
560 | | Args:
561 | | Here's a note.
562 | |
563 | | Returns:
564 | | """
| |_______^ D407
|
= help: Add dashed line under "Returns"

ℹ Safe fix
561 561 | Here's a note.
562 562 |
563 563 | Returns:
564 |+ -------
564 565 | """


Original file line number Diff line number Diff line change
Expand Up @@ -97,4 +97,18 @@ sections.py:261:5: D414 Section has no content ("Returns")
| |_______^ D414
|

sections.py:558:5: D414 Section has no content ("Returns")
|
557 | def titlecase_sub_section_header():
558 | """Below, `Returns:` should be considered a section header.
| _____^
559 | |
560 | | Args:
561 | | Here's a note.
562 | |
563 | | Returns:
564 | | """
| |_______^ D414
|


Original file line number Diff line number Diff line change
Expand Up @@ -64,4 +64,12 @@ D417.py:108:5: D417 Missing argument description in the docstring for `f`: `*arg
109 | """Do something.
|

D417.py:155:5: D417 Missing argument description in the docstring for `select_data`: `auto_save`
|
155 | def select_data(
| ^^^^^^^^^^^ D417
156 | query: str,
157 | args: tuple,
|


Original file line number Diff line number Diff line change
Expand Up @@ -64,4 +64,12 @@ D417.py:108:5: D417 Missing argument description in the docstring for `f`: `*arg
109 | """Do something.
|

D417.py:155:5: D417 Missing argument description in the docstring for `select_data`: `auto_save`
|
155 | def select_data(
| ^^^^^^^^^^^ D417
156 | query: str,
157 | args: tuple,
|


Loading