diff options
-rw-r--r-- | crates/hir_expand/src/name.rs | 2 | ||||
-rw-r--r-- | crates/ide_assists/src/handlers/generate_is_empty_from_len.rs | 104 | ||||
-rw-r--r-- | crates/ide_assists/src/tests/generated.rs | 4 |
3 files changed, 73 insertions, 37 deletions
diff --git a/crates/hir_expand/src/name.rs b/crates/hir_expand/src/name.rs index e833e032c..43de9edd6 100644 --- a/crates/hir_expand/src/name.rs +++ b/crates/hir_expand/src/name.rs | |||
@@ -191,6 +191,8 @@ pub mod known { | |||
191 | filter_map, | 191 | filter_map, |
192 | next, | 192 | next, |
193 | iter_mut, | 193 | iter_mut, |
194 | len, | ||
195 | is_empty, | ||
194 | // Builtin macros | 196 | // Builtin macros |
195 | file, | 197 | file, |
196 | column, | 198 | column, |
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 index bd29dddb3..aa7072f25 100644 --- a/crates/ide_assists/src/handlers/generate_is_empty_from_len.rs +++ b/crates/ide_assists/src/handlers/generate_is_empty_from_len.rs | |||
@@ -1,4 +1,4 @@ | |||
1 | use hir::{AssocItem, HasSource, Impl}; | 1 | use hir::{known, HasSource, Name}; |
2 | use syntax::{ | 2 | use syntax::{ |
3 | ast::{self, NameOwner}, | 3 | ast::{self, NameOwner}, |
4 | AstNode, TextRange, | 4 | AstNode, TextRange, |
@@ -14,6 +14,8 @@ use crate::{ | |||
14 | // Generates is_empty implementation from the len method. | 14 | // Generates is_empty implementation from the len method. |
15 | // | 15 | // |
16 | // ``` | 16 | // ``` |
17 | // struct MyStruct { data: Vec<String> } | ||
18 | // | ||
17 | // impl MyStruct { | 19 | // impl MyStruct { |
18 | // p$0ub fn len(&self) -> usize { | 20 | // p$0ub fn len(&self) -> usize { |
19 | // self.data.len() | 21 | // self.data.len() |
@@ -22,6 +24,8 @@ use crate::{ | |||
22 | // ``` | 24 | // ``` |
23 | // -> | 25 | // -> |
24 | // ``` | 26 | // ``` |
27 | // struct MyStruct { data: Vec<String> } | ||
28 | // | ||
25 | // impl MyStruct { | 29 | // impl MyStruct { |
26 | // pub fn len(&self) -> usize { | 30 | // pub fn len(&self) -> usize { |
27 | // self.data.len() | 31 | // self.data.len() |
@@ -46,60 +50,50 @@ pub(crate) fn generate_is_empty_from_len(acc: &mut Assists, ctx: &AssistContext) | |||
46 | return None; | 50 | return None; |
47 | } | 51 | } |
48 | 52 | ||
49 | let impl_ = fn_node.syntax().ancestors().into_iter().find_map(ast::Impl::cast)?; | 53 | let impl_ = fn_node.syntax().ancestors().find_map(ast::Impl::cast)?; |
50 | let impl_def = ctx.sema.to_def(&impl_)?; | 54 | if get_impl_method(ctx, &impl_, &known::is_empty).is_some() { |
51 | if is_empty_implemented(ctx, &impl_def) { | ||
52 | cov_mark::hit!(is_empty_already_implemented); | 55 | cov_mark::hit!(is_empty_already_implemented); |
53 | return None; | 56 | return None; |
54 | } | 57 | } |
55 | 58 | ||
56 | let range = get_text_range_of_len_function(ctx, &impl_def)?; | 59 | let range = get_text_range_of_len_function(ctx, &impl_)?; |
57 | 60 | ||
58 | acc.add( | 61 | acc.add( |
59 | AssistId("generate_is_empty_from_len", AssistKind::Generate), | 62 | AssistId("generate_is_empty_from_len", AssistKind::Generate), |
60 | "Generate a is_empty impl from a len function", | 63 | "Generate a is_empty impl from a len function", |
61 | range, | 64 | range, |
62 | |builder| { | 65 | |builder| { |
63 | let code = get_is_empty_code(); | 66 | let code = r#" |
67 | |||
68 | pub fn is_empty(&self) -> bool { | ||
69 | self.len() == 0 | ||
70 | }"# | ||
71 | .to_string(); | ||
64 | builder.insert(range.end(), code) | 72 | builder.insert(range.end(), code) |
65 | }, | 73 | }, |
66 | ) | 74 | ) |
67 | } | 75 | } |
68 | 76 | ||
69 | fn get_function_from_impl(ctx: &AssistContext, impl_def: &Impl, name: &str) -> Option<AssocItem> { | 77 | fn get_impl_method( |
78 | ctx: &AssistContext, | ||
79 | impl_: &ast::Impl, | ||
80 | fn_name: &Name, | ||
81 | ) -> Option<hir::Function> { | ||
70 | let db = ctx.sema.db; | 82 | let db = ctx.sema.db; |
71 | impl_def.items(db).into_iter().filter(|item| matches!(item, AssocItem::Function(_value))).find( | 83 | let impl_def: hir::Impl = ctx.sema.to_def(impl_)?; |
72 | |func| match func.name(db) { | ||
73 | Some(fn_name) => fn_name.to_string() == name, | ||
74 | None => false, | ||
75 | }, | ||
76 | ) | ||
77 | } | ||
78 | 84 | ||
79 | fn is_empty_implemented(ctx: &AssistContext, impl_def: &Impl) -> bool { | 85 | let scope = ctx.sema.scope(impl_.syntax()); |
80 | get_function_from_impl(ctx, impl_def, "is_empty").is_some() | 86 | let krate = impl_def.module(db).krate(); |
87 | let ty = impl_def.target_ty(db); | ||
88 | let traits_in_scope = scope.traits_in_scope(); | ||
89 | ty.iterate_method_candidates(db, krate, &traits_in_scope, Some(fn_name), |_, func| Some(func)) | ||
81 | } | 90 | } |
82 | 91 | ||
83 | fn get_text_range_of_len_function(ctx: &AssistContext, impl_def: &Impl) -> Option<TextRange> { | 92 | fn get_text_range_of_len_function(ctx: &AssistContext, impl_: &ast::Impl) -> Option<TextRange> { |
84 | let db = ctx.sema.db; | 93 | let db = ctx.sema.db; |
85 | let len_fn = get_function_from_impl(ctx, impl_def, "len")?; | 94 | let func = get_impl_method(ctx, impl_, &known::len)?; |
86 | 95 | let node = func.source(db)?; | |
87 | let mut range = None; | 96 | Some(node.syntax().value.text_range()) |
88 | if let AssocItem::Function(node) = len_fn { | ||
89 | let node = node.source(db)?; | ||
90 | range = Some(node.syntax().value.text_range()); | ||
91 | } | ||
92 | |||
93 | range | ||
94 | } | ||
95 | |||
96 | fn get_is_empty_code() -> String { | ||
97 | r#" | ||
98 | |||
99 | pub fn is_empty(&self) -> bool { | ||
100 | self.len() == 0 | ||
101 | }"# | ||
102 | .to_string() | ||
103 | } | 97 | } |
104 | 98 | ||
105 | #[cfg(test)] | 99 | #[cfg(test)] |
@@ -114,6 +108,8 @@ mod tests { | |||
114 | check_assist_not_applicable( | 108 | check_assist_not_applicable( |
115 | generate_is_empty_from_len, | 109 | generate_is_empty_from_len, |
116 | r#" | 110 | r#" |
111 | struct MyStruct { data: Vec<String> } | ||
112 | |||
117 | impl MyStruct { | 113 | impl MyStruct { |
118 | p$0ub fn test(&self) -> usize { | 114 | p$0ub fn test(&self) -> usize { |
119 | self.data.len() | 115 | self.data.len() |
@@ -129,6 +125,8 @@ impl MyStruct { | |||
129 | check_assist_not_applicable( | 125 | check_assist_not_applicable( |
130 | generate_is_empty_from_len, | 126 | generate_is_empty_from_len, |
131 | r#" | 127 | r#" |
128 | struct MyStruct { data: Vec<String> } | ||
129 | |||
132 | impl MyStruct { | 130 | impl MyStruct { |
133 | p$0ub fn len(&self, _i: bool) -> usize { | 131 | p$0ub fn len(&self, _i: bool) -> usize { |
134 | self.data.len() | 132 | self.data.len() |
@@ -144,6 +142,8 @@ impl MyStruct { | |||
144 | check_assist_not_applicable( | 142 | check_assist_not_applicable( |
145 | generate_is_empty_from_len, | 143 | generate_is_empty_from_len, |
146 | r#" | 144 | r#" |
145 | struct MyStruct { data: Vec<String> } | ||
146 | |||
147 | impl MyStruct { | 147 | impl MyStruct { |
148 | p$0ub fn len(&self) -> usize { | 148 | p$0ub fn len(&self) -> usize { |
149 | self.data.len() | 149 | self.data.len() |
@@ -162,6 +162,8 @@ impl MyStruct { | |||
162 | check_assist( | 162 | check_assist( |
163 | generate_is_empty_from_len, | 163 | generate_is_empty_from_len, |
164 | r#" | 164 | r#" |
165 | struct MyStruct { data: Vec<String> } | ||
166 | |||
165 | impl MyStruct { | 167 | impl MyStruct { |
166 | p$0ub fn len(&self) -> usize { | 168 | p$0ub fn len(&self) -> usize { |
167 | self.data.len() | 169 | self.data.len() |
@@ -169,6 +171,8 @@ impl MyStruct { | |||
169 | } | 171 | } |
170 | "#, | 172 | "#, |
171 | r#" | 173 | r#" |
174 | struct MyStruct { data: Vec<String> } | ||
175 | |||
172 | impl MyStruct { | 176 | impl MyStruct { |
173 | pub fn len(&self) -> usize { | 177 | pub fn len(&self) -> usize { |
174 | self.data.len() | 178 | self.data.len() |
@@ -187,6 +191,8 @@ impl MyStruct { | |||
187 | check_assist( | 191 | check_assist( |
188 | generate_is_empty_from_len, | 192 | generate_is_empty_from_len, |
189 | r#" | 193 | r#" |
194 | struct MyStruct { data: Vec<String> } | ||
195 | |||
190 | impl MyStruct { | 196 | impl MyStruct { |
191 | pub fn new() -> Self { | 197 | pub fn new() -> Self { |
192 | Self { data: 0 } | 198 | Self { data: 0 } |
@@ -197,11 +203,13 @@ impl MyStruct { | |||
197 | } | 203 | } |
198 | 204 | ||
199 | pub fn work(&self) -> Option<usize> { | 205 | pub fn work(&self) -> Option<usize> { |
200 | // do some work | 206 | |
201 | } | 207 | } |
202 | } | 208 | } |
203 | "#, | 209 | "#, |
204 | r#" | 210 | r#" |
211 | struct MyStruct { data: Vec<String> } | ||
212 | |||
205 | impl MyStruct { | 213 | impl MyStruct { |
206 | pub fn new() -> Self { | 214 | pub fn new() -> Self { |
207 | Self { data: 0 } | 215 | Self { data: 0 } |
@@ -216,7 +224,29 @@ impl MyStruct { | |||
216 | } | 224 | } |
217 | 225 | ||
218 | pub fn work(&self) -> Option<usize> { | 226 | pub fn work(&self) -> Option<usize> { |
219 | // do some work | 227 | |
228 | } | ||
229 | } | ||
230 | "#, | ||
231 | ); | ||
232 | } | ||
233 | |||
234 | #[test] | ||
235 | fn multiple_impls() { | ||
236 | check_assist_not_applicable( | ||
237 | generate_is_empty_from_len, | ||
238 | r#" | ||
239 | struct MyStruct { data: Vec<String> } | ||
240 | |||
241 | impl MyStruct { | ||
242 | p$0ub fn len(&self) -> usize { | ||
243 | self.data.len() | ||
244 | } | ||
245 | } | ||
246 | |||
247 | impl MyStruct { | ||
248 | pub fn is_empty(&self) -> bool { | ||
249 | self.len() == 0 | ||
220 | } | 250 | } |
221 | } | 251 | } |
222 | "#, | 252 | "#, |
diff --git a/crates/ide_assists/src/tests/generated.rs b/crates/ide_assists/src/tests/generated.rs index 66fbcc968..736027ff0 100644 --- a/crates/ide_assists/src/tests/generated.rs +++ b/crates/ide_assists/src/tests/generated.rs | |||
@@ -726,6 +726,8 @@ fn doctest_generate_is_empty_from_len() { | |||
726 | check_doc_test( | 726 | check_doc_test( |
727 | "generate_is_empty_from_len", | 727 | "generate_is_empty_from_len", |
728 | r#####" | 728 | r#####" |
729 | struct MyStruct { data: Vec<String> } | ||
730 | |||
729 | impl MyStruct { | 731 | impl MyStruct { |
730 | p$0ub fn len(&self) -> usize { | 732 | p$0ub fn len(&self) -> usize { |
731 | self.data.len() | 733 | self.data.len() |
@@ -733,6 +735,8 @@ impl MyStruct { | |||
733 | } | 735 | } |
734 | "#####, | 736 | "#####, |
735 | r#####" | 737 | r#####" |
738 | struct MyStruct { data: Vec<String> } | ||
739 | |||
736 | impl MyStruct { | 740 | impl MyStruct { |
737 | pub fn len(&self) -> usize { | 741 | pub fn len(&self) -> usize { |
738 | self.data.len() | 742 | self.data.len() |