diff options
Diffstat (limited to 'crates/ra_hir/src/source_binder.rs')
-rw-r--r-- | crates/ra_hir/src/source_binder.rs | 171 |
1 files changed, 171 insertions, 0 deletions
diff --git a/crates/ra_hir/src/source_binder.rs b/crates/ra_hir/src/source_binder.rs new file mode 100644 index 000000000..cec3f8c2c --- /dev/null +++ b/crates/ra_hir/src/source_binder.rs | |||
@@ -0,0 +1,171 @@ | |||
1 | //! `SourceBinder` should be the main entry point for getting info about source code. | ||
2 | //! It's main task is to map source syntax trees to hir-level IDs. | ||
3 | //! | ||
4 | //! It is intended to subsume `FromSource` and `SourceAnalyzer`. | ||
5 | |||
6 | use hir_def::{ | ||
7 | child_by_source::ChildBySource, | ||
8 | dyn_map::DynMap, | ||
9 | keys::{self, Key}, | ||
10 | resolver::{HasResolver, Resolver}, | ||
11 | ConstId, DefWithBodyId, EnumId, FunctionId, ImplId, ModuleId, StaticId, StructId, TraitId, | ||
12 | UnionId, VariantId, | ||
13 | }; | ||
14 | use hir_expand::InFile; | ||
15 | use ra_prof::profile; | ||
16 | use ra_syntax::{ast, match_ast, AstNode, SyntaxNode, TextUnit}; | ||
17 | use rustc_hash::FxHashMap; | ||
18 | |||
19 | use crate::{db::HirDatabase, ModuleSource, SourceAnalyzer}; | ||
20 | |||
21 | #[derive(Default)] | ||
22 | pub struct SourceBinder { | ||
23 | child_by_source_cache: FxHashMap<ChildContainer, DynMap>, | ||
24 | } | ||
25 | |||
26 | impl SourceBinder { | ||
27 | pub fn analyze( | ||
28 | &mut self, | ||
29 | db: &impl HirDatabase, | ||
30 | src: InFile<&SyntaxNode>, | ||
31 | offset: Option<TextUnit>, | ||
32 | ) -> SourceAnalyzer { | ||
33 | let _p = profile("SourceBinder::analyzer"); | ||
34 | let container = match self.find_container(db, src) { | ||
35 | Some(it) => it, | ||
36 | None => return SourceAnalyzer::new_for_resolver(Resolver::default(), src), | ||
37 | }; | ||
38 | |||
39 | let resolver = match container { | ||
40 | ChildContainer::DefWithBodyId(def) => { | ||
41 | return SourceAnalyzer::new_for_body(db, def, src, offset) | ||
42 | } | ||
43 | ChildContainer::TraitId(it) => it.resolver(db), | ||
44 | ChildContainer::ImplId(it) => it.resolver(db), | ||
45 | ChildContainer::ModuleId(it) => it.resolver(db), | ||
46 | ChildContainer::EnumId(it) => it.resolver(db), | ||
47 | ChildContainer::VariantId(it) => it.resolver(db), | ||
48 | }; | ||
49 | SourceAnalyzer::new_for_resolver(resolver, src) | ||
50 | } | ||
51 | |||
52 | pub fn to_def<D, ID>(&mut self, db: &impl HirDatabase, src: InFile<ID::Ast>) -> Option<D> | ||
53 | where | ||
54 | D: From<ID>, | ||
55 | ID: ToId, | ||
56 | { | ||
57 | let id: ID = self.to_id(db, src)?; | ||
58 | Some(id.into()) | ||
59 | } | ||
60 | |||
61 | fn to_id<D: ToId>(&mut self, db: &impl HirDatabase, src: InFile<D::Ast>) -> Option<D> { | ||
62 | let container = self.find_container(db, src.as_ref().map(|it| it.syntax()))?; | ||
63 | let dyn_map = | ||
64 | &*self.child_by_source_cache.entry(container).or_insert_with(|| match container { | ||
65 | ChildContainer::DefWithBodyId(it) => it.child_by_source(db), | ||
66 | ChildContainer::ModuleId(it) => it.child_by_source(db), | ||
67 | ChildContainer::TraitId(it) => it.child_by_source(db), | ||
68 | ChildContainer::ImplId(it) => it.child_by_source(db), | ||
69 | ChildContainer::EnumId(it) => it.child_by_source(db), | ||
70 | ChildContainer::VariantId(it) => it.child_by_source(db), | ||
71 | }); | ||
72 | dyn_map[D::KEY].get(&src).copied() | ||
73 | } | ||
74 | |||
75 | fn find_container( | ||
76 | &mut self, | ||
77 | db: &impl HirDatabase, | ||
78 | src: InFile<&SyntaxNode>, | ||
79 | ) -> Option<ChildContainer> { | ||
80 | for container in src.cloned().ancestors_with_macros(db).skip(1) { | ||
81 | let res: ChildContainer = match_ast! { | ||
82 | match (container.value) { | ||
83 | ast::TraitDef(it) => { | ||
84 | let def: TraitId = self.to_id(db, container.with_value(it))?; | ||
85 | def.into() | ||
86 | }, | ||
87 | ast::ImplBlock(it) => { | ||
88 | let def: ImplId = self.to_id(db, container.with_value(it))?; | ||
89 | def.into() | ||
90 | }, | ||
91 | ast::FnDef(it) => { | ||
92 | let def: FunctionId = self.to_id(db, container.with_value(it))?; | ||
93 | DefWithBodyId::from(def).into() | ||
94 | }, | ||
95 | ast::StaticDef(it) => { | ||
96 | let def: StaticId = self.to_id(db, container.with_value(it))?; | ||
97 | DefWithBodyId::from(def).into() | ||
98 | }, | ||
99 | ast::ConstDef(it) => { | ||
100 | let def: ConstId = self.to_id(db, container.with_value(it))?; | ||
101 | DefWithBodyId::from(def).into() | ||
102 | }, | ||
103 | ast::EnumDef(it) => { | ||
104 | let def: EnumId = self.to_id(db, container.with_value(it))?; | ||
105 | def.into() | ||
106 | }, | ||
107 | ast::StructDef(it) => { | ||
108 | let def: StructId = self.to_id(db, container.with_value(it))?; | ||
109 | VariantId::from(def).into() | ||
110 | }, | ||
111 | ast::UnionDef(it) => { | ||
112 | let def: UnionId = self.to_id(db, container.with_value(it))?; | ||
113 | VariantId::from(def).into() | ||
114 | }, | ||
115 | // FIXME: handle out-of-line modules here | ||
116 | _ => { continue }, | ||
117 | } | ||
118 | }; | ||
119 | return Some(res); | ||
120 | } | ||
121 | |||
122 | let module_source = ModuleSource::from_child_node(db, src); | ||
123 | let c = crate::Module::from_definition(db, src.with_value(module_source))?; | ||
124 | Some(c.id.into()) | ||
125 | } | ||
126 | } | ||
127 | |||
128 | #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)] | ||
129 | enum ChildContainer { | ||
130 | DefWithBodyId(DefWithBodyId), | ||
131 | ModuleId(ModuleId), | ||
132 | TraitId(TraitId), | ||
133 | ImplId(ImplId), | ||
134 | EnumId(EnumId), | ||
135 | VariantId(VariantId), | ||
136 | } | ||
137 | impl_froms! { | ||
138 | ChildContainer: | ||
139 | DefWithBodyId, | ||
140 | ModuleId, | ||
141 | TraitId, | ||
142 | ImplId, | ||
143 | EnumId, | ||
144 | VariantId, | ||
145 | } | ||
146 | |||
147 | pub trait ToId: Sized + Copy + 'static { | ||
148 | type Ast: AstNode + 'static; | ||
149 | const KEY: Key<Self::Ast, Self>; | ||
150 | } | ||
151 | |||
152 | macro_rules! to_id_impls { | ||
153 | ($(($id:ident, $ast:path, $key:path)),* ,) => {$( | ||
154 | impl ToId for $id { | ||
155 | type Ast = $ast; | ||
156 | const KEY: Key<Self::Ast, Self> = $key; | ||
157 | } | ||
158 | )*} | ||
159 | } | ||
160 | |||
161 | to_id_impls![ | ||
162 | (StructId, ast::StructDef, keys::STRUCT), | ||
163 | (UnionId, ast::UnionDef, keys::UNION), | ||
164 | (EnumId, ast::EnumDef, keys::ENUM), | ||
165 | (TraitId, ast::TraitDef, keys::TRAIT), | ||
166 | (FunctionId, ast::FnDef, keys::FUNCTION), | ||
167 | (StaticId, ast::StaticDef, keys::STATIC), | ||
168 | (ConstId, ast::ConstDef, keys::CONST), | ||
169 | // (TypeAlias, TypeAliasId, ast::TypeAliasDef, keys::TYPE_ALIAS), | ||
170 | (ImplId, ast::ImplBlock, keys::IMPL), | ||
171 | ]; | ||