From 13a1483f1e51087d7c95922a6b3d2914ac2b68c3 Mon Sep 17 00:00:00 2001 From: InSync Date: Tue, 12 Nov 2024 18:13:15 +0700 Subject: [PATCH] [`flake8-pyi`] Add "replace with `Self`" fix (`PYI019`) (#14238) Co-authored-by: Micha Reiser --- .../test/fixtures/flake8_pyi/PYI019.py | 35 ++ .../test/fixtures/flake8_pyi/PYI019.pyi | 35 ++ .../src/checkers/ast/analyze/statement.rs | 11 +- .../ruff_linter/src/rules/flake8_pyi/mod.rs | 17 + .../rules/custom_type_var_return_type.rs | 268 ++++++++++-- .../flake8_pyi/rules/non_self_return_type.rs | 7 +- ...__flake8_pyi__tests__PYI019_PYI019.py.snap | 151 ++++++- ..._flake8_pyi__tests__PYI019_PYI019.pyi.snap | 171 +++++++- ...sts__custom_classmethod_rules_preview.snap | 414 ++++++++++++++++++ 9 files changed, 1045 insertions(+), 64 deletions(-) create mode 100644 crates/ruff_linter/src/rules/flake8_pyi/snapshots/ruff_linter__rules__flake8_pyi__tests__custom_classmethod_rules_preview.snap diff --git a/crates/ruff_linter/resources/test/fixtures/flake8_pyi/PYI019.py b/crates/ruff_linter/resources/test/fixtures/flake8_pyi/PYI019.py index 8e9338574e0ae..e6fd279b14605 100644 --- a/crates/ruff_linter/resources/test/fixtures/flake8_pyi/PYI019.py +++ b/crates/ruff_linter/resources/test/fixtures/flake8_pyi/PYI019.py @@ -52,3 +52,38 @@ class CustomClassMethod: # in the settings for this test: @foo_classmethod def foo[S](cls: type[S]) -> S: ... # PYI019 + + +_S695 = TypeVar("_S695", bound="PEP695Fix") + +# Only .pyi gets fixes, no fixes for .py +class PEP695Fix: + def __new__[S: PEP695Fix](cls: type[S]) -> S: ... + + def __init_subclass__[S](cls: type[S]) -> S: ... + + def __neg__[S: PEP695Fix](self: S) -> S: ... + + def __pos__[S](self: S) -> S: ... + + def __add__[S: PEP695Fix](self: S, other: S) -> S: ... + + def __sub__[S](self: S, other: S) -> S: ... + + @classmethod + def class_method_bound[S: PEP695Fix](cls: type[S]) -> S: ... + + @classmethod + def class_method_unbound[S](cls: type[S]) -> S: ... + + def instance_method_bound[S: PEP695Fix](self: S) -> S: ... + + def instance_method_unbound[S](self: S) -> S: ... + + def instance_method_bound_with_another_parameter[S: PEP695Fix](self: S, other: S) -> S: ... + + def instance_method_unbound_with_another_parameter[S](self: S, other: S) -> S: ... + + def multiple_type_vars[S, *Ts, T](self: S, other: S, /, *args: *Ts, a: T, b: list[T]) -> S: ... + + def mixing_old_and_new_style_type_vars[T](self: _S695, a: T, b: T) -> _S695: ... diff --git a/crates/ruff_linter/resources/test/fixtures/flake8_pyi/PYI019.pyi b/crates/ruff_linter/resources/test/fixtures/flake8_pyi/PYI019.pyi index 8e9338574e0ae..e6fd279b14605 100644 --- a/crates/ruff_linter/resources/test/fixtures/flake8_pyi/PYI019.pyi +++ b/crates/ruff_linter/resources/test/fixtures/flake8_pyi/PYI019.pyi @@ -52,3 +52,38 @@ class CustomClassMethod: # in the settings for this test: @foo_classmethod def foo[S](cls: type[S]) -> S: ... # PYI019 + + +_S695 = TypeVar("_S695", bound="PEP695Fix") + +# Only .pyi gets fixes, no fixes for .py +class PEP695Fix: + def __new__[S: PEP695Fix](cls: type[S]) -> S: ... + + def __init_subclass__[S](cls: type[S]) -> S: ... + + def __neg__[S: PEP695Fix](self: S) -> S: ... + + def __pos__[S](self: S) -> S: ... + + def __add__[S: PEP695Fix](self: S, other: S) -> S: ... + + def __sub__[S](self: S, other: S) -> S: ... + + @classmethod + def class_method_bound[S: PEP695Fix](cls: type[S]) -> S: ... + + @classmethod + def class_method_unbound[S](cls: type[S]) -> S: ... + + def instance_method_bound[S: PEP695Fix](self: S) -> S: ... + + def instance_method_unbound[S](self: S) -> S: ... + + def instance_method_bound_with_another_parameter[S: PEP695Fix](self: S, other: S) -> S: ... + + def instance_method_unbound_with_another_parameter[S](self: S, other: S) -> S: ... + + def multiple_type_vars[S, *Ts, T](self: S, other: S, /, *args: *Ts, a: T, b: list[T]) -> S: ... + + def mixing_old_and_new_style_type_vars[T](self: _S695, a: T, b: T) -> _S695: ... diff --git a/crates/ruff_linter/src/checkers/ast/analyze/statement.rs b/crates/ruff_linter/src/checkers/ast/analyze/statement.rs index 6624dd8d14479..1883fab5f4ada 100644 --- a/crates/ruff_linter/src/checkers/ast/analyze/statement.rs +++ b/crates/ruff_linter/src/checkers/ast/analyze/statement.rs @@ -81,7 +81,7 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) { returns, parameters, body, - type_params, + type_params: _, range: _, }, ) => { @@ -160,14 +160,7 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) { flake8_pyi::rules::bad_generator_return_type(function_def, checker); } if checker.enabled(Rule::CustomTypeVarReturnType) { - flake8_pyi::rules::custom_type_var_return_type( - checker, - name, - decorator_list, - returns.as_ref().map(AsRef::as_ref), - parameters, - type_params.as_deref(), - ); + flake8_pyi::rules::custom_type_var_return_type(checker, function_def); } if checker.source_type.is_stub() { if checker.enabled(Rule::StrOrReprDefinedInStub) { diff --git a/crates/ruff_linter/src/rules/flake8_pyi/mod.rs b/crates/ruff_linter/src/rules/flake8_pyi/mod.rs index 61e396aa21128..9c666432ab8b1 100644 --- a/crates/ruff_linter/src/rules/flake8_pyi/mod.rs +++ b/crates/ruff_linter/src/rules/flake8_pyi/mod.rs @@ -152,6 +152,23 @@ mod tests { Ok(()) } + #[test] + fn custom_classmethod_rules_preview() -> Result<()> { + let diagnostics = test_path( + Path::new("flake8_pyi/PYI019.pyi"), + &settings::LinterSettings { + pep8_naming: pep8_naming::settings::Settings { + classmethod_decorators: vec!["foo_classmethod".to_string()], + ..pep8_naming::settings::Settings::default() + }, + preview: PreviewMode::Enabled, + ..settings::LinterSettings::for_rule(Rule::CustomTypeVarReturnType) + }, + )?; + assert_messages!(diagnostics); + Ok(()) + } + #[test_case(Rule::TypeAliasWithoutAnnotation, Path::new("PYI026.py"))] #[test_case(Rule::TypeAliasWithoutAnnotation, Path::new("PYI026.pyi"))] fn py38(rule_code: Rule, path: &Path) -> Result<()> { diff --git a/crates/ruff_linter/src/rules/flake8_pyi/rules/custom_type_var_return_type.rs b/crates/ruff_linter/src/rules/flake8_pyi/rules/custom_type_var_return_type.rs index 34eb68943f693..5baf7907d2ad6 100644 --- a/crates/ruff_linter/src/rules/flake8_pyi/rules/custom_type_var_return_type.rs +++ b/crates/ruff_linter/src/rules/flake8_pyi/rules/custom_type_var_return_type.rs @@ -1,26 +1,27 @@ -use ruff_diagnostics::{Diagnostic, Violation}; +use crate::checkers::ast::Checker; +use crate::importer::ImportRequest; +use itertools::Itertools; +use ruff_diagnostics::{Applicability, Diagnostic, Edit, Fix, FixAvailability, Violation}; use ruff_macros::{derive_message_formats, violation}; use ruff_python_ast as ast; use ruff_python_ast::helpers::map_subscript; -use ruff_python_ast::{Decorator, Expr, Parameters, TypeParam, TypeParams}; +use ruff_python_ast::{Expr, Parameters, TypeParam, TypeParams}; use ruff_python_semantic::analyze::function_type::{self, FunctionType}; use ruff_python_semantic::analyze::visibility::{is_abstract, is_overload}; -use ruff_text_size::Ranged; - -use crate::checkers::ast::Checker; +use ruff_text_size::{Ranged, TextRange}; /// ## What it does /// Checks for methods that define a custom `TypeVar` for their return type -/// annotation instead of using `typing_extensions.Self`. +/// annotation instead of using `Self`. /// /// ## Why is this bad? -/// While the semantics are often identical, using `typing_extensions.Self` is -/// more intuitive and succinct (per [PEP 673]) than a custom `TypeVar`. For -/// example, the use of `Self` will typically allow for the omission of type -/// parameters on the `self` and `cls` arguments. +/// While the semantics are often identical, using `Self` is more intuitive +/// and succinct (per [PEP 673]) than a custom `TypeVar`. For example, the +/// use of `Self` will typically allow for the omission of type parameters +/// on the `self` and `cls` arguments. /// -/// This check currently applies to instance methods that return `self`, class -/// methods that return an instance of `cls`, and `__new__` methods. +/// This check currently applies to instance methods that return `self`, +/// class methods that return an instance of `cls`, and `__new__` methods. /// /// ## Example /// @@ -44,47 +45,67 @@ use crate::checkers::ast::Checker; /// def bar(cls, arg: int) -> Self: ... /// ``` /// +/// ## Fix safety +/// The fix is only available in stub files. +/// It will try to remove all usages and declarations of the custom type variable. +/// Pre-[PEP-695]-style declarations will not be removed. +/// +/// If a variable's annotation is too complex to handle, +/// the fix will be marked as display only. +/// Otherwise, it will be marked as safe. +/// /// [PEP 673]: https://peps.python.org/pep-0673/#motivation +/// [PEP 695]: https://peps.python.org/pep-0695/ #[violation] pub struct CustomTypeVarReturnType { method_name: String, + in_stub: bool, } impl Violation for CustomTypeVarReturnType { + const FIX_AVAILABILITY: FixAvailability = FixAvailability::Sometimes; + #[derive_message_formats] fn message(&self) -> String { - let CustomTypeVarReturnType { method_name } = self; - format!( - "Methods like `{method_name}` should return `typing.Self` instead of a custom `TypeVar`" - ) + let method_name = &self.method_name; + format!("Methods like `{method_name}` should return `Self` instead of a custom `TypeVar`") + } + + fn fix_title(&self) -> Option { + // See `replace_custom_typevar_with_self`'s doc comment + if self.in_stub { + Some("Replace with `Self`".to_string()) + } else { + None + } } } /// PYI019 pub(crate) fn custom_type_var_return_type( checker: &mut Checker, - name: &str, - decorator_list: &[Decorator], - returns: Option<&Expr>, - args: &Parameters, - type_params: Option<&TypeParams>, + function_def: &ast::StmtFunctionDef, ) { // Given, e.g., `def foo(self: _S, arg: bytes) -> _T`, extract `_T`. - let Some(returns) = returns else { + let Some(returns) = function_def.returns.as_ref() else { return; }; + let parameters = &*function_def.parameters; + // Given, e.g., `def foo(self: _S, arg: bytes)`, extract `_S`. - let Some(self_or_cls_annotation) = args + let Some(self_or_cls_annotation) = parameters .posonlyargs .iter() - .chain(&args.args) + .chain(¶meters.args) .next() .and_then(|parameter_with_default| parameter_with_default.parameter.annotation.as_ref()) else { return; }; + let decorator_list = &*function_def.decorator_list; + let semantic = checker.semantic(); // Skip any abstract, static, and overloaded methods. @@ -93,7 +114,7 @@ pub(crate) fn custom_type_var_return_type( } let method = match function_type::classify( - name, + &function_def.name, decorator_list, semantic.current_scope(), semantic, @@ -105,22 +126,17 @@ pub(crate) fn custom_type_var_return_type( FunctionType::ClassMethod => Method::Class(ClassMethod { cls_annotation: self_or_cls_annotation, returns, - type_params, + type_params: function_def.type_params.as_deref(), }), FunctionType::Method => Method::Instance(InstanceMethod { self_annotation: self_or_cls_annotation, returns, - type_params, + type_params: function_def.type_params.as_deref(), }), }; if method.uses_custom_var() { - checker.diagnostics.push(Diagnostic::new( - CustomTypeVarReturnType { - method_name: name.to_string(), - }, - returns.range(), - )); + add_diagnostic(checker, function_def, returns); } } @@ -147,8 +163,8 @@ struct ClassMethod<'a> { } impl<'a> ClassMethod<'a> { - /// Returns `true` if the class method is annotated with a custom `TypeVar` that is likely - /// private. + /// Returns `true` if the class method is annotated with + /// a custom `TypeVar` that is likely private. fn uses_custom_var(&self) -> bool { let Expr::Subscript(ast::ExprSubscript { slice, value, .. }) = self.cls_annotation else { return false; @@ -188,8 +204,8 @@ struct InstanceMethod<'a> { } impl<'a> InstanceMethod<'a> { - /// Returns `true` if the instance method is annotated with a custom `TypeVar` that is likely - /// private. + /// Returns `true` if the instance method is annotated with + /// a custom `TypeVar` that is likely private. fn uses_custom_var(&self) -> bool { let Expr::Name(ast::ExprName { id: first_arg_type, .. @@ -230,3 +246,181 @@ fn is_likely_private_typevar(type_var_name: &str, type_params: Option<&TypeParam }) }) } + +fn add_diagnostic(checker: &mut Checker, function_def: &ast::StmtFunctionDef, returns: &Expr) { + let in_stub = checker.source_type.is_stub(); + + let mut diagnostic = Diagnostic::new( + CustomTypeVarReturnType { + method_name: function_def.name.to_string(), + in_stub, + }, + returns.range(), + ); + + // See `replace_custom_typevar_with_self`'s doc comment + if in_stub { + if let Some(fix) = replace_custom_typevar_with_self(checker, function_def, returns) { + diagnostic.set_fix(fix); + } + } + + checker.diagnostics.push(diagnostic); +} + +/// Add a "Replace with `Self`" fix that does the following: +/// +/// * Import `Self` if necessary +/// * Remove the first parameter's annotation +/// * Replace the return annotation with `Self` +/// * Replace other uses of the original type variable elsewhere in the signature with `Self` +/// * Remove that type variable from the PEP 695 type parameter list +/// +/// This fix cannot be suggested for non-stubs, +/// as a non-stub fix would have to deal with references in body/at runtime as well, +/// which is substantially harder and requires a type-aware backend. +/// +/// The fourth step above has the same problem. +/// This function thus only does replacements for the simplest of cases +/// and will mark the fix as unsafe if an annotation cannot be handled. +fn replace_custom_typevar_with_self( + checker: &Checker, + function_def: &ast::StmtFunctionDef, + returns: &Expr, +) -> Option { + if checker.settings.preview.is_disabled() { + return None; + } + + // The return annotation is guaranteed to be a name, + // as verified by `uses_custom_var()`. + let typevar_name = returns.as_name_expr().unwrap().id(); + + let mut all_edits = vec![ + replace_return_annotation_with_self(returns), + remove_first_parameter_annotation(&function_def.parameters), + ]; + + let edit = import_self(checker, returns.range())?; + all_edits.push(edit); + + if let Some(edit) = + remove_typevar_declaration(function_def.type_params.as_deref(), typevar_name) + { + all_edits.push(edit); + } + + let (mut edits, fix_applicability) = + replace_typevar_usages_with_self(&function_def.parameters, typevar_name); + all_edits.append(&mut edits); + + let (first, rest) = (all_edits.swap_remove(0), all_edits); + + Some(Fix::applicable_edits(first, rest, fix_applicability)) +} + +fn import_self(checker: &Checker, return_range: TextRange) -> Option { + // From PYI034's fix + let target_version = checker.settings.target_version.as_tuple(); + let source_module = if target_version >= (3, 11) { + "typing" + } else { + "typing_extensions" + }; + + let (importer, semantic) = (checker.importer(), checker.semantic()); + let request = ImportRequest::import_from(source_module, "Self"); + + let position = return_range.start(); + let (edit, ..) = importer + .get_or_import_symbol(&request, position, semantic) + .ok()?; + + Some(edit) +} + +fn remove_first_parameter_annotation(parameters: &Parameters) -> Edit { + // The first parameter is guaranteed to be `self`/`cls`, + // as verified by `uses_custom_var()`. + let mut non_variadic_positional = parameters.posonlyargs.iter().chain(¶meters.args); + let first = &non_variadic_positional.next().unwrap().parameter; + + let name_end = first.name.range.end(); + let annotation_end = first.range.end(); + + Edit::deletion(name_end, annotation_end) +} + +fn replace_return_annotation_with_self(returns: &Expr) -> Edit { + Edit::range_replacement("Self".to_string(), returns.range()) +} + +fn replace_typevar_usages_with_self( + parameters: &Parameters, + typevar_name: &str, +) -> (Vec, Applicability) { + let mut edits = vec![]; + let mut could_not_handle_all_usages = false; + + for parameter in parameters.iter().skip(1) { + let Some(annotation) = parameter.annotation() else { + continue; + }; + let Expr::Name(name) = annotation else { + could_not_handle_all_usages = true; + continue; + }; + + if name.id.as_str() == typevar_name { + let edit = Edit::range_replacement("Self".to_string(), annotation.range()); + edits.push(edit); + } else { + could_not_handle_all_usages = true; + } + } + + if could_not_handle_all_usages { + (edits, Applicability::DisplayOnly) + } else { + (edits, Applicability::Safe) + } +} + +fn remove_typevar_declaration(type_params: Option<&TypeParams>, name: &str) -> Option { + let is_declaration_in_question = |type_param: &&TypeParam| -> bool { + if let TypeParam::TypeVar(typevar) = type_param { + return typevar.name.as_str() == name; + }; + + false + }; + + let parameter_list = type_params?; + let parameters = ¶meter_list.type_params; + let first = parameters.first()?; + + if parameter_list.len() == 1 && is_declaration_in_question(&first) { + return Some(Edit::range_deletion(parameter_list.range)); + } + + let (index, declaration) = parameters + .iter() + .find_position(is_declaration_in_question)?; + + let typevar_range = declaration.range(); + let last_index = parameters.len() - 1; + + let range = if index < last_index { + // [A, B, C] + // ^^^ Remove this + let next_range = parameters[index + 1].range(); + TextRange::new(typevar_range.start(), next_range.start()) + } else { + // [A, B, C] + // ^^^ Remove this + let previous_range = parameters[index - 1].range(); + TextRange::new(previous_range.end(), typevar_range.start()) + }; + + Some(Edit::range_deletion(range)) +} 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 a0b772e8b51e3..eb67537f77e25 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 @@ -200,10 +200,9 @@ fn add_diagnostic( let (importer, semantic) = (checker.importer(), checker.semantic()); let request = ImportRequest::import_from(source_module, "Self"); - let Ok((edit, ..)) = importer.get_or_import_symbol(&request, range.start(), semantic) - else { - return None; - }; + let (edit, ..) = importer + .get_or_import_symbol(&request, range.start(), semantic) + .ok()?; Some(edit) } diff --git a/crates/ruff_linter/src/rules/flake8_pyi/snapshots/ruff_linter__rules__flake8_pyi__tests__PYI019_PYI019.py.snap b/crates/ruff_linter/src/rules/flake8_pyi/snapshots/ruff_linter__rules__flake8_pyi__tests__PYI019_PYI019.py.snap index eaa6a4bcc533b..d073dea248930 100644 --- a/crates/ruff_linter/src/rules/flake8_pyi/snapshots/ruff_linter__rules__flake8_pyi__tests__PYI019_PYI019.py.snap +++ b/crates/ruff_linter/src/rules/flake8_pyi/snapshots/ruff_linter__rules__flake8_pyi__tests__PYI019_PYI019.py.snap @@ -1,34 +1,35 @@ --- source: crates/ruff_linter/src/rules/flake8_pyi/mod.rs +snapshot_kind: text --- -PYI019.py:7:62: PYI019 Methods like `__new__` should return `typing.Self` instead of a custom `TypeVar` +PYI019.py:7:62: PYI019 Methods like `__new__` should return `Self` instead of a custom `TypeVar` | 6 | class BadClass: 7 | def __new__(cls: type[_S], *args: str, **kwargs: int) -> _S: ... # PYI019 | ^^ PYI019 | -PYI019.py:10:54: PYI019 Methods like `bad_instance_method` should return `typing.Self` instead of a custom `TypeVar` +PYI019.py:10:54: PYI019 Methods like `bad_instance_method` should return `Self` instead of a custom `TypeVar` | 10 | def bad_instance_method(self: _S, arg: bytes) -> _S: ... # PYI019 | ^^ PYI019 | -PYI019.py:14:54: PYI019 Methods like `bad_class_method` should return `typing.Self` instead of a custom `TypeVar` +PYI019.py:14:54: PYI019 Methods like `bad_class_method` should return `Self` instead of a custom `TypeVar` | 13 | @classmethod 14 | def bad_class_method(cls: type[_S], arg: int) -> _S: ... # PYI019 | ^^ PYI019 | -PYI019.py:18:55: PYI019 Methods like `bad_posonly_class_method` should return `typing.Self` instead of a custom `TypeVar` +PYI019.py:18:55: PYI019 Methods like `bad_posonly_class_method` should return `Self` instead of a custom `TypeVar` | 17 | @classmethod 18 | def bad_posonly_class_method(cls: type[_S], /) -> _S: ... # PYI019 | ^^ PYI019 | -PYI019.py:39:63: PYI019 Methods like `__new__` should return `typing.Self` instead of a custom `TypeVar` +PYI019.py:39:63: PYI019 Methods like `__new__` should return `Self` instead of a custom `TypeVar` | 37 | # Python > 3.12 38 | class PEP695BadDunderNew[T]: @@ -36,16 +37,152 @@ PYI019.py:39:63: PYI019 Methods like `__new__` should return `typing.Self` inste | ^ PYI019 | -PYI019.py:42:46: PYI019 Methods like `generic_instance_method` should return `typing.Self` instead of a custom `TypeVar` +PYI019.py:42:46: PYI019 Methods like `generic_instance_method` should return `Self` instead of a custom `TypeVar` | 42 | def generic_instance_method[S](self: S) -> S: ... # PYI019 | ^ PYI019 | -PYI019.py:54:32: PYI019 Methods like `foo` should return `typing.Self` instead of a custom `TypeVar` +PYI019.py:54:32: PYI019 Methods like `foo` should return `Self` instead of a custom `TypeVar` | 52 | # in the settings for this test: 53 | @foo_classmethod 54 | def foo[S](cls: type[S]) -> S: ... # PYI019 | ^ PYI019 | + +PYI019.py:61:48: PYI019 Methods like `__new__` should return `Self` instead of a custom `TypeVar` + | +59 | # Only .pyi gets fixes, no fixes for .py +60 | class PEP695Fix: +61 | def __new__[S: PEP695Fix](cls: type[S]) -> S: ... + | ^ PYI019 +62 | +63 | def __init_subclass__[S](cls: type[S]) -> S: ... + | + +PYI019.py:63:47: PYI019 Methods like `__init_subclass__` should return `Self` instead of a custom `TypeVar` + | +61 | def __new__[S: PEP695Fix](cls: type[S]) -> S: ... +62 | +63 | def __init_subclass__[S](cls: type[S]) -> S: ... + | ^ PYI019 +64 | +65 | def __neg__[S: PEP695Fix](self: S) -> S: ... + | + +PYI019.py:65:43: PYI019 Methods like `__neg__` should return `Self` instead of a custom `TypeVar` + | +63 | def __init_subclass__[S](cls: type[S]) -> S: ... +64 | +65 | def __neg__[S: PEP695Fix](self: S) -> S: ... + | ^ PYI019 +66 | +67 | def __pos__[S](self: S) -> S: ... + | + +PYI019.py:67:32: PYI019 Methods like `__pos__` should return `Self` instead of a custom `TypeVar` + | +65 | def __neg__[S: PEP695Fix](self: S) -> S: ... +66 | +67 | def __pos__[S](self: S) -> S: ... + | ^ PYI019 +68 | +69 | def __add__[S: PEP695Fix](self: S, other: S) -> S: ... + | + +PYI019.py:69:53: PYI019 Methods like `__add__` should return `Self` instead of a custom `TypeVar` + | +67 | def __pos__[S](self: S) -> S: ... +68 | +69 | def __add__[S: PEP695Fix](self: S, other: S) -> S: ... + | ^ PYI019 +70 | +71 | def __sub__[S](self: S, other: S) -> S: ... + | + +PYI019.py:71:42: PYI019 Methods like `__sub__` should return `Self` instead of a custom `TypeVar` + | +69 | def __add__[S: PEP695Fix](self: S, other: S) -> S: ... +70 | +71 | def __sub__[S](self: S, other: S) -> S: ... + | ^ PYI019 +72 | +73 | @classmethod + | + +PYI019.py:74:59: PYI019 Methods like `class_method_bound` should return `Self` instead of a custom `TypeVar` + | +73 | @classmethod +74 | def class_method_bound[S: PEP695Fix](cls: type[S]) -> S: ... + | ^ PYI019 +75 | +76 | @classmethod + | + +PYI019.py:77:50: PYI019 Methods like `class_method_unbound` should return `Self` instead of a custom `TypeVar` + | +76 | @classmethod +77 | def class_method_unbound[S](cls: type[S]) -> S: ... + | ^ PYI019 +78 | +79 | def instance_method_bound[S: PEP695Fix](self: S) -> S: ... + | + +PYI019.py:79:57: PYI019 Methods like `instance_method_bound` should return `Self` instead of a custom `TypeVar` + | +77 | def class_method_unbound[S](cls: type[S]) -> S: ... +78 | +79 | def instance_method_bound[S: PEP695Fix](self: S) -> S: ... + | ^ PYI019 +80 | +81 | def instance_method_unbound[S](self: S) -> S: ... + | + +PYI019.py:81:48: PYI019 Methods like `instance_method_unbound` should return `Self` instead of a custom `TypeVar` + | +79 | def instance_method_bound[S: PEP695Fix](self: S) -> S: ... +80 | +81 | def instance_method_unbound[S](self: S) -> S: ... + | ^ PYI019 +82 | +83 | def instance_method_bound_with_another_parameter[S: PEP695Fix](self: S, other: S) -> S: ... + | + +PYI019.py:83:90: PYI019 Methods like `instance_method_bound_with_another_parameter` should return `Self` instead of a custom `TypeVar` + | +81 | def instance_method_unbound[S](self: S) -> S: ... +82 | +83 | def instance_method_bound_with_another_parameter[S: PEP695Fix](self: S, other: S) -> S: ... + | ^ PYI019 +84 | +85 | def instance_method_unbound_with_another_parameter[S](self: S, other: S) -> S: ... + | + +PYI019.py:85:81: PYI019 Methods like `instance_method_unbound_with_another_parameter` should return `Self` instead of a custom `TypeVar` + | +83 | def instance_method_bound_with_another_parameter[S: PEP695Fix](self: S, other: S) -> S: ... +84 | +85 | def instance_method_unbound_with_another_parameter[S](self: S, other: S) -> S: ... + | ^ PYI019 +86 | +87 | def multiple_type_vars[S, *Ts, T](self: S, other: S, /, *args: *Ts, a: T, b: list[T]) -> S: ... + | + +PYI019.py:87:94: PYI019 Methods like `multiple_type_vars` should return `Self` instead of a custom `TypeVar` + | +85 | def instance_method_unbound_with_another_parameter[S](self: S, other: S) -> S: ... +86 | +87 | def multiple_type_vars[S, *Ts, T](self: S, other: S, /, *args: *Ts, a: T, b: list[T]) -> S: ... + | ^ PYI019 +88 | +89 | def mixing_old_and_new_style_type_vars[T](self: _S695, a: T, b: T) -> _S695: ... + | + +PYI019.py:89:75: PYI019 Methods like `mixing_old_and_new_style_type_vars` should return `Self` instead of a custom `TypeVar` + | +87 | def multiple_type_vars[S, *Ts, T](self: S, other: S, /, *args: *Ts, a: T, b: list[T]) -> S: ... +88 | +89 | def mixing_old_and_new_style_type_vars[T](self: _S695, a: T, b: T) -> _S695: ... + | ^^^^^ PYI019 + | diff --git a/crates/ruff_linter/src/rules/flake8_pyi/snapshots/ruff_linter__rules__flake8_pyi__tests__PYI019_PYI019.pyi.snap b/crates/ruff_linter/src/rules/flake8_pyi/snapshots/ruff_linter__rules__flake8_pyi__tests__PYI019_PYI019.pyi.snap index b3ddd0801d1f1..0fba8ea76bacf 100644 --- a/crates/ruff_linter/src/rules/flake8_pyi/snapshots/ruff_linter__rules__flake8_pyi__tests__PYI019_PYI019.pyi.snap +++ b/crates/ruff_linter/src/rules/flake8_pyi/snapshots/ruff_linter__rules__flake8_pyi__tests__PYI019_PYI019.pyi.snap @@ -1,51 +1,208 @@ --- source: crates/ruff_linter/src/rules/flake8_pyi/mod.rs --- -PYI019.pyi:7:62: PYI019 Methods like `__new__` should return `typing.Self` instead of a custom `TypeVar` +PYI019.pyi:7:62: PYI019 Methods like `__new__` should return `Self` instead of a custom `TypeVar` | 6 | class BadClass: 7 | def __new__(cls: type[_S], *args: str, **kwargs: int) -> _S: ... # PYI019 | ^^ PYI019 | + = help: Replace with `Self` -PYI019.pyi:10:54: PYI019 Methods like `bad_instance_method` should return `typing.Self` instead of a custom `TypeVar` +PYI019.pyi:10:54: PYI019 Methods like `bad_instance_method` should return `Self` instead of a custom `TypeVar` | 10 | def bad_instance_method(self: _S, arg: bytes) -> _S: ... # PYI019 | ^^ PYI019 | + = help: Replace with `Self` -PYI019.pyi:14:54: PYI019 Methods like `bad_class_method` should return `typing.Self` instead of a custom `TypeVar` +PYI019.pyi:14:54: PYI019 Methods like `bad_class_method` should return `Self` instead of a custom `TypeVar` | 13 | @classmethod 14 | def bad_class_method(cls: type[_S], arg: int) -> _S: ... # PYI019 | ^^ PYI019 | + = help: Replace with `Self` -PYI019.pyi:18:55: PYI019 Methods like `bad_posonly_class_method` should return `typing.Self` instead of a custom `TypeVar` +PYI019.pyi:18:55: PYI019 Methods like `bad_posonly_class_method` should return `Self` instead of a custom `TypeVar` | 17 | @classmethod 18 | def bad_posonly_class_method(cls: type[_S], /) -> _S: ... # PYI019 | ^^ PYI019 | + = help: Replace with `Self` -PYI019.pyi:39:63: PYI019 Methods like `__new__` should return `typing.Self` instead of a custom `TypeVar` +PYI019.pyi:39:63: PYI019 Methods like `__new__` should return `Self` instead of a custom `TypeVar` | 37 | # Python > 3.12 38 | class PEP695BadDunderNew[T]: 39 | def __new__[S](cls: type[S], *args: Any, ** kwargs: Any) -> S: ... # PYI019 | ^ PYI019 | + = help: Replace with `Self` -PYI019.pyi:42:46: PYI019 Methods like `generic_instance_method` should return `typing.Self` instead of a custom `TypeVar` +PYI019.pyi:42:46: PYI019 Methods like `generic_instance_method` should return `Self` instead of a custom `TypeVar` | 42 | def generic_instance_method[S](self: S) -> S: ... # PYI019 | ^ PYI019 | + = help: Replace with `Self` -PYI019.pyi:54:32: PYI019 Methods like `foo` should return `typing.Self` instead of a custom `TypeVar` +PYI019.pyi:54:32: PYI019 Methods like `foo` should return `Self` instead of a custom `TypeVar` | 52 | # in the settings for this test: 53 | @foo_classmethod 54 | def foo[S](cls: type[S]) -> S: ... # PYI019 | ^ PYI019 | + = help: Replace with `Self` + +PYI019.pyi:61:48: PYI019 Methods like `__new__` should return `Self` instead of a custom `TypeVar` + | +59 | # Only .pyi gets fixes, no fixes for .py +60 | class PEP695Fix: +61 | def __new__[S: PEP695Fix](cls: type[S]) -> S: ... + | ^ PYI019 +62 | +63 | def __init_subclass__[S](cls: type[S]) -> S: ... + | + = help: Replace with `Self` + +PYI019.pyi:63:47: PYI019 Methods like `__init_subclass__` should return `Self` instead of a custom `TypeVar` + | +61 | def __new__[S: PEP695Fix](cls: type[S]) -> S: ... +62 | +63 | def __init_subclass__[S](cls: type[S]) -> S: ... + | ^ PYI019 +64 | +65 | def __neg__[S: PEP695Fix](self: S) -> S: ... + | + = help: Replace with `Self` + +PYI019.pyi:65:43: PYI019 Methods like `__neg__` should return `Self` instead of a custom `TypeVar` + | +63 | def __init_subclass__[S](cls: type[S]) -> S: ... +64 | +65 | def __neg__[S: PEP695Fix](self: S) -> S: ... + | ^ PYI019 +66 | +67 | def __pos__[S](self: S) -> S: ... + | + = help: Replace with `Self` + +PYI019.pyi:67:32: PYI019 Methods like `__pos__` should return `Self` instead of a custom `TypeVar` + | +65 | def __neg__[S: PEP695Fix](self: S) -> S: ... +66 | +67 | def __pos__[S](self: S) -> S: ... + | ^ PYI019 +68 | +69 | def __add__[S: PEP695Fix](self: S, other: S) -> S: ... + | + = help: Replace with `Self` + +PYI019.pyi:69:53: PYI019 Methods like `__add__` should return `Self` instead of a custom `TypeVar` + | +67 | def __pos__[S](self: S) -> S: ... +68 | +69 | def __add__[S: PEP695Fix](self: S, other: S) -> S: ... + | ^ PYI019 +70 | +71 | def __sub__[S](self: S, other: S) -> S: ... + | + = help: Replace with `Self` + +PYI019.pyi:71:42: PYI019 Methods like `__sub__` should return `Self` instead of a custom `TypeVar` + | +69 | def __add__[S: PEP695Fix](self: S, other: S) -> S: ... +70 | +71 | def __sub__[S](self: S, other: S) -> S: ... + | ^ PYI019 +72 | +73 | @classmethod + | + = help: Replace with `Self` + +PYI019.pyi:74:59: PYI019 Methods like `class_method_bound` should return `Self` instead of a custom `TypeVar` + | +73 | @classmethod +74 | def class_method_bound[S: PEP695Fix](cls: type[S]) -> S: ... + | ^ PYI019 +75 | +76 | @classmethod + | + = help: Replace with `Self` + +PYI019.pyi:77:50: PYI019 Methods like `class_method_unbound` should return `Self` instead of a custom `TypeVar` + | +76 | @classmethod +77 | def class_method_unbound[S](cls: type[S]) -> S: ... + | ^ PYI019 +78 | +79 | def instance_method_bound[S: PEP695Fix](self: S) -> S: ... + | + = help: Replace with `Self` + +PYI019.pyi:79:57: PYI019 Methods like `instance_method_bound` should return `Self` instead of a custom `TypeVar` + | +77 | def class_method_unbound[S](cls: type[S]) -> S: ... +78 | +79 | def instance_method_bound[S: PEP695Fix](self: S) -> S: ... + | ^ PYI019 +80 | +81 | def instance_method_unbound[S](self: S) -> S: ... + | + = help: Replace with `Self` + +PYI019.pyi:81:48: PYI019 Methods like `instance_method_unbound` should return `Self` instead of a custom `TypeVar` + | +79 | def instance_method_bound[S: PEP695Fix](self: S) -> S: ... +80 | +81 | def instance_method_unbound[S](self: S) -> S: ... + | ^ PYI019 +82 | +83 | def instance_method_bound_with_another_parameter[S: PEP695Fix](self: S, other: S) -> S: ... + | + = help: Replace with `Self` + +PYI019.pyi:83:90: PYI019 Methods like `instance_method_bound_with_another_parameter` should return `Self` instead of a custom `TypeVar` + | +81 | def instance_method_unbound[S](self: S) -> S: ... +82 | +83 | def instance_method_bound_with_another_parameter[S: PEP695Fix](self: S, other: S) -> S: ... + | ^ PYI019 +84 | +85 | def instance_method_unbound_with_another_parameter[S](self: S, other: S) -> S: ... + | + = help: Replace with `Self` + +PYI019.pyi:85:81: PYI019 Methods like `instance_method_unbound_with_another_parameter` should return `Self` instead of a custom `TypeVar` + | +83 | def instance_method_bound_with_another_parameter[S: PEP695Fix](self: S, other: S) -> S: ... +84 | +85 | def instance_method_unbound_with_another_parameter[S](self: S, other: S) -> S: ... + | ^ PYI019 +86 | +87 | def multiple_type_vars[S, *Ts, T](self: S, other: S, /, *args: *Ts, a: T, b: list[T]) -> S: ... + | + = help: Replace with `Self` + +PYI019.pyi:87:94: PYI019 Methods like `multiple_type_vars` should return `Self` instead of a custom `TypeVar` + | +85 | def instance_method_unbound_with_another_parameter[S](self: S, other: S) -> S: ... +86 | +87 | def multiple_type_vars[S, *Ts, T](self: S, other: S, /, *args: *Ts, a: T, b: list[T]) -> S: ... + | ^ PYI019 +88 | +89 | def mixing_old_and_new_style_type_vars[T](self: _S695, a: T, b: T) -> _S695: ... + | + = help: Replace with `Self` + +PYI019.pyi:89:75: PYI019 Methods like `mixing_old_and_new_style_type_vars` should return `Self` instead of a custom `TypeVar` + | +87 | def multiple_type_vars[S, *Ts, T](self: S, other: S, /, *args: *Ts, a: T, b: list[T]) -> S: ... +88 | +89 | def mixing_old_and_new_style_type_vars[T](self: _S695, a: T, b: T) -> _S695: ... + | ^^^^^ PYI019 + | + = help: Replace with `Self` diff --git a/crates/ruff_linter/src/rules/flake8_pyi/snapshots/ruff_linter__rules__flake8_pyi__tests__custom_classmethod_rules_preview.snap b/crates/ruff_linter/src/rules/flake8_pyi/snapshots/ruff_linter__rules__flake8_pyi__tests__custom_classmethod_rules_preview.snap new file mode 100644 index 0000000000000..db2592f12e34c --- /dev/null +++ b/crates/ruff_linter/src/rules/flake8_pyi/snapshots/ruff_linter__rules__flake8_pyi__tests__custom_classmethod_rules_preview.snap @@ -0,0 +1,414 @@ +--- +source: crates/ruff_linter/src/rules/flake8_pyi/mod.rs +--- +PYI019.pyi:7:62: PYI019 Methods like `__new__` should return `Self` instead of a custom `TypeVar` + | +6 | class BadClass: +7 | def __new__(cls: type[_S], *args: str, **kwargs: int) -> _S: ... # PYI019 + | ^^ PYI019 + | + = help: Replace with `Self` + +ℹ Display-only fix +4 4 | _S2 = TypeVar("_S2", BadClass, GoodClass) +5 5 | +6 6 | class BadClass: +7 |- def __new__(cls: type[_S], *args: str, **kwargs: int) -> _S: ... # PYI019 + 7 |+ def __new__(cls, *args: str, **kwargs: int) -> Self: ... # PYI019 +8 8 | +9 9 | +10 10 | def bad_instance_method(self: _S, arg: bytes) -> _S: ... # PYI019 + +PYI019.pyi:10:54: PYI019 Methods like `bad_instance_method` should return `Self` instead of a custom `TypeVar` + | +10 | def bad_instance_method(self: _S, arg: bytes) -> _S: ... # PYI019 + | ^^ PYI019 + | + = help: Replace with `Self` + +ℹ Display-only fix +7 7 | def __new__(cls: type[_S], *args: str, **kwargs: int) -> _S: ... # PYI019 +8 8 | +9 9 | +10 |- def bad_instance_method(self: _S, arg: bytes) -> _S: ... # PYI019 + 10 |+ def bad_instance_method(self, arg: bytes) -> Self: ... # PYI019 +11 11 | +12 12 | +13 13 | @classmethod + +PYI019.pyi:14:54: PYI019 Methods like `bad_class_method` should return `Self` instead of a custom `TypeVar` + | +13 | @classmethod +14 | def bad_class_method(cls: type[_S], arg: int) -> _S: ... # PYI019 + | ^^ PYI019 + | + = help: Replace with `Self` + +ℹ Display-only fix +11 11 | +12 12 | +13 13 | @classmethod +14 |- def bad_class_method(cls: type[_S], arg: int) -> _S: ... # PYI019 + 14 |+ def bad_class_method(cls, arg: int) -> Self: ... # PYI019 +15 15 | +16 16 | +17 17 | @classmethod + +PYI019.pyi:18:55: PYI019 [*] Methods like `bad_posonly_class_method` should return `Self` instead of a custom `TypeVar` + | +17 | @classmethod +18 | def bad_posonly_class_method(cls: type[_S], /) -> _S: ... # PYI019 + | ^^ PYI019 + | + = help: Replace with `Self` + +ℹ Safe fix +15 15 | +16 16 | +17 17 | @classmethod +18 |- def bad_posonly_class_method(cls: type[_S], /) -> _S: ... # PYI019 + 18 |+ def bad_posonly_class_method(cls, /) -> Self: ... # PYI019 +19 19 | +20 20 | +21 21 | @classmethod + +PYI019.pyi:39:63: PYI019 Methods like `__new__` should return `Self` instead of a custom `TypeVar` + | +37 | # Python > 3.12 +38 | class PEP695BadDunderNew[T]: +39 | def __new__[S](cls: type[S], *args: Any, ** kwargs: Any) -> S: ... # PYI019 + | ^ PYI019 + | + = help: Replace with `Self` + +ℹ Display-only fix +36 36 | +37 37 | # Python > 3.12 +38 38 | class PEP695BadDunderNew[T]: +39 |- def __new__[S](cls: type[S], *args: Any, ** kwargs: Any) -> S: ... # PYI019 + 39 |+ def __new__(cls, *args: Any, ** kwargs: Any) -> Self: ... # PYI019 +40 40 | +41 41 | +42 42 | def generic_instance_method[S](self: S) -> S: ... # PYI019 + +PYI019.pyi:42:46: PYI019 [*] Methods like `generic_instance_method` should return `Self` instead of a custom `TypeVar` + | +42 | def generic_instance_method[S](self: S) -> S: ... # PYI019 + | ^ PYI019 + | + = help: Replace with `Self` + +ℹ Safe fix +39 39 | def __new__[S](cls: type[S], *args: Any, ** kwargs: Any) -> S: ... # PYI019 +40 40 | +41 41 | +42 |- def generic_instance_method[S](self: S) -> S: ... # PYI019 + 42 |+ def generic_instance_method(self) -> Self: ... # PYI019 +43 43 | +44 44 | +45 45 | class PEP695GoodDunderNew[T]: + +PYI019.pyi:54:32: PYI019 [*] Methods like `foo` should return `Self` instead of a custom `TypeVar` + | +52 | # in the settings for this test: +53 | @foo_classmethod +54 | def foo[S](cls: type[S]) -> S: ... # PYI019 + | ^ PYI019 + | + = help: Replace with `Self` + +ℹ Safe fix +51 51 | # due to `foo_classmethod being listed in `pep8_naming.classmethod-decorators` +52 52 | # in the settings for this test: +53 53 | @foo_classmethod +54 |- def foo[S](cls: type[S]) -> S: ... # PYI019 + 54 |+ def foo(cls) -> Self: ... # PYI019 +55 55 | +56 56 | +57 57 | _S695 = TypeVar("_S695", bound="PEP695Fix") + +PYI019.pyi:61:48: PYI019 [*] Methods like `__new__` should return `Self` instead of a custom `TypeVar` + | +59 | # Only .pyi gets fixes, no fixes for .py +60 | class PEP695Fix: +61 | def __new__[S: PEP695Fix](cls: type[S]) -> S: ... + | ^ PYI019 +62 | +63 | def __init_subclass__[S](cls: type[S]) -> S: ... + | + = help: Replace with `Self` + +ℹ Safe fix +58 58 | +59 59 | # Only .pyi gets fixes, no fixes for .py +60 60 | class PEP695Fix: +61 |- def __new__[S: PEP695Fix](cls: type[S]) -> S: ... + 61 |+ def __new__(cls) -> Self: ... +62 62 | +63 63 | def __init_subclass__[S](cls: type[S]) -> S: ... +64 64 | + +PYI019.pyi:63:47: PYI019 [*] Methods like `__init_subclass__` should return `Self` instead of a custom `TypeVar` + | +61 | def __new__[S: PEP695Fix](cls: type[S]) -> S: ... +62 | +63 | def __init_subclass__[S](cls: type[S]) -> S: ... + | ^ PYI019 +64 | +65 | def __neg__[S: PEP695Fix](self: S) -> S: ... + | + = help: Replace with `Self` + +ℹ Safe fix +60 60 | class PEP695Fix: +61 61 | def __new__[S: PEP695Fix](cls: type[S]) -> S: ... +62 62 | +63 |- def __init_subclass__[S](cls: type[S]) -> S: ... + 63 |+ def __init_subclass__(cls) -> Self: ... +64 64 | +65 65 | def __neg__[S: PEP695Fix](self: S) -> S: ... +66 66 | + +PYI019.pyi:65:43: PYI019 [*] Methods like `__neg__` should return `Self` instead of a custom `TypeVar` + | +63 | def __init_subclass__[S](cls: type[S]) -> S: ... +64 | +65 | def __neg__[S: PEP695Fix](self: S) -> S: ... + | ^ PYI019 +66 | +67 | def __pos__[S](self: S) -> S: ... + | + = help: Replace with `Self` + +ℹ Safe fix +62 62 | +63 63 | def __init_subclass__[S](cls: type[S]) -> S: ... +64 64 | +65 |- def __neg__[S: PEP695Fix](self: S) -> S: ... + 65 |+ def __neg__(self) -> Self: ... +66 66 | +67 67 | def __pos__[S](self: S) -> S: ... +68 68 | + +PYI019.pyi:67:32: PYI019 [*] Methods like `__pos__` should return `Self` instead of a custom `TypeVar` + | +65 | def __neg__[S: PEP695Fix](self: S) -> S: ... +66 | +67 | def __pos__[S](self: S) -> S: ... + | ^ PYI019 +68 | +69 | def __add__[S: PEP695Fix](self: S, other: S) -> S: ... + | + = help: Replace with `Self` + +ℹ Safe fix +64 64 | +65 65 | def __neg__[S: PEP695Fix](self: S) -> S: ... +66 66 | +67 |- def __pos__[S](self: S) -> S: ... + 67 |+ def __pos__(self) -> Self: ... +68 68 | +69 69 | def __add__[S: PEP695Fix](self: S, other: S) -> S: ... +70 70 | + +PYI019.pyi:69:53: PYI019 [*] Methods like `__add__` should return `Self` instead of a custom `TypeVar` + | +67 | def __pos__[S](self: S) -> S: ... +68 | +69 | def __add__[S: PEP695Fix](self: S, other: S) -> S: ... + | ^ PYI019 +70 | +71 | def __sub__[S](self: S, other: S) -> S: ... + | + = help: Replace with `Self` + +ℹ Safe fix +66 66 | +67 67 | def __pos__[S](self: S) -> S: ... +68 68 | +69 |- def __add__[S: PEP695Fix](self: S, other: S) -> S: ... + 69 |+ def __add__(self, other: Self) -> Self: ... +70 70 | +71 71 | def __sub__[S](self: S, other: S) -> S: ... +72 72 | + +PYI019.pyi:71:42: PYI019 [*] Methods like `__sub__` should return `Self` instead of a custom `TypeVar` + | +69 | def __add__[S: PEP695Fix](self: S, other: S) -> S: ... +70 | +71 | def __sub__[S](self: S, other: S) -> S: ... + | ^ PYI019 +72 | +73 | @classmethod + | + = help: Replace with `Self` + +ℹ Safe fix +68 68 | +69 69 | def __add__[S: PEP695Fix](self: S, other: S) -> S: ... +70 70 | +71 |- def __sub__[S](self: S, other: S) -> S: ... + 71 |+ def __sub__(self, other: Self) -> Self: ... +72 72 | +73 73 | @classmethod +74 74 | def class_method_bound[S: PEP695Fix](cls: type[S]) -> S: ... + +PYI019.pyi:74:59: PYI019 [*] Methods like `class_method_bound` should return `Self` instead of a custom `TypeVar` + | +73 | @classmethod +74 | def class_method_bound[S: PEP695Fix](cls: type[S]) -> S: ... + | ^ PYI019 +75 | +76 | @classmethod + | + = help: Replace with `Self` + +ℹ Safe fix +71 71 | def __sub__[S](self: S, other: S) -> S: ... +72 72 | +73 73 | @classmethod +74 |- def class_method_bound[S: PEP695Fix](cls: type[S]) -> S: ... + 74 |+ def class_method_bound(cls) -> Self: ... +75 75 | +76 76 | @classmethod +77 77 | def class_method_unbound[S](cls: type[S]) -> S: ... + +PYI019.pyi:77:50: PYI019 [*] Methods like `class_method_unbound` should return `Self` instead of a custom `TypeVar` + | +76 | @classmethod +77 | def class_method_unbound[S](cls: type[S]) -> S: ... + | ^ PYI019 +78 | +79 | def instance_method_bound[S: PEP695Fix](self: S) -> S: ... + | + = help: Replace with `Self` + +ℹ Safe fix +74 74 | def class_method_bound[S: PEP695Fix](cls: type[S]) -> S: ... +75 75 | +76 76 | @classmethod +77 |- def class_method_unbound[S](cls: type[S]) -> S: ... + 77 |+ def class_method_unbound(cls) -> Self: ... +78 78 | +79 79 | def instance_method_bound[S: PEP695Fix](self: S) -> S: ... +80 80 | + +PYI019.pyi:79:57: PYI019 [*] Methods like `instance_method_bound` should return `Self` instead of a custom `TypeVar` + | +77 | def class_method_unbound[S](cls: type[S]) -> S: ... +78 | +79 | def instance_method_bound[S: PEP695Fix](self: S) -> S: ... + | ^ PYI019 +80 | +81 | def instance_method_unbound[S](self: S) -> S: ... + | + = help: Replace with `Self` + +ℹ Safe fix +76 76 | @classmethod +77 77 | def class_method_unbound[S](cls: type[S]) -> S: ... +78 78 | +79 |- def instance_method_bound[S: PEP695Fix](self: S) -> S: ... + 79 |+ def instance_method_bound(self) -> Self: ... +80 80 | +81 81 | def instance_method_unbound[S](self: S) -> S: ... +82 82 | + +PYI019.pyi:81:48: PYI019 [*] Methods like `instance_method_unbound` should return `Self` instead of a custom `TypeVar` + | +79 | def instance_method_bound[S: PEP695Fix](self: S) -> S: ... +80 | +81 | def instance_method_unbound[S](self: S) -> S: ... + | ^ PYI019 +82 | +83 | def instance_method_bound_with_another_parameter[S: PEP695Fix](self: S, other: S) -> S: ... + | + = help: Replace with `Self` + +ℹ Safe fix +78 78 | +79 79 | def instance_method_bound[S: PEP695Fix](self: S) -> S: ... +80 80 | +81 |- def instance_method_unbound[S](self: S) -> S: ... + 81 |+ def instance_method_unbound(self) -> Self: ... +82 82 | +83 83 | def instance_method_bound_with_another_parameter[S: PEP695Fix](self: S, other: S) -> S: ... +84 84 | + +PYI019.pyi:83:90: PYI019 [*] Methods like `instance_method_bound_with_another_parameter` should return `Self` instead of a custom `TypeVar` + | +81 | def instance_method_unbound[S](self: S) -> S: ... +82 | +83 | def instance_method_bound_with_another_parameter[S: PEP695Fix](self: S, other: S) -> S: ... + | ^ PYI019 +84 | +85 | def instance_method_unbound_with_another_parameter[S](self: S, other: S) -> S: ... + | + = help: Replace with `Self` + +ℹ Safe fix +80 80 | +81 81 | def instance_method_unbound[S](self: S) -> S: ... +82 82 | +83 |- def instance_method_bound_with_another_parameter[S: PEP695Fix](self: S, other: S) -> S: ... + 83 |+ def instance_method_bound_with_another_parameter(self, other: Self) -> Self: ... +84 84 | +85 85 | def instance_method_unbound_with_another_parameter[S](self: S, other: S) -> S: ... +86 86 | + +PYI019.pyi:85:81: PYI019 [*] Methods like `instance_method_unbound_with_another_parameter` should return `Self` instead of a custom `TypeVar` + | +83 | def instance_method_bound_with_another_parameter[S: PEP695Fix](self: S, other: S) -> S: ... +84 | +85 | def instance_method_unbound_with_another_parameter[S](self: S, other: S) -> S: ... + | ^ PYI019 +86 | +87 | def multiple_type_vars[S, *Ts, T](self: S, other: S, /, *args: *Ts, a: T, b: list[T]) -> S: ... + | + = help: Replace with `Self` + +ℹ Safe fix +82 82 | +83 83 | def instance_method_bound_with_another_parameter[S: PEP695Fix](self: S, other: S) -> S: ... +84 84 | +85 |- def instance_method_unbound_with_another_parameter[S](self: S, other: S) -> S: ... + 85 |+ def instance_method_unbound_with_another_parameter(self, other: Self) -> Self: ... +86 86 | +87 87 | def multiple_type_vars[S, *Ts, T](self: S, other: S, /, *args: *Ts, a: T, b: list[T]) -> S: ... +88 88 | + +PYI019.pyi:87:94: PYI019 Methods like `multiple_type_vars` should return `Self` instead of a custom `TypeVar` + | +85 | def instance_method_unbound_with_another_parameter[S](self: S, other: S) -> S: ... +86 | +87 | def multiple_type_vars[S, *Ts, T](self: S, other: S, /, *args: *Ts, a: T, b: list[T]) -> S: ... + | ^ PYI019 +88 | +89 | def mixing_old_and_new_style_type_vars[T](self: _S695, a: T, b: T) -> _S695: ... + | + = help: Replace with `Self` + +ℹ Display-only fix +84 84 | +85 85 | def instance_method_unbound_with_another_parameter[S](self: S, other: S) -> S: ... +86 86 | +87 |- def multiple_type_vars[S, *Ts, T](self: S, other: S, /, *args: *Ts, a: T, b: list[T]) -> S: ... + 87 |+ def multiple_type_vars[*Ts, T](self, other: Self, /, *args: *Ts, a: T, b: list[T]) -> Self: ... +88 88 | +89 89 | def mixing_old_and_new_style_type_vars[T](self: _S695, a: T, b: T) -> _S695: ... + +PYI019.pyi:89:75: PYI019 Methods like `mixing_old_and_new_style_type_vars` should return `Self` instead of a custom `TypeVar` + | +87 | def multiple_type_vars[S, *Ts, T](self: S, other: S, /, *args: *Ts, a: T, b: list[T]) -> S: ... +88 | +89 | def mixing_old_and_new_style_type_vars[T](self: _S695, a: T, b: T) -> _S695: ... + | ^^^^^ PYI019 + | + = help: Replace with `Self` + +ℹ Display-only fix +86 86 | +87 87 | def multiple_type_vars[S, *Ts, T](self: S, other: S, /, *args: *Ts, a: T, b: list[T]) -> S: ... +88 88 | +89 |- def mixing_old_and_new_style_type_vars[T](self: _S695, a: T, b: T) -> _S695: ... + 89 |+ def mixing_old_and_new_style_type_vars[T](self, a: T, b: T) -> Self: ...