aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--crates/hir_expand/src/name.rs2
-rw-r--r--crates/ide_assists/src/handlers/generate_is_empty_from_len.rs104
-rw-r--r--crates/ide_assists/src/tests/generated.rs4
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 @@
1use hir::{AssocItem, HasSource, Impl}; 1use hir::{known, HasSource, Name};
2use syntax::{ 2use 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
69fn get_function_from_impl(ctx: &AssistContext, impl_def: &Impl, name: &str) -> Option<AssocItem> { 77fn 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
79fn 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
83fn get_text_range_of_len_function(ctx: &AssistContext, impl_def: &Impl) -> Option<TextRange> { 92fn 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
96fn 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#"
111struct MyStruct { data: Vec<String> }
112
117impl MyStruct { 113impl 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#"
128struct MyStruct { data: Vec<String> }
129
132impl MyStruct { 130impl 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#"
145struct MyStruct { data: Vec<String> }
146
147impl MyStruct { 147impl 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#"
165struct MyStruct { data: Vec<String> }
166
165impl MyStruct { 167impl 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#"
174struct MyStruct { data: Vec<String> }
175
172impl MyStruct { 176impl 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#"
194struct MyStruct { data: Vec<String> }
195
190impl MyStruct { 196impl 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#"
211struct MyStruct { data: Vec<String> }
212
205impl MyStruct { 213impl 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#"
239struct MyStruct { data: Vec<String> }
240
241impl MyStruct {
242 p$0ub fn len(&self) -> usize {
243 self.data.len()
244 }
245}
246
247impl 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#####"
729struct MyStruct { data: Vec<String> }
730
729impl MyStruct { 731impl 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#####"
738struct MyStruct { data: Vec<String> }
739
736impl MyStruct { 740impl MyStruct {
737 pub fn len(&self) -> usize { 741 pub fn len(&self) -> usize {
738 self.data.len() 742 self.data.len()