diff --git a/crates/ruff/src/autofix/codemods.rs b/crates/ruff/src/autofix/codemods.rs new file mode 100644 index 0000000000..0797818407 --- /dev/null +++ b/crates/ruff/src/autofix/codemods.rs @@ -0,0 +1,125 @@ +//! Interface for editing code snippets. These functions take statements or expressions as input, +//! and return the modified code snippet as output. +use anyhow::{bail, Result}; +use libcst_native::{ + Codegen, CodegenState, ImportNames, ParenthesizableWhitespace, SmallStatement, Statement, +}; +use rustpython_parser::ast::{Ranged, Stmt}; + +use ruff_python_ast::source_code::{Locator, Stylist}; + +use crate::cst::helpers::compose_module_path; +use crate::cst::matchers::match_statement; + +/// Given an import statement, remove any imports that are specified in the `imports` iterator. +/// +/// Returns `Ok(None)` if the statement is empty after removing the imports. +pub(crate) fn remove_imports<'a>( + imports: impl Iterator, + stmt: &Stmt, + locator: &Locator, + stylist: &Stylist, +) -> Result> { + let module_text = locator.slice(stmt.range()); + let mut tree = match_statement(module_text)?; + + let Statement::Simple(body) = &mut tree else { + bail!("Expected Statement::Simple"); + }; + + let (aliases, import_module) = match body.body.first_mut() { + Some(SmallStatement::Import(import_body)) => (&mut import_body.names, None), + Some(SmallStatement::ImportFrom(import_body)) => { + if let ImportNames::Aliases(names) = &mut import_body.names { + ( + names, + Some((&import_body.relative, import_body.module.as_ref())), + ) + } else if let ImportNames::Star(..) = &import_body.names { + // Special-case: if the import is a `from ... import *`, then we delete the + // entire statement. + let mut found_star = false; + for import in imports { + let full_name = match import_body.module.as_ref() { + Some(module_name) => format!("{}.*", compose_module_path(module_name)), + None => "*".to_string(), + }; + if import == full_name { + found_star = true; + } else { + bail!("Expected \"*\" for unused import (got: \"{}\")", import); + } + } + if !found_star { + bail!("Expected \'*\' for unused import"); + } + return Ok(None); + } else { + bail!("Expected: ImportNames::Aliases | ImportNames::Star"); + } + } + _ => bail!("Expected: SmallStatement::ImportFrom | SmallStatement::Import"), + }; + + // Preserve the trailing comma (or not) from the last entry. + let trailing_comma = aliases.last().and_then(|alias| alias.comma.clone()); + + for import in imports { + let alias_index = aliases.iter().position(|alias| { + let full_name = match import_module { + Some((relative, module)) => { + let module = module.map(compose_module_path); + let member = compose_module_path(&alias.name); + let mut full_name = String::with_capacity( + relative.len() + module.as_ref().map_or(0, String::len) + member.len() + 1, + ); + for _ in 0..relative.len() { + full_name.push('.'); + } + if let Some(module) = module { + full_name.push_str(&module); + full_name.push('.'); + } + full_name.push_str(&member); + full_name + } + None => compose_module_path(&alias.name), + }; + full_name == import + }); + + if let Some(index) = alias_index { + aliases.remove(index); + } + } + + // But avoid destroying any trailing comments. + if let Some(alias) = aliases.last_mut() { + let has_comment = if let Some(comma) = &alias.comma { + match &comma.whitespace_after { + ParenthesizableWhitespace::SimpleWhitespace(_) => false, + ParenthesizableWhitespace::ParenthesizedWhitespace(whitespace) => { + whitespace.first_line.comment.is_some() + } + } + } else { + false + }; + if !has_comment { + alias.comma = trailing_comma; + } + } + + if aliases.is_empty() { + return Ok(None); + } + + let mut state = CodegenState { + default_newline: &stylist.line_ending(), + default_indent: stylist.indentation(), + ..CodegenState::default() + }; + tree.codegen(&mut state); + + Ok(Some(state.to_string())) +} diff --git a/crates/ruff/src/autofix/actions.rs b/crates/ruff/src/autofix/edits.rs similarity index 75% rename from crates/ruff/src/autofix/actions.rs rename to crates/ruff/src/autofix/edits.rs index 8166987395..c9a460b341 100644 --- a/crates/ruff/src/autofix/actions.rs +++ b/crates/ruff/src/autofix/edits.rs @@ -1,9 +1,6 @@ //! Interface for generating autofix edits from higher-level actions (e.g., "remove an argument"). use anyhow::{bail, Result}; use itertools::Itertools; -use libcst_native::{ - Codegen, CodegenState, ImportNames, ParenthesizableWhitespace, SmallStatement, Statement, -}; use ruff_text_size::{TextLen, TextRange, TextSize}; use rustpython_parser::ast::{self, Excepthandler, Expr, Keyword, Ranged, Stmt}; use rustpython_parser::{lexer, Mode, Tok}; @@ -13,8 +10,174 @@ use ruff_newlines::NewlineWithTrailingNewline; use ruff_python_ast::helpers; use ruff_python_ast::source_code::{Indexer, Locator, Stylist}; -use crate::cst::helpers::compose_module_path; -use crate::cst::matchers::match_statement; +use crate::autofix::codemods; + +/// Return the `Fix` to use when deleting a `Stmt`. +/// +/// In some cases, this is as simple as deleting the `Range` of the `Stmt` +/// itself. However, there are a few exceptions: +/// - If the `Stmt` is _not_ the terminal statement in a multi-statement line, +/// we need to delete up to the start of the next statement (and avoid +/// deleting any content that precedes the statement). +/// - If the `Stmt` is the terminal statement in a multi-statement line, we need +/// to avoid deleting any content that precedes the statement. +/// - If the `Stmt` has no trailing and leading content, then it's convenient to +/// remove the entire start and end lines. +/// - If the `Stmt` is the last statement in its parent body, replace it with a +/// `pass` instead. +pub(crate) fn delete_stmt( + stmt: &Stmt, + parent: Option<&Stmt>, + deleted: &[&Stmt], + locator: &Locator, + indexer: &Indexer, + stylist: &Stylist, +) -> Result { + if parent + .map(|parent| is_lone_child(stmt, parent, deleted)) + .map_or(Ok(None), |v| v.map(Some))? + .unwrap_or_default() + { + // If removing this node would lead to an invalid syntax tree, replace + // it with a `pass`. + Ok(Edit::range_replacement("pass".to_string(), stmt.range())) + } else { + Ok(if let Some(semicolon) = trailing_semicolon(stmt, locator) { + let next = next_stmt_break(semicolon, locator); + Edit::deletion(stmt.start(), next) + } else if helpers::has_leading_content(stmt, locator) { + Edit::range_deletion(stmt.range()) + } else if helpers::preceded_by_continuation(stmt, indexer, locator) { + if is_end_of_file(stmt, locator) && locator.is_at_start_of_line(stmt.start()) { + // Special-case: a file can't end in a continuation. + Edit::range_replacement(stylist.line_ending().to_string(), stmt.range()) + } else { + Edit::range_deletion(stmt.range()) + } + } else { + let range = locator.full_lines_range(stmt.range()); + Edit::range_deletion(range) + }) + } +} + +/// Generate a `Fix` to remove any unused imports from an `import` statement. +pub(crate) fn remove_unused_imports<'a>( + unused_imports: impl Iterator, + stmt: &Stmt, + parent: Option<&Stmt>, + deleted: &[&Stmt], + locator: &Locator, + indexer: &Indexer, + stylist: &Stylist, +) -> Result { + match codemods::remove_imports(unused_imports, stmt, locator, stylist)? { + None => delete_stmt(stmt, parent, deleted, locator, indexer, stylist), + Some(content) => Ok(Edit::range_replacement(content, stmt.range())), + } +} + +/// Generic function to remove arguments or keyword arguments in function +/// calls and class definitions. (For classes `args` should be considered +/// `bases`) +/// +/// Supports the removal of parentheses when this is the only (kw)arg left. +/// For this behavior, set `remove_parentheses` to `true`. +pub(crate) fn remove_argument( + locator: &Locator, + call_at: TextSize, + expr_range: TextRange, + args: &[Expr], + keywords: &[Keyword], + remove_parentheses: bool, +) -> Result { + // TODO(sbrugman): Preserve trailing comments. + let contents = locator.after(call_at); + + let mut fix_start = None; + let mut fix_end = None; + + let n_arguments = keywords.len() + args.len(); + if n_arguments == 0 { + bail!("No arguments or keywords to remove"); + } + + if n_arguments == 1 { + // Case 1: there is only one argument. + let mut count: usize = 0; + for (tok, range) in lexer::lex_starts_at(contents, Mode::Module, call_at).flatten() { + if matches!(tok, Tok::Lpar) { + if count == 0 { + fix_start = Some(if remove_parentheses { + range.start() + } else { + range.start() + TextSize::from(1) + }); + } + count += 1; + } + + if matches!(tok, Tok::Rpar) { + count -= 1; + if count == 0 { + fix_end = Some(if remove_parentheses { + range.end() + } else { + range.end() - TextSize::from(1) + }); + break; + } + } + } + } else if args + .iter() + .map(Expr::start) + .chain(keywords.iter().map(Keyword::start)) + .any(|location| location > expr_range.start()) + { + // Case 2: argument or keyword is _not_ the last node. + let mut seen_comma = false; + for (tok, range) in lexer::lex_starts_at(contents, Mode::Module, call_at).flatten() { + if seen_comma { + if matches!(tok, Tok::NonLogicalNewline) { + // Also delete any non-logical newlines after the comma. + continue; + } + fix_end = Some(if matches!(tok, Tok::Newline) { + range.end() + } else { + range.start() + }); + break; + } + if range.start() == expr_range.start() { + fix_start = Some(range.start()); + } + if fix_start.is_some() && matches!(tok, Tok::Comma) { + seen_comma = true; + } + } + } else { + // Case 3: argument or keyword is the last node, so we have to find the last + // comma in the stmt. + for (tok, range) in lexer::lex_starts_at(contents, Mode::Module, call_at).flatten() { + if range.start() == expr_range.start() { + fix_end = Some(expr_range.end()); + break; + } + if matches!(tok, Tok::Comma) { + fix_start = Some(range.start()); + } + } + } + + match (fix_start, fix_end) { + (Some(start), Some(end)) => Ok(Edit::deletion(start, end)), + _ => { + bail!("No fix could be constructed") + } + } +} /// Determine if a body contains only a single statement, taking into account /// deleted. @@ -152,274 +315,6 @@ fn is_end_of_file(stmt: &Stmt, locator: &Locator) -> bool { stmt.end() == locator.contents().text_len() } -/// Return the `Fix` to use when deleting a `Stmt`. -/// -/// In some cases, this is as simple as deleting the `Range` of the `Stmt` -/// itself. However, there are a few exceptions: -/// - If the `Stmt` is _not_ the terminal statement in a multi-statement line, -/// we need to delete up to the start of the next statement (and avoid -/// deleting any content that precedes the statement). -/// - If the `Stmt` is the terminal statement in a multi-statement line, we need -/// to avoid deleting any content that precedes the statement. -/// - If the `Stmt` has no trailing and leading content, then it's convenient to -/// remove the entire start and end lines. -/// - If the `Stmt` is the last statement in its parent body, replace it with a -/// `pass` instead. -pub(crate) fn delete_stmt( - stmt: &Stmt, - parent: Option<&Stmt>, - deleted: &[&Stmt], - locator: &Locator, - indexer: &Indexer, - stylist: &Stylist, -) -> Result { - if parent - .map(|parent| is_lone_child(stmt, parent, deleted)) - .map_or(Ok(None), |v| v.map(Some))? - .unwrap_or_default() - { - // If removing this node would lead to an invalid syntax tree, replace - // it with a `pass`. - Ok(Edit::range_replacement("pass".to_string(), stmt.range())) - } else { - Ok(if let Some(semicolon) = trailing_semicolon(stmt, locator) { - let next = next_stmt_break(semicolon, locator); - Edit::deletion(stmt.start(), next) - } else if helpers::has_leading_content(stmt, locator) { - Edit::range_deletion(stmt.range()) - } else if helpers::preceded_by_continuation(stmt, indexer, locator) { - if is_end_of_file(stmt, locator) && locator.is_at_start_of_line(stmt.start()) { - // Special-case: a file can't end in a continuation. - Edit::range_replacement(stylist.line_ending().to_string(), stmt.range()) - } else { - Edit::range_deletion(stmt.range()) - } - } else { - let range = locator.full_lines_range(stmt.range()); - Edit::range_deletion(range) - }) - } -} - -/// Generate a `Fix` to remove any unused imports from an `import` statement. -pub(crate) fn remove_unused_imports<'a>( - unused_imports: impl Iterator, - stmt: &Stmt, - parent: Option<&Stmt>, - deleted: &[&Stmt], - locator: &Locator, - indexer: &Indexer, - stylist: &Stylist, -) -> Result { - let module_text = locator.slice(stmt.range()); - let mut tree = match_statement(module_text)?; - - let Statement::Simple(body) = &mut tree else { - bail!("Expected Statement::Simple"); - }; - - let (aliases, import_module) = match body.body.first_mut() { - Some(SmallStatement::Import(import_body)) => (&mut import_body.names, None), - Some(SmallStatement::ImportFrom(import_body)) => { - if let ImportNames::Aliases(names) = &mut import_body.names { - ( - names, - Some((&import_body.relative, import_body.module.as_ref())), - ) - } else if let ImportNames::Star(..) = &import_body.names { - // Special-case: if the import is a `from ... import *`, then we delete the - // entire statement. - let mut found_star = false; - for unused_import in unused_imports { - let full_name = match import_body.module.as_ref() { - Some(module_name) => format!("{}.*", compose_module_path(module_name)), - None => "*".to_string(), - }; - if unused_import == full_name { - found_star = true; - } else { - bail!( - "Expected \"*\" for unused import (got: \"{}\")", - unused_import - ); - } - } - if !found_star { - bail!("Expected \'*\' for unused import"); - } - return delete_stmt(stmt, parent, deleted, locator, indexer, stylist); - } else { - bail!("Expected: ImportNames::Aliases | ImportNames::Star"); - } - } - _ => bail!("Expected: SmallStatement::ImportFrom | SmallStatement::Import"), - }; - - // Preserve the trailing comma (or not) from the last entry. - let trailing_comma = aliases.last().and_then(|alias| alias.comma.clone()); - - for unused_import in unused_imports { - let alias_index = aliases.iter().position(|alias| { - let full_name = match import_module { - Some((relative, module)) => { - let module = module.map(compose_module_path); - let member = compose_module_path(&alias.name); - let mut full_name = String::with_capacity( - relative.len() + module.as_ref().map_or(0, String::len) + member.len() + 1, - ); - for _ in 0..relative.len() { - full_name.push('.'); - } - if let Some(module) = module { - full_name.push_str(&module); - full_name.push('.'); - } - full_name.push_str(&member); - full_name - } - None => compose_module_path(&alias.name), - }; - full_name == unused_import - }); - - if let Some(index) = alias_index { - aliases.remove(index); - } - } - - // But avoid destroying any trailing comments. - if let Some(alias) = aliases.last_mut() { - let has_comment = if let Some(comma) = &alias.comma { - match &comma.whitespace_after { - ParenthesizableWhitespace::SimpleWhitespace(_) => false, - ParenthesizableWhitespace::ParenthesizedWhitespace(whitespace) => { - whitespace.first_line.comment.is_some() - } - } - } else { - false - }; - if !has_comment { - alias.comma = trailing_comma; - } - } - - if aliases.is_empty() { - delete_stmt(stmt, parent, deleted, locator, indexer, stylist) - } else { - let mut state = CodegenState { - default_newline: &stylist.line_ending(), - default_indent: stylist.indentation(), - ..CodegenState::default() - }; - tree.codegen(&mut state); - - Ok(Edit::range_replacement(state.to_string(), stmt.range())) - } -} - -/// Generic function to remove arguments or keyword arguments in function -/// calls and class definitions. (For classes `args` should be considered -/// `bases`) -/// -/// Supports the removal of parentheses when this is the only (kw)arg left. -/// For this behavior, set `remove_parentheses` to `true`. -pub(crate) fn remove_argument( - locator: &Locator, - call_at: TextSize, - expr_range: TextRange, - args: &[Expr], - keywords: &[Keyword], - remove_parentheses: bool, -) -> Result { - // TODO(sbrugman): Preserve trailing comments. - let contents = locator.after(call_at); - - let mut fix_start = None; - let mut fix_end = None; - - let n_arguments = keywords.len() + args.len(); - if n_arguments == 0 { - bail!("No arguments or keywords to remove"); - } - - if n_arguments == 1 { - // Case 1: there is only one argument. - let mut count: usize = 0; - for (tok, range) in lexer::lex_starts_at(contents, Mode::Module, call_at).flatten() { - if matches!(tok, Tok::Lpar) { - if count == 0 { - fix_start = Some(if remove_parentheses { - range.start() - } else { - range.start() + TextSize::from(1) - }); - } - count += 1; - } - - if matches!(tok, Tok::Rpar) { - count -= 1; - if count == 0 { - fix_end = Some(if remove_parentheses { - range.end() - } else { - range.end() - TextSize::from(1) - }); - break; - } - } - } - } else if args - .iter() - .map(Expr::start) - .chain(keywords.iter().map(Keyword::start)) - .any(|location| location > expr_range.start()) - { - // Case 2: argument or keyword is _not_ the last node. - let mut seen_comma = false; - for (tok, range) in lexer::lex_starts_at(contents, Mode::Module, call_at).flatten() { - if seen_comma { - if matches!(tok, Tok::NonLogicalNewline) { - // Also delete any non-logical newlines after the comma. - continue; - } - fix_end = Some(if matches!(tok, Tok::Newline) { - range.end() - } else { - range.start() - }); - break; - } - if range.start() == expr_range.start() { - fix_start = Some(range.start()); - } - if fix_start.is_some() && matches!(tok, Tok::Comma) { - seen_comma = true; - } - } - } else { - // Case 3: argument or keyword is the last node, so we have to find the last - // comma in the stmt. - for (tok, range) in lexer::lex_starts_at(contents, Mode::Module, call_at).flatten() { - if range.start() == expr_range.start() { - fix_end = Some(expr_range.end()); - break; - } - if matches!(tok, Tok::Comma) { - fix_start = Some(range.start()); - } - } - } - - match (fix_start, fix_end) { - (Some(start), Some(end)) => Ok(Edit::deletion(start, end)), - _ => { - bail!("No fix could be constructed") - } - } -} - #[cfg(test)] mod tests { use anyhow::Result; @@ -429,7 +324,7 @@ mod tests { use ruff_python_ast::source_code::Locator; - use crate::autofix::actions::{next_stmt_break, trailing_semicolon}; + use crate::autofix::edits::{next_stmt_break, trailing_semicolon}; #[test] fn find_semicolon() -> Result<()> { diff --git a/crates/ruff/src/autofix/mod.rs b/crates/ruff/src/autofix/mod.rs index a82ba1659d..04e4714bb0 100644 --- a/crates/ruff/src/autofix/mod.rs +++ b/crates/ruff/src/autofix/mod.rs @@ -10,7 +10,8 @@ use ruff_python_ast::source_code::Locator; use crate::linter::FixTable; use crate::registry::{AsRule, Rule}; -pub(crate) mod actions; +pub(crate) mod codemods; +pub(crate) mod edits; /// Auto-fix errors in a file, and write the fixed source code to disk. pub(crate) fn fix_file( diff --git a/crates/ruff/src/checkers/ast/mod.rs b/crates/ruff/src/checkers/ast/mod.rs index 328fcc8b2f..8fb77b4e96 100644 --- a/crates/ruff/src/checkers/ast/mod.rs +++ b/crates/ruff/src/checkers/ast/mod.rs @@ -5223,7 +5223,7 @@ impl<'a> Checker<'a> { let fix = if !in_init && !in_except_handler && self.patch(Rule::UnusedImport) { let deleted: Vec<&Stmt> = self.deletions.iter().map(Into::into).collect(); - match autofix::actions::remove_unused_imports( + match autofix::edits::remove_unused_imports( unused_imports.iter().map(|(full_name, _)| *full_name), child, parent, diff --git a/crates/ruff/src/rules/flake8_pie/rules/duplicate_class_field_definition.rs b/crates/ruff/src/rules/flake8_pie/rules/duplicate_class_field_definition.rs index f75d9928d2..871c2c48cd 100644 --- a/crates/ruff/src/rules/flake8_pie/rules/duplicate_class_field_definition.rs +++ b/crates/ruff/src/rules/flake8_pie/rules/duplicate_class_field_definition.rs @@ -9,7 +9,7 @@ use ruff_macros::{derive_message_formats, violation}; use ruff_python_ast::types::RefEquality; -use crate::autofix::actions::delete_stmt; +use crate::autofix::edits::delete_stmt; use crate::checkers::ast::Checker; use crate::registry::AsRule; diff --git a/crates/ruff/src/rules/flake8_pie/rules/no_unnecessary_pass.rs b/crates/ruff/src/rules/flake8_pie/rules/no_unnecessary_pass.rs index 7b08ed371f..e309f03819 100644 --- a/crates/ruff/src/rules/flake8_pie/rules/no_unnecessary_pass.rs +++ b/crates/ruff/src/rules/flake8_pie/rules/no_unnecessary_pass.rs @@ -6,7 +6,7 @@ use ruff_macros::{derive_message_formats, violation}; use ruff_python_ast::helpers::trailing_comment_start_offset; -use crate::autofix::actions::delete_stmt; +use crate::autofix::edits::delete_stmt; use crate::checkers::ast::Checker; use crate::registry::AsRule; diff --git a/crates/ruff/src/rules/flake8_pyi/rules/ellipsis_in_non_empty_class_body.rs b/crates/ruff/src/rules/flake8_pyi/rules/ellipsis_in_non_empty_class_body.rs index 966c0fa8b5..834bd45920 100644 --- a/crates/ruff/src/rules/flake8_pyi/rules/ellipsis_in_non_empty_class_body.rs +++ b/crates/ruff/src/rules/flake8_pyi/rules/ellipsis_in_non_empty_class_body.rs @@ -4,7 +4,7 @@ use ruff_diagnostics::{AutofixKind, Diagnostic, Fix, Violation}; use ruff_macros::{derive_message_formats, violation}; use ruff_python_ast::types::RefEquality; -use crate::autofix::actions::delete_stmt; +use crate::autofix::edits::delete_stmt; use crate::checkers::ast::Checker; use crate::registry::AsRule; diff --git a/crates/ruff/src/rules/flake8_pyi/rules/pass_in_class_body.rs b/crates/ruff/src/rules/flake8_pyi/rules/pass_in_class_body.rs index 5e73f9e7f4..10e5221fca 100644 --- a/crates/ruff/src/rules/flake8_pyi/rules/pass_in_class_body.rs +++ b/crates/ruff/src/rules/flake8_pyi/rules/pass_in_class_body.rs @@ -5,7 +5,7 @@ use ruff_diagnostics::{AlwaysAutofixableViolation, Diagnostic}; use ruff_macros::{derive_message_formats, violation}; use ruff_python_ast::types::RefEquality; -use crate::autofix::actions::delete_stmt; +use crate::autofix::edits::delete_stmt; use crate::checkers::ast::Checker; use crate::registry::AsRule; diff --git a/crates/ruff/src/rules/flake8_pytest_style/rules/fixture.rs b/crates/ruff/src/rules/flake8_pytest_style/rules/fixture.rs index 78c8623225..6d019d9000 100644 --- a/crates/ruff/src/rules/flake8_pytest_style/rules/fixture.rs +++ b/crates/ruff/src/rules/flake8_pytest_style/rules/fixture.rs @@ -15,7 +15,7 @@ use ruff_python_ast::{helpers, visitor}; use ruff_python_semantic::analyze::visibility::is_abstract; use ruff_python_semantic::model::SemanticModel; -use crate::autofix::actions::remove_argument; +use crate::autofix::edits::remove_argument; use crate::checkers::ast::Checker; use crate::registry::{AsRule, Rule}; diff --git a/crates/ruff/src/rules/flake8_type_checking/rules/empty_type_checking_block.rs b/crates/ruff/src/rules/flake8_type_checking/rules/empty_type_checking_block.rs index cc8b29bb7d..1735fbbe40 100644 --- a/crates/ruff/src/rules/flake8_type_checking/rules/empty_type_checking_block.rs +++ b/crates/ruff/src/rules/flake8_type_checking/rules/empty_type_checking_block.rs @@ -5,7 +5,7 @@ use ruff_diagnostics::{AlwaysAutofixableViolation, Diagnostic, Fix}; use ruff_macros::{derive_message_formats, violation}; use ruff_python_ast::types::RefEquality; -use crate::autofix::actions::delete_stmt; +use crate::autofix::edits::delete_stmt; use crate::checkers::ast::Checker; use crate::registry::AsRule; diff --git a/crates/ruff/src/rules/pandas_vet/fixes.rs b/crates/ruff/src/rules/pandas_vet/fixes.rs index a189dbac2a..23ac1a5c3e 100644 --- a/crates/ruff/src/rules/pandas_vet/fixes.rs +++ b/crates/ruff/src/rules/pandas_vet/fixes.rs @@ -4,7 +4,7 @@ use rustpython_parser::ast::{self, Expr, Keyword, Ranged}; use ruff_diagnostics::{Edit, Fix}; use ruff_python_ast::source_code::Locator; -use crate::autofix::actions::remove_argument; +use crate::autofix::edits::remove_argument; fn match_name(expr: &Expr) -> Option<&str> { if let Expr::Call(ast::ExprCall { func, .. }) = expr { diff --git a/crates/ruff/src/rules/pyflakes/rules/unused_variable.rs b/crates/ruff/src/rules/pyflakes/rules/unused_variable.rs index f9d4ab1d39..b65ce9e019 100644 --- a/crates/ruff/src/rules/pyflakes/rules/unused_variable.rs +++ b/crates/ruff/src/rules/pyflakes/rules/unused_variable.rs @@ -11,7 +11,7 @@ use ruff_python_ast::source_code::Locator; use ruff_python_ast::types::RefEquality; use ruff_python_semantic::scope::{ScopeId, ScopeKind}; -use crate::autofix::actions::delete_stmt; +use crate::autofix::edits::delete_stmt; use crate::checkers::ast::Checker; use crate::registry::AsRule; diff --git a/crates/ruff/src/rules/pylint/rules/useless_return.rs b/crates/ruff/src/rules/pylint/rules/useless_return.rs index 2fc8336b2c..d292cd4aea 100644 --- a/crates/ruff/src/rules/pylint/rules/useless_return.rs +++ b/crates/ruff/src/rules/pylint/rules/useless_return.rs @@ -7,7 +7,7 @@ use ruff_python_ast::helpers::{is_const_none, ReturnStatementVisitor}; use ruff_python_ast::statement_visitor::StatementVisitor; use ruff_python_ast::types::RefEquality; -use crate::autofix::actions::delete_stmt; +use crate::autofix::edits::delete_stmt; use crate::checkers::ast::Checker; use crate::registry::AsRule; diff --git a/crates/ruff/src/rules/pyupgrade/rules/outdated_version_block.rs b/crates/ruff/src/rules/pyupgrade/rules/outdated_version_block.rs index c52442678d..819ce89141 100644 --- a/crates/ruff/src/rules/pyupgrade/rules/outdated_version_block.rs +++ b/crates/ruff/src/rules/pyupgrade/rules/outdated_version_block.rs @@ -12,7 +12,7 @@ use ruff_python_ast::source_code::Locator; use ruff_python_ast::types::RefEquality; use ruff_python_ast::whitespace::indentation; -use crate::autofix::actions::delete_stmt; +use crate::autofix::edits::delete_stmt; use crate::checkers::ast::Checker; use crate::registry::AsRule; use crate::rules::pyupgrade::fixes::adjust_indentation; 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 91f943b0ad..b1aee43ed4 100644 --- a/crates/ruff/src/rules/pyupgrade/rules/replace_stdout_stderr.rs +++ b/crates/ruff/src/rules/pyupgrade/rules/replace_stdout_stderr.rs @@ -6,7 +6,7 @@ use ruff_macros::{derive_message_formats, violation}; use ruff_python_ast::helpers::find_keyword; use ruff_python_ast::source_code::Locator; -use crate::autofix::actions::remove_argument; +use crate::autofix::edits::remove_argument; use crate::checkers::ast::Checker; use crate::registry::AsRule; diff --git a/crates/ruff/src/rules/pyupgrade/rules/unnecessary_builtin_import.rs b/crates/ruff/src/rules/pyupgrade/rules/unnecessary_builtin_import.rs index 4c7415de11..98ee6aa8d5 100644 --- a/crates/ruff/src/rules/pyupgrade/rules/unnecessary_builtin_import.rs +++ b/crates/ruff/src/rules/pyupgrade/rules/unnecessary_builtin_import.rs @@ -112,7 +112,7 @@ pub(crate) fn unnecessary_builtin_import( .iter() .map(|alias| format!("{module}.{}", alias.name)) .collect(); - match autofix::actions::remove_unused_imports( + match autofix::edits::remove_unused_imports( unused_imports.iter().map(String::as_str), defined_by, defined_in, 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 9d304c3e04..3e286b2d95 100644 --- a/crates/ruff/src/rules/pyupgrade/rules/unnecessary_encode_utf8.rs +++ b/crates/ruff/src/rules/pyupgrade/rules/unnecessary_encode_utf8.rs @@ -6,7 +6,7 @@ use ruff_diagnostics::{AlwaysAutofixableViolation, Diagnostic, Edit, Fix}; use ruff_macros::{derive_message_formats, violation}; use ruff_python_ast::source_code::Locator; -use crate::autofix::actions::remove_argument; +use crate::autofix::edits::remove_argument; use crate::checkers::ast::Checker; use crate::registry::Rule; diff --git a/crates/ruff/src/rules/pyupgrade/rules/unnecessary_future_import.rs b/crates/ruff/src/rules/pyupgrade/rules/unnecessary_future_import.rs index e9da4149d0..a8b2ea2b95 100644 --- a/crates/ruff/src/rules/pyupgrade/rules/unnecessary_future_import.rs +++ b/crates/ruff/src/rules/pyupgrade/rules/unnecessary_future_import.rs @@ -92,7 +92,7 @@ pub(crate) fn unnecessary_future_import(checker: &mut Checker, stmt: &Stmt, name .iter() .map(|alias| format!("__future__.{}", alias.name)) .collect(); - match autofix::actions::remove_unused_imports( + match autofix::edits::remove_unused_imports( unused_imports.iter().map(String::as_str), defined_by, defined_in, diff --git a/crates/ruff/src/rules/pyupgrade/rules/useless_metaclass_type.rs b/crates/ruff/src/rules/pyupgrade/rules/useless_metaclass_type.rs index 9cf7e04057..b0c720807e 100644 --- a/crates/ruff/src/rules/pyupgrade/rules/useless_metaclass_type.rs +++ b/crates/ruff/src/rules/pyupgrade/rules/useless_metaclass_type.rs @@ -6,7 +6,7 @@ use ruff_diagnostics::{AlwaysAutofixableViolation, Diagnostic, Fix}; use ruff_macros::{derive_message_formats, violation}; use ruff_python_ast::types::RefEquality; -use crate::autofix::actions; +use crate::autofix::edits; use crate::checkers::ast::Checker; use crate::registry::AsRule; @@ -58,7 +58,7 @@ pub(crate) fn useless_metaclass_type( let deleted: Vec<&Stmt> = checker.deletions.iter().map(Into::into).collect(); let defined_by = checker.semantic_model().stmt(); let defined_in = checker.semantic_model().stmt_parent(); - match actions::delete_stmt( + match edits::delete_stmt( defined_by, defined_in, &deleted, diff --git a/crates/ruff/src/rules/pyupgrade/rules/useless_object_inheritance.rs b/crates/ruff/src/rules/pyupgrade/rules/useless_object_inheritance.rs index 858e2aa55d..8a3bc3839d 100644 --- a/crates/ruff/src/rules/pyupgrade/rules/useless_object_inheritance.rs +++ b/crates/ruff/src/rules/pyupgrade/rules/useless_object_inheritance.rs @@ -5,7 +5,7 @@ use ruff_macros::{derive_message_formats, violation}; use ruff_python_semantic::binding::{Binding, BindingKind, Bindings}; use ruff_python_semantic::scope::Scope; -use crate::autofix::actions::remove_argument; +use crate::autofix::edits::remove_argument; use crate::checkers::ast::Checker; use crate::registry::AsRule;