Auto merge of #109005 - Nilstrieb:dont-forgor-too-much-from-cfg, r=petrochenkov

Remember names of `cfg`-ed out items to mention them in diagnostics

# Examples

## `serde::Deserialize` without the `derive` feature (a classic beginner mistake)

I had to slightly modify serde so that it uses explicit re-exports instead of a glob re-export. (Update: a serde PR was merged that adds the manual re-exports)

```
error[E0433]: failed to resolve: could not find `Serialize` in `serde`
   --> src/main.rs:1:17
    |
1   | #[derive(serde::Serialize)]
    |                 ^^^^^^^^^ could not find `Serialize` in `serde`
    |
note: crate `serde` has an item named `Serialize` but it is inactive because its cfg predicate evaluated to false
   --> /home/gh-Nilstrieb/.cargo/registry/src/index.crates.io-6f17d22bba15001f/serde-1.0.160/src/lib.rs:343:1
    |
343 | #[cfg(feature = "serde_derive")]
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
344 | pub use serde_derive::{Deserialize, Serialize};
    |                                     ^^^^^^^^^
    = note: the item is gated behind the `serde_derive` feature
    = note: see https://doc.rust-lang.org/cargo/reference/features.html for how to activate a crate's feature
```
(the suggestion is not ideal but that's serde's fault)

I already tested the metadata size impact locally by compiling the `windows` crate without any features. `800k`  -> `809k`

r? `@ghost`
This commit is contained in:
bors 2023-06-07 17:38:57 +00:00
commit a97c36dd2e
30 changed files with 599 additions and 84 deletions

View file

@ -947,6 +947,8 @@ pub trait ResolverExpand {
/// HIR proc macros items back to their harness items.
fn declare_proc_macro(&mut self, id: NodeId);
fn append_stripped_cfg_item(&mut self, parent_node: NodeId, name: Ident, cfg: ast::MetaItem);
/// Tools registered with `#![register_tool]` and used by tool attributes and lints.
fn registered_tools(&self) -> &RegisteredTools;
}
@ -965,7 +967,7 @@ pub trait LintStoreExpand {
type LintStoreExpandDyn<'a> = Option<&'a (dyn LintStoreExpand + 'a)>;
#[derive(Clone, Default)]
#[derive(Debug, Clone, Default)]
pub struct ModuleData {
/// Path to the module starting from the crate name, like `my_crate::foo::bar`.
pub mod_path: Vec<Ident>,

View file

@ -416,20 +416,28 @@ impl<'a> StripUnconfigured<'a> {
/// Determines if a node with the given attributes should be included in this configuration.
fn in_cfg(&self, attrs: &[Attribute]) -> bool {
attrs.iter().all(|attr| !is_cfg(attr) || self.cfg_true(attr))
attrs.iter().all(|attr| !is_cfg(attr) || self.cfg_true(attr).0)
}
pub(crate) fn cfg_true(&self, attr: &Attribute) -> bool {
pub(crate) fn cfg_true(&self, attr: &Attribute) -> (bool, Option<MetaItem>) {
let meta_item = match validate_attr::parse_meta(&self.sess.parse_sess, attr) {
Ok(meta_item) => meta_item,
Err(mut err) => {
err.emit();
return true;
return (true, None);
}
};
parse_cfg(&meta_item, &self.sess).map_or(true, |meta_item| {
attr::cfg_matches(&meta_item, &self.sess.parse_sess, self.lint_node_id, self.features)
})
(
parse_cfg(&meta_item, &self.sess).map_or(true, |meta_item| {
attr::cfg_matches(
&meta_item,
&self.sess.parse_sess,
self.lint_node_id,
self.features,
)
}),
Some(meta_item),
)
}
/// If attributes are not allowed on expressions, emit an error for `attr`

View file

@ -1042,6 +1042,12 @@ trait InvocationCollectorNode: HasAttrs + HasNodeId + Sized {
fn expand_cfg_false(&mut self, collector: &mut InvocationCollector<'_, '_>, span: Span) {
collector.cx.emit_err(RemoveNodeNotSupported { span, descr: Self::descr() });
}
/// All of the names (items) declared by this node.
/// This is an approximation and should only be used for diagnostics.
fn declared_names(&self) -> Vec<Ident> {
vec![]
}
}
impl InvocationCollectorNode for P<ast::Item> {
@ -1148,6 +1154,27 @@ impl InvocationCollectorNode for P<ast::Item> {
collector.cx.current_expansion.module = orig_module;
res
}
fn declared_names(&self) -> Vec<Ident> {
if let ItemKind::Use(ut) = &self.kind {
fn collect_use_tree_leaves(ut: &ast::UseTree, idents: &mut Vec<Ident>) {
match &ut.kind {
ast::UseTreeKind::Glob => {}
ast::UseTreeKind::Simple(_) => idents.push(ut.ident()),
ast::UseTreeKind::Nested(nested) => {
for (ut, _) in nested {
collect_use_tree_leaves(&ut, idents);
}
}
}
}
let mut idents = Vec::new();
collect_use_tree_leaves(&ut, &mut idents);
return idents;
}
vec![self.ident]
}
}
struct TraitItemTag;
@ -1685,8 +1712,8 @@ impl<'a, 'b> InvocationCollector<'a, 'b> {
node: &mut impl HasAttrs,
attr: ast::Attribute,
pos: usize,
) -> bool {
let res = self.cfg().cfg_true(&attr);
) -> (bool, Option<ast::MetaItem>) {
let (res, meta_item) = self.cfg().cfg_true(&attr);
if res {
// FIXME: `cfg(TRUE)` attributes do not currently remove themselves during expansion,
// and some tools like rustdoc and clippy rely on that. Find a way to remove them
@ -1694,7 +1721,8 @@ impl<'a, 'b> InvocationCollector<'a, 'b> {
self.cx.expanded_inert_attrs.mark(&attr);
node.visit_attrs(|attrs| attrs.insert(pos, attr));
}
res
(res, meta_item)
}
fn expand_cfg_attr(&self, node: &mut impl HasAttrs, attr: &ast::Attribute, pos: usize) {
@ -1715,9 +1743,20 @@ impl<'a, 'b> InvocationCollector<'a, 'b> {
return match self.take_first_attr(&mut node) {
Some((attr, pos, derives)) => match attr.name_or_empty() {
sym::cfg => {
if self.expand_cfg_true(&mut node, attr, pos) {
let (res, meta_item) = self.expand_cfg_true(&mut node, attr, pos);
if res {
continue;
}
if let Some(meta_item) = meta_item {
for name in node.declared_names() {
self.cx.resolver.append_stripped_cfg_item(
self.cx.current_expansion.lint_node_id,
name,
meta_item.clone(),
)
}
}
Default::default()
}
sym::cfg_attr => {
@ -1761,7 +1800,7 @@ impl<'a, 'b> InvocationCollector<'a, 'b> {
Some((attr, pos, derives)) => match attr.name_or_empty() {
sym::cfg => {
let span = attr.span;
if self.expand_cfg_true(node, attr, pos) {
if self.expand_cfg_true(node, attr, pos).0 {
continue;
}