1
1
use ruff_diagnostics:: { AlwaysFixableViolation , Diagnostic , Edit , Fix } ;
2
2
use ruff_macros:: { derive_message_formats, violation} ;
3
3
use ruff_python_ast as ast;
4
+ use ruff_python_ast:: comparable:: ComparableExpr ;
5
+ use ruff_python_ast:: ExprGenerator ;
4
6
use ruff_text_size:: { Ranged , TextSize } ;
5
7
6
8
use crate :: checkers:: ast:: Checker ;
@@ -9,37 +11,53 @@ use super::helpers;
9
11
10
12
/// ## What it does
11
13
/// Checks for unnecessary generators that can be rewritten as `list`
12
- /// comprehensions.
14
+ /// comprehensions (or with `list` directly) .
13
15
///
14
16
/// ## Why is this bad?
15
17
/// It is unnecessary to use `list` around a generator expression, since
16
18
/// there are equivalent comprehensions for these types. Using a
17
19
/// comprehension is clearer and more idiomatic.
18
20
///
21
+ /// Further, if the comprehension can be removed entirely, as in the case of
22
+ /// `list(x for x in foo)`, it's better to use `list(foo)` directly, since it's
23
+ /// even more direct.
24
+ ///
19
25
/// ## Examples
20
26
/// ```python
21
27
/// list(f(x) for x in foo)
28
+ /// list(x for x in foo)
22
29
/// ```
23
30
///
24
31
/// Use instead:
25
32
/// ```python
26
33
/// [f(x) for x in foo]
34
+ /// list(foo)
27
35
/// ```
28
36
///
29
37
/// ## Fix safety
30
38
/// This rule's fix is marked as unsafe, as it may occasionally drop comments
31
39
/// when rewriting the call. In most cases, though, comments will be preserved.
32
40
#[ violation]
33
- pub struct UnnecessaryGeneratorList ;
41
+ pub struct UnnecessaryGeneratorList {
42
+ short_circuit : bool ,
43
+ }
34
44
35
45
impl AlwaysFixableViolation for UnnecessaryGeneratorList {
36
46
#[ derive_message_formats]
37
47
fn message ( & self ) -> String {
38
- format ! ( "Unnecessary generator (rewrite as a `list` comprehension)" )
48
+ if self . short_circuit {
49
+ format ! ( "Unnecessary generator (rewrite using `list()`" )
50
+ } else {
51
+ format ! ( "Unnecessary generator (rewrite as a `list` comprehension)" )
52
+ }
39
53
}
40
54
41
55
fn fix_title ( & self ) -> String {
42
- "Rewrite as a `list` comprehension" . to_string ( )
56
+ if self . short_circuit {
57
+ "Rewrite using `list()`" . to_string ( )
58
+ } else {
59
+ "Rewrite as a `list` comprehension" . to_string ( )
60
+ }
43
61
}
44
62
}
45
63
@@ -56,28 +74,59 @@ pub(crate) fn unnecessary_generator_list(checker: &mut Checker, call: &ast::Expr
56
74
if !checker. semantic ( ) . is_builtin ( "list" ) {
57
75
return ;
58
76
}
59
- if argument. is_generator_expr ( ) {
60
- let mut diagnostic = Diagnostic :: new ( UnnecessaryGeneratorList , call. range ( ) ) ;
61
77
62
- // Convert `list(x for x in y)` to `[x for x in y]`.
63
- diagnostic. set_fix ( {
64
- // Replace `list(` with `[`.
65
- let call_start = Edit :: replacement (
66
- "[" . to_string ( ) ,
67
- call. start ( ) ,
68
- call. arguments . start ( ) + TextSize :: from ( 1 ) ,
69
- ) ;
78
+ let Some ( ExprGenerator {
79
+ elt, generators, ..
80
+ } ) = argument. as_generator_expr ( )
81
+ else {
82
+ return ;
83
+ } ;
84
+
85
+ // Short-circuit: given `list(x for x in y)`, generate `list(y)` (in lieu of `[x for x in y]`).
86
+ if let [ generator] = generators. as_slice ( ) {
87
+ if generator. ifs . is_empty ( ) && !generator. is_async {
88
+ if ComparableExpr :: from ( elt) == ComparableExpr :: from ( & generator. target ) {
89
+ let mut diagnostic = Diagnostic :: new (
90
+ UnnecessaryGeneratorList {
91
+ short_circuit : true ,
92
+ } ,
93
+ call. range ( ) ,
94
+ ) ;
95
+ let iterator = format ! ( "list({})" , checker. locator( ) . slice( generator. iter. range( ) ) ) ;
96
+ diagnostic. set_fix ( Fix :: unsafe_edit ( Edit :: range_replacement (
97
+ iterator,
98
+ call. range ( ) ,
99
+ ) ) ) ;
100
+ checker. diagnostics . push ( diagnostic) ;
101
+ return ;
102
+ }
103
+ }
104
+ }
105
+
106
+ // Convert `list(f(x) for x in y)` to `[f(x) for x in y]`.
107
+ let mut diagnostic = Diagnostic :: new (
108
+ UnnecessaryGeneratorList {
109
+ short_circuit : false ,
110
+ } ,
111
+ call. range ( ) ,
112
+ ) ;
113
+ diagnostic. set_fix ( {
114
+ // Replace `list(` with `[`.
115
+ let call_start = Edit :: replacement (
116
+ "[" . to_string ( ) ,
117
+ call. start ( ) ,
118
+ call. arguments . start ( ) + TextSize :: from ( 1 ) ,
119
+ ) ;
70
120
71
- // Replace `)` with `]`.
72
- let call_end = Edit :: replacement (
73
- "]" . to_string ( ) ,
74
- call. arguments . end ( ) - TextSize :: from ( 1 ) ,
75
- call. end ( ) ,
76
- ) ;
121
+ // Replace `)` with `]`.
122
+ let call_end = Edit :: replacement (
123
+ "]" . to_string ( ) ,
124
+ call. arguments . end ( ) - TextSize :: from ( 1 ) ,
125
+ call. end ( ) ,
126
+ ) ;
77
127
78
- Fix :: unsafe_edits ( call_start, [ call_end] )
79
- } ) ;
128
+ Fix :: unsafe_edits ( call_start, [ call_end] )
129
+ } ) ;
80
130
81
- checker. diagnostics . push ( diagnostic) ;
82
- }
131
+ checker. diagnostics . push ( diagnostic) ;
83
132
}
0 commit comments