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) => false,
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 #[instrument(level = "debug", skip(self))]
327 pub(super) fn lookup_method_for_operator(
328 &self,
329 cause: ObligationCause<'tcx>,
330 method_name: Symbol,
331 trait_def_id: DefId,
332 self_ty: Ty<'tcx>,
333 opt_rhs_ty: Option<Ty<'tcx>>,
334 ) -> Option<InferOk<'tcx, MethodCallee<'tcx>>> {
335 let args = GenericArgs::for_item(self.tcx, trait_def_id, |param, _| match param.kind {
337 GenericParamDefKind::Lifetime | GenericParamDefKind::Const { .. } => {
338 unreachable!("did not expect operator trait to have lifetime/const")
339 }
340 GenericParamDefKind::Type { .. } => {
341 if param.index == 0 {
342 self_ty.into()
343 } else if let Some(rhs_ty) = opt_rhs_ty {
344 assert_eq!(param.index, 1, "did not expect >1 param on operator trait");
345 rhs_ty.into()
346 } else {
347 self.var_for_def(cause.span, param)
351 }
352 }
353 });
354
355 let obligation = traits::Obligation::new(
356 self.tcx,
357 cause,
358 self.param_env,
359 ty::TraitRef::new_from_args(self.tcx, trait_def_id, args),
360 );
361
362 if !self.predicate_may_hold(&obligation) {
364 debug!("--> Cannot match obligation");
365 return None;
367 }
368
369 let tcx = self.tcx;
372 let Some(method_item) =
375 self.associated_value(trait_def_id, Ident::with_dummy_span(method_name))
376 else {
377 bug!("expected associated item for operator trait")
378 };
379
380 let def_id = method_item.def_id;
381 if !method_item.is_fn() {
382 span_bug!(
383 tcx.def_span(def_id),
384 "expected `{method_name}` to be an associated function"
385 );
386 }
387
388 debug!("lookup_in_trait_adjusted: method_item={:?}", method_item);
389 let mut obligations = PredicateObligations::new();
390
391 let fn_sig = tcx.fn_sig(def_id).instantiate(self.tcx, args);
398 let fn_sig = self.instantiate_binder_with_fresh_vars(
399 obligation.cause.span,
400 BoundRegionConversionTime::FnCall,
401 fn_sig,
402 );
403
404 let InferOk { value: fn_sig, obligations: o } =
405 self.at(&obligation.cause, self.param_env).normalize(fn_sig);
406 obligations.extend(o);
407
408 let bounds = self.tcx.predicates_of(def_id).instantiate(self.tcx, args);
417
418 let InferOk { value: bounds, obligations: o } =
419 self.at(&obligation.cause, self.param_env).normalize(bounds);
420 obligations.extend(o);
421 assert!(!bounds.has_escaping_bound_vars());
422
423 let predicates_cause = obligation.cause.clone();
424 obligations.extend(traits::predicates_for_generics(
425 move |_, _| predicates_cause.clone(),
426 self.param_env,
427 bounds,
428 ));
429
430 let method_ty = Ty::new_fn_ptr(tcx, ty::Binder::dummy(fn_sig));
432 debug!(
433 "lookup_method_in_trait: matched method method_ty={:?} obligation={:?}",
434 method_ty, obligation
435 );
436 obligations.push(traits::Obligation::new(
437 tcx,
438 obligation.cause,
439 self.param_env,
440 ty::Binder::dummy(ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(
441 method_ty.into(),
442 ))),
443 ));
444
445 let callee = MethodCallee { def_id, args, sig: fn_sig };
446 debug!("callee = {:?}", callee);
447
448 Some(InferOk { obligations, value: callee })
449 }
450
451 #[instrument(level = "debug", skip(self), ret)]
468 pub(crate) fn resolve_fully_qualified_call(
469 &self,
470 span: Span,
471 method_name: Ident,
472 self_ty: Ty<'tcx>,
473 self_ty_span: Span,
474 expr_id: hir::HirId,
475 ) -> Result<(DefKind, DefId), MethodError<'tcx>> {
476 let tcx = self.tcx;
477
478 let mut struct_variant = None;
480 if let ty::Adt(adt_def, _) = self_ty.kind() {
481 if adt_def.is_enum() {
482 let variant_def = adt_def
483 .variants()
484 .iter()
485 .find(|vd| tcx.hygienic_eq(method_name, vd.ident(tcx), adt_def.did()));
486 if let Some(variant_def) = variant_def {
487 if let Some((ctor_kind, ctor_def_id)) = variant_def.ctor {
488 tcx.check_stability(
489 ctor_def_id,
490 Some(expr_id),
491 span,
492 Some(method_name.span),
493 );
494 return Ok((DefKind::Ctor(CtorOf::Variant, ctor_kind), ctor_def_id));
495 } else {
496 struct_variant = Some((DefKind::Variant, variant_def.def_id));
497 }
498 }
499 }
500 }
501
502 let pick = self.probe_for_name(
503 probe::Mode::Path,
504 method_name,
505 None,
506 IsSuggestion(false),
507 self_ty,
508 expr_id,
509 ProbeScope::TraitsInScope,
510 );
511 let pick = match (pick, struct_variant) {
512 (Err(_), Some(res)) => return Ok(res),
514 (pick, _) => pick?,
515 };
516
517 pick.maybe_emit_unstable_name_collision_hint(self.tcx, span, expr_id);
518
519 self.lint_fully_qualified_call_from_2018(
520 span,
521 method_name,
522 self_ty,
523 self_ty_span,
524 expr_id,
525 &pick,
526 );
527
528 debug!(?pick);
529 {
530 let mut typeck_results = self.typeck_results.borrow_mut();
531 for import_id in pick.import_ids {
532 debug!(used_trait_import=?import_id);
533 typeck_results.used_trait_imports.insert(import_id);
534 }
535 }
536
537 let def_kind = pick.item.as_def_kind();
538 tcx.check_stability(pick.item.def_id, Some(expr_id), span, Some(method_name.span));
539 Ok((def_kind, pick.item.def_id))
540 }
541
542 fn associated_value(&self, def_id: DefId, item_ident: Ident) -> Option<ty::AssocItem> {
545 self.tcx
546 .associated_items(def_id)
547 .find_by_ident_and_namespace(self.tcx, item_ident, Namespace::ValueNS, def_id)
548 .copied()
549 }
550}