mirror of
https://github.com/rust-lang/rust.git
synced 2025-05-02 13:07:37 +00:00
Merge #3880
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:
commit
412eda7387
@ -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);
|
||||||
|
@ -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()
|
||||||
|
@ -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::{
|
||||||
|
@ -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###""###);
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user