diff options
Diffstat (limited to 'crates/ra_assists/src')
5 files changed, 582 insertions, 55 deletions
diff --git a/crates/ra_assists/src/handlers/auto_import.rs b/crates/ra_assists/src/handlers/auto_import.rs index 1fb701da5..c4aea2a06 100644 --- a/crates/ra_assists/src/handlers/auto_import.rs +++ b/crates/ra_assists/src/handlers/auto_import.rs | |||
@@ -1,10 +1,18 @@ | |||
1 | use ra_ide_db::imports_locator::ImportsLocator; | ||
2 | use ra_syntax::ast::{self, AstNode}; | ||
3 | |||
4 | use crate::{ | 1 | use crate::{ |
5 | assist_ctx::{Assist, AssistCtx}, | 2 | assist_ctx::{Assist, AssistCtx}, |
6 | insert_use_statement, AssistId, | 3 | insert_use_statement, AssistId, |
7 | }; | 4 | }; |
5 | use hir::{ | ||
6 | db::HirDatabase, AsAssocItem, AssocItemContainer, ModPath, Module, ModuleDef, PathResolution, | ||
7 | SourceAnalyzer, Trait, Type, | ||
8 | }; | ||
9 | use ra_ide_db::{imports_locator::ImportsLocator, RootDatabase}; | ||
10 | use ra_prof::profile; | ||
11 | use ra_syntax::{ | ||
12 | ast::{self, AstNode}, | ||
13 | SyntaxNode, | ||
14 | }; | ||
15 | use rustc_hash::FxHashSet; | ||
8 | use std::collections::BTreeSet; | 16 | use std::collections::BTreeSet; |
9 | 17 | ||
10 | // Assist: auto_import | 18 | // Assist: auto_import |
@@ -27,52 +35,24 @@ use std::collections::BTreeSet; | |||
27 | // # pub mod std { pub mod collections { pub struct HashMap { } } } | 35 | // # pub mod std { pub mod collections { pub struct HashMap { } } } |
28 | // ``` | 36 | // ``` |
29 | pub(crate) fn auto_import(ctx: AssistCtx) -> Option<Assist> { | 37 | pub(crate) fn auto_import(ctx: AssistCtx) -> Option<Assist> { |
30 | let path_under_caret: ast::Path = ctx.find_node_at_offset()?; | 38 | let auto_import_assets = AutoImportAssets::new(&ctx)?; |
31 | if path_under_caret.syntax().ancestors().find_map(ast::UseItem::cast).is_some() { | 39 | let proposed_imports = auto_import_assets.search_for_imports(ctx.db); |
32 | return None; | ||
33 | } | ||
34 | |||
35 | let module = path_under_caret.syntax().ancestors().find_map(ast::Module::cast); | ||
36 | let position = match module.and_then(|it| it.item_list()) { | ||
37 | Some(item_list) => item_list.syntax().clone(), | ||
38 | None => { | ||
39 | let current_file = | ||
40 | path_under_caret.syntax().ancestors().find_map(ast::SourceFile::cast)?; | ||
41 | current_file.syntax().clone() | ||
42 | } | ||
43 | }; | ||
44 | let source_analyzer = ctx.source_analyzer(&position, None); | ||
45 | let module_with_name_to_import = source_analyzer.module()?; | ||
46 | |||
47 | let name_ref_to_import = | ||
48 | path_under_caret.syntax().descendants().find_map(ast::NameRef::cast)?; | ||
49 | if source_analyzer | ||
50 | .resolve_path(ctx.db, &name_ref_to_import.syntax().ancestors().find_map(ast::Path::cast)?) | ||
51 | .is_some() | ||
52 | { | ||
53 | return None; | ||
54 | } | ||
55 | |||
56 | let name_to_import = name_ref_to_import.syntax().to_string(); | ||
57 | let proposed_imports = ImportsLocator::new(ctx.db) | ||
58 | .find_imports(&name_to_import) | ||
59 | .into_iter() | ||
60 | .filter_map(|module_def| module_with_name_to_import.find_use_path(ctx.db, module_def)) | ||
61 | .filter(|use_path| !use_path.segments.is_empty()) | ||
62 | .take(20) | ||
63 | .collect::<BTreeSet<_>>(); | ||
64 | |||
65 | if proposed_imports.is_empty() { | 40 | if proposed_imports.is_empty() { |
66 | return None; | 41 | return None; |
67 | } | 42 | } |
68 | 43 | ||
69 | let mut group = ctx.add_assist_group(format!("Import {}", name_to_import)); | 44 | let assist_group_name = if proposed_imports.len() == 1 { |
45 | format!("Import `{}`", proposed_imports.iter().next().unwrap()) | ||
46 | } else { | ||
47 | auto_import_assets.get_import_group_message() | ||
48 | }; | ||
49 | let mut group = ctx.add_assist_group(assist_group_name); | ||
70 | for import in proposed_imports { | 50 | for import in proposed_imports { |
71 | group.add_assist(AssistId("auto_import"), format!("Import `{}`", &import), |edit| { | 51 | group.add_assist(AssistId("auto_import"), format!("Import `{}`", &import), |edit| { |
72 | edit.target(path_under_caret.syntax().text_range()); | 52 | edit.target(auto_import_assets.syntax_under_caret.text_range()); |
73 | insert_use_statement( | 53 | insert_use_statement( |
74 | &position, | 54 | &auto_import_assets.syntax_under_caret, |
75 | path_under_caret.syntax(), | 55 | &auto_import_assets.syntax_under_caret, |
76 | &import, | 56 | &import, |
77 | edit.text_edit_builder(), | 57 | edit.text_edit_builder(), |
78 | ); | 58 | ); |
@@ -81,11 +61,232 @@ pub(crate) fn auto_import(ctx: AssistCtx) -> Option<Assist> { | |||
81 | group.finish() | 61 | group.finish() |
82 | } | 62 | } |
83 | 63 | ||
64 | struct AutoImportAssets { | ||
65 | import_candidate: ImportCandidate, | ||
66 | module_with_name_to_import: Module, | ||
67 | syntax_under_caret: SyntaxNode, | ||
68 | } | ||
69 | |||
70 | impl AutoImportAssets { | ||
71 | fn new(ctx: &AssistCtx) -> Option<Self> { | ||
72 | if let Some(path_under_caret) = ctx.find_node_at_offset::<ast::Path>() { | ||
73 | Self::for_regular_path(path_under_caret, &ctx) | ||
74 | } else { | ||
75 | Self::for_method_call(ctx.find_node_at_offset()?, &ctx) | ||
76 | } | ||
77 | } | ||
78 | |||
79 | fn for_method_call(method_call: ast::MethodCallExpr, ctx: &AssistCtx) -> Option<Self> { | ||
80 | let syntax_under_caret = method_call.syntax().to_owned(); | ||
81 | let source_analyzer = ctx.source_analyzer(&syntax_under_caret, None); | ||
82 | let module_with_name_to_import = source_analyzer.module()?; | ||
83 | Some(Self { | ||
84 | import_candidate: ImportCandidate::for_method_call( | ||
85 | &method_call, | ||
86 | &source_analyzer, | ||
87 | ctx.db, | ||
88 | )?, | ||
89 | module_with_name_to_import, | ||
90 | syntax_under_caret, | ||
91 | }) | ||
92 | } | ||
93 | |||
94 | fn for_regular_path(path_under_caret: ast::Path, ctx: &AssistCtx) -> Option<Self> { | ||
95 | let syntax_under_caret = path_under_caret.syntax().to_owned(); | ||
96 | if syntax_under_caret.ancestors().find_map(ast::UseItem::cast).is_some() { | ||
97 | return None; | ||
98 | } | ||
99 | |||
100 | let source_analyzer = ctx.source_analyzer(&syntax_under_caret, None); | ||
101 | let module_with_name_to_import = source_analyzer.module()?; | ||
102 | Some(Self { | ||
103 | import_candidate: ImportCandidate::for_regular_path( | ||
104 | &path_under_caret, | ||
105 | &source_analyzer, | ||
106 | ctx.db, | ||
107 | )?, | ||
108 | module_with_name_to_import, | ||
109 | syntax_under_caret, | ||
110 | }) | ||
111 | } | ||
112 | |||
113 | fn get_search_query(&self) -> &str { | ||
114 | match &self.import_candidate { | ||
115 | ImportCandidate::UnqualifiedName(name) => name, | ||
116 | ImportCandidate::QualifierStart(qualifier_start) => qualifier_start, | ||
117 | ImportCandidate::TraitAssocItem(_, trait_assoc_item_name) => trait_assoc_item_name, | ||
118 | ImportCandidate::TraitMethod(_, trait_method_name) => trait_method_name, | ||
119 | } | ||
120 | } | ||
121 | |||
122 | fn get_import_group_message(&self) -> String { | ||
123 | match &self.import_candidate { | ||
124 | ImportCandidate::UnqualifiedName(name) => format!("Import {}", name), | ||
125 | ImportCandidate::QualifierStart(qualifier_start) => { | ||
126 | format!("Import {}", qualifier_start) | ||
127 | } | ||
128 | ImportCandidate::TraitAssocItem(_, trait_assoc_item_name) => { | ||
129 | format!("Import a trait for item {}", trait_assoc_item_name) | ||
130 | } | ||
131 | ImportCandidate::TraitMethod(_, trait_method_name) => { | ||
132 | format!("Import a trait for method {}", trait_method_name) | ||
133 | } | ||
134 | } | ||
135 | } | ||
136 | |||
137 | fn search_for_imports(&self, db: &RootDatabase) -> BTreeSet<ModPath> { | ||
138 | let _p = profile("auto_import::search_for_imports"); | ||
139 | let current_crate = self.module_with_name_to_import.krate(); | ||
140 | ImportsLocator::new(db) | ||
141 | .find_imports(&self.get_search_query()) | ||
142 | .into_iter() | ||
143 | .filter_map(|module_def| match &self.import_candidate { | ||
144 | ImportCandidate::TraitAssocItem(assoc_item_type, _) => { | ||
145 | let located_assoc_item = match module_def { | ||
146 | ModuleDef::Function(located_function) => located_function | ||
147 | .as_assoc_item(db) | ||
148 | .map(|assoc| assoc.container(db)) | ||
149 | .and_then(Self::assoc_to_trait), | ||
150 | ModuleDef::Const(located_const) => located_const | ||
151 | .as_assoc_item(db) | ||
152 | .map(|assoc| assoc.container(db)) | ||
153 | .and_then(Self::assoc_to_trait), | ||
154 | _ => None, | ||
155 | }?; | ||
156 | |||
157 | let mut trait_candidates = FxHashSet::default(); | ||
158 | trait_candidates.insert(located_assoc_item.into()); | ||
159 | |||
160 | assoc_item_type | ||
161 | .iterate_path_candidates( | ||
162 | db, | ||
163 | current_crate, | ||
164 | &trait_candidates, | ||
165 | None, | ||
166 | |_, assoc| Self::assoc_to_trait(assoc.container(db)), | ||
167 | ) | ||
168 | .map(ModuleDef::from) | ||
169 | } | ||
170 | ImportCandidate::TraitMethod(function_callee, _) => { | ||
171 | let located_assoc_item = | ||
172 | if let ModuleDef::Function(located_function) = module_def { | ||
173 | located_function | ||
174 | .as_assoc_item(db) | ||
175 | .map(|assoc| assoc.container(db)) | ||
176 | .and_then(Self::assoc_to_trait) | ||
177 | } else { | ||
178 | None | ||
179 | }?; | ||
180 | |||
181 | let mut trait_candidates = FxHashSet::default(); | ||
182 | trait_candidates.insert(located_assoc_item.into()); | ||
183 | |||
184 | function_callee | ||
185 | .iterate_method_candidates( | ||
186 | db, | ||
187 | current_crate, | ||
188 | &trait_candidates, | ||
189 | None, | ||
190 | |_, function| { | ||
191 | Self::assoc_to_trait(function.as_assoc_item(db)?.container(db)) | ||
192 | }, | ||
193 | ) | ||
194 | .map(ModuleDef::from) | ||
195 | } | ||
196 | _ => Some(module_def), | ||
197 | }) | ||
198 | .filter_map(|module_def| self.module_with_name_to_import.find_use_path(db, module_def)) | ||
199 | .filter(|use_path| !use_path.segments.is_empty()) | ||
200 | .take(20) | ||
201 | .collect::<BTreeSet<_>>() | ||
202 | } | ||
203 | |||
204 | fn assoc_to_trait(assoc: AssocItemContainer) -> Option<Trait> { | ||
205 | if let AssocItemContainer::Trait(extracted_trait) = assoc { | ||
206 | Some(extracted_trait) | ||
207 | } else { | ||
208 | None | ||
209 | } | ||
210 | } | ||
211 | } | ||
212 | |||
213 | #[derive(Debug)] | ||
214 | enum ImportCandidate { | ||
215 | /// Simple name like 'HashMap' | ||
216 | UnqualifiedName(String), | ||
217 | /// First part of the qualified name. | ||
218 | /// For 'std::collections::HashMap', that will be 'std'. | ||
219 | QualifierStart(String), | ||
220 | /// A trait associated function (with no self parameter) or associated constant. | ||
221 | /// For 'test_mod::TestEnum::test_function', `Type` is the `test_mod::TestEnum` expression type | ||
222 | /// and `String` is the `test_function` | ||
223 | TraitAssocItem(Type, String), | ||
224 | /// A trait method with self parameter. | ||
225 | /// For 'test_enum.test_method()', `Type` is the `test_enum` expression type | ||
226 | /// and `String` is the `test_method` | ||
227 | TraitMethod(Type, String), | ||
228 | } | ||
229 | |||
230 | impl ImportCandidate { | ||
231 | fn for_method_call( | ||
232 | method_call: &ast::MethodCallExpr, | ||
233 | source_analyzer: &SourceAnalyzer, | ||
234 | db: &impl HirDatabase, | ||
235 | ) -> Option<Self> { | ||
236 | if source_analyzer.resolve_method_call(method_call).is_some() { | ||
237 | return None; | ||
238 | } | ||
239 | Some(Self::TraitMethod( | ||
240 | source_analyzer.type_of(db, &method_call.expr()?)?, | ||
241 | method_call.name_ref()?.syntax().to_string(), | ||
242 | )) | ||
243 | } | ||
244 | |||
245 | fn for_regular_path( | ||
246 | path_under_caret: &ast::Path, | ||
247 | source_analyzer: &SourceAnalyzer, | ||
248 | db: &impl HirDatabase, | ||
249 | ) -> Option<Self> { | ||
250 | if source_analyzer.resolve_path(db, path_under_caret).is_some() { | ||
251 | return None; | ||
252 | } | ||
253 | |||
254 | let segment = path_under_caret.segment()?; | ||
255 | if let Some(qualifier) = path_under_caret.qualifier() { | ||
256 | let qualifier_start = qualifier.syntax().descendants().find_map(ast::NameRef::cast)?; | ||
257 | let qualifier_start_path = | ||
258 | qualifier_start.syntax().ancestors().find_map(ast::Path::cast)?; | ||
259 | if let Some(qualifier_start_resolution) = | ||
260 | source_analyzer.resolve_path(db, &qualifier_start_path) | ||
261 | { | ||
262 | let qualifier_resolution = if qualifier_start_path == qualifier { | ||
263 | qualifier_start_resolution | ||
264 | } else { | ||
265 | source_analyzer.resolve_path(db, &qualifier)? | ||
266 | }; | ||
267 | if let PathResolution::Def(ModuleDef::Adt(assoc_item_path)) = qualifier_resolution { | ||
268 | Some(ImportCandidate::TraitAssocItem( | ||
269 | assoc_item_path.ty(db), | ||
270 | segment.syntax().to_string(), | ||
271 | )) | ||
272 | } else { | ||
273 | None | ||
274 | } | ||
275 | } else { | ||
276 | Some(ImportCandidate::QualifierStart(qualifier_start.syntax().to_string())) | ||
277 | } | ||
278 | } else { | ||
279 | Some(ImportCandidate::UnqualifiedName( | ||
280 | segment.syntax().descendants().find_map(ast::NameRef::cast)?.syntax().to_string(), | ||
281 | )) | ||
282 | } | ||
283 | } | ||
284 | } | ||
285 | |||
84 | #[cfg(test)] | 286 | #[cfg(test)] |
85 | mod tests { | 287 | mod tests { |
86 | use crate::helpers::{check_assist, check_assist_not_applicable, check_assist_target}; | ||
87 | |||
88 | use super::*; | 288 | use super::*; |
289 | use crate::helpers::{check_assist, check_assist_not_applicable, check_assist_target}; | ||
89 | 290 | ||
90 | #[test] | 291 | #[test] |
91 | fn applicable_when_found_an_import() { | 292 | fn applicable_when_found_an_import() { |
@@ -290,4 +491,303 @@ mod tests { | |||
290 | ", | 491 | ", |
291 | ); | 492 | ); |
292 | } | 493 | } |
494 | |||
495 | #[test] | ||
496 | fn not_applicable_for_imported_function() { | ||
497 | check_assist_not_applicable( | ||
498 | auto_import, | ||
499 | r" | ||
500 | pub mod test_mod { | ||
501 | pub fn test_function() {} | ||
502 | } | ||
503 | |||
504 | use test_mod::test_function; | ||
505 | fn main() { | ||
506 | test_function<|> | ||
507 | } | ||
508 | ", | ||
509 | ); | ||
510 | } | ||
511 | |||
512 | #[test] | ||
513 | fn associated_struct_function() { | ||
514 | check_assist( | ||
515 | auto_import, | ||
516 | r" | ||
517 | mod test_mod { | ||
518 | pub struct TestStruct {} | ||
519 | impl TestStruct { | ||
520 | pub fn test_function() {} | ||
521 | } | ||
522 | } | ||
523 | |||
524 | fn main() { | ||
525 | TestStruct::test_function<|> | ||
526 | } | ||
527 | ", | ||
528 | r" | ||
529 | use test_mod::TestStruct; | ||
530 | |||
531 | mod test_mod { | ||
532 | pub struct TestStruct {} | ||
533 | impl TestStruct { | ||
534 | pub fn test_function() {} | ||
535 | } | ||
536 | } | ||
537 | |||
538 | fn main() { | ||
539 | TestStruct::test_function<|> | ||
540 | } | ||
541 | ", | ||
542 | ); | ||
543 | } | ||
544 | |||
545 | #[test] | ||
546 | fn associated_struct_const() { | ||
547 | check_assist( | ||
548 | auto_import, | ||
549 | r" | ||
550 | mod test_mod { | ||
551 | pub struct TestStruct {} | ||
552 | impl TestStruct { | ||
553 | const TEST_CONST: u8 = 42; | ||
554 | } | ||
555 | } | ||
556 | |||
557 | fn main() { | ||
558 | TestStruct::TEST_CONST<|> | ||
559 | } | ||
560 | ", | ||
561 | r" | ||
562 | use test_mod::TestStruct; | ||
563 | |||
564 | mod test_mod { | ||
565 | pub struct TestStruct {} | ||
566 | impl TestStruct { | ||
567 | const TEST_CONST: u8 = 42; | ||
568 | } | ||
569 | } | ||
570 | |||
571 | fn main() { | ||
572 | TestStruct::TEST_CONST<|> | ||
573 | } | ||
574 | ", | ||
575 | ); | ||
576 | } | ||
577 | |||
578 | #[test] | ||
579 | fn associated_trait_function() { | ||
580 | check_assist( | ||
581 | auto_import, | ||
582 | r" | ||
583 | mod test_mod { | ||
584 | pub trait TestTrait { | ||
585 | fn test_function(); | ||
586 | } | ||
587 | pub struct TestStruct {} | ||
588 | impl TestTrait for TestStruct { | ||
589 | fn test_function() {} | ||
590 | } | ||
591 | } | ||
592 | |||
593 | fn main() { | ||
594 | test_mod::TestStruct::test_function<|> | ||
595 | } | ||
596 | ", | ||
597 | r" | ||
598 | use test_mod::TestTrait; | ||
599 | |||
600 | mod test_mod { | ||
601 | pub trait TestTrait { | ||
602 | fn test_function(); | ||
603 | } | ||
604 | pub struct TestStruct {} | ||
605 | impl TestTrait for TestStruct { | ||
606 | fn test_function() {} | ||
607 | } | ||
608 | } | ||
609 | |||
610 | fn main() { | ||
611 | test_mod::TestStruct::test_function<|> | ||
612 | } | ||
613 | ", | ||
614 | ); | ||
615 | } | ||
616 | |||
617 | #[test] | ||
618 | fn not_applicable_for_imported_trait_for_function() { | ||
619 | check_assist_not_applicable( | ||
620 | auto_import, | ||
621 | r" | ||
622 | mod test_mod { | ||
623 | pub trait TestTrait { | ||
624 | fn test_function(); | ||
625 | } | ||
626 | pub trait TestTrait2 { | ||
627 | fn test_function(); | ||
628 | } | ||
629 | pub enum TestEnum { | ||
630 | One, | ||
631 | Two, | ||
632 | } | ||
633 | impl TestTrait2 for TestEnum { | ||
634 | fn test_function() {} | ||
635 | } | ||
636 | impl TestTrait for TestEnum { | ||
637 | fn test_function() {} | ||
638 | } | ||
639 | } | ||
640 | |||
641 | use test_mod::TestTrait2; | ||
642 | fn main() { | ||
643 | test_mod::TestEnum::test_function<|>; | ||
644 | } | ||
645 | ", | ||
646 | ) | ||
647 | } | ||
648 | |||
649 | #[test] | ||
650 | fn associated_trait_const() { | ||
651 | check_assist( | ||
652 | auto_import, | ||
653 | r" | ||
654 | mod test_mod { | ||
655 | pub trait TestTrait { | ||
656 | const TEST_CONST: u8; | ||
657 | } | ||
658 | pub struct TestStruct {} | ||
659 | impl TestTrait for TestStruct { | ||
660 | const TEST_CONST: u8 = 42; | ||
661 | } | ||
662 | } | ||
663 | |||
664 | fn main() { | ||
665 | test_mod::TestStruct::TEST_CONST<|> | ||
666 | } | ||
667 | ", | ||
668 | r" | ||
669 | use test_mod::TestTrait; | ||
670 | |||
671 | mod test_mod { | ||
672 | pub trait TestTrait { | ||
673 | const TEST_CONST: u8; | ||
674 | } | ||
675 | pub struct TestStruct {} | ||
676 | impl TestTrait for TestStruct { | ||
677 | const TEST_CONST: u8 = 42; | ||
678 | } | ||
679 | } | ||
680 | |||
681 | fn main() { | ||
682 | test_mod::TestStruct::TEST_CONST<|> | ||
683 | } | ||
684 | ", | ||
685 | ); | ||
686 | } | ||
687 | |||
688 | #[test] | ||
689 | fn not_applicable_for_imported_trait_for_const() { | ||
690 | check_assist_not_applicable( | ||
691 | auto_import, | ||
692 | r" | ||
693 | mod test_mod { | ||
694 | pub trait TestTrait { | ||
695 | const TEST_CONST: u8; | ||
696 | } | ||
697 | pub trait TestTrait2 { | ||
698 | const TEST_CONST: f64; | ||
699 | } | ||
700 | pub enum TestEnum { | ||
701 | One, | ||
702 | Two, | ||
703 | } | ||
704 | impl TestTrait2 for TestEnum { | ||
705 | const TEST_CONST: f64 = 42.0; | ||
706 | } | ||
707 | impl TestTrait for TestEnum { | ||
708 | const TEST_CONST: u8 = 42; | ||
709 | } | ||
710 | } | ||
711 | |||
712 | use test_mod::TestTrait2; | ||
713 | fn main() { | ||
714 | test_mod::TestEnum::TEST_CONST<|>; | ||
715 | } | ||
716 | ", | ||
717 | ) | ||
718 | } | ||
719 | |||
720 | #[test] | ||
721 | fn trait_method() { | ||
722 | check_assist( | ||
723 | auto_import, | ||
724 | r" | ||
725 | mod test_mod { | ||
726 | pub trait TestTrait { | ||
727 | fn test_method(&self); | ||
728 | } | ||
729 | pub struct TestStruct {} | ||
730 | impl TestTrait for TestStruct { | ||
731 | fn test_method(&self) {} | ||
732 | } | ||
733 | } | ||
734 | |||
735 | fn main() { | ||
736 | let test_struct = test_mod::TestStruct {}; | ||
737 | test_struct.test_meth<|>od() | ||
738 | } | ||
739 | ", | ||
740 | r" | ||
741 | use test_mod::TestTrait; | ||
742 | |||
743 | mod test_mod { | ||
744 | pub trait TestTrait { | ||
745 | fn test_method(&self); | ||
746 | } | ||
747 | pub struct TestStruct {} | ||
748 | impl TestTrait for TestStruct { | ||
749 | fn test_method(&self) {} | ||
750 | } | ||
751 | } | ||
752 | |||
753 | fn main() { | ||
754 | let test_struct = test_mod::TestStruct {}; | ||
755 | test_struct.test_meth<|>od() | ||
756 | } | ||
757 | ", | ||
758 | ); | ||
759 | } | ||
760 | |||
761 | #[test] | ||
762 | fn not_applicable_for_imported_trait_for_method() { | ||
763 | check_assist_not_applicable( | ||
764 | auto_import, | ||
765 | r" | ||
766 | mod test_mod { | ||
767 | pub trait TestTrait { | ||
768 | fn test_method(&self); | ||
769 | } | ||
770 | pub trait TestTrait2 { | ||
771 | fn test_method(&self); | ||
772 | } | ||
773 | pub enum TestEnum { | ||
774 | One, | ||
775 | Two, | ||
776 | } | ||
777 | impl TestTrait2 for TestEnum { | ||
778 | fn test_method(&self) {} | ||
779 | } | ||
780 | impl TestTrait for TestEnum { | ||
781 | fn test_method(&self) {} | ||
782 | } | ||
783 | } | ||
784 | |||
785 | use test_mod::TestTrait2; | ||
786 | fn main() { | ||
787 | let one = test_mod::TestEnum::One; | ||
788 | one.test<|>_method(); | ||
789 | } | ||
790 | ", | ||
791 | ) | ||
792 | } | ||
293 | } | 793 | } |
diff --git a/crates/ra_assists/src/handlers/fill_match_arms.rs b/crates/ra_assists/src/handlers/fill_match_arms.rs index 0908fc246..ae2437ed3 100644 --- a/crates/ra_assists/src/handlers/fill_match_arms.rs +++ b/crates/ra_assists/src/handlers/fill_match_arms.rs | |||
@@ -75,10 +75,10 @@ pub(crate) fn fill_match_arms(ctx: AssistCtx) -> Option<Assist> { | |||
75 | } | 75 | } |
76 | 76 | ||
77 | fn is_trivial(arm: &ast::MatchArm) -> bool { | 77 | fn is_trivial(arm: &ast::MatchArm) -> bool { |
78 | arm.pats().any(|pat| match pat { | 78 | match arm.pat() { |
79 | ast::Pat::PlaceholderPat(..) => true, | 79 | Some(ast::Pat::PlaceholderPat(..)) => true, |
80 | _ => false, | 80 | _ => false, |
81 | }) | 81 | } |
82 | } | 82 | } |
83 | 83 | ||
84 | fn resolve_enum_def( | 84 | fn resolve_enum_def( |
diff --git a/crates/ra_assists/src/handlers/merge_match_arms.rs b/crates/ra_assists/src/handlers/merge_match_arms.rs index 670614dd8..b2a194cb5 100644 --- a/crates/ra_assists/src/handlers/merge_match_arms.rs +++ b/crates/ra_assists/src/handlers/merge_match_arms.rs | |||
@@ -75,7 +75,7 @@ pub(crate) fn merge_match_arms(ctx: AssistCtx) -> Option<Assist> { | |||
75 | } else { | 75 | } else { |
76 | arms_to_merge | 76 | arms_to_merge |
77 | .iter() | 77 | .iter() |
78 | .flat_map(ast::MatchArm::pats) | 78 | .filter_map(ast::MatchArm::pat) |
79 | .map(|x| x.syntax().to_string()) | 79 | .map(|x| x.syntax().to_string()) |
80 | .collect::<Vec<String>>() | 80 | .collect::<Vec<String>>() |
81 | .join(" | ") | 81 | .join(" | ") |
@@ -96,10 +96,10 @@ pub(crate) fn merge_match_arms(ctx: AssistCtx) -> Option<Assist> { | |||
96 | } | 96 | } |
97 | 97 | ||
98 | fn contains_placeholder(a: &ast::MatchArm) -> bool { | 98 | fn contains_placeholder(a: &ast::MatchArm) -> bool { |
99 | a.pats().any(|x| match x { | 99 | match a.pat() { |
100 | ra_syntax::ast::Pat::PlaceholderPat(..) => true, | 100 | Some(ra_syntax::ast::Pat::PlaceholderPat(..)) => true, |
101 | _ => false, | 101 | _ => false, |
102 | }) | 102 | } |
103 | } | 103 | } |
104 | 104 | ||
105 | fn next_arm(arm: &ast::MatchArm) -> Option<ast::MatchArm> { | 105 | fn next_arm(arm: &ast::MatchArm) -> Option<ast::MatchArm> { |
diff --git a/crates/ra_assists/src/handlers/move_guard.rs b/crates/ra_assists/src/handlers/move_guard.rs index 2b91ce7c4..a61a2ba3e 100644 --- a/crates/ra_assists/src/handlers/move_guard.rs +++ b/crates/ra_assists/src/handlers/move_guard.rs | |||
@@ -90,7 +90,7 @@ pub(crate) fn move_guard_to_arm_body(ctx: AssistCtx) -> Option<Assist> { | |||
90 | // ``` | 90 | // ``` |
91 | pub(crate) fn move_arm_cond_to_match_guard(ctx: AssistCtx) -> Option<Assist> { | 91 | pub(crate) fn move_arm_cond_to_match_guard(ctx: AssistCtx) -> Option<Assist> { |
92 | let match_arm: MatchArm = ctx.find_node_at_offset::<MatchArm>()?; | 92 | let match_arm: MatchArm = ctx.find_node_at_offset::<MatchArm>()?; |
93 | let last_match_pat = match_arm.pats().last()?; | 93 | let match_pat = match_arm.pat()?; |
94 | 94 | ||
95 | let arm_body = match_arm.expr()?; | 95 | let arm_body = match_arm.expr()?; |
96 | let if_expr: IfExpr = IfExpr::cast(arm_body.syntax().clone())?; | 96 | let if_expr: IfExpr = IfExpr::cast(arm_body.syntax().clone())?; |
@@ -122,8 +122,8 @@ pub(crate) fn move_arm_cond_to_match_guard(ctx: AssistCtx) -> Option<Assist> { | |||
122 | _ => edit.replace(if_expr.syntax().text_range(), then_block.syntax().text()), | 122 | _ => edit.replace(if_expr.syntax().text_range(), then_block.syntax().text()), |
123 | } | 123 | } |
124 | 124 | ||
125 | edit.insert(last_match_pat.syntax().text_range().end(), buf); | 125 | edit.insert(match_pat.syntax().text_range().end(), buf); |
126 | edit.set_cursor(last_match_pat.syntax().text_range().end() + TextUnit::from(1)); | 126 | edit.set_cursor(match_pat.syntax().text_range().end() + TextUnit::from(1)); |
127 | }, | 127 | }, |
128 | ) | 128 | ) |
129 | } | 129 | } |
diff --git a/crates/ra_assists/src/handlers/replace_qualified_name_with_use.rs b/crates/ra_assists/src/handlers/replace_qualified_name_with_use.rs index b70c88ec2..eac452413 100644 --- a/crates/ra_assists/src/handlers/replace_qualified_name_with_use.rs +++ b/crates/ra_assists/src/handlers/replace_qualified_name_with_use.rs | |||
@@ -431,7 +431,12 @@ fn best_action_for_target( | |||
431 | .find(|n| n.text_range().start() < anchor.text_range().start()) | 431 | .find(|n| n.text_range().start() < anchor.text_range().start()) |
432 | .or_else(|| Some(anchor)); | 432 | .or_else(|| Some(anchor)); |
433 | 433 | ||
434 | ImportAction::add_new_use(anchor, false) | 434 | let add_after_anchor = anchor |
435 | .clone() | ||
436 | .and_then(ast::Attr::cast) | ||
437 | .map(|attr| attr.kind() == ast::AttrKind::Inner) | ||
438 | .unwrap_or(false); | ||
439 | ImportAction::add_new_use(anchor, add_after_anchor) | ||
435 | } | 440 | } |
436 | } | 441 | } |
437 | } | 442 | } |
@@ -962,4 +967,26 @@ mod foo { | |||
962 | ", | 967 | ", |
963 | ); | 968 | ); |
964 | } | 969 | } |
970 | |||
971 | #[test] | ||
972 | fn inserts_imports_after_inner_attributes() { | ||
973 | check_assist( | ||
974 | replace_qualified_name_with_use, | ||
975 | " | ||
976 | #![allow(dead_code)] | ||
977 | |||
978 | fn main() { | ||
979 | std::fmt::Debug<|> | ||
980 | } | ||
981 | ", | ||
982 | " | ||
983 | #![allow(dead_code)] | ||
984 | use std::fmt::Debug; | ||
985 | |||
986 | fn main() { | ||
987 | Debug<|> | ||
988 | } | ||
989 | ", | ||
990 | ); | ||
991 | } | ||
965 | } | 992 | } |