mirror of
https://github.com/astral-sh/ruff.git
synced 2025-10-01 14:21:24 +00:00
Remove per-diagnostic check for fixability (#7919)
## Summary Throughout the codebase, we have this pattern: ```rust let mut diagnostic = ... if checker.patch(Rule::UnusedVariable) { // Do the fix. } diagnostics.push(diagnostic) ``` This was helpful when we computed fixes lazily; however, we now compute fixes eagerly, and this is _only_ used to ensure that we don't generate fixes for rules marked as unfixable. We often forget to add this, and it leads to bugs in enforcing `--unfixable`. This PR instead removes all of these checks, moving the responsibility of enforcing `--unfixable` up to `check_path`. This is similar to how @zanieb handled the `--extend-unsafe` logic: we post-process the diagnostics to remove any fixes that should be ignored.
This commit is contained in:
parent
1835d7bb45
commit
c38617fa27
189 changed files with 2433 additions and 3210 deletions
|
@ -32,15 +32,10 @@ pub(crate) fn bindings(checker: &mut Checker) {
|
|||
},
|
||||
binding.range(),
|
||||
);
|
||||
if checker.patch(Rule::UnusedVariable) {
|
||||
diagnostic.try_set_fix(|| {
|
||||
pyflakes::fixes::remove_exception_handler_assignment(
|
||||
binding,
|
||||
checker.locator,
|
||||
)
|
||||
diagnostic.try_set_fix(|| {
|
||||
pyflakes::fixes::remove_exception_handler_assignment(binding, checker.locator)
|
||||
.map(Fix::safe_edit)
|
||||
});
|
||||
}
|
||||
});
|
||||
checker.diagnostics.push(diagnostic);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -143,11 +143,6 @@ impl<'a> Checker<'a> {
|
|||
}
|
||||
|
||||
impl<'a> Checker<'a> {
|
||||
/// Return `true` if a patch should be generated for a given [`Rule`].
|
||||
pub(crate) fn patch(&self, code: Rule) -> bool {
|
||||
self.settings.rules.should_fix(code)
|
||||
}
|
||||
|
||||
/// Return `true` if a [`Rule`] is disabled by a `noqa` directive.
|
||||
pub(crate) fn rule_is_ignored(&self, code: Rule, offset: TextSize) -> bool {
|
||||
// TODO(charlie): `noqa` directives are mostly enforced in `check_lines.rs`.
|
||||
|
|
|
@ -5,7 +5,7 @@ use ruff_python_parser::TokenKind;
|
|||
use ruff_source_file::Locator;
|
||||
use ruff_text_size::{Ranged, TextRange};
|
||||
|
||||
use crate::registry::{AsRule, Rule};
|
||||
use crate::registry::AsRule;
|
||||
use crate::rules::pycodestyle::rules::logical_lines::{
|
||||
extraneous_whitespace, indentation, missing_whitespace, missing_whitespace_after_keyword,
|
||||
missing_whitespace_around_operator, space_after_comma, space_around_operator,
|
||||
|
@ -38,17 +38,6 @@ pub(crate) fn check_logical_lines(
|
|||
) -> Vec<Diagnostic> {
|
||||
let mut context = LogicalLinesContext::new(settings);
|
||||
|
||||
let should_fix_missing_whitespace = settings.rules.should_fix(Rule::MissingWhitespace);
|
||||
let should_fix_whitespace_before_parameters =
|
||||
settings.rules.should_fix(Rule::WhitespaceBeforeParameters);
|
||||
let should_fix_whitespace_after_open_bracket =
|
||||
settings.rules.should_fix(Rule::WhitespaceAfterOpenBracket);
|
||||
let should_fix_whitespace_before_close_bracket = settings
|
||||
.rules
|
||||
.should_fix(Rule::WhitespaceBeforeCloseBracket);
|
||||
let should_fix_whitespace_before_punctuation =
|
||||
settings.rules.should_fix(Rule::WhitespaceBeforePunctuation);
|
||||
|
||||
let mut prev_line = None;
|
||||
let mut prev_indent_level = None;
|
||||
let indent_char = stylist.indentation().as_char();
|
||||
|
@ -58,7 +47,7 @@ pub(crate) fn check_logical_lines(
|
|||
space_around_operator(&line, &mut context);
|
||||
whitespace_around_named_parameter_equals(&line, &mut context);
|
||||
missing_whitespace_around_operator(&line, &mut context);
|
||||
missing_whitespace(&line, should_fix_missing_whitespace, &mut context);
|
||||
missing_whitespace(&line, &mut context);
|
||||
}
|
||||
if line.flags().contains(TokenFlags::PUNCTUATION) {
|
||||
space_after_comma(&line, &mut context);
|
||||
|
@ -68,13 +57,7 @@ pub(crate) fn check_logical_lines(
|
|||
.flags()
|
||||
.intersects(TokenFlags::OPERATOR | TokenFlags::BRACKET | TokenFlags::PUNCTUATION)
|
||||
{
|
||||
extraneous_whitespace(
|
||||
&line,
|
||||
&mut context,
|
||||
should_fix_whitespace_after_open_bracket,
|
||||
should_fix_whitespace_before_close_bracket,
|
||||
should_fix_whitespace_before_punctuation,
|
||||
);
|
||||
extraneous_whitespace(&line, &mut context);
|
||||
}
|
||||
|
||||
if line.flags().contains(TokenFlags::KEYWORD) {
|
||||
|
@ -87,11 +70,7 @@ pub(crate) fn check_logical_lines(
|
|||
}
|
||||
|
||||
if line.flags().contains(TokenFlags::BRACKET) {
|
||||
whitespace_before_parameters(
|
||||
&line,
|
||||
should_fix_whitespace_before_parameters,
|
||||
&mut context,
|
||||
);
|
||||
whitespace_before_parameters(&line, &mut context);
|
||||
}
|
||||
|
||||
// Extract the indentation level.
|
||||
|
|
|
@ -109,10 +109,8 @@ pub(crate) fn check_noqa(
|
|||
if line.matches.is_empty() {
|
||||
let mut diagnostic =
|
||||
Diagnostic::new(UnusedNOQA { codes: None }, directive.range());
|
||||
if settings.rules.should_fix(diagnostic.kind.rule()) {
|
||||
diagnostic
|
||||
.set_fix(Fix::safe_edit(delete_noqa(directive.range(), locator)));
|
||||
}
|
||||
diagnostic.set_fix(Fix::safe_edit(delete_noqa(directive.range(), locator)));
|
||||
|
||||
diagnostics.push(diagnostic);
|
||||
}
|
||||
}
|
||||
|
@ -173,18 +171,14 @@ pub(crate) fn check_noqa(
|
|||
},
|
||||
directive.range(),
|
||||
);
|
||||
if settings.rules.should_fix(diagnostic.kind.rule()) {
|
||||
if valid_codes.is_empty() {
|
||||
diagnostic.set_fix(Fix::safe_edit(delete_noqa(
|
||||
directive.range(),
|
||||
locator,
|
||||
)));
|
||||
} else {
|
||||
diagnostic.set_fix(Fix::safe_edit(Edit::range_replacement(
|
||||
format!("# noqa: {}", valid_codes.join(", ")),
|
||||
directive.range(),
|
||||
)));
|
||||
}
|
||||
if valid_codes.is_empty() {
|
||||
diagnostic
|
||||
.set_fix(Fix::safe_edit(delete_noqa(directive.range(), locator)));
|
||||
} else {
|
||||
diagnostic.set_fix(Fix::safe_edit(Edit::range_replacement(
|
||||
format!("# noqa: {}", valid_codes.join(", ")),
|
||||
directive.range(),
|
||||
)));
|
||||
}
|
||||
diagnostics.push(diagnostic);
|
||||
}
|
||||
|
|
|
@ -71,11 +71,7 @@ pub(crate) fn check_physical_lines(
|
|||
}
|
||||
|
||||
if enforce_no_newline_at_end_of_file {
|
||||
if let Some(diagnostic) = no_newline_at_end_of_file(
|
||||
locator,
|
||||
stylist,
|
||||
settings.rules.should_fix(Rule::MissingNewlineAtEndOfFile),
|
||||
) {
|
||||
if let Some(diagnostic) = no_newline_at_end_of_file(locator, stylist) {
|
||||
diagnostics.push(diagnostic);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -72,7 +72,7 @@ pub(crate) fn check_tokens(
|
|||
}
|
||||
|
||||
if settings.rules.enabled(Rule::UTF8EncodingDeclaration) {
|
||||
pyupgrade::rules::unnecessary_coding_comment(&mut diagnostics, locator, indexer, settings);
|
||||
pyupgrade::rules::unnecessary_coding_comment(&mut diagnostics, locator, indexer);
|
||||
}
|
||||
|
||||
if settings.rules.enabled(Rule::InvalidEscapeSequence) {
|
||||
|
@ -83,7 +83,6 @@ pub(crate) fn check_tokens(
|
|||
indexer,
|
||||
tok,
|
||||
*range,
|
||||
settings.rules.should_fix(Rule::InvalidEscapeSequence),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -109,13 +108,7 @@ pub(crate) fn check_tokens(
|
|||
Rule::MultipleStatementsOnOneLineSemicolon,
|
||||
Rule::UselessSemicolon,
|
||||
]) {
|
||||
pycodestyle::rules::compound_statements(
|
||||
&mut diagnostics,
|
||||
tokens,
|
||||
locator,
|
||||
indexer,
|
||||
settings,
|
||||
);
|
||||
pycodestyle::rules::compound_statements(&mut diagnostics, tokens, locator, indexer);
|
||||
}
|
||||
|
||||
if settings.rules.enabled(Rule::AvoidableEscapedQuote) && settings.flake8_quotes.avoid_escape {
|
||||
|
@ -148,11 +141,11 @@ pub(crate) fn check_tokens(
|
|||
Rule::TrailingCommaOnBareTuple,
|
||||
Rule::ProhibitedTrailingComma,
|
||||
]) {
|
||||
flake8_commas::rules::trailing_commas(&mut diagnostics, tokens, locator, settings);
|
||||
flake8_commas::rules::trailing_commas(&mut diagnostics, tokens, locator);
|
||||
}
|
||||
|
||||
if settings.rules.enabled(Rule::ExtraneousParentheses) {
|
||||
pyupgrade::rules::extraneous_parentheses(&mut diagnostics, tokens, locator, settings);
|
||||
pyupgrade::rules::extraneous_parentheses(&mut diagnostics, tokens, locator);
|
||||
}
|
||||
|
||||
if is_stub && settings.rules.enabled(Rule::TypeCommentInStub) {
|
||||
|
@ -166,7 +159,7 @@ pub(crate) fn check_tokens(
|
|||
Rule::ShebangNotFirstLine,
|
||||
Rule::ShebangMissingPython,
|
||||
]) {
|
||||
flake8_executable::rules::from_tokens(tokens, path, locator, settings, &mut diagnostics);
|
||||
flake8_executable::rules::from_tokens(tokens, path, locator, &mut diagnostics);
|
||||
}
|
||||
|
||||
if settings.rules.any_enabled(&[
|
||||
|
@ -191,7 +184,7 @@ pub(crate) fn check_tokens(
|
|||
TodoComment::from_comment(comment, *comment_range, i)
|
||||
})
|
||||
.collect();
|
||||
flake8_todos::rules::todos(&mut diagnostics, &todo_comments, locator, indexer, settings);
|
||||
flake8_todos::rules::todos(&mut diagnostics, &todo_comments, locator, indexer);
|
||||
flake8_fixme::rules::todos(&mut diagnostics, &todo_comments);
|
||||
}
|
||||
|
||||
|
|
|
@ -260,6 +260,13 @@ pub fn check_path(
|
|||
}
|
||||
}
|
||||
|
||||
// Remove fixes for any rules marked as unfixable.
|
||||
for diagnostic in &mut diagnostics {
|
||||
if !settings.rules.should_fix(diagnostic.kind.rule()) {
|
||||
diagnostic.fix = None;
|
||||
}
|
||||
}
|
||||
|
||||
// Update fix applicability to account for overrides
|
||||
if !settings.extend_safe_fixes.is_empty() || !settings.extend_unsafe_fixes.is_empty() {
|
||||
for diagnostic in &mut diagnostics {
|
||||
|
|
|
@ -3,7 +3,6 @@ use ruff_macros::{derive_message_formats, violation};
|
|||
use ruff_python_index::Indexer;
|
||||
use ruff_source_file::Locator;
|
||||
|
||||
use crate::registry::Rule;
|
||||
use crate::settings::LinterSettings;
|
||||
|
||||
use super::super::detection::comment_contains_code;
|
||||
|
@ -67,11 +66,9 @@ pub(crate) fn commented_out_code(
|
|||
if is_standalone_comment(line) && comment_contains_code(line, &settings.task_tags[..]) {
|
||||
let mut diagnostic = Diagnostic::new(CommentedOutCode, *range);
|
||||
|
||||
if settings.rules.should_fix(Rule::CommentedOutCode) {
|
||||
diagnostic.set_fix(Fix::display_edit(Edit::range_deletion(
|
||||
locator.full_lines_range(*range),
|
||||
)));
|
||||
}
|
||||
diagnostic.set_fix(Fix::display_edit(Edit::range_deletion(
|
||||
locator.full_lines_range(*range),
|
||||
)));
|
||||
diagnostics.push(diagnostic);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,7 +11,7 @@ use ruff_python_stdlib::typing::simple_magic_return_type;
|
|||
use ruff_text_size::Ranged;
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::registry::{AsRule, Rule};
|
||||
use crate::registry::Rule;
|
||||
use crate::rules::ruff::typing::type_hint_resolves_to_any;
|
||||
|
||||
/// ## What it does
|
||||
|
@ -702,12 +702,10 @@ pub(crate) fn definition(
|
|||
},
|
||||
function.identifier(),
|
||||
);
|
||||
if checker.patch(diagnostic.kind.rule()) {
|
||||
diagnostic.set_fix(Fix::unsafe_edit(Edit::insertion(
|
||||
" -> None".to_string(),
|
||||
function.parameters.range().end(),
|
||||
)));
|
||||
}
|
||||
diagnostic.set_fix(Fix::unsafe_edit(Edit::insertion(
|
||||
" -> None".to_string(),
|
||||
function.parameters.range().end(),
|
||||
)));
|
||||
diagnostics.push(diagnostic);
|
||||
}
|
||||
}
|
||||
|
@ -719,13 +717,11 @@ pub(crate) fn definition(
|
|||
},
|
||||
function.identifier(),
|
||||
);
|
||||
if checker.patch(diagnostic.kind.rule()) {
|
||||
if let Some(return_type) = simple_magic_return_type(name) {
|
||||
diagnostic.set_fix(Fix::unsafe_edit(Edit::insertion(
|
||||
format!(" -> {return_type}"),
|
||||
function.parameters.range().end(),
|
||||
)));
|
||||
}
|
||||
if let Some(return_type) = simple_magic_return_type(name) {
|
||||
diagnostic.set_fix(Fix::unsafe_edit(Edit::insertion(
|
||||
format!(" -> {return_type}"),
|
||||
function.parameters.range().end(),
|
||||
)));
|
||||
}
|
||||
diagnostics.push(diagnostic);
|
||||
}
|
||||
|
|
|
@ -6,7 +6,6 @@ use ruff_macros::{derive_message_formats, violation};
|
|||
use ruff_python_ast::helpers::is_const_false;
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::registry::AsRule;
|
||||
|
||||
/// ## What it does
|
||||
/// Checks for uses of `assert False`.
|
||||
|
@ -75,11 +74,9 @@ pub(crate) fn assert_false(checker: &mut Checker, stmt: &Stmt, test: &Expr, msg:
|
|||
}
|
||||
|
||||
let mut diagnostic = Diagnostic::new(AssertFalse, test.range());
|
||||
if checker.patch(diagnostic.kind.rule()) {
|
||||
diagnostic.set_fix(Fix::unsafe_edit(Edit::range_replacement(
|
||||
checker.generator().stmt(&assertion_error(msg)),
|
||||
stmt.range(),
|
||||
)));
|
||||
}
|
||||
diagnostic.set_fix(Fix::unsafe_edit(Edit::range_replacement(
|
||||
checker.generator().stmt(&assertion_error(msg)),
|
||||
stmt.range(),
|
||||
)));
|
||||
checker.diagnostics.push(diagnostic);
|
||||
}
|
||||
|
|
|
@ -11,7 +11,7 @@ use ruff_python_ast::call_path::CallPath;
|
|||
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::fix::edits::pad;
|
||||
use crate::registry::{AsRule, Rule};
|
||||
use crate::registry::Rule;
|
||||
|
||||
/// ## What it does
|
||||
/// Checks for `try-except` blocks with duplicate exception handlers.
|
||||
|
@ -146,24 +146,22 @@ fn duplicate_handler_exceptions<'a>(
|
|||
},
|
||||
expr.range(),
|
||||
);
|
||||
if checker.patch(diagnostic.kind.rule()) {
|
||||
diagnostic.set_fix(Fix::safe_edit(Edit::range_replacement(
|
||||
// Single exceptions don't require parentheses, but since we're _removing_
|
||||
// parentheses, insert whitespace as needed.
|
||||
if let [elt] = unique_elts.as_slice() {
|
||||
pad(
|
||||
checker.generator().expr(elt),
|
||||
expr.range(),
|
||||
checker.locator(),
|
||||
)
|
||||
} else {
|
||||
// Multiple exceptions must always be parenthesized. This is done
|
||||
// manually as the generator never parenthesizes lone tuples.
|
||||
format!("({})", checker.generator().expr(&type_pattern(unique_elts)))
|
||||
},
|
||||
expr.range(),
|
||||
)));
|
||||
}
|
||||
diagnostic.set_fix(Fix::safe_edit(Edit::range_replacement(
|
||||
// Single exceptions don't require parentheses, but since we're _removing_
|
||||
// parentheses, insert whitespace as needed.
|
||||
if let [elt] = unique_elts.as_slice() {
|
||||
pad(
|
||||
checker.generator().expr(elt),
|
||||
expr.range(),
|
||||
checker.locator(),
|
||||
)
|
||||
} else {
|
||||
// Multiple exceptions must always be parenthesized. This is done
|
||||
// manually as the generator never parenthesizes lone tuples.
|
||||
format!("({})", checker.generator().expr(&type_pattern(unique_elts)))
|
||||
},
|
||||
expr.range(),
|
||||
)));
|
||||
checker.diagnostics.push(diagnostic);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,7 +6,6 @@ use ruff_python_stdlib::identifiers::{is_identifier, is_mangled_private};
|
|||
use ruff_text_size::Ranged;
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::registry::AsRule;
|
||||
|
||||
/// ## What it does
|
||||
/// Checks for uses of `getattr` that take a constant attribute value as an
|
||||
|
@ -85,26 +84,24 @@ pub(crate) fn getattr_with_constant(
|
|||
}
|
||||
|
||||
let mut diagnostic = Diagnostic::new(GetAttrWithConstant, expr.range());
|
||||
if checker.patch(diagnostic.kind.rule()) {
|
||||
diagnostic.set_fix(Fix::safe_edit(Edit::range_replacement(
|
||||
pad(
|
||||
if matches!(
|
||||
obj,
|
||||
Expr::Name(_) | Expr::Attribute(_) | Expr::Subscript(_) | Expr::Call(_)
|
||||
) && !checker.locator().contains_line_break(obj.range())
|
||||
{
|
||||
format!("{}.{}", checker.locator().slice(obj), value)
|
||||
} else {
|
||||
// Defensively parenthesize any other expressions. For example, attribute accesses
|
||||
// on `int` literals must be parenthesized, e.g., `getattr(1, "real")` becomes
|
||||
// `(1).real`. The same is true for named expressions and others.
|
||||
format!("({}).{}", checker.locator().slice(obj), value)
|
||||
},
|
||||
expr.range(),
|
||||
checker.locator(),
|
||||
),
|
||||
diagnostic.set_fix(Fix::safe_edit(Edit::range_replacement(
|
||||
pad(
|
||||
if matches!(
|
||||
obj,
|
||||
Expr::Name(_) | Expr::Attribute(_) | Expr::Subscript(_) | Expr::Call(_)
|
||||
) && !checker.locator().contains_line_break(obj.range())
|
||||
{
|
||||
format!("{}.{}", checker.locator().slice(obj), value)
|
||||
} else {
|
||||
// Defensively parenthesize any other expressions. For example, attribute accesses
|
||||
// on `int` literals must be parenthesized, e.g., `getattr(1, "real")` becomes
|
||||
// `(1).real`. The same is true for named expressions and others.
|
||||
format!("({}).{}", checker.locator().slice(obj), value)
|
||||
},
|
||||
expr.range(),
|
||||
)));
|
||||
}
|
||||
checker.locator(),
|
||||
),
|
||||
expr.range(),
|
||||
)));
|
||||
checker.diagnostics.push(diagnostic);
|
||||
}
|
||||
|
|
|
@ -11,7 +11,6 @@ use ruff_source_file::Locator;
|
|||
use ruff_text_size::Ranged;
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::registry::AsRule;
|
||||
|
||||
/// ## What it does
|
||||
/// Checks for uses of mutable objects as function argument defaults.
|
||||
|
@ -110,18 +109,16 @@ pub(crate) fn mutable_argument_default(checker: &mut Checker, function_def: &ast
|
|||
let mut diagnostic = Diagnostic::new(MutableArgumentDefault, default.range());
|
||||
|
||||
// If the function body is on the same line as the function def, do not fix
|
||||
if checker.patch(diagnostic.kind.rule()) {
|
||||
if let Some(fix) = move_initialization(
|
||||
function_def,
|
||||
parameter,
|
||||
default,
|
||||
checker.locator(),
|
||||
checker.stylist(),
|
||||
checker.indexer(),
|
||||
checker.generator(),
|
||||
) {
|
||||
diagnostic.set_fix(fix);
|
||||
}
|
||||
if let Some(fix) = move_initialization(
|
||||
function_def,
|
||||
parameter,
|
||||
default,
|
||||
checker.locator(),
|
||||
checker.stylist(),
|
||||
checker.indexer(),
|
||||
checker.generator(),
|
||||
) {
|
||||
diagnostic.set_fix(fix);
|
||||
}
|
||||
checker.diagnostics.push(diagnostic);
|
||||
}
|
||||
|
|
|
@ -6,7 +6,6 @@ use ruff_text_size::Ranged;
|
|||
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::fix::edits::pad;
|
||||
use crate::registry::AsRule;
|
||||
|
||||
/// ## What it does
|
||||
/// Checks for single-element tuples in exception handlers (e.g.,
|
||||
|
@ -77,23 +76,21 @@ pub(crate) fn redundant_tuple_in_exception_handler(
|
|||
},
|
||||
type_.range(),
|
||||
);
|
||||
if checker.patch(diagnostic.kind.rule()) {
|
||||
// If there's no space between the `except` and the tuple, we need to insert a space,
|
||||
// as in:
|
||||
// ```python
|
||||
// except(ValueError,):
|
||||
// ```
|
||||
// Otherwise, the output will be invalid syntax, since we're removing a set of
|
||||
// parentheses.
|
||||
diagnostic.set_fix(Fix::safe_edit(Edit::range_replacement(
|
||||
pad(
|
||||
checker.generator().expr(elt),
|
||||
type_.range(),
|
||||
checker.locator(),
|
||||
),
|
||||
// If there's no space between the `except` and the tuple, we need to insert a space,
|
||||
// as in:
|
||||
// ```python
|
||||
// except(ValueError,):
|
||||
// ```
|
||||
// Otherwise, the output will be invalid syntax, since we're removing a set of
|
||||
// parentheses.
|
||||
diagnostic.set_fix(Fix::safe_edit(Edit::range_replacement(
|
||||
pad(
|
||||
checker.generator().expr(elt),
|
||||
type_.range(),
|
||||
)));
|
||||
}
|
||||
checker.locator(),
|
||||
),
|
||||
type_.range(),
|
||||
)));
|
||||
checker.diagnostics.push(diagnostic);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,7 +7,6 @@ use ruff_python_codegen::Generator;
|
|||
use ruff_python_stdlib::identifiers::{is_identifier, is_mangled_private};
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::registry::AsRule;
|
||||
|
||||
/// ## What it does
|
||||
/// Checks for uses of `setattr` that take a constant attribute value as an
|
||||
|
@ -108,12 +107,10 @@ pub(crate) fn setattr_with_constant(
|
|||
{
|
||||
if expr == child.as_ref() {
|
||||
let mut diagnostic = Diagnostic::new(SetAttrWithConstant, expr.range());
|
||||
if checker.patch(diagnostic.kind.rule()) {
|
||||
diagnostic.set_fix(Fix::safe_edit(Edit::range_replacement(
|
||||
assignment(obj, name, value, checker.generator()),
|
||||
expr.range(),
|
||||
)));
|
||||
}
|
||||
diagnostic.set_fix(Fix::safe_edit(Edit::range_replacement(
|
||||
assignment(obj, name, value, checker.generator()),
|
||||
expr.range(),
|
||||
)));
|
||||
checker.diagnostics.push(diagnostic);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,7 +5,6 @@ use ruff_macros::{derive_message_formats, violation};
|
|||
use ruff_text_size::Ranged;
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::registry::AsRule;
|
||||
|
||||
/// ## What it does
|
||||
/// Checks for uses of `hasattr` to test if an object is callable (e.g.,
|
||||
|
@ -80,14 +79,12 @@ pub(crate) fn unreliable_callable_check(
|
|||
}
|
||||
|
||||
let mut diagnostic = Diagnostic::new(UnreliableCallableCheck, expr.range());
|
||||
if checker.patch(diagnostic.kind.rule()) {
|
||||
if id == "hasattr" {
|
||||
if checker.semantic().is_builtin("callable") {
|
||||
diagnostic.set_fix(Fix::safe_edit(Edit::range_replacement(
|
||||
format!("callable({})", checker.locator().slice(obj)),
|
||||
expr.range(),
|
||||
)));
|
||||
}
|
||||
if id == "hasattr" {
|
||||
if checker.semantic().is_builtin("callable") {
|
||||
diagnostic.set_fix(Fix::safe_edit(Edit::range_replacement(
|
||||
format!("callable({})", checker.locator().slice(obj)),
|
||||
expr.range(),
|
||||
)));
|
||||
}
|
||||
}
|
||||
checker.diagnostics.push(diagnostic);
|
||||
|
|
|
@ -8,7 +8,6 @@ use ruff_python_ast::{helpers, visitor};
|
|||
use ruff_text_size::Ranged;
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::registry::AsRule;
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, result_like::BoolLike)]
|
||||
enum Certainty {
|
||||
|
@ -156,23 +155,21 @@ pub(crate) fn unused_loop_control_variable(checker: &mut Checker, stmt_for: &ast
|
|||
},
|
||||
expr.range(),
|
||||
);
|
||||
if checker.patch(diagnostic.kind.rule()) {
|
||||
if let Some(rename) = rename {
|
||||
if certainty.into() {
|
||||
// Avoid fixing if the variable, or any future bindings to the variable, are
|
||||
// used _after_ the loop.
|
||||
let scope = checker.semantic().current_scope();
|
||||
if scope
|
||||
.get_all(name)
|
||||
.map(|binding_id| checker.semantic().binding(binding_id))
|
||||
.filter(|binding| binding.start() >= expr.start())
|
||||
.all(|binding| !binding.is_used())
|
||||
{
|
||||
diagnostic.set_fix(Fix::unsafe_edit(Edit::range_replacement(
|
||||
rename,
|
||||
expr.range(),
|
||||
)));
|
||||
}
|
||||
if let Some(rename) = rename {
|
||||
if certainty.into() {
|
||||
// Avoid fixing if the variable, or any future bindings to the variable, are
|
||||
// used _after_ the loop.
|
||||
let scope = checker.semantic().current_scope();
|
||||
if scope
|
||||
.get_all(name)
|
||||
.map(|binding_id| checker.semantic().binding(binding_id))
|
||||
.filter(|binding| binding.start() >= expr.start())
|
||||
.all(|binding| !binding.is_used())
|
||||
{
|
||||
diagnostic.set_fix(Fix::unsafe_edit(Edit::range_replacement(
|
||||
rename,
|
||||
expr.range(),
|
||||
)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,9 +8,6 @@ use ruff_python_parser::Tok;
|
|||
use ruff_source_file::Locator;
|
||||
use ruff_text_size::{Ranged, TextRange};
|
||||
|
||||
use crate::registry::Rule;
|
||||
use crate::settings::LinterSettings;
|
||||
|
||||
/// Simplified token type.
|
||||
#[derive(Copy, Clone, PartialEq, Eq)]
|
||||
enum TokenType {
|
||||
|
@ -225,7 +222,6 @@ pub(crate) fn trailing_commas(
|
|||
diagnostics: &mut Vec<Diagnostic>,
|
||||
tokens: &[LexResult],
|
||||
locator: &Locator,
|
||||
settings: &LinterSettings,
|
||||
) {
|
||||
let tokens = tokens
|
||||
.iter()
|
||||
|
@ -324,9 +320,7 @@ pub(crate) fn trailing_commas(
|
|||
if comma_prohibited {
|
||||
let comma = prev.spanned.unwrap();
|
||||
let mut diagnostic = Diagnostic::new(ProhibitedTrailingComma, comma.1);
|
||||
if settings.rules.should_fix(Rule::ProhibitedTrailingComma) {
|
||||
diagnostic.set_fix(Fix::safe_edit(Edit::range_deletion(diagnostic.range())));
|
||||
}
|
||||
diagnostic.set_fix(Fix::safe_edit(Edit::range_deletion(diagnostic.range())));
|
||||
diagnostics.push(diagnostic);
|
||||
}
|
||||
|
||||
|
@ -359,17 +353,15 @@ pub(crate) fn trailing_commas(
|
|||
MissingTrailingComma,
|
||||
TextRange::empty(missing_comma.1.end()),
|
||||
);
|
||||
if settings.rules.should_fix(Rule::MissingTrailingComma) {
|
||||
// Create a replacement that includes the final bracket (or other token),
|
||||
// rather than just inserting a comma at the end. This prevents the UP034 fix
|
||||
// removing any brackets in the same linter pass - doing both at the same time could
|
||||
// lead to a syntax error.
|
||||
let contents = locator.slice(missing_comma.1);
|
||||
diagnostic.set_fix(Fix::safe_edit(Edit::range_replacement(
|
||||
format!("{contents},"),
|
||||
missing_comma.1,
|
||||
)));
|
||||
}
|
||||
// Create a replacement that includes the final bracket (or other token),
|
||||
// rather than just inserting a comma at the end. This prevents the UP034 fix
|
||||
// removing any brackets in the same linter pass - doing both at the same time could
|
||||
// lead to a syntax error.
|
||||
let contents = locator.slice(missing_comma.1);
|
||||
diagnostic.set_fix(Fix::safe_edit(Edit::range_replacement(
|
||||
format!("{contents},"),
|
||||
missing_comma.1,
|
||||
)));
|
||||
diagnostics.push(diagnostic);
|
||||
}
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@ use ruff_python_ast::{self as ast, Expr};
|
|||
use ruff_text_size::Ranged;
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::registry::AsRule;
|
||||
|
||||
use crate::rules::flake8_comprehensions::fixes;
|
||||
|
||||
/// ## What it does
|
||||
|
@ -82,19 +82,14 @@ pub(crate) fn unnecessary_call_around_sorted(
|
|||
},
|
||||
expr.range(),
|
||||
);
|
||||
if checker.patch(diagnostic.kind.rule()) {
|
||||
diagnostic.try_set_fix(|| {
|
||||
let edit = fixes::fix_unnecessary_call_around_sorted(
|
||||
expr,
|
||||
checker.locator(),
|
||||
checker.stylist(),
|
||||
)?;
|
||||
if outer.id == "reversed" {
|
||||
Ok(Fix::unsafe_edit(edit))
|
||||
} else {
|
||||
Ok(Fix::safe_edit(edit))
|
||||
}
|
||||
});
|
||||
}
|
||||
diagnostic.try_set_fix(|| {
|
||||
let edit =
|
||||
fixes::fix_unnecessary_call_around_sorted(expr, checker.locator(), checker.stylist())?;
|
||||
if outer.id == "reversed" {
|
||||
Ok(Fix::unsafe_edit(edit))
|
||||
} else {
|
||||
Ok(Fix::safe_edit(edit))
|
||||
}
|
||||
});
|
||||
checker.diagnostics.push(diagnostic);
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@ use ruff_python_ast::{Expr, Keyword};
|
|||
use ruff_text_size::Ranged;
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::registry::AsRule;
|
||||
|
||||
use crate::rules::flake8_comprehensions::fixes;
|
||||
use crate::rules::flake8_comprehensions::settings::Settings;
|
||||
|
||||
|
@ -86,10 +86,8 @@ pub(crate) fn unnecessary_collection_call(
|
|||
},
|
||||
expr.range(),
|
||||
);
|
||||
if checker.patch(diagnostic.kind.rule()) {
|
||||
diagnostic.try_set_fix(|| {
|
||||
fixes::fix_unnecessary_collection_call(expr, checker).map(Fix::unsafe_edit)
|
||||
});
|
||||
}
|
||||
diagnostic.try_set_fix(|| {
|
||||
fixes::fix_unnecessary_collection_call(expr, checker).map(Fix::unsafe_edit)
|
||||
});
|
||||
checker.diagnostics.push(diagnostic);
|
||||
}
|
||||
|
|
|
@ -5,7 +5,7 @@ use ruff_python_ast::{self as ast, Comprehension, Expr};
|
|||
use ruff_text_size::Ranged;
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::registry::AsRule;
|
||||
|
||||
use crate::rules::flake8_comprehensions::fixes;
|
||||
|
||||
/// ## What it does
|
||||
|
@ -64,12 +64,10 @@ fn add_diagnostic(checker: &mut Checker, expr: &Expr) {
|
|||
},
|
||||
expr.range(),
|
||||
);
|
||||
if checker.patch(diagnostic.kind.rule()) {
|
||||
diagnostic.try_set_fix(|| {
|
||||
fixes::fix_unnecessary_comprehension(expr, checker.locator(), checker.stylist())
|
||||
.map(Fix::unsafe_edit)
|
||||
});
|
||||
}
|
||||
diagnostic.try_set_fix(|| {
|
||||
fixes::fix_unnecessary_comprehension(expr, checker.locator(), checker.stylist())
|
||||
.map(Fix::unsafe_edit)
|
||||
});
|
||||
checker.diagnostics.push(diagnostic);
|
||||
}
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@ use ruff_python_ast::helpers::any_over_expr;
|
|||
use ruff_text_size::Ranged;
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::registry::AsRule;
|
||||
|
||||
use crate::rules::flake8_comprehensions::fixes;
|
||||
|
||||
/// ## What it does
|
||||
|
@ -89,11 +89,9 @@ pub(crate) fn unnecessary_comprehension_any_all(
|
|||
}
|
||||
|
||||
let mut diagnostic = Diagnostic::new(UnnecessaryComprehensionAnyAll, arg.range());
|
||||
if checker.patch(diagnostic.kind.rule()) {
|
||||
diagnostic.try_set_fix(|| {
|
||||
fixes::fix_unnecessary_comprehension_any_all(expr, checker.locator(), checker.stylist())
|
||||
});
|
||||
}
|
||||
diagnostic.try_set_fix(|| {
|
||||
fixes::fix_unnecessary_comprehension_any_all(expr, checker.locator(), checker.stylist())
|
||||
});
|
||||
checker.diagnostics.push(diagnostic);
|
||||
}
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@ use ruff_python_ast::{self as ast, Arguments, Expr, Keyword};
|
|||
use ruff_text_size::Ranged;
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::registry::AsRule;
|
||||
|
||||
use crate::rules::flake8_comprehensions::fixes;
|
||||
|
||||
/// ## What it does
|
||||
|
@ -130,16 +130,14 @@ pub(crate) fn unnecessary_double_cast_or_process(
|
|||
},
|
||||
expr.range(),
|
||||
);
|
||||
if checker.patch(diagnostic.kind.rule()) {
|
||||
diagnostic.try_set_fix(|| {
|
||||
fixes::fix_unnecessary_double_cast_or_process(
|
||||
expr,
|
||||
checker.locator(),
|
||||
checker.stylist(),
|
||||
)
|
||||
.map(Fix::unsafe_edit)
|
||||
});
|
||||
}
|
||||
diagnostic.try_set_fix(|| {
|
||||
fixes::fix_unnecessary_double_cast_or_process(
|
||||
expr,
|
||||
checker.locator(),
|
||||
checker.stylist(),
|
||||
)
|
||||
.map(Fix::unsafe_edit)
|
||||
});
|
||||
checker.diagnostics.push(diagnostic);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@ use ruff_python_ast::{self as ast, Expr, Keyword};
|
|||
use ruff_text_size::Ranged;
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::registry::AsRule;
|
||||
|
||||
use crate::rules::flake8_comprehensions::fixes;
|
||||
|
||||
use super::helpers;
|
||||
|
@ -67,10 +67,7 @@ pub(crate) fn unnecessary_generator_dict(
|
|||
return;
|
||||
}
|
||||
let mut diagnostic = Diagnostic::new(UnnecessaryGeneratorDict, expr.range());
|
||||
if checker.patch(diagnostic.kind.rule()) {
|
||||
diagnostic.try_set_fix(|| {
|
||||
fixes::fix_unnecessary_generator_dict(expr, checker).map(Fix::unsafe_edit)
|
||||
});
|
||||
}
|
||||
diagnostic
|
||||
.try_set_fix(|| fixes::fix_unnecessary_generator_dict(expr, checker).map(Fix::unsafe_edit));
|
||||
checker.diagnostics.push(diagnostic);
|
||||
}
|
||||
|
|
|
@ -5,7 +5,7 @@ use ruff_macros::{derive_message_formats, violation};
|
|||
use ruff_text_size::Ranged;
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::registry::AsRule;
|
||||
|
||||
use crate::rules::flake8_comprehensions::fixes;
|
||||
|
||||
use super::helpers;
|
||||
|
@ -60,12 +60,10 @@ pub(crate) fn unnecessary_generator_list(
|
|||
}
|
||||
if let Expr::GeneratorExp(_) = argument {
|
||||
let mut diagnostic = Diagnostic::new(UnnecessaryGeneratorList, expr.range());
|
||||
if checker.patch(diagnostic.kind.rule()) {
|
||||
diagnostic.try_set_fix(|| {
|
||||
fixes::fix_unnecessary_generator_list(expr, checker.locator(), checker.stylist())
|
||||
.map(Fix::unsafe_edit)
|
||||
});
|
||||
}
|
||||
diagnostic.try_set_fix(|| {
|
||||
fixes::fix_unnecessary_generator_list(expr, checker.locator(), checker.stylist())
|
||||
.map(Fix::unsafe_edit)
|
||||
});
|
||||
checker.diagnostics.push(diagnostic);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,7 +5,7 @@ use ruff_macros::{derive_message_formats, violation};
|
|||
use ruff_text_size::Ranged;
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::registry::AsRule;
|
||||
|
||||
use crate::rules::flake8_comprehensions::fixes;
|
||||
|
||||
use super::helpers;
|
||||
|
@ -60,11 +60,9 @@ pub(crate) fn unnecessary_generator_set(
|
|||
}
|
||||
if let Expr::GeneratorExp(_) = argument {
|
||||
let mut diagnostic = Diagnostic::new(UnnecessaryGeneratorSet, expr.range());
|
||||
if checker.patch(diagnostic.kind.rule()) {
|
||||
diagnostic.try_set_fix(|| {
|
||||
fixes::fix_unnecessary_generator_set(expr, checker).map(Fix::unsafe_edit)
|
||||
});
|
||||
}
|
||||
diagnostic.try_set_fix(|| {
|
||||
fixes::fix_unnecessary_generator_set(expr, checker).map(Fix::unsafe_edit)
|
||||
});
|
||||
checker.diagnostics.push(diagnostic);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,7 +5,7 @@ use ruff_macros::{derive_message_formats, violation};
|
|||
use ruff_text_size::Ranged;
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::registry::AsRule;
|
||||
|
||||
use crate::rules::flake8_comprehensions::fixes;
|
||||
|
||||
use super::helpers;
|
||||
|
@ -56,11 +56,9 @@ pub(crate) fn unnecessary_list_call(
|
|||
return;
|
||||
}
|
||||
let mut diagnostic = Diagnostic::new(UnnecessaryListCall, expr.range());
|
||||
if checker.patch(diagnostic.kind.rule()) {
|
||||
diagnostic.try_set_fix(|| {
|
||||
fixes::fix_unnecessary_list_call(expr, checker.locator(), checker.stylist())
|
||||
.map(Fix::unsafe_edit)
|
||||
});
|
||||
}
|
||||
diagnostic.try_set_fix(|| {
|
||||
fixes::fix_unnecessary_list_call(expr, checker.locator(), checker.stylist())
|
||||
.map(Fix::unsafe_edit)
|
||||
});
|
||||
checker.diagnostics.push(diagnostic);
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@ use ruff_python_ast::{self as ast, Expr, Keyword};
|
|||
use ruff_text_size::Ranged;
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::registry::AsRule;
|
||||
|
||||
use crate::rules::flake8_comprehensions::fixes;
|
||||
|
||||
use super::helpers;
|
||||
|
@ -65,10 +65,8 @@ pub(crate) fn unnecessary_list_comprehension_dict(
|
|||
return;
|
||||
}
|
||||
let mut diagnostic = Diagnostic::new(UnnecessaryListComprehensionDict, expr.range());
|
||||
if checker.patch(diagnostic.kind.rule()) {
|
||||
diagnostic.try_set_fix(|| {
|
||||
fixes::fix_unnecessary_list_comprehension_dict(expr, checker).map(Fix::unsafe_edit)
|
||||
});
|
||||
}
|
||||
diagnostic.try_set_fix(|| {
|
||||
fixes::fix_unnecessary_list_comprehension_dict(expr, checker).map(Fix::unsafe_edit)
|
||||
});
|
||||
checker.diagnostics.push(diagnostic);
|
||||
}
|
||||
|
|
|
@ -5,7 +5,7 @@ use ruff_macros::{derive_message_formats, violation};
|
|||
use ruff_text_size::Ranged;
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::registry::AsRule;
|
||||
|
||||
use crate::rules::flake8_comprehensions::fixes;
|
||||
|
||||
use super::helpers;
|
||||
|
@ -58,11 +58,9 @@ pub(crate) fn unnecessary_list_comprehension_set(
|
|||
}
|
||||
if argument.is_list_comp_expr() {
|
||||
let mut diagnostic = Diagnostic::new(UnnecessaryListComprehensionSet, expr.range());
|
||||
if checker.patch(diagnostic.kind.rule()) {
|
||||
diagnostic.try_set_fix(|| {
|
||||
fixes::fix_unnecessary_list_comprehension_set(expr, checker).map(Fix::unsafe_edit)
|
||||
});
|
||||
}
|
||||
diagnostic.try_set_fix(|| {
|
||||
fixes::fix_unnecessary_list_comprehension_set(expr, checker).map(Fix::unsafe_edit)
|
||||
});
|
||||
checker.diagnostics.push(diagnostic);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@ use ruff_python_ast::{self as ast, Expr, Keyword};
|
|||
use ruff_text_size::Ranged;
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::registry::AsRule;
|
||||
|
||||
use crate::rules::flake8_comprehensions::fixes;
|
||||
|
||||
use super::helpers;
|
||||
|
@ -80,10 +80,7 @@ pub(crate) fn unnecessary_literal_dict(
|
|||
},
|
||||
expr.range(),
|
||||
);
|
||||
if checker.patch(diagnostic.kind.rule()) {
|
||||
diagnostic.try_set_fix(|| {
|
||||
fixes::fix_unnecessary_literal_dict(expr, checker).map(Fix::unsafe_edit)
|
||||
});
|
||||
}
|
||||
diagnostic
|
||||
.try_set_fix(|| fixes::fix_unnecessary_literal_dict(expr, checker).map(Fix::unsafe_edit));
|
||||
checker.diagnostics.push(diagnostic);
|
||||
}
|
||||
|
|
|
@ -5,7 +5,7 @@ use ruff_macros::{derive_message_formats, violation};
|
|||
use ruff_text_size::Ranged;
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::registry::AsRule;
|
||||
|
||||
use crate::rules::flake8_comprehensions::fixes;
|
||||
|
||||
use super::helpers;
|
||||
|
@ -75,10 +75,7 @@ pub(crate) fn unnecessary_literal_set(
|
|||
},
|
||||
expr.range(),
|
||||
);
|
||||
if checker.patch(diagnostic.kind.rule()) {
|
||||
diagnostic.try_set_fix(|| {
|
||||
fixes::fix_unnecessary_literal_set(expr, checker).map(Fix::unsafe_edit)
|
||||
});
|
||||
}
|
||||
diagnostic
|
||||
.try_set_fix(|| fixes::fix_unnecessary_literal_set(expr, checker).map(Fix::unsafe_edit));
|
||||
checker.diagnostics.push(diagnostic);
|
||||
}
|
||||
|
|
|
@ -7,7 +7,7 @@ use ruff_macros::{derive_message_formats, violation};
|
|||
use ruff_text_size::Ranged;
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::registry::AsRule;
|
||||
|
||||
use crate::rules::flake8_comprehensions::fixes;
|
||||
|
||||
use super::helpers;
|
||||
|
@ -91,15 +91,9 @@ pub(crate) fn unnecessary_literal_within_dict_call(
|
|||
},
|
||||
expr.range(),
|
||||
);
|
||||
if checker.patch(diagnostic.kind.rule()) {
|
||||
diagnostic.try_set_fix(|| {
|
||||
fixes::fix_unnecessary_literal_within_dict_call(
|
||||
expr,
|
||||
checker.locator(),
|
||||
checker.stylist(),
|
||||
)
|
||||
diagnostic.try_set_fix(|| {
|
||||
fixes::fix_unnecessary_literal_within_dict_call(expr, checker.locator(), checker.stylist())
|
||||
.map(Fix::unsafe_edit)
|
||||
});
|
||||
}
|
||||
});
|
||||
checker.diagnostics.push(diagnostic);
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@ use ruff_python_ast::{Expr, Keyword};
|
|||
use ruff_text_size::Ranged;
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::registry::AsRule;
|
||||
|
||||
use crate::rules::flake8_comprehensions::fixes;
|
||||
|
||||
use super::helpers;
|
||||
|
@ -93,15 +93,9 @@ pub(crate) fn unnecessary_literal_within_list_call(
|
|||
},
|
||||
expr.range(),
|
||||
);
|
||||
if checker.patch(diagnostic.kind.rule()) {
|
||||
diagnostic.try_set_fix(|| {
|
||||
fixes::fix_unnecessary_literal_within_list_call(
|
||||
expr,
|
||||
checker.locator(),
|
||||
checker.stylist(),
|
||||
)
|
||||
diagnostic.try_set_fix(|| {
|
||||
fixes::fix_unnecessary_literal_within_list_call(expr, checker.locator(), checker.stylist())
|
||||
.map(Fix::unsafe_edit)
|
||||
});
|
||||
}
|
||||
});
|
||||
checker.diagnostics.push(diagnostic);
|
||||
}
|
||||
|
|
|
@ -5,7 +5,7 @@ use ruff_macros::{derive_message_formats, violation};
|
|||
use ruff_text_size::Ranged;
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::registry::AsRule;
|
||||
|
||||
use crate::rules::flake8_comprehensions::fixes;
|
||||
|
||||
use super::helpers;
|
||||
|
@ -95,15 +95,9 @@ pub(crate) fn unnecessary_literal_within_tuple_call(
|
|||
},
|
||||
expr.range(),
|
||||
);
|
||||
if checker.patch(diagnostic.kind.rule()) {
|
||||
diagnostic.try_set_fix(|| {
|
||||
fixes::fix_unnecessary_literal_within_tuple_call(
|
||||
expr,
|
||||
checker.locator(),
|
||||
checker.stylist(),
|
||||
)
|
||||
diagnostic.try_set_fix(|| {
|
||||
fixes::fix_unnecessary_literal_within_tuple_call(expr, checker.locator(), checker.stylist())
|
||||
.map(Fix::unsafe_edit)
|
||||
});
|
||||
}
|
||||
});
|
||||
checker.diagnostics.push(diagnostic);
|
||||
}
|
||||
|
|
|
@ -9,7 +9,7 @@ use ruff_python_ast::{self as ast, Arguments, Expr, ExprContext, Parameters, Stm
|
|||
use ruff_text_size::Ranged;
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::registry::AsRule;
|
||||
|
||||
use crate::rules::flake8_comprehensions::fixes;
|
||||
|
||||
use super::helpers;
|
||||
|
@ -221,18 +221,16 @@ pub(crate) fn unnecessary_map(
|
|||
};
|
||||
|
||||
let mut diagnostic = Diagnostic::new(UnnecessaryMap { object_type }, expr.range());
|
||||
if checker.patch(diagnostic.kind.rule()) {
|
||||
diagnostic.try_set_fix(|| {
|
||||
fixes::fix_unnecessary_map(
|
||||
expr,
|
||||
parent,
|
||||
object_type,
|
||||
checker.locator(),
|
||||
checker.stylist(),
|
||||
)
|
||||
.map(Fix::unsafe_edit)
|
||||
});
|
||||
}
|
||||
diagnostic.try_set_fix(|| {
|
||||
fixes::fix_unnecessary_map(
|
||||
expr,
|
||||
parent,
|
||||
object_type,
|
||||
checker.locator(),
|
||||
checker.stylist(),
|
||||
)
|
||||
.map(Fix::unsafe_edit)
|
||||
});
|
||||
checker.diagnostics.push(diagnostic);
|
||||
}
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@ use ruff_python_ast::whitespace;
|
|||
use ruff_python_codegen::{Generator, Stylist};
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::registry::{AsRule, Rule};
|
||||
use crate::registry::Rule;
|
||||
|
||||
/// ## What it does
|
||||
/// Checks for the use of string literals in exception constructors.
|
||||
|
@ -190,30 +190,6 @@ pub(crate) fn string_in_exception(checker: &mut Checker, stmt: &Stmt, exc: &Expr
|
|||
if string.len() >= checker.settings.flake8_errmsg.max_string_length {
|
||||
let mut diagnostic =
|
||||
Diagnostic::new(RawStringInException, first.range());
|
||||
if checker.patch(diagnostic.kind.rule()) {
|
||||
if let Some(indentation) =
|
||||
whitespace::indentation(checker.locator(), stmt)
|
||||
{
|
||||
if checker.semantic().is_available("msg") {
|
||||
diagnostic.set_fix(generate_fix(
|
||||
stmt,
|
||||
first,
|
||||
indentation,
|
||||
checker.stylist(),
|
||||
checker.generator(),
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
checker.diagnostics.push(diagnostic);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Check for f-strings.
|
||||
Expr::FString(_) => {
|
||||
if checker.enabled(Rule::FStringInException) {
|
||||
let mut diagnostic = Diagnostic::new(FStringInException, first.range());
|
||||
if checker.patch(diagnostic.kind.rule()) {
|
||||
if let Some(indentation) =
|
||||
whitespace::indentation(checker.locator(), stmt)
|
||||
{
|
||||
|
@ -227,6 +203,25 @@ pub(crate) fn string_in_exception(checker: &mut Checker, stmt: &Stmt, exc: &Expr
|
|||
));
|
||||
}
|
||||
}
|
||||
checker.diagnostics.push(diagnostic);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Check for f-strings.
|
||||
Expr::FString(_) => {
|
||||
if checker.enabled(Rule::FStringInException) {
|
||||
let mut diagnostic = Diagnostic::new(FStringInException, first.range());
|
||||
if let Some(indentation) = whitespace::indentation(checker.locator(), stmt)
|
||||
{
|
||||
if checker.semantic().is_available("msg") {
|
||||
diagnostic.set_fix(generate_fix(
|
||||
stmt,
|
||||
first,
|
||||
indentation,
|
||||
checker.stylist(),
|
||||
checker.generator(),
|
||||
));
|
||||
}
|
||||
}
|
||||
checker.diagnostics.push(diagnostic);
|
||||
}
|
||||
|
@ -240,19 +235,17 @@ pub(crate) fn string_in_exception(checker: &mut Checker, stmt: &Stmt, exc: &Expr
|
|||
if attr == "format" && value.is_constant_expr() {
|
||||
let mut diagnostic =
|
||||
Diagnostic::new(DotFormatInException, first.range());
|
||||
if checker.patch(diagnostic.kind.rule()) {
|
||||
if let Some(indentation) =
|
||||
whitespace::indentation(checker.locator(), stmt)
|
||||
{
|
||||
if checker.semantic().is_available("msg") {
|
||||
diagnostic.set_fix(generate_fix(
|
||||
stmt,
|
||||
first,
|
||||
indentation,
|
||||
checker.stylist(),
|
||||
checker.generator(),
|
||||
));
|
||||
}
|
||||
if let Some(indentation) =
|
||||
whitespace::indentation(checker.locator(), stmt)
|
||||
{
|
||||
if checker.semantic().is_available("msg") {
|
||||
diagnostic.set_fix(generate_fix(
|
||||
stmt,
|
||||
first,
|
||||
indentation,
|
||||
checker.stylist(),
|
||||
checker.generator(),
|
||||
));
|
||||
}
|
||||
}
|
||||
checker.diagnostics.push(diagnostic);
|
||||
|
|
|
@ -12,7 +12,6 @@ pub(crate) use shebang_not_executable::*;
|
|||
pub(crate) use shebang_not_first_line::*;
|
||||
|
||||
use crate::comments::shebang::ShebangDirective;
|
||||
use crate::settings::LinterSettings;
|
||||
|
||||
mod shebang_leading_whitespace;
|
||||
mod shebang_missing_executable_file;
|
||||
|
@ -24,7 +23,6 @@ pub(crate) fn from_tokens(
|
|||
tokens: &[LexResult],
|
||||
path: &Path,
|
||||
locator: &Locator,
|
||||
settings: &LinterSettings,
|
||||
diagnostics: &mut Vec<Diagnostic>,
|
||||
) {
|
||||
let mut has_any_shebang = false;
|
||||
|
@ -41,7 +39,7 @@ pub(crate) fn from_tokens(
|
|||
diagnostics.push(diagnostic);
|
||||
}
|
||||
|
||||
if let Some(diagnostic) = shebang_leading_whitespace(*range, locator, settings) {
|
||||
if let Some(diagnostic) = shebang_leading_whitespace(*range, locator) {
|
||||
diagnostics.push(diagnostic);
|
||||
}
|
||||
|
||||
|
|
|
@ -5,9 +5,6 @@ use ruff_macros::{derive_message_formats, violation};
|
|||
use ruff_python_trivia::is_python_whitespace;
|
||||
use ruff_source_file::Locator;
|
||||
|
||||
use crate::registry::AsRule;
|
||||
use crate::settings::LinterSettings;
|
||||
|
||||
/// ## What it does
|
||||
/// Checks for whitespace before a shebang directive.
|
||||
///
|
||||
|
@ -50,7 +47,6 @@ impl AlwaysFixableViolation for ShebangLeadingWhitespace {
|
|||
pub(crate) fn shebang_leading_whitespace(
|
||||
range: TextRange,
|
||||
locator: &Locator,
|
||||
settings: &LinterSettings,
|
||||
) -> Option<Diagnostic> {
|
||||
// If the shebang is at the beginning of the file, abort.
|
||||
if range.start() == TextSize::from(0) {
|
||||
|
@ -68,8 +64,6 @@ pub(crate) fn shebang_leading_whitespace(
|
|||
|
||||
let prefix = TextRange::up_to(range.start());
|
||||
let mut diagnostic = Diagnostic::new(ShebangLeadingWhitespace, prefix);
|
||||
if settings.rules.should_fix(diagnostic.kind.rule()) {
|
||||
diagnostic.set_fix(Fix::safe_edit(Edit::range_deletion(prefix)));
|
||||
}
|
||||
diagnostic.set_fix(Fix::safe_edit(Edit::range_deletion(prefix)));
|
||||
Some(diagnostic)
|
||||
}
|
||||
|
|
|
@ -9,7 +9,6 @@ use ruff_python_parser::Tok;
|
|||
use ruff_source_file::Locator;
|
||||
use ruff_text_size::{Ranged, TextRange};
|
||||
|
||||
use crate::registry::AsRule;
|
||||
use crate::settings::LinterSettings;
|
||||
|
||||
/// ## What it does
|
||||
|
@ -137,10 +136,8 @@ pub(crate) fn implicit(
|
|||
TextRange::new(a_range.start(), b_range.end()),
|
||||
);
|
||||
|
||||
if settings.rules.should_fix(diagnostic.kind.rule()) {
|
||||
if let Some(fix) = concatenate_strings(a_range, b_range, locator) {
|
||||
diagnostic.set_fix(fix);
|
||||
}
|
||||
if let Some(fix) = concatenate_strings(a_range, b_range, locator) {
|
||||
diagnostic.set_fix(fix);
|
||||
}
|
||||
|
||||
diagnostics.push(diagnostic);
|
||||
|
|
|
@ -6,7 +6,7 @@ use ruff_python_semantic::{Binding, Imported};
|
|||
use ruff_text_size::Ranged;
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::registry::AsRule;
|
||||
|
||||
use crate::renamer::Renamer;
|
||||
|
||||
/// ## What it does
|
||||
|
@ -79,16 +79,14 @@ pub(crate) fn unconventional_import_alias(
|
|||
},
|
||||
binding.range(),
|
||||
);
|
||||
if checker.patch(diagnostic.kind.rule()) {
|
||||
if !import.is_submodule_import() {
|
||||
if checker.semantic().is_available(expected_alias) {
|
||||
diagnostic.try_set_fix(|| {
|
||||
let scope = &checker.semantic().scopes[binding.scope];
|
||||
let (edit, rest) =
|
||||
Renamer::rename(name, expected_alias, scope, checker.semantic())?;
|
||||
Ok(Fix::unsafe_edits(edit, rest))
|
||||
});
|
||||
}
|
||||
if !import.is_submodule_import() {
|
||||
if checker.semantic().is_available(expected_alias) {
|
||||
diagnostic.try_set_fix(|| {
|
||||
let scope = &checker.semantic().scopes[binding.scope];
|
||||
let (edit, rest) =
|
||||
Renamer::rename(name, expected_alias, scope, checker.semantic())?;
|
||||
Ok(Fix::unsafe_edits(edit, rest))
|
||||
});
|
||||
}
|
||||
}
|
||||
Some(diagnostic)
|
||||
|
|
|
@ -5,7 +5,6 @@ use ruff_text_size::Ranged;
|
|||
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::importer::ImportRequest;
|
||||
use crate::registry::AsRule;
|
||||
|
||||
/// ## What it does
|
||||
/// Checks for direct instantiation of `logging.Logger`, as opposed to using
|
||||
|
@ -61,17 +60,15 @@ pub(crate) fn direct_logger_instantiation(checker: &mut Checker, call: &ast::Exp
|
|||
.is_some_and(|call_path| matches!(call_path.as_slice(), ["logging", "Logger"]))
|
||||
{
|
||||
let mut diagnostic = Diagnostic::new(DirectLoggerInstantiation, call.func.range());
|
||||
if checker.patch(diagnostic.kind.rule()) {
|
||||
diagnostic.try_set_fix(|| {
|
||||
let (import_edit, binding) = checker.importer().get_or_import_symbol(
|
||||
&ImportRequest::import("logging", "getLogger"),
|
||||
call.func.start(),
|
||||
checker.semantic(),
|
||||
)?;
|
||||
let reference_edit = Edit::range_replacement(binding, call.func.range());
|
||||
Ok(Fix::unsafe_edits(import_edit, [reference_edit]))
|
||||
});
|
||||
}
|
||||
diagnostic.try_set_fix(|| {
|
||||
let (import_edit, binding) = checker.importer().get_or_import_symbol(
|
||||
&ImportRequest::import("logging", "getLogger"),
|
||||
call.func.start(),
|
||||
checker.semantic(),
|
||||
)?;
|
||||
let reference_edit = Edit::range_replacement(binding, call.func.range());
|
||||
Ok(Fix::unsafe_edits(import_edit, [reference_edit]))
|
||||
});
|
||||
checker.diagnostics.push(diagnostic);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,7 +4,6 @@ use ruff_python_ast::{self as ast, Expr};
|
|||
use ruff_text_size::Ranged;
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::registry::AsRule;
|
||||
|
||||
/// ## What it does
|
||||
/// Checks for any usage of `__cached__` and `__file__` as an argument to
|
||||
|
@ -80,13 +79,11 @@ pub(crate) fn invalid_get_logger_argument(checker: &mut Checker, call: &ast::Exp
|
|||
}
|
||||
|
||||
let mut diagnostic = Diagnostic::new(InvalidGetLoggerArgument, expr.range());
|
||||
if checker.patch(diagnostic.kind.rule()) {
|
||||
if checker.semantic().is_builtin("__name__") {
|
||||
diagnostic.set_fix(Fix::unsafe_edit(Edit::range_replacement(
|
||||
"__name__".to_string(),
|
||||
expr.range(),
|
||||
)));
|
||||
}
|
||||
if checker.semantic().is_builtin("__name__") {
|
||||
diagnostic.set_fix(Fix::unsafe_edit(Edit::range_replacement(
|
||||
"__name__".to_string(),
|
||||
expr.range(),
|
||||
)));
|
||||
}
|
||||
checker.diagnostics.push(diagnostic);
|
||||
}
|
||||
|
|
|
@ -6,7 +6,6 @@ use ruff_text_size::Ranged;
|
|||
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::importer::ImportRequest;
|
||||
use crate::registry::AsRule;
|
||||
|
||||
/// ## What it does
|
||||
/// Checks for uses of `logging.WARN`.
|
||||
|
@ -55,17 +54,15 @@ pub(crate) fn undocumented_warn(checker: &mut Checker, expr: &Expr) {
|
|||
.is_some_and(|call_path| matches!(call_path.as_slice(), ["logging", "WARN"]))
|
||||
{
|
||||
let mut diagnostic = Diagnostic::new(UndocumentedWarn, expr.range());
|
||||
if checker.patch(diagnostic.kind.rule()) {
|
||||
diagnostic.try_set_fix(|| {
|
||||
let (import_edit, binding) = checker.importer().get_or_import_symbol(
|
||||
&ImportRequest::import("logging", "WARNING"),
|
||||
expr.start(),
|
||||
checker.semantic(),
|
||||
)?;
|
||||
let reference_edit = Edit::range_replacement(binding, expr.range());
|
||||
Ok(Fix::safe_edits(import_edit, [reference_edit]))
|
||||
});
|
||||
}
|
||||
diagnostic.try_set_fix(|| {
|
||||
let (import_edit, binding) = checker.importer().get_or_import_symbol(
|
||||
&ImportRequest::import("logging", "WARNING"),
|
||||
expr.start(),
|
||||
checker.semantic(),
|
||||
)?;
|
||||
let reference_edit = Edit::range_replacement(binding, expr.range());
|
||||
Ok(Fix::safe_edits(import_edit, [reference_edit]))
|
||||
});
|
||||
checker.diagnostics.push(diagnostic);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,7 +5,7 @@ use ruff_python_stdlib::logging::LoggingLevel;
|
|||
use ruff_text_size::Ranged;
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::registry::{AsRule, Rule};
|
||||
use crate::registry::Rule;
|
||||
use crate::rules::flake8_logging_format::violations::{
|
||||
LoggingExcInfo, LoggingExtraAttrClash, LoggingFString, LoggingPercentFormat,
|
||||
LoggingRedundantExcInfo, LoggingStringConcat, LoggingStringFormat, LoggingWarn,
|
||||
|
@ -196,12 +196,10 @@ pub(crate) fn logging_call(checker: &mut Checker, call: &ast::ExprCall) {
|
|||
LoggingCallType::LevelCall(LoggingLevel::Warn)
|
||||
) {
|
||||
let mut diagnostic = Diagnostic::new(LoggingWarn, range);
|
||||
if checker.patch(diagnostic.kind.rule()) {
|
||||
diagnostic.set_fix(Fix::safe_edit(Edit::range_replacement(
|
||||
"warning".to_string(),
|
||||
range,
|
||||
)));
|
||||
}
|
||||
diagnostic.set_fix(Fix::safe_edit(Edit::range_replacement(
|
||||
"warning".to_string(),
|
||||
range,
|
||||
)));
|
||||
checker.diagnostics.push(diagnostic);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,7 +8,6 @@ use ruff_text_size::Ranged;
|
|||
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::fix;
|
||||
use crate::registry::AsRule;
|
||||
|
||||
/// ## What it does
|
||||
/// Checks for duplicate field definitions in classes.
|
||||
|
@ -79,13 +78,11 @@ pub(crate) fn duplicate_class_field_definition(checker: &mut Checker, body: &[St
|
|||
},
|
||||
stmt.range(),
|
||||
);
|
||||
if checker.patch(diagnostic.kind.rule()) {
|
||||
let edit =
|
||||
fix::edits::delete_stmt(stmt, Some(stmt), checker.locator(), checker.indexer());
|
||||
diagnostic.set_fix(Fix::unsafe_edit(edit).isolate(Checker::isolation(
|
||||
checker.semantic().current_statement_id(),
|
||||
)));
|
||||
}
|
||||
let edit =
|
||||
fix::edits::delete_stmt(stmt, Some(stmt), checker.locator(), checker.indexer());
|
||||
diagnostic.set_fix(Fix::unsafe_edit(edit).isolate(Checker::isolation(
|
||||
checker.semantic().current_statement_id(),
|
||||
)));
|
||||
checker.diagnostics.push(diagnostic);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,7 +12,6 @@ use ruff_diagnostics::{Diagnostic, Edit, Fix};
|
|||
use ruff_macros::{derive_message_formats, violation};
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::registry::AsRule;
|
||||
|
||||
/// ## What it does
|
||||
/// Checks for `startswith` or `endswith` calls on the same value with
|
||||
|
@ -115,92 +114,90 @@ pub(crate) fn multiple_starts_ends_with(checker: &mut Checker, expr: &Expr) {
|
|||
},
|
||||
expr.range(),
|
||||
);
|
||||
if checker.patch(diagnostic.kind.rule()) {
|
||||
let words: Vec<&Expr> = indices
|
||||
let words: Vec<&Expr> = indices
|
||||
.iter()
|
||||
.map(|index| &values[*index])
|
||||
.map(|expr| {
|
||||
let Expr::Call(ast::ExprCall {
|
||||
func: _,
|
||||
arguments:
|
||||
Arguments {
|
||||
args,
|
||||
keywords: _,
|
||||
range: _,
|
||||
},
|
||||
range: _,
|
||||
}) = expr
|
||||
else {
|
||||
unreachable!(
|
||||
"{}",
|
||||
format!("Indices should only contain `{attr_name}` calls")
|
||||
)
|
||||
};
|
||||
args.get(0)
|
||||
.unwrap_or_else(|| panic!("`{attr_name}` should have one argument"))
|
||||
})
|
||||
.collect();
|
||||
|
||||
let node = Expr::Tuple(ast::ExprTuple {
|
||||
elts: words
|
||||
.iter()
|
||||
.map(|index| &values[*index])
|
||||
.map(|expr| {
|
||||
let Expr::Call(ast::ExprCall {
|
||||
func: _,
|
||||
arguments:
|
||||
Arguments {
|
||||
args,
|
||||
keywords: _,
|
||||
range: _,
|
||||
},
|
||||
range: _,
|
||||
}) = expr
|
||||
else {
|
||||
unreachable!(
|
||||
"{}",
|
||||
format!("Indices should only contain `{attr_name}` calls")
|
||||
)
|
||||
};
|
||||
args.get(0)
|
||||
.unwrap_or_else(|| panic!("`{attr_name}` should have one argument"))
|
||||
.flat_map(|value| {
|
||||
if let Expr::Tuple(ast::ExprTuple { elts, .. }) = value {
|
||||
Left(elts.iter())
|
||||
} else {
|
||||
Right(iter::once(*value))
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
.map(Clone::clone)
|
||||
.collect(),
|
||||
ctx: ExprContext::Load,
|
||||
range: TextRange::default(),
|
||||
});
|
||||
let node1 = Expr::Name(ast::ExprName {
|
||||
id: arg_name.into(),
|
||||
ctx: ExprContext::Load,
|
||||
range: TextRange::default(),
|
||||
});
|
||||
let node2 = Expr::Attribute(ast::ExprAttribute {
|
||||
value: Box::new(node1),
|
||||
attr: Identifier::new(attr_name.to_string(), TextRange::default()),
|
||||
ctx: ExprContext::Load,
|
||||
range: TextRange::default(),
|
||||
});
|
||||
let node3 = Expr::Call(ast::ExprCall {
|
||||
func: Box::new(node2),
|
||||
arguments: Arguments {
|
||||
args: vec![node],
|
||||
keywords: vec![],
|
||||
range: TextRange::default(),
|
||||
},
|
||||
range: TextRange::default(),
|
||||
});
|
||||
let call = node3;
|
||||
|
||||
let node = Expr::Tuple(ast::ExprTuple {
|
||||
elts: words
|
||||
.iter()
|
||||
.flat_map(|value| {
|
||||
if let Expr::Tuple(ast::ExprTuple { elts, .. }) = value {
|
||||
Left(elts.iter())
|
||||
} else {
|
||||
Right(iter::once(*value))
|
||||
}
|
||||
})
|
||||
.map(Clone::clone)
|
||||
.collect(),
|
||||
ctx: ExprContext::Load,
|
||||
range: TextRange::default(),
|
||||
});
|
||||
let node1 = Expr::Name(ast::ExprName {
|
||||
id: arg_name.into(),
|
||||
ctx: ExprContext::Load,
|
||||
range: TextRange::default(),
|
||||
});
|
||||
let node2 = Expr::Attribute(ast::ExprAttribute {
|
||||
value: Box::new(node1),
|
||||
attr: Identifier::new(attr_name.to_string(), TextRange::default()),
|
||||
ctx: ExprContext::Load,
|
||||
range: TextRange::default(),
|
||||
});
|
||||
let node3 = Expr::Call(ast::ExprCall {
|
||||
func: Box::new(node2),
|
||||
arguments: Arguments {
|
||||
args: vec![node],
|
||||
keywords: vec![],
|
||||
range: TextRange::default(),
|
||||
},
|
||||
range: TextRange::default(),
|
||||
});
|
||||
let call = node3;
|
||||
|
||||
// Generate the combined `BoolOp`.
|
||||
let mut call = Some(call);
|
||||
let node = Expr::BoolOp(ast::ExprBoolOp {
|
||||
op: BoolOp::Or,
|
||||
values: values
|
||||
.iter()
|
||||
.enumerate()
|
||||
.filter_map(|(index, elt)| {
|
||||
if indices.contains(&index) {
|
||||
std::mem::take(&mut call)
|
||||
} else {
|
||||
Some(elt.clone())
|
||||
}
|
||||
})
|
||||
.collect(),
|
||||
range: TextRange::default(),
|
||||
});
|
||||
let bool_op = node;
|
||||
diagnostic.set_fix(Fix::unsafe_edit(Edit::range_replacement(
|
||||
checker.generator().expr(&bool_op),
|
||||
expr.range(),
|
||||
)));
|
||||
}
|
||||
// Generate the combined `BoolOp`.
|
||||
let mut call = Some(call);
|
||||
let node = Expr::BoolOp(ast::ExprBoolOp {
|
||||
op: BoolOp::Or,
|
||||
values: values
|
||||
.iter()
|
||||
.enumerate()
|
||||
.filter_map(|(index, elt)| {
|
||||
if indices.contains(&index) {
|
||||
std::mem::take(&mut call)
|
||||
} else {
|
||||
Some(elt.clone())
|
||||
}
|
||||
})
|
||||
.collect(),
|
||||
range: TextRange::default(),
|
||||
});
|
||||
let bool_op = node;
|
||||
diagnostic.set_fix(Fix::unsafe_edit(Edit::range_replacement(
|
||||
checker.generator().expr(&bool_op),
|
||||
expr.range(),
|
||||
)));
|
||||
checker.diagnostics.push(diagnostic);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,7 +6,6 @@ use ruff_macros::{derive_message_formats, violation};
|
|||
use ruff_text_size::Ranged;
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::registry::AsRule;
|
||||
|
||||
/// ## What it does
|
||||
/// Checks for lambdas that can be replaced with the `list` builtin.
|
||||
|
@ -64,13 +63,11 @@ pub(crate) fn reimplemented_list_builtin(checker: &mut Checker, expr: &ExprLambd
|
|||
if let Expr::List(ast::ExprList { elts, .. }) = body.as_ref() {
|
||||
if elts.is_empty() {
|
||||
let mut diagnostic = Diagnostic::new(ReimplementedListBuiltin, expr.range());
|
||||
if checker.patch(diagnostic.kind.rule()) {
|
||||
if checker.semantic().is_builtin("list") {
|
||||
diagnostic.set_fix(Fix::safe_edit(Edit::range_replacement(
|
||||
"list".to_string(),
|
||||
expr.range(),
|
||||
)));
|
||||
}
|
||||
if checker.semantic().is_builtin("list") {
|
||||
diagnostic.set_fix(Fix::safe_edit(Edit::range_replacement(
|
||||
"list".to_string(),
|
||||
expr.range(),
|
||||
)));
|
||||
}
|
||||
checker.diagnostics.push(diagnostic);
|
||||
}
|
||||
|
|
|
@ -8,7 +8,6 @@ use ruff_text_size::Ranged;
|
|||
use ruff_python_stdlib::identifiers::is_identifier;
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::registry::AsRule;
|
||||
|
||||
/// ## What it does
|
||||
/// Checks for unnecessary `dict` kwargs.
|
||||
|
@ -68,12 +67,10 @@ pub(crate) fn unnecessary_dict_kwargs(checker: &mut Checker, expr: &Expr, kwargs
|
|||
if matches!(keys.as_slice(), [None]) {
|
||||
let mut diagnostic = Diagnostic::new(UnnecessaryDictKwargs, expr.range());
|
||||
|
||||
if checker.patch(diagnostic.kind.rule()) {
|
||||
diagnostic.set_fix(Fix::safe_edit(Edit::range_replacement(
|
||||
format!("**{}", checker.locator().slice(values[0].range())),
|
||||
kw.range(),
|
||||
)));
|
||||
}
|
||||
diagnostic.set_fix(Fix::safe_edit(Edit::range_replacement(
|
||||
format!("**{}", checker.locator().slice(values[0].range())),
|
||||
kw.range(),
|
||||
)));
|
||||
|
||||
checker.diagnostics.push(diagnostic);
|
||||
continue;
|
||||
|
@ -91,18 +88,16 @@ pub(crate) fn unnecessary_dict_kwargs(checker: &mut Checker, expr: &Expr, kwargs
|
|||
|
||||
let mut diagnostic = Diagnostic::new(UnnecessaryDictKwargs, expr.range());
|
||||
|
||||
if checker.patch(diagnostic.kind.rule()) {
|
||||
diagnostic.set_fix(Fix::safe_edit(Edit::range_replacement(
|
||||
kwargs
|
||||
.iter()
|
||||
.zip(values.iter())
|
||||
.map(|(kwarg, value)| {
|
||||
format!("{}={}", kwarg.value, checker.locator().slice(value.range()))
|
||||
})
|
||||
.join(", "),
|
||||
kw.range(),
|
||||
)));
|
||||
}
|
||||
diagnostic.set_fix(Fix::safe_edit(Edit::range_replacement(
|
||||
kwargs
|
||||
.iter()
|
||||
.zip(values.iter())
|
||||
.map(|(kwarg, value)| {
|
||||
format!("{}={}", kwarg.value, checker.locator().slice(value.range()))
|
||||
})
|
||||
.join(", "),
|
||||
kw.range(),
|
||||
)));
|
||||
|
||||
checker.diagnostics.push(diagnostic);
|
||||
}
|
||||
|
|
|
@ -8,7 +8,6 @@ use ruff_text_size::Ranged;
|
|||
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::fix;
|
||||
use crate::registry::AsRule;
|
||||
|
||||
/// ## What it does
|
||||
/// Checks for unnecessary `pass` statements in functions, classes, and other
|
||||
|
@ -63,17 +62,14 @@ pub(crate) fn no_unnecessary_pass(checker: &mut Checker, body: &[Stmt]) {
|
|||
.filter(|stmt| stmt.is_pass_stmt())
|
||||
.for_each(|stmt| {
|
||||
let mut diagnostic = Diagnostic::new(UnnecessaryPass, stmt.range());
|
||||
if checker.patch(diagnostic.kind.rule()) {
|
||||
let edit =
|
||||
if let Some(index) = trailing_comment_start_offset(stmt, checker.locator()) {
|
||||
Edit::range_deletion(stmt.range().add_end(index))
|
||||
} else {
|
||||
fix::edits::delete_stmt(stmt, None, checker.locator(), checker.indexer())
|
||||
};
|
||||
diagnostic.set_fix(Fix::safe_edit(edit).isolate(Checker::isolation(
|
||||
checker.semantic().current_statement_id(),
|
||||
)));
|
||||
}
|
||||
let edit = if let Some(index) = trailing_comment_start_offset(stmt, checker.locator()) {
|
||||
Edit::range_deletion(stmt.range().add_end(index))
|
||||
} else {
|
||||
fix::edits::delete_stmt(stmt, None, checker.locator(), checker.indexer())
|
||||
};
|
||||
diagnostic.set_fix(Fix::safe_edit(edit).isolate(Checker::isolation(
|
||||
checker.semantic().current_statement_id(),
|
||||
)));
|
||||
checker.diagnostics.push(diagnostic);
|
||||
});
|
||||
}
|
||||
|
|
|
@ -6,7 +6,6 @@ use ruff_text_size::Ranged;
|
|||
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::fix::edits::{remove_argument, Parentheses};
|
||||
use crate::registry::AsRule;
|
||||
|
||||
/// ## What it does
|
||||
/// Checks for `range` calls with an unnecessary `start` argument.
|
||||
|
@ -78,16 +77,14 @@ pub(crate) fn unnecessary_range_start(checker: &mut Checker, call: &ast::ExprCal
|
|||
};
|
||||
|
||||
let mut diagnostic = Diagnostic::new(UnnecessaryRangeStart, start.range());
|
||||
if checker.patch(diagnostic.kind.rule()) {
|
||||
diagnostic.try_set_fix(|| {
|
||||
remove_argument(
|
||||
&start,
|
||||
&call.arguments,
|
||||
Parentheses::Preserve,
|
||||
checker.locator().contents(),
|
||||
)
|
||||
.map(Fix::safe_edit)
|
||||
});
|
||||
}
|
||||
diagnostic.try_set_fix(|| {
|
||||
remove_argument(
|
||||
&start,
|
||||
&call.arguments,
|
||||
Parentheses::Preserve,
|
||||
checker.locator().contents(),
|
||||
)
|
||||
.map(Fix::safe_edit)
|
||||
});
|
||||
checker.diagnostics.push(diagnostic);
|
||||
}
|
||||
|
|
|
@ -5,7 +5,6 @@ use ruff_macros::{derive_message_formats, violation};
|
|||
use ruff_text_size::Ranged;
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::registry::AsRule;
|
||||
|
||||
/// ## What it does
|
||||
/// Checks for `__eq__` and `__ne__` implementations that use `typing.Any` as
|
||||
|
@ -78,14 +77,12 @@ pub(crate) fn any_eq_ne_annotation(checker: &mut Checker, name: &str, parameters
|
|||
},
|
||||
annotation.range(),
|
||||
);
|
||||
if checker.patch(diagnostic.kind.rule()) {
|
||||
// Ex) `def __eq__(self, obj: Any): ...`
|
||||
if checker.semantic().is_builtin("object") {
|
||||
diagnostic.set_fix(Fix::safe_edit(Edit::range_replacement(
|
||||
"object".to_string(),
|
||||
annotation.range(),
|
||||
)));
|
||||
}
|
||||
// Ex) `def __eq__(self, obj: Any): ...`
|
||||
if checker.semantic().is_builtin("object") {
|
||||
diagnostic.set_fix(Fix::safe_edit(Edit::range_replacement(
|
||||
"object".to_string(),
|
||||
annotation.range(),
|
||||
)));
|
||||
}
|
||||
checker.diagnostics.push(diagnostic);
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@ use rustc_hash::FxHashSet;
|
|||
use std::collections::HashSet;
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::registry::AsRule;
|
||||
|
||||
use crate::rules::flake8_pyi::helpers::traverse_union;
|
||||
use ruff_diagnostics::{Diagnostic, Edit, Fix, FixAvailability, Violation};
|
||||
use ruff_macros::{derive_message_formats, violation};
|
||||
|
@ -64,19 +64,17 @@ pub(crate) fn duplicate_union_member<'a>(checker: &mut Checker, expr: &'a Expr)
|
|||
},
|
||||
expr.range(),
|
||||
);
|
||||
if checker.patch(diagnostic.kind.rule()) {
|
||||
// Delete the "|" character as well as the duplicate value by reconstructing the
|
||||
// parent without the duplicate.
|
||||
// Delete the "|" character as well as the duplicate value by reconstructing the
|
||||
// parent without the duplicate.
|
||||
|
||||
// If the parent node is not a `BinOp` we will not perform a fix
|
||||
if let Some(parent @ Expr::BinOp(ast::ExprBinOp { left, right, .. })) = parent {
|
||||
// Replace the parent with its non-duplicate child.
|
||||
let child = if expr == left.as_ref() { right } else { left };
|
||||
diagnostic.set_fix(Fix::safe_edit(Edit::range_replacement(
|
||||
checker.locator().slice(child.as_ref()).to_string(),
|
||||
parent.range(),
|
||||
)));
|
||||
}
|
||||
// If the parent node is not a `BinOp` we will not perform a fix
|
||||
if let Some(parent @ Expr::BinOp(ast::ExprBinOp { left, right, .. })) = parent {
|
||||
// Replace the parent with its non-duplicate child.
|
||||
let child = if expr == left.as_ref() { right } else { left };
|
||||
diagnostic.set_fix(Fix::safe_edit(Edit::range_replacement(
|
||||
checker.locator().slice(child.as_ref()).to_string(),
|
||||
parent.range(),
|
||||
)));
|
||||
}
|
||||
diagnostics.push(diagnostic);
|
||||
}
|
||||
|
|
|
@ -5,7 +5,6 @@ use ruff_text_size::Ranged;
|
|||
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::fix;
|
||||
use crate::registry::AsRule;
|
||||
|
||||
/// ## What it does
|
||||
/// Removes ellipses (`...`) in otherwise non-empty class bodies.
|
||||
|
@ -63,13 +62,11 @@ pub(crate) fn ellipsis_in_non_empty_class_body(checker: &mut Checker, body: &[St
|
|||
})
|
||||
) {
|
||||
let mut diagnostic = Diagnostic::new(EllipsisInNonEmptyClassBody, stmt.range());
|
||||
if checker.patch(diagnostic.kind.rule()) {
|
||||
let edit =
|
||||
fix::edits::delete_stmt(stmt, Some(stmt), checker.locator(), checker.indexer());
|
||||
diagnostic.set_fix(Fix::safe_edit(edit).isolate(Checker::isolation(
|
||||
checker.semantic().current_statement_id(),
|
||||
)));
|
||||
}
|
||||
let edit =
|
||||
fix::edits::delete_stmt(stmt, Some(stmt), checker.locator(), checker.indexer());
|
||||
diagnostic.set_fix(Fix::safe_edit(edit).isolate(Checker::isolation(
|
||||
checker.semantic().current_statement_id(),
|
||||
)));
|
||||
checker.diagnostics.push(diagnostic);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,7 +13,6 @@ use ruff_python_semantic::SemanticModel;
|
|||
use ruff_text_size::Ranged;
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::registry::AsRule;
|
||||
|
||||
/// ## What it does
|
||||
/// Checks for incorrect function signatures on `__exit__` and `__aexit__`
|
||||
|
@ -175,13 +174,11 @@ fn check_short_args_list(checker: &mut Checker, parameters: &Parameters, func_ki
|
|||
annotation.range(),
|
||||
);
|
||||
|
||||
if checker.patch(diagnostic.kind.rule()) {
|
||||
if checker.semantic().is_builtin("object") {
|
||||
diagnostic.set_fix(Fix::safe_edit(Edit::range_replacement(
|
||||
"object".to_string(),
|
||||
annotation.range(),
|
||||
)));
|
||||
}
|
||||
if checker.semantic().is_builtin("object") {
|
||||
diagnostic.set_fix(Fix::safe_edit(Edit::range_replacement(
|
||||
"object".to_string(),
|
||||
annotation.range(),
|
||||
)));
|
||||
}
|
||||
|
||||
checker.diagnostics.push(diagnostic);
|
||||
|
|
|
@ -5,7 +5,6 @@ use ruff_python_ast::{self as ast, Expr, Stmt};
|
|||
use ruff_text_size::Ranged;
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::registry::Rule;
|
||||
|
||||
/// ## What it does
|
||||
/// Checks for non-empty function stub bodies.
|
||||
|
@ -69,11 +68,9 @@ pub(crate) fn non_empty_stub_body(checker: &mut Checker, body: &[Stmt]) {
|
|||
}
|
||||
|
||||
let mut diagnostic = Diagnostic::new(NonEmptyStubBody, stmt.range());
|
||||
if checker.patch(Rule::NonEmptyStubBody) {
|
||||
diagnostic.set_fix(Fix::safe_edit(Edit::range_replacement(
|
||||
format!("..."),
|
||||
stmt.range(),
|
||||
)));
|
||||
};
|
||||
diagnostic.set_fix(Fix::safe_edit(Edit::range_replacement(
|
||||
format!("..."),
|
||||
stmt.range(),
|
||||
)));
|
||||
checker.diagnostics.push(diagnostic);
|
||||
}
|
||||
|
|
|
@ -5,7 +5,6 @@ use ruff_diagnostics::{AlwaysFixableViolation, Diagnostic, Edit, Fix};
|
|||
use ruff_macros::{derive_message_formats, violation};
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::registry::AsRule;
|
||||
|
||||
/// ## What it does
|
||||
/// Checks for numeric literals with a string representation longer than ten
|
||||
|
@ -50,11 +49,9 @@ pub(crate) fn numeric_literal_too_long(checker: &mut Checker, expr: &Expr) {
|
|||
}
|
||||
|
||||
let mut diagnostic = Diagnostic::new(NumericLiteralTooLong, expr.range());
|
||||
if checker.patch(diagnostic.kind.rule()) {
|
||||
diagnostic.set_fix(Fix::safe_edit(Edit::range_replacement(
|
||||
"...".to_string(),
|
||||
expr.range(),
|
||||
)));
|
||||
}
|
||||
diagnostic.set_fix(Fix::safe_edit(Edit::range_replacement(
|
||||
"...".to_string(),
|
||||
expr.range(),
|
||||
)));
|
||||
checker.diagnostics.push(diagnostic);
|
||||
}
|
||||
|
|
|
@ -5,7 +5,6 @@ use ruff_text_size::Ranged;
|
|||
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::fix;
|
||||
use crate::registry::AsRule;
|
||||
|
||||
/// ## What it does
|
||||
/// Checks for the presence of the `pass` statement within a class body
|
||||
|
@ -60,13 +59,10 @@ pub(crate) fn pass_in_class_body(checker: &mut Checker, class_def: &ast::StmtCla
|
|||
}
|
||||
|
||||
let mut diagnostic = Diagnostic::new(PassInClassBody, stmt.range());
|
||||
if checker.patch(diagnostic.kind.rule()) {
|
||||
let edit =
|
||||
fix::edits::delete_stmt(stmt, Some(stmt), checker.locator(), checker.indexer());
|
||||
diagnostic.set_fix(Fix::safe_edit(edit).isolate(Checker::isolation(
|
||||
checker.semantic().current_statement_id(),
|
||||
)));
|
||||
}
|
||||
let edit = fix::edits::delete_stmt(stmt, Some(stmt), checker.locator(), checker.indexer());
|
||||
diagnostic.set_fix(Fix::safe_edit(edit).isolate(Checker::isolation(
|
||||
checker.semantic().current_statement_id(),
|
||||
)));
|
||||
checker.diagnostics.push(diagnostic);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,7 +4,6 @@ use ruff_python_ast::Stmt;
|
|||
use ruff_text_size::Ranged;
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::registry::Rule;
|
||||
|
||||
/// ## What it does
|
||||
/// Checks for `pass` statements in empty stub bodies.
|
||||
|
@ -47,11 +46,9 @@ pub(crate) fn pass_statement_stub_body(checker: &mut Checker, body: &[Stmt]) {
|
|||
};
|
||||
|
||||
let mut diagnostic = Diagnostic::new(PassStatementStubBody, pass.range());
|
||||
if checker.patch(Rule::PassStatementStubBody) {
|
||||
diagnostic.set_fix(Fix::safe_edit(Edit::range_replacement(
|
||||
format!("..."),
|
||||
pass.range(),
|
||||
)));
|
||||
};
|
||||
diagnostic.set_fix(Fix::safe_edit(Edit::range_replacement(
|
||||
format!("..."),
|
||||
pass.range(),
|
||||
)));
|
||||
checker.diagnostics.push(diagnostic);
|
||||
}
|
||||
|
|
|
@ -4,7 +4,6 @@ use ruff_diagnostics::{AlwaysFixableViolation, Diagnostic, Edit, Fix};
|
|||
use ruff_macros::{derive_message_formats, violation};
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::registry::Rule;
|
||||
|
||||
/// ## What it does
|
||||
/// Checks for quoted type annotations in stub (`.pyi`) files, which should be avoided.
|
||||
|
@ -43,11 +42,9 @@ impl AlwaysFixableViolation for QuotedAnnotationInStub {
|
|||
/// PYI020
|
||||
pub(crate) fn quoted_annotation_in_stub(checker: &mut Checker, annotation: &str, range: TextRange) {
|
||||
let mut diagnostic = Diagnostic::new(QuotedAnnotationInStub, range);
|
||||
if checker.patch(Rule::QuotedAnnotationInStub) {
|
||||
diagnostic.set_fix(Fix::safe_edit(Edit::range_replacement(
|
||||
annotation.to_string(),
|
||||
range,
|
||||
)));
|
||||
}
|
||||
diagnostic.set_fix(Fix::safe_edit(Edit::range_replacement(
|
||||
annotation.to_string(),
|
||||
range,
|
||||
)));
|
||||
checker.diagnostics.push(diagnostic);
|
||||
}
|
||||
|
|
|
@ -11,7 +11,7 @@ use ruff_text_size::Ranged;
|
|||
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::importer::ImportRequest;
|
||||
use crate::registry::AsRule;
|
||||
|
||||
use crate::rules::flake8_pyi::rules::TypingModule;
|
||||
use crate::settings::types::PythonVersion;
|
||||
|
||||
|
@ -534,12 +534,10 @@ pub(crate) fn typed_argument_simple_defaults(checker: &mut Checker, parameters:
|
|||
) {
|
||||
let mut diagnostic = Diagnostic::new(TypedArgumentDefaultInStub, default.range());
|
||||
|
||||
if checker.patch(diagnostic.kind.rule()) {
|
||||
diagnostic.set_fix(Fix::safe_edit(Edit::range_replacement(
|
||||
"...".to_string(),
|
||||
default.range(),
|
||||
)));
|
||||
}
|
||||
diagnostic.set_fix(Fix::safe_edit(Edit::range_replacement(
|
||||
"...".to_string(),
|
||||
default.range(),
|
||||
)));
|
||||
|
||||
checker.diagnostics.push(diagnostic);
|
||||
}
|
||||
|
@ -571,12 +569,10 @@ pub(crate) fn argument_simple_defaults(checker: &mut Checker, parameters: &Param
|
|||
) {
|
||||
let mut diagnostic = Diagnostic::new(ArgumentDefaultInStub, default.range());
|
||||
|
||||
if checker.patch(diagnostic.kind.rule()) {
|
||||
diagnostic.set_fix(Fix::safe_edit(Edit::range_replacement(
|
||||
"...".to_string(),
|
||||
default.range(),
|
||||
)));
|
||||
}
|
||||
diagnostic.set_fix(Fix::safe_edit(Edit::range_replacement(
|
||||
"...".to_string(),
|
||||
default.range(),
|
||||
)));
|
||||
|
||||
checker.diagnostics.push(diagnostic);
|
||||
}
|
||||
|
@ -606,12 +602,10 @@ pub(crate) fn assignment_default_in_stub(checker: &mut Checker, targets: &[Expr]
|
|||
}
|
||||
|
||||
let mut diagnostic = Diagnostic::new(AssignmentDefaultInStub, value.range());
|
||||
if checker.patch(diagnostic.kind.rule()) {
|
||||
diagnostic.set_fix(Fix::safe_edit(Edit::range_replacement(
|
||||
"...".to_string(),
|
||||
value.range(),
|
||||
)));
|
||||
}
|
||||
diagnostic.set_fix(Fix::safe_edit(Edit::range_replacement(
|
||||
"...".to_string(),
|
||||
value.range(),
|
||||
)));
|
||||
checker.diagnostics.push(diagnostic);
|
||||
}
|
||||
|
||||
|
@ -642,12 +636,10 @@ pub(crate) fn annotated_assignment_default_in_stub(
|
|||
}
|
||||
|
||||
let mut diagnostic = Diagnostic::new(AssignmentDefaultInStub, value.range());
|
||||
if checker.patch(diagnostic.kind.rule()) {
|
||||
diagnostic.set_fix(Fix::safe_edit(Edit::range_replacement(
|
||||
"...".to_string(),
|
||||
value.range(),
|
||||
)));
|
||||
}
|
||||
diagnostic.set_fix(Fix::safe_edit(Edit::range_replacement(
|
||||
"...".to_string(),
|
||||
value.range(),
|
||||
)));
|
||||
checker.diagnostics.push(diagnostic);
|
||||
}
|
||||
|
||||
|
@ -741,18 +733,16 @@ pub(crate) fn type_alias_without_annotation(checker: &mut Checker, value: &Expr,
|
|||
},
|
||||
target.range(),
|
||||
);
|
||||
if checker.patch(diagnostic.kind.rule()) {
|
||||
diagnostic.try_set_fix(|| {
|
||||
let (import_edit, binding) = checker.importer().get_or_import_symbol(
|
||||
&ImportRequest::import(module.as_str(), "TypeAlias"),
|
||||
target.start(),
|
||||
checker.semantic(),
|
||||
)?;
|
||||
Ok(Fix::safe_edits(
|
||||
Edit::range_replacement(format!("{id}: {binding}"), target.range()),
|
||||
[import_edit],
|
||||
))
|
||||
});
|
||||
}
|
||||
diagnostic.try_set_fix(|| {
|
||||
let (import_edit, binding) = checker.importer().get_or_import_symbol(
|
||||
&ImportRequest::import(module.as_str(), "TypeAlias"),
|
||||
target.start(),
|
||||
checker.semantic(),
|
||||
)?;
|
||||
Ok(Fix::safe_edits(
|
||||
Edit::range_replacement(format!("{id}: {binding}"), target.range()),
|
||||
[import_edit],
|
||||
))
|
||||
});
|
||||
checker.diagnostics.push(diagnostic);
|
||||
}
|
||||
|
|
|
@ -8,7 +8,6 @@ use ruff_python_semantic::analyze::visibility::is_abstract;
|
|||
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::fix::edits::delete_stmt;
|
||||
use crate::registry::AsRule;
|
||||
|
||||
/// ## What it does
|
||||
/// Checks for redundant definitions of `__str__` or `__repr__` in stubs.
|
||||
|
@ -95,13 +94,11 @@ pub(crate) fn str_or_repr_defined_in_stub(checker: &mut Checker, stmt: &Stmt) {
|
|||
},
|
||||
stmt.identifier(),
|
||||
);
|
||||
if checker.patch(diagnostic.kind.rule()) {
|
||||
let stmt = checker.semantic().current_statement();
|
||||
let parent = checker.semantic().current_statement_parent();
|
||||
let edit = delete_stmt(stmt, parent, checker.locator(), checker.indexer());
|
||||
diagnostic.set_fix(Fix::safe_edit(edit).isolate(Checker::isolation(
|
||||
checker.semantic().current_statement_parent_id(),
|
||||
)));
|
||||
}
|
||||
let stmt = checker.semantic().current_statement();
|
||||
let parent = checker.semantic().current_statement_parent();
|
||||
let edit = delete_stmt(stmt, parent, checker.locator(), checker.indexer());
|
||||
diagnostic.set_fix(Fix::safe_edit(edit).isolate(Checker::isolation(
|
||||
checker.semantic().current_statement_parent_id(),
|
||||
)));
|
||||
checker.diagnostics.push(diagnostic);
|
||||
}
|
||||
|
|
|
@ -6,7 +6,6 @@ use ruff_python_ast::helpers::is_docstring_stmt;
|
|||
use ruff_text_size::Ranged;
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::registry::AsRule;
|
||||
|
||||
/// ## What it does
|
||||
/// Checks for the use of string and bytes literals longer than 50 characters
|
||||
|
@ -67,11 +66,9 @@ pub(crate) fn string_or_bytes_too_long(checker: &mut Checker, expr: &Expr) {
|
|||
}
|
||||
|
||||
let mut diagnostic = Diagnostic::new(StringOrBytesTooLong, expr.range());
|
||||
if checker.patch(diagnostic.kind.rule()) {
|
||||
diagnostic.set_fix(Fix::safe_edit(Edit::range_replacement(
|
||||
"...".to_string(),
|
||||
expr.range(),
|
||||
)));
|
||||
}
|
||||
diagnostic.set_fix(Fix::safe_edit(Edit::range_replacement(
|
||||
"...".to_string(),
|
||||
expr.range(),
|
||||
)));
|
||||
checker.diagnostics.push(diagnostic);
|
||||
}
|
||||
|
|
|
@ -5,7 +5,7 @@ use ruff_python_semantic::{Binding, BindingKind};
|
|||
use ruff_text_size::Ranged;
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::registry::AsRule;
|
||||
|
||||
use crate::renamer::Renamer;
|
||||
|
||||
/// ## What it does
|
||||
|
@ -65,14 +65,12 @@ pub(crate) fn unaliased_collections_abc_set_import(
|
|||
}
|
||||
|
||||
let mut diagnostic = Diagnostic::new(UnaliasedCollectionsAbcSetImport, binding.range());
|
||||
if checker.patch(diagnostic.kind.rule()) {
|
||||
if checker.semantic().is_available("AbstractSet") {
|
||||
diagnostic.try_set_fix(|| {
|
||||
let scope = &checker.semantic().scopes[binding.scope];
|
||||
let (edit, rest) = Renamer::rename(name, "AbstractSet", scope, checker.semantic())?;
|
||||
Ok(Fix::unsafe_edits(edit, rest))
|
||||
});
|
||||
}
|
||||
if checker.semantic().is_available("AbstractSet") {
|
||||
diagnostic.try_set_fix(|| {
|
||||
let scope = &checker.semantic().scopes[binding.scope];
|
||||
let (edit, rest) = Renamer::rename(name, "AbstractSet", scope, checker.semantic())?;
|
||||
Ok(Fix::unsafe_edits(edit, rest))
|
||||
});
|
||||
}
|
||||
Some(diagnostic)
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@ use ruff_python_ast::{self as ast, Expr};
|
|||
use ruff_text_size::Ranged;
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::registry::AsRule;
|
||||
|
||||
use crate::rules::flake8_pyi::helpers::traverse_union;
|
||||
|
||||
/// ## What it does
|
||||
|
@ -76,12 +76,10 @@ pub(crate) fn unnecessary_literal_union<'a>(checker: &mut Checker, expr: &'a Exp
|
|||
expr.range(),
|
||||
);
|
||||
|
||||
if checker.patch(diagnostic.kind.rule()) {
|
||||
diagnostic.set_fix(Fix::safe_edit(Edit::range_replacement(
|
||||
format!("Literal[{}]", literal_members.join(", ")),
|
||||
expr.range(),
|
||||
)));
|
||||
}
|
||||
diagnostic.set_fix(Fix::safe_edit(Edit::range_replacement(
|
||||
format!("Literal[{}]", literal_members.join(", ")),
|
||||
expr.range(),
|
||||
)));
|
||||
|
||||
checker.diagnostics.push(diagnostic);
|
||||
}
|
||||
|
|
|
@ -26,7 +26,6 @@ use crate::cst::matchers::match_indented_block;
|
|||
use crate::cst::matchers::match_module;
|
||||
use crate::fix::codemods::CodegenStylist;
|
||||
use crate::importer::ImportRequest;
|
||||
use crate::registry::AsRule;
|
||||
|
||||
use super::unittest_assert::UnittestAssert;
|
||||
|
||||
|
@ -284,25 +283,23 @@ pub(crate) fn unittest_assertion(
|
|||
},
|
||||
func.range(),
|
||||
);
|
||||
if checker.patch(diagnostic.kind.rule()) {
|
||||
// We're converting an expression to a statement, so avoid applying the fix if
|
||||
// the assertion is part of a larger expression.
|
||||
if checker.semantic().current_statement().is_expr_stmt()
|
||||
&& checker.semantic().current_expression_parent().is_none()
|
||||
&& !checker.indexer().comment_ranges().intersects(expr.range())
|
||||
{
|
||||
if let Ok(stmt) = unittest_assert.generate_assert(args, keywords) {
|
||||
diagnostic.set_fix(Fix::unsafe_edit(Edit::range_replacement(
|
||||
checker.generator().stmt(&stmt),
|
||||
parenthesized_range(
|
||||
expr.into(),
|
||||
checker.semantic().current_statement().into(),
|
||||
checker.indexer().comment_ranges(),
|
||||
checker.locator().contents(),
|
||||
)
|
||||
.unwrap_or(expr.range()),
|
||||
)));
|
||||
}
|
||||
// We're converting an expression to a statement, so avoid applying the fix if
|
||||
// the assertion is part of a larger expression.
|
||||
if checker.semantic().current_statement().is_expr_stmt()
|
||||
&& checker.semantic().current_expression_parent().is_none()
|
||||
&& !checker.indexer().comment_ranges().intersects(expr.range())
|
||||
{
|
||||
if let Ok(stmt) = unittest_assert.generate_assert(args, keywords) {
|
||||
diagnostic.set_fix(Fix::unsafe_edit(Edit::range_replacement(
|
||||
checker.generator().stmt(&stmt),
|
||||
parenthesized_range(
|
||||
expr.into(),
|
||||
checker.semantic().current_statement().into(),
|
||||
checker.indexer().comment_ranges(),
|
||||
checker.locator().contents(),
|
||||
)
|
||||
.unwrap_or(expr.range()),
|
||||
)));
|
||||
}
|
||||
}
|
||||
Some(diagnostic)
|
||||
|
@ -390,9 +387,7 @@ pub(crate) fn unittest_raises_assertion(
|
|||
},
|
||||
call.func.range(),
|
||||
);
|
||||
if checker.patch(diagnostic.kind.rule())
|
||||
&& !checker.indexer().has_comments(call, checker.locator())
|
||||
{
|
||||
if !checker.indexer().has_comments(call, checker.locator()) {
|
||||
if let Some(args) = to_pytest_raises_args(checker, attr.as_str(), &call.arguments) {
|
||||
diagnostic.try_set_fix(|| {
|
||||
let (import_edit, binding) = checker.importer().get_or_import_symbol(
|
||||
|
@ -746,19 +741,17 @@ pub(crate) fn composite_condition(
|
|||
let composite = is_composite_condition(test);
|
||||
if matches!(composite, CompositionKind::Simple | CompositionKind::Mixed) {
|
||||
let mut diagnostic = Diagnostic::new(PytestCompositeAssertion, stmt.range());
|
||||
if checker.patch(diagnostic.kind.rule()) {
|
||||
if matches!(composite, CompositionKind::Simple)
|
||||
&& msg.is_none()
|
||||
&& !checker.indexer().comment_ranges().intersects(stmt.range())
|
||||
&& !checker
|
||||
.indexer()
|
||||
.in_multi_statement_line(stmt, checker.locator())
|
||||
{
|
||||
diagnostic.try_set_fix(|| {
|
||||
fix_composite_condition(stmt, checker.locator(), checker.stylist())
|
||||
.map(Fix::unsafe_edit)
|
||||
});
|
||||
}
|
||||
if matches!(composite, CompositionKind::Simple)
|
||||
&& msg.is_none()
|
||||
&& !checker.indexer().comment_ranges().intersects(stmt.range())
|
||||
&& !checker
|
||||
.indexer()
|
||||
.in_multi_statement_line(stmt, checker.locator())
|
||||
{
|
||||
diagnostic.try_set_fix(|| {
|
||||
fix_composite_condition(stmt, checker.locator(), checker.stylist())
|
||||
.map(Fix::unsafe_edit)
|
||||
});
|
||||
}
|
||||
checker.diagnostics.push(diagnostic);
|
||||
}
|
||||
|
|
|
@ -16,7 +16,7 @@ use ruff_text_size::{TextLen, TextRange};
|
|||
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::fix::edits;
|
||||
use crate::registry::{AsRule, Rule};
|
||||
use crate::registry::Rule;
|
||||
|
||||
use super::helpers::{
|
||||
get_mark_decorators, is_pytest_fixture, is_pytest_yield_fixture, keyword_is_literal,
|
||||
|
@ -681,9 +681,7 @@ fn pytest_fixture_parentheses(
|
|||
PytestFixtureIncorrectParenthesesStyle { expected, actual },
|
||||
decorator.range(),
|
||||
);
|
||||
if checker.patch(diagnostic.kind.rule()) {
|
||||
diagnostic.set_fix(fix);
|
||||
}
|
||||
diagnostic.set_fix(fix);
|
||||
checker.diagnostics.push(diagnostic);
|
||||
}
|
||||
|
||||
|
@ -727,17 +725,15 @@ fn check_fixture_decorator(checker: &mut Checker, func_name: &str, decorator: &D
|
|||
if keyword_is_literal(keyword, "function") {
|
||||
let mut diagnostic =
|
||||
Diagnostic::new(PytestExtraneousScopeFunction, keyword.range());
|
||||
if checker.patch(diagnostic.kind.rule()) {
|
||||
diagnostic.try_set_fix(|| {
|
||||
edits::remove_argument(
|
||||
keyword,
|
||||
arguments,
|
||||
edits::Parentheses::Preserve,
|
||||
checker.locator().contents(),
|
||||
)
|
||||
.map(Fix::unsafe_edit)
|
||||
});
|
||||
}
|
||||
diagnostic.try_set_fix(|| {
|
||||
edits::remove_argument(
|
||||
keyword,
|
||||
arguments,
|
||||
edits::Parentheses::Preserve,
|
||||
checker.locator().contents(),
|
||||
)
|
||||
.map(Fix::unsafe_edit)
|
||||
});
|
||||
checker.diagnostics.push(diagnostic);
|
||||
}
|
||||
}
|
||||
|
@ -819,30 +815,28 @@ fn check_fixture_returns(
|
|||
},
|
||||
stmt.range(),
|
||||
);
|
||||
if checker.patch(diagnostic.kind.rule()) {
|
||||
let yield_edit = Edit::range_replacement(
|
||||
"return".to_string(),
|
||||
TextRange::at(stmt.start(), "yield".text_len()),
|
||||
);
|
||||
let return_type_edit = returns.and_then(|returns| {
|
||||
let ast::ExprSubscript { value, slice, .. } = returns.as_subscript_expr()?;
|
||||
let ast::ExprTuple { elts, .. } = slice.as_tuple_expr()?;
|
||||
let [first, ..] = elts.as_slice() else {
|
||||
return None;
|
||||
};
|
||||
if !checker.semantic().match_typing_expr(value, "Generator") {
|
||||
return None;
|
||||
}
|
||||
Some(Edit::range_replacement(
|
||||
checker.generator().expr(first),
|
||||
returns.range(),
|
||||
))
|
||||
});
|
||||
if let Some(return_type_edit) = return_type_edit {
|
||||
diagnostic.set_fix(Fix::safe_edits(yield_edit, [return_type_edit]));
|
||||
} else {
|
||||
diagnostic.set_fix(Fix::safe_edit(yield_edit));
|
||||
let yield_edit = Edit::range_replacement(
|
||||
"return".to_string(),
|
||||
TextRange::at(stmt.start(), "yield".text_len()),
|
||||
);
|
||||
let return_type_edit = returns.and_then(|returns| {
|
||||
let ast::ExprSubscript { value, slice, .. } = returns.as_subscript_expr()?;
|
||||
let ast::ExprTuple { elts, .. } = slice.as_tuple_expr()?;
|
||||
let [first, ..] = elts.as_slice() else {
|
||||
return None;
|
||||
};
|
||||
if !checker.semantic().match_typing_expr(value, "Generator") {
|
||||
return None;
|
||||
}
|
||||
Some(Edit::range_replacement(
|
||||
checker.generator().expr(first),
|
||||
returns.range(),
|
||||
))
|
||||
});
|
||||
if let Some(return_type_edit) = return_type_edit {
|
||||
diagnostic.set_fix(Fix::safe_edits(yield_edit, [return_type_edit]));
|
||||
} else {
|
||||
diagnostic.set_fix(Fix::safe_edit(yield_edit));
|
||||
}
|
||||
checker.diagnostics.push(diagnostic);
|
||||
}
|
||||
|
@ -912,10 +906,8 @@ fn check_fixture_marks(checker: &mut Checker, decorators: &[Decorator]) {
|
|||
if *name == "asyncio" {
|
||||
let mut diagnostic =
|
||||
Diagnostic::new(PytestUnnecessaryAsyncioMarkOnFixture, expr.range());
|
||||
if checker.patch(diagnostic.kind.rule()) {
|
||||
let range = checker.locator().full_lines_range(expr.range());
|
||||
diagnostic.set_fix(Fix::safe_edit(Edit::range_deletion(range)));
|
||||
}
|
||||
let range = checker.locator().full_lines_range(expr.range());
|
||||
diagnostic.set_fix(Fix::safe_edit(Edit::range_deletion(range)));
|
||||
checker.diagnostics.push(diagnostic);
|
||||
}
|
||||
}
|
||||
|
@ -924,10 +916,8 @@ fn check_fixture_marks(checker: &mut Checker, decorators: &[Decorator]) {
|
|||
if *name == "usefixtures" {
|
||||
let mut diagnostic =
|
||||
Diagnostic::new(PytestErroneousUseFixturesOnFixture, expr.range());
|
||||
if checker.patch(diagnostic.kind.rule()) {
|
||||
let line_range = checker.locator().full_lines_range(expr.range());
|
||||
diagnostic.set_fix(Fix::safe_edit(Edit::range_deletion(line_range)));
|
||||
}
|
||||
let line_range = checker.locator().full_lines_range(expr.range());
|
||||
diagnostic.set_fix(Fix::safe_edit(Edit::range_deletion(line_range)));
|
||||
checker.diagnostics.push(diagnostic);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,7 +6,7 @@ use ruff_python_ast::call_path::CallPath;
|
|||
use ruff_text_size::Ranged;
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::registry::{AsRule, Rule};
|
||||
use crate::registry::Rule;
|
||||
|
||||
use super::helpers::get_mark_decorators;
|
||||
|
||||
|
@ -130,9 +130,7 @@ fn pytest_mark_parentheses(
|
|||
},
|
||||
decorator.range(),
|
||||
);
|
||||
if checker.patch(diagnostic.kind.rule()) {
|
||||
diagnostic.set_fix(fix);
|
||||
}
|
||||
diagnostic.set_fix(fix);
|
||||
checker.diagnostics.push(diagnostic);
|
||||
}
|
||||
|
||||
|
@ -184,9 +182,7 @@ fn check_useless_usefixtures(checker: &mut Checker, decorator: &Decorator, call_
|
|||
|
||||
if !has_parameters {
|
||||
let mut diagnostic = Diagnostic::new(PytestUseFixturesWithoutParameters, decorator.range());
|
||||
if checker.patch(diagnostic.kind.rule()) {
|
||||
diagnostic.set_fix(Fix::unsafe_edit(Edit::range_deletion(decorator.range())));
|
||||
}
|
||||
diagnostic.set_fix(Fix::unsafe_edit(Edit::range_deletion(decorator.range())));
|
||||
checker.diagnostics.push(diagnostic);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,7 +14,7 @@ use ruff_python_trivia::{SimpleTokenKind, SimpleTokenizer};
|
|||
use ruff_text_size::{Ranged, TextRange, TextSize};
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::registry::{AsRule, Rule};
|
||||
use crate::registry::Rule;
|
||||
|
||||
use super::super::types;
|
||||
use super::helpers::{is_pytest_parametrize, split_names};
|
||||
|
@ -338,25 +338,23 @@ fn check_names(checker: &mut Checker, decorator: &Decorator, expr: &Expr) {
|
|||
},
|
||||
name_range,
|
||||
);
|
||||
if checker.patch(diagnostic.kind.rule()) {
|
||||
let node = Expr::Tuple(ast::ExprTuple {
|
||||
elts: names
|
||||
.iter()
|
||||
.map(|name| {
|
||||
Expr::Constant(ast::ExprConstant {
|
||||
value: (*name).to_string().into(),
|
||||
range: TextRange::default(),
|
||||
})
|
||||
let node = Expr::Tuple(ast::ExprTuple {
|
||||
elts: names
|
||||
.iter()
|
||||
.map(|name| {
|
||||
Expr::Constant(ast::ExprConstant {
|
||||
value: (*name).to_string().into(),
|
||||
range: TextRange::default(),
|
||||
})
|
||||
.collect(),
|
||||
ctx: ExprContext::Load,
|
||||
range: TextRange::default(),
|
||||
});
|
||||
diagnostic.set_fix(Fix::unsafe_edit(Edit::range_replacement(
|
||||
format!("({})", checker.generator().expr(&node)),
|
||||
name_range,
|
||||
)));
|
||||
}
|
||||
})
|
||||
.collect(),
|
||||
ctx: ExprContext::Load,
|
||||
range: TextRange::default(),
|
||||
});
|
||||
diagnostic.set_fix(Fix::unsafe_edit(Edit::range_replacement(
|
||||
format!("({})", checker.generator().expr(&node)),
|
||||
name_range,
|
||||
)));
|
||||
checker.diagnostics.push(diagnostic);
|
||||
}
|
||||
types::ParametrizeNameType::List => {
|
||||
|
@ -373,25 +371,23 @@ fn check_names(checker: &mut Checker, decorator: &Decorator, expr: &Expr) {
|
|||
},
|
||||
name_range,
|
||||
);
|
||||
if checker.patch(diagnostic.kind.rule()) {
|
||||
let node = Expr::List(ast::ExprList {
|
||||
elts: names
|
||||
.iter()
|
||||
.map(|name| {
|
||||
Expr::Constant(ast::ExprConstant {
|
||||
value: (*name).to_string().into(),
|
||||
range: TextRange::default(),
|
||||
})
|
||||
let node = Expr::List(ast::ExprList {
|
||||
elts: names
|
||||
.iter()
|
||||
.map(|name| {
|
||||
Expr::Constant(ast::ExprConstant {
|
||||
value: (*name).to_string().into(),
|
||||
range: TextRange::default(),
|
||||
})
|
||||
.collect(),
|
||||
ctx: ExprContext::Load,
|
||||
range: TextRange::default(),
|
||||
});
|
||||
diagnostic.set_fix(Fix::unsafe_edit(Edit::range_replacement(
|
||||
checker.generator().expr(&node),
|
||||
name_range,
|
||||
)));
|
||||
}
|
||||
})
|
||||
.collect(),
|
||||
ctx: ExprContext::Load,
|
||||
range: TextRange::default(),
|
||||
});
|
||||
diagnostic.set_fix(Fix::unsafe_edit(Edit::range_replacement(
|
||||
checker.generator().expr(&node),
|
||||
name_range,
|
||||
)));
|
||||
checker.diagnostics.push(diagnostic);
|
||||
}
|
||||
types::ParametrizeNameType::Csv => {}
|
||||
|
@ -413,17 +409,15 @@ fn check_names(checker: &mut Checker, decorator: &Decorator, expr: &Expr) {
|
|||
},
|
||||
expr.range(),
|
||||
);
|
||||
if checker.patch(diagnostic.kind.rule()) {
|
||||
let node = Expr::List(ast::ExprList {
|
||||
elts: elts.clone(),
|
||||
ctx: ExprContext::Load,
|
||||
range: TextRange::default(),
|
||||
});
|
||||
diagnostic.set_fix(Fix::unsafe_edit(Edit::range_replacement(
|
||||
checker.generator().expr(&node),
|
||||
expr.range(),
|
||||
)));
|
||||
}
|
||||
let node = Expr::List(ast::ExprList {
|
||||
elts: elts.clone(),
|
||||
ctx: ExprContext::Load,
|
||||
range: TextRange::default(),
|
||||
});
|
||||
diagnostic.set_fix(Fix::unsafe_edit(Edit::range_replacement(
|
||||
checker.generator().expr(&node),
|
||||
expr.range(),
|
||||
)));
|
||||
checker.diagnostics.push(diagnostic);
|
||||
}
|
||||
types::ParametrizeNameType::Csv => {
|
||||
|
@ -433,13 +427,11 @@ fn check_names(checker: &mut Checker, decorator: &Decorator, expr: &Expr) {
|
|||
},
|
||||
expr.range(),
|
||||
);
|
||||
if checker.patch(diagnostic.kind.rule()) {
|
||||
if let Some(content) = elts_to_csv(elts, checker.generator()) {
|
||||
diagnostic.set_fix(Fix::unsafe_edit(Edit::range_replacement(
|
||||
content,
|
||||
expr.range(),
|
||||
)));
|
||||
}
|
||||
if let Some(content) = elts_to_csv(elts, checker.generator()) {
|
||||
diagnostic.set_fix(Fix::unsafe_edit(Edit::range_replacement(
|
||||
content,
|
||||
expr.range(),
|
||||
)));
|
||||
}
|
||||
checker.diagnostics.push(diagnostic);
|
||||
}
|
||||
|
@ -461,17 +453,15 @@ fn check_names(checker: &mut Checker, decorator: &Decorator, expr: &Expr) {
|
|||
},
|
||||
expr.range(),
|
||||
);
|
||||
if checker.patch(diagnostic.kind.rule()) {
|
||||
let node = Expr::Tuple(ast::ExprTuple {
|
||||
elts: elts.clone(),
|
||||
ctx: ExprContext::Load,
|
||||
range: TextRange::default(),
|
||||
});
|
||||
diagnostic.set_fix(Fix::unsafe_edit(Edit::range_replacement(
|
||||
format!("({})", checker.generator().expr(&node)),
|
||||
expr.range(),
|
||||
)));
|
||||
}
|
||||
let node = Expr::Tuple(ast::ExprTuple {
|
||||
elts: elts.clone(),
|
||||
ctx: ExprContext::Load,
|
||||
range: TextRange::default(),
|
||||
});
|
||||
diagnostic.set_fix(Fix::unsafe_edit(Edit::range_replacement(
|
||||
format!("({})", checker.generator().expr(&node)),
|
||||
expr.range(),
|
||||
)));
|
||||
checker.diagnostics.push(diagnostic);
|
||||
}
|
||||
types::ParametrizeNameType::Csv => {
|
||||
|
@ -481,13 +471,11 @@ fn check_names(checker: &mut Checker, decorator: &Decorator, expr: &Expr) {
|
|||
},
|
||||
expr.range(),
|
||||
);
|
||||
if checker.patch(diagnostic.kind.rule()) {
|
||||
if let Some(content) = elts_to_csv(elts, checker.generator()) {
|
||||
diagnostic.set_fix(Fix::unsafe_edit(Edit::range_replacement(
|
||||
content,
|
||||
expr.range(),
|
||||
)));
|
||||
}
|
||||
if let Some(content) = elts_to_csv(elts, checker.generator()) {
|
||||
diagnostic.set_fix(Fix::unsafe_edit(Edit::range_replacement(
|
||||
content,
|
||||
expr.range(),
|
||||
)));
|
||||
}
|
||||
checker.diagnostics.push(diagnostic);
|
||||
}
|
||||
|
@ -585,22 +573,19 @@ fn check_duplicates(checker: &mut Checker, values: &Expr) {
|
|||
PytestDuplicateParametrizeTestCases { index: *index },
|
||||
element.range(),
|
||||
);
|
||||
if checker.patch(diagnostic.kind.rule()) {
|
||||
if let Some(prev) = prev {
|
||||
let values_end = values.range().end() - TextSize::new(1);
|
||||
let previous_end = trailing_comma(prev, checker.locator().contents())
|
||||
.unwrap_or(values_end);
|
||||
let element_end = trailing_comma(element, checker.locator().contents())
|
||||
.unwrap_or(values_end);
|
||||
let deletion_range = TextRange::new(previous_end, element_end);
|
||||
if !checker
|
||||
.indexer()
|
||||
.comment_ranges()
|
||||
.intersects(deletion_range)
|
||||
{
|
||||
diagnostic
|
||||
.set_fix(Fix::unsafe_edit(Edit::range_deletion(deletion_range)));
|
||||
}
|
||||
if let Some(prev) = prev {
|
||||
let values_end = values.range().end() - TextSize::new(1);
|
||||
let previous_end =
|
||||
trailing_comma(prev, checker.locator().contents()).unwrap_or(values_end);
|
||||
let element_end =
|
||||
trailing_comma(element, checker.locator().contents()).unwrap_or(values_end);
|
||||
let deletion_range = TextRange::new(previous_end, element_end);
|
||||
if !checker
|
||||
.indexer()
|
||||
.comment_ranges()
|
||||
.intersects(deletion_range)
|
||||
{
|
||||
diagnostic.set_fix(Fix::unsafe_edit(Edit::range_deletion(deletion_range)));
|
||||
}
|
||||
}
|
||||
checker.diagnostics.push(diagnostic);
|
||||
|
@ -618,13 +603,11 @@ fn handle_single_name(checker: &mut Checker, expr: &Expr, value: &Expr) {
|
|||
expr.range(),
|
||||
);
|
||||
|
||||
if checker.patch(diagnostic.kind.rule()) {
|
||||
let node = value.clone();
|
||||
diagnostic.set_fix(Fix::safe_edit(Edit::range_replacement(
|
||||
checker.generator().expr(&node),
|
||||
expr.range(),
|
||||
)));
|
||||
}
|
||||
let node = value.clone();
|
||||
diagnostic.set_fix(Fix::safe_edit(Edit::range_replacement(
|
||||
checker.generator().expr(&node),
|
||||
expr.range(),
|
||||
)));
|
||||
checker.diagnostics.push(diagnostic);
|
||||
}
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@ use ruff_source_file::Locator;
|
|||
use ruff_text_size::TextRange;
|
||||
|
||||
use crate::lex::docstring_detection::StateMachine;
|
||||
use crate::registry::AsRule;
|
||||
|
||||
use crate::settings::LinterSettings;
|
||||
|
||||
/// ## What it does
|
||||
|
@ -135,18 +135,16 @@ pub(crate) fn avoidable_escaped_quote(
|
|||
&& !string_contents.contains(quotes_settings.inline_quotes.opposite().as_char())
|
||||
{
|
||||
let mut diagnostic = Diagnostic::new(AvoidableEscapedQuote, tok_range);
|
||||
if settings.rules.should_fix(diagnostic.kind.rule()) {
|
||||
let fixed_contents = format!(
|
||||
"{prefix}{quote}{value}{quote}",
|
||||
prefix = kind.as_str(),
|
||||
quote = quotes_settings.inline_quotes.opposite().as_char(),
|
||||
value = unescape_string(string_contents)
|
||||
);
|
||||
diagnostic.set_fix(Fix::safe_edit(Edit::range_replacement(
|
||||
fixed_contents,
|
||||
tok_range,
|
||||
)));
|
||||
}
|
||||
let fixed_contents = format!(
|
||||
"{prefix}{quote}{value}{quote}",
|
||||
prefix = kind.as_str(),
|
||||
quote = quotes_settings.inline_quotes.opposite().as_char(),
|
||||
value = unescape_string(string_contents)
|
||||
);
|
||||
diagnostic.set_fix(Fix::safe_edit(Edit::range_replacement(
|
||||
fixed_contents,
|
||||
tok_range,
|
||||
)));
|
||||
diagnostics.push(diagnostic);
|
||||
}
|
||||
}
|
||||
|
@ -192,35 +190,33 @@ pub(crate) fn avoidable_escaped_quote(
|
|||
AvoidableEscapedQuote,
|
||||
TextRange::new(context.start_range.start(), tok_range.end()),
|
||||
);
|
||||
if settings.rules.should_fix(diagnostic.kind.rule()) {
|
||||
let fstring_start_edit = Edit::range_replacement(
|
||||
// No need for `r`/`R` as we don't perform the checks
|
||||
// for raw strings.
|
||||
format!("f{}", quotes_settings.inline_quotes.opposite().as_char()),
|
||||
context.start_range,
|
||||
);
|
||||
let fstring_middle_and_end_edits = context
|
||||
.middle_ranges_with_escapes
|
||||
.iter()
|
||||
.map(|&range| {
|
||||
Edit::range_replacement(unescape_string(locator.slice(range)), range)
|
||||
})
|
||||
.chain(std::iter::once(
|
||||
// `FStringEnd` edit
|
||||
Edit::range_replacement(
|
||||
quotes_settings
|
||||
.inline_quotes
|
||||
.opposite()
|
||||
.as_char()
|
||||
.to_string(),
|
||||
tok_range,
|
||||
),
|
||||
));
|
||||
diagnostic.set_fix(Fix::safe_edits(
|
||||
fstring_start_edit,
|
||||
fstring_middle_and_end_edits,
|
||||
let fstring_start_edit = Edit::range_replacement(
|
||||
// No need for `r`/`R` as we don't perform the checks
|
||||
// for raw strings.
|
||||
format!("f{}", quotes_settings.inline_quotes.opposite().as_char()),
|
||||
context.start_range,
|
||||
);
|
||||
let fstring_middle_and_end_edits = context
|
||||
.middle_ranges_with_escapes
|
||||
.iter()
|
||||
.map(|&range| {
|
||||
Edit::range_replacement(unescape_string(locator.slice(range)), range)
|
||||
})
|
||||
.chain(std::iter::once(
|
||||
// `FStringEnd` edit
|
||||
Edit::range_replacement(
|
||||
quotes_settings
|
||||
.inline_quotes
|
||||
.opposite()
|
||||
.as_char()
|
||||
.to_string(),
|
||||
tok_range,
|
||||
),
|
||||
));
|
||||
}
|
||||
diagnostic.set_fix(Fix::safe_edits(
|
||||
fstring_start_edit,
|
||||
fstring_middle_and_end_edits,
|
||||
));
|
||||
diagnostics.push(diagnostic);
|
||||
}
|
||||
_ => {}
|
||||
|
|
|
@ -7,7 +7,7 @@ use ruff_macros::{derive_message_formats, violation};
|
|||
use ruff_source_file::Locator;
|
||||
|
||||
use crate::lex::docstring_detection::StateMachine;
|
||||
use crate::registry::Rule;
|
||||
|
||||
use crate::settings::LinterSettings;
|
||||
|
||||
use super::super::settings::Quote;
|
||||
|
@ -230,21 +230,19 @@ fn docstring(locator: &Locator, range: TextRange, settings: &LinterSettings) ->
|
|||
},
|
||||
range,
|
||||
);
|
||||
if settings.rules.should_fix(Rule::BadQuotesDocstring) {
|
||||
let quote_count = if trivia.is_multiline { 3 } else { 1 };
|
||||
let string_contents = &trivia.raw_text[quote_count..trivia.raw_text.len() - quote_count];
|
||||
let quote = good_docstring(quotes_settings.docstring_quotes).repeat(quote_count);
|
||||
let mut fixed_contents =
|
||||
String::with_capacity(trivia.prefix.len() + string_contents.len() + quote.len() * 2);
|
||||
fixed_contents.push_str(trivia.prefix);
|
||||
fixed_contents.push_str("e);
|
||||
fixed_contents.push_str(string_contents);
|
||||
fixed_contents.push_str("e);
|
||||
diagnostic.set_fix(Fix::safe_edit(Edit::range_replacement(
|
||||
fixed_contents,
|
||||
range,
|
||||
)));
|
||||
}
|
||||
let quote_count = if trivia.is_multiline { 3 } else { 1 };
|
||||
let string_contents = &trivia.raw_text[quote_count..trivia.raw_text.len() - quote_count];
|
||||
let quote = good_docstring(quotes_settings.docstring_quotes).repeat(quote_count);
|
||||
let mut fixed_contents =
|
||||
String::with_capacity(trivia.prefix.len() + string_contents.len() + quote.len() * 2);
|
||||
fixed_contents.push_str(trivia.prefix);
|
||||
fixed_contents.push_str("e);
|
||||
fixed_contents.push_str(string_contents);
|
||||
fixed_contents.push_str("e);
|
||||
diagnostic.set_fix(Fix::safe_edit(Edit::range_replacement(
|
||||
fixed_contents,
|
||||
range,
|
||||
)));
|
||||
Some(diagnostic)
|
||||
}
|
||||
|
||||
|
@ -307,21 +305,19 @@ fn strings(
|
|||
*range,
|
||||
);
|
||||
|
||||
if settings.rules.should_fix(Rule::BadQuotesMultilineString) {
|
||||
let string_contents = &trivia.raw_text[3..trivia.raw_text.len() - 3];
|
||||
let quote = good_multiline(quotes_settings.multiline_quotes);
|
||||
let mut fixed_contents = String::with_capacity(
|
||||
trivia.prefix.len() + string_contents.len() + quote.len() * 2,
|
||||
);
|
||||
fixed_contents.push_str(trivia.prefix);
|
||||
fixed_contents.push_str(quote);
|
||||
fixed_contents.push_str(string_contents);
|
||||
fixed_contents.push_str(quote);
|
||||
diagnostic.set_fix(Fix::safe_edit(Edit::range_replacement(
|
||||
fixed_contents,
|
||||
*range,
|
||||
)));
|
||||
}
|
||||
let string_contents = &trivia.raw_text[3..trivia.raw_text.len() - 3];
|
||||
let quote = good_multiline(quotes_settings.multiline_quotes);
|
||||
let mut fixed_contents = String::with_capacity(
|
||||
trivia.prefix.len() + string_contents.len() + quote.len() * 2,
|
||||
);
|
||||
fixed_contents.push_str(trivia.prefix);
|
||||
fixed_contents.push_str(quote);
|
||||
fixed_contents.push_str(string_contents);
|
||||
fixed_contents.push_str(quote);
|
||||
diagnostic.set_fix(Fix::safe_edit(Edit::range_replacement(
|
||||
fixed_contents,
|
||||
*range,
|
||||
)));
|
||||
diagnostics.push(diagnostic);
|
||||
} else if trivia.last_quote_char != quotes_settings.inline_quotes.as_char()
|
||||
// If we're not using the preferred type, only allow use to avoid escapes.
|
||||
|
@ -333,20 +329,18 @@ fn strings(
|
|||
},
|
||||
*range,
|
||||
);
|
||||
if settings.rules.should_fix(Rule::BadQuotesInlineString) {
|
||||
let quote = quotes_settings.inline_quotes.as_char();
|
||||
let string_contents = &trivia.raw_text[1..trivia.raw_text.len() - 1];
|
||||
let mut fixed_contents =
|
||||
String::with_capacity(trivia.prefix.len() + string_contents.len() + 2);
|
||||
fixed_contents.push_str(trivia.prefix);
|
||||
fixed_contents.push(quote);
|
||||
fixed_contents.push_str(string_contents);
|
||||
fixed_contents.push(quote);
|
||||
diagnostic.set_fix(Fix::safe_edit(Edit::range_replacement(
|
||||
fixed_contents,
|
||||
*range,
|
||||
)));
|
||||
}
|
||||
let quote = quotes_settings.inline_quotes.as_char();
|
||||
let string_contents = &trivia.raw_text[1..trivia.raw_text.len() - 1];
|
||||
let mut fixed_contents =
|
||||
String::with_capacity(trivia.prefix.len() + string_contents.len() + 2);
|
||||
fixed_contents.push_str(trivia.prefix);
|
||||
fixed_contents.push(quote);
|
||||
fixed_contents.push_str(string_contents);
|
||||
fixed_contents.push(quote);
|
||||
diagnostic.set_fix(Fix::safe_edit(Edit::range_replacement(
|
||||
fixed_contents,
|
||||
*range,
|
||||
)));
|
||||
diagnostics.push(diagnostic);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,7 +4,6 @@ use ruff_python_ast::{self as ast, Expr};
|
|||
use ruff_text_size::Ranged;
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::registry::AsRule;
|
||||
|
||||
/// ## What it does
|
||||
/// Checks for unnecessary parentheses on raised exceptions.
|
||||
|
@ -74,26 +73,24 @@ pub(crate) fn unnecessary_paren_on_raise_exception(checker: &mut Checker, expr:
|
|||
}
|
||||
|
||||
let mut diagnostic = Diagnostic::new(UnnecessaryParenOnRaiseException, arguments.range());
|
||||
if checker.patch(diagnostic.kind.rule()) {
|
||||
// If the arguments are immediately followed by a `from`, insert whitespace to avoid
|
||||
// a syntax error, as in:
|
||||
// ```python
|
||||
// raise IndexError()from ZeroDivisionError
|
||||
// ```
|
||||
if checker
|
||||
.locator()
|
||||
.after(arguments.end())
|
||||
.chars()
|
||||
.next()
|
||||
.is_some_and(char::is_alphanumeric)
|
||||
{
|
||||
diagnostic.set_fix(Fix::safe_edit(Edit::range_replacement(
|
||||
" ".to_string(),
|
||||
arguments.range(),
|
||||
)));
|
||||
} else {
|
||||
diagnostic.set_fix(Fix::safe_edit(Edit::range_deletion(arguments.range())));
|
||||
}
|
||||
// If the arguments are immediately followed by a `from`, insert whitespace to avoid
|
||||
// a syntax error, as in:
|
||||
// ```python
|
||||
// raise IndexError()from ZeroDivisionError
|
||||
// ```
|
||||
if checker
|
||||
.locator()
|
||||
.after(arguments.end())
|
||||
.chars()
|
||||
.next()
|
||||
.is_some_and(char::is_alphanumeric)
|
||||
{
|
||||
diagnostic.set_fix(Fix::safe_edit(Edit::range_replacement(
|
||||
" ".to_string(),
|
||||
arguments.range(),
|
||||
)));
|
||||
} else {
|
||||
diagnostic.set_fix(Fix::safe_edit(Edit::range_deletion(arguments.range())));
|
||||
}
|
||||
checker.diagnostics.push(diagnostic);
|
||||
}
|
||||
|
|
|
@ -345,12 +345,10 @@ fn unnecessary_return_none(checker: &mut Checker, stack: &Stack) {
|
|||
continue;
|
||||
}
|
||||
let mut diagnostic = Diagnostic::new(UnnecessaryReturnNone, stmt.range);
|
||||
if checker.patch(diagnostic.kind.rule()) {
|
||||
diagnostic.set_fix(Fix::safe_edit(Edit::range_replacement(
|
||||
"return".to_string(),
|
||||
stmt.range(),
|
||||
)));
|
||||
}
|
||||
diagnostic.set_fix(Fix::safe_edit(Edit::range_replacement(
|
||||
"return".to_string(),
|
||||
stmt.range(),
|
||||
)));
|
||||
checker.diagnostics.push(diagnostic);
|
||||
}
|
||||
}
|
||||
|
@ -362,12 +360,10 @@ fn implicit_return_value(checker: &mut Checker, stack: &Stack) {
|
|||
continue;
|
||||
}
|
||||
let mut diagnostic = Diagnostic::new(ImplicitReturnValue, stmt.range);
|
||||
if checker.patch(diagnostic.kind.rule()) {
|
||||
diagnostic.set_fix(Fix::safe_edit(Edit::range_replacement(
|
||||
"return None".to_string(),
|
||||
stmt.range,
|
||||
)));
|
||||
}
|
||||
diagnostic.set_fix(Fix::safe_edit(Edit::range_replacement(
|
||||
"return None".to_string(),
|
||||
stmt.range,
|
||||
)));
|
||||
checker.diagnostics.push(diagnostic);
|
||||
}
|
||||
}
|
||||
|
@ -409,17 +405,15 @@ fn implicit_return(checker: &mut Checker, stmt: &Stmt) {
|
|||
None | Some(ast::ElifElseClause { test: Some(_), .. })
|
||||
) {
|
||||
let mut diagnostic = Diagnostic::new(ImplicitReturn, stmt.range());
|
||||
if checker.patch(diagnostic.kind.rule()) {
|
||||
if let Some(indent) = indentation(checker.locator(), stmt) {
|
||||
let mut content = String::new();
|
||||
content.push_str(checker.stylist().line_ending().as_str());
|
||||
content.push_str(indent);
|
||||
content.push_str("return None");
|
||||
diagnostic.set_fix(Fix::unsafe_edit(Edit::insertion(
|
||||
content,
|
||||
end_of_last_statement(stmt, checker.locator()),
|
||||
)));
|
||||
}
|
||||
if let Some(indent) = indentation(checker.locator(), stmt) {
|
||||
let mut content = String::new();
|
||||
content.push_str(checker.stylist().line_ending().as_str());
|
||||
content.push_str(indent);
|
||||
content.push_str("return None");
|
||||
diagnostic.set_fix(Fix::unsafe_edit(Edit::insertion(
|
||||
content,
|
||||
end_of_last_statement(stmt, checker.locator()),
|
||||
)));
|
||||
}
|
||||
checker.diagnostics.push(diagnostic);
|
||||
}
|
||||
|
@ -431,17 +425,15 @@ fn implicit_return(checker: &mut Checker, stmt: &Stmt) {
|
|||
implicit_return(checker, last_stmt);
|
||||
} else {
|
||||
let mut diagnostic = Diagnostic::new(ImplicitReturn, stmt.range());
|
||||
if checker.patch(diagnostic.kind.rule()) {
|
||||
if let Some(indent) = indentation(checker.locator(), stmt) {
|
||||
let mut content = String::new();
|
||||
content.push_str(checker.stylist().line_ending().as_str());
|
||||
content.push_str(indent);
|
||||
content.push_str("return None");
|
||||
diagnostic.set_fix(Fix::unsafe_edit(Edit::insertion(
|
||||
content,
|
||||
end_of_last_statement(stmt, checker.locator()),
|
||||
)));
|
||||
}
|
||||
if let Some(indent) = indentation(checker.locator(), stmt) {
|
||||
let mut content = String::new();
|
||||
content.push_str(checker.stylist().line_ending().as_str());
|
||||
content.push_str(indent);
|
||||
content.push_str("return None");
|
||||
diagnostic.set_fix(Fix::unsafe_edit(Edit::insertion(
|
||||
content,
|
||||
end_of_last_statement(stmt, checker.locator()),
|
||||
)));
|
||||
}
|
||||
checker.diagnostics.push(diagnostic);
|
||||
}
|
||||
|
@ -467,17 +459,15 @@ fn implicit_return(checker: &mut Checker, stmt: &Stmt) {
|
|||
) => {}
|
||||
_ => {
|
||||
let mut diagnostic = Diagnostic::new(ImplicitReturn, stmt.range());
|
||||
if checker.patch(diagnostic.kind.rule()) {
|
||||
if let Some(indent) = indentation(checker.locator(), stmt) {
|
||||
let mut content = String::new();
|
||||
content.push_str(checker.stylist().line_ending().as_str());
|
||||
content.push_str(indent);
|
||||
content.push_str("return None");
|
||||
diagnostic.set_fix(Fix::unsafe_edit(Edit::insertion(
|
||||
content,
|
||||
end_of_last_statement(stmt, checker.locator()),
|
||||
)));
|
||||
}
|
||||
if let Some(indent) = indentation(checker.locator(), stmt) {
|
||||
let mut content = String::new();
|
||||
content.push_str(checker.stylist().line_ending().as_str());
|
||||
content.push_str(indent);
|
||||
content.push_str("return None");
|
||||
diagnostic.set_fix(Fix::unsafe_edit(Edit::insertion(
|
||||
content,
|
||||
end_of_last_statement(stmt, checker.locator()),
|
||||
)));
|
||||
}
|
||||
checker.diagnostics.push(diagnostic);
|
||||
}
|
||||
|
@ -529,47 +519,45 @@ fn unnecessary_assign(checker: &mut Checker, stack: &Stack) {
|
|||
},
|
||||
value.range(),
|
||||
);
|
||||
if checker.patch(diagnostic.kind.rule()) {
|
||||
diagnostic.try_set_fix(|| {
|
||||
// Delete the `return` statement. There's no need to treat this as an isolated
|
||||
// edit, since we're editing the preceding statement, so no conflicting edit would
|
||||
// be allowed to remove that preceding statement.
|
||||
let delete_return =
|
||||
edits::delete_stmt(stmt, None, checker.locator(), checker.indexer());
|
||||
diagnostic.try_set_fix(|| {
|
||||
// Delete the `return` statement. There's no need to treat this as an isolated
|
||||
// edit, since we're editing the preceding statement, so no conflicting edit would
|
||||
// be allowed to remove that preceding statement.
|
||||
let delete_return =
|
||||
edits::delete_stmt(stmt, None, checker.locator(), checker.indexer());
|
||||
|
||||
// Replace the `x = 1` statement with `return 1`.
|
||||
let content = checker.locator().slice(assign);
|
||||
let equals_index = content
|
||||
.find('=')
|
||||
.ok_or(anyhow::anyhow!("expected '=' in assignment statement"))?;
|
||||
let after_equals = equals_index + 1;
|
||||
// Replace the `x = 1` statement with `return 1`.
|
||||
let content = checker.locator().slice(assign);
|
||||
let equals_index = content
|
||||
.find('=')
|
||||
.ok_or(anyhow::anyhow!("expected '=' in assignment statement"))?;
|
||||
let after_equals = equals_index + 1;
|
||||
|
||||
let replace_assign = Edit::range_replacement(
|
||||
// If necessary, add whitespace after the `return` keyword.
|
||||
// Ex) Convert `x=y` to `return y` (instead of `returny`).
|
||||
if content[after_equals..]
|
||||
.chars()
|
||||
.next()
|
||||
.is_some_and(is_python_whitespace)
|
||||
{
|
||||
"return".to_string()
|
||||
} else {
|
||||
"return ".to_string()
|
||||
},
|
||||
// Replace from the start of the assignment statement to the end of the equals
|
||||
// sign.
|
||||
TextRange::new(
|
||||
assign.start(),
|
||||
assign
|
||||
.range()
|
||||
.start()
|
||||
.add(TextSize::try_from(after_equals)?),
|
||||
),
|
||||
);
|
||||
let replace_assign = Edit::range_replacement(
|
||||
// If necessary, add whitespace after the `return` keyword.
|
||||
// Ex) Convert `x=y` to `return y` (instead of `returny`).
|
||||
if content[after_equals..]
|
||||
.chars()
|
||||
.next()
|
||||
.is_some_and(is_python_whitespace)
|
||||
{
|
||||
"return".to_string()
|
||||
} else {
|
||||
"return ".to_string()
|
||||
},
|
||||
// Replace from the start of the assignment statement to the end of the equals
|
||||
// sign.
|
||||
TextRange::new(
|
||||
assign.start(),
|
||||
assign
|
||||
.range()
|
||||
.start()
|
||||
.add(TextSize::try_from(after_equals)?),
|
||||
),
|
||||
);
|
||||
|
||||
Ok(Fix::unsafe_edits(replace_assign, [delete_return]))
|
||||
});
|
||||
}
|
||||
Ok(Fix::unsafe_edits(replace_assign, [delete_return]))
|
||||
});
|
||||
checker.diagnostics.push(diagnostic);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,7 +15,6 @@ use ruff_python_codegen::Generator;
|
|||
use ruff_python_semantic::SemanticModel;
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::registry::AsRule;
|
||||
|
||||
/// ## What it does
|
||||
/// Checks for multiple `isinstance` calls on the same target.
|
||||
|
@ -394,78 +393,76 @@ pub(crate) fn duplicate_isinstance_call(checker: &mut Checker, expr: &Expr) {
|
|||
},
|
||||
expr.range(),
|
||||
);
|
||||
if checker.patch(diagnostic.kind.rule()) {
|
||||
if !contains_effect(target, |id| checker.semantic().is_builtin(id)) {
|
||||
// Grab the types used in each duplicate `isinstance` call (e.g., `int` and `str`
|
||||
// in `isinstance(obj, int) or isinstance(obj, str)`).
|
||||
let types: Vec<&Expr> = indices
|
||||
if !contains_effect(target, |id| checker.semantic().is_builtin(id)) {
|
||||
// Grab the types used in each duplicate `isinstance` call (e.g., `int` and `str`
|
||||
// in `isinstance(obj, int) or isinstance(obj, str)`).
|
||||
let types: Vec<&Expr> = indices
|
||||
.iter()
|
||||
.map(|index| &values[*index])
|
||||
.map(|expr| {
|
||||
let Expr::Call(ast::ExprCall {
|
||||
arguments: Arguments { args, .. },
|
||||
..
|
||||
}) = expr
|
||||
else {
|
||||
unreachable!("Indices should only contain `isinstance` calls")
|
||||
};
|
||||
args.get(1).expect("`isinstance` should have two arguments")
|
||||
})
|
||||
.collect();
|
||||
|
||||
// Generate a single `isinstance` call.
|
||||
let node = ast::ExprTuple {
|
||||
// Flatten all the types used across the `isinstance` calls.
|
||||
elts: types
|
||||
.iter()
|
||||
.map(|index| &values[*index])
|
||||
.map(|expr| {
|
||||
let Expr::Call(ast::ExprCall {
|
||||
arguments: Arguments { args, .. },
|
||||
..
|
||||
}) = expr
|
||||
else {
|
||||
unreachable!("Indices should only contain `isinstance` calls")
|
||||
};
|
||||
args.get(1).expect("`isinstance` should have two arguments")
|
||||
.flat_map(|value| {
|
||||
if let Expr::Tuple(ast::ExprTuple { elts, .. }) = value {
|
||||
Left(elts.iter())
|
||||
} else {
|
||||
Right(iter::once(*value))
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
.map(Clone::clone)
|
||||
.collect(),
|
||||
ctx: ExprContext::Load,
|
||||
range: TextRange::default(),
|
||||
};
|
||||
let node1 = ast::ExprName {
|
||||
id: "isinstance".into(),
|
||||
ctx: ExprContext::Load,
|
||||
range: TextRange::default(),
|
||||
};
|
||||
let node2 = ast::ExprCall {
|
||||
func: Box::new(node1.into()),
|
||||
arguments: Arguments {
|
||||
args: vec![target.clone(), node.into()],
|
||||
keywords: vec![],
|
||||
range: TextRange::default(),
|
||||
},
|
||||
range: TextRange::default(),
|
||||
};
|
||||
let call = node2.into();
|
||||
|
||||
// Generate a single `isinstance` call.
|
||||
let node = ast::ExprTuple {
|
||||
// Flatten all the types used across the `isinstance` calls.
|
||||
elts: types
|
||||
.iter()
|
||||
.flat_map(|value| {
|
||||
if let Expr::Tuple(ast::ExprTuple { elts, .. }) = value {
|
||||
Left(elts.iter())
|
||||
} else {
|
||||
Right(iter::once(*value))
|
||||
}
|
||||
})
|
||||
.map(Clone::clone)
|
||||
.collect(),
|
||||
ctx: ExprContext::Load,
|
||||
range: TextRange::default(),
|
||||
};
|
||||
let node1 = ast::ExprName {
|
||||
id: "isinstance".into(),
|
||||
ctx: ExprContext::Load,
|
||||
range: TextRange::default(),
|
||||
};
|
||||
let node2 = ast::ExprCall {
|
||||
func: Box::new(node1.into()),
|
||||
arguments: Arguments {
|
||||
args: vec![target.clone(), node.into()],
|
||||
keywords: vec![],
|
||||
range: TextRange::default(),
|
||||
},
|
||||
range: TextRange::default(),
|
||||
};
|
||||
let call = node2.into();
|
||||
// Generate the combined `BoolOp`.
|
||||
let [first, .., last] = indices.as_slice() else {
|
||||
unreachable!("Indices should have at least two elements")
|
||||
};
|
||||
let before = values.iter().take(*first).cloned();
|
||||
let after = values.iter().skip(last + 1).cloned();
|
||||
let node = ast::ExprBoolOp {
|
||||
op: BoolOp::Or,
|
||||
values: before.chain(iter::once(call)).chain(after).collect(),
|
||||
range: TextRange::default(),
|
||||
};
|
||||
let bool_op = node.into();
|
||||
|
||||
// Generate the combined `BoolOp`.
|
||||
let [first, .., last] = indices.as_slice() else {
|
||||
unreachable!("Indices should have at least two elements")
|
||||
};
|
||||
let before = values.iter().take(*first).cloned();
|
||||
let after = values.iter().skip(last + 1).cloned();
|
||||
let node = ast::ExprBoolOp {
|
||||
op: BoolOp::Or,
|
||||
values: before.chain(iter::once(call)).chain(after).collect(),
|
||||
range: TextRange::default(),
|
||||
};
|
||||
let bool_op = node.into();
|
||||
|
||||
// Populate the `Fix`. Replace the _entire_ `BoolOp`. Note that if we have
|
||||
// multiple duplicates, the fixes will conflict.
|
||||
diagnostic.set_fix(Fix::unsafe_edit(Edit::range_replacement(
|
||||
checker.generator().expr(&bool_op),
|
||||
expr.range(),
|
||||
)));
|
||||
}
|
||||
// Populate the `Fix`. Replace the _entire_ `BoolOp`. Note that if we have
|
||||
// multiple duplicates, the fixes will conflict.
|
||||
diagnostic.set_fix(Fix::unsafe_edit(Edit::range_replacement(
|
||||
checker.generator().expr(&bool_op),
|
||||
expr.range(),
|
||||
)));
|
||||
}
|
||||
checker.diagnostics.push(diagnostic);
|
||||
}
|
||||
|
@ -564,29 +561,27 @@ pub(crate) fn compare_with_tuple(checker: &mut Checker, expr: &Expr) {
|
|||
},
|
||||
expr.range(),
|
||||
);
|
||||
if checker.patch(diagnostic.kind.rule()) {
|
||||
let unmatched: Vec<Expr> = values
|
||||
.iter()
|
||||
.enumerate()
|
||||
.filter(|(index, _)| !indices.contains(index))
|
||||
.map(|(_, elt)| elt.clone())
|
||||
.collect();
|
||||
let in_expr = if unmatched.is_empty() {
|
||||
in_expr
|
||||
} else {
|
||||
// Wrap in a `x in (a, b) or ...` boolean operation.
|
||||
let node = ast::ExprBoolOp {
|
||||
op: BoolOp::Or,
|
||||
values: iter::once(in_expr).chain(unmatched).collect(),
|
||||
range: TextRange::default(),
|
||||
};
|
||||
node.into()
|
||||
let unmatched: Vec<Expr> = values
|
||||
.iter()
|
||||
.enumerate()
|
||||
.filter(|(index, _)| !indices.contains(index))
|
||||
.map(|(_, elt)| elt.clone())
|
||||
.collect();
|
||||
let in_expr = if unmatched.is_empty() {
|
||||
in_expr
|
||||
} else {
|
||||
// Wrap in a `x in (a, b) or ...` boolean operation.
|
||||
let node = ast::ExprBoolOp {
|
||||
op: BoolOp::Or,
|
||||
values: iter::once(in_expr).chain(unmatched).collect(),
|
||||
range: TextRange::default(),
|
||||
};
|
||||
diagnostic.set_fix(Fix::unsafe_edit(Edit::range_replacement(
|
||||
checker.generator().expr(&in_expr),
|
||||
expr.range(),
|
||||
)));
|
||||
}
|
||||
node.into()
|
||||
};
|
||||
diagnostic.set_fix(Fix::unsafe_edit(Edit::range_replacement(
|
||||
checker.generator().expr(&in_expr),
|
||||
expr.range(),
|
||||
)));
|
||||
checker.diagnostics.push(diagnostic);
|
||||
}
|
||||
}
|
||||
|
@ -638,12 +633,10 @@ pub(crate) fn expr_and_not_expr(checker: &mut Checker, expr: &Expr) {
|
|||
},
|
||||
expr.range(),
|
||||
);
|
||||
if checker.patch(diagnostic.kind.rule()) {
|
||||
diagnostic.set_fix(Fix::unsafe_edit(Edit::range_replacement(
|
||||
"False".to_string(),
|
||||
expr.range(),
|
||||
)));
|
||||
}
|
||||
diagnostic.set_fix(Fix::unsafe_edit(Edit::range_replacement(
|
||||
"False".to_string(),
|
||||
expr.range(),
|
||||
)));
|
||||
checker.diagnostics.push(diagnostic);
|
||||
}
|
||||
}
|
||||
|
@ -697,12 +690,10 @@ pub(crate) fn expr_or_not_expr(checker: &mut Checker, expr: &Expr) {
|
|||
},
|
||||
expr.range(),
|
||||
);
|
||||
if checker.patch(diagnostic.kind.rule()) {
|
||||
diagnostic.set_fix(Fix::unsafe_edit(Edit::range_replacement(
|
||||
"True".to_string(),
|
||||
expr.range(),
|
||||
)));
|
||||
}
|
||||
diagnostic.set_fix(Fix::unsafe_edit(Edit::range_replacement(
|
||||
"True".to_string(),
|
||||
expr.range(),
|
||||
)));
|
||||
checker.diagnostics.push(diagnostic);
|
||||
}
|
||||
}
|
||||
|
@ -850,9 +841,7 @@ pub(crate) fn expr_or_true(checker: &mut Checker, expr: &Expr) {
|
|||
},
|
||||
edit.range(),
|
||||
);
|
||||
if checker.patch(diagnostic.kind.rule()) {
|
||||
diagnostic.set_fix(Fix::unsafe_edit(edit));
|
||||
}
|
||||
diagnostic.set_fix(Fix::unsafe_edit(edit));
|
||||
checker.diagnostics.push(diagnostic);
|
||||
}
|
||||
}
|
||||
|
@ -867,9 +856,7 @@ pub(crate) fn expr_and_false(checker: &mut Checker, expr: &Expr) {
|
|||
},
|
||||
edit.range(),
|
||||
);
|
||||
if checker.patch(diagnostic.kind.rule()) {
|
||||
diagnostic.set_fix(Fix::unsafe_edit(edit));
|
||||
}
|
||||
diagnostic.set_fix(Fix::unsafe_edit(edit));
|
||||
checker.diagnostics.push(diagnostic);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,7 +7,6 @@ use ruff_macros::{derive_message_formats, violation};
|
|||
use ruff_python_ast::helpers::is_const_none;
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::registry::AsRule;
|
||||
|
||||
/// ## What it does
|
||||
/// Check for environment variables that are not capitalized.
|
||||
|
@ -207,21 +206,19 @@ fn check_os_environ_subscript(checker: &mut Checker, expr: &Expr) {
|
|||
},
|
||||
slice.range(),
|
||||
);
|
||||
if checker.patch(diagnostic.kind.rule()) {
|
||||
let node = ast::ExprConstant {
|
||||
value: ast::Constant::Str(ast::StringConstant {
|
||||
value: capital_env_var,
|
||||
unicode: *unicode,
|
||||
implicit_concatenated: false,
|
||||
}),
|
||||
range: TextRange::default(),
|
||||
};
|
||||
let new_env_var = node.into();
|
||||
diagnostic.set_fix(Fix::unsafe_edit(Edit::range_replacement(
|
||||
checker.generator().expr(&new_env_var),
|
||||
slice.range(),
|
||||
)));
|
||||
}
|
||||
let node = ast::ExprConstant {
|
||||
value: ast::Constant::Str(ast::StringConstant {
|
||||
value: capital_env_var,
|
||||
unicode: *unicode,
|
||||
implicit_concatenated: false,
|
||||
}),
|
||||
range: TextRange::default(),
|
||||
};
|
||||
let new_env_var = node.into();
|
||||
diagnostic.set_fix(Fix::unsafe_edit(Edit::range_replacement(
|
||||
checker.generator().expr(&new_env_var),
|
||||
slice.range(),
|
||||
)));
|
||||
checker.diagnostics.push(diagnostic);
|
||||
}
|
||||
|
||||
|
@ -275,11 +272,9 @@ pub(crate) fn dict_get_with_none_default(checker: &mut Checker, expr: &Expr) {
|
|||
expr.range(),
|
||||
);
|
||||
|
||||
if checker.patch(diagnostic.kind.rule()) {
|
||||
diagnostic.set_fix(Fix::safe_edit(Edit::range_replacement(
|
||||
expected,
|
||||
expr.range(),
|
||||
)));
|
||||
}
|
||||
diagnostic.set_fix(Fix::safe_edit(Edit::range_replacement(
|
||||
expected,
|
||||
expr.range(),
|
||||
)));
|
||||
checker.diagnostics.push(diagnostic);
|
||||
}
|
||||
|
|
|
@ -7,7 +7,6 @@ use ruff_python_ast::helpers::{is_const_false, is_const_true};
|
|||
use ruff_python_ast::parenthesize::parenthesized_range;
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::registry::AsRule;
|
||||
|
||||
/// ## What it does
|
||||
/// Checks for `if` expressions that can be replaced with `bool()` calls.
|
||||
|
@ -157,48 +156,46 @@ pub(crate) fn if_expr_with_true_false(
|
|||
},
|
||||
expr.range(),
|
||||
);
|
||||
if checker.patch(diagnostic.kind.rule()) {
|
||||
if test.is_compare_expr() {
|
||||
diagnostic.set_fix(Fix::unsafe_edit(Edit::range_replacement(
|
||||
checker
|
||||
.locator()
|
||||
.slice(
|
||||
parenthesized_range(
|
||||
test.into(),
|
||||
expr.into(),
|
||||
checker.indexer().comment_ranges(),
|
||||
checker.locator().contents(),
|
||||
)
|
||||
.unwrap_or(test.range()),
|
||||
if test.is_compare_expr() {
|
||||
diagnostic.set_fix(Fix::unsafe_edit(Edit::range_replacement(
|
||||
checker
|
||||
.locator()
|
||||
.slice(
|
||||
parenthesized_range(
|
||||
test.into(),
|
||||
expr.into(),
|
||||
checker.indexer().comment_ranges(),
|
||||
checker.locator().contents(),
|
||||
)
|
||||
.to_string(),
|
||||
expr.range(),
|
||||
)));
|
||||
} else if checker.semantic().is_builtin("bool") {
|
||||
diagnostic.set_fix(Fix::unsafe_edit(Edit::range_replacement(
|
||||
checker.generator().expr(
|
||||
&ast::ExprCall {
|
||||
func: Box::new(
|
||||
ast::ExprName {
|
||||
id: "bool".into(),
|
||||
ctx: ExprContext::Load,
|
||||
range: TextRange::default(),
|
||||
}
|
||||
.into(),
|
||||
),
|
||||
arguments: Arguments {
|
||||
args: vec![test.clone()],
|
||||
keywords: vec![],
|
||||
.unwrap_or(test.range()),
|
||||
)
|
||||
.to_string(),
|
||||
expr.range(),
|
||||
)));
|
||||
} else if checker.semantic().is_builtin("bool") {
|
||||
diagnostic.set_fix(Fix::unsafe_edit(Edit::range_replacement(
|
||||
checker.generator().expr(
|
||||
&ast::ExprCall {
|
||||
func: Box::new(
|
||||
ast::ExprName {
|
||||
id: "bool".into(),
|
||||
ctx: ExprContext::Load,
|
||||
range: TextRange::default(),
|
||||
},
|
||||
}
|
||||
.into(),
|
||||
),
|
||||
arguments: Arguments {
|
||||
args: vec![test.clone()],
|
||||
keywords: vec![],
|
||||
range: TextRange::default(),
|
||||
}
|
||||
.into(),
|
||||
),
|
||||
expr.range(),
|
||||
)));
|
||||
};
|
||||
}
|
||||
},
|
||||
range: TextRange::default(),
|
||||
}
|
||||
.into(),
|
||||
),
|
||||
expr.range(),
|
||||
)));
|
||||
};
|
||||
checker.diagnostics.push(diagnostic);
|
||||
}
|
||||
|
||||
|
@ -215,19 +212,17 @@ pub(crate) fn if_expr_with_false_true(
|
|||
}
|
||||
|
||||
let mut diagnostic = Diagnostic::new(IfExprWithFalseTrue, expr.range());
|
||||
if checker.patch(diagnostic.kind.rule()) {
|
||||
diagnostic.set_fix(Fix::unsafe_edit(Edit::range_replacement(
|
||||
checker.generator().expr(
|
||||
&ast::ExprUnaryOp {
|
||||
op: UnaryOp::Not,
|
||||
operand: Box::new(test.clone()),
|
||||
range: TextRange::default(),
|
||||
}
|
||||
.into(),
|
||||
),
|
||||
expr.range(),
|
||||
)));
|
||||
}
|
||||
diagnostic.set_fix(Fix::unsafe_edit(Edit::range_replacement(
|
||||
checker.generator().expr(
|
||||
&ast::ExprUnaryOp {
|
||||
op: UnaryOp::Not,
|
||||
operand: Box::new(test.clone()),
|
||||
range: TextRange::default(),
|
||||
}
|
||||
.into(),
|
||||
),
|
||||
expr.range(),
|
||||
)));
|
||||
checker.diagnostics.push(diagnostic);
|
||||
}
|
||||
|
||||
|
@ -269,20 +264,18 @@ pub(crate) fn twisted_arms_in_ifexpr(
|
|||
},
|
||||
expr.range(),
|
||||
);
|
||||
if checker.patch(diagnostic.kind.rule()) {
|
||||
let node = body.clone();
|
||||
let node1 = orelse.clone();
|
||||
let node2 = orelse.clone();
|
||||
let node3 = ast::ExprIfExp {
|
||||
test: Box::new(node2),
|
||||
body: Box::new(node1),
|
||||
orelse: Box::new(node),
|
||||
range: TextRange::default(),
|
||||
};
|
||||
diagnostic.set_fix(Fix::unsafe_edit(Edit::range_replacement(
|
||||
checker.generator().expr(&node3.into()),
|
||||
expr.range(),
|
||||
)));
|
||||
}
|
||||
let node = body.clone();
|
||||
let node1 = orelse.clone();
|
||||
let node2 = orelse.clone();
|
||||
let node3 = ast::ExprIfExp {
|
||||
test: Box::new(node2),
|
||||
body: Box::new(node1),
|
||||
orelse: Box::new(node),
|
||||
range: TextRange::default(),
|
||||
};
|
||||
diagnostic.set_fix(Fix::unsafe_edit(Edit::range_replacement(
|
||||
checker.generator().expr(&node3.into()),
|
||||
expr.range(),
|
||||
)));
|
||||
checker.diagnostics.push(diagnostic);
|
||||
}
|
||||
|
|
|
@ -6,7 +6,6 @@ use ruff_macros::{derive_message_formats, violation};
|
|||
use ruff_python_semantic::ScopeKind;
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::registry::AsRule;
|
||||
|
||||
/// ## What it does
|
||||
/// Checks for negated `==` operators.
|
||||
|
@ -175,18 +174,16 @@ pub(crate) fn negation_with_equal_op(
|
|||
},
|
||||
expr.range(),
|
||||
);
|
||||
if checker.patch(diagnostic.kind.rule()) {
|
||||
let node = ast::ExprCompare {
|
||||
left: left.clone(),
|
||||
ops: vec![CmpOp::NotEq],
|
||||
comparators: comparators.clone(),
|
||||
range: TextRange::default(),
|
||||
};
|
||||
diagnostic.set_fix(Fix::safe_edit(Edit::range_replacement(
|
||||
checker.generator().expr(&node.into()),
|
||||
expr.range(),
|
||||
)));
|
||||
}
|
||||
let node = ast::ExprCompare {
|
||||
left: left.clone(),
|
||||
ops: vec![CmpOp::NotEq],
|
||||
comparators: comparators.clone(),
|
||||
range: TextRange::default(),
|
||||
};
|
||||
diagnostic.set_fix(Fix::safe_edit(Edit::range_replacement(
|
||||
checker.generator().expr(&node.into()),
|
||||
expr.range(),
|
||||
)));
|
||||
checker.diagnostics.push(diagnostic);
|
||||
}
|
||||
|
||||
|
@ -232,18 +229,16 @@ pub(crate) fn negation_with_not_equal_op(
|
|||
},
|
||||
expr.range(),
|
||||
);
|
||||
if checker.patch(diagnostic.kind.rule()) {
|
||||
let node = ast::ExprCompare {
|
||||
left: left.clone(),
|
||||
ops: vec![CmpOp::Eq],
|
||||
comparators: comparators.clone(),
|
||||
range: TextRange::default(),
|
||||
};
|
||||
diagnostic.set_fix(Fix::safe_edit(Edit::range_replacement(
|
||||
checker.generator().expr(&node.into()),
|
||||
expr.range(),
|
||||
)));
|
||||
}
|
||||
let node = ast::ExprCompare {
|
||||
left: left.clone(),
|
||||
ops: vec![CmpOp::Eq],
|
||||
comparators: comparators.clone(),
|
||||
range: TextRange::default(),
|
||||
};
|
||||
diagnostic.set_fix(Fix::safe_edit(Edit::range_replacement(
|
||||
checker.generator().expr(&node.into()),
|
||||
expr.range(),
|
||||
)));
|
||||
checker.diagnostics.push(diagnostic);
|
||||
}
|
||||
|
||||
|
@ -270,32 +265,30 @@ pub(crate) fn double_negation(checker: &mut Checker, expr: &Expr, op: UnaryOp, o
|
|||
},
|
||||
expr.range(),
|
||||
);
|
||||
if checker.patch(diagnostic.kind.rule()) {
|
||||
if checker.semantic().in_boolean_test() {
|
||||
diagnostic.set_fix(Fix::safe_edit(Edit::range_replacement(
|
||||
checker.locator().slice(operand.as_ref()).to_string(),
|
||||
expr.range(),
|
||||
)));
|
||||
} else if checker.semantic().is_builtin("bool") {
|
||||
let node = ast::ExprName {
|
||||
id: "bool".into(),
|
||||
ctx: ExprContext::Load,
|
||||
range: TextRange::default(),
|
||||
};
|
||||
let node1 = ast::ExprCall {
|
||||
func: Box::new(node.into()),
|
||||
arguments: Arguments {
|
||||
args: vec![*operand.clone()],
|
||||
keywords: vec![],
|
||||
range: TextRange::default(),
|
||||
},
|
||||
range: TextRange::default(),
|
||||
};
|
||||
diagnostic.set_fix(Fix::safe_edit(Edit::range_replacement(
|
||||
checker.generator().expr(&node1.into()),
|
||||
expr.range(),
|
||||
)));
|
||||
if checker.semantic().in_boolean_test() {
|
||||
diagnostic.set_fix(Fix::safe_edit(Edit::range_replacement(
|
||||
checker.locator().slice(operand.as_ref()).to_string(),
|
||||
expr.range(),
|
||||
)));
|
||||
} else if checker.semantic().is_builtin("bool") {
|
||||
let node = ast::ExprName {
|
||||
id: "bool".into(),
|
||||
ctx: ExprContext::Load,
|
||||
range: TextRange::default(),
|
||||
};
|
||||
}
|
||||
let node1 = ast::ExprCall {
|
||||
func: Box::new(node.into()),
|
||||
arguments: Arguments {
|
||||
args: vec![*operand.clone()],
|
||||
keywords: vec![],
|
||||
range: TextRange::default(),
|
||||
},
|
||||
range: TextRange::default(),
|
||||
};
|
||||
diagnostic.set_fix(Fix::safe_edit(Edit::range_replacement(
|
||||
checker.generator().expr(&node1.into()),
|
||||
expr.range(),
|
||||
)));
|
||||
};
|
||||
checker.diagnostics.push(diagnostic);
|
||||
}
|
||||
|
|
|
@ -9,7 +9,6 @@ use ruff_text_size::{Ranged, TextRange};
|
|||
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::fix::edits::fits;
|
||||
use crate::registry::AsRule;
|
||||
|
||||
use super::fix_with;
|
||||
|
||||
|
@ -124,32 +123,30 @@ pub(crate) fn multiple_with_statements(
|
|||
MultipleWithStatements,
|
||||
TextRange::new(with_stmt.start(), colon.end()),
|
||||
);
|
||||
if checker.patch(diagnostic.kind.rule()) {
|
||||
if !checker
|
||||
.indexer()
|
||||
.comment_ranges()
|
||||
.intersects(TextRange::new(with_stmt.start(), with_stmt.body[0].start()))
|
||||
{
|
||||
match fix_with::fix_multiple_with_statements(
|
||||
checker.locator(),
|
||||
checker.stylist(),
|
||||
with_stmt,
|
||||
) {
|
||||
Ok(edit) => {
|
||||
if edit.content().map_or(true, |content| {
|
||||
fits(
|
||||
content,
|
||||
with_stmt.into(),
|
||||
checker.locator(),
|
||||
checker.settings.line_length,
|
||||
checker.settings.tab_size,
|
||||
)
|
||||
}) {
|
||||
diagnostic.set_fix(Fix::unsafe_edit(edit));
|
||||
}
|
||||
if !checker
|
||||
.indexer()
|
||||
.comment_ranges()
|
||||
.intersects(TextRange::new(with_stmt.start(), with_stmt.body[0].start()))
|
||||
{
|
||||
match fix_with::fix_multiple_with_statements(
|
||||
checker.locator(),
|
||||
checker.stylist(),
|
||||
with_stmt,
|
||||
) {
|
||||
Ok(edit) => {
|
||||
if edit.content().map_or(true, |content| {
|
||||
fits(
|
||||
content,
|
||||
with_stmt.into(),
|
||||
checker.locator(),
|
||||
checker.settings.line_length,
|
||||
checker.settings.tab_size,
|
||||
)
|
||||
}) {
|
||||
diagnostic.set_fix(Fix::unsafe_edit(edit));
|
||||
}
|
||||
Err(err) => error!("Failed to fix nested with: {err}"),
|
||||
}
|
||||
Err(err) => error!("Failed to fix nested with: {err}"),
|
||||
}
|
||||
}
|
||||
checker.diagnostics.push(diagnostic);
|
||||
|
|
|
@ -18,7 +18,6 @@ use crate::cst::helpers::space;
|
|||
use crate::cst::matchers::{match_function_def, match_if, match_indented_block, match_statement};
|
||||
use crate::fix::codemods::CodegenStylist;
|
||||
use crate::fix::edits::fits;
|
||||
use crate::registry::AsRule;
|
||||
|
||||
/// ## What it does
|
||||
/// Checks for nested `if` statements that can be collapsed into a single `if`
|
||||
|
@ -101,33 +100,31 @@ pub(crate) fn nested_if_statements(
|
|||
CollapsibleIf,
|
||||
TextRange::new(nested_if.start(), colon.end()),
|
||||
);
|
||||
if checker.patch(diagnostic.kind.rule()) {
|
||||
// The fixer preserves comments in the nested body, but removes comments between
|
||||
// the outer and inner if statements.
|
||||
if !checker
|
||||
.indexer()
|
||||
.comment_ranges()
|
||||
.intersects(TextRange::new(
|
||||
nested_if.start(),
|
||||
nested_if.body()[0].start(),
|
||||
))
|
||||
{
|
||||
match collapse_nested_if(checker.locator(), checker.stylist(), nested_if) {
|
||||
Ok(edit) => {
|
||||
if edit.content().map_or(true, |content| {
|
||||
fits(
|
||||
content,
|
||||
(&nested_if).into(),
|
||||
checker.locator(),
|
||||
checker.settings.line_length,
|
||||
checker.settings.tab_size,
|
||||
)
|
||||
}) {
|
||||
diagnostic.set_fix(Fix::unsafe_edit(edit));
|
||||
}
|
||||
// The fixer preserves comments in the nested body, but removes comments between
|
||||
// the outer and inner if statements.
|
||||
if !checker
|
||||
.indexer()
|
||||
.comment_ranges()
|
||||
.intersects(TextRange::new(
|
||||
nested_if.start(),
|
||||
nested_if.body()[0].start(),
|
||||
))
|
||||
{
|
||||
match collapse_nested_if(checker.locator(), checker.stylist(), nested_if) {
|
||||
Ok(edit) => {
|
||||
if edit.content().map_or(true, |content| {
|
||||
fits(
|
||||
content,
|
||||
(&nested_if).into(),
|
||||
checker.locator(),
|
||||
checker.settings.line_length,
|
||||
checker.settings.tab_size,
|
||||
)
|
||||
}) {
|
||||
diagnostic.set_fix(Fix::unsafe_edit(edit));
|
||||
}
|
||||
Err(err) => error!("Failed to fix nested if: {err}"),
|
||||
}
|
||||
Err(err) => error!("Failed to fix nested if: {err}"),
|
||||
}
|
||||
}
|
||||
checker.diagnostics.push(diagnostic);
|
||||
|
|
|
@ -9,7 +9,6 @@ use ruff_text_size::{Ranged, TextRange};
|
|||
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::fix::edits::fits;
|
||||
use crate::registry::AsRule;
|
||||
|
||||
/// ## What it does
|
||||
/// Checks for `if` statements that can be replaced with `dict.get` calls.
|
||||
|
@ -184,13 +183,11 @@ pub(crate) fn use_dict_get_with_default(checker: &mut Checker, stmt_if: &ast::St
|
|||
},
|
||||
stmt_if.range(),
|
||||
);
|
||||
if checker.patch(diagnostic.kind.rule()) {
|
||||
if !checker.indexer().has_comments(stmt_if, checker.locator()) {
|
||||
diagnostic.set_fix(Fix::unsafe_edit(Edit::range_replacement(
|
||||
contents,
|
||||
stmt_if.range(),
|
||||
)));
|
||||
}
|
||||
if !checker.indexer().has_comments(stmt_if, checker.locator()) {
|
||||
diagnostic.set_fix(Fix::unsafe_edit(Edit::range_replacement(
|
||||
contents,
|
||||
stmt_if.range(),
|
||||
)));
|
||||
}
|
||||
checker.diagnostics.push(diagnostic);
|
||||
}
|
||||
|
|
|
@ -7,7 +7,6 @@ use ruff_text_size::{Ranged, TextRange};
|
|||
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::fix::edits::fits;
|
||||
use crate::registry::AsRule;
|
||||
|
||||
/// ## What it does
|
||||
/// Check for `if`-`else`-blocks that can be replaced with a ternary operator.
|
||||
|
@ -143,13 +142,11 @@ pub(crate) fn use_ternary_operator(checker: &mut Checker, stmt: &Stmt) {
|
|||
},
|
||||
stmt.range(),
|
||||
);
|
||||
if checker.patch(diagnostic.kind.rule()) {
|
||||
if !checker.indexer().has_comments(stmt, checker.locator()) {
|
||||
diagnostic.set_fix(Fix::unsafe_edit(Edit::range_replacement(
|
||||
contents,
|
||||
stmt.range(),
|
||||
)));
|
||||
}
|
||||
if !checker.indexer().has_comments(stmt, checker.locator()) {
|
||||
diagnostic.set_fix(Fix::unsafe_edit(Edit::range_replacement(
|
||||
contents,
|
||||
stmt.range(),
|
||||
)));
|
||||
}
|
||||
checker.diagnostics.push(diagnostic);
|
||||
}
|
||||
|
|
|
@ -8,7 +8,6 @@ use ruff_python_trivia::{SimpleTokenKind, SimpleTokenizer};
|
|||
use ruff_text_size::{Ranged, TextRange};
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::registry::AsRule;
|
||||
|
||||
/// ## What it does
|
||||
/// Checks for key-existence checks against `dict.keys()` calls.
|
||||
|
@ -109,32 +108,30 @@ fn key_in_dict(
|
|||
},
|
||||
TextRange::new(left_range.start(), right_range.end()),
|
||||
);
|
||||
if checker.patch(diagnostic.kind.rule()) {
|
||||
// Delete from the start of the dot to the end of the expression.
|
||||
if let Some(dot) = SimpleTokenizer::starts_at(value.end(), checker.locator().contents())
|
||||
.skip_trivia()
|
||||
.find(|token| token.kind == SimpleTokenKind::Dot)
|
||||
// Delete from the start of the dot to the end of the expression.
|
||||
if let Some(dot) = SimpleTokenizer::starts_at(value.end(), checker.locator().contents())
|
||||
.skip_trivia()
|
||||
.find(|token| token.kind == SimpleTokenKind::Dot)
|
||||
{
|
||||
// If the `.keys()` is followed by (e.g.) a keyword, we need to insert a space,
|
||||
// since we're removing parentheses, which could lead to invalid syntax, as in:
|
||||
// ```python
|
||||
// if key in foo.keys()and bar:
|
||||
// ```
|
||||
let range = TextRange::new(dot.start(), right.end());
|
||||
if checker
|
||||
.locator()
|
||||
.after(range.end())
|
||||
.chars()
|
||||
.next()
|
||||
.is_some_and(|char| char.is_ascii_alphabetic())
|
||||
{
|
||||
// If the `.keys()` is followed by (e.g.) a keyword, we need to insert a space,
|
||||
// since we're removing parentheses, which could lead to invalid syntax, as in:
|
||||
// ```python
|
||||
// if key in foo.keys()and bar:
|
||||
// ```
|
||||
let range = TextRange::new(dot.start(), right.end());
|
||||
if checker
|
||||
.locator()
|
||||
.after(range.end())
|
||||
.chars()
|
||||
.next()
|
||||
.is_some_and(|char| char.is_ascii_alphabetic())
|
||||
{
|
||||
diagnostic.set_fix(Fix::unsafe_edit(Edit::range_replacement(
|
||||
" ".to_string(),
|
||||
range,
|
||||
)));
|
||||
} else {
|
||||
diagnostic.set_fix(Fix::unsafe_edit(Edit::range_deletion(range)));
|
||||
}
|
||||
diagnostic.set_fix(Fix::unsafe_edit(Edit::range_replacement(
|
||||
" ".to_string(),
|
||||
range,
|
||||
)));
|
||||
} else {
|
||||
diagnostic.set_fix(Fix::unsafe_edit(Edit::range_deletion(range)));
|
||||
}
|
||||
}
|
||||
checker.diagnostics.push(diagnostic);
|
||||
|
|
|
@ -4,7 +4,6 @@ use ruff_python_ast::{self as ast, Arguments, Constant, ElifElseClause, Expr, Ex
|
|||
use ruff_text_size::{Ranged, TextRange};
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::registry::AsRule;
|
||||
|
||||
/// ## What it does
|
||||
/// Checks for `if` statements that can be replaced with `bool`.
|
||||
|
@ -100,49 +99,47 @@ pub(crate) fn needless_bool(checker: &mut Checker, stmt: &Stmt) {
|
|||
|
||||
let condition = checker.generator().expr(if_test);
|
||||
let mut diagnostic = Diagnostic::new(NeedlessBool { condition }, range);
|
||||
if checker.patch(diagnostic.kind.rule()) {
|
||||
if matches!(if_return, Bool::True)
|
||||
&& matches!(else_return, Bool::False)
|
||||
&& !checker.indexer().has_comments(&range, checker.locator())
|
||||
&& (if_test.is_compare_expr() || checker.semantic().is_builtin("bool"))
|
||||
{
|
||||
if if_test.is_compare_expr() {
|
||||
// If the condition is a comparison, we can replace it with the condition.
|
||||
let node = ast::StmtReturn {
|
||||
value: Some(Box::new(if_test.clone())),
|
||||
range: TextRange::default(),
|
||||
};
|
||||
diagnostic.set_fix(Fix::unsafe_edit(Edit::range_replacement(
|
||||
checker.generator().stmt(&node.into()),
|
||||
range,
|
||||
)));
|
||||
} else {
|
||||
// Otherwise, we need to wrap the condition in a call to `bool`. (We've already
|
||||
// verified, above, that `bool` is a builtin.)
|
||||
let node = ast::ExprName {
|
||||
id: "bool".into(),
|
||||
ctx: ExprContext::Load,
|
||||
range: TextRange::default(),
|
||||
};
|
||||
let node1 = ast::ExprCall {
|
||||
func: Box::new(node.into()),
|
||||
arguments: Arguments {
|
||||
args: vec![if_test.clone()],
|
||||
keywords: vec![],
|
||||
range: TextRange::default(),
|
||||
},
|
||||
range: TextRange::default(),
|
||||
};
|
||||
let node2 = ast::StmtReturn {
|
||||
value: Some(Box::new(node1.into())),
|
||||
range: TextRange::default(),
|
||||
};
|
||||
diagnostic.set_fix(Fix::unsafe_edit(Edit::range_replacement(
|
||||
checker.generator().stmt(&node2.into()),
|
||||
range,
|
||||
)));
|
||||
if matches!(if_return, Bool::True)
|
||||
&& matches!(else_return, Bool::False)
|
||||
&& !checker.indexer().has_comments(&range, checker.locator())
|
||||
&& (if_test.is_compare_expr() || checker.semantic().is_builtin("bool"))
|
||||
{
|
||||
if if_test.is_compare_expr() {
|
||||
// If the condition is a comparison, we can replace it with the condition.
|
||||
let node = ast::StmtReturn {
|
||||
value: Some(Box::new(if_test.clone())),
|
||||
range: TextRange::default(),
|
||||
};
|
||||
}
|
||||
diagnostic.set_fix(Fix::unsafe_edit(Edit::range_replacement(
|
||||
checker.generator().stmt(&node.into()),
|
||||
range,
|
||||
)));
|
||||
} else {
|
||||
// Otherwise, we need to wrap the condition in a call to `bool`. (We've already
|
||||
// verified, above, that `bool` is a builtin.)
|
||||
let node = ast::ExprName {
|
||||
id: "bool".into(),
|
||||
ctx: ExprContext::Load,
|
||||
range: TextRange::default(),
|
||||
};
|
||||
let node1 = ast::ExprCall {
|
||||
func: Box::new(node.into()),
|
||||
arguments: Arguments {
|
||||
args: vec![if_test.clone()],
|
||||
keywords: vec![],
|
||||
range: TextRange::default(),
|
||||
},
|
||||
range: TextRange::default(),
|
||||
};
|
||||
let node2 = ast::StmtReturn {
|
||||
value: Some(Box::new(node1.into())),
|
||||
range: TextRange::default(),
|
||||
};
|
||||
diagnostic.set_fix(Fix::unsafe_edit(Edit::range_replacement(
|
||||
checker.generator().stmt(&node2.into()),
|
||||
range,
|
||||
)));
|
||||
};
|
||||
}
|
||||
checker.diagnostics.push(diagnostic);
|
||||
}
|
||||
|
|
|
@ -11,7 +11,6 @@ use ruff_text_size::{Ranged, TextRange};
|
|||
use crate::checkers::ast::Checker;
|
||||
use crate::fix::edits::fits;
|
||||
use crate::line_width::LineWidthBuilder;
|
||||
use crate::registry::AsRule;
|
||||
|
||||
/// ## What it does
|
||||
/// Checks for `for` loops that can be replaced with a builtin function, like
|
||||
|
@ -114,7 +113,7 @@ pub(crate) fn convert_for_loop_to_any_all(checker: &mut Checker, stmt: &Stmt) {
|
|||
},
|
||||
TextRange::new(stmt.start(), terminal.stmt.end()),
|
||||
);
|
||||
if checker.patch(diagnostic.kind.rule()) && checker.semantic().is_builtin("any") {
|
||||
if checker.semantic().is_builtin("any") {
|
||||
diagnostic.set_fix(Fix::unsafe_edit(Edit::replacement(
|
||||
contents,
|
||||
stmt.start(),
|
||||
|
@ -200,7 +199,7 @@ pub(crate) fn convert_for_loop_to_any_all(checker: &mut Checker, stmt: &Stmt) {
|
|||
},
|
||||
TextRange::new(stmt.start(), terminal.stmt.end()),
|
||||
);
|
||||
if checker.patch(diagnostic.kind.rule()) && checker.semantic().is_builtin("all") {
|
||||
if checker.semantic().is_builtin("all") {
|
||||
diagnostic.set_fix(Fix::unsafe_edit(Edit::replacement(
|
||||
contents,
|
||||
stmt.start(),
|
||||
|
|
|
@ -8,7 +8,6 @@ use ruff_text_size::{TextLen, TextRange};
|
|||
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::importer::ImportRequest;
|
||||
use crate::registry::AsRule;
|
||||
|
||||
/// ## What it does
|
||||
/// Checks for `try`-`except`-`pass` blocks that can be replaced with the
|
||||
|
@ -132,28 +131,25 @@ pub(crate) fn suppressible_exception(
|
|||
},
|
||||
stmt.range(),
|
||||
);
|
||||
if checker.patch(diagnostic.kind.rule()) {
|
||||
if !checker.indexer().has_comments(stmt, checker.locator()) {
|
||||
diagnostic.try_set_fix(|| {
|
||||
// let range = statement_range(stmt, checker.locator(), checker.indexer());
|
||||
if !checker.indexer().has_comments(stmt, checker.locator()) {
|
||||
diagnostic.try_set_fix(|| {
|
||||
// let range = statement_range(stmt, checker.locator(), checker.indexer());
|
||||
|
||||
let (import_edit, binding) = checker.importer().get_or_import_symbol(
|
||||
&ImportRequest::import("contextlib", "suppress"),
|
||||
stmt.start(),
|
||||
checker.semantic(),
|
||||
)?;
|
||||
let replace_try = Edit::range_replacement(
|
||||
format!("with {binding}({exception})"),
|
||||
TextRange::at(stmt.start(), "try".text_len()),
|
||||
);
|
||||
let remove_handler =
|
||||
Edit::range_deletion(checker.locator().full_lines_range(*range));
|
||||
Ok(Fix::unsafe_edits(
|
||||
import_edit,
|
||||
[replace_try, remove_handler],
|
||||
))
|
||||
});
|
||||
}
|
||||
let (import_edit, binding) = checker.importer().get_or_import_symbol(
|
||||
&ImportRequest::import("contextlib", "suppress"),
|
||||
stmt.start(),
|
||||
checker.semantic(),
|
||||
)?;
|
||||
let replace_try = Edit::range_replacement(
|
||||
format!("with {binding}({exception})"),
|
||||
TextRange::at(stmt.start(), "try".text_len()),
|
||||
);
|
||||
let remove_handler = Edit::range_deletion(checker.locator().full_lines_range(*range));
|
||||
Ok(Fix::unsafe_edits(
|
||||
import_edit,
|
||||
[replace_try, remove_handler],
|
||||
))
|
||||
});
|
||||
}
|
||||
checker.diagnostics.push(diagnostic);
|
||||
}
|
||||
|
|
|
@ -14,7 +14,6 @@ use crate::cst::helpers::or_space;
|
|||
use crate::cst::matchers::{match_comparison, transform_expression};
|
||||
use crate::fix::edits::pad;
|
||||
use crate::fix::snippet::SourceCodeSnippet;
|
||||
use crate::registry::AsRule;
|
||||
|
||||
/// ## What it does
|
||||
/// Checks for conditions that position a constant on the left-hand side of the
|
||||
|
@ -193,12 +192,10 @@ pub(crate) fn yoda_conditions(
|
|||
},
|
||||
expr.range(),
|
||||
);
|
||||
if checker.patch(diagnostic.kind.rule()) {
|
||||
diagnostic.set_fix(Fix::safe_edit(Edit::range_replacement(
|
||||
pad(suggestion, expr.range(), checker.locator()),
|
||||
expr.range(),
|
||||
)));
|
||||
}
|
||||
diagnostic.set_fix(Fix::safe_edit(Edit::range_replacement(
|
||||
pad(suggestion, expr.range(), checker.locator()),
|
||||
expr.range(),
|
||||
)));
|
||||
checker.diagnostics.push(diagnostic);
|
||||
} else {
|
||||
checker.diagnostics.push(Diagnostic::new(
|
||||
|
|
|
@ -8,7 +8,7 @@ use ruff_python_codegen::Generator;
|
|||
use ruff_python_stdlib::identifiers::is_identifier;
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::registry::AsRule;
|
||||
|
||||
use crate::rules::flake8_tidy_imports::settings::Strictness;
|
||||
|
||||
/// ## What it does
|
||||
|
@ -124,13 +124,11 @@ pub(crate) fn banned_relative_import(
|
|||
};
|
||||
if level? > strictness_level {
|
||||
let mut diagnostic = Diagnostic::new(RelativeImports { strictness }, stmt.range());
|
||||
if checker.patch(diagnostic.kind.rule()) {
|
||||
if let Some(fix) =
|
||||
fix_banned_relative_import(stmt, level, module, module_path, checker.generator())
|
||||
{
|
||||
diagnostic.set_fix(fix);
|
||||
};
|
||||
}
|
||||
if let Some(fix) =
|
||||
fix_banned_relative_import(stmt, level, module, module_path, checker.generator())
|
||||
{
|
||||
diagnostic.set_fix(fix);
|
||||
};
|
||||
Some(diagnostic)
|
||||
} else {
|
||||
None
|
||||
|
|
|
@ -7,11 +7,7 @@ use ruff_text_size::{TextLen, TextRange, TextSize};
|
|||
use ruff_diagnostics::{AlwaysFixableViolation, Diagnostic, Edit, Fix, Violation};
|
||||
use ruff_macros::{derive_message_formats, violation};
|
||||
|
||||
use crate::settings::LinterSettings;
|
||||
use crate::{
|
||||
directives::{TodoComment, TodoDirective, TodoDirectiveKind},
|
||||
registry::Rule,
|
||||
};
|
||||
use crate::directives::{TodoComment, TodoDirective, TodoDirectiveKind};
|
||||
|
||||
/// ## What it does
|
||||
/// Checks that a TODO comment is labelled with "TODO".
|
||||
|
@ -240,7 +236,6 @@ pub(crate) fn todos(
|
|||
todo_comments: &[TodoComment],
|
||||
locator: &Locator,
|
||||
indexer: &Indexer,
|
||||
settings: &LinterSettings,
|
||||
) {
|
||||
for todo_comment in todo_comments {
|
||||
let TodoComment {
|
||||
|
@ -256,7 +251,7 @@ pub(crate) fn todos(
|
|||
continue;
|
||||
}
|
||||
|
||||
directive_errors(diagnostics, directive, settings);
|
||||
directive_errors(diagnostics, directive);
|
||||
static_errors(diagnostics, content, range, directive);
|
||||
|
||||
let mut has_issue_link = false;
|
||||
|
@ -300,11 +295,7 @@ pub(crate) fn todos(
|
|||
}
|
||||
|
||||
/// Check that the directive itself is valid. This function modifies `diagnostics` in-place.
|
||||
fn directive_errors(
|
||||
diagnostics: &mut Vec<Diagnostic>,
|
||||
directive: &TodoDirective,
|
||||
settings: &LinterSettings,
|
||||
) {
|
||||
fn directive_errors(diagnostics: &mut Vec<Diagnostic>, directive: &TodoDirective) {
|
||||
if directive.content == "TODO" {
|
||||
return;
|
||||
}
|
||||
|
@ -318,12 +309,10 @@ fn directive_errors(
|
|||
directive.range,
|
||||
);
|
||||
|
||||
if settings.rules.should_fix(Rule::InvalidTodoCapitalization) {
|
||||
diagnostic.set_fix(Fix::safe_edit(Edit::range_replacement(
|
||||
"TODO".to_string(),
|
||||
directive.range,
|
||||
)));
|
||||
}
|
||||
diagnostic.set_fix(Fix::safe_edit(Edit::range_replacement(
|
||||
"TODO".to_string(),
|
||||
directive.range,
|
||||
)));
|
||||
|
||||
diagnostics.push(diagnostic);
|
||||
} else {
|
||||
|
|
|
@ -6,7 +6,6 @@ use ruff_text_size::Ranged;
|
|||
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::fix;
|
||||
use crate::registry::AsRule;
|
||||
|
||||
/// ## What it does
|
||||
/// Checks for an empty type-checking block.
|
||||
|
@ -56,14 +55,12 @@ pub(crate) fn empty_type_checking_block(checker: &mut Checker, stmt: &ast::StmtI
|
|||
}
|
||||
|
||||
let mut diagnostic = Diagnostic::new(EmptyTypeCheckingBlock, stmt.range());
|
||||
if checker.patch(diagnostic.kind.rule()) {
|
||||
// Delete the entire type-checking block.
|
||||
let stmt = checker.semantic().current_statement();
|
||||
let parent = checker.semantic().current_statement_parent();
|
||||
let edit = fix::edits::delete_stmt(stmt, parent, checker.locator(), checker.indexer());
|
||||
diagnostic.set_fix(Fix::safe_edit(edit).isolate(Checker::isolation(
|
||||
checker.semantic().current_statement_parent_id(),
|
||||
)));
|
||||
}
|
||||
// Delete the entire type-checking block.
|
||||
let stmt = checker.semantic().current_statement();
|
||||
let parent = checker.semantic().current_statement_parent();
|
||||
let edit = fix::edits::delete_stmt(stmt, parent, checker.locator(), checker.indexer());
|
||||
diagnostic.set_fix(Fix::safe_edit(edit).isolate(Checker::isolation(
|
||||
checker.semantic().current_statement_parent_id(),
|
||||
)));
|
||||
checker.diagnostics.push(diagnostic);
|
||||
}
|
||||
|
|
|
@ -126,11 +126,7 @@ pub(crate) fn runtime_import_in_type_checking_block(
|
|||
// Generate a diagnostic for every import, but share a fix across all imports within the same
|
||||
// statement (excluding those that are ignored).
|
||||
for (node_id, imports) in errors_by_statement {
|
||||
let fix = if checker.patch(Rule::RuntimeImportInTypeCheckingBlock) {
|
||||
fix_imports(checker, node_id, &imports).ok()
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let fix = fix_imports(checker, node_id, &imports).ok();
|
||||
|
||||
for ImportBinding {
|
||||
import,
|
||||
|
|
|
@ -334,11 +334,7 @@ pub(crate) fn typing_only_runtime_import(
|
|||
// Generate a diagnostic for every import, but share a fix across all imports within the same
|
||||
// statement (excluding those that are ignored).
|
||||
for ((node_id, import_type), imports) in errors_by_statement {
|
||||
let fix = if checker.patch(rule_for(import_type)) {
|
||||
fix_imports(checker, node_id, &imports).ok()
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let fix = fix_imports(checker, node_id, &imports).ok();
|
||||
|
||||
for ImportBinding {
|
||||
import,
|
||||
|
|
|
@ -4,7 +4,6 @@ use ruff_diagnostics::{AlwaysFixableViolation, Diagnostic, Edit, Fix};
|
|||
use ruff_macros::{derive_message_formats, violation};
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::registry::AsRule;
|
||||
|
||||
/// ## What it does
|
||||
/// Checks for `pathlib.Path` objects that are initialized with the current
|
||||
|
@ -76,9 +75,7 @@ pub(crate) fn path_constructor_current_directory(checker: &mut Checker, expr: &E
|
|||
|
||||
if matches!(value.as_str(), "" | ".") {
|
||||
let mut diagnostic = Diagnostic::new(PathConstructorCurrentDirectory, *range);
|
||||
if checker.patch(diagnostic.kind.rule()) {
|
||||
diagnostic.set_fix(Fix::safe_edit(Edit::range_deletion(*range)));
|
||||
}
|
||||
diagnostic.set_fix(Fix::safe_edit(Edit::range_deletion(*range)));
|
||||
checker.diagnostics.push(diagnostic);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,7 +8,7 @@ use ruff_text_size::{Ranged, TextRange};
|
|||
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::fix::snippet::SourceCodeSnippet;
|
||||
use crate::registry::AsRule;
|
||||
|
||||
use crate::rules::flynt::helpers;
|
||||
|
||||
/// ## What it does
|
||||
|
@ -154,11 +154,9 @@ pub(crate) fn static_join_to_fstring(checker: &mut Checker, expr: &Expr, joiner:
|
|||
},
|
||||
expr.range(),
|
||||
);
|
||||
if checker.patch(diagnostic.kind.rule()) {
|
||||
diagnostic.set_fix(Fix::unsafe_edit(Edit::range_replacement(
|
||||
pad(contents, expr.range(), checker.locator()),
|
||||
expr.range(),
|
||||
)));
|
||||
}
|
||||
diagnostic.set_fix(Fix::unsafe_edit(Edit::range_replacement(
|
||||
pad(contents, expr.range(), checker.locator()),
|
||||
expr.range(),
|
||||
)));
|
||||
checker.diagnostics.push(diagnostic);
|
||||
}
|
||||
|
|
|
@ -11,7 +11,7 @@ use ruff_source_file::Locator;
|
|||
use ruff_text_size::{TextRange, TextSize};
|
||||
|
||||
use crate::importer::Importer;
|
||||
use crate::registry::Rule;
|
||||
|
||||
use crate::settings::LinterSettings;
|
||||
|
||||
/// ## What it does
|
||||
|
@ -90,7 +90,6 @@ fn add_required_import(
|
|||
python_ast: &Suite,
|
||||
locator: &Locator,
|
||||
stylist: &Stylist,
|
||||
settings: &LinterSettings,
|
||||
source_type: PySourceType,
|
||||
) -> Option<Diagnostic> {
|
||||
// Don't add imports to semantically-empty files.
|
||||
|
@ -116,12 +115,10 @@ fn add_required_import(
|
|||
MissingRequiredImport(required_import.to_string()),
|
||||
TextRange::default(),
|
||||
);
|
||||
if settings.rules.should_fix(Rule::MissingRequiredImport) {
|
||||
diagnostic.set_fix(Fix::safe_edit(
|
||||
Importer::new(python_ast, locator, stylist)
|
||||
.add_import(required_import, TextSize::default()),
|
||||
));
|
||||
}
|
||||
diagnostic.set_fix(Fix::safe_edit(
|
||||
Importer::new(python_ast, locator, stylist)
|
||||
.add_import(required_import, TextSize::default()),
|
||||
));
|
||||
Some(diagnostic)
|
||||
}
|
||||
|
||||
|
@ -171,7 +168,6 @@ pub(crate) fn add_required_imports(
|
|||
python_ast,
|
||||
locator,
|
||||
stylist,
|
||||
settings,
|
||||
source_type,
|
||||
)
|
||||
})
|
||||
|
@ -189,7 +185,6 @@ pub(crate) fn add_required_imports(
|
|||
python_ast,
|
||||
locator,
|
||||
stylist,
|
||||
settings,
|
||||
source_type,
|
||||
)
|
||||
})
|
||||
|
|
|
@ -13,7 +13,7 @@ use ruff_source_file::{Locator, UniversalNewlines};
|
|||
use ruff_text_size::{Ranged, TextRange};
|
||||
|
||||
use crate::line_width::LineWidthBuilder;
|
||||
use crate::registry::AsRule;
|
||||
|
||||
use crate::settings::LinterSettings;
|
||||
|
||||
use super::super::block::Block;
|
||||
|
@ -138,11 +138,9 @@ pub(crate) fn organize_imports(
|
|||
}
|
||||
|
||||
let mut diagnostic = Diagnostic::new(UnsortedImports, range);
|
||||
if settings.rules.should_fix(diagnostic.kind.rule()) {
|
||||
diagnostic.set_fix(Fix::safe_edit(Edit::range_replacement(
|
||||
indent(&expected, indentation).to_string(),
|
||||
range,
|
||||
)));
|
||||
}
|
||||
diagnostic.set_fix(Fix::safe_edit(Edit::range_replacement(
|
||||
indent(&expected, indentation).to_string(),
|
||||
range,
|
||||
)));
|
||||
Some(diagnostic)
|
||||
}
|
||||
|
|
|
@ -5,7 +5,6 @@ use ruff_text_size::Ranged;
|
|||
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::importer::ImportRequest;
|
||||
use crate::registry::AsRule;
|
||||
|
||||
/// ## What it does
|
||||
/// Checks for uses of deprecated NumPy functions.
|
||||
|
@ -76,17 +75,15 @@ pub(crate) fn deprecated_function(checker: &mut Checker, expr: &Expr) {
|
|||
},
|
||||
expr.range(),
|
||||
);
|
||||
if checker.patch(diagnostic.kind.rule()) {
|
||||
diagnostic.try_set_fix(|| {
|
||||
let (import_edit, binding) = checker.importer().get_or_import_symbol(
|
||||
&ImportRequest::import_from("numpy", replacement),
|
||||
expr.start(),
|
||||
checker.semantic(),
|
||||
)?;
|
||||
let replacement_edit = Edit::range_replacement(binding, expr.range());
|
||||
Ok(Fix::safe_edits(import_edit, [replacement_edit]))
|
||||
});
|
||||
}
|
||||
diagnostic.try_set_fix(|| {
|
||||
let (import_edit, binding) = checker.importer().get_or_import_symbol(
|
||||
&ImportRequest::import_from("numpy", replacement),
|
||||
expr.start(),
|
||||
checker.semantic(),
|
||||
)?;
|
||||
let replacement_edit = Edit::range_replacement(binding, expr.range());
|
||||
Ok(Fix::safe_edits(import_edit, [replacement_edit]))
|
||||
});
|
||||
checker.diagnostics.push(diagnostic);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,7 +4,6 @@ use ruff_python_ast::Expr;
|
|||
use ruff_text_size::Ranged;
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::registry::AsRule;
|
||||
|
||||
/// ## What it does
|
||||
/// Checks for deprecated NumPy type aliases.
|
||||
|
@ -73,18 +72,16 @@ pub(crate) fn deprecated_type_alias(checker: &mut Checker, expr: &Expr) {
|
|||
},
|
||||
expr.range(),
|
||||
);
|
||||
if checker.patch(diagnostic.kind.rule()) {
|
||||
let type_name = match type_name {
|
||||
"unicode" => "str",
|
||||
"long" => "int",
|
||||
_ => type_name,
|
||||
};
|
||||
if checker.semantic().is_builtin(type_name) {
|
||||
diagnostic.set_fix(Fix::safe_edit(Edit::range_replacement(
|
||||
type_name.to_string(),
|
||||
expr.range(),
|
||||
)));
|
||||
}
|
||||
let type_name = match type_name {
|
||||
"unicode" => "str",
|
||||
"long" => "int",
|
||||
_ => type_name,
|
||||
};
|
||||
if checker.semantic().is_builtin(type_name) {
|
||||
diagnostic.set_fix(Fix::safe_edit(Edit::range_replacement(
|
||||
type_name.to_string(),
|
||||
expr.range(),
|
||||
)));
|
||||
}
|
||||
checker.diagnostics.push(diagnostic);
|
||||
}
|
||||
|
|
|
@ -9,7 +9,6 @@ use ruff_text_size::Ranged;
|
|||
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::fix::edits::{remove_argument, Parentheses};
|
||||
use crate::registry::AsRule;
|
||||
|
||||
/// ## What it does
|
||||
/// Checks for `inplace=True` usages in `pandas` function and method
|
||||
|
@ -71,26 +70,24 @@ pub(crate) fn inplace_argument(checker: &mut Checker, call: &ast::ExprCall) {
|
|||
if arg == "inplace" {
|
||||
if is_const_true(&keyword.value) {
|
||||
let mut diagnostic = Diagnostic::new(PandasUseOfInplaceArgument, keyword.range());
|
||||
if checker.patch(diagnostic.kind.rule()) {
|
||||
// Avoid applying the fix if:
|
||||
// 1. The keyword argument is followed by a star argument (we can't be certain that
|
||||
// the star argument _doesn't_ contain an override).
|
||||
// 2. The call is part of a larger expression (we're converting an expression to a
|
||||
// statement, and expressions can't contain statements).
|
||||
let statement = checker.semantic().current_statement();
|
||||
if !seen_star
|
||||
&& checker.semantic().current_expression_parent().is_none()
|
||||
&& statement.is_expr_stmt()
|
||||
{
|
||||
if let Some(fix) = convert_inplace_argument_to_assignment(
|
||||
call,
|
||||
keyword,
|
||||
statement,
|
||||
checker.indexer().comment_ranges(),
|
||||
checker.locator(),
|
||||
) {
|
||||
diagnostic.set_fix(fix);
|
||||
}
|
||||
// Avoid applying the fix if:
|
||||
// 1. The keyword argument is followed by a star argument (we can't be certain that
|
||||
// the star argument _doesn't_ contain an override).
|
||||
// 2. The call is part of a larger expression (we're converting an expression to a
|
||||
// statement, and expressions can't contain statements).
|
||||
let statement = checker.semantic().current_statement();
|
||||
if !seen_star
|
||||
&& checker.semantic().current_expression_parent().is_none()
|
||||
&& statement.is_expr_stmt()
|
||||
{
|
||||
if let Some(fix) = convert_inplace_argument_to_assignment(
|
||||
call,
|
||||
keyword,
|
||||
statement,
|
||||
checker.indexer().comment_ranges(),
|
||||
checker.locator(),
|
||||
) {
|
||||
diagnostic.set_fix(fix);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -8,7 +8,6 @@ use ruff_python_ast::{Arguments, Expr};
|
|||
use ruff_text_size::Ranged;
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::registry::AsRule;
|
||||
|
||||
/// ## What it does
|
||||
/// Checks for uses of `dict.items()` that discard either the key or the value
|
||||
|
@ -100,18 +99,16 @@ pub(crate) fn incorrect_dict_iterator(checker: &mut Checker, stmt_for: &ast::Stm
|
|||
},
|
||||
func.range(),
|
||||
);
|
||||
if checker.patch(diagnostic.kind.rule()) {
|
||||
let replace_attribute = Edit::range_replacement("values".to_string(), attr.range());
|
||||
let replace_target = Edit::range_replacement(
|
||||
pad(
|
||||
checker.locator().slice(value).to_string(),
|
||||
stmt_for.target.range(),
|
||||
checker.locator(),
|
||||
),
|
||||
let replace_attribute = Edit::range_replacement("values".to_string(), attr.range());
|
||||
let replace_target = Edit::range_replacement(
|
||||
pad(
|
||||
checker.locator().slice(value).to_string(),
|
||||
stmt_for.target.range(),
|
||||
);
|
||||
diagnostic.set_fix(Fix::unsafe_edits(replace_attribute, [replace_target]));
|
||||
}
|
||||
checker.locator(),
|
||||
),
|
||||
stmt_for.target.range(),
|
||||
);
|
||||
diagnostic.set_fix(Fix::unsafe_edits(replace_attribute, [replace_target]));
|
||||
checker.diagnostics.push(diagnostic);
|
||||
}
|
||||
(false, true) => {
|
||||
|
@ -122,18 +119,16 @@ pub(crate) fn incorrect_dict_iterator(checker: &mut Checker, stmt_for: &ast::Stm
|
|||
},
|
||||
func.range(),
|
||||
);
|
||||
if checker.patch(diagnostic.kind.rule()) {
|
||||
let replace_attribute = Edit::range_replacement("keys".to_string(), attr.range());
|
||||
let replace_target = Edit::range_replacement(
|
||||
pad(
|
||||
checker.locator().slice(key).to_string(),
|
||||
stmt_for.target.range(),
|
||||
checker.locator(),
|
||||
),
|
||||
let replace_attribute = Edit::range_replacement("keys".to_string(), attr.range());
|
||||
let replace_target = Edit::range_replacement(
|
||||
pad(
|
||||
checker.locator().slice(key).to_string(),
|
||||
stmt_for.target.range(),
|
||||
);
|
||||
diagnostic.set_fix(Fix::unsafe_edits(replace_attribute, [replace_target]));
|
||||
}
|
||||
checker.locator(),
|
||||
),
|
||||
stmt_for.target.range(),
|
||||
);
|
||||
diagnostic.set_fix(Fix::unsafe_edits(replace_attribute, [replace_target]));
|
||||
checker.diagnostics.push(diagnostic);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,7 +5,6 @@ use ruff_python_ast::{self as ast, Arguments, Expr};
|
|||
use ruff_text_size::TextRange;
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::registry::AsRule;
|
||||
|
||||
/// ## What it does
|
||||
/// Checks for explicit casts to `list` on for-loop iterables.
|
||||
|
@ -91,9 +90,7 @@ pub(crate) fn unnecessary_list_cast(checker: &mut Checker, iter: &Expr) {
|
|||
..
|
||||
}) => {
|
||||
let mut diagnostic = Diagnostic::new(UnnecessaryListCast, *list_range);
|
||||
if checker.patch(diagnostic.kind.rule()) {
|
||||
diagnostic.set_fix(remove_cast(*list_range, *iterable_range));
|
||||
}
|
||||
diagnostic.set_fix(remove_cast(*list_range, *iterable_range));
|
||||
checker.diagnostics.push(diagnostic);
|
||||
}
|
||||
Expr::Name(ast::ExprName {
|
||||
|
@ -119,9 +116,7 @@ pub(crate) fn unnecessary_list_cast(checker: &mut Checker, iter: &Expr) {
|
|||
) {
|
||||
let mut diagnostic =
|
||||
Diagnostic::new(UnnecessaryListCast, *list_range);
|
||||
if checker.patch(diagnostic.kind.rule()) {
|
||||
diagnostic.set_fix(remove_cast(*list_range, *iterable_range));
|
||||
}
|
||||
diagnostic.set_fix(remove_cast(*list_range, *iterable_range));
|
||||
checker.diagnostics.push(diagnostic);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,9 +8,6 @@ use ruff_macros::{derive_message_formats, violation};
|
|||
use ruff_python_index::Indexer;
|
||||
use ruff_source_file::Locator;
|
||||
|
||||
use crate::registry::Rule;
|
||||
use crate::settings::LinterSettings;
|
||||
|
||||
/// ## What it does
|
||||
/// Checks for compound statements (multiple statements on the same line).
|
||||
///
|
||||
|
@ -104,7 +101,6 @@ pub(crate) fn compound_statements(
|
|||
lxr: &[LexResult],
|
||||
locator: &Locator,
|
||||
indexer: &Indexer,
|
||||
settings: &LinterSettings,
|
||||
) {
|
||||
// Track the last seen instance of a variety of tokens.
|
||||
let mut colon = None;
|
||||
|
@ -169,14 +165,12 @@ pub(crate) fn compound_statements(
|
|||
if let Some((start, end)) = semi {
|
||||
let mut diagnostic =
|
||||
Diagnostic::new(UselessSemicolon, TextRange::new(start, end));
|
||||
if settings.rules.should_fix(Rule::UselessSemicolon) {
|
||||
diagnostic.set_fix(Fix::safe_edit(Edit::deletion(
|
||||
indexer
|
||||
.preceded_by_continuations(start, locator)
|
||||
.unwrap_or(start),
|
||||
end,
|
||||
)));
|
||||
};
|
||||
diagnostic.set_fix(Fix::safe_edit(Edit::deletion(
|
||||
indexer
|
||||
.preceded_by_continuations(start, locator)
|
||||
.unwrap_or(start),
|
||||
end,
|
||||
)));
|
||||
diagnostics.push(diagnostic);
|
||||
}
|
||||
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue