rust/crates/ra_hir_def/src/adt.rs

182 lines
5.6 KiB
Rust
Raw Normal View History

2019-10-31 08:40:36 -05:00
//! Defines hir-level representation of structs, enums and unions
use std::sync::Arc;
use hir_expand::{
either::Either,
name::{AsName, Name},
Source,
};
use ra_arena::{map::ArenaMap, Arena};
2019-10-31 08:40:36 -05:00
use ra_syntax::ast::{self, NameOwner, TypeAscriptionOwner};
use crate::{
2019-11-23 05:44:43 -06:00
db::DefDatabase, trace::Trace, type_ref::TypeRef, AstItemDef, EnumId, HasChildSource,
LocalEnumVariantId, LocalStructFieldId, StructOrUnionId, VariantId,
2019-10-31 08:40:36 -05:00
};
/// Note that we use `StructData` for unions as well!
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct StructData {
pub name: Option<Name>,
pub variant_data: Arc<VariantData>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct EnumData {
pub name: Option<Name>,
pub variants: Arena<LocalEnumVariantId, EnumVariantData>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct EnumVariantData {
pub name: Option<Name>,
pub variant_data: Arc<VariantData>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
2019-11-22 12:52:06 -06:00
pub enum VariantData {
Record(Arena<LocalStructFieldId, StructFieldData>),
2019-10-31 08:40:36 -05:00
Tuple(Arena<LocalStructFieldId, StructFieldData>),
Unit,
}
/// A single field of an enum variant or struct
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct StructFieldData {
pub name: Name,
pub type_ref: TypeRef,
}
impl StructData {
2019-11-23 05:44:43 -06:00
pub(crate) fn struct_data_query(db: &impl DefDatabase, id: StructOrUnionId) -> Arc<StructData> {
2019-11-09 06:34:00 -06:00
let src = id.source(db);
2019-11-20 00:40:36 -06:00
let name = src.value.name().map(|n| n.as_name());
let variant_data = VariantData::new(src.value.kind());
2019-10-31 10:45:10 -05:00
let variant_data = Arc::new(variant_data);
Arc::new(StructData { name, variant_data })
}
2019-10-31 08:40:36 -05:00
}
impl EnumData {
2019-11-23 05:44:43 -06:00
pub(crate) fn enum_data_query(db: &impl DefDatabase, e: EnumId) -> Arc<EnumData> {
2019-10-31 08:40:36 -05:00
let src = e.source(db);
2019-11-20 00:40:36 -06:00
let name = src.value.name().map(|n| n.as_name());
let mut trace = Trace::new_for_arena();
lower_enum(&mut trace, &src.value);
Arc::new(EnumData { name, variants: trace.into_arena() })
2019-10-31 08:40:36 -05:00
}
2019-10-31 10:45:10 -05:00
pub(crate) fn variant(&self, name: &Name) -> Option<LocalEnumVariantId> {
let (id, _) = self.variants.iter().find(|(_id, data)| data.name.as_ref() == Some(name))?;
Some(id)
}
2019-10-31 08:40:36 -05:00
}
impl HasChildSource for EnumId {
type ChildId = LocalEnumVariantId;
type Value = ast::EnumVariant;
2019-11-23 05:44:43 -06:00
fn child_source(&self, db: &impl DefDatabase) -> Source<ArenaMap<Self::ChildId, Self::Value>> {
let src = self.source(db);
let mut trace = Trace::new_for_map();
lower_enum(&mut trace, &src.value);
src.with_value(trace.into_map())
}
}
fn lower_enum(
trace: &mut Trace<LocalEnumVariantId, EnumVariantData, ast::EnumVariant>,
ast: &ast::EnumDef,
) {
for var in ast.variant_list().into_iter().flat_map(|it| it.variants()) {
trace.alloc(
|| var.clone(),
|| EnumVariantData {
name: var.name().map(|it| it.as_name()),
variant_data: Arc::new(VariantData::new(var.kind())),
},
2019-11-24 08:49:49 -06:00
);
}
}
2019-10-31 08:40:36 -05:00
impl VariantData {
fn new(flavor: ast::StructKind) -> Self {
let mut trace = Trace::new_for_arena();
match lower_struct(&mut trace, &flavor) {
StructKind::Tuple => VariantData::Tuple(trace.into_arena()),
StructKind::Record => VariantData::Record(trace.into_arena()),
StructKind::Unit => VariantData::Unit,
2019-11-22 12:52:06 -06:00
}
2019-10-31 08:40:36 -05:00
}
pub fn fields(&self) -> Option<&Arena<LocalStructFieldId, StructFieldData>> {
match &self {
2019-11-22 12:52:06 -06:00
VariantData::Record(fields) | VariantData::Tuple(fields) => Some(fields),
2019-10-31 08:40:36 -05:00
_ => None,
}
}
}
impl HasChildSource for VariantId {
type ChildId = LocalStructFieldId;
type Value = Either<ast::TupleFieldDef, ast::RecordFieldDef>;
2019-11-23 05:44:43 -06:00
fn child_source(&self, db: &impl DefDatabase) -> Source<ArenaMap<Self::ChildId, Self::Value>> {
let src = match self {
VariantId::EnumVariantId(it) => {
// I don't really like the fact that we call into parent source
// here, this might add to more queries then necessary.
let src = it.parent.child_source(db);
src.map(|map| map[it.local_id].kind())
}
VariantId::StructId(it) => it.0.source(db).map(|it| it.kind()),
};
let mut trace = Trace::new_for_map();
lower_struct(&mut trace, &src.value);
src.with_value(trace.into_map())
}
}
enum StructKind {
Tuple,
Record,
Unit,
}
fn lower_struct(
trace: &mut Trace<
LocalStructFieldId,
StructFieldData,
Either<ast::TupleFieldDef, ast::RecordFieldDef>,
>,
ast: &ast::StructKind,
) -> StructKind {
match ast {
ast::StructKind::Tuple(fl) => {
for (i, fd) in fl.fields().enumerate() {
trace.alloc(
|| Either::A(fd.clone()),
|| StructFieldData {
name: Name::new_tuple_field(i),
type_ref: TypeRef::from_ast_opt(fd.type_ref()),
},
2019-11-24 08:49:49 -06:00
);
}
StructKind::Tuple
}
ast::StructKind::Record(fl) => {
for fd in fl.fields() {
trace.alloc(
|| Either::B(fd.clone()),
|| StructFieldData {
name: fd.name().map(|n| n.as_name()).unwrap_or_else(Name::missing),
type_ref: TypeRef::from_ast_opt(fd.ascribed_type()),
},
2019-11-24 08:49:49 -06:00
);
}
StructKind::Record
}
ast::StructKind::Unit => StructKind::Unit,
}
}