aboutsummaryrefslogtreecommitdiff
path: root/crates/assists/src/utils
diff options
context:
space:
mode:
authorKirill Bulatov <[email protected]>2021-01-16 17:33:36 +0000
committerKirill Bulatov <[email protected]>2021-01-16 17:33:36 +0000
commit6742f38e49d001359a7a9911becc0fcae4c67910 (patch)
tree807a038d74e345668b8f5025bc58a44b11503a33 /crates/assists/src/utils
parent3782c78d7558633be5483a04aa1c098fe76100b9 (diff)
Share import_assets and related entities
Diffstat (limited to 'crates/assists/src/utils')
-rw-r--r--crates/assists/src/utils/import_assets.rs265
1 files changed, 0 insertions, 265 deletions
diff --git a/crates/assists/src/utils/import_assets.rs b/crates/assists/src/utils/import_assets.rs
deleted file mode 100644
index 4ce82c1ba..000000000
--- a/crates/assists/src/utils/import_assets.rs
+++ /dev/null
@@ -1,265 +0,0 @@
1//! Look up accessible paths for items.
2use either::Either;
3use hir::{AsAssocItem, AssocItemContainer, ModuleDef, Semantics};
4use ide_db::{imports_locator, RootDatabase};
5use rustc_hash::FxHashSet;
6use syntax::{ast, AstNode, SyntaxNode};
7
8use crate::assist_config::InsertUseConfig;
9
10#[derive(Debug)]
11pub(crate) enum ImportCandidate {
12 /// Simple name like 'HashMap'
13 UnqualifiedName(PathImportCandidate),
14 /// First part of the qualified name.
15 /// For 'std::collections::HashMap', that will be 'std'.
16 QualifierStart(PathImportCandidate),
17 /// A trait associated function (with no self parameter) or associated constant.
18 /// For 'test_mod::TestEnum::test_function', `ty` is the `test_mod::TestEnum` expression type
19 /// and `name` is the `test_function`
20 TraitAssocItem(TraitImportCandidate),
21 /// A trait method with self parameter.
22 /// For 'test_enum.test_method()', `ty` is the `test_enum` expression type
23 /// and `name` is the `test_method`
24 TraitMethod(TraitImportCandidate),
25}
26
27#[derive(Debug)]
28pub(crate) struct TraitImportCandidate {
29 pub(crate) ty: hir::Type,
30 pub(crate) name: ast::NameRef,
31}
32
33#[derive(Debug)]
34pub(crate) struct PathImportCandidate {
35 pub(crate) name: ast::NameRef,
36}
37
38#[derive(Debug)]
39pub(crate) struct ImportAssets {
40 import_candidate: ImportCandidate,
41 module_with_name_to_import: hir::Module,
42 syntax_under_caret: SyntaxNode,
43}
44
45impl ImportAssets {
46 pub(crate) fn for_method_call(
47 method_call: ast::MethodCallExpr,
48 sema: &Semantics<RootDatabase>,
49 ) -> Option<Self> {
50 let syntax_under_caret = method_call.syntax().to_owned();
51 let module_with_name_to_import = sema.scope(&syntax_under_caret).module()?;
52 Some(Self {
53 import_candidate: ImportCandidate::for_method_call(sema, &method_call)?,
54 module_with_name_to_import,
55 syntax_under_caret,
56 })
57 }
58
59 pub(crate) fn for_regular_path(
60 path_under_caret: ast::Path,
61 sema: &Semantics<RootDatabase>,
62 ) -> Option<Self> {
63 let syntax_under_caret = path_under_caret.syntax().to_owned();
64 if syntax_under_caret.ancestors().find_map(ast::Use::cast).is_some() {
65 return None;
66 }
67
68 let module_with_name_to_import = sema.scope(&syntax_under_caret).module()?;
69 Some(Self {
70 import_candidate: ImportCandidate::for_regular_path(sema, &path_under_caret)?,
71 module_with_name_to_import,
72 syntax_under_caret,
73 })
74 }
75
76 pub(crate) fn syntax_under_caret(&self) -> &SyntaxNode {
77 &self.syntax_under_caret
78 }
79
80 pub(crate) fn import_candidate(&self) -> &ImportCandidate {
81 &self.import_candidate
82 }
83
84 fn get_search_query(&self) -> &str {
85 match &self.import_candidate {
86 ImportCandidate::UnqualifiedName(candidate)
87 | ImportCandidate::QualifierStart(candidate) => candidate.name.text(),
88 ImportCandidate::TraitAssocItem(candidate)
89 | ImportCandidate::TraitMethod(candidate) => candidate.name.text(),
90 }
91 }
92
93 pub(crate) fn search_for_imports(
94 &self,
95 sema: &Semantics<RootDatabase>,
96 config: &InsertUseConfig,
97 ) -> Vec<(hir::ModPath, hir::ItemInNs)> {
98 let _p = profile::span("import_assists::search_for_imports");
99 self.search_for(sema, Some(config.prefix_kind))
100 }
101
102 /// This may return non-absolute paths if a part of the returned path is already imported into scope.
103 #[allow(dead_code)]
104 pub(crate) fn search_for_relative_paths(
105 &self,
106 sema: &Semantics<RootDatabase>,
107 ) -> Vec<(hir::ModPath, hir::ItemInNs)> {
108 let _p = profile::span("import_assists::search_for_relative_paths");
109 self.search_for(sema, None)
110 }
111
112 fn search_for(
113 &self,
114 sema: &Semantics<RootDatabase>,
115 prefixed: Option<hir::PrefixKind>,
116 ) -> Vec<(hir::ModPath, hir::ItemInNs)> {
117 let db = sema.db;
118 let mut trait_candidates = FxHashSet::default();
119 let current_crate = self.module_with_name_to_import.krate();
120
121 let filter = |candidate: Either<hir::ModuleDef, hir::MacroDef>| {
122 trait_candidates.clear();
123 match &self.import_candidate {
124 ImportCandidate::TraitAssocItem(trait_candidate) => {
125 let located_assoc_item = match candidate {
126 Either::Left(ModuleDef::Function(located_function)) => {
127 located_function.as_assoc_item(db)
128 }
129 Either::Left(ModuleDef::Const(located_const)) => {
130 located_const.as_assoc_item(db)
131 }
132 _ => None,
133 }
134 .map(|assoc| assoc.container(db))
135 .and_then(Self::assoc_to_trait)?;
136
137 trait_candidates.insert(located_assoc_item.into());
138
139 trait_candidate
140 .ty
141 .iterate_path_candidates(
142 db,
143 current_crate,
144 &trait_candidates,
145 None,
146 |_, assoc| Self::assoc_to_trait(assoc.container(db)),
147 )
148 .map(ModuleDef::from)
149 .map(Either::Left)
150 }
151 ImportCandidate::TraitMethod(trait_candidate) => {
152 let located_assoc_item =
153 if let Either::Left(ModuleDef::Function(located_function)) = candidate {
154 located_function
155 .as_assoc_item(db)
156 .map(|assoc| assoc.container(db))
157 .and_then(Self::assoc_to_trait)
158 } else {
159 None
160 }?;
161
162 trait_candidates.insert(located_assoc_item.into());
163
164 trait_candidate
165 .ty
166 .iterate_method_candidates(
167 db,
168 current_crate,
169 &trait_candidates,
170 None,
171 |_, function| {
172 Self::assoc_to_trait(function.as_assoc_item(db)?.container(db))
173 },
174 )
175 .map(ModuleDef::from)
176 .map(Either::Left)
177 }
178 _ => Some(candidate),
179 }
180 };
181
182 let mut res = imports_locator::find_exact_imports(
183 sema,
184 current_crate,
185 self.get_search_query().to_string(),
186 )
187 .filter_map(filter)
188 .filter_map(|candidate| {
189 let item: hir::ItemInNs = candidate.either(Into::into, Into::into);
190 if let Some(prefix_kind) = prefixed {
191 self.module_with_name_to_import.find_use_path_prefixed(db, item, prefix_kind)
192 } else {
193 self.module_with_name_to_import.find_use_path(db, item)
194 }
195 .map(|path| (path, item))
196 })
197 .filter(|(use_path, _)| use_path.len() > 1)
198 .take(20)
199 .collect::<Vec<_>>();
200 res.sort_by_key(|(path, _)| path.clone());
201 res
202 }
203
204 fn assoc_to_trait(assoc: AssocItemContainer) -> Option<hir::Trait> {
205 if let AssocItemContainer::Trait(extracted_trait) = assoc {
206 Some(extracted_trait)
207 } else {
208 None
209 }
210 }
211}
212
213impl ImportCandidate {
214 fn for_method_call(
215 sema: &Semantics<RootDatabase>,
216 method_call: &ast::MethodCallExpr,
217 ) -> Option<Self> {
218 match sema.resolve_method_call(method_call) {
219 Some(_) => None,
220 None => Some(Self::TraitMethod(TraitImportCandidate {
221 ty: sema.type_of_expr(&method_call.receiver()?)?,
222 name: method_call.name_ref()?,
223 })),
224 }
225 }
226
227 fn for_regular_path(
228 sema: &Semantics<RootDatabase>,
229 path_under_caret: &ast::Path,
230 ) -> Option<Self> {
231 if sema.resolve_path(path_under_caret).is_some() {
232 return None;
233 }
234
235 let segment = path_under_caret.segment()?;
236 let candidate = if let Some(qualifier) = path_under_caret.qualifier() {
237 let qualifier_start = qualifier.syntax().descendants().find_map(ast::NameRef::cast)?;
238 let qualifier_start_path =
239 qualifier_start.syntax().ancestors().find_map(ast::Path::cast)?;
240 if let Some(qualifier_start_resolution) = sema.resolve_path(&qualifier_start_path) {
241 let qualifier_resolution = if qualifier_start_path == qualifier {
242 qualifier_start_resolution
243 } else {
244 sema.resolve_path(&qualifier)?
245 };
246 match qualifier_resolution {
247 hir::PathResolution::Def(hir::ModuleDef::Adt(assoc_item_path)) => {
248 ImportCandidate::TraitAssocItem(TraitImportCandidate {
249 ty: assoc_item_path.ty(sema.db),
250 name: segment.name_ref()?,
251 })
252 }
253 _ => return None,
254 }
255 } else {
256 ImportCandidate::QualifierStart(PathImportCandidate { name: qualifier_start })
257 }
258 } else {
259 ImportCandidate::UnqualifiedName(PathImportCandidate {
260 name: segment.syntax().descendants().find_map(ast::NameRef::cast)?,
261 })
262 };
263 Some(candidate)
264 }
265}