1// ignore-tidy-file-filelength23use std::borrow::Cow;4use std::path::PathBuf;5use std::{debug_assert_matches, iter};67use itertools::{EitherOrBoth, Itertools};8use rustc_abi::ExternAbi;9use rustc_attr_ir::lang_items::{self, LangItem};10use rustc_data_structures::fx::FxHashSet;11use rustc_errors::codes::*;12use rustc_errors::{13 Applicability, Diag, MultiSpan, Style, SuggestionStyle, pluralize, struct_span_code_err,14};15use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res};16use rustc_hir::def_id::DefId;17use rustc_hir::intravisit::Visitor;18use rustc_hir::{19 self as hir, AmbigArg, CoroutineDesugaring, CoroutineKind, CoroutineSource, Expr, HirId, Node,20 expr_needs_parens,21};22use rustc_infer::infer::{BoundRegionConversionTime, DefineOpaqueTypes, InferCtxt, InferOk};23use rustc_infer::traits::ImplSource;24use rustc_middle::middle::privacy::Level;25use rustc_middle::traits::IsConstable;26use rustc_middle::ty::adjustment::{Adjust, DerefAdjustKind};27use rustc_middle::ty::error::TypeError;28use rustc_middle::ty::print::{29 PrintPolyTraitClauseExt as _, PrintPolyTraitRefExt, PrintTraitClauseExt as _,30 PrintTraitRefExt as _, with_forced_trimmed_paths, with_no_trimmed_paths,31 with_types_for_suggestion,32};33use rustc_middle::ty::{34 self, AdtKind, GenericArgs, InferTy, IsSuggestable, Ty, TyCtxt, TypeFoldable, TypeFolder,35 TypeSuperFoldable, TypeSuperVisitable, TypeVisitableExt, TypeVisitor, TypeckResults,36 Unnormalized, Upcast, suggest_arbitrary_trait_bound, suggest_constraining_type_param,37};38use rustc_span::def_id::LocalDefId;39use rustc_span::{40 BytePos, DUMMY_SP, DesugaringKind, ExpnKind, Ident, MacroKind, Span, Symbol, bug, kw, span_bug,41 sym,42};43use tracing::{debug, instrument};4445use super::{46 DefIdOrName, FindExprBySpan, ImplCandidate, Obligation, ObligationCause, ObligationCauseCode,47 PredicateObligation,48};49use crate::diagnostics;50use crate::error_reporting::TypeErrCtxt;51use crate::infer::InferCtxtExt as _;52use crate::traits::query::evaluate_obligation::InferCtxtExt as _;53use crate::traits::{ImplDerivedCause, NormalizeExt, ObligationCtxt, SelectionContext};5455#[derive(Debug)]56pub enum CoroutineInteriorOrUpvar {57 // span of interior type58 Interior(Span, Option<(Span, Option<Span>)>),59 // span of upvar60 Upvar(Span),61}6263// This type provides a uniform interface to retrieve data on coroutines, whether it originated from64// the local crate being compiled or from a foreign crate.65#[derive(Debug)]66struct CoroutineData<'a, 'tcx>(&'a TypeckResults<'tcx>);6768impl<'a, 'tcx> CoroutineData<'a, 'tcx> {69 /// Try to get information about variables captured by the coroutine that matches a type we are70 /// looking for with `ty_matches` function. We uses it to find upvar which causes a failure to71 /// meet an obligation72 fn try_get_upvar_span<F>(73 &self,74 infer_context: &InferCtxt<'tcx>,75 coroutine_did: DefId,76 ty_matches: F,77 ) -> Option<CoroutineInteriorOrUpvar>78 where79 F: Fn(ty::Binder<'tcx, Ty<'tcx>>) -> bool,80 {81 infer_context.tcx.upvars_mentioned(coroutine_did).and_then(|upvars| {82 upvars.iter().find_map(|(upvar_id, upvar)| {83 let upvar_ty = self.0.node_type(*upvar_id);84 let upvar_ty = infer_context.deeply_resolve_ignoring_regions(upvar_ty);85 ty_matches(ty::Binder::dummy(upvar_ty))86 .then(|| CoroutineInteriorOrUpvar::Upvar(upvar.span))87 })88 })89 }9091 /// Try to get the span of a type being awaited on that matches the type we are looking with the92 /// `ty_matches` function. We uses it to find awaited type which causes a failure to meet an93 /// obligation94 fn get_from_await_ty<F>(95 &self,96 visitor: AwaitsVisitor,97 tcx: TyCtxt<'tcx>,98 ty_matches: F,99 ) -> Option<Span>100 where101 F: Fn(ty::Binder<'tcx, Ty<'tcx>>) -> bool,102 {103 visitor104 .awaits105 .into_iter()106 .map(|id| tcx.hir_expect_expr(id))107 .find(|await_expr| ty_matches(ty::Binder::dummy(self.0.expr_ty_adjusted(await_expr))))108 .map(|expr| expr.span)109 }110}111112fn predicate_constraint(generics: &hir::Generics<'_>, pred: ty::Predicate<'_>) -> (Span, String) {113 (114 generics.tail_span_for_predicate_suggestion(),115 with_types_for_suggestion!(format!("{} {}", generics.add_where_or_trailing_comma(), pred)),116 )117}118119/// Type parameter needs more bounds. The trivial case is `T` `where T: Bound`, but120/// it can also be an `impl Trait` param that needs to be decomposed to a type121/// param for cleaner code.122pub fn suggest_restriction<'tcx>(123 tcx: TyCtxt<'tcx>,124 item_id: LocalDefId,125 hir_generics: &hir::Generics<'tcx>,126 msg: &str,127 err: &mut Diag<'_>,128 fn_sig: Option<&hir::FnSig<'_>>,129 projection: Option<ty::ProjectionAliasTy<'_>>,130 trait_pred: ty::PolyTraitClause<'tcx>,131 // When we are dealing with a trait, `super_traits` will be `Some`:132 // Given `trait T: A + B + C {}`133 // - ^^^^^^^^^ GenericBounds134 // |135 // &Ident136 super_traits: Option<(&Ident, &hir::GenericBounds<'_>)>,137) {138 if hir_generics.where_clause_span.from_expansion()139 || hir_generics.where_clause_span.desugaring_kind().is_some()140 || projection.is_some_and(|projection| {141 (tcx.is_impl_trait_in_trait(projection.kind) && !tcx.features().return_type_notation())142 || tcx.lookup_stability(projection.kind).is_some_and(|stab| stab.is_unstable())143 })144 {145 return;146 }147 let generics = tcx.generics_of(item_id);148 // Given `fn foo(t: impl Trait)` where `Trait` requires assoc type `A`...149 if let Some((param, bound_str, fn_sig)) =150 fn_sig.zip(projection).and_then(|(sig, p)| match *p.projection_self_ty().kind() {151 // Shenanigans to get the `Trait` from the `impl Trait`.152 ty::Param(param) => {153 let param_def = generics.type_param(param, tcx);154 if param_def.kind.is_synthetic() {155 let bound_str =156 param_def.name.as_str().strip_prefix("impl ")?.trim_start().to_string();157 return Some((param_def, bound_str, sig));158 }159 None160 }161 _ => None,162 })163 {164 let type_param_name = hir_generics.params.next_type_param_name(Some(&bound_str));165 let trait_pred = trait_pred.fold_with(&mut ReplaceImplTraitFolder {166 tcx,167 param,168 replace_ty: ty::ParamTy::new(generics.count() as u32, Symbol::intern(&type_param_name))169 .to_ty(tcx),170 });171 if !trait_pred.is_suggestable(tcx, false) {172 return;173 }174 // We know we have an `impl Trait` that doesn't satisfy a required projection.175176 // Find all of the occurrences of `impl Trait` for `Trait` in the function arguments'177 // types. There should be at least one, but there might be *more* than one. In that178 // case we could just ignore it and try to identify which one needs the restriction,179 // but instead we choose to suggest replacing all instances of `impl Trait` with `T`180 // where `T: Trait`.181 let mut ty_spans = vec![];182 for input in fn_sig.decl.inputs {183 ReplaceImplTraitVisitor { ty_spans: &mut ty_spans, param_did: param.def_id }184 .visit_ty_unambig(input);185 }186 // The type param `T: Trait` we will suggest to introduce.187 let type_param = format!("{type_param_name}: {bound_str}");188189 let mut sugg = vec![190 if let Some(span) = hir_generics.span_for_param_suggestion() {191 (span, format!(", {type_param}"))192 } else {193 (hir_generics.span, format!("<{type_param}>"))194 },195 // `fn foo(t: impl Trait)`196 // ^ suggest `where <T as Trait>::A: Bound`197 predicate_constraint(hir_generics, trait_pred.upcast(tcx)),198 ];199 sugg.extend(ty_spans.into_iter().map(|s| (s, type_param_name.to_string())));200201 // Suggest `fn foo<T: Trait>(t: T) where <T as Trait>::A: Bound`.202 // FIXME: we should suggest `fn foo(t: impl Trait<A: Bound>)` instead.203 err.multipart_suggestion(204 "introduce a type parameter with a trait bound instead of using `impl Trait`",205 sugg,206 Applicability::MaybeIncorrect,207 );208 } else {209 if !trait_pred.is_suggestable(tcx, false) {210 return;211 }212 // Trivial case: `T` needs an extra bound: `T: Bound`.213 let (sp, suggestion) = match (214 hir_generics215 .params216 .iter()217 .find(|p| !matches!(p.kind, hir::GenericParamKind::Type { synthetic: true, .. })),218 super_traits,219 ) {220 (_, None) => predicate_constraint(hir_generics, trait_pred.upcast(tcx)),221 (None, Some((ident, []))) => (222 ident.span.shrink_to_hi(),223 format!(": {}", trait_pred.print_modifiers_and_trait_path()),224 ),225 (_, Some((_, [.., bounds]))) => (226 bounds.span().shrink_to_hi(),227 format!(" + {}", trait_pred.print_modifiers_and_trait_path()),228 ),229 (Some(_), Some((_, []))) => (230 hir_generics.span.shrink_to_hi(),231 format!(": {}", trait_pred.print_modifiers_and_trait_path()),232 ),233 };234235 err.span_suggestion_verbose(236 sp,237 format!("consider further restricting {msg}"),238 suggestion,239 Applicability::MachineApplicable,240 );241 }242}243244/// A single layer of `&` peeled from an expression, used by245/// [`TypeErrCtxt::peel_expr_refs`].246struct PeeledRef<'tcx> {247 /// The span covering the `&` (and any whitespace/mutability keyword) to remove.248 span: Span,249 /// The type after peeling this layer (and all prior layers).250 peeled_ty: Ty<'tcx>,251}252253impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {254 pub fn note_field_shadowed_by_private_candidate_in_cause(255 &self,256 err: &mut Diag<'_>,257 cause: &ObligationCause<'tcx>,258 param_env: ty::ParamEnv<'tcx>,259 ) {260 let mut hir_ids = FxHashSet::default();261 // Walk the parent chain so we can recover262 // the source expression from whichever layer carries them.263 let mut next_code = Some(cause.code());264 while let Some(cause_code) = next_code {265 match cause_code {266 ObligationCauseCode::BinOp { lhs_hir_id, rhs_hir_id, .. } => {267 hir_ids.insert(*lhs_hir_id);268 hir_ids.insert(*rhs_hir_id);269 }270 ObligationCauseCode::FunctionArg { arg_hir_id, .. }271 | ObligationCauseCode::ReturnValue(arg_hir_id)272 | ObligationCauseCode::AwaitableExpr(arg_hir_id)273 | ObligationCauseCode::BlockTailExpression(arg_hir_id, _)274 | ObligationCauseCode::UnOp { hir_id: arg_hir_id } => {275 hir_ids.insert(*arg_hir_id);276 }277 ObligationCauseCode::OpaqueReturnType(Some((_, hir_id))) => {278 hir_ids.insert(*hir_id);279 }280 _ => {}281 }282 next_code = cause_code.parent();283 }284285 if !cause.span.is_dummy()286 && let Some(body) = self.tcx.hir_maybe_body_owned_by(cause.body_def_id)287 {288 let mut expr_finder = FindExprBySpan::new(cause.span, self.tcx);289 expr_finder.visit_body(body);290 if let Some(expr) = expr_finder.result {291 hir_ids.insert(expr.hir_id);292 }293 }294295 // we will sort immediately by source order before emitting any diagnostics296 #[allow(rustc::potential_query_instability)]297 let mut hir_ids: Vec<_> = hir_ids.into_iter().collect();298 let source_map = self.tcx.sess.source_map();299 hir_ids.sort_by_cached_key(|hir_id| {300 let span = self.tcx.hir_span(*hir_id);301 let lo = source_map.lookup_byte_offset(span.lo());302 let hi = source_map.lookup_byte_offset(span.hi());303 (lo.sf.name.prefer_remapped_unconditionally().to_string(), lo.pos.0, hi.pos.0)304 });305306 for hir_id in hir_ids {307 self.note_field_shadowed_by_private_candidate(err, hir_id, param_env);308 }309 }310311 pub fn note_field_shadowed_by_private_candidate(312 &self,313 err: &mut Diag<'_>,314 hir_id: hir::HirId,315 param_env: ty::ParamEnv<'tcx>,316 ) {317 let Some(typeck_results) = &self.typeck_results else {318 return;319 };320 let Node::Expr(expr) = self.tcx.hir_node(hir_id) else {321 return;322 };323 let hir::ExprKind::Field(base_expr, field_ident) = expr.kind else {324 return;325 };326327 let Some(base_ty) = typeck_results.expr_ty_opt(base_expr) else {328 return;329 };330 let base_ty = self.deeply_resolve_ignoring_regions(base_ty);331 if base_ty.references_error() {332 return;333 }334335 let mut private_candidate: Option<(Ty<'tcx>, Ty<'tcx>, Span)> = None;336337 for (deref_base_ty, _) in (self.autoderef_steps)(base_ty) {338 let ty::Adt(base_def, args) = deref_base_ty.kind() else {339 continue;340 };341342 if base_def.is_enum() {343 continue;344 }345346 let (adjusted_ident, def_scope) = self.tcx.adjust_ident_and_get_scope(347 field_ident,348 base_def.did(),349 self.tcx.parent_module_from_def_id(typeck_results.hir_owner.def_id),350 );351352 let Some((_, field_def)) =353 base_def.non_enum_variant().fields.iter_enumerated().find(|(_, field)| {354 field.ident(self.tcx).normalize_to_macros_2_0() == adjusted_ident355 })356 else {357 continue;358 };359 let field_span = self360 .tcx361 .def_ident_span(field_def.did)362 .unwrap_or_else(|| self.tcx.def_span(field_def.did));363364 if field_def.vis.is_accessible_from(def_scope, self.tcx) {365 let accessible_field_ty = field_def.ty(self.tcx, args).skip_norm_wip();366 if let Some((private_base_ty, private_field_ty, private_field_span)) =367 private_candidate368 && !self.can_eq(param_env, private_field_ty, accessible_field_ty)369 {370 let private_struct_span = match private_base_ty.kind() {371 ty::Adt(private_base_def, _) => self372 .tcx373 .def_ident_span(private_base_def.did())374 .unwrap_or_else(|| self.tcx.def_span(private_base_def.did())),375 _ => DUMMY_SP,376 };377 let accessible_struct_span = self378 .tcx379 .def_ident_span(base_def.did())380 .unwrap_or_else(|| self.tcx.def_span(base_def.did()));381 let deref_impl_span = (typeck_results382 .expr_adjustments(base_expr)383 .iter()384 .filter(|adj| {385 matches!(adj.kind, Adjust::Deref(DerefAdjustKind::Overloaded(_)))386 })387 .count()388 == 1)389 .then(|| {390 self.probe(|_| {391 let deref_trait_did =392 self.tcx.require_lang_item(LangItem::Deref, DUMMY_SP);393 let trait_ref =394 ty::TraitRef::new(self.tcx, deref_trait_did, [private_base_ty]);395 let obligation: Obligation<'tcx, ty::Predicate<'tcx>> =396 Obligation::new(397 self.tcx,398 ObligationCause::dummy(),399 param_env,400 trait_ref,401 );402 let Ok(Some(ImplSource::UserDefined(impl_data))) =403 SelectionContext::new(self)404 .select(&obligation.with(self.tcx, trait_ref))405 else {406 return None;407 };408 Some(self.tcx.def_span(impl_data.impl_def_id))409 })410 })411 .flatten();412413 let mut note_spans: MultiSpan = private_struct_span.into();414 if private_struct_span != DUMMY_SP {415 note_spans.push_span_label(private_struct_span, "in this struct");416 }417 if private_field_span != DUMMY_SP {418 note_spans.push_span_label(419 private_field_span,420 "if this field wasn't private, it would be accessible",421 );422 }423 if accessible_struct_span != DUMMY_SP {424 note_spans.push_span_label(425 accessible_struct_span,426 "this struct is accessible through auto-deref",427 );428 }429 if field_span != DUMMY_SP {430 note_spans431 .push_span_label(field_span, "this is the field that was accessed");432 }433 if let Some(deref_impl_span) = deref_impl_span434 && deref_impl_span != DUMMY_SP435 {436 note_spans.push_span_label(437 deref_impl_span,438 "the field was accessed through this `Deref`",439 );440 }441442 err.span_note(443 note_spans,444 format!(445 "there is a field `{field_ident}` on `{private_base_ty}` with type `{private_field_ty}` but it is private; `{field_ident}` from `{deref_base_ty}` was accessed through auto-deref instead"446 ),447 );448 }449450 // we finally get to the accessible field,451 // so we can return early without checking the rest of the autoderef candidates452 return;453 }454455 private_candidate.get_or_insert((456 deref_base_ty,457 field_def.ty(self.tcx, args).skip_norm_wip(),458 field_span,459 ));460 }461 }462463 pub fn suggest_restricting_param_bound(464 &self,465 err: &mut Diag<'_>,466 trait_pred: ty::PolyTraitClause<'tcx>,467 associated_ty: Option<(&'static str, Ty<'tcx>)>,468 mut body_def_id: LocalDefId,469 ) {470 if trait_pred.skip_binder().polarity != ty::ClausePolarity::Positive {471 return;472 }473474 let trait_pred = self.resolve_numeric_literals_with_default(trait_pred);475476 let self_ty = trait_pred.skip_binder().self_ty();477 let (param_ty, projection) = match *self_ty.kind() {478 ty::Param(_) => (true, None),479 ty::Alias(_, alias) => {480 if let Some(projection) = alias.try_to_projection() {481 (false, Some(projection))482 } else {483 (false, None)484 }485 }486 _ => (false, None),487 };488489 let mut finder = ParamFinder { .. };490 finder.visit_binder(&trait_pred);491492 // FIXME: Add check for trait bound that is already present, particularly `?Sized` so we493 // don't suggest `T: Sized + ?Sized`.494 loop {495 let node = self.tcx.hir_node_by_def_id(body_def_id);496 match node {497 hir::Node::Item(hir::Item {498 kind: hir::ItemKind::Trait { ident, generics, bounds, .. },499 ..500 }) if self_ty == self.tcx.types.self_param => {501 assert!(param_ty);502 // Restricting `Self` for a single method.503 suggest_restriction(504 self.tcx,505 body_def_id,506 generics,507 "`Self`",508 err,509 None,510 projection,511 trait_pred,512 Some((&ident, bounds)),513 );514 return;515 }516517 hir::Node::TraitItem(hir::TraitItem {518 generics,519 kind: hir::TraitItemKind::Fn(..),520 ..521 }) if self_ty == self.tcx.types.self_param => {522 assert!(param_ty);523 // Restricting `Self` for a single method.524 suggest_restriction(525 self.tcx,526 body_def_id,527 generics,528 "`Self`",529 err,530 None,531 projection,532 trait_pred,533 None,534 );535 return;536 }537538 hir::Node::TraitItem(hir::TraitItem {539 generics,540 kind: hir::TraitItemKind::Fn(fn_sig, ..),541 ..542 })543 | hir::Node::ImplItem(hir::ImplItem {544 generics,545 kind: hir::ImplItemKind::Fn(fn_sig, ..),546 ..547 })548 | hir::Node::Item(hir::Item {549 kind: hir::ItemKind::Fn { sig: fn_sig, generics, .. },550 ..551 }) if projection.is_some() => {552 // Missing restriction on associated type of type parameter (unmet projection).553 suggest_restriction(554 self.tcx,555 body_def_id,556 generics,557 "the associated type",558 err,559 Some(fn_sig),560 projection,561 trait_pred,562 None,563 );564 return;565 }566 hir::Node::Item(hir::Item {567 kind:568 hir::ItemKind::Trait { generics, .. }569 | hir::ItemKind::Impl(hir::Impl { generics, .. }),570 ..571 }) if projection.is_some() => {572 // Missing restriction on associated type of type parameter (unmet projection).573 suggest_restriction(574 self.tcx,575 body_def_id,576 generics,577 "the associated type",578 err,579 None,580 projection,581 trait_pred,582 None,583 );584 return;585 }586587 hir::Node::Item(hir::Item {588 kind:589 hir::ItemKind::Struct(_, generics, _)590 | hir::ItemKind::Enum(_, generics, _)591 | hir::ItemKind::Union(_, generics, _)592 | hir::ItemKind::Trait { generics, .. }593 | hir::ItemKind::Impl(hir::Impl { generics, .. })594 | hir::ItemKind::Fn { generics, .. }595 | hir::ItemKind::TyAlias(_, generics, _)596 | hir::ItemKind::Const(_, generics, _, _)597 | hir::ItemKind::TraitAlias(_, _, generics, _),598 ..599 })600 | hir::Node::TraitItem(hir::TraitItem { generics, .. })601 | hir::Node::ImplItem(hir::ImplItem { generics, .. })602 if param_ty =>603 {604 // We skip the 0'th arg (self) because we do not want605 // to consider the predicate as not suggestible if the606 // self type is an arg position `impl Trait` -- instead,607 // we handle that by adding ` + Bound` below.608 // FIXME(compiler-errors): It would be nice to do the same609 // this that we do in `suggest_restriction` and pull the610 // `impl Trait` into a new generic if it shows up somewhere611 // else in the predicate.612 if !trait_pred.skip_binder().trait_ref.args[1..]613 .iter()614 .all(|g| g.is_suggestable(self.tcx, false))615 {616 return;617 }618 // Missing generic type parameter bound.619 let param_name = self_ty.to_string();620 let mut constraint = with_no_trimmed_paths!(621 trait_pred.print_modifiers_and_trait_path().to_string()622 );623624 if let Some((name, term)) = associated_ty {625 // FIXME: this case overlaps with code in TyCtxt::note_and_explain_type_err.626 // That should be extracted into a helper function.627 if let Some(stripped) = constraint.strip_suffix('>') {628 constraint = format!("{stripped}, {name} = {term}>");629 } else {630 constraint.push_str(&format!("<{name} = {term}>"));631 }632 }633634 if suggest_constraining_type_param(635 self.tcx,636 generics,637 err,638 ¶m_name,639 &constraint,640 Some(trait_pred.def_id()),641 None,642 ) {643 return;644 }645 }646647 hir::Node::TraitItem(hir::TraitItem {648 generics,649 kind: hir::TraitItemKind::Fn(..),650 ..651 })652 | hir::Node::ImplItem(hir::ImplItem {653 generics,654 impl_kind: hir::ImplItemImplKind::Inherent { .. },655 kind: hir::ImplItemKind::Fn(..),656 ..657 }) if finder.can_suggest_bound(generics) => {658 // Missing generic type parameter bound.659 suggest_arbitrary_trait_bound(660 self.tcx,661 generics,662 err,663 trait_pred,664 associated_ty,665 );666 }667 hir::Node::Item(hir::Item {668 kind:669 hir::ItemKind::Struct(_, generics, _)670 | hir::ItemKind::Enum(_, generics, _)671 | hir::ItemKind::Union(_, generics, _)672 | hir::ItemKind::Trait { generics, .. }673 | hir::ItemKind::Impl(hir::Impl { generics, .. })674 | hir::ItemKind::Fn { generics, .. }675 | hir::ItemKind::TyAlias(_, generics, _)676 | hir::ItemKind::Const(_, generics, _, _)677 | hir::ItemKind::TraitAlias(_, _, generics, _),678 ..679 }) if finder.can_suggest_bound(generics) => {680 // Missing generic type parameter bound.681 if suggest_arbitrary_trait_bound(682 self.tcx,683 generics,684 err,685 trait_pred,686 associated_ty,687 ) {688 return;689 }690 }691 hir::Node::Crate(..) => return,692693 _ => {}694 }695 body_def_id = self.tcx.local_parent(body_def_id);696 }697 }698699 /// Provide a suggestion to dereference arguments to functions and binary operators, if that700 /// would satisfy trait bounds.701 pub(super) fn suggest_dereferences(702 &self,703 obligation: &PredicateObligation<'tcx>,704 err: &mut Diag<'_>,705 trait_pred: ty::PolyTraitClause<'tcx>,706 ) -> bool {707 let mut code = obligation.cause.code();708 if let ObligationCauseCode::FunctionArg { arg_hir_id, call_hir_id, .. } = code709 && let Some(typeck_results) = &self.typeck_results710 && let hir::Node::Expr(expr) = self.tcx.hir_node(*arg_hir_id)711 && let Some(arg_ty) = typeck_results.expr_ty_adjusted_opt(expr)712 {713 // Suggest dereferencing the argument to a function/method call if possible714715 // Get the root obligation, since the leaf obligation we have may be unhelpful (#87437)716 let mut real_trait_pred = trait_pred;717 while let Some((parent_code, parent_trait_pred)) = code.parent_with_predicate() {718 code = parent_code;719 if let Some(parent_trait_pred) = parent_trait_pred {720 real_trait_pred = parent_trait_pred;721 }722 }723724 // We `instantiate_bound_regions_with_erased` here because `make_subregion` does not handle725 // `ReBound`, and we don't particularly care about the regions.726 let real_ty = self.tcx.instantiate_bound_regions_with_erased(real_trait_pred.self_ty());727 if !self.can_eq(obligation.param_env, real_ty, arg_ty) {728 return false;729 }730731 // Potentially, we'll want to place our dereferences under a `&`. We don't try this for732 // `&mut`, since we can't be sure users will get the side-effects they want from it.733 // If this doesn't work, we'll try removing the `&` in `suggest_remove_reference`.734 // FIXME(dianne): this misses the case where users need both to deref and remove `&`s.735 // This method could be combined with `TypeErrCtxt::suggest_remove_reference` to handle736 // that, similar to what `FnCtxt::suggest_deref_or_ref` does.737 let (is_under_ref, base_ty, span) = match expr.kind {738 hir::ExprKind::AddrOf(hir::BorrowKind::Ref, hir::Mutability::Not, subexpr)739 if let &ty::Ref(region, base_ty, hir::Mutability::Not) = real_ty.kind() =>740 {741 (Some(region), base_ty, subexpr.span)742 }743 // Don't suggest `*&mut`, etc.744 hir::ExprKind::AddrOf(..) => return false,745 _ => (None, real_ty, obligation.cause.span),746 };747748 let autoderef = (self.autoderef_steps)(base_ty);749 let mut is_boxed = base_ty.is_box();750 if let Some(steps) = autoderef.into_iter().position(|(mut ty, obligations)| {751 // Ensure one of the following for dereferencing to be valid: we're passing by752 // reference, `ty` is `Copy`, or we're moving out of a (potentially nested) `Box`.753 let can_deref = is_under_ref.is_some()754 || self.type_is_copy_modulo_regions(obligation.param_env, ty)755 || ty.is_numeric() // for inference vars (presumably but not provably `Copy`)756 || is_boxed && self.type_is_sized_modulo_regions(obligation.param_env, ty);757 is_boxed &= ty.is_box();758759 // Re-add the `&` if necessary760 if let Some(region) = is_under_ref {761 ty = Ty::new_ref(self.tcx, region, ty, hir::Mutability::Not);762 }763764 // Remapping bound vars here765 let real_trait_pred_and_ty =766 real_trait_pred.map_bound(|inner_trait_pred| (inner_trait_pred, ty));767 let obligation = self.mk_trait_obligation_with_new_self_ty(768 obligation.param_env,769 real_trait_pred_and_ty,770 );771772 can_deref773 && obligations774 .iter()775 .chain([&obligation])776 .all(|obligation| self.predicate_may_hold(obligation))777 }) && steps > 0778 {779 if span.in_external_macro(self.tcx.sess.source_map()) {780 return false;781 }782783 // For a shared reference, prefer removing the outer `&` over suggesting784 // `&*reference`. Keep the reborrow for `&mut T` and smart pointers.785 if is_under_ref.is_some()786 && steps == 1787 && matches!(base_ty.kind(), ty::Ref(_, _, hir::Mutability::Not))788 && !expr.span.from_expansion()789 && self.suggest_remove_reference(obligation, err, real_trait_pred)790 {791 return true;792 }793 let derefs = "*".repeat(steps);794 let msg = "consider dereferencing here";795796 let call_node = self.tcx.hir_node(*call_hir_id);797 let is_receiver = matches!(798 call_node,799 Node::Expr(hir::Expr {800 kind: hir::ExprKind::MethodCall(_, receiver_expr, ..),801 ..802 })803 if receiver_expr.hir_id == *arg_hir_id804 );805 if is_receiver {806 err.multipart_suggestion(807 msg,808 vec![809 (span.shrink_to_lo(), format!("({derefs}")),810 (span.shrink_to_hi(), ")".to_string()),811 ],812 Applicability::MachineApplicable,813 )814 } else {815 err.span_suggestion_verbose(816 span.shrink_to_lo(),817 msg,818 derefs,819 Applicability::MachineApplicable,820 )821 };822 return true;823 }824 } else if let (825 ObligationCauseCode::BinOp { lhs_hir_id, rhs_hir_id, .. },826 predicate,827 ) = code.peel_derives_with_predicate()828 && let Some(typeck_results) = &self.typeck_results829 && let hir::Node::Expr(lhs) = self.tcx.hir_node(*lhs_hir_id)830 && let hir::Node::Expr(rhs) = self.tcx.hir_node(*rhs_hir_id)831 && let Some(rhs_ty) = typeck_results.expr_ty_opt(rhs)832 && let trait_pred = predicate.unwrap_or(trait_pred)833 // Only run this code on binary operators834 && lang_items::BINARY_OPERATORS835 .iter()836 .filter_map(|&op| self.tcx.lang_items().get(op))837 .any(|op| {838 op == trait_pred.skip_binder().trait_ref.def_id839 })840 {841 // Suggest dereferencing the LHS, RHS, or both terms of a binop if possible842 let trait_pred = predicate.unwrap_or(trait_pred);843 let lhs_ty = self.tcx.instantiate_bound_regions_with_erased(trait_pred.self_ty());844 let lhs_autoderef = (self.autoderef_steps)(lhs_ty);845 let rhs_autoderef = (self.autoderef_steps)(rhs_ty);846 let first_lhs = lhs_autoderef.first().unwrap().clone();847 let first_rhs = rhs_autoderef.first().unwrap().clone();848 let mut autoderefs = lhs_autoderef849 .into_iter()850 .enumerate()851 .rev()852 .zip_longest(rhs_autoderef.into_iter().enumerate().rev())853 .map(|t| match t {854 EitherOrBoth::Both(a, b) => (a, b),855 EitherOrBoth::Left(a) => (a, (0, first_rhs.clone())),856 EitherOrBoth::Right(b) => ((0, first_lhs.clone()), b),857 })858 .rev();859 if let Some((lsteps, rsteps)) =860 autoderefs.find_map(|((lsteps, (l_ty, _)), (rsteps, (r_ty, _)))| {861 // Create a new predicate with the dereferenced LHS and RHS862 // We simultaneously dereference both sides rather than doing them863 // one at a time to account for cases such as &Box<T> == &&T864 let trait_pred_and_ty = trait_pred.map_bound(|inner| {865 (866 ty::TraitClause {867 trait_ref: ty::TraitRef::new_from_args(868 self.tcx,869 inner.trait_ref.def_id,870 self.tcx.mk_args(871 &[&[l_ty.into(), r_ty.into()], &inner.trait_ref.args[2..]]872 .concat(),873 ),874 ),875 ..inner876 },877 l_ty,878 )879 });880 let obligation = self.mk_trait_obligation_with_new_self_ty(881 obligation.param_env,882 trait_pred_and_ty,883 );884 self.predicate_may_hold(&obligation).then_some(match (lsteps, rsteps) {885 (_, 0) => (Some(lsteps), None),886 (0, _) => (None, Some(rsteps)),887 _ => (Some(lsteps), Some(rsteps)),888 })889 })890 {891 let make_sugg = |mut expr: &Expr<'_>, mut steps| {892 if expr.span.in_external_macro(self.tcx.sess.source_map()) {893 return None;894 }895 let mut prefix_span = expr.span.shrink_to_lo();896 let mut msg = "consider dereferencing here";897 if let hir::ExprKind::AddrOf(_, _, inner) = expr.kind {898 msg = "consider removing the borrow and dereferencing instead";899 if let hir::ExprKind::AddrOf(..) = inner.kind {900 msg = "consider removing the borrows and dereferencing instead";901 }902 }903 while let hir::ExprKind::AddrOf(_, _, inner) = expr.kind904 && steps > 0905 {906 prefix_span = prefix_span.with_hi(inner.span.lo());907 expr = inner;908 steps -= 1;909 }910 // Empty suggestions with empty spans ICE with debug assertions911 if steps == 0 {912 return Some((913 msg.trim_end_matches(" and dereferencing instead"),914 vec![(prefix_span, String::new())],915 ));916 }917 let derefs = "*".repeat(steps);918 let needs_parens = steps > 0 && expr_needs_parens(expr);919 let mut suggestion = if needs_parens {920 vec![921 (922 expr.span.with_lo(prefix_span.hi()).shrink_to_lo(),923 format!("{derefs}("),924 ),925 (expr.span.shrink_to_hi(), ")".to_string()),926 ]927 } else {928 vec![(929 expr.span.with_lo(prefix_span.hi()).shrink_to_lo(),930 format!("{derefs}"),931 )]932 };933 // Empty suggestions with empty spans ICE with debug assertions934 if !prefix_span.is_empty() {935 suggestion.push((prefix_span, String::new()));936 }937 Some((msg, suggestion))938 };939940 if let Some(lsteps) = lsteps941 && let Some(rsteps) = rsteps942 && lsteps > 0943 && rsteps > 0944 {945 let Some((_, mut suggestion)) = make_sugg(lhs, lsteps) else {946 return false;947 };948 let Some((_, mut rhs_suggestion)) = make_sugg(rhs, rsteps) else {949 return false;950 };951 suggestion.append(&mut rhs_suggestion);952 err.multipart_suggestion(953 "consider dereferencing both sides of the expression",954 suggestion,955 Applicability::MachineApplicable,956 );957 return true;958 } else if let Some(lsteps) = lsteps959 && lsteps > 0960 {961 let Some((msg, suggestion)) = make_sugg(lhs, lsteps) else {962 return false;963 };964 err.multipart_suggestion(msg, suggestion, Applicability::MachineApplicable);965 return true;966 } else if let Some(rsteps) = rsteps967 && rsteps > 0968 {969 let Some((msg, suggestion)) = make_sugg(rhs, rsteps) else {970 return false;971 };972 err.multipart_suggestion(msg, suggestion, Applicability::MachineApplicable);973 return true;974 }975 }976 }977 false978 }979980 /// Given a closure's `DefId`, return the given name of the closure.981 ///982 /// This doesn't account for reassignments, but it's only used for suggestions.983 fn get_closure_name(984 &self,985 def_id: DefId,986 err: &mut Diag<'_>,987 msg: Cow<'static, str>,988 ) -> Option<Symbol> {989 let get_name = |err: &mut Diag<'_>, kind: &hir::PatKind<'_>| -> Option<Symbol> {990 // Get the local name of this closure. This can be inaccurate because991 // of the possibility of reassignment, but this should be good enough.992 match &kind {993 hir::PatKind::Binding(hir::BindingMode::NONE, _, ident, None) => Some(ident.name),994 _ => {995 err.note(msg);996 None997 }998 }999 };10001001 let hir_id = self.tcx.local_def_id_to_hir_id(def_id.as_local()?);1002 match self.tcx.parent_hir_node(hir_id) {1003 hir::Node::Stmt(hir::Stmt { kind: hir::StmtKind::Let(local), .. }) => {1004 get_name(err, &local.pat.kind)1005 }1006 // Different to previous arm because one is `&hir::Local` and the other1007 // is `Box<hir::Local>`.1008 hir::Node::LetStmt(local) => get_name(err, &local.pat.kind),1009 _ => None,1010 }1011 }10121013 /// We tried to apply the bound to an `fn` or closure. Check whether calling it would1014 /// evaluate to a type that *would* satisfy the trait bound. If it would, suggest calling1015 /// it: `bar(foo)` → `bar(foo())`. This case is *very* likely to be hit if `foo` is `async`.1016 pub(super) fn suggest_fn_call(1017 &self,1018 obligation: &PredicateObligation<'tcx>,1019 err: &mut Diag<'_>,1020 trait_pred: ty::PolyTraitClause<'tcx>,1021 ) -> bool {1022 // It doesn't make sense to make this suggestion outside of typeck...1023 // (also autoderef will ICE...)1024 if self.typeck_results.is_none() {1025 return false;1026 }10271028 if let ty::PredicateKind::Clause(ty::ClauseKind::Trait(trait_pred)) =1029 obligation.predicate.kind().skip_binder()1030 && self.tcx.is_lang_item(trait_pred.def_id(), LangItem::Sized)1031 {1032 // Don't suggest calling to turn an unsized type into a sized type1033 return false;1034 }10351036 let self_ty = self.instantiate_binder_with_fresh_vars(1037 DUMMY_SP,1038 BoundRegionConversionTime::FnCall,1039 trait_pred.self_ty(),1040 );10411042 let Some((def_id_or_name, output, inputs)) =1043 self.extract_callable_info(obligation.cause.body_def_id, obligation.param_env, self_ty)1044 else {1045 return false;1046 };10471048 // Remapping bound vars here1049 let trait_pred_and_self = trait_pred.map_bound(|trait_pred| (trait_pred, output));10501051 let new_obligation =1052 self.mk_trait_obligation_with_new_self_ty(obligation.param_env, trait_pred_and_self);1053 if !self.predicate_must_hold_modulo_regions(&new_obligation) {1054 return false;1055 }10561057 // If this is a zero-argument async closure directly passed as an argument1058 // and the expected type is `Future`, suggest using `async {}` block instead1059 // of `async || {}`1060 if let ty::CoroutineClosure(def_id, args) = *self_ty.kind()1061 && let sig = args.as_coroutine_closure().coroutine_closure_sig().skip_binder()1062 && let ty::Tuple(inputs) = *sig.tupled_inputs_ty.kind()1063 && inputs.is_empty()1064 && self.tcx.is_lang_item(trait_pred.def_id(), LangItem::Future)1065 && let ObligationCauseCode::FunctionArg { arg_hir_id, .. } = obligation.cause.code()1066 && let hir::Node::Expr(hir::Expr { kind: hir::ExprKind::Closure(..), .. }) =1067 self.tcx.hir_node(*arg_hir_id)1068 && let Some(hir::Node::Expr(hir::Expr {1069 kind: hir::ExprKind::Closure(closure), ..1070 })) = self.tcx.hir_get_if_local(def_id)1071 && let hir::ClosureKind::CoroutineClosure(CoroutineDesugaring::Async) = closure.kind1072 && let Some(arg_span) = closure.fn_arg_span1073 && obligation.cause.span.contains(arg_span)1074 {1075 let mut body = self.tcx.hir_body(closure.body).value;1076 let peeled = body.peel_blocks().peel_drop_temps();1077 if let hir::ExprKind::Closure(inner) = peeled.kind {1078 body = self.tcx.hir_body(inner.body).value;1079 }1080 if !matches!(body.peel_blocks().peel_drop_temps().kind, hir::ExprKind::Block(..)) {1081 return false;1082 }10831084 let sm = self.tcx.sess.source_map();1085 let removal_span = if let Ok(snippet) =1086 sm.span_to_snippet(arg_span.with_hi(arg_span.hi() + rustc_span::BytePos(1)))1087 && snippet.ends_with(' ')1088 {1089 // There's a space after `||`, include it in the removal1090 arg_span.with_hi(arg_span.hi() + rustc_span::BytePos(1))1091 } else {1092 arg_span1093 };1094 err.span_suggestion_verbose(1095 removal_span,1096 "use `async {}` instead of `async || {}` to introduce an async block",1097 "",1098 Applicability::MachineApplicable,1099 );1100 return true;1101 }11021103 // Get the name of the callable and the arguments to be used in the suggestion.1104 let msg = match def_id_or_name {1105 DefIdOrName::DefId(def_id) => match self.tcx.def_kind(def_id) {1106 DefKind::Ctor(CtorOf::Struct, _) => {1107 Cow::from("use parentheses to construct this tuple struct")1108 }1109 DefKind::Ctor(CtorOf::Variant, _) => {1110 Cow::from("use parentheses to construct this tuple variant")1111 }1112 kind => Cow::from(format!(1113 "use parentheses to call this {}",1114 self.tcx.def_kind_descr(kind, def_id)1115 )),1116 },1117 DefIdOrName::Name(name) => Cow::from(format!("use parentheses to call this {name}")),1118 };11191120 let args = inputs1121 .into_iter()1122 .map(|ty| {1123 if ty.is_suggestable(self.tcx, false) {1124 format!("/* {ty} */")1125 } else {1126 "/* value */".to_string()1127 }1128 })1129 .collect::<Vec<_>>()1130 .join(", ");11311132 let callee_hir_id = match obligation.cause.code() {1133 ObligationCauseCode::FunctionArg { arg_hir_id, .. }1134 if obligation.cause.span.can_be_used_for_suggestions() =>1135 {1136 Some(*arg_hir_id)1137 }1138 // The iterator of a `for` loop is passed to `IntoIterator::into_iter`, so the failing1139 // `Iterator` goal is a derived obligation and `cause.span` carries the loop's1140 // desugaring context. The expression is still the user's, which its own span attests.1141 code => match code.peel_derives() {1142 ObligationCauseCode::ForLoopIterator(iter_hir_id)1143 if self.tcx.hir_span(*iter_hir_id).can_be_used_for_suggestions() =>1144 {1145 Some(*iter_hir_id)1146 }1147 _ => None,1148 },1149 };11501151 if let Some(callee_hir_id) = callee_hir_id {1152 let span = obligation.cause.span;11531154 let arg_expr = match self.tcx.hir_node(callee_hir_id) {1155 hir::Node::Expr(expr) => Some(expr),1156 _ => None,1157 };11581159 let is_closure_expr =1160 arg_expr.is_some_and(|expr| matches!(expr.kind, hir::ExprKind::Closure(..)));11611162 // If the user wrote `|| {}()`, suggesting to call the closure would produce `(|| {}())()`,1163 // which doesn't help and is often outright wrong.1164 if args.is_empty()1165 && let Some(expr) = arg_expr1166 && let hir::ExprKind::Closure(closure) = expr.kind1167 {1168 let mut body = self.tcx.hir_body(closure.body).value;11691170 // Async closures desugar to a closure returning a coroutine1171 if let hir::ClosureKind::CoroutineClosure(hir::CoroutineDesugaring::Async) =1172 closure.kind1173 {1174 let peeled = body.peel_blocks().peel_drop_temps();1175 if let hir::ExprKind::Closure(inner) = peeled.kind {1176 body = self.tcx.hir_body(inner.body).value;1177 }1178 }11791180 let peeled_body = body.peel_blocks().peel_drop_temps();1181 if let hir::ExprKind::Call(callee, call_args) = peeled_body.kind1182 && call_args.is_empty()1183 && let hir::ExprKind::Block(..) = callee.peel_blocks().peel_drop_temps().kind1184 {1185 return false;1186 }1187 }11881189 if is_closure_expr {1190 err.multipart_suggestions(1191 msg,1192 vec![vec![1193 (span.shrink_to_lo(), "(".to_string()),1194 (span.shrink_to_hi(), format!(")({args})")),1195 ]],1196 Applicability::HasPlaceholders,1197 );1198 } else {1199 err.span_suggestion_verbose(1200 span.shrink_to_hi(),1201 msg,1202 format!("({args})"),1203 Applicability::HasPlaceholders,1204 );1205 }1206 } else if let DefIdOrName::DefId(def_id) = def_id_or_name {1207 let name = match self.tcx.hir_get_if_local(def_id) {1208 Some(hir::Node::Expr(hir::Expr {1209 kind: hir::ExprKind::Closure(hir::Closure { fn_decl_span, .. }),1210 ..1211 })) => {1212 err.span_label(*fn_decl_span, "consider calling this closure");1213 let Some(name) = self.get_closure_name(def_id, err, msg.clone()) else {1214 return false;1215 };1216 name.to_string()1217 }1218 Some(hir::Node::Item(hir::Item {1219 kind: hir::ItemKind::Fn { ident, .. }, ..1220 })) => {1221 err.span_label(ident.span, "consider calling this function");1222 ident.to_string()1223 }1224 Some(hir::Node::Ctor(..)) => {1225 let name = self.tcx.def_path_str(def_id);1226 err.span_label(1227 self.tcx.def_span(def_id),1228 format!("consider calling the constructor for `{name}`"),1229 );1230 name1231 }1232 _ => return false,1233 };1234 err.help(format!("{msg}: `{name}({args})`"));1235 }1236 true1237 }12381239 pub(super) fn suggest_cast_to_fn_pointer(1240 &self,1241 obligation: &PredicateObligation<'tcx>,1242 err: &mut Diag<'_>,1243 leaf_trait_predicate: ty::PolyTraitClause<'tcx>,1244 main_trait_predicate: ty::PolyTraitClause<'tcx>,1245 span: Span,1246 ) -> bool {1247 let &[candidate] = &self.find_similar_impl_candidates(1248 leaf_trait_predicate.def_id(),1249 leaf_trait_predicate.self_ty().skip_binder(),1250 )[..] else {1251 return false;1252 };1253 let candidate = candidate.trait_ref;12541255 if !matches!(1256 (candidate.self_ty().kind(), main_trait_predicate.self_ty().skip_binder().kind(),),1257 (ty::FnPtr(..), ty::FnDef(..))1258 ) {1259 return false;1260 }12611262 let parenthesized_cast = |span: Span| {1263 vec![1264 (span.shrink_to_lo(), "(".to_string()),1265 (span.shrink_to_hi(), format!(" as {})", candidate.self_ty())),1266 ]1267 };1268 // Wrap method receivers and `&`-references in parens.1269 let suggestion = if self.tcx.sess.source_map().span_followed_by(span, ".").is_some() {1270 parenthesized_cast(span)1271 } else if let Some(body) = self.tcx.hir_maybe_body_owned_by(obligation.cause.body_def_id) {1272 let mut expr_finder = FindExprBySpan::new(span, self.tcx);1273 expr_finder.visit_expr(&body.value);1274 if let Some(expr) = expr_finder.result1275 && let hir::ExprKind::AddrOf(_, _, expr) = expr.kind1276 {1277 parenthesized_cast(expr.span)1278 } else {1279 vec![(span.shrink_to_hi(), format!(" as {}", candidate.self_ty()))]1280 }1281 } else {1282 vec![(span.shrink_to_hi(), format!(" as {}", candidate.self_ty()))]1283 };12841285 let trait_ = self.tcx.short_string(candidate.print_trait_sugared(), err.long_ty_path());1286 let self_ty = self.tcx.short_string(candidate.self_ty(), err.long_ty_path());1287 err.multipart_suggestion(1288 format!(1289 "the trait `{trait_}` is implemented for fn pointer \1290 `{self_ty}`, try casting using `as`",1291 ),1292 suggestion,1293 Applicability::MaybeIncorrect,1294 );1295 true1296 }12971298 pub(super) fn check_for_binding_assigned_block_without_tail_expression(1299 &self,1300 obligation: &PredicateObligation<'tcx>,1301 err: &mut Diag<'_>,1302 trait_pred: ty::PolyTraitClause<'tcx>,1303 ) {1304 let mut span = obligation.cause.span;1305 while span.from_expansion() {1306 // Remove all the desugaring and macro contexts.1307 span.remove_mark();1308 }1309 let mut expr_finder = FindExprBySpan::new(span, self.tcx);1310 let Some(body) = self.tcx.hir_maybe_body_owned_by(obligation.cause.body_def_id) else {1311 return;1312 };1313 expr_finder.visit_expr(&body.value);1314 let Some(expr) = expr_finder.result else {1315 return;1316 };1317 let Some(typeck) = &self.typeck_results else {1318 return;1319 };1320 let Some(ty) = typeck.expr_ty_adjusted_opt(expr) else {1321 return;1322 };1323 if !ty.is_unit() {1324 return;1325 };1326 let hir::ExprKind::Path(hir::QPath::Resolved(None, path)) = expr.kind else {1327 return;1328 };1329 let Res::Local(hir_id) = path.res else {1330 return;1331 };1332 let hir::Node::Pat(pat) = self.tcx.hir_node(hir_id) else {1333 return;1334 };1335 let hir::Node::LetStmt(hir::LetStmt { ty: None, init: Some(init), .. }) =1336 self.tcx.parent_hir_node(pat.hir_id)1337 else {1338 return;1339 };1340 let hir::ExprKind::Block(block, None) = init.kind else {1341 return;1342 };1343 if block.expr.is_some() {1344 return;1345 }1346 let [.., stmt] = block.stmts else {1347 err.span_label(block.span, "this empty block is missing a tail expression");1348 return;1349 };1350 // FIXME expr and stmt have the same span if expr comes from expansion1351 // cc: https://github.com/rust-lang/rust/pull/147416#discussion_r24994075231352 if stmt.span.from_expansion() {1353 return;1354 }1355 let hir::StmtKind::Semi(tail_expr) = stmt.kind else {1356 return;1357 };1358 let Some(ty) = typeck.expr_ty_opt(tail_expr) else {1359 err.span_label(block.span, "this block is missing a tail expression");1360 return;1361 };1362 let ty =1363 self.resolve_numeric_literals_with_default(self.deeply_resolve_ignoring_regions(ty));1364 let trait_pred_and_self = trait_pred.map_bound(|trait_pred| (trait_pred, ty));13651366 let new_obligation =1367 self.mk_trait_obligation_with_new_self_ty(obligation.param_env, trait_pred_and_self);1368 if !matches!(tail_expr.kind, hir::ExprKind::Err(_))1369 && self.predicate_must_hold_modulo_regions(&new_obligation)1370 {1371 err.span_suggestion_short(1372 stmt.span.with_lo(tail_expr.span.hi()),1373 "remove this semicolon",1374 "",1375 Applicability::MachineApplicable,1376 );1377 } else {1378 err.span_label(block.span, "this block is missing a tail expression");1379 }1380 }13811382 pub(super) fn suggest_add_clone_to_arg(1383 &self,1384 obligation: &PredicateObligation<'tcx>,1385 err: &mut Diag<'_>,1386 trait_pred: ty::PolyTraitClause<'tcx>,1387 ) -> bool {1388 let self_ty = self.deeply_resolve_ignoring_regions(trait_pred.self_ty());1389 self.enter_forall(self_ty, |ty: Ty<'_>| {1390 let Some(generics) = self.tcx.hir_get_generics(obligation.cause.body_def_id) else {1391 return false;1392 };1393 let ty::Ref(_, inner_ty, hir::Mutability::Not) = ty.kind() else { return false };1394 let ty::Param(param) = inner_ty.kind() else { return false };1395 let ObligationCauseCode::FunctionArg { arg_hir_id, .. } = obligation.cause.code()1396 else {1397 return false;1398 };13991400 let clone_trait = self.tcx.require_lang_item(LangItem::Clone, obligation.cause.span);1401 let has_clone = |ty| {1402 self.type_implements_trait(clone_trait, [ty], obligation.param_env)1403 .must_apply_modulo_regions()1404 };14051406 let existing_clone_call = match self.tcx.hir_node(*arg_hir_id) {1407 // It's just a variable. Propose cloning it.1408 Node::Expr(Expr { kind: hir::ExprKind::Path(_), .. }) => None,1409 // It's already a call to `clone()`. We might be able to suggest1410 // adding a `+ Clone` bound, though.1411 Node::Expr(Expr {1412 kind:1413 hir::ExprKind::MethodCall(1414 hir::PathSegment { ident, .. },1415 _receiver,1416 [],1417 call_span,1418 ),1419 hir_id,1420 ..1421 }) if ident.name == sym::clone1422 && !call_span.from_expansion()1423 && !has_clone(*inner_ty) =>1424 {1425 // We only care about method calls corresponding to the real `Clone` trait.1426 let Some(typeck_results) = self.typeck_results.as_ref() else { return false };1427 let Some((DefKind::AssocFn, did)) = typeck_results.type_dependent_def(*hir_id)1428 else {1429 return false;1430 };1431 if self.tcx.trait_of_assoc(did) != Some(clone_trait) {1432 return false;1433 }1434 Some(ident.span)1435 }1436 _ => return false,1437 };14381439 let new_obligation = self.mk_trait_obligation_with_new_self_ty(1440 obligation.param_env,1441 trait_pred.map_bound(|trait_pred| (trait_pred, *inner_ty)),1442 );14431444 if self.predicate_may_hold(&new_obligation) && has_clone(ty) {1445 if !has_clone(param.to_ty(self.tcx)) {1446 suggest_constraining_type_param(1447 self.tcx,1448 generics,1449 err,1450 param.name.as_str(),1451 "Clone",1452 Some(clone_trait),1453 None,1454 );1455 }1456 if let Some(existing_clone_call) = existing_clone_call {1457 err.span_note(1458 existing_clone_call,1459 format!(1460 "this `clone()` copies the reference, \1461 which does not do anything, \1462 because `{inner_ty}` does not implement `Clone`"1463 ),1464 );1465 } else {1466 err.span_suggestion_verbose(1467 obligation.cause.span.shrink_to_hi(),1468 "consider using clone here",1469 ".clone()".to_string(),1470 Applicability::MaybeIncorrect,1471 );1472 }1473 return true;1474 }1475 false1476 })1477 }14781479 /// Extracts information about a callable type for diagnostics. This is a1480 /// heuristic -- it doesn't necessarily mean that a type is always callable,1481 /// because the callable type must also be well-formed to be called.1482 pub fn extract_callable_info(1483 &self,1484 body_def_id: LocalDefId,1485 param_env: ty::ParamEnv<'tcx>,1486 found: Ty<'tcx>,1487 ) -> Option<(DefIdOrName, Ty<'tcx>, Vec<Ty<'tcx>>)> {1488 // Autoderef is useful here because sometimes we box callables, etc.1489 let Some((def_id_or_name, output, inputs)) =1490 (self.autoderef_steps)(found).into_iter().find_map(|(found, _)| match *found.kind() {1491 ty::FnPtr(sig_tys, _) => Some((1492 DefIdOrName::Name("function pointer"),1493 sig_tys.output(),1494 sig_tys.inputs(),1495 )),1496 ty::FnDef(def_id, _) => {1497 let fn_sig = found.fn_sig(self.tcx);1498 Some((DefIdOrName::DefId(def_id), fn_sig.output(), fn_sig.inputs()))1499 }1500 ty::Closure(def_id, args) => {1501 let fn_sig = args.as_closure().sig();1502 Some((1503 DefIdOrName::DefId(def_id),1504 fn_sig.output(),1505 fn_sig.inputs().map_bound(|inputs| inputs[0].tuple_fields().as_slice()),1506 ))1507 }1508 ty::CoroutineClosure(def_id, args) => {1509 let sig_parts = args.as_coroutine_closure().coroutine_closure_sig();1510 Some((1511 DefIdOrName::DefId(def_id),1512 sig_parts.map_bound(|sig| {1513 sig.to_coroutine(1514 self.tcx,1515 args.as_coroutine_closure().parent_args(),1516 // Just use infer vars here, since we don't really care1517 // what these types are, just that we're returning a coroutine.1518 self.next_ty_var(DUMMY_SP),1519 self.tcx.coroutine_for_closure(def_id),1520 self.next_ty_var(DUMMY_SP),1521 )1522 }),1523 sig_parts.map_bound(|sig| sig.tupled_inputs_ty.tuple_fields().as_slice()),1524 ))1525 }1526 ty::Alias(_, ty::AliasTy { kind: ty::Opaque { def_id }, args, .. }) => {1527 self.tcx1528 .item_self_bounds(def_id)1529 .instantiate(self.tcx, args)1530 .skip_norm_wip()1531 .iter()1532 .find_map(|pred| {1533 if let ty::ClauseKind::Projection(proj) = pred.kind().skip_binder()1534 && self1535 .tcx1536 .is_lang_item(proj.def_id(), LangItem::FnOnceOutput)1537 // args tuple will always be args[1]1538 && let ty::Tuple(args) = proj.projection_term.args.type_at(1).kind()1539 {1540 Some((1541 DefIdOrName::DefId(def_id),1542 pred.kind().rebind(proj.term.expect_type()),1543 pred.kind().rebind(args.as_slice()),1544 ))1545 } else {1546 None1547 }1548 })1549 }1550 ty::Dynamic(data, _) => data.iter().find_map(|pred| {1551 if let ty::ExistentialPredicate::Projection(proj) = pred.skip_binder()1552 && self.tcx.is_lang_item(proj.def_id, LangItem::FnOnceOutput)1553 // for existential projection, args are shifted over by 11554 && let ty::Tuple(args) = proj.args.type_at(0).kind()1555 {1556 Some((1557 DefIdOrName::Name("trait object"),1558 pred.rebind(proj.term.expect_type()),1559 pred.rebind(args.as_slice()),1560 ))1561 } else {1562 None1563 }1564 }),1565 ty::Param(param) => {1566 let generics = self.tcx.generics_of(body_def_id);1567 let name = if generics.count() > param.index as usize1568 && let def = generics.param_at(param.index as usize, self.tcx)1569 && matches!(def.kind, ty::GenericParamDefKind::Type { .. })1570 && def.name == param.name1571 {1572 DefIdOrName::DefId(def.def_id)1573 } else {1574 DefIdOrName::Name("type parameter")1575 };1576 param_env.caller_bounds().find_map(|clause| {1577 if let ty::ClauseKind::Projection(proj) = clause.kind().skip_binder()1578 && self1579 .tcx1580 .is_lang_item(proj.def_id(), LangItem::FnOnceOutput)1581 && proj.projection_term.self_ty() == found1582 // args tuple will always be args[1]1583 && let ty::Tuple(args) = proj.projection_term.args.type_at(1).kind()1584 {1585 Some((1586 name,1587 clause.kind().rebind(proj.term.expect_type()),1588 clause.kind().rebind(args.as_slice()),1589 ))1590 } else {1591 None1592 }1593 })1594 }1595 _ => None,1596 })1597 else {1598 return None;1599 };16001601 let output = self.instantiate_binder_with_fresh_vars(1602 DUMMY_SP,1603 BoundRegionConversionTime::FnCall,1604 output,1605 );1606 let inputs = inputs1607 .skip_binder()1608 .iter()1609 .map(|ty| {1610 self.instantiate_binder_with_fresh_vars(1611 DUMMY_SP,1612 BoundRegionConversionTime::FnCall,1613 inputs.rebind(*ty),1614 )1615 })1616 .collect();16171618 // We don't want to register any extra obligations, which should be1619 // implied by wf, but also because that would possibly result in1620 // erroneous errors later on.1621 let InferOk { value: output, obligations: _ } =1622 self.at(&ObligationCause::dummy(), param_env).normalize(Unnormalized::new_wip(output));16231624 if output.is_ty_var() { None } else { Some((def_id_or_name, output, inputs)) }1625 }16261627 pub(super) fn where_clause_expr_matches_failed_self_ty(1628 &self,1629 obligation: &PredicateObligation<'tcx>,1630 old_self_ty: Ty<'tcx>,1631 ) -> bool {1632 let ObligationCauseCode::WhereClauseInExpr(..) = obligation.cause.code() else {1633 return true;1634 };1635 let (Some(typeck_results), Some(body)) = (1636 self.typeck_results.as_ref(),1637 self.tcx.hir_maybe_body_owned_by(obligation.cause.body_def_id),1638 ) else {1639 return true;1640 };16411642 let mut expr_finder = FindExprBySpan::new(obligation.cause.span, self.tcx);1643 expr_finder.visit_expr(&body.value);1644 let Some(expr) = expr_finder.result else {1645 return true;1646 };16471648 let inner_old_self_ty = match old_self_ty.kind() {1649 ty::Ref(_, inner_ty, _) => Some(*inner_ty),1650 _ => None,1651 };16521653 typeck_results.expr_ty_adjusted_opt(expr).is_some_and(|expr_ty| {1654 self.can_eq(obligation.param_env, expr_ty, old_self_ty)1655 || inner_old_self_ty1656 .is_some_and(|inner_ty| self.can_eq(obligation.param_env, expr_ty, inner_ty))1657 })1658 }16591660 pub(super) fn suggest_add_reference_to_arg(1661 &self,1662 obligation: &PredicateObligation<'tcx>,1663 err: &mut Diag<'_>,1664 poly_trait_pred: ty::PolyTraitClause<'tcx>,1665 has_custom_message: bool,1666 ) -> bool {1667 let span = obligation.cause.span;1668 let param_env = obligation.param_env;16691670 let mk_result = |trait_pred_and_new_ty| {1671 let obligation =1672 self.mk_trait_obligation_with_new_self_ty(param_env, trait_pred_and_new_ty);1673 self.predicate_must_hold_modulo_regions(&obligation)1674 };16751676 let trait_pred_and_imm_ref = poly_trait_pred.map_bound(|p| {1677 (p, Ty::new_imm_ref(self.tcx, self.tcx.lifetimes.re_static, p.self_ty()))1678 });1679 let trait_pred_and_mut_ref = poly_trait_pred.map_bound(|p| {1680 (p, Ty::new_mut_ref(self.tcx, self.tcx.lifetimes.re_static, p.self_ty()))1681 });16821683 let imm_ref_self_ty_satisfies_pred = mk_result(trait_pred_and_imm_ref);1684 let mut_ref_self_ty_satisfies_pred = mk_result(trait_pred_and_mut_ref);16851686 let mut point_at_relevant_args =1687 |pred_ty: Ty<'tcx>, args_and_inputs: Vec<(hir::Expr<'_>, Ty<'tcx>)>| {1688 let Some(typeck_results) = &self.typeck_results else { return false };16891690 let erased_self_ty =1691 self.tcx.instantiate_bound_regions_with_erased(poly_trait_pred.self_ty());1692 let mut spans = vec![];1693 for (arg, input) in args_and_inputs {1694 let Some(arg_ty) = typeck_results.expr_ty_adjusted_opt(&arg) else { continue };1695 let pred_has_arg_type = self.infcx.can_eq(param_env, arg_ty, erased_self_ty);1696 let arg_is_type_param = self.infcx.can_eq(param_env, pred_ty, input);1697 if pred_has_arg_type && arg_is_type_param {1698 err.span_label(1699 arg.span,1700 format!("`{arg_ty}` doesn't satisfy the trait bound"),1701 );1702 spans.push(arg.span);1703 }1704 }1705 let this = pluralize!("this", spans.len());1706 if !spans.is_empty() {1707 if imm_ref_self_ty_satisfies_pred {1708 err.multipart_suggestion(1709 format!("consider borrowing {this} argument"),1710 spans.iter().map(|sp| (sp.shrink_to_lo(), "&".into())).collect(),1711 Applicability::MaybeIncorrect,1712 );1713 }1714 if mut_ref_self_ty_satisfies_pred {1715 err.multipart_suggestion(1716 format!("consider mutably borrowing {this} argument"),1717 spans.iter().map(|sp| (sp.shrink_to_lo(), "&mut ".into())).collect(),1718 Applicability::MaybeIncorrect,1719 );1720 }1721 }1722 !spans.is_empty()1723 };1724 let code = match obligation.cause.code() {1725 ObligationCauseCode::FunctionArg { parent_code, .. } => parent_code,1726 // FIXME(compiler-errors): This is kind of a mess, but required for obligations1727 // that come from a path expr to affect the *call* expr.1728 c @ ObligationCauseCode::WhereClauseInExpr(def_id, _, hir_id, idx)1729 if self.tcx.hir_span(*hir_id).lo() == span.lo() =>1730 {1731 // `hir_id` corresponds to the HIR node that introduced a `where`-clause obligation.1732 if let hir::Node::Expr(expr) = self.tcx.parent_hir_node(*hir_id) {1733 // If that obligation comes from a type in an associated method call, we need1734 // special handling here.1735 if let hir::ExprKind::Call(base, _) = expr.kind1736 && let hir::ExprKind::Path(hir::QPath::TypeRelative(ty, segment)) =1737 base.kind1738 && let hir::Node::Expr(outer) = self.tcx.parent_hir_node(expr.hir_id)1739 && let hir::ExprKind::AddrOf(hir::BorrowKind::Ref, mtbl, _) = outer.kind1740 && ty.span == span1741 {1742 // We've encountered something like `&str::from("")`, where the intended code1743 // was likely `<&str>::from("")`. The former is interpreted as "call method1744 // `from` on `str` and borrow the result", while the latter means "call method1745 // `from` on `&str`".17461747 let sugg_msg = |pre: &str| {1748 format!(1749 "you likely meant to call the associated function `{FN}` for type \1750 `&{pre}{TY}`, but the code as written calls associated function `{FN}` on \1751 type `{TY}`",1752 FN = segment.ident,1753 TY = poly_trait_pred.self_ty(),1754 )1755 };1756 match (imm_ref_self_ty_satisfies_pred, mut_ref_self_ty_satisfies_pred, mtbl)1757 {1758 (true, _, hir::Mutability::Not) | (_, true, hir::Mutability::Mut) => {1759 err.multipart_suggestion(1760 sugg_msg(mtbl.prefix_str()),1761 vec![1762 (outer.span.shrink_to_lo(), "<".to_string()),1763 (span.shrink_to_hi(), ">".to_string()),1764 ],1765 Applicability::MachineApplicable,1766 );1767 }1768 (true, _, hir::Mutability::Mut) => {1769 // There's an associated function found on the immutable borrow of the1770 err.multipart_suggestion(1771 sugg_msg("mut "),1772 vec![1773 (outer.span.shrink_to_lo().until(span), "<&".to_string()),1774 (span.shrink_to_hi(), ">".to_string()),1775 ],1776 Applicability::MachineApplicable,1777 );1778 }1779 (_, true, hir::Mutability::Not) => {1780 err.multipart_suggestion(1781 sugg_msg(""),1782 vec![1783 (1784 outer.span.shrink_to_lo().until(span),1785 "<&mut ".to_string(),1786 ),1787 (span.shrink_to_hi(), ">".to_string()),1788 ],1789 Applicability::MachineApplicable,1790 );1791 }1792 _ => {}1793 }1794 // If we didn't return early here, we would instead suggest `&&str::from("")`.1795 return false;1796 } else if let hir::ExprKind::Call(_, args) = expr.kind {1797 // The `def_id` can point at a struct, which has no fn sig.1798 if matches!(1799 self.tcx.def_kind(*def_id),1800 DefKind::AssocFn | DefKind::Fn | DefKind::Ctor(_, CtorKind::Fn)1801 ) && let Some(pred) = self1802 .tcx1803 .clauses_of(*def_id)1804 .instantiate_identity(self.tcx)1805 .clauses1806 .into_iter()1807 .nth(*idx)1808 && let Some(pred) = pred.as_trait_clause()1809 // This feature allows for `for<T> T: Trait`, which fails1810 // `instantiate_bound_regions_with_erased`. Avoid suggesting for now.1811 && !self.tcx.features().non_lifetime_binders()1812 {1813 let pred_ty = self.tcx.instantiate_bound_regions_with_erased(1814 pred.self_ty().skip_norm_wip(),1815 );1816 let fn_sig = self.tcx.instantiate_bound_regions_with_erased(1817 self.tcx.fn_sig(*def_id).instantiate_identity().skip_norm_wip(),1818 );1819 if point_at_relevant_args(1820 pred_ty,1821 args.into_iter()1822 .zip(fn_sig.inputs())1823 .map(|(e, t)| (*e, *t))1824 .collect(),1825 ) {1826 return false;1827 }1828 }1829 }1830 }1831 c1832 }1833 c @ ObligationCauseCode::WhereClauseInExpr(def_id, _, hir_id, idx)1834 if let hir::Node::Expr(expr) = self.tcx.hir_node(*hir_id)1835 && let hir::ExprKind::MethodCall(_segment, rcvr, args, ..) = expr.kind1836 // The `def_id` can also point at the impl, which has no fn sig.1837 && matches!(1838 self.tcx.def_kind(*def_id),1839 DefKind::AssocFn | DefKind::Fn | DefKind::Ctor(_, CtorKind::Fn)1840 )1841 && let Some(pred) = self1842 .tcx1843 .clauses_of(*def_id)1844 .instantiate_identity(self.tcx)1845 .clauses1846 .into_iter()1847 .nth(*idx)1848 && let Some(pred) = pred.as_trait_clause()1849 // This feature allows for `for<T> T: Trait`, which fails1850 // `instantiate_bound_regions_with_erased`. Avoid suggesting for now.1851 && !self.tcx.features().non_lifetime_binders() =>1852 {1853 let fn_sig = self.tcx.instantiate_bound_regions_with_erased(1854 self.tcx.fn_sig(*def_id).instantiate_identity().skip_norm_wip(),1855 );1856 // We've got a method call where likely one of the arguments didn't meet a bound.1857 let pred_ty =1858 self.tcx.instantiate_bound_regions_with_erased(pred.self_ty().skip_norm_wip());1859 if point_at_relevant_args(1860 pred_ty,1861 [rcvr]1862 .into_iter()1863 .chain(args.into_iter())1864 .zip(fn_sig.inputs())1865 .map(|(e, t)| (*e, *t))1866 .collect(),1867 ) {1868 return false;1869 }1870 c1871 }1872 c if matches!(1873 span.ctxt().outer_expn_data().kind,1874 ExpnKind::Desugaring(DesugaringKind::ForLoop)1875 ) =>1876 {1877 c1878 }1879 _ => return false,1880 };18811882 // List of traits for which it would be nonsensical to suggest borrowing.1883 // For instance, immutable references are always Copy, so suggesting to1884 // borrow would always succeed, but it's probably not what the user wanted.1885 let mut never_suggest_borrow: Vec<_> =1886 [LangItem::Copy, LangItem::Clone, LangItem::Unpin, LangItem::Sized]1887 .iter()1888 .filter_map(|lang_item| self.tcx.lang_items().get(*lang_item))1889 .collect();18901891 if let Some(def_id) = self.tcx.get_diagnostic_item(sym::Send) {1892 never_suggest_borrow.push(def_id);1893 }18941895 // Try to apply the original trait bound by borrowing.1896 let mut try_borrowing = |old_pred: ty::PolyTraitClause<'tcx>,1897 blacklist: &[DefId]|1898 -> bool {1899 if blacklist.contains(&old_pred.def_id()) {1900 return false;1901 }1902 // We map bounds to `&T` and `&mut T`1903 let trait_pred_and_imm_ref = old_pred.map_bound(|trait_pred| {1904 (1905 trait_pred,1906 Ty::new_imm_ref(self.tcx, self.tcx.lifetimes.re_static, trait_pred.self_ty()),1907 )1908 });1909 let trait_pred_and_mut_ref = old_pred.map_bound(|trait_pred| {1910 (1911 trait_pred,1912 Ty::new_mut_ref(self.tcx, self.tcx.lifetimes.re_static, trait_pred.self_ty()),1913 )1914 });19151916 let imm_ref_self_ty_satisfies_pred = mk_result(trait_pred_and_imm_ref);1917 let mut_ref_self_ty_satisfies_pred = mk_result(trait_pred_and_mut_ref);19181919 let (ref_inner_ty_satisfies_pred, ref_inner_ty_is_mut) =1920 if let ObligationCauseCode::WhereClauseInExpr(..) = obligation.cause.code()1921 && let ty::Ref(_, ty, mutability) = old_pred.self_ty().skip_binder().kind()1922 {1923 (1924 mk_result(old_pred.map_bound(|trait_pred| (trait_pred, *ty))),1925 mutability.is_mut(),1926 )1927 } else {1928 (false, false)1929 };19301931 let is_immut = imm_ref_self_ty_satisfies_pred1932 || (ref_inner_ty_satisfies_pred && !ref_inner_ty_is_mut);1933 let is_mut = mut_ref_self_ty_satisfies_pred || ref_inner_ty_is_mut;1934 if !is_immut && !is_mut {1935 return false;1936 }1937 let Ok(_snippet) = self.tcx.sess.source_map().span_to_snippet(span) else {1938 return false;1939 };1940 // We don't want a borrowing suggestion on the fields in structs1941 // ```1942 // #[derive(Clone)]1943 // struct Foo {1944 // the_foos: Vec<Foo>1945 // }1946 // ```1947 if !matches!(1948 span.ctxt().outer_expn_data().kind,1949 ExpnKind::Root | ExpnKind::Desugaring(DesugaringKind::ForLoop)1950 ) {1951 return false;1952 }1953 // We have a very specific type of error, where just borrowing this argument1954 // might solve the problem. In cases like this, the important part is the1955 // original type obligation, not the last one that failed, which is arbitrary.1956 // Because of this, we modify the error to refer to the original obligation and1957 // return early in the caller.19581959 let mut label = || {1960 // Special case `Sized` as `old_pred` will be the trait itself instead of1961 // `Sized` when the trait bound is the source of the error.1962 let is_sized = match obligation.predicate.kind().skip_binder() {1963 ty::PredicateKind::Clause(ty::ClauseKind::Trait(trait_pred)) => {1964 self.tcx.is_lang_item(trait_pred.def_id(), LangItem::Sized)1965 }1966 _ => false,1967 };19681969 let msg = format!(1970 "the trait bound `{}` is not satisfied",1971 self.tcx.short_string(old_pred, err.long_ty_path()),1972 );1973 let self_ty_str = self.tcx.short_string(old_pred.self_ty(), err.long_ty_path());1974 let trait_path = self1975 .tcx1976 .short_string(old_pred.print_modifiers_and_trait_path(), err.long_ty_path());19771978 if has_custom_message {1979 let msg = if is_sized {1980 "the trait bound `Sized` is not satisfied".into()1981 } else {1982 msg1983 };1984 err.note(msg);1985 } else {1986 err.messages = vec![(rustc_errors::DiagMessage::from(msg), Style::NoStyle)];1987 }1988 if is_sized {1989 err.span_label(1990 span,1991 format!("the trait `Sized` is not implemented for `{self_ty_str}`"),1992 );1993 } else {1994 err.span_label(1995 span,1996 format!("the trait `{trait_path}` is not implemented for `{self_ty_str}`"),1997 );1998 }1999 };
Findings
✓ No findings reported for this file.