diff options
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.rs | 878 |
1 files changed, 601 insertions, 277 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 15f2ba809..6b09bc898 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs @@ -22,12 +22,13 @@ use rustc_hir::def_id::DefId; use rustc_hir::intravisit::Visitor; use rustc_hir::is_range_literal; use rustc_hir::lang_items::LangItem; -use rustc_hir::{AsyncGeneratorKind, GeneratorKind, Node}; +use rustc_hir::{CoroutineKind, CoroutineSource, Node}; use rustc_hir::{Expr, HirId}; use rustc_infer::infer::error_reporting::TypeErrCtxt; use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; use rustc_infer::infer::{DefineOpaqueTypes, InferOk, LateBoundRegionConversionTime}; use rustc_middle::hir::map; +use rustc_middle::traits::IsConstable; use rustc_middle::ty::error::TypeError::{self, Sorts}; use rustc_middle::ty::{ self, suggest_arbitrary_trait_bound, suggest_constraining_type_param, AdtKind, GenericArgs, @@ -41,43 +42,43 @@ use rustc_target::spec::abi; use std::borrow::Cow; use std::iter; -use super::InferCtxtPrivExt; use crate::infer::InferCtxtExt as _; +use crate::traits::error_reporting::type_err_ctxt_ext::InferCtxtPrivExt; use crate::traits::query::evaluate_obligation::InferCtxtExt as _; use rustc_middle::ty::print::{with_forced_trimmed_paths, with_no_trimmed_paths}; #[derive(Debug)] -pub enum GeneratorInteriorOrUpvar { +pub enum CoroutineInteriorOrUpvar { // span of interior type Interior(Span, Option<(Span, Option<Span>)>), // span of upvar Upvar(Span), } -// This type provides a uniform interface to retrieve data on generators, whether it originated from +// This type provides a uniform interface to retrieve data on coroutines, whether it originated from // the local crate being compiled or from a foreign crate. #[derive(Debug)] -struct GeneratorData<'tcx, 'a>(&'a TypeckResults<'tcx>); +struct CoroutineData<'tcx, 'a>(&'a TypeckResults<'tcx>); -impl<'tcx, 'a> GeneratorData<'tcx, 'a> { - /// Try to get information about variables captured by the generator that matches a type we are +impl<'tcx, 'a> CoroutineData<'tcx, 'a> { + /// Try to get information about variables captured by the coroutine that matches a type we are /// looking for with `ty_matches` function. We uses it to find upvar which causes a failure to /// meet an obligation fn try_get_upvar_span<F>( &self, infer_context: &InferCtxt<'tcx>, - generator_did: DefId, + coroutine_did: DefId, ty_matches: F, - ) -> Option<GeneratorInteriorOrUpvar> + ) -> Option<CoroutineInteriorOrUpvar> where F: Fn(ty::Binder<'tcx, Ty<'tcx>>) -> bool, { - infer_context.tcx.upvars_mentioned(generator_did).and_then(|upvars| { + infer_context.tcx.upvars_mentioned(coroutine_did).and_then(|upvars| { upvars.iter().find_map(|(upvar_id, upvar)| { let upvar_ty = self.0.node_type(*upvar_id); let upvar_ty = infer_context.resolve_vars_if_possible(upvar_ty); ty_matches(ty::Binder::dummy(upvar_ty)) - .then(|| GeneratorInteriorOrUpvar::Upvar(upvar.span)) + .then(|| CoroutineInteriorOrUpvar::Upvar(upvar.span)) }) }) } @@ -244,9 +245,9 @@ pub trait TypeErrCtxtExt<'tcx> { fn note_obligation_cause_for_async_await( &self, err: &mut Diagnostic, - interior_or_upvar_span: GeneratorInteriorOrUpvar, + interior_or_upvar_span: CoroutineInteriorOrUpvar, is_async: bool, - outer_generator: Option<DefId>, + outer_coroutine: Option<DefId>, trait_pred: ty::TraitPredicate<'tcx>, target_ty: Ty<'tcx>, obligation: &PredicateObligation<'tcx>, @@ -313,6 +314,18 @@ pub trait TypeErrCtxtExt<'tcx> { predicate: ty::Predicate<'tcx>, call_hir_id: HirId, ); + + fn look_for_iterator_item_mistakes( + &self, + assocs_in_this_method: &[Option<(Span, (DefId, Ty<'tcx>))>], + typeck_results: &TypeckResults<'tcx>, + type_diffs: &[TypeError<'tcx>], + param_env: ty::ParamEnv<'tcx>, + path_segment: &hir::PathSegment<'_>, + args: &[hir::Expr<'_>], + err: &mut Diagnostic, + ); + fn point_at_chain( &self, expr: &hir::Expr<'_>, @@ -321,6 +334,7 @@ pub trait TypeErrCtxtExt<'tcx> { param_env: ty::ParamEnv<'tcx>, err: &mut Diagnostic, ); + fn probe_assoc_types_at_expr( &self, type_diffs: &[TypeError<'tcx>], @@ -364,7 +378,7 @@ fn predicate_constraint(generics: &hir::Generics<'_>, pred: ty::Predicate<'_>) - /// Type parameter needs more bounds. The trivial case is `T` `where T: Bound`, but /// it can also be an `impl Trait` param that needs to be decomposed to a type /// param for cleaner code. -fn suggest_restriction<'tcx>( +pub fn suggest_restriction<'tcx>( tcx: TyCtxt<'tcx>, item_id: LocalDefId, hir_generics: &hir::Generics<'tcx>, @@ -884,7 +898,8 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { return false; } - if let ty::PredicateKind::Clause(ty::ClauseKind::Trait(trait_pred)) = obligation.predicate.kind().skip_binder() + if let ty::PredicateKind::Clause(ty::ClauseKind::Trait(trait_pred)) = + obligation.predicate.kind().skip_binder() && Some(trait_pred.def_id()) == self.tcx.lang_items().sized_trait() { // Don't suggest calling to turn an unsized type into a sized type @@ -1156,15 +1171,15 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { && Some(proj.projection_ty.def_id) == self.tcx.lang_items().fn_once_output() // args tuple will always be args[1] && let ty::Tuple(args) = proj.projection_ty.args.type_at(1).kind() - { - Some(( - DefIdOrName::DefId(def_id), - pred.kind().rebind(proj.term.ty().unwrap()), - pred.kind().rebind(args.as_slice()), - )) - } else { - None - } + { + Some(( + DefIdOrName::DefId(def_id), + pred.kind().rebind(proj.term.ty().unwrap()), + pred.kind().rebind(args.as_slice()), + )) + } else { + None + } }, ) } @@ -1174,43 +1189,43 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { && Some(proj.def_id) == self.tcx.lang_items().fn_once_output() // for existential projection, args are shifted over by 1 && let ty::Tuple(args) = proj.args.type_at(0).kind() - { - Some(( - DefIdOrName::Name("trait object"), - pred.rebind(proj.term.ty().unwrap()), - pred.rebind(args.as_slice()), - )) - } else { - None - } + { + Some(( + DefIdOrName::Name("trait object"), + pred.rebind(proj.term.ty().unwrap()), + pred.rebind(args.as_slice()), + )) + } else { + None + } }) } ty::Param(param) => { 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 { .. }) - && def.name == param.name - { - DefIdOrName::DefId(def.def_id) - } else { - DefIdOrName::Name("type parameter") - }; + && let def = generics.param_at(param.index as usize, self.tcx) + && matches!(def.kind, ty::GenericParamDefKind::Type { .. }) + && def.name == param.name + { + DefIdOrName::DefId(def.def_id) + } else { + DefIdOrName::Name("type parameter") + }; param_env.caller_bounds().iter().find_map(|pred| { if let ty::ClauseKind::Projection(proj) = pred.kind().skip_binder() && Some(proj.projection_ty.def_id) == self.tcx.lang_items().fn_once_output() && proj.projection_ty.self_ty() == found // args tuple will always be args[1] && let ty::Tuple(args) = proj.projection_ty.args.type_at(1).kind() - { - Some(( - name, - pred.kind().rebind(proj.term.ty().unwrap()), - pred.kind().rebind(args.as_slice()), - )) - } else { - None - } + { + Some(( + name, + pred.kind().rebind(proj.term.ty().unwrap()), + pred.kind().rebind(args.as_slice()), + )) + } else { + None + } }) } _ => None, @@ -1316,7 +1331,8 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { let mut_ref_self_ty_satisfies_pred = mk_result(trait_pred_and_mut_ref); let (ref_inner_ty_satisfies_pred, ref_inner_ty_mut) = - if let ObligationCauseCode::ItemObligation(_) | ObligationCauseCode::ExprItemObligation(..) = obligation.cause.code() + if let ObligationCauseCode::ItemObligation(_) + | ObligationCauseCode::ExprItemObligation(..) = obligation.cause.code() && let ty::Ref(_, ty, mutability) = old_pred.self_ty().skip_binder().kind() { ( @@ -1618,7 +1634,8 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { fn suggest_remove_await(&self, obligation: &PredicateObligation<'tcx>, err: &mut Diagnostic) { let hir = self.tcx.hir(); - if let ObligationCauseCode::AwaitableExpr(Some(hir_id)) = obligation.cause.code().peel_derives() + if let ObligationCauseCode::AwaitableExpr(Some(hir_id)) = + obligation.cause.code().peel_derives() && let hir::Node::Expr(expr) = hir.get(*hir_id) { // FIXME: use `obligation.predicate.kind()...trait_ref.self_ty()` to see if we have `()` @@ -1628,9 +1645,10 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { // use nth(1) to skip one layer of desugaring from `IntoIter::into_iter` if let Some((_, hir::Node::Expr(await_expr))) = hir.parent_iter(*hir_id).nth(1) - && let Some(expr_span) = expr.span.find_ancestor_inside(await_expr.span) + && let Some(expr_span) = expr.span.find_ancestor_inside_same_ctxt(await_expr.span) { - let removal_span = self.tcx + let removal_span = self + .tcx .sess .source_map() .span_extend_while(expr_span, char::is_whitespace) @@ -1654,30 +1672,28 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { err.span_label(*span, format!("this call returns `{}`", pred.self_ty())); } if let Some(typeck_results) = &self.typeck_results - && let ty = typeck_results.expr_ty_adjusted(base) - && let ty::FnDef(def_id, _args) = ty.kind() - && let Some(hir::Node::Item(hir::Item { ident, span, vis_span, .. })) = - hir.get_if_local(*def_id) - { - let msg = format!( - "alternatively, consider making `fn {ident}` asynchronous" + && let ty = typeck_results.expr_ty_adjusted(base) + && let ty::FnDef(def_id, _args) = ty.kind() + && let Some(hir::Node::Item(hir::Item { ident, span, vis_span, .. })) = + hir.get_if_local(*def_id) + { + let msg = format!("alternatively, consider making `fn {ident}` asynchronous"); + if vis_span.is_empty() { + err.span_suggestion_verbose( + span.shrink_to_lo(), + msg, + "async ", + Applicability::MaybeIncorrect, + ); + } else { + err.span_suggestion_verbose( + vis_span.shrink_to_hi(), + msg, + " async", + Applicability::MaybeIncorrect, ); - if vis_span.is_empty() { - err.span_suggestion_verbose( - span.shrink_to_lo(), - msg, - "async ", - Applicability::MaybeIncorrect, - ); - } else { - err.span_suggestion_verbose( - vis_span.shrink_to_hi(), - msg, - " async", - Applicability::MaybeIncorrect, - ); - } } + } } } } @@ -1791,13 +1807,13 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { "this expression has type `{}`, which implements `{}`", ty, trait_pred.print_modifiers_and_trait_path() - ) + ), ); err.span_suggestion( self.tcx.sess.source_map().end_point(stmt.span), "remove this semicolon", "", - Applicability::MachineApplicable + Applicability::MachineApplicable, ); return true; } @@ -1856,14 +1872,18 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { let mut sugg = vec![(span.shrink_to_lo(), "Box<".to_string()), (span.shrink_to_hi(), ">".to_string())]; sugg.extend(visitor.returns.into_iter().flat_map(|expr| { - let span = expr.span.find_ancestor_in_same_ctxt(obligation.cause.span).unwrap_or(expr.span); + let span = + expr.span.find_ancestor_in_same_ctxt(obligation.cause.span).unwrap_or(expr.span); if !span.can_be_used_for_suggestions() { vec![] } else if let hir::ExprKind::Call(path, ..) = expr.kind && let hir::ExprKind::Path(hir::QPath::TypeRelative(ty, method)) = path.kind && method.ident.name == sym::new && let hir::TyKind::Path(hir::QPath::Resolved(.., box_path)) = ty.kind - && box_path.res.opt_def_id().is_some_and(|def_id| Some(def_id) == self.tcx.lang_items().owned_box()) + && box_path + .res + .opt_def_id() + .is_some_and(|def_id| Some(def_id) == self.tcx.lang_items().owned_box()) { // Don't box `Box::new` vec![] @@ -1963,7 +1983,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { let argument_kind = match expected.skip_binder().self_ty().kind() { ty::Closure(..) => "closure", - ty::Generator(..) => "generator", + ty::Coroutine(..) => "coroutine", _ => "function", }; let mut err = struct_span_err!( @@ -2013,15 +2033,13 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { { let expected_self = self.tcx.anonymize_bound_vars(pred.kind().rebind(trait_pred.self_ty())); - let expected_args = self - .tcx - .anonymize_bound_vars(pred.kind().rebind(trait_pred.trait_ref.args)); + let expected_args = + self.tcx.anonymize_bound_vars(pred.kind().rebind(trait_pred.trait_ref.args)); // Find another predicate whose self-type is equal to the expected self type, // but whose args don't match. - let other_pred = predicates.into_iter() - .enumerate() - .find(|(other_idx, (pred, _))| match pred.kind().skip_binder() { + let other_pred = predicates.into_iter().enumerate().find(|(other_idx, (pred, _))| { + match pred.kind().skip_binder() { ty::ClauseKind::Trait(trait_pred) if self.tcx.is_fn_trait(trait_pred.def_id()) && other_idx != idx @@ -2040,7 +2058,8 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { true } _ => false, - }); + } + }); // If we found one, then it's very likely the cause of the error. if let Some((_, (_, other_pred_span))) = other_pred { err.span_note( @@ -2126,33 +2145,33 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { let hir = self.tcx.hir(); // Attempt to detect an async-await error by looking at the obligation causes, looking - // for a generator to be present. + // for a coroutine to be present. // // When a future does not implement a trait because of a captured type in one of the - // generators somewhere in the call stack, then the result is a chain of obligations. + // coroutines somewhere in the call stack, then the result is a chain of obligations. // // Given an `async fn` A that calls an `async fn` B which captures a non-send type and that // future is passed as an argument to a function C which requires a `Send` type, then the // chain looks something like this: // - // - `BuiltinDerivedObligation` with a generator witness (B) - // - `BuiltinDerivedObligation` with a generator (B) + // - `BuiltinDerivedObligation` with a coroutine witness (B) + // - `BuiltinDerivedObligation` with a coroutine (B) // - `BuiltinDerivedObligation` with `impl std::future::Future` (B) - // - `BuiltinDerivedObligation` with a generator witness (A) - // - `BuiltinDerivedObligation` with a generator (A) + // - `BuiltinDerivedObligation` with a coroutine witness (A) + // - `BuiltinDerivedObligation` with a coroutine (A) // - `BuiltinDerivedObligation` with `impl std::future::Future` (A) // - `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 - // bound was introduced. At least one generator should be present for this diagnostic to be + // The first obligation in the chain is the most useful and has the coroutine that captured + // the type. The last coroutine (`outer_coroutine` below) has information about where the + // bound was introduced. At least one coroutine should be present for this diagnostic to be // modified. let (mut trait_ref, mut target_ty) = match obligation.predicate.kind().skip_binder() { ty::PredicateKind::Clause(ty::ClauseKind::Trait(p)) => (Some(p), Some(p.self_ty())), _ => (None, None), }; - let mut generator = None; - let mut outer_generator = None; + let mut coroutine = None; + let mut outer_coroutine = None; let mut next_code = Some(obligation.cause.code()); let mut seen_upvar_tys_infer_tuple = false; @@ -2172,18 +2191,18 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { ); match *ty.kind() { - ty::Generator(did, ..) | ty::GeneratorWitness(did, _) => { - generator = generator.or(Some(did)); - outer_generator = Some(did); + ty::Coroutine(did, ..) | ty::CoroutineWitness(did, _) => { + coroutine = coroutine.or(Some(did)); + outer_coroutine = Some(did); } ty::Tuple(_) if !seen_upvar_tys_infer_tuple => { // By introducing a tuple of upvar types into the chain of obligations - // of a generator, the first non-generator item is now the tuple itself, + // of a coroutine, the first non-coroutine item is now the tuple itself, // we shall ignore this. seen_upvar_tys_infer_tuple = true; } - _ if generator.is_none() => { + _ if coroutine.is_none() => { trait_ref = Some(cause.derived.parent_trait_pred.skip_binder()); target_ty = Some(ty); } @@ -2201,18 +2220,18 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { ); match *ty.kind() { - ty::Generator(did, ..) | ty::GeneratorWitness(did, ..) => { - generator = generator.or(Some(did)); - outer_generator = Some(did); + ty::Coroutine(did, ..) | ty::CoroutineWitness(did, ..) => { + coroutine = coroutine.or(Some(did)); + outer_coroutine = Some(did); } ty::Tuple(_) if !seen_upvar_tys_infer_tuple => { // By introducing a tuple of upvar types into the chain of obligations - // of a generator, the first non-generator item is now the tuple itself, + // of a coroutine, the first non-coroutine item is now the tuple itself, // we shall ignore this. seen_upvar_tys_infer_tuple = true; } - _ if generator.is_none() => { + _ if coroutine.is_none() => { trait_ref = Some(derived_obligation.parent_trait_pred.skip_binder()); target_ty = Some(ty); } @@ -2225,48 +2244,48 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { } } - // Only continue if a generator was found. - debug!(?generator, ?trait_ref, ?target_ty); - let (Some(generator_did), Some(trait_ref), Some(target_ty)) = - (generator, trait_ref, target_ty) + // Only continue if a coroutine was found. + debug!(?coroutine, ?trait_ref, ?target_ty); + let (Some(coroutine_did), Some(trait_ref), Some(target_ty)) = + (coroutine, trait_ref, target_ty) else { return false; }; - let span = self.tcx.def_span(generator_did); + let span = self.tcx.def_span(coroutine_did); - let generator_did_root = self.tcx.typeck_root_def_id(generator_did); + let coroutine_did_root = self.tcx.typeck_root_def_id(coroutine_did); debug!( - ?generator_did, - ?generator_did_root, + ?coroutine_did, + ?coroutine_did_root, typeck_results.hir_owner = ?self.typeck_results.as_ref().map(|t| t.hir_owner), ?span, ); - let generator_body = generator_did + let coroutine_body = coroutine_did .as_local() .and_then(|def_id| hir.maybe_body_owned_by(def_id)) .map(|body_id| hir.body(body_id)); let mut visitor = AwaitsVisitor::default(); - if let Some(body) = generator_body { + if let Some(body) = coroutine_body { visitor.visit_body(body); } debug!(awaits = ?visitor.awaits); - // Look for a type inside the generator interior that matches the target type to get + // Look for a type inside the coroutine interior that matches the target type to get // a span. let target_ty_erased = self.tcx.erase_regions(target_ty); let ty_matches = |ty| -> bool { // Careful: the regions for types that appear in the - // generator interior are not generally known, so we + // coroutine interior are not generally known, so we // want to erase them when comparing (and anyway, // `Send` and other bounds are generally unaffected by // the choice of region). When erasing regions, we // also have to erase late-bound regions. This is - // because the types that appear in the generator + // because the types that appear in the coroutine // interior generally contain "bound regions" to // represent regions that are part of the suspended - // generator frame. Bound regions are preserved by + // coroutine frame. Bound regions are preserved by // `erase_regions` and so we must also call // `erase_late_bound_regions`. let ty_erased = self.tcx.erase_late_bound_regions(ty); @@ -2276,44 +2295,44 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { eq }; - // Get the typeck results from the infcx if the generator is the function we are currently + // Get the typeck results from the infcx if the coroutine is the function we are currently // type-checking; otherwise, get them by performing a query. This is needed to avoid - // cycles. If we can't use resolved types because the generator comes from another crate, + // cycles. If we can't use resolved types because the coroutine comes from another crate, // we still provide a targeted error but without all the relevant spans. - let generator_data = match &self.typeck_results { - Some(t) if t.hir_owner.to_def_id() == generator_did_root => GeneratorData(&t), - _ if generator_did.is_local() => { - GeneratorData(self.tcx.typeck(generator_did.expect_local())) + let coroutine_data = match &self.typeck_results { + Some(t) if t.hir_owner.to_def_id() == coroutine_did_root => CoroutineData(&t), + _ if coroutine_did.is_local() => { + CoroutineData(self.tcx.typeck(coroutine_did.expect_local())) } _ => return false, }; - let generator_within_in_progress_typeck = match &self.typeck_results { - Some(t) => t.hir_owner.to_def_id() == generator_did_root, + let coroutine_within_in_progress_typeck = match &self.typeck_results { + Some(t) => t.hir_owner.to_def_id() == coroutine_did_root, _ => false, }; let mut interior_or_upvar_span = None; - let from_awaited_ty = generator_data.get_from_await_ty(visitor, hir, ty_matches); + let from_awaited_ty = coroutine_data.get_from_await_ty(visitor, hir, ty_matches); debug!(?from_awaited_ty); // Avoid disclosing internal information to downstream crates. - if generator_did.is_local() + if coroutine_did.is_local() // Try to avoid cycles. - && !generator_within_in_progress_typeck - && let Some(generator_info) = self.tcx.mir_generator_witnesses(generator_did) + && !coroutine_within_in_progress_typeck + && let Some(coroutine_info) = self.tcx.mir_coroutine_witnesses(coroutine_did) { - debug!(?generator_info); + debug!(?coroutine_info); 'find_source: for (variant, source_info) in - generator_info.variant_fields.iter().zip(&generator_info.variant_source_info) + coroutine_info.variant_fields.iter().zip(&coroutine_info.variant_source_info) { debug!(?variant); for &local in variant { - let decl = &generator_info.field_tys[local]; + let decl = &coroutine_info.field_tys[local]; debug!(?decl); if ty_matches(ty::Binder::dummy(decl.ty)) && !decl.ignore_for_traits { - interior_or_upvar_span = Some(GeneratorInteriorOrUpvar::Interior( + interior_or_upvar_span = Some(CoroutineInteriorOrUpvar::Interior( decl.source_info.span, Some((source_info.span, from_awaited_ty)), )); @@ -2325,21 +2344,21 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { if interior_or_upvar_span.is_none() { interior_or_upvar_span = - generator_data.try_get_upvar_span(&self, generator_did, ty_matches); + coroutine_data.try_get_upvar_span(&self, coroutine_did, ty_matches); } - if interior_or_upvar_span.is_none() && !generator_did.is_local() { - interior_or_upvar_span = Some(GeneratorInteriorOrUpvar::Interior(span, None)); + if interior_or_upvar_span.is_none() && !coroutine_did.is_local() { + interior_or_upvar_span = Some(CoroutineInteriorOrUpvar::Interior(span, None)); } debug!(?interior_or_upvar_span); if let Some(interior_or_upvar_span) = interior_or_upvar_span { - let is_async = self.tcx.generator_is_async(generator_did); + let is_async = self.tcx.coroutine_is_async(coroutine_did); self.note_obligation_cause_for_async_await( err, interior_or_upvar_span, is_async, - outer_generator, + outer_coroutine, trait_ref, target_ty, obligation, @@ -2357,9 +2376,9 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { fn note_obligation_cause_for_async_await( &self, err: &mut Diagnostic, - interior_or_upvar_span: GeneratorInteriorOrUpvar, + interior_or_upvar_span: CoroutineInteriorOrUpvar, is_async: bool, - outer_generator: Option<DefId>, + outer_coroutine: Option<DefId>, trait_pred: ty::TraitPredicate<'tcx>, target_ty: Ty<'tcx>, obligation: &PredicateObligation<'tcx>, @@ -2369,7 +2388,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { let (await_or_yield, an_await_or_yield) = if is_async { ("await", "an await") } else { ("yield", "a yield") }; - let future_or_generator = if is_async { "future" } else { "generator" }; + let future_or_coroutine = if is_async { "future" } else { "coroutine" }; // Special case the primary error message when send or sync is the trait that was // not implemented. @@ -2382,34 +2401,49 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { err.clear_code(); err.set_primary_message(format!( - "{future_or_generator} cannot be {trait_verb} between threads safely" + "{future_or_coroutine} cannot be {trait_verb} between threads safely" )); let original_span = err.span.primary_span().unwrap(); let mut span = MultiSpan::from_span(original_span); - let message = outer_generator - .and_then(|generator_did| { - Some(match self.tcx.generator_kind(generator_did).unwrap() { - GeneratorKind::Gen => format!("generator is not {trait_name}"), - GeneratorKind::Async(AsyncGeneratorKind::Fn) => self + let message = outer_coroutine + .and_then(|coroutine_did| { + Some(match self.tcx.coroutine_kind(coroutine_did).unwrap() { + CoroutineKind::Coroutine => format!("coroutine is not {trait_name}"), + CoroutineKind::Async(CoroutineSource::Fn) => self .tcx - .parent(generator_did) + .parent(coroutine_did) .as_local() .map(|parent_did| hir.local_def_id_to_hir_id(parent_did)) .and_then(|parent_hir_id| hir.opt_name(parent_hir_id)) .map(|name| { format!("future returned by `{name}` is not {trait_name}") })?, - GeneratorKind::Async(AsyncGeneratorKind::Block) => { + CoroutineKind::Async(CoroutineSource::Block) => { format!("future created by async block is not {trait_name}") } - GeneratorKind::Async(AsyncGeneratorKind::Closure) => { + CoroutineKind::Async(CoroutineSource::Closure) => { format!("future created by async closure is not {trait_name}") } + CoroutineKind::Gen(CoroutineSource::Fn) => self + .tcx + .parent(coroutine_did) + .as_local() + .map(|parent_did| hir.local_def_id_to_hir_id(parent_did)) + .and_then(|parent_hir_id| hir.opt_name(parent_hir_id)) + .map(|name| { + format!("iterator returned by `{name}` is not {trait_name}") + })?, + CoroutineKind::Gen(CoroutineSource::Block) => { + format!("iterator created by gen block is not {trait_name}") + } + CoroutineKind::Gen(CoroutineSource::Closure) => { + format!("iterator created by gen closure is not {trait_name}") + } }) }) - .unwrap_or_else(|| format!("{future_or_generator} is not {trait_name}")); + .unwrap_or_else(|| format!("{future_or_coroutine} is not {trait_name}")); span.push_span_label(original_span, message); err.set_span(span); @@ -2453,11 +2487,11 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { ); err.span_note( span, - format!("{future_or_generator} {trait_explanation} as this value is used across {an_await_or_yield}"), + format!("{future_or_coroutine} {trait_explanation} as this value is used across {an_await_or_yield}"), ); }; match interior_or_upvar_span { - GeneratorInteriorOrUpvar::Interior(interior_span, interior_extra_info) => { + CoroutineInteriorOrUpvar::Interior(interior_span, interior_extra_info) => { if let Some((yield_span, from_awaited_ty)) = interior_extra_info { if let Some(await_span) = from_awaited_ty { // The type causing this obligation is one being awaited at await_span. @@ -2480,7 +2514,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { } } } - GeneratorInteriorOrUpvar::Upvar(upvar_span) => { + CoroutineInteriorOrUpvar::Upvar(upvar_span) => { // `Some((ref_ty, is_mut))` if `target_ty` is `&T` or `&mut T` and fails to impl `Send` let non_send = match target_ty.kind() { ty::Ref(_, ref_ty, mutability) => match self.evaluate_obligation(&obligation) { @@ -2647,9 +2681,32 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { // Check for foreign traits being reachable. self.tcx.visible_parent_map(()).get(&def_id).is_some() }; - if let DefKind::Trait = tcx.def_kind(item_def_id) && !visible_item { - // FIXME(estebank): extend this to search for all the types that do - // implement this trait and list them. + if Some(def_id) == self.tcx.lang_items().sized_trait() + && let Some(hir::Node::TraitItem(hir::TraitItem { + ident, + kind: hir::TraitItemKind::Type(bounds, None), + .. + })) = tcx.hir().get_if_local(item_def_id) + // Do not suggest relaxing if there is an explicit `Sized` obligation. + && !bounds.iter() + .filter_map(|bound| bound.trait_ref()) + .any(|tr| tr.trait_def_id() == self.tcx.lang_items().sized_trait()) + { + let (span, separator) = if let [.., last] = bounds { + (last.span().shrink_to_hi(), " +") + } else { + (ident.span.shrink_to_hi(), ":") + }; + err.span_suggestion_verbose( + span, + "consider relaxing the implicit `Sized` restriction", + format!("{separator} ?Sized"), + Applicability::MachineApplicable, + ); + } + if let DefKind::Trait = tcx.def_kind(item_def_id) + && !visible_item + { err.note(format!( "`{short_item_name}` is a \"sealed trait\", because to implement \ it you also need to implement `{}`, which is not accessible; \ @@ -2657,6 +2714,34 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { types that already implement it", with_no_trimmed_paths!(tcx.def_path_str(def_id)), )); + let impls_of = tcx.trait_impls_of(def_id); + let impls = impls_of + .non_blanket_impls() + .values() + .flatten() + .chain(impls_of.blanket_impls().iter()) + .collect::<Vec<_>>(); + if !impls.is_empty() { + let len = impls.len(); + let mut types = impls.iter() + .map(|t| with_no_trimmed_paths!(format!( + " {}", + tcx.type_of(*t).instantiate_identity(), + ))) + .collect::<Vec<_>>(); + let post = if types.len() > 9 { + types.truncate(8); + format!("\nand {} others", len - 8) + } else { + String::new() + }; + err.help(format!( + "the following type{} implement{} the trait:\n{}{post}", + pluralize!(len), + if len == 1 { "s" } else { "" }, + types.join("\n"), + )); + } } } } else { @@ -2684,20 +2769,30 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { )); } } - ObligationCauseCode::RepeatElementCopy { is_const_fn } => { + ObligationCauseCode::RepeatElementCopy { is_constable, elt_type, elt_span, elt_stmt_span } => { err.note( "the `Copy` trait is required because this value will be copied for each element of the array", ); - - if is_const_fn { - err.help( - "consider creating a new `const` item and initializing it with the result \ - of the function call to be used in the repeat position, like \ - `const VAL: Type = const_fn();` and `let x = [VAL; 42];`", - ); + let value_kind = match is_constable { + IsConstable::Fn => Some("the result of the function call"), + IsConstable::Ctor => Some("the result of the constructor"), + _ => None + }; + let sm = tcx.sess.source_map(); + if let Some(value_kind) = value_kind && + let Ok(snip) = sm.span_to_snippet(elt_span) + { + let help_msg = format!( + "consider creating a new `const` item and initializing it with {value_kind} \ + to be used in the repeat position"); + let indentation = sm.indentation_before(elt_stmt_span).unwrap_or_default(); + err.multipart_suggestion(help_msg, vec![ + (elt_stmt_span.shrink_to_lo(), format!("const ARRAY_REPEAT_VALUE: {elt_type} = {snip};\n{indentation}")), + (elt_span, "ARRAY_REPEAT_VALUE".to_string()) + ], Applicability::MachineApplicable); } - if self.tcx.sess.is_nightly_build() && is_const_fn { + if self.tcx.sess.is_nightly_build() && matches!(is_constable, IsConstable::Fn|IsConstable::Ctor) { err.help( "create an inline `const` block, see RFC #2920 \ <https://github.com/rust-lang/rfcs/pull/2920> for more information", @@ -2754,7 +2849,8 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { && let ty::ClauseKind::Trait(trait_pred) = clause && let ty::Dynamic(..) = trait_pred.self_ty().kind() { - let span = if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span) + let span = if let Ok(snippet) = + self.tcx.sess.source_map().span_to_snippet(span) && snippet.starts_with("dyn ") { let pos = snippet.len() - snippet[3..].trim_start().len(); @@ -2789,7 +2885,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { err.note("the return type of a function must have a statically known size"); } ObligationCauseCode::SizedYieldType => { - err.note("the yield type of a generator must have a statically known size"); + err.note("the yield type of a coroutine must have a statically known size"); } ObligationCauseCode::AssignmentLhsSized => { err.note("the left-hand-side of an assignment must have a statically known size"); @@ -2845,22 +2941,28 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { err.note("all inline asm arguments must have a statically known size"); } ObligationCauseCode::SizedClosureCapture(closure_def_id) => { - err.note("all values captured by value by a closure must have a statically known size"); - let hir::ExprKind::Closure(closure) = self.tcx.hir().get_by_def_id(closure_def_id).expect_expr().kind else { + err.note( + "all values captured by value by a closure must have a statically known size", + ); + let hir::ExprKind::Closure(closure) = + self.tcx.hir().get_by_def_id(closure_def_id).expect_expr().kind + else { bug!("expected closure in SizedClosureCapture obligation"); }; - if let hir::CaptureBy::Value = closure.capture_clause + if let hir::CaptureBy::Value { .. } = closure.capture_clause && let Some(span) = closure.fn_arg_span { err.span_label(span, "this closure captures all values by move"); } } - ObligationCauseCode::SizedGeneratorInterior(generator_def_id) => { - let what = match self.tcx.generator_kind(generator_def_id) { - None | Some(hir::GeneratorKind::Gen) => "yield", - Some(hir::GeneratorKind::Async(..)) => "await", + ObligationCauseCode::SizedCoroutineInterior(coroutine_def_id) => { + let what = match self.tcx.coroutine_kind(coroutine_def_id) { + None | Some(hir::CoroutineKind::Coroutine) | Some(hir::CoroutineKind::Gen(_)) => "yield", + Some(hir::CoroutineKind::Async(..)) => "await", }; - err.note(format!("all values live across `{what}` must have a statically known size")); + err.note(format!( + "all values live across `{what}` must have a statically known size" + )); } ObligationCauseCode::ConstPatternStructural => { err.note("constants used for pattern-matching must derive `PartialEq` and `Eq`"); @@ -2878,7 +2980,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { return; } - // If the obligation for a tuple is set directly by a Generator or Closure, + // If the obligation for a tuple is set directly by a Coroutine or Closure, // then the tuple must be the one containing capture types. let is_upvar_tys_infer_tuple = if !matches!(ty.kind(), ty::Tuple(..)) { false @@ -2888,7 +2990,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { let parent_trait_ref = self.resolve_vars_if_possible(data.parent_trait_pred); let nested_ty = parent_trait_ref.skip_binder().self_ty(); - matches!(nested_ty.kind(), ty::Generator(..)) + matches!(nested_ty.kind(), ty::Coroutine(..)) || matches!(nested_ty.kind(), ty::Closure(..)) } else { false @@ -2908,7 +3010,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { }, ty::Alias(ty::Opaque, ty::AliasTy { def_id, .. }) => { // 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). + // Avoid printing it twice (it was already printed in the `ty::Coroutine` arm below). let is_future = tcx.ty_is_opaque_future(ty); debug!( ?obligated_types, @@ -2917,8 +3019,8 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { ); if is_future && obligated_types.last().is_some_and(|ty| match ty.kind() { - ty::Generator(last_def_id, ..) => { - tcx.generator_is_async(*last_def_id) + ty::Coroutine(last_def_id, ..) => { + tcx.coroutine_is_async(*last_def_id) } _ => false, }) @@ -2927,7 +3029,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { } err.span_note(self.tcx.def_span(def_id), msg) } - ty::GeneratorWitness(def_id, args) => { + ty::CoroutineWitness(def_id, args) => { use std::fmt::Write; // FIXME: this is kind of an unusual format for rustc, can we make it more clear? @@ -2935,21 +3037,21 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { // FIXME: only print types which don't meet the trait requirement let mut msg = "required because it captures the following types: ".to_owned(); - for bty in tcx.generator_hidden_types(*def_id) { + for bty in tcx.coroutine_hidden_types(*def_id) { let ty = bty.instantiate(tcx, args); write!(msg, "`{ty}`, ").unwrap(); } err.note(msg.trim_end_matches(", ").to_string()) } - ty::Generator(def_id, _, _) => { + ty::Coroutine(def_id, _, _) => { let sp = self.tcx.def_span(def_id); - // Special-case this to say "async block" instead of `[static generator]`. - let kind = tcx.generator_kind(def_id).unwrap().descr(); + // Special-case this to say "async block" instead of `[static coroutine]`. + let kind = tcx.coroutine_kind(def_id).unwrap(); err.span_note( sp, with_forced_trimmed_paths!(format!( - "required because it's used within this {kind}", + "required because it's used within this {kind:#}", )), ) } @@ -3244,7 +3346,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { ) { if let Some(body_id) = self.tcx.hir().maybe_body_owned_by(obligation.cause.body_id) { let body = self.tcx.hir().body(body_id); - if let Some(hir::GeneratorKind::Async(_)) = body.generator_kind { + if let Some(hir::CoroutineKind::Async(_)) = body.coroutine_kind { let future_trait = self.tcx.require_lang_item(LangItem::Future, None); let self_ty = self.resolve_vars_if_possible(trait_pred.self_ty()); @@ -3385,15 +3487,17 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { trait_pred: ty::PolyTraitPredicate<'tcx>, ) { if let ObligationCauseCode::ImplDerivedObligation(_) = obligation.cause.code() - && self.tcx.is_diagnostic_item(sym::SliceIndex, trait_pred.skip_binder().trait_ref.def_id) + && self + .tcx + .is_diagnostic_item(sym::SliceIndex, trait_pred.skip_binder().trait_ref.def_id) && let ty::Slice(_) = trait_pred.skip_binder().trait_ref.args.type_at(1).kind() && let ty::Ref(_, inner_ty, _) = trait_pred.skip_binder().self_ty().kind() && let ty::Uint(ty::UintTy::Usize) = inner_ty.kind() { err.span_suggestion_verbose( obligation.cause.span.shrink_to_lo(), - "dereference this index", - '*', + "dereference this index", + '*', Applicability::MachineApplicable, ); } @@ -3413,10 +3517,11 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { if let Some(Node::Expr(expr)) = hir.find(arg_hir_id) && let Some(typeck_results) = &self.typeck_results { - if let hir::Expr { kind: hir::ExprKind::Block(..), .. } = expr { - let expr = expr.peel_blocks(); - let ty = typeck_results.expr_ty_adjusted_opt(expr).unwrap_or(Ty::new_misc_error(tcx,)); - let span = expr.span; + if let hir::Expr { kind: hir::ExprKind::Block(block, _), .. } = expr { + let inner_expr = expr.peel_blocks(); + let ty = typeck_results.expr_ty_adjusted_opt(inner_expr) + .unwrap_or(Ty::new_misc_error(tcx)); + let span = inner_expr.span; if Some(span) != err.span.primary_span() { err.span_label( span, @@ -3427,6 +3532,49 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { format!("this tail expression is of type `{ty}`") }, ); + if let ty::PredicateKind::Clause(clause) = failed_pred.kind().skip_binder() + && let ty::ClauseKind::Trait(pred) = clause + && [ + tcx.lang_items().fn_once_trait(), + tcx.lang_items().fn_mut_trait(), + tcx.lang_items().fn_trait(), + ].contains(&Some(pred.def_id())) + { + if let [stmt, ..] = block.stmts + && let hir::StmtKind::Semi(value) = stmt.kind + && let hir::ExprKind::Closure(hir::Closure { + body, + fn_decl_span, + .. + }) = value.kind + && let body = hir.body(*body) + && !matches!(body.value.kind, hir::ExprKind::Block(..)) + { + // Check if the failed predicate was an expectation of a closure type + // and if there might have been a `{ |args|` typo instead of `|args| {`. + err.multipart_suggestion( + "you might have meant to open the closure body instead of placing \ + a closure within a block", + vec![ + (expr.span.with_hi(value.span.lo()), String::new()), + (fn_decl_span.shrink_to_hi(), " {".to_string()), + ], + Applicability::MaybeIncorrect, + ); + } else { + // Maybe the bare block was meant to be a closure. + err.span_suggestion_verbose( + expr.span.shrink_to_lo(), + "you might have meant to create the closure instead of a block", + format!( + "|{}| ", + (0..pred.trait_ref.args.len() - 1).map(|_| "_") + .collect::<Vec<_>>() + .join(", ")), + Applicability::MaybeIncorrect, + ); + } + } } } @@ -3437,7 +3585,8 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { let mut type_diffs = vec![]; if let ObligationCauseCode::ExprBindingObligation(def_id, _, _, idx) = parent_code && let Some(node_args) = typeck_results.node_args_opt(call_hir_id) - && let where_clauses = self.tcx.predicates_of(def_id).instantiate(self.tcx, node_args) + && let where_clauses = + self.tcx.predicates_of(def_id).instantiate(self.tcx, node_args) && let Some(where_pred) = where_clauses.predicates.get(*idx) { if let Some(where_pred) = where_pred.as_trait_clause() @@ -3447,32 +3596,34 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { let failed_pred = self.instantiate_binder_with_fresh_vars( expr.span, LateBoundRegionConversionTime::FnCall, - failed_pred + failed_pred, ); - let zipped = - iter::zip(where_pred.trait_ref.args, failed_pred.trait_ref.args); + let zipped = iter::zip(where_pred.trait_ref.args, failed_pred.trait_ref.args); for (expected, actual) in zipped { self.probe(|_| { - match self - .at(&ObligationCause::misc(expr.span, body_id), param_env) - .eq(DefineOpaqueTypes::No, expected, actual) - { + match self.at(&ObligationCause::misc(expr.span, body_id), param_env).eq( + DefineOpaqueTypes::No, + expected, + actual, + ) { Ok(_) => (), // We ignore nested obligations here for now. Err(err) => type_diffs.push(err), } }) - }; + } } else if let Some(where_pred) = where_pred.as_projection_clause() && let Some(failed_pred) = failed_pred.to_opt_poly_projection_pred() && let Some(found) = failed_pred.skip_binder().term.ty() { - type_diffs = vec![ - Sorts(ty::error::ExpectedFound { - expected: Ty::new_alias(self.tcx,ty::Projection, where_pred.skip_binder().projection_ty), - found, - }), - ]; + type_diffs = vec![Sorts(ty::error::ExpectedFound { + expected: Ty::new_alias( + self.tcx, + ty::Projection, + where_pred.skip_binder().projection_ty, + ), + found, + })]; } } if let hir::ExprKind::Path(hir::QPath::Resolved(None, path)) = expr.kind @@ -3586,12 +3737,115 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { msg, sugg, Applicability::MaybeIncorrect, - SuggestionStyle::ShowAlways + SuggestionStyle::ShowAlways, ); } } } + fn look_for_iterator_item_mistakes( + &self, + assocs_in_this_method: &[Option<(Span, (DefId, Ty<'tcx>))>], + typeck_results: &TypeckResults<'tcx>, + type_diffs: &[TypeError<'tcx>], + param_env: ty::ParamEnv<'tcx>, + path_segment: &hir::PathSegment<'_>, + args: &[hir::Expr<'_>], + err: &mut Diagnostic, + ) { + let tcx = self.tcx; + // Special case for iterator chains, we look at potential failures of `Iterator::Item` + // not being `: Clone` and `Iterator::map` calls with spurious trailing `;`. + for entry in assocs_in_this_method { + let Some((_span, (def_id, ty))) = entry else { + continue; + }; + for diff in type_diffs { + let Sorts(expected_found) = diff else { + continue; + }; + if tcx.is_diagnostic_item(sym::IteratorItem, *def_id) + && path_segment.ident.name == sym::map + && self.can_eq(param_env, expected_found.found, *ty) + && let [arg] = args + && let hir::ExprKind::Closure(closure) = arg.kind + { + let body = tcx.hir().body(closure.body); + if let hir::ExprKind::Block(block, None) = body.value.kind + && let None = block.expr + && let [.., stmt] = block.stmts + && let hir::StmtKind::Semi(expr) = stmt.kind + // FIXME: actually check the expected vs found types, but right now + // the expected is a projection that we need to resolve. + // && let Some(tail_ty) = typeck_results.expr_ty_opt(expr) + && expected_found.found.is_unit() + { + err.span_suggestion_verbose( + expr.span.shrink_to_hi().with_hi(stmt.span.hi()), + "consider removing this semicolon", + String::new(), + Applicability::MachineApplicable, + ); + } + let expr = if let hir::ExprKind::Block(block, None) = body.value.kind + && let Some(expr) = block.expr + { + expr + } else { + body.value + }; + if let hir::ExprKind::MethodCall(path_segment, rcvr, [], span) = expr.kind + && path_segment.ident.name == sym::clone + && let Some(expr_ty) = typeck_results.expr_ty_opt(expr) + && let Some(rcvr_ty) = typeck_results.expr_ty_opt(rcvr) + && self.can_eq(param_env, expr_ty, rcvr_ty) + && let ty::Ref(_, ty, _) = expr_ty.kind() + { + err.span_label( + span, + format!( + "this method call is cloning the reference `{expr_ty}`, not \ + `{ty}` which doesn't implement `Clone`", + ), + ); + let ty::Param(..) = ty.kind() else { + continue; + }; + let hir = tcx.hir(); + let node = hir.get_by_def_id(hir.get_parent_item(expr.hir_id).def_id); + + let pred = ty::Binder::dummy(ty::TraitPredicate { + trait_ref: ty::TraitRef::from_lang_item( + tcx, + LangItem::Clone, + span, + [*ty], + ), + polarity: ty::ImplPolarity::Positive, + }); + let Some(generics) = node.generics() else { + continue; + }; + let Some(body_id) = node.body_id() else { + continue; + }; + suggest_restriction( + tcx, + hir.body_owner_def_id(body_id), + &generics, + &format!("type parameter `{ty}`"), + err, + node.fn_sig(), + None, + pred, + None, + ); + } + } + } + } + } + fn point_at_chain( &self, expr: &hir::Expr<'_>, @@ -3611,13 +3865,22 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { let mut prev_ty = self.resolve_vars_if_possible( typeck_results.expr_ty_adjusted_opt(expr).unwrap_or(Ty::new_misc_error(tcx)), ); - while let hir::ExprKind::MethodCall(_path_segment, rcvr_expr, _args, span) = expr.kind { + while let hir::ExprKind::MethodCall(path_segment, rcvr_expr, args, span) = expr.kind { // Point at every method call in the chain with the resulting type. // vec![1, 2, 3].iter().map(mapper).sum<i32>() // ^^^^^^ ^^^^^^^^^^^ expr = rcvr_expr; let assocs_in_this_method = self.probe_assoc_types_at_expr(&type_diffs, span, prev_ty, expr.hir_id, param_env); + self.look_for_iterator_item_mistakes( + &assocs_in_this_method, + typeck_results, + &type_diffs, + param_env, + path_segment, + args, + err, + ); assocs.push(assocs_in_this_method); prev_ty = self.resolve_vars_if_possible( typeck_results.expr_ty_adjusted_opt(expr).unwrap_or(Ty::new_misc_error(tcx)), @@ -3638,9 +3901,17 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { if let hir::Node::Param(param) = parent { // ...and it is a an fn argument. let prev_ty = self.resolve_vars_if_possible( - typeck_results.node_type_opt(param.hir_id).unwrap_or(Ty::new_misc_error(tcx,)), + typeck_results + .node_type_opt(param.hir_id) + .unwrap_or(Ty::new_misc_error(tcx)), + ); + let assocs_in_this_method = self.probe_assoc_types_at_expr( + &type_diffs, + param.ty_span, + prev_ty, + param.hir_id, + param_env, ); - let assocs_in_this_method = self.probe_assoc_types_at_expr(&type_diffs, param.ty_span, prev_ty, param.hir_id, param_env); if assocs_in_this_method.iter().any(|a| a.is_some()) { assocs.push(assocs_in_this_method); print_root_expr = false; @@ -3651,7 +3922,9 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { } // We want the type before deref coercions, otherwise we talk about `&[_]` // instead of `Vec<_>`. - if let Some(ty) = typeck_results.expr_ty_opt(expr) && print_root_expr { + if let Some(ty) = typeck_results.expr_ty_opt(expr) + && print_root_expr + { let ty = with_forced_trimmed_paths!(self.ty_to_string(ty)); // Point at the root expression // vec![1, 2, 3].iter().map(mapper).sum<i32>() @@ -3782,7 +4055,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { // This corresponds to `<ExprTy as Iterator>::Item = _`. let projection = ty::Binder::dummy(ty::PredicateKind::Clause( ty::ClauseKind::Projection(ty::ProjectionPredicate { - projection_ty: self.tcx.mk_alias_ty(proj.def_id, args), + projection_ty: ty::AliasTy::new(self.tcx, proj.def_id, args), term: ty_var.into(), }), )); @@ -4000,14 +4273,6 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { // ... whose signature is `async` (i.e. this is an AFIT) let (sig, body) = item.expect_fn(); - let hir::IsAsync::Async(async_span) = sig.header.asyncness else { - return; - }; - let Ok(async_span) = - self.tcx.sess.source_map().span_extend_while(async_span, |c| c.is_whitespace()) - else { - return; - }; let hir::FnRetTy::Return(hir::Ty { kind: hir::TyKind::OpaqueDef(def, ..), .. }) = sig.decl.output else { @@ -4021,55 +4286,17 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { return; } - let future = self.tcx.hir().item(*def).expect_opaque_ty(); - let Some(hir::GenericBound::LangItemTrait(_, _, _, generics)) = future.bounds.get(0) else { - // `async fn` should always lower to a lang item bound... but don't ICE. - return; - }; - let Some(hir::TypeBindingKind::Equality { term: hir::Term::Ty(future_output_ty) }) = - generics.bindings.get(0).map(|binding| binding.kind) - else { - // Also should never happen. + let Some(sugg) = suggest_desugaring_async_fn_to_impl_future_in_trait( + self.tcx, + *sig, + *body, + opaque_def_id.expect_local(), + &format!(" + {auto_trait}"), + ) else { return; }; let function_name = self.tcx.def_path_str(fn_def_id); - - let mut sugg = if future_output_ty.span.is_empty() { - vec![ - (async_span, String::new()), - ( - future_output_ty.span, - format!(" -> impl std::future::Future<Output = ()> + {auto_trait}"), - ), - ] - } else { - vec![ - ( - future_output_ty.span.shrink_to_lo(), - "impl std::future::Future<Output = ".to_owned(), - ), - (future_output_ty.span.shrink_to_hi(), format!("> + {auto_trait}")), - (async_span, String::new()), - ] - }; - - // If there's a body, we also need to wrap it in `async {}` - if let hir::TraitFn::Provided(body) = body { - let body = self.tcx.hir().body(*body); - let body_span = body.value.span; - let body_span_without_braces = - body_span.with_lo(body_span.lo() + BytePos(1)).with_hi(body_span.hi() - BytePos(1)); - if body_span_without_braces.is_empty() { - sugg.push((body_span_without_braces, " async {} ".to_owned())); - } else { - sugg.extend([ - (body_span_without_braces.shrink_to_lo(), "async {".to_owned()), - (body_span_without_braces.shrink_to_hi(), "} ".to_owned()), - ]); - } - } - err.multipart_suggestion( format!( "`{auto_trait}` can be made part of the associated future's \ @@ -4150,7 +4377,9 @@ fn hint_missing_borrow<'tcx>( let mut span = arg.span.shrink_to_lo(); let mut left = found_refs.len() - expected_refs.len(); let mut ty = arg; - while let hir::TyKind::Ref(_, mut_ty) = &ty.kind && left > 0 { + while let hir::TyKind::Ref(_, mut_ty) = &ty.kind + && left > 0 + { span = span.with_hi(mut_ty.ty.span.lo()); ty = mut_ty.ty; left -= 1; @@ -4221,7 +4450,7 @@ impl<'v> Visitor<'v> for ReturnsVisitor<'v> { fn visit_body(&mut self, body: &'v hir::Body<'v>) { assert!(!self.in_block_tail); - if body.generator_kind().is_none() { + if body.coroutine_kind().is_none() { if let hir::ExprKind::Block(block, None) = body.value.kind { if block.expr.is_some() { self.in_block_tail = true; @@ -4300,6 +4529,39 @@ impl<'a, 'hir> hir::intravisit::Visitor<'hir> for ReplaceImplTraitVisitor<'a> { } } +pub(super) fn get_explanation_based_on_obligation<'tcx>( + obligation: &PredicateObligation<'tcx>, + trait_ref: ty::PolyTraitRef<'tcx>, + trait_predicate: &ty::PolyTraitPredicate<'tcx>, + pre_message: String, +) -> String { + if let ObligationCauseCode::MainFunctionType = obligation.cause.code() { + "consider using `()`, or a `Result`".to_owned() + } else { + let ty_desc = match trait_ref.skip_binder().self_ty().kind() { + ty::FnDef(_, _) => Some("fn item"), + ty::Closure(_, _) => Some("closure"), + _ => None, + }; + + match ty_desc { + Some(desc) => format!( + "{}the trait `{}` is not implemented for {} `{}`", + pre_message, + trait_predicate.print_modifiers_and_trait_path(), + desc, + trait_ref.skip_binder().self_ty(), + ), + None => format!( + "{}the trait `{}` is not implemented for `{}`", + pre_message, + trait_predicate.print_modifiers_and_trait_path(), + trait_ref.skip_binder().self_ty(), + ), + } + } +} + // Replace `param` with `replace_ty` struct ReplaceImplTraitFolder<'tcx> { tcx: TyCtxt<'tcx>, @@ -4321,3 +4583,65 @@ impl<'tcx> TypeFolder<TyCtxt<'tcx>> for ReplaceImplTraitFolder<'tcx> { self.tcx } } + +pub fn suggest_desugaring_async_fn_to_impl_future_in_trait<'tcx>( + tcx: TyCtxt<'tcx>, + sig: hir::FnSig<'tcx>, + body: hir::TraitFn<'tcx>, + opaque_def_id: LocalDefId, + add_bounds: &str, +) -> Option<Vec<(Span, String)>> { + let hir::IsAsync::Async(async_span) = sig.header.asyncness else { + return None; + }; + let Ok(async_span) = tcx.sess.source_map().span_extend_while(async_span, |c| c.is_whitespace()) + else { + return None; + }; + + let future = tcx.hir().get_by_def_id(opaque_def_id).expect_item().expect_opaque_ty(); + let Some(hir::GenericBound::LangItemTrait(_, _, _, generics)) = future.bounds.get(0) else { + // `async fn` should always lower to a lang item bound... but don't ICE. + return None; + }; + let Some(hir::TypeBindingKind::Equality { term: hir::Term::Ty(future_output_ty) }) = + generics.bindings.get(0).map(|binding| binding.kind) + else { + // Also should never happen. + return None; + }; + + let mut sugg = if future_output_ty.span.is_empty() { + vec![ + (async_span, String::new()), + ( + future_output_ty.span, + format!(" -> impl std::future::Future<Output = ()>{add_bounds}"), + ), + ] + } else { + vec![ + (future_output_ty.span.shrink_to_lo(), "impl std::future::Future<Output = ".to_owned()), + (future_output_ty.span.shrink_to_hi(), format!(">{add_bounds}")), + (async_span, String::new()), + ] + }; + + // If there's a body, we also need to wrap it in `async {}` + if let hir::TraitFn::Provided(body) = body { + let body = tcx.hir().body(body); + let body_span = body.value.span; + let body_span_without_braces = + body_span.with_lo(body_span.lo() + BytePos(1)).with_hi(body_span.hi() - BytePos(1)); + if body_span_without_braces.is_empty() { + sugg.push((body_span_without_braces, " async {} ".to_owned())); + } else { + sugg.extend([ + (body_span_without_braces.shrink_to_lo(), "async {".to_owned()), + (body_span_without_braces.shrink_to_hi(), "} ".to_owned()), + ]); + } + } + + Some(sugg) +} |