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