2019-10-31 08:40:36 -05:00
|
|
|
//! Defines hir-level representation of structs, enums and unions
|
|
|
|
|
|
|
|
use std::sync::Arc;
|
|
|
|
|
2019-12-03 10:07:56 -06:00
|
|
|
use either::Either;
|
2019-11-22 12:43:36 -06:00
|
|
|
use hir_expand::{
|
|
|
|
name::{AsName, Name},
|
2019-11-28 03:50:26 -06:00
|
|
|
InFile,
|
2019-11-22 12:43:36 -06:00
|
|
|
};
|
|
|
|
use ra_arena::{map::ArenaMap, Arena};
|
2020-07-30 13:51:43 -05:00
|
|
|
use ra_syntax::ast::{self, NameOwner, VisibilityOwner};
|
2020-06-03 22:38:25 -05:00
|
|
|
use tt::{Delimiter, DelimiterKind, Leaf, Subtree, TokenTree};
|
2019-10-31 08:40:36 -05:00
|
|
|
|
|
|
|
use crate::{
|
2020-06-03 22:38:25 -05:00
|
|
|
attr::AttrInput,
|
2020-04-30 05:20:13 -05:00
|
|
|
body::{CfgExpander, LowerCtx},
|
|
|
|
db::DefDatabase,
|
2020-06-03 22:38:25 -05:00
|
|
|
item_tree::{AttrOwner, Field, Fields, ItemTree, ModItem},
|
2020-04-30 05:20:13 -05:00
|
|
|
src::HasChildSource,
|
|
|
|
src::HasSource,
|
|
|
|
trace::Trace,
|
|
|
|
type_ref::TypeRef,
|
|
|
|
visibility::RawVisibility,
|
|
|
|
EnumId, HasModule, LocalEnumVariantId, LocalFieldId, Lookup, ModuleId, StructId, UnionId,
|
|
|
|
VariantId,
|
2019-10-31 08:40:36 -05:00
|
|
|
};
|
2020-06-25 09:41:08 -05:00
|
|
|
use ra_cfg::CfgOptions;
|
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 {
|
2019-11-27 14:22:20 -06:00
|
|
|
pub name: Name,
|
2019-10-31 08:40:36 -05:00
|
|
|
pub variant_data: Arc<VariantData>,
|
2020-06-03 22:38:25 -05:00
|
|
|
pub repr: Option<ReprKind>,
|
2019-10-31 08:40:36 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
|
|
|
pub struct EnumData {
|
2019-11-27 14:22:20 -06:00
|
|
|
pub name: Name,
|
2020-03-19 10:00:11 -05:00
|
|
|
pub variants: Arena<EnumVariantData>,
|
2019-10-31 08:40:36 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
|
|
|
pub struct EnumVariantData {
|
2019-11-27 14:22:20 -06:00
|
|
|
pub name: Name,
|
2019-10-31 08:40:36 -05:00
|
|
|
pub variant_data: Arc<VariantData>,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
2019-11-22 12:52:06 -06:00
|
|
|
pub enum VariantData {
|
2020-04-25 07:23:34 -05:00
|
|
|
Record(Arena<FieldData>),
|
|
|
|
Tuple(Arena<FieldData>),
|
2019-10-31 08:40:36 -05:00
|
|
|
Unit,
|
|
|
|
}
|
|
|
|
|
|
|
|
/// A single field of an enum variant or struct
|
|
|
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
2020-04-25 07:23:34 -05:00
|
|
|
pub struct FieldData {
|
2019-10-31 08:40:36 -05:00
|
|
|
pub name: Name,
|
|
|
|
pub type_ref: TypeRef,
|
2019-12-26 09:22:15 -06:00
|
|
|
pub visibility: RawVisibility,
|
2019-10-31 08:40:36 -05:00
|
|
|
}
|
|
|
|
|
2020-06-03 22:38:25 -05:00
|
|
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
|
|
|
pub enum ReprKind {
|
|
|
|
Packed,
|
|
|
|
Other,
|
|
|
|
}
|
|
|
|
|
|
|
|
fn repr_from_value(item_tree: &ItemTree, of: AttrOwner) -> Option<ReprKind> {
|
|
|
|
item_tree.attrs(of).iter().find_map(|a| {
|
|
|
|
if a.path.segments[0].to_string() == "repr" {
|
|
|
|
if let Some(AttrInput::TokenTree(subtree)) = &a.input {
|
|
|
|
parse_repr_tt(subtree)
|
|
|
|
} else {
|
|
|
|
None
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
None
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
fn parse_repr_tt(tt: &Subtree) -> Option<ReprKind> {
|
|
|
|
match tt.delimiter {
|
|
|
|
Some(Delimiter { kind: DelimiterKind::Parenthesis, .. }) => {}
|
|
|
|
_ => return None,
|
|
|
|
}
|
|
|
|
|
|
|
|
let mut it = tt.token_trees.iter();
|
|
|
|
match it.next() {
|
|
|
|
None => None,
|
|
|
|
Some(TokenTree::Leaf(Leaf::Ident(ident))) if ident.text == "packed" => {
|
|
|
|
Some(ReprKind::Packed)
|
|
|
|
}
|
|
|
|
_ => Some(ReprKind::Other),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-10-31 08:40:36 -05:00
|
|
|
impl StructData {
|
2020-03-13 10:05:46 -05:00
|
|
|
pub(crate) fn struct_data_query(db: &dyn DefDatabase, id: StructId) -> Arc<StructData> {
|
2020-06-25 09:41:08 -05:00
|
|
|
let loc = id.lookup(db);
|
|
|
|
let item_tree = db.item_tree(loc.id.file_id);
|
2020-06-03 22:38:25 -05:00
|
|
|
let repr = repr_from_value(&item_tree, ModItem::from(loc.id.value).into());
|
2020-06-25 09:41:08 -05:00
|
|
|
let cfg_options = db.crate_graph()[loc.container.module(db).krate].cfg_options.clone();
|
2020-04-07 10:58:05 -05:00
|
|
|
|
2020-06-25 09:41:08 -05:00
|
|
|
let strukt = &item_tree[loc.id.value];
|
|
|
|
let variant_data = lower_fields(&item_tree, &cfg_options, &strukt.fields);
|
2020-06-03 22:38:25 -05:00
|
|
|
Arc::new(StructData {
|
|
|
|
name: strukt.name.clone(),
|
|
|
|
variant_data: Arc::new(variant_data),
|
|
|
|
repr,
|
|
|
|
})
|
2019-10-31 10:45:10 -05:00
|
|
|
}
|
2020-03-13 10:05:46 -05:00
|
|
|
pub(crate) fn union_data_query(db: &dyn DefDatabase, id: UnionId) -> Arc<StructData> {
|
2020-06-25 09:41:08 -05:00
|
|
|
let loc = id.lookup(db);
|
|
|
|
let item_tree = db.item_tree(loc.id.file_id);
|
2020-06-03 22:38:25 -05:00
|
|
|
let repr = repr_from_value(&item_tree, ModItem::from(loc.id.value).into());
|
2020-06-25 09:41:08 -05:00
|
|
|
let cfg_options = db.crate_graph()[loc.container.module(db).krate].cfg_options.clone();
|
|
|
|
|
|
|
|
let union = &item_tree[loc.id.value];
|
|
|
|
let variant_data = lower_fields(&item_tree, &cfg_options, &union.fields);
|
|
|
|
|
2020-06-03 22:38:25 -05:00
|
|
|
Arc::new(StructData {
|
|
|
|
name: union.name.clone(),
|
|
|
|
variant_data: Arc::new(variant_data),
|
|
|
|
repr,
|
|
|
|
})
|
2019-11-25 08:30:50 -06:00
|
|
|
}
|
2019-10-31 08:40:36 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
impl EnumData {
|
2020-03-13 10:05:46 -05:00
|
|
|
pub(crate) fn enum_data_query(db: &dyn DefDatabase, e: EnumId) -> Arc<EnumData> {
|
2020-06-25 09:52:47 -05:00
|
|
|
let loc = e.lookup(db);
|
|
|
|
let item_tree = db.item_tree(loc.id.file_id);
|
|
|
|
let cfg_options = db.crate_graph()[loc.container.module(db).krate].cfg_options.clone();
|
|
|
|
|
|
|
|
let enum_ = &item_tree[loc.id.value];
|
|
|
|
let mut variants = Arena::new();
|
|
|
|
for var_id in enum_.variants.clone() {
|
|
|
|
if item_tree.attrs(var_id.into()).is_cfg_enabled(&cfg_options) {
|
|
|
|
let var = &item_tree[var_id];
|
|
|
|
let var_data = lower_fields(&item_tree, &cfg_options, &var.fields);
|
|
|
|
|
|
|
|
variants.alloc(EnumVariantData {
|
|
|
|
name: var.name.clone(),
|
|
|
|
variant_data: Arc::new(var_data),
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Arc::new(EnumData { name: enum_.name.clone(), variants })
|
2019-10-31 08:40:36 -05:00
|
|
|
}
|
2019-10-31 10:45:10 -05:00
|
|
|
|
2019-11-27 14:22:20 -06:00
|
|
|
pub fn variant(&self, name: &Name) -> Option<LocalEnumVariantId> {
|
|
|
|
let (id, _) = self.variants.iter().find(|(_id, data)| &data.name == name)?;
|
2019-10-31 10:45:10 -05:00
|
|
|
Some(id)
|
|
|
|
}
|
2019-10-31 08:40:36 -05:00
|
|
|
}
|
|
|
|
|
2019-11-22 12:43:36 -06:00
|
|
|
impl HasChildSource for EnumId {
|
|
|
|
type ChildId = LocalEnumVariantId;
|
2020-07-30 10:56:53 -05:00
|
|
|
type Value = ast::Variant;
|
2020-03-13 10:05:46 -05:00
|
|
|
fn child_source(&self, db: &dyn DefDatabase) -> InFile<ArenaMap<Self::ChildId, Self::Value>> {
|
2019-12-12 08:11:57 -06:00
|
|
|
let src = self.lookup(db).source(db);
|
2019-11-22 12:43:36 -06:00
|
|
|
let mut trace = Trace::new_for_map();
|
2020-04-09 11:32:02 -05:00
|
|
|
lower_enum(db, &mut trace, &src, self.lookup(db).container.module(db));
|
2019-11-22 12:43:36 -06:00
|
|
|
src.with_value(trace.into_map())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn lower_enum(
|
2020-03-13 10:05:46 -05:00
|
|
|
db: &dyn DefDatabase,
|
2020-07-30 10:56:53 -05:00
|
|
|
trace: &mut Trace<EnumVariantData, ast::Variant>,
|
2020-07-30 10:52:53 -05:00
|
|
|
ast: &InFile<ast::Enum>,
|
2020-04-09 11:32:02 -05:00
|
|
|
module_id: ModuleId,
|
2019-11-22 12:43:36 -06:00
|
|
|
) {
|
2020-05-03 10:56:45 -05:00
|
|
|
let expander = CfgExpander::new(db, ast.file_id, module_id.krate);
|
2020-05-05 02:34:07 -05:00
|
|
|
let variants = ast
|
|
|
|
.value
|
|
|
|
.variant_list()
|
|
|
|
.into_iter()
|
|
|
|
.flat_map(|it| it.variants())
|
|
|
|
.filter(|var| expander.is_cfg_enabled(var));
|
2020-05-03 10:56:45 -05:00
|
|
|
for var in variants {
|
2019-11-22 12:43:36 -06:00
|
|
|
trace.alloc(
|
|
|
|
|| var.clone(),
|
|
|
|
|| EnumVariantData {
|
2019-11-27 14:22:20 -06:00
|
|
|
name: var.name().map_or_else(Name::missing, |it| it.as_name()),
|
2020-04-09 11:32:02 -05:00
|
|
|
variant_data: Arc::new(VariantData::new(db, ast.with_value(var.kind()), module_id)),
|
2019-11-22 12:43:36 -06:00
|
|
|
},
|
2019-11-24 08:49:49 -06:00
|
|
|
);
|
2019-11-22 12:43:36 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-10-31 08:40:36 -05:00
|
|
|
impl VariantData {
|
2020-04-09 11:32:02 -05:00
|
|
|
fn new(db: &dyn DefDatabase, flavor: InFile<ast::StructKind>, module_id: ModuleId) -> Self {
|
2020-04-11 10:52:26 -05:00
|
|
|
let mut expander = CfgExpander::new(db, flavor.file_id, module_id.krate);
|
2019-11-22 12:43:36 -06:00
|
|
|
let mut trace = Trace::new_for_arena();
|
2020-04-11 10:17:12 -05:00
|
|
|
match lower_struct(db, &mut expander, &mut trace, &flavor) {
|
2019-11-22 12:43:36 -06:00
|
|
|
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
|
|
|
}
|
|
|
|
|
2020-04-25 07:23:34 -05:00
|
|
|
pub fn fields(&self) -> &Arena<FieldData> {
|
|
|
|
const EMPTY: &Arena<FieldData> = &Arena::new();
|
2019-11-22 12:43:36 -06:00
|
|
|
match &self {
|
2019-11-24 13:44:24 -06:00
|
|
|
VariantData::Record(fields) | VariantData::Tuple(fields) => fields,
|
|
|
|
_ => EMPTY,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-04-25 07:23:34 -05:00
|
|
|
pub fn field(&self, name: &Name) -> Option<LocalFieldId> {
|
2019-11-26 05:29:12 -06:00
|
|
|
self.fields().iter().find_map(|(id, data)| if &data.name == name { Some(id) } else { None })
|
|
|
|
}
|
|
|
|
|
2020-02-15 14:48:20 -06:00
|
|
|
pub fn kind(&self) -> StructKind {
|
|
|
|
match self {
|
|
|
|
VariantData::Record(_) => StructKind::Record,
|
|
|
|
VariantData::Tuple(_) => StructKind::Tuple,
|
|
|
|
VariantData::Unit => StructKind::Unit,
|
|
|
|
}
|
|
|
|
}
|
2019-10-31 08:40:36 -05:00
|
|
|
}
|
2019-11-22 12:43:36 -06:00
|
|
|
|
|
|
|
impl HasChildSource for VariantId {
|
2020-04-25 07:23:34 -05:00
|
|
|
type ChildId = LocalFieldId;
|
2020-07-30 09:49:13 -05:00
|
|
|
type Value = Either<ast::TupleField, ast::RecordField>;
|
2019-11-22 12:43:36 -06:00
|
|
|
|
2020-03-13 10:05:46 -05:00
|
|
|
fn child_source(&self, db: &dyn DefDatabase) -> InFile<ArenaMap<Self::ChildId, Self::Value>> {
|
2020-04-09 11:32:02 -05:00
|
|
|
let (src, module_id) = match self {
|
2019-11-22 12:43:36 -06:00
|
|
|
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);
|
2020-04-09 11:32:02 -05:00
|
|
|
(src.map(|map| map[it.local_id].kind()), it.parent.lookup(db).container.module(db))
|
2019-11-22 12:43:36 -06:00
|
|
|
}
|
2020-04-09 11:32:02 -05:00
|
|
|
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| {
|
2020-07-30 09:49:13 -05:00
|
|
|
it.record_field_list()
|
2020-04-09 11:32:02 -05:00
|
|
|
.map(ast::StructKind::Record)
|
|
|
|
.unwrap_or(ast::StructKind::Unit)
|
|
|
|
}),
|
|
|
|
it.lookup(db).container.module(db),
|
|
|
|
),
|
2019-11-22 12:43:36 -06:00
|
|
|
};
|
2020-04-11 10:52:26 -05:00
|
|
|
let mut expander = CfgExpander::new(db, src.file_id, module_id.krate);
|
2019-11-22 12:43:36 -06:00
|
|
|
let mut trace = Trace::new_for_map();
|
2020-04-11 10:17:12 -05:00
|
|
|
lower_struct(db, &mut expander, &mut trace, &src);
|
2019-11-22 12:43:36 -06:00
|
|
|
src.with_value(trace.into_map())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-02-21 09:56:34 -06:00
|
|
|
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
2020-02-15 14:48:20 -06:00
|
|
|
pub enum StructKind {
|
2019-11-22 12:43:36 -06:00
|
|
|
Tuple,
|
|
|
|
Record,
|
|
|
|
Unit,
|
|
|
|
}
|
|
|
|
|
|
|
|
fn lower_struct(
|
2020-03-13 10:05:46 -05:00
|
|
|
db: &dyn DefDatabase,
|
2020-04-11 10:52:26 -05:00
|
|
|
expander: &mut CfgExpander,
|
2020-07-30 09:49:13 -05:00
|
|
|
trace: &mut Trace<FieldData, Either<ast::TupleField, ast::RecordField>>,
|
2019-12-26 09:22:15 -06:00
|
|
|
ast: &InFile<ast::StructKind>,
|
2019-11-22 12:43:36 -06:00
|
|
|
) -> StructKind {
|
2020-04-30 05:20:13 -05:00
|
|
|
let ctx = LowerCtx::new(db, ast.file_id);
|
|
|
|
|
2019-12-26 09:22:15 -06:00
|
|
|
match &ast.value {
|
2019-11-22 12:43:36 -06:00
|
|
|
ast::StructKind::Tuple(fl) => {
|
|
|
|
for (i, fd) in fl.fields().enumerate() {
|
2020-05-05 02:34:07 -05:00
|
|
|
if !expander.is_cfg_enabled(&fd) {
|
2020-04-09 11:32:02 -05:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2019-11-22 12:43:36 -06:00
|
|
|
trace.alloc(
|
2019-12-03 10:07:56 -06:00
|
|
|
|| Either::Left(fd.clone()),
|
2020-04-25 07:23:34 -05:00
|
|
|
|| FieldData {
|
2019-11-22 12:43:36 -06:00
|
|
|
name: Name::new_tuple_field(i),
|
2020-07-30 14:02:55 -05:00
|
|
|
type_ref: TypeRef::from_ast_opt(&ctx, fd.ty()),
|
2019-12-26 09:22:15 -06:00
|
|
|
visibility: RawVisibility::from_ast(db, ast.with_value(fd.visibility())),
|
2019-11-22 12:43:36 -06:00
|
|
|
},
|
2019-11-24 08:49:49 -06:00
|
|
|
);
|
2019-11-22 12:43:36 -06:00
|
|
|
}
|
|
|
|
StructKind::Tuple
|
|
|
|
}
|
|
|
|
ast::StructKind::Record(fl) => {
|
|
|
|
for fd in fl.fields() {
|
2020-05-05 02:34:07 -05:00
|
|
|
if !expander.is_cfg_enabled(&fd) {
|
2020-04-09 11:32:02 -05:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2019-11-22 12:43:36 -06:00
|
|
|
trace.alloc(
|
2019-12-03 10:07:56 -06:00
|
|
|
|| Either::Right(fd.clone()),
|
2020-04-25 07:23:34 -05:00
|
|
|
|| FieldData {
|
2019-11-22 12:43:36 -06:00
|
|
|
name: fd.name().map(|n| n.as_name()).unwrap_or_else(Name::missing),
|
2020-07-30 13:51:43 -05:00
|
|
|
type_ref: TypeRef::from_ast_opt(&ctx, fd.ty()),
|
2019-12-26 09:22:15 -06:00
|
|
|
visibility: RawVisibility::from_ast(db, ast.with_value(fd.visibility())),
|
2019-11-22 12:43:36 -06:00
|
|
|
},
|
2019-11-24 08:49:49 -06:00
|
|
|
);
|
2019-11-22 12:43:36 -06:00
|
|
|
}
|
|
|
|
StructKind::Record
|
|
|
|
}
|
|
|
|
ast::StructKind::Unit => StructKind::Unit,
|
|
|
|
}
|
|
|
|
}
|
2020-06-25 09:41:08 -05:00
|
|
|
|
|
|
|
fn lower_fields(item_tree: &ItemTree, cfg_options: &CfgOptions, fields: &Fields) -> VariantData {
|
|
|
|
match fields {
|
|
|
|
Fields::Record(flds) => {
|
|
|
|
let mut arena = Arena::new();
|
|
|
|
for field_id in flds.clone() {
|
|
|
|
if item_tree.attrs(field_id.into()).is_cfg_enabled(cfg_options) {
|
|
|
|
arena.alloc(lower_field(item_tree, &item_tree[field_id]));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
VariantData::Record(arena)
|
|
|
|
}
|
|
|
|
Fields::Tuple(flds) => {
|
|
|
|
let mut arena = Arena::new();
|
|
|
|
for field_id in flds.clone() {
|
|
|
|
if item_tree.attrs(field_id.into()).is_cfg_enabled(cfg_options) {
|
|
|
|
arena.alloc(lower_field(item_tree, &item_tree[field_id]));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
VariantData::Tuple(arena)
|
|
|
|
}
|
|
|
|
Fields::Unit => VariantData::Unit,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn lower_field(item_tree: &ItemTree, field: &Field) -> FieldData {
|
|
|
|
FieldData {
|
|
|
|
name: field.name.clone(),
|
|
|
|
type_ref: field.type_ref.clone(),
|
|
|
|
visibility: item_tree[field.visibility].clone(),
|
|
|
|
}
|
|
|
|
}
|