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