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

View file

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

View file

@ -1,7 +1,21 @@
use ruff_macros::derive_message_formats;
use crate::ast::types::Range;
use crate::define_violation;
use crate::registry::Diagnostic;
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
pub fn ambiguous_class_name<F>(name: &str, locate: F) -> Option<Diagnostic>
@ -10,7 +24,7 @@ where
{
if is_ambiguous_name(name) {
Some(Diagnostic::new(
violations::AmbiguousClassName(name.to_string()),
AmbiguousClassName(name.to_string()),
locate(),
))
} else {

View file

@ -1,7 +1,21 @@
use ruff_macros::derive_message_formats;
use crate::ast::types::Range;
use crate::define_violation;
use crate::registry::Diagnostic;
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
pub fn ambiguous_function_name<F>(name: &str, locate: F) -> Option<Diagnostic>
@ -10,7 +24,7 @@ where
{
if is_ambiguous_name(name) {
Some(Diagnostic::new(
violations::AmbiguousFunctionName(name.to_string()),
AmbiguousFunctionName(name.to_string()),
locate(),
))
} else {

View file

@ -1,13 +1,27 @@
use ruff_macros::derive_message_formats;
use crate::ast::types::Range;
use crate::define_violation;
use crate::registry::Diagnostic;
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
pub fn ambiguous_variable_name(name: &str, range: Range) -> Option<Diagnostic> {
if is_ambiguous_name(name) {
Some(Diagnostic::new(
violations::AmbiguousVariableName(name.to_string()),
AmbiguousVariableName(name.to_string()),
range,
))
} else {

View file

@ -1,3 +1,4 @@
use ruff_macros::derive_message_formats;
use rustpython_ast::{Arguments, Location, Stmt, StmtKind};
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::whitespace::leading_space;
use crate::checkers::ast::Checker;
use crate::define_violation;
use crate::fix::Fix;
use crate::registry::Diagnostic;
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
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::Lambda { args, body } = &value.node {
let mut diagnostic = Diagnostic::new(
violations::DoNotAssignLambda(id.to_string()),
Range::from_located(stmt),
);
let mut diagnostic =
Diagnostic::new(DoNotAssignLambda(id.to_string()), Range::from_located(stmt));
if checker.patch(diagnostic.kind.rule()) {
if !match_leading_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_parser::ast::Expr;
use crate::ast::helpers::except_range;
use crate::define_violation;
use crate::registry::Diagnostic;
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
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, .. }))
{
Some(Diagnostic::new(
violations::DoNotUseBareExcept,
DoNotUseBareExcept,
except_range(handler, locator),
))
} else {

View file

@ -1,10 +1,23 @@
use ruff_macros::derive_message_formats;
use rustpython_ast::Location;
use crate::ast::types::Range;
use crate::define_violation;
use crate::registry::Diagnostic;
use crate::rules::pycodestyle::helpers::is_overlong;
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
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,
) {
Some(Diagnostic::new(
violations::DocLineTooLong(line_length, limit),
DocLineTooLong(line_length, limit),
Range::new(
Location::new(lineno + 1, limit),
Location::new(lineno + 1, line_length),

View file

@ -1,10 +1,27 @@
use ruff_macros::derive_message_formats;
use rustpython_ast::Location;
use crate::ast::types::Range;
use crate::define_violation;
use crate::fix::Fix;
use crate::registry::Diagnostic;
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
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 end_location = Location::new(location.row(), location.column() + 2);
let mut diagnostic = Diagnostic::new(
violations::InvalidEscapeSequence(next_char),
InvalidEscapeSequence(next_char),
Range::new(location, end_location),
);
if autofix {

View file

@ -1,10 +1,23 @@
use ruff_macros::derive_message_formats;
use rustpython_ast::Location;
use crate::ast::types::Range;
use crate::define_violation;
use crate::registry::Diagnostic;
use crate::rules::pycodestyle::helpers::is_overlong;
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
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,
) {
Some(Diagnostic::new(
violations::LineTooLong(line_length, limit),
LineTooLong(line_length, limit),
Range::new(
Location::new(lineno + 1, limit),
Location::new(lineno + 1, line_length),

View file

@ -1,15 +1,86 @@
use itertools::izip;
use ruff_macros::derive_message_formats;
use rustc_hash::FxHashMap;
use rustpython_ast::Constant;
use rustpython_parser::ast::{Cmpop, Expr, ExprKind};
use serde::{Deserialize, Serialize};
use crate::ast::helpers;
use crate::ast::types::Range;
use crate::checkers::ast::Checker;
use crate::define_violation;
use crate::fix::Fix;
use crate::registry::Diagnostic;
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
pub fn literal_comparisons(
@ -43,20 +114,16 @@ pub fn literal_comparisons(
)
{
if matches!(op, Cmpop::Eq) {
let diagnostic = Diagnostic::new(
violations::NoneComparison(op.into()),
Range::from_located(comparator),
);
let diagnostic =
Diagnostic::new(NoneComparison(op.into()), Range::from_located(comparator));
if checker.patch(diagnostic.kind.rule()) && !helpers::is_constant_non_singleton(next) {
bad_ops.insert(0, Cmpop::Is);
}
diagnostics.push(diagnostic);
}
if matches!(op, Cmpop::NotEq) {
let diagnostic = Diagnostic::new(
violations::NoneComparison(op.into()),
Range::from_located(comparator),
);
let diagnostic =
Diagnostic::new(NoneComparison(op.into()), Range::from_located(comparator));
if checker.patch(diagnostic.kind.rule()) && !helpers::is_constant_non_singleton(next) {
bad_ops.insert(0, Cmpop::IsNot);
}
@ -72,7 +139,7 @@ pub fn literal_comparisons(
{
if matches!(op, Cmpop::Eq) {
let diagnostic = Diagnostic::new(
violations::TrueFalseComparison(value, op.into()),
TrueFalseComparison(value, op.into()),
Range::from_located(comparator),
);
if checker.patch(diagnostic.kind.rule())
@ -84,7 +151,7 @@ pub fn literal_comparisons(
}
if matches!(op, Cmpop::NotEq) {
let diagnostic = Diagnostic::new(
violations::TrueFalseComparison(value, op.into()),
TrueFalseComparison(value, op.into()),
Range::from_located(comparator),
);
if checker.patch(diagnostic.kind.rule())
@ -109,10 +176,8 @@ pub fn literal_comparisons(
)
{
if matches!(op, Cmpop::Eq) {
let diagnostic = Diagnostic::new(
violations::NoneComparison(op.into()),
Range::from_located(next),
);
let diagnostic =
Diagnostic::new(NoneComparison(op.into()), Range::from_located(next));
if checker.patch(diagnostic.kind.rule())
&& !helpers::is_constant_non_singleton(comparator)
{
@ -121,10 +186,8 @@ pub fn literal_comparisons(
diagnostics.push(diagnostic);
}
if matches!(op, Cmpop::NotEq) {
let diagnostic = Diagnostic::new(
violations::NoneComparison(op.into()),
Range::from_located(next),
);
let diagnostic =
Diagnostic::new(NoneComparison(op.into()), Range::from_located(next));
if checker.patch(diagnostic.kind.rule())
&& !helpers::is_constant_non_singleton(comparator)
{
@ -142,7 +205,7 @@ pub fn literal_comparisons(
{
if matches!(op, Cmpop::Eq) {
let diagnostic = Diagnostic::new(
violations::TrueFalseComparison(value, op.into()),
TrueFalseComparison(value, op.into()),
Range::from_located(next),
);
if checker.patch(diagnostic.kind.rule())
@ -154,7 +217,7 @@ pub fn literal_comparisons(
}
if matches!(op, Cmpop::NotEq) {
let diagnostic = Diagnostic::new(
violations::TrueFalseComparison(value, op.into()),
TrueFalseComparison(value, op.into()),
Range::from_located(next),
);
if checker.patch(diagnostic.kind.rule())

View file

@ -1,9 +1,21 @@
use ruff_macros::derive_message_formats;
use rustpython_ast::Location;
use crate::ast::types::Range;
use crate::ast::whitespace::leading_space;
use crate::define_violation;
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
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') {
Some(Diagnostic::new(
violations::MixedSpacesAndTabs,
MixedSpacesAndTabs,
Range::new(
Location::new(lineno + 1, 0),
Location::new(lineno + 1, indent.chars().count()),

View file

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

View file

@ -1,10 +1,26 @@
use ruff_macros::derive_message_formats;
use rustpython_ast::Location;
use crate::ast::types::Range;
use crate::define_violation;
use crate::fix::Fix;
use crate::registry::Diagnostic;
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
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() {
// Both locations are at the end of the file (and thus the same).
let location = Location::new(contents.lines().count(), line.len());
let mut diagnostic = Diagnostic::new(
violations::NoNewLineAtEndOfFile,
Range::new(location, location),
);
let mut diagnostic =
Diagnostic::new(NoNewLineAtEndOfFile, Range::new(location, location));
if autofix {
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_parser::ast::{Cmpop, Expr, ExprKind};
use crate::ast::types::Range;
use crate::checkers::ast::Checker;
use crate::define_violation;
use crate::fix::Fix;
use crate::registry::Diagnostic;
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
pub fn not_tests(
@ -30,10 +60,8 @@ pub fn not_tests(
match op {
Cmpop::In => {
if check_not_in {
let mut diagnostic = Diagnostic::new(
violations::NotInTest,
Range::from_located(operand),
);
let mut diagnostic =
Diagnostic::new(NotInTest, Range::from_located(operand));
if checker.patch(diagnostic.kind.rule()) && should_fix {
diagnostic.amend(Fix::replacement(
compare(left, &[Cmpop::NotIn], comparators, checker.stylist),
@ -46,10 +74,8 @@ pub fn not_tests(
}
Cmpop::Is => {
if check_not_is {
let mut diagnostic = Diagnostic::new(
violations::NotIsTest,
Range::from_located(operand),
);
let mut diagnostic =
Diagnostic::new(NotIsTest, Range::from_located(operand));
if checker.patch(diagnostic.kind.rule()) && should_fix {
diagnostic.amend(Fix::replacement(
compare(left, &[Cmpop::IsNot], comparators, checker.stylist),

View file

@ -1,10 +1,22 @@
use itertools::izip;
use ruff_macros::derive_message_formats;
use rustpython_ast::Constant;
use rustpython_parser::ast::{Cmpop, Expr, ExprKind};
use crate::ast::types::Range;
use crate::define_violation;
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
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
}
) {
diagnostics
.push(Diagnostic::new(violations::TypeComparison, location));
diagnostics.push(Diagnostic::new(TypeComparison, location));
}
}
}
@ -40,7 +51,7 @@ pub fn type_comparison(ops: &[Cmpop], comparators: &[Expr], location: Range) ->
if let ExprKind::Name { id, .. } = &value.node {
// Ex) types.IntType
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!(
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
define_violation!(