Skip to content

Commit 33df208

Browse files
bors[bot]bnjjj
andauthored
Merge #3918
3918: Add support for feature attributes in struct literal r=matklad a=bnjjj As promised here is the next PR to solve 2 different scenarios with feature flag on struct literal. close #3870 Co-authored-by: Benjamin Coenen <[email protected]>
2 parents 9635d8b + c1317d6 commit 33df208

File tree

5 files changed

+142
-45
lines changed

5 files changed

+142
-45
lines changed

crates/ra_hir_def/src/adt.rs

Lines changed: 39 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ use std::sync::Arc;
44

55
use either::Either;
66
use hir_expand::{
7+
hygiene::Hygiene,
78
name::{AsName, Name},
89
InFile,
910
};
@@ -12,9 +13,9 @@ use ra_prof::profile;
1213
use ra_syntax::ast::{self, NameOwner, TypeAscriptionOwner, VisibilityOwner};
1314

1415
use crate::{
15-
db::DefDatabase, src::HasChildSource, src::HasSource, trace::Trace, type_ref::TypeRef,
16-
visibility::RawVisibility, EnumId, LocalEnumVariantId, LocalStructFieldId, Lookup, StructId,
17-
UnionId, VariantId,
16+
attr::Attrs, db::DefDatabase, src::HasChildSource, src::HasSource, trace::Trace,
17+
type_ref::TypeRef, visibility::RawVisibility, EnumId, HasModule, LocalEnumVariantId,
18+
LocalStructFieldId, Lookup, ModuleId, StructId, UnionId, VariantId,
1819
};
1920

2021
/// Note that we use `StructData` for unions as well!
@@ -56,7 +57,8 @@ impl StructData {
5657
let src = id.lookup(db).source(db);
5758

5859
let name = src.value.name().map_or_else(Name::missing, |n| n.as_name());
59-
let variant_data = VariantData::new(db, src.map(|s| s.kind()));
60+
let variant_data =
61+
VariantData::new(db, src.map(|s| s.kind()), id.lookup(db).container.module(db));
6062
let variant_data = Arc::new(variant_data);
6163
Arc::new(StructData { name, variant_data })
6264
}
@@ -70,6 +72,7 @@ impl StructData {
7072
.map(ast::StructKind::Record)
7173
.unwrap_or(ast::StructKind::Unit)
7274
}),
75+
id.lookup(db).container.module(db),
7376
);
7477
let variant_data = Arc::new(variant_data);
7578
Arc::new(StructData { name, variant_data })
@@ -82,7 +85,7 @@ impl EnumData {
8285
let src = e.lookup(db).source(db);
8386
let name = src.value.name().map_or_else(Name::missing, |n| n.as_name());
8487
let mut trace = Trace::new_for_arena();
85-
lower_enum(db, &mut trace, &src);
88+
lower_enum(db, &mut trace, &src, e.lookup(db).container.module(db));
8689
Arc::new(EnumData { name, variants: trace.into_arena() })
8790
}
8891

@@ -98,7 +101,7 @@ impl HasChildSource for EnumId {
98101
fn child_source(&self, db: &dyn DefDatabase) -> InFile<ArenaMap<Self::ChildId, Self::Value>> {
99102
let src = self.lookup(db).source(db);
100103
let mut trace = Trace::new_for_map();
101-
lower_enum(db, &mut trace, &src);
104+
lower_enum(db, &mut trace, &src, self.lookup(db).container.module(db));
102105
src.with_value(trace.into_map())
103106
}
104107
}
@@ -107,22 +110,23 @@ fn lower_enum(
107110
db: &dyn DefDatabase,
108111
trace: &mut Trace<EnumVariantData, ast::EnumVariant>,
109112
ast: &InFile<ast::EnumDef>,
113+
module_id: ModuleId,
110114
) {
111115
for var in ast.value.variant_list().into_iter().flat_map(|it| it.variants()) {
112116
trace.alloc(
113117
|| var.clone(),
114118
|| EnumVariantData {
115119
name: var.name().map_or_else(Name::missing, |it| it.as_name()),
116-
variant_data: Arc::new(VariantData::new(db, ast.with_value(var.kind()))),
120+
variant_data: Arc::new(VariantData::new(db, ast.with_value(var.kind()), module_id)),
117121
},
118122
);
119123
}
120124
}
121125

122126
impl VariantData {
123-
fn new(db: &dyn DefDatabase, flavor: InFile<ast::StructKind>) -> Self {
127+
fn new(db: &dyn DefDatabase, flavor: InFile<ast::StructKind>, module_id: ModuleId) -> Self {
124128
let mut trace = Trace::new_for_arena();
125-
match lower_struct(db, &mut trace, &flavor) {
129+
match lower_struct(db, &mut trace, &flavor, module_id) {
126130
StructKind::Tuple => VariantData::Tuple(trace.into_arena()),
127131
StructKind::Record => VariantData::Record(trace.into_arena()),
128132
StructKind::Unit => VariantData::Unit,
@@ -155,22 +159,27 @@ impl HasChildSource for VariantId {
155159
type Value = Either<ast::TupleFieldDef, ast::RecordFieldDef>;
156160

157161
fn child_source(&self, db: &dyn DefDatabase) -> InFile<ArenaMap<Self::ChildId, Self::Value>> {
158-
let src = match self {
162+
let (src, module_id) = match self {
159163
VariantId::EnumVariantId(it) => {
160164
// I don't really like the fact that we call into parent source
161165
// here, this might add to more queries then necessary.
162166
let src = it.parent.child_source(db);
163-
src.map(|map| map[it.local_id].kind())
167+
(src.map(|map| map[it.local_id].kind()), it.parent.lookup(db).container.module(db))
164168
}
165-
VariantId::StructId(it) => it.lookup(db).source(db).map(|it| it.kind()),
166-
VariantId::UnionId(it) => it.lookup(db).source(db).map(|it| {
167-
it.record_field_def_list()
168-
.map(ast::StructKind::Record)
169-
.unwrap_or(ast::StructKind::Unit)
170-
}),
169+
VariantId::StructId(it) => {
170+
(it.lookup(db).source(db).map(|it| it.kind()), it.lookup(db).container.module(db))
171+
}
172+
VariantId::UnionId(it) => (
173+
it.lookup(db).source(db).map(|it| {
174+
it.record_field_def_list()
175+
.map(ast::StructKind::Record)
176+
.unwrap_or(ast::StructKind::Unit)
177+
}),
178+
it.lookup(db).container.module(db),
179+
),
171180
};
172181
let mut trace = Trace::new_for_map();
173-
lower_struct(db, &mut trace, &src);
182+
lower_struct(db, &mut trace, &src, module_id);
174183
src.with_value(trace.into_map())
175184
}
176185
}
@@ -186,10 +195,17 @@ fn lower_struct(
186195
db: &dyn DefDatabase,
187196
trace: &mut Trace<StructFieldData, Either<ast::TupleFieldDef, ast::RecordFieldDef>>,
188197
ast: &InFile<ast::StructKind>,
198+
module_id: ModuleId,
189199
) -> StructKind {
200+
let crate_graph = db.crate_graph();
190201
match &ast.value {
191202
ast::StructKind::Tuple(fl) => {
192203
for (i, fd) in fl.fields().enumerate() {
204+
let attrs = Attrs::new(&fd, &Hygiene::new(db.upcast(), ast.file_id));
205+
if !attrs.is_cfg_enabled(&crate_graph[module_id.krate].cfg_options) {
206+
continue;
207+
}
208+
193209
trace.alloc(
194210
|| Either::Left(fd.clone()),
195211
|| StructFieldData {
@@ -203,6 +219,11 @@ fn lower_struct(
203219
}
204220
ast::StructKind::Record(fl) => {
205221
for fd in fl.fields() {
222+
let attrs = Attrs::new(&fd, &Hygiene::new(db.upcast(), ast.file_id));
223+
if !attrs.is_cfg_enabled(&crate_graph[module_id.krate].cfg_options) {
224+
continue;
225+
}
226+
206227
trace.alloc(
207228
|| Either::Right(fd.clone()),
208229
|| StructFieldData {

crates/ra_hir_def/src/attr.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ use std::{ops, sync::Arc};
55
use either::Either;
66
use hir_expand::{hygiene::Hygiene, AstId, InFile};
77
use mbe::ast_to_token_tree;
8+
use ra_cfg::CfgOptions;
89
use ra_syntax::{
910
ast::{self, AstNode, AttrsOwner},
1011
SmolStr,
@@ -90,6 +91,10 @@ impl Attrs {
9091
pub fn by_key(&self, key: &'static str) -> AttrQuery<'_> {
9192
AttrQuery { attrs: self, key }
9293
}
94+
95+
pub(crate) fn is_cfg_enabled(&self, cfg_options: &CfgOptions) -> bool {
96+
self.by_key("cfg").tt_values().all(|tt| cfg_options.is_cfg_enabled(tt) != Some(false))
97+
}
9398
}
9499

95100
#[derive(Debug, Clone, PartialEq, Eq)]

crates/ra_hir_def/src/body/lower.rs

Lines changed: 33 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
use either::Either;
55

66
use hir_expand::{
7+
hygiene::Hygiene,
78
name::{name, AsName, Name},
89
MacroDefId, MacroDefKind,
910
};
@@ -20,6 +21,7 @@ use test_utils::tested_by;
2021
use super::{ExprSource, PatSource};
2122
use crate::{
2223
adt::StructKind,
24+
attr::Attrs,
2325
body::{Body, BodySourceMap, Expander, PatPtr, SyntheticSyntax},
2426
builtin_type::{BuiltinFloat, BuiltinInt},
2527
db::DefDatabase,
@@ -31,8 +33,8 @@ use crate::{
3133
path::GenericArgs,
3234
path::Path,
3335
type_ref::{Mutability, TypeRef},
34-
AdtId, ConstLoc, ContainerId, DefWithBodyId, EnumLoc, FunctionLoc, Intern, ModuleDefId,
35-
StaticLoc, StructLoc, TraitLoc, TypeAliasLoc, UnionLoc,
36+
AdtId, ConstLoc, ContainerId, DefWithBodyId, EnumLoc, FunctionLoc, HasModule, Intern,
37+
ModuleDefId, StaticLoc, StructLoc, TraitLoc, TypeAliasLoc, UnionLoc,
3638
};
3739

3840
pub(super) fn lower(
@@ -298,28 +300,41 @@ impl ExprCollector<'_> {
298300
self.alloc_expr(Expr::Return { expr }, syntax_ptr)
299301
}
300302
ast::Expr::RecordLit(e) => {
303+
let crate_graph = self.db.crate_graph();
301304
let path = e.path().and_then(|path| self.expander.parse_path(path));
302305
let mut field_ptrs = Vec::new();
303306
let record_lit = if let Some(nfl) = e.record_field_list() {
304307
let fields = nfl
305308
.fields()
306309
.inspect(|field| field_ptrs.push(AstPtr::new(field)))
307-
.map(|field| RecordLitField {
308-
name: field
309-
.name_ref()
310-
.map(|nr| nr.as_name())
311-
.unwrap_or_else(Name::missing),
312-
expr: if let Some(e) = field.expr() {
313-
self.collect_expr(e)
314-
} else if let Some(nr) = field.name_ref() {
315-
// field shorthand
316-
self.alloc_expr_field_shorthand(
317-
Expr::Path(Path::from_name_ref(&nr)),
318-
AstPtr::new(&field),
319-
)
320-
} else {
321-
self.missing_expr()
322-
},
310+
.filter_map(|field| {
311+
let module_id = ContainerId::DefWithBodyId(self.def).module(self.db);
312+
let attrs = Attrs::new(
313+
&field,
314+
&Hygiene::new(self.db.upcast(), self.expander.current_file_id),
315+
);
316+
317+
if !attrs.is_cfg_enabled(&crate_graph[module_id.krate].cfg_options) {
318+
return None;
319+
}
320+
321+
Some(RecordLitField {
322+
name: field
323+
.name_ref()
324+
.map(|nr| nr.as_name())
325+
.unwrap_or_else(Name::missing),
326+
expr: if let Some(e) = field.expr() {
327+
self.collect_expr(e)
328+
} else if let Some(nr) = field.name_ref() {
329+
// field shorthand
330+
self.alloc_expr_field_shorthand(
331+
Expr::Path(Path::from_name_ref(&nr)),
332+
AstPtr::new(&field),
333+
)
334+
} else {
335+
self.missing_expr()
336+
},
337+
})
323338
})
324339
.collect();
325340
let spread = nfl.spread().map(|s| self.collect_expr(s));

crates/ra_hir_def/src/data.rs

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ use hir_expand::{
77
name::{name, AsName, Name},
88
AstId, InFile,
99
};
10-
use ra_cfg::CfgOptions;
1110
use ra_prof::profile;
1211
use ra_syntax::ast::{
1312
self, AstNode, ImplItem, ModuleItemOwner, NameOwner, TypeAscriptionOwner, VisibilityOwner,
@@ -318,10 +317,6 @@ fn collect_impl_items_in_macro(
318317
}
319318
}
320319

321-
fn is_cfg_enabled(cfg_options: &CfgOptions, attrs: &Attrs) -> bool {
322-
attrs.by_key("cfg").tt_values().all(|tt| cfg_options.is_cfg_enabled(tt) != Some(false))
323-
}
324-
325320
fn collect_impl_items(
326321
db: &dyn DefDatabase,
327322
impl_items: impl Iterator<Item = ImplItem>,
@@ -341,10 +336,11 @@ fn collect_impl_items(
341336
}
342337
.intern(db);
343338

344-
if !is_cfg_enabled(
345-
&crate_graph[module_id.krate].cfg_options,
346-
&db.function_data(def).attrs,
347-
) {
339+
if !db
340+
.function_data(def)
341+
.attrs
342+
.is_cfg_enabled(&crate_graph[module_id.krate].cfg_options)
343+
{
348344
None
349345
} else {
350346
Some(def.into())

crates/ra_hir_ty/src/tests.rs

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -349,3 +349,63 @@ fn no_such_field_with_feature_flag_diagnostics() {
349349

350350
assert_snapshot!(diagnostics, @r###""###);
351351
}
352+
353+
#[test]
354+
fn no_such_field_with_feature_flag_diagnostics_on_struct_lit() {
355+
let diagnostics = TestDB::with_files(
356+
r#"
357+
//- /lib.rs crate:foo cfg:feature=foo
358+
struct S {
359+
#[cfg(feature = "foo")]
360+
foo: u32,
361+
#[cfg(not(feature = "foo"))]
362+
bar: u32,
363+
}
364+
365+
impl S {
366+
#[cfg(feature = "foo")]
367+
fn new(foo: u32) -> Self {
368+
Self { foo }
369+
}
370+
#[cfg(not(feature = "foo"))]
371+
fn new(bar: u32) -> Self {
372+
Self { bar }
373+
}
374+
}
375+
"#,
376+
)
377+
.diagnostics()
378+
.0;
379+
380+
assert_snapshot!(diagnostics, @r###""###);
381+
}
382+
383+
#[test]
384+
fn no_such_field_with_feature_flag_diagnostics_on_struct_fields() {
385+
let diagnostics = TestDB::with_files(
386+
r#"
387+
//- /lib.rs crate:foo cfg:feature=foo
388+
struct S {
389+
#[cfg(feature = "foo")]
390+
foo: u32,
391+
#[cfg(not(feature = "foo"))]
392+
bar: u32,
393+
}
394+
395+
impl S {
396+
fn new(val: u32) -> Self {
397+
Self {
398+
#[cfg(feature = "foo")]
399+
foo: val,
400+
#[cfg(not(feature = "foo"))]
401+
bar: val,
402+
}
403+
}
404+
}
405+
"#,
406+
)
407+
.diagnostics()
408+
.0;
409+
410+
assert_snapshot!(diagnostics, @r###""###);
411+
}

0 commit comments

Comments
 (0)