mirror of
https://github.com/rust-lang/rust-analyzer.git
synced 2025-10-01 22:31:43 +00:00
Auto merge of #15350 - max-heller:issue-11756, r=Veykril
Handle `#[cfg]`s on generic parameters Records attributes on generic parameters in the item tree and filters out generic parameters disabled by `#[cfg]`s in `generic_params_query`. Closes #11756
This commit is contained in:
commit
f98d654ddf
6 changed files with 142 additions and 24 deletions
|
@ -21,6 +21,7 @@ use crate::{
|
||||||
db::DefDatabase,
|
db::DefDatabase,
|
||||||
dyn_map::{keys, DynMap},
|
dyn_map::{keys, DynMap},
|
||||||
expander::Expander,
|
expander::Expander,
|
||||||
|
item_tree::{AttrOwner, ItemTree},
|
||||||
lower::LowerCtx,
|
lower::LowerCtx,
|
||||||
nameres::{DefMap, MacroSubNs},
|
nameres::{DefMap, MacroSubNs},
|
||||||
src::{HasChildSource, HasSource},
|
src::{HasChildSource, HasSource},
|
||||||
|
@ -154,12 +155,58 @@ impl GenericParams {
|
||||||
def: GenericDefId,
|
def: GenericDefId,
|
||||||
) -> Interned<GenericParams> {
|
) -> Interned<GenericParams> {
|
||||||
let _p = profile::span("generic_params_query");
|
let _p = profile::span("generic_params_query");
|
||||||
|
|
||||||
|
let krate = def.module(db).krate;
|
||||||
|
let cfg_options = db.crate_graph();
|
||||||
|
let cfg_options = &cfg_options[krate].cfg_options;
|
||||||
|
|
||||||
|
// Returns the generic parameters that are enabled under the current `#[cfg]` options
|
||||||
|
let enabled_params = |params: &Interned<GenericParams>, item_tree: &ItemTree| {
|
||||||
|
let enabled = |param| item_tree.attrs(db, krate, param).is_cfg_enabled(cfg_options);
|
||||||
|
|
||||||
|
// In the common case, no parameters will by disabled by `#[cfg]` attributes.
|
||||||
|
// Therefore, make a first pass to check if all parameters are enabled and, if so,
|
||||||
|
// clone the `Interned<GenericParams>` instead of recreating an identical copy.
|
||||||
|
let all_type_or_consts_enabled =
|
||||||
|
params.type_or_consts.iter().all(|(idx, _)| enabled(idx.into()));
|
||||||
|
let all_lifetimes_enabled = params.lifetimes.iter().all(|(idx, _)| enabled(idx.into()));
|
||||||
|
|
||||||
|
if all_type_or_consts_enabled && all_lifetimes_enabled {
|
||||||
|
params.clone()
|
||||||
|
} else {
|
||||||
|
Interned::new(GenericParams {
|
||||||
|
type_or_consts: all_type_or_consts_enabled
|
||||||
|
.then(|| params.type_or_consts.clone())
|
||||||
|
.unwrap_or_else(|| {
|
||||||
|
params
|
||||||
|
.type_or_consts
|
||||||
|
.iter()
|
||||||
|
.filter_map(|(idx, param)| {
|
||||||
|
enabled(idx.into()).then(|| param.clone())
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
}),
|
||||||
|
lifetimes: all_lifetimes_enabled
|
||||||
|
.then(|| params.lifetimes.clone())
|
||||||
|
.unwrap_or_else(|| {
|
||||||
|
params
|
||||||
|
.lifetimes
|
||||||
|
.iter()
|
||||||
|
.filter_map(|(idx, param)| {
|
||||||
|
enabled(idx.into()).then(|| param.clone())
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
}),
|
||||||
|
where_predicates: params.where_predicates.clone(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
};
|
||||||
macro_rules! id_to_generics {
|
macro_rules! id_to_generics {
|
||||||
($id:ident) => {{
|
($id:ident) => {{
|
||||||
let id = $id.lookup(db).id;
|
let id = $id.lookup(db).id;
|
||||||
let tree = id.item_tree(db);
|
let tree = id.item_tree(db);
|
||||||
let item = &tree[id.value];
|
let item = &tree[id.value];
|
||||||
item.generic_params.clone()
|
enabled_params(&item.generic_params, &tree)
|
||||||
}};
|
}};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -169,7 +216,8 @@ impl GenericParams {
|
||||||
let tree = loc.id.item_tree(db);
|
let tree = loc.id.item_tree(db);
|
||||||
let item = &tree[loc.id.value];
|
let item = &tree[loc.id.value];
|
||||||
|
|
||||||
let mut generic_params = GenericParams::clone(&item.explicit_generic_params);
|
let enabled_params = enabled_params(&item.explicit_generic_params, &tree);
|
||||||
|
let mut generic_params = GenericParams::clone(&enabled_params);
|
||||||
|
|
||||||
let module = loc.container.module(db);
|
let module = loc.container.module(db);
|
||||||
let func_data = db.function_data(id);
|
let func_data = db.function_data(id);
|
||||||
|
@ -198,9 +246,14 @@ impl GenericParams {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn fill(&mut self, lower_ctx: &LowerCtx<'_>, node: &dyn HasGenericParams) {
|
pub(crate) fn fill(
|
||||||
|
&mut self,
|
||||||
|
lower_ctx: &LowerCtx<'_>,
|
||||||
|
node: &dyn HasGenericParams,
|
||||||
|
add_param_attrs: impl FnMut(AttrOwner, ast::GenericParam),
|
||||||
|
) {
|
||||||
if let Some(params) = node.generic_param_list() {
|
if let Some(params) = node.generic_param_list() {
|
||||||
self.fill_params(lower_ctx, params)
|
self.fill_params(lower_ctx, params, add_param_attrs)
|
||||||
}
|
}
|
||||||
if let Some(where_clause) = node.where_clause() {
|
if let Some(where_clause) = node.where_clause() {
|
||||||
self.fill_where_predicates(lower_ctx, where_clause);
|
self.fill_where_predicates(lower_ctx, where_clause);
|
||||||
|
@ -218,7 +271,12 @@ impl GenericParams {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fill_params(&mut self, lower_ctx: &LowerCtx<'_>, params: ast::GenericParamList) {
|
fn fill_params(
|
||||||
|
&mut self,
|
||||||
|
lower_ctx: &LowerCtx<'_>,
|
||||||
|
params: ast::GenericParamList,
|
||||||
|
mut add_param_attrs: impl FnMut(AttrOwner, ast::GenericParam),
|
||||||
|
) {
|
||||||
for type_or_const_param in params.type_or_const_params() {
|
for type_or_const_param in params.type_or_const_params() {
|
||||||
match type_or_const_param {
|
match type_or_const_param {
|
||||||
ast::TypeOrConstParam::Type(type_param) => {
|
ast::TypeOrConstParam::Type(type_param) => {
|
||||||
|
@ -232,13 +290,14 @@ impl GenericParams {
|
||||||
default,
|
default,
|
||||||
provenance: TypeParamProvenance::TypeParamList,
|
provenance: TypeParamProvenance::TypeParamList,
|
||||||
};
|
};
|
||||||
self.type_or_consts.alloc(param.into());
|
let idx = self.type_or_consts.alloc(param.into());
|
||||||
let type_ref = TypeRef::Path(name.into());
|
let type_ref = TypeRef::Path(name.into());
|
||||||
self.fill_bounds(
|
self.fill_bounds(
|
||||||
lower_ctx,
|
lower_ctx,
|
||||||
type_param.type_bound_list(),
|
type_param.type_bound_list(),
|
||||||
Either::Left(type_ref),
|
Either::Left(type_ref),
|
||||||
);
|
);
|
||||||
|
add_param_attrs(idx.into(), ast::GenericParam::TypeParam(type_param));
|
||||||
}
|
}
|
||||||
ast::TypeOrConstParam::Const(const_param) => {
|
ast::TypeOrConstParam::Const(const_param) => {
|
||||||
let name = const_param.name().map_or_else(Name::missing, |it| it.as_name());
|
let name = const_param.name().map_or_else(Name::missing, |it| it.as_name());
|
||||||
|
@ -250,7 +309,8 @@ impl GenericParams {
|
||||||
ty: Interned::new(ty),
|
ty: Interned::new(ty),
|
||||||
has_default: const_param.default_val().is_some(),
|
has_default: const_param.default_val().is_some(),
|
||||||
};
|
};
|
||||||
self.type_or_consts.alloc(param.into());
|
let idx = self.type_or_consts.alloc(param.into());
|
||||||
|
add_param_attrs(idx.into(), ast::GenericParam::ConstParam(const_param));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -258,13 +318,14 @@ impl GenericParams {
|
||||||
let name =
|
let name =
|
||||||
lifetime_param.lifetime().map_or_else(Name::missing, |lt| Name::new_lifetime(<));
|
lifetime_param.lifetime().map_or_else(Name::missing, |lt| Name::new_lifetime(<));
|
||||||
let param = LifetimeParamData { name: name.clone() };
|
let param = LifetimeParamData { name: name.clone() };
|
||||||
self.lifetimes.alloc(param);
|
let idx = self.lifetimes.alloc(param);
|
||||||
let lifetime_ref = LifetimeRef::new_name(name);
|
let lifetime_ref = LifetimeRef::new_name(name);
|
||||||
self.fill_bounds(
|
self.fill_bounds(
|
||||||
lower_ctx,
|
lower_ctx,
|
||||||
lifetime_param.type_bound_list(),
|
lifetime_param.type_bound_list(),
|
||||||
Either::Right(lifetime_ref),
|
Either::Right(lifetime_ref),
|
||||||
);
|
);
|
||||||
|
add_param_attrs(idx.into(), ast::GenericParam::LifetimeParam(lifetime_param));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -64,7 +64,7 @@ use triomphe::Arc;
|
||||||
use crate::{
|
use crate::{
|
||||||
attr::Attrs,
|
attr::Attrs,
|
||||||
db::DefDatabase,
|
db::DefDatabase,
|
||||||
generics::GenericParams,
|
generics::{GenericParams, LifetimeParamData, TypeOrConstParamData},
|
||||||
path::{path, AssociatedTypeBinding, GenericArgs, ImportAlias, ModPath, Path, PathKind},
|
path::{path, AssociatedTypeBinding, GenericArgs, ImportAlias, ModPath, Path, PathKind},
|
||||||
type_ref::{Mutability, TraitRef, TypeBound, TypeRef},
|
type_ref::{Mutability, TraitRef, TypeBound, TypeRef},
|
||||||
visibility::RawVisibility,
|
visibility::RawVisibility,
|
||||||
|
@ -296,10 +296,12 @@ pub enum AttrOwner {
|
||||||
Variant(Idx<Variant>),
|
Variant(Idx<Variant>),
|
||||||
Field(Idx<Field>),
|
Field(Idx<Field>),
|
||||||
Param(Idx<Param>),
|
Param(Idx<Param>),
|
||||||
|
TypeOrConstParamData(Idx<TypeOrConstParamData>),
|
||||||
|
LifetimeParamData(Idx<LifetimeParamData>),
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! from_attrs {
|
macro_rules! from_attrs {
|
||||||
( $( $var:ident($t:ty) ),+ ) => {
|
( $( $var:ident($t:ty) ),+ $(,)? ) => {
|
||||||
$(
|
$(
|
||||||
impl From<$t> for AttrOwner {
|
impl From<$t> for AttrOwner {
|
||||||
fn from(t: $t) -> AttrOwner {
|
fn from(t: $t) -> AttrOwner {
|
||||||
|
@ -310,7 +312,14 @@ macro_rules! from_attrs {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
from_attrs!(ModItem(ModItem), Variant(Idx<Variant>), Field(Idx<Field>), Param(Idx<Param>));
|
from_attrs!(
|
||||||
|
ModItem(ModItem),
|
||||||
|
Variant(Idx<Variant>),
|
||||||
|
Field(Idx<Field>),
|
||||||
|
Param(Idx<Param>),
|
||||||
|
TypeOrConstParamData(Idx<TypeOrConstParamData>),
|
||||||
|
LifetimeParamData(Idx<LifetimeParamData>),
|
||||||
|
);
|
||||||
|
|
||||||
/// Trait implemented by all item nodes in the item tree.
|
/// Trait implemented by all item nodes in the item tree.
|
||||||
pub trait ItemTreeNode: Clone {
|
pub trait ItemTreeNode: Clone {
|
||||||
|
|
|
@ -602,7 +602,21 @@ impl<'a> Ctx<'a> {
|
||||||
generics.fill_bounds(&self.body_ctx, bounds, Either::Left(self_param));
|
generics.fill_bounds(&self.body_ctx, bounds, Either::Left(self_param));
|
||||||
}
|
}
|
||||||
|
|
||||||
generics.fill(&self.body_ctx, node);
|
let add_param_attrs = |item, param| {
|
||||||
|
let attrs = RawAttrs::new(self.db.upcast(), ¶m, self.body_ctx.hygiene());
|
||||||
|
// This is identical to the body of `Ctx::add_attrs()` but we can't call that here
|
||||||
|
// because it requires `&mut self` and the call to `generics.fill()` below also
|
||||||
|
// references `self`.
|
||||||
|
match self.tree.attrs.entry(item) {
|
||||||
|
Entry::Occupied(mut entry) => {
|
||||||
|
*entry.get_mut() = entry.get().merge(attrs);
|
||||||
|
}
|
||||||
|
Entry::Vacant(entry) => {
|
||||||
|
entry.insert(attrs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
generics.fill(&self.body_ctx, node, add_param_attrs);
|
||||||
|
|
||||||
generics.shrink_to_fit();
|
generics.shrink_to_fit();
|
||||||
Interned::new(generics)
|
Interned::new(generics)
|
||||||
|
|
|
@ -16,7 +16,7 @@ pub(super) fn print_item_tree(db: &dyn ExpandDatabase, tree: &ItemTree) -> Strin
|
||||||
let mut p = Printer { db, tree, buf: String::new(), indent_level: 0, needs_indent: true };
|
let mut p = Printer { db, tree, buf: String::new(), indent_level: 0, needs_indent: true };
|
||||||
|
|
||||||
if let Some(attrs) = tree.attrs.get(&AttrOwner::TopLevel) {
|
if let Some(attrs) = tree.attrs.get(&AttrOwner::TopLevel) {
|
||||||
p.print_attrs(attrs, true);
|
p.print_attrs(attrs, true, "\n");
|
||||||
}
|
}
|
||||||
p.blank();
|
p.blank();
|
||||||
|
|
||||||
|
@ -84,22 +84,23 @@ impl Printer<'_> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn print_attrs(&mut self, attrs: &RawAttrs, inner: bool) {
|
fn print_attrs(&mut self, attrs: &RawAttrs, inner: bool, separated_by: &str) {
|
||||||
let inner = if inner { "!" } else { "" };
|
let inner = if inner { "!" } else { "" };
|
||||||
for attr in &**attrs {
|
for attr in &**attrs {
|
||||||
wln!(
|
w!(
|
||||||
self,
|
self,
|
||||||
"#{}[{}{}]",
|
"#{}[{}{}]{}",
|
||||||
inner,
|
inner,
|
||||||
attr.path.display(self.db),
|
attr.path.display(self.db),
|
||||||
attr.input.as_ref().map(|it| it.to_string()).unwrap_or_default(),
|
attr.input.as_ref().map(|it| it.to_string()).unwrap_or_default(),
|
||||||
|
separated_by,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn print_attrs_of(&mut self, of: impl Into<AttrOwner>) {
|
fn print_attrs_of(&mut self, of: impl Into<AttrOwner>, separated_by: &str) {
|
||||||
if let Some(attrs) = self.tree.attrs.get(&of.into()) {
|
if let Some(attrs) = self.tree.attrs.get(&of.into()) {
|
||||||
self.print_attrs(attrs, false);
|
self.print_attrs(attrs, false, separated_by);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -118,7 +119,7 @@ impl Printer<'_> {
|
||||||
self.indented(|this| {
|
self.indented(|this| {
|
||||||
for field in fields.clone() {
|
for field in fields.clone() {
|
||||||
let Field { visibility, name, type_ref, ast_id: _ } = &this.tree[field];
|
let Field { visibility, name, type_ref, ast_id: _ } = &this.tree[field];
|
||||||
this.print_attrs_of(field);
|
this.print_attrs_of(field, "\n");
|
||||||
this.print_visibility(*visibility);
|
this.print_visibility(*visibility);
|
||||||
w!(this, "{}: ", name.display(self.db));
|
w!(this, "{}: ", name.display(self.db));
|
||||||
this.print_type_ref(type_ref);
|
this.print_type_ref(type_ref);
|
||||||
|
@ -132,7 +133,7 @@ impl Printer<'_> {
|
||||||
self.indented(|this| {
|
self.indented(|this| {
|
||||||
for field in fields.clone() {
|
for field in fields.clone() {
|
||||||
let Field { visibility, name, type_ref, ast_id: _ } = &this.tree[field];
|
let Field { visibility, name, type_ref, ast_id: _ } = &this.tree[field];
|
||||||
this.print_attrs_of(field);
|
this.print_attrs_of(field, "\n");
|
||||||
this.print_visibility(*visibility);
|
this.print_visibility(*visibility);
|
||||||
w!(this, "{}: ", name.display(self.db));
|
w!(this, "{}: ", name.display(self.db));
|
||||||
this.print_type_ref(type_ref);
|
this.print_type_ref(type_ref);
|
||||||
|
@ -195,7 +196,7 @@ impl Printer<'_> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn print_mod_item(&mut self, item: ModItem) {
|
fn print_mod_item(&mut self, item: ModItem) {
|
||||||
self.print_attrs_of(item);
|
self.print_attrs_of(item, "\n");
|
||||||
|
|
||||||
match item {
|
match item {
|
||||||
ModItem::Use(it) => {
|
ModItem::Use(it) => {
|
||||||
|
@ -261,7 +262,7 @@ impl Printer<'_> {
|
||||||
if !params.is_empty() {
|
if !params.is_empty() {
|
||||||
self.indented(|this| {
|
self.indented(|this| {
|
||||||
for param in params.clone() {
|
for param in params.clone() {
|
||||||
this.print_attrs_of(param);
|
this.print_attrs_of(param, "\n");
|
||||||
match &this.tree[param] {
|
match &this.tree[param] {
|
||||||
Param::Normal(ty) => {
|
Param::Normal(ty) => {
|
||||||
if flags.contains(FnFlags::HAS_SELF_PARAM) {
|
if flags.contains(FnFlags::HAS_SELF_PARAM) {
|
||||||
|
@ -319,7 +320,7 @@ impl Printer<'_> {
|
||||||
self.indented(|this| {
|
self.indented(|this| {
|
||||||
for variant in variants.clone() {
|
for variant in variants.clone() {
|
||||||
let Variant { name, fields, ast_id: _ } = &this.tree[variant];
|
let Variant { name, fields, ast_id: _ } = &this.tree[variant];
|
||||||
this.print_attrs_of(variant);
|
this.print_attrs_of(variant, "\n");
|
||||||
w!(this, "{}", name.display(self.db));
|
w!(this, "{}", name.display(self.db));
|
||||||
this.print_fields(fields);
|
this.print_fields(fields);
|
||||||
wln!(this, ",");
|
wln!(this, ",");
|
||||||
|
@ -484,11 +485,12 @@ impl Printer<'_> {
|
||||||
|
|
||||||
w!(self, "<");
|
w!(self, "<");
|
||||||
let mut first = true;
|
let mut first = true;
|
||||||
for (_, lt) in params.lifetimes.iter() {
|
for (idx, lt) in params.lifetimes.iter() {
|
||||||
if !first {
|
if !first {
|
||||||
w!(self, ", ");
|
w!(self, ", ");
|
||||||
}
|
}
|
||||||
first = false;
|
first = false;
|
||||||
|
self.print_attrs_of(idx, " ");
|
||||||
w!(self, "{}", lt.name.display(self.db));
|
w!(self, "{}", lt.name.display(self.db));
|
||||||
}
|
}
|
||||||
for (idx, x) in params.type_or_consts.iter() {
|
for (idx, x) in params.type_or_consts.iter() {
|
||||||
|
@ -496,6 +498,7 @@ impl Printer<'_> {
|
||||||
w!(self, ", ");
|
w!(self, ", ");
|
||||||
}
|
}
|
||||||
first = false;
|
first = false;
|
||||||
|
self.print_attrs_of(idx, " ");
|
||||||
match x {
|
match x {
|
||||||
TypeOrConstParamData::TypeParamData(ty) => match &ty.name {
|
TypeOrConstParamData::TypeParamData(ty) => match &ty.name {
|
||||||
Some(name) => w!(self, "{}", name.display(self.db)),
|
Some(name) => w!(self, "{}", name.display(self.db)),
|
||||||
|
|
|
@ -358,3 +358,15 @@ trait Tr<'a, T: 'a>: Super where Self: for<'a> Tr<'a, T> {}
|
||||||
"#]],
|
"#]],
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn generics_with_attributes() {
|
||||||
|
check(
|
||||||
|
r#"
|
||||||
|
struct S<#[cfg(never)] T>;
|
||||||
|
"#,
|
||||||
|
expect![[r#"
|
||||||
|
pub(self) struct S<#[cfg(never)] T>;
|
||||||
|
"#]],
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
|
@ -6469,3 +6469,22 @@ fn test() {
|
||||||
"#]],
|
"#]],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn generic_params_disabled_by_cfg() {
|
||||||
|
check(
|
||||||
|
r#"
|
||||||
|
struct S<#[cfg(never)] T>;
|
||||||
|
fn test() {
|
||||||
|
let s$0: S = S;
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
expect![[r#"
|
||||||
|
*s*
|
||||||
|
|
||||||
|
```rust
|
||||||
|
let s: S // size = 0, align = 1
|
||||||
|
```
|
||||||
|
"#]],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue