diff --git a/README.md b/README.md index 3ce57f4aae..297a24d7e6 100644 --- a/README.md +++ b/README.md @@ -916,8 +916,8 @@ For more, see [flake8-pytest-style](https://pypi.org/project/flake8-pytest-style | PT001 | IncorrectFixtureParenthesesStyle | Use `@pytest.fixture()` over `@pytest.fixture` | 🛠 | | PT002 | FixturePositionalArgs | Configuration for fixture `...` specified via positional args, use kwargs | | | PT003 | ExtraneousScopeFunction | `scope='function'` is implied in `@pytest.fixture()` | | -| PT004 | MissingFixtureNameUnderscore | Fixture `...` does not return anything, add leading underscore | | -| PT005 | IncorrectFixtureNameUnderscore | Fixture `...` returns a value, remove leading underscore | | +| PT004 | MissingFixtureNameUnderscore | Fixture `...` does not return anything, add leading underscore | 🛠 | +| PT005 | IncorrectFixtureNameUnderscore | Fixture `...` returns a value, remove leading underscore | 🛠 | | PT006 | ParametrizeNamesWrongType | Wrong name(s) type in `@pytest.mark.parametrize`, expected `tuple` | 🛠 | | PT007 | ParametrizeValuesWrongType | Wrong values type in `@pytest.mark.parametrize` expected `list` of `tuple` | | | PT008 | PatchWithLambda | Use `return_value=` instead of patching with `lambda` | | @@ -935,8 +935,8 @@ For more, see [flake8-pytest-style](https://pypi.org/project/flake8-pytest-style | PT021 | FixtureFinalizerCallback | Use `yield` instead of `request.addfinalizer` | | | PT022 | UselessYieldFixture | No teardown in fixture `...`, use `return` instead of `yield` | 🛠 | | PT023 | IncorrectMarkParenthesesStyle | Use `@pytest.mark....` over `@pytest.mark....()` | 🛠 | -| PT024 | UnnecessaryAsyncioMarkOnFixture | `pytest.mark.asyncio` is unnecessary for fixtures | | -| PT025 | ErroneousUseFixturesOnFixture | `pytest.mark.usefixtures` has no effect on fixtures | | +| PT024 | UnnecessaryAsyncioMarkOnFixture | `pytest.mark.asyncio` is unnecessary for fixtures | 🛠 | +| PT025 | ErroneousUseFixturesOnFixture | `pytest.mark.usefixtures` has no effect on fixtures | 🛠 | | PT026 | UseFixturesWithoutParameters | Useless `pytest.mark.usefixtures` without parameters | 🛠 | ### flake8-quotes (Q) diff --git a/src/flake8_pytest_style/plugins/fixture.rs b/src/flake8_pytest_style/plugins/fixture.rs index 6edc9b97c5..e76af0ea48 100644 --- a/src/flake8_pytest_style/plugins/fixture.rs +++ b/src/flake8_pytest_style/plugins/fixture.rs @@ -4,7 +4,7 @@ use super::helpers::{ get_mark_decorators, get_mark_name, is_abstractmethod_decorator, is_pytest_fixture, is_pytest_yield_fixture, keyword_is_literal, }; -use crate::ast::helpers::{collect_arg_names, collect_call_paths}; +use crate::ast::helpers::{collect_arg_names, collect_call_paths, identifier_range}; use crate::ast::types::Range; use crate::ast::visitor; use crate::ast::visitor::Visitor; @@ -156,19 +156,33 @@ fn check_fixture_returns(checker: &mut Checker, func: &Stmt, func_name: &str, bo && visitor.has_return_with_value && func_name.starts_with('_') { - checker.diagnostics.push(Diagnostic::new( + let mut diagnostic = Diagnostic::new( violations::IncorrectFixtureNameUnderscore(func_name.to_string()), Range::from_located(func), - )); + ); + if checker.patch(diagnostic.kind.code()) { + let func_name_range = identifier_range(func, checker.locator); + let num_underscores = func_name.len() - func_name.trim_start_matches('_').len(); + diagnostic.amend(Fix::deletion( + func_name_range.location, + func_name_range.location.with_col_offset(num_underscores), + )); + } + checker.diagnostics.push(diagnostic); } else if checker.settings.enabled.contains(&RuleCode::PT004) && !visitor.has_return_with_value && !visitor.has_yield_from && !func_name.starts_with('_') { - checker.diagnostics.push(Diagnostic::new( + let mut diagnostic = Diagnostic::new( violations::MissingFixtureNameUnderscore(func_name.to_string()), Range::from_located(func), - )); + ); + if checker.patch(diagnostic.kind.code()) { + let func_name_range = identifier_range(func, checker.locator); + diagnostic.amend(Fix::insertion("_".to_string(), func_name_range.location)); + } + checker.diagnostics.push(diagnostic); } if checker.settings.enabled.contains(&RuleCode::PT022) { @@ -248,19 +262,31 @@ fn check_fixture_marks(checker: &mut Checker, decorators: &[Expr]) { if checker.settings.enabled.contains(&RuleCode::PT024) { if name == "asyncio" { - checker.diagnostics.push(Diagnostic::new( + let mut diagnostic = Diagnostic::new( violations::UnnecessaryAsyncioMarkOnFixture, Range::from_located(mark), - )); + ); + if checker.patch(diagnostic.kind.code()) { + let start = Location::new(mark.location.row(), 0); + let end = Location::new(mark.end_location.unwrap().row() + 1, 0); + diagnostic.amend(Fix::deletion(start, end)); + } + checker.diagnostics.push(diagnostic); } } if checker.settings.enabled.contains(&RuleCode::PT025) { if name == "usefixtures" { - checker.diagnostics.push(Diagnostic::new( + let mut diagnostic = Diagnostic::new( violations::ErroneousUseFixturesOnFixture, Range::from_located(mark), - )); + ); + if checker.patch(diagnostic.kind.code()) { + let start = Location::new(mark.location.row(), 0); + let end = Location::new(mark.end_location.unwrap().row() + 1, 0); + diagnostic.amend(Fix::deletion(start, end)); + } + checker.diagnostics.push(diagnostic); } } } diff --git a/src/flake8_pytest_style/snapshots/ruff__flake8_pytest_style__tests__PT004.snap b/src/flake8_pytest_style/snapshots/ruff__flake8_pytest_style__tests__PT004.snap index 98b8f9936b..4763f75e1b 100644 --- a/src/flake8_pytest_style/snapshots/ruff__flake8_pytest_style__tests__PT004.snap +++ b/src/flake8_pytest_style/snapshots/ruff__flake8_pytest_style__tests__PT004.snap @@ -1,6 +1,6 @@ --- source: src/flake8_pytest_style/mod.rs -expression: checks +expression: diagnostics --- - kind: MissingFixtureNameUnderscore: patch_something @@ -10,7 +10,14 @@ expression: checks end_location: row: 52 column: 30 - fix: ~ + fix: + content: _ + location: + row: 51 + column: 4 + end_location: + row: 51 + column: 4 parent: ~ - kind: MissingFixtureNameUnderscore: activate_context @@ -20,6 +27,13 @@ expression: checks end_location: row: 58 column: 13 - fix: ~ + fix: + content: _ + location: + row: 56 + column: 4 + end_location: + row: 56 + column: 4 parent: ~ diff --git a/src/flake8_pytest_style/snapshots/ruff__flake8_pytest_style__tests__PT005.snap b/src/flake8_pytest_style/snapshots/ruff__flake8_pytest_style__tests__PT005.snap index c2607ba1ed..3b8961e46e 100644 --- a/src/flake8_pytest_style/snapshots/ruff__flake8_pytest_style__tests__PT005.snap +++ b/src/flake8_pytest_style/snapshots/ruff__flake8_pytest_style__tests__PT005.snap @@ -1,6 +1,6 @@ --- source: src/flake8_pytest_style/mod.rs -expression: checks +expression: diagnostics --- - kind: IncorrectFixtureNameUnderscore: _my_fixture @@ -10,7 +10,14 @@ expression: checks end_location: row: 42 column: 12 - fix: ~ + fix: + content: "" + location: + row: 41 + column: 4 + end_location: + row: 41 + column: 5 parent: ~ - kind: IncorrectFixtureNameUnderscore: _activate_context @@ -20,7 +27,14 @@ expression: checks end_location: row: 48 column: 21 - fix: ~ + fix: + content: "" + location: + row: 46 + column: 4 + end_location: + row: 46 + column: 5 parent: ~ - kind: IncorrectFixtureNameUnderscore: _activate_context @@ -30,6 +44,13 @@ expression: checks end_location: row: 57 column: 34 - fix: ~ + fix: + content: "" + location: + row: 52 + column: 4 + end_location: + row: 52 + column: 5 parent: ~ diff --git a/src/flake8_pytest_style/snapshots/ruff__flake8_pytest_style__tests__PT024.snap b/src/flake8_pytest_style/snapshots/ruff__flake8_pytest_style__tests__PT024.snap index 376e8ab14e..465a8e3176 100644 --- a/src/flake8_pytest_style/snapshots/ruff__flake8_pytest_style__tests__PT024.snap +++ b/src/flake8_pytest_style/snapshots/ruff__flake8_pytest_style__tests__PT024.snap @@ -1,6 +1,6 @@ --- source: src/flake8_pytest_style/mod.rs -expression: checks +expression: diagnostics --- - kind: UnnecessaryAsyncioMarkOnFixture: ~ @@ -10,7 +10,14 @@ expression: checks end_location: row: 14 column: 22 - fix: ~ + fix: + content: "" + location: + row: 14 + column: 0 + end_location: + row: 15 + column: 0 parent: ~ - kind: UnnecessaryAsyncioMarkOnFixture: ~ @@ -20,7 +27,14 @@ expression: checks end_location: row: 20 column: 20 - fix: ~ + fix: + content: "" + location: + row: 20 + column: 0 + end_location: + row: 21 + column: 0 parent: ~ - kind: UnnecessaryAsyncioMarkOnFixture: ~ @@ -30,7 +44,14 @@ expression: checks end_location: row: 27 column: 22 - fix: ~ + fix: + content: "" + location: + row: 27 + column: 0 + end_location: + row: 28 + column: 0 parent: ~ - kind: UnnecessaryAsyncioMarkOnFixture: ~ @@ -40,6 +61,13 @@ expression: checks end_location: row: 33 column: 20 - fix: ~ + fix: + content: "" + location: + row: 33 + column: 0 + end_location: + row: 34 + column: 0 parent: ~ diff --git a/src/flake8_pytest_style/snapshots/ruff__flake8_pytest_style__tests__PT025.snap b/src/flake8_pytest_style/snapshots/ruff__flake8_pytest_style__tests__PT025.snap index daf4eed5f5..c947b0f415 100644 --- a/src/flake8_pytest_style/snapshots/ruff__flake8_pytest_style__tests__PT025.snap +++ b/src/flake8_pytest_style/snapshots/ruff__flake8_pytest_style__tests__PT025.snap @@ -1,6 +1,6 @@ --- source: src/flake8_pytest_style/mod.rs -expression: checks +expression: diagnostics --- - kind: ErroneousUseFixturesOnFixture: ~ @@ -10,7 +10,14 @@ expression: checks end_location: row: 9 column: 29 - fix: ~ + fix: + content: "" + location: + row: 9 + column: 0 + end_location: + row: 10 + column: 0 parent: ~ - kind: ErroneousUseFixturesOnFixture: ~ @@ -20,6 +27,13 @@ expression: checks end_location: row: 16 column: 29 - fix: ~ + fix: + content: "" + location: + row: 16 + column: 0 + end_location: + row: 17 + column: 0 parent: ~ diff --git a/src/violations.rs b/src/violations.rs index 9f159d6193..92b2e8fca0 100644 --- a/src/violations.rs +++ b/src/violations.rs @@ -5358,12 +5358,16 @@ impl Violation for ExtraneousScopeFunction { define_violation!( pub struct MissingFixtureNameUnderscore(pub String); ); -impl Violation for MissingFixtureNameUnderscore { +impl AlwaysAutofixableViolation for MissingFixtureNameUnderscore { fn message(&self) -> String { let MissingFixtureNameUnderscore(function) = self; format!("Fixture `{function}` does not return anything, add leading underscore") } + fn autofix_title(&self) -> String { + "Add leading underscore".to_string() + } + fn placeholder() -> Self { MissingFixtureNameUnderscore("...".to_string()) } @@ -5372,12 +5376,16 @@ impl Violation for MissingFixtureNameUnderscore { define_violation!( pub struct IncorrectFixtureNameUnderscore(pub String); ); -impl Violation for IncorrectFixtureNameUnderscore { +impl AlwaysAutofixableViolation for IncorrectFixtureNameUnderscore { fn message(&self) -> String { let IncorrectFixtureNameUnderscore(function) = self; format!("Fixture `{function}` returns a value, remove leading underscore") } + fn autofix_title(&self) -> String { + "Remove leading underscore".to_string() + } + fn placeholder() -> Self { IncorrectFixtureNameUnderscore("...".to_string()) } @@ -5644,11 +5652,15 @@ impl AlwaysAutofixableViolation for IncorrectMarkParenthesesStyle { define_violation!( pub struct UnnecessaryAsyncioMarkOnFixture; ); -impl Violation for UnnecessaryAsyncioMarkOnFixture { +impl AlwaysAutofixableViolation for UnnecessaryAsyncioMarkOnFixture { fn message(&self) -> String { "`pytest.mark.asyncio` is unnecessary for fixtures".to_string() } + fn autofix_title(&self) -> String { + "Remove `pytest.mark.asyncio`".to_string() + } + fn placeholder() -> Self { UnnecessaryAsyncioMarkOnFixture } @@ -5657,11 +5669,15 @@ impl Violation for UnnecessaryAsyncioMarkOnFixture { define_violation!( pub struct ErroneousUseFixturesOnFixture; ); -impl Violation for ErroneousUseFixturesOnFixture { +impl AlwaysAutofixableViolation for ErroneousUseFixturesOnFixture { fn message(&self) -> String { "`pytest.mark.usefixtures` has no effect on fixtures".to_string() } + fn autofix_title(&self) -> String { + "Remove `pytest.mark.usefixtures`".to_string() + } + fn placeholder() -> Self { ErroneousUseFixturesOnFixture }