Move pydocstyle violations (#2524)

This commit is contained in:
Aarni Koskela 2023-02-03 14:42:52 +02:00 committed by GitHub
parent 82784a7607
commit 87c3b0e4e2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
23 changed files with 860 additions and 799 deletions

View File

@ -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);

View File

@ -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,

View File

@ -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),
));
}

View File

@ -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<AutofixKind> = 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<fn(&Self) -> 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),

View File

@ -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),

View File

@ -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),

View File

@ -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),
));
}

View File

@ -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(':')

View File

@ -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(':')

View File

@ -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),
));
}

View File

@ -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),

View File

@ -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;

View File

@ -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()) {

View File

@ -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()) {

View File

@ -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),
));
}

View File

@ -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

View File

@ -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),
));
}

View File

@ -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),
));
}

View File

@ -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),

View File

@ -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<String>,
}
);
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),

View File

@ -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),
));
}

View File

@ -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),
));
}

View File

@ -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<AutofixKind> = 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<fn(&Self) -> 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<String>,
}
);
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!(