From a647ba250a65b44574830cb71aab5c0403adf31b Mon Sep 17 00:00:00 2001 From: Nilstrieb <48135649+Nilstrieb@users.noreply.github.com> Date: Fri, 10 Mar 2023 22:39:14 +0100 Subject: [PATCH] Remember names of `cfg`-ed out items to mention them in diagnostics `#[cfg]`s are frequently used to gate crate content behind cargo features. This can lead to very confusing errors when features are missing. For example, `serde` doesn't have the `derive` feature by default. Therefore, `serde::Serialize` fails to resolve with a generic error, even though the macro is present in the docs. This commit adds a list of all stripped item names to metadata. This is filled during macro expansion and then, through a fed query, persisted in metadata. The downstream resolver can then access the metadata to look at possible candidates for mentioning in the errors. This slightly increases metadata (800k->809k for the feature-heavy windows crate), but not enough to really matter. --- compiler/rustc_ast/src/expand/mod.rs | 17 ++++ compiler/rustc_expand/src/base.rs | 4 +- compiler/rustc_expand/src/config.rs | 20 ++-- compiler/rustc_expand/src/expand.rs | 49 +++++++++- compiler/rustc_metadata/src/rmeta/decoder.rs | 9 ++ .../src/rmeta/decoder/cstore_impl.rs | 1 + compiler/rustc_metadata/src/rmeta/encoder.rs | 13 +++ compiler/rustc_metadata/src/rmeta/mod.rs | 2 + compiler/rustc_middle/src/arena.rs | 1 + compiler/rustc_middle/src/query/mod.rs | 11 ++- compiler/rustc_middle/src/ty/parameterized.rs | 1 + compiler/rustc_resolve/src/diagnostics.rs | 69 ++++++++++++-- compiler/rustc_resolve/src/errors.rs | 4 + compiler/rustc_resolve/src/ident.rs | 92 ++++++++++--------- compiler/rustc_resolve/src/imports.rs | 26 +++++- compiler/rustc_resolve/src/late.rs | 24 ++++- .../rustc_resolve/src/late/diagnostics.rs | 37 ++++++-- compiler/rustc_resolve/src/lib.rs | 27 +++++- compiler/rustc_resolve/src/macros.rs | 12 ++- tests/ui/cfg/auxiliary/cfged_out.rs | 22 +++++ tests/ui/cfg/diagnostics-cross-crate.rs | 31 +++++++ tests/ui/cfg/diagnostics-cross-crate.stderr | 53 +++++++++++ tests/ui/cfg/diagnostics-not-a-def.rs | 12 +++ tests/ui/cfg/diagnostics-not-a-def.stderr | 9 ++ tests/ui/cfg/diagnostics-reexport.rs | 16 ++++ tests/ui/cfg/diagnostics-reexport.stderr | 15 +++ tests/ui/cfg/diagnostics-same-crate.rs | 51 ++++++++++ tests/ui/cfg/diagnostics-same-crate.stderr | 47 ++++++++++ tests/ui/macros/builtin-std-paths-fail.stderr | 3 + tests/ui/macros/macro-outer-attributes.stderr | 5 + 30 files changed, 599 insertions(+), 84 deletions(-) create mode 100644 tests/ui/cfg/auxiliary/cfged_out.rs create mode 100644 tests/ui/cfg/diagnostics-cross-crate.rs create mode 100644 tests/ui/cfg/diagnostics-cross-crate.stderr create mode 100644 tests/ui/cfg/diagnostics-not-a-def.rs create mode 100644 tests/ui/cfg/diagnostics-not-a-def.stderr create mode 100644 tests/ui/cfg/diagnostics-reexport.rs create mode 100644 tests/ui/cfg/diagnostics-reexport.stderr create mode 100644 tests/ui/cfg/diagnostics-same-crate.rs create mode 100644 tests/ui/cfg/diagnostics-same-crate.stderr diff --git a/compiler/rustc_ast/src/expand/mod.rs b/compiler/rustc_ast/src/expand/mod.rs index 2ee1bfe0ae71b..942347383ce31 100644 --- a/compiler/rustc_ast/src/expand/mod.rs +++ b/compiler/rustc_ast/src/expand/mod.rs @@ -1,3 +1,20 @@ //! Definitions shared by macros / syntax extensions and e.g. `rustc_middle`. +use rustc_span::{def_id::DefId, symbol::Ident}; + +use crate::MetaItem; + pub mod allocator; + +#[derive(Debug, Clone, Encodable, Decodable, HashStable_Generic)] +pub struct StrippedCfgItem { + pub parent_module: ModId, + pub name: Ident, + pub cfg: MetaItem, +} + +impl StrippedCfgItem { + pub fn map_mod_id(self, f: impl FnOnce(ModId) -> New) -> StrippedCfgItem { + StrippedCfgItem { parent_module: f(self.parent_module), name: self.name, cfg: self.cfg } + } +} diff --git a/compiler/rustc_expand/src/base.rs b/compiler/rustc_expand/src/base.rs index 0d43b30474b06..b7c30841983d7 100644 --- a/compiler/rustc_expand/src/base.rs +++ b/compiler/rustc_expand/src/base.rs @@ -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, diff --git a/compiler/rustc_expand/src/config.rs b/compiler/rustc_expand/src/config.rs index 4ff8e409d88e3..690f80f6876e4 100644 --- a/compiler/rustc_expand/src/config.rs +++ b/compiler/rustc_expand/src/config.rs @@ -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) { 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` diff --git a/compiler/rustc_expand/src/expand.rs b/compiler/rustc_expand/src/expand.rs index ce0093c7d4c0e..dd8863df1953c 100644 --- a/compiler/rustc_expand/src/expand.rs +++ b/compiler/rustc_expand/src/expand.rs @@ -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 { + vec![] + } } impl InvocationCollectorNode for P { @@ -1148,6 +1154,27 @@ impl InvocationCollectorNode for P { collector.cx.current_expansion.module = orig_module; res } + fn declared_names(&self) -> Vec { + if let ItemKind::Use(ut) = &self.kind { + fn collect_use_tree_leaves(ut: &ast::UseTree, idents: &mut Vec) { + 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) { + 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; } diff --git a/compiler/rustc_metadata/src/rmeta/decoder.rs b/compiler/rustc_metadata/src/rmeta/decoder.rs index 8f883bdcf12b8..21cbab542933c 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder.rs @@ -995,6 +995,15 @@ impl<'a, 'tcx> CrateMetadataRef<'a> { ) } + fn get_stripped_cfg_items(self, cnum: CrateNum, tcx: TyCtxt<'tcx>) -> &'tcx [StrippedCfgItem] { + let item_names = self + .root + .stripped_cfg_items + .decode((self, tcx)) + .map(|item| item.map_mod_id(|index| DefId { krate: cnum, index })); + tcx.arena.alloc_from_iter(item_names) + } + /// Iterates over the diagnostic items in the given crate. fn get_diagnostic_items(self) -> DiagnosticItems { let mut id_to_name = FxHashMap::default(); diff --git a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs index a15307e4345c1..2a609303197d6 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs @@ -345,6 +345,7 @@ provide! { tcx, def_id, other, cdata, stability_implications => { cdata.get_stability_implications(tcx).iter().copied().collect() } + stripped_cfg_items => { cdata.get_stripped_cfg_items(cdata.cnum, tcx) } is_intrinsic => { cdata.get_is_intrinsic(def_id.index) } defined_lang_items => { cdata.get_lang_items(tcx) } diagnostic_items => { cdata.get_diagnostic_items() } diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs index 6ceb61e793e53..8f63a0c9df629 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder.rs @@ -3,6 +3,7 @@ use crate::rmeta::def_path_hash_map::DefPathHashMapRef; use crate::rmeta::table::TableBuilder; use crate::rmeta::*; +use rustc_ast::expand::StrippedCfgItem; use rustc_ast::Attribute; use rustc_data_structures::fingerprint::Fingerprint; use rustc_data_structures::fx::{FxHashMap, FxIndexSet}; @@ -584,6 +585,8 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { (self.encode_lang_items(), self.encode_lang_items_missing()) }); + let stripped_cfg_items = stat!("stripped-cfg-items", || self.encode_stripped_cfg_items()); + let diagnostic_items = stat!("diagnostic-items", || self.encode_diagnostic_items()); let native_libraries = stat!("native-libs", || self.encode_native_libraries()); @@ -694,6 +697,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { lang_items, diagnostic_items, lang_items_missing, + stripped_cfg_items, native_libraries, foreign_modules, source_map, @@ -1940,6 +1944,15 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { self.lazy_array(&tcx.lang_items().missing) } + fn encode_stripped_cfg_items(&mut self) -> LazyArray> { + self.lazy_array( + self.tcx + .stripped_cfg_items(LOCAL_CRATE) + .into_iter() + .map(|item| item.clone().map_mod_id(|def_id| def_id.index)), + ) + } + fn encode_traits(&mut self) -> LazyArray { empty_proc_macro!(self); self.lazy_array(self.tcx.traits(LOCAL_CRATE).iter().map(|def_id| def_id.index)) diff --git a/compiler/rustc_metadata/src/rmeta/mod.rs b/compiler/rustc_metadata/src/rmeta/mod.rs index 2da888f4468c0..f6bbc4bc60b66 100644 --- a/compiler/rustc_metadata/src/rmeta/mod.rs +++ b/compiler/rustc_metadata/src/rmeta/mod.rs @@ -6,6 +6,7 @@ use rustc_middle::middle::debugger_visualizer::DebuggerVisualizerFile; use table::TableBuilder; use rustc_ast as ast; +use rustc_ast::expand::StrippedCfgItem; use rustc_attr as attr; use rustc_data_structures::svh::Svh; use rustc_hir as hir; @@ -256,6 +257,7 @@ pub(crate) struct CrateRoot { stability_implications: LazyArray<(Symbol, Symbol)>, lang_items: LazyArray<(DefIndex, LangItem)>, lang_items_missing: LazyArray, + stripped_cfg_items: LazyArray>, diagnostic_items: LazyArray<(Symbol, DefIndex)>, native_libraries: LazyArray, foreign_modules: LazyArray, diff --git a/compiler/rustc_middle/src/arena.rs b/compiler/rustc_middle/src/arena.rs index a149a61ec136e..6c404fbb7c684 100644 --- a/compiler/rustc_middle/src/arena.rs +++ b/compiler/rustc_middle/src/arena.rs @@ -124,6 +124,7 @@ macro_rules! arena_types { [] predefined_opaques_in_body: rustc_middle::traits::solve::PredefinedOpaquesData<'tcx>, [decode] doc_link_resolutions: rustc_hir::def::DocLinkResMap, [] closure_kind_origin: (rustc_span::Span, rustc_middle::hir::place::Place<'tcx>), + [] stripped_cfg_items: rustc_ast::expand::StrippedCfgItem, [] mod_child: rustc_middle::metadata::ModChild, ]); ) diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index 0b31c9bbf8149..bcffbed8d94cd 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -54,7 +54,7 @@ use crate::ty::{ }; use rustc_arena::TypedArena; use rustc_ast as ast; -use rustc_ast::expand::allocator::AllocatorKind; +use rustc_ast::expand::{allocator::AllocatorKind, StrippedCfgItem}; use rustc_attr as attr; use rustc_data_structures::fingerprint::Fingerprint; use rustc_data_structures::fx::{FxHashMap, FxIndexMap, FxIndexSet}; @@ -2173,6 +2173,15 @@ rustc_queries! { query check_tys_might_be_eq(arg: Canonical<'tcx, (ty::ParamEnv<'tcx>, Ty<'tcx>, Ty<'tcx>)>) -> Result<(), NoSolution> { desc { "check whether two const param are definitely not equal to eachother"} } + + /// Get all item paths that were stripped by a `#[cfg]` in a particular crate. + /// Should not be called for the local crate before the resolver outputs are created, as it + /// is only fed there. + query stripped_cfg_items(cnum: CrateNum) -> &'tcx [StrippedCfgItem] { + feedable + desc { "getting cfg-ed out item names" } + separate_provide_extern + } } rustc_query_append! { define_callbacks! } diff --git a/compiler/rustc_middle/src/ty/parameterized.rs b/compiler/rustc_middle/src/ty/parameterized.rs index a2e77d9cdfe38..13be15269f4c2 100644 --- a/compiler/rustc_middle/src/ty/parameterized.rs +++ b/compiler/rustc_middle/src/ty/parameterized.rs @@ -73,6 +73,7 @@ trivially_parameterized_over_tcx! { ty::fast_reject::SimplifiedType, rustc_ast::Attribute, rustc_ast::DelimArgs, + rustc_ast::expand::StrippedCfgItem, rustc_attr::ConstStability, rustc_attr::DefaultBodyStability, rustc_attr::Deprecation, diff --git a/compiler/rustc_resolve/src/diagnostics.rs b/compiler/rustc_resolve/src/diagnostics.rs index 377652ce71bb2..15c8a690530e7 100644 --- a/compiler/rustc_resolve/src/diagnostics.rs +++ b/compiler/rustc_resolve/src/diagnostics.rs @@ -1,8 +1,10 @@ use std::ptr; +use rustc_ast::expand::StrippedCfgItem; use rustc_ast::ptr::P; use rustc_ast::visit::{self, Visitor}; use rustc_ast::{self as ast, Crate, ItemKind, ModKind, NodeId, Path, CRATE_NODE_ID}; +use rustc_ast::{MetaItemKind, NestedMetaItem}; use rustc_ast_pretty::pprust; use rustc_data_structures::fx::FxHashSet; use rustc_errors::{ @@ -776,7 +778,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { .tcx .sess .create_err(errs::SelfImportOnlyInImportListWithNonEmptyPrefix { span }), - ResolutionError::FailedToResolve { label, suggestion } => { + ResolutionError::FailedToResolve { last_segment, label, suggestion, module } => { let mut err = struct_span_err!(self.tcx.sess, span, E0433, "failed to resolve: {}", &label); err.span_label(span, label); @@ -789,6 +791,13 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { err.multipart_suggestion(msg, suggestions, applicability); } + if let Some(ModuleOrUniformRoot::Module(module)) = module + && let Some(module) = module.opt_def_id() + && let Some(last_segment) = last_segment + { + self.find_cfg_stripped(&mut err, &last_segment, module); + } + err } ResolutionError::CannotCaptureDynamicEnvironmentInFnItem => { @@ -971,9 +980,15 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { VisResolutionError::AncestorOnly(span) => { self.tcx.sess.create_err(errs::AncestorOnly(span)) } - VisResolutionError::FailedToResolve(span, label, suggestion) => { - self.into_struct_error(span, ResolutionError::FailedToResolve { label, suggestion }) - } + VisResolutionError::FailedToResolve(span, label, suggestion) => self.into_struct_error( + span, + ResolutionError::FailedToResolve { + last_segment: None, + label, + suggestion, + module: None, + }, + ), VisResolutionError::ExpectedFound(span, path_str, res) => { self.tcx.sess.create_err(errs::ExpectedFound { span, res, path_str }) } @@ -1721,10 +1736,10 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { ribs: Option<&PerNS>>>, ignore_binding: Option<&'a NameBinding<'a>>, module: Option>, - i: usize, + failed_segment_idx: usize, ident: Ident, ) -> (String, Option) { - let is_last = i == path.len() - 1; + let is_last = failed_segment_idx == path.len() - 1; let ns = if is_last { opt_ns.unwrap_or(TypeNS) } else { TypeNS }; let module_res = match module { Some(ModuleOrUniformRoot::Module(module)) => module.res(), @@ -1758,8 +1773,8 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { } else { (format!("could not find `{ident}` in the crate root"), None) } - } else if i > 0 { - let parent = path[i - 1].ident.name; + } else if failed_segment_idx > 0 { + let parent = path[failed_segment_idx - 1].ident.name; let parent = match parent { // ::foo is mounted at the crate root for 2015, and is the extern // prelude for 2018+ @@ -2207,6 +2222,44 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { None } } + + /// Finds a cfg-ed out item inside `module` with the matching name. + pub(crate) fn find_cfg_stripped( + &mut self, + err: &mut Diagnostic, + last_segment: &Symbol, + module: DefId, + ) { + let local_items; + let symbols = if module.is_local() { + local_items = self + .stripped_cfg_items + .iter() + .filter_map(|item| { + let parent_module = self.opt_local_def_id(item.parent_module)?.to_def_id(); + Some(StrippedCfgItem { parent_module, name: item.name, cfg: item.cfg.clone() }) + }) + .collect::>(); + local_items.as_slice() + } else { + self.tcx.stripped_cfg_items(module.krate) + }; + + for &StrippedCfgItem { parent_module, name, ref cfg } in symbols { + if parent_module != module || name.name != *last_segment { + continue; + } + + err.span_note(name.span, "found an item that was configured out"); + + if let MetaItemKind::List(nested) = &cfg.kind + && let NestedMetaItem::MetaItem(meta_item) = &nested[0] + && let MetaItemKind::NameValue(feature_name) = &meta_item.kind + { + err.note(format!("the item is gated behind the `{}` feature", feature_name.symbol)); + } + } + } } /// Given a `binding_span` of a binding within a use statement: diff --git a/compiler/rustc_resolve/src/errors.rs b/compiler/rustc_resolve/src/errors.rs index 2ab55f12637c8..e88cbb955b556 100644 --- a/compiler/rustc_resolve/src/errors.rs +++ b/compiler/rustc_resolve/src/errors.rs @@ -330,6 +330,7 @@ pub(crate) struct ParamInTyOfConstParam { pub(crate) param_kind: Option, } +#[derive(Debug)] #[derive(Subdiagnostic)] pub(crate) enum ParamKindInTyOfConstParam { #[note(resolve_type_param_in_ty_of_const_param)] @@ -365,6 +366,7 @@ pub(crate) struct ParamInNonTrivialAnonConst { #[help(resolve_param_in_non_trivial_anon_const_help)] pub(crate) struct ParamInNonTrivialAnonConstHelp; +#[derive(Debug)] #[derive(Subdiagnostic)] pub(crate) enum ParamKindInNonTrivialAnonConst { #[note(resolve_type_param_in_non_trivial_anon_const)] @@ -562,6 +564,7 @@ pub(crate) struct CfgAccessibleUnsure { pub(crate) span: Span, } +#[derive(Debug)] #[derive(Diagnostic)] #[diag(resolve_param_in_enum_discriminant)] pub(crate) struct ParamInEnumDiscriminant { @@ -573,6 +576,7 @@ pub(crate) struct ParamInEnumDiscriminant { pub(crate) param_kind: ParamKindInEnumDiscriminant, } +#[derive(Debug)] #[derive(Subdiagnostic)] pub(crate) enum ParamKindInEnumDiscriminant { #[note(resolve_type_param_in_enum_discriminant)] diff --git a/compiler/rustc_resolve/src/ident.rs b/compiler/rustc_resolve/src/ident.rs index 945c7ce3a9b32..ec0a8535e7180 100644 --- a/compiler/rustc_resolve/src/ident.rs +++ b/compiler/rustc_resolve/src/ident.rs @@ -1365,20 +1365,12 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { ribs: Option<&PerNS>>>, ignore_binding: Option<&'a NameBinding<'a>>, ) -> PathResult<'a> { - debug!( - "resolve_path(path={:?}, opt_ns={:?}, finalize={:?}) path_len: {}", - path, - opt_ns, - finalize, - path.len() - ); - let mut module = None; let mut allow_super = true; let mut second_binding = None; - for (i, &Segment { ident, id, .. }) in path.iter().enumerate() { - debug!("resolve_path ident {} {:?} {:?}", i, ident, id); + for (segment_idx, &Segment { ident, id, .. }) in path.iter().enumerate() { + debug!("resolve_path ident {} {:?} {:?}", segment_idx, ident, id); let record_segment_res = |this: &mut Self, res| { if finalize.is_some() { if let Some(id) = id { @@ -1390,7 +1382,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { } }; - let is_last = i + 1 == path.len(); + let is_last = segment_idx + 1 == path.len(); let ns = if is_last { opt_ns.unwrap_or(TypeNS) } else { TypeNS }; let name = ident.name; @@ -1399,7 +1391,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { if ns == TypeNS { if allow_super && name == kw::Super { let mut ctxt = ident.span.ctxt().normalize_to_macros_2_0(); - let self_module = match i { + let self_module = match segment_idx { 0 => Some(self.resolve_self(&mut ctxt, parent_scope.module)), _ => match module { Some(ModuleOrUniformRoot::Module(module)) => Some(module), @@ -1414,11 +1406,15 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { continue; } } - return PathResult::failed(ident.span, false, finalize.is_some(), || { - ("there are too many leading `super` keywords".to_string(), None) - }); + return PathResult::failed( + ident.span, + false, + finalize.is_some(), + module, + || ("there are too many leading `super` keywords".to_string(), None), + ); } - if i == 0 { + if segment_idx == 0 { if name == kw::SelfLower { let mut ctxt = ident.span.ctxt().normalize_to_macros_2_0(); module = Some(ModuleOrUniformRoot::Module( @@ -1447,14 +1443,14 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { } // Report special messages for path segment keywords in wrong positions. - if ident.is_path_segment_keyword() && i != 0 { - return PathResult::failed(ident.span, false, finalize.is_some(), || { + if ident.is_path_segment_keyword() && segment_idx != 0 { + return PathResult::failed(ident.span, false, finalize.is_some(), module, || { let name_str = if name == kw::PathRoot { "crate root".to_string() } else { format!("`{}`", name) }; - let label = if i == 1 && path[0].ident.name == kw::PathRoot { + let label = if segment_idx == 1 && path[0].ident.name == kw::PathRoot { format!("global paths cannot start with {}", name_str) } else { format!("{} in paths can only be used in start position", name_str) @@ -1519,7 +1515,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { }; match binding { Ok(binding) => { - if i == 1 { + if segment_idx == 1 { second_binding = Some(binding); } let res = binding.res(); @@ -1543,17 +1539,23 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { record_segment_res(self, res); return PathResult::NonModule(PartialRes::with_unresolved_segments( res, - path.len() - i - 1, + path.len() - segment_idx - 1, )); } else { - return PathResult::failed(ident.span, is_last, finalize.is_some(), || { - let label = format!( - "`{ident}` is {} {}, not a module", - res.article(), - res.descr() - ); - (label, None) - }); + return PathResult::failed( + ident.span, + is_last, + finalize.is_some(), + module, + || { + let label = format!( + "`{ident}` is {} {}, not a module", + res.article(), + res.descr() + ); + (label, None) + }, + ); } } Err(Undetermined) => return PathResult::Indeterminate, @@ -1562,23 +1564,29 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { if opt_ns.is_some() && !module.is_normal() { return PathResult::NonModule(PartialRes::with_unresolved_segments( module.res().unwrap(), - path.len() - i, + path.len() - segment_idx, )); } } - return PathResult::failed(ident.span, is_last, finalize.is_some(), || { - self.report_path_resolution_error( - path, - opt_ns, - parent_scope, - ribs, - ignore_binding, - module, - i, - ident, - ) - }); + return PathResult::failed( + ident.span, + is_last, + finalize.is_some(), + module, + || { + self.report_path_resolution_error( + path, + opt_ns, + parent_scope, + ribs, + ignore_binding, + module, + segment_idx, + ident, + ) + }, + ); } } } diff --git a/compiler/rustc_resolve/src/imports.rs b/compiler/rustc_resolve/src/imports.rs index c1bb262c0d407..7f944fb574596 100644 --- a/compiler/rustc_resolve/src/imports.rs +++ b/compiler/rustc_resolve/src/imports.rs @@ -803,14 +803,34 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { module } - PathResult::Failed { is_error_from_last_segment: false, span, label, suggestion } => { + PathResult::Failed { + is_error_from_last_segment: false, + span, + label, + suggestion, + module, + } => { if no_ambiguity { assert!(import.imported_module.get().is_none()); - self.report_error(span, ResolutionError::FailedToResolve { label, suggestion }); + self.report_error( + span, + ResolutionError::FailedToResolve { + last_segment: None, + label, + suggestion, + module, + }, + ); } return None; } - PathResult::Failed { is_error_from_last_segment: true, span, label, suggestion } => { + PathResult::Failed { + is_error_from_last_segment: true, + span, + label, + suggestion, + .. + } => { if no_ambiguity { assert!(import.imported_module.get().is_none()); let err = match self.make_path_suggestion( diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index e0611907613c4..ddd75ea3b33e0 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -3524,7 +3524,7 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> { None }; - this.r.use_injections.push(UseError { + let ue = UseError { err, candidates, def_id, @@ -3532,7 +3532,9 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> { suggestion, path: path.into(), is_call: source.is_call(), - }); + }; + + this.r.use_injections.push(ue); } PartialRes::new(Res::Err) @@ -3866,8 +3868,22 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> { PathResult::Module(ModuleOrUniformRoot::Module(module)) => { PartialRes::new(module.res().unwrap()) } - PathResult::Failed { is_error_from_last_segment: false, span, label, suggestion } => { - return Err(respan(span, ResolutionError::FailedToResolve { label, suggestion })); + PathResult::Failed { + is_error_from_last_segment: false, + span, + label, + suggestion, + module, + } => { + return Err(respan( + span, + ResolutionError::FailedToResolve { + last_segment: None, + label, + suggestion, + module, + }, + )); } PathResult::Module(..) | PathResult::Failed { .. } => return Ok(None), PathResult::Indeterminate => bug!("indeterminate path result in resolve_qpath"), diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs index f79f8d0c6ca4e..2f9759a668bbe 100644 --- a/compiler/rustc_resolve/src/late/diagnostics.rs +++ b/compiler/rustc_resolve/src/late/diagnostics.rs @@ -149,6 +149,7 @@ struct BaseError { span_label: Option<(Span, &'static str)>, could_be_expr: bool, suggestion: Option<(Span, &'static str, String)>, + module: Option, } #[derive(Debug)] @@ -210,10 +211,11 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> { _ => false, }, suggestion: None, + module: None, } } else { let item_span = path.last().unwrap().ident.span; - let (mod_prefix, mod_str, suggestion) = if path.len() == 1 { + let (mod_prefix, mod_str, module, suggestion) = if path.len() == 1 { debug!(?self.diagnostic_metadata.current_impl_items); debug!(?self.diagnostic_metadata.current_function); let suggestion = if self.current_trait_ref.is_none() @@ -247,26 +249,37 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> { } else { None }; - (String::new(), "this scope".to_string(), suggestion) + (String::new(), "this scope".to_string(), None, suggestion) } else if path.len() == 2 && path[0].ident.name == kw::PathRoot { if self.r.tcx.sess.edition() > Edition::Edition2015 { // In edition 2018 onwards, the `::foo` syntax may only pull from the extern prelude // which overrides all other expectations of item type expected = "crate"; - (String::new(), "the list of imported crates".to_string(), None) + (String::new(), "the list of imported crates".to_string(), None, None) } else { - (String::new(), "the crate root".to_string(), None) + ( + String::new(), + "the crate root".to_string(), + Some(CRATE_DEF_ID.to_def_id()), + None, + ) } } else if path.len() == 2 && path[0].ident.name == kw::Crate { - (String::new(), "the crate root".to_string(), None) + (String::new(), "the crate root".to_string(), Some(CRATE_DEF_ID.to_def_id()), None) } else { let mod_path = &path[..path.len() - 1]; - let mod_prefix = match self.resolve_path(mod_path, Some(TypeNS), None) { + let mod_res = self.resolve_path(mod_path, Some(TypeNS), None); + let mod_prefix = match mod_res { PathResult::Module(ModuleOrUniformRoot::Module(module)) => module.res(), _ => None, - } - .map_or_else(String::new, |res| format!("{} ", res.descr())); - (mod_prefix, format!("`{}`", Segment::names_to_string(mod_path)), None) + }; + + let module_did = mod_prefix.as_ref().and_then(Res::mod_def_id); + + let mod_prefix = + mod_prefix.map_or_else(String::new, |res| (format!("{} ", res.descr()))); + + (mod_prefix, format!("`{}`", Segment::names_to_string(mod_path)), module_did, None) }; let (fallback_label, suggestion) = if path_str == "async" @@ -300,6 +313,7 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> { span_label: None, could_be_expr: false, suggestion, + module, } } } @@ -315,6 +329,7 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> { ) -> (DiagnosticBuilder<'tcx, ErrorGuaranteed>, Vec) { debug!(?res, ?source); let base_error = self.make_base_error(path, span, source, res); + let code = source.error_code(res.is_some()); let mut err = self.r.tcx.sess.struct_span_err_with_code( base_error.span, @@ -366,6 +381,10 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> { } self.err_code_special_cases(&mut err, source, path, span); + if let Some(module) = base_error.module { + self.r.find_cfg_stripped(&mut err, &path.last().unwrap().ident.name, module); + } + (err, candidates) } diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs index fd977e8e254a2..dd8d01e35e5de 100644 --- a/compiler/rustc_resolve/src/lib.rs +++ b/compiler/rustc_resolve/src/lib.rs @@ -25,6 +25,7 @@ use errors::{ ParamKindInEnumDiscriminant, ParamKindInNonTrivialAnonConst, ParamKindInTyOfConstParam, }; use rustc_arena::{DroplessArena, TypedArena}; +use rustc_ast::expand::StrippedCfgItem; use rustc_ast::node_id::NodeMap; use rustc_ast::{self as ast, attr, NodeId, CRATE_NODE_ID}; use rustc_ast::{AngleBracketedArg, Crate, Expr, ExprKind, GenericArg, GenericArgs, LitKind, Path}; @@ -171,6 +172,7 @@ enum ImplTraitContext { Universal(LocalDefId), } +#[derive(Debug)] struct BindingError { name: Symbol, origin: BTreeSet, @@ -178,6 +180,7 @@ struct BindingError { could_be_path: bool, } +#[derive(Debug)] enum ResolutionError<'a> { /// Error E0401: can't use type or const parameters from outer function. GenericParamsFromOuterFunction(Res, HasGenericParams), @@ -207,7 +210,12 @@ enum ResolutionError<'a> { /// Error E0431: `self` import can only appear in an import list with a non-empty prefix. SelfImportOnlyInImportListWithNonEmptyPrefix, /// Error E0433: failed to resolve. - FailedToResolve { label: String, suggestion: Option }, + FailedToResolve { + last_segment: Option, + label: String, + suggestion: Option, + module: Option>, + }, /// Error E0434: can't capture dynamic environment in a fn item. CannotCaptureDynamicEnvironmentInFnItem, /// Error E0435: attempt to use a non-constant value in a constant. @@ -402,6 +410,7 @@ enum PathResult<'a> { label: String, suggestion: Option, is_error_from_last_segment: bool, + module: Option>, }, } @@ -410,11 +419,12 @@ impl<'a> PathResult<'a> { span: Span, is_error_from_last_segment: bool, finalize: bool, + module: Option>, label_and_suggestion: impl FnOnce() -> (String, Option), ) -> PathResult<'a> { let (label, suggestion) = if finalize { label_and_suggestion() } else { (String::new(), None) }; - PathResult::Failed { span, label, suggestion, is_error_from_last_segment } + PathResult::Failed { span, label, suggestion, is_error_from_last_segment, module } } } @@ -685,6 +695,7 @@ struct PrivacyError<'a> { dedup_span: Span, } +#[derive(Debug)] struct UseError<'a> { err: DiagnosticBuilder<'a, ErrorGuaranteed>, /// Candidates which user could `use` to access the missing type. @@ -1059,6 +1070,9 @@ pub struct Resolver<'a, 'tcx> { /// Whether lifetime elision was successful. lifetime_elision_allowed: FxHashSet, + /// Names of items that were stripped out via cfg with their corresponding cfg meta item. + stripped_cfg_items: Vec>, + effective_visibilities: EffectiveVisibilities, doc_link_resolutions: FxHashMap, doc_link_traits_in_scope: FxHashMap>, @@ -1353,6 +1367,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { proc_macros: Default::default(), confused_type_with_std_module: Default::default(), lifetime_elision_allowed: Default::default(), + stripped_cfg_items: Default::default(), effective_visibilities: Default::default(), doc_link_resolutions: Default::default(), doc_link_traits_in_scope: Default::default(), @@ -1410,6 +1425,14 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { let main_def = self.main_def; let confused_type_with_std_module = self.confused_type_with_std_module; let effective_visibilities = self.effective_visibilities; + + self.tcx.feed_local_crate().stripped_cfg_items(self.tcx.arena.alloc_from_iter( + self.stripped_cfg_items.into_iter().filter_map(|item| { + let parent_module = self.node_id_to_def_id.get(&item.parent_module)?.to_def_id(); + Some(StrippedCfgItem { parent_module, name: item.name, cfg: item.cfg }) + }), + )); + let global_ctxt = ResolverGlobalCtxt { expn_that_defined, visibilities, diff --git a/compiler/rustc_resolve/src/macros.rs b/compiler/rustc_resolve/src/macros.rs index d8a7bcbfff955..805c804e5759f 100644 --- a/compiler/rustc_resolve/src/macros.rs +++ b/compiler/rustc_resolve/src/macros.rs @@ -6,6 +6,7 @@ use crate::Namespace::*; use crate::{BuiltinMacroState, Determinacy}; use crate::{DeriveData, Finalize, ParentScope, ResolutionError, Resolver, ScopeSet}; use crate::{ModuleKind, ModuleOrUniformRoot, NameBinding, PathResult, Segment}; +use rustc_ast::expand::StrippedCfgItem; use rustc_ast::{self as ast, attr, Inline, ItemKind, ModKind, NodeId}; use rustc_ast_pretty::pprust; use rustc_attr::StabilityLevel; @@ -465,6 +466,10 @@ impl<'a, 'tcx> ResolverExpand for Resolver<'a, 'tcx> { self.proc_macros.push(id) } + fn append_stripped_cfg_item(&mut self, parent_node: NodeId, name: Ident, cfg: ast::MetaItem) { + self.stripped_cfg_items.push(StrippedCfgItem { parent_module: parent_node, name, cfg }); + } + fn registered_tools(&self) -> &RegisteredTools { &self.registered_tools } @@ -721,7 +726,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { } path_res @ (PathResult::NonModule(..) | PathResult::Failed { .. }) => { let mut suggestion = None; - let (span, label) = if let PathResult::Failed { span, label, .. } = path_res { + let (span, label, module) = if let PathResult::Failed { span, label, module, .. } = path_res { // try to suggest if it's not a macro, maybe a function if let PathResult::NonModule(partial_res) = self.maybe_resolve_path(&path, Some(ValueNS), &parent_scope) && partial_res.unresolved_segments() == 0 { @@ -733,7 +738,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { Applicability::MaybeIncorrect )); } - (span, label) + (span, label, module) } else { ( path_span, @@ -742,11 +747,12 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { kind.article(), kind.descr() ), + None, ) }; self.report_error( span, - ResolutionError::FailedToResolve { label, suggestion }, + ResolutionError::FailedToResolve { last_segment: path.last().map(|segment| segment.ident.name), label, suggestion, module }, ); } PathResult::Module(..) | PathResult::Indeterminate => unreachable!(), diff --git a/tests/ui/cfg/auxiliary/cfged_out.rs b/tests/ui/cfg/auxiliary/cfged_out.rs new file mode 100644 index 0000000000000..f6a9089cf29de --- /dev/null +++ b/tests/ui/cfg/auxiliary/cfged_out.rs @@ -0,0 +1,22 @@ +pub mod inner { + #[cfg(FALSE)] + pub fn uwu() {} + + #[cfg(FALSE)] + pub mod doesnt_exist { + pub fn hello() {} + } + + pub mod wrong { + #[cfg(feature = "suggesting me fails the test!!")] + pub fn meow() {} + } + + pub mod right { + #[cfg(feature = "what-a-cool-feature")] + pub fn meow() {} + } +} + +#[cfg(i_dont_exist_and_you_can_do_nothing_about_it)] +pub fn vanished() {} diff --git a/tests/ui/cfg/diagnostics-cross-crate.rs b/tests/ui/cfg/diagnostics-cross-crate.rs new file mode 100644 index 0000000000000..d2725c94b083b --- /dev/null +++ b/tests/ui/cfg/diagnostics-cross-crate.rs @@ -0,0 +1,31 @@ +// aux-build:cfged_out.rs + +extern crate cfged_out; + +fn main() { + // There is no uwu at this path - no diagnostic. + cfged_out::uwu(); //~ ERROR cannot find function + //~^ NOTE not found in `cfged_out` + + // It does exist here - diagnostic. + cfged_out::inner::uwu(); //~ ERROR cannot find function + //~^ 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. + // FIXME(Nilstrieb): Also add a note to the cfg diagnostic here + cfged_out::inner::doesnt_exist::hello(); //~ ERROR failed to resolve + //~^ NOTE could not find `doesnt_exist` in `inner` + + // It should find the one in the right module, not the wrong one. + cfged_out::inner::right::meow(); //~ ERROR cannot find function + //~^ NOTE found an item that was configured out + //~| NOTE not found in `cfged_out::inner::right + //~| NOTE the item is gated behind the `what-a-cool-feature` feature + + // Exists in the crate root - diagnostic. + cfged_out::vanished(); //~ ERROR cannot find function + //~^ NOTE found an item that was configured out + //~| NOTE not found in `cfged_out` +} diff --git a/tests/ui/cfg/diagnostics-cross-crate.stderr b/tests/ui/cfg/diagnostics-cross-crate.stderr new file mode 100644 index 0000000000000..046929bc26023 --- /dev/null +++ b/tests/ui/cfg/diagnostics-cross-crate.stderr @@ -0,0 +1,53 @@ +error[E0433]: failed to resolve: could not find `doesnt_exist` in `inner` + --> $DIR/diagnostics-cross-crate.rs:18:23 + | +LL | cfged_out::inner::doesnt_exist::hello(); + | ^^^^^^^^^^^^ could not find `doesnt_exist` in `inner` + +error[E0425]: cannot find function `uwu` in crate `cfged_out` + --> $DIR/diagnostics-cross-crate.rs:7:16 + | +LL | cfged_out::uwu(); + | ^^^ not found in `cfged_out` + +error[E0425]: cannot find function `uwu` in module `cfged_out::inner` + --> $DIR/diagnostics-cross-crate.rs:11:23 + | +LL | cfged_out::inner::uwu(); + | ^^^ not found in `cfged_out::inner` + | +note: found an item that was configured out + --> $DIR/auxiliary/cfged_out.rs:3:12 + | +LL | pub fn uwu() {} + | ^^^ + +error[E0425]: cannot find function `meow` in module `cfged_out::inner::right` + --> $DIR/diagnostics-cross-crate.rs:22: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 + | +LL | pub fn meow() {} + | ^^^^ + = note: the item is gated behind the `what-a-cool-feature` feature + +error[E0425]: cannot find function `vanished` in crate `cfged_out` + --> $DIR/diagnostics-cross-crate.rs:28: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 + | +LL | pub fn vanished() {} + | ^^^^^^^^ + +error: aborting due to 5 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-not-a-def.rs b/tests/ui/cfg/diagnostics-not-a-def.rs new file mode 100644 index 0000000000000..7293947122675 --- /dev/null +++ b/tests/ui/cfg/diagnostics-not-a-def.rs @@ -0,0 +1,12 @@ +pub mod inner { + pub fn i_am_here() { + #[cfg(feature = "another one that doesn't exist")] + loop {} + } +} + +fn main() { + inner::i_am_here(); + // ensure that nothing bad happens when we are checking for cfgs + inner::i_am_not(); //~ ERROR cannot find function +} diff --git a/tests/ui/cfg/diagnostics-not-a-def.stderr b/tests/ui/cfg/diagnostics-not-a-def.stderr new file mode 100644 index 0000000000000..af0e1a1727579 --- /dev/null +++ b/tests/ui/cfg/diagnostics-not-a-def.stderr @@ -0,0 +1,9 @@ +error[E0425]: cannot find function `i_am_not` in module `inner` + --> $DIR/diagnostics-not-a-def.rs:11:12 + | +LL | inner::i_am_not(); + | ^^^^^^^^ not found in `inner` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0425`. diff --git a/tests/ui/cfg/diagnostics-reexport.rs b/tests/ui/cfg/diagnostics-reexport.rs new file mode 100644 index 0000000000000..1d43d6ba02f56 --- /dev/null +++ b/tests/ui/cfg/diagnostics-reexport.rs @@ -0,0 +1,16 @@ +pub mod inner { + #[cfg(FALSE)] + mod gone { + pub fn uwu() {} + } + + #[cfg(FALSE)] + pub use super::uwu; + //~^ NOTE found an item that was configured out +} + +fn main() { + // There is no uwu at this path - no diagnostic. + inner::uwu(); //~ ERROR cannot find function + //~^ NOTE not found in `inner` +} diff --git a/tests/ui/cfg/diagnostics-reexport.stderr b/tests/ui/cfg/diagnostics-reexport.stderr new file mode 100644 index 0000000000000..6c977cbfa4172 --- /dev/null +++ b/tests/ui/cfg/diagnostics-reexport.stderr @@ -0,0 +1,15 @@ +error[E0425]: cannot find function `uwu` in module `inner` + --> $DIR/diagnostics-reexport.rs:14:12 + | +LL | inner::uwu(); + | ^^^ not found in `inner` + | +note: found an item that was configured out + --> $DIR/diagnostics-reexport.rs:8:20 + | +LL | pub use super::uwu; + | ^^^ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0425`. diff --git a/tests/ui/cfg/diagnostics-same-crate.rs b/tests/ui/cfg/diagnostics-same-crate.rs new file mode 100644 index 0000000000000..f76ace06a762d --- /dev/null +++ b/tests/ui/cfg/diagnostics-same-crate.rs @@ -0,0 +1,51 @@ +pub mod inner { + #[cfg(FALSE)] + pub fn uwu() {} + //~^ NOTE found an item that was configured out + + #[cfg(FALSE)] + pub mod doesnt_exist { + pub fn hello() {} + } + + pub mod wrong { + #[cfg(feature = "suggesting me fails the test!!")] + pub fn meow() {} + } + + pub mod right { + #[cfg(feature = "what-a-cool-feature")] + pub fn meow() {} + //~^ NOTE found an item that was configured out + } +} + +#[cfg(i_dont_exist_and_you_can_do_nothing_about_it)] +pub fn vanished() {} + +fn main() { + // There is no uwu at this path - no diagnostic. + uwu(); //~ ERROR cannot find function + //~^ NOTE not found in this scope + + // It does exist here - diagnostic. + inner::uwu(); //~ ERROR cannot find function + //~| NOTE not found in `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. + // FIXME(Nilstrieb): Also add a note to the cfg diagnostic here + inner::doesnt_exist::hello(); //~ ERROR failed to resolve + //~| NOTE could not find `doesnt_exist` 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 + //~| NOTE the item is gated behind the `what-a-cool-feature` feature + + // Exists in the crate root - we would generally want a diagnostic, + // but currently don't have one. + // Not that it matters much though, this is highly unlikely to confuse anyone. + vanished(); //~ ERROR cannot find function + //~^ NOTE not found in this scope +} diff --git a/tests/ui/cfg/diagnostics-same-crate.stderr b/tests/ui/cfg/diagnostics-same-crate.stderr new file mode 100644 index 0000000000000..30ee6479bd26c --- /dev/null +++ b/tests/ui/cfg/diagnostics-same-crate.stderr @@ -0,0 +1,47 @@ +error[E0433]: failed to resolve: could not find `doesnt_exist` in `inner` + --> $DIR/diagnostics-same-crate.rs:38:12 + | +LL | inner::doesnt_exist::hello(); + | ^^^^^^^^^^^^ could not find `doesnt_exist` in `inner` + +error[E0425]: cannot find function `uwu` in module `inner` + --> $DIR/diagnostics-same-crate.rs:32:12 + | +LL | inner::uwu(); + | ^^^ not found in `inner` + | +note: found an item that was configured out + --> $DIR/diagnostics-same-crate.rs:3:12 + | +LL | pub fn uwu() {} + | ^^^ + +error[E0425]: cannot find function `meow` in module `inner::right` + --> $DIR/diagnostics-same-crate.rs:42:19 + | +LL | inner::right::meow(); + | ^^^^ not found in `inner::right` + | +note: found an item that was configured out + --> $DIR/diagnostics-same-crate.rs:18:16 + | +LL | pub fn meow() {} + | ^^^^ + = note: the item is gated behind the `what-a-cool-feature` feature + +error[E0425]: cannot find function `uwu` in this scope + --> $DIR/diagnostics-same-crate.rs:28:5 + | +LL | uwu(); + | ^^^ not found in this scope + +error[E0425]: cannot find function `vanished` in this scope + --> $DIR/diagnostics-same-crate.rs:49:5 + | +LL | vanished(); + | ^^^^^^^^ not found in this scope + +error: aborting due to 5 previous errors + +Some errors have detailed explanations: E0425, E0433. +For more information about an error, try `rustc --explain E0425`. diff --git a/tests/ui/macros/builtin-std-paths-fail.stderr b/tests/ui/macros/builtin-std-paths-fail.stderr index ba6261011907c..004a39043b778 100644 --- a/tests/ui/macros/builtin-std-paths-fail.stderr +++ b/tests/ui/macros/builtin-std-paths-fail.stderr @@ -93,6 +93,9 @@ error[E0433]: failed to resolve: could not find `test` in `std` | LL | #[std::test] | ^^^^ could not find `test` in `std` + | +note: found an item that was configured out + --> $SRC_DIR/std/src/lib.rs:LL:COL error: aborting due to 16 previous errors diff --git a/tests/ui/macros/macro-outer-attributes.stderr b/tests/ui/macros/macro-outer-attributes.stderr index 0bdc3416f807e..0418e6116041b 100644 --- a/tests/ui/macros/macro-outer-attributes.stderr +++ b/tests/ui/macros/macro-outer-attributes.stderr @@ -4,6 +4,11 @@ error[E0425]: cannot find function `bar` in module `a` LL | a::bar(); | ^^^ not found in `a` | +note: found an item that was configured out + --> $DIR/macro-outer-attributes.rs:9:14 + | +LL | pub fn bar() { }); + | ^^^ help: consider importing this function | LL + use b::bar;