1use rustc_ast::TraitObjectSyntax;
2use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet};
3use rustc_errors::codes::*;
4use rustc_errors::{
5 Applicability, Diag, DiagCtxtHandle, Diagnostic, EmissionGuarantee, Level, StashKey,
6 Suggestions, struct_span_code_err,
7};
8use rustc_hir::def::{DefKind, Res};
9use rustc_hir::def_id::DefId;
10use rustc_hir::{self as hir, HirId, LangItem};
11use rustc_lint_defs::builtin::{BARE_TRAIT_OBJECTS, UNUSED_ASSOCIATED_TYPE_BOUNDS};
12use rustc_middle::ty::elaborate::ClauseWithSupertraitSpan;
13use rustc_middle::ty::{
14 self, BottomUpFolder, ExistentialPredicateStableCmpExt as _, Ty, TyCtxt, TypeFoldable,
15 TypeVisitableExt, Upcast,
16};
17use rustc_span::edit_distance::find_best_match_for_name;
18use rustc_span::{ErrorGuaranteed, Span};
19use rustc_trait_selection::error_reporting::traits::report_dyn_incompatibility;
20use rustc_trait_selection::error_reporting::traits::suggestions::NextTypeParamName;
21use rustc_trait_selection::traits;
22use smallvec::{SmallVec, smallvec};
23use tracing::{debug, instrument};
24
25use super::HirTyLowerer;
26use crate::errors::DynTraitAssocItemBindingMentionsSelf;
27use crate::hir_ty_lowering::{
28 GenericArgCountMismatch, ImpliedBoundsContext, OverlappingAsssocItemConstraints,
29 PredicateFilter, RegionInferReason,
30};
31
32impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
33 x;#[instrument(level = "debug", skip_all, ret)]
35 pub(super) fn lower_trait_object_ty(
36 &self,
37 span: Span,
38 hir_id: hir::HirId,
39 hir_bounds: &[hir::PolyTraitRef<'tcx>],
40 lifetime: &hir::Lifetime,
41 syntax: TraitObjectSyntax,
42 ) -> Ty<'tcx> {
43 let tcx = self.tcx();
44 let dummy_self = tcx.types.trait_object_dummy_self;
45
46 match syntax {
47 TraitObjectSyntax::Dyn => {}
48 TraitObjectSyntax::None => {
49 match self.prohibit_or_lint_bare_trait_object_ty(span, hir_id, hir_bounds) {
50 Some(guar) => return Ty::new_error(tcx, guar),
54 None => {}
55 }
56 }
57 }
58
59 let mut user_written_bounds = Vec::new();
60 let mut potential_assoc_items = Vec::new();
61 for poly_trait_ref in hir_bounds.iter() {
62 let result = self.lower_poly_trait_ref(
68 poly_trait_ref,
69 dummy_self,
70 &mut user_written_bounds,
71 PredicateFilter::SelfOnly,
72 OverlappingAsssocItemConstraints::Forbidden,
73 );
74 if let Err(GenericArgCountMismatch { invalid_args, .. }) = result.correct {
75 potential_assoc_items.extend(invalid_args);
76 }
77 }
78
79 self.add_default_traits(
80 &mut user_written_bounds,
81 dummy_self,
82 &hir_bounds
83 .iter()
84 .map(|&trait_ref| hir::GenericBound::Trait(trait_ref))
85 .collect::<Vec<_>>(),
86 ImpliedBoundsContext::AssociatedTypeOrImplTrait,
87 span,
88 );
89
90 let (mut elaborated_trait_bounds, elaborated_projection_bounds) =
91 traits::expand_trait_aliases(tcx, user_written_bounds.iter().copied());
92
93 debug!(?user_written_bounds, ?elaborated_trait_bounds);
95 let meta_sized_did = tcx.require_lang_item(LangItem::MetaSized, span);
96 if user_written_bounds
99 .iter()
100 .all(|(clause, _)| clause.as_trait_clause().map(|p| p.def_id()) != Some(meta_sized_did))
101 {
102 elaborated_trait_bounds.retain(|(pred, _)| pred.def_id() != meta_sized_did);
103 }
104 debug!(?user_written_bounds, ?elaborated_trait_bounds);
105
106 let (regular_traits, mut auto_traits): (Vec<_>, Vec<_>) = elaborated_trait_bounds
107 .into_iter()
108 .partition(|(trait_ref, _)| !tcx.trait_is_auto(trait_ref.def_id()));
109
110 if regular_traits.is_empty() && auto_traits.is_empty() {
112 let guar =
113 self.report_trait_object_with_no_traits(span, user_written_bounds.iter().copied());
114 return Ty::new_error(tcx, guar);
115 }
116 if regular_traits.len() > 1 {
118 let guar = self.report_trait_object_addition_traits(®ular_traits);
119 return Ty::new_error(tcx, guar);
120 }
121 if let Err(guar) = regular_traits.error_reported() {
123 return Ty::new_error(tcx, guar);
124 }
125
126 for (clause, span) in user_written_bounds {
130 if let Some(trait_pred) = clause.as_trait_clause() {
131 let violations = self.dyn_compatibility_violations(trait_pred.def_id());
132 if !violations.is_empty() {
133 let reported = report_dyn_incompatibility(
134 tcx,
135 span,
136 Some(hir_id),
137 trait_pred.def_id(),
138 &violations,
139 )
140 .emit();
141 return Ty::new_error(tcx, reported);
142 }
143 }
144 }
145
146 let mut projection_bounds = FxIndexMap::default();
156 for (proj, proj_span) in elaborated_projection_bounds {
157 let item_def_id = proj.item_def_id();
158
159 let proj = proj.map_bound(|mut proj| {
160 let references_self = proj.term.walk().any(|arg| arg == dummy_self.into());
161 if references_self {
162 let guar = self.dcx().emit_err(DynTraitAssocItemBindingMentionsSelf {
163 span,
164 kind: tcx.def_descr(item_def_id),
165 binding: proj_span,
166 });
167 proj.term = replace_dummy_self_with_error(tcx, proj.term, guar);
168 }
169 proj
170 });
171
172 let key = (
173 item_def_id,
174 tcx.anonymize_bound_vars(
175 proj.map_bound(|proj| proj.projection_term.trait_ref(tcx)),
176 ),
177 );
178 if let Some((old_proj, old_proj_span)) =
179 projection_bounds.insert(key, (proj, proj_span))
180 && tcx.anonymize_bound_vars(proj) != tcx.anonymize_bound_vars(old_proj)
181 {
182 let kind = tcx.def_descr(item_def_id);
183 let name = tcx.item_name(item_def_id);
184 self.dcx()
185 .struct_span_err(span, format!("conflicting {kind} bindings for `{name}`"))
186 .with_span_label(
187 old_proj_span,
188 format!("`{name}` is specified to be `{}` here", old_proj.term()),
189 )
190 .with_span_label(
191 proj_span,
192 format!("`{name}` is specified to be `{}` here", proj.term()),
193 )
194 .emit();
195 }
196 }
197
198 let principal_trait = regular_traits.into_iter().next();
199
200 let mut ordered_associated_items = vec![];
206
207 if let Some((principal_trait, ref spans)) = principal_trait {
208 let principal_trait = principal_trait.map_bound(|trait_pred| {
209 assert_eq!(trait_pred.polarity, ty::PredicatePolarity::Positive);
210 trait_pred.trait_ref
211 });
212
213 for ClauseWithSupertraitSpan { clause, supertrait_span } in traits::elaborate(
214 tcx,
215 [ClauseWithSupertraitSpan::new(
216 ty::TraitRef::identity(tcx, principal_trait.def_id()).upcast(tcx),
217 *spans.last().unwrap(),
218 )],
219 )
220 .filter_only_self()
221 {
222 let clause = clause.instantiate_supertrait(tcx, principal_trait);
223 debug!("observing object predicate `{clause:?}`");
224
225 let bound_predicate = clause.kind();
226 match bound_predicate.skip_binder() {
227 ty::ClauseKind::Trait(pred) => {
228 let trait_ref =
230 tcx.anonymize_bound_vars(bound_predicate.rebind(pred.trait_ref));
231 ordered_associated_items.extend(
232 tcx.associated_items(pred.trait_ref.def_id)
233 .in_definition_order()
234 .filter(|item| item.is_type() || item.is_type_const())
237 .filter(|item| !item.is_impl_trait_in_trait())
239 .map(|item| (item.def_id, trait_ref)),
240 );
241 }
242 ty::ClauseKind::Projection(pred) => {
243 let pred = bound_predicate.rebind(pred);
244 let references_self =
247 pred.skip_binder().term.walk().any(|arg| arg == dummy_self.into());
248
249 if !references_self {
267 let key = (
268 pred.item_def_id(),
269 tcx.anonymize_bound_vars(
270 pred.map_bound(|proj| proj.projection_term.trait_ref(tcx)),
271 ),
272 );
273 if !projection_bounds.contains_key(&key) {
274 projection_bounds.insert(key, (pred, supertrait_span));
275 }
276 }
277
278 self.check_elaborated_projection_mentions_input_lifetimes(
279 pred,
280 *spans.first().unwrap(),
281 supertrait_span,
282 );
283 }
284 _ => (),
285 }
286 }
287 }
288
289 for &(projection_bound, span) in projection_bounds.values() {
291 let def_id = projection_bound.item_def_id();
292 if tcx.generics_require_sized_self(def_id) {
293 tcx.emit_node_span_lint(
297 UNUSED_ASSOCIATED_TYPE_BOUNDS,
298 hir_id,
299 span,
300 crate::errors::UnusedAssociatedTypeBounds { span },
301 );
302 }
303 }
304
305 let mut missing_assoc_items = FxIndexSet::default();
317 let projection_bounds: Vec<_> = ordered_associated_items
318 .into_iter()
319 .filter_map(|key @ (def_id, _)| {
320 if let Some(&assoc) = projection_bounds.get(&key) {
321 return Some(assoc);
322 }
323 if !tcx.generics_require_sized_self(def_id) {
324 missing_assoc_items.insert(key);
325 }
326 None
327 })
328 .collect();
329
330 if let Err(guar) = self.check_for_required_assoc_items(
332 principal_trait.as_ref().map_or(smallvec![], |(_, spans)| spans.clone()),
333 missing_assoc_items,
334 potential_assoc_items,
335 hir_bounds,
336 ) {
337 return Ty::new_error(tcx, guar);
338 }
339
340 let mut duplicates = FxHashSet::default();
345 auto_traits.retain(|(trait_pred, _)| duplicates.insert(trait_pred.def_id()));
346
347 debug!(?principal_trait);
348 debug!(?auto_traits);
349
350 let principal_trait_ref = principal_trait.map(|(trait_pred, spans)| {
352 trait_pred.map_bound(|trait_pred| {
353 let trait_ref = trait_pred.trait_ref;
354 assert_eq!(trait_pred.polarity, ty::PredicatePolarity::Positive);
355 assert_eq!(trait_ref.self_ty(), dummy_self);
356
357 let span = *spans.first().unwrap();
358
359 let mut missing_generic_params = Vec::new();
362 let generics = tcx.generics_of(trait_ref.def_id);
363 let args: Vec<_> = trait_ref
364 .args
365 .iter()
366 .enumerate()
367 .skip(1)
369 .map(|(index, arg)| {
370 if arg.walk().any(|arg| arg == dummy_self.into()) {
371 let param = &generics.own_params[index];
372 missing_generic_params.push((param.name, param.kind.clone()));
373 param.to_error(tcx)
374 } else {
375 arg
376 }
377 })
378 .collect();
379
380 let empty_generic_args = hir_bounds.iter().any(|hir_bound| {
381 hir_bound.trait_ref.path.res == Res::Def(DefKind::Trait, trait_ref.def_id)
382 && hir_bound.span.contains(span)
383 });
384 self.report_missing_generic_params(
385 missing_generic_params,
386 trait_ref.def_id,
387 span,
388 empty_generic_args,
389 );
390
391 ty::ExistentialPredicate::Trait(ty::ExistentialTraitRef::new(
392 tcx,
393 trait_ref.def_id,
394 args,
395 ))
396 })
397 });
398
399 let existential_projections = projection_bounds.into_iter().map(|(bound, _)| {
400 bound.map_bound(|mut b| {
401 assert_eq!(b.projection_term.self_ty(), dummy_self);
402
403 let references_self = b.projection_term.args.iter().skip(1).any(|arg| {
406 if arg.walk().any(|arg| arg == dummy_self.into()) {
407 return true;
408 }
409 false
410 });
411 if references_self {
412 let guar = tcx
413 .dcx()
414 .span_delayed_bug(span, "trait object projection bounds reference `Self`");
415 b.projection_term = replace_dummy_self_with_error(tcx, b.projection_term, guar);
416 }
417
418 ty::ExistentialPredicate::Projection(ty::ExistentialProjection::erase_self_ty(
419 tcx, b,
420 ))
421 })
422 });
423
424 let mut auto_trait_predicates: Vec<_> = auto_traits
425 .into_iter()
426 .map(|(trait_pred, _)| {
427 assert_eq!(trait_pred.polarity(), ty::PredicatePolarity::Positive);
428 assert_eq!(trait_pred.self_ty().skip_binder(), dummy_self);
429
430 ty::Binder::dummy(ty::ExistentialPredicate::AutoTrait(trait_pred.def_id()))
431 })
432 .collect();
433 auto_trait_predicates.dedup();
434
435 let mut v = principal_trait_ref
438 .into_iter()
439 .chain(existential_projections)
440 .chain(auto_trait_predicates)
441 .collect::<SmallVec<[_; 8]>>();
442 v.sort_by(|a, b| a.skip_binder().stable_cmp(tcx, &b.skip_binder()));
443 let existential_predicates = tcx.mk_poly_existential_predicates(&v);
444
445 let region_bound = if !lifetime.is_elided() {
447 self.lower_lifetime(lifetime, RegionInferReason::ExplicitObjectLifetime)
448 } else {
449 self.compute_object_lifetime_bound(span, existential_predicates).unwrap_or_else(|| {
450 if tcx.named_bound_var(lifetime.hir_id).is_some() {
452 self.lower_lifetime(lifetime, RegionInferReason::ExplicitObjectLifetime)
453 } else {
454 let reason =
455 if let hir::LifetimeKind::ImplicitObjectLifetimeDefault = lifetime.kind {
456 RegionInferReason::ObjectLifetimeDefault(span.shrink_to_hi())
457 } else {
458 RegionInferReason::ExplicitObjectLifetime
459 };
460 self.re_infer(span, reason)
461 }
462 })
463 };
464 debug!(?region_bound);
465
466 Ty::new_dynamic(tcx, existential_predicates, region_bound)
467 }
468
469 fn check_elaborated_projection_mentions_input_lifetimes(
474 &self,
475 pred: ty::PolyProjectionPredicate<'tcx>,
476 span: Span,
477 supertrait_span: Span,
478 ) {
479 let tcx = self.tcx();
480
481 let late_bound_in_projection_term =
489 tcx.collect_constrained_late_bound_regions(pred.map_bound(|pred| pred.projection_term));
490 let late_bound_in_term =
491 tcx.collect_referenced_late_bound_regions(pred.map_bound(|pred| pred.term));
492 {
use ::tracing::__macro_support::Callsite as _;
static __CALLSITE: ::tracing::callsite::DefaultCallsite =
{
static META: ::tracing::Metadata<'static> =
{
::tracing_core::metadata::Metadata::new("event compiler/rustc_hir_analysis/src/hir_ty_lowering/dyn_trait.rs:492",
"rustc_hir_analysis::hir_ty_lowering::dyn_trait",
::tracing::Level::DEBUG,
::tracing_core::__macro_support::Option::Some("compiler/rustc_hir_analysis/src/hir_ty_lowering/dyn_trait.rs"),
::tracing_core::__macro_support::Option::Some(492u32),
::tracing_core::__macro_support::Option::Some("rustc_hir_analysis::hir_ty_lowering::dyn_trait"),
::tracing_core::field::FieldSet::new(&["late_bound_in_projection_term"],
::tracing_core::callsite::Identifier(&__CALLSITE)),
::tracing::metadata::Kind::EVENT)
};
::tracing::callsite::DefaultCallsite::new(&META)
};
let enabled =
::tracing::Level::DEBUG <= ::tracing::level_filters::STATIC_MAX_LEVEL
&&
::tracing::Level::DEBUG <=
::tracing::level_filters::LevelFilter::current() &&
{
let interest = __CALLSITE.interest();
!interest.is_never() &&
::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
interest)
};
if enabled {
(|value_set: ::tracing::field::ValueSet|
{
let meta = __CALLSITE.metadata();
::tracing::Event::dispatch(meta, &value_set);
;
})({
#[allow(unused_imports)]
use ::tracing::field::{debug, display, Value};
let mut iter = __CALLSITE.metadata().fields().iter();
__CALLSITE.metadata().fields().value_set(&[(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
::tracing::__macro_support::Option::Some(&debug(&late_bound_in_projection_term)
as &dyn Value))])
});
} else { ; }
};debug!(?late_bound_in_projection_term);
493 {
use ::tracing::__macro_support::Callsite as _;
static __CALLSITE: ::tracing::callsite::DefaultCallsite =
{
static META: ::tracing::Metadata<'static> =
{
::tracing_core::metadata::Metadata::new("event compiler/rustc_hir_analysis/src/hir_ty_lowering/dyn_trait.rs:493",
"rustc_hir_analysis::hir_ty_lowering::dyn_trait",
::tracing::Level::DEBUG,
::tracing_core::__macro_support::Option::Some("compiler/rustc_hir_analysis/src/hir_ty_lowering/dyn_trait.rs"),
::tracing_core::__macro_support::Option::Some(493u32),
::tracing_core::__macro_support::Option::Some("rustc_hir_analysis::hir_ty_lowering::dyn_trait"),
::tracing_core::field::FieldSet::new(&["late_bound_in_term"],
::tracing_core::callsite::Identifier(&__CALLSITE)),
::tracing::metadata::Kind::EVENT)
};
::tracing::callsite::DefaultCallsite::new(&META)
};
let enabled =
::tracing::Level::DEBUG <= ::tracing::level_filters::STATIC_MAX_LEVEL
&&
::tracing::Level::DEBUG <=
::tracing::level_filters::LevelFilter::current() &&
{
let interest = __CALLSITE.interest();
!interest.is_never() &&
::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
interest)
};
if enabled {
(|value_set: ::tracing::field::ValueSet|
{
let meta = __CALLSITE.metadata();
::tracing::Event::dispatch(meta, &value_set);
;
})({
#[allow(unused_imports)]
use ::tracing::field::{debug, display, Value};
let mut iter = __CALLSITE.metadata().fields().iter();
__CALLSITE.metadata().fields().value_set(&[(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
::tracing::__macro_support::Option::Some(&debug(&late_bound_in_term)
as &dyn Value))])
});
} else { ; }
};debug!(?late_bound_in_term);
494
495 self.validate_late_bound_regions(
500 late_bound_in_projection_term,
501 late_bound_in_term,
502 |br_name| {
503 let item_name = tcx.item_name(pred.item_def_id());
504 {
self.dcx().struct_span_err(span,
::alloc::__export::must_use({
::alloc::fmt::format(format_args!("binding for associated type `{0}` references {1}, which does not appear in the trait input types",
item_name, br_name))
})).with_code(E0582)
}struct_span_code_err!(
505 self.dcx(),
506 span,
507 E0582,
508 "binding for associated type `{}` references {}, \
509 which does not appear in the trait input types",
510 item_name,
511 br_name
512 )
513 .with_span_label(supertrait_span, "due to this supertrait")
514 },
515 );
516 }
517
518 x;#[instrument(level = "debug", skip(self, span), ret)]
526 fn compute_object_lifetime_bound(
527 &self,
528 span: Span,
529 existential_predicates: &'tcx ty::List<ty::PolyExistentialPredicate<'tcx>>,
530 ) -> Option<ty::Region<'tcx>> {
532 let tcx = self.tcx();
533
534 let derived_region_bounds = traits::wf::object_region_bounds(tcx, existential_predicates);
537
538 if derived_region_bounds.is_empty() {
541 return None;
542 }
543
544 if derived_region_bounds.iter().any(|r| r.is_static()) {
547 return Some(tcx.lifetimes.re_static);
548 }
549
550 let r = derived_region_bounds[0];
554 if derived_region_bounds[1..].iter().any(|r1| r != *r1) {
555 self.dcx().emit_err(crate::errors::AmbiguousLifetimeBound { span });
556 }
557 Some(r)
558 }
559
560 fn prohibit_or_lint_bare_trait_object_ty(
565 &self,
566 span: Span,
567 hir_id: hir::HirId,
568 hir_bounds: &[hir::PolyTraitRef<'tcx>],
569 ) -> Option<ErrorGuaranteed> {
570 struct TraitObjectWithoutDyn<'a, 'tcx> {
571 span: Span,
572 hir_id: HirId,
573 sugg: Vec<(Span, String)>,
574 this: &'a dyn HirTyLowerer<'tcx>,
575 }
576
577 impl<'a, 'b, 'tcx> Diagnostic<'a, ()> for TraitObjectWithoutDyn<'b, 'tcx> {
578 fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, ()> {
579 let Self { span, hir_id, sugg, this } = self;
580 let mut lint =
581 Diag::new(dcx, level, "trait objects without an explicit `dyn` are deprecated");
582 if span.can_be_used_for_suggestions() {
583 lint.multipart_suggestion(
584 "if this is a dyn-compatible trait, use `dyn`",
585 sugg,
586 Applicability::MachineApplicable,
587 );
588 }
589 this.maybe_suggest_blanket_trait_impl(span, hir_id, &mut lint);
590 lint
591 }
592 }
593
594 let tcx = self.tcx();
595 let [poly_trait_ref, ..] = hir_bounds else { return None };
596
597 let in_path = match tcx.parent_hir_node(hir_id) {
598 hir::Node::Ty(hir::Ty {
599 kind: hir::TyKind::Path(hir::QPath::TypeRelative(qself, _)),
600 ..
601 })
602 | hir::Node::Expr(hir::Expr {
603 kind: hir::ExprKind::Path(hir::QPath::TypeRelative(qself, _)),
604 ..
605 })
606 | hir::Node::PatExpr(hir::PatExpr {
607 kind: hir::PatExprKind::Path(hir::QPath::TypeRelative(qself, _)),
608 ..
609 }) if qself.hir_id == hir_id => true,
610 _ => false,
611 };
612 let needs_bracket = in_path
613 && !tcx
614 .sess
615 .source_map()
616 .span_to_prev_source(span)
617 .ok()
618 .is_some_and(|s| s.trim_end().ends_with('<'));
619
620 let is_global = poly_trait_ref.trait_ref.path.is_global();
621
622 let mut sugg = ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[(span.shrink_to_lo(),
::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0}dyn {1}",
if needs_bracket { "<" } else { "" },
if is_global { "(" } else { "" }))
}))]))vec![(
623 span.shrink_to_lo(),
624 format!(
625 "{}dyn {}",
626 if needs_bracket { "<" } else { "" },
627 if is_global { "(" } else { "" },
628 ),
629 )];
630
631 if is_global || needs_bracket {
632 sugg.push((
633 span.shrink_to_hi(),
634 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0}{1}",
if is_global { ")" } else { "" },
if needs_bracket { ">" } else { "" }))
})format!(
635 "{}{}",
636 if is_global { ")" } else { "" },
637 if needs_bracket { ">" } else { "" },
638 ),
639 ));
640 }
641
642 if span.edition().at_least_rust_2021() {
643 let mut diag = {
self.dcx().struct_span_err(span,
::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0}",
"expected a type, found a trait"))
})).with_code(E0782)
}rustc_errors::struct_span_code_err!(
644 self.dcx(),
645 span,
646 E0782,
647 "{}",
648 "expected a type, found a trait"
649 );
650 if span.can_be_used_for_suggestions()
651 && poly_trait_ref.trait_ref.trait_def_id().is_some()
652 && !self.maybe_suggest_impl_trait(span, hir_id, hir_bounds, &mut diag)
653 && !self.maybe_suggest_dyn_trait(hir_id, span, sugg, &mut diag)
654 {
655 self.maybe_suggest_add_generic_impl_trait(span, hir_id, &mut diag);
656 }
657 self.maybe_suggest_blanket_trait_impl(span, hir_id, &mut diag);
659 self.maybe_suggest_assoc_ty_bound(hir_id, &mut diag);
660 self.maybe_suggest_typoed_method(
661 hir_id,
662 poly_trait_ref.trait_ref.trait_def_id(),
663 &mut diag,
664 );
665 if let Some(mut sugg) =
668 self.dcx().steal_non_err(span, StashKey::AssociatedTypeSuggestion)
669 && let Suggestions::Enabled(ref mut s1) = diag.suggestions
670 && let Suggestions::Enabled(ref mut s2) = sugg.suggestions
671 {
672 s1.append(s2);
673 sugg.cancel();
674 }
675 Some(diag.emit())
676 } else {
677 tcx.emit_node_span_lint(
678 BARE_TRAIT_OBJECTS,
679 hir_id,
680 span,
681 TraitObjectWithoutDyn { span, hir_id, sugg, this: self },
682 );
683 None
684 }
685 }
686
687 fn maybe_suggest_add_generic_impl_trait(
690 &self,
691 span: Span,
692 hir_id: hir::HirId,
693 diag: &mut Diag<'_>,
694 ) -> bool {
695 let tcx = self.tcx();
696
697 let parent_hir_id = tcx.parent_hir_id(hir_id);
698 let parent_item = tcx.hir_get_parent_item(hir_id).def_id;
699
700 let generics = match tcx.hir_node_by_def_id(parent_item) {
701 hir::Node::Item(hir::Item {
702 kind: hir::ItemKind::Struct(_, generics, variant),
703 ..
704 }) => {
705 if !variant.fields().iter().any(|field| field.hir_id == parent_hir_id) {
706 return false;
707 }
708 generics
709 }
710 hir::Node::Item(hir::Item { kind: hir::ItemKind::Enum(_, generics, def), .. }) => {
711 if !def
712 .variants
713 .iter()
714 .flat_map(|variant| variant.data.fields().iter())
715 .any(|field| field.hir_id == parent_hir_id)
716 {
717 return false;
718 }
719 generics
720 }
721 _ => return false,
722 };
723
724 let Ok(rendered_ty) = tcx.sess.source_map().span_to_snippet(span) else {
725 return false;
726 };
727
728 let param = "TUV"
729 .chars()
730 .map(|c| c.to_string())
731 .chain((0..).map(|i| ::alloc::__export::must_use({ ::alloc::fmt::format(format_args!("P{0}", i)) })format!("P{i}")))
732 .find(|s| !generics.params.iter().any(|param| param.name.ident().as_str() == s))
733 .expect("we definitely can find at least one param name to generate");
734 let mut sugg = ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[(span, param.to_string())]))vec![(span, param.to_string())];
735 if let Some(insertion_span) = generics.span_for_param_suggestion() {
736 sugg.push((insertion_span, ::alloc::__export::must_use({
::alloc::fmt::format(format_args!(", {1}: {0}", rendered_ty, param))
})format!(", {param}: {}", rendered_ty)));
737 } else {
738 sugg.push((generics.where_clause_span, ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("<{1}: {0}>", rendered_ty, param))
})format!("<{param}: {}>", rendered_ty)));
739 }
740 diag.multipart_suggestion(
741 "you might be missing a type parameter",
742 sugg,
743 Applicability::MachineApplicable,
744 );
745 true
746 }
747
748 fn maybe_suggest_blanket_trait_impl<G: EmissionGuarantee>(
750 &self,
751 span: Span,
752 hir_id: hir::HirId,
753 diag: &mut Diag<'_, G>,
754 ) {
755 let tcx = self.tcx();
756 let parent_id = tcx.hir_get_parent_item(hir_id).def_id;
757 if let hir::Node::Item(hir::Item {
758 kind: hir::ItemKind::Impl(hir::Impl { self_ty: impl_self_ty, of_trait, generics, .. }),
759 ..
760 }) = tcx.hir_node_by_def_id(parent_id)
761 && hir_id == impl_self_ty.hir_id
762 {
763 let Some(of_trait) = of_trait else {
764 diag.span_suggestion_verbose(
765 impl_self_ty.span.shrink_to_hi(),
766 "you might have intended to implement this trait for a given type",
767 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!(" for /* Type */"))
})format!(" for /* Type */"),
768 Applicability::HasPlaceholders,
769 );
770 return;
771 };
772 if !of_trait.trait_ref.trait_def_id().is_some_and(|def_id| def_id.is_local()) {
773 return;
774 }
775 let of_trait_span = of_trait.trait_ref.path.span;
776 let Ok(of_trait_name) = tcx.sess.source_map().span_to_snippet(of_trait_span) else {
778 return;
779 };
780
781 let Ok(impl_trait_name) = self.tcx().sess.source_map().span_to_snippet(span) else {
782 return;
783 };
784 let sugg = self.add_generic_param_suggestion(generics, span, &impl_trait_name);
785 diag.multipart_suggestion(
786 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("alternatively use a blanket implementation to implement `{0}` for all types that also implement `{1}`",
of_trait_name, impl_trait_name))
})format!(
787 "alternatively use a blanket implementation to implement `{of_trait_name}` for \
788 all types that also implement `{impl_trait_name}`"
789 ),
790 sugg,
791 Applicability::MaybeIncorrect,
792 );
793 }
794 }
795
796 fn maybe_suggest_dyn_trait(
804 &self,
805 hir_id: hir::HirId,
806 span: Span,
807 sugg: Vec<(Span, String)>,
808 diag: &mut Diag<'_>,
809 ) -> bool {
810 let tcx = self.tcx();
811 if span.in_derive_expansion() {
812 return false;
813 }
814
815 match tcx.parent_hir_node(hir_id) {
818 hir::Node::Ty(_)
824 | hir::Node::Expr(_)
825 | hir::Node::PatExpr(_)
826 | hir::Node::PathSegment(_)
827 | hir::Node::AssocItemConstraint(_)
828 | hir::Node::TraitRef(_)
829 | hir::Node::Item(_)
830 | hir::Node::WherePredicate(_) => {}
831
832 hir::Node::Field(field) => {
833 if let hir::Node::Item(hir::Item {
835 kind: hir::ItemKind::Struct(_, _, variant), ..
836 }) = tcx.parent_hir_node(field.hir_id)
837 && variant
838 .fields()
839 .last()
840 .is_some_and(|tail_field| tail_field.hir_id == field.hir_id)
841 {
842 } else {
844 return false;
845 }
846 }
847 _ => return false,
848 }
849
850 diag.multipart_suggestion(
852 "you can add the `dyn` keyword if you want a trait object",
853 sugg,
854 Applicability::MachineApplicable,
855 );
856 true
857 }
858
859 fn add_generic_param_suggestion(
860 &self,
861 generics: &hir::Generics<'_>,
862 self_ty_span: Span,
863 impl_trait_name: &str,
864 ) -> Vec<(Span, String)> {
865 let param_name = generics.params.next_type_param_name(None);
867
868 let add_generic_sugg = if let Some(span) = generics.span_for_param_suggestion() {
869 (span, ::alloc::__export::must_use({
::alloc::fmt::format(format_args!(", {0}: {1}", param_name,
impl_trait_name))
})format!(", {param_name}: {impl_trait_name}"))
870 } else {
871 (generics.span, ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("<{0}: {1}>", param_name,
impl_trait_name))
})format!("<{param_name}: {impl_trait_name}>"))
872 };
873 ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[(self_ty_span, param_name), add_generic_sugg]))vec![(self_ty_span, param_name), add_generic_sugg]
874 }
875
876 fn maybe_suggest_impl_trait(
878 &self,
879 span: Span,
880 hir_id: hir::HirId,
881 hir_bounds: &[hir::PolyTraitRef<'tcx>],
882 diag: &mut Diag<'_>,
883 ) -> bool {
884 let tcx = self.tcx();
885 let parent_id = tcx.hir_get_parent_item(hir_id).def_id;
886 let (sig, generics) = match tcx.hir_node_by_def_id(parent_id) {
893 hir::Node::Item(hir::Item {
894 kind: hir::ItemKind::Fn { sig, generics, .. }, ..
895 }) => (sig, generics),
896 hir::Node::TraitItem(hir::TraitItem {
897 kind: hir::TraitItemKind::Fn(sig, _),
898 generics,
899 ..
900 }) => (sig, generics),
901 hir::Node::ImplItem(hir::ImplItem {
902 kind: hir::ImplItemKind::Fn(sig, _),
903 generics,
904 ..
905 }) => (sig, generics),
906 _ => return false,
907 };
908 let Ok(trait_name) = tcx.sess.source_map().span_to_snippet(span) else {
909 return false;
910 };
911 let impl_sugg = ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[(span.shrink_to_lo(), "impl ".to_string())]))vec![(span.shrink_to_lo(), "impl ".to_string())];
912 let is_dyn_compatible = hir_bounds.iter().all(|bound| match bound.trait_ref.path.res {
914 Res::Def(DefKind::Trait, id) => tcx.is_dyn_compatible(id),
915 _ => false,
916 });
917
918 let borrowed = #[allow(non_exhaustive_omitted_patterns)] match tcx.parent_hir_node(hir_id) {
hir::Node::Ty(hir::Ty { kind: hir::TyKind::Ref(..), .. }) => true,
_ => false,
}matches!(
919 tcx.parent_hir_node(hir_id),
920 hir::Node::Ty(hir::Ty { kind: hir::TyKind::Ref(..), .. })
921 );
922
923 if let hir::FnRetTy::Return(ty) = sig.decl.output
925 && ty.peel_refs().hir_id == hir_id
926 {
927 let pre = if !is_dyn_compatible {
928 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("`{0}` is dyn-incompatible, ",
trait_name))
})format!("`{trait_name}` is dyn-incompatible, ")
929 } else {
930 String::new()
931 };
932 let msg = ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0}use `impl {1}` to return an opaque type, as long as you return a single underlying type",
pre, trait_name))
})format!(
933 "{pre}use `impl {trait_name}` to return an opaque type, as long as you return a \
934 single underlying type",
935 );
936
937 diag.multipart_suggestion(msg, impl_sugg, Applicability::MachineApplicable);
938
939 if is_dyn_compatible {
941 let suggestion = if borrowed {
945 ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[(ty.span,
::alloc::__export::must_use({
::alloc::fmt::format(format_args!("Box<dyn {0}>",
trait_name))
}))]))vec![(ty.span, format!("Box<dyn {trait_name}>"))]
946 } else {
947 ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[(ty.span.shrink_to_lo(), "Box<dyn ".to_string()),
(ty.span.shrink_to_hi(), ">".to_string())]))vec![
948 (ty.span.shrink_to_lo(), "Box<dyn ".to_string()),
949 (ty.span.shrink_to_hi(), ">".to_string()),
950 ]
951 };
952
953 diag.multipart_suggestion(
954 "alternatively, you can return an owned trait object",
955 suggestion,
956 Applicability::MachineApplicable,
957 );
958 }
959 return true;
960 }
961
962 for ty in sig.decl.inputs {
964 if ty.peel_refs().hir_id != hir_id {
965 continue;
966 }
967 let sugg = self.add_generic_param_suggestion(generics, span, &trait_name);
968 diag.multipart_suggestion(
969 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("use a new generic type parameter, constrained by `{0}`",
trait_name))
})format!("use a new generic type parameter, constrained by `{trait_name}`"),
970 sugg,
971 Applicability::MachineApplicable,
972 );
973 diag.multipart_suggestion(
974 "you can also use an opaque type, but users won't be able to specify the type \
975 parameter when calling the `fn`, having to rely exclusively on type inference",
976 impl_sugg,
977 Applicability::MachineApplicable,
978 );
979 if !is_dyn_compatible {
980 diag.note(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("`{0}` is dyn-incompatible, otherwise a trait object could be used",
trait_name))
})format!(
981 "`{trait_name}` is dyn-incompatible, otherwise a trait object could be used"
982 ));
983 } else {
984 let (dyn_str, paren_dyn_str) =
986 if borrowed { ("dyn ", "(dyn ") } else { ("&dyn ", "&(dyn ") };
987
988 let sugg = if let [_, _, ..] = hir_bounds {
989 ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[(span.shrink_to_lo(), paren_dyn_str.to_string()),
(span.shrink_to_hi(), ")".to_string())]))vec![
991 (span.shrink_to_lo(), paren_dyn_str.to_string()),
992 (span.shrink_to_hi(), ")".to_string()),
993 ]
994 } else {
995 ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[(span.shrink_to_lo(), dyn_str.to_string())]))vec![(span.shrink_to_lo(), dyn_str.to_string())]
996 };
997 diag.multipart_suggestion(
998 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("alternatively, use a trait object to accept any type that implements `{0}`, accessing its methods at runtime using dynamic dispatch",
trait_name))
})format!(
999 "alternatively, use a trait object to accept any type that implements \
1000 `{trait_name}`, accessing its methods at runtime using dynamic dispatch",
1001 ),
1002 sugg,
1003 Applicability::MachineApplicable,
1004 );
1005 }
1006 return true;
1007 }
1008 false
1009 }
1010
1011 fn maybe_suggest_assoc_ty_bound(&self, hir_id: hir::HirId, diag: &mut Diag<'_>) {
1012 let mut parents = self.tcx().hir_parent_iter(hir_id);
1013
1014 if let Some((c_hir_id, hir::Node::AssocItemConstraint(constraint))) = parents.next()
1015 && let Some(obj_ty) = constraint.ty()
1016 && let Some((_, hir::Node::TraitRef(trait_ref))) = parents.next()
1017 {
1018 if let Some((_, hir::Node::Ty(ty))) = parents.next()
1019 && let hir::TyKind::TraitObject(..) = ty.kind
1020 {
1021 return;
1023 }
1024
1025 if trait_ref
1026 .path
1027 .segments
1028 .iter()
1029 .find_map(|seg| {
1030 seg.args.filter(|args| args.constraints.iter().any(|c| c.hir_id == c_hir_id))
1031 })
1032 .is_none_or(|args| args.parenthesized != hir::GenericArgsParentheses::No)
1033 {
1034 return;
1036 }
1037
1038 let lo = if constraint.gen_args.span_ext.is_dummy() {
1039 constraint.ident.span
1040 } else {
1041 constraint.gen_args.span_ext
1042 };
1043 let hi = obj_ty.span;
1044
1045 if !lo.eq_ctxt(hi) {
1046 return;
1047 }
1048
1049 diag.span_suggestion_verbose(
1050 lo.between(hi),
1051 "you might have meant to write a bound here",
1052 ": ",
1053 Applicability::MaybeIncorrect,
1054 );
1055 }
1056 }
1057
1058 fn maybe_suggest_typoed_method(
1059 &self,
1060 hir_id: hir::HirId,
1061 trait_def_id: Option<DefId>,
1062 diag: &mut Diag<'_>,
1063 ) {
1064 let tcx = self.tcx();
1065 let Some(trait_def_id) = trait_def_id else {
1066 return;
1067 };
1068 let hir::Node::Expr(hir::Expr {
1069 kind: hir::ExprKind::Path(hir::QPath::TypeRelative(path_ty, segment)),
1070 ..
1071 }) = tcx.parent_hir_node(hir_id)
1072 else {
1073 return;
1074 };
1075 if path_ty.hir_id != hir_id {
1076 return;
1077 }
1078 let names: Vec<_> = tcx
1079 .associated_items(trait_def_id)
1080 .in_definition_order()
1081 .filter(|assoc| assoc.namespace() == hir::def::Namespace::ValueNS)
1082 .map(|cand| cand.name())
1083 .collect();
1084 if let Some(typo) = find_best_match_for_name(&names, segment.ident.name, None) {
1085 diag.span_suggestion_verbose(
1086 segment.ident.span,
1087 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("you may have misspelled this associated item, causing `{0}` to be interpreted as a type rather than a trait",
tcx.item_name(trait_def_id)))
})format!(
1088 "you may have misspelled this associated item, causing `{}` \
1089 to be interpreted as a type rather than a trait",
1090 tcx.item_name(trait_def_id),
1091 ),
1092 typo,
1093 Applicability::MaybeIncorrect,
1094 );
1095 }
1096 }
1097}
1098
1099fn replace_dummy_self_with_error<'tcx, T: TypeFoldable<TyCtxt<'tcx>>>(
1100 tcx: TyCtxt<'tcx>,
1101 t: T,
1102 guar: ErrorGuaranteed,
1103) -> T {
1104 t.fold_with(&mut BottomUpFolder {
1105 tcx,
1106 ty_op: |ty| {
1107 if ty == tcx.types.trait_object_dummy_self { Ty::new_error(tcx, guar) } else { ty }
1108 },
1109 lt_op: |lt| lt,
1110 ct_op: |ct| ct,
1111 })
1112}