1use syntax::{
2 AstNode, SyntaxKind, T,
3 ast::{self, syntax_factory::SyntaxFactory},
4 syntax_editor::Position,
5};
6
7use crate::{AssistContext, AssistId, Assists};
8
9pub(crate) fn remove_parentheses(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
25 let parens = ctx.find_node_at_offset::<ast::ParenExpr>()?;
26
27 let cursor_in_range =
28 parens.l_paren_token()?.text_range().contains_range(ctx.selection_trimmed())
29 || parens.r_paren_token()?.text_range().contains_range(ctx.selection_trimmed());
30 if !cursor_in_range {
31 return None;
32 }
33
34 let expr = parens.expr()?;
35
36 let parent = parens.syntax().parent()?;
37 if expr.needs_parens_in(&parent) {
38 return None;
39 }
40
41 let target = parens.syntax().text_range();
42 acc.add(
43 AssistId::refactor("remove_parentheses"),
44 "Remove redundant parentheses",
45 target,
46 |builder| {
47 let mut editor = builder.make_editor(parens.syntax());
48 let prev_token = parens.syntax().first_token().and_then(|it| it.prev_token());
49 let need_to_add_ws = match prev_token {
50 Some(it) => {
51 let tokens = [T![&], T![!], T!['('], T!['['], T!['{']];
52 it.kind() != SyntaxKind::WHITESPACE && !tokens.contains(&it.kind())
53 }
54 None => false,
55 };
56 if need_to_add_ws {
57 let make = SyntaxFactory::with_mappings();
58 editor.insert(Position::before(parens.syntax()), make.whitespace(" "));
59 editor.add_mappings(make.finish_with_mappings());
60 }
61 editor.replace(parens.syntax(), expr.syntax());
62 builder.add_file_edits(ctx.vfs_file_id(), editor);
63 },
64 )
65}
66
67#[cfg(test)]
68mod tests {
69 use crate::tests::{check_assist, check_assist_not_applicable};
70
71 use super::*;
72
73 #[test]
74 fn remove_parens_space() {
75 check_assist(
76 remove_parentheses,
77 r#"fn f() { match$0(true) {} }"#,
78 r#"fn f() { match true {} }"#,
79 );
80 }
81
82 #[test]
83 fn remove_parens_simple() {
84 check_assist(remove_parentheses, r#"fn f() { $0(2) + 2; }"#, r#"fn f() { 2 + 2; }"#);
85 check_assist(remove_parentheses, r#"fn f() { ($02) + 2; }"#, r#"fn f() { 2 + 2; }"#);
86 check_assist(remove_parentheses, r#"fn f() { (2)$0 + 2; }"#, r#"fn f() { 2 + 2; }"#);
87 check_assist(remove_parentheses, r#"fn f() { (2$0) + 2; }"#, r#"fn f() { 2 + 2; }"#);
88 }
89
90 #[test]
91 fn remove_parens_closure() {
92 check_assist(remove_parentheses, r#"fn f() { &$0(|| 42) }"#, r#"fn f() { &|| 42 }"#);
93
94 check_assist_not_applicable(remove_parentheses, r#"fn f() { $0(|| 42).f() }"#);
95 }
96
97 #[test]
98 fn remove_parens_if_let_chains() {
99 check_assist_not_applicable(
100 remove_parentheses,
101 r#"fn f() { if let true = $0(true && true) {} }"#,
102 );
103 }
104
105 #[test]
106 fn remove_parens_associativity() {
107 check_assist(
108 remove_parentheses,
109 r#"fn f() { $0(2 + 2) + 2; }"#,
110 r#"fn f() { 2 + 2 + 2; }"#,
111 );
112 check_assist_not_applicable(remove_parentheses, r#"fn f() { 2 + $0(2 + 2); }"#);
113 }
114
115 #[test]
116 fn remove_parens_precedence() {
117 check_assist(
118 remove_parentheses,
119 r#"fn f() { $0(2 * 3) + 1; }"#,
120 r#"fn f() { 2 * 3 + 1; }"#,
121 );
122 check_assist(remove_parentheses, r#"fn f() { ( $0(2) ); }"#, r#"fn f() { ( 2 ); }"#);
123 check_assist(remove_parentheses, r#"fn f() { $0(2?)?; }"#, r#"fn f() { 2??; }"#);
124 check_assist(remove_parentheses, r#"fn f() { f(($02 + 2)); }"#, r#"fn f() { f(2 + 2); }"#);
125 check_assist(
126 remove_parentheses,
127 r#"fn f() { (1<2) &&$0(3>4); }"#,
128 r#"fn f() { (1<2) && 3>4; }"#,
129 );
130 }
131
132 #[test]
133 fn remove_parens_doesnt_apply_precedence() {
134 check_assist_not_applicable(remove_parentheses, r#"fn f() { $0(2 + 2) * 8; }"#);
135 check_assist_not_applicable(remove_parentheses, r#"fn f() { $0(2 + 2).f(); }"#);
136 check_assist_not_applicable(remove_parentheses, r#"fn f() { $0(2 + 2).await; }"#);
137 check_assist_not_applicable(remove_parentheses, r#"fn f() { $0!(2..2); }"#);
138 }
139
140 #[test]
141 fn remove_parens_doesnt_apply_with_cursor_not_on_paren() {
142 check_assist_not_applicable(remove_parentheses, r#"fn f() { (2 +$0 2) }"#);
143 check_assist_not_applicable(remove_parentheses, r#"fn f() {$0 (2 + 2) }"#);
144 }
145
146 #[test]
147 fn remove_parens_doesnt_apply_when_expr_would_be_turned_into_a_statement() {
148 check_assist_not_applicable(remove_parentheses, r#"fn x() -> u8 { $0({ 0 } + 1) }"#);
149 check_assist_not_applicable(
150 remove_parentheses,
151 r#"fn x() -> u8 { $0(if true { 0 } else { 1 } + 1) }"#,
152 );
153 check_assist_not_applicable(remove_parentheses, r#"fn x() -> u8 { $0(loop {} + 1) }"#);
154 }
155
156 #[test]
157 fn remove_parens_doesnt_apply_weird_syntax_and_edge_cases() {
158 check_assist_not_applicable(remove_parentheses, r#"fn f() { for _ in $0(0..{3}) {} }"#);
160 check_assist_not_applicable(remove_parentheses, r#"fn f() { for _ in $0(S {}) {} }"#);
161 check_assist_not_applicable(remove_parentheses, r#"fn f() { if $0(S {} == 2) {} }"#);
162 check_assist_not_applicable(remove_parentheses, r#"fn f() { if $0(return) {} }"#);
163 }
164
165 #[test]
166 fn remove_parens_prefix_with_ret_like_prefix() {
167 check_assist(remove_parentheses, r#"fn f() { !$0(return) }"#, r#"fn f() { !return }"#);
168 check_assist(remove_parentheses, r#"fn f() { !$0(break) }"#, r#"fn f() { !break }"#);
170 check_assist(remove_parentheses, r#"fn f() { !$0(continue) }"#, r#"fn f() { !continue }"#);
171 check_assist(
172 remove_parentheses,
173 r#"fn f() { !$0(return false) }"#,
174 r#"fn f() { !return false }"#,
175 );
176
177 check_assist(
179 remove_parentheses,
180 r#"fn f() { true || $0(return) }"#,
181 r#"fn f() { true || return }"#,
182 );
183 check_assist(
184 remove_parentheses,
185 r#"fn f() { cond && $0(return) }"#,
186 r#"fn f() { cond && return }"#,
187 );
188 }
189
190 #[test]
191 fn remove_parens_return_with_value_followed_by_block() {
192 check_assist(
193 remove_parentheses,
194 r#"fn f() { if $0(return ()) {} }"#,
195 r#"fn f() { if return () {} }"#,
196 );
197 }
198
199 #[test]
200 fn remove_exprs_let_else_restrictions() {
201 check_assist_not_applicable(
203 remove_parentheses,
204 r#"fn f() { let _ = $0(S{}) else { return }; }"#,
205 );
206
207 check_assist_not_applicable(
209 remove_parentheses,
210 r#"fn f() { let _ = $0(false || false) else { return }; }"#,
211 );
212 check_assist_not_applicable(
213 remove_parentheses,
214 r#"fn f() { let _ = $0(true && true) else { return }; }"#,
215 );
216 }
217
218 #[test]
219 fn remove_parens_weird_places() {
220 check_assist(
221 remove_parentheses,
222 r#"fn f() { match () { _ =>$0(()) } }"#,
223 r#"fn f() { match () { _ => () } }"#,
224 );
225
226 check_assist(
227 remove_parentheses,
228 r#"fn x() -> u8 { { [$0({ 0 } + 1)] } }"#,
229 r#"fn x() -> u8 { { [{ 0 } + 1] } }"#,
230 );
231 }
232
233 #[test]
234 fn remove_parens_return_dot_f() {
235 check_assist(
236 remove_parentheses,
237 r#"fn f() { $0(return).f() }"#,
238 r#"fn f() { return.f() }"#,
239 );
240 }
241
242 #[test]
243 fn remove_parens_prefix_then_return_something() {
244 check_assist(
245 remove_parentheses,
246 r#"fn f() { &$0(return ()) }"#,
247 r#"fn f() { &return () }"#,
248 );
249 }
250
251 #[test]
252 fn remove_parens_return_in_unary_not() {
253 check_assist(
254 remove_parentheses,
255 r#"fn f() { cond && !$0(return) }"#,
256 r#"fn f() { cond && !return }"#,
257 );
258 check_assist(
259 remove_parentheses,
260 r#"fn f() { cond && !$0(return false) }"#,
261 r#"fn f() { cond && !return false }"#,
262 );
263 }
264
265 #[test]
266 fn remove_parens_return_in_disjunction_with_closure_risk() {
267 check_assist_not_applicable(
269 remove_parentheses,
270 r#"fn f() { let _x = true && $0(return) || true; }"#,
271 );
272 check_assist_not_applicable(
273 remove_parentheses,
274 r#"fn f() { let _x = true && !$0(return) || true; }"#,
275 );
276 check_assist_not_applicable(
277 remove_parentheses,
278 r#"fn f() { let _x = true && $0(return false) || true; }"#,
279 );
280 check_assist_not_applicable(
281 remove_parentheses,
282 r#"fn f() { let _x = true && !$0(return false) || true; }"#,
283 );
284 check_assist_not_applicable(
285 remove_parentheses,
286 r#"fn f() { let _x = true && $0(return) && true; }"#,
287 );
288 check_assist_not_applicable(
289 remove_parentheses,
290 r#"fn f() { let _x = true && !$0(return) && true; }"#,
291 );
292 check_assist_not_applicable(
293 remove_parentheses,
294 r#"fn f() { let _x = true && $0(return false) && true; }"#,
295 );
296 check_assist_not_applicable(
297 remove_parentheses,
298 r#"fn f() { let _x = true && !$0(return false) && true; }"#,
299 );
300 check_assist_not_applicable(
301 remove_parentheses,
302 r#"fn f() { let _x = $0(return) || true; }"#,
303 );
304 check_assist_not_applicable(
305 remove_parentheses,
306 r#"fn f() { let _x = $0(return) && true; }"#,
307 );
308 }
309
310 #[test]
311 fn remove_parens_return_in_disjunction_is_ok() {
312 check_assist(
313 remove_parentheses,
314 r#"fn f() { let _x = true || $0(return); }"#,
315 r#"fn f() { let _x = true || return; }"#,
316 );
317 check_assist(
318 remove_parentheses,
319 r#"fn f() { let _x = true && $0(return); }"#,
320 r#"fn f() { let _x = true && return; }"#,
321 );
322 }
323
324 #[test]
325 fn remove_parens_double_paren_stmt() {
326 check_assist(
327 remove_parentheses,
328 r#"fn x() -> u8 { $0(({ 0 } + 1)) }"#,
329 r#"fn x() -> u8 { ({ 0 } + 1) }"#,
330 );
331
332 check_assist(
333 remove_parentheses,
334 r#"fn x() -> u8 { (($0{ 0 } + 1)) }"#,
335 r#"fn x() -> u8 { ({ 0 } + 1) }"#,
336 );
337 }
338
339 #[test]
340 fn remove_parens_im_tired_of_naming_tests() {
341 check_assist(
342 remove_parentheses,
343 r#"fn f() { 2 + $0(return 2) }"#,
344 r#"fn f() { 2 + return 2 }"#,
345 );
346
347 check_assist_not_applicable(remove_parentheses, r#"fn f() { $0(return 2) + 2 }"#);
348 }
349
350 #[test]
351 fn remove_parens_indirect_calls() {
352 check_assist(
353 remove_parentheses,
354 r#"fn f(call: fn(usize), arg: usize) { $0(call)(arg); }"#,
355 r#"fn f(call: fn(usize), arg: usize) { call(arg); }"#,
356 );
357 check_assist(
358 remove_parentheses,
359 r#"fn f<F>(call: F, arg: usize) where F: Fn(usize) { $0(call)(arg); }"#,
360 r#"fn f<F>(call: F, arg: usize) where F: Fn(usize) { call(arg); }"#,
361 );
362
363 check_assist_not_applicable(
365 remove_parentheses,
366 r#"
367struct Foo<T> {
368 t: T,
369}
370
371impl Foo<fn(usize)> {
372 fn foo(&self, arg: usize) {
373 $0(self.t)(arg);
374 }
375}"#,
376 );
377 }
378}