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,
dyn_map::{keys, DynMap},
expander::Expander,
item_tree::{AttrOwner, ItemTree},
lower::LowerCtx,
nameres::{DefMap, MacroSubNs},
src::{HasChildSource, HasSource},
@ -154,12 +155,58 @@ impl GenericParams {
def: GenericDefId,
) -> Interned<GenericParams> {
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 {
($id:ident) => {{
let id = $id.lookup(db).id;
let tree = id.item_tree(db);
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 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 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() {
self.fill_params(lower_ctx, params)
self.fill_params(lower_ctx, params, add_param_attrs)
}
if let Some(where_clause) = node.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() {
match type_or_const_param {
ast::TypeOrConstParam::Type(type_param) => {
@ -232,13 +290,14 @@ impl GenericParams {
default,
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());
self.fill_bounds(
lower_ctx,
type_param.type_bound_list(),
Either::Left(type_ref),
);
add_param_attrs(idx.into(), ast::GenericParam::TypeParam(type_param));
}
ast::TypeOrConstParam::Const(const_param) => {
let name = const_param.name().map_or_else(Name::missing, |it| it.as_name());
@ -250,7 +309,8 @@ impl GenericParams {
ty: Interned::new(ty),
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 =
lifetime_param.lifetime().map_or_else(Name::missing, |lt| Name::new_lifetime(&lt));
let param = LifetimeParamData { name: name.clone() };
self.lifetimes.alloc(param);
let idx = self.lifetimes.alloc(param);
let lifetime_ref = LifetimeRef::new_name(name);
self.fill_bounds(
lower_ctx,
lifetime_param.type_bound_list(),
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::{
attr::Attrs,
db::DefDatabase,
generics::GenericParams,
generics::{GenericParams, LifetimeParamData, TypeOrConstParamData},
path::{path, AssociatedTypeBinding, GenericArgs, ImportAlias, ModPath, Path, PathKind},
type_ref::{Mutability, TraitRef, TypeBound, TypeRef},
visibility::RawVisibility,
@ -296,10 +296,12 @@ pub enum AttrOwner {
Variant(Idx<Variant>),
Field(Idx<Field>),
Param(Idx<Param>),
TypeOrConstParamData(Idx<TypeOrConstParamData>),
LifetimeParamData(Idx<LifetimeParamData>),
}
macro_rules! from_attrs {
( $( $var:ident($t:ty) ),+ ) => {
( $( $var:ident($t:ty) ),+ $(,)? ) => {
$(
impl From<$t> for 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.
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(&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();
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 };
if let Some(attrs) = tree.attrs.get(&AttrOwner::TopLevel) {
p.print_attrs(attrs, true);
p.print_attrs(attrs, true, "\n");
}
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 { "" };
for attr in &**attrs {
wln!(
w!(
self,
"#{}[{}{}]",
"#{}[{}{}]{}",
inner,
attr.path.display(self.db),
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()) {
self.print_attrs(attrs, false);
self.print_attrs(attrs, false, separated_by);
}
}
@ -118,7 +119,7 @@ impl Printer<'_> {
self.indented(|this| {
for field in fields.clone() {
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);
w!(this, "{}: ", name.display(self.db));
this.print_type_ref(type_ref);
@ -132,7 +133,7 @@ impl Printer<'_> {
self.indented(|this| {
for field in fields.clone() {
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);
w!(this, "{}: ", name.display(self.db));
this.print_type_ref(type_ref);
@ -195,7 +196,7 @@ impl Printer<'_> {
}
fn print_mod_item(&mut self, item: ModItem) {
self.print_attrs_of(item);
self.print_attrs_of(item, "\n");
match item {
ModItem::Use(it) => {
@ -261,7 +262,7 @@ impl Printer<'_> {
if !params.is_empty() {
self.indented(|this| {
for param in params.clone() {
this.print_attrs_of(param);
this.print_attrs_of(param, "\n");
match &this.tree[param] {
Param::Normal(ty) => {
if flags.contains(FnFlags::HAS_SELF_PARAM) {
@ -319,7 +320,7 @@ impl Printer<'_> {
self.indented(|this| {
for variant in variants.clone() {
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));
this.print_fields(fields);
wln!(this, ",");
@ -484,11 +485,12 @@ impl Printer<'_> {
w!(self, "<");
let mut first = true;
for (_, lt) in params.lifetimes.iter() {
for (idx, lt) in params.lifetimes.iter() {
if !first {
w!(self, ", ");
}
first = false;
self.print_attrs_of(idx, " ");
w!(self, "{}", lt.name.display(self.db));
}
for (idx, x) in params.type_or_consts.iter() {
@ -496,6 +498,7 @@ impl Printer<'_> {
w!(self, ", ");
}
first = false;
self.print_attrs_of(idx, " ");
match x {
TypeOrConstParamData::TypeParamData(ty) => match &ty.name {
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
```
"#]],
);
}