]>
Commit | Line | Data |
---|---|---|
5099ac24 FG |
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 | |
3 | //! context. | |
4 | ||
5 | use rustc_hir::def_id::DefId; | |
487cf647 | 6 | use rustc_hir::{lang_items, LangItem}; |
5099ac24 FG |
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}; | |
11 | ||
12 | #[derive(Clone, Copy, PartialEq, Eq, Debug)] | |
13 | pub enum CallDesugaringKind { | |
14 | /// for _ in x {} calls x.into_iter() | |
15 | ForLoopIntoIter, | |
16 | /// x? calls x.branch() | |
17 | QuestionBranch, | |
18 | /// x? calls type_of(x)::from_residual() | |
19 | QuestionFromResidual, | |
20 | /// try { ..; x } calls type_of(x)::from_output(x) | |
21 | TryBlockFromOutput, | |
22 | } | |
23 | ||
24 | impl CallDesugaringKind { | |
25 | pub fn trait_def_id(self, tcx: TyCtxt<'_>) -> DefId { | |
26 | match self { | |
27 | Self::ForLoopIntoIter => tcx.get_diagnostic_item(sym::IntoIterator).unwrap(), | |
28 | Self::QuestionBranch | Self::TryBlockFromOutput => { | |
487cf647 | 29 | tcx.require_lang_item(LangItem::Try, None) |
5099ac24 FG |
30 | } |
31 | Self::QuestionFromResidual => tcx.get_diagnostic_item(sym::FromResidual).unwrap(), | |
32 | } | |
33 | } | |
34 | } | |
35 | ||
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)` | |
39 | Normal { | |
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, | |
45 | }, | |
46 | /// A call to `Fn(..)::call(..)`, desugared from `my_closure(a, b, c)` | |
47 | FnCall { fn_trait_id: DefId, self_ty: Ty<'tcx> }, | |
5e7ed085 | 48 | /// A call to an operator trait, desugared from operator syntax (e.g. `a << b`) |
5099ac24 FG |
49 | Operator { self_arg: Option<Ident>, trait_id: DefId, self_ty: Ty<'tcx> }, |
50 | DerefCoercion { | |
51 | /// The `Span` of the `Target` associated type | |
52 | /// in the `Deref` impl we are using. | |
53 | deref_target: Span, | |
54 | /// The type `T::Deref` we are dereferencing to | |
55 | deref_target_ty: Ty<'tcx>, | |
56 | self_ty: Ty<'tcx>, | |
57 | }, | |
58 | } | |
59 | ||
60 | pub fn call_kind<'tcx>( | |
61 | tcx: TyCtxt<'tcx>, | |
62 | param_env: ParamEnv<'tcx>, | |
63 | method_did: DefId, | |
64 | method_substs: SubstsRef<'tcx>, | |
65 | fn_call_span: Span, | |
66 | from_hir_call: bool, | |
67 | self_arg: Option<Ident>, | |
68 | ) -> CallKind<'tcx> { | |
064997fb FG |
69 | let parent = tcx.opt_associated_item(method_did).and_then(|assoc| { |
70 | let container_id = assoc.container_id(tcx); | |
71 | match assoc.container { | |
72 | AssocItemContainer::ImplContainer => tcx.trait_id_of_impl(container_id), | |
73 | AssocItemContainer::TraitContainer => Some(container_id), | |
74 | } | |
5099ac24 FG |
75 | }); |
76 | ||
487cf647 FG |
77 | let fn_call = parent.and_then(|p| { |
78 | lang_items::FN_TRAITS.iter().filter_map(|&l| tcx.lang_items().get(l)).find(|&id| id == p) | |
79 | }); | |
5099ac24 | 80 | |
487cf647 FG |
81 | let operator = if !from_hir_call && let Some(p) = parent { |
82 | lang_items::OPERATORS.iter().filter_map(|&l| tcx.lang_items().get(l)).find(|&id| id == p) | |
83 | } else { | |
84 | None | |
85 | }; | |
5099ac24 FG |
86 | |
87 | let is_deref = !from_hir_call && tcx.is_diagnostic_item(sym::deref_method, method_did); | |
88 | ||
89 | // Check for a 'special' use of 'self' - | |
90 | // an FnOnce call, an operator (e.g. `<<`), or a | |
91 | // deref coercion. | |
487cf647 | 92 | let kind = if let Some(trait_id) = fn_call { |
5099ac24 | 93 | Some(CallKind::FnCall { fn_trait_id: trait_id, self_ty: method_substs.type_at(0) }) |
487cf647 | 94 | } else if let Some(trait_id) = operator { |
5099ac24 FG |
95 | Some(CallKind::Operator { self_arg, trait_id, self_ty: method_substs.type_at(0) }) |
96 | } else if is_deref { | |
97 | let deref_target = tcx.get_diagnostic_item(sym::deref_target).and_then(|deref_target| { | |
98 | Instance::resolve(tcx, param_env, deref_target, method_substs).transpose() | |
99 | }); | |
100 | if let Some(Ok(instance)) = deref_target { | |
101 | let deref_target_ty = instance.ty(tcx, param_env); | |
102 | Some(CallKind::DerefCoercion { | |
103 | deref_target: tcx.def_span(instance.def_id()), | |
104 | deref_target_ty, | |
105 | self_ty: method_substs.type_at(0), | |
106 | }) | |
107 | } else { | |
108 | None | |
109 | } | |
110 | } else { | |
111 | None | |
112 | }; | |
113 | ||
114 | kind.unwrap_or_else(|| { | |
115 | // This isn't a 'special' use of `self` | |
116 | debug!(?method_did, ?fn_call_span); | |
117 | let desugaring = if Some(method_did) == tcx.lang_items().into_iter_fn() | |
118 | && fn_call_span.desugaring_kind() == Some(DesugaringKind::ForLoop) | |
119 | { | |
120 | Some((CallDesugaringKind::ForLoopIntoIter, method_substs.type_at(0))) | |
121 | } else if fn_call_span.desugaring_kind() == Some(DesugaringKind::QuestionMark) { | |
122 | if Some(method_did) == tcx.lang_items().branch_fn() { | |
123 | Some((CallDesugaringKind::QuestionBranch, method_substs.type_at(0))) | |
124 | } else if Some(method_did) == tcx.lang_items().from_residual_fn() { | |
125 | Some((CallDesugaringKind::QuestionFromResidual, method_substs.type_at(0))) | |
126 | } else { | |
127 | None | |
128 | } | |
129 | } else if Some(method_did) == tcx.lang_items().from_output_fn() | |
130 | && fn_call_span.desugaring_kind() == Some(DesugaringKind::TryBlock) | |
131 | { | |
132 | Some((CallDesugaringKind::TryBlockFromOutput, method_substs.type_at(0))) | |
133 | } else { | |
134 | None | |
135 | }; | |
04454e1e FG |
136 | let parent_did = tcx.parent(method_did); |
137 | let parent_self_ty = (tcx.def_kind(parent_did) == rustc_hir::def::DefKind::Impl) | |
138 | .then_some(parent_did) | |
5099ac24 | 139 | .and_then(|did| match tcx.type_of(did).kind() { |
5e7ed085 | 140 | ty::Adt(def, ..) => Some(def.did()), |
5099ac24 FG |
141 | _ => None, |
142 | }); | |
143 | let is_option_or_result = parent_self_ty.map_or(false, |def_id| { | |
144 | matches!(tcx.get_diagnostic_name(def_id), Some(sym::Option | sym::Result)) | |
145 | }); | |
146 | CallKind::Normal { self_arg, desugaring, is_option_or_result } | |
147 | }) | |
148 | } |