diff options
Diffstat (limited to 'crates/ra_hir/src/source_binder.rs')
-rw-r--r-- | crates/ra_hir/src/source_binder.rs | 828 |
1 files changed, 308 insertions, 520 deletions
diff --git a/crates/ra_hir/src/source_binder.rs b/crates/ra_hir/src/source_binder.rs index a2a9d968c..f3150f578 100644 --- a/crates/ra_hir/src/source_binder.rs +++ b/crates/ra_hir/src/source_binder.rs | |||
@@ -1,569 +1,357 @@ | |||
1 | //! Lookup hir elements using positions in the source code. This is a lossy | 1 | //! `SourceBinder` is the main entry point for getting info about source code. |
2 | //! transformation: in general, a single source might correspond to several | 2 | //! It's main task is to map source syntax trees to hir-level IDs. |
3 | //! modules, functions, etc, due to macros, cfgs and `#[path=]` attributes on | 3 | |
4 | //! modules. | ||
5 | //! | ||
6 | //! So, this modules should not be used during hir construction, it exists | ||
7 | //! purely for "IDE needs". | ||
8 | use std::sync::Arc; | ||
9 | |||
10 | use either::Either; | ||
11 | use hir_def::{ | 4 | use hir_def::{ |
12 | body::{ | 5 | child_by_source::ChildBySource, |
13 | scope::{ExprScopes, ScopeId}, | 6 | dyn_map::DynMap, |
14 | BodySourceMap, | 7 | keys::{self, Key}, |
15 | }, | 8 | resolver::{HasResolver, Resolver}, |
16 | expr::{ExprId, PatId}, | 9 | ConstId, DefWithBodyId, EnumId, EnumVariantId, FunctionId, GenericDefId, ImplId, ModuleId, |
17 | nameres::ModuleSource, | 10 | StaticId, StructFieldId, StructId, TraitId, TypeAliasId, UnionId, VariantId, |
18 | path::path, | ||
19 | resolver::{self, resolver_for_scope, HasResolver, Resolver, TypeNs, ValueNs}, | ||
20 | AssocItemId, DefWithBodyId, | ||
21 | }; | ||
22 | use hir_expand::{ | ||
23 | hygiene::Hygiene, name::AsName, AstId, HirFileId, InFile, MacroCallId, MacroCallKind, | ||
24 | }; | ||
25 | use hir_ty::{ | ||
26 | method_resolution::{self, implements_trait}, | ||
27 | Canonical, InEnvironment, InferenceResult, TraitEnvironment, Ty, | ||
28 | }; | 11 | }; |
12 | use hir_expand::{name::AsName, AstId, InFile, MacroDefId, MacroDefKind}; | ||
29 | use ra_prof::profile; | 13 | use ra_prof::profile; |
30 | use ra_syntax::{ | 14 | use ra_syntax::{ |
31 | ast::{self, AstNode}, | 15 | ast::{self, NameOwner}, |
32 | match_ast, AstPtr, | 16 | match_ast, AstNode, SyntaxNode, TextUnit, |
33 | SyntaxKind::*, | ||
34 | SyntaxNode, SyntaxNodePtr, SyntaxToken, TextRange, TextUnit, | ||
35 | }; | 17 | }; |
18 | use rustc_hash::FxHashMap; | ||
36 | 19 | ||
37 | use crate::{ | 20 | use crate::{db::HirDatabase, Local, Module, SourceAnalyzer, TypeParam}; |
38 | db::HirDatabase, Adt, AssocItem, Const, DefWithBody, Enum, EnumVariant, FromSource, Function, | 21 | use ra_db::FileId; |
39 | ImplBlock, Local, MacroDef, Name, Path, ScopeDef, Static, Struct, Trait, Type, TypeAlias, | ||
40 | TypeParam, | ||
41 | }; | ||
42 | |||
43 | fn try_get_resolver_for_node(db: &impl HirDatabase, node: InFile<&SyntaxNode>) -> Option<Resolver> { | ||
44 | match_ast! { | ||
45 | match (node.value) { | ||
46 | ast::Module(it) => { | ||
47 | let src = node.with_value(it); | ||
48 | Some(crate::Module::from_declaration(db, src)?.id.resolver(db)) | ||
49 | }, | ||
50 | ast::SourceFile(it) => { | ||
51 | let src = node.with_value(ModuleSource::SourceFile(it)); | ||
52 | Some(crate::Module::from_definition(db, src)?.id.resolver(db)) | ||
53 | }, | ||
54 | ast::StructDef(it) => { | ||
55 | let src = node.with_value(it); | ||
56 | Some(Struct::from_source(db, src)?.id.resolver(db)) | ||
57 | }, | ||
58 | ast::EnumDef(it) => { | ||
59 | let src = node.with_value(it); | ||
60 | Some(Enum::from_source(db, src)?.id.resolver(db)) | ||
61 | }, | ||
62 | ast::ImplBlock(it) => { | ||
63 | let src = node.with_value(it); | ||
64 | Some(ImplBlock::from_source(db, src)?.id.resolver(db)) | ||
65 | }, | ||
66 | ast::TraitDef(it) => { | ||
67 | let src = node.with_value(it); | ||
68 | Some(Trait::from_source(db, src)?.id.resolver(db)) | ||
69 | }, | ||
70 | _ => match node.value.kind() { | ||
71 | FN_DEF | CONST_DEF | STATIC_DEF => { | ||
72 | let def = def_with_body_from_child_node(db, node)?; | ||
73 | let def = DefWithBodyId::from(def); | ||
74 | Some(def.resolver(db)) | ||
75 | } | ||
76 | // FIXME add missing cases | ||
77 | _ => None | ||
78 | } | ||
79 | } | ||
80 | } | ||
81 | } | ||
82 | |||
83 | fn def_with_body_from_child_node( | ||
84 | db: &impl HirDatabase, | ||
85 | child: InFile<&SyntaxNode>, | ||
86 | ) -> Option<DefWithBody> { | ||
87 | let _p = profile("def_with_body_from_child_node"); | ||
88 | child.cloned().ancestors_with_macros(db).find_map(|node| { | ||
89 | let n = &node.value; | ||
90 | match_ast! { | ||
91 | match n { | ||
92 | ast::FnDef(def) => { return Function::from_source(db, node.with_value(def)).map(DefWithBody::from); }, | ||
93 | ast::ConstDef(def) => { return Const::from_source(db, node.with_value(def)).map(DefWithBody::from); }, | ||
94 | ast::StaticDef(def) => { return Static::from_source(db, node.with_value(def)).map(DefWithBody::from); }, | ||
95 | _ => { None }, | ||
96 | } | ||
97 | } | ||
98 | }) | ||
99 | } | ||
100 | |||
101 | /// `SourceAnalyzer` is a convenience wrapper which exposes HIR API in terms of | ||
102 | /// original source files. It should not be used inside the HIR itself. | ||
103 | #[derive(Debug)] | ||
104 | pub struct SourceAnalyzer { | ||
105 | file_id: HirFileId, | ||
106 | resolver: Resolver, | ||
107 | body_owner: Option<DefWithBody>, | ||
108 | body_source_map: Option<Arc<BodySourceMap>>, | ||
109 | infer: Option<Arc<InferenceResult>>, | ||
110 | scopes: Option<Arc<ExprScopes>>, | ||
111 | } | ||
112 | |||
113 | #[derive(Debug, Clone, PartialEq, Eq)] | ||
114 | pub enum PathResolution { | ||
115 | /// An item | ||
116 | Def(crate::ModuleDef), | ||
117 | /// A local binding (only value namespace) | ||
118 | Local(Local), | ||
119 | /// A generic parameter | ||
120 | TypeParam(TypeParam), | ||
121 | SelfType(crate::ImplBlock), | ||
122 | Macro(MacroDef), | ||
123 | AssocItem(crate::AssocItem), | ||
124 | } | ||
125 | |||
126 | #[derive(Debug, Clone, PartialEq, Eq)] | ||
127 | pub struct ScopeEntryWithSyntax { | ||
128 | pub(crate) name: Name, | ||
129 | pub(crate) ptr: Either<AstPtr<ast::Pat>, AstPtr<ast::SelfParam>>, | ||
130 | } | ||
131 | |||
132 | impl ScopeEntryWithSyntax { | ||
133 | pub fn name(&self) -> &Name { | ||
134 | &self.name | ||
135 | } | ||
136 | |||
137 | pub fn ptr(&self) -> Either<AstPtr<ast::Pat>, AstPtr<ast::SelfParam>> { | ||
138 | self.ptr | ||
139 | } | ||
140 | } | ||
141 | |||
142 | #[derive(Debug)] | ||
143 | pub struct ReferenceDescriptor { | ||
144 | pub range: TextRange, | ||
145 | pub name: String, | ||
146 | } | ||
147 | 22 | ||
148 | #[derive(Debug)] | 23 | pub struct SourceBinder<'a, DB> { |
149 | pub struct Expansion { | 24 | pub db: &'a DB, |
150 | macro_call_id: MacroCallId, | 25 | child_by_source_cache: FxHashMap<ChildContainer, DynMap>, |
151 | } | 26 | } |
152 | 27 | ||
153 | impl Expansion { | 28 | impl<DB: HirDatabase> SourceBinder<'_, DB> { |
154 | pub fn map_token_down( | 29 | pub fn new(db: &DB) -> SourceBinder<DB> { |
155 | &self, | 30 | SourceBinder { db, child_by_source_cache: FxHashMap::default() } |
156 | db: &impl HirDatabase, | ||
157 | token: InFile<&SyntaxToken>, | ||
158 | ) -> Option<InFile<SyntaxToken>> { | ||
159 | let exp_info = self.file_id().expansion_info(db)?; | ||
160 | exp_info.map_token_down(token) | ||
161 | } | ||
162 | |||
163 | pub fn file_id(&self) -> HirFileId { | ||
164 | self.macro_call_id.as_file() | ||
165 | } | 31 | } |
166 | } | ||
167 | 32 | ||
168 | impl SourceAnalyzer { | 33 | pub fn analyze( |
169 | pub fn new( | 34 | &mut self, |
170 | db: &impl HirDatabase, | 35 | src: InFile<&SyntaxNode>, |
171 | node: InFile<&SyntaxNode>, | ||
172 | offset: Option<TextUnit>, | 36 | offset: Option<TextUnit>, |
173 | ) -> SourceAnalyzer { | 37 | ) -> SourceAnalyzer { |
174 | let _p = profile("SourceAnalyzer::new"); | 38 | let _p = profile("SourceBinder::analyzer"); |
175 | let def_with_body = def_with_body_from_child_node(db, node); | 39 | let container = match self.find_container(src) { |
176 | if let Some(def) = def_with_body { | 40 | Some(it) => it, |
177 | let (_body, source_map) = db.body_with_source_map(def.into()); | 41 | None => return SourceAnalyzer::new_for_resolver(Resolver::default(), src), |
178 | let scopes = db.expr_scopes(def.into()); | 42 | }; |
179 | let scope = match offset { | 43 | |
180 | None => scope_for(&scopes, &source_map, node), | 44 | let resolver = match container { |
181 | Some(offset) => scope_for_offset(&scopes, &source_map, node.with_value(offset)), | 45 | ChildContainer::DefWithBodyId(def) => { |
182 | }; | 46 | return SourceAnalyzer::new_for_body(self.db, def, src, offset) |
183 | let resolver = resolver_for_scope(db, def.into(), scope); | ||
184 | SourceAnalyzer { | ||
185 | resolver, | ||
186 | body_owner: Some(def), | ||
187 | body_source_map: Some(source_map), | ||
188 | infer: Some(db.infer(def.into())), | ||
189 | scopes: Some(scopes), | ||
190 | file_id: node.file_id, | ||
191 | } | ||
192 | } else { | ||
193 | SourceAnalyzer { | ||
194 | resolver: node | ||
195 | .value | ||
196 | .ancestors() | ||
197 | .find_map(|it| try_get_resolver_for_node(db, node.with_value(&it))) | ||
198 | .unwrap_or_default(), | ||
199 | body_owner: None, | ||
200 | body_source_map: None, | ||
201 | infer: None, | ||
202 | scopes: None, | ||
203 | file_id: node.file_id, | ||
204 | } | 47 | } |
205 | } | 48 | ChildContainer::TraitId(it) => it.resolver(self.db), |
49 | ChildContainer::ImplId(it) => it.resolver(self.db), | ||
50 | ChildContainer::ModuleId(it) => it.resolver(self.db), | ||
51 | ChildContainer::EnumId(it) => it.resolver(self.db), | ||
52 | ChildContainer::VariantId(it) => it.resolver(self.db), | ||
53 | ChildContainer::GenericDefId(it) => it.resolver(self.db), | ||
54 | }; | ||
55 | SourceAnalyzer::new_for_resolver(resolver, src) | ||
206 | } | 56 | } |
207 | 57 | ||
208 | pub fn module(&self) -> Option<crate::code_model::Module> { | 58 | pub fn to_def<T: ToDef>(&mut self, src: InFile<T>) -> Option<T::Def> { |
209 | Some(crate::code_model::Module { id: self.resolver.module()? }) | 59 | T::to_def(self, src) |
210 | } | 60 | } |
211 | 61 | ||
212 | fn expr_id(&self, expr: &ast::Expr) -> Option<ExprId> { | 62 | pub fn to_module_def(&mut self, file: FileId) -> Option<Module> { |
213 | let src = InFile { file_id: self.file_id, value: expr }; | 63 | let _p = profile("SourceBinder::to_module_def"); |
214 | self.body_source_map.as_ref()?.node_expr(src) | 64 | let (krate, local_id) = self.db.relevant_crates(file).iter().find_map(|&crate_id| { |
65 | let crate_def_map = self.db.crate_def_map(crate_id); | ||
66 | let local_id = crate_def_map.modules_for_file(file).next()?; | ||
67 | Some((crate_id, local_id)) | ||
68 | })?; | ||
69 | Some(Module { id: ModuleId { krate, local_id } }) | ||
215 | } | 70 | } |
216 | 71 | ||
217 | fn pat_id(&self, pat: &ast::Pat) -> Option<PatId> { | 72 | fn to_id<T: ToId>(&mut self, src: InFile<T>) -> Option<T::ID> { |
218 | let src = InFile { file_id: self.file_id, value: pat }; | 73 | T::to_id(self, src) |
219 | self.body_source_map.as_ref()?.node_pat(src) | ||
220 | } | 74 | } |
221 | 75 | ||
222 | fn expand_expr( | 76 | fn find_container(&mut self, src: InFile<&SyntaxNode>) -> Option<ChildContainer> { |
223 | &self, | 77 | for container in src.cloned().ancestors_with_macros(self.db).skip(1) { |
224 | db: &impl HirDatabase, | 78 | let res: ChildContainer = match_ast! { |
225 | expr: InFile<&ast::Expr>, | 79 | match (container.value) { |
226 | ) -> Option<InFile<ast::Expr>> { | 80 | ast::TraitDef(it) => { |
227 | let macro_call = ast::MacroCall::cast(expr.value.syntax().clone())?; | 81 | let def: TraitId = self.to_id(container.with_value(it))?; |
228 | let macro_file = | 82 | def.into() |
229 | self.body_source_map.as_ref()?.node_macro_file(expr.with_value(¯o_call))?; | 83 | }, |
230 | let expanded = db.parse_or_expand(macro_file)?; | 84 | ast::ImplBlock(it) => { |
231 | let kind = expanded.kind(); | 85 | let def: ImplId = self.to_id(container.with_value(it))?; |
232 | let expr = InFile::new(macro_file, ast::Expr::cast(expanded)?); | 86 | def.into() |
233 | 87 | }, | |
234 | if ast::MacroCall::can_cast(kind) { | 88 | ast::FnDef(it) => { |
235 | self.expand_expr(db, expr.as_ref()) | 89 | let def: FunctionId = self.to_id(container.with_value(it))?; |
236 | } else { | 90 | DefWithBodyId::from(def).into() |
237 | Some(expr) | 91 | }, |
92 | ast::StaticDef(it) => { | ||
93 | let def: StaticId = self.to_id(container.with_value(it))?; | ||
94 | DefWithBodyId::from(def).into() | ||
95 | }, | ||
96 | ast::ConstDef(it) => { | ||
97 | let def: ConstId = self.to_id(container.with_value(it))?; | ||
98 | DefWithBodyId::from(def).into() | ||
99 | }, | ||
100 | ast::EnumDef(it) => { | ||
101 | let def: EnumId = self.to_id(container.with_value(it))?; | ||
102 | def.into() | ||
103 | }, | ||
104 | ast::StructDef(it) => { | ||
105 | let def: StructId = self.to_id(container.with_value(it))?; | ||
106 | VariantId::from(def).into() | ||
107 | }, | ||
108 | ast::UnionDef(it) => { | ||
109 | let def: UnionId = self.to_id(container.with_value(it))?; | ||
110 | VariantId::from(def).into() | ||
111 | }, | ||
112 | ast::Module(it) => { | ||
113 | let def: ModuleId = self.to_id(container.with_value(it))?; | ||
114 | def.into() | ||
115 | }, | ||
116 | _ => { continue }, | ||
117 | } | ||
118 | }; | ||
119 | return Some(res); | ||
238 | } | 120 | } |
239 | } | ||
240 | 121 | ||
241 | pub fn type_of(&self, db: &impl HirDatabase, expr: &ast::Expr) -> Option<Type> { | 122 | let c = self.to_module_def(src.file_id.original_file(self.db))?; |
242 | let expr_id = if let Some(expr) = self.expand_expr(db, InFile::new(self.file_id, expr)) { | 123 | Some(c.id.into()) |
243 | self.body_source_map.as_ref()?.node_expr(expr.as_ref())? | ||
244 | } else { | ||
245 | self.expr_id(expr)? | ||
246 | }; | ||
247 | |||
248 | let ty = self.infer.as_ref()?[expr_id].clone(); | ||
249 | let environment = TraitEnvironment::lower(db, &self.resolver); | ||
250 | Some(Type { krate: self.resolver.krate()?, ty: InEnvironment { value: ty, environment } }) | ||
251 | } | ||
252 | |||
253 | pub fn type_of_pat(&self, db: &impl HirDatabase, pat: &ast::Pat) -> Option<Type> { | ||
254 | let pat_id = self.pat_id(pat)?; | ||
255 | let ty = self.infer.as_ref()?[pat_id].clone(); | ||
256 | let environment = TraitEnvironment::lower(db, &self.resolver); | ||
257 | Some(Type { krate: self.resolver.krate()?, ty: InEnvironment { value: ty, environment } }) | ||
258 | } | 124 | } |
259 | 125 | ||
260 | pub fn resolve_method_call(&self, call: &ast::MethodCallExpr) -> Option<Function> { | 126 | fn child_by_source(&mut self, container: ChildContainer) -> &DynMap { |
261 | let expr_id = self.expr_id(&call.clone().into())?; | 127 | let db = self.db; |
262 | self.infer.as_ref()?.method_resolution(expr_id).map(Function::from) | 128 | self.child_by_source_cache.entry(container).or_insert_with(|| match container { |
129 | ChildContainer::DefWithBodyId(it) => it.child_by_source(db), | ||
130 | ChildContainer::ModuleId(it) => it.child_by_source(db), | ||
131 | ChildContainer::TraitId(it) => it.child_by_source(db), | ||
132 | ChildContainer::ImplId(it) => it.child_by_source(db), | ||
133 | ChildContainer::EnumId(it) => it.child_by_source(db), | ||
134 | ChildContainer::VariantId(it) => it.child_by_source(db), | ||
135 | ChildContainer::GenericDefId(it) => it.child_by_source(db), | ||
136 | }) | ||
263 | } | 137 | } |
138 | } | ||
264 | 139 | ||
265 | pub fn resolve_field(&self, field: &ast::FieldExpr) -> Option<crate::StructField> { | 140 | pub trait ToId: Sized { |
266 | let expr_id = self.expr_id(&field.clone().into())?; | 141 | type ID: Sized + Copy + 'static; |
267 | self.infer.as_ref()?.field_resolution(expr_id).map(|it| it.into()) | 142 | fn to_id<DB: HirDatabase>(sb: &mut SourceBinder<'_, DB>, src: InFile<Self>) |
268 | } | 143 | -> Option<Self::ID>; |
144 | } | ||
269 | 145 | ||
270 | pub fn resolve_record_field(&self, field: &ast::RecordField) -> Option<crate::StructField> { | 146 | pub trait ToDef: Sized + AstNode + 'static { |
271 | let expr_id = match field.expr() { | 147 | type Def; |
272 | Some(it) => self.expr_id(&it)?, | 148 | fn to_def<DB: HirDatabase>( |
273 | None => { | 149 | sb: &mut SourceBinder<'_, DB>, |
274 | let src = InFile { file_id: self.file_id, value: field }; | 150 | src: InFile<Self>, |
275 | self.body_source_map.as_ref()?.field_init_shorthand_expr(src)? | 151 | ) -> Option<Self::Def>; |
276 | } | 152 | } |
277 | }; | ||
278 | self.infer.as_ref()?.record_field_resolution(expr_id).map(|it| it.into()) | ||
279 | } | ||
280 | 153 | ||
281 | pub fn resolve_record_literal(&self, record_lit: &ast::RecordLit) -> Option<crate::VariantDef> { | 154 | macro_rules! to_def_impls { |
282 | let expr_id = self.expr_id(&record_lit.clone().into())?; | 155 | ($(($def:path, $ast:path)),* ,) => {$( |
283 | self.infer.as_ref()?.variant_resolution_for_expr(expr_id).map(|it| it.into()) | 156 | impl ToDef for $ast { |
284 | } | 157 | type Def = $def; |
158 | fn to_def<DB: HirDatabase>(sb: &mut SourceBinder<'_, DB>, src: InFile<Self>) | ||
159 | -> Option<Self::Def> | ||
160 | { sb.to_id(src).map(Into::into) } | ||
161 | } | ||
162 | )*} | ||
163 | } | ||
285 | 164 | ||
286 | pub fn resolve_record_pattern(&self, record_pat: &ast::RecordPat) -> Option<crate::VariantDef> { | 165 | to_def_impls![ |
287 | let pat_id = self.pat_id(&record_pat.clone().into())?; | 166 | (crate::Module, ast::Module), |
288 | self.infer.as_ref()?.variant_resolution_for_pat(pat_id).map(|it| it.into()) | 167 | (crate::Struct, ast::StructDef), |
289 | } | 168 | (crate::Enum, ast::EnumDef), |
169 | (crate::Union, ast::UnionDef), | ||
170 | (crate::Trait, ast::TraitDef), | ||
171 | (crate::ImplBlock, ast::ImplBlock), | ||
172 | (crate::TypeAlias, ast::TypeAliasDef), | ||
173 | (crate::Const, ast::ConstDef), | ||
174 | (crate::Static, ast::StaticDef), | ||
175 | (crate::Function, ast::FnDef), | ||
176 | (crate::StructField, ast::RecordFieldDef), | ||
177 | (crate::EnumVariant, ast::EnumVariant), | ||
178 | (crate::MacroDef, ast::MacroCall), // this one is dubious, not all calls are macros | ||
179 | ]; | ||
180 | |||
181 | #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)] | ||
182 | enum ChildContainer { | ||
183 | DefWithBodyId(DefWithBodyId), | ||
184 | ModuleId(ModuleId), | ||
185 | TraitId(TraitId), | ||
186 | ImplId(ImplId), | ||
187 | EnumId(EnumId), | ||
188 | VariantId(VariantId), | ||
189 | /// XXX: this might be the same def as, for example an `EnumId`. However, | ||
190 | /// here the children generic parameters, and not, eg enum variants. | ||
191 | GenericDefId(GenericDefId), | ||
192 | } | ||
193 | impl_froms! { | ||
194 | ChildContainer: | ||
195 | DefWithBodyId, | ||
196 | ModuleId, | ||
197 | TraitId, | ||
198 | ImplId, | ||
199 | EnumId, | ||
200 | VariantId, | ||
201 | GenericDefId | ||
202 | } | ||
290 | 203 | ||
291 | pub fn resolve_macro_call( | 204 | pub trait ToIdByKey: Sized + AstNode + 'static { |
292 | &self, | 205 | type ID: Sized + Copy + 'static; |
293 | db: &impl HirDatabase, | 206 | const KEY: Key<Self, Self::ID>; |
294 | macro_call: InFile<&ast::MacroCall>, | 207 | } |
295 | ) -> Option<MacroDef> { | ||
296 | let hygiene = Hygiene::new(db, macro_call.file_id); | ||
297 | let path = macro_call.value.path().and_then(|ast| Path::from_src(ast, &hygiene))?; | ||
298 | self.resolver.resolve_path_as_macro(db, path.mod_path()).map(|it| it.into()) | ||
299 | } | ||
300 | 208 | ||
301 | pub fn resolve_hir_path( | 209 | impl<T: ToIdByKey> ToId for T { |
302 | &self, | 210 | type ID = <T as ToIdByKey>::ID; |
303 | db: &impl HirDatabase, | 211 | fn to_id<DB: HirDatabase>( |
304 | path: &crate::Path, | 212 | sb: &mut SourceBinder<'_, DB>, |
305 | ) -> Option<PathResolution> { | 213 | src: InFile<Self>, |
306 | let types = | 214 | ) -> Option<Self::ID> { |
307 | self.resolver.resolve_path_in_type_ns_fully(db, path.mod_path()).map(|ty| match ty { | 215 | let container = sb.find_container(src.as_ref().map(|it| it.syntax()))?; |
308 | TypeNs::SelfType(it) => PathResolution::SelfType(it.into()), | 216 | let db = sb.db; |
309 | TypeNs::GenericParam(id) => PathResolution::TypeParam(TypeParam { id }), | 217 | let dyn_map = |
310 | TypeNs::AdtSelfType(it) | TypeNs::AdtId(it) => { | 218 | &*sb.child_by_source_cache.entry(container).or_insert_with(|| match container { |
311 | PathResolution::Def(Adt::from(it).into()) | 219 | ChildContainer::DefWithBodyId(it) => it.child_by_source(db), |
312 | } | 220 | ChildContainer::ModuleId(it) => it.child_by_source(db), |
313 | TypeNs::EnumVariantId(it) => PathResolution::Def(EnumVariant::from(it).into()), | 221 | ChildContainer::TraitId(it) => it.child_by_source(db), |
314 | TypeNs::TypeAliasId(it) => PathResolution::Def(TypeAlias::from(it).into()), | 222 | ChildContainer::ImplId(it) => it.child_by_source(db), |
315 | TypeNs::BuiltinType(it) => PathResolution::Def(it.into()), | 223 | ChildContainer::EnumId(it) => it.child_by_source(db), |
316 | TypeNs::TraitId(it) => PathResolution::Def(Trait::from(it).into()), | 224 | ChildContainer::VariantId(it) => it.child_by_source(db), |
317 | }); | 225 | ChildContainer::GenericDefId(it) => it.child_by_source(db), |
318 | let values = | ||
319 | self.resolver.resolve_path_in_value_ns_fully(db, path.mod_path()).and_then(|val| { | ||
320 | let res = match val { | ||
321 | ValueNs::LocalBinding(pat_id) => { | ||
322 | let var = Local { parent: self.body_owner?, pat_id }; | ||
323 | PathResolution::Local(var) | ||
324 | } | ||
325 | ValueNs::FunctionId(it) => PathResolution::Def(Function::from(it).into()), | ||
326 | ValueNs::ConstId(it) => PathResolution::Def(Const::from(it).into()), | ||
327 | ValueNs::StaticId(it) => PathResolution::Def(Static::from(it).into()), | ||
328 | ValueNs::StructId(it) => PathResolution::Def(Struct::from(it).into()), | ||
329 | ValueNs::EnumVariantId(it) => PathResolution::Def(EnumVariant::from(it).into()), | ||
330 | }; | ||
331 | Some(res) | ||
332 | }); | 226 | }); |
333 | 227 | dyn_map[T::KEY].get(&src).copied() | |
334 | let items = self | ||
335 | .resolver | ||
336 | .resolve_module_path_in_items(db, path.mod_path()) | ||
337 | .take_types() | ||
338 | .map(|it| PathResolution::Def(it.into())); | ||
339 | types.or(values).or(items).or_else(|| { | ||
340 | self.resolver | ||
341 | .resolve_path_as_macro(db, path.mod_path()) | ||
342 | .map(|def| PathResolution::Macro(def.into())) | ||
343 | }) | ||
344 | } | 228 | } |
229 | } | ||
345 | 230 | ||
346 | pub fn resolve_path(&self, db: &impl HirDatabase, path: &ast::Path) -> Option<PathResolution> { | 231 | macro_rules! to_id_key_impls { |
347 | if let Some(path_expr) = path.syntax().parent().and_then(ast::PathExpr::cast) { | 232 | ($(($id:ident, $ast:path, $key:path)),* ,) => {$( |
348 | let expr_id = self.expr_id(&path_expr.into())?; | 233 | impl ToIdByKey for $ast { |
349 | if let Some(assoc) = self.infer.as_ref()?.assoc_resolutions_for_expr(expr_id) { | 234 | type ID = $id; |
350 | return Some(PathResolution::AssocItem(assoc.into())); | 235 | const KEY: Key<Self, Self::ID> = $key; |
351 | } | ||
352 | } | 236 | } |
353 | if let Some(path_pat) = path.syntax().parent().and_then(ast::PathPat::cast) { | 237 | )*} |
354 | let pat_id = self.pat_id(&path_pat.into())?; | 238 | } |
355 | if let Some(assoc) = self.infer.as_ref()?.assoc_resolutions_for_pat(pat_id) { | ||
356 | return Some(PathResolution::AssocItem(assoc.into())); | ||
357 | } | ||
358 | } | ||
359 | // This must be a normal source file rather than macro file. | ||
360 | let hir_path = crate::Path::from_ast(path.clone())?; | ||
361 | self.resolve_hir_path(db, &hir_path) | ||
362 | } | ||
363 | 239 | ||
364 | fn resolve_local_name(&self, name_ref: &ast::NameRef) -> Option<ScopeEntryWithSyntax> { | 240 | to_id_key_impls![ |
365 | let name = name_ref.as_name(); | 241 | (StructId, ast::StructDef, keys::STRUCT), |
366 | let source_map = self.body_source_map.as_ref()?; | 242 | (UnionId, ast::UnionDef, keys::UNION), |
367 | let scopes = self.scopes.as_ref()?; | 243 | (EnumId, ast::EnumDef, keys::ENUM), |
368 | let scope = scope_for(scopes, source_map, InFile::new(self.file_id, name_ref.syntax()))?; | 244 | (TraitId, ast::TraitDef, keys::TRAIT), |
369 | let entry = scopes.resolve_name_in_scope(scope, &name)?; | 245 | (FunctionId, ast::FnDef, keys::FUNCTION), |
370 | Some(ScopeEntryWithSyntax { | 246 | (StaticId, ast::StaticDef, keys::STATIC), |
371 | name: entry.name().clone(), | 247 | (ConstId, ast::ConstDef, keys::CONST), |
372 | ptr: source_map.pat_syntax(entry.pat())?.value, | 248 | (TypeAliasId, ast::TypeAliasDef, keys::TYPE_ALIAS), |
373 | }) | 249 | (ImplId, ast::ImplBlock, keys::IMPL), |
250 | (StructFieldId, ast::RecordFieldDef, keys::RECORD_FIELD), | ||
251 | (EnumVariantId, ast::EnumVariant, keys::ENUM_VARIANT), | ||
252 | ]; | ||
253 | |||
254 | // FIXME: use DynMap as well? | ||
255 | impl ToId for ast::MacroCall { | ||
256 | type ID = MacroDefId; | ||
257 | fn to_id<DB: HirDatabase>( | ||
258 | sb: &mut SourceBinder<'_, DB>, | ||
259 | src: InFile<Self>, | ||
260 | ) -> Option<Self::ID> { | ||
261 | let kind = MacroDefKind::Declarative; | ||
262 | |||
263 | let krate = sb.to_module_def(src.file_id.original_file(sb.db))?.id.krate; | ||
264 | |||
265 | let ast_id = | ||
266 | Some(AstId::new(src.file_id, sb.db.ast_id_map(src.file_id).ast_id(&src.value))); | ||
267 | |||
268 | Some(MacroDefId { krate: Some(krate), ast_id, kind }) | ||
374 | } | 269 | } |
270 | } | ||
375 | 271 | ||
376 | pub fn process_all_names(&self, db: &impl HirDatabase, f: &mut dyn FnMut(Name, ScopeDef)) { | 272 | impl ToDef for ast::BindPat { |
377 | self.resolver.process_all_names(db, &mut |name, def| { | 273 | type Def = Local; |
378 | let def = match def { | 274 | |
379 | resolver::ScopeDef::PerNs(it) => it.into(), | 275 | fn to_def<DB: HirDatabase>(sb: &mut SourceBinder<'_, DB>, src: InFile<Self>) -> Option<Local> { |
380 | resolver::ScopeDef::ImplSelfType(it) => ScopeDef::ImplSelfType(it.into()), | 276 | let file_id = src.file_id; |
381 | resolver::ScopeDef::AdtSelfType(it) => ScopeDef::AdtSelfType(it.into()), | 277 | let parent: DefWithBodyId = src.value.syntax().ancestors().find_map(|it| { |
382 | resolver::ScopeDef::GenericParam(id) => ScopeDef::GenericParam(TypeParam { id }), | 278 | let res = match_ast! { |
383 | resolver::ScopeDef::Local(pat_id) => { | 279 | match it { |
384 | let parent = self.resolver.body_owner().unwrap().into(); | 280 | ast::ConstDef(value) => { sb.to_id(InFile { value, file_id})?.into() }, |
385 | ScopeDef::Local(Local { parent, pat_id }) | 281 | ast::StaticDef(value) => { sb.to_id(InFile { value, file_id})?.into() }, |
282 | ast::FnDef(value) => { sb.to_id(InFile { value, file_id})?.into() }, | ||
283 | _ => return None, | ||
386 | } | 284 | } |
387 | }; | 285 | }; |
388 | f(name, def) | 286 | Some(res) |
389 | }) | 287 | })?; |
390 | } | 288 | let (_body, source_map) = sb.db.body_with_source_map(parent); |
391 | 289 | let src = src.map(ast::Pat::from); | |
392 | // FIXME: we only use this in `inline_local_variable` assist, ideally, we | 290 | let pat_id = source_map.node_pat(src.as_ref())?; |
393 | // should switch to general reference search infra there. | 291 | Some(Local { parent: parent.into(), pat_id }) |
394 | pub fn find_all_refs(&self, pat: &ast::BindPat) -> Vec<ReferenceDescriptor> { | ||
395 | let fn_def = pat.syntax().ancestors().find_map(ast::FnDef::cast).unwrap(); | ||
396 | let ptr = Either::Left(AstPtr::new(&ast::Pat::from(pat.clone()))); | ||
397 | fn_def | ||
398 | .syntax() | ||
399 | .descendants() | ||
400 | .filter_map(ast::NameRef::cast) | ||
401 | .filter(|name_ref| match self.resolve_local_name(&name_ref) { | ||
402 | None => false, | ||
403 | Some(entry) => entry.ptr() == ptr, | ||
404 | }) | ||
405 | .map(|name_ref| ReferenceDescriptor { | ||
406 | name: name_ref.text().to_string(), | ||
407 | range: name_ref.syntax().text_range(), | ||
408 | }) | ||
409 | .collect() | ||
410 | } | ||
411 | |||
412 | pub fn iterate_method_candidates<T>( | ||
413 | &self, | ||
414 | db: &impl HirDatabase, | ||
415 | ty: &Type, | ||
416 | name: Option<&Name>, | ||
417 | mut callback: impl FnMut(&Ty, Function) -> Option<T>, | ||
418 | ) -> Option<T> { | ||
419 | // There should be no inference vars in types passed here | ||
420 | // FIXME check that? | ||
421 | // FIXME replace Unknown by bound vars here | ||
422 | let canonical = Canonical { value: ty.ty.value.clone(), num_vars: 0 }; | ||
423 | method_resolution::iterate_method_candidates( | ||
424 | &canonical, | ||
425 | db, | ||
426 | &self.resolver, | ||
427 | name, | ||
428 | method_resolution::LookupMode::MethodCall, | ||
429 | |ty, it| match it { | ||
430 | AssocItemId::FunctionId(f) => callback(ty, f.into()), | ||
431 | _ => None, | ||
432 | }, | ||
433 | ) | ||
434 | } | ||
435 | |||
436 | pub fn iterate_path_candidates<T>( | ||
437 | &self, | ||
438 | db: &impl HirDatabase, | ||
439 | ty: &Type, | ||
440 | name: Option<&Name>, | ||
441 | mut callback: impl FnMut(&Ty, AssocItem) -> Option<T>, | ||
442 | ) -> Option<T> { | ||
443 | // There should be no inference vars in types passed here | ||
444 | // FIXME check that? | ||
445 | // FIXME replace Unknown by bound vars here | ||
446 | let canonical = Canonical { value: ty.ty.value.clone(), num_vars: 0 }; | ||
447 | method_resolution::iterate_method_candidates( | ||
448 | &canonical, | ||
449 | db, | ||
450 | &self.resolver, | ||
451 | name, | ||
452 | method_resolution::LookupMode::Path, | ||
453 | |ty, it| callback(ty, it.into()), | ||
454 | ) | ||
455 | } | ||
456 | |||
457 | /// Checks that particular type `ty` implements `std::future::Future`. | ||
458 | /// This function is used in `.await` syntax completion. | ||
459 | pub fn impls_future(&self, db: &impl HirDatabase, ty: Type) -> bool { | ||
460 | let std_future_path = path![std::future::Future]; | ||
461 | |||
462 | let std_future_trait = match self.resolver.resolve_known_trait(db, &std_future_path) { | ||
463 | Some(it) => it.into(), | ||
464 | _ => return false, | ||
465 | }; | ||
466 | |||
467 | let krate = match self.resolver.krate() { | ||
468 | Some(krate) => krate, | ||
469 | _ => return false, | ||
470 | }; | ||
471 | |||
472 | let canonical_ty = Canonical { value: ty.ty.value, num_vars: 0 }; | ||
473 | implements_trait(&canonical_ty, db, &self.resolver, krate.into(), std_future_trait) | ||
474 | } | ||
475 | |||
476 | pub fn expand( | ||
477 | &self, | ||
478 | db: &impl HirDatabase, | ||
479 | macro_call: InFile<&ast::MacroCall>, | ||
480 | ) -> Option<Expansion> { | ||
481 | let def = self.resolve_macro_call(db, macro_call)?.id; | ||
482 | let ast_id = AstId::new( | ||
483 | macro_call.file_id, | ||
484 | db.ast_id_map(macro_call.file_id).ast_id(macro_call.value), | ||
485 | ); | ||
486 | Some(Expansion { macro_call_id: def.as_call_id(db, MacroCallKind::FnLike(ast_id)) }) | ||
487 | } | 292 | } |
488 | } | 293 | } |
489 | 294 | ||
490 | fn scope_for( | 295 | impl ToDef for ast::TypeParam { |
491 | scopes: &ExprScopes, | 296 | type Def = TypeParam; |
492 | source_map: &BodySourceMap, | 297 | |
493 | node: InFile<&SyntaxNode>, | 298 | fn to_def<DB: HirDatabase>( |
494 | ) -> Option<ScopeId> { | 299 | sb: &mut SourceBinder<'_, DB>, |
495 | node.value | 300 | src: InFile<ast::TypeParam>, |
496 | .ancestors() | 301 | ) -> Option<TypeParam> { |
497 | .filter_map(ast::Expr::cast) | 302 | let mut sb = SourceBinder::new(sb.db); |
498 | .filter_map(|it| source_map.node_expr(InFile::new(node.file_id, &it))) | 303 | let file_id = src.file_id; |
499 | .find_map(|it| scopes.scope_for(it)) | 304 | let parent: GenericDefId = src.value.syntax().ancestors().find_map(|it| { |
305 | let res = match_ast! { | ||
306 | match it { | ||
307 | ast::FnDef(value) => { sb.to_id(InFile { value, file_id})?.into() }, | ||
308 | ast::StructDef(value) => { sb.to_id(InFile { value, file_id})?.into() }, | ||
309 | ast::EnumDef(value) => { sb.to_id(InFile { value, file_id})?.into() }, | ||
310 | ast::TraitDef(value) => { sb.to_id(InFile { value, file_id})?.into() }, | ||
311 | ast::TypeAliasDef(value) => { sb.to_id(InFile { value, file_id})?.into() }, | ||
312 | ast::ImplBlock(value) => { sb.to_id(InFile { value, file_id})?.into() }, | ||
313 | _ => return None, | ||
314 | } | ||
315 | }; | ||
316 | Some(res) | ||
317 | })?; | ||
318 | let &id = sb.child_by_source(parent.into())[keys::TYPE_PARAM].get(&src)?; | ||
319 | Some(TypeParam { id }) | ||
320 | } | ||
500 | } | 321 | } |
501 | 322 | ||
502 | fn scope_for_offset( | 323 | impl ToId for ast::Module { |
503 | scopes: &ExprScopes, | 324 | type ID = ModuleId; |
504 | source_map: &BodySourceMap, | 325 | |
505 | offset: InFile<TextUnit>, | 326 | fn to_id<DB: HirDatabase>( |
506 | ) -> Option<ScopeId> { | 327 | sb: &mut SourceBinder<'_, DB>, |
507 | scopes | 328 | src: InFile<ast::Module>, |
508 | .scope_by_expr() | 329 | ) -> Option<ModuleId> { |
509 | .iter() | 330 | { |
510 | .filter_map(|(id, scope)| { | 331 | let _p = profile("ast::Module::to_def"); |
511 | let source = source_map.expr_syntax(*id)?; | 332 | let parent_declaration = src |
512 | // FIXME: correctly handle macro expansion | 333 | .as_ref() |
513 | if source.file_id != offset.file_id { | 334 | .map(|it| it.syntax()) |
514 | return None; | 335 | .cloned() |
515 | } | 336 | .ancestors_with_macros(sb.db) |
516 | let syntax_node_ptr = | 337 | .skip(1) |
517 | source.value.either(|it| it.syntax_node_ptr(), |it| it.syntax_node_ptr()); | 338 | .find_map(|it| { |
518 | Some((syntax_node_ptr, scope)) | 339 | let m = ast::Module::cast(it.value.clone())?; |
519 | }) | 340 | Some(it.with_value(m)) |
520 | // find containing scope | 341 | }); |
521 | .min_by_key(|(ptr, _scope)| { | 342 | |
522 | ( | 343 | let parent_module = match parent_declaration { |
523 | !(ptr.range().start() <= offset.value && offset.value <= ptr.range().end()), | 344 | Some(parent_declaration) => sb.to_id(parent_declaration)?, |
524 | ptr.range().len(), | 345 | None => { |
525 | ) | 346 | let file_id = src.file_id.original_file(sb.db); |
526 | }) | 347 | sb.to_module_def(file_id)?.id |
527 | .map(|(ptr, scope)| { | 348 | } |
528 | adjust(scopes, source_map, ptr, offset.file_id, offset.value).unwrap_or(*scope) | 349 | }; |
529 | }) | ||
530 | } | ||
531 | 350 | ||
532 | // XXX: during completion, cursor might be outside of any particular | 351 | let child_name = src.value.name()?.as_name(); |
533 | // expression. Try to figure out the correct scope... | 352 | let def_map = sb.db.crate_def_map(parent_module.krate); |
534 | fn adjust( | 353 | let child_id = *def_map[parent_module.local_id].children.get(&child_name)?; |
535 | scopes: &ExprScopes, | 354 | Some(ModuleId { krate: parent_module.krate, local_id: child_id }) |
536 | source_map: &BodySourceMap, | 355 | } |
537 | ptr: SyntaxNodePtr, | 356 | } |
538 | file_id: HirFileId, | ||
539 | offset: TextUnit, | ||
540 | ) -> Option<ScopeId> { | ||
541 | let r = ptr.range(); | ||
542 | let child_scopes = scopes | ||
543 | .scope_by_expr() | ||
544 | .iter() | ||
545 | .filter_map(|(id, scope)| { | ||
546 | let source = source_map.expr_syntax(*id)?; | ||
547 | // FIXME: correctly handle macro expansion | ||
548 | if source.file_id != file_id { | ||
549 | return None; | ||
550 | } | ||
551 | let syntax_node_ptr = | ||
552 | source.value.either(|it| it.syntax_node_ptr(), |it| it.syntax_node_ptr()); | ||
553 | Some((syntax_node_ptr, scope)) | ||
554 | }) | ||
555 | .map(|(ptr, scope)| (ptr.range(), scope)) | ||
556 | .filter(|(range, _)| range.start() <= offset && range.is_subrange(&r) && *range != r); | ||
557 | |||
558 | child_scopes | ||
559 | .max_by(|(r1, _), (r2, _)| { | ||
560 | if r2.is_subrange(&r1) { | ||
561 | std::cmp::Ordering::Greater | ||
562 | } else if r1.is_subrange(&r2) { | ||
563 | std::cmp::Ordering::Less | ||
564 | } else { | ||
565 | r1.start().cmp(&r2.start()) | ||
566 | } | ||
567 | }) | ||
568 | .map(|(_ptr, scope)| *scope) | ||
569 | } | 357 | } |