Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 44 additions & 6 deletions compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1256,7 +1256,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
self.suggest_cloning_inner(err, ty, expr);
}
} else if let ty::Adt(def, args) = ty.kind()
&& def.did().as_local().is_some()
&& let Some(local_did) = def.did().as_local()
&& def.variants().iter().all(|variant| {
variant
.fields
Expand All @@ -1266,12 +1266,50 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
{
let ty_span = self.infcx.tcx.def_span(def.did());
let mut span: MultiSpan = ty_span.into();
span.push_span_label(ty_span, "consider implementing `Clone` for this type");
span.push_span_label(expr.span, "you could clone this value");
err.span_note(
span,
format!("if `{ty}` implemented `Clone`, you could clone the value"),
let mut derive_clone = false;
self.infcx.tcx.for_each_relevant_impl(
self.infcx.tcx.lang_items().clone_trait().unwrap(),
ty,
|def_id| {
if self.infcx.tcx.is_automatically_derived(def_id) {
derive_clone = true;
span.push_span_label(
self.infcx.tcx.def_span(def_id),
"derived `Clone` adds implicit bounds on type parameters",
);
if let Some(generics) = self.infcx.tcx.hir_get_generics(local_did) {
for param in generics.params {
if let hir::GenericParamKind::Type { .. } = param.kind {
span.push_span_label(
param.span,
format!(
"introduces an implicit `{}: Clone` bound",
param.name.ident()
),
);
}
}
}
}
},
);
let msg = if !derive_clone {
span.push_span_label(
ty_span,
format!(
"consider {}implementing `Clone` for this type",
if derive_clone { "manually " } else { "" }
),
);
format!("if `{ty}` implemented `Clone`, you could clone the value")
} else {
format!("if all bounds were met, you could clone the value")
};
span.push_span_label(expr.span, "you could clone this value");
err.span_note(span, msg);
if derive_clone {
err.help("consider manually implementing `Clone` to avoid undesired bounds");
}
} else if let ty::Param(param) = ty.kind()
&& let Some(_clone_trait_def) = self.infcx.tcx.lang_items().clone_trait()
&& let generics = self.infcx.tcx.generics_of(self.mir_def_id())
Expand Down
21 changes: 11 additions & 10 deletions compiler/rustc_builtin_macros/src/deriving/generic/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -638,27 +638,27 @@ impl<'a> TraitDef<'a> {
GenericParamKind::Type { .. } => {
// Extra restrictions on the generics parameters to the
// type being derived upon.
let span = param.ident.span.with_ctxt(ctxt);
let bounds: Vec<_> = self
.additional_bounds
.iter()
.map(|p| {
cx.trait_bound(
p.to_path(cx, self.span, type_ident, generics),
self.is_const,
)
cx.trait_bound(p.to_path(cx, span, type_ident, generics), self.is_const)
})
.chain(
// Add a bound for the current trait.
self.skip_path_as_bound
.not()
.then(|| cx.trait_bound(trait_path.clone(), self.is_const)),
self.skip_path_as_bound.not().then(|| {
let mut trait_path = trait_path.clone();
trait_path.span = span;
cx.trait_bound(trait_path, self.is_const)
}),
)
.chain({
// Add a `Copy` bound if required.
if is_packed && self.needs_copy_as_bound_if_packed {
let p = deriving::path_std!(marker::Copy);
Some(cx.trait_bound(
p.to_path(cx, self.span, type_ident, generics),
p.to_path(cx, span, type_ident, generics),
self.is_const,
))
} else {
Expand All @@ -671,7 +671,7 @@ impl<'a> TraitDef<'a> {
)
.collect();

cx.typaram(param.ident.span.with_ctxt(ctxt), param.ident, bounds, None)
cx.typaram(span, param.ident, bounds, None)
}
GenericParamKind::Const { ty, span, .. } => {
let const_nodefault_kind = GenericParamKind::Const {
Expand Down Expand Up @@ -791,7 +791,8 @@ impl<'a> TraitDef<'a> {
.collect();

// Create the type of `self`.
let path = cx.path_all(self.span, false, vec![type_ident], self_params);
let path =
cx.path_all(type_ident.span.with_ctxt(ctxt), false, vec![type_ident], self_params);
let self_type = cx.ty_path(path);
let rustc_const_unstable =
cx.path_ident(self.span, Ident::new(sym::rustc_const_unstable, self.span));
Expand Down
11 changes: 8 additions & 3 deletions compiler/rustc_hir/src/hir.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// ignore-tidy-filelength
use std::borrow::Cow;
use std::fmt;
use std::ops::Not;

use rustc_abi::ExternAbi;
use rustc_ast::attr::AttributeExt;
Expand Down Expand Up @@ -1012,10 +1013,14 @@ impl<'hir> Generics<'hir> {

span_for_parentheses.map_or_else(
|| {
// We include bounds that come from a `#[derive(_)]` but point at the user's code,
// as we use this method to get a span appropriate for suggestions.
// We include bounds that come from a `#[derive(_)]` but point at the user's
// code, as we use this method to get a span appropriate for suggestions.
let bs = bound.span();
bs.can_be_used_for_suggestions().then(|| (bs.shrink_to_hi(), None))
// We use `from_expansion` instead of `can_be_used_for_suggestions` because
// the trait bound from imperfect derives do point at the type parameter,
// but expanded to a where clause, so we want to ignore those. This is only
// true for derive intrinsics.
bs.from_expansion().not().then(|| (bs.shrink_to_hi(), None))
},
|span| Some((span.shrink_to_hi(), Some(span.shrink_to_lo()))),
)
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_hir_analysis/messages.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,7 @@ hir_analysis_copy_impl_on_non_adt =
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
.note = destructor declared here

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
Expand Down
5 changes: 3 additions & 2 deletions compiler/rustc_hir_analysis/src/coherence/builtin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -121,9 +121,10 @@ fn visit_implementation_of_copy(checker: &Checker<'_>) -> Result<(), ErrorGuaran
let span = tcx.hir_expect_item(impl_did).expect_impl().self_ty.span;
Err(tcx.dcx().emit_err(errors::CopyImplOnNonAdt { span }))
}
Err(CopyImplementationError::HasDestructor) => {
Err(CopyImplementationError::HasDestructor(did)) => {
let span = tcx.hir_expect_item(impl_did).expect_impl().self_ty.span;
Err(tcx.dcx().emit_err(errors::CopyImplOnTypeWithDtor { span }))
let impl_ = tcx.def_span(did);
Err(tcx.dcx().emit_err(errors::CopyImplOnTypeWithDtor { span, impl_ }))
}
Err(CopyImplementationError::HasUnsafeFields) => {
let span = tcx.hir_expect_item(impl_did).expect_impl().self_ty.span;
Expand Down
2 changes: 2 additions & 0 deletions compiler/rustc_hir_analysis/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -278,6 +278,8 @@ pub(crate) struct CopyImplOnTypeWithDtor {
#[primary_span]
#[label]
pub span: Span,
#[note]
pub impl_: Span,
}

#[derive(Diagnostic)]
Expand Down
6 changes: 5 additions & 1 deletion compiler/rustc_hir_analysis/src/hir_ty_lowering/dyn_trait.rs
Original file line number Diff line number Diff line change
Expand Up @@ -592,7 +592,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
if span.can_be_used_for_suggestions()
&& poly_trait_ref.trait_ref.trait_def_id().is_some()
&& !self.maybe_suggest_impl_trait(span, hir_id, hir_bounds, &mut diag)
&& !self.maybe_suggest_dyn_trait(hir_id, sugg, &mut diag)
&& !self.maybe_suggest_dyn_trait(hir_id, span, sugg, &mut diag)
{
self.maybe_suggest_add_generic_impl_trait(span, hir_id, &mut diag);
}
Expand Down Expand Up @@ -750,10 +750,14 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
fn maybe_suggest_dyn_trait(
&self,
hir_id: hir::HirId,
span: Span,
sugg: Vec<(Span, String)>,
diag: &mut Diag<'_>,
) -> bool {
let tcx = self.tcx();
if span.in_derive_expansion() {
return false;
}

// Look at the direct HIR parent, since we care about the relationship between
// the type and the thing that directly encloses it.
Expand Down
95 changes: 83 additions & 12 deletions compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1932,25 +1932,94 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
None,
);
} else {
let mut suggest_derive = true;
if let Some(errors) =
self.type_implements_trait_shallow(clone_trait_did, expected_ty, self.param_env)
{
let manually_impl = "consider manually implementing `Clone` to avoid the \
implicit type parameter bounds";
match &errors[..] {
[] => {}
[error] => {
diag.help(format!(
"`Clone` is not implemented because the trait bound `{}` is \
not satisfied",
error.obligation.predicate,
));
let msg = "`Clone` is not implemented because a trait bound is not \
satisfied";
if let traits::ObligationCauseCode::ImplDerived(data) =
error.obligation.cause.code()
{
let mut span: MultiSpan = data.span.into();
if self.tcx.is_automatically_derived(data.impl_or_alias_def_id) {
span.push_span_label(
data.span,
format!(
"derive introduces an implicit `{}` bound",
error.obligation.predicate
),
);
}
diag.span_help(span, msg);
if self.tcx.is_automatically_derived(data.impl_or_alias_def_id)
&& data.impl_or_alias_def_id.is_local()
{
diag.help(manually_impl);
suggest_derive = false;
}
} else {
diag.help(msg);
}
}
_ => {
diag.help(format!(
"`Clone` is not implemented because the following trait bounds \
could not be satisfied: {}",
listify(&errors, |e| format!("`{}`", e.obligation.predicate))
.unwrap(),
));
let unsatisfied_bounds: Vec<_> = errors
.iter()
.filter_map(|error| match error.obligation.cause.code() {
traits::ObligationCauseCode::ImplDerived(data) => {
let pre = if self
.tcx
.is_automatically_derived(data.impl_or_alias_def_id)
{
"derive introduces an implicit "
} else {
""
};
Some((
data.span,
format!(
"{pre}unsatisfied trait bound `{}`",
error.obligation.predicate
),
))
}
_ => None,
})
.collect();
let msg = "`Clone` is not implemented because the some trait bounds \
could not be satisfied";
if errors.len() == unsatisfied_bounds.len() {
let mut unsatisfied_bounds_spans: MultiSpan = unsatisfied_bounds
.iter()
.map(|(span, _)| *span)
.collect::<Vec<Span>>()
.into();
for (span, label) in unsatisfied_bounds {
unsatisfied_bounds_spans.push_span_label(span, label);
}
diag.span_help(unsatisfied_bounds_spans, msg);
if errors.iter().all(|error| match error.obligation.cause.code() {
traits::ObligationCauseCode::ImplDerived(data) => {
self.tcx.is_automatically_derived(data.impl_or_alias_def_id)
&& data.impl_or_alias_def_id.is_local()
}
_ => false,
}) {
diag.help(manually_impl);
suggest_derive = false;
}
} else {
diag.help(format!(
"{msg}: {}",
listify(&errors, |e| format!("`{}`", e.obligation.predicate))
.unwrap(),
));
}
}
}
for error in errors {
Expand All @@ -1968,7 +2037,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}
}
}
self.suggest_derive(diag, &vec![(trait_ref.upcast(self.tcx), None, None)]);
if suggest_derive {
self.suggest_derive(diag, &vec![(trait_ref.upcast(self.tcx), None, None)]);
}
}
}
}
Expand Down
Loading
Loading