From 21403122ac8cdf793b2f074b98ce8bf9632f6c80 Mon Sep 17 00:00:00 2001 From: augustelalande Date: Mon, 5 Aug 2024 00:35:47 -0400 Subject: [PATCH 01/18] create violations --- .../test/fixtures/pydoclint/DOC101_google.py | 0 .../test/fixtures/pydoclint/DOC101_numpy.py | 0 .../test/fixtures/pydoclint/DOC102_google.py | 0 .../test/fixtures/pydoclint/DOC102_numpy.py | 0 .../src/checkers/ast/analyze/definitions.rs | 2 + crates/ruff_linter/src/codes.rs | 2 + crates/ruff_linter/src/rules/pydoclint/mod.rs | 4 + .../rules/pydoclint/rules/check_docstring.rs | 123 ++++++++++++++++++ ruff.schema.json | 4 + 9 files changed, 135 insertions(+) create mode 100644 crates/ruff_linter/resources/test/fixtures/pydoclint/DOC101_google.py create mode 100644 crates/ruff_linter/resources/test/fixtures/pydoclint/DOC101_numpy.py create mode 100644 crates/ruff_linter/resources/test/fixtures/pydoclint/DOC102_google.py create mode 100644 crates/ruff_linter/resources/test/fixtures/pydoclint/DOC102_numpy.py diff --git a/crates/ruff_linter/resources/test/fixtures/pydoclint/DOC101_google.py b/crates/ruff_linter/resources/test/fixtures/pydoclint/DOC101_google.py new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/crates/ruff_linter/resources/test/fixtures/pydoclint/DOC101_numpy.py b/crates/ruff_linter/resources/test/fixtures/pydoclint/DOC101_numpy.py new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/crates/ruff_linter/resources/test/fixtures/pydoclint/DOC102_google.py b/crates/ruff_linter/resources/test/fixtures/pydoclint/DOC102_google.py new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/crates/ruff_linter/resources/test/fixtures/pydoclint/DOC102_numpy.py b/crates/ruff_linter/resources/test/fixtures/pydoclint/DOC102_numpy.py new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/crates/ruff_linter/src/checkers/ast/analyze/definitions.rs b/crates/ruff_linter/src/checkers/ast/analyze/definitions.rs index 2f1dcda09e953..062c4c52b35b7 100644 --- a/crates/ruff_linter/src/checkers/ast/analyze/definitions.rs +++ b/crates/ruff_linter/src/checkers/ast/analyze/definitions.rs @@ -84,6 +84,8 @@ pub(crate) fn definitions(checker: &mut Checker) { Rule::UndocumentedPublicPackage, ]); let enforce_pydoclint = checker.any_enabled(&[ + Rule::DocstringMissingArgument, + Rule::DocstringExtraneousArgument, Rule::DocstringMissingReturns, Rule::DocstringExtraneousReturns, Rule::DocstringMissingYields, diff --git a/crates/ruff_linter/src/codes.rs b/crates/ruff_linter/src/codes.rs index 969c5dc4b7066..1d0c3764f54dd 100644 --- a/crates/ruff_linter/src/codes.rs +++ b/crates/ruff_linter/src/codes.rs @@ -921,6 +921,8 @@ pub fn code_to_rule(linter: Linter, code: &str) -> Option<(RuleGroup, Rule)> { (FastApi, "002") => (RuleGroup::Preview, rules::fastapi::rules::FastApiNonAnnotatedDependency), // pydoclint + (Pydoclint, "101") => (RuleGroup::Preview, rules::pydoclint::rules::DocstringMissingArgument), + (Pydoclint, "102") => (RuleGroup::Preview, rules::pydoclint::rules::DocstringExtraneousArgument), (Pydoclint, "201") => (RuleGroup::Preview, rules::pydoclint::rules::DocstringMissingReturns), (Pydoclint, "202") => (RuleGroup::Preview, rules::pydoclint::rules::DocstringExtraneousReturns), (Pydoclint, "402") => (RuleGroup::Preview, rules::pydoclint::rules::DocstringMissingYields), diff --git a/crates/ruff_linter/src/rules/pydoclint/mod.rs b/crates/ruff_linter/src/rules/pydoclint/mod.rs index 68565de689e19..543be8212ad82 100644 --- a/crates/ruff_linter/src/rules/pydoclint/mod.rs +++ b/crates/ruff_linter/src/rules/pydoclint/mod.rs @@ -26,6 +26,8 @@ mod tests { Ok(()) } + #[test_case(Rule::DocstringMissingArgument, Path::new("DOC101_google.py"))] + #[test_case(Rule::DocstringExtraneousArgument, Path::new("DOC102_google.py"))] #[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"))] @@ -45,6 +47,8 @@ mod tests { Ok(()) } + #[test_case(Rule::DocstringMissingArgument, Path::new("DOC101_numpy.py"))] + #[test_case(Rule::DocstringExtraneousArgument, Path::new("DOC102_numpy.py"))] #[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"))] 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 bedacf153f10b..755829212231f 100644 --- a/crates/ruff_linter/src/rules/pydoclint/rules/check_docstring.rs +++ b/crates/ruff_linter/src/rules/pydoclint/rules/check_docstring.rs @@ -16,6 +16,129 @@ use crate::docstrings::styles::SectionStyle; use crate::registry::Rule; use crate::rules::pydocstyle::settings::Convention; +/// ## What it does +/// Checks for function docstrings that do not include documentation for all +/// arguments. +/// +/// ## Why is this bad? +/// If a function accepts an argument without documenting it in its docstring, +/// it can be misleading to users and/or a sign of incomplete documentation or +/// refactors. +/// +/// ## Example +/// ```python +/// def calculate_speed(distance: float, time: float) -> float: +/// """Calculate speed as distance divided by time. +/// +/// Args: +/// distance: Distance traveled. +/// +/// Returns: +/// Speed as distance divided by time. +/// """ +/// return distance / time +/// ``` +/// +/// Use instead: +/// ```python +/// def calculate_speed(distance: float, time: float) -> float: +/// """Calculate speed as distance divided by time. +/// +/// Args: +/// distance: Distance traveled. +/// time: Time spent travelling. +/// +/// Returns: +/// Speed as distance divided by time. +/// """ +/// return distance / time +/// ``` raise FasterThanLightError from exc +/// ``` +#[violation] +pub struct DocstringMissingArgument { + id: String, +} + +impl Violation for DocstringMissingArgument { + #[derive_message_formats] + fn message(&self) -> String { + let DocstringMissingArgument { id } = self; + format!("Argument `{id}` missing from docstring") + } + + fn fix_title(&self) -> Option { + let DocstringMissingArgument { id } = self; + Some(format!("Add `{id}` to the docstring")) + } +} + +/// ## What it does +/// Checks for function docstrings that include arguments which are not +/// in the function signature. +/// +/// ## Why is this bad? +/// If a docstring documents an argument which is not in the function signature, +/// it can be misleading to users and/or a sign of incomplete documentation or +/// refactors. +/// +/// ## Example +/// ```python +/// def calculate_speed(distance: float, time: float) -> float: +/// """Calculate speed as distance divided by time. +/// +/// Args: +/// distance: Distance traveled. +/// time: Time spent traveling. +/// acceleration: Rate of change of speed. +/// +/// Returns: +/// Speed as distance divided by time. +/// """ +/// return distance / time +/// ``` +/// +/// Use instead: +/// ```python +/// """Calculate speed as distance divided by time. +/// +/// Args: +/// distance: Distance traveled. +/// time: Time spent traveling. +/// +/// Returns: +/// Speed as distance divided by time. +/// """ +/// return distance / time +/// ``` +#[violation] +pub struct DocstringExtraneousArgument { + ids: Vec, +} + +impl Violation for DocstringExtraneousArgument { + #[derive_message_formats] + fn message(&self) -> String { + let DocstringExtraneousArgument { ids } = self; + + if let [id] = ids.as_slice() { + format!("`{id}` is not in the function's signature") + } else { + format!( + "These arguments are not in the function's signature: {}", + ids.iter().map(|id| format!("`{id}`")).join(", ") + ) + } + } + + fn fix_title(&self) -> Option { + let DocstringExtraneousArgument { ids } = self; + Some(format!( + "Remove {} from the docstring", + ids.iter().map(|id| format!("`{id}`")).join(", ") + )) + } +} + /// ## What it does /// Checks for functions with explicit returns missing a "returns" section in /// their docstring. diff --git a/ruff.schema.json b/ruff.schema.json index 3de1fe4db5376..64d53cb0aa561 100644 --- a/ruff.schema.json +++ b/ruff.schema.json @@ -2891,6 +2891,10 @@ "DJ012", "DJ013", "DOC", + "DOC1", + "DOC10", + "DOC101", + "DOC102", "DOC2", "DOC20", "DOC201", From 7316554ce63a896e603fe48493f70abcd0abf887 Mon Sep 17 00:00:00 2001 From: augustelalande Date: Sat, 7 Sep 2024 18:43:56 -0400 Subject: [PATCH 02/18] more work --- .../test/fixtures/pydoclint/DOC101_google.py | 372 ++++++++++++++ .../test/fixtures/pydoclint/DOC101_numpy.py | 473 ++++++++++++++++++ .../test/fixtures/pydoclint/DOC102_google.py | 195 ++++++++ .../test/fixtures/pydoclint/DOC102_numpy.py | 257 ++++++++++ .../src/checkers/ast/analyze/definitions.rs | 4 +- crates/ruff_linter/src/codes.rs | 4 +- crates/ruff_linter/src/rules/pydoclint/mod.rs | 8 +- .../rules/pydoclint/rules/check_docstring.rs | 61 ++- 8 files changed, 1350 insertions(+), 24 deletions(-) diff --git a/crates/ruff_linter/resources/test/fixtures/pydoclint/DOC101_google.py b/crates/ruff_linter/resources/test/fixtures/pydoclint/DOC101_google.py index e69de29bb2d1d..4a62be73dbd40 100644 --- a/crates/ruff_linter/resources/test/fixtures/pydoclint/DOC101_google.py +++ b/crates/ruff_linter/resources/test/fixtures/pydoclint/DOC101_google.py @@ -0,0 +1,372 @@ +# OK +def add_numbers(a, b): + """ + Adds two numbers and returns the result. + + Args: + a (int): The first number to add. + b (int): The second number to add. + + Returns: + int: The sum of the two numbers. + """ + return a + b + + +# OK +def multiply_list_elements(lst, multiplier): + """ + Multiplies each element in a list by a given multiplier. + + Args: + lst (list of int): A list of integers. + multiplier (int): The multiplier for each element in the list. + + Returns: + list of int: A new list with each element multiplied. + """ + return [x * multiplier for x in lst] + + +# OK +def find_max_value(numbers): + """ + Finds the maximum value in a list of numbers. + + Args: + numbers (list of int): A list of integers to search through. + + Returns: + int: The maximum value found in the list. + """ + return max(numbers) + + +# OK +def create_user_profile(name, age, email, location="here"): + """ + Creates a user profile with basic information. + + Args: + name (str): The name of the user. + age (int): The age of the user. + email (str): The user's email address. + location (str): The location of the user. + + Returns: + dict: A dictionary containing the user's profile. + """ + return { + 'name': name, + 'age': age, + 'email': email, + 'location': location + } + + +# OK +def calculate_total_price(item_prices, tax_rate, discount): + """ + Calculates the total price after applying tax and a discount. + + Args: + item_prices (list of float): A list of prices for each item. + tax_rate (float): The tax rate to apply. + discount (float): The discount to subtract from the total. + + Returns: + float: The final total price after tax and discount. + """ + total = sum(item_prices) + total_with_tax = total + (total * tax_rate) + final_total = total_with_tax - discount + return final_total + + +# OK +def send_email(subject, body, to_address, cc_address=None, bcc_address=None): + """ + Sends an email to the specified recipients. + + Args: + subject (str): The subject of the email. + body (str): The content of the email. + to_address (str): The recipient's email address. + cc_address (str, optional): The email address for CC. Defaults to None. + bcc_address (str, optional): The email address for BCC. Defaults to None. + + Returns: + bool: True if the email was sent successfully, False otherwise. + """ + return True + + +# OK +def concatenate_strings(separator, *args): + """ + Concatenates multiple strings with a specified separator. + + Args: + separator (str): The separator to use between strings. + *args (str): Variable length argument list of strings to concatenate. + + Returns: + str: A single concatenated string. + """ + return separator.join(args) + + +# OK +def process_order(order_id, *items, **details): + """ + Processes an order with a list of items and optional order details. + + Args: + order_id (int): The unique identifier for the order. + *items (str): Variable length argument list of items in the order. + **details (dict): Additional details such as shipping method and address. + + Returns: + dict: A dictionary containing the order summary. + """ + return { + 'order_id': order_id, + 'items': items, + 'details': details + } + + +class Calculator: + """ + A simple calculator class that can perform basic arithmetic operations. + """ + + # OK + def __init__(self, value=0): + """ + Initializes the calculator with an initial value. + + Args: + value (int, optional): The initial value of the calculator. Defaults to 0. + """ + self.value = value + + # OK + def add(self, number, number2): + """ + Adds a number to the current value. + + Args: + number (int or float): The number to add to the current value. + + Returns: + int or float: The updated value after addition. + """ + self.value += number + number2 + return self.value + + # OK + @classmethod + def from_string(cls, value_str): + """ + Creates a Calculator instance from a string representation of a number. + + Args: + value_str (str): The string representing the initial value. + + Returns: + Calculator: A new instance of Calculator initialized with the value from the string. + """ + value = float(value_str) + return cls(value) + + # OK + @staticmethod + def is_valid_number(number): + """ + Checks if a given number is valid (int or float). + + Args: + number (any): The value to check. + + Returns: + bool: True if the number is valid, False otherwise. + """ + return isinstance(number, (int, float)) + + +# DOC101 +def add_numbers(a, b): + """ + Adds two numbers and returns the result. + + Args: + a (int): The first number to add. + + Returns: + int: The sum of the two numbers. + """ + return a + b + + +# DOC101 +def multiply_list_elements(lst, multiplier): + """ + Multiplies each element in a list by a given multiplier. + + Args: + lst (list of int): A list of integers. + + Returns: + list of int: A new list with each element multiplied. + """ + return [x * multiplier for x in lst] + + +# DOC101 +def find_max_value(numbers): + """ + Finds the maximum value in a list of numbers. + + Returns: + int: The maximum value found in the list. + """ + return max(numbers) + + +# DOC101 +def create_user_profile(name, age, email, location="here"): + """ + Creates a user profile with basic information. + + Args: + email (str): The user's email address. + location (str): The location of the user. + + Returns: + dict: A dictionary containing the user's profile. + """ + return { + 'name': name, + 'age': age, + 'email': email, + 'location': location + } + + +# DOC101 +def calculate_total_price(item_prices, tax_rate, discount): + """ + Calculates the total price after applying tax and a discount. + + Args: + item_prices (list of float): A list of prices for each item. + + Returns: + float: The final total price after tax and discount. + """ + total = sum(item_prices) + total_with_tax = total + (total * tax_rate) + final_total = total_with_tax - discount + return final_total + + +# DOC101 +def send_email(subject, body, to_address, cc_address=None, bcc_address=None): + """ + Sends an email to the specified recipients. + + Args: + subject (str): The subject of the email. + body (str): The content of the email. + to_address (str): The recipient's email address. + + Returns: + bool: True if the email was sent successfully, False otherwise. + """ + return True + + +# DOC101 +def concatenate_strings(separator, *args): + """ + Concatenates multiple strings with a specified separator. + + Args: + separator (str): The separator to use between strings. + + Returns: + str: A single concatenated string. + """ + return separator.join(args) + + +# DOC101 +def process_order(order_id, *items, **details): + """ + Processes an order with a list of items and optional order details. + + Args: + order_id (int): The unique identifier for the order. + + Returns: + dict: A dictionary containing the order summary. + """ + return { + 'order_id': order_id, + 'items': items, + 'details': details + } + + +class Calculator: + """ + A simple calculator class that can perform basic arithmetic operations. + """ + + # DOC101 + def __init__(self, value=0): + """ + Initializes the calculator with an initial value. + + Returns: + None + """ + self.value = value + + # DOC101 + def add(self, number, number2): + """ + Adds a number to the current value. + + Args: + number (int or float): The number to add to the current value. + + Returns: + int or float: The updated value after addition. + """ + self.value += number + number2 + return self.value + + # DOC101 + @classmethod + def from_string(cls, value_str): + """ + Creates a Calculator instance from a string representation of a number. + + Returns: + Calculator: A new instance of Calculator initialized with the value from the string. + """ + value = float(value_str) + return cls(value) + + # DOC101 + @staticmethod + def is_valid_number(number): + """ + Checks if a given number is valid (int or float). + + Returns: + bool: True if the number is valid, False otherwise. + """ + return isinstance(number, (int, float)) diff --git a/crates/ruff_linter/resources/test/fixtures/pydoclint/DOC101_numpy.py b/crates/ruff_linter/resources/test/fixtures/pydoclint/DOC101_numpy.py index e69de29bb2d1d..ce0c9113652bc 100644 --- a/crates/ruff_linter/resources/test/fixtures/pydoclint/DOC101_numpy.py +++ b/crates/ruff_linter/resources/test/fixtures/pydoclint/DOC101_numpy.py @@ -0,0 +1,473 @@ +# OK +def add_numbers(a, b): + """ + Adds two numbers and returns the result. + + Parameters + ---------- + a : int + The first number to add. + b : int + The second number to add. + + Returns + ------- + int + The sum of the two numbers. + """ + return a + b + + +# OK +def multiply_list_elements(lst, multiplier): + """ + Multiplies each element in a list by a given multiplier. + + Parameters + ---------- + lst : list of int + A list of integers. + multiplier : int + The multiplier for each element in the list. + + Returns + ------- + list of int + A new list with each element multiplied. + """ + return [x * multiplier for x in lst] + + +# OK +def find_max_value(numbers): + """ + Finds the maximum value in a list of numbers. + + Parameters + ---------- + numbers : list of int + A list of integers to search through. + + Returns + ------- + int + The maximum value found in the list. + """ + return max(numbers) + + +# OK +def create_user_profile(name, age, email, location="here"): + """ + Creates a user profile with basic information. + + Parameters + ---------- + name : str + The name of the user. + age : int + The age of the user. + email : str + The user's email address. + location : str, optional + The location of the user, by default "here". + + Returns + ------- + dict + A dictionary containing the user's profile. + """ + return { + 'name': name, + 'age': age, + 'email': email, + 'location': location + } + + +# OK +def calculate_total_price(item_prices, tax_rate, discount): + """ + Calculates the total price after applying tax and a discount. + + Parameters + ---------- + item_prices : list of float + A list of prices for each item. + tax_rate : float + The tax rate to apply. + discount : float + The discount to subtract from the total. + + Returns + ------- + float + The final total price after tax and discount. + """ + total = sum(item_prices) + total_with_tax = total + (total * tax_rate) + final_total = total_with_tax - discount + return final_total + + +# OK +def send_email(subject, body, to_address, cc_address=None, bcc_address=None): + """ + Sends an email to the specified recipients. + + Parameters + ---------- + subject : str + The subject of the email. + body : str + The content of the email. + to_address : str + The recipient's email address. + cc_address : str, optional + The email address for CC, by default None. + bcc_address : str, optional + The email address for BCC, by default None. + + Returns + ------- + bool + True if the email was sent successfully, False otherwise. + """ + return True + + +# OK +def concatenate_strings(separator, *args): + """ + Concatenates multiple strings with a specified separator. + + Parameters + ---------- + separator : str + The separator to use between strings. + *args : str + Variable length argument list of strings to concatenate. + + Returns + ------- + str + A single concatenated string. + """ + return separator.join(args) + + +# OK +def process_order(order_id, *items, **details): + """ + Processes an order with a list of items and optional order details. + + Parameters + ---------- + order_id : int + The unique identifier for the order. + *items : str + Variable length argument list of items in the order. + **details : dict + Additional details such as shipping method and address. + + Returns + ------- + dict + A dictionary containing the order summary. + """ + return { + 'order_id': order_id, + 'items': items, + 'details': details + } + + +class Calculator: + """ + A simple calculator class that can perform basic arithmetic operations. + """ + + # OK + def __init__(self, value=0): + """ + Initializes the calculator with an initial value. + + Parameters + ---------- + value : int, optional + The initial value of the calculator, by default 0. + """ + self.value = value + + # OK + def add(self, number, number2): + """ + Adds two numbers to the current value. + + Parameters + ---------- + number : int or float + The first number to add. + number2 : int or float + The second number to add. + + Returns + ------- + int or float + The updated value after addition. + """ + self.value += number + number2 + return self.value + + # OK + @classmethod + def from_string(cls, value_str): + """ + Creates a Calculator instance from a string representation of a number. + + Parameters + ---------- + value_str : str + The string representing the initial value. + + Returns + ------- + Calculator + A new instance of Calculator initialized with the value from the string. + """ + value = float(value_str) + return cls(value) + + # OK + @staticmethod + def is_valid_number(number): + """ + Checks if a given number is valid (int or float). + + Parameters + ---------- + number : any + The value to check. + + Returns + ------- + bool + True if the number is valid, False otherwise. + """ + return isinstance(number, (int, float)) + + +# DOC101 +def add_numbers(a, b): + """ + Adds two numbers and returns the result. + + Parameters + ---------- + a : int + The first number to add. + + Returns + ------- + int + The sum of the two numbers. + """ + return a + b + + +# DOC101 +def multiply_list_elements(lst, multiplier): + """ + Multiplies each element in a list by a given multiplier. + + Parameters + ---------- + lst : list of int + A list of integers. + + Returns + ------- + list of int + A new list with each element multiplied. + """ + return [x * multiplier for x in lst] + + +# DOC101 +def find_max_value(numbers): + """ + Finds the maximum value in a list of numbers. + + Returns + ------- + int + The maximum value found in the list. + """ + return max(numbers) + + +# DOC101 +def create_user_profile(name, age, email, location="here"): + """ + Creates a user profile with basic information. + + Parameters + ---------- + email : str + The user's email address. + location : str, optional + The location of the user, by default "here". + + Returns + ------- + dict + A dictionary containing the user's profile. + """ + return { + 'name': name, + 'age': age, + 'email': email, + 'location': location + } + + +# DOC101 +def calculate_total_price(item_prices, tax_rate, discount): + """ + Calculates the total price after applying tax and a discount. + + Parameters + ---------- + item_prices : list of float + A list of prices for each item. + + Returns + ------- + float + The final total price after tax and discount. + """ + total = sum(item_prices) + total_with_tax = total + (total * tax_rate) + final_total = total_with_tax - discount + return final_total + + +# DOC101 +def send_email(subject, body, to_address, cc_address=None, bcc_address=None): + """ + Sends an email to the specified recipients. + + Parameters + ---------- + subject : str + The subject of the email. + body : str + The content of the email. + to_address : str + The recipient's email address. + + Returns + ------- + bool + True if the email was sent successfully, False otherwise. + """ + return True + + +# DOC101 +def concatenate_strings(separator, *args): + """ + Concatenates multiple strings with a specified separator. + + Parameters + ---------- + separator : str + The separator to use between strings. + + Returns + ------- + str + A single concatenated string. + """ + return separator.join(args) + + +# DOC101 +def process_order(order_id, *items, **details): + """ + Processes an order with a list of items and optional order details. + + Parameters + ---------- + order_id : int + The unique identifier for the order. + + Returns + ------- + dict + A dictionary containing the order summary. + """ + return { + 'order_id': order_id, + 'items': items, + 'details': details + } + + +class Calculator: + """ + A simple calculator class that can perform basic arithmetic operations. + """ + + # DOC101 + def __init__(self, value=0): + """ + Initializes the calculator with an initial value. + + """ + self.value = value + + # DOC101 + def add(self, number, number2): + """ + Adds two numbers to the current value. + + Parameters + ---------- + number : int or float + The first number to add. + + Returns + ------- + int or float + The updated value after addition. + """ + self.value += number + number2 + return self.value + + # DOC101 + @classmethod + def from_string(cls, value_str): + """ + Creates a Calculator instance from a string representation of a number. + + Returns + ------- + Calculator + A new instance of Calculator initialized with the value from the string. + """ + value = float(value_str) + return cls(value) + + # DOC101 + @staticmethod + def is_valid_number(number): + """ + Checks if a given number is valid (int or float). + + Returns + ------- + bool + True if the number is valid, False otherwise. + """ + return isinstance(number, (int, float)) diff --git a/crates/ruff_linter/resources/test/fixtures/pydoclint/DOC102_google.py b/crates/ruff_linter/resources/test/fixtures/pydoclint/DOC102_google.py index e69de29bb2d1d..62b6583e5d260 100644 --- a/crates/ruff_linter/resources/test/fixtures/pydoclint/DOC102_google.py +++ b/crates/ruff_linter/resources/test/fixtures/pydoclint/DOC102_google.py @@ -0,0 +1,195 @@ +# DOC102 +def add_numbers(b): + """ + Adds two numbers and returns the result. + + Args: + a (int): The first number to add. + b (int): The second number to add. + + Returns: + int: The sum of the two numbers. + """ + return a + b + + +# DOC102 +def multiply_list_elements(lst): + """ + Multiplies each element in a list by a given multiplier. + + Args: + lst (list of int): A list of integers. + multiplier (int): The multiplier for each element in the list. + + Returns: + list of int: A new list with each element multiplied. + """ + return [x * multiplier for x in lst] + + +# DOC102 +def find_max_value(): + """ + Finds the maximum value in a list of numbers. + + Args: + numbers (list of int): A list of integers to search through. + + Returns: + int: The maximum value found in the list. + """ + return max(numbers) + + +# DOC102 +def create_user_profile(location="here"): + """ + Creates a user profile with basic information. + + Args: + name (str): The name of the user. + age (int): The age of the user. + email (str): The user's email address. + location (str): The location of the user. + + Returns: + dict: A dictionary containing the user's profile. + """ + return { + 'name': name, + 'age': age, + 'email': email, + 'location': location + } + + +# DOC102 +def calculate_total_price(item_prices, discount): + """ + Calculates the total price after applying tax and a discount. + + Args: + item_prices (list of float): A list of prices for each item. + tax_rate (float): The tax rate to apply. + discount (float): The discount to subtract from the total. + + Returns: + float: The final total price after tax and discount. + """ + total = sum(item_prices) + total_with_tax = total + (total * tax_rate) + final_total = total_with_tax - discount + return final_total + + +# DOC102 +def send_email(subject, body, bcc_address=None): + """ + Sends an email to the specified recipients. + + Args: + subject (str): The subject of the email. + body (str): The content of the email. + to_address (str): The recipient's email address. + cc_address (str, optional): The email address for CC. Defaults to None. + bcc_address (str, optional): The email address for BCC. Defaults to None. + + Returns: + bool: True if the email was sent successfully, False otherwise. + """ + return True + + +# DOC102 +def concatenate_strings(*args): + """ + Concatenates multiple strings with a specified separator. + + Args: + separator (str): The separator to use between strings. + *args (str): Variable length argument list of strings to concatenate. + + Returns: + str: A single concatenated string. + """ + return separator.join(args) + + +# DOC102 +def process_order(order_id): + """ + Processes an order with a list of items and optional order details. + + Args: + order_id (int): The unique identifier for the order. + *items (str): Variable length argument list of items in the order. + **details (dict): Additional details such as shipping method and address. + + Returns: + dict: A dictionary containing the order summary. + """ + return { + 'order_id': order_id, + 'items': items, + 'details': details + } + + +class Calculator: + """ + A simple calculator class that can perform basic arithmetic operations. + """ + + # DOC102 + def __init__(self): + """ + Initializes the calculator with an initial value. + + Args: + value (int, optional): The initial value of the calculator. Defaults to 0. + """ + self.value = value + + # DOC102 + def add(self, number2): + """ + Adds a number to the current value. + + Args: + number (int or float): The number to add to the current value. + + Returns: + int or float: The updated value after addition. + """ + self.value += number + number2 + return self.value + + # DOC102 + @classmethod + def from_string(cls): + """ + Creates a Calculator instance from a string representation of a number. + + Args: + value_str (str): The string representing the initial value. + + Returns: + Calculator: A new instance of Calculator initialized with the value from the string. + """ + value = float(value_str) + return cls(value) + + # DOC102 + @staticmethod + def is_valid_number(): + """ + Checks if a given number is valid (int or float). + + Args: + number (any): The value to check. + + Returns: + bool: True if the number is valid, False otherwise. + """ + return isinstance(number, (int, float)) diff --git a/crates/ruff_linter/resources/test/fixtures/pydoclint/DOC102_numpy.py b/crates/ruff_linter/resources/test/fixtures/pydoclint/DOC102_numpy.py index e69de29bb2d1d..d21db5a46253d 100644 --- a/crates/ruff_linter/resources/test/fixtures/pydoclint/DOC102_numpy.py +++ b/crates/ruff_linter/resources/test/fixtures/pydoclint/DOC102_numpy.py @@ -0,0 +1,257 @@ +# DOC102 +def add_numbers(b): + """ + Adds two numbers and returns the result. + + Parameters + ---------- + a : int + The first number to add. + b : int + The second number to add. + + Returns + ------- + int + The sum of the two numbers. + """ + return a + b + + +# DOC102 +def multiply_list_elements(lst): + """ + Multiplies each element in a list by a given multiplier. + + Parameters + ---------- + lst : list of int + A list of integers. + multiplier : int + The multiplier for each element in the list. + + Returns + ------- + list of int + A new list with each element multiplied. + """ + return [x * multiplier for x in lst] + + +# DOC102 +def find_max_value(): + """ + Finds the maximum value in a list of numbers. + + Parameters + ---------- + numbers : list of int + A list of integers to search through. + + Returns + ------- + int + The maximum value found in the list. + """ + return max(numbers) + + +# DOC102 +def create_user_profile(location="here"): + """ + Creates a user profile with basic information. + + Parameters + ---------- + name : str + The name of the user. + age : int + The age of the user. + email : str + The user's email address. + location : str, optional + The location of the user, by default "here". + + Returns + ------- + dict + A dictionary containing the user's profile. + """ + return { + 'name': name, + 'age': age, + 'email': email, + 'location': location + } + + +# DOC102 +def calculate_total_price(item_prices, discount): + """ + Calculates the total price after applying tax and a discount. + + Parameters + ---------- + item_prices : list of float + A list of prices for each item. + tax_rate : float + The tax rate to apply. + discount : float + The discount to subtract from the total. + + Returns + ------- + float + The final total price after tax and discount. + """ + total = sum(item_prices) + total_with_tax = total + (total * tax_rate) + final_total = total_with_tax - discount + return final_total + + +# DOC102 +def send_email(subject, body, bcc_address=None): + """ + Sends an email to the specified recipients. + + Parameters + ---------- + subject : str + The subject of the email. + body : str + The content of the email. + to_address : str + The recipient's email address. + cc_address : str, optional + The email address for CC, by default None. + bcc_address : str, optional + The email address for BCC, by default None. + + Returns + ------- + bool + True if the email was sent successfully, False otherwise. + """ + return True + + +# DOC102 +def concatenate_strings(*args): + """ + Concatenates multiple strings with a specified separator. + + Parameters + ---------- + separator : str + The separator to use between strings. + *args : str + Variable length argument list of strings to concatenate. + + Returns + ------- + str + A single concatenated string. + """ + return separator.join(args) + + +# DOC102 +def process_order(order_id): + """ + Processes an order with a list of items and optional order details. + + Parameters + ---------- + order_id : int + The unique identifier for the order. + *items : str + Variable length argument list of items in the order. + **details : dict + Additional details such as shipping method and address. + + Returns + ------- + dict + A dictionary containing the order summary. + """ + return { + 'order_id': order_id, + 'items': items, + 'details': details + } + + +class Calculator: + """ + A simple calculator class that can perform basic arithmetic operations. + """ + + # DOC102 + def __init__(self): + """ + Initializes the calculator with an initial value. + + Parameters + ---------- + value : int, optional + The initial value of the calculator, by default 0. + """ + self.value = value + + # DOC102 + def add(self, number2): + """ + Adds two numbers to the current value. + + Parameters + ---------- + number : int or float + The first number to add. + number2 : int or float + The second number to add. + + Returns + ------- + int or float + The updated value after addition. + """ + self.value += number + number2 + return self.value + + # DOC102 + @classmethod + def from_string(cls): + """ + Creates a Calculator instance from a string representation of a number. + + Parameters + ---------- + value_str : str + The string representing the initial value. + + Returns + ------- + Calculator + A new instance of Calculator initialized with the value from the string. + """ + value = float(value_str) + return cls(value) + + # DOC102 + @staticmethod + def is_valid_number(): + """ + Checks if a given number is valid (int or float). + + Parameters + ---------- + number : any + The value to check. + + Returns + ------- + bool + True if the number is valid, False otherwise. + """ + return isinstance(number, (int, float)) diff --git a/crates/ruff_linter/src/checkers/ast/analyze/definitions.rs b/crates/ruff_linter/src/checkers/ast/analyze/definitions.rs index c2bc7bb6d83e8..2cbaa9096d262 100644 --- a/crates/ruff_linter/src/checkers/ast/analyze/definitions.rs +++ b/crates/ruff_linter/src/checkers/ast/analyze/definitions.rs @@ -84,8 +84,8 @@ pub(crate) fn definitions(checker: &mut Checker) { Rule::UndocumentedPublicPackage, ]); let enforce_pydoclint = checker.any_enabled(&[ - Rule::DocstringMissingArgument, - Rule::DocstringExtraneousArgument, + Rule::DocstringMissingParameter, + Rule::DocstringExtraneousParameter, Rule::DocstringMissingReturns, Rule::DocstringExtraneousReturns, Rule::DocstringMissingYields, diff --git a/crates/ruff_linter/src/codes.rs b/crates/ruff_linter/src/codes.rs index d7ac6d99ff70e..071463c358726 100644 --- a/crates/ruff_linter/src/codes.rs +++ b/crates/ruff_linter/src/codes.rs @@ -923,8 +923,8 @@ pub fn code_to_rule(linter: Linter, code: &str) -> Option<(RuleGroup, Rule)> { (FastApi, "003") => (RuleGroup::Preview, rules::fastapi::rules::FastApiUnusedPathParameter), // pydoclint - (Pydoclint, "101") => (RuleGroup::Preview, rules::pydoclint::rules::DocstringMissingArgument), - (Pydoclint, "102") => (RuleGroup::Preview, rules::pydoclint::rules::DocstringExtraneousArgument), + (Pydoclint, "101") => (RuleGroup::Preview, rules::pydoclint::rules::DocstringMissingParameter), + (Pydoclint, "102") => (RuleGroup::Preview, rules::pydoclint::rules::DocstringExtraneousParameter), (Pydoclint, "201") => (RuleGroup::Preview, rules::pydoclint::rules::DocstringMissingReturns), (Pydoclint, "202") => (RuleGroup::Preview, rules::pydoclint::rules::DocstringExtraneousReturns), (Pydoclint, "402") => (RuleGroup::Preview, rules::pydoclint::rules::DocstringMissingYields), diff --git a/crates/ruff_linter/src/rules/pydoclint/mod.rs b/crates/ruff_linter/src/rules/pydoclint/mod.rs index 543be8212ad82..c60deb56c83bb 100644 --- a/crates/ruff_linter/src/rules/pydoclint/mod.rs +++ b/crates/ruff_linter/src/rules/pydoclint/mod.rs @@ -26,8 +26,8 @@ mod tests { Ok(()) } - #[test_case(Rule::DocstringMissingArgument, Path::new("DOC101_google.py"))] - #[test_case(Rule::DocstringExtraneousArgument, Path::new("DOC102_google.py"))] + #[test_case(Rule::DocstringMissingParameter, Path::new("DOC101_google.py"))] + #[test_case(Rule::DocstringExtraneousParameter, Path::new("DOC102_google.py"))] #[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"))] @@ -47,8 +47,8 @@ mod tests { Ok(()) } - #[test_case(Rule::DocstringMissingArgument, Path::new("DOC101_numpy.py"))] - #[test_case(Rule::DocstringExtraneousArgument, Path::new("DOC102_numpy.py"))] + #[test_case(Rule::DocstringMissingParameter, Path::new("DOC101_numpy.py"))] + #[test_case(Rule::DocstringExtraneousParameter, Path::new("DOC102_numpy.py"))] #[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"))] 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 0c016aee15ae8..40ca7ac8756a2 100644 --- a/crates/ruff_linter/src/rules/pydoclint/rules/check_docstring.rs +++ b/crates/ruff_linter/src/rules/pydoclint/rules/check_docstring.rs @@ -20,10 +20,10 @@ use crate::rules::pydocstyle::settings::Convention; /// ## What it does /// Checks for function docstrings that do not include documentation for all -/// arguments. +/// parameters. /// /// ## Why is this bad? -/// If a function accepts an argument without documenting it in its docstring, +/// If a function accepts a parameter without documenting it in its docstring, /// it can be misleading to users and/or a sign of incomplete documentation or /// refactors. /// @@ -57,29 +57,29 @@ use crate::rules::pydocstyle::settings::Convention; /// ``` raise FasterThanLightError from exc /// ``` #[violation] -pub struct DocstringMissingArgument { +pub struct DocstringMissingParameter { id: String, } -impl Violation for DocstringMissingArgument { +impl Violation for DocstringMissingParameter { #[derive_message_formats] fn message(&self) -> String { - let DocstringMissingArgument { id } = self; - format!("Argument `{id}` missing from docstring") + let DocstringMissingParameter { id } = self; + format!("Parameter `{id}` missing from docstring") } fn fix_title(&self) -> Option { - let DocstringMissingArgument { id } = self; + let DocstringMissingParameter { id } = self; Some(format!("Add `{id}` to the docstring")) } } /// ## What it does -/// Checks for function docstrings that include arguments which are not +/// Checks for function docstrings that include parameters which are not /// in the function signature. /// /// ## Why is this bad? -/// If a docstring documents an argument which is not in the function signature, +/// If a docstring documents a parameter which is not in the function signature, /// it can be misleading to users and/or a sign of incomplete documentation or /// refactors. /// @@ -113,27 +113,27 @@ impl Violation for DocstringMissingArgument { /// return distance / time /// ``` #[violation] -pub struct DocstringExtraneousArgument { +pub struct DocstringExtraneousParameter { ids: Vec, } -impl Violation for DocstringExtraneousArgument { +impl Violation for DocstringExtraneousParameter { #[derive_message_formats] fn message(&self) -> String { - let DocstringExtraneousArgument { ids } = self; + let DocstringExtraneousParameter { ids } = self; if let [id] = ids.as_slice() { format!("`{id}` is not in the function's signature") } else { format!( - "These arguments are not in the function's signature: {}", + "These parameters are not in the function's signature: {}", ids.iter().map(|id| format!("`{id}`")).join(", ") ) } } fn fix_title(&self) -> Option { - let DocstringExtraneousArgument { ids } = self; + let DocstringExtraneousParameter { ids } = self; Some(format!( "Remove {} from the docstring", ids.iter().map(|id| format!("`{id}`")).join(", ") @@ -525,11 +525,36 @@ impl<'a> RaisesSection<'a> { } } +/// An "Args" or "Parameters" section in a docstring. +#[derive(Debug)] +struct ParametersSection<'a> { + parameters: Vec>, + range: TextRange, +} + +impl Ranged for ParametersSection<'_> { + fn range(&self) -> TextRange { + self.range + } +} + +impl<'a> ParametersSection<'a> { + /// Return the parameters for the docstring, or `None` if the docstring does not contain + /// an "Args" or "Parameters" section. + fn from_section(section: &SectionContext<'a>, style: Option) -> Self { + Self { + parameters: parse_entries(section.following_lines_str(), style), + range: section.range(), + } + } +} + #[derive(Debug, Default)] struct DocstringSections<'a> { returns: Option, yields: Option, raises: Option>, + parameters: Option>, } impl<'a> DocstringSections<'a> { @@ -537,13 +562,17 @@ impl<'a> DocstringSections<'a> { let mut docstring_sections = Self::default(); for section in sections { match section.kind() { + SectionKind::Args | SectionKind::Arguments | SectionKind::Parameters => { + docstring_sections.parameters = + Some(ParametersSection::from_section(§ion, style)); + } SectionKind::Raises => { docstring_sections.raises = Some(RaisesSection::from_section(§ion, style)); } - SectionKind::Returns => { + SectionKind::Returns | SectionKind::Return => { docstring_sections.returns = Some(GenericSection::from_section(§ion)); } - SectionKind::Yields => { + SectionKind::Yields | SectionKind::Yield => { docstring_sections.yields = Some(GenericSection::from_section(§ion)); } _ => continue, From 928c0c7adaa7cbf701623b03ae2f574a97f04c17 Mon Sep 17 00:00:00 2001 From: augustelalande Date: Sat, 7 Sep 2024 19:00:59 -0400 Subject: [PATCH 03/18] parse parameters from docstring --- .../rules/pydoclint/rules/check_docstring.rs | 94 ++++++++++++++++--- 1 file changed, 82 insertions(+), 12 deletions(-) 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 40ca7ac8756a2..c11e3eea7e324 100644 --- a/crates/ruff_linter/src/rules/pydoclint/rules/check_docstring.rs +++ b/crates/ruff_linter/src/rules/pydoclint/rules/check_docstring.rs @@ -519,7 +519,7 @@ impl<'a> RaisesSection<'a> { /// a "Raises" section. fn from_section(section: &SectionContext<'a>, style: Option) -> Self { Self { - raised_exceptions: parse_entries(section.following_lines_str(), style), + raised_exceptions: parse_raises(section.following_lines_str(), style), range: section.range(), } } @@ -528,7 +528,7 @@ impl<'a> RaisesSection<'a> { /// An "Args" or "Parameters" section in a docstring. #[derive(Debug)] struct ParametersSection<'a> { - parameters: Vec>, + parameters: Vec<&'a str>, range: TextRange, } @@ -543,7 +543,7 @@ impl<'a> ParametersSection<'a> { /// an "Args" or "Parameters" section. fn from_section(section: &SectionContext<'a>, style: Option) -> Self { Self { - parameters: parse_entries(section.following_lines_str(), style), + parameters: parse_parameters(section.following_lines_str(), style), range: section.range(), } } @@ -582,18 +582,88 @@ impl<'a> DocstringSections<'a> { } } +/// Parse the entries in a "Parameters" section of a docstring. +/// +/// Attempts to parse using the specified [`SectionStyle`], falling back to the other style if no +/// entries are found. +fn parse_parameters(content: &str, style: Option) -> Vec<&str> { + match style { + Some(SectionStyle::Google) => parse_parameters_google(content), + Some(SectionStyle::Numpy) => parse_parameters_numpy(content), + None => { + let entries = parse_parameters_google(content); + if entries.is_empty() { + parse_parameters_numpy(content) + } else { + entries + } + } + } +} + +/// Parses Google-style "Args" sections of the form: +/// +/// ```python +/// Args: +/// a (int): The first number to add. +/// b (int): The second number to add. +/// ``` +fn parse_parameters_google(content: &str) -> Vec<&str> { + let mut entries: Vec<&str> = Vec::new(); + for potential in content.lines() { + let Some(colon_idx) = potential.find(':') else { + continue; + }; + if let Some(param) = potential[..colon_idx].split_whitespace().next() { + entries.push(param); + } + } + entries +} + +/// Parses NumPy-style "Parameters" sections of the form: +/// +/// ```python +/// Parameters +/// ---------- +/// a : int +/// The first number to add. +/// b : int +/// The second number to add. +/// ``` +fn parse_parameters_numpy(content: &str) -> Vec<&str> { + let mut entries: Vec<&str> = Vec::new(); + let mut lines = content.lines(); + let Some(dashes) = lines.next() else { + return entries; + }; + let indentation = &dashes[..dashes.len() - dashes.trim_start().len()]; + for potential in lines { + if let Some(entry) = potential.strip_prefix(indentation) { + if let Some(first_char) = entry.chars().next() { + if !first_char.is_whitespace() { + if let Some(param) = entry.split(':').next() { + entries.push(param.trim_end()); + } + } + } + } + } + entries +} + /// Parse the entries in a "Raises" section of a docstring. /// /// Attempts to parse using the specified [`SectionStyle`], falling back to the other style if no /// entries are found. -fn parse_entries(content: &str, style: Option) -> Vec { +fn parse_raises(content: &str, style: Option) -> Vec { match style { - Some(SectionStyle::Google) => parse_entries_google(content), - Some(SectionStyle::Numpy) => parse_entries_numpy(content), + Some(SectionStyle::Google) => parse_raises_google(content), + Some(SectionStyle::Numpy) => parse_raises_numpy(content), None => { - let entries = parse_entries_google(content); + let entries = parse_raises_google(content); if entries.is_empty() { - parse_entries_numpy(content) + parse_raises_numpy(content) } else { entries } @@ -601,14 +671,14 @@ fn parse_entries(content: &str, style: Option) -> Vec Vec { +fn parse_raises_google(content: &str) -> Vec { let mut entries: Vec = Vec::new(); for potential in content.lines() { let Some(colon_idx) = potential.find(':') else { @@ -620,7 +690,7 @@ fn parse_entries_google(content: &str) -> Vec { entries } -/// Parses NumPy-style docstring sections of the form: +/// Parses NumPy-style "Raises" section of the form: /// /// ```python /// Raises @@ -630,7 +700,7 @@ fn parse_entries_google(content: &str) -> Vec { /// DivisionByZero /// If attempting to divide by zero. /// ``` -fn parse_entries_numpy(content: &str) -> Vec { +fn parse_raises_numpy(content: &str) -> Vec { let mut entries: Vec = Vec::new(); let mut lines = content.lines(); let Some(dashes) = lines.next() else { From 62d447f6b2d82f0dc4db784395349c0210ee9e67 Mon Sep 17 00:00:00 2001 From: augustelalande Date: Sat, 7 Sep 2024 19:55:16 -0400 Subject: [PATCH 04/18] finish implementation --- .../test/fixtures/pydoclint/DOC101_google.py | 2 +- .../rules/pydoclint/rules/check_docstring.rs | 106 ++++++++- ...extraneous-parameter_DOC102_google.py.snap | 182 ++++++++++++++ ...-extraneous-parameter_DOC102_numpy.py.snap | 222 ++++++++++++++++++ ...ng-missing-parameter_DOC101_google.py.snap | 164 +++++++++++++ ...ing-missing-parameter_DOC101_numpy.py.snap | 164 +++++++++++++ 6 files changed, 837 insertions(+), 3 deletions(-) create mode 100644 crates/ruff_linter/src/rules/pydoclint/snapshots/ruff_linter__rules__pydoclint__tests__docstring-extraneous-parameter_DOC102_google.py.snap create mode 100644 crates/ruff_linter/src/rules/pydoclint/snapshots/ruff_linter__rules__pydoclint__tests__docstring-extraneous-parameter_DOC102_numpy.py.snap create mode 100644 crates/ruff_linter/src/rules/pydoclint/snapshots/ruff_linter__rules__pydoclint__tests__docstring-missing-parameter_DOC101_google.py.snap create mode 100644 crates/ruff_linter/src/rules/pydoclint/snapshots/ruff_linter__rules__pydoclint__tests__docstring-missing-parameter_DOC101_numpy.py.snap diff --git a/crates/ruff_linter/resources/test/fixtures/pydoclint/DOC101_google.py b/crates/ruff_linter/resources/test/fixtures/pydoclint/DOC101_google.py index 4a62be73dbd40..74db06c8ae3e0 100644 --- a/crates/ruff_linter/resources/test/fixtures/pydoclint/DOC101_google.py +++ b/crates/ruff_linter/resources/test/fixtures/pydoclint/DOC101_google.py @@ -152,7 +152,7 @@ def __init__(self, value=0): self.value = value # OK - def add(self, number, number2): + def add(self, number): """ Adds a number to the current value. 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 c11e3eea7e324..766c649b36c62 100644 --- a/crates/ruff_linter/src/rules/pydoclint/rules/check_docstring.rs +++ b/crates/ruff_linter/src/rules/pydoclint/rules/check_docstring.rs @@ -7,6 +7,8 @@ use ruff_python_ast::helpers::map_subscript; 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_ast::{AnyParameterRef, Parameter, ParameterWithDefault}; +use ruff_python_semantic::analyze::visibility::is_staticmethod; use ruff_python_semantic::analyze::{function_type, visibility}; use ruff_python_semantic::{Definition, SemanticModel}; use ruff_text_size::{Ranged, TextRange}; @@ -615,7 +617,7 @@ fn parse_parameters_google(content: &str) -> Vec<&str> { continue; }; if let Some(param) = potential[..colon_idx].split_whitespace().next() { - entries.push(param); + entries.push(param.trim_start_matches('*')); } } entries @@ -643,7 +645,7 @@ fn parse_parameters_numpy(content: &str) -> Vec<&str> { if let Some(first_char) = entry.chars().next() { if !first_char.is_whitespace() { if let Some(param) = entry.split(':').next() { - entries.push(param.trim_end()); + entries.push(param.trim_end().trim_start_matches('*')); } } } @@ -1049,6 +1051,56 @@ fn is_generator_function_annotated_as_returning_none( .is_some_and(GeneratorOrIteratorArguments::indicates_none_returned) } +/// An individual parameter from a function's signature. +#[derive(Debug)] +struct SignatureParameter<'a> { + name: &'a str, + range: TextRange, +} + +impl Ranged for SignatureParameter<'_> { + fn range(&self) -> TextRange { + self.range + } +} + +fn parameters_from_signature<'a>( + docstring: &'a Docstring, + semantic: &'a SemanticModel, +) -> Vec> { + let mut parameters = Vec::new(); + let Some(function) = docstring.definition.as_function_def() else { + return parameters; + }; + for param in function.parameters.iter().skip(usize::from( + docstring.definition.is_method() && !is_staticmethod(&function.decorator_list, semantic), + )) { + match param { + AnyParameterRef::Variadic(Parameter { name, range, .. }) => { + let name = name.as_str(); + if !name.starts_with('_') { + parameters.push(SignatureParameter { + name, + range: *range, + }); + } + } + AnyParameterRef::NonVariadic(ParameterWithDefault { + parameter, range, .. + }) => { + let name = parameter.name.as_str(); + if !name.starts_with('_') { + parameters.push(SignatureParameter { + name, + range: *range, + }); + } + } + } + } + parameters +} + /// DOC201, DOC202, DOC402, DOC403, DOC501, DOC502 pub(crate) fn check_docstring( checker: &mut Checker, @@ -1088,6 +1140,32 @@ pub(crate) fn check_docstring( visitor.finish() }; + let signature_parameters = parameters_from_signature(docstring, semantic); + + // DOC101 + if checker.enabled(Rule::DocstringMissingParameter) { + for signature_param in &signature_parameters { + if !docstring_sections + .parameters + .as_ref() + .is_some_and(|section| { + section + .parameters + .iter() + .any(|param| *param == signature_param.name) + }) + { + let diagnostic = Diagnostic::new( + DocstringMissingParameter { + id: (*signature_param.name).to_string(), + }, + signature_param.range(), + ); + diagnostics.push(diagnostic); + } + } + } + // DOC201 if checker.enabled(Rule::DocstringMissingReturns) { if !returns_documented(docstring, &docstring_sections, convention) { @@ -1185,6 +1263,30 @@ pub(crate) fn check_docstring( // Avoid applying "extraneous" rules to abstract methods. An abstract method's docstring _could_ // document that it raises an exception without including the exception in the implementation. if !visibility::is_abstract(&function_def.decorator_list, semantic) { + // DOC102 + if checker.enabled(Rule::DocstringExtraneousParameter) { + if let Some(docstring_params) = docstring_sections.parameters { + let mut extraneous_parameters = Vec::new(); + for docstring_param in &docstring_params.parameters { + if !signature_parameters + .iter() + .any(|param| param.name == *docstring_param) + { + extraneous_parameters.push(docstring_param.to_string()); + } + } + if !extraneous_parameters.is_empty() { + let diagnostic = Diagnostic::new( + DocstringExtraneousParameter { + ids: extraneous_parameters, + }, + docstring_params.range(), + ); + diagnostics.push(diagnostic); + } + } + } + // DOC202 if checker.enabled(Rule::DocstringExtraneousReturns) { if let Some(ref docstring_returns) = docstring_sections.returns { diff --git a/crates/ruff_linter/src/rules/pydoclint/snapshots/ruff_linter__rules__pydoclint__tests__docstring-extraneous-parameter_DOC102_google.py.snap b/crates/ruff_linter/src/rules/pydoclint/snapshots/ruff_linter__rules__pydoclint__tests__docstring-extraneous-parameter_DOC102_google.py.snap new file mode 100644 index 0000000000000..c9634a402bfb0 --- /dev/null +++ b/crates/ruff_linter/src/rules/pydoclint/snapshots/ruff_linter__rules__pydoclint__tests__docstring-extraneous-parameter_DOC102_google.py.snap @@ -0,0 +1,182 @@ +--- +source: crates/ruff_linter/src/rules/pydoclint/mod.rs +--- +DOC102_google.py:6:1: DOC102 `a` is not in the function's signature + | + 4 | Adds two numbers and returns the result. + 5 | + 6 | / Args: + 7 | | a (int): The first number to add. + 8 | | b (int): The second number to add. + 9 | | +10 | | Returns: + | |_^ DOC102 +11 | int: The sum of the two numbers. +12 | """ + | + = help: Remove `a` from the docstring + +DOC102_google.py:21:1: DOC102 `multiplier` is not in the function's signature + | +19 | Multiplies each element in a list by a given multiplier. +20 | +21 | / Args: +22 | | lst (list of int): A list of integers. +23 | | multiplier (int): The multiplier for each element in the list. +24 | | +25 | | Returns: + | |_^ DOC102 +26 | list of int: A new list with each element multiplied. +27 | """ + | + = help: Remove `multiplier` from the docstring + +DOC102_google.py:36:1: DOC102 `numbers` is not in the function's signature + | +34 | Finds the maximum value in a list of numbers. +35 | +36 | / Args: +37 | | numbers (list of int): A list of integers to search through. +38 | | +39 | | Returns: + | |_^ DOC102 +40 | int: The maximum value found in the list. +41 | """ + | + = help: Remove `numbers` from the docstring + +DOC102_google.py:50:1: DOC102 These parameters are not in the function's signature: `name`, `age`, `email` + | +48 | Creates a user profile with basic information. +49 | +50 | / Args: +51 | | name (str): The name of the user. +52 | | age (int): The age of the user. +53 | | email (str): The user's email address. +54 | | location (str): The location of the user. +55 | | +56 | | Returns: + | |_^ DOC102 +57 | dict: A dictionary containing the user's profile. +58 | """ + | + = help: Remove `name`, `age`, `email` from the docstring + +DOC102_google.py:72:1: DOC102 `tax_rate` is not in the function's signature + | +70 | Calculates the total price after applying tax and a discount. +71 | +72 | / Args: +73 | | item_prices (list of float): A list of prices for each item. +74 | | tax_rate (float): The tax rate to apply. +75 | | discount (float): The discount to subtract from the total. +76 | | +77 | | Returns: + | |_^ DOC102 +78 | float: The final total price after tax and discount. +79 | """ + | + = help: Remove `tax_rate` from the docstring + +DOC102_google.py:91:1: DOC102 These parameters are not in the function's signature: `to_address`, `cc_address` + | + 89 | Sends an email to the specified recipients. + 90 | + 91 | / Args: + 92 | | subject (str): The subject of the email. + 93 | | body (str): The content of the email. + 94 | | to_address (str): The recipient's email address. + 95 | | cc_address (str, optional): The email address for CC. Defaults to None. + 96 | | bcc_address (str, optional): The email address for BCC. Defaults to None. + 97 | | + 98 | | Returns: + | |_^ DOC102 + 99 | bool: True if the email was sent successfully, False otherwise. +100 | """ + | + = help: Remove `to_address`, `cc_address` from the docstring + +DOC102_google.py:109:1: DOC102 `separator` is not in the function's signature + | +107 | Concatenates multiple strings with a specified separator. +108 | +109 | / Args: +110 | | separator (str): The separator to use between strings. +111 | | *args (str): Variable length argument list of strings to concatenate. +112 | | +113 | | Returns: + | |_^ DOC102 +114 | str: A single concatenated string. +115 | """ + | + = help: Remove `separator` from the docstring + +DOC102_google.py:124:1: DOC102 These parameters are not in the function's signature: `items`, `details` + | +122 | Processes an order with a list of items and optional order details. +123 | +124 | / Args: +125 | | order_id (int): The unique identifier for the order. +126 | | *items (str): Variable length argument list of items in the order. +127 | | **details (dict): Additional details such as shipping method and address. +128 | | +129 | | Returns: + | |_^ DOC102 +130 | dict: A dictionary containing the order summary. +131 | """ + | + = help: Remove `items`, `details` from the docstring + +DOC102_google.py:149:1: DOC102 `value` is not in the function's signature + | +147 | Initializes the calculator with an initial value. +148 | +149 | / Args: +150 | | value (int, optional): The initial value of the calculator. Defaults to 0. +151 | | """ + | |________^ DOC102 +152 | self.value = value + | + = help: Remove `value` from the docstring + +DOC102_google.py:159:1: DOC102 `number` is not in the function's signature + | +157 | Adds a number to the current value. +158 | +159 | / Args: +160 | | number (int or float): The number to add to the current value. +161 | | +162 | | Returns: + | |_^ DOC102 +163 | int or float: The updated value after addition. +164 | """ + | + = help: Remove `number` from the docstring + +DOC102_google.py:174:1: DOC102 `value_str` is not in the function's signature + | +172 | Creates a Calculator instance from a string representation of a number. +173 | +174 | / Args: +175 | | value_str (str): The string representing the initial value. +176 | | +177 | | Returns: + | |_^ DOC102 +178 | Calculator: A new instance of Calculator initialized with the value from the string. +179 | """ + | + = help: Remove `value_str` from the docstring + +DOC102_google.py:189:1: DOC102 `number` is not in the function's signature + | +187 | Checks if a given number is valid (int or float). +188 | +189 | / Args: +190 | | number (any): The value to check. +191 | | +192 | | Returns: + | |_^ DOC102 +193 | bool: True if the number is valid, False otherwise. +194 | """ + | + = help: Remove `number` from the docstring diff --git a/crates/ruff_linter/src/rules/pydoclint/snapshots/ruff_linter__rules__pydoclint__tests__docstring-extraneous-parameter_DOC102_numpy.py.snap b/crates/ruff_linter/src/rules/pydoclint/snapshots/ruff_linter__rules__pydoclint__tests__docstring-extraneous-parameter_DOC102_numpy.py.snap new file mode 100644 index 0000000000000..24c477635d7ac --- /dev/null +++ b/crates/ruff_linter/src/rules/pydoclint/snapshots/ruff_linter__rules__pydoclint__tests__docstring-extraneous-parameter_DOC102_numpy.py.snap @@ -0,0 +1,222 @@ +--- +source: crates/ruff_linter/src/rules/pydoclint/mod.rs +--- +DOC102_numpy.py:6:1: DOC102 `a` is not in the function's signature + | + 4 | Adds two numbers and returns the result. + 5 | + 6 | / Parameters + 7 | | ---------- + 8 | | a : int + 9 | | The first number to add. +10 | | b : int +11 | | The second number to add. +12 | | +13 | | Returns + | |_^ DOC102 +14 | ------- +15 | int + | + = help: Remove `a` from the docstring + +DOC102_numpy.py:26:1: DOC102 `multiplier` is not in the function's signature + | +24 | Multiplies each element in a list by a given multiplier. +25 | +26 | / Parameters +27 | | ---------- +28 | | lst : list of int +29 | | A list of integers. +30 | | multiplier : int +31 | | The multiplier for each element in the list. +32 | | +33 | | Returns + | |_^ DOC102 +34 | ------- +35 | list of int + | + = help: Remove `multiplier` from the docstring + +DOC102_numpy.py:46:1: DOC102 `numbers` is not in the function's signature + | +44 | Finds the maximum value in a list of numbers. +45 | +46 | / Parameters +47 | | ---------- +48 | | numbers : list of int +49 | | A list of integers to search through. +50 | | +51 | | Returns + | |_^ DOC102 +52 | ------- +53 | int + | + = help: Remove `numbers` from the docstring + +DOC102_numpy.py:64:1: DOC102 These parameters are not in the function's signature: `name`, `age`, `email` + | +62 | Creates a user profile with basic information. +63 | +64 | / Parameters +65 | | ---------- +66 | | name : str +67 | | The name of the user. +68 | | age : int +69 | | The age of the user. +70 | | email : str +71 | | The user's email address. +72 | | location : str, optional +73 | | The location of the user, by default "here". +74 | | +75 | | Returns + | |_^ DOC102 +76 | ------- +77 | dict + | + = help: Remove `name`, `age`, `email` from the docstring + +DOC102_numpy.py:93:1: DOC102 `tax_rate` is not in the function's signature + | + 91 | Calculates the total price after applying tax and a discount. + 92 | + 93 | / Parameters + 94 | | ---------- + 95 | | item_prices : list of float + 96 | | A list of prices for each item. + 97 | | tax_rate : float + 98 | | The tax rate to apply. + 99 | | discount : float +100 | | The discount to subtract from the total. +101 | | +102 | | Returns + | |_^ DOC102 +103 | ------- +104 | float + | + = help: Remove `tax_rate` from the docstring + +DOC102_numpy.py:118:1: DOC102 These parameters are not in the function's signature: `to_address`, `cc_address` + | +116 | Sends an email to the specified recipients. +117 | +118 | / Parameters +119 | | ---------- +120 | | subject : str +121 | | The subject of the email. +122 | | body : str +123 | | The content of the email. +124 | | to_address : str +125 | | The recipient's email address. +126 | | cc_address : str, optional +127 | | The email address for CC, by default None. +128 | | bcc_address : str, optional +129 | | The email address for BCC, by default None. +130 | | +131 | | Returns + | |_^ DOC102 +132 | ------- +133 | bool + | + = help: Remove `to_address`, `cc_address` from the docstring + +DOC102_numpy.py:144:1: DOC102 `separator` is not in the function's signature + | +142 | Concatenates multiple strings with a specified separator. +143 | +144 | / Parameters +145 | | ---------- +146 | | separator : str +147 | | The separator to use between strings. +148 | | *args : str +149 | | Variable length argument list of strings to concatenate. +150 | | +151 | | Returns + | |_^ DOC102 +152 | ------- +153 | str + | + = help: Remove `separator` from the docstring + +DOC102_numpy.py:164:1: DOC102 These parameters are not in the function's signature: `items`, `details` + | +162 | Processes an order with a list of items and optional order details. +163 | +164 | / Parameters +165 | | ---------- +166 | | order_id : int +167 | | The unique identifier for the order. +168 | | *items : str +169 | | Variable length argument list of items in the order. +170 | | **details : dict +171 | | Additional details such as shipping method and address. +172 | | +173 | | Returns + | |_^ DOC102 +174 | ------- +175 | dict + | + = help: Remove `items`, `details` from the docstring + +DOC102_numpy.py:195:1: DOC102 `value` is not in the function's signature + | +193 | Initializes the calculator with an initial value. +194 | +195 | / Parameters +196 | | ---------- +197 | | value : int, optional +198 | | The initial value of the calculator, by default 0. +199 | | """ + | |________^ DOC102 +200 | self.value = value + | + = help: Remove `value` from the docstring + +DOC102_numpy.py:207:1: DOC102 `number` is not in the function's signature + | +205 | Adds two numbers to the current value. +206 | +207 | / Parameters +208 | | ---------- +209 | | number : int or float +210 | | The first number to add. +211 | | number2 : int or float +212 | | The second number to add. +213 | | +214 | | Returns + | |_^ DOC102 +215 | ------- +216 | int or float + | + = help: Remove `number` from the docstring + +DOC102_numpy.py:228:1: DOC102 `value_str` is not in the function's signature + | +226 | Creates a Calculator instance from a string representation of a number. +227 | +228 | / Parameters +229 | | ---------- +230 | | value_str : str +231 | | The string representing the initial value. +232 | | +233 | | Returns + | |_^ DOC102 +234 | ------- +235 | Calculator + | + = help: Remove `value_str` from the docstring + +DOC102_numpy.py:247:1: DOC102 `number` is not in the function's signature + | +245 | Checks if a given number is valid (int or float). +246 | +247 | / Parameters +248 | | ---------- +249 | | number : any +250 | | The value to check. +251 | | +252 | | Returns + | |_^ DOC102 +253 | ------- +254 | bool + | + = help: Remove `number` from the docstring diff --git a/crates/ruff_linter/src/rules/pydoclint/snapshots/ruff_linter__rules__pydoclint__tests__docstring-missing-parameter_DOC101_google.py.snap b/crates/ruff_linter/src/rules/pydoclint/snapshots/ruff_linter__rules__pydoclint__tests__docstring-missing-parameter_DOC101_google.py.snap new file mode 100644 index 0000000000000..b444c7d365e68 --- /dev/null +++ b/crates/ruff_linter/src/rules/pydoclint/snapshots/ruff_linter__rules__pydoclint__tests__docstring-missing-parameter_DOC101_google.py.snap @@ -0,0 +1,164 @@ +--- +source: crates/ruff_linter/src/rules/pydoclint/mod.rs +--- +DOC101_google.py:199:20: DOC101 Parameter `b` missing from docstring + | +198 | # DOC101 +199 | def add_numbers(a, b): + | ^ DOC101 +200 | """ +201 | Adds two numbers and returns the result. + | + = help: Add `b` to the docstring + +DOC101_google.py:213:33: DOC101 Parameter `multiplier` missing from docstring + | +212 | # DOC101 +213 | def multiply_list_elements(lst, multiplier): + | ^^^^^^^^^^ DOC101 +214 | """ +215 | Multiplies each element in a list by a given multiplier. + | + = help: Add `multiplier` to the docstring + +DOC101_google.py:227:20: DOC101 Parameter `numbers` missing from docstring + | +226 | # DOC101 +227 | def find_max_value(numbers): + | ^^^^^^^ DOC101 +228 | """ +229 | Finds the maximum value in a list of numbers. + | + = help: Add `numbers` to the docstring + +DOC101_google.py:238:25: DOC101 Parameter `name` missing from docstring + | +237 | # DOC101 +238 | def create_user_profile(name, age, email, location="here"): + | ^^^^ DOC101 +239 | """ +240 | Creates a user profile with basic information. + | + = help: Add `name` to the docstring + +DOC101_google.py:238:31: DOC101 Parameter `age` missing from docstring + | +237 | # DOC101 +238 | def create_user_profile(name, age, email, location="here"): + | ^^^ DOC101 +239 | """ +240 | Creates a user profile with basic information. + | + = help: Add `age` to the docstring + +DOC101_google.py:258:40: DOC101 Parameter `tax_rate` missing from docstring + | +257 | # DOC101 +258 | def calculate_total_price(item_prices, tax_rate, discount): + | ^^^^^^^^ DOC101 +259 | """ +260 | Calculates the total price after applying tax and a discount. + | + = help: Add `tax_rate` to the docstring + +DOC101_google.py:258:50: DOC101 Parameter `discount` missing from docstring + | +257 | # DOC101 +258 | def calculate_total_price(item_prices, tax_rate, discount): + | ^^^^^^^^ DOC101 +259 | """ +260 | Calculates the total price after applying tax and a discount. + | + = help: Add `discount` to the docstring + +DOC101_google.py:275:43: DOC101 Parameter `cc_address` missing from docstring + | +274 | # DOC101 +275 | def send_email(subject, body, to_address, cc_address=None, bcc_address=None): + | ^^^^^^^^^^^^^^^ DOC101 +276 | """ +277 | Sends an email to the specified recipients. + | + = help: Add `cc_address` to the docstring + +DOC101_google.py:275:60: DOC101 Parameter `bcc_address` missing from docstring + | +274 | # DOC101 +275 | def send_email(subject, body, to_address, cc_address=None, bcc_address=None): + | ^^^^^^^^^^^^^^^^ DOC101 +276 | """ +277 | Sends an email to the specified recipients. + | + = help: Add `bcc_address` to the docstring + +DOC101_google.py:291:36: DOC101 Parameter `args` missing from docstring + | +290 | # DOC101 +291 | def concatenate_strings(separator, *args): + | ^^^^^ DOC101 +292 | """ +293 | Concatenates multiple strings with a specified separator. + | + = help: Add `args` to the docstring + +DOC101_google.py:305:29: DOC101 Parameter `items` missing from docstring + | +304 | # DOC101 +305 | def process_order(order_id, *items, **details): + | ^^^^^^ DOC101 +306 | """ +307 | Processes an order with a list of items and optional order details. + | + = help: Add `items` to the docstring + +DOC101_google.py:305:37: DOC101 Parameter `details` missing from docstring + | +304 | # DOC101 +305 | def process_order(order_id, *items, **details): + | ^^^^^^^^^ DOC101 +306 | """ +307 | Processes an order with a list of items and optional order details. + | + = help: Add `details` to the docstring + +DOC101_google.py:328:24: DOC101 Parameter `value` missing from docstring + | +327 | # DOC101 +328 | def __init__(self, value=0): + | ^^^^^^^ DOC101 +329 | """ +330 | Initializes the calculator with an initial value. + | + = help: Add `value` to the docstring + +DOC101_google.py:338:27: DOC101 Parameter `number2` missing from docstring + | +337 | # DOC101 +338 | def add(self, number, number2): + | ^^^^^^^ DOC101 +339 | """ +340 | Adds a number to the current value. + | + = help: Add `number2` to the docstring + +DOC101_google.py:353:26: DOC101 Parameter `value_str` missing from docstring + | +351 | # DOC101 +352 | @classmethod +353 | def from_string(cls, value_str): + | ^^^^^^^^^ DOC101 +354 | """ +355 | Creates a Calculator instance from a string representation of a number. + | + = help: Add `value_str` to the docstring + +DOC101_google.py:365:25: DOC101 Parameter `number` missing from docstring + | +363 | # DOC101 +364 | @staticmethod +365 | def is_valid_number(number): + | ^^^^^^ DOC101 +366 | """ +367 | Checks if a given number is valid (int or float). + | + = help: Add `number` to the docstring diff --git a/crates/ruff_linter/src/rules/pydoclint/snapshots/ruff_linter__rules__pydoclint__tests__docstring-missing-parameter_DOC101_numpy.py.snap b/crates/ruff_linter/src/rules/pydoclint/snapshots/ruff_linter__rules__pydoclint__tests__docstring-missing-parameter_DOC101_numpy.py.snap new file mode 100644 index 0000000000000..158444dc616aa --- /dev/null +++ b/crates/ruff_linter/src/rules/pydoclint/snapshots/ruff_linter__rules__pydoclint__tests__docstring-missing-parameter_DOC101_numpy.py.snap @@ -0,0 +1,164 @@ +--- +source: crates/ruff_linter/src/rules/pydoclint/mod.rs +--- +DOC101_numpy.py:261:20: DOC101 Parameter `b` missing from docstring + | +260 | # DOC101 +261 | def add_numbers(a, b): + | ^ DOC101 +262 | """ +263 | Adds two numbers and returns the result. + | + = help: Add `b` to the docstring + +DOC101_numpy.py:279:33: DOC101 Parameter `multiplier` missing from docstring + | +278 | # DOC101 +279 | def multiply_list_elements(lst, multiplier): + | ^^^^^^^^^^ DOC101 +280 | """ +281 | Multiplies each element in a list by a given multiplier. + | + = help: Add `multiplier` to the docstring + +DOC101_numpy.py:297:20: DOC101 Parameter `numbers` missing from docstring + | +296 | # DOC101 +297 | def find_max_value(numbers): + | ^^^^^^^ DOC101 +298 | """ +299 | Finds the maximum value in a list of numbers. + | + = help: Add `numbers` to the docstring + +DOC101_numpy.py:310:25: DOC101 Parameter `name` missing from docstring + | +309 | # DOC101 +310 | def create_user_profile(name, age, email, location="here"): + | ^^^^ DOC101 +311 | """ +312 | Creates a user profile with basic information. + | + = help: Add `name` to the docstring + +DOC101_numpy.py:310:31: DOC101 Parameter `age` missing from docstring + | +309 | # DOC101 +310 | def create_user_profile(name, age, email, location="here"): + | ^^^ DOC101 +311 | """ +312 | Creates a user profile with basic information. + | + = help: Add `age` to the docstring + +DOC101_numpy.py:335:40: DOC101 Parameter `tax_rate` missing from docstring + | +334 | # DOC101 +335 | def calculate_total_price(item_prices, tax_rate, discount): + | ^^^^^^^^ DOC101 +336 | """ +337 | Calculates the total price after applying tax and a discount. + | + = help: Add `tax_rate` to the docstring + +DOC101_numpy.py:335:50: DOC101 Parameter `discount` missing from docstring + | +334 | # DOC101 +335 | def calculate_total_price(item_prices, tax_rate, discount): + | ^^^^^^^^ DOC101 +336 | """ +337 | Calculates the total price after applying tax and a discount. + | + = help: Add `discount` to the docstring + +DOC101_numpy.py:356:43: DOC101 Parameter `cc_address` missing from docstring + | +355 | # DOC101 +356 | def send_email(subject, body, to_address, cc_address=None, bcc_address=None): + | ^^^^^^^^^^^^^^^ DOC101 +357 | """ +358 | Sends an email to the specified recipients. + | + = help: Add `cc_address` to the docstring + +DOC101_numpy.py:356:60: DOC101 Parameter `bcc_address` missing from docstring + | +355 | # DOC101 +356 | def send_email(subject, body, to_address, cc_address=None, bcc_address=None): + | ^^^^^^^^^^^^^^^^ DOC101 +357 | """ +358 | Sends an email to the specified recipients. + | + = help: Add `bcc_address` to the docstring + +DOC101_numpy.py:378:36: DOC101 Parameter `args` missing from docstring + | +377 | # DOC101 +378 | def concatenate_strings(separator, *args): + | ^^^^^ DOC101 +379 | """ +380 | Concatenates multiple strings with a specified separator. + | + = help: Add `args` to the docstring + +DOC101_numpy.py:396:29: DOC101 Parameter `items` missing from docstring + | +395 | # DOC101 +396 | def process_order(order_id, *items, **details): + | ^^^^^^ DOC101 +397 | """ +398 | Processes an order with a list of items and optional order details. + | + = help: Add `items` to the docstring + +DOC101_numpy.py:396:37: DOC101 Parameter `details` missing from docstring + | +395 | # DOC101 +396 | def process_order(order_id, *items, **details): + | ^^^^^^^^^ DOC101 +397 | """ +398 | Processes an order with a list of items and optional order details. + | + = help: Add `details` to the docstring + +DOC101_numpy.py:423:24: DOC101 Parameter `value` missing from docstring + | +422 | # DOC101 +423 | def __init__(self, value=0): + | ^^^^^^^ DOC101 +424 | """ +425 | Initializes the calculator with an initial value. + | + = help: Add `value` to the docstring + +DOC101_numpy.py:431:27: DOC101 Parameter `number2` missing from docstring + | +430 | # DOC101 +431 | def add(self, number, number2): + | ^^^^^^^ DOC101 +432 | """ +433 | Adds two numbers to the current value. + | + = help: Add `number2` to the docstring + +DOC101_numpy.py:450:26: DOC101 Parameter `value_str` missing from docstring + | +448 | # DOC101 +449 | @classmethod +450 | def from_string(cls, value_str): + | ^^^^^^^^^ DOC101 +451 | """ +452 | Creates a Calculator instance from a string representation of a number. + | + = help: Add `value_str` to the docstring + +DOC101_numpy.py:464:25: DOC101 Parameter `number` missing from docstring + | +462 | # DOC101 +463 | @staticmethod +464 | def is_valid_number(number): + | ^^^^^^ DOC101 +465 | """ +466 | Checks if a given number is valid (int or float). + | + = help: Add `number` to the docstring From d45967f330914cf57e0056a02cdf710bb16f918a Mon Sep 17 00:00:00 2001 From: augustelalande Date: Sat, 7 Sep 2024 20:09:26 -0400 Subject: [PATCH 05/18] clippy and doc --- .../ruff_linter/src/rules/pydoclint/rules/check_docstring.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) 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 766c649b36c62..ad046ab4c20eb 100644 --- a/crates/ruff_linter/src/rules/pydoclint/rules/check_docstring.rs +++ b/crates/ruff_linter/src/rules/pydoclint/rules/check_docstring.rs @@ -103,6 +103,7 @@ impl Violation for DocstringMissingParameter { /// /// Use instead: /// ```python +/// def calculate_speed(distance: float, time: float) -> float: /// """Calculate speed as distance divided by time. /// /// Args: @@ -1272,7 +1273,7 @@ pub(crate) fn check_docstring( .iter() .any(|param| param.name == *docstring_param) { - extraneous_parameters.push(docstring_param.to_string()); + extraneous_parameters.push((*docstring_param).to_string()); } } if !extraneous_parameters.is_empty() { From 04a91fe67bb184ee90d32d8353c31ffaf5e26572 Mon Sep 17 00:00:00 2001 From: augustelalande Date: Tue, 10 Sep 2024 16:43:23 -0400 Subject: [PATCH 06/18] group missing params into one violation --- .../rules/pydoclint/rules/check_docstring.rs | 44 ++- ...ng-missing-parameter_DOC101_google.py.snap | 286 ++++++++-------- ...ing-missing-parameter_DOC101_numpy.py.snap | 309 ++++++++++-------- 3 files changed, 359 insertions(+), 280 deletions(-) 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 ad046ab4c20eb..4124bd7daf185 100644 --- a/crates/ruff_linter/src/rules/pydoclint/rules/check_docstring.rs +++ b/crates/ruff_linter/src/rules/pydoclint/rules/check_docstring.rs @@ -60,19 +60,30 @@ use crate::rules::pydocstyle::settings::Convention; /// ``` #[violation] pub struct DocstringMissingParameter { - id: String, + ids: Vec, } impl Violation for DocstringMissingParameter { #[derive_message_formats] fn message(&self) -> String { - let DocstringMissingParameter { id } = self; - format!("Parameter `{id}` missing from docstring") + let DocstringMissingParameter { ids } = self; + + if let [id] = ids.as_slice() { + format!("Parameter `{id}` missing from the docstring") + } else { + format!( + "These parameters are missing from the docstring: {}", + ids.iter().map(|id| format!("`{id}`")).join(", ") + ) + } } fn fix_title(&self) -> Option { - let DocstringMissingParameter { id } = self; - Some(format!("Add `{id}` to the docstring")) + let DocstringMissingParameter { ids } = self; + Some(format!( + "Add {} to the docstring", + ids.iter().map(|id| format!("`{id}`")).join(", ") + )) } } @@ -1145,6 +1156,7 @@ pub(crate) fn check_docstring( // DOC101 if checker.enabled(Rule::DocstringMissingParameter) { + let mut missing_parameters = Vec::new(); for signature_param in &signature_parameters { if !docstring_sections .parameters @@ -1156,15 +1168,23 @@ pub(crate) fn check_docstring( .any(|param| *param == signature_param.name) }) { - let diagnostic = Diagnostic::new( - DocstringMissingParameter { - id: (*signature_param.name).to_string(), - }, - signature_param.range(), - ); - diagnostics.push(diagnostic); + missing_parameters.push((*signature_param.name).to_string()); } } + if !missing_parameters.is_empty() { + let range = if let Some(ref docstring_params) = docstring_sections.parameters { + docstring_params.range() + } else { + docstring.range() + }; + let diagnostic = Diagnostic::new( + DocstringMissingParameter { + ids: missing_parameters, + }, + range, + ); + diagnostics.push(diagnostic); + } } // DOC201 diff --git a/crates/ruff_linter/src/rules/pydoclint/snapshots/ruff_linter__rules__pydoclint__tests__docstring-missing-parameter_DOC101_google.py.snap b/crates/ruff_linter/src/rules/pydoclint/snapshots/ruff_linter__rules__pydoclint__tests__docstring-missing-parameter_DOC101_google.py.snap index b444c7d365e68..3316053e997af 100644 --- a/crates/ruff_linter/src/rules/pydoclint/snapshots/ruff_linter__rules__pydoclint__tests__docstring-missing-parameter_DOC101_google.py.snap +++ b/crates/ruff_linter/src/rules/pydoclint/snapshots/ruff_linter__rules__pydoclint__tests__docstring-missing-parameter_DOC101_google.py.snap @@ -1,164 +1,182 @@ --- source: crates/ruff_linter/src/rules/pydoclint/mod.rs --- -DOC101_google.py:199:20: DOC101 Parameter `b` missing from docstring - | -198 | # DOC101 -199 | def add_numbers(a, b): - | ^ DOC101 -200 | """ -201 | Adds two numbers and returns the result. +DOC101_google.py:203:1: DOC101 Parameter `b` missing from the docstring + | +201 | Adds two numbers and returns the result. +202 | +203 | / Args: +204 | | a (int): The first number to add. +205 | | +206 | | Returns: + | |_^ DOC101 +207 | int: The sum of the two numbers. +208 | """ | = help: Add `b` to the docstring -DOC101_google.py:213:33: DOC101 Parameter `multiplier` missing from docstring - | -212 | # DOC101 -213 | def multiply_list_elements(lst, multiplier): - | ^^^^^^^^^^ DOC101 -214 | """ -215 | Multiplies each element in a list by a given multiplier. +DOC101_google.py:217:1: DOC101 Parameter `multiplier` missing from the docstring + | +215 | Multiplies each element in a list by a given multiplier. +216 | +217 | / Args: +218 | | lst (list of int): A list of integers. +219 | | +220 | | Returns: + | |_^ DOC101 +221 | list of int: A new list with each element multiplied. +222 | """ | = help: Add `multiplier` to the docstring -DOC101_google.py:227:20: DOC101 Parameter `numbers` missing from docstring - | -226 | # DOC101 -227 | def find_max_value(numbers): - | ^^^^^^^ DOC101 -228 | """ -229 | Finds the maximum value in a list of numbers. +DOC101_google.py:228:5: DOC101 Parameter `numbers` missing from the docstring + | +226 | # DOC101 +227 | def find_max_value(numbers): +228 | """ + | _____^ +229 | | Finds the maximum value in a list of numbers. +230 | | +231 | | Returns: +232 | | int: The maximum value found in the list. +233 | | """ + | |_______^ DOC101 +234 | return max(numbers) | = help: Add `numbers` to the docstring -DOC101_google.py:238:25: DOC101 Parameter `name` missing from docstring - | -237 | # DOC101 -238 | def create_user_profile(name, age, email, location="here"): - | ^^^^ DOC101 -239 | """ -240 | Creates a user profile with basic information. - | - = help: Add `name` to the docstring - -DOC101_google.py:238:31: DOC101 Parameter `age` missing from docstring - | -237 | # DOC101 -238 | def create_user_profile(name, age, email, location="here"): - | ^^^ DOC101 -239 | """ -240 | Creates a user profile with basic information. - | - = help: Add `age` to the docstring +DOC101_google.py:242:1: DOC101 These parameters are missing from the docstring: `name`, `age` + | +240 | Creates a user profile with basic information. +241 | +242 | / Args: +243 | | email (str): The user's email address. +244 | | location (str): The location of the user. +245 | | +246 | | Returns: + | |_^ DOC101 +247 | dict: A dictionary containing the user's profile. +248 | """ + | + = help: Add `name`, `age` to the docstring -DOC101_google.py:258:40: DOC101 Parameter `tax_rate` missing from docstring - | -257 | # DOC101 -258 | def calculate_total_price(item_prices, tax_rate, discount): - | ^^^^^^^^ DOC101 -259 | """ -260 | Calculates the total price after applying tax and a discount. - | - = help: Add `tax_rate` to the docstring +DOC101_google.py:262:1: DOC101 These parameters are missing from the docstring: `tax_rate`, `discount` + | +260 | Calculates the total price after applying tax and a discount. +261 | +262 | / Args: +263 | | item_prices (list of float): A list of prices for each item. +264 | | +265 | | Returns: + | |_^ DOC101 +266 | float: The final total price after tax and discount. +267 | """ + | + = help: Add `tax_rate`, `discount` to the docstring -DOC101_google.py:258:50: DOC101 Parameter `discount` missing from docstring - | -257 | # DOC101 -258 | def calculate_total_price(item_prices, tax_rate, discount): - | ^^^^^^^^ DOC101 -259 | """ -260 | Calculates the total price after applying tax and a discount. - | - = help: Add `discount` to the docstring +DOC101_google.py:279:1: DOC101 These parameters are missing from the docstring: `cc_address`, `bcc_address` + | +277 | Sends an email to the specified recipients. +278 | +279 | / Args: +280 | | subject (str): The subject of the email. +281 | | body (str): The content of the email. +282 | | to_address (str): The recipient's email address. +283 | | +284 | | Returns: + | |_^ DOC101 +285 | bool: True if the email was sent successfully, False otherwise. +286 | """ + | + = help: Add `cc_address`, `bcc_address` to the docstring -DOC101_google.py:275:43: DOC101 Parameter `cc_address` missing from docstring - | -274 | # DOC101 -275 | def send_email(subject, body, to_address, cc_address=None, bcc_address=None): - | ^^^^^^^^^^^^^^^ DOC101 -276 | """ -277 | Sends an email to the specified recipients. - | - = help: Add `cc_address` to the docstring - -DOC101_google.py:275:60: DOC101 Parameter `bcc_address` missing from docstring - | -274 | # DOC101 -275 | def send_email(subject, body, to_address, cc_address=None, bcc_address=None): - | ^^^^^^^^^^^^^^^^ DOC101 -276 | """ -277 | Sends an email to the specified recipients. - | - = help: Add `bcc_address` to the docstring - -DOC101_google.py:291:36: DOC101 Parameter `args` missing from docstring - | -290 | # DOC101 -291 | def concatenate_strings(separator, *args): - | ^^^^^ DOC101 -292 | """ -293 | Concatenates multiple strings with a specified separator. +DOC101_google.py:295:1: DOC101 Parameter `args` missing from the docstring + | +293 | Concatenates multiple strings with a specified separator. +294 | +295 | / Args: +296 | | separator (str): The separator to use between strings. +297 | | +298 | | Returns: + | |_^ DOC101 +299 | str: A single concatenated string. +300 | """ | = help: Add `args` to the docstring -DOC101_google.py:305:29: DOC101 Parameter `items` missing from docstring - | -304 | # DOC101 -305 | def process_order(order_id, *items, **details): - | ^^^^^^ DOC101 -306 | """ -307 | Processes an order with a list of items and optional order details. - | - = help: Add `items` to the docstring +DOC101_google.py:309:1: DOC101 These parameters are missing from the docstring: `items`, `details` + | +307 | Processes an order with a list of items and optional order details. +308 | +309 | / Args: +310 | | order_id (int): The unique identifier for the order. +311 | | +312 | | Returns: + | |_^ DOC101 +313 | dict: A dictionary containing the order summary. +314 | """ + | + = help: Add `items`, `details` to the docstring -DOC101_google.py:305:37: DOC101 Parameter `details` missing from docstring - | -304 | # DOC101 -305 | def process_order(order_id, *items, **details): - | ^^^^^^^^^ DOC101 -306 | """ -307 | Processes an order with a list of items and optional order details. - | - = help: Add `details` to the docstring - -DOC101_google.py:328:24: DOC101 Parameter `value` missing from docstring - | -327 | # DOC101 -328 | def __init__(self, value=0): - | ^^^^^^^ DOC101 -329 | """ -330 | Initializes the calculator with an initial value. +DOC101_google.py:329:9: DOC101 Parameter `value` missing from the docstring + | +327 | # DOC101 +328 | def __init__(self, value=0): +329 | """ + | _________^ +330 | | Initializes the calculator with an initial value. +331 | | +332 | | Returns: +333 | | None +334 | | """ + | |___________^ DOC101 +335 | self.value = value | = help: Add `value` to the docstring -DOC101_google.py:338:27: DOC101 Parameter `number2` missing from docstring - | -337 | # DOC101 -338 | def add(self, number, number2): - | ^^^^^^^ DOC101 -339 | """ -340 | Adds a number to the current value. +DOC101_google.py:342:1: DOC101 Parameter `number2` missing from the docstring + | +340 | Adds a number to the current value. +341 | +342 | / Args: +343 | | number (int or float): The number to add to the current value. +344 | | +345 | | Returns: + | |_^ DOC101 +346 | int or float: The updated value after addition. +347 | """ | = help: Add `number2` to the docstring -DOC101_google.py:353:26: DOC101 Parameter `value_str` missing from docstring - | -351 | # DOC101 -352 | @classmethod -353 | def from_string(cls, value_str): - | ^^^^^^^^^ DOC101 -354 | """ -355 | Creates a Calculator instance from a string representation of a number. +DOC101_google.py:354:9: DOC101 Parameter `value_str` missing from the docstring + | +352 | @classmethod +353 | def from_string(cls, value_str): +354 | """ + | _________^ +355 | | Creates a Calculator instance from a string representation of a number. +356 | | +357 | | Returns: +358 | | Calculator: A new instance of Calculator initialized with the value from the string. +359 | | """ + | |___________^ DOC101 +360 | value = float(value_str) +361 | return cls(value) | = help: Add `value_str` to the docstring -DOC101_google.py:365:25: DOC101 Parameter `number` missing from docstring - | -363 | # DOC101 -364 | @staticmethod -365 | def is_valid_number(number): - | ^^^^^^ DOC101 -366 | """ -367 | Checks if a given number is valid (int or float). +DOC101_google.py:366:9: DOC101 Parameter `number` missing from the docstring + | +364 | @staticmethod +365 | def is_valid_number(number): +366 | """ + | _________^ +367 | | Checks if a given number is valid (int or float). +368 | | +369 | | Returns: +370 | | bool: True if the number is valid, False otherwise. +371 | | """ + | |___________^ DOC101 +372 | return isinstance(number, (int, float)) | = help: Add `number` to the docstring diff --git a/crates/ruff_linter/src/rules/pydoclint/snapshots/ruff_linter__rules__pydoclint__tests__docstring-missing-parameter_DOC101_numpy.py.snap b/crates/ruff_linter/src/rules/pydoclint/snapshots/ruff_linter__rules__pydoclint__tests__docstring-missing-parameter_DOC101_numpy.py.snap index 158444dc616aa..f7342bd58368d 100644 --- a/crates/ruff_linter/src/rules/pydoclint/snapshots/ruff_linter__rules__pydoclint__tests__docstring-missing-parameter_DOC101_numpy.py.snap +++ b/crates/ruff_linter/src/rules/pydoclint/snapshots/ruff_linter__rules__pydoclint__tests__docstring-missing-parameter_DOC101_numpy.py.snap @@ -1,164 +1,205 @@ --- source: crates/ruff_linter/src/rules/pydoclint/mod.rs --- -DOC101_numpy.py:261:20: DOC101 Parameter `b` missing from docstring - | -260 | # DOC101 -261 | def add_numbers(a, b): - | ^ DOC101 -262 | """ -263 | Adds two numbers and returns the result. +DOC101_numpy.py:265:1: DOC101 Parameter `b` missing from the docstring + | +263 | Adds two numbers and returns the result. +264 | +265 | / Parameters +266 | | ---------- +267 | | a : int +268 | | The first number to add. +269 | | +270 | | Returns + | |_^ DOC101 +271 | ------- +272 | int | = help: Add `b` to the docstring -DOC101_numpy.py:279:33: DOC101 Parameter `multiplier` missing from docstring - | -278 | # DOC101 -279 | def multiply_list_elements(lst, multiplier): - | ^^^^^^^^^^ DOC101 -280 | """ -281 | Multiplies each element in a list by a given multiplier. +DOC101_numpy.py:283:1: DOC101 Parameter `multiplier` missing from the docstring + | +281 | Multiplies each element in a list by a given multiplier. +282 | +283 | / Parameters +284 | | ---------- +285 | | lst : list of int +286 | | A list of integers. +287 | | +288 | | Returns + | |_^ DOC101 +289 | ------- +290 | list of int | = help: Add `multiplier` to the docstring -DOC101_numpy.py:297:20: DOC101 Parameter `numbers` missing from docstring - | -296 | # DOC101 -297 | def find_max_value(numbers): - | ^^^^^^^ DOC101 -298 | """ -299 | Finds the maximum value in a list of numbers. +DOC101_numpy.py:298:5: DOC101 Parameter `numbers` missing from the docstring + | +296 | # DOC101 +297 | def find_max_value(numbers): +298 | """ + | _____^ +299 | | Finds the maximum value in a list of numbers. +300 | | +301 | | Returns +302 | | ------- +303 | | int +304 | | The maximum value found in the list. +305 | | """ + | |_______^ DOC101 +306 | return max(numbers) | = help: Add `numbers` to the docstring -DOC101_numpy.py:310:25: DOC101 Parameter `name` missing from docstring - | -309 | # DOC101 -310 | def create_user_profile(name, age, email, location="here"): - | ^^^^ DOC101 -311 | """ -312 | Creates a user profile with basic information. - | - = help: Add `name` to the docstring - -DOC101_numpy.py:310:31: DOC101 Parameter `age` missing from docstring - | -309 | # DOC101 -310 | def create_user_profile(name, age, email, location="here"): - | ^^^ DOC101 -311 | """ -312 | Creates a user profile with basic information. - | - = help: Add `age` to the docstring +DOC101_numpy.py:314:1: DOC101 These parameters are missing from the docstring: `name`, `age` + | +312 | Creates a user profile with basic information. +313 | +314 | / Parameters +315 | | ---------- +316 | | email : str +317 | | The user's email address. +318 | | location : str, optional +319 | | The location of the user, by default "here". +320 | | +321 | | Returns + | |_^ DOC101 +322 | ------- +323 | dict + | + = help: Add `name`, `age` to the docstring -DOC101_numpy.py:335:40: DOC101 Parameter `tax_rate` missing from docstring - | -334 | # DOC101 -335 | def calculate_total_price(item_prices, tax_rate, discount): - | ^^^^^^^^ DOC101 -336 | """ -337 | Calculates the total price after applying tax and a discount. - | - = help: Add `tax_rate` to the docstring +DOC101_numpy.py:339:1: DOC101 These parameters are missing from the docstring: `tax_rate`, `discount` + | +337 | Calculates the total price after applying tax and a discount. +338 | +339 | / Parameters +340 | | ---------- +341 | | item_prices : list of float +342 | | A list of prices for each item. +343 | | +344 | | Returns + | |_^ DOC101 +345 | ------- +346 | float + | + = help: Add `tax_rate`, `discount` to the docstring -DOC101_numpy.py:335:50: DOC101 Parameter `discount` missing from docstring - | -334 | # DOC101 -335 | def calculate_total_price(item_prices, tax_rate, discount): - | ^^^^^^^^ DOC101 -336 | """ -337 | Calculates the total price after applying tax and a discount. - | - = help: Add `discount` to the docstring +DOC101_numpy.py:360:1: DOC101 These parameters are missing from the docstring: `cc_address`, `bcc_address` + | +358 | Sends an email to the specified recipients. +359 | +360 | / Parameters +361 | | ---------- +362 | | subject : str +363 | | The subject of the email. +364 | | body : str +365 | | The content of the email. +366 | | to_address : str +367 | | The recipient's email address. +368 | | +369 | | Returns + | |_^ DOC101 +370 | ------- +371 | bool + | + = help: Add `cc_address`, `bcc_address` to the docstring -DOC101_numpy.py:356:43: DOC101 Parameter `cc_address` missing from docstring - | -355 | # DOC101 -356 | def send_email(subject, body, to_address, cc_address=None, bcc_address=None): - | ^^^^^^^^^^^^^^^ DOC101 -357 | """ -358 | Sends an email to the specified recipients. - | - = help: Add `cc_address` to the docstring - -DOC101_numpy.py:356:60: DOC101 Parameter `bcc_address` missing from docstring - | -355 | # DOC101 -356 | def send_email(subject, body, to_address, cc_address=None, bcc_address=None): - | ^^^^^^^^^^^^^^^^ DOC101 -357 | """ -358 | Sends an email to the specified recipients. - | - = help: Add `bcc_address` to the docstring - -DOC101_numpy.py:378:36: DOC101 Parameter `args` missing from docstring - | -377 | # DOC101 -378 | def concatenate_strings(separator, *args): - | ^^^^^ DOC101 -379 | """ -380 | Concatenates multiple strings with a specified separator. +DOC101_numpy.py:382:1: DOC101 Parameter `args` missing from the docstring + | +380 | Concatenates multiple strings with a specified separator. +381 | +382 | / Parameters +383 | | ---------- +384 | | separator : str +385 | | The separator to use between strings. +386 | | +387 | | Returns + | |_^ DOC101 +388 | ------- +389 | str | = help: Add `args` to the docstring -DOC101_numpy.py:396:29: DOC101 Parameter `items` missing from docstring - | -395 | # DOC101 -396 | def process_order(order_id, *items, **details): - | ^^^^^^ DOC101 -397 | """ -398 | Processes an order with a list of items and optional order details. - | - = help: Add `items` to the docstring +DOC101_numpy.py:400:1: DOC101 These parameters are missing from the docstring: `items`, `details` + | +398 | Processes an order with a list of items and optional order details. +399 | +400 | / Parameters +401 | | ---------- +402 | | order_id : int +403 | | The unique identifier for the order. +404 | | +405 | | Returns + | |_^ DOC101 +406 | ------- +407 | dict + | + = help: Add `items`, `details` to the docstring -DOC101_numpy.py:396:37: DOC101 Parameter `details` missing from docstring - | -395 | # DOC101 -396 | def process_order(order_id, *items, **details): - | ^^^^^^^^^ DOC101 -397 | """ -398 | Processes an order with a list of items and optional order details. - | - = help: Add `details` to the docstring - -DOC101_numpy.py:423:24: DOC101 Parameter `value` missing from docstring - | -422 | # DOC101 -423 | def __init__(self, value=0): - | ^^^^^^^ DOC101 -424 | """ -425 | Initializes the calculator with an initial value. +DOC101_numpy.py:424:9: DOC101 Parameter `value` missing from the docstring + | +422 | # DOC101 +423 | def __init__(self, value=0): +424 | """ + | _________^ +425 | | Initializes the calculator with an initial value. +426 | | +427 | | """ + | |___________^ DOC101 +428 | self.value = value | = help: Add `value` to the docstring -DOC101_numpy.py:431:27: DOC101 Parameter `number2` missing from docstring - | -430 | # DOC101 -431 | def add(self, number, number2): - | ^^^^^^^ DOC101 -432 | """ -433 | Adds two numbers to the current value. +DOC101_numpy.py:435:1: DOC101 Parameter `number2` missing from the docstring + | +433 | Adds two numbers to the current value. +434 | +435 | / Parameters +436 | | ---------- +437 | | number : int or float +438 | | The first number to add. +439 | | +440 | | Returns + | |_^ DOC101 +441 | ------- +442 | int or float | = help: Add `number2` to the docstring -DOC101_numpy.py:450:26: DOC101 Parameter `value_str` missing from docstring - | -448 | # DOC101 -449 | @classmethod -450 | def from_string(cls, value_str): - | ^^^^^^^^^ DOC101 -451 | """ -452 | Creates a Calculator instance from a string representation of a number. +DOC101_numpy.py:451:9: DOC101 Parameter `value_str` missing from the docstring + | +449 | @classmethod +450 | def from_string(cls, value_str): +451 | """ + | _________^ +452 | | Creates a Calculator instance from a string representation of a number. +453 | | +454 | | Returns +455 | | ------- +456 | | Calculator +457 | | A new instance of Calculator initialized with the value from the string. +458 | | """ + | |___________^ DOC101 +459 | value = float(value_str) +460 | return cls(value) | = help: Add `value_str` to the docstring -DOC101_numpy.py:464:25: DOC101 Parameter `number` missing from docstring - | -462 | # DOC101 -463 | @staticmethod -464 | def is_valid_number(number): - | ^^^^^^ DOC101 -465 | """ -466 | Checks if a given number is valid (int or float). +DOC101_numpy.py:465:9: DOC101 Parameter `number` missing from the docstring + | +463 | @staticmethod +464 | def is_valid_number(number): +465 | """ + | _________^ +466 | | Checks if a given number is valid (int or float). +467 | | +468 | | Returns +469 | | ------- +470 | | bool +471 | | True if the number is valid, False otherwise. +472 | | """ + | |___________^ DOC101 +473 | return isinstance(number, (int, float)) | = help: Add `number` to the docstring From 7c60ddd03280b888589a049bb0d981bcf2443cc8 Mon Sep 17 00:00:00 2001 From: augustelalande Date: Tue, 10 Sep 2024 16:49:39 -0400 Subject: [PATCH 07/18] simplify fix message --- .../rules/pydoclint/rules/check_docstring.rs | 10 ++++---- ...extraneous-parameter_DOC102_google.py.snap | 24 +++++++++---------- ...-extraneous-parameter_DOC102_numpy.py.snap | 24 +++++++++---------- ...ng-missing-parameter_DOC101_google.py.snap | 24 +++++++++---------- ...ing-missing-parameter_DOC101_numpy.py.snap | 24 +++++++++---------- 5 files changed, 52 insertions(+), 54 deletions(-) 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 4124bd7daf185..9752cacbbc39f 100644 --- a/crates/ruff_linter/src/rules/pydoclint/rules/check_docstring.rs +++ b/crates/ruff_linter/src/rules/pydoclint/rules/check_docstring.rs @@ -80,10 +80,8 @@ impl Violation for DocstringMissingParameter { fn fix_title(&self) -> Option { let DocstringMissingParameter { ids } = self; - Some(format!( - "Add {} to the docstring", - ids.iter().map(|id| format!("`{id}`")).join(", ") - )) + let s = if ids.len() == 1 { "" } else { "s" }; + Some(format!("Add the missing parameter{s} to the docstring")) } } @@ -148,9 +146,9 @@ impl Violation for DocstringExtraneousParameter { fn fix_title(&self) -> Option { let DocstringExtraneousParameter { ids } = self; + let s = if ids.len() == 1 { "" } else { "s" }; Some(format!( - "Remove {} from the docstring", - ids.iter().map(|id| format!("`{id}`")).join(", ") + "Remove the extraneous parameter{s} from the docstring" )) } } diff --git a/crates/ruff_linter/src/rules/pydoclint/snapshots/ruff_linter__rules__pydoclint__tests__docstring-extraneous-parameter_DOC102_google.py.snap b/crates/ruff_linter/src/rules/pydoclint/snapshots/ruff_linter__rules__pydoclint__tests__docstring-extraneous-parameter_DOC102_google.py.snap index c9634a402bfb0..921c8fc5359f6 100644 --- a/crates/ruff_linter/src/rules/pydoclint/snapshots/ruff_linter__rules__pydoclint__tests__docstring-extraneous-parameter_DOC102_google.py.snap +++ b/crates/ruff_linter/src/rules/pydoclint/snapshots/ruff_linter__rules__pydoclint__tests__docstring-extraneous-parameter_DOC102_google.py.snap @@ -14,7 +14,7 @@ DOC102_google.py:6:1: DOC102 `a` is not in the function's signature 11 | int: The sum of the two numbers. 12 | """ | - = help: Remove `a` from the docstring + = help: Remove the extraneous parameter from the docstring DOC102_google.py:21:1: DOC102 `multiplier` is not in the function's signature | @@ -29,7 +29,7 @@ DOC102_google.py:21:1: DOC102 `multiplier` is not in the function's signature 26 | list of int: A new list with each element multiplied. 27 | """ | - = help: Remove `multiplier` from the docstring + = help: Remove the extraneous parameter from the docstring DOC102_google.py:36:1: DOC102 `numbers` is not in the function's signature | @@ -43,7 +43,7 @@ DOC102_google.py:36:1: DOC102 `numbers` is not in the function's signature 40 | int: The maximum value found in the list. 41 | """ | - = help: Remove `numbers` from the docstring + = help: Remove the extraneous parameter from the docstring DOC102_google.py:50:1: DOC102 These parameters are not in the function's signature: `name`, `age`, `email` | @@ -60,7 +60,7 @@ DOC102_google.py:50:1: DOC102 These parameters are not in the function's signatu 57 | dict: A dictionary containing the user's profile. 58 | """ | - = help: Remove `name`, `age`, `email` from the docstring + = help: Remove the extraneous parameters from the docstring DOC102_google.py:72:1: DOC102 `tax_rate` is not in the function's signature | @@ -76,7 +76,7 @@ DOC102_google.py:72:1: DOC102 `tax_rate` is not in the function's signature 78 | float: The final total price after tax and discount. 79 | """ | - = help: Remove `tax_rate` from the docstring + = help: Remove the extraneous parameter from the docstring DOC102_google.py:91:1: DOC102 These parameters are not in the function's signature: `to_address`, `cc_address` | @@ -94,7 +94,7 @@ DOC102_google.py:91:1: DOC102 These parameters are not in the function's signatu 99 | bool: True if the email was sent successfully, False otherwise. 100 | """ | - = help: Remove `to_address`, `cc_address` from the docstring + = help: Remove the extraneous parameters from the docstring DOC102_google.py:109:1: DOC102 `separator` is not in the function's signature | @@ -109,7 +109,7 @@ DOC102_google.py:109:1: DOC102 `separator` is not in the function's signature 114 | str: A single concatenated string. 115 | """ | - = help: Remove `separator` from the docstring + = help: Remove the extraneous parameter from the docstring DOC102_google.py:124:1: DOC102 These parameters are not in the function's signature: `items`, `details` | @@ -125,7 +125,7 @@ DOC102_google.py:124:1: DOC102 These parameters are not in the function's signat 130 | dict: A dictionary containing the order summary. 131 | """ | - = help: Remove `items`, `details` from the docstring + = help: Remove the extraneous parameters from the docstring DOC102_google.py:149:1: DOC102 `value` is not in the function's signature | @@ -137,7 +137,7 @@ DOC102_google.py:149:1: DOC102 `value` is not in the function's signature | |________^ DOC102 152 | self.value = value | - = help: Remove `value` from the docstring + = help: Remove the extraneous parameter from the docstring DOC102_google.py:159:1: DOC102 `number` is not in the function's signature | @@ -151,7 +151,7 @@ DOC102_google.py:159:1: DOC102 `number` is not in the function's signature 163 | int or float: The updated value after addition. 164 | """ | - = help: Remove `number` from the docstring + = help: Remove the extraneous parameter from the docstring DOC102_google.py:174:1: DOC102 `value_str` is not in the function's signature | @@ -165,7 +165,7 @@ DOC102_google.py:174:1: DOC102 `value_str` is not in the function's signature 178 | Calculator: A new instance of Calculator initialized with the value from the string. 179 | """ | - = help: Remove `value_str` from the docstring + = help: Remove the extraneous parameter from the docstring DOC102_google.py:189:1: DOC102 `number` is not in the function's signature | @@ -179,4 +179,4 @@ DOC102_google.py:189:1: DOC102 `number` is not in the function's signature 193 | bool: True if the number is valid, False otherwise. 194 | """ | - = help: Remove `number` from the docstring + = help: Remove the extraneous parameter from the docstring diff --git a/crates/ruff_linter/src/rules/pydoclint/snapshots/ruff_linter__rules__pydoclint__tests__docstring-extraneous-parameter_DOC102_numpy.py.snap b/crates/ruff_linter/src/rules/pydoclint/snapshots/ruff_linter__rules__pydoclint__tests__docstring-extraneous-parameter_DOC102_numpy.py.snap index 24c477635d7ac..1b3a0fe9b8c20 100644 --- a/crates/ruff_linter/src/rules/pydoclint/snapshots/ruff_linter__rules__pydoclint__tests__docstring-extraneous-parameter_DOC102_numpy.py.snap +++ b/crates/ruff_linter/src/rules/pydoclint/snapshots/ruff_linter__rules__pydoclint__tests__docstring-extraneous-parameter_DOC102_numpy.py.snap @@ -17,7 +17,7 @@ DOC102_numpy.py:6:1: DOC102 `a` is not in the function's signature 14 | ------- 15 | int | - = help: Remove `a` from the docstring + = help: Remove the extraneous parameter from the docstring DOC102_numpy.py:26:1: DOC102 `multiplier` is not in the function's signature | @@ -35,7 +35,7 @@ DOC102_numpy.py:26:1: DOC102 `multiplier` is not in the function's signature 34 | ------- 35 | list of int | - = help: Remove `multiplier` from the docstring + = help: Remove the extraneous parameter from the docstring DOC102_numpy.py:46:1: DOC102 `numbers` is not in the function's signature | @@ -51,7 +51,7 @@ DOC102_numpy.py:46:1: DOC102 `numbers` is not in the function's signature 52 | ------- 53 | int | - = help: Remove `numbers` from the docstring + = help: Remove the extraneous parameter from the docstring DOC102_numpy.py:64:1: DOC102 These parameters are not in the function's signature: `name`, `age`, `email` | @@ -73,7 +73,7 @@ DOC102_numpy.py:64:1: DOC102 These parameters are not in the function's signatur 76 | ------- 77 | dict | - = help: Remove `name`, `age`, `email` from the docstring + = help: Remove the extraneous parameters from the docstring DOC102_numpy.py:93:1: DOC102 `tax_rate` is not in the function's signature | @@ -93,7 +93,7 @@ DOC102_numpy.py:93:1: DOC102 `tax_rate` is not in the function's signature 103 | ------- 104 | float | - = help: Remove `tax_rate` from the docstring + = help: Remove the extraneous parameter from the docstring DOC102_numpy.py:118:1: DOC102 These parameters are not in the function's signature: `to_address`, `cc_address` | @@ -117,7 +117,7 @@ DOC102_numpy.py:118:1: DOC102 These parameters are not in the function's signatu 132 | ------- 133 | bool | - = help: Remove `to_address`, `cc_address` from the docstring + = help: Remove the extraneous parameters from the docstring DOC102_numpy.py:144:1: DOC102 `separator` is not in the function's signature | @@ -135,7 +135,7 @@ DOC102_numpy.py:144:1: DOC102 `separator` is not in the function's signature 152 | ------- 153 | str | - = help: Remove `separator` from the docstring + = help: Remove the extraneous parameter from the docstring DOC102_numpy.py:164:1: DOC102 These parameters are not in the function's signature: `items`, `details` | @@ -155,7 +155,7 @@ DOC102_numpy.py:164:1: DOC102 These parameters are not in the function's signatu 174 | ------- 175 | dict | - = help: Remove `items`, `details` from the docstring + = help: Remove the extraneous parameters from the docstring DOC102_numpy.py:195:1: DOC102 `value` is not in the function's signature | @@ -169,7 +169,7 @@ DOC102_numpy.py:195:1: DOC102 `value` is not in the function's signature | |________^ DOC102 200 | self.value = value | - = help: Remove `value` from the docstring + = help: Remove the extraneous parameter from the docstring DOC102_numpy.py:207:1: DOC102 `number` is not in the function's signature | @@ -187,7 +187,7 @@ DOC102_numpy.py:207:1: DOC102 `number` is not in the function's signature 215 | ------- 216 | int or float | - = help: Remove `number` from the docstring + = help: Remove the extraneous parameter from the docstring DOC102_numpy.py:228:1: DOC102 `value_str` is not in the function's signature | @@ -203,7 +203,7 @@ DOC102_numpy.py:228:1: DOC102 `value_str` is not in the function's signature 234 | ------- 235 | Calculator | - = help: Remove `value_str` from the docstring + = help: Remove the extraneous parameter from the docstring DOC102_numpy.py:247:1: DOC102 `number` is not in the function's signature | @@ -219,4 +219,4 @@ DOC102_numpy.py:247:1: DOC102 `number` is not in the function's signature 253 | ------- 254 | bool | - = help: Remove `number` from the docstring + = help: Remove the extraneous parameter from the docstring diff --git a/crates/ruff_linter/src/rules/pydoclint/snapshots/ruff_linter__rules__pydoclint__tests__docstring-missing-parameter_DOC101_google.py.snap b/crates/ruff_linter/src/rules/pydoclint/snapshots/ruff_linter__rules__pydoclint__tests__docstring-missing-parameter_DOC101_google.py.snap index 3316053e997af..63769d07bacaf 100644 --- a/crates/ruff_linter/src/rules/pydoclint/snapshots/ruff_linter__rules__pydoclint__tests__docstring-missing-parameter_DOC101_google.py.snap +++ b/crates/ruff_linter/src/rules/pydoclint/snapshots/ruff_linter__rules__pydoclint__tests__docstring-missing-parameter_DOC101_google.py.snap @@ -13,7 +13,7 @@ DOC101_google.py:203:1: DOC101 Parameter `b` missing from the docstring 207 | int: The sum of the two numbers. 208 | """ | - = help: Add `b` to the docstring + = help: Add the missing parameter to the docstring DOC101_google.py:217:1: DOC101 Parameter `multiplier` missing from the docstring | @@ -27,7 +27,7 @@ DOC101_google.py:217:1: DOC101 Parameter `multiplier` missing from the docstring 221 | list of int: A new list with each element multiplied. 222 | """ | - = help: Add `multiplier` to the docstring + = help: Add the missing parameter to the docstring DOC101_google.py:228:5: DOC101 Parameter `numbers` missing from the docstring | @@ -43,7 +43,7 @@ DOC101_google.py:228:5: DOC101 Parameter `numbers` missing from the docstring | |_______^ DOC101 234 | return max(numbers) | - = help: Add `numbers` to the docstring + = help: Add the missing parameter to the docstring DOC101_google.py:242:1: DOC101 These parameters are missing from the docstring: `name`, `age` | @@ -58,7 +58,7 @@ DOC101_google.py:242:1: DOC101 These parameters are missing from the docstring: 247 | dict: A dictionary containing the user's profile. 248 | """ | - = help: Add `name`, `age` to the docstring + = help: Add the missing parameters to the docstring DOC101_google.py:262:1: DOC101 These parameters are missing from the docstring: `tax_rate`, `discount` | @@ -72,7 +72,7 @@ DOC101_google.py:262:1: DOC101 These parameters are missing from the docstring: 266 | float: The final total price after tax and discount. 267 | """ | - = help: Add `tax_rate`, `discount` to the docstring + = help: Add the missing parameters to the docstring DOC101_google.py:279:1: DOC101 These parameters are missing from the docstring: `cc_address`, `bcc_address` | @@ -88,7 +88,7 @@ DOC101_google.py:279:1: DOC101 These parameters are missing from the docstring: 285 | bool: True if the email was sent successfully, False otherwise. 286 | """ | - = help: Add `cc_address`, `bcc_address` to the docstring + = help: Add the missing parameters to the docstring DOC101_google.py:295:1: DOC101 Parameter `args` missing from the docstring | @@ -102,7 +102,7 @@ DOC101_google.py:295:1: DOC101 Parameter `args` missing from the docstring 299 | str: A single concatenated string. 300 | """ | - = help: Add `args` to the docstring + = help: Add the missing parameter to the docstring DOC101_google.py:309:1: DOC101 These parameters are missing from the docstring: `items`, `details` | @@ -116,7 +116,7 @@ DOC101_google.py:309:1: DOC101 These parameters are missing from the docstring: 313 | dict: A dictionary containing the order summary. 314 | """ | - = help: Add `items`, `details` to the docstring + = help: Add the missing parameters to the docstring DOC101_google.py:329:9: DOC101 Parameter `value` missing from the docstring | @@ -132,7 +132,7 @@ DOC101_google.py:329:9: DOC101 Parameter `value` missing from the docstring | |___________^ DOC101 335 | self.value = value | - = help: Add `value` to the docstring + = help: Add the missing parameter to the docstring DOC101_google.py:342:1: DOC101 Parameter `number2` missing from the docstring | @@ -146,7 +146,7 @@ DOC101_google.py:342:1: DOC101 Parameter `number2` missing from the docstring 346 | int or float: The updated value after addition. 347 | """ | - = help: Add `number2` to the docstring + = help: Add the missing parameter to the docstring DOC101_google.py:354:9: DOC101 Parameter `value_str` missing from the docstring | @@ -163,7 +163,7 @@ DOC101_google.py:354:9: DOC101 Parameter `value_str` missing from the docstring 360 | value = float(value_str) 361 | return cls(value) | - = help: Add `value_str` to the docstring + = help: Add the missing parameter to the docstring DOC101_google.py:366:9: DOC101 Parameter `number` missing from the docstring | @@ -179,4 +179,4 @@ DOC101_google.py:366:9: DOC101 Parameter `number` missing from the docstring | |___________^ DOC101 372 | return isinstance(number, (int, float)) | - = help: Add `number` to the docstring + = help: Add the missing parameter to the docstring diff --git a/crates/ruff_linter/src/rules/pydoclint/snapshots/ruff_linter__rules__pydoclint__tests__docstring-missing-parameter_DOC101_numpy.py.snap b/crates/ruff_linter/src/rules/pydoclint/snapshots/ruff_linter__rules__pydoclint__tests__docstring-missing-parameter_DOC101_numpy.py.snap index f7342bd58368d..965d65258d76f 100644 --- a/crates/ruff_linter/src/rules/pydoclint/snapshots/ruff_linter__rules__pydoclint__tests__docstring-missing-parameter_DOC101_numpy.py.snap +++ b/crates/ruff_linter/src/rules/pydoclint/snapshots/ruff_linter__rules__pydoclint__tests__docstring-missing-parameter_DOC101_numpy.py.snap @@ -15,7 +15,7 @@ DOC101_numpy.py:265:1: DOC101 Parameter `b` missing from the docstring 271 | ------- 272 | int | - = help: Add `b` to the docstring + = help: Add the missing parameter to the docstring DOC101_numpy.py:283:1: DOC101 Parameter `multiplier` missing from the docstring | @@ -31,7 +31,7 @@ DOC101_numpy.py:283:1: DOC101 Parameter `multiplier` missing from the docstring 289 | ------- 290 | list of int | - = help: Add `multiplier` to the docstring + = help: Add the missing parameter to the docstring DOC101_numpy.py:298:5: DOC101 Parameter `numbers` missing from the docstring | @@ -49,7 +49,7 @@ DOC101_numpy.py:298:5: DOC101 Parameter `numbers` missing from the docstring | |_______^ DOC101 306 | return max(numbers) | - = help: Add `numbers` to the docstring + = help: Add the missing parameter to the docstring DOC101_numpy.py:314:1: DOC101 These parameters are missing from the docstring: `name`, `age` | @@ -67,7 +67,7 @@ DOC101_numpy.py:314:1: DOC101 These parameters are missing from the docstring: ` 322 | ------- 323 | dict | - = help: Add `name`, `age` to the docstring + = help: Add the missing parameters to the docstring DOC101_numpy.py:339:1: DOC101 These parameters are missing from the docstring: `tax_rate`, `discount` | @@ -83,7 +83,7 @@ DOC101_numpy.py:339:1: DOC101 These parameters are missing from the docstring: ` 345 | ------- 346 | float | - = help: Add `tax_rate`, `discount` to the docstring + = help: Add the missing parameters to the docstring DOC101_numpy.py:360:1: DOC101 These parameters are missing from the docstring: `cc_address`, `bcc_address` | @@ -103,7 +103,7 @@ DOC101_numpy.py:360:1: DOC101 These parameters are missing from the docstring: ` 370 | ------- 371 | bool | - = help: Add `cc_address`, `bcc_address` to the docstring + = help: Add the missing parameters to the docstring DOC101_numpy.py:382:1: DOC101 Parameter `args` missing from the docstring | @@ -119,7 +119,7 @@ DOC101_numpy.py:382:1: DOC101 Parameter `args` missing from the docstring 388 | ------- 389 | str | - = help: Add `args` to the docstring + = help: Add the missing parameter to the docstring DOC101_numpy.py:400:1: DOC101 These parameters are missing from the docstring: `items`, `details` | @@ -135,7 +135,7 @@ DOC101_numpy.py:400:1: DOC101 These parameters are missing from the docstring: ` 406 | ------- 407 | dict | - = help: Add `items`, `details` to the docstring + = help: Add the missing parameters to the docstring DOC101_numpy.py:424:9: DOC101 Parameter `value` missing from the docstring | @@ -149,7 +149,7 @@ DOC101_numpy.py:424:9: DOC101 Parameter `value` missing from the docstring | |___________^ DOC101 428 | self.value = value | - = help: Add `value` to the docstring + = help: Add the missing parameter to the docstring DOC101_numpy.py:435:1: DOC101 Parameter `number2` missing from the docstring | @@ -165,7 +165,7 @@ DOC101_numpy.py:435:1: DOC101 Parameter `number2` missing from the docstring 441 | ------- 442 | int or float | - = help: Add `number2` to the docstring + = help: Add the missing parameter to the docstring DOC101_numpy.py:451:9: DOC101 Parameter `value_str` missing from the docstring | @@ -184,7 +184,7 @@ DOC101_numpy.py:451:9: DOC101 Parameter `value_str` missing from the docstring 459 | value = float(value_str) 460 | return cls(value) | - = help: Add `value_str` to the docstring + = help: Add the missing parameter to the docstring DOC101_numpy.py:465:9: DOC101 Parameter `number` missing from the docstring | @@ -202,4 +202,4 @@ DOC101_numpy.py:465:9: DOC101 Parameter `number` missing from the docstring | |___________^ DOC101 473 | return isinstance(number, (int, float)) | - = help: Add `number` to the docstring + = help: Add the missing parameter to the docstring From dcc0065009c60b23e84f4d31e66ce24a49cba50c Mon Sep 17 00:00:00 2001 From: augustelalande Date: Tue, 10 Sep 2024 17:11:38 -0400 Subject: [PATCH 08/18] better extraction of google params --- .../test/fixtures/pydoclint/DOC102_google.py | 13 +++++++++++ .../rules/pydoclint/rules/check_docstring.rs | 23 ++++++++++++++++--- 2 files changed, 33 insertions(+), 3 deletions(-) diff --git a/crates/ruff_linter/resources/test/fixtures/pydoclint/DOC102_google.py b/crates/ruff_linter/resources/test/fixtures/pydoclint/DOC102_google.py index 62b6583e5d260..ca761efbf3428 100644 --- a/crates/ruff_linter/resources/test/fixtures/pydoclint/DOC102_google.py +++ b/crates/ruff_linter/resources/test/fixtures/pydoclint/DOC102_google.py @@ -193,3 +193,16 @@ def is_valid_number(): bool: True if the number is valid, False otherwise. """ return isinstance(number, (int, float)) + +# OK +def foo(param1, param2, *args, **kwargs): + """Foo. + + Args: + param1 (int): The first parameter. + param2 (:obj:`str`, optional): The second parameter. Defaults to None. + Second line of description: should be indented. + *args: Variable length argument list. + **kwargs: Arbitrary keyword arguments. + """ + return 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 9752cacbbc39f..90ca2c1773524 100644 --- a/crates/ruff_linter/src/rules/pydoclint/rules/check_docstring.rs +++ b/crates/ruff_linter/src/rules/pydoclint/rules/check_docstring.rs @@ -622,12 +622,29 @@ fn parse_parameters(content: &str, style: Option) -> Vec<&str> { /// ``` fn parse_parameters_google(content: &str) -> Vec<&str> { let mut entries: Vec<&str> = Vec::new(); + // Find first entry to determine indentation + let mut indentation = None; for potential in content.lines() { - let Some(colon_idx) = potential.find(':') else { + if potential.find(':').is_none() { continue; }; - if let Some(param) = potential[..colon_idx].split_whitespace().next() { - entries.push(param.trim_start_matches('*')); + indentation = Some(&potential[..potential.len() - potential.trim_start().len()]); + } + let Some(indentation) = indentation else { + return entries; + }; + for potential in content.lines() { + if let Some(entry) = potential.strip_prefix(indentation) { + if let Some(first_char) = entry.chars().next() { + if !first_char.is_whitespace() { + let Some(colon_idx) = entry.find(':') else { + continue; + }; + if let Some(param) = entry[..colon_idx].split_whitespace().next() { + entries.push(param.trim_start_matches('*')); + } + } + } } } entries From 352c8157c3a167708b3704ef96e198eb87b3e335 Mon Sep 17 00:00:00 2001 From: augustelalande Date: Tue, 10 Sep 2024 17:14:32 -0400 Subject: [PATCH 09/18] add numpy test without types --- .../test/fixtures/pydoclint/DOC101_numpy.py | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/crates/ruff_linter/resources/test/fixtures/pydoclint/DOC101_numpy.py b/crates/ruff_linter/resources/test/fixtures/pydoclint/DOC101_numpy.py index ce0c9113652bc..4f47f905a6a3e 100644 --- a/crates/ruff_linter/resources/test/fixtures/pydoclint/DOC101_numpy.py +++ b/crates/ruff_linter/resources/test/fixtures/pydoclint/DOC101_numpy.py @@ -471,3 +471,25 @@ def is_valid_number(number): True if the number is valid, False otherwise. """ return isinstance(number, (int, float)) + +# OK +def function_with_pep484_type_annotations(param1: int, param2: str) -> bool: + """Example function with PEP 484 type annotations. + + The return type must be duplicated in the docstring to comply + with the NumPy docstring style. + + Parameters + ---------- + param1 + The first parameter. + param2 + The second parameter. + + Returns + ------- + bool + True if successful, False otherwise. + + """ + return False From 6c8cc9993dd5884feae04a27a0c75d5f1d8b933e Mon Sep 17 00:00:00 2001 From: augustelalande Date: Tue, 10 Sep 2024 17:21:18 -0400 Subject: [PATCH 10/18] simply signature parameter extraction --- .../rules/pydoclint/rules/check_docstring.rs | 45 +++---------------- 1 file changed, 5 insertions(+), 40 deletions(-) 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 90ca2c1773524..8806223d8fdeb 100644 --- a/crates/ruff_linter/src/rules/pydoclint/rules/check_docstring.rs +++ b/crates/ruff_linter/src/rules/pydoclint/rules/check_docstring.rs @@ -7,7 +7,6 @@ use ruff_python_ast::helpers::map_subscript; 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_ast::{AnyParameterRef, Parameter, ParameterWithDefault}; use ruff_python_semantic::analyze::visibility::is_staticmethod; use ruff_python_semantic::analyze::{function_type, visibility}; use ruff_python_semantic::{Definition, SemanticModel}; @@ -1078,23 +1077,10 @@ fn is_generator_function_annotated_as_returning_none( .is_some_and(GeneratorOrIteratorArguments::indicates_none_returned) } -/// An individual parameter from a function's signature. -#[derive(Debug)] -struct SignatureParameter<'a> { - name: &'a str, - range: TextRange, -} - -impl Ranged for SignatureParameter<'_> { - fn range(&self) -> TextRange { - self.range - } -} - fn parameters_from_signature<'a>( docstring: &'a Docstring, semantic: &'a SemanticModel, -) -> Vec> { +) -> Vec<&'a str> { let mut parameters = Vec::new(); let Some(function) = docstring.definition.as_function_def() else { return parameters; @@ -1102,28 +1088,7 @@ fn parameters_from_signature<'a>( for param in function.parameters.iter().skip(usize::from( docstring.definition.is_method() && !is_staticmethod(&function.decorator_list, semantic), )) { - match param { - AnyParameterRef::Variadic(Parameter { name, range, .. }) => { - let name = name.as_str(); - if !name.starts_with('_') { - parameters.push(SignatureParameter { - name, - range: *range, - }); - } - } - AnyParameterRef::NonVariadic(ParameterWithDefault { - parameter, range, .. - }) => { - let name = parameter.name.as_str(); - if !name.starts_with('_') { - parameters.push(SignatureParameter { - name, - range: *range, - }); - } - } - } + parameters.push(param.name()); } parameters } @@ -1180,10 +1145,10 @@ pub(crate) fn check_docstring( section .parameters .iter() - .any(|param| *param == signature_param.name) + .any(|param| param == signature_param) }) { - missing_parameters.push((*signature_param.name).to_string()); + missing_parameters.push((*signature_param).to_string()); } } if !missing_parameters.is_empty() { @@ -1306,7 +1271,7 @@ pub(crate) fn check_docstring( for docstring_param in &docstring_params.parameters { if !signature_parameters .iter() - .any(|param| param.name == *docstring_param) + .any(|param| param == docstring_param) { extraneous_parameters.push((*docstring_param).to_string()); } From 65b9b5b41d1a024546a4cd2ee9556af3dee76615 Mon Sep 17 00:00:00 2001 From: augustelalande Date: Tue, 10 Sep 2024 17:29:05 -0400 Subject: [PATCH 11/18] use dummy_variable_rgx --- .../src/rules/pydoclint/rules/check_docstring.rs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) 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 8806223d8fdeb..1f2166aaf5620 100644 --- a/crates/ruff_linter/src/rules/pydoclint/rules/check_docstring.rs +++ b/crates/ruff_linter/src/rules/pydoclint/rules/check_docstring.rs @@ -1,4 +1,5 @@ use itertools::Itertools; +use regex::Regex; use ruff_diagnostics::Diagnostic; use ruff_diagnostics::Violation; use ruff_macros::{derive_message_formats, violation}; @@ -1080,6 +1081,7 @@ fn is_generator_function_annotated_as_returning_none( fn parameters_from_signature<'a>( docstring: &'a Docstring, semantic: &'a SemanticModel, + dummy_variable_rgx: &'a Regex, ) -> Vec<&'a str> { let mut parameters = Vec::new(); let Some(function) = docstring.definition.as_function_def() else { @@ -1088,7 +1090,9 @@ fn parameters_from_signature<'a>( for param in function.parameters.iter().skip(usize::from( docstring.definition.is_method() && !is_staticmethod(&function.decorator_list, semantic), )) { - parameters.push(param.name()); + if !dummy_variable_rgx.is_match(param.name()) { + parameters.push(param.name()); + } } parameters } @@ -1132,7 +1136,8 @@ pub(crate) fn check_docstring( visitor.finish() }; - let signature_parameters = parameters_from_signature(docstring, semantic); + let signature_parameters = + parameters_from_signature(docstring, semantic, &checker.settings.dummy_variable_rgx); // DOC101 if checker.enabled(Rule::DocstringMissingParameter) { From 993deb8ded3097fa5ccd86d031470298ecca5e5c Mon Sep 17 00:00:00 2001 From: augustelalande Date: Tue, 10 Sep 2024 18:00:03 -0400 Subject: [PATCH 12/18] better extraction of google params --- .../test/fixtures/pydoclint/DOC102_google.py | 17 +++++++++++++++++ .../rules/pydoclint/rules/check_docstring.rs | 1 + 2 files changed, 18 insertions(+) diff --git a/crates/ruff_linter/resources/test/fixtures/pydoclint/DOC102_google.py b/crates/ruff_linter/resources/test/fixtures/pydoclint/DOC102_google.py index ca761efbf3428..f5cab892db43a 100644 --- a/crates/ruff_linter/resources/test/fixtures/pydoclint/DOC102_google.py +++ b/crates/ruff_linter/resources/test/fixtures/pydoclint/DOC102_google.py @@ -206,3 +206,20 @@ def foo(param1, param2, *args, **kwargs): **kwargs: Arbitrary keyword arguments. """ return + +# OK +def on_server_unloaded(self, server_context: ServerContext) -> None: + ''' Execute ``on_server_unloaded`` from ``server_lifecycle.py`` (if + it is defined) when the server cleanly exits. (Before stopping the + server's ``IOLoop``.) + + Args: + server_context (ServerContext) : + + .. warning:: + In practice this code may not run, since servers are often killed + by a signal. + + + ''' + return self._lifecycle_handler.on_server_unloaded(server_context) 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 1f2166aaf5620..7a7414b4d1be1 100644 --- a/crates/ruff_linter/src/rules/pydoclint/rules/check_docstring.rs +++ b/crates/ruff_linter/src/rules/pydoclint/rules/check_docstring.rs @@ -629,6 +629,7 @@ fn parse_parameters_google(content: &str) -> Vec<&str> { continue; }; indentation = Some(&potential[..potential.len() - potential.trim_start().len()]); + break; } let Some(indentation) = indentation else { return entries; From 4d47b2db63d61ea05f8e3bc61cb9bddc89c9e7e5 Mon Sep 17 00:00:00 2001 From: Auguste Lalande Date: Tue, 10 Sep 2024 18:14:50 -0400 Subject: [PATCH 13/18] Nit Co-authored-by: Micha Reiser --- .../rules/pydoclint/rules/check_docstring.rs | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) 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 7a7414b4d1be1..c695b0f2307ac 100644 --- a/crates/ruff_linter/src/rules/pydoclint/rules/check_docstring.rs +++ b/crates/ruff_linter/src/rules/pydoclint/rules/check_docstring.rs @@ -636,14 +636,16 @@ fn parse_parameters_google(content: &str) -> Vec<&str> { }; for potential in content.lines() { if let Some(entry) = potential.strip_prefix(indentation) { - if let Some(first_char) = entry.chars().next() { - if !first_char.is_whitespace() { - let Some(colon_idx) = entry.find(':') else { - continue; - }; - if let Some(param) = entry[..colon_idx].split_whitespace().next() { - entries.push(param.trim_start_matches('*')); - } + if entry + .chars() + .next() + .is_some_and(|first_char| !first_char.is_whitespace()) + { + let Some(colon_idx) = entry.find(':') else { + continue; + }; + if let Some(param) = entry[..colon_idx].split_whitespace().next() { + entries.push(param.trim_start_matches('*')); } } } From 55aa072ddb4c549aef19e5103e98ac07aa31b108 Mon Sep 17 00:00:00 2001 From: augustelalande Date: Tue, 10 Sep 2024 18:17:48 -0400 Subject: [PATCH 14/18] Nit --- .../ruff_linter/src/rules/pydoclint/rules/check_docstring.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 c695b0f2307ac..e2270be74cb30 100644 --- a/crates/ruff_linter/src/rules/pydoclint/rules/check_docstring.rs +++ b/crates/ruff_linter/src/rules/pydoclint/rules/check_docstring.rs @@ -641,10 +641,10 @@ fn parse_parameters_google(content: &str) -> Vec<&str> { .next() .is_some_and(|first_char| !first_char.is_whitespace()) { - let Some(colon_idx) = entry.find(':') else { + let Some(before_colon) = entry.split_once(':') else { continue; }; - if let Some(param) = entry[..colon_idx].split_whitespace().next() { + if let Some(param) = before_colon.split_whitespace().next() { entries.push(param.trim_start_matches('*')); } } From 888bcacedd1d03b3a304bafa87ef2de2fa6af6c3 Mon Sep 17 00:00:00 2001 From: augustelalande Date: Tue, 10 Sep 2024 18:21:05 -0400 Subject: [PATCH 15/18] Nit --- .../src/rules/pydoclint/rules/check_docstring.rs | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) 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 e2270be74cb30..02b56e9d56387 100644 --- a/crates/ruff_linter/src/rules/pydoclint/rules/check_docstring.rs +++ b/crates/ruff_linter/src/rules/pydoclint/rules/check_docstring.rs @@ -672,11 +672,13 @@ fn parse_parameters_numpy(content: &str) -> Vec<&str> { let indentation = &dashes[..dashes.len() - dashes.trim_start().len()]; for potential in lines { if let Some(entry) = potential.strip_prefix(indentation) { - if let Some(first_char) = entry.chars().next() { - if !first_char.is_whitespace() { - if let Some(param) = entry.split(':').next() { - entries.push(param.trim_end().trim_start_matches('*')); - } + if entry + .chars() + .next() + .is_some_and(|first_char| !first_char.is_whitespace()) + { + if let Some(param) = entry.split(':').next() { + entries.push(param.trim_end().trim_start_matches('*')); } } } From f0f6ef12f5e1197b58539a128f4bd68f0465ad2a Mon Sep 17 00:00:00 2001 From: augustelalande Date: Tue, 10 Sep 2024 18:25:45 -0400 Subject: [PATCH 16/18] fix --- crates/ruff_linter/src/rules/pydoclint/rules/check_docstring.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 02b56e9d56387..9dc63259c8df7 100644 --- a/crates/ruff_linter/src/rules/pydoclint/rules/check_docstring.rs +++ b/crates/ruff_linter/src/rules/pydoclint/rules/check_docstring.rs @@ -641,7 +641,7 @@ fn parse_parameters_google(content: &str) -> Vec<&str> { .next() .is_some_and(|first_char| !first_char.is_whitespace()) { - let Some(before_colon) = entry.split_once(':') else { + let Some((before_colon, _)) = entry.split_once(':') else { continue; }; if let Some(param) = before_colon.split_whitespace().next() { From 03a1182e35a5c7af0f9a793a59a49d930b483abb Mon Sep 17 00:00:00 2001 From: augustelalande Date: Tue, 10 Sep 2024 18:31:45 -0400 Subject: [PATCH 17/18] update violation message --- .../rules/pydoclint/rules/check_docstring.rs | 4 ++-- ...extraneous-parameter_DOC102_google.py.snap | 24 +++++++++---------- ...-extraneous-parameter_DOC102_numpy.py.snap | 24 +++++++++---------- 3 files changed, 26 insertions(+), 26 deletions(-) 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 9dc63259c8df7..74c92384df419 100644 --- a/crates/ruff_linter/src/rules/pydoclint/rules/check_docstring.rs +++ b/crates/ruff_linter/src/rules/pydoclint/rules/check_docstring.rs @@ -135,10 +135,10 @@ impl Violation for DocstringExtraneousParameter { let DocstringExtraneousParameter { ids } = self; if let [id] = ids.as_slice() { - format!("`{id}` is not in the function's signature") + format!("Documented parameter `{id}` is not in the function's signature") } else { format!( - "These parameters are not in the function's signature: {}", + "These documented parameters are not in the function's signature: {}", ids.iter().map(|id| format!("`{id}`")).join(", ") ) } diff --git a/crates/ruff_linter/src/rules/pydoclint/snapshots/ruff_linter__rules__pydoclint__tests__docstring-extraneous-parameter_DOC102_google.py.snap b/crates/ruff_linter/src/rules/pydoclint/snapshots/ruff_linter__rules__pydoclint__tests__docstring-extraneous-parameter_DOC102_google.py.snap index 921c8fc5359f6..a5919824f8450 100644 --- a/crates/ruff_linter/src/rules/pydoclint/snapshots/ruff_linter__rules__pydoclint__tests__docstring-extraneous-parameter_DOC102_google.py.snap +++ b/crates/ruff_linter/src/rules/pydoclint/snapshots/ruff_linter__rules__pydoclint__tests__docstring-extraneous-parameter_DOC102_google.py.snap @@ -1,7 +1,7 @@ --- source: crates/ruff_linter/src/rules/pydoclint/mod.rs --- -DOC102_google.py:6:1: DOC102 `a` is not in the function's signature +DOC102_google.py:6:1: DOC102 Documented parameter `a` is not in the function's signature | 4 | Adds two numbers and returns the result. 5 | @@ -16,7 +16,7 @@ DOC102_google.py:6:1: DOC102 `a` is not in the function's signature | = help: Remove the extraneous parameter from the docstring -DOC102_google.py:21:1: DOC102 `multiplier` is not in the function's signature +DOC102_google.py:21:1: DOC102 Documented parameter `multiplier` is not in the function's signature | 19 | Multiplies each element in a list by a given multiplier. 20 | @@ -31,7 +31,7 @@ DOC102_google.py:21:1: DOC102 `multiplier` is not in the function's signature | = help: Remove the extraneous parameter from the docstring -DOC102_google.py:36:1: DOC102 `numbers` is not in the function's signature +DOC102_google.py:36:1: DOC102 Documented parameter `numbers` is not in the function's signature | 34 | Finds the maximum value in a list of numbers. 35 | @@ -45,7 +45,7 @@ DOC102_google.py:36:1: DOC102 `numbers` is not in the function's signature | = help: Remove the extraneous parameter from the docstring -DOC102_google.py:50:1: DOC102 These parameters are not in the function's signature: `name`, `age`, `email` +DOC102_google.py:50:1: DOC102 These documented parameters are not in the function's signature: `name`, `age`, `email` | 48 | Creates a user profile with basic information. 49 | @@ -62,7 +62,7 @@ DOC102_google.py:50:1: DOC102 These parameters are not in the function's signatu | = help: Remove the extraneous parameters from the docstring -DOC102_google.py:72:1: DOC102 `tax_rate` is not in the function's signature +DOC102_google.py:72:1: DOC102 Documented parameter `tax_rate` is not in the function's signature | 70 | Calculates the total price after applying tax and a discount. 71 | @@ -78,7 +78,7 @@ DOC102_google.py:72:1: DOC102 `tax_rate` is not in the function's signature | = help: Remove the extraneous parameter from the docstring -DOC102_google.py:91:1: DOC102 These parameters are not in the function's signature: `to_address`, `cc_address` +DOC102_google.py:91:1: DOC102 These documented parameters are not in the function's signature: `to_address`, `cc_address` | 89 | Sends an email to the specified recipients. 90 | @@ -96,7 +96,7 @@ DOC102_google.py:91:1: DOC102 These parameters are not in the function's signatu | = help: Remove the extraneous parameters from the docstring -DOC102_google.py:109:1: DOC102 `separator` is not in the function's signature +DOC102_google.py:109:1: DOC102 Documented parameter `separator` is not in the function's signature | 107 | Concatenates multiple strings with a specified separator. 108 | @@ -111,7 +111,7 @@ DOC102_google.py:109:1: DOC102 `separator` is not in the function's signature | = help: Remove the extraneous parameter from the docstring -DOC102_google.py:124:1: DOC102 These parameters are not in the function's signature: `items`, `details` +DOC102_google.py:124:1: DOC102 These documented parameters are not in the function's signature: `items`, `details` | 122 | Processes an order with a list of items and optional order details. 123 | @@ -127,7 +127,7 @@ DOC102_google.py:124:1: DOC102 These parameters are not in the function's signat | = help: Remove the extraneous parameters from the docstring -DOC102_google.py:149:1: DOC102 `value` is not in the function's signature +DOC102_google.py:149:1: DOC102 Documented parameter `value` is not in the function's signature | 147 | Initializes the calculator with an initial value. 148 | @@ -139,7 +139,7 @@ DOC102_google.py:149:1: DOC102 `value` is not in the function's signature | = help: Remove the extraneous parameter from the docstring -DOC102_google.py:159:1: DOC102 `number` is not in the function's signature +DOC102_google.py:159:1: DOC102 Documented parameter `number` is not in the function's signature | 157 | Adds a number to the current value. 158 | @@ -153,7 +153,7 @@ DOC102_google.py:159:1: DOC102 `number` is not in the function's signature | = help: Remove the extraneous parameter from the docstring -DOC102_google.py:174:1: DOC102 `value_str` is not in the function's signature +DOC102_google.py:174:1: DOC102 Documented parameter `value_str` is not in the function's signature | 172 | Creates a Calculator instance from a string representation of a number. 173 | @@ -167,7 +167,7 @@ DOC102_google.py:174:1: DOC102 `value_str` is not in the function's signature | = help: Remove the extraneous parameter from the docstring -DOC102_google.py:189:1: DOC102 `number` is not in the function's signature +DOC102_google.py:189:1: DOC102 Documented parameter `number` is not in the function's signature | 187 | Checks if a given number is valid (int or float). 188 | diff --git a/crates/ruff_linter/src/rules/pydoclint/snapshots/ruff_linter__rules__pydoclint__tests__docstring-extraneous-parameter_DOC102_numpy.py.snap b/crates/ruff_linter/src/rules/pydoclint/snapshots/ruff_linter__rules__pydoclint__tests__docstring-extraneous-parameter_DOC102_numpy.py.snap index 1b3a0fe9b8c20..6596d97a364c7 100644 --- a/crates/ruff_linter/src/rules/pydoclint/snapshots/ruff_linter__rules__pydoclint__tests__docstring-extraneous-parameter_DOC102_numpy.py.snap +++ b/crates/ruff_linter/src/rules/pydoclint/snapshots/ruff_linter__rules__pydoclint__tests__docstring-extraneous-parameter_DOC102_numpy.py.snap @@ -1,7 +1,7 @@ --- source: crates/ruff_linter/src/rules/pydoclint/mod.rs --- -DOC102_numpy.py:6:1: DOC102 `a` is not in the function's signature +DOC102_numpy.py:6:1: DOC102 Documented parameter `a` is not in the function's signature | 4 | Adds two numbers and returns the result. 5 | @@ -19,7 +19,7 @@ DOC102_numpy.py:6:1: DOC102 `a` is not in the function's signature | = help: Remove the extraneous parameter from the docstring -DOC102_numpy.py:26:1: DOC102 `multiplier` is not in the function's signature +DOC102_numpy.py:26:1: DOC102 Documented parameter `multiplier` is not in the function's signature | 24 | Multiplies each element in a list by a given multiplier. 25 | @@ -37,7 +37,7 @@ DOC102_numpy.py:26:1: DOC102 `multiplier` is not in the function's signature | = help: Remove the extraneous parameter from the docstring -DOC102_numpy.py:46:1: DOC102 `numbers` is not in the function's signature +DOC102_numpy.py:46:1: DOC102 Documented parameter `numbers` is not in the function's signature | 44 | Finds the maximum value in a list of numbers. 45 | @@ -53,7 +53,7 @@ DOC102_numpy.py:46:1: DOC102 `numbers` is not in the function's signature | = help: Remove the extraneous parameter from the docstring -DOC102_numpy.py:64:1: DOC102 These parameters are not in the function's signature: `name`, `age`, `email` +DOC102_numpy.py:64:1: DOC102 These documented parameters are not in the function's signature: `name`, `age`, `email` | 62 | Creates a user profile with basic information. 63 | @@ -75,7 +75,7 @@ DOC102_numpy.py:64:1: DOC102 These parameters are not in the function's signatur | = help: Remove the extraneous parameters from the docstring -DOC102_numpy.py:93:1: DOC102 `tax_rate` is not in the function's signature +DOC102_numpy.py:93:1: DOC102 Documented parameter `tax_rate` is not in the function's signature | 91 | Calculates the total price after applying tax and a discount. 92 | @@ -95,7 +95,7 @@ DOC102_numpy.py:93:1: DOC102 `tax_rate` is not in the function's signature | = help: Remove the extraneous parameter from the docstring -DOC102_numpy.py:118:1: DOC102 These parameters are not in the function's signature: `to_address`, `cc_address` +DOC102_numpy.py:118:1: DOC102 These documented parameters are not in the function's signature: `to_address`, `cc_address` | 116 | Sends an email to the specified recipients. 117 | @@ -119,7 +119,7 @@ DOC102_numpy.py:118:1: DOC102 These parameters are not in the function's signatu | = help: Remove the extraneous parameters from the docstring -DOC102_numpy.py:144:1: DOC102 `separator` is not in the function's signature +DOC102_numpy.py:144:1: DOC102 Documented parameter `separator` is not in the function's signature | 142 | Concatenates multiple strings with a specified separator. 143 | @@ -137,7 +137,7 @@ DOC102_numpy.py:144:1: DOC102 `separator` is not in the function's signature | = help: Remove the extraneous parameter from the docstring -DOC102_numpy.py:164:1: DOC102 These parameters are not in the function's signature: `items`, `details` +DOC102_numpy.py:164:1: DOC102 These documented parameters are not in the function's signature: `items`, `details` | 162 | Processes an order with a list of items and optional order details. 163 | @@ -157,7 +157,7 @@ DOC102_numpy.py:164:1: DOC102 These parameters are not in the function's signatu | = help: Remove the extraneous parameters from the docstring -DOC102_numpy.py:195:1: DOC102 `value` is not in the function's signature +DOC102_numpy.py:195:1: DOC102 Documented parameter `value` is not in the function's signature | 193 | Initializes the calculator with an initial value. 194 | @@ -171,7 +171,7 @@ DOC102_numpy.py:195:1: DOC102 `value` is not in the function's signature | = help: Remove the extraneous parameter from the docstring -DOC102_numpy.py:207:1: DOC102 `number` is not in the function's signature +DOC102_numpy.py:207:1: DOC102 Documented parameter `number` is not in the function's signature | 205 | Adds two numbers to the current value. 206 | @@ -189,7 +189,7 @@ DOC102_numpy.py:207:1: DOC102 `number` is not in the function's signature | = help: Remove the extraneous parameter from the docstring -DOC102_numpy.py:228:1: DOC102 `value_str` is not in the function's signature +DOC102_numpy.py:228:1: DOC102 Documented parameter `value_str` is not in the function's signature | 226 | Creates a Calculator instance from a string representation of a number. 227 | @@ -205,7 +205,7 @@ DOC102_numpy.py:228:1: DOC102 `value_str` is not in the function's signature | = help: Remove the extraneous parameter from the docstring -DOC102_numpy.py:247:1: DOC102 `number` is not in the function's signature +DOC102_numpy.py:247:1: DOC102 Documented parameter `number` is not in the function's signature | 245 | Checks if a given number is valid (int or float). 246 | From 76c1e5fa7c3c948e2da3d4862bf4e8245b496163 Mon Sep 17 00:00:00 2001 From: augustelalande Date: Tue, 10 Sep 2024 18:55:00 -0400 Subject: [PATCH 18/18] seperate PR --- .../ruff_linter/src/rules/pydoclint/rules/check_docstring.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 74c92384df419..c220840c579da 100644 --- a/crates/ruff_linter/src/rules/pydoclint/rules/check_docstring.rs +++ b/crates/ruff_linter/src/rules/pydoclint/rules/check_docstring.rs @@ -581,10 +581,10 @@ impl<'a> DocstringSections<'a> { SectionKind::Raises => { docstring_sections.raises = Some(RaisesSection::from_section(§ion, style)); } - SectionKind::Returns | SectionKind::Return => { + SectionKind::Returns => { docstring_sections.returns = Some(GenericSection::from_section(§ion)); } - SectionKind::Yields | SectionKind::Yield => { + SectionKind::Yields => { docstring_sections.yields = Some(GenericSection::from_section(§ion)); } _ => continue,