aboutsummaryrefslogtreecommitdiff
path: root/crates/assists
diff options
context:
space:
mode:
authorMichael-F-Bryan <[email protected]>2021-01-02 05:47:35 +0000
committerMichael-F-Bryan <[email protected]>2021-01-04 20:18:57 +0000
commit7b4b4ef02681053299dda5111c0d4b0113e29224 (patch)
tree1c84b3438d96f522efd1d2e7039c585291e56391 /crates/assists
parent0708bfeb7270923be5a2059ad5b99de183e667ba (diff)
Created an inline-function assist (fixes #6863)
Diffstat (limited to 'crates/assists')
-rw-r--r--crates/assists/src/handlers/inline_function.rs202
-rw-r--r--crates/assists/src/lib.rs2
-rw-r--r--crates/assists/src/tests/generated.rs23
3 files changed, 227 insertions, 0 deletions
diff --git a/crates/assists/src/handlers/inline_function.rs b/crates/assists/src/handlers/inline_function.rs
new file mode 100644
index 000000000..2edf252b7
--- /dev/null
+++ b/crates/assists/src/handlers/inline_function.rs
@@ -0,0 +1,202 @@
1use ast::make;
2use hir::{HasSource, PathResolution};
3use syntax::{
4 ast::{self, edit::AstNodeEdit, ArgListOwner},
5 AstNode,
6};
7use test_utils::mark;
8
9use crate::{
10 assist_context::{AssistContext, Assists},
11 AssistId, AssistKind,
12};
13
14// Assist: inline_function
15//
16// Inlines a function body.
17//
18// ```
19// fn add(a: u32, b: u32) -> u32 { a + b }
20// fn main() {
21// let x = add<|>(1, 2);
22// }
23// ```
24// ->
25// ```
26// fn add(a: u32, b: u32) -> u32 { a + b }
27// fn main() {
28// let x = {
29// let a = 1;
30// let b = 2;
31// a + b
32// };
33// }
34// ```
35pub(crate) fn inline_function(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
36 let path_expr: ast::PathExpr = ctx.find_node_at_offset()?;
37 let call = path_expr.syntax().parent().and_then(ast::CallExpr::cast)?;
38 let path = path_expr.path()?;
39
40 let function = match ctx.sema.resolve_path(&path)? {
41 PathResolution::Def(hir::ModuleDef::Function(f)) => f,
42 _ => return None,
43 };
44
45 let function_source = function.source(ctx.db())?;
46 let arguments: Vec<_> = call.arg_list()?.args().collect();
47 let parameters = function_parameter_patterns(&function_source.value)?;
48
49 if arguments.len() != parameters.len() {
50 // Can't inline the function because they've passed the wrong number of
51 // arguments to this function
52 mark::hit!(inline_function_incorrect_number_of_arguments);
53 return None;
54 }
55
56 let new_bindings = parameters.into_iter().zip(arguments);
57
58 let body = function_source.value.body()?;
59
60 acc.add(
61 AssistId("inline_function", AssistKind::RefactorInline),
62 format!("Inline `{}`", path),
63 call.syntax().text_range(),
64 |builder| {
65 let mut statements: Vec<ast::Stmt> = Vec::new();
66
67 for (pattern, value) in new_bindings {
68 statements.push(make::let_stmt(pattern, Some(value)).into());
69 }
70
71 statements.extend(body.statements());
72
73 let original_indentation = call.indent_level();
74 let replacement = make::block_expr(statements, body.expr())
75 .reset_indent()
76 .indent(original_indentation);
77
78 builder.replace_ast(ast::Expr::CallExpr(call), ast::Expr::BlockExpr(replacement));
79 },
80 )
81}
82
83fn function_parameter_patterns(value: &ast::Fn) -> Option<Vec<ast::Pat>> {
84 let mut patterns = Vec::new();
85
86 for param in value.param_list()?.params() {
87 let pattern = param.pat()?;
88 patterns.push(pattern);
89 }
90
91 Some(patterns)
92}
93
94#[cfg(test)]
95mod tests {
96 use crate::tests::{check_assist, check_assist_not_applicable};
97
98 use super::*;
99
100 #[test]
101 fn no_args_or_return_value_gets_inlined_without_block() {
102 check_assist(
103 inline_function,
104 r#"
105fn foo() { println!("Hello, World!"); }
106fn main() {
107 fo<|>o();
108}
109"#,
110 r#"
111fn foo() { println!("Hello, World!"); }
112fn main() {
113 {
114 println!("Hello, World!");
115 };
116}
117"#,
118 );
119 }
120
121 #[test]
122 fn args_with_side_effects() {
123 check_assist(
124 inline_function,
125 r#"
126fn foo(name: String) { println!("Hello, {}!", name); }
127fn main() {
128 foo<|>(String::from("Michael"));
129}
130"#,
131 r#"
132fn foo(name: String) { println!("Hello, {}!", name); }
133fn main() {
134 {
135 let name = String::from("Michael");
136 println!("Hello, {}!", name);
137 };
138}
139"#,
140 );
141 }
142
143 #[test]
144 fn method_inlining_isnt_supported() {
145 check_assist_not_applicable(
146 inline_function,
147 r"
148struct Foo;
149impl Foo { fn bar(&self) {} }
150
151fn main() { Foo.bar<|>(); }
152",
153 );
154 }
155
156 #[test]
157 fn not_applicable_when_incorrect_number_of_parameters_are_provided() {
158 mark::check!(inline_function_incorrect_number_of_arguments);
159 check_assist_not_applicable(
160 inline_function,
161 r#"
162fn add(a: u32, b: u32) -> u32 { a + b }
163fn main() { let x = add<|>(42); }
164"#,
165 );
166 }
167
168 #[test]
169 fn function_with_multiple_statements() {
170 check_assist(
171 inline_function,
172 r#"
173fn foo(a: u32, b: u32) -> u32 {
174 let x = a + b;
175 let y = x - b;
176 x * y
177}
178
179fn main() {
180 let x = foo<|>(1, 2);
181}
182"#,
183 r#"
184fn foo(a: u32, b: u32) -> u32 {
185 let x = a + b;
186 let y = x - b;
187 x * y
188}
189
190fn main() {
191 let x = {
192 let a = 1;
193 let b = 2;
194 let x = a + b;
195 let y = x - b;
196 x * y
197 };
198}
199"#,
200 );
201 }
202}
diff --git a/crates/assists/src/lib.rs b/crates/assists/src/lib.rs
index 01baa65fe..9c2a95735 100644
--- a/crates/assists/src/lib.rs
+++ b/crates/assists/src/lib.rs
@@ -131,6 +131,7 @@ mod handlers {
131 mod generate_impl; 131 mod generate_impl;
132 mod generate_new; 132 mod generate_new;
133 mod infer_function_return_type; 133 mod infer_function_return_type;
134 mod inline_function;
134 mod inline_local_variable; 135 mod inline_local_variable;
135 mod introduce_named_lifetime; 136 mod introduce_named_lifetime;
136 mod invert_if; 137 mod invert_if;
@@ -183,6 +184,7 @@ mod handlers {
183 generate_impl::generate_impl, 184 generate_impl::generate_impl,
184 generate_new::generate_new, 185 generate_new::generate_new,
185 infer_function_return_type::infer_function_return_type, 186 infer_function_return_type::infer_function_return_type,
187 inline_function::inline_function,
186 inline_local_variable::inline_local_variable, 188 inline_local_variable::inline_local_variable,
187 introduce_named_lifetime::introduce_named_lifetime, 189 introduce_named_lifetime::introduce_named_lifetime,
188 invert_if::invert_if, 190 invert_if::invert_if,
diff --git a/crates/assists/src/tests/generated.rs b/crates/assists/src/tests/generated.rs
index 85e3c6742..b15352cf3 100644
--- a/crates/assists/src/tests/generated.rs
+++ b/crates/assists/src/tests/generated.rs
@@ -531,6 +531,29 @@ fn foo() -> i32 { 42i32 }
531} 531}
532 532
533#[test] 533#[test]
534fn doctest_inline_function() {
535 check_doc_test(
536 "inline_function",
537 r#####"
538fn add(a: u32, b: u32) -> u32 { a + b }
539fn main() {
540 let x = add<|>(1, 2);
541}
542"#####,
543 r#####"
544fn add(a: u32, b: u32) -> u32 { a + b }
545fn main() {
546 let x = {
547 let a = 1;
548 let b = 2;
549 a + b
550 };
551}
552"#####,
553 )
554}
555
556#[test]
534fn doctest_inline_local_variable() { 557fn doctest_inline_local_variable() {
535 check_doc_test( 558 check_doc_test(
536 "inline_local_variable", 559 "inline_local_variable",