Move pyupgrade violations to rule modules (#2490)

This commit is contained in:
Aarni Koskela 2023-02-02 21:47:43 +02:00 committed by GitHub
parent 5f1bbf0b6b
commit 858af8debb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
38 changed files with 778 additions and 715 deletions

View File

@ -224,38 +224,38 @@ ruff_macros::define_rule_mapping!(
SIM300 => violations::YodaConditions, SIM300 => violations::YodaConditions,
SIM401 => violations::DictGetWithDefault, SIM401 => violations::DictGetWithDefault,
// pyupgrade // pyupgrade
UP001 => violations::UselessMetaclassType, UP001 => rules::pyupgrade::rules::UselessMetaclassType,
UP003 => violations::TypeOfPrimitive, UP003 => rules::pyupgrade::rules::TypeOfPrimitive,
UP004 => violations::UselessObjectInheritance, UP004 => rules::pyupgrade::rules::UselessObjectInheritance,
UP005 => violations::DeprecatedUnittestAlias, UP005 => rules::pyupgrade::rules::DeprecatedUnittestAlias,
UP006 => violations::UsePEP585Annotation, UP006 => rules::pyupgrade::rules::UsePEP585Annotation,
UP007 => violations::UsePEP604Annotation, UP007 => rules::pyupgrade::rules::UsePEP604Annotation,
UP008 => violations::SuperCallWithParameters, UP008 => rules::pyupgrade::rules::SuperCallWithParameters,
UP009 => violations::PEP3120UnnecessaryCodingComment, UP009 => rules::pyupgrade::rules::PEP3120UnnecessaryCodingComment,
UP010 => violations::UnnecessaryFutureImport, UP010 => rules::pyupgrade::rules::UnnecessaryFutureImport,
UP011 => violations::LRUCacheWithoutParameters, UP011 => rules::pyupgrade::rules::LRUCacheWithoutParameters,
UP012 => violations::UnnecessaryEncodeUTF8, UP012 => rules::pyupgrade::rules::UnnecessaryEncodeUTF8,
UP013 => violations::ConvertTypedDictFunctionalToClass, UP013 => rules::pyupgrade::rules::ConvertTypedDictFunctionalToClass,
UP014 => violations::ConvertNamedTupleFunctionalToClass, UP014 => rules::pyupgrade::rules::ConvertNamedTupleFunctionalToClass,
UP015 => violations::RedundantOpenModes, UP015 => rules::pyupgrade::rules::RedundantOpenModes,
UP017 => violations::DatetimeTimezoneUTC, UP017 => rules::pyupgrade::rules::DatetimeTimezoneUTC,
UP018 => violations::NativeLiterals, UP018 => rules::pyupgrade::rules::NativeLiterals,
UP019 => violations::TypingTextStrAlias, UP019 => rules::pyupgrade::rules::TypingTextStrAlias,
UP020 => violations::OpenAlias, UP020 => rules::pyupgrade::rules::OpenAlias,
UP021 => violations::ReplaceUniversalNewlines, UP021 => rules::pyupgrade::rules::ReplaceUniversalNewlines,
UP022 => violations::ReplaceStdoutStderr, UP022 => rules::pyupgrade::rules::ReplaceStdoutStderr,
UP023 => violations::RewriteCElementTree, UP023 => rules::pyupgrade::rules::RewriteCElementTree,
UP024 => violations::OSErrorAlias, UP024 => rules::pyupgrade::rules::OSErrorAlias,
UP025 => violations::RewriteUnicodeLiteral, UP025 => rules::pyupgrade::rules::RewriteUnicodeLiteral,
UP026 => violations::RewriteMockImport, UP026 => rules::pyupgrade::rules::RewriteMockImport,
UP027 => violations::RewriteListComprehension, UP027 => rules::pyupgrade::rules::RewriteListComprehension,
UP028 => violations::RewriteYieldFrom, UP028 => rules::pyupgrade::rules::RewriteYieldFrom,
UP029 => violations::UnnecessaryBuiltinImport, UP029 => rules::pyupgrade::rules::UnnecessaryBuiltinImport,
UP030 => violations::FormatLiterals, UP030 => rules::pyupgrade::rules::FormatLiterals,
UP031 => violations::PrintfStringFormatting, UP031 => rules::pyupgrade::rules::PrintfStringFormatting,
UP032 => violations::FString, UP032 => rules::pyupgrade::rules::FString,
UP033 => violations::FunctoolsCache, UP033 => rules::pyupgrade::rules::FunctoolsCache,
UP034 => violations::ExtraneousParentheses, UP034 => rules::pyupgrade::rules::ExtraneousParentheses,
UP035 => rules::pyupgrade::rules::ImportReplacements, UP035 => rules::pyupgrade::rules::ImportReplacements,
UP036 => rules::pyupgrade::rules::OutdatedVersionBlock, UP036 => rules::pyupgrade::rules::OutdatedVersionBlock,
// pydocstyle // pydocstyle

View File

@ -1,5 +1,8 @@
use crate::define_violation;
use crate::violation::AlwaysAutofixableViolation;
use anyhow::{bail, Result}; use anyhow::{bail, Result};
use log::debug; use log::debug;
use ruff_macros::derive_message_formats;
use rustpython_ast::{Constant, Expr, ExprContext, ExprKind, Keyword, Stmt, StmtKind}; use rustpython_ast::{Constant, Expr, ExprContext, ExprKind, Keyword, Stmt, StmtKind};
use crate::ast::helpers::{create_expr, create_stmt, unparse_stmt}; 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::python::keyword::KWLIST;
use crate::registry::Diagnostic; use crate::registry::Diagnostic;
use crate::source_code::Stylist; 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. /// Return the typename, args, keywords, and base class.
fn match_named_tuple_assign<'a>( fn match_named_tuple_assign<'a>(
@ -154,7 +174,7 @@ pub fn convert_named_tuple_functional_to_class(
return; return;
}; };
let mut diagnostic = Diagnostic::new( let mut diagnostic = Diagnostic::new(
violations::ConvertNamedTupleFunctionalToClass { ConvertNamedTupleFunctionalToClass {
name: typename.to_string(), name: typename.to_string(),
}, },
Range::from_located(stmt), Range::from_located(stmt),

View File

@ -1,5 +1,8 @@
use crate::define_violation;
use crate::violation::AlwaysAutofixableViolation;
use anyhow::{bail, Result}; use anyhow::{bail, Result};
use log::debug; use log::debug;
use ruff_macros::derive_message_formats;
use rustpython_ast::{Constant, Expr, ExprContext, ExprKind, Keyword, Stmt, StmtKind}; use rustpython_ast::{Constant, Expr, ExprContext, ExprKind, Keyword, Stmt, StmtKind};
use crate::ast::helpers::{create_expr, create_stmt, unparse_stmt}; 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::python::keyword::KWLIST;
use crate::registry::Diagnostic; use crate::registry::Diagnostic;
use crate::source_code::Stylist; 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` /// Return the class name, arguments, keywords and base class for a `TypedDict`
/// assignment. /// assignment.
@ -201,7 +221,7 @@ pub fn convert_typed_dict_functional_to_class(
}; };
let mut diagnostic = Diagnostic::new( let mut diagnostic = Diagnostic::new(
violations::ConvertTypedDictFunctionalToClass { ConvertTypedDictFunctionalToClass {
name: class_name.to_string(), name: class_name.to_string(),
}, },
Range::from_located(stmt), Range::from_located(stmt),

View File

@ -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 rustpython_ast::Expr;
use crate::ast::helpers::collect_call_path; use crate::ast::helpers::collect_call_path;
@ -5,7 +8,28 @@ use crate::ast::types::Range;
use crate::checkers::ast::Checker; use crate::checkers::ast::Checker;
use crate::fix::Fix; use crate::fix::Fix;
use crate::registry::Diagnostic; use crate::registry::Diagnostic;
use crate::violations;
define_violation!(
pub struct DatetimeTimezoneUTC {
pub straight_import: bool,
}
);
impl Violation for DatetimeTimezoneUTC {
const AUTOFIX: Option<AutofixKind> = Some(AutofixKind::new(Availability::Always));
#[derive_message_formats]
fn message(&self) -> String {
format!("Use `datetime.UTC` alias")
}
fn autofix_title_formatter(&self) -> Option<fn(&Self) -> String> {
if self.straight_import {
Some(|_| "Convert to `datetime.UTC` alias".to_string())
} else {
None
}
}
}
/// UP017 /// UP017
pub fn datetime_utc_alias(checker: &mut Checker, expr: &Expr) { 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 straight_import = collect_call_path(expr).as_slice() == ["datetime", "timezone", "utc"];
let mut diagnostic = Diagnostic::new( let mut diagnostic = Diagnostic::new(
violations::DatetimeTimezoneUTC { straight_import }, DatetimeTimezoneUTC { straight_import },
Range::from_located(expr), Range::from_located(expr),
); );
if checker.patch(diagnostic.kind.rule()) { if checker.patch(diagnostic.kind.rule()) {

View File

@ -1,4 +1,7 @@
use crate::define_violation;
use crate::violation::AlwaysAutofixableViolation;
use once_cell::sync::Lazy; use once_cell::sync::Lazy;
use ruff_macros::derive_message_formats;
use rustc_hash::FxHashMap; use rustc_hash::FxHashMap;
use rustpython_ast::{Expr, ExprKind}; use rustpython_ast::{Expr, ExprKind};
@ -6,7 +9,25 @@ use crate::ast::types::Range;
use crate::checkers::ast::Checker; use crate::checkers::ast::Checker;
use crate::fix::Fix; use crate::fix::Fix;
use crate::registry::Diagnostic; 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<FxHashMap<&'static str, &'static str>> = Lazy::new(|| { static DEPRECATED_ALIASES: Lazy<FxHashMap<&'static str, &'static str>> = Lazy::new(|| {
FxHashMap::from_iter([ FxHashMap::from_iter([
@ -43,7 +64,7 @@ pub fn deprecated_unittest_alias(checker: &mut Checker, expr: &Expr) {
return; return;
} }
let mut diagnostic = Diagnostic::new( let mut diagnostic = Diagnostic::new(
violations::DeprecatedUnittestAlias { DeprecatedUnittestAlias {
alias: attr.to_string(), alias: attr.to_string(),
target: target.to_string(), target: target.to_string(),
}, },

View File

@ -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 rustpython_parser::lexer::{LexResult, Tok};
use crate::ast::types::Range; use crate::ast::types::Range;
@ -5,7 +8,20 @@ use crate::fix::Fix;
use crate::registry::{Diagnostic, Rule}; use crate::registry::{Diagnostic, Rule};
use crate::settings::{flags, Settings}; use crate::settings::{flags, Settings};
use crate::source_code::Locator; 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 // See: https://github.com/asottile/pyupgrade/blob/97ed6fb3cf2e650d4f762ba231c3f04c41797710/pyupgrade/_main.py#L148
fn match_extraneous_parentheses(tokens: &[LexResult], mut i: usize) -> Option<(usize, usize)> { fn match_extraneous_parentheses(tokens: &[LexResult], mut i: usize) -> Option<(usize, usize)> {
@ -120,7 +136,7 @@ pub fn extraneous_parentheses(
return diagnostics; return diagnostics;
}; };
let mut diagnostic = let mut diagnostic =
Diagnostic::new(violations::ExtraneousParentheses, Range::new(*start, *end)); Diagnostic::new(ExtraneousParentheses, Range::new(*start, *end));
if matches!(autofix, flags::Autofix::Enabled) if matches!(autofix, flags::Autofix::Enabled)
&& settings.rules.should_fix(&Rule::ExtraneousParentheses) && settings.rules.should_fix(&Rule::ExtraneousParentheses)
{ {

View File

@ -1,3 +1,6 @@
use crate::define_violation;
use crate::violation::AlwaysAutofixableViolation;
use ruff_macros::derive_message_formats;
use rustc_hash::FxHashMap; use rustc_hash::FxHashMap;
use rustpython_ast::{Constant, Expr, ExprKind, KeywordData}; use rustpython_ast::{Constant, Expr, ExprKind, KeywordData};
use rustpython_common::format::{ use rustpython_common::format::{
@ -13,7 +16,20 @@ use crate::registry::Diagnostic;
use crate::rules::pydocstyle::helpers::{leading_quote, trailing_quote}; use crate::rules::pydocstyle::helpers::{leading_quote, trailing_quote};
use crate::rules::pyflakes::format::FormatSummary; use crate::rules::pyflakes::format::FormatSummary;
use crate::rules::pyupgrade::helpers::curly_escape; 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 /// Like [`FormatSummary`], but maps positional and keyword arguments to their
/// values. For example, given `{a} {b}".format(a=1, b=2)`, `FormatFunction` /// 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; 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()) { if checker.patch(diagnostic.kind.rule()) {
diagnostic.amend(Fix::replacement( diagnostic.amend(Fix::replacement(
contents, contents,

View File

@ -1,7 +1,10 @@
use crate::define_violation;
use crate::violation::AlwaysAutofixableViolation;
use anyhow::{anyhow, bail, Result}; use anyhow::{anyhow, bail, Result};
use libcst_native::{Arg, Codegen, CodegenState, Expression}; use libcst_native::{Arg, Codegen, CodegenState, Expression};
use once_cell::sync::Lazy; use once_cell::sync::Lazy;
use regex::Regex; use regex::Regex;
use ruff_macros::derive_message_formats;
use rustpython_ast::Expr; use rustpython_ast::Expr;
use crate::ast::types::Range; use crate::ast::types::Range;
@ -11,7 +14,20 @@ use crate::fix::Fix;
use crate::registry::Diagnostic; use crate::registry::Diagnostic;
use crate::rules::pyflakes::format::FormatSummary; use crate::rules::pyflakes::format::FormatSummary;
use crate::source_code::{Locator, Stylist}; 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, // An opening curly brace, followed by any integer, followed by any text,
// followed by a closing brace. // followed by a closing brace.
@ -112,7 +128,7 @@ pub(crate) fn format_literals(checker: &mut Checker, summary: &FormatSummary, ex
return; 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()) { if checker.patch(diagnostic.kind.rule()) {
// Currently, the only issue we know of is in LibCST: // Currently, the only issue we know of is in LibCST:
// https://github.com/Instagram/LibCST/issues/846 // https://github.com/Instagram/LibCST/issues/846

View File

@ -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_ast::{Constant, ExprKind, KeywordData};
use rustpython_parser::ast::Expr; use rustpython_parser::ast::Expr;
@ -6,7 +9,20 @@ use crate::ast::types::Range;
use crate::checkers::ast::Checker; use crate::checkers::ast::Checker;
use crate::fix::Fix; use crate::fix::Fix;
use crate::registry::Diagnostic; 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 /// UP033
pub fn functools_cache(checker: &mut Checker, decorator_list: &[Expr]) { 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( let mut diagnostic = Diagnostic::new(
violations::FunctoolsCache, FunctoolsCache,
Range::new(func.end_location.unwrap(), expr.end_location.unwrap()), Range::new(func.end_location.unwrap(), expr.end_location.unwrap()),
); );
if checker.patch(diagnostic.kind.rule()) { if checker.patch(diagnostic.kind.rule()) {

View File

@ -1,7 +1,6 @@
use itertools::Itertools; use itertools::Itertools;
use rustpython_ast::{Alias, AliasData, Stmt};
use ruff_macros::derive_message_formats; use ruff_macros::derive_message_formats;
use rustpython_ast::{Alias, AliasData, Stmt};
use crate::ast::types::Range; use crate::ast::types::Range;
use crate::ast::whitespace::indentation; use crate::ast::whitespace::indentation;

View File

@ -1,3 +1,6 @@
use crate::define_violation;
use crate::violation::AlwaysAutofixableViolation;
use ruff_macros::derive_message_formats;
use rustpython_ast::ExprKind; use rustpython_ast::ExprKind;
use rustpython_parser::ast::Expr; use rustpython_parser::ast::Expr;
@ -6,7 +9,20 @@ use crate::ast::types::Range;
use crate::checkers::ast::Checker; use crate::checkers::ast::Checker;
use crate::fix::Fix; use crate::fix::Fix;
use crate::registry::Diagnostic; 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 /// UP011
pub fn lru_cache_without_parameters(checker: &mut Checker, decorator_list: &[Expr]) { 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( let mut diagnostic = Diagnostic::new(
violations::LRUCacheWithoutParameters, LRUCacheWithoutParameters,
Range::new(func.end_location.unwrap(), expr.end_location.unwrap()), Range::new(func.end_location.unwrap(), expr.end_location.unwrap()),
); );
if checker.patch(diagnostic.kind.rule()) { if checker.patch(diagnostic.kind.rule()) {

View File

@ -1,38 +1,48 @@
pub(crate) use convert_named_tuple_functional_to_class::convert_named_tuple_functional_to_class; pub(crate) use convert_named_tuple_functional_to_class::{
pub(crate) use convert_typed_dict_functional_to_class::convert_typed_dict_functional_to_class; convert_named_tuple_functional_to_class, ConvertNamedTupleFunctionalToClass,
pub(crate) use datetime_utc_alias::datetime_utc_alias; };
pub(crate) use deprecated_unittest_alias::deprecated_unittest_alias; pub(crate) use convert_typed_dict_functional_to_class::{
pub(crate) use extraneous_parentheses::extraneous_parentheses; convert_typed_dict_functional_to_class, ConvertTypedDictFunctionalToClass,
pub(crate) use f_strings::f_strings; };
pub(crate) use format_literals::format_literals; pub(crate) use datetime_utc_alias::{datetime_utc_alias, DatetimeTimezoneUTC};
pub(crate) use functools_cache::functools_cache; 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 import_replacements::{import_replacements, ImportReplacements};
pub(crate) use lru_cache_without_parameters::lru_cache_without_parameters; pub(crate) use lru_cache_without_parameters::{
pub(crate) use native_literals::native_literals; lru_cache_without_parameters, LRUCacheWithoutParameters,
pub(crate) use open_alias::open_alias; };
pub(crate) use os_error_alias::os_error_alias; 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 outdated_version_block::{outdated_version_block, OutdatedVersionBlock};
pub(crate) use printf_string_formatting::printf_string_formatting; pub(crate) use printf_string_formatting::{printf_string_formatting, PrintfStringFormatting};
pub(crate) use redundant_open_modes::redundant_open_modes; pub(crate) use redundant_open_modes::{redundant_open_modes, RedundantOpenModes};
pub(crate) use replace_stdout_stderr::replace_stdout_stderr; pub(crate) use replace_stdout_stderr::{replace_stdout_stderr, ReplaceStdoutStderr};
pub(crate) use replace_universal_newlines::replace_universal_newlines; pub(crate) use replace_universal_newlines::{replace_universal_newlines, ReplaceUniversalNewlines};
pub(crate) use rewrite_c_element_tree::replace_c_element_tree; pub(crate) use rewrite_c_element_tree::{replace_c_element_tree, RewriteCElementTree};
pub(crate) use rewrite_mock_import::{rewrite_mock_attribute, rewrite_mock_import}; pub(crate) use rewrite_mock_import::{
pub(crate) use rewrite_unicode_literal::rewrite_unicode_literal; rewrite_mock_attribute, rewrite_mock_import, RewriteMockImport,
pub(crate) use rewrite_yield_from::rewrite_yield_from; };
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_args::super_args;
pub(crate) use super_call_with_parameters::super_call_with_parameters; pub(crate) use super_call_with_parameters::{super_call_with_parameters, SuperCallWithParameters};
pub(crate) use type_of_primitive::type_of_primitive; pub(crate) use type_of_primitive::{type_of_primitive, TypeOfPrimitive};
pub(crate) use typing_text_str_alias::typing_text_str_alias; pub(crate) use typing_text_str_alias::{typing_text_str_alias, TypingTextStrAlias};
pub(crate) use unnecessary_builtin_import::unnecessary_builtin_import; pub(crate) use unnecessary_builtin_import::{unnecessary_builtin_import, UnnecessaryBuiltinImport};
pub(crate) use unnecessary_coding_comment::unnecessary_coding_comment; pub(crate) use unnecessary_coding_comment::{
pub(crate) use unnecessary_encode_utf8::unnecessary_encode_utf8; unnecessary_coding_comment, PEP3120UnnecessaryCodingComment,
pub(crate) use unnecessary_future_import::unnecessary_future_import; };
pub(crate) use unpack_list_comprehension::unpack_list_comprehension; pub(crate) use unnecessary_encode_utf8::{unnecessary_encode_utf8, UnnecessaryEncodeUTF8};
pub(crate) use use_pep585_annotation::use_pep585_annotation; pub(crate) use unnecessary_future_import::{unnecessary_future_import, UnnecessaryFutureImport};
pub(crate) use use_pep604_annotation::use_pep604_annotation; pub(crate) use unpack_list_comprehension::{unpack_list_comprehension, RewriteListComprehension};
pub(crate) use useless_metaclass_type::useless_metaclass_type; pub(crate) use use_pep585_annotation::{use_pep585_annotation, UsePEP585Annotation};
pub(crate) use useless_object_inheritance::useless_object_inheritance; 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_named_tuple_functional_to_class;
mod convert_typed_dict_functional_to_class; mod convert_typed_dict_functional_to_class;

View File

@ -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_ast::{Constant, Expr, ExprKind, Keyword};
use rustpython_parser::lexer; use rustpython_parser::lexer;
use rustpython_parser::lexer::Tok; use rustpython_parser::lexer::Tok;
use serde::{Deserialize, Serialize};
use std::fmt;
use crate::ast::types::Range; use crate::ast::types::Range;
use crate::checkers::ast::Checker; use crate::checkers::ast::Checker;
use crate::fix::Fix; use crate::fix::Fix;
use crate::registry::Diagnostic; 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 /// UP018
pub fn native_literals( pub fn native_literals(
@ -25,7 +61,7 @@ pub fn native_literals(
if (id == "str" || id == "bytes") && checker.is_builtin(id) { if (id == "str" || id == "bytes") && checker.is_builtin(id) {
let Some(arg) = args.get(0) else { 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 LiteralType::Str
} else { } else {
LiteralType::Bytes LiteralType::Bytes
@ -94,7 +130,7 @@ pub fn native_literals(
} }
let mut diagnostic = Diagnostic::new( let mut diagnostic = Diagnostic::new(
violations::NativeLiterals { NativeLiterals {
literal_type: if id == "str" { literal_type: if id == "str" {
LiteralType::Str LiteralType::Str
} else { } else {

View File

@ -1,10 +1,26 @@
use crate::define_violation;
use crate::violation::AlwaysAutofixableViolation;
use ruff_macros::derive_message_formats;
use rustpython_ast::Expr; use rustpython_ast::Expr;
use crate::ast::types::Range; use crate::ast::types::Range;
use crate::checkers::ast::Checker; use crate::checkers::ast::Checker;
use crate::fix::Fix; use crate::fix::Fix;
use crate::registry::Diagnostic; 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 /// UP020
pub fn open_alias(checker: &mut Checker, expr: &Expr, func: &Expr) { 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) .resolve_call_path(func)
.map_or(false, |call_path| call_path.as_slice() == ["io", "open"]) .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()) { if checker.patch(diagnostic.kind.rule()) {
diagnostic.amend(Fix::replacement( diagnostic.amend(Fix::replacement(
"open".to_string(), "open".to_string(),

View File

@ -1,4 +1,7 @@
use crate::define_violation;
use crate::violation::AlwaysAutofixableViolation;
use itertools::Itertools; use itertools::Itertools;
use ruff_macros::derive_message_formats;
use rustpython_ast::{Excepthandler, ExcepthandlerKind, Expr, ExprKind, Located}; use rustpython_ast::{Excepthandler, ExcepthandlerKind, Expr, ExprKind, Located};
use crate::ast::helpers::compose_call_path; use crate::ast::helpers::compose_call_path;
@ -6,7 +9,26 @@ use crate::ast::types::Range;
use crate::checkers::ast::Checker; use crate::checkers::ast::Checker;
use crate::fix::Fix; use crate::fix::Fix;
use crate::registry::Diagnostic; use crate::registry::Diagnostic;
use crate::violations;
define_violation!(
pub struct OSErrorAlias {
pub name: Option<String>,
}
);
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_NAMES: &[&str] = &["EnvironmentError", "IOError", "WindowsError"];
const ERROR_MODULES: &[&str] = &["mmap", "select", "socket"]; const ERROR_MODULES: &[&str] = &["mmap", "select", "socket"];
@ -145,7 +167,7 @@ fn handle_making_changes(
final_str.push(')'); final_str.push(')');
} }
let mut diagnostic = Diagnostic::new( let mut diagnostic = Diagnostic::new(
violations::OSErrorAlias { OSErrorAlias {
name: compose_call_path(target), name: compose_call_path(target),
}, },
Range::from_located(target), Range::from_located(target),

View File

@ -1,3 +1,5 @@
use ruff_macros::derive_message_formats;
use std::cmp::Ordering; use std::cmp::Ordering;
use log::error; 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;
use rustpython_parser::lexer::Tok; use rustpython_parser::lexer::Tok;
use ruff_macros::derive_message_formats;
use crate::ast::types::{Range, RefEquality}; use crate::ast::types::{Range, RefEquality};
use crate::ast::whitespace::indentation; use crate::ast::whitespace::indentation;
use crate::autofix::helpers::delete_stmt; use crate::autofix::helpers::delete_stmt;

View File

@ -1,3 +1,7 @@
use crate::define_violation;
use crate::violation::AlwaysAutofixableViolation;
use ruff_macros::derive_message_formats;
use std::str::FromStr; use std::str::FromStr;
use rustpython_ast::Location; use rustpython_ast::Location;
@ -17,7 +21,20 @@ use crate::python::keyword::KWLIST;
use crate::registry::Diagnostic; use crate::registry::Diagnostic;
use crate::rules::pydocstyle::helpers::{leading_quote, trailing_quote}; use crate::rules::pydocstyle::helpers::{leading_quote, trailing_quote};
use crate::rules::pyupgrade::helpers::curly_escape; 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 { fn simplify_conversion_flag(flags: CConversionFlags) -> String {
let mut flag_string = String::new(); let mut flag_string = String::new();
@ -406,10 +423,7 @@ pub(crate) fn printf_string_formatting(
// Add the `.format` call. // Add the `.format` call.
contents.push_str(&format!(".format{params_string}")); contents.push_str(&format!(".format{params_string}"));
let mut diagnostic = Diagnostic::new( let mut diagnostic = Diagnostic::new(PrintfStringFormatting, Range::from_located(expr));
violations::PrintfStringFormatting,
Range::from_located(expr),
);
if checker.patch(diagnostic.kind.rule()) { if checker.patch(diagnostic.kind.rule()) {
diagnostic.amend(Fix::replacement( diagnostic.amend(Fix::replacement(
contents, contents,

View File

@ -1,3 +1,7 @@
use crate::define_violation;
use crate::violation::AlwaysAutofixableViolation;
use ruff_macros::derive_message_formats;
use std::str::FromStr; use std::str::FromStr;
use anyhow::{anyhow, Result}; use anyhow::{anyhow, Result};
@ -12,7 +16,34 @@ use crate::checkers::ast::Checker;
use crate::fix::Fix; use crate::fix::Fix;
use crate::registry::{Diagnostic, Rule}; use crate::registry::{Diagnostic, Rule};
use crate::source_code::Locator; use crate::source_code::Locator;
use crate::violations;
define_violation!(
pub struct RedundantOpenModes {
pub replacement: Option<String>,
}
);
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 OPEN_FUNC_NAME: &str = "open";
const MODE_KEYWORD_ARGUMENT: &str = "mode"; const MODE_KEYWORD_ARGUMENT: &str = "mode";
@ -81,7 +112,7 @@ fn create_check(
patch: bool, patch: bool,
) -> Diagnostic { ) -> Diagnostic {
let mut diagnostic = Diagnostic::new( let mut diagnostic = Diagnostic::new(
violations::RedundantOpenModes { RedundantOpenModes {
replacement: replacement_value.clone(), replacement: replacement_value.clone(),
}, },
Range::from_located(expr), Range::from_located(expr),

View File

@ -1,3 +1,6 @@
use crate::define_violation;
use crate::violation::AlwaysAutofixableViolation;
use ruff_macros::derive_message_formats;
use rustpython_ast::{Expr, Keyword}; use rustpython_ast::{Expr, Keyword};
use crate::ast::helpers::find_keyword; use crate::ast::helpers::find_keyword;
@ -7,7 +10,20 @@ use crate::checkers::ast::Checker;
use crate::fix::Fix; use crate::fix::Fix;
use crate::registry::Diagnostic; use crate::registry::Diagnostic;
use crate::source_code::{Locator, Stylist}; 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)] #[derive(Debug)]
struct MiddleContent<'a> { struct MiddleContent<'a> {
@ -116,8 +132,7 @@ pub fn replace_stdout_stderr(checker: &mut Checker, expr: &Expr, kwargs: &[Keywo
return; return;
} }
let mut diagnostic = let mut diagnostic = Diagnostic::new(ReplaceStdoutStderr, Range::from_located(expr));
Diagnostic::new(violations::ReplaceStdoutStderr, Range::from_located(expr));
if checker.patch(diagnostic.kind.rule()) { if checker.patch(diagnostic.kind.rule()) {
if let Some(fix) = generate_fix(checker.stylist, checker.locator, stdout, stderr) { if let Some(fix) = generate_fix(checker.stylist, checker.locator, stdout, stderr) {
diagnostic.amend(fix); diagnostic.amend(fix);

View File

@ -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 rustpython_ast::{Expr, Keyword, Location};
use crate::ast::helpers::find_keyword; use crate::ast::helpers::find_keyword;
@ -5,7 +8,20 @@ use crate::ast::types::Range;
use crate::checkers::ast::Checker; use crate::checkers::ast::Checker;
use crate::fix::Fix; use crate::fix::Fix;
use crate::registry::Diagnostic; 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 /// UP021
pub fn replace_universal_newlines(checker: &mut Checker, expr: &Expr, kwargs: &[Keyword]) { 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(), 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()) { if checker.patch(diagnostic.kind.rule()) {
diagnostic.amend(Fix::replacement( diagnostic.amend(Fix::replacement(
"text".to_string(), "text".to_string(),

View File

@ -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 rustpython_ast::{Located, Stmt, StmtKind};
use crate::ast::types::Range; use crate::ast::types::Range;
use crate::checkers::ast::Checker; use crate::checkers::ast::Checker;
use crate::fix::Fix; use crate::fix::Fix;
use crate::registry::Diagnostic; 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<T>(checker: &mut Checker, node: &Located<T>) { fn add_check_for_node<T>(checker: &mut Checker, node: &Located<T>) {
let mut diagnostic = let mut diagnostic = Diagnostic::new(RewriteCElementTree, Range::from_located(node));
Diagnostic::new(violations::RewriteCElementTree, Range::from_located(node));
if checker.patch(diagnostic.kind.rule()) { if checker.patch(diagnostic.kind.rule()) {
let contents = checker let contents = checker
.locator .locator

View File

@ -1,10 +1,14 @@
use crate::define_violation;
use crate::violation::AlwaysAutofixableViolation;
use anyhow::Result; use anyhow::Result;
use libcst_native::{ use libcst_native::{
AsName, AssignTargetExpression, Attribute, Codegen, CodegenState, Dot, Expression, Import, AsName, AssignTargetExpression, Attribute, Codegen, CodegenState, Dot, Expression, Import,
ImportAlias, ImportFrom, ImportNames, Name, NameOrAttribute, ParenthesizableWhitespace, ImportAlias, ImportFrom, ImportNames, Name, NameOrAttribute, ParenthesizableWhitespace,
}; };
use log::error; use log::error;
use ruff_macros::derive_message_formats;
use rustpython_ast::{Expr, ExprKind, Stmt, StmtKind}; use rustpython_ast::{Expr, ExprKind, Stmt, StmtKind};
use serde::{Deserialize, Serialize};
use crate::ast::helpers::collect_call_path; use crate::ast::helpers::collect_call_path;
use crate::ast::types::Range; 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::fix::Fix;
use crate::registry::{Diagnostic, Rule}; use crate::registry::{Diagnostic, Rule};
use crate::source_code::{Locator, Stylist}; 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. /// Return a vector of all non-`mock` imports.
fn clean_import_aliases(aliases: Vec<ImportAlias>) -> (Vec<ImportAlias>, Vec<Option<AsName>>) { fn clean_import_aliases(aliases: Vec<ImportAlias>) -> (Vec<ImportAlias>, Vec<Option<AsName>>) {
@ -209,7 +237,7 @@ pub fn rewrite_mock_attribute(checker: &mut Checker, expr: &Expr) {
if let ExprKind::Attribute { value, .. } = &expr.node { if let ExprKind::Attribute { value, .. } = &expr.node {
if collect_call_path(value).as_slice() == ["mock", "mock"] { if collect_call_path(value).as_slice() == ["mock", "mock"] {
let mut diagnostic = Diagnostic::new( let mut diagnostic = Diagnostic::new(
violations::RewriteMockImport { RewriteMockImport {
reference_type: MockReference::Attribute, reference_type: MockReference::Attribute,
}, },
Range::from_located(value), Range::from_located(value),
@ -256,7 +284,7 @@ pub fn rewrite_mock_import(checker: &mut Checker, stmt: &Stmt) {
for name in names { for name in names {
if name.node.name == "mock" || name.node.name == "mock.mock" { if name.node.name == "mock" || name.node.name == "mock.mock" {
let mut diagnostic = Diagnostic::new( let mut diagnostic = Diagnostic::new(
violations::RewriteMockImport { RewriteMockImport {
reference_type: MockReference::Import, reference_type: MockReference::Import,
}, },
Range::from_located(name), Range::from_located(name),
@ -284,7 +312,7 @@ pub fn rewrite_mock_import(checker: &mut Checker, stmt: &Stmt) {
if module == "mock" { if module == "mock" {
let mut diagnostic = Diagnostic::new( let mut diagnostic = Diagnostic::new(
violations::RewriteMockImport { RewriteMockImport {
reference_type: MockReference::Import, reference_type: MockReference::Import,
}, },
Range::from_located(stmt), Range::from_located(stmt),

View File

@ -1,17 +1,32 @@
use crate::define_violation;
use crate::violation::AlwaysAutofixableViolation;
use ruff_macros::derive_message_formats;
use rustpython_ast::{Expr, Location}; use rustpython_ast::{Expr, Location};
use crate::ast::types::Range; use crate::ast::types::Range;
use crate::checkers::ast::Checker; use crate::checkers::ast::Checker;
use crate::fix::Fix; use crate::fix::Fix;
use crate::registry::Diagnostic; 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 /// UP025
pub fn rewrite_unicode_literal(checker: &mut Checker, expr: &Expr, kind: Option<&str>) { pub fn rewrite_unicode_literal(checker: &mut Checker, expr: &Expr, kind: Option<&str>) {
if let Some(const_kind) = kind { if let Some(const_kind) = kind {
if const_kind.to_lowercase() == "u" { if const_kind.to_lowercase() == "u" {
let mut diagnostic = let mut diagnostic = Diagnostic::new(RewriteUnicodeLiteral, Range::from_located(expr));
Diagnostic::new(violations::RewriteUnicodeLiteral, Range::from_located(expr));
if checker.patch(diagnostic.kind.rule()) { if checker.patch(diagnostic.kind.rule()) {
diagnostic.amend(Fix::deletion( diagnostic.amend(Fix::deletion(
expr.location, expr.location,

View File

@ -1,3 +1,6 @@
use crate::define_violation;
use crate::violation::AlwaysAutofixableViolation;
use ruff_macros::derive_message_formats;
use rustc_hash::FxHashMap; use rustc_hash::FxHashMap;
use rustpython_ast::{Expr, ExprContext, ExprKind, Stmt, StmtKind}; use rustpython_ast::{Expr, ExprContext, ExprKind, Stmt, StmtKind};
@ -7,7 +10,20 @@ use crate::ast::visitor::Visitor;
use crate::checkers::ast::Checker; use crate::checkers::ast::Checker;
use crate::fix::Fix; use crate::fix::Fix;
use crate::registry::Diagnostic; 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 /// Return `true` if the two expressions are equivalent, and consistent solely
/// of tuples and names. /// of tuples and names.
@ -157,8 +173,7 @@ pub fn rewrite_yield_from(checker: &mut Checker, stmt: &Stmt) {
continue; continue;
} }
let mut diagnostic = let mut diagnostic = Diagnostic::new(RewriteYieldFrom, Range::from_located(item.stmt));
Diagnostic::new(violations::RewriteYieldFrom, Range::from_located(item.stmt));
if checker.patch(diagnostic.kind.rule()) { if checker.patch(diagnostic.kind.rule()) {
let contents = checker let contents = checker
.locator .locator

View File

@ -1,7 +1,10 @@
use crate::ast::helpers; use crate::ast::helpers;
use crate::ast::types::{Range, Scope, ScopeKind}; use crate::ast::types::{Range, Scope, ScopeKind};
use crate::registry::Diagnostic; use crate::registry::Diagnostic;
use crate::violations;
use crate::rules::pyupgrade::rules::SuperCallWithParameters;
use rustpython_ast::{ArgData, Expr, ExprKind, Stmt, StmtKind}; use rustpython_ast::{ArgData, Expr, ExprKind, Stmt, StmtKind};
/// UP008 /// UP008
@ -68,7 +71,7 @@ pub fn super_args(
if first_arg_id == parent_name && second_arg_id == parent_arg { if first_arg_id == parent_name && second_arg_id == parent_arg {
return Some(Diagnostic::new( return Some(Diagnostic::new(
violations::SuperCallWithParameters, SuperCallWithParameters,
Range::from_located(expr), Range::from_located(expr),
)); ));
} }

View File

@ -1,9 +1,26 @@
use crate::define_violation;
use crate::violation::AlwaysAutofixableViolation;
use ruff_macros::derive_message_formats;
use rustpython_ast::{Expr, Stmt}; use rustpython_ast::{Expr, Stmt};
use super::super::fixes; use super::super::fixes;
use crate::ast::helpers; use crate::ast::helpers;
use crate::checkers::ast::Checker; 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 /// UP008
pub fn super_call_with_parameters(checker: &mut Checker, expr: &Expr, func: &Expr, args: &[Expr]) { 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. // Only bother going through the super check at all if we're in a `super` call.

View File

@ -1,3 +1,6 @@
use crate::define_violation;
use crate::violation::AlwaysAutofixableViolation;
use ruff_macros::derive_message_formats;
use rustpython_ast::{Expr, ExprKind}; use rustpython_ast::{Expr, ExprKind};
use super::super::types::Primitive; use super::super::types::Primitive;
@ -5,7 +8,24 @@ use crate::ast::types::Range;
use crate::checkers::ast::Checker; use crate::checkers::ast::Checker;
use crate::fix::Fix; use crate::fix::Fix;
use crate::registry::Diagnostic; 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 /// UP003
pub fn type_of_primitive(checker: &mut Checker, expr: &Expr, func: &Expr, args: &[Expr]) { 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 { let Some(primitive) = Primitive::from_constant(value) else {
return; return;
}; };
let mut diagnostic = Diagnostic::new( let mut diagnostic = Diagnostic::new(TypeOfPrimitive { primitive }, Range::from_located(expr));
violations::TypeOfPrimitive { primitive },
Range::from_located(expr),
);
if checker.patch(diagnostic.kind.rule()) { if checker.patch(diagnostic.kind.rule()) {
diagnostic.amend(Fix::replacement( diagnostic.amend(Fix::replacement(
primitive.builtin(), primitive.builtin(),

View File

@ -1,18 +1,33 @@
use crate::define_violation;
use crate::violation::AlwaysAutofixableViolation;
use ruff_macros::derive_message_formats;
use rustpython_ast::Expr; use rustpython_ast::Expr;
use crate::ast::types::Range; use crate::ast::types::Range;
use crate::checkers::ast::Checker; use crate::checkers::ast::Checker;
use crate::fix::Fix; use crate::fix::Fix;
use crate::registry::Diagnostic; 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 /// UP019
pub fn typing_text_str_alias(checker: &mut Checker, expr: &Expr) { pub fn typing_text_str_alias(checker: &mut Checker, expr: &Expr) {
if checker.resolve_call_path(expr).map_or(false, |call_path| { if checker.resolve_call_path(expr).map_or(false, |call_path| {
call_path.as_slice() == ["typing", "Text"] call_path.as_slice() == ["typing", "Text"]
}) { }) {
let mut diagnostic = let mut diagnostic = Diagnostic::new(TypingTextStrAlias, Range::from_located(expr));
Diagnostic::new(violations::TypingTextStrAlias, Range::from_located(expr));
if checker.patch(diagnostic.kind.rule()) { if checker.patch(diagnostic.kind.rule()) {
diagnostic.amend(Fix::replacement( diagnostic.amend(Fix::replacement(
"str".to_string(), "str".to_string(),

View File

@ -1,12 +1,38 @@
use crate::define_violation;
use crate::violation::AlwaysAutofixableViolation;
use itertools::Itertools; use itertools::Itertools;
use log::error; use log::error;
use ruff_macros::derive_message_formats;
use rustpython_ast::{Alias, AliasData, Located}; use rustpython_ast::{Alias, AliasData, Located};
use rustpython_parser::ast::Stmt; use rustpython_parser::ast::Stmt;
use crate::ast::types::Range; use crate::ast::types::Range;
use crate::autofix;
use crate::checkers::ast::Checker; use crate::checkers::ast::Checker;
use crate::registry::Diagnostic; use crate::registry::Diagnostic;
use crate::{autofix, violations};
define_violation!(
pub struct UnnecessaryBuiltinImport {
pub names: Vec<String>,
}
);
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] = &[ const BUILTINS: &[&str] = &[
"*", "*",
@ -69,7 +95,7 @@ pub fn unnecessary_builtin_import(
return; return;
} }
let mut diagnostic = Diagnostic::new( let mut diagnostic = Diagnostic::new(
violations::UnnecessaryBuiltinImport { UnnecessaryBuiltinImport {
names: unused_imports names: unused_imports
.iter() .iter()
.map(|alias| alias.node.name.to_string()) .map(|alias| alias.node.name.to_string())

View File

@ -1,11 +1,28 @@
use crate::ast::types::Range; use crate::ast::types::Range;
use crate::define_violation;
use crate::fix::Fix; use crate::fix::Fix;
use crate::registry::Diagnostic; use crate::registry::Diagnostic;
use crate::violations; use crate::violation::AlwaysAutofixableViolation;
use once_cell::sync::Lazy; use once_cell::sync::Lazy;
use regex::Regex; use regex::Regex;
use ruff_macros::derive_message_formats;
use rustpython_ast::Location; 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. // Regex from PEP263.
static CODING_COMMENT_REGEX: Lazy<Regex> = static CODING_COMMENT_REGEX: Lazy<Regex> =
Lazy::new(|| Regex::new(r"^[ \t\f]*#.*?coding[:=][ \t]*utf-?8").unwrap()); 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. // PEP3120 makes utf-8 the default encoding.
if CODING_COMMENT_REGEX.is_match(line) { if CODING_COMMENT_REGEX.is_match(line) {
let mut diagnostic = Diagnostic::new( let mut diagnostic = Diagnostic::new(
violations::PEP3120UnnecessaryCodingComment, PEP3120UnnecessaryCodingComment,
Range::new(Location::new(lineno + 1, 0), Location::new(lineno + 2, 0)), Range::new(Location::new(lineno + 1, 0), Location::new(lineno + 2, 0)),
); );
if autofix { if autofix {

View File

@ -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 rustpython_ast::{Constant, Expr, ExprKind, Keyword};
use crate::ast::types::Range; use crate::ast::types::Range;
@ -5,7 +8,20 @@ use crate::checkers::ast::Checker;
use crate::fix::Fix; use crate::fix::Fix;
use crate::registry::{Diagnostic, Rule}; use crate::registry::{Diagnostic, Rule};
use crate::source_code::Locator; 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"]; 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, patch: bool,
) -> Option<Diagnostic> { ) -> Option<Diagnostic> {
if let Some(arg) = args.get(0) { if let Some(arg) = args.get(0) {
let mut diagnostic = let mut diagnostic = Diagnostic::new(UnnecessaryEncodeUTF8, Range::from_located(expr));
Diagnostic::new(violations::UnnecessaryEncodeUTF8, Range::from_located(expr));
if patch { if patch {
diagnostic.amend(Fix::deletion(arg.location, arg.end_location.unwrap())); diagnostic.amend(Fix::deletion(arg.location, arg.end_location.unwrap()));
} }
Some(diagnostic) Some(diagnostic)
} else if let Some(kwarg) = kwargs.get(0) { } else if let Some(kwarg) = kwargs.get(0) {
let mut diagnostic = let mut diagnostic = Diagnostic::new(UnnecessaryEncodeUTF8, Range::from_located(expr));
Diagnostic::new(violations::UnnecessaryEncodeUTF8, Range::from_located(expr));
if patch { if patch {
diagnostic.amend(Fix::deletion(kwarg.location, kwarg.end_location.unwrap())); diagnostic.amend(Fix::deletion(kwarg.location, kwarg.end_location.unwrap()));
} }
@ -85,8 +99,7 @@ fn replace_with_bytes_literal(
locator: &Locator, locator: &Locator,
patch: bool, patch: bool,
) -> Diagnostic { ) -> Diagnostic {
let mut diagnostic = let mut diagnostic = Diagnostic::new(UnnecessaryEncodeUTF8, Range::from_located(expr));
Diagnostic::new(violations::UnnecessaryEncodeUTF8, Range::from_located(expr));
if patch { if patch {
let content = locator.slice_source_code_range(&Range::new( let content = locator.slice_source_code_range(&Range::new(
constant.location, constant.location,

View File

@ -1,12 +1,38 @@
use crate::define_violation;
use crate::violation::AlwaysAutofixableViolation;
use itertools::Itertools; use itertools::Itertools;
use log::error; use log::error;
use ruff_macros::derive_message_formats;
use rustpython_ast::{Alias, AliasData, Located}; use rustpython_ast::{Alias, AliasData, Located};
use rustpython_parser::ast::Stmt; use rustpython_parser::ast::Stmt;
use crate::ast::types::Range; use crate::ast::types::Range;
use crate::autofix;
use crate::checkers::ast::Checker; use crate::checkers::ast::Checker;
use crate::registry::Diagnostic; use crate::registry::Diagnostic;
use crate::{autofix, violations};
define_violation!(
pub struct UnnecessaryFutureImport {
pub names: Vec<String>,
}
);
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] = &[ const PY33_PLUS_REMOVE_FUTURES: &[&str] = &[
"nested_scopes", "nested_scopes",
@ -49,7 +75,7 @@ pub fn unnecessary_future_import(checker: &mut Checker, stmt: &Stmt, names: &[Lo
return; return;
} }
let mut diagnostic = Diagnostic::new( let mut diagnostic = Diagnostic::new(
violations::UnnecessaryFutureImport { UnnecessaryFutureImport {
names: unused_imports names: unused_imports
.iter() .iter()
.map(|alias| alias.node.name.to_string()) .map(|alias| alias.node.name.to_string())

View File

@ -1,10 +1,26 @@
use crate::define_violation;
use crate::violation::AlwaysAutofixableViolation;
use ruff_macros::derive_message_formats;
use rustpython_ast::{Expr, ExprKind}; use rustpython_ast::{Expr, ExprKind};
use crate::ast::types::Range; use crate::ast::types::Range;
use crate::checkers::ast::Checker; use crate::checkers::ast::Checker;
use crate::fix::Fix; use crate::fix::Fix;
use crate::registry::Diagnostic; 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`. /// Returns `true` if `expr` contains an `ExprKind::Await`.
fn contains_await(expr: &Expr) -> bool { fn contains_await(expr: &Expr) -> bool {
@ -79,10 +95,8 @@ pub fn unpack_list_comprehension(checker: &mut Checker, targets: &[Expr], value:
return; return;
} }
let mut diagnostic = Diagnostic::new( let mut diagnostic =
violations::RewriteListComprehension, Diagnostic::new(RewriteListComprehension, Range::from_located(value));
Range::from_located(value),
);
if checker.patch(diagnostic.kind.rule()) { if checker.patch(diagnostic.kind.rule()) {
let existing = checker let existing = checker
.locator .locator

View File

@ -1,10 +1,34 @@
use crate::define_violation;
use crate::violation::AlwaysAutofixableViolation;
use ruff_macros::derive_message_formats;
use rustpython_ast::Expr; use rustpython_ast::Expr;
use crate::ast::types::Range; use crate::ast::types::Range;
use crate::checkers::ast::Checker; use crate::checkers::ast::Checker;
use crate::fix::Fix; use crate::fix::Fix;
use crate::registry::Diagnostic; 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 /// UP006
pub fn use_pep585_annotation(checker: &mut Checker, expr: &Expr) { 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()) .and_then(|call_path| call_path.last().copied())
{ {
let mut diagnostic = Diagnostic::new( let mut diagnostic = Diagnostic::new(
violations::UsePEP585Annotation { UsePEP585Annotation {
name: binding.to_string(), name: binding.to_string(),
}, },
Range::from_located(expr), Range::from_located(expr),

View File

@ -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 rustpython_ast::{Constant, Expr, ExprKind, Location, Operator};
use crate::ast::helpers::unparse_expr; use crate::ast::helpers::unparse_expr;
@ -5,7 +8,20 @@ use crate::ast::types::Range;
use crate::checkers::ast::Checker; use crate::checkers::ast::Checker;
use crate::fix::Fix; use crate::fix::Fix;
use crate::registry::Diagnostic; 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 { fn optional(expr: &Expr) -> Expr {
Expr::new( Expr::new(
@ -80,8 +96,7 @@ pub fn use_pep604_annotation(checker: &mut Checker, expr: &Expr, value: &Expr, s
match typing_member { match typing_member {
TypingMember::Optional => { TypingMember::Optional => {
let mut diagnostic = let mut diagnostic = Diagnostic::new(UsePEP604Annotation, Range::from_located(expr));
Diagnostic::new(violations::UsePEP604Annotation, Range::from_located(expr));
if checker.patch(diagnostic.kind.rule()) { if checker.patch(diagnostic.kind.rule()) {
diagnostic.amend(Fix::replacement( diagnostic.amend(Fix::replacement(
unparse_expr(&optional(slice), checker.stylist), 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); checker.diagnostics.push(diagnostic);
} }
TypingMember::Union => { TypingMember::Union => {
let mut diagnostic = let mut diagnostic = Diagnostic::new(UsePEP604Annotation, Range::from_located(expr));
Diagnostic::new(violations::UsePEP604Annotation, Range::from_located(expr));
if checker.patch(diagnostic.kind.rule()) { if checker.patch(diagnostic.kind.rule()) {
match &slice.node { match &slice.node {
ExprKind::Slice { .. } => { ExprKind::Slice { .. } => {

View File

@ -1,11 +1,27 @@
use crate::define_violation;
use crate::violation::AlwaysAutofixableViolation;
use log::error; use log::error;
use ruff_macros::derive_message_formats;
use rustpython_ast::{Expr, ExprKind, Stmt}; use rustpython_ast::{Expr, ExprKind, Stmt};
use crate::ast::types::Range; use crate::ast::types::Range;
use crate::autofix::helpers; use crate::autofix::helpers;
use crate::checkers::ast::Checker; use crate::checkers::ast::Checker;
use crate::registry::Diagnostic; 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<Diagnostic> { fn rule(targets: &[Expr], value: &Expr, location: Range) -> Option<Diagnostic> {
if targets.len() != 1 { if targets.len() != 1 {
@ -23,7 +39,7 @@ fn rule(targets: &[Expr], value: &Expr, location: Range) -> Option<Diagnostic> {
if id != "type" { if id != "type" {
return None; return None;
} }
Some(Diagnostic::new(violations::UselessMetaclassType, location)) Some(Diagnostic::new(UselessMetaclassType, location))
} }
/// UP001 /// UP001

View File

@ -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 rustpython_ast::{Expr, ExprKind, Keyword, Stmt};
use super::super::fixes; use super::super::fixes;
use crate::ast::types::{Binding, BindingKind, Range, Scope}; use crate::ast::types::{Binding, BindingKind, Range, Scope};
use crate::checkers::ast::Checker; use crate::checkers::ast::Checker;
use crate::registry::Diagnostic; 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<Diagnostic> { fn rule(name: &str, bases: &[Expr], scope: &Scope, bindings: &[Binding]) -> Option<Diagnostic> {
for expr in bases { for expr in bases {
@ -27,7 +46,7 @@ fn rule(name: &str, bases: &[Expr], scope: &Scope, bindings: &[Binding]) -> Opti
continue; continue;
} }
return Some(Diagnostic::new( return Some(Diagnostic::new(
violations::UselessObjectInheritance { UselessObjectInheritance {
name: name.to_string(), name: name.to_string(),
}, },
Range::from_located(expr), Range::from_located(expr),

View File

@ -8,7 +8,7 @@ use serde::{Deserialize, Serialize};
use crate::define_violation; use crate::define_violation;
use crate::rules::flake8_debugger::types::DebuggerUsingType; use crate::rules::flake8_debugger::types::DebuggerUsingType;
use crate::rules::pyupgrade::types::Primitive;
use crate::violation::{AlwaysAutofixableViolation, AutofixKind, Availability, Violation}; use crate::violation::{AlwaysAutofixableViolation, AutofixKind, Availability, Violation};
// pylint // 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<String>,
}
);
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<String>,
}
);
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<AutofixKind> = Some(AutofixKind::new(Availability::Always));
#[derive_message_formats]
fn message(&self) -> String {
format!("Use `datetime.UTC` alias")
}
fn autofix_title_formatter(&self) -> Option<fn(&Self) -> 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<String>,
}
);
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<String>,
}
);
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 // pydocstyle
define_violation!( define_violation!(