From 87c3b0e4e270b78da182fea07154552152b63321 Mon Sep 17 00:00:00 2001 From: Aarni Koskela Date: Fri, 3 Feb 2023 14:42:52 +0200 Subject: [PATCH] Move pydocstyle violations (#2524) --- src/checkers/ast.rs | 2 +- src/registry.rs | 92 +-- src/rules/pydocstyle/rules/backslashes.rs | 17 +- .../pydocstyle/rules/blank_after_summary.rs | 39 +- .../rules/blank_before_after_class.rs | 59 +- .../rules/blank_before_after_function.rs | 43 +- src/rules/pydocstyle/rules/capitalized.rs | 17 +- .../pydocstyle/rules/ends_with_period.rs | 24 +- .../pydocstyle/rules/ends_with_punctuation.rs | 25 +- src/rules/pydocstyle/rules/if_needed.rs | 17 +- src/rules/pydocstyle/rules/indent.rs | 51 +- src/rules/pydocstyle/rules/mod.rs | 57 +- .../rules/multi_line_summary_start.rs | 37 +- .../rules/newline_after_last_paragraph.rs | 21 +- src/rules/pydocstyle/rules/no_signature.rs | 16 +- .../rules/no_surrounding_whitespace.rs | 25 +- src/rules/pydocstyle/rules/not_empty.rs | 17 +- src/rules/pydocstyle/rules/not_missing.rs | 103 ++- src/rules/pydocstyle/rules/one_liner.rs | 24 +- src/rules/pydocstyle/rules/sections.rs | 288 +++++++- .../pydocstyle/rules/starts_with_this.rs | 17 +- src/rules/pydocstyle/rules/triple_quotes.rs | 17 +- src/violations.rs | 651 +----------------- 23 files changed, 860 insertions(+), 799 deletions(-) diff --git a/src/checkers/ast.rs b/src/checkers/ast.rs index 4e9466992f..fb38f6de56 100644 --- a/src/checkers/ast.rs +++ b/src/checkers/ast.rs @@ -5088,7 +5088,7 @@ impl<'a> Checker<'a> { pydocstyle::rules::ends_with_period(self, &docstring); } if self.settings.rules.enabled(&Rule::NonImperativeMood) { - pydocstyle::rules::non_imperative_mood::non_imperative_mood(self, &docstring); + pydocstyle::rules::non_imperative_mood(self, &docstring); } if self.settings.rules.enabled(&Rule::NoSignature) { pydocstyle::rules::no_signature(self, &docstring); diff --git a/src/registry.rs b/src/registry.rs index 18d59de56f..2bc3cc1e89 100644 --- a/src/registry.rs +++ b/src/registry.rs @@ -259,52 +259,52 @@ ruff_macros::define_rule_mapping!( UP035 => rules::pyupgrade::rules::ImportReplacements, UP036 => rules::pyupgrade::rules::OutdatedVersionBlock, // pydocstyle - D100 => violations::PublicModule, - D101 => violations::PublicClass, - D102 => violations::PublicMethod, - D103 => violations::PublicFunction, - D104 => violations::PublicPackage, - D105 => violations::MagicMethod, - D106 => violations::PublicNestedClass, - D107 => violations::PublicInit, - D200 => violations::FitsOnOneLine, - D201 => violations::NoBlankLineBeforeFunction, - D202 => violations::NoBlankLineAfterFunction, - D203 => violations::OneBlankLineBeforeClass, - D204 => violations::OneBlankLineAfterClass, - D205 => violations::BlankLineAfterSummary, - D206 => violations::IndentWithSpaces, - D207 => violations::NoUnderIndentation, - D208 => violations::NoOverIndentation, - D209 => violations::NewLineAfterLastParagraph, - D210 => violations::NoSurroundingWhitespace, - D211 => violations::NoBlankLineBeforeClass, - D212 => violations::MultiLineSummaryFirstLine, - D213 => violations::MultiLineSummarySecondLine, - D214 => violations::SectionNotOverIndented, - D215 => violations::SectionUnderlineNotOverIndented, - D300 => violations::UsesTripleQuotes, - D301 => violations::UsesRPrefixForBackslashedContent, - D400 => violations::EndsInPeriod, - D401 => rules::pydocstyle::rules::non_imperative_mood::NonImperativeMood, - D402 => violations::NoSignature, - D403 => violations::FirstLineCapitalized, - D404 => violations::NoThisPrefix, - D405 => violations::CapitalizeSectionName, - D406 => violations::NewLineAfterSectionName, - D407 => violations::DashedUnderlineAfterSection, - D408 => violations::SectionUnderlineAfterName, - D409 => violations::SectionUnderlineMatchesSectionLength, - D410 => violations::BlankLineAfterSection, - D411 => violations::BlankLineBeforeSection, - D412 => violations::NoBlankLinesBetweenHeaderAndContent, - D413 => violations::BlankLineAfterLastSection, - D414 => violations::NonEmptySection, - D415 => violations::EndsInPunctuation, - D416 => violations::SectionNameEndsInColon, - D417 => violations::DocumentAllArguments, - D418 => violations::SkipDocstring, - D419 => violations::NonEmpty, + D100 => rules::pydocstyle::rules::PublicModule, + D101 => rules::pydocstyle::rules::PublicClass, + D102 => rules::pydocstyle::rules::PublicMethod, + D103 => rules::pydocstyle::rules::PublicFunction, + D104 => rules::pydocstyle::rules::PublicPackage, + D105 => rules::pydocstyle::rules::MagicMethod, + D106 => rules::pydocstyle::rules::PublicNestedClass, + D107 => rules::pydocstyle::rules::PublicInit, + D200 => rules::pydocstyle::rules::FitsOnOneLine, + D201 => rules::pydocstyle::rules::NoBlankLineBeforeFunction, + D202 => rules::pydocstyle::rules::NoBlankLineAfterFunction, + D203 => rules::pydocstyle::rules::OneBlankLineBeforeClass, + D204 => rules::pydocstyle::rules::OneBlankLineAfterClass, + D205 => rules::pydocstyle::rules::BlankLineAfterSummary, + D206 => rules::pydocstyle::rules::IndentWithSpaces, + D207 => rules::pydocstyle::rules::NoUnderIndentation, + D208 => rules::pydocstyle::rules::NoOverIndentation, + D209 => rules::pydocstyle::rules::NewLineAfterLastParagraph, + D210 => rules::pydocstyle::rules::NoSurroundingWhitespace, + D211 => rules::pydocstyle::rules::NoBlankLineBeforeClass, + D212 => rules::pydocstyle::rules::MultiLineSummaryFirstLine, + D213 => rules::pydocstyle::rules::MultiLineSummarySecondLine, + D214 => rules::pydocstyle::rules::SectionNotOverIndented, + D215 => rules::pydocstyle::rules::SectionUnderlineNotOverIndented, + D300 => rules::pydocstyle::rules::UsesTripleQuotes, + D301 => rules::pydocstyle::rules::UsesRPrefixForBackslashedContent, + D400 => rules::pydocstyle::rules::EndsInPeriod, + D401 => rules::pydocstyle::rules::NonImperativeMood, + D402 => rules::pydocstyle::rules::NoSignature, + D403 => rules::pydocstyle::rules::FirstLineCapitalized, + D404 => rules::pydocstyle::rules::NoThisPrefix, + D405 => rules::pydocstyle::rules::CapitalizeSectionName, + D406 => rules::pydocstyle::rules::NewLineAfterSectionName, + D407 => rules::pydocstyle::rules::DashedUnderlineAfterSection, + D408 => rules::pydocstyle::rules::SectionUnderlineAfterName, + D409 => rules::pydocstyle::rules::SectionUnderlineMatchesSectionLength, + D410 => rules::pydocstyle::rules::BlankLineAfterSection, + D411 => rules::pydocstyle::rules::BlankLineBeforeSection, + D412 => rules::pydocstyle::rules::NoBlankLinesBetweenHeaderAndContent, + D413 => rules::pydocstyle::rules::BlankLineAfterLastSection, + D414 => rules::pydocstyle::rules::NonEmptySection, + D415 => rules::pydocstyle::rules::EndsInPunctuation, + D416 => rules::pydocstyle::rules::SectionNameEndsInColon, + D417 => rules::pydocstyle::rules::DocumentAllArguments, + D418 => rules::pydocstyle::rules::SkipDocstring, + D419 => rules::pydocstyle::rules::NonEmpty, // pep8-naming N801 => violations::InvalidClassName, N802 => violations::InvalidFunctionName, diff --git a/src/rules/pydocstyle/rules/backslashes.rs b/src/rules/pydocstyle/rules/backslashes.rs index 15a1cd889e..0e121f285c 100644 --- a/src/rules/pydocstyle/rules/backslashes.rs +++ b/src/rules/pydocstyle/rules/backslashes.rs @@ -3,7 +3,20 @@ use crate::checkers::ast::Checker; use crate::docstrings::definition::Docstring; use crate::registry::Diagnostic; use crate::rules::pydocstyle::rules::regexes::BACKSLASH_REGEX; -use crate::violations; +use crate::violation::Violation; + +use crate::define_violation; +use ruff_macros::derive_message_formats; + +define_violation!( + pub struct UsesRPrefixForBackslashedContent; +); +impl Violation for UsesRPrefixForBackslashedContent { + #[derive_message_formats] + fn message(&self) -> String { + format!(r#"Use r""" if any backslashes in a docstring"#) + } +} /// D301 pub fn backslashes(checker: &mut Checker, docstring: &Docstring) { @@ -16,7 +29,7 @@ pub fn backslashes(checker: &mut Checker, docstring: &Docstring) { if BACKSLASH_REGEX.is_match(contents) { checker.diagnostics.push(Diagnostic::new( - violations::UsesRPrefixForBackslashedContent, + UsesRPrefixForBackslashedContent, Range::from_located(docstring.expr), )); } diff --git a/src/rules/pydocstyle/rules/blank_after_summary.rs b/src/rules/pydocstyle/rules/blank_after_summary.rs index d668c5b390..099d13376f 100644 --- a/src/rules/pydocstyle/rules/blank_after_summary.rs +++ b/src/rules/pydocstyle/rules/blank_after_summary.rs @@ -4,7 +4,42 @@ use crate::docstrings::definition::Docstring; use crate::fix::Fix; use crate::message::Location; use crate::registry::Diagnostic; -use crate::violations; +use crate::violation::{Availability, Violation}; + +use crate::{define_violation, AutofixKind}; +use ruff_macros::derive_message_formats; + +define_violation!( + pub struct BlankLineAfterSummary { + pub num_lines: usize, + } +); +fn fmt_blank_line_after_summary_autofix_msg(_: &BlankLineAfterSummary) -> String { + "Insert single blank line".to_string() +} +impl Violation for BlankLineAfterSummary { + const AUTOFIX: Option = Some(AutofixKind::new(Availability::Always)); + + #[derive_message_formats] + fn message(&self) -> String { + let BlankLineAfterSummary { num_lines } = self; + if *num_lines == 0 { + format!("1 blank line required between summary line and description") + } else { + format!( + "1 blank line required between summary line and description (found {num_lines})" + ) + } + } + + fn autofix_title_formatter(&self) -> Option String> { + let BlankLineAfterSummary { num_lines } = self; + if *num_lines > 0 { + return Some(fmt_blank_line_after_summary_autofix_msg); + } + None + } +} /// D205 pub fn blank_after_summary(checker: &mut Checker, docstring: &Docstring) { @@ -22,7 +57,7 @@ pub fn blank_after_summary(checker: &mut Checker, docstring: &Docstring) { } if lines_count > 1 && blanks_count != 1 { let mut diagnostic = Diagnostic::new( - violations::BlankLineAfterSummary { + BlankLineAfterSummary { num_lines: blanks_count, }, Range::from_located(docstring.expr), diff --git a/src/rules/pydocstyle/rules/blank_before_after_class.rs b/src/rules/pydocstyle/rules/blank_before_after_class.rs index f1edf5e192..80a16da100 100644 --- a/src/rules/pydocstyle/rules/blank_before_after_class.rs +++ b/src/rules/pydocstyle/rules/blank_before_after_class.rs @@ -5,7 +5,58 @@ use crate::fix::Fix; use crate::message::Location; use crate::registry::{Diagnostic, Rule}; use crate::rules::pydocstyle::rules::regexes::COMMENT_REGEX; -use crate::violations; +use crate::violation::AlwaysAutofixableViolation; + +use crate::define_violation; +use ruff_macros::derive_message_formats; + +define_violation!( + pub struct OneBlankLineBeforeClass { + pub lines: usize, + } +); +impl AlwaysAutofixableViolation for OneBlankLineBeforeClass { + #[derive_message_formats] + fn message(&self) -> String { + format!("1 blank line required before class docstring") + } + + fn autofix_title(&self) -> String { + "Insert 1 blank line before class docstring".to_string() + } +} + +define_violation!( + pub struct OneBlankLineAfterClass { + pub lines: usize, + } +); +impl AlwaysAutofixableViolation for OneBlankLineAfterClass { + #[derive_message_formats] + fn message(&self) -> String { + format!("1 blank line required after class docstring") + } + + fn autofix_title(&self) -> String { + "Insert 1 blank line after class docstring".to_string() + } +} + +define_violation!( + pub struct NoBlankLineBeforeClass { + pub lines: usize, + } +); +impl AlwaysAutofixableViolation for NoBlankLineBeforeClass { + #[derive_message_formats] + fn message(&self) -> String { + format!("No blank lines allowed before class docstring") + } + + fn autofix_title(&self) -> String { + "Remove blank line(s) before class docstring".to_string() + } +} /// D203, D204, D211 pub fn blank_before_after_class(checker: &mut Checker, docstring: &Docstring) { @@ -40,7 +91,7 @@ pub fn blank_before_after_class(checker: &mut Checker, docstring: &Docstring) { { if blank_lines_before != 0 { let mut diagnostic = Diagnostic::new( - violations::NoBlankLineBeforeClass { + NoBlankLineBeforeClass { lines: blank_lines_before, }, Range::from_located(docstring.expr), @@ -62,7 +113,7 @@ pub fn blank_before_after_class(checker: &mut Checker, docstring: &Docstring) { { if blank_lines_before != 1 { let mut diagnostic = Diagnostic::new( - violations::OneBlankLineBeforeClass { + OneBlankLineBeforeClass { lines: blank_lines_before, }, Range::from_located(docstring.expr), @@ -105,7 +156,7 @@ pub fn blank_before_after_class(checker: &mut Checker, docstring: &Docstring) { .count(); if blank_lines_after != 1 { let mut diagnostic = Diagnostic::new( - violations::OneBlankLineAfterClass { + OneBlankLineAfterClass { lines: blank_lines_after, }, Range::from_located(docstring.expr), diff --git a/src/rules/pydocstyle/rules/blank_before_after_function.rs b/src/rules/pydocstyle/rules/blank_before_after_function.rs index b9cf3c9698..d6ea760a24 100644 --- a/src/rules/pydocstyle/rules/blank_before_after_function.rs +++ b/src/rules/pydocstyle/rules/blank_before_after_function.rs @@ -5,7 +5,44 @@ use crate::fix::Fix; use crate::message::Location; use crate::registry::{Diagnostic, Rule}; use crate::rules::pydocstyle::rules::regexes::{COMMENT_REGEX, INNER_FUNCTION_OR_CLASS_REGEX}; -use crate::violations; +use crate::violation::AlwaysAutofixableViolation; + +use crate::define_violation; +use ruff_macros::derive_message_formats; + +define_violation!( + pub struct NoBlankLineBeforeFunction { + pub num_lines: usize, + } +); +impl AlwaysAutofixableViolation for NoBlankLineBeforeFunction { + #[derive_message_formats] + fn message(&self) -> String { + let NoBlankLineBeforeFunction { num_lines } = self; + format!("No blank lines allowed before function docstring (found {num_lines})") + } + + fn autofix_title(&self) -> String { + "Remove blank line(s) before function docstring".to_string() + } +} + +define_violation!( + pub struct NoBlankLineAfterFunction { + pub num_lines: usize, + } +); +impl AlwaysAutofixableViolation for NoBlankLineAfterFunction { + #[derive_message_formats] + fn message(&self) -> String { + let NoBlankLineAfterFunction { num_lines } = self; + format!("No blank lines allowed after function docstring (found {num_lines})") + } + + fn autofix_title(&self) -> String { + "Remove blank line(s) after function docstring".to_string() + } +} /// D201, D202 pub fn blank_before_after_function(checker: &mut Checker, docstring: &Docstring) { @@ -35,7 +72,7 @@ pub fn blank_before_after_function(checker: &mut Checker, docstring: &Docstring) .count(); if blank_lines_before != 0 { let mut diagnostic = Diagnostic::new( - violations::NoBlankLineBeforeFunction { + NoBlankLineBeforeFunction { num_lines: blank_lines_before, }, Range::from_located(docstring.expr), @@ -82,7 +119,7 @@ pub fn blank_before_after_function(checker: &mut Checker, docstring: &Docstring) if blank_lines_after != 0 { let mut diagnostic = Diagnostic::new( - violations::NoBlankLineAfterFunction { + NoBlankLineAfterFunction { num_lines: blank_lines_after, }, Range::from_located(docstring.expr), diff --git a/src/rules/pydocstyle/rules/capitalized.rs b/src/rules/pydocstyle/rules/capitalized.rs index 21cdba0c94..b134db2b87 100644 --- a/src/rules/pydocstyle/rules/capitalized.rs +++ b/src/rules/pydocstyle/rules/capitalized.rs @@ -2,7 +2,20 @@ use crate::ast::types::Range; use crate::checkers::ast::Checker; use crate::docstrings::definition::{DefinitionKind, Docstring}; use crate::registry::Diagnostic; -use crate::violations; +use crate::violation::Violation; + +use crate::define_violation; +use ruff_macros::derive_message_formats; + +define_violation!( + pub struct FirstLineCapitalized; +); +impl Violation for FirstLineCapitalized { + #[derive_message_formats] + fn message(&self) -> String { + format!("First word of the first line should be properly capitalized") + } +} /// D403 pub fn capitalized(checker: &mut Checker, docstring: &Docstring) { @@ -30,7 +43,7 @@ pub fn capitalized(checker: &mut Checker, docstring: &Docstring) { return; }; checker.diagnostics.push(Diagnostic::new( - violations::FirstLineCapitalized, + FirstLineCapitalized, Range::from_located(docstring.expr), )); } diff --git a/src/rules/pydocstyle/rules/ends_with_period.rs b/src/rules/pydocstyle/rules/ends_with_period.rs index 608926153b..5de2261c2c 100644 --- a/src/rules/pydocstyle/rules/ends_with_period.rs +++ b/src/rules/pydocstyle/rules/ends_with_period.rs @@ -6,7 +6,24 @@ use crate::fix::Fix; use crate::message::Location; use crate::registry::Diagnostic; use crate::rules::pydocstyle::helpers::{leading_quote, logical_line}; -use crate::violations; +use crate::violation::AlwaysAutofixableViolation; + +use crate::define_violation; +use ruff_macros::derive_message_formats; + +define_violation!( + pub struct EndsInPeriod; +); +impl AlwaysAutofixableViolation for EndsInPeriod { + #[derive_message_formats] + fn message(&self) -> String { + format!("First line should end with a period") + } + + fn autofix_title(&self) -> String { + "Add period".to_string() + } +} /// D400 pub fn ends_with_period(checker: &mut Checker, docstring: &Docstring) { @@ -43,10 +60,7 @@ pub fn ends_with_period(checker: &mut Checker, docstring: &Docstring) { let trimmed = line.trim_end(); if !trimmed.ends_with('.') { - let mut diagnostic = Diagnostic::new( - violations::EndsInPeriod, - Range::from_located(docstring.expr), - ); + let mut diagnostic = Diagnostic::new(EndsInPeriod, Range::from_located(docstring.expr)); // Best-effort autofix: avoid adding a period after other punctuation marks. if checker.patch(diagnostic.kind.rule()) && !trimmed.ends_with(':') diff --git a/src/rules/pydocstyle/rules/ends_with_punctuation.rs b/src/rules/pydocstyle/rules/ends_with_punctuation.rs index 8777381d6d..8591b7d516 100644 --- a/src/rules/pydocstyle/rules/ends_with_punctuation.rs +++ b/src/rules/pydocstyle/rules/ends_with_punctuation.rs @@ -6,7 +6,24 @@ use crate::fix::Fix; use crate::message::Location; use crate::registry::Diagnostic; use crate::rules::pydocstyle::helpers::{leading_quote, logical_line}; -use crate::violations; +use crate::violation::AlwaysAutofixableViolation; + +use crate::define_violation; +use ruff_macros::derive_message_formats; + +define_violation!( + pub struct EndsInPunctuation; +); +impl AlwaysAutofixableViolation for EndsInPunctuation { + #[derive_message_formats] + fn message(&self) -> String { + format!("First line should end with a period, question mark, or exclamation point") + } + + fn autofix_title(&self) -> String { + "Add closing punctuation".to_string() + } +} /// D415 pub fn ends_with_punctuation(checker: &mut Checker, docstring: &Docstring) { @@ -42,10 +59,8 @@ pub fn ends_with_punctuation(checker: &mut Checker, docstring: &Docstring) { let line = body.lines().nth(index).unwrap(); let trimmed = line.trim_end(); if !(trimmed.ends_with('.') || trimmed.ends_with('!') || trimmed.ends_with('?')) { - let mut diagnostic = Diagnostic::new( - violations::EndsInPunctuation, - Range::from_located(docstring.expr), - ); + let mut diagnostic = + Diagnostic::new(EndsInPunctuation, Range::from_located(docstring.expr)); // Best-effort autofix: avoid adding a period after other punctuation marks. if checker.patch(diagnostic.kind.rule()) && !trimmed.ends_with(':') diff --git a/src/rules/pydocstyle/rules/if_needed.rs b/src/rules/pydocstyle/rules/if_needed.rs index 9aeb6ce72c..47afac0386 100644 --- a/src/rules/pydocstyle/rules/if_needed.rs +++ b/src/rules/pydocstyle/rules/if_needed.rs @@ -3,8 +3,21 @@ use crate::ast::helpers::identifier_range; use crate::checkers::ast::Checker; use crate::docstrings::definition::{DefinitionKind, Docstring}; use crate::registry::Diagnostic; -use crate::violations; +use crate::violation::Violation; + +use crate::define_violation; use crate::visibility::is_overload; +use ruff_macros::derive_message_formats; + +define_violation!( + pub struct SkipDocstring; +); +impl Violation for SkipDocstring { + #[derive_message_formats] + fn message(&self) -> String { + format!("Function decorated with `@overload` shouldn't contain a docstring") + } +} /// D418 pub fn if_needed(checker: &mut Checker, docstring: &Docstring) { @@ -19,7 +32,7 @@ pub fn if_needed(checker: &mut Checker, docstring: &Docstring) { return; } checker.diagnostics.push(Diagnostic::new( - violations::SkipDocstring, + SkipDocstring, identifier_range(stmt, checker.locator), )); } diff --git a/src/rules/pydocstyle/rules/indent.rs b/src/rules/pydocstyle/rules/indent.rs index be9437fde2..6b4a81d451 100644 --- a/src/rules/pydocstyle/rules/indent.rs +++ b/src/rules/pydocstyle/rules/indent.rs @@ -6,7 +6,48 @@ use crate::docstrings::definition::Docstring; use crate::fix::Fix; use crate::message::Location; use crate::registry::{Diagnostic, Rule}; -use crate::violations; +use crate::violation::{AlwaysAutofixableViolation, Violation}; + +use crate::define_violation; +use ruff_macros::derive_message_formats; + +define_violation!( + pub struct IndentWithSpaces; +); +impl Violation for IndentWithSpaces { + #[derive_message_formats] + fn message(&self) -> String { + format!("Docstring should be indented with spaces, not tabs") + } +} + +define_violation!( + pub struct NoUnderIndentation; +); +impl AlwaysAutofixableViolation for NoUnderIndentation { + #[derive_message_formats] + fn message(&self) -> String { + format!("Docstring is under-indented") + } + + fn autofix_title(&self) -> String { + "Increase indentation".to_string() + } +} + +define_violation!( + pub struct NoOverIndentation; +); +impl AlwaysAutofixableViolation for NoOverIndentation { + #[derive_message_formats] + fn message(&self) -> String { + format!("Docstring is over-indented") + } + + fn autofix_title(&self) -> String { + "Remove over-indentation".to_string() + } +} /// D206, D207, D208 pub fn indent(checker: &mut Checker, docstring: &Docstring) { @@ -47,7 +88,7 @@ pub fn indent(checker: &mut Checker, docstring: &Docstring) { && line_indent.len() < docstring.indentation.len() { let mut diagnostic = Diagnostic::new( - violations::NoUnderIndentation, + NoUnderIndentation, Range::new( Location::new(docstring.expr.location.row() + i, 0), Location::new(docstring.expr.location.row() + i, 0), @@ -82,7 +123,7 @@ pub fn indent(checker: &mut Checker, docstring: &Docstring) { if checker.settings.rules.enabled(&Rule::IndentWithSpaces) { if has_seen_tab { checker.diagnostics.push(Diagnostic::new( - violations::IndentWithSpaces, + IndentWithSpaces, Range::from_located(docstring.expr), )); } @@ -97,7 +138,7 @@ pub fn indent(checker: &mut Checker, docstring: &Docstring) { // We report over-indentation on every line. This isn't great, but // enables autofix. let mut diagnostic = Diagnostic::new( - violations::NoOverIndentation, + NoOverIndentation, Range::new( Location::new(docstring.expr.location.row() + i, 0), Location::new(docstring.expr.location.row() + i, 0), @@ -121,7 +162,7 @@ pub fn indent(checker: &mut Checker, docstring: &Docstring) { let line_indent = whitespace::leading_space(lines[i]); if line_indent.len() > docstring.indentation.len() { let mut diagnostic = Diagnostic::new( - violations::NoOverIndentation, + NoOverIndentation, Range::new( Location::new(docstring.expr.location.row() + i, 0), Location::new(docstring.expr.location.row() + i, 0), diff --git a/src/rules/pydocstyle/rules/mod.rs b/src/rules/pydocstyle/rules/mod.rs index 3bd6c1db6f..922c3c5e9b 100644 --- a/src/rules/pydocstyle/rules/mod.rs +++ b/src/rules/pydocstyle/rules/mod.rs @@ -1,22 +1,39 @@ -pub use backslashes::backslashes; -pub use blank_after_summary::blank_after_summary; -pub use blank_before_after_class::blank_before_after_class; -pub use blank_before_after_function::blank_before_after_function; -pub use capitalized::capitalized; -pub use ends_with_period::ends_with_period; -pub use ends_with_punctuation::ends_with_punctuation; -pub use if_needed::if_needed; -pub use indent::indent; -pub use multi_line_summary_start::multi_line_summary_start; -pub use newline_after_last_paragraph::newline_after_last_paragraph; -pub use no_signature::no_signature; -pub use no_surrounding_whitespace::no_surrounding_whitespace; -pub use not_empty::not_empty; -pub use not_missing::not_missing; -pub use one_liner::one_liner; -pub use sections::sections; -pub use starts_with_this::starts_with_this; -pub use triple_quotes::triple_quotes; +pub use backslashes::{backslashes, UsesRPrefixForBackslashedContent}; +pub use blank_after_summary::{blank_after_summary, BlankLineAfterSummary}; +pub use blank_before_after_class::{ + blank_before_after_class, NoBlankLineBeforeClass, OneBlankLineAfterClass, + OneBlankLineBeforeClass, +}; +pub use blank_before_after_function::{ + blank_before_after_function, NoBlankLineAfterFunction, NoBlankLineBeforeFunction, +}; +pub use capitalized::{capitalized, FirstLineCapitalized}; +pub use ends_with_period::{ends_with_period, EndsInPeriod}; +pub use ends_with_punctuation::{ends_with_punctuation, EndsInPunctuation}; +pub use if_needed::{if_needed, SkipDocstring}; +pub use indent::{indent, IndentWithSpaces, NoOverIndentation, NoUnderIndentation}; +pub use multi_line_summary_start::{ + multi_line_summary_start, MultiLineSummaryFirstLine, MultiLineSummarySecondLine, +}; +pub use newline_after_last_paragraph::{newline_after_last_paragraph, NewLineAfterLastParagraph}; +pub use no_signature::{no_signature, NoSignature}; +pub use no_surrounding_whitespace::{no_surrounding_whitespace, NoSurroundingWhitespace}; +pub use non_imperative_mood::{non_imperative_mood, NonImperativeMood}; +pub use not_empty::{not_empty, NonEmpty}; +pub use not_missing::{ + not_missing, MagicMethod, PublicClass, PublicFunction, PublicInit, PublicMethod, PublicModule, + PublicNestedClass, PublicPackage, +}; +pub use one_liner::{one_liner, FitsOnOneLine}; +pub use sections::{ + sections, BlankLineAfterLastSection, BlankLineAfterSection, BlankLineBeforeSection, + CapitalizeSectionName, DashedUnderlineAfterSection, DocumentAllArguments, + NewLineAfterSectionName, NoBlankLinesBetweenHeaderAndContent, NonEmptySection, + SectionNameEndsInColon, SectionNotOverIndented, SectionUnderlineAfterName, + SectionUnderlineMatchesSectionLength, SectionUnderlineNotOverIndented, +}; +pub use starts_with_this::{starts_with_this, NoThisPrefix}; +pub use triple_quotes::{triple_quotes, UsesTripleQuotes}; mod backslashes; mod blank_after_summary; @@ -31,7 +48,7 @@ mod multi_line_summary_start; mod newline_after_last_paragraph; mod no_signature; mod no_surrounding_whitespace; -pub mod non_imperative_mood; +mod non_imperative_mood; mod not_empty; mod not_missing; mod one_liner; diff --git a/src/rules/pydocstyle/rules/multi_line_summary_start.rs b/src/rules/pydocstyle/rules/multi_line_summary_start.rs index 891726ea3d..2b7c5b2f4c 100644 --- a/src/rules/pydocstyle/rules/multi_line_summary_start.rs +++ b/src/rules/pydocstyle/rules/multi_line_summary_start.rs @@ -7,7 +7,38 @@ use crate::fix::Fix; use crate::message::Location; use crate::registry::{Diagnostic, Rule}; use crate::rules::pydocstyle::helpers::leading_quote; -use crate::violations; +use crate::violation::AlwaysAutofixableViolation; + +use crate::define_violation; +use ruff_macros::derive_message_formats; + +define_violation!( + pub struct MultiLineSummaryFirstLine; +); +impl AlwaysAutofixableViolation for MultiLineSummaryFirstLine { + #[derive_message_formats] + fn message(&self) -> String { + format!("Multi-line docstring summary should start at the first line") + } + + fn autofix_title(&self) -> String { + "Remove whitespace after opening quotes".to_string() + } +} + +define_violation!( + pub struct MultiLineSummarySecondLine; +); +impl AlwaysAutofixableViolation for MultiLineSummarySecondLine { + #[derive_message_formats] + fn message(&self) -> String { + format!("Multi-line docstring summary should start at the second line") + } + + fn autofix_title(&self) -> String { + "Insert line break and indentation after opening quotes".to_string() + } +} /// D212, D213 pub fn multi_line_summary_start(checker: &mut Checker, docstring: &Docstring) { @@ -31,7 +62,7 @@ pub fn multi_line_summary_start(checker: &mut Checker, docstring: &Docstring) { .enabled(&Rule::MultiLineSummaryFirstLine) { let mut diagnostic = Diagnostic::new( - violations::MultiLineSummaryFirstLine, + MultiLineSummaryFirstLine, Range::from_located(docstring.expr), ); if checker.patch(diagnostic.kind.rule()) { @@ -58,7 +89,7 @@ pub fn multi_line_summary_start(checker: &mut Checker, docstring: &Docstring) { .enabled(&Rule::MultiLineSummarySecondLine) { let mut diagnostic = Diagnostic::new( - violations::MultiLineSummarySecondLine, + MultiLineSummarySecondLine, Range::from_located(docstring.expr), ); if checker.patch(diagnostic.kind.rule()) { diff --git a/src/rules/pydocstyle/rules/newline_after_last_paragraph.rs b/src/rules/pydocstyle/rules/newline_after_last_paragraph.rs index 3c5c5df8a7..257f86d29a 100644 --- a/src/rules/pydocstyle/rules/newline_after_last_paragraph.rs +++ b/src/rules/pydocstyle/rules/newline_after_last_paragraph.rs @@ -6,7 +6,24 @@ use crate::docstrings::definition::Docstring; use crate::fix::Fix; use crate::message::Location; use crate::registry::Diagnostic; -use crate::violations; +use crate::violation::AlwaysAutofixableViolation; + +use crate::define_violation; +use ruff_macros::derive_message_formats; + +define_violation!( + pub struct NewLineAfterLastParagraph; +); +impl AlwaysAutofixableViolation for NewLineAfterLastParagraph { + #[derive_message_formats] + fn message(&self) -> String { + format!("Multi-line docstring closing quotes should be on a separate line") + } + + fn autofix_title(&self) -> String { + "Move closing quotes to new line".to_string() + } +} /// D209 pub fn newline_after_last_paragraph(checker: &mut Checker, docstring: &Docstring) { @@ -22,7 +39,7 @@ pub fn newline_after_last_paragraph(checker: &mut Checker, docstring: &Docstring if let Some(last_line) = contents.lines().last().map(str::trim) { if last_line != "\"\"\"" && last_line != "'''" { let mut diagnostic = Diagnostic::new( - violations::NewLineAfterLastParagraph, + NewLineAfterLastParagraph, Range::from_located(docstring.expr), ); if checker.patch(diagnostic.kind.rule()) { diff --git a/src/rules/pydocstyle/rules/no_signature.rs b/src/rules/pydocstyle/rules/no_signature.rs index 9f547bf790..5979dd5c3c 100644 --- a/src/rules/pydocstyle/rules/no_signature.rs +++ b/src/rules/pydocstyle/rules/no_signature.rs @@ -1,10 +1,22 @@ +use crate::define_violation; +use crate::violation::Violation; +use ruff_macros::derive_message_formats; use rustpython_ast::StmtKind; use crate::ast::types::Range; use crate::checkers::ast::Checker; use crate::docstrings::definition::{DefinitionKind, Docstring}; use crate::registry::Diagnostic; -use crate::violations; + +define_violation!( + pub struct NoSignature; +); +impl Violation for NoSignature { + #[derive_message_formats] + fn message(&self) -> String { + format!("First line should not be the function's signature") + } +} /// D402 pub fn no_signature(checker: &mut Checker, docstring: &Docstring) { @@ -28,7 +40,7 @@ pub fn no_signature(checker: &mut Checker, docstring: &Docstring) { return; }; checker.diagnostics.push(Diagnostic::new( - violations::NoSignature, + NoSignature, Range::from_located(docstring.expr), )); } diff --git a/src/rules/pydocstyle/rules/no_surrounding_whitespace.rs b/src/rules/pydocstyle/rules/no_surrounding_whitespace.rs index 1ef1c42516..0b74369050 100644 --- a/src/rules/pydocstyle/rules/no_surrounding_whitespace.rs +++ b/src/rules/pydocstyle/rules/no_surrounding_whitespace.rs @@ -6,7 +6,24 @@ use crate::fix::Fix; use crate::message::Location; use crate::registry::Diagnostic; use crate::rules::pydocstyle::helpers::leading_quote; -use crate::violations; +use crate::violation::AlwaysAutofixableViolation; + +use crate::define_violation; +use ruff_macros::derive_message_formats; + +define_violation!( + pub struct NoSurroundingWhitespace; +); +impl AlwaysAutofixableViolation for NoSurroundingWhitespace { + #[derive_message_formats] + fn message(&self) -> String { + format!("No whitespaces allowed surrounding docstring text") + } + + fn autofix_title(&self) -> String { + "Trim surrounding whitespace".to_string() + } +} /// D210 pub fn no_surrounding_whitespace(checker: &mut Checker, docstring: &Docstring) { @@ -24,10 +41,8 @@ pub fn no_surrounding_whitespace(checker: &mut Checker, docstring: &Docstring) { if line == trimmed { return; } - let mut diagnostic = Diagnostic::new( - violations::NoSurroundingWhitespace, - Range::from_located(docstring.expr), - ); + let mut diagnostic = + Diagnostic::new(NoSurroundingWhitespace, Range::from_located(docstring.expr)); if checker.patch(diagnostic.kind.rule()) { if let Some(pattern) = leading_quote(contents) { // If removing whitespace would lead to an invalid string of quote diff --git a/src/rules/pydocstyle/rules/not_empty.rs b/src/rules/pydocstyle/rules/not_empty.rs index 585e3bf431..9942dddf11 100644 --- a/src/rules/pydocstyle/rules/not_empty.rs +++ b/src/rules/pydocstyle/rules/not_empty.rs @@ -2,7 +2,20 @@ use crate::ast::types::Range; use crate::checkers::ast::Checker; use crate::docstrings::definition::Docstring; use crate::registry::{Diagnostic, Rule}; -use crate::violations; +use crate::violation::Violation; + +use crate::define_violation; +use ruff_macros::derive_message_formats; + +define_violation!( + pub struct NonEmpty; +); +impl Violation for NonEmpty { + #[derive_message_formats] + fn message(&self) -> String { + format!("Docstring is empty") + } +} /// D419 pub fn not_empty(checker: &mut Checker, docstring: &Docstring) -> bool { @@ -12,7 +25,7 @@ pub fn not_empty(checker: &mut Checker, docstring: &Docstring) -> bool { if checker.settings.rules.enabled(&Rule::NonEmpty) { checker.diagnostics.push(Diagnostic::new( - violations::NonEmpty, + NonEmpty, Range::from_located(docstring.expr), )); } diff --git a/src/rules/pydocstyle/rules/not_missing.rs b/src/rules/pydocstyle/rules/not_missing.rs index 4ac8c80389..468cf99afc 100644 --- a/src/rules/pydocstyle/rules/not_missing.rs +++ b/src/rules/pydocstyle/rules/not_missing.rs @@ -5,8 +5,91 @@ use crate::checkers::ast::Checker; use crate::docstrings::definition::{Definition, DefinitionKind}; use crate::message::Location; use crate::registry::{Diagnostic, Rule}; -use crate::violations; +use crate::violation::Violation; + +use crate::define_violation; use crate::visibility::{is_call, is_init, is_magic, is_new, is_overload, is_override, Visibility}; +use ruff_macros::derive_message_formats; + +define_violation!( + pub struct PublicModule; +); +impl Violation for PublicModule { + #[derive_message_formats] + fn message(&self) -> String { + format!("Missing docstring in public module") + } +} + +define_violation!( + pub struct PublicClass; +); +impl Violation for PublicClass { + #[derive_message_formats] + fn message(&self) -> String { + format!("Missing docstring in public class") + } +} + +define_violation!( + pub struct PublicMethod; +); +impl Violation for PublicMethod { + #[derive_message_formats] + fn message(&self) -> String { + format!("Missing docstring in public method") + } +} + +define_violation!( + pub struct PublicFunction; +); +impl Violation for PublicFunction { + #[derive_message_formats] + fn message(&self) -> String { + format!("Missing docstring in public function") + } +} + +define_violation!( + pub struct PublicPackage; +); +impl Violation for PublicPackage { + #[derive_message_formats] + fn message(&self) -> String { + format!("Missing docstring in public package") + } +} + +define_violation!( + pub struct MagicMethod; +); +impl Violation for MagicMethod { + #[derive_message_formats] + fn message(&self) -> String { + format!("Missing docstring in magic method") + } +} + +define_violation!( + pub struct PublicNestedClass; +); +impl Violation for PublicNestedClass { + #[derive_message_formats] + fn message(&self) -> String { + format!("Missing docstring in public nested class") + } +} + +define_violation!( + pub struct PublicInit; +); +impl Violation for PublicInit { + #[derive_message_formats] + fn message(&self) -> String { + format!("Missing docstring in `__init__`") + } +} /// D100, D101, D102, D103, D104, D105, D106, D107 pub fn not_missing( @@ -22,7 +105,7 @@ pub fn not_missing( DefinitionKind::Module => { if checker.settings.rules.enabled(&Rule::PublicModule) { checker.diagnostics.push(Diagnostic::new( - violations::PublicModule, + PublicModule, Range::new(Location::new(1, 0), Location::new(1, 0)), )); } @@ -31,7 +114,7 @@ pub fn not_missing( DefinitionKind::Package => { if checker.settings.rules.enabled(&Rule::PublicPackage) { checker.diagnostics.push(Diagnostic::new( - violations::PublicPackage, + PublicPackage, Range::new(Location::new(1, 0), Location::new(1, 0)), )); } @@ -40,7 +123,7 @@ pub fn not_missing( DefinitionKind::Class(stmt) => { if checker.settings.rules.enabled(&Rule::PublicClass) { checker.diagnostics.push(Diagnostic::new( - violations::PublicClass, + PublicClass, identifier_range(stmt, checker.locator), )); } @@ -49,7 +132,7 @@ pub fn not_missing( DefinitionKind::NestedClass(stmt) => { if checker.settings.rules.enabled(&Rule::PublicNestedClass) { checker.diagnostics.push(Diagnostic::new( - violations::PublicNestedClass, + PublicNestedClass, identifier_range(stmt, checker.locator), )); } @@ -61,7 +144,7 @@ pub fn not_missing( } else { if checker.settings.rules.enabled(&Rule::PublicFunction) { checker.diagnostics.push(Diagnostic::new( - violations::PublicFunction, + PublicFunction, identifier_range(stmt, checker.locator), )); } @@ -76,7 +159,7 @@ pub fn not_missing( } else if is_init(cast::name(stmt)) { if checker.settings.rules.enabled(&Rule::PublicInit) { checker.diagnostics.push(Diagnostic::new( - violations::PublicInit, + PublicInit, identifier_range(stmt, checker.locator), )); } @@ -84,7 +167,7 @@ pub fn not_missing( } else if is_new(cast::name(stmt)) || is_call(cast::name(stmt)) { if checker.settings.rules.enabled(&Rule::PublicMethod) { checker.diagnostics.push(Diagnostic::new( - violations::PublicMethod, + PublicMethod, identifier_range(stmt, checker.locator), )); } @@ -92,7 +175,7 @@ pub fn not_missing( } else if is_magic(cast::name(stmt)) { if checker.settings.rules.enabled(&Rule::MagicMethod) { checker.diagnostics.push(Diagnostic::new( - violations::MagicMethod, + MagicMethod, identifier_range(stmt, checker.locator), )); } @@ -100,7 +183,7 @@ pub fn not_missing( } else { if checker.settings.rules.enabled(&Rule::PublicMethod) { checker.diagnostics.push(Diagnostic::new( - violations::PublicMethod, + PublicMethod, identifier_range(stmt, checker.locator), )); } diff --git a/src/rules/pydocstyle/rules/one_liner.rs b/src/rules/pydocstyle/rules/one_liner.rs index 459d36b936..0b21b08b66 100644 --- a/src/rules/pydocstyle/rules/one_liner.rs +++ b/src/rules/pydocstyle/rules/one_liner.rs @@ -5,7 +5,24 @@ use crate::docstrings::definition::Docstring; use crate::fix::Fix; use crate::registry::Diagnostic; use crate::rules::pydocstyle::helpers; -use crate::violations; +use crate::violation::AlwaysAutofixableViolation; + +use crate::define_violation; +use ruff_macros::derive_message_formats; + +define_violation!( + pub struct FitsOnOneLine; +); +impl AlwaysAutofixableViolation for FitsOnOneLine { + #[derive_message_formats] + fn message(&self) -> String { + format!("One-line docstring should fit on one line") + } + + fn autofix_title(&self) -> String { + "Reformat to one line".to_string() + } +} /// D200 pub fn one_liner(checker: &mut Checker, docstring: &Docstring) { @@ -22,10 +39,7 @@ pub fn one_liner(checker: &mut Checker, docstring: &Docstring) { } if non_empty_line_count == 1 && line_count > 1 { - let mut diagnostic = Diagnostic::new( - violations::FitsOnOneLine, - Range::from_located(docstring.expr), - ); + let mut diagnostic = Diagnostic::new(FitsOnOneLine, Range::from_located(docstring.expr)); if checker.patch(diagnostic.kind.rule()) { if let (Some(leading), Some(trailing)) = ( helpers::leading_quote(docstring.contents), diff --git a/src/rules/pydocstyle/rules/sections.rs b/src/rules/pydocstyle/rules/sections.rs index 2edf702509..0ef42dad2f 100644 --- a/src/rules/pydocstyle/rules/sections.rs +++ b/src/rules/pydocstyle/rules/sections.rs @@ -1,6 +1,9 @@ +use crate::define_violation; +use crate::violation::{AlwaysAutofixableViolation, Violation}; use itertools::Itertools; use once_cell::sync::Lazy; use regex::Regex; +use ruff_macros::derive_message_formats; use rustc_hash::FxHashSet; use rustpython_ast::StmtKind; @@ -15,9 +18,256 @@ use crate::fix::Fix; use crate::message::Location; use crate::registry::{Diagnostic, Rule}; use crate::rules::pydocstyle::settings::Convention; -use crate::violations; + use crate::visibility::is_staticmethod; +define_violation!( + pub struct SectionNotOverIndented { + pub name: String, + } +); +impl AlwaysAutofixableViolation for SectionNotOverIndented { + #[derive_message_formats] + fn message(&self) -> String { + let SectionNotOverIndented { name } = self; + format!("Section is over-indented (\"{name}\")") + } + + fn autofix_title(&self) -> String { + let SectionNotOverIndented { name } = self; + format!("Remove over-indentation from \"{name}\"") + } +} + +define_violation!( + pub struct SectionUnderlineNotOverIndented { + pub name: String, + } +); +impl AlwaysAutofixableViolation for SectionUnderlineNotOverIndented { + #[derive_message_formats] + fn message(&self) -> String { + let SectionUnderlineNotOverIndented { name } = self; + format!("Section underline is over-indented (\"{name}\")") + } + + fn autofix_title(&self) -> String { + let SectionUnderlineNotOverIndented { name } = self; + format!("Remove over-indentation from \"{name}\" underline") + } +} + +define_violation!( + pub struct CapitalizeSectionName { + pub name: String, + } +); +impl AlwaysAutofixableViolation for CapitalizeSectionName { + #[derive_message_formats] + fn message(&self) -> String { + let CapitalizeSectionName { name } = self; + format!("Section name should be properly capitalized (\"{name}\")") + } + + fn autofix_title(&self) -> String { + let CapitalizeSectionName { name } = self; + format!("Capitalize \"{name}\"") + } +} + +define_violation!( + pub struct NewLineAfterSectionName { + pub name: String, + } +); +impl AlwaysAutofixableViolation for NewLineAfterSectionName { + #[derive_message_formats] + fn message(&self) -> String { + let NewLineAfterSectionName { name } = self; + format!("Section name should end with a newline (\"{name}\")") + } + + fn autofix_title(&self) -> String { + let NewLineAfterSectionName { name } = self; + format!("Add newline after \"{name}\"") + } +} + +define_violation!( + pub struct DashedUnderlineAfterSection { + pub name: String, + } +); +impl AlwaysAutofixableViolation for DashedUnderlineAfterSection { + #[derive_message_formats] + fn message(&self) -> String { + let DashedUnderlineAfterSection { name } = self; + format!("Missing dashed underline after section (\"{name}\")") + } + + fn autofix_title(&self) -> String { + let DashedUnderlineAfterSection { name } = self; + format!("Add dashed line under \"{name}\"") + } +} + +define_violation!( + pub struct SectionUnderlineAfterName { + pub name: String, + } +); +impl AlwaysAutofixableViolation for SectionUnderlineAfterName { + #[derive_message_formats] + fn message(&self) -> String { + let SectionUnderlineAfterName { name } = self; + format!("Section underline should be in the line following the section's name (\"{name}\")") + } + + fn autofix_title(&self) -> String { + let SectionUnderlineAfterName { name } = self; + format!("Add underline to \"{name}\"") + } +} + +define_violation!( + pub struct SectionUnderlineMatchesSectionLength { + pub name: String, + } +); +impl AlwaysAutofixableViolation for SectionUnderlineMatchesSectionLength { + #[derive_message_formats] + fn message(&self) -> String { + let SectionUnderlineMatchesSectionLength { name } = self; + format!("Section underline should match the length of its name (\"{name}\")") + } + + fn autofix_title(&self) -> String { + let SectionUnderlineMatchesSectionLength { name } = self; + format!("Adjust underline length to match \"{name}\"") + } +} + +define_violation!( + pub struct BlankLineAfterSection { + pub name: String, + } +); +impl AlwaysAutofixableViolation for BlankLineAfterSection { + #[derive_message_formats] + fn message(&self) -> String { + let BlankLineAfterSection { name } = self; + format!("Missing blank line after section (\"{name}\")") + } + + fn autofix_title(&self) -> String { + let BlankLineAfterSection { name } = self; + format!("Add blank line after \"{name}\"") + } +} + +define_violation!( + pub struct BlankLineBeforeSection { + pub name: String, + } +); +impl AlwaysAutofixableViolation for BlankLineBeforeSection { + #[derive_message_formats] + fn message(&self) -> String { + let BlankLineBeforeSection { name } = self; + format!("Missing blank line before section (\"{name}\")") + } + + fn autofix_title(&self) -> String { + let BlankLineBeforeSection { name } = self; + format!("Add blank line before \"{name}\"") + } +} + +define_violation!( + pub struct BlankLineAfterLastSection { + pub name: String, + } +); +impl AlwaysAutofixableViolation for BlankLineAfterLastSection { + #[derive_message_formats] + fn message(&self) -> String { + let BlankLineAfterLastSection { name } = self; + format!("Missing blank line after last section (\"{name}\")") + } + + fn autofix_title(&self) -> String { + let BlankLineAfterLastSection { name } = self; + format!("Add blank line after \"{name}\"") + } +} + +define_violation!( + pub struct NonEmptySection { + pub name: String, + } +); +impl Violation for NonEmptySection { + #[derive_message_formats] + fn message(&self) -> String { + let NonEmptySection { name } = self; + format!("Section has no content (\"{name}\")") + } +} + +define_violation!( + pub struct SectionNameEndsInColon { + pub name: String, + } +); +impl AlwaysAutofixableViolation for SectionNameEndsInColon { + #[derive_message_formats] + fn message(&self) -> String { + let SectionNameEndsInColon { name } = self; + format!("Section name should end with a colon (\"{name}\")") + } + + fn autofix_title(&self) -> String { + let SectionNameEndsInColon { name } = self; + format!("Add colon to \"{name}\"") + } +} + +define_violation!( + pub struct DocumentAllArguments { + pub names: Vec, + } +); +impl Violation for DocumentAllArguments { + #[derive_message_formats] + fn message(&self) -> String { + let DocumentAllArguments { names } = self; + if names.len() == 1 { + let name = &names[0]; + format!("Missing argument description in the docstring: `{name}`") + } else { + let names = names.iter().map(|name| format!("`{name}`")).join(", "); + format!("Missing argument descriptions in the docstring: {names}") + } + } +} + +define_violation!( + pub struct NoBlankLinesBetweenHeaderAndContent { + pub name: String, + } +); +impl AlwaysAutofixableViolation for NoBlankLinesBetweenHeaderAndContent { + #[derive_message_formats] + fn message(&self) -> String { + let NoBlankLinesBetweenHeaderAndContent { name } = self; + format!("No blank lines allowed between a section header and its content (\"{name}\")") + } + + fn autofix_title(&self) -> String { + "Remove blank line(s)".to_string() + } +} + /// D212, D214, D215, D405, D406, D407, D408, D409, D410, D411, D412, D413, /// D414, D416, D417 pub fn sections(checker: &mut Checker, docstring: &Docstring, convention: Option<&Convention>) { @@ -78,7 +328,7 @@ fn blanks_and_section_underline( .enabled(&Rule::DashedUnderlineAfterSection) { let mut diagnostic = Diagnostic::new( - violations::DashedUnderlineAfterSection { + DashedUnderlineAfterSection { name: context.section_name.to_string(), }, Range::from_located(docstring.expr), @@ -103,7 +353,7 @@ fn blanks_and_section_underline( } if checker.settings.rules.enabled(&Rule::NonEmptySection) { checker.diagnostics.push(Diagnostic::new( - violations::NonEmptySection { + NonEmptySection { name: context.section_name.to_string(), }, Range::from_located(docstring.expr), @@ -125,7 +375,7 @@ fn blanks_and_section_underline( .enabled(&Rule::SectionUnderlineAfterName) { let mut diagnostic = Diagnostic::new( - violations::SectionUnderlineAfterName { + SectionUnderlineAfterName { name: context.section_name.to_string(), }, Range::from_located(docstring.expr), @@ -163,7 +413,7 @@ fn blanks_and_section_underline( .enabled(&Rule::SectionUnderlineMatchesSectionLength) { let mut diagnostic = Diagnostic::new( - violations::SectionUnderlineMatchesSectionLength { + SectionUnderlineMatchesSectionLength { name: context.section_name.to_string(), }, Range::from_located(docstring.expr), @@ -207,7 +457,7 @@ fn blanks_and_section_underline( let leading_space = whitespace::leading_space(non_empty_line); if leading_space.len() > docstring.indentation.len() { let mut diagnostic = Diagnostic::new( - violations::SectionUnderlineNotOverIndented { + SectionUnderlineNotOverIndented { name: context.section_name.to_string(), }, Range::from_located(docstring.expr), @@ -249,7 +499,7 @@ fn blanks_and_section_underline( if blank_lines_after_dashes == rest_of_lines.len() { if checker.settings.rules.enabled(&Rule::NonEmptySection) { checker.diagnostics.push(Diagnostic::new( - violations::NonEmptySection { + NonEmptySection { name: context.section_name.to_string(), }, Range::from_located(docstring.expr), @@ -262,7 +512,7 @@ fn blanks_and_section_underline( .enabled(&Rule::NoBlankLinesBetweenHeaderAndContent) { let mut diagnostic = Diagnostic::new( - violations::NoBlankLinesBetweenHeaderAndContent { + NoBlankLinesBetweenHeaderAndContent { name: context.section_name.to_string(), }, Range::from_located(docstring.expr), @@ -294,7 +544,7 @@ fn blanks_and_section_underline( } else { if checker.settings.rules.enabled(&Rule::NonEmptySection) { checker.diagnostics.push(Diagnostic::new( - violations::NonEmptySection { + NonEmptySection { name: context.section_name.to_string(), }, Range::from_located(docstring.expr), @@ -308,7 +558,7 @@ fn blanks_and_section_underline( .enabled(&Rule::DashedUnderlineAfterSection) { let mut diagnostic = Diagnostic::new( - violations::DashedUnderlineAfterSection { + DashedUnderlineAfterSection { name: context.section_name.to_string(), }, Range::from_located(docstring.expr), @@ -338,7 +588,7 @@ fn blanks_and_section_underline( .enabled(&Rule::NoBlankLinesBetweenHeaderAndContent) { let mut diagnostic = Diagnostic::new( - violations::NoBlankLinesBetweenHeaderAndContent { + NoBlankLinesBetweenHeaderAndContent { name: context.section_name.to_string(), }, Range::from_located(docstring.expr), @@ -379,7 +629,7 @@ fn common_section( .contains(capitalized_section_name.as_str()) { let mut diagnostic = Diagnostic::new( - violations::CapitalizeSectionName { + CapitalizeSectionName { name: context.section_name.to_string(), }, Range::from_located(docstring.expr), @@ -417,7 +667,7 @@ fn common_section( let leading_space = whitespace::leading_space(context.line); if leading_space.len() > docstring.indentation.len() { let mut diagnostic = Diagnostic::new( - violations::SectionNotOverIndented { + SectionNotOverIndented { name: context.section_name.to_string(), }, Range::from_located(docstring.expr), @@ -450,7 +700,7 @@ fn common_section( .enabled(&Rule::BlankLineAfterLastSection) { let mut diagnostic = Diagnostic::new( - violations::BlankLineAfterLastSection { + BlankLineAfterLastSection { name: context.section_name.to_string(), }, Range::from_located(docstring.expr), @@ -473,7 +723,7 @@ fn common_section( } else { if checker.settings.rules.enabled(&Rule::BlankLineAfterSection) { let mut diagnostic = Diagnostic::new( - violations::BlankLineAfterSection { + BlankLineAfterSection { name: context.section_name.to_string(), }, Range::from_located(docstring.expr), @@ -503,7 +753,7 @@ fn common_section( { if !context.previous_line.is_empty() { let mut diagnostic = Diagnostic::new( - violations::BlankLineBeforeSection { + BlankLineBeforeSection { name: context.section_name.to_string(), }, Range::from_located(docstring.expr), @@ -588,7 +838,7 @@ fn missing_args(checker: &mut Checker, docstring: &Docstring, docstrings_args: & if !missing_arg_names.is_empty() { let names = missing_arg_names.into_iter().sorted().collect(); checker.diagnostics.push(Diagnostic::new( - violations::DocumentAllArguments { names }, + DocumentAllArguments { names }, Range::from_located(parent), )); } @@ -698,7 +948,7 @@ fn numpy_section(checker: &mut Checker, docstring: &Docstring, context: &Section .unwrap(); if !suffix.is_empty() { let mut diagnostic = Diagnostic::new( - violations::NewLineAfterSectionName { + NewLineAfterSectionName { name: context.section_name.to_string(), }, Range::from_located(docstring.expr), @@ -750,7 +1000,7 @@ fn google_section(checker: &mut Checker, docstring: &Docstring, context: &Sectio .unwrap(); if suffix != ":" { let mut diagnostic = Diagnostic::new( - violations::SectionNameEndsInColon { + SectionNameEndsInColon { name: context.section_name.to_string(), }, Range::from_located(docstring.expr), diff --git a/src/rules/pydocstyle/rules/starts_with_this.rs b/src/rules/pydocstyle/rules/starts_with_this.rs index 43d0005b1c..fa854becee 100644 --- a/src/rules/pydocstyle/rules/starts_with_this.rs +++ b/src/rules/pydocstyle/rules/starts_with_this.rs @@ -3,7 +3,20 @@ use crate::checkers::ast::Checker; use crate::docstrings::definition::Docstring; use crate::registry::Diagnostic; use crate::rules::pydocstyle::helpers::normalize_word; -use crate::violations; +use crate::violation::Violation; + +use crate::define_violation; +use ruff_macros::derive_message_formats; + +define_violation!( + pub struct NoThisPrefix; +); +impl Violation for NoThisPrefix { + #[derive_message_formats] + fn message(&self) -> String { + format!(r#"First word of the docstring should not be "This""#) + } +} /// D404 pub fn starts_with_this(checker: &mut Checker, docstring: &Docstring) { @@ -21,7 +34,7 @@ pub fn starts_with_this(checker: &mut Checker, docstring: &Docstring) { return; } checker.diagnostics.push(Diagnostic::new( - violations::NoThisPrefix, + NoThisPrefix, Range::from_located(docstring.expr), )); } diff --git a/src/rules/pydocstyle/rules/triple_quotes.rs b/src/rules/pydocstyle/rules/triple_quotes.rs index ab52e8b8ae..8f01ea350b 100644 --- a/src/rules/pydocstyle/rules/triple_quotes.rs +++ b/src/rules/pydocstyle/rules/triple_quotes.rs @@ -2,7 +2,20 @@ use crate::ast::types::Range; use crate::checkers::ast::Checker; use crate::docstrings::definition::Docstring; use crate::registry::Diagnostic; -use crate::violations; +use crate::violation::Violation; + +use crate::define_violation; +use ruff_macros::derive_message_formats; + +define_violation!( + pub struct UsesTripleQuotes; +); +impl Violation for UsesTripleQuotes { + #[derive_message_formats] + fn message(&self) -> String { + format!(r#"Use """triple double quotes""""#) + } +} /// D300 pub fn triple_quotes(checker: &mut Checker, docstring: &Docstring) { @@ -29,7 +42,7 @@ pub fn triple_quotes(checker: &mut Checker, docstring: &Docstring) { }; if !starts_with_triple { checker.diagnostics.push(Diagnostic::new( - violations::UsesTripleQuotes, + UsesTripleQuotes, Range::from_located(docstring.expr), )); } diff --git a/src/violations.rs b/src/violations.rs index 340140f3c1..aa49956570 100644 --- a/src/violations.rs +++ b/src/violations.rs @@ -6,7 +6,7 @@ use serde::{Deserialize, Serialize}; use crate::define_violation; -use crate::violation::{AlwaysAutofixableViolation, AutofixKind, Availability, Violation}; +use crate::violation::{AlwaysAutofixableViolation, Violation}; // mccabe @@ -193,655 +193,6 @@ impl Violation for SysVersionSlice1Referenced { } } -// pydocstyle - -define_violation!( - pub struct PublicModule; -); -impl Violation for PublicModule { - #[derive_message_formats] - fn message(&self) -> String { - format!("Missing docstring in public module") - } -} - -define_violation!( - pub struct PublicClass; -); -impl Violation for PublicClass { - #[derive_message_formats] - fn message(&self) -> String { - format!("Missing docstring in public class") - } -} - -define_violation!( - pub struct PublicMethod; -); -impl Violation for PublicMethod { - #[derive_message_formats] - fn message(&self) -> String { - format!("Missing docstring in public method") - } -} - -define_violation!( - pub struct PublicFunction; -); -impl Violation for PublicFunction { - #[derive_message_formats] - fn message(&self) -> String { - format!("Missing docstring in public function") - } -} - -define_violation!( - pub struct PublicPackage; -); -impl Violation for PublicPackage { - #[derive_message_formats] - fn message(&self) -> String { - format!("Missing docstring in public package") - } -} - -define_violation!( - pub struct MagicMethod; -); -impl Violation for MagicMethod { - #[derive_message_formats] - fn message(&self) -> String { - format!("Missing docstring in magic method") - } -} - -define_violation!( - pub struct PublicNestedClass; -); -impl Violation for PublicNestedClass { - #[derive_message_formats] - fn message(&self) -> String { - format!("Missing docstring in public nested class") - } -} - -define_violation!( - pub struct PublicInit; -); -impl Violation for PublicInit { - #[derive_message_formats] - fn message(&self) -> String { - format!("Missing docstring in `__init__`") - } -} - -define_violation!( - pub struct FitsOnOneLine; -); -impl AlwaysAutofixableViolation for FitsOnOneLine { - #[derive_message_formats] - fn message(&self) -> String { - format!("One-line docstring should fit on one line") - } - - fn autofix_title(&self) -> String { - "Reformat to one line".to_string() - } -} - -define_violation!( - pub struct NoBlankLineBeforeFunction { - pub num_lines: usize, - } -); -impl AlwaysAutofixableViolation for NoBlankLineBeforeFunction { - #[derive_message_formats] - fn message(&self) -> String { - let NoBlankLineBeforeFunction { num_lines } = self; - format!("No blank lines allowed before function docstring (found {num_lines})") - } - - fn autofix_title(&self) -> String { - "Remove blank line(s) before function docstring".to_string() - } -} - -define_violation!( - pub struct NoBlankLineAfterFunction { - pub num_lines: usize, - } -); -impl AlwaysAutofixableViolation for NoBlankLineAfterFunction { - #[derive_message_formats] - fn message(&self) -> String { - let NoBlankLineAfterFunction { num_lines } = self; - format!("No blank lines allowed after function docstring (found {num_lines})") - } - - fn autofix_title(&self) -> String { - "Remove blank line(s) after function docstring".to_string() - } -} - -define_violation!( - pub struct OneBlankLineBeforeClass { - pub lines: usize, - } -); -impl AlwaysAutofixableViolation for OneBlankLineBeforeClass { - #[derive_message_formats] - fn message(&self) -> String { - format!("1 blank line required before class docstring") - } - - fn autofix_title(&self) -> String { - "Insert 1 blank line before class docstring".to_string() - } -} - -define_violation!( - pub struct OneBlankLineAfterClass { - pub lines: usize, - } -); -impl AlwaysAutofixableViolation for OneBlankLineAfterClass { - #[derive_message_formats] - fn message(&self) -> String { - format!("1 blank line required after class docstring") - } - - fn autofix_title(&self) -> String { - "Insert 1 blank line after class docstring".to_string() - } -} - -define_violation!( - pub struct BlankLineAfterSummary { - pub num_lines: usize, - } -); -fn fmt_blank_line_after_summary_autofix_msg(_: &BlankLineAfterSummary) -> String { - "Insert single blank line".to_string() -} -impl Violation for BlankLineAfterSummary { - const AUTOFIX: Option = Some(AutofixKind::new(Availability::Always)); - - #[derive_message_formats] - fn message(&self) -> String { - let BlankLineAfterSummary { num_lines } = self; - if *num_lines == 0 { - format!("1 blank line required between summary line and description") - } else { - format!( - "1 blank line required between summary line and description (found {num_lines})" - ) - } - } - - fn autofix_title_formatter(&self) -> Option String> { - let BlankLineAfterSummary { num_lines } = self; - if *num_lines > 0 { - return Some(fmt_blank_line_after_summary_autofix_msg); - } - None - } -} - -define_violation!( - pub struct IndentWithSpaces; -); -impl Violation for IndentWithSpaces { - #[derive_message_formats] - fn message(&self) -> String { - format!("Docstring should be indented with spaces, not tabs") - } -} - -define_violation!( - pub struct NoUnderIndentation; -); -impl AlwaysAutofixableViolation for NoUnderIndentation { - #[derive_message_formats] - fn message(&self) -> String { - format!("Docstring is under-indented") - } - - fn autofix_title(&self) -> String { - "Increase indentation".to_string() - } -} - -define_violation!( - pub struct NoOverIndentation; -); -impl AlwaysAutofixableViolation for NoOverIndentation { - #[derive_message_formats] - fn message(&self) -> String { - format!("Docstring is over-indented") - } - - fn autofix_title(&self) -> String { - "Remove over-indentation".to_string() - } -} - -define_violation!( - pub struct NewLineAfterLastParagraph; -); -impl AlwaysAutofixableViolation for NewLineAfterLastParagraph { - #[derive_message_formats] - fn message(&self) -> String { - format!("Multi-line docstring closing quotes should be on a separate line") - } - - fn autofix_title(&self) -> String { - "Move closing quotes to new line".to_string() - } -} - -define_violation!( - pub struct NoSurroundingWhitespace; -); -impl AlwaysAutofixableViolation for NoSurroundingWhitespace { - #[derive_message_formats] - fn message(&self) -> String { - format!("No whitespaces allowed surrounding docstring text") - } - - fn autofix_title(&self) -> String { - "Trim surrounding whitespace".to_string() - } -} - -define_violation!( - pub struct NoBlankLineBeforeClass { - pub lines: usize, - } -); -impl AlwaysAutofixableViolation for NoBlankLineBeforeClass { - #[derive_message_formats] - fn message(&self) -> String { - format!("No blank lines allowed before class docstring") - } - - fn autofix_title(&self) -> String { - "Remove blank line(s) before class docstring".to_string() - } -} - -define_violation!( - pub struct MultiLineSummaryFirstLine; -); -impl AlwaysAutofixableViolation for MultiLineSummaryFirstLine { - #[derive_message_formats] - fn message(&self) -> String { - format!("Multi-line docstring summary should start at the first line") - } - - fn autofix_title(&self) -> String { - "Remove whitespace after opening quotes".to_string() - } -} - -define_violation!( - pub struct MultiLineSummarySecondLine; -); -impl AlwaysAutofixableViolation for MultiLineSummarySecondLine { - #[derive_message_formats] - fn message(&self) -> String { - format!("Multi-line docstring summary should start at the second line") - } - - fn autofix_title(&self) -> String { - "Insert line break and indentation after opening quotes".to_string() - } -} - -define_violation!( - pub struct SectionNotOverIndented { - pub name: String, - } -); -impl AlwaysAutofixableViolation for SectionNotOverIndented { - #[derive_message_formats] - fn message(&self) -> String { - let SectionNotOverIndented { name } = self; - format!("Section is over-indented (\"{name}\")") - } - - fn autofix_title(&self) -> String { - let SectionNotOverIndented { name } = self; - format!("Remove over-indentation from \"{name}\"") - } -} - -define_violation!( - pub struct SectionUnderlineNotOverIndented { - pub name: String, - } -); -impl AlwaysAutofixableViolation for SectionUnderlineNotOverIndented { - #[derive_message_formats] - fn message(&self) -> String { - let SectionUnderlineNotOverIndented { name } = self; - format!("Section underline is over-indented (\"{name}\")") - } - - fn autofix_title(&self) -> String { - let SectionUnderlineNotOverIndented { name } = self; - format!("Remove over-indentation from \"{name}\" underline") - } -} - -define_violation!( - pub struct UsesTripleQuotes; -); -impl Violation for UsesTripleQuotes { - #[derive_message_formats] - fn message(&self) -> String { - format!(r#"Use """triple double quotes""""#) - } -} - -define_violation!( - pub struct UsesRPrefixForBackslashedContent; -); -impl Violation for UsesRPrefixForBackslashedContent { - #[derive_message_formats] - fn message(&self) -> String { - format!(r#"Use r""" if any backslashes in a docstring"#) - } -} - -define_violation!( - pub struct EndsInPeriod; -); -impl AlwaysAutofixableViolation for EndsInPeriod { - #[derive_message_formats] - fn message(&self) -> String { - format!("First line should end with a period") - } - - fn autofix_title(&self) -> String { - "Add period".to_string() - } -} - -define_violation!( - pub struct NoSignature; -); -impl Violation for NoSignature { - #[derive_message_formats] - fn message(&self) -> String { - format!("First line should not be the function's signature") - } -} - -define_violation!( - pub struct FirstLineCapitalized; -); -impl Violation for FirstLineCapitalized { - #[derive_message_formats] - fn message(&self) -> String { - format!("First word of the first line should be properly capitalized") - } -} - -define_violation!( - pub struct NoThisPrefix; -); -impl Violation for NoThisPrefix { - #[derive_message_formats] - fn message(&self) -> String { - format!(r#"First word of the docstring should not be "This""#) - } -} - -define_violation!( - pub struct CapitalizeSectionName { - pub name: String, - } -); -impl AlwaysAutofixableViolation for CapitalizeSectionName { - #[derive_message_formats] - fn message(&self) -> String { - let CapitalizeSectionName { name } = self; - format!("Section name should be properly capitalized (\"{name}\")") - } - - fn autofix_title(&self) -> String { - let CapitalizeSectionName { name } = self; - format!("Capitalize \"{name}\"") - } -} - -define_violation!( - pub struct NewLineAfterSectionName { - pub name: String, - } -); -impl AlwaysAutofixableViolation for NewLineAfterSectionName { - #[derive_message_formats] - fn message(&self) -> String { - let NewLineAfterSectionName { name } = self; - format!("Section name should end with a newline (\"{name}\")") - } - - fn autofix_title(&self) -> String { - let NewLineAfterSectionName { name } = self; - format!("Add newline after \"{name}\"") - } -} - -define_violation!( - pub struct DashedUnderlineAfterSection { - pub name: String, - } -); -impl AlwaysAutofixableViolation for DashedUnderlineAfterSection { - #[derive_message_formats] - fn message(&self) -> String { - let DashedUnderlineAfterSection { name } = self; - format!("Missing dashed underline after section (\"{name}\")") - } - - fn autofix_title(&self) -> String { - let DashedUnderlineAfterSection { name } = self; - format!("Add dashed line under \"{name}\"") - } -} - -define_violation!( - pub struct SectionUnderlineAfterName { - pub name: String, - } -); -impl AlwaysAutofixableViolation for SectionUnderlineAfterName { - #[derive_message_formats] - fn message(&self) -> String { - let SectionUnderlineAfterName { name } = self; - format!("Section underline should be in the line following the section's name (\"{name}\")") - } - - fn autofix_title(&self) -> String { - let SectionUnderlineAfterName { name } = self; - format!("Add underline to \"{name}\"") - } -} - -define_violation!( - pub struct SectionUnderlineMatchesSectionLength { - pub name: String, - } -); -impl AlwaysAutofixableViolation for SectionUnderlineMatchesSectionLength { - #[derive_message_formats] - fn message(&self) -> String { - let SectionUnderlineMatchesSectionLength { name } = self; - format!("Section underline should match the length of its name (\"{name}\")") - } - - fn autofix_title(&self) -> String { - let SectionUnderlineMatchesSectionLength { name } = self; - format!("Adjust underline length to match \"{name}\"") - } -} - -define_violation!( - pub struct BlankLineAfterSection { - pub name: String, - } -); -impl AlwaysAutofixableViolation for BlankLineAfterSection { - #[derive_message_formats] - fn message(&self) -> String { - let BlankLineAfterSection { name } = self; - format!("Missing blank line after section (\"{name}\")") - } - - fn autofix_title(&self) -> String { - let BlankLineAfterSection { name } = self; - format!("Add blank line after \"{name}\"") - } -} - -define_violation!( - pub struct BlankLineBeforeSection { - pub name: String, - } -); -impl AlwaysAutofixableViolation for BlankLineBeforeSection { - #[derive_message_formats] - fn message(&self) -> String { - let BlankLineBeforeSection { name } = self; - format!("Missing blank line before section (\"{name}\")") - } - - fn autofix_title(&self) -> String { - let BlankLineBeforeSection { name } = self; - format!("Add blank line before \"{name}\"") - } -} - -define_violation!( - pub struct NoBlankLinesBetweenHeaderAndContent { - pub name: String, - } -); -impl AlwaysAutofixableViolation for NoBlankLinesBetweenHeaderAndContent { - #[derive_message_formats] - fn message(&self) -> String { - let NoBlankLinesBetweenHeaderAndContent { name } = self; - format!("No blank lines allowed between a section header and its content (\"{name}\")") - } - - fn autofix_title(&self) -> String { - "Remove blank line(s)".to_string() - } -} - -define_violation!( - pub struct BlankLineAfterLastSection { - pub name: String, - } -); -impl AlwaysAutofixableViolation for BlankLineAfterLastSection { - #[derive_message_formats] - fn message(&self) -> String { - let BlankLineAfterLastSection { name } = self; - format!("Missing blank line after last section (\"{name}\")") - } - - fn autofix_title(&self) -> String { - let BlankLineAfterLastSection { name } = self; - format!("Add blank line after \"{name}\"") - } -} - -define_violation!( - pub struct NonEmptySection { - pub name: String, - } -); -impl Violation for NonEmptySection { - #[derive_message_formats] - fn message(&self) -> String { - let NonEmptySection { name } = self; - format!("Section has no content (\"{name}\")") - } -} - -define_violation!( - pub struct EndsInPunctuation; -); -impl AlwaysAutofixableViolation for EndsInPunctuation { - #[derive_message_formats] - fn message(&self) -> String { - format!("First line should end with a period, question mark, or exclamation point") - } - - fn autofix_title(&self) -> String { - "Add closing punctuation".to_string() - } -} - -define_violation!( - pub struct SectionNameEndsInColon { - pub name: String, - } -); -impl AlwaysAutofixableViolation for SectionNameEndsInColon { - #[derive_message_formats] - fn message(&self) -> String { - let SectionNameEndsInColon { name } = self; - format!("Section name should end with a colon (\"{name}\")") - } - - fn autofix_title(&self) -> String { - let SectionNameEndsInColon { name } = self; - format!("Add colon to \"{name}\"") - } -} - -define_violation!( - pub struct DocumentAllArguments { - pub names: Vec, - } -); -impl Violation for DocumentAllArguments { - #[derive_message_formats] - fn message(&self) -> String { - let DocumentAllArguments { names } = self; - if names.len() == 1 { - let name = &names[0]; - format!("Missing argument description in the docstring: `{name}`") - } else { - let names = names.iter().map(|name| format!("`{name}`")).join(", "); - format!("Missing argument descriptions in the docstring: {names}") - } - } -} - -define_violation!( - pub struct SkipDocstring; -); -impl Violation for SkipDocstring { - #[derive_message_formats] - fn message(&self) -> String { - format!("Function decorated with `@overload` shouldn't contain a docstring") - } -} - -define_violation!( - pub struct NonEmpty; -); -impl Violation for NonEmpty { - #[derive_message_formats] - fn message(&self) -> String { - format!("Docstring is empty") - } -} - // pep8-naming define_violation!(