-
Notifications
You must be signed in to change notification settings - Fork 899
/
unnecessary_literal_within_tuple_call.rs
121 lines (109 loc) · 3.46 KB
/
unnecessary_literal_within_tuple_call.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
use ruff_diagnostics::{AlwaysFixableViolation, Diagnostic, Edit, Fix};
use ruff_macros::{derive_message_formats, violation};
use ruff_python_ast::{self as ast, Expr};
use ruff_text_size::Ranged;
use crate::checkers::ast::Checker;
use super::helpers;
/// ## What it does
/// Checks for `tuple` calls that take unnecessary list or tuple literals as
/// arguments.
///
/// ## Why is this bad?
/// It's unnecessary to use a list or tuple literal within a `tuple()` call,
/// since there is a literal syntax for these types.
///
/// If a list literal was passed, then it should be rewritten as a `tuple`
/// literal. Otherwise, if a tuple literal was passed, then the outer call
/// to `tuple()` should be removed.
///
/// ## Examples
/// ```python
/// tuple([1, 2])
/// tuple((1, 2))
/// ```
///
/// Use instead:
/// ```python
/// (1, 2)
/// (1, 2)
/// ```
///
/// ## 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 UnnecessaryLiteralWithinTupleCall {
literal: String,
}
impl AlwaysFixableViolation for UnnecessaryLiteralWithinTupleCall {
#[derive_message_formats]
fn message(&self) -> String {
let UnnecessaryLiteralWithinTupleCall { literal } = self;
if literal == "list" {
format!(
"Unnecessary `{literal}` literal passed to `tuple()` (rewrite as a `tuple` literal)"
)
} else {
format!(
"Unnecessary `{literal}` literal passed to `tuple()` (remove the outer call to `tuple()`)"
)
}
}
fn fix_title(&self) -> String {
let UnnecessaryLiteralWithinTupleCall { literal } = self;
{
if literal == "list" {
"Rewrite as a `tuple` literal".to_string()
} else {
"Remove outer `tuple` call".to_string()
}
}
}
}
/// C409
pub(crate) fn unnecessary_literal_within_tuple_call(checker: &mut Checker, call: &ast::ExprCall) {
if !call.arguments.keywords.is_empty() {
return;
}
let Some(argument) = helpers::exactly_one_argument_with_matching_function(
"tuple",
&call.func,
&call.arguments.args,
&call.arguments.keywords,
) else {
return;
};
if !checker.semantic().is_builtin("tuple") {
return;
}
let argument_kind = match argument {
Expr::Tuple(_) => "tuple",
Expr::List(_) => "list",
_ => return,
};
let mut diagnostic = Diagnostic::new(
UnnecessaryLiteralWithinTupleCall {
literal: argument_kind.to_string(),
},
call.range(),
);
// Convert `tuple([1, 2])` to `(1, 2)`
diagnostic.set_fix({
let elts: Vec<Expr> = match argument {
Expr::List(list_expr) => list_expr.elts.clone(),
Expr::Tuple(tuple_expr) => tuple_expr.elts.clone(),
_ => return,
};
// Replace [1, 2] -> (1, 2) or [1] -> (1,)
let tuple: String = if let [elt] = elts.as_slice() {
let elt: &str = checker.locator().slice(elt);
format!("({elt},)")
} else {
let elt_set: &str = checker.locator().slice(argument);
format!("({})", &elt_set[1..elt_set.len() - 1])
};
let edit = Edit::replacement(tuple, call.start(), call.end());
Fix::unsafe_edit(edit)
});
checker.diagnostics.push(diagnostic);
}