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::{self, InferOk};
15use rustc_infer::traits::PredicateObligations;
16use rustc_middle::query::Providers;
17use rustc_middle::traits::ObligationCause;
18use rustc_middle::ty::{
19 self, GenericArgs, GenericArgsRef, GenericParamDefKind, Ty, TypeVisitableExt,
20};
21use rustc_middle::{bug, span_bug};
22use rustc_span::{ErrorGuaranteed, Ident, Span};
23use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt;
24use rustc_trait_selection::traits::{self, NormalizeExt};
25use tracing::{debug, instrument};
26
27pub(crate) use self::MethodError::*;
28use self::probe::{IsSuggestion, ProbeScope};
29use crate::FnCtxt;
30
31pub(crate) fn provide(providers: &mut Providers) {
32 probe::provide(providers);
33}
34
35#[derive(Clone, Copy, Debug)]
36pub(crate) struct MethodCallee<'tcx> {
37 pub def_id: DefId,
39 pub args: GenericArgsRef<'tcx>,
40
41 pub sig: ty::FnSig<'tcx>,
45}
46
47#[derive(Debug)]
48pub(crate) enum MethodError<'tcx> {
49 NoMatch(NoMatchData<'tcx>),
51
52 Ambiguity(Vec<CandidateSource>),
54
55 PrivateMatch(DefKind, DefId, Vec<DefId>),
58
59 IllegalSizedBound {
61 candidates: Vec<DefId>,
62 needs_mut: bool,
63 bound_span: Span,
64 self_expr: &'tcx hir::Expr<'tcx>,
65 },
66
67 BadReturnType,
69
70 ErrorReported(ErrorGuaranteed),
72}
73
74#[derive(Debug)]
77pub(crate) struct NoMatchData<'tcx> {
78 pub static_candidates: Vec<CandidateSource>,
79 pub unsatisfied_predicates:
80 Vec<(ty::Predicate<'tcx>, Option<ty::Predicate<'tcx>>, Option<ObligationCause<'tcx>>)>,
81 pub out_of_scope_traits: Vec<DefId>,
82 pub similar_candidate: Option<ty::AssocItem>,
83 pub mode: probe::Mode,
84}
85
86#[derive(Copy, Clone, Debug, Eq, PartialEq)]
89pub(crate) enum CandidateSource {
90 Impl(DefId),
91 Trait(DefId ),
92}
93
94impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
95 #[instrument(level = "debug", skip(self))]
97 pub(crate) fn method_exists_for_diagnostic(
98 &self,
99 method_name: Ident,
100 self_ty: Ty<'tcx>,
101 call_expr_id: hir::HirId,
102 return_type: Option<Ty<'tcx>>,
103 ) -> bool {
104 match self.probe_for_name(
105 probe::Mode::MethodCall,
106 method_name,
107 return_type,
108 IsSuggestion(true),
109 self_ty,
110 call_expr_id,
111 ProbeScope::TraitsInScope,
112 ) {
113 Ok(pick) => {
114 pick.maybe_emit_unstable_name_collision_hint(
115 self.tcx,
116 method_name.span,
117 call_expr_id,
118 );
119 true
120 }
121 Err(NoMatch(..)) => false,
122 Err(Ambiguity(..)) => true,
123 Err(PrivateMatch(..)) => false,
124 Err(IllegalSizedBound { .. }) => true,
125 Err(BadReturnType) => false,
126 Err(ErrorReported(_)) => false,
127 }
128 }
129
130 #[instrument(level = "debug", skip(self, err, call_expr))]
132 pub(crate) fn suggest_method_call(
133 &self,
134 err: &mut Diag<'_>,
135 msg: impl Into<SubdiagMessage> + std::fmt::Debug,
136 method_name: Ident,
137 self_ty: Ty<'tcx>,
138 call_expr: &hir::Expr<'tcx>,
139 span: Option<Span>,
140 ) {
141 let params = self
142 .lookup_probe_for_diagnostic(
143 method_name,
144 self_ty,
145 call_expr,
146 ProbeScope::TraitsInScope,
147 None,
148 )
149 .map(|pick| {
150 let sig = self.tcx.fn_sig(pick.item.def_id);
151 sig.skip_binder().inputs().skip_binder().len().saturating_sub(1)
152 })
153 .unwrap_or(0);
154
155 let sugg_span = span.unwrap_or(call_expr.span).shrink_to_hi();
157 let (suggestion, applicability) = (
158 format!("({})", (0..params).map(|_| "_").collect::<Vec<_>>().join(", ")),
159 if params > 0 { Applicability::HasPlaceholders } else { Applicability::MaybeIncorrect },
160 );
161
162 err.span_suggestion_verbose(sugg_span, msg, suggestion, applicability);
163 }
164
165 #[instrument(level = "debug", skip(self))]
181 pub(crate) fn lookup_method(
182 &self,
183 self_ty: Ty<'tcx>,
184 segment: &'tcx hir::PathSegment<'tcx>,
185 span: Span,
186 call_expr: &'tcx hir::Expr<'tcx>,
187 self_expr: &'tcx hir::Expr<'tcx>,
188 args: &'tcx [hir::Expr<'tcx>],
189 ) -> Result<MethodCallee<'tcx>, MethodError<'tcx>> {
190 let scope = if let Some(only_method) = segment.res.opt_def_id() {
191 ProbeScope::Single(only_method)
192 } else {
193 ProbeScope::TraitsInScope
194 };
195
196 let pick = self.lookup_probe(segment.ident, self_ty, call_expr, scope)?;
197
198 self.lint_edition_dependent_dot_call(
199 self_ty, segment, span, call_expr, self_expr, &pick, args,
200 );
201
202 for &import_id in &pick.import_ids {
205 debug!("used_trait_import: {:?}", import_id);
206 self.typeck_results.borrow_mut().used_trait_imports.insert(import_id);
207 }
208
209 self.tcx.check_stability(pick.item.def_id, Some(call_expr.hir_id), span, None);
210
211 let result = self.confirm_method(span, self_expr, call_expr, self_ty, &pick, segment);
212 debug!("result = {:?}", result);
213
214 if let Some(span) = result.illegal_sized_bound {
215 let mut needs_mut = false;
216 if let ty::Ref(region, t_type, mutability) = self_ty.kind() {
217 let trait_type = Ty::new_ref(self.tcx, *region, *t_type, mutability.invert());
218 match self.lookup_probe(
220 segment.ident,
221 trait_type,
222 call_expr,
223 ProbeScope::TraitsInScope,
224 ) {
225 Ok(ref new_pick) if pick.differs_from(new_pick) => {
226 needs_mut = new_pick.self_ty.ref_mutability() != self_ty.ref_mutability();
227 }
228 _ => {}
229 }
230 }
231
232 let candidates = match self.lookup_probe_for_diagnostic(
234 segment.ident,
235 self_ty,
236 call_expr,
237 ProbeScope::AllTraits,
238 None,
239 ) {
240 Ok(ref new_pick) if pick.differs_from(new_pick) => {
242 vec![new_pick.item.container_id(self.tcx)]
243 }
244 Err(Ambiguity(ref sources)) => sources
245 .iter()
246 .filter_map(|source| {
247 match *source {
248 CandidateSource::Impl(def) => self.tcx.trait_id_of_impl(def),
251 CandidateSource::Trait(_) => None,
252 }
253 })
254 .collect(),
255 _ => Vec::new(),
256 };
257
258 return Err(IllegalSizedBound { candidates, needs_mut, bound_span: span, self_expr });
259 }
260
261 Ok(result.callee)
262 }
263
264 pub(crate) fn lookup_method_for_diagnostic(
265 &self,
266 self_ty: Ty<'tcx>,
267 segment: &hir::PathSegment<'tcx>,
268 span: Span,
269 call_expr: &'tcx hir::Expr<'tcx>,
270 self_expr: &'tcx hir::Expr<'tcx>,
271 ) -> Result<MethodCallee<'tcx>, MethodError<'tcx>> {
272 let pick = self.lookup_probe_for_diagnostic(
273 segment.ident,
274 self_ty,
275 call_expr,
276 ProbeScope::TraitsInScope,
277 None,
278 )?;
279
280 Ok(self
281 .confirm_method_for_diagnostic(span, self_expr, call_expr, self_ty, &pick, segment)
282 .callee)
283 }
284
285 #[instrument(level = "debug", skip(self, call_expr))]
286 pub(crate) fn lookup_probe(
287 &self,
288 method_name: Ident,
289 self_ty: Ty<'tcx>,
290 call_expr: &hir::Expr<'_>,
291 scope: ProbeScope,
292 ) -> probe::PickResult<'tcx> {
293 let pick = self.probe_for_name(
294 probe::Mode::MethodCall,
295 method_name,
296 None,
297 IsSuggestion(false),
298 self_ty,
299 call_expr.hir_id,
300 scope,
301 )?;
302 pick.maybe_emit_unstable_name_collision_hint(self.tcx, method_name.span, call_expr.hir_id);
303 Ok(pick)
304 }
305
306 pub(crate) fn lookup_probe_for_diagnostic(
307 &self,
308 method_name: Ident,
309 self_ty: Ty<'tcx>,
310 call_expr: &hir::Expr<'_>,
311 scope: ProbeScope,
312 return_type: Option<Ty<'tcx>>,
313 ) -> probe::PickResult<'tcx> {
314 let pick = self.probe_for_name(
315 probe::Mode::MethodCall,
316 method_name,
317 return_type,
318 IsSuggestion(true),
319 self_ty,
320 call_expr.hir_id,
321 scope,
322 )?;
323 Ok(pick)
324 }
325
326 #[instrument(level = "debug", skip(self))]
332 pub(super) fn lookup_method_in_trait(
333 &self,
334 cause: ObligationCause<'tcx>,
335 m_name: Ident,
336 trait_def_id: DefId,
337 self_ty: Ty<'tcx>,
338 opt_rhs_ty: Option<Ty<'tcx>>,
339 ) -> Option<InferOk<'tcx, MethodCallee<'tcx>>> {
340 let args = GenericArgs::for_item(self.tcx, trait_def_id, |param, _| match param.kind {
342 GenericParamDefKind::Lifetime | GenericParamDefKind::Const { .. } => {
343 unreachable!("did not expect operator trait to have lifetime/const")
344 }
345 GenericParamDefKind::Type { .. } => {
346 if param.index == 0 {
347 self_ty.into()
348 } else if let Some(rhs_ty) = opt_rhs_ty {
349 assert_eq!(param.index, 1, "did not expect >1 param on operator trait");
350 rhs_ty.into()
351 } else {
352 self.var_for_def(cause.span, param)
356 }
357 }
358 });
359
360 let obligation = traits::Obligation::new(
361 self.tcx,
362 cause,
363 self.param_env,
364 ty::TraitRef::new_from_args(self.tcx, trait_def_id, args),
365 );
366
367 if !self.predicate_may_hold(&obligation) {
369 debug!("--> Cannot match obligation");
370 return None;
372 }
373
374 let tcx = self.tcx;
377 let Some(method_item) = self.associated_value(trait_def_id, m_name) else {
378 bug!("expected associated item for operator trait")
379 };
380
381 let def_id = method_item.def_id;
382 if method_item.kind != ty::AssocKind::Fn {
383 span_bug!(tcx.def_span(def_id), "expected `{m_name}` to be an associated function");
384 }
385
386 debug!("lookup_in_trait_adjusted: method_item={:?}", method_item);
387 let mut obligations = PredicateObligations::new();
388
389 let fn_sig = tcx.fn_sig(def_id).instantiate(self.tcx, args);
396 let fn_sig =
397 self.instantiate_binder_with_fresh_vars(obligation.cause.span, infer::FnCall, fn_sig);
398
399 let InferOk { value: fn_sig, obligations: o } =
400 self.at(&obligation.cause, self.param_env).normalize(fn_sig);
401 obligations.extend(o);
402
403 let bounds = self.tcx.predicates_of(def_id).instantiate(self.tcx, args);
412
413 let InferOk { value: bounds, obligations: o } =
414 self.at(&obligation.cause, self.param_env).normalize(bounds);
415 obligations.extend(o);
416 assert!(!bounds.has_escaping_bound_vars());
417
418 let predicates_cause = obligation.cause.clone();
419 obligations.extend(traits::predicates_for_generics(
420 move |_, _| predicates_cause.clone(),
421 self.param_env,
422 bounds,
423 ));
424
425 let method_ty = Ty::new_fn_ptr(tcx, ty::Binder::dummy(fn_sig));
427 debug!(
428 "lookup_method_in_trait: matched method method_ty={:?} obligation={:?}",
429 method_ty, obligation
430 );
431 obligations.push(traits::Obligation::new(
432 tcx,
433 obligation.cause,
434 self.param_env,
435 ty::Binder::dummy(ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(
436 method_ty.into(),
437 ))),
438 ));
439
440 let callee = MethodCallee { def_id, args, sig: fn_sig };
441 debug!("callee = {:?}", callee);
442
443 Some(InferOk { obligations, value: callee })
444 }
445
446 #[instrument(level = "debug", skip(self), ret)]
463 pub(crate) fn resolve_fully_qualified_call(
464 &self,
465 span: Span,
466 method_name: Ident,
467 self_ty: Ty<'tcx>,
468 self_ty_span: Span,
469 expr_id: hir::HirId,
470 ) -> Result<(DefKind, DefId), MethodError<'tcx>> {
471 let tcx = self.tcx;
472
473 let mut struct_variant = None;
475 if let ty::Adt(adt_def, _) = self_ty.kind() {
476 if adt_def.is_enum() {
477 let variant_def = adt_def
478 .variants()
479 .iter()
480 .find(|vd| tcx.hygienic_eq(method_name, vd.ident(tcx), adt_def.did()));
481 if let Some(variant_def) = variant_def {
482 if let Some((ctor_kind, ctor_def_id)) = variant_def.ctor {
483 tcx.check_stability(
484 ctor_def_id,
485 Some(expr_id),
486 span,
487 Some(method_name.span),
488 );
489 return Ok((DefKind::Ctor(CtorOf::Variant, ctor_kind), ctor_def_id));
490 } else {
491 struct_variant = Some((DefKind::Variant, variant_def.def_id));
492 }
493 }
494 }
495 }
496
497 let pick = self.probe_for_name(
498 probe::Mode::Path,
499 method_name,
500 None,
501 IsSuggestion(false),
502 self_ty,
503 expr_id,
504 ProbeScope::TraitsInScope,
505 );
506 let pick = match (pick, struct_variant) {
507 (Err(_), Some(res)) => return Ok(res),
509 (pick, _) => pick?,
510 };
511
512 pick.maybe_emit_unstable_name_collision_hint(self.tcx, span, expr_id);
513
514 self.lint_fully_qualified_call_from_2018(
515 span,
516 method_name,
517 self_ty,
518 self_ty_span,
519 expr_id,
520 &pick,
521 );
522
523 debug!(?pick);
524 {
525 let mut typeck_results = self.typeck_results.borrow_mut();
526 for import_id in pick.import_ids {
527 debug!(used_trait_import=?import_id);
528 typeck_results.used_trait_imports.insert(import_id);
529 }
530 }
531
532 let def_kind = pick.item.kind.as_def_kind();
533 tcx.check_stability(pick.item.def_id, Some(expr_id), span, Some(method_name.span));
534 Ok((def_kind, pick.item.def_id))
535 }
536
537 fn associated_value(&self, def_id: DefId, item_name: Ident) -> Option<ty::AssocItem> {
540 self.tcx
541 .associated_items(def_id)
542 .find_by_name_and_namespace(self.tcx, item_name, Namespace::ValueNS, def_id)
543 .copied()
544 }
545}