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 {
pub(crate) fn struct_data_query(db: &dyn DefDatabase, id: StructId) -> Arc<StructData> {
let src = id.lookup(db).source(db);
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 = Arc::new(variant_data);

View File

@ -3,15 +3,18 @@
use std::sync::Arc;
use hir_expand::{
hygiene::Hygiene,
name::{name, AsName, Name},
AstId, InFile,
};
use ra_cfg::CfgOptions;
use ra_prof::profile;
use ra_syntax::ast::{
self, AstNode, ImplItem, ModuleItemOwner, NameOwner, TypeAscriptionOwner, VisibilityOwner,
};
use crate::{
attr::Attrs,
db::DefDatabase,
path::{path, GenericArgs, Path},
src::HasSource,
@ -26,6 +29,7 @@ pub struct FunctionData {
pub name: Name,
pub params: Vec<TypeRef>,
pub ret_type: TypeRef,
pub attrs: Attrs,
/// True if the first param is `self`. This is relevant to decide whether this
/// can be called as a method.
pub has_self_param: bool,
@ -63,6 +67,8 @@ impl FunctionData {
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()) {
TypeRef::from_ast(type_ref)
} else {
@ -81,7 +87,7 @@ impl FunctionData {
let 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)
}
}
@ -211,6 +217,7 @@ impl ImplData {
let module_id = impl_loc.container.module(db);
let mut items = Vec::new();
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_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(
db: &dyn DefDatabase,
impl_items: impl Iterator<Item = ImplItem>,
@ -318,16 +329,26 @@ fn collect_impl_items(
id: ImplId,
) -> Vec<AssocItemId> {
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
.map(|item_node| match item_node {
.filter_map(|item_node| match item_node {
ast::ImplItem::FnDef(it) => {
let def = FunctionLoc {
container: AssocContainerId::ImplId(id),
ast_id: AstId::new(file_id, items.ast_id(&it)),
}
.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) => {
let def = ConstLoc {
@ -335,7 +356,7 @@ fn collect_impl_items(
ast_id: AstId::new(file_id, items.ast_id(&it)),
}
.intern(db);
def.into()
Some(def.into())
}
ast::ImplItem::TypeAliasDef(it) => {
let def = TypeAliasLoc {
@ -343,7 +364,7 @@ fn collect_impl_items(
ast_id: AstId::new(file_id, items.ast_id(&it)),
}
.intern(db);
def.into()
Some(def.into())
}
})
.collect()

View File

@ -4,8 +4,7 @@ use std::sync::Arc;
use hir_def::{path::path, resolver::HasResolver, AdtId, FunctionId};
use hir_expand::diagnostics::DiagnosticSink;
use ra_syntax::ast;
use ra_syntax::AstPtr;
use ra_syntax::{ast, AstPtr};
use rustc_hash::FxHashSet;
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###""###);
}