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 | |
parent | 5b19202e00fffe62a1a9c07f4b974f0affdd0c66 (diff) |
More principled sources for enums and fields
-rw-r--r-- | crates/ra_hir/src/code_model/src.rs | 50 | ||||
-rw-r--r-- | crates/ra_hir/src/from_id.rs | 19 | ||||
-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 |
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 | ||
3 | use hir_def::{HasSource as _, Lookup}; | 3 | use hir_def::{HasChildSource, HasSource as _, Lookup, VariantId}; |
4 | use ra_syntax::ast::{self, AstNode}; | 4 | use ra_syntax::ast::{self, AstNode}; |
5 | 5 | ||
6 | use crate::{ | 6 | use 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 | ||
13 | pub use hir_expand::Source; | 13 | pub use hir_expand::Source; |
@@ -46,33 +46,12 @@ impl Module { | |||
46 | impl HasSource for StructField { | 46 | impl 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 | } |
78 | impl HasSource for Struct { | 57 | impl HasSource for Struct { |
@@ -96,18 +75,7 @@ impl HasSource for Enum { | |||
96 | impl HasSource for EnumVariant { | 75 | impl 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 | } |
113 | impl HasSource for Function { | 81 | impl 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 | ||
6 | use hir_def::{ | 6 | use 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 | ||
11 | use crate::{ | 11 | use 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 | ||
17 | impl From<ra_db::CrateId> for Crate { | 17 | impl From<ra_db::CrateId> for Crate { |
@@ -70,6 +70,12 @@ impl From<EnumVariantId> for EnumVariant { | |||
70 | } | 70 | } |
71 | } | 71 | } |
72 | 72 | ||
73 | impl From<EnumVariant> for EnumVariantId { | ||
74 | fn from(def: EnumVariant) -> Self { | ||
75 | EnumVariantId { parent: def.parent.id, local_id: def.id } | ||
76 | } | ||
77 | } | ||
78 | |||
73 | impl From<ModuleDefId> for ModuleDef { | 79 | impl 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 | |||
229 | impl 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 | ||
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 | } | ||