diff options
author | Seivan Heidari <[email protected]> | 2019-11-18 01:27:53 +0000 |
---|---|---|
committer | Seivan Heidari <[email protected]> | 2019-11-18 01:27:53 +0000 |
commit | 166636ba77adcf5bf2c4ef935e9aa75e20f25e10 (patch) | |
tree | 168be1ca55c73b016e20586c08417c608450c92c /crates/ra_hir/src/source_binder.rs | |
parent | cb26df950699586b314731fb70786e0db8eaa049 (diff) | |
parent | 28c2d74b2150102a8756a5357a5a965d7610bd15 (diff) |
Merge branch 'master' of https://github.com/rust-analyzer/rust-analyzer into feature/themes
Diffstat (limited to 'crates/ra_hir/src/source_binder.rs')
-rw-r--r-- | crates/ra_hir/src/source_binder.rs | 119 |
1 files changed, 70 insertions, 49 deletions
diff --git a/crates/ra_hir/src/source_binder.rs b/crates/ra_hir/src/source_binder.rs index 662d3f880..f0ed8e2b2 100644 --- a/crates/ra_hir/src/source_binder.rs +++ b/crates/ra_hir/src/source_binder.rs | |||
@@ -11,13 +11,12 @@ use hir_def::{ | |||
11 | expr::{ExprId, PatId}, | 11 | expr::{ExprId, PatId}, |
12 | path::known, | 12 | path::known, |
13 | }; | 13 | }; |
14 | use hir_expand::{name::AsName, Source}; | 14 | use hir_expand::{name::AsName, AstId, MacroCallId, MacroCallLoc, MacroFileKind, Source}; |
15 | use ra_db::FileId; | ||
16 | use ra_syntax::{ | 15 | use ra_syntax::{ |
17 | ast::{self, AstNode}, | 16 | ast::{self, AstNode}, |
18 | match_ast, AstPtr, | 17 | match_ast, AstPtr, |
19 | SyntaxKind::*, | 18 | SyntaxKind::*, |
20 | SyntaxNode, SyntaxNodePtr, TextRange, TextUnit, | 19 | SyntaxNode, SyntaxNodePtr, SyntaxToken, TextRange, TextUnit, |
21 | }; | 20 | }; |
22 | 21 | ||
23 | use crate::{ | 22 | use crate::{ |
@@ -30,52 +29,45 @@ use crate::{ | |||
30 | HirFileId, Local, MacroDef, Module, Name, Path, Resolver, Static, Struct, Ty, | 29 | HirFileId, Local, MacroDef, Module, Name, Path, Resolver, Static, Struct, Ty, |
31 | }; | 30 | }; |
32 | 31 | ||
33 | fn try_get_resolver_for_node( | 32 | fn try_get_resolver_for_node(db: &impl HirDatabase, node: Source<&SyntaxNode>) -> Option<Resolver> { |
34 | db: &impl HirDatabase, | ||
35 | file_id: FileId, | ||
36 | node: &SyntaxNode, | ||
37 | ) -> Option<Resolver> { | ||
38 | match_ast! { | 33 | match_ast! { |
39 | match node { | 34 | match (node.ast) { |
40 | ast::Module(it) => { | 35 | ast::Module(it) => { |
41 | let src = crate::Source { file_id: file_id.into(), ast: it }; | 36 | let src = node.with_ast(it); |
42 | Some(crate::Module::from_declaration(db, src)?.resolver(db)) | 37 | Some(crate::Module::from_declaration(db, src)?.resolver(db)) |
43 | }, | 38 | }, |
44 | ast::SourceFile(it) => { | 39 | ast::SourceFile(it) => { |
45 | let src = | 40 | let src = node.with_ast(crate::ModuleSource::SourceFile(it)); |
46 | crate::Source { file_id: file_id.into(), ast: crate::ModuleSource::SourceFile(it) }; | ||
47 | Some(crate::Module::from_definition(db, src)?.resolver(db)) | 41 | Some(crate::Module::from_definition(db, src)?.resolver(db)) |
48 | }, | 42 | }, |
49 | ast::StructDef(it) => { | 43 | ast::StructDef(it) => { |
50 | let src = crate::Source { file_id: file_id.into(), ast: it }; | 44 | let src = node.with_ast(it); |
51 | Some(Struct::from_source(db, src)?.resolver(db)) | 45 | Some(Struct::from_source(db, src)?.resolver(db)) |
52 | }, | 46 | }, |
53 | ast::EnumDef(it) => { | 47 | ast::EnumDef(it) => { |
54 | let src = crate::Source { file_id: file_id.into(), ast: it }; | 48 | let src = node.with_ast(it); |
55 | Some(Enum::from_source(db, src)?.resolver(db)) | 49 | Some(Enum::from_source(db, src)?.resolver(db)) |
56 | }, | 50 | }, |
57 | _ => { | 51 | _ => match node.ast.kind() { |
58 | if node.kind() == FN_DEF || node.kind() == CONST_DEF || node.kind() == STATIC_DEF { | 52 | FN_DEF | CONST_DEF | STATIC_DEF => { |
59 | Some(def_with_body_from_child_node(db, file_id, node)?.resolver(db)) | 53 | Some(def_with_body_from_child_node(db, node)?.resolver(db)) |
60 | } else { | ||
61 | // FIXME add missing cases | ||
62 | None | ||
63 | } | 54 | } |
64 | }, | 55 | // FIXME add missing cases |
56 | _ => None | ||
57 | } | ||
65 | } | 58 | } |
66 | } | 59 | } |
67 | } | 60 | } |
68 | 61 | ||
69 | fn def_with_body_from_child_node( | 62 | fn def_with_body_from_child_node( |
70 | db: &impl HirDatabase, | 63 | db: &impl HirDatabase, |
71 | file_id: FileId, | 64 | child: Source<&SyntaxNode>, |
72 | node: &SyntaxNode, | ||
73 | ) -> Option<DefWithBody> { | 65 | ) -> Option<DefWithBody> { |
74 | let src = crate::ModuleSource::from_child_node(db, file_id, node); | 66 | let module_source = crate::ModuleSource::from_child_node(db, child); |
75 | let module = Module::from_definition(db, crate::Source { file_id: file_id.into(), ast: src })?; | 67 | let module = Module::from_definition(db, Source::new(child.file_id, module_source))?; |
76 | let ctx = LocationCtx::new(db, module.id, file_id.into()); | 68 | let ctx = LocationCtx::new(db, module.id, child.file_id); |
77 | 69 | ||
78 | node.ancestors().find_map(|node| { | 70 | child.ast.ancestors().find_map(|node| { |
79 | match_ast! { | 71 | match_ast! { |
80 | match node { | 72 | match node { |
81 | ast::FnDef(def) => { Some(Function {id: ctx.to_def(&def) }.into()) }, | 73 | ast::FnDef(def) => { Some(Function {id: ctx.to_def(&def) }.into()) }, |
@@ -91,8 +83,7 @@ fn def_with_body_from_child_node( | |||
91 | /// original source files. It should not be used inside the HIR itself. | 83 | /// original source files. It should not be used inside the HIR itself. |
92 | #[derive(Debug)] | 84 | #[derive(Debug)] |
93 | pub struct SourceAnalyzer { | 85 | pub struct SourceAnalyzer { |
94 | // FIXME: this doesn't handle macros at all | 86 | file_id: HirFileId, |
95 | file_id: FileId, | ||
96 | resolver: Resolver, | 87 | resolver: Resolver, |
97 | body_owner: Option<DefWithBody>, | 88 | body_owner: Option<DefWithBody>, |
98 | body_source_map: Option<Arc<BodySourceMap>>, | 89 | body_source_map: Option<Arc<BodySourceMap>>, |
@@ -135,20 +126,38 @@ pub struct ReferenceDescriptor { | |||
135 | pub name: String, | 126 | pub name: String, |
136 | } | 127 | } |
137 | 128 | ||
129 | pub struct Expansion { | ||
130 | macro_call_id: MacroCallId, | ||
131 | } | ||
132 | |||
133 | impl Expansion { | ||
134 | pub fn map_token_down( | ||
135 | &self, | ||
136 | db: &impl HirDatabase, | ||
137 | token: Source<&SyntaxToken>, | ||
138 | ) -> Option<Source<SyntaxToken>> { | ||
139 | let exp_info = self.file_id().expansion_info(db)?; | ||
140 | exp_info.map_token_down(token) | ||
141 | } | ||
142 | |||
143 | fn file_id(&self) -> HirFileId { | ||
144 | self.macro_call_id.as_file(MacroFileKind::Items) | ||
145 | } | ||
146 | } | ||
147 | |||
138 | impl SourceAnalyzer { | 148 | impl SourceAnalyzer { |
139 | pub fn new( | 149 | pub fn new( |
140 | db: &impl HirDatabase, | 150 | db: &impl HirDatabase, |
141 | file_id: FileId, | 151 | node: Source<&SyntaxNode>, |
142 | node: &SyntaxNode, | ||
143 | offset: Option<TextUnit>, | 152 | offset: Option<TextUnit>, |
144 | ) -> SourceAnalyzer { | 153 | ) -> SourceAnalyzer { |
145 | let def_with_body = def_with_body_from_child_node(db, file_id, node); | 154 | let def_with_body = def_with_body_from_child_node(db, node); |
146 | if let Some(def) = def_with_body { | 155 | if let Some(def) = def_with_body { |
147 | let source_map = def.body_source_map(db); | 156 | let source_map = def.body_source_map(db); |
148 | let scopes = def.expr_scopes(db); | 157 | let scopes = def.expr_scopes(db); |
149 | let scope = match offset { | 158 | let scope = match offset { |
150 | None => scope_for(&scopes, &source_map, file_id.into(), &node), | 159 | None => scope_for(&scopes, &source_map, node), |
151 | Some(offset) => scope_for_offset(&scopes, &source_map, file_id.into(), offset), | 160 | Some(offset) => scope_for_offset(&scopes, &source_map, node.with_ast(offset)), |
152 | }; | 161 | }; |
153 | let resolver = expr::resolver_for_scope(db, def, scope); | 162 | let resolver = expr::resolver_for_scope(db, def, scope); |
154 | SourceAnalyzer { | 163 | SourceAnalyzer { |
@@ -157,30 +166,31 @@ impl SourceAnalyzer { | |||
157 | body_source_map: Some(source_map), | 166 | body_source_map: Some(source_map), |
158 | infer: Some(def.infer(db)), | 167 | infer: Some(def.infer(db)), |
159 | scopes: Some(scopes), | 168 | scopes: Some(scopes), |
160 | file_id, | 169 | file_id: node.file_id, |
161 | } | 170 | } |
162 | } else { | 171 | } else { |
163 | SourceAnalyzer { | 172 | SourceAnalyzer { |
164 | resolver: node | 173 | resolver: node |
174 | .ast | ||
165 | .ancestors() | 175 | .ancestors() |
166 | .find_map(|node| try_get_resolver_for_node(db, file_id, &node)) | 176 | .find_map(|it| try_get_resolver_for_node(db, node.with_ast(&it))) |
167 | .unwrap_or_default(), | 177 | .unwrap_or_default(), |
168 | body_owner: None, | 178 | body_owner: None, |
169 | body_source_map: None, | 179 | body_source_map: None, |
170 | infer: None, | 180 | infer: None, |
171 | scopes: None, | 181 | scopes: None, |
172 | file_id, | 182 | file_id: node.file_id, |
173 | } | 183 | } |
174 | } | 184 | } |
175 | } | 185 | } |
176 | 186 | ||
177 | fn expr_id(&self, expr: &ast::Expr) -> Option<ExprId> { | 187 | fn expr_id(&self, expr: &ast::Expr) -> Option<ExprId> { |
178 | let src = Source { file_id: self.file_id.into(), ast: expr }; | 188 | let src = Source { file_id: self.file_id, ast: expr }; |
179 | self.body_source_map.as_ref()?.node_expr(src) | 189 | self.body_source_map.as_ref()?.node_expr(src) |
180 | } | 190 | } |
181 | 191 | ||
182 | fn pat_id(&self, pat: &ast::Pat) -> Option<PatId> { | 192 | fn pat_id(&self, pat: &ast::Pat) -> Option<PatId> { |
183 | let src = Source { file_id: self.file_id.into(), ast: pat }; | 193 | let src = Source { file_id: self.file_id, ast: pat }; |
184 | self.body_source_map.as_ref()?.node_pat(src) | 194 | self.body_source_map.as_ref()?.node_pat(src) |
185 | } | 195 | } |
186 | 196 | ||
@@ -288,7 +298,7 @@ impl SourceAnalyzer { | |||
288 | let name = name_ref.as_name(); | 298 | let name = name_ref.as_name(); |
289 | let source_map = self.body_source_map.as_ref()?; | 299 | let source_map = self.body_source_map.as_ref()?; |
290 | let scopes = self.scopes.as_ref()?; | 300 | let scopes = self.scopes.as_ref()?; |
291 | let scope = scope_for(scopes, source_map, self.file_id.into(), name_ref.syntax())?; | 301 | let scope = scope_for(scopes, source_map, Source::new(self.file_id, name_ref.syntax()))?; |
292 | let entry = scopes.resolve_name_in_scope(scope, &name)?; | 302 | let entry = scopes.resolve_name_in_scope(scope, &name)?; |
293 | Some(ScopeEntryWithSyntax { | 303 | Some(ScopeEntryWithSyntax { |
294 | name: entry.name().clone(), | 304 | name: entry.name().clone(), |
@@ -395,6 +405,13 @@ impl SourceAnalyzer { | |||
395 | implements_trait(&canonical_ty, db, &self.resolver, krate, std_future_trait) | 405 | implements_trait(&canonical_ty, db, &self.resolver, krate, std_future_trait) |
396 | } | 406 | } |
397 | 407 | ||
408 | pub fn expand(&self, db: &impl HirDatabase, macro_call: &ast::MacroCall) -> Option<Expansion> { | ||
409 | let def = self.resolve_macro_call(db, macro_call)?.id; | ||
410 | let ast_id = AstId::new(self.file_id, db.ast_id_map(self.file_id).ast_id(macro_call)); | ||
411 | let macro_call_loc = MacroCallLoc { def, ast_id }; | ||
412 | Some(Expansion { macro_call_id: db.intern_macro(macro_call_loc) }) | ||
413 | } | ||
414 | |||
398 | #[cfg(test)] | 415 | #[cfg(test)] |
399 | pub(crate) fn body_source_map(&self) -> Arc<BodySourceMap> { | 416 | pub(crate) fn body_source_map(&self) -> Arc<BodySourceMap> { |
400 | self.body_source_map.clone().unwrap() | 417 | self.body_source_map.clone().unwrap() |
@@ -409,20 +426,19 @@ impl SourceAnalyzer { | |||
409 | fn scope_for( | 426 | fn scope_for( |
410 | scopes: &ExprScopes, | 427 | scopes: &ExprScopes, |
411 | source_map: &BodySourceMap, | 428 | source_map: &BodySourceMap, |
412 | file_id: HirFileId, | 429 | node: Source<&SyntaxNode>, |
413 | node: &SyntaxNode, | ||
414 | ) -> Option<ScopeId> { | 430 | ) -> Option<ScopeId> { |
415 | node.ancestors() | 431 | node.ast |
432 | .ancestors() | ||
416 | .filter_map(ast::Expr::cast) | 433 | .filter_map(ast::Expr::cast) |
417 | .filter_map(|it| source_map.node_expr(Source { file_id, ast: &it })) | 434 | .filter_map(|it| source_map.node_expr(Source::new(node.file_id, &it))) |
418 | .find_map(|it| scopes.scope_for(it)) | 435 | .find_map(|it| scopes.scope_for(it)) |
419 | } | 436 | } |
420 | 437 | ||
421 | fn scope_for_offset( | 438 | fn scope_for_offset( |
422 | scopes: &ExprScopes, | 439 | scopes: &ExprScopes, |
423 | source_map: &BodySourceMap, | 440 | source_map: &BodySourceMap, |
424 | file_id: HirFileId, | 441 | offset: Source<TextUnit>, |
425 | offset: TextUnit, | ||
426 | ) -> Option<ScopeId> { | 442 | ) -> Option<ScopeId> { |
427 | scopes | 443 | scopes |
428 | .scope_by_expr() | 444 | .scope_by_expr() |
@@ -430,7 +446,7 @@ fn scope_for_offset( | |||
430 | .filter_map(|(id, scope)| { | 446 | .filter_map(|(id, scope)| { |
431 | let source = source_map.expr_syntax(*id)?; | 447 | let source = source_map.expr_syntax(*id)?; |
432 | // FIXME: correctly handle macro expansion | 448 | // FIXME: correctly handle macro expansion |
433 | if source.file_id != file_id { | 449 | if source.file_id != offset.file_id { |
434 | return None; | 450 | return None; |
435 | } | 451 | } |
436 | let syntax_node_ptr = | 452 | let syntax_node_ptr = |
@@ -439,9 +455,14 @@ fn scope_for_offset( | |||
439 | }) | 455 | }) |
440 | // find containing scope | 456 | // find containing scope |
441 | .min_by_key(|(ptr, _scope)| { | 457 | .min_by_key(|(ptr, _scope)| { |
442 | (!(ptr.range().start() <= offset && offset <= ptr.range().end()), ptr.range().len()) | 458 | ( |
459 | !(ptr.range().start() <= offset.ast && offset.ast <= ptr.range().end()), | ||
460 | ptr.range().len(), | ||
461 | ) | ||
462 | }) | ||
463 | .map(|(ptr, scope)| { | ||
464 | adjust(scopes, source_map, ptr, offset.file_id, offset.ast).unwrap_or(*scope) | ||
443 | }) | 465 | }) |
444 | .map(|(ptr, scope)| adjust(scopes, source_map, ptr, file_id, offset).unwrap_or(*scope)) | ||
445 | } | 466 | } |
446 | 467 | ||
447 | // XXX: during completion, cursor might be outside of any particular | 468 | // XXX: during completion, cursor might be outside of any particular |