ide_assists/handlers/
extract_module.rs

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