From 229024097f0dc78402e9badfc5d163b0c6866923 Mon Sep 17 00:00:00 2001 From: Alex Waygood Date: Mon, 9 Dec 2024 14:49:15 +0000 Subject: [PATCH] cleanups --- .../flake8_pyi/rules/non_self_return_type.rs | 54 +++++-------- ...__flake8_pyi__tests__PYI034_PYI034.py.snap | 49 ++++++------ ..._flake8_pyi__tests__PYI034_PYI034.pyi.snap | 77 +++++++++---------- .../ruff_python_semantic/src/analyze/class.rs | 19 ++--- 4 files changed, 88 insertions(+), 111 deletions(-) diff --git a/crates/ruff_linter/src/rules/flake8_pyi/rules/non_self_return_type.rs b/crates/ruff_linter/src/rules/flake8_pyi/rules/non_self_return_type.rs index 4c4a25ec04ee87..2e964361dff75e 100644 --- a/crates/ruff_linter/src/rules/flake8_pyi/rules/non_self_return_type.rs +++ b/crates/ruff_linter/src/rules/flake8_pyi/rules/non_self_return_type.rs @@ -4,6 +4,7 @@ use ruff_python_ast::{ use crate::checkers::ast::Checker; use crate::importer::ImportRequest; +use crate::settings::types::PythonVersion; use ruff_diagnostics::{Applicability, Diagnostic, Edit, Fix, FixAvailability, Violation}; use ruff_macros::{derive_message_formats, ViolationMetadata}; use ruff_python_ast::helpers::map_subscript; @@ -74,6 +75,10 @@ use ruff_text_size::Ranged; /// async def __aenter__(self) -> Self: ... /// def __iadd__(self, other: Foo) -> Self: ... /// ``` +/// +/// ## Fix safety +/// This rule's fix is marked as unsafe as it changes the meaning of your type annotations. +/// /// ## References /// - [Python documentation: `typing.Self`](https://docs.python.org/3/library/typing.html#typing.Self) #[derive(ViolationMetadata)] @@ -129,7 +134,7 @@ pub(crate) fn non_self_return_type( }; // PEP 673 forbids the use of `typing(_extensions).Self` in metaclasses. - if analyze::class::is_metaclass(class_def, semantic).into() { + if analyze::class::is_metaclass(class_def, semantic).is_yes() { return; } @@ -199,9 +204,7 @@ fn add_diagnostic( stmt.identifier(), ); - if let Some(fix) = replace_with_self_fix(checker, stmt, returns, class_def) { - diagnostic.set_fix(fix); - } + diagnostic.try_set_fix(|| replace_with_self_fix(checker, stmt, returns, class_def)); checker.diagnostics.push(diagnostic); } @@ -211,12 +214,11 @@ fn replace_with_self_fix( stmt: &Stmt, returns: &Expr, class_def: &ast::StmtClassDef, -) -> Option { +) -> anyhow::Result { let semantic = checker.semantic(); - let import_self = || -> Option { - let target_version = checker.settings.target_version.as_tuple(); - let source_module = if target_version >= (3, 11) { + let (self_import, self_binding) = { + let source_module = if checker.settings.target_version >= PythonVersion::Py311 { "typing" } else { "typing_extensions" @@ -224,40 +226,22 @@ fn replace_with_self_fix( let (importer, semantic) = (checker.importer(), checker.semantic()); let request = ImportRequest::import_from(source_module, "Self"); - - let (edit, ..) = importer - .get_or_import_symbol(&request, returns.start(), semantic) - .ok()?; - - Some(edit) + importer.get_or_import_symbol(&request, returns.start(), semantic)? }; - let remove_first_argument_type_hint = || -> Option { - let Stmt::FunctionDef(StmtFunctionDef { parameters, .. }) = stmt else { - return None; - }; + let mut others = Vec::with_capacity(2); + let remove_first_argument_type_hint = || -> Option { + let StmtFunctionDef { parameters, .. } = stmt.as_function_def_stmt()?; let first = parameters.iter().next()?; let annotation = first.annotation()?; - if !is_class_reference(semantic, annotation, &class_def.name) { - return None; - } - - Some(Edit::deletion(first.name().end(), annotation.end())) + is_class_reference(semantic, annotation, &class_def.name) + .then(|| Edit::deletion(first.name().end(), annotation.end())) }; - let replace_return_type_with_self = - || -> Edit { Edit::range_replacement("Self".to_string(), returns.range()) }; - - let import_self = import_self()?; - let mut others = Vec::with_capacity(2); - - others.push(replace_return_type_with_self()); - - if let Some(edit) = remove_first_argument_type_hint() { - others.push(edit); - } + others.extend(remove_first_argument_type_hint()); + others.push(Edit::range_replacement(self_binding, returns.range())); let applicability = if might_be_generic(class_def, checker.semantic()) { Applicability::DisplayOnly @@ -265,7 +249,7 @@ fn replace_with_self_fix( Applicability::Unsafe }; - Some(Fix::applicable_edits(import_self, others, applicability)) + Ok(Fix::applicable_edits(self_import, others, applicability)) } /// Return true if `annotation` is either `ClassName` or `type[ClassName]` diff --git a/crates/ruff_linter/src/rules/flake8_pyi/snapshots/ruff_linter__rules__flake8_pyi__tests__PYI034_PYI034.py.snap b/crates/ruff_linter/src/rules/flake8_pyi/snapshots/ruff_linter__rules__flake8_pyi__tests__PYI034_PYI034.py.snap index 9c0ac7fcf51fff..d2b3356ff6077f 100644 --- a/crates/ruff_linter/src/rules/flake8_pyi/snapshots/ruff_linter__rules__flake8_pyi__tests__PYI034_PYI034.py.snap +++ b/crates/ruff_linter/src/rules/flake8_pyi/snapshots/ruff_linter__rules__flake8_pyi__tests__PYI034_PYI034.py.snap @@ -1,6 +1,5 @@ --- source: crates/ruff_linter/src/rules/flake8_pyi/mod.rs -snapshot_kind: text --- PYI034.py:21:9: PYI034 [*] `__new__` methods in classes like `Bad` usually return `self` at runtime | @@ -17,7 +16,7 @@ PYI034.py:21:9: PYI034 [*] `__new__` methods in classes like `Bad` usually retur 19 19 | object 20 20 | ): # Y040 Do not inherit from "object" explicitly, as it is redundant in Python 3 21 |- def __new__(cls, *args: Any, **kwargs: Any) -> Bad: - 21 |+ def __new__(cls, *args: Any, **kwargs: Any) -> Self: + 21 |+ def __new__(cls, *args: Any, **kwargs: Any) -> typing.Self: 22 22 | ... # Y034 "__new__" methods usually return "self" at runtime. Consider using "typing_extensions.Self" in "Bad.__new__", e.g. "def __new__(cls, *args: Any, **kwargs: Any) -> Self: ..." 23 23 | 24 24 | def __repr__(self) -> str: @@ -37,7 +36,7 @@ PYI034.py:36:9: PYI034 [*] `__enter__` methods in classes like `Bad` usually ret 34 34 | ... # Y032 Prefer "object" to "Any" for the second parameter in "__ne__" methods 35 35 | 36 |- def __enter__(self) -> Bad: - 36 |+ def __enter__(self) -> Self: + 36 |+ def __enter__(self) -> typing.Self: 37 37 | ... # Y034 "__enter__" methods in classes like "Bad" usually return "self" at runtime. Consider using "typing_extensions.Self" in "Bad.__enter__", e.g. "def __enter__(self) -> Self: ..." 38 38 | 39 39 | async def __aenter__(self) -> Bad: @@ -57,7 +56,7 @@ PYI034.py:39:15: PYI034 [*] `__aenter__` methods in classes like `Bad` usually r 37 37 | ... # Y034 "__enter__" methods in classes like "Bad" usually return "self" at runtime. Consider using "typing_extensions.Self" in "Bad.__enter__", e.g. "def __enter__(self) -> Self: ..." 38 38 | 39 |- async def __aenter__(self) -> Bad: - 39 |+ async def __aenter__(self) -> Self: + 39 |+ async def __aenter__(self) -> typing.Self: 40 40 | ... # Y034 "__aenter__" methods in classes like "Bad" usually return "self" at runtime. Consider using "typing_extensions.Self" in "Bad.__aenter__", e.g. "async def __aenter__(self) -> Self: ..." 41 41 | 42 42 | def __iadd__(self, other: Bad) -> Bad: @@ -77,7 +76,7 @@ PYI034.py:42:9: PYI034 [*] `__iadd__` methods in classes like `Bad` usually retu 40 40 | ... # Y034 "__aenter__" methods in classes like "Bad" usually return "self" at runtime. Consider using "typing_extensions.Self" in "Bad.__aenter__", e.g. "async def __aenter__(self) -> Self: ..." 41 41 | 42 |- def __iadd__(self, other: Bad) -> Bad: - 42 |+ def __iadd__(self, other: Bad) -> Self: + 42 |+ def __iadd__(self, other: Bad) -> typing.Self: 43 43 | ... # Y034 "__iadd__" methods in classes like "Bad" usually return "self" at runtime. Consider using "typing_extensions.Self" in "Bad.__iadd__", e.g. "def __iadd__(self, other: Bad) -> Self: ..." 44 44 | 45 45 | @@ -96,7 +95,7 @@ PYI034.py:165:9: PYI034 [*] `__iter__` methods in classes like `BadIterator1` us 163 163 | 164 164 | class BadIterator1(Iterator[int]): 165 |- def __iter__(self) -> Iterator[int]: - 165 |+ def __iter__(self) -> Self: + 165 |+ def __iter__(self) -> typing.Self: 166 166 | ... # Y034 "__iter__" methods in classes like "BadIterator1" usually return "self" at runtime. Consider using "typing_extensions.Self" in "BadIterator1.__iter__", e.g. "def __iter__(self) -> Self: ..." 167 167 | 168 168 | @@ -116,7 +115,7 @@ PYI034.py:172:9: PYI034 [*] `__iter__` methods in classes like `BadIterator2` us 170 170 | typing.Iterator[int] 171 171 | ): # Y022 Use "collections.abc.Iterator[T]" instead of "typing.Iterator[T]" (PEP 585 syntax) 172 |- def __iter__(self) -> Iterator[int]: - 172 |+ def __iter__(self) -> Self: + 172 |+ def __iter__(self) -> typing.Self: 173 173 | ... # Y034 "__iter__" methods in classes like "BadIterator2" usually return "self" at runtime. Consider using "typing_extensions.Self" in "BadIterator2.__iter__", e.g. "def __iter__(self) -> Self: ..." 174 174 | 175 175 | @@ -136,7 +135,7 @@ PYI034.py:179:9: PYI034 [*] `__iter__` methods in classes like `BadIterator3` us 177 177 | typing.Iterator[int] 178 178 | ): # Y022 Use "collections.abc.Iterator[T]" instead of "typing.Iterator[T]" (PEP 585 syntax) 179 |- def __iter__(self) -> collections.abc.Iterator[int]: - 179 |+ def __iter__(self) -> Self: + 179 |+ def __iter__(self) -> typing.Self: 180 180 | ... # Y034 "__iter__" methods in classes like "BadIterator3" usually return "self" at runtime. Consider using "typing_extensions.Self" in "BadIterator3.__iter__", e.g. "def __iter__(self) -> Self: ..." 181 181 | 182 182 | @@ -156,7 +155,7 @@ PYI034.py:185:9: PYI034 [*] `__iter__` methods in classes like `BadIterator4` us 183 183 | class BadIterator4(Iterator[int]): 184 184 | # Note: *Iterable*, not *Iterator*, returned! 185 |- def __iter__(self) -> Iterable[int]: - 185 |+ def __iter__(self) -> Self: + 185 |+ def __iter__(self) -> typing.Self: 186 186 | ... # Y034 "__iter__" methods in classes like "BadIterator4" usually return "self" at runtime. Consider using "typing_extensions.Self" in "BadIterator4.__iter__", e.g. "def __iter__(self) -> Self: ..." 187 187 | 188 188 | @@ -175,7 +174,7 @@ PYI034.py:195:9: PYI034 [*] `__aiter__` methods in classes like `BadAsyncIterato 193 193 | 194 194 | class BadAsyncIterator(collections.abc.AsyncIterator[str]): 195 |- def __aiter__(self) -> typing.AsyncIterator[str]: - 195 |+ def __aiter__(self) -> Self: + 195 |+ def __aiter__(self) -> typing.Self: 196 196 | ... # Y034 "__aiter__" methods in classes like "BadAsyncIterator" usually return "self" at runtime. Consider using "typing_extensions.Self" in "BadAsyncIterator.__aiter__", e.g. "def __aiter__(self) -> Self: ..." # Y022 Use "collections.abc.AsyncIterator[T]" instead of "typing.AsyncIterator[T]" (PEP 585 syntax) 197 197 | 198 198 | class SubclassOfBadIterator3(BadIterator3): @@ -194,7 +193,7 @@ PYI034.py:199:9: PYI034 [*] `__iter__` methods in classes like `SubclassOfBadIte 197 197 | 198 198 | class SubclassOfBadIterator3(BadIterator3): 199 |- def __iter__(self) -> Iterator[int]: # Y034 - 199 |+ def __iter__(self) -> Self: # Y034 + 199 |+ def __iter__(self) -> typing.Self: # Y034 200 200 | ... 201 201 | 202 202 | class SubclassOfBadAsyncIterator(BadAsyncIterator): @@ -213,7 +212,7 @@ PYI034.py:203:9: PYI034 [*] `__aiter__` methods in classes like `SubclassOfBadAs 201 201 | 202 202 | class SubclassOfBadAsyncIterator(BadAsyncIterator): 203 |- def __aiter__(self) -> collections.abc.AsyncIterator[str]: # Y034 - 203 |+ def __aiter__(self) -> Self: # Y034 + 203 |+ def __aiter__(self) -> typing.Self: # Y034 204 204 | ... 205 205 | 206 206 | class AsyncIteratorReturningAsyncIterable: @@ -232,7 +231,7 @@ PYI034.py:327:9: PYI034 [*] `__new__` methods in classes like `NonGeneric1` usua 325 325 | 326 326 | class NonGeneric1(tuple): 327 |- def __new__(cls: type[NonGeneric1], *args, **kwargs) -> NonGeneric1: ... - 327 |+ def __new__(cls, *args, **kwargs) -> Self: ... + 327 |+ def __new__(cls, *args, **kwargs) -> typing.Self: ... 328 328 | def __enter__(self: NonGeneric1) -> NonGeneric1: ... 329 329 | 330 330 | class NonGeneric2(tuple): @@ -253,7 +252,7 @@ PYI034.py:328:9: PYI034 [*] `__enter__` methods in classes like `NonGeneric1` us 326 326 | class NonGeneric1(tuple): 327 327 | def __new__(cls: type[NonGeneric1], *args, **kwargs) -> NonGeneric1: ... 328 |- def __enter__(self: NonGeneric1) -> NonGeneric1: ... - 328 |+ def __enter__(self) -> Self: ... + 328 |+ def __enter__(self) -> typing.Self: ... 329 329 | 330 330 | class NonGeneric2(tuple): 331 331 | def __new__(cls: Type[NonGeneric2]) -> NonGeneric2: ... @@ -273,7 +272,7 @@ PYI034.py:331:9: PYI034 [*] `__new__` methods in classes like `NonGeneric2` usua 329 329 | 330 330 | class NonGeneric2(tuple): 331 |- def __new__(cls: Type[NonGeneric2]) -> NonGeneric2: ... - 331 |+ def __new__(cls) -> Self: ... + 331 |+ def __new__(cls) -> typing.Self: ... 332 332 | 333 333 | class Generic1[T](list): 334 334 | def __new__(cls: type[Generic1]) -> Generic1: ... @@ -292,7 +291,7 @@ PYI034.py:334:9: PYI034 `__new__` methods in classes like `Generic1` usually ret 332 332 | 333 333 | class Generic1[T](list): 334 |- def __new__(cls: type[Generic1]) -> Generic1: ... - 334 |+ def __new__(cls) -> Self: ... + 334 |+ def __new__(cls) -> typing.Self: ... 335 335 | def __enter__(self: Generic1) -> Generic1: ... 336 336 | 337 337 | @@ -311,7 +310,7 @@ PYI034.py:335:9: PYI034 `__enter__` methods in classes like `Generic1` usually r 333 333 | class Generic1[T](list): 334 334 | def __new__(cls: type[Generic1]) -> Generic1: ... 335 |- def __enter__(self: Generic1) -> Generic1: ... - 335 |+ def __enter__(self) -> Self: ... + 335 |+ def __enter__(self) -> typing.Self: ... 336 336 | 337 337 | 338 338 | ### Correctness of typevar-likes are not verified. @@ -330,7 +329,7 @@ PYI034.py:345:9: PYI034 `__new__` methods in classes like `Generic2` usually ret 343 343 | 344 344 | class Generic2(Generic[T]): 345 |- def __new__(cls: type[Generic2]) -> Generic2: ... - 345 |+ def __new__(cls) -> Self: ... + 345 |+ def __new__(cls) -> typing.Self: ... 346 346 | def __enter__(self: Generic2) -> Generic2: ... 347 347 | 348 348 | class Generic3(tuple[*Ts]): @@ -351,7 +350,7 @@ PYI034.py:346:9: PYI034 `__enter__` methods in classes like `Generic2` usually r 344 344 | class Generic2(Generic[T]): 345 345 | def __new__(cls: type[Generic2]) -> Generic2: ... 346 |- def __enter__(self: Generic2) -> Generic2: ... - 346 |+ def __enter__(self) -> Self: ... + 346 |+ def __enter__(self) -> typing.Self: ... 347 347 | 348 348 | class Generic3(tuple[*Ts]): 349 349 | def __new__(cls: type[Generic3]) -> Generic3: ... @@ -370,7 +369,7 @@ PYI034.py:349:9: PYI034 `__new__` methods in classes like `Generic3` usually ret 347 347 | 348 348 | class Generic3(tuple[*Ts]): 349 |- def __new__(cls: type[Generic3]) -> Generic3: ... - 349 |+ def __new__(cls) -> Self: ... + 349 |+ def __new__(cls) -> typing.Self: ... 350 350 | def __enter__(self: Generic3) -> Generic3: ... 351 351 | 352 352 | class Generic4(collections.abc.Callable[P, ...]): @@ -391,7 +390,7 @@ PYI034.py:350:9: PYI034 `__enter__` methods in classes like `Generic3` usually r 348 348 | class Generic3(tuple[*Ts]): 349 349 | def __new__(cls: type[Generic3]) -> Generic3: ... 350 |- def __enter__(self: Generic3) -> Generic3: ... - 350 |+ def __enter__(self) -> Self: ... + 350 |+ def __enter__(self) -> typing.Self: ... 351 351 | 352 352 | class Generic4(collections.abc.Callable[P, ...]): 353 353 | def __new__(cls: type[Generic4]) -> Generic4: ... @@ -410,7 +409,7 @@ PYI034.py:353:9: PYI034 `__new__` methods in classes like `Generic4` usually ret 351 351 | 352 352 | class Generic4(collections.abc.Callable[P, ...]): 353 |- def __new__(cls: type[Generic4]) -> Generic4: ... - 353 |+ def __new__(cls) -> Self: ... + 353 |+ def __new__(cls) -> typing.Self: ... 354 354 | def __enter__(self: Generic4) -> Generic4: ... 355 355 | 356 356 | from some_module import PotentialTypeVar @@ -431,7 +430,7 @@ PYI034.py:354:9: PYI034 `__enter__` methods in classes like `Generic4` usually r 352 352 | class Generic4(collections.abc.Callable[P, ...]): 353 353 | def __new__(cls: type[Generic4]) -> Generic4: ... 354 |- def __enter__(self: Generic4) -> Generic4: ... - 354 |+ def __enter__(self) -> Self: ... + 354 |+ def __enter__(self) -> typing.Self: ... 355 355 | 356 356 | from some_module import PotentialTypeVar 357 357 | @@ -450,7 +449,7 @@ PYI034.py:359:9: PYI034 [*] `__new__` methods in classes like `Generic5` usually 357 357 | 358 358 | class Generic5(list[PotentialTypeVar]): 359 |- def __new__(cls: type[Generic5]) -> Generic5: ... - 359 |+ def __new__(cls) -> Self: ... + 359 |+ def __new__(cls) -> typing.Self: ... 360 360 | def __enter__(self: Generic5) -> Generic5: ... 361 361 | @@ -468,5 +467,5 @@ PYI034.py:360:9: PYI034 [*] `__enter__` methods in classes like `Generic5` usual 358 358 | class Generic5(list[PotentialTypeVar]): 359 359 | def __new__(cls: type[Generic5]) -> Generic5: ... 360 |- def __enter__(self: Generic5) -> Generic5: ... - 360 |+ def __enter__(self) -> Self: ... + 360 |+ def __enter__(self) -> typing.Self: ... 361 361 | diff --git a/crates/ruff_linter/src/rules/flake8_pyi/snapshots/ruff_linter__rules__flake8_pyi__tests__PYI034_PYI034.pyi.snap b/crates/ruff_linter/src/rules/flake8_pyi/snapshots/ruff_linter__rules__flake8_pyi__tests__PYI034_PYI034.pyi.snap index d5e4d9ad044a7a..941c11fe991734 100644 --- a/crates/ruff_linter/src/rules/flake8_pyi/snapshots/ruff_linter__rules__flake8_pyi__tests__PYI034_PYI034.pyi.snap +++ b/crates/ruff_linter/src/rules/flake8_pyi/snapshots/ruff_linter__rules__flake8_pyi__tests__PYI034_PYI034.pyi.snap @@ -1,6 +1,5 @@ --- source: crates/ruff_linter/src/rules/flake8_pyi/mod.rs -snapshot_kind: text --- PYI034.pyi:20:9: PYI034 [*] `__new__` methods in classes like `Bad` usually return `self` at runtime | @@ -18,7 +17,7 @@ PYI034.pyi:20:9: PYI034 [*] `__new__` methods in classes like `Bad` usually retu 20 20 | def __new__( 21 21 | cls, *args: Any, **kwargs: Any 22 |- ) -> Bad: ... # Y034 "__new__" methods usually return "self" at runtime. Consider using "typing_extensions.Self" in "Bad.__new__", e.g. "def __new__(cls, *args: Any, **kwargs: Any) -> Self: ..." - 22 |+ ) -> Self: ... # Y034 "__new__" methods usually return "self" at runtime. Consider using "typing_extensions.Self" in "Bad.__new__", e.g. "def __new__(cls, *args: Any, **kwargs: Any) -> Self: ..." + 22 |+ ) -> typing.Self: ... # Y034 "__new__" methods usually return "self" at runtime. Consider using "typing_extensions.Self" in "Bad.__new__", e.g. "def __new__(cls, *args: Any, **kwargs: Any) -> Self: ..." 23 23 | def __repr__( 24 24 | self, 25 25 | ) -> str: ... # Y029 Defining __repr__ or __str__ in a stub is almost always redundant @@ -39,7 +38,7 @@ PYI034.pyi:35:9: PYI034 [*] `__enter__` methods in classes like `Bad` usually re 35 35 | def __enter__( 36 36 | self, 37 |- ) -> Bad: ... # Y034 "__enter__" methods in classes like "Bad" usually return "self" at runtime. Consider using "typing_extensions.Self" in "Bad.__enter__", e.g. "def __enter__(self) -> Self: ..." - 37 |+ ) -> Self: ... # Y034 "__enter__" methods in classes like "Bad" usually return "self" at runtime. Consider using "typing_extensions.Self" in "Bad.__enter__", e.g. "def __enter__(self) -> Self: ..." + 37 |+ ) -> typing.Self: ... # Y034 "__enter__" methods in classes like "Bad" usually return "self" at runtime. Consider using "typing_extensions.Self" in "Bad.__enter__", e.g. "def __enter__(self) -> Self: ..." 38 38 | async def __aenter__( 39 39 | self, 40 40 | ) -> Bad: ... # Y034 "__aenter__" methods in classes like "Bad" usually return "self" at runtime. Consider using "typing_extensions.Self" in "Bad.__aenter__", e.g. "async def __aenter__(self) -> Self: ..." @@ -60,7 +59,7 @@ PYI034.pyi:38:15: PYI034 [*] `__aenter__` methods in classes like `Bad` usually 38 38 | async def __aenter__( 39 39 | self, 40 |- ) -> Bad: ... # Y034 "__aenter__" methods in classes like "Bad" usually return "self" at runtime. Consider using "typing_extensions.Self" in "Bad.__aenter__", e.g. "async def __aenter__(self) -> Self: ..." - 40 |+ ) -> Self: ... # Y034 "__aenter__" methods in classes like "Bad" usually return "self" at runtime. Consider using "typing_extensions.Self" in "Bad.__aenter__", e.g. "async def __aenter__(self) -> Self: ..." + 40 |+ ) -> typing.Self: ... # Y034 "__aenter__" methods in classes like "Bad" usually return "self" at runtime. Consider using "typing_extensions.Self" in "Bad.__aenter__", e.g. "async def __aenter__(self) -> Self: ..." 41 41 | def __iadd__( 42 42 | self, other: Bad 43 43 | ) -> Bad: ... # Y034 "__iadd__" methods in classes like "Bad" usually return "self" at runtime. Consider using "typing_extensions.Self" in "Bad.__iadd__", e.g. "def __iadd__(self, other: Bad) -> Self: ..." @@ -81,7 +80,7 @@ PYI034.pyi:41:9: PYI034 [*] `__iadd__` methods in classes like `Bad` usually ret 41 41 | def __iadd__( 42 42 | self, other: Bad 43 |- ) -> Bad: ... # Y034 "__iadd__" methods in classes like "Bad" usually return "self" at runtime. Consider using "typing_extensions.Self" in "Bad.__iadd__", e.g. "def __iadd__(self, other: Bad) -> Self: ..." - 43 |+ ) -> Self: ... # Y034 "__iadd__" methods in classes like "Bad" usually return "self" at runtime. Consider using "typing_extensions.Self" in "Bad.__iadd__", e.g. "def __iadd__(self, other: Bad) -> Self: ..." + 43 |+ ) -> typing.Self: ... # Y034 "__iadd__" methods in classes like "Bad" usually return "self" at runtime. Consider using "typing_extensions.Self" in "Bad.__iadd__", e.g. "def __iadd__(self, other: Bad) -> Self: ..." 44 44 | 45 45 | class AlsoBad( 46 46 | int, builtins.object @@ -103,7 +102,7 @@ PYI034.pyi:104:9: PYI034 [*] `__iter__` methods in classes like `BadIterator1` u 106 |- ) -> Iterator[ 107 |- int 108 |- ]: ... # Y034 "__iter__" methods in classes like "BadIterator1" usually return "self" at runtime. Consider using "typing_extensions.Self" in "BadIterator1.__iter__", e.g. "def __iter__(self) -> Self: ..." - 106 |+ ) -> Self: ... # Y034 "__iter__" methods in classes like "BadIterator1" usually return "self" at runtime. Consider using "typing_extensions.Self" in "BadIterator1.__iter__", e.g. "def __iter__(self) -> Self: ..." + 106 |+ ) -> typing.Self: ... # Y034 "__iter__" methods in classes like "BadIterator1" usually return "self" at runtime. Consider using "typing_extensions.Self" in "BadIterator1.__iter__", e.g. "def __iter__(self) -> Self: ..." 109 107 | 110 108 | class BadIterator2( 111 109 | typing.Iterator[int] @@ -126,7 +125,7 @@ PYI034.pyi:113:9: PYI034 [*] `__iter__` methods in classes like `BadIterator2` u 115 |- ) -> Iterator[ 116 |- int 117 |- ]: ... # Y034 "__iter__" methods in classes like "BadIterator2" usually return "self" at runtime. Consider using "typing_extensions.Self" in "BadIterator2.__iter__", e.g. "def __iter__(self) -> Self: ..." - 115 |+ ) -> Self: ... # Y034 "__iter__" methods in classes like "BadIterator2" usually return "self" at runtime. Consider using "typing_extensions.Self" in "BadIterator2.__iter__", e.g. "def __iter__(self) -> Self: ..." + 115 |+ ) -> typing.Self: ... # Y034 "__iter__" methods in classes like "BadIterator2" usually return "self" at runtime. Consider using "typing_extensions.Self" in "BadIterator2.__iter__", e.g. "def __iter__(self) -> Self: ..." 118 116 | 119 117 | class BadIterator3( 120 118 | typing.Iterator[int] @@ -149,7 +148,7 @@ PYI034.pyi:122:9: PYI034 [*] `__iter__` methods in classes like `BadIterator3` u 124 |- ) -> collections.abc.Iterator[ 125 |- int 126 |- ]: ... # Y034 "__iter__" methods in classes like "BadIterator3" usually return "self" at runtime. Consider using "typing_extensions.Self" in "BadIterator3.__iter__", e.g. "def __iter__(self) -> Self: ..." - 124 |+ ) -> Self: ... # Y034 "__iter__" methods in classes like "BadIterator3" usually return "self" at runtime. Consider using "typing_extensions.Self" in "BadIterator3.__iter__", e.g. "def __iter__(self) -> Self: ..." + 124 |+ ) -> typing.Self: ... # Y034 "__iter__" methods in classes like "BadIterator3" usually return "self" at runtime. Consider using "typing_extensions.Self" in "BadIterator3.__iter__", e.g. "def __iter__(self) -> Self: ..." 127 125 | 128 126 | class BadIterator4(Iterator[int]): 129 127 | # Note: *Iterable*, not *Iterator*, returned! @@ -172,7 +171,7 @@ PYI034.pyi:130:9: PYI034 [*] `__iter__` methods in classes like `BadIterator4` u 132 |- ) -> Iterable[ 133 |- int 134 |- ]: ... # Y034 "__iter__" methods in classes like "BadIterator4" usually return "self" at runtime. Consider using "typing_extensions.Self" in "BadIterator4.__iter__", e.g. "def __iter__(self) -> Self: ..." - 132 |+ ) -> Self: ... # Y034 "__iter__" methods in classes like "BadIterator4" usually return "self" at runtime. Consider using "typing_extensions.Self" in "BadIterator4.__iter__", e.g. "def __iter__(self) -> Self: ..." + 132 |+ ) -> typing.Self: ... # Y034 "__iter__" methods in classes like "BadIterator4" usually return "self" at runtime. Consider using "typing_extensions.Self" in "BadIterator4.__iter__", e.g. "def __iter__(self) -> Self: ..." 135 133 | 136 134 | class IteratorReturningIterable: 137 135 | def __iter__( @@ -194,7 +193,7 @@ PYI034.pyi:144:9: PYI034 [*] `__aiter__` methods in classes like `BadAsyncIterat 146 |- ) -> typing.AsyncIterator[ 147 |- str 148 |- ]: ... # Y034 "__aiter__" methods in classes like "BadAsyncIterator" usually return "self" at runtime. Consider using "typing_extensions.Self" in "BadAsyncIterator.__aiter__", e.g. "def __aiter__(self) -> Self: ..." # Y022 Use "collections.abc.AsyncIterator[T]" instead of "typing.AsyncIterator[T]" (PEP 585 syntax) - 146 |+ ) -> Self: ... # Y034 "__aiter__" methods in classes like "BadAsyncIterator" usually return "self" at runtime. Consider using "typing_extensions.Self" in "BadAsyncIterator.__aiter__", e.g. "def __aiter__(self) -> Self: ..." # Y022 Use "collections.abc.AsyncIterator[T]" instead of "typing.AsyncIterator[T]" (PEP 585 syntax) + 146 |+ ) -> typing.Self: ... # Y034 "__aiter__" methods in classes like "BadAsyncIterator" usually return "self" at runtime. Consider using "typing_extensions.Self" in "BadAsyncIterator.__aiter__", e.g. "def __aiter__(self) -> Self: ..." # Y022 Use "collections.abc.AsyncIterator[T]" instead of "typing.AsyncIterator[T]" (PEP 585 syntax) 149 147 | 150 148 | class AsyncIteratorReturningAsyncIterable: 151 149 | def __aiter__( @@ -213,7 +212,7 @@ PYI034.pyi:221:9: PYI034 [*] `__new__` methods in classes like `NonGeneric1` usu 219 219 | 220 220 | class NonGeneric1(tuple): 221 |- def __new__(cls: type[NonGeneric1], *args, **kwargs) -> NonGeneric1: ... - 221 |+ def __new__(cls, *args, **kwargs) -> Self: ... + 221 |+ def __new__(cls, *args, **kwargs) -> typing.Self: ... 222 222 | def __enter__(self: NonGeneric1) -> NonGeneric1: ... 223 223 | 224 224 | class NonGeneric2(tuple): @@ -234,7 +233,7 @@ PYI034.pyi:222:9: PYI034 [*] `__enter__` methods in classes like `NonGeneric1` u 220 220 | class NonGeneric1(tuple): 221 221 | def __new__(cls: type[NonGeneric1], *args, **kwargs) -> NonGeneric1: ... 222 |- def __enter__(self: NonGeneric1) -> NonGeneric1: ... - 222 |+ def __enter__(self) -> Self: ... + 222 |+ def __enter__(self) -> typing.Self: ... 223 223 | 224 224 | class NonGeneric2(tuple): 225 225 | def __new__(cls: Type[NonGeneric2]) -> NonGeneric2: ... @@ -254,7 +253,7 @@ PYI034.pyi:225:9: PYI034 [*] `__new__` methods in classes like `NonGeneric2` usu 223 223 | 224 224 | class NonGeneric2(tuple): 225 |- def __new__(cls: Type[NonGeneric2]) -> NonGeneric2: ... - 225 |+ def __new__(cls) -> Self: ... + 225 |+ def __new__(cls) -> typing.Self: ... 226 226 | 227 227 | class Generic1[T](list): 228 228 | def __new__(cls: type[Generic1]) -> Generic1: ... @@ -273,7 +272,7 @@ PYI034.pyi:228:9: PYI034 `__new__` methods in classes like `Generic1` usually re 226 226 | 227 227 | class Generic1[T](list): 228 |- def __new__(cls: type[Generic1]) -> Generic1: ... - 228 |+ def __new__(cls) -> Self: ... + 228 |+ def __new__(cls) -> typing.Self: ... 229 229 | def __enter__(self: Generic1) -> Generic1: ... 230 230 | 231 231 | @@ -292,12 +291,12 @@ PYI034.pyi:229:9: PYI034 `__enter__` methods in classes like `Generic1` usually 227 227 | class Generic1[T](list): 228 228 | def __new__(cls: type[Generic1]) -> Generic1: ... 229 |- def __enter__(self: Generic1) -> Generic1: ... - 229 |+ def __enter__(self) -> Self: ... + 229 |+ def __enter__(self) -> typing.Self: ... 230 230 | 231 231 | 232 232 | ### Correctness of typevar-likes are not verified. -PYI034.pyi:239:9: PYI034 [*] `__new__` methods in classes like `Generic2` usually return `self` at runtime +PYI034.pyi:239:9: PYI034 `__new__` methods in classes like `Generic2` usually return `self` at runtime | 238 | class Generic2(Generic[T]): 239 | def __new__(cls: type[Generic2]) -> Generic2: ... @@ -306,17 +305,17 @@ PYI034.pyi:239:9: PYI034 [*] `__new__` methods in classes like `Generic2` usuall | = help: Use `Self` as return type -ℹ Unsafe fix +ℹ Display-only fix 236 236 | Ts = TypeVarTuple('foo') 237 237 | 238 238 | class Generic2(Generic[T]): 239 |- def __new__(cls: type[Generic2]) -> Generic2: ... - 239 |+ def __new__(cls) -> Self: ... + 239 |+ def __new__(cls) -> typing.Self: ... 240 240 | def __enter__(self: Generic2) -> Generic2: ... 241 241 | 242 242 | class Generic3(tuple[*Ts]): -PYI034.pyi:240:9: PYI034 [*] `__enter__` methods in classes like `Generic2` usually return `self` at runtime +PYI034.pyi:240:9: PYI034 `__enter__` methods in classes like `Generic2` usually return `self` at runtime | 238 | class Generic2(Generic[T]): 239 | def __new__(cls: type[Generic2]) -> Generic2: ... @@ -327,17 +326,17 @@ PYI034.pyi:240:9: PYI034 [*] `__enter__` methods in classes like `Generic2` usua | = help: Use `Self` as return type -ℹ Unsafe fix +ℹ Display-only fix 237 237 | 238 238 | class Generic2(Generic[T]): 239 239 | def __new__(cls: type[Generic2]) -> Generic2: ... 240 |- def __enter__(self: Generic2) -> Generic2: ... - 240 |+ def __enter__(self) -> Self: ... + 240 |+ def __enter__(self) -> typing.Self: ... 241 241 | 242 242 | class Generic3(tuple[*Ts]): 243 243 | def __new__(cls: type[Generic3]) -> Generic3: ... -PYI034.pyi:243:9: PYI034 [*] `__new__` methods in classes like `Generic3` usually return `self` at runtime +PYI034.pyi:243:9: PYI034 `__new__` methods in classes like `Generic3` usually return `self` at runtime | 242 | class Generic3(tuple[*Ts]): 243 | def __new__(cls: type[Generic3]) -> Generic3: ... @@ -346,17 +345,17 @@ PYI034.pyi:243:9: PYI034 [*] `__new__` methods in classes like `Generic3` usuall | = help: Use `Self` as return type -ℹ Unsafe fix +ℹ Display-only fix 240 240 | def __enter__(self: Generic2) -> Generic2: ... 241 241 | 242 242 | class Generic3(tuple[*Ts]): 243 |- def __new__(cls: type[Generic3]) -> Generic3: ... - 243 |+ def __new__(cls) -> Self: ... + 243 |+ def __new__(cls) -> typing.Self: ... 244 244 | def __enter__(self: Generic3) -> Generic3: ... 245 245 | 246 246 | class Generic4(collections.abc.Callable[P, ...]): -PYI034.pyi:244:9: PYI034 [*] `__enter__` methods in classes like `Generic3` usually return `self` at runtime +PYI034.pyi:244:9: PYI034 `__enter__` methods in classes like `Generic3` usually return `self` at runtime | 242 | class Generic3(tuple[*Ts]): 243 | def __new__(cls: type[Generic3]) -> Generic3: ... @@ -367,17 +366,17 @@ PYI034.pyi:244:9: PYI034 [*] `__enter__` methods in classes like `Generic3` usua | = help: Use `Self` as return type -ℹ Unsafe fix +ℹ Display-only fix 241 241 | 242 242 | class Generic3(tuple[*Ts]): 243 243 | def __new__(cls: type[Generic3]) -> Generic3: ... 244 |- def __enter__(self: Generic3) -> Generic3: ... - 244 |+ def __enter__(self) -> Self: ... + 244 |+ def __enter__(self) -> typing.Self: ... 245 245 | 246 246 | class Generic4(collections.abc.Callable[P, ...]): 247 247 | def __new__(cls: type[Generic4]) -> Generic4: ... -PYI034.pyi:247:9: PYI034 [*] `__new__` methods in classes like `Generic4` usually return `self` at runtime +PYI034.pyi:247:9: PYI034 `__new__` methods in classes like `Generic4` usually return `self` at runtime | 246 | class Generic4(collections.abc.Callable[P, ...]): 247 | def __new__(cls: type[Generic4]) -> Generic4: ... @@ -386,17 +385,17 @@ PYI034.pyi:247:9: PYI034 [*] `__new__` methods in classes like `Generic4` usuall | = help: Use `Self` as return type -ℹ Unsafe fix +ℹ Display-only fix 244 244 | def __enter__(self: Generic3) -> Generic3: ... 245 245 | 246 246 | class Generic4(collections.abc.Callable[P, ...]): 247 |- def __new__(cls: type[Generic4]) -> Generic4: ... - 247 |+ def __new__(cls) -> Self: ... + 247 |+ def __new__(cls) -> typing.Self: ... 248 248 | def __enter__(self: Generic4) -> Generic4: ... 249 249 | 250 250 | from some_module import PotentialTypeVar -PYI034.pyi:248:9: PYI034 [*] `__enter__` methods in classes like `Generic4` usually return `self` at runtime +PYI034.pyi:248:9: PYI034 `__enter__` methods in classes like `Generic4` usually return `self` at runtime | 246 | class Generic4(collections.abc.Callable[P, ...]): 247 | def __new__(cls: type[Generic4]) -> Generic4: ... @@ -407,17 +406,17 @@ PYI034.pyi:248:9: PYI034 [*] `__enter__` methods in classes like `Generic4` usua | = help: Use `Self` as return type -ℹ Unsafe fix +ℹ Display-only fix 245 245 | 246 246 | class Generic4(collections.abc.Callable[P, ...]): 247 247 | def __new__(cls: type[Generic4]) -> Generic4: ... 248 |- def __enter__(self: Generic4) -> Generic4: ... - 248 |+ def __enter__(self) -> Self: ... + 248 |+ def __enter__(self) -> typing.Self: ... 249 249 | 250 250 | from some_module import PotentialTypeVar 251 251 | -PYI034.pyi:253:9: PYI034 [*] `__new__` methods in classes like `Generic5` usually return `self` at runtime +PYI034.pyi:253:9: PYI034 `__new__` methods in classes like `Generic5` usually return `self` at runtime | 252 | class Generic5(list[PotentialTypeVar]): 253 | def __new__(cls: type[Generic5]) -> Generic5: ... @@ -426,15 +425,15 @@ PYI034.pyi:253:9: PYI034 [*] `__new__` methods in classes like `Generic5` usuall | = help: Use `Self` as return type -ℹ Unsafe fix +ℹ Display-only fix 250 250 | from some_module import PotentialTypeVar 251 251 | 252 252 | class Generic5(list[PotentialTypeVar]): 253 |- def __new__(cls: type[Generic5]) -> Generic5: ... - 253 |+ def __new__(cls) -> Self: ... + 253 |+ def __new__(cls) -> typing.Self: ... 254 254 | def __enter__(self: Generic5) -> Generic5: ... -PYI034.pyi:254:9: PYI034 [*] `__enter__` methods in classes like `Generic5` usually return `self` at runtime +PYI034.pyi:254:9: PYI034 `__enter__` methods in classes like `Generic5` usually return `self` at runtime | 252 | class Generic5(list[PotentialTypeVar]): 253 | def __new__(cls: type[Generic5]) -> Generic5: ... @@ -443,9 +442,9 @@ PYI034.pyi:254:9: PYI034 [*] `__enter__` methods in classes like `Generic5` usua | = help: Use `Self` as return type -ℹ Unsafe fix +ℹ Display-only fix 251 251 | 252 252 | class Generic5(list[PotentialTypeVar]): 253 253 | def __new__(cls: type[Generic5]) -> Generic5: ... 254 |- def __enter__(self: Generic5) -> Generic5: ... - 254 |+ def __enter__(self) -> Self: ... + 254 |+ def __enter__(self) -> typing.Self: ... diff --git a/crates/ruff_python_semantic/src/analyze/class.rs b/crates/ruff_python_semantic/src/analyze/class.rs index d68f9ec95bbe51..64e49421a73308 100644 --- a/crates/ruff_python_semantic/src/analyze/class.rs +++ b/crates/ruff_python_semantic/src/analyze/class.rs @@ -130,9 +130,9 @@ pub enum IsMetaclass { Maybe, } -impl From for bool { - fn from(value: IsMetaclass) -> Self { - matches!(value, IsMetaclass::Yes) +impl IsMetaclass { + pub const fn is_yes(self) -> bool { + matches!(self, IsMetaclass::Yes) } } @@ -212,21 +212,16 @@ fn expr_might_be_old_style_typevar_like(expr: &Expr, semantic: &SemanticModel) - match expr { Expr::Attribute(..) => true, Expr::Name(name) => might_be_old_style_typevar_like(name, semantic), - - Expr::Starred(ExprStarred { value, .. }) => match value.as_ref() { - Expr::Attribute(..) => true, - Expr::Name(name) => might_be_old_style_typevar_like(name, semantic), - _ => false, - }, - + Expr::Starred(ExprStarred { value, .. }) => { + expr_might_be_old_style_typevar_like(value, semantic) + } _ => false, } } fn might_be_old_style_typevar_like(name: &ExprName, semantic: &SemanticModel) -> bool { let Some(binding) = semantic.only_binding(name).map(|id| semantic.binding(id)) else { - return false; + return !semantic.has_builtin_binding(&name.id); }; - typing::is_type_var_like(binding, semantic) }