diff options
Diffstat (limited to 'crates/assists/src')
-rw-r--r-- | crates/assists/src/handlers/extract_struct_from_enum_variant.rs | 120 | ||||
-rw-r--r-- | crates/assists/src/handlers/remove_unused_param.rs | 83 | ||||
-rw-r--r-- | crates/assists/src/utils/insert_use.rs | 96 |
3 files changed, 251 insertions, 48 deletions
diff --git a/crates/assists/src/handlers/extract_struct_from_enum_variant.rs b/crates/assists/src/handlers/extract_struct_from_enum_variant.rs index 84662d832..067afabf2 100644 --- a/crates/assists/src/handlers/extract_struct_from_enum_variant.rs +++ b/crates/assists/src/handlers/extract_struct_from_enum_variant.rs | |||
@@ -5,10 +5,9 @@ use hir::{AsName, EnumVariant, Module, ModuleDef, Name}; | |||
5 | use ide_db::{defs::Definition, search::Reference, RootDatabase}; | 5 | use ide_db::{defs::Definition, search::Reference, RootDatabase}; |
6 | use rustc_hash::{FxHashMap, FxHashSet}; | 6 | use rustc_hash::{FxHashMap, FxHashSet}; |
7 | use syntax::{ | 7 | use syntax::{ |
8 | algo::find_node_at_offset, | 8 | algo::{find_node_at_offset, SyntaxRewriter}, |
9 | algo::SyntaxRewriter, | 9 | ast::{self, edit::IndentLevel, make, AstNode, NameOwner, VisibilityOwner}, |
10 | ast::{self, edit::IndentLevel, make, ArgListOwner, AstNode, NameOwner, VisibilityOwner}, | 10 | SourceFile, SyntaxElement, SyntaxNode, T, |
11 | SourceFile, SyntaxElement, | ||
12 | }; | 11 | }; |
13 | 12 | ||
14 | use crate::{ | 13 | use crate::{ |
@@ -130,17 +129,21 @@ fn existing_definition(db: &RootDatabase, variant_name: &ast::Name, variant: &En | |||
130 | fn insert_import( | 129 | fn insert_import( |
131 | ctx: &AssistContext, | 130 | ctx: &AssistContext, |
132 | rewriter: &mut SyntaxRewriter, | 131 | rewriter: &mut SyntaxRewriter, |
133 | path: &ast::PathExpr, | 132 | scope_node: &SyntaxNode, |
134 | module: &Module, | 133 | module: &Module, |
135 | enum_module_def: &ModuleDef, | 134 | enum_module_def: &ModuleDef, |
136 | variant_hir_name: &Name, | 135 | variant_hir_name: &Name, |
137 | ) -> Option<()> { | 136 | ) -> Option<()> { |
138 | let db = ctx.db(); | 137 | let db = ctx.db(); |
139 | let mod_path = module.find_use_path(db, enum_module_def.clone()); | 138 | let mod_path = module.find_use_path_prefixed( |
139 | db, | ||
140 | enum_module_def.clone(), | ||
141 | ctx.config.insert_use.prefix_kind, | ||
142 | ); | ||
140 | if let Some(mut mod_path) = mod_path { | 143 | if let Some(mut mod_path) = mod_path { |
141 | mod_path.segments.pop(); | 144 | mod_path.segments.pop(); |
142 | mod_path.segments.push(variant_hir_name.clone()); | 145 | mod_path.segments.push(variant_hir_name.clone()); |
143 | let scope = ImportScope::find_insert_use_container(path.syntax(), ctx)?; | 146 | let scope = ImportScope::find_insert_use_container(scope_node, ctx)?; |
144 | 147 | ||
145 | *rewriter += insert_use(&scope, mod_path_to_ast(&mod_path), ctx.config.insert_use.merge); | 148 | *rewriter += insert_use(&scope, mod_path_to_ast(&mod_path), ctx.config.insert_use.merge); |
146 | } | 149 | } |
@@ -204,27 +207,31 @@ fn update_reference( | |||
204 | variant_hir_name: &Name, | 207 | variant_hir_name: &Name, |
205 | visited_modules_set: &mut FxHashSet<Module>, | 208 | visited_modules_set: &mut FxHashSet<Module>, |
206 | ) -> Option<()> { | 209 | ) -> Option<()> { |
207 | let path_expr: ast::PathExpr = find_node_at_offset::<ast::PathExpr>( | 210 | let offset = reference.file_range.range.start(); |
208 | source_file.syntax(), | 211 | let (segment, expr) = if let Some(path_expr) = |
209 | reference.file_range.range.start(), | 212 | find_node_at_offset::<ast::PathExpr>(source_file.syntax(), offset) |
210 | )?; | 213 | { |
211 | let call = path_expr.syntax().parent().and_then(ast::CallExpr::cast)?; | 214 | // tuple variant |
212 | let list = call.arg_list()?; | 215 | (path_expr.path()?.segment()?, path_expr.syntax().parent()?.clone()) |
213 | let segment = path_expr.path()?.segment()?; | 216 | } else if let Some(record_expr) = |
214 | let module = ctx.sema.scope(&path_expr.syntax()).module()?; | 217 | find_node_at_offset::<ast::RecordExpr>(source_file.syntax(), offset) |
218 | { | ||
219 | // record variant | ||
220 | (record_expr.path()?.segment()?, record_expr.syntax().clone()) | ||
221 | } else { | ||
222 | return None; | ||
223 | }; | ||
224 | |||
225 | let module = ctx.sema.scope(&expr).module()?; | ||
215 | if !visited_modules_set.contains(&module) { | 226 | if !visited_modules_set.contains(&module) { |
216 | if insert_import(ctx, rewriter, &path_expr, &module, enum_module_def, variant_hir_name) | 227 | if insert_import(ctx, rewriter, &expr, &module, enum_module_def, variant_hir_name).is_some() |
217 | .is_some() | ||
218 | { | 228 | { |
219 | visited_modules_set.insert(module); | 229 | visited_modules_set.insert(module); |
220 | } | 230 | } |
221 | } | 231 | } |
222 | 232 | rewriter.insert_after(segment.syntax(), &make::token(T!['('])); | |
223 | let lparen = syntax::SyntaxElement::from(list.l_paren_token()?); | 233 | rewriter.insert_after(segment.syntax(), segment.syntax()); |
224 | let rparen = syntax::SyntaxElement::from(list.r_paren_token()?); | 234 | rewriter.insert_after(&expr, &make::token(T![')'])); |
225 | rewriter.insert_after(&lparen, segment.syntax()); | ||
226 | rewriter.insert_after(&lparen, &lparen); | ||
227 | rewriter.insert_before(&rparen, &rparen); | ||
228 | Some(()) | 235 | Some(()) |
229 | } | 236 | } |
230 | 237 | ||
@@ -320,7 +327,7 @@ fn another_fn() { | |||
320 | r#"use my_mod::my_other_mod::MyField; | 327 | r#"use my_mod::my_other_mod::MyField; |
321 | 328 | ||
322 | mod my_mod { | 329 | mod my_mod { |
323 | use my_other_mod::MyField; | 330 | use self::my_other_mod::MyField; |
324 | 331 | ||
325 | fn another_fn() { | 332 | fn another_fn() { |
326 | let m = my_other_mod::MyEnum::MyField(MyField(1, 1)); | 333 | let m = my_other_mod::MyEnum::MyField(MyField(1, 1)); |
@@ -346,6 +353,33 @@ fn another_fn() { | |||
346 | } | 353 | } |
347 | 354 | ||
348 | #[test] | 355 | #[test] |
356 | fn extract_record_fix_references() { | ||
357 | check_assist( | ||
358 | extract_struct_from_enum_variant, | ||
359 | r#" | ||
360 | enum E { | ||
361 | <|>V { i: i32, j: i32 } | ||
362 | } | ||
363 | |||
364 | fn f() { | ||
365 | let e = E::V { i: 9, j: 2 }; | ||
366 | } | ||
367 | "#, | ||
368 | r#" | ||
369 | struct V{ pub i: i32, pub j: i32 } | ||
370 | |||
371 | enum E { | ||
372 | V(V) | ||
373 | } | ||
374 | |||
375 | fn f() { | ||
376 | let e = E::V(V { i: 9, j: 2 }); | ||
377 | } | ||
378 | "#, | ||
379 | ) | ||
380 | } | ||
381 | |||
382 | #[test] | ||
349 | fn test_several_files() { | 383 | fn test_several_files() { |
350 | check_assist( | 384 | check_assist( |
351 | extract_struct_from_enum_variant, | 385 | extract_struct_from_enum_variant, |
@@ -372,9 +406,7 @@ enum E { | |||
372 | mod foo; | 406 | mod foo; |
373 | 407 | ||
374 | //- /foo.rs | 408 | //- /foo.rs |
375 | use V; | 409 | use crate::{E, V}; |
376 | |||
377 | use crate::E; | ||
378 | fn f() { | 410 | fn f() { |
379 | let e = E::V(V(9, 2)); | 411 | let e = E::V(V(9, 2)); |
380 | } | 412 | } |
@@ -384,7 +416,6 @@ fn f() { | |||
384 | 416 | ||
385 | #[test] | 417 | #[test] |
386 | fn test_several_files_record() { | 418 | fn test_several_files_record() { |
387 | // FIXME: this should fix the usage as well! | ||
388 | check_assist( | 419 | check_assist( |
389 | extract_struct_from_enum_variant, | 420 | extract_struct_from_enum_variant, |
390 | r#" | 421 | r#" |
@@ -401,6 +432,7 @@ fn f() { | |||
401 | } | 432 | } |
402 | "#, | 433 | "#, |
403 | r#" | 434 | r#" |
435 | //- /main.rs | ||
404 | struct V{ pub i: i32, pub j: i32 } | 436 | struct V{ pub i: i32, pub j: i32 } |
405 | 437 | ||
406 | enum E { | 438 | enum E { |
@@ -408,10 +440,42 @@ enum E { | |||
408 | } | 440 | } |
409 | mod foo; | 441 | mod foo; |
410 | 442 | ||
443 | //- /foo.rs | ||
444 | use crate::{E, V}; | ||
445 | fn f() { | ||
446 | let e = E::V(V { i: 9, j: 2 }); | ||
447 | } | ||
411 | "#, | 448 | "#, |
412 | ) | 449 | ) |
413 | } | 450 | } |
414 | 451 | ||
452 | #[test] | ||
453 | fn test_extract_struct_record_nested_call_exp() { | ||
454 | check_assist( | ||
455 | extract_struct_from_enum_variant, | ||
456 | r#" | ||
457 | enum A { <|>One { a: u32, b: u32 } } | ||
458 | |||
459 | struct B(A); | ||
460 | |||
461 | fn foo() { | ||
462 | let _ = B(A::One { a: 1, b: 2 }); | ||
463 | } | ||
464 | "#, | ||
465 | r#" | ||
466 | struct One{ pub a: u32, pub b: u32 } | ||
467 | |||
468 | enum A { One(One) } | ||
469 | |||
470 | struct B(A); | ||
471 | |||
472 | fn foo() { | ||
473 | let _ = B(A::One(One { a: 1, b: 2 })); | ||
474 | } | ||
475 | "#, | ||
476 | ); | ||
477 | } | ||
478 | |||
415 | fn check_not_applicable(ra_fixture: &str) { | 479 | fn check_not_applicable(ra_fixture: &str) { |
416 | let fixture = | 480 | let fixture = |
417 | format!("//- /main.rs crate:main deps:core\n{}\n{}", ra_fixture, FamousDefs::FIXTURE); | 481 | format!("//- /main.rs crate:main deps:core\n{}\n{}", ra_fixture, FamousDefs::FIXTURE); |
diff --git a/crates/assists/src/handlers/remove_unused_param.rs b/crates/assists/src/handlers/remove_unused_param.rs index 5fccca54b..1ff5e92b0 100644 --- a/crates/assists/src/handlers/remove_unused_param.rs +++ b/crates/assists/src/handlers/remove_unused_param.rs | |||
@@ -73,7 +73,8 @@ fn process_usage( | |||
73 | let source_file = ctx.sema.parse(usage.file_range.file_id); | 73 | let source_file = ctx.sema.parse(usage.file_range.file_id); |
74 | let call_expr: ast::CallExpr = | 74 | let call_expr: ast::CallExpr = |
75 | find_node_at_range(source_file.syntax(), usage.file_range.range)?; | 75 | find_node_at_range(source_file.syntax(), usage.file_range.range)?; |
76 | if call_expr.expr()?.syntax().text_range() != usage.file_range.range { | 76 | let call_expr_range = call_expr.expr()?.syntax().text_range(); |
77 | if !call_expr_range.contains_range(usage.file_range.range) { | ||
77 | return None; | 78 | return None; |
78 | } | 79 | } |
79 | let arg = call_expr.arg_list()?.args().nth(arg_to_remove)?; | 80 | let arg = call_expr.arg_list()?.args().nth(arg_to_remove)?; |
@@ -118,6 +119,53 @@ fn b() { foo(9, ) } | |||
118 | } | 119 | } |
119 | 120 | ||
120 | #[test] | 121 | #[test] |
122 | fn remove_unused_qualified_call() { | ||
123 | check_assist( | ||
124 | remove_unused_param, | ||
125 | r#" | ||
126 | mod bar { pub fn foo(x: i32, <|>y: i32) { x; } } | ||
127 | fn b() { bar::foo(9, 2) } | ||
128 | "#, | ||
129 | r#" | ||
130 | mod bar { pub fn foo(x: i32) { x; } } | ||
131 | fn b() { bar::foo(9) } | ||
132 | "#, | ||
133 | ); | ||
134 | } | ||
135 | |||
136 | #[test] | ||
137 | fn remove_unused_turbofished_func() { | ||
138 | check_assist( | ||
139 | remove_unused_param, | ||
140 | r#" | ||
141 | pub fn foo<T>(x: T, <|>y: i32) { x; } | ||
142 | fn b() { foo::<i32>(9, 2) } | ||
143 | "#, | ||
144 | r#" | ||
145 | pub fn foo<T>(x: T) { x; } | ||
146 | fn b() { foo::<i32>(9) } | ||
147 | "#, | ||
148 | ); | ||
149 | } | ||
150 | |||
151 | #[test] | ||
152 | fn remove_unused_generic_unused_param_func() { | ||
153 | check_assist( | ||
154 | remove_unused_param, | ||
155 | r#" | ||
156 | pub fn foo<T>(x: i32, <|>y: T) { x; } | ||
157 | fn b() { foo::<i32>(9, 2) } | ||
158 | fn b2() { foo(9, 2) } | ||
159 | "#, | ||
160 | r#" | ||
161 | pub fn foo<T>(x: i32) { x; } | ||
162 | fn b() { foo::<i32>(9) } | ||
163 | fn b2() { foo(9) } | ||
164 | "#, | ||
165 | ); | ||
166 | } | ||
167 | |||
168 | #[test] | ||
121 | fn keep_used() { | 169 | fn keep_used() { |
122 | mark::check!(keep_used); | 170 | mark::check!(keep_used); |
123 | check_assist_not_applicable( | 171 | check_assist_not_applicable( |
@@ -128,4 +176,37 @@ fn main() { foo(9, 2) } | |||
128 | "#, | 176 | "#, |
129 | ); | 177 | ); |
130 | } | 178 | } |
179 | |||
180 | #[test] | ||
181 | fn remove_across_files() { | ||
182 | check_assist( | ||
183 | remove_unused_param, | ||
184 | r#" | ||
185 | //- /main.rs | ||
186 | fn foo(x: i32, <|>y: i32) { x; } | ||
187 | |||
188 | mod foo; | ||
189 | |||
190 | //- /foo.rs | ||
191 | use super::foo; | ||
192 | |||
193 | fn bar() { | ||
194 | let _ = foo(1, 2); | ||
195 | } | ||
196 | "#, | ||
197 | r#" | ||
198 | //- /main.rs | ||
199 | fn foo(x: i32) { x; } | ||
200 | |||
201 | mod foo; | ||
202 | |||
203 | //- /foo.rs | ||
204 | use super::foo; | ||
205 | |||
206 | fn bar() { | ||
207 | let _ = foo(1); | ||
208 | } | ||
209 | "#, | ||
210 | ) | ||
211 | } | ||
131 | } | 212 | } |
diff --git a/crates/assists/src/utils/insert_use.rs b/crates/assists/src/utils/insert_use.rs index 84a0dffdd..af3fc96b6 100644 --- a/crates/assists/src/utils/insert_use.rs +++ b/crates/assists/src/utils/insert_use.rs | |||
@@ -9,7 +9,7 @@ use syntax::{ | |||
9 | edit::{AstNodeEdit, IndentLevel}, | 9 | edit::{AstNodeEdit, IndentLevel}, |
10 | make, AstNode, PathSegmentKind, VisibilityOwner, | 10 | make, AstNode, PathSegmentKind, VisibilityOwner, |
11 | }, | 11 | }, |
12 | InsertPosition, SyntaxElement, SyntaxNode, | 12 | AstToken, InsertPosition, NodeOrToken, SyntaxElement, SyntaxNode, SyntaxToken, |
13 | }; | 13 | }; |
14 | use test_utils::mark; | 14 | use test_utils::mark; |
15 | 15 | ||
@@ -63,27 +63,30 @@ impl ImportScope { | |||
63 | } | 63 | } |
64 | } | 64 | } |
65 | 65 | ||
66 | fn insert_pos_after_inner_attribute(&self) -> (InsertPosition<SyntaxElement>, AddBlankLine) { | 66 | fn insert_pos_after_last_inner_element(&self) -> (InsertPosition<SyntaxElement>, AddBlankLine) { |
67 | // check if the scope has inner attributes, we dont want to insert in front of them | 67 | self.as_syntax_node() |
68 | match self | 68 | .children_with_tokens() |
69 | .as_syntax_node() | 69 | .filter(|child| match child { |
70 | .children() | 70 | NodeOrToken::Node(node) => is_inner_attribute(node.clone()), |
71 | // no flat_map here cause we want to short circuit the iterator | 71 | NodeOrToken::Token(token) => is_inner_comment(token.clone()), |
72 | .map(ast::Attr::cast) | ||
73 | .take_while(|attr| { | ||
74 | attr.as_ref().map(|attr| attr.kind() == ast::AttrKind::Inner).unwrap_or(false) | ||
75 | }) | 72 | }) |
76 | .last() | 73 | .last() |
77 | .flatten() | 74 | .map(|last_inner_element| { |
78 | { | 75 | (InsertPosition::After(last_inner_element.into()), AddBlankLine::BeforeTwice) |
79 | Some(attr) => { | 76 | }) |
80 | (InsertPosition::After(attr.syntax().clone().into()), AddBlankLine::BeforeTwice) | 77 | .unwrap_or_else(|| self.first_insert_pos()) |
81 | } | ||
82 | None => self.first_insert_pos(), | ||
83 | } | ||
84 | } | 78 | } |
85 | } | 79 | } |
86 | 80 | ||
81 | fn is_inner_attribute(node: SyntaxNode) -> bool { | ||
82 | ast::Attr::cast(node).map(|attr| attr.kind()) == Some(ast::AttrKind::Inner) | ||
83 | } | ||
84 | |||
85 | fn is_inner_comment(token: SyntaxToken) -> bool { | ||
86 | ast::Comment::cast(token).and_then(|comment| comment.kind().doc) | ||
87 | == Some(ast::CommentPlacement::Inner) | ||
88 | } | ||
89 | |||
87 | /// Insert an import path into the given file/node. A `merge` value of none indicates that no import merging is allowed to occur. | 90 | /// Insert an import path into the given file/node. A `merge` value of none indicates that no import merging is allowed to occur. |
88 | pub(crate) fn insert_use<'a>( | 91 | pub(crate) fn insert_use<'a>( |
89 | scope: &ImportScope, | 92 | scope: &ImportScope, |
@@ -558,7 +561,7 @@ fn find_insert_position( | |||
558 | (InsertPosition::After(node.into()), AddBlankLine::BeforeTwice) | 561 | (InsertPosition::After(node.into()), AddBlankLine::BeforeTwice) |
559 | } | 562 | } |
560 | // there are no imports in this file at all | 563 | // there are no imports in this file at all |
561 | None => scope.insert_pos_after_inner_attribute(), | 564 | None => scope.insert_pos_after_last_inner_element(), |
562 | }, | 565 | }, |
563 | } | 566 | } |
564 | } | 567 | } |
@@ -830,12 +833,67 @@ use foo::bar;", | |||
830 | "foo::bar", | 833 | "foo::bar", |
831 | r"#![allow(unused_imports)] | 834 | r"#![allow(unused_imports)] |
832 | 835 | ||
836 | #![no_std] | ||
833 | fn main() {}", | 837 | fn main() {}", |
834 | r"#![allow(unused_imports)] | 838 | r"#![allow(unused_imports)] |
835 | 839 | ||
836 | use foo::bar; | 840 | #![no_std] |
837 | 841 | ||
842 | use foo::bar; | ||
838 | fn main() {}", | 843 | fn main() {}", |
844 | ); | ||
845 | } | ||
846 | |||
847 | #[test] | ||
848 | fn inserts_after_single_line_inner_comments() { | ||
849 | check_none( | ||
850 | "foo::bar::Baz", | ||
851 | "//! Single line inner comments do not allow any code before them.", | ||
852 | r#"//! Single line inner comments do not allow any code before them. | ||
853 | |||
854 | use foo::bar::Baz;"#, | ||
855 | ); | ||
856 | } | ||
857 | |||
858 | #[test] | ||
859 | fn inserts_after_multiline_inner_comments() { | ||
860 | check_none( | ||
861 | "foo::bar::Baz", | ||
862 | r#"/*! Multiline inner comments do not allow any code before them. */ | ||
863 | |||
864 | /*! Still an inner comment, cannot place any code before. */ | ||
865 | fn main() {}"#, | ||
866 | r#"/*! Multiline inner comments do not allow any code before them. */ | ||
867 | |||
868 | /*! Still an inner comment, cannot place any code before. */ | ||
869 | |||
870 | use foo::bar::Baz; | ||
871 | fn main() {}"#, | ||
872 | ) | ||
873 | } | ||
874 | |||
875 | #[test] | ||
876 | fn inserts_after_all_inner_items() { | ||
877 | check_none( | ||
878 | "foo::bar::Baz", | ||
879 | r#"#![allow(unused_imports)] | ||
880 | /*! Multiline line comment 2 */ | ||
881 | |||
882 | |||
883 | //! Single line comment 1 | ||
884 | #![no_std] | ||
885 | //! Single line comment 2 | ||
886 | fn main() {}"#, | ||
887 | r#"#![allow(unused_imports)] | ||
888 | /*! Multiline line comment 2 */ | ||
889 | |||
890 | |||
891 | //! Single line comment 1 | ||
892 | #![no_std] | ||
893 | //! Single line comment 2 | ||
894 | |||
895 | use foo::bar::Baz; | ||
896 | fn main() {}"#, | ||
839 | ) | 897 | ) |
840 | } | 898 | } |
841 | 899 | ||