Move all error reporting into rustc_trait_selection
This commit is contained in:
parent
f49738ba6c
commit
ce8a625092
76 changed files with 2541 additions and 2531 deletions
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
@ -1,163 +0,0 @@
|
|||
//! Error Reporting for Anonymous Region Lifetime Errors
|
||||
//! where both the regions are anonymous.
|
||||
|
||||
use crate::error_reporting::infer::nice_region_error::find_anon_type::find_anon_type;
|
||||
use crate::error_reporting::infer::nice_region_error::util::AnonymousParamInfo;
|
||||
use crate::error_reporting::infer::nice_region_error::NiceRegionError;
|
||||
use crate::errors::AddLifetimeParamsSuggestion;
|
||||
use crate::errors::LifetimeMismatch;
|
||||
use crate::errors::LifetimeMismatchLabels;
|
||||
use crate::infer::RegionResolutionError;
|
||||
use crate::infer::SubregionOrigin;
|
||||
|
||||
use rustc_errors::Subdiagnostic;
|
||||
use rustc_errors::{Diag, ErrorGuaranteed};
|
||||
use rustc_hir::def_id::LocalDefId;
|
||||
use rustc_hir::Ty;
|
||||
use rustc_middle::ty::{Region, TyCtxt};
|
||||
|
||||
impl<'a, 'tcx> NiceRegionError<'a, 'tcx> {
|
||||
/// Print the error message for lifetime errors when both the concerned regions are anonymous.
|
||||
///
|
||||
/// Consider a case where we have
|
||||
///
|
||||
/// ```compile_fail
|
||||
/// fn foo(x: &mut Vec<&u8>, y: &u8) {
|
||||
/// x.push(y);
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// The example gives
|
||||
///
|
||||
/// ```text
|
||||
/// fn foo(x: &mut Vec<&u8>, y: &u8) {
|
||||
/// --- --- these references are declared with different lifetimes...
|
||||
/// x.push(y);
|
||||
/// ^ ...but data from `y` flows into `x` here
|
||||
/// ```
|
||||
///
|
||||
/// It has been extended for the case of structs too.
|
||||
///
|
||||
/// Consider the example
|
||||
///
|
||||
/// ```no_run
|
||||
/// struct Ref<'a> { x: &'a u32 }
|
||||
/// ```
|
||||
///
|
||||
/// ```text
|
||||
/// fn foo(mut x: Vec<Ref>, y: Ref) {
|
||||
/// --- --- these structs are declared with different lifetimes...
|
||||
/// x.push(y);
|
||||
/// ^ ...but data from `y` flows into `x` here
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// It will later be extended to trait objects.
|
||||
pub(super) fn try_report_anon_anon_conflict(&self) -> Option<ErrorGuaranteed> {
|
||||
let (span, sub, sup) = self.regions()?;
|
||||
|
||||
if let Some(RegionResolutionError::ConcreteFailure(
|
||||
SubregionOrigin::ReferenceOutlivesReferent(..),
|
||||
..,
|
||||
)) = self.error
|
||||
{
|
||||
// This error doesn't make much sense in this case.
|
||||
return None;
|
||||
}
|
||||
|
||||
// Determine whether the sub and sup consist of both anonymous (elided) regions.
|
||||
let anon_reg_sup = self.tcx().is_suitable_region(self.generic_param_scope, sup)?;
|
||||
|
||||
let anon_reg_sub = self.tcx().is_suitable_region(self.generic_param_scope, sub)?;
|
||||
let scope_def_id_sup = anon_reg_sup.def_id;
|
||||
let bregion_sup = anon_reg_sup.bound_region;
|
||||
let scope_def_id_sub = anon_reg_sub.def_id;
|
||||
let bregion_sub = anon_reg_sub.bound_region;
|
||||
|
||||
let ty_sup = find_anon_type(self.tcx(), self.generic_param_scope, sup, &bregion_sup)?;
|
||||
|
||||
let ty_sub = find_anon_type(self.tcx(), self.generic_param_scope, sub, &bregion_sub)?;
|
||||
|
||||
debug!(
|
||||
"try_report_anon_anon_conflict: found_param1={:?} sup={:?} br1={:?}",
|
||||
ty_sub, sup, bregion_sup
|
||||
);
|
||||
debug!(
|
||||
"try_report_anon_anon_conflict: found_param2={:?} sub={:?} br2={:?}",
|
||||
ty_sup, sub, bregion_sub
|
||||
);
|
||||
|
||||
let (ty_sup, ty_fndecl_sup) = ty_sup;
|
||||
let (ty_sub, ty_fndecl_sub) = ty_sub;
|
||||
|
||||
let AnonymousParamInfo { param: anon_param_sup, .. } =
|
||||
self.find_param_with_region(sup, sup)?;
|
||||
let AnonymousParamInfo { param: anon_param_sub, .. } =
|
||||
self.find_param_with_region(sub, sub)?;
|
||||
|
||||
let sup_is_ret_type =
|
||||
self.is_return_type_anon(scope_def_id_sup, bregion_sup, ty_fndecl_sup);
|
||||
let sub_is_ret_type =
|
||||
self.is_return_type_anon(scope_def_id_sub, bregion_sub, ty_fndecl_sub);
|
||||
|
||||
debug!(
|
||||
"try_report_anon_anon_conflict: sub_is_ret_type={:?} sup_is_ret_type={:?}",
|
||||
sub_is_ret_type, sup_is_ret_type
|
||||
);
|
||||
|
||||
let labels = match (sup_is_ret_type, sub_is_ret_type) {
|
||||
(ret_capture @ Some(ret_span), _) | (_, ret_capture @ Some(ret_span)) => {
|
||||
let param_span =
|
||||
if sup_is_ret_type == ret_capture { ty_sub.span } else { ty_sup.span };
|
||||
LifetimeMismatchLabels::InRet {
|
||||
param_span,
|
||||
ret_span,
|
||||
span,
|
||||
label_var1: anon_param_sup.pat.simple_ident(),
|
||||
}
|
||||
}
|
||||
|
||||
(None, None) => LifetimeMismatchLabels::Normal {
|
||||
hir_equal: ty_sup.hir_id == ty_sub.hir_id,
|
||||
ty_sup: ty_sup.span,
|
||||
ty_sub: ty_sub.span,
|
||||
span,
|
||||
sup: anon_param_sup.pat.simple_ident(),
|
||||
sub: anon_param_sub.pat.simple_ident(),
|
||||
},
|
||||
};
|
||||
|
||||
let suggestion = AddLifetimeParamsSuggestion {
|
||||
tcx: self.tcx(),
|
||||
sub,
|
||||
ty_sup,
|
||||
ty_sub,
|
||||
add_note: true,
|
||||
generic_param_scope: self.generic_param_scope,
|
||||
};
|
||||
let err = LifetimeMismatch { span, labels, suggestion };
|
||||
let reported = self.tcx().dcx().emit_err(err);
|
||||
Some(reported)
|
||||
}
|
||||
}
|
||||
|
||||
/// Currently only used in rustc_borrowck, probably should be
|
||||
/// removed in favour of public_errors::AddLifetimeParamsSuggestion
|
||||
pub fn suggest_adding_lifetime_params<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
err: &mut Diag<'_>,
|
||||
generic_param_scope: LocalDefId,
|
||||
sub: Region<'tcx>,
|
||||
ty_sup: &'tcx Ty<'_>,
|
||||
ty_sub: &'tcx Ty<'_>,
|
||||
) {
|
||||
let suggestion = AddLifetimeParamsSuggestion {
|
||||
tcx,
|
||||
sub,
|
||||
ty_sup,
|
||||
ty_sub,
|
||||
add_note: false,
|
||||
generic_param_scope,
|
||||
};
|
||||
suggestion.add_to_diag(err);
|
||||
}
|
|
@ -1,236 +0,0 @@
|
|||
use core::ops::ControlFlow;
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::def_id::LocalDefId;
|
||||
use rustc_hir::intravisit::{self, Visitor};
|
||||
use rustc_middle::hir::map::Map;
|
||||
use rustc_middle::hir::nested_filter;
|
||||
use rustc_middle::middle::resolve_bound_vars as rbv;
|
||||
use rustc_middle::ty::{self, Region, TyCtxt};
|
||||
|
||||
/// This function calls the `visit_ty` method for the parameters
|
||||
/// corresponding to the anonymous regions. The `nested_visitor.found_type`
|
||||
/// contains the anonymous type.
|
||||
///
|
||||
/// # Arguments
|
||||
/// region - the anonymous region corresponding to the anon_anon conflict
|
||||
/// br - the bound region corresponding to the above region which is of type `BrAnon(_)`
|
||||
///
|
||||
/// # Example
|
||||
/// ```compile_fail
|
||||
/// fn foo(x: &mut Vec<&u8>, y: &u8)
|
||||
/// { x.push(y); }
|
||||
/// ```
|
||||
/// The function returns the nested type corresponding to the anonymous region
|
||||
/// for e.g., `&u8` and `Vec<&u8>`.
|
||||
pub fn find_anon_type<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
generic_param_scope: LocalDefId,
|
||||
region: Region<'tcx>,
|
||||
br: &ty::BoundRegionKind,
|
||||
) -> Option<(&'tcx hir::Ty<'tcx>, &'tcx hir::FnSig<'tcx>)> {
|
||||
let anon_reg = tcx.is_suitable_region(generic_param_scope, region)?;
|
||||
let fn_sig = tcx.hir_node_by_def_id(anon_reg.def_id).fn_sig()?;
|
||||
|
||||
fn_sig
|
||||
.decl
|
||||
.inputs
|
||||
.iter()
|
||||
.find_map(|arg| find_component_for_bound_region(tcx, arg, br))
|
||||
.map(|ty| (ty, fn_sig))
|
||||
}
|
||||
|
||||
// This method creates a FindNestedTypeVisitor which returns the type corresponding
|
||||
// to the anonymous region.
|
||||
fn find_component_for_bound_region<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
arg: &'tcx hir::Ty<'tcx>,
|
||||
br: &ty::BoundRegionKind,
|
||||
) -> Option<&'tcx hir::Ty<'tcx>> {
|
||||
FindNestedTypeVisitor { tcx, bound_region: *br, current_index: ty::INNERMOST }
|
||||
.visit_ty(arg)
|
||||
.break_value()
|
||||
}
|
||||
|
||||
// The FindNestedTypeVisitor captures the corresponding `hir::Ty` of the
|
||||
// anonymous region. The example above would lead to a conflict between
|
||||
// the two anonymous lifetimes for &u8 in x and y respectively. This visitor
|
||||
// would be invoked twice, once for each lifetime, and would
|
||||
// walk the types like &mut Vec<&u8> and &u8 looking for the HIR
|
||||
// where that lifetime appears. This allows us to highlight the
|
||||
// specific part of the type in the error message.
|
||||
struct FindNestedTypeVisitor<'tcx> {
|
||||
tcx: TyCtxt<'tcx>,
|
||||
// The bound_region corresponding to the Refree(freeregion)
|
||||
// associated with the anonymous region we are looking for.
|
||||
bound_region: ty::BoundRegionKind,
|
||||
current_index: ty::DebruijnIndex,
|
||||
}
|
||||
|
||||
impl<'tcx> Visitor<'tcx> for FindNestedTypeVisitor<'tcx> {
|
||||
type Result = ControlFlow<&'tcx hir::Ty<'tcx>>;
|
||||
type NestedFilter = nested_filter::OnlyBodies;
|
||||
|
||||
fn nested_visit_map(&mut self) -> Self::Map {
|
||||
self.tcx.hir()
|
||||
}
|
||||
|
||||
fn visit_ty(&mut self, arg: &'tcx hir::Ty<'tcx>) -> Self::Result {
|
||||
match arg.kind {
|
||||
hir::TyKind::BareFn(_) => {
|
||||
self.current_index.shift_in(1);
|
||||
intravisit::walk_ty(self, arg);
|
||||
self.current_index.shift_out(1);
|
||||
return ControlFlow::Continue(());
|
||||
}
|
||||
|
||||
hir::TyKind::TraitObject(bounds, ..) => {
|
||||
for bound in bounds {
|
||||
self.current_index.shift_in(1);
|
||||
self.visit_poly_trait_ref(bound);
|
||||
self.current_index.shift_out(1);
|
||||
}
|
||||
}
|
||||
|
||||
hir::TyKind::Ref(lifetime, _) => {
|
||||
// the lifetime of the Ref
|
||||
let hir_id = lifetime.hir_id;
|
||||
match (self.tcx.named_bound_var(hir_id), self.bound_region) {
|
||||
// Find the index of the named region that was part of the
|
||||
// error. We will then search the function parameters for a bound
|
||||
// region at the right depth with the same index
|
||||
(Some(rbv::ResolvedArg::EarlyBound(id)), ty::BrNamed(def_id, _)) => {
|
||||
debug!("EarlyBound id={:?} def_id={:?}", id, def_id);
|
||||
if id == def_id {
|
||||
return ControlFlow::Break(arg);
|
||||
}
|
||||
}
|
||||
|
||||
// Find the index of the named region that was part of the
|
||||
// error. We will then search the function parameters for a bound
|
||||
// region at the right depth with the same index
|
||||
(
|
||||
Some(rbv::ResolvedArg::LateBound(debruijn_index, _, id)),
|
||||
ty::BrNamed(def_id, _),
|
||||
) => {
|
||||
debug!(
|
||||
"FindNestedTypeVisitor::visit_ty: LateBound depth = {:?}",
|
||||
debruijn_index
|
||||
);
|
||||
debug!("LateBound id={:?} def_id={:?}", id, def_id);
|
||||
if debruijn_index == self.current_index && id == def_id {
|
||||
return ControlFlow::Break(arg);
|
||||
}
|
||||
}
|
||||
|
||||
(
|
||||
Some(
|
||||
rbv::ResolvedArg::StaticLifetime
|
||||
| rbv::ResolvedArg::Free(_, _)
|
||||
| rbv::ResolvedArg::EarlyBound(_)
|
||||
| rbv::ResolvedArg::LateBound(_, _, _)
|
||||
| rbv::ResolvedArg::Error(_),
|
||||
)
|
||||
| None,
|
||||
_,
|
||||
) => {
|
||||
debug!("no arg found");
|
||||
}
|
||||
}
|
||||
}
|
||||
// Checks if it is of type `hir::TyKind::Path` which corresponds to a struct.
|
||||
hir::TyKind::Path(_) => {
|
||||
// Prefer using the lifetime in type arguments rather than lifetime arguments.
|
||||
intravisit::walk_ty(self, arg)?;
|
||||
|
||||
// Call `walk_ty` as `visit_ty` is empty.
|
||||
return if intravisit::walk_ty(
|
||||
&mut TyPathVisitor {
|
||||
tcx: self.tcx,
|
||||
bound_region: self.bound_region,
|
||||
current_index: self.current_index,
|
||||
},
|
||||
arg,
|
||||
)
|
||||
.is_break()
|
||||
{
|
||||
ControlFlow::Break(arg)
|
||||
} else {
|
||||
ControlFlow::Continue(())
|
||||
};
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
// walk the embedded contents: e.g., if we are visiting `Vec<&Foo>`,
|
||||
// go on to visit `&Foo`
|
||||
intravisit::walk_ty(self, arg)
|
||||
}
|
||||
}
|
||||
|
||||
// The visitor captures the corresponding `hir::Ty` of the anonymous region
|
||||
// in the case of structs ie. `hir::TyKind::Path`.
|
||||
// This visitor would be invoked for each lifetime corresponding to a struct,
|
||||
// and would walk the types like Vec<Ref> in the above example and Ref looking for the HIR
|
||||
// where that lifetime appears. This allows us to highlight the
|
||||
// specific part of the type in the error message.
|
||||
struct TyPathVisitor<'tcx> {
|
||||
tcx: TyCtxt<'tcx>,
|
||||
bound_region: ty::BoundRegionKind,
|
||||
current_index: ty::DebruijnIndex,
|
||||
}
|
||||
|
||||
impl<'tcx> Visitor<'tcx> for TyPathVisitor<'tcx> {
|
||||
type Result = ControlFlow<()>;
|
||||
type NestedFilter = nested_filter::OnlyBodies;
|
||||
|
||||
fn nested_visit_map(&mut self) -> Map<'tcx> {
|
||||
self.tcx.hir()
|
||||
}
|
||||
|
||||
fn visit_lifetime(&mut self, lifetime: &hir::Lifetime) -> Self::Result {
|
||||
match (self.tcx.named_bound_var(lifetime.hir_id), self.bound_region) {
|
||||
// the lifetime of the TyPath!
|
||||
(Some(rbv::ResolvedArg::EarlyBound(id)), ty::BrNamed(def_id, _)) => {
|
||||
debug!("EarlyBound id={:?} def_id={:?}", id, def_id);
|
||||
if id == def_id {
|
||||
return ControlFlow::Break(());
|
||||
}
|
||||
}
|
||||
|
||||
(Some(rbv::ResolvedArg::LateBound(debruijn_index, _, id)), ty::BrNamed(def_id, _)) => {
|
||||
debug!("FindNestedTypeVisitor::visit_ty: LateBound depth = {:?}", debruijn_index,);
|
||||
debug!("id={:?}", id);
|
||||
debug!("def_id={:?}", def_id);
|
||||
if debruijn_index == self.current_index && id == def_id {
|
||||
return ControlFlow::Break(());
|
||||
}
|
||||
}
|
||||
|
||||
(
|
||||
Some(
|
||||
rbv::ResolvedArg::StaticLifetime
|
||||
| rbv::ResolvedArg::EarlyBound(_)
|
||||
| rbv::ResolvedArg::LateBound(_, _, _)
|
||||
| rbv::ResolvedArg::Free(_, _)
|
||||
| rbv::ResolvedArg::Error(_),
|
||||
)
|
||||
| None,
|
||||
_,
|
||||
) => {
|
||||
debug!("no arg found");
|
||||
}
|
||||
}
|
||||
ControlFlow::Continue(())
|
||||
}
|
||||
|
||||
fn visit_ty(&mut self, arg: &'tcx hir::Ty<'tcx>) -> Self::Result {
|
||||
// ignore nested types
|
||||
//
|
||||
// If you have a type like `Foo<'a, &Ty>` we
|
||||
// are only interested in the immediate lifetimes ('a).
|
||||
//
|
||||
// Making `visit_ty` empty will ignore the `&Ty` embedded
|
||||
// inside, it will get reached by the outer visitor.
|
||||
debug!("`Ty` corresponding to a struct is {:?}", arg);
|
||||
ControlFlow::Continue(())
|
||||
}
|
||||
}
|
|
@ -1,127 +0,0 @@
|
|||
//! Error Reporting for when the lifetime for a type doesn't match the `impl` selected for a predicate
|
||||
//! to hold.
|
||||
|
||||
use crate::error_reporting::infer::nice_region_error::NiceRegionError;
|
||||
use crate::errors::{note_and_explain, IntroducesStaticBecauseUnmetLifetimeReq};
|
||||
use crate::errors::{
|
||||
DoesNotOutliveStaticFromImpl, ImplicitStaticLifetimeSubdiag, MismatchedStaticLifetime,
|
||||
};
|
||||
use crate::infer::RegionResolutionError;
|
||||
use crate::infer::{SubregionOrigin, TypeTrace};
|
||||
use crate::traits::ObligationCauseCode;
|
||||
use rustc_data_structures::fx::FxIndexSet;
|
||||
use rustc_errors::{ErrorGuaranteed, MultiSpan};
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::intravisit::Visitor;
|
||||
use rustc_middle::bug;
|
||||
use rustc_middle::ty::TypeVisitor;
|
||||
|
||||
impl<'a, 'tcx> NiceRegionError<'a, 'tcx> {
|
||||
pub(super) fn try_report_mismatched_static_lifetime(&self) -> Option<ErrorGuaranteed> {
|
||||
let error = self.error.as_ref()?;
|
||||
debug!("try_report_mismatched_static_lifetime {:?}", error);
|
||||
|
||||
let RegionResolutionError::ConcreteFailure(origin, sub, sup) = error.clone() else {
|
||||
return None;
|
||||
};
|
||||
if !sub.is_static() {
|
||||
return None;
|
||||
}
|
||||
let SubregionOrigin::Subtype(box TypeTrace { ref cause, .. }) = origin else {
|
||||
return None;
|
||||
};
|
||||
// If we added a "points at argument expression" obligation, we remove it here, we care
|
||||
// about the original obligation only.
|
||||
let code = match cause.code() {
|
||||
ObligationCauseCode::FunctionArg { parent_code, .. } => &*parent_code,
|
||||
code => code,
|
||||
};
|
||||
let ObligationCauseCode::MatchImpl(parent, impl_def_id) = code else {
|
||||
return None;
|
||||
};
|
||||
let (ObligationCauseCode::WhereClause(_, binding_span)
|
||||
| ObligationCauseCode::WhereClauseInExpr(_, binding_span, ..)) = *parent.code()
|
||||
else {
|
||||
return None;
|
||||
};
|
||||
if binding_span.is_dummy() {
|
||||
return None;
|
||||
}
|
||||
|
||||
// FIXME: we should point at the lifetime
|
||||
let multi_span: MultiSpan = vec![binding_span].into();
|
||||
let multispan_subdiag = IntroducesStaticBecauseUnmetLifetimeReq {
|
||||
unmet_requirements: multi_span,
|
||||
binding_span,
|
||||
};
|
||||
|
||||
let expl = note_and_explain::RegionExplanation::new(
|
||||
self.tcx(),
|
||||
self.generic_param_scope,
|
||||
sup,
|
||||
Some(binding_span),
|
||||
note_and_explain::PrefixKind::Empty,
|
||||
note_and_explain::SuffixKind::Continues,
|
||||
);
|
||||
let mut impl_span = None;
|
||||
let mut implicit_static_lifetimes = Vec::new();
|
||||
if let Some(impl_node) = self.tcx().hir().get_if_local(*impl_def_id) {
|
||||
// If an impl is local, then maybe this isn't what they want. Try to
|
||||
// be as helpful as possible with implicit lifetimes.
|
||||
|
||||
// First, let's get the hir self type of the impl
|
||||
let hir::Node::Item(hir::Item {
|
||||
kind: hir::ItemKind::Impl(hir::Impl { self_ty: impl_self_ty, .. }),
|
||||
..
|
||||
}) = impl_node
|
||||
else {
|
||||
bug!("Node not an impl.");
|
||||
};
|
||||
|
||||
// Next, let's figure out the set of trait objects with implicit static bounds
|
||||
let ty = self.tcx().type_of(*impl_def_id).instantiate_identity();
|
||||
let mut v = super::static_impl_trait::TraitObjectVisitor(FxIndexSet::default());
|
||||
v.visit_ty(ty);
|
||||
let mut traits = vec![];
|
||||
for matching_def_id in v.0 {
|
||||
let mut hir_v =
|
||||
super::static_impl_trait::HirTraitObjectVisitor(&mut traits, matching_def_id);
|
||||
hir_v.visit_ty(impl_self_ty);
|
||||
}
|
||||
|
||||
if traits.is_empty() {
|
||||
// If there are no trait object traits to point at, either because
|
||||
// there aren't trait objects or because none are implicit, then just
|
||||
// write a single note on the impl itself.
|
||||
|
||||
impl_span = Some(self.tcx().def_span(*impl_def_id));
|
||||
} else {
|
||||
// Otherwise, point at all implicit static lifetimes
|
||||
|
||||
for span in &traits {
|
||||
implicit_static_lifetimes
|
||||
.push(ImplicitStaticLifetimeSubdiag::Note { span: *span });
|
||||
// It would be nice to put this immediately under the above note, but they get
|
||||
// pushed to the end.
|
||||
implicit_static_lifetimes
|
||||
.push(ImplicitStaticLifetimeSubdiag::Sugg { span: span.shrink_to_hi() });
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Otherwise just point out the impl.
|
||||
|
||||
impl_span = Some(self.tcx().def_span(*impl_def_id));
|
||||
}
|
||||
let err = MismatchedStaticLifetime {
|
||||
cause_span: cause.span,
|
||||
unmet_lifetime_reqs: multispan_subdiag,
|
||||
expl,
|
||||
does_not_outlive_static_from_impl: impl_span
|
||||
.map(|span| DoesNotOutliveStaticFromImpl::Spanned { span })
|
||||
.unwrap_or(DoesNotOutliveStaticFromImpl::Unspanned),
|
||||
implicit_static_lifetimes,
|
||||
};
|
||||
let reported = self.tcx().dcx().emit_err(err);
|
||||
Some(reported)
|
||||
}
|
||||
}
|
|
@ -1,93 +0,0 @@
|
|||
use crate::error_reporting::infer::TypeErrCtxt;
|
||||
use crate::infer::RegionResolutionError;
|
||||
use crate::infer::RegionResolutionError::*;
|
||||
use rustc_errors::{Diag, ErrorGuaranteed};
|
||||
use rustc_hir::def_id::LocalDefId;
|
||||
use rustc_middle::ty::{self, TyCtxt};
|
||||
use rustc_span::Span;
|
||||
|
||||
mod different_lifetimes;
|
||||
pub mod find_anon_type;
|
||||
mod mismatched_static_lifetime;
|
||||
mod named_anon_conflict;
|
||||
pub(crate) mod placeholder_error;
|
||||
mod placeholder_relation;
|
||||
mod static_impl_trait;
|
||||
mod trait_impl_difference;
|
||||
mod util;
|
||||
|
||||
pub use different_lifetimes::suggest_adding_lifetime_params;
|
||||
pub use find_anon_type::find_anon_type;
|
||||
pub use static_impl_trait::{suggest_new_region_bound, HirTraitObjectVisitor, TraitObjectVisitor};
|
||||
pub use util::find_param_with_region;
|
||||
|
||||
impl<'cx, 'tcx> TypeErrCtxt<'cx, 'tcx> {
|
||||
pub fn try_report_nice_region_error(
|
||||
&'cx self,
|
||||
generic_param_scope: LocalDefId,
|
||||
error: &RegionResolutionError<'tcx>,
|
||||
) -> Option<ErrorGuaranteed> {
|
||||
NiceRegionError::new(self, generic_param_scope, error.clone()).try_report()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct NiceRegionError<'cx, 'tcx> {
|
||||
cx: &'cx TypeErrCtxt<'cx, 'tcx>,
|
||||
/// The innermost definition that introduces generic parameters that may be involved in
|
||||
/// the region errors we are dealing with.
|
||||
generic_param_scope: LocalDefId,
|
||||
error: Option<RegionResolutionError<'tcx>>,
|
||||
regions: Option<(Span, ty::Region<'tcx>, ty::Region<'tcx>)>,
|
||||
}
|
||||
|
||||
impl<'cx, 'tcx> NiceRegionError<'cx, 'tcx> {
|
||||
pub fn new(
|
||||
cx: &'cx TypeErrCtxt<'cx, 'tcx>,
|
||||
generic_param_scope: LocalDefId,
|
||||
error: RegionResolutionError<'tcx>,
|
||||
) -> Self {
|
||||
Self { cx, error: Some(error), regions: None, generic_param_scope }
|
||||
}
|
||||
|
||||
pub fn new_from_span(
|
||||
cx: &'cx TypeErrCtxt<'cx, 'tcx>,
|
||||
generic_param_scope: LocalDefId,
|
||||
span: Span,
|
||||
sub: ty::Region<'tcx>,
|
||||
sup: ty::Region<'tcx>,
|
||||
) -> Self {
|
||||
Self { cx, error: None, regions: Some((span, sub, sup)), generic_param_scope }
|
||||
}
|
||||
|
||||
fn tcx(&self) -> TyCtxt<'tcx> {
|
||||
self.cx.tcx
|
||||
}
|
||||
|
||||
pub fn try_report_from_nll(&self) -> Option<Diag<'tcx>> {
|
||||
// Due to the improved diagnostics returned by the MIR borrow checker, only a subset of
|
||||
// the nice region errors are required when running under the MIR borrow checker.
|
||||
self.try_report_named_anon_conflict()
|
||||
.or_else(|| self.try_report_placeholder_conflict())
|
||||
.or_else(|| self.try_report_placeholder_relation())
|
||||
}
|
||||
|
||||
pub fn try_report(&self) -> Option<ErrorGuaranteed> {
|
||||
self.try_report_from_nll()
|
||||
.map(|diag| diag.emit())
|
||||
.or_else(|| self.try_report_impl_not_conforming_to_trait())
|
||||
.or_else(|| self.try_report_anon_anon_conflict())
|
||||
.or_else(|| self.try_report_static_impl_trait())
|
||||
.or_else(|| self.try_report_mismatched_static_lifetime())
|
||||
}
|
||||
|
||||
pub(super) fn regions(&self) -> Option<(Span, ty::Region<'tcx>, ty::Region<'tcx>)> {
|
||||
match (&self.error, self.regions) {
|
||||
(Some(ConcreteFailure(origin, sub, sup)), None) => Some((origin.span(), *sub, *sup)),
|
||||
(Some(SubSupConflict(_, _, origin, sub, _, sup, _)), None) => {
|
||||
Some((origin.span(), *sub, *sup))
|
||||
}
|
||||
(None, Some((span, sub, sup))) => Some((span, sub, sup)),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,92 +0,0 @@
|
|||
//! Error Reporting for Anonymous Region Lifetime Errors
|
||||
//! where one region is named and the other is anonymous.
|
||||
|
||||
use crate::error_reporting::infer::nice_region_error::find_anon_type::find_anon_type;
|
||||
use crate::error_reporting::infer::nice_region_error::NiceRegionError;
|
||||
use crate::errors::ExplicitLifetimeRequired;
|
||||
use rustc_errors::Diag;
|
||||
use rustc_middle::ty;
|
||||
use rustc_span::symbol::kw;
|
||||
|
||||
impl<'a, 'tcx> NiceRegionError<'a, 'tcx> {
|
||||
/// When given a `ConcreteFailure` for a function with parameters containing a named region and
|
||||
/// an anonymous region, emit an descriptive diagnostic error.
|
||||
pub(super) fn try_report_named_anon_conflict(&self) -> Option<Diag<'tcx>> {
|
||||
let (span, sub, sup) = self.regions()?;
|
||||
|
||||
debug!(
|
||||
"try_report_named_anon_conflict(sub={:?}, sup={:?}, error={:?})",
|
||||
sub, sup, self.error,
|
||||
);
|
||||
|
||||
// Determine whether the sub and sup consist of one named region ('a)
|
||||
// and one anonymous (elided) region. If so, find the parameter arg
|
||||
// where the anonymous region appears (there must always be one; we
|
||||
// only introduced anonymous regions in parameters) as well as a
|
||||
// version new_ty of its type where the anonymous region is replaced
|
||||
// with the named one.
|
||||
let (named, anon, anon_param_info, region_info) = if sub.has_name()
|
||||
&& let Some(region_info) = self.tcx().is_suitable_region(self.generic_param_scope, sup)
|
||||
&& let Some(anon_param_info) = self.find_param_with_region(sup, sub)
|
||||
{
|
||||
(sub, sup, anon_param_info, region_info)
|
||||
} else if sup.has_name()
|
||||
&& let Some(region_info) = self.tcx().is_suitable_region(self.generic_param_scope, sub)
|
||||
&& let Some(anon_param_info) = self.find_param_with_region(sub, sup)
|
||||
{
|
||||
(sup, sub, anon_param_info, region_info)
|
||||
} else {
|
||||
return None; // inapplicable
|
||||
};
|
||||
|
||||
// Suggesting to add a `'static` lifetime to a parameter is nearly always incorrect,
|
||||
// and can steer users down the wrong path.
|
||||
if named.is_static() {
|
||||
return None;
|
||||
}
|
||||
|
||||
debug!("try_report_named_anon_conflict: named = {:?}", named);
|
||||
debug!("try_report_named_anon_conflict: anon_param_info = {:?}", anon_param_info);
|
||||
debug!("try_report_named_anon_conflict: region_info = {:?}", region_info);
|
||||
|
||||
let param = anon_param_info.param;
|
||||
let new_ty = anon_param_info.param_ty;
|
||||
let new_ty_span = anon_param_info.param_ty_span;
|
||||
let br = anon_param_info.bound_region;
|
||||
let is_first = anon_param_info.is_first;
|
||||
let scope_def_id = region_info.def_id;
|
||||
let is_impl_item = region_info.is_impl_item;
|
||||
|
||||
match br {
|
||||
ty::BrNamed(_, kw::UnderscoreLifetime) | ty::BrAnon => {}
|
||||
_ => {
|
||||
/* not an anonymous region */
|
||||
debug!("try_report_named_anon_conflict: not an anonymous region");
|
||||
return None;
|
||||
}
|
||||
}
|
||||
|
||||
if is_impl_item {
|
||||
debug!("try_report_named_anon_conflict: impl item, bail out");
|
||||
return None;
|
||||
}
|
||||
|
||||
if find_anon_type(self.tcx(), self.generic_param_scope, anon, &br).is_some()
|
||||
&& self.is_self_anon(is_first, scope_def_id)
|
||||
{
|
||||
return None;
|
||||
}
|
||||
let named = named.to_string();
|
||||
let err = match param.pat.simple_ident() {
|
||||
Some(simple_ident) => ExplicitLifetimeRequired::WithIdent {
|
||||
span,
|
||||
simple_ident,
|
||||
named,
|
||||
new_ty_span,
|
||||
new_ty,
|
||||
},
|
||||
None => ExplicitLifetimeRequired::WithParamType { span, named, new_ty_span, new_ty },
|
||||
};
|
||||
Some(self.tcx().sess.dcx().create_err(err))
|
||||
}
|
||||
}
|
|
@ -1,496 +0,0 @@
|
|||
use crate::error_reporting::infer::nice_region_error::NiceRegionError;
|
||||
use crate::errors::{
|
||||
ActualImplExpectedKind, ActualImplExpectedLifetimeKind, ActualImplExplNotes,
|
||||
TraitPlaceholderMismatch, TyOrSig,
|
||||
};
|
||||
use crate::infer::RegionResolutionError;
|
||||
use crate::infer::ValuePairs;
|
||||
use crate::infer::{SubregionOrigin, TypeTrace};
|
||||
use crate::traits::{ObligationCause, ObligationCauseCode};
|
||||
use rustc_data_structures::intern::Interned;
|
||||
use rustc_errors::{Diag, IntoDiagArg};
|
||||
use rustc_hir::def::Namespace;
|
||||
use rustc_hir::def_id::{DefId, CRATE_DEF_ID};
|
||||
use rustc_middle::bug;
|
||||
use rustc_middle::ty::error::ExpectedFound;
|
||||
use rustc_middle::ty::print::{FmtPrinter, Print, PrintTraitRefExt as _, RegionHighlightMode};
|
||||
use rustc_middle::ty::GenericArgsRef;
|
||||
use rustc_middle::ty::{self, RePlaceholder, Region, TyCtxt};
|
||||
|
||||
use std::fmt;
|
||||
|
||||
// HACK(eddyb) maybe move this in a more central location.
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct Highlighted<'tcx, T> {
|
||||
tcx: TyCtxt<'tcx>,
|
||||
highlight: RegionHighlightMode<'tcx>,
|
||||
value: T,
|
||||
}
|
||||
|
||||
impl<'tcx, T> IntoDiagArg for Highlighted<'tcx, T>
|
||||
where
|
||||
T: for<'a> Print<'tcx, FmtPrinter<'a, 'tcx>>,
|
||||
{
|
||||
fn into_diag_arg(self) -> rustc_errors::DiagArgValue {
|
||||
rustc_errors::DiagArgValue::Str(self.to_string().into())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx, T> Highlighted<'tcx, T> {
|
||||
fn map<U>(self, f: impl FnOnce(T) -> U) -> Highlighted<'tcx, U> {
|
||||
Highlighted { tcx: self.tcx, highlight: self.highlight, value: f(self.value) }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx, T> fmt::Display for Highlighted<'tcx, T>
|
||||
where
|
||||
T: for<'a> Print<'tcx, FmtPrinter<'a, 'tcx>>,
|
||||
{
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
let mut printer = ty::print::FmtPrinter::new(self.tcx, Namespace::TypeNS);
|
||||
printer.region_highlight_mode = self.highlight;
|
||||
|
||||
self.value.print(&mut printer)?;
|
||||
f.write_str(&printer.into_buffer())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> NiceRegionError<'_, 'tcx> {
|
||||
/// When given a `ConcreteFailure` for a function with arguments containing a named region and
|
||||
/// an anonymous region, emit a descriptive diagnostic error.
|
||||
pub(super) fn try_report_placeholder_conflict(&self) -> Option<Diag<'tcx>> {
|
||||
match &self.error {
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// NB. The ordering of cases in this match is very
|
||||
// sensitive, because we are often matching against
|
||||
// specific cases and then using an `_` to match all
|
||||
// others.
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// Check for errors from comparing trait failures -- first
|
||||
// with two placeholders, then with one.
|
||||
Some(RegionResolutionError::SubSupConflict(
|
||||
vid,
|
||||
_,
|
||||
SubregionOrigin::Subtype(box TypeTrace { cause, values }),
|
||||
sub_placeholder @ Region(Interned(RePlaceholder(_), _)),
|
||||
_,
|
||||
sup_placeholder @ Region(Interned(RePlaceholder(_), _)),
|
||||
_,
|
||||
)) => self.try_report_trait_placeholder_mismatch(
|
||||
Some(ty::Region::new_var(self.tcx(), *vid)),
|
||||
cause,
|
||||
Some(*sub_placeholder),
|
||||
Some(*sup_placeholder),
|
||||
values,
|
||||
),
|
||||
|
||||
Some(RegionResolutionError::SubSupConflict(
|
||||
vid,
|
||||
_,
|
||||
SubregionOrigin::Subtype(box TypeTrace { cause, values }),
|
||||
sub_placeholder @ Region(Interned(RePlaceholder(_), _)),
|
||||
_,
|
||||
_,
|
||||
_,
|
||||
)) => self.try_report_trait_placeholder_mismatch(
|
||||
Some(ty::Region::new_var(self.tcx(), *vid)),
|
||||
cause,
|
||||
Some(*sub_placeholder),
|
||||
None,
|
||||
values,
|
||||
),
|
||||
|
||||
Some(RegionResolutionError::SubSupConflict(
|
||||
vid,
|
||||
_,
|
||||
SubregionOrigin::Subtype(box TypeTrace { cause, values }),
|
||||
_,
|
||||
_,
|
||||
sup_placeholder @ Region(Interned(RePlaceholder(_), _)),
|
||||
_,
|
||||
)) => self.try_report_trait_placeholder_mismatch(
|
||||
Some(ty::Region::new_var(self.tcx(), *vid)),
|
||||
cause,
|
||||
None,
|
||||
Some(*sup_placeholder),
|
||||
values,
|
||||
),
|
||||
|
||||
Some(RegionResolutionError::SubSupConflict(
|
||||
vid,
|
||||
_,
|
||||
_,
|
||||
_,
|
||||
SubregionOrigin::Subtype(box TypeTrace { cause, values }),
|
||||
sup_placeholder @ Region(Interned(RePlaceholder(_), _)),
|
||||
_,
|
||||
)) => self.try_report_trait_placeholder_mismatch(
|
||||
Some(ty::Region::new_var(self.tcx(), *vid)),
|
||||
cause,
|
||||
None,
|
||||
Some(*sup_placeholder),
|
||||
values,
|
||||
),
|
||||
|
||||
Some(RegionResolutionError::UpperBoundUniverseConflict(
|
||||
vid,
|
||||
_,
|
||||
_,
|
||||
SubregionOrigin::Subtype(box TypeTrace { cause, values }),
|
||||
sup_placeholder @ Region(Interned(RePlaceholder(_), _)),
|
||||
)) => self.try_report_trait_placeholder_mismatch(
|
||||
Some(ty::Region::new_var(self.tcx(), *vid)),
|
||||
cause,
|
||||
None,
|
||||
Some(*sup_placeholder),
|
||||
values,
|
||||
),
|
||||
|
||||
Some(RegionResolutionError::ConcreteFailure(
|
||||
SubregionOrigin::Subtype(box TypeTrace { cause, values }),
|
||||
sub_region @ Region(Interned(RePlaceholder(_), _)),
|
||||
sup_region @ Region(Interned(RePlaceholder(_), _)),
|
||||
)) => self.try_report_trait_placeholder_mismatch(
|
||||
None,
|
||||
cause,
|
||||
Some(*sub_region),
|
||||
Some(*sup_region),
|
||||
values,
|
||||
),
|
||||
|
||||
Some(RegionResolutionError::ConcreteFailure(
|
||||
SubregionOrigin::Subtype(box TypeTrace { cause, values }),
|
||||
sub_region @ Region(Interned(RePlaceholder(_), _)),
|
||||
sup_region,
|
||||
)) => self.try_report_trait_placeholder_mismatch(
|
||||
(!sup_region.has_name()).then_some(*sup_region),
|
||||
cause,
|
||||
Some(*sub_region),
|
||||
None,
|
||||
values,
|
||||
),
|
||||
|
||||
Some(RegionResolutionError::ConcreteFailure(
|
||||
SubregionOrigin::Subtype(box TypeTrace { cause, values }),
|
||||
sub_region,
|
||||
sup_region @ Region(Interned(RePlaceholder(_), _)),
|
||||
)) => self.try_report_trait_placeholder_mismatch(
|
||||
(!sub_region.has_name()).then_some(*sub_region),
|
||||
cause,
|
||||
None,
|
||||
Some(*sup_region),
|
||||
values,
|
||||
),
|
||||
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn try_report_trait_placeholder_mismatch(
|
||||
&self,
|
||||
vid: Option<Region<'tcx>>,
|
||||
cause: &ObligationCause<'tcx>,
|
||||
sub_placeholder: Option<Region<'tcx>>,
|
||||
sup_placeholder: Option<Region<'tcx>>,
|
||||
value_pairs: &ValuePairs<'tcx>,
|
||||
) -> Option<Diag<'tcx>> {
|
||||
let (expected_args, found_args, trait_def_id) = match value_pairs {
|
||||
ValuePairs::TraitRefs(ExpectedFound { expected, found })
|
||||
if expected.def_id == found.def_id =>
|
||||
{
|
||||
// It's possible that the placeholders come from a binder
|
||||
// outside of this value pair. Use `no_bound_vars` as a
|
||||
// simple heuristic for that.
|
||||
(expected.args, found.args, expected.def_id)
|
||||
}
|
||||
_ => return None,
|
||||
};
|
||||
|
||||
Some(self.report_trait_placeholder_mismatch(
|
||||
vid,
|
||||
cause,
|
||||
sub_placeholder,
|
||||
sup_placeholder,
|
||||
trait_def_id,
|
||||
expected_args,
|
||||
found_args,
|
||||
))
|
||||
}
|
||||
|
||||
// error[E0308]: implementation of `Foo` does not apply to enough lifetimes
|
||||
// --> /home/nmatsakis/tmp/foo.rs:12:5
|
||||
// |
|
||||
// 12 | all::<&'static u32>();
|
||||
// | ^^^^^^^^^^^^^^^^^^^ lifetime mismatch
|
||||
// |
|
||||
// = note: Due to a where-clause on the function `all`,
|
||||
// = note: `T` must implement `...` for any two lifetimes `'1` and `'2`.
|
||||
// = note: However, the type `T` only implements `...` for some specific lifetime `'2`.
|
||||
#[instrument(level = "debug", skip(self))]
|
||||
fn report_trait_placeholder_mismatch(
|
||||
&self,
|
||||
vid: Option<Region<'tcx>>,
|
||||
cause: &ObligationCause<'tcx>,
|
||||
sub_placeholder: Option<Region<'tcx>>,
|
||||
sup_placeholder: Option<Region<'tcx>>,
|
||||
trait_def_id: DefId,
|
||||
expected_args: GenericArgsRef<'tcx>,
|
||||
actual_args: GenericArgsRef<'tcx>,
|
||||
) -> Diag<'tcx> {
|
||||
let span = cause.span();
|
||||
|
||||
let (leading_ellipsis, satisfy_span, where_span, dup_span, def_id) =
|
||||
if let ObligationCauseCode::WhereClause(def_id, span)
|
||||
| ObligationCauseCode::WhereClauseInExpr(def_id, span, ..) = *cause.code()
|
||||
&& def_id != CRATE_DEF_ID.to_def_id()
|
||||
{
|
||||
(
|
||||
true,
|
||||
Some(span),
|
||||
Some(self.tcx().def_span(def_id)),
|
||||
None,
|
||||
self.tcx().def_path_str(def_id),
|
||||
)
|
||||
} else {
|
||||
(false, None, None, Some(span), String::new())
|
||||
};
|
||||
|
||||
let expected_trait_ref = self.cx.resolve_vars_if_possible(ty::TraitRef::new_from_args(
|
||||
self.cx.tcx,
|
||||
trait_def_id,
|
||||
expected_args,
|
||||
));
|
||||
let actual_trait_ref = self.cx.resolve_vars_if_possible(ty::TraitRef::new_from_args(
|
||||
self.cx.tcx,
|
||||
trait_def_id,
|
||||
actual_args,
|
||||
));
|
||||
|
||||
// Search the expected and actual trait references to see (a)
|
||||
// whether the sub/sup placeholders appear in them (sometimes
|
||||
// you have a trait ref like `T: Foo<fn(&u8)>`, where the
|
||||
// placeholder was created as part of an inner type) and (b)
|
||||
// whether the inference variable appears. In each case,
|
||||
// assign a counter value in each case if so.
|
||||
let mut counter = 0;
|
||||
let mut has_sub = None;
|
||||
let mut has_sup = None;
|
||||
|
||||
let mut actual_has_vid = None;
|
||||
let mut expected_has_vid = None;
|
||||
|
||||
self.tcx().for_each_free_region(&expected_trait_ref, |r| {
|
||||
if Some(r) == sub_placeholder && has_sub.is_none() {
|
||||
has_sub = Some(counter);
|
||||
counter += 1;
|
||||
} else if Some(r) == sup_placeholder && has_sup.is_none() {
|
||||
has_sup = Some(counter);
|
||||
counter += 1;
|
||||
}
|
||||
|
||||
if Some(r) == vid && expected_has_vid.is_none() {
|
||||
expected_has_vid = Some(counter);
|
||||
counter += 1;
|
||||
}
|
||||
});
|
||||
|
||||
self.tcx().for_each_free_region(&actual_trait_ref, |r| {
|
||||
if Some(r) == vid && actual_has_vid.is_none() {
|
||||
actual_has_vid = Some(counter);
|
||||
counter += 1;
|
||||
}
|
||||
});
|
||||
|
||||
let actual_self_ty_has_vid =
|
||||
self.tcx().any_free_region_meets(&actual_trait_ref.self_ty(), |r| Some(r) == vid);
|
||||
|
||||
let expected_self_ty_has_vid =
|
||||
self.tcx().any_free_region_meets(&expected_trait_ref.self_ty(), |r| Some(r) == vid);
|
||||
|
||||
let any_self_ty_has_vid = actual_self_ty_has_vid || expected_self_ty_has_vid;
|
||||
|
||||
debug!(
|
||||
?actual_has_vid,
|
||||
?expected_has_vid,
|
||||
?has_sub,
|
||||
?has_sup,
|
||||
?actual_self_ty_has_vid,
|
||||
?expected_self_ty_has_vid,
|
||||
);
|
||||
|
||||
let actual_impl_expl_notes = self.explain_actual_impl_that_was_found(
|
||||
sub_placeholder,
|
||||
sup_placeholder,
|
||||
has_sub,
|
||||
has_sup,
|
||||
expected_trait_ref,
|
||||
actual_trait_ref,
|
||||
vid,
|
||||
expected_has_vid,
|
||||
actual_has_vid,
|
||||
any_self_ty_has_vid,
|
||||
leading_ellipsis,
|
||||
);
|
||||
|
||||
self.tcx().dcx().create_err(TraitPlaceholderMismatch {
|
||||
span,
|
||||
satisfy_span,
|
||||
where_span,
|
||||
dup_span,
|
||||
def_id,
|
||||
trait_def_id: self.tcx().def_path_str(trait_def_id),
|
||||
actual_impl_expl_notes,
|
||||
})
|
||||
}
|
||||
|
||||
/// Add notes with details about the expected and actual trait refs, with attention to cases
|
||||
/// when placeholder regions are involved: either the trait or the self type containing
|
||||
/// them needs to be mentioned the closest to the placeholders.
|
||||
/// This makes the error messages read better, however at the cost of some complexity
|
||||
/// due to the number of combinations we have to deal with.
|
||||
fn explain_actual_impl_that_was_found(
|
||||
&self,
|
||||
sub_placeholder: Option<Region<'tcx>>,
|
||||
sup_placeholder: Option<Region<'tcx>>,
|
||||
has_sub: Option<usize>,
|
||||
has_sup: Option<usize>,
|
||||
expected_trait_ref: ty::TraitRef<'tcx>,
|
||||
actual_trait_ref: ty::TraitRef<'tcx>,
|
||||
vid: Option<Region<'tcx>>,
|
||||
expected_has_vid: Option<usize>,
|
||||
actual_has_vid: Option<usize>,
|
||||
any_self_ty_has_vid: bool,
|
||||
leading_ellipsis: bool,
|
||||
) -> Vec<ActualImplExplNotes<'tcx>> {
|
||||
// The weird thing here with the `maybe_highlighting_region` calls and the
|
||||
// the match inside is meant to be like this:
|
||||
//
|
||||
// - The match checks whether the given things (placeholders, etc) appear
|
||||
// in the types are about to print
|
||||
// - Meanwhile, the `maybe_highlighting_region` calls set up
|
||||
// highlights so that, if they do appear, we will replace
|
||||
// them `'0` and whatever. (This replacement takes place
|
||||
// inside the closure given to `maybe_highlighting_region`.)
|
||||
//
|
||||
// There is some duplication between the calls -- i.e., the
|
||||
// `maybe_highlighting_region` checks if (e.g.) `has_sub` is
|
||||
// None, an then we check again inside the closure, but this
|
||||
// setup sort of minimized the number of calls and so form.
|
||||
|
||||
let highlight_trait_ref = |trait_ref| Highlighted {
|
||||
tcx: self.tcx(),
|
||||
highlight: RegionHighlightMode::default(),
|
||||
value: trait_ref,
|
||||
};
|
||||
|
||||
let same_self_type = actual_trait_ref.self_ty() == expected_trait_ref.self_ty();
|
||||
|
||||
let mut expected_trait_ref = highlight_trait_ref(expected_trait_ref);
|
||||
expected_trait_ref.highlight.maybe_highlighting_region(sub_placeholder, has_sub);
|
||||
expected_trait_ref.highlight.maybe_highlighting_region(sup_placeholder, has_sup);
|
||||
|
||||
let passive_voice = match (has_sub, has_sup) {
|
||||
(Some(_), _) | (_, Some(_)) => any_self_ty_has_vid,
|
||||
(None, None) => {
|
||||
expected_trait_ref.highlight.maybe_highlighting_region(vid, expected_has_vid);
|
||||
match expected_has_vid {
|
||||
Some(_) => true,
|
||||
None => any_self_ty_has_vid,
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let (kind, ty_or_sig, trait_path) = if same_self_type {
|
||||
let mut self_ty = expected_trait_ref.map(|tr| tr.self_ty());
|
||||
self_ty.highlight.maybe_highlighting_region(vid, actual_has_vid);
|
||||
|
||||
if self_ty.value.is_closure() && self.tcx().is_fn_trait(expected_trait_ref.value.def_id)
|
||||
{
|
||||
let closure_sig = self_ty.map(|closure| {
|
||||
if let ty::Closure(_, args) = closure.kind() {
|
||||
self.tcx()
|
||||
.signature_unclosure(args.as_closure().sig(), rustc_hir::Safety::Safe)
|
||||
} else {
|
||||
bug!("type is not longer closure");
|
||||
}
|
||||
});
|
||||
(
|
||||
ActualImplExpectedKind::Signature,
|
||||
TyOrSig::ClosureSig(closure_sig),
|
||||
expected_trait_ref.map(|tr| tr.print_only_trait_path()),
|
||||
)
|
||||
} else {
|
||||
(
|
||||
ActualImplExpectedKind::Other,
|
||||
TyOrSig::Ty(self_ty),
|
||||
expected_trait_ref.map(|tr| tr.print_only_trait_path()),
|
||||
)
|
||||
}
|
||||
} else if passive_voice {
|
||||
(
|
||||
ActualImplExpectedKind::Passive,
|
||||
TyOrSig::Ty(expected_trait_ref.map(|tr| tr.self_ty())),
|
||||
expected_trait_ref.map(|tr| tr.print_only_trait_path()),
|
||||
)
|
||||
} else {
|
||||
(
|
||||
ActualImplExpectedKind::Other,
|
||||
TyOrSig::Ty(expected_trait_ref.map(|tr| tr.self_ty())),
|
||||
expected_trait_ref.map(|tr| tr.print_only_trait_path()),
|
||||
)
|
||||
};
|
||||
|
||||
let (lt_kind, lifetime_1, lifetime_2) = match (has_sub, has_sup) {
|
||||
(Some(n1), Some(n2)) => {
|
||||
(ActualImplExpectedLifetimeKind::Two, std::cmp::min(n1, n2), std::cmp::max(n1, n2))
|
||||
}
|
||||
(Some(n), _) | (_, Some(n)) => (ActualImplExpectedLifetimeKind::Any, n, 0),
|
||||
(None, None) => {
|
||||
if let Some(n) = expected_has_vid {
|
||||
(ActualImplExpectedLifetimeKind::Some, n, 0)
|
||||
} else {
|
||||
(ActualImplExpectedLifetimeKind::Nothing, 0, 0)
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let note_1 = ActualImplExplNotes::new_expected(
|
||||
kind,
|
||||
lt_kind,
|
||||
leading_ellipsis,
|
||||
ty_or_sig,
|
||||
trait_path,
|
||||
lifetime_1,
|
||||
lifetime_2,
|
||||
);
|
||||
|
||||
let mut actual_trait_ref = highlight_trait_ref(actual_trait_ref);
|
||||
actual_trait_ref.highlight.maybe_highlighting_region(vid, actual_has_vid);
|
||||
|
||||
let passive_voice = match actual_has_vid {
|
||||
Some(_) => any_self_ty_has_vid,
|
||||
None => true,
|
||||
};
|
||||
|
||||
let trait_path = actual_trait_ref.map(|tr| tr.print_only_trait_path());
|
||||
let ty = actual_trait_ref.map(|tr| tr.self_ty()).to_string();
|
||||
let has_lifetime = actual_has_vid.is_some();
|
||||
let lifetime = actual_has_vid.unwrap_or_default();
|
||||
|
||||
let note_2 = if same_self_type {
|
||||
ActualImplExplNotes::ButActuallyImplementsTrait { trait_path, has_lifetime, lifetime }
|
||||
} else if passive_voice {
|
||||
ActualImplExplNotes::ButActuallyImplementedForTy {
|
||||
trait_path,
|
||||
ty,
|
||||
has_lifetime,
|
||||
lifetime,
|
||||
}
|
||||
} else {
|
||||
ActualImplExplNotes::ButActuallyTyImplements { trait_path, ty, has_lifetime, lifetime }
|
||||
};
|
||||
|
||||
vec![note_1, note_2]
|
||||
}
|
||||
}
|
|
@ -1,86 +0,0 @@
|
|||
use crate::error_reporting::infer::nice_region_error::NiceRegionError;
|
||||
use crate::errors::PlaceholderRelationLfNotSatisfied;
|
||||
use crate::infer::{RegionResolutionError, SubregionOrigin};
|
||||
use rustc_data_structures::intern::Interned;
|
||||
use rustc_errors::Diag;
|
||||
use rustc_middle::ty::{self, RePlaceholder, Region};
|
||||
|
||||
impl<'tcx> NiceRegionError<'_, 'tcx> {
|
||||
/// Emitted wwhen given a `ConcreteFailure` when relating two placeholders.
|
||||
pub(super) fn try_report_placeholder_relation(&self) -> Option<Diag<'tcx>> {
|
||||
match &self.error {
|
||||
Some(RegionResolutionError::ConcreteFailure(
|
||||
SubregionOrigin::RelateRegionParamBound(span),
|
||||
Region(Interned(
|
||||
RePlaceholder(ty::Placeholder {
|
||||
bound: ty::BoundRegion { kind: sub_name, .. },
|
||||
..
|
||||
}),
|
||||
_,
|
||||
)),
|
||||
Region(Interned(
|
||||
RePlaceholder(ty::Placeholder {
|
||||
bound: ty::BoundRegion { kind: sup_name, .. },
|
||||
..
|
||||
}),
|
||||
_,
|
||||
)),
|
||||
)) => {
|
||||
let span = *span;
|
||||
let (sub_span, sub_symbol) = match sub_name {
|
||||
ty::BrNamed(def_id, symbol) => {
|
||||
(Some(self.tcx().def_span(def_id)), Some(symbol))
|
||||
}
|
||||
ty::BrAnon | ty::BrEnv => (None, None),
|
||||
};
|
||||
let (sup_span, sup_symbol) = match sup_name {
|
||||
ty::BrNamed(def_id, symbol) => {
|
||||
(Some(self.tcx().def_span(def_id)), Some(symbol))
|
||||
}
|
||||
ty::BrAnon | ty::BrEnv => (None, None),
|
||||
};
|
||||
let diag = match (sub_span, sup_span, sub_symbol, sup_symbol) {
|
||||
(Some(sub_span), Some(sup_span), Some(&sub_symbol), Some(&sup_symbol)) => {
|
||||
PlaceholderRelationLfNotSatisfied::HasBoth {
|
||||
span,
|
||||
sub_span,
|
||||
sup_span,
|
||||
sub_symbol,
|
||||
sup_symbol,
|
||||
note: (),
|
||||
}
|
||||
}
|
||||
(Some(sub_span), Some(sup_span), _, Some(&sup_symbol)) => {
|
||||
PlaceholderRelationLfNotSatisfied::HasSup {
|
||||
span,
|
||||
sub_span,
|
||||
sup_span,
|
||||
sup_symbol,
|
||||
note: (),
|
||||
}
|
||||
}
|
||||
(Some(sub_span), Some(sup_span), Some(&sub_symbol), _) => {
|
||||
PlaceholderRelationLfNotSatisfied::HasSub {
|
||||
span,
|
||||
sub_span,
|
||||
sup_span,
|
||||
sub_symbol,
|
||||
note: (),
|
||||
}
|
||||
}
|
||||
(Some(sub_span), Some(sup_span), _, _) => {
|
||||
PlaceholderRelationLfNotSatisfied::HasNone {
|
||||
span,
|
||||
sub_span,
|
||||
sup_span,
|
||||
note: (),
|
||||
}
|
||||
}
|
||||
_ => PlaceholderRelationLfNotSatisfied::OnlyPrimarySpan { span, note: () },
|
||||
};
|
||||
Some(self.tcx().dcx().create_err(diag))
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,618 +0,0 @@
|
|||
//! Error Reporting for static impl Traits.
|
||||
|
||||
use crate::error_reporting::infer::nice_region_error::NiceRegionError;
|
||||
use crate::errors::{
|
||||
ButCallingIntroduces, ButNeedsToSatisfy, DynTraitConstraintSuggestion, MoreTargeted,
|
||||
ReqIntroducedLocations,
|
||||
};
|
||||
use crate::infer::RegionResolutionError;
|
||||
use crate::infer::{SubregionOrigin, TypeTrace};
|
||||
use crate::traits::{ObligationCauseCode, UnifyReceiverContext};
|
||||
use rustc_data_structures::fx::FxIndexSet;
|
||||
use rustc_errors::{Applicability, Diag, ErrorGuaranteed, MultiSpan, Subdiagnostic};
|
||||
use rustc_hir::def_id::DefId;
|
||||
use rustc_hir::intravisit::{walk_ty, Visitor};
|
||||
use rustc_hir::{
|
||||
self as hir, GenericBound, GenericParam, GenericParamKind, Item, ItemKind, Lifetime,
|
||||
LifetimeName, LifetimeParamKind, MissingLifetimeKind, Node, TyKind,
|
||||
};
|
||||
use rustc_middle::ty::{
|
||||
self, AssocItemContainer, StaticLifetimeVisitor, Ty, TyCtxt, TypeSuperVisitable, TypeVisitor,
|
||||
};
|
||||
use rustc_span::symbol::Ident;
|
||||
use rustc_span::Span;
|
||||
|
||||
use rustc_span::def_id::LocalDefId;
|
||||
|
||||
impl<'a, 'tcx> NiceRegionError<'a, 'tcx> {
|
||||
/// Print the error message for lifetime errors when the return type is a static `impl Trait`,
|
||||
/// `dyn Trait` or if a method call on a trait object introduces a static requirement.
|
||||
pub(super) fn try_report_static_impl_trait(&self) -> Option<ErrorGuaranteed> {
|
||||
debug!("try_report_static_impl_trait(error={:?})", self.error);
|
||||
let tcx = self.tcx();
|
||||
let (var_origin, sub_origin, sub_r, sup_origin, sup_r, spans) = match self.error.as_ref()? {
|
||||
RegionResolutionError::SubSupConflict(
|
||||
_,
|
||||
var_origin,
|
||||
sub_origin,
|
||||
sub_r,
|
||||
sup_origin,
|
||||
sup_r,
|
||||
spans,
|
||||
) if sub_r.is_static() => (var_origin, sub_origin, sub_r, sup_origin, sup_r, spans),
|
||||
RegionResolutionError::ConcreteFailure(
|
||||
SubregionOrigin::Subtype(box TypeTrace { cause, .. }),
|
||||
sub_r,
|
||||
sup_r,
|
||||
) if sub_r.is_static() => {
|
||||
// This is for an implicit `'static` requirement coming from `impl dyn Trait {}`.
|
||||
if let ObligationCauseCode::UnifyReceiver(ctxt) = cause.code() {
|
||||
// This may have a closure and it would cause ICE
|
||||
// through `find_param_with_region` (#78262).
|
||||
let anon_reg_sup = tcx.is_suitable_region(self.generic_param_scope, *sup_r)?;
|
||||
let fn_returns = tcx.return_type_impl_or_dyn_traits(anon_reg_sup.def_id);
|
||||
if fn_returns.is_empty() {
|
||||
return None;
|
||||
}
|
||||
|
||||
let param = self.find_param_with_region(*sup_r, *sub_r)?;
|
||||
let simple_ident = param.param.pat.simple_ident();
|
||||
|
||||
let (has_impl_path, impl_path) = match ctxt.assoc_item.container {
|
||||
AssocItemContainer::TraitContainer => {
|
||||
let id = ctxt.assoc_item.container_id(tcx);
|
||||
(true, tcx.def_path_str(id))
|
||||
}
|
||||
AssocItemContainer::ImplContainer => (false, String::new()),
|
||||
};
|
||||
|
||||
let mut err = self.tcx().dcx().create_err(ButCallingIntroduces {
|
||||
param_ty_span: param.param_ty_span,
|
||||
cause_span: cause.span,
|
||||
has_param_name: simple_ident.is_some(),
|
||||
param_name: simple_ident.map(|x| x.to_string()).unwrap_or_default(),
|
||||
has_lifetime: sup_r.has_name(),
|
||||
lifetime: sup_r.to_string(),
|
||||
assoc_item: ctxt.assoc_item.name,
|
||||
has_impl_path,
|
||||
impl_path,
|
||||
});
|
||||
if self.find_impl_on_dyn_trait(&mut err, param.param_ty, ctxt) {
|
||||
let reported = err.emit();
|
||||
return Some(reported);
|
||||
} else {
|
||||
err.cancel()
|
||||
}
|
||||
}
|
||||
return None;
|
||||
}
|
||||
_ => return None,
|
||||
};
|
||||
debug!(
|
||||
"try_report_static_impl_trait(var={:?}, sub={:?} {:?} sup={:?} {:?})",
|
||||
var_origin, sub_origin, sub_r, sup_origin, sup_r
|
||||
);
|
||||
let anon_reg_sup = tcx.is_suitable_region(self.generic_param_scope, *sup_r)?;
|
||||
debug!("try_report_static_impl_trait: anon_reg_sup={:?}", anon_reg_sup);
|
||||
let sp = var_origin.span();
|
||||
let return_sp = sub_origin.span();
|
||||
let param = self.find_param_with_region(*sup_r, *sub_r)?;
|
||||
let simple_ident = param.param.pat.simple_ident();
|
||||
let lifetime_name = if sup_r.has_name() { sup_r.to_string() } else { "'_".to_owned() };
|
||||
|
||||
let (mention_influencer, influencer_point) =
|
||||
if sup_origin.span().overlaps(param.param_ty_span) {
|
||||
// Account for `async fn` like in `async-await/issues/issue-62097.rs`.
|
||||
// The desugaring of `async fn`s causes `sup_origin` and `param` to point at the same
|
||||
// place (but with different `ctxt`, hence `overlaps` instead of `==` above).
|
||||
//
|
||||
// This avoids the following:
|
||||
//
|
||||
// LL | pub async fn run_dummy_fn(&self) {
|
||||
// | ^^^^^
|
||||
// | |
|
||||
// | this data with an anonymous lifetime `'_`...
|
||||
// | ...is captured here...
|
||||
(false, sup_origin.span())
|
||||
} else {
|
||||
(!sup_origin.span().overlaps(return_sp), param.param_ty_span)
|
||||
};
|
||||
|
||||
debug!("try_report_static_impl_trait: param_info={:?}", param);
|
||||
|
||||
let mut spans = spans.clone();
|
||||
|
||||
if mention_influencer {
|
||||
spans.push(sup_origin.span());
|
||||
}
|
||||
// We dedup the spans *ignoring* expansion context.
|
||||
spans.sort();
|
||||
spans.dedup_by_key(|span| (span.lo(), span.hi()));
|
||||
|
||||
// We try to make the output have fewer overlapping spans if possible.
|
||||
let require_span =
|
||||
if sup_origin.span().overlaps(return_sp) { sup_origin.span() } else { return_sp };
|
||||
|
||||
let spans_empty = spans.is_empty();
|
||||
let require_as_note = spans.iter().any(|sp| sp.overlaps(return_sp) || *sp > return_sp);
|
||||
let bound = if let SubregionOrigin::RelateParamBound(_, _, Some(bound)) = sub_origin {
|
||||
Some(*bound)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let mut subdiag = None;
|
||||
|
||||
if let SubregionOrigin::Subtype(box TypeTrace { cause, .. }) = sub_origin {
|
||||
if let ObligationCauseCode::ReturnValue(hir_id)
|
||||
| ObligationCauseCode::BlockTailExpression(hir_id, ..) = cause.code()
|
||||
{
|
||||
let parent_id = tcx.hir().get_parent_item(*hir_id);
|
||||
if let Some(fn_decl) = tcx.hir().fn_decl_by_hir_id(parent_id.into()) {
|
||||
let mut span: MultiSpan = fn_decl.output.span().into();
|
||||
let mut spans = Vec::new();
|
||||
let mut add_label = true;
|
||||
if let hir::FnRetTy::Return(ty) = fn_decl.output {
|
||||
let mut v = StaticLifetimeVisitor(vec![], tcx.hir());
|
||||
v.visit_ty(ty);
|
||||
if !v.0.is_empty() {
|
||||
span = v.0.clone().into();
|
||||
spans = v.0;
|
||||
add_label = false;
|
||||
}
|
||||
}
|
||||
let fn_decl_span = fn_decl.output.span();
|
||||
|
||||
subdiag = Some(ReqIntroducedLocations {
|
||||
span,
|
||||
spans,
|
||||
fn_decl_span,
|
||||
cause_span: cause.span,
|
||||
add_label,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let diag = ButNeedsToSatisfy {
|
||||
sp,
|
||||
influencer_point,
|
||||
spans: spans.clone(),
|
||||
// If any of the "captured here" labels appears on the same line or after
|
||||
// `require_span`, we put it on a note to ensure the text flows by appearing
|
||||
// always at the end.
|
||||
require_span_as_note: require_as_note.then_some(require_span),
|
||||
// We don't need a note, it's already at the end, it can be shown as a `span_label`.
|
||||
require_span_as_label: (!require_as_note).then_some(require_span),
|
||||
req_introduces_loc: subdiag,
|
||||
|
||||
has_lifetime: sup_r.has_name(),
|
||||
lifetime: lifetime_name.clone(),
|
||||
has_param_name: simple_ident.is_some(),
|
||||
param_name: simple_ident.map(|x| x.to_string()).unwrap_or_default(),
|
||||
spans_empty,
|
||||
bound,
|
||||
};
|
||||
|
||||
let mut err = self.tcx().dcx().create_err(diag);
|
||||
|
||||
let fn_returns = tcx.return_type_impl_or_dyn_traits(anon_reg_sup.def_id);
|
||||
|
||||
let mut override_error_code = None;
|
||||
if let SubregionOrigin::Subtype(box TypeTrace { cause, .. }) = &sup_origin
|
||||
&& let ObligationCauseCode::UnifyReceiver(ctxt) = cause.code()
|
||||
// Handle case of `impl Foo for dyn Bar { fn qux(&self) {} }` introducing a
|
||||
// `'static` lifetime when called as a method on a binding: `bar.qux()`.
|
||||
&& self.find_impl_on_dyn_trait(&mut err, param.param_ty, ctxt)
|
||||
{
|
||||
override_error_code = Some(ctxt.assoc_item.name);
|
||||
}
|
||||
|
||||
if let SubregionOrigin::Subtype(box TypeTrace { cause, .. }) = &sub_origin
|
||||
&& let code = match cause.code() {
|
||||
ObligationCauseCode::MatchImpl(parent, ..) => parent.code(),
|
||||
_ => cause.code(),
|
||||
}
|
||||
&& let (
|
||||
&ObligationCauseCode::WhereClause(item_def_id, _)
|
||||
| &ObligationCauseCode::WhereClauseInExpr(item_def_id, ..),
|
||||
None,
|
||||
) = (code, override_error_code)
|
||||
{
|
||||
// Same case of `impl Foo for dyn Bar { fn qux(&self) {} }` introducing a `'static`
|
||||
// lifetime as above, but called using a fully-qualified path to the method:
|
||||
// `Foo::qux(bar)`.
|
||||
let mut v = TraitObjectVisitor(FxIndexSet::default());
|
||||
v.visit_ty(param.param_ty);
|
||||
if let Some((ident, self_ty)) =
|
||||
NiceRegionError::get_impl_ident_and_self_ty_from_trait(tcx, item_def_id, &v.0)
|
||||
&& self.suggest_constrain_dyn_trait_in_impl(&mut err, &v.0, ident, self_ty)
|
||||
{
|
||||
override_error_code = Some(ident.name);
|
||||
}
|
||||
}
|
||||
if let (Some(ident), true) = (override_error_code, fn_returns.is_empty()) {
|
||||
// Provide a more targeted error code and description.
|
||||
let retarget_subdiag = MoreTargeted { ident };
|
||||
retarget_subdiag.add_to_diag(&mut err);
|
||||
}
|
||||
|
||||
let arg = match param.param.pat.simple_ident() {
|
||||
Some(simple_ident) => format!("argument `{simple_ident}`"),
|
||||
None => "the argument".to_string(),
|
||||
};
|
||||
let captures = format!("captures data from {arg}");
|
||||
suggest_new_region_bound(
|
||||
tcx,
|
||||
&mut err,
|
||||
fn_returns,
|
||||
lifetime_name,
|
||||
Some(arg),
|
||||
captures,
|
||||
Some((param.param_ty_span, param.param_ty.to_string())),
|
||||
Some(anon_reg_sup.def_id),
|
||||
);
|
||||
|
||||
let reported = err.emit();
|
||||
Some(reported)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn suggest_new_region_bound(
|
||||
tcx: TyCtxt<'_>,
|
||||
err: &mut Diag<'_>,
|
||||
fn_returns: Vec<&rustc_hir::Ty<'_>>,
|
||||
lifetime_name: String,
|
||||
arg: Option<String>,
|
||||
captures: String,
|
||||
param: Option<(Span, String)>,
|
||||
scope_def_id: Option<LocalDefId>,
|
||||
) {
|
||||
debug!("try_report_static_impl_trait: fn_return={:?}", fn_returns);
|
||||
// FIXME: account for the need of parens in `&(dyn Trait + '_)`
|
||||
let consider = "consider changing";
|
||||
let declare = "to declare that";
|
||||
let explicit = format!("you can add an explicit `{lifetime_name}` lifetime bound");
|
||||
let explicit_static =
|
||||
arg.map(|arg| format!("explicit `'static` bound to the lifetime of {arg}"));
|
||||
let add_static_bound = "alternatively, add an explicit `'static` bound to this reference";
|
||||
let plus_lt = format!(" + {lifetime_name}");
|
||||
for fn_return in fn_returns {
|
||||
if fn_return.span.desugaring_kind().is_some() {
|
||||
// Skip `async` desugaring `impl Future`.
|
||||
continue;
|
||||
}
|
||||
match fn_return.kind {
|
||||
// FIXME(precise_captures): Suggest adding to `use<...>` list instead.
|
||||
TyKind::OpaqueDef(item_id, _, _) => {
|
||||
let item = tcx.hir().item(item_id);
|
||||
let ItemKind::OpaqueTy(opaque) = &item.kind else {
|
||||
return;
|
||||
};
|
||||
|
||||
// Get the identity type for this RPIT
|
||||
let did = item_id.owner_id.to_def_id();
|
||||
let ty = Ty::new_opaque(tcx, did, ty::GenericArgs::identity_for_item(tcx, did));
|
||||
|
||||
if let Some(span) = opaque.bounds.iter().find_map(|arg| match arg {
|
||||
GenericBound::Outlives(Lifetime {
|
||||
res: LifetimeName::Static, ident, ..
|
||||
}) => Some(ident.span),
|
||||
_ => None,
|
||||
}) {
|
||||
if let Some(explicit_static) = &explicit_static {
|
||||
err.span_suggestion_verbose(
|
||||
span,
|
||||
format!("{consider} `{ty}`'s {explicit_static}"),
|
||||
&lifetime_name,
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
}
|
||||
if let Some((param_span, ref param_ty)) = param {
|
||||
err.span_suggestion_verbose(
|
||||
param_span,
|
||||
add_static_bound,
|
||||
param_ty,
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
}
|
||||
} else if opaque.bounds.iter().any(|arg| {
|
||||
matches!(arg,
|
||||
GenericBound::Outlives(Lifetime { ident, .. })
|
||||
if ident.name.to_string() == lifetime_name )
|
||||
}) {
|
||||
} else {
|
||||
// get a lifetime name of existing named lifetimes if any
|
||||
let existing_lt_name = if let Some(id) = scope_def_id
|
||||
&& let Some(generics) = tcx.hir().get_generics(id)
|
||||
&& let named_lifetimes = generics
|
||||
.params
|
||||
.iter()
|
||||
.filter(|p| {
|
||||
matches!(
|
||||
p.kind,
|
||||
GenericParamKind::Lifetime {
|
||||
kind: hir::LifetimeParamKind::Explicit
|
||||
}
|
||||
)
|
||||
})
|
||||
.map(|p| {
|
||||
if let hir::ParamName::Plain(name) = p.name {
|
||||
Some(name.to_string())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.filter(|n| !matches!(n, None))
|
||||
.collect::<Vec<_>>()
|
||||
&& named_lifetimes.len() > 0
|
||||
{
|
||||
named_lifetimes[0].clone()
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let name = if let Some(name) = &existing_lt_name { name } else { "'a" };
|
||||
// if there are more than one elided lifetimes in inputs, the explicit `'_` lifetime cannot be used.
|
||||
// introducing a new lifetime `'a` or making use of one from existing named lifetimes if any
|
||||
if let Some(id) = scope_def_id
|
||||
&& let Some(generics) = tcx.hir().get_generics(id)
|
||||
&& let mut spans_suggs =
|
||||
make_elided_region_spans_suggs(name, generics.params.iter())
|
||||
&& spans_suggs.len() > 1
|
||||
{
|
||||
let use_lt = if existing_lt_name == None {
|
||||
spans_suggs.push((generics.span.shrink_to_hi(), format!("<{name}>")));
|
||||
format!("you can introduce a named lifetime parameter `{name}`")
|
||||
} else {
|
||||
// make use the existing named lifetime
|
||||
format!("you can use the named lifetime parameter `{name}`")
|
||||
};
|
||||
spans_suggs.push((fn_return.span.shrink_to_hi(), format!(" + {name} ")));
|
||||
err.multipart_suggestion_verbose(
|
||||
format!("{declare} `{ty}` {captures}, {use_lt}",),
|
||||
spans_suggs,
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
} else {
|
||||
err.span_suggestion_verbose(
|
||||
fn_return.span.shrink_to_hi(),
|
||||
format!("{declare} `{ty}` {captures}, {explicit}",),
|
||||
&plus_lt,
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
TyKind::TraitObject(_, lt, _) => {
|
||||
if let LifetimeName::ImplicitObjectLifetimeDefault = lt.res {
|
||||
err.span_suggestion_verbose(
|
||||
fn_return.span.shrink_to_hi(),
|
||||
format!("{declare} the trait object {captures}, {explicit}",),
|
||||
&plus_lt,
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
} else if lt.ident.name.to_string() != lifetime_name {
|
||||
// With this check we avoid suggesting redundant bounds. This
|
||||
// would happen if there are nested impl/dyn traits and only
|
||||
// one of them has the bound we'd suggest already there, like
|
||||
// in `impl Foo<X = dyn Bar> + '_`.
|
||||
if let Some(explicit_static) = &explicit_static {
|
||||
err.span_suggestion_verbose(
|
||||
lt.ident.span,
|
||||
format!("{consider} the trait object's {explicit_static}"),
|
||||
&lifetime_name,
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
}
|
||||
if let Some((param_span, param_ty)) = param.clone() {
|
||||
err.span_suggestion_verbose(
|
||||
param_span,
|
||||
add_static_bound,
|
||||
param_ty,
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn make_elided_region_spans_suggs<'a>(
|
||||
name: &str,
|
||||
generic_params: impl Iterator<Item = &'a GenericParam<'a>>,
|
||||
) -> Vec<(Span, String)> {
|
||||
let mut spans_suggs = Vec::new();
|
||||
let mut bracket_span = None;
|
||||
let mut consecutive_brackets = 0;
|
||||
|
||||
let mut process_consecutive_brackets =
|
||||
|span: Option<Span>, spans_suggs: &mut Vec<(Span, String)>| {
|
||||
if span
|
||||
.is_some_and(|span| bracket_span.map_or(true, |bracket_span| span == bracket_span))
|
||||
{
|
||||
consecutive_brackets += 1;
|
||||
} else if let Some(bracket_span) = bracket_span.take() {
|
||||
let sugg = std::iter::once("<")
|
||||
.chain(std::iter::repeat(name).take(consecutive_brackets).intersperse(", "))
|
||||
.chain([">"])
|
||||
.collect();
|
||||
spans_suggs.push((bracket_span.shrink_to_hi(), sugg));
|
||||
consecutive_brackets = 0;
|
||||
}
|
||||
bracket_span = span;
|
||||
};
|
||||
|
||||
for p in generic_params {
|
||||
if let GenericParamKind::Lifetime { kind: LifetimeParamKind::Elided(kind) } = p.kind {
|
||||
match kind {
|
||||
MissingLifetimeKind::Underscore => {
|
||||
process_consecutive_brackets(None, &mut spans_suggs);
|
||||
spans_suggs.push((p.span, name.to_string()))
|
||||
}
|
||||
MissingLifetimeKind::Ampersand => {
|
||||
process_consecutive_brackets(None, &mut spans_suggs);
|
||||
spans_suggs.push((p.span.shrink_to_hi(), format!("{name} ")));
|
||||
}
|
||||
MissingLifetimeKind::Comma => {
|
||||
process_consecutive_brackets(None, &mut spans_suggs);
|
||||
spans_suggs.push((p.span.shrink_to_hi(), format!("{name}, ")));
|
||||
}
|
||||
MissingLifetimeKind::Brackets => {
|
||||
process_consecutive_brackets(Some(p.span), &mut spans_suggs);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
process_consecutive_brackets(None, &mut spans_suggs);
|
||||
|
||||
spans_suggs
|
||||
}
|
||||
|
||||
impl<'a, 'tcx> NiceRegionError<'a, 'tcx> {
|
||||
pub fn get_impl_ident_and_self_ty_from_trait(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
def_id: DefId,
|
||||
trait_objects: &FxIndexSet<DefId>,
|
||||
) -> Option<(Ident, &'tcx hir::Ty<'tcx>)> {
|
||||
match tcx.hir().get_if_local(def_id)? {
|
||||
Node::ImplItem(impl_item) => {
|
||||
let impl_did = tcx.hir().get_parent_item(impl_item.hir_id());
|
||||
if let hir::OwnerNode::Item(Item {
|
||||
kind: ItemKind::Impl(hir::Impl { self_ty, .. }),
|
||||
..
|
||||
}) = tcx.hir_owner_node(impl_did)
|
||||
{
|
||||
Some((impl_item.ident, self_ty))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
Node::TraitItem(trait_item) => {
|
||||
let trait_id = tcx.hir().get_parent_item(trait_item.hir_id());
|
||||
debug_assert_eq!(tcx.def_kind(trait_id.def_id), hir::def::DefKind::Trait);
|
||||
// The method being called is defined in the `trait`, but the `'static`
|
||||
// obligation comes from the `impl`. Find that `impl` so that we can point
|
||||
// at it in the suggestion.
|
||||
let trait_did = trait_id.to_def_id();
|
||||
tcx.hir().trait_impls(trait_did).iter().find_map(|&impl_did| {
|
||||
if let Node::Item(Item {
|
||||
kind: ItemKind::Impl(hir::Impl { self_ty, .. }), ..
|
||||
}) = tcx.hir_node_by_def_id(impl_did)
|
||||
&& trait_objects.iter().all(|did| {
|
||||
// FIXME: we should check `self_ty` against the receiver
|
||||
// type in the `UnifyReceiver` context, but for now, use
|
||||
// this imperfect proxy. This will fail if there are
|
||||
// multiple `impl`s for the same trait like
|
||||
// `impl Foo for Box<dyn Bar>` and `impl Foo for dyn Bar`.
|
||||
// In that case, only the first one will get suggestions.
|
||||
let mut traits = vec![];
|
||||
let mut hir_v = HirTraitObjectVisitor(&mut traits, *did);
|
||||
hir_v.visit_ty(self_ty);
|
||||
!traits.is_empty()
|
||||
})
|
||||
{
|
||||
Some((trait_item.ident, *self_ty))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// When we call a method coming from an `impl Foo for dyn Bar`, `dyn Bar` introduces a default
|
||||
/// `'static` obligation. Suggest relaxing that implicit bound.
|
||||
fn find_impl_on_dyn_trait(
|
||||
&self,
|
||||
err: &mut Diag<'_>,
|
||||
ty: Ty<'_>,
|
||||
ctxt: &UnifyReceiverContext<'tcx>,
|
||||
) -> bool {
|
||||
let tcx = self.tcx();
|
||||
|
||||
// Find the method being called.
|
||||
let Ok(Some(instance)) = ty::Instance::try_resolve(
|
||||
tcx,
|
||||
ctxt.param_env,
|
||||
ctxt.assoc_item.def_id,
|
||||
self.cx.resolve_vars_if_possible(ctxt.args),
|
||||
) else {
|
||||
return false;
|
||||
};
|
||||
|
||||
let mut v = TraitObjectVisitor(FxIndexSet::default());
|
||||
v.visit_ty(ty);
|
||||
|
||||
// Get the `Ident` of the method being called and the corresponding `impl` (to point at
|
||||
// `Bar` in `impl Foo for dyn Bar {}` and the definition of the method being called).
|
||||
let Some((ident, self_ty)) =
|
||||
NiceRegionError::get_impl_ident_and_self_ty_from_trait(tcx, instance.def_id(), &v.0)
|
||||
else {
|
||||
return false;
|
||||
};
|
||||
|
||||
// Find the trait object types in the argument, so we point at *only* the trait object.
|
||||
self.suggest_constrain_dyn_trait_in_impl(err, &v.0, ident, self_ty)
|
||||
}
|
||||
|
||||
fn suggest_constrain_dyn_trait_in_impl(
|
||||
&self,
|
||||
err: &mut Diag<'_>,
|
||||
found_dids: &FxIndexSet<DefId>,
|
||||
ident: Ident,
|
||||
self_ty: &hir::Ty<'_>,
|
||||
) -> bool {
|
||||
let mut suggested = false;
|
||||
for found_did in found_dids {
|
||||
let mut traits = vec![];
|
||||
let mut hir_v = HirTraitObjectVisitor(&mut traits, *found_did);
|
||||
hir_v.visit_ty(self_ty);
|
||||
for &span in &traits {
|
||||
let subdiag = DynTraitConstraintSuggestion { span, ident };
|
||||
subdiag.add_to_diag(err);
|
||||
suggested = true;
|
||||
}
|
||||
}
|
||||
suggested
|
||||
}
|
||||
}
|
||||
|
||||
/// Collect all the trait objects in a type that could have received an implicit `'static` lifetime.
|
||||
pub struct TraitObjectVisitor(pub FxIndexSet<DefId>);
|
||||
|
||||
impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for TraitObjectVisitor {
|
||||
fn visit_ty(&mut self, t: Ty<'tcx>) {
|
||||
match t.kind() {
|
||||
ty::Dynamic(preds, re, _) if re.is_static() => {
|
||||
if let Some(def_id) = preds.principal_def_id() {
|
||||
self.0.insert(def_id);
|
||||
}
|
||||
}
|
||||
_ => t.super_visit_with(self),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Collect all `hir::Ty<'_>` `Span`s for trait objects with an implicit lifetime.
|
||||
pub struct HirTraitObjectVisitor<'a>(pub &'a mut Vec<Span>, pub DefId);
|
||||
|
||||
impl<'a, 'tcx> Visitor<'tcx> for HirTraitObjectVisitor<'a> {
|
||||
fn visit_ty(&mut self, t: &'tcx hir::Ty<'tcx>) {
|
||||
if let TyKind::TraitObject(
|
||||
poly_trait_refs,
|
||||
Lifetime { res: LifetimeName::ImplicitObjectLifetimeDefault, .. },
|
||||
_,
|
||||
) = t.kind
|
||||
{
|
||||
for ptr in poly_trait_refs {
|
||||
if Some(self.1) == ptr.trait_ref.trait_def_id() {
|
||||
self.0.push(ptr.span);
|
||||
}
|
||||
}
|
||||
}
|
||||
walk_ty(self, t);
|
||||
}
|
||||
}
|
|
@ -1,162 +0,0 @@
|
|||
//! Error Reporting for `impl` items that do not match the obligations from their `trait`.
|
||||
|
||||
use crate::error_reporting::infer::nice_region_error::NiceRegionError;
|
||||
use crate::errors::{ConsiderBorrowingParamHelp, RelationshipHelp, TraitImplDiff};
|
||||
use crate::infer::RegionResolutionError;
|
||||
use crate::infer::{Subtype, ValuePairs};
|
||||
use rustc_errors::ErrorGuaranteed;
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::def::Res;
|
||||
use rustc_hir::def_id::DefId;
|
||||
use rustc_hir::intravisit::Visitor;
|
||||
use rustc_middle::hir::nested_filter;
|
||||
use rustc_middle::traits::ObligationCauseCode;
|
||||
use rustc_middle::ty::error::ExpectedFound;
|
||||
use rustc_middle::ty::print::RegionHighlightMode;
|
||||
use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitor};
|
||||
use rustc_span::Span;
|
||||
|
||||
impl<'a, 'tcx> NiceRegionError<'a, 'tcx> {
|
||||
/// Print the error message for lifetime errors when the `impl` doesn't conform to the `trait`.
|
||||
pub(super) fn try_report_impl_not_conforming_to_trait(&self) -> Option<ErrorGuaranteed> {
|
||||
let error = self.error.as_ref()?;
|
||||
debug!("try_report_impl_not_conforming_to_trait {:?}", error);
|
||||
if let RegionResolutionError::SubSupConflict(
|
||||
_,
|
||||
var_origin,
|
||||
sub_origin,
|
||||
_sub,
|
||||
sup_origin,
|
||||
_sup,
|
||||
_,
|
||||
) = error.clone()
|
||||
&& let (Subtype(sup_trace), Subtype(sub_trace)) = (&sup_origin, &sub_origin)
|
||||
&& let ObligationCauseCode::CompareImplItem { trait_item_def_id, .. } =
|
||||
sub_trace.cause.code()
|
||||
&& sub_trace.values == sup_trace.values
|
||||
&& let ValuePairs::PolySigs(ExpectedFound { expected, found }) = sub_trace.values
|
||||
{
|
||||
// FIXME(compiler-errors): Don't like that this needs `Ty`s, but
|
||||
// all of the region highlighting machinery only deals with those.
|
||||
let guar = self.emit_err(
|
||||
var_origin.span(),
|
||||
Ty::new_fn_ptr(self.cx.tcx, expected),
|
||||
Ty::new_fn_ptr(self.cx.tcx, found),
|
||||
*trait_item_def_id,
|
||||
);
|
||||
return Some(guar);
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
fn emit_err(
|
||||
&self,
|
||||
sp: Span,
|
||||
expected: Ty<'tcx>,
|
||||
found: Ty<'tcx>,
|
||||
trait_def_id: DefId,
|
||||
) -> ErrorGuaranteed {
|
||||
let trait_sp = self.tcx().def_span(trait_def_id);
|
||||
|
||||
// Mark all unnamed regions in the type with a number.
|
||||
// This diagnostic is called in response to lifetime errors, so be informative.
|
||||
struct HighlightBuilder<'tcx> {
|
||||
highlight: RegionHighlightMode<'tcx>,
|
||||
counter: usize,
|
||||
}
|
||||
|
||||
impl<'tcx> HighlightBuilder<'tcx> {
|
||||
fn build(ty: Ty<'tcx>) -> RegionHighlightMode<'tcx> {
|
||||
let mut builder =
|
||||
HighlightBuilder { highlight: RegionHighlightMode::default(), counter: 1 };
|
||||
builder.visit_ty(ty);
|
||||
builder.highlight
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> ty::visit::TypeVisitor<TyCtxt<'tcx>> for HighlightBuilder<'tcx> {
|
||||
fn visit_region(&mut self, r: ty::Region<'tcx>) {
|
||||
if !r.has_name() && self.counter <= 3 {
|
||||
self.highlight.highlighting_region(r, self.counter);
|
||||
self.counter += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let expected_highlight = HighlightBuilder::build(expected);
|
||||
let expected = self
|
||||
.cx
|
||||
.extract_inference_diagnostics_data(expected.into(), Some(expected_highlight))
|
||||
.name;
|
||||
let found_highlight = HighlightBuilder::build(found);
|
||||
let found =
|
||||
self.cx.extract_inference_diagnostics_data(found.into(), Some(found_highlight)).name;
|
||||
|
||||
// Get the span of all the used type parameters in the method.
|
||||
let assoc_item = self.tcx().associated_item(trait_def_id);
|
||||
let mut visitor = TypeParamSpanVisitor { tcx: self.tcx(), types: vec![] };
|
||||
match assoc_item.kind {
|
||||
ty::AssocKind::Fn => {
|
||||
let hir = self.tcx().hir();
|
||||
if let Some(hir_id) =
|
||||
assoc_item.def_id.as_local().map(|id| self.tcx().local_def_id_to_hir_id(id))
|
||||
{
|
||||
if let Some(decl) = hir.fn_decl_by_hir_id(hir_id) {
|
||||
visitor.visit_fn_decl(decl);
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
let diag = TraitImplDiff {
|
||||
sp,
|
||||
trait_sp,
|
||||
note: (),
|
||||
param_help: ConsiderBorrowingParamHelp { spans: visitor.types.to_vec() },
|
||||
rel_help: visitor.types.is_empty().then_some(RelationshipHelp),
|
||||
expected,
|
||||
found,
|
||||
};
|
||||
|
||||
self.tcx().dcx().emit_err(diag)
|
||||
}
|
||||
}
|
||||
|
||||
struct TypeParamSpanVisitor<'tcx> {
|
||||
tcx: TyCtxt<'tcx>,
|
||||
types: Vec<Span>,
|
||||
}
|
||||
|
||||
impl<'tcx> Visitor<'tcx> for TypeParamSpanVisitor<'tcx> {
|
||||
type NestedFilter = nested_filter::OnlyBodies;
|
||||
|
||||
fn nested_visit_map(&mut self) -> Self::Map {
|
||||
self.tcx.hir()
|
||||
}
|
||||
|
||||
fn visit_ty(&mut self, arg: &'tcx hir::Ty<'tcx>) {
|
||||
match arg.kind {
|
||||
hir::TyKind::Ref(_, ref mut_ty) => {
|
||||
// We don't want to suggest looking into borrowing `&T` or `&Self`.
|
||||
hir::intravisit::walk_ty(self, mut_ty.ty);
|
||||
return;
|
||||
}
|
||||
hir::TyKind::Path(hir::QPath::Resolved(None, path)) => match &path.segments {
|
||||
[segment]
|
||||
if matches!(
|
||||
segment.res,
|
||||
Res::SelfTyParam { .. }
|
||||
| Res::SelfTyAlias { .. }
|
||||
| Res::Def(hir::def::DefKind::TyParam, _)
|
||||
) =>
|
||||
{
|
||||
self.types.push(path.span);
|
||||
}
|
||||
_ => {}
|
||||
},
|
||||
_ => {}
|
||||
}
|
||||
hir::intravisit::walk_ty(self, arg);
|
||||
}
|
||||
}
|
|
@ -1,166 +0,0 @@
|
|||
//! Helper functions corresponding to lifetime errors due to
|
||||
//! anonymous regions.
|
||||
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::def_id::LocalDefId;
|
||||
use rustc_middle::ty::{self, Binder, Region, Ty, TyCtxt, TypeFoldable};
|
||||
use rustc_span::Span;
|
||||
|
||||
use crate::error_reporting::infer::nice_region_error::NiceRegionError;
|
||||
|
||||
/// Information about the anonymous region we are searching for.
|
||||
#[derive(Debug)]
|
||||
pub struct AnonymousParamInfo<'tcx> {
|
||||
/// The parameter corresponding to the anonymous region.
|
||||
pub param: &'tcx hir::Param<'tcx>,
|
||||
/// The type corresponding to the anonymous region parameter.
|
||||
pub param_ty: Ty<'tcx>,
|
||||
/// The ty::BoundRegionKind corresponding to the anonymous region.
|
||||
pub bound_region: ty::BoundRegionKind,
|
||||
/// The `Span` of the parameter type.
|
||||
pub param_ty_span: Span,
|
||||
/// Signals that the argument is the first parameter in the declaration.
|
||||
pub is_first: bool,
|
||||
}
|
||||
|
||||
// This method walks the Type of the function body parameters using
|
||||
// `fold_regions()` function and returns the
|
||||
// &hir::Param of the function parameter corresponding to the anonymous
|
||||
// region and the Ty corresponding to the named region.
|
||||
// Currently only the case where the function declaration consists of
|
||||
// one named region and one anonymous region is handled.
|
||||
// Consider the example `fn foo<'a>(x: &'a i32, y: &i32) -> &'a i32`
|
||||
// Here, we would return the hir::Param for y, we return the type &'a
|
||||
// i32, which is the type of y but with the anonymous region replaced
|
||||
// with 'a, the corresponding bound region and is_first which is true if
|
||||
// the hir::Param is the first parameter in the function declaration.
|
||||
#[instrument(skip(tcx), level = "debug")]
|
||||
pub fn find_param_with_region<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
generic_param_scope: LocalDefId,
|
||||
anon_region: Region<'tcx>,
|
||||
replace_region: Region<'tcx>,
|
||||
) -> Option<AnonymousParamInfo<'tcx>> {
|
||||
let (id, bound_region) = match *anon_region {
|
||||
ty::ReLateParam(late_param) => (late_param.scope, late_param.bound_region),
|
||||
ty::ReEarlyParam(ebr) => {
|
||||
let region_def = tcx.generics_of(generic_param_scope).region_param(ebr, tcx).def_id;
|
||||
(tcx.parent(region_def), ty::BoundRegionKind::BrNamed(region_def, ebr.name))
|
||||
}
|
||||
_ => return None, // not a free region
|
||||
};
|
||||
|
||||
let hir = &tcx.hir();
|
||||
let def_id = id.as_local()?;
|
||||
|
||||
// FIXME: use def_kind
|
||||
// Don't perform this on closures
|
||||
match tcx.hir_node_by_def_id(generic_param_scope) {
|
||||
hir::Node::Expr(&hir::Expr { kind: hir::ExprKind::Closure { .. }, .. }) => {
|
||||
return None;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
let body = hir.maybe_body_owned_by(def_id)?;
|
||||
|
||||
let owner_id = hir.body_owner(body.id());
|
||||
let fn_decl = hir.fn_decl_by_hir_id(owner_id)?;
|
||||
let poly_fn_sig = tcx.fn_sig(id).instantiate_identity();
|
||||
|
||||
let fn_sig = tcx.liberate_late_bound_regions(id, poly_fn_sig);
|
||||
body.params
|
||||
.iter()
|
||||
.take(if fn_sig.c_variadic {
|
||||
fn_sig.inputs().len()
|
||||
} else {
|
||||
assert_eq!(fn_sig.inputs().len(), body.params.len());
|
||||
body.params.len()
|
||||
})
|
||||
.enumerate()
|
||||
.find_map(|(index, param)| {
|
||||
// May return None; sometimes the tables are not yet populated.
|
||||
let ty = fn_sig.inputs()[index];
|
||||
let mut found_anon_region = false;
|
||||
let new_param_ty = tcx.fold_regions(ty, |r, _| {
|
||||
if r == anon_region {
|
||||
found_anon_region = true;
|
||||
replace_region
|
||||
} else {
|
||||
r
|
||||
}
|
||||
});
|
||||
found_anon_region.then(|| {
|
||||
let ty_hir_id = fn_decl.inputs[index].hir_id;
|
||||
let param_ty_span = hir.span(ty_hir_id);
|
||||
let is_first = index == 0;
|
||||
AnonymousParamInfo {
|
||||
param,
|
||||
param_ty: new_param_ty,
|
||||
param_ty_span,
|
||||
bound_region,
|
||||
is_first,
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
impl<'a, 'tcx> NiceRegionError<'a, 'tcx> {
|
||||
pub(super) fn find_param_with_region(
|
||||
&self,
|
||||
anon_region: Region<'tcx>,
|
||||
replace_region: Region<'tcx>,
|
||||
) -> Option<AnonymousParamInfo<'tcx>> {
|
||||
find_param_with_region(self.tcx(), self.generic_param_scope, anon_region, replace_region)
|
||||
}
|
||||
|
||||
// Here, we check for the case where the anonymous region
|
||||
// is in the return type as written by the user.
|
||||
// FIXME(#42703) - Need to handle certain cases here.
|
||||
pub(super) fn is_return_type_anon(
|
||||
&self,
|
||||
scope_def_id: LocalDefId,
|
||||
br: ty::BoundRegionKind,
|
||||
hir_sig: &hir::FnSig<'_>,
|
||||
) -> Option<Span> {
|
||||
let fn_ty = self.tcx().type_of(scope_def_id).instantiate_identity();
|
||||
if let ty::FnDef(_, _) = fn_ty.kind() {
|
||||
let ret_ty = fn_ty.fn_sig(self.tcx()).output();
|
||||
let span = hir_sig.decl.output.span();
|
||||
let future_output = if hir_sig.header.is_async() {
|
||||
ret_ty.map_bound(|ty| self.cx.get_impl_future_output_ty(ty)).transpose()
|
||||
} else {
|
||||
None
|
||||
};
|
||||
return match future_output {
|
||||
Some(output) if self.includes_region(output, br) => Some(span),
|
||||
None if self.includes_region(ret_ty, br) => Some(span),
|
||||
_ => None,
|
||||
};
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
fn includes_region(
|
||||
&self,
|
||||
ty: Binder<'tcx, impl TypeFoldable<TyCtxt<'tcx>>>,
|
||||
region: ty::BoundRegionKind,
|
||||
) -> bool {
|
||||
let late_bound_regions = self.tcx().collect_referenced_late_bound_regions(ty);
|
||||
// We are only checking is any region meets the condition so order doesn't matter
|
||||
#[allow(rustc::potential_query_instability)]
|
||||
late_bound_regions.iter().any(|r| *r == region)
|
||||
}
|
||||
|
||||
// Here we check for the case where anonymous region
|
||||
// corresponds to self and if yes, we display E0312.
|
||||
// FIXME(#42700) - Need to format self properly to
|
||||
// enable E0621 for it.
|
||||
pub(super) fn is_self_anon(&self, is_first: bool, scope_def_id: LocalDefId) -> bool {
|
||||
is_first
|
||||
&& self
|
||||
.tcx()
|
||||
.opt_associated_item(scope_def_id.to_def_id())
|
||||
.is_some_and(|i| i.fn_has_self_parameter)
|
||||
}
|
||||
}
|
|
@ -1,421 +0,0 @@
|
|||
use crate::error_reporting::infer::{note_and_explain_region, TypeErrCtxt};
|
||||
use crate::errors::{
|
||||
note_and_explain, FulfillReqLifetime, LfBoundNotSatisfied, OutlivesBound, OutlivesContent,
|
||||
RefLongerThanData, RegionOriginNote, WhereClauseSuggestions,
|
||||
};
|
||||
use crate::fluent_generated as fluent;
|
||||
use crate::infer::{self, SubregionOrigin};
|
||||
use rustc_errors::{Diag, Subdiagnostic};
|
||||
use rustc_hir::def_id::{DefId, LocalDefId};
|
||||
use rustc_middle::traits::ObligationCauseCode;
|
||||
use rustc_middle::ty::error::TypeError;
|
||||
use rustc_middle::ty::{self, IsSuggestable, Region, Ty};
|
||||
use rustc_span::symbol::kw;
|
||||
|
||||
use super::ObligationCauseAsDiagArg;
|
||||
|
||||
impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
|
||||
pub(super) fn note_region_origin(&self, err: &mut Diag<'_>, origin: &SubregionOrigin<'tcx>) {
|
||||
match *origin {
|
||||
infer::Subtype(ref trace) => RegionOriginNote::WithRequirement {
|
||||
span: trace.cause.span,
|
||||
requirement: ObligationCauseAsDiagArg(trace.cause.clone()),
|
||||
expected_found: self.values_str(trace.values).map(|(e, f, _)| (e, f)),
|
||||
}
|
||||
.add_to_diag(err),
|
||||
infer::Reborrow(span) => {
|
||||
RegionOriginNote::Plain { span, msg: fluent::infer_reborrow }.add_to_diag(err)
|
||||
}
|
||||
infer::RelateObjectBound(span) => {
|
||||
RegionOriginNote::Plain { span, msg: fluent::infer_relate_object_bound }
|
||||
.add_to_diag(err);
|
||||
}
|
||||
infer::ReferenceOutlivesReferent(ty, span) => {
|
||||
RegionOriginNote::WithName {
|
||||
span,
|
||||
msg: fluent::infer_reference_outlives_referent,
|
||||
name: &self.ty_to_string(ty),
|
||||
continues: false,
|
||||
}
|
||||
.add_to_diag(err);
|
||||
}
|
||||
infer::RelateParamBound(span, ty, opt_span) => {
|
||||
RegionOriginNote::WithName {
|
||||
span,
|
||||
msg: fluent::infer_relate_param_bound,
|
||||
name: &self.ty_to_string(ty),
|
||||
continues: opt_span.is_some(),
|
||||
}
|
||||
.add_to_diag(err);
|
||||
if let Some(span) = opt_span {
|
||||
RegionOriginNote::Plain { span, msg: fluent::infer_relate_param_bound_2 }
|
||||
.add_to_diag(err);
|
||||
}
|
||||
}
|
||||
infer::RelateRegionParamBound(span) => {
|
||||
RegionOriginNote::Plain { span, msg: fluent::infer_relate_region_param_bound }
|
||||
.add_to_diag(err);
|
||||
}
|
||||
infer::CompareImplItemObligation { span, .. } => {
|
||||
RegionOriginNote::Plain { span, msg: fluent::infer_compare_impl_item_obligation }
|
||||
.add_to_diag(err);
|
||||
}
|
||||
infer::CheckAssociatedTypeBounds { ref parent, .. } => {
|
||||
self.note_region_origin(err, parent);
|
||||
}
|
||||
infer::AscribeUserTypeProvePredicate(span) => {
|
||||
RegionOriginNote::Plain {
|
||||
span,
|
||||
msg: fluent::infer_ascribe_user_type_prove_predicate,
|
||||
}
|
||||
.add_to_diag(err);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn report_concrete_failure(
|
||||
&self,
|
||||
generic_param_scope: LocalDefId,
|
||||
origin: SubregionOrigin<'tcx>,
|
||||
sub: Region<'tcx>,
|
||||
sup: Region<'tcx>,
|
||||
) -> Diag<'a> {
|
||||
let mut err = match origin {
|
||||
infer::Subtype(box trace) => {
|
||||
let terr = TypeError::RegionsDoesNotOutlive(sup, sub);
|
||||
let mut err = self.report_and_explain_type_error(trace, terr);
|
||||
match (*sub, *sup) {
|
||||
(ty::RePlaceholder(_), ty::RePlaceholder(_)) => {}
|
||||
(ty::RePlaceholder(_), _) => {
|
||||
note_and_explain_region(
|
||||
self.tcx,
|
||||
&mut err,
|
||||
generic_param_scope,
|
||||
"",
|
||||
sup,
|
||||
" doesn't meet the lifetime requirements",
|
||||
None,
|
||||
);
|
||||
}
|
||||
(_, ty::RePlaceholder(_)) => {
|
||||
note_and_explain_region(
|
||||
self.tcx,
|
||||
&mut err,
|
||||
generic_param_scope,
|
||||
"the required lifetime does not necessarily outlive ",
|
||||
sub,
|
||||
"",
|
||||
None,
|
||||
);
|
||||
}
|
||||
_ => {
|
||||
note_and_explain_region(
|
||||
self.tcx,
|
||||
&mut err,
|
||||
generic_param_scope,
|
||||
"",
|
||||
sup,
|
||||
"...",
|
||||
None,
|
||||
);
|
||||
note_and_explain_region(
|
||||
self.tcx,
|
||||
&mut err,
|
||||
generic_param_scope,
|
||||
"...does not necessarily outlive ",
|
||||
sub,
|
||||
"",
|
||||
None,
|
||||
);
|
||||
}
|
||||
}
|
||||
err
|
||||
}
|
||||
infer::Reborrow(span) => {
|
||||
let reference_valid = note_and_explain::RegionExplanation::new(
|
||||
self.tcx,
|
||||
generic_param_scope,
|
||||
sub,
|
||||
None,
|
||||
note_and_explain::PrefixKind::RefValidFor,
|
||||
note_and_explain::SuffixKind::Continues,
|
||||
);
|
||||
let content_valid = note_and_explain::RegionExplanation::new(
|
||||
self.tcx,
|
||||
generic_param_scope,
|
||||
sup,
|
||||
None,
|
||||
note_and_explain::PrefixKind::ContentValidFor,
|
||||
note_and_explain::SuffixKind::Empty,
|
||||
);
|
||||
self.dcx().create_err(OutlivesContent {
|
||||
span,
|
||||
notes: reference_valid.into_iter().chain(content_valid).collect(),
|
||||
})
|
||||
}
|
||||
infer::RelateObjectBound(span) => {
|
||||
let object_valid = note_and_explain::RegionExplanation::new(
|
||||
self.tcx,
|
||||
generic_param_scope,
|
||||
sub,
|
||||
None,
|
||||
note_and_explain::PrefixKind::TypeObjValidFor,
|
||||
note_and_explain::SuffixKind::Empty,
|
||||
);
|
||||
let pointer_valid = note_and_explain::RegionExplanation::new(
|
||||
self.tcx,
|
||||
generic_param_scope,
|
||||
sup,
|
||||
None,
|
||||
note_and_explain::PrefixKind::SourcePointerValidFor,
|
||||
note_and_explain::SuffixKind::Empty,
|
||||
);
|
||||
self.dcx().create_err(OutlivesBound {
|
||||
span,
|
||||
notes: object_valid.into_iter().chain(pointer_valid).collect(),
|
||||
})
|
||||
}
|
||||
infer::RelateParamBound(span, ty, opt_span) => {
|
||||
let prefix = match *sub {
|
||||
ty::ReStatic => note_and_explain::PrefixKind::TypeSatisfy,
|
||||
_ => note_and_explain::PrefixKind::TypeOutlive,
|
||||
};
|
||||
let suffix = if opt_span.is_some() {
|
||||
note_and_explain::SuffixKind::ReqByBinding
|
||||
} else {
|
||||
note_and_explain::SuffixKind::Empty
|
||||
};
|
||||
let note = note_and_explain::RegionExplanation::new(
|
||||
self.tcx,
|
||||
generic_param_scope,
|
||||
sub,
|
||||
opt_span,
|
||||
prefix,
|
||||
suffix,
|
||||
);
|
||||
self.dcx().create_err(FulfillReqLifetime {
|
||||
span,
|
||||
ty: self.resolve_vars_if_possible(ty),
|
||||
note,
|
||||
})
|
||||
}
|
||||
infer::RelateRegionParamBound(span) => {
|
||||
let param_instantiated = note_and_explain::RegionExplanation::new(
|
||||
self.tcx,
|
||||
generic_param_scope,
|
||||
sup,
|
||||
None,
|
||||
note_and_explain::PrefixKind::LfParamInstantiatedWith,
|
||||
note_and_explain::SuffixKind::Empty,
|
||||
);
|
||||
let param_must_outlive = note_and_explain::RegionExplanation::new(
|
||||
self.tcx,
|
||||
generic_param_scope,
|
||||
sub,
|
||||
None,
|
||||
note_and_explain::PrefixKind::LfParamMustOutlive,
|
||||
note_and_explain::SuffixKind::Empty,
|
||||
);
|
||||
self.dcx().create_err(LfBoundNotSatisfied {
|
||||
span,
|
||||
notes: param_instantiated.into_iter().chain(param_must_outlive).collect(),
|
||||
})
|
||||
}
|
||||
infer::ReferenceOutlivesReferent(ty, span) => {
|
||||
let pointer_valid = note_and_explain::RegionExplanation::new(
|
||||
self.tcx,
|
||||
generic_param_scope,
|
||||
sub,
|
||||
None,
|
||||
note_and_explain::PrefixKind::PointerValidFor,
|
||||
note_and_explain::SuffixKind::Empty,
|
||||
);
|
||||
let data_valid = note_and_explain::RegionExplanation::new(
|
||||
self.tcx,
|
||||
generic_param_scope,
|
||||
sup,
|
||||
None,
|
||||
note_and_explain::PrefixKind::DataValidFor,
|
||||
note_and_explain::SuffixKind::Empty,
|
||||
);
|
||||
self.dcx().create_err(RefLongerThanData {
|
||||
span,
|
||||
ty: self.resolve_vars_if_possible(ty),
|
||||
notes: pointer_valid.into_iter().chain(data_valid).collect(),
|
||||
})
|
||||
}
|
||||
infer::CompareImplItemObligation { span, impl_item_def_id, trait_item_def_id } => {
|
||||
let mut err = self.infcx.report_extra_impl_obligation(
|
||||
span,
|
||||
impl_item_def_id,
|
||||
trait_item_def_id,
|
||||
&format!("`{sup}: {sub}`"),
|
||||
);
|
||||
// We should only suggest rewriting the `where` clause if the predicate is within that `where` clause
|
||||
if let Some(generics) = self.tcx.hir().get_generics(impl_item_def_id)
|
||||
&& generics.where_clause_span.contains(span)
|
||||
{
|
||||
self.suggest_copy_trait_method_bounds(
|
||||
trait_item_def_id,
|
||||
impl_item_def_id,
|
||||
&mut err,
|
||||
);
|
||||
}
|
||||
err
|
||||
}
|
||||
infer::CheckAssociatedTypeBounds { impl_item_def_id, trait_item_def_id, parent } => {
|
||||
let mut err = self.report_concrete_failure(generic_param_scope, *parent, sub, sup);
|
||||
|
||||
// Don't mention the item name if it's an RPITIT, since that'll just confuse
|
||||
// folks.
|
||||
if !self.tcx.is_impl_trait_in_trait(impl_item_def_id.to_def_id()) {
|
||||
let trait_item_span = self.tcx.def_span(trait_item_def_id);
|
||||
let item_name = self.tcx.item_name(impl_item_def_id.to_def_id());
|
||||
err.span_label(
|
||||
trait_item_span,
|
||||
format!("definition of `{item_name}` from trait"),
|
||||
);
|
||||
}
|
||||
|
||||
self.suggest_copy_trait_method_bounds(
|
||||
trait_item_def_id,
|
||||
impl_item_def_id,
|
||||
&mut err,
|
||||
);
|
||||
err
|
||||
}
|
||||
infer::AscribeUserTypeProvePredicate(span) => {
|
||||
let instantiated = note_and_explain::RegionExplanation::new(
|
||||
self.tcx,
|
||||
generic_param_scope,
|
||||
sup,
|
||||
None,
|
||||
note_and_explain::PrefixKind::LfInstantiatedWith,
|
||||
note_and_explain::SuffixKind::Empty,
|
||||
);
|
||||
let must_outlive = note_and_explain::RegionExplanation::new(
|
||||
self.tcx,
|
||||
generic_param_scope,
|
||||
sub,
|
||||
None,
|
||||
note_and_explain::PrefixKind::LfMustOutlive,
|
||||
note_and_explain::SuffixKind::Empty,
|
||||
);
|
||||
self.dcx().create_err(LfBoundNotSatisfied {
|
||||
span,
|
||||
notes: instantiated.into_iter().chain(must_outlive).collect(),
|
||||
})
|
||||
}
|
||||
};
|
||||
if sub.is_error() || sup.is_error() {
|
||||
err.downgrade_to_delayed_bug();
|
||||
}
|
||||
err
|
||||
}
|
||||
|
||||
pub fn suggest_copy_trait_method_bounds(
|
||||
&self,
|
||||
trait_item_def_id: DefId,
|
||||
impl_item_def_id: LocalDefId,
|
||||
err: &mut Diag<'_>,
|
||||
) {
|
||||
// FIXME(compiler-errors): Right now this is only being used for region
|
||||
// predicate mismatches. Ideally, we'd use it for *all* predicate mismatches,
|
||||
// but right now it's not really very smart when it comes to implicit `Sized`
|
||||
// predicates and bounds on the trait itself.
|
||||
|
||||
let Some(impl_def_id) = self.tcx.associated_item(impl_item_def_id).impl_container(self.tcx)
|
||||
else {
|
||||
return;
|
||||
};
|
||||
let Some(trait_ref) = self.tcx.impl_trait_ref(impl_def_id) else {
|
||||
return;
|
||||
};
|
||||
let trait_args = trait_ref
|
||||
.instantiate_identity()
|
||||
// Replace the explicit self type with `Self` for better suggestion rendering
|
||||
.with_self_ty(self.tcx, Ty::new_param(self.tcx, 0, kw::SelfUpper))
|
||||
.args;
|
||||
let trait_item_args = ty::GenericArgs::identity_for_item(self.tcx, impl_item_def_id)
|
||||
.rebase_onto(self.tcx, impl_def_id, trait_args);
|
||||
|
||||
let Ok(trait_predicates) =
|
||||
self.tcx
|
||||
.explicit_predicates_of(trait_item_def_id)
|
||||
.instantiate_own(self.tcx, trait_item_args)
|
||||
.map(|(pred, _)| {
|
||||
if pred.is_suggestable(self.tcx, false) {
|
||||
Ok(pred.to_string())
|
||||
} else {
|
||||
Err(())
|
||||
}
|
||||
})
|
||||
.collect::<Result<Vec<_>, ()>>()
|
||||
else {
|
||||
return;
|
||||
};
|
||||
|
||||
let Some(generics) = self.tcx.hir().get_generics(impl_item_def_id) else {
|
||||
return;
|
||||
};
|
||||
|
||||
let suggestion = if trait_predicates.is_empty() {
|
||||
WhereClauseSuggestions::Remove { span: generics.where_clause_span }
|
||||
} else {
|
||||
let space = if generics.where_clause_span.is_empty() { " " } else { "" };
|
||||
WhereClauseSuggestions::CopyPredicates {
|
||||
span: generics.where_clause_span,
|
||||
space,
|
||||
trait_predicates: trait_predicates.join(", "),
|
||||
}
|
||||
};
|
||||
err.subdiagnostic(suggestion);
|
||||
}
|
||||
|
||||
pub(super) fn report_placeholder_failure(
|
||||
&self,
|
||||
generic_param_scope: LocalDefId,
|
||||
placeholder_origin: SubregionOrigin<'tcx>,
|
||||
sub: Region<'tcx>,
|
||||
sup: Region<'tcx>,
|
||||
) -> Diag<'a> {
|
||||
// I can't think how to do better than this right now. -nikomatsakis
|
||||
debug!(?placeholder_origin, ?sub, ?sup, "report_placeholder_failure");
|
||||
match placeholder_origin {
|
||||
infer::Subtype(box ref trace)
|
||||
if matches!(
|
||||
&trace.cause.code().peel_derives(),
|
||||
ObligationCauseCode::WhereClause(..)
|
||||
| ObligationCauseCode::WhereClauseInExpr(..)
|
||||
) =>
|
||||
{
|
||||
// Hack to get around the borrow checker because trace.cause has an `Rc`.
|
||||
if let ObligationCauseCode::WhereClause(_, span)
|
||||
| ObligationCauseCode::WhereClauseInExpr(_, span, ..) =
|
||||
&trace.cause.code().peel_derives()
|
||||
&& !span.is_dummy()
|
||||
{
|
||||
let span = *span;
|
||||
self.report_concrete_failure(generic_param_scope, placeholder_origin, sub, sup)
|
||||
.with_span_note(span, "the lifetime requirement is introduced here")
|
||||
} else {
|
||||
unreachable!(
|
||||
"control flow ensures we have a `BindingObligation` or `WhereClauseInExpr` here..."
|
||||
)
|
||||
}
|
||||
}
|
||||
infer::Subtype(box trace) => {
|
||||
let terr = TypeError::RegionsPlaceholderMismatch;
|
||||
return self.report_and_explain_type_error(trace, terr);
|
||||
}
|
||||
_ => {
|
||||
return self.report_concrete_failure(
|
||||
generic_param_scope,
|
||||
placeholder_origin,
|
||||
sub,
|
||||
sup,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,940 +0,0 @@
|
|||
use super::TypeErrCtxt;
|
||||
use rustc_errors::Applicability::{MachineApplicable, MaybeIncorrect};
|
||||
use rustc_errors::{pluralize, Diag, MultiSpan};
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::def::DefKind;
|
||||
use rustc_middle::traits::ObligationCauseCode;
|
||||
use rustc_middle::ty::error::ExpectedFound;
|
||||
use rustc_middle::ty::print::Printer;
|
||||
use rustc_middle::{
|
||||
traits::ObligationCause,
|
||||
ty::{self, error::TypeError, print::FmtPrinter, suggest_constraining_type_param, Ty},
|
||||
};
|
||||
use rustc_span::{def_id::DefId, sym, BytePos, Span, Symbol};
|
||||
|
||||
impl<'tcx> TypeErrCtxt<'_, 'tcx> {
|
||||
pub fn note_and_explain_type_err(
|
||||
&self,
|
||||
diag: &mut Diag<'_>,
|
||||
err: TypeError<'tcx>,
|
||||
cause: &ObligationCause<'tcx>,
|
||||
sp: Span,
|
||||
body_owner_def_id: DefId,
|
||||
) {
|
||||
debug!("note_and_explain_type_err err={:?} cause={:?}", err, cause);
|
||||
|
||||
let tcx = self.tcx;
|
||||
|
||||
match err {
|
||||
TypeError::ArgumentSorts(values, _) | TypeError::Sorts(values) => {
|
||||
match (*values.expected.kind(), *values.found.kind()) {
|
||||
(ty::Closure(..), ty::Closure(..)) => {
|
||||
diag.note("no two closures, even if identical, have the same type");
|
||||
diag.help("consider boxing your closure and/or using it as a trait object");
|
||||
}
|
||||
(ty::Coroutine(def_id1, ..), ty::Coroutine(def_id2, ..))
|
||||
if self.tcx.coroutine_is_async(def_id1)
|
||||
&& self.tcx.coroutine_is_async(def_id2) =>
|
||||
{
|
||||
diag.note("no two async blocks, even if identical, have the same type");
|
||||
diag.help(
|
||||
"consider pinning your async block and casting it to a trait object",
|
||||
);
|
||||
}
|
||||
(ty::Alias(ty::Opaque, ..), ty::Alias(ty::Opaque, ..)) => {
|
||||
// Issue #63167
|
||||
diag.note("distinct uses of `impl Trait` result in different opaque types");
|
||||
}
|
||||
(ty::Float(_), ty::Infer(ty::IntVar(_)))
|
||||
if let Ok(
|
||||
// Issue #53280
|
||||
snippet,
|
||||
) = tcx.sess.source_map().span_to_snippet(sp) =>
|
||||
{
|
||||
if snippet.chars().all(|c| c.is_digit(10) || c == '-' || c == '_') {
|
||||
diag.span_suggestion_verbose(
|
||||
sp.shrink_to_hi(),
|
||||
"use a float literal",
|
||||
".0",
|
||||
MachineApplicable,
|
||||
);
|
||||
}
|
||||
}
|
||||
(ty::Param(expected), ty::Param(found)) => {
|
||||
let generics = tcx.generics_of(body_owner_def_id);
|
||||
let e_span = tcx.def_span(generics.type_param(expected, tcx).def_id);
|
||||
if !sp.contains(e_span) {
|
||||
diag.span_label(e_span, "expected type parameter");
|
||||
}
|
||||
let f_span = tcx.def_span(generics.type_param(found, tcx).def_id);
|
||||
if !sp.contains(f_span) {
|
||||
diag.span_label(f_span, "found type parameter");
|
||||
}
|
||||
diag.note(
|
||||
"a type parameter was expected, but a different one was found; \
|
||||
you might be missing a type parameter or trait bound",
|
||||
);
|
||||
diag.note(
|
||||
"for more information, visit \
|
||||
https://doc.rust-lang.org/book/ch10-02-traits.html\
|
||||
#traits-as-parameters",
|
||||
);
|
||||
}
|
||||
(
|
||||
ty::Alias(ty::Projection | ty::Inherent, _),
|
||||
ty::Alias(ty::Projection | ty::Inherent, _),
|
||||
) => {
|
||||
diag.note("an associated type was expected, but a different one was found");
|
||||
}
|
||||
// FIXME(inherent_associated_types): Extend this to support `ty::Inherent`, too.
|
||||
(ty::Param(p), ty::Alias(ty::Projection, proj))
|
||||
| (ty::Alias(ty::Projection, proj), ty::Param(p))
|
||||
if !tcx.is_impl_trait_in_trait(proj.def_id) =>
|
||||
{
|
||||
let param = tcx.generics_of(body_owner_def_id).type_param(p, tcx);
|
||||
let p_def_id = param.def_id;
|
||||
let p_span = tcx.def_span(p_def_id);
|
||||
let expected = match (values.expected.kind(), values.found.kind()) {
|
||||
(ty::Param(_), _) => "expected ",
|
||||
(_, ty::Param(_)) => "found ",
|
||||
_ => "",
|
||||
};
|
||||
if !sp.contains(p_span) {
|
||||
diag.span_label(p_span, format!("{expected}this type parameter"));
|
||||
}
|
||||
let parent = p_def_id.as_local().and_then(|id| {
|
||||
let local_id = tcx.local_def_id_to_hir_id(id);
|
||||
let generics = tcx.parent_hir_node(local_id).generics()?;
|
||||
Some((id, generics))
|
||||
});
|
||||
let mut note = true;
|
||||
if let Some((local_id, generics)) = parent {
|
||||
// Synthesize the associated type restriction `Add<Output = Expected>`.
|
||||
// FIXME: extract this logic for use in other diagnostics.
|
||||
let (trait_ref, assoc_args) = proj.trait_ref_and_own_args(tcx);
|
||||
let item_name = tcx.item_name(proj.def_id);
|
||||
let item_args = self.format_generic_args(assoc_args);
|
||||
|
||||
// Here, we try to see if there's an existing
|
||||
// trait implementation that matches the one that
|
||||
// we're suggesting to restrict. If so, find the
|
||||
// "end", whether it be at the end of the trait
|
||||
// or the end of the generic arguments.
|
||||
let mut matching_span = None;
|
||||
let mut matched_end_of_args = false;
|
||||
for bound in generics.bounds_for_param(local_id) {
|
||||
let potential_spans = bound.bounds.iter().find_map(|bound| {
|
||||
let bound_trait_path = bound.trait_ref()?.path;
|
||||
let def_id = bound_trait_path.res.opt_def_id()?;
|
||||
let generic_args = bound_trait_path
|
||||
.segments
|
||||
.iter()
|
||||
.last()
|
||||
.map(|path| path.args());
|
||||
(def_id == trait_ref.def_id)
|
||||
.then_some((bound_trait_path.span, generic_args))
|
||||
});
|
||||
|
||||
if let Some((end_of_trait, end_of_args)) = potential_spans {
|
||||
let args_span = end_of_args.and_then(|args| args.span());
|
||||
matched_end_of_args = args_span.is_some();
|
||||
matching_span = args_span
|
||||
.or_else(|| Some(end_of_trait))
|
||||
.map(|span| span.shrink_to_hi());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if matched_end_of_args {
|
||||
// Append suggestion to the end of our args
|
||||
let path = format!(", {item_name}{item_args} = {p}");
|
||||
note = !suggest_constraining_type_param(
|
||||
tcx,
|
||||
generics,
|
||||
diag,
|
||||
&proj.self_ty().to_string(),
|
||||
&path,
|
||||
None,
|
||||
matching_span,
|
||||
);
|
||||
} else {
|
||||
// Suggest adding a bound to an existing trait
|
||||
// or if the trait doesn't exist, add the trait
|
||||
// and the suggested bounds.
|
||||
let path = format!("<{item_name}{item_args} = {p}>");
|
||||
note = !suggest_constraining_type_param(
|
||||
tcx,
|
||||
generics,
|
||||
diag,
|
||||
&proj.self_ty().to_string(),
|
||||
&path,
|
||||
None,
|
||||
matching_span,
|
||||
);
|
||||
}
|
||||
}
|
||||
if note {
|
||||
diag.note("you might be missing a type parameter or trait bound");
|
||||
}
|
||||
}
|
||||
(ty::Param(p), ty::Dynamic(..) | ty::Alias(ty::Opaque, ..))
|
||||
| (ty::Dynamic(..) | ty::Alias(ty::Opaque, ..), ty::Param(p)) => {
|
||||
let generics = tcx.generics_of(body_owner_def_id);
|
||||
let p_span = tcx.def_span(generics.type_param(p, tcx).def_id);
|
||||
let expected = match (values.expected.kind(), values.found.kind()) {
|
||||
(ty::Param(_), _) => "expected ",
|
||||
(_, ty::Param(_)) => "found ",
|
||||
_ => "",
|
||||
};
|
||||
if !sp.contains(p_span) {
|
||||
diag.span_label(p_span, format!("{expected}this type parameter"));
|
||||
}
|
||||
diag.help("type parameters must be constrained to match other types");
|
||||
if diag.code.is_some_and(|code| tcx.sess.teach(code)) {
|
||||
diag.help(
|
||||
"given a type parameter `T` and a method `foo`:
|
||||
```
|
||||
trait Trait<T> { fn foo(&self) -> T; }
|
||||
```
|
||||
the only ways to implement method `foo` are:
|
||||
- constrain `T` with an explicit type:
|
||||
```
|
||||
impl Trait<String> for X {
|
||||
fn foo(&self) -> String { String::new() }
|
||||
}
|
||||
```
|
||||
- add a trait bound to `T` and call a method on that trait that returns `Self`:
|
||||
```
|
||||
impl<T: std::default::Default> Trait<T> for X {
|
||||
fn foo(&self) -> T { <T as std::default::Default>::default() }
|
||||
}
|
||||
```
|
||||
- change `foo` to return an argument of type `T`:
|
||||
```
|
||||
impl<T> Trait<T> for X {
|
||||
fn foo(&self, x: T) -> T { x }
|
||||
}
|
||||
```",
|
||||
);
|
||||
}
|
||||
diag.note(
|
||||
"for more information, visit \
|
||||
https://doc.rust-lang.org/book/ch10-02-traits.html\
|
||||
#traits-as-parameters",
|
||||
);
|
||||
}
|
||||
(
|
||||
ty::Param(p),
|
||||
ty::Closure(..) | ty::CoroutineClosure(..) | ty::Coroutine(..),
|
||||
) => {
|
||||
let generics = tcx.generics_of(body_owner_def_id);
|
||||
let p_span = tcx.def_span(generics.type_param(p, tcx).def_id);
|
||||
if !sp.contains(p_span) {
|
||||
diag.span_label(p_span, "expected this type parameter");
|
||||
}
|
||||
diag.help(format!(
|
||||
"every closure has a distinct type and so could not always match the \
|
||||
caller-chosen type of parameter `{p}`"
|
||||
));
|
||||
}
|
||||
(ty::Param(p), _) | (_, ty::Param(p)) => {
|
||||
let generics = tcx.generics_of(body_owner_def_id);
|
||||
let p_span = tcx.def_span(generics.type_param(p, tcx).def_id);
|
||||
let expected = match (values.expected.kind(), values.found.kind()) {
|
||||
(ty::Param(_), _) => "expected ",
|
||||
(_, ty::Param(_)) => "found ",
|
||||
_ => "",
|
||||
};
|
||||
if !sp.contains(p_span) {
|
||||
diag.span_label(p_span, format!("{expected}this type parameter"));
|
||||
}
|
||||
}
|
||||
(ty::Alias(ty::Projection | ty::Inherent, proj_ty), _)
|
||||
if !tcx.is_impl_trait_in_trait(proj_ty.def_id) =>
|
||||
{
|
||||
self.expected_projection(
|
||||
diag,
|
||||
proj_ty,
|
||||
values,
|
||||
body_owner_def_id,
|
||||
cause.code(),
|
||||
);
|
||||
}
|
||||
(_, ty::Alias(ty::Projection | ty::Inherent, proj_ty))
|
||||
if !tcx.is_impl_trait_in_trait(proj_ty.def_id) =>
|
||||
{
|
||||
let msg = || {
|
||||
format!(
|
||||
"consider constraining the associated type `{}` to `{}`",
|
||||
values.found, values.expected,
|
||||
)
|
||||
};
|
||||
if !(self.suggest_constraining_opaque_associated_type(
|
||||
diag,
|
||||
msg,
|
||||
proj_ty,
|
||||
values.expected,
|
||||
) || self.suggest_constraint(
|
||||
diag,
|
||||
&msg,
|
||||
body_owner_def_id,
|
||||
proj_ty,
|
||||
values.expected,
|
||||
)) {
|
||||
diag.help(msg());
|
||||
diag.note(
|
||||
"for more information, visit \
|
||||
https://doc.rust-lang.org/book/ch19-03-advanced-traits.html",
|
||||
);
|
||||
}
|
||||
}
|
||||
(ty::Dynamic(t, _, ty::DynKind::Dyn), ty::Alias(ty::Opaque, alias))
|
||||
if let Some(def_id) = t.principal_def_id()
|
||||
&& tcx
|
||||
.explicit_item_super_predicates(alias.def_id)
|
||||
.skip_binder()
|
||||
.iter()
|
||||
.any(|(pred, _span)| match pred.kind().skip_binder() {
|
||||
ty::ClauseKind::Trait(trait_predicate)
|
||||
if trait_predicate.polarity
|
||||
== ty::PredicatePolarity::Positive =>
|
||||
{
|
||||
trait_predicate.def_id() == def_id
|
||||
}
|
||||
_ => false,
|
||||
}) =>
|
||||
{
|
||||
diag.help(format!(
|
||||
"you can box the `{}` to coerce it to `Box<{}>`, but you'll have to \
|
||||
change the expected type as well",
|
||||
values.found, values.expected,
|
||||
));
|
||||
}
|
||||
(ty::Dynamic(t, _, ty::DynKind::Dyn), _)
|
||||
if let Some(def_id) = t.principal_def_id() =>
|
||||
{
|
||||
let mut impl_def_ids = vec![];
|
||||
tcx.for_each_relevant_impl(def_id, values.found, |did| {
|
||||
impl_def_ids.push(did)
|
||||
});
|
||||
if let [_] = &impl_def_ids[..] {
|
||||
let trait_name = tcx.item_name(def_id);
|
||||
diag.help(format!(
|
||||
"`{}` implements `{trait_name}` so you could box the found value \
|
||||
and coerce it to the trait object `Box<dyn {trait_name}>`, you \
|
||||
will have to change the expected type as well",
|
||||
values.found,
|
||||
));
|
||||
}
|
||||
}
|
||||
(_, ty::Dynamic(t, _, ty::DynKind::Dyn))
|
||||
if let Some(def_id) = t.principal_def_id() =>
|
||||
{
|
||||
let mut impl_def_ids = vec![];
|
||||
tcx.for_each_relevant_impl(def_id, values.expected, |did| {
|
||||
impl_def_ids.push(did)
|
||||
});
|
||||
if let [_] = &impl_def_ids[..] {
|
||||
let trait_name = tcx.item_name(def_id);
|
||||
diag.help(format!(
|
||||
"`{}` implements `{trait_name}` so you could change the expected \
|
||||
type to `Box<dyn {trait_name}>`",
|
||||
values.expected,
|
||||
));
|
||||
}
|
||||
}
|
||||
(ty::Dynamic(t, _, ty::DynKind::DynStar), _)
|
||||
if let Some(def_id) = t.principal_def_id() =>
|
||||
{
|
||||
let mut impl_def_ids = vec![];
|
||||
tcx.for_each_relevant_impl(def_id, values.found, |did| {
|
||||
impl_def_ids.push(did)
|
||||
});
|
||||
if let [_] = &impl_def_ids[..] {
|
||||
let trait_name = tcx.item_name(def_id);
|
||||
diag.help(format!(
|
||||
"`{}` implements `{trait_name}`, `#[feature(dyn_star)]` is likely \
|
||||
not enabled; that feature it is currently incomplete",
|
||||
values.found,
|
||||
));
|
||||
}
|
||||
}
|
||||
(_, ty::Alias(ty::Opaque, opaque_ty))
|
||||
| (ty::Alias(ty::Opaque, opaque_ty), _) => {
|
||||
if opaque_ty.def_id.is_local()
|
||||
&& matches!(
|
||||
tcx.def_kind(body_owner_def_id),
|
||||
DefKind::Fn
|
||||
| DefKind::Static { .. }
|
||||
| DefKind::Const
|
||||
| DefKind::AssocFn
|
||||
| DefKind::AssocConst
|
||||
)
|
||||
&& tcx.is_type_alias_impl_trait(opaque_ty.def_id)
|
||||
&& !tcx
|
||||
.opaque_types_defined_by(body_owner_def_id.expect_local())
|
||||
.contains(&opaque_ty.def_id.expect_local())
|
||||
{
|
||||
let sp = tcx
|
||||
.def_ident_span(body_owner_def_id)
|
||||
.unwrap_or_else(|| tcx.def_span(body_owner_def_id));
|
||||
diag.span_note(
|
||||
sp,
|
||||
"this item must have the opaque type in its signature in order to \
|
||||
be able to register hidden types",
|
||||
);
|
||||
}
|
||||
// If two if arms can be coerced to a trait object, provide a structured
|
||||
// suggestion.
|
||||
let ObligationCauseCode::IfExpression(cause) = cause.code() else {
|
||||
return;
|
||||
};
|
||||
let hir::Node::Block(blk) = self.tcx.hir_node(cause.then_id) else {
|
||||
return;
|
||||
};
|
||||
let Some(then) = blk.expr else {
|
||||
return;
|
||||
};
|
||||
let hir::Node::Block(blk) = self.tcx.hir_node(cause.else_id) else {
|
||||
return;
|
||||
};
|
||||
let Some(else_) = blk.expr else {
|
||||
return;
|
||||
};
|
||||
let expected = match values.found.kind() {
|
||||
ty::Alias(..) => values.expected,
|
||||
_ => values.found,
|
||||
};
|
||||
let preds = tcx.explicit_item_super_predicates(opaque_ty.def_id);
|
||||
for (pred, _span) in preds.skip_binder() {
|
||||
let ty::ClauseKind::Trait(trait_predicate) = pred.kind().skip_binder()
|
||||
else {
|
||||
continue;
|
||||
};
|
||||
if trait_predicate.polarity != ty::PredicatePolarity::Positive {
|
||||
continue;
|
||||
}
|
||||
let def_id = trait_predicate.def_id();
|
||||
let mut impl_def_ids = vec![];
|
||||
tcx.for_each_relevant_impl(def_id, expected, |did| {
|
||||
impl_def_ids.push(did)
|
||||
});
|
||||
if let [_] = &impl_def_ids[..] {
|
||||
let trait_name = tcx.item_name(def_id);
|
||||
diag.multipart_suggestion(
|
||||
format!(
|
||||
"`{expected}` implements `{trait_name}` so you can box \
|
||||
both arms and coerce to the trait object \
|
||||
`Box<dyn {trait_name}>`",
|
||||
),
|
||||
vec![
|
||||
(then.span.shrink_to_lo(), "Box::new(".to_string()),
|
||||
(
|
||||
then.span.shrink_to_hi(),
|
||||
format!(") as Box<dyn {}>", tcx.def_path_str(def_id)),
|
||||
),
|
||||
(else_.span.shrink_to_lo(), "Box::new(".to_string()),
|
||||
(else_.span.shrink_to_hi(), ")".to_string()),
|
||||
],
|
||||
MachineApplicable,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
(ty::FnPtr(sig), ty::FnDef(def_id, _))
|
||||
| (ty::FnDef(def_id, _), ty::FnPtr(sig)) => {
|
||||
if tcx.fn_sig(def_id).skip_binder().safety() < sig.safety() {
|
||||
diag.note(
|
||||
"unsafe functions cannot be coerced into safe function pointers",
|
||||
);
|
||||
}
|
||||
}
|
||||
(ty::Adt(_, _), ty::Adt(def, args))
|
||||
if let ObligationCauseCode::IfExpression(cause) = cause.code()
|
||||
&& let hir::Node::Block(blk) = self.tcx.hir_node(cause.then_id)
|
||||
&& let Some(then) = blk.expr
|
||||
&& def.is_box()
|
||||
&& let boxed_ty = args.type_at(0)
|
||||
&& let ty::Dynamic(t, _, _) = boxed_ty.kind()
|
||||
&& let Some(def_id) = t.principal_def_id()
|
||||
&& let mut impl_def_ids = vec![]
|
||||
&& let _ =
|
||||
tcx.for_each_relevant_impl(def_id, values.expected, |did| {
|
||||
impl_def_ids.push(did)
|
||||
})
|
||||
&& let [_] = &impl_def_ids[..] =>
|
||||
{
|
||||
// We have divergent if/else arms where the expected value is a type that
|
||||
// implements the trait of the found boxed trait object.
|
||||
diag.multipart_suggestion(
|
||||
format!(
|
||||
"`{}` implements `{}` so you can box it to coerce to the trait \
|
||||
object `{}`",
|
||||
values.expected,
|
||||
tcx.item_name(def_id),
|
||||
values.found,
|
||||
),
|
||||
vec![
|
||||
(then.span.shrink_to_lo(), "Box::new(".to_string()),
|
||||
(then.span.shrink_to_hi(), ")".to_string()),
|
||||
],
|
||||
MachineApplicable,
|
||||
);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
debug!(
|
||||
"note_and_explain_type_err expected={:?} ({:?}) found={:?} ({:?})",
|
||||
values.expected,
|
||||
values.expected.kind(),
|
||||
values.found,
|
||||
values.found.kind(),
|
||||
);
|
||||
}
|
||||
TypeError::CyclicTy(ty) => {
|
||||
// Watch out for various cases of cyclic types and try to explain.
|
||||
if ty.is_closure() || ty.is_coroutine() || ty.is_coroutine_closure() {
|
||||
diag.note(
|
||||
"closures cannot capture themselves or take themselves as argument;\n\
|
||||
this error may be the result of a recent compiler bug-fix,\n\
|
||||
see issue #46062 <https://github.com/rust-lang/rust/issues/46062>\n\
|
||||
for more information",
|
||||
);
|
||||
}
|
||||
}
|
||||
TypeError::TargetFeatureCast(def_id) => {
|
||||
let target_spans = tcx.get_attrs(def_id, sym::target_feature).map(|attr| attr.span);
|
||||
diag.note(
|
||||
"functions with `#[target_feature]` can only be coerced to `unsafe` function pointers"
|
||||
);
|
||||
diag.span_labels(target_spans, "`#[target_feature]` added here");
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
fn suggest_constraint(
|
||||
&self,
|
||||
diag: &mut Diag<'_>,
|
||||
msg: impl Fn() -> String,
|
||||
body_owner_def_id: DefId,
|
||||
proj_ty: ty::AliasTy<'tcx>,
|
||||
ty: Ty<'tcx>,
|
||||
) -> bool {
|
||||
let tcx = self.tcx;
|
||||
let assoc = tcx.associated_item(proj_ty.def_id);
|
||||
let (trait_ref, assoc_args) = proj_ty.trait_ref_and_own_args(tcx);
|
||||
let Some(item) = tcx.hir().get_if_local(body_owner_def_id) else {
|
||||
return false;
|
||||
};
|
||||
let Some(hir_generics) = item.generics() else {
|
||||
return false;
|
||||
};
|
||||
// Get the `DefId` for the type parameter corresponding to `A` in `<A as T>::Foo`.
|
||||
// This will also work for `impl Trait`.
|
||||
let ty::Param(param_ty) = *proj_ty.self_ty().kind() else {
|
||||
return false;
|
||||
};
|
||||
let generics = tcx.generics_of(body_owner_def_id);
|
||||
let def_id = generics.type_param(param_ty, tcx).def_id;
|
||||
let Some(def_id) = def_id.as_local() else {
|
||||
return false;
|
||||
};
|
||||
|
||||
// First look in the `where` clause, as this might be
|
||||
// `fn foo<T>(x: T) where T: Trait`.
|
||||
for pred in hir_generics.bounds_for_param(def_id) {
|
||||
if self.constrain_generic_bound_associated_type_structured_suggestion(
|
||||
diag,
|
||||
trait_ref,
|
||||
pred.bounds,
|
||||
assoc,
|
||||
assoc_args,
|
||||
ty,
|
||||
&msg,
|
||||
false,
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (param_ty.index as usize) >= generics.parent_count {
|
||||
// The param comes from the current item, do not look at the parent. (#117209)
|
||||
return false;
|
||||
}
|
||||
// If associated item, look to constrain the params of the trait/impl.
|
||||
let hir_id = match item {
|
||||
hir::Node::ImplItem(item) => item.hir_id(),
|
||||
hir::Node::TraitItem(item) => item.hir_id(),
|
||||
_ => return false,
|
||||
};
|
||||
let parent = tcx.hir().get_parent_item(hir_id).def_id;
|
||||
self.suggest_constraint(diag, msg, parent.into(), proj_ty, ty)
|
||||
}
|
||||
|
||||
/// An associated type was expected and a different type was found.
|
||||
///
|
||||
/// We perform a few different checks to see what we can suggest:
|
||||
///
|
||||
/// - In the current item, look for associated functions that return the expected type and
|
||||
/// suggest calling them. (Not a structured suggestion.)
|
||||
/// - If any of the item's generic bounds can be constrained, we suggest constraining the
|
||||
/// associated type to the found type.
|
||||
/// - If the associated type has a default type and was expected inside of a `trait`, we
|
||||
/// mention that this is disallowed.
|
||||
/// - If all other things fail, and the error is not because of a mismatch between the `trait`
|
||||
/// and the `impl`, we provide a generic `help` to constrain the assoc type or call an assoc
|
||||
/// fn that returns the type.
|
||||
fn expected_projection(
|
||||
&self,
|
||||
diag: &mut Diag<'_>,
|
||||
proj_ty: ty::AliasTy<'tcx>,
|
||||
values: ExpectedFound<Ty<'tcx>>,
|
||||
body_owner_def_id: DefId,
|
||||
cause_code: &ObligationCauseCode<'_>,
|
||||
) {
|
||||
let tcx = self.tcx;
|
||||
|
||||
// Don't suggest constraining a projection to something containing itself
|
||||
if self.tcx.erase_regions(values.found).contains(self.tcx.erase_regions(values.expected)) {
|
||||
return;
|
||||
}
|
||||
|
||||
let msg = || {
|
||||
format!(
|
||||
"consider constraining the associated type `{}` to `{}`",
|
||||
values.expected, values.found
|
||||
)
|
||||
};
|
||||
|
||||
let body_owner = tcx.hir().get_if_local(body_owner_def_id);
|
||||
let current_method_ident = body_owner.and_then(|n| n.ident()).map(|i| i.name);
|
||||
|
||||
// We don't want to suggest calling an assoc fn in a scope where that isn't feasible.
|
||||
let callable_scope = matches!(
|
||||
body_owner,
|
||||
Some(
|
||||
hir::Node::Item(hir::Item { kind: hir::ItemKind::Fn(..), .. })
|
||||
| hir::Node::TraitItem(hir::TraitItem { kind: hir::TraitItemKind::Fn(..), .. })
|
||||
| hir::Node::ImplItem(hir::ImplItem { kind: hir::ImplItemKind::Fn(..), .. }),
|
||||
)
|
||||
);
|
||||
let impl_comparison = matches!(cause_code, ObligationCauseCode::CompareImplItem { .. });
|
||||
let assoc = tcx.associated_item(proj_ty.def_id);
|
||||
if impl_comparison {
|
||||
// We do not want to suggest calling functions when the reason of the
|
||||
// type error is a comparison of an `impl` with its `trait`.
|
||||
} else {
|
||||
let point_at_assoc_fn = if callable_scope
|
||||
&& self.point_at_methods_that_satisfy_associated_type(
|
||||
diag,
|
||||
assoc.container_id(tcx),
|
||||
current_method_ident,
|
||||
proj_ty.def_id,
|
||||
values.expected,
|
||||
) {
|
||||
// If we find a suitable associated function that returns the expected type, we
|
||||
// don't want the more general suggestion later in this method about "consider
|
||||
// constraining the associated type or calling a method that returns the associated
|
||||
// type".
|
||||
true
|
||||
} else {
|
||||
false
|
||||
};
|
||||
// Possibly suggest constraining the associated type to conform to the
|
||||
// found type.
|
||||
if self.suggest_constraint(diag, &msg, body_owner_def_id, proj_ty, values.found)
|
||||
|| point_at_assoc_fn
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
self.suggest_constraining_opaque_associated_type(diag, &msg, proj_ty, values.found);
|
||||
|
||||
if self.point_at_associated_type(diag, body_owner_def_id, values.found) {
|
||||
return;
|
||||
}
|
||||
|
||||
if !impl_comparison {
|
||||
// Generic suggestion when we can't be more specific.
|
||||
if callable_scope {
|
||||
diag.help(format!(
|
||||
"{} or calling a method that returns `{}`",
|
||||
msg(),
|
||||
values.expected
|
||||
));
|
||||
} else {
|
||||
diag.help(msg());
|
||||
}
|
||||
diag.note(
|
||||
"for more information, visit \
|
||||
https://doc.rust-lang.org/book/ch19-03-advanced-traits.html",
|
||||
);
|
||||
}
|
||||
if diag.code.is_some_and(|code| tcx.sess.teach(code)) {
|
||||
diag.help(
|
||||
"given an associated type `T` and a method `foo`:
|
||||
```
|
||||
trait Trait {
|
||||
type T;
|
||||
fn foo(&self) -> Self::T;
|
||||
}
|
||||
```
|
||||
the only way of implementing method `foo` is to constrain `T` with an explicit associated type:
|
||||
```
|
||||
impl Trait for X {
|
||||
type T = String;
|
||||
fn foo(&self) -> Self::T { String::new() }
|
||||
}
|
||||
```",
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// When the expected `impl Trait` is not defined in the current item, it will come from
|
||||
/// a return type. This can occur when dealing with `TryStream` (#71035).
|
||||
fn suggest_constraining_opaque_associated_type(
|
||||
&self,
|
||||
diag: &mut Diag<'_>,
|
||||
msg: impl Fn() -> String,
|
||||
proj_ty: ty::AliasTy<'tcx>,
|
||||
ty: Ty<'tcx>,
|
||||
) -> bool {
|
||||
let tcx = self.tcx;
|
||||
|
||||
let assoc = tcx.associated_item(proj_ty.def_id);
|
||||
if let ty::Alias(ty::Opaque, ty::AliasTy { def_id, .. }) = *proj_ty.self_ty().kind() {
|
||||
let opaque_local_def_id = def_id.as_local();
|
||||
let opaque_hir_ty = if let Some(opaque_local_def_id) = opaque_local_def_id {
|
||||
tcx.hir().expect_item(opaque_local_def_id).expect_opaque_ty()
|
||||
} else {
|
||||
return false;
|
||||
};
|
||||
|
||||
let (trait_ref, assoc_args) = proj_ty.trait_ref_and_own_args(tcx);
|
||||
|
||||
self.constrain_generic_bound_associated_type_structured_suggestion(
|
||||
diag,
|
||||
trait_ref,
|
||||
opaque_hir_ty.bounds,
|
||||
assoc,
|
||||
assoc_args,
|
||||
ty,
|
||||
msg,
|
||||
true,
|
||||
)
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
fn point_at_methods_that_satisfy_associated_type(
|
||||
&self,
|
||||
diag: &mut Diag<'_>,
|
||||
assoc_container_id: DefId,
|
||||
current_method_ident: Option<Symbol>,
|
||||
proj_ty_item_def_id: DefId,
|
||||
expected: Ty<'tcx>,
|
||||
) -> bool {
|
||||
let tcx = self.tcx;
|
||||
|
||||
let items = tcx.associated_items(assoc_container_id);
|
||||
// Find all the methods in the trait that could be called to construct the
|
||||
// expected associated type.
|
||||
// FIXME: consider suggesting the use of associated `const`s.
|
||||
let methods: Vec<(Span, String)> = items
|
||||
.in_definition_order()
|
||||
.filter(|item| {
|
||||
ty::AssocKind::Fn == item.kind
|
||||
&& Some(item.name) != current_method_ident
|
||||
&& !tcx.is_doc_hidden(item.def_id)
|
||||
})
|
||||
.filter_map(|item| {
|
||||
let method = tcx.fn_sig(item.def_id).instantiate_identity();
|
||||
match *method.output().skip_binder().kind() {
|
||||
ty::Alias(ty::Projection, ty::AliasTy { def_id: item_def_id, .. })
|
||||
if item_def_id == proj_ty_item_def_id =>
|
||||
{
|
||||
Some((
|
||||
tcx.def_span(item.def_id),
|
||||
format!("consider calling `{}`", tcx.def_path_str(item.def_id)),
|
||||
))
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
if !methods.is_empty() {
|
||||
// Use a single `help:` to show all the methods in the trait that can
|
||||
// be used to construct the expected associated type.
|
||||
let mut span: MultiSpan =
|
||||
methods.iter().map(|(sp, _)| *sp).collect::<Vec<Span>>().into();
|
||||
let msg = format!(
|
||||
"{some} method{s} {are} available that return{r} `{ty}`",
|
||||
some = if methods.len() == 1 { "a" } else { "some" },
|
||||
s = pluralize!(methods.len()),
|
||||
are = pluralize!("is", methods.len()),
|
||||
r = if methods.len() == 1 { "s" } else { "" },
|
||||
ty = expected
|
||||
);
|
||||
for (sp, label) in methods.into_iter() {
|
||||
span.push_span_label(sp, label);
|
||||
}
|
||||
diag.span_help(span, msg);
|
||||
return true;
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
fn point_at_associated_type(
|
||||
&self,
|
||||
diag: &mut Diag<'_>,
|
||||
body_owner_def_id: DefId,
|
||||
found: Ty<'tcx>,
|
||||
) -> bool {
|
||||
let tcx = self.tcx;
|
||||
|
||||
let Some(def_id) = body_owner_def_id.as_local() else {
|
||||
return false;
|
||||
};
|
||||
|
||||
// When `body_owner` is an `impl` or `trait` item, look in its associated types for
|
||||
// `expected` and point at it.
|
||||
let hir_id = tcx.local_def_id_to_hir_id(def_id);
|
||||
let parent_id = tcx.hir().get_parent_item(hir_id);
|
||||
let item = tcx.hir_node_by_def_id(parent_id.def_id);
|
||||
|
||||
debug!("expected_projection parent item {:?}", item);
|
||||
|
||||
let param_env = tcx.param_env(body_owner_def_id);
|
||||
|
||||
match item {
|
||||
hir::Node::Item(hir::Item { kind: hir::ItemKind::Trait(.., items), .. }) => {
|
||||
// FIXME: account for `#![feature(specialization)]`
|
||||
for item in &items[..] {
|
||||
match item.kind {
|
||||
hir::AssocItemKind::Type => {
|
||||
// FIXME: account for returning some type in a trait fn impl that has
|
||||
// an assoc type as a return type (#72076).
|
||||
if let hir::Defaultness::Default { has_value: true } =
|
||||
tcx.defaultness(item.id.owner_id)
|
||||
{
|
||||
let assoc_ty = tcx.type_of(item.id.owner_id).instantiate_identity();
|
||||
if self.infcx.can_eq_shallow(param_env, assoc_ty, found) {
|
||||
diag.span_label(
|
||||
item.span,
|
||||
"associated type defaults can't be assumed inside the \
|
||||
trait defining them",
|
||||
);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
hir::Node::Item(hir::Item {
|
||||
kind: hir::ItemKind::Impl(hir::Impl { items, .. }),
|
||||
..
|
||||
}) => {
|
||||
for item in &items[..] {
|
||||
if let hir::AssocItemKind::Type = item.kind {
|
||||
let assoc_ty = tcx.type_of(item.id.owner_id).instantiate_identity();
|
||||
if let hir::Defaultness::Default { has_value: true } =
|
||||
tcx.defaultness(item.id.owner_id)
|
||||
&& self.infcx.can_eq_shallow(param_env, assoc_ty, found)
|
||||
{
|
||||
diag.span_label(
|
||||
item.span,
|
||||
"associated type is `default` and may be overridden",
|
||||
);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
/// Given a slice of `hir::GenericBound`s, if any of them corresponds to the `trait_ref`
|
||||
/// requirement, provide a structured suggestion to constrain it to a given type `ty`.
|
||||
///
|
||||
/// `is_bound_surely_present` indicates whether we know the bound we're looking for is
|
||||
/// inside `bounds`. If that's the case then we can consider `bounds` containing only one
|
||||
/// trait bound as the one we're looking for. This can help in cases where the associated
|
||||
/// type is defined on a supertrait of the one present in the bounds.
|
||||
fn constrain_generic_bound_associated_type_structured_suggestion(
|
||||
&self,
|
||||
diag: &mut Diag<'_>,
|
||||
trait_ref: ty::TraitRef<'tcx>,
|
||||
bounds: hir::GenericBounds<'_>,
|
||||
assoc: ty::AssocItem,
|
||||
assoc_args: &[ty::GenericArg<'tcx>],
|
||||
ty: Ty<'tcx>,
|
||||
msg: impl Fn() -> String,
|
||||
is_bound_surely_present: bool,
|
||||
) -> bool {
|
||||
// FIXME: we would want to call `resolve_vars_if_possible` on `ty` before suggesting.
|
||||
|
||||
let trait_bounds = bounds.iter().filter_map(|bound| match bound {
|
||||
hir::GenericBound::Trait(ptr, hir::TraitBoundModifier::None) => Some(ptr),
|
||||
_ => None,
|
||||
});
|
||||
|
||||
let matching_trait_bounds = trait_bounds
|
||||
.clone()
|
||||
.filter(|ptr| ptr.trait_ref.trait_def_id() == Some(trait_ref.def_id))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let span = match &matching_trait_bounds[..] {
|
||||
&[ptr] => ptr.span,
|
||||
&[] if is_bound_surely_present => match &trait_bounds.collect::<Vec<_>>()[..] {
|
||||
&[ptr] => ptr.span,
|
||||
_ => return false,
|
||||
},
|
||||
_ => return false,
|
||||
};
|
||||
|
||||
self.constrain_associated_type_structured_suggestion(diag, span, assoc, assoc_args, ty, msg)
|
||||
}
|
||||
|
||||
/// Given a span corresponding to a bound, provide a structured suggestion to set an
|
||||
/// associated type to a given type `ty`.
|
||||
fn constrain_associated_type_structured_suggestion(
|
||||
&self,
|
||||
diag: &mut Diag<'_>,
|
||||
span: Span,
|
||||
assoc: ty::AssocItem,
|
||||
assoc_args: &[ty::GenericArg<'tcx>],
|
||||
ty: Ty<'tcx>,
|
||||
msg: impl Fn() -> String,
|
||||
) -> bool {
|
||||
let tcx = self.tcx;
|
||||
|
||||
if let Ok(has_params) =
|
||||
tcx.sess.source_map().span_to_snippet(span).map(|snippet| snippet.ends_with('>'))
|
||||
{
|
||||
let (span, sugg) = if has_params {
|
||||
let pos = span.hi() - BytePos(1);
|
||||
let span = Span::new(pos, pos, span.ctxt(), span.parent());
|
||||
(span, format!(", {} = {}", assoc.ident(tcx), ty))
|
||||
} else {
|
||||
let item_args = self.format_generic_args(assoc_args);
|
||||
(span.shrink_to_hi(), format!("<{}{} = {}>", assoc.ident(tcx), item_args, ty))
|
||||
};
|
||||
diag.span_suggestion_verbose(span, msg(), sugg, MaybeIncorrect);
|
||||
return true;
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
pub fn format_generic_args(&self, args: &[ty::GenericArg<'tcx>]) -> String {
|
||||
FmtPrinter::print_string(self.tcx, hir::def::Namespace::TypeNS, |cx| {
|
||||
cx.path_generic_args(|_| Ok(()), args)
|
||||
})
|
||||
.expect("could not write to `String`.")
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load diff
|
@ -1,81 +0,0 @@
|
|||
use rustc_data_structures::fx::FxHashMap;
|
||||
use rustc_data_structures::undo_log::NoUndo;
|
||||
use rustc_data_structures::unify as ut;
|
||||
use rustc_middle::ty;
|
||||
|
||||
use crate::infer::InferCtxt;
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq)]
|
||||
struct SubId(u32);
|
||||
impl ut::UnifyKey for SubId {
|
||||
type Value = ();
|
||||
#[inline]
|
||||
fn index(&self) -> u32 {
|
||||
self.0
|
||||
}
|
||||
#[inline]
|
||||
fn from_index(i: u32) -> SubId {
|
||||
SubId(i)
|
||||
}
|
||||
fn tag() -> &'static str {
|
||||
"SubId"
|
||||
}
|
||||
}
|
||||
|
||||
/// When reporting ambiguity errors, we sometimes want to
|
||||
/// treat all inference vars which are subtypes of each
|
||||
/// others as if they are equal. For this case we compute
|
||||
/// the transitive closure of our subtype obligations here.
|
||||
///
|
||||
/// E.g. when encountering ambiguity errors, we want to suggest
|
||||
/// specifying some method argument or to add a type annotation
|
||||
/// to a local variable. Because subtyping cannot change the
|
||||
/// shape of a type, it's fine if the cause of the ambiguity error
|
||||
/// is only related to the suggested variable via subtyping.
|
||||
///
|
||||
/// Even for something like `let x = returns_arg(); x.method();` the
|
||||
/// type of `x` is only a supertype of the argument of `returns_arg`. We
|
||||
/// still want to suggest specifying the type of the argument.
|
||||
#[derive(Default)]
|
||||
pub struct SubRelations {
|
||||
map: FxHashMap<ty::TyVid, SubId>,
|
||||
table: ut::UnificationTableStorage<SubId>,
|
||||
}
|
||||
|
||||
impl SubRelations {
|
||||
fn get_id<'tcx>(&mut self, infcx: &InferCtxt<'tcx>, vid: ty::TyVid) -> SubId {
|
||||
let root_vid = infcx.root_var(vid);
|
||||
*self.map.entry(root_vid).or_insert_with(|| self.table.with_log(&mut NoUndo).new_key(()))
|
||||
}
|
||||
|
||||
pub fn add_constraints<'tcx>(
|
||||
&mut self,
|
||||
infcx: &InferCtxt<'tcx>,
|
||||
obls: impl IntoIterator<Item = ty::Predicate<'tcx>>,
|
||||
) {
|
||||
for p in obls {
|
||||
let (a, b) = match p.kind().skip_binder() {
|
||||
ty::PredicateKind::Subtype(ty::SubtypePredicate { a_is_expected: _, a, b }) => {
|
||||
(a, b)
|
||||
}
|
||||
ty::PredicateKind::Coerce(ty::CoercePredicate { a, b }) => (a, b),
|
||||
_ => continue,
|
||||
};
|
||||
|
||||
match (a.kind(), b.kind()) {
|
||||
(&ty::Infer(ty::TyVar(a_vid)), &ty::Infer(ty::TyVar(b_vid))) => {
|
||||
let a = self.get_id(infcx, a_vid);
|
||||
let b = self.get_id(infcx, b_vid);
|
||||
self.table.with_log(&mut NoUndo).unify_var_var(a, b).unwrap();
|
||||
}
|
||||
_ => continue,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn unified<'tcx>(&mut self, infcx: &InferCtxt<'tcx>, a: ty::TyVid, b: ty::TyVid) -> bool {
|
||||
let a = self.get_id(infcx, a);
|
||||
let b = self.get_id(infcx, b);
|
||||
self.table.with_log(&mut NoUndo).unioned(a, b)
|
||||
}
|
||||
}
|
|
@ -1,899 +0,0 @@
|
|||
use crate::error_reporting::infer::hir::Path;
|
||||
use core::ops::ControlFlow;
|
||||
use hir::def::CtorKind;
|
||||
use hir::intravisit::{walk_expr, walk_stmt, Visitor};
|
||||
use hir::{LetStmt, QPath};
|
||||
use rustc_data_structures::fx::FxIndexSet;
|
||||
use rustc_errors::{Applicability, Diag};
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::def::Res;
|
||||
use rustc_hir::MatchSource;
|
||||
use rustc_hir::Node;
|
||||
use rustc_middle::traits::{
|
||||
IfExpressionCause, MatchExpressionArmCause, ObligationCause, ObligationCauseCode,
|
||||
StatementAsExpression,
|
||||
};
|
||||
use rustc_middle::ty::print::with_no_trimmed_paths;
|
||||
use rustc_middle::ty::{self as ty, GenericArgKind, IsSuggestable, Ty, TypeVisitableExt};
|
||||
use rustc_span::{sym, Span};
|
||||
|
||||
use crate::errors::{
|
||||
ConsiderAddingAwait, FnConsiderCasting, FnItemsAreDistinct, FnUniqTypes,
|
||||
FunctionPointerSuggestion, SuggestAccessingField, SuggestRemoveSemiOrReturnBinding,
|
||||
SuggestTuplePatternMany, SuggestTuplePatternOne, TypeErrorAdditionalDiags,
|
||||
};
|
||||
|
||||
use super::TypeErrCtxt;
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum SuggestAsRefKind {
|
||||
Option,
|
||||
Result,
|
||||
}
|
||||
|
||||
impl<'tcx> TypeErrCtxt<'_, 'tcx> {
|
||||
pub(super) fn suggest_remove_semi_or_return_binding(
|
||||
&self,
|
||||
first_id: Option<hir::HirId>,
|
||||
first_ty: Ty<'tcx>,
|
||||
first_span: Span,
|
||||
second_id: Option<hir::HirId>,
|
||||
second_ty: Ty<'tcx>,
|
||||
second_span: Span,
|
||||
) -> Option<SuggestRemoveSemiOrReturnBinding> {
|
||||
let remove_semicolon = [
|
||||
(first_id, self.resolve_vars_if_possible(second_ty)),
|
||||
(second_id, self.resolve_vars_if_possible(first_ty)),
|
||||
]
|
||||
.into_iter()
|
||||
.find_map(|(id, ty)| {
|
||||
let hir::Node::Block(blk) = self.tcx.hir_node(id?) else { return None };
|
||||
self.could_remove_semicolon(blk, ty)
|
||||
});
|
||||
match remove_semicolon {
|
||||
Some((sp, StatementAsExpression::NeedsBoxing)) => {
|
||||
Some(SuggestRemoveSemiOrReturnBinding::RemoveAndBox {
|
||||
first_lo: first_span.shrink_to_lo(),
|
||||
first_hi: first_span.shrink_to_hi(),
|
||||
second_lo: second_span.shrink_to_lo(),
|
||||
second_hi: second_span.shrink_to_hi(),
|
||||
sp,
|
||||
})
|
||||
}
|
||||
Some((sp, StatementAsExpression::CorrectType)) => {
|
||||
Some(SuggestRemoveSemiOrReturnBinding::Remove { sp })
|
||||
}
|
||||
None => {
|
||||
let mut ret = None;
|
||||
for (id, ty) in [(first_id, second_ty), (second_id, first_ty)] {
|
||||
if let Some(id) = id
|
||||
&& let hir::Node::Block(blk) = self.tcx.hir_node(id)
|
||||
&& let Some(diag) = self.consider_returning_binding_diag(blk, ty)
|
||||
{
|
||||
ret = Some(diag);
|
||||
break;
|
||||
}
|
||||
}
|
||||
ret
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn suggest_tuple_pattern(
|
||||
&self,
|
||||
cause: &ObligationCause<'tcx>,
|
||||
exp_found: &ty::error::ExpectedFound<Ty<'tcx>>,
|
||||
diag: &mut Diag<'_>,
|
||||
) {
|
||||
// Heavily inspired by `FnCtxt::suggest_compatible_variants`, with
|
||||
// some modifications due to that being in typeck and this being in infer.
|
||||
if let ObligationCauseCode::Pattern { .. } = cause.code() {
|
||||
if let ty::Adt(expected_adt, args) = exp_found.expected.kind() {
|
||||
let compatible_variants: Vec<_> = expected_adt
|
||||
.variants()
|
||||
.iter()
|
||||
.filter(|variant| {
|
||||
variant.fields.len() == 1 && variant.ctor_kind() == Some(CtorKind::Fn)
|
||||
})
|
||||
.filter_map(|variant| {
|
||||
let sole_field = &variant.single_field();
|
||||
let sole_field_ty = sole_field.ty(self.tcx, args);
|
||||
if self.same_type_modulo_infer(sole_field_ty, exp_found.found) {
|
||||
let variant_path =
|
||||
with_no_trimmed_paths!(self.tcx.def_path_str(variant.def_id));
|
||||
// FIXME #56861: DRYer prelude filtering
|
||||
if let Some(path) = variant_path.strip_prefix("std::prelude::") {
|
||||
if let Some((_, path)) = path.split_once("::") {
|
||||
return Some(path.to_string());
|
||||
}
|
||||
}
|
||||
Some(variant_path)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
match &compatible_variants[..] {
|
||||
[] => {}
|
||||
[variant] => {
|
||||
let sugg = SuggestTuplePatternOne {
|
||||
variant: variant.to_owned(),
|
||||
span_low: cause.span.shrink_to_lo(),
|
||||
span_high: cause.span.shrink_to_hi(),
|
||||
};
|
||||
diag.subdiagnostic(sugg);
|
||||
}
|
||||
_ => {
|
||||
// More than one matching variant.
|
||||
let sugg = SuggestTuplePatternMany {
|
||||
path: self.tcx.def_path_str(expected_adt.did()),
|
||||
cause_span: cause.span,
|
||||
compatible_variants,
|
||||
};
|
||||
diag.subdiagnostic(sugg);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A possible error is to forget to add `.await` when using futures:
|
||||
///
|
||||
/// ```compile_fail,E0308
|
||||
/// async fn make_u32() -> u32 {
|
||||
/// 22
|
||||
/// }
|
||||
///
|
||||
/// fn take_u32(x: u32) {}
|
||||
///
|
||||
/// async fn foo() {
|
||||
/// let x = make_u32();
|
||||
/// take_u32(x);
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// This routine checks if the found type `T` implements `Future<Output=U>` where `U` is the
|
||||
/// expected type. If this is the case, and we are inside of an async body, it suggests adding
|
||||
/// `.await` to the tail of the expression.
|
||||
pub(super) fn suggest_await_on_expect_found(
|
||||
&self,
|
||||
cause: &ObligationCause<'tcx>,
|
||||
exp_span: Span,
|
||||
exp_found: &ty::error::ExpectedFound<Ty<'tcx>>,
|
||||
diag: &mut Diag<'_>,
|
||||
) {
|
||||
debug!(
|
||||
"suggest_await_on_expect_found: exp_span={:?}, expected_ty={:?}, found_ty={:?}",
|
||||
exp_span, exp_found.expected, exp_found.found,
|
||||
);
|
||||
|
||||
if let ObligationCauseCode::CompareImplItem { .. } = cause.code() {
|
||||
return;
|
||||
}
|
||||
|
||||
let subdiag = match (
|
||||
self.get_impl_future_output_ty(exp_found.expected),
|
||||
self.get_impl_future_output_ty(exp_found.found),
|
||||
) {
|
||||
(Some(exp), Some(found)) if self.same_type_modulo_infer(exp, found) => match cause
|
||||
.code()
|
||||
{
|
||||
ObligationCauseCode::IfExpression(box IfExpressionCause { then_id, .. }) => {
|
||||
let then_span = self.find_block_span_from_hir_id(*then_id);
|
||||
Some(ConsiderAddingAwait::BothFuturesSugg {
|
||||
first: then_span.shrink_to_hi(),
|
||||
second: exp_span.shrink_to_hi(),
|
||||
})
|
||||
}
|
||||
ObligationCauseCode::MatchExpressionArm(box MatchExpressionArmCause {
|
||||
prior_non_diverging_arms,
|
||||
..
|
||||
}) => {
|
||||
if let [.., arm_span] = &prior_non_diverging_arms[..] {
|
||||
Some(ConsiderAddingAwait::BothFuturesSugg {
|
||||
first: arm_span.shrink_to_hi(),
|
||||
second: exp_span.shrink_to_hi(),
|
||||
})
|
||||
} else {
|
||||
Some(ConsiderAddingAwait::BothFuturesHelp)
|
||||
}
|
||||
}
|
||||
_ => Some(ConsiderAddingAwait::BothFuturesHelp),
|
||||
},
|
||||
(_, Some(ty)) if self.same_type_modulo_infer(exp_found.expected, ty) => {
|
||||
// FIXME: Seems like we can't have a suggestion and a note with different spans in a single subdiagnostic
|
||||
diag.subdiagnostic(ConsiderAddingAwait::FutureSugg {
|
||||
span: exp_span.shrink_to_hi(),
|
||||
});
|
||||
Some(ConsiderAddingAwait::FutureSuggNote { span: exp_span })
|
||||
}
|
||||
(Some(ty), _) if self.same_type_modulo_infer(ty, exp_found.found) => match cause.code()
|
||||
{
|
||||
ObligationCauseCode::Pattern { span: Some(then_span), origin_expr, .. } => {
|
||||
origin_expr.then_some(ConsiderAddingAwait::FutureSugg {
|
||||
span: then_span.shrink_to_hi(),
|
||||
})
|
||||
}
|
||||
ObligationCauseCode::IfExpression(box IfExpressionCause { then_id, .. }) => {
|
||||
let then_span = self.find_block_span_from_hir_id(*then_id);
|
||||
Some(ConsiderAddingAwait::FutureSugg { span: then_span.shrink_to_hi() })
|
||||
}
|
||||
ObligationCauseCode::MatchExpressionArm(box MatchExpressionArmCause {
|
||||
ref prior_non_diverging_arms,
|
||||
..
|
||||
}) => Some({
|
||||
ConsiderAddingAwait::FutureSuggMultiple {
|
||||
spans: prior_non_diverging_arms
|
||||
.iter()
|
||||
.map(|arm| arm.shrink_to_hi())
|
||||
.collect(),
|
||||
}
|
||||
}),
|
||||
_ => None,
|
||||
},
|
||||
_ => None,
|
||||
};
|
||||
if let Some(subdiag) = subdiag {
|
||||
diag.subdiagnostic(subdiag);
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn suggest_accessing_field_where_appropriate(
|
||||
&self,
|
||||
cause: &ObligationCause<'tcx>,
|
||||
exp_found: &ty::error::ExpectedFound<Ty<'tcx>>,
|
||||
diag: &mut Diag<'_>,
|
||||
) {
|
||||
debug!(
|
||||
"suggest_accessing_field_where_appropriate(cause={:?}, exp_found={:?})",
|
||||
cause, exp_found
|
||||
);
|
||||
if let ty::Adt(expected_def, expected_args) = exp_found.expected.kind() {
|
||||
if expected_def.is_enum() {
|
||||
return;
|
||||
}
|
||||
|
||||
if let Some((name, ty)) = expected_def
|
||||
.non_enum_variant()
|
||||
.fields
|
||||
.iter()
|
||||
.filter(|field| field.vis.is_accessible_from(field.did, self.tcx))
|
||||
.map(|field| (field.name, field.ty(self.tcx, expected_args)))
|
||||
.find(|(_, ty)| self.same_type_modulo_infer(*ty, exp_found.found))
|
||||
{
|
||||
if let ObligationCauseCode::Pattern { span: Some(span), .. } = *cause.code() {
|
||||
if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span) {
|
||||
let suggestion = if expected_def.is_struct() {
|
||||
SuggestAccessingField::Safe { span, snippet, name, ty }
|
||||
} else if expected_def.is_union() {
|
||||
SuggestAccessingField::Unsafe { span, snippet, name, ty }
|
||||
} else {
|
||||
return;
|
||||
};
|
||||
diag.subdiagnostic(suggestion);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn suggest_turning_stmt_into_expr(
|
||||
&self,
|
||||
cause: &ObligationCause<'tcx>,
|
||||
exp_found: &ty::error::ExpectedFound<Ty<'tcx>>,
|
||||
diag: &mut Diag<'_>,
|
||||
) {
|
||||
let ty::error::ExpectedFound { expected, found } = exp_found;
|
||||
if !found.peel_refs().is_unit() {
|
||||
return;
|
||||
}
|
||||
|
||||
let ObligationCauseCode::BlockTailExpression(hir_id, MatchSource::Normal) = cause.code()
|
||||
else {
|
||||
return;
|
||||
};
|
||||
|
||||
let node = self.tcx.hir_node(*hir_id);
|
||||
let mut blocks = vec![];
|
||||
if let hir::Node::Block(block) = node
|
||||
&& let Some(expr) = block.expr
|
||||
&& let hir::ExprKind::Path(QPath::Resolved(_, Path { res, .. })) = expr.kind
|
||||
&& let Res::Local(local) = res
|
||||
&& let Node::LetStmt(LetStmt { init: Some(init), .. }) =
|
||||
self.tcx.parent_hir_node(*local)
|
||||
{
|
||||
fn collect_blocks<'hir>(expr: &hir::Expr<'hir>, blocks: &mut Vec<&hir::Block<'hir>>) {
|
||||
match expr.kind {
|
||||
// `blk1` and `blk2` must be have the same types, it will be reported before reaching here
|
||||
hir::ExprKind::If(_, blk1, Some(blk2)) => {
|
||||
collect_blocks(blk1, blocks);
|
||||
collect_blocks(blk2, blocks);
|
||||
}
|
||||
hir::ExprKind::Match(_, arms, _) => {
|
||||
// all arms must have same types
|
||||
for arm in arms.iter() {
|
||||
collect_blocks(arm.body, blocks);
|
||||
}
|
||||
}
|
||||
hir::ExprKind::Block(blk, _) => {
|
||||
blocks.push(blk);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
collect_blocks(init, &mut blocks);
|
||||
}
|
||||
|
||||
let expected_inner: Ty<'_> = expected.peel_refs();
|
||||
for block in blocks.iter() {
|
||||
self.consider_removing_semicolon(block, expected_inner, diag);
|
||||
}
|
||||
}
|
||||
|
||||
/// A common error is to add an extra semicolon:
|
||||
///
|
||||
/// ```compile_fail,E0308
|
||||
/// fn foo() -> usize {
|
||||
/// 22;
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// This routine checks if the final statement in a block is an
|
||||
/// expression with an explicit semicolon whose type is compatible
|
||||
/// with `expected_ty`. If so, it suggests removing the semicolon.
|
||||
pub fn consider_removing_semicolon(
|
||||
&self,
|
||||
blk: &'tcx hir::Block<'tcx>,
|
||||
expected_ty: Ty<'tcx>,
|
||||
diag: &mut Diag<'_>,
|
||||
) -> bool {
|
||||
if let Some((span_semi, boxed)) = self.could_remove_semicolon(blk, expected_ty) {
|
||||
if let StatementAsExpression::NeedsBoxing = boxed {
|
||||
diag.span_suggestion_verbose(
|
||||
span_semi,
|
||||
"consider removing this semicolon and boxing the expression",
|
||||
"",
|
||||
Applicability::HasPlaceholders,
|
||||
);
|
||||
} else {
|
||||
diag.span_suggestion_short(
|
||||
span_semi,
|
||||
"remove this semicolon to return this value",
|
||||
"",
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn suggest_function_pointers(
|
||||
&self,
|
||||
cause: &ObligationCause<'tcx>,
|
||||
span: Span,
|
||||
exp_found: &ty::error::ExpectedFound<Ty<'tcx>>,
|
||||
diag: &mut Diag<'_>,
|
||||
) {
|
||||
debug!("suggest_function_pointers(cause={:?}, exp_found={:?})", cause, exp_found);
|
||||
let ty::error::ExpectedFound { expected, found } = exp_found;
|
||||
let expected_inner = expected.peel_refs();
|
||||
let found_inner = found.peel_refs();
|
||||
if !expected_inner.is_fn() || !found_inner.is_fn() {
|
||||
return;
|
||||
}
|
||||
match (&expected_inner.kind(), &found_inner.kind()) {
|
||||
(ty::FnPtr(sig), ty::FnDef(did, args)) => {
|
||||
let expected_sig = &(self.normalize_fn_sig)(*sig);
|
||||
let found_sig =
|
||||
&(self.normalize_fn_sig)(self.tcx.fn_sig(*did).instantiate(self.tcx, args));
|
||||
|
||||
let fn_name = self.tcx.def_path_str_with_args(*did, args);
|
||||
|
||||
if !self.same_type_modulo_infer(*found_sig, *expected_sig)
|
||||
|| !sig.is_suggestable(self.tcx, true)
|
||||
|| self.tcx.intrinsic(*did).is_some()
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
let sugg = match (expected.is_ref(), found.is_ref()) {
|
||||
(true, false) => FunctionPointerSuggestion::UseRef { span, fn_name },
|
||||
(false, true) => FunctionPointerSuggestion::RemoveRef { span, fn_name },
|
||||
(true, true) => {
|
||||
diag.subdiagnostic(FnItemsAreDistinct);
|
||||
FunctionPointerSuggestion::CastRef { span, fn_name, sig: *sig }
|
||||
}
|
||||
(false, false) => {
|
||||
diag.subdiagnostic(FnItemsAreDistinct);
|
||||
FunctionPointerSuggestion::Cast { span, fn_name, sig: *sig }
|
||||
}
|
||||
};
|
||||
diag.subdiagnostic(sugg);
|
||||
}
|
||||
(ty::FnDef(did1, args1), ty::FnDef(did2, args2)) => {
|
||||
let expected_sig =
|
||||
&(self.normalize_fn_sig)(self.tcx.fn_sig(*did1).instantiate(self.tcx, args1));
|
||||
let found_sig =
|
||||
&(self.normalize_fn_sig)(self.tcx.fn_sig(*did2).instantiate(self.tcx, args2));
|
||||
|
||||
if self.same_type_modulo_infer(*expected_sig, *found_sig) {
|
||||
diag.subdiagnostic(FnUniqTypes);
|
||||
}
|
||||
|
||||
if !self.same_type_modulo_infer(*found_sig, *expected_sig)
|
||||
|| !found_sig.is_suggestable(self.tcx, true)
|
||||
|| !expected_sig.is_suggestable(self.tcx, true)
|
||||
|| self.tcx.intrinsic(*did1).is_some()
|
||||
|| self.tcx.intrinsic(*did2).is_some()
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
let fn_name = self.tcx.def_path_str_with_args(*did2, args2);
|
||||
let sug = if found.is_ref() {
|
||||
FunctionPointerSuggestion::CastBothRef {
|
||||
span,
|
||||
fn_name,
|
||||
found_sig: *found_sig,
|
||||
expected_sig: *expected_sig,
|
||||
}
|
||||
} else {
|
||||
FunctionPointerSuggestion::CastBoth {
|
||||
span,
|
||||
fn_name,
|
||||
found_sig: *found_sig,
|
||||
expected_sig: *expected_sig,
|
||||
}
|
||||
};
|
||||
|
||||
diag.subdiagnostic(sug);
|
||||
}
|
||||
(ty::FnDef(did, args), ty::FnPtr(sig)) => {
|
||||
let expected_sig =
|
||||
&(self.normalize_fn_sig)(self.tcx.fn_sig(*did).instantiate(self.tcx, args));
|
||||
let found_sig = &(self.normalize_fn_sig)(*sig);
|
||||
|
||||
if !self.same_type_modulo_infer(*found_sig, *expected_sig) {
|
||||
return;
|
||||
}
|
||||
|
||||
let fn_name = self.tcx.def_path_str_with_args(*did, args);
|
||||
|
||||
let casting = if expected.is_ref() {
|
||||
format!("&({fn_name} as {found_sig})")
|
||||
} else {
|
||||
format!("{fn_name} as {found_sig}")
|
||||
};
|
||||
|
||||
diag.subdiagnostic(FnConsiderCasting { casting });
|
||||
}
|
||||
_ => {
|
||||
return;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
pub fn should_suggest_as_ref_kind(
|
||||
&self,
|
||||
expected: Ty<'tcx>,
|
||||
found: Ty<'tcx>,
|
||||
) -> Option<SuggestAsRefKind> {
|
||||
if let (ty::Adt(exp_def, exp_args), ty::Ref(_, found_ty, _)) =
|
||||
(expected.kind(), found.kind())
|
||||
{
|
||||
if let ty::Adt(found_def, found_args) = *found_ty.kind() {
|
||||
if exp_def == &found_def {
|
||||
let have_as_ref = &[
|
||||
(sym::Option, SuggestAsRefKind::Option),
|
||||
(sym::Result, SuggestAsRefKind::Result),
|
||||
];
|
||||
if let Some(msg) = have_as_ref.iter().find_map(|(name, msg)| {
|
||||
self.tcx.is_diagnostic_item(*name, exp_def.did()).then_some(msg)
|
||||
}) {
|
||||
let mut show_suggestion = true;
|
||||
for (exp_ty, found_ty) in
|
||||
std::iter::zip(exp_args.types(), found_args.types())
|
||||
{
|
||||
match *exp_ty.kind() {
|
||||
ty::Ref(_, exp_ty, _) => {
|
||||
match (exp_ty.kind(), found_ty.kind()) {
|
||||
(_, ty::Param(_))
|
||||
| (_, ty::Infer(_))
|
||||
| (ty::Param(_), _)
|
||||
| (ty::Infer(_), _) => {}
|
||||
_ if self.same_type_modulo_infer(exp_ty, found_ty) => {}
|
||||
_ => show_suggestion = false,
|
||||
};
|
||||
}
|
||||
ty::Param(_) | ty::Infer(_) => {}
|
||||
_ => show_suggestion = false,
|
||||
}
|
||||
}
|
||||
if show_suggestion {
|
||||
return Some(*msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
// FIXME: Remove once `rustc_hir_typeck` is migrated to diagnostic structs
|
||||
pub fn should_suggest_as_ref(&self, expected: Ty<'tcx>, found: Ty<'tcx>) -> Option<&str> {
|
||||
match self.should_suggest_as_ref_kind(expected, found) {
|
||||
Some(SuggestAsRefKind::Option) => Some(
|
||||
"you can convert from `&Option<T>` to `Option<&T>` using \
|
||||
`.as_ref()`",
|
||||
),
|
||||
Some(SuggestAsRefKind::Result) => Some(
|
||||
"you can convert from `&Result<T, E>` to \
|
||||
`Result<&T, &E>` using `.as_ref()`",
|
||||
),
|
||||
None => None,
|
||||
}
|
||||
}
|
||||
/// Try to find code with pattern `if Some(..) = expr`
|
||||
/// use a `visitor` to mark the `if` which its span contains given error span,
|
||||
/// and then try to find a assignment in the `cond` part, which span is equal with error span
|
||||
pub(super) fn suggest_let_for_letchains(
|
||||
&self,
|
||||
cause: &ObligationCause<'_>,
|
||||
span: Span,
|
||||
) -> Option<TypeErrorAdditionalDiags> {
|
||||
/// Find the if expression with given span
|
||||
struct IfVisitor {
|
||||
pub found_if: bool,
|
||||
pub err_span: Span,
|
||||
}
|
||||
|
||||
impl<'v> Visitor<'v> for IfVisitor {
|
||||
type Result = ControlFlow<()>;
|
||||
fn visit_expr(&mut self, ex: &'v hir::Expr<'v>) -> Self::Result {
|
||||
match ex.kind {
|
||||
hir::ExprKind::If(cond, _, _) => {
|
||||
self.found_if = true;
|
||||
walk_expr(self, cond)?;
|
||||
self.found_if = false;
|
||||
ControlFlow::Continue(())
|
||||
}
|
||||
_ => walk_expr(self, ex),
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_stmt(&mut self, ex: &'v hir::Stmt<'v>) -> Self::Result {
|
||||
if let hir::StmtKind::Let(LetStmt {
|
||||
span,
|
||||
pat: hir::Pat { .. },
|
||||
ty: None,
|
||||
init: Some(_),
|
||||
..
|
||||
}) = &ex.kind
|
||||
&& self.found_if
|
||||
&& span.eq(&self.err_span)
|
||||
{
|
||||
ControlFlow::Break(())
|
||||
} else {
|
||||
walk_stmt(self, ex)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self.tcx.hir().maybe_body_owned_by(cause.body_id).and_then(|body| {
|
||||
IfVisitor { err_span: span, found_if: false }
|
||||
.visit_body(&body)
|
||||
.is_break()
|
||||
.then(|| TypeErrorAdditionalDiags::AddLetForLetChains { span: span.shrink_to_lo() })
|
||||
})
|
||||
}
|
||||
|
||||
/// For "one type is more general than the other" errors on closures, suggest changing the lifetime
|
||||
/// of the parameters to accept all lifetimes.
|
||||
pub(super) fn suggest_for_all_lifetime_closure(
|
||||
&self,
|
||||
span: Span,
|
||||
hir: hir::Node<'_>,
|
||||
exp_found: &ty::error::ExpectedFound<ty::TraitRef<'tcx>>,
|
||||
diag: &mut Diag<'_>,
|
||||
) {
|
||||
// 0. Extract fn_decl from hir
|
||||
let hir::Node::Expr(hir::Expr {
|
||||
kind: hir::ExprKind::Closure(hir::Closure { body, fn_decl, .. }),
|
||||
..
|
||||
}) = hir
|
||||
else {
|
||||
return;
|
||||
};
|
||||
let hir::Body { params, .. } = self.tcx.hir().body(*body);
|
||||
|
||||
// 1. Get the args of the closure.
|
||||
// 2. Assume exp_found is FnOnce / FnMut / Fn, we can extract function parameters from [1].
|
||||
let Some(expected) = exp_found.expected.args.get(1) else {
|
||||
return;
|
||||
};
|
||||
let Some(found) = exp_found.found.args.get(1) else {
|
||||
return;
|
||||
};
|
||||
let expected = expected.unpack();
|
||||
let found = found.unpack();
|
||||
// 3. Extract the tuple type from Fn trait and suggest the change.
|
||||
if let GenericArgKind::Type(expected) = expected
|
||||
&& let GenericArgKind::Type(found) = found
|
||||
&& let ty::Tuple(expected) = expected.kind()
|
||||
&& let ty::Tuple(found) = found.kind()
|
||||
&& expected.len() == found.len()
|
||||
{
|
||||
let mut suggestion = "|".to_string();
|
||||
let mut is_first = true;
|
||||
let mut has_suggestion = false;
|
||||
|
||||
for (((expected, found), param_hir), arg_hir) in
|
||||
expected.iter().zip(found.iter()).zip(params.iter()).zip(fn_decl.inputs.iter())
|
||||
{
|
||||
if is_first {
|
||||
is_first = false;
|
||||
} else {
|
||||
suggestion += ", ";
|
||||
}
|
||||
|
||||
if let ty::Ref(expected_region, _, _) = expected.kind()
|
||||
&& let ty::Ref(found_region, _, _) = found.kind()
|
||||
&& expected_region.is_bound()
|
||||
&& !found_region.is_bound()
|
||||
&& let hir::TyKind::Infer = arg_hir.kind
|
||||
{
|
||||
// If the expected region is late bound, the found region is not, and users are asking compiler
|
||||
// to infer the type, we can suggest adding `: &_`.
|
||||
if param_hir.pat.span == param_hir.ty_span {
|
||||
// for `|x|`, `|_|`, `|x: impl Foo|`
|
||||
let Ok(pat) =
|
||||
self.tcx.sess.source_map().span_to_snippet(param_hir.pat.span)
|
||||
else {
|
||||
return;
|
||||
};
|
||||
suggestion += &format!("{pat}: &_");
|
||||
} else {
|
||||
// for `|x: ty|`, `|_: ty|`
|
||||
let Ok(pat) =
|
||||
self.tcx.sess.source_map().span_to_snippet(param_hir.pat.span)
|
||||
else {
|
||||
return;
|
||||
};
|
||||
let Ok(ty) = self.tcx.sess.source_map().span_to_snippet(param_hir.ty_span)
|
||||
else {
|
||||
return;
|
||||
};
|
||||
suggestion += &format!("{pat}: &{ty}");
|
||||
}
|
||||
has_suggestion = true;
|
||||
} else {
|
||||
let Ok(arg) = self.tcx.sess.source_map().span_to_snippet(param_hir.span) else {
|
||||
return;
|
||||
};
|
||||
// Otherwise, keep it as-is.
|
||||
suggestion += &arg;
|
||||
}
|
||||
}
|
||||
suggestion += "|";
|
||||
|
||||
if has_suggestion {
|
||||
diag.span_suggestion_verbose(
|
||||
span,
|
||||
"consider specifying the type of the closure parameters",
|
||||
suggestion,
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> TypeErrCtxt<'_, 'tcx> {
|
||||
/// Be helpful when the user wrote `{... expr; }` and taking the `;` off
|
||||
/// is enough to fix the error.
|
||||
pub fn could_remove_semicolon(
|
||||
&self,
|
||||
blk: &'tcx hir::Block<'tcx>,
|
||||
expected_ty: Ty<'tcx>,
|
||||
) -> Option<(Span, StatementAsExpression)> {
|
||||
let blk = blk.innermost_block();
|
||||
// Do not suggest if we have a tail expr.
|
||||
if blk.expr.is_some() {
|
||||
return None;
|
||||
}
|
||||
let last_stmt = blk.stmts.last()?;
|
||||
let hir::StmtKind::Semi(last_expr) = last_stmt.kind else {
|
||||
return None;
|
||||
};
|
||||
let last_expr_ty = self.typeck_results.as_ref()?.expr_ty_opt(last_expr)?;
|
||||
let needs_box = match (last_expr_ty.kind(), expected_ty.kind()) {
|
||||
_ if last_expr_ty.references_error() => return None,
|
||||
_ if self.same_type_modulo_infer(last_expr_ty, expected_ty) => {
|
||||
StatementAsExpression::CorrectType
|
||||
}
|
||||
(
|
||||
ty::Alias(ty::Opaque, ty::AliasTy { def_id: last_def_id, .. }),
|
||||
ty::Alias(ty::Opaque, ty::AliasTy { def_id: exp_def_id, .. }),
|
||||
) if last_def_id == exp_def_id => StatementAsExpression::CorrectType,
|
||||
(
|
||||
ty::Alias(ty::Opaque, ty::AliasTy { def_id: last_def_id, args: last_bounds, .. }),
|
||||
ty::Alias(ty::Opaque, ty::AliasTy { def_id: exp_def_id, args: exp_bounds, .. }),
|
||||
) => {
|
||||
debug!(
|
||||
"both opaque, likely future {:?} {:?} {:?} {:?}",
|
||||
last_def_id, last_bounds, exp_def_id, exp_bounds
|
||||
);
|
||||
|
||||
let last_local_id = last_def_id.as_local()?;
|
||||
let exp_local_id = exp_def_id.as_local()?;
|
||||
|
||||
match (
|
||||
&self.tcx.hir().expect_item(last_local_id).kind,
|
||||
&self.tcx.hir().expect_item(exp_local_id).kind,
|
||||
) {
|
||||
(
|
||||
hir::ItemKind::OpaqueTy(hir::OpaqueTy { bounds: last_bounds, .. }),
|
||||
hir::ItemKind::OpaqueTy(hir::OpaqueTy { bounds: exp_bounds, .. }),
|
||||
) if std::iter::zip(*last_bounds, *exp_bounds).all(|(left, right)| match (
|
||||
left, right,
|
||||
) {
|
||||
(hir::GenericBound::Trait(tl, ml), hir::GenericBound::Trait(tr, mr))
|
||||
if tl.trait_ref.trait_def_id() == tr.trait_ref.trait_def_id()
|
||||
&& ml == mr =>
|
||||
{
|
||||
true
|
||||
}
|
||||
_ => false,
|
||||
}) =>
|
||||
{
|
||||
StatementAsExpression::NeedsBoxing
|
||||
}
|
||||
_ => StatementAsExpression::CorrectType,
|
||||
}
|
||||
}
|
||||
_ => return None,
|
||||
};
|
||||
let span = if last_stmt.span.from_expansion() {
|
||||
let mac_call = rustc_span::source_map::original_sp(last_stmt.span, blk.span);
|
||||
self.tcx.sess.source_map().mac_call_stmt_semi_span(mac_call)?
|
||||
} else {
|
||||
self.tcx
|
||||
.sess
|
||||
.source_map()
|
||||
.span_extend_while_whitespace(last_expr.span)
|
||||
.shrink_to_hi()
|
||||
.with_hi(last_stmt.span.hi())
|
||||
};
|
||||
|
||||
Some((span, needs_box))
|
||||
}
|
||||
|
||||
/// Suggest returning a local binding with a compatible type if the block
|
||||
/// has no return expression.
|
||||
pub fn consider_returning_binding_diag(
|
||||
&self,
|
||||
blk: &'tcx hir::Block<'tcx>,
|
||||
expected_ty: Ty<'tcx>,
|
||||
) -> Option<SuggestRemoveSemiOrReturnBinding> {
|
||||
let blk = blk.innermost_block();
|
||||
// Do not suggest if we have a tail expr.
|
||||
if blk.expr.is_some() {
|
||||
return None;
|
||||
}
|
||||
let mut shadowed = FxIndexSet::default();
|
||||
let mut candidate_idents = vec![];
|
||||
let mut find_compatible_candidates = |pat: &hir::Pat<'_>| {
|
||||
if let hir::PatKind::Binding(_, hir_id, ident, _) = &pat.kind
|
||||
&& let Some(pat_ty) = self
|
||||
.typeck_results
|
||||
.as_ref()
|
||||
.and_then(|typeck_results| typeck_results.node_type_opt(*hir_id))
|
||||
{
|
||||
let pat_ty = self.resolve_vars_if_possible(pat_ty);
|
||||
if self.same_type_modulo_infer(pat_ty, expected_ty)
|
||||
&& !(pat_ty, expected_ty).references_error()
|
||||
&& shadowed.insert(ident.name)
|
||||
{
|
||||
candidate_idents.push((*ident, pat_ty));
|
||||
}
|
||||
}
|
||||
true
|
||||
};
|
||||
|
||||
let hir = self.tcx.hir();
|
||||
for stmt in blk.stmts.iter().rev() {
|
||||
let hir::StmtKind::Let(local) = &stmt.kind else {
|
||||
continue;
|
||||
};
|
||||
local.pat.walk(&mut find_compatible_candidates);
|
||||
}
|
||||
match self.tcx.parent_hir_node(blk.hir_id) {
|
||||
hir::Node::Expr(hir::Expr { hir_id, .. }) => match self.tcx.parent_hir_node(*hir_id) {
|
||||
hir::Node::Arm(hir::Arm { pat, .. }) => {
|
||||
pat.walk(&mut find_compatible_candidates);
|
||||
}
|
||||
|
||||
hir::Node::Item(hir::Item { kind: hir::ItemKind::Fn(_, _, body), .. })
|
||||
| hir::Node::ImplItem(hir::ImplItem {
|
||||
kind: hir::ImplItemKind::Fn(_, body), ..
|
||||
})
|
||||
| hir::Node::TraitItem(hir::TraitItem {
|
||||
kind: hir::TraitItemKind::Fn(_, hir::TraitFn::Provided(body)),
|
||||
..
|
||||
})
|
||||
| hir::Node::Expr(hir::Expr {
|
||||
kind: hir::ExprKind::Closure(hir::Closure { body, .. }),
|
||||
..
|
||||
}) => {
|
||||
for param in hir.body(*body).params {
|
||||
param.pat.walk(&mut find_compatible_candidates);
|
||||
}
|
||||
}
|
||||
hir::Node::Expr(hir::Expr {
|
||||
kind:
|
||||
hir::ExprKind::If(
|
||||
hir::Expr { kind: hir::ExprKind::Let(let_), .. },
|
||||
then_block,
|
||||
_,
|
||||
),
|
||||
..
|
||||
}) if then_block.hir_id == *hir_id => {
|
||||
let_.pat.walk(&mut find_compatible_candidates);
|
||||
}
|
||||
_ => {}
|
||||
},
|
||||
_ => {}
|
||||
}
|
||||
|
||||
match &candidate_idents[..] {
|
||||
[(ident, _ty)] => {
|
||||
let sm = self.tcx.sess.source_map();
|
||||
let (span, sugg) = if let Some(stmt) = blk.stmts.last() {
|
||||
let stmt_span = sm.stmt_span(stmt.span, blk.span);
|
||||
let sugg = if sm.is_multiline(blk.span)
|
||||
&& let Some(spacing) = sm.indentation_before(stmt_span)
|
||||
{
|
||||
format!("\n{spacing}{ident}")
|
||||
} else {
|
||||
format!(" {ident}")
|
||||
};
|
||||
(stmt_span.shrink_to_hi(), sugg)
|
||||
} else {
|
||||
let sugg = if sm.is_multiline(blk.span)
|
||||
&& let Some(spacing) = sm.indentation_before(blk.span.shrink_to_lo())
|
||||
{
|
||||
format!("\n{spacing} {ident}\n{spacing}")
|
||||
} else {
|
||||
format!(" {ident} ")
|
||||
};
|
||||
let left_span = sm.span_through_char(blk.span, '{').shrink_to_hi();
|
||||
(sm.span_extend_while_whitespace(left_span), sugg)
|
||||
};
|
||||
Some(SuggestRemoveSemiOrReturnBinding::Add { sp: span, code: sugg, ident: *ident })
|
||||
}
|
||||
values if (1..3).contains(&values.len()) => {
|
||||
let spans = values.iter().map(|(ident, _)| ident.span).collect::<Vec<_>>();
|
||||
Some(SuggestRemoveSemiOrReturnBinding::AddOne { spans: spans.into() })
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn consider_returning_binding(
|
||||
&self,
|
||||
blk: &'tcx hir::Block<'tcx>,
|
||||
expected_ty: Ty<'tcx>,
|
||||
err: &mut Diag<'_>,
|
||||
) -> bool {
|
||||
let diag = self.consider_returning_binding_diag(blk, expected_ty);
|
||||
match diag {
|
||||
Some(diag) => {
|
||||
err.subdiagnostic(diag);
|
||||
true
|
||||
}
|
||||
None => false,
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1 +0,0 @@
|
|||
pub mod infer;
|
File diff suppressed because it is too large
Load diff
|
@ -1,183 +0,0 @@
|
|||
use crate::error_reporting::infer::nice_region_error::find_anon_type;
|
||||
use crate::fluent_generated as fluent;
|
||||
use rustc_errors::{Diag, EmissionGuarantee, IntoDiagArg, SubdiagMessageOp, Subdiagnostic};
|
||||
use rustc_hir::def_id::LocalDefId;
|
||||
use rustc_middle::bug;
|
||||
use rustc_middle::ty::{self, TyCtxt};
|
||||
use rustc_span::{symbol::kw, Span};
|
||||
|
||||
struct DescriptionCtx<'a> {
|
||||
span: Option<Span>,
|
||||
kind: &'a str,
|
||||
arg: String,
|
||||
}
|
||||
|
||||
impl<'a> DescriptionCtx<'a> {
|
||||
fn new<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
generic_param_scope: LocalDefId,
|
||||
region: ty::Region<'tcx>,
|
||||
alt_span: Option<Span>,
|
||||
) -> Option<Self> {
|
||||
let (span, kind, arg) = match *region {
|
||||
ty::ReEarlyParam(br) => {
|
||||
let scope = tcx
|
||||
.parent(tcx.generics_of(generic_param_scope).region_param(br, tcx).def_id)
|
||||
.expect_local();
|
||||
let span = if let Some(param) =
|
||||
tcx.hir().get_generics(scope).and_then(|generics| generics.get_named(br.name))
|
||||
{
|
||||
param.span
|
||||
} else {
|
||||
tcx.def_span(scope)
|
||||
};
|
||||
if br.has_name() {
|
||||
(Some(span), "as_defined", br.name.to_string())
|
||||
} else {
|
||||
(Some(span), "as_defined_anon", String::new())
|
||||
}
|
||||
}
|
||||
ty::ReLateParam(ref fr) => {
|
||||
if !fr.bound_region.is_named()
|
||||
&& let Some((ty, _)) =
|
||||
find_anon_type(tcx, generic_param_scope, region, &fr.bound_region)
|
||||
{
|
||||
(Some(ty.span), "defined_here", String::new())
|
||||
} else {
|
||||
let scope = fr.scope.expect_local();
|
||||
match fr.bound_region {
|
||||
ty::BoundRegionKind::BrNamed(_, name) => {
|
||||
let span = if let Some(param) = tcx
|
||||
.hir()
|
||||
.get_generics(scope)
|
||||
.and_then(|generics| generics.get_named(name))
|
||||
{
|
||||
param.span
|
||||
} else {
|
||||
tcx.def_span(scope)
|
||||
};
|
||||
if name == kw::UnderscoreLifetime {
|
||||
(Some(span), "as_defined_anon", String::new())
|
||||
} else {
|
||||
(Some(span), "as_defined", name.to_string())
|
||||
}
|
||||
}
|
||||
ty::BrAnon => {
|
||||
let span = Some(tcx.def_span(scope));
|
||||
(span, "defined_here", String::new())
|
||||
}
|
||||
_ => (Some(tcx.def_span(scope)), "defined_here_reg", region.to_string()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ty::ReStatic => (alt_span, "restatic", String::new()),
|
||||
|
||||
ty::RePlaceholder(_) | ty::ReError(_) => return None,
|
||||
|
||||
ty::ReVar(_) | ty::ReBound(..) | ty::ReErased => {
|
||||
bug!("unexpected region for DescriptionCtx: {:?}", region);
|
||||
}
|
||||
};
|
||||
Some(DescriptionCtx { span, kind, arg })
|
||||
}
|
||||
}
|
||||
|
||||
pub enum PrefixKind {
|
||||
Empty,
|
||||
RefValidFor,
|
||||
ContentValidFor,
|
||||
TypeObjValidFor,
|
||||
SourcePointerValidFor,
|
||||
TypeSatisfy,
|
||||
TypeOutlive,
|
||||
LfParamInstantiatedWith,
|
||||
LfParamMustOutlive,
|
||||
LfInstantiatedWith,
|
||||
LfMustOutlive,
|
||||
PointerValidFor,
|
||||
DataValidFor,
|
||||
}
|
||||
|
||||
pub enum SuffixKind {
|
||||
Empty,
|
||||
Continues,
|
||||
ReqByBinding,
|
||||
}
|
||||
|
||||
impl IntoDiagArg for PrefixKind {
|
||||
fn into_diag_arg(self) -> rustc_errors::DiagArgValue {
|
||||
let kind = match self {
|
||||
Self::Empty => "empty",
|
||||
Self::RefValidFor => "ref_valid_for",
|
||||
Self::ContentValidFor => "content_valid_for",
|
||||
Self::TypeObjValidFor => "type_obj_valid_for",
|
||||
Self::SourcePointerValidFor => "source_pointer_valid_for",
|
||||
Self::TypeSatisfy => "type_satisfy",
|
||||
Self::TypeOutlive => "type_outlive",
|
||||
Self::LfParamInstantiatedWith => "lf_param_instantiated_with",
|
||||
Self::LfParamMustOutlive => "lf_param_must_outlive",
|
||||
Self::LfInstantiatedWith => "lf_instantiated_with",
|
||||
Self::LfMustOutlive => "lf_must_outlive",
|
||||
Self::PointerValidFor => "pointer_valid_for",
|
||||
Self::DataValidFor => "data_valid_for",
|
||||
}
|
||||
.into();
|
||||
rustc_errors::DiagArgValue::Str(kind)
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoDiagArg for SuffixKind {
|
||||
fn into_diag_arg(self) -> rustc_errors::DiagArgValue {
|
||||
let kind = match self {
|
||||
Self::Empty => "empty",
|
||||
Self::Continues => "continues",
|
||||
Self::ReqByBinding => "req_by_binding",
|
||||
}
|
||||
.into();
|
||||
rustc_errors::DiagArgValue::Str(kind)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct RegionExplanation<'a> {
|
||||
desc: DescriptionCtx<'a>,
|
||||
prefix: PrefixKind,
|
||||
suffix: SuffixKind,
|
||||
}
|
||||
|
||||
impl RegionExplanation<'_> {
|
||||
pub fn new<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
generic_param_scope: LocalDefId,
|
||||
region: ty::Region<'tcx>,
|
||||
alt_span: Option<Span>,
|
||||
prefix: PrefixKind,
|
||||
suffix: SuffixKind,
|
||||
) -> Option<Self> {
|
||||
Some(Self {
|
||||
desc: DescriptionCtx::new(tcx, generic_param_scope, region, alt_span)?,
|
||||
prefix,
|
||||
suffix,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Subdiagnostic for RegionExplanation<'_> {
|
||||
fn add_to_diag_with<G: EmissionGuarantee, F: SubdiagMessageOp<G>>(
|
||||
self,
|
||||
diag: &mut Diag<'_, G>,
|
||||
f: &F,
|
||||
) {
|
||||
diag.arg("pref_kind", self.prefix);
|
||||
diag.arg("suff_kind", self.suffix);
|
||||
diag.arg("desc_kind", self.desc.kind);
|
||||
diag.arg("desc_arg", self.desc.arg);
|
||||
|
||||
let msg = f(diag, fluent::infer_region_explanation.into());
|
||||
if let Some(span) = self.desc.span {
|
||||
diag.span_note(span, msg);
|
||||
} else {
|
||||
diag.note(msg);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -11,7 +11,6 @@ pub use BoundRegionConversionTime::*;
|
|||
pub use RegionVariableOrigin::*;
|
||||
pub use SubregionOrigin::*;
|
||||
|
||||
use crate::error_reporting::infer::TypeErrCtxt;
|
||||
use crate::infer::relate::RelateResult;
|
||||
use crate::traits::{self, ObligationCause, ObligationInspector, PredicateObligation, TraitEngine};
|
||||
use free_regions::RegionRelations;
|
||||
|
@ -24,7 +23,8 @@ use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap};
|
|||
use rustc_data_structures::sync::Lrc;
|
||||
use rustc_data_structures::undo_log::Rollback;
|
||||
use rustc_data_structures::unify as ut;
|
||||
use rustc_errors::{Diag, ErrorGuaranteed};
|
||||
use rustc_errors::ErrorGuaranteed;
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::def_id::{DefId, LocalDefId};
|
||||
use rustc_macros::extension;
|
||||
use rustc_middle::infer::canonical::{Canonical, CanonicalVarValues};
|
||||
|
@ -696,22 +696,6 @@ impl<'tcx> InferCtxt<'tcx> {
|
|||
self.next_trait_solver
|
||||
}
|
||||
|
||||
/// Creates a `TypeErrCtxt` for emitting various inference errors.
|
||||
/// During typeck, use `FnCtxt::err_ctxt` instead.
|
||||
pub fn err_ctxt(&self) -> TypeErrCtxt<'_, 'tcx> {
|
||||
TypeErrCtxt {
|
||||
infcx: self,
|
||||
sub_relations: Default::default(),
|
||||
typeck_results: None,
|
||||
fallback_has_occurred: false,
|
||||
normalize_fn_sig: Box::new(|fn_sig| fn_sig),
|
||||
autoderef_steps: Box::new(|ty| {
|
||||
debug_assert!(false, "shouldn't be using autoderef_steps outside of typeck");
|
||||
vec![(ty, vec![])]
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn freshen<T: TypeFoldable<TyCtxt<'tcx>>>(&self, t: T) -> T {
|
||||
t.fold_with(&mut self.freshener())
|
||||
}
|
||||
|
@ -1591,60 +1575,6 @@ impl<'tcx> InferCtxt<'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
|
||||
// [Note-Type-error-reporting]
|
||||
// An invariant is that anytime the expected or actual type is Error (the special
|
||||
// error type, meaning that an error occurred when typechecking this expression),
|
||||
// this is a derived error. The error cascaded from another error (that was already
|
||||
// reported), so it's not useful to display it to the user.
|
||||
// The following methods implement this logic.
|
||||
// They check if either the actual or expected type is Error, and don't print the error
|
||||
// in this case. The typechecker should only ever report type errors involving mismatched
|
||||
// types using one of these methods, and should not call span_err directly for such
|
||||
// errors.
|
||||
pub fn type_error_struct_with_diag<M>(
|
||||
&self,
|
||||
sp: Span,
|
||||
mk_diag: M,
|
||||
actual_ty: Ty<'tcx>,
|
||||
) -> Diag<'a>
|
||||
where
|
||||
M: FnOnce(String) -> Diag<'a>,
|
||||
{
|
||||
let actual_ty = self.resolve_vars_if_possible(actual_ty);
|
||||
debug!("type_error_struct_with_diag({:?}, {:?})", sp, actual_ty);
|
||||
|
||||
let mut err = mk_diag(self.ty_to_string(actual_ty));
|
||||
|
||||
// Don't report an error if actual type is `Error`.
|
||||
if actual_ty.references_error() {
|
||||
err.downgrade_to_delayed_bug();
|
||||
}
|
||||
|
||||
err
|
||||
}
|
||||
|
||||
pub fn report_mismatched_types(
|
||||
&self,
|
||||
cause: &ObligationCause<'tcx>,
|
||||
expected: Ty<'tcx>,
|
||||
actual: Ty<'tcx>,
|
||||
err: TypeError<'tcx>,
|
||||
) -> Diag<'a> {
|
||||
self.report_and_explain_type_error(TypeTrace::types(cause, true, expected, actual), err)
|
||||
}
|
||||
|
||||
pub fn report_mismatched_consts(
|
||||
&self,
|
||||
cause: &ObligationCause<'tcx>,
|
||||
expected: ty::Const<'tcx>,
|
||||
actual: ty::Const<'tcx>,
|
||||
err: TypeError<'tcx>,
|
||||
) -> Diag<'a> {
|
||||
self.report_and_explain_type_error(TypeTrace::consts(cause, true, expected, actual), err)
|
||||
}
|
||||
}
|
||||
|
||||
/// Helper for [InferCtxt::ty_or_const_infer_var_changed] (see comment on that), currently
|
||||
/// used only for `traits::fulfill`'s list of `stalled_on` inference variables.
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
|
@ -1890,3 +1820,32 @@ fn replace_param_and_infer_args_with_placeholder<'tcx>(
|
|||
|
||||
args.fold_with(&mut ReplaceParamAndInferWithPlaceholder { tcx, idx: 0 })
|
||||
}
|
||||
|
||||
impl<'tcx> InferCtxt<'tcx> {
|
||||
/// Given a [`hir::Block`], get the span of its last expression or
|
||||
/// statement, peeling off any inner blocks.
|
||||
pub fn find_block_span(&self, block: &'tcx hir::Block<'tcx>) -> Span {
|
||||
let block = block.innermost_block();
|
||||
if let Some(expr) = &block.expr {
|
||||
expr.span
|
||||
} else if let Some(stmt) = block.stmts.last() {
|
||||
// possibly incorrect trailing `;` in the else arm
|
||||
stmt.span
|
||||
} else {
|
||||
// empty block; point at its entirety
|
||||
block.span
|
||||
}
|
||||
}
|
||||
|
||||
/// Given a [`hir::HirId`] for a block, get the span of its last expression
|
||||
/// or statement, peeling off any inner blocks.
|
||||
pub fn find_block_span_from_hir_id(&self, hir_id: hir::HirId) -> Span {
|
||||
match self.tcx.hir_node(hir_id) {
|
||||
hir::Node::Block(blk) => self.find_block_span(blk),
|
||||
// The parser was in a weird state if either of these happen, but
|
||||
// it's better not to panic.
|
||||
hir::Node::Expr(e) => e.span,
|
||||
_ => rustc_span::DUMMY_SP,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -34,7 +34,6 @@
|
|||
#[macro_use]
|
||||
extern crate tracing;
|
||||
|
||||
pub mod error_reporting;
|
||||
mod errors;
|
||||
pub mod infer;
|
||||
pub mod traits;
|
||||
|
|
|
@ -1,203 +0,0 @@
|
|||
use super::ObjectSafetyViolation;
|
||||
|
||||
use crate::error_reporting::infer::TypeErrCtxt;
|
||||
use rustc_data_structures::fx::FxIndexSet;
|
||||
use rustc_errors::{codes::*, struct_span_code_err, Applicability, Diag, MultiSpan};
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::def_id::{DefId, LocalDefId};
|
||||
use rustc_middle::ty::print::with_no_trimmed_paths;
|
||||
use rustc_middle::ty::{self, TyCtxt};
|
||||
use rustc_span::Span;
|
||||
use std::fmt;
|
||||
use std::iter;
|
||||
|
||||
impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
|
||||
pub fn report_extra_impl_obligation(
|
||||
&self,
|
||||
error_span: Span,
|
||||
impl_item_def_id: LocalDefId,
|
||||
trait_item_def_id: DefId,
|
||||
requirement: &dyn fmt::Display,
|
||||
) -> Diag<'a> {
|
||||
let mut err = struct_span_code_err!(
|
||||
self.dcx(),
|
||||
error_span,
|
||||
E0276,
|
||||
"impl has stricter requirements than trait"
|
||||
);
|
||||
|
||||
if !self.tcx.is_impl_trait_in_trait(trait_item_def_id) {
|
||||
if let Some(span) = self.tcx.hir().span_if_local(trait_item_def_id) {
|
||||
let item_name = self.tcx.item_name(impl_item_def_id.to_def_id());
|
||||
err.span_label(span, format!("definition of `{item_name}` from trait"));
|
||||
}
|
||||
}
|
||||
|
||||
err.span_label(error_span, format!("impl has extra requirement {requirement}"));
|
||||
|
||||
err
|
||||
}
|
||||
}
|
||||
|
||||
pub fn report_object_safety_error<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
span: Span,
|
||||
hir_id: Option<hir::HirId>,
|
||||
trait_def_id: DefId,
|
||||
violations: &[ObjectSafetyViolation],
|
||||
) -> Diag<'tcx> {
|
||||
let trait_str = tcx.def_path_str(trait_def_id);
|
||||
let trait_span = tcx.hir().get_if_local(trait_def_id).and_then(|node| match node {
|
||||
hir::Node::Item(item) => Some(item.ident.span),
|
||||
_ => None,
|
||||
});
|
||||
let mut err = struct_span_code_err!(
|
||||
tcx.dcx(),
|
||||
span,
|
||||
E0038,
|
||||
"the trait `{}` cannot be made into an object",
|
||||
trait_str
|
||||
);
|
||||
err.span_label(span, format!("`{trait_str}` cannot be made into an object"));
|
||||
|
||||
if let Some(hir_id) = hir_id
|
||||
&& let hir::Node::Ty(ty) = tcx.hir_node(hir_id)
|
||||
&& let hir::TyKind::TraitObject([trait_ref, ..], ..) = ty.kind
|
||||
{
|
||||
let mut hir_id = hir_id;
|
||||
while let hir::Node::Ty(ty) = tcx.parent_hir_node(hir_id) {
|
||||
hir_id = ty.hir_id;
|
||||
}
|
||||
if tcx.parent_hir_node(hir_id).fn_sig().is_some() {
|
||||
// Do not suggest `impl Trait` when dealing with things like super-traits.
|
||||
err.span_suggestion_verbose(
|
||||
ty.span.until(trait_ref.span),
|
||||
"consider using an opaque type instead",
|
||||
"impl ",
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
}
|
||||
}
|
||||
let mut reported_violations = FxIndexSet::default();
|
||||
let mut multi_span = vec![];
|
||||
let mut messages = vec![];
|
||||
for violation in violations {
|
||||
if let ObjectSafetyViolation::SizedSelf(sp) = &violation
|
||||
&& !sp.is_empty()
|
||||
{
|
||||
// Do not report `SizedSelf` without spans pointing at `SizedSelf` obligations
|
||||
// with a `Span`.
|
||||
reported_violations.insert(ObjectSafetyViolation::SizedSelf(vec![].into()));
|
||||
}
|
||||
if reported_violations.insert(violation.clone()) {
|
||||
let spans = violation.spans();
|
||||
let msg = if trait_span.is_none() || spans.is_empty() {
|
||||
format!("the trait cannot be made into an object because {}", violation.error_msg())
|
||||
} else {
|
||||
format!("...because {}", violation.error_msg())
|
||||
};
|
||||
if spans.is_empty() {
|
||||
err.note(msg);
|
||||
} else {
|
||||
for span in spans {
|
||||
multi_span.push(span);
|
||||
messages.push(msg.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
let has_multi_span = !multi_span.is_empty();
|
||||
let mut note_span = MultiSpan::from_spans(multi_span.clone());
|
||||
if let (Some(trait_span), true) = (trait_span, has_multi_span) {
|
||||
note_span.push_span_label(trait_span, "this trait cannot be made into an object...");
|
||||
}
|
||||
for (span, msg) in iter::zip(multi_span, messages) {
|
||||
note_span.push_span_label(span, msg);
|
||||
}
|
||||
err.span_note(
|
||||
note_span,
|
||||
"for a trait to be \"object safe\" it needs to allow building a vtable to allow the call \
|
||||
to be resolvable dynamically; for more information visit \
|
||||
<https://doc.rust-lang.org/reference/items/traits.html#object-safety>",
|
||||
);
|
||||
|
||||
// Only provide the help if its a local trait, otherwise it's not actionable.
|
||||
if trait_span.is_some() {
|
||||
let mut reported_violations: Vec<_> = reported_violations.into_iter().collect();
|
||||
reported_violations.sort();
|
||||
|
||||
let mut potential_solutions: Vec<_> =
|
||||
reported_violations.into_iter().map(|violation| violation.solution()).collect();
|
||||
potential_solutions.sort();
|
||||
// Allows us to skip suggesting that the same item should be moved to another trait multiple times.
|
||||
potential_solutions.dedup();
|
||||
for solution in potential_solutions {
|
||||
solution.add_to(&mut err);
|
||||
}
|
||||
}
|
||||
|
||||
let impls_of = tcx.trait_impls_of(trait_def_id);
|
||||
let impls = if impls_of.blanket_impls().is_empty() {
|
||||
impls_of
|
||||
.non_blanket_impls()
|
||||
.values()
|
||||
.flatten()
|
||||
.filter(|def_id| {
|
||||
!matches!(tcx.type_of(*def_id).instantiate_identity().kind(), ty::Dynamic(..))
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
} else {
|
||||
vec![]
|
||||
};
|
||||
let externally_visible = if !impls.is_empty()
|
||||
&& let Some(def_id) = trait_def_id.as_local()
|
||||
// We may be executing this during typeck, which would result in cycle
|
||||
// if we used effective_visibilities query, which looks into opaque types
|
||||
// (and therefore calls typeck).
|
||||
&& tcx.resolutions(()).effective_visibilities.is_exported(def_id)
|
||||
{
|
||||
true
|
||||
} else {
|
||||
false
|
||||
};
|
||||
match &impls[..] {
|
||||
[] => {}
|
||||
_ if impls.len() > 9 => {}
|
||||
[only] if externally_visible => {
|
||||
err.help(with_no_trimmed_paths!(format!(
|
||||
"only type `{}` is seen to implement the trait in this crate, consider using it \
|
||||
directly instead",
|
||||
tcx.type_of(*only).instantiate_identity(),
|
||||
)));
|
||||
}
|
||||
[only] => {
|
||||
err.help(with_no_trimmed_paths!(format!(
|
||||
"only type `{}` implements the trait, consider using it directly instead",
|
||||
tcx.type_of(*only).instantiate_identity(),
|
||||
)));
|
||||
}
|
||||
impls => {
|
||||
let types = impls
|
||||
.iter()
|
||||
.map(|t| {
|
||||
with_no_trimmed_paths!(format!(" {}", tcx.type_of(*t).instantiate_identity(),))
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
err.help(format!(
|
||||
"the following types implement the trait, consider defining an enum where each \
|
||||
variant holds one of these types, implementing `{}` for this new enum and using \
|
||||
it instead:\n{}",
|
||||
trait_str,
|
||||
types.join("\n"),
|
||||
));
|
||||
}
|
||||
}
|
||||
if externally_visible {
|
||||
err.note(format!(
|
||||
"`{trait_str}` can be implemented in other crates; if you want to support your users \
|
||||
passing their own types here, you can't refer to a specific type",
|
||||
));
|
||||
}
|
||||
|
||||
err
|
||||
}
|
|
@ -3,7 +3,6 @@
|
|||
//! [rustc-dev-guide]: https://rustc-dev-guide.rust-lang.org/traits/resolution.html
|
||||
|
||||
mod engine;
|
||||
pub mod error_reporting;
|
||||
mod project;
|
||||
mod structural_impls;
|
||||
pub mod util;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue