diff --git a/crates/ruff_linter/resources/test/fixtures/flake8_type_checking/quote.py b/crates/ruff_linter/resources/test/fixtures/flake8_type_checking/quote.py index 547cd30117672..d344b0cfac781 100644 --- a/crates/ruff_linter/resources/test/fixtures/flake8_type_checking/quote.py +++ b/crates/ruff_linter/resources/test/fixtures/flake8_type_checking/quote.py @@ -90,3 +90,14 @@ def f(): def func(self) -> DataFrame | list[Series]: pass + + +def f(): + from typing import Annotated + + from fastapi import Depends + + from .foo import get_foo + + def test_annotated_non_typing_reference(user: Annotated[str, Depends(get_foo)]): + pass diff --git a/crates/ruff_linter/resources/test/fixtures/flake8_type_checking/quote2.py b/crates/ruff_linter/resources/test/fixtures/flake8_type_checking/quote2.py index 4433416da5087..7e2b9fd0c6de4 100644 --- a/crates/ruff_linter/resources/test/fixtures/flake8_type_checking/quote2.py +++ b/crates/ruff_linter/resources/test/fixtures/flake8_type_checking/quote2.py @@ -85,3 +85,14 @@ def f(): def test_optional_literal_no_removal(arg: Optional[Literal["red", "blue"]]): pass + + +def f(): + from typing import Annotated + + from fastapi import Depends + + from .foo import get_foo + + def test_annotated_non_typing_reference(user: Annotated[str, Depends(get_foo)]): + pass diff --git a/crates/ruff_linter/resources/test/fixtures/flake8_type_checking/quote3.py b/crates/ruff_linter/resources/test/fixtures/flake8_type_checking/quote3.py index b360230b4e6e1..f2c624886056e 100644 --- a/crates/ruff_linter/resources/test/fixtures/flake8_type_checking/quote3.py +++ b/crates/ruff_linter/resources/test/fixtures/flake8_type_checking/quote3.py @@ -42,3 +42,14 @@ def f(): def test_attribute_typing_literal(arg: models.AbstractBaseUser[Literal["admin"]]): pass + +def f(): + from typing import Annotated + + from fastapi import Depends + + from .foo import get_foo + + def test_annotated_non_typing_reference(user: Annotated[str, Depends(get_foo)]): + pass + diff --git a/crates/ruff_linter/src/rules/flake8_type_checking/helpers.rs b/crates/ruff_linter/src/rules/flake8_type_checking/helpers.rs index cd86affbeff30..04d627a8e9dff 100644 --- a/crates/ruff_linter/src/rules/flake8_type_checking/helpers.rs +++ b/crates/ruff_linter/src/rules/flake8_type_checking/helpers.rs @@ -19,10 +19,13 @@ use crate::rules::flake8_type_checking::settings::Settings; /// context (with quoting enabled). pub(crate) fn is_typing_reference(reference: &ResolvedReference, settings: &Settings) -> bool { reference.in_type_checking_block() - || reference.in_typing_only_annotation() - || reference.in_complex_string_type_definition() - || reference.in_simple_string_type_definition() - || (settings.quote_annotations && reference.in_runtime_evaluated_annotation()) + // if we're not in a type checking block, we necessarily need to be within a + // type definition to be considered a typing reference + || (reference.in_type_definition() + && (reference.in_typing_only_annotation() + || reference.in_complex_string_type_definition() + || reference.in_simple_string_type_definition() + || (settings.quote_annotations && reference.in_runtime_evaluated_annotation()))) } /// Returns `true` if the [`Binding`] represents a runtime-required import. diff --git a/crates/ruff_linter/src/rules/flake8_type_checking/snapshots/ruff_linter__rules__flake8_type_checking__tests__quote_typing-only-third-party-import_quote.py.snap b/crates/ruff_linter/src/rules/flake8_type_checking/snapshots/ruff_linter__rules__flake8_type_checking__tests__quote_typing-only-third-party-import_quote.py.snap index cc72734ecf5e9..ff6f2e6437542 100644 --- a/crates/ruff_linter/src/rules/flake8_type_checking/snapshots/ruff_linter__rules__flake8_type_checking__tests__quote_typing-only-third-party-import_quote.py.snap +++ b/crates/ruff_linter/src/rules/flake8_type_checking/snapshots/ruff_linter__rules__flake8_type_checking__tests__quote_typing-only-third-party-import_quote.py.snap @@ -379,6 +379,8 @@ quote.py:89:24: TCH002 [*] Move third-party import `pandas.DataFrame` into a typ 91 |- def func(self) -> DataFrame | list[Series]: 94 |+ def func(self) -> "DataFrame | list[Series]": 92 95 | pass +93 96 | +94 97 | quote.py:89:35: TCH002 [*] Move third-party import `pandas.Series` into a type-checking block | @@ -407,3 +409,5 @@ quote.py:89:35: TCH002 [*] Move third-party import `pandas.Series` into a type-c 91 |- def func(self) -> DataFrame | list[Series]: 94 |+ def func(self) -> "DataFrame | list[Series]": 92 95 | pass +93 96 | +94 97 | diff --git a/crates/ruff_linter/src/rules/flake8_type_checking/snapshots/ruff_linter__rules__flake8_type_checking__tests__quote_typing-only-third-party-import_quote3.py.snap b/crates/ruff_linter/src/rules/flake8_type_checking/snapshots/ruff_linter__rules__flake8_type_checking__tests__quote_typing-only-third-party-import_quote3.py.snap index 579cc74973b14..01853a363783d 100644 --- a/crates/ruff_linter/src/rules/flake8_type_checking/snapshots/ruff_linter__rules__flake8_type_checking__tests__quote_typing-only-third-party-import_quote3.py.snap +++ b/crates/ruff_linter/src/rules/flake8_type_checking/snapshots/ruff_linter__rules__flake8_type_checking__tests__quote_typing-only-third-party-import_quote3.py.snap @@ -149,4 +149,5 @@ quote3.py:40:37: TCH002 [*] Move third-party import `django.contrib.auth.models` 42 |- def test_attribute_typing_literal(arg: models.AbstractBaseUser[Literal["admin"]]): 45 |+ def test_attribute_typing_literal(arg: 'models.AbstractBaseUser[Literal["admin"]]'): 43 46 | pass -44 47 | +44 47 | +45 48 | diff --git a/crates/ruff_python_semantic/src/reference.rs b/crates/ruff_python_semantic/src/reference.rs index 1b9b7364b0b27..de6fab30216f0 100644 --- a/crates/ruff_python_semantic/src/reference.rs +++ b/crates/ruff_python_semantic/src/reference.rs @@ -87,6 +87,11 @@ impl ResolvedReference { .intersects(SemanticModelFlags::DEFERRED_TYPE_DEFINITION) } + /// Return `true` if the context is in any kind of type definition. + pub const fn in_type_definition(&self) -> bool { + self.flags.intersects(SemanticModelFlags::TYPE_DEFINITION) + } + /// Return `true` if the context is in a type-checking block. pub const fn in_type_checking_block(&self) -> bool { self.flags