1 //! Common logic for borrowck use-after-move errors when moved into a `fn(self)`,
2 //! as well as errors when attempting to call a non-const function in a const
5 use rustc_hir::def_id::DefId;
6 use rustc_hir::lang_items::LangItemGroup;
7 use rustc_middle::ty::subst::SubstsRef;
8 use rustc_middle::ty::{self, AssocItemContainer, DefIdTree, Instance, ParamEnv, Ty, TyCtxt};
9 use rustc_span::symbol::Ident;
10 use rustc_span::{sym, DesugaringKind, Span};
12 #[derive(Clone, Copy, PartialEq, Eq, Debug)]
13 pub enum CallDesugaringKind {
14 /// for _ in x {} calls x.into_iter()
16 /// x? calls x.branch()
18 /// x? calls type_of(x)::from_residual()
20 /// try { ..; x } calls type_of(x)::from_output(x)
24 impl CallDesugaringKind {
25 pub fn trait_def_id(self, tcx: TyCtxt<'_>) -> DefId {
27 Self::ForLoopIntoIter => tcx.get_diagnostic_item(sym::IntoIterator).unwrap(),
28 Self::QuestionBranch | Self::TryBlockFromOutput => {
29 tcx.lang_items().try_trait().unwrap()
31 Self::QuestionFromResidual => tcx.get_diagnostic_item(sym::FromResidual).unwrap(),
36 #[derive(Clone, Copy, PartialEq, Eq, Debug)]
37 pub enum CallKind<'tcx> {
38 /// A normal method call of the form `receiver.foo(a, b, c)`
40 self_arg: Option<Ident>,
41 desugaring: Option<(CallDesugaringKind, Ty<'tcx>)>,
42 /// Whether the self type of the method call has an `.as_ref()` method.
43 /// Used for better diagnostics.
44 is_option_or_result: bool,
46 /// A call to `Fn(..)::call(..)`, desugared from `my_closure(a, b, c)`
47 FnCall { fn_trait_id: DefId, self_ty: Ty<'tcx> },
48 /// A call to an operator trait, desuraged from operator syntax (e.g. `a << b`)
49 Operator { self_arg: Option<Ident>, trait_id: DefId, self_ty: Ty<'tcx> },
51 /// The `Span` of the `Target` associated type
52 /// in the `Deref` impl we are using.
54 /// The type `T::Deref` we are dereferencing to
55 deref_target_ty: Ty<'tcx>,
60 pub fn call_kind<'tcx>(
62 param_env: ParamEnv<'tcx>,
64 method_substs: SubstsRef<'tcx>,
67 self_arg: Option<Ident>,
69 let parent = tcx.opt_associated_item(method_did).and_then(|assoc| match assoc.container {
70 AssocItemContainer::ImplContainer(impl_did) => tcx.trait_id_of_impl(impl_did),
71 AssocItemContainer::TraitContainer(trait_did) => Some(trait_did),
75 .and_then(|p| tcx.lang_items().group(LangItemGroup::Fn).iter().find(|did| **did == p));
77 let operator = (!from_hir_call)
80 .and_then(|p| tcx.lang_items().group(LangItemGroup::Op).iter().find(|did| **did == p));
82 let is_deref = !from_hir_call && tcx.is_diagnostic_item(sym::deref_method, method_did);
84 // Check for a 'special' use of 'self' -
85 // an FnOnce call, an operator (e.g. `<<`), or a
87 let kind = if let Some(&trait_id) = fn_call {
88 Some(CallKind::FnCall { fn_trait_id: trait_id, self_ty: method_substs.type_at(0) })
89 } else if let Some(&trait_id) = operator {
90 Some(CallKind::Operator { self_arg, trait_id, self_ty: method_substs.type_at(0) })
92 let deref_target = tcx.get_diagnostic_item(sym::deref_target).and_then(|deref_target| {
93 Instance::resolve(tcx, param_env, deref_target, method_substs).transpose()
95 if let Some(Ok(instance)) = deref_target {
96 let deref_target_ty = instance.ty(tcx, param_env);
97 Some(CallKind::DerefCoercion {
98 deref_target: tcx.def_span(instance.def_id()),
100 self_ty: method_substs.type_at(0),
109 kind.unwrap_or_else(|| {
110 // This isn't a 'special' use of `self`
111 debug!(?method_did, ?fn_call_span);
112 let desugaring = if Some(method_did) == tcx.lang_items().into_iter_fn()
113 && fn_call_span.desugaring_kind() == Some(DesugaringKind::ForLoop)
115 Some((CallDesugaringKind::ForLoopIntoIter, method_substs.type_at(0)))
116 } else if fn_call_span.desugaring_kind() == Some(DesugaringKind::QuestionMark) {
117 if Some(method_did) == tcx.lang_items().branch_fn() {
118 Some((CallDesugaringKind::QuestionBranch, method_substs.type_at(0)))
119 } else if Some(method_did) == tcx.lang_items().from_residual_fn() {
120 Some((CallDesugaringKind::QuestionFromResidual, method_substs.type_at(0)))
124 } else if Some(method_did) == tcx.lang_items().from_output_fn()
125 && fn_call_span.desugaring_kind() == Some(DesugaringKind::TryBlock)
127 Some((CallDesugaringKind::TryBlockFromOutput, method_substs.type_at(0)))
131 let parent_self_ty = tcx
133 .filter(|did| tcx.def_kind(*did) == rustc_hir::def::DefKind::Impl)
134 .and_then(|did| match tcx.type_of(did).kind() {
135 ty::Adt(def, ..) => Some(def.did()),
138 let is_option_or_result = parent_self_ty.map_or(false, |def_id| {
139 matches!(tcx.get_diagnostic_name(def_id), Some(sym::Option | sym::Result))
141 CallKind::Normal { self_arg, desugaring, is_option_or_result }