3880: Add support for attributes for struct fields r=matklad a=bnjjj

Hello I try to solve this example:
```rust
struct MyStruct {
    my_val: usize,
    #[cfg(feature = "foo")]
    bar: bool,
}
impl MyStruct {
    #[cfg(feature = "foo")]
    pub(crate) fn new(my_val: usize, bar: bool) -> Self {
        Self { my_val, bar }
    }
    #[cfg(not(feature = "foo"))]
    pub(crate) fn new(my_val: usize, _bar: bool) -> Self {
        Self { my_val }
    }
}
```

Here is a draft PR to try to solve this issue. In fact for now when i have this kind of example, rust-analyzer tells me that my second Self {} miss the bar field. Which is a bug.

I have some difficulties to add this features. Here in my draft I share my work about adding attributes support on struct field data. But I'm stuck when I have to fetch attributes from parent expressions. I don't really know how to do that. For the first iteration I just want to solve my issue without solving on all different expressions. And then after I will try to implement that on different kind of expression. I think I have to fetch my FunctionId and then I will be able to find attributes with myFunction.attrs() But I don't know if it's the right way.

@matklad (or anyone else) if you can help me it would be great :D 

Co-authored-by: Benjamin Coenen <5719034+bnjjj@users.noreply.github.com>
This commit is contained in:
bors[bot] 2020-04-09 08:08:55 +00:00 committed by GitHub
commit 412eda7387
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 58 additions and 7 deletions

View File

@ -54,6 +54,7 @@ pub struct StructFieldData {
impl StructData { impl StructData {
pub(crate) fn struct_data_query(db: &dyn DefDatabase, id: StructId) -> Arc<StructData> { pub(crate) fn struct_data_query(db: &dyn DefDatabase, id: StructId) -> Arc<StructData> {
let src = id.lookup(db).source(db); let src = id.lookup(db).source(db);
let name = src.value.name().map_or_else(Name::missing, |n| n.as_name()); 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())); let variant_data = VariantData::new(db, src.map(|s| s.kind()));
let variant_data = Arc::new(variant_data); let variant_data = Arc::new(variant_data);

View File

@ -3,15 +3,18 @@
use std::sync::Arc; use std::sync::Arc;
use hir_expand::{ use hir_expand::{
hygiene::Hygiene,
name::{name, AsName, Name}, name::{name, AsName, Name},
AstId, InFile, AstId, InFile,
}; };
use ra_cfg::CfgOptions;
use ra_prof::profile; use ra_prof::profile;
use ra_syntax::ast::{ use ra_syntax::ast::{
self, AstNode, ImplItem, ModuleItemOwner, NameOwner, TypeAscriptionOwner, VisibilityOwner, self, AstNode, ImplItem, ModuleItemOwner, NameOwner, TypeAscriptionOwner, VisibilityOwner,
}; };
use crate::{ use crate::{
attr::Attrs,
db::DefDatabase, db::DefDatabase,
path::{path, GenericArgs, Path}, path::{path, GenericArgs, Path},
src::HasSource, src::HasSource,
@ -26,6 +29,7 @@ pub struct FunctionData {
pub name: Name, pub name: Name,
pub params: Vec<TypeRef>, pub params: Vec<TypeRef>,
pub ret_type: TypeRef, pub ret_type: TypeRef,
pub attrs: Attrs,
/// True if the first param is `self`. This is relevant to decide whether this /// True if the first param is `self`. This is relevant to decide whether this
/// can be called as a method. /// can be called as a method.
pub has_self_param: bool, pub has_self_param: bool,
@ -63,6 +67,8 @@ impl FunctionData {
params.push(type_ref); params.push(type_ref);
} }
} }
let attrs = Attrs::new(&src.value, &Hygiene::new(db.upcast(), src.file_id));
let ret_type = if let Some(type_ref) = src.value.ret_type().and_then(|rt| rt.type_ref()) { let ret_type = if let Some(type_ref) = src.value.ret_type().and_then(|rt| rt.type_ref()) {
TypeRef::from_ast(type_ref) TypeRef::from_ast(type_ref)
} else { } else {
@ -81,7 +87,7 @@ impl FunctionData {
let visibility = let visibility =
RawVisibility::from_ast_with_default(db, vis_default, src.map(|s| s.visibility())); RawVisibility::from_ast_with_default(db, vis_default, src.map(|s| s.visibility()));
let sig = FunctionData { name, params, ret_type, has_self_param, visibility }; let sig = FunctionData { name, params, ret_type, has_self_param, visibility, attrs };
Arc::new(sig) Arc::new(sig)
} }
} }
@ -211,6 +217,7 @@ impl ImplData {
let module_id = impl_loc.container.module(db); let module_id = impl_loc.container.module(db);
let mut items = Vec::new(); let mut items = Vec::new();
if let Some(item_list) = src.value.item_list() { if let Some(item_list) = src.value.item_list() {
items.extend(collect_impl_items(db, item_list.impl_items(), src.file_id, id)); items.extend(collect_impl_items(db, item_list.impl_items(), src.file_id, id));
items.extend(collect_impl_items_in_macros( items.extend(collect_impl_items_in_macros(
@ -311,6 +318,10 @@ fn collect_impl_items_in_macro(
} }
} }
fn is_cfg_enabled(cfg_options: &CfgOptions, attrs: &Attrs) -> bool {
attrs.by_key("cfg").tt_values().all(|tt| cfg_options.is_cfg_enabled(tt) != Some(false))
}
fn collect_impl_items( fn collect_impl_items(
db: &dyn DefDatabase, db: &dyn DefDatabase,
impl_items: impl Iterator<Item = ImplItem>, impl_items: impl Iterator<Item = ImplItem>,
@ -318,16 +329,26 @@ fn collect_impl_items(
id: ImplId, id: ImplId,
) -> Vec<AssocItemId> { ) -> Vec<AssocItemId> {
let items = db.ast_id_map(file_id); let items = db.ast_id_map(file_id);
let crate_graph = db.crate_graph();
let module_id = id.lookup(db).container.module(db);
impl_items impl_items
.map(|item_node| match item_node { .filter_map(|item_node| match item_node {
ast::ImplItem::FnDef(it) => { ast::ImplItem::FnDef(it) => {
let def = FunctionLoc { let def = FunctionLoc {
container: AssocContainerId::ImplId(id), container: AssocContainerId::ImplId(id),
ast_id: AstId::new(file_id, items.ast_id(&it)), ast_id: AstId::new(file_id, items.ast_id(&it)),
} }
.intern(db); .intern(db);
def.into()
if !is_cfg_enabled(
&crate_graph[module_id.krate].cfg_options,
&db.function_data(def).attrs,
) {
None
} else {
Some(def.into())
}
} }
ast::ImplItem::ConstDef(it) => { ast::ImplItem::ConstDef(it) => {
let def = ConstLoc { let def = ConstLoc {
@ -335,7 +356,7 @@ fn collect_impl_items(
ast_id: AstId::new(file_id, items.ast_id(&it)), ast_id: AstId::new(file_id, items.ast_id(&it)),
} }
.intern(db); .intern(db);
def.into() Some(def.into())
} }
ast::ImplItem::TypeAliasDef(it) => { ast::ImplItem::TypeAliasDef(it) => {
let def = TypeAliasLoc { let def = TypeAliasLoc {
@ -343,7 +364,7 @@ fn collect_impl_items(
ast_id: AstId::new(file_id, items.ast_id(&it)), ast_id: AstId::new(file_id, items.ast_id(&it)),
} }
.intern(db); .intern(db);
def.into() Some(def.into())
} }
}) })
.collect() .collect()

View File

@ -4,8 +4,7 @@ use std::sync::Arc;
use hir_def::{path::path, resolver::HasResolver, AdtId, FunctionId}; use hir_def::{path::path, resolver::HasResolver, AdtId, FunctionId};
use hir_expand::diagnostics::DiagnosticSink; use hir_expand::diagnostics::DiagnosticSink;
use ra_syntax::ast; use ra_syntax::{ast, AstPtr};
use ra_syntax::AstPtr;
use rustc_hash::FxHashSet; use rustc_hash::FxHashSet;
use crate::{ use crate::{

View File

@ -319,3 +319,33 @@ fn no_such_field_diagnostics() {
"### "###
); );
} }
#[test]
fn no_such_field_with_feature_flag_diagnostics() {
let diagnostics = TestDB::with_files(
r#"
//- /lib.rs crate:foo cfg:feature=foo
struct MyStruct {
my_val: usize,
#[cfg(feature = "foo")]
bar: bool,
}
impl MyStruct {
#[cfg(feature = "foo")]
pub(crate) fn new(my_val: usize, bar: bool) -> Self {
Self { my_val, bar }
}
#[cfg(not(feature = "foo"))]
pub(crate) fn new(my_val: usize, _bar: bool) -> Self {
Self { my_val }
}
}
"#,
)
.diagnostics()
.0;
assert_snapshot!(diagnostics, @r###""###);
}