aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_hir/src/source_binder.rs
diff options
context:
space:
mode:
authorAleksey Kladov <[email protected]>2020-01-14 14:27:05 +0000
committerAleksey Kladov <[email protected]>2020-01-15 15:52:28 +0000
commitccfe53376ac579c2874000a939ea8be331c626aa (patch)
treec5e828be066a53e38fcb97b0f4c0ef411fa8951b /crates/ra_hir/src/source_binder.rs
parentc0661ce7444223b0fff1f5d54adb41022ab788cb (diff)
Introduce SourceBinder
Diffstat (limited to 'crates/ra_hir/src/source_binder.rs')
-rw-r--r--crates/ra_hir/src/source_binder.rs171
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
6use 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};
14use hir_expand::InFile;
15use ra_prof::profile;
16use ra_syntax::{ast, match_ast, AstNode, SyntaxNode, TextUnit};
17use rustc_hash::FxHashMap;
18
19use crate::{db::HirDatabase, ModuleSource, SourceAnalyzer};
20
21#[derive(Default)]
22pub struct SourceBinder {
23 child_by_source_cache: FxHashMap<ChildContainer, DynMap>,
24}
25
26impl 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)]
129enum ChildContainer {
130 DefWithBodyId(DefWithBodyId),
131 ModuleId(ModuleId),
132 TraitId(TraitId),
133 ImplId(ImplId),
134 EnumId(EnumId),
135 VariantId(VariantId),
136}
137impl_froms! {
138 ChildContainer:
139 DefWithBodyId,
140 ModuleId,
141 TraitId,
142 ImplId,
143 EnumId,
144 VariantId,
145}
146
147pub trait ToId: Sized + Copy + 'static {
148 type Ast: AstNode + 'static;
149 const KEY: Key<Self::Ast, Self>;
150}
151
152macro_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
161to_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];