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:
bors 2023-08-08 13:05:26 +00:00
commit f98d654ddf
6 changed files with 142 additions and 24 deletions

View file

@ -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(&lt)); lifetime_param.lifetime().map_or_else(Name::missing, |lt| Name::new_lifetime(&lt));
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));
} }
} }

View file

@ -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 {

View file

@ -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(), &param, 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)

View file

@ -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)),

View file

@ -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>;
"#]],
)
}

View file

@ -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
```
"#]],
);
}