diff options
| author | Matthias Krüger <matthias.krueger@famsik.de> | 2023-03-12 08:13:26 +0100 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2023-03-12 08:13:26 +0100 |
| commit | 67c9dbfe0276c3893627640960cb388678252ae3 (patch) | |
| tree | 75a9907896a790459d3a133fa0e18cb783ec6e9a /compiler/rustc_trait_selection/src/traits | |
| parent | b16ed694f7967d1381da5b43085dfbce2f1de723 (diff) | |
| parent | 8ac7d0eef5621338d3650ec6884c58bc28a5c5f6 (diff) | |
| download | rust-67c9dbfe0276c3893627640960cb388678252ae3.tar.gz rust-67c9dbfe0276c3893627640960cb388678252ae3.zip | |
Rollup merge of #108841 - jackh726:issue-90528, r=compiler-errors
Add suggestion to diagnostic when user has array but trait wants slice. (rebased) Rebase of #91314, except for change to multipart suggestion Resolves #90528 r? ``@compiler-errors`` since you requested the multipart suggestion
Diffstat (limited to 'compiler/rustc_trait_selection/src/traits')
| -rw-r--r-- | compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs | 20 | ||||
| -rw-r--r-- | compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs | 77 |
2 files changed, 90 insertions, 7 deletions
diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs index 2dfebfcb904..704b0d0bd1c 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs @@ -1024,7 +1024,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { // Can't show anything else useful, try to find similar impls. let impl_candidates = self.find_similar_impl_candidates(trait_predicate); if !self.report_similar_impl_candidates( - impl_candidates, + &impl_candidates, trait_ref, body_def_id, &mut err, @@ -1060,7 +1060,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { let impl_candidates = self.find_similar_impl_candidates(trait_pred); self.report_similar_impl_candidates( - impl_candidates, + &impl_candidates, trait_ref, body_def_id, &mut err, @@ -1068,6 +1068,13 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { ); } } + + self.maybe_suggest_convert_to_slice( + &mut err, + trait_ref, + impl_candidates.as_slice(), + span, + ); } // Changing mutability doesn't make a difference to whether we have @@ -1514,7 +1521,7 @@ trait InferCtxtPrivExt<'tcx> { fn report_similar_impl_candidates( &self, - impl_candidates: Vec<ImplCandidate<'tcx>>, + impl_candidates: &[ImplCandidate<'tcx>], trait_ref: ty::PolyTraitRef<'tcx>, body_def_id: LocalDefId, err: &mut Diagnostic, @@ -2004,7 +2011,7 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { fn report_similar_impl_candidates( &self, - impl_candidates: Vec<ImplCandidate<'tcx>>, + impl_candidates: &[ImplCandidate<'tcx>], trait_ref: ty::PolyTraitRef<'tcx>, body_def_id: LocalDefId, err: &mut Diagnostic, @@ -2113,7 +2120,8 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { // Prefer more similar candidates first, then sort lexicographically // by their normalized string representation. let mut normalized_impl_candidates_and_similarities = impl_candidates - .into_iter() + .iter() + .copied() .map(|ImplCandidate { trait_ref, similarity }| { // FIXME(compiler-errors): This should be using `NormalizeExt::normalize` let normalized = self @@ -2326,7 +2334,7 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { ); if impl_candidates.len() < 10 { self.report_similar_impl_candidates( - impl_candidates, + impl_candidates.as_slice(), trait_ref, obligation.cause.body_id, &mut err, diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs index c93c26cc04b..11567ff39dd 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs @@ -1,7 +1,7 @@ // ignore-tidy-filelength use super::{ - DefIdOrName, FindExprBySpan, Obligation, ObligationCause, ObligationCauseCode, + DefIdOrName, FindExprBySpan, ImplCandidate, Obligation, ObligationCause, ObligationCauseCode, PredicateObligation, }; @@ -382,6 +382,14 @@ pub trait TypeErrCtxtExt<'tcx> { body_id: hir::HirId, param_env: ty::ParamEnv<'tcx>, ) -> Vec<Option<(Span, (DefId, Ty<'tcx>))>>; + + fn maybe_suggest_convert_to_slice( + &self, + err: &mut Diagnostic, + trait_ref: ty::Binder<'tcx, ty::TraitRef<'tcx>>, + candidate_impls: &[ImplCandidate<'tcx>], + span: Span, + ); } fn predicate_constraint(generics: &hir::Generics<'_>, pred: ty::Predicate<'_>) -> (Span, String) { @@ -3826,6 +3834,73 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { } assocs_in_this_method } + + /// If the type that failed selection is an array or a reference to an array, + /// but the trait is implemented for slices, suggest that the user converts + /// the array into a slice. + fn maybe_suggest_convert_to_slice( + &self, + err: &mut Diagnostic, + trait_ref: ty::Binder<'tcx, ty::TraitRef<'tcx>>, + candidate_impls: &[ImplCandidate<'tcx>], + span: Span, + ) { + // Three cases where we can make a suggestion: + // 1. `[T; _]` (array of T) + // 2. `&[T; _]` (reference to array of T) + // 3. `&mut [T; _]` (mutable reference to array of T) + let (element_ty, mut mutability) = match *trait_ref.skip_binder().self_ty().kind() { + ty::Array(element_ty, _) => (element_ty, None), + + ty::Ref(_, pointee_ty, mutability) => match *pointee_ty.kind() { + ty::Array(element_ty, _) => (element_ty, Some(mutability)), + _ => return, + }, + + _ => return, + }; + + // Go through all the candidate impls to see if any of them is for + // slices of `element_ty` with `mutability`. + let mut is_slice = |candidate: Ty<'tcx>| match *candidate.kind() { + ty::RawPtr(ty::TypeAndMut { ty: t, mutbl: m }) | ty::Ref(_, t, m) => { + if matches!(*t.kind(), ty::Slice(e) if e == element_ty) + && m == mutability.unwrap_or(m) + { + // Use the candidate's mutability going forward. + mutability = Some(m); + true + } else { + false + } + } + _ => false, + }; + + // Grab the first candidate that matches, if any, and make a suggestion. + if let Some(slice_ty) = candidate_impls + .iter() + .map(|trait_ref| trait_ref.trait_ref.self_ty()) + .filter(|t| is_slice(*t)) + .next() + { + let msg = &format!("convert the array to a `{}` slice instead", slice_ty); + + if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span) { + let mut suggestions = vec![]; + if snippet.starts_with('&') { + } else if let Some(hir::Mutability::Mut) = mutability { + suggestions.push((span.shrink_to_lo(), "&mut ".into())); + } else { + suggestions.push((span.shrink_to_lo(), "&".into())); + } + suggestions.push((span.shrink_to_hi(), "[..]".into())); + err.multipart_suggestion_verbose(msg, suggestions, Applicability::MaybeIncorrect); + } else { + err.span_help(span, msg); + } + } + } } /// Add a hint to add a missing borrow or remove an unnecessary one. |
