about summary refs log tree commit diff
path: root/compiler/rustc_lint/src
diff options
context:
space:
mode:
authorRejyr <jerrylwang123@gmail.com>2022-11-13 09:46:31 -0500
committerRejyr <jerrylwang123@gmail.com>2023-01-09 18:56:59 -0500
commit758140e196ae2d520d3f286797c654d517bed5b5 (patch)
tree401a5ee6e4d4524cf64ba72e1d6ddc0e9ad746d1 /compiler/rustc_lint/src
parent587d49766b711fab79015adecb101138fffa8acc (diff)
downloadrust-758140e196ae2d520d3f286797c654d517bed5b5.tar.gz
rust-758140e196ae2d520d3f286797c654d517bed5b5.zip
migrate: rest of `builtin.rs` without `builtin_asm_labels`
Diffstat (limited to 'compiler/rustc_lint/src')
-rw-r--r--compiler/rustc_lint/src/builtin.rs295
-rw-r--r--compiler/rustc_lint/src/lints.rs241
2 files changed, 353 insertions, 183 deletions
diff --git a/compiler/rustc_lint/src/builtin.rs b/compiler/rustc_lint/src/builtin.rs
index f7055019bab..2bdff0d5a09 100644
--- a/compiler/rustc_lint/src/builtin.rs
+++ b/compiler/rustc_lint/src/builtin.rs
@@ -1,3 +1,5 @@
+// #![deny(rustc::untranslatable_diagnostic)]
+// #![deny(rustc::diagnostic_outside_of_impl)]
 //! Lints in the Rust compiler.
 //!
 //! This contains lints which can feasibly be implemented as their own
@@ -23,16 +25,21 @@
 use crate::{
     errors::BuiltinEllpisisInclusiveRangePatterns,
     lints::{
-        BuiltinAnonymousParams, BuiltinBoxPointers, BuiltinConstNoMangle,
-        BuiltinDeprecatedAttrUsed, BuiltinDerefNullptr, BuiltinEllipsisInclusiveRangePatternsLint,
-        BuiltinExplicitOutlives, BuiltinExplicitOutlivesSuggestion, BuiltinIncompleteFeatures,
+        BuiltinAnonymousParams, BuiltinBoxPointers, BuiltinClashingExtern,
+        BuiltinClashingExternSub, BuiltinConstNoMangle, BuiltinDeprecatedAttrLink,
+        BuiltinDeprecatedAttrLinkSuggestion, BuiltinDeprecatedAttrUsed, BuiltinDerefNullptr,
+        BuiltinEllipsisInclusiveRangePatternsLint, BuiltinExplicitOutlives,
+        BuiltinExplicitOutlivesSuggestion, BuiltinIncompleteFeatures,
         BuiltinIncompleteFeaturesHelp, BuiltinIncompleteFeaturesNote, BuiltinKeywordIdents,
         BuiltinMissingCopyImpl, BuiltinMissingDebugImpl, BuiltinMissingDoc,
         BuiltinMutablesTransmutes, BuiltinNoMangleGeneric, BuiltinNonShorthandFieldPatterns,
-        BuiltinSpecialModuleNameUsed, BuiltinTrivialBounds, BuiltinUnexpectedCliConfigName,
-        BuiltinUnexpectedCliConfigValue, BuiltinUnnameableTestItems, BuiltinUnreachablePub,
-        BuiltinUnsafe, BuiltinUnstableFeatures, BuiltinUnusedDocComment,
-        BuiltinUnusedDocCommentSub, BuiltinWhileTrue,
+        BuiltinSpecialModuleNameUsed, BuiltinTrivialBounds, BuiltinTypeAliasGenericBounds,
+        BuiltinTypeAliasGenericBoundsSuggestion, BuiltinTypeAliasWhereClause,
+        BuiltinUnexpectedCliConfigName, BuiltinUnexpectedCliConfigValue,
+        BuiltinUngatedAsyncFnTrackCaller, BuiltinUnnameableTestItems, BuiltinUnpermittedTypeInit,
+        BuiltinUnpermittedTypeInitSub, BuiltinUnreachablePub, BuiltinUnsafe,
+        BuiltinUnstableFeatures, BuiltinUnusedDocComment, BuiltinUnusedDocCommentSub,
+        BuiltinWhileTrue, SuggestChangingAssocTypes,
     },
     types::{transparent_newtype_field, CItemKind},
     EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintContext,
@@ -45,9 +52,7 @@ use rustc_ast::{self as ast, *};
 use rustc_ast_pretty::pprust::{self, expr_to_string};
 use rustc_data_structures::fx::{FxHashMap, FxHashSet};
 use rustc_data_structures::stack::ensure_sufficient_stack;
-use rustc_errors::{
-    fluent, Applicability, DecorateLint, DelayDm, Diagnostic, DiagnosticStyledString, MultiSpan,
-};
+use rustc_errors::{fluent, Applicability, DecorateLint, MultiSpan};
 use rustc_feature::{deprecated_attributes, AttributeGate, BuiltinAttribute, GateIssue, Stability};
 use rustc_hir as hir;
 use rustc_hir::def::{DefKind, Res};
@@ -923,24 +928,18 @@ impl EarlyLintPass for DeprecatedAttr {
                     _,
                 ) = gate
                 {
-                    // FIXME(davidtwco) translatable deprecated attr
-                    cx.struct_span_lint(
+                    let suggestion = match suggestion {
+                        Some(msg) => {
+                            BuiltinDeprecatedAttrLinkSuggestion::Msg { suggestion: attr.span, msg }
+                        }
+                        None => {
+                            BuiltinDeprecatedAttrLinkSuggestion::Default { suggestion: attr.span }
+                        }
+                    };
+                    cx.emit_spanned_lint(
                         DEPRECATED,
                         attr.span,
-                        fluent::lint_builtin_deprecated_attr_link,
-                        |lint| {
-                            lint.set_arg("name", name)
-                                .set_arg("reason", reason)
-                                .set_arg("link", link)
-                                .span_suggestion_short(
-                                    attr.span,
-                                    suggestion.map(|s| s.into()).unwrap_or(
-                                        fluent::lint_builtin_deprecated_attr_default_suggestion,
-                                    ),
-                                    "",
-                                    Applicability::MachineApplicable,
-                                )
-                        },
+                        BuiltinDeprecatedAttrLink { name, reason, link, suggestion },
                     );
                 }
                 return;
@@ -1305,20 +1304,10 @@ impl<'tcx> LateLintPass<'tcx> for UngatedAsyncFnTrackCaller {
             // Now, check if the function has the `#[track_caller]` attribute
             && let Some(attr) = attrs.iter().find(|attr| attr.has_name(sym::track_caller))
             {
-                cx.struct_span_lint(
-                    UNGATED_ASYNC_FN_TRACK_CALLER,
-                    attr.span,
-                    fluent::lint_ungated_async_fn_track_caller,
-                    |lint| {
-                        lint.span_label(span, fluent::label);
-                        rustc_session::parse::add_feature_diagnostics(
-                            lint,
-                            &cx.tcx.sess.parse_sess,
-                            sym::closure_track_caller,
-                        );
-                        lint
-                    },
-                );
+                cx.emit_spanned_lint(UNGATED_ASYNC_FN_TRACK_CALLER, attr.span, BuiltinUngatedAsyncFnTrackCaller {
+                    label: span,
+                    parse_sess: &cx.tcx.sess.parse_sess,
+                });
             }
     }
 }
@@ -1447,7 +1436,7 @@ declare_lint_pass!(
 );
 
 impl TypeAliasBounds {
-    fn is_type_variable_assoc(qpath: &hir::QPath<'_>) -> bool {
+    pub(crate) fn is_type_variable_assoc(qpath: &hir::QPath<'_>) -> bool {
         match *qpath {
             hir::QPath::TypeRelative(ref ty, _) => {
                 // If this is a type variable, we found a `T::Assoc`.
@@ -1461,29 +1450,6 @@ impl TypeAliasBounds {
             hir::QPath::Resolved(..) | hir::QPath::LangItem(..) => false,
         }
     }
-
-    fn suggest_changing_assoc_types(ty: &hir::Ty<'_>, err: &mut Diagnostic) {
-        // Access to associates types should use `<T as Bound>::Assoc`, which does not need a
-        // bound.  Let's see if this type does that.
-
-        // We use a HIR visitor to walk the type.
-        use rustc_hir::intravisit::{self, Visitor};
-        struct WalkAssocTypes<'a> {
-            err: &'a mut Diagnostic,
-        }
-        impl Visitor<'_> for WalkAssocTypes<'_> {
-            fn visit_qpath(&mut self, qpath: &hir::QPath<'_>, id: hir::HirId, span: Span) {
-                if TypeAliasBounds::is_type_variable_assoc(qpath) {
-                    self.err.span_help(span, fluent::lint_builtin_type_alias_bounds_help);
-                }
-                intravisit::walk_qpath(self, qpath, id)
-            }
-        }
-
-        // Let's go for a walk!
-        let mut visitor = WalkAssocTypes { err };
-        visitor.visit_ty(ty);
-    }
 }
 
 impl<'tcx> LateLintPass<'tcx> for TypeAliasBounds {
@@ -1517,35 +1483,31 @@ impl<'tcx> LateLintPass<'tcx> for TypeAliasBounds {
 
         let mut suggested_changing_assoc_types = false;
         if !where_spans.is_empty() {
-            cx.lint(TYPE_ALIAS_BOUNDS, fluent::lint_builtin_type_alias_where_clause, |lint| {
-                lint.set_span(where_spans);
-                lint.span_suggestion(
-                    type_alias_generics.where_clause_span,
-                    fluent::suggestion,
-                    "",
-                    Applicability::MachineApplicable,
-                );
-                if !suggested_changing_assoc_types {
-                    TypeAliasBounds::suggest_changing_assoc_types(ty, lint);
-                    suggested_changing_assoc_types = true;
-                }
-                lint
+            let sub = (!suggested_changing_assoc_types).then(|| {
+                suggested_changing_assoc_types = true;
+                SuggestChangingAssocTypes { ty }
             });
+            cx.emit_spanned_lint(
+                TYPE_ALIAS_BOUNDS,
+                where_spans,
+                BuiltinTypeAliasWhereClause {
+                    suggestion: type_alias_generics.where_clause_span,
+                    sub,
+                },
+            );
         }
 
         if !inline_spans.is_empty() {
-            cx.lint(TYPE_ALIAS_BOUNDS, fluent::lint_builtin_type_alias_generic_bounds, |lint| {
-                lint.set_span(inline_spans);
-                lint.multipart_suggestion(
-                    fluent::suggestion,
-                    inline_sugg,
-                    Applicability::MachineApplicable,
-                );
-                if !suggested_changing_assoc_types {
-                    TypeAliasBounds::suggest_changing_assoc_types(ty, lint);
-                }
-                lint
+            let suggestion = BuiltinTypeAliasGenericBoundsSuggestion { suggestions: inline_sugg };
+            let sub = (!suggested_changing_assoc_types).then(|| {
+                suggested_changing_assoc_types = true;
+                SuggestChangingAssocTypes { ty }
             });
+            cx.emit_spanned_lint(
+                TYPE_ALIAS_BOUNDS,
+                inline_spans,
+                BuiltinTypeAliasGenericBounds { suggestion, sub },
+            );
         }
     }
 }
@@ -2376,6 +2338,36 @@ declare_lint! {
 
 declare_lint_pass!(InvalidValue => [INVALID_VALUE]);
 
+/// Information about why a type cannot be initialized this way.
+pub struct InitError {
+    pub(crate) message: String,
+    /// Spans from struct fields and similar that can be obtained from just the type.
+    pub(crate) span: Option<Span>,
+    /// Used to report a trace through adts.
+    pub(crate) nested: Option<Box<InitError>>,
+}
+impl InitError {
+    fn spanned(self, span: Span) -> InitError {
+        Self { span: Some(span), ..self }
+    }
+
+    fn nested(self, nested: impl Into<Option<InitError>>) -> InitError {
+        assert!(self.nested.is_none());
+        Self { nested: nested.into().map(Box::new), ..self }
+    }
+}
+
+impl<'a> From<&'a str> for InitError {
+    fn from(s: &'a str) -> Self {
+        s.to_owned().into()
+    }
+}
+impl From<String> for InitError {
+    fn from(message: String) -> Self {
+        Self { message, span: None, nested: None }
+    }
+}
+
 impl<'tcx> LateLintPass<'tcx> for InvalidValue {
     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &hir::Expr<'_>) {
         #[derive(Debug, Copy, Clone, PartialEq)]
@@ -2384,36 +2376,6 @@ impl<'tcx> LateLintPass<'tcx> for InvalidValue {
             Uninit,
         }
 
-        /// Information about why a type cannot be initialized this way.
-        struct InitError {
-            message: String,
-            /// Spans from struct fields and similar that can be obtained from just the type.
-            span: Option<Span>,
-            /// Used to report a trace through adts.
-            nested: Option<Box<InitError>>,
-        }
-        impl InitError {
-            fn spanned(self, span: Span) -> InitError {
-                Self { span: Some(span), ..self }
-            }
-
-            fn nested(self, nested: impl Into<Option<InitError>>) -> InitError {
-                assert!(self.nested.is_none());
-                Self { nested: nested.into().map(Box::new), ..self }
-            }
-        }
-
-        impl<'a> From<&'a str> for InitError {
-            fn from(s: &'a str) -> Self {
-                s.to_owned().into()
-            }
-        }
-        impl From<String> for InitError {
-            fn from(message: String) -> Self {
-                Self { message, span: None, nested: None }
-            }
-        }
-
         /// Test if this constant is all-0.
         fn is_zero(expr: &hir::Expr<'_>) -> bool {
             use hir::ExprKind::*;
@@ -2637,46 +2599,16 @@ impl<'tcx> LateLintPass<'tcx> for InvalidValue {
             // using zeroed or uninitialized memory.
             // We are extremely conservative with what we warn about.
             let conjured_ty = cx.typeck_results().expr_ty(expr);
-            if let Some(mut err) = with_no_trimmed_paths!(ty_find_init_error(cx, conjured_ty, init))
-            {
-                // FIXME(davidtwco): make translatable
-                cx.struct_span_lint(
+            if let Some(err) = with_no_trimmed_paths!(ty_find_init_error(cx, conjured_ty, init)) {
+                let msg = match init {
+                    InitKind::Zeroed => fluent::lint_builtin_unpermitted_type_init_zeroed,
+                    InitKind::Uninit => fluent::lint_builtin_unpermitted_type_init_unint,
+                };
+                let sub = BuiltinUnpermittedTypeInitSub { err };
+                cx.emit_spanned_lint(
                     INVALID_VALUE,
                     expr.span,
-                    DelayDm(|| {
-                        format!(
-                            "the type `{}` does not permit {}",
-                            conjured_ty,
-                            match init {
-                                InitKind::Zeroed => "zero-initialization",
-                                InitKind::Uninit => "being left uninitialized",
-                            },
-                        )
-                    }),
-                    |lint| {
-                        lint.span_label(
-                            expr.span,
-                            "this code causes undefined behavior when executed",
-                        );
-                        lint.span_label(
-                            expr.span,
-                            "help: use `MaybeUninit<T>` instead, \
-                            and only call `assume_init` after initialization is done",
-                        );
-                        loop {
-                            if let Some(span) = err.span {
-                                lint.span_note(span, &err.message);
-                            } else {
-                                lint.note(&err.message);
-                            }
-                            if let Some(e) = err.nested {
-                                err = *e;
-                            } else {
-                                break;
-                            }
-                        }
-                        lint
-                    },
+                    BuiltinUnpermittedTypeInit { msg, ty: conjured_ty, label: expr.span, sub },
                 );
             }
         }
@@ -3022,31 +2954,44 @@ impl<'tcx> LateLintPass<'tcx> for ClashingExternDeclarations {
                             SymbolName::Normal(_) => fi.span,
                             SymbolName::Link(_, annot_span) => fi.span.to(annot_span),
                         };
-                    // Finally, emit the diagnostic.
 
-                    let msg = if orig.get_name() == this_fi.ident.name {
-                        fluent::lint_builtin_clashing_extern_same_name
+                    // Finally, emit the diagnostic.
+                    let mut expected_str = DiagnosticStyledString::new();
+                    expected_str.push(existing_decl_ty.fn_sig(tcx).to_string(), false);
+                    let mut found_str = DiagnosticStyledString::new();
+                    found_str.push(this_decl_ty.fn_sig(tcx).to_string(), true);
+
+                    let this = this_fi.ident.name;
+                    let orig = orig.get_name();
+                    let previous_decl_label = get_relevant_span(orig_fi);
+                    let mismatch_label = get_relevant_span(this_fi);
+                    let sub = BuiltinClashingExternSub {
+                        tcx,
+                        expected: existing_decl_ty,
+                        found: this_decl_ty,
+                    };
+                    let decorator = if orig == this {
+                        BuiltinClashingExtern::SameName {
+                            this,
+                            orig,
+                            previous_decl_label,
+                            mismatch_label,
+                            sub,
+                        }
                     } else {
-                        fluent::lint_builtin_clashing_extern_diff_name
+                        BuiltinClashingExtern::DiffName {
+                            this,
+                            orig,
+                            previous_decl_label,
+                            mismatch_label,
+                            sub,
+                        }
                     };
-                    tcx.struct_span_lint_hir(
+                    tcx.emit_spanned_lint(
                         CLASHING_EXTERN_DECLARATIONS,
                         this_fi.hir_id(),
                         get_relevant_span(this_fi),
-                        msg,
-                        |lint| {
-                            let mut expected_str = DiagnosticStyledString::new();
-                            expected_str.push(existing_decl_ty.fn_sig(tcx).to_string(), false);
-                            let mut found_str = DiagnosticStyledString::new();
-                            found_str.push(this_decl_ty.fn_sig(tcx).to_string(), true);
-
-                            lint.set_arg("this_fi", this_fi.ident.name)
-                                .set_arg("orig", orig.get_name())
-                                .span_label(get_relevant_span(orig_fi), fluent::previous_decl_label)
-                                .span_label(get_relevant_span(this_fi), fluent::mismatch_label)
-                                // FIXME(davidtwco): translatable expected/found
-                                .note_expected_found(&"", expected_str, &"", found_str)
-                        },
+                        decorator,
                     );
                 }
             }
diff --git a/compiler/rustc_lint/src/lints.rs b/compiler/rustc_lint/src/lints.rs
index 147cd3e0460..ee6fa5300f4 100644
--- a/compiler/rustc_lint/src/lints.rs
+++ b/compiler/rustc_lint/src/lints.rs
@@ -1,14 +1,18 @@
 use std::num::NonZeroU32;
 
 use rustc_errors::{
-    fluent, AddToDiagnostic, Applicability, DecorateLint, DiagnosticMessage, SuggestionStyle,
+    fluent, AddToDiagnostic, Applicability, DecorateLint, DiagnosticMessage,
+    DiagnosticStyledString, SuggestionStyle,
 };
 use rustc_hir::def_id::DefId;
 use rustc_macros::{LintDiagnostic, Subdiagnostic};
 use rustc_middle::ty::{Predicate, Ty, TyCtxt};
-use rustc_span::{edition::Edition, symbol::Ident, Span, Symbol};
+use rustc_session::parse::ParseSess;
+use rustc_span::{edition::Edition, sym, symbol::Ident, Span, Symbol};
 
-use crate::{errors::OverruledAttributeSub, LateContext};
+use crate::{
+    builtin::InitError, builtin::TypeAliasBounds, errors::OverruledAttributeSub, LateContext,
+};
 
 // array_into_iter.rs
 #[derive(LintDiagnostic)]
@@ -142,7 +146,31 @@ pub struct BuiltinAnonymousParams<'a> {
     pub ty_snip: &'a str,
 }
 
-// FIXME: add lint::builtin_deprecated_attr_link
+// FIXME(davidtwco) translatable deprecated attr
+#[derive(LintDiagnostic)]
+#[diag(lint_builtin_deprecated_attr_link)]
+pub struct BuiltinDeprecatedAttrLink<'a> {
+    pub name: Symbol,
+    pub reason: &'a str,
+    pub link: &'a str,
+    #[subdiagnostic]
+    pub suggestion: BuiltinDeprecatedAttrLinkSuggestion<'a>,
+}
+
+#[derive(Subdiagnostic)]
+pub enum BuiltinDeprecatedAttrLinkSuggestion<'a> {
+    #[suggestion(msg_suggestion, code = "", applicability = "machine-applicable")]
+    Msg {
+        #[primary_span]
+        suggestion: Span,
+        msg: &'a str,
+    },
+    #[suggestion(default_suggestion, code = "", applicability = "machine-applicable")]
+    Default {
+        #[primary_span]
+        suggestion: Span,
+    },
+}
 
 #[derive(LintDiagnostic)]
 #[diag(lint_builtin_deprecated_attr_used)]
@@ -199,6 +227,31 @@ pub struct BuiltinMutablesTransmutes;
 #[diag(lint_builtin_unstable_features)]
 pub struct BuiltinUnstableFeatures;
 
+// lint_ungated_async_fn_track_caller
+pub struct BuiltinUngatedAsyncFnTrackCaller<'a> {
+    pub label: Span,
+    pub parse_sess: &'a ParseSess,
+}
+
+impl<'a> DecorateLint<'a, ()> for BuiltinUngatedAsyncFnTrackCaller<'_> {
+    fn decorate_lint<'b>(
+        self,
+        diag: &'b mut rustc_errors::DiagnosticBuilder<'a, ()>,
+    ) -> &'b mut rustc_errors::DiagnosticBuilder<'a, ()> {
+        diag.span_label(self.label, fluent::label);
+        rustc_session::parse::add_feature_diagnostics(
+            diag,
+            &self.parse_sess,
+            sym::closure_track_caller,
+        );
+        diag
+    }
+
+    fn msg(&self) -> DiagnosticMessage {
+        fluent::lint_ungated_async_fn_track_caller
+    }
+}
+
 #[derive(LintDiagnostic)]
 #[diag(lint_builtin_unreachable_pub)]
 pub struct BuiltinUnreachablePub<'a> {
@@ -209,9 +262,83 @@ pub struct BuiltinUnreachablePub<'a> {
     pub help: Option<()>,
 }
 
-// FIXME: migrate builtin_type_alias_where_clause
+pub struct SuggestChangingAssocTypes<'a, 'b> {
+    pub ty: &'a rustc_hir::Ty<'b>,
+}
+
+impl AddToDiagnostic for SuggestChangingAssocTypes<'_, '_> {
+    fn add_to_diagnostic_with<F>(self, diag: &mut rustc_errors::Diagnostic, _: F)
+    where
+        F: Fn(
+            &mut rustc_errors::Diagnostic,
+            rustc_errors::SubdiagnosticMessage,
+        ) -> rustc_errors::SubdiagnosticMessage,
+    {
+        // Access to associates types should use `<T as Bound>::Assoc`, which does not need a
+        // bound.  Let's see if this type does that.
+
+        // We use a HIR visitor to walk the type.
+        use rustc_hir::intravisit::{self, Visitor};
+        struct WalkAssocTypes<'a> {
+            err: &'a mut rustc_errors::Diagnostic,
+        }
+        impl Visitor<'_> for WalkAssocTypes<'_> {
+            fn visit_qpath(
+                &mut self,
+                qpath: &rustc_hir::QPath<'_>,
+                id: rustc_hir::HirId,
+                span: Span,
+            ) {
+                if TypeAliasBounds::is_type_variable_assoc(qpath) {
+                    self.err.span_help(span, fluent::lint_builtin_type_alias_bounds_help);
+                }
+                intravisit::walk_qpath(self, qpath, id)
+            }
+        }
+
+        // Let's go for a walk!
+        let mut visitor = WalkAssocTypes { err: diag };
+        visitor.visit_ty(self.ty);
+    }
+}
 
-// FIXME: migrate builtin_type_alias_generic_bounds
+#[derive(LintDiagnostic)]
+#[diag(lint_builtin_type_alias_where_clause)]
+pub struct BuiltinTypeAliasWhereClause<'a, 'b> {
+    #[suggestion(code = "", applicability = "machine-applicable")]
+    pub suggestion: Span,
+    #[subdiagnostic]
+    pub sub: Option<SuggestChangingAssocTypes<'a, 'b>>,
+}
+
+#[derive(LintDiagnostic)]
+#[diag(lint_builtin_type_alias_generic_bounds)]
+pub struct BuiltinTypeAliasGenericBounds<'a, 'b> {
+    #[subdiagnostic]
+    pub suggestion: BuiltinTypeAliasGenericBoundsSuggestion,
+    #[subdiagnostic]
+    pub sub: Option<SuggestChangingAssocTypes<'a, 'b>>,
+}
+
+pub struct BuiltinTypeAliasGenericBoundsSuggestion {
+    pub suggestions: Vec<(Span, String)>,
+}
+
+impl AddToDiagnostic for BuiltinTypeAliasGenericBoundsSuggestion {
+    fn add_to_diagnostic_with<F>(self, diag: &mut rustc_errors::Diagnostic, _: F)
+    where
+        F: Fn(
+            &mut rustc_errors::Diagnostic,
+            rustc_errors::SubdiagnosticMessage,
+        ) -> rustc_errors::SubdiagnosticMessage,
+    {
+        diag.multipart_suggestion(
+            fluent::suggestion,
+            self.suggestions,
+            Applicability::MachineApplicable,
+        );
+    }
+}
 
 #[derive(LintDiagnostic)]
 #[diag(lint_builtin_trivial_bounds)]
@@ -285,9 +412,107 @@ pub struct BuiltinIncompleteFeaturesNote {
     pub n: NonZeroU32,
 }
 
-// FIXME: migrate "the type `{}` does not permit {}"
+pub struct BuiltinUnpermittedTypeInit<'a> {
+    pub msg: DiagnosticMessage,
+    pub ty: Ty<'a>,
+    pub label: Span,
+    pub sub: BuiltinUnpermittedTypeInitSub,
+}
+
+impl<'a> DecorateLint<'a, ()> for BuiltinUnpermittedTypeInit<'_> {
+    fn decorate_lint<'b>(
+        self,
+        diag: &'b mut rustc_errors::DiagnosticBuilder<'a, ()>,
+    ) -> &'b mut rustc_errors::DiagnosticBuilder<'a, ()> {
+        diag.set_arg("ty", self.ty);
+        diag.span_label(self.label, fluent::lint_builtin_unpermitted_type_init_label);
+        diag.span_label(self.label, fluent::lint_builtin_unpermitted_type_init_label_suggestion);
+        self.sub.add_to_diagnostic(diag);
+        diag
+    }
+
+    fn msg(&self) -> rustc_errors::DiagnosticMessage {
+        self.msg.clone()
+    }
+}
+
+// FIXME(davidtwco): make translatable
+pub struct BuiltinUnpermittedTypeInitSub {
+    pub err: InitError,
+}
 
-// FIXME: fluent::lint::builtin_clashing_extern_{same,diff}_name
+impl AddToDiagnostic for BuiltinUnpermittedTypeInitSub {
+    fn add_to_diagnostic_with<F>(self, diag: &mut rustc_errors::Diagnostic, _: F)
+    where
+        F: Fn(
+            &mut rustc_errors::Diagnostic,
+            rustc_errors::SubdiagnosticMessage,
+        ) -> rustc_errors::SubdiagnosticMessage,
+    {
+        let mut err = self.err;
+        loop {
+            if let Some(span) = err.span {
+                diag.span_note(span, err.message);
+            } else {
+                diag.note(err.message);
+            }
+            if let Some(e) = err.nested {
+                err = *e;
+            } else {
+                break;
+            }
+        }
+    }
+}
+
+#[derive(LintDiagnostic)]
+pub enum BuiltinClashingExtern<'a> {
+    #[diag(lint_builtin_clashing_extern_same_name)]
+    SameName {
+        this: Symbol,
+        orig: Symbol,
+        #[label(previous_decl_label)]
+        previous_decl_label: Span,
+        #[label(mismatch_label)]
+        mismatch_label: Span,
+        #[subdiagnostic]
+        sub: BuiltinClashingExternSub<'a>,
+    },
+    #[diag(lint_builtin_clashing_extern_diff_name)]
+    DiffName {
+        this: Symbol,
+        orig: Symbol,
+        #[label(previous_decl_label)]
+        previous_decl_label: Span,
+        #[label(mismatch_label)]
+        mismatch_label: Span,
+        #[subdiagnostic]
+        sub: BuiltinClashingExternSub<'a>,
+    },
+}
+
+// FIXME(davidtwco): translatable expected/found
+pub struct BuiltinClashingExternSub<'a> {
+    pub tcx: TyCtxt<'a>,
+    pub expected: Ty<'a>,
+    pub found: Ty<'a>,
+}
+
+impl AddToDiagnostic for BuiltinClashingExternSub<'_> {
+    fn add_to_diagnostic_with<F>(self, diag: &mut rustc_errors::Diagnostic, _: F)
+    where
+        F: Fn(
+            &mut rustc_errors::Diagnostic,
+            rustc_errors::SubdiagnosticMessage,
+        ) -> rustc_errors::SubdiagnosticMessage,
+    {
+        let mut expected_str = DiagnosticStyledString::new();
+        expected_str.push(self.expected.fn_sig(self.tcx).to_string(), false);
+        let mut found_str = DiagnosticStyledString::new();
+        found_str.push(self.found.fn_sig(self.tcx).to_string(), true);
+        diag.note_expected_found(&"", expected_str, &"", found_str);
+    }
+}
 
 #[derive(LintDiagnostic)]
 #[diag(lint_builtin_deref_nullptr)]