1mod confirm;
6mod prelude_edition_lints;
7pub(crate) mod probe;
8mod suggest;
9
10use rustc_errors::{Applicability, Diag, SubdiagMessage};
11use rustc_hir as hir;
12use rustc_hir::def::{CtorOf, DefKind, Namespace};
13use rustc_hir::def_id::DefId;
14use rustc_infer::infer::{BoundRegionConversionTime, InferOk};
15use rustc_infer::traits::PredicateObligations;
16use rustc_middle::traits::ObligationCause;
17use rustc_middle::ty::{
18 self, GenericArgs, GenericArgsRef, GenericParamDefKind, Ty, TypeVisitableExt,
19};
20use rustc_middle::{bug, span_bug};
21use rustc_span::{ErrorGuaranteed, Ident, Span, Symbol};
22use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt;
23use rustc_trait_selection::traits::{self, NormalizeExt};
24use tracing::{debug, instrument};
25
26pub(crate) use self::MethodError::*;
27use self::probe::{IsSuggestion, ProbeScope};
28use crate::FnCtxt;
29
30#[derive(Clone, Copy, Debug)]
31pub(crate) struct MethodCallee<'tcx> {
32 pub def_id: DefId,
34 pub args: GenericArgsRef<'tcx>,
35
36 pub sig: ty::FnSig<'tcx>,
40}
41
42#[derive(Debug)]
43pub(crate) enum MethodError<'tcx> {
44 NoMatch(NoMatchData<'tcx>),
46
47 Ambiguity(Vec<CandidateSource>),
49
50 PrivateMatch(DefKind, DefId, Vec<DefId>),
53
54 IllegalSizedBound {
56 candidates: Vec<DefId>,
57 needs_mut: bool,
58 bound_span: Span,
59 self_expr: &'tcx hir::Expr<'tcx>,
60 },
61
62 BadReturnType,
64
65 ErrorReported(ErrorGuaranteed),
67}
68
69#[derive(Debug)]
72pub(crate) struct NoMatchData<'tcx> {
73 pub static_candidates: Vec<CandidateSource>,
74 pub unsatisfied_predicates:
75 Vec<(ty::Predicate<'tcx>, Option<ty::Predicate<'tcx>>, Option<ObligationCause<'tcx>>)>,
76 pub out_of_scope_traits: Vec<DefId>,
77 pub similar_candidate: Option<ty::AssocItem>,
78 pub mode: probe::Mode,
79}
80
81#[derive(Copy, Clone, Debug, Eq, PartialEq)]
84pub(crate) enum CandidateSource {
85 Impl(DefId),
86 Trait(DefId ),
87}
88
89impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
90 #[instrument(level = "debug", skip(self))]
92 pub(crate) fn method_exists_for_diagnostic(
93 &self,
94 method_name: Ident,
95 self_ty: Ty<'tcx>,
96 call_expr_id: hir::HirId,
97 return_type: Option<Ty<'tcx>>,
98 ) -> bool {
99 match self.probe_for_name(
100 probe::Mode::MethodCall,
101 method_name,
102 return_type,
103 IsSuggestion(true),
104 self_ty,
105 call_expr_id,
106 ProbeScope::TraitsInScope,
107 ) {
108 Ok(pick) => {
109 pick.maybe_emit_unstable_name_collision_hint(
110 self.tcx,
111 method_name.span,
112 call_expr_id,
113 );
114 true
115 }
116 Err(NoMatch(..)) => false,
117 Err(Ambiguity(..)) => true,
118 Err(PrivateMatch(..)) => false,
119 Err(IllegalSizedBound { .. }) => true,
120 Err(BadReturnType) => true,
121 Err(ErrorReported(_)) => false,
122 }
123 }
124
125 #[instrument(level = "debug", skip(self, err, call_expr))]
127 pub(crate) fn suggest_method_call(
128 &self,
129 err: &mut Diag<'_>,
130 msg: impl Into<SubdiagMessage> + std::fmt::Debug,
131 method_name: Ident,
132 self_ty: Ty<'tcx>,
133 call_expr: &hir::Expr<'tcx>,
134 span: Option<Span>,
135 ) {
136 let params = self
137 .lookup_probe_for_diagnostic(
138 method_name,
139 self_ty,
140 call_expr,
141 ProbeScope::TraitsInScope,
142 None,
143 )
144 .map(|pick| {
145 let sig = self.tcx.fn_sig(pick.item.def_id);
146 sig.skip_binder().inputs().skip_binder().len().saturating_sub(1)
147 })
148 .unwrap_or(0);
149
150 let sugg_span = span.unwrap_or(call_expr.span).shrink_to_hi();
152 let (suggestion, applicability) = (
153 format!("({})", (0..params).map(|_| "_").collect::<Vec<_>>().join(", ")),
154 if params > 0 { Applicability::HasPlaceholders } else { Applicability::MaybeIncorrect },
155 );
156
157 err.span_suggestion_verbose(sugg_span, msg, suggestion, applicability);
158 }
159
160 #[instrument(level = "debug", skip(self))]
176 pub(crate) fn lookup_method(
177 &self,
178 self_ty: Ty<'tcx>,
179 segment: &'tcx hir::PathSegment<'tcx>,
180 span: Span,
181 call_expr: &'tcx hir::Expr<'tcx>,
182 self_expr: &'tcx hir::Expr<'tcx>,
183 args: &'tcx [hir::Expr<'tcx>],
184 ) -> Result<MethodCallee<'tcx>, MethodError<'tcx>> {
185 let scope = if let Some(only_method) = segment.res.opt_def_id() {
186 ProbeScope::Single(only_method)
187 } else {
188 ProbeScope::TraitsInScope
189 };
190
191 let pick = self.lookup_probe(segment.ident, self_ty, call_expr, scope)?;
192
193 self.lint_edition_dependent_dot_call(
194 self_ty, segment, span, call_expr, self_expr, &pick, args,
195 );
196
197 for &import_id in &pick.import_ids {
200 debug!("used_trait_import: {:?}", import_id);
201 self.typeck_results.borrow_mut().used_trait_imports.insert(import_id);
202 }
203
204 self.tcx.check_stability(pick.item.def_id, Some(call_expr.hir_id), span, None);
205
206 let result = self.confirm_method(span, self_expr, call_expr, self_ty, &pick, segment);
207 debug!("result = {:?}", result);
208
209 if let Some(span) = result.illegal_sized_bound {
210 let mut needs_mut = false;
211 if let ty::Ref(region, t_type, mutability) = self_ty.kind() {
212 let trait_type = Ty::new_ref(self.tcx, *region, *t_type, mutability.invert());
213 match self.lookup_probe(
215 segment.ident,
216 trait_type,
217 call_expr,
218 ProbeScope::TraitsInScope,
219 ) {
220 Ok(ref new_pick) if pick.differs_from(new_pick) => {
221 needs_mut = new_pick.self_ty.ref_mutability() != self_ty.ref_mutability();
222 }
223 _ => {}
224 }
225 }
226
227 let candidates = match self.lookup_probe_for_diagnostic(
229 segment.ident,
230 self_ty,
231 call_expr,
232 ProbeScope::AllTraits,
233 None,
234 ) {
235 Ok(ref new_pick) if pick.differs_from(new_pick) => {
237 vec![new_pick.item.container_id(self.tcx)]
238 }
239 Err(Ambiguity(ref sources)) => sources
240 .iter()
241 .filter_map(|source| {
242 match *source {
243 CandidateSource::Impl(def) => self.tcx.trait_id_of_impl(def),
246 CandidateSource::Trait(_) => None,
247 }
248 })
249 .collect(),
250 _ => Vec::new(),
251 };
252
253 return Err(IllegalSizedBound { candidates, needs_mut, bound_span: span, self_expr });
254 }
255
256 Ok(result.callee)
257 }
258
259 pub(crate) fn lookup_method_for_diagnostic(
260 &self,
261 self_ty: Ty<'tcx>,
262 segment: &hir::PathSegment<'tcx>,
263 span: Span,
264 call_expr: &'tcx hir::Expr<'tcx>,
265 self_expr: &'tcx hir::Expr<'tcx>,
266 ) -> Result<MethodCallee<'tcx>, MethodError<'tcx>> {
267 let pick = self.lookup_probe_for_diagnostic(
268 segment.ident,
269 self_ty,
270 call_expr,
271 ProbeScope::TraitsInScope,
272 None,
273 )?;
274
275 Ok(self
276 .confirm_method_for_diagnostic(span, self_expr, call_expr, self_ty, &pick, segment)
277 .callee)
278 }
279
280 #[instrument(level = "debug", skip(self, call_expr))]
281 pub(crate) fn lookup_probe(
282 &self,
283 method_name: Ident,
284 self_ty: Ty<'tcx>,
285 call_expr: &hir::Expr<'_>,
286 scope: ProbeScope,
287 ) -> probe::PickResult<'tcx> {
288 let pick = self.probe_for_name(
289 probe::Mode::MethodCall,
290 method_name,
291 None,
292 IsSuggestion(false),
293 self_ty,
294 call_expr.hir_id,
295 scope,
296 )?;
297 pick.maybe_emit_unstable_name_collision_hint(self.tcx, method_name.span, call_expr.hir_id);
298 Ok(pick)
299 }
300
301 pub(crate) fn lookup_probe_for_diagnostic(
302 &self,
303 method_name: Ident,
304 self_ty: Ty<'tcx>,
305 call_expr: &hir::Expr<'_>,
306 scope: ProbeScope,
307 return_type: Option<Ty<'tcx>>,
308 ) -> probe::PickResult<'tcx> {
309 let pick = self.probe_for_name(
310 probe::Mode::MethodCall,
311 method_name,
312 return_type,
313 IsSuggestion(true),
314 self_ty,
315 call_expr.hir_id,
316 scope,
317 )?;
318 Ok(pick)
319 }
320}
321
322#[derive(Debug)]
334pub(super) enum TreatNotYetDefinedOpaques {
335 AsInfer,
336 AsRigid,
337}
338
339impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
340 #[instrument(level = "debug", skip(self))]
346 pub(super) fn lookup_method_for_operator(
347 &self,
348 cause: ObligationCause<'tcx>,
349 method_name: Symbol,
350 trait_def_id: DefId,
351 self_ty: Ty<'tcx>,
352 opt_rhs_ty: Option<Ty<'tcx>>,
353 treat_opaques: TreatNotYetDefinedOpaques,
354 ) -> Option<InferOk<'tcx, MethodCallee<'tcx>>> {
355 let args = GenericArgs::for_item(self.tcx, trait_def_id, |param, _| match param.kind {
357 GenericParamDefKind::Lifetime | GenericParamDefKind::Const { .. } => {
358 unreachable!("did not expect operator trait to have lifetime/const")
359 }
360 GenericParamDefKind::Type { .. } => {
361 if param.index == 0 {
362 self_ty.into()
363 } else if let Some(rhs_ty) = opt_rhs_ty {
364 assert_eq!(param.index, 1, "did not expect >1 param on operator trait");
365 rhs_ty.into()
366 } else {
367 self.var_for_def(cause.span, param)
371 }
372 }
373 });
374
375 let obligation = traits::Obligation::new(
376 self.tcx,
377 cause,
378 self.param_env,
379 ty::TraitRef::new_from_args(self.tcx, trait_def_id, args),
380 );
381
382 let matches_trait = match treat_opaques {
384 TreatNotYetDefinedOpaques::AsInfer => self.predicate_may_hold(&obligation),
385 TreatNotYetDefinedOpaques::AsRigid => {
386 self.predicate_may_hold_opaque_types_jank(&obligation)
387 }
388 };
389
390 if !matches_trait {
391 debug!("--> Cannot match obligation");
392 return None;
394 }
395
396 let tcx = self.tcx;
399 let Some(method_item) =
402 self.associated_value(trait_def_id, Ident::with_dummy_span(method_name))
403 else {
404 bug!("expected associated item for operator trait")
405 };
406
407 let def_id = method_item.def_id;
408 if !method_item.is_fn() {
409 span_bug!(
410 tcx.def_span(def_id),
411 "expected `{method_name}` to be an associated function"
412 );
413 }
414
415 debug!("lookup_in_trait_adjusted: method_item={:?}", method_item);
416 let mut obligations = PredicateObligations::new();
417
418 let fn_sig = tcx.fn_sig(def_id).instantiate(self.tcx, args);
425 let fn_sig = self.instantiate_binder_with_fresh_vars(
426 obligation.cause.span,
427 BoundRegionConversionTime::FnCall,
428 fn_sig,
429 );
430
431 let InferOk { value: fn_sig, obligations: o } =
432 self.at(&obligation.cause, self.param_env).normalize(fn_sig);
433 obligations.extend(o);
434
435 let bounds = self.tcx.predicates_of(def_id).instantiate(self.tcx, args);
444
445 let InferOk { value: bounds, obligations: o } =
446 self.at(&obligation.cause, self.param_env).normalize(bounds);
447 obligations.extend(o);
448 assert!(!bounds.has_escaping_bound_vars());
449
450 let predicates_cause = obligation.cause.clone();
451 obligations.extend(traits::predicates_for_generics(
452 move |_, _| predicates_cause.clone(),
453 self.param_env,
454 bounds,
455 ));
456
457 debug!(
459 "lookup_method_in_trait: matched method fn_sig={:?} obligation={:?}",
460 fn_sig, obligation
461 );
462 for ty in fn_sig.inputs_and_output {
463 obligations.push(traits::Obligation::new(
464 tcx,
465 obligation.cause.clone(),
466 self.param_env,
467 ty::Binder::dummy(ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(ty.into()))),
468 ));
469 }
470
471 let callee = MethodCallee { def_id, args, sig: fn_sig };
472 debug!("callee = {:?}", callee);
473
474 Some(InferOk { obligations, value: callee })
475 }
476
477 #[instrument(level = "debug", skip(self), ret)]
494 pub(crate) fn resolve_fully_qualified_call(
495 &self,
496 span: Span,
497 method_name: Ident,
498 self_ty: Ty<'tcx>,
499 self_ty_span: Span,
500 expr_id: hir::HirId,
501 ) -> Result<(DefKind, DefId), MethodError<'tcx>> {
502 let tcx = self.tcx;
503
504 let mut struct_variant = None;
506 if let ty::Adt(adt_def, _) = self_ty.kind() {
507 if adt_def.is_enum() {
508 let variant_def = adt_def
509 .variants()
510 .iter()
511 .find(|vd| tcx.hygienic_eq(method_name, vd.ident(tcx), adt_def.did()));
512 if let Some(variant_def) = variant_def {
513 if let Some((ctor_kind, ctor_def_id)) = variant_def.ctor {
514 tcx.check_stability(
515 ctor_def_id,
516 Some(expr_id),
517 span,
518 Some(method_name.span),
519 );
520 return Ok((DefKind::Ctor(CtorOf::Variant, ctor_kind), ctor_def_id));
521 } else {
522 struct_variant = Some((DefKind::Variant, variant_def.def_id));
523 }
524 }
525 }
526 }
527
528 let pick = self.probe_for_name(
529 probe::Mode::Path,
530 method_name,
531 None,
532 IsSuggestion(false),
533 self_ty,
534 expr_id,
535 ProbeScope::TraitsInScope,
536 );
537 let pick = match (pick, struct_variant) {
538 (Err(_), Some(res)) => return Ok(res),
540 (pick, _) => pick?,
541 };
542
543 pick.maybe_emit_unstable_name_collision_hint(self.tcx, span, expr_id);
544
545 self.lint_fully_qualified_call_from_2018(
546 span,
547 method_name,
548 self_ty,
549 self_ty_span,
550 expr_id,
551 &pick,
552 );
553
554 debug!(?pick);
555 {
556 let mut typeck_results = self.typeck_results.borrow_mut();
557 for import_id in pick.import_ids {
558 debug!(used_trait_import=?import_id);
559 typeck_results.used_trait_imports.insert(import_id);
560 }
561 }
562
563 let def_kind = pick.item.as_def_kind();
564 tcx.check_stability(pick.item.def_id, Some(expr_id), span, Some(method_name.span));
565 Ok((def_kind, pick.item.def_id))
566 }
567
568 fn associated_value(&self, def_id: DefId, item_ident: Ident) -> Option<ty::AssocItem> {
571 self.tcx
572 .associated_items(def_id)
573 .find_by_ident_and_namespace(self.tcx, item_ident, Namespace::ValueNS, def_id)
574 .copied()
575 }
576}