@@ -71,20 +71,8 @@ use ruff_python_ast::PythonVersion;
71
71
/// The fix is only marked as unsafe if there is the possibility that it might delete a comment
72
72
/// from your code.
73
73
///
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
- ///
86
74
/// [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/
88
76
/// [PYI018]: https://docs.astral.sh/ruff/rules/unused-private-type-var/
89
77
/// [type parameter list]: https://docs.python.org/3/reference/compound_stmts.html#type-params
90
78
/// [Self]: https://docs.python.org/3/library/typing.html#typing.Self
@@ -162,73 +150,33 @@ pub(crate) fn custom_type_var_instead_of_self(
162
150
& checker. settings . pep8_naming . staticmethod_decorators ,
163
151
) ;
164
152
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
+ } ) ,
219
162
FunctionType :: Function | FunctionType :: StaticMethod => return None ,
220
163
} ;
221
164
222
165
let custom_typevar = method. custom_typevar ( semantic, binding. scope ) ?;
223
166
167
+ let function_header_end = returns
168
+ . as_deref ( )
169
+ . map ( Ranged :: end)
170
+ . unwrap_or_else ( || parameters. end ( ) ) ;
171
+
224
172
let mut diagnostic = Diagnostic :: new (
225
173
CustomTypeVarForSelf {
226
174
typevar_name : custom_typevar. name ( checker. source ( ) ) . to_string ( ) ,
227
175
} ,
228
- diagnostic_range ,
176
+ TextRange :: new ( function_name . end ( ) , function_header_end ) ,
229
177
) ;
230
178
231
- diagnostic. try_set_optional_fix ( || {
179
+ diagnostic. try_set_fix ( || {
232
180
replace_custom_typevar_with_self (
233
181
checker,
234
182
function_def,
@@ -244,9 +192,7 @@ pub(crate) fn custom_type_var_instead_of_self(
244
192
#[ derive( Debug ) ]
245
193
enum Method < ' a > {
246
194
Class ( ClassMethod < ' a > ) ,
247
- PreviewClass ( PreviewClassMethod < ' a > ) ,
248
195
Instance ( InstanceMethod < ' a > ) ,
249
- PreviewInstance ( PreviewInstanceMethod < ' a > ) ,
250
196
}
251
197
252
198
impl Method < ' _ > {
@@ -257,86 +203,18 @@ impl Method<'_> {
257
203
) -> Option < TypeVar < ' a > > {
258
204
match self {
259
205
Self :: Class ( class_method) => class_method. custom_typevar ( semantic, scope) ,
260
- Self :: PreviewClass ( class_method) => class_method. custom_typevar ( semantic, scope) ,
261
206
Self :: Instance ( instance_method) => instance_method. custom_typevar ( semantic) ,
262
- Self :: PreviewInstance ( instance_method) => instance_method. custom_typevar ( semantic) ,
263
207
}
264
208
}
265
209
}
266
210
267
211
#[ derive( Debug ) ]
268
212
struct ClassMethod < ' a > {
269
213
cls_annotation : & ' a ast:: Expr ,
270
- returns : & ' a ast:: Expr ,
271
214
type_params : Option < & ' a ast:: TypeParams > ,
272
215
}
273
216
274
217
impl 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 < ' _ > {
340
218
/// Returns `Some(typevar)` if the class method is annotated with
341
219
/// a custom `TypeVar` for the `cls` parameter
342
220
fn custom_typevar < ' a > (
@@ -360,90 +238,30 @@ impl PreviewClassMethod<'_> {
360
238
return None ;
361
239
}
362
240
363
- custom_typevar_preview ( cls_annotation_typevar, self . type_params , semantic)
241
+ custom_typevar ( cls_annotation_typevar, self . type_params , semantic)
364
242
}
365
243
}
366
244
367
245
#[ derive( Debug ) ]
368
246
struct InstanceMethod < ' a > {
369
247
self_annotation : & ' a ast:: Expr ,
370
- returns : & ' a ast:: Expr ,
371
248
type_params : Option < & ' a ast:: TypeParams > ,
372
249
}
373
250
374
251
impl 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 < ' _ > {
412
252
/// Returns `Some(typevar)` if the instance method is annotated with
413
253
/// a custom `TypeVar` for the `self` parameter
414
254
fn custom_typevar < ' a > ( & ' a self , semantic : & ' a SemanticModel < ' a > ) -> Option < TypeVar < ' a > > {
415
- custom_typevar_preview (
255
+ custom_typevar (
416
256
self . self_annotation . as_name_expr ( ) ?,
417
257
self . type_params ,
418
258
semantic,
419
259
)
420
260
}
421
261
}
422
262
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
-
445
263
/// Returns `Some(TypeVar)` if `typevar_expr` refers to a `TypeVar` binding
446
- fn custom_typevar_preview < ' a > (
264
+ fn custom_typevar < ' a > (
447
265
typevar_expr : & ' a ast:: ExprName ,
448
266
type_params : Option < & ast:: TypeParams > ,
449
267
semantic : & ' a SemanticModel < ' a > ,
@@ -497,11 +315,7 @@ fn replace_custom_typevar_with_self(
497
315
custom_typevar : TypeVar ,
498
316
self_or_cls_parameter : & ast:: ParameterWithDefault ,
499
317
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 > {
505
319
// (1) Import `Self` (if necessary)
506
320
let ( import_edit, self_symbol_binding) = import_self ( checker, function_def. start ( ) ) ?;
507
321
@@ -513,9 +327,9 @@ fn replace_custom_typevar_with_self(
513
327
514
328
// (3) If it was a PEP-695 type variable, remove that `TypeVar` from the PEP-695 type-parameter list
515
329
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
+ ) ? ;
519
333
let deletion_edit = remove_pep695_typevar_declaration ( type_params, custom_typevar)
520
334
. context ( "Failed to find a `TypeVar` in the type params that matches the binding" ) ?;
521
335
other_edits. push ( deletion_edit) ;
@@ -546,11 +360,11 @@ fn replace_custom_typevar_with_self(
546
360
Applicability :: Safe
547
361
} ;
548
362
549
- Ok ( Some ( Fix :: applicable_edits (
363
+ Ok ( Fix :: applicable_edits (
550
364
import_edit,
551
365
other_edits,
552
366
applicability,
553
- ) ) )
367
+ ) )
554
368
}
555
369
556
370
/// Attempt to create an [`Edit`] that imports `Self`.
0 commit comments