Skip to content

Commit 4f07d8d

Browse files
Refactor attributes API to allow handling cfg_attr
1 parent a4e17a5 commit 4f07d8d

File tree

8 files changed

+187
-114
lines changed

8 files changed

+187
-114
lines changed

crates/hir_def/src/adt.rs

Lines changed: 32 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
use std::sync::Arc;
44

55
use arena::{map::ArenaMap, Arena};
6+
use base_db::CrateId;
67
use either::Either;
78
use hir_expand::{
89
name::{AsName, Name},
@@ -66,8 +67,13 @@ pub enum ReprKind {
6667
Other,
6768
}
6869

69-
fn repr_from_value(item_tree: &ItemTree, of: AttrOwner) -> Option<ReprKind> {
70-
item_tree.attrs(of).by_key("repr").tt_values().find_map(parse_repr_tt)
70+
fn repr_from_value(
71+
db: &dyn DefDatabase,
72+
krate: CrateId,
73+
item_tree: &ItemTree,
74+
of: AttrOwner,
75+
) -> Option<ReprKind> {
76+
item_tree.attrs(db, krate, of).by_key("repr").tt_values().find_map(parse_repr_tt)
7177
}
7278

7379
fn parse_repr_tt(tt: &Subtree) -> Option<ReprKind> {
@@ -86,12 +92,13 @@ fn parse_repr_tt(tt: &Subtree) -> Option<ReprKind> {
8692
impl StructData {
8793
pub(crate) fn struct_data_query(db: &dyn DefDatabase, id: StructId) -> Arc<StructData> {
8894
let loc = id.lookup(db);
95+
let krate = loc.container.module(db).krate;
8996
let item_tree = db.item_tree(loc.id.file_id);
90-
let repr = repr_from_value(&item_tree, ModItem::from(loc.id.value).into());
97+
let repr = repr_from_value(db, krate, &item_tree, ModItem::from(loc.id.value).into());
9198
let cfg_options = db.crate_graph()[loc.container.module(db).krate].cfg_options.clone();
9299

93100
let strukt = &item_tree[loc.id.value];
94-
let variant_data = lower_fields(&item_tree, &cfg_options, &strukt.fields, None);
101+
let variant_data = lower_fields(db, krate, &item_tree, &cfg_options, &strukt.fields, None);
95102
Arc::new(StructData {
96103
name: strukt.name.clone(),
97104
variant_data: Arc::new(variant_data),
@@ -100,12 +107,13 @@ impl StructData {
100107
}
101108
pub(crate) fn union_data_query(db: &dyn DefDatabase, id: UnionId) -> Arc<StructData> {
102109
let loc = id.lookup(db);
110+
let krate = loc.container.module(db).krate;
103111
let item_tree = db.item_tree(loc.id.file_id);
104-
let repr = repr_from_value(&item_tree, ModItem::from(loc.id.value).into());
112+
let repr = repr_from_value(db, krate, &item_tree, ModItem::from(loc.id.value).into());
105113
let cfg_options = db.crate_graph()[loc.container.module(db).krate].cfg_options.clone();
106114

107115
let union = &item_tree[loc.id.value];
108-
let variant_data = lower_fields(&item_tree, &cfg_options, &union.fields, None);
116+
let variant_data = lower_fields(db, krate, &item_tree, &cfg_options, &union.fields, None);
109117

110118
Arc::new(StructData {
111119
name: union.name.clone(),
@@ -118,16 +126,23 @@ impl StructData {
118126
impl EnumData {
119127
pub(crate) fn enum_data_query(db: &dyn DefDatabase, e: EnumId) -> Arc<EnumData> {
120128
let loc = e.lookup(db);
129+
let krate = loc.container.module(db).krate;
121130
let item_tree = db.item_tree(loc.id.file_id);
122-
let cfg_options = db.crate_graph()[loc.container.module(db).krate].cfg_options.clone();
131+
let cfg_options = db.crate_graph()[krate].cfg_options.clone();
123132

124133
let enum_ = &item_tree[loc.id.value];
125134
let mut variants = Arena::new();
126135
for var_id in enum_.variants.clone() {
127-
if item_tree.attrs(var_id.into()).is_cfg_enabled(&cfg_options) {
136+
if item_tree.attrs(db, krate, var_id.into()).is_cfg_enabled(&cfg_options) {
128137
let var = &item_tree[var_id];
129-
let var_data =
130-
lower_fields(&item_tree, &cfg_options, &var.fields, Some(enum_.visibility));
138+
let var_data = lower_fields(
139+
db,
140+
krate,
141+
&item_tree,
142+
&cfg_options,
143+
&var.fields,
144+
Some(enum_.visibility),
145+
);
131146

132147
variants.alloc(EnumVariantData {
133148
name: var.name.clone(),
@@ -170,7 +185,7 @@ fn lower_enum(
170185
.variant_list()
171186
.into_iter()
172187
.flat_map(|it| it.variants())
173-
.filter(|var| expander.is_cfg_enabled(var));
188+
.filter(|var| expander.is_cfg_enabled(db, var));
174189
for var in variants {
175190
trace.alloc(
176191
|| var.clone(),
@@ -262,7 +277,7 @@ fn lower_struct(
262277
match &ast.value {
263278
ast::StructKind::Tuple(fl) => {
264279
for (i, fd) in fl.fields().enumerate() {
265-
if !expander.is_cfg_enabled(&fd) {
280+
if !expander.is_cfg_enabled(db, &fd) {
266281
continue;
267282
}
268283

@@ -279,7 +294,7 @@ fn lower_struct(
279294
}
280295
ast::StructKind::Record(fl) => {
281296
for fd in fl.fields() {
282-
if !expander.is_cfg_enabled(&fd) {
297+
if !expander.is_cfg_enabled(db, &fd) {
283298
continue;
284299
}
285300

@@ -299,6 +314,8 @@ fn lower_struct(
299314
}
300315

301316
fn lower_fields(
317+
db: &dyn DefDatabase,
318+
krate: CrateId,
302319
item_tree: &ItemTree,
303320
cfg_options: &CfgOptions,
304321
fields: &Fields,
@@ -308,7 +325,7 @@ fn lower_fields(
308325
Fields::Record(flds) => {
309326
let mut arena = Arena::new();
310327
for field_id in flds.clone() {
311-
if item_tree.attrs(field_id.into()).is_cfg_enabled(cfg_options) {
328+
if item_tree.attrs(db, krate, field_id.into()).is_cfg_enabled(cfg_options) {
312329
arena.alloc(lower_field(item_tree, &item_tree[field_id], override_visibility));
313330
}
314331
}
@@ -317,7 +334,7 @@ fn lower_fields(
317334
Fields::Tuple(flds) => {
318335
let mut arena = Arena::new();
319336
for field_id in flds.clone() {
320-
if item_tree.attrs(field_id.into()).is_cfg_enabled(cfg_options) {
337+
if item_tree.attrs(db, krate, field_id.into()).is_cfg_enabled(cfg_options) {
321338
arena.alloc(lower_field(item_tree, &item_tree[field_id], override_visibility));
322339
}
323340
}

crates/hir_def/src/attr.rs

Lines changed: 93 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
33
use std::{ops, sync::Arc};
44

5+
use base_db::CrateId;
56
use cfg::{CfgExpr, CfgOptions};
67
use either::Either;
78
use hir_expand::{hygiene::Hygiene, AstId, InFile};
@@ -38,12 +39,16 @@ impl From<Documentation> for String {
3839
}
3940
}
4041

42+
/// Syntactical attributes, without filtering of `cfg_attr`s.
4143
#[derive(Default, Debug, Clone, PartialEq, Eq)]
42-
pub struct Attrs {
44+
pub struct RawAttrs {
4345
entries: Option<Arc<[Attr]>>,
4446
}
4547

46-
impl ops::Deref for Attrs {
48+
#[derive(Default, Debug, Clone, PartialEq, Eq)]
49+
pub struct Attrs(RawAttrs);
50+
51+
impl ops::Deref for RawAttrs {
4752
type Target = [Attr];
4853

4954
fn deref(&self) -> &[Attr] {
@@ -54,19 +59,88 @@ impl ops::Deref for Attrs {
5459
}
5560
}
5661

62+
impl ops::Deref for Attrs {
63+
type Target = [Attr];
64+
65+
fn deref(&self) -> &[Attr] {
66+
match &self.0.entries {
67+
Some(it) => &*it,
68+
None => &[],
69+
}
70+
}
71+
}
72+
73+
impl RawAttrs {
74+
pub const EMPTY: Self = Self { entries: None };
75+
76+
pub(crate) fn new(owner: &dyn AttrsOwner, hygiene: &Hygiene) -> Self {
77+
let (inner_attrs, inner_docs) = inner_attributes(owner.syntax())
78+
.map_or((None, None), |(attrs, docs)| ((Some(attrs), Some(docs))));
79+
80+
let outer_attrs = owner.attrs().filter(|attr| attr.excl_token().is_none());
81+
let attrs = outer_attrs
82+
.chain(inner_attrs.into_iter().flatten())
83+
.map(|attr| (attr.syntax().text_range().start(), Attr::from_src(attr, hygiene)));
84+
85+
let outer_docs =
86+
ast::CommentIter::from_syntax_node(owner.syntax()).filter(ast::Comment::is_outer);
87+
let docs = outer_docs.chain(inner_docs.into_iter().flatten()).map(|docs_text| {
88+
(
89+
docs_text.syntax().text_range().start(),
90+
docs_text.doc_comment().map(|doc| Attr {
91+
input: Some(AttrInput::Literal(SmolStr::new(doc))),
92+
path: ModPath::from(hir_expand::name!(doc)),
93+
}),
94+
)
95+
});
96+
// sort here by syntax node offset because the source can have doc attributes and doc strings be interleaved
97+
let attrs: Vec<_> = docs.chain(attrs).sorted_by_key(|&(offset, _)| offset).collect();
98+
let entries = if attrs.is_empty() {
99+
// Avoid heap allocation
100+
None
101+
} else {
102+
Some(attrs.into_iter().flat_map(|(_, attr)| attr).collect())
103+
};
104+
Self { entries }
105+
}
106+
107+
fn from_attrs_owner(db: &dyn DefDatabase, owner: InFile<&dyn AttrsOwner>) -> Self {
108+
let hygiene = Hygiene::new(db.upcast(), owner.file_id);
109+
Self::new(owner.value, &hygiene)
110+
}
111+
112+
pub(crate) fn merge(&self, other: Self) -> Self {
113+
match (&self.entries, &other.entries) {
114+
(None, None) => Self::EMPTY,
115+
(Some(entries), None) | (None, Some(entries)) => {
116+
Self { entries: Some(entries.clone()) }
117+
}
118+
(Some(a), Some(b)) => {
119+
Self { entries: Some(a.iter().chain(b.iter()).cloned().collect()) }
120+
}
121+
}
122+
}
123+
124+
/// Processes `cfg_attr`s, returning the resulting semantic `Attrs`.
125+
pub(crate) fn filter(self, _db: &dyn DefDatabase, _krate: CrateId) -> Attrs {
126+
// FIXME actually implement this
127+
Attrs(self)
128+
}
129+
}
130+
57131
impl Attrs {
58-
pub const EMPTY: Attrs = Attrs { entries: None };
132+
pub const EMPTY: Self = Self(RawAttrs::EMPTY);
59133

60134
pub(crate) fn attrs_query(db: &dyn DefDatabase, def: AttrDefId) -> Attrs {
61-
match def {
135+
let raw_attrs = match def {
62136
AttrDefId::ModuleId(module) => {
63137
let def_map = db.crate_def_map(module.krate);
64138
let mod_data = &def_map[module.local_id];
65139
match mod_data.declaration_source(db) {
66140
Some(it) => {
67-
Attrs::from_attrs_owner(db, it.as_ref().map(|it| it as &dyn AttrsOwner))
141+
RawAttrs::from_attrs_owner(db, it.as_ref().map(|it| it as &dyn AttrsOwner))
68142
}
69-
None => Attrs::from_attrs_owner(
143+
None => RawAttrs::from_attrs_owner(
70144
db,
71145
mod_data.definition_source(db).as_ref().map(|src| match src {
72146
ModuleSource::SourceFile(file) => file as &dyn AttrsOwner,
@@ -78,14 +152,14 @@ impl Attrs {
78152
AttrDefId::FieldId(it) => {
79153
let src = it.parent.child_source(db);
80154
match &src.value[it.local_id] {
81-
Either::Left(_tuple) => Attrs::default(),
82-
Either::Right(record) => Attrs::from_attrs_owner(db, src.with_value(record)),
155+
Either::Left(_tuple) => RawAttrs::default(),
156+
Either::Right(record) => RawAttrs::from_attrs_owner(db, src.with_value(record)),
83157
}
84158
}
85159
AttrDefId::EnumVariantId(var_id) => {
86160
let src = var_id.parent.child_source(db);
87161
let src = src.as_ref().map(|it| &it[var_id.local_id]);
88-
Attrs::from_attrs_owner(db, src.map(|it| it as &dyn AttrsOwner))
162+
RawAttrs::from_attrs_owner(db, src.map(|it| it as &dyn AttrsOwner))
89163
}
90164
AttrDefId::AdtId(it) => match it {
91165
AdtId::StructId(it) => attrs_from_item_tree(it.lookup(db).id, db),
@@ -101,53 +175,19 @@ impl Attrs {
101175
AttrDefId::StaticId(it) => attrs_from_item_tree(it.lookup(db).id, db),
102176
AttrDefId::FunctionId(it) => attrs_from_item_tree(it.lookup(db).id, db),
103177
AttrDefId::TypeAliasId(it) => attrs_from_item_tree(it.lookup(db).id, db),
104-
}
105-
}
106-
107-
fn from_attrs_owner(db: &dyn DefDatabase, owner: InFile<&dyn AttrsOwner>) -> Attrs {
108-
let hygiene = Hygiene::new(db.upcast(), owner.file_id);
109-
Attrs::new(owner.value, &hygiene)
110-
}
111-
112-
pub(crate) fn new(owner: &dyn AttrsOwner, hygiene: &Hygiene) -> Attrs {
113-
let (inner_attrs, inner_docs) = inner_attributes(owner.syntax())
114-
.map_or((None, None), |(attrs, docs)| ((Some(attrs), Some(docs))));
115-
116-
let outer_attrs = owner.attrs().filter(|attr| attr.excl_token().is_none());
117-
let attrs = outer_attrs
118-
.chain(inner_attrs.into_iter().flatten())
119-
.map(|attr| (attr.syntax().text_range().start(), Attr::from_src(attr, hygiene)));
120-
121-
let outer_docs =
122-
ast::CommentIter::from_syntax_node(owner.syntax()).filter(ast::Comment::is_outer);
123-
let docs = outer_docs.chain(inner_docs.into_iter().flatten()).map(|docs_text| {
124-
(
125-
docs_text.syntax().text_range().start(),
126-
docs_text.doc_comment().map(|doc| Attr {
127-
input: Some(AttrInput::Literal(SmolStr::new(doc))),
128-
path: ModPath::from(hir_expand::name!(doc)),
129-
}),
130-
)
131-
});
132-
// sort here by syntax node offset because the source can have doc attributes and doc strings be interleaved
133-
let attrs: Vec<_> = docs.chain(attrs).sorted_by_key(|&(offset, _)| offset).collect();
134-
let entries = if attrs.is_empty() {
135-
// Avoid heap allocation
136-
None
137-
} else {
138-
Some(attrs.into_iter().flat_map(|(_, attr)| attr).collect())
139178
};
140-
Attrs { entries }
179+
180+
raw_attrs.filter(db, def.krate(db))
141181
}
142182

143183
pub fn merge(&self, other: Attrs) -> Attrs {
144-
match (&self.entries, &other.entries) {
145-
(None, None) => Attrs { entries: None },
184+
match (&self.0.entries, &other.0.entries) {
185+
(None, None) => Attrs::EMPTY,
146186
(Some(entries), None) | (None, Some(entries)) => {
147-
Attrs { entries: Some(entries.clone()) }
187+
Attrs(RawAttrs { entries: Some(entries.clone()) })
148188
}
149189
(Some(a), Some(b)) => {
150-
Attrs { entries: Some(a.iter().chain(b.iter()).cloned().collect()) }
190+
Attrs(RawAttrs { entries: Some(a.iter().chain(b.iter()).cloned().collect()) })
151191
}
152192
}
153193
}
@@ -291,16 +331,16 @@ impl<'a> AttrQuery<'a> {
291331
}
292332
}
293333

294-
fn attrs_from_ast<N>(src: AstId<N>, db: &dyn DefDatabase) -> Attrs
334+
fn attrs_from_ast<N>(src: AstId<N>, db: &dyn DefDatabase) -> RawAttrs
295335
where
296336
N: ast::AttrsOwner,
297337
{
298338
let src = InFile::new(src.file_id, src.to_node(db.upcast()));
299-
Attrs::from_attrs_owner(db, src.as_ref().map(|it| it as &dyn AttrsOwner))
339+
RawAttrs::from_attrs_owner(db, src.as_ref().map(|it| it as &dyn AttrsOwner))
300340
}
301341

302-
fn attrs_from_item_tree<N: ItemTreeNode>(id: ItemTreeId<N>, db: &dyn DefDatabase) -> Attrs {
342+
fn attrs_from_item_tree<N: ItemTreeNode>(id: ItemTreeId<N>, db: &dyn DefDatabase) -> RawAttrs {
303343
let tree = db.item_tree(id.file_id);
304344
let mod_item = N::id_to_mod_item(id.value);
305-
tree.attrs(mod_item.into()).clone()
345+
tree.raw_attrs(mod_item.into()).clone()
306346
}

0 commit comments

Comments
 (0)