summaryrefslogtreecommitdiffstats
path: root/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs')
-rw-r--r--compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs137
1 files changed, 109 insertions, 28 deletions
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 66d74fd05..fb75ec767 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,
};
@@ -30,7 +30,7 @@ use rustc_middle::hir::map;
use rustc_middle::ty::error::TypeError::{self, Sorts};
use rustc_middle::ty::relate::TypeRelation;
use rustc_middle::ty::{
- self, suggest_arbitrary_trait_bound, suggest_constraining_type_param, AdtKind, DefIdTree,
+ self, suggest_arbitrary_trait_bound, suggest_constraining_type_param, AdtKind,
GeneratorDiagnosticData, GeneratorInteriorTypeCause, Infer, InferTy, InternalSubsts,
IsSuggestable, ToPredicate, Ty, TyCtxt, TypeAndMut, TypeFoldable, TypeFolder,
TypeSuperFoldable, TypeVisitableExt, TypeckResults,
@@ -212,7 +212,7 @@ pub trait TypeErrCtxtExt<'tcx> {
fn extract_callable_info(
&self,
- hir_id: HirId,
+ body_id: LocalDefId,
param_env: ty::ParamEnv<'tcx>,
found: Ty<'tcx>,
) -> Option<(DefIdOrName, Ty<'tcx>, Vec<Ty<'tcx>>)>;
@@ -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) {
@@ -412,6 +420,7 @@ fn suggest_restriction<'tcx>(
) {
if hir_generics.where_clause_span.from_expansion()
|| hir_generics.where_clause_span.desugaring_kind().is_some()
+ || projection.map_or(false, |projection| tcx.opt_rpitit_info(projection.def_id).is_some())
{
return;
}
@@ -900,9 +909,8 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
trait_pred.self_ty(),
);
- let body_hir_id = self.tcx.hir().local_def_id_to_hir_id(obligation.cause.body_id);
let Some((def_id_or_name, output, inputs)) = self.extract_callable_info(
- body_hir_id,
+ obligation.cause.body_id,
obligation.param_env,
self_ty,
) else { return false; };
@@ -1104,10 +1112,9 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
/// Extracts information about a callable type for diagnostics. This is a
/// heuristic -- it doesn't necessarily mean that a type is always callable,
/// because the callable type must also be well-formed to be called.
- // FIXME(vincenzopalazzo): move the HirId to a LocalDefId
fn extract_callable_info(
&self,
- hir_id: HirId,
+ body_id: LocalDefId,
param_env: ty::ParamEnv<'tcx>,
found: Ty<'tcx>,
) -> Option<(DefIdOrName, Ty<'tcx>, Vec<Ty<'tcx>>)> {
@@ -1159,7 +1166,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
})
}
ty::Param(param) => {
- let generics = self.tcx.generics_of(hir_id.owner.to_def_id());
+ let generics = self.tcx.generics_of(body_id);
let name = if generics.count() > param.index as usize
&& let def = generics.param_at(param.index as usize, self.tcx)
&& matches!(def.kind, ty::GenericParamDefKind::Type { .. })
@@ -1349,6 +1356,31 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
Applicability::MaybeIncorrect,
);
} else {
+ let is_mut = mut_ref_self_ty_satisfies_pred || ref_inner_ty_mut;
+ let sugg_prefix = format!("&{}", if is_mut { "mut " } else { "" });
+ let sugg_msg = &format!(
+ "consider{} borrowing here",
+ if is_mut { " mutably" } else { "" }
+ );
+
+ // Issue #109436, we need to add parentheses properly for method calls
+ // for example, `foo.into()` should be `(&foo).into()`
+ if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(
+ self.tcx.sess.source_map().span_look_ahead(span, Some("."), Some(50)),
+ ) {
+ if snippet == "." {
+ err.multipart_suggestion_verbose(
+ sugg_msg,
+ vec![
+ (span.shrink_to_lo(), format!("({}", sugg_prefix)),
+ (span.shrink_to_hi(), ")".to_string()),
+ ],
+ Applicability::MaybeIncorrect,
+ );
+ return true;
+ }
+ }
+
// Issue #104961, we need to add parentheses properly for compond expressions
// for example, `x.starts_with("hi".to_string() + "you")`
// should be `x.starts_with(&("hi".to_string() + "you"))`
@@ -1365,14 +1397,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
_ => false,
};
- let is_mut = mut_ref_self_ty_satisfies_pred || ref_inner_ty_mut;
let span = if needs_parens { span } else { span.shrink_to_lo() };
- let sugg_prefix = format!("&{}", if is_mut { "mut " } else { "" });
- let sugg_msg = &format!(
- "consider{} borrowing here",
- if is_mut { " mutably" } else { "" }
- );
-
let suggestions = if !needs_parens {
vec![(span.shrink_to_lo(), format!("{}", sugg_prefix))]
} else {
@@ -2220,7 +2245,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
// - `BuiltinDerivedObligation` with a generator witness (A)
// - `BuiltinDerivedObligation` with a generator (A)
// - `BuiltinDerivedObligation` with `impl std::future::Future` (A)
- // - `BindingObligation` with `impl_send (Send requirement)
+ // - `BindingObligation` with `impl_send` (Send requirement)
//
// The first obligation in the chain is the most useful and has the generator that captured
// the type. The last generator (`outer_generator` below) has information about where the
@@ -2936,9 +2961,6 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
ObligationCauseCode::SizedYieldType => {
err.note("the yield type of a generator must have a statically known size");
}
- ObligationCauseCode::SizedBoxType => {
- err.note("the type of a box expression must have a statically known size");
- }
ObligationCauseCode::AssignmentLhsSized => {
err.note("the left-hand-side of an assignment must have a statically known size");
}
@@ -3025,8 +3047,6 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
}
};
- let identity_future = tcx.require_lang_item(LangItem::IdentityFuture, None);
-
// Don't print the tuple of capture types
'print: {
if !is_upvar_tys_infer_tuple {
@@ -3039,12 +3059,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
None => err.note(&msg),
},
ty::Alias(ty::Opaque, ty::AliasTy { def_id, .. }) => {
- // Avoid printing the future from `core::future::identity_future`, it's not helpful
- if tcx.parent(*def_id) == identity_future {
- break 'print;
- }
-
- // If the previous type is `identity_future`, this is the future generated by the body of an async function.
+ // If the previous type is async fn, this is the future generated by the body of an async function.
// Avoid printing it twice (it was already printed in the `ty::Generator` arm below).
let is_future = tcx.ty_is_opaque_future(ty);
debug!(
@@ -3826,6 +3841,72 @@ 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())
+ .find(|t| is_slice(*t))
+ {
+ 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.
@@ -3854,7 +3935,7 @@ fn hint_missing_borrow<'tcx>(
// This could be a variant constructor, for example.
let Some(fn_decl) = found_node.fn_decl() else { return; };
- let args = fn_decl.inputs.iter().map(|ty| ty);
+ let args = fn_decl.inputs.iter();
fn get_deref_type_and_refs(mut ty: Ty<'_>) -> (Ty<'_>, Vec<hir::Mutability>) {
let mut refs = vec![];