diff options
Diffstat (limited to 'crates/ide_assists/src')
-rw-r--r-- | crates/ide_assists/src/handlers/add_turbo_fish.rs | 2 | ||||
-rw-r--r-- | crates/ide_assists/src/handlers/change_visibility.rs | 2 | ||||
-rw-r--r-- | crates/ide_assists/src/handlers/expand_glob_import.rs | 2 | ||||
-rw-r--r-- | crates/ide_assists/src/handlers/extract_function.rs | 20 | ||||
-rw-r--r-- | crates/ide_assists/src/handlers/flip_trait_bound.rs | 2 | ||||
-rw-r--r-- | crates/ide_assists/src/handlers/generate_is_empty_from_len.rs | 272 | ||||
-rw-r--r-- | crates/ide_assists/src/handlers/invert_if.rs | 2 | ||||
-rw-r--r-- | crates/ide_assists/src/handlers/move_bounds.rs | 62 | ||||
-rw-r--r-- | crates/ide_assists/src/handlers/split_import.rs | 2 | ||||
-rw-r--r-- | crates/ide_assists/src/handlers/unwrap_block.rs | 2 | ||||
-rw-r--r-- | crates/ide_assists/src/lib.rs | 2 | ||||
-rw-r--r-- | crates/ide_assists/src/tests/generated.rs | 29 |
12 files changed, 337 insertions, 62 deletions
diff --git a/crates/ide_assists/src/handlers/add_turbo_fish.rs b/crates/ide_assists/src/handlers/add_turbo_fish.rs index ee879c151..436767895 100644 --- a/crates/ide_assists/src/handlers/add_turbo_fish.rs +++ b/crates/ide_assists/src/handlers/add_turbo_fish.rs | |||
@@ -38,7 +38,7 @@ pub(crate) fn add_turbo_fish(acc: &mut Assists, ctx: &AssistContext) -> Option<( | |||
38 | cov_mark::hit!(add_turbo_fish_one_fish_is_enough); | 38 | cov_mark::hit!(add_turbo_fish_one_fish_is_enough); |
39 | return None; | 39 | return None; |
40 | } | 40 | } |
41 | let name_ref = ast::NameRef::cast(ident.parent())?; | 41 | let name_ref = ast::NameRef::cast(ident.parent()?)?; |
42 | let def = match NameRefClass::classify(&ctx.sema, &name_ref)? { | 42 | let def = match NameRefClass::classify(&ctx.sema, &name_ref)? { |
43 | NameRefClass::Definition(def) => def, | 43 | NameRefClass::Definition(def) => def, |
44 | NameRefClass::ExternCrate(_) | NameRefClass::FieldShorthand { .. } => return None, | 44 | NameRefClass::ExternCrate(_) | NameRefClass::FieldShorthand { .. } => return None, |
diff --git a/crates/ide_assists/src/handlers/change_visibility.rs b/crates/ide_assists/src/handlers/change_visibility.rs index ec99a5505..d7e39b2ae 100644 --- a/crates/ide_assists/src/handlers/change_visibility.rs +++ b/crates/ide_assists/src/handlers/change_visibility.rs | |||
@@ -41,7 +41,7 @@ fn add_vis(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { | |||
41 | }); | 41 | }); |
42 | 42 | ||
43 | let (offset, target) = if let Some(keyword) = item_keyword { | 43 | let (offset, target) = if let Some(keyword) = item_keyword { |
44 | let parent = keyword.parent(); | 44 | let parent = keyword.parent()?; |
45 | let def_kws = vec![CONST, STATIC, TYPE_ALIAS, FN, MODULE, STRUCT, ENUM, TRAIT]; | 45 | let def_kws = vec![CONST, STATIC, TYPE_ALIAS, FN, MODULE, STRUCT, ENUM, TRAIT]; |
46 | // Parent is not a definition, can't add visibility | 46 | // Parent is not a definition, can't add visibility |
47 | if !def_kws.iter().any(|&def_kw| def_kw == parent.kind()) { | 47 | if !def_kws.iter().any(|&def_kw| def_kw == parent.kind()) { |
diff --git a/crates/ide_assists/src/handlers/expand_glob_import.rs b/crates/ide_assists/src/handlers/expand_glob_import.rs index 5fe617ba4..5b540df5c 100644 --- a/crates/ide_assists/src/handlers/expand_glob_import.rs +++ b/crates/ide_assists/src/handlers/expand_glob_import.rs | |||
@@ -48,7 +48,7 @@ pub(crate) fn expand_glob_import(acc: &mut Assists, ctx: &AssistContext) -> Opti | |||
48 | _ => return None, | 48 | _ => return None, |
49 | }; | 49 | }; |
50 | 50 | ||
51 | let current_scope = ctx.sema.scope(&star.parent()); | 51 | let current_scope = ctx.sema.scope(&star.parent()?); |
52 | let current_module = current_scope.module()?; | 52 | let current_module = current_scope.module()?; |
53 | 53 | ||
54 | let refs_in_target = find_refs_in_mod(ctx, target_module, Some(current_module))?; | 54 | let refs_in_target = find_refs_in_mod(ctx, target_module, Some(current_module))?; |
diff --git a/crates/ide_assists/src/handlers/extract_function.rs b/crates/ide_assists/src/handlers/extract_function.rs index dd4501709..5fdc8bf38 100644 --- a/crates/ide_assists/src/handlers/extract_function.rs +++ b/crates/ide_assists/src/handlers/extract_function.rs | |||
@@ -16,7 +16,6 @@ use syntax::{ | |||
16 | edit::{AstNodeEdit, IndentLevel}, | 16 | edit::{AstNodeEdit, IndentLevel}, |
17 | AstNode, | 17 | AstNode, |
18 | }, | 18 | }, |
19 | SyntaxElement, | ||
20 | SyntaxKind::{self, BLOCK_EXPR, BREAK_EXPR, COMMENT, PATH_EXPR, RETURN_EXPR}, | 19 | SyntaxKind::{self, BLOCK_EXPR, BREAK_EXPR, COMMENT, PATH_EXPR, RETURN_EXPR}, |
21 | SyntaxNode, SyntaxToken, TextRange, TextSize, TokenAtOffset, WalkEvent, T, | 20 | SyntaxNode, SyntaxToken, TextRange, TextSize, TokenAtOffset, WalkEvent, T, |
22 | }; | 21 | }; |
@@ -62,7 +61,10 @@ pub(crate) fn extract_function(acc: &mut Assists, ctx: &AssistContext) -> Option | |||
62 | return None; | 61 | return None; |
63 | } | 62 | } |
64 | 63 | ||
65 | let node = element_to_node(node); | 64 | let node = match node { |
65 | syntax::NodeOrToken::Node(n) => n, | ||
66 | syntax::NodeOrToken::Token(t) => t.parent()?, | ||
67 | }; | ||
66 | 68 | ||
67 | let body = extraction_target(&node, ctx.frange.range)?; | 69 | let body = extraction_target(&node, ctx.frange.range)?; |
68 | 70 | ||
@@ -560,14 +562,6 @@ impl HasTokenAtOffset for FunctionBody { | |||
560 | } | 562 | } |
561 | } | 563 | } |
562 | 564 | ||
563 | /// node or token's parent | ||
564 | fn element_to_node(node: SyntaxElement) -> SyntaxNode { | ||
565 | match node { | ||
566 | syntax::NodeOrToken::Node(n) => n, | ||
567 | syntax::NodeOrToken::Token(t) => t.parent(), | ||
568 | } | ||
569 | } | ||
570 | |||
571 | /// Try to guess what user wants to extract | 565 | /// Try to guess what user wants to extract |
572 | /// | 566 | /// |
573 | /// We have basically have two cases: | 567 | /// We have basically have two cases: |
@@ -1246,7 +1240,7 @@ fn make_body( | |||
1246 | }) | 1240 | }) |
1247 | } | 1241 | } |
1248 | FlowHandler::If { .. } => { | 1242 | FlowHandler::If { .. } => { |
1249 | let lit_false = ast::Literal::cast(make::tokens::literal("false").parent()).unwrap(); | 1243 | let lit_false = make::expr_literal("false"); |
1250 | with_tail_expr(block, lit_false.into()) | 1244 | with_tail_expr(block, lit_false.into()) |
1251 | } | 1245 | } |
1252 | FlowHandler::IfOption { .. } => { | 1246 | FlowHandler::IfOption { .. } => { |
@@ -1420,9 +1414,7 @@ fn update_external_control_flow(handler: &FlowHandler, syntax: &SyntaxNode) -> S | |||
1420 | fn make_rewritten_flow(handler: &FlowHandler, arg_expr: Option<ast::Expr>) -> Option<ast::Expr> { | 1414 | fn make_rewritten_flow(handler: &FlowHandler, arg_expr: Option<ast::Expr>) -> Option<ast::Expr> { |
1421 | let value = match handler { | 1415 | let value = match handler { |
1422 | FlowHandler::None | FlowHandler::Try { .. } => return None, | 1416 | FlowHandler::None | FlowHandler::Try { .. } => return None, |
1423 | FlowHandler::If { .. } => { | 1417 | FlowHandler::If { .. } => make::expr_literal("true").into(), |
1424 | ast::Literal::cast(make::tokens::literal("true").parent()).unwrap().into() | ||
1425 | } | ||
1426 | FlowHandler::IfOption { .. } => { | 1418 | FlowHandler::IfOption { .. } => { |
1427 | let expr = arg_expr.unwrap_or_else(|| make::expr_tuple(Vec::new())); | 1419 | let expr = arg_expr.unwrap_or_else(|| make::expr_tuple(Vec::new())); |
1428 | let args = make::arg_list(iter::once(expr)); | 1420 | let args = make::arg_list(iter::once(expr)); |
diff --git a/crates/ide_assists/src/handlers/flip_trait_bound.rs b/crates/ide_assists/src/handlers/flip_trait_bound.rs index d419d263e..a868aa43d 100644 --- a/crates/ide_assists/src/handlers/flip_trait_bound.rs +++ b/crates/ide_assists/src/handlers/flip_trait_bound.rs | |||
@@ -23,7 +23,7 @@ pub(crate) fn flip_trait_bound(acc: &mut Assists, ctx: &AssistContext) -> Option | |||
23 | let plus = ctx.find_token_syntax_at_offset(T![+])?; | 23 | let plus = ctx.find_token_syntax_at_offset(T![+])?; |
24 | 24 | ||
25 | // Make sure we're in a `TypeBoundList` | 25 | // Make sure we're in a `TypeBoundList` |
26 | if ast::TypeBoundList::cast(plus.parent()).is_none() { | 26 | if ast::TypeBoundList::cast(plus.parent()?).is_none() { |
27 | return None; | 27 | return None; |
28 | } | 28 | } |
29 | 29 | ||
diff --git a/crates/ide_assists/src/handlers/generate_is_empty_from_len.rs b/crates/ide_assists/src/handlers/generate_is_empty_from_len.rs new file mode 100644 index 000000000..b8834d283 --- /dev/null +++ b/crates/ide_assists/src/handlers/generate_is_empty_from_len.rs | |||
@@ -0,0 +1,272 @@ | |||
1 | use hir::{known, HasSource, Name}; | ||
2 | use syntax::{ | ||
3 | ast::{self, NameOwner}, | ||
4 | AstNode, | ||
5 | }; | ||
6 | |||
7 | use crate::{ | ||
8 | assist_context::{AssistContext, Assists}, | ||
9 | AssistId, AssistKind, | ||
10 | }; | ||
11 | |||
12 | // Assist: generate_is_empty_from_len | ||
13 | // | ||
14 | // Generates is_empty implementation from the len method. | ||
15 | // | ||
16 | // ``` | ||
17 | // struct MyStruct { data: Vec<String> } | ||
18 | // | ||
19 | // impl MyStruct { | ||
20 | // p$0ub fn len(&self) -> usize { | ||
21 | // self.data.len() | ||
22 | // } | ||
23 | // } | ||
24 | // ``` | ||
25 | // -> | ||
26 | // ``` | ||
27 | // struct MyStruct { data: Vec<String> } | ||
28 | // | ||
29 | // impl MyStruct { | ||
30 | // pub fn len(&self) -> usize { | ||
31 | // self.data.len() | ||
32 | // } | ||
33 | // | ||
34 | // pub fn is_empty(&self) -> bool { | ||
35 | // self.len() == 0 | ||
36 | // } | ||
37 | // } | ||
38 | // ``` | ||
39 | pub(crate) fn generate_is_empty_from_len(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { | ||
40 | let fn_node = ctx.find_node_at_offset::<ast::Fn>()?; | ||
41 | let fn_name = fn_node.name()?; | ||
42 | |||
43 | if fn_name.text() != "len" { | ||
44 | cov_mark::hit!(len_function_not_present); | ||
45 | return None; | ||
46 | } | ||
47 | |||
48 | if fn_node.param_list()?.params().next().is_some() { | ||
49 | cov_mark::hit!(len_function_with_parameters); | ||
50 | return None; | ||
51 | } | ||
52 | |||
53 | let impl_ = fn_node.syntax().ancestors().find_map(ast::Impl::cast)?; | ||
54 | let len_fn = get_impl_method(ctx, &impl_, &known::len)?; | ||
55 | if !len_fn.ret_type(ctx.sema.db).is_usize() { | ||
56 | cov_mark::hit!(len_fn_different_return_type); | ||
57 | return None; | ||
58 | } | ||
59 | |||
60 | if get_impl_method(ctx, &impl_, &known::is_empty).is_some() { | ||
61 | cov_mark::hit!(is_empty_already_implemented); | ||
62 | return None; | ||
63 | } | ||
64 | |||
65 | let node = len_fn.source(ctx.sema.db)?; | ||
66 | let range = node.syntax().value.text_range(); | ||
67 | |||
68 | acc.add( | ||
69 | AssistId("generate_is_empty_from_len", AssistKind::Generate), | ||
70 | "Generate a is_empty impl from a len function", | ||
71 | range, | ||
72 | |builder| { | ||
73 | let code = r#" | ||
74 | |||
75 | pub fn is_empty(&self) -> bool { | ||
76 | self.len() == 0 | ||
77 | }"# | ||
78 | .to_string(); | ||
79 | builder.insert(range.end(), code) | ||
80 | }, | ||
81 | ) | ||
82 | } | ||
83 | |||
84 | fn get_impl_method( | ||
85 | ctx: &AssistContext, | ||
86 | impl_: &ast::Impl, | ||
87 | fn_name: &Name, | ||
88 | ) -> Option<hir::Function> { | ||
89 | let db = ctx.sema.db; | ||
90 | let impl_def: hir::Impl = ctx.sema.to_def(impl_)?; | ||
91 | |||
92 | let scope = ctx.sema.scope(impl_.syntax()); | ||
93 | let krate = impl_def.module(db).krate(); | ||
94 | let ty = impl_def.target_ty(db); | ||
95 | let traits_in_scope = scope.traits_in_scope(); | ||
96 | ty.iterate_method_candidates(db, krate, &traits_in_scope, Some(fn_name), |_, func| Some(func)) | ||
97 | } | ||
98 | |||
99 | #[cfg(test)] | ||
100 | mod tests { | ||
101 | use crate::tests::{check_assist, check_assist_not_applicable}; | ||
102 | |||
103 | use super::*; | ||
104 | |||
105 | #[test] | ||
106 | fn len_function_not_present() { | ||
107 | cov_mark::check!(len_function_not_present); | ||
108 | check_assist_not_applicable( | ||
109 | generate_is_empty_from_len, | ||
110 | r#" | ||
111 | struct MyStruct { data: Vec<String> } | ||
112 | |||
113 | impl MyStruct { | ||
114 | p$0ub fn test(&self) -> usize { | ||
115 | self.data.len() | ||
116 | } | ||
117 | } | ||
118 | "#, | ||
119 | ); | ||
120 | } | ||
121 | |||
122 | #[test] | ||
123 | fn len_function_with_parameters() { | ||
124 | cov_mark::check!(len_function_with_parameters); | ||
125 | check_assist_not_applicable( | ||
126 | generate_is_empty_from_len, | ||
127 | r#" | ||
128 | struct MyStruct { data: Vec<String> } | ||
129 | |||
130 | impl MyStruct { | ||
131 | p$0ub fn len(&self, _i: bool) -> usize { | ||
132 | self.data.len() | ||
133 | } | ||
134 | } | ||
135 | "#, | ||
136 | ); | ||
137 | } | ||
138 | |||
139 | #[test] | ||
140 | fn is_empty_already_implemented() { | ||
141 | cov_mark::check!(is_empty_already_implemented); | ||
142 | check_assist_not_applicable( | ||
143 | generate_is_empty_from_len, | ||
144 | r#" | ||
145 | struct MyStruct { data: Vec<String> } | ||
146 | |||
147 | impl MyStruct { | ||
148 | p$0ub fn len(&self) -> usize { | ||
149 | self.data.len() | ||
150 | } | ||
151 | |||
152 | pub fn is_empty(&self) -> bool { | ||
153 | self.len() == 0 | ||
154 | } | ||
155 | } | ||
156 | "#, | ||
157 | ); | ||
158 | } | ||
159 | |||
160 | #[test] | ||
161 | fn len_fn_different_return_type() { | ||
162 | cov_mark::check!(len_fn_different_return_type); | ||
163 | check_assist_not_applicable( | ||
164 | generate_is_empty_from_len, | ||
165 | r#" | ||
166 | struct MyStruct { data: Vec<String> } | ||
167 | |||
168 | impl MyStruct { | ||
169 | p$0ub fn len(&self) -> u32 { | ||
170 | self.data.len() | ||
171 | } | ||
172 | } | ||
173 | "#, | ||
174 | ); | ||
175 | } | ||
176 | |||
177 | #[test] | ||
178 | fn generate_is_empty() { | ||
179 | check_assist( | ||
180 | generate_is_empty_from_len, | ||
181 | r#" | ||
182 | struct MyStruct { data: Vec<String> } | ||
183 | |||
184 | impl MyStruct { | ||
185 | p$0ub fn len(&self) -> usize { | ||
186 | self.data.len() | ||
187 | } | ||
188 | } | ||
189 | "#, | ||
190 | r#" | ||
191 | struct MyStruct { data: Vec<String> } | ||
192 | |||
193 | impl MyStruct { | ||
194 | pub fn len(&self) -> usize { | ||
195 | self.data.len() | ||
196 | } | ||
197 | |||
198 | pub fn is_empty(&self) -> bool { | ||
199 | self.len() == 0 | ||
200 | } | ||
201 | } | ||
202 | "#, | ||
203 | ); | ||
204 | } | ||
205 | |||
206 | #[test] | ||
207 | fn multiple_functions_in_impl() { | ||
208 | check_assist( | ||
209 | generate_is_empty_from_len, | ||
210 | r#" | ||
211 | struct MyStruct { data: Vec<String> } | ||
212 | |||
213 | impl MyStruct { | ||
214 | pub fn new() -> Self { | ||
215 | Self { data: 0 } | ||
216 | } | ||
217 | |||
218 | p$0ub fn len(&self) -> usize { | ||
219 | self.data.len() | ||
220 | } | ||
221 | |||
222 | pub fn work(&self) -> Option<usize> { | ||
223 | |||
224 | } | ||
225 | } | ||
226 | "#, | ||
227 | r#" | ||
228 | struct MyStruct { data: Vec<String> } | ||
229 | |||
230 | impl MyStruct { | ||
231 | pub fn new() -> Self { | ||
232 | Self { data: 0 } | ||
233 | } | ||
234 | |||
235 | pub fn len(&self) -> usize { | ||
236 | self.data.len() | ||
237 | } | ||
238 | |||
239 | pub fn is_empty(&self) -> bool { | ||
240 | self.len() == 0 | ||
241 | } | ||
242 | |||
243 | pub fn work(&self) -> Option<usize> { | ||
244 | |||
245 | } | ||
246 | } | ||
247 | "#, | ||
248 | ); | ||
249 | } | ||
250 | |||
251 | #[test] | ||
252 | fn multiple_impls() { | ||
253 | check_assist_not_applicable( | ||
254 | generate_is_empty_from_len, | ||
255 | r#" | ||
256 | struct MyStruct { data: Vec<String> } | ||
257 | |||
258 | impl MyStruct { | ||
259 | p$0ub fn len(&self) -> usize { | ||
260 | self.data.len() | ||
261 | } | ||
262 | } | ||
263 | |||
264 | impl MyStruct { | ||
265 | pub fn is_empty(&self) -> bool { | ||
266 | self.len() == 0 | ||
267 | } | ||
268 | } | ||
269 | "#, | ||
270 | ); | ||
271 | } | ||
272 | } | ||
diff --git a/crates/ide_assists/src/handlers/invert_if.rs b/crates/ide_assists/src/handlers/invert_if.rs index b131dc205..53612ec3f 100644 --- a/crates/ide_assists/src/handlers/invert_if.rs +++ b/crates/ide_assists/src/handlers/invert_if.rs | |||
@@ -30,7 +30,7 @@ use crate::{ | |||
30 | 30 | ||
31 | pub(crate) fn invert_if(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { | 31 | pub(crate) fn invert_if(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { |
32 | let if_keyword = ctx.find_token_syntax_at_offset(T![if])?; | 32 | let if_keyword = ctx.find_token_syntax_at_offset(T![if])?; |
33 | let expr = ast::IfExpr::cast(if_keyword.parent())?; | 33 | let expr = ast::IfExpr::cast(if_keyword.parent()?)?; |
34 | let if_range = if_keyword.text_range(); | 34 | let if_range = if_keyword.text_range(); |
35 | let cursor_in_range = if_range.contains_range(ctx.frange.range); | 35 | let cursor_in_range = if_range.contains_range(ctx.frange.range); |
36 | if !cursor_in_range { | 36 | if !cursor_in_range { |
diff --git a/crates/ide_assists/src/handlers/move_bounds.rs b/crates/ide_assists/src/handlers/move_bounds.rs index cf260c6f8..48efa67ed 100644 --- a/crates/ide_assists/src/handlers/move_bounds.rs +++ b/crates/ide_assists/src/handlers/move_bounds.rs | |||
@@ -1,8 +1,6 @@ | |||
1 | use syntax::{ | 1 | use syntax::{ |
2 | ast::{self, edit::AstNodeEdit, make, AstNode, NameOwner, TypeBoundsOwner}, | 2 | ast::{self, edit_in_place::GenericParamsOwnerEdit, make, AstNode, NameOwner, TypeBoundsOwner}, |
3 | match_ast, | 3 | match_ast, |
4 | SyntaxKind::*, | ||
5 | T, | ||
6 | }; | 4 | }; |
7 | 5 | ||
8 | use crate::{AssistContext, AssistId, AssistKind, Assists}; | 6 | use crate::{AssistContext, AssistId, AssistKind, Assists}; |
@@ -23,7 +21,7 @@ use crate::{AssistContext, AssistId, AssistKind, Assists}; | |||
23 | // } | 21 | // } |
24 | // ``` | 22 | // ``` |
25 | pub(crate) fn move_bounds_to_where_clause(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { | 23 | pub(crate) fn move_bounds_to_where_clause(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { |
26 | let type_param_list = ctx.find_node_at_offset::<ast::GenericParamList>()?; | 24 | let type_param_list = ctx.find_node_at_offset::<ast::GenericParamList>()?.clone_for_update(); |
27 | 25 | ||
28 | let mut type_params = type_param_list.type_params(); | 26 | let mut type_params = type_param_list.type_params(); |
29 | if type_params.all(|p| p.type_bound_list().is_none()) { | 27 | if type_params.all(|p| p.type_bound_list().is_none()) { |
@@ -31,23 +29,7 @@ pub(crate) fn move_bounds_to_where_clause(acc: &mut Assists, ctx: &AssistContext | |||
31 | } | 29 | } |
32 | 30 | ||
33 | let parent = type_param_list.syntax().parent()?; | 31 | let parent = type_param_list.syntax().parent()?; |
34 | if parent.children_with_tokens().any(|it| it.kind() == WHERE_CLAUSE) { | 32 | let original_parent_range = parent.text_range(); |
35 | return None; | ||
36 | } | ||
37 | |||
38 | let anchor = match_ast! { | ||
39 | match parent { | ||
40 | ast::Fn(it) => it.body()?.syntax().clone().into(), | ||
41 | ast::Trait(it) => it.assoc_item_list()?.syntax().clone().into(), | ||
42 | ast::Impl(it) => it.assoc_item_list()?.syntax().clone().into(), | ||
43 | ast::Enum(it) => it.variant_list()?.syntax().clone().into(), | ||
44 | ast::Struct(it) => { | ||
45 | it.syntax().children_with_tokens() | ||
46 | .find(|it| it.kind() == RECORD_FIELD_LIST || it.kind() == T![;])? | ||
47 | }, | ||
48 | _ => return None | ||
49 | } | ||
50 | }; | ||
51 | 33 | ||
52 | let target = type_param_list.syntax().text_range(); | 34 | let target = type_param_list.syntax().text_range(); |
53 | acc.add( | 35 | acc.add( |
@@ -55,29 +37,27 @@ pub(crate) fn move_bounds_to_where_clause(acc: &mut Assists, ctx: &AssistContext | |||
55 | "Move to where clause", | 37 | "Move to where clause", |
56 | target, | 38 | target, |
57 | |edit| { | 39 | |edit| { |
58 | let new_params = type_param_list | 40 | let where_clause: ast::WhereClause = match_ast! { |
59 | .type_params() | 41 | match parent { |
60 | .filter(|it| it.type_bound_list().is_some()) | 42 | ast::Fn(it) => it.get_or_create_where_clause(), |
61 | .map(|type_param| { | 43 | // ast::Trait(it) => it.get_or_create_where_clause(), |
62 | let without_bounds = type_param.remove_bounds(); | 44 | ast::Impl(it) => it.get_or_create_where_clause(), |
63 | (type_param, without_bounds) | 45 | // ast::Enum(it) => it.get_or_create_where_clause(), |
64 | }); | 46 | ast::Struct(it) => it.get_or_create_where_clause(), |
65 | 47 | _ => return, | |
66 | let new_type_param_list = type_param_list.replace_descendants(new_params); | 48 | } |
67 | edit.replace_ast(type_param_list.clone(), new_type_param_list); | ||
68 | |||
69 | let where_clause = { | ||
70 | let predicates = type_param_list.type_params().filter_map(build_predicate); | ||
71 | make::where_clause(predicates) | ||
72 | }; | 49 | }; |
73 | 50 | ||
74 | let to_insert = match anchor.prev_sibling_or_token() { | 51 | for type_param in type_param_list.type_params() { |
75 | Some(ref elem) if elem.kind() == WHITESPACE => { | 52 | if let Some(tbl) = type_param.type_bound_list() { |
76 | format!("{} ", where_clause.syntax()) | 53 | if let Some(predicate) = build_predicate(type_param.clone()) { |
54 | where_clause.add_predicate(predicate.clone_for_update()) | ||
55 | } | ||
56 | tbl.remove() | ||
77 | } | 57 | } |
78 | _ => format!(" {}", where_clause.syntax()), | 58 | } |
79 | }; | 59 | |
80 | edit.insert(anchor.text_range().start(), to_insert); | 60 | edit.replace(original_parent_range, parent.to_string()) |
81 | }, | 61 | }, |
82 | ) | 62 | ) |
83 | } | 63 | } |
diff --git a/crates/ide_assists/src/handlers/split_import.rs b/crates/ide_assists/src/handlers/split_import.rs index 9319a4267..446f30544 100644 --- a/crates/ide_assists/src/handlers/split_import.rs +++ b/crates/ide_assists/src/handlers/split_import.rs | |||
@@ -17,7 +17,7 @@ use crate::{AssistContext, AssistId, AssistKind, Assists}; | |||
17 | // ``` | 17 | // ``` |
18 | pub(crate) fn split_import(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { | 18 | pub(crate) fn split_import(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { |
19 | let colon_colon = ctx.find_token_syntax_at_offset(T![::])?; | 19 | let colon_colon = ctx.find_token_syntax_at_offset(T![::])?; |
20 | let path = ast::Path::cast(colon_colon.parent())?.qualifier()?; | 20 | let path = ast::Path::cast(colon_colon.parent()?)?.qualifier()?; |
21 | let top_path = successors(Some(path.clone()), |it| it.parent_path()).last()?; | 21 | let top_path = successors(Some(path.clone()), |it| it.parent_path()).last()?; |
22 | 22 | ||
23 | let use_tree = top_path.syntax().ancestors().find_map(ast::UseTree::cast)?; | 23 | let use_tree = top_path.syntax().ancestors().find_map(ast::UseTree::cast)?; |
diff --git a/crates/ide_assists/src/handlers/unwrap_block.rs b/crates/ide_assists/src/handlers/unwrap_block.rs index ed6f6177d..440639322 100644 --- a/crates/ide_assists/src/handlers/unwrap_block.rs +++ b/crates/ide_assists/src/handlers/unwrap_block.rs | |||
@@ -30,7 +30,7 @@ pub(crate) fn unwrap_block(acc: &mut Assists, ctx: &AssistContext) -> Option<()> | |||
30 | let assist_label = "Unwrap block"; | 30 | let assist_label = "Unwrap block"; |
31 | 31 | ||
32 | let l_curly_token = ctx.find_token_syntax_at_offset(T!['{'])?; | 32 | let l_curly_token = ctx.find_token_syntax_at_offset(T!['{'])?; |
33 | let mut block = ast::BlockExpr::cast(l_curly_token.parent())?; | 33 | let mut block = ast::BlockExpr::cast(l_curly_token.parent()?)?; |
34 | let target = block.syntax().text_range(); | 34 | let target = block.syntax().text_range(); |
35 | let mut parent = block.syntax().parent()?; | 35 | let mut parent = block.syntax().parent()?; |
36 | if ast::MatchArm::can_cast(parent.kind()) { | 36 | if ast::MatchArm::can_cast(parent.kind()) { |
diff --git a/crates/ide_assists/src/lib.rs b/crates/ide_assists/src/lib.rs index f1aab74d4..8c068a6c0 100644 --- a/crates/ide_assists/src/lib.rs +++ b/crates/ide_assists/src/lib.rs | |||
@@ -129,6 +129,7 @@ mod handlers { | |||
129 | mod flip_trait_bound; | 129 | mod flip_trait_bound; |
130 | mod generate_default_from_enum_variant; | 130 | mod generate_default_from_enum_variant; |
131 | mod generate_default_from_new; | 131 | mod generate_default_from_new; |
132 | mod generate_is_empty_from_len; | ||
132 | mod generate_derive; | 133 | mod generate_derive; |
133 | mod generate_enum_is_method; | 134 | mod generate_enum_is_method; |
134 | mod generate_enum_projection_method; | 135 | mod generate_enum_projection_method; |
@@ -193,6 +194,7 @@ mod handlers { | |||
193 | flip_trait_bound::flip_trait_bound, | 194 | flip_trait_bound::flip_trait_bound, |
194 | generate_default_from_enum_variant::generate_default_from_enum_variant, | 195 | generate_default_from_enum_variant::generate_default_from_enum_variant, |
195 | generate_default_from_new::generate_default_from_new, | 196 | generate_default_from_new::generate_default_from_new, |
197 | generate_is_empty_from_len::generate_is_empty_from_len, | ||
196 | generate_derive::generate_derive, | 198 | generate_derive::generate_derive, |
197 | generate_enum_is_method::generate_enum_is_method, | 199 | generate_enum_is_method::generate_enum_is_method, |
198 | generate_enum_projection_method::generate_enum_as_method, | 200 | generate_enum_projection_method::generate_enum_as_method, |
diff --git a/crates/ide_assists/src/tests/generated.rs b/crates/ide_assists/src/tests/generated.rs index 3f77edd8d..736027ff0 100644 --- a/crates/ide_assists/src/tests/generated.rs +++ b/crates/ide_assists/src/tests/generated.rs | |||
@@ -722,6 +722,35 @@ impl<T: Clone> Ctx<T> { | |||
722 | } | 722 | } |
723 | 723 | ||
724 | #[test] | 724 | #[test] |
725 | fn doctest_generate_is_empty_from_len() { | ||
726 | check_doc_test( | ||
727 | "generate_is_empty_from_len", | ||
728 | r#####" | ||
729 | struct MyStruct { data: Vec<String> } | ||
730 | |||
731 | impl MyStruct { | ||
732 | p$0ub fn len(&self) -> usize { | ||
733 | self.data.len() | ||
734 | } | ||
735 | } | ||
736 | "#####, | ||
737 | r#####" | ||
738 | struct MyStruct { data: Vec<String> } | ||
739 | |||
740 | impl MyStruct { | ||
741 | pub fn len(&self) -> usize { | ||
742 | self.data.len() | ||
743 | } | ||
744 | |||
745 | pub fn is_empty(&self) -> bool { | ||
746 | self.len() == 0 | ||
747 | } | ||
748 | } | ||
749 | "#####, | ||
750 | ) | ||
751 | } | ||
752 | |||
753 | #[test] | ||
725 | fn doctest_generate_new() { | 754 | fn doctest_generate_new() { |
726 | check_doc_test( | 755 | check_doc_test( |
727 | "generate_new", | 756 | "generate_new", |