diff options
Diffstat (limited to 'crates/hir/src')
-rw-r--r-- | crates/hir/src/code_model.rs | 1891 | ||||
-rw-r--r-- | crates/hir/src/db.rs | 21 | ||||
-rw-r--r-- | crates/hir/src/diagnostics.rs | 6 | ||||
-rw-r--r-- | crates/hir/src/from_id.rs | 247 | ||||
-rw-r--r-- | crates/hir/src/has_source.rs | 135 | ||||
-rw-r--r-- | crates/hir/src/lib.rs | 69 | ||||
-rw-r--r-- | crates/hir/src/link_rewrite.rs | 226 | ||||
-rw-r--r-- | crates/hir/src/semantics.rs | 819 | ||||
-rw-r--r-- | crates/hir/src/semantics/source_to_def.rs | 275 | ||||
-rw-r--r-- | crates/hir/src/source_analyzer.rs | 534 |
10 files changed, 4223 insertions, 0 deletions
diff --git a/crates/hir/src/code_model.rs b/crates/hir/src/code_model.rs new file mode 100644 index 000000000..9395efe4f --- /dev/null +++ b/crates/hir/src/code_model.rs | |||
@@ -0,0 +1,1891 @@ | |||
1 | //! FIXME: write short doc here | ||
2 | use std::{iter, sync::Arc}; | ||
3 | |||
4 | use arrayvec::ArrayVec; | ||
5 | use base_db::{CrateId, Edition, FileId}; | ||
6 | use either::Either; | ||
7 | use hir_def::{ | ||
8 | adt::ReprKind, | ||
9 | adt::StructKind, | ||
10 | adt::VariantData, | ||
11 | builtin_type::BuiltinType, | ||
12 | docs::Documentation, | ||
13 | expr::{BindingAnnotation, Pat, PatId}, | ||
14 | import_map, | ||
15 | lang_item::LangItemTarget, | ||
16 | path::ModPath, | ||
17 | per_ns::PerNs, | ||
18 | resolver::{HasResolver, Resolver}, | ||
19 | src::HasSource as _, | ||
20 | type_ref::{Mutability, TypeRef}, | ||
21 | AdtId, AssocContainerId, ConstId, DefWithBodyId, EnumId, FunctionId, GenericDefId, HasModule, | ||
22 | ImplId, LocalEnumVariantId, LocalFieldId, LocalModuleId, Lookup, ModuleId, StaticId, StructId, | ||
23 | TraitId, TypeAliasId, TypeParamId, UnionId, VariantId, | ||
24 | }; | ||
25 | use hir_expand::{ | ||
26 | diagnostics::DiagnosticSink, | ||
27 | name::{name, AsName}, | ||
28 | MacroDefId, MacroDefKind, | ||
29 | }; | ||
30 | use hir_ty::{ | ||
31 | autoderef, | ||
32 | display::{HirDisplayError, HirFormatter}, | ||
33 | method_resolution, ApplicationTy, CallableDefId, Canonical, FnSig, GenericPredicate, | ||
34 | InEnvironment, Substs, TraitEnvironment, Ty, TyDefId, TypeCtor, | ||
35 | }; | ||
36 | use rustc_hash::FxHashSet; | ||
37 | use stdx::impl_from; | ||
38 | use syntax::{ | ||
39 | ast::{self, AttrsOwner, NameOwner}, | ||
40 | AstNode, SmolStr, | ||
41 | }; | ||
42 | use tt::{Ident, Leaf, Literal, TokenTree}; | ||
43 | |||
44 | use crate::{ | ||
45 | db::{DefDatabase, HirDatabase}, | ||
46 | has_source::HasSource, | ||
47 | link_rewrite::Resolvable, | ||
48 | HirDisplay, InFile, Name, | ||
49 | }; | ||
50 | |||
51 | /// hir::Crate describes a single crate. It's the main interface with which | ||
52 | /// a crate's dependencies interact. Mostly, it should be just a proxy for the | ||
53 | /// root module. | ||
54 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | ||
55 | pub struct Crate { | ||
56 | pub(crate) id: CrateId, | ||
57 | } | ||
58 | |||
59 | #[derive(Debug)] | ||
60 | pub struct CrateDependency { | ||
61 | pub krate: Crate, | ||
62 | pub name: Name, | ||
63 | } | ||
64 | |||
65 | impl Crate { | ||
66 | pub fn dependencies(self, db: &dyn HirDatabase) -> Vec<CrateDependency> { | ||
67 | db.crate_graph()[self.id] | ||
68 | .dependencies | ||
69 | .iter() | ||
70 | .map(|dep| { | ||
71 | let krate = Crate { id: dep.crate_id }; | ||
72 | let name = dep.as_name(); | ||
73 | CrateDependency { krate, name } | ||
74 | }) | ||
75 | .collect() | ||
76 | } | ||
77 | |||
78 | // FIXME: add `transitive_reverse_dependencies`. | ||
79 | pub fn reverse_dependencies(self, db: &dyn HirDatabase) -> Vec<Crate> { | ||
80 | let crate_graph = db.crate_graph(); | ||
81 | crate_graph | ||
82 | .iter() | ||
83 | .filter(|&krate| { | ||
84 | crate_graph[krate].dependencies.iter().any(|it| it.crate_id == self.id) | ||
85 | }) | ||
86 | .map(|id| Crate { id }) | ||
87 | .collect() | ||
88 | } | ||
89 | |||
90 | pub fn root_module(self, db: &dyn HirDatabase) -> Module { | ||
91 | let module_id = db.crate_def_map(self.id).root; | ||
92 | Module::new(self, module_id) | ||
93 | } | ||
94 | |||
95 | pub fn root_file(self, db: &dyn HirDatabase) -> FileId { | ||
96 | db.crate_graph()[self.id].root_file_id | ||
97 | } | ||
98 | |||
99 | pub fn edition(self, db: &dyn HirDatabase) -> Edition { | ||
100 | db.crate_graph()[self.id].edition | ||
101 | } | ||
102 | |||
103 | pub fn display_name(self, db: &dyn HirDatabase) -> Option<String> { | ||
104 | db.crate_graph()[self.id].display_name.clone() | ||
105 | } | ||
106 | |||
107 | pub fn query_external_importables( | ||
108 | self, | ||
109 | db: &dyn DefDatabase, | ||
110 | query: &str, | ||
111 | ) -> impl Iterator<Item = Either<ModuleDef, MacroDef>> { | ||
112 | import_map::search_dependencies( | ||
113 | db, | ||
114 | self.into(), | ||
115 | import_map::Query::new(query).anchor_end().case_sensitive().limit(40), | ||
116 | ) | ||
117 | .into_iter() | ||
118 | .map(|item| match item { | ||
119 | ItemInNs::Types(mod_id) | ItemInNs::Values(mod_id) => Either::Left(mod_id.into()), | ||
120 | ItemInNs::Macros(mac_id) => Either::Right(mac_id.into()), | ||
121 | }) | ||
122 | } | ||
123 | |||
124 | pub fn all(db: &dyn HirDatabase) -> Vec<Crate> { | ||
125 | db.crate_graph().iter().map(|id| Crate { id }).collect() | ||
126 | } | ||
127 | |||
128 | /// Try to get the root URL of the documentation of a crate. | ||
129 | pub fn get_doc_url(self: &Crate, db: &dyn HirDatabase) -> Option<String> { | ||
130 | // Look for #![doc(html_root_url = "...")] | ||
131 | let attrs = db.attrs(AttrDef::from(self.root_module(db)).into()); | ||
132 | let doc_attr_q = attrs.by_key("doc"); | ||
133 | |||
134 | let doc_url = if doc_attr_q.exists() { | ||
135 | doc_attr_q.tt_values().map(|tt| { | ||
136 | let name = tt.token_trees.iter() | ||
137 | .skip_while(|tt| !matches!(tt, TokenTree::Leaf(Leaf::Ident(Ident{text: ref ident, ..})) if ident == "html_root_url")) | ||
138 | .skip(2) | ||
139 | .next(); | ||
140 | |||
141 | match name { | ||
142 | Some(TokenTree::Leaf(Leaf::Literal(Literal{ref text, ..}))) => Some(text), | ||
143 | _ => None | ||
144 | } | ||
145 | }).flat_map(|t| t).next().map(|s| s.to_string()) | ||
146 | } else { | ||
147 | None | ||
148 | }; | ||
149 | |||
150 | doc_url | ||
151 | .map(|s| s.trim_matches('"').trim_end_matches("/").to_owned() + "/") | ||
152 | .map(|s| s.to_string()) | ||
153 | } | ||
154 | } | ||
155 | |||
156 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | ||
157 | pub struct Module { | ||
158 | pub(crate) id: ModuleId, | ||
159 | } | ||
160 | |||
161 | /// The defs which can be visible in the module. | ||
162 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | ||
163 | pub enum ModuleDef { | ||
164 | Module(Module), | ||
165 | Function(Function), | ||
166 | Adt(Adt), | ||
167 | // Can't be directly declared, but can be imported. | ||
168 | EnumVariant(EnumVariant), | ||
169 | Const(Const), | ||
170 | Static(Static), | ||
171 | Trait(Trait), | ||
172 | TypeAlias(TypeAlias), | ||
173 | BuiltinType(BuiltinType), | ||
174 | } | ||
175 | impl_from!( | ||
176 | Module, | ||
177 | Function, | ||
178 | Adt(Struct, Enum, Union), | ||
179 | EnumVariant, | ||
180 | Const, | ||
181 | Static, | ||
182 | Trait, | ||
183 | TypeAlias, | ||
184 | BuiltinType | ||
185 | for ModuleDef | ||
186 | ); | ||
187 | |||
188 | impl ModuleDef { | ||
189 | pub fn module(self, db: &dyn HirDatabase) -> Option<Module> { | ||
190 | match self { | ||
191 | ModuleDef::Module(it) => it.parent(db), | ||
192 | ModuleDef::Function(it) => Some(it.module(db)), | ||
193 | ModuleDef::Adt(it) => Some(it.module(db)), | ||
194 | ModuleDef::EnumVariant(it) => Some(it.module(db)), | ||
195 | ModuleDef::Const(it) => Some(it.module(db)), | ||
196 | ModuleDef::Static(it) => Some(it.module(db)), | ||
197 | ModuleDef::Trait(it) => Some(it.module(db)), | ||
198 | ModuleDef::TypeAlias(it) => Some(it.module(db)), | ||
199 | ModuleDef::BuiltinType(_) => None, | ||
200 | } | ||
201 | } | ||
202 | |||
203 | pub fn definition_visibility(&self, db: &dyn HirDatabase) -> Option<Visibility> { | ||
204 | let module = match self { | ||
205 | ModuleDef::Module(it) => it.parent(db)?, | ||
206 | ModuleDef::Function(it) => return Some(it.visibility(db)), | ||
207 | ModuleDef::Adt(it) => it.module(db), | ||
208 | ModuleDef::EnumVariant(it) => { | ||
209 | let parent = it.parent_enum(db); | ||
210 | let module = it.module(db); | ||
211 | return module.visibility_of(db, &ModuleDef::Adt(Adt::Enum(parent))); | ||
212 | } | ||
213 | ModuleDef::Const(it) => return Some(it.visibility(db)), | ||
214 | ModuleDef::Static(it) => it.module(db), | ||
215 | ModuleDef::Trait(it) => it.module(db), | ||
216 | ModuleDef::TypeAlias(it) => return Some(it.visibility(db)), | ||
217 | ModuleDef::BuiltinType(_) => return None, | ||
218 | }; | ||
219 | |||
220 | module.visibility_of(db, self) | ||
221 | } | ||
222 | |||
223 | pub fn name(self, db: &dyn HirDatabase) -> Option<Name> { | ||
224 | match self { | ||
225 | ModuleDef::Adt(it) => Some(it.name(db)), | ||
226 | ModuleDef::Trait(it) => Some(it.name(db)), | ||
227 | ModuleDef::Function(it) => Some(it.name(db)), | ||
228 | ModuleDef::EnumVariant(it) => Some(it.name(db)), | ||
229 | ModuleDef::TypeAlias(it) => Some(it.name(db)), | ||
230 | ModuleDef::Module(it) => it.name(db), | ||
231 | ModuleDef::Const(it) => it.name(db), | ||
232 | ModuleDef::Static(it) => it.name(db), | ||
233 | |||
234 | ModuleDef::BuiltinType(it) => Some(it.as_name()), | ||
235 | } | ||
236 | } | ||
237 | |||
238 | pub fn resolver<D: DefDatabase + HirDatabase>(&self, db: &D) -> Option<Resolver> { | ||
239 | Some(match self { | ||
240 | ModuleDef::Module(m) => ModuleId::from(m.clone()).resolver(db), | ||
241 | ModuleDef::Function(f) => FunctionId::from(f.clone()).resolver(db), | ||
242 | ModuleDef::Adt(adt) => AdtId::from(adt.clone()).resolver(db), | ||
243 | ModuleDef::EnumVariant(ev) => { | ||
244 | GenericDefId::from(GenericDef::from(ev.clone())).resolver(db) | ||
245 | } | ||
246 | ModuleDef::Const(c) => GenericDefId::from(GenericDef::from(c.clone())).resolver(db), | ||
247 | ModuleDef::Static(s) => StaticId::from(s.clone()).resolver(db), | ||
248 | ModuleDef::Trait(t) => TraitId::from(t.clone()).resolver(db), | ||
249 | ModuleDef::TypeAlias(t) => ModuleId::from(t.module(db)).resolver(db), | ||
250 | // FIXME: This should be a resolver relative to `std/core` | ||
251 | ModuleDef::BuiltinType(_t) => None?, | ||
252 | }) | ||
253 | } | ||
254 | } | ||
255 | |||
256 | pub use hir_def::{ | ||
257 | attr::Attrs, item_scope::ItemInNs, item_tree::ItemTreeNode, visibility::Visibility, | ||
258 | AssocItemId, AssocItemLoc, | ||
259 | }; | ||
260 | |||
261 | impl Module { | ||
262 | pub(crate) fn new(krate: Crate, crate_module_id: LocalModuleId) -> Module { | ||
263 | Module { id: ModuleId { krate: krate.id, local_id: crate_module_id } } | ||
264 | } | ||
265 | |||
266 | /// Name of this module. | ||
267 | pub fn name(self, db: &dyn HirDatabase) -> Option<Name> { | ||
268 | let def_map = db.crate_def_map(self.id.krate); | ||
269 | let parent = def_map[self.id.local_id].parent?; | ||
270 | def_map[parent].children.iter().find_map(|(name, module_id)| { | ||
271 | if *module_id == self.id.local_id { | ||
272 | Some(name.clone()) | ||
273 | } else { | ||
274 | None | ||
275 | } | ||
276 | }) | ||
277 | } | ||
278 | |||
279 | /// Returns the crate this module is part of. | ||
280 | pub fn krate(self) -> Crate { | ||
281 | Crate { id: self.id.krate } | ||
282 | } | ||
283 | |||
284 | /// Topmost parent of this module. Every module has a `crate_root`, but some | ||
285 | /// might be missing `krate`. This can happen if a module's file is not included | ||
286 | /// in the module tree of any target in `Cargo.toml`. | ||
287 | pub fn crate_root(self, db: &dyn HirDatabase) -> Module { | ||
288 | let def_map = db.crate_def_map(self.id.krate); | ||
289 | self.with_module_id(def_map.root) | ||
290 | } | ||
291 | |||
292 | /// Iterates over all child modules. | ||
293 | pub fn children(self, db: &dyn HirDatabase) -> impl Iterator<Item = Module> { | ||
294 | let def_map = db.crate_def_map(self.id.krate); | ||
295 | let children = def_map[self.id.local_id] | ||
296 | .children | ||
297 | .iter() | ||
298 | .map(|(_, module_id)| self.with_module_id(*module_id)) | ||
299 | .collect::<Vec<_>>(); | ||
300 | children.into_iter() | ||
301 | } | ||
302 | |||
303 | /// Finds a parent module. | ||
304 | pub fn parent(self, db: &dyn HirDatabase) -> Option<Module> { | ||
305 | let def_map = db.crate_def_map(self.id.krate); | ||
306 | let parent_id = def_map[self.id.local_id].parent?; | ||
307 | Some(self.with_module_id(parent_id)) | ||
308 | } | ||
309 | |||
310 | pub fn path_to_root(self, db: &dyn HirDatabase) -> Vec<Module> { | ||
311 | let mut res = vec![self]; | ||
312 | let mut curr = self; | ||
313 | while let Some(next) = curr.parent(db) { | ||
314 | res.push(next); | ||
315 | curr = next | ||
316 | } | ||
317 | res | ||
318 | } | ||
319 | |||
320 | /// Returns a `ModuleScope`: a set of items, visible in this module. | ||
321 | pub fn scope( | ||
322 | self, | ||
323 | db: &dyn HirDatabase, | ||
324 | visible_from: Option<Module>, | ||
325 | ) -> Vec<(Name, ScopeDef)> { | ||
326 | db.crate_def_map(self.id.krate)[self.id.local_id] | ||
327 | .scope | ||
328 | .entries() | ||
329 | .filter_map(|(name, def)| { | ||
330 | if let Some(m) = visible_from { | ||
331 | let filtered = | ||
332 | def.filter_visibility(|vis| vis.is_visible_from(db.upcast(), m.id)); | ||
333 | if filtered.is_none() && !def.is_none() { | ||
334 | None | ||
335 | } else { | ||
336 | Some((name, filtered)) | ||
337 | } | ||
338 | } else { | ||
339 | Some((name, def)) | ||
340 | } | ||
341 | }) | ||
342 | .flat_map(|(name, def)| { | ||
343 | ScopeDef::all_items(def).into_iter().map(move |item| (name.clone(), item)) | ||
344 | }) | ||
345 | .collect() | ||
346 | } | ||
347 | |||
348 | pub fn visibility_of(self, db: &dyn HirDatabase, def: &ModuleDef) -> Option<Visibility> { | ||
349 | db.crate_def_map(self.id.krate)[self.id.local_id].scope.visibility_of(def.clone().into()) | ||
350 | } | ||
351 | |||
352 | pub fn diagnostics(self, db: &dyn HirDatabase, sink: &mut DiagnosticSink) { | ||
353 | let _p = profile::span("Module::diagnostics"); | ||
354 | let crate_def_map = db.crate_def_map(self.id.krate); | ||
355 | crate_def_map.add_diagnostics(db.upcast(), self.id.local_id, sink); | ||
356 | for decl in self.declarations(db) { | ||
357 | match decl { | ||
358 | crate::ModuleDef::Function(f) => f.diagnostics(db, sink), | ||
359 | crate::ModuleDef::Module(m) => { | ||
360 | // Only add diagnostics from inline modules | ||
361 | if crate_def_map[m.id.local_id].origin.is_inline() { | ||
362 | m.diagnostics(db, sink) | ||
363 | } | ||
364 | } | ||
365 | _ => (), | ||
366 | } | ||
367 | } | ||
368 | |||
369 | for impl_def in self.impl_defs(db) { | ||
370 | for item in impl_def.items(db) { | ||
371 | if let AssocItem::Function(f) = item { | ||
372 | f.diagnostics(db, sink); | ||
373 | } | ||
374 | } | ||
375 | } | ||
376 | } | ||
377 | |||
378 | pub fn declarations(self, db: &dyn HirDatabase) -> Vec<ModuleDef> { | ||
379 | let def_map = db.crate_def_map(self.id.krate); | ||
380 | def_map[self.id.local_id].scope.declarations().map(ModuleDef::from).collect() | ||
381 | } | ||
382 | |||
383 | pub fn impl_defs(self, db: &dyn HirDatabase) -> Vec<ImplDef> { | ||
384 | let def_map = db.crate_def_map(self.id.krate); | ||
385 | def_map[self.id.local_id].scope.impls().map(ImplDef::from).collect() | ||
386 | } | ||
387 | |||
388 | pub(crate) fn with_module_id(self, module_id: LocalModuleId) -> Module { | ||
389 | Module::new(self.krate(), module_id) | ||
390 | } | ||
391 | |||
392 | /// Finds a path that can be used to refer to the given item from within | ||
393 | /// this module, if possible. | ||
394 | pub fn find_use_path(self, db: &dyn DefDatabase, item: impl Into<ItemInNs>) -> Option<ModPath> { | ||
395 | hir_def::find_path::find_path(db, item.into(), self.into()) | ||
396 | } | ||
397 | } | ||
398 | |||
399 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | ||
400 | pub struct Field { | ||
401 | pub(crate) parent: VariantDef, | ||
402 | pub(crate) id: LocalFieldId, | ||
403 | } | ||
404 | |||
405 | #[derive(Debug, PartialEq, Eq)] | ||
406 | pub enum FieldSource { | ||
407 | Named(ast::RecordField), | ||
408 | Pos(ast::TupleField), | ||
409 | } | ||
410 | |||
411 | impl Field { | ||
412 | pub fn name(&self, db: &dyn HirDatabase) -> Name { | ||
413 | self.parent.variant_data(db).fields()[self.id].name.clone() | ||
414 | } | ||
415 | |||
416 | /// Returns the type as in the signature of the struct (i.e., with | ||
417 | /// placeholder types for type parameters). This is good for showing | ||
418 | /// signature help, but not so good to actually get the type of the field | ||
419 | /// when you actually have a variable of the struct. | ||
420 | pub fn signature_ty(&self, db: &dyn HirDatabase) -> Type { | ||
421 | let var_id = self.parent.into(); | ||
422 | let generic_def_id: GenericDefId = match self.parent { | ||
423 | VariantDef::Struct(it) => it.id.into(), | ||
424 | VariantDef::Union(it) => it.id.into(), | ||
425 | VariantDef::EnumVariant(it) => it.parent.id.into(), | ||
426 | }; | ||
427 | let substs = Substs::type_params(db, generic_def_id); | ||
428 | let ty = db.field_types(var_id)[self.id].clone().subst(&substs); | ||
429 | Type::new(db, self.parent.module(db).id.krate, var_id, ty) | ||
430 | } | ||
431 | |||
432 | pub fn parent_def(&self, _db: &dyn HirDatabase) -> VariantDef { | ||
433 | self.parent | ||
434 | } | ||
435 | } | ||
436 | |||
437 | impl HasVisibility for Field { | ||
438 | fn visibility(&self, db: &dyn HirDatabase) -> Visibility { | ||
439 | let variant_data = self.parent.variant_data(db); | ||
440 | let visibility = &variant_data.fields()[self.id].visibility; | ||
441 | let parent_id: hir_def::VariantId = self.parent.into(); | ||
442 | visibility.resolve(db.upcast(), &parent_id.resolver(db.upcast())) | ||
443 | } | ||
444 | } | ||
445 | |||
446 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | ||
447 | pub struct Struct { | ||
448 | pub(crate) id: StructId, | ||
449 | } | ||
450 | |||
451 | impl Struct { | ||
452 | pub fn module(self, db: &dyn HirDatabase) -> Module { | ||
453 | Module { id: self.id.lookup(db.upcast()).container.module(db.upcast()) } | ||
454 | } | ||
455 | |||
456 | pub fn krate(self, db: &dyn HirDatabase) -> Option<Crate> { | ||
457 | Some(self.module(db).krate()) | ||
458 | } | ||
459 | |||
460 | pub fn name(self, db: &dyn HirDatabase) -> Name { | ||
461 | db.struct_data(self.id).name.clone() | ||
462 | } | ||
463 | |||
464 | pub fn fields(self, db: &dyn HirDatabase) -> Vec<Field> { | ||
465 | db.struct_data(self.id) | ||
466 | .variant_data | ||
467 | .fields() | ||
468 | .iter() | ||
469 | .map(|(id, _)| Field { parent: self.into(), id }) | ||
470 | .collect() | ||
471 | } | ||
472 | |||
473 | pub fn ty(self, db: &dyn HirDatabase) -> Type { | ||
474 | Type::from_def(db, self.id.lookup(db.upcast()).container.module(db.upcast()).krate, self.id) | ||
475 | } | ||
476 | |||
477 | pub fn repr(self, db: &dyn HirDatabase) -> Option<ReprKind> { | ||
478 | db.struct_data(self.id).repr.clone() | ||
479 | } | ||
480 | |||
481 | fn variant_data(self, db: &dyn HirDatabase) -> Arc<VariantData> { | ||
482 | db.struct_data(self.id).variant_data.clone() | ||
483 | } | ||
484 | } | ||
485 | |||
486 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | ||
487 | pub struct Union { | ||
488 | pub(crate) id: UnionId, | ||
489 | } | ||
490 | |||
491 | impl Union { | ||
492 | pub fn name(self, db: &dyn HirDatabase) -> Name { | ||
493 | db.union_data(self.id).name.clone() | ||
494 | } | ||
495 | |||
496 | pub fn module(self, db: &dyn HirDatabase) -> Module { | ||
497 | Module { id: self.id.lookup(db.upcast()).container.module(db.upcast()) } | ||
498 | } | ||
499 | |||
500 | pub fn ty(self, db: &dyn HirDatabase) -> Type { | ||
501 | Type::from_def(db, self.id.lookup(db.upcast()).container.module(db.upcast()).krate, self.id) | ||
502 | } | ||
503 | |||
504 | pub fn fields(self, db: &dyn HirDatabase) -> Vec<Field> { | ||
505 | db.union_data(self.id) | ||
506 | .variant_data | ||
507 | .fields() | ||
508 | .iter() | ||
509 | .map(|(id, _)| Field { parent: self.into(), id }) | ||
510 | .collect() | ||
511 | } | ||
512 | |||
513 | fn variant_data(self, db: &dyn HirDatabase) -> Arc<VariantData> { | ||
514 | db.union_data(self.id).variant_data.clone() | ||
515 | } | ||
516 | } | ||
517 | |||
518 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | ||
519 | pub struct Enum { | ||
520 | pub(crate) id: EnumId, | ||
521 | } | ||
522 | |||
523 | impl Enum { | ||
524 | pub fn module(self, db: &dyn HirDatabase) -> Module { | ||
525 | Module { id: self.id.lookup(db.upcast()).container.module(db.upcast()) } | ||
526 | } | ||
527 | |||
528 | pub fn krate(self, db: &dyn HirDatabase) -> Option<Crate> { | ||
529 | Some(self.module(db).krate()) | ||
530 | } | ||
531 | |||
532 | pub fn name(self, db: &dyn HirDatabase) -> Name { | ||
533 | db.enum_data(self.id).name.clone() | ||
534 | } | ||
535 | |||
536 | pub fn variants(self, db: &dyn HirDatabase) -> Vec<EnumVariant> { | ||
537 | db.enum_data(self.id) | ||
538 | .variants | ||
539 | .iter() | ||
540 | .map(|(id, _)| EnumVariant { parent: self, id }) | ||
541 | .collect() | ||
542 | } | ||
543 | |||
544 | pub fn ty(self, db: &dyn HirDatabase) -> Type { | ||
545 | Type::from_def(db, self.id.lookup(db.upcast()).container.module(db.upcast()).krate, self.id) | ||
546 | } | ||
547 | } | ||
548 | |||
549 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | ||
550 | pub struct EnumVariant { | ||
551 | pub(crate) parent: Enum, | ||
552 | pub(crate) id: LocalEnumVariantId, | ||
553 | } | ||
554 | |||
555 | impl EnumVariant { | ||
556 | pub fn module(self, db: &dyn HirDatabase) -> Module { | ||
557 | self.parent.module(db) | ||
558 | } | ||
559 | pub fn parent_enum(self, _db: &dyn HirDatabase) -> Enum { | ||
560 | self.parent | ||
561 | } | ||
562 | |||
563 | pub fn name(self, db: &dyn HirDatabase) -> Name { | ||
564 | db.enum_data(self.parent.id).variants[self.id].name.clone() | ||
565 | } | ||
566 | |||
567 | pub fn fields(self, db: &dyn HirDatabase) -> Vec<Field> { | ||
568 | self.variant_data(db) | ||
569 | .fields() | ||
570 | .iter() | ||
571 | .map(|(id, _)| Field { parent: self.into(), id }) | ||
572 | .collect() | ||
573 | } | ||
574 | |||
575 | pub fn kind(self, db: &dyn HirDatabase) -> StructKind { | ||
576 | self.variant_data(db).kind() | ||
577 | } | ||
578 | |||
579 | pub(crate) fn variant_data(self, db: &dyn HirDatabase) -> Arc<VariantData> { | ||
580 | db.enum_data(self.parent.id).variants[self.id].variant_data.clone() | ||
581 | } | ||
582 | } | ||
583 | |||
584 | /// A Data Type | ||
585 | #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] | ||
586 | pub enum Adt { | ||
587 | Struct(Struct), | ||
588 | Union(Union), | ||
589 | Enum(Enum), | ||
590 | } | ||
591 | impl_from!(Struct, Union, Enum for Adt); | ||
592 | |||
593 | impl Adt { | ||
594 | pub fn has_non_default_type_params(self, db: &dyn HirDatabase) -> bool { | ||
595 | let subst = db.generic_defaults(self.into()); | ||
596 | subst.iter().any(|ty| &ty.value == &Ty::Unknown) | ||
597 | } | ||
598 | |||
599 | /// Turns this ADT into a type. Any type parameters of the ADT will be | ||
600 | /// turned into unknown types, which is good for e.g. finding the most | ||
601 | /// general set of completions, but will not look very nice when printed. | ||
602 | pub fn ty(self, db: &dyn HirDatabase) -> Type { | ||
603 | let id = AdtId::from(self); | ||
604 | Type::from_def(db, id.module(db.upcast()).krate, id) | ||
605 | } | ||
606 | |||
607 | pub fn module(self, db: &dyn HirDatabase) -> Module { | ||
608 | match self { | ||
609 | Adt::Struct(s) => s.module(db), | ||
610 | Adt::Union(s) => s.module(db), | ||
611 | Adt::Enum(e) => e.module(db), | ||
612 | } | ||
613 | } | ||
614 | |||
615 | pub fn krate(self, db: &dyn HirDatabase) -> Option<Crate> { | ||
616 | Some(self.module(db).krate()) | ||
617 | } | ||
618 | |||
619 | pub fn name(self, db: &dyn HirDatabase) -> Name { | ||
620 | match self { | ||
621 | Adt::Struct(s) => s.name(db), | ||
622 | Adt::Union(u) => u.name(db), | ||
623 | Adt::Enum(e) => e.name(db), | ||
624 | } | ||
625 | } | ||
626 | } | ||
627 | |||
628 | #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] | ||
629 | pub enum VariantDef { | ||
630 | Struct(Struct), | ||
631 | Union(Union), | ||
632 | EnumVariant(EnumVariant), | ||
633 | } | ||
634 | impl_from!(Struct, Union, EnumVariant for VariantDef); | ||
635 | |||
636 | impl VariantDef { | ||
637 | pub fn fields(self, db: &dyn HirDatabase) -> Vec<Field> { | ||
638 | match self { | ||
639 | VariantDef::Struct(it) => it.fields(db), | ||
640 | VariantDef::Union(it) => it.fields(db), | ||
641 | VariantDef::EnumVariant(it) => it.fields(db), | ||
642 | } | ||
643 | } | ||
644 | |||
645 | pub fn module(self, db: &dyn HirDatabase) -> Module { | ||
646 | match self { | ||
647 | VariantDef::Struct(it) => it.module(db), | ||
648 | VariantDef::Union(it) => it.module(db), | ||
649 | VariantDef::EnumVariant(it) => it.module(db), | ||
650 | } | ||
651 | } | ||
652 | |||
653 | pub fn name(&self, db: &dyn HirDatabase) -> Name { | ||
654 | match self { | ||
655 | VariantDef::Struct(s) => s.name(db), | ||
656 | VariantDef::Union(u) => u.name(db), | ||
657 | VariantDef::EnumVariant(e) => e.name(db), | ||
658 | } | ||
659 | } | ||
660 | |||
661 | pub(crate) fn variant_data(self, db: &dyn HirDatabase) -> Arc<VariantData> { | ||
662 | match self { | ||
663 | VariantDef::Struct(it) => it.variant_data(db), | ||
664 | VariantDef::Union(it) => it.variant_data(db), | ||
665 | VariantDef::EnumVariant(it) => it.variant_data(db), | ||
666 | } | ||
667 | } | ||
668 | } | ||
669 | |||
670 | /// The defs which have a body. | ||
671 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | ||
672 | pub enum DefWithBody { | ||
673 | Function(Function), | ||
674 | Static(Static), | ||
675 | Const(Const), | ||
676 | } | ||
677 | impl_from!(Function, Const, Static for DefWithBody); | ||
678 | |||
679 | impl DefWithBody { | ||
680 | pub fn module(self, db: &dyn HirDatabase) -> Module { | ||
681 | match self { | ||
682 | DefWithBody::Const(c) => c.module(db), | ||
683 | DefWithBody::Function(f) => f.module(db), | ||
684 | DefWithBody::Static(s) => s.module(db), | ||
685 | } | ||
686 | } | ||
687 | |||
688 | pub fn name(self, db: &dyn HirDatabase) -> Option<Name> { | ||
689 | match self { | ||
690 | DefWithBody::Function(f) => Some(f.name(db)), | ||
691 | DefWithBody::Static(s) => s.name(db), | ||
692 | DefWithBody::Const(c) => c.name(db), | ||
693 | } | ||
694 | } | ||
695 | } | ||
696 | |||
697 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | ||
698 | pub struct Function { | ||
699 | pub(crate) id: FunctionId, | ||
700 | } | ||
701 | |||
702 | impl Function { | ||
703 | pub fn module(self, db: &dyn HirDatabase) -> Module { | ||
704 | self.id.lookup(db.upcast()).module(db.upcast()).into() | ||
705 | } | ||
706 | |||
707 | pub fn krate(self, db: &dyn HirDatabase) -> Option<Crate> { | ||
708 | Some(self.module(db).krate()) | ||
709 | } | ||
710 | |||
711 | pub fn name(self, db: &dyn HirDatabase) -> Name { | ||
712 | db.function_data(self.id).name.clone() | ||
713 | } | ||
714 | |||
715 | pub fn self_param(self, db: &dyn HirDatabase) -> Option<SelfParam> { | ||
716 | if !db.function_data(self.id).has_self_param { | ||
717 | return None; | ||
718 | } | ||
719 | Some(SelfParam { func: self.id }) | ||
720 | } | ||
721 | |||
722 | pub fn params(self, db: &dyn HirDatabase) -> Vec<Param> { | ||
723 | db.function_data(self.id) | ||
724 | .params | ||
725 | .iter() | ||
726 | .skip(if self.self_param(db).is_some() { 1 } else { 0 }) | ||
727 | .map(|_| Param { _ty: () }) | ||
728 | .collect() | ||
729 | } | ||
730 | |||
731 | pub fn is_unsafe(self, db: &dyn HirDatabase) -> bool { | ||
732 | db.function_data(self.id).is_unsafe | ||
733 | } | ||
734 | |||
735 | pub fn diagnostics(self, db: &dyn HirDatabase, sink: &mut DiagnosticSink) { | ||
736 | hir_ty::diagnostics::validate_body(db, self.id.into(), sink) | ||
737 | } | ||
738 | } | ||
739 | |||
740 | // Note: logically, this belongs to `hir_ty`, but we are not using it there yet. | ||
741 | pub enum Access { | ||
742 | Shared, | ||
743 | Exclusive, | ||
744 | Owned, | ||
745 | } | ||
746 | |||
747 | impl From<Mutability> for Access { | ||
748 | fn from(mutability: Mutability) -> Access { | ||
749 | match mutability { | ||
750 | Mutability::Shared => Access::Shared, | ||
751 | Mutability::Mut => Access::Exclusive, | ||
752 | } | ||
753 | } | ||
754 | } | ||
755 | |||
756 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | ||
757 | pub struct SelfParam { | ||
758 | func: FunctionId, | ||
759 | } | ||
760 | |||
761 | pub struct Param { | ||
762 | _ty: (), | ||
763 | } | ||
764 | |||
765 | impl SelfParam { | ||
766 | pub fn access(self, db: &dyn HirDatabase) -> Access { | ||
767 | let func_data = db.function_data(self.func); | ||
768 | func_data | ||
769 | .params | ||
770 | .first() | ||
771 | .map(|param| match *param { | ||
772 | TypeRef::Reference(_, mutability) => mutability.into(), | ||
773 | _ => Access::Owned, | ||
774 | }) | ||
775 | .unwrap_or(Access::Owned) | ||
776 | } | ||
777 | } | ||
778 | |||
779 | impl HasVisibility for Function { | ||
780 | fn visibility(&self, db: &dyn HirDatabase) -> Visibility { | ||
781 | let function_data = db.function_data(self.id); | ||
782 | let visibility = &function_data.visibility; | ||
783 | visibility.resolve(db.upcast(), &self.id.resolver(db.upcast())) | ||
784 | } | ||
785 | } | ||
786 | |||
787 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | ||
788 | pub struct Const { | ||
789 | pub(crate) id: ConstId, | ||
790 | } | ||
791 | |||
792 | impl Const { | ||
793 | pub fn module(self, db: &dyn HirDatabase) -> Module { | ||
794 | Module { id: self.id.lookup(db.upcast()).module(db.upcast()) } | ||
795 | } | ||
796 | |||
797 | pub fn krate(self, db: &dyn HirDatabase) -> Option<Crate> { | ||
798 | Some(self.module(db).krate()) | ||
799 | } | ||
800 | |||
801 | pub fn name(self, db: &dyn HirDatabase) -> Option<Name> { | ||
802 | db.const_data(self.id).name.clone() | ||
803 | } | ||
804 | } | ||
805 | |||
806 | impl HasVisibility for Const { | ||
807 | fn visibility(&self, db: &dyn HirDatabase) -> Visibility { | ||
808 | let function_data = db.const_data(self.id); | ||
809 | let visibility = &function_data.visibility; | ||
810 | visibility.resolve(db.upcast(), &self.id.resolver(db.upcast())) | ||
811 | } | ||
812 | } | ||
813 | |||
814 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | ||
815 | pub struct Static { | ||
816 | pub(crate) id: StaticId, | ||
817 | } | ||
818 | |||
819 | impl Static { | ||
820 | pub fn module(self, db: &dyn HirDatabase) -> Module { | ||
821 | Module { id: self.id.lookup(db.upcast()).module(db.upcast()) } | ||
822 | } | ||
823 | |||
824 | pub fn krate(self, db: &dyn HirDatabase) -> Option<Crate> { | ||
825 | Some(self.module(db).krate()) | ||
826 | } | ||
827 | |||
828 | pub fn name(self, db: &dyn HirDatabase) -> Option<Name> { | ||
829 | db.static_data(self.id).name.clone() | ||
830 | } | ||
831 | |||
832 | pub fn is_mut(self, db: &dyn HirDatabase) -> bool { | ||
833 | db.static_data(self.id).mutable | ||
834 | } | ||
835 | } | ||
836 | |||
837 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | ||
838 | pub struct Trait { | ||
839 | pub(crate) id: TraitId, | ||
840 | } | ||
841 | |||
842 | impl Trait { | ||
843 | pub fn module(self, db: &dyn HirDatabase) -> Module { | ||
844 | Module { id: self.id.lookup(db.upcast()).container.module(db.upcast()) } | ||
845 | } | ||
846 | |||
847 | pub fn name(self, db: &dyn HirDatabase) -> Name { | ||
848 | db.trait_data(self.id).name.clone() | ||
849 | } | ||
850 | |||
851 | pub fn items(self, db: &dyn HirDatabase) -> Vec<AssocItem> { | ||
852 | db.trait_data(self.id).items.iter().map(|(_name, it)| (*it).into()).collect() | ||
853 | } | ||
854 | |||
855 | pub fn is_auto(self, db: &dyn HirDatabase) -> bool { | ||
856 | db.trait_data(self.id).auto | ||
857 | } | ||
858 | } | ||
859 | |||
860 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | ||
861 | pub struct TypeAlias { | ||
862 | pub(crate) id: TypeAliasId, | ||
863 | } | ||
864 | |||
865 | impl TypeAlias { | ||
866 | pub fn has_non_default_type_params(self, db: &dyn HirDatabase) -> bool { | ||
867 | let subst = db.generic_defaults(self.id.into()); | ||
868 | subst.iter().any(|ty| &ty.value == &Ty::Unknown) | ||
869 | } | ||
870 | |||
871 | pub fn module(self, db: &dyn HirDatabase) -> Module { | ||
872 | Module { id: self.id.lookup(db.upcast()).module(db.upcast()) } | ||
873 | } | ||
874 | |||
875 | pub fn krate(self, db: &dyn HirDatabase) -> Option<Crate> { | ||
876 | Some(self.module(db).krate()) | ||
877 | } | ||
878 | |||
879 | pub fn type_ref(self, db: &dyn HirDatabase) -> Option<TypeRef> { | ||
880 | db.type_alias_data(self.id).type_ref.clone() | ||
881 | } | ||
882 | |||
883 | pub fn ty(self, db: &dyn HirDatabase) -> Type { | ||
884 | Type::from_def(db, self.id.lookup(db.upcast()).module(db.upcast()).krate, self.id) | ||
885 | } | ||
886 | |||
887 | pub fn name(self, db: &dyn HirDatabase) -> Name { | ||
888 | db.type_alias_data(self.id).name.clone() | ||
889 | } | ||
890 | } | ||
891 | |||
892 | impl HasVisibility for TypeAlias { | ||
893 | fn visibility(&self, db: &dyn HirDatabase) -> Visibility { | ||
894 | let function_data = db.type_alias_data(self.id); | ||
895 | let visibility = &function_data.visibility; | ||
896 | visibility.resolve(db.upcast(), &self.id.resolver(db.upcast())) | ||
897 | } | ||
898 | } | ||
899 | |||
900 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | ||
901 | pub struct MacroDef { | ||
902 | pub(crate) id: MacroDefId, | ||
903 | } | ||
904 | |||
905 | impl MacroDef { | ||
906 | /// FIXME: right now, this just returns the root module of the crate that | ||
907 | /// defines this macro. The reasons for this is that macros are expanded | ||
908 | /// early, in `hir_expand`, where modules simply do not exist yet. | ||
909 | pub fn module(self, db: &dyn HirDatabase) -> Option<Module> { | ||
910 | let krate = self.id.krate?; | ||
911 | let module_id = db.crate_def_map(krate).root; | ||
912 | Some(Module::new(Crate { id: krate }, module_id)) | ||
913 | } | ||
914 | |||
915 | /// XXX: this parses the file | ||
916 | pub fn name(self, db: &dyn HirDatabase) -> Option<Name> { | ||
917 | self.source(db).value.name().map(|it| it.as_name()) | ||
918 | } | ||
919 | |||
920 | /// Indicate it is a proc-macro | ||
921 | pub fn is_proc_macro(&self) -> bool { | ||
922 | matches!(self.id.kind, MacroDefKind::CustomDerive(_)) | ||
923 | } | ||
924 | |||
925 | /// Indicate it is a derive macro | ||
926 | pub fn is_derive_macro(&self) -> bool { | ||
927 | matches!(self.id.kind, MacroDefKind::CustomDerive(_) | MacroDefKind::BuiltInDerive(_)) | ||
928 | } | ||
929 | } | ||
930 | |||
931 | /// Invariant: `inner.as_assoc_item(db).is_some()` | ||
932 | /// We do not actively enforce this invariant. | ||
933 | #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] | ||
934 | pub enum AssocItem { | ||
935 | Function(Function), | ||
936 | Const(Const), | ||
937 | TypeAlias(TypeAlias), | ||
938 | } | ||
939 | pub enum AssocItemContainer { | ||
940 | Trait(Trait), | ||
941 | ImplDef(ImplDef), | ||
942 | } | ||
943 | pub trait AsAssocItem { | ||
944 | fn as_assoc_item(self, db: &dyn HirDatabase) -> Option<AssocItem>; | ||
945 | } | ||
946 | |||
947 | impl AsAssocItem for Function { | ||
948 | fn as_assoc_item(self, db: &dyn HirDatabase) -> Option<AssocItem> { | ||
949 | as_assoc_item(db, AssocItem::Function, self.id) | ||
950 | } | ||
951 | } | ||
952 | impl AsAssocItem for Const { | ||
953 | fn as_assoc_item(self, db: &dyn HirDatabase) -> Option<AssocItem> { | ||
954 | as_assoc_item(db, AssocItem::Const, self.id) | ||
955 | } | ||
956 | } | ||
957 | impl AsAssocItem for TypeAlias { | ||
958 | fn as_assoc_item(self, db: &dyn HirDatabase) -> Option<AssocItem> { | ||
959 | as_assoc_item(db, AssocItem::TypeAlias, self.id) | ||
960 | } | ||
961 | } | ||
962 | fn as_assoc_item<ID, DEF, CTOR, AST>(db: &dyn HirDatabase, ctor: CTOR, id: ID) -> Option<AssocItem> | ||
963 | where | ||
964 | ID: Lookup<Data = AssocItemLoc<AST>>, | ||
965 | DEF: From<ID>, | ||
966 | CTOR: FnOnce(DEF) -> AssocItem, | ||
967 | AST: ItemTreeNode, | ||
968 | { | ||
969 | match id.lookup(db.upcast()).container { | ||
970 | AssocContainerId::TraitId(_) | AssocContainerId::ImplId(_) => Some(ctor(DEF::from(id))), | ||
971 | AssocContainerId::ContainerId(_) => None, | ||
972 | } | ||
973 | } | ||
974 | |||
975 | impl AssocItem { | ||
976 | pub fn name(self, db: &dyn HirDatabase) -> Option<Name> { | ||
977 | match self { | ||
978 | AssocItem::Function(it) => Some(it.name(db)), | ||
979 | AssocItem::Const(it) => it.name(db), | ||
980 | AssocItem::TypeAlias(it) => Some(it.name(db)), | ||
981 | } | ||
982 | } | ||
983 | pub fn module(self, db: &dyn HirDatabase) -> Module { | ||
984 | match self { | ||
985 | AssocItem::Function(f) => f.module(db), | ||
986 | AssocItem::Const(c) => c.module(db), | ||
987 | AssocItem::TypeAlias(t) => t.module(db), | ||
988 | } | ||
989 | } | ||
990 | pub fn container(self, db: &dyn HirDatabase) -> AssocItemContainer { | ||
991 | let container = match self { | ||
992 | AssocItem::Function(it) => it.id.lookup(db.upcast()).container, | ||
993 | AssocItem::Const(it) => it.id.lookup(db.upcast()).container, | ||
994 | AssocItem::TypeAlias(it) => it.id.lookup(db.upcast()).container, | ||
995 | }; | ||
996 | match container { | ||
997 | AssocContainerId::TraitId(id) => AssocItemContainer::Trait(id.into()), | ||
998 | AssocContainerId::ImplId(id) => AssocItemContainer::ImplDef(id.into()), | ||
999 | AssocContainerId::ContainerId(_) => panic!("invalid AssocItem"), | ||
1000 | } | ||
1001 | } | ||
1002 | } | ||
1003 | |||
1004 | impl HasVisibility for AssocItem { | ||
1005 | fn visibility(&self, db: &dyn HirDatabase) -> Visibility { | ||
1006 | match self { | ||
1007 | AssocItem::Function(f) => f.visibility(db), | ||
1008 | AssocItem::Const(c) => c.visibility(db), | ||
1009 | AssocItem::TypeAlias(t) => t.visibility(db), | ||
1010 | } | ||
1011 | } | ||
1012 | } | ||
1013 | |||
1014 | #[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)] | ||
1015 | pub enum GenericDef { | ||
1016 | Function(Function), | ||
1017 | Adt(Adt), | ||
1018 | Trait(Trait), | ||
1019 | TypeAlias(TypeAlias), | ||
1020 | ImplDef(ImplDef), | ||
1021 | // enum variants cannot have generics themselves, but their parent enums | ||
1022 | // can, and this makes some code easier to write | ||
1023 | EnumVariant(EnumVariant), | ||
1024 | // consts can have type parameters from their parents (i.e. associated consts of traits) | ||
1025 | Const(Const), | ||
1026 | } | ||
1027 | impl_from!( | ||
1028 | Function, | ||
1029 | Adt(Struct, Enum, Union), | ||
1030 | Trait, | ||
1031 | TypeAlias, | ||
1032 | ImplDef, | ||
1033 | EnumVariant, | ||
1034 | Const | ||
1035 | for GenericDef | ||
1036 | ); | ||
1037 | |||
1038 | impl GenericDef { | ||
1039 | pub fn params(self, db: &dyn HirDatabase) -> Vec<TypeParam> { | ||
1040 | let generics: Arc<hir_def::generics::GenericParams> = db.generic_params(self.into()); | ||
1041 | generics | ||
1042 | .types | ||
1043 | .iter() | ||
1044 | .map(|(local_id, _)| TypeParam { id: TypeParamId { parent: self.into(), local_id } }) | ||
1045 | .collect() | ||
1046 | } | ||
1047 | } | ||
1048 | |||
1049 | #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] | ||
1050 | pub struct Local { | ||
1051 | pub(crate) parent: DefWithBodyId, | ||
1052 | pub(crate) pat_id: PatId, | ||
1053 | } | ||
1054 | |||
1055 | impl Local { | ||
1056 | pub fn is_param(self, db: &dyn HirDatabase) -> bool { | ||
1057 | let src = self.source(db); | ||
1058 | match src.value { | ||
1059 | Either::Left(bind_pat) => { | ||
1060 | bind_pat.syntax().ancestors().any(|it| ast::Param::can_cast(it.kind())) | ||
1061 | } | ||
1062 | Either::Right(_self_param) => true, | ||
1063 | } | ||
1064 | } | ||
1065 | |||
1066 | // FIXME: why is this an option? It shouldn't be? | ||
1067 | pub fn name(self, db: &dyn HirDatabase) -> Option<Name> { | ||
1068 | let body = db.body(self.parent.into()); | ||
1069 | match &body[self.pat_id] { | ||
1070 | Pat::Bind { name, .. } => Some(name.clone()), | ||
1071 | _ => None, | ||
1072 | } | ||
1073 | } | ||
1074 | |||
1075 | pub fn is_self(self, db: &dyn HirDatabase) -> bool { | ||
1076 | self.name(db) == Some(name![self]) | ||
1077 | } | ||
1078 | |||
1079 | pub fn is_mut(self, db: &dyn HirDatabase) -> bool { | ||
1080 | let body = db.body(self.parent.into()); | ||
1081 | match &body[self.pat_id] { | ||
1082 | Pat::Bind { mode, .. } => match mode { | ||
1083 | BindingAnnotation::Mutable | BindingAnnotation::RefMut => true, | ||
1084 | _ => false, | ||
1085 | }, | ||
1086 | _ => false, | ||
1087 | } | ||
1088 | } | ||
1089 | |||
1090 | pub fn parent(self, _db: &dyn HirDatabase) -> DefWithBody { | ||
1091 | self.parent.into() | ||
1092 | } | ||
1093 | |||
1094 | pub fn module(self, db: &dyn HirDatabase) -> Module { | ||
1095 | self.parent(db).module(db) | ||
1096 | } | ||
1097 | |||
1098 | pub fn ty(self, db: &dyn HirDatabase) -> Type { | ||
1099 | let def = DefWithBodyId::from(self.parent); | ||
1100 | let infer = db.infer(def); | ||
1101 | let ty = infer[self.pat_id].clone(); | ||
1102 | let krate = def.module(db.upcast()).krate; | ||
1103 | Type::new(db, krate, def, ty) | ||
1104 | } | ||
1105 | |||
1106 | pub fn source(self, db: &dyn HirDatabase) -> InFile<Either<ast::IdentPat, ast::SelfParam>> { | ||
1107 | let (_body, source_map) = db.body_with_source_map(self.parent.into()); | ||
1108 | let src = source_map.pat_syntax(self.pat_id).unwrap(); // Hmm... | ||
1109 | let root = src.file_syntax(db.upcast()); | ||
1110 | src.map(|ast| { | ||
1111 | ast.map_left(|it| it.cast().unwrap().to_node(&root)).map_right(|it| it.to_node(&root)) | ||
1112 | }) | ||
1113 | } | ||
1114 | } | ||
1115 | |||
1116 | #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] | ||
1117 | pub struct TypeParam { | ||
1118 | pub(crate) id: TypeParamId, | ||
1119 | } | ||
1120 | |||
1121 | impl TypeParam { | ||
1122 | pub fn name(self, db: &dyn HirDatabase) -> Name { | ||
1123 | let params = db.generic_params(self.id.parent); | ||
1124 | params.types[self.id.local_id].name.clone().unwrap_or_else(Name::missing) | ||
1125 | } | ||
1126 | |||
1127 | pub fn module(self, db: &dyn HirDatabase) -> Module { | ||
1128 | self.id.parent.module(db.upcast()).into() | ||
1129 | } | ||
1130 | |||
1131 | pub fn ty(self, db: &dyn HirDatabase) -> Type { | ||
1132 | let resolver = self.id.parent.resolver(db.upcast()); | ||
1133 | let environment = TraitEnvironment::lower(db, &resolver); | ||
1134 | let ty = Ty::Placeholder(self.id); | ||
1135 | Type { | ||
1136 | krate: self.id.parent.module(db.upcast()).krate, | ||
1137 | ty: InEnvironment { value: ty, environment }, | ||
1138 | } | ||
1139 | } | ||
1140 | |||
1141 | pub fn default(self, db: &dyn HirDatabase) -> Option<Type> { | ||
1142 | let params = db.generic_defaults(self.id.parent); | ||
1143 | let local_idx = hir_ty::param_idx(db, self.id)?; | ||
1144 | let resolver = self.id.parent.resolver(db.upcast()); | ||
1145 | let environment = TraitEnvironment::lower(db, &resolver); | ||
1146 | let ty = params.get(local_idx)?.clone(); | ||
1147 | let subst = Substs::type_params(db, self.id.parent); | ||
1148 | let ty = ty.subst(&subst.prefix(local_idx)); | ||
1149 | Some(Type { | ||
1150 | krate: self.id.parent.module(db.upcast()).krate, | ||
1151 | ty: InEnvironment { value: ty, environment }, | ||
1152 | }) | ||
1153 | } | ||
1154 | } | ||
1155 | |||
1156 | // FIXME: rename from `ImplDef` to `Impl` | ||
1157 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | ||
1158 | pub struct ImplDef { | ||
1159 | pub(crate) id: ImplId, | ||
1160 | } | ||
1161 | |||
1162 | impl ImplDef { | ||
1163 | pub fn all_in_crate(db: &dyn HirDatabase, krate: Crate) -> Vec<ImplDef> { | ||
1164 | let inherent = db.inherent_impls_in_crate(krate.id); | ||
1165 | let trait_ = db.trait_impls_in_crate(krate.id); | ||
1166 | |||
1167 | inherent.all_impls().chain(trait_.all_impls()).map(Self::from).collect() | ||
1168 | } | ||
1169 | pub fn for_trait(db: &dyn HirDatabase, krate: Crate, trait_: Trait) -> Vec<ImplDef> { | ||
1170 | let impls = db.trait_impls_in_crate(krate.id); | ||
1171 | impls.for_trait(trait_.id).map(Self::from).collect() | ||
1172 | } | ||
1173 | |||
1174 | pub fn target_trait(self, db: &dyn HirDatabase) -> Option<TypeRef> { | ||
1175 | db.impl_data(self.id).target_trait.clone() | ||
1176 | } | ||
1177 | |||
1178 | pub fn target_type(self, db: &dyn HirDatabase) -> TypeRef { | ||
1179 | db.impl_data(self.id).target_type.clone() | ||
1180 | } | ||
1181 | |||
1182 | pub fn target_ty(self, db: &dyn HirDatabase) -> Type { | ||
1183 | let impl_data = db.impl_data(self.id); | ||
1184 | let resolver = self.id.resolver(db.upcast()); | ||
1185 | let ctx = hir_ty::TyLoweringContext::new(db, &resolver); | ||
1186 | let environment = TraitEnvironment::lower(db, &resolver); | ||
1187 | let ty = Ty::from_hir(&ctx, &impl_data.target_type); | ||
1188 | Type { | ||
1189 | krate: self.id.lookup(db.upcast()).container.module(db.upcast()).krate, | ||
1190 | ty: InEnvironment { value: ty, environment }, | ||
1191 | } | ||
1192 | } | ||
1193 | |||
1194 | pub fn items(self, db: &dyn HirDatabase) -> Vec<AssocItem> { | ||
1195 | db.impl_data(self.id).items.iter().map(|it| (*it).into()).collect() | ||
1196 | } | ||
1197 | |||
1198 | pub fn is_negative(self, db: &dyn HirDatabase) -> bool { | ||
1199 | db.impl_data(self.id).is_negative | ||
1200 | } | ||
1201 | |||
1202 | pub fn module(self, db: &dyn HirDatabase) -> Module { | ||
1203 | self.id.lookup(db.upcast()).container.module(db.upcast()).into() | ||
1204 | } | ||
1205 | |||
1206 | pub fn krate(self, db: &dyn HirDatabase) -> Crate { | ||
1207 | Crate { id: self.module(db).id.krate } | ||
1208 | } | ||
1209 | |||
1210 | pub fn is_builtin_derive(self, db: &dyn HirDatabase) -> Option<InFile<ast::Attr>> { | ||
1211 | let src = self.source(db); | ||
1212 | let item = src.file_id.is_builtin_derive(db.upcast())?; | ||
1213 | let hygenic = hir_expand::hygiene::Hygiene::new(db.upcast(), item.file_id); | ||
1214 | |||
1215 | let attr = item | ||
1216 | .value | ||
1217 | .attrs() | ||
1218 | .filter_map(|it| { | ||
1219 | let path = ModPath::from_src(it.path()?, &hygenic)?; | ||
1220 | if path.as_ident()?.to_string() == "derive" { | ||
1221 | Some(it) | ||
1222 | } else { | ||
1223 | None | ||
1224 | } | ||
1225 | }) | ||
1226 | .last()?; | ||
1227 | |||
1228 | Some(item.with_value(attr)) | ||
1229 | } | ||
1230 | } | ||
1231 | |||
1232 | #[derive(Clone, PartialEq, Eq, Debug)] | ||
1233 | pub struct Type { | ||
1234 | krate: CrateId, | ||
1235 | ty: InEnvironment<Ty>, | ||
1236 | } | ||
1237 | |||
1238 | impl Type { | ||
1239 | pub(crate) fn new_with_resolver( | ||
1240 | db: &dyn HirDatabase, | ||
1241 | resolver: &Resolver, | ||
1242 | ty: Ty, | ||
1243 | ) -> Option<Type> { | ||
1244 | let krate = resolver.krate()?; | ||
1245 | Some(Type::new_with_resolver_inner(db, krate, resolver, ty)) | ||
1246 | } | ||
1247 | pub(crate) fn new_with_resolver_inner( | ||
1248 | db: &dyn HirDatabase, | ||
1249 | krate: CrateId, | ||
1250 | resolver: &Resolver, | ||
1251 | ty: Ty, | ||
1252 | ) -> Type { | ||
1253 | let environment = TraitEnvironment::lower(db, &resolver); | ||
1254 | Type { krate, ty: InEnvironment { value: ty, environment } } | ||
1255 | } | ||
1256 | |||
1257 | fn new(db: &dyn HirDatabase, krate: CrateId, lexical_env: impl HasResolver, ty: Ty) -> Type { | ||
1258 | let resolver = lexical_env.resolver(db.upcast()); | ||
1259 | let environment = TraitEnvironment::lower(db, &resolver); | ||
1260 | Type { krate, ty: InEnvironment { value: ty, environment } } | ||
1261 | } | ||
1262 | |||
1263 | fn from_def( | ||
1264 | db: &dyn HirDatabase, | ||
1265 | krate: CrateId, | ||
1266 | def: impl HasResolver + Into<TyDefId> + Into<GenericDefId>, | ||
1267 | ) -> Type { | ||
1268 | let substs = Substs::build_for_def(db, def).fill_with_unknown().build(); | ||
1269 | let ty = db.ty(def.into()).subst(&substs); | ||
1270 | Type::new(db, krate, def, ty) | ||
1271 | } | ||
1272 | |||
1273 | pub fn is_unit(&self) -> bool { | ||
1274 | matches!( | ||
1275 | self.ty.value, | ||
1276 | Ty::Apply(ApplicationTy { ctor: TypeCtor::Tuple { cardinality: 0 }, .. }) | ||
1277 | ) | ||
1278 | } | ||
1279 | pub fn is_bool(&self) -> bool { | ||
1280 | matches!(self.ty.value, Ty::Apply(ApplicationTy { ctor: TypeCtor::Bool, .. })) | ||
1281 | } | ||
1282 | |||
1283 | pub fn is_mutable_reference(&self) -> bool { | ||
1284 | matches!( | ||
1285 | self.ty.value, | ||
1286 | Ty::Apply(ApplicationTy { ctor: TypeCtor::Ref(Mutability::Mut), .. }) | ||
1287 | ) | ||
1288 | } | ||
1289 | |||
1290 | pub fn is_unknown(&self) -> bool { | ||
1291 | matches!(self.ty.value, Ty::Unknown) | ||
1292 | } | ||
1293 | |||
1294 | /// Checks that particular type `ty` implements `std::future::Future`. | ||
1295 | /// This function is used in `.await` syntax completion. | ||
1296 | pub fn impls_future(&self, db: &dyn HirDatabase) -> bool { | ||
1297 | let krate = self.krate; | ||
1298 | |||
1299 | let std_future_trait = | ||
1300 | db.lang_item(krate, "future_trait".into()).and_then(|it| it.as_trait()); | ||
1301 | let std_future_trait = match std_future_trait { | ||
1302 | Some(it) => it, | ||
1303 | None => return false, | ||
1304 | }; | ||
1305 | |||
1306 | let canonical_ty = Canonical { value: self.ty.value.clone(), kinds: Arc::new([]) }; | ||
1307 | method_resolution::implements_trait( | ||
1308 | &canonical_ty, | ||
1309 | db, | ||
1310 | self.ty.environment.clone(), | ||
1311 | krate, | ||
1312 | std_future_trait, | ||
1313 | ) | ||
1314 | } | ||
1315 | |||
1316 | pub fn impls_trait(&self, db: &dyn HirDatabase, trait_: Trait, args: &[Type]) -> bool { | ||
1317 | let trait_ref = hir_ty::TraitRef { | ||
1318 | trait_: trait_.id, | ||
1319 | substs: Substs::build_for_def(db, trait_.id) | ||
1320 | .push(self.ty.value.clone()) | ||
1321 | .fill(args.iter().map(|t| t.ty.value.clone())) | ||
1322 | .build(), | ||
1323 | }; | ||
1324 | |||
1325 | let goal = Canonical { | ||
1326 | value: hir_ty::InEnvironment::new( | ||
1327 | self.ty.environment.clone(), | ||
1328 | hir_ty::Obligation::Trait(trait_ref), | ||
1329 | ), | ||
1330 | kinds: Arc::new([]), | ||
1331 | }; | ||
1332 | |||
1333 | db.trait_solve(self.krate, goal).is_some() | ||
1334 | } | ||
1335 | |||
1336 | pub fn is_copy(&self, db: &dyn HirDatabase) -> bool { | ||
1337 | let lang_item = db.lang_item(self.krate, SmolStr::new("copy")); | ||
1338 | let copy_trait = match lang_item { | ||
1339 | Some(LangItemTarget::TraitId(it)) => it, | ||
1340 | _ => return false, | ||
1341 | }; | ||
1342 | self.impls_trait(db, copy_trait.into(), &[]) | ||
1343 | } | ||
1344 | |||
1345 | pub fn as_callable(&self, db: &dyn HirDatabase) -> Option<Callable> { | ||
1346 | let def = match self.ty.value { | ||
1347 | Ty::Apply(ApplicationTy { ctor: TypeCtor::FnDef(def), parameters: _ }) => Some(def), | ||
1348 | _ => None, | ||
1349 | }; | ||
1350 | |||
1351 | let sig = self.ty.value.callable_sig(db)?; | ||
1352 | Some(Callable { ty: self.clone(), sig, def, is_bound_method: false }) | ||
1353 | } | ||
1354 | |||
1355 | pub fn is_closure(&self) -> bool { | ||
1356 | matches!(&self.ty.value, Ty::Apply(ApplicationTy { ctor: TypeCtor::Closure { .. }, .. })) | ||
1357 | } | ||
1358 | |||
1359 | pub fn is_fn(&self) -> bool { | ||
1360 | matches!(&self.ty.value, | ||
1361 | Ty::Apply(ApplicationTy { ctor: TypeCtor::FnDef(..), .. }) | | ||
1362 | Ty::Apply(ApplicationTy { ctor: TypeCtor::FnPtr { .. }, .. }) | ||
1363 | ) | ||
1364 | } | ||
1365 | |||
1366 | pub fn is_packed(&self, db: &dyn HirDatabase) -> bool { | ||
1367 | let adt_id = match self.ty.value { | ||
1368 | Ty::Apply(ApplicationTy { ctor: TypeCtor::Adt(adt_id), .. }) => adt_id, | ||
1369 | _ => return false, | ||
1370 | }; | ||
1371 | |||
1372 | let adt = adt_id.into(); | ||
1373 | match adt { | ||
1374 | Adt::Struct(s) => matches!(s.repr(db), Some(ReprKind::Packed)), | ||
1375 | _ => false, | ||
1376 | } | ||
1377 | } | ||
1378 | |||
1379 | pub fn is_raw_ptr(&self) -> bool { | ||
1380 | matches!(&self.ty.value, Ty::Apply(ApplicationTy { ctor: TypeCtor::RawPtr(..), .. })) | ||
1381 | } | ||
1382 | |||
1383 | pub fn contains_unknown(&self) -> bool { | ||
1384 | return go(&self.ty.value); | ||
1385 | |||
1386 | fn go(ty: &Ty) -> bool { | ||
1387 | match ty { | ||
1388 | Ty::Unknown => true, | ||
1389 | Ty::Apply(a_ty) => a_ty.parameters.iter().any(go), | ||
1390 | _ => false, | ||
1391 | } | ||
1392 | } | ||
1393 | } | ||
1394 | |||
1395 | pub fn fields(&self, db: &dyn HirDatabase) -> Vec<(Field, Type)> { | ||
1396 | if let Ty::Apply(a_ty) = &self.ty.value { | ||
1397 | let variant_id = match a_ty.ctor { | ||
1398 | TypeCtor::Adt(AdtId::StructId(s)) => s.into(), | ||
1399 | TypeCtor::Adt(AdtId::UnionId(u)) => u.into(), | ||
1400 | _ => return Vec::new(), | ||
1401 | }; | ||
1402 | |||
1403 | return db | ||
1404 | .field_types(variant_id) | ||
1405 | .iter() | ||
1406 | .map(|(local_id, ty)| { | ||
1407 | let def = Field { parent: variant_id.into(), id: local_id }; | ||
1408 | let ty = ty.clone().subst(&a_ty.parameters); | ||
1409 | (def, self.derived(ty)) | ||
1410 | }) | ||
1411 | .collect(); | ||
1412 | }; | ||
1413 | Vec::new() | ||
1414 | } | ||
1415 | |||
1416 | pub fn tuple_fields(&self, _db: &dyn HirDatabase) -> Vec<Type> { | ||
1417 | let mut res = Vec::new(); | ||
1418 | if let Ty::Apply(a_ty) = &self.ty.value { | ||
1419 | if let TypeCtor::Tuple { .. } = a_ty.ctor { | ||
1420 | for ty in a_ty.parameters.iter() { | ||
1421 | let ty = ty.clone(); | ||
1422 | res.push(self.derived(ty)); | ||
1423 | } | ||
1424 | } | ||
1425 | }; | ||
1426 | res | ||
1427 | } | ||
1428 | |||
1429 | pub fn autoderef<'a>(&'a self, db: &'a dyn HirDatabase) -> impl Iterator<Item = Type> + 'a { | ||
1430 | // There should be no inference vars in types passed here | ||
1431 | // FIXME check that? | ||
1432 | let canonical = Canonical { value: self.ty.value.clone(), kinds: Arc::new([]) }; | ||
1433 | let environment = self.ty.environment.clone(); | ||
1434 | let ty = InEnvironment { value: canonical, environment }; | ||
1435 | autoderef(db, Some(self.krate), ty) | ||
1436 | .map(|canonical| canonical.value) | ||
1437 | .map(move |ty| self.derived(ty)) | ||
1438 | } | ||
1439 | |||
1440 | // This would be nicer if it just returned an iterator, but that runs into | ||
1441 | // lifetime problems, because we need to borrow temp `CrateImplDefs`. | ||
1442 | pub fn iterate_assoc_items<T>( | ||
1443 | self, | ||
1444 | db: &dyn HirDatabase, | ||
1445 | krate: Crate, | ||
1446 | mut callback: impl FnMut(AssocItem) -> Option<T>, | ||
1447 | ) -> Option<T> { | ||
1448 | for krate in self.ty.value.def_crates(db, krate.id)? { | ||
1449 | let impls = db.inherent_impls_in_crate(krate); | ||
1450 | |||
1451 | for impl_def in impls.for_self_ty(&self.ty.value) { | ||
1452 | for &item in db.impl_data(*impl_def).items.iter() { | ||
1453 | if let Some(result) = callback(item.into()) { | ||
1454 | return Some(result); | ||
1455 | } | ||
1456 | } | ||
1457 | } | ||
1458 | } | ||
1459 | None | ||
1460 | } | ||
1461 | |||
1462 | pub fn iterate_method_candidates<T>( | ||
1463 | &self, | ||
1464 | db: &dyn HirDatabase, | ||
1465 | krate: Crate, | ||
1466 | traits_in_scope: &FxHashSet<TraitId>, | ||
1467 | name: Option<&Name>, | ||
1468 | mut callback: impl FnMut(&Ty, Function) -> Option<T>, | ||
1469 | ) -> Option<T> { | ||
1470 | // There should be no inference vars in types passed here | ||
1471 | // FIXME check that? | ||
1472 | // FIXME replace Unknown by bound vars here | ||
1473 | let canonical = Canonical { value: self.ty.value.clone(), kinds: Arc::new([]) }; | ||
1474 | |||
1475 | let env = self.ty.environment.clone(); | ||
1476 | let krate = krate.id; | ||
1477 | |||
1478 | method_resolution::iterate_method_candidates( | ||
1479 | &canonical, | ||
1480 | db, | ||
1481 | env, | ||
1482 | krate, | ||
1483 | traits_in_scope, | ||
1484 | name, | ||
1485 | method_resolution::LookupMode::MethodCall, | ||
1486 | |ty, it| match it { | ||
1487 | AssocItemId::FunctionId(f) => callback(ty, f.into()), | ||
1488 | _ => None, | ||
1489 | }, | ||
1490 | ) | ||
1491 | } | ||
1492 | |||
1493 | pub fn iterate_path_candidates<T>( | ||
1494 | &self, | ||
1495 | db: &dyn HirDatabase, | ||
1496 | krate: Crate, | ||
1497 | traits_in_scope: &FxHashSet<TraitId>, | ||
1498 | name: Option<&Name>, | ||
1499 | mut callback: impl FnMut(&Ty, AssocItem) -> Option<T>, | ||
1500 | ) -> Option<T> { | ||
1501 | // There should be no inference vars in types passed here | ||
1502 | // FIXME check that? | ||
1503 | // FIXME replace Unknown by bound vars here | ||
1504 | let canonical = Canonical { value: self.ty.value.clone(), kinds: Arc::new([]) }; | ||
1505 | |||
1506 | let env = self.ty.environment.clone(); | ||
1507 | let krate = krate.id; | ||
1508 | |||
1509 | method_resolution::iterate_method_candidates( | ||
1510 | &canonical, | ||
1511 | db, | ||
1512 | env, | ||
1513 | krate, | ||
1514 | traits_in_scope, | ||
1515 | name, | ||
1516 | method_resolution::LookupMode::Path, | ||
1517 | |ty, it| callback(ty, it.into()), | ||
1518 | ) | ||
1519 | } | ||
1520 | |||
1521 | pub fn as_adt(&self) -> Option<Adt> { | ||
1522 | let (adt, _subst) = self.ty.value.as_adt()?; | ||
1523 | Some(adt.into()) | ||
1524 | } | ||
1525 | |||
1526 | pub fn as_dyn_trait(&self) -> Option<Trait> { | ||
1527 | self.ty.value.dyn_trait().map(Into::into) | ||
1528 | } | ||
1529 | |||
1530 | pub fn as_impl_traits(&self, db: &dyn HirDatabase) -> Option<Vec<Trait>> { | ||
1531 | self.ty.value.impl_trait_bounds(db).map(|it| { | ||
1532 | it.into_iter() | ||
1533 | .filter_map(|pred| match pred { | ||
1534 | hir_ty::GenericPredicate::Implemented(trait_ref) => { | ||
1535 | Some(Trait::from(trait_ref.trait_)) | ||
1536 | } | ||
1537 | _ => None, | ||
1538 | }) | ||
1539 | .collect() | ||
1540 | }) | ||
1541 | } | ||
1542 | |||
1543 | pub fn as_associated_type_parent_trait(&self, db: &dyn HirDatabase) -> Option<Trait> { | ||
1544 | self.ty.value.associated_type_parent_trait(db).map(Into::into) | ||
1545 | } | ||
1546 | |||
1547 | // FIXME: provide required accessors such that it becomes implementable from outside. | ||
1548 | pub fn is_equal_for_find_impls(&self, other: &Type) -> bool { | ||
1549 | match (&self.ty.value, &other.ty.value) { | ||
1550 | (Ty::Apply(a_original_ty), Ty::Apply(ApplicationTy { ctor, parameters })) => match ctor | ||
1551 | { | ||
1552 | TypeCtor::Ref(..) => match parameters.as_single() { | ||
1553 | Ty::Apply(a_ty) => a_original_ty.ctor == a_ty.ctor, | ||
1554 | _ => false, | ||
1555 | }, | ||
1556 | _ => a_original_ty.ctor == *ctor, | ||
1557 | }, | ||
1558 | _ => false, | ||
1559 | } | ||
1560 | } | ||
1561 | |||
1562 | fn derived(&self, ty: Ty) -> Type { | ||
1563 | Type { | ||
1564 | krate: self.krate, | ||
1565 | ty: InEnvironment { value: ty, environment: self.ty.environment.clone() }, | ||
1566 | } | ||
1567 | } | ||
1568 | |||
1569 | pub fn walk(&self, db: &dyn HirDatabase, mut cb: impl FnMut(Type)) { | ||
1570 | // TypeWalk::walk for a Ty at first visits parameters and only after that the Ty itself. | ||
1571 | // We need a different order here. | ||
1572 | |||
1573 | fn walk_substs( | ||
1574 | db: &dyn HirDatabase, | ||
1575 | type_: &Type, | ||
1576 | substs: &Substs, | ||
1577 | cb: &mut impl FnMut(Type), | ||
1578 | ) { | ||
1579 | for ty in substs.iter() { | ||
1580 | walk_type(db, &type_.derived(ty.clone()), cb); | ||
1581 | } | ||
1582 | } | ||
1583 | |||
1584 | fn walk_bounds( | ||
1585 | db: &dyn HirDatabase, | ||
1586 | type_: &Type, | ||
1587 | bounds: &[GenericPredicate], | ||
1588 | cb: &mut impl FnMut(Type), | ||
1589 | ) { | ||
1590 | for pred in bounds { | ||
1591 | match pred { | ||
1592 | GenericPredicate::Implemented(trait_ref) => { | ||
1593 | cb(type_.clone()); | ||
1594 | walk_substs(db, type_, &trait_ref.substs, cb); | ||
1595 | } | ||
1596 | _ => (), | ||
1597 | } | ||
1598 | } | ||
1599 | } | ||
1600 | |||
1601 | fn walk_type(db: &dyn HirDatabase, type_: &Type, cb: &mut impl FnMut(Type)) { | ||
1602 | let ty = type_.ty.value.strip_references(); | ||
1603 | match ty { | ||
1604 | Ty::Apply(ApplicationTy { ctor, parameters }) => { | ||
1605 | match ctor { | ||
1606 | TypeCtor::Adt(_) => { | ||
1607 | cb(type_.derived(ty.clone())); | ||
1608 | } | ||
1609 | TypeCtor::AssociatedType(_) => { | ||
1610 | if let Some(_) = ty.associated_type_parent_trait(db) { | ||
1611 | cb(type_.derived(ty.clone())); | ||
1612 | } | ||
1613 | } | ||
1614 | _ => (), | ||
1615 | } | ||
1616 | |||
1617 | // adt params, tuples, etc... | ||
1618 | walk_substs(db, type_, parameters, cb); | ||
1619 | } | ||
1620 | Ty::Opaque(opaque_ty) => { | ||
1621 | if let Some(bounds) = ty.impl_trait_bounds(db) { | ||
1622 | walk_bounds(db, &type_.derived(ty.clone()), &bounds, cb); | ||
1623 | } | ||
1624 | |||
1625 | walk_substs(db, type_, &opaque_ty.parameters, cb); | ||
1626 | } | ||
1627 | Ty::Placeholder(_) => { | ||
1628 | if let Some(bounds) = ty.impl_trait_bounds(db) { | ||
1629 | walk_bounds(db, &type_.derived(ty.clone()), &bounds, cb); | ||
1630 | } | ||
1631 | } | ||
1632 | Ty::Dyn(bounds) => { | ||
1633 | walk_bounds(db, &type_.derived(ty.clone()), bounds.as_ref(), cb); | ||
1634 | } | ||
1635 | |||
1636 | _ => (), | ||
1637 | } | ||
1638 | } | ||
1639 | |||
1640 | walk_type(db, self, &mut cb); | ||
1641 | } | ||
1642 | } | ||
1643 | |||
1644 | impl HirDisplay for Type { | ||
1645 | fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError> { | ||
1646 | self.ty.value.hir_fmt(f) | ||
1647 | } | ||
1648 | } | ||
1649 | |||
1650 | // FIXME: closures | ||
1651 | #[derive(Debug)] | ||
1652 | pub struct Callable { | ||
1653 | ty: Type, | ||
1654 | sig: FnSig, | ||
1655 | def: Option<CallableDefId>, | ||
1656 | pub(crate) is_bound_method: bool, | ||
1657 | } | ||
1658 | |||
1659 | pub enum CallableKind { | ||
1660 | Function(Function), | ||
1661 | TupleStruct(Struct), | ||
1662 | TupleEnumVariant(EnumVariant), | ||
1663 | Closure, | ||
1664 | } | ||
1665 | |||
1666 | impl Callable { | ||
1667 | pub fn kind(&self) -> CallableKind { | ||
1668 | match self.def { | ||
1669 | Some(CallableDefId::FunctionId(it)) => CallableKind::Function(it.into()), | ||
1670 | Some(CallableDefId::StructId(it)) => CallableKind::TupleStruct(it.into()), | ||
1671 | Some(CallableDefId::EnumVariantId(it)) => CallableKind::TupleEnumVariant(it.into()), | ||
1672 | None => CallableKind::Closure, | ||
1673 | } | ||
1674 | } | ||
1675 | pub fn receiver_param(&self, db: &dyn HirDatabase) -> Option<ast::SelfParam> { | ||
1676 | let func = match self.def { | ||
1677 | Some(CallableDefId::FunctionId(it)) if self.is_bound_method => it, | ||
1678 | _ => return None, | ||
1679 | }; | ||
1680 | let src = func.lookup(db.upcast()).source(db.upcast()); | ||
1681 | let param_list = src.value.param_list()?; | ||
1682 | param_list.self_param() | ||
1683 | } | ||
1684 | pub fn n_params(&self) -> usize { | ||
1685 | self.sig.params().len() - if self.is_bound_method { 1 } else { 0 } | ||
1686 | } | ||
1687 | pub fn params( | ||
1688 | &self, | ||
1689 | db: &dyn HirDatabase, | ||
1690 | ) -> Vec<(Option<Either<ast::SelfParam, ast::Pat>>, Type)> { | ||
1691 | let types = self | ||
1692 | .sig | ||
1693 | .params() | ||
1694 | .iter() | ||
1695 | .skip(if self.is_bound_method { 1 } else { 0 }) | ||
1696 | .map(|ty| self.ty.derived(ty.clone())); | ||
1697 | let patterns = match self.def { | ||
1698 | Some(CallableDefId::FunctionId(func)) => { | ||
1699 | let src = func.lookup(db.upcast()).source(db.upcast()); | ||
1700 | src.value.param_list().map(|param_list| { | ||
1701 | param_list | ||
1702 | .self_param() | ||
1703 | .map(|it| Some(Either::Left(it))) | ||
1704 | .filter(|_| !self.is_bound_method) | ||
1705 | .into_iter() | ||
1706 | .chain(param_list.params().map(|it| it.pat().map(Either::Right))) | ||
1707 | }) | ||
1708 | } | ||
1709 | _ => None, | ||
1710 | }; | ||
1711 | patterns.into_iter().flatten().chain(iter::repeat(None)).zip(types).collect() | ||
1712 | } | ||
1713 | pub fn return_type(&self) -> Type { | ||
1714 | self.ty.derived(self.sig.ret().clone()) | ||
1715 | } | ||
1716 | } | ||
1717 | |||
1718 | /// For IDE only | ||
1719 | #[derive(Debug)] | ||
1720 | pub enum ScopeDef { | ||
1721 | ModuleDef(ModuleDef), | ||
1722 | MacroDef(MacroDef), | ||
1723 | GenericParam(TypeParam), | ||
1724 | ImplSelfType(ImplDef), | ||
1725 | AdtSelfType(Adt), | ||
1726 | Local(Local), | ||
1727 | Unknown, | ||
1728 | } | ||
1729 | |||
1730 | impl ScopeDef { | ||
1731 | pub fn all_items(def: PerNs) -> ArrayVec<[Self; 3]> { | ||
1732 | let mut items = ArrayVec::new(); | ||
1733 | |||
1734 | match (def.take_types(), def.take_values()) { | ||
1735 | (Some(m1), None) => items.push(ScopeDef::ModuleDef(m1.into())), | ||
1736 | (None, Some(m2)) => items.push(ScopeDef::ModuleDef(m2.into())), | ||
1737 | (Some(m1), Some(m2)) => { | ||
1738 | // Some items, like unit structs and enum variants, are | ||
1739 | // returned as both a type and a value. Here we want | ||
1740 | // to de-duplicate them. | ||
1741 | if m1 != m2 { | ||
1742 | items.push(ScopeDef::ModuleDef(m1.into())); | ||
1743 | items.push(ScopeDef::ModuleDef(m2.into())); | ||
1744 | } else { | ||
1745 | items.push(ScopeDef::ModuleDef(m1.into())); | ||
1746 | } | ||
1747 | } | ||
1748 | (None, None) => {} | ||
1749 | }; | ||
1750 | |||
1751 | if let Some(macro_def_id) = def.take_macros() { | ||
1752 | items.push(ScopeDef::MacroDef(macro_def_id.into())); | ||
1753 | } | ||
1754 | |||
1755 | if items.is_empty() { | ||
1756 | items.push(ScopeDef::Unknown); | ||
1757 | } | ||
1758 | |||
1759 | items | ||
1760 | } | ||
1761 | } | ||
1762 | |||
1763 | #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] | ||
1764 | pub enum AttrDef { | ||
1765 | Module(Module), | ||
1766 | Field(Field), | ||
1767 | Adt(Adt), | ||
1768 | Function(Function), | ||
1769 | EnumVariant(EnumVariant), | ||
1770 | Static(Static), | ||
1771 | Const(Const), | ||
1772 | Trait(Trait), | ||
1773 | TypeAlias(TypeAlias), | ||
1774 | MacroDef(MacroDef), | ||
1775 | } | ||
1776 | |||
1777 | impl_from!( | ||
1778 | Module, | ||
1779 | Field, | ||
1780 | Adt(Struct, Enum, Union), | ||
1781 | EnumVariant, | ||
1782 | Static, | ||
1783 | Const, | ||
1784 | Function, | ||
1785 | Trait, | ||
1786 | TypeAlias, | ||
1787 | MacroDef | ||
1788 | for AttrDef | ||
1789 | ); | ||
1790 | |||
1791 | pub trait HasAttrs { | ||
1792 | fn attrs(self, db: &dyn HirDatabase) -> Attrs; | ||
1793 | } | ||
1794 | |||
1795 | impl<T: Into<AttrDef>> HasAttrs for T { | ||
1796 | fn attrs(self, db: &dyn HirDatabase) -> Attrs { | ||
1797 | let def: AttrDef = self.into(); | ||
1798 | db.attrs(def.into()) | ||
1799 | } | ||
1800 | } | ||
1801 | |||
1802 | pub trait Docs { | ||
1803 | fn docs(&self, db: &dyn HirDatabase) -> Option<Documentation>; | ||
1804 | } | ||
1805 | impl<T: Into<AttrDef> + Copy> Docs for T { | ||
1806 | fn docs(&self, db: &dyn HirDatabase) -> Option<Documentation> { | ||
1807 | let def: AttrDef = (*self).into(); | ||
1808 | db.documentation(def.into()) | ||
1809 | } | ||
1810 | } | ||
1811 | |||
1812 | pub trait HasVisibility { | ||
1813 | fn visibility(&self, db: &dyn HirDatabase) -> Visibility; | ||
1814 | fn is_visible_from(&self, db: &dyn HirDatabase, module: Module) -> bool { | ||
1815 | let vis = self.visibility(db); | ||
1816 | vis.is_visible_from(db.upcast(), module.id) | ||
1817 | } | ||
1818 | } | ||
1819 | |||
1820 | impl Resolvable for ModuleDef { | ||
1821 | fn resolver<D: DefDatabase + HirDatabase>(&self, db: &D) -> Option<Resolver> { | ||
1822 | Some(match self { | ||
1823 | ModuleDef::Module(m) => ModuleId::from(m.clone()).resolver(db), | ||
1824 | ModuleDef::Function(f) => FunctionId::from(f.clone()).resolver(db), | ||
1825 | ModuleDef::Adt(adt) => AdtId::from(adt.clone()).resolver(db), | ||
1826 | ModuleDef::EnumVariant(ev) => { | ||
1827 | GenericDefId::from(GenericDef::from(ev.clone())).resolver(db) | ||
1828 | } | ||
1829 | ModuleDef::Const(c) => GenericDefId::from(GenericDef::from(c.clone())).resolver(db), | ||
1830 | ModuleDef::Static(s) => StaticId::from(s.clone()).resolver(db), | ||
1831 | ModuleDef::Trait(t) => TraitId::from(t.clone()).resolver(db), | ||
1832 | ModuleDef::TypeAlias(t) => ModuleId::from(t.module(db)).resolver(db), | ||
1833 | // FIXME: This should be a resolver relative to `std/core` | ||
1834 | ModuleDef::BuiltinType(_t) => None?, | ||
1835 | }) | ||
1836 | } | ||
1837 | |||
1838 | fn try_into_module_def(self) -> Option<ModuleDef> { | ||
1839 | Some(self) | ||
1840 | } | ||
1841 | } | ||
1842 | |||
1843 | impl Resolvable for TypeParam { | ||
1844 | fn resolver<D: DefDatabase + HirDatabase>(&self, db: &D) -> Option<Resolver> { | ||
1845 | Some(Into::<ModuleId>::into(self.module(db)).resolver(db)) | ||
1846 | } | ||
1847 | |||
1848 | fn try_into_module_def(self) -> Option<ModuleDef> { | ||
1849 | None | ||
1850 | } | ||
1851 | } | ||
1852 | |||
1853 | impl Resolvable for MacroDef { | ||
1854 | fn resolver<D: DefDatabase + HirDatabase>(&self, db: &D) -> Option<Resolver> { | ||
1855 | Some(Into::<ModuleId>::into(self.module(db)?).resolver(db)) | ||
1856 | } | ||
1857 | |||
1858 | fn try_into_module_def(self) -> Option<ModuleDef> { | ||
1859 | None | ||
1860 | } | ||
1861 | } | ||
1862 | |||
1863 | impl Resolvable for Field { | ||
1864 | fn resolver<D: DefDatabase + HirDatabase>(&self, db: &D) -> Option<Resolver> { | ||
1865 | Some(Into::<VariantId>::into(Into::<VariantDef>::into(self.parent_def(db))).resolver(db)) | ||
1866 | } | ||
1867 | |||
1868 | fn try_into_module_def(self) -> Option<ModuleDef> { | ||
1869 | None | ||
1870 | } | ||
1871 | } | ||
1872 | |||
1873 | impl Resolvable for ImplDef { | ||
1874 | fn resolver<D: DefDatabase + HirDatabase>(&self, db: &D) -> Option<Resolver> { | ||
1875 | Some(Into::<ModuleId>::into(self.module(db)).resolver(db)) | ||
1876 | } | ||
1877 | |||
1878 | fn try_into_module_def(self) -> Option<ModuleDef> { | ||
1879 | None | ||
1880 | } | ||
1881 | } | ||
1882 | |||
1883 | impl Resolvable for Local { | ||
1884 | fn resolver<D: DefDatabase + HirDatabase>(&self, db: &D) -> Option<Resolver> { | ||
1885 | Some(Into::<ModuleId>::into(self.module(db)).resolver(db)) | ||
1886 | } | ||
1887 | |||
1888 | fn try_into_module_def(self) -> Option<ModuleDef> { | ||
1889 | None | ||
1890 | } | ||
1891 | } | ||
diff --git a/crates/hir/src/db.rs b/crates/hir/src/db.rs new file mode 100644 index 000000000..07333c453 --- /dev/null +++ b/crates/hir/src/db.rs | |||
@@ -0,0 +1,21 @@ | |||
1 | //! FIXME: write short doc here | ||
2 | |||
3 | pub use hir_def::db::{ | ||
4 | AttrsQuery, BodyQuery, BodyWithSourceMapQuery, ConstDataQuery, CrateDefMapQueryQuery, | ||
5 | CrateLangItemsQuery, DefDatabase, DefDatabaseStorage, DocumentationQuery, EnumDataQuery, | ||
6 | ExprScopesQuery, FunctionDataQuery, GenericParamsQuery, ImplDataQuery, ImportMapQuery, | ||
7 | InternConstQuery, InternDatabase, InternDatabaseStorage, InternEnumQuery, InternFunctionQuery, | ||
8 | InternImplQuery, InternStaticQuery, InternStructQuery, InternTraitQuery, InternTypeAliasQuery, | ||
9 | InternUnionQuery, ItemTreeQuery, LangItemQuery, ModuleLangItemsQuery, StaticDataQuery, | ||
10 | StructDataQuery, TraitDataQuery, TypeAliasDataQuery, UnionDataQuery, | ||
11 | }; | ||
12 | pub use hir_expand::db::{ | ||
13 | AstDatabase, AstDatabaseStorage, AstIdMapQuery, InternEagerExpansionQuery, InternMacroQuery, | ||
14 | MacroArgTextQuery, MacroDefQuery, MacroExpandQuery, ParseMacroQuery, | ||
15 | }; | ||
16 | pub use hir_ty::db::*; | ||
17 | |||
18 | #[test] | ||
19 | fn hir_database_is_object_safe() { | ||
20 | fn _assert_object_safe(_: &dyn HirDatabase) {} | ||
21 | } | ||
diff --git a/crates/hir/src/diagnostics.rs b/crates/hir/src/diagnostics.rs new file mode 100644 index 000000000..363164b9b --- /dev/null +++ b/crates/hir/src/diagnostics.rs | |||
@@ -0,0 +1,6 @@ | |||
1 | //! FIXME: write short doc here | ||
2 | pub use hir_def::diagnostics::UnresolvedModule; | ||
3 | pub use hir_expand::diagnostics::{Diagnostic, DiagnosticSink, DiagnosticSinkBuilder}; | ||
4 | pub use hir_ty::diagnostics::{ | ||
5 | MismatchedArgCount, MissingFields, MissingMatchArms, MissingOkInTailExpr, NoSuchField, | ||
6 | }; | ||
diff --git a/crates/hir/src/from_id.rs b/crates/hir/src/from_id.rs new file mode 100644 index 000000000..a53ac1e08 --- /dev/null +++ b/crates/hir/src/from_id.rs | |||
@@ -0,0 +1,247 @@ | |||
1 | //! Utility module for converting between hir_def ids and code_model wrappers. | ||
2 | //! | ||
3 | //! It's unclear if we need this long-term, but it's definitelly useful while we | ||
4 | //! are splitting the hir. | ||
5 | |||
6 | use hir_def::{ | ||
7 | expr::PatId, AdtId, AssocItemId, AttrDefId, DefWithBodyId, EnumVariantId, FieldId, | ||
8 | GenericDefId, ModuleDefId, VariantId, | ||
9 | }; | ||
10 | |||
11 | use crate::{ | ||
12 | code_model::ItemInNs, Adt, AssocItem, AttrDef, DefWithBody, EnumVariant, Field, GenericDef, | ||
13 | Local, MacroDef, ModuleDef, VariantDef, | ||
14 | }; | ||
15 | |||
16 | macro_rules! from_id { | ||
17 | ($(($id:path, $ty:path)),*) => {$( | ||
18 | impl From<$id> for $ty { | ||
19 | fn from(id: $id) -> $ty { | ||
20 | $ty { id } | ||
21 | } | ||
22 | } | ||
23 | impl From<$ty> for $id { | ||
24 | fn from(ty: $ty) -> $id { | ||
25 | ty.id | ||
26 | } | ||
27 | } | ||
28 | )*} | ||
29 | } | ||
30 | |||
31 | from_id![ | ||
32 | (base_db::CrateId, crate::Crate), | ||
33 | (hir_def::ModuleId, crate::Module), | ||
34 | (hir_def::StructId, crate::Struct), | ||
35 | (hir_def::UnionId, crate::Union), | ||
36 | (hir_def::EnumId, crate::Enum), | ||
37 | (hir_def::TypeAliasId, crate::TypeAlias), | ||
38 | (hir_def::TraitId, crate::Trait), | ||
39 | (hir_def::StaticId, crate::Static), | ||
40 | (hir_def::ConstId, crate::Const), | ||
41 | (hir_def::FunctionId, crate::Function), | ||
42 | (hir_def::ImplId, crate::ImplDef), | ||
43 | (hir_def::TypeParamId, crate::TypeParam), | ||
44 | (hir_expand::MacroDefId, crate::MacroDef) | ||
45 | ]; | ||
46 | |||
47 | impl From<AdtId> for Adt { | ||
48 | fn from(id: AdtId) -> Self { | ||
49 | match id { | ||
50 | AdtId::StructId(it) => Adt::Struct(it.into()), | ||
51 | AdtId::UnionId(it) => Adt::Union(it.into()), | ||
52 | AdtId::EnumId(it) => Adt::Enum(it.into()), | ||
53 | } | ||
54 | } | ||
55 | } | ||
56 | |||
57 | impl From<Adt> for AdtId { | ||
58 | fn from(id: Adt) -> Self { | ||
59 | match id { | ||
60 | Adt::Struct(it) => AdtId::StructId(it.id), | ||
61 | Adt::Union(it) => AdtId::UnionId(it.id), | ||
62 | Adt::Enum(it) => AdtId::EnumId(it.id), | ||
63 | } | ||
64 | } | ||
65 | } | ||
66 | |||
67 | impl From<EnumVariantId> for EnumVariant { | ||
68 | fn from(id: EnumVariantId) -> Self { | ||
69 | EnumVariant { parent: id.parent.into(), id: id.local_id } | ||
70 | } | ||
71 | } | ||
72 | |||
73 | impl From<EnumVariant> for EnumVariantId { | ||
74 | fn from(def: EnumVariant) -> Self { | ||
75 | EnumVariantId { parent: def.parent.id, local_id: def.id } | ||
76 | } | ||
77 | } | ||
78 | |||
79 | impl From<ModuleDefId> for ModuleDef { | ||
80 | fn from(id: ModuleDefId) -> Self { | ||
81 | match id { | ||
82 | ModuleDefId::ModuleId(it) => ModuleDef::Module(it.into()), | ||
83 | ModuleDefId::FunctionId(it) => ModuleDef::Function(it.into()), | ||
84 | ModuleDefId::AdtId(it) => ModuleDef::Adt(it.into()), | ||
85 | ModuleDefId::EnumVariantId(it) => ModuleDef::EnumVariant(it.into()), | ||
86 | ModuleDefId::ConstId(it) => ModuleDef::Const(it.into()), | ||
87 | ModuleDefId::StaticId(it) => ModuleDef::Static(it.into()), | ||
88 | ModuleDefId::TraitId(it) => ModuleDef::Trait(it.into()), | ||
89 | ModuleDefId::TypeAliasId(it) => ModuleDef::TypeAlias(it.into()), | ||
90 | ModuleDefId::BuiltinType(it) => ModuleDef::BuiltinType(it), | ||
91 | } | ||
92 | } | ||
93 | } | ||
94 | |||
95 | impl From<ModuleDef> for ModuleDefId { | ||
96 | fn from(id: ModuleDef) -> Self { | ||
97 | match id { | ||
98 | ModuleDef::Module(it) => ModuleDefId::ModuleId(it.into()), | ||
99 | ModuleDef::Function(it) => ModuleDefId::FunctionId(it.into()), | ||
100 | ModuleDef::Adt(it) => ModuleDefId::AdtId(it.into()), | ||
101 | ModuleDef::EnumVariant(it) => ModuleDefId::EnumVariantId(it.into()), | ||
102 | ModuleDef::Const(it) => ModuleDefId::ConstId(it.into()), | ||
103 | ModuleDef::Static(it) => ModuleDefId::StaticId(it.into()), | ||
104 | ModuleDef::Trait(it) => ModuleDefId::TraitId(it.into()), | ||
105 | ModuleDef::TypeAlias(it) => ModuleDefId::TypeAliasId(it.into()), | ||
106 | ModuleDef::BuiltinType(it) => ModuleDefId::BuiltinType(it), | ||
107 | } | ||
108 | } | ||
109 | } | ||
110 | |||
111 | impl From<DefWithBody> for DefWithBodyId { | ||
112 | fn from(def: DefWithBody) -> Self { | ||
113 | match def { | ||
114 | DefWithBody::Function(it) => DefWithBodyId::FunctionId(it.id), | ||
115 | DefWithBody::Static(it) => DefWithBodyId::StaticId(it.id), | ||
116 | DefWithBody::Const(it) => DefWithBodyId::ConstId(it.id), | ||
117 | } | ||
118 | } | ||
119 | } | ||
120 | |||
121 | impl From<DefWithBodyId> for DefWithBody { | ||
122 | fn from(def: DefWithBodyId) -> Self { | ||
123 | match def { | ||
124 | DefWithBodyId::FunctionId(it) => DefWithBody::Function(it.into()), | ||
125 | DefWithBodyId::StaticId(it) => DefWithBody::Static(it.into()), | ||
126 | DefWithBodyId::ConstId(it) => DefWithBody::Const(it.into()), | ||
127 | } | ||
128 | } | ||
129 | } | ||
130 | |||
131 | impl From<AssocItemId> for AssocItem { | ||
132 | fn from(def: AssocItemId) -> Self { | ||
133 | match def { | ||
134 | AssocItemId::FunctionId(it) => AssocItem::Function(it.into()), | ||
135 | AssocItemId::TypeAliasId(it) => AssocItem::TypeAlias(it.into()), | ||
136 | AssocItemId::ConstId(it) => AssocItem::Const(it.into()), | ||
137 | } | ||
138 | } | ||
139 | } | ||
140 | |||
141 | impl From<GenericDef> for GenericDefId { | ||
142 | fn from(def: GenericDef) -> Self { | ||
143 | match def { | ||
144 | GenericDef::Function(it) => GenericDefId::FunctionId(it.id), | ||
145 | GenericDef::Adt(it) => GenericDefId::AdtId(it.into()), | ||
146 | GenericDef::Trait(it) => GenericDefId::TraitId(it.id), | ||
147 | GenericDef::TypeAlias(it) => GenericDefId::TypeAliasId(it.id), | ||
148 | GenericDef::ImplDef(it) => GenericDefId::ImplId(it.id), | ||
149 | GenericDef::EnumVariant(it) => { | ||
150 | GenericDefId::EnumVariantId(EnumVariantId { parent: it.parent.id, local_id: it.id }) | ||
151 | } | ||
152 | GenericDef::Const(it) => GenericDefId::ConstId(it.id), | ||
153 | } | ||
154 | } | ||
155 | } | ||
156 | |||
157 | impl From<Adt> for GenericDefId { | ||
158 | fn from(id: Adt) -> Self { | ||
159 | match id { | ||
160 | Adt::Struct(it) => it.id.into(), | ||
161 | Adt::Union(it) => it.id.into(), | ||
162 | Adt::Enum(it) => it.id.into(), | ||
163 | } | ||
164 | } | ||
165 | } | ||
166 | |||
167 | impl From<VariantId> for VariantDef { | ||
168 | fn from(def: VariantId) -> Self { | ||
169 | match def { | ||
170 | VariantId::StructId(it) => VariantDef::Struct(it.into()), | ||
171 | VariantId::EnumVariantId(it) => VariantDef::EnumVariant(it.into()), | ||
172 | VariantId::UnionId(it) => VariantDef::Union(it.into()), | ||
173 | } | ||
174 | } | ||
175 | } | ||
176 | |||
177 | impl From<VariantDef> for VariantId { | ||
178 | fn from(def: VariantDef) -> Self { | ||
179 | match def { | ||
180 | VariantDef::Struct(it) => VariantId::StructId(it.id), | ||
181 | VariantDef::EnumVariant(it) => VariantId::EnumVariantId(it.into()), | ||
182 | VariantDef::Union(it) => VariantId::UnionId(it.id), | ||
183 | } | ||
184 | } | ||
185 | } | ||
186 | |||
187 | impl From<Field> for FieldId { | ||
188 | fn from(def: Field) -> Self { | ||
189 | FieldId { parent: def.parent.into(), local_id: def.id } | ||
190 | } | ||
191 | } | ||
192 | |||
193 | impl From<FieldId> for Field { | ||
194 | fn from(def: FieldId) -> Self { | ||
195 | Field { parent: def.parent.into(), id: def.local_id } | ||
196 | } | ||
197 | } | ||
198 | |||
199 | impl From<AttrDef> for AttrDefId { | ||
200 | fn from(def: AttrDef) -> Self { | ||
201 | match def { | ||
202 | AttrDef::Module(it) => AttrDefId::ModuleId(it.id), | ||
203 | AttrDef::Field(it) => AttrDefId::FieldId(it.into()), | ||
204 | AttrDef::Adt(it) => AttrDefId::AdtId(it.into()), | ||
205 | AttrDef::Function(it) => AttrDefId::FunctionId(it.id), | ||
206 | AttrDef::EnumVariant(it) => AttrDefId::EnumVariantId(it.into()), | ||
207 | AttrDef::Static(it) => AttrDefId::StaticId(it.id), | ||
208 | AttrDef::Const(it) => AttrDefId::ConstId(it.id), | ||
209 | AttrDef::Trait(it) => AttrDefId::TraitId(it.id), | ||
210 | AttrDef::TypeAlias(it) => AttrDefId::TypeAliasId(it.id), | ||
211 | AttrDef::MacroDef(it) => AttrDefId::MacroDefId(it.id), | ||
212 | } | ||
213 | } | ||
214 | } | ||
215 | |||
216 | impl From<AssocItem> for GenericDefId { | ||
217 | fn from(item: AssocItem) -> Self { | ||
218 | match item { | ||
219 | AssocItem::Function(f) => f.id.into(), | ||
220 | AssocItem::Const(c) => c.id.into(), | ||
221 | AssocItem::TypeAlias(t) => t.id.into(), | ||
222 | } | ||
223 | } | ||
224 | } | ||
225 | |||
226 | impl From<(DefWithBodyId, PatId)> for Local { | ||
227 | fn from((parent, pat_id): (DefWithBodyId, PatId)) -> Self { | ||
228 | Local { parent, pat_id } | ||
229 | } | ||
230 | } | ||
231 | |||
232 | impl From<MacroDef> for ItemInNs { | ||
233 | fn from(macro_def: MacroDef) -> Self { | ||
234 | ItemInNs::Macros(macro_def.into()) | ||
235 | } | ||
236 | } | ||
237 | |||
238 | impl From<ModuleDef> for ItemInNs { | ||
239 | fn from(module_def: ModuleDef) -> Self { | ||
240 | match module_def { | ||
241 | ModuleDef::Static(_) | ModuleDef::Const(_) | ModuleDef::Function(_) => { | ||
242 | ItemInNs::Values(module_def.into()) | ||
243 | } | ||
244 | _ => ItemInNs::Types(module_def.into()), | ||
245 | } | ||
246 | } | ||
247 | } | ||
diff --git a/crates/hir/src/has_source.rs b/crates/hir/src/has_source.rs new file mode 100644 index 000000000..3bad2338a --- /dev/null +++ b/crates/hir/src/has_source.rs | |||
@@ -0,0 +1,135 @@ | |||
1 | //! Provides set of implementation for hir's objects that allows get back location in file. | ||
2 | |||
3 | use either::Either; | ||
4 | use hir_def::{ | ||
5 | nameres::{ModuleOrigin, ModuleSource}, | ||
6 | src::{HasChildSource, HasSource as _}, | ||
7 | Lookup, VariantId, | ||
8 | }; | ||
9 | use syntax::ast; | ||
10 | |||
11 | use crate::{ | ||
12 | db::HirDatabase, Const, Enum, EnumVariant, Field, FieldSource, Function, ImplDef, MacroDef, | ||
13 | Module, Static, Struct, Trait, TypeAlias, TypeParam, Union, | ||
14 | }; | ||
15 | |||
16 | pub use hir_expand::InFile; | ||
17 | |||
18 | pub trait HasSource { | ||
19 | type Ast; | ||
20 | fn source(self, db: &dyn HirDatabase) -> InFile<Self::Ast>; | ||
21 | } | ||
22 | |||
23 | /// NB: Module is !HasSource, because it has two source nodes at the same time: | ||
24 | /// definition and declaration. | ||
25 | impl Module { | ||
26 | /// Returns a node which defines this module. That is, a file or a `mod foo {}` with items. | ||
27 | pub fn definition_source(self, db: &dyn HirDatabase) -> InFile<ModuleSource> { | ||
28 | let def_map = db.crate_def_map(self.id.krate); | ||
29 | def_map[self.id.local_id].definition_source(db.upcast()) | ||
30 | } | ||
31 | |||
32 | pub fn is_mod_rs(self, db: &dyn HirDatabase) -> bool { | ||
33 | let def_map = db.crate_def_map(self.id.krate); | ||
34 | match def_map[self.id.local_id].origin { | ||
35 | ModuleOrigin::File { is_mod_rs, .. } => is_mod_rs, | ||
36 | _ => false, | ||
37 | } | ||
38 | } | ||
39 | |||
40 | /// Returns a node which declares this module, either a `mod foo;` or a `mod foo {}`. | ||
41 | /// `None` for the crate root. | ||
42 | pub fn declaration_source(self, db: &dyn HirDatabase) -> Option<InFile<ast::Module>> { | ||
43 | let def_map = db.crate_def_map(self.id.krate); | ||
44 | def_map[self.id.local_id].declaration_source(db.upcast()) | ||
45 | } | ||
46 | } | ||
47 | |||
48 | impl HasSource for Field { | ||
49 | type Ast = FieldSource; | ||
50 | fn source(self, db: &dyn HirDatabase) -> InFile<FieldSource> { | ||
51 | let var = VariantId::from(self.parent); | ||
52 | let src = var.child_source(db.upcast()); | ||
53 | src.map(|it| match it[self.id].clone() { | ||
54 | Either::Left(it) => FieldSource::Pos(it), | ||
55 | Either::Right(it) => FieldSource::Named(it), | ||
56 | }) | ||
57 | } | ||
58 | } | ||
59 | impl HasSource for Struct { | ||
60 | type Ast = ast::Struct; | ||
61 | fn source(self, db: &dyn HirDatabase) -> InFile<ast::Struct> { | ||
62 | self.id.lookup(db.upcast()).source(db.upcast()) | ||
63 | } | ||
64 | } | ||
65 | impl HasSource for Union { | ||
66 | type Ast = ast::Union; | ||
67 | fn source(self, db: &dyn HirDatabase) -> InFile<ast::Union> { | ||
68 | self.id.lookup(db.upcast()).source(db.upcast()) | ||
69 | } | ||
70 | } | ||
71 | impl HasSource for Enum { | ||
72 | type Ast = ast::Enum; | ||
73 | fn source(self, db: &dyn HirDatabase) -> InFile<ast::Enum> { | ||
74 | self.id.lookup(db.upcast()).source(db.upcast()) | ||
75 | } | ||
76 | } | ||
77 | impl HasSource for EnumVariant { | ||
78 | type Ast = ast::Variant; | ||
79 | fn source(self, db: &dyn HirDatabase) -> InFile<ast::Variant> { | ||
80 | self.parent.id.child_source(db.upcast()).map(|map| map[self.id].clone()) | ||
81 | } | ||
82 | } | ||
83 | impl HasSource for Function { | ||
84 | type Ast = ast::Fn; | ||
85 | fn source(self, db: &dyn HirDatabase) -> InFile<ast::Fn> { | ||
86 | self.id.lookup(db.upcast()).source(db.upcast()) | ||
87 | } | ||
88 | } | ||
89 | impl HasSource for Const { | ||
90 | type Ast = ast::Const; | ||
91 | fn source(self, db: &dyn HirDatabase) -> InFile<ast::Const> { | ||
92 | self.id.lookup(db.upcast()).source(db.upcast()) | ||
93 | } | ||
94 | } | ||
95 | impl HasSource for Static { | ||
96 | type Ast = ast::Static; | ||
97 | fn source(self, db: &dyn HirDatabase) -> InFile<ast::Static> { | ||
98 | self.id.lookup(db.upcast()).source(db.upcast()) | ||
99 | } | ||
100 | } | ||
101 | impl HasSource for Trait { | ||
102 | type Ast = ast::Trait; | ||
103 | fn source(self, db: &dyn HirDatabase) -> InFile<ast::Trait> { | ||
104 | self.id.lookup(db.upcast()).source(db.upcast()) | ||
105 | } | ||
106 | } | ||
107 | impl HasSource for TypeAlias { | ||
108 | type Ast = ast::TypeAlias; | ||
109 | fn source(self, db: &dyn HirDatabase) -> InFile<ast::TypeAlias> { | ||
110 | self.id.lookup(db.upcast()).source(db.upcast()) | ||
111 | } | ||
112 | } | ||
113 | impl HasSource for MacroDef { | ||
114 | type Ast = ast::MacroCall; | ||
115 | fn source(self, db: &dyn HirDatabase) -> InFile<ast::MacroCall> { | ||
116 | InFile { | ||
117 | file_id: self.id.ast_id.expect("MacroDef without ast_id").file_id, | ||
118 | value: self.id.ast_id.expect("MacroDef without ast_id").to_node(db.upcast()), | ||
119 | } | ||
120 | } | ||
121 | } | ||
122 | impl HasSource for ImplDef { | ||
123 | type Ast = ast::Impl; | ||
124 | fn source(self, db: &dyn HirDatabase) -> InFile<ast::Impl> { | ||
125 | self.id.lookup(db.upcast()).source(db.upcast()) | ||
126 | } | ||
127 | } | ||
128 | |||
129 | impl HasSource for TypeParam { | ||
130 | type Ast = Either<ast::Trait, ast::TypeParam>; | ||
131 | fn source(self, db: &dyn HirDatabase) -> InFile<Self::Ast> { | ||
132 | let child_source = self.id.parent.child_source(db.upcast()); | ||
133 | child_source.map(|it| it[self.id.local_id].clone()) | ||
134 | } | ||
135 | } | ||
diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs new file mode 100644 index 000000000..ae2f1fd4d --- /dev/null +++ b/crates/hir/src/lib.rs | |||
@@ -0,0 +1,69 @@ | |||
1 | //! HIR (previously known as descriptors) provides a high-level object oriented | ||
2 | //! access to Rust code. | ||
3 | //! | ||
4 | //! The principal difference between HIR and syntax trees is that HIR is bound | ||
5 | //! to a particular crate instance. That is, it has cfg flags and features | ||
6 | //! applied. So, the relation between syntax and HIR is many-to-one. | ||
7 | //! | ||
8 | //! HIR is the public API of the all of the compiler logic above syntax trees. | ||
9 | //! It is written in "OO" style. Each type is self contained (as in, it knows it's | ||
10 | //! parents and full context). It should be "clean code". | ||
11 | //! | ||
12 | //! `hir_*` crates are the implementation of the compiler logic. | ||
13 | //! They are written in "ECS" style, with relatively little abstractions. | ||
14 | //! Many types are not self-contained, and explicitly use local indexes, arenas, etc. | ||
15 | //! | ||
16 | //! `hir` is what insulates the "we don't know how to actually write an incremental compiler" | ||
17 | //! from the ide with completions, hovers, etc. It is a (soft, internal) boundary: | ||
18 | //! https://www.tedinski.com/2018/02/06/system-boundaries.html. | ||
19 | |||
20 | #![recursion_limit = "512"] | ||
21 | |||
22 | mod semantics; | ||
23 | pub mod db; | ||
24 | mod source_analyzer; | ||
25 | |||
26 | pub mod diagnostics; | ||
27 | |||
28 | mod from_id; | ||
29 | mod code_model; | ||
30 | mod link_rewrite; | ||
31 | |||
32 | mod has_source; | ||
33 | |||
34 | pub use crate::{ | ||
35 | code_model::{ | ||
36 | Access, Adt, AsAssocItem, AssocItem, AssocItemContainer, AttrDef, Callable, CallableKind, | ||
37 | Const, Crate, CrateDependency, DefWithBody, Docs, Enum, EnumVariant, Field, FieldSource, | ||
38 | Function, GenericDef, HasAttrs, HasVisibility, ImplDef, Local, MacroDef, Module, ModuleDef, | ||
39 | ScopeDef, Static, Struct, Trait, Type, TypeAlias, TypeParam, Union, VariantDef, Visibility, | ||
40 | }, | ||
41 | has_source::HasSource, | ||
42 | link_rewrite::resolve_doc_link, | ||
43 | semantics::{original_range, PathResolution, Semantics, SemanticsScope}, | ||
44 | }; | ||
45 | |||
46 | pub use hir_def::{ | ||
47 | adt::StructKind, | ||
48 | attr::Attrs, | ||
49 | body::scope::ExprScopes, | ||
50 | builtin_type::BuiltinType, | ||
51 | docs::Documentation, | ||
52 | item_scope::ItemInNs, | ||
53 | nameres::ModuleSource, | ||
54 | path::ModPath, | ||
55 | type_ref::{Mutability, TypeRef}, | ||
56 | }; | ||
57 | pub use hir_expand::{ | ||
58 | name::AsName, name::Name, HirFileId, InFile, MacroCallId, MacroCallLoc, | ||
59 | /* FIXME */ MacroDefId, MacroFile, Origin, | ||
60 | }; | ||
61 | pub use hir_ty::display::HirDisplay; | ||
62 | |||
63 | // These are negative re-exports: pub using these names is forbidden, they | ||
64 | // should remain private to hir internals. | ||
65 | #[allow(unused)] | ||
66 | use { | ||
67 | hir_def::path::{Path, PathKind}, | ||
68 | hir_expand::hygiene::Hygiene, | ||
69 | }; | ||
diff --git a/crates/hir/src/link_rewrite.rs b/crates/hir/src/link_rewrite.rs new file mode 100644 index 000000000..dad3a39cf --- /dev/null +++ b/crates/hir/src/link_rewrite.rs | |||
@@ -0,0 +1,226 @@ | |||
1 | //! Resolves and rewrites links in markdown documentation for hovers/completion windows. | ||
2 | |||
3 | use std::iter::once; | ||
4 | |||
5 | use itertools::Itertools; | ||
6 | use url::Url; | ||
7 | |||
8 | use crate::{db::HirDatabase, Adt, AsName, Crate, Hygiene, ItemInNs, ModPath, ModuleDef}; | ||
9 | use hir_def::{db::DefDatabase, resolver::Resolver}; | ||
10 | use syntax::ast::Path; | ||
11 | |||
12 | pub fn resolve_doc_link<T: Resolvable + Clone, D: DefDatabase + HirDatabase>( | ||
13 | db: &D, | ||
14 | definition: &T, | ||
15 | link_text: &str, | ||
16 | link_target: &str, | ||
17 | ) -> Option<(String, String)> { | ||
18 | try_resolve_intra(db, definition, link_text, &link_target).or_else(|| { | ||
19 | if let Some(definition) = definition.clone().try_into_module_def() { | ||
20 | try_resolve_path(db, &definition, &link_target) | ||
21 | .map(|target| (target, link_text.to_string())) | ||
22 | } else { | ||
23 | None | ||
24 | } | ||
25 | }) | ||
26 | } | ||
27 | |||
28 | /// Try to resolve path to local documentation via intra-doc-links (i.e. `super::gateway::Shard`). | ||
29 | /// | ||
30 | /// See [RFC1946](https://github.com/rust-lang/rfcs/blob/master/text/1946-intra-rustdoc-links.md). | ||
31 | fn try_resolve_intra<T: Resolvable, D: DefDatabase + HirDatabase>( | ||
32 | db: &D, | ||
33 | definition: &T, | ||
34 | link_text: &str, | ||
35 | link_target: &str, | ||
36 | ) -> Option<(String, String)> { | ||
37 | // Set link_target for implied shortlinks | ||
38 | let link_target = | ||
39 | if link_target.is_empty() { link_text.trim_matches('`') } else { link_target }; | ||
40 | |||
41 | // Namespace disambiguation | ||
42 | let namespace = Namespace::from_intra_spec(link_target); | ||
43 | |||
44 | // Strip prefixes/suffixes | ||
45 | let link_target = strip_prefixes_suffixes(link_target); | ||
46 | |||
47 | // Parse link as a module path | ||
48 | let path = Path::parse(link_target).ok()?; | ||
49 | let modpath = ModPath::from_src(path, &Hygiene::new_unhygienic()).unwrap(); | ||
50 | |||
51 | // Resolve it relative to symbol's location (according to the RFC this should consider small scopes) | ||
52 | let resolver = definition.resolver(db)?; | ||
53 | |||
54 | let resolved = resolver.resolve_module_path_in_items(db, &modpath); | ||
55 | let (defid, namespace) = match namespace { | ||
56 | // FIXME: .or(resolved.macros) | ||
57 | None => resolved | ||
58 | .types | ||
59 | .map(|t| (t.0, Namespace::Types)) | ||
60 | .or(resolved.values.map(|t| (t.0, Namespace::Values)))?, | ||
61 | Some(ns @ Namespace::Types) => (resolved.types?.0, ns), | ||
62 | Some(ns @ Namespace::Values) => (resolved.values?.0, ns), | ||
63 | // FIXME: | ||
64 | Some(Namespace::Macros) => None?, | ||
65 | }; | ||
66 | |||
67 | // Get the filepath of the final symbol | ||
68 | let def: ModuleDef = defid.into(); | ||
69 | let module = def.module(db)?; | ||
70 | let krate = module.krate(); | ||
71 | let ns = match namespace { | ||
72 | Namespace::Types => ItemInNs::Types(defid), | ||
73 | Namespace::Values => ItemInNs::Values(defid), | ||
74 | // FIXME: | ||
75 | Namespace::Macros => None?, | ||
76 | }; | ||
77 | let import_map = db.import_map(krate.into()); | ||
78 | let path = import_map.path_of(ns)?; | ||
79 | |||
80 | Some(( | ||
81 | get_doc_url(db, &krate)? | ||
82 | .join(&format!("{}/", krate.display_name(db)?)) | ||
83 | .ok()? | ||
84 | .join(&path.segments.iter().map(|name| name.to_string()).join("/")) | ||
85 | .ok()? | ||
86 | .join(&get_symbol_filename(db, &def)?) | ||
87 | .ok()? | ||
88 | .into_string(), | ||
89 | strip_prefixes_suffixes(link_text).to_string(), | ||
90 | )) | ||
91 | } | ||
92 | |||
93 | /// Try to resolve path to local documentation via path-based links (i.e. `../gateway/struct.Shard.html`). | ||
94 | fn try_resolve_path(db: &dyn HirDatabase, moddef: &ModuleDef, link_target: &str) -> Option<String> { | ||
95 | if !link_target.contains("#") && !link_target.contains(".html") { | ||
96 | return None; | ||
97 | } | ||
98 | let ns = ItemInNs::Types(moddef.clone().into()); | ||
99 | |||
100 | let module = moddef.module(db)?; | ||
101 | let krate = module.krate(); | ||
102 | let import_map = db.import_map(krate.into()); | ||
103 | let base = once(format!("{}", krate.display_name(db)?)) | ||
104 | .chain(import_map.path_of(ns)?.segments.iter().map(|name| format!("{}", name))) | ||
105 | .join("/"); | ||
106 | |||
107 | get_doc_url(db, &krate) | ||
108 | .and_then(|url| url.join(&base).ok()) | ||
109 | .and_then(|url| { | ||
110 | get_symbol_filename(db, moddef).as_deref().map(|f| url.join(f).ok()).flatten() | ||
111 | }) | ||
112 | .and_then(|url| url.join(link_target).ok()) | ||
113 | .map(|url| url.into_string()) | ||
114 | } | ||
115 | |||
116 | // Strip prefixes, suffixes, and inline code marks from the given string. | ||
117 | fn strip_prefixes_suffixes(mut s: &str) -> &str { | ||
118 | s = s.trim_matches('`'); | ||
119 | |||
120 | [ | ||
121 | (TYPES.0.iter(), TYPES.1.iter()), | ||
122 | (VALUES.0.iter(), VALUES.1.iter()), | ||
123 | (MACROS.0.iter(), MACROS.1.iter()), | ||
124 | ] | ||
125 | .iter() | ||
126 | .for_each(|(prefixes, suffixes)| { | ||
127 | prefixes.clone().for_each(|prefix| s = s.trim_start_matches(*prefix)); | ||
128 | suffixes.clone().for_each(|suffix| s = s.trim_end_matches(*suffix)); | ||
129 | }); | ||
130 | let s = s.trim_start_matches("@").trim(); | ||
131 | s | ||
132 | } | ||
133 | |||
134 | fn get_doc_url(db: &dyn HirDatabase, krate: &Crate) -> Option<Url> { | ||
135 | krate | ||
136 | .get_doc_url(db) | ||
137 | .or_else(|| | ||
138 | // Fallback to docs.rs | ||
139 | // FIXME: Specify an exact version here. This may be difficult, as multiple versions of the same crate could exist. | ||
140 | Some(format!("https://docs.rs/{}/*/", krate.display_name(db)?))) | ||
141 | .and_then(|s| Url::parse(&s).ok()) | ||
142 | } | ||
143 | |||
144 | /// Get the filename and extension generated for a symbol by rustdoc. | ||
145 | /// | ||
146 | /// Example: `struct.Shard.html` | ||
147 | fn get_symbol_filename(db: &dyn HirDatabase, definition: &ModuleDef) -> Option<String> { | ||
148 | Some(match definition { | ||
149 | ModuleDef::Adt(adt) => match adt { | ||
150 | Adt::Struct(s) => format!("struct.{}.html", s.name(db)), | ||
151 | Adt::Enum(e) => format!("enum.{}.html", e.name(db)), | ||
152 | Adt::Union(u) => format!("union.{}.html", u.name(db)), | ||
153 | }, | ||
154 | ModuleDef::Module(_) => "index.html".to_string(), | ||
155 | ModuleDef::Trait(t) => format!("trait.{}.html", t.name(db)), | ||
156 | ModuleDef::TypeAlias(t) => format!("type.{}.html", t.name(db)), | ||
157 | ModuleDef::BuiltinType(t) => format!("primitive.{}.html", t.as_name()), | ||
158 | ModuleDef::Function(f) => format!("fn.{}.html", f.name(db)), | ||
159 | ModuleDef::EnumVariant(ev) => { | ||
160 | format!("enum.{}.html#variant.{}", ev.parent_enum(db).name(db), ev.name(db)) | ||
161 | } | ||
162 | ModuleDef::Const(c) => format!("const.{}.html", c.name(db)?), | ||
163 | ModuleDef::Static(s) => format!("static.{}.html", s.name(db)?), | ||
164 | }) | ||
165 | } | ||
166 | |||
167 | #[derive(PartialEq, Eq, Hash, Copy, Clone, Debug)] | ||
168 | enum Namespace { | ||
169 | Types, | ||
170 | Values, | ||
171 | Macros, | ||
172 | } | ||
173 | |||
174 | static TYPES: ([&str; 7], [&str; 0]) = | ||
175 | (["type", "struct", "enum", "mod", "trait", "union", "module"], []); | ||
176 | static VALUES: ([&str; 8], [&str; 1]) = | ||
177 | (["value", "function", "fn", "method", "const", "static", "mod", "module"], ["()"]); | ||
178 | static MACROS: ([&str; 1], [&str; 1]) = (["macro"], ["!"]); | ||
179 | |||
180 | impl Namespace { | ||
181 | /// Extract the specified namespace from an intra-doc-link if one exists. | ||
182 | /// | ||
183 | /// # Examples | ||
184 | /// | ||
185 | /// * `struct MyStruct` -> `Namespace::Types` | ||
186 | /// * `panic!` -> `Namespace::Macros` | ||
187 | /// * `fn@from_intra_spec` -> `Namespace::Values` | ||
188 | fn from_intra_spec(s: &str) -> Option<Self> { | ||
189 | [ | ||
190 | (Namespace::Types, (TYPES.0.iter(), TYPES.1.iter())), | ||
191 | (Namespace::Values, (VALUES.0.iter(), VALUES.1.iter())), | ||
192 | (Namespace::Macros, (MACROS.0.iter(), MACROS.1.iter())), | ||
193 | ] | ||
194 | .iter() | ||
195 | .filter(|(_ns, (prefixes, suffixes))| { | ||
196 | prefixes | ||
197 | .clone() | ||
198 | .map(|prefix| { | ||
199 | s.starts_with(*prefix) | ||
200 | && s.chars() | ||
201 | .nth(prefix.len() + 1) | ||
202 | .map(|c| c == '@' || c == ' ') | ||
203 | .unwrap_or(false) | ||
204 | }) | ||
205 | .any(|cond| cond) | ||
206 | || suffixes | ||
207 | .clone() | ||
208 | .map(|suffix| { | ||
209 | s.starts_with(*suffix) | ||
210 | && s.chars() | ||
211 | .nth(suffix.len() + 1) | ||
212 | .map(|c| c == '@' || c == ' ') | ||
213 | .unwrap_or(false) | ||
214 | }) | ||
215 | .any(|cond| cond) | ||
216 | }) | ||
217 | .map(|(ns, (_, _))| *ns) | ||
218 | .next() | ||
219 | } | ||
220 | } | ||
221 | |||
222 | /// Sealed trait used solely for the generic bound on [`resolve_doc_link`]. | ||
223 | pub trait Resolvable { | ||
224 | fn resolver<D: DefDatabase + HirDatabase>(&self, db: &D) -> Option<Resolver>; | ||
225 | fn try_into_module_def(self) -> Option<ModuleDef>; | ||
226 | } | ||
diff --git a/crates/hir/src/semantics.rs b/crates/hir/src/semantics.rs new file mode 100644 index 000000000..1594d4f0f --- /dev/null +++ b/crates/hir/src/semantics.rs | |||
@@ -0,0 +1,819 @@ | |||
1 | //! See `Semantics`. | ||
2 | |||
3 | mod source_to_def; | ||
4 | |||
5 | use std::{cell::RefCell, fmt, iter::successors}; | ||
6 | |||
7 | use base_db::{FileId, FileRange}; | ||
8 | use hir_def::{ | ||
9 | resolver::{self, HasResolver, Resolver, TypeNs}, | ||
10 | AsMacroCall, FunctionId, TraitId, VariantId, | ||
11 | }; | ||
12 | use hir_expand::{hygiene::Hygiene, name::AsName, ExpansionInfo}; | ||
13 | use hir_ty::associated_type_shorthand_candidates; | ||
14 | use itertools::Itertools; | ||
15 | use rustc_hash::{FxHashMap, FxHashSet}; | ||
16 | use syntax::{ | ||
17 | algo::{find_node_at_offset, skip_trivia_token}, | ||
18 | ast, AstNode, Direction, SyntaxNode, SyntaxToken, TextRange, TextSize, | ||
19 | }; | ||
20 | |||
21 | use crate::{ | ||
22 | code_model::Access, | ||
23 | db::HirDatabase, | ||
24 | diagnostics::Diagnostic, | ||
25 | semantics::source_to_def::{ChildContainer, SourceToDefCache, SourceToDefCtx}, | ||
26 | source_analyzer::{resolve_hir_path, SourceAnalyzer}, | ||
27 | AssocItem, Callable, Crate, Field, Function, HirFileId, ImplDef, InFile, Local, MacroDef, | ||
28 | Module, ModuleDef, Name, Origin, Path, ScopeDef, Trait, Type, TypeAlias, TypeParam, VariantDef, | ||
29 | }; | ||
30 | |||
31 | #[derive(Debug, Clone, PartialEq, Eq)] | ||
32 | pub enum PathResolution { | ||
33 | /// An item | ||
34 | Def(ModuleDef), | ||
35 | /// A local binding (only value namespace) | ||
36 | Local(Local), | ||
37 | /// A generic parameter | ||
38 | TypeParam(TypeParam), | ||
39 | SelfType(ImplDef), | ||
40 | Macro(MacroDef), | ||
41 | AssocItem(AssocItem), | ||
42 | } | ||
43 | |||
44 | impl PathResolution { | ||
45 | fn in_type_ns(&self) -> Option<TypeNs> { | ||
46 | match self { | ||
47 | PathResolution::Def(ModuleDef::Adt(adt)) => Some(TypeNs::AdtId((*adt).into())), | ||
48 | PathResolution::Def(ModuleDef::BuiltinType(builtin)) => { | ||
49 | Some(TypeNs::BuiltinType(*builtin)) | ||
50 | } | ||
51 | PathResolution::Def(ModuleDef::Const(_)) | ||
52 | | PathResolution::Def(ModuleDef::EnumVariant(_)) | ||
53 | | PathResolution::Def(ModuleDef::Function(_)) | ||
54 | | PathResolution::Def(ModuleDef::Module(_)) | ||
55 | | PathResolution::Def(ModuleDef::Static(_)) | ||
56 | | PathResolution::Def(ModuleDef::Trait(_)) => None, | ||
57 | PathResolution::Def(ModuleDef::TypeAlias(alias)) => { | ||
58 | Some(TypeNs::TypeAliasId((*alias).into())) | ||
59 | } | ||
60 | PathResolution::Local(_) | PathResolution::Macro(_) => None, | ||
61 | PathResolution::TypeParam(param) => Some(TypeNs::GenericParam((*param).into())), | ||
62 | PathResolution::SelfType(impl_def) => Some(TypeNs::SelfType((*impl_def).into())), | ||
63 | PathResolution::AssocItem(AssocItem::Const(_)) | ||
64 | | PathResolution::AssocItem(AssocItem::Function(_)) => None, | ||
65 | PathResolution::AssocItem(AssocItem::TypeAlias(alias)) => { | ||
66 | Some(TypeNs::TypeAliasId((*alias).into())) | ||
67 | } | ||
68 | } | ||
69 | } | ||
70 | |||
71 | /// Returns an iterator over associated types that may be specified after this path (using | ||
72 | /// `Ty::Assoc` syntax). | ||
73 | pub fn assoc_type_shorthand_candidates<R>( | ||
74 | &self, | ||
75 | db: &dyn HirDatabase, | ||
76 | mut cb: impl FnMut(TypeAlias) -> Option<R>, | ||
77 | ) -> Option<R> { | ||
78 | associated_type_shorthand_candidates(db, self.in_type_ns()?, |_, _, id| cb(id.into())) | ||
79 | } | ||
80 | } | ||
81 | |||
82 | /// Primary API to get semantic information, like types, from syntax trees. | ||
83 | pub struct Semantics<'db, DB> { | ||
84 | pub db: &'db DB, | ||
85 | imp: SemanticsImpl<'db>, | ||
86 | } | ||
87 | |||
88 | pub struct SemanticsImpl<'db> { | ||
89 | pub db: &'db dyn HirDatabase, | ||
90 | s2d_cache: RefCell<SourceToDefCache>, | ||
91 | expansion_info_cache: RefCell<FxHashMap<HirFileId, Option<ExpansionInfo>>>, | ||
92 | cache: RefCell<FxHashMap<SyntaxNode, HirFileId>>, | ||
93 | } | ||
94 | |||
95 | impl<DB> fmt::Debug for Semantics<'_, DB> { | ||
96 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||
97 | write!(f, "Semantics {{ ... }}") | ||
98 | } | ||
99 | } | ||
100 | |||
101 | impl<'db, DB: HirDatabase> Semantics<'db, DB> { | ||
102 | pub fn new(db: &DB) -> Semantics<DB> { | ||
103 | let impl_ = SemanticsImpl::new(db); | ||
104 | Semantics { db, imp: impl_ } | ||
105 | } | ||
106 | |||
107 | pub fn parse(&self, file_id: FileId) -> ast::SourceFile { | ||
108 | self.imp.parse(file_id) | ||
109 | } | ||
110 | |||
111 | pub fn expand(&self, macro_call: &ast::MacroCall) -> Option<SyntaxNode> { | ||
112 | self.imp.expand(macro_call) | ||
113 | } | ||
114 | pub fn speculative_expand( | ||
115 | &self, | ||
116 | actual_macro_call: &ast::MacroCall, | ||
117 | hypothetical_args: &ast::TokenTree, | ||
118 | token_to_map: SyntaxToken, | ||
119 | ) -> Option<(SyntaxNode, SyntaxToken)> { | ||
120 | self.imp.speculative_expand(actual_macro_call, hypothetical_args, token_to_map) | ||
121 | } | ||
122 | |||
123 | pub fn descend_into_macros(&self, token: SyntaxToken) -> SyntaxToken { | ||
124 | self.imp.descend_into_macros(token) | ||
125 | } | ||
126 | |||
127 | pub fn descend_node_at_offset<N: ast::AstNode>( | ||
128 | &self, | ||
129 | node: &SyntaxNode, | ||
130 | offset: TextSize, | ||
131 | ) -> Option<N> { | ||
132 | self.imp.descend_node_at_offset(node, offset).find_map(N::cast) | ||
133 | } | ||
134 | |||
135 | pub fn original_range(&self, node: &SyntaxNode) -> FileRange { | ||
136 | self.imp.original_range(node) | ||
137 | } | ||
138 | |||
139 | pub fn diagnostics_display_range(&self, diagnostics: &dyn Diagnostic) -> FileRange { | ||
140 | self.imp.diagnostics_display_range(diagnostics) | ||
141 | } | ||
142 | |||
143 | pub fn ancestors_with_macros(&self, node: SyntaxNode) -> impl Iterator<Item = SyntaxNode> + '_ { | ||
144 | self.imp.ancestors_with_macros(node) | ||
145 | } | ||
146 | |||
147 | pub fn ancestors_at_offset_with_macros( | ||
148 | &self, | ||
149 | node: &SyntaxNode, | ||
150 | offset: TextSize, | ||
151 | ) -> impl Iterator<Item = SyntaxNode> + '_ { | ||
152 | self.imp.ancestors_at_offset_with_macros(node, offset) | ||
153 | } | ||
154 | |||
155 | /// Find a AstNode by offset inside SyntaxNode, if it is inside *Macrofile*, | ||
156 | /// search up until it is of the target AstNode type | ||
157 | pub fn find_node_at_offset_with_macros<N: AstNode>( | ||
158 | &self, | ||
159 | node: &SyntaxNode, | ||
160 | offset: TextSize, | ||
161 | ) -> Option<N> { | ||
162 | self.imp.ancestors_at_offset_with_macros(node, offset).find_map(N::cast) | ||
163 | } | ||
164 | |||
165 | /// Find a AstNode by offset inside SyntaxNode, if it is inside *MacroCall*, | ||
166 | /// descend it and find again | ||
167 | pub fn find_node_at_offset_with_descend<N: AstNode>( | ||
168 | &self, | ||
169 | node: &SyntaxNode, | ||
170 | offset: TextSize, | ||
171 | ) -> Option<N> { | ||
172 | if let Some(it) = find_node_at_offset(&node, offset) { | ||
173 | return Some(it); | ||
174 | } | ||
175 | |||
176 | self.imp.descend_node_at_offset(node, offset).find_map(N::cast) | ||
177 | } | ||
178 | |||
179 | pub fn type_of_expr(&self, expr: &ast::Expr) -> Option<Type> { | ||
180 | self.imp.type_of_expr(expr) | ||
181 | } | ||
182 | |||
183 | pub fn type_of_pat(&self, pat: &ast::Pat) -> Option<Type> { | ||
184 | self.imp.type_of_pat(pat) | ||
185 | } | ||
186 | |||
187 | pub fn type_of_self(&self, param: &ast::SelfParam) -> Option<Type> { | ||
188 | self.imp.type_of_self(param) | ||
189 | } | ||
190 | |||
191 | pub fn resolve_method_call(&self, call: &ast::MethodCallExpr) -> Option<Function> { | ||
192 | self.imp.resolve_method_call(call).map(Function::from) | ||
193 | } | ||
194 | |||
195 | pub fn resolve_method_call_as_callable(&self, call: &ast::MethodCallExpr) -> Option<Callable> { | ||
196 | self.imp.resolve_method_call_as_callable(call) | ||
197 | } | ||
198 | |||
199 | pub fn resolve_field(&self, field: &ast::FieldExpr) -> Option<Field> { | ||
200 | self.imp.resolve_field(field) | ||
201 | } | ||
202 | |||
203 | pub fn resolve_record_field( | ||
204 | &self, | ||
205 | field: &ast::RecordExprField, | ||
206 | ) -> Option<(Field, Option<Local>)> { | ||
207 | self.imp.resolve_record_field(field) | ||
208 | } | ||
209 | |||
210 | pub fn resolve_record_field_pat(&self, field: &ast::RecordPatField) -> Option<Field> { | ||
211 | self.imp.resolve_record_field_pat(field) | ||
212 | } | ||
213 | |||
214 | pub fn resolve_macro_call(&self, macro_call: &ast::MacroCall) -> Option<MacroDef> { | ||
215 | self.imp.resolve_macro_call(macro_call) | ||
216 | } | ||
217 | |||
218 | pub fn resolve_path(&self, path: &ast::Path) -> Option<PathResolution> { | ||
219 | self.imp.resolve_path(path) | ||
220 | } | ||
221 | |||
222 | pub fn resolve_extern_crate(&self, extern_crate: &ast::ExternCrate) -> Option<Crate> { | ||
223 | self.imp.resolve_extern_crate(extern_crate) | ||
224 | } | ||
225 | |||
226 | pub fn resolve_variant(&self, record_lit: ast::RecordExpr) -> Option<VariantDef> { | ||
227 | self.imp.resolve_variant(record_lit).map(VariantDef::from) | ||
228 | } | ||
229 | |||
230 | pub fn resolve_bind_pat_to_const(&self, pat: &ast::IdentPat) -> Option<ModuleDef> { | ||
231 | self.imp.resolve_bind_pat_to_const(pat) | ||
232 | } | ||
233 | |||
234 | // FIXME: use this instead? | ||
235 | // pub fn resolve_name_ref(&self, name_ref: &ast::NameRef) -> Option<???>; | ||
236 | |||
237 | pub fn record_literal_missing_fields(&self, literal: &ast::RecordExpr) -> Vec<(Field, Type)> { | ||
238 | self.imp.record_literal_missing_fields(literal) | ||
239 | } | ||
240 | |||
241 | pub fn record_pattern_missing_fields(&self, pattern: &ast::RecordPat) -> Vec<(Field, Type)> { | ||
242 | self.imp.record_pattern_missing_fields(pattern) | ||
243 | } | ||
244 | |||
245 | pub fn to_def<T: ToDef>(&self, src: &T) -> Option<T::Def> { | ||
246 | let src = self.imp.find_file(src.syntax().clone()).with_value(src).cloned(); | ||
247 | T::to_def(&self.imp, src) | ||
248 | } | ||
249 | |||
250 | pub fn to_module_def(&self, file: FileId) -> Option<Module> { | ||
251 | self.imp.to_module_def(file) | ||
252 | } | ||
253 | |||
254 | pub fn scope(&self, node: &SyntaxNode) -> SemanticsScope<'db> { | ||
255 | self.imp.scope(node) | ||
256 | } | ||
257 | |||
258 | pub fn scope_at_offset(&self, node: &SyntaxNode, offset: TextSize) -> SemanticsScope<'db> { | ||
259 | self.imp.scope_at_offset(node, offset) | ||
260 | } | ||
261 | |||
262 | pub fn scope_for_def(&self, def: Trait) -> SemanticsScope<'db> { | ||
263 | self.imp.scope_for_def(def) | ||
264 | } | ||
265 | |||
266 | pub fn assert_contains_node(&self, node: &SyntaxNode) { | ||
267 | self.imp.assert_contains_node(node) | ||
268 | } | ||
269 | |||
270 | pub fn is_unsafe_method_call(&self, method_call_expr: &ast::MethodCallExpr) -> bool { | ||
271 | self.imp.is_unsafe_method_call(method_call_expr) | ||
272 | } | ||
273 | |||
274 | pub fn is_unsafe_ref_expr(&self, ref_expr: &ast::RefExpr) -> bool { | ||
275 | self.imp.is_unsafe_ref_expr(ref_expr) | ||
276 | } | ||
277 | |||
278 | pub fn is_unsafe_ident_pat(&self, ident_pat: &ast::IdentPat) -> bool { | ||
279 | self.imp.is_unsafe_ident_pat(ident_pat) | ||
280 | } | ||
281 | } | ||
282 | |||
283 | impl<'db> SemanticsImpl<'db> { | ||
284 | fn new(db: &'db dyn HirDatabase) -> Self { | ||
285 | SemanticsImpl { | ||
286 | db, | ||
287 | s2d_cache: Default::default(), | ||
288 | cache: Default::default(), | ||
289 | expansion_info_cache: Default::default(), | ||
290 | } | ||
291 | } | ||
292 | |||
293 | fn parse(&self, file_id: FileId) -> ast::SourceFile { | ||
294 | let tree = self.db.parse(file_id).tree(); | ||
295 | self.cache(tree.syntax().clone(), file_id.into()); | ||
296 | tree | ||
297 | } | ||
298 | |||
299 | fn expand(&self, macro_call: &ast::MacroCall) -> Option<SyntaxNode> { | ||
300 | let macro_call = self.find_file(macro_call.syntax().clone()).with_value(macro_call); | ||
301 | let sa = self.analyze2(macro_call.map(|it| it.syntax()), None); | ||
302 | let file_id = sa.expand(self.db, macro_call)?; | ||
303 | let node = self.db.parse_or_expand(file_id)?; | ||
304 | self.cache(node.clone(), file_id); | ||
305 | Some(node) | ||
306 | } | ||
307 | |||
308 | fn speculative_expand( | ||
309 | &self, | ||
310 | actual_macro_call: &ast::MacroCall, | ||
311 | hypothetical_args: &ast::TokenTree, | ||
312 | token_to_map: SyntaxToken, | ||
313 | ) -> Option<(SyntaxNode, SyntaxToken)> { | ||
314 | let macro_call = | ||
315 | self.find_file(actual_macro_call.syntax().clone()).with_value(actual_macro_call); | ||
316 | let sa = self.analyze2(macro_call.map(|it| it.syntax()), None); | ||
317 | let krate = sa.resolver.krate()?; | ||
318 | let macro_call_id = macro_call.as_call_id(self.db.upcast(), krate, |path| { | ||
319 | sa.resolver.resolve_path_as_macro(self.db.upcast(), &path) | ||
320 | })?; | ||
321 | hir_expand::db::expand_hypothetical( | ||
322 | self.db.upcast(), | ||
323 | macro_call_id, | ||
324 | hypothetical_args, | ||
325 | token_to_map, | ||
326 | ) | ||
327 | } | ||
328 | |||
329 | fn descend_into_macros(&self, token: SyntaxToken) -> SyntaxToken { | ||
330 | let _p = profile::span("descend_into_macros"); | ||
331 | let parent = token.parent(); | ||
332 | let parent = self.find_file(parent); | ||
333 | let sa = self.analyze2(parent.as_ref(), None); | ||
334 | |||
335 | let token = successors(Some(parent.with_value(token)), |token| { | ||
336 | self.db.check_canceled(); | ||
337 | let macro_call = token.value.ancestors().find_map(ast::MacroCall::cast)?; | ||
338 | let tt = macro_call.token_tree()?; | ||
339 | if !tt.syntax().text_range().contains_range(token.value.text_range()) { | ||
340 | return None; | ||
341 | } | ||
342 | let file_id = sa.expand(self.db, token.with_value(¯o_call))?; | ||
343 | let token = self | ||
344 | .expansion_info_cache | ||
345 | .borrow_mut() | ||
346 | .entry(file_id) | ||
347 | .or_insert_with(|| file_id.expansion_info(self.db.upcast())) | ||
348 | .as_ref()? | ||
349 | .map_token_down(token.as_ref())?; | ||
350 | |||
351 | self.cache(find_root(&token.value.parent()), token.file_id); | ||
352 | |||
353 | Some(token) | ||
354 | }) | ||
355 | .last() | ||
356 | .unwrap(); | ||
357 | |||
358 | token.value | ||
359 | } | ||
360 | |||
361 | fn descend_node_at_offset( | ||
362 | &self, | ||
363 | node: &SyntaxNode, | ||
364 | offset: TextSize, | ||
365 | ) -> impl Iterator<Item = SyntaxNode> + '_ { | ||
366 | // Handle macro token cases | ||
367 | node.token_at_offset(offset) | ||
368 | .map(|token| self.descend_into_macros(token)) | ||
369 | .map(|it| self.ancestors_with_macros(it.parent())) | ||
370 | .flatten() | ||
371 | } | ||
372 | |||
373 | fn original_range(&self, node: &SyntaxNode) -> FileRange { | ||
374 | let node = self.find_file(node.clone()); | ||
375 | original_range(self.db, node.as_ref()) | ||
376 | } | ||
377 | |||
378 | fn diagnostics_display_range(&self, diagnostics: &dyn Diagnostic) -> FileRange { | ||
379 | let src = diagnostics.display_source(); | ||
380 | let root = self.db.parse_or_expand(src.file_id).unwrap(); | ||
381 | let node = src.value.to_node(&root); | ||
382 | self.cache(root, src.file_id); | ||
383 | original_range(self.db, src.with_value(&node)) | ||
384 | } | ||
385 | |||
386 | fn ancestors_with_macros(&self, node: SyntaxNode) -> impl Iterator<Item = SyntaxNode> + '_ { | ||
387 | let node = self.find_file(node); | ||
388 | node.ancestors_with_macros(self.db.upcast()).map(|it| it.value) | ||
389 | } | ||
390 | |||
391 | fn ancestors_at_offset_with_macros( | ||
392 | &self, | ||
393 | node: &SyntaxNode, | ||
394 | offset: TextSize, | ||
395 | ) -> impl Iterator<Item = SyntaxNode> + '_ { | ||
396 | node.token_at_offset(offset) | ||
397 | .map(|token| self.ancestors_with_macros(token.parent())) | ||
398 | .kmerge_by(|node1, node2| node1.text_range().len() < node2.text_range().len()) | ||
399 | } | ||
400 | |||
401 | fn type_of_expr(&self, expr: &ast::Expr) -> Option<Type> { | ||
402 | self.analyze(expr.syntax()).type_of_expr(self.db, &expr) | ||
403 | } | ||
404 | |||
405 | fn type_of_pat(&self, pat: &ast::Pat) -> Option<Type> { | ||
406 | self.analyze(pat.syntax()).type_of_pat(self.db, &pat) | ||
407 | } | ||
408 | |||
409 | fn type_of_self(&self, param: &ast::SelfParam) -> Option<Type> { | ||
410 | self.analyze(param.syntax()).type_of_self(self.db, ¶m) | ||
411 | } | ||
412 | |||
413 | fn resolve_method_call(&self, call: &ast::MethodCallExpr) -> Option<FunctionId> { | ||
414 | self.analyze(call.syntax()).resolve_method_call(self.db, call) | ||
415 | } | ||
416 | |||
417 | fn resolve_method_call_as_callable(&self, call: &ast::MethodCallExpr) -> Option<Callable> { | ||
418 | // FIXME: this erases Substs | ||
419 | let func = self.resolve_method_call(call)?; | ||
420 | let ty = self.db.value_ty(func.into()); | ||
421 | let resolver = self.analyze(call.syntax()).resolver; | ||
422 | let ty = Type::new_with_resolver(self.db, &resolver, ty.value)?; | ||
423 | let mut res = ty.as_callable(self.db)?; | ||
424 | res.is_bound_method = true; | ||
425 | Some(res) | ||
426 | } | ||
427 | |||
428 | fn resolve_field(&self, field: &ast::FieldExpr) -> Option<Field> { | ||
429 | self.analyze(field.syntax()).resolve_field(self.db, field) | ||
430 | } | ||
431 | |||
432 | fn resolve_record_field(&self, field: &ast::RecordExprField) -> Option<(Field, Option<Local>)> { | ||
433 | self.analyze(field.syntax()).resolve_record_field(self.db, field) | ||
434 | } | ||
435 | |||
436 | fn resolve_record_field_pat(&self, field: &ast::RecordPatField) -> Option<Field> { | ||
437 | self.analyze(field.syntax()).resolve_record_field_pat(self.db, field) | ||
438 | } | ||
439 | |||
440 | fn resolve_macro_call(&self, macro_call: &ast::MacroCall) -> Option<MacroDef> { | ||
441 | let sa = self.analyze(macro_call.syntax()); | ||
442 | let macro_call = self.find_file(macro_call.syntax().clone()).with_value(macro_call); | ||
443 | sa.resolve_macro_call(self.db, macro_call) | ||
444 | } | ||
445 | |||
446 | fn resolve_path(&self, path: &ast::Path) -> Option<PathResolution> { | ||
447 | self.analyze(path.syntax()).resolve_path(self.db, path) | ||
448 | } | ||
449 | |||
450 | fn resolve_extern_crate(&self, extern_crate: &ast::ExternCrate) -> Option<Crate> { | ||
451 | let krate = self.scope(extern_crate.syntax()).krate()?; | ||
452 | krate.dependencies(self.db).into_iter().find_map(|dep| { | ||
453 | if dep.name == extern_crate.name_ref()?.as_name() { | ||
454 | Some(dep.krate) | ||
455 | } else { | ||
456 | None | ||
457 | } | ||
458 | }) | ||
459 | } | ||
460 | |||
461 | fn resolve_variant(&self, record_lit: ast::RecordExpr) -> Option<VariantId> { | ||
462 | self.analyze(record_lit.syntax()).resolve_variant(self.db, record_lit) | ||
463 | } | ||
464 | |||
465 | fn resolve_bind_pat_to_const(&self, pat: &ast::IdentPat) -> Option<ModuleDef> { | ||
466 | self.analyze(pat.syntax()).resolve_bind_pat_to_const(self.db, pat) | ||
467 | } | ||
468 | |||
469 | fn record_literal_missing_fields(&self, literal: &ast::RecordExpr) -> Vec<(Field, Type)> { | ||
470 | self.analyze(literal.syntax()) | ||
471 | .record_literal_missing_fields(self.db, literal) | ||
472 | .unwrap_or_default() | ||
473 | } | ||
474 | |||
475 | fn record_pattern_missing_fields(&self, pattern: &ast::RecordPat) -> Vec<(Field, Type)> { | ||
476 | self.analyze(pattern.syntax()) | ||
477 | .record_pattern_missing_fields(self.db, pattern) | ||
478 | .unwrap_or_default() | ||
479 | } | ||
480 | |||
481 | fn with_ctx<F: FnOnce(&mut SourceToDefCtx) -> T, T>(&self, f: F) -> T { | ||
482 | let mut cache = self.s2d_cache.borrow_mut(); | ||
483 | let mut ctx = SourceToDefCtx { db: self.db, cache: &mut *cache }; | ||
484 | f(&mut ctx) | ||
485 | } | ||
486 | |||
487 | fn to_module_def(&self, file: FileId) -> Option<Module> { | ||
488 | self.with_ctx(|ctx| ctx.file_to_def(file)).map(Module::from) | ||
489 | } | ||
490 | |||
491 | fn scope(&self, node: &SyntaxNode) -> SemanticsScope<'db> { | ||
492 | let node = self.find_file(node.clone()); | ||
493 | let resolver = self.analyze2(node.as_ref(), None).resolver; | ||
494 | SemanticsScope { db: self.db, file_id: node.file_id, resolver } | ||
495 | } | ||
496 | |||
497 | fn scope_at_offset(&self, node: &SyntaxNode, offset: TextSize) -> SemanticsScope<'db> { | ||
498 | let node = self.find_file(node.clone()); | ||
499 | let resolver = self.analyze2(node.as_ref(), Some(offset)).resolver; | ||
500 | SemanticsScope { db: self.db, file_id: node.file_id, resolver } | ||
501 | } | ||
502 | |||
503 | fn scope_for_def(&self, def: Trait) -> SemanticsScope<'db> { | ||
504 | let file_id = self.db.lookup_intern_trait(def.id).id.file_id; | ||
505 | let resolver = def.id.resolver(self.db.upcast()); | ||
506 | SemanticsScope { db: self.db, file_id, resolver } | ||
507 | } | ||
508 | |||
509 | fn analyze(&self, node: &SyntaxNode) -> SourceAnalyzer { | ||
510 | let src = self.find_file(node.clone()); | ||
511 | self.analyze2(src.as_ref(), None) | ||
512 | } | ||
513 | |||
514 | fn analyze2(&self, src: InFile<&SyntaxNode>, offset: Option<TextSize>) -> SourceAnalyzer { | ||
515 | let _p = profile::span("Semantics::analyze2"); | ||
516 | |||
517 | let container = match self.with_ctx(|ctx| ctx.find_container(src)) { | ||
518 | Some(it) => it, | ||
519 | None => return SourceAnalyzer::new_for_resolver(Resolver::default(), src), | ||
520 | }; | ||
521 | |||
522 | let resolver = match container { | ||
523 | ChildContainer::DefWithBodyId(def) => { | ||
524 | return SourceAnalyzer::new_for_body(self.db, def, src, offset) | ||
525 | } | ||
526 | ChildContainer::TraitId(it) => it.resolver(self.db.upcast()), | ||
527 | ChildContainer::ImplId(it) => it.resolver(self.db.upcast()), | ||
528 | ChildContainer::ModuleId(it) => it.resolver(self.db.upcast()), | ||
529 | ChildContainer::EnumId(it) => it.resolver(self.db.upcast()), | ||
530 | ChildContainer::VariantId(it) => it.resolver(self.db.upcast()), | ||
531 | ChildContainer::TypeAliasId(it) => it.resolver(self.db.upcast()), | ||
532 | ChildContainer::GenericDefId(it) => it.resolver(self.db.upcast()), | ||
533 | }; | ||
534 | SourceAnalyzer::new_for_resolver(resolver, src) | ||
535 | } | ||
536 | |||
537 | fn cache(&self, root_node: SyntaxNode, file_id: HirFileId) { | ||
538 | assert!(root_node.parent().is_none()); | ||
539 | let mut cache = self.cache.borrow_mut(); | ||
540 | let prev = cache.insert(root_node, file_id); | ||
541 | assert!(prev == None || prev == Some(file_id)) | ||
542 | } | ||
543 | |||
544 | fn assert_contains_node(&self, node: &SyntaxNode) { | ||
545 | self.find_file(node.clone()); | ||
546 | } | ||
547 | |||
548 | fn lookup(&self, root_node: &SyntaxNode) -> Option<HirFileId> { | ||
549 | let cache = self.cache.borrow(); | ||
550 | cache.get(root_node).copied() | ||
551 | } | ||
552 | |||
553 | fn find_file(&self, node: SyntaxNode) -> InFile<SyntaxNode> { | ||
554 | let root_node = find_root(&node); | ||
555 | let file_id = self.lookup(&root_node).unwrap_or_else(|| { | ||
556 | panic!( | ||
557 | "\n\nFailed to lookup {:?} in this Semantics.\n\ | ||
558 | Make sure to use only query nodes, derived from this instance of Semantics.\n\ | ||
559 | root node: {:?}\n\ | ||
560 | known nodes: {}\n\n", | ||
561 | node, | ||
562 | root_node, | ||
563 | self.cache | ||
564 | .borrow() | ||
565 | .keys() | ||
566 | .map(|it| format!("{:?}", it)) | ||
567 | .collect::<Vec<_>>() | ||
568 | .join(", ") | ||
569 | ) | ||
570 | }); | ||
571 | InFile::new(file_id, node) | ||
572 | } | ||
573 | |||
574 | fn is_unsafe_method_call(&self, method_call_expr: &ast::MethodCallExpr) -> bool { | ||
575 | method_call_expr | ||
576 | .receiver() | ||
577 | .and_then(|expr| { | ||
578 | let field_expr = match expr { | ||
579 | ast::Expr::FieldExpr(field_expr) => field_expr, | ||
580 | _ => return None, | ||
581 | }; | ||
582 | let ty = self.type_of_expr(&field_expr.expr()?)?; | ||
583 | if !ty.is_packed(self.db) { | ||
584 | return None; | ||
585 | } | ||
586 | |||
587 | let func = self.resolve_method_call(&method_call_expr).map(Function::from)?; | ||
588 | let res = match func.self_param(self.db)?.access(self.db) { | ||
589 | Access::Shared | Access::Exclusive => true, | ||
590 | Access::Owned => false, | ||
591 | }; | ||
592 | Some(res) | ||
593 | }) | ||
594 | .unwrap_or(false) | ||
595 | } | ||
596 | |||
597 | fn is_unsafe_ref_expr(&self, ref_expr: &ast::RefExpr) -> bool { | ||
598 | ref_expr | ||
599 | .expr() | ||
600 | .and_then(|expr| { | ||
601 | let field_expr = match expr { | ||
602 | ast::Expr::FieldExpr(field_expr) => field_expr, | ||
603 | _ => return None, | ||
604 | }; | ||
605 | let expr = field_expr.expr()?; | ||
606 | self.type_of_expr(&expr) | ||
607 | }) | ||
608 | // Binding a reference to a packed type is possibly unsafe. | ||
609 | .map(|ty| ty.is_packed(self.db)) | ||
610 | .unwrap_or(false) | ||
611 | |||
612 | // FIXME This needs layout computation to be correct. It will highlight | ||
613 | // more than it should with the current implementation. | ||
614 | } | ||
615 | |||
616 | fn is_unsafe_ident_pat(&self, ident_pat: &ast::IdentPat) -> bool { | ||
617 | if !ident_pat.ref_token().is_some() { | ||
618 | return false; | ||
619 | } | ||
620 | |||
621 | ident_pat | ||
622 | .syntax() | ||
623 | .parent() | ||
624 | .and_then(|parent| { | ||
625 | // `IdentPat` can live under `RecordPat` directly under `RecordPatField` or | ||
626 | // `RecordPatFieldList`. `RecordPatField` also lives under `RecordPatFieldList`, | ||
627 | // so this tries to lookup the `IdentPat` anywhere along that structure to the | ||
628 | // `RecordPat` so we can get the containing type. | ||
629 | let record_pat = ast::RecordPatField::cast(parent.clone()) | ||
630 | .and_then(|record_pat| record_pat.syntax().parent()) | ||
631 | .or_else(|| Some(parent.clone())) | ||
632 | .and_then(|parent| { | ||
633 | ast::RecordPatFieldList::cast(parent)? | ||
634 | .syntax() | ||
635 | .parent() | ||
636 | .and_then(ast::RecordPat::cast) | ||
637 | }); | ||
638 | |||
639 | // If this doesn't match a `RecordPat`, fallback to a `LetStmt` to see if | ||
640 | // this is initialized from a `FieldExpr`. | ||
641 | if let Some(record_pat) = record_pat { | ||
642 | self.type_of_pat(&ast::Pat::RecordPat(record_pat)) | ||
643 | } else if let Some(let_stmt) = ast::LetStmt::cast(parent) { | ||
644 | let field_expr = match let_stmt.initializer()? { | ||
645 | ast::Expr::FieldExpr(field_expr) => field_expr, | ||
646 | _ => return None, | ||
647 | }; | ||
648 | |||
649 | self.type_of_expr(&field_expr.expr()?) | ||
650 | } else { | ||
651 | None | ||
652 | } | ||
653 | }) | ||
654 | // Binding a reference to a packed type is possibly unsafe. | ||
655 | .map(|ty| ty.is_packed(self.db)) | ||
656 | .unwrap_or(false) | ||
657 | } | ||
658 | } | ||
659 | |||
660 | pub trait ToDef: AstNode + Clone { | ||
661 | type Def; | ||
662 | |||
663 | fn to_def(sema: &SemanticsImpl, src: InFile<Self>) -> Option<Self::Def>; | ||
664 | } | ||
665 | |||
666 | macro_rules! to_def_impls { | ||
667 | ($(($def:path, $ast:path, $meth:ident)),* ,) => {$( | ||
668 | impl ToDef for $ast { | ||
669 | type Def = $def; | ||
670 | fn to_def(sema: &SemanticsImpl, src: InFile<Self>) -> Option<Self::Def> { | ||
671 | sema.with_ctx(|ctx| ctx.$meth(src)).map(<$def>::from) | ||
672 | } | ||
673 | } | ||
674 | )*} | ||
675 | } | ||
676 | |||
677 | to_def_impls![ | ||
678 | (crate::Module, ast::Module, module_to_def), | ||
679 | (crate::Struct, ast::Struct, struct_to_def), | ||
680 | (crate::Enum, ast::Enum, enum_to_def), | ||
681 | (crate::Union, ast::Union, union_to_def), | ||
682 | (crate::Trait, ast::Trait, trait_to_def), | ||
683 | (crate::ImplDef, ast::Impl, impl_to_def), | ||
684 | (crate::TypeAlias, ast::TypeAlias, type_alias_to_def), | ||
685 | (crate::Const, ast::Const, const_to_def), | ||
686 | (crate::Static, ast::Static, static_to_def), | ||
687 | (crate::Function, ast::Fn, fn_to_def), | ||
688 | (crate::Field, ast::RecordField, record_field_to_def), | ||
689 | (crate::Field, ast::TupleField, tuple_field_to_def), | ||
690 | (crate::EnumVariant, ast::Variant, enum_variant_to_def), | ||
691 | (crate::TypeParam, ast::TypeParam, type_param_to_def), | ||
692 | (crate::MacroDef, ast::MacroCall, macro_call_to_def), // this one is dubious, not all calls are macros | ||
693 | (crate::Local, ast::IdentPat, bind_pat_to_def), | ||
694 | ]; | ||
695 | |||
696 | fn find_root(node: &SyntaxNode) -> SyntaxNode { | ||
697 | node.ancestors().last().unwrap() | ||
698 | } | ||
699 | |||
700 | #[derive(Debug)] | ||
701 | pub struct SemanticsScope<'a> { | ||
702 | pub db: &'a dyn HirDatabase, | ||
703 | file_id: HirFileId, | ||
704 | resolver: Resolver, | ||
705 | } | ||
706 | |||
707 | impl<'a> SemanticsScope<'a> { | ||
708 | pub fn module(&self) -> Option<Module> { | ||
709 | Some(Module { id: self.resolver.module()? }) | ||
710 | } | ||
711 | |||
712 | pub fn krate(&self) -> Option<Crate> { | ||
713 | Some(Crate { id: self.resolver.krate()? }) | ||
714 | } | ||
715 | |||
716 | /// Note: `FxHashSet<TraitId>` should be treated as an opaque type, passed into `Type | ||
717 | // FIXME: rename to visible_traits to not repeat scope? | ||
718 | pub fn traits_in_scope(&self) -> FxHashSet<TraitId> { | ||
719 | let resolver = &self.resolver; | ||
720 | resolver.traits_in_scope(self.db.upcast()) | ||
721 | } | ||
722 | |||
723 | pub fn process_all_names(&self, f: &mut dyn FnMut(Name, ScopeDef)) { | ||
724 | let resolver = &self.resolver; | ||
725 | |||
726 | resolver.process_all_names(self.db.upcast(), &mut |name, def| { | ||
727 | let def = match def { | ||
728 | resolver::ScopeDef::PerNs(it) => { | ||
729 | let items = ScopeDef::all_items(it); | ||
730 | for item in items { | ||
731 | f(name.clone(), item); | ||
732 | } | ||
733 | return; | ||
734 | } | ||
735 | resolver::ScopeDef::ImplSelfType(it) => ScopeDef::ImplSelfType(it.into()), | ||
736 | resolver::ScopeDef::AdtSelfType(it) => ScopeDef::AdtSelfType(it.into()), | ||
737 | resolver::ScopeDef::GenericParam(id) => ScopeDef::GenericParam(TypeParam { id }), | ||
738 | resolver::ScopeDef::Local(pat_id) => { | ||
739 | let parent = resolver.body_owner().unwrap().into(); | ||
740 | ScopeDef::Local(Local { parent, pat_id }) | ||
741 | } | ||
742 | }; | ||
743 | f(name, def) | ||
744 | }) | ||
745 | } | ||
746 | |||
747 | /// Resolve a path as-if it was written at the given scope. This is | ||
748 | /// necessary a heuristic, as it doesn't take hygiene into account. | ||
749 | pub fn speculative_resolve(&self, path: &ast::Path) -> Option<PathResolution> { | ||
750 | let hygiene = Hygiene::new(self.db.upcast(), self.file_id); | ||
751 | let path = Path::from_src(path.clone(), &hygiene)?; | ||
752 | resolve_hir_path(self.db, &self.resolver, &path) | ||
753 | } | ||
754 | } | ||
755 | |||
756 | // FIXME: Change `HasSource` trait to work with `Semantics` and remove this? | ||
757 | pub fn original_range(db: &dyn HirDatabase, node: InFile<&SyntaxNode>) -> FileRange { | ||
758 | if let Some(range) = original_range_opt(db, node) { | ||
759 | let original_file = range.file_id.original_file(db.upcast()); | ||
760 | if range.file_id == original_file.into() { | ||
761 | return FileRange { file_id: original_file, range: range.value }; | ||
762 | } | ||
763 | |||
764 | log::error!("Fail to mapping up more for {:?}", range); | ||
765 | return FileRange { file_id: range.file_id.original_file(db.upcast()), range: range.value }; | ||
766 | } | ||
767 | |||
768 | // Fall back to whole macro call | ||
769 | if let Some(expansion) = node.file_id.expansion_info(db.upcast()) { | ||
770 | if let Some(call_node) = expansion.call_node() { | ||
771 | return FileRange { | ||
772 | file_id: call_node.file_id.original_file(db.upcast()), | ||
773 | range: call_node.value.text_range(), | ||
774 | }; | ||
775 | } | ||
776 | } | ||
777 | |||
778 | FileRange { file_id: node.file_id.original_file(db.upcast()), range: node.value.text_range() } | ||
779 | } | ||
780 | |||
781 | fn original_range_opt( | ||
782 | db: &dyn HirDatabase, | ||
783 | node: InFile<&SyntaxNode>, | ||
784 | ) -> Option<InFile<TextRange>> { | ||
785 | let expansion = node.file_id.expansion_info(db.upcast())?; | ||
786 | |||
787 | // the input node has only one token ? | ||
788 | let single = skip_trivia_token(node.value.first_token()?, Direction::Next)? | ||
789 | == skip_trivia_token(node.value.last_token()?, Direction::Prev)?; | ||
790 | |||
791 | Some(node.value.descendants().find_map(|it| { | ||
792 | let first = skip_trivia_token(it.first_token()?, Direction::Next)?; | ||
793 | let first = ascend_call_token(db, &expansion, node.with_value(first))?; | ||
794 | |||
795 | let last = skip_trivia_token(it.last_token()?, Direction::Prev)?; | ||
796 | let last = ascend_call_token(db, &expansion, node.with_value(last))?; | ||
797 | |||
798 | if (!single && first == last) || (first.file_id != last.file_id) { | ||
799 | return None; | ||
800 | } | ||
801 | |||
802 | Some(first.with_value(first.value.text_range().cover(last.value.text_range()))) | ||
803 | })?) | ||
804 | } | ||
805 | |||
806 | fn ascend_call_token( | ||
807 | db: &dyn HirDatabase, | ||
808 | expansion: &ExpansionInfo, | ||
809 | token: InFile<SyntaxToken>, | ||
810 | ) -> Option<InFile<SyntaxToken>> { | ||
811 | let (mapped, origin) = expansion.map_token_up(token.as_ref())?; | ||
812 | if origin != Origin::Call { | ||
813 | return None; | ||
814 | } | ||
815 | if let Some(info) = mapped.file_id.expansion_info(db.upcast()) { | ||
816 | return ascend_call_token(db, &info, mapped); | ||
817 | } | ||
818 | Some(mapped) | ||
819 | } | ||
diff --git a/crates/hir/src/semantics/source_to_def.rs b/crates/hir/src/semantics/source_to_def.rs new file mode 100644 index 000000000..5918b9541 --- /dev/null +++ b/crates/hir/src/semantics/source_to_def.rs | |||
@@ -0,0 +1,275 @@ | |||
1 | //! Maps *syntax* of various definitions to their semantic ids. | ||
2 | |||
3 | use base_db::FileId; | ||
4 | use hir_def::{ | ||
5 | child_by_source::ChildBySource, | ||
6 | dyn_map::DynMap, | ||
7 | expr::PatId, | ||
8 | keys::{self, Key}, | ||
9 | ConstId, DefWithBodyId, EnumId, EnumVariantId, FieldId, FunctionId, GenericDefId, ImplId, | ||
10 | ModuleId, StaticId, StructId, TraitId, TypeAliasId, TypeParamId, UnionId, VariantId, | ||
11 | }; | ||
12 | use hir_expand::{name::AsName, AstId, MacroDefKind}; | ||
13 | use rustc_hash::FxHashMap; | ||
14 | use stdx::impl_from; | ||
15 | use syntax::{ | ||
16 | ast::{self, NameOwner}, | ||
17 | match_ast, AstNode, SyntaxNode, | ||
18 | }; | ||
19 | |||
20 | use crate::{db::HirDatabase, InFile, MacroDefId}; | ||
21 | |||
22 | pub(super) type SourceToDefCache = FxHashMap<ChildContainer, DynMap>; | ||
23 | |||
24 | pub(super) struct SourceToDefCtx<'a, 'b> { | ||
25 | pub(super) db: &'b dyn HirDatabase, | ||
26 | pub(super) cache: &'a mut SourceToDefCache, | ||
27 | } | ||
28 | |||
29 | impl SourceToDefCtx<'_, '_> { | ||
30 | pub(super) fn file_to_def(&mut self, file: FileId) -> Option<ModuleId> { | ||
31 | let _p = profile::span("SourceBinder::to_module_def"); | ||
32 | let (krate, local_id) = self.db.relevant_crates(file).iter().find_map(|&crate_id| { | ||
33 | let crate_def_map = self.db.crate_def_map(crate_id); | ||
34 | let local_id = crate_def_map.modules_for_file(file).next()?; | ||
35 | Some((crate_id, local_id)) | ||
36 | })?; | ||
37 | Some(ModuleId { krate, local_id }) | ||
38 | } | ||
39 | |||
40 | pub(super) fn module_to_def(&mut self, src: InFile<ast::Module>) -> Option<ModuleId> { | ||
41 | let _p = profile::span("module_to_def"); | ||
42 | let parent_declaration = src | ||
43 | .as_ref() | ||
44 | .map(|it| it.syntax()) | ||
45 | .cloned() | ||
46 | .ancestors_with_macros(self.db.upcast()) | ||
47 | .skip(1) | ||
48 | .find_map(|it| { | ||
49 | let m = ast::Module::cast(it.value.clone())?; | ||
50 | Some(it.with_value(m)) | ||
51 | }); | ||
52 | |||
53 | let parent_module = match parent_declaration { | ||
54 | Some(parent_declaration) => self.module_to_def(parent_declaration), | ||
55 | None => { | ||
56 | let file_id = src.file_id.original_file(self.db.upcast()); | ||
57 | self.file_to_def(file_id) | ||
58 | } | ||
59 | }?; | ||
60 | |||
61 | let child_name = src.value.name()?.as_name(); | ||
62 | let def_map = self.db.crate_def_map(parent_module.krate); | ||
63 | let child_id = *def_map[parent_module.local_id].children.get(&child_name)?; | ||
64 | Some(ModuleId { krate: parent_module.krate, local_id: child_id }) | ||
65 | } | ||
66 | |||
67 | pub(super) fn trait_to_def(&mut self, src: InFile<ast::Trait>) -> Option<TraitId> { | ||
68 | self.to_def(src, keys::TRAIT) | ||
69 | } | ||
70 | pub(super) fn impl_to_def(&mut self, src: InFile<ast::Impl>) -> Option<ImplId> { | ||
71 | self.to_def(src, keys::IMPL) | ||
72 | } | ||
73 | pub(super) fn fn_to_def(&mut self, src: InFile<ast::Fn>) -> Option<FunctionId> { | ||
74 | self.to_def(src, keys::FUNCTION) | ||
75 | } | ||
76 | pub(super) fn struct_to_def(&mut self, src: InFile<ast::Struct>) -> Option<StructId> { | ||
77 | self.to_def(src, keys::STRUCT) | ||
78 | } | ||
79 | pub(super) fn enum_to_def(&mut self, src: InFile<ast::Enum>) -> Option<EnumId> { | ||
80 | self.to_def(src, keys::ENUM) | ||
81 | } | ||
82 | pub(super) fn union_to_def(&mut self, src: InFile<ast::Union>) -> Option<UnionId> { | ||
83 | self.to_def(src, keys::UNION) | ||
84 | } | ||
85 | pub(super) fn static_to_def(&mut self, src: InFile<ast::Static>) -> Option<StaticId> { | ||
86 | self.to_def(src, keys::STATIC) | ||
87 | } | ||
88 | pub(super) fn const_to_def(&mut self, src: InFile<ast::Const>) -> Option<ConstId> { | ||
89 | self.to_def(src, keys::CONST) | ||
90 | } | ||
91 | pub(super) fn type_alias_to_def(&mut self, src: InFile<ast::TypeAlias>) -> Option<TypeAliasId> { | ||
92 | self.to_def(src, keys::TYPE_ALIAS) | ||
93 | } | ||
94 | pub(super) fn record_field_to_def(&mut self, src: InFile<ast::RecordField>) -> Option<FieldId> { | ||
95 | self.to_def(src, keys::RECORD_FIELD) | ||
96 | } | ||
97 | pub(super) fn tuple_field_to_def(&mut self, src: InFile<ast::TupleField>) -> Option<FieldId> { | ||
98 | self.to_def(src, keys::TUPLE_FIELD) | ||
99 | } | ||
100 | pub(super) fn enum_variant_to_def( | ||
101 | &mut self, | ||
102 | src: InFile<ast::Variant>, | ||
103 | ) -> Option<EnumVariantId> { | ||
104 | self.to_def(src, keys::VARIANT) | ||
105 | } | ||
106 | pub(super) fn bind_pat_to_def( | ||
107 | &mut self, | ||
108 | src: InFile<ast::IdentPat>, | ||
109 | ) -> Option<(DefWithBodyId, PatId)> { | ||
110 | let container = self.find_pat_container(src.as_ref().map(|it| it.syntax()))?; | ||
111 | let (_body, source_map) = self.db.body_with_source_map(container); | ||
112 | let src = src.map(ast::Pat::from); | ||
113 | let pat_id = source_map.node_pat(src.as_ref())?; | ||
114 | Some((container, pat_id)) | ||
115 | } | ||
116 | |||
117 | fn to_def<Ast: AstNode + 'static, ID: Copy + 'static>( | ||
118 | &mut self, | ||
119 | src: InFile<Ast>, | ||
120 | key: Key<Ast, ID>, | ||
121 | ) -> Option<ID> { | ||
122 | let container = self.find_container(src.as_ref().map(|it| it.syntax()))?; | ||
123 | let db = self.db; | ||
124 | let dyn_map = | ||
125 | &*self.cache.entry(container).or_insert_with(|| container.child_by_source(db)); | ||
126 | dyn_map[key].get(&src).copied() | ||
127 | } | ||
128 | |||
129 | pub(super) fn type_param_to_def(&mut self, src: InFile<ast::TypeParam>) -> Option<TypeParamId> { | ||
130 | let container: ChildContainer = | ||
131 | self.find_type_param_container(src.as_ref().map(|it| it.syntax()))?.into(); | ||
132 | let db = self.db; | ||
133 | let dyn_map = | ||
134 | &*self.cache.entry(container).or_insert_with(|| container.child_by_source(db)); | ||
135 | dyn_map[keys::TYPE_PARAM].get(&src).copied() | ||
136 | } | ||
137 | |||
138 | // FIXME: use DynMap as well? | ||
139 | pub(super) fn macro_call_to_def(&mut self, src: InFile<ast::MacroCall>) -> Option<MacroDefId> { | ||
140 | let kind = MacroDefKind::Declarative; | ||
141 | let file_id = src.file_id.original_file(self.db.upcast()); | ||
142 | let krate = self.file_to_def(file_id)?.krate; | ||
143 | let file_ast_id = self.db.ast_id_map(src.file_id).ast_id(&src.value); | ||
144 | let ast_id = Some(AstId::new(src.file_id, file_ast_id)); | ||
145 | Some(MacroDefId { krate: Some(krate), ast_id, kind, local_inner: false }) | ||
146 | } | ||
147 | |||
148 | pub(super) fn find_container(&mut self, src: InFile<&SyntaxNode>) -> Option<ChildContainer> { | ||
149 | for container in src.cloned().ancestors_with_macros(self.db.upcast()).skip(1) { | ||
150 | let res: ChildContainer = match_ast! { | ||
151 | match (container.value) { | ||
152 | ast::Module(it) => { | ||
153 | let def = self.module_to_def(container.with_value(it))?; | ||
154 | def.into() | ||
155 | }, | ||
156 | ast::Trait(it) => { | ||
157 | let def = self.trait_to_def(container.with_value(it))?; | ||
158 | def.into() | ||
159 | }, | ||
160 | ast::Impl(it) => { | ||
161 | let def = self.impl_to_def(container.with_value(it))?; | ||
162 | def.into() | ||
163 | }, | ||
164 | ast::Fn(it) => { | ||
165 | let def = self.fn_to_def(container.with_value(it))?; | ||
166 | DefWithBodyId::from(def).into() | ||
167 | }, | ||
168 | ast::Struct(it) => { | ||
169 | let def = self.struct_to_def(container.with_value(it))?; | ||
170 | VariantId::from(def).into() | ||
171 | }, | ||
172 | ast::Enum(it) => { | ||
173 | let def = self.enum_to_def(container.with_value(it))?; | ||
174 | def.into() | ||
175 | }, | ||
176 | ast::Union(it) => { | ||
177 | let def = self.union_to_def(container.with_value(it))?; | ||
178 | VariantId::from(def).into() | ||
179 | }, | ||
180 | ast::Static(it) => { | ||
181 | let def = self.static_to_def(container.with_value(it))?; | ||
182 | DefWithBodyId::from(def).into() | ||
183 | }, | ||
184 | ast::Const(it) => { | ||
185 | let def = self.const_to_def(container.with_value(it))?; | ||
186 | DefWithBodyId::from(def).into() | ||
187 | }, | ||
188 | ast::TypeAlias(it) => { | ||
189 | let def = self.type_alias_to_def(container.with_value(it))?; | ||
190 | def.into() | ||
191 | }, | ||
192 | _ => continue, | ||
193 | } | ||
194 | }; | ||
195 | return Some(res); | ||
196 | } | ||
197 | |||
198 | let def = self.file_to_def(src.file_id.original_file(self.db.upcast()))?; | ||
199 | Some(def.into()) | ||
200 | } | ||
201 | |||
202 | fn find_type_param_container(&mut self, src: InFile<&SyntaxNode>) -> Option<GenericDefId> { | ||
203 | for container in src.cloned().ancestors_with_macros(self.db.upcast()).skip(1) { | ||
204 | let res: GenericDefId = match_ast! { | ||
205 | match (container.value) { | ||
206 | ast::Fn(it) => self.fn_to_def(container.with_value(it))?.into(), | ||
207 | ast::Struct(it) => self.struct_to_def(container.with_value(it))?.into(), | ||
208 | ast::Enum(it) => self.enum_to_def(container.with_value(it))?.into(), | ||
209 | ast::Trait(it) => self.trait_to_def(container.with_value(it))?.into(), | ||
210 | ast::TypeAlias(it) => self.type_alias_to_def(container.with_value(it))?.into(), | ||
211 | ast::Impl(it) => self.impl_to_def(container.with_value(it))?.into(), | ||
212 | _ => continue, | ||
213 | } | ||
214 | }; | ||
215 | return Some(res); | ||
216 | } | ||
217 | None | ||
218 | } | ||
219 | |||
220 | fn find_pat_container(&mut self, src: InFile<&SyntaxNode>) -> Option<DefWithBodyId> { | ||
221 | for container in src.cloned().ancestors_with_macros(self.db.upcast()).skip(1) { | ||
222 | let res: DefWithBodyId = match_ast! { | ||
223 | match (container.value) { | ||
224 | ast::Const(it) => self.const_to_def(container.with_value(it))?.into(), | ||
225 | ast::Static(it) => self.static_to_def(container.with_value(it))?.into(), | ||
226 | ast::Fn(it) => self.fn_to_def(container.with_value(it))?.into(), | ||
227 | _ => continue, | ||
228 | } | ||
229 | }; | ||
230 | return Some(res); | ||
231 | } | ||
232 | None | ||
233 | } | ||
234 | } | ||
235 | |||
236 | #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)] | ||
237 | pub(crate) enum ChildContainer { | ||
238 | DefWithBodyId(DefWithBodyId), | ||
239 | ModuleId(ModuleId), | ||
240 | TraitId(TraitId), | ||
241 | ImplId(ImplId), | ||
242 | EnumId(EnumId), | ||
243 | VariantId(VariantId), | ||
244 | TypeAliasId(TypeAliasId), | ||
245 | /// XXX: this might be the same def as, for example an `EnumId`. However, | ||
246 | /// here the children generic parameters, and not, eg enum variants. | ||
247 | GenericDefId(GenericDefId), | ||
248 | } | ||
249 | impl_from! { | ||
250 | DefWithBodyId, | ||
251 | ModuleId, | ||
252 | TraitId, | ||
253 | ImplId, | ||
254 | EnumId, | ||
255 | VariantId, | ||
256 | TypeAliasId, | ||
257 | GenericDefId | ||
258 | for ChildContainer | ||
259 | } | ||
260 | |||
261 | impl ChildContainer { | ||
262 | fn child_by_source(self, db: &dyn HirDatabase) -> DynMap { | ||
263 | let db = db.upcast(); | ||
264 | match self { | ||
265 | ChildContainer::DefWithBodyId(it) => it.child_by_source(db), | ||
266 | ChildContainer::ModuleId(it) => it.child_by_source(db), | ||
267 | ChildContainer::TraitId(it) => it.child_by_source(db), | ||
268 | ChildContainer::ImplId(it) => it.child_by_source(db), | ||
269 | ChildContainer::EnumId(it) => it.child_by_source(db), | ||
270 | ChildContainer::VariantId(it) => it.child_by_source(db), | ||
271 | ChildContainer::TypeAliasId(_) => DynMap::default(), | ||
272 | ChildContainer::GenericDefId(it) => it.child_by_source(db), | ||
273 | } | ||
274 | } | ||
275 | } | ||
diff --git a/crates/hir/src/source_analyzer.rs b/crates/hir/src/source_analyzer.rs new file mode 100644 index 000000000..1d13c4f1d --- /dev/null +++ b/crates/hir/src/source_analyzer.rs | |||
@@ -0,0 +1,534 @@ | |||
1 | //! Lookup hir elements using positions in the source code. This is a lossy | ||
2 | //! transformation: in general, a single source might correspond to several | ||
3 | //! modules, functions, etc, due to macros, cfgs and `#[path=]` attributes on | ||
4 | //! modules. | ||
5 | //! | ||
6 | //! So, this modules should not be used during hir construction, it exists | ||
7 | //! purely for "IDE needs". | ||
8 | use std::{iter::once, sync::Arc}; | ||
9 | |||
10 | use hir_def::{ | ||
11 | body::{ | ||
12 | scope::{ExprScopes, ScopeId}, | ||
13 | Body, BodySourceMap, | ||
14 | }, | ||
15 | expr::{ExprId, Pat, PatId}, | ||
16 | path::{ModPath, Path, PathKind}, | ||
17 | resolver::{resolver_for_scope, Resolver, TypeNs, ValueNs}, | ||
18 | AsMacroCall, DefWithBodyId, FieldId, FunctionId, LocalFieldId, VariantId, | ||
19 | }; | ||
20 | use hir_expand::{hygiene::Hygiene, name::AsName, HirFileId, InFile}; | ||
21 | use hir_ty::{ | ||
22 | diagnostics::{record_literal_missing_fields, record_pattern_missing_fields}, | ||
23 | InferenceResult, Substs, Ty, | ||
24 | }; | ||
25 | use syntax::{ | ||
26 | ast::{self, AstNode}, | ||
27 | SyntaxNode, TextRange, TextSize, | ||
28 | }; | ||
29 | |||
30 | use crate::{ | ||
31 | db::HirDatabase, semantics::PathResolution, Adt, Const, EnumVariant, Field, Function, Local, | ||
32 | MacroDef, ModuleDef, Static, Struct, Trait, Type, TypeAlias, TypeParam, | ||
33 | }; | ||
34 | use base_db::CrateId; | ||
35 | |||
36 | /// `SourceAnalyzer` is a convenience wrapper which exposes HIR API in terms of | ||
37 | /// original source files. It should not be used inside the HIR itself. | ||
38 | #[derive(Debug)] | ||
39 | pub(crate) struct SourceAnalyzer { | ||
40 | file_id: HirFileId, | ||
41 | pub(crate) resolver: Resolver, | ||
42 | body: Option<Arc<Body>>, | ||
43 | body_source_map: Option<Arc<BodySourceMap>>, | ||
44 | infer: Option<Arc<InferenceResult>>, | ||
45 | scopes: Option<Arc<ExprScopes>>, | ||
46 | } | ||
47 | |||
48 | impl SourceAnalyzer { | ||
49 | pub(crate) fn new_for_body( | ||
50 | db: &dyn HirDatabase, | ||
51 | def: DefWithBodyId, | ||
52 | node: InFile<&SyntaxNode>, | ||
53 | offset: Option<TextSize>, | ||
54 | ) -> SourceAnalyzer { | ||
55 | let (body, source_map) = db.body_with_source_map(def); | ||
56 | let scopes = db.expr_scopes(def); | ||
57 | let scope = match offset { | ||
58 | None => scope_for(&scopes, &source_map, node), | ||
59 | Some(offset) => scope_for_offset(db, &scopes, &source_map, node.with_value(offset)), | ||
60 | }; | ||
61 | let resolver = resolver_for_scope(db.upcast(), def, scope); | ||
62 | SourceAnalyzer { | ||
63 | resolver, | ||
64 | body: Some(body), | ||
65 | body_source_map: Some(source_map), | ||
66 | infer: Some(db.infer(def)), | ||
67 | scopes: Some(scopes), | ||
68 | file_id: node.file_id, | ||
69 | } | ||
70 | } | ||
71 | |||
72 | pub(crate) fn new_for_resolver( | ||
73 | resolver: Resolver, | ||
74 | node: InFile<&SyntaxNode>, | ||
75 | ) -> SourceAnalyzer { | ||
76 | SourceAnalyzer { | ||
77 | resolver, | ||
78 | body: None, | ||
79 | body_source_map: None, | ||
80 | infer: None, | ||
81 | scopes: None, | ||
82 | file_id: node.file_id, | ||
83 | } | ||
84 | } | ||
85 | |||
86 | fn expr_id(&self, db: &dyn HirDatabase, expr: &ast::Expr) -> Option<ExprId> { | ||
87 | let src = match expr { | ||
88 | ast::Expr::MacroCall(call) => { | ||
89 | self.expand_expr(db, InFile::new(self.file_id, call.clone()))? | ||
90 | } | ||
91 | _ => InFile::new(self.file_id, expr.clone()), | ||
92 | }; | ||
93 | let sm = self.body_source_map.as_ref()?; | ||
94 | sm.node_expr(src.as_ref()) | ||
95 | } | ||
96 | |||
97 | fn pat_id(&self, pat: &ast::Pat) -> Option<PatId> { | ||
98 | // FIXME: macros, see `expr_id` | ||
99 | let src = InFile { file_id: self.file_id, value: pat }; | ||
100 | self.body_source_map.as_ref()?.node_pat(src) | ||
101 | } | ||
102 | |||
103 | fn expand_expr( | ||
104 | &self, | ||
105 | db: &dyn HirDatabase, | ||
106 | expr: InFile<ast::MacroCall>, | ||
107 | ) -> Option<InFile<ast::Expr>> { | ||
108 | let macro_file = self.body_source_map.as_ref()?.node_macro_file(expr.as_ref())?; | ||
109 | let expanded = db.parse_or_expand(macro_file)?; | ||
110 | |||
111 | let res = match ast::MacroCall::cast(expanded.clone()) { | ||
112 | Some(call) => self.expand_expr(db, InFile::new(macro_file, call))?, | ||
113 | _ => InFile::new(macro_file, ast::Expr::cast(expanded)?), | ||
114 | }; | ||
115 | Some(res) | ||
116 | } | ||
117 | |||
118 | pub(crate) fn type_of_expr(&self, db: &dyn HirDatabase, expr: &ast::Expr) -> Option<Type> { | ||
119 | let expr_id = self.expr_id(db, expr)?; | ||
120 | let ty = self.infer.as_ref()?[expr_id].clone(); | ||
121 | Type::new_with_resolver(db, &self.resolver, ty) | ||
122 | } | ||
123 | |||
124 | pub(crate) fn type_of_pat(&self, db: &dyn HirDatabase, pat: &ast::Pat) -> Option<Type> { | ||
125 | let pat_id = self.pat_id(pat)?; | ||
126 | let ty = self.infer.as_ref()?[pat_id].clone(); | ||
127 | Type::new_with_resolver(db, &self.resolver, ty) | ||
128 | } | ||
129 | |||
130 | pub(crate) fn type_of_self( | ||
131 | &self, | ||
132 | db: &dyn HirDatabase, | ||
133 | param: &ast::SelfParam, | ||
134 | ) -> Option<Type> { | ||
135 | let src = InFile { file_id: self.file_id, value: param }; | ||
136 | let pat_id = self.body_source_map.as_ref()?.node_self_param(src)?; | ||
137 | let ty = self.infer.as_ref()?[pat_id].clone(); | ||
138 | Type::new_with_resolver(db, &self.resolver, ty) | ||
139 | } | ||
140 | |||
141 | pub(crate) fn resolve_method_call( | ||
142 | &self, | ||
143 | db: &dyn HirDatabase, | ||
144 | call: &ast::MethodCallExpr, | ||
145 | ) -> Option<FunctionId> { | ||
146 | let expr_id = self.expr_id(db, &call.clone().into())?; | ||
147 | self.infer.as_ref()?.method_resolution(expr_id) | ||
148 | } | ||
149 | |||
150 | pub(crate) fn resolve_field( | ||
151 | &self, | ||
152 | db: &dyn HirDatabase, | ||
153 | field: &ast::FieldExpr, | ||
154 | ) -> Option<Field> { | ||
155 | let expr_id = self.expr_id(db, &field.clone().into())?; | ||
156 | self.infer.as_ref()?.field_resolution(expr_id).map(|it| it.into()) | ||
157 | } | ||
158 | |||
159 | pub(crate) fn resolve_record_field( | ||
160 | &self, | ||
161 | db: &dyn HirDatabase, | ||
162 | field: &ast::RecordExprField, | ||
163 | ) -> Option<(Field, Option<Local>)> { | ||
164 | let expr = field.expr()?; | ||
165 | let expr_id = self.expr_id(db, &expr)?; | ||
166 | let local = if field.name_ref().is_some() { | ||
167 | None | ||
168 | } else { | ||
169 | let local_name = field.field_name()?.as_name(); | ||
170 | let path = ModPath::from_segments(PathKind::Plain, once(local_name)); | ||
171 | match self.resolver.resolve_path_in_value_ns_fully(db.upcast(), &path) { | ||
172 | Some(ValueNs::LocalBinding(pat_id)) => { | ||
173 | Some(Local { pat_id, parent: self.resolver.body_owner()? }) | ||
174 | } | ||
175 | _ => None, | ||
176 | } | ||
177 | }; | ||
178 | let struct_field = self.infer.as_ref()?.record_field_resolution(expr_id)?; | ||
179 | Some((struct_field.into(), local)) | ||
180 | } | ||
181 | |||
182 | pub(crate) fn resolve_record_field_pat( | ||
183 | &self, | ||
184 | _db: &dyn HirDatabase, | ||
185 | field: &ast::RecordPatField, | ||
186 | ) -> Option<Field> { | ||
187 | let pat_id = self.pat_id(&field.pat()?)?; | ||
188 | let struct_field = self.infer.as_ref()?.record_field_pat_resolution(pat_id)?; | ||
189 | Some(struct_field.into()) | ||
190 | } | ||
191 | |||
192 | pub(crate) fn resolve_macro_call( | ||
193 | &self, | ||
194 | db: &dyn HirDatabase, | ||
195 | macro_call: InFile<&ast::MacroCall>, | ||
196 | ) -> Option<MacroDef> { | ||
197 | let hygiene = Hygiene::new(db.upcast(), macro_call.file_id); | ||
198 | let path = macro_call.value.path().and_then(|ast| Path::from_src(ast, &hygiene))?; | ||
199 | self.resolver.resolve_path_as_macro(db.upcast(), path.mod_path()).map(|it| it.into()) | ||
200 | } | ||
201 | |||
202 | pub(crate) fn resolve_bind_pat_to_const( | ||
203 | &self, | ||
204 | db: &dyn HirDatabase, | ||
205 | pat: &ast::IdentPat, | ||
206 | ) -> Option<ModuleDef> { | ||
207 | let pat_id = self.pat_id(&pat.clone().into())?; | ||
208 | let body = self.body.as_ref()?; | ||
209 | let path = match &body[pat_id] { | ||
210 | Pat::Path(path) => path, | ||
211 | _ => return None, | ||
212 | }; | ||
213 | let res = resolve_hir_path(db, &self.resolver, &path)?; | ||
214 | match res { | ||
215 | PathResolution::Def(def) => Some(def), | ||
216 | _ => None, | ||
217 | } | ||
218 | } | ||
219 | |||
220 | pub(crate) fn resolve_path( | ||
221 | &self, | ||
222 | db: &dyn HirDatabase, | ||
223 | path: &ast::Path, | ||
224 | ) -> Option<PathResolution> { | ||
225 | if let Some(path_expr) = path.syntax().parent().and_then(ast::PathExpr::cast) { | ||
226 | let expr_id = self.expr_id(db, &path_expr.into())?; | ||
227 | if let Some(assoc) = self.infer.as_ref()?.assoc_resolutions_for_expr(expr_id) { | ||
228 | return Some(PathResolution::AssocItem(assoc.into())); | ||
229 | } | ||
230 | if let Some(VariantId::EnumVariantId(variant)) = | ||
231 | self.infer.as_ref()?.variant_resolution_for_expr(expr_id) | ||
232 | { | ||
233 | return Some(PathResolution::Def(ModuleDef::EnumVariant(variant.into()))); | ||
234 | } | ||
235 | } | ||
236 | |||
237 | if let Some(path_pat) = path.syntax().parent().and_then(ast::PathPat::cast) { | ||
238 | let pat_id = self.pat_id(&path_pat.into())?; | ||
239 | if let Some(assoc) = self.infer.as_ref()?.assoc_resolutions_for_pat(pat_id) { | ||
240 | return Some(PathResolution::AssocItem(assoc.into())); | ||
241 | } | ||
242 | if let Some(VariantId::EnumVariantId(variant)) = | ||
243 | self.infer.as_ref()?.variant_resolution_for_pat(pat_id) | ||
244 | { | ||
245 | return Some(PathResolution::Def(ModuleDef::EnumVariant(variant.into()))); | ||
246 | } | ||
247 | } | ||
248 | |||
249 | if let Some(rec_lit) = path.syntax().parent().and_then(ast::RecordExpr::cast) { | ||
250 | let expr_id = self.expr_id(db, &rec_lit.into())?; | ||
251 | if let Some(VariantId::EnumVariantId(variant)) = | ||
252 | self.infer.as_ref()?.variant_resolution_for_expr(expr_id) | ||
253 | { | ||
254 | return Some(PathResolution::Def(ModuleDef::EnumVariant(variant.into()))); | ||
255 | } | ||
256 | } | ||
257 | |||
258 | if let Some(rec_pat) = path.syntax().parent().and_then(ast::RecordPat::cast) { | ||
259 | let pat_id = self.pat_id(&rec_pat.into())?; | ||
260 | if let Some(VariantId::EnumVariantId(variant)) = | ||
261 | self.infer.as_ref()?.variant_resolution_for_pat(pat_id) | ||
262 | { | ||
263 | return Some(PathResolution::Def(ModuleDef::EnumVariant(variant.into()))); | ||
264 | } | ||
265 | } | ||
266 | |||
267 | // This must be a normal source file rather than macro file. | ||
268 | let hir_path = Path::from_src(path.clone(), &Hygiene::new(db.upcast(), self.file_id))?; | ||
269 | |||
270 | // Case where path is a qualifier of another path, e.g. foo::bar::Baz where we | ||
271 | // trying to resolve foo::bar. | ||
272 | if let Some(outer_path) = path.syntax().parent().and_then(ast::Path::cast) { | ||
273 | if let Some(qualifier) = outer_path.qualifier() { | ||
274 | if path == &qualifier { | ||
275 | return resolve_hir_path_qualifier(db, &self.resolver, &hir_path); | ||
276 | } | ||
277 | } | ||
278 | } | ||
279 | |||
280 | resolve_hir_path(db, &self.resolver, &hir_path) | ||
281 | } | ||
282 | |||
283 | pub(crate) fn record_literal_missing_fields( | ||
284 | &self, | ||
285 | db: &dyn HirDatabase, | ||
286 | literal: &ast::RecordExpr, | ||
287 | ) -> Option<Vec<(Field, Type)>> { | ||
288 | let krate = self.resolver.krate()?; | ||
289 | let body = self.body.as_ref()?; | ||
290 | let infer = self.infer.as_ref()?; | ||
291 | |||
292 | let expr_id = self.expr_id(db, &literal.clone().into())?; | ||
293 | let substs = match &infer.type_of_expr[expr_id] { | ||
294 | Ty::Apply(a_ty) => &a_ty.parameters, | ||
295 | _ => return None, | ||
296 | }; | ||
297 | |||
298 | let (variant, missing_fields, _exhaustive) = | ||
299 | record_literal_missing_fields(db, infer, expr_id, &body[expr_id])?; | ||
300 | let res = self.missing_fields(db, krate, substs, variant, missing_fields); | ||
301 | Some(res) | ||
302 | } | ||
303 | |||
304 | pub(crate) fn record_pattern_missing_fields( | ||
305 | &self, | ||
306 | db: &dyn HirDatabase, | ||
307 | pattern: &ast::RecordPat, | ||
308 | ) -> Option<Vec<(Field, Type)>> { | ||
309 | let krate = self.resolver.krate()?; | ||
310 | let body = self.body.as_ref()?; | ||
311 | let infer = self.infer.as_ref()?; | ||
312 | |||
313 | let pat_id = self.pat_id(&pattern.clone().into())?; | ||
314 | let substs = match &infer.type_of_pat[pat_id] { | ||
315 | Ty::Apply(a_ty) => &a_ty.parameters, | ||
316 | _ => return None, | ||
317 | }; | ||
318 | |||
319 | let (variant, missing_fields, _exhaustive) = | ||
320 | record_pattern_missing_fields(db, infer, pat_id, &body[pat_id])?; | ||
321 | let res = self.missing_fields(db, krate, substs, variant, missing_fields); | ||
322 | Some(res) | ||
323 | } | ||
324 | |||
325 | fn missing_fields( | ||
326 | &self, | ||
327 | db: &dyn HirDatabase, | ||
328 | krate: CrateId, | ||
329 | substs: &Substs, | ||
330 | variant: VariantId, | ||
331 | missing_fields: Vec<LocalFieldId>, | ||
332 | ) -> Vec<(Field, Type)> { | ||
333 | let field_types = db.field_types(variant); | ||
334 | |||
335 | missing_fields | ||
336 | .into_iter() | ||
337 | .map(|local_id| { | ||
338 | let field = FieldId { parent: variant, local_id }; | ||
339 | let ty = field_types[local_id].clone().subst(substs); | ||
340 | (field.into(), Type::new_with_resolver_inner(db, krate, &self.resolver, ty)) | ||
341 | }) | ||
342 | .collect() | ||
343 | } | ||
344 | |||
345 | pub(crate) fn expand( | ||
346 | &self, | ||
347 | db: &dyn HirDatabase, | ||
348 | macro_call: InFile<&ast::MacroCall>, | ||
349 | ) -> Option<HirFileId> { | ||
350 | let krate = self.resolver.krate()?; | ||
351 | let macro_call_id = macro_call.as_call_id(db.upcast(), krate, |path| { | ||
352 | self.resolver.resolve_path_as_macro(db.upcast(), &path) | ||
353 | })?; | ||
354 | Some(macro_call_id.as_file()).filter(|it| it.expansion_level(db.upcast()) < 64) | ||
355 | } | ||
356 | |||
357 | pub(crate) fn resolve_variant( | ||
358 | &self, | ||
359 | db: &dyn HirDatabase, | ||
360 | record_lit: ast::RecordExpr, | ||
361 | ) -> Option<VariantId> { | ||
362 | let infer = self.infer.as_ref()?; | ||
363 | let expr_id = self.expr_id(db, &record_lit.into())?; | ||
364 | infer.variant_resolution_for_expr(expr_id) | ||
365 | } | ||
366 | } | ||
367 | |||
368 | fn scope_for( | ||
369 | scopes: &ExprScopes, | ||
370 | source_map: &BodySourceMap, | ||
371 | node: InFile<&SyntaxNode>, | ||
372 | ) -> Option<ScopeId> { | ||
373 | node.value | ||
374 | .ancestors() | ||
375 | .filter_map(ast::Expr::cast) | ||
376 | .filter_map(|it| source_map.node_expr(InFile::new(node.file_id, &it))) | ||
377 | .find_map(|it| scopes.scope_for(it)) | ||
378 | } | ||
379 | |||
380 | fn scope_for_offset( | ||
381 | db: &dyn HirDatabase, | ||
382 | scopes: &ExprScopes, | ||
383 | source_map: &BodySourceMap, | ||
384 | offset: InFile<TextSize>, | ||
385 | ) -> Option<ScopeId> { | ||
386 | scopes | ||
387 | .scope_by_expr() | ||
388 | .iter() | ||
389 | .filter_map(|(id, scope)| { | ||
390 | let source = source_map.expr_syntax(*id).ok()?; | ||
391 | // FIXME: correctly handle macro expansion | ||
392 | if source.file_id != offset.file_id { | ||
393 | return None; | ||
394 | } | ||
395 | let root = source.file_syntax(db.upcast()); | ||
396 | let node = source.value.to_node(&root); | ||
397 | Some((node.syntax().text_range(), scope)) | ||
398 | }) | ||
399 | // find containing scope | ||
400 | .min_by_key(|(expr_range, _scope)| { | ||
401 | ( | ||
402 | !(expr_range.start() <= offset.value && offset.value <= expr_range.end()), | ||
403 | expr_range.len(), | ||
404 | ) | ||
405 | }) | ||
406 | .map(|(expr_range, scope)| { | ||
407 | adjust(db, scopes, source_map, expr_range, offset).unwrap_or(*scope) | ||
408 | }) | ||
409 | } | ||
410 | |||
411 | // XXX: during completion, cursor might be outside of any particular | ||
412 | // expression. Try to figure out the correct scope... | ||
413 | fn adjust( | ||
414 | db: &dyn HirDatabase, | ||
415 | scopes: &ExprScopes, | ||
416 | source_map: &BodySourceMap, | ||
417 | expr_range: TextRange, | ||
418 | offset: InFile<TextSize>, | ||
419 | ) -> Option<ScopeId> { | ||
420 | let child_scopes = scopes | ||
421 | .scope_by_expr() | ||
422 | .iter() | ||
423 | .filter_map(|(id, scope)| { | ||
424 | let source = source_map.expr_syntax(*id).ok()?; | ||
425 | // FIXME: correctly handle macro expansion | ||
426 | if source.file_id != offset.file_id { | ||
427 | return None; | ||
428 | } | ||
429 | let root = source.file_syntax(db.upcast()); | ||
430 | let node = source.value.to_node(&root); | ||
431 | Some((node.syntax().text_range(), scope)) | ||
432 | }) | ||
433 | .filter(|&(range, _)| { | ||
434 | range.start() <= offset.value && expr_range.contains_range(range) && range != expr_range | ||
435 | }); | ||
436 | |||
437 | child_scopes | ||
438 | .max_by(|&(r1, _), &(r2, _)| { | ||
439 | if r1.contains_range(r2) { | ||
440 | std::cmp::Ordering::Greater | ||
441 | } else if r2.contains_range(r1) { | ||
442 | std::cmp::Ordering::Less | ||
443 | } else { | ||
444 | r1.start().cmp(&r2.start()) | ||
445 | } | ||
446 | }) | ||
447 | .map(|(_ptr, scope)| *scope) | ||
448 | } | ||
449 | |||
450 | pub(crate) fn resolve_hir_path( | ||
451 | db: &dyn HirDatabase, | ||
452 | resolver: &Resolver, | ||
453 | path: &Path, | ||
454 | ) -> Option<PathResolution> { | ||
455 | let types = | ||
456 | resolver.resolve_path_in_type_ns_fully(db.upcast(), path.mod_path()).map(|ty| match ty { | ||
457 | TypeNs::SelfType(it) => PathResolution::SelfType(it.into()), | ||
458 | TypeNs::GenericParam(id) => PathResolution::TypeParam(TypeParam { id }), | ||
459 | TypeNs::AdtSelfType(it) | TypeNs::AdtId(it) => { | ||
460 | PathResolution::Def(Adt::from(it).into()) | ||
461 | } | ||
462 | TypeNs::EnumVariantId(it) => PathResolution::Def(EnumVariant::from(it).into()), | ||
463 | TypeNs::TypeAliasId(it) => PathResolution::Def(TypeAlias::from(it).into()), | ||
464 | TypeNs::BuiltinType(it) => PathResolution::Def(it.into()), | ||
465 | TypeNs::TraitId(it) => PathResolution::Def(Trait::from(it).into()), | ||
466 | }); | ||
467 | |||
468 | let body_owner = resolver.body_owner(); | ||
469 | let values = | ||
470 | resolver.resolve_path_in_value_ns_fully(db.upcast(), path.mod_path()).and_then(|val| { | ||
471 | let res = match val { | ||
472 | ValueNs::LocalBinding(pat_id) => { | ||
473 | let var = Local { parent: body_owner?.into(), pat_id }; | ||
474 | PathResolution::Local(var) | ||
475 | } | ||
476 | ValueNs::FunctionId(it) => PathResolution::Def(Function::from(it).into()), | ||
477 | ValueNs::ConstId(it) => PathResolution::Def(Const::from(it).into()), | ||
478 | ValueNs::StaticId(it) => PathResolution::Def(Static::from(it).into()), | ||
479 | ValueNs::StructId(it) => PathResolution::Def(Struct::from(it).into()), | ||
480 | ValueNs::EnumVariantId(it) => PathResolution::Def(EnumVariant::from(it).into()), | ||
481 | ValueNs::ImplSelf(impl_id) => PathResolution::SelfType(impl_id.into()), | ||
482 | }; | ||
483 | Some(res) | ||
484 | }); | ||
485 | |||
486 | let items = resolver | ||
487 | .resolve_module_path_in_items(db.upcast(), path.mod_path()) | ||
488 | .take_types() | ||
489 | .map(|it| PathResolution::Def(it.into())); | ||
490 | |||
491 | types.or(values).or(items).or_else(|| { | ||
492 | resolver | ||
493 | .resolve_path_as_macro(db.upcast(), path.mod_path()) | ||
494 | .map(|def| PathResolution::Macro(def.into())) | ||
495 | }) | ||
496 | } | ||
497 | |||
498 | /// Resolves a path where we know it is a qualifier of another path. | ||
499 | /// | ||
500 | /// For example, if we have: | ||
501 | /// ``` | ||
502 | /// mod my { | ||
503 | /// pub mod foo { | ||
504 | /// struct Bar; | ||
505 | /// } | ||
506 | /// | ||
507 | /// pub fn foo() {} | ||
508 | /// } | ||
509 | /// ``` | ||
510 | /// then we know that `foo` in `my::foo::Bar` refers to the module, not the function. | ||
511 | fn resolve_hir_path_qualifier( | ||
512 | db: &dyn HirDatabase, | ||
513 | resolver: &Resolver, | ||
514 | path: &Path, | ||
515 | ) -> Option<PathResolution> { | ||
516 | let items = resolver | ||
517 | .resolve_module_path_in_items(db.upcast(), path.mod_path()) | ||
518 | .take_types() | ||
519 | .map(|it| PathResolution::Def(it.into())); | ||
520 | |||
521 | if items.is_some() { | ||
522 | return items; | ||
523 | } | ||
524 | |||
525 | resolver.resolve_path_in_type_ns_fully(db.upcast(), path.mod_path()).map(|ty| match ty { | ||
526 | TypeNs::SelfType(it) => PathResolution::SelfType(it.into()), | ||
527 | TypeNs::GenericParam(id) => PathResolution::TypeParam(TypeParam { id }), | ||
528 | TypeNs::AdtSelfType(it) | TypeNs::AdtId(it) => PathResolution::Def(Adt::from(it).into()), | ||
529 | TypeNs::EnumVariantId(it) => PathResolution::Def(EnumVariant::from(it).into()), | ||
530 | TypeNs::TypeAliasId(it) => PathResolution::Def(TypeAlias::from(it).into()), | ||
531 | TypeNs::BuiltinType(it) => PathResolution::Def(it.into()), | ||
532 | TypeNs::TraitId(it) => PathResolution::Def(Trait::from(it).into()), | ||
533 | }) | ||
534 | } | ||