diff options
author | Aleksey Kladov <[email protected]> | 2019-11-22 18:43:36 +0000 |
---|---|---|
committer | Aleksey Kladov <[email protected]> | 2019-11-22 20:09:17 +0000 |
commit | 0f415dd4b30289117fe76d071293e9bdd3556336 (patch) | |
tree | 908faccf9e77fcb1d2804da3289ebc4d026ce339 /crates/ra_hir_def/src | |
parent | 5b19202e00fffe62a1a9c07f4b974f0affdd0c66 (diff) |
More principled sources for enums and fields
Diffstat (limited to 'crates/ra_hir_def/src')
-rw-r--r-- | crates/ra_hir_def/src/adt.rs | 145 | ||||
-rw-r--r-- | crates/ra_hir_def/src/lib.rs | 13 | ||||
-rw-r--r-- | crates/ra_hir_def/src/trace.rs | 49 |
3 files changed, 167 insertions, 40 deletions
diff --git a/crates/ra_hir_def/src/adt.rs b/crates/ra_hir_def/src/adt.rs index 2bd18255c..ae99afe39 100644 --- a/crates/ra_hir_def/src/adt.rs +++ b/crates/ra_hir_def/src/adt.rs | |||
@@ -2,13 +2,17 @@ | |||
2 | 2 | ||
3 | use std::sync::Arc; | 3 | use std::sync::Arc; |
4 | 4 | ||
5 | use hir_expand::name::{AsName, Name}; | 5 | use hir_expand::{ |
6 | use ra_arena::Arena; | 6 | either::Either, |
7 | name::{AsName, Name}, | ||
8 | Source, | ||
9 | }; | ||
10 | use ra_arena::{map::ArenaMap, Arena}; | ||
7 | use ra_syntax::ast::{self, NameOwner, TypeAscriptionOwner}; | 11 | use ra_syntax::ast::{self, NameOwner, TypeAscriptionOwner}; |
8 | 12 | ||
9 | use crate::{ | 13 | use crate::{ |
10 | db::DefDatabase2, type_ref::TypeRef, AstItemDef, EnumId, LocalEnumVariantId, | 14 | db::DefDatabase2, trace::Trace, type_ref::TypeRef, AstItemDef, EnumId, HasChildSource, |
11 | LocalStructFieldId, StructOrUnionId, | 15 | LocalEnumVariantId, LocalStructFieldId, StructOrUnionId, VariantId, |
12 | }; | 16 | }; |
13 | 17 | ||
14 | /// Note that we use `StructData` for unions as well! | 18 | /// Note that we use `StructData` for unions as well! |
@@ -61,17 +65,9 @@ impl EnumData { | |||
61 | pub(crate) fn enum_data_query(db: &impl DefDatabase2, e: EnumId) -> Arc<EnumData> { | 65 | pub(crate) fn enum_data_query(db: &impl DefDatabase2, e: EnumId) -> Arc<EnumData> { |
62 | let src = e.source(db); | 66 | let src = e.source(db); |
63 | let name = src.value.name().map(|n| n.as_name()); | 67 | let name = src.value.name().map(|n| n.as_name()); |
64 | let variants = src | 68 | let mut trace = Trace::new_for_arena(); |
65 | .value | 69 | lower_enum(&mut trace, &src.value); |
66 | .variant_list() | 70 | Arc::new(EnumData { name, variants: trace.into_arena() }) |
67 | .into_iter() | ||
68 | .flat_map(|it| it.variants()) | ||
69 | .map(|var| EnumVariantData { | ||
70 | name: var.name().map(|it| it.as_name()), | ||
71 | variant_data: Arc::new(VariantData::new(var.kind())), | ||
72 | }) | ||
73 | .collect(); | ||
74 | Arc::new(EnumData { name, variants }) | ||
75 | } | 71 | } |
76 | 72 | ||
77 | pub(crate) fn variant(&self, name: &Name) -> Option<LocalEnumVariantId> { | 73 | pub(crate) fn variant(&self, name: &Name) -> Option<LocalEnumVariantId> { |
@@ -80,38 +76,109 @@ impl EnumData { | |||
80 | } | 76 | } |
81 | } | 77 | } |
82 | 78 | ||
79 | impl HasChildSource for EnumId { | ||
80 | type ChildId = LocalEnumVariantId; | ||
81 | type Value = ast::EnumVariant; | ||
82 | fn child_source(&self, db: &impl DefDatabase2) -> Source<ArenaMap<Self::ChildId, Self::Value>> { | ||
83 | let src = self.source(db); | ||
84 | let mut trace = Trace::new_for_map(); | ||
85 | lower_enum(&mut trace, &src.value); | ||
86 | src.with_value(trace.into_map()) | ||
87 | } | ||
88 | } | ||
89 | |||
90 | fn lower_enum( | ||
91 | trace: &mut Trace<LocalEnumVariantId, EnumVariantData, ast::EnumVariant>, | ||
92 | ast: &ast::EnumDef, | ||
93 | ) { | ||
94 | for var in ast.variant_list().into_iter().flat_map(|it| it.variants()) { | ||
95 | trace.alloc( | ||
96 | || var.clone(), | ||
97 | || EnumVariantData { | ||
98 | name: var.name().map(|it| it.as_name()), | ||
99 | variant_data: Arc::new(VariantData::new(var.kind())), | ||
100 | }, | ||
101 | ) | ||
102 | } | ||
103 | } | ||
104 | |||
83 | impl VariantData { | 105 | impl VariantData { |
84 | fn new(flavor: ast::StructKind) -> Self { | 106 | fn new(flavor: ast::StructKind) -> Self { |
85 | match flavor { | 107 | let mut trace = Trace::new_for_arena(); |
86 | ast::StructKind::Tuple(fl) => { | 108 | match lower_struct(&mut trace, &flavor) { |
87 | let fields = fl | 109 | StructKind::Tuple => VariantData::Tuple(trace.into_arena()), |
88 | .fields() | 110 | StructKind::Record => VariantData::Record(trace.into_arena()), |
89 | .enumerate() | 111 | StructKind::Unit => VariantData::Unit, |
90 | .map(|(i, fd)| StructFieldData { | ||
91 | name: Name::new_tuple_field(i), | ||
92 | type_ref: TypeRef::from_ast_opt(fd.type_ref()), | ||
93 | }) | ||
94 | .collect(); | ||
95 | VariantData::Tuple(fields) | ||
96 | } | ||
97 | ast::StructKind::Record(fl) => { | ||
98 | let fields = fl | ||
99 | .fields() | ||
100 | .map(|fd| StructFieldData { | ||
101 | name: fd.name().map(|n| n.as_name()).unwrap_or_else(Name::missing), | ||
102 | type_ref: TypeRef::from_ast_opt(fd.ascribed_type()), | ||
103 | }) | ||
104 | .collect(); | ||
105 | VariantData::Record(fields) | ||
106 | } | ||
107 | ast::StructKind::Unit => VariantData::Unit, | ||
108 | } | 112 | } |
109 | } | 113 | } |
110 | 114 | ||
111 | pub fn fields(&self) -> Option<&Arena<LocalStructFieldId, StructFieldData>> { | 115 | pub fn fields(&self) -> Option<&Arena<LocalStructFieldId, StructFieldData>> { |
112 | match self { | 116 | match &self { |
113 | VariantData::Record(fields) | VariantData::Tuple(fields) => Some(fields), | 117 | VariantData::Record(fields) | VariantData::Tuple(fields) => Some(fields), |
114 | _ => None, | 118 | _ => None, |
115 | } | 119 | } |
116 | } | 120 | } |
117 | } | 121 | } |
122 | |||
123 | impl HasChildSource for VariantId { | ||
124 | type ChildId = LocalStructFieldId; | ||
125 | type Value = Either<ast::TupleFieldDef, ast::RecordFieldDef>; | ||
126 | |||
127 | fn child_source(&self, db: &impl DefDatabase2) -> Source<ArenaMap<Self::ChildId, Self::Value>> { | ||
128 | let src = match self { | ||
129 | VariantId::EnumVariantId(it) => { | ||
130 | // I don't really like the fact that we call into parent source | ||
131 | // here, this might add to more queries then necessary. | ||
132 | let src = it.parent.child_source(db); | ||
133 | src.map(|map| map[it.local_id].kind()) | ||
134 | } | ||
135 | VariantId::StructId(it) => it.0.source(db).map(|it| it.kind()), | ||
136 | }; | ||
137 | let mut trace = Trace::new_for_map(); | ||
138 | lower_struct(&mut trace, &src.value); | ||
139 | src.with_value(trace.into_map()) | ||
140 | } | ||
141 | } | ||
142 | |||
143 | enum StructKind { | ||
144 | Tuple, | ||
145 | Record, | ||
146 | Unit, | ||
147 | } | ||
148 | |||
149 | fn lower_struct( | ||
150 | trace: &mut Trace< | ||
151 | LocalStructFieldId, | ||
152 | StructFieldData, | ||
153 | Either<ast::TupleFieldDef, ast::RecordFieldDef>, | ||
154 | >, | ||
155 | ast: &ast::StructKind, | ||
156 | ) -> StructKind { | ||
157 | match ast { | ||
158 | ast::StructKind::Tuple(fl) => { | ||
159 | for (i, fd) in fl.fields().enumerate() { | ||
160 | trace.alloc( | ||
161 | || Either::A(fd.clone()), | ||
162 | || StructFieldData { | ||
163 | name: Name::new_tuple_field(i), | ||
164 | type_ref: TypeRef::from_ast_opt(fd.type_ref()), | ||
165 | }, | ||
166 | ) | ||
167 | } | ||
168 | StructKind::Tuple | ||
169 | } | ||
170 | ast::StructKind::Record(fl) => { | ||
171 | for fd in fl.fields() { | ||
172 | trace.alloc( | ||
173 | || Either::B(fd.clone()), | ||
174 | || StructFieldData { | ||
175 | name: fd.name().map(|n| n.as_name()).unwrap_or_else(Name::missing), | ||
176 | type_ref: TypeRef::from_ast_opt(fd.ascribed_type()), | ||
177 | }, | ||
178 | ) | ||
179 | } | ||
180 | StructKind::Record | ||
181 | } | ||
182 | ast::StructKind::Unit => StructKind::Unit, | ||
183 | } | ||
184 | } | ||
diff --git a/crates/ra_hir_def/src/lib.rs b/crates/ra_hir_def/src/lib.rs index 3a0420da0..2edf743ab 100644 --- a/crates/ra_hir_def/src/lib.rs +++ b/crates/ra_hir_def/src/lib.rs | |||
@@ -20,6 +20,8 @@ pub mod generics; | |||
20 | pub mod resolver; | 20 | pub mod resolver; |
21 | pub mod data; | 21 | pub mod data; |
22 | 22 | ||
23 | mod trace; | ||
24 | |||
23 | #[cfg(test)] | 25 | #[cfg(test)] |
24 | mod test_db; | 26 | mod test_db; |
25 | #[cfg(test)] | 27 | #[cfg(test)] |
@@ -31,7 +33,7 @@ pub mod nameres; | |||
31 | use std::hash::{Hash, Hasher}; | 33 | use std::hash::{Hash, Hasher}; |
32 | 34 | ||
33 | use hir_expand::{ast_id_map::FileAstId, db::AstDatabase, AstId, HirFileId, Source}; | 35 | use hir_expand::{ast_id_map::FileAstId, db::AstDatabase, AstId, HirFileId, Source}; |
34 | use ra_arena::{impl_arena_id, RawId}; | 36 | use ra_arena::{impl_arena_id, map::ArenaMap, RawId}; |
35 | use ra_db::{salsa, CrateId, FileId}; | 37 | use ra_db::{salsa, CrateId, FileId}; |
36 | use ra_syntax::{ast, AstNode, SyntaxNode}; | 38 | use ra_syntax::{ast, AstNode, SyntaxNode}; |
37 | 39 | ||
@@ -550,3 +552,12 @@ impl HasSource for ConstLoc { | |||
550 | Source::new(self.ast_id.file_id(), node) | 552 | Source::new(self.ast_id.file_id(), node) |
551 | } | 553 | } |
552 | } | 554 | } |
555 | |||
556 | pub trait HasChildSource { | ||
557 | type ChildId; | ||
558 | type Value; | ||
559 | fn child_source( | ||
560 | &self, | ||
561 | db: &impl db::DefDatabase2, | ||
562 | ) -> Source<ArenaMap<Self::ChildId, Self::Value>>; | ||
563 | } | ||
diff --git a/crates/ra_hir_def/src/trace.rs b/crates/ra_hir_def/src/trace.rs new file mode 100644 index 000000000..fc26f5a48 --- /dev/null +++ b/crates/ra_hir_def/src/trace.rs | |||
@@ -0,0 +1,49 @@ | |||
1 | //! Trace is a pretty niche data structure which is used when lowering a CST | ||
2 | //! into HIR. | ||
3 | //! | ||
4 | //! Lowering process calculates two bits of information: | ||
5 | //! * the lowered syntax itself | ||
6 | //! * a mapping between lowered syntax and original syntax | ||
7 | //! | ||
8 | //! Due to the way salsa works, the mapping is usually hot lava, as it contains | ||
9 | //! absolute offsets. The `Trace` structure (inspired, at least in name, by | ||
10 | //! Kotlin's `BindingTrace`) allows use the same code to compute both | ||
11 | //! projections. | ||
12 | use ra_arena::{map::ArenaMap, Arena, ArenaId, RawId}; | ||
13 | |||
14 | pub(crate) struct Trace<ID: ArenaId, T, V> { | ||
15 | for_arena: bool, | ||
16 | arena: Arena<ID, T>, | ||
17 | map: ArenaMap<ID, V>, | ||
18 | len: u32, | ||
19 | } | ||
20 | |||
21 | impl<ID: ra_arena::ArenaId, T, V> Trace<ID, T, V> { | ||
22 | pub(crate) fn new_for_arena() -> Trace<ID, T, V> { | ||
23 | Trace { for_arena: true, arena: Arena::default(), map: ArenaMap::default(), len: 0 } | ||
24 | } | ||
25 | |||
26 | pub(crate) fn new_for_map() -> Trace<ID, T, V> { | ||
27 | Trace { for_arena: false, arena: Arena::default(), map: ArenaMap::default(), len: 0 } | ||
28 | } | ||
29 | |||
30 | pub(crate) fn alloc(&mut self, value: impl Fn() -> V, data: impl Fn() -> T) { | ||
31 | if self.for_arena { | ||
32 | self.arena.alloc(data()); | ||
33 | } else { | ||
34 | let id = ID::from_raw(RawId::from(self.len)); | ||
35 | self.len += 1; | ||
36 | self.map.insert(id, value()); | ||
37 | } | ||
38 | } | ||
39 | |||
40 | pub(crate) fn into_arena(self) -> Arena<ID, T> { | ||
41 | assert!(self.for_arena); | ||
42 | self.arena | ||
43 | } | ||
44 | |||
45 | pub(crate) fn into_map(self) -> ArenaMap<ID, V> { | ||
46 | assert!(!self.for_arena); | ||
47 | self.map | ||
48 | } | ||
49 | } | ||