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_missing_impl_members.rs3
-rw-r--r--crates/ide_assists/src/handlers/auto_import.rs26
-rw-r--r--crates/ide_assists/src/handlers/generate_new.rs153
-rw-r--r--crates/ide_assists/src/handlers/move_module_to_file.rs38
-rw-r--r--crates/ide_assists/src/handlers/replace_unwrap_with_match.rs2
-rw-r--r--crates/ide_assists/src/tests/generated.rs1
-rw-r--r--crates/ide_assists/src/utils.rs48
7 files changed, 185 insertions, 86 deletions
diff --git a/crates/ide_assists/src/handlers/add_missing_impl_members.rs b/crates/ide_assists/src/handlers/add_missing_impl_members.rs
index 0148635f9..8225ae22c 100644
--- a/crates/ide_assists/src/handlers/add_missing_impl_members.rs
+++ b/crates/ide_assists/src/handlers/add_missing_impl_members.rs
@@ -64,7 +64,6 @@ pub(crate) fn add_missing_impl_members(acc: &mut Assists, ctx: &AssistContext) -
64// impl Trait for () { 64// impl Trait for () {
65// type X = (); 65// type X = ();
66// fn foo(&self) {}$0 66// fn foo(&self) {}$0
67//
68// } 67// }
69// ``` 68// ```
70// -> 69// ->
@@ -195,6 +194,7 @@ impl Foo for S {
195 fn baz(&self) { 194 fn baz(&self) {
196 todo!() 195 todo!()
197 } 196 }
197
198}"#, 198}"#,
199 ); 199 );
200 } 200 }
@@ -231,6 +231,7 @@ impl Foo for S {
231 fn foo(&self) { 231 fn foo(&self) {
232 ${0:todo!()} 232 ${0:todo!()}
233 } 233 }
234
234}"#, 235}"#,
235 ); 236 );
236 } 237 }
diff --git a/crates/ide_assists/src/handlers/auto_import.rs b/crates/ide_assists/src/handlers/auto_import.rs
index a454a2af3..506cc292c 100644
--- a/crates/ide_assists/src/handlers/auto_import.rs
+++ b/crates/ide_assists/src/handlers/auto_import.rs
@@ -969,4 +969,30 @@ mod bar {
969"#, 969"#,
970 ); 970 );
971 } 971 }
972
973 #[test]
974 fn uses_abs_path_with_extern_crate_clash() {
975 check_assist(
976 auto_import,
977 r#"
978//- /main.rs crate:main deps:foo
979mod foo {}
980
981const _: () = {
982 Foo$0
983};
984//- /foo.rs crate:foo
985pub struct Foo
986"#,
987 r#"
988use ::foo::Foo;
989
990mod foo {}
991
992const _: () = {
993 Foo
994};
995"#,
996 );
997 }
972} 998}
diff --git a/crates/ide_assists/src/handlers/generate_new.rs b/crates/ide_assists/src/handlers/generate_new.rs
index 8ce5930b7..959a1f86c 100644
--- a/crates/ide_assists/src/handlers/generate_new.rs
+++ b/crates/ide_assists/src/handlers/generate_new.rs
@@ -1,4 +1,3 @@
1use ast::Adt;
2use itertools::Itertools; 1use itertools::Itertools;
3use stdx::format_to; 2use stdx::format_to;
4use syntax::ast::{self, AstNode, NameOwner, StructKind, VisibilityOwner}; 3use syntax::ast::{self, AstNode, NameOwner, StructKind, VisibilityOwner};
@@ -37,7 +36,7 @@ pub(crate) fn generate_new(acc: &mut Assists, ctx: &AssistContext) -> Option<()>
37 }; 36 };
38 37
39 // Return early if we've found an existing new fn 38 // Return early if we've found an existing new fn
40 let impl_def = find_struct_impl(&ctx, &Adt::Struct(strukt.clone()), "new")?; 39 let impl_def = find_struct_impl(&ctx, &ast::Adt::Struct(strukt.clone()), "new")?;
41 40
42 let target = strukt.syntax().text_range(); 41 let target = strukt.syntax().text_range();
43 acc.add(AssistId("generate_new", AssistKind::Generate), "Generate `new`", target, |builder| { 42 acc.add(AssistId("generate_new", AssistKind::Generate), "Generate `new`", target, |builder| {
@@ -60,7 +59,7 @@ pub(crate) fn generate_new(acc: &mut Assists, ctx: &AssistContext) -> Option<()>
60 let start_offset = impl_def 59 let start_offset = impl_def
61 .and_then(|impl_def| find_impl_block_start(impl_def, &mut buf)) 60 .and_then(|impl_def| find_impl_block_start(impl_def, &mut buf))
62 .unwrap_or_else(|| { 61 .unwrap_or_else(|| {
63 buf = generate_impl_text(&Adt::Struct(strukt.clone()), &buf); 62 buf = generate_impl_text(&ast::Adt::Struct(strukt.clone()), &buf);
64 strukt.syntax().text_range().end() 63 strukt.syntax().text_range().end()
65 }); 64 });
66 65
@@ -81,101 +80,132 @@ mod tests {
81 use super::*; 80 use super::*;
82 81
83 #[test] 82 #[test]
84 #[rustfmt::skip]
85 fn test_generate_new() { 83 fn test_generate_new() {
86 // Check output of generation
87 check_assist( 84 check_assist(
88 generate_new, 85 generate_new,
89"struct Foo {$0}", 86 r#"
90"struct Foo {} 87struct Foo {$0}
88"#,
89 r#"
90struct Foo {}
91 91
92impl Foo { 92impl Foo {
93 fn $0new() -> Self { Self { } } 93 fn $0new() -> Self { Self { } }
94}", 94}
95"#,
95 ); 96 );
96 check_assist( 97 check_assist(
97 generate_new, 98 generate_new,
98"struct Foo<T: Clone> {$0}", 99 r#"
99"struct Foo<T: Clone> {} 100struct Foo<T: Clone> {$0}
101"#,
102 r#"
103struct Foo<T: Clone> {}
100 104
101impl<T: Clone> Foo<T> { 105impl<T: Clone> Foo<T> {
102 fn $0new() -> Self { Self { } } 106 fn $0new() -> Self { Self { } }
103}", 107}
108"#,
104 ); 109 );
105 check_assist( 110 check_assist(
106 generate_new, 111 generate_new,
107"struct Foo<'a, T: Foo<'a>> {$0}", 112 r#"
108"struct Foo<'a, T: Foo<'a>> {} 113struct Foo<'a, T: Foo<'a>> {$0}
114"#,
115 r#"
116struct Foo<'a, T: Foo<'a>> {}
109 117
110impl<'a, T: Foo<'a>> Foo<'a, T> { 118impl<'a, T: Foo<'a>> Foo<'a, T> {
111 fn $0new() -> Self { Self { } } 119 fn $0new() -> Self { Self { } }
112}", 120}
121"#,
113 ); 122 );
114 check_assist( 123 check_assist(
115 generate_new, 124 generate_new,
116"struct Foo { baz: String $0}", 125 r#"
117"struct Foo { baz: String } 126struct Foo { baz: String $0}
127"#,
128 r#"
129struct Foo { baz: String }
118 130
119impl Foo { 131impl Foo {
120 fn $0new(baz: String) -> Self { Self { baz } } 132 fn $0new(baz: String) -> Self { Self { baz } }
121}", 133}
134"#,
122 ); 135 );
123 check_assist( 136 check_assist(
124 generate_new, 137 generate_new,
125"struct Foo { baz: String, qux: Vec<i32> $0}", 138 r#"
126"struct Foo { baz: String, qux: Vec<i32> } 139struct Foo { baz: String, qux: Vec<i32> $0}
140"#,
141 r#"
142struct Foo { baz: String, qux: Vec<i32> }
127 143
128impl Foo { 144impl Foo {
129 fn $0new(baz: String, qux: Vec<i32>) -> Self { Self { baz, qux } } 145 fn $0new(baz: String, qux: Vec<i32>) -> Self { Self { baz, qux } }
130}", 146}
147"#,
131 ); 148 );
149 }
132 150
133 // Check that visibility modifiers don't get brought in for fields 151 #[test]
152 fn check_that_visibility_modifiers_dont_get_brought_in() {
134 check_assist( 153 check_assist(
135 generate_new, 154 generate_new,
136"struct Foo { pub baz: String, pub qux: Vec<i32> $0}", 155 r#"
137"struct Foo { pub baz: String, pub qux: Vec<i32> } 156struct Foo { pub baz: String, pub qux: Vec<i32> $0}
157"#,
158 r#"
159struct Foo { pub baz: String, pub qux: Vec<i32> }
138 160
139impl Foo { 161impl Foo {
140 fn $0new(baz: String, qux: Vec<i32>) -> Self { Self { baz, qux } } 162 fn $0new(baz: String, qux: Vec<i32>) -> Self { Self { baz, qux } }
141}", 163}
164"#,
142 ); 165 );
166 }
143 167
144 // Check that it reuses existing impls 168 #[test]
169 fn check_it_reuses_existing_impls() {
145 check_assist( 170 check_assist(
146 generate_new, 171 generate_new,
147"struct Foo {$0} 172 r#"
173struct Foo {$0}
148 174
149impl Foo {} 175impl Foo {}
150", 176"#,
151"struct Foo {} 177 r#"
178struct Foo {}
152 179
153impl Foo { 180impl Foo {
154 fn $0new() -> Self { Self { } } 181 fn $0new() -> Self { Self { } }
155} 182}
156", 183"#,
157 ); 184 );
158 check_assist( 185 check_assist(
159 generate_new, 186 generate_new,
160"struct Foo {$0} 187 r#"
188struct Foo {$0}
161 189
162impl Foo { 190impl Foo {
163 fn qux(&self) {} 191 fn qux(&self) {}
164} 192}
165", 193"#,
166"struct Foo {} 194 r#"
195struct Foo {}
167 196
168impl Foo { 197impl Foo {
169 fn $0new() -> Self { Self { } } 198 fn $0new() -> Self { Self { } }
170 199
171 fn qux(&self) {} 200 fn qux(&self) {}
172} 201}
173", 202"#,
174 ); 203 );
175 204
176 check_assist( 205 check_assist(
177 generate_new, 206 generate_new,
178"struct Foo {$0} 207 r#"
208struct Foo {$0}
179 209
180impl Foo { 210impl Foo {
181 fn qux(&self) {} 211 fn qux(&self) {}
@@ -183,8 +213,9 @@ impl Foo {
183 5 213 5
184 } 214 }
185} 215}
186", 216"#,
187"struct Foo {} 217 r#"
218struct Foo {}
188 219
189impl Foo { 220impl Foo {
190 fn $0new() -> Self { Self { } } 221 fn $0new() -> Self { Self { } }
@@ -194,27 +225,37 @@ impl Foo {
194 5 225 5
195 } 226 }
196} 227}
197", 228"#,
198 ); 229 );
230 }
199 231
200 // Check visibility of new fn based on struct 232 #[test]
233 fn check_visibility_of_new_fn_based_on_struct() {
201 check_assist( 234 check_assist(
202 generate_new, 235 generate_new,
203"pub struct Foo {$0}", 236 r#"
204"pub struct Foo {} 237pub struct Foo {$0}
238"#,
239 r#"
240pub struct Foo {}
205 241
206impl Foo { 242impl Foo {
207 pub fn $0new() -> Self { Self { } } 243 pub fn $0new() -> Self { Self { } }
208}", 244}
245"#,
209 ); 246 );
210 check_assist( 247 check_assist(
211 generate_new, 248 generate_new,
212"pub(crate) struct Foo {$0}", 249 r#"
213"pub(crate) struct Foo {} 250pub(crate) struct Foo {$0}
251"#,
252 r#"
253pub(crate) struct Foo {}
214 254
215impl Foo { 255impl Foo {
216 pub(crate) fn $0new() -> Self { Self { } } 256 pub(crate) fn $0new() -> Self { Self { } }
217}", 257}
258"#,
218 ); 259 );
219 } 260 }
220 261
@@ -222,26 +263,28 @@ impl Foo {
222 fn generate_new_not_applicable_if_fn_exists() { 263 fn generate_new_not_applicable_if_fn_exists() {
223 check_assist_not_applicable( 264 check_assist_not_applicable(
224 generate_new, 265 generate_new,
225 " 266 r#"
226struct Foo {$0} 267struct Foo {$0}
227 268
228impl Foo { 269impl Foo {
229 fn new() -> Self { 270 fn new() -> Self {
230 Self 271 Self
231 } 272 }
232}", 273}
274"#,
233 ); 275 );
234 276
235 check_assist_not_applicable( 277 check_assist_not_applicable(
236 generate_new, 278 generate_new,
237 " 279 r#"
238struct Foo {$0} 280struct Foo {$0}
239 281
240impl Foo { 282impl Foo {
241 fn New() -> Self { 283 fn New() -> Self {
242 Self 284 Self
243 } 285 }
244}", 286}
287"#,
245 ); 288 );
246 } 289 }
247 290
@@ -249,12 +292,12 @@ impl Foo {
249 fn generate_new_target() { 292 fn generate_new_target() {
250 check_assist_target( 293 check_assist_target(
251 generate_new, 294 generate_new,
252 " 295 r#"
253struct SomeThingIrrelevant; 296struct SomeThingIrrelevant;
254/// Has a lifetime parameter 297/// Has a lifetime parameter
255struct Foo<'a, T: Foo<'a>> {$0} 298struct Foo<'a, T: Foo<'a>> {$0}
256struct EvenMoreIrrelevant; 299struct EvenMoreIrrelevant;
257", 300"#,
258 "/// Has a lifetime parameter 301 "/// Has a lifetime parameter
259struct Foo<'a, T: Foo<'a>> {}", 302struct Foo<'a, T: Foo<'a>> {}",
260 ); 303 );
@@ -264,7 +307,7 @@ struct Foo<'a, T: Foo<'a>> {}",
264 fn test_unrelated_new() { 307 fn test_unrelated_new() {
265 check_assist( 308 check_assist(
266 generate_new, 309 generate_new,
267 r##" 310 r#"
268pub struct AstId<N: AstNode> { 311pub struct AstId<N: AstNode> {
269 file_id: HirFileId, 312 file_id: HirFileId,
270 file_ast_id: FileAstId<N>, 313 file_ast_id: FileAstId<N>,
@@ -285,8 +328,9 @@ impl<T> Source<T> {
285 pub fn map<F: FnOnce(T) -> U, U>(self, f: F) -> Source<U> { 328 pub fn map<F: FnOnce(T) -> U, U>(self, f: F) -> Source<U> {
286 Source { file_id: self.file_id, ast: f(self.ast) } 329 Source { file_id: self.file_id, ast: f(self.ast) }
287 } 330 }
288}"##, 331}
289 r##" 332"#,
333 r#"
290pub struct AstId<N: AstNode> { 334pub struct AstId<N: AstNode> {
291 file_id: HirFileId, 335 file_id: HirFileId,
292 file_ast_id: FileAstId<N>, 336 file_ast_id: FileAstId<N>,
@@ -309,7 +353,8 @@ impl<T> Source<T> {
309 pub fn map<F: FnOnce(T) -> U, U>(self, f: F) -> Source<U> { 353 pub fn map<F: FnOnce(T) -> U, U>(self, f: F) -> Source<U> {
310 Source { file_id: self.file_id, ast: f(self.ast) } 354 Source { file_id: self.file_id, ast: f(self.ast) }
311 } 355 }
312}"##, 356}
357"#,
313 ); 358 );
314 } 359 }
315} 360}
diff --git a/crates/ide_assists/src/handlers/move_module_to_file.rs b/crates/ide_assists/src/handlers/move_module_to_file.rs
index 6e685b4b2..93f702c55 100644
--- a/crates/ide_assists/src/handlers/move_module_to_file.rs
+++ b/crates/ide_assists/src/handlers/move_module_to_file.rs
@@ -1,4 +1,4 @@
1use ast::{edit::IndentLevel, VisibilityOwner}; 1use ast::edit::IndentLevel;
2use ide_db::base_db::AnchoredPathBuf; 2use ide_db::base_db::AnchoredPathBuf;
3use stdx::format_to; 3use stdx::format_to;
4use syntax::{ 4use syntax::{
@@ -60,12 +60,18 @@ pub(crate) fn move_module_to_file(acc: &mut Assists, ctx: &AssistContext) -> Opt
60 }; 60 };
61 61
62 let mut buf = String::new(); 62 let mut buf = String::new();
63 if let Some(v) = module_ast.visibility() {
64 format_to!(buf, "{} ", v);
65 }
66 format_to!(buf, "mod {};", module_name); 63 format_to!(buf, "mod {};", module_name);
67 64
68 builder.replace(module_ast.syntax().text_range(), buf); 65 let replacement_start = if let Some(mod_token) = module_ast.mod_token() {
66 mod_token.text_range().start()
67 } else {
68 module_ast.syntax().text_range().start()
69 };
70
71 builder.replace(
72 TextRange::new(replacement_start, module_ast.syntax().text_range().end()),
73 buf,
74 );
69 75
70 let dst = AnchoredPathBuf { anchor: ctx.frange.file_id, path }; 76 let dst = AnchoredPathBuf { anchor: ctx.frange.file_id, path };
71 builder.create_file(dst, contents); 77 builder.create_file(dst, contents);
@@ -184,4 +190,26 @@ pub(crate) mod tests;
184 cov_mark::check!(available_before_curly); 190 cov_mark::check!(available_before_curly);
185 check_assist_not_applicable(move_module_to_file, r#"mod m { $0 }"#); 191 check_assist_not_applicable(move_module_to_file, r#"mod m { $0 }"#);
186 } 192 }
193
194 #[test]
195 fn keep_outer_comments_and_attributes() {
196 check_assist(
197 move_module_to_file,
198 r#"
199/// doc comment
200#[attribute]
201mod $0tests {
202 #[test] fn t() {}
203}
204"#,
205 r#"
206//- /main.rs
207/// doc comment
208#[attribute]
209mod tests;
210//- /tests.rs
211#[test] fn t() {}
212"#,
213 );
214 }
187} 215}
diff --git a/crates/ide_assists/src/handlers/replace_unwrap_with_match.rs b/crates/ide_assists/src/handlers/replace_unwrap_with_match.rs
index 0fec961b4..a3bfa221c 100644
--- a/crates/ide_assists/src/handlers/replace_unwrap_with_match.rs
+++ b/crates/ide_assists/src/handlers/replace_unwrap_with_match.rs
@@ -17,7 +17,7 @@ use ide_db::ty_filter::TryEnum;
17 17
18// Assist: replace_unwrap_with_match 18// Assist: replace_unwrap_with_match
19// 19//
20// Replaces `unwrap` a `match` expression. Works for Result and Option. 20// Replaces `unwrap` with a `match` expression. Works for Result and Option.
21// 21//
22// ``` 22// ```
23// enum Result<T, E> { Ok(T), Err(E) } 23// enum Result<T, E> { Ok(T), Err(E) }
diff --git a/crates/ide_assists/src/tests/generated.rs b/crates/ide_assists/src/tests/generated.rs
index 49c1a9776..4406406a2 100644
--- a/crates/ide_assists/src/tests/generated.rs
+++ b/crates/ide_assists/src/tests/generated.rs
@@ -50,7 +50,6 @@ trait Trait {
50impl Trait for () { 50impl Trait for () {
51 type X = (); 51 type X = ();
52 fn foo(&self) {}$0 52 fn foo(&self) {}$0
53
54} 53}
55"#####, 54"#####,
56 r#####" 55 r#####"
diff --git a/crates/ide_assists/src/utils.rs b/crates/ide_assists/src/utils.rs
index 0dcf20c61..fc7caee04 100644
--- a/crates/ide_assists/src/utils.rs
+++ b/crates/ide_assists/src/utils.rs
@@ -17,7 +17,7 @@ use syntax::{
17 ast::AttrsOwner, 17 ast::AttrsOwner,
18 ast::NameOwner, 18 ast::NameOwner,
19 ast::{self, edit, make, ArgListOwner, GenericParamsOwner}, 19 ast::{self, edit, make, ArgListOwner, GenericParamsOwner},
20 AstNode, Direction, SmolStr, 20 ted, AstNode, Direction, SmolStr,
21 SyntaxKind::*, 21 SyntaxKind::*,
22 SyntaxNode, TextSize, T, 22 SyntaxNode, TextSize, T,
23}; 23};
@@ -128,43 +128,43 @@ pub fn add_trait_assoc_items_to_impl(
128 sema: &hir::Semantics<ide_db::RootDatabase>, 128 sema: &hir::Semantics<ide_db::RootDatabase>,
129 items: Vec<ast::AssocItem>, 129 items: Vec<ast::AssocItem>,
130 trait_: hir::Trait, 130 trait_: hir::Trait,
131 impl_def: ast::Impl, 131 impl_: ast::Impl,
132 target_scope: hir::SemanticsScope, 132 target_scope: hir::SemanticsScope,
133) -> (ast::Impl, ast::AssocItem) { 133) -> (ast::Impl, ast::AssocItem) {
134 let impl_item_list = impl_def.assoc_item_list().unwrap_or_else(make::assoc_item_list);
135
136 let n_existing_items = impl_item_list.assoc_items().count();
137 let source_scope = sema.scope_for_def(trait_); 134 let source_scope = sema.scope_for_def(trait_);
138 let ast_transform = QualifyPaths::new(&target_scope, &source_scope) 135 let ast_transform = QualifyPaths::new(&target_scope, &source_scope)
139 .or(SubstituteTypeParams::for_trait_impl(&source_scope, trait_, impl_def.clone())); 136 .or(SubstituteTypeParams::for_trait_impl(&source_scope, trait_, impl_.clone()));
140 137
141 let items = items 138 let items = items
142 .into_iter() 139 .into_iter()
143 .map(|it| it.clone_for_update()) 140 .map(|it| it.clone_for_update())
144 .inspect(|it| ast_transform::apply(&*ast_transform, it)) 141 .inspect(|it| ast_transform::apply(&*ast_transform, it))
145 .map(|it| match it { 142 .map(|it| edit::remove_attrs_and_docs(&it).clone_subtree().clone_for_update());
146 ast::AssocItem::Fn(def) => ast::AssocItem::Fn(add_body(def)), 143
147 ast::AssocItem::TypeAlias(def) => ast::AssocItem::TypeAlias(def.remove_bounds()), 144 let res = impl_.clone_for_update();
148 _ => it, 145
149 }) 146 let assoc_item_list = res.get_or_create_assoc_item_list();
150 .map(|it| edit::remove_attrs_and_docs(&it)); 147 let mut first_item = None;
151 148 for item in items {
152 let new_impl_item_list = impl_item_list.append_items(items); 149 first_item.get_or_insert_with(|| item.clone());
153 let new_impl_def = impl_def.with_assoc_item_list(new_impl_item_list); 150 match &item {
154 let first_new_item = 151 ast::AssocItem::Fn(fn_) if fn_.body().is_none() => {
155 new_impl_def.assoc_item_list().unwrap().assoc_items().nth(n_existing_items).unwrap();
156 return (new_impl_def, first_new_item);
157
158 fn add_body(fn_def: ast::Fn) -> ast::Fn {
159 match fn_def.body() {
160 Some(_) => fn_def,
161 None => {
162 let body = make::block_expr(None, Some(make::ext::expr_todo())) 152 let body = make::block_expr(None, Some(make::ext::expr_todo()))
163 .indent(edit::IndentLevel(1)); 153 .indent(edit::IndentLevel(1));
164 fn_def.with_body(body) 154 ted::replace(fn_.get_or_create_body().syntax(), body.clone_for_update().syntax())
165 } 155 }
156 ast::AssocItem::TypeAlias(type_alias) => {
157 if let Some(type_bound_list) = type_alias.type_bound_list() {
158 type_bound_list.remove()
159 }
160 }
161 _ => {}
166 } 162 }
163
164 assoc_item_list.add_item(item)
167 } 165 }
166
167 (res, first_item.unwrap())
168} 168}
169 169
170#[derive(Clone, Copy, Debug)] 170#[derive(Clone, Copy, Debug)]