about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--compiler/rustc_hir_analysis/messages.ftl41
-rw-r--r--compiler/rustc_hir_analysis/src/coherence/orphan.rs291
-rw-r--r--compiler/rustc_hir_analysis/src/errors.rs172
3 files changed, 341 insertions, 163 deletions
diff --git a/compiler/rustc_hir_analysis/messages.ftl b/compiler/rustc_hir_analysis/messages.ftl
index 6bf6650986a..e8d9918be22 100644
--- a/compiler/rustc_hir_analysis/messages.ftl
+++ b/compiler/rustc_hir_analysis/messages.ftl
@@ -72,6 +72,12 @@ hir_analysis_copy_impl_on_type_with_dtor =
     the trait `Copy` cannot be implemented for this type; the type has a destructor
     .label = `Copy` not allowed on types with destructors
 
+hir_analysis_cross_crate_traits = cross-crate traits with a default impl, like `{$traits}`, can only be implemented for a struct/enum type, not `{$self_ty}`
+    .label = can't implement cross-crate trait with a default impl for non-struct/enum type
+
+hir_analysis_cross_crate_traits_defined = cross-crate traits with a default impl, like `{$traits}`, can only be implemented for a struct/enum type defined in the current crate
+    .label = can't implement cross-crate trait for type in another crate
+
 hir_analysis_dispatch_from_dyn_multi = implementing the `DispatchFromDyn` trait requires multiple coercions
     .note = the trait `DispatchFromDyn` may only be implemented for a coercion between structures with a single field being coerced
     .coercions_note = currently, {$number} fields need coercions: {$coercions}
@@ -237,6 +243,28 @@ hir_analysis_must_implement_not_function_span_note = required by this annotation
 
 hir_analysis_must_implement_one_of_attribute = the `#[rustc_must_implement_one_of]` attribute must be used with at least 2 args
 
+hir_analysis_only_current_traits_arbitrary = only traits defined in the current crate can be implemented for arbitrary types
+
+hir_analysis_only_current_traits_foreign = this is not defined in the current crate because this is a foreign trait
+
+hir_analysis_only_current_traits_label = impl doesn't use only types from inside the current crate
+
+hir_analysis_only_current_traits_name = this is not defined in the current crate because {$name} are always foreign
+
+hir_analysis_only_current_traits_note = define and implement a trait or new type instead
+
+hir_analysis_only_current_traits_opaque = type alias impl trait is treated as if it were foreign, because its hidden type could be from a foreign crate
+
+hir_analysis_only_current_traits_outside = only traits defined in the current crate can be implemented for types defined outside of the crate
+
+hir_analysis_only_current_traits_pointer = `{$pointer}` is not defined in the current crate because raw pointers are always foreign
+
+hir_analysis_only_current_traits_pointer_sugg = consider introducing a new wrapper type
+
+hir_analysis_only_current_traits_primitive = only traits defined in the current crate can be implemented for primitive types
+
+hir_analysis_only_current_traits_ty = `{$ty}` is not defined in the current crate
+
 hir_analysis_paren_sugar_attribute = the `#[rustc_paren_sugar]` attribute is a temporary means of controlling which traits can use parenthetical notation
     .help = add `#![feature(unboxed_closures)]` to the crate attributes to use it
 
@@ -326,6 +354,9 @@ hir_analysis_trait_object_declared_with_no_traits =
     at least one trait is required for an object type
     .alias_span = this alias does not contain a trait
 
+hir_analysis_traits_with_defualt_impl = traits with a default impl, like `{$traits}`, cannot be implemented for {$problematic_kind} `{$self_ty}`
+    .note = a trait object implements `{$traits}` if and only if `{$traits}` is one of the trait object's trait bounds
+
 hir_analysis_transparent_enum_variant = transparent enum needs exactly one variant, but has {$number}
     .label = needs exactly one variant, but has {$number}
     .many_label = too many variants in `{$path}`
@@ -339,6 +370,16 @@ hir_analysis_transparent_non_zero_sized_enum = the variant of a transparent {$de
     .label = needs at most one field with non-trivial size or alignment, but has {$field_count}
     .labels = this field has non-zero size or requires alignment
 
+hir_analysis_ty_param_first_local = type parameter `{$param_ty}` must be covered by another type when it appears before the first local type (`{$local_type}`)
+    .label = type parameter `{$param_ty}` must be covered by another type when it appears before the first local type (`{$local_type}`)
+    .note = implementing a foreign trait is only possible if at least one of the types for which it is implemented is local, and no uncovered type parameters appear before that first local type
+    .case_note = in this case, 'before' refers to the following order: `impl<..> ForeignTrait<T1, ..., Tn> for T0`, where `T0` is the first and `Tn` is the last
+
+hir_analysis_ty_param_some = type parameter `{$param_ty}` must be used as the type parameter for some local type (e.g., `MyStruct<{$param_ty}>`)
+    .label = type parameter `{$param_ty}` must be used as the type parameter for some local type
+    .note = implementing a foreign trait is only possible if at least one of the types for which it is implemented is local
+    .only_note = only traits defined in the current crate can be implemented for a type parameter
+
 hir_analysis_type_of = {$type_of}
 
 hir_analysis_typeof_reserved_keyword_used =
diff --git a/compiler/rustc_hir_analysis/src/coherence/orphan.rs b/compiler/rustc_hir_analysis/src/coherence/orphan.rs
index faddb0c3829..7eeb7837467 100644
--- a/compiler/rustc_hir_analysis/src/coherence/orphan.rs
+++ b/compiler/rustc_hir_analysis/src/coherence/orphan.rs
@@ -2,8 +2,7 @@
 //! crate or pertains to a type defined in this crate.
 
 use rustc_data_structures::fx::FxHashSet;
-use rustc_errors::{struct_span_err, DelayDm};
-use rustc_errors::{Diagnostic, ErrorGuaranteed};
+use rustc_errors::{DelayDm, ErrorGuaranteed};
 use rustc_hir as hir;
 use rustc_middle::ty::util::CheckRegions;
 use rustc_middle::ty::GenericArgs;
@@ -17,6 +16,8 @@ use rustc_span::Span;
 use rustc_trait_selection::traits;
 use std::ops::ControlFlow;
 
+use crate::errors;
+
 #[instrument(skip(tcx), level = "debug")]
 pub(crate) fn orphan_check_impl(
     tcx: TyCtxt<'_>,
@@ -259,49 +260,30 @@ fn do_orphan_check_impl<'tcx>(
             match local_impl {
                 LocalImpl::Allow => {}
                 LocalImpl::Disallow { problematic_kind } => {
-                    let msg = format!(
-                        "traits with a default impl, like `{trait}`, \
-                                cannot be implemented for {problematic_kind} `{self_ty}`",
-                        trait = tcx.def_path_str(trait_def_id),
-                    );
-                    let label = format!(
-                        "a trait object implements `{trait}` if and only if `{trait}` \
-                                is one of the trait object's trait bounds",
-                        trait = tcx.def_path_str(trait_def_id),
-                    );
-                    let sp = tcx.def_span(def_id);
-                    let reported =
-                        struct_span_err!(tcx.sess, sp, E0321, "{}", msg).note(label).emit();
-                    return Err(reported);
+                    return Err(tcx.sess.emit_err(errors::TraitsWithDefaultImpl {
+                        span: tcx.def_span(def_id),
+                        traits: tcx.def_path_str(trait_def_id),
+                        problematic_kind,
+                        self_ty,
+                    }));
                 }
             }
         } else {
-            if let Some((msg, label)) = match nonlocal_impl {
-                NonlocalImpl::Allow => None,
-                NonlocalImpl::DisallowBecauseNonlocal => Some((
-                    format!(
-                        "cross-crate traits with a default impl, like `{}`, \
-                                can only be implemented for a struct/enum type \
-                                defined in the current crate",
-                        tcx.def_path_str(trait_def_id)
-                    ),
-                    "can't implement cross-crate trait for type in another crate",
-                )),
-                NonlocalImpl::DisallowOther => Some((
-                    format!(
-                        "cross-crate traits with a default impl, like `{}`, can \
-                                only be implemented for a struct/enum type, not `{}`",
-                        tcx.def_path_str(trait_def_id),
-                        self_ty
-                    ),
-                    "can't implement cross-crate trait with a default impl for \
-                            non-struct/enum type",
-                )),
-            } {
-                let sp = tcx.def_span(def_id);
-                let reported =
-                    struct_span_err!(tcx.sess, sp, E0321, "{}", msg).span_label(sp, label).emit();
-                return Err(reported);
+            match nonlocal_impl {
+                NonlocalImpl::Allow => {}
+                NonlocalImpl::DisallowBecauseNonlocal => {
+                    return Err(tcx.sess.emit_err(errors::CrossCrateTraitsDefined {
+                        span: tcx.def_span(def_id),
+                        traits: tcx.def_path_str(trait_def_id),
+                    }));
+                }
+                NonlocalImpl::DisallowOther => {
+                    return Err(tcx.sess.emit_err(errors::CrossCrateTraits {
+                        span: tcx.def_span(def_id),
+                        traits: tcx.def_path_str(trait_def_id),
+                        self_ty,
+                    }));
+                }
             }
         }
     }
@@ -322,19 +304,18 @@ fn emit_orphan_check_error<'tcx>(
     let self_ty = trait_ref.self_ty();
     Err(match err {
         traits::OrphanCheckErr::NonLocalInputType(tys) => {
-            let msg = match self_ty.kind() {
-                ty::Adt(..) => "can be implemented for types defined outside of the crate",
-                _ if self_ty.is_primitive() => "can be implemented for primitive types",
-                _ => "can be implemented for arbitrary types",
-            };
-            let mut err = struct_span_err!(
-                tcx.sess,
-                sp,
-                E0117,
-                "only traits defined in the current crate {msg}"
-            );
-            err.span_label(sp, "impl doesn't use only types from inside the current crate");
+            let (mut opaque, mut foreign, mut name, mut pointer, mut ty_diag) =
+                (Vec::new(), Vec::new(), Vec::new(), Vec::new(), Vec::new());
+            let mut sugg = None;
             for &(mut ty, is_target_ty) in &tys {
+                let span = if is_target_ty {
+                    // Point at `D<A>` in `impl<A, B> for C<B> in D<A>`
+                    self_ty_span
+                } else {
+                    // Point at `C<B>` in `impl<A, B> for C<B> in D<A>`
+                    trait_span
+                };
+
                 ty = tcx.erase_regions(ty);
                 ty = match ty.kind() {
                     // Remove the type arguments from the output, as they are not relevant.
@@ -345,50 +326,103 @@ fn emit_orphan_check_error<'tcx>(
                     ty::Adt(def, _) => Ty::new_adt(tcx, *def, ty::List::empty()),
                     _ => ty,
                 };
-                let msg = |ty: &str, postfix: &str| {
-                    format!("{ty} is not defined in the current crate{postfix}")
-                };
 
-                let this = |name: &str| {
-                    if !trait_ref.def_id.is_local() && !is_target_ty {
-                        msg("this", " because this is a foreign trait")
+                fn push_to_foreign_or_name<'tcx>(
+                    is_foreign: bool,
+                    foreign: &mut Vec<errors::OnlyCurrentTraitsForeign>,
+                    name: &mut Vec<errors::OnlyCurrentTraitsName<'tcx>>,
+                    span: Span,
+                    sname: &'tcx str,
+                ) {
+                    if is_foreign {
+                        foreign.push(errors::OnlyCurrentTraitsForeign { span })
                     } else {
-                        msg("this", &format!(" because {name} are always foreign"))
+                        name.push(errors::OnlyCurrentTraitsName { span, name: sname });
+                    }
+                }
+
+                let is_foreign = !trait_ref.def_id.is_local() && !is_target_ty;
+
+                match &ty.kind() {
+                    ty::Slice(_) => {
+                        push_to_foreign_or_name(
+                            is_foreign,
+                            &mut foreign,
+                            &mut name,
+                            span,
+                            "slices",
+                        );
+                    }
+                    ty::Array(..) => {
+                        push_to_foreign_or_name(
+                            is_foreign,
+                            &mut foreign,
+                            &mut name,
+                            span,
+                            "arrays",
+                        );
+                    }
+                    ty::Tuple(..) => {
+                        push_to_foreign_or_name(
+                            is_foreign,
+                            &mut foreign,
+                            &mut name,
+                            span,
+                            "tuples",
+                        );
                     }
-                };
-                let msg = match &ty.kind() {
-                    ty::Slice(_) => this("slices"),
-                    ty::Array(..) => this("arrays"),
-                    ty::Tuple(..) => this("tuples"),
                     ty::Alias(ty::Opaque, ..) => {
-                        "type alias impl trait is treated as if it were foreign, \
-                        because its hidden type could be from a foreign crate"
-                            .to_string()
+                        opaque.push(errors::OnlyCurrentTraitsOpaque { span })
                     }
                     ty::RawPtr(ptr_ty) => {
-                        emit_newtype_suggestion_for_raw_ptr(
-                            full_impl_span,
-                            self_ty,
-                            self_ty_span,
-                            ptr_ty,
-                            &mut err,
-                        );
-
-                        msg(&format!("`{ty}`"), " because raw pointers are always foreign")
+                        if !self_ty.has_param() {
+                            let mut_key = ptr_ty.mutbl.prefix_str();
+                            sugg = Some(errors::OnlyCurrentTraitsPointerSugg {
+                                wrapper_span: self_ty_span,
+                                struct_span: full_impl_span.shrink_to_lo(),
+                                mut_key,
+                                ptr_ty: ptr_ty.ty,
+                            });
+                        }
+                        pointer.push(errors::OnlyCurrentTraitsPointer { span, pointer: ty });
                     }
-                    _ => msg(&format!("`{ty}`"), ""),
-                };
-
-                if is_target_ty {
-                    // Point at `D<A>` in `impl<A, B> for C<B> in D<A>`
-                    err.span_label(self_ty_span, msg);
-                } else {
-                    // Point at `C<B>` in `impl<A, B> for C<B> in D<A>`
-                    err.span_label(trait_span, msg);
+                    _ => ty_diag.push(errors::OnlyCurrentTraitsTy { span, ty }),
                 }
             }
-            err.note("define and implement a trait or new type instead");
-            err.emit()
+
+            let err_struct = match self_ty.kind() {
+                ty::Adt(..) => errors::OnlyCurrentTraits::Outside {
+                    span: sp,
+                    note: (),
+                    opaque,
+                    foreign,
+                    name,
+                    pointer,
+                    ty: ty_diag,
+                    sugg,
+                },
+                _ if self_ty.is_primitive() => errors::OnlyCurrentTraits::Primitive {
+                    span: sp,
+                    note: (),
+                    opaque,
+                    foreign,
+                    name,
+                    pointer,
+                    ty: ty_diag,
+                    sugg,
+                },
+                _ => errors::OnlyCurrentTraits::Arbitrary {
+                    span: sp,
+                    note: (),
+                    opaque,
+                    foreign,
+                    name,
+                    pointer,
+                    ty: ty_diag,
+                    sugg,
+                },
+            };
+            tcx.sess.emit_err(err_struct)
         }
         traits::OrphanCheckErr::UncoveredTy(param_ty, local_type) => {
             let mut sp = sp;
@@ -399,85 +433,18 @@ fn emit_orphan_check_error<'tcx>(
             }
 
             match local_type {
-                Some(local_type) => struct_span_err!(
-                    tcx.sess,
-                    sp,
-                    E0210,
-                    "type parameter `{}` must be covered by another type \
-                    when it appears before the first local type (`{}`)",
-                    param_ty,
-                    local_type
-                )
-                .span_label(
-                    sp,
-                    format!(
-                        "type parameter `{param_ty}` must be covered by another type \
-                    when it appears before the first local type (`{local_type}`)"
-                    ),
-                )
-                .note(
-                    "implementing a foreign trait is only possible if at \
-                        least one of the types for which it is implemented is local, \
-                        and no uncovered type parameters appear before that first \
-                        local type",
-                )
-                .note(
-                    "in this case, 'before' refers to the following order: \
-                        `impl<..> ForeignTrait<T1, ..., Tn> for T0`, \
-                        where `T0` is the first and `Tn` is the last",
-                )
-                .emit(),
-                None => struct_span_err!(
-                    tcx.sess,
-                    sp,
-                    E0210,
-                    "type parameter `{}` must be used as the type parameter for some \
-                    local type (e.g., `MyStruct<{}>`)",
+                Some(local_type) => tcx.sess.emit_err(errors::TyParamFirstLocal {
+                    span: sp,
+                    note: (),
                     param_ty,
-                    param_ty
-                )
-                .span_label(
-                    sp,
-                    format!(
-                        "type parameter `{param_ty}` must be used as the type parameter for some \
-                    local type",
-                    ),
-                )
-                .note(
-                    "implementing a foreign trait is only possible if at \
-                        least one of the types for which it is implemented is local",
-                )
-                .note(
-                    "only traits defined in the current crate can be \
-                        implemented for a type parameter",
-                )
-                .emit(),
+                    local_type,
+                }),
+                None => tcx.sess.emit_err(errors::TyParamSome { span: sp, note: (), param_ty }),
             }
         }
     })
 }
 
-fn emit_newtype_suggestion_for_raw_ptr(
-    full_impl_span: Span,
-    self_ty: Ty<'_>,
-    self_ty_span: Span,
-    ptr_ty: &ty::TypeAndMut<'_>,
-    diag: &mut Diagnostic,
-) {
-    if !self_ty.has_param() {
-        let mut_key = ptr_ty.mutbl.prefix_str();
-        let msg_sugg = "consider introducing a new wrapper type".to_owned();
-        let sugg = vec![
-            (
-                full_impl_span.shrink_to_lo(),
-                format!("struct WrapperType(*{}{});\n\n", mut_key, ptr_ty.ty),
-            ),
-            (self_ty_span, "WrapperType".to_owned()),
-        ];
-        diag.multipart_suggestion(msg_sugg, sugg, rustc_errors::Applicability::MaybeIncorrect);
-    }
-}
-
 /// Lint impls of auto traits if they are likely to have
 /// unsound or surprising effects on auto impls.
 fn lint_auto_trait_impl<'tcx>(
diff --git a/compiler/rustc_hir_analysis/src/errors.rs b/compiler/rustc_hir_analysis/src/errors.rs
index 189564d4e33..6a2db1d0627 100644
--- a/compiler/rustc_hir_analysis/src/errors.rs
+++ b/compiler/rustc_hir_analysis/src/errors.rs
@@ -683,7 +683,6 @@ pub(crate) struct SIMDFFIHighlyExperimental {
 }
 
 #[derive(Diagnostic)]
-
 pub enum ImplNotMarkedDefault {
     #[diag(hir_analysis_impl_not_marked_default, code = "E0520")]
     #[note]
@@ -1159,3 +1158,174 @@ pub struct ImplForTyRequires {
     pub trait_name: String,
     pub ty: String,
 }
+
+#[derive(Diagnostic)]
+#[diag(hir_analysis_traits_with_defualt_impl, code = "E0321")]
+#[note]
+pub struct TraitsWithDefaultImpl<'a> {
+    #[primary_span]
+    pub span: Span,
+    pub traits: String,
+    pub problematic_kind: &'a str,
+    pub self_ty: Ty<'a>,
+}
+
+#[derive(Diagnostic)]
+#[diag(hir_analysis_cross_crate_traits, code = "E0321")]
+pub struct CrossCrateTraits<'a> {
+    #[primary_span]
+    #[label]
+    pub span: Span,
+    pub traits: String,
+    pub self_ty: Ty<'a>,
+}
+
+#[derive(Diagnostic)]
+#[diag(hir_analysis_cross_crate_traits_defined, code = "E0321")]
+pub struct CrossCrateTraitsDefined {
+    #[primary_span]
+    #[label]
+    pub span: Span,
+    pub traits: String,
+}
+
+#[derive(Diagnostic)]
+#[diag(hir_analysis_ty_param_first_local, code = "E0210")]
+#[note]
+pub struct TyParamFirstLocal<'a> {
+    #[primary_span]
+    #[label]
+    pub span: Span,
+    #[note(hir_analysis_case_note)]
+    pub note: (),
+    pub param_ty: Ty<'a>,
+    pub local_type: Ty<'a>,
+}
+
+#[derive(Diagnostic)]
+#[diag(hir_analysis_ty_param_some, code = "E0210")]
+#[note]
+pub struct TyParamSome<'a> {
+    #[primary_span]
+    #[label]
+    pub span: Span,
+    #[note(hir_analysis_only_note)]
+    pub note: (),
+    pub param_ty: Ty<'a>,
+}
+
+#[derive(Diagnostic)]
+pub enum OnlyCurrentTraits<'a> {
+    #[diag(hir_analysis_only_current_traits_outside, code = "E0117")]
+    Outside {
+        #[primary_span]
+        #[label(hir_analysis_only_current_traits_label)]
+        span: Span,
+        #[note(hir_analysis_only_current_traits_note)]
+        note: (),
+        #[subdiagnostic]
+        opaque: Vec<OnlyCurrentTraitsOpaque>,
+        #[subdiagnostic]
+        foreign: Vec<OnlyCurrentTraitsForeign>,
+        #[subdiagnostic]
+        name: Vec<OnlyCurrentTraitsName<'a>>,
+        #[subdiagnostic]
+        pointer: Vec<OnlyCurrentTraitsPointer<'a>>,
+        #[subdiagnostic]
+        ty: Vec<OnlyCurrentTraitsTy<'a>>,
+        #[subdiagnostic]
+        sugg: Option<OnlyCurrentTraitsPointerSugg<'a>>,
+    },
+    #[diag(hir_analysis_only_current_traits_primitive, code = "E0117")]
+    Primitive {
+        #[primary_span]
+        #[label(hir_analysis_only_current_traits_label)]
+        span: Span,
+        #[note(hir_analysis_only_current_traits_note)]
+        note: (),
+        #[subdiagnostic]
+        opaque: Vec<OnlyCurrentTraitsOpaque>,
+        #[subdiagnostic]
+        foreign: Vec<OnlyCurrentTraitsForeign>,
+        #[subdiagnostic]
+        name: Vec<OnlyCurrentTraitsName<'a>>,
+        #[subdiagnostic]
+        pointer: Vec<OnlyCurrentTraitsPointer<'a>>,
+        #[subdiagnostic]
+        ty: Vec<OnlyCurrentTraitsTy<'a>>,
+        #[subdiagnostic]
+        sugg: Option<OnlyCurrentTraitsPointerSugg<'a>>,
+    },
+    #[diag(hir_analysis_only_current_traits_arbitrary, code = "E0117")]
+    Arbitrary {
+        #[primary_span]
+        #[label(hir_analysis_only_current_traits_label)]
+        span: Span,
+        #[note(hir_analysis_only_current_traits_note)]
+        note: (),
+        #[subdiagnostic]
+        opaque: Vec<OnlyCurrentTraitsOpaque>,
+        #[subdiagnostic]
+        foreign: Vec<OnlyCurrentTraitsForeign>,
+        #[subdiagnostic]
+        name: Vec<OnlyCurrentTraitsName<'a>>,
+        #[subdiagnostic]
+        pointer: Vec<OnlyCurrentTraitsPointer<'a>>,
+        #[subdiagnostic]
+        ty: Vec<OnlyCurrentTraitsTy<'a>>,
+        #[subdiagnostic]
+        sugg: Option<OnlyCurrentTraitsPointerSugg<'a>>,
+    },
+}
+
+#[derive(Subdiagnostic)]
+#[label(hir_analysis_only_current_traits_opaque)]
+pub struct OnlyCurrentTraitsOpaque {
+    #[primary_span]
+    pub span: Span,
+}
+
+#[derive(Subdiagnostic)]
+#[label(hir_analysis_only_current_traits_foreign)]
+pub struct OnlyCurrentTraitsForeign {
+    #[primary_span]
+    pub span: Span,
+}
+
+#[derive(Subdiagnostic)]
+#[label(hir_analysis_only_current_traits_name)]
+pub struct OnlyCurrentTraitsName<'a> {
+    #[primary_span]
+    pub span: Span,
+    pub name: &'a str,
+}
+
+#[derive(Subdiagnostic)]
+#[label(hir_analysis_only_current_traits_pointer)]
+pub struct OnlyCurrentTraitsPointer<'a> {
+    #[primary_span]
+    pub span: Span,
+    pub pointer: Ty<'a>,
+}
+
+#[derive(Subdiagnostic)]
+#[label(hir_analysis_only_current_traits_ty)]
+pub struct OnlyCurrentTraitsTy<'a> {
+    #[primary_span]
+    pub span: Span,
+    pub ty: Ty<'a>,
+}
+
+#[derive(Subdiagnostic)]
+#[multipart_suggestion(
+    hir_analysis_only_current_traits_pointer_sugg,
+    applicability = "maybe-incorrect"
+)]
+pub struct OnlyCurrentTraitsPointerSugg<'a> {
+    #[suggestion_part(code = "WrapperType")]
+    pub wrapper_span: Span,
+    #[suggestion_part(code = "struct WrapperType(*{mut_key}{ptr_ty});\n\n")]
+    pub struct_span: Span,
+    pub mut_key: &'a str,
+    pub ptr_ty: Ty<'a>,
+}