149 lines
5.8 KiB
Rust
149 lines
5.8 KiB
Rust
//! Common logic for borrowck use-after-move errors when moved into a `fn(self)`,
|
|
//! as well as errors when attempting to call a non-const function in a const
|
|
//! context.
|
|
|
|
use rustc_hir::def_id::DefId;
|
|
use rustc_hir::{lang_items, LangItem};
|
|
use rustc_middle::ty::subst::SubstsRef;
|
|
use rustc_middle::ty::{self, AssocItemContainer, DefIdTree, Instance, ParamEnv, Ty, TyCtxt};
|
|
use rustc_span::symbol::Ident;
|
|
use rustc_span::{sym, DesugaringKind, Span};
|
|
|
|
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
|
|
pub enum CallDesugaringKind {
|
|
/// for _ in x {} calls x.into_iter()
|
|
ForLoopIntoIter,
|
|
/// x? calls x.branch()
|
|
QuestionBranch,
|
|
/// x? calls type_of(x)::from_residual()
|
|
QuestionFromResidual,
|
|
/// try { ..; x } calls type_of(x)::from_output(x)
|
|
TryBlockFromOutput,
|
|
}
|
|
|
|
impl CallDesugaringKind {
|
|
pub fn trait_def_id(self, tcx: TyCtxt<'_>) -> DefId {
|
|
match self {
|
|
Self::ForLoopIntoIter => tcx.get_diagnostic_item(sym::IntoIterator).unwrap(),
|
|
Self::QuestionBranch | Self::TryBlockFromOutput => {
|
|
tcx.require_lang_item(LangItem::Try, None)
|
|
}
|
|
Self::QuestionFromResidual => tcx.get_diagnostic_item(sym::FromResidual).unwrap(),
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
|
|
pub enum CallKind<'tcx> {
|
|
/// A normal method call of the form `receiver.foo(a, b, c)`
|
|
Normal {
|
|
self_arg: Option<Ident>,
|
|
desugaring: Option<(CallDesugaringKind, Ty<'tcx>)>,
|
|
/// Whether the self type of the method call has an `.as_ref()` method.
|
|
/// Used for better diagnostics.
|
|
is_option_or_result: bool,
|
|
},
|
|
/// A call to `Fn(..)::call(..)`, desugared from `my_closure(a, b, c)`
|
|
FnCall { fn_trait_id: DefId, self_ty: Ty<'tcx> },
|
|
/// A call to an operator trait, desugared from operator syntax (e.g. `a << b`)
|
|
Operator { self_arg: Option<Ident>, trait_id: DefId, self_ty: Ty<'tcx> },
|
|
DerefCoercion {
|
|
/// The `Span` of the `Target` associated type
|
|
/// in the `Deref` impl we are using.
|
|
deref_target: Span,
|
|
/// The type `T::Deref` we are dereferencing to
|
|
deref_target_ty: Ty<'tcx>,
|
|
self_ty: Ty<'tcx>,
|
|
},
|
|
}
|
|
|
|
pub fn call_kind<'tcx>(
|
|
tcx: TyCtxt<'tcx>,
|
|
param_env: ParamEnv<'tcx>,
|
|
method_did: DefId,
|
|
method_substs: SubstsRef<'tcx>,
|
|
fn_call_span: Span,
|
|
from_hir_call: bool,
|
|
self_arg: Option<Ident>,
|
|
) -> CallKind<'tcx> {
|
|
let parent = tcx.opt_associated_item(method_did).and_then(|assoc| {
|
|
let container_id = assoc.container_id(tcx);
|
|
match assoc.container {
|
|
AssocItemContainer::ImplContainer => tcx.trait_id_of_impl(container_id),
|
|
AssocItemContainer::TraitContainer => Some(container_id),
|
|
}
|
|
});
|
|
|
|
let fn_call = parent.and_then(|p| {
|
|
lang_items::FN_TRAITS.iter().filter_map(|&l| tcx.lang_items().get(l)).find(|&id| id == p)
|
|
});
|
|
|
|
let operator = if !from_hir_call && let Some(p) = parent {
|
|
lang_items::OPERATORS.iter().filter_map(|&l| tcx.lang_items().get(l)).find(|&id| id == p)
|
|
} else {
|
|
None
|
|
};
|
|
|
|
let is_deref = !from_hir_call && tcx.is_diagnostic_item(sym::deref_method, method_did);
|
|
|
|
// Check for a 'special' use of 'self' -
|
|
// an FnOnce call, an operator (e.g. `<<`), or a
|
|
// deref coercion.
|
|
let kind = if let Some(trait_id) = fn_call {
|
|
Some(CallKind::FnCall { fn_trait_id: trait_id, self_ty: method_substs.type_at(0) })
|
|
} else if let Some(trait_id) = operator {
|
|
Some(CallKind::Operator { self_arg, trait_id, self_ty: method_substs.type_at(0) })
|
|
} else if is_deref {
|
|
let deref_target = tcx.get_diagnostic_item(sym::deref_target).and_then(|deref_target| {
|
|
Instance::resolve(tcx, param_env, deref_target, method_substs).transpose()
|
|
});
|
|
if let Some(Ok(instance)) = deref_target {
|
|
let deref_target_ty = instance.ty(tcx, param_env);
|
|
Some(CallKind::DerefCoercion {
|
|
deref_target: tcx.def_span(instance.def_id()),
|
|
deref_target_ty,
|
|
self_ty: method_substs.type_at(0),
|
|
})
|
|
} else {
|
|
None
|
|
}
|
|
} else {
|
|
None
|
|
};
|
|
|
|
kind.unwrap_or_else(|| {
|
|
// This isn't a 'special' use of `self`
|
|
debug!(?method_did, ?fn_call_span);
|
|
let desugaring = if Some(method_did) == tcx.lang_items().into_iter_fn()
|
|
&& fn_call_span.desugaring_kind() == Some(DesugaringKind::ForLoop)
|
|
{
|
|
Some((CallDesugaringKind::ForLoopIntoIter, method_substs.type_at(0)))
|
|
} else if fn_call_span.desugaring_kind() == Some(DesugaringKind::QuestionMark) {
|
|
if Some(method_did) == tcx.lang_items().branch_fn() {
|
|
Some((CallDesugaringKind::QuestionBranch, method_substs.type_at(0)))
|
|
} else if Some(method_did) == tcx.lang_items().from_residual_fn() {
|
|
Some((CallDesugaringKind::QuestionFromResidual, method_substs.type_at(0)))
|
|
} else {
|
|
None
|
|
}
|
|
} else if Some(method_did) == tcx.lang_items().from_output_fn()
|
|
&& fn_call_span.desugaring_kind() == Some(DesugaringKind::TryBlock)
|
|
{
|
|
Some((CallDesugaringKind::TryBlockFromOutput, method_substs.type_at(0)))
|
|
} else {
|
|
None
|
|
};
|
|
let parent_did = tcx.parent(method_did);
|
|
let parent_self_ty = (tcx.def_kind(parent_did) == rustc_hir::def::DefKind::Impl)
|
|
.then_some(parent_did)
|
|
.and_then(|did| match tcx.type_of(did).kind() {
|
|
ty::Adt(def, ..) => Some(def.did()),
|
|
_ => None,
|
|
});
|
|
let is_option_or_result = parent_self_ty.map_or(false, |def_id| {
|
|
matches!(tcx.get_diagnostic_name(def_id), Some(sym::Option | sym::Result))
|
|
});
|
|
CallKind::Normal { self_arg, desugaring, is_option_or_result }
|
|
})
|
|
}
|