mirror of https://github.com/astral-sh/ruff
374 lines
15 KiB
Rust
374 lines
15 KiB
Rust
//! Rules from [pycodestyle](https://pypi.org/project/pycodestyle/).
|
|
pub(crate) mod rules;
|
|
pub mod settings;
|
|
|
|
pub(crate) mod helpers;
|
|
pub(super) mod overlong;
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use std::num::NonZeroU8;
|
|
use std::path::Path;
|
|
|
|
use anyhow::Result;
|
|
|
|
use test_case::test_case;
|
|
|
|
use crate::line_width::LineLength;
|
|
use crate::registry::Rule;
|
|
use crate::rules::{isort, pycodestyle};
|
|
use crate::settings::types::PreviewMode;
|
|
use crate::test::test_path;
|
|
use crate::{assert_messages, settings};
|
|
|
|
use super::settings::Settings;
|
|
|
|
#[test_case(Rule::AmbiguousClassName, Path::new("E742.py"))]
|
|
#[test_case(Rule::AmbiguousFunctionName, Path::new("E743.py"))]
|
|
#[test_case(Rule::AmbiguousVariableName, Path::new("E741.py"))]
|
|
#[test_case(Rule::LambdaAssignment, Path::new("E731.py"))]
|
|
#[test_case(Rule::BareExcept, Path::new("E722.py"))]
|
|
#[test_case(Rule::BlankLineWithWhitespace, Path::new("W29.py"))]
|
|
#[test_case(Rule::BlankLineWithWhitespace, Path::new("W293.py"))]
|
|
#[test_case(Rule::InvalidEscapeSequence, Path::new("W605_0.py"))]
|
|
#[test_case(Rule::InvalidEscapeSequence, Path::new("W605_1.py"))]
|
|
#[test_case(Rule::LineTooLong, Path::new("E501.py"))]
|
|
#[test_case(Rule::LineTooLong, Path::new("E501_3.py"))]
|
|
#[test_case(Rule::MixedSpacesAndTabs, Path::new("E101.py"))]
|
|
#[test_case(Rule::ModuleImportNotAtTopOfFile, Path::new("E40.py"))]
|
|
#[test_case(Rule::ModuleImportNotAtTopOfFile, Path::new("E402_0.py"))]
|
|
#[test_case(Rule::ModuleImportNotAtTopOfFile, Path::new("E402_1.py"))]
|
|
#[test_case(Rule::ModuleImportNotAtTopOfFile, Path::new("E402_2.py"))]
|
|
#[test_case(Rule::ModuleImportNotAtTopOfFile, Path::new("E402.ipynb"))]
|
|
#[test_case(Rule::MultipleImportsOnOneLine, Path::new("E40.py"))]
|
|
#[test_case(Rule::MultipleStatementsOnOneLineColon, Path::new("E70.py"))]
|
|
#[test_case(Rule::MultipleStatementsOnOneLineSemicolon, Path::new("E70.py"))]
|
|
#[test_case(Rule::MissingNewlineAtEndOfFile, Path::new("W292_0.py"))]
|
|
#[test_case(Rule::MissingNewlineAtEndOfFile, Path::new("W292_1.py"))]
|
|
#[test_case(Rule::MissingNewlineAtEndOfFile, Path::new("W292_2.py"))]
|
|
#[test_case(Rule::MissingNewlineAtEndOfFile, Path::new("W292_3.py"))]
|
|
#[test_case(Rule::NoneComparison, Path::new("E711.py"))]
|
|
#[test_case(Rule::NotInTest, Path::new("E713.py"))]
|
|
#[test_case(Rule::NotIsTest, Path::new("E714.py"))]
|
|
#[test_case(Rule::SyntaxError, Path::new("E999.py"))]
|
|
#[test_case(Rule::TabIndentation, Path::new("W19.py"))]
|
|
#[test_case(Rule::TrailingWhitespace, Path::new("W29.py"))]
|
|
#[test_case(Rule::TrailingWhitespace, Path::new("W291.py"))]
|
|
#[test_case(Rule::TrueFalseComparison, Path::new("E712.py"))]
|
|
#[test_case(Rule::TypeComparison, Path::new("E721.py"))]
|
|
#[test_case(Rule::UselessSemicolon, Path::new("E70.py"))]
|
|
#[test_case(Rule::UselessSemicolon, Path::new("E703.ipynb"))]
|
|
fn rules(rule_code: Rule, path: &Path) -> Result<()> {
|
|
let snapshot = format!("{}_{}", rule_code.noqa_code(), path.to_string_lossy());
|
|
let diagnostics = test_path(
|
|
Path::new("pycodestyle").join(path).as_path(),
|
|
&settings::LinterSettings::for_rule(rule_code),
|
|
)?;
|
|
assert_messages!(snapshot, diagnostics);
|
|
Ok(())
|
|
}
|
|
|
|
#[test_case(Rule::IsLiteral, Path::new("constant_literals.py"))]
|
|
#[test_case(Rule::TypeComparison, Path::new("E721.py"))]
|
|
#[test_case(Rule::ModuleImportNotAtTopOfFile, Path::new("E402_2.py"))]
|
|
fn preview_rules(rule_code: Rule, path: &Path) -> Result<()> {
|
|
let snapshot = format!(
|
|
"preview__{}_{}",
|
|
rule_code.noqa_code(),
|
|
path.to_string_lossy()
|
|
);
|
|
let diagnostics = test_path(
|
|
Path::new("pycodestyle").join(path).as_path(),
|
|
&settings::LinterSettings {
|
|
preview: PreviewMode::Enabled,
|
|
..settings::LinterSettings::for_rule(rule_code)
|
|
},
|
|
)?;
|
|
assert_messages!(snapshot, diagnostics);
|
|
Ok(())
|
|
}
|
|
|
|
#[test]
|
|
fn w292_4() -> Result<()> {
|
|
let diagnostics = test_path(
|
|
Path::new("pycodestyle/W292_4.py"),
|
|
&settings::LinterSettings::for_rule(Rule::MissingNewlineAtEndOfFile),
|
|
)?;
|
|
|
|
assert_messages!(diagnostics);
|
|
Ok(())
|
|
}
|
|
|
|
#[test_case(Rule::IndentationWithInvalidMultiple, Path::new("E11.py"))]
|
|
#[test_case(Rule::IndentationWithInvalidMultipleComment, Path::new("E11.py"))]
|
|
#[test_case(Rule::MultipleLeadingHashesForBlockComment, Path::new("E26.py"))]
|
|
#[test_case(Rule::MultipleSpacesAfterComma, Path::new("E24.py"))]
|
|
#[test_case(Rule::MultipleSpacesAfterKeyword, Path::new("E27.py"))]
|
|
#[test_case(Rule::MultipleSpacesAfterOperator, Path::new("E22.py"))]
|
|
#[test_case(Rule::MultipleSpacesBeforeKeyword, Path::new("E27.py"))]
|
|
#[test_case(Rule::MissingWhitespaceAfterKeyword, Path::new("E27.py"))]
|
|
#[test_case(Rule::MultipleSpacesBeforeOperator, Path::new("E22.py"))]
|
|
#[test_case(Rule::NoIndentedBlock, Path::new("E11.py"))]
|
|
#[test_case(Rule::NoIndentedBlockComment, Path::new("E11.py"))]
|
|
#[test_case(Rule::NoSpaceAfterBlockComment, Path::new("E26.py"))]
|
|
#[test_case(Rule::NoSpaceAfterInlineComment, Path::new("E26.py"))]
|
|
#[test_case(Rule::OverIndented, Path::new("E11.py"))]
|
|
#[test_case(Rule::TabAfterComma, Path::new("E24.py"))]
|
|
#[test_case(Rule::TabAfterKeyword, Path::new("E27.py"))]
|
|
#[test_case(Rule::TabAfterOperator, Path::new("E22.py"))]
|
|
#[test_case(Rule::TabBeforeKeyword, Path::new("E27.py"))]
|
|
#[test_case(Rule::TabBeforeOperator, Path::new("E22.py"))]
|
|
#[test_case(Rule::MissingWhitespaceAroundOperator, Path::new("E22.py"))]
|
|
#[test_case(Rule::MissingWhitespaceAroundArithmeticOperator, Path::new("E22.py"))]
|
|
#[test_case(
|
|
Rule::MissingWhitespaceAroundBitwiseOrShiftOperator,
|
|
Path::new("E22.py")
|
|
)]
|
|
#[test_case(Rule::MissingWhitespaceAroundModuloOperator, Path::new("E22.py"))]
|
|
#[test_case(Rule::MissingWhitespace, Path::new("E23.py"))]
|
|
#[test_case(Rule::TooFewSpacesBeforeInlineComment, Path::new("E26.py"))]
|
|
#[test_case(Rule::UnexpectedIndentation, Path::new("E11.py"))]
|
|
#[test_case(Rule::UnexpectedIndentationComment, Path::new("E11.py"))]
|
|
#[test_case(Rule::WhitespaceAfterOpenBracket, Path::new("E20.py"))]
|
|
#[test_case(Rule::WhitespaceBeforeCloseBracket, Path::new("E20.py"))]
|
|
#[test_case(Rule::WhitespaceBeforePunctuation, Path::new("E20.py"))]
|
|
#[test_case(Rule::WhitespaceBeforeParameters, Path::new("E21.py"))]
|
|
#[test_case(
|
|
Rule::UnexpectedSpacesAroundKeywordParameterEquals,
|
|
Path::new("E25.py")
|
|
)]
|
|
#[test_case(Rule::MissingWhitespaceAroundParameterEquals, Path::new("E25.py"))]
|
|
fn logical(rule_code: Rule, path: &Path) -> Result<()> {
|
|
let snapshot = format!("{}_{}", rule_code.noqa_code(), path.to_string_lossy());
|
|
let diagnostics = test_path(
|
|
Path::new("pycodestyle").join(path).as_path(),
|
|
&settings::LinterSettings::for_rule(rule_code),
|
|
)?;
|
|
assert_messages!(snapshot, diagnostics);
|
|
Ok(())
|
|
}
|
|
|
|
/// Tests the compatibility of E2 rules (E202, E225 and E275) on syntactically incorrect code.
|
|
#[test]
|
|
fn white_space_syntax_error_compatibility() -> Result<()> {
|
|
let diagnostics = test_path(
|
|
Path::new("pycodestyle").join("E2_syntax_error.py"),
|
|
&settings::LinterSettings {
|
|
..settings::LinterSettings::for_rules([
|
|
Rule::MissingWhitespaceAroundOperator,
|
|
Rule::MissingWhitespaceAfterKeyword,
|
|
Rule::WhitespaceBeforeCloseBracket,
|
|
])
|
|
},
|
|
)?;
|
|
assert_messages!(diagnostics);
|
|
Ok(())
|
|
}
|
|
|
|
#[test_case(Rule::BlankLineBetweenMethods, Path::new("E30.py"))]
|
|
#[test_case(Rule::BlankLinesTopLevel, Path::new("E30.py"))]
|
|
#[test_case(Rule::TooManyBlankLines, Path::new("E30.py"))]
|
|
#[test_case(Rule::BlankLineAfterDecorator, Path::new("E30.py"))]
|
|
#[test_case(Rule::BlankLinesAfterFunctionOrClass, Path::new("E30.py"))]
|
|
#[test_case(Rule::BlankLinesBeforeNestedDefinition, Path::new("E30.py"))]
|
|
fn blank_lines(rule_code: Rule, path: &Path) -> Result<()> {
|
|
let snapshot = format!("{}_{}", rule_code.noqa_code(), path.to_string_lossy());
|
|
let diagnostics = test_path(
|
|
Path::new("pycodestyle").join(path).as_path(),
|
|
&settings::LinterSettings::for_rule(rule_code),
|
|
)?;
|
|
assert_messages!(snapshot, diagnostics);
|
|
Ok(())
|
|
}
|
|
|
|
/// Tests the compatibility of the blank line top level rule and isort.
|
|
#[test_case(-1, 0)]
|
|
#[test_case(1, 1)]
|
|
#[test_case(0, 0)]
|
|
#[test_case(4, 4)]
|
|
fn blank_lines_top_level_isort_compatibility(
|
|
lines_after_imports: isize,
|
|
lines_between_types: usize,
|
|
) -> Result<()> {
|
|
let snapshot = format!(
|
|
"blank_lines_top_level_isort_compatibility-lines-after({lines_after_imports})-between({lines_between_types})"
|
|
);
|
|
let diagnostics = test_path(
|
|
Path::new("pycodestyle").join("E30_isort.py"),
|
|
&settings::LinterSettings {
|
|
isort: isort::settings::Settings {
|
|
lines_after_imports,
|
|
lines_between_types,
|
|
..isort::settings::Settings::default()
|
|
},
|
|
..settings::LinterSettings::for_rules([
|
|
Rule::BlankLinesTopLevel,
|
|
Rule::UnsortedImports,
|
|
])
|
|
},
|
|
)?;
|
|
assert_messages!(snapshot, diagnostics);
|
|
Ok(())
|
|
}
|
|
|
|
/// Tests the compatibility of the blank line too many lines and isort.
|
|
#[test_case(-1, 0)]
|
|
#[test_case(1, 1)]
|
|
#[test_case(0, 0)]
|
|
#[test_case(4, 4)]
|
|
fn too_many_blank_lines_isort_compatibility(
|
|
lines_after_imports: isize,
|
|
lines_between_types: usize,
|
|
) -> Result<()> {
|
|
let snapshot = format!("too_many_blank_lines_isort_compatibility-lines-after({lines_after_imports})-between({lines_between_types})");
|
|
let diagnostics = test_path(
|
|
Path::new("pycodestyle").join("E30_isort.py"),
|
|
&settings::LinterSettings {
|
|
isort: isort::settings::Settings {
|
|
lines_after_imports,
|
|
lines_between_types,
|
|
..isort::settings::Settings::default()
|
|
},
|
|
..settings::LinterSettings::for_rules([
|
|
Rule::TooManyBlankLines,
|
|
Rule::UnsortedImports,
|
|
])
|
|
},
|
|
)?;
|
|
assert_messages!(snapshot, diagnostics);
|
|
Ok(())
|
|
}
|
|
|
|
#[test_case(Rule::BlankLineBetweenMethods)]
|
|
#[test_case(Rule::BlankLinesTopLevel)]
|
|
#[test_case(Rule::TooManyBlankLines)]
|
|
#[test_case(Rule::BlankLineAfterDecorator)]
|
|
#[test_case(Rule::BlankLinesAfterFunctionOrClass)]
|
|
#[test_case(Rule::BlankLinesBeforeNestedDefinition)]
|
|
fn blank_lines_typing_stub(rule_code: Rule) -> Result<()> {
|
|
let snapshot = format!("blank_lines_{}_typing_stub", rule_code.noqa_code());
|
|
let diagnostics = test_path(
|
|
Path::new("pycodestyle").join("E30.pyi"),
|
|
&settings::LinterSettings::for_rule(rule_code),
|
|
)?;
|
|
assert_messages!(snapshot, diagnostics);
|
|
Ok(())
|
|
}
|
|
|
|
#[test]
|
|
fn blank_lines_typing_stub_isort() -> Result<()> {
|
|
let diagnostics = test_path(
|
|
Path::new("pycodestyle").join("E30_isort.pyi"),
|
|
&settings::LinterSettings {
|
|
..settings::LinterSettings::for_rules([
|
|
Rule::TooManyBlankLines,
|
|
Rule::BlankLinesTopLevel,
|
|
Rule::UnsortedImports,
|
|
])
|
|
},
|
|
)?;
|
|
assert_messages!(diagnostics);
|
|
Ok(())
|
|
}
|
|
|
|
#[test]
|
|
fn constant_literals() -> Result<()> {
|
|
let diagnostics = test_path(
|
|
Path::new("pycodestyle/constant_literals.py"),
|
|
&settings::LinterSettings::for_rules(vec![
|
|
Rule::NoneComparison,
|
|
Rule::TrueFalseComparison,
|
|
Rule::IsLiteral,
|
|
]),
|
|
)?;
|
|
assert_messages!(diagnostics);
|
|
Ok(())
|
|
}
|
|
|
|
#[test]
|
|
fn shebang() -> Result<()> {
|
|
let diagnostics = test_path(
|
|
Path::new("pycodestyle/shebang.py"),
|
|
&settings::LinterSettings::for_rules(vec![
|
|
Rule::TooFewSpacesBeforeInlineComment,
|
|
Rule::NoSpaceAfterInlineComment,
|
|
Rule::NoSpaceAfterBlockComment,
|
|
Rule::MultipleLeadingHashesForBlockComment,
|
|
]),
|
|
)?;
|
|
assert_messages!(diagnostics);
|
|
Ok(())
|
|
}
|
|
|
|
#[test_case(false)]
|
|
#[test_case(true)]
|
|
fn task_tags(ignore_overlong_task_comments: bool) -> Result<()> {
|
|
let snapshot = format!("task_tags_{ignore_overlong_task_comments}");
|
|
let diagnostics = test_path(
|
|
Path::new("pycodestyle/E501_1.py"),
|
|
&settings::LinterSettings {
|
|
pycodestyle: Settings {
|
|
ignore_overlong_task_comments,
|
|
..Settings::default()
|
|
},
|
|
..settings::LinterSettings::for_rule(Rule::LineTooLong)
|
|
},
|
|
)?;
|
|
assert_messages!(snapshot, diagnostics);
|
|
Ok(())
|
|
}
|
|
|
|
#[test]
|
|
fn max_doc_length() -> Result<()> {
|
|
let diagnostics = test_path(
|
|
Path::new("pycodestyle/W505.py"),
|
|
&settings::LinterSettings {
|
|
pycodestyle: Settings {
|
|
max_doc_length: Some(LineLength::try_from(50).unwrap()),
|
|
..Settings::default()
|
|
},
|
|
..settings::LinterSettings::for_rule(Rule::DocLineTooLong)
|
|
},
|
|
)?;
|
|
assert_messages!(diagnostics);
|
|
Ok(())
|
|
}
|
|
|
|
#[test]
|
|
fn max_doc_length_with_utf_8() -> Result<()> {
|
|
let diagnostics = test_path(
|
|
Path::new("pycodestyle/W505_utf_8.py"),
|
|
&settings::LinterSettings {
|
|
pycodestyle: Settings {
|
|
max_doc_length: Some(LineLength::try_from(50).unwrap()),
|
|
..Settings::default()
|
|
},
|
|
..settings::LinterSettings::for_rule(Rule::DocLineTooLong)
|
|
},
|
|
)?;
|
|
assert_messages!(diagnostics);
|
|
Ok(())
|
|
}
|
|
|
|
#[test_case(1)]
|
|
#[test_case(2)]
|
|
#[test_case(4)]
|
|
#[test_case(8)]
|
|
fn tab_size(tab_size: u8) -> Result<()> {
|
|
let snapshot = format!("tab_size_{tab_size}");
|
|
let diagnostics = test_path(
|
|
Path::new("pycodestyle/E501_2.py"),
|
|
&settings::LinterSettings {
|
|
tab_size: NonZeroU8::new(tab_size).unwrap().into(),
|
|
pycodestyle: pycodestyle::settings::Settings {
|
|
max_line_length: LineLength::try_from(6).unwrap(),
|
|
..pycodestyle::settings::Settings::default()
|
|
},
|
|
..settings::LinterSettings::for_rule(Rule::LineTooLong)
|
|
},
|
|
)?;
|
|
assert_messages!(snapshot, diagnostics);
|
|
Ok(())
|
|
}
|
|
}
|