]>
Commit | Line | Data |
---|---|---|
d9579d0f | 1 | //! Give useful errors and suggestions to users when an item can't be |
85aaf69f SL |
2 | //! found or is otherwise invalid. |
3 | ||
2b03887a | 4 | use crate::errors; |
9c376795 | 5 | use crate::Expectation; |
2b03887a FG |
6 | use crate::FnCtxt; |
7 | use rustc_ast::ast::Mutability; | |
74b04a01 | 8 | use rustc_data_structures::fx::{FxHashMap, FxHashSet}; |
487cf647 | 9 | use rustc_errors::StashKey; |
5e7ed085 FG |
10 | use rustc_errors::{ |
11 | pluralize, struct_span_err, Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed, | |
04454e1e | 12 | MultiSpan, |
5e7ed085 | 13 | }; |
dfeec247 | 14 | use rustc_hir as hir; |
04454e1e FG |
15 | use rustc_hir::def::DefKind; |
16 | use rustc_hir::def_id::DefId; | |
3dfed10e | 17 | use rustc_hir::lang_items::LangItem; |
487cf647 FG |
18 | use rustc_hir::PatKind::Binding; |
19 | use rustc_hir::PathSegment; | |
dfeec247 | 20 | use rustc_hir::{ExprKind, Node, QPath}; |
487cf647 FG |
21 | use rustc_infer::infer::{ |
22 | type_variable::{TypeVariableOrigin, TypeVariableOriginKind}, | |
23 | RegionVariableOrigin, | |
24 | }; | |
25 | use rustc_middle::infer::unify_key::{ConstVariableOrigin, ConstVariableOriginKind}; | |
5099ac24 | 26 | use rustc_middle::traits::util::supertraits; |
487cf647 | 27 | use rustc_middle::ty::fast_reject::DeepRejectCtxt; |
5e7ed085 | 28 | use rustc_middle::ty::fast_reject::{simplify_type, TreatParams}; |
9c376795 | 29 | use rustc_middle::ty::print::{with_crate_prefix, with_forced_trimmed_paths}; |
353b0b11 | 30 | use rustc_middle::ty::{self, GenericArgKind, Ty, TyCtxt, TypeVisitableExt}; |
f2b60f7d | 31 | use rustc_middle::ty::{IsSuggestable, ToPolyTraitRef}; |
3dfed10e | 32 | use rustc_span::symbol::{kw, sym, Ident}; |
064997fb | 33 | use rustc_span::Symbol; |
9ffffee4 | 34 | use rustc_span::{edit_distance, source_map, ExpnKind, FileName, MacroKind, Span}; |
487cf647 | 35 | use rustc_trait_selection::traits::error_reporting::on_unimplemented::OnUnimplementedNote; |
2b03887a | 36 | use rustc_trait_selection::traits::error_reporting::on_unimplemented::TypeErrCtxtExt as _; |
5e7ed085 | 37 | use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _; |
3c0e092e | 38 | use rustc_trait_selection::traits::{ |
487cf647 | 39 | FulfillmentError, Obligation, ObligationCause, ObligationCauseCode, |
3c0e092e | 40 | }; |
85aaf69f | 41 | |
2b03887a | 42 | use super::probe::{AutorefOrPtrAdjustment, IsSuggestion, Mode, ProbeScope}; |
f2b60f7d | 43 | use super::{CandidateSource, MethodError, NoMatchData}; |
487cf647 | 44 | use rustc_hir::intravisit::Visitor; |
353b0b11 | 45 | use std::cmp::{self, Ordering}; |
487cf647 | 46 | use std::iter; |
85aaf69f | 47 | |
dc9dc135 | 48 | impl<'a, 'tcx> FnCtxt<'a, 'tcx> { |
48663c56 | 49 | fn is_fn_ty(&self, ty: Ty<'tcx>, span: Span) -> bool { |
a7813a04 | 50 | let tcx = self.tcx; |
1b1a35ee | 51 | match ty.kind() { |
0731742a XL |
52 | // Not all of these (e.g., unsafe fns) implement `FnOnce`, |
53 | // so we look for these beforehand. | |
dfeec247 | 54 | ty::Closure(..) | ty::FnDef(..) | ty::FnPtr(_) => true, |
0731742a | 55 | // If it's not a simple function, look for things which implement `FnOnce`. |
a7813a04 | 56 | _ => { |
5e7ed085 FG |
57 | let Some(fn_once) = tcx.lang_items().fn_once_trait() else { |
58 | return false; | |
3157f602 | 59 | }; |
a7813a04 | 60 | |
5099ac24 FG |
61 | // This conditional prevents us from asking to call errors and unresolved types. |
62 | // It might seem that we can use `predicate_must_hold_modulo_regions`, | |
63 | // but since a Dummy binder is used to fill in the FnOnce trait's arguments, | |
64 | // type resolution always gives a "maybe" here. | |
65 | if self.autoderef(span, ty).any(|(ty, _)| { | |
66 | info!("check deref {:?} error", ty); | |
67 | matches!(ty.kind(), ty::Error(_) | ty::Infer(_)) | |
68 | }) { | |
69 | return false; | |
70 | } | |
71 | ||
c30ab7b3 | 72 | self.autoderef(span, ty).any(|(ty, _)| { |
5099ac24 | 73 | info!("check deref {:?} impl FnOnce", ty); |
c30ab7b3 | 74 | self.probe(|_| { |
487cf647 FG |
75 | let trait_ref = tcx.mk_trait_ref( |
76 | fn_once, | |
77 | [ | |
78 | ty, | |
79 | self.next_ty_var(TypeVariableOrigin { | |
dfeec247 XL |
80 | kind: TypeVariableOriginKind::MiscVariable, |
81 | span, | |
487cf647 FG |
82 | }), |
83 | ], | |
dfeec247 | 84 | ); |
c295e0f8 | 85 | let poly_trait_ref = ty::Binder::dummy(trait_ref); |
dfeec247 | 86 | let obligation = Obligation::misc( |
487cf647 | 87 | tcx, |
dfeec247 XL |
88 | span, |
89 | self.body_id, | |
90 | self.param_env, | |
487cf647 | 91 | poly_trait_ref.without_const(), |
dfeec247 | 92 | ); |
83c7162d | 93 | self.predicate_may_hold(&obligation) |
c30ab7b3 SL |
94 | }) |
95 | }) | |
54a0048b SL |
96 | } |
97 | } | |
98 | } | |
3157f602 | 99 | |
a2a8927a XL |
100 | fn is_slice_ty(&self, ty: Ty<'tcx>, span: Span) -> bool { |
101 | self.autoderef(span, ty).any(|(ty, _)| matches!(ty.kind(), ty::Slice(..) | ty::Array(..))) | |
102 | } | |
103 | ||
9c376795 | 104 | #[instrument(level = "debug", skip(self))] |
cdc7bbd5 | 105 | pub fn report_method_error( |
532ac7d7 | 106 | &self, |
9c376795 | 107 | span: Span, |
532ac7d7 | 108 | rcvr_ty: Ty<'tcx>, |
f9f354fc | 109 | item_name: Ident, |
cdc7bbd5 | 110 | source: SelfSource<'tcx>, |
532ac7d7 | 111 | error: MethodError<'tcx>, |
f2b60f7d | 112 | args: Option<(&'tcx hir::Expr<'tcx>, &'tcx [hir::Expr<'tcx>])>, |
9c376795 | 113 | expected: Expectation<'tcx>, |
5e7ed085 | 114 | ) -> Option<DiagnosticBuilder<'_, ErrorGuaranteed>> { |
0731742a | 115 | // Avoid suggestions when we don't know what's going on. |
a7813a04 | 116 | if rcvr_ty.references_error() { |
e1599b0c | 117 | return None; |
a7813a04 | 118 | } |
85aaf69f | 119 | |
9c376795 FG |
120 | let sugg_span = if let SelfSource::MethodCall(expr) = source { |
121 | // Given `foo.bar(baz)`, `expr` is `bar`, but we want to point to the whole thing. | |
122 | self.tcx.hir().expect_expr(self.tcx.hir().parent_id(expr.hir_id)).span | |
123 | } else { | |
124 | span | |
125 | }; | |
064997fb | 126 | |
9c376795 FG |
127 | match error { |
128 | MethodError::NoMatch(mut no_match_data) => { | |
129 | return self.report_no_match_method_error( | |
130 | span, | |
131 | rcvr_ty, | |
132 | item_name, | |
133 | source, | |
134 | args, | |
135 | sugg_span, | |
136 | &mut no_match_data, | |
137 | expected, | |
138 | ); | |
139 | } | |
a7813a04 | 140 | |
9c376795 FG |
141 | MethodError::Ambiguity(mut sources) => { |
142 | let mut err = struct_span_err!( | |
143 | self.sess(), | |
144 | item_name.span, | |
145 | E0034, | |
146 | "multiple applicable items in scope" | |
147 | ); | |
148 | err.span_label(item_name.span, format!("multiple `{}` found", item_name)); | |
a7813a04 | 149 | |
9c376795 FG |
150 | self.note_candidates_on_method_error( |
151 | rcvr_ty, | |
152 | item_name, | |
153 | args, | |
154 | span, | |
155 | &mut err, | |
156 | &mut sources, | |
157 | Some(sugg_span), | |
158 | ); | |
159 | err.emit(); | |
160 | } | |
a7813a04 | 161 | |
9c376795 | 162 | MethodError::PrivateMatch(kind, def_id, out_of_scope_traits) => { |
9ffffee4 | 163 | let kind = self.tcx.def_kind_descr(kind, def_id); |
9c376795 FG |
164 | let mut err = struct_span_err!( |
165 | self.tcx.sess, | |
166 | item_name.span, | |
167 | E0624, | |
168 | "{} `{}` is private", | |
169 | kind, | |
170 | item_name | |
171 | ); | |
172 | err.span_label(item_name.span, &format!("private {}", kind)); | |
173 | let sp = self | |
174 | .tcx | |
175 | .hir() | |
176 | .span_if_local(def_id) | |
177 | .unwrap_or_else(|| self.tcx.def_span(def_id)); | |
178 | err.span_label(sp, &format!("private {} defined here", kind)); | |
179 | self.suggest_valid_traits(&mut err, out_of_scope_traits); | |
180 | err.emit(); | |
181 | } | |
182 | ||
183 | MethodError::IllegalSizedBound { candidates, needs_mut, bound_span, self_expr } => { | |
184 | let msg = if needs_mut { | |
185 | with_forced_trimmed_paths!(format!( | |
186 | "the `{item_name}` method cannot be invoked on `{rcvr_ty}`" | |
187 | )) | |
188 | } else { | |
189 | format!("the `{item_name}` method cannot be invoked on a trait object") | |
190 | }; | |
191 | let mut err = self.sess().struct_span_err(span, &msg); | |
192 | if !needs_mut { | |
193 | err.span_label(bound_span, "this has a `Sized` requirement"); | |
194 | } | |
195 | if !candidates.is_empty() { | |
196 | let help = format!( | |
197 | "{an}other candidate{s} {were} found in the following trait{s}, perhaps \ | |
198 | add a `use` for {one_of_them}:", | |
199 | an = if candidates.len() == 1 { "an" } else { "" }, | |
200 | s = pluralize!(candidates.len()), | |
201 | were = pluralize!("was", candidates.len()), | |
202 | one_of_them = if candidates.len() == 1 { "it" } else { "one_of_them" }, | |
203 | ); | |
204 | self.suggest_use_candidates(&mut err, help, candidates); | |
205 | } | |
206 | if let ty::Ref(region, t_type, mutability) = rcvr_ty.kind() { | |
207 | if needs_mut { | |
208 | let trait_type = self.tcx.mk_ref( | |
209 | *region, | |
210 | ty::TypeAndMut { ty: *t_type, mutbl: mutability.invert() }, | |
211 | ); | |
212 | let msg = format!("you need `{}` instead of `{}`", trait_type, rcvr_ty); | |
213 | let mut kind = &self_expr.kind; | |
214 | while let hir::ExprKind::AddrOf(_, _, expr) | |
215 | | hir::ExprKind::Unary(hir::UnOp::Deref, expr) = kind | |
216 | { | |
217 | kind = &expr.kind; | |
416331ca | 218 | } |
9c376795 FG |
219 | if let hir::ExprKind::Path(hir::QPath::Resolved(None, path)) = kind |
220 | && let hir::def::Res::Local(hir_id) = path.res | |
221 | && let Some(hir::Node::Pat(b)) = self.tcx.hir().find(hir_id) | |
222 | && let Some(hir::Node::Param(p)) = self.tcx.hir().find_parent(b.hir_id) | |
223 | && let Some(node) = self.tcx.hir().find_parent(p.hir_id) | |
224 | && let Some(decl) = node.fn_decl() | |
225 | && let Some(ty) = decl.inputs.iter().find(|ty| ty.span == p.ty_span) | |
226 | && let hir::TyKind::Ref(_, mut_ty) = &ty.kind | |
227 | && let hir::Mutability::Not = mut_ty.mutbl | |
228 | { | |
229 | err.span_suggestion_verbose( | |
230 | mut_ty.ty.span.shrink_to_lo(), | |
231 | &msg, | |
232 | "mut ", | |
233 | Applicability::MachineApplicable, | |
dfeec247 | 234 | ); |
94b46f34 | 235 | } else { |
9c376795 | 236 | err.help(&msg); |
487cf647 | 237 | } |
9cc50fc6 | 238 | } |
54a0048b | 239 | } |
9c376795 | 240 | err.emit(); |
54a0048b | 241 | } |
62682a34 | 242 | |
9c376795 FG |
243 | MethodError::BadReturnType => bug!("no return type expectations but got BadReturnType"), |
244 | } | |
245 | None | |
246 | } | |
247 | ||
353b0b11 FG |
248 | fn suggest_missing_writer( |
249 | &self, | |
250 | rcvr_ty: Ty<'tcx>, | |
251 | args: (&'tcx hir::Expr<'tcx>, &'tcx [hir::Expr<'tcx>]), | |
252 | ) -> DiagnosticBuilder<'_, ErrorGuaranteed> { | |
253 | let (ty_str, _ty_file) = self.tcx.short_ty_string(rcvr_ty); | |
254 | let mut err = | |
255 | struct_span_err!(self.tcx.sess, args.0.span, E0599, "cannot write into `{}`", ty_str); | |
256 | err.span_note( | |
257 | args.0.span, | |
258 | "must implement `io::Write`, `fmt::Write`, or have a `write_fmt` method", | |
259 | ); | |
260 | if let ExprKind::Lit(_) = args.0.kind { | |
261 | err.span_help( | |
262 | args.0.span.shrink_to_lo(), | |
263 | "a writer is needed before this format string", | |
264 | ); | |
265 | }; | |
266 | ||
267 | err | |
268 | } | |
269 | ||
9c376795 FG |
270 | pub fn report_no_match_method_error( |
271 | &self, | |
272 | mut span: Span, | |
273 | rcvr_ty: Ty<'tcx>, | |
274 | item_name: Ident, | |
275 | source: SelfSource<'tcx>, | |
276 | args: Option<(&'tcx hir::Expr<'tcx>, &'tcx [hir::Expr<'tcx>])>, | |
277 | sugg_span: Span, | |
278 | no_match_data: &mut NoMatchData<'tcx>, | |
279 | expected: Expectation<'tcx>, | |
280 | ) -> Option<DiagnosticBuilder<'_, ErrorGuaranteed>> { | |
281 | let mode = no_match_data.mode; | |
282 | let tcx = self.tcx; | |
283 | let rcvr_ty = self.resolve_vars_if_possible(rcvr_ty); | |
9ffffee4 FG |
284 | let (ty_str, ty_file) = tcx.short_ty_string(rcvr_ty); |
285 | let short_ty_str = with_forced_trimmed_paths!(rcvr_ty.to_string()); | |
9c376795 FG |
286 | let is_method = mode == Mode::MethodCall; |
287 | let unsatisfied_predicates = &no_match_data.unsatisfied_predicates; | |
9ffffee4 | 288 | let similar_candidate = no_match_data.similar_candidate; |
9c376795 FG |
289 | let item_kind = if is_method { |
290 | "method" | |
291 | } else if rcvr_ty.is_enum() { | |
292 | "variant or associated item" | |
dfeec247 | 293 | } else { |
9c376795 FG |
294 | match (item_name.as_str().chars().next(), rcvr_ty.is_fresh_ty()) { |
295 | (Some(name), false) if name.is_lowercase() => "function or associated item", | |
296 | (Some(_), false) => "associated item", | |
297 | (Some(_), true) | (None, false) => "variant or associated item", | |
298 | (None, true) => "variant", | |
299 | } | |
dfeec247 XL |
300 | }; |
301 | ||
9ffffee4 | 302 | // We could pass the file for long types into these two, but it isn't strictly necessary |
353b0b11 | 303 | // given how targeted they are. |
9ffffee4 FG |
304 | if self.suggest_wrapping_range_with_parens( |
305 | tcx, | |
306 | rcvr_ty, | |
307 | source, | |
308 | span, | |
309 | item_name, | |
310 | &short_ty_str, | |
311 | ) || self.suggest_constraining_numerical_ty( | |
312 | tcx, | |
313 | rcvr_ty, | |
314 | source, | |
315 | span, | |
316 | item_kind, | |
317 | item_name, | |
318 | &short_ty_str, | |
319 | ) { | |
9c376795 FG |
320 | return None; |
321 | } | |
322 | span = item_name.span; | |
323 | ||
324 | // Don't show generic arguments when the method can't be found in any implementation (#81576). | |
325 | let mut ty_str_reported = ty_str.clone(); | |
326 | if let ty::Adt(_, generics) = rcvr_ty.kind() { | |
327 | if generics.len() > 0 { | |
328 | let mut autoderef = self.autoderef(span, rcvr_ty); | |
329 | let candidate_found = autoderef.any(|(ty, _)| { | |
330 | if let ty::Adt(adt_def, _) = ty.kind() { | |
331 | self.tcx | |
332 | .inherent_impls(adt_def.did()) | |
333 | .iter() | |
334 | .any(|def_id| self.associated_value(*def_id, item_name).is_some()) | |
335 | } else { | |
336 | false | |
ff7c6d11 | 337 | } |
9c376795 FG |
338 | }); |
339 | let has_deref = autoderef.step_count() > 0; | |
340 | if !candidate_found && !has_deref && unsatisfied_predicates.is_empty() { | |
341 | if let Some((path_string, _)) = ty_str.split_once('<') { | |
342 | ty_str_reported = path_string.to_string(); | |
04454e1e FG |
343 | } |
344 | } | |
9c376795 FG |
345 | } |
346 | } | |
17df50a5 | 347 | |
353b0b11 FG |
348 | let is_write = sugg_span.ctxt().outer_expn_data().macro_def_id.map_or(false, |def_id| { |
349 | tcx.is_diagnostic_item(sym::write_macro, def_id) | |
350 | || tcx.is_diagnostic_item(sym::writeln_macro, def_id) | |
351 | }) && item_name.name == Symbol::intern("write_fmt"); | |
352 | let mut err = if is_write | |
353 | && let Some(args) = args | |
354 | { | |
355 | self.suggest_missing_writer(rcvr_ty, args) | |
356 | } else { | |
357 | struct_span_err!( | |
358 | tcx.sess, | |
359 | span, | |
360 | E0599, | |
361 | "no {} named `{}` found for {} `{}` in the current scope", | |
362 | item_kind, | |
363 | item_name, | |
364 | rcvr_ty.prefix_string(self.tcx), | |
365 | ty_str_reported, | |
366 | ) | |
367 | }; | |
9ffffee4 FG |
368 | if tcx.sess.source_map().is_multiline(sugg_span) { |
369 | err.span_label(sugg_span.with_hi(span.lo()), ""); | |
370 | } | |
371 | let ty_str = if short_ty_str.len() < ty_str.len() && ty_str.len() > 10 { | |
372 | short_ty_str | |
373 | } else { | |
374 | ty_str | |
375 | }; | |
376 | if let Some(file) = ty_file { | |
377 | err.note(&format!("the full type name has been written to '{}'", file.display(),)); | |
378 | } | |
9c376795 FG |
379 | if rcvr_ty.references_error() { |
380 | err.downgrade_to_delayed_bug(); | |
381 | } | |
04454e1e | 382 | |
353b0b11 FG |
383 | if tcx.ty_is_opaque_future(rcvr_ty) && item_name.name == sym::poll { |
384 | err.help(&format!( | |
385 | "method `poll` found on `Pin<&mut {ty_str}>`, \ | |
386 | see documentation for `std::pin::Pin`" | |
387 | )); | |
388 | err.help("self type must be pinned to call `Future::poll`, \ | |
389 | see https://rust-lang.github.io/async-book/04_pinning/01_chapter.html#pinning-in-practice" | |
390 | ); | |
391 | } | |
392 | ||
9c376795 FG |
393 | if let Mode::MethodCall = mode && let SelfSource::MethodCall(cal) = source { |
394 | self.suggest_await_before_method( | |
395 | &mut err, item_name, rcvr_ty, cal, span, expected.only_has_type(self), | |
396 | ); | |
397 | } | |
398 | if let Some(span) = | |
399 | tcx.resolutions(()).confused_type_with_std_module.get(&span.with_parent(None)) | |
400 | { | |
401 | err.span_suggestion( | |
402 | span.shrink_to_lo(), | |
403 | "you are looking for the module in `std`, not the primitive type", | |
404 | "std::", | |
405 | Applicability::MachineApplicable, | |
406 | ); | |
407 | } | |
408 | if let ty::RawPtr(_) = &rcvr_ty.kind() { | |
409 | err.note( | |
410 | "try using `<*const T>::as_ref()` to get a reference to the \ | |
411 | type behind the pointer: https://doc.rust-lang.org/std/\ | |
412 | primitive.pointer.html#method.as_ref", | |
413 | ); | |
414 | err.note( | |
415 | "using `<*const T>::as_ref()` on a pointer which is unaligned or points \ | |
416 | to invalid or uninitialized memory is undefined behavior", | |
417 | ); | |
418 | } | |
5e7ed085 | 419 | |
9c376795 FG |
420 | let ty_span = match rcvr_ty.kind() { |
421 | ty::Param(param_type) => { | |
9ffffee4 | 422 | Some(param_type.span_from_generics(self.tcx, self.body_id.to_def_id())) |
9c376795 FG |
423 | } |
424 | ty::Adt(def, _) if def.did().is_local() => Some(tcx.def_span(def.did())), | |
425 | _ => None, | |
426 | }; | |
427 | if let Some(span) = ty_span { | |
428 | err.span_label( | |
429 | span, | |
430 | format!( | |
431 | "{item_kind} `{item_name}` not found for this {}", | |
432 | rcvr_ty.prefix_string(self.tcx) | |
433 | ), | |
434 | ); | |
435 | } | |
ff7c6d11 | 436 | |
9c376795 FG |
437 | if let SelfSource::MethodCall(rcvr_expr) = source { |
438 | self.suggest_fn_call(&mut err, rcvr_expr, rcvr_ty, |output_ty| { | |
439 | let call_expr = | |
440 | self.tcx.hir().expect_expr(self.tcx.hir().parent_id(rcvr_expr.hir_id)); | |
441 | let probe = self.lookup_probe_for_diagnostic( | |
442 | item_name, | |
443 | output_ty, | |
444 | call_expr, | |
445 | ProbeScope::AllTraits, | |
446 | expected.only_has_type(self), | |
447 | ); | |
448 | probe.is_ok() | |
449 | }); | |
353b0b11 FG |
450 | |
451 | self.note_internal_mutation_in_method( | |
452 | &mut err, | |
453 | rcvr_expr, | |
454 | expected.to_option(&self), | |
455 | rcvr_ty, | |
456 | ); | |
9c376795 | 457 | } |
85aaf69f | 458 | |
9c376795 | 459 | let mut custom_span_label = false; |
5e7ed085 | 460 | |
9c376795 FG |
461 | let static_candidates = &mut no_match_data.static_candidates; |
462 | if !static_candidates.is_empty() { | |
463 | err.note( | |
464 | "found the following associated functions; to be used as methods, \ | |
465 | functions must have a `self` parameter", | |
466 | ); | |
467 | err.span_label(span, "this is an associated function, not a method"); | |
468 | custom_span_label = true; | |
469 | } | |
470 | if static_candidates.len() == 1 { | |
471 | self.suggest_associated_call_syntax( | |
472 | &mut err, | |
473 | &static_candidates, | |
474 | rcvr_ty, | |
475 | source, | |
476 | item_name, | |
477 | args, | |
478 | sugg_span, | |
479 | ); | |
9c376795 FG |
480 | self.note_candidates_on_method_error( |
481 | rcvr_ty, | |
482 | item_name, | |
483 | args, | |
484 | span, | |
485 | &mut err, | |
486 | static_candidates, | |
487 | None, | |
488 | ); | |
489 | } else if static_candidates.len() > 1 { | |
490 | self.note_candidates_on_method_error( | |
491 | rcvr_ty, | |
492 | item_name, | |
493 | args, | |
494 | span, | |
495 | &mut err, | |
496 | static_candidates, | |
497 | Some(sugg_span), | |
498 | ); | |
499 | } | |
85aaf69f | 500 | |
9c376795 FG |
501 | let mut bound_spans = vec![]; |
502 | let mut restrict_type_params = false; | |
503 | let mut unsatisfied_bounds = false; | |
504 | if item_name.name == sym::count && self.is_slice_ty(rcvr_ty, span) { | |
505 | let msg = "consider using `len` instead"; | |
506 | if let SelfSource::MethodCall(_expr) = source { | |
507 | err.span_suggestion_short(span, msg, "len", Applicability::MachineApplicable); | |
508 | } else { | |
509 | err.span_label(span, msg); | |
510 | } | |
511 | if let Some(iterator_trait) = self.tcx.get_diagnostic_item(sym::Iterator) { | |
512 | let iterator_trait = self.tcx.def_path_str(iterator_trait); | |
513 | err.note(&format!( | |
514 | "`count` is defined on `{iterator_trait}`, which `{rcvr_ty}` does not implement" | |
515 | )); | |
516 | } | |
517 | } else if !unsatisfied_predicates.is_empty() { | |
518 | let mut type_params = FxHashMap::default(); | |
519 | ||
520 | // Pick out the list of unimplemented traits on the receiver. | |
521 | // This is used for custom error messages with the `#[rustc_on_unimplemented]` attribute. | |
522 | let mut unimplemented_traits = FxHashMap::default(); | |
523 | let mut unimplemented_traits_only = true; | |
524 | for (predicate, _parent_pred, cause) in unsatisfied_predicates { | |
525 | if let (ty::PredicateKind::Clause(ty::Clause::Trait(p)), Some(cause)) = | |
526 | (predicate.kind().skip_binder(), cause.as_ref()) | |
527 | { | |
528 | if p.trait_ref.self_ty() != rcvr_ty { | |
529 | // This is necessary, not just to keep the errors clean, but also | |
530 | // because our derived obligations can wind up with a trait ref that | |
531 | // requires a different param_env to be correctly compared. | |
532 | continue; | |
5e7ed085 | 533 | } |
9c376795 FG |
534 | unimplemented_traits.entry(p.trait_ref.def_id).or_insert(( |
535 | predicate.kind().rebind(p.trait_ref), | |
536 | Obligation { | |
537 | cause: cause.clone(), | |
538 | param_env: self.param_env, | |
539 | predicate: *predicate, | |
540 | recursion_depth: 0, | |
541 | }, | |
542 | )); | |
543 | } | |
544 | } | |
5e7ed085 | 545 | |
9c376795 FG |
546 | // Make sure that, if any traits other than the found ones were involved, |
547 | // we don't don't report an unimplemented trait. | |
548 | // We don't want to say that `iter::Cloned` is not an iterator, just | |
549 | // because of some non-Clone item being iterated over. | |
550 | for (predicate, _parent_pred, _cause) in unsatisfied_predicates { | |
551 | match predicate.kind().skip_binder() { | |
552 | ty::PredicateKind::Clause(ty::Clause::Trait(p)) | |
553 | if unimplemented_traits.contains_key(&p.trait_ref.def_id) => {} | |
554 | _ => { | |
555 | unimplemented_traits_only = false; | |
556 | break; | |
5e7ed085 | 557 | } |
9c376795 FG |
558 | } |
559 | } | |
3dfed10e | 560 | |
9c376795 FG |
561 | let mut collect_type_param_suggestions = |
562 | |self_ty: Ty<'tcx>, parent_pred: ty::Predicate<'tcx>, obligation: &str| { | |
563 | // We don't care about regions here, so it's fine to skip the binder here. | |
564 | if let (ty::Param(_), ty::PredicateKind::Clause(ty::Clause::Trait(p))) = | |
565 | (self_ty.kind(), parent_pred.kind().skip_binder()) | |
566 | { | |
567 | let hir = self.tcx.hir(); | |
568 | let node = match p.trait_ref.self_ty().kind() { | |
569 | ty::Param(_) => { | |
570 | // Account for `fn` items like in `issue-35677.rs` to | |
571 | // suggest restricting its type params. | |
9ffffee4 | 572 | Some(hir.get_by_def_id(self.body_id)) |
74b04a01 | 573 | } |
064997fb | 574 | ty::Adt(def, _) => { |
9c376795 | 575 | def.did().as_local().map(|def_id| hir.get_by_def_id(def_id)) |
064997fb | 576 | } |
9c376795 FG |
577 | _ => None, |
578 | }; | |
579 | if let Some(hir::Node::Item(hir::Item { kind, .. })) = node | |
580 | && let Some(g) = kind.generics() | |
581 | { | |
582 | let key = ( | |
583 | g.tail_span_for_predicate_suggestion(), | |
584 | g.add_where_or_trailing_comma(), | |
585 | ); | |
586 | type_params | |
587 | .entry(key) | |
588 | .or_insert_with(FxHashSet::default) | |
589 | .insert(obligation.to_owned()); | |
590 | return true; | |
591 | } | |
592 | } | |
593 | false | |
594 | }; | |
595 | let mut bound_span_label = |self_ty: Ty<'_>, obligation: &str, quiet: &str| { | |
596 | let msg = format!( | |
597 | "doesn't satisfy `{}`", | |
598 | if obligation.len() > 50 { quiet } else { obligation } | |
599 | ); | |
600 | match &self_ty.kind() { | |
601 | // Point at the type that couldn't satisfy the bound. | |
602 | ty::Adt(def, _) => bound_spans.push((self.tcx.def_span(def.did()), msg)), | |
603 | // Point at the trait object that couldn't satisfy the bound. | |
604 | ty::Dynamic(preds, _, _) => { | |
605 | for pred in preds.iter() { | |
606 | match pred.skip_binder() { | |
607 | ty::ExistentialPredicate::Trait(tr) => { | |
608 | bound_spans.push((self.tcx.def_span(tr.def_id), msg.clone())) | |
74b04a01 | 609 | } |
9c376795 FG |
610 | ty::ExistentialPredicate::Projection(_) |
611 | | ty::ExistentialPredicate::AutoTrait(_) => {} | |
74b04a01 | 612 | } |
74b04a01 | 613 | } |
9c376795 FG |
614 | } |
615 | // Point at the closure that couldn't satisfy the bound. | |
616 | ty::Closure(def_id, _) => bound_spans | |
617 | .push((tcx.def_span(*def_id), format!("doesn't satisfy `{}`", quiet))), | |
618 | _ => {} | |
619 | } | |
620 | }; | |
621 | let mut format_pred = |pred: ty::Predicate<'tcx>| { | |
622 | let bound_predicate = pred.kind(); | |
623 | match bound_predicate.skip_binder() { | |
624 | ty::PredicateKind::Clause(ty::Clause::Projection(pred)) => { | |
625 | let pred = bound_predicate.rebind(pred); | |
626 | // `<Foo as Iterator>::Item = String`. | |
627 | let projection_ty = pred.skip_binder().projection_ty; | |
628 | ||
9ffffee4 | 629 | let substs_with_infer_self = tcx.mk_substs_from_iter( |
9c376795 FG |
630 | iter::once(tcx.mk_ty_var(ty::TyVid::from_u32(0)).into()) |
631 | .chain(projection_ty.substs.iter().skip(1)), | |
632 | ); | |
6a06907d | 633 | |
9c376795 FG |
634 | let quiet_projection_ty = |
635 | tcx.mk_alias_ty(projection_ty.def_id, substs_with_infer_self); | |
3c0e092e | 636 | |
9c376795 | 637 | let term = pred.skip_binder().term; |
5e7ed085 | 638 | |
9c376795 FG |
639 | let obligation = format!("{} = {}", projection_ty, term); |
640 | let quiet = with_forced_trimmed_paths!(format!( | |
641 | "{} = {}", | |
642 | quiet_projection_ty, term | |
643 | )); | |
5e7ed085 | 644 | |
9c376795 FG |
645 | bound_span_label(projection_ty.self_ty(), &obligation, &quiet); |
646 | Some((obligation, projection_ty.self_ty())) | |
3c0e092e | 647 | } |
9c376795 FG |
648 | ty::PredicateKind::Clause(ty::Clause::Trait(poly_trait_ref)) => { |
649 | let p = poly_trait_ref.trait_ref; | |
650 | let self_ty = p.self_ty(); | |
651 | let path = p.print_only_trait_path(); | |
652 | let obligation = format!("{}: {}", self_ty, path); | |
653 | let quiet = with_forced_trimmed_paths!(format!("_: {}", path)); | |
654 | bound_span_label(self_ty, &obligation, &quiet); | |
655 | Some((obligation, self_ty)) | |
3c0e092e | 656 | } |
9c376795 FG |
657 | _ => None, |
658 | } | |
659 | }; | |
3c0e092e | 660 | |
9c376795 FG |
661 | // Find all the requirements that come from a local `impl` block. |
662 | let mut skip_list: FxHashSet<_> = Default::default(); | |
663 | let mut spanned_predicates = FxHashMap::default(); | |
353b0b11 FG |
664 | for (p, parent_p, cause) in unsatisfied_predicates { |
665 | // Extract the predicate span and parent def id of the cause, | |
666 | // if we have one. | |
667 | let (item_def_id, cause_span) = match cause.as_ref().map(|cause| cause.code()) { | |
668 | Some(ObligationCauseCode::ImplDerivedObligation(data)) => { | |
669 | (data.impl_or_alias_def_id, data.span) | |
74b04a01 | 670 | } |
353b0b11 FG |
671 | Some( |
672 | ObligationCauseCode::ExprBindingObligation(def_id, span, _, _) | |
673 | | ObligationCauseCode::BindingObligation(def_id, span), | |
674 | ) => (*def_id, *span), | |
675 | _ => continue, | |
676 | }; | |
677 | ||
678 | // Don't point out the span of `WellFormed` predicates. | |
679 | if !matches!(p.kind().skip_binder(), ty::PredicateKind::Clause(_)) { | |
680 | continue; | |
681 | }; | |
682 | ||
683 | match self.tcx.hir().get_if_local(item_def_id) { | |
9c376795 FG |
684 | // Unmet obligation comes from a `derive` macro, point at it once to |
685 | // avoid multiple span labels pointing at the same place. | |
686 | Some(Node::Item(hir::Item { | |
687 | kind: hir::ItemKind::Impl(hir::Impl { of_trait, self_ty, .. }), | |
688 | .. | |
689 | })) if matches!( | |
690 | self_ty.span.ctxt().outer_expn_data().kind, | |
691 | ExpnKind::Macro(MacroKind::Derive, _) | |
692 | ) || matches!( | |
693 | of_trait.as_ref().map(|t| t.path.span.ctxt().outer_expn_data().kind), | |
694 | Some(ExpnKind::Macro(MacroKind::Derive, _)) | |
695 | ) => | |
696 | { | |
697 | let span = self_ty.span.ctxt().outer_expn_data().call_site; | |
698 | let entry = spanned_predicates.entry(span); | |
699 | let entry = entry.or_insert_with(|| { | |
700 | (FxHashSet::default(), FxHashSet::default(), Vec::new()) | |
701 | }); | |
702 | entry.0.insert(span); | |
703 | entry.1.insert(( | |
704 | span, | |
705 | "unsatisfied trait bound introduced in this `derive` macro", | |
5869c6ff | 706 | )); |
9c376795 FG |
707 | entry.2.push(p); |
708 | skip_list.insert(p); | |
709 | } | |
710 | ||
711 | // Unmet obligation coming from an `impl`. | |
712 | Some(Node::Item(hir::Item { | |
713 | kind: hir::ItemKind::Impl(hir::Impl { of_trait, self_ty, generics, .. }), | |
714 | span: item_span, | |
715 | .. | |
716 | })) => { | |
717 | let sized_pred = | |
718 | unsatisfied_predicates.iter().any(|(pred, _, _)| { | |
719 | match pred.kind().skip_binder() { | |
720 | ty::PredicateKind::Clause(ty::Clause::Trait(pred)) => { | |
721 | Some(pred.def_id()) == self.tcx.lang_items().sized_trait() | |
722 | && pred.polarity == ty::ImplPolarity::Positive | |
723 | } | |
724 | _ => false, | |
725 | } | |
726 | }); | |
727 | for param in generics.params { | |
353b0b11 | 728 | if param.span == cause_span && sized_pred { |
9c376795 FG |
729 | let (sp, sugg) = match param.colon_span { |
730 | Some(sp) => (sp.shrink_to_hi(), " ?Sized +"), | |
731 | None => (param.span.shrink_to_hi(), ": ?Sized"), | |
732 | }; | |
733 | err.span_suggestion_verbose( | |
734 | sp, | |
735 | "consider relaxing the type parameter's implicit `Sized` bound", | |
736 | sugg, | |
737 | Applicability::MachineApplicable, | |
738 | ); | |
739 | } | |
5e7ed085 | 740 | } |
9c376795 FG |
741 | if let Some(pred) = parent_p { |
742 | // Done to add the "doesn't satisfy" `span_label`. | |
743 | let _ = format_pred(*pred); | |
3c0e092e | 744 | } |
9c376795 FG |
745 | skip_list.insert(p); |
746 | let entry = spanned_predicates.entry(self_ty.span); | |
747 | let entry = entry.or_insert_with(|| { | |
748 | (FxHashSet::default(), FxHashSet::default(), Vec::new()) | |
749 | }); | |
750 | entry.2.push(p); | |
353b0b11 FG |
751 | if cause_span != *item_span { |
752 | entry.0.insert(cause_span); | |
753 | entry.1.insert((cause_span, "unsatisfied trait bound introduced here")); | |
9c376795 FG |
754 | } else { |
755 | if let Some(trait_ref) = of_trait { | |
756 | entry.0.insert(trait_ref.path.span); | |
757 | } | |
758 | entry.0.insert(self_ty.span); | |
759 | }; | |
760 | if let Some(trait_ref) = of_trait { | |
761 | entry.1.insert((trait_ref.path.span, "")); | |
762 | } | |
763 | entry.1.insert((self_ty.span, "")); | |
764 | } | |
765 | Some(Node::Item(hir::Item { | |
766 | kind: hir::ItemKind::Trait(rustc_ast::ast::IsAuto::Yes, ..), | |
767 | span: item_span, | |
768 | .. | |
769 | })) => { | |
770 | tcx.sess.delay_span_bug( | |
771 | *item_span, | |
772 | "auto trait is invoked with no method error, but no error reported?", | |
773 | ); | |
774 | } | |
775 | Some(Node::Item(hir::Item { | |
9ffffee4 FG |
776 | ident, |
777 | kind: hir::ItemKind::Trait(..) | hir::ItemKind::TraitAlias(..), | |
778 | .. | |
9c376795 FG |
779 | })) => { |
780 | skip_list.insert(p); | |
781 | let entry = spanned_predicates.entry(ident.span); | |
782 | let entry = entry.or_insert_with(|| { | |
783 | (FxHashSet::default(), FxHashSet::default(), Vec::new()) | |
784 | }); | |
353b0b11 | 785 | entry.0.insert(cause_span); |
9c376795 | 786 | entry.1.insert((ident.span, "")); |
353b0b11 | 787 | entry.1.insert((cause_span, "unsatisfied trait bound introduced here")); |
9c376795 | 788 | entry.2.push(p); |
74b04a01 | 789 | } |
9c376795 FG |
790 | Some(node) => unreachable!("encountered `{node:?}`"), |
791 | None => (), | |
792 | } | |
793 | } | |
794 | let mut spanned_predicates: Vec<_> = spanned_predicates.into_iter().collect(); | |
795 | spanned_predicates.sort_by_key(|(span, _)| *span); | |
796 | for (_, (primary_spans, span_labels, predicates)) in spanned_predicates { | |
797 | let mut preds: Vec<_> = predicates | |
798 | .iter() | |
799 | .filter_map(|pred| format_pred(**pred)) | |
800 | .map(|(p, _)| format!("`{}`", p)) | |
801 | .collect(); | |
802 | preds.sort(); | |
803 | preds.dedup(); | |
804 | let msg = if let [pred] = &preds[..] { | |
805 | format!("trait bound {} was not satisfied", pred) | |
806 | } else { | |
807 | format!("the following trait bounds were not satisfied:\n{}", preds.join("\n"),) | |
808 | }; | |
809 | let mut span: MultiSpan = primary_spans.into_iter().collect::<Vec<_>>().into(); | |
810 | for (sp, label) in span_labels { | |
811 | span.push_span_label(sp, label); | |
a7813a04 | 812 | } |
9c376795 FG |
813 | err.span_note(span, &msg); |
814 | unsatisfied_bounds = true; | |
815 | } | |
62682a34 | 816 | |
9c376795 FG |
817 | let mut suggested_bounds = FxHashSet::default(); |
818 | // The requirements that didn't have an `impl` span to show. | |
819 | let mut bound_list = unsatisfied_predicates | |
820 | .iter() | |
821 | .filter_map(|(pred, parent_pred, _cause)| { | |
822 | let mut suggested = false; | |
823 | format_pred(*pred).map(|(p, self_ty)| { | |
824 | if let Some(parent) = parent_pred && suggested_bounds.contains(parent) { | |
825 | // We don't suggest `PartialEq` when we already suggest `Eq`. | |
826 | } else if !suggested_bounds.contains(pred) { | |
827 | if collect_type_param_suggestions(self_ty, *pred, &p) { | |
828 | suggested = true; | |
829 | suggested_bounds.insert(pred); | |
5e7ed085 | 830 | } |
5e7ed085 | 831 | } |
9c376795 FG |
832 | ( |
833 | match parent_pred { | |
834 | None => format!("`{}`", &p), | |
835 | Some(parent_pred) => match format_pred(*parent_pred) { | |
836 | None => format!("`{}`", &p), | |
837 | Some((parent_p, _)) => { | |
838 | if !suggested | |
839 | && !suggested_bounds.contains(pred) | |
840 | && !suggested_bounds.contains(parent_pred) | |
841 | { | |
842 | if collect_type_param_suggestions( | |
843 | self_ty, | |
844 | *parent_pred, | |
845 | &p, | |
846 | ) { | |
847 | suggested_bounds.insert(pred); | |
5e7ed085 | 848 | } |
5e7ed085 | 849 | } |
9c376795 | 850 | format!("`{}`\nwhich is required by `{}`", p, parent_p) |
5e7ed085 | 851 | } |
9c376795 FG |
852 | }, |
853 | }, | |
854 | *pred, | |
855 | ) | |
856 | }) | |
857 | }) | |
858 | .filter(|(_, pred)| !skip_list.contains(&pred)) | |
859 | .map(|(t, _)| t) | |
860 | .enumerate() | |
861 | .collect::<Vec<(usize, String)>>(); | |
862 | ||
863 | for ((span, add_where_or_comma), obligations) in type_params.into_iter() { | |
864 | restrict_type_params = true; | |
865 | // #74886: Sort here so that the output is always the same. | |
866 | let mut obligations = obligations.into_iter().collect::<Vec<_>>(); | |
867 | obligations.sort(); | |
868 | err.span_suggestion_verbose( | |
869 | span, | |
870 | &format!( | |
871 | "consider restricting the type parameter{s} to satisfy the \ | |
872 | trait bound{s}", | |
873 | s = pluralize!(obligations.len()) | |
874 | ), | |
875 | format!("{} {}", add_where_or_comma, obligations.join(", ")), | |
876 | Applicability::MaybeIncorrect, | |
877 | ); | |
878 | } | |
879 | ||
880 | bound_list.sort_by(|(_, a), (_, b)| a.cmp(b)); // Sort alphabetically. | |
881 | bound_list.dedup_by(|(_, a), (_, b)| a == b); // #35677 | |
882 | bound_list.sort_by_key(|(pos, _)| *pos); // Keep the original predicate order. | |
883 | ||
884 | if !bound_list.is_empty() || !skip_list.is_empty() { | |
885 | let bound_list = | |
886 | bound_list.into_iter().map(|(_, path)| path).collect::<Vec<_>>().join("\n"); | |
887 | let actual_prefix = rcvr_ty.prefix_string(self.tcx); | |
888 | info!("unimplemented_traits.len() == {}", unimplemented_traits.len()); | |
889 | let (primary_message, label) = if unimplemented_traits.len() == 1 | |
890 | && unimplemented_traits_only | |
891 | { | |
892 | unimplemented_traits | |
893 | .into_iter() | |
894 | .next() | |
895 | .map(|(_, (trait_ref, obligation))| { | |
896 | if trait_ref.self_ty().references_error() || rcvr_ty.references_error() | |
897 | { | |
898 | // Avoid crashing. | |
899 | return (None, None); | |
5e7ed085 | 900 | } |
9c376795 FG |
901 | let OnUnimplementedNote { message, label, .. } = |
902 | self.err_ctxt().on_unimplemented_note(trait_ref, &obligation); | |
903 | (message, label) | |
904 | }) | |
905 | .unwrap() | |
906 | } else { | |
907 | (None, None) | |
5e7ed085 | 908 | }; |
9c376795 FG |
909 | let primary_message = primary_message.unwrap_or_else(|| { |
910 | format!( | |
911 | "the {item_kind} `{item_name}` exists for {actual_prefix} `{ty_str}`, \ | |
9ffffee4 | 912 | but its trait bounds were not satisfied" |
9c376795 FG |
913 | ) |
914 | }); | |
915 | err.set_primary_message(&primary_message); | |
916 | if let Some(label) = label { | |
917 | custom_span_label = true; | |
918 | err.span_label(span, label); | |
f2b60f7d | 919 | } |
9c376795 FG |
920 | if !bound_list.is_empty() { |
921 | err.note(&format!( | |
922 | "the following trait bounds were not satisfied:\n{bound_list}" | |
923 | )); | |
5e7ed085 | 924 | } |
9c376795 | 925 | self.suggest_derive(&mut err, &unsatisfied_predicates); |
5e7ed085 | 926 | |
9c376795 FG |
927 | unsatisfied_bounds = true; |
928 | } | |
929 | } | |
ea8adc8c | 930 | |
9c376795 FG |
931 | let label_span_not_found = |err: &mut Diagnostic| { |
932 | if unsatisfied_predicates.is_empty() { | |
933 | err.span_label(span, format!("{item_kind} not found in `{ty_str}`")); | |
934 | let is_string_or_ref_str = match rcvr_ty.kind() { | |
935 | ty::Ref(_, ty, _) => { | |
936 | ty.is_str() | |
937 | || matches!( | |
938 | ty.kind(), | |
939 | ty::Adt(adt, _) if Some(adt.did()) == self.tcx.lang_items().string() | |
940 | ) | |
48663c56 | 941 | } |
9c376795 FG |
942 | ty::Adt(adt, _) => Some(adt.did()) == self.tcx.lang_items().string(), |
943 | _ => false, | |
944 | }; | |
945 | if is_string_or_ref_str && item_name.name == sym::iter { | |
946 | err.span_suggestion_verbose( | |
947 | item_name.span, | |
948 | "because of the in-memory representation of `&str`, to obtain \ | |
949 | an `Iterator` over each of its codepoint use method `chars`", | |
950 | "chars", | |
951 | Applicability::MachineApplicable, | |
952 | ); | |
48663c56 | 953 | } |
9c376795 FG |
954 | if let ty::Adt(adt, _) = rcvr_ty.kind() { |
955 | let mut inherent_impls_candidate = self | |
956 | .tcx | |
957 | .inherent_impls(adt.did()) | |
958 | .iter() | |
959 | .copied() | |
960 | .filter(|def_id| { | |
961 | if let Some(assoc) = self.associated_value(*def_id, item_name) { | |
962 | // Check for both mode is the same so we avoid suggesting | |
963 | // incorrect associated item. | |
964 | match (mode, assoc.fn_has_self_parameter, source) { | |
965 | (Mode::MethodCall, true, SelfSource::MethodCall(_)) => { | |
966 | // We check that the suggest type is actually | |
967 | // different from the received one | |
968 | // So we avoid suggestion method with Box<Self> | |
969 | // for instance | |
9ffffee4 FG |
970 | self.tcx.at(span).type_of(*def_id).subst_identity() |
971 | != rcvr_ty | |
972 | && self.tcx.at(span).type_of(*def_id).subst_identity() | |
973 | != rcvr_ty | |
9c376795 FG |
974 | } |
975 | (Mode::Path, false, _) => true, | |
976 | _ => false, | |
977 | } | |
978 | } else { | |
979 | false | |
980 | } | |
981 | }) | |
982 | .collect::<Vec<_>>(); | |
983 | if !inherent_impls_candidate.is_empty() { | |
984 | inherent_impls_candidate.sort(); | |
985 | inherent_impls_candidate.dedup(); | |
986 | ||
987 | // number of type to shows at most. | |
988 | let limit = if inherent_impls_candidate.len() == 5 { 5 } else { 4 }; | |
989 | let type_candidates = inherent_impls_candidate | |
990 | .iter() | |
991 | .take(limit) | |
992 | .map(|impl_item| { | |
9ffffee4 FG |
993 | format!( |
994 | "- `{}`", | |
995 | self.tcx.at(span).type_of(*impl_item).subst_identity() | |
996 | ) | |
9c376795 FG |
997 | }) |
998 | .collect::<Vec<_>>() | |
999 | .join("\n"); | |
1000 | let additional_types = if inherent_impls_candidate.len() > limit { | |
1001 | format!("\nand {} more types", inherent_impls_candidate.len() - limit) | |
f2b60f7d | 1002 | } else { |
9c376795 FG |
1003 | "".to_string() |
1004 | }; | |
1005 | err.note(&format!( | |
1006 | "the {item_kind} was found for\n{}{}", | |
1007 | type_candidates, additional_types | |
1008 | )); | |
5869c6ff | 1009 | } |
ea8adc8c | 1010 | } |
9c376795 FG |
1011 | } else { |
1012 | let ty_str = | |
1013 | if ty_str.len() > 50 { String::new() } else { format!("on `{ty_str}` ") }; | |
1014 | err.span_label( | |
1015 | span, | |
1016 | format!("{item_kind} cannot be called {ty_str}due to unsatisfied trait bounds"), | |
1017 | ); | |
1018 | } | |
1019 | }; | |
48663c56 | 1020 | |
9c376795 FG |
1021 | // If the method name is the name of a field with a function or closure type, |
1022 | // give a helping note that it has to be called as `(x.f)(...)`. | |
1023 | if let SelfSource::MethodCall(expr) = source { | |
1024 | if !self.suggest_calling_field_as_fn(span, rcvr_ty, expr, item_name, &mut err) | |
9ffffee4 | 1025 | && similar_candidate.is_none() |
9c376795 FG |
1026 | && !custom_span_label |
1027 | { | |
1028 | label_span_not_found(&mut err); | |
a7813a04 | 1029 | } |
9c376795 FG |
1030 | } else if !custom_span_label { |
1031 | label_span_not_found(&mut err); | |
1032 | } | |
85aaf69f | 1033 | |
9c376795 FG |
1034 | // Don't suggest (for example) `expr.field.clone()` if `expr.clone()` |
1035 | // can't be called due to `typeof(expr): Clone` not holding. | |
1036 | if unsatisfied_predicates.is_empty() { | |
1037 | self.suggest_calling_method_on_field( | |
1038 | &mut err, | |
1039 | source, | |
1040 | span, | |
1041 | rcvr_ty, | |
1042 | item_name, | |
1043 | expected.only_has_type(self), | |
1044 | ); | |
1045 | } | |
85aaf69f | 1046 | |
9c376795 | 1047 | self.check_for_inner_self(&mut err, source, rcvr_ty, item_name); |
85aaf69f | 1048 | |
9c376795 FG |
1049 | bound_spans.sort(); |
1050 | bound_spans.dedup(); | |
1051 | for (span, msg) in bound_spans.into_iter() { | |
1052 | err.span_label(span, &msg); | |
1053 | } | |
1054 | ||
1055 | if rcvr_ty.is_numeric() && rcvr_ty.is_fresh() || restrict_type_params { | |
1056 | } else { | |
1057 | self.suggest_traits_to_import( | |
1058 | &mut err, | |
1059 | span, | |
1060 | rcvr_ty, | |
1061 | item_name, | |
1062 | args.map(|(_, args)| args.len() + 1), | |
1063 | source, | |
1064 | no_match_data.out_of_scope_traits.clone(), | |
1065 | &unsatisfied_predicates, | |
1066 | &static_candidates, | |
1067 | unsatisfied_bounds, | |
1068 | expected.only_has_type(self), | |
1069 | ); | |
1070 | } | |
1071 | ||
1072 | // Don't emit a suggestion if we found an actual method | |
1073 | // that had unsatisfied trait bounds | |
1074 | if unsatisfied_predicates.is_empty() && rcvr_ty.is_enum() { | |
1075 | let adt_def = rcvr_ty.ty_adt_def().expect("enum is not an ADT"); | |
9ffffee4 | 1076 | if let Some(suggestion) = edit_distance::find_best_match_for_name( |
9c376795 FG |
1077 | &adt_def.variants().iter().map(|s| s.name).collect::<Vec<_>>(), |
1078 | item_name.name, | |
1079 | None, | |
1080 | ) { | |
1081 | err.span_suggestion( | |
1082 | span, | |
1083 | "there is a variant with a similar name", | |
1084 | suggestion, | |
1085 | Applicability::MaybeIncorrect, | |
dfeec247 | 1086 | ); |
a7813a04 | 1087 | } |
9c376795 | 1088 | } |
3b2f2976 | 1089 | |
9c376795 FG |
1090 | if item_name.name == sym::as_str && rcvr_ty.peel_refs().is_str() { |
1091 | let msg = "remove this method call"; | |
1092 | let mut fallback_span = true; | |
1093 | if let SelfSource::MethodCall(expr) = source { | |
1094 | let call_expr = self.tcx.hir().expect_expr(self.tcx.hir().parent_id(expr.hir_id)); | |
1095 | if let Some(span) = call_expr.span.trim_start(expr.span) { | |
1096 | err.span_suggestion(span, msg, "", Applicability::MachineApplicable); | |
1097 | fallback_span = false; | |
1098 | } | |
1099 | } | |
1100 | if fallback_span { | |
1101 | err.span_label(span, msg); | |
1102 | } | |
9ffffee4 | 1103 | } else if let Some(similar_candidate) = similar_candidate { |
9c376795 FG |
1104 | // Don't emit a suggestion if we found an actual method |
1105 | // that had unsatisfied trait bounds | |
1106 | if unsatisfied_predicates.is_empty() { | |
9ffffee4 | 1107 | let def_kind = similar_candidate.kind.as_def_kind(); |
9c376795 FG |
1108 | // Methods are defined within the context of a struct and their first parameter is always self, |
1109 | // which represents the instance of the struct the method is being called on | |
1110 | // Associated functions don’t take self as a parameter and | |
1111 | // they are not methods because they don’t have an instance of the struct to work with. | |
9ffffee4 | 1112 | if def_kind == DefKind::AssocFn && similar_candidate.fn_has_self_parameter { |
9c376795 FG |
1113 | err.span_suggestion( |
1114 | span, | |
1115 | "there is a method with a similar name", | |
9ffffee4 | 1116 | similar_candidate.name, |
9c376795 FG |
1117 | Applicability::MaybeIncorrect, |
1118 | ); | |
1119 | } else { | |
1120 | err.span_suggestion( | |
1121 | span, | |
1122 | &format!( | |
1123 | "there is {} {} with a similar name", | |
9ffffee4 FG |
1124 | self.tcx.def_kind_descr_article(def_kind, similar_candidate.def_id), |
1125 | self.tcx.def_kind_descr(def_kind, similar_candidate.def_id) | |
9c376795 | 1126 | ), |
9ffffee4 | 1127 | similar_candidate.name, |
9c376795 | 1128 | Applicability::MaybeIncorrect, |
e74abb32 | 1129 | ); |
3b2f2976 | 1130 | } |
9c376795 FG |
1131 | } |
1132 | } | |
1133 | ||
1134 | self.check_for_deref_method(&mut err, source, rcvr_ty, item_name, expected); | |
1135 | return Some(err); | |
1136 | } | |
1137 | ||
1138 | fn note_candidates_on_method_error( | |
1139 | &self, | |
1140 | rcvr_ty: Ty<'tcx>, | |
1141 | item_name: Ident, | |
1142 | args: Option<(&'tcx hir::Expr<'tcx>, &'tcx [hir::Expr<'tcx>])>, | |
1143 | span: Span, | |
1144 | err: &mut Diagnostic, | |
1145 | sources: &mut Vec<CandidateSource>, | |
1146 | sugg_span: Option<Span>, | |
1147 | ) { | |
1148 | sources.sort(); | |
1149 | sources.dedup(); | |
1150 | // Dynamic limit to avoid hiding just one candidate, which is silly. | |
1151 | let limit = if sources.len() == 5 { 5 } else { 4 }; | |
1152 | ||
1153 | for (idx, source) in sources.iter().take(limit).enumerate() { | |
1154 | match *source { | |
1155 | CandidateSource::Impl(impl_did) => { | |
1156 | // Provide the best span we can. Use the item, if local to crate, else | |
1157 | // the impl, if local to crate (item may be defaulted), else nothing. | |
1158 | let Some(item) = self.associated_value(impl_did, item_name).or_else(|| { | |
1159 | let impl_trait_ref = self.tcx.impl_trait_ref(impl_did)?; | |
1160 | self.associated_value(impl_trait_ref.skip_binder().def_id, item_name) | |
1161 | }) else { | |
1162 | continue; | |
1163 | }; | |
1164 | ||
1165 | let note_span = if item.def_id.is_local() { | |
1166 | Some(self.tcx.def_span(item.def_id)) | |
1167 | } else if impl_did.is_local() { | |
1168 | Some(self.tcx.def_span(impl_did)) | |
1169 | } else { | |
1170 | None | |
1171 | }; | |
1172 | ||
9ffffee4 | 1173 | let impl_ty = self.tcx.at(span).type_of(impl_did).subst_identity(); |
9c376795 FG |
1174 | |
1175 | let insertion = match self.tcx.impl_trait_ref(impl_did) { | |
1176 | None => String::new(), | |
1177 | Some(trait_ref) => { | |
1178 | format!( | |
1179 | " of the trait `{}`", | |
1180 | self.tcx.def_path_str(trait_ref.skip_binder().def_id) | |
1181 | ) | |
1182 | } | |
1183 | }; | |
1184 | ||
1185 | let (note_str, idx) = if sources.len() > 1 { | |
1186 | ( | |
1187 | format!( | |
1188 | "candidate #{} is defined in an impl{} for the type `{}`", | |
1189 | idx + 1, | |
1190 | insertion, | |
1191 | impl_ty, | |
1192 | ), | |
1193 | Some(idx + 1), | |
1194 | ) | |
1195 | } else { | |
1196 | ( | |
1197 | format!( | |
1198 | "the candidate is defined in an impl{} for the type `{}`", | |
1199 | insertion, impl_ty, | |
1200 | ), | |
1201 | None, | |
1202 | ) | |
1203 | }; | |
1204 | if let Some(note_span) = note_span { | |
1205 | // We have a span pointing to the method. Show note with snippet. | |
1206 | err.span_note(note_span, ¬e_str); | |
1207 | } else { | |
1208 | err.note(¬e_str); | |
1209 | } | |
1210 | if let Some(sugg_span) = sugg_span | |
1211 | && let Some(trait_ref) = self.tcx.impl_trait_ref(impl_did) { | |
1212 | let path = self.tcx.def_path_str(trait_ref.skip_binder().def_id); | |
1213 | ||
1214 | let ty = match item.kind { | |
1215 | ty::AssocKind::Const | ty::AssocKind::Type => rcvr_ty, | |
1216 | ty::AssocKind::Fn => self | |
1217 | .tcx | |
1218 | .fn_sig(item.def_id) | |
9ffffee4 | 1219 | .subst_identity() |
9c376795 FG |
1220 | .inputs() |
1221 | .skip_binder() | |
1222 | .get(0) | |
353b0b11 | 1223 | .filter(|ty| ty.is_ref() && !rcvr_ty.is_ref()) |
9c376795 FG |
1224 | .copied() |
1225 | .unwrap_or(rcvr_ty), | |
1226 | }; | |
1227 | print_disambiguation_help( | |
1228 | item_name, | |
1229 | args, | |
1230 | err, | |
1231 | path, | |
1232 | ty, | |
1233 | item.kind, | |
9ffffee4 | 1234 | self.tcx.def_kind_descr(item.kind.as_def_kind(), item.def_id), |
9c376795 FG |
1235 | sugg_span, |
1236 | idx, | |
1237 | self.tcx.sess.source_map(), | |
1238 | item.fn_has_self_parameter, | |
1239 | ); | |
1240 | } | |
1241 | } | |
1242 | CandidateSource::Trait(trait_did) => { | |
1243 | let Some(item) = self.associated_value(trait_did, item_name) else { continue }; | |
1244 | let item_span = self.tcx.def_span(item.def_id); | |
1245 | let idx = if sources.len() > 1 { | |
1246 | let msg = &format!( | |
1247 | "candidate #{} is defined in the trait `{}`", | |
1248 | idx + 1, | |
1249 | self.tcx.def_path_str(trait_did) | |
1250 | ); | |
1251 | err.span_note(item_span, msg); | |
1252 | Some(idx + 1) | |
1253 | } else { | |
1254 | let msg = &format!( | |
1255 | "the candidate is defined in the trait `{}`", | |
1256 | self.tcx.def_path_str(trait_did) | |
1257 | ); | |
1258 | err.span_note(item_span, msg); | |
1259 | None | |
1260 | }; | |
1261 | if let Some(sugg_span) = sugg_span { | |
1262 | let path = self.tcx.def_path_str(trait_did); | |
1263 | print_disambiguation_help( | |
1264 | item_name, | |
1265 | args, | |
1266 | err, | |
1267 | path, | |
1268 | rcvr_ty, | |
1269 | item.kind, | |
9ffffee4 | 1270 | self.tcx.def_kind_descr(item.kind.as_def_kind(), item.def_id), |
9c376795 FG |
1271 | sugg_span, |
1272 | idx, | |
1273 | self.tcx.sess.source_map(), | |
1274 | item.fn_has_self_parameter, | |
dfeec247 | 1275 | ); |
e74abb32 XL |
1276 | } |
1277 | } | |
3b2f2976 XL |
1278 | } |
1279 | } | |
9c376795 FG |
1280 | if sources.len() > limit { |
1281 | err.note(&format!("and {} others", sources.len() - limit)); | |
1282 | } | |
3b2f2976 XL |
1283 | } |
1284 | ||
487cf647 FG |
1285 | /// Suggest calling `Ty::method` if `.method()` isn't found because the method |
1286 | /// doesn't take a `self` receiver. | |
1287 | fn suggest_associated_call_syntax( | |
1288 | &self, | |
1289 | err: &mut Diagnostic, | |
1290 | static_candidates: &Vec<CandidateSource>, | |
1291 | rcvr_ty: Ty<'tcx>, | |
1292 | source: SelfSource<'tcx>, | |
1293 | item_name: Ident, | |
1294 | args: Option<(&hir::Expr<'tcx>, &[hir::Expr<'tcx>])>, | |
1295 | sugg_span: Span, | |
1296 | ) { | |
1297 | let mut has_unsuggestable_args = false; | |
1298 | let ty_str = if let Some(CandidateSource::Impl(impl_did)) = static_candidates.get(0) { | |
1299 | // When the "method" is resolved through dereferencing, we really want the | |
1300 | // original type that has the associated function for accurate suggestions. | |
1301 | // (#61411) | |
9ffffee4 | 1302 | let impl_ty = self.tcx.type_of(*impl_did).subst_identity(); |
487cf647 FG |
1303 | let target_ty = self |
1304 | .autoderef(sugg_span, rcvr_ty) | |
1305 | .find(|(rcvr_ty, _)| { | |
353b0b11 | 1306 | DeepRejectCtxt { treat_obligation_params: TreatParams::AsCandidateKey } |
487cf647 FG |
1307 | .types_may_unify(*rcvr_ty, impl_ty) |
1308 | }) | |
1309 | .map_or(impl_ty, |(ty, _)| ty) | |
1310 | .peel_refs(); | |
1311 | if let ty::Adt(def, substs) = target_ty.kind() { | |
1312 | // If there are any inferred arguments, (`{integer}`), we should replace | |
1313 | // them with underscores to allow the compiler to infer them | |
9ffffee4 | 1314 | let infer_substs = self.tcx.mk_substs_from_iter(substs.into_iter().map(|arg| { |
487cf647 FG |
1315 | if !arg.is_suggestable(self.tcx, true) { |
1316 | has_unsuggestable_args = true; | |
1317 | match arg.unpack() { | |
1318 | GenericArgKind::Lifetime(_) => self | |
1319 | .next_region_var(RegionVariableOrigin::MiscVariable( | |
1320 | rustc_span::DUMMY_SP, | |
1321 | )) | |
1322 | .into(), | |
1323 | GenericArgKind::Type(_) => self | |
1324 | .next_ty_var(TypeVariableOrigin { | |
1325 | span: rustc_span::DUMMY_SP, | |
1326 | kind: TypeVariableOriginKind::MiscVariable, | |
1327 | }) | |
1328 | .into(), | |
1329 | GenericArgKind::Const(arg) => self | |
1330 | .next_const_var( | |
1331 | arg.ty(), | |
1332 | ConstVariableOrigin { | |
1333 | span: rustc_span::DUMMY_SP, | |
1334 | kind: ConstVariableOriginKind::MiscVariable, | |
1335 | }, | |
1336 | ) | |
1337 | .into(), | |
1338 | } | |
1339 | } else { | |
1340 | arg | |
1341 | } | |
1342 | })); | |
1343 | ||
1344 | self.tcx.value_path_str_with_substs(def.did(), infer_substs) | |
1345 | } else { | |
1346 | self.ty_to_value_string(target_ty) | |
1347 | } | |
1348 | } else { | |
1349 | self.ty_to_value_string(rcvr_ty.peel_refs()) | |
1350 | }; | |
1351 | if let SelfSource::MethodCall(_) = source { | |
1352 | let first_arg = if let Some(CandidateSource::Impl(impl_did)) = static_candidates.get(0) | |
1353 | && let Some(assoc) = self.associated_value(*impl_did, item_name) | |
1354 | && assoc.kind == ty::AssocKind::Fn | |
1355 | { | |
9ffffee4 | 1356 | let sig = self.tcx.fn_sig(assoc.def_id).subst_identity(); |
487cf647 FG |
1357 | sig.inputs().skip_binder().get(0).and_then(|first| if first.peel_refs() == rcvr_ty.peel_refs() { |
1358 | None | |
1359 | } else { | |
1360 | Some(first.ref_mutability().map_or("", |mutbl| mutbl.ref_prefix_str())) | |
1361 | }) | |
1362 | } else { | |
1363 | None | |
1364 | }; | |
1365 | let mut applicability = Applicability::MachineApplicable; | |
1366 | let args = if let Some((receiver, args)) = args { | |
1367 | // The first arg is the same kind as the receiver | |
1368 | let explicit_args = if first_arg.is_some() { | |
1369 | std::iter::once(receiver).chain(args.iter()).collect::<Vec<_>>() | |
1370 | } else { | |
1371 | // There is no `Self` kind to infer the arguments from | |
1372 | if has_unsuggestable_args { | |
1373 | applicability = Applicability::HasPlaceholders; | |
1374 | } | |
1375 | args.iter().collect() | |
1376 | }; | |
1377 | format!( | |
1378 | "({}{})", | |
1379 | first_arg.unwrap_or(""), | |
1380 | explicit_args | |
1381 | .iter() | |
1382 | .map(|arg| self | |
1383 | .tcx | |
1384 | .sess | |
1385 | .source_map() | |
1386 | .span_to_snippet(arg.span) | |
1387 | .unwrap_or_else(|_| { | |
1388 | applicability = Applicability::HasPlaceholders; | |
1389 | "_".to_owned() | |
1390 | })) | |
1391 | .collect::<Vec<_>>() | |
1392 | .join(", "), | |
1393 | ) | |
1394 | } else { | |
1395 | applicability = Applicability::HasPlaceholders; | |
1396 | "(...)".to_owned() | |
1397 | }; | |
1398 | err.span_suggestion( | |
1399 | sugg_span, | |
1400 | "use associated function syntax instead", | |
1401 | format!("{}::{}{}", ty_str, item_name, args), | |
1402 | applicability, | |
1403 | ); | |
1404 | } else { | |
1405 | err.help(&format!("try with `{}::{}`", ty_str, item_name,)); | |
1406 | } | |
1407 | } | |
1408 | ||
1409 | /// Suggest calling a field with a type that implements the `Fn*` traits instead of a method with | |
1410 | /// the same name as the field i.e. `(a.my_fn_ptr)(10)` instead of `a.my_fn_ptr(10)`. | |
1411 | fn suggest_calling_field_as_fn( | |
04454e1e FG |
1412 | &self, |
1413 | span: Span, | |
1414 | rcvr_ty: Ty<'tcx>, | |
1415 | expr: &hir::Expr<'_>, | |
1416 | item_name: Ident, | |
f2b60f7d | 1417 | err: &mut Diagnostic, |
04454e1e FG |
1418 | ) -> bool { |
1419 | let tcx = self.tcx; | |
1420 | let field_receiver = self.autoderef(span, rcvr_ty).find_map(|(ty, _)| match ty.kind() { | |
1421 | ty::Adt(def, substs) if !def.is_enum() => { | |
1422 | let variant = &def.non_enum_variant(); | |
1423 | tcx.find_field_index(item_name, variant).map(|index| { | |
1424 | let field = &variant.fields[index]; | |
1425 | let field_ty = field.ty(tcx, substs); | |
1426 | (field, field_ty) | |
1427 | }) | |
1428 | } | |
1429 | _ => None, | |
1430 | }); | |
1431 | if let Some((field, field_ty)) = field_receiver { | |
9ffffee4 | 1432 | let scope = tcx.parent_module_from_def_id(self.body_id); |
04454e1e FG |
1433 | let is_accessible = field.vis.is_accessible_from(scope, tcx); |
1434 | ||
1435 | if is_accessible { | |
1436 | if self.is_fn_ty(field_ty, span) { | |
1437 | let expr_span = expr.span.to(item_name.span); | |
1438 | err.multipart_suggestion( | |
1439 | &format!( | |
1440 | "to call the function stored in `{}`, \ | |
1441 | surround the field access with parentheses", | |
1442 | item_name, | |
1443 | ), | |
1444 | vec![ | |
1445 | (expr_span.shrink_to_lo(), '('.to_string()), | |
1446 | (expr_span.shrink_to_hi(), ')'.to_string()), | |
1447 | ], | |
1448 | Applicability::MachineApplicable, | |
1449 | ); | |
1450 | } else { | |
9c376795 | 1451 | let call_expr = tcx.hir().expect_expr(tcx.hir().parent_id(expr.hir_id)); |
04454e1e FG |
1452 | |
1453 | if let Some(span) = call_expr.span.trim_start(item_name.span) { | |
1454 | err.span_suggestion( | |
1455 | span, | |
1456 | "remove the arguments", | |
923072b8 | 1457 | "", |
04454e1e FG |
1458 | Applicability::MaybeIncorrect, |
1459 | ); | |
1460 | } | |
1461 | } | |
1462 | } | |
1463 | ||
1464 | let field_kind = if is_accessible { "field" } else { "private field" }; | |
1465 | err.span_label(item_name.span, format!("{}, not a method", field_kind)); | |
1466 | return true; | |
1467 | } | |
1468 | false | |
1469 | } | |
1470 | ||
2b03887a FG |
1471 | /// Suggest possible range with adding parentheses, for example: |
1472 | /// when encountering `0..1.map(|i| i + 1)` suggest `(0..1).map(|i| i + 1)`. | |
1473 | fn suggest_wrapping_range_with_parens( | |
1474 | &self, | |
1475 | tcx: TyCtxt<'tcx>, | |
1476 | actual: Ty<'tcx>, | |
1477 | source: SelfSource<'tcx>, | |
1478 | span: Span, | |
1479 | item_name: Ident, | |
1480 | ty_str: &str, | |
1481 | ) -> bool { | |
1482 | if let SelfSource::MethodCall(expr) = source { | |
1483 | for (_, parent) in tcx.hir().parent_iter(expr.hir_id).take(5) { | |
1484 | if let Node::Expr(parent_expr) = parent { | |
1485 | let lang_item = match parent_expr.kind { | |
1486 | ExprKind::Struct(ref qpath, _, _) => match **qpath { | |
1487 | QPath::LangItem(LangItem::Range, ..) => Some(LangItem::Range), | |
1488 | QPath::LangItem(LangItem::RangeTo, ..) => Some(LangItem::RangeTo), | |
1489 | QPath::LangItem(LangItem::RangeToInclusive, ..) => { | |
1490 | Some(LangItem::RangeToInclusive) | |
1491 | } | |
1492 | _ => None, | |
1493 | }, | |
1494 | ExprKind::Call(ref func, _) => match func.kind { | |
1495 | // `..=` desugars into `::std::ops::RangeInclusive::new(...)`. | |
1496 | ExprKind::Path(QPath::LangItem(LangItem::RangeInclusiveNew, ..)) => { | |
1497 | Some(LangItem::RangeInclusiveStruct) | |
1498 | } | |
1499 | _ => None, | |
1500 | }, | |
1501 | _ => None, | |
1502 | }; | |
1503 | ||
1504 | if lang_item.is_none() { | |
1505 | continue; | |
1506 | } | |
1507 | ||
1508 | let span_included = match parent_expr.kind { | |
1509 | hir::ExprKind::Struct(_, eps, _) => { | |
1510 | eps.len() > 0 && eps.last().map_or(false, |ep| ep.span.contains(span)) | |
1511 | } | |
1512 | // `..=` desugars into `::std::ops::RangeInclusive::new(...)`. | |
1513 | hir::ExprKind::Call(ref func, ..) => func.span.contains(span), | |
1514 | _ => false, | |
1515 | }; | |
1516 | ||
1517 | if !span_included { | |
1518 | continue; | |
1519 | } | |
1520 | ||
1521 | let range_def_id = self.tcx.require_lang_item(lang_item.unwrap(), None); | |
9ffffee4 | 1522 | let range_ty = self.tcx.type_of(range_def_id).subst(self.tcx, &[actual.into()]); |
2b03887a | 1523 | |
9c376795 | 1524 | let pick = self.lookup_probe_for_diagnostic( |
2b03887a | 1525 | item_name, |
2b03887a | 1526 | range_ty, |
9c376795 | 1527 | expr, |
2b03887a | 1528 | ProbeScope::AllTraits, |
9c376795 | 1529 | None, |
2b03887a FG |
1530 | ); |
1531 | if pick.is_ok() { | |
1532 | let range_span = parent_expr.span.with_hi(expr.span.hi()); | |
1533 | tcx.sess.emit_err(errors::MissingParentheseInRange { | |
1534 | span, | |
1535 | ty_str: ty_str.to_string(), | |
1536 | method_name: item_name.as_str().to_string(), | |
1537 | add_missing_parentheses: Some(errors::AddMissingParenthesesInRange { | |
1538 | func_name: item_name.name.as_str().to_string(), | |
1539 | left: range_span.shrink_to_lo(), | |
1540 | right: range_span.shrink_to_hi(), | |
1541 | }), | |
1542 | }); | |
1543 | return true; | |
1544 | } | |
1545 | } | |
1546 | } | |
1547 | } | |
1548 | false | |
1549 | } | |
1550 | ||
04454e1e FG |
1551 | fn suggest_constraining_numerical_ty( |
1552 | &self, | |
1553 | tcx: TyCtxt<'tcx>, | |
1554 | actual: Ty<'tcx>, | |
1555 | source: SelfSource<'_>, | |
1556 | span: Span, | |
1557 | item_kind: &str, | |
1558 | item_name: Ident, | |
1559 | ty_str: &str, | |
1560 | ) -> bool { | |
1561 | let found_candidate = all_traits(self.tcx) | |
1562 | .into_iter() | |
1563 | .any(|info| self.associated_value(info.def_id, item_name).is_some()); | |
1564 | let found_assoc = |ty: Ty<'tcx>| { | |
353b0b11 | 1565 | simplify_type(tcx, ty, TreatParams::AsCandidateKey) |
04454e1e FG |
1566 | .and_then(|simp| { |
1567 | tcx.incoherent_impls(simp) | |
1568 | .iter() | |
1569 | .find_map(|&id| self.associated_value(id, item_name)) | |
1570 | }) | |
1571 | .is_some() | |
1572 | }; | |
1573 | let found_candidate = found_candidate | |
1574 | || found_assoc(tcx.types.i8) | |
1575 | || found_assoc(tcx.types.i16) | |
1576 | || found_assoc(tcx.types.i32) | |
1577 | || found_assoc(tcx.types.i64) | |
1578 | || found_assoc(tcx.types.i128) | |
1579 | || found_assoc(tcx.types.u8) | |
1580 | || found_assoc(tcx.types.u16) | |
1581 | || found_assoc(tcx.types.u32) | |
1582 | || found_assoc(tcx.types.u64) | |
1583 | || found_assoc(tcx.types.u128) | |
1584 | || found_assoc(tcx.types.f32) | |
1585 | || found_assoc(tcx.types.f32); | |
1586 | if found_candidate | |
1587 | && actual.is_numeric() | |
1588 | && !actual.has_concrete_skeleton() | |
1589 | && let SelfSource::MethodCall(expr) = source | |
1590 | { | |
1591 | let mut err = struct_span_err!( | |
1592 | tcx.sess, | |
1593 | span, | |
1594 | E0689, | |
1595 | "can't call {} `{}` on ambiguous numeric type `{}`", | |
1596 | item_kind, | |
1597 | item_name, | |
1598 | ty_str | |
1599 | ); | |
1600 | let concrete_type = if actual.is_integral() { "i32" } else { "f32" }; | |
1601 | match expr.kind { | |
1602 | ExprKind::Lit(ref lit) => { | |
1603 | // numeric literal | |
1604 | let snippet = tcx | |
1605 | .sess | |
1606 | .source_map() | |
1607 | .span_to_snippet(lit.span) | |
1608 | .unwrap_or_else(|_| "<numeric literal>".to_owned()); | |
1609 | ||
1610 | // If this is a floating point literal that ends with '.', | |
1611 | // get rid of it to stop this from becoming a member access. | |
1612 | let snippet = snippet.strip_suffix('.').unwrap_or(&snippet); | |
04454e1e FG |
1613 | err.span_suggestion( |
1614 | lit.span, | |
1615 | &format!( | |
1616 | "you must specify a concrete type for this numeric value, \ | |
1617 | like `{}`", | |
1618 | concrete_type | |
1619 | ), | |
1620 | format!("{snippet}_{concrete_type}"), | |
1621 | Applicability::MaybeIncorrect, | |
1622 | ); | |
1623 | } | |
1624 | ExprKind::Path(QPath::Resolved(_, path)) => { | |
1625 | // local binding | |
1626 | if let hir::def::Res::Local(hir_id) = path.res { | |
1627 | let span = tcx.hir().span(hir_id); | |
04454e1e FG |
1628 | let filename = tcx.sess.source_map().span_to_filename(span); |
1629 | ||
1630 | let parent_node = | |
9c376795 | 1631 | self.tcx.hir().get_parent(hir_id); |
04454e1e FG |
1632 | let msg = format!( |
1633 | "you must specify a type for this binding, like `{}`", | |
1634 | concrete_type, | |
1635 | ); | |
1636 | ||
f2b60f7d | 1637 | match (filename, parent_node) { |
04454e1e FG |
1638 | ( |
1639 | FileName::Real(_), | |
1640 | Node::Local(hir::Local { | |
1641 | source: hir::LocalSource::Normal, | |
1642 | ty, | |
1643 | .. | |
1644 | }), | |
04454e1e | 1645 | ) => { |
f2b60f7d | 1646 | let type_span = ty.map(|ty| ty.span.with_lo(span.hi())).unwrap_or(span.shrink_to_hi()); |
04454e1e FG |
1647 | err.span_suggestion( |
1648 | // account for `let x: _ = 42;` | |
f2b60f7d FG |
1649 | // ^^^ |
1650 | type_span, | |
04454e1e | 1651 | &msg, |
f2b60f7d | 1652 | format!(": {concrete_type}"), |
04454e1e FG |
1653 | Applicability::MaybeIncorrect, |
1654 | ); | |
1655 | } | |
1656 | _ => { | |
1657 | err.span_label(span, msg); | |
1658 | } | |
1659 | } | |
1660 | } | |
1661 | } | |
1662 | _ => {} | |
1663 | } | |
1664 | err.emit(); | |
1665 | return true; | |
1666 | } | |
1667 | false | |
1668 | } | |
1669 | ||
487cf647 FG |
1670 | /// For code `rect::area(...)`, |
1671 | /// if `rect` is a local variable and `area` is a valid assoc method for it, | |
1672 | /// we try to suggest `rect.area()` | |
1673 | pub(crate) fn suggest_assoc_method_call(&self, segs: &[PathSegment<'_>]) { | |
1674 | debug!("suggest_assoc_method_call segs: {:?}", segs); | |
1675 | let [seg1, seg2] = segs else { return; }; | |
1676 | let Some(mut diag) = | |
1677 | self.tcx.sess.diagnostic().steal_diagnostic(seg1.ident.span, StashKey::CallAssocMethod) | |
1678 | else { return }; | |
1679 | ||
1680 | let map = self.infcx.tcx.hir(); | |
9ffffee4 FG |
1681 | let body_id = self.tcx.hir().body_owned_by(self.body_id); |
1682 | let body = map.body(body_id); | |
487cf647 FG |
1683 | struct LetVisitor<'a> { |
1684 | result: Option<&'a hir::Expr<'a>>, | |
1685 | ident_name: Symbol, | |
1686 | } | |
1687 | ||
1688 | // FIXME: This really should be taking scoping, etc into account. | |
1689 | impl<'v> Visitor<'v> for LetVisitor<'v> { | |
1690 | fn visit_stmt(&mut self, ex: &'v hir::Stmt<'v>) { | |
1691 | if let hir::StmtKind::Local(hir::Local { pat, init, .. }) = &ex.kind | |
1692 | && let Binding(_, _, ident, ..) = pat.kind | |
1693 | && ident.name == self.ident_name | |
1694 | { | |
1695 | self.result = *init; | |
1696 | } else { | |
1697 | hir::intravisit::walk_stmt(self, ex); | |
1698 | } | |
1699 | } | |
1700 | } | |
1701 | ||
1702 | let mut visitor = LetVisitor { result: None, ident_name: seg1.ident.name }; | |
1703 | visitor.visit_body(&body); | |
1704 | ||
9c376795 | 1705 | let parent = self.tcx.hir().parent_id(seg1.hir_id); |
487cf647 FG |
1706 | if let Some(Node::Expr(call_expr)) = self.tcx.hir().find(parent) |
1707 | && let Some(expr) = visitor.result | |
1708 | && let Some(self_ty) = self.node_ty_opt(expr.hir_id) | |
1709 | { | |
9c376795 | 1710 | let probe = self.lookup_probe_for_diagnostic( |
487cf647 FG |
1711 | seg2.ident, |
1712 | self_ty, | |
1713 | call_expr, | |
1714 | ProbeScope::TraitsInScope, | |
9c376795 | 1715 | None, |
487cf647 FG |
1716 | ); |
1717 | if probe.is_ok() { | |
1718 | let sm = self.infcx.tcx.sess.source_map(); | |
1719 | diag.span_suggestion_verbose( | |
1720 | sm.span_extend_while(seg1.ident.span.shrink_to_hi(), |c| c == ':').unwrap(), | |
1721 | "you may have meant to call an instance method", | |
1722 | ".".to_string(), | |
1723 | Applicability::MaybeIncorrect, | |
1724 | ); | |
1725 | } | |
1726 | } | |
1727 | diag.emit(); | |
1728 | } | |
1729 | ||
1730 | /// Suggest calling a method on a field i.e. `a.field.bar()` instead of `a.bar()` | |
1731 | fn suggest_calling_method_on_field( | |
923072b8 | 1732 | &self, |
f2b60f7d | 1733 | err: &mut Diagnostic, |
923072b8 FG |
1734 | source: SelfSource<'tcx>, |
1735 | span: Span, | |
1736 | actual: Ty<'tcx>, | |
1737 | item_name: Ident, | |
9c376795 | 1738 | return_type: Option<Ty<'tcx>>, |
923072b8 FG |
1739 | ) { |
1740 | if let SelfSource::MethodCall(expr) = source | |
f2b60f7d FG |
1741 | && let mod_id = self.tcx.parent_module(expr.hir_id).to_def_id() |
1742 | && let Some((fields, substs)) = | |
1743 | self.get_field_candidates_considering_privacy(span, actual, mod_id) | |
923072b8 | 1744 | { |
9c376795 | 1745 | let call_expr = self.tcx.hir().expect_expr(self.tcx.hir().parent_id(expr.hir_id)); |
f2b60f7d FG |
1746 | |
1747 | let lang_items = self.tcx.lang_items(); | |
1748 | let never_mention_traits = [ | |
1749 | lang_items.clone_trait(), | |
1750 | lang_items.deref_trait(), | |
1751 | lang_items.deref_mut_trait(), | |
1752 | self.tcx.get_diagnostic_item(sym::AsRef), | |
1753 | self.tcx.get_diagnostic_item(sym::AsMut), | |
1754 | self.tcx.get_diagnostic_item(sym::Borrow), | |
1755 | self.tcx.get_diagnostic_item(sym::BorrowMut), | |
1756 | ]; | |
1757 | let candidate_fields: Vec<_> = fields | |
1758 | .filter_map(|candidate_field| { | |
1759 | self.check_for_nested_field_satisfying( | |
1760 | span, | |
1761 | &|_, field_ty| { | |
9c376795 | 1762 | self.lookup_probe_for_diagnostic( |
f2b60f7d FG |
1763 | item_name, |
1764 | field_ty, | |
1765 | call_expr, | |
1766 | ProbeScope::TraitsInScope, | |
9c376795 | 1767 | return_type, |
f2b60f7d FG |
1768 | ) |
1769 | .map_or(false, |pick| { | |
1770 | !never_mention_traits | |
1771 | .iter() | |
1772 | .flatten() | |
1773 | .any(|def_id| self.tcx.parent(pick.item.def_id) == *def_id) | |
1774 | }) | |
1775 | }, | |
1776 | candidate_field, | |
1777 | substs, | |
1778 | vec![], | |
1779 | mod_id, | |
1780 | ) | |
1781 | }) | |
1782 | .map(|field_path| { | |
1783 | field_path | |
923072b8 FG |
1784 | .iter() |
1785 | .map(|id| id.name.to_ident_string()) | |
1786 | .collect::<Vec<String>>() | |
f2b60f7d FG |
1787 | .join(".") |
1788 | }) | |
1789 | .collect(); | |
1790 | ||
1791 | let len = candidate_fields.len(); | |
1792 | if len > 0 { | |
1793 | err.span_suggestions( | |
1794 | item_name.span.shrink_to_lo(), | |
1795 | format!( | |
1796 | "{} of the expressions' fields {} a method of the same name", | |
1797 | if len > 1 { "some" } else { "one" }, | |
1798 | if len > 1 { "have" } else { "has" }, | |
1799 | ), | |
1800 | candidate_fields.iter().map(|path| format!("{path}.")), | |
1801 | Applicability::MaybeIncorrect, | |
1802 | ); | |
923072b8 FG |
1803 | } |
1804 | } | |
1805 | } | |
1806 | ||
2b03887a | 1807 | fn check_for_inner_self( |
923072b8 | 1808 | &self, |
f2b60f7d | 1809 | err: &mut Diagnostic, |
923072b8 | 1810 | source: SelfSource<'tcx>, |
923072b8 FG |
1811 | actual: Ty<'tcx>, |
1812 | item_name: Ident, | |
1813 | ) { | |
1814 | let tcx = self.tcx; | |
1815 | let SelfSource::MethodCall(expr) = source else { return; }; | |
9c376795 | 1816 | let call_expr = tcx.hir().expect_expr(tcx.hir().parent_id(expr.hir_id)); |
923072b8 FG |
1817 | |
1818 | let ty::Adt(kind, substs) = actual.kind() else { return; }; | |
2b03887a FG |
1819 | match kind.adt_kind() { |
1820 | ty::AdtKind::Enum => { | |
1821 | let matching_variants: Vec<_> = kind | |
1822 | .variants() | |
1823 | .iter() | |
1824 | .flat_map(|variant| { | |
353b0b11 | 1825 | let [field] = &variant.fields.raw[..] else { return None; }; |
2b03887a FG |
1826 | let field_ty = field.ty(tcx, substs); |
1827 | ||
1828 | // Skip `_`, since that'll just lead to ambiguity. | |
1829 | if self.resolve_vars_if_possible(field_ty).is_ty_var() { | |
1830 | return None; | |
1831 | } | |
923072b8 | 1832 | |
9c376795 FG |
1833 | self.lookup_probe_for_diagnostic( |
1834 | item_name, | |
1835 | field_ty, | |
1836 | call_expr, | |
1837 | ProbeScope::TraitsInScope, | |
1838 | None, | |
1839 | ) | |
1840 | .ok() | |
1841 | .map(|pick| (variant, field, pick)) | |
2b03887a FG |
1842 | }) |
1843 | .collect(); | |
1844 | ||
1845 | let ret_ty_matches = |diagnostic_item| { | |
1846 | if let Some(ret_ty) = self | |
1847 | .ret_coercion | |
1848 | .as_ref() | |
1849 | .map(|c| self.resolve_vars_if_possible(c.borrow().expected_ty())) | |
1850 | && let ty::Adt(kind, _) = ret_ty.kind() | |
1851 | && tcx.get_diagnostic_item(diagnostic_item) == Some(kind.did()) | |
1852 | { | |
1853 | true | |
1854 | } else { | |
1855 | false | |
1856 | } | |
1857 | }; | |
923072b8 | 1858 | |
2b03887a FG |
1859 | match &matching_variants[..] { |
1860 | [(_, field, pick)] => { | |
1861 | let self_ty = field.ty(tcx, substs); | |
1862 | err.span_note( | |
1863 | tcx.def_span(pick.item.def_id), | |
1864 | &format!("the method `{item_name}` exists on the type `{self_ty}`"), | |
1865 | ); | |
1866 | let (article, kind, variant, question) = | |
1867 | if tcx.is_diagnostic_item(sym::Result, kind.did()) { | |
1868 | ("a", "Result", "Err", ret_ty_matches(sym::Result)) | |
1869 | } else if tcx.is_diagnostic_item(sym::Option, kind.did()) { | |
1870 | ("an", "Option", "None", ret_ty_matches(sym::Option)) | |
1871 | } else { | |
1872 | return; | |
1873 | }; | |
1874 | if question { | |
1875 | err.span_suggestion_verbose( | |
1876 | expr.span.shrink_to_hi(), | |
1877 | format!( | |
1878 | "use the `?` operator to extract the `{self_ty}` value, propagating \ | |
1879 | {article} `{kind}::{variant}` value to the caller" | |
1880 | ), | |
1881 | "?", | |
1882 | Applicability::MachineApplicable, | |
1883 | ); | |
1884 | } else { | |
1885 | err.span_suggestion_verbose( | |
1886 | expr.span.shrink_to_hi(), | |
1887 | format!( | |
1888 | "consider using `{kind}::expect` to unwrap the `{self_ty}` value, \ | |
1889 | panicking if the value is {article} `{kind}::{variant}`" | |
1890 | ), | |
1891 | ".expect(\"REASON\")", | |
1892 | Applicability::HasPlaceholders, | |
1893 | ); | |
1894 | } | |
1895 | } | |
1896 | // FIXME(compiler-errors): Support suggestions for other matching enum variants | |
1897 | _ => {} | |
923072b8 | 1898 | } |
923072b8 | 1899 | } |
2b03887a FG |
1900 | // Target wrapper types - types that wrap or pretend to wrap another type, |
1901 | // perhaps this inner type is meant to be called? | |
1902 | ty::AdtKind::Struct | ty::AdtKind::Union => { | |
1903 | let [first] = ***substs else { return; }; | |
1904 | let ty::GenericArgKind::Type(ty) = first.unpack() else { return; }; | |
9c376795 | 1905 | let Ok(pick) = self.lookup_probe_for_diagnostic( |
487cf647 FG |
1906 | item_name, |
1907 | ty, | |
1908 | call_expr, | |
1909 | ProbeScope::TraitsInScope, | |
9c376795 | 1910 | None, |
487cf647 | 1911 | ) else { return; }; |
923072b8 | 1912 | |
2b03887a FG |
1913 | let name = self.ty_to_value_string(actual); |
1914 | let inner_id = kind.did(); | |
1915 | let mutable = if let Some(AutorefOrPtrAdjustment::Autoref { mutbl, .. }) = | |
1916 | pick.autoref_or_ptr_adjustment | |
1917 | { | |
1918 | Some(mutbl) | |
1919 | } else { | |
1920 | None | |
1921 | }; | |
1922 | ||
1923 | if tcx.is_diagnostic_item(sym::LocalKey, inner_id) { | |
1924 | err.help("use `with` or `try_with` to access thread local storage"); | |
1925 | } else if Some(kind.did()) == tcx.lang_items().maybe_uninit() { | |
1926 | err.help(format!( | |
1927 | "if this `{name}` has been initialized, \ | |
1928 | use one of the `assume_init` methods to access the inner value" | |
1929 | )); | |
1930 | } else if tcx.is_diagnostic_item(sym::RefCell, inner_id) { | |
1931 | let (suggestion, borrow_kind, panic_if) = match mutable { | |
1932 | Some(Mutability::Not) => (".borrow()", "borrow", "a mutable borrow exists"), | |
1933 | Some(Mutability::Mut) => { | |
1934 | (".borrow_mut()", "mutably borrow", "any borrows exist") | |
1935 | } | |
1936 | None => return, | |
923072b8 | 1937 | }; |
923072b8 FG |
1938 | err.span_suggestion_verbose( |
1939 | expr.span.shrink_to_hi(), | |
1940 | format!( | |
2b03887a FG |
1941 | "use `{suggestion}` to {borrow_kind} the `{ty}`, \ |
1942 | panicking if {panic_if}" | |
923072b8 | 1943 | ), |
2b03887a FG |
1944 | suggestion, |
1945 | Applicability::MaybeIncorrect, | |
923072b8 | 1946 | ); |
2b03887a | 1947 | } else if tcx.is_diagnostic_item(sym::Mutex, inner_id) { |
923072b8 FG |
1948 | err.span_suggestion_verbose( |
1949 | expr.span.shrink_to_hi(), | |
1950 | format!( | |
2b03887a FG |
1951 | "use `.lock().unwrap()` to borrow the `{ty}`, \ |
1952 | blocking the current thread until it can be acquired" | |
923072b8 | 1953 | ), |
2b03887a FG |
1954 | ".lock().unwrap()", |
1955 | Applicability::MaybeIncorrect, | |
923072b8 | 1956 | ); |
2b03887a FG |
1957 | } else if tcx.is_diagnostic_item(sym::RwLock, inner_id) { |
1958 | let (suggestion, borrow_kind) = match mutable { | |
1959 | Some(Mutability::Not) => (".read().unwrap()", "borrow"), | |
1960 | Some(Mutability::Mut) => (".write().unwrap()", "mutably borrow"), | |
1961 | None => return, | |
1962 | }; | |
1963 | err.span_suggestion_verbose( | |
1964 | expr.span.shrink_to_hi(), | |
1965 | format!( | |
1966 | "use `{suggestion}` to {borrow_kind} the `{ty}`, \ | |
1967 | blocking the current thread until it can be acquired" | |
1968 | ), | |
1969 | suggestion, | |
1970 | Applicability::MaybeIncorrect, | |
1971 | ); | |
1972 | } else { | |
1973 | return; | |
1974 | }; | |
1975 | ||
1976 | err.span_note( | |
1977 | tcx.def_span(pick.item.def_id), | |
1978 | &format!("the method `{item_name}` exists on the type `{ty}`"), | |
1979 | ); | |
923072b8 | 1980 | } |
923072b8 FG |
1981 | } |
1982 | } | |
1983 | ||
1984 | pub(crate) fn note_unmet_impls_on_type( | |
c295e0f8 | 1985 | &self, |
5e7ed085 | 1986 | err: &mut Diagnostic, |
c295e0f8 XL |
1987 | errors: Vec<FulfillmentError<'tcx>>, |
1988 | ) { | |
1989 | let all_local_types_needing_impls = | |
1990 | errors.iter().all(|e| match e.obligation.predicate.kind().skip_binder() { | |
487cf647 | 1991 | ty::PredicateKind::Clause(ty::Clause::Trait(pred)) => match pred.self_ty().kind() { |
5e7ed085 | 1992 | ty::Adt(def, _) => def.did().is_local(), |
c295e0f8 XL |
1993 | _ => false, |
1994 | }, | |
1995 | _ => false, | |
1996 | }); | |
1997 | let mut preds: Vec<_> = errors | |
1998 | .iter() | |
1999 | .filter_map(|e| match e.obligation.predicate.kind().skip_binder() { | |
487cf647 | 2000 | ty::PredicateKind::Clause(ty::Clause::Trait(pred)) => Some(pred), |
c295e0f8 XL |
2001 | _ => None, |
2002 | }) | |
2003 | .collect(); | |
2004 | preds.sort_by_key(|pred| (pred.def_id(), pred.self_ty())); | |
2005 | let def_ids = preds | |
2006 | .iter() | |
2007 | .filter_map(|pred| match pred.self_ty().kind() { | |
5e7ed085 | 2008 | ty::Adt(def, _) => Some(def.did()), |
c295e0f8 XL |
2009 | _ => None, |
2010 | }) | |
2011 | .collect::<FxHashSet<_>>(); | |
c295e0f8 XL |
2012 | let mut spans: MultiSpan = def_ids |
2013 | .iter() | |
2014 | .filter_map(|def_id| { | |
2015 | let span = self.tcx.def_span(*def_id); | |
064997fb | 2016 | if span.is_dummy() { None } else { Some(span) } |
c295e0f8 XL |
2017 | }) |
2018 | .collect::<Vec<_>>() | |
2019 | .into(); | |
2020 | ||
2021 | for pred in &preds { | |
2022 | match pred.self_ty().kind() { | |
064997fb | 2023 | ty::Adt(def, _) if def.did().is_local() => { |
c295e0f8 | 2024 | spans.push_span_label( |
064997fb | 2025 | self.tcx.def_span(def.did()), |
c295e0f8 XL |
2026 | format!("must implement `{}`", pred.trait_ref.print_only_trait_path()), |
2027 | ); | |
2028 | } | |
2029 | _ => {} | |
2030 | } | |
2031 | } | |
2032 | ||
2033 | if all_local_types_needing_impls && spans.primary_span().is_some() { | |
2034 | let msg = if preds.len() == 1 { | |
2035 | format!( | |
2036 | "an implementation of `{}` might be missing for `{}`", | |
2037 | preds[0].trait_ref.print_only_trait_path(), | |
2038 | preds[0].self_ty() | |
2039 | ) | |
2040 | } else { | |
2041 | format!( | |
2042 | "the following type{} would have to `impl` {} required trait{} for this \ | |
2043 | operation to be valid", | |
2044 | pluralize!(def_ids.len()), | |
2045 | if def_ids.len() == 1 { "its" } else { "their" }, | |
2046 | pluralize!(preds.len()), | |
2047 | ) | |
2048 | }; | |
2049 | err.span_note(spans, &msg); | |
2050 | } | |
2051 | ||
3c0e092e XL |
2052 | let preds: Vec<_> = errors |
2053 | .iter() | |
2054 | .map(|e| (e.obligation.predicate, None, Some(e.obligation.cause.clone()))) | |
2055 | .collect(); | |
c295e0f8 XL |
2056 | self.suggest_derive(err, &preds); |
2057 | } | |
2058 | ||
9c376795 | 2059 | pub fn suggest_derive( |
c295e0f8 | 2060 | &self, |
5e7ed085 | 2061 | err: &mut Diagnostic, |
a2a8927a | 2062 | unsatisfied_predicates: &[( |
3c0e092e XL |
2063 | ty::Predicate<'tcx>, |
2064 | Option<ty::Predicate<'tcx>>, | |
2065 | Option<ObligationCause<'tcx>>, | |
a2a8927a | 2066 | )], |
c295e0f8 | 2067 | ) { |
064997fb | 2068 | let mut derives = Vec::<(String, Span, Symbol)>::new(); |
9c376795 | 2069 | let mut traits = Vec::new(); |
3c0e092e | 2070 | for (pred, _, _) in unsatisfied_predicates { |
487cf647 | 2071 | let ty::PredicateKind::Clause(ty::Clause::Trait(trait_pred)) = pred.kind().skip_binder() else { continue }; |
c295e0f8 | 2072 | let adt = match trait_pred.self_ty().ty_adt_def() { |
5e7ed085 | 2073 | Some(adt) if adt.did().is_local() => adt, |
c295e0f8 XL |
2074 | _ => continue, |
2075 | }; | |
5099ac24 FG |
2076 | if let Some(diagnostic_name) = self.tcx.get_diagnostic_name(trait_pred.def_id()) { |
2077 | let can_derive = match diagnostic_name { | |
2078 | sym::Default => !adt.is_enum(), | |
c295e0f8 XL |
2079 | sym::Eq |
2080 | | sym::PartialEq | |
2081 | | sym::Ord | |
2082 | | sym::PartialOrd | |
2083 | | sym::Clone | |
2084 | | sym::Copy | |
2085 | | sym::Hash | |
5099ac24 FG |
2086 | | sym::Debug => true, |
2087 | _ => false, | |
2088 | }; | |
2089 | if can_derive { | |
2090 | let self_name = trait_pred.self_ty().to_string(); | |
5e7ed085 | 2091 | let self_span = self.tcx.def_span(adt.did()); |
5099ac24 FG |
2092 | if let Some(poly_trait_ref) = pred.to_opt_poly_trait_pred() { |
2093 | for super_trait in supertraits(self.tcx, poly_trait_ref.to_poly_trait_ref()) | |
2094 | { | |
2095 | if let Some(parent_diagnostic_name) = | |
2096 | self.tcx.get_diagnostic_name(super_trait.def_id()) | |
2097 | { | |
2098 | derives.push(( | |
2099 | self_name.clone(), | |
923072b8 | 2100 | self_span, |
064997fb | 2101 | parent_diagnostic_name, |
5099ac24 FG |
2102 | )); |
2103 | } | |
2104 | } | |
2105 | } | |
064997fb | 2106 | derives.push((self_name, self_span, diagnostic_name)); |
5099ac24 | 2107 | } else { |
9c376795 | 2108 | traits.push(trait_pred.def_id()); |
5099ac24 | 2109 | } |
c295e0f8 | 2110 | } else { |
9c376795 | 2111 | traits.push(trait_pred.def_id()); |
c295e0f8 XL |
2112 | } |
2113 | } | |
c295e0f8 XL |
2114 | traits.sort(); |
2115 | traits.dedup(); | |
2116 | ||
a2a8927a XL |
2117 | derives.sort(); |
2118 | derives.dedup(); | |
2119 | ||
2120 | let mut derives_grouped = Vec::<(String, Span, String)>::new(); | |
2121 | for (self_name, self_span, trait_name) in derives.into_iter() { | |
2122 | if let Some((last_self_name, _, ref mut last_trait_names)) = derives_grouped.last_mut() | |
2123 | { | |
2124 | if last_self_name == &self_name { | |
2125 | last_trait_names.push_str(format!(", {}", trait_name).as_str()); | |
2126 | continue; | |
2127 | } | |
2128 | } | |
064997fb | 2129 | derives_grouped.push((self_name, self_span, trait_name.to_string())); |
a2a8927a XL |
2130 | } |
2131 | ||
c295e0f8 XL |
2132 | let len = traits.len(); |
2133 | if len > 0 { | |
9c376795 FG |
2134 | let span = |
2135 | MultiSpan::from_spans(traits.iter().map(|&did| self.tcx.def_span(did)).collect()); | |
2136 | let mut names = format!("`{}`", self.tcx.def_path_str(traits[0])); | |
2137 | for (i, &did) in traits.iter().enumerate().skip(1) { | |
2138 | if len > 2 { | |
2139 | names.push_str(", "); | |
2140 | } | |
2141 | if i == len - 1 { | |
2142 | names.push_str(" and "); | |
2143 | } | |
2144 | names.push('`'); | |
2145 | names.push_str(&self.tcx.def_path_str(did)); | |
2146 | names.push('`'); | |
2147 | } | |
c295e0f8 XL |
2148 | err.span_note( |
2149 | span, | |
9c376795 | 2150 | &format!("the trait{} {} must be implemented", pluralize!(len), names), |
c295e0f8 XL |
2151 | ); |
2152 | } | |
2153 | ||
2154 | for (self_name, self_span, traits) in &derives_grouped { | |
2155 | err.span_suggestion_verbose( | |
2156 | self_span.shrink_to_lo(), | |
2157 | &format!("consider annotating `{}` with `#[derive({})]`", self_name, traits), | |
2158 | format!("#[derive({})]\n", traits), | |
2159 | Applicability::MaybeIncorrect, | |
2160 | ); | |
2161 | } | |
2162 | } | |
2163 | ||
f2b60f7d FG |
2164 | fn check_for_deref_method( |
2165 | &self, | |
2166 | err: &mut Diagnostic, | |
2167 | self_source: SelfSource<'tcx>, | |
2168 | rcvr_ty: Ty<'tcx>, | |
2169 | item_name: Ident, | |
9c376795 | 2170 | expected: Expectation<'tcx>, |
f2b60f7d FG |
2171 | ) { |
2172 | let SelfSource::QPath(ty) = self_source else { return; }; | |
2173 | for (deref_ty, _) in self.autoderef(rustc_span::DUMMY_SP, rcvr_ty).skip(1) { | |
2174 | if let Ok(pick) = self.probe_for_name( | |
f2b60f7d FG |
2175 | Mode::Path, |
2176 | item_name, | |
9c376795 | 2177 | expected.only_has_type(self), |
f2b60f7d FG |
2178 | IsSuggestion(true), |
2179 | deref_ty, | |
2180 | ty.hir_id, | |
2181 | ProbeScope::TraitsInScope, | |
2182 | ) { | |
2183 | if deref_ty.is_suggestable(self.tcx, true) | |
2184 | // If this method receives `&self`, then the provided | |
2185 | // argument _should_ coerce, so it's valid to suggest | |
2186 | // just changing the path. | |
2187 | && pick.item.fn_has_self_parameter | |
2188 | && let Some(self_ty) = | |
9ffffee4 | 2189 | self.tcx.fn_sig(pick.item.def_id).subst_identity().inputs().skip_binder().get(0) |
f2b60f7d FG |
2190 | && self_ty.is_ref() |
2191 | { | |
2192 | let suggested_path = match deref_ty.kind() { | |
2193 | ty::Bool | |
2194 | | ty::Char | |
2195 | | ty::Int(_) | |
2196 | | ty::Uint(_) | |
2197 | | ty::Float(_) | |
2198 | | ty::Adt(_, _) | |
2199 | | ty::Str | |
9c376795 | 2200 | | ty::Alias(ty::Projection, _) |
f2b60f7d | 2201 | | ty::Param(_) => format!("{deref_ty}"), |
487cf647 FG |
2202 | // we need to test something like <&[_]>::len or <(&[u32])>::len |
2203 | // and Vec::function(); | |
2204 | // <&[_]>::len or <&[u32]>::len doesn't need an extra "<>" between | |
2205 | // but for Adt type like Vec::function() | |
2206 | // we would suggest <[_]>::function(); | |
2207 | _ if self.tcx.sess.source_map().span_wrapped_by_angle_or_parentheses(ty.span) => format!("{deref_ty}"), | |
f2b60f7d FG |
2208 | _ => format!("<{deref_ty}>"), |
2209 | }; | |
2210 | err.span_suggestion_verbose( | |
2211 | ty.span, | |
2212 | format!("the function `{item_name}` is implemented on `{deref_ty}`"), | |
2213 | suggested_path, | |
2214 | Applicability::MaybeIncorrect, | |
2215 | ); | |
2216 | } else { | |
2217 | err.span_note( | |
2218 | ty.span, | |
2219 | format!("the function `{item_name}` is implemented on `{deref_ty}`"), | |
2220 | ); | |
2221 | } | |
2222 | return; | |
2223 | } | |
2224 | } | |
2225 | } | |
2226 | ||
e74abb32 XL |
2227 | /// Print out the type for use in value namespace. |
2228 | fn ty_to_value_string(&self, ty: Ty<'tcx>) -> String { | |
1b1a35ee | 2229 | match ty.kind() { |
487cf647 | 2230 | ty::Adt(def, substs) => self.tcx.def_path_str_with_substs(def.did(), substs), |
e74abb32 XL |
2231 | _ => self.ty_to_string(ty), |
2232 | } | |
2233 | } | |
2234 | ||
1b1a35ee XL |
2235 | fn suggest_await_before_method( |
2236 | &self, | |
5e7ed085 | 2237 | err: &mut Diagnostic, |
1b1a35ee XL |
2238 | item_name: Ident, |
2239 | ty: Ty<'tcx>, | |
2240 | call: &hir::Expr<'_>, | |
2241 | span: Span, | |
9c376795 | 2242 | return_type: Option<Ty<'tcx>>, |
1b1a35ee | 2243 | ) { |
064997fb | 2244 | let output_ty = match self.get_impl_future_output_ty(ty) { |
487cf647 | 2245 | Some(output_ty) => self.resolve_vars_if_possible(output_ty), |
29967ef6 XL |
2246 | _ => return, |
2247 | }; | |
9c376795 FG |
2248 | let method_exists = |
2249 | self.method_exists(item_name, output_ty, call.hir_id, true, return_type); | |
29967ef6 XL |
2250 | debug!("suggest_await_before_method: is_method_exist={}", method_exists); |
2251 | if method_exists { | |
2252 | err.span_suggestion_verbose( | |
2253 | span.shrink_to_lo(), | |
2254 | "consider `await`ing on the `Future` and calling the method on its `Output`", | |
923072b8 | 2255 | "await.", |
29967ef6 XL |
2256 | Applicability::MaybeIncorrect, |
2257 | ); | |
1b1a35ee XL |
2258 | } |
2259 | } | |
2260 | ||
04454e1e | 2261 | fn suggest_use_candidates(&self, err: &mut Diagnostic, msg: String, candidates: Vec<DefId>) { |
a2a8927a XL |
2262 | let parent_map = self.tcx.visible_parent_map(()); |
2263 | ||
2264 | // Separate out candidates that must be imported with a glob, because they are named `_` | |
2265 | // and cannot be referred with their identifier. | |
2266 | let (candidates, globs): (Vec<_>, Vec<_>) = candidates.into_iter().partition(|trait_did| { | |
2267 | if let Some(parent_did) = parent_map.get(trait_did) { | |
2268 | // If the item is re-exported as `_`, we should suggest a glob-import instead. | |
04454e1e | 2269 | if *parent_did != self.tcx.parent(*trait_did) |
a2a8927a XL |
2270 | && self |
2271 | .tcx | |
5099ac24 | 2272 | .module_children(*parent_did) |
a2a8927a XL |
2273 | .iter() |
2274 | .filter(|child| child.res.opt_def_id() == Some(*trait_did)) | |
2275 | .all(|child| child.ident.name == kw::Underscore) | |
2276 | { | |
2277 | return false; | |
2278 | } | |
2279 | } | |
2280 | ||
2281 | true | |
2282 | }); | |
2283 | ||
9ffffee4 | 2284 | let module_did = self.tcx.parent_module_from_def_id(self.body_id); |
04454e1e FG |
2285 | let (module, _, _) = self.tcx.hir().get_module(module_did); |
2286 | let span = module.spans.inject_use_span; | |
ff7c6d11 | 2287 | |
04454e1e FG |
2288 | let path_strings = candidates.iter().map(|trait_did| { |
2289 | format!("use {};\n", with_crate_prefix!(self.tcx.def_path_str(*trait_did)),) | |
2290 | }); | |
a2a8927a | 2291 | |
04454e1e FG |
2292 | let glob_path_strings = globs.iter().map(|trait_did| { |
2293 | let parent_did = parent_map.get(trait_did).unwrap(); | |
2294 | format!( | |
2295 | "use {}::*; // trait {}\n", | |
2296 | with_crate_prefix!(self.tcx.def_path_str(*parent_did)), | |
2297 | self.tcx.item_name(*trait_did), | |
2298 | ) | |
2299 | }); | |
a2a8927a | 2300 | |
04454e1e FG |
2301 | err.span_suggestions( |
2302 | span, | |
2303 | &msg, | |
2304 | path_strings.chain(glob_path_strings), | |
2305 | Applicability::MaybeIncorrect, | |
2306 | ); | |
3b2f2976 XL |
2307 | } |
2308 | ||
e74abb32 XL |
2309 | fn suggest_valid_traits( |
2310 | &self, | |
5e7ed085 | 2311 | err: &mut Diagnostic, |
e74abb32 XL |
2312 | valid_out_of_scope_traits: Vec<DefId>, |
2313 | ) -> bool { | |
3b2f2976 XL |
2314 | if !valid_out_of_scope_traits.is_empty() { |
2315 | let mut candidates = valid_out_of_scope_traits; | |
2316 | candidates.sort(); | |
2317 | candidates.dedup(); | |
3c0e092e XL |
2318 | |
2319 | // `TryFrom` and `FromIterator` have no methods | |
2320 | let edition_fix = candidates | |
2321 | .iter() | |
2322 | .find(|did| self.tcx.is_diagnostic_item(sym::TryInto, **did)) | |
2323 | .copied(); | |
2324 | ||
3b2f2976 | 2325 | err.help("items from traits can only be used if the trait is in scope"); |
e74abb32 XL |
2326 | let msg = format!( |
2327 | "the following {traits_are} implemented but not in scope; \ | |
2328 | perhaps add a `use` for {one_of_them}:", | |
dfeec247 XL |
2329 | traits_are = if candidates.len() == 1 { "trait is" } else { "traits are" }, |
2330 | one_of_them = if candidates.len() == 1 { "it" } else { "one of them" }, | |
e74abb32 | 2331 | ); |
3b2f2976 XL |
2332 | |
2333 | self.suggest_use_candidates(err, msg, candidates); | |
3c0e092e XL |
2334 | if let Some(did) = edition_fix { |
2335 | err.note(&format!( | |
2336 | "'{}' is included in the prelude starting in Edition 2021", | |
5e7ed085 | 2337 | with_crate_prefix!(self.tcx.def_path_str(did)) |
3c0e092e XL |
2338 | )); |
2339 | } | |
2340 | ||
3b2f2976 XL |
2341 | true |
2342 | } else { | |
2343 | false | |
54a0048b | 2344 | } |
85aaf69f SL |
2345 | } |
2346 | ||
cdc7bbd5 | 2347 | fn suggest_traits_to_import( |
416331ca | 2348 | &self, |
5e7ed085 | 2349 | err: &mut Diagnostic, |
416331ca XL |
2350 | span: Span, |
2351 | rcvr_ty: Ty<'tcx>, | |
f9f354fc | 2352 | item_name: Ident, |
064997fb | 2353 | inputs_len: Option<usize>, |
cdc7bbd5 | 2354 | source: SelfSource<'tcx>, |
416331ca | 2355 | valid_out_of_scope_traits: Vec<DefId>, |
3c0e092e XL |
2356 | unsatisfied_predicates: &[( |
2357 | ty::Predicate<'tcx>, | |
2358 | Option<ty::Predicate<'tcx>>, | |
2359 | Option<ObligationCause<'tcx>>, | |
2360 | )], | |
2b03887a | 2361 | static_candidates: &[CandidateSource], |
cdc7bbd5 | 2362 | unsatisfied_bounds: bool, |
9c376795 | 2363 | return_type: Option<Ty<'tcx>>, |
416331ca | 2364 | ) { |
cdc7bbd5 XL |
2365 | let mut alt_rcvr_sugg = false; |
2366 | if let (SelfSource::MethodCall(rcvr), false) = (source, unsatisfied_bounds) { | |
487cf647 FG |
2367 | debug!( |
2368 | "suggest_traits_to_import: span={:?}, item_name={:?}, rcvr_ty={:?}, rcvr={:?}", | |
2369 | span, item_name, rcvr_ty, rcvr | |
2370 | ); | |
cdc7bbd5 XL |
2371 | let skippable = [ |
2372 | self.tcx.lang_items().clone_trait(), | |
2373 | self.tcx.lang_items().deref_trait(), | |
2374 | self.tcx.lang_items().deref_mut_trait(), | |
2375 | self.tcx.lang_items().drop_trait(), | |
3c0e092e | 2376 | self.tcx.get_diagnostic_item(sym::AsRef), |
cdc7bbd5 XL |
2377 | ]; |
2378 | // Try alternative arbitrary self types that could fulfill this call. | |
2379 | // FIXME: probe for all types that *could* be arbitrary self-types, not | |
2380 | // just this list. | |
2381 | for (rcvr_ty, post) in &[ | |
2382 | (rcvr_ty, ""), | |
5099ac24 FG |
2383 | (self.tcx.mk_mut_ref(self.tcx.lifetimes.re_erased, rcvr_ty), "&mut "), |
2384 | (self.tcx.mk_imm_ref(self.tcx.lifetimes.re_erased, rcvr_ty), "&"), | |
cdc7bbd5 | 2385 | ] { |
9c376795 FG |
2386 | match self.lookup_probe_for_diagnostic( |
2387 | item_name, | |
2388 | *rcvr_ty, | |
2389 | rcvr, | |
2390 | ProbeScope::AllTraits, | |
2391 | return_type, | |
2392 | ) { | |
5e7ed085 FG |
2393 | Ok(pick) => { |
2394 | // If the method is defined for the receiver we have, it likely wasn't `use`d. | |
2395 | // We point at the method, but we just skip the rest of the check for arbitrary | |
2396 | // self types and rely on the suggestion to `use` the trait from | |
2397 | // `suggest_valid_traits`. | |
064997fb | 2398 | let did = Some(pick.item.container_id(self.tcx)); |
5e7ed085 FG |
2399 | let skip = skippable.contains(&did); |
2400 | if pick.autoderefs == 0 && !skip { | |
2401 | err.span_label( | |
2402 | pick.item.ident(self.tcx).span, | |
2403 | &format!("the method is available for `{}` here", rcvr_ty), | |
2404 | ); | |
2405 | } | |
2406 | break; | |
cdc7bbd5 | 2407 | } |
5e7ed085 FG |
2408 | Err(MethodError::Ambiguity(_)) => { |
2409 | // If the method is defined (but ambiguous) for the receiver we have, it is also | |
2410 | // likely we haven't `use`d it. It may be possible that if we `Box`/`Pin`/etc. | |
2411 | // the receiver, then it might disambiguate this method, but I think these | |
2412 | // suggestions are generally misleading (see #94218). | |
2413 | break; | |
2414 | } | |
487cf647 | 2415 | Err(_) => (), |
cdc7bbd5 | 2416 | } |
5e7ed085 | 2417 | |
cdc7bbd5 | 2418 | for (rcvr_ty, pre) in &[ |
5099ac24 FG |
2419 | (self.tcx.mk_lang_item(*rcvr_ty, LangItem::OwnedBox), "Box::new"), |
2420 | (self.tcx.mk_lang_item(*rcvr_ty, LangItem::Pin), "Pin::new"), | |
2421 | (self.tcx.mk_diagnostic_item(*rcvr_ty, sym::Arc), "Arc::new"), | |
2422 | (self.tcx.mk_diagnostic_item(*rcvr_ty, sym::Rc), "Rc::new"), | |
cdc7bbd5 | 2423 | ] { |
923072b8 | 2424 | if let Some(new_rcvr_t) = *rcvr_ty |
9c376795 | 2425 | && let Ok(pick) = self.lookup_probe_for_diagnostic( |
923072b8 FG |
2426 | item_name, |
2427 | new_rcvr_t, | |
2428 | rcvr, | |
2429 | ProbeScope::AllTraits, | |
9c376795 | 2430 | return_type, |
923072b8 FG |
2431 | ) |
2432 | { | |
5e7ed085 | 2433 | debug!("try_alt_rcvr: pick candidate {:?}", pick); |
064997fb | 2434 | let did = Some(pick.item.container_id(self.tcx)); |
5e7ed085 FG |
2435 | // We don't want to suggest a container type when the missing |
2436 | // method is `.clone()` or `.deref()` otherwise we'd suggest | |
2437 | // `Arc::new(foo).clone()`, which is far from what the user wants. | |
2438 | // Explicitly ignore the `Pin::as_ref()` method as `Pin` does not | |
2439 | // implement the `AsRef` trait. | |
2440 | let skip = skippable.contains(&did) | |
064997fb | 2441 | || (("Pin::new" == *pre) && (sym::as_ref == item_name.name)) |
9ffffee4 | 2442 | || inputs_len.map_or(false, |inputs_len| pick.item.kind == ty::AssocKind::Fn && self.tcx.fn_sig(pick.item.def_id).skip_binder().skip_binder().inputs().len() != inputs_len); |
5e7ed085 FG |
2443 | // Make sure the method is defined for the *actual* receiver: we don't |
2444 | // want to treat `Box<Self>` as a receiver if it only works because of | |
2445 | // an autoderef to `&self` | |
2446 | if pick.autoderefs == 0 && !skip { | |
2447 | err.span_label( | |
2448 | pick.item.ident(self.tcx).span, | |
2449 | &format!("the method is available for `{}` here", new_rcvr_t), | |
2450 | ); | |
2451 | err.multipart_suggestion( | |
2452 | "consider wrapping the receiver expression with the \ | |
2453 | appropriate type", | |
2454 | vec![ | |
2455 | (rcvr.span.shrink_to_lo(), format!("{}({}", pre, post)), | |
2456 | (rcvr.span.shrink_to_hi(), ")".to_string()), | |
2457 | ], | |
2458 | Applicability::MaybeIncorrect, | |
2459 | ); | |
2460 | // We don't care about the other suggestions. | |
2461 | alt_rcvr_sugg = true; | |
cdc7bbd5 XL |
2462 | } |
2463 | } | |
2464 | } | |
2465 | } | |
2466 | } | |
3b2f2976 | 2467 | if self.suggest_valid_traits(err, valid_out_of_scope_traits) { |
c30ab7b3 | 2468 | return; |
85aaf69f | 2469 | } |
85aaf69f | 2470 | |
0731742a | 2471 | let type_is_local = self.type_derefs_to_local(span, rcvr_ty, source); |
a7813a04 | 2472 | |
74b04a01 | 2473 | let mut arbitrary_rcvr = vec![]; |
0731742a | 2474 | // There are no traits implemented, so lets suggest some traits to |
a7813a04 XL |
2475 | // implement, by finding ones that have the item name, and are |
2476 | // legal to implement. | |
8bb4bdeb | 2477 | let mut candidates = all_traits(self.tcx) |
83c7162d | 2478 | .into_iter() |
3dfed10e XL |
2479 | // Don't issue suggestions for unstable traits since they're |
2480 | // unlikely to be implementable anyway | |
2481 | .filter(|info| match self.tcx.lookup_stability(info.def_id) { | |
2482 | Some(attr) => attr.level.is_stable(), | |
2483 | None => true, | |
2484 | }) | |
2b03887a FG |
2485 | .filter(|info| { |
2486 | // Static candidates are already implemented, and known not to work | |
2487 | // Do not suggest them again | |
2488 | static_candidates.iter().all(|sc| match *sc { | |
2489 | CandidateSource::Trait(def_id) => def_id != info.def_id, | |
2490 | CandidateSource::Impl(def_id) => { | |
2491 | self.tcx.trait_id_of_impl(def_id) != Some(info.def_id) | |
2492 | } | |
2493 | }) | |
2494 | }) | |
a7813a04 | 2495 | .filter(|info| { |
0731742a | 2496 | // We approximate the coherence rules to only suggest |
a7813a04 | 2497 | // traits that are legal to implement by requiring that |
0731742a | 2498 | // either the type or trait is local. Multi-dispatch means |
a7813a04 XL |
2499 | // this isn't perfect (that is, there are cases when |
2500 | // implementing a trait would be legal but is rejected | |
2501 | // here). | |
3c0e092e | 2502 | unsatisfied_predicates.iter().all(|(p, _, _)| { |
5869c6ff | 2503 | match p.kind().skip_binder() { |
3dfed10e XL |
2504 | // Hide traits if they are present in predicates as they can be fixed without |
2505 | // having to implement them. | |
487cf647 FG |
2506 | ty::PredicateKind::Clause(ty::Clause::Trait(t)) => { |
2507 | t.def_id() == info.def_id | |
2508 | } | |
2509 | ty::PredicateKind::Clause(ty::Clause::Projection(p)) => { | |
9c376795 | 2510 | p.projection_ty.def_id == info.def_id |
3dfed10e XL |
2511 | } |
2512 | _ => false, | |
2513 | } | |
74b04a01 | 2514 | }) && (type_is_local || info.def_id.is_local()) |
9c376795 | 2515 | && !self.tcx.trait_is_auto(info.def_id) |
dfeec247 | 2516 | && self |
5099ac24 | 2517 | .associated_value(info.def_id, item_name) |
2c00a5a8 | 2518 | .filter(|item| { |
ba9703b0 | 2519 | if let ty::AssocKind::Fn = item.kind { |
f9f354fc XL |
2520 | let id = item |
2521 | .def_id | |
2522 | .as_local() | |
3dfed10e | 2523 | .map(|def_id| self.tcx.hir().local_def_id_to_hir_id(def_id)); |
74b04a01 | 2524 | if let Some(hir::Node::TraitItem(hir::TraitItem { |
ba9703b0 | 2525 | kind: hir::TraitItemKind::Fn(fn_sig, method), |
74b04a01 XL |
2526 | .. |
2527 | })) = id.map(|id| self.tcx.hir().get(id)) | |
2528 | { | |
2529 | let self_first_arg = match method { | |
ba9703b0 | 2530 | hir::TraitFn::Required([ident, ..]) => { |
74b04a01 XL |
2531 | ident.name == kw::SelfLower |
2532 | } | |
ba9703b0 | 2533 | hir::TraitFn::Provided(body_id) => { |
f9f354fc XL |
2534 | self.tcx.hir().body(*body_id).params.first().map_or( |
2535 | false, | |
2536 | |param| { | |
2537 | matches!( | |
2538 | param.pat.kind, | |
2539 | hir::PatKind::Binding(_, _, ident, _) | |
2540 | if ident.name == kw::SelfLower | |
2541 | ) | |
2542 | }, | |
2543 | ) | |
74b04a01 XL |
2544 | } |
2545 | _ => false, | |
2546 | }; | |
2547 | ||
2548 | if !fn_sig.decl.implicit_self.has_implicit_self() | |
2549 | && self_first_arg | |
2550 | { | |
2551 | if let Some(ty) = fn_sig.decl.inputs.get(0) { | |
2552 | arbitrary_rcvr.push(ty.span); | |
2553 | } | |
2554 | return false; | |
2555 | } | |
2556 | } | |
2557 | } | |
2c00a5a8 | 2558 | // We only want to suggest public or local traits (#45781). |
064997fb | 2559 | item.visibility(self.tcx).is_public() || info.def_id.is_local() |
2c00a5a8 XL |
2560 | }) |
2561 | .is_some() | |
a7813a04 XL |
2562 | }) |
2563 | .collect::<Vec<_>>(); | |
74b04a01 XL |
2564 | for span in &arbitrary_rcvr { |
2565 | err.span_label( | |
2566 | *span, | |
2567 | "the method might not be found because of this arbitrary self type", | |
2568 | ); | |
2569 | } | |
cdc7bbd5 XL |
2570 | if alt_rcvr_sugg { |
2571 | return; | |
2572 | } | |
a7813a04 XL |
2573 | |
2574 | if !candidates.is_empty() { | |
0731742a | 2575 | // Sort from most relevant to least relevant. |
353b0b11 | 2576 | candidates.sort_by_key(|&info| cmp::Reverse(info)); |
a7813a04 XL |
2577 | candidates.dedup(); |
2578 | ||
1b1a35ee | 2579 | let param_type = match rcvr_ty.kind() { |
416331ca | 2580 | ty::Param(param) => Some(param), |
1b1a35ee | 2581 | ty::Ref(_, ty, _) => match ty.kind() { |
416331ca XL |
2582 | ty::Param(param) => Some(param), |
2583 | _ => None, | |
dfeec247 | 2584 | }, |
416331ca XL |
2585 | _ => None, |
2586 | }; | |
2587 | err.help(if param_type.is_some() { | |
2588 | "items from traits can only be used if the type parameter is bounded by the trait" | |
2589 | } else { | |
2590 | "items from traits can only be used if the trait is implemented and in scope" | |
2591 | }); | |
fc512014 | 2592 | let candidates_len = candidates.len(); |
dfeec247 XL |
2593 | let message = |action| { |
2594 | format!( | |
2595 | "the following {traits_define} an item `{name}`, perhaps you need to {action} \ | |
74b04a01 | 2596 | {one_of_them}:", |
dfeec247 | 2597 | traits_define = |
fc512014 | 2598 | if candidates_len == 1 { "trait defines" } else { "traits define" }, |
dfeec247 | 2599 | action = action, |
fc512014 | 2600 | one_of_them = if candidates_len == 1 { "it" } else { "one of them" }, |
dfeec247 XL |
2601 | name = item_name, |
2602 | ) | |
2603 | }; | |
416331ca | 2604 | // Obtain the span for `param` and use it for a structured suggestion. |
064997fb | 2605 | if let Some(param) = param_type { |
9ffffee4 | 2606 | let generics = self.tcx.generics_of(self.body_id.to_def_id()); |
f035d41b | 2607 | let type_param = generics.type_param(param, self.tcx); |
5099ac24 | 2608 | let hir = self.tcx.hir(); |
f035d41b | 2609 | if let Some(def_id) = type_param.def_id.as_local() { |
3dfed10e | 2610 | let id = hir.local_def_id_to_hir_id(def_id); |
f035d41b XL |
2611 | // Get the `hir::Param` to verify whether it already has any bounds. |
2612 | // We do this to avoid suggesting code that ends up as `T: FooBar`, | |
2613 | // instead we suggest `T: Foo + Bar` in that case. | |
2614 | match hir.get(id) { | |
c295e0f8 | 2615 | Node::GenericParam(param) => { |
04454e1e FG |
2616 | enum Introducer { |
2617 | Plus, | |
2618 | Colon, | |
2619 | Nothing, | |
2620 | } | |
2b03887a | 2621 | let ast_generics = hir.get_generics(id.owner.def_id).unwrap(); |
04454e1e FG |
2622 | let (sp, mut introducer) = if let Some(span) = |
2623 | ast_generics.bounds_span_for_suggestions(def_id) | |
2624 | { | |
2625 | (span, Introducer::Plus) | |
2626 | } else if let Some(colon_span) = param.colon_span { | |
2627 | (colon_span.shrink_to_hi(), Introducer::Nothing) | |
f035d41b | 2628 | } else { |
04454e1e | 2629 | (param.span.shrink_to_hi(), Introducer::Colon) |
f035d41b | 2630 | }; |
04454e1e FG |
2631 | if matches!( |
2632 | param.kind, | |
2633 | hir::GenericParamKind::Type { synthetic: true, .. }, | |
2634 | ) { | |
2635 | introducer = Introducer::Plus | |
2636 | } | |
2637 | let trait_def_ids: FxHashSet<DefId> = ast_generics | |
2638 | .bounds_for_param(def_id) | |
2639 | .flat_map(|bp| bp.bounds.iter()) | |
6a06907d | 2640 | .filter_map(|bound| bound.trait_ref()?.trait_def_id()) |
f035d41b XL |
2641 | .collect(); |
2642 | if !candidates.iter().any(|t| trait_def_ids.contains(&t.def_id)) { | |
e74abb32 XL |
2643 | err.span_suggestions( |
2644 | sp, | |
f035d41b XL |
2645 | &message(format!( |
2646 | "restrict type parameter `{}` with", | |
2647 | param.name.ident(), | |
2648 | )), | |
dfeec247 | 2649 | candidates.iter().map(|t| { |
f035d41b | 2650 | format!( |
04454e1e FG |
2651 | "{} {}", |
2652 | match introducer { | |
2653 | Introducer::Plus => " +", | |
2654 | Introducer::Colon => ":", | |
2655 | Introducer::Nothing => "", | |
2656 | }, | |
f035d41b | 2657 | self.tcx.def_path_str(t.def_id), |
f035d41b | 2658 | ) |
dfeec247 | 2659 | }), |
e74abb32 XL |
2660 | Applicability::MaybeIncorrect, |
2661 | ); | |
e1599b0c | 2662 | } |
fc512014 | 2663 | return; |
f035d41b XL |
2664 | } |
2665 | Node::Item(hir::Item { | |
2666 | kind: hir::ItemKind::Trait(.., bounds, _), | |
2667 | ident, | |
2668 | .. | |
2669 | }) => { | |
2670 | let (sp, sep, article) = if bounds.is_empty() { | |
2671 | (ident.span.shrink_to_hi(), ":", "a") | |
2672 | } else { | |
2673 | (bounds.last().unwrap().span().shrink_to_hi(), " +", "another") | |
2674 | }; | |
2675 | err.span_suggestions( | |
2676 | sp, | |
2677 | &message(format!("add {} supertrait for", article)), | |
2678 | candidates.iter().map(|t| { | |
2679 | format!("{} {}", sep, self.tcx.def_path_str(t.def_id),) | |
2680 | }), | |
2681 | Applicability::MaybeIncorrect, | |
2682 | ); | |
fc512014 | 2683 | return; |
416331ca | 2684 | } |
f035d41b | 2685 | _ => {} |
416331ca | 2686 | } |
f035d41b | 2687 | } |
416331ca XL |
2688 | } |
2689 | ||
fc512014 XL |
2690 | let (potential_candidates, explicitly_negative) = if param_type.is_some() { |
2691 | // FIXME: Even though negative bounds are not implemented, we could maybe handle | |
2692 | // cases where a positive bound implies a negative impl. | |
2693 | (candidates, Vec::new()) | |
5e7ed085 | 2694 | } else if let Some(simp_rcvr_ty) = |
353b0b11 | 2695 | simplify_type(self.tcx, rcvr_ty, TreatParams::ForLookup) |
a2a8927a | 2696 | { |
fc512014 XL |
2697 | let mut potential_candidates = Vec::new(); |
2698 | let mut explicitly_negative = Vec::new(); | |
2699 | for candidate in candidates { | |
2700 | // Check if there's a negative impl of `candidate` for `rcvr_ty` | |
2701 | if self | |
2702 | .tcx | |
2703 | .all_impls(candidate.def_id) | |
2704 | .filter(|imp_did| { | |
2705 | self.tcx.impl_polarity(*imp_did) == ty::ImplPolarity::Negative | |
2706 | }) | |
2707 | .any(|imp_did| { | |
9c376795 | 2708 | let imp = self.tcx.impl_trait_ref(imp_did).unwrap().subst_identity(); |
5099ac24 | 2709 | let imp_simp = |
353b0b11 | 2710 | simplify_type(self.tcx, imp.self_ty(), TreatParams::ForLookup); |
5869c6ff | 2711 | imp_simp.map_or(false, |s| s == simp_rcvr_ty) |
fc512014 XL |
2712 | }) |
2713 | { | |
2714 | explicitly_negative.push(candidate); | |
2715 | } else { | |
2716 | potential_candidates.push(candidate); | |
74b04a01 XL |
2717 | } |
2718 | } | |
fc512014 XL |
2719 | (potential_candidates, explicitly_negative) |
2720 | } else { | |
2721 | // We don't know enough about `recv_ty` to make proper suggestions. | |
2722 | (candidates, Vec::new()) | |
2723 | }; | |
2724 | ||
2725 | let action = if let Some(param) = param_type { | |
2726 | format!("restrict type parameter `{}` with", param) | |
2727 | } else { | |
2728 | // FIXME: it might only need to be imported into scope, not implemented. | |
2729 | "implement".to_string() | |
2730 | }; | |
2731 | match &potential_candidates[..] { | |
2732 | [] => {} | |
2733 | [trait_info] if trait_info.def_id.is_local() => { | |
fc512014 | 2734 | err.span_note( |
064997fb | 2735 | self.tcx.def_span(trait_info.def_id), |
fc512014 XL |
2736 | &format!( |
2737 | "`{}` defines an item `{}`, perhaps you need to {} it", | |
2738 | self.tcx.def_path_str(trait_info.def_id), | |
2739 | item_name, | |
2740 | action | |
2741 | ), | |
2742 | ); | |
2743 | } | |
2744 | trait_infos => { | |
74b04a01 | 2745 | let mut msg = message(action); |
fc512014 | 2746 | for (i, trait_info) in trait_infos.iter().enumerate() { |
74b04a01 XL |
2747 | msg.push_str(&format!( |
2748 | "\ncandidate #{}: `{}`", | |
2749 | i + 1, | |
2750 | self.tcx.def_path_str(trait_info.def_id), | |
2751 | )); | |
2752 | } | |
fc512014 XL |
2753 | err.note(&msg); |
2754 | } | |
2755 | } | |
2756 | match &explicitly_negative[..] { | |
2757 | [] => {} | |
2758 | [trait_info] => { | |
2759 | let msg = format!( | |
5099ac24 | 2760 | "the trait `{}` defines an item `{}`, but is explicitly unimplemented", |
fc512014 XL |
2761 | self.tcx.def_path_str(trait_info.def_id), |
2762 | item_name | |
2763 | ); | |
2764 | err.note(&msg); | |
2765 | } | |
2766 | trait_infos => { | |
2767 | let mut msg = format!( | |
5099ac24 | 2768 | "the following traits define an item `{}`, but are explicitly unimplemented:", |
fc512014 XL |
2769 | item_name |
2770 | ); | |
2771 | for trait_info in trait_infos { | |
2772 | msg.push_str(&format!("\n{}", self.tcx.def_path_str(trait_info.def_id))); | |
2773 | } | |
2774 | err.note(&msg); | |
416331ca | 2775 | } |
a7813a04 | 2776 | } |
85aaf69f | 2777 | } |
85aaf69f SL |
2778 | } |
2779 | ||
2b03887a FG |
2780 | /// issue #102320, for `unwrap_or` with closure as argument, suggest `unwrap_or_else` |
2781 | /// FIXME: currently not working for suggesting `map_or_else`, see #102408 | |
2782 | pub(crate) fn suggest_else_fn_with_closure( | |
2783 | &self, | |
2784 | err: &mut Diagnostic, | |
2785 | expr: &hir::Expr<'_>, | |
2786 | found: Ty<'tcx>, | |
2787 | expected: Ty<'tcx>, | |
2788 | ) -> bool { | |
9c376795 FG |
2789 | let Some((_def_id_or_name, output, _inputs)) = |
2790 | self.extract_callable_info(found) else { | |
2791 | return false; | |
2792 | }; | |
2b03887a FG |
2793 | |
2794 | if !self.can_coerce(output, expected) { | |
2795 | return false; | |
2796 | } | |
2797 | ||
9c376795 | 2798 | let parent = self.tcx.hir().parent_id(expr.hir_id); |
2b03887a FG |
2799 | if let Some(Node::Expr(call_expr)) = self.tcx.hir().find(parent) && |
2800 | let hir::ExprKind::MethodCall( | |
2801 | hir::PathSegment { ident: method_name, .. }, | |
2802 | self_expr, | |
2803 | args, | |
2804 | .., | |
2805 | ) = call_expr.kind && | |
2806 | let Some(self_ty) = self.typeck_results.borrow().expr_ty_opt(self_expr) { | |
2807 | let new_name = Ident { | |
2808 | name: Symbol::intern(&format!("{}_else", method_name.as_str())), | |
2809 | span: method_name.span, | |
2810 | }; | |
9c376795 | 2811 | let probe = self.lookup_probe_for_diagnostic( |
2b03887a FG |
2812 | new_name, |
2813 | self_ty, | |
2814 | self_expr, | |
2815 | ProbeScope::TraitsInScope, | |
9c376795 | 2816 | Some(expected), |
2b03887a FG |
2817 | ); |
2818 | ||
2819 | // check the method arguments number | |
2820 | if let Ok(pick) = probe && | |
2821 | let fn_sig = self.tcx.fn_sig(pick.item.def_id) && | |
9ffffee4 | 2822 | let fn_args = fn_sig.skip_binder().skip_binder().inputs() && |
2b03887a FG |
2823 | fn_args.len() == args.len() + 1 { |
2824 | err.span_suggestion_verbose( | |
2825 | method_name.span.shrink_to_hi(), | |
2826 | &format!("try calling `{}` instead", new_name.name.as_str()), | |
2827 | "_else", | |
2828 | Applicability::MaybeIncorrect, | |
2829 | ); | |
2830 | return true; | |
2831 | } | |
2832 | } | |
2833 | false | |
2834 | } | |
2835 | ||
a7813a04 XL |
2836 | /// Checks whether there is a local type somewhere in the chain of |
2837 | /// autoderefs of `rcvr_ty`. | |
cdc7bbd5 XL |
2838 | fn type_derefs_to_local( |
2839 | &self, | |
2840 | span: Span, | |
2841 | rcvr_ty: Ty<'tcx>, | |
2842 | source: SelfSource<'tcx>, | |
2843 | ) -> bool { | |
9fa01778 | 2844 | fn is_local(ty: Ty<'_>) -> bool { |
1b1a35ee | 2845 | match ty.kind() { |
5e7ed085 | 2846 | ty::Adt(def, _) => def.did().is_local(), |
b7449926 | 2847 | ty::Foreign(did) => did.is_local(), |
c295e0f8 | 2848 | ty::Dynamic(tr, ..) => tr.principal().map_or(false, |d| d.def_id().is_local()), |
b7449926 | 2849 | ty::Param(_) => true, |
a7813a04 | 2850 | |
0731742a XL |
2851 | // Everything else (primitive types, etc.) is effectively |
2852 | // non-local (there are "edge" cases, e.g., `(LocalType,)`, but | |
a7813a04 XL |
2853 | // the noise from these sort of types is usually just really |
2854 | // annoying, rather than any sort of help). | |
c30ab7b3 | 2855 | _ => false, |
a7813a04 | 2856 | } |
85aaf69f | 2857 | } |
85aaf69f | 2858 | |
a7813a04 XL |
2859 | // This occurs for UFCS desugaring of `T::method`, where there is no |
2860 | // receiver expression for the method call, and thus no autoderef. | |
0731742a | 2861 | if let SelfSource::QPath(_) = source { |
e74abb32 | 2862 | return is_local(self.resolve_vars_with_obligations(rcvr_ty)); |
c34b1796 | 2863 | } |
c34b1796 | 2864 | |
3157f602 | 2865 | self.autoderef(span, rcvr_ty).any(|(ty, _)| is_local(ty)) |
c34b1796 | 2866 | } |
85aaf69f SL |
2867 | } |
2868 | ||
cdc7bbd5 | 2869 | #[derive(Copy, Clone, Debug)] |
0731742a | 2870 | pub enum SelfSource<'a> { |
dfeec247 XL |
2871 | QPath(&'a hir::Ty<'a>), |
2872 | MethodCall(&'a hir::Expr<'a> /* rcvr */), | |
0731742a XL |
2873 | } |
2874 | ||
c34b1796 | 2875 | #[derive(Copy, Clone)] |
85aaf69f | 2876 | pub struct TraitInfo { |
e9174d1e | 2877 | pub def_id: DefId, |
85aaf69f SL |
2878 | } |
2879 | ||
85aaf69f SL |
2880 | impl PartialEq for TraitInfo { |
2881 | fn eq(&self, other: &TraitInfo) -> bool { | |
2882 | self.cmp(other) == Ordering::Equal | |
2883 | } | |
2884 | } | |
2885 | impl Eq for TraitInfo {} | |
2886 | impl PartialOrd for TraitInfo { | |
c30ab7b3 SL |
2887 | fn partial_cmp(&self, other: &TraitInfo) -> Option<Ordering> { |
2888 | Some(self.cmp(other)) | |
2889 | } | |
85aaf69f SL |
2890 | } |
2891 | impl Ord for TraitInfo { | |
2892 | fn cmp(&self, other: &TraitInfo) -> Ordering { | |
0731742a XL |
2893 | // Local crates are more important than remote ones (local: |
2894 | // `cnum == 0`), and otherwise we throw in the defid for totality. | |
85aaf69f | 2895 | |
b039eaaf SL |
2896 | let lhs = (other.def_id.krate, other.def_id); |
2897 | let rhs = (self.def_id.krate, self.def_id); | |
85aaf69f SL |
2898 | lhs.cmp(&rhs) |
2899 | } | |
2900 | } | |
2901 | ||
a2a8927a XL |
2902 | /// Retrieves all traits in this crate and any dependent crates, |
2903 | /// and wraps them into `TraitInfo` for custom sorting. | |
416331ca | 2904 | pub fn all_traits(tcx: TyCtxt<'_>) -> Vec<TraitInfo> { |
a2a8927a | 2905 | tcx.all_traits().map(|def_id| TraitInfo { def_id }).collect() |
85aaf69f | 2906 | } |
ff7c6d11 | 2907 | |
a2a8927a | 2908 | fn print_disambiguation_help<'tcx>( |
f9f354fc | 2909 | item_name: Ident, |
f2b60f7d | 2910 | args: Option<(&'tcx hir::Expr<'tcx>, &'tcx [hir::Expr<'tcx>])>, |
5e7ed085 | 2911 | err: &mut Diagnostic, |
dfeec247 XL |
2912 | trait_name: String, |
2913 | rcvr_ty: Ty<'_>, | |
2914 | kind: ty::AssocKind, | |
9ffffee4 | 2915 | def_kind_descr: &'static str, |
dfeec247 XL |
2916 | span: Span, |
2917 | candidate: Option<usize>, | |
2918 | source_map: &source_map::SourceMap, | |
c295e0f8 | 2919 | fn_has_self_parameter: bool, |
dfeec247 XL |
2920 | ) { |
2921 | let mut applicability = Applicability::MachineApplicable; | |
f2b60f7d | 2922 | let (span, sugg) = if let (ty::AssocKind::Fn, Some((receiver, args))) = (kind, args) { |
94222f64 | 2923 | let args = format!( |
dfeec247 | 2924 | "({}{})", |
487cf647 | 2925 | rcvr_ty.ref_mutability().map_or("", |mutbl| mutbl.ref_prefix_str()), |
f2b60f7d FG |
2926 | std::iter::once(receiver) |
2927 | .chain(args.iter()) | |
dfeec247 XL |
2928 | .map(|arg| source_map.span_to_snippet(arg.span).unwrap_or_else(|_| { |
2929 | applicability = Applicability::HasPlaceholders; | |
2930 | "_".to_owned() | |
2931 | })) | |
2932 | .collect::<Vec<_>>() | |
2933 | .join(", "), | |
94222f64 | 2934 | ); |
c295e0f8 XL |
2935 | let trait_name = if !fn_has_self_parameter { |
2936 | format!("<{} as {}>", rcvr_ty, trait_name) | |
2937 | } else { | |
2938 | trait_name | |
2939 | }; | |
94222f64 | 2940 | (span, format!("{}::{}{}", trait_name, item_name, args)) |
dfeec247 | 2941 | } else { |
c295e0f8 | 2942 | (span.with_hi(item_name.span.lo()), format!("<{} as {}>::", rcvr_ty, trait_name)) |
dfeec247 | 2943 | }; |
94222f64 | 2944 | err.span_suggestion_verbose( |
dfeec247 XL |
2945 | span, |
2946 | &format!( | |
2947 | "disambiguate the {} for {}", | |
9ffffee4 | 2948 | def_kind_descr, |
dfeec247 XL |
2949 | if let Some(candidate) = candidate { |
2950 | format!("candidate #{}", candidate) | |
2951 | } else { | |
2952 | "the candidate".to_string() | |
2953 | }, | |
2954 | ), | |
2955 | sugg, | |
2956 | applicability, | |
2957 | ); | |
2958 | } |