mirror of
https://github.com/astral-sh/ruff.git
synced 2025-08-04 18:58:04 +00:00
[flake8-pyi] Significantly improve accuracy of PYI019
if preview mode is enabled (#15888)
Some checks are pending
CI / cargo fuzz build (push) Blocked by required conditions
CI / Determine changes (push) Waiting to run
CI / cargo fmt (push) Waiting to run
CI / cargo clippy (push) Blocked by required conditions
CI / cargo test (linux) (push) Blocked by required conditions
CI / cargo test (linux, release) (push) Blocked by required conditions
CI / cargo test (windows) (push) Blocked by required conditions
CI / cargo test (wasm) (push) Blocked by required conditions
CI / cargo build (release) (push) Waiting to run
CI / cargo build (msrv) (push) Blocked by required conditions
CI / fuzz parser (push) Blocked by required conditions
CI / test scripts (push) Blocked by required conditions
CI / ecosystem (push) Blocked by required conditions
CI / cargo shear (push) Blocked by required conditions
CI / python package (push) Waiting to run
CI / pre-commit (push) Waiting to run
CI / mkdocs (push) Waiting to run
CI / formatter instabilities and black similarity (push) Blocked by required conditions
CI / test ruff-lsp (push) Blocked by required conditions
CI / benchmarks (push) Blocked by required conditions
Some checks are pending
CI / cargo fuzz build (push) Blocked by required conditions
CI / Determine changes (push) Waiting to run
CI / cargo fmt (push) Waiting to run
CI / cargo clippy (push) Blocked by required conditions
CI / cargo test (linux) (push) Blocked by required conditions
CI / cargo test (linux, release) (push) Blocked by required conditions
CI / cargo test (windows) (push) Blocked by required conditions
CI / cargo test (wasm) (push) Blocked by required conditions
CI / cargo build (release) (push) Waiting to run
CI / cargo build (msrv) (push) Blocked by required conditions
CI / fuzz parser (push) Blocked by required conditions
CI / test scripts (push) Blocked by required conditions
CI / ecosystem (push) Blocked by required conditions
CI / cargo shear (push) Blocked by required conditions
CI / python package (push) Waiting to run
CI / pre-commit (push) Waiting to run
CI / mkdocs (push) Waiting to run
CI / formatter instabilities and black similarity (push) Blocked by required conditions
CI / test ruff-lsp (push) Blocked by required conditions
CI / benchmarks (push) Blocked by required conditions
This commit is contained in:
parent
dfe1b849d0
commit
62075afe4f
9 changed files with 882 additions and 181 deletions
|
@ -111,4 +111,27 @@ def shadowed_type():
|
|||
|
||||
class SubscriptReturnType:
|
||||
@classmethod
|
||||
def m[S](cls: type[S]) -> type[S]: ... # PYI019, but no autofix (yet)
|
||||
def m[S](cls: type[S]) -> type[S]: ... # PYI019
|
||||
|
||||
|
||||
class SelfNotUsedInReturnAnnotation:
|
||||
def m[S](self: S, other: S) -> int: ...
|
||||
@classmethod
|
||||
def n[S](cls: type[S], other: S) -> int: ...
|
||||
|
||||
|
||||
class _NotATypeVar: ...
|
||||
|
||||
# Our stable-mode logic uses heuristics and thinks this is a `TypeVar`
|
||||
# because `self` and the return annotation use the same name as their annotation,
|
||||
# but our preview-mode logic is smarter about this.
|
||||
class Foo:
|
||||
def x(self: _NotATypeVar) -> _NotATypeVar: ...
|
||||
@classmethod
|
||||
def y(self: type[_NotATypeVar]) -> _NotATypeVar: ...
|
||||
|
||||
|
||||
class NoReturnAnnotations:
|
||||
def m[S](self: S, other: S): ...
|
||||
@classmethod
|
||||
def n[S](cls: type[S], other: S): ...
|
||||
|
|
|
@ -111,7 +111,7 @@ def shadowed_type():
|
|||
|
||||
class SubscriptReturnType:
|
||||
@classmethod
|
||||
def m[S](cls: type[S]) -> type[S]: ... # PYI019, but no autofix (yet)
|
||||
def m[S](cls: type[S]) -> type[S]: ... # PYI019
|
||||
|
||||
|
||||
class PEP695TypeParameterAtTheVeryEndOfTheList:
|
||||
|
@ -139,3 +139,25 @@ class PEP695Again:
|
|||
S, T
|
||||
]
|
||||
) -> S: ...
|
||||
|
||||
|
||||
class SelfNotUsedInReturnAnnotation:
|
||||
def m[S](self: S, other: S) -> int: ...
|
||||
@classmethod
|
||||
def n[S](cls: type[S], other: S) -> int: ...
|
||||
|
||||
|
||||
class _NotATypeVar: ...
|
||||
|
||||
# Our stable-mode logic uses heuristics and thinks this is a `TypeVar`
|
||||
# because `self` and the return annotation use the same name as their annotation,
|
||||
# but our preview-mode logic is smarter about this.
|
||||
class Foo:
|
||||
def x(self: _NotATypeVar) -> _NotATypeVar: ...
|
||||
@classmethod
|
||||
def y(self: type[_NotATypeVar]) -> _NotATypeVar: ...
|
||||
|
||||
class NoReturnAnnotations:
|
||||
def m[S](self: S, other: S): ...
|
||||
@classmethod
|
||||
def n[S](cls: type[S], other: S): ...
|
||||
|
|
|
@ -155,6 +155,7 @@ mod tests {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
#[test_case(Rule::CustomTypeVarForSelf, Path::new("PYI019_0.py"))]
|
||||
#[test_case(Rule::CustomTypeVarForSelf, Path::new("PYI019_0.pyi"))]
|
||||
#[test_case(Rule::CustomTypeVarForSelf, Path::new("PYI019_1.pyi"))]
|
||||
fn custom_classmethod_rules_preview(rule_code: Rule, path: &Path) -> Result<()> {
|
||||
|
|
|
@ -1,12 +1,11 @@
|
|||
use itertools::Itertools;
|
||||
use std::cmp;
|
||||
use anyhow::{bail, Context};
|
||||
|
||||
use ruff_diagnostics::{Applicability, Diagnostic, Edit, Fix, FixAvailability, Violation};
|
||||
use ruff_macros::{derive_message_formats, ViolationMetadata};
|
||||
use ruff_python_ast as ast;
|
||||
use ruff_python_semantic::analyze::function_type::{self, FunctionType};
|
||||
use ruff_python_semantic::analyze::visibility::{is_abstract, is_overload};
|
||||
use ruff_python_semantic::{Binding, ScopeId, SemanticModel};
|
||||
use ruff_python_semantic::{Binding, ResolvedReference, ScopeId, SemanticModel};
|
||||
use ruff_text_size::{Ranged, TextRange, TextSize};
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
|
@ -57,6 +56,18 @@ use crate::settings::types::PythonVersion;
|
|||
/// If there are any comments within the fix ranges, it will be marked as unsafe.
|
||||
/// Otherwise, it will be marked as safe.
|
||||
///
|
||||
/// ## Preview-mode behaviour
|
||||
/// This rule's behaviour has several differences when [`preview`] mode is enabled:
|
||||
/// 1. The fix for this rule is currently only available if `preview` mode is enabled.
|
||||
/// 2. By default, this rule is only applied to methods that have return-type annotations,
|
||||
/// and the range of the diagnostic is the range of the return-type annotation.
|
||||
/// In preview mode, this rule is also applied to some methods that do not have
|
||||
/// return-type annotations. The range of the diagnostic is the range of the function
|
||||
/// header (from the end of the function name to the end of the parameters).
|
||||
/// 3. In `preview` mode, the rule uses different logic to determine whether an annotation
|
||||
/// refers to a type variable. The `preview`-mode logic is more accurate, but may lead
|
||||
/// to more methods being flagged than if `preview` mode is disabled.
|
||||
///
|
||||
/// [PEP 673]: https://peps.python.org/pep-0673/#motivation
|
||||
/// [PEP 695]: https://peps.python.org/pep-0695/
|
||||
#[derive(ViolationMetadata)]
|
||||
|
@ -101,7 +112,7 @@ pub(crate) fn custom_type_var_instead_of_self(
|
|||
..
|
||||
} = function_def;
|
||||
|
||||
let returns = returns.as_deref()?;
|
||||
let type_params = type_params.as_deref();
|
||||
|
||||
// Given, e.g., `def foo(self: _S, arg: bytes)`, extract `_S`.
|
||||
let self_or_cls_parameter = parameters
|
||||
|
@ -117,44 +128,89 @@ pub(crate) fn custom_type_var_instead_of_self(
|
|||
return None;
|
||||
}
|
||||
|
||||
let method = match function_type::classify(
|
||||
let function_kind = function_type::classify(
|
||||
function_name,
|
||||
decorator_list,
|
||||
current_scope,
|
||||
semantic,
|
||||
&checker.settings.pep8_naming.classmethod_decorators,
|
||||
&checker.settings.pep8_naming.staticmethod_decorators,
|
||||
) {
|
||||
FunctionType::Function => return None,
|
||||
FunctionType::StaticMethod => return None,
|
||||
FunctionType::ClassMethod => Method::Class(ClassMethod {
|
||||
cls_annotation: self_or_cls_annotation,
|
||||
returns,
|
||||
type_params: type_params.as_deref(),
|
||||
}),
|
||||
FunctionType::Method => Method::Instance(InstanceMethod {
|
||||
self_annotation: self_or_cls_annotation,
|
||||
returns,
|
||||
type_params: type_params.as_deref(),
|
||||
}),
|
||||
);
|
||||
|
||||
let function_header_end = returns
|
||||
.as_deref()
|
||||
.map(Ranged::end)
|
||||
.unwrap_or_else(|| parameters.end());
|
||||
|
||||
// In stable mode, we only emit the diagnostic on methods that have a return type annotation.
|
||||
// In preview mode, we have a more principled approach to determine if an annotation refers
|
||||
// to a type variable, and we emit the diagnostic on some methods that do not have return
|
||||
// annotations.
|
||||
let (method, diagnostic_range) = match function_kind {
|
||||
FunctionType::ClassMethod => {
|
||||
if checker.settings.preview.is_enabled() {
|
||||
(
|
||||
Method::PreviewClass(PreviewClassMethod {
|
||||
cls_annotation: self_or_cls_annotation,
|
||||
type_params,
|
||||
}),
|
||||
TextRange::new(function_name.end(), function_header_end),
|
||||
)
|
||||
} else {
|
||||
returns.as_deref().map(|returns| {
|
||||
(
|
||||
Method::Class(ClassMethod {
|
||||
cls_annotation: self_or_cls_annotation,
|
||||
returns,
|
||||
type_params,
|
||||
}),
|
||||
returns.range(),
|
||||
)
|
||||
})?
|
||||
}
|
||||
}
|
||||
FunctionType::Method => {
|
||||
if checker.settings.preview.is_enabled() {
|
||||
(
|
||||
Method::PreviewInstance(PreviewInstanceMethod {
|
||||
self_annotation: self_or_cls_annotation,
|
||||
type_params,
|
||||
}),
|
||||
TextRange::new(function_name.end(), function_header_end),
|
||||
)
|
||||
} else {
|
||||
returns.as_deref().map(|returns| {
|
||||
(
|
||||
Method::Instance(InstanceMethod {
|
||||
self_annotation: self_or_cls_annotation,
|
||||
returns,
|
||||
type_params,
|
||||
}),
|
||||
returns.range(),
|
||||
)
|
||||
})?
|
||||
}
|
||||
}
|
||||
FunctionType::Function | FunctionType::StaticMethod => return None,
|
||||
};
|
||||
|
||||
let custom_typevar_name = method.custom_typevar(semantic, binding.scope)?;
|
||||
let custom_typevar = method.custom_typevar(semantic, binding.scope)?;
|
||||
|
||||
let mut diagnostic = Diagnostic::new(
|
||||
CustomTypeVarForSelf {
|
||||
typevar_name: custom_typevar_name.to_string(),
|
||||
typevar_name: custom_typevar.name.to_string(),
|
||||
},
|
||||
returns.range(),
|
||||
diagnostic_range,
|
||||
);
|
||||
|
||||
diagnostic.try_set_optional_fix(|| {
|
||||
replace_custom_typevar_with_self(
|
||||
checker,
|
||||
function_def,
|
||||
&custom_typevar,
|
||||
self_or_cls_parameter,
|
||||
self_or_cls_annotation,
|
||||
returns,
|
||||
function_header_end,
|
||||
)
|
||||
});
|
||||
|
||||
|
@ -164,14 +220,22 @@ pub(crate) fn custom_type_var_instead_of_self(
|
|||
#[derive(Debug)]
|
||||
enum Method<'a> {
|
||||
Class(ClassMethod<'a>),
|
||||
PreviewClass(PreviewClassMethod<'a>),
|
||||
Instance(InstanceMethod<'a>),
|
||||
PreviewInstance(PreviewInstanceMethod<'a>),
|
||||
}
|
||||
|
||||
impl Method<'_> {
|
||||
fn custom_typevar(&self, semantic: &SemanticModel, scope: ScopeId) -> Option<&str> {
|
||||
fn custom_typevar<'a>(
|
||||
&'a self,
|
||||
semantic: &'a SemanticModel<'a>,
|
||||
scope: ScopeId,
|
||||
) -> Option<TypeVar<'a>> {
|
||||
match self {
|
||||
Self::Class(class_method) => class_method.custom_typevar(semantic, scope),
|
||||
Self::Instance(instance_method) => instance_method.custom_typevar(),
|
||||
Self::PreviewClass(class_method) => class_method.custom_typevar(semantic, scope),
|
||||
Self::Instance(instance_method) => instance_method.custom_typevar(semantic),
|
||||
Self::PreviewInstance(instance_method) => instance_method.custom_typevar(semantic),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -184,16 +248,21 @@ struct ClassMethod<'a> {
|
|||
}
|
||||
|
||||
impl ClassMethod<'_> {
|
||||
/// Returns `Some(typevar_name)` if the class method is annotated with
|
||||
/// Returns `Some(typevar)` if the class method is annotated with
|
||||
/// a custom `TypeVar` that is likely private.
|
||||
fn custom_typevar(&self, semantic: &SemanticModel, scope: ScopeId) -> Option<&str> {
|
||||
fn custom_typevar<'a>(
|
||||
&'a self,
|
||||
semantic: &'a SemanticModel<'a>,
|
||||
scope: ScopeId,
|
||||
) -> Option<TypeVar<'a>> {
|
||||
let ast::ExprSubscript {
|
||||
value: cls_annotation_value,
|
||||
slice: cls_annotation_typevar,
|
||||
..
|
||||
} = self.cls_annotation.as_subscript_expr()?;
|
||||
|
||||
let cls_annotation_typevar = &cls_annotation_typevar.as_name_expr()?.id;
|
||||
let cls_annotation_typevar = cls_annotation_typevar.as_name_expr()?;
|
||||
let cls_annotation_typevar_name = &cls_annotation_typevar.id;
|
||||
let ast::ExprName { id, .. } = cls_annotation_value.as_name_expr()?;
|
||||
|
||||
if id != "type" {
|
||||
|
@ -217,12 +286,62 @@ impl ClassMethod<'_> {
|
|||
_ => return None,
|
||||
};
|
||||
|
||||
if cls_annotation_typevar != return_annotation_typevar {
|
||||
if cls_annotation_typevar_name != return_annotation_typevar {
|
||||
return None;
|
||||
}
|
||||
|
||||
is_likely_private_typevar(cls_annotation_typevar, self.type_params)
|
||||
.then_some(cls_annotation_typevar)
|
||||
if !is_likely_private_typevar(cls_annotation_typevar_name, self.type_params) {
|
||||
return None;
|
||||
}
|
||||
|
||||
let binding = semantic
|
||||
.resolve_name(cls_annotation_typevar)
|
||||
.map(|binding_id| semantic.binding(binding_id))?;
|
||||
|
||||
Some(TypeVar {
|
||||
name: cls_annotation_typevar_name,
|
||||
binding,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Struct for implementing this rule as applied to classmethods in preview mode.
|
||||
///
|
||||
/// In stable mode, we only emit this diagnostic on methods that have return annotations,
|
||||
/// so the stable-mode version of this struct has a `returns: &ast::Expr` field. In preview
|
||||
/// mode, we also emit this diagnostic on methods that do not have return annotations, so
|
||||
/// the preview-mode version of this struct does not have a `returns` field.
|
||||
#[derive(Debug)]
|
||||
struct PreviewClassMethod<'a> {
|
||||
cls_annotation: &'a ast::Expr,
|
||||
type_params: Option<&'a ast::TypeParams>,
|
||||
}
|
||||
|
||||
impl PreviewClassMethod<'_> {
|
||||
/// Returns `Some(typevar)` if the class method is annotated with
|
||||
/// a custom `TypeVar` for the `cls` parameter
|
||||
fn custom_typevar<'a>(
|
||||
&'a self,
|
||||
semantic: &'a SemanticModel<'a>,
|
||||
scope: ScopeId,
|
||||
) -> Option<TypeVar<'a>> {
|
||||
let ast::ExprSubscript {
|
||||
value: cls_annotation_value,
|
||||
slice: cls_annotation_typevar,
|
||||
..
|
||||
} = self.cls_annotation.as_subscript_expr()?;
|
||||
|
||||
let cls_annotation_typevar = cls_annotation_typevar.as_name_expr()?;
|
||||
|
||||
let ast::ExprName { id, .. } = cls_annotation_value.as_name_expr()?;
|
||||
if id != "type" {
|
||||
return None;
|
||||
}
|
||||
if !semantic.has_builtin_binding_in_scope("type", scope) {
|
||||
return None;
|
||||
}
|
||||
|
||||
custom_typevar_preview(cls_annotation_typevar, self.type_params, semantic)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -234,12 +353,11 @@ struct InstanceMethod<'a> {
|
|||
}
|
||||
|
||||
impl InstanceMethod<'_> {
|
||||
/// Returns `Some(typevar_name)` if the instance method is annotated with
|
||||
/// Returns `Some(typevar)` if the instance method is annotated with
|
||||
/// a custom `TypeVar` that is likely private.
|
||||
fn custom_typevar(&self) -> Option<&str> {
|
||||
let ast::ExprName {
|
||||
id: first_arg_type, ..
|
||||
} = self.self_annotation.as_name_expr()?;
|
||||
fn custom_typevar<'a>(&'a self, semantic: &'a SemanticModel<'a>) -> Option<TypeVar<'a>> {
|
||||
let self_annotation = self.self_annotation.as_name_expr()?;
|
||||
let first_arg_type = &self_annotation.id;
|
||||
|
||||
let ast::ExprName {
|
||||
id: return_type, ..
|
||||
|
@ -249,11 +367,50 @@ impl InstanceMethod<'_> {
|
|||
return None;
|
||||
}
|
||||
|
||||
is_likely_private_typevar(first_arg_type, self.type_params).then_some(first_arg_type)
|
||||
if !is_likely_private_typevar(first_arg_type, self.type_params) {
|
||||
return None;
|
||||
}
|
||||
|
||||
let binding = semantic
|
||||
.resolve_name(self_annotation)
|
||||
.map(|binding_id| semantic.binding(binding_id))?;
|
||||
|
||||
Some(TypeVar {
|
||||
name: first_arg_type,
|
||||
binding,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Struct for implementing this rule as applied to instance methods in preview mode.
|
||||
///
|
||||
/// In stable mode, we only emit this diagnostic on methods that have return annotations,
|
||||
/// so the stable-mode version of this struct has a `returns: &ast::Expr` field. In preview
|
||||
/// mode, we also emit this diagnostic on methods that do not have return annotations, so
|
||||
/// the preview-mode version of this struct does not have a `returns` field.
|
||||
#[derive(Debug)]
|
||||
struct PreviewInstanceMethod<'a> {
|
||||
self_annotation: &'a ast::Expr,
|
||||
type_params: Option<&'a ast::TypeParams>,
|
||||
}
|
||||
|
||||
impl PreviewInstanceMethod<'_> {
|
||||
/// Returns `Some(typevar)` if the instance method is annotated with
|
||||
/// a custom `TypeVar` for the `self` parameter
|
||||
fn custom_typevar<'a>(&'a self, semantic: &'a SemanticModel<'a>) -> Option<TypeVar<'a>> {
|
||||
custom_typevar_preview(
|
||||
self.self_annotation.as_name_expr()?,
|
||||
self.type_params,
|
||||
semantic,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns `true` if the type variable is likely private.
|
||||
///
|
||||
/// This routine is only used if `--preview` is not enabled,
|
||||
/// as it uses heuristics to determine if an annotation uses a type variable.
|
||||
/// In preview mode, we apply a more principled approach.
|
||||
fn is_likely_private_typevar(type_var_name: &str, type_params: Option<&ast::TypeParams>) -> bool {
|
||||
// Ex) `_T`
|
||||
if type_var_name.starts_with('_') {
|
||||
|
@ -271,19 +428,68 @@ fn is_likely_private_typevar(type_var_name: &str, type_params: Option<&ast::Type
|
|||
})
|
||||
}
|
||||
|
||||
/// Returns `Some(TypeVar)` if `typevar_expr` refers to a `TypeVar` binding
|
||||
fn custom_typevar_preview<'a>(
|
||||
typevar_expr: &'a ast::ExprName,
|
||||
type_params: Option<&ast::TypeParams>,
|
||||
semantic: &'a SemanticModel<'a>,
|
||||
) -> Option<TypeVar<'a>> {
|
||||
let binding = semantic
|
||||
.resolve_name(typevar_expr)
|
||||
.map(|binding_id| semantic.binding(binding_id))?;
|
||||
|
||||
// Example:
|
||||
// ```py
|
||||
// class Foo:
|
||||
// def m[S](self: S) -> S: ...
|
||||
// ```
|
||||
if binding.kind.is_type_param() {
|
||||
return type_params?
|
||||
.iter()
|
||||
.filter_map(ast::TypeParam::as_type_var)
|
||||
.any(|ast::TypeParamTypeVar { name, .. }| name.id == typevar_expr.id)
|
||||
.then_some(TypeVar {
|
||||
name: &typevar_expr.id,
|
||||
binding,
|
||||
});
|
||||
}
|
||||
|
||||
// Example:
|
||||
// ```py
|
||||
// from typing import TypeVar
|
||||
//
|
||||
// S = TypeVar("S", bound="Foo")
|
||||
//
|
||||
// class Foo:
|
||||
// def m(self: S) -> S: ...
|
||||
// ```
|
||||
if !semantic.seen_typing() {
|
||||
return None;
|
||||
}
|
||||
let statement = binding.source.map(|node_id| semantic.statement(node_id))?;
|
||||
let rhs_function = statement.as_assign_stmt()?.value.as_call_expr()?;
|
||||
|
||||
semantic
|
||||
.match_typing_expr(&rhs_function.func, "TypeVar")
|
||||
.then_some(TypeVar {
|
||||
name: &typevar_expr.id,
|
||||
binding,
|
||||
})
|
||||
}
|
||||
|
||||
/// 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
|
||||
/// * If it was a PEP-695 type variable, removes that `TypeVar` from the PEP-695 type-parameter list
|
||||
fn replace_custom_typevar_with_self(
|
||||
checker: &Checker,
|
||||
function_def: &ast::StmtFunctionDef,
|
||||
custom_typevar: &TypeVar,
|
||||
self_or_cls_parameter: &ast::ParameterWithDefault,
|
||||
self_or_cls_annotation: &ast::Expr,
|
||||
returns: &ast::Expr,
|
||||
function_header_end: TextSize,
|
||||
) -> anyhow::Result<Option<Fix>> {
|
||||
if checker.settings.preview.is_disabled() {
|
||||
return Ok(None);
|
||||
|
@ -296,48 +502,49 @@ fn replace_custom_typevar_with_self(
|
|||
return Ok(None);
|
||||
}
|
||||
|
||||
// Non-`Name` return annotations are not currently autofixed
|
||||
let ast::Expr::Name(typevar) = &returns else {
|
||||
return Ok(None);
|
||||
};
|
||||
|
||||
let mut applicability = Applicability::Safe;
|
||||
|
||||
let typevar_name = &typevar.id;
|
||||
|
||||
let (import_edit, self_symbol_binding) = import_self(checker, returns.start())?;
|
||||
// (1) Import `Self` (if necessary)
|
||||
let (import_edit, self_symbol_binding) = import_self(checker, function_def.start())?;
|
||||
|
||||
// (2) Remove the first parameter's annotation
|
||||
let mut other_edits = vec![Edit::deletion(
|
||||
self_or_cls_parameter.name().end(),
|
||||
self_or_cls_annotation.end(),
|
||||
)];
|
||||
|
||||
let replace_references_range = TextRange::new(self_or_cls_annotation.end(), returns.end());
|
||||
// (3) If it was a PEP-695 type variable, remove that `TypeVar` from the PEP-695 type-parameter list
|
||||
if custom_typevar.is_pep695_typevar() {
|
||||
let Some(type_params) = function_def.type_params.as_deref() else {
|
||||
bail!("Should not be possible to have a type parameter without a type parameter list");
|
||||
};
|
||||
let deletion_edit = remove_pep695_typevar_declaration(type_params, custom_typevar)
|
||||
.context("Failed to find a `TypeVar` in the type params that matches the binding")?;
|
||||
other_edits.push(deletion_edit);
|
||||
}
|
||||
|
||||
other_edits.extend(remove_typevar_declaration(
|
||||
function_def.type_params.as_deref(),
|
||||
typevar_name,
|
||||
));
|
||||
// (4) Replace all other references to the original type variable elsewhere in the function's header
|
||||
// with `Self`
|
||||
let replace_references_range =
|
||||
TextRange::new(self_or_cls_annotation.end(), function_header_end);
|
||||
|
||||
if let Some(edits) = replace_typevar_usages_with_self(
|
||||
typevar,
|
||||
other_edits.extend(replace_typevar_usages_with_self(
|
||||
custom_typevar,
|
||||
self_or_cls_annotation.range(),
|
||||
&self_symbol_binding,
|
||||
replace_references_range,
|
||||
checker.semantic(),
|
||||
) {
|
||||
other_edits.extend(edits);
|
||||
} else {
|
||||
applicability = Applicability::DisplayOnly;
|
||||
}
|
||||
));
|
||||
|
||||
// (5) Determine the safety of the fixes as a whole
|
||||
let comment_ranges = checker.comment_ranges();
|
||||
|
||||
if other_edits
|
||||
let applicability = if other_edits
|
||||
.iter()
|
||||
.any(|edit| comment_ranges.intersects(edit.range()))
|
||||
{
|
||||
applicability = cmp::min(applicability, Applicability::Unsafe);
|
||||
}
|
||||
Applicability::Unsafe
|
||||
} else {
|
||||
Applicability::Safe
|
||||
};
|
||||
|
||||
Ok(Some(Fix::applicable_edits(
|
||||
import_edit,
|
||||
|
@ -346,8 +553,13 @@ fn replace_custom_typevar_with_self(
|
|||
)))
|
||||
}
|
||||
|
||||
/// Attempt to create an [`Edit`] that imports `Self`.
|
||||
///
|
||||
/// On Python <3.11, `Self` is imported from `typing_extensions`;
|
||||
/// on Python >=3.11, it is imported from `typing`.
|
||||
/// This is because it was added to the `typing` module on Python 3.11,
|
||||
/// but is available from the backport package `typing_extensions` on all versions.
|
||||
fn import_self(checker: &Checker, position: TextSize) -> Result<(Edit, String), ResolutionError> {
|
||||
// See also PYI034's fix
|
||||
let source_module = if checker.settings.target_version >= PythonVersion::Py311 {
|
||||
"typing"
|
||||
} else {
|
||||
|
@ -359,68 +571,83 @@ fn import_self(checker: &Checker, position: TextSize) -> Result<(Edit, String),
|
|||
.get_or_import_symbol(&request, position, checker.semantic())
|
||||
}
|
||||
|
||||
/// Returns a series of [`Edit`]s that modify all references to the given `typevar`,
|
||||
/// or `None` when it is not possible to resolve the binding.
|
||||
/// Returns a series of [`Edit`]s that modify all references to the given `typevar`.
|
||||
///
|
||||
/// Only references within `editable_range` will be modified.
|
||||
/// This ensures that no edit in this series will overlap with other edits.
|
||||
fn replace_typevar_usages_with_self(
|
||||
typevar: &ast::ExprName,
|
||||
self_symbol_binding: &str,
|
||||
fn replace_typevar_usages_with_self<'a>(
|
||||
typevar: &'a TypeVar<'a>,
|
||||
self_or_cls_annotation_range: TextRange,
|
||||
self_symbol_binding: &'a str,
|
||||
editable_range: TextRange,
|
||||
semantic: &SemanticModel,
|
||||
) -> Option<Vec<Edit>> {
|
||||
let binding = semantic
|
||||
.only_binding(typevar)
|
||||
.map(|id| semantic.binding(id))?;
|
||||
|
||||
let mut edits = vec![];
|
||||
|
||||
for reference_id in binding.references() {
|
||||
let reference = semantic.reference(reference_id);
|
||||
let range = reference.range();
|
||||
|
||||
if editable_range.contains_range(range) {
|
||||
let edit = Edit::range_replacement(self_symbol_binding.to_string(), range);
|
||||
edits.push(edit);
|
||||
}
|
||||
}
|
||||
|
||||
Some(edits)
|
||||
semantic: &'a SemanticModel<'a>,
|
||||
) -> impl Iterator<Item = Edit> + 'a {
|
||||
typevar
|
||||
.references(semantic)
|
||||
.map(Ranged::range)
|
||||
.filter(move |reference_range| editable_range.contains_range(*reference_range))
|
||||
.filter(move |reference_range| {
|
||||
!self_or_cls_annotation_range.contains_range(*reference_range)
|
||||
})
|
||||
.map(|reference_range| {
|
||||
Edit::range_replacement(self_symbol_binding.to_string(), reference_range)
|
||||
})
|
||||
}
|
||||
|
||||
fn remove_typevar_declaration(type_params: Option<&ast::TypeParams>, name: &str) -> Option<Edit> {
|
||||
let is_declaration_in_question = |type_param: &&ast::TypeParam| -> bool {
|
||||
if let ast::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));
|
||||
/// Create an [`Edit`] removing the `TypeVar` binding from the PEP 695 type parameter list.
|
||||
///
|
||||
/// Return `None` if we fail to find a `TypeVar` that matches the range of `typevar_binding`.
|
||||
fn remove_pep695_typevar_declaration(
|
||||
type_params: &ast::TypeParams,
|
||||
custom_typevar: &TypeVar,
|
||||
) -> Option<Edit> {
|
||||
if let [sole_typevar] = &**type_params {
|
||||
return (sole_typevar.range() == custom_typevar.range())
|
||||
.then(|| Edit::range_deletion(type_params.range));
|
||||
}
|
||||
|
||||
let (index, declaration) = parameters
|
||||
let tvar_index = type_params
|
||||
.iter()
|
||||
.find_position(is_declaration_in_question)?;
|
||||
.position(|param| param.range() == custom_typevar.range())?;
|
||||
|
||||
let last_index = parameters.len() - 1;
|
||||
let last_index = type_params.len() - 1;
|
||||
|
||||
let range = if index < last_index {
|
||||
// [A, B, C]
|
||||
// ^^^ Remove this
|
||||
TextRange::new(declaration.start(), parameters[index + 1].start())
|
||||
let deletion_range = if tvar_index < last_index {
|
||||
// def f[A, B, C](): ...
|
||||
// ^^^ Remove this
|
||||
TextRange::new(custom_typevar.start(), type_params[tvar_index + 1].start())
|
||||
} else {
|
||||
// [A, B, C]
|
||||
// ^^^ Remove this
|
||||
TextRange::new(parameters[index - 1].end(), declaration.end())
|
||||
// def f[A, B, C](): ...
|
||||
// ^^^ Remove this
|
||||
TextRange::new(type_params[tvar_index - 1].end(), custom_typevar.end())
|
||||
};
|
||||
|
||||
Some(Edit::range_deletion(range))
|
||||
Some(Edit::range_deletion(deletion_range))
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct TypeVar<'a> {
|
||||
name: &'a str,
|
||||
binding: &'a Binding<'a>,
|
||||
}
|
||||
|
||||
impl TypeVar<'_> {
|
||||
const fn is_pep695_typevar(&self) -> bool {
|
||||
self.binding.kind.is_type_param()
|
||||
}
|
||||
|
||||
fn references<'a>(
|
||||
&'a self,
|
||||
semantic: &'a SemanticModel<'a>,
|
||||
) -> impl Iterator<Item = &'a ResolvedReference> + 'a {
|
||||
self.binding
|
||||
.references()
|
||||
.map(|reference_id| semantic.reference(reference_id))
|
||||
}
|
||||
}
|
||||
|
||||
impl Ranged for TypeVar<'_> {
|
||||
fn range(&self) -> TextRange {
|
||||
self.binding.range()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -211,7 +211,27 @@ PYI019_0.py:114:31: PYI019 Use `Self` instead of custom TypeVar `S`
|
|||
|
|
||||
112 | class SubscriptReturnType:
|
||||
113 | @classmethod
|
||||
114 | def m[S](cls: type[S]) -> type[S]: ... # PYI019, but no autofix (yet)
|
||||
114 | def m[S](cls: type[S]) -> type[S]: ... # PYI019
|
||||
| ^^^^^^^ PYI019
|
||||
|
|
||||
= help: Replace TypeVar `S` with `Self`
|
||||
|
||||
PYI019_0.py:129:34: PYI019 Use `Self` instead of custom TypeVar `_NotATypeVar`
|
||||
|
|
||||
127 | # but our preview-mode logic is smarter about this.
|
||||
128 | class Foo:
|
||||
129 | def x(self: _NotATypeVar) -> _NotATypeVar: ...
|
||||
| ^^^^^^^^^^^^ PYI019
|
||||
130 | @classmethod
|
||||
131 | def y(self: type[_NotATypeVar]) -> _NotATypeVar: ...
|
||||
|
|
||||
= help: Replace TypeVar `_NotATypeVar` with `Self`
|
||||
|
||||
PYI019_0.py:131:40: PYI019 Use `Self` instead of custom TypeVar `_NotATypeVar`
|
||||
|
|
||||
129 | def x(self: _NotATypeVar) -> _NotATypeVar: ...
|
||||
130 | @classmethod
|
||||
131 | def y(self: type[_NotATypeVar]) -> _NotATypeVar: ...
|
||||
| ^^^^^^^^^^^^ PYI019
|
||||
|
|
||||
= help: Replace TypeVar `_NotATypeVar` with `Self`
|
||||
|
|
|
@ -211,7 +211,7 @@ PYI019_0.pyi:114:31: PYI019 Use `Self` instead of custom TypeVar `S`
|
|||
|
|
||||
112 | class SubscriptReturnType:
|
||||
113 | @classmethod
|
||||
114 | def m[S](cls: type[S]) -> type[S]: ... # PYI019, but no autofix (yet)
|
||||
114 | def m[S](cls: type[S]) -> type[S]: ... # PYI019
|
||||
| ^^^^^^^ PYI019
|
||||
|
|
||||
= help: Replace TypeVar `S` with `Self`
|
||||
|
@ -252,3 +252,25 @@ PYI019_0.pyi:141:10: PYI019 Use `Self` instead of custom TypeVar `S`
|
|||
| ^ PYI019
|
||||
|
|
||||
= help: Replace TypeVar `S` with `Self`
|
||||
|
||||
PYI019_0.pyi:156:34: PYI019 Use `Self` instead of custom TypeVar `_NotATypeVar`
|
||||
|
|
||||
154 | # but our preview-mode logic is smarter about this.
|
||||
155 | class Foo:
|
||||
156 | def x(self: _NotATypeVar) -> _NotATypeVar: ...
|
||||
| ^^^^^^^^^^^^ PYI019
|
||||
157 | @classmethod
|
||||
158 | def y(self: type[_NotATypeVar]) -> _NotATypeVar: ...
|
||||
|
|
||||
= help: Replace TypeVar `_NotATypeVar` with `Self`
|
||||
|
||||
PYI019_0.pyi:158:40: PYI019 Use `Self` instead of custom TypeVar `_NotATypeVar`
|
||||
|
|
||||
156 | def x(self: _NotATypeVar) -> _NotATypeVar: ...
|
||||
157 | @classmethod
|
||||
158 | def y(self: type[_NotATypeVar]) -> _NotATypeVar: ...
|
||||
| ^^^^^^^^^^^^ PYI019
|
||||
159 |
|
||||
160 | class NoReturnAnnotations:
|
||||
|
|
||||
= help: Replace TypeVar `_NotATypeVar` with `Self`
|
||||
|
|
|
@ -0,0 +1,265 @@
|
|||
---
|
||||
source: crates/ruff_linter/src/rules/flake8_pyi/mod.rs
|
||||
---
|
||||
PYI019_0.py:7:16: PYI019 Use `Self` instead of custom TypeVar `_S`
|
||||
|
|
||||
6 | class BadClass:
|
||||
7 | def __new__(cls: type[_S], *args: str, **kwargs: int) -> _S: ... # PYI019
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ PYI019
|
||||
|
|
||||
= help: Replace TypeVar `_S` with `Self`
|
||||
|
||||
PYI019_0.py:10:28: PYI019 Use `Self` instead of custom TypeVar `_S`
|
||||
|
|
||||
10 | def bad_instance_method(self: _S, arg: bytes) -> _S: ... # PYI019
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ PYI019
|
||||
|
|
||||
= help: Replace TypeVar `_S` with `Self`
|
||||
|
||||
PYI019_0.py:14:25: PYI019 Use `Self` instead of custom TypeVar `_S`
|
||||
|
|
||||
13 | @classmethod
|
||||
14 | def bad_class_method(cls: type[_S], arg: int) -> _S: ... # PYI019
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ PYI019
|
||||
|
|
||||
= help: Replace TypeVar `_S` with `Self`
|
||||
|
||||
PYI019_0.py:18:33: PYI019 Use `Self` instead of custom TypeVar `_S`
|
||||
|
|
||||
17 | @classmethod
|
||||
18 | def bad_posonly_class_method(cls: type[_S], /) -> _S: ... # PYI019
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^ PYI019
|
||||
|
|
||||
= help: Replace TypeVar `_S` with `Self`
|
||||
|
||||
PYI019_0.py:39:14: PYI019 Use `Self` instead of custom TypeVar `S`
|
||||
|
|
||||
37 | # Python > 3.12
|
||||
38 | class PEP695BadDunderNew[T]:
|
||||
39 | def __new__[S](cls: type[S], *args: Any, ** kwargs: Any) -> S: ... # PYI019
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ PYI019
|
||||
|
|
||||
= help: Replace TypeVar `S` with `Self`
|
||||
|
||||
PYI019_0.py:42:30: PYI019 Use `Self` instead of custom TypeVar `S`
|
||||
|
|
||||
42 | def generic_instance_method[S](self: S) -> S: ... # PYI019
|
||||
| ^^^^^^^^^^^^^^^^^ PYI019
|
||||
|
|
||||
= help: Replace TypeVar `S` with `Self`
|
||||
|
||||
PYI019_0.py:54:11: PYI019 Use `Self` instead of custom TypeVar `S`
|
||||
|
|
||||
52 | # in the settings for this test:
|
||||
53 | @foo_classmethod
|
||||
54 | def foo[S](cls: type[S]) -> S: ... # PYI019
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^ PYI019
|
||||
|
|
||||
= help: Replace TypeVar `S` with `Self`
|
||||
|
||||
PYI019_0.py:61:16: PYI019 Use `Self` instead of custom TypeVar `S`
|
||||
|
|
||||
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 TypeVar `S` with `Self`
|
||||
|
||||
PYI019_0.py:63:26: PYI019 Use `Self` instead of custom TypeVar `S`
|
||||
|
|
||||
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 TypeVar `S` with `Self`
|
||||
|
||||
PYI019_0.py:65:16: PYI019 Use `Self` instead of custom TypeVar `S`
|
||||
|
|
||||
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 TypeVar `S` with `Self`
|
||||
|
||||
PYI019_0.py:67:16: PYI019 Use `Self` instead of custom TypeVar `S`
|
||||
|
|
||||
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 TypeVar `S` with `Self`
|
||||
|
||||
PYI019_0.py:69:16: PYI019 Use `Self` instead of custom TypeVar `S`
|
||||
|
|
||||
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 TypeVar `S` with `Self`
|
||||
|
||||
PYI019_0.py:71:16: PYI019 Use `Self` instead of custom TypeVar `S`
|
||||
|
|
||||
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 TypeVar `S` with `Self`
|
||||
|
||||
PYI019_0.py:74:27: PYI019 Use `Self` instead of custom TypeVar `S`
|
||||
|
|
||||
73 | @classmethod
|
||||
74 | def class_method_bound[S: PEP695Fix](cls: type[S]) -> S: ...
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ PYI019
|
||||
75 |
|
||||
76 | @classmethod
|
||||
|
|
||||
= help: Replace TypeVar `S` with `Self`
|
||||
|
||||
PYI019_0.py:77:29: PYI019 Use `Self` instead of custom TypeVar `S`
|
||||
|
|
||||
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 TypeVar `S` with `Self`
|
||||
|
||||
PYI019_0.py:79:30: PYI019 Use `Self` instead of custom TypeVar `S`
|
||||
|
|
||||
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 TypeVar `S` with `Self`
|
||||
|
||||
PYI019_0.py:81:32: PYI019 Use `Self` instead of custom TypeVar `S`
|
||||
|
|
||||
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 TypeVar `S` with `Self`
|
||||
|
||||
PYI019_0.py:83:53: PYI019 Use `Self` instead of custom TypeVar `S`
|
||||
|
|
||||
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 TypeVar `S` with `Self`
|
||||
|
||||
PYI019_0.py:85:55: PYI019 Use `Self` instead of custom TypeVar `S`
|
||||
|
|
||||
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 TypeVar `S` with `Self`
|
||||
|
||||
PYI019_0.py:87:27: PYI019 Use `Self` instead of custom TypeVar `S`
|
||||
|
|
||||
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 TypeVar `S` with `Self`
|
||||
|
||||
PYI019_0.py:89:43: PYI019 Use `Self` instead of custom TypeVar `_S695`
|
||||
|
|
||||
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 TypeVar `_S695` with `Self`
|
||||
|
||||
PYI019_0.py:94:10: PYI019 Use `Self` instead of custom TypeVar `S`
|
||||
|
|
||||
92 | class InvalidButWeDoNotPanic:
|
||||
93 | @classmethod
|
||||
94 | def m[S](cls: type[S], /) -> S[int]: ...
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ PYI019
|
||||
95 | def n(self: S) -> S[int]: ...
|
||||
|
|
||||
= help: Replace TypeVar `S` with `Self`
|
||||
|
||||
PYI019_0.py:114:10: PYI019 Use `Self` instead of custom TypeVar `S`
|
||||
|
|
||||
112 | class SubscriptReturnType:
|
||||
113 | @classmethod
|
||||
114 | def m[S](cls: type[S]) -> type[S]: ... # PYI019
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ PYI019
|
||||
|
|
||||
= help: Replace TypeVar `S` with `Self`
|
||||
|
||||
PYI019_0.py:118:10: PYI019 Use `Self` instead of custom TypeVar `S`
|
||||
|
|
||||
117 | class SelfNotUsedInReturnAnnotation:
|
||||
118 | def m[S](self: S, other: S) -> int: ...
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ PYI019
|
||||
119 | @classmethod
|
||||
120 | def n[S](cls: type[S], other: S) -> int: ...
|
||||
|
|
||||
= help: Replace TypeVar `S` with `Self`
|
||||
|
||||
PYI019_0.py:120:10: PYI019 Use `Self` instead of custom TypeVar `S`
|
||||
|
|
||||
118 | def m[S](self: S, other: S) -> int: ...
|
||||
119 | @classmethod
|
||||
120 | def n[S](cls: type[S], other: S) -> int: ...
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ PYI019
|
||||
|
|
||||
= help: Replace TypeVar `S` with `Self`
|
||||
|
||||
PYI019_0.py:135:10: PYI019 Use `Self` instead of custom TypeVar `S`
|
||||
|
|
||||
134 | class NoReturnAnnotations:
|
||||
135 | def m[S](self: S, other: S): ...
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^ PYI019
|
||||
136 | @classmethod
|
||||
137 | def n[S](cls: type[S], other: S): ...
|
||||
|
|
||||
= help: Replace TypeVar `S` with `Self`
|
||||
|
||||
PYI019_0.py:137:10: PYI019 Use `Self` instead of custom TypeVar `S`
|
||||
|
|
||||
135 | def m[S](self: S, other: S): ...
|
||||
136 | @classmethod
|
||||
137 | def n[S](cls: type[S], other: S): ...
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ PYI019
|
||||
|
|
||||
= help: Replace TypeVar `S` with `Self`
|
|
@ -1,11 +1,11 @@
|
|||
---
|
||||
source: crates/ruff_linter/src/rules/flake8_pyi/mod.rs
|
||||
---
|
||||
PYI019_0.pyi:7:62: PYI019 [*] Use `Self` instead of custom TypeVar `_S`
|
||||
PYI019_0.pyi:7:16: PYI019 [*] Use `Self` instead of custom TypeVar `_S`
|
||||
|
|
||||
6 | class BadClass:
|
||||
7 | def __new__(cls: type[_S], *args: str, **kwargs: int) -> _S: ... # PYI019
|
||||
| ^^ PYI019
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ PYI019
|
||||
|
|
||||
= help: Replace TypeVar `_S` with `Self`
|
||||
|
||||
|
@ -19,10 +19,10 @@ PYI019_0.pyi:7:62: PYI019 [*] Use `Self` instead of custom TypeVar `_S`
|
|||
9 9 |
|
||||
10 10 | def bad_instance_method(self: _S, arg: bytes) -> _S: ... # PYI019
|
||||
|
||||
PYI019_0.pyi:10:54: PYI019 [*] Use `Self` instead of custom TypeVar `_S`
|
||||
PYI019_0.pyi:10:28: PYI019 [*] Use `Self` instead of custom TypeVar `_S`
|
||||
|
|
||||
10 | def bad_instance_method(self: _S, arg: bytes) -> _S: ... # PYI019
|
||||
| ^^ PYI019
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ PYI019
|
||||
|
|
||||
= help: Replace TypeVar `_S` with `Self`
|
||||
|
||||
|
@ -36,11 +36,11 @@ PYI019_0.pyi:10:54: PYI019 [*] Use `Self` instead of custom TypeVar `_S`
|
|||
12 12 |
|
||||
13 13 | @classmethod
|
||||
|
||||
PYI019_0.pyi:14:54: PYI019 [*] Use `Self` instead of custom TypeVar `_S`
|
||||
PYI019_0.pyi:14:25: PYI019 [*] Use `Self` instead of custom TypeVar `_S`
|
||||
|
|
||||
13 | @classmethod
|
||||
14 | def bad_class_method(cls: type[_S], arg: int) -> _S: ... # PYI019
|
||||
| ^^ PYI019
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ PYI019
|
||||
|
|
||||
= help: Replace TypeVar `_S` with `Self`
|
||||
|
||||
|
@ -54,11 +54,11 @@ PYI019_0.pyi:14:54: PYI019 [*] Use `Self` instead of custom TypeVar `_S`
|
|||
16 16 |
|
||||
17 17 | @classmethod
|
||||
|
||||
PYI019_0.pyi:18:55: PYI019 [*] Use `Self` instead of custom TypeVar `_S`
|
||||
PYI019_0.pyi:18:33: PYI019 [*] Use `Self` instead of custom TypeVar `_S`
|
||||
|
|
||||
17 | @classmethod
|
||||
18 | def bad_posonly_class_method(cls: type[_S], /) -> _S: ... # PYI019
|
||||
| ^^ PYI019
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^ PYI019
|
||||
|
|
||||
= help: Replace TypeVar `_S` with `Self`
|
||||
|
||||
|
@ -72,12 +72,12 @@ PYI019_0.pyi:18:55: PYI019 [*] Use `Self` instead of custom TypeVar `_S`
|
|||
20 20 |
|
||||
21 21 | @classmethod
|
||||
|
||||
PYI019_0.pyi:39:63: PYI019 [*] Use `Self` instead of custom TypeVar `S`
|
||||
PYI019_0.pyi:39:14: PYI019 [*] Use `Self` instead of custom TypeVar `S`
|
||||
|
|
||||
37 | # Python > 3.12
|
||||
38 | class PEP695BadDunderNew[T]:
|
||||
39 | def __new__[S](cls: type[S], *args: Any, ** kwargs: Any) -> S: ... # PYI019
|
||||
| ^ PYI019
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ PYI019
|
||||
|
|
||||
= help: Replace TypeVar `S` with `Self`
|
||||
|
||||
|
@ -91,10 +91,10 @@ PYI019_0.pyi:39:63: PYI019 [*] Use `Self` instead of custom TypeVar `S`
|
|||
41 41 |
|
||||
42 42 | def generic_instance_method[S](self: S) -> S: ... # PYI019
|
||||
|
||||
PYI019_0.pyi:42:46: PYI019 [*] Use `Self` instead of custom TypeVar `S`
|
||||
PYI019_0.pyi:42:30: PYI019 [*] Use `Self` instead of custom TypeVar `S`
|
||||
|
|
||||
42 | def generic_instance_method[S](self: S) -> S: ... # PYI019
|
||||
| ^ PYI019
|
||||
| ^^^^^^^^^^^^^^^^^ PYI019
|
||||
|
|
||||
= help: Replace TypeVar `S` with `Self`
|
||||
|
||||
|
@ -108,12 +108,12 @@ PYI019_0.pyi:42:46: PYI019 [*] Use `Self` instead of custom TypeVar `S`
|
|||
44 44 |
|
||||
45 45 | class PEP695GoodDunderNew[T]:
|
||||
|
||||
PYI019_0.pyi:54:32: PYI019 [*] Use `Self` instead of custom TypeVar `S`
|
||||
PYI019_0.pyi:54:11: PYI019 [*] Use `Self` instead of custom TypeVar `S`
|
||||
|
|
||||
52 | # in the settings for this test:
|
||||
53 | @foo_classmethod
|
||||
54 | def foo[S](cls: type[S]) -> S: ... # PYI019
|
||||
| ^ PYI019
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^ PYI019
|
||||
|
|
||||
= help: Replace TypeVar `S` with `Self`
|
||||
|
||||
|
@ -127,12 +127,12 @@ PYI019_0.pyi:54:32: PYI019 [*] Use `Self` instead of custom TypeVar `S`
|
|||
56 56 |
|
||||
57 57 | _S695 = TypeVar("_S695", bound="PEP695Fix")
|
||||
|
||||
PYI019_0.pyi:61:48: PYI019 [*] Use `Self` instead of custom TypeVar `S`
|
||||
PYI019_0.pyi:61:16: PYI019 [*] Use `Self` instead of custom TypeVar `S`
|
||||
|
|
||||
59 | # Only .pyi gets fixes, no fixes for .py
|
||||
60 | class PEP695Fix:
|
||||
61 | def __new__[S: PEP695Fix](cls: type[S]) -> S: ...
|
||||
| ^ PYI019
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ PYI019
|
||||
62 |
|
||||
63 | def __init_subclass__[S](cls: type[S]) -> S: ...
|
||||
|
|
||||
|
@ -148,12 +148,12 @@ PYI019_0.pyi:61:48: PYI019 [*] Use `Self` instead of custom TypeVar `S`
|
|||
63 63 | def __init_subclass__[S](cls: type[S]) -> S: ...
|
||||
64 64 |
|
||||
|
||||
PYI019_0.pyi:63:47: PYI019 [*] Use `Self` instead of custom TypeVar `S`
|
||||
PYI019_0.pyi:63:26: PYI019 [*] Use `Self` instead of custom TypeVar `S`
|
||||
|
|
||||
61 | def __new__[S: PEP695Fix](cls: type[S]) -> S: ...
|
||||
62 |
|
||||
63 | def __init_subclass__[S](cls: type[S]) -> S: ...
|
||||
| ^ PYI019
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^ PYI019
|
||||
64 |
|
||||
65 | def __neg__[S: PEP695Fix](self: S) -> S: ...
|
||||
|
|
||||
|
@ -169,12 +169,12 @@ PYI019_0.pyi:63:47: PYI019 [*] Use `Self` instead of custom TypeVar `S`
|
|||
65 65 | def __neg__[S: PEP695Fix](self: S) -> S: ...
|
||||
66 66 |
|
||||
|
||||
PYI019_0.pyi:65:43: PYI019 [*] Use `Self` instead of custom TypeVar `S`
|
||||
PYI019_0.pyi:65:16: PYI019 [*] Use `Self` instead of custom TypeVar `S`
|
||||
|
|
||||
63 | def __init_subclass__[S](cls: type[S]) -> S: ...
|
||||
64 |
|
||||
65 | def __neg__[S: PEP695Fix](self: S) -> S: ...
|
||||
| ^ PYI019
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ PYI019
|
||||
66 |
|
||||
67 | def __pos__[S](self: S) -> S: ...
|
||||
|
|
||||
|
@ -190,12 +190,12 @@ PYI019_0.pyi:65:43: PYI019 [*] Use `Self` instead of custom TypeVar `S`
|
|||
67 67 | def __pos__[S](self: S) -> S: ...
|
||||
68 68 |
|
||||
|
||||
PYI019_0.pyi:67:32: PYI019 [*] Use `Self` instead of custom TypeVar `S`
|
||||
PYI019_0.pyi:67:16: PYI019 [*] Use `Self` instead of custom TypeVar `S`
|
||||
|
|
||||
65 | def __neg__[S: PEP695Fix](self: S) -> S: ...
|
||||
66 |
|
||||
67 | def __pos__[S](self: S) -> S: ...
|
||||
| ^ PYI019
|
||||
| ^^^^^^^^^^^^^^^^^ PYI019
|
||||
68 |
|
||||
69 | def __add__[S: PEP695Fix](self: S, other: S) -> S: ...
|
||||
|
|
||||
|
@ -211,12 +211,12 @@ PYI019_0.pyi:67:32: PYI019 [*] Use `Self` instead of custom TypeVar `S`
|
|||
69 69 | def __add__[S: PEP695Fix](self: S, other: S) -> S: ...
|
||||
70 70 |
|
||||
|
||||
PYI019_0.pyi:69:53: PYI019 [*] Use `Self` instead of custom TypeVar `S`
|
||||
PYI019_0.pyi:69:16: PYI019 [*] Use `Self` instead of custom TypeVar `S`
|
||||
|
|
||||
67 | def __pos__[S](self: S) -> S: ...
|
||||
68 |
|
||||
69 | def __add__[S: PEP695Fix](self: S, other: S) -> S: ...
|
||||
| ^ PYI019
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ PYI019
|
||||
70 |
|
||||
71 | def __sub__[S](self: S, other: S) -> S: ...
|
||||
|
|
||||
|
@ -232,12 +232,12 @@ PYI019_0.pyi:69:53: PYI019 [*] Use `Self` instead of custom TypeVar `S`
|
|||
71 71 | def __sub__[S](self: S, other: S) -> S: ...
|
||||
72 72 |
|
||||
|
||||
PYI019_0.pyi:71:42: PYI019 [*] Use `Self` instead of custom TypeVar `S`
|
||||
PYI019_0.pyi:71:16: PYI019 [*] Use `Self` instead of custom TypeVar `S`
|
||||
|
|
||||
69 | def __add__[S: PEP695Fix](self: S, other: S) -> S: ...
|
||||
70 |
|
||||
71 | def __sub__[S](self: S, other: S) -> S: ...
|
||||
| ^ PYI019
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ PYI019
|
||||
72 |
|
||||
73 | @classmethod
|
||||
|
|
||||
|
@ -253,11 +253,11 @@ PYI019_0.pyi:71:42: PYI019 [*] Use `Self` instead of custom TypeVar `S`
|
|||
73 73 | @classmethod
|
||||
74 74 | def class_method_bound[S: PEP695Fix](cls: type[S]) -> S: ...
|
||||
|
||||
PYI019_0.pyi:74:59: PYI019 [*] Use `Self` instead of custom TypeVar `S`
|
||||
PYI019_0.pyi:74:27: PYI019 [*] Use `Self` instead of custom TypeVar `S`
|
||||
|
|
||||
73 | @classmethod
|
||||
74 | def class_method_bound[S: PEP695Fix](cls: type[S]) -> S: ...
|
||||
| ^ PYI019
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ PYI019
|
||||
75 |
|
||||
76 | @classmethod
|
||||
|
|
||||
|
@ -273,11 +273,11 @@ PYI019_0.pyi:74:59: PYI019 [*] Use `Self` instead of custom TypeVar `S`
|
|||
76 76 | @classmethod
|
||||
77 77 | def class_method_unbound[S](cls: type[S]) -> S: ...
|
||||
|
||||
PYI019_0.pyi:77:50: PYI019 [*] Use `Self` instead of custom TypeVar `S`
|
||||
PYI019_0.pyi:77:29: PYI019 [*] Use `Self` instead of custom TypeVar `S`
|
||||
|
|
||||
76 | @classmethod
|
||||
77 | def class_method_unbound[S](cls: type[S]) -> S: ...
|
||||
| ^ PYI019
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^ PYI019
|
||||
78 |
|
||||
79 | def instance_method_bound[S: PEP695Fix](self: S) -> S: ...
|
||||
|
|
||||
|
@ -293,12 +293,12 @@ PYI019_0.pyi:77:50: PYI019 [*] Use `Self` instead of custom TypeVar `S`
|
|||
79 79 | def instance_method_bound[S: PEP695Fix](self: S) -> S: ...
|
||||
80 80 |
|
||||
|
||||
PYI019_0.pyi:79:57: PYI019 [*] Use `Self` instead of custom TypeVar `S`
|
||||
PYI019_0.pyi:79:30: PYI019 [*] Use `Self` instead of custom TypeVar `S`
|
||||
|
|
||||
77 | def class_method_unbound[S](cls: type[S]) -> S: ...
|
||||
78 |
|
||||
79 | def instance_method_bound[S: PEP695Fix](self: S) -> S: ...
|
||||
| ^ PYI019
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ PYI019
|
||||
80 |
|
||||
81 | def instance_method_unbound[S](self: S) -> S: ...
|
||||
|
|
||||
|
@ -314,12 +314,12 @@ PYI019_0.pyi:79:57: PYI019 [*] Use `Self` instead of custom TypeVar `S`
|
|||
81 81 | def instance_method_unbound[S](self: S) -> S: ...
|
||||
82 82 |
|
||||
|
||||
PYI019_0.pyi:81:48: PYI019 [*] Use `Self` instead of custom TypeVar `S`
|
||||
PYI019_0.pyi:81:32: PYI019 [*] Use `Self` instead of custom TypeVar `S`
|
||||
|
|
||||
79 | def instance_method_bound[S: PEP695Fix](self: S) -> S: ...
|
||||
80 |
|
||||
81 | def instance_method_unbound[S](self: S) -> S: ...
|
||||
| ^ PYI019
|
||||
| ^^^^^^^^^^^^^^^^^ PYI019
|
||||
82 |
|
||||
83 | def instance_method_bound_with_another_parameter[S: PEP695Fix](self: S, other: S) -> S: ...
|
||||
|
|
||||
|
@ -335,12 +335,12 @@ PYI019_0.pyi:81:48: PYI019 [*] Use `Self` instead of custom TypeVar `S`
|
|||
83 83 | def instance_method_bound_with_another_parameter[S: PEP695Fix](self: S, other: S) -> S: ...
|
||||
84 84 |
|
||||
|
||||
PYI019_0.pyi:83:90: PYI019 [*] Use `Self` instead of custom TypeVar `S`
|
||||
PYI019_0.pyi:83:53: PYI019 [*] Use `Self` instead of custom TypeVar `S`
|
||||
|
|
||||
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
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ PYI019
|
||||
84 |
|
||||
85 | def instance_method_unbound_with_another_parameter[S](self: S, other: S) -> S: ...
|
||||
|
|
||||
|
@ -356,12 +356,12 @@ PYI019_0.pyi:83:90: PYI019 [*] Use `Self` instead of custom TypeVar `S`
|
|||
85 85 | def instance_method_unbound_with_another_parameter[S](self: S, other: S) -> S: ...
|
||||
86 86 |
|
||||
|
||||
PYI019_0.pyi:85:81: PYI019 [*] Use `Self` instead of custom TypeVar `S`
|
||||
PYI019_0.pyi:85:55: PYI019 [*] Use `Self` instead of custom TypeVar `S`
|
||||
|
|
||||
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
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ PYI019
|
||||
86 |
|
||||
87 | def multiple_type_vars[S, *Ts, T](self: S, other: S, /, *args: *Ts, a: T, b: list[T]) -> S: ...
|
||||
|
|
||||
|
@ -377,12 +377,12 @@ PYI019_0.pyi:85:81: PYI019 [*] Use `Self` instead of custom TypeVar `S`
|
|||
87 87 | def multiple_type_vars[S, *Ts, T](self: S, other: S, /, *args: *Ts, a: T, b: list[T]) -> S: ...
|
||||
88 88 |
|
||||
|
||||
PYI019_0.pyi:87:94: PYI019 [*] Use `Self` instead of custom TypeVar `S`
|
||||
PYI019_0.pyi:87:27: PYI019 [*] Use `Self` instead of custom TypeVar `S`
|
||||
|
|
||||
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
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ PYI019
|
||||
88 |
|
||||
89 | def mixing_old_and_new_style_type_vars[T](self: _S695, a: T, b: T) -> _S695: ...
|
||||
|
|
||||
|
@ -398,12 +398,12 @@ PYI019_0.pyi:87:94: PYI019 [*] Use `Self` instead of custom TypeVar `S`
|
|||
89 89 | def mixing_old_and_new_style_type_vars[T](self: _S695, a: T, b: T) -> _S695: ...
|
||||
90 90 |
|
||||
|
||||
PYI019_0.pyi:89:75: PYI019 [*] Use `Self` instead of custom TypeVar `_S695`
|
||||
PYI019_0.pyi:89:43: PYI019 [*] Use `Self` instead of custom TypeVar `_S695`
|
||||
|
|
||||
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
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ PYI019
|
||||
|
|
||||
= help: Replace TypeVar `_S695` with `Self`
|
||||
|
||||
|
@ -417,20 +417,50 @@ PYI019_0.pyi:89:75: PYI019 [*] Use `Self` instead of custom TypeVar `_S695`
|
|||
91 91 |
|
||||
92 92 | class InvalidButWeDoNotPanic:
|
||||
|
||||
PYI019_0.pyi:114:31: PYI019 Use `Self` instead of custom TypeVar `S`
|
||||
PYI019_0.pyi:94:10: PYI019 [*] Use `Self` instead of custom TypeVar `S`
|
||||
|
|
||||
92 | class InvalidButWeDoNotPanic:
|
||||
93 | @classmethod
|
||||
94 | def m[S](cls: type[S], /) -> S[int]: ...
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ PYI019
|
||||
95 | def n(self: S) -> S[int]: ...
|
||||
|
|
||||
= help: Replace TypeVar `S` with `Self`
|
||||
|
||||
ℹ Safe fix
|
||||
91 91 |
|
||||
92 92 | class InvalidButWeDoNotPanic:
|
||||
93 93 | @classmethod
|
||||
94 |- def m[S](cls: type[S], /) -> S[int]: ...
|
||||
94 |+ def m(cls, /) -> Self[int]: ...
|
||||
95 95 | def n(self: S) -> S[int]: ...
|
||||
96 96 |
|
||||
97 97 |
|
||||
|
||||
PYI019_0.pyi:114:10: PYI019 [*] Use `Self` instead of custom TypeVar `S`
|
||||
|
|
||||
112 | class SubscriptReturnType:
|
||||
113 | @classmethod
|
||||
114 | def m[S](cls: type[S]) -> type[S]: ... # PYI019, but no autofix (yet)
|
||||
| ^^^^^^^ PYI019
|
||||
114 | def m[S](cls: type[S]) -> type[S]: ... # PYI019
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ PYI019
|
||||
|
|
||||
= help: Replace TypeVar `S` with `Self`
|
||||
|
||||
PYI019_0.pyi:118:29: PYI019 [*] Use `Self` instead of custom TypeVar `S`
|
||||
ℹ Safe fix
|
||||
111 111 |
|
||||
112 112 | class SubscriptReturnType:
|
||||
113 113 | @classmethod
|
||||
114 |- def m[S](cls: type[S]) -> type[S]: ... # PYI019
|
||||
114 |+ def m(cls) -> type[Self]: ... # PYI019
|
||||
115 115 |
|
||||
116 116 |
|
||||
117 117 | class PEP695TypeParameterAtTheVeryEndOfTheList:
|
||||
|
||||
PYI019_0.pyi:118:10: PYI019 [*] Use `Self` instead of custom TypeVar `S`
|
||||
|
|
||||
117 | class PEP695TypeParameterAtTheVeryEndOfTheList:
|
||||
118 | def f[T, S](self: S) -> S: ...
|
||||
| ^ PYI019
|
||||
| ^^^^^^^^^^^^^^^^^^^^ PYI019
|
||||
|
|
||||
= help: Replace TypeVar `S` with `Self`
|
||||
|
||||
|
@ -444,11 +474,11 @@ PYI019_0.pyi:118:29: PYI019 [*] Use `Self` instead of custom TypeVar `S`
|
|||
120 120 |
|
||||
121 121 | class PEP695Again:
|
||||
|
||||
PYI019_0.pyi:122:100: PYI019 [*] Use `Self` instead of custom TypeVar `_S695`
|
||||
PYI019_0.pyi:122:26: PYI019 [*] Use `Self` instead of custom TypeVar `_S695`
|
||||
|
|
||||
121 | class PEP695Again:
|
||||
122 | def mixing_and_nested[T](self: _S695, a: list[_S695], b: dict[_S695, str | T | set[_S695]]) -> _S695: ...
|
||||
| ^^^^^ PYI019
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ PYI019
|
||||
123 | def also_uses_s695_but_should_not_be_edited(self, v: set[tuple[_S695]]) -> _S695: ...
|
||||
|
|
||||
= help: Replace TypeVar `_S695` with `Self`
|
||||
|
@ -463,14 +493,20 @@ PYI019_0.pyi:122:100: PYI019 [*] Use `Self` instead of custom TypeVar `_S695`
|
|||
124 124 |
|
||||
125 125 | @classmethod
|
||||
|
||||
PYI019_0.pyi:132:10: PYI019 [*] Use `Self` instead of custom TypeVar `S`
|
||||
PYI019_0.pyi:126:29: PYI019 [*] Use `Self` instead of custom TypeVar `S`
|
||||
|
|
||||
130 | a: T,
|
||||
131 | b: tuple[S, T]
|
||||
132 | ) -> S: ...
|
||||
| ^ PYI019
|
||||
125 | @classmethod
|
||||
126 | def comment_in_fix_range[T, S](
|
||||
| _____________________________^
|
||||
127 | | cls: type[ # Lorem ipsum
|
||||
128 | | S
|
||||
129 | | ],
|
||||
130 | | a: T,
|
||||
131 | | b: tuple[S, T]
|
||||
132 | | ) -> S: ...
|
||||
| |__________^ PYI019
|
||||
133 |
|
||||
134 | def comment_outside_fix_range[T, S](
|
||||
134 | def comment_outside_fix_range[T, S](
|
||||
|
|
||||
= help: Replace TypeVar `S` with `Self`
|
||||
|
||||
|
@ -493,12 +529,20 @@ PYI019_0.pyi:132:10: PYI019 [*] Use `Self` instead of custom TypeVar `S`
|
|||
134 132 | def comment_outside_fix_range[T, S](
|
||||
135 133 | self: S,
|
||||
|
||||
PYI019_0.pyi:141:10: PYI019 [*] Use `Self` instead of custom TypeVar `S`
|
||||
PYI019_0.pyi:134:34: PYI019 [*] Use `Self` instead of custom TypeVar `S`
|
||||
|
|
||||
139 | S, T
|
||||
140 | ]
|
||||
141 | ) -> S: ...
|
||||
| ^ PYI019
|
||||
132 | ) -> S: ...
|
||||
133 |
|
||||
134 | def comment_outside_fix_range[T, S](
|
||||
| __________________________________^
|
||||
135 | | self: S,
|
||||
136 | | a: T,
|
||||
137 | | b: tuple[
|
||||
138 | | # Lorem ipsum
|
||||
139 | | S, T
|
||||
140 | | ]
|
||||
141 | | ) -> S: ...
|
||||
| |__________^ PYI019
|
||||
|
|
||||
= help: Replace TypeVar `S` with `Self`
|
||||
|
||||
|
@ -518,3 +562,80 @@ PYI019_0.pyi:141:10: PYI019 [*] Use `Self` instead of custom TypeVar `S`
|
|||
140 140 | ]
|
||||
141 |- ) -> S: ...
|
||||
141 |+ ) -> Self: ...
|
||||
142 142 |
|
||||
143 143 |
|
||||
144 144 | class SelfNotUsedInReturnAnnotation:
|
||||
|
||||
PYI019_0.pyi:145:10: PYI019 [*] Use `Self` instead of custom TypeVar `S`
|
||||
|
|
||||
144 | class SelfNotUsedInReturnAnnotation:
|
||||
145 | def m[S](self: S, other: S) -> int: ...
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ PYI019
|
||||
146 | @classmethod
|
||||
147 | def n[S](cls: type[S], other: S) -> int: ...
|
||||
|
|
||||
= help: Replace TypeVar `S` with `Self`
|
||||
|
||||
ℹ Safe fix
|
||||
142 142 |
|
||||
143 143 |
|
||||
144 144 | class SelfNotUsedInReturnAnnotation:
|
||||
145 |- def m[S](self: S, other: S) -> int: ...
|
||||
145 |+ def m(self, other: Self) -> int: ...
|
||||
146 146 | @classmethod
|
||||
147 147 | def n[S](cls: type[S], other: S) -> int: ...
|
||||
148 148 |
|
||||
|
||||
PYI019_0.pyi:147:10: PYI019 [*] Use `Self` instead of custom TypeVar `S`
|
||||
|
|
||||
145 | def m[S](self: S, other: S) -> int: ...
|
||||
146 | @classmethod
|
||||
147 | def n[S](cls: type[S], other: S) -> int: ...
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ PYI019
|
||||
|
|
||||
= help: Replace TypeVar `S` with `Self`
|
||||
|
||||
ℹ Safe fix
|
||||
144 144 | class SelfNotUsedInReturnAnnotation:
|
||||
145 145 | def m[S](self: S, other: S) -> int: ...
|
||||
146 146 | @classmethod
|
||||
147 |- def n[S](cls: type[S], other: S) -> int: ...
|
||||
147 |+ def n(cls, other: Self) -> int: ...
|
||||
148 148 |
|
||||
149 149 |
|
||||
150 150 | class _NotATypeVar: ...
|
||||
|
||||
PYI019_0.pyi:161:10: PYI019 [*] Use `Self` instead of custom TypeVar `S`
|
||||
|
|
||||
160 | class NoReturnAnnotations:
|
||||
161 | def m[S](self: S, other: S): ...
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^ PYI019
|
||||
162 | @classmethod
|
||||
163 | def n[S](cls: type[S], other: S): ...
|
||||
|
|
||||
= help: Replace TypeVar `S` with `Self`
|
||||
|
||||
ℹ Safe fix
|
||||
158 158 | def y(self: type[_NotATypeVar]) -> _NotATypeVar: ...
|
||||
159 159 |
|
||||
160 160 | class NoReturnAnnotations:
|
||||
161 |- def m[S](self: S, other: S): ...
|
||||
161 |+ def m(self, other: Self): ...
|
||||
162 162 | @classmethod
|
||||
163 163 | def n[S](cls: type[S], other: S): ...
|
||||
|
||||
PYI019_0.pyi:163:10: PYI019 [*] Use `Self` instead of custom TypeVar `S`
|
||||
|
|
||||
161 | def m[S](self: S, other: S): ...
|
||||
162 | @classmethod
|
||||
163 | def n[S](cls: type[S], other: S): ...
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ PYI019
|
||||
|
|
||||
= help: Replace TypeVar `S` with `Self`
|
||||
|
||||
ℹ Safe fix
|
||||
160 160 | class NoReturnAnnotations:
|
||||
161 161 | def m[S](self: S, other: S): ...
|
||||
162 162 | @classmethod
|
||||
163 |- def n[S](cls: type[S], other: S): ...
|
||||
163 |+ def n(cls, other: Self): ...
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
---
|
||||
source: crates/ruff_linter/src/rules/flake8_pyi/mod.rs
|
||||
---
|
||||
PYI019_1.pyi:4:26: PYI019 [*] Use `Self` instead of custom TypeVar `S`
|
||||
PYI019_1.pyi:4:10: PYI019 [*] Use `Self` instead of custom TypeVar `S`
|
||||
|
|
||||
3 | class F:
|
||||
4 | def m[S](self: S) -> S: ...
|
||||
| ^ PYI019
|
||||
| ^^^^^^^^^^^^^^^^^ PYI019
|
||||
|
|
||||
= help: Replace TypeVar `S` with `Self`
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue