diff options
author | Zac Pullar-Strecker <[email protected]> | 2020-08-24 10:19:53 +0100 |
---|---|---|
committer | Zac Pullar-Strecker <[email protected]> | 2020-08-24 10:20:13 +0100 |
commit | 7bbca7a1b3f9293d2f5cc5745199bc5f8396f2f0 (patch) | |
tree | bdb47765991cb973b2cd5481a088fac636bd326c /crates/assists/src/handlers/remove_unused_param.rs | |
parent | ca464650eeaca6195891199a93f4f76cf3e7e697 (diff) | |
parent | e65d48d1fb3d4d91d9dc1148a7a836ff5c9a3c87 (diff) |
Merge remote-tracking branch 'upstream/master' into 503-hover-doc-links
Diffstat (limited to 'crates/assists/src/handlers/remove_unused_param.rs')
-rw-r--r-- | crates/assists/src/handlers/remove_unused_param.rs | 131 |
1 files changed, 131 insertions, 0 deletions
diff --git a/crates/assists/src/handlers/remove_unused_param.rs b/crates/assists/src/handlers/remove_unused_param.rs new file mode 100644 index 000000000..5fccca54b --- /dev/null +++ b/crates/assists/src/handlers/remove_unused_param.rs | |||
@@ -0,0 +1,131 @@ | |||
1 | use ide_db::{defs::Definition, search::Reference}; | ||
2 | use syntax::{ | ||
3 | algo::find_node_at_range, | ||
4 | ast::{self, ArgListOwner}, | ||
5 | AstNode, SyntaxNode, TextRange, T, | ||
6 | }; | ||
7 | use test_utils::mark; | ||
8 | |||
9 | use crate::{ | ||
10 | assist_context::AssistBuilder, utils::next_prev, AssistContext, AssistId, AssistKind, Assists, | ||
11 | }; | ||
12 | |||
13 | // Assist: remove_unused_param | ||
14 | // | ||
15 | // Removes unused function parameter. | ||
16 | // | ||
17 | // ``` | ||
18 | // fn frobnicate(x: i32<|>) {} | ||
19 | // | ||
20 | // fn main() { | ||
21 | // frobnicate(92); | ||
22 | // } | ||
23 | // ``` | ||
24 | // -> | ||
25 | // ``` | ||
26 | // fn frobnicate() {} | ||
27 | // | ||
28 | // fn main() { | ||
29 | // frobnicate(); | ||
30 | // } | ||
31 | // ``` | ||
32 | pub(crate) fn remove_unused_param(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { | ||
33 | let param: ast::Param = ctx.find_node_at_offset()?; | ||
34 | let ident_pat = match param.pat()? { | ||
35 | ast::Pat::IdentPat(it) => it, | ||
36 | _ => return None, | ||
37 | }; | ||
38 | let func = param.syntax().ancestors().find_map(ast::Fn::cast)?; | ||
39 | let param_position = func.param_list()?.params().position(|it| it == param)?; | ||
40 | |||
41 | let fn_def = { | ||
42 | let func = ctx.sema.to_def(&func)?; | ||
43 | Definition::ModuleDef(func.into()) | ||
44 | }; | ||
45 | |||
46 | let param_def = { | ||
47 | let local = ctx.sema.to_def(&ident_pat)?; | ||
48 | Definition::Local(local) | ||
49 | }; | ||
50 | if param_def.usages(&ctx.sema).at_least_one() { | ||
51 | mark::hit!(keep_used); | ||
52 | return None; | ||
53 | } | ||
54 | acc.add( | ||
55 | AssistId("remove_unused_param", AssistKind::Refactor), | ||
56 | "Remove unused parameter", | ||
57 | param.syntax().text_range(), | ||
58 | |builder| { | ||
59 | builder.delete(range_with_coma(param.syntax())); | ||
60 | for usage in fn_def.usages(&ctx.sema).all() { | ||
61 | process_usage(ctx, builder, usage, param_position); | ||
62 | } | ||
63 | }, | ||
64 | ) | ||
65 | } | ||
66 | |||
67 | fn process_usage( | ||
68 | ctx: &AssistContext, | ||
69 | builder: &mut AssistBuilder, | ||
70 | usage: Reference, | ||
71 | arg_to_remove: usize, | ||
72 | ) -> Option<()> { | ||
73 | let source_file = ctx.sema.parse(usage.file_range.file_id); | ||
74 | let call_expr: ast::CallExpr = | ||
75 | find_node_at_range(source_file.syntax(), usage.file_range.range)?; | ||
76 | if call_expr.expr()?.syntax().text_range() != usage.file_range.range { | ||
77 | return None; | ||
78 | } | ||
79 | let arg = call_expr.arg_list()?.args().nth(arg_to_remove)?; | ||
80 | |||
81 | builder.edit_file(usage.file_range.file_id); | ||
82 | builder.delete(range_with_coma(arg.syntax())); | ||
83 | |||
84 | Some(()) | ||
85 | } | ||
86 | |||
87 | fn range_with_coma(node: &SyntaxNode) -> TextRange { | ||
88 | let up_to = next_prev().find_map(|dir| { | ||
89 | node.siblings_with_tokens(dir) | ||
90 | .filter_map(|it| it.into_token()) | ||
91 | .find(|it| it.kind() == T![,]) | ||
92 | }); | ||
93 | let up_to = up_to.map_or(node.text_range(), |it| it.text_range()); | ||
94 | node.text_range().cover(up_to) | ||
95 | } | ||
96 | |||
97 | #[cfg(test)] | ||
98 | mod tests { | ||
99 | use crate::tests::{check_assist, check_assist_not_applicable}; | ||
100 | |||
101 | use super::*; | ||
102 | |||
103 | #[test] | ||
104 | fn remove_unused() { | ||
105 | check_assist( | ||
106 | remove_unused_param, | ||
107 | r#" | ||
108 | fn a() { foo(9, 2) } | ||
109 | fn foo(x: i32, <|>y: i32) { x; } | ||
110 | fn b() { foo(9, 2,) } | ||
111 | "#, | ||
112 | r#" | ||
113 | fn a() { foo(9) } | ||
114 | fn foo(x: i32) { x; } | ||
115 | fn b() { foo(9, ) } | ||
116 | "#, | ||
117 | ); | ||
118 | } | ||
119 | |||
120 | #[test] | ||
121 | fn keep_used() { | ||
122 | mark::check!(keep_used); | ||
123 | check_assist_not_applicable( | ||
124 | remove_unused_param, | ||
125 | r#" | ||
126 | fn foo(x: i32, <|>y: i32) { y; } | ||
127 | fn main() { foo(9, 2) } | ||
128 | "#, | ||
129 | ); | ||
130 | } | ||
131 | } | ||