aboutsummaryrefslogtreecommitdiff
path: root/crates/ide_assists
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ide_assists')
-rw-r--r--crates/ide_assists/src/handlers/add_turbo_fish.rs2
-rw-r--r--crates/ide_assists/src/handlers/change_visibility.rs2
-rw-r--r--crates/ide_assists/src/handlers/expand_glob_import.rs2
-rw-r--r--crates/ide_assists/src/handlers/extract_function.rs20
-rw-r--r--crates/ide_assists/src/handlers/flip_trait_bound.rs2
-rw-r--r--crates/ide_assists/src/handlers/generate_is_empty_from_len.rs272
-rw-r--r--crates/ide_assists/src/handlers/invert_if.rs2
-rw-r--r--crates/ide_assists/src/handlers/move_bounds.rs62
-rw-r--r--crates/ide_assists/src/handlers/split_import.rs2
-rw-r--r--crates/ide_assists/src/handlers/unwrap_block.rs2
-rw-r--r--crates/ide_assists/src/lib.rs2
-rw-r--r--crates/ide_assists/src/tests/generated.rs29
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
564fn 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
1420fn make_rewritten_flow(handler: &FlowHandler, arg_expr: Option<ast::Expr>) -> Option<ast::Expr> { 1414fn 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 @@
1use hir::{known, HasSource, Name};
2use syntax::{
3 ast::{self, NameOwner},
4 AstNode,
5};
6
7use 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// ```
39pub(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
84fn 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)]
100mod 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#"
111struct MyStruct { data: Vec<String> }
112
113impl 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#"
128struct MyStruct { data: Vec<String> }
129
130impl 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#"
145struct MyStruct { data: Vec<String> }
146
147impl 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#"
166struct MyStruct { data: Vec<String> }
167
168impl 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#"
182struct MyStruct { data: Vec<String> }
183
184impl MyStruct {
185 p$0ub fn len(&self) -> usize {
186 self.data.len()
187 }
188}
189"#,
190 r#"
191struct MyStruct { data: Vec<String> }
192
193impl 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#"
211struct MyStruct { data: Vec<String> }
212
213impl 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#"
228struct MyStruct { data: Vec<String> }
229
230impl 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#"
256struct MyStruct { data: Vec<String> }
257
258impl MyStruct {
259 p$0ub fn len(&self) -> usize {
260 self.data.len()
261 }
262}
263
264impl 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
31pub(crate) fn invert_if(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { 31pub(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 @@
1use syntax::{ 1use 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
8use crate::{AssistContext, AssistId, AssistKind, Assists}; 6use crate::{AssistContext, AssistId, AssistKind, Assists};
@@ -23,7 +21,7 @@ use crate::{AssistContext, AssistId, AssistKind, Assists};
23// } 21// }
24// ``` 22// ```
25pub(crate) fn move_bounds_to_where_clause(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { 23pub(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// ```
18pub(crate) fn split_import(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { 18pub(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]
725fn doctest_generate_is_empty_from_len() {
726 check_doc_test(
727 "generate_is_empty_from_len",
728 r#####"
729struct MyStruct { data: Vec<String> }
730
731impl MyStruct {
732 p$0ub fn len(&self) -> usize {
733 self.data.len()
734 }
735}
736"#####,
737 r#####"
738struct MyStruct { data: Vec<String> }
739
740impl 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]
725fn doctest_generate_new() { 754fn doctest_generate_new() {
726 check_doc_test( 755 check_doc_test(
727 "generate_new", 756 "generate_new",