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::{AssocContainer, 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 AssocContainer::InherentImpl => None,
80 AssocContainer::TraitImpl(_) => tcx.trait_id_of_impl(container_id),
81 AssocContainer::Trait => Some(container_id),
82 }
83 });
84
85 let fn_call = parent.filter(|&p| tcx.fn_trait_kind_from_def_id(p).is_some());
86
87 let operator = if !from_hir_call && let Some(p) = parent {
88 lang_items::OPERATORS.iter().filter_map(|&l| tcx.lang_items().get(l)).find(|&id| id == p)
89 } else {
90 None
91 };
92
93 if let Some(trait_id) = fn_call {
97 return CallKind::FnCall { fn_trait_id: trait_id, self_ty: method_args.type_at(0) };
98 } else if let Some(trait_id) = operator {
99 return CallKind::Operator { self_arg, trait_id, self_ty: method_args.type_at(0) };
100 } else if !from_hir_call && tcx.is_diagnostic_item(sym::deref_method, method_did) {
101 let deref_target_def_id =
102 tcx.get_diagnostic_item(sym::deref_target).expect("deref method but no deref target");
103 let deref_target_ty = tcx.normalize_erasing_regions(
104 typing_env,
105 Ty::new_projection(tcx, deref_target_def_id, method_args),
106 );
107 let deref_target_span = if let Ok(Some(instance)) =
108 Instance::try_resolve(tcx, typing_env, method_did, method_args)
109 && let instance_parent_def_id = tcx.parent(instance.def_id())
110 && matches!(tcx.def_kind(instance_parent_def_id), DefKind::Impl { .. })
111 && let Ok(instance) =
112 specialization_graph::assoc_def(tcx, instance_parent_def_id, deref_target_def_id)
113 && instance.is_final()
114 {
115 Some(tcx.def_span(instance.item.def_id))
116 } else {
117 None
118 };
119 return CallKind::DerefCoercion {
120 deref_target_ty,
121 deref_target_span,
122 self_ty: method_args.type_at(0),
123 };
124 }
125
126 debug!(?method_did, ?fn_call_span);
128 let desugaring = if tcx.is_lang_item(method_did, LangItem::IntoIterIntoIter)
129 && fn_call_span.desugaring_kind() == Some(DesugaringKind::ForLoop)
130 {
131 Some((CallDesugaringKind::ForLoopIntoIter, method_args.type_at(0)))
132 } else if tcx.is_lang_item(method_did, LangItem::IteratorNext)
133 && fn_call_span.desugaring_kind() == Some(DesugaringKind::ForLoop)
134 {
135 Some((CallDesugaringKind::ForLoopNext, method_args.type_at(0)))
136 } else if fn_call_span.desugaring_kind() == Some(DesugaringKind::QuestionMark) {
137 if tcx.is_lang_item(method_did, LangItem::TryTraitBranch) {
138 Some((CallDesugaringKind::QuestionBranch, method_args.type_at(0)))
139 } else if tcx.is_lang_item(method_did, LangItem::TryTraitFromResidual) {
140 Some((CallDesugaringKind::QuestionFromResidual, method_args.type_at(0)))
141 } else {
142 None
143 }
144 } else if tcx.is_lang_item(method_did, LangItem::TryTraitFromOutput)
145 && fn_call_span.desugaring_kind() == Some(DesugaringKind::TryBlock)
146 {
147 Some((CallDesugaringKind::TryBlockFromOutput, method_args.type_at(0)))
148 } else if fn_call_span.is_desugaring(DesugaringKind::Await) {
149 Some((CallDesugaringKind::Await, method_args.type_at(0)))
150 } else {
151 None
152 };
153 CallKind::Normal { self_arg, desugaring, method_did, method_args }
154}