From df3b95a73dc813ca640554d0d20418c6bb501442 Mon Sep 17 00:00:00 2001 From: Charlie Marsh Date: Sun, 21 May 2023 14:49:20 -0400 Subject: [PATCH] clean up --- crates/ruff/src/checkers/ast/mod.rs | 104 ++++-------------- crates/ruff/src/checkers/ast/traits.rs | 44 +++++--- .../rules/locals_in_render_function.rs | 15 ++- .../ruff/src/rules/flake8_django/rules/mod.rs | 2 +- crates/ruff/src/rules/pyupgrade/fixes.rs | 1 - .../rules/deprecated_unittest_alias.rs | 19 +++- crates/ruff/src/rules/pyupgrade/rules/mod.rs | 22 ++-- .../rules/pyupgrade/rules/native_literals.rs | 21 +++- .../src/rules/pyupgrade/rules/open_alias.rs | 29 +++-- .../rules/pyupgrade/rules/os_error_alias.rs | 19 +++- .../pyupgrade/rules/redundant_open_modes.rs | 29 +++-- .../pyupgrade/rules/replace_stdout_stderr.rs | 21 +++- .../rules/replace_universal_newlines.rs | 19 +++- .../rules/super_call_with_parameters.rs | 21 +++- .../pyupgrade/rules/type_of_primitive.rs | 12 +- .../rules/unnecessary_encode_utf8.rs | 20 +++- .../pyupgrade/rules/use_pep604_isinstance.rs | 21 +++- 17 files changed, 250 insertions(+), 169 deletions(-) diff --git a/crates/ruff/src/checkers/ast/mod.rs b/crates/ruff/src/checkers/ast/mod.rs index 78336f9171..2f03169693 100644 --- a/crates/ruff/src/checkers/ast/mod.rs +++ b/crates/ruff/src/checkers/ast/mod.rs @@ -35,15 +35,13 @@ use ruff_python_stdlib::builtins::{BUILTINS, MAGIC_GLOBALS}; use ruff_python_stdlib::path::is_python_stub_file; use crate::checkers::ast::deferred::Deferred; -use crate::checkers::ast::traits::{AstRule, AstRuleExecutor, RegisteredAstRule}; +use crate::checkers::ast::traits::RegisteredAstRule; use crate::docstrings::extraction::ExtractionTarget; use crate::docstrings::Docstring; use crate::fs::relativize_path; use crate::importer::Importer; use crate::noqa::NoqaMapping; use crate::registry::{AsRule, Rule}; -use crate::rules::flake8_django::rules::DjangoLocalsInRenderFunction; -use crate::rules::pyupgrade::rules::TypeOfPrimitive; use crate::rules::{ flake8_2020, flake8_annotations, flake8_async, flake8_bandit, flake8_blind_except, flake8_boolean_trap, flake8_bugbear, flake8_builtins, flake8_comprehensions, flake8_datetimez, @@ -81,11 +79,8 @@ pub(crate) struct Checker<'a> { deferred: Deferred<'a>, // Check-specific state. pub(crate) flake8_bugbear_seen: Vec<&'a Expr>, - // Dispatches - // We can have a separate vector for each rule type? - rules: Vec, &RuleContext, &ast::ExprCall)>, - analysis_rules: Vec>, - _analysis_rules: Vec>, + // Dispatchers + call_rules: Vec>, } pub(crate) struct RuleContext<'a> { @@ -110,73 +105,6 @@ impl<'a> Checker<'a> { indexer: &'a Indexer, importer: Importer<'a>, ) -> Checker<'a> { - let mut rules: Vec, &RuleContext, &ast::ExprCall)> = vec![]; - - // flake8-django - if settings.rules.enabled(Rule::DjangoLocalsInRenderFunction) { - rules.push(flake8_django::rules::locals_in_render_function); - } - - // pyupgrade - if settings.rules.enabled(Rule::DeprecatedUnittestAlias) { - rules.push(pyupgrade::rules::deprecated_unittest_alias); - } - if settings.rules.enabled(Rule::SuperCallWithParameters) { - rules.push(pyupgrade::rules::super_call_with_parameters); - } - if settings.rules.enabled(Rule::UnnecessaryEncodeUTF8) { - rules.push(pyupgrade::rules::unnecessary_encode_utf8); - } - if settings.rules.enabled(Rule::RedundantOpenModes) { - rules.push(pyupgrade::rules::redundant_open_modes); - } - if settings.rules.enabled(Rule::NativeLiterals) { - rules.push(pyupgrade::rules::native_literals); - } - if settings.rules.enabled(Rule::OpenAlias) { - rules.push(pyupgrade::rules::open_alias); - } - if settings.rules.enabled(Rule::ReplaceUniversalNewlines) { - rules.push(pyupgrade::rules::replace_universal_newlines); - } - if settings.rules.enabled(Rule::ReplaceStdoutStderr) { - rules.push(pyupgrade::rules::replace_stdout_stderr); - } - if settings.rules.enabled(Rule::OSErrorAlias) { - rules.push(pyupgrade::rules::os_error_alias_call); - } - if settings.rules.enabled(Rule::NonPEP604Isinstance) - && settings.target_version >= PythonVersion::Py310 - { - rules.push(pyupgrade::rules::use_pep604_isinstance); - } - if settings.rules.enabled(Rule::TypeOfPrimitive) { - rules.push(pyupgrade::rules::type_of_primitive); - } - - let mut _analysis_rules: Vec> = vec![]; - - // flake8-django - if settings.rules.enabled(Rule::DjangoLocalsInRenderFunction) { - _analysis_rules.push(DjangoLocalsInRenderFunction::run); - } - - // We _can_ do this which is nice. - - // This is closest to Rome. - let mut analysis_rules: Vec> = vec![]; - - for analyzer in [ - RegisteredAstRule::new::( - Rule::DjangoLocalsInRenderFunction, - ), - RegisteredAstRule::new::(Rule::TypeOfPrimitive), - ] { - if settings.rules.enabled(analyzer.rule) { - analysis_rules.push(analyzer); - } - } - Checker { settings, noqa_line_for, @@ -194,9 +122,23 @@ impl<'a> Checker<'a> { diagnostics: Vec::default(), deletions: FxHashSet::default(), flake8_bugbear_seen: Vec::default(), - rules, - analysis_rules, - _analysis_rules, + call_rules: [ + RegisteredAstRule::new::(), + RegisteredAstRule::new::(), + RegisteredAstRule::new::(), + RegisteredAstRule::new::(), + RegisteredAstRule::new::(), + RegisteredAstRule::new::(), + RegisteredAstRule::new::(), + RegisteredAstRule::new::(), + RegisteredAstRule::new::(), + RegisteredAstRule::new::(), + RegisteredAstRule::new::(), + RegisteredAstRule::new::(), + ] + .into_iter() + .filter(|rule| rule.enabled(settings)) + .collect(), } } } @@ -230,7 +172,7 @@ impl<'a> RuleContext<'a> { Generator::new( self.stylist.indentation(), - quote_style(&self.ctx, self.locator, self.indexer).unwrap_or(self.stylist.quote()), + quote_style(self.ctx, self.locator, self.indexer).unwrap_or(self.stylist.quote()), self.stylist.line_ending(), ) } @@ -2697,8 +2639,8 @@ where indexer: self.indexer, ctx: &self.ctx, }; - for rule in &self.analysis_rules { - (rule.run)(&mut self.diagnostics, &immutable_checker, call); + for rule in &self.call_rules { + rule.run(&mut self.diagnostics, &immutable_checker, call); } let ast::ExprCall { diff --git a/crates/ruff/src/checkers/ast/traits.rs b/crates/ruff/src/checkers/ast/traits.rs index e7f13a0c25..022f9a92bf 100644 --- a/crates/ruff/src/checkers/ast/traits.rs +++ b/crates/ruff/src/checkers/ast/traits.rs @@ -2,23 +2,41 @@ use ruff_diagnostics::Diagnostic; use crate::checkers::ast::RuleContext; use crate::registry::Rule; +use crate::settings::Settings; -pub(crate) struct RegisteredAstRule { - pub(crate) run: AstRuleExecutor, - pub(crate) rule: Rule, +/// Trait for a lint rule that can be run on an AST node of type `T`. +pub(crate) trait AstAnalyzer: Sized { + /// The [`Rule`] that this analyzer implements. + fn rule() -> Rule; + + /// Run the analyzer on the given node. + fn run(diagnostics: &mut Vec, checker: &RuleContext, node: &T); +} + +/// Internal representation of a single [`Rule`] that can be run on an AST node of type `T`. +pub(super) struct RegisteredAstRule { + rule: Rule, + run: Run, } -// A nice thing about this is that we can have state that lives in this struct, -// and we can pass it to the `run` function... E.g., flake8_bugbear_seen. impl RegisteredAstRule { - pub(crate) fn new + 'static>(rule: Rule) -> Self { - Self { run: R::run, rule } + pub(super) fn new + 'static>() -> Self { + Self { + rule: R::rule(), + run: R::run, + } + } + + #[inline] + pub(super) fn enabled(&self, settings: &Settings) -> bool { + settings.rules.enabled(self.rule) + } + + #[inline] + pub(super) fn run(&self, diagnostics: &mut Vec, context: &RuleContext, node: &T) { + (self.run)(diagnostics, context, node); } } -pub(crate) type AstRuleExecutor = - fn(diagnostics: &mut Vec, checker: &RuleContext, node: &T); - -pub(crate) trait AstRule: Sized { - fn run(diagnostics: &mut Vec, checker: &RuleContext, node: &T); -} +/// Executor for an [`AstAnalyzer`] as a generic function pointer. +type Run = fn(diagnostics: &mut Vec, checker: &RuleContext, node: &T); diff --git a/crates/ruff/src/rules/flake8_django/rules/locals_in_render_function.rs b/crates/ruff/src/rules/flake8_django/rules/locals_in_render_function.rs index 02f82de857..efbbf21f54 100644 --- a/crates/ruff/src/rules/flake8_django/rules/locals_in_render_function.rs +++ b/crates/ruff/src/rules/flake8_django/rules/locals_in_render_function.rs @@ -3,8 +3,9 @@ use rustpython_parser::ast::{self, Expr, Ranged}; use ruff_diagnostics::{Diagnostic, Violation}; use ruff_macros::{derive_message_formats, violation}; -use crate::checkers::ast::traits::AstRule; -use crate::checkers::ast::{Checker, RuleContext}; +use crate::checkers::ast::traits::AstAnalyzer; +use crate::checkers::ast::RuleContext; +use crate::registry::Rule; /// ## What it does /// Checks for the use of `locals()` in `render` functions. @@ -43,13 +44,17 @@ impl Violation for DjangoLocalsInRenderFunction { } } -impl AstRule for DjangoLocalsInRenderFunction { +/// DJ003 +impl AstAnalyzer for DjangoLocalsInRenderFunction { + fn rule() -> Rule { + Rule::DjangoLocalsInRenderFunction + } + fn run(diagnostics: &mut Vec, context: &RuleContext, node: &ast::ExprCall) { - locals_in_render_function(diagnostics, context, node) + locals_in_render_function(diagnostics, context, node); } } -/// DJ003 pub(crate) fn locals_in_render_function( diagnostics: &mut Vec, checker: &RuleContext, diff --git a/crates/ruff/src/rules/flake8_django/rules/mod.rs b/crates/ruff/src/rules/flake8_django/rules/mod.rs index 2b5188d71a..fa28c7a6d7 100644 --- a/crates/ruff/src/rules/flake8_django/rules/mod.rs +++ b/crates/ruff/src/rules/flake8_django/rules/mod.rs @@ -1,7 +1,7 @@ pub(crate) use all_with_model_form::{all_with_model_form, DjangoAllWithModelForm}; pub(crate) use exclude_with_model_form::{exclude_with_model_form, DjangoExcludeWithModelForm}; pub(crate) use locals_in_render_function::{ - locals_in_render_function, DjangoLocalsInRenderFunction, + DjangoLocalsInRenderFunction, }; pub(crate) use model_without_dunder_str::{model_without_dunder_str, DjangoModelWithoutDunderStr}; pub(crate) use non_leading_receiver_decorator::{ diff --git a/crates/ruff/src/rules/pyupgrade/fixes.rs b/crates/ruff/src/rules/pyupgrade/fixes.rs index 8ff8c5de58..5c77674285 100644 --- a/crates/ruff/src/rules/pyupgrade/fixes.rs +++ b/crates/ruff/src/rules/pyupgrade/fixes.rs @@ -4,7 +4,6 @@ use libcst_native::{ SmallStatement, Statement, Suite, }; use ruff_text_size::{TextRange, TextSize}; -use rustpython_parser::ast::{Expr, Ranged}; use rustpython_parser::{lexer, Mode, Tok}; use ruff_diagnostics::Edit; diff --git a/crates/ruff/src/rules/pyupgrade/rules/deprecated_unittest_alias.rs b/crates/ruff/src/rules/pyupgrade/rules/deprecated_unittest_alias.rs index 9ce5ddddd4..e620550c23 100644 --- a/crates/ruff/src/rules/pyupgrade/rules/deprecated_unittest_alias.rs +++ b/crates/ruff/src/rules/pyupgrade/rules/deprecated_unittest_alias.rs @@ -1,12 +1,13 @@ use once_cell::sync::Lazy; use rustc_hash::FxHashMap; -use rustpython_parser::ast::{self, Expr, ExprCall}; +use rustpython_parser::ast::{self, Expr}; use ruff_diagnostics::{AlwaysAutofixableViolation, Diagnostic, Edit, Fix}; use ruff_macros::{derive_message_formats, violation}; -use crate::checkers::ast::{Checker, RuleContext}; -use crate::registry::AsRule; +use crate::checkers::ast::traits::AstAnalyzer; +use crate::checkers::ast::RuleContext; +use crate::registry::{AsRule, Rule}; #[violation] pub struct DeprecatedUnittestAlias { @@ -27,6 +28,16 @@ impl AlwaysAutofixableViolation for DeprecatedUnittestAlias { } } +impl AstAnalyzer for DeprecatedUnittestAlias { + fn rule() -> Rule { + Rule::DeprecatedUnittestAlias + } + + fn run(diagnostics: &mut Vec, checker: &RuleContext, node: &ast::ExprCall) { + deprecated_unittest_alias(diagnostics, checker, node); + } +} + static DEPRECATED_ALIASES: Lazy> = Lazy::new(|| { FxHashMap::from_iter([ ("failUnlessEqual", "assertEqual"), @@ -51,7 +62,7 @@ static DEPRECATED_ALIASES: Lazy> = Lazy::n pub(crate) fn deprecated_unittest_alias( diagnostics: &mut Vec, checker: &RuleContext, - ExprCall { func, .. }: &ExprCall, + ast::ExprCall { func, .. }: &ast::ExprCall, ) { let Expr::Attribute(ast::ExprAttribute { value, attr, range, .. }) = func.as_ref() else { return; diff --git a/crates/ruff/src/rules/pyupgrade/rules/mod.rs b/crates/ruff/src/rules/pyupgrade/rules/mod.rs index 2a09b35d65..7b5eb7813d 100644 --- a/crates/ruff/src/rules/pyupgrade/rules/mod.rs +++ b/crates/ruff/src/rules/pyupgrade/rules/mod.rs @@ -10,7 +10,7 @@ pub(crate) use deprecated_import::{deprecated_import, DeprecatedImport}; pub(crate) use deprecated_mock_import::{ deprecated_mock_attribute, deprecated_mock_import, DeprecatedMockImport, }; -pub(crate) use deprecated_unittest_alias::{deprecated_unittest_alias, DeprecatedUnittestAlias}; +pub(crate) use 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}; @@ -20,31 +20,31 @@ pub(crate) use lru_cache_with_maxsize_none::{ 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 native_literals::{NativeLiterals}; +pub(crate) use open_alias::{OpenAlias}; pub(crate) use os_error_alias::{ - os_error_alias_call, os_error_alias_handlers, os_error_alias_raise, OSErrorAlias, + os_error_alias_handlers, os_error_alias_raise, OSErrorAlias, }; pub(crate) use outdated_version_block::{outdated_version_block, OutdatedVersionBlock}; pub(crate) use printf_string_formatting::{printf_string_formatting, PrintfStringFormatting}; pub(crate) use quoted_annotation::{quoted_annotation, QuotedAnnotation}; -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 super_call_with_parameters::{super_call_with_parameters, SuperCallWithParameters}; -pub(crate) use type_of_primitive::{type_of_primitive, TypeOfPrimitive}; +pub(crate) use redundant_open_modes::{RedundantOpenModes}; +pub(crate) use replace_stdout_stderr::{ReplaceStdoutStderr}; +pub(crate) use replace_universal_newlines::{ReplaceUniversalNewlines}; +pub(crate) use super_call_with_parameters::{SuperCallWithParameters}; +pub(crate) use type_of_primitive::{TypeOfPrimitive}; pub(crate) use typing_text_str_alias::{typing_text_str_alias, TypingTextStrAlias}; pub(crate) use unicode_kind_prefix::{unicode_kind_prefix, UnicodeKindPrefix}; pub(crate) use unnecessary_builtin_import::{unnecessary_builtin_import, UnnecessaryBuiltinImport}; pub(crate) use unnecessary_coding_comment::{unnecessary_coding_comment, UTF8EncodingDeclaration}; -pub(crate) use unnecessary_encode_utf8::{unnecessary_encode_utf8, UnnecessaryEncodeUTF8}; +pub(crate) use unnecessary_encode_utf8::{UnnecessaryEncodeUTF8}; pub(crate) use unnecessary_future_import::{unnecessary_future_import, UnnecessaryFutureImport}; pub(crate) use unpacked_list_comprehension::{ unpacked_list_comprehension, UnpackedListComprehension, }; pub(crate) use use_pep585_annotation::{use_pep585_annotation, NonPEP585Annotation}; pub(crate) use use_pep604_annotation::{use_pep604_annotation, NonPEP604Annotation}; -pub(crate) use use_pep604_isinstance::{use_pep604_isinstance, NonPEP604Isinstance}; +pub(crate) use use_pep604_isinstance::{NonPEP604Isinstance}; pub(crate) use useless_metaclass_type::{useless_metaclass_type, UselessMetaclassType}; pub(crate) use useless_object_inheritance::{useless_object_inheritance, UselessObjectInheritance}; pub(crate) use yield_in_for_loop::{yield_in_for_loop, YieldInForLoop}; diff --git a/crates/ruff/src/rules/pyupgrade/rules/native_literals.rs b/crates/ruff/src/rules/pyupgrade/rules/native_literals.rs index 8faf34d183..59e6472496 100644 --- a/crates/ruff/src/rules/pyupgrade/rules/native_literals.rs +++ b/crates/ruff/src/rules/pyupgrade/rules/native_literals.rs @@ -1,13 +1,14 @@ use std::fmt; -use rustpython_parser::ast::{self, Constant, Expr, ExprCall, Ranged}; +use rustpython_parser::ast::{self, Constant, Expr, Ranged}; use ruff_diagnostics::{AlwaysAutofixableViolation, Diagnostic, Edit, Fix}; use ruff_macros::{derive_message_formats, violation}; use ruff_python_ast::str::is_implicit_concatenation; -use crate::checkers::ast::{Checker, RuleContext}; -use crate::registry::AsRule; +use crate::checkers::ast::traits::AstAnalyzer; +use crate::checkers::ast::RuleContext; +use crate::registry::{AsRule, Rule}; #[derive(Debug, PartialEq, Eq, Copy, Clone)] pub(crate) enum LiteralType { @@ -42,16 +43,26 @@ impl AlwaysAutofixableViolation for NativeLiterals { } } +impl AstAnalyzer for NativeLiterals { + fn rule() -> Rule { + Rule::NativeLiterals + } + + fn run(diagnostics: &mut Vec, checker: &RuleContext, node: &ast::ExprCall) { + native_literals(diagnostics, checker, node); + } +} + /// UP018 pub(crate) fn native_literals( diagnostics: &mut Vec, checker: &RuleContext, - ExprCall { + ast::ExprCall { func, args, keywords, range, - }: &ExprCall, + }: &ast::ExprCall, ) { let Expr::Name(ast::ExprName { id, .. }) = func.as_ref() else { return; diff --git a/crates/ruff/src/rules/pyupgrade/rules/open_alias.rs b/crates/ruff/src/rules/pyupgrade/rules/open_alias.rs index 05f60b1ee0..05696ea9b3 100644 --- a/crates/ruff/src/rules/pyupgrade/rules/open_alias.rs +++ b/crates/ruff/src/rules/pyupgrade/rules/open_alias.rs @@ -1,10 +1,11 @@ -use rustpython_parser::ast::{ExprCall, Ranged}; +use rustpython_parser::ast::{self, Ranged}; -use ruff_diagnostics::{AutofixKind, Diagnostic, DiagnosticKind, Edit, Fix, Violation}; +use ruff_diagnostics::{AutofixKind, Diagnostic, Edit, Fix, Violation}; use ruff_macros::{derive_message_formats, violation}; -use crate::checkers::ast::{Checker, RuleContext}; -use crate::registry::AsRule; +use crate::checkers::ast::traits::AstAnalyzer; +use crate::checkers::ast::RuleContext; +use crate::registry::{AsRule, Rule}; #[violation] pub struct OpenAlias; @@ -22,23 +23,33 @@ impl Violation for OpenAlias { } } +impl AstAnalyzer for OpenAlias { + fn rule() -> Rule { + Rule::OpenAlias + } + + fn run(diagnostics: &mut Vec, context: &RuleContext, node: &ast::ExprCall) { + open_alias(diagnostics, context, node); + } +} + /// UP020 pub(crate) fn open_alias( diagnostics: &mut Vec, - checker: &RuleContext, - ExprCall { func, range, .. }: &ExprCall, + context: &RuleContext, + ast::ExprCall { func, range, .. }: &ast::ExprCall, ) { - if checker + if context .ctx .resolve_call_path(func) .map_or(false, |call_path| call_path.as_slice() == ["io", "open"]) { - let fixable = checker + let fixable = context .ctx .find_binding("open") .map_or(true, |binding| binding.kind.is_builtin()); let mut diagnostic = Diagnostic::new(OpenAlias, *range); - if fixable && checker.patch(diagnostic.kind.rule()) { + if fixable && context.patch(diagnostic.kind.rule()) { #[allow(deprecated)] diagnostic.set_fix(Fix::unspecified(Edit::range_replacement( "open".to_string(), diff --git a/crates/ruff/src/rules/pyupgrade/rules/os_error_alias.rs b/crates/ruff/src/rules/pyupgrade/rules/os_error_alias.rs index 810805d906..4fed54057b 100644 --- a/crates/ruff/src/rules/pyupgrade/rules/os_error_alias.rs +++ b/crates/ruff/src/rules/pyupgrade/rules/os_error_alias.rs @@ -1,13 +1,14 @@ use ruff_text_size::TextRange; -use rustpython_parser::ast::{self, Excepthandler, Expr, ExprCall, ExprContext, Ranged}; +use rustpython_parser::ast::{self, Excepthandler, Expr, ExprContext, Ranged}; use ruff_diagnostics::{AlwaysAutofixableViolation, Diagnostic, Edit, Fix}; use ruff_macros::{derive_message_formats, violation}; use ruff_python_ast::call_path::compose_call_path; use ruff_python_semantic::context::Context; +use crate::checkers::ast::traits::AstAnalyzer; use crate::checkers::ast::{Checker, RuleContext}; -use crate::registry::AsRule; +use crate::registry::{AsRule, Rule}; #[violation] pub struct OSErrorAlias { @@ -29,6 +30,16 @@ impl AlwaysAutofixableViolation for OSErrorAlias { } } +impl AstAnalyzer for OSErrorAlias { + fn rule() -> Rule { + Rule::OSErrorAlias + } + + fn run(diagnostics: &mut Vec, checker: &RuleContext, node: &ast::ExprCall) { + os_error_alias_call(diagnostics, checker, node); + } +} + const ALIASES: &[(&str, &str)] = &[ ("", "EnvironmentError"), ("", "IOError"), @@ -176,9 +187,9 @@ pub(crate) fn os_error_alias_handlers(checker: &mut Checker, handlers: &[Excepth pub(crate) fn os_error_alias_call( diagnostics: &mut Vec, checker: &RuleContext, - ExprCall { func, .. }: &ExprCall, + ast::ExprCall { func, .. }: &ast::ExprCall, ) { - if is_alias(&checker.ctx, func) { + if is_alias(checker.ctx, func) { diagnostics.push(immutable_atom_diagnostic(checker, func)); } } diff --git a/crates/ruff/src/rules/pyupgrade/rules/redundant_open_modes.rs b/crates/ruff/src/rules/pyupgrade/rules/redundant_open_modes.rs index 3b632c5ea3..7a45c9b5fe 100644 --- a/crates/ruff/src/rules/pyupgrade/rules/redundant_open_modes.rs +++ b/crates/ruff/src/rules/pyupgrade/rules/redundant_open_modes.rs @@ -2,7 +2,7 @@ use std::str::FromStr; use anyhow::{anyhow, Result}; use ruff_text_size::TextSize; -use rustpython_parser::ast::{self, Constant, Expr, ExprCall, Keyword, Ranged}; +use rustpython_parser::ast::{self, Constant, Expr, Keyword, Ranged}; use rustpython_parser::{lexer, Mode, Tok}; use ruff_diagnostics::{AlwaysAutofixableViolation, Diagnostic, Edit, Fix}; @@ -10,7 +10,8 @@ use ruff_macros::{derive_message_formats, violation}; use ruff_python_ast::helpers::find_keyword; use ruff_python_ast::source_code::Locator; -use crate::checkers::ast::{Checker, RuleContext}; +use crate::checkers::ast::traits::AstAnalyzer; +use crate::checkers::ast::RuleContext; use crate::registry::Rule; #[violation] @@ -41,6 +42,16 @@ impl AlwaysAutofixableViolation for RedundantOpenModes { } } +impl AstAnalyzer for RedundantOpenModes { + fn rule() -> Rule { + Rule::RedundantOpenModes + } + + fn run(diagnostics: &mut Vec, checker: &RuleContext, node: &ast::ExprCall) { + redundant_open_modes(diagnostics, checker, node); + } +} + const OPEN_FUNC_NAME: &str = "open"; const MODE_KEYWORD_ARGUMENT: &str = "mode"; @@ -87,12 +98,12 @@ impl OpenMode { } fn match_open( - ExprCall { + ast::ExprCall { func, args, keywords, range: _, - }: &ExprCall, + }: &ast::ExprCall, ) -> (Option<&Expr>, Vec) { if matches!(func.as_ref(), Expr::Name(ast::ExprName {id, ..}) if id == OPEN_FUNC_NAME) { // Return the "open mode" parameter and keywords. @@ -103,7 +114,7 @@ fn match_open( } fn create_check( - expr: &ExprCall, + expr: &ast::ExprCall, mode_param: &Expr, replacement_value: Option<&str>, locator: &Locator, @@ -130,7 +141,11 @@ fn create_check( diagnostic } -fn create_remove_param_fix(locator: &Locator, expr: &ExprCall, mode_param: &Expr) -> Result { +fn create_remove_param_fix( + locator: &Locator, + expr: &ast::ExprCall, + mode_param: &Expr, +) -> Result { let content = locator.slice(expr.range()); // Find the last comma before mode_param and create a deletion fix // starting from the comma and ending after mode_param. @@ -174,7 +189,7 @@ fn create_remove_param_fix(locator: &Locator, expr: &ExprCall, mode_param: &Expr pub(crate) fn redundant_open_modes( diagnostics: &mut Vec, checker: &RuleContext, - expr: &ExprCall, + expr: &ast::ExprCall, ) { // If `open` has been rebound, skip this check entirely. if !checker.ctx.is_builtin(OPEN_FUNC_NAME) { diff --git a/crates/ruff/src/rules/pyupgrade/rules/replace_stdout_stderr.rs b/crates/ruff/src/rules/pyupgrade/rules/replace_stdout_stderr.rs index 7838131994..937d5a561d 100644 --- a/crates/ruff/src/rules/pyupgrade/rules/replace_stdout_stderr.rs +++ b/crates/ruff/src/rules/pyupgrade/rules/replace_stdout_stderr.rs @@ -1,5 +1,5 @@ use anyhow::Result; -use rustpython_parser::ast::{Expr, ExprCall, Keyword, Ranged}; +use rustpython_parser::ast::{self, Expr, Keyword, Ranged}; use ruff_diagnostics::{AlwaysAutofixableViolation, Diagnostic, Edit, Fix}; use ruff_macros::{derive_message_formats, violation}; @@ -7,8 +7,9 @@ use ruff_python_ast::helpers::find_keyword; use ruff_python_ast::source_code::Locator; use crate::autofix::actions::remove_argument; -use crate::checkers::ast::{Checker, RuleContext}; -use crate::registry::AsRule; +use crate::checkers::ast::traits::AstAnalyzer; +use crate::checkers::ast::RuleContext; +use crate::registry::{AsRule, Rule}; #[violation] pub struct ReplaceStdoutStderr; @@ -24,6 +25,16 @@ impl AlwaysAutofixableViolation for ReplaceStdoutStderr { } } +impl AstAnalyzer for ReplaceStdoutStderr { + fn rule() -> Rule { + Rule::ReplaceStdoutStderr + } + + fn run(diagnostics: &mut Vec, checker: &RuleContext, node: &ast::ExprCall) { + replace_stdout_stderr(diagnostics, checker, node); + } +} + /// Generate a [`Edit`] for a `stdout` and `stderr` [`Keyword`] pair. fn generate_fix( locator: &Locator, @@ -56,12 +67,12 @@ fn generate_fix( pub(crate) fn replace_stdout_stderr( diagnostics: &mut Vec, checker: &RuleContext, - ExprCall { + ast::ExprCall { func, args, keywords, range, - }: &ExprCall, + }: &ast::ExprCall, ) { if checker .ctx diff --git a/crates/ruff/src/rules/pyupgrade/rules/replace_universal_newlines.rs b/crates/ruff/src/rules/pyupgrade/rules/replace_universal_newlines.rs index 64ef11270e..7d8dbfcd77 100644 --- a/crates/ruff/src/rules/pyupgrade/rules/replace_universal_newlines.rs +++ b/crates/ruff/src/rules/pyupgrade/rules/replace_universal_newlines.rs @@ -1,12 +1,13 @@ use ruff_text_size::{TextLen, TextRange}; -use rustpython_parser::ast::{ExprCall, Ranged}; +use rustpython_parser::ast::{self, Ranged}; use ruff_diagnostics::{AlwaysAutofixableViolation, Diagnostic, Edit, Fix}; use ruff_macros::{derive_message_formats, violation}; use ruff_python_ast::helpers::find_keyword; -use crate::checkers::ast::{Checker, RuleContext}; -use crate::registry::AsRule; +use crate::checkers::ast::traits::AstAnalyzer; +use crate::checkers::ast::RuleContext; +use crate::registry::{AsRule, Rule}; #[violation] pub struct ReplaceUniversalNewlines; @@ -22,11 +23,21 @@ impl AlwaysAutofixableViolation for ReplaceUniversalNewlines { } } +impl AstAnalyzer for ReplaceUniversalNewlines { + fn rule() -> Rule { + Rule::ReplaceUniversalNewlines + } + + fn run(diagnostics: &mut Vec, checker: &RuleContext, node: &ast::ExprCall) { + replace_universal_newlines(diagnostics, checker, node); + } +} + /// UP021 pub(crate) fn replace_universal_newlines( diagnostics: &mut Vec, checker: &RuleContext, - ExprCall { func, keywords, .. }: &ExprCall, + ast::ExprCall { func, keywords, .. }: &ast::ExprCall, ) { if checker .ctx diff --git a/crates/ruff/src/rules/pyupgrade/rules/super_call_with_parameters.rs b/crates/ruff/src/rules/pyupgrade/rules/super_call_with_parameters.rs index 04e9663524..5244ff246c 100644 --- a/crates/ruff/src/rules/pyupgrade/rules/super_call_with_parameters.rs +++ b/crates/ruff/src/rules/pyupgrade/rules/super_call_with_parameters.rs @@ -1,11 +1,12 @@ -use rustpython_parser::ast::{self, Arg, Expr, ExprCall, Stmt}; +use rustpython_parser::ast::{self, Arg, Expr, Stmt}; use ruff_diagnostics::{AlwaysAutofixableViolation, Diagnostic, Fix}; use ruff_macros::{derive_message_formats, violation}; use ruff_python_semantic::scope::ScopeKind; -use crate::checkers::ast::{Checker, RuleContext}; -use crate::registry::AsRule; +use crate::checkers::ast::traits::AstAnalyzer; +use crate::checkers::ast::RuleContext; +use crate::registry::{AsRule, Rule}; use crate::rules::pyupgrade::fixes; #[violation] @@ -22,6 +23,16 @@ impl AlwaysAutofixableViolation for SuperCallWithParameters { } } +impl AstAnalyzer for SuperCallWithParameters { + fn rule() -> Rule { + Rule::SuperCallWithParameters + } + + fn run(diagnostics: &mut Vec, checker: &RuleContext, node: &ast::ExprCall) { + super_call_with_parameters(diagnostics, checker, node); + } +} + /// Returns `true` if a call is an argumented `super` invocation. fn is_super_call_with_arguments(func: &Expr, args: &[Expr]) -> bool { if let Expr::Name(ast::ExprName { id, .. }) = func { @@ -35,9 +46,9 @@ fn is_super_call_with_arguments(func: &Expr, args: &[Expr]) -> bool { pub(crate) fn super_call_with_parameters( diagnostics: &mut Vec, checker: &RuleContext, - ExprCall { + ast::ExprCall { func, args, range, .. - }: &ExprCall, + }: &ast::ExprCall, ) { // Only bother going through the super check at all if we're in a `super` call. // (We check this in `super_args` too, so this is just an optimization.) diff --git a/crates/ruff/src/rules/pyupgrade/rules/type_of_primitive.rs b/crates/ruff/src/rules/pyupgrade/rules/type_of_primitive.rs index b443f62172..fb0767dd32 100644 --- a/crates/ruff/src/rules/pyupgrade/rules/type_of_primitive.rs +++ b/crates/ruff/src/rules/pyupgrade/rules/type_of_primitive.rs @@ -3,9 +3,9 @@ use rustpython_parser::ast::{self, Expr}; use ruff_diagnostics::{AlwaysAutofixableViolation, Diagnostic, Edit, Fix}; use ruff_macros::{derive_message_formats, violation}; -use crate::checkers::ast::traits::AstRule; +use crate::checkers::ast::traits::AstAnalyzer; use crate::checkers::ast::RuleContext; -use crate::registry::AsRule; +use crate::registry::{AsRule, Rule}; use super::super::types::Primitive; @@ -27,9 +27,13 @@ impl AlwaysAutofixableViolation for TypeOfPrimitive { } } -impl AstRule for TypeOfPrimitive { +impl AstAnalyzer for TypeOfPrimitive { + fn rule() -> Rule { + Rule::TypeOfPrimitive + } + fn run(diagnostics: &mut Vec, checker: &RuleContext, node: &ast::ExprCall) { - type_of_primitive(diagnostics, checker, node) + type_of_primitive(diagnostics, checker, node); } } diff --git a/crates/ruff/src/rules/pyupgrade/rules/unnecessary_encode_utf8.rs b/crates/ruff/src/rules/pyupgrade/rules/unnecessary_encode_utf8.rs index 1f789c2e08..4dd4617b9a 100644 --- a/crates/ruff/src/rules/pyupgrade/rules/unnecessary_encode_utf8.rs +++ b/crates/ruff/src/rules/pyupgrade/rules/unnecessary_encode_utf8.rs @@ -1,5 +1,5 @@ use ruff_text_size::TextRange; -use rustpython_parser::ast::{self, Constant, Expr, ExprCall, Keyword, Ranged}; +use rustpython_parser::ast::{self, Constant, Expr, Keyword, Ranged}; use rustpython_parser::{lexer, Mode, Tok}; use ruff_diagnostics::{AlwaysAutofixableViolation, Diagnostic, Edit, Fix}; @@ -7,7 +7,8 @@ use ruff_macros::{derive_message_formats, violation}; use ruff_python_ast::source_code::Locator; use crate::autofix::actions::remove_argument; -use crate::checkers::ast::{Checker, RuleContext}; +use crate::checkers::ast::traits::AstAnalyzer; +use crate::checkers::ast::RuleContext; use crate::registry::Rule; #[derive(Debug, PartialEq, Eq)] @@ -35,6 +36,16 @@ impl AlwaysAutofixableViolation for UnnecessaryEncodeUTF8 { } } +impl AstAnalyzer for UnnecessaryEncodeUTF8 { + fn rule() -> Rule { + Rule::UnnecessaryEncodeUTF8 + } + + fn run(diagnostics: &mut Vec, checker: &RuleContext, node: &ast::ExprCall) { + unnecessary_encode_utf8(diagnostics, checker, node); + } +} + const UTF8_LITERALS: &[&str] = &["utf-8", "utf8", "utf_8", "u8", "utf", "cp65001"]; fn match_encoded_variable(func: &Expr) -> Option<&Expr> { @@ -132,13 +143,12 @@ fn replace_with_bytes_literal(locator: &Locator, range: TextRange) -> Fix { pub(crate) fn unnecessary_encode_utf8( diagnostics: &mut Vec, checker: &RuleContext, - ExprCall { + ast::ExprCall { func, args, range, keywords, - .. - }: &ExprCall, + }: &ast::ExprCall, ) { let Some(variable) = match_encoded_variable(func) else { return; diff --git a/crates/ruff/src/rules/pyupgrade/rules/use_pep604_isinstance.rs b/crates/ruff/src/rules/pyupgrade/rules/use_pep604_isinstance.rs index 033e2e9a04..07edbc054b 100644 --- a/crates/ruff/src/rules/pyupgrade/rules/use_pep604_isinstance.rs +++ b/crates/ruff/src/rules/pyupgrade/rules/use_pep604_isinstance.rs @@ -1,13 +1,14 @@ use std::fmt; use ruff_text_size::TextRange; -use rustpython_parser::ast::{self, Expr, ExprCall, Operator, Ranged}; +use rustpython_parser::ast::{self, Expr, Operator, Ranged}; use ruff_diagnostics::{AlwaysAutofixableViolation, Diagnostic, Edit, Fix}; use ruff_macros::{derive_message_formats, violation}; -use crate::checkers::ast::{Checker, RuleContext}; -use crate::registry::AsRule; +use crate::checkers::ast::traits::AstAnalyzer; +use crate::checkers::ast::RuleContext; +use crate::registry::{AsRule, Rule}; #[derive(Debug, PartialEq, Eq, Copy, Clone)] pub(crate) enum CallKind { @@ -50,6 +51,16 @@ impl AlwaysAutofixableViolation for NonPEP604Isinstance { } } +impl AstAnalyzer for NonPEP604Isinstance { + fn rule() -> Rule { + Rule::NonPEP604Isinstance + } + + fn run(diagnostics: &mut Vec, checker: &RuleContext, node: &ast::ExprCall) { + use_pep604_isinstance(diagnostics, checker, node); + } +} + fn union(elts: &[Expr]) -> Expr { if elts.len() == 1 { elts[0].clone() @@ -67,9 +78,9 @@ fn union(elts: &[Expr]) -> Expr { pub(crate) fn use_pep604_isinstance( diagnostics: &mut Vec, checker: &RuleContext, - ExprCall { + ast::ExprCall { func, args, range, .. - }: &ExprCall, + }: &ast::ExprCall, ) { if let Expr::Name(ast::ExprName { id, .. }) = func.as_ref() { let Some(kind) = CallKind::from_name(id) else {