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, Symbol};
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_for_operator(
333 &self,
334 cause: ObligationCause<'tcx>,
335 method_name: Symbol,
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) =
380 self.associated_value(trait_def_id, Ident::with_dummy_span(method_name))
381 else {
382 bug!("expected associated item for operator trait")
383 };
384
385 let def_id = method_item.def_id;
386 if !method_item.is_fn() {
387 span_bug!(
388 tcx.def_span(def_id),
389 "expected `{method_name}` to be an associated function"
390 );
391 }
392
393 debug!("lookup_in_trait_adjusted: method_item={:?}", method_item);
394 let mut obligations = PredicateObligations::new();
395
396 let fn_sig = tcx.fn_sig(def_id).instantiate(self.tcx, args);
403 let fn_sig =
404 self.instantiate_binder_with_fresh_vars(obligation.cause.span, infer::FnCall, fn_sig);
405
406 let InferOk { value: fn_sig, obligations: o } =
407 self.at(&obligation.cause, self.param_env).normalize(fn_sig);
408 obligations.extend(o);
409
410 let bounds = self.tcx.predicates_of(def_id).instantiate(self.tcx, args);
419
420 let InferOk { value: bounds, obligations: o } =
421 self.at(&obligation.cause, self.param_env).normalize(bounds);
422 obligations.extend(o);
423 assert!(!bounds.has_escaping_bound_vars());
424
425 let predicates_cause = obligation.cause.clone();
426 obligations.extend(traits::predicates_for_generics(
427 move |_, _| predicates_cause.clone(),
428 self.param_env,
429 bounds,
430 ));
431
432 let method_ty = Ty::new_fn_ptr(tcx, ty::Binder::dummy(fn_sig));
434 debug!(
435 "lookup_method_in_trait: matched method method_ty={:?} obligation={:?}",
436 method_ty, obligation
437 );
438 obligations.push(traits::Obligation::new(
439 tcx,
440 obligation.cause,
441 self.param_env,
442 ty::Binder::dummy(ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(
443 method_ty.into(),
444 ))),
445 ));
446
447 let callee = MethodCallee { def_id, args, sig: fn_sig };
448 debug!("callee = {:?}", callee);
449
450 Some(InferOk { obligations, value: callee })
451 }
452
453 #[instrument(level = "debug", skip(self), ret)]
470 pub(crate) fn resolve_fully_qualified_call(
471 &self,
472 span: Span,
473 method_name: Ident,
474 self_ty: Ty<'tcx>,
475 self_ty_span: Span,
476 expr_id: hir::HirId,
477 ) -> Result<(DefKind, DefId), MethodError<'tcx>> {
478 let tcx = self.tcx;
479
480 let mut struct_variant = None;
482 if let ty::Adt(adt_def, _) = self_ty.kind() {
483 if adt_def.is_enum() {
484 let variant_def = adt_def
485 .variants()
486 .iter()
487 .find(|vd| tcx.hygienic_eq(method_name, vd.ident(tcx), adt_def.did()));
488 if let Some(variant_def) = variant_def {
489 if let Some((ctor_kind, ctor_def_id)) = variant_def.ctor {
490 tcx.check_stability(
491 ctor_def_id,
492 Some(expr_id),
493 span,
494 Some(method_name.span),
495 );
496 return Ok((DefKind::Ctor(CtorOf::Variant, ctor_kind), ctor_def_id));
497 } else {
498 struct_variant = Some((DefKind::Variant, variant_def.def_id));
499 }
500 }
501 }
502 }
503
504 let pick = self.probe_for_name(
505 probe::Mode::Path,
506 method_name,
507 None,
508 IsSuggestion(false),
509 self_ty,
510 expr_id,
511 ProbeScope::TraitsInScope,
512 );
513 let pick = match (pick, struct_variant) {
514 (Err(_), Some(res)) => return Ok(res),
516 (pick, _) => pick?,
517 };
518
519 pick.maybe_emit_unstable_name_collision_hint(self.tcx, span, expr_id);
520
521 self.lint_fully_qualified_call_from_2018(
522 span,
523 method_name,
524 self_ty,
525 self_ty_span,
526 expr_id,
527 &pick,
528 );
529
530 debug!(?pick);
531 {
532 let mut typeck_results = self.typeck_results.borrow_mut();
533 for import_id in pick.import_ids {
534 debug!(used_trait_import=?import_id);
535 typeck_results.used_trait_imports.insert(import_id);
536 }
537 }
538
539 let def_kind = pick.item.as_def_kind();
540 tcx.check_stability(pick.item.def_id, Some(expr_id), span, Some(method_name.span));
541 Ok((def_kind, pick.item.def_id))
542 }
543
544 fn associated_value(&self, def_id: DefId, item_ident: Ident) -> Option<ty::AssocItem> {
547 self.tcx
548 .associated_items(def_id)
549 .find_by_ident_and_namespace(self.tcx, item_ident, Namespace::ValueNS, def_id)
550 .copied()
551 }
552}