Skip to content

Account for sealed traits in privacy and trait bound errors #112686

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 2 commits into from
Jun 22, 2023
Merged
Show file tree
Hide file tree
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
92 changes: 64 additions & 28 deletions compiler/rustc_resolve/src/diagnostics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ pub(crate) struct ImportSuggestion {
pub descr: &'static str,
pub path: Path,
pub accessible: bool,
pub via_import: bool,
/// An extra note that should be issued if this item is suggested
pub note: Option<String>,
}
Expand Down Expand Up @@ -140,9 +141,9 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
}

let mut reported_spans = FxHashSet::default();
for error in &self.privacy_errors {
for error in std::mem::take(&mut self.privacy_errors) {
if reported_spans.insert(error.dedup_span) {
self.report_privacy_error(error);
self.report_privacy_error(&error);
}
}
}
Expand Down Expand Up @@ -1256,6 +1257,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
path,
accessible: child_accessible,
note,
via_import,
});
}
}
Expand Down Expand Up @@ -1609,8 +1611,9 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
None
}

fn report_privacy_error(&self, privacy_error: &PrivacyError<'_>) {
let PrivacyError { ident, binding, .. } = *privacy_error;
fn report_privacy_error(&mut self, privacy_error: &PrivacyError<'a>) {
let PrivacyError { ident, binding, outermost_res, parent_scope, dedup_span } =
*privacy_error;

let res = binding.res();
let ctor_fields_span = self.ctor_fields_span(binding);
Expand All @@ -1627,6 +1630,33 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
struct_span_err!(self.tcx.sess, ident.span, E0603, "{} `{}` is private", descr, ident);
err.span_label(ident.span, format!("private {}", descr));

if let Some((this_res, outer_ident)) = outermost_res {
let import_suggestions = self.lookup_import_candidates(
outer_ident,
this_res.ns().unwrap_or(Namespace::TypeNS),
&parent_scope,
&|res: Res| res == this_res,
);
let point_to_def = !show_candidates(
self.tcx,
&mut err,
Some(dedup_span.until(outer_ident.span.shrink_to_hi())),
&import_suggestions,
Instead::Yes,
FoundUse::Yes,
DiagnosticMode::Import,
vec![],
"",
);
// If we suggest importing a public re-export, don't point at the definition.
if point_to_def && ident.span != outer_ident.span {
err.span_label(
outer_ident.span,
format!("{} `{outer_ident}` is not publicly re-exported", this_res.descr()),
);
}
}

let mut non_exhaustive = None;
// If an ADT is foreign and marked as `non_exhaustive`, then that's
// probably why we have the privacy error.
Expand Down Expand Up @@ -2455,7 +2485,8 @@ pub(crate) fn import_candidates(

/// When an entity with a given name is not available in scope, we search for
/// entities with that name in all crates. This method allows outputting the
/// results of this search in a programmer-friendly way
/// results of this search in a programmer-friendly way. If any entities are
/// found and suggested, returns `true`, otherwise returns `false`.
fn show_candidates(
tcx: TyCtxt<'_>,
err: &mut Diagnostic,
Expand All @@ -2467,19 +2498,19 @@ fn show_candidates(
mode: DiagnosticMode,
path: Vec<Segment>,
append: &str,
) {
) -> bool {
if candidates.is_empty() {
return;
return false;
}

let mut accessible_path_strings: Vec<(String, &str, Option<DefId>, &Option<String>)> =
let mut accessible_path_strings: Vec<(String, &str, Option<DefId>, &Option<String>, bool)> =
Vec::new();
let mut inaccessible_path_strings: Vec<(String, &str, Option<DefId>, &Option<String>)> =
let mut inaccessible_path_strings: Vec<(String, &str, Option<DefId>, &Option<String>, bool)> =
Vec::new();

candidates.iter().for_each(|c| {
(if c.accessible { &mut accessible_path_strings } else { &mut inaccessible_path_strings })
.push((path_names_to_string(&c.path), c.descr, c.did, &c.note))
.push((path_names_to_string(&c.path), c.descr, c.did, &c.note, c.via_import))
});

// we want consistent results across executions, but candidates are produced
Expand All @@ -2493,20 +2524,25 @@ fn show_candidates(
}

if !accessible_path_strings.is_empty() {
let (determiner, kind, name) = if accessible_path_strings.len() == 1 {
("this", accessible_path_strings[0].1, format!(" `{}`", accessible_path_strings[0].0))
} else {
("one of these", "items", String::new())
};
let (determiner, kind, name, through) =
if let [(name, descr, _, _, via_import)] = &accessible_path_strings[..] {
(
"this",
*descr,
format!(" `{name}`"),
if *via_import { " through its public re-export" } else { "" },
)
} else {
("one of these", "items", String::new(), "")
};

let instead = if let Instead::Yes = instead { " instead" } else { "" };
let mut msg = if let DiagnosticMode::Pattern = mode {
format!(
"if you meant to match on {}{}{}, use the full path in the pattern",
kind, instead, name
"if you meant to match on {kind}{instead}{name}, use the full path in the pattern",
)
} else {
format!("consider importing {} {}{}", determiner, kind, instead)
format!("consider importing {determiner} {kind}{through}{instead}")
};

for note in accessible_path_strings.iter().flat_map(|cand| cand.3.as_ref()) {
Expand All @@ -2522,7 +2558,7 @@ fn show_candidates(
accessible_path_strings.into_iter().map(|a| a.0),
Applicability::MaybeIncorrect,
);
return;
return true;
}
DiagnosticMode::Import => ("", ""),
DiagnosticMode::Normal => ("use ", ";\n"),
Expand Down Expand Up @@ -2563,6 +2599,7 @@ fn show_candidates(

err.help(msg);
}
true
} else if !matches!(mode, DiagnosticMode::Import) {
assert!(!inaccessible_path_strings.is_empty());

Expand All @@ -2571,13 +2608,9 @@ fn show_candidates(
} else {
""
};
if inaccessible_path_strings.len() == 1 {
let (name, descr, def_id, note) = &inaccessible_path_strings[0];
if let [(name, descr, def_id, note, _)] = &inaccessible_path_strings[..] {
let msg = format!(
"{}{} `{}`{} exists but is inaccessible",
prefix,
descr,
name,
"{prefix}{descr} `{name}`{} exists but is inaccessible",
if let DiagnosticMode::Pattern = mode { ", which" } else { "" }
);

Expand All @@ -2594,11 +2627,11 @@ fn show_candidates(
err.note(note.to_string());
}
} else {
let (_, descr_first, _, _) = &inaccessible_path_strings[0];
let (_, descr_first, _, _, _) = &inaccessible_path_strings[0];
let descr = if inaccessible_path_strings
.iter()
.skip(1)
.all(|(_, descr, _, _)| descr == descr_first)
.all(|(_, descr, _, _, _)| descr == descr_first)
{
descr_first
} else {
Expand All @@ -2611,7 +2644,7 @@ fn show_candidates(
let mut has_colon = false;

let mut spans = Vec::new();
for (name, _, def_id, _) in &inaccessible_path_strings {
for (name, _, def_id, _, _) in &inaccessible_path_strings {
if let Some(local_def_id) = def_id.and_then(|did| did.as_local()) {
let span = tcx.source_span(local_def_id);
let span = tcx.sess.source_map().guess_head_span(span);
Expand All @@ -2637,6 +2670,9 @@ fn show_candidates(

err.span_note(multi_span, msg);
}
true
} else {
false
}
}

Expand Down
13 changes: 13 additions & 0 deletions compiler/rustc_resolve/src/ident.rs
Original file line number Diff line number Diff line change
Expand Up @@ -893,6 +893,8 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
ident,
binding,
dedup_span: path_span,
outermost_res: None,
parent_scope: *parent_scope,
});
} else {
return Err((Determined, Weak::No));
Expand Down Expand Up @@ -1369,6 +1371,9 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
let mut allow_super = true;
let mut second_binding = None;

// We'll provide more context to the privacy errors later, up to `len`.
let privacy_errors_len = self.privacy_errors.len();

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| {
Expand Down Expand Up @@ -1506,6 +1511,14 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
second_binding = Some(binding);
}
let res = binding.res();

// Mark every privacy error in this path with the res to the last element. This allows us
// to detect the item the user cares about and either find an alternative import, or tell
// the user it is not accessible.
for error in &mut self.privacy_errors[privacy_errors_len..] {
error.outermost_res = Some((res, ident));
}

let maybe_assoc = opt_ns != Some(MacroNS) && PathSource::Type.is_expected(res);
if let Some(next_module) = binding.module() {
module = Some(ModuleOrUniformRoot::Module(next_module));
Expand Down
19 changes: 19 additions & 0 deletions compiler/rustc_resolve/src/imports.rs
Original file line number Diff line number Diff line change
Expand Up @@ -792,6 +792,10 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
};
let prev_ambiguity_errors_len = self.ambiguity_errors.len();
let finalize = Finalize::with_root_span(import.root_id, import.span, import.root_span);

// We'll provide more context to the privacy errors later, up to `len`.
let privacy_errors_len = self.privacy_errors.len();

let path_res = self.resolve_path(
&import.module_path,
None,
Expand Down Expand Up @@ -931,6 +935,21 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
_ => unreachable!(),
};

if self.privacy_errors.len() != privacy_errors_len {
// Get the Res for the last element, so that we can point to alternative ways of
// importing it if available.
let mut path = import.module_path.clone();
path.push(Segment::from_ident(ident));
if let PathResult::Module(ModuleOrUniformRoot::Module(module)) =
self.resolve_path(&path, None, &import.parent_scope, Some(finalize), ignore_binding)
{
let res = module.res().map(|r| (r, ident));
for error in &mut self.privacy_errors[privacy_errors_len..] {
error.outermost_res = res;
}
}
}

let mut all_ns_err = true;
self.per_ns(|this, ns| {
if !type_ns_only || ns == TypeNS {
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_resolve/src/late/diagnostics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1831,6 +1831,7 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> {
path,
accessible: true,
note: None,
via_import: false,
},
));
} else {
Expand Down
3 changes: 3 additions & 0 deletions compiler/rustc_resolve/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -689,10 +689,13 @@ impl<'a> NameBindingKind<'a> {
}
}

#[derive(Debug)]
struct PrivacyError<'a> {
ident: Ident,
binding: &'a NameBinding<'a>,
dedup_span: Span,
outermost_res: Option<(Res, Ident)>,
parent_scope: ParentScope<'a>,
}

#[derive(Debug)]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2724,6 +2724,32 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
let msg = format!("required by this bound in `{short_item_name}`");
multispan.push_span_label(span, msg);
err.span_note(multispan, descr);
if let ty::PredicateKind::Clause(clause) = predicate.kind().skip_binder()
&& let ty::ClauseKind::Trait(trait_pred) = clause
{
let def_id = trait_pred.def_id();
let visible_item = if let Some(local) = def_id.as_local() {
// Check for local traits being reachable.
let vis = &self.tcx.resolutions(()).effective_visibilities;
// Account for non-`pub` traits in the root of the local crate.
let is_locally_reachable = self.tcx.parent(def_id).is_crate_root();
vis.is_reachable(local) || is_locally_reachable
} else {
// Check for foreign traits being reachable.
self.tcx.visible_parent_map(()).get(&def_id).is_some()
};
if let DefKind::Trait = tcx.def_kind(item_def_id) && !visible_item {
// FIXME(estebank): extend this to search for all the types that do
// implement this trait and list them.
err.note(format!(
"`{short_item_name}` is a \"sealed trait\", because to implement \
it you also need to implelement `{}`, which is not accessible; \
this is usually done to force you to use one of the provided \
types that already implement it",
with_no_trimmed_paths!(tcx.def_path_str(def_id)),
));
}
}
} else {
err.span_note(tcx.def_span(item_def_id), descr);
}
Expand Down
8 changes: 8 additions & 0 deletions tests/ui/extern/extern-crate-visibility.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ note: the crate import `core` is defined here
|
LL | extern crate core;
| ^^^^^^^^^^^^^^^^^^
help: consider importing this module instead
|
LL | use std::cell;
| ~~~~~~~~~

error[E0603]: crate import `core` is private
--> $DIR/extern-crate-visibility.rs:9:10
Expand All @@ -21,6 +25,10 @@ note: the crate import `core` is defined here
|
LL | extern crate core;
| ^^^^^^^^^^^^^^^^^^
help: consider importing this struct instead
|
LL | std::cell::Cell::new(0);
| ~~~~~~~~~~~~~~~

error: aborting due to 2 previous errors

Expand Down
8 changes: 6 additions & 2 deletions tests/ui/issues/issue-11680.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@ error[E0603]: enum `Foo` is private
--> $DIR/issue-11680.rs:6:21
|
LL | let _b = other::Foo::Bar(1);
| ^^^ private enum
| ^^^ --- tuple variant `Bar` is not publicly re-exported
| |
| private enum
|
note: the enum `Foo` is defined here
--> $DIR/auxiliary/issue-11680.rs:1:1
Expand All @@ -14,7 +16,9 @@ error[E0603]: enum `Foo` is private
--> $DIR/issue-11680.rs:9:27
|
LL | let _b = other::test::Foo::Bar(1);
| ^^^ private enum
| ^^^ --- tuple variant `Bar` is not publicly re-exported
| |
| private enum
|
note: the enum `Foo` is defined here
--> $DIR/auxiliary/issue-11680.rs:6:5
Expand Down
4 changes: 2 additions & 2 deletions tests/ui/macros/issue-88228.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ error: cannot find macro `bla` in this scope
LL | bla!();
| ^^^
|
help: consider importing this macro
help: consider importing this macro through its public re-export
|
LL + use crate::hey::bla;
|
Expand All @@ -23,7 +23,7 @@ error: cannot find derive macro `Bla` in this scope
LL | #[derive(Bla)]
| ^^^
|
help: consider importing this derive macro
help: consider importing this derive macro through its public re-export
|
LL + use crate::hey::Bla;
|
Expand Down
Loading