rust-analyzer/crates/ra_hir_def/src/adt.rs

255 lines
8.5 KiB
Rust
Raw Normal View History

2019-10-31 13:40:36 +00:00
//! Defines hir-level representation of structs, enums and unions
use std::sync::Arc;
use either::Either;
use hir_expand::{
name::{AsName, Name},
2019-11-28 09:50:26 +00:00
InFile,
};
use ra_arena::{map::ArenaMap, Arena};
2019-12-21 18:40:20 +00:00
use ra_prof::profile;
use ra_syntax::ast::{self, NameOwner, TypeAscriptionOwner, VisibilityOwner};
2019-10-31 13:40:36 +00:00
use crate::{
2020-04-30 10:20:13 +00:00
body::{CfgExpander, LowerCtx},
db::DefDatabase,
src::HasChildSource,
src::HasSource,
trace::Trace,
type_ref::TypeRef,
visibility::RawVisibility,
EnumId, HasModule, LocalEnumVariantId, LocalFieldId, Lookup, ModuleId, StructId, UnionId,
VariantId,
2019-10-31 13:40:36 +00:00
};
/// Note that we use `StructData` for unions as well!
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct StructData {
2019-11-27 20:22:20 +00:00
pub name: Name,
2019-10-31 13:40:36 +00:00
pub variant_data: Arc<VariantData>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct EnumData {
2019-11-27 20:22:20 +00:00
pub name: Name,
2020-03-19 15:00:11 +00:00
pub variants: Arena<EnumVariantData>,
2019-10-31 13:40:36 +00:00
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct EnumVariantData {
2019-11-27 20:22:20 +00:00
pub name: Name,
2019-10-31 13:40:36 +00:00
pub variant_data: Arc<VariantData>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
2019-11-22 18:52:06 +00:00
pub enum VariantData {
2020-04-25 12:23:34 +00:00
Record(Arena<FieldData>),
Tuple(Arena<FieldData>),
2019-10-31 13:40:36 +00:00
Unit,
}
/// A single field of an enum variant or struct
#[derive(Debug, Clone, PartialEq, Eq)]
2020-04-25 12:23:34 +00:00
pub struct FieldData {
2019-10-31 13:40:36 +00:00
pub name: Name,
pub type_ref: TypeRef,
pub visibility: RawVisibility,
2019-10-31 13:40:36 +00:00
}
impl StructData {
pub(crate) fn struct_data_query(db: &dyn DefDatabase, id: StructId) -> Arc<StructData> {
2019-12-12 13:58:04 +00:00
let src = id.lookup(db).source(db);
2019-11-27 20:22:20 +00:00
let name = src.value.name().map_or_else(Name::missing, |n| n.as_name());
let variant_data =
VariantData::new(db, src.map(|s| s.kind()), id.lookup(db).container.module(db));
2019-10-31 15:45:10 +00:00
let variant_data = Arc::new(variant_data);
Arc::new(StructData { name, variant_data })
}
pub(crate) fn union_data_query(db: &dyn DefDatabase, id: UnionId) -> Arc<StructData> {
2019-12-12 14:11:57 +00:00
let src = id.lookup(db).source(db);
2019-11-27 20:22:20 +00:00
let name = src.value.name().map_or_else(Name::missing, |n| n.as_name());
2019-11-25 14:30:50 +00:00
let variant_data = VariantData::new(
db,
src.map(|s| {
s.record_field_def_list()
.map(ast::StructKind::Record)
.unwrap_or(ast::StructKind::Unit)
}),
id.lookup(db).container.module(db),
2019-11-25 14:30:50 +00:00
);
let variant_data = Arc::new(variant_data);
Arc::new(StructData { name, variant_data })
}
2019-10-31 13:40:36 +00:00
}
impl EnumData {
pub(crate) fn enum_data_query(db: &dyn DefDatabase, e: EnumId) -> Arc<EnumData> {
2019-12-21 18:40:20 +00:00
let _p = profile("enum_data_query");
2019-12-12 14:11:57 +00:00
let src = e.lookup(db).source(db);
2019-11-27 20:22:20 +00:00
let name = src.value.name().map_or_else(Name::missing, |n| n.as_name());
let mut trace = Trace::new_for_arena();
lower_enum(db, &mut trace, &src, e.lookup(db).container.module(db));
Arc::new(EnumData { name, variants: trace.into_arena() })
2019-10-31 13:40:36 +00:00
}
2019-10-31 15:45:10 +00:00
2019-11-27 20:22:20 +00:00
pub fn variant(&self, name: &Name) -> Option<LocalEnumVariantId> {
let (id, _) = self.variants.iter().find(|(_id, data)| &data.name == name)?;
2019-10-31 15:45:10 +00:00
Some(id)
}
2019-10-31 13:40:36 +00:00
}
impl HasChildSource for EnumId {
type ChildId = LocalEnumVariantId;
type Value = ast::EnumVariant;
fn child_source(&self, db: &dyn DefDatabase) -> InFile<ArenaMap<Self::ChildId, Self::Value>> {
2019-12-12 14:11:57 +00:00
let src = self.lookup(db).source(db);
let mut trace = Trace::new_for_map();
lower_enum(db, &mut trace, &src, self.lookup(db).container.module(db));
src.with_value(trace.into_map())
}
}
fn lower_enum(
db: &dyn DefDatabase,
2020-03-19 15:00:11 +00:00
trace: &mut Trace<EnumVariantData, ast::EnumVariant>,
ast: &InFile<ast::EnumDef>,
module_id: ModuleId,
) {
let expander = CfgExpander::new(db, ast.file_id, module_id.krate);
let variants =
ast.value.variant_list().into_iter().flat_map(|it| it.variants()).filter(|var| {
let attrs = expander.parse_attrs(var);
expander.is_cfg_enabled(&attrs)
});
for var in variants {
trace.alloc(
|| var.clone(),
|| EnumVariantData {
2019-11-27 20:22:20 +00:00
name: var.name().map_or_else(Name::missing, |it| it.as_name()),
variant_data: Arc::new(VariantData::new(db, ast.with_value(var.kind()), module_id)),
},
2019-11-24 14:49:49 +00:00
);
}
}
2019-10-31 13:40:36 +00:00
impl VariantData {
fn new(db: &dyn DefDatabase, flavor: InFile<ast::StructKind>, module_id: ModuleId) -> Self {
let mut expander = CfgExpander::new(db, flavor.file_id, module_id.krate);
let mut trace = Trace::new_for_arena();
match lower_struct(db, &mut expander, &mut trace, &flavor) {
StructKind::Tuple => VariantData::Tuple(trace.into_arena()),
StructKind::Record => VariantData::Record(trace.into_arena()),
StructKind::Unit => VariantData::Unit,
2019-11-22 18:52:06 +00:00
}
2019-10-31 13:40:36 +00:00
}
2020-04-25 12:23:34 +00:00
pub fn fields(&self) -> &Arena<FieldData> {
const EMPTY: &Arena<FieldData> = &Arena::new();
match &self {
2019-11-24 19:44:24 +00:00
VariantData::Record(fields) | VariantData::Tuple(fields) => fields,
_ => EMPTY,
}
}
2020-04-25 12:23:34 +00:00
pub fn field(&self, name: &Name) -> Option<LocalFieldId> {
2019-11-26 11:29:12 +00:00
self.fields().iter().find_map(|(id, data)| if &data.name == name { Some(id) } else { None })
}
pub fn kind(&self) -> StructKind {
match self {
VariantData::Record(_) => StructKind::Record,
VariantData::Tuple(_) => StructKind::Tuple,
VariantData::Unit => StructKind::Unit,
}
}
2019-10-31 13:40:36 +00:00
}
impl HasChildSource for VariantId {
2020-04-25 12:23:34 +00:00
type ChildId = LocalFieldId;
type Value = Either<ast::TupleFieldDef, ast::RecordFieldDef>;
fn child_source(&self, db: &dyn DefDatabase) -> InFile<ArenaMap<Self::ChildId, Self::Value>> {
let (src, module_id) = 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()), it.parent.lookup(db).container.module(db))
}
VariantId::StructId(it) => {
(it.lookup(db).source(db).map(|it| it.kind()), it.lookup(db).container.module(db))
}
VariantId::UnionId(it) => (
it.lookup(db).source(db).map(|it| {
it.record_field_def_list()
.map(ast::StructKind::Record)
.unwrap_or(ast::StructKind::Unit)
}),
it.lookup(db).container.module(db),
),
};
let mut expander = CfgExpander::new(db, src.file_id, module_id.krate);
let mut trace = Trace::new_for_map();
lower_struct(db, &mut expander, &mut trace, &src);
src.with_value(trace.into_map())
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum StructKind {
Tuple,
Record,
Unit,
}
fn lower_struct(
db: &dyn DefDatabase,
expander: &mut CfgExpander,
2020-04-25 12:23:34 +00:00
trace: &mut Trace<FieldData, Either<ast::TupleFieldDef, ast::RecordFieldDef>>,
ast: &InFile<ast::StructKind>,
) -> StructKind {
2020-04-30 10:20:13 +00:00
let ctx = LowerCtx::new(db, ast.file_id);
match &ast.value {
ast::StructKind::Tuple(fl) => {
for (i, fd) in fl.fields().enumerate() {
let attrs = expander.parse_attrs(&fd);
2020-04-11 15:20:26 +00:00
if !expander.is_cfg_enabled(&attrs) {
continue;
}
trace.alloc(
|| Either::Left(fd.clone()),
2020-04-25 12:23:34 +00:00
|| FieldData {
name: Name::new_tuple_field(i),
2020-04-30 10:20:13 +00:00
type_ref: TypeRef::from_ast_opt(&ctx, fd.type_ref()),
visibility: RawVisibility::from_ast(db, ast.with_value(fd.visibility())),
},
2019-11-24 14:49:49 +00:00
);
}
StructKind::Tuple
}
ast::StructKind::Record(fl) => {
for fd in fl.fields() {
let attrs = expander.parse_attrs(&fd);
2020-04-11 15:20:26 +00:00
if !expander.is_cfg_enabled(&attrs) {
continue;
}
trace.alloc(
|| Either::Right(fd.clone()),
2020-04-25 12:23:34 +00:00
|| FieldData {
name: fd.name().map(|n| n.as_name()).unwrap_or_else(Name::missing),
2020-04-30 10:20:13 +00:00
type_ref: TypeRef::from_ast_opt(&ctx, fd.ascribed_type()),
visibility: RawVisibility::from_ast(db, ast.with_value(fd.visibility())),
},
2019-11-24 14:49:49 +00:00
);
}
StructKind::Record
}
ast::StructKind::Unit => StructKind::Unit,
}
}