Move violations for pycodestyle rules to rules files (#2138)

This commit is contained in:
Eric Roberts 2023-01-25 20:11:36 -05:00 committed by GitHub
parent 4190f1045e
commit 708295f4c9
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
17 changed files with 329 additions and 317 deletions

View file

@ -211,6 +211,7 @@ mod tests {
use crate::ast::types::Range; use crate::ast::types::Range;
use crate::noqa::{add_noqa_inner, NOQA_LINE_REGEX}; use crate::noqa::{add_noqa_inner, NOQA_LINE_REGEX};
use crate::registry::Diagnostic; use crate::registry::Diagnostic;
use crate::rules::pycodestyle::rules::AmbiguousVariableName;
use crate::settings::hashable::HashableHashSet; use crate::settings::hashable::HashableHashSet;
use crate::source_code::LineEnding; use crate::source_code::LineEnding;
use crate::violations; use crate::violations;
@ -264,7 +265,7 @@ mod tests {
let diagnostics = vec![ let diagnostics = vec![
Diagnostic::new( Diagnostic::new(
violations::AmbiguousVariableName("x".to_string()), AmbiguousVariableName("x".to_string()),
Range::new(Location::new(1, 0), Location::new(1, 0)), Range::new(Location::new(1, 0), Location::new(1, 0)),
), ),
Diagnostic::new( Diagnostic::new(
@ -287,7 +288,7 @@ mod tests {
let diagnostics = vec![ let diagnostics = vec![
Diagnostic::new( Diagnostic::new(
violations::AmbiguousVariableName("x".to_string()), AmbiguousVariableName("x".to_string()),
Range::new(Location::new(1, 0), Location::new(1, 0)), Range::new(Location::new(1, 0), Location::new(1, 0)),
), ),
Diagnostic::new( Diagnostic::new(

View file

@ -13,26 +13,26 @@ use crate::{rules, violations};
ruff_macros::define_rule_mapping!( ruff_macros::define_rule_mapping!(
// pycodestyle errors // pycodestyle errors
E101 => violations::MixedSpacesAndTabs, E101 => rules::pycodestyle::rules::MixedSpacesAndTabs,
E401 => violations::MultipleImportsOnOneLine, E401 => violations::MultipleImportsOnOneLine,
E402 => violations::ModuleImportNotAtTopOfFile, E402 => violations::ModuleImportNotAtTopOfFile,
E501 => violations::LineTooLong, E501 => rules::pycodestyle::rules::LineTooLong,
E711 => violations::NoneComparison, E711 => rules::pycodestyle::rules::NoneComparison,
E712 => violations::TrueFalseComparison, E712 => rules::pycodestyle::rules::TrueFalseComparison,
E713 => violations::NotInTest, E713 => rules::pycodestyle::rules::NotInTest,
E714 => violations::NotIsTest, E714 => rules::pycodestyle::rules::NotIsTest,
E721 => violations::TypeComparison, E721 => rules::pycodestyle::rules::TypeComparison,
E722 => violations::DoNotUseBareExcept, E722 => rules::pycodestyle::rules::DoNotUseBareExcept,
E731 => violations::DoNotAssignLambda, E731 => rules::pycodestyle::rules::DoNotAssignLambda,
E741 => violations::AmbiguousVariableName, E741 => rules::pycodestyle::rules::AmbiguousVariableName,
E742 => violations::AmbiguousClassName, E742 => rules::pycodestyle::rules::AmbiguousClassName,
E743 => violations::AmbiguousFunctionName, E743 => rules::pycodestyle::rules::AmbiguousFunctionName,
E902 => violations::IOError, E902 => violations::IOError,
E999 => violations::SyntaxError, E999 => violations::SyntaxError,
// pycodestyle warnings // pycodestyle warnings
W292 => violations::NoNewLineAtEndOfFile, W292 => rules::pycodestyle::rules::NoNewLineAtEndOfFile,
W505 => violations::DocLineTooLong, W505 => rules::pycodestyle::rules::DocLineTooLong,
W605 => violations::InvalidEscapeSequence, W605 => rules::pycodestyle::rules::InvalidEscapeSequence,
// pyflakes // pyflakes
F401 => violations::UnusedImport, F401 => violations::UnusedImport,
F402 => violations::ImportShadowedByLoopVar, F402 => violations::ImportShadowedByLoopVar,

View file

@ -1,7 +1,21 @@
use ruff_macros::derive_message_formats;
use crate::ast::types::Range; use crate::ast::types::Range;
use crate::define_violation;
use crate::registry::Diagnostic; use crate::registry::Diagnostic;
use crate::rules::pycodestyle::helpers::is_ambiguous_name; use crate::rules::pycodestyle::helpers::is_ambiguous_name;
use crate::violations; use crate::violation::Violation;
define_violation!(
pub struct AmbiguousClassName(pub String);
);
impl Violation for AmbiguousClassName {
#[derive_message_formats]
fn message(&self) -> String {
let AmbiguousClassName(name) = self;
format!("Ambiguous class name: `{name}`")
}
}
/// E742 /// E742
pub fn ambiguous_class_name<F>(name: &str, locate: F) -> Option<Diagnostic> pub fn ambiguous_class_name<F>(name: &str, locate: F) -> Option<Diagnostic>
@ -10,7 +24,7 @@ where
{ {
if is_ambiguous_name(name) { if is_ambiguous_name(name) {
Some(Diagnostic::new( Some(Diagnostic::new(
violations::AmbiguousClassName(name.to_string()), AmbiguousClassName(name.to_string()),
locate(), locate(),
)) ))
} else { } else {

View file

@ -1,7 +1,21 @@
use ruff_macros::derive_message_formats;
use crate::ast::types::Range; use crate::ast::types::Range;
use crate::define_violation;
use crate::registry::Diagnostic; use crate::registry::Diagnostic;
use crate::rules::pycodestyle::helpers::is_ambiguous_name; use crate::rules::pycodestyle::helpers::is_ambiguous_name;
use crate::violations; use crate::violation::Violation;
define_violation!(
pub struct AmbiguousFunctionName(pub String);
);
impl Violation for AmbiguousFunctionName {
#[derive_message_formats]
fn message(&self) -> String {
let AmbiguousFunctionName(name) = self;
format!("Ambiguous function name: `{name}`")
}
}
/// E743 /// E743
pub fn ambiguous_function_name<F>(name: &str, locate: F) -> Option<Diagnostic> pub fn ambiguous_function_name<F>(name: &str, locate: F) -> Option<Diagnostic>
@ -10,7 +24,7 @@ where
{ {
if is_ambiguous_name(name) { if is_ambiguous_name(name) {
Some(Diagnostic::new( Some(Diagnostic::new(
violations::AmbiguousFunctionName(name.to_string()), AmbiguousFunctionName(name.to_string()),
locate(), locate(),
)) ))
} else { } else {

View file

@ -1,13 +1,27 @@
use ruff_macros::derive_message_formats;
use crate::ast::types::Range; use crate::ast::types::Range;
use crate::define_violation;
use crate::registry::Diagnostic; use crate::registry::Diagnostic;
use crate::rules::pycodestyle::helpers::is_ambiguous_name; use crate::rules::pycodestyle::helpers::is_ambiguous_name;
use crate::violations; use crate::violation::Violation;
define_violation!(
pub struct AmbiguousVariableName(pub String);
);
impl Violation for AmbiguousVariableName {
#[derive_message_formats]
fn message(&self) -> String {
let AmbiguousVariableName(name) = self;
format!("Ambiguous variable name: `{name}`")
}
}
/// E741 /// E741
pub fn ambiguous_variable_name(name: &str, range: Range) -> Option<Diagnostic> { pub fn ambiguous_variable_name(name: &str, range: Range) -> Option<Diagnostic> {
if is_ambiguous_name(name) { if is_ambiguous_name(name) {
Some(Diagnostic::new( Some(Diagnostic::new(
violations::AmbiguousVariableName(name.to_string()), AmbiguousVariableName(name.to_string()),
range, range,
)) ))
} else { } else {

View file

@ -1,3 +1,4 @@
use ruff_macros::derive_message_formats;
use rustpython_ast::{Arguments, Location, Stmt, StmtKind}; use rustpython_ast::{Arguments, Location, Stmt, StmtKind};
use rustpython_parser::ast::{Expr, ExprKind}; use rustpython_parser::ast::{Expr, ExprKind};
@ -5,19 +6,33 @@ use crate::ast::helpers::{match_leading_content, match_trailing_content, unparse
use crate::ast::types::Range; use crate::ast::types::Range;
use crate::ast::whitespace::leading_space; use crate::ast::whitespace::leading_space;
use crate::checkers::ast::Checker; use crate::checkers::ast::Checker;
use crate::define_violation;
use crate::fix::Fix; use crate::fix::Fix;
use crate::registry::Diagnostic; use crate::registry::Diagnostic;
use crate::source_code::Stylist; use crate::source_code::Stylist;
use crate::violations; use crate::violation::AlwaysAutofixableViolation;
define_violation!(
pub struct DoNotAssignLambda(pub String);
);
impl AlwaysAutofixableViolation for DoNotAssignLambda {
#[derive_message_formats]
fn message(&self) -> String {
format!("Do not assign a `lambda` expression, use a `def`")
}
fn autofix_title(&self) -> String {
let DoNotAssignLambda(name) = self;
format!("Rewrite `{name}` as a `def`")
}
}
/// E731 /// E731
pub fn do_not_assign_lambda(checker: &mut Checker, target: &Expr, value: &Expr, stmt: &Stmt) { pub fn do_not_assign_lambda(checker: &mut Checker, target: &Expr, value: &Expr, stmt: &Stmt) {
if let ExprKind::Name { id, .. } = &target.node { if let ExprKind::Name { id, .. } = &target.node {
if let ExprKind::Lambda { args, body } = &value.node { if let ExprKind::Lambda { args, body } = &value.node {
let mut diagnostic = Diagnostic::new( let mut diagnostic =
violations::DoNotAssignLambda(id.to_string()), Diagnostic::new(DoNotAssignLambda(id.to_string()), Range::from_located(stmt));
Range::from_located(stmt),
);
if checker.patch(diagnostic.kind.rule()) { if checker.patch(diagnostic.kind.rule()) {
if !match_leading_content(stmt, checker.locator) if !match_leading_content(stmt, checker.locator)
&& !match_trailing_content(stmt, checker.locator) && !match_trailing_content(stmt, checker.locator)

View file

@ -1,10 +1,22 @@
use ruff_macros::derive_message_formats;
use rustpython_ast::{Excepthandler, Stmt, StmtKind}; use rustpython_ast::{Excepthandler, Stmt, StmtKind};
use rustpython_parser::ast::Expr; use rustpython_parser::ast::Expr;
use crate::ast::helpers::except_range; use crate::ast::helpers::except_range;
use crate::define_violation;
use crate::registry::Diagnostic; use crate::registry::Diagnostic;
use crate::source_code::Locator; use crate::source_code::Locator;
use crate::violations; use crate::violation::Violation;
define_violation!(
pub struct DoNotUseBareExcept;
);
impl Violation for DoNotUseBareExcept {
#[derive_message_formats]
fn message(&self) -> String {
format!("Do not use bare `except`")
}
}
/// E722 /// E722
pub fn do_not_use_bare_except( pub fn do_not_use_bare_except(
@ -19,7 +31,7 @@ pub fn do_not_use_bare_except(
.any(|stmt| matches!(stmt.node, StmtKind::Raise { exc: None, .. })) .any(|stmt| matches!(stmt.node, StmtKind::Raise { exc: None, .. }))
{ {
Some(Diagnostic::new( Some(Diagnostic::new(
violations::DoNotUseBareExcept, DoNotUseBareExcept,
except_range(handler, locator), except_range(handler, locator),
)) ))
} else { } else {

View file

@ -1,10 +1,23 @@
use ruff_macros::derive_message_formats;
use rustpython_ast::Location; use rustpython_ast::Location;
use crate::ast::types::Range; use crate::ast::types::Range;
use crate::define_violation;
use crate::registry::Diagnostic; use crate::registry::Diagnostic;
use crate::rules::pycodestyle::helpers::is_overlong; use crate::rules::pycodestyle::helpers::is_overlong;
use crate::settings::Settings; use crate::settings::Settings;
use crate::violations; use crate::violation::Violation;
define_violation!(
pub struct DocLineTooLong(pub usize, pub usize);
);
impl Violation for DocLineTooLong {
#[derive_message_formats]
fn message(&self) -> String {
let DocLineTooLong(length, limit) = self;
format!("Doc line too long ({length} > {limit} characters)")
}
}
/// W505 /// W505
pub fn doc_line_too_long(lineno: usize, line: &str, settings: &Settings) -> Option<Diagnostic> { pub fn doc_line_too_long(lineno: usize, line: &str, settings: &Settings) -> Option<Diagnostic> {
@ -21,7 +34,7 @@ pub fn doc_line_too_long(lineno: usize, line: &str, settings: &Settings) -> Opti
&settings.task_tags, &settings.task_tags,
) { ) {
Some(Diagnostic::new( Some(Diagnostic::new(
violations::DocLineTooLong(line_length, limit), DocLineTooLong(line_length, limit),
Range::new( Range::new(
Location::new(lineno + 1, limit), Location::new(lineno + 1, limit),
Location::new(lineno + 1, line_length), Location::new(lineno + 1, line_length),

View file

@ -1,10 +1,27 @@
use ruff_macros::derive_message_formats;
use rustpython_ast::Location; use rustpython_ast::Location;
use crate::ast::types::Range; use crate::ast::types::Range;
use crate::define_violation;
use crate::fix::Fix; use crate::fix::Fix;
use crate::registry::Diagnostic; use crate::registry::Diagnostic;
use crate::source_code::Locator; use crate::source_code::Locator;
use crate::violations; use crate::violation::AlwaysAutofixableViolation;
define_violation!(
pub struct InvalidEscapeSequence(pub char);
);
impl AlwaysAutofixableViolation for InvalidEscapeSequence {
#[derive_message_formats]
fn message(&self) -> String {
let InvalidEscapeSequence(char) = self;
format!("Invalid escape sequence: '\\{char}'")
}
fn autofix_title(&self) -> String {
"Add backslash to escape sequence".to_string()
}
}
// See: https://docs.python.org/3/reference/lexical_analysis.html#string-and-bytes-literals // See: https://docs.python.org/3/reference/lexical_analysis.html#string-and-bytes-literals
const VALID_ESCAPE_SEQUENCES: &[char; 23] = &[ const VALID_ESCAPE_SEQUENCES: &[char; 23] = &[
@ -75,7 +92,7 @@ pub fn invalid_escape_sequence(
let location = Location::new(start.row() + row_offset, col); let location = Location::new(start.row() + row_offset, col);
let end_location = Location::new(location.row(), location.column() + 2); let end_location = Location::new(location.row(), location.column() + 2);
let mut diagnostic = Diagnostic::new( let mut diagnostic = Diagnostic::new(
violations::InvalidEscapeSequence(next_char), InvalidEscapeSequence(next_char),
Range::new(location, end_location), Range::new(location, end_location),
); );
if autofix { if autofix {

View file

@ -1,10 +1,23 @@
use ruff_macros::derive_message_formats;
use rustpython_ast::Location; use rustpython_ast::Location;
use crate::ast::types::Range; use crate::ast::types::Range;
use crate::define_violation;
use crate::registry::Diagnostic; use crate::registry::Diagnostic;
use crate::rules::pycodestyle::helpers::is_overlong; use crate::rules::pycodestyle::helpers::is_overlong;
use crate::settings::Settings; use crate::settings::Settings;
use crate::violations; use crate::violation::Violation;
define_violation!(
pub struct LineTooLong(pub usize, pub usize);
);
impl Violation for LineTooLong {
#[derive_message_formats]
fn message(&self) -> String {
let LineTooLong(length, limit) = self;
format!("Line too long ({length} > {limit} characters)")
}
}
/// E501 /// E501
pub fn line_too_long(lineno: usize, line: &str, settings: &Settings) -> Option<Diagnostic> { pub fn line_too_long(lineno: usize, line: &str, settings: &Settings) -> Option<Diagnostic> {
@ -18,7 +31,7 @@ pub fn line_too_long(lineno: usize, line: &str, settings: &Settings) -> Option<D
&settings.task_tags, &settings.task_tags,
) { ) {
Some(Diagnostic::new( Some(Diagnostic::new(
violations::LineTooLong(line_length, limit), LineTooLong(line_length, limit),
Range::new( Range::new(
Location::new(lineno + 1, limit), Location::new(lineno + 1, limit),
Location::new(lineno + 1, line_length), Location::new(lineno + 1, line_length),

View file

@ -1,15 +1,86 @@
use itertools::izip; use itertools::izip;
use ruff_macros::derive_message_formats;
use rustc_hash::FxHashMap; use rustc_hash::FxHashMap;
use rustpython_ast::Constant; use rustpython_ast::Constant;
use rustpython_parser::ast::{Cmpop, Expr, ExprKind}; use rustpython_parser::ast::{Cmpop, Expr, ExprKind};
use serde::{Deserialize, Serialize};
use crate::ast::helpers; use crate::ast::helpers;
use crate::ast::types::Range; use crate::ast::types::Range;
use crate::checkers::ast::Checker; use crate::checkers::ast::Checker;
use crate::define_violation;
use crate::fix::Fix; use crate::fix::Fix;
use crate::registry::Diagnostic; use crate::registry::Diagnostic;
use crate::rules::pycodestyle::helpers::compare; use crate::rules::pycodestyle::helpers::compare;
use crate::violations; use crate::violation::AlwaysAutofixableViolation;
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
pub enum EqCmpop {
Eq,
NotEq,
}
impl From<&Cmpop> for EqCmpop {
fn from(cmpop: &Cmpop) -> Self {
match cmpop {
Cmpop::Eq => EqCmpop::Eq,
Cmpop::NotEq => EqCmpop::NotEq,
_ => unreachable!("Expected Cmpop::Eq | Cmpop::NotEq"),
}
}
}
define_violation!(
pub struct NoneComparison(pub EqCmpop);
);
impl AlwaysAutofixableViolation for NoneComparison {
#[derive_message_formats]
fn message(&self) -> String {
let NoneComparison(op) = self;
match op {
EqCmpop::Eq => format!("Comparison to `None` should be `cond is None`"),
EqCmpop::NotEq => format!("Comparison to `None` should be `cond is not None`"),
}
}
fn autofix_title(&self) -> String {
let NoneComparison(op) = self;
match op {
EqCmpop::Eq => "Replace with `cond is None`".to_string(),
EqCmpop::NotEq => "Replace with `cond is not None`".to_string(),
}
}
}
define_violation!(
pub struct TrueFalseComparison(pub bool, pub EqCmpop);
);
impl AlwaysAutofixableViolation for TrueFalseComparison {
#[derive_message_formats]
fn message(&self) -> String {
let TrueFalseComparison(value, op) = self;
match (value, op) {
(true, EqCmpop::Eq) => format!("Comparison to `True` should be `cond is True`"),
(true, EqCmpop::NotEq) => {
format!("Comparison to `True` should be `cond is not True`")
}
(false, EqCmpop::Eq) => format!("Comparison to `False` should be `cond is False`"),
(false, EqCmpop::NotEq) => {
format!("Comparison to `False` should be `cond is not False`")
}
}
}
fn autofix_title(&self) -> String {
let TrueFalseComparison(value, op) = self;
match (value, op) {
(true, EqCmpop::Eq) => "Replace with `cond is True`".to_string(),
(true, EqCmpop::NotEq) => "Replace with `cond is not True`".to_string(),
(false, EqCmpop::Eq) => "Replace with `cond is False`".to_string(),
(false, EqCmpop::NotEq) => "Replace with `cond is not False`".to_string(),
}
}
}
/// E711, E712 /// E711, E712
pub fn literal_comparisons( pub fn literal_comparisons(
@ -43,20 +114,16 @@ pub fn literal_comparisons(
) )
{ {
if matches!(op, Cmpop::Eq) { if matches!(op, Cmpop::Eq) {
let diagnostic = Diagnostic::new( let diagnostic =
violations::NoneComparison(op.into()), Diagnostic::new(NoneComparison(op.into()), Range::from_located(comparator));
Range::from_located(comparator),
);
if checker.patch(diagnostic.kind.rule()) && !helpers::is_constant_non_singleton(next) { if checker.patch(diagnostic.kind.rule()) && !helpers::is_constant_non_singleton(next) {
bad_ops.insert(0, Cmpop::Is); bad_ops.insert(0, Cmpop::Is);
} }
diagnostics.push(diagnostic); diagnostics.push(diagnostic);
} }
if matches!(op, Cmpop::NotEq) { if matches!(op, Cmpop::NotEq) {
let diagnostic = Diagnostic::new( let diagnostic =
violations::NoneComparison(op.into()), Diagnostic::new(NoneComparison(op.into()), Range::from_located(comparator));
Range::from_located(comparator),
);
if checker.patch(diagnostic.kind.rule()) && !helpers::is_constant_non_singleton(next) { if checker.patch(diagnostic.kind.rule()) && !helpers::is_constant_non_singleton(next) {
bad_ops.insert(0, Cmpop::IsNot); bad_ops.insert(0, Cmpop::IsNot);
} }
@ -72,7 +139,7 @@ pub fn literal_comparisons(
{ {
if matches!(op, Cmpop::Eq) { if matches!(op, Cmpop::Eq) {
let diagnostic = Diagnostic::new( let diagnostic = Diagnostic::new(
violations::TrueFalseComparison(value, op.into()), TrueFalseComparison(value, op.into()),
Range::from_located(comparator), Range::from_located(comparator),
); );
if checker.patch(diagnostic.kind.rule()) if checker.patch(diagnostic.kind.rule())
@ -84,7 +151,7 @@ pub fn literal_comparisons(
} }
if matches!(op, Cmpop::NotEq) { if matches!(op, Cmpop::NotEq) {
let diagnostic = Diagnostic::new( let diagnostic = Diagnostic::new(
violations::TrueFalseComparison(value, op.into()), TrueFalseComparison(value, op.into()),
Range::from_located(comparator), Range::from_located(comparator),
); );
if checker.patch(diagnostic.kind.rule()) if checker.patch(diagnostic.kind.rule())
@ -109,10 +176,8 @@ pub fn literal_comparisons(
) )
{ {
if matches!(op, Cmpop::Eq) { if matches!(op, Cmpop::Eq) {
let diagnostic = Diagnostic::new( let diagnostic =
violations::NoneComparison(op.into()), Diagnostic::new(NoneComparison(op.into()), Range::from_located(next));
Range::from_located(next),
);
if checker.patch(diagnostic.kind.rule()) if checker.patch(diagnostic.kind.rule())
&& !helpers::is_constant_non_singleton(comparator) && !helpers::is_constant_non_singleton(comparator)
{ {
@ -121,10 +186,8 @@ pub fn literal_comparisons(
diagnostics.push(diagnostic); diagnostics.push(diagnostic);
} }
if matches!(op, Cmpop::NotEq) { if matches!(op, Cmpop::NotEq) {
let diagnostic = Diagnostic::new( let diagnostic =
violations::NoneComparison(op.into()), Diagnostic::new(NoneComparison(op.into()), Range::from_located(next));
Range::from_located(next),
);
if checker.patch(diagnostic.kind.rule()) if checker.patch(diagnostic.kind.rule())
&& !helpers::is_constant_non_singleton(comparator) && !helpers::is_constant_non_singleton(comparator)
{ {
@ -142,7 +205,7 @@ pub fn literal_comparisons(
{ {
if matches!(op, Cmpop::Eq) { if matches!(op, Cmpop::Eq) {
let diagnostic = Diagnostic::new( let diagnostic = Diagnostic::new(
violations::TrueFalseComparison(value, op.into()), TrueFalseComparison(value, op.into()),
Range::from_located(next), Range::from_located(next),
); );
if checker.patch(diagnostic.kind.rule()) if checker.patch(diagnostic.kind.rule())
@ -154,7 +217,7 @@ pub fn literal_comparisons(
} }
if matches!(op, Cmpop::NotEq) { if matches!(op, Cmpop::NotEq) {
let diagnostic = Diagnostic::new( let diagnostic = Diagnostic::new(
violations::TrueFalseComparison(value, op.into()), TrueFalseComparison(value, op.into()),
Range::from_located(next), Range::from_located(next),
); );
if checker.patch(diagnostic.kind.rule()) if checker.patch(diagnostic.kind.rule())

View file

@ -1,9 +1,21 @@
use ruff_macros::derive_message_formats;
use rustpython_ast::Location; use rustpython_ast::Location;
use crate::ast::types::Range; use crate::ast::types::Range;
use crate::ast::whitespace::leading_space; use crate::ast::whitespace::leading_space;
use crate::define_violation;
use crate::registry::Diagnostic; use crate::registry::Diagnostic;
use crate::violations; use crate::violation::Violation;
define_violation!(
pub struct MixedSpacesAndTabs;
);
impl Violation for MixedSpacesAndTabs {
#[derive_message_formats]
fn message(&self) -> String {
format!("Indentation contains mixed spaces and tabs")
}
}
/// E101 /// E101
pub fn mixed_spaces_and_tabs(lineno: usize, line: &str) -> Option<Diagnostic> { pub fn mixed_spaces_and_tabs(lineno: usize, line: &str) -> Option<Diagnostic> {
@ -11,7 +23,7 @@ pub fn mixed_spaces_and_tabs(lineno: usize, line: &str) -> Option<Diagnostic> {
if indent.contains(' ') && indent.contains('\t') { if indent.contains(' ') && indent.contains('\t') {
Some(Diagnostic::new( Some(Diagnostic::new(
violations::MixedSpacesAndTabs, MixedSpacesAndTabs,
Range::new( Range::new(
Location::new(lineno + 1, 0), Location::new(lineno + 1, 0),
Location::new(lineno + 1, indent.chars().count()), Location::new(lineno + 1, indent.chars().count()),

View file

@ -1,16 +1,16 @@
pub use ambiguous_class_name::ambiguous_class_name; pub use ambiguous_class_name::{ambiguous_class_name, AmbiguousClassName};
pub use ambiguous_function_name::ambiguous_function_name; pub use ambiguous_function_name::{ambiguous_function_name, AmbiguousFunctionName};
pub use ambiguous_variable_name::ambiguous_variable_name; pub use ambiguous_variable_name::{ambiguous_variable_name, AmbiguousVariableName};
pub use do_not_assign_lambda::do_not_assign_lambda; pub use do_not_assign_lambda::{do_not_assign_lambda, DoNotAssignLambda};
pub use do_not_use_bare_except::do_not_use_bare_except; pub use do_not_use_bare_except::{do_not_use_bare_except, DoNotUseBareExcept};
pub use doc_line_too_long::doc_line_too_long; pub use doc_line_too_long::{doc_line_too_long, DocLineTooLong};
pub use invalid_escape_sequence::invalid_escape_sequence; pub use invalid_escape_sequence::{invalid_escape_sequence, InvalidEscapeSequence};
pub use line_too_long::line_too_long; pub use line_too_long::{line_too_long, LineTooLong};
pub use literal_comparisons::literal_comparisons; pub use literal_comparisons::{literal_comparisons, NoneComparison, TrueFalseComparison};
pub use mixed_spaces_and_tabs::mixed_spaces_and_tabs; pub use mixed_spaces_and_tabs::{mixed_spaces_and_tabs, MixedSpacesAndTabs};
pub use no_newline_at_end_of_file::no_newline_at_end_of_file; pub use no_newline_at_end_of_file::{no_newline_at_end_of_file, NoNewLineAtEndOfFile};
pub use not_tests::not_tests; pub use not_tests::{not_tests, NotInTest, NotIsTest};
pub use type_comparison::type_comparison; pub use type_comparison::{type_comparison, TypeComparison};
mod ambiguous_class_name; mod ambiguous_class_name;
mod ambiguous_function_name; mod ambiguous_function_name;

View file

@ -1,10 +1,26 @@
use ruff_macros::derive_message_formats;
use rustpython_ast::Location; use rustpython_ast::Location;
use crate::ast::types::Range; use crate::ast::types::Range;
use crate::define_violation;
use crate::fix::Fix; use crate::fix::Fix;
use crate::registry::Diagnostic; use crate::registry::Diagnostic;
use crate::source_code::Stylist; use crate::source_code::Stylist;
use crate::violations; use crate::violation::AlwaysAutofixableViolation;
define_violation!(
pub struct NoNewLineAtEndOfFile;
);
impl AlwaysAutofixableViolation for NoNewLineAtEndOfFile {
#[derive_message_formats]
fn message(&self) -> String {
format!("No newline at end of file")
}
fn autofix_title(&self) -> String {
"Add trailing newline".to_string()
}
}
/// W292 /// W292
pub fn no_newline_at_end_of_file( pub fn no_newline_at_end_of_file(
@ -18,10 +34,8 @@ pub fn no_newline_at_end_of_file(
if let Some(line) = contents.lines().last() { if let Some(line) = contents.lines().last() {
// Both locations are at the end of the file (and thus the same). // Both locations are at the end of the file (and thus the same).
let location = Location::new(contents.lines().count(), line.len()); let location = Location::new(contents.lines().count(), line.len());
let mut diagnostic = Diagnostic::new( let mut diagnostic =
violations::NoNewLineAtEndOfFile, Diagnostic::new(NoNewLineAtEndOfFile, Range::new(location, location));
Range::new(location, location),
);
if autofix { if autofix {
diagnostic.amend(Fix::insertion(stylist.line_ending().to_string(), location)); diagnostic.amend(Fix::insertion(stylist.line_ending().to_string(), location));
} }

View file

@ -1,12 +1,42 @@
use ruff_macros::derive_message_formats;
use rustpython_ast::Unaryop; use rustpython_ast::Unaryop;
use rustpython_parser::ast::{Cmpop, Expr, ExprKind}; use rustpython_parser::ast::{Cmpop, Expr, ExprKind};
use crate::ast::types::Range; use crate::ast::types::Range;
use crate::checkers::ast::Checker; use crate::checkers::ast::Checker;
use crate::define_violation;
use crate::fix::Fix; use crate::fix::Fix;
use crate::registry::Diagnostic; use crate::registry::Diagnostic;
use crate::rules::pycodestyle::helpers::compare; use crate::rules::pycodestyle::helpers::compare;
use crate::violations; use crate::violation::AlwaysAutofixableViolation;
define_violation!(
pub struct NotInTest;
);
impl AlwaysAutofixableViolation for NotInTest {
#[derive_message_formats]
fn message(&self) -> String {
format!("Test for membership should be `not in`")
}
fn autofix_title(&self) -> String {
"Convert to `not in`".to_string()
}
}
define_violation!(
pub struct NotIsTest;
);
impl AlwaysAutofixableViolation for NotIsTest {
#[derive_message_formats]
fn message(&self) -> String {
format!("Test for object identity should be `is not`")
}
fn autofix_title(&self) -> String {
"Convert to `is not`".to_string()
}
}
/// E713, E714 /// E713, E714
pub fn not_tests( pub fn not_tests(
@ -30,10 +60,8 @@ pub fn not_tests(
match op { match op {
Cmpop::In => { Cmpop::In => {
if check_not_in { if check_not_in {
let mut diagnostic = Diagnostic::new( let mut diagnostic =
violations::NotInTest, Diagnostic::new(NotInTest, Range::from_located(operand));
Range::from_located(operand),
);
if checker.patch(diagnostic.kind.rule()) && should_fix { if checker.patch(diagnostic.kind.rule()) && should_fix {
diagnostic.amend(Fix::replacement( diagnostic.amend(Fix::replacement(
compare(left, &[Cmpop::NotIn], comparators, checker.stylist), compare(left, &[Cmpop::NotIn], comparators, checker.stylist),
@ -46,10 +74,8 @@ pub fn not_tests(
} }
Cmpop::Is => { Cmpop::Is => {
if check_not_is { if check_not_is {
let mut diagnostic = Diagnostic::new( let mut diagnostic =
violations::NotIsTest, Diagnostic::new(NotIsTest, Range::from_located(operand));
Range::from_located(operand),
);
if checker.patch(diagnostic.kind.rule()) && should_fix { if checker.patch(diagnostic.kind.rule()) && should_fix {
diagnostic.amend(Fix::replacement( diagnostic.amend(Fix::replacement(
compare(left, &[Cmpop::IsNot], comparators, checker.stylist), compare(left, &[Cmpop::IsNot], comparators, checker.stylist),

View file

@ -1,10 +1,22 @@
use itertools::izip; use itertools::izip;
use ruff_macros::derive_message_formats;
use rustpython_ast::Constant; use rustpython_ast::Constant;
use rustpython_parser::ast::{Cmpop, Expr, ExprKind}; use rustpython_parser::ast::{Cmpop, Expr, ExprKind};
use crate::ast::types::Range; use crate::ast::types::Range;
use crate::define_violation;
use crate::registry::Diagnostic; use crate::registry::Diagnostic;
use crate::violations; use crate::violation::Violation;
define_violation!(
pub struct TypeComparison;
);
impl Violation for TypeComparison {
#[derive_message_formats]
fn message(&self) -> String {
format!("Do not compare types, use `isinstance()`")
}
}
/// E721 /// E721
pub fn type_comparison(ops: &[Cmpop], comparators: &[Expr], location: Range) -> Vec<Diagnostic> { pub fn type_comparison(ops: &[Cmpop], comparators: &[Expr], location: Range) -> Vec<Diagnostic> {
@ -29,8 +41,7 @@ pub fn type_comparison(ops: &[Cmpop], comparators: &[Expr], location: Range) ->
kind: None kind: None
} }
) { ) {
diagnostics diagnostics.push(Diagnostic::new(TypeComparison, location));
.push(Diagnostic::new(violations::TypeComparison, location));
} }
} }
} }
@ -40,7 +51,7 @@ pub fn type_comparison(ops: &[Cmpop], comparators: &[Expr], location: Range) ->
if let ExprKind::Name { id, .. } = &value.node { if let ExprKind::Name { id, .. } = &value.node {
// Ex) types.IntType // Ex) types.IntType
if id == "types" { if id == "types" {
diagnostics.push(Diagnostic::new(violations::TypeComparison, location)); diagnostics.push(Diagnostic::new(TypeComparison, location));
} }
} }
} }

View file

@ -37,191 +37,6 @@ impl Violation for ModuleImportNotAtTopOfFile {
} }
} }
define_violation!(
pub struct LineTooLong(pub usize, pub usize);
);
impl Violation for LineTooLong {
#[derive_message_formats]
fn message(&self) -> String {
let LineTooLong(length, limit) = self;
format!("Line too long ({length} > {limit} characters)")
}
}
define_violation!(
pub struct MixedSpacesAndTabs;
);
impl Violation for MixedSpacesAndTabs {
#[derive_message_formats]
fn message(&self) -> String {
format!("Indentation contains mixed spaces and tabs")
}
}
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
pub enum EqCmpop {
Eq,
NotEq,
}
impl From<&Cmpop> for EqCmpop {
fn from(cmpop: &Cmpop) -> Self {
match cmpop {
Cmpop::Eq => EqCmpop::Eq,
Cmpop::NotEq => EqCmpop::NotEq,
_ => unreachable!("Expected Cmpop::Eq | Cmpop::NotEq"),
}
}
}
define_violation!(
pub struct NoneComparison(pub EqCmpop);
);
impl AlwaysAutofixableViolation for NoneComparison {
#[derive_message_formats]
fn message(&self) -> String {
let NoneComparison(op) = self;
match op {
EqCmpop::Eq => format!("Comparison to `None` should be `cond is None`"),
EqCmpop::NotEq => format!("Comparison to `None` should be `cond is not None`"),
}
}
fn autofix_title(&self) -> String {
let NoneComparison(op) = self;
match op {
EqCmpop::Eq => "Replace with `cond is None`".to_string(),
EqCmpop::NotEq => "Replace with `cond is not None`".to_string(),
}
}
}
define_violation!(
pub struct TrueFalseComparison(pub bool, pub EqCmpop);
);
impl AlwaysAutofixableViolation for TrueFalseComparison {
#[derive_message_formats]
fn message(&self) -> String {
let TrueFalseComparison(value, op) = self;
match (value, op) {
(true, EqCmpop::Eq) => format!("Comparison to `True` should be `cond is True`"),
(true, EqCmpop::NotEq) => {
format!("Comparison to `True` should be `cond is not True`")
}
(false, EqCmpop::Eq) => format!("Comparison to `False` should be `cond is False`"),
(false, EqCmpop::NotEq) => {
format!("Comparison to `False` should be `cond is not False`")
}
}
}
fn autofix_title(&self) -> String {
let TrueFalseComparison(value, op) = self;
match (value, op) {
(true, EqCmpop::Eq) => "Replace with `cond is True`".to_string(),
(true, EqCmpop::NotEq) => "Replace with `cond is not True`".to_string(),
(false, EqCmpop::Eq) => "Replace with `cond is False`".to_string(),
(false, EqCmpop::NotEq) => "Replace with `cond is not False`".to_string(),
}
}
}
define_violation!(
pub struct NotInTest;
);
impl AlwaysAutofixableViolation for NotInTest {
#[derive_message_formats]
fn message(&self) -> String {
format!("Test for membership should be `not in`")
}
fn autofix_title(&self) -> String {
"Convert to `not in`".to_string()
}
}
define_violation!(
pub struct NotIsTest;
);
impl AlwaysAutofixableViolation for NotIsTest {
#[derive_message_formats]
fn message(&self) -> String {
format!("Test for object identity should be `is not`")
}
fn autofix_title(&self) -> String {
"Convert to `is not`".to_string()
}
}
define_violation!(
pub struct TypeComparison;
);
impl Violation for TypeComparison {
#[derive_message_formats]
fn message(&self) -> String {
format!("Do not compare types, use `isinstance()`")
}
}
define_violation!(
pub struct DoNotUseBareExcept;
);
impl Violation for DoNotUseBareExcept {
#[derive_message_formats]
fn message(&self) -> String {
format!("Do not use bare `except`")
}
}
define_violation!(
pub struct DoNotAssignLambda(pub String);
);
impl AlwaysAutofixableViolation for DoNotAssignLambda {
#[derive_message_formats]
fn message(&self) -> String {
format!("Do not assign a `lambda` expression, use a `def`")
}
fn autofix_title(&self) -> String {
let DoNotAssignLambda(name) = self;
format!("Rewrite `{name}` as a `def`")
}
}
define_violation!(
pub struct AmbiguousVariableName(pub String);
);
impl Violation for AmbiguousVariableName {
#[derive_message_formats]
fn message(&self) -> String {
let AmbiguousVariableName(name) = self;
format!("Ambiguous variable name: `{name}`")
}
}
define_violation!(
pub struct AmbiguousClassName(pub String);
);
impl Violation for AmbiguousClassName {
#[derive_message_formats]
fn message(&self) -> String {
let AmbiguousClassName(name) = self;
format!("Ambiguous class name: `{name}`")
}
}
define_violation!(
pub struct AmbiguousFunctionName(pub String);
);
impl Violation for AmbiguousFunctionName {
#[derive_message_formats]
fn message(&self) -> String {
let AmbiguousFunctionName(name) = self;
format!("Ambiguous function name: `{name}`")
}
}
define_violation!( define_violation!(
pub struct IOError(pub String); pub struct IOError(pub String);
); );
@ -244,48 +59,6 @@ impl Violation for SyntaxError {
} }
} }
// pycodestyle warnings
define_violation!(
pub struct NoNewLineAtEndOfFile;
);
impl AlwaysAutofixableViolation for NoNewLineAtEndOfFile {
#[derive_message_formats]
fn message(&self) -> String {
format!("No newline at end of file")
}
fn autofix_title(&self) -> String {
"Add trailing newline".to_string()
}
}
define_violation!(
pub struct InvalidEscapeSequence(pub char);
);
impl AlwaysAutofixableViolation for InvalidEscapeSequence {
#[derive_message_formats]
fn message(&self) -> String {
let InvalidEscapeSequence(char) = self;
format!("Invalid escape sequence: '\\{char}'")
}
fn autofix_title(&self) -> String {
"Add backslash to escape sequence".to_string()
}
}
define_violation!(
pub struct DocLineTooLong(pub usize, pub usize);
);
impl Violation for DocLineTooLong {
#[derive_message_formats]
fn message(&self) -> String {
let DocLineTooLong(length, limit) = self;
format!("Doc line too long ({length} > {limit} characters)")
}
}
// pyflakes // pyflakes
define_violation!( define_violation!(