diff --git a/crates/ruff_linter/resources/test/fixtures/flake8_pytest_style/PT001.py b/crates/ruff_linter/resources/test/fixtures/flake8_pytest_style/PT001.py index 49d53fcb8e..1160090d20 100644 --- a/crates/ruff_linter/resources/test/fixtures/flake8_pytest_style/PT001.py +++ b/crates/ruff_linter/resources/test/fixtures/flake8_pytest_style/PT001.py @@ -76,3 +76,10 @@ def aliased_parentheses_with_params(): ) def aliased_parentheses_no_params_multiline(): return 42 + +# https://github.com/astral-sh/ruff/issues/18770 +@pytest.fixture( + # TODO: use module scope + # scope="module" +) +def my_fixture(): ... diff --git a/crates/ruff_linter/resources/test/fixtures/flake8_pytest_style/PT023.py b/crates/ruff_linter/resources/test/fixtures/flake8_pytest_style/PT023.py index b212ded929..682e78d80a 100644 --- a/crates/ruff_linter/resources/test/fixtures/flake8_pytest_style/PT023.py +++ b/crates/ruff_linter/resources/test/fixtures/flake8_pytest_style/PT023.py @@ -77,3 +77,14 @@ class TestClass: @pytest.mark.foo() def test_something(): pass + +# https://github.com/astral-sh/ruff/issues/18770 +@pytest.mark.parametrize( + # TODO: fix later + # ("param1", "param2"), + # ( + # (1, 2), + # (3, 4), + # ), +) +def test_bar(param1, param2): ... diff --git a/crates/ruff_linter/src/rules/flake8_pytest_style/rules/fixture.rs b/crates/ruff_linter/src/rules/flake8_pytest_style/rules/fixture.rs index 9f7138336c..6c5f84706d 100644 --- a/crates/ruff_linter/src/rules/flake8_pytest_style/rules/fixture.rs +++ b/crates/ruff_linter/src/rules/flake8_pytest_style/rules/fixture.rs @@ -1,3 +1,4 @@ +use ruff_diagnostics::Applicability; use ruff_macros::{ViolationMetadata, derive_message_formats}; use ruff_python_ast::Decorator; use ruff_python_ast::helpers::map_callable; @@ -57,6 +58,22 @@ use super::helpers::{ /// def my_fixture(): ... /// ``` /// +/// ## Fix safety +/// This rule's fix is marked as unsafe if there's comments in the +/// `pytest.fixture` decorator. +/// +/// For example, the fix would be marked as unsafe in the following case: +/// ```python +/// import pytest +/// +/// +/// @pytest.fixture( +/// # comment +/// # scope = "module" +/// ) +/// def my_fixture(): ... +/// ``` +/// /// ## Options /// - `lint.flake8-pytest-style.fixture-parentheses` /// @@ -701,7 +718,17 @@ fn check_fixture_decorator(checker: &Checker, func_name: &str, decorator: &Decor && arguments.args.is_empty() && arguments.keywords.is_empty() { - let fix = Fix::safe_edit(Edit::deletion(func.end(), decorator.end())); + let fix = Fix::applicable_edit( + Edit::deletion(func.end(), decorator.end()), + if checker + .comment_ranges() + .intersects(TextRange::new(func.end(), decorator.end())) + { + Applicability::Unsafe + } else { + Applicability::Safe + }, + ); pytest_fixture_parentheses( checker, decorator, diff --git a/crates/ruff_linter/src/rules/flake8_pytest_style/rules/marks.rs b/crates/ruff_linter/src/rules/flake8_pytest_style/rules/marks.rs index 36bbd312a6..75e64da73e 100644 --- a/crates/ruff_linter/src/rules/flake8_pytest_style/rules/marks.rs +++ b/crates/ruff_linter/src/rules/flake8_pytest_style/rules/marks.rs @@ -1,7 +1,8 @@ +use ruff_diagnostics::Applicability; use ruff_python_ast::{self as ast, Arguments, Decorator, Expr}; use ruff_macros::{ViolationMetadata, derive_message_formats}; -use ruff_text_size::Ranged; +use ruff_text_size::{Ranged, TextRange}; use crate::checkers::ast::Checker; use crate::registry::Rule; @@ -44,6 +45,19 @@ use super::helpers::{Parentheses, get_mark_decorators}; /// def test_something(): ... /// ``` /// +/// ## Fix safety +/// This rule's fix is marked as unsafe if there's comments in the +/// `pytest.mark.` decorator. +/// ```python +/// import pytest +/// +/// +/// @pytest.mark.foo( +/// # comment +/// ) +/// def test_something(): ... +/// ``` +/// /// ## Options /// - `lint.flake8-pytest-style.mark-parentheses` /// @@ -155,7 +169,17 @@ fn check_mark_parentheses(checker: &Checker, decorator: &Decorator, marker: &str && args.is_empty() && keywords.is_empty() { - let fix = Fix::safe_edit(Edit::deletion(func.end(), decorator.end())); + let fix = Fix::applicable_edit( + Edit::deletion(func.end(), decorator.end()), + if checker + .comment_ranges() + .intersects(TextRange::new(func.end(), decorator.end())) + { + Applicability::Unsafe + } else { + Applicability::Safe + }, + ); pytest_mark_parentheses( checker, decorator, diff --git a/crates/ruff_linter/src/rules/flake8_pytest_style/snapshots/ruff_linter__rules__flake8_pytest_style__tests__PT001_default.snap b/crates/ruff_linter/src/rules/flake8_pytest_style/snapshots/ruff_linter__rules__flake8_pytest_style__tests__PT001_default.snap index 8bcf1ce60d..5ad98a5079 100644 --- a/crates/ruff_linter/src/rules/flake8_pytest_style/snapshots/ruff_linter__rules__flake8_pytest_style__tests__PT001_default.snap +++ b/crates/ruff_linter/src/rules/flake8_pytest_style/snapshots/ruff_linter__rules__flake8_pytest_style__tests__PT001_default.snap @@ -125,3 +125,27 @@ PT001.py:74:1: PT001 [*] Use `@pytest.fixture` over `@pytest.fixture()` 74 |+@aliased 77 75 | def aliased_parentheses_no_params_multiline(): 78 76 | return 42 +79 77 | + +PT001.py:81:1: PT001 [*] Use `@pytest.fixture` over `@pytest.fixture()` + | +80 | # https://github.com/astral-sh/ruff/issues/18770 +81 | / @pytest.fixture( +82 | | # TODO: use module scope +83 | | # scope="module" +84 | | ) + | |_^ PT001 +85 | def my_fixture(): ... + | + = help: Remove parentheses + +ℹ Unsafe fix +78 78 | return 42 +79 79 | +80 80 | # https://github.com/astral-sh/ruff/issues/18770 +81 |-@pytest.fixture( +82 |- # TODO: use module scope +83 |- # scope="module" +84 |-) + 81 |+@pytest.fixture +85 82 | def my_fixture(): ... diff --git a/crates/ruff_linter/src/rules/flake8_pytest_style/snapshots/ruff_linter__rules__flake8_pytest_style__tests__PT023_default.snap b/crates/ruff_linter/src/rules/flake8_pytest_style/snapshots/ruff_linter__rules__flake8_pytest_style__tests__PT023_default.snap index 20466f29fe..df12e9c4bc 100644 --- a/crates/ruff_linter/src/rules/flake8_pytest_style/snapshots/ruff_linter__rules__flake8_pytest_style__tests__PT023_default.snap +++ b/crates/ruff_linter/src/rules/flake8_pytest_style/snapshots/ruff_linter__rules__flake8_pytest_style__tests__PT023_default.snap @@ -98,3 +98,35 @@ PT023.py:77:9: PT023 [*] Use `@pytest.mark.foo` over `@pytest.mark.foo()` 77 |+ @pytest.mark.foo 78 78 | def test_something(): 79 79 | pass +80 80 | + +PT023.py:82:1: PT023 [*] Use `@pytest.mark.parametrize` over `@pytest.mark.parametrize()` + | +81 | # https://github.com/astral-sh/ruff/issues/18770 +82 | / @pytest.mark.parametrize( +83 | | # TODO: fix later +84 | | # ("param1", "param2"), +85 | | # ( +86 | | # (1, 2), +87 | | # (3, 4), +88 | | # ), +89 | | ) + | |_^ PT023 +90 | def test_bar(param1, param2): ... + | + = help: Remove parentheses + +ℹ Unsafe fix +79 79 | pass +80 80 | +81 81 | # https://github.com/astral-sh/ruff/issues/18770 +82 |-@pytest.mark.parametrize( +83 |- # TODO: fix later +84 |- # ("param1", "param2"), +85 |- # ( +86 |- # (1, 2), +87 |- # (3, 4), +88 |- # ), +89 |-) + 82 |+@pytest.mark.parametrize +90 83 | def test_bar(param1, param2): ...