aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAleksey Kladov <[email protected]>2019-11-22 18:43:36 +0000
committerAleksey Kladov <[email protected]>2019-11-22 20:09:17 +0000
commit0f415dd4b30289117fe76d071293e9bdd3556336 (patch)
tree908faccf9e77fcb1d2804da3289ebc4d026ce339
parent5b19202e00fffe62a1a9c07f4b974f0affdd0c66 (diff)
More principled sources for enums and fields
-rw-r--r--crates/ra_hir/src/code_model/src.rs50
-rw-r--r--crates/ra_hir/src/from_id.rs19
-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
5 files changed, 193 insertions, 83 deletions
diff --git a/crates/ra_hir/src/code_model/src.rs b/crates/ra_hir/src/code_model/src.rs
index fc466c1f0..a45c062bf 100644
--- a/crates/ra_hir/src/code_model/src.rs
+++ b/crates/ra_hir/src/code_model/src.rs
@@ -1,13 +1,13 @@
1//! FIXME: write short doc here 1//! FIXME: write short doc here
2 2
3use hir_def::{HasSource as _, Lookup}; 3use hir_def::{HasChildSource, HasSource as _, Lookup, VariantId};
4use ra_syntax::ast::{self, AstNode}; 4use ra_syntax::ast::{self, AstNode};
5 5
6use crate::{ 6use crate::{
7 db::{AstDatabase, DefDatabase, HirDatabase}, 7 db::{AstDatabase, DefDatabase, HirDatabase},
8 ids::AstItemDef, 8 ids::AstItemDef,
9 Const, Either, Enum, EnumVariant, FieldSource, Function, HasBody, HirFileId, MacroDef, Module, 9 Const, Either, Enum, EnumVariant, FieldSource, Function, HasBody, HirFileId, MacroDef, Module,
10 ModuleSource, Static, Struct, StructField, Trait, TypeAlias, Union, VariantDef, 10 ModuleSource, Static, Struct, StructField, Trait, TypeAlias, Union,
11}; 11};
12 12
13pub use hir_expand::Source; 13pub use hir_expand::Source;
@@ -46,33 +46,12 @@ impl Module {
46impl HasSource for StructField { 46impl HasSource for StructField {
47 type Ast = FieldSource; 47 type Ast = FieldSource;
48 fn source(self, db: &(impl DefDatabase + AstDatabase)) -> Source<FieldSource> { 48 fn source(self, db: &(impl DefDatabase + AstDatabase)) -> Source<FieldSource> {
49 let var_data = self.parent.variant_data(db); 49 let var = VariantId::from(self.parent);
50 let fields = var_data.fields().unwrap(); 50 let src = var.child_source(db);
51 let ss; 51 src.map(|it| match it[self.id].clone() {
52 let es; 52 Either::A(it) => FieldSource::Pos(it),
53 let (file_id, struct_kind) = match self.parent { 53 Either::B(it) => FieldSource::Named(it),
54 VariantDef::Struct(s) => { 54 })
55 ss = s.source(db);
56 (ss.file_id, ss.value.kind())
57 }
58 VariantDef::EnumVariant(e) => {
59 es = e.source(db);
60 (es.file_id, es.value.kind())
61 }
62 };
63
64 let field_sources = match struct_kind {
65 ast::StructKind::Tuple(fl) => fl.fields().map(|it| FieldSource::Pos(it)).collect(),
66 ast::StructKind::Record(fl) => fl.fields().map(|it| FieldSource::Named(it)).collect(),
67 ast::StructKind::Unit => Vec::new(),
68 };
69 let value = field_sources
70 .into_iter()
71 .zip(fields.iter())
72 .find(|(_syntax, (id, _))| *id == self.id)
73 .unwrap()
74 .0;
75 Source { file_id, value }
76 } 55 }
77} 56}
78impl HasSource for Struct { 57impl HasSource for Struct {
@@ -96,18 +75,7 @@ impl HasSource for Enum {
96impl HasSource for EnumVariant { 75impl HasSource for EnumVariant {
97 type Ast = ast::EnumVariant; 76 type Ast = ast::EnumVariant;
98 fn source(self, db: &(impl DefDatabase + AstDatabase)) -> Source<ast::EnumVariant> { 77 fn source(self, db: &(impl DefDatabase + AstDatabase)) -> Source<ast::EnumVariant> {
99 let enum_data = db.enum_data(self.parent.id); 78 self.parent.id.child_source(db).map(|map| map[self.id].clone())
100 let src = self.parent.id.source(db);
101 let value = src
102 .value
103 .variant_list()
104 .into_iter()
105 .flat_map(|it| it.variants())
106 .zip(enum_data.variants.iter())
107 .find(|(_syntax, (id, _))| *id == self.id)
108 .unwrap()
109 .0;
110 Source { file_id: src.file_id, value }
111 } 79 }
112} 80}
113impl HasSource for Function { 81impl HasSource for Function {
diff --git a/crates/ra_hir/src/from_id.rs b/crates/ra_hir/src/from_id.rs
index e8ed04056..8900fc1f2 100644
--- a/crates/ra_hir/src/from_id.rs
+++ b/crates/ra_hir/src/from_id.rs
@@ -5,13 +5,13 @@
5 5
6use hir_def::{ 6use hir_def::{
7 AdtId, AssocItemId, ConstId, DefWithBodyId, EnumId, EnumVariantId, FunctionId, GenericDefId, 7 AdtId, AssocItemId, ConstId, DefWithBodyId, EnumId, EnumVariantId, FunctionId, GenericDefId,
8 ModuleDefId, StaticId, StructId, TypeAliasId, UnionId, 8 ModuleDefId, StaticId, StructId, TypeAliasId, UnionId, VariantId,
9}; 9};
10 10
11use crate::{ 11use crate::{
12 ty::{CallableDef, TypableDef}, 12 ty::{CallableDef, TypableDef},
13 Adt, AssocItem, Const, Crate, DefWithBody, EnumVariant, Function, GenericDef, ModuleDef, 13 Adt, AssocItem, Const, Crate, DefWithBody, EnumVariant, Function, GenericDef, ModuleDef,
14 Static, TypeAlias, 14 Static, TypeAlias, VariantDef,
15}; 15};
16 16
17impl From<ra_db::CrateId> for Crate { 17impl From<ra_db::CrateId> for Crate {
@@ -70,6 +70,12 @@ impl From<EnumVariantId> for EnumVariant {
70 } 70 }
71} 71}
72 72
73impl From<EnumVariant> for EnumVariantId {
74 fn from(def: EnumVariant) -> Self {
75 EnumVariantId { parent: def.parent.id, local_id: def.id }
76 }
77}
78
73impl From<ModuleDefId> for ModuleDef { 79impl From<ModuleDefId> for ModuleDef {
74 fn from(id: ModuleDefId) -> Self { 80 fn from(id: ModuleDefId) -> Self {
75 match id { 81 match id {
@@ -219,3 +225,12 @@ impl From<CallableDef> for GenericDefId {
219 } 225 }
220 } 226 }
221} 227}
228
229impl From<VariantDef> for VariantId {
230 fn from(def: VariantDef) -> Self {
231 match def {
232 VariantDef::Struct(it) => VariantId::StructId(it.id),
233 VariantDef::EnumVariant(it) => VariantId::EnumVariantId(it.into()),
234 }
235 }
236}
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}