From 858af8debb981d19a38e2bead34d1c60b062993e Mon Sep 17 00:00:00 2001 From: Aarni Koskela Date: Thu, 2 Feb 2023 21:47:43 +0200 Subject: [PATCH] Move pyupgrade violations to rule modules (#2490) --- src/registry.rs | 64 +- ...convert_named_tuple_functional_to_class.rs | 24 +- .../convert_typed_dict_functional_to_class.rs | 24 +- .../pyupgrade/rules/datetime_utc_alias.rs | 28 +- .../rules/deprecated_unittest_alias.rs | 25 +- .../pyupgrade/rules/extraneous_parentheses.rs | 20 +- src/rules/pyupgrade/rules/f_strings.rs | 20 +- src/rules/pyupgrade/rules/format_literals.rs | 20 +- src/rules/pyupgrade/rules/functools_cache.rs | 20 +- .../pyupgrade/rules/import_replacements.rs | 3 +- .../rules/lru_cache_without_parameters.rs | 20 +- src/rules/pyupgrade/rules/mod.rs | 74 ++- src/rules/pyupgrade/rules/native_literals.rs | 44 +- src/rules/pyupgrade/rules/open_alias.rs | 20 +- src/rules/pyupgrade/rules/os_error_alias.rs | 26 +- .../pyupgrade/rules/outdated_version_block.rs | 4 +- .../rules/printf_string_formatting.rs | 24 +- .../pyupgrade/rules/redundant_open_modes.rs | 35 +- .../pyupgrade/rules/replace_stdout_stderr.rs | 21 +- .../rules/replace_universal_newlines.rs | 20 +- .../pyupgrade/rules/rewrite_c_element_tree.rs | 21 +- .../pyupgrade/rules/rewrite_mock_import.rs | 38 +- .../rules/rewrite_unicode_literal.rs | 21 +- .../pyupgrade/rules/rewrite_yield_from.rs | 21 +- src/rules/pyupgrade/rules/super_args.rs | 7 +- .../rules/super_call_with_parameters.rs | 17 + .../pyupgrade/rules/type_of_primitive.rs | 27 +- .../pyupgrade/rules/typing_text_str_alias.rs | 21 +- .../rules/unnecessary_builtin_import.rs | 30 +- .../rules/unnecessary_coding_comment.rs | 21 +- .../rules/unnecessary_encode_utf8.rs | 27 +- .../rules/unnecessary_future_import.rs | 30 +- .../rules/unpack_list_comprehension.rs | 24 +- .../pyupgrade/rules/use_pep585_annotation.rs | 28 +- .../pyupgrade/rules/use_pep604_annotation.rs | 24 +- .../pyupgrade/rules/useless_metaclass_type.rs | 20 +- .../rules/useless_object_inheritance.rs | 23 +- src/violations.rs | 557 +----------------- 38 files changed, 778 insertions(+), 715 deletions(-) diff --git a/src/registry.rs b/src/registry.rs index 9201304fbd..c4bb6adabc 100644 --- a/src/registry.rs +++ b/src/registry.rs @@ -224,38 +224,38 @@ ruff_macros::define_rule_mapping!( SIM300 => violations::YodaConditions, SIM401 => violations::DictGetWithDefault, // pyupgrade - UP001 => violations::UselessMetaclassType, - UP003 => violations::TypeOfPrimitive, - UP004 => violations::UselessObjectInheritance, - UP005 => violations::DeprecatedUnittestAlias, - UP006 => violations::UsePEP585Annotation, - UP007 => violations::UsePEP604Annotation, - UP008 => violations::SuperCallWithParameters, - UP009 => violations::PEP3120UnnecessaryCodingComment, - UP010 => violations::UnnecessaryFutureImport, - UP011 => violations::LRUCacheWithoutParameters, - UP012 => violations::UnnecessaryEncodeUTF8, - UP013 => violations::ConvertTypedDictFunctionalToClass, - UP014 => violations::ConvertNamedTupleFunctionalToClass, - UP015 => violations::RedundantOpenModes, - UP017 => violations::DatetimeTimezoneUTC, - UP018 => violations::NativeLiterals, - UP019 => violations::TypingTextStrAlias, - UP020 => violations::OpenAlias, - UP021 => violations::ReplaceUniversalNewlines, - UP022 => violations::ReplaceStdoutStderr, - UP023 => violations::RewriteCElementTree, - UP024 => violations::OSErrorAlias, - UP025 => violations::RewriteUnicodeLiteral, - UP026 => violations::RewriteMockImport, - UP027 => violations::RewriteListComprehension, - UP028 => violations::RewriteYieldFrom, - UP029 => violations::UnnecessaryBuiltinImport, - UP030 => violations::FormatLiterals, - UP031 => violations::PrintfStringFormatting, - UP032 => violations::FString, - UP033 => violations::FunctoolsCache, - UP034 => violations::ExtraneousParentheses, + UP001 => rules::pyupgrade::rules::UselessMetaclassType, + UP003 => rules::pyupgrade::rules::TypeOfPrimitive, + UP004 => rules::pyupgrade::rules::UselessObjectInheritance, + UP005 => rules::pyupgrade::rules::DeprecatedUnittestAlias, + UP006 => rules::pyupgrade::rules::UsePEP585Annotation, + UP007 => rules::pyupgrade::rules::UsePEP604Annotation, + UP008 => rules::pyupgrade::rules::SuperCallWithParameters, + UP009 => rules::pyupgrade::rules::PEP3120UnnecessaryCodingComment, + UP010 => rules::pyupgrade::rules::UnnecessaryFutureImport, + UP011 => rules::pyupgrade::rules::LRUCacheWithoutParameters, + UP012 => rules::pyupgrade::rules::UnnecessaryEncodeUTF8, + UP013 => rules::pyupgrade::rules::ConvertTypedDictFunctionalToClass, + UP014 => rules::pyupgrade::rules::ConvertNamedTupleFunctionalToClass, + UP015 => rules::pyupgrade::rules::RedundantOpenModes, + UP017 => rules::pyupgrade::rules::DatetimeTimezoneUTC, + UP018 => rules::pyupgrade::rules::NativeLiterals, + UP019 => rules::pyupgrade::rules::TypingTextStrAlias, + UP020 => rules::pyupgrade::rules::OpenAlias, + UP021 => rules::pyupgrade::rules::ReplaceUniversalNewlines, + UP022 => rules::pyupgrade::rules::ReplaceStdoutStderr, + UP023 => rules::pyupgrade::rules::RewriteCElementTree, + UP024 => rules::pyupgrade::rules::OSErrorAlias, + UP025 => rules::pyupgrade::rules::RewriteUnicodeLiteral, + UP026 => rules::pyupgrade::rules::RewriteMockImport, + UP027 => rules::pyupgrade::rules::RewriteListComprehension, + UP028 => rules::pyupgrade::rules::RewriteYieldFrom, + UP029 => rules::pyupgrade::rules::UnnecessaryBuiltinImport, + UP030 => rules::pyupgrade::rules::FormatLiterals, + UP031 => rules::pyupgrade::rules::PrintfStringFormatting, + UP032 => rules::pyupgrade::rules::FString, + UP033 => rules::pyupgrade::rules::FunctoolsCache, + UP034 => rules::pyupgrade::rules::ExtraneousParentheses, UP035 => rules::pyupgrade::rules::ImportReplacements, UP036 => rules::pyupgrade::rules::OutdatedVersionBlock, // pydocstyle diff --git a/src/rules/pyupgrade/rules/convert_named_tuple_functional_to_class.rs b/src/rules/pyupgrade/rules/convert_named_tuple_functional_to_class.rs index c37987ed2e..c976568867 100644 --- a/src/rules/pyupgrade/rules/convert_named_tuple_functional_to_class.rs +++ b/src/rules/pyupgrade/rules/convert_named_tuple_functional_to_class.rs @@ -1,5 +1,8 @@ +use crate::define_violation; +use crate::violation::AlwaysAutofixableViolation; use anyhow::{bail, Result}; use log::debug; +use ruff_macros::derive_message_formats; use rustpython_ast::{Constant, Expr, ExprContext, ExprKind, Keyword, Stmt, StmtKind}; use crate::ast::helpers::{create_expr, create_stmt, unparse_stmt}; @@ -10,7 +13,24 @@ use crate::python::identifiers::is_identifier; use crate::python::keyword::KWLIST; use crate::registry::Diagnostic; use crate::source_code::Stylist; -use crate::violations; + +define_violation!( + pub struct ConvertNamedTupleFunctionalToClass { + pub name: String, + } +); +impl AlwaysAutofixableViolation for ConvertNamedTupleFunctionalToClass { + #[derive_message_formats] + fn message(&self) -> String { + let ConvertNamedTupleFunctionalToClass { name } = self; + format!("Convert `{name}` from `NamedTuple` functional to class syntax") + } + + fn autofix_title(&self) -> String { + let ConvertNamedTupleFunctionalToClass { name } = self; + format!("Convert `{name}` to class syntax") + } +} /// Return the typename, args, keywords, and base class. fn match_named_tuple_assign<'a>( @@ -154,7 +174,7 @@ pub fn convert_named_tuple_functional_to_class( return; }; let mut diagnostic = Diagnostic::new( - violations::ConvertNamedTupleFunctionalToClass { + ConvertNamedTupleFunctionalToClass { name: typename.to_string(), }, Range::from_located(stmt), diff --git a/src/rules/pyupgrade/rules/convert_typed_dict_functional_to_class.rs b/src/rules/pyupgrade/rules/convert_typed_dict_functional_to_class.rs index 635939a5be..2a21760ee8 100644 --- a/src/rules/pyupgrade/rules/convert_typed_dict_functional_to_class.rs +++ b/src/rules/pyupgrade/rules/convert_typed_dict_functional_to_class.rs @@ -1,5 +1,8 @@ +use crate::define_violation; +use crate::violation::AlwaysAutofixableViolation; use anyhow::{bail, Result}; use log::debug; +use ruff_macros::derive_message_formats; use rustpython_ast::{Constant, Expr, ExprContext, ExprKind, Keyword, Stmt, StmtKind}; use crate::ast::helpers::{create_expr, create_stmt, unparse_stmt}; @@ -10,7 +13,24 @@ use crate::python::identifiers::is_identifier; use crate::python::keyword::KWLIST; use crate::registry::Diagnostic; use crate::source_code::Stylist; -use crate::violations; + +define_violation!( + pub struct ConvertTypedDictFunctionalToClass { + pub name: String, + } +); +impl AlwaysAutofixableViolation for ConvertTypedDictFunctionalToClass { + #[derive_message_formats] + fn message(&self) -> String { + let ConvertTypedDictFunctionalToClass { name } = self; + format!("Convert `{name}` from `TypedDict` functional to class syntax") + } + + fn autofix_title(&self) -> String { + let ConvertTypedDictFunctionalToClass { name } = self; + format!("Convert `{name}` to class syntax") + } +} /// Return the class name, arguments, keywords and base class for a `TypedDict` /// assignment. @@ -201,7 +221,7 @@ pub fn convert_typed_dict_functional_to_class( }; let mut diagnostic = Diagnostic::new( - violations::ConvertTypedDictFunctionalToClass { + ConvertTypedDictFunctionalToClass { name: class_name.to_string(), }, Range::from_located(stmt), diff --git a/src/rules/pyupgrade/rules/datetime_utc_alias.rs b/src/rules/pyupgrade/rules/datetime_utc_alias.rs index 79f045d3cf..f8607c5177 100644 --- a/src/rules/pyupgrade/rules/datetime_utc_alias.rs +++ b/src/rules/pyupgrade/rules/datetime_utc_alias.rs @@ -1,3 +1,6 @@ +use crate::violation::{Availability, Violation}; +use crate::{define_violation, AutofixKind}; +use ruff_macros::derive_message_formats; use rustpython_ast::Expr; use crate::ast::helpers::collect_call_path; @@ -5,7 +8,28 @@ use crate::ast::types::Range; use crate::checkers::ast::Checker; use crate::fix::Fix; use crate::registry::Diagnostic; -use crate::violations; + +define_violation!( + pub struct DatetimeTimezoneUTC { + pub straight_import: bool, + } +); +impl Violation for DatetimeTimezoneUTC { + const AUTOFIX: Option = Some(AutofixKind::new(Availability::Always)); + + #[derive_message_formats] + fn message(&self) -> String { + format!("Use `datetime.UTC` alias") + } + + fn autofix_title_formatter(&self) -> Option String> { + if self.straight_import { + Some(|_| "Convert to `datetime.UTC` alias".to_string()) + } else { + None + } + } +} /// UP017 pub fn datetime_utc_alias(checker: &mut Checker, expr: &Expr) { @@ -14,7 +38,7 @@ pub fn datetime_utc_alias(checker: &mut Checker, expr: &Expr) { }) { let straight_import = collect_call_path(expr).as_slice() == ["datetime", "timezone", "utc"]; let mut diagnostic = Diagnostic::new( - violations::DatetimeTimezoneUTC { straight_import }, + DatetimeTimezoneUTC { straight_import }, Range::from_located(expr), ); if checker.patch(diagnostic.kind.rule()) { diff --git a/src/rules/pyupgrade/rules/deprecated_unittest_alias.rs b/src/rules/pyupgrade/rules/deprecated_unittest_alias.rs index b5b33cdbb5..dfbafca860 100644 --- a/src/rules/pyupgrade/rules/deprecated_unittest_alias.rs +++ b/src/rules/pyupgrade/rules/deprecated_unittest_alias.rs @@ -1,4 +1,7 @@ +use crate::define_violation; +use crate::violation::AlwaysAutofixableViolation; use once_cell::sync::Lazy; +use ruff_macros::derive_message_formats; use rustc_hash::FxHashMap; use rustpython_ast::{Expr, ExprKind}; @@ -6,7 +9,25 @@ use crate::ast::types::Range; use crate::checkers::ast::Checker; use crate::fix::Fix; use crate::registry::Diagnostic; -use crate::violations; + +define_violation!( + pub struct DeprecatedUnittestAlias { + pub alias: String, + pub target: String, + } +); +impl AlwaysAutofixableViolation for DeprecatedUnittestAlias { + #[derive_message_formats] + fn message(&self) -> String { + let DeprecatedUnittestAlias { alias, target } = self; + format!("`{alias}` is deprecated, use `{target}`") + } + + fn autofix_title(&self) -> String { + let DeprecatedUnittestAlias { alias, target } = self; + format!("Replace `{target}` with `{alias}`") + } +} static DEPRECATED_ALIASES: Lazy> = Lazy::new(|| { FxHashMap::from_iter([ @@ -43,7 +64,7 @@ pub fn deprecated_unittest_alias(checker: &mut Checker, expr: &Expr) { return; } let mut diagnostic = Diagnostic::new( - violations::DeprecatedUnittestAlias { + DeprecatedUnittestAlias { alias: attr.to_string(), target: target.to_string(), }, diff --git a/src/rules/pyupgrade/rules/extraneous_parentheses.rs b/src/rules/pyupgrade/rules/extraneous_parentheses.rs index c7ee2508f4..9fe4e16b41 100644 --- a/src/rules/pyupgrade/rules/extraneous_parentheses.rs +++ b/src/rules/pyupgrade/rules/extraneous_parentheses.rs @@ -1,3 +1,6 @@ +use crate::define_violation; +use crate::violation::AlwaysAutofixableViolation; +use ruff_macros::derive_message_formats; use rustpython_parser::lexer::{LexResult, Tok}; use crate::ast::types::Range; @@ -5,7 +8,20 @@ use crate::fix::Fix; use crate::registry::{Diagnostic, Rule}; use crate::settings::{flags, Settings}; use crate::source_code::Locator; -use crate::violations; + +define_violation!( + pub struct ExtraneousParentheses; +); +impl AlwaysAutofixableViolation for ExtraneousParentheses { + #[derive_message_formats] + fn message(&self) -> String { + format!("Avoid extraneous parentheses") + } + + fn autofix_title(&self) -> String { + "Remove extraneous parentheses".to_string() + } +} // See: https://github.com/asottile/pyupgrade/blob/97ed6fb3cf2e650d4f762ba231c3f04c41797710/pyupgrade/_main.py#L148 fn match_extraneous_parentheses(tokens: &[LexResult], mut i: usize) -> Option<(usize, usize)> { @@ -120,7 +136,7 @@ pub fn extraneous_parentheses( return diagnostics; }; let mut diagnostic = - Diagnostic::new(violations::ExtraneousParentheses, Range::new(*start, *end)); + Diagnostic::new(ExtraneousParentheses, Range::new(*start, *end)); if matches!(autofix, flags::Autofix::Enabled) && settings.rules.should_fix(&Rule::ExtraneousParentheses) { diff --git a/src/rules/pyupgrade/rules/f_strings.rs b/src/rules/pyupgrade/rules/f_strings.rs index 6abd1cc3c4..52f93dfbb2 100644 --- a/src/rules/pyupgrade/rules/f_strings.rs +++ b/src/rules/pyupgrade/rules/f_strings.rs @@ -1,3 +1,6 @@ +use crate::define_violation; +use crate::violation::AlwaysAutofixableViolation; +use ruff_macros::derive_message_formats; use rustc_hash::FxHashMap; use rustpython_ast::{Constant, Expr, ExprKind, KeywordData}; use rustpython_common::format::{ @@ -13,7 +16,20 @@ use crate::registry::Diagnostic; use crate::rules::pydocstyle::helpers::{leading_quote, trailing_quote}; use crate::rules::pyflakes::format::FormatSummary; use crate::rules::pyupgrade::helpers::curly_escape; -use crate::violations; + +define_violation!( + pub struct FString; +); +impl AlwaysAutofixableViolation for FString { + #[derive_message_formats] + fn message(&self) -> String { + format!("Use f-string instead of `format` call") + } + + fn autofix_title(&self) -> String { + "Convert to f-string".to_string() + } +} /// Like [`FormatSummary`], but maps positional and keyword arguments to their /// values. For example, given `{a} {b}".format(a=1, b=2)`, `FormatFunction` @@ -257,7 +273,7 @@ pub(crate) fn f_strings(checker: &mut Checker, summary: &FormatSummary, expr: &E return; } - let mut diagnostic = Diagnostic::new(violations::FString, Range::from_located(expr)); + let mut diagnostic = Diagnostic::new(FString, Range::from_located(expr)); if checker.patch(diagnostic.kind.rule()) { diagnostic.amend(Fix::replacement( contents, diff --git a/src/rules/pyupgrade/rules/format_literals.rs b/src/rules/pyupgrade/rules/format_literals.rs index cb7c79d4f0..3641c0794f 100644 --- a/src/rules/pyupgrade/rules/format_literals.rs +++ b/src/rules/pyupgrade/rules/format_literals.rs @@ -1,7 +1,10 @@ +use crate::define_violation; +use crate::violation::AlwaysAutofixableViolation; use anyhow::{anyhow, bail, Result}; use libcst_native::{Arg, Codegen, CodegenState, Expression}; use once_cell::sync::Lazy; use regex::Regex; +use ruff_macros::derive_message_formats; use rustpython_ast::Expr; use crate::ast::types::Range; @@ -11,7 +14,20 @@ use crate::fix::Fix; use crate::registry::Diagnostic; use crate::rules::pyflakes::format::FormatSummary; use crate::source_code::{Locator, Stylist}; -use crate::violations; + +define_violation!( + pub struct FormatLiterals; +); +impl AlwaysAutofixableViolation for FormatLiterals { + #[derive_message_formats] + fn message(&self) -> String { + format!("Use implicit references for positional format fields") + } + + fn autofix_title(&self) -> String { + "Remove explicit positional indexes".to_string() + } +} // An opening curly brace, followed by any integer, followed by any text, // followed by a closing brace. @@ -112,7 +128,7 @@ pub(crate) fn format_literals(checker: &mut Checker, summary: &FormatSummary, ex return; } - let mut diagnostic = Diagnostic::new(violations::FormatLiterals, Range::from_located(expr)); + let mut diagnostic = Diagnostic::new(FormatLiterals, Range::from_located(expr)); if checker.patch(diagnostic.kind.rule()) { // Currently, the only issue we know of is in LibCST: // https://github.com/Instagram/LibCST/issues/846 diff --git a/src/rules/pyupgrade/rules/functools_cache.rs b/src/rules/pyupgrade/rules/functools_cache.rs index b456cb966b..72f431693a 100644 --- a/src/rules/pyupgrade/rules/functools_cache.rs +++ b/src/rules/pyupgrade/rules/functools_cache.rs @@ -1,3 +1,6 @@ +use crate::define_violation; +use crate::violation::AlwaysAutofixableViolation; +use ruff_macros::derive_message_formats; use rustpython_ast::{Constant, ExprKind, KeywordData}; use rustpython_parser::ast::Expr; @@ -6,7 +9,20 @@ use crate::ast::types::Range; use crate::checkers::ast::Checker; use crate::fix::Fix; use crate::registry::Diagnostic; -use crate::violations; + +define_violation!( + pub struct FunctoolsCache; +); +impl AlwaysAutofixableViolation for FunctoolsCache { + #[derive_message_formats] + fn message(&self) -> String { + format!("Use `@functools.cache` instead of `@functools.lru_cache(maxsize=None)`") + } + + fn autofix_title(&self) -> String { + "Rewrite with `@functools.cache".to_string() + } +} /// UP033 pub fn functools_cache(checker: &mut Checker, decorator_list: &[Expr]) { @@ -37,7 +53,7 @@ pub fn functools_cache(checker: &mut Checker, decorator_list: &[Expr]) { ) { let mut diagnostic = Diagnostic::new( - violations::FunctoolsCache, + FunctoolsCache, Range::new(func.end_location.unwrap(), expr.end_location.unwrap()), ); if checker.patch(diagnostic.kind.rule()) { diff --git a/src/rules/pyupgrade/rules/import_replacements.rs b/src/rules/pyupgrade/rules/import_replacements.rs index f3ca2a1494..dfcfe1674b 100644 --- a/src/rules/pyupgrade/rules/import_replacements.rs +++ b/src/rules/pyupgrade/rules/import_replacements.rs @@ -1,7 +1,6 @@ use itertools::Itertools; -use rustpython_ast::{Alias, AliasData, Stmt}; - use ruff_macros::derive_message_formats; +use rustpython_ast::{Alias, AliasData, Stmt}; use crate::ast::types::Range; use crate::ast::whitespace::indentation; diff --git a/src/rules/pyupgrade/rules/lru_cache_without_parameters.rs b/src/rules/pyupgrade/rules/lru_cache_without_parameters.rs index c2fadf1f00..bae41d2fce 100644 --- a/src/rules/pyupgrade/rules/lru_cache_without_parameters.rs +++ b/src/rules/pyupgrade/rules/lru_cache_without_parameters.rs @@ -1,3 +1,6 @@ +use crate::define_violation; +use crate::violation::AlwaysAutofixableViolation; +use ruff_macros::derive_message_formats; use rustpython_ast::ExprKind; use rustpython_parser::ast::Expr; @@ -6,7 +9,20 @@ use crate::ast::types::Range; use crate::checkers::ast::Checker; use crate::fix::Fix; use crate::registry::Diagnostic; -use crate::violations; + +define_violation!( + pub struct LRUCacheWithoutParameters; +); +impl AlwaysAutofixableViolation for LRUCacheWithoutParameters { + #[derive_message_formats] + fn message(&self) -> String { + format!("Unnecessary parameters to `functools.lru_cache`") + } + + fn autofix_title(&self) -> String { + "Remove unnecessary parameters".to_string() + } +} /// UP011 pub fn lru_cache_without_parameters(checker: &mut Checker, decorator_list: &[Expr]) { @@ -27,7 +43,7 @@ pub fn lru_cache_without_parameters(checker: &mut Checker, decorator_list: &[Exp }) { let mut diagnostic = Diagnostic::new( - violations::LRUCacheWithoutParameters, + LRUCacheWithoutParameters, Range::new(func.end_location.unwrap(), expr.end_location.unwrap()), ); if checker.patch(diagnostic.kind.rule()) { diff --git a/src/rules/pyupgrade/rules/mod.rs b/src/rules/pyupgrade/rules/mod.rs index c58ec44a3d..8875beaa33 100644 --- a/src/rules/pyupgrade/rules/mod.rs +++ b/src/rules/pyupgrade/rules/mod.rs @@ -1,38 +1,48 @@ -pub(crate) use convert_named_tuple_functional_to_class::convert_named_tuple_functional_to_class; -pub(crate) use convert_typed_dict_functional_to_class::convert_typed_dict_functional_to_class; -pub(crate) use datetime_utc_alias::datetime_utc_alias; -pub(crate) use deprecated_unittest_alias::deprecated_unittest_alias; -pub(crate) use extraneous_parentheses::extraneous_parentheses; -pub(crate) use f_strings::f_strings; -pub(crate) use format_literals::format_literals; -pub(crate) use functools_cache::functools_cache; +pub(crate) use convert_named_tuple_functional_to_class::{ + convert_named_tuple_functional_to_class, ConvertNamedTupleFunctionalToClass, +}; +pub(crate) use convert_typed_dict_functional_to_class::{ + convert_typed_dict_functional_to_class, ConvertTypedDictFunctionalToClass, +}; +pub(crate) use datetime_utc_alias::{datetime_utc_alias, DatetimeTimezoneUTC}; +pub(crate) use deprecated_unittest_alias::{deprecated_unittest_alias, DeprecatedUnittestAlias}; +pub(crate) use extraneous_parentheses::{extraneous_parentheses, ExtraneousParentheses}; +pub(crate) use f_strings::{f_strings, FString}; +pub(crate) use format_literals::{format_literals, FormatLiterals}; +pub(crate) use functools_cache::{functools_cache, FunctoolsCache}; pub(crate) use import_replacements::{import_replacements, ImportReplacements}; -pub(crate) use lru_cache_without_parameters::lru_cache_without_parameters; -pub(crate) use native_literals::native_literals; -pub(crate) use open_alias::open_alias; -pub(crate) use os_error_alias::os_error_alias; +pub(crate) use lru_cache_without_parameters::{ + lru_cache_without_parameters, LRUCacheWithoutParameters, +}; +pub(crate) use native_literals::{native_literals, NativeLiterals}; +pub(crate) use open_alias::{open_alias, OpenAlias}; +pub(crate) use os_error_alias::{os_error_alias, OSErrorAlias}; pub(crate) use outdated_version_block::{outdated_version_block, OutdatedVersionBlock}; -pub(crate) use printf_string_formatting::printf_string_formatting; -pub(crate) use redundant_open_modes::redundant_open_modes; -pub(crate) use replace_stdout_stderr::replace_stdout_stderr; -pub(crate) use replace_universal_newlines::replace_universal_newlines; -pub(crate) use rewrite_c_element_tree::replace_c_element_tree; -pub(crate) use rewrite_mock_import::{rewrite_mock_attribute, rewrite_mock_import}; -pub(crate) use rewrite_unicode_literal::rewrite_unicode_literal; -pub(crate) use rewrite_yield_from::rewrite_yield_from; +pub(crate) use printf_string_formatting::{printf_string_formatting, PrintfStringFormatting}; +pub(crate) use redundant_open_modes::{redundant_open_modes, RedundantOpenModes}; +pub(crate) use replace_stdout_stderr::{replace_stdout_stderr, ReplaceStdoutStderr}; +pub(crate) use replace_universal_newlines::{replace_universal_newlines, ReplaceUniversalNewlines}; +pub(crate) use rewrite_c_element_tree::{replace_c_element_tree, RewriteCElementTree}; +pub(crate) use rewrite_mock_import::{ + rewrite_mock_attribute, rewrite_mock_import, RewriteMockImport, +}; +pub(crate) use rewrite_unicode_literal::{rewrite_unicode_literal, RewriteUnicodeLiteral}; +pub(crate) use rewrite_yield_from::{rewrite_yield_from, RewriteYieldFrom}; pub(crate) use super_args::super_args; -pub(crate) use super_call_with_parameters::super_call_with_parameters; -pub(crate) use type_of_primitive::type_of_primitive; -pub(crate) use typing_text_str_alias::typing_text_str_alias; -pub(crate) use unnecessary_builtin_import::unnecessary_builtin_import; -pub(crate) use unnecessary_coding_comment::unnecessary_coding_comment; -pub(crate) use unnecessary_encode_utf8::unnecessary_encode_utf8; -pub(crate) use unnecessary_future_import::unnecessary_future_import; -pub(crate) use unpack_list_comprehension::unpack_list_comprehension; -pub(crate) use use_pep585_annotation::use_pep585_annotation; -pub(crate) use use_pep604_annotation::use_pep604_annotation; -pub(crate) use useless_metaclass_type::useless_metaclass_type; -pub(crate) use useless_object_inheritance::useless_object_inheritance; +pub(crate) use super_call_with_parameters::{super_call_with_parameters, SuperCallWithParameters}; +pub(crate) use type_of_primitive::{type_of_primitive, TypeOfPrimitive}; +pub(crate) use typing_text_str_alias::{typing_text_str_alias, TypingTextStrAlias}; +pub(crate) use unnecessary_builtin_import::{unnecessary_builtin_import, UnnecessaryBuiltinImport}; +pub(crate) use unnecessary_coding_comment::{ + unnecessary_coding_comment, PEP3120UnnecessaryCodingComment, +}; +pub(crate) use unnecessary_encode_utf8::{unnecessary_encode_utf8, UnnecessaryEncodeUTF8}; +pub(crate) use unnecessary_future_import::{unnecessary_future_import, UnnecessaryFutureImport}; +pub(crate) use unpack_list_comprehension::{unpack_list_comprehension, RewriteListComprehension}; +pub(crate) use use_pep585_annotation::{use_pep585_annotation, UsePEP585Annotation}; +pub(crate) use use_pep604_annotation::{use_pep604_annotation, UsePEP604Annotation}; +pub(crate) use useless_metaclass_type::{useless_metaclass_type, UselessMetaclassType}; +pub(crate) use useless_object_inheritance::{useless_object_inheritance, UselessObjectInheritance}; mod convert_named_tuple_functional_to_class; mod convert_typed_dict_functional_to_class; diff --git a/src/rules/pyupgrade/rules/native_literals.rs b/src/rules/pyupgrade/rules/native_literals.rs index 5fec25f781..ddf81c9228 100644 --- a/src/rules/pyupgrade/rules/native_literals.rs +++ b/src/rules/pyupgrade/rules/native_literals.rs @@ -1,13 +1,49 @@ +use crate::define_violation; +use crate::violation::AlwaysAutofixableViolation; +use ruff_macros::derive_message_formats; use rustpython_ast::{Constant, Expr, ExprKind, Keyword}; use rustpython_parser::lexer; use rustpython_parser::lexer::Tok; +use serde::{Deserialize, Serialize}; +use std::fmt; use crate::ast::types::Range; use crate::checkers::ast::Checker; use crate::fix::Fix; use crate::registry::Diagnostic; -use crate::violations; -use crate::violations::LiteralType; + +#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)] +pub enum LiteralType { + Str, + Bytes, +} + +impl fmt::Display for LiteralType { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + match self { + LiteralType::Str => fmt.write_str("str"), + LiteralType::Bytes => fmt.write_str("bytes"), + } + } +} + +define_violation!( + pub struct NativeLiterals { + pub literal_type: LiteralType, + } +); +impl AlwaysAutofixableViolation for NativeLiterals { + #[derive_message_formats] + fn message(&self) -> String { + let NativeLiterals { literal_type } = self; + format!("Unnecessary call to `{literal_type}`") + } + + fn autofix_title(&self) -> String { + let NativeLiterals { literal_type } = self; + format!("Replace with `{literal_type}`") + } +} /// UP018 pub fn native_literals( @@ -25,7 +61,7 @@ pub fn native_literals( if (id == "str" || id == "bytes") && checker.is_builtin(id) { let Some(arg) = args.get(0) else { - let mut diagnostic = Diagnostic::new(violations::NativeLiterals{literal_type:if id == "str" { + let mut diagnostic = Diagnostic::new(NativeLiterals{literal_type:if id == "str" { LiteralType::Str } else { LiteralType::Bytes @@ -94,7 +130,7 @@ pub fn native_literals( } let mut diagnostic = Diagnostic::new( - violations::NativeLiterals { + NativeLiterals { literal_type: if id == "str" { LiteralType::Str } else { diff --git a/src/rules/pyupgrade/rules/open_alias.rs b/src/rules/pyupgrade/rules/open_alias.rs index 5f01be396a..52bec007e3 100644 --- a/src/rules/pyupgrade/rules/open_alias.rs +++ b/src/rules/pyupgrade/rules/open_alias.rs @@ -1,10 +1,26 @@ +use crate::define_violation; +use crate::violation::AlwaysAutofixableViolation; +use ruff_macros::derive_message_formats; use rustpython_ast::Expr; use crate::ast::types::Range; use crate::checkers::ast::Checker; use crate::fix::Fix; use crate::registry::Diagnostic; -use crate::violations; + +define_violation!( + pub struct OpenAlias; +); +impl AlwaysAutofixableViolation for OpenAlias { + #[derive_message_formats] + fn message(&self) -> String { + format!("Use builtin `open`") + } + + fn autofix_title(&self) -> String { + "Replace with builtin `open`".to_string() + } +} /// UP020 pub fn open_alias(checker: &mut Checker, expr: &Expr, func: &Expr) { @@ -12,7 +28,7 @@ pub fn open_alias(checker: &mut Checker, expr: &Expr, func: &Expr) { .resolve_call_path(func) .map_or(false, |call_path| call_path.as_slice() == ["io", "open"]) { - let mut diagnostic = Diagnostic::new(violations::OpenAlias, Range::from_located(expr)); + let mut diagnostic = Diagnostic::new(OpenAlias, Range::from_located(expr)); if checker.patch(diagnostic.kind.rule()) { diagnostic.amend(Fix::replacement( "open".to_string(), diff --git a/src/rules/pyupgrade/rules/os_error_alias.rs b/src/rules/pyupgrade/rules/os_error_alias.rs index 2451b0dea4..9a26cc8ae6 100644 --- a/src/rules/pyupgrade/rules/os_error_alias.rs +++ b/src/rules/pyupgrade/rules/os_error_alias.rs @@ -1,4 +1,7 @@ +use crate::define_violation; +use crate::violation::AlwaysAutofixableViolation; use itertools::Itertools; +use ruff_macros::derive_message_formats; use rustpython_ast::{Excepthandler, ExcepthandlerKind, Expr, ExprKind, Located}; use crate::ast::helpers::compose_call_path; @@ -6,7 +9,26 @@ use crate::ast::types::Range; use crate::checkers::ast::Checker; use crate::fix::Fix; use crate::registry::Diagnostic; -use crate::violations; + +define_violation!( + pub struct OSErrorAlias { + pub name: Option, + } +); +impl AlwaysAutofixableViolation for OSErrorAlias { + #[derive_message_formats] + fn message(&self) -> String { + format!("Replace aliased errors with `OSError`") + } + + fn autofix_title(&self) -> String { + let OSErrorAlias { name } = self; + match name { + None => "Replace with builtin `OSError`".to_string(), + Some(name) => format!("Replace `{name}` with builtin `OSError`"), + } + } +} const ERROR_NAMES: &[&str] = &["EnvironmentError", "IOError", "WindowsError"]; const ERROR_MODULES: &[&str] = &["mmap", "select", "socket"]; @@ -145,7 +167,7 @@ fn handle_making_changes( final_str.push(')'); } let mut diagnostic = Diagnostic::new( - violations::OSErrorAlias { + OSErrorAlias { name: compose_call_path(target), }, Range::from_located(target), diff --git a/src/rules/pyupgrade/rules/outdated_version_block.rs b/src/rules/pyupgrade/rules/outdated_version_block.rs index e9bac98a4e..b2153aa837 100644 --- a/src/rules/pyupgrade/rules/outdated_version_block.rs +++ b/src/rules/pyupgrade/rules/outdated_version_block.rs @@ -1,3 +1,5 @@ +use ruff_macros::derive_message_formats; + use std::cmp::Ordering; use log::error; @@ -7,8 +9,6 @@ use rustpython_parser::ast::{Cmpop, Constant, Expr, ExprKind, Located, Stmt}; use rustpython_parser::lexer; use rustpython_parser::lexer::Tok; -use ruff_macros::derive_message_formats; - use crate::ast::types::{Range, RefEquality}; use crate::ast::whitespace::indentation; use crate::autofix::helpers::delete_stmt; diff --git a/src/rules/pyupgrade/rules/printf_string_formatting.rs b/src/rules/pyupgrade/rules/printf_string_formatting.rs index d9bfd7fb58..f8350fa4d9 100644 --- a/src/rules/pyupgrade/rules/printf_string_formatting.rs +++ b/src/rules/pyupgrade/rules/printf_string_formatting.rs @@ -1,3 +1,7 @@ +use crate::define_violation; +use crate::violation::AlwaysAutofixableViolation; +use ruff_macros::derive_message_formats; + use std::str::FromStr; use rustpython_ast::Location; @@ -17,7 +21,20 @@ use crate::python::keyword::KWLIST; use crate::registry::Diagnostic; use crate::rules::pydocstyle::helpers::{leading_quote, trailing_quote}; use crate::rules::pyupgrade::helpers::curly_escape; -use crate::violations; + +define_violation!( + pub struct PrintfStringFormatting; +); +impl AlwaysAutofixableViolation for PrintfStringFormatting { + #[derive_message_formats] + fn message(&self) -> String { + format!("Use format specifiers instead of percent format") + } + + fn autofix_title(&self) -> String { + "Replace with format specifiers".to_string() + } +} fn simplify_conversion_flag(flags: CConversionFlags) -> String { let mut flag_string = String::new(); @@ -406,10 +423,7 @@ pub(crate) fn printf_string_formatting( // Add the `.format` call. contents.push_str(&format!(".format{params_string}")); - let mut diagnostic = Diagnostic::new( - violations::PrintfStringFormatting, - Range::from_located(expr), - ); + let mut diagnostic = Diagnostic::new(PrintfStringFormatting, Range::from_located(expr)); if checker.patch(diagnostic.kind.rule()) { diagnostic.amend(Fix::replacement( contents, diff --git a/src/rules/pyupgrade/rules/redundant_open_modes.rs b/src/rules/pyupgrade/rules/redundant_open_modes.rs index f933fb18e8..60493af16f 100644 --- a/src/rules/pyupgrade/rules/redundant_open_modes.rs +++ b/src/rules/pyupgrade/rules/redundant_open_modes.rs @@ -1,3 +1,7 @@ +use crate::define_violation; +use crate::violation::AlwaysAutofixableViolation; +use ruff_macros::derive_message_formats; + use std::str::FromStr; use anyhow::{anyhow, Result}; @@ -12,7 +16,34 @@ use crate::checkers::ast::Checker; use crate::fix::Fix; use crate::registry::{Diagnostic, Rule}; use crate::source_code::Locator; -use crate::violations; + +define_violation!( + pub struct RedundantOpenModes { + pub replacement: Option, + } +); +impl AlwaysAutofixableViolation for RedundantOpenModes { + #[derive_message_formats] + fn message(&self) -> String { + let RedundantOpenModes { replacement } = self; + match replacement { + None => format!("Unnecessary open mode parameters"), + Some(replacement) => { + format!("Unnecessary open mode parameters, use \"{replacement}\"") + } + } + } + + fn autofix_title(&self) -> String { + let RedundantOpenModes { replacement } = self; + match replacement { + None => "Remove open mode parameters".to_string(), + Some(replacement) => { + format!("Replace with \"{replacement}\"") + } + } + } +} const OPEN_FUNC_NAME: &str = "open"; const MODE_KEYWORD_ARGUMENT: &str = "mode"; @@ -81,7 +112,7 @@ fn create_check( patch: bool, ) -> Diagnostic { let mut diagnostic = Diagnostic::new( - violations::RedundantOpenModes { + RedundantOpenModes { replacement: replacement_value.clone(), }, Range::from_located(expr), diff --git a/src/rules/pyupgrade/rules/replace_stdout_stderr.rs b/src/rules/pyupgrade/rules/replace_stdout_stderr.rs index 3bfd6fe478..77025b9bd9 100644 --- a/src/rules/pyupgrade/rules/replace_stdout_stderr.rs +++ b/src/rules/pyupgrade/rules/replace_stdout_stderr.rs @@ -1,3 +1,6 @@ +use crate::define_violation; +use crate::violation::AlwaysAutofixableViolation; +use ruff_macros::derive_message_formats; use rustpython_ast::{Expr, Keyword}; use crate::ast::helpers::find_keyword; @@ -7,7 +10,20 @@ use crate::checkers::ast::Checker; use crate::fix::Fix; use crate::registry::Diagnostic; use crate::source_code::{Locator, Stylist}; -use crate::violations; + +define_violation!( + pub struct ReplaceStdoutStderr; +); +impl AlwaysAutofixableViolation for ReplaceStdoutStderr { + #[derive_message_formats] + fn message(&self) -> String { + format!("Sending stdout and stderr to pipe is deprecated, use `capture_output`") + } + + fn autofix_title(&self) -> String { + "Replace with `capture_output` keyword argument".to_string() + } +} #[derive(Debug)] struct MiddleContent<'a> { @@ -116,8 +132,7 @@ pub fn replace_stdout_stderr(checker: &mut Checker, expr: &Expr, kwargs: &[Keywo return; } - let mut diagnostic = - Diagnostic::new(violations::ReplaceStdoutStderr, Range::from_located(expr)); + let mut diagnostic = Diagnostic::new(ReplaceStdoutStderr, Range::from_located(expr)); if checker.patch(diagnostic.kind.rule()) { if let Some(fix) = generate_fix(checker.stylist, checker.locator, stdout, stderr) { diagnostic.amend(fix); diff --git a/src/rules/pyupgrade/rules/replace_universal_newlines.rs b/src/rules/pyupgrade/rules/replace_universal_newlines.rs index 8b4579dd36..566a179cfe 100644 --- a/src/rules/pyupgrade/rules/replace_universal_newlines.rs +++ b/src/rules/pyupgrade/rules/replace_universal_newlines.rs @@ -1,3 +1,6 @@ +use crate::define_violation; +use crate::violation::AlwaysAutofixableViolation; +use ruff_macros::derive_message_formats; use rustpython_ast::{Expr, Keyword, Location}; use crate::ast::helpers::find_keyword; @@ -5,7 +8,20 @@ use crate::ast::types::Range; use crate::checkers::ast::Checker; use crate::fix::Fix; use crate::registry::Diagnostic; -use crate::violations; + +define_violation!( + pub struct ReplaceUniversalNewlines; +); +impl AlwaysAutofixableViolation for ReplaceUniversalNewlines { + #[derive_message_formats] + fn message(&self) -> String { + format!("`universal_newlines` is deprecated, use `text`") + } + + fn autofix_title(&self) -> String { + "Replace with `text` keyword argument".to_string() + } +} /// UP021 pub fn replace_universal_newlines(checker: &mut Checker, expr: &Expr, kwargs: &[Keyword]) { @@ -20,7 +36,7 @@ pub fn replace_universal_newlines(checker: &mut Checker, expr: &Expr, kwargs: &[ kwarg.location.column() + "universal_newlines".len(), ), ); - let mut diagnostic = Diagnostic::new(violations::ReplaceUniversalNewlines, range); + let mut diagnostic = Diagnostic::new(ReplaceUniversalNewlines, range); if checker.patch(diagnostic.kind.rule()) { diagnostic.amend(Fix::replacement( "text".to_string(), diff --git a/src/rules/pyupgrade/rules/rewrite_c_element_tree.rs b/src/rules/pyupgrade/rules/rewrite_c_element_tree.rs index 7d4ac401a2..7ef639492d 100644 --- a/src/rules/pyupgrade/rules/rewrite_c_element_tree.rs +++ b/src/rules/pyupgrade/rules/rewrite_c_element_tree.rs @@ -1,14 +1,29 @@ +use crate::define_violation; +use crate::violation::AlwaysAutofixableViolation; +use ruff_macros::derive_message_formats; use rustpython_ast::{Located, Stmt, StmtKind}; use crate::ast::types::Range; use crate::checkers::ast::Checker; use crate::fix::Fix; use crate::registry::Diagnostic; -use crate::violations; + +define_violation!( + pub struct RewriteCElementTree; +); +impl AlwaysAutofixableViolation for RewriteCElementTree { + #[derive_message_formats] + fn message(&self) -> String { + format!("`cElementTree` is deprecated, use `ElementTree`") + } + + fn autofix_title(&self) -> String { + "Replace with `ElementTree`".to_string() + } +} fn add_check_for_node(checker: &mut Checker, node: &Located) { - let mut diagnostic = - Diagnostic::new(violations::RewriteCElementTree, Range::from_located(node)); + let mut diagnostic = Diagnostic::new(RewriteCElementTree, Range::from_located(node)); if checker.patch(diagnostic.kind.rule()) { let contents = checker .locator diff --git a/src/rules/pyupgrade/rules/rewrite_mock_import.rs b/src/rules/pyupgrade/rules/rewrite_mock_import.rs index 9d762a8814..037b16243e 100644 --- a/src/rules/pyupgrade/rules/rewrite_mock_import.rs +++ b/src/rules/pyupgrade/rules/rewrite_mock_import.rs @@ -1,10 +1,14 @@ +use crate::define_violation; +use crate::violation::AlwaysAutofixableViolation; use anyhow::Result; use libcst_native::{ AsName, AssignTargetExpression, Attribute, Codegen, CodegenState, Dot, Expression, Import, ImportAlias, ImportFrom, ImportNames, Name, NameOrAttribute, ParenthesizableWhitespace, }; use log::error; +use ruff_macros::derive_message_formats; use rustpython_ast::{Expr, ExprKind, Stmt, StmtKind}; +use serde::{Deserialize, Serialize}; use crate::ast::helpers::collect_call_path; use crate::ast::types::Range; @@ -14,8 +18,32 @@ use crate::cst::matchers::{match_import, match_import_from, match_module}; use crate::fix::Fix; use crate::registry::{Diagnostic, Rule}; use crate::source_code::{Locator, Stylist}; -use crate::violations; -use crate::violations::MockReference; + +#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)] +pub enum MockReference { + Import, + Attribute, +} + +define_violation!( + pub struct RewriteMockImport { + pub reference_type: MockReference, + } +); +impl AlwaysAutofixableViolation for RewriteMockImport { + #[derive_message_formats] + fn message(&self) -> String { + format!("`mock` is deprecated, use `unittest.mock`") + } + + fn autofix_title(&self) -> String { + let RewriteMockImport { reference_type } = self; + match reference_type { + MockReference::Import => "Import from `unittest.mock` instead".to_string(), + MockReference::Attribute => "Replace `mock.mock` with `mock`".to_string(), + } + } +} /// Return a vector of all non-`mock` imports. fn clean_import_aliases(aliases: Vec) -> (Vec, Vec>) { @@ -209,7 +237,7 @@ pub fn rewrite_mock_attribute(checker: &mut Checker, expr: &Expr) { if let ExprKind::Attribute { value, .. } = &expr.node { if collect_call_path(value).as_slice() == ["mock", "mock"] { let mut diagnostic = Diagnostic::new( - violations::RewriteMockImport { + RewriteMockImport { reference_type: MockReference::Attribute, }, Range::from_located(value), @@ -256,7 +284,7 @@ pub fn rewrite_mock_import(checker: &mut Checker, stmt: &Stmt) { for name in names { if name.node.name == "mock" || name.node.name == "mock.mock" { let mut diagnostic = Diagnostic::new( - violations::RewriteMockImport { + RewriteMockImport { reference_type: MockReference::Import, }, Range::from_located(name), @@ -284,7 +312,7 @@ pub fn rewrite_mock_import(checker: &mut Checker, stmt: &Stmt) { if module == "mock" { let mut diagnostic = Diagnostic::new( - violations::RewriteMockImport { + RewriteMockImport { reference_type: MockReference::Import, }, Range::from_located(stmt), diff --git a/src/rules/pyupgrade/rules/rewrite_unicode_literal.rs b/src/rules/pyupgrade/rules/rewrite_unicode_literal.rs index d31fd4a64c..31940e94f9 100644 --- a/src/rules/pyupgrade/rules/rewrite_unicode_literal.rs +++ b/src/rules/pyupgrade/rules/rewrite_unicode_literal.rs @@ -1,17 +1,32 @@ +use crate::define_violation; +use crate::violation::AlwaysAutofixableViolation; +use ruff_macros::derive_message_formats; use rustpython_ast::{Expr, Location}; use crate::ast::types::Range; use crate::checkers::ast::Checker; use crate::fix::Fix; use crate::registry::Diagnostic; -use crate::violations; + +define_violation!( + pub struct RewriteUnicodeLiteral; +); +impl AlwaysAutofixableViolation for RewriteUnicodeLiteral { + #[derive_message_formats] + fn message(&self) -> String { + format!("Remove unicode literals from strings") + } + + fn autofix_title(&self) -> String { + "Remove unicode prefix".to_string() + } +} /// UP025 pub fn rewrite_unicode_literal(checker: &mut Checker, expr: &Expr, kind: Option<&str>) { if let Some(const_kind) = kind { if const_kind.to_lowercase() == "u" { - let mut diagnostic = - Diagnostic::new(violations::RewriteUnicodeLiteral, Range::from_located(expr)); + let mut diagnostic = Diagnostic::new(RewriteUnicodeLiteral, Range::from_located(expr)); if checker.patch(diagnostic.kind.rule()) { diagnostic.amend(Fix::deletion( expr.location, diff --git a/src/rules/pyupgrade/rules/rewrite_yield_from.rs b/src/rules/pyupgrade/rules/rewrite_yield_from.rs index 19820a40aa..27a84b45a3 100644 --- a/src/rules/pyupgrade/rules/rewrite_yield_from.rs +++ b/src/rules/pyupgrade/rules/rewrite_yield_from.rs @@ -1,3 +1,6 @@ +use crate::define_violation; +use crate::violation::AlwaysAutofixableViolation; +use ruff_macros::derive_message_formats; use rustc_hash::FxHashMap; use rustpython_ast::{Expr, ExprContext, ExprKind, Stmt, StmtKind}; @@ -7,7 +10,20 @@ use crate::ast::visitor::Visitor; use crate::checkers::ast::Checker; use crate::fix::Fix; use crate::registry::Diagnostic; -use crate::violations; + +define_violation!( + pub struct RewriteYieldFrom; +); +impl AlwaysAutofixableViolation for RewriteYieldFrom { + #[derive_message_formats] + fn message(&self) -> String { + format!("Replace `yield` over `for` loop with `yield from`") + } + + fn autofix_title(&self) -> String { + "Replace with `yield from`".to_string() + } +} /// Return `true` if the two expressions are equivalent, and consistent solely /// of tuples and names. @@ -157,8 +173,7 @@ pub fn rewrite_yield_from(checker: &mut Checker, stmt: &Stmt) { continue; } - let mut diagnostic = - Diagnostic::new(violations::RewriteYieldFrom, Range::from_located(item.stmt)); + let mut diagnostic = Diagnostic::new(RewriteYieldFrom, Range::from_located(item.stmt)); if checker.patch(diagnostic.kind.rule()) { let contents = checker .locator diff --git a/src/rules/pyupgrade/rules/super_args.rs b/src/rules/pyupgrade/rules/super_args.rs index 850a326d48..e1505cd7fe 100644 --- a/src/rules/pyupgrade/rules/super_args.rs +++ b/src/rules/pyupgrade/rules/super_args.rs @@ -1,7 +1,10 @@ use crate::ast::helpers; use crate::ast::types::{Range, Scope, ScopeKind}; + use crate::registry::Diagnostic; -use crate::violations; + +use crate::rules::pyupgrade::rules::SuperCallWithParameters; + use rustpython_ast::{ArgData, Expr, ExprKind, Stmt, StmtKind}; /// UP008 @@ -68,7 +71,7 @@ pub fn super_args( if first_arg_id == parent_name && second_arg_id == parent_arg { return Some(Diagnostic::new( - violations::SuperCallWithParameters, + SuperCallWithParameters, Range::from_located(expr), )); } diff --git a/src/rules/pyupgrade/rules/super_call_with_parameters.rs b/src/rules/pyupgrade/rules/super_call_with_parameters.rs index 6671da526c..1010a85a97 100644 --- a/src/rules/pyupgrade/rules/super_call_with_parameters.rs +++ b/src/rules/pyupgrade/rules/super_call_with_parameters.rs @@ -1,9 +1,26 @@ +use crate::define_violation; +use crate::violation::AlwaysAutofixableViolation; +use ruff_macros::derive_message_formats; use rustpython_ast::{Expr, Stmt}; use super::super::fixes; use crate::ast::helpers; use crate::checkers::ast::Checker; +define_violation!( + pub struct SuperCallWithParameters; +); +impl AlwaysAutofixableViolation for SuperCallWithParameters { + #[derive_message_formats] + fn message(&self) -> String { + format!("Use `super()` instead of `super(__class__, self)`") + } + + fn autofix_title(&self) -> String { + "Remove `__super__` parameters".to_string() + } +} + /// UP008 pub fn super_call_with_parameters(checker: &mut Checker, expr: &Expr, func: &Expr, args: &[Expr]) { // Only bother going through the super check at all if we're in a `super` call. diff --git a/src/rules/pyupgrade/rules/type_of_primitive.rs b/src/rules/pyupgrade/rules/type_of_primitive.rs index 19198c5fd0..5cad1dc2fb 100644 --- a/src/rules/pyupgrade/rules/type_of_primitive.rs +++ b/src/rules/pyupgrade/rules/type_of_primitive.rs @@ -1,3 +1,6 @@ +use crate::define_violation; +use crate::violation::AlwaysAutofixableViolation; +use ruff_macros::derive_message_formats; use rustpython_ast::{Expr, ExprKind}; use super::super::types::Primitive; @@ -5,7 +8,24 @@ use crate::ast::types::Range; use crate::checkers::ast::Checker; use crate::fix::Fix; use crate::registry::Diagnostic; -use crate::violations; + +define_violation!( + pub struct TypeOfPrimitive { + pub primitive: Primitive, + } +); +impl AlwaysAutofixableViolation for TypeOfPrimitive { + #[derive_message_formats] + fn message(&self) -> String { + let TypeOfPrimitive { primitive } = self; + format!("Use `{}` instead of `type(...)`", primitive.builtin()) + } + + fn autofix_title(&self) -> String { + let TypeOfPrimitive { primitive } = self; + format!("Replace `type(...)` with `{}`", primitive.builtin()) + } +} /// UP003 pub fn type_of_primitive(checker: &mut Checker, expr: &Expr, func: &Expr, args: &[Expr]) { @@ -24,10 +44,7 @@ pub fn type_of_primitive(checker: &mut Checker, expr: &Expr, func: &Expr, args: let Some(primitive) = Primitive::from_constant(value) else { return; }; - let mut diagnostic = Diagnostic::new( - violations::TypeOfPrimitive { primitive }, - Range::from_located(expr), - ); + let mut diagnostic = Diagnostic::new(TypeOfPrimitive { primitive }, Range::from_located(expr)); if checker.patch(diagnostic.kind.rule()) { diagnostic.amend(Fix::replacement( primitive.builtin(), diff --git a/src/rules/pyupgrade/rules/typing_text_str_alias.rs b/src/rules/pyupgrade/rules/typing_text_str_alias.rs index 10cc11aa63..25b05cfe04 100644 --- a/src/rules/pyupgrade/rules/typing_text_str_alias.rs +++ b/src/rules/pyupgrade/rules/typing_text_str_alias.rs @@ -1,18 +1,33 @@ +use crate::define_violation; +use crate::violation::AlwaysAutofixableViolation; +use ruff_macros::derive_message_formats; use rustpython_ast::Expr; use crate::ast::types::Range; use crate::checkers::ast::Checker; use crate::fix::Fix; use crate::registry::Diagnostic; -use crate::violations; + +define_violation!( + pub struct TypingTextStrAlias; +); +impl AlwaysAutofixableViolation for TypingTextStrAlias { + #[derive_message_formats] + fn message(&self) -> String { + format!("`typing.Text` is deprecated, use `str`") + } + + fn autofix_title(&self) -> String { + "Replace with `str`".to_string() + } +} /// UP019 pub fn typing_text_str_alias(checker: &mut Checker, expr: &Expr) { if checker.resolve_call_path(expr).map_or(false, |call_path| { call_path.as_slice() == ["typing", "Text"] }) { - let mut diagnostic = - Diagnostic::new(violations::TypingTextStrAlias, Range::from_located(expr)); + let mut diagnostic = Diagnostic::new(TypingTextStrAlias, Range::from_located(expr)); if checker.patch(diagnostic.kind.rule()) { diagnostic.amend(Fix::replacement( "str".to_string(), diff --git a/src/rules/pyupgrade/rules/unnecessary_builtin_import.rs b/src/rules/pyupgrade/rules/unnecessary_builtin_import.rs index 042359bc87..5d39fe7579 100644 --- a/src/rules/pyupgrade/rules/unnecessary_builtin_import.rs +++ b/src/rules/pyupgrade/rules/unnecessary_builtin_import.rs @@ -1,12 +1,38 @@ +use crate::define_violation; +use crate::violation::AlwaysAutofixableViolation; use itertools::Itertools; use log::error; +use ruff_macros::derive_message_formats; use rustpython_ast::{Alias, AliasData, Located}; use rustpython_parser::ast::Stmt; use crate::ast::types::Range; +use crate::autofix; use crate::checkers::ast::Checker; use crate::registry::Diagnostic; -use crate::{autofix, violations}; + +define_violation!( + pub struct UnnecessaryBuiltinImport { + pub names: Vec, + } +); +impl AlwaysAutofixableViolation for UnnecessaryBuiltinImport { + #[derive_message_formats] + fn message(&self) -> String { + let UnnecessaryBuiltinImport { names } = self; + if names.len() == 1 { + let import = &names[0]; + format!("Unnecessary builtin import: `{import}`") + } else { + let imports = names.iter().map(|name| format!("`{name}`")).join(", "); + format!("Unnecessary builtin imports: {imports}") + } + } + + fn autofix_title(&self) -> String { + "Remove unnecessary builtin import".to_string() + } +} const BUILTINS: &[&str] = &[ "*", @@ -69,7 +95,7 @@ pub fn unnecessary_builtin_import( return; } let mut diagnostic = Diagnostic::new( - violations::UnnecessaryBuiltinImport { + UnnecessaryBuiltinImport { names: unused_imports .iter() .map(|alias| alias.node.name.to_string()) diff --git a/src/rules/pyupgrade/rules/unnecessary_coding_comment.rs b/src/rules/pyupgrade/rules/unnecessary_coding_comment.rs index bf2fd122fb..cda61f0070 100644 --- a/src/rules/pyupgrade/rules/unnecessary_coding_comment.rs +++ b/src/rules/pyupgrade/rules/unnecessary_coding_comment.rs @@ -1,11 +1,28 @@ use crate::ast::types::Range; +use crate::define_violation; use crate::fix::Fix; use crate::registry::Diagnostic; -use crate::violations; +use crate::violation::AlwaysAutofixableViolation; + use once_cell::sync::Lazy; use regex::Regex; +use ruff_macros::derive_message_formats; use rustpython_ast::Location; +define_violation!( + pub struct PEP3120UnnecessaryCodingComment; +); +impl AlwaysAutofixableViolation for PEP3120UnnecessaryCodingComment { + #[derive_message_formats] + fn message(&self) -> String { + format!("UTF-8 encoding declaration is unnecessary") + } + + fn autofix_title(&self) -> String { + "Remove unnecessary coding comment".to_string() + } +} + // Regex from PEP263. static CODING_COMMENT_REGEX: Lazy = Lazy::new(|| Regex::new(r"^[ \t\f]*#.*?coding[:=][ \t]*utf-?8").unwrap()); @@ -15,7 +32,7 @@ pub fn unnecessary_coding_comment(lineno: usize, line: &str, autofix: bool) -> O // PEP3120 makes utf-8 the default encoding. if CODING_COMMENT_REGEX.is_match(line) { let mut diagnostic = Diagnostic::new( - violations::PEP3120UnnecessaryCodingComment, + PEP3120UnnecessaryCodingComment, Range::new(Location::new(lineno + 1, 0), Location::new(lineno + 2, 0)), ); if autofix { diff --git a/src/rules/pyupgrade/rules/unnecessary_encode_utf8.rs b/src/rules/pyupgrade/rules/unnecessary_encode_utf8.rs index 8da89badc8..f29b58d2a4 100644 --- a/src/rules/pyupgrade/rules/unnecessary_encode_utf8.rs +++ b/src/rules/pyupgrade/rules/unnecessary_encode_utf8.rs @@ -1,3 +1,6 @@ +use crate::define_violation; +use crate::violation::AlwaysAutofixableViolation; +use ruff_macros::derive_message_formats; use rustpython_ast::{Constant, Expr, ExprKind, Keyword}; use crate::ast::types::Range; @@ -5,7 +8,20 @@ use crate::checkers::ast::Checker; use crate::fix::Fix; use crate::registry::{Diagnostic, Rule}; use crate::source_code::Locator; -use crate::violations; + +define_violation!( + pub struct UnnecessaryEncodeUTF8; +); +impl AlwaysAutofixableViolation for UnnecessaryEncodeUTF8 { + #[derive_message_formats] + fn message(&self) -> String { + format!("Unnecessary call to `encode` as UTF-8") + } + + fn autofix_title(&self) -> String { + "Remove unnecessary `encode`".to_string() + } +} const UTF8_LITERALS: &[&str] = &["utf-8", "utf8", "utf_8", "u8", "utf", "cp65001"]; @@ -60,15 +76,13 @@ fn delete_default_encode_arg_or_kwarg( patch: bool, ) -> Option { if let Some(arg) = args.get(0) { - let mut diagnostic = - Diagnostic::new(violations::UnnecessaryEncodeUTF8, Range::from_located(expr)); + let mut diagnostic = Diagnostic::new(UnnecessaryEncodeUTF8, Range::from_located(expr)); if patch { diagnostic.amend(Fix::deletion(arg.location, arg.end_location.unwrap())); } Some(diagnostic) } else if let Some(kwarg) = kwargs.get(0) { - let mut diagnostic = - Diagnostic::new(violations::UnnecessaryEncodeUTF8, Range::from_located(expr)); + let mut diagnostic = Diagnostic::new(UnnecessaryEncodeUTF8, Range::from_located(expr)); if patch { diagnostic.amend(Fix::deletion(kwarg.location, kwarg.end_location.unwrap())); } @@ -85,8 +99,7 @@ fn replace_with_bytes_literal( locator: &Locator, patch: bool, ) -> Diagnostic { - let mut diagnostic = - Diagnostic::new(violations::UnnecessaryEncodeUTF8, Range::from_located(expr)); + let mut diagnostic = Diagnostic::new(UnnecessaryEncodeUTF8, Range::from_located(expr)); if patch { let content = locator.slice_source_code_range(&Range::new( constant.location, diff --git a/src/rules/pyupgrade/rules/unnecessary_future_import.rs b/src/rules/pyupgrade/rules/unnecessary_future_import.rs index 990d92d00a..b71b54292e 100644 --- a/src/rules/pyupgrade/rules/unnecessary_future_import.rs +++ b/src/rules/pyupgrade/rules/unnecessary_future_import.rs @@ -1,12 +1,38 @@ +use crate::define_violation; +use crate::violation::AlwaysAutofixableViolation; use itertools::Itertools; use log::error; +use ruff_macros::derive_message_formats; use rustpython_ast::{Alias, AliasData, Located}; use rustpython_parser::ast::Stmt; use crate::ast::types::Range; +use crate::autofix; use crate::checkers::ast::Checker; use crate::registry::Diagnostic; -use crate::{autofix, violations}; + +define_violation!( + pub struct UnnecessaryFutureImport { + pub names: Vec, + } +); +impl AlwaysAutofixableViolation for UnnecessaryFutureImport { + #[derive_message_formats] + fn message(&self) -> String { + let UnnecessaryFutureImport { names } = self; + if names.len() == 1 { + let import = &names[0]; + format!("Unnecessary `__future__` import `{import}` for target Python version") + } else { + let imports = names.iter().map(|name| format!("`{name}`")).join(", "); + format!("Unnecessary `__future__` imports {imports} for target Python version") + } + } + + fn autofix_title(&self) -> String { + "Remove unnecessary `__future__` import".to_string() + } +} const PY33_PLUS_REMOVE_FUTURES: &[&str] = &[ "nested_scopes", @@ -49,7 +75,7 @@ pub fn unnecessary_future_import(checker: &mut Checker, stmt: &Stmt, names: &[Lo return; } let mut diagnostic = Diagnostic::new( - violations::UnnecessaryFutureImport { + UnnecessaryFutureImport { names: unused_imports .iter() .map(|alias| alias.node.name.to_string()) diff --git a/src/rules/pyupgrade/rules/unpack_list_comprehension.rs b/src/rules/pyupgrade/rules/unpack_list_comprehension.rs index fe47ea9708..4b4a175dd8 100644 --- a/src/rules/pyupgrade/rules/unpack_list_comprehension.rs +++ b/src/rules/pyupgrade/rules/unpack_list_comprehension.rs @@ -1,10 +1,26 @@ +use crate::define_violation; +use crate::violation::AlwaysAutofixableViolation; +use ruff_macros::derive_message_formats; use rustpython_ast::{Expr, ExprKind}; use crate::ast::types::Range; use crate::checkers::ast::Checker; use crate::fix::Fix; use crate::registry::Diagnostic; -use crate::violations; + +define_violation!( + pub struct RewriteListComprehension; +); +impl AlwaysAutofixableViolation for RewriteListComprehension { + #[derive_message_formats] + fn message(&self) -> String { + format!("Replace unpacked list comprehension with a generator expression") + } + + fn autofix_title(&self) -> String { + "Replace with generator expression".to_string() + } +} /// Returns `true` if `expr` contains an `ExprKind::Await`. fn contains_await(expr: &Expr) -> bool { @@ -79,10 +95,8 @@ pub fn unpack_list_comprehension(checker: &mut Checker, targets: &[Expr], value: return; } - let mut diagnostic = Diagnostic::new( - violations::RewriteListComprehension, - Range::from_located(value), - ); + let mut diagnostic = + Diagnostic::new(RewriteListComprehension, Range::from_located(value)); if checker.patch(diagnostic.kind.rule()) { let existing = checker .locator diff --git a/src/rules/pyupgrade/rules/use_pep585_annotation.rs b/src/rules/pyupgrade/rules/use_pep585_annotation.rs index ad9eda6bc0..cf412d6076 100644 --- a/src/rules/pyupgrade/rules/use_pep585_annotation.rs +++ b/src/rules/pyupgrade/rules/use_pep585_annotation.rs @@ -1,10 +1,34 @@ +use crate::define_violation; +use crate::violation::AlwaysAutofixableViolation; +use ruff_macros::derive_message_formats; use rustpython_ast::Expr; use crate::ast::types::Range; use crate::checkers::ast::Checker; use crate::fix::Fix; use crate::registry::Diagnostic; -use crate::violations; + +define_violation!( + pub struct UsePEP585Annotation { + pub name: String, + } +); +impl AlwaysAutofixableViolation for UsePEP585Annotation { + #[derive_message_formats] + fn message(&self) -> String { + let UsePEP585Annotation { name } = self; + format!( + "Use `{}` instead of `{}` for type annotations", + name.to_lowercase(), + name, + ) + } + + fn autofix_title(&self) -> String { + let UsePEP585Annotation { name } = self; + format!("Replace `{name}` with `{}`", name.to_lowercase(),) + } +} /// UP006 pub fn use_pep585_annotation(checker: &mut Checker, expr: &Expr) { @@ -13,7 +37,7 @@ pub fn use_pep585_annotation(checker: &mut Checker, expr: &Expr) { .and_then(|call_path| call_path.last().copied()) { let mut diagnostic = Diagnostic::new( - violations::UsePEP585Annotation { + UsePEP585Annotation { name: binding.to_string(), }, Range::from_located(expr), diff --git a/src/rules/pyupgrade/rules/use_pep604_annotation.rs b/src/rules/pyupgrade/rules/use_pep604_annotation.rs index 7ab5371d88..8ad888c5de 100644 --- a/src/rules/pyupgrade/rules/use_pep604_annotation.rs +++ b/src/rules/pyupgrade/rules/use_pep604_annotation.rs @@ -1,3 +1,6 @@ +use crate::define_violation; +use crate::violation::AlwaysAutofixableViolation; +use ruff_macros::derive_message_formats; use rustpython_ast::{Constant, Expr, ExprKind, Location, Operator}; use crate::ast::helpers::unparse_expr; @@ -5,7 +8,20 @@ use crate::ast::types::Range; use crate::checkers::ast::Checker; use crate::fix::Fix; use crate::registry::Diagnostic; -use crate::violations; + +define_violation!( + pub struct UsePEP604Annotation; +); +impl AlwaysAutofixableViolation for UsePEP604Annotation { + #[derive_message_formats] + fn message(&self) -> String { + format!("Use `X | Y` for type annotations") + } + + fn autofix_title(&self) -> String { + "Convert to `X | Y`".to_string() + } +} fn optional(expr: &Expr) -> Expr { Expr::new( @@ -80,8 +96,7 @@ pub fn use_pep604_annotation(checker: &mut Checker, expr: &Expr, value: &Expr, s match typing_member { TypingMember::Optional => { - let mut diagnostic = - Diagnostic::new(violations::UsePEP604Annotation, Range::from_located(expr)); + let mut diagnostic = Diagnostic::new(UsePEP604Annotation, Range::from_located(expr)); if checker.patch(diagnostic.kind.rule()) { diagnostic.amend(Fix::replacement( unparse_expr(&optional(slice), checker.stylist), @@ -92,8 +107,7 @@ pub fn use_pep604_annotation(checker: &mut Checker, expr: &Expr, value: &Expr, s checker.diagnostics.push(diagnostic); } TypingMember::Union => { - let mut diagnostic = - Diagnostic::new(violations::UsePEP604Annotation, Range::from_located(expr)); + let mut diagnostic = Diagnostic::new(UsePEP604Annotation, Range::from_located(expr)); if checker.patch(diagnostic.kind.rule()) { match &slice.node { ExprKind::Slice { .. } => { diff --git a/src/rules/pyupgrade/rules/useless_metaclass_type.rs b/src/rules/pyupgrade/rules/useless_metaclass_type.rs index 431c3013c0..c883dc9a25 100644 --- a/src/rules/pyupgrade/rules/useless_metaclass_type.rs +++ b/src/rules/pyupgrade/rules/useless_metaclass_type.rs @@ -1,11 +1,27 @@ +use crate::define_violation; +use crate::violation::AlwaysAutofixableViolation; use log::error; +use ruff_macros::derive_message_formats; use rustpython_ast::{Expr, ExprKind, Stmt}; use crate::ast::types::Range; use crate::autofix::helpers; use crate::checkers::ast::Checker; use crate::registry::Diagnostic; -use crate::violations; + +define_violation!( + pub struct UselessMetaclassType; +); +impl AlwaysAutofixableViolation for UselessMetaclassType { + #[derive_message_formats] + fn message(&self) -> String { + format!("`__metaclass__ = type` is implied") + } + + fn autofix_title(&self) -> String { + "Remove `__metaclass__ = type`".to_string() + } +} fn rule(targets: &[Expr], value: &Expr, location: Range) -> Option { if targets.len() != 1 { @@ -23,7 +39,7 @@ fn rule(targets: &[Expr], value: &Expr, location: Range) -> Option { if id != "type" { return None; } - Some(Diagnostic::new(violations::UselessMetaclassType, location)) + Some(Diagnostic::new(UselessMetaclassType, location)) } /// UP001 diff --git a/src/rules/pyupgrade/rules/useless_object_inheritance.rs b/src/rules/pyupgrade/rules/useless_object_inheritance.rs index ed418a2108..75e30f1663 100644 --- a/src/rules/pyupgrade/rules/useless_object_inheritance.rs +++ b/src/rules/pyupgrade/rules/useless_object_inheritance.rs @@ -1,10 +1,29 @@ +use crate::define_violation; +use crate::violation::AlwaysAutofixableViolation; +use ruff_macros::derive_message_formats; use rustpython_ast::{Expr, ExprKind, Keyword, Stmt}; use super::super::fixes; use crate::ast::types::{Binding, BindingKind, Range, Scope}; use crate::checkers::ast::Checker; use crate::registry::Diagnostic; -use crate::violations; + +define_violation!( + pub struct UselessObjectInheritance { + pub name: String, + } +); +impl AlwaysAutofixableViolation for UselessObjectInheritance { + #[derive_message_formats] + fn message(&self) -> String { + let UselessObjectInheritance { name } = self; + format!("Class `{name}` inherits from `object`") + } + + fn autofix_title(&self) -> String { + "Remove `object` inheritance".to_string() + } +} fn rule(name: &str, bases: &[Expr], scope: &Scope, bindings: &[Binding]) -> Option { for expr in bases { @@ -27,7 +46,7 @@ fn rule(name: &str, bases: &[Expr], scope: &Scope, bindings: &[Binding]) -> Opti continue; } return Some(Diagnostic::new( - violations::UselessObjectInheritance { + UselessObjectInheritance { name: name.to_string(), }, Range::from_located(expr), diff --git a/src/violations.rs b/src/violations.rs index f82d610e70..eba5a5ea0b 100644 --- a/src/violations.rs +++ b/src/violations.rs @@ -8,7 +8,7 @@ use serde::{Deserialize, Serialize}; use crate::define_violation; use crate::rules::flake8_debugger::types::DebuggerUsingType; -use crate::rules::pyupgrade::types::Primitive; + use crate::violation::{AlwaysAutofixableViolation, AutofixKind, Availability, Violation}; // pylint @@ -1176,561 +1176,6 @@ impl Violation for UnpackInsteadOfConcatenatingToCollectionLiteral { } } -// pyupgrade - -define_violation!( - pub struct UselessMetaclassType; -); -impl AlwaysAutofixableViolation for UselessMetaclassType { - #[derive_message_formats] - fn message(&self) -> String { - format!("`__metaclass__ = type` is implied") - } - - fn autofix_title(&self) -> String { - "Remove `__metaclass__ = type`".to_string() - } -} - -define_violation!( - pub struct TypeOfPrimitive { - pub primitive: Primitive, - } -); -impl AlwaysAutofixableViolation for TypeOfPrimitive { - #[derive_message_formats] - fn message(&self) -> String { - let TypeOfPrimitive { primitive } = self; - format!("Use `{}` instead of `type(...)`", primitive.builtin()) - } - - fn autofix_title(&self) -> String { - let TypeOfPrimitive { primitive } = self; - format!("Replace `type(...)` with `{}`", primitive.builtin()) - } -} - -define_violation!( - pub struct UselessObjectInheritance { - pub name: String, - } -); -impl AlwaysAutofixableViolation for UselessObjectInheritance { - #[derive_message_formats] - fn message(&self) -> String { - let UselessObjectInheritance { name } = self; - format!("Class `{name}` inherits from `object`") - } - - fn autofix_title(&self) -> String { - "Remove `object` inheritance".to_string() - } -} - -define_violation!( - pub struct DeprecatedUnittestAlias { - pub alias: String, - pub target: String, - } -); -impl AlwaysAutofixableViolation for DeprecatedUnittestAlias { - #[derive_message_formats] - fn message(&self) -> String { - let DeprecatedUnittestAlias { alias, target } = self; - format!("`{alias}` is deprecated, use `{target}`") - } - - fn autofix_title(&self) -> String { - let DeprecatedUnittestAlias { alias, target } = self; - format!("Replace `{target}` with `{alias}`") - } -} - -define_violation!( - pub struct UsePEP585Annotation { - pub name: String, - } -); -impl AlwaysAutofixableViolation for UsePEP585Annotation { - #[derive_message_formats] - fn message(&self) -> String { - let UsePEP585Annotation { name } = self; - format!( - "Use `{}` instead of `{}` for type annotations", - name.to_lowercase(), - name, - ) - } - - fn autofix_title(&self) -> String { - let UsePEP585Annotation { name } = self; - format!("Replace `{name}` with `{}`", name.to_lowercase(),) - } -} - -define_violation!( - pub struct UsePEP604Annotation; -); -impl AlwaysAutofixableViolation for UsePEP604Annotation { - #[derive_message_formats] - fn message(&self) -> String { - format!("Use `X | Y` for type annotations") - } - - fn autofix_title(&self) -> String { - "Convert to `X | Y`".to_string() - } -} - -define_violation!( - pub struct SuperCallWithParameters; -); -impl AlwaysAutofixableViolation for SuperCallWithParameters { - #[derive_message_formats] - fn message(&self) -> String { - format!("Use `super()` instead of `super(__class__, self)`") - } - - fn autofix_title(&self) -> String { - "Remove `__super__` parameters".to_string() - } -} - -define_violation!( - pub struct PEP3120UnnecessaryCodingComment; -); -impl AlwaysAutofixableViolation for PEP3120UnnecessaryCodingComment { - #[derive_message_formats] - fn message(&self) -> String { - format!("UTF-8 encoding declaration is unnecessary") - } - - fn autofix_title(&self) -> String { - "Remove unnecessary coding comment".to_string() - } -} - -define_violation!( - pub struct UnnecessaryFutureImport { - pub names: Vec, - } -); -impl AlwaysAutofixableViolation for UnnecessaryFutureImport { - #[derive_message_formats] - fn message(&self) -> String { - let UnnecessaryFutureImport { names } = self; - if names.len() == 1 { - let import = &names[0]; - format!("Unnecessary `__future__` import `{import}` for target Python version") - } else { - let imports = names.iter().map(|name| format!("`{name}`")).join(", "); - format!("Unnecessary `__future__` imports {imports} for target Python version") - } - } - - fn autofix_title(&self) -> String { - "Remove unnecessary `__future__` import".to_string() - } -} - -define_violation!( - pub struct LRUCacheWithoutParameters; -); -impl AlwaysAutofixableViolation for LRUCacheWithoutParameters { - #[derive_message_formats] - fn message(&self) -> String { - format!("Unnecessary parameters to `functools.lru_cache`") - } - - fn autofix_title(&self) -> String { - "Remove unnecessary parameters".to_string() - } -} - -define_violation!( - pub struct UnnecessaryEncodeUTF8; -); -impl AlwaysAutofixableViolation for UnnecessaryEncodeUTF8 { - #[derive_message_formats] - fn message(&self) -> String { - format!("Unnecessary call to `encode` as UTF-8") - } - - fn autofix_title(&self) -> String { - "Remove unnecessary `encode`".to_string() - } -} - -define_violation!( - pub struct ConvertTypedDictFunctionalToClass { - pub name: String, - } -); -impl AlwaysAutofixableViolation for ConvertTypedDictFunctionalToClass { - #[derive_message_formats] - fn message(&self) -> String { - let ConvertTypedDictFunctionalToClass { name } = self; - format!("Convert `{name}` from `TypedDict` functional to class syntax") - } - - fn autofix_title(&self) -> String { - let ConvertTypedDictFunctionalToClass { name } = self; - format!("Convert `{name}` to class syntax") - } -} - -define_violation!( - pub struct ConvertNamedTupleFunctionalToClass { - pub name: String, - } -); -impl AlwaysAutofixableViolation for ConvertNamedTupleFunctionalToClass { - #[derive_message_formats] - fn message(&self) -> String { - let ConvertNamedTupleFunctionalToClass { name } = self; - format!("Convert `{name}` from `NamedTuple` functional to class syntax") - } - - fn autofix_title(&self) -> String { - let ConvertNamedTupleFunctionalToClass { name } = self; - format!("Convert `{name}` to class syntax") - } -} - -define_violation!( - pub struct RedundantOpenModes { - pub replacement: Option, - } -); -impl AlwaysAutofixableViolation for RedundantOpenModes { - #[derive_message_formats] - fn message(&self) -> String { - let RedundantOpenModes { replacement } = self; - match replacement { - None => format!("Unnecessary open mode parameters"), - Some(replacement) => { - format!("Unnecessary open mode parameters, use \"{replacement}\"") - } - } - } - - fn autofix_title(&self) -> String { - let RedundantOpenModes { replacement } = self; - match replacement { - None => "Remove open mode parameters".to_string(), - Some(replacement) => { - format!("Replace with \"{replacement}\"") - } - } - } -} - -define_violation!( - pub struct DatetimeTimezoneUTC { - pub straight_import: bool, - } -); -impl Violation for DatetimeTimezoneUTC { - const AUTOFIX: Option = Some(AutofixKind::new(Availability::Always)); - - #[derive_message_formats] - fn message(&self) -> String { - format!("Use `datetime.UTC` alias") - } - - fn autofix_title_formatter(&self) -> Option String> { - if self.straight_import { - Some(|_| "Convert to `datetime.UTC` alias".to_string()) - } else { - None - } - } -} - -#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)] -pub enum LiteralType { - Str, - Bytes, -} - -impl fmt::Display for LiteralType { - fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { - match self { - LiteralType::Str => fmt.write_str("str"), - LiteralType::Bytes => fmt.write_str("bytes"), - } - } -} - -define_violation!( - pub struct NativeLiterals { - pub literal_type: LiteralType, - } -); -impl AlwaysAutofixableViolation for NativeLiterals { - #[derive_message_formats] - fn message(&self) -> String { - let NativeLiterals { literal_type } = self; - format!("Unnecessary call to `{literal_type}`") - } - - fn autofix_title(&self) -> String { - let NativeLiterals { literal_type } = self; - format!("Replace with `{literal_type}`") - } -} - -define_violation!( - pub struct TypingTextStrAlias; -); -impl AlwaysAutofixableViolation for TypingTextStrAlias { - #[derive_message_formats] - fn message(&self) -> String { - format!("`typing.Text` is deprecated, use `str`") - } - - fn autofix_title(&self) -> String { - "Replace with `str`".to_string() - } -} - -define_violation!( - pub struct OpenAlias; -); -impl AlwaysAutofixableViolation for OpenAlias { - #[derive_message_formats] - fn message(&self) -> String { - format!("Use builtin `open`") - } - - fn autofix_title(&self) -> String { - "Replace with builtin `open`".to_string() - } -} - -define_violation!( - pub struct ReplaceUniversalNewlines; -); -impl AlwaysAutofixableViolation for ReplaceUniversalNewlines { - #[derive_message_formats] - fn message(&self) -> String { - format!("`universal_newlines` is deprecated, use `text`") - } - - fn autofix_title(&self) -> String { - "Replace with `text` keyword argument".to_string() - } -} - -define_violation!( - pub struct PrintfStringFormatting; -); -impl AlwaysAutofixableViolation for PrintfStringFormatting { - #[derive_message_formats] - fn message(&self) -> String { - format!("Use format specifiers instead of percent format") - } - - fn autofix_title(&self) -> String { - "Replace with format specifiers".to_string() - } -} - -define_violation!( - pub struct ReplaceStdoutStderr; -); -impl AlwaysAutofixableViolation for ReplaceStdoutStderr { - #[derive_message_formats] - fn message(&self) -> String { - format!("Sending stdout and stderr to pipe is deprecated, use `capture_output`") - } - - fn autofix_title(&self) -> String { - "Replace with `capture_output` keyword argument".to_string() - } -} - -define_violation!( - pub struct RewriteCElementTree; -); -impl AlwaysAutofixableViolation for RewriteCElementTree { - #[derive_message_formats] - fn message(&self) -> String { - format!("`cElementTree` is deprecated, use `ElementTree`") - } - - fn autofix_title(&self) -> String { - "Replace with `ElementTree`".to_string() - } -} - -define_violation!( - pub struct OSErrorAlias { - pub name: Option, - } -); -impl AlwaysAutofixableViolation for OSErrorAlias { - #[derive_message_formats] - fn message(&self) -> String { - format!("Replace aliased errors with `OSError`") - } - - fn autofix_title(&self) -> String { - let OSErrorAlias { name } = self; - match name { - None => "Replace with builtin `OSError`".to_string(), - Some(name) => format!("Replace `{name}` with builtin `OSError`"), - } - } -} - -define_violation!( - pub struct RewriteUnicodeLiteral; -); -impl AlwaysAutofixableViolation for RewriteUnicodeLiteral { - #[derive_message_formats] - fn message(&self) -> String { - format!("Remove unicode literals from strings") - } - - fn autofix_title(&self) -> String { - "Remove unicode prefix".to_string() - } -} - -#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)] -pub enum MockReference { - Import, - Attribute, -} - -define_violation!( - pub struct RewriteMockImport { - pub reference_type: MockReference, - } -); -impl AlwaysAutofixableViolation for RewriteMockImport { - #[derive_message_formats] - fn message(&self) -> String { - format!("`mock` is deprecated, use `unittest.mock`") - } - - fn autofix_title(&self) -> String { - let RewriteMockImport { reference_type } = self; - match reference_type { - MockReference::Import => "Import from `unittest.mock` instead".to_string(), - MockReference::Attribute => "Replace `mock.mock` with `mock`".to_string(), - } - } -} - -define_violation!( - pub struct RewriteListComprehension; -); -impl AlwaysAutofixableViolation for RewriteListComprehension { - #[derive_message_formats] - fn message(&self) -> String { - format!("Replace unpacked list comprehension with a generator expression") - } - - fn autofix_title(&self) -> String { - "Replace with generator expression".to_string() - } -} - -define_violation!( - pub struct RewriteYieldFrom; -); -impl AlwaysAutofixableViolation for RewriteYieldFrom { - #[derive_message_formats] - fn message(&self) -> String { - format!("Replace `yield` over `for` loop with `yield from`") - } - - fn autofix_title(&self) -> String { - "Replace with `yield from`".to_string() - } -} - -define_violation!( - pub struct UnnecessaryBuiltinImport { - pub names: Vec, - } -); -impl AlwaysAutofixableViolation for UnnecessaryBuiltinImport { - #[derive_message_formats] - fn message(&self) -> String { - let UnnecessaryBuiltinImport { names } = self; - if names.len() == 1 { - let import = &names[0]; - format!("Unnecessary builtin import: `{import}`") - } else { - let imports = names.iter().map(|name| format!("`{name}`")).join(", "); - format!("Unnecessary builtin imports: {imports}") - } - } - - fn autofix_title(&self) -> String { - "Remove unnecessary builtin import".to_string() - } -} - -define_violation!( - pub struct FormatLiterals; -); -impl AlwaysAutofixableViolation for FormatLiterals { - #[derive_message_formats] - fn message(&self) -> String { - format!("Use implicit references for positional format fields") - } - - fn autofix_title(&self) -> String { - "Remove explicit positional indexes".to_string() - } -} - -define_violation!( - pub struct ExtraneousParentheses; -); -impl AlwaysAutofixableViolation for ExtraneousParentheses { - #[derive_message_formats] - fn message(&self) -> String { - format!("Avoid extraneous parentheses") - } - - fn autofix_title(&self) -> String { - "Remove extraneous parentheses".to_string() - } -} - -define_violation!( - pub struct FString; -); -impl AlwaysAutofixableViolation for FString { - #[derive_message_formats] - fn message(&self) -> String { - format!("Use f-string instead of `format` call") - } - - fn autofix_title(&self) -> String { - "Convert to f-string".to_string() - } -} - -define_violation!( - pub struct FunctoolsCache; -); -impl AlwaysAutofixableViolation for FunctoolsCache { - #[derive_message_formats] - fn message(&self) -> String { - format!("Use `@functools.cache` instead of `@functools.lru_cache(maxsize=None)`") - } - - fn autofix_title(&self) -> String { - "Rewrite with `@functools.cache".to_string() - } -} - // pydocstyle define_violation!(