diff --git a/compiler/rustc_attr_parsing/src/attributes/cfg_select.rs b/compiler/rustc_attr_parsing/src/attributes/cfg_select.rs index b860770ca32a5..68de0a53e918b 100644 --- a/compiler/rustc_attr_parsing/src/attributes/cfg_select.rs +++ b/compiler/rustc_attr_parsing/src/attributes/cfg_select.rs @@ -47,25 +47,28 @@ pub struct CfgSelectBranches { impl CfgSelectBranches { /// Removes the top-most branch for which `predicate` returns `true`, /// or the wildcard if none of the reachable branches satisfied the predicate. - pub fn pop_first_match(&mut self, predicate: F) -> Option<(TokenStream, Span)> + pub fn pop_first_match(&mut self, predicate: F) -> Option<(CfgEntry, TokenStream, Span)> where F: Fn(&CfgEntry) -> bool, { for (index, (cfg, _, _)) in self.reachable.iter().enumerate() { if predicate(cfg) { - let matched = self.reachable.remove(index); - return Some((matched.1, matched.2)); + return Some(self.reachable.remove(index)); } } - self.wildcard.take().map(|(_, tts, span)| (tts, span)) + self.wildcard.take().map(|(_, tts, span)| (CfgEntry::Bool(true, span), tts, span)) } /// Consume this value and iterate over all the `TokenStream`s that it stores. - pub fn into_iter_tts(self) -> impl Iterator { - let it1 = self.reachable.into_iter().map(|(_, tts, span)| (tts, span)); - let it2 = self.wildcard.into_iter().map(|(_, tts, span)| (tts, span)); - let it3 = self.unreachable.into_iter().map(|(_, tts, span)| (tts, span)); + pub fn into_iter_tts(self) -> impl Iterator { + let it1 = self.reachable.into_iter(); + let it2 = + self.wildcard.into_iter().map(|(_, tts, span)| (CfgEntry::Bool(true, span), tts, span)); + let it3 = self + .unreachable + .into_iter() + .map(|(_, tts, span)| (CfgEntry::Bool(false, span), tts, span)); it1.chain(it2).chain(it3) } diff --git a/compiler/rustc_builtin_macros/src/cfg_select.rs b/compiler/rustc_builtin_macros/src/cfg_select.rs index 5df24c1a47ed6..7202c56efed4e 100644 --- a/compiler/rustc_builtin_macros/src/cfg_select.rs +++ b/compiler/rustc_builtin_macros/src/cfg_select.rs @@ -1,8 +1,11 @@ +use rustc_ast::attr::AttrIdGenerator; use rustc_ast::tokenstream::TokenStream; -use rustc_ast::{Expr, ast}; +use rustc_ast::{AttrKind, Expr, SyntheticAttr, ast}; +use rustc_attr_ir::CfgEntry; use rustc_attr_parsing as attr; use rustc_attr_parsing::{CfgSelectBranches, EvalConfigResult, parse_cfg_select}; use rustc_expand::base::{DummyResult, ExpandResult, ExtCtxt, MacResult, MacroExpanderResult}; +use rustc_expand::expand::DeclaredIdents; use rustc_span::{Ident, Span, sym}; use smallvec::SmallVec; @@ -17,6 +20,7 @@ struct CfgSelectResult<'cx, 'sess> { selected_tts: TokenStream, selected_span: Span, other_branches: CfgSelectBranches, + cfg_entry: CfgEntry, } fn tts_to_mac_result<'cx, 'sess>( @@ -33,23 +37,74 @@ fn tts_to_mac_result<'cx, 'sess>( } macro_rules! forward_to_parser_any_macro { - ($method_name:ident, $ret_ty:ty) => { + ($method_name:ident, $ret_ty:ty, $other:expr, $selected:expr) => { fn $method_name(self: Box) -> Option<$ret_ty> { - let CfgSelectResult { ecx, site_span, selected_tts, selected_span, .. } = *self; + let CfgSelectResult { ecx, site_span, selected_tts, selected_span, cfg_entry, .. } = + *self; - for (tts, span) in self.other_branches.into_iter_tts() { - let _ = tts_to_mac_result(ecx, site_span, tts, span).$method_name(); + for (cfg_entry, tts, span) in self.other_branches.into_iter_tts() { + let result = tts_to_mac_result(ecx, site_span, tts, span).$method_name(); + $other(&mut *ecx, cfg_entry, span, result); } - tts_to_mac_result(ecx, site_span, selected_tts, selected_span).$method_name() + tts_to_mac_result(ecx, site_span, selected_tts, selected_span) + .$method_name() + .map(|elements| $selected(&mut *ecx, cfg_entry, elements)) } }; + + ($method_name:ident, $ret_ty:ty) => { + forward_to_parser_any_macro!($method_name, $ret_ty, |_, _, _, _| {}, |_, _, elements| { + elements + }); + }; +} + +/// Construct a `#[]` attribute from a `CfgEntry`. This allows us to keep track of items +/// that were behind a `cfg_select!`, which is relevant for some diagnostics. +fn mk_attr(g: &AttrIdGenerator, cfg_entry: CfgEntry) -> ast::Attribute { + let cfg_span = cfg_entry.span(); + ast::Attribute { + kind: AttrKind::Synthetic(Box::new(SyntheticAttr::CfgAttrTrace(cfg_entry))), + id: g.mk_attr_id(), + style: ast::AttrStyle::Outer, + span: cfg_span, + } } impl<'cx, 'sess> MacResult for CfgSelectResult<'cx, 'sess> { forward_to_parser_any_macro!(make_expr, Box); forward_to_parser_any_macro!(make_stmts, SmallVec<[ast::Stmt; 1]>); - forward_to_parser_any_macro!(make_items, SmallVec<[Box; 1]>); + forward_to_parser_any_macro!( + make_items, + SmallVec<[Box; 1]>, + |ecx: &mut ExtCtxt<'_>, + cfg_entry: CfgEntry, + _span: Span, + items: Option; 1]>>| if let Some(items) = items { + // Register item names that were not selected for error reporting. We do this + // for `#[cfg]` too. + for item in items { + for name in item.declared_idents() { + ecx.resolver.append_stripped_cfg_item( + ecx.current_expansion.lint_node_id, + name, + cfg_entry.clone(), + cfg_entry.span(), + ); + } + } + }, + |ecx: &mut ExtCtxt<'_>, cfg_entry: CfgEntry, items: SmallVec<[Box; 1]>| { + items + .into_iter() + .map(|mut item| { + item.attrs.push(mk_attr(&ecx.sess.psess.attr_id_generator, cfg_entry.clone())); + item + }) + .collect() + } + ); forward_to_parser_any_macro!(make_impl_items, SmallVec<[Box; 1]>); forward_to_parser_any_macro!(make_trait_impl_items, SmallVec<[Box; 1]>); @@ -73,15 +128,18 @@ pub(super) fn expand_cfg_select<'cx>( ecx.current_expansion.lint_node_id, ) { Ok(mut branches) => { - if let Some((selected_tts, selected_span)) = branches.pop_first_match(|cfg| { - matches!(attr::eval_config_entry(ecx.sess, cfg), EvalConfigResult::True) - }) { + if let Some((cfg_entry, selected_tts, selected_span)) = + branches.pop_first_match(|cfg| { + matches!(attr::eval_config_entry(ecx.sess, cfg), EvalConfigResult::True) + }) + { let mac = CfgSelectResult { ecx, selected_tts, selected_span, other_branches: branches, site_span: sp, + cfg_entry, }; return ExpandResult::Ready(Box::new(mac)); } else { diff --git a/compiler/rustc_expand/src/expand.rs b/compiler/rustc_expand/src/expand.rs index 3a51b758a427b..e4d84847db55d 100644 --- a/compiler/rustc_expand/src/expand.rs +++ b/compiler/rustc_expand/src/expand.rs @@ -1229,7 +1229,7 @@ enum AddSemicolon { /// A trait implemented for all `AstFragment` nodes and providing all pieces /// of functionality used by `InvocationCollector`. -trait InvocationCollectorNode: HasAttrs + HasNodeId + Sized { +trait InvocationCollectorNode: HasAttrs + HasNodeId + Sized + DeclaredIdents { type OutputTy = SmallVec<[Self; 1]>; type ItemKind = ItemKind; const KIND: AstFragmentKind; @@ -1281,13 +1281,44 @@ trait InvocationCollectorNode: HasAttrs + HasNodeId + Sized { collector.cx.dcx().emit_err(RemoveNodeNotSupported { span, descr: Self::descr() }); } + fn as_target(&self) -> Target; +} + +pub trait DeclaredIdents { /// All of the identifiers (items) declared by this node. /// This is an approximation and should only be used for diagnostics. fn declared_idents(&self) -> Vec { vec![] } +} - fn as_target(&self) -> Target; +macro_rules! declared_idents { + ($($ty:ty),*) => { + $(impl DeclaredIdents for $ty {})* + }; +} + +// Use the default "empty" list of idents for the following: +declared_idents! { + AstNodeWrapper, TraitItemTag>, + AstNodeWrapper, ImplItemTag>, + AstNodeWrapper, TraitImplItemTag>, + Box, + ast::Variant, + ast::WherePredicate, + ast::FieldDef, + ast::PatField, + ast::ExprField, + ast::Param, + ast::GenericParam, + ast::Arm, + ast::Stmt, + ast::Crate, + ast::Ty, + ast::Pat, + ast::Expr, + AstNodeWrapper, OptExprTag>, + AstNodeWrapper } impl InvocationCollectorNode for Box { @@ -1415,6 +1446,12 @@ impl InvocationCollectorNode for Box { res } + fn as_target(&self) -> Target { + Target::from_ast_item(self) + } +} + +impl DeclaredIdents for Box { fn declared_idents(&self) -> Vec { if let ItemKind::Use(ut) = &self.kind { fn collect_use_tree_leaves(ut: &ast::UseTree, idents: &mut Vec) { @@ -1435,10 +1472,6 @@ impl InvocationCollectorNode for Box { self.kind.ident().into_iter().collect() } } - - fn as_target(&self) -> Target { - Target::from_ast_item(self) - } } struct TraitItemTag; diff --git a/tests/ui/cfg/auxiliary/cfged_out.rs b/tests/ui/cfg/auxiliary/cfged_out.rs index 564280b24f598..fff8b7bab8cba 100644 --- a/tests/ui/cfg/auxiliary/cfged_out.rs +++ b/tests/ui/cfg/auxiliary/cfged_out.rs @@ -3,10 +3,19 @@ pub mod inner { pub fn uwu() {} #[cfg(false)] - pub mod doesnt_exist { + pub mod cfgd_out { pub fn hello() {} } + cfg_select! { + false => { + pub mod selected_out { + pub fn hello() {} + } + } + _ => {} + } + pub mod wrong { #[cfg(feature = "suggesting me fails the test!!")] pub fn meow() {} diff --git a/tests/ui/cfg/diagnostics-cross-crate.rs b/tests/ui/cfg/diagnostics-cross-crate.rs index c5d8dcdc62f0a..f7b1dd06a8923 100644 --- a/tests/ui/cfg/diagnostics-cross-crate.rs +++ b/tests/ui/cfg/diagnostics-cross-crate.rs @@ -12,10 +12,14 @@ fn main() { //~^ NOTE found an item that was configured out //~| NOTE not found in `cfged_out::inner` - // The module isn't found - we would like to get a diagnostic, but currently don't due to - // the awkward way the resolver diagnostics are currently implemented. - cfged_out::inner::doesnt_exist::hello(); //~ ERROR cannot find - //~^ NOTE could not find `doesnt_exist` in `inner` + // The module isn't found - we mention that `cfgd_out` is `cfg`d out + cfged_out::inner::cfgd_out::hello(); //~ ERROR cannot find + //~^ NOTE could not find `cfgd_out` in `inner` + //~| NOTE found an item that was configured out + + // The module isn't found - we mention that `selected_out` is `cfg_select`d out + cfged_out::inner::selected_out::hello(); //~ ERROR cannot find + //~^ NOTE could not find `selected_out` in `inner` //~| NOTE found an item that was configured out // It should find the one in the right module, not the wrong one. diff --git a/tests/ui/cfg/diagnostics-cross-crate.stderr b/tests/ui/cfg/diagnostics-cross-crate.stderr index 15e60cc43a3c9..5d70e54df3de4 100644 --- a/tests/ui/cfg/diagnostics-cross-crate.stderr +++ b/tests/ui/cfg/diagnostics-cross-crate.stderr @@ -1,16 +1,30 @@ -error[E0433]: cannot find `doesnt_exist` in `inner` - --> $DIR/diagnostics-cross-crate.rs:17:23 +error[E0433]: cannot find `cfgd_out` in `inner` + --> $DIR/diagnostics-cross-crate.rs:16:23 | -LL | cfged_out::inner::doesnt_exist::hello(); - | ^^^^^^^^^^^^ could not find `doesnt_exist` in `inner` +LL | cfged_out::inner::cfgd_out::hello(); + | ^^^^^^^^ could not find `cfgd_out` in `inner` | note: found an item that was configured out --> $DIR/auxiliary/cfged_out.rs:6:13 | LL | #[cfg(false)] | ----- the item is gated here -LL | pub mod doesnt_exist { - | ^^^^^^^^^^^^ +LL | pub mod cfgd_out { + | ^^^^^^^^ + +error[E0433]: cannot find `selected_out` in `inner` + --> $DIR/diagnostics-cross-crate.rs:21:23 + | +LL | cfged_out::inner::selected_out::hello(); + | ^^^^^^^^^^^^ could not find `selected_out` in `inner` + | +note: found an item that was configured out + --> $DIR/auxiliary/cfged_out.rs:12:21 + | +LL | false => { + | ----- the item is gated here +LL | pub mod selected_out { + | ^^^^^^^^^^^^ error[E0425]: cannot find function `uwu` in crate `cfged_out` --> $DIR/diagnostics-cross-crate.rs:7:16 @@ -33,13 +47,13 @@ LL | pub fn uwu() {} | ^^^ error[E0425]: cannot find function `meow` in module `cfged_out::inner::right` - --> $DIR/diagnostics-cross-crate.rs:22:30 + --> $DIR/diagnostics-cross-crate.rs:26:30 | LL | cfged_out::inner::right::meow(); | ^^^^ not found in `cfged_out::inner::right` | note: found an item that was configured out - --> $DIR/auxiliary/cfged_out.rs:17:16 + --> $DIR/auxiliary/cfged_out.rs:26:16 | LL | #[cfg(feature = "what-a-cool-feature")] | ------------------------------- the item is gated behind the `what-a-cool-feature` feature @@ -47,20 +61,20 @@ LL | pub fn meow() {} | ^^^^ error[E0425]: cannot find function `vanished` in crate `cfged_out` - --> $DIR/diagnostics-cross-crate.rs:27:16 + --> $DIR/diagnostics-cross-crate.rs:31:16 | LL | cfged_out::vanished(); | ^^^^^^^^ not found in `cfged_out` | note: found an item that was configured out - --> $DIR/auxiliary/cfged_out.rs:22:8 + --> $DIR/auxiliary/cfged_out.rs:31:8 | LL | #[cfg(i_dont_exist_and_you_can_do_nothing_about_it)] | -------------------------------------------- the item is gated here LL | pub fn vanished() {} | ^^^^^^^^ -error: aborting due to 5 previous errors +error: aborting due to 6 previous errors Some errors have detailed explanations: E0425, E0433. For more information about an error, try `rustc --explain E0425`. diff --git a/tests/ui/cfg/diagnostics-same-crate.rs b/tests/ui/cfg/diagnostics-same-crate.rs index 40babaa3d4c9a..00369eb9c8507 100644 --- a/tests/ui/cfg/diagnostics-same-crate.rs +++ b/tests/ui/cfg/diagnostics-same-crate.rs @@ -16,6 +16,21 @@ pub mod inner { pub mod hi {} } + cfg_select! { + false => { //~ NOTE the item is gated here + //~^ NOTE the item is gated here + //~| NOTE the item is gated here + pub mod selected_out { + //~^ NOTE found an item that was configured out + //~| NOTE found an item that was configured out + //~| NOTE found an item that was configured out + pub fn hello() {} + pub mod hi {} + } + } + _ => {} + } + pub mod wrong { #[cfg(feature = "suggesting me fails the test!!")] pub fn meow() {} @@ -35,6 +50,12 @@ mod placeholder { use super::inner::doesnt_exist::hi; //~^ ERROR unresolved import `super::inner::doesnt_exist` //~| NOTE could not find `doesnt_exist` in `inner` + use super::inner::selected_out; + //~^ ERROR unresolved import `super::inner::selected_out` + //~| NOTE no `selected_out` in `inner` + use super::inner::selected_out::hi; + //~^ ERROR unresolved import `super::inner::selected_out` + //~| NOTE could not find `selected_out` in `inner` } #[cfg(i_dont_exist_and_you_can_do_nothing_about_it)] //~ NOTE the item is gated here @@ -53,6 +74,10 @@ fn main() { inner::doesnt_exist::hello(); //~ ERROR cannot find //~| NOTE could not find `doesnt_exist` in `inner` + // The module isn't found - we get a diagnostic. + inner::selected_out::hello(); //~ ERROR cannot find + //~| NOTE could not find `selected_out` in `inner` + // It should find the one in the right module, not the wrong one. inner::right::meow(); //~ ERROR cannot find function //~| NOTE not found in `inner::right diff --git a/tests/ui/cfg/diagnostics-same-crate.stderr b/tests/ui/cfg/diagnostics-same-crate.stderr index 0f5ad1d12adc5..3399acf5dde91 100644 --- a/tests/ui/cfg/diagnostics-same-crate.stderr +++ b/tests/ui/cfg/diagnostics-same-crate.stderr @@ -1,5 +1,5 @@ error[E0432]: unresolved import `super::inner::doesnt_exist` - --> $DIR/diagnostics-same-crate.rs:32:9 + --> $DIR/diagnostics-same-crate.rs:47:9 | LL | use super::inner::doesnt_exist; | ^^^^^^^^^^^^^^------------ @@ -16,7 +16,7 @@ LL | pub mod doesnt_exist { | ^^^^^^^^^^^^ error[E0432]: unresolved import `super::inner::doesnt_exist` - --> $DIR/diagnostics-same-crate.rs:35:23 + --> $DIR/diagnostics-same-crate.rs:50:23 | LL | use super::inner::doesnt_exist::hi; | ^^^^^^^^^^^^ could not find `doesnt_exist` in `inner` @@ -30,8 +30,40 @@ LL | #[cfg(false)] LL | pub mod doesnt_exist { | ^^^^^^^^^^^^ +error[E0432]: unresolved import `super::inner::selected_out` + --> $DIR/diagnostics-same-crate.rs:53:9 + | +LL | use super::inner::selected_out; + | ^^^^^^^^^^^^^^------------ + | | + | no `selected_out` in `inner` + | +note: found an item that was configured out + --> $DIR/diagnostics-same-crate.rs:23:21 + | +LL | false => { + | ----- the item is gated here +... +LL | pub mod selected_out { + | ^^^^^^^^^^^^ + +error[E0432]: unresolved import `super::inner::selected_out` + --> $DIR/diagnostics-same-crate.rs:56:23 + | +LL | use super::inner::selected_out::hi; + | ^^^^^^^^^^^^ could not find `selected_out` in `inner` + | +note: found an item that was configured out + --> $DIR/diagnostics-same-crate.rs:23:21 + | +LL | false => { + | ----- the item is gated here +... +LL | pub mod selected_out { + | ^^^^^^^^^^^^ + error[E0433]: cannot find `doesnt_exist` in `inner` - --> $DIR/diagnostics-same-crate.rs:53:12 + --> $DIR/diagnostics-same-crate.rs:74:12 | LL | inner::doesnt_exist::hello(); | ^^^^^^^^^^^^ could not find `doesnt_exist` in `inner` @@ -45,8 +77,23 @@ LL | #[cfg(false)] LL | pub mod doesnt_exist { | ^^^^^^^^^^^^ +error[E0433]: cannot find `selected_out` in `inner` + --> $DIR/diagnostics-same-crate.rs:78:12 + | +LL | inner::selected_out::hello(); + | ^^^^^^^^^^^^ could not find `selected_out` in `inner` + | +note: found an item that was configured out + --> $DIR/diagnostics-same-crate.rs:23:21 + | +LL | false => { + | ----- the item is gated here +... +LL | pub mod selected_out { + | ^^^^^^^^^^^^ + error[E0425]: cannot find function `uwu` in module `inner` - --> $DIR/diagnostics-same-crate.rs:49:12 + --> $DIR/diagnostics-same-crate.rs:70:12 | LL | inner::uwu(); | ^^^ not found in `inner` @@ -60,13 +107,13 @@ LL | pub fn uwu() {} | ^^^ error[E0425]: cannot find function `meow` in module `inner::right` - --> $DIR/diagnostics-same-crate.rs:57:19 + --> $DIR/diagnostics-same-crate.rs:82:19 | LL | inner::right::meow(); | ^^^^ not found in `inner::right` | note: found an item that was configured out - --> $DIR/diagnostics-same-crate.rs:26:16 + --> $DIR/diagnostics-same-crate.rs:41:16 | LL | #[cfg(feature = "what-a-cool-feature")] | ------------------------------- the item is gated behind the `what-a-cool-feature` feature @@ -74,26 +121,26 @@ LL | pub fn meow() {} | ^^^^ error[E0425]: cannot find function `uwu` in this scope - --> $DIR/diagnostics-same-crate.rs:45:5 + --> $DIR/diagnostics-same-crate.rs:66:5 | LL | uwu(); | ^^^ not found in this scope error[E0425]: cannot find function `vanished` in this scope - --> $DIR/diagnostics-same-crate.rs:62:5 + --> $DIR/diagnostics-same-crate.rs:87:5 | LL | vanished(); | ^^^^^^^^ not found in this scope | note: found an item that was configured out - --> $DIR/diagnostics-same-crate.rs:41:8 + --> $DIR/diagnostics-same-crate.rs:62:8 | LL | #[cfg(i_dont_exist_and_you_can_do_nothing_about_it)] | -------------------------------------------- the item is gated here LL | pub fn vanished() {} | ^^^^^^^^ -error: aborting due to 7 previous errors +error: aborting due to 10 previous errors Some errors have detailed explanations: E0425, E0432, E0433. For more information about an error, try `rustc --explain E0425`.