diff --git a/resources/test/fixtures/flake8_pytest_style/PT003.py b/resources/test/fixtures/flake8_pytest_style/PT003.py index 965fe50ef4..93fcff085f 100644 --- a/resources/test/fixtures/flake8_pytest_style/PT003.py +++ b/resources/test/fixtures/flake8_pytest_style/PT003.py @@ -14,3 +14,60 @@ def ok_other_scope(): @pytest.fixture(scope="function") def error(): ... + + +@pytest.fixture(scope="function", name="my_fixture") +def error_multiple_args(): + ... + + +@pytest.fixture(name="my_fixture", scope="function") +def error_multiple_args(): + ... + + +@pytest.fixture(name="my_fixture", scope="function", **kwargs) +def error_second_arg(): + ... + + +# pytest.fixture does not take positional arguments, however this +# tests the general case as we use a helper function that should +# work for all cases. +@pytest.fixture("my_fixture", scope="function") +def error_arg(): + ... + + +@pytest.fixture( + scope="function", + name="my_fixture", +) +def error_multiple_args(): + ... + + +@pytest.fixture( + name="my_fixture", + scope="function", +) +def error_multiple_args(): + ... + + +@pytest.fixture( + "hello", + name, + *args + , + + # another comment ,) + + scope=\ + "function" # some comment ), + , + + name2=name, name3="my_fixture", **kwargs +) +def error_multiple_args(): + ... diff --git a/src/autofix/helpers.rs b/src/autofix/helpers.rs index 7eea5292bd..aa75cc45f7 100644 --- a/src/autofix/helpers.rs +++ b/src/autofix/helpers.rs @@ -3,7 +3,9 @@ use itertools::Itertools; use libcst_native::{ Codegen, CodegenState, ImportNames, ParenthesizableWhitespace, SmallStatement, Statement, }; -use rustpython_parser::ast::{ExcepthandlerKind, Location, Stmt, StmtKind}; +use rustpython_parser::ast::{ExcepthandlerKind, Expr, Keyword, Location, Stmt, StmtKind}; +use rustpython_parser::lexer; +use rustpython_parser::lexer::Tok; use crate::ast::helpers; use crate::ast::helpers::to_absolute; @@ -321,6 +323,108 @@ pub fn remove_unused_imports<'a>( } } +/// Generic function te remove (keyword)arguments in function calls +/// and class definitions. (For classes `args` should be considered `bases`) +/// +/// Supports the removal of parentheses when this is the only (kw)arg left. +/// For this behaviour set `remove_parentheses` to `true`. +pub fn remove_argument( + locator: &Locator, + stmt_at: Location, + expr_at: Location, + expr_end: Location, + args: &[Expr], + keywords: &[Keyword], + remove_parentheses: bool, +) -> Result { + // TODO: preserve trailing comments + let contents = locator.slice_source_code_at(stmt_at); + + let mut fix_start = None; + let mut fix_end = None; + + let n_keywords = keywords.len() + args.len(); + if n_keywords == 0 { + bail!("No arguments or keywords to remove"); + } + + if n_keywords == 1 { + // Case 1: there is only one argument. + let mut count: usize = 0; + for (start, tok, end) in lexer::make_tokenizer_located(contents, stmt_at).flatten() { + if matches!(tok, Tok::Lpar) { + if count == 0 { + fix_start = Some(if remove_parentheses { + start + } else { + Location::new(start.row(), start.column() + 1) + }) + } + count += 1; + } + + if matches!(tok, Tok::Rpar) { + count -= 1; + if count == 0 { + fix_end = Some(if remove_parentheses { + end + } else { + Location::new(end.row(), end.column() - 1) + }); + break; + } + } + } + } else if args + .iter() + .map(|node| node.location) + .chain(keywords.iter().map(|node| node.location)) + .any(|location| location > expr_at) + { + // Case 2: argument or keyword is _not_ the last node. + let mut seen_comma = false; + for (start, tok, end) in lexer::make_tokenizer_located(contents, stmt_at).flatten() { + if seen_comma { + if matches!(tok, Tok::NonLogicalNewline) { + // Also delete any non-logical newlines after the comma. + continue; + } + fix_end = Some(if matches!(tok, Tok::Newline) { + end + } else { + start + }); + break; + } + if start == expr_at { + fix_start = Some(start); + } + if fix_start.is_some() && matches!(tok, Tok::Comma) { + seen_comma = true; + } + } + } else { + // Case 3: argument or keyword is the last node, so we have to find the last + // comma in the stmt. + for (start, tok, _) in lexer::make_tokenizer_located(contents, stmt_at).flatten() { + if start == expr_at { + fix_end = Some(expr_end); + break; + } + if matches!(tok, Tok::Comma) { + fix_start = Some(start); + } + } + } + + match (fix_start, fix_end) { + (Some(start), Some(end)) => Ok(Fix::deletion(start, end)), + _ => { + bail!("No fix could be constructed") + } + } +} + #[cfg(test)] mod tests { use anyhow::Result; diff --git a/src/registry.rs b/src/registry.rs index 497e2c832d..eaa22c269a 100644 --- a/src/registry.rs +++ b/src/registry.rs @@ -386,11 +386,11 @@ ruff_macros::define_rule_mapping!( EM102 => violations::FStringInException, EM103 => violations::DotFormatInException, // flake8-pytest-style - PT001 => violations::IncorrectFixtureParenthesesStyle, - PT002 => violations::FixturePositionalArgs, - PT003 => violations::ExtraneousScopeFunction, - PT004 => violations::MissingFixtureNameUnderscore, - PT005 => violations::IncorrectFixtureNameUnderscore, + PT001 => rules::flake8_pytest_style::rules::IncorrectFixtureParenthesesStyle, + PT002 => rules::flake8_pytest_style::rules::FixturePositionalArgs, + PT003 => rules::flake8_pytest_style::rules::ExtraneousScopeFunction, + PT004 => rules::flake8_pytest_style::rules::MissingFixtureNameUnderscore, + PT005 => rules::flake8_pytest_style::rules::IncorrectFixtureNameUnderscore, PT006 => violations::ParametrizeNamesWrongType, PT007 => violations::ParametrizeValuesWrongType, PT008 => violations::PatchWithLambda, @@ -403,13 +403,13 @@ ruff_macros::define_rule_mapping!( PT016 => violations::FailWithoutMessage, PT017 => violations::AssertInExcept, PT018 => violations::CompositeAssertion, - PT019 => violations::FixtureParamWithoutValue, - PT020 => violations::DeprecatedYieldFixture, - PT021 => violations::FixtureFinalizerCallback, - PT022 => violations::UselessYieldFixture, + PT019 => rules::flake8_pytest_style::rules::FixtureParamWithoutValue, + PT020 => rules::flake8_pytest_style::rules::DeprecatedYieldFixture, + PT021 => rules::flake8_pytest_style::rules::FixtureFinalizerCallback, + PT022 => rules::flake8_pytest_style::rules::UselessYieldFixture, PT023 => violations::IncorrectMarkParenthesesStyle, - PT024 => violations::UnnecessaryAsyncioMarkOnFixture, - PT025 => violations::ErroneousUseFixturesOnFixture, + PT024 => rules::flake8_pytest_style::rules::UnnecessaryAsyncioMarkOnFixture, + PT025 => rules::flake8_pytest_style::rules::ErroneousUseFixturesOnFixture, PT026 => violations::UseFixturesWithoutParameters, // flake8-pie PIE790 => rules::flake8_pie::rules::NoUnnecessaryPass, diff --git a/src/rules/flake8_pytest_style/rules/fixture.rs b/src/rules/flake8_pytest_style/rules/fixture.rs index 698099e38c..509a1ff785 100644 --- a/src/rules/flake8_pytest_style/rules/fixture.rs +++ b/src/rules/flake8_pytest_style/rules/fixture.rs @@ -1,4 +1,7 @@ -use rustpython_ast::{Arguments, Expr, ExprKind, Location, Stmt, StmtKind}; +use anyhow::Result; +use log::error; +use ruff_macros::derive_message_formats; +use rustpython_ast::{Arguments, Expr, ExprKind, Keyword, Location, Stmt, StmtKind}; use super::helpers::{ get_mark_decorators, get_mark_name, is_abstractmethod_decorator, is_pytest_fixture, @@ -8,10 +11,164 @@ use crate::ast::helpers::{collect_arg_names, collect_call_path}; use crate::ast::types::Range; use crate::ast::visitor; use crate::ast::visitor::Visitor; +use crate::autofix::helpers::remove_argument; use crate::checkers::ast::Checker; +use crate::define_violation; use crate::fix::Fix; use crate::registry::{Diagnostic, Rule}; -use crate::violations; +use crate::source_code::Locator; +use crate::violation::{AlwaysAutofixableViolation, Violation}; + +define_violation!( + pub struct IncorrectFixtureParenthesesStyle { + pub expected_parens: String, + pub actual_parens: String, + } +); +impl AlwaysAutofixableViolation for IncorrectFixtureParenthesesStyle { + #[derive_message_formats] + fn message(&self) -> String { + let IncorrectFixtureParenthesesStyle { + expected_parens, + actual_parens, + } = self; + format!("Use `@pytest.fixture{expected_parens}` over `@pytest.fixture{actual_parens}`") + } + + fn autofix_title(&self) -> String { + "Add/remove parentheses".to_string() + } +} + +define_violation!( + pub struct FixturePositionalArgs { + pub function: String, + } +); +impl Violation for FixturePositionalArgs { + #[derive_message_formats] + fn message(&self) -> String { + let FixturePositionalArgs { function } = self; + format!("Configuration for fixture `{function}` specified via positional args, use kwargs") + } +} + +define_violation!( + pub struct ExtraneousScopeFunction; +); +impl Violation for ExtraneousScopeFunction { + #[derive_message_formats] + fn message(&self) -> String { + format!("`scope='function'` is implied in `@pytest.fixture()`") + } +} + +define_violation!( + pub struct MissingFixtureNameUnderscore { + pub function: String, + } +); +impl Violation for MissingFixtureNameUnderscore { + #[derive_message_formats] + fn message(&self) -> String { + let MissingFixtureNameUnderscore { function } = self; + format!("Fixture `{function}` does not return anything, add leading underscore") + } +} + +define_violation!( + pub struct IncorrectFixtureNameUnderscore { + pub function: String, + } +); +impl Violation for IncorrectFixtureNameUnderscore { + #[derive_message_formats] + fn message(&self) -> String { + let IncorrectFixtureNameUnderscore { function } = self; + format!("Fixture `{function}` returns a value, remove leading underscore") + } +} + +define_violation!( + pub struct FixtureParamWithoutValue { + pub name: String, + } +); +impl Violation for FixtureParamWithoutValue { + #[derive_message_formats] + fn message(&self) -> String { + let FixtureParamWithoutValue { name } = self; + format!( + "Fixture `{name}` without value is injected as parameter, use \ + `@pytest.mark.usefixtures` instead" + ) + } +} + +define_violation!( + pub struct DeprecatedYieldFixture; +); +impl Violation for DeprecatedYieldFixture { + #[derive_message_formats] + fn message(&self) -> String { + format!("`@pytest.yield_fixture` is deprecated, use `@pytest.fixture`") + } +} + +define_violation!( + pub struct FixtureFinalizerCallback; +); +impl Violation for FixtureFinalizerCallback { + #[derive_message_formats] + fn message(&self) -> String { + format!("Use `yield` instead of `request.addfinalizer`") + } +} + +define_violation!( + pub struct UselessYieldFixture { + pub name: String, + } +); +impl AlwaysAutofixableViolation for UselessYieldFixture { + #[derive_message_formats] + fn message(&self) -> String { + let UselessYieldFixture { name } = self; + format!("No teardown in fixture `{name}`, use `return` instead of `yield`") + } + + fn autofix_title(&self) -> String { + "Replace `yield` with `return`".to_string() + } +} + +define_violation!( + pub struct ErroneousUseFixturesOnFixture; +); +impl AlwaysAutofixableViolation for ErroneousUseFixturesOnFixture { + #[derive_message_formats] + fn message(&self) -> String { + format!("`pytest.mark.usefixtures` has no effect on fixtures") + } + + fn autofix_title(&self) -> String { + "Remove `pytest.mark.usefixtures`".to_string() + } +} + +define_violation!( + pub struct UnnecessaryAsyncioMarkOnFixture; +); +impl AlwaysAutofixableViolation for UnnecessaryAsyncioMarkOnFixture { + #[derive_message_formats] + fn message(&self) -> String { + format!("`pytest.mark.asyncio` is unnecessary for fixtures") + } + + fn autofix_title(&self) -> String { + "Remove `pytest.mark.asyncio`".to_string() + } +} #[derive(Default)] /// Visitor that skips functions @@ -80,7 +237,7 @@ fn pytest_fixture_parentheses( actual: &str, ) { let mut diagnostic = Diagnostic::new( - violations::IncorrectFixtureParenthesesStyle { + IncorrectFixtureParenthesesStyle { expected_parens: preferred.to_string(), actual_parens: actual.to_string(), }, @@ -92,6 +249,17 @@ fn pytest_fixture_parentheses( checker.diagnostics.push(diagnostic); } +pub fn fix_extraneous_scope_function( + locator: &Locator, + stmt_at: Location, + expr_at: Location, + expr_end: Location, + args: &[Expr], + keywords: &[Keyword], +) -> Result { + remove_argument(locator, stmt_at, expr_at, expr_end, args, keywords, false) +} + /// PT001, PT002, PT003 fn check_fixture_decorator(checker: &mut Checker, func_name: &str, decorator: &Expr) { match &decorator.node { @@ -116,7 +284,7 @@ fn check_fixture_decorator(checker: &mut Checker, func_name: &str, decorator: &E if checker.settings.rules.enabled(&Rule::FixturePositionalArgs) && !args.is_empty() { checker.diagnostics.push(Diagnostic::new( - violations::FixturePositionalArgs { + FixturePositionalArgs { function: func_name.to_string(), }, Range::from_located(decorator), @@ -134,10 +302,26 @@ fn check_fixture_decorator(checker: &mut Checker, func_name: &str, decorator: &E if let Some(scope_keyword) = scope_keyword { if keyword_is_literal(scope_keyword, "function") { - checker.diagnostics.push(Diagnostic::new( - violations::ExtraneousScopeFunction, + let mut diagnostic = Diagnostic::new( + ExtraneousScopeFunction, Range::from_located(scope_keyword), - )); + ); + if checker.patch(diagnostic.kind.rule()) { + match fix_extraneous_scope_function( + checker.locator, + decorator.location, + diagnostic.location, + diagnostic.end_location, + args, + keywords, + ) { + Ok(fix) => { + diagnostic.amend(fix); + } + Err(e) => error!("Failed to generate fix: {e}"), + } + } + checker.diagnostics.push(diagnostic); } } } @@ -172,7 +356,7 @@ fn check_fixture_returns(checker: &mut Checker, func: &Stmt, func_name: &str, bo && func_name.starts_with('_') { checker.diagnostics.push(Diagnostic::new( - violations::IncorrectFixtureNameUnderscore { + IncorrectFixtureNameUnderscore { function: func_name.to_string(), }, Range::from_located(func), @@ -186,7 +370,7 @@ fn check_fixture_returns(checker: &mut Checker, func: &Stmt, func_name: &str, bo && !func_name.starts_with('_') { checker.diagnostics.push(Diagnostic::new( - violations::MissingFixtureNameUnderscore { + MissingFixtureNameUnderscore { function: func_name.to_string(), }, Range::from_located(func), @@ -199,7 +383,7 @@ fn check_fixture_returns(checker: &mut Checker, func: &Stmt, func_name: &str, bo if let ExprKind::Yield { .. } = value.node { if visitor.yield_statements.len() == 1 { let mut diagnostic = Diagnostic::new( - violations::UselessYieldFixture { + UselessYieldFixture { name: func_name.to_string(), }, Range::from_located(stmt), @@ -228,7 +412,7 @@ fn check_test_function_args(checker: &mut Checker, args: &Arguments) { let name = &arg.node.arg; if name.starts_with('_') { checker.diagnostics.push(Diagnostic::new( - violations::FixtureParamWithoutValue { + FixtureParamWithoutValue { name: name.to_string(), }, Range::from_located(arg), @@ -241,7 +425,7 @@ fn check_test_function_args(checker: &mut Checker, args: &Arguments) { fn check_fixture_decorator_name(checker: &mut Checker, decorator: &Expr) { if is_pytest_yield_fixture(decorator, checker) { checker.diagnostics.push(Diagnostic::new( - violations::DeprecatedYieldFixture, + DeprecatedYieldFixture, Range::from_located(decorator), )); } @@ -261,7 +445,7 @@ fn check_fixture_addfinalizer(checker: &mut Checker, args: &Arguments, body: &[S if let Some(addfinalizer) = visitor.addfinalizer_call { checker.diagnostics.push(Diagnostic::new( - violations::FixtureFinalizerCallback, + FixtureFinalizerCallback, Range::from_located(addfinalizer), )); } @@ -278,10 +462,8 @@ fn check_fixture_marks(checker: &mut Checker, decorators: &[Expr]) { .enabled(&Rule::UnnecessaryAsyncioMarkOnFixture) { if name == "asyncio" { - let mut diagnostic = Diagnostic::new( - violations::UnnecessaryAsyncioMarkOnFixture, - Range::from_located(mark), - ); + let mut diagnostic = + Diagnostic::new(UnnecessaryAsyncioMarkOnFixture, Range::from_located(mark)); if checker.patch(diagnostic.kind.rule()) { let start = Location::new(mark.location.row(), 0); let end = Location::new(mark.end_location.unwrap().row() + 1, 0); @@ -297,10 +479,8 @@ fn check_fixture_marks(checker: &mut Checker, decorators: &[Expr]) { .enabled(&Rule::ErroneousUseFixturesOnFixture) { if name == "usefixtures" { - let mut diagnostic = Diagnostic::new( - violations::ErroneousUseFixturesOnFixture, - Range::from_located(mark), - ); + let mut diagnostic = + Diagnostic::new(ErroneousUseFixturesOnFixture, Range::from_located(mark)); if checker.patch(diagnostic.kind.rule()) { let start = Location::new(mark.location.row(), 0); let end = Location::new(mark.end_location.unwrap().row() + 1, 0); diff --git a/src/rules/flake8_pytest_style/rules/mod.rs b/src/rules/flake8_pytest_style/rules/mod.rs index 758e54e237..6025deaf75 100644 --- a/src/rules/flake8_pytest_style/rules/mod.rs +++ b/src/rules/flake8_pytest_style/rules/mod.rs @@ -2,7 +2,12 @@ pub use assertion::{ assert_falsy, assert_in_exception_handler, composite_condition, unittest_assertion, }; pub use fail::fail_call; -pub use fixture::fixture; +pub use fixture::{ + fixture, DeprecatedYieldFixture, ErroneousUseFixturesOnFixture, ExtraneousScopeFunction, + FixtureFinalizerCallback, FixtureParamWithoutValue, FixturePositionalArgs, + IncorrectFixtureNameUnderscore, IncorrectFixtureParenthesesStyle, MissingFixtureNameUnderscore, + UnnecessaryAsyncioMarkOnFixture, UselessYieldFixture, +}; pub use imports::{import, import_from}; pub use marks::marks; pub use parametrize::parametrize; diff --git a/src/rules/flake8_pytest_style/snapshots/ruff__rules__flake8_pytest_style__tests__PT003.snap b/src/rules/flake8_pytest_style/snapshots/ruff__rules__flake8_pytest_style__tests__PT003.snap index a43a407bc1..74ae7643fa 100644 --- a/src/rules/flake8_pytest_style/snapshots/ruff__rules__flake8_pytest_style__tests__PT003.snap +++ b/src/rules/flake8_pytest_style/snapshots/ruff__rules__flake8_pytest_style__tests__PT003.snap @@ -10,6 +10,140 @@ expression: diagnostics end_location: row: 14 column: 32 - fix: ~ + fix: + content: + - "" + location: + row: 14 + column: 16 + end_location: + row: 14 + column: 32 + parent: ~ +- kind: + ExtraneousScopeFunction: ~ + location: + row: 19 + column: 16 + end_location: + row: 19 + column: 32 + fix: + content: + - "" + location: + row: 19 + column: 16 + end_location: + row: 19 + column: 34 + parent: ~ +- kind: + ExtraneousScopeFunction: ~ + location: + row: 24 + column: 35 + end_location: + row: 24 + column: 51 + fix: + content: + - "" + location: + row: 24 + column: 33 + end_location: + row: 24 + column: 51 + parent: ~ +- kind: + ExtraneousScopeFunction: ~ + location: + row: 29 + column: 35 + end_location: + row: 29 + column: 51 + fix: + content: + - "" + location: + row: 29 + column: 35 + end_location: + row: 29 + column: 53 + parent: ~ +- kind: + ExtraneousScopeFunction: ~ + location: + row: 37 + column: 30 + end_location: + row: 37 + column: 46 + fix: + content: + - "" + location: + row: 37 + column: 28 + end_location: + row: 37 + column: 46 + parent: ~ +- kind: + ExtraneousScopeFunction: ~ + location: + row: 43 + column: 4 + end_location: + row: 43 + column: 20 + fix: + content: + - "" + location: + row: 43 + column: 4 + end_location: + row: 44 + column: 4 + parent: ~ +- kind: + ExtraneousScopeFunction: ~ + location: + row: 52 + column: 4 + end_location: + row: 52 + column: 20 + fix: + content: + - "" + location: + row: 51 + column: 21 + end_location: + row: 52 + column: 20 + parent: ~ +- kind: + ExtraneousScopeFunction: ~ + location: + row: 66 + column: 4 + end_location: + row: 67 + column: 18 + fix: + content: + - "" + location: + row: 66 + column: 4 + end_location: + row: 70 + column: 4 parent: ~ diff --git a/src/rules/pyupgrade/fixes.rs b/src/rules/pyupgrade/fixes.rs index be3c478d1a..84b3cf4fcf 100644 --- a/src/rules/pyupgrade/fixes.rs +++ b/src/rules/pyupgrade/fixes.rs @@ -2,10 +2,9 @@ use libcst_native::{ Codegen, CodegenState, Expression, ParenthesizableWhitespace, SmallStatement, Statement, }; use rustpython_ast::{Expr, Keyword, Location}; -use rustpython_parser::lexer; -use rustpython_parser::lexer::Tok; use crate::ast::types::Range; +use crate::autofix::helpers::remove_argument; use crate::fix::Fix; use crate::source_code::{Locator, Stylist}; @@ -14,93 +13,14 @@ pub fn remove_class_def_base( locator: &Locator, stmt_at: Location, expr_at: Location, + expr_end: Location, bases: &[Expr], keywords: &[Keyword], ) -> Option { - let contents = locator.slice_source_code_at(stmt_at); - - // Case 1: `object` is the only base. - if bases.len() == 1 && keywords.is_empty() { - let mut fix_start = None; - let mut fix_end = None; - let mut count: usize = 0; - for (start, tok, end) in lexer::make_tokenizer_located(contents, stmt_at).flatten() { - if matches!(tok, Tok::Lpar) { - if count == 0 { - fix_start = Some(start); - } - count += 1; - } - - if matches!(tok, Tok::Rpar) { - count -= 1; - if count == 0 { - fix_end = Some(end); - break; - } - } - } - - return match (fix_start, fix_end) { - (Some(start), Some(end)) => Some(Fix::deletion(start, end)), - _ => None, - }; - } - - if bases - .iter() - .map(|node| node.location) - .chain(keywords.iter().map(|node| node.location)) - .any(|location| location > expr_at) - { - // Case 2: `object` is _not_ the last node. - let mut fix_start: Option = None; - let mut fix_end: Option = None; - let mut seen_comma = false; - for (start, tok, end) in lexer::make_tokenizer_located(contents, stmt_at).flatten() { - if seen_comma { - if matches!(tok, Tok::NonLogicalNewline) { - // Also delete any non-logical newlines after the comma. - continue; - } - if matches!(tok, Tok::Newline) { - fix_end = Some(end); - } else { - fix_end = Some(start); - } - break; - } - if start == expr_at { - fix_start = Some(start); - } - if fix_start.is_some() && matches!(tok, Tok::Comma) { - seen_comma = true; - } - } - - match (fix_start, fix_end) { - (Some(start), Some(end)) => Some(Fix::deletion(start, end)), - _ => None, - } + if let Ok(fix) = remove_argument(locator, stmt_at, expr_at, expr_end, bases, keywords, true) { + Some(fix) } else { - // Case 3: `object` is the last node, so we have to find the last token that - // isn't a comma. - let mut fix_start: Option = None; - let mut fix_end: Option = None; - for (start, tok, end) in lexer::make_tokenizer_located(contents, stmt_at).flatten() { - if start == expr_at { - fix_end = Some(end); - break; - } - if matches!(tok, Tok::Comma) { - fix_start = Some(start); - } - } - - match (fix_start, fix_end) { - (Some(start), Some(end)) => Some(Fix::deletion(start, end)), - _ => None, - } + None } } diff --git a/src/rules/pyupgrade/rules/useless_object_inheritance.rs b/src/rules/pyupgrade/rules/useless_object_inheritance.rs index f6805356cc..ed418a2108 100644 --- a/src/rules/pyupgrade/rules/useless_object_inheritance.rs +++ b/src/rules/pyupgrade/rules/useless_object_inheritance.rs @@ -53,6 +53,7 @@ pub fn useless_object_inheritance( checker.locator, stmt.location, diagnostic.location, + diagnostic.end_location, bases, keywords, ) { diff --git a/src/violations.rs b/src/violations.rs index 906a5b53fc..7f847b6e57 100644 --- a/src/violations.rs +++ b/src/violations.rs @@ -4684,76 +4684,6 @@ impl Violation for DotFormatInException { // flake8-pytest-style -define_violation!( - pub struct IncorrectFixtureParenthesesStyle { - pub expected_parens: String, - pub actual_parens: String, - } -); -impl AlwaysAutofixableViolation for IncorrectFixtureParenthesesStyle { - #[derive_message_formats] - fn message(&self) -> String { - let IncorrectFixtureParenthesesStyle { - expected_parens, - actual_parens, - } = self; - format!("Use `@pytest.fixture{expected_parens}` over `@pytest.fixture{actual_parens}`") - } - - fn autofix_title(&self) -> String { - "Add/remove parentheses".to_string() - } -} - -define_violation!( - pub struct FixturePositionalArgs { - pub function: String, - } -); -impl Violation for FixturePositionalArgs { - #[derive_message_formats] - fn message(&self) -> String { - let FixturePositionalArgs { function } = self; - format!("Configuration for fixture `{function}` specified via positional args, use kwargs") - } -} - -define_violation!( - pub struct ExtraneousScopeFunction; -); -impl Violation for ExtraneousScopeFunction { - #[derive_message_formats] - fn message(&self) -> String { - format!("`scope='function'` is implied in `@pytest.fixture()`") - } -} - -define_violation!( - pub struct MissingFixtureNameUnderscore { - pub function: String, - } -); -impl Violation for MissingFixtureNameUnderscore { - #[derive_message_formats] - fn message(&self) -> String { - let MissingFixtureNameUnderscore { function } = self; - format!("Fixture `{function}` does not return anything, add leading underscore") - } -} - -define_violation!( - pub struct IncorrectFixtureNameUnderscore { - pub function: String, - } -); -impl Violation for IncorrectFixtureNameUnderscore { - #[derive_message_formats] - fn message(&self) -> String { - let IncorrectFixtureNameUnderscore { function } = self; - format!("Fixture `{function}` returns a value, remove leading underscore") - } -} - define_violation!( pub struct ParametrizeNamesWrongType { pub expected: ParametrizeNameType, @@ -4905,59 +4835,6 @@ impl Violation for CompositeAssertion { } } -define_violation!( - pub struct FixtureParamWithoutValue { - pub name: String, - } -); -impl Violation for FixtureParamWithoutValue { - #[derive_message_formats] - fn message(&self) -> String { - let FixtureParamWithoutValue { name } = self; - format!( - "Fixture `{name}` without value is injected as parameter, use \ - `@pytest.mark.usefixtures` instead" - ) - } -} - -define_violation!( - pub struct DeprecatedYieldFixture; -); -impl Violation for DeprecatedYieldFixture { - #[derive_message_formats] - fn message(&self) -> String { - format!("`@pytest.yield_fixture` is deprecated, use `@pytest.fixture`") - } -} - -define_violation!( - pub struct FixtureFinalizerCallback; -); -impl Violation for FixtureFinalizerCallback { - #[derive_message_formats] - fn message(&self) -> String { - format!("Use `yield` instead of `request.addfinalizer`") - } -} - -define_violation!( - pub struct UselessYieldFixture { - pub name: String, - } -); -impl AlwaysAutofixableViolation for UselessYieldFixture { - #[derive_message_formats] - fn message(&self) -> String { - let UselessYieldFixture { name } = self; - format!("No teardown in fixture `{name}`, use `return` instead of `yield`") - } - - fn autofix_title(&self) -> String { - "Replace `yield` with `return`".to_string() - } -} - define_violation!( pub struct IncorrectMarkParenthesesStyle { pub mark_name: String, @@ -4984,34 +4861,6 @@ impl AlwaysAutofixableViolation for IncorrectMarkParenthesesStyle { } } -define_violation!( - pub struct UnnecessaryAsyncioMarkOnFixture; -); -impl AlwaysAutofixableViolation for UnnecessaryAsyncioMarkOnFixture { - #[derive_message_formats] - fn message(&self) -> String { - format!("`pytest.mark.asyncio` is unnecessary for fixtures") - } - - fn autofix_title(&self) -> String { - "Remove `pytest.mark.asyncio`".to_string() - } -} - -define_violation!( - pub struct ErroneousUseFixturesOnFixture; -); -impl AlwaysAutofixableViolation for ErroneousUseFixturesOnFixture { - #[derive_message_formats] - fn message(&self) -> String { - format!("`pytest.mark.usefixtures` has no effect on fixtures") - } - - fn autofix_title(&self) -> String { - "Remove `pytest.mark.usefixtures`".to_string() - } -} - define_violation!( pub struct UseFixturesWithoutParameters; );