diff options
Diffstat (limited to 'crates/ra_hir/src/source_binder.rs')
-rw-r--r-- | crates/ra_hir/src/source_binder.rs | 173 |
1 files changed, 173 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..00541dbe1 --- /dev/null +++ b/crates/ra_hir/src/source_binder.rs | |||
@@ -0,0 +1,173 @@ | |||
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, EnumVariantId, FunctionId, ImplId, ModuleId, StaticId, | ||
12 | StructFieldId, StructId, TraitId, TypeAliasId, 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 | pub struct SourceBinder<'a, DB> { | ||
22 | pub db: &'a DB, | ||
23 | child_by_source_cache: FxHashMap<ChildContainer, DynMap>, | ||
24 | } | ||
25 | |||
26 | impl<DB: HirDatabase> SourceBinder<'_, DB> { | ||
27 | pub fn new(db: &DB) -> SourceBinder<DB> { | ||
28 | SourceBinder { db, child_by_source_cache: FxHashMap::default() } | ||
29 | } | ||
30 | |||
31 | pub fn analyze( | ||
32 | &mut self, | ||
33 | src: InFile<&SyntaxNode>, | ||
34 | offset: Option<TextUnit>, | ||
35 | ) -> SourceAnalyzer { | ||
36 | let _p = profile("SourceBinder::analyzer"); | ||
37 | let container = match self.find_container(src) { | ||
38 | Some(it) => it, | ||
39 | None => return SourceAnalyzer::new_for_resolver(Resolver::default(), src), | ||
40 | }; | ||
41 | |||
42 | let resolver = match container { | ||
43 | ChildContainer::DefWithBodyId(def) => { | ||
44 | return SourceAnalyzer::new_for_body(self.db, def, src, offset) | ||
45 | } | ||
46 | ChildContainer::TraitId(it) => it.resolver(self.db), | ||
47 | ChildContainer::ImplId(it) => it.resolver(self.db), | ||
48 | ChildContainer::ModuleId(it) => it.resolver(self.db), | ||
49 | ChildContainer::EnumId(it) => it.resolver(self.db), | ||
50 | ChildContainer::VariantId(it) => it.resolver(self.db), | ||
51 | }; | ||
52 | SourceAnalyzer::new_for_resolver(resolver, src) | ||
53 | } | ||
54 | |||
55 | pub fn to_def<D, T>(&mut self, src: InFile<T>) -> Option<D> | ||
56 | where | ||
57 | D: From<T::ID>, | ||
58 | T: ToId, | ||
59 | { | ||
60 | let id: T::ID = self.to_id(src)?; | ||
61 | Some(id.into()) | ||
62 | } | ||
63 | |||
64 | fn to_id<T: ToId>(&mut self, src: InFile<T>) -> Option<T::ID> { | ||
65 | let container = self.find_container(src.as_ref().map(|it| it.syntax()))?; | ||
66 | let db = self.db; | ||
67 | let dyn_map = | ||
68 | &*self.child_by_source_cache.entry(container).or_insert_with(|| match container { | ||
69 | ChildContainer::DefWithBodyId(it) => it.child_by_source(db), | ||
70 | ChildContainer::ModuleId(it) => it.child_by_source(db), | ||
71 | ChildContainer::TraitId(it) => it.child_by_source(db), | ||
72 | ChildContainer::ImplId(it) => it.child_by_source(db), | ||
73 | ChildContainer::EnumId(it) => it.child_by_source(db), | ||
74 | ChildContainer::VariantId(it) => it.child_by_source(db), | ||
75 | }); | ||
76 | dyn_map[T::KEY].get(&src).copied() | ||
77 | } | ||
78 | |||
79 | fn find_container(&mut self, src: InFile<&SyntaxNode>) -> Option<ChildContainer> { | ||
80 | for container in src.cloned().ancestors_with_macros(self.db).skip(1) { | ||
81 | let res: ChildContainer = match_ast! { | ||
82 | match (container.value) { | ||
83 | ast::TraitDef(it) => { | ||
84 | let def: TraitId = self.to_id(container.with_value(it))?; | ||
85 | def.into() | ||
86 | }, | ||
87 | ast::ImplBlock(it) => { | ||
88 | let def: ImplId = self.to_id(container.with_value(it))?; | ||
89 | def.into() | ||
90 | }, | ||
91 | ast::FnDef(it) => { | ||
92 | let def: FunctionId = self.to_id(container.with_value(it))?; | ||
93 | DefWithBodyId::from(def).into() | ||
94 | }, | ||
95 | ast::StaticDef(it) => { | ||
96 | let def: StaticId = self.to_id(container.with_value(it))?; | ||
97 | DefWithBodyId::from(def).into() | ||
98 | }, | ||
99 | ast::ConstDef(it) => { | ||
100 | let def: ConstId = self.to_id(container.with_value(it))?; | ||
101 | DefWithBodyId::from(def).into() | ||
102 | }, | ||
103 | ast::EnumDef(it) => { | ||
104 | let def: EnumId = self.to_id(container.with_value(it))?; | ||
105 | def.into() | ||
106 | }, | ||
107 | ast::StructDef(it) => { | ||
108 | let def: StructId = self.to_id(container.with_value(it))?; | ||
109 | VariantId::from(def).into() | ||
110 | }, | ||
111 | ast::UnionDef(it) => { | ||
112 | let def: UnionId = self.to_id(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(self.db, src); | ||
123 | let c = crate::Module::from_definition(self.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 + AstNode + 'static { | ||
148 | type ID: Sized + Copy + 'static; | ||
149 | const KEY: Key<Self, Self::ID>; | ||
150 | } | ||
151 | |||
152 | macro_rules! to_id_impls { | ||
153 | ($(($id:ident, $ast:path, $key:path)),* ,) => {$( | ||
154 | impl ToId for $ast { | ||
155 | type ID = $id; | ||
156 | const KEY: Key<Self, Self::ID> = $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 | (TypeAliasId, ast::TypeAliasDef, keys::TYPE_ALIAS), | ||
170 | (ImplId, ast::ImplBlock, keys::IMPL), | ||
171 | (StructFieldId, ast::RecordFieldDef, keys::RECORD_FIELD), | ||
172 | (EnumVariantId, ast::EnumVariant, keys::ENUM_VARIANT), | ||
173 | ]; | ||