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 }