| use rustc_ast::TraitObjectSyntax; |
| use rustc_errors::{Diagnostic, StashKey}; |
| use rustc_hir as hir; |
| use rustc_lint_defs::{builtin::BARE_TRAIT_OBJECTS, Applicability}; |
| use rustc_trait_selection::traits::error_reporting::suggestions::NextTypeParamName; |
| |
| use super::AstConv; |
| |
| impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { |
| /// Make sure that we are in the condition to suggest the blanket implementation. |
| pub(super) fn maybe_lint_blanket_trait_impl( |
| &self, |
| self_ty: &hir::Ty<'_>, |
| diag: &mut Diagnostic, |
| ) { |
| let tcx = self.tcx(); |
| let parent_id = tcx.hir().get_parent_item(self_ty.hir_id).def_id; |
| if let hir::Node::Item(hir::Item { |
| kind: |
| hir::ItemKind::Impl(hir::Impl { |
| self_ty: impl_self_ty, |
| of_trait: Some(of_trait_ref), |
| generics, |
| .. |
| }), |
| .. |
| }) = tcx.hir().get_by_def_id(parent_id) |
| && self_ty.hir_id == impl_self_ty.hir_id |
| { |
| if !of_trait_ref.trait_def_id().is_some_and(|def_id| def_id.is_local()) { |
| return; |
| } |
| let of_trait_span = of_trait_ref.path.span; |
| // make sure that we are not calling unwrap to abort during the compilation |
| let Ok(impl_trait_name) = tcx.sess.source_map().span_to_snippet(self_ty.span) else { |
| return; |
| }; |
| let Ok(of_trait_name) = tcx.sess.source_map().span_to_snippet(of_trait_span) else { |
| return; |
| }; |
| // check if the trait has generics, to make a correct suggestion |
| let param_name = generics.params.next_type_param_name(None); |
| |
| let add_generic_sugg = if let Some(span) = generics.span_for_param_suggestion() { |
| (span, format!(", {param_name}: {impl_trait_name}")) |
| } else { |
| (generics.span, format!("<{param_name}: {impl_trait_name}>")) |
| }; |
| diag.multipart_suggestion( |
| format!( |
| "alternatively use a blanket \ |
| implementation to implement `{of_trait_name}` for \ |
| all types that also implement `{impl_trait_name}`" |
| ), |
| vec![(self_ty.span, param_name), add_generic_sugg], |
| Applicability::MaybeIncorrect, |
| ); |
| } |
| } |
| |
| pub(super) fn maybe_lint_bare_trait(&self, self_ty: &hir::Ty<'_>, in_path: bool) { |
| let tcx = self.tcx(); |
| if let hir::TyKind::TraitObject([poly_trait_ref, ..], _, TraitObjectSyntax::None) = |
| self_ty.kind |
| { |
| let needs_bracket = in_path |
| && !tcx |
| .sess |
| .source_map() |
| .span_to_prev_source(self_ty.span) |
| .ok() |
| .is_some_and(|s| s.trim_end().ends_with('<')); |
| |
| let is_global = poly_trait_ref.trait_ref.path.is_global(); |
| |
| let mut sugg = Vec::from_iter([( |
| self_ty.span.shrink_to_lo(), |
| format!( |
| "{}dyn {}", |
| if needs_bracket { "<" } else { "" }, |
| if is_global { "(" } else { "" }, |
| ), |
| )]); |
| |
| if is_global || needs_bracket { |
| sugg.push(( |
| self_ty.span.shrink_to_hi(), |
| format!( |
| "{}{}", |
| if is_global { ")" } else { "" }, |
| if needs_bracket { ">" } else { "" }, |
| ), |
| )); |
| } |
| |
| if self_ty.span.edition().at_least_rust_2021() { |
| let msg = "trait objects must include the `dyn` keyword"; |
| let label = "add `dyn` keyword before this trait"; |
| let mut diag = |
| rustc_errors::struct_span_err!(tcx.sess, self_ty.span, E0782, "{}", msg); |
| if self_ty.span.can_be_used_for_suggestions() { |
| diag.multipart_suggestion_verbose( |
| label, |
| sugg, |
| Applicability::MachineApplicable, |
| ); |
| } |
| // check if the impl trait that we are considering is a impl of a local trait |
| self.maybe_lint_blanket_trait_impl(&self_ty, &mut diag); |
| diag.stash(self_ty.span, StashKey::TraitMissingMethod); |
| } else { |
| let msg = "trait objects without an explicit `dyn` are deprecated"; |
| tcx.struct_span_lint_hir( |
| BARE_TRAIT_OBJECTS, |
| self_ty.hir_id, |
| self_ty.span, |
| msg, |
| |lint| { |
| lint.multipart_suggestion_verbose( |
| "use `dyn`", |
| sugg, |
| Applicability::MachineApplicable, |
| ); |
| self.maybe_lint_blanket_trait_impl(&self_ty, lint); |
| lint |
| }, |
| ); |
| } |
| } |
| } |
| } |