Skip to content

Commit 6a77a75

Browse files
authored
Rollup merge of rust-lang#69811 - petrochenkov:privdiag2, r=estebank
resolve: Print import chains on privacy errors A part of rust-lang#67951 that doesn't require hacks. r? @estebank
2 parents 8f39930 + 059e825 commit 6a77a75

File tree

5 files changed

+115
-42
lines changed

5 files changed

+115
-42
lines changed

src/librustc_resolve/diagnostics.rs

+69-36
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
use std::cmp::Reverse;
2+
use std::ptr;
23

34
use log::debug;
45
use rustc::bug;
@@ -916,50 +917,82 @@ impl<'a> Resolver<'a> {
916917
err.emit();
917918
}
918919

919-
crate fn report_privacy_error(&self, privacy_error: &PrivacyError<'_>) {
920-
let PrivacyError { ident, binding, .. } = *privacy_error;
921-
let session = &self.session;
922-
let mk_struct_span_error = |is_constructor| {
923-
let mut descr = binding.res().descr().to_string();
924-
if is_constructor {
925-
descr += " constructor";
926-
}
927-
if binding.is_import() {
928-
descr += " import";
929-
}
930-
931-
let mut err =
932-
struct_span_err!(session, ident.span, E0603, "{} `{}` is private", descr, ident);
933-
934-
err.span_label(ident.span, &format!("this {} is private", descr));
935-
err.span_note(
936-
session.source_map().def_span(binding.span),
937-
&format!("the {} `{}` is defined here", descr, ident),
938-
);
939-
940-
err
941-
};
942-
943-
let mut err = if let NameBindingKind::Res(
920+
/// If the binding refers to a tuple struct constructor with fields,
921+
/// returns the span of its fields.
922+
fn ctor_fields_span(&self, binding: &NameBinding<'_>) -> Option<Span> {
923+
if let NameBindingKind::Res(
944924
Res::Def(DefKind::Ctor(CtorOf::Struct, CtorKind::Fn), ctor_def_id),
945925
_,
946926
) = binding.kind
947927
{
948928
let def_id = (&*self).parent(ctor_def_id).expect("no parent for a constructor");
949929
if let Some(fields) = self.field_names.get(&def_id) {
950-
let mut err = mk_struct_span_error(true);
951930
let first_field = fields.first().expect("empty field list in the map");
952-
err.span_label(
953-
fields.iter().fold(first_field.span, |acc, field| acc.to(field.span)),
954-
"a constructor is private if any of the fields is private",
955-
);
956-
err
957-
} else {
958-
mk_struct_span_error(false)
931+
return Some(fields.iter().fold(first_field.span, |acc, field| acc.to(field.span)));
959932
}
960-
} else {
961-
mk_struct_span_error(false)
962-
};
933+
}
934+
None
935+
}
936+
937+
crate fn report_privacy_error(&self, privacy_error: &PrivacyError<'_>) {
938+
let PrivacyError { ident, binding, .. } = *privacy_error;
939+
940+
let res = binding.res();
941+
let ctor_fields_span = self.ctor_fields_span(binding);
942+
let plain_descr = res.descr().to_string();
943+
let nonimport_descr =
944+
if ctor_fields_span.is_some() { plain_descr + " constructor" } else { plain_descr };
945+
let import_descr = nonimport_descr.clone() + " import";
946+
let get_descr =
947+
|b: &NameBinding<'_>| if b.is_import() { &import_descr } else { &nonimport_descr };
948+
949+
// Print the primary message.
950+
let descr = get_descr(binding);
951+
let mut err =
952+
struct_span_err!(self.session, ident.span, E0603, "{} `{}` is private", descr, ident);
953+
err.span_label(ident.span, &format!("this {} is private", descr));
954+
if let Some(span) = ctor_fields_span {
955+
err.span_label(span, "a constructor is private if any of the fields is private");
956+
}
957+
958+
// Print the whole import chain to make it easier to see what happens.
959+
let first_binding = binding;
960+
let mut next_binding = Some(binding);
961+
let mut next_ident = ident;
962+
while let Some(binding) = next_binding {
963+
let name = next_ident;
964+
next_binding = match binding.kind {
965+
_ if res == Res::Err => None,
966+
NameBindingKind::Import { binding, directive, .. } => match directive.subclass {
967+
_ if binding.span.is_dummy() => None,
968+
ImportDirectiveSubclass::SingleImport { source, .. } => {
969+
next_ident = source;
970+
Some(binding)
971+
}
972+
ImportDirectiveSubclass::GlobImport { .. }
973+
| ImportDirectiveSubclass::MacroUse => Some(binding),
974+
ImportDirectiveSubclass::ExternCrate { .. } => None,
975+
},
976+
_ => None,
977+
};
978+
979+
let first = ptr::eq(binding, first_binding);
980+
let descr = get_descr(binding);
981+
let msg = format!(
982+
"{and_refers_to}the {item} `{name}`{which} is defined here{dots}",
983+
and_refers_to = if first { "" } else { "...and refers to " },
984+
item = descr,
985+
name = name,
986+
which = if first { "" } else { " which" },
987+
dots = if next_binding.is_some() { "..." } else { "" },
988+
);
989+
let def_span = self.session.source_map().def_span(binding.span);
990+
let mut note_span = MultiSpan::from_span(def_span);
991+
if !first && next_binding.is_none() && binding.vis == ty::Visibility::Public {
992+
note_span.push_span_label(def_span, "consider importing it directly".into());
993+
}
994+
err.span_note(note_span, &msg);
995+
}
963996

964997
err.emit();
965998
}

src/test/ui/imports/issue-55884-2.stderr

+16-1
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,26 @@ error[E0603]: struct import `ParseOptions` is private
44
LL | pub use parser::ParseOptions;
55
| ^^^^^^^^^^^^ this struct import is private
66
|
7-
note: the struct import `ParseOptions` is defined here
7+
note: the struct import `ParseOptions` is defined here...
88
--> $DIR/issue-55884-2.rs:9:9
99
|
1010
LL | use ParseOptions;
1111
| ^^^^^^^^^^^^
12+
note: ...and refers to the struct import `ParseOptions` which is defined here...
13+
--> $DIR/issue-55884-2.rs:12:9
14+
|
15+
LL | pub use parser::ParseOptions;
16+
| ^^^^^^^^^^^^^^^^^^^^
17+
note: ...and refers to the struct import `ParseOptions` which is defined here...
18+
--> $DIR/issue-55884-2.rs:6:13
19+
|
20+
LL | pub use options::*;
21+
| ^^^^^^^^^^
22+
note: ...and refers to the struct `ParseOptions` which is defined here
23+
--> $DIR/issue-55884-2.rs:2:5
24+
|
25+
LL | pub struct ParseOptions {}
26+
| ^^^^^^^^^^^^^^^^^^^^^^^ consider importing it directly
1227

1328
error: aborting due to previous error
1429

src/test/ui/imports/reexports.stderr

+12-2
Original file line numberDiff line numberDiff line change
@@ -16,23 +16,33 @@ error[E0603]: module import `foo` is private
1616
LL | use b::a::foo::S;
1717
| ^^^ this module import is private
1818
|
19-
note: the module import `foo` is defined here
19+
note: the module import `foo` is defined here...
2020
--> $DIR/reexports.rs:21:17
2121
|
2222
LL | pub use super::foo; // This is OK since the value `foo` is visible enough.
2323
| ^^^^^^^^^^
24+
note: ...and refers to the module `foo` which is defined here
25+
--> $DIR/reexports.rs:16:5
26+
|
27+
LL | mod foo {
28+
| ^^^^^^^
2429

2530
error[E0603]: module import `foo` is private
2631
--> $DIR/reexports.rs:34:15
2732
|
2833
LL | use b::b::foo::S as T;
2934
| ^^^ this module import is private
3035
|
31-
note: the module import `foo` is defined here
36+
note: the module import `foo` is defined here...
3237
--> $DIR/reexports.rs:26:17
3338
|
3439
LL | pub use super::*; // This is also OK since the value `foo` is visible enough.
3540
| ^^^^^^^^
41+
note: ...and refers to the module `foo` which is defined here
42+
--> $DIR/reexports.rs:16:5
43+
|
44+
LL | mod foo {
45+
| ^^^^^^^
3646

3747
warning: glob import doesn't reexport anything because no candidate is public enough
3848
--> $DIR/reexports.rs:9:17

src/test/ui/privacy/privacy2.stderr

+6-1
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,16 @@ error[E0603]: function import `foo` is private
1010
LL | use bar::glob::foo;
1111
| ^^^ this function import is private
1212
|
13-
note: the function import `foo` is defined here
13+
note: the function import `foo` is defined here...
1414
--> $DIR/privacy2.rs:10:13
1515
|
1616
LL | use foo;
1717
| ^^^
18+
note: ...and refers to the function `foo` which is defined here
19+
--> $DIR/privacy2.rs:14:1
20+
|
21+
LL | pub fn foo() {}
22+
| ^^^^^^^^^^^^ consider importing it directly
1823

1924
error: requires `sized` lang_item
2025

src/test/ui/shadowed/shadowed-use-visibility.stderr

+12-2
Original file line numberDiff line numberDiff line change
@@ -4,23 +4,33 @@ error[E0603]: module import `bar` is private
44
LL | use foo::bar::f as g;
55
| ^^^ this module import is private
66
|
7-
note: the module import `bar` is defined here
7+
note: the module import `bar` is defined here...
88
--> $DIR/shadowed-use-visibility.rs:4:9
99
|
1010
LL | use foo as bar;
1111
| ^^^^^^^^^^
12+
note: ...and refers to the module `foo` which is defined here
13+
--> $DIR/shadowed-use-visibility.rs:1:1
14+
|
15+
LL | mod foo {
16+
| ^^^^^^^
1217

1318
error[E0603]: module import `f` is private
1419
--> $DIR/shadowed-use-visibility.rs:15:10
1520
|
1621
LL | use bar::f::f;
1722
| ^ this module import is private
1823
|
19-
note: the module import `f` is defined here
24+
note: the module import `f` is defined here...
2025
--> $DIR/shadowed-use-visibility.rs:11:9
2126
|
2227
LL | use foo as f;
2328
| ^^^^^^^^
29+
note: ...and refers to the module `foo` which is defined here
30+
--> $DIR/shadowed-use-visibility.rs:1:1
31+
|
32+
LL | mod foo {
33+
| ^^^^^^^
2434

2535
error: aborting due to 2 previous errors
2636

0 commit comments

Comments
 (0)