|
1 | 1 | use std::cmp::Reverse;
|
| 2 | +use std::ptr; |
2 | 3 |
|
3 | 4 | use log::debug;
|
4 | 5 | use rustc::bug;
|
@@ -937,26 +938,63 @@ impl<'a> Resolver<'a> {
|
937 | 938 | crate fn report_privacy_error(&self, privacy_error: &PrivacyError<'_>) {
|
938 | 939 | let PrivacyError { ident, binding, .. } = *privacy_error;
|
939 | 940 |
|
| 941 | + let res = binding.res(); |
940 | 942 | let ctor_fields_span = self.ctor_fields_span(binding);
|
941 |
| - let mut descr = binding.res().descr().to_string(); |
942 |
| - if ctor_fields_span.is_some() { |
943 |
| - descr += " constructor"; |
944 |
| - } |
945 |
| - if binding.is_import() { |
946 |
| - descr += " import"; |
947 |
| - } |
948 |
| - |
| 943 | + let plain_descr = res.descr().to_string(); |
| 944 | + let nonimport_descr = |
| 945 | + if ctor_fields_span.is_some() { plain_descr + " constructor" } else { plain_descr }; |
| 946 | + let import_descr = nonimport_descr.clone() + " import"; |
| 947 | + let get_descr = |
| 948 | + |b: &NameBinding<'_>| if b.is_import() { &import_descr } else { &nonimport_descr }; |
| 949 | + |
| 950 | + // Print the primary message. |
| 951 | + let descr = get_descr(binding); |
949 | 952 | let mut err =
|
950 | 953 | struct_span_err!(self.session, ident.span, E0603, "{} `{}` is private", descr, ident);
|
951 | 954 | err.span_label(ident.span, &format!("this {} is private", descr));
|
952 | 955 | if let Some(span) = ctor_fields_span {
|
953 | 956 | err.span_label(span, "a constructor is private if any of the fields is private");
|
954 | 957 | }
|
955 | 958 |
|
956 |
| - err.span_note( |
957 |
| - self.session.source_map().def_span(binding.span), |
958 |
| - &format!("the {} `{}` is defined here", descr, ident), |
959 |
| - ); |
| 959 | + // Print the whole import chain to make it easier to see what happens. |
| 960 | + let first_binding = binding; |
| 961 | + let mut next_binding = Some(binding); |
| 962 | + let mut next_ident = ident; |
| 963 | + while let Some(binding) = next_binding { |
| 964 | + let name = next_ident; |
| 965 | + next_binding = match binding.kind { |
| 966 | + _ if res == Res::Err => None, |
| 967 | + NameBindingKind::Import { binding, directive, .. } => match directive.subclass { |
| 968 | + _ if binding.span.is_dummy() => None, |
| 969 | + ImportDirectiveSubclass::SingleImport { source, .. } => { |
| 970 | + next_ident = source; |
| 971 | + Some(binding) |
| 972 | + } |
| 973 | + ImportDirectiveSubclass::GlobImport { .. } |
| 974 | + | ImportDirectiveSubclass::MacroUse => Some(binding), |
| 975 | + ImportDirectiveSubclass::ExternCrate { .. } => None, |
| 976 | + }, |
| 977 | + _ => None, |
| 978 | + }; |
| 979 | + |
| 980 | + let first = ptr::eq(binding, first_binding); |
| 981 | + let descr = get_descr(binding); |
| 982 | + let msg = format!( |
| 983 | + "{and_refers_to}the {item} `{name}`{which} is defined here{dots}", |
| 984 | + and_refers_to = if first { "" } else { "...and refers to " }, |
| 985 | + item = descr, |
| 986 | + name = name, |
| 987 | + which = if first { "" } else { " which" }, |
| 988 | + dots = if next_binding.is_some() { "..." } else { "" }, |
| 989 | + ); |
| 990 | + let def_span = self.session.source_map().def_span(binding.span); |
| 991 | + let mut note_span = MultiSpan::from_span(def_span); |
| 992 | + if !first && next_binding.is_none() && binding.vis == ty::Visibility::Public { |
| 993 | + note_span.push_span_label(def_span, "consider importing it directly".into()); |
| 994 | + } |
| 995 | + err.span_note(note_span, &msg); |
| 996 | + } |
| 997 | + |
960 | 998 | err.emit();
|
961 | 999 | }
|
962 | 1000 | }
|
|
0 commit comments