Migrate diagnostics in rustc_hir_analysis/src/coherence/orphan.rs

This commit is contained in:
Obei Sideg 2023-10-03 20:17:40 +03:00
parent 271dcc1d40
commit 68f5536667
3 changed files with 341 additions and 163 deletions

View File

@ -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 =

View File

@ -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_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 @@ enum NonlocalImpl {
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 (`{}`)",
Some(local_type) => tcx.sess.emit_err(errors::TyParamFirstLocal {
span: sp,
note: (),
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<{}>`)",
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>(

View File

@ -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>,
}