aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_hir_def/src
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_hir_def/src')
-rw-r--r--crates/ra_hir_def/src/adt.rs145
-rw-r--r--crates/ra_hir_def/src/lib.rs13
-rw-r--r--crates/ra_hir_def/src/trace.rs49
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
3use std::sync::Arc; 3use std::sync::Arc;
4 4
5use hir_expand::name::{AsName, Name}; 5use hir_expand::{
6use ra_arena::Arena; 6 either::Either,
7 name::{AsName, Name},
8 Source,
9};
10use ra_arena::{map::ArenaMap, Arena};
7use ra_syntax::ast::{self, NameOwner, TypeAscriptionOwner}; 11use ra_syntax::ast::{self, NameOwner, TypeAscriptionOwner};
8 12
9use crate::{ 13use 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
79impl 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
90fn 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
83impl VariantData { 105impl 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
123impl 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
143enum StructKind {
144 Tuple,
145 Record,
146 Unit,
147}
148
149fn 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;
20pub mod resolver; 20pub mod resolver;
21pub mod data; 21pub mod data;
22 22
23mod trace;
24
23#[cfg(test)] 25#[cfg(test)]
24mod test_db; 26mod test_db;
25#[cfg(test)] 27#[cfg(test)]
@@ -31,7 +33,7 @@ pub mod nameres;
31use std::hash::{Hash, Hasher}; 33use std::hash::{Hash, Hasher};
32 34
33use hir_expand::{ast_id_map::FileAstId, db::AstDatabase, AstId, HirFileId, Source}; 35use hir_expand::{ast_id_map::FileAstId, db::AstDatabase, AstId, HirFileId, Source};
34use ra_arena::{impl_arena_id, RawId}; 36use ra_arena::{impl_arena_id, map::ArenaMap, RawId};
35use ra_db::{salsa, CrateId, FileId}; 37use ra_db::{salsa, CrateId, FileId};
36use ra_syntax::{ast, AstNode, SyntaxNode}; 38use 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
556pub 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.
12use ra_arena::{map::ArenaMap, Arena, ArenaId, RawId};
13
14pub(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
21impl<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}