1 use std::{
2 collections::{HashMap, HashSet},
3 iter,
4 };
5
6 use hir::{HasSource, ModuleSource};
7 use ide_db::{
8 assists::{AssistId, AssistKind},
9 base_db::FileId,
10 defs::{Definition, NameClass, NameRefClass},
11 search::{FileReference, SearchScope},
12 };
13 use itertools::Itertools;
14 use smallvec::SmallVec;
15 use stdx::format_to;
16 use syntax::{
17 algo::find_node_at_range,
18 ast::{
19 self,
20 edit::{AstNodeEdit, IndentLevel},
21 make, HasName, HasVisibility,
22 },
23 match_ast, ted, AstNode, SourceFile,
24 SyntaxKind::{self, WHITESPACE},
25 SyntaxNode, TextRange,
26 };
27
28 use crate::{AssistContext, Assists};
29
30 use super::remove_unused_param::range_to_remove;
31
32 // Assist: extract_module
33 //
34 // Extracts a selected region as separate module. All the references, visibility and imports are
35 // resolved.
36 //
37 // ```
38 // $0fn foo(name: i32) -> i32 {
39 // name + 1
40 // }$0
41 //
42 // fn bar(name: i32) -> i32 {
43 // name + 2
44 // }
45 // ```
46 // ->
47 // ```
48 // mod modname {
49 // pub(crate) fn foo(name: i32) -> i32 {
50 // name + 1
51 // }
52 // }
53 //
54 // fn bar(name: i32) -> i32 {
55 // name + 2
56 // }
57 // ```
extract_module(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()>58 pub(crate) fn extract_module(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
59 if ctx.has_empty_selection() {
60 return None;
61 }
62
63 let node = ctx.covering_element();
64 let node = match node {
65 syntax::NodeOrToken::Node(n) => n,
66 syntax::NodeOrToken::Token(t) => t.parent()?,
67 };
68
69 //If the selection is inside impl block, we need to place new module outside impl block,
70 //as impl blocks cannot contain modules
71
72 let mut impl_parent: Option<ast::Impl> = None;
73 let mut impl_child_count: usize = 0;
74 if let Some(parent_assoc_list) = node.parent() {
75 if let Some(parent_impl) = parent_assoc_list.parent() {
76 if let Some(impl_) = ast::Impl::cast(parent_impl) {
77 impl_child_count = parent_assoc_list.children().count();
78 impl_parent = Some(impl_);
79 }
80 }
81 }
82
83 let mut curr_parent_module: Option<ast::Module> = None;
84 if let Some(mod_syn_opt) = node.ancestors().find(|it| ast::Module::can_cast(it.kind())) {
85 curr_parent_module = ast::Module::cast(mod_syn_opt);
86 }
87
88 let mut module = extract_target(&node, ctx.selection_trimmed())?;
89 if module.body_items.is_empty() {
90 return None;
91 }
92
93 let old_item_indent = module.body_items[0].indent_level();
94
95 acc.add(
96 AssistId("extract_module", AssistKind::RefactorExtract),
97 "Extract Module",
98 module.text_range,
99 |builder| {
100 //This takes place in three steps:
101 //
102 //- Firstly, we will update the references(usages) e.g. converting a
103 // function call bar() to modname::bar(), and similarly for other items
104 //
105 //- Secondly, changing the visibility of each item inside the newly selected module
106 // i.e. making a fn a() {} to pub(crate) fn a() {}
107 //
108 //- Thirdly, resolving all the imports this includes removing paths from imports
109 // outside the module, shifting/cloning them inside new module, or shifting the imports, or making
110 // new import statements
111
112 //We are getting item usages and record_fields together, record_fields
113 //for change_visibility and usages for first point mentioned above in the process
114 let (usages_to_be_processed, record_fields) = module.get_usages_and_record_fields(ctx);
115
116 let import_paths_to_be_removed = module.resolve_imports(curr_parent_module, ctx);
117 module.change_visibility(record_fields);
118
119 let mut body_items: Vec<String> = Vec::new();
120 let mut items_to_be_processed: Vec<ast::Item> = module.body_items.clone();
121
122 let new_item_indent = if impl_parent.is_some() {
123 old_item_indent + 2
124 } else {
125 items_to_be_processed = [module.use_items.clone(), items_to_be_processed].concat();
126 old_item_indent + 1
127 };
128
129 for item in items_to_be_processed {
130 let item = item.indent(IndentLevel(1));
131 let mut indented_item = String::new();
132 format_to!(indented_item, "{new_item_indent}{item}");
133 body_items.push(indented_item);
134 }
135
136 let mut body = body_items.join("\n\n");
137
138 if let Some(impl_) = &impl_parent {
139 let mut impl_body_def = String::new();
140
141 if let Some(self_ty) = impl_.self_ty() {
142 {
143 let impl_indent = old_item_indent + 1;
144 format_to!(
145 impl_body_def,
146 "{impl_indent}impl {self_ty} {{\n{body}\n{impl_indent}}}",
147 );
148 }
149 body = impl_body_def;
150
151 // Add the import for enum/struct corresponding to given impl block
152 module.make_use_stmt_of_node_with_super(self_ty.syntax());
153 for item in module.use_items {
154 let item_indent = old_item_indent + 1;
155 body = format!("{item_indent}{item}\n\n{body}");
156 }
157 }
158 }
159
160 let mut module_def = String::new();
161
162 let module_name = module.name;
163 format_to!(module_def, "mod {module_name} {{\n{body}\n{old_item_indent}}}");
164
165 let mut usages_to_be_updated_for_curr_file = vec![];
166 for usages_to_be_updated_for_file in usages_to_be_processed {
167 if usages_to_be_updated_for_file.0 == ctx.file_id() {
168 usages_to_be_updated_for_curr_file = usages_to_be_updated_for_file.1;
169 continue;
170 }
171 builder.edit_file(usages_to_be_updated_for_file.0);
172 for usage_to_be_processed in usages_to_be_updated_for_file.1 {
173 builder.replace(usage_to_be_processed.0, usage_to_be_processed.1)
174 }
175 }
176
177 builder.edit_file(ctx.file_id());
178 for usage_to_be_processed in usages_to_be_updated_for_curr_file {
179 builder.replace(usage_to_be_processed.0, usage_to_be_processed.1)
180 }
181
182 for import_path_text_range in import_paths_to_be_removed {
183 builder.delete(import_path_text_range);
184 }
185
186 if let Some(impl_) = impl_parent {
187 // Remove complete impl block if it has only one child (as such it will be empty
188 // after deleting that child)
189 let node_to_be_removed = if impl_child_count == 1 {
190 impl_.syntax()
191 } else {
192 //Remove selected node
193 &node
194 };
195
196 builder.delete(node_to_be_removed.text_range());
197 // Remove preceding indentation from node
198 if let Some(range) = indent_range_before_given_node(node_to_be_removed) {
199 builder.delete(range);
200 }
201
202 builder.insert(impl_.syntax().text_range().end(), format!("\n\n{module_def}"));
203 } else {
204 builder.replace(module.text_range, module_def)
205 }
206 },
207 )
208 }
209
210 #[derive(Debug)]
211 struct Module {
212 text_range: TextRange,
213 name: &'static str,
214 /// All items except use items.
215 body_items: Vec<ast::Item>,
216 /// Use items are kept separately as they help when the selection is inside an impl block,
217 /// we can directly take these items and keep them outside generated impl block inside
218 /// generated module.
219 use_items: Vec<ast::Item>,
220 }
221
extract_target(node: &SyntaxNode, selection_range: TextRange) -> Option<Module>222 fn extract_target(node: &SyntaxNode, selection_range: TextRange) -> Option<Module> {
223 let selected_nodes = node
224 .children()
225 .filter(|node| selection_range.contains_range(node.text_range()))
226 .chain(iter::once(node.clone()));
227 let (use_items, body_items) = selected_nodes
228 .filter_map(ast::Item::cast)
229 .partition(|item| matches!(item, ast::Item::Use(..)));
230
231 Some(Module { text_range: selection_range, name: "modname", body_items, use_items })
232 }
233
234 impl Module {
get_usages_and_record_fields( &self, ctx: &AssistContext<'_>, ) -> (HashMap<FileId, Vec<(TextRange, String)>>, Vec<SyntaxNode>)235 fn get_usages_and_record_fields(
236 &self,
237 ctx: &AssistContext<'_>,
238 ) -> (HashMap<FileId, Vec<(TextRange, String)>>, Vec<SyntaxNode>) {
239 let mut adt_fields = Vec::new();
240 let mut refs: HashMap<FileId, Vec<(TextRange, String)>> = HashMap::new();
241
242 //Here impl is not included as each item inside impl will be tied to the parent of
243 //implementing block(a struct, enum, etc), if the parent is in selected module, it will
244 //get updated by ADT section given below or if it is not, then we dont need to do any operation
245 for item in &self.body_items {
246 match_ast! {
247 match (item.syntax()) {
248 ast::Adt(it) => {
249 if let Some( nod ) = ctx.sema.to_def(&it) {
250 let node_def = Definition::Adt(nod);
251 self.expand_and_group_usages_file_wise(ctx, node_def, &mut refs);
252
253 //Enum Fields are not allowed to explicitly specify pub, it is implied
254 match it {
255 ast::Adt::Struct(x) => {
256 if let Some(field_list) = x.field_list() {
257 match field_list {
258 ast::FieldList::RecordFieldList(record_field_list) => {
259 record_field_list.fields().for_each(|record_field| {
260 adt_fields.push(record_field.syntax().clone());
261 });
262 },
263 ast::FieldList::TupleFieldList(tuple_field_list) => {
264 tuple_field_list.fields().for_each(|tuple_field| {
265 adt_fields.push(tuple_field.syntax().clone());
266 });
267 },
268 }
269 }
270 },
271 ast::Adt::Union(x) => {
272 if let Some(record_field_list) = x.record_field_list() {
273 record_field_list.fields().for_each(|record_field| {
274 adt_fields.push(record_field.syntax().clone());
275 });
276 }
277 },
278 ast::Adt::Enum(_) => {},
279 }
280 }
281 },
282 ast::TypeAlias(it) => {
283 if let Some( nod ) = ctx.sema.to_def(&it) {
284 let node_def = Definition::TypeAlias(nod);
285 self.expand_and_group_usages_file_wise(ctx, node_def, &mut refs);
286 }
287 },
288 ast::Const(it) => {
289 if let Some( nod ) = ctx.sema.to_def(&it) {
290 let node_def = Definition::Const(nod);
291 self.expand_and_group_usages_file_wise(ctx, node_def, &mut refs);
292 }
293 },
294 ast::Static(it) => {
295 if let Some( nod ) = ctx.sema.to_def(&it) {
296 let node_def = Definition::Static(nod);
297 self.expand_and_group_usages_file_wise(ctx, node_def, &mut refs);
298 }
299 },
300 ast::Fn(it) => {
301 if let Some( nod ) = ctx.sema.to_def(&it) {
302 let node_def = Definition::Function(nod);
303 self.expand_and_group_usages_file_wise(ctx, node_def, &mut refs);
304 }
305 },
306 ast::Macro(it) => {
307 if let Some(nod) = ctx.sema.to_def(&it) {
308 self.expand_and_group_usages_file_wise(ctx, Definition::Macro(nod), &mut refs);
309 }
310 },
311 _ => (),
312 }
313 }
314 }
315
316 (refs, adt_fields)
317 }
318
expand_and_group_usages_file_wise( &self, ctx: &AssistContext<'_>, node_def: Definition, refs_in_files: &mut HashMap<FileId, Vec<(TextRange, String)>>, )319 fn expand_and_group_usages_file_wise(
320 &self,
321 ctx: &AssistContext<'_>,
322 node_def: Definition,
323 refs_in_files: &mut HashMap<FileId, Vec<(TextRange, String)>>,
324 ) {
325 for (file_id, references) in node_def.usages(&ctx.sema).all() {
326 let source_file = ctx.sema.parse(file_id);
327 let usages_in_file = references
328 .into_iter()
329 .filter_map(|usage| self.get_usage_to_be_processed(&source_file, usage));
330 refs_in_files.entry(file_id).or_default().extend(usages_in_file);
331 }
332 }
333
334 fn get_usage_to_be_processed(
335 &self,
336 source_file: &SourceFile,
337 FileReference { range, name, .. }: FileReference,
338 ) -> Option<(TextRange, String)> {
339 let path: ast::Path = find_node_at_range(source_file.syntax(), range)?;
340
341 for desc in path.syntax().descendants() {
342 if desc.to_string() == name.syntax().to_string()
343 && !self.text_range.contains_range(desc.text_range())
344 {
345 if let Some(name_ref) = ast::NameRef::cast(desc) {
346 let mod_name = self.name;
347 return Some((
348 name_ref.syntax().text_range(),
349 format!("{mod_name}::{name_ref}"),
350 ));
351 }
352 }
353 }
354
355 None
356 }
357
change_visibility(&mut self, record_fields: Vec<SyntaxNode>)358 fn change_visibility(&mut self, record_fields: Vec<SyntaxNode>) {
359 let (mut replacements, record_field_parents, impls) =
360 get_replacements_for_visibility_change(&mut self.body_items, false);
361
362 let mut impl_items: Vec<ast::Item> = impls
363 .into_iter()
364 .flat_map(|impl_| impl_.syntax().descendants())
365 .filter_map(ast::Item::cast)
366 .collect();
367
368 let (mut impl_item_replacements, _, _) =
369 get_replacements_for_visibility_change(&mut impl_items, true);
370
371 replacements.append(&mut impl_item_replacements);
372
373 for (_, field_owner) in record_field_parents {
374 for desc in field_owner.descendants().filter_map(ast::RecordField::cast) {
375 let is_record_field_present =
376 record_fields.clone().into_iter().any(|x| x.to_string() == desc.to_string());
377 if is_record_field_present {
378 replacements.push((desc.visibility(), desc.syntax().clone()));
379 }
380 }
381 }
382
383 for (vis, syntax) in replacements {
384 let item = syntax.children_with_tokens().find(|node_or_token| {
385 match node_or_token.kind() {
386 // We're skipping comments, doc comments, and attribute macros that may precede the keyword
387 // that the visibility should be placed before.
388 SyntaxKind::COMMENT | SyntaxKind::ATTR | SyntaxKind::WHITESPACE => false,
389 _ => true,
390 }
391 });
392
393 add_change_vis(vis, item);
394 }
395 }
396
resolve_imports( &mut self, curr_parent_module: Option<ast::Module>, ctx: &AssistContext<'_>, ) -> Vec<TextRange>397 fn resolve_imports(
398 &mut self,
399 curr_parent_module: Option<ast::Module>,
400 ctx: &AssistContext<'_>,
401 ) -> Vec<TextRange> {
402 let mut import_paths_to_be_removed: Vec<TextRange> = vec![];
403 let mut node_set: HashSet<String> = HashSet::new();
404
405 for item in self.body_items.clone() {
406 for x in item.syntax().descendants() {
407 if let Some(name) = ast::Name::cast(x.clone()) {
408 if let Some(name_classify) = NameClass::classify(&ctx.sema, &name) {
409 //Necessary to avoid two same names going through
410 if !node_set.contains(&name.syntax().to_string()) {
411 node_set.insert(name.syntax().to_string());
412 let def_opt: Option<Definition> = match name_classify {
413 NameClass::Definition(def) => Some(def),
414 _ => None,
415 };
416
417 if let Some(def) = def_opt {
418 if let Some(import_path) = self
419 .process_names_and_namerefs_for_import_resolve(
420 def,
421 name.syntax(),
422 &curr_parent_module,
423 ctx,
424 )
425 {
426 check_intersection_and_push(
427 &mut import_paths_to_be_removed,
428 import_path,
429 );
430 }
431 }
432 }
433 }
434 }
435
436 if let Some(name_ref) = ast::NameRef::cast(x) {
437 if let Some(name_classify) = NameRefClass::classify(&ctx.sema, &name_ref) {
438 //Necessary to avoid two same names going through
439 if !node_set.contains(&name_ref.syntax().to_string()) {
440 node_set.insert(name_ref.syntax().to_string());
441 let def_opt: Option<Definition> = match name_classify {
442 NameRefClass::Definition(def) => Some(def),
443 _ => None,
444 };
445
446 if let Some(def) = def_opt {
447 if let Some(import_path) = self
448 .process_names_and_namerefs_for_import_resolve(
449 def,
450 name_ref.syntax(),
451 &curr_parent_module,
452 ctx,
453 )
454 {
455 check_intersection_and_push(
456 &mut import_paths_to_be_removed,
457 import_path,
458 );
459 }
460 }
461 }
462 }
463 }
464 }
465 }
466
467 import_paths_to_be_removed
468 }
469
process_names_and_namerefs_for_import_resolve( &mut self, def: Definition, node_syntax: &SyntaxNode, curr_parent_module: &Option<ast::Module>, ctx: &AssistContext<'_>, ) -> Option<TextRange>470 fn process_names_and_namerefs_for_import_resolve(
471 &mut self,
472 def: Definition,
473 node_syntax: &SyntaxNode,
474 curr_parent_module: &Option<ast::Module>,
475 ctx: &AssistContext<'_>,
476 ) -> Option<TextRange> {
477 //We only need to find in the current file
478 let selection_range = ctx.selection_trimmed();
479 let curr_file_id = ctx.file_id();
480 let search_scope = SearchScope::single_file(curr_file_id);
481 let usage_res = def.usages(&ctx.sema).in_scope(search_scope).all();
482 let file = ctx.sema.parse(curr_file_id);
483
484 let mut exists_inside_sel = false;
485 let mut exists_outside_sel = false;
486 for (_, refs) in usage_res.iter() {
487 let mut non_use_nodes_itr = refs.iter().filter_map(|x| {
488 if find_node_at_range::<ast::Use>(file.syntax(), x.range).is_none() {
489 let path_opt = find_node_at_range::<ast::Path>(file.syntax(), x.range);
490 return path_opt;
491 }
492
493 None
494 });
495
496 if non_use_nodes_itr
497 .clone()
498 .any(|x| !selection_range.contains_range(x.syntax().text_range()))
499 {
500 exists_outside_sel = true;
501 }
502 if non_use_nodes_itr.any(|x| selection_range.contains_range(x.syntax().text_range())) {
503 exists_inside_sel = true;
504 }
505 }
506
507 let source_exists_outside_sel_in_same_mod = does_source_exists_outside_sel_in_same_mod(
508 def,
509 ctx,
510 curr_parent_module,
511 selection_range,
512 curr_file_id,
513 );
514
515 let use_stmt_opt: Option<ast::Use> = usage_res.into_iter().find_map(|(file_id, refs)| {
516 if file_id == curr_file_id {
517 refs.into_iter()
518 .rev()
519 .find_map(|fref| find_node_at_range(file.syntax(), fref.range))
520 } else {
521 None
522 }
523 });
524
525 let mut use_tree_str_opt: Option<Vec<ast::Path>> = None;
526 //Exists inside and outside selection
527 // - Use stmt for item is present -> get the use_tree_str and reconstruct the path in new
528 // module
529 // - Use stmt for item is not present ->
530 //If it is not found, the definition is either ported inside new module or it stays
531 //outside:
532 //- Def is inside: Nothing to import
533 //- Def is outside: Import it inside with super
534
535 //Exists inside selection but not outside -> Check for the import of it in original module,
536 //get the use_tree_str, reconstruct the use stmt in new module
537
538 let mut import_path_to_be_removed: Option<TextRange> = None;
539 if exists_inside_sel && exists_outside_sel {
540 //Changes to be made only inside new module
541
542 //If use_stmt exists, find the use_tree_str, reconstruct it inside new module
543 //If not, insert a use stmt with super and the given nameref
544 if let Some((use_tree_str, _)) =
545 self.process_use_stmt_for_import_resolve(use_stmt_opt, node_syntax)
546 {
547 use_tree_str_opt = Some(use_tree_str);
548 } else if source_exists_outside_sel_in_same_mod {
549 //Considered only after use_stmt is not present
550 //source_exists_outside_sel_in_same_mod | exists_outside_sel(exists_inside_sel =
551 //true for all cases)
552 // false | false -> Do nothing
553 // false | true -> If source is in selection -> nothing to do, If source is outside
554 // mod -> ust_stmt transversal
555 // true | false -> super import insertion
556 // true | true -> super import insertion
557 self.make_use_stmt_of_node_with_super(node_syntax);
558 }
559 } else if exists_inside_sel && !exists_outside_sel {
560 //Changes to be made inside new module, and remove import from outside
561
562 if let Some((mut use_tree_str, text_range_opt)) =
563 self.process_use_stmt_for_import_resolve(use_stmt_opt, node_syntax)
564 {
565 if let Some(text_range) = text_range_opt {
566 import_path_to_be_removed = Some(text_range);
567 }
568
569 if source_exists_outside_sel_in_same_mod {
570 if let Some(first_path_in_use_tree) = use_tree_str.last() {
571 let first_path_in_use_tree_str = first_path_in_use_tree.to_string();
572 if !first_path_in_use_tree_str.contains("super")
573 && !first_path_in_use_tree_str.contains("crate")
574 {
575 let super_path = make::ext::ident_path("super");
576 use_tree_str.push(super_path);
577 }
578 }
579 }
580
581 use_tree_str_opt = Some(use_tree_str);
582 } else if source_exists_outside_sel_in_same_mod {
583 self.make_use_stmt_of_node_with_super(node_syntax);
584 }
585 }
586
587 if let Some(use_tree_str) = use_tree_str_opt {
588 let mut use_tree_str = use_tree_str;
589 use_tree_str.reverse();
590
591 if !(!exists_outside_sel && exists_inside_sel && source_exists_outside_sel_in_same_mod)
592 {
593 if let Some(first_path_in_use_tree) = use_tree_str.first() {
594 let first_path_in_use_tree_str = first_path_in_use_tree.to_string();
595 if first_path_in_use_tree_str.contains("super") {
596 let super_path = make::ext::ident_path("super");
597 use_tree_str.insert(0, super_path)
598 }
599 }
600 }
601
602 let use_ =
603 make::use_(None, make::use_tree(make::join_paths(use_tree_str), None, None, false));
604 let item = ast::Item::from(use_);
605 self.use_items.insert(0, item);
606 }
607
608 import_path_to_be_removed
609 }
610
make_use_stmt_of_node_with_super(&mut self, node_syntax: &SyntaxNode) -> ast::Item611 fn make_use_stmt_of_node_with_super(&mut self, node_syntax: &SyntaxNode) -> ast::Item {
612 let super_path = make::ext::ident_path("super");
613 let node_path = make::ext::ident_path(&node_syntax.to_string());
614 let use_ = make::use_(
615 None,
616 make::use_tree(make::join_paths(vec![super_path, node_path]), None, None, false),
617 );
618
619 let item = ast::Item::from(use_);
620 self.use_items.insert(0, item.clone());
621 item
622 }
623
process_use_stmt_for_import_resolve( &self, use_stmt_opt: Option<ast::Use>, node_syntax: &SyntaxNode, ) -> Option<(Vec<ast::Path>, Option<TextRange>)>624 fn process_use_stmt_for_import_resolve(
625 &self,
626 use_stmt_opt: Option<ast::Use>,
627 node_syntax: &SyntaxNode,
628 ) -> Option<(Vec<ast::Path>, Option<TextRange>)> {
629 if let Some(use_stmt) = use_stmt_opt {
630 for desc in use_stmt.syntax().descendants() {
631 if let Some(path_seg) = ast::PathSegment::cast(desc) {
632 if path_seg.syntax().to_string() == node_syntax.to_string() {
633 let mut use_tree_str = vec![path_seg.parent_path()];
634 get_use_tree_paths_from_path(path_seg.parent_path(), &mut use_tree_str);
635 for ancs in path_seg.syntax().ancestors() {
636 //Here we are looking for use_tree with same string value as node
637 //passed above as the range_to_remove function looks for a comma and
638 //then includes it in the text range to remove it. But the comma only
639 //appears at the use_tree level
640 if let Some(use_tree) = ast::UseTree::cast(ancs) {
641 if use_tree.syntax().to_string() == node_syntax.to_string() {
642 return Some((
643 use_tree_str,
644 Some(range_to_remove(use_tree.syntax())),
645 ));
646 }
647 }
648 }
649
650 return Some((use_tree_str, None));
651 }
652 }
653 }
654 }
655
656 None
657 }
658 }
659
check_intersection_and_push( import_paths_to_be_removed: &mut Vec<TextRange>, mut import_path: TextRange, )660 fn check_intersection_and_push(
661 import_paths_to_be_removed: &mut Vec<TextRange>,
662 mut import_path: TextRange,
663 ) {
664 // Text ranges received here for imports are extended to the
665 // next/previous comma which can cause intersections among them
666 // and later deletion of these can cause panics similar
667 // to reported in #11766. So to mitigate it, we
668 // check for intersection between all current members
669 // and combine all such ranges into one.
670 let s: SmallVec<[_; 2]> = import_paths_to_be_removed
671 .into_iter()
672 .positions(|it| it.intersect(import_path).is_some())
673 .collect();
674 for pos in s.into_iter().rev() {
675 let intersecting_path = import_paths_to_be_removed.swap_remove(pos);
676 import_path = import_path.cover(intersecting_path);
677 }
678 import_paths_to_be_removed.push(import_path);
679 }
680
does_source_exists_outside_sel_in_same_mod( def: Definition, ctx: &AssistContext<'_>, curr_parent_module: &Option<ast::Module>, selection_range: TextRange, curr_file_id: FileId, ) -> bool681 fn does_source_exists_outside_sel_in_same_mod(
682 def: Definition,
683 ctx: &AssistContext<'_>,
684 curr_parent_module: &Option<ast::Module>,
685 selection_range: TextRange,
686 curr_file_id: FileId,
687 ) -> bool {
688 let mut source_exists_outside_sel_in_same_mod = false;
689 match def {
690 Definition::Module(x) => {
691 let source = x.definition_source(ctx.db());
692 let have_same_parent;
693 if let Some(ast_module) = &curr_parent_module {
694 if let Some(hir_module) = x.parent(ctx.db()) {
695 have_same_parent =
696 compare_hir_and_ast_module(ast_module, hir_module, ctx).is_some();
697 } else {
698 let source_file_id = source.file_id.original_file(ctx.db());
699 have_same_parent = source_file_id == curr_file_id;
700 }
701 } else {
702 let source_file_id = source.file_id.original_file(ctx.db());
703 have_same_parent = source_file_id == curr_file_id;
704 }
705
706 if have_same_parent {
707 match source.value {
708 ModuleSource::Module(module_) => {
709 source_exists_outside_sel_in_same_mod =
710 !selection_range.contains_range(module_.syntax().text_range());
711 }
712 _ => {}
713 }
714 }
715 }
716 Definition::Function(x) => {
717 if let Some(source) = x.source(ctx.db()) {
718 let have_same_parent = if let Some(ast_module) = &curr_parent_module {
719 compare_hir_and_ast_module(ast_module, x.module(ctx.db()), ctx).is_some()
720 } else {
721 let source_file_id = source.file_id.original_file(ctx.db());
722 source_file_id == curr_file_id
723 };
724
725 if have_same_parent {
726 source_exists_outside_sel_in_same_mod =
727 !selection_range.contains_range(source.value.syntax().text_range());
728 }
729 }
730 }
731 Definition::Adt(x) => {
732 if let Some(source) = x.source(ctx.db()) {
733 let have_same_parent = if let Some(ast_module) = &curr_parent_module {
734 compare_hir_and_ast_module(ast_module, x.module(ctx.db()), ctx).is_some()
735 } else {
736 let source_file_id = source.file_id.original_file(ctx.db());
737 source_file_id == curr_file_id
738 };
739
740 if have_same_parent {
741 source_exists_outside_sel_in_same_mod =
742 !selection_range.contains_range(source.value.syntax().text_range());
743 }
744 }
745 }
746 Definition::Variant(x) => {
747 if let Some(source) = x.source(ctx.db()) {
748 let have_same_parent = if let Some(ast_module) = &curr_parent_module {
749 compare_hir_and_ast_module(ast_module, x.module(ctx.db()), ctx).is_some()
750 } else {
751 let source_file_id = source.file_id.original_file(ctx.db());
752 source_file_id == curr_file_id
753 };
754
755 if have_same_parent {
756 source_exists_outside_sel_in_same_mod =
757 !selection_range.contains_range(source.value.syntax().text_range());
758 }
759 }
760 }
761 Definition::Const(x) => {
762 if let Some(source) = x.source(ctx.db()) {
763 let have_same_parent = if let Some(ast_module) = &curr_parent_module {
764 compare_hir_and_ast_module(ast_module, x.module(ctx.db()), ctx).is_some()
765 } else {
766 let source_file_id = source.file_id.original_file(ctx.db());
767 source_file_id == curr_file_id
768 };
769
770 if have_same_parent {
771 source_exists_outside_sel_in_same_mod =
772 !selection_range.contains_range(source.value.syntax().text_range());
773 }
774 }
775 }
776 Definition::Static(x) => {
777 if let Some(source) = x.source(ctx.db()) {
778 let have_same_parent = if let Some(ast_module) = &curr_parent_module {
779 compare_hir_and_ast_module(ast_module, x.module(ctx.db()), ctx).is_some()
780 } else {
781 let source_file_id = source.file_id.original_file(ctx.db());
782 source_file_id == curr_file_id
783 };
784
785 if have_same_parent {
786 source_exists_outside_sel_in_same_mod =
787 !selection_range.contains_range(source.value.syntax().text_range());
788 }
789 }
790 }
791 Definition::Trait(x) => {
792 if let Some(source) = x.source(ctx.db()) {
793 let have_same_parent = if let Some(ast_module) = &curr_parent_module {
794 compare_hir_and_ast_module(ast_module, x.module(ctx.db()), ctx).is_some()
795 } else {
796 let source_file_id = source.file_id.original_file(ctx.db());
797 source_file_id == curr_file_id
798 };
799
800 if have_same_parent {
801 source_exists_outside_sel_in_same_mod =
802 !selection_range.contains_range(source.value.syntax().text_range());
803 }
804 }
805 }
806 Definition::TypeAlias(x) => {
807 if let Some(source) = x.source(ctx.db()) {
808 let have_same_parent = if let Some(ast_module) = &curr_parent_module {
809 compare_hir_and_ast_module(ast_module, x.module(ctx.db()), ctx).is_some()
810 } else {
811 let source_file_id = source.file_id.original_file(ctx.db());
812 source_file_id == curr_file_id
813 };
814
815 if have_same_parent {
816 source_exists_outside_sel_in_same_mod =
817 !selection_range.contains_range(source.value.syntax().text_range());
818 }
819 }
820 }
821 _ => {}
822 }
823
824 source_exists_outside_sel_in_same_mod
825 }
826
get_replacements_for_visibility_change( items: &mut [ast::Item], is_clone_for_updated: bool, ) -> ( Vec<(Option<ast::Visibility>, SyntaxNode)>, Vec<(Option<ast::Visibility>, SyntaxNode)>, Vec<ast::Impl>, )827 fn get_replacements_for_visibility_change(
828 items: &mut [ast::Item],
829 is_clone_for_updated: bool,
830 ) -> (
831 Vec<(Option<ast::Visibility>, SyntaxNode)>,
832 Vec<(Option<ast::Visibility>, SyntaxNode)>,
833 Vec<ast::Impl>,
834 ) {
835 let mut replacements = Vec::new();
836 let mut record_field_parents = Vec::new();
837 let mut impls = Vec::new();
838
839 for item in items {
840 if !is_clone_for_updated {
841 *item = item.clone_for_update();
842 }
843 //Use stmts are ignored
844 match item {
845 ast::Item::Const(it) => replacements.push((it.visibility(), it.syntax().clone())),
846 ast::Item::Enum(it) => replacements.push((it.visibility(), it.syntax().clone())),
847 ast::Item::ExternCrate(it) => replacements.push((it.visibility(), it.syntax().clone())),
848 ast::Item::Fn(it) => replacements.push((it.visibility(), it.syntax().clone())),
849 //Associated item's visibility should not be changed
850 ast::Item::Impl(it) if it.for_token().is_none() => impls.push(it.clone()),
851 ast::Item::MacroDef(it) => replacements.push((it.visibility(), it.syntax().clone())),
852 ast::Item::Module(it) => replacements.push((it.visibility(), it.syntax().clone())),
853 ast::Item::Static(it) => replacements.push((it.visibility(), it.syntax().clone())),
854 ast::Item::Struct(it) => {
855 replacements.push((it.visibility(), it.syntax().clone()));
856 record_field_parents.push((it.visibility(), it.syntax().clone()));
857 }
858 ast::Item::Trait(it) => replacements.push((it.visibility(), it.syntax().clone())),
859 ast::Item::TypeAlias(it) => replacements.push((it.visibility(), it.syntax().clone())),
860 ast::Item::Union(it) => {
861 replacements.push((it.visibility(), it.syntax().clone()));
862 record_field_parents.push((it.visibility(), it.syntax().clone()));
863 }
864 _ => (),
865 }
866 }
867
868 (replacements, record_field_parents, impls)
869 }
870
get_use_tree_paths_from_path( path: ast::Path, use_tree_str: &mut Vec<ast::Path>, ) -> Option<&mut Vec<ast::Path>>871 fn get_use_tree_paths_from_path(
872 path: ast::Path,
873 use_tree_str: &mut Vec<ast::Path>,
874 ) -> Option<&mut Vec<ast::Path>> {
875 path.syntax().ancestors().filter(|x| x.to_string() != path.to_string()).find_map(|x| {
876 if let Some(use_tree) = ast::UseTree::cast(x) {
877 if let Some(upper_tree_path) = use_tree.path() {
878 if upper_tree_path.to_string() != path.to_string() {
879 use_tree_str.push(upper_tree_path.clone());
880 get_use_tree_paths_from_path(upper_tree_path, use_tree_str);
881 return Some(use_tree);
882 }
883 }
884 }
885 None
886 })?;
887
888 Some(use_tree_str)
889 }
890
add_change_vis(vis: Option<ast::Visibility>, node_or_token_opt: Option<syntax::SyntaxElement>)891 fn add_change_vis(vis: Option<ast::Visibility>, node_or_token_opt: Option<syntax::SyntaxElement>) {
892 if vis.is_none() {
893 if let Some(node_or_token) = node_or_token_opt {
894 let pub_crate_vis = make::visibility_pub_crate().clone_for_update();
895 ted::insert(ted::Position::before(node_or_token), pub_crate_vis.syntax());
896 }
897 }
898 }
899
compare_hir_and_ast_module( ast_module: &ast::Module, hir_module: hir::Module, ctx: &AssistContext<'_>, ) -> Option<()>900 fn compare_hir_and_ast_module(
901 ast_module: &ast::Module,
902 hir_module: hir::Module,
903 ctx: &AssistContext<'_>,
904 ) -> Option<()> {
905 let hir_mod_name = hir_module.name(ctx.db())?;
906 let ast_mod_name = ast_module.name()?;
907 if hir_mod_name.display(ctx.db()).to_string() != ast_mod_name.to_string() {
908 return None;
909 }
910
911 Some(())
912 }
913
indent_range_before_given_node(node: &SyntaxNode) -> Option<TextRange>914 fn indent_range_before_given_node(node: &SyntaxNode) -> Option<TextRange> {
915 node.siblings_with_tokens(syntax::Direction::Prev)
916 .find(|x| x.kind() == WHITESPACE)
917 .map(|x| x.text_range())
918 }
919
920 #[cfg(test)]
921 mod tests {
922 use crate::tests::{check_assist, check_assist_not_applicable};
923
924 use super::*;
925
926 #[test]
test_not_applicable_without_selection()927 fn test_not_applicable_without_selection() {
928 check_assist_not_applicable(
929 extract_module,
930 r"
931 $0pub struct PublicStruct {
932 field: i32,
933 }
934 ",
935 )
936 }
937
938 #[test]
test_extract_module()939 fn test_extract_module() {
940 check_assist(
941 extract_module,
942 r"
943 mod thirdpartycrate {
944 pub mod nest {
945 pub struct SomeType;
946 pub struct SomeType2;
947 }
948 pub struct SomeType1;
949 }
950
951 mod bar {
952 use crate::thirdpartycrate::{nest::{SomeType, SomeType2}, SomeType1};
953
954 pub struct PublicStruct {
955 field: PrivateStruct,
956 field1: SomeType1,
957 }
958
959 impl PublicStruct {
960 pub fn new() -> Self {
961 Self { field: PrivateStruct::new(), field1: SomeType1 }
962 }
963 }
964
965 fn foo() {
966 let _s = PrivateStruct::new();
967 let _a = bar();
968 }
969
970 $0struct PrivateStruct {
971 inner: SomeType,
972 }
973
974 pub struct PrivateStruct1 {
975 pub inner: i32,
976 }
977
978 impl PrivateStruct {
979 fn new() -> Self {
980 PrivateStruct { inner: SomeType }
981 }
982 }
983
984 fn bar() -> i32 {
985 2
986 }$0
987 }
988 ",
989 r"
990 mod thirdpartycrate {
991 pub mod nest {
992 pub struct SomeType;
993 pub struct SomeType2;
994 }
995 pub struct SomeType1;
996 }
997
998 mod bar {
999 use crate::thirdpartycrate::{nest::{SomeType2}, SomeType1};
1000
1001 pub struct PublicStruct {
1002 field: modname::PrivateStruct,
1003 field1: SomeType1,
1004 }
1005
1006 impl PublicStruct {
1007 pub fn new() -> Self {
1008 Self { field: modname::PrivateStruct::new(), field1: SomeType1 }
1009 }
1010 }
1011
1012 fn foo() {
1013 let _s = modname::PrivateStruct::new();
1014 let _a = modname::bar();
1015 }
1016
1017 mod modname {
1018 use crate::thirdpartycrate::nest::SomeType;
1019
1020 pub(crate) struct PrivateStruct {
1021 pub(crate) inner: SomeType,
1022 }
1023
1024 pub struct PrivateStruct1 {
1025 pub inner: i32,
1026 }
1027
1028 impl PrivateStruct {
1029 pub(crate) fn new() -> Self {
1030 PrivateStruct { inner: SomeType }
1031 }
1032 }
1033
1034 pub(crate) fn bar() -> i32 {
1035 2
1036 }
1037 }
1038 }
1039 ",
1040 );
1041 }
1042
1043 #[test]
test_extract_module_for_function_only()1044 fn test_extract_module_for_function_only() {
1045 check_assist(
1046 extract_module,
1047 r"
1048 $0fn foo(name: i32) -> i32 {
1049 name + 1
1050 }$0
1051
1052 fn bar(name: i32) -> i32 {
1053 name + 2
1054 }
1055 ",
1056 r"
1057 mod modname {
1058 pub(crate) fn foo(name: i32) -> i32 {
1059 name + 1
1060 }
1061 }
1062
1063 fn bar(name: i32) -> i32 {
1064 name + 2
1065 }
1066 ",
1067 )
1068 }
1069
1070 #[test]
test_extract_module_for_impl_having_corresponding_adt_in_selection()1071 fn test_extract_module_for_impl_having_corresponding_adt_in_selection() {
1072 check_assist(
1073 extract_module,
1074 r"
1075 mod impl_play {
1076 $0struct A {}
1077
1078 impl A {
1079 pub fn new_a() -> i32 {
1080 2
1081 }
1082 }$0
1083
1084 fn a() {
1085 let _a = A::new_a();
1086 }
1087 }
1088 ",
1089 r"
1090 mod impl_play {
1091 mod modname {
1092 pub(crate) struct A {}
1093
1094 impl A {
1095 pub fn new_a() -> i32 {
1096 2
1097 }
1098 }
1099 }
1100
1101 fn a() {
1102 let _a = modname::A::new_a();
1103 }
1104 }
1105 ",
1106 )
1107 }
1108
1109 #[test]
test_import_resolve_when_its_only_inside_selection()1110 fn test_import_resolve_when_its_only_inside_selection() {
1111 check_assist(
1112 extract_module,
1113 r"
1114 mod foo {
1115 pub struct PrivateStruct;
1116 pub struct PrivateStruct1;
1117 }
1118
1119 mod bar {
1120 use super::foo::{PrivateStruct, PrivateStruct1};
1121
1122 $0struct Strukt {
1123 field: PrivateStruct,
1124 }$0
1125
1126 struct Strukt1 {
1127 field: PrivateStruct1,
1128 }
1129 }
1130 ",
1131 r"
1132 mod foo {
1133 pub struct PrivateStruct;
1134 pub struct PrivateStruct1;
1135 }
1136
1137 mod bar {
1138 use super::foo::{PrivateStruct1};
1139
1140 mod modname {
1141 use super::super::foo::PrivateStruct;
1142
1143 pub(crate) struct Strukt {
1144 pub(crate) field: PrivateStruct,
1145 }
1146 }
1147
1148 struct Strukt1 {
1149 field: PrivateStruct1,
1150 }
1151 }
1152 ",
1153 )
1154 }
1155
1156 #[test]
test_import_resolve_when_its_inside_and_outside_selection_and_source_not_in_same_mod()1157 fn test_import_resolve_when_its_inside_and_outside_selection_and_source_not_in_same_mod() {
1158 check_assist(
1159 extract_module,
1160 r"
1161 mod foo {
1162 pub struct PrivateStruct;
1163 }
1164
1165 mod bar {
1166 use super::foo::PrivateStruct;
1167
1168 $0struct Strukt {
1169 field: PrivateStruct,
1170 }$0
1171
1172 struct Strukt1 {
1173 field: PrivateStruct,
1174 }
1175 }
1176 ",
1177 r"
1178 mod foo {
1179 pub struct PrivateStruct;
1180 }
1181
1182 mod bar {
1183 use super::foo::PrivateStruct;
1184
1185 mod modname {
1186 use super::super::foo::PrivateStruct;
1187
1188 pub(crate) struct Strukt {
1189 pub(crate) field: PrivateStruct,
1190 }
1191 }
1192
1193 struct Strukt1 {
1194 field: PrivateStruct,
1195 }
1196 }
1197 ",
1198 )
1199 }
1200
1201 #[test]
test_import_resolve_when_its_inside_and_outside_selection_and_source_is_in_same_mod()1202 fn test_import_resolve_when_its_inside_and_outside_selection_and_source_is_in_same_mod() {
1203 check_assist(
1204 extract_module,
1205 r"
1206 mod bar {
1207 pub struct PrivateStruct;
1208
1209 $0struct Strukt {
1210 field: PrivateStruct,
1211 }$0
1212
1213 struct Strukt1 {
1214 field: PrivateStruct,
1215 }
1216 }
1217 ",
1218 r"
1219 mod bar {
1220 pub struct PrivateStruct;
1221
1222 mod modname {
1223 use super::PrivateStruct;
1224
1225 pub(crate) struct Strukt {
1226 pub(crate) field: PrivateStruct,
1227 }
1228 }
1229
1230 struct Strukt1 {
1231 field: PrivateStruct,
1232 }
1233 }
1234 ",
1235 )
1236 }
1237
1238 #[test]
test_extract_module_for_corresponding_adt_of_impl_present_in_same_mod_but_not_in_selection()1239 fn test_extract_module_for_corresponding_adt_of_impl_present_in_same_mod_but_not_in_selection()
1240 {
1241 check_assist(
1242 extract_module,
1243 r"
1244 mod impl_play {
1245 struct A {}
1246
1247 $0impl A {
1248 pub fn new_a() -> i32 {
1249 2
1250 }
1251 }$0
1252
1253 fn a() {
1254 let _a = A::new_a();
1255 }
1256 }
1257 ",
1258 r"
1259 mod impl_play {
1260 struct A {}
1261
1262 mod modname {
1263 use super::A;
1264
1265 impl A {
1266 pub fn new_a() -> i32 {
1267 2
1268 }
1269 }
1270 }
1271
1272 fn a() {
1273 let _a = A::new_a();
1274 }
1275 }
1276 ",
1277 )
1278 }
1279
1280 #[test]
test_extract_module_for_impl_not_having_corresponding_adt_in_selection_and_not_in_same_mod_but_with_super( )1281 fn test_extract_module_for_impl_not_having_corresponding_adt_in_selection_and_not_in_same_mod_but_with_super(
1282 ) {
1283 check_assist(
1284 extract_module,
1285 r"
1286 mod foo {
1287 pub struct A {}
1288 }
1289 mod impl_play {
1290 use super::foo::A;
1291
1292 $0impl A {
1293 pub fn new_a() -> i32 {
1294 2
1295 }
1296 }$0
1297
1298 fn a() {
1299 let _a = A::new_a();
1300 }
1301 }
1302 ",
1303 r"
1304 mod foo {
1305 pub struct A {}
1306 }
1307 mod impl_play {
1308 use super::foo::A;
1309
1310 mod modname {
1311 use super::super::foo::A;
1312
1313 impl A {
1314 pub fn new_a() -> i32 {
1315 2
1316 }
1317 }
1318 }
1319
1320 fn a() {
1321 let _a = A::new_a();
1322 }
1323 }
1324 ",
1325 )
1326 }
1327
1328 #[test]
test_import_resolve_for_trait_bounds_on_function()1329 fn test_import_resolve_for_trait_bounds_on_function() {
1330 check_assist(
1331 extract_module,
1332 r"
1333 mod impl_play2 {
1334 trait JustATrait {}
1335
1336 $0struct A {}
1337
1338 fn foo<T: JustATrait>(arg: T) -> T {
1339 arg
1340 }
1341
1342 impl JustATrait for A {}
1343
1344 fn bar() {
1345 let a = A {};
1346 foo(a);
1347 }$0
1348 }
1349 ",
1350 r"
1351 mod impl_play2 {
1352 trait JustATrait {}
1353
1354 mod modname {
1355 use super::JustATrait;
1356
1357 pub(crate) struct A {}
1358
1359 pub(crate) fn foo<T: JustATrait>(arg: T) -> T {
1360 arg
1361 }
1362
1363 impl JustATrait for A {}
1364
1365 pub(crate) fn bar() {
1366 let a = A {};
1367 foo(a);
1368 }
1369 }
1370 }
1371 ",
1372 )
1373 }
1374
1375 #[test]
test_extract_module_for_module()1376 fn test_extract_module_for_module() {
1377 check_assist(
1378 extract_module,
1379 r"
1380 mod impl_play2 {
1381 $0mod impl_play {
1382 pub struct A {}
1383 }$0
1384 }
1385 ",
1386 r"
1387 mod impl_play2 {
1388 mod modname {
1389 pub(crate) mod impl_play {
1390 pub struct A {}
1391 }
1392 }
1393 }
1394 ",
1395 )
1396 }
1397
1398 #[test]
test_extract_module_with_multiple_files()1399 fn test_extract_module_with_multiple_files() {
1400 check_assist(
1401 extract_module,
1402 r"
1403 //- /main.rs
1404 mod foo;
1405
1406 use foo::PrivateStruct;
1407
1408 pub struct Strukt {
1409 field: PrivateStruct,
1410 }
1411
1412 fn main() {
1413 $0struct Strukt1 {
1414 field: Strukt,
1415 }$0
1416 }
1417 //- /foo.rs
1418 pub struct PrivateStruct;
1419 ",
1420 r"
1421 mod foo;
1422
1423 use foo::PrivateStruct;
1424
1425 pub struct Strukt {
1426 field: PrivateStruct,
1427 }
1428
1429 fn main() {
1430 mod modname {
1431 use super::Strukt;
1432
1433 pub(crate) struct Strukt1 {
1434 pub(crate) field: Strukt,
1435 }
1436 }
1437 }
1438 ",
1439 )
1440 }
1441
1442 #[test]
test_extract_module_macro_rules()1443 fn test_extract_module_macro_rules() {
1444 check_assist(
1445 extract_module,
1446 r"
1447 $0macro_rules! m {
1448 () => {};
1449 }$0
1450 m! {}
1451 ",
1452 r"
1453 mod modname {
1454 macro_rules! m {
1455 () => {};
1456 }
1457 }
1458 modname::m! {}
1459 ",
1460 );
1461 }
1462
1463 #[test]
test_do_not_apply_visibility_modifier_to_trait_impl_items()1464 fn test_do_not_apply_visibility_modifier_to_trait_impl_items() {
1465 check_assist(
1466 extract_module,
1467 r"
1468 trait ATrait {
1469 fn function();
1470 }
1471
1472 struct A {}
1473
1474 $0impl ATrait for A {
1475 fn function() {}
1476 }$0
1477 ",
1478 r"
1479 trait ATrait {
1480 fn function();
1481 }
1482
1483 struct A {}
1484
1485 mod modname {
1486 use super::A;
1487
1488 use super::ATrait;
1489
1490 impl ATrait for A {
1491 fn function() {}
1492 }
1493 }
1494 ",
1495 )
1496 }
1497
1498 #[test]
test_if_inside_impl_block_generate_module_outside()1499 fn test_if_inside_impl_block_generate_module_outside() {
1500 check_assist(
1501 extract_module,
1502 r"
1503 struct A {}
1504
1505 impl A {
1506 $0fn foo() {}$0
1507 fn bar() {}
1508 }
1509 ",
1510 r"
1511 struct A {}
1512
1513 impl A {
1514 fn bar() {}
1515 }
1516
1517 mod modname {
1518 use super::A;
1519
1520 impl A {
1521 pub(crate) fn foo() {}
1522 }
1523 }
1524 ",
1525 )
1526 }
1527
1528 #[test]
test_if_inside_impl_block_generate_module_outside_but_impl_block_having_one_child()1529 fn test_if_inside_impl_block_generate_module_outside_but_impl_block_having_one_child() {
1530 check_assist(
1531 extract_module,
1532 r"
1533 struct A {}
1534 struct B {}
1535
1536 impl A {
1537 $0fn foo(x: B) {}$0
1538 }
1539 ",
1540 r"
1541 struct A {}
1542 struct B {}
1543
1544 mod modname {
1545 use super::B;
1546
1547 use super::A;
1548
1549 impl A {
1550 pub(crate) fn foo(x: B) {}
1551 }
1552 }
1553 ",
1554 )
1555 }
1556
1557 #[test]
test_issue_11766()1558 fn test_issue_11766() {
1559 //https://github.com/rust-lang/rust-analyzer/issues/11766
1560 check_assist(
1561 extract_module,
1562 r"
1563 mod x {
1564 pub struct Foo;
1565 pub struct Bar;
1566 }
1567
1568 use x::{Bar, Foo};
1569
1570 $0type A = (Foo, Bar);$0
1571 ",
1572 r"
1573 mod x {
1574 pub struct Foo;
1575 pub struct Bar;
1576 }
1577
1578 use x::{};
1579
1580 mod modname {
1581 use super::x::Bar;
1582
1583 use super::x::Foo;
1584
1585 pub(crate) type A = (Foo, Bar);
1586 }
1587 ",
1588 )
1589 }
1590
1591 #[test]
test_issue_12790()1592 fn test_issue_12790() {
1593 check_assist(
1594 extract_module,
1595 r"
1596 $0/// A documented function
1597 fn documented_fn() {}
1598
1599 // A commented function with a #[] attribute macro
1600 #[cfg(test)]
1601 fn attribute_fn() {}
1602
1603 // A normally commented function
1604 fn normal_fn() {}
1605
1606 /// A documented Struct
1607 struct DocumentedStruct {
1608 // Normal field
1609 x: i32,
1610
1611 /// Documented field
1612 y: i32,
1613
1614 // Macroed field
1615 #[cfg(test)]
1616 z: i32,
1617 }
1618
1619 // A macroed Struct
1620 #[cfg(test)]
1621 struct MacroedStruct {
1622 // Normal field
1623 x: i32,
1624
1625 /// Documented field
1626 y: i32,
1627
1628 // Macroed field
1629 #[cfg(test)]
1630 z: i32,
1631 }
1632
1633 // A normal Struct
1634 struct NormalStruct {
1635 // Normal field
1636 x: i32,
1637
1638 /// Documented field
1639 y: i32,
1640
1641 // Macroed field
1642 #[cfg(test)]
1643 z: i32,
1644 }
1645
1646 /// A documented type
1647 type DocumentedType = i32;
1648
1649 // A macroed type
1650 #[cfg(test)]
1651 type MacroedType = i32;
1652
1653 /// A module to move
1654 mod module {}
1655
1656 /// An impl to move
1657 impl NormalStruct {
1658 /// A method
1659 fn new() {}
1660 }
1661
1662 /// A documented trait
1663 trait DocTrait {
1664 /// Inner function
1665 fn doc() {}
1666 }
1667
1668 /// An enum
1669 enum DocumentedEnum {
1670 /// A variant
1671 A,
1672 /// Another variant
1673 B { x: i32, y: i32 }
1674 }
1675
1676 /// Documented const
1677 const MY_CONST: i32 = 0;$0
1678 ",
1679 r"
1680 mod modname {
1681 /// A documented function
1682 pub(crate) fn documented_fn() {}
1683
1684 // A commented function with a #[] attribute macro
1685 #[cfg(test)]
1686 pub(crate) fn attribute_fn() {}
1687
1688 // A normally commented function
1689 pub(crate) fn normal_fn() {}
1690
1691 /// A documented Struct
1692 pub(crate) struct DocumentedStruct {
1693 // Normal field
1694 pub(crate) x: i32,
1695
1696 /// Documented field
1697 pub(crate) y: i32,
1698
1699 // Macroed field
1700 #[cfg(test)]
1701 pub(crate) z: i32,
1702 }
1703
1704 // A macroed Struct
1705 #[cfg(test)]
1706 pub(crate) struct MacroedStruct {
1707 // Normal field
1708 pub(crate) x: i32,
1709
1710 /// Documented field
1711 pub(crate) y: i32,
1712
1713 // Macroed field
1714 #[cfg(test)]
1715 pub(crate) z: i32,
1716 }
1717
1718 // A normal Struct
1719 pub(crate) struct NormalStruct {
1720 // Normal field
1721 pub(crate) x: i32,
1722
1723 /// Documented field
1724 pub(crate) y: i32,
1725
1726 // Macroed field
1727 #[cfg(test)]
1728 pub(crate) z: i32,
1729 }
1730
1731 /// A documented type
1732 pub(crate) type DocumentedType = i32;
1733
1734 // A macroed type
1735 #[cfg(test)]
1736 pub(crate) type MacroedType = i32;
1737
1738 /// A module to move
1739 pub(crate) mod module {}
1740
1741 /// An impl to move
1742 impl NormalStruct {
1743 /// A method
1744 pub(crate) fn new() {}
1745 }
1746
1747 /// A documented trait
1748 pub(crate) trait DocTrait {
1749 /// Inner function
1750 fn doc() {}
1751 }
1752
1753 /// An enum
1754 pub(crate) enum DocumentedEnum {
1755 /// A variant
1756 A,
1757 /// Another variant
1758 B { x: i32, y: i32 }
1759 }
1760
1761 /// Documented const
1762 pub(crate) const MY_CONST: i32 = 0;
1763 }
1764 ",
1765 )
1766 }
1767
1768 #[test]
test_merge_multiple_intersections()1769 fn test_merge_multiple_intersections() {
1770 check_assist(
1771 extract_module,
1772 r#"
1773 mod dep {
1774 pub struct A;
1775 pub struct B;
1776 pub struct C;
1777 }
1778
1779 use dep::{A, B, C};
1780
1781 $0struct S {
1782 inner: A,
1783 state: C,
1784 condvar: B,
1785 }$0
1786 "#,
1787 r#"
1788 mod dep {
1789 pub struct A;
1790 pub struct B;
1791 pub struct C;
1792 }
1793
1794 use dep::{};
1795
1796 mod modname {
1797 use super::dep::B;
1798
1799 use super::dep::C;
1800
1801 use super::dep::A;
1802
1803 pub(crate) struct S {
1804 pub(crate) inner: A,
1805 pub(crate) state: C,
1806 pub(crate) condvar: B,
1807 }
1808 }
1809 "#,
1810 );
1811 }
1812 }
1813