@@ -71,20 +71,8 @@ use ruff_python_ast::PythonVersion;
7171/// The fix is only marked as unsafe if there is the possibility that it might delete a comment
7272/// from your code.
7373///
74- /// ## Preview-mode behaviour
75- /// This rule's behaviour has several differences when [`preview`] mode is enabled:
76- /// 1. The fix for this rule is currently only available if `preview` mode is enabled.
77- /// 2. By default, this rule is only applied to methods that have return-type annotations,
78- /// and the range of the diagnostic is the range of the return-type annotation.
79- /// In preview mode, this rule is also applied to some methods that do not have
80- /// return-type annotations. The range of the diagnostic is the range of the function
81- /// header (from the end of the function name to the end of the parameters).
82- /// 3. In `preview` mode, the rule uses different logic to determine whether an annotation
83- /// refers to a type variable. The `preview`-mode logic is more accurate, but may lead
84- /// to more methods being flagged than if `preview` mode is disabled.
85- ///
8674/// [PEP 673]: https://peps.python.org/pep-0673/#motivation
87- /// [PEP 695]: https://peps.python.org/pep-0695/
75+ /// [PEP- 695]: https://peps.python.org/pep-0695/
8876/// [PYI018]: https://docs.astral.sh/ruff/rules/unused-private-type-var/
8977/// [type parameter list]: https://docs.python.org/3/reference/compound_stmts.html#type-params
9078/// [Self]: https://docs.python.org/3/library/typing.html#typing.Self
@@ -162,73 +150,33 @@ pub(crate) fn custom_type_var_instead_of_self(
162150 & checker. settings . pep8_naming . staticmethod_decorators ,
163151 ) ;
164152
165- let function_header_end = returns
166- . as_deref ( )
167- . map ( Ranged :: end)
168- . unwrap_or_else ( || parameters. end ( ) ) ;
169-
170- // In stable mode, we only emit the diagnostic on methods that have a return type annotation.
171- // In preview mode, we have a more principled approach to determine if an annotation refers
172- // to a type variable, and we emit the diagnostic on some methods that do not have return
173- // annotations.
174- let ( method, diagnostic_range) = match function_kind {
175- FunctionType :: ClassMethod | FunctionType :: NewMethod => {
176- if checker. settings . preview . is_enabled ( ) {
177- (
178- Method :: PreviewClass ( PreviewClassMethod {
179- cls_annotation : self_or_cls_annotation,
180- type_params,
181- } ) ,
182- TextRange :: new ( function_name. end ( ) , function_header_end) ,
183- )
184- } else {
185- returns. as_deref ( ) . map ( |returns| {
186- (
187- Method :: Class ( ClassMethod {
188- cls_annotation : self_or_cls_annotation,
189- returns,
190- type_params,
191- } ) ,
192- returns. range ( ) ,
193- )
194- } ) ?
195- }
196- }
197- FunctionType :: Method => {
198- if checker. settings . preview . is_enabled ( ) {
199- (
200- Method :: PreviewInstance ( PreviewInstanceMethod {
201- self_annotation : self_or_cls_annotation,
202- type_params,
203- } ) ,
204- TextRange :: new ( function_name. end ( ) , function_header_end) ,
205- )
206- } else {
207- returns. as_deref ( ) . map ( |returns| {
208- (
209- Method :: Instance ( InstanceMethod {
210- self_annotation : self_or_cls_annotation,
211- returns,
212- type_params,
213- } ) ,
214- returns. range ( ) ,
215- )
216- } ) ?
217- }
218- }
153+ let method = match function_kind {
154+ FunctionType :: ClassMethod | FunctionType :: NewMethod => Method :: Class ( ClassMethod {
155+ cls_annotation : self_or_cls_annotation,
156+ type_params,
157+ } ) ,
158+ FunctionType :: Method => Method :: Instance ( InstanceMethod {
159+ self_annotation : self_or_cls_annotation,
160+ type_params,
161+ } ) ,
219162 FunctionType :: Function | FunctionType :: StaticMethod => return None ,
220163 } ;
221164
222165 let custom_typevar = method. custom_typevar ( semantic, binding. scope ) ?;
223166
167+ let function_header_end = returns
168+ . as_deref ( )
169+ . map ( Ranged :: end)
170+ . unwrap_or_else ( || parameters. end ( ) ) ;
171+
224172 let mut diagnostic = Diagnostic :: new (
225173 CustomTypeVarForSelf {
226174 typevar_name : custom_typevar. name ( checker. source ( ) ) . to_string ( ) ,
227175 } ,
228- diagnostic_range ,
176+ TextRange :: new ( function_name . end ( ) , function_header_end ) ,
229177 ) ;
230178
231- diagnostic. try_set_optional_fix ( || {
179+ diagnostic. try_set_fix ( || {
232180 replace_custom_typevar_with_self (
233181 checker,
234182 function_def,
@@ -244,9 +192,7 @@ pub(crate) fn custom_type_var_instead_of_self(
244192#[ derive( Debug ) ]
245193enum Method < ' a > {
246194 Class ( ClassMethod < ' a > ) ,
247- PreviewClass ( PreviewClassMethod < ' a > ) ,
248195 Instance ( InstanceMethod < ' a > ) ,
249- PreviewInstance ( PreviewInstanceMethod < ' a > ) ,
250196}
251197
252198impl Method < ' _ > {
@@ -257,86 +203,18 @@ impl Method<'_> {
257203 ) -> Option < TypeVar < ' a > > {
258204 match self {
259205 Self :: Class ( class_method) => class_method. custom_typevar ( semantic, scope) ,
260- Self :: PreviewClass ( class_method) => class_method. custom_typevar ( semantic, scope) ,
261206 Self :: Instance ( instance_method) => instance_method. custom_typevar ( semantic) ,
262- Self :: PreviewInstance ( instance_method) => instance_method. custom_typevar ( semantic) ,
263207 }
264208 }
265209}
266210
267211#[ derive( Debug ) ]
268212struct ClassMethod < ' a > {
269213 cls_annotation : & ' a ast:: Expr ,
270- returns : & ' a ast:: Expr ,
271214 type_params : Option < & ' a ast:: TypeParams > ,
272215}
273216
274217impl ClassMethod < ' _ > {
275- /// Returns `Some(typevar)` if the class method is annotated with
276- /// a custom `TypeVar` that is likely private.
277- fn custom_typevar < ' a > (
278- & ' a self ,
279- semantic : & ' a SemanticModel < ' a > ,
280- scope : ScopeId ,
281- ) -> Option < TypeVar < ' a > > {
282- let ast:: ExprSubscript {
283- value : cls_annotation_value,
284- slice : cls_annotation_typevar,
285- ..
286- } = self . cls_annotation . as_subscript_expr ( ) ?;
287-
288- let cls_annotation_typevar = cls_annotation_typevar. as_name_expr ( ) ?;
289- let cls_annotation_typevar_name = & cls_annotation_typevar. id ;
290- let ast:: ExprName { id, .. } = cls_annotation_value. as_name_expr ( ) ?;
291-
292- if id != "type" {
293- return None ;
294- }
295-
296- if !semantic. has_builtin_binding_in_scope ( "type" , scope) {
297- return None ;
298- }
299-
300- let return_annotation_typevar = match self . returns {
301- ast:: Expr :: Name ( ast:: ExprName { id, .. } ) => id,
302- ast:: Expr :: Subscript ( ast:: ExprSubscript { value, slice, .. } ) => {
303- let return_annotation_typevar = slice. as_name_expr ( ) ?;
304- let ast:: ExprName { id, .. } = value. as_name_expr ( ) ?;
305- if id != "type" {
306- return None ;
307- }
308- & return_annotation_typevar. id
309- }
310- _ => return None ,
311- } ;
312-
313- if cls_annotation_typevar_name != return_annotation_typevar {
314- return None ;
315- }
316-
317- if !is_likely_private_typevar ( cls_annotation_typevar_name, self . type_params ) {
318- return None ;
319- }
320-
321- semantic
322- . resolve_name ( cls_annotation_typevar)
323- . map ( |binding_id| TypeVar ( semantic. binding ( binding_id) ) )
324- }
325- }
326-
327- /// Struct for implementing this rule as applied to classmethods in preview mode.
328- ///
329- /// In stable mode, we only emit this diagnostic on methods that have return annotations,
330- /// so the stable-mode version of this struct has a `returns: &ast::Expr` field. In preview
331- /// mode, we also emit this diagnostic on methods that do not have return annotations, so
332- /// the preview-mode version of this struct does not have a `returns` field.
333- #[ derive( Debug ) ]
334- struct PreviewClassMethod < ' a > {
335- cls_annotation : & ' a ast:: Expr ,
336- type_params : Option < & ' a ast:: TypeParams > ,
337- }
338-
339- impl PreviewClassMethod < ' _ > {
340218 /// Returns `Some(typevar)` if the class method is annotated with
341219 /// a custom `TypeVar` for the `cls` parameter
342220 fn custom_typevar < ' a > (
@@ -360,90 +238,30 @@ impl PreviewClassMethod<'_> {
360238 return None ;
361239 }
362240
363- custom_typevar_preview ( cls_annotation_typevar, self . type_params , semantic)
241+ custom_typevar ( cls_annotation_typevar, self . type_params , semantic)
364242 }
365243}
366244
367245#[ derive( Debug ) ]
368246struct InstanceMethod < ' a > {
369247 self_annotation : & ' a ast:: Expr ,
370- returns : & ' a ast:: Expr ,
371248 type_params : Option < & ' a ast:: TypeParams > ,
372249}
373250
374251impl InstanceMethod < ' _ > {
375- /// Returns `Some(typevar)` if the instance method is annotated with
376- /// a custom `TypeVar` that is likely private.
377- fn custom_typevar < ' a > ( & ' a self , semantic : & ' a SemanticModel < ' a > ) -> Option < TypeVar < ' a > > {
378- let self_annotation = self . self_annotation . as_name_expr ( ) ?;
379- let first_arg_type = & self_annotation. id ;
380-
381- let ast:: ExprName {
382- id : return_type, ..
383- } = self . returns . as_name_expr ( ) ?;
384-
385- if first_arg_type != return_type {
386- return None ;
387- }
388-
389- if !is_likely_private_typevar ( first_arg_type, self . type_params ) {
390- return None ;
391- }
392-
393- semantic
394- . resolve_name ( self_annotation)
395- . map ( |binding_id| TypeVar ( semantic. binding ( binding_id) ) )
396- }
397- }
398-
399- /// Struct for implementing this rule as applied to instance methods in preview mode.
400- ///
401- /// In stable mode, we only emit this diagnostic on methods that have return annotations,
402- /// so the stable-mode version of this struct has a `returns: &ast::Expr` field. In preview
403- /// mode, we also emit this diagnostic on methods that do not have return annotations, so
404- /// the preview-mode version of this struct does not have a `returns` field.
405- #[ derive( Debug ) ]
406- struct PreviewInstanceMethod < ' a > {
407- self_annotation : & ' a ast:: Expr ,
408- type_params : Option < & ' a ast:: TypeParams > ,
409- }
410-
411- impl PreviewInstanceMethod < ' _ > {
412252 /// Returns `Some(typevar)` if the instance method is annotated with
413253 /// a custom `TypeVar` for the `self` parameter
414254 fn custom_typevar < ' a > ( & ' a self , semantic : & ' a SemanticModel < ' a > ) -> Option < TypeVar < ' a > > {
415- custom_typevar_preview (
255+ custom_typevar (
416256 self . self_annotation . as_name_expr ( ) ?,
417257 self . type_params ,
418258 semantic,
419259 )
420260 }
421261}
422262
423- /// Returns `true` if the type variable is likely private.
424- ///
425- /// This routine is only used if `--preview` is not enabled,
426- /// as it uses heuristics to determine if an annotation uses a type variable.
427- /// In preview mode, we apply a more principled approach.
428- fn is_likely_private_typevar ( type_var_name : & str , type_params : Option < & ast:: TypeParams > ) -> bool {
429- // Ex) `_T`
430- if type_var_name. starts_with ( '_' ) {
431- return true ;
432- }
433- // Ex) `class Foo[T]: ...`
434- type_params. is_some_and ( |type_params| {
435- type_params. iter ( ) . any ( |type_param| {
436- if let ast:: TypeParam :: TypeVar ( ast:: TypeParamTypeVar { name, .. } ) = type_param {
437- name == type_var_name
438- } else {
439- false
440- }
441- } )
442- } )
443- }
444-
445263/// Returns `Some(TypeVar)` if `typevar_expr` refers to a `TypeVar` binding
446- fn custom_typevar_preview < ' a > (
264+ fn custom_typevar < ' a > (
447265 typevar_expr : & ' a ast:: ExprName ,
448266 type_params : Option < & ast:: TypeParams > ,
449267 semantic : & ' a SemanticModel < ' a > ,
@@ -497,11 +315,7 @@ fn replace_custom_typevar_with_self(
497315 custom_typevar : TypeVar ,
498316 self_or_cls_parameter : & ast:: ParameterWithDefault ,
499317 self_or_cls_annotation : & ast:: Expr ,
500- ) -> anyhow:: Result < Option < Fix > > {
501- if checker. settings . preview . is_disabled ( ) {
502- return Ok ( None ) ;
503- }
504-
318+ ) -> anyhow:: Result < Fix > {
505319 // (1) Import `Self` (if necessary)
506320 let ( import_edit, self_symbol_binding) = import_self ( checker, function_def. start ( ) ) ?;
507321
@@ -513,9 +327,9 @@ fn replace_custom_typevar_with_self(
513327
514328 // (3) If it was a PEP-695 type variable, remove that `TypeVar` from the PEP-695 type-parameter list
515329 if custom_typevar. is_pep695_typevar ( ) {
516- let Some ( type_params) = function_def. type_params . as_deref ( ) else {
517- bail ! ( "Should not be possible to have a type parameter without a type parameter list" ) ;
518- } ;
330+ let type_params = function_def. type_params . as_deref ( ) . context (
331+ "Should not be possible to have a type parameter without a type parameter list" ,
332+ ) ? ;
519333 let deletion_edit = remove_pep695_typevar_declaration ( type_params, custom_typevar)
520334 . context ( "Failed to find a `TypeVar` in the type params that matches the binding" ) ?;
521335 other_edits. push ( deletion_edit) ;
@@ -546,11 +360,11 @@ fn replace_custom_typevar_with_self(
546360 Applicability :: Safe
547361 } ;
548362
549- Ok ( Some ( Fix :: applicable_edits (
363+ Ok ( Fix :: applicable_edits (
550364 import_edit,
551365 other_edits,
552366 applicability,
553- ) ) )
367+ ) )
554368}
555369
556370/// Attempt to create an [`Edit`] that imports `Self`.
0 commit comments