]> git.lizzy.rs Git - rust.git/blob - src/tools/rust-analyzer/crates/ide-completion/src/completions/use_.rs
Auto merge of #101168 - jachris:dataflow-const-prop, r=oli-obk
[rust.git] / src / tools / rust-analyzer / crates / ide-completion / src / completions / use_.rs
1 //! Completion for use trees
2
3 use hir::ScopeDef;
4 use ide_db::{FxHashSet, SymbolKind};
5 use syntax::{ast, AstNode};
6
7 use crate::{
8     context::{CompletionContext, PathCompletionCtx, Qualified},
9     item::Builder,
10     CompletionItem, CompletionItemKind, CompletionRelevance, Completions,
11 };
12
13 pub(crate) fn complete_use_path(
14     acc: &mut Completions,
15     ctx: &CompletionContext<'_>,
16     path_ctx @ PathCompletionCtx { qualified, use_tree_parent, .. }: &PathCompletionCtx,
17     name_ref: &Option<ast::NameRef>,
18 ) {
19     match qualified {
20         Qualified::With { path, resolution: Some(resolution), super_chain_len } => {
21             acc.add_super_keyword(ctx, *super_chain_len);
22
23             // only show `self` in a new use-tree when the qualifier doesn't end in self
24             let not_preceded_by_self = *use_tree_parent
25                 && !matches!(
26                     path.segment().and_then(|it| it.kind()),
27                     Some(ast::PathSegmentKind::SelfKw)
28                 );
29             if not_preceded_by_self {
30                 acc.add_keyword(ctx, "self");
31             }
32
33             let mut already_imported_names = FxHashSet::default();
34             if let Some(list) = ctx.token.parent_ancestors().find_map(ast::UseTreeList::cast) {
35                 let use_tree = list.parent_use_tree();
36                 if use_tree.path().as_ref() == Some(path) {
37                     for tree in list.use_trees().filter(|tree| tree.is_simple_path()) {
38                         if let Some(name) = tree.path().and_then(|path| path.as_single_name_ref()) {
39                             already_imported_names.insert(name.to_string());
40                         }
41                     }
42                 }
43             }
44
45             match resolution {
46                 hir::PathResolution::Def(hir::ModuleDef::Module(module)) => {
47                     let module_scope = module.scope(ctx.db, Some(ctx.module));
48                     let unknown_is_current = |name: &hir::Name| {
49                         matches!(
50                             name_ref,
51                             Some(name_ref) if name_ref.syntax().text() == name.to_smol_str().as_str()
52                         )
53                     };
54                     for (name, def) in module_scope {
55                         let is_name_already_imported = name
56                             .as_text()
57                             .map_or(false, |text| already_imported_names.contains(text.as_str()));
58
59                         let add_resolution = match def {
60                             ScopeDef::Unknown if unknown_is_current(&name) => {
61                                 // for `use self::foo$0`, don't suggest `foo` as a completion
62                                 cov_mark::hit!(dont_complete_current_use);
63                                 continue;
64                             }
65                             ScopeDef::ModuleDef(_) | ScopeDef::Unknown => true,
66                             _ => false,
67                         };
68
69                         if add_resolution {
70                             let mut builder = Builder::from_resolution(ctx, path_ctx, name, def);
71                             builder.set_relevance(CompletionRelevance {
72                                 is_name_already_imported,
73                                 ..Default::default()
74                             });
75                             acc.add(builder.build());
76                         }
77                     }
78                 }
79                 hir::PathResolution::Def(hir::ModuleDef::Adt(hir::Adt::Enum(e))) => {
80                     cov_mark::hit!(enum_plain_qualified_use_tree);
81                     acc.add_enum_variants(ctx, path_ctx, *e);
82                 }
83                 _ => {}
84             }
85         }
86         // fresh use tree with leading colon2, only show crate roots
87         Qualified::Absolute => {
88             cov_mark::hit!(use_tree_crate_roots_only);
89             acc.add_crate_roots(ctx, path_ctx);
90         }
91         // only show modules and non-std enum in a fresh UseTree
92         Qualified::No => {
93             cov_mark::hit!(unqualified_path_selected_only);
94             ctx.process_all_names(&mut |name, res| {
95                 match res {
96                     ScopeDef::ModuleDef(hir::ModuleDef::Module(module)) => {
97                         acc.add_module(ctx, path_ctx, module, name);
98                     }
99                     ScopeDef::ModuleDef(hir::ModuleDef::Adt(hir::Adt::Enum(e))) => {
100                         // exclude prelude enum
101                         let is_builtin =
102                             res.krate(ctx.db).map_or(false, |krate| krate.is_builtin(ctx.db));
103
104                         if !is_builtin {
105                             let item = CompletionItem::new(
106                                 CompletionItemKind::SymbolKind(SymbolKind::Enum),
107                                 ctx.source_range(),
108                                 format!("{}::", e.name(ctx.db)),
109                             );
110                             acc.add(item.build());
111                         }
112                     }
113                     _ => {}
114                 };
115             });
116             acc.add_nameref_keywords_with_colon(ctx);
117         }
118         Qualified::TypeAnchor { .. } | Qualified::With { resolution: None, .. } => {}
119     }
120 }