diff options
| author | LeSeulArtichaut <leseulartichaut@gmail.com> | 2020-12-07 12:34:27 +0100 |
|---|---|---|
| committer | LeSeulArtichaut <leseulartichaut@gmail.com> | 2020-12-16 14:41:47 +0100 |
| commit | cfc38d2d0892f0842dd50cd0bd889e9ae1ea7508 (patch) | |
| tree | 67556f4f12f9f50b7f41371398f862654d791078 /compiler | |
| parent | a68864b68859e5848865bd392ce6d892b16f5cdd (diff) | |
| download | rust-cfc38d2d0892f0842dd50cd0bd889e9ae1ea7508.tar.gz rust-cfc38d2d0892f0842dd50cd0bd889e9ae1ea7508.zip | |
Take into account negative impls in "trait item not found" suggestions
Diffstat (limited to 'compiler')
| -rw-r--r-- | compiler/rustc_typeck/src/check/method/suggest.rs | 108 |
1 files changed, 80 insertions, 28 deletions
diff --git a/compiler/rustc_typeck/src/check/method/suggest.rs b/compiler/rustc_typeck/src/check/method/suggest.rs index 7ed2933c08b..0f6253493cf 100644 --- a/compiler/rustc_typeck/src/check/method/suggest.rs +++ b/compiler/rustc_typeck/src/check/method/suggest.rs @@ -12,6 +12,7 @@ use rustc_hir::lang_items::LangItem; use rustc_hir::{ExprKind, Node, QPath}; use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; use rustc_middle::hir::map as hir_map; +use rustc_middle::ty::fast_reject::simplify_type; use rustc_middle::ty::print::with_crate_prefix; use rustc_middle::ty::{ self, ToPolyTraitRef, ToPredicate, Ty, TyCtxt, TypeFoldable, WithConstness, @@ -1074,19 +1075,19 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } else { "items from traits can only be used if the trait is implemented and in scope" }); + let candidates_len = candidates.len(); let message = |action| { format!( "the following {traits_define} an item `{name}`, perhaps you need to {action} \ {one_of_them}:", traits_define = - if candidates.len() == 1 { "trait defines" } else { "traits define" }, + if candidates_len == 1 { "trait defines" } else { "traits define" }, action = action, - one_of_them = if candidates.len() == 1 { "it" } else { "one of them" }, + one_of_them = if candidates_len == 1 { "it" } else { "one of them" }, name = item_name, ) }; // Obtain the span for `param` and use it for a structured suggestion. - let mut suggested = false; if let (Some(ref param), Some(ref table)) = (param_type, self.in_progress_typeck_results) { @@ -1147,7 +1148,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { Applicability::MaybeIncorrect, ); } - suggested = true; + return; } Node::Item(hir::Item { kind: hir::ItemKind::Trait(.., bounds, _), @@ -1167,45 +1168,96 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }), Applicability::MaybeIncorrect, ); - suggested = true; + return; } _ => {} } } } - if !suggested { - let action = if let Some(param) = param_type { - format!("restrict type parameter `{}` with", param) - } else { - // FIXME: it might only need to be imported into scope, not implemented. - "implement".to_string() - }; - let mut use_note = true; - if let [trait_info] = &candidates[..] { - if let Some(span) = self.tcx.hir().span_if_local(trait_info.def_id) { - err.span_note( - self.tcx.sess.source_map().guess_head_span(span), - &format!( - "`{}` defines an item `{}`, perhaps you need to {} it", - self.tcx.def_path_str(trait_info.def_id), - item_name, - action - ), - ); - use_note = false + let (potential_candidates, explicitly_negative) = if param_type.is_some() { + // FIXME: Even though negative bounds are not implemented, we could maybe handle + // cases where a positive bound implies a negative impl. + (candidates, Vec::new()) + } else if let Some(simp_rcvr_ty) = simplify_type(self.tcx, rcvr_ty, true) { + let mut potential_candidates = Vec::new(); + let mut explicitly_negative = Vec::new(); + for candidate in candidates { + // Check if there's a negative impl of `candidate` for `rcvr_ty` + if self + .tcx + .all_impls(candidate.def_id) + .filter(|imp_did| { + self.tcx.impl_polarity(*imp_did) == ty::ImplPolarity::Negative + }) + .any(|imp_did| { + let imp = self.tcx.impl_trait_ref(imp_did).unwrap(); + let imp_simp = simplify_type(self.tcx, imp.self_ty(), true); + imp_simp.map(|s| s == simp_rcvr_ty).unwrap_or(false) + }) + { + explicitly_negative.push(candidate); + } else { + potential_candidates.push(candidate); } } - if use_note { + (potential_candidates, explicitly_negative) + } else { + // We don't know enough about `recv_ty` to make proper suggestions. + (candidates, Vec::new()) + }; + + let action = if let Some(param) = param_type { + format!("restrict type parameter `{}` with", param) + } else { + // FIXME: it might only need to be imported into scope, not implemented. + "implement".to_string() + }; + match &potential_candidates[..] { + [] => {} + [trait_info] if trait_info.def_id.is_local() => { + let span = self.tcx.hir().span_if_local(trait_info.def_id).unwrap(); + err.span_note( + self.tcx.sess.source_map().guess_head_span(span), + &format!( + "`{}` defines an item `{}`, perhaps you need to {} it", + self.tcx.def_path_str(trait_info.def_id), + item_name, + action + ), + ); + } + trait_infos => { let mut msg = message(action); - for (i, trait_info) in candidates.iter().enumerate() { + for (i, trait_info) in trait_infos.iter().enumerate() { msg.push_str(&format!( "\ncandidate #{}: `{}`", i + 1, self.tcx.def_path_str(trait_info.def_id), )); } - err.note(&msg[..]); + err.note(&msg); + } + } + match &explicitly_negative[..] { + [] => {} + [trait_info] => { + let msg = format!( + "the trait `{}` defines an item `{}`, but is explicitely unimplemented", + self.tcx.def_path_str(trait_info.def_id), + item_name + ); + err.note(&msg); + } + trait_infos => { + let mut msg = format!( + "the following traits define an item `{}`, but are explicitely unimplemented:", + item_name + ); + for trait_info in trait_infos { + msg.push_str(&format!("\n{}", self.tcx.def_path_str(trait_info.def_id))); + } + err.note(&msg); } } } |
