rustc_trait_selection/error_reporting/traits/
call_kind.rs1use rustc_hir::def::DefKind;
6use rustc_hir::def_id::DefId;
7use rustc_hir::{LangItem, lang_items};
8use rustc_middle::ty::{AssocItemContainer, GenericArgsRef, Instance, Ty, TyCtxt, TypingEnv};
9use rustc_span::{DUMMY_SP, DesugaringKind, Ident, Span, sym};
10use tracing::debug;
11
12use crate::traits::specialization_graph;
13
14#[derive(Clone, Copy, PartialEq, Eq, Debug)]
15pub enum CallDesugaringKind {
16 ForLoopIntoIter,
18 ForLoopNext,
20 QuestionBranch,
22 QuestionFromResidual,
24 TryBlockFromOutput,
26 Await,
28}
29
30impl CallDesugaringKind {
31 pub fn trait_def_id(self, tcx: TyCtxt<'_>) -> DefId {
32 match self {
33 Self::ForLoopIntoIter => tcx.get_diagnostic_item(sym::IntoIterator).unwrap(),
34 Self::ForLoopNext => tcx.require_lang_item(LangItem::Iterator, DUMMY_SP),
35 Self::QuestionBranch | Self::TryBlockFromOutput => {
36 tcx.require_lang_item(LangItem::Try, DUMMY_SP)
37 }
38 Self::QuestionFromResidual => tcx.get_diagnostic_item(sym::FromResidual).unwrap(),
39 Self::Await => tcx.get_diagnostic_item(sym::IntoFuture).unwrap(),
40 }
41 }
42}
43
44#[derive(Clone, Copy, PartialEq, Eq, Debug)]
45pub enum CallKind<'tcx> {
46 Normal {
48 self_arg: Option<Ident>,
49 desugaring: Option<(CallDesugaringKind, Ty<'tcx>)>,
50 method_did: DefId,
51 method_args: GenericArgsRef<'tcx>,
52 },
53 FnCall { fn_trait_id: DefId, self_ty: Ty<'tcx> },
55 Operator { self_arg: Option<Ident>, trait_id: DefId, self_ty: Ty<'tcx> },
57 DerefCoercion {
58 deref_target_span: Option<Span>,
61 deref_target_ty: Ty<'tcx>,
63 self_ty: Ty<'tcx>,
64 },
65}
66
67pub fn call_kind<'tcx>(
68 tcx: TyCtxt<'tcx>,
69 typing_env: TypingEnv<'tcx>,
70 method_did: DefId,
71 method_args: GenericArgsRef<'tcx>,
72 fn_call_span: Span,
73 from_hir_call: bool,
74 self_arg: Option<Ident>,
75) -> CallKind<'tcx> {
76 let parent = tcx.opt_associated_item(method_did).and_then(|assoc| {
77 let container_id = assoc.container_id(tcx);
78 match assoc.container {
79 AssocItemContainer::Impl => tcx.trait_id_of_impl(container_id),
80 AssocItemContainer::Trait => Some(container_id),
81 }
82 });
83
84 let fn_call = parent.filter(|&p| tcx.fn_trait_kind_from_def_id(p).is_some());
85
86 let operator = if !from_hir_call && let Some(p) = parent {
87 lang_items::OPERATORS.iter().filter_map(|&l| tcx.lang_items().get(l)).find(|&id| id == p)
88 } else {
89 None
90 };
91
92 if let Some(trait_id) = fn_call {
96 return CallKind::FnCall { fn_trait_id: trait_id, self_ty: method_args.type_at(0) };
97 } else if let Some(trait_id) = operator {
98 return CallKind::Operator { self_arg, trait_id, self_ty: method_args.type_at(0) };
99 } else if !from_hir_call && tcx.is_diagnostic_item(sym::deref_method, method_did) {
100 let deref_target_def_id =
101 tcx.get_diagnostic_item(sym::deref_target).expect("deref method but no deref target");
102 let deref_target_ty = tcx.normalize_erasing_regions(
103 typing_env,
104 Ty::new_projection(tcx, deref_target_def_id, method_args),
105 );
106 let deref_target_span = if let Ok(Some(instance)) =
107 Instance::try_resolve(tcx, typing_env, method_did, method_args)
108 && let instance_parent_def_id = tcx.parent(instance.def_id())
109 && matches!(tcx.def_kind(instance_parent_def_id), DefKind::Impl { .. })
110 && let Ok(instance) =
111 specialization_graph::assoc_def(tcx, instance_parent_def_id, deref_target_def_id)
112 && instance.is_final()
113 {
114 Some(tcx.def_span(instance.item.def_id))
115 } else {
116 None
117 };
118 return CallKind::DerefCoercion {
119 deref_target_ty,
120 deref_target_span,
121 self_ty: method_args.type_at(0),
122 };
123 }
124
125 debug!(?method_did, ?fn_call_span);
127 let desugaring = if tcx.is_lang_item(method_did, LangItem::IntoIterIntoIter)
128 && fn_call_span.desugaring_kind() == Some(DesugaringKind::ForLoop)
129 {
130 Some((CallDesugaringKind::ForLoopIntoIter, method_args.type_at(0)))
131 } else if tcx.is_lang_item(method_did, LangItem::IteratorNext)
132 && fn_call_span.desugaring_kind() == Some(DesugaringKind::ForLoop)
133 {
134 Some((CallDesugaringKind::ForLoopNext, method_args.type_at(0)))
135 } else if fn_call_span.desugaring_kind() == Some(DesugaringKind::QuestionMark) {
136 if tcx.is_lang_item(method_did, LangItem::TryTraitBranch) {
137 Some((CallDesugaringKind::QuestionBranch, method_args.type_at(0)))
138 } else if tcx.is_lang_item(method_did, LangItem::TryTraitFromResidual) {
139 Some((CallDesugaringKind::QuestionFromResidual, method_args.type_at(0)))
140 } else {
141 None
142 }
143 } else if tcx.is_lang_item(method_did, LangItem::TryTraitFromOutput)
144 && fn_call_span.desugaring_kind() == Some(DesugaringKind::TryBlock)
145 {
146 Some((CallDesugaringKind::TryBlockFromOutput, method_args.type_at(0)))
147 } else if fn_call_span.is_desugaring(DesugaringKind::Await) {
148 Some((CallDesugaringKind::Await, method_args.type_at(0)))
149 } else {
150 None
151 };
152 CallKind::Normal { self_arg, desugaring, method_did, method_args }
153}