Skip to content

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

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jun 7, 2023
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions compiler/rustc_ast/src/expand/mod.rs
Original file line number Diff line number Diff line change
@@ -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<ModId = DefId> {
pub parent_module: ModId,
pub name: Ident,
pub cfg: MetaItem,
}

impl<ModId> StrippedCfgItem<ModId> {
pub fn map_mod_id<New>(self, f: impl FnOnce(ModId) -> New) -> StrippedCfgItem<New> {
StrippedCfgItem { parent_module: f(self.parent_module), name: self.name, cfg: self.cfg }
}
}
4 changes: 3 additions & 1 deletion compiler/rustc_expand/src/base.rs
Original file line number Diff line number Diff line change
@@ -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>,
20 changes: 14 additions & 6 deletions compiler/rustc_expand/src/config.rs
Original file line number Diff line number Diff line change
@@ -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`
49 changes: 44 additions & 5 deletions compiler/rustc_expand/src/expand.rs
Original file line number Diff line number Diff line change
@@ -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,16 +1712,17 @@ 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
// while keeping the tools working.
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;
}

9 changes: 9 additions & 0 deletions compiler/rustc_metadata/src/rmeta/decoder.rs
Original file line number Diff line number Diff line change
@@ -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();
1 change: 1 addition & 0 deletions compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs
Original file line number Diff line number Diff line change
@@ -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() }
13 changes: 13 additions & 0 deletions compiler/rustc_metadata/src/rmeta/encoder.rs
Original file line number Diff line number Diff line change
@@ -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<StrippedCfgItem<DefIndex>> {
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<DefIndex> {
empty_proc_macro!(self);
self.lazy_array(self.tcx.traits(LOCAL_CRATE).iter().map(|def_id| def_id.index))
2 changes: 2 additions & 0 deletions compiler/rustc_metadata/src/rmeta/mod.rs
Original file line number Diff line number Diff line change
@@ -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<LangItem>,
stripped_cfg_items: LazyArray<StrippedCfgItem<DefIndex>>,
diagnostic_items: LazyArray<(Symbol, DefIndex)>,
native_libraries: LazyArray<NativeLib>,
foreign_modules: LazyArray<ForeignModule>,
1 change: 1 addition & 0 deletions compiler/rustc_middle/src/arena.rs
Original file line number Diff line number Diff line change
@@ -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,
]);
)
11 changes: 10 additions & 1 deletion compiler/rustc_middle/src/query/mod.rs
Original file line number Diff line number Diff line change
@@ -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! }
1 change: 1 addition & 0 deletions compiler/rustc_middle/src/ty/parameterized.rs
Original file line number Diff line number Diff line change
@@ -73,6 +73,7 @@ trivially_parameterized_over_tcx! {
ty::fast_reject::SimplifiedType,
rustc_ast::Attribute,
rustc_ast::DelimArgs,
rustc_ast::expand::StrippedCfgItem<rustc_hir::def_id::DefIndex>,
rustc_attr::ConstStability,
rustc_attr::DefaultBodyStability,
rustc_attr::Deprecation,
69 changes: 61 additions & 8 deletions compiler/rustc_resolve/src/diagnostics.rs
Original file line number Diff line number Diff line change
@@ -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<Vec<Rib<'a>>>>,
ignore_binding: Option<&'a NameBinding<'a>>,
module: Option<ModuleOrUniformRoot<'a>>,
i: usize,
failed_segment_idx: usize,
ident: Ident,
) -> (String, Option<Suggestion>) {
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::<Vec<_>>();
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:
4 changes: 4 additions & 0 deletions compiler/rustc_resolve/src/errors.rs
Original file line number Diff line number Diff line change
@@ -330,6 +330,7 @@ pub(crate) struct ParamInTyOfConstParam {
pub(crate) param_kind: Option<ParamKindInTyOfConstParam>,
}

#[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)]
92 changes: 50 additions & 42 deletions compiler/rustc_resolve/src/ident.rs
Original file line number Diff line number Diff line change
@@ -1365,20 +1365,12 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
ribs: Option<&PerNS<Vec<Rib<'a>>>>,
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,
)
},
);
}
}
}
26 changes: 23 additions & 3 deletions compiler/rustc_resolve/src/imports.rs
Original file line number Diff line number Diff line change
@@ -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(
24 changes: 20 additions & 4 deletions compiler/rustc_resolve/src/late.rs
Original file line number Diff line number Diff line change
@@ -3524,15 +3524,17 @@ 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,
instead,
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"),
37 changes: 28 additions & 9 deletions compiler/rustc_resolve/src/late/diagnostics.rs
Original file line number Diff line number Diff line change
@@ -149,6 +149,7 @@ struct BaseError {
span_label: Option<(Span, &'static str)>,
could_be_expr: bool,
suggestion: Option<(Span, &'static str, String)>,
module: Option<DefId>,
}

#[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<ImportSuggestion>) {
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)
}

27 changes: 25 additions & 2 deletions compiler/rustc_resolve/src/lib.rs
Original file line number Diff line number Diff line change
@@ -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,13 +172,15 @@ enum ImplTraitContext {
Universal(LocalDefId),
}

#[derive(Debug)]
struct BindingError {
name: Symbol,
origin: BTreeSet<Span>,
target: BTreeSet<Span>,
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<Suggestion> },
FailedToResolve {
last_segment: Option<Symbol>,
label: String,
suggestion: Option<Suggestion>,
module: Option<ModuleOrUniformRoot<'a>>,
},
/// 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<Suggestion>,
is_error_from_last_segment: bool,
module: Option<ModuleOrUniformRoot<'a>>,
},
}

@@ -410,11 +419,12 @@ impl<'a> PathResult<'a> {
span: Span,
is_error_from_last_segment: bool,
finalize: bool,
module: Option<ModuleOrUniformRoot<'a>>,
label_and_suggestion: impl FnOnce() -> (String, Option<Suggestion>),
) -> 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<NodeId>,

/// Names of items that were stripped out via cfg with their corresponding cfg meta item.
stripped_cfg_items: Vec<StrippedCfgItem<NodeId>>,

effective_visibilities: EffectiveVisibilities,
doc_link_resolutions: FxHashMap<LocalDefId, DocLinkResMap>,
doc_link_traits_in_scope: FxHashMap<LocalDefId, Vec<DefId>>,
@@ -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,
12 changes: 9 additions & 3 deletions compiler/rustc_resolve/src/macros.rs
Original file line number Diff line number Diff line change
@@ -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!(),
22 changes: 22 additions & 0 deletions tests/ui/cfg/auxiliary/cfged_out.rs
Original file line number Diff line number Diff line change
@@ -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() {}
31 changes: 31 additions & 0 deletions tests/ui/cfg/diagnostics-cross-crate.rs
Original file line number Diff line number Diff line change
@@ -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`
}
53 changes: 53 additions & 0 deletions tests/ui/cfg/diagnostics-cross-crate.stderr
Original file line number Diff line number Diff line change
@@ -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`.
12 changes: 12 additions & 0 deletions tests/ui/cfg/diagnostics-not-a-def.rs
Original file line number Diff line number Diff line change
@@ -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
}
9 changes: 9 additions & 0 deletions tests/ui/cfg/diagnostics-not-a-def.stderr
Original file line number Diff line number Diff line change
@@ -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`.
16 changes: 16 additions & 0 deletions tests/ui/cfg/diagnostics-reexport.rs
Original file line number Diff line number Diff line change
@@ -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`
}
15 changes: 15 additions & 0 deletions tests/ui/cfg/diagnostics-reexport.stderr
Original file line number Diff line number Diff line change
@@ -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`.
51 changes: 51 additions & 0 deletions tests/ui/cfg/diagnostics-same-crate.rs
Original file line number Diff line number Diff line change
@@ -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
}
47 changes: 47 additions & 0 deletions tests/ui/cfg/diagnostics-same-crate.stderr
Original file line number Diff line number Diff line change
@@ -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`.
3 changes: 3 additions & 0 deletions tests/ui/macros/builtin-std-paths-fail.stderr
Original file line number Diff line number Diff line change
@@ -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

5 changes: 5 additions & 0 deletions tests/ui/macros/macro-outer-attributes.stderr
Original file line number Diff line number Diff line change
@@ -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;