diff --git a/compiler/rustc_hir_analysis/src/astconv/errors.rs b/compiler/rustc_hir_analysis/src/astconv/errors.rs index 32be7e0837b6d..cae3aae46a50a 100644 --- a/compiler/rustc_hir_analysis/src/astconv/errors.rs +++ b/compiler/rustc_hir_analysis/src/astconv/errors.rs @@ -9,7 +9,7 @@ use rustc_errors::{pluralize, struct_span_err, Applicability, Diagnostic, ErrorG use rustc_hir as hir; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_infer::traits::FulfillmentError; -use rustc_middle::ty::{self, suggest_constraining_type_param, Ty, TyCtxt}; +use rustc_middle::ty::{self, suggest_constraining_type_param, AssocItem, Ty, TyCtxt}; use rustc_session::parse::feature_err; use rustc_span::edit_distance::find_best_match_for_name; use rustc_span::symbol::{sym, Ident}; @@ -513,6 +513,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { if associated_types.values().all(|v| v.is_empty()) { return; } + let tcx = self.tcx(); // FIXME: Marked `mut` so that we can replace the spans further below with a more // appropriate one, but this should be handled earlier in the span assignment. @@ -585,6 +586,24 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { } } + let bound_names = trait_bounds + .iter() + .flat_map(|poly_trait_ref| { + poly_trait_ref.trait_ref.path.segments.iter().flat_map(|x| { + x.args.iter().flat_map(|args| { + args.bindings.iter().map(|binding| { + let name = binding.ident.name; + let trait_def = poly_trait_ref.trait_ref.path.res.opt_def_id(); + let assoc_item = trait_def.and_then(|did| { + tcx.associated_items(did).filter_by_name_unhygienic(name).next() + }); + (name, assoc_item) + }) + }) + }) + }) + .collect::>>(); + let mut names = names .into_iter() .map(|(trait_, mut assocs)| { @@ -614,6 +633,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { pluralize!(names_len), names, ); + let mut rename_suggestions = vec![]; let mut suggestions = vec![]; let mut types_count = 0; let mut where_constraints = vec![]; @@ -625,23 +645,47 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { *names.entry(item.name).or_insert(0) += 1; } let mut dupes = false; + let mut shadows = false; for item in assoc_items { let prefix = if names[&item.name] > 1 { let trait_def_id = item.container_id(tcx); dupes = true; format!("{}::", tcx.def_path_str(trait_def_id)) + } else if bound_names.contains_key(&item.name) { + let trait_def_id = item.container_id(tcx); + shadows = true; + format!("{}::", tcx.def_path_str(trait_def_id)) } else { String::new() }; if let Some(sp) = tcx.hir().span_if_local(item.def_id) { err.span_label(sp, format!("`{}{}` defined here", prefix, item.name)); } + + if let Some(Some(assoc_item)) = bound_names.get(&item.name) { + err.span_label( + tcx.def_span(assoc_item.def_id), + format!("`{}{}` shadowed here", prefix, item.name), + ); + } } if potential_assoc_types.len() == assoc_items.len() { // When the amount of missing associated types equals the number of // extra type arguments present. A suggesting to replace the generic args with // associated types is already emitted. already_has_generics_args_suggestion = true; + } else if shadows { + for item in assoc_items { + if let Some(Some(assoc_item)) = bound_names.get(&item.name) { + if let Some(sp) = tcx.hir().span_if_local(item.def_id) { + rename_suggestions.push(sp); + } + + if let Some(sp) = tcx.hir().span_if_local(assoc_item.def_id) { + rename_suggestions.push(sp); + } + } + } } else if let (Ok(snippet), false) = (tcx.sess.source_map().span_to_snippet(*span), dupes) { @@ -725,6 +769,10 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { err.span_help(where_constraints, where_msg); } } + + for span in rename_suggestions { + err.span_help(span, "consider renaming this associated type"); + } err.emit(); } }