]>
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 | ||
9fa01778 | 4 | use crate::check::FnCtxt; |
74b04a01 XL |
5 | use rustc_ast::util::lev_distance; |
6 | use rustc_data_structures::fx::{FxHashMap, FxHashSet}; | |
dfeec247 XL |
7 | use rustc_errors::{pluralize, struct_span_err, Applicability, DiagnosticBuilder}; |
8 | use rustc_hir as hir; | |
74b04a01 | 9 | use rustc_hir::def::{DefKind, Namespace, Res}; |
dfeec247 XL |
10 | use rustc_hir::def_id::{DefId, CRATE_DEF_INDEX, LOCAL_CRATE}; |
11 | use rustc_hir::intravisit; | |
ba9703b0 | 12 | use rustc_hir::lang_items::FnOnceTraitLangItem; |
dfeec247 | 13 | use rustc_hir::{ExprKind, Node, QPath}; |
74b04a01 | 14 | use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; |
ba9703b0 XL |
15 | use rustc_middle::hir::map as hir_map; |
16 | use rustc_middle::ty::print::with_crate_prefix; | |
17 | use rustc_middle::ty::{ | |
18 | self, ToPolyTraitRef, ToPredicate, Ty, TyCtxt, TypeFoldable, WithConstness, | |
19 | }; | |
f9f354fc | 20 | use rustc_span::symbol::{kw, Ident}; |
dfeec247 | 21 | use rustc_span::{source_map, FileName, Span}; |
ba9703b0 XL |
22 | use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt; |
23 | use rustc_trait_selection::traits::Obligation; | |
85aaf69f | 24 | |
85aaf69f SL |
25 | use std::cmp::Ordering; |
26 | ||
d9579d0f | 27 | use super::probe::Mode; |
dfeec247 | 28 | use super::{CandidateSource, MethodError, NoMatchData}; |
85aaf69f | 29 | |
dc9dc135 | 30 | impl<'a, 'tcx> FnCtxt<'a, 'tcx> { |
48663c56 | 31 | fn is_fn_ty(&self, ty: Ty<'tcx>, span: Span) -> bool { |
a7813a04 | 32 | let tcx = self.tcx; |
e74abb32 | 33 | match ty.kind { |
0731742a XL |
34 | // Not all of these (e.g., unsafe fns) implement `FnOnce`, |
35 | // so we look for these beforehand. | |
dfeec247 | 36 | ty::Closure(..) | ty::FnDef(..) | ty::FnPtr(_) => true, |
0731742a | 37 | // If it's not a simple function, look for things which implement `FnOnce`. |
a7813a04 | 38 | _ => { |
ea8adc8c | 39 | let fn_once = match tcx.lang_items().require(FnOnceTraitLangItem) { |
3157f602 | 40 | Ok(fn_once) => fn_once, |
c30ab7b3 | 41 | Err(..) => return false, |
3157f602 | 42 | }; |
a7813a04 | 43 | |
c30ab7b3 SL |
44 | self.autoderef(span, ty).any(|(ty, _)| { |
45 | self.probe(|_| { | |
dfeec247 XL |
46 | let fn_once_substs = tcx.mk_substs_trait( |
47 | ty, | |
48 | &[self | |
49 | .next_ty_var(TypeVariableOrigin { | |
50 | kind: TypeVariableOriginKind::MiscVariable, | |
51 | span, | |
52 | }) | |
53 | .into()], | |
54 | ); | |
c30ab7b3 SL |
55 | let trait_ref = ty::TraitRef::new(fn_once, fn_once_substs); |
56 | let poly_trait_ref = trait_ref.to_poly_trait_ref(); | |
dfeec247 XL |
57 | let obligation = Obligation::misc( |
58 | span, | |
59 | self.body_id, | |
60 | self.param_env, | |
f9f354fc | 61 | poly_trait_ref.without_const().to_predicate(tcx), |
dfeec247 | 62 | ); |
83c7162d | 63 | self.predicate_may_hold(&obligation) |
c30ab7b3 SL |
64 | }) |
65 | }) | |
54a0048b SL |
66 | } |
67 | } | |
68 | } | |
3157f602 | 69 | |
532ac7d7 XL |
70 | pub fn report_method_error<'b>( |
71 | &self, | |
72 | span: Span, | |
73 | rcvr_ty: Ty<'tcx>, | |
f9f354fc | 74 | item_name: Ident, |
532ac7d7 XL |
75 | source: SelfSource<'b>, |
76 | error: MethodError<'tcx>, | |
dfeec247 | 77 | args: Option<&'tcx [hir::Expr<'tcx>]>, |
e1599b0c | 78 | ) -> Option<DiagnosticBuilder<'_>> { |
532ac7d7 XL |
79 | let orig_span = span; |
80 | let mut span = span; | |
0731742a | 81 | // Avoid suggestions when we don't know what's going on. |
a7813a04 | 82 | if rcvr_ty.references_error() { |
e1599b0c | 83 | return None; |
a7813a04 | 84 | } |
85aaf69f | 85 | |
dfeec247 XL |
86 | let report_candidates = |span: Span, |
87 | err: &mut DiagnosticBuilder<'_>, | |
88 | mut sources: Vec<CandidateSource>, | |
89 | sugg_span: Span| { | |
a7813a04 XL |
90 | sources.sort(); |
91 | sources.dedup(); | |
92 | // Dynamic limit to avoid hiding just one candidate, which is silly. | |
93 | let limit = if sources.len() == 5 { 5 } else { 4 }; | |
94 | ||
95 | for (idx, source) in sources.iter().take(limit).enumerate() { | |
96 | match *source { | |
97 | CandidateSource::ImplSource(impl_did) => { | |
98 | // Provide the best span we can. Use the item, if local to crate, else | |
99 | // the impl, if local to crate (item may be defaulted), else nothing. | |
dfeec247 | 100 | let item = match self |
74b04a01 | 101 | .associated_item(impl_did, item_name, Namespace::ValueNS) |
dfeec247 XL |
102 | .or_else(|| { |
103 | let impl_trait_ref = self.tcx.impl_trait_ref(impl_did)?; | |
104 | self.associated_item( | |
105 | impl_trait_ref.def_id, | |
106 | item_name, | |
74b04a01 | 107 | Namespace::ValueNS, |
dfeec247 XL |
108 | ) |
109 | }) { | |
48663c56 XL |
110 | Some(item) => item, |
111 | None => continue, | |
112 | }; | |
dfeec247 XL |
113 | let note_span = self |
114 | .tcx | |
115 | .hir() | |
116 | .span_if_local(item.def_id) | |
117 | .or_else(|| self.tcx.hir().span_if_local(impl_did)); | |
a7813a04 | 118 | |
ba9703b0 | 119 | let impl_ty = self.tcx.at(span).type_of(impl_did); |
a7813a04 XL |
120 | |
121 | let insertion = match self.tcx.impl_trait_ref(impl_did) { | |
8faf50e0 | 122 | None => String::new(), |
dfeec247 XL |
123 | Some(trait_ref) => format!( |
124 | " of the trait `{}`", | |
125 | self.tcx.def_path_str(trait_ref.def_id) | |
126 | ), | |
a7813a04 XL |
127 | }; |
128 | ||
dfeec247 XL |
129 | let (note_str, idx) = if sources.len() > 1 { |
130 | ( | |
131 | format!( | |
132 | "candidate #{} is defined in an impl{} for the type `{}`", | |
94b46f34 XL |
133 | idx + 1, |
134 | insertion, | |
dfeec247 XL |
135 | impl_ty, |
136 | ), | |
137 | Some(idx + 1), | |
138 | ) | |
94b46f34 | 139 | } else { |
dfeec247 XL |
140 | ( |
141 | format!( | |
142 | "the candidate is defined in an impl{} for the type `{}`", | |
143 | insertion, impl_ty, | |
144 | ), | |
145 | None, | |
146 | ) | |
94b46f34 | 147 | }; |
a7813a04 XL |
148 | if let Some(note_span) = note_span { |
149 | // We have a span pointing to the method. Show note with snippet. | |
dfeec247 | 150 | err.span_note( |
ba9703b0 | 151 | self.tcx.sess.source_map().guess_head_span(note_span), |
dfeec247 XL |
152 | ¬e_str, |
153 | ); | |
a7813a04 XL |
154 | } else { |
155 | err.note(¬e_str); | |
156 | } | |
416331ca | 157 | if let Some(trait_ref) = self.tcx.impl_trait_ref(impl_did) { |
dfeec247 XL |
158 | let path = self.tcx.def_path_str(trait_ref.def_id); |
159 | ||
160 | let ty = match item.kind { | |
f035d41b | 161 | ty::AssocKind::Const | ty::AssocKind::Type => rcvr_ty, |
ba9703b0 | 162 | ty::AssocKind::Fn => self |
dfeec247 XL |
163 | .tcx |
164 | .fn_sig(item.def_id) | |
165 | .inputs() | |
166 | .skip_binder() | |
167 | .get(0) | |
168 | .filter(|ty| ty.is_region_ptr() && !rcvr_ty.is_region_ptr()) | |
74b04a01 | 169 | .copied() |
dfeec247 XL |
170 | .unwrap_or(rcvr_ty), |
171 | }; | |
172 | print_disambiguation_help( | |
173 | item_name, | |
174 | args, | |
175 | err, | |
176 | path, | |
177 | ty, | |
178 | item.kind, | |
ba9703b0 | 179 | item.def_id, |
dfeec247 XL |
180 | sugg_span, |
181 | idx, | |
182 | self.tcx.sess.source_map(), | |
183 | ); | |
416331ca | 184 | } |
a7813a04 XL |
185 | } |
186 | CandidateSource::TraitSource(trait_did) => { | |
dfeec247 | 187 | let item = |
74b04a01 | 188 | match self.associated_item(trait_did, item_name, Namespace::ValueNS) { |
dfeec247 XL |
189 | Some(item) => item, |
190 | None => continue, | |
191 | }; | |
ba9703b0 XL |
192 | let item_span = self |
193 | .tcx | |
194 | .sess | |
195 | .source_map() | |
196 | .guess_head_span(self.tcx.def_span(item.def_id)); | |
dfeec247 XL |
197 | let idx = if sources.len() > 1 { |
198 | let msg = &format!( | |
199 | "candidate #{} is defined in the trait `{}`", | |
200 | idx + 1, | |
201 | self.tcx.def_path_str(trait_did) | |
202 | ); | |
203 | err.span_note(item_span, msg); | |
204 | Some(idx + 1) | |
94b46f34 | 205 | } else { |
dfeec247 XL |
206 | let msg = &format!( |
207 | "the candidate is defined in the trait `{}`", | |
208 | self.tcx.def_path_str(trait_did) | |
209 | ); | |
210 | err.span_note(item_span, msg); | |
211 | None | |
212 | }; | |
213 | let path = self.tcx.def_path_str(trait_did); | |
214 | print_disambiguation_help( | |
215 | item_name, | |
216 | args, | |
217 | err, | |
218 | path, | |
219 | rcvr_ty, | |
220 | item.kind, | |
ba9703b0 | 221 | item.def_id, |
dfeec247 XL |
222 | sugg_span, |
223 | idx, | |
224 | self.tcx.sess.source_map(), | |
225 | ); | |
9cc50fc6 | 226 | } |
54a0048b SL |
227 | } |
228 | } | |
a7813a04 XL |
229 | if sources.len() > limit { |
230 | err.note(&format!("and {} others", sources.len() - limit)); | |
231 | } | |
232 | }; | |
62682a34 | 233 | |
dfeec247 XL |
234 | let sugg_span = if let SelfSource::MethodCall(expr) = source { |
235 | // Given `foo.bar(baz)`, `expr` is `bar`, but we want to point to the whole thing. | |
236 | self.tcx.hir().expect_expr(self.tcx.hir().get_parent_node(expr.hir_id)).span | |
237 | } else { | |
238 | span | |
239 | }; | |
240 | ||
a7813a04 | 241 | match error { |
ff7c6d11 XL |
242 | MethodError::NoMatch(NoMatchData { |
243 | static_candidates: static_sources, | |
244 | unsatisfied_predicates, | |
245 | out_of_scope_traits, | |
246 | lev_candidate, | |
247 | mode, | |
ff7c6d11 | 248 | }) => { |
a7813a04 XL |
249 | let tcx = self.tcx; |
250 | ||
dc9dc135 | 251 | let actual = self.resolve_vars_if_possible(&rcvr_ty); |
0731742a | 252 | let ty_str = self.ty_to_string(actual); |
ff7c6d11 | 253 | let is_method = mode == Mode::MethodCall; |
94b46f34 | 254 | let item_kind = if is_method { |
ff7c6d11 XL |
255 | "method" |
256 | } else if actual.is_enum() { | |
48663c56 | 257 | "variant or associated item" |
ff7c6d11 XL |
258 | } else { |
259 | match (item_name.as_str().chars().next(), actual.is_fresh_ty()) { | |
dfeec247 | 260 | (Some(name), false) if name.is_lowercase() => "function or associated item", |
ff7c6d11 | 261 | (Some(_), false) => "associated item", |
dfeec247 | 262 | (Some(_), true) | (None, false) => "variant or associated item", |
ff7c6d11 XL |
263 | (None, true) => "variant", |
264 | } | |
265 | }; | |
7cac9316 | 266 | let mut err = if !actual.references_error() { |
2c00a5a8 | 267 | // Suggest clamping down the type if the method that is being attempted to |
e74abb32 | 268 | // be used exists at all, and the type is an ambiguous numeric type |
2c00a5a8 | 269 | // ({integer}/{float}). |
dfeec247 | 270 | let mut candidates = all_traits(self.tcx).into_iter().filter_map(|info| { |
74b04a01 | 271 | self.associated_item(info.def_id, item_name, Namespace::ValueNS) |
dfeec247 | 272 | }); |
ba9703b0 XL |
273 | // There are methods that are defined on the primitive types and won't be |
274 | // found when exploring `all_traits`, but we also need them to be acurate on | |
275 | // our suggestions (#47759). | |
276 | let fund_assoc = |opt_def_id: Option<DefId>| { | |
277 | opt_def_id | |
278 | .and_then(|id| self.associated_item(id, item_name, Namespace::ValueNS)) | |
279 | .is_some() | |
280 | }; | |
281 | let lang_items = tcx.lang_items(); | |
282 | let found_candidate = candidates.next().is_some() | |
283 | || fund_assoc(lang_items.i8_impl()) | |
284 | || fund_assoc(lang_items.i16_impl()) | |
285 | || fund_assoc(lang_items.i32_impl()) | |
286 | || fund_assoc(lang_items.i64_impl()) | |
287 | || fund_assoc(lang_items.i128_impl()) | |
288 | || fund_assoc(lang_items.u8_impl()) | |
289 | || fund_assoc(lang_items.u16_impl()) | |
290 | || fund_assoc(lang_items.u32_impl()) | |
291 | || fund_assoc(lang_items.u64_impl()) | |
292 | || fund_assoc(lang_items.u128_impl()) | |
293 | || fund_assoc(lang_items.f32_impl()) | |
294 | || fund_assoc(lang_items.f32_runtime_impl()) | |
295 | || fund_assoc(lang_items.f64_impl()) | |
296 | || fund_assoc(lang_items.f64_runtime_impl()); | |
297 | if let (true, false, SelfSource::MethodCall(expr), true) = ( | |
dfeec247 XL |
298 | actual.is_numeric(), |
299 | actual.has_concrete_skeleton(), | |
300 | source, | |
ba9703b0 | 301 | found_candidate, |
dfeec247 | 302 | ) { |
2c00a5a8 XL |
303 | let mut err = struct_span_err!( |
304 | tcx.sess, | |
305 | span, | |
306 | E0689, | |
307 | "can't call {} `{}` on ambiguous numeric type `{}`", | |
94b46f34 | 308 | item_kind, |
2c00a5a8 | 309 | item_name, |
0731742a | 310 | ty_str |
2c00a5a8 | 311 | ); |
dfeec247 | 312 | let concrete_type = if actual.is_integral() { "i32" } else { "f32" }; |
e74abb32 | 313 | match expr.kind { |
0731742a XL |
314 | ExprKind::Lit(ref lit) => { |
315 | // numeric literal | |
dfeec247 XL |
316 | let snippet = tcx |
317 | .sess | |
318 | .source_map() | |
319 | .span_to_snippet(lit.span) | |
0bf4aa26 | 320 | .unwrap_or_else(|_| "<numeric literal>".to_owned()); |
2c00a5a8 | 321 | |
9fa01778 XL |
322 | err.span_suggestion( |
323 | lit.span, | |
dfeec247 XL |
324 | &format!( |
325 | "you must specify a concrete type for \ | |
326 | this numeric value, like `{}`", | |
327 | concrete_type | |
328 | ), | |
9fa01778 XL |
329 | format!("{}_{}", snippet, concrete_type), |
330 | Applicability::MaybeIncorrect, | |
0bf4aa26 | 331 | ); |
2c00a5a8 | 332 | } |
0731742a XL |
333 | ExprKind::Path(ref qpath) => { |
334 | // local binding | |
335 | if let &QPath::Resolved(_, ref path) = &qpath { | |
48663c56 | 336 | if let hir::def::Res::Local(hir_id) = path.res { |
dc9dc135 | 337 | let span = tcx.hir().span(hir_id); |
48663c56 | 338 | let snippet = tcx.sess.source_map().span_to_snippet(span); |
b7449926 | 339 | let filename = tcx.sess.source_map().span_to_filename(span); |
8faf50e0 | 340 | |
dfeec247 XL |
341 | let parent_node = self |
342 | .tcx | |
343 | .hir() | |
344 | .get(self.tcx.hir().get_parent_node(hir_id)); | |
8faf50e0 XL |
345 | let msg = format!( |
346 | "you must specify a type for this binding, like `{}`", | |
347 | concrete_type, | |
348 | ); | |
349 | ||
48663c56 | 350 | match (filename, parent_node, snippet) { |
dfeec247 XL |
351 | ( |
352 | FileName::Real(_), | |
353 | Node::Local(hir::Local { | |
354 | source: hir::LocalSource::Normal, | |
355 | ty, | |
356 | .. | |
357 | }), | |
358 | Ok(ref snippet), | |
359 | ) => { | |
9fa01778 | 360 | err.span_suggestion( |
8faf50e0 XL |
361 | // account for `let x: _ = 42;` |
362 | // ^^^^ | |
dfeec247 XL |
363 | span.to(ty |
364 | .as_ref() | |
365 | .map(|ty| ty.span) | |
8faf50e0 XL |
366 | .unwrap_or(span)), |
367 | &msg, | |
368 | format!("{}: {}", snippet, concrete_type), | |
0bf4aa26 | 369 | Applicability::MaybeIncorrect, |
8faf50e0 XL |
370 | ); |
371 | } | |
372 | _ => { | |
373 | err.span_label(span, msg); | |
374 | } | |
375 | } | |
2c00a5a8 XL |
376 | } |
377 | } | |
378 | } | |
379 | _ => {} | |
380 | } | |
381 | err.emit(); | |
e1599b0c | 382 | return None; |
2c00a5a8 | 383 | } else { |
532ac7d7 | 384 | span = item_name.span; |
0531ce1d | 385 | let mut err = struct_span_err!( |
2c00a5a8 | 386 | tcx.sess, |
532ac7d7 | 387 | span, |
2c00a5a8 | 388 | E0599, |
dfeec247 | 389 | "no {} named `{}` found for {} `{}` in the current scope", |
94b46f34 | 390 | item_kind, |
2c00a5a8 | 391 | item_name, |
dfeec247 XL |
392 | actual.prefix_string(), |
393 | ty_str, | |
0531ce1d | 394 | ); |
dfeec247 XL |
395 | if let Some(span) = |
396 | tcx.sess.confused_type_with_std_module.borrow().get(&span) | |
48663c56 XL |
397 | { |
398 | if let Ok(snippet) = tcx.sess.source_map().span_to_snippet(*span) { | |
399 | err.span_suggestion( | |
400 | *span, | |
401 | "you are looking for the module in `std`, \ | |
402 | not the primitive type", | |
403 | format!("std::{}", snippet), | |
404 | Applicability::MachineApplicable, | |
405 | ); | |
406 | } | |
0531ce1d | 407 | } |
e74abb32 | 408 | if let ty::RawPtr(_) = &actual.kind { |
dfeec247 XL |
409 | err.note( |
410 | "try using `<*const T>::as_ref()` to get a reference to the \ | |
dc9dc135 | 411 | type behind the pointer: https://doc.rust-lang.org/std/\ |
dfeec247 XL |
412 | primitive.pointer.html#method.as_ref", |
413 | ); | |
414 | err.note( | |
415 | "using `<*const T>::as_ref()` on a pointer \ | |
416331ca | 416 | which is unaligned or points to invalid \ |
dfeec247 XL |
417 | or uninitialized memory is undefined behavior", |
418 | ); | |
dc9dc135 | 419 | } |
0531ce1d | 420 | err |
2c00a5a8 | 421 | } |
7cac9316 | 422 | } else { |
ff7c6d11 | 423 | tcx.sess.diagnostic().struct_dummy() |
7cac9316 | 424 | }; |
5bcae85e | 425 | |
94b46f34 | 426 | if let Some(def) = actual.ty_adt_def() { |
0731742a | 427 | if let Some(full_sp) = tcx.hir().span_if_local(def.did) { |
ba9703b0 | 428 | let def_sp = tcx.sess.source_map().guess_head_span(full_sp); |
dfeec247 XL |
429 | err.span_label( |
430 | def_sp, | |
431 | format!( | |
432 | "{} `{}` not found {}", | |
433 | item_kind, | |
434 | item_name, | |
435 | if def.is_enum() && !is_method { "here" } else { "for this" } | |
436 | ), | |
437 | ); | |
ff7c6d11 XL |
438 | } |
439 | } | |
440 | ||
5bcae85e | 441 | // If the method name is the name of a field with a function or closure type, |
0731742a XL |
442 | // give a helping note that it has to be called as `(x.f)(...)`. |
443 | if let SelfSource::MethodCall(expr) = source { | |
dfeec247 XL |
444 | let field_receiver = |
445 | self.autoderef(span, rcvr_ty).find_map(|(ty, _)| match ty.kind { | |
532ac7d7 | 446 | ty::Adt(def, substs) if !def.is_enum() => { |
83c7162d | 447 | let variant = &def.non_enum_variant(); |
532ac7d7 | 448 | self.tcx.find_field_index(item_name, variant).map(|index| { |
83c7162d | 449 | let field = &variant.fields[index]; |
9e0c209e | 450 | let field_ty = field.ty(tcx, substs); |
532ac7d7 XL |
451 | (field, field_ty) |
452 | }) | |
453 | } | |
454 | _ => None, | |
455 | }); | |
456 | ||
457 | if let Some((field, field_ty)) = field_receiver { | |
ba9703b0 | 458 | let scope = self.tcx.parent_module(self.body_id).to_def_id(); |
532ac7d7 XL |
459 | let is_accessible = field.vis.is_accessible_from(scope, self.tcx); |
460 | ||
461 | if is_accessible { | |
462 | if self.is_fn_ty(&field_ty, span) { | |
463 | let expr_span = expr.span.to(item_name.span); | |
464 | err.multipart_suggestion( | |
465 | &format!( | |
466 | "to call the function stored in `{}`, \ | |
467 | surround the field access with parentheses", | |
468 | item_name, | |
469 | ), | |
470 | vec![ | |
471 | (expr_span.shrink_to_lo(), '('.to_string()), | |
472 | (expr_span.shrink_to_hi(), ')'.to_string()), | |
473 | ], | |
474 | Applicability::MachineApplicable, | |
475 | ); | |
476 | } else { | |
dfeec247 XL |
477 | let call_expr = self |
478 | .tcx | |
479 | .hir() | |
480 | .expect_expr(self.tcx.hir().get_parent_node(expr.hir_id)); | |
532ac7d7 | 481 | |
48663c56 XL |
482 | if let Some(span) = call_expr.span.trim_start(item_name.span) { |
483 | err.span_suggestion( | |
484 | span, | |
485 | "remove the arguments", | |
486 | String::new(), | |
487 | Applicability::MaybeIncorrect, | |
488 | ); | |
489 | } | |
5bcae85e | 490 | } |
a7813a04 | 491 | } |
532ac7d7 | 492 | |
dfeec247 | 493 | let field_kind = if is_accessible { "field" } else { "private field" }; |
532ac7d7 | 494 | err.span_label(item_name.span, format!("{}, not a method", field_kind)); |
e1599b0c XL |
495 | } else if lev_candidate.is_none() && static_sources.is_empty() { |
496 | err.span_label(span, format!("{} not found in `{}`", item_kind, ty_str)); | |
497 | self.tcx.sess.trait_methods_not_found.borrow_mut().insert(orig_span); | |
54a0048b | 498 | } |
ff7c6d11 | 499 | } else { |
0731742a | 500 | err.span_label(span, format!("{} not found in `{}`", item_kind, ty_str)); |
532ac7d7 | 501 | self.tcx.sess.trait_methods_not_found.borrow_mut().insert(orig_span); |
54a0048b | 502 | } |
92a42be0 | 503 | |
a7813a04 XL |
504 | if self.is_fn_ty(&rcvr_ty, span) { |
505 | macro_rules! report_function { | |
506 | ($span:expr, $name:expr) => { | |
dfeec247 XL |
507 | err.note(&format!( |
508 | "`{}` is a function, perhaps you wish to call it", | |
509 | $name | |
510 | )); | |
511 | }; | |
54a0048b | 512 | } |
a7813a04 | 513 | |
0731742a | 514 | if let SelfSource::MethodCall(expr) = source { |
b7449926 | 515 | if let Ok(expr_string) = tcx.sess.source_map().span_to_snippet(expr.span) { |
a7813a04 | 516 | report_function!(expr.span, expr_string); |
dfeec247 | 517 | } else if let ExprKind::Path(QPath::Resolved(_, ref path)) = expr.kind { |
a7813a04 | 518 | if let Some(segment) = path.segments.last() { |
8faf50e0 | 519 | report_function!(expr.span, segment.ident); |
a7813a04 | 520 | } |
62682a34 SL |
521 | } |
522 | } | |
c34b1796 | 523 | } |
85aaf69f | 524 | |
a7813a04 | 525 | if !static_sources.is_empty() { |
dfeec247 XL |
526 | err.note( |
527 | "found the following associated functions; to be used as methods, \ | |
74b04a01 | 528 | functions must have a `self` parameter", |
dfeec247 | 529 | ); |
94b46f34 XL |
530 | err.span_label(span, "this is an associated function, not a method"); |
531 | } | |
532 | if static_sources.len() == 1 { | |
dfeec247 XL |
533 | let ty_str = if let Some(CandidateSource::ImplSource(impl_did)) = |
534 | static_sources.get(0) | |
535 | { | |
e74abb32 XL |
536 | // When the "method" is resolved through dereferencing, we really want the |
537 | // original type that has the associated function for accurate suggestions. | |
538 | // (#61411) | |
ba9703b0 | 539 | let ty = tcx.at(span).type_of(*impl_did); |
e74abb32 XL |
540 | match (&ty.peel_refs().kind, &actual.peel_refs().kind) { |
541 | (ty::Adt(def, _), ty::Adt(def_actual, _)) if def == def_actual => { | |
542 | // Use `actual` as it will have more `substs` filled in. | |
543 | self.ty_to_value_string(actual.peel_refs()) | |
544 | } | |
545 | _ => self.ty_to_value_string(ty.peel_refs()), | |
546 | } | |
547 | } else { | |
548 | self.ty_to_value_string(actual.peel_refs()) | |
549 | }; | |
0731742a | 550 | if let SelfSource::MethodCall(expr) = source { |
e74abb32 XL |
551 | err.span_suggestion( |
552 | expr.span.to(span), | |
553 | "use associated function syntax instead", | |
554 | format!("{}::{}", ty_str, item_name), | |
555 | Applicability::MachineApplicable, | |
556 | ); | |
94b46f34 | 557 | } else { |
dfeec247 | 558 | err.help(&format!("try with `{}::{}`", ty_str, item_name,)); |
94b46f34 XL |
559 | } |
560 | ||
dfeec247 | 561 | report_candidates(span, &mut err, static_sources, sugg_span); |
94b46f34 | 562 | } else if static_sources.len() > 1 { |
dfeec247 | 563 | report_candidates(span, &mut err, static_sources, sugg_span); |
a7813a04 | 564 | } |
85aaf69f | 565 | |
74b04a01 | 566 | let mut restrict_type_params = false; |
a7813a04 | 567 | if !unsatisfied_predicates.is_empty() { |
ba9703b0 XL |
568 | let def_span = |def_id| { |
569 | self.tcx.sess.source_map().guess_head_span(self.tcx.def_span(def_id)) | |
570 | }; | |
74b04a01 XL |
571 | let mut type_params = FxHashMap::default(); |
572 | let mut bound_spans = vec![]; | |
573 | let mut collect_type_param_suggestions = | |
574 | |self_ty: Ty<'_>, parent_pred: &ty::Predicate<'_>, obligation: &str| { | |
f9f354fc XL |
575 | if let (ty::Param(_), ty::PredicateKind::Trait(p, _)) = |
576 | (&self_ty.kind, parent_pred.kind()) | |
74b04a01 XL |
577 | { |
578 | if let ty::Adt(def, _) = p.skip_binder().trait_ref.self_ty().kind { | |
f9f354fc XL |
579 | let node = def.did.as_local().map(|def_id| { |
580 | self.tcx.hir().get(self.tcx.hir().as_local_hir_id(def_id)) | |
581 | }); | |
ba9703b0 XL |
582 | if let Some(hir::Node::Item(hir::Item { kind, .. })) = node { |
583 | if let Some(g) = kind.generics() { | |
584 | let key = match &g.where_clause.predicates[..] { | |
585 | [.., pred] => (pred.span().shrink_to_hi(), false), | |
586 | [] => ( | |
587 | g.where_clause | |
588 | .span_for_predicates_or_empty_place(), | |
589 | true, | |
590 | ), | |
591 | }; | |
592 | type_params | |
593 | .entry(key) | |
594 | .or_insert_with(FxHashSet::default) | |
595 | .insert(obligation.to_owned()); | |
74b04a01 | 596 | } |
74b04a01 XL |
597 | } |
598 | } | |
599 | } | |
600 | }; | |
601 | let mut bound_span_label = |self_ty: Ty<'_>, obligation: &str, quiet: &str| { | |
602 | let msg = format!( | |
603 | "doesn't satisfy `{}`", | |
604 | if obligation.len() > 50 { quiet } else { obligation } | |
605 | ); | |
606 | match &self_ty.kind { | |
607 | // Point at the type that couldn't satisfy the bound. | |
608 | ty::Adt(def, _) => bound_spans.push((def_span(def.did), msg)), | |
609 | // Point at the trait object that couldn't satisfy the bound. | |
610 | ty::Dynamic(preds, _) => { | |
f035d41b | 611 | for pred in preds.skip_binder() { |
74b04a01 XL |
612 | match pred { |
613 | ty::ExistentialPredicate::Trait(tr) => { | |
614 | bound_spans.push((def_span(tr.def_id), msg.clone())) | |
615 | } | |
616 | ty::ExistentialPredicate::Projection(_) | |
617 | | ty::ExistentialPredicate::AutoTrait(_) => {} | |
618 | } | |
619 | } | |
620 | } | |
621 | // Point at the closure that couldn't satisfy the bound. | |
622 | ty::Closure(def_id, _) => bound_spans | |
623 | .push((def_span(*def_id), format!("doesn't satisfy `{}`", quiet))), | |
624 | _ => {} | |
625 | } | |
626 | }; | |
f9f354fc XL |
627 | let mut format_pred = |pred: ty::Predicate<'tcx>| { |
628 | match pred.kind() { | |
629 | ty::PredicateKind::Projection(pred) => { | |
74b04a01 XL |
630 | // `<Foo as Iterator>::Item = String`. |
631 | let trait_ref = | |
632 | pred.skip_binder().projection_ty.trait_ref(self.tcx); | |
633 | let assoc = self | |
634 | .tcx | |
635 | .associated_item(pred.skip_binder().projection_ty.item_def_id); | |
636 | let ty = pred.skip_binder().ty; | |
637 | let obligation = format!("{}::{} = {}", trait_ref, assoc.ident, ty); | |
638 | let quiet = format!( | |
639 | "<_ as {}>::{} = {}", | |
640 | trait_ref.print_only_trait_path(), | |
641 | assoc.ident, | |
642 | ty | |
643 | ); | |
644 | bound_span_label(trait_ref.self_ty(), &obligation, &quiet); | |
645 | Some((obligation, trait_ref.self_ty())) | |
646 | } | |
f9f354fc | 647 | ty::PredicateKind::Trait(poly_trait_ref, _) => { |
74b04a01 XL |
648 | let p = poly_trait_ref.skip_binder().trait_ref; |
649 | let self_ty = p.self_ty(); | |
650 | let path = p.print_only_trait_path(); | |
651 | let obligation = format!("{}: {}", self_ty, path); | |
652 | let quiet = format!("_: {}", path); | |
653 | bound_span_label(self_ty, &obligation, &quiet); | |
654 | Some((obligation, self_ty)) | |
655 | } | |
656 | _ => None, | |
657 | } | |
658 | }; | |
dfeec247 XL |
659 | let mut bound_list = unsatisfied_predicates |
660 | .iter() | |
74b04a01 XL |
661 | .filter_map(|(pred, parent_pred)| { |
662 | format_pred(*pred).map(|(p, self_ty)| match parent_pred { | |
663 | None => format!("`{}`", p), | |
664 | Some(parent_pred) => match format_pred(*parent_pred) { | |
665 | None => format!("`{}`", p), | |
666 | Some((parent_p, _)) => { | |
667 | collect_type_param_suggestions(self_ty, parent_pred, &p); | |
668 | format!("`{}`\nwhich is required by `{}`", p, parent_p) | |
669 | } | |
670 | }, | |
671 | }) | |
672 | }) | |
673 | .enumerate() | |
674 | .collect::<Vec<(usize, String)>>(); | |
675 | for ((span, empty_where), obligations) in type_params.into_iter() { | |
676 | restrict_type_params = true; | |
677 | err.span_suggestion_verbose( | |
678 | span, | |
679 | &format!( | |
680 | "consider restricting the type parameter{s} to satisfy the \ | |
681 | trait bound{s}", | |
682 | s = pluralize!(obligations.len()) | |
683 | ), | |
684 | format!( | |
685 | "{} {}", | |
686 | if empty_where { " where" } else { "," }, | |
687 | obligations.into_iter().collect::<Vec<_>>().join(", ") | |
688 | ), | |
689 | Applicability::MaybeIncorrect, | |
690 | ); | |
691 | } | |
692 | ||
693 | bound_list.sort_by(|(_, a), (_, b)| a.cmp(&b)); // Sort alphabetically. | |
694 | bound_list.dedup_by(|(_, a), (_, b)| a == b); // #35677 | |
695 | bound_list.sort_by_key(|(pos, _)| *pos); // Keep the original predicate order. | |
696 | bound_spans.sort(); | |
697 | bound_spans.dedup(); | |
698 | for (span, msg) in bound_spans.into_iter() { | |
699 | err.span_label(span, &msg); | |
700 | } | |
701 | if !bound_list.is_empty() { | |
702 | let bound_list = bound_list | |
703 | .into_iter() | |
704 | .map(|(_, path)| path) | |
705 | .collect::<Vec<_>>() | |
706 | .join("\n"); | |
707 | err.note(&format!( | |
708 | "the method `{}` exists but the following trait bounds were not \ | |
709 | satisfied:\n{}", | |
710 | item_name, bound_list | |
711 | )); | |
712 | } | |
a7813a04 | 713 | } |
62682a34 | 714 | |
74b04a01 | 715 | if actual.is_numeric() && actual.is_fresh() || restrict_type_params { |
2c00a5a8 | 716 | } else { |
dfeec247 XL |
717 | self.suggest_traits_to_import( |
718 | &mut err, | |
719 | span, | |
720 | rcvr_ty, | |
721 | item_name, | |
722 | source, | |
723 | out_of_scope_traits, | |
74b04a01 | 724 | &unsatisfied_predicates, |
dfeec247 | 725 | ); |
2c00a5a8 | 726 | } |
ea8adc8c | 727 | |
48663c56 XL |
728 | if actual.is_enum() { |
729 | let adt_def = actual.ty_adt_def().expect("enum is not an ADT"); | |
730 | if let Some(suggestion) = lev_distance::find_best_match_for_name( | |
731 | adt_def.variants.iter().map(|s| &s.ident.name), | |
732 | &item_name.as_str(), | |
733 | None, | |
734 | ) { | |
735 | err.span_suggestion( | |
736 | span, | |
737 | "there is a variant with a similar name", | |
738 | suggestion.to_string(), | |
739 | Applicability::MaybeIncorrect, | |
740 | ); | |
741 | } | |
742 | } | |
743 | ||
e74abb32 XL |
744 | let mut fallback_span = true; |
745 | let msg = "remove this method call"; | |
746 | if item_name.as_str() == "as_str" && actual.peel_refs().is_str() { | |
747 | if let SelfSource::MethodCall(expr) = source { | |
dfeec247 XL |
748 | let call_expr = |
749 | self.tcx.hir().expect_expr(self.tcx.hir().get_parent_node(expr.hir_id)); | |
e74abb32 XL |
750 | if let Some(span) = call_expr.span.trim_start(expr.span) { |
751 | err.span_suggestion( | |
752 | span, | |
753 | msg, | |
754 | String::new(), | |
755 | Applicability::MachineApplicable, | |
756 | ); | |
757 | fallback_span = false; | |
758 | } | |
759 | } | |
760 | if fallback_span { | |
761 | err.span_label(span, msg); | |
762 | } | |
763 | } else if let Some(lev_candidate) = lev_candidate { | |
ba9703b0 | 764 | let def_kind = lev_candidate.kind.as_def_kind(); |
9fa01778 XL |
765 | err.span_suggestion( |
766 | span, | |
48663c56 XL |
767 | &format!( |
768 | "there is {} {} with a similar name", | |
769 | def_kind.article(), | |
416331ca | 770 | def_kind.descr(lev_candidate.def_id), |
48663c56 | 771 | ), |
9fa01778 XL |
772 | lev_candidate.ident.to_string(), |
773 | Applicability::MaybeIncorrect, | |
774 | ); | |
ea8adc8c | 775 | } |
48663c56 | 776 | |
e1599b0c | 777 | return Some(err); |
a7813a04 | 778 | } |
85aaf69f | 779 | |
a7813a04 | 780 | MethodError::Ambiguity(sources) => { |
dfeec247 XL |
781 | let mut err = struct_span_err!( |
782 | self.sess(), | |
ba9703b0 | 783 | item_name.span, |
dfeec247 XL |
784 | E0034, |
785 | "multiple applicable items in scope" | |
786 | ); | |
ba9703b0 | 787 | err.span_label(item_name.span, format!("multiple `{}` found", item_name)); |
85aaf69f | 788 | |
dfeec247 | 789 | report_candidates(span, &mut err, sources, sugg_span); |
a7813a04 XL |
790 | err.emit(); |
791 | } | |
85aaf69f | 792 | |
416331ca | 793 | MethodError::PrivateMatch(kind, def_id, out_of_scope_traits) => { |
ba9703b0 | 794 | let kind = kind.descr(def_id); |
dfeec247 XL |
795 | let mut err = struct_span_err!( |
796 | self.tcx.sess, | |
ba9703b0 | 797 | item_name.span, |
dfeec247 XL |
798 | E0624, |
799 | "{} `{}` is private", | |
ba9703b0 | 800 | kind, |
dfeec247 XL |
801 | item_name |
802 | ); | |
ba9703b0 | 803 | err.span_label(item_name.span, &format!("private {}", kind)); |
3b2f2976 XL |
804 | self.suggest_valid_traits(&mut err, out_of_scope_traits); |
805 | err.emit(); | |
a7813a04 | 806 | } |
3b2f2976 | 807 | |
74b04a01 | 808 | MethodError::IllegalSizedBound(candidates, needs_mut, bound_span) => { |
3b2f2976 XL |
809 | let msg = format!("the `{}` method cannot be invoked on a trait object", item_name); |
810 | let mut err = self.sess().struct_span_err(span, &msg); | |
74b04a01 | 811 | err.span_label(bound_span, "this has a `Sized` requirement"); |
3b2f2976 | 812 | if !candidates.is_empty() { |
e74abb32 XL |
813 | let help = format!( |
814 | "{an}other candidate{s} {were} found in the following trait{s}, perhaps \ | |
815 | add a `use` for {one_of_them}:", | |
dfeec247 | 816 | an = if candidates.len() == 1 { "an" } else { "" }, |
60c5eb7d | 817 | s = pluralize!(candidates.len()), |
e74abb32 | 818 | were = if candidates.len() == 1 { "was" } else { "were" }, |
dfeec247 | 819 | one_of_them = if candidates.len() == 1 { "it" } else { "one_of_them" }, |
e74abb32 | 820 | ); |
3b2f2976 XL |
821 | self.suggest_use_candidates(&mut err, help, candidates); |
822 | } | |
e74abb32 XL |
823 | if let ty::Ref(region, t_type, mutability) = rcvr_ty.kind { |
824 | if needs_mut { | |
dfeec247 XL |
825 | let trait_type = self.tcx.mk_ref( |
826 | region, | |
827 | ty::TypeAndMut { ty: t_type, mutbl: mutability.invert() }, | |
828 | ); | |
e74abb32 XL |
829 | err.note(&format!("you need `{}` instead of `{}`", trait_type, rcvr_ty)); |
830 | } | |
831 | } | |
3b2f2976 XL |
832 | err.emit(); |
833 | } | |
ea8adc8c | 834 | |
dfeec247 | 835 | MethodError::BadReturnType => bug!("no return type expectations but got BadReturnType"), |
3b2f2976 | 836 | } |
e1599b0c | 837 | None |
3b2f2976 XL |
838 | } |
839 | ||
e74abb32 XL |
840 | /// Print out the type for use in value namespace. |
841 | fn ty_to_value_string(&self, ty: Ty<'tcx>) -> String { | |
842 | match ty.kind { | |
843 | ty::Adt(def, substs) => format!("{}", ty::Instance::new(def.did, substs)), | |
844 | _ => self.ty_to_string(ty), | |
845 | } | |
846 | } | |
847 | ||
dfeec247 XL |
848 | fn suggest_use_candidates( |
849 | &self, | |
850 | err: &mut DiagnosticBuilder<'_>, | |
851 | mut msg: String, | |
852 | candidates: Vec<DefId>, | |
853 | ) { | |
74b04a01 | 854 | let module_did = self.tcx.parent_module(self.body_id); |
f9f354fc | 855 | let module_id = self.tcx.hir().as_local_hir_id(module_did); |
0731742a | 856 | let krate = self.tcx.hir().krate(); |
ff7c6d11 XL |
857 | let (span, found_use) = UsePlacementFinder::check(self.tcx, krate, module_id); |
858 | if let Some(span) = span { | |
859 | let path_strings = candidates.iter().map(|did| { | |
0731742a | 860 | // Produce an additional newline to separate the new use statement |
ff7c6d11 | 861 | // from the directly following item. |
dfeec247 | 862 | let additional_newline = if found_use { "" } else { "\n" }; |
0bf4aa26 XL |
863 | format!( |
864 | "use {};\n{}", | |
532ac7d7 | 865 | with_crate_prefix(|| self.tcx.def_path_str(*did)), |
0bf4aa26 XL |
866 | additional_newline |
867 | ) | |
a1dfa0c6 | 868 | }); |
ff7c6d11 | 869 | |
9fa01778 | 870 | err.span_suggestions(span, &msg, path_strings, Applicability::MaybeIncorrect); |
ff7c6d11 XL |
871 | } else { |
872 | let limit = if candidates.len() == 5 { 5 } else { 4 }; | |
873 | for (i, trait_did) in candidates.iter().take(limit).enumerate() { | |
94b46f34 | 874 | if candidates.len() > 1 { |
dfeec247 XL |
875 | msg.push_str(&format!( |
876 | "\ncandidate #{}: `use {};`", | |
877 | i + 1, | |
878 | with_crate_prefix(|| self.tcx.def_path_str(*trait_did)) | |
879 | )); | |
94b46f34 | 880 | } else { |
dfeec247 XL |
881 | msg.push_str(&format!( |
882 | "\n`use {};`", | |
883 | with_crate_prefix(|| self.tcx.def_path_str(*trait_did)) | |
884 | )); | |
94b46f34 | 885 | } |
ff7c6d11 XL |
886 | } |
887 | if candidates.len() > limit { | |
888 | msg.push_str(&format!("\nand {} others", candidates.len() - limit)); | |
889 | } | |
890 | err.note(&msg[..]); | |
3b2f2976 | 891 | } |
3b2f2976 XL |
892 | } |
893 | ||
e74abb32 XL |
894 | fn suggest_valid_traits( |
895 | &self, | |
896 | err: &mut DiagnosticBuilder<'_>, | |
897 | valid_out_of_scope_traits: Vec<DefId>, | |
898 | ) -> bool { | |
3b2f2976 XL |
899 | if !valid_out_of_scope_traits.is_empty() { |
900 | let mut candidates = valid_out_of_scope_traits; | |
901 | candidates.sort(); | |
902 | candidates.dedup(); | |
903 | err.help("items from traits can only be used if the trait is in scope"); | |
e74abb32 XL |
904 | let msg = format!( |
905 | "the following {traits_are} implemented but not in scope; \ | |
906 | perhaps add a `use` for {one_of_them}:", | |
dfeec247 XL |
907 | traits_are = if candidates.len() == 1 { "trait is" } else { "traits are" }, |
908 | one_of_them = if candidates.len() == 1 { "it" } else { "one of them" }, | |
e74abb32 | 909 | ); |
3b2f2976 XL |
910 | |
911 | self.suggest_use_candidates(err, msg, candidates); | |
912 | true | |
913 | } else { | |
914 | false | |
54a0048b | 915 | } |
85aaf69f SL |
916 | } |
917 | ||
416331ca XL |
918 | fn suggest_traits_to_import<'b>( |
919 | &self, | |
920 | err: &mut DiagnosticBuilder<'_>, | |
921 | span: Span, | |
922 | rcvr_ty: Ty<'tcx>, | |
f9f354fc | 923 | item_name: Ident, |
416331ca XL |
924 | source: SelfSource<'b>, |
925 | valid_out_of_scope_traits: Vec<DefId>, | |
74b04a01 | 926 | unsatisfied_predicates: &[(ty::Predicate<'tcx>, Option<ty::Predicate<'tcx>>)], |
416331ca | 927 | ) { |
3b2f2976 | 928 | if self.suggest_valid_traits(err, valid_out_of_scope_traits) { |
c30ab7b3 | 929 | return; |
85aaf69f | 930 | } |
85aaf69f | 931 | |
0731742a | 932 | let type_is_local = self.type_derefs_to_local(span, rcvr_ty, source); |
a7813a04 | 933 | |
74b04a01 | 934 | let mut arbitrary_rcvr = vec![]; |
0731742a | 935 | // There are no traits implemented, so lets suggest some traits to |
a7813a04 XL |
936 | // implement, by finding ones that have the item name, and are |
937 | // legal to implement. | |
8bb4bdeb | 938 | let mut candidates = all_traits(self.tcx) |
83c7162d | 939 | .into_iter() |
a7813a04 | 940 | .filter(|info| { |
0731742a | 941 | // We approximate the coherence rules to only suggest |
a7813a04 | 942 | // traits that are legal to implement by requiring that |
0731742a | 943 | // either the type or trait is local. Multi-dispatch means |
a7813a04 XL |
944 | // this isn't perfect (that is, there are cases when |
945 | // implementing a trait would be legal but is rejected | |
946 | // here). | |
f9f354fc | 947 | unsatisfied_predicates.iter().all(|(p, _)| match p.kind() { |
74b04a01 XL |
948 | // Hide traits if they are present in predicates as they can be fixed without |
949 | // having to implement them. | |
f9f354fc XL |
950 | ty::PredicateKind::Trait(t, _) => t.def_id() == info.def_id, |
951 | ty::PredicateKind::Projection(p) => p.item_def_id() == info.def_id, | |
952 | _ => false, | |
74b04a01 | 953 | }) && (type_is_local || info.def_id.is_local()) |
dfeec247 | 954 | && self |
74b04a01 | 955 | .associated_item(info.def_id, item_name, Namespace::ValueNS) |
2c00a5a8 | 956 | .filter(|item| { |
ba9703b0 | 957 | if let ty::AssocKind::Fn = item.kind { |
f9f354fc XL |
958 | let id = item |
959 | .def_id | |
960 | .as_local() | |
961 | .map(|def_id| self.tcx.hir().as_local_hir_id(def_id)); | |
74b04a01 | 962 | if let Some(hir::Node::TraitItem(hir::TraitItem { |
ba9703b0 | 963 | kind: hir::TraitItemKind::Fn(fn_sig, method), |
74b04a01 XL |
964 | .. |
965 | })) = id.map(|id| self.tcx.hir().get(id)) | |
966 | { | |
967 | let self_first_arg = match method { | |
ba9703b0 | 968 | hir::TraitFn::Required([ident, ..]) => { |
74b04a01 XL |
969 | ident.name == kw::SelfLower |
970 | } | |
ba9703b0 | 971 | hir::TraitFn::Provided(body_id) => { |
f9f354fc XL |
972 | self.tcx.hir().body(*body_id).params.first().map_or( |
973 | false, | |
974 | |param| { | |
975 | matches!( | |
976 | param.pat.kind, | |
977 | hir::PatKind::Binding(_, _, ident, _) | |
978 | if ident.name == kw::SelfLower | |
979 | ) | |
980 | }, | |
981 | ) | |
74b04a01 XL |
982 | } |
983 | _ => false, | |
984 | }; | |
985 | ||
986 | if !fn_sig.decl.implicit_self.has_implicit_self() | |
987 | && self_first_arg | |
988 | { | |
989 | if let Some(ty) = fn_sig.decl.inputs.get(0) { | |
990 | arbitrary_rcvr.push(ty.span); | |
991 | } | |
992 | return false; | |
993 | } | |
994 | } | |
995 | } | |
2c00a5a8 XL |
996 | // We only want to suggest public or local traits (#45781). |
997 | item.vis == ty::Visibility::Public || info.def_id.is_local() | |
998 | }) | |
999 | .is_some() | |
a7813a04 XL |
1000 | }) |
1001 | .collect::<Vec<_>>(); | |
74b04a01 XL |
1002 | for span in &arbitrary_rcvr { |
1003 | err.span_label( | |
1004 | *span, | |
1005 | "the method might not be found because of this arbitrary self type", | |
1006 | ); | |
1007 | } | |
a7813a04 XL |
1008 | |
1009 | if !candidates.is_empty() { | |
0731742a | 1010 | // Sort from most relevant to least relevant. |
a7813a04 XL |
1011 | candidates.sort_by(|a, b| a.cmp(b).reverse()); |
1012 | candidates.dedup(); | |
1013 | ||
e74abb32 | 1014 | let param_type = match rcvr_ty.kind { |
416331ca | 1015 | ty::Param(param) => Some(param), |
e74abb32 | 1016 | ty::Ref(_, ty, _) => match ty.kind { |
416331ca XL |
1017 | ty::Param(param) => Some(param), |
1018 | _ => None, | |
dfeec247 | 1019 | }, |
416331ca XL |
1020 | _ => None, |
1021 | }; | |
1022 | err.help(if param_type.is_some() { | |
1023 | "items from traits can only be used if the type parameter is bounded by the trait" | |
1024 | } else { | |
1025 | "items from traits can only be used if the trait is implemented and in scope" | |
1026 | }); | |
dfeec247 XL |
1027 | let message = |action| { |
1028 | format!( | |
1029 | "the following {traits_define} an item `{name}`, perhaps you need to {action} \ | |
74b04a01 | 1030 | {one_of_them}:", |
dfeec247 XL |
1031 | traits_define = |
1032 | if candidates.len() == 1 { "trait defines" } else { "traits define" }, | |
1033 | action = action, | |
1034 | one_of_them = if candidates.len() == 1 { "it" } else { "one of them" }, | |
1035 | name = item_name, | |
1036 | ) | |
1037 | }; | |
416331ca XL |
1038 | // Obtain the span for `param` and use it for a structured suggestion. |
1039 | let mut suggested = false; | |
1040 | if let (Some(ref param), Some(ref table)) = (param_type, self.in_progress_tables) { | |
ba9703b0 | 1041 | let table_owner = table.borrow().hir_owner; |
f035d41b XL |
1042 | let generics = self.tcx.generics_of(table_owner.to_def_id()); |
1043 | let type_param = generics.type_param(param, self.tcx); | |
1044 | let hir = &self.tcx.hir(); | |
1045 | if let Some(def_id) = type_param.def_id.as_local() { | |
1046 | let id = hir.as_local_hir_id(def_id); | |
1047 | // Get the `hir::Param` to verify whether it already has any bounds. | |
1048 | // We do this to avoid suggesting code that ends up as `T: FooBar`, | |
1049 | // instead we suggest `T: Foo + Bar` in that case. | |
1050 | match hir.get(id) { | |
1051 | Node::GenericParam(ref param) => { | |
1052 | let mut impl_trait = false; | |
1053 | let has_bounds = | |
1054 | if let hir::GenericParamKind::Type { synthetic: Some(_), .. } = | |
1055 | ¶m.kind | |
dfeec247 | 1056 | { |
e1599b0c XL |
1057 | // We've found `fn foo(x: impl Trait)` instead of |
1058 | // `fn foo<T>(x: T)`. We want to suggest the correct | |
1059 | // `fn foo(x: impl Trait + TraitBound)` instead of | |
1060 | // `fn foo<T: TraitBound>(x: T)`. (#63706) | |
1061 | impl_trait = true; | |
e74abb32 XL |
1062 | param.bounds.get(1) |
1063 | } else { | |
1064 | param.bounds.get(0) | |
1065 | }; | |
f035d41b XL |
1066 | let sp = hir.span(id); |
1067 | let sp = if let Some(first_bound) = has_bounds { | |
1068 | // `sp` only covers `T`, change it so that it covers | |
1069 | // `T:` when appropriate | |
1070 | sp.until(first_bound.span()) | |
1071 | } else { | |
1072 | sp | |
1073 | }; | |
1074 | let trait_def_ids: FxHashSet<DefId> = param | |
1075 | .bounds | |
1076 | .iter() | |
1077 | .filter_map(|bound| Some(bound.trait_ref()?.trait_def_id()?)) | |
1078 | .collect(); | |
1079 | if !candidates.iter().any(|t| trait_def_ids.contains(&t.def_id)) { | |
e74abb32 XL |
1080 | err.span_suggestions( |
1081 | sp, | |
f035d41b XL |
1082 | &message(format!( |
1083 | "restrict type parameter `{}` with", | |
1084 | param.name.ident(), | |
1085 | )), | |
dfeec247 | 1086 | candidates.iter().map(|t| { |
f035d41b XL |
1087 | format!( |
1088 | "{}{} {}{}", | |
1089 | param.name.ident(), | |
1090 | if impl_trait { " +" } else { ":" }, | |
1091 | self.tcx.def_path_str(t.def_id), | |
1092 | if has_bounds.is_some() { " + " } else { "" }, | |
1093 | ) | |
dfeec247 | 1094 | }), |
e74abb32 XL |
1095 | Applicability::MaybeIncorrect, |
1096 | ); | |
e1599b0c | 1097 | } |
f035d41b XL |
1098 | suggested = true; |
1099 | } | |
1100 | Node::Item(hir::Item { | |
1101 | kind: hir::ItemKind::Trait(.., bounds, _), | |
1102 | ident, | |
1103 | .. | |
1104 | }) => { | |
1105 | let (sp, sep, article) = if bounds.is_empty() { | |
1106 | (ident.span.shrink_to_hi(), ":", "a") | |
1107 | } else { | |
1108 | (bounds.last().unwrap().span().shrink_to_hi(), " +", "another") | |
1109 | }; | |
1110 | err.span_suggestions( | |
1111 | sp, | |
1112 | &message(format!("add {} supertrait for", article)), | |
1113 | candidates.iter().map(|t| { | |
1114 | format!("{} {}", sep, self.tcx.def_path_str(t.def_id),) | |
1115 | }), | |
1116 | Applicability::MaybeIncorrect, | |
1117 | ); | |
1118 | suggested = true; | |
416331ca | 1119 | } |
f035d41b | 1120 | _ => {} |
416331ca | 1121 | } |
f035d41b | 1122 | } |
416331ca XL |
1123 | } |
1124 | ||
1125 | if !suggested { | |
74b04a01 | 1126 | let action = if let Some(param) = param_type { |
e74abb32 XL |
1127 | format!("restrict type parameter `{}` with", param) |
1128 | } else { | |
74b04a01 | 1129 | // FIXME: it might only need to be imported into scope, not implemented. |
e74abb32 | 1130 | "implement".to_string() |
74b04a01 XL |
1131 | }; |
1132 | let mut use_note = true; | |
1133 | if let [trait_info] = &candidates[..] { | |
1134 | if let Some(span) = self.tcx.hir().span_if_local(trait_info.def_id) { | |
1135 | err.span_note( | |
ba9703b0 | 1136 | self.tcx.sess.source_map().guess_head_span(span), |
74b04a01 XL |
1137 | &format!( |
1138 | "`{}` defines an item `{}`, perhaps you need to {} it", | |
1139 | self.tcx.def_path_str(trait_info.def_id), | |
1140 | item_name, | |
1141 | action | |
1142 | ), | |
1143 | ); | |
1144 | use_note = false | |
1145 | } | |
1146 | } | |
1147 | if use_note { | |
1148 | let mut msg = message(action); | |
1149 | for (i, trait_info) in candidates.iter().enumerate() { | |
1150 | msg.push_str(&format!( | |
1151 | "\ncandidate #{}: `{}`", | |
1152 | i + 1, | |
1153 | self.tcx.def_path_str(trait_info.def_id), | |
1154 | )); | |
1155 | } | |
1156 | err.note(&msg[..]); | |
416331ca | 1157 | } |
a7813a04 | 1158 | } |
85aaf69f | 1159 | } |
85aaf69f SL |
1160 | } |
1161 | ||
a7813a04 XL |
1162 | /// Checks whether there is a local type somewhere in the chain of |
1163 | /// autoderefs of `rcvr_ty`. | |
dfeec247 | 1164 | fn type_derefs_to_local(&self, span: Span, rcvr_ty: Ty<'tcx>, source: SelfSource<'_>) -> bool { |
9fa01778 | 1165 | fn is_local(ty: Ty<'_>) -> bool { |
e74abb32 | 1166 | match ty.kind { |
b7449926 XL |
1167 | ty::Adt(def, _) => def.did.is_local(), |
1168 | ty::Foreign(did) => did.is_local(), | |
a7813a04 | 1169 | |
dfeec247 XL |
1170 | ty::Dynamic(ref tr, ..) => { |
1171 | tr.principal().map(|d| d.def_id().is_local()).unwrap_or(false) | |
1172 | } | |
a7813a04 | 1173 | |
b7449926 | 1174 | ty::Param(_) => true, |
a7813a04 | 1175 | |
0731742a XL |
1176 | // Everything else (primitive types, etc.) is effectively |
1177 | // non-local (there are "edge" cases, e.g., `(LocalType,)`, but | |
a7813a04 XL |
1178 | // the noise from these sort of types is usually just really |
1179 | // annoying, rather than any sort of help). | |
c30ab7b3 | 1180 | _ => false, |
a7813a04 | 1181 | } |
85aaf69f | 1182 | } |
85aaf69f | 1183 | |
a7813a04 XL |
1184 | // This occurs for UFCS desugaring of `T::method`, where there is no |
1185 | // receiver expression for the method call, and thus no autoderef. | |
0731742a | 1186 | if let SelfSource::QPath(_) = source { |
e74abb32 | 1187 | return is_local(self.resolve_vars_with_obligations(rcvr_ty)); |
c34b1796 | 1188 | } |
c34b1796 | 1189 | |
3157f602 | 1190 | self.autoderef(span, rcvr_ty).any(|(ty, _)| is_local(ty)) |
c34b1796 | 1191 | } |
85aaf69f SL |
1192 | } |
1193 | ||
0731742a XL |
1194 | #[derive(Copy, Clone)] |
1195 | pub enum SelfSource<'a> { | |
dfeec247 XL |
1196 | QPath(&'a hir::Ty<'a>), |
1197 | MethodCall(&'a hir::Expr<'a> /* rcvr */), | |
0731742a XL |
1198 | } |
1199 | ||
c34b1796 | 1200 | #[derive(Copy, Clone)] |
85aaf69f | 1201 | pub struct TraitInfo { |
e9174d1e | 1202 | pub def_id: DefId, |
85aaf69f SL |
1203 | } |
1204 | ||
85aaf69f SL |
1205 | impl PartialEq for TraitInfo { |
1206 | fn eq(&self, other: &TraitInfo) -> bool { | |
1207 | self.cmp(other) == Ordering::Equal | |
1208 | } | |
1209 | } | |
1210 | impl Eq for TraitInfo {} | |
1211 | impl PartialOrd for TraitInfo { | |
c30ab7b3 SL |
1212 | fn partial_cmp(&self, other: &TraitInfo) -> Option<Ordering> { |
1213 | Some(self.cmp(other)) | |
1214 | } | |
85aaf69f SL |
1215 | } |
1216 | impl Ord for TraitInfo { | |
1217 | fn cmp(&self, other: &TraitInfo) -> Ordering { | |
0731742a XL |
1218 | // Local crates are more important than remote ones (local: |
1219 | // `cnum == 0`), and otherwise we throw in the defid for totality. | |
85aaf69f | 1220 | |
b039eaaf SL |
1221 | let lhs = (other.def_id.krate, other.def_id); |
1222 | let rhs = (self.def_id.krate, self.def_id); | |
85aaf69f SL |
1223 | lhs.cmp(&rhs) |
1224 | } | |
1225 | } | |
1226 | ||
9fa01778 | 1227 | /// Retrieves all traits in this crate and any dependent crates. |
416331ca | 1228 | pub fn all_traits(tcx: TyCtxt<'_>) -> Vec<TraitInfo> { |
83c7162d XL |
1229 | tcx.all_traits(LOCAL_CRATE).iter().map(|&def_id| TraitInfo { def_id }).collect() |
1230 | } | |
1231 | ||
9fa01778 | 1232 | /// Computes all traits in this crate and any dependent crates. |
416331ca | 1233 | fn compute_all_traits(tcx: TyCtxt<'_>) -> Vec<DefId> { |
0731742a | 1234 | use hir::itemlikevisit; |
85aaf69f | 1235 | |
0731742a | 1236 | let mut traits = vec![]; |
85aaf69f | 1237 | |
0731742a | 1238 | // Crate-local: |
476ff2be | 1239 | |
dc9dc135 | 1240 | struct Visitor<'a, 'tcx> { |
0731742a XL |
1241 | map: &'a hir_map::Map<'tcx>, |
1242 | traits: &'a mut Vec<DefId>, | |
1243 | } | |
32a655c1 | 1244 | |
0731742a | 1245 | impl<'v, 'a, 'tcx> itemlikevisit::ItemLikeVisitor<'v> for Visitor<'a, 'tcx> { |
dfeec247 | 1246 | fn visit_item(&mut self, i: &'v hir::Item<'v>) { |
e74abb32 | 1247 | match i.kind { |
dfeec247 | 1248 | hir::ItemKind::Trait(..) | hir::ItemKind::TraitAlias(..) => { |
416331ca | 1249 | let def_id = self.map.local_def_id(i.hir_id); |
f9f354fc | 1250 | self.traits.push(def_id.to_def_id()); |
532ac7d7 | 1251 | } |
dfeec247 | 1252 | _ => (), |
476ff2be | 1253 | } |
85aaf69f | 1254 | } |
0731742a | 1255 | |
dfeec247 | 1256 | fn visit_trait_item(&mut self, _trait_item: &hir::TraitItem<'_>) {} |
0731742a | 1257 | |
dfeec247 | 1258 | fn visit_impl_item(&mut self, _impl_item: &hir::ImplItem<'_>) {} |
0731742a XL |
1259 | } |
1260 | ||
dfeec247 | 1261 | tcx.hir().krate().visit_all_item_likes(&mut Visitor { map: &tcx.hir(), traits: &mut traits }); |
0731742a XL |
1262 | |
1263 | // Cross-crate: | |
1264 | ||
1265 | let mut external_mods = FxHashSet::default(); | |
dc9dc135 XL |
1266 | fn handle_external_res( |
1267 | tcx: TyCtxt<'_>, | |
1268 | traits: &mut Vec<DefId>, | |
1269 | external_mods: &mut FxHashSet<DefId>, | |
1270 | res: Res, | |
1271 | ) { | |
48663c56 | 1272 | match res { |
ba9703b0 | 1273 | Res::Def(DefKind::Trait | DefKind::TraitAlias, def_id) => { |
0731742a XL |
1274 | traits.push(def_id); |
1275 | } | |
48663c56 | 1276 | Res::Def(DefKind::Mod, def_id) => { |
0731742a XL |
1277 | if !external_mods.insert(def_id) { |
1278 | return; | |
85aaf69f | 1279 | } |
0731742a | 1280 | for child in tcx.item_children(def_id).iter() { |
48663c56 | 1281 | handle_external_res(tcx, traits, external_mods, child.res) |
85aaf69f | 1282 | } |
85aaf69f | 1283 | } |
0731742a | 1284 | _ => {} |
85aaf69f | 1285 | } |
0731742a XL |
1286 | } |
1287 | for &cnum in tcx.crates().iter() { | |
dfeec247 | 1288 | let def_id = DefId { krate: cnum, index: CRATE_DEF_INDEX }; |
48663c56 | 1289 | handle_external_res(tcx, &mut traits, &mut external_mods, Res::Def(DefKind::Mod, def_id)); |
0731742a | 1290 | } |
85aaf69f | 1291 | |
83c7162d | 1292 | traits |
85aaf69f SL |
1293 | } |
1294 | ||
f035d41b | 1295 | pub fn provide(providers: &mut ty::query::Providers) { |
83c7162d XL |
1296 | providers.all_traits = |tcx, cnum| { |
1297 | assert_eq!(cnum, LOCAL_CRATE); | |
dc9dc135 | 1298 | &tcx.arena.alloc(compute_all_traits(tcx))[..] |
0531ce1d | 1299 | } |
85aaf69f | 1300 | } |
ff7c6d11 | 1301 | |
dc9dc135 | 1302 | struct UsePlacementFinder<'tcx> { |
9fa01778 | 1303 | target_module: hir::HirId, |
ff7c6d11 XL |
1304 | span: Option<Span>, |
1305 | found_use: bool, | |
dc9dc135 | 1306 | tcx: TyCtxt<'tcx>, |
ff7c6d11 XL |
1307 | } |
1308 | ||
dc9dc135 | 1309 | impl UsePlacementFinder<'tcx> { |
ff7c6d11 | 1310 | fn check( |
dc9dc135 | 1311 | tcx: TyCtxt<'tcx>, |
dfeec247 | 1312 | krate: &'tcx hir::Crate<'tcx>, |
9fa01778 | 1313 | target_module: hir::HirId, |
ff7c6d11 | 1314 | ) -> (Option<Span>, bool) { |
dfeec247 XL |
1315 | let mut finder = UsePlacementFinder { target_module, span: None, found_use: false, tcx }; |
1316 | intravisit::walk_crate(&mut finder, krate); | |
ff7c6d11 XL |
1317 | (finder.span, finder.found_use) |
1318 | } | |
1319 | } | |
1320 | ||
dfeec247 XL |
1321 | impl intravisit::Visitor<'tcx> for UsePlacementFinder<'tcx> { |
1322 | fn visit_mod(&mut self, module: &'tcx hir::Mod<'tcx>, _: Span, hir_id: hir::HirId) { | |
ff7c6d11 XL |
1323 | if self.span.is_some() { |
1324 | return; | |
1325 | } | |
9fa01778 | 1326 | if hir_id != self.target_module { |
dfeec247 | 1327 | intravisit::walk_mod(self, module, hir_id); |
ff7c6d11 XL |
1328 | return; |
1329 | } | |
0731742a | 1330 | // Find a `use` statement. |
dfeec247 | 1331 | for item_id in module.item_ids { |
dc9dc135 | 1332 | let item = self.tcx.hir().expect_item(item_id.id); |
e74abb32 | 1333 | match item.kind { |
8faf50e0 | 1334 | hir::ItemKind::Use(..) => { |
0731742a XL |
1335 | // Don't suggest placing a `use` before the prelude |
1336 | // import or other generated ones. | |
e1599b0c | 1337 | if !item.span.from_expansion() { |
0531ce1d | 1338 | self.span = Some(item.span.shrink_to_lo()); |
ff7c6d11 XL |
1339 | self.found_use = true; |
1340 | return; | |
1341 | } | |
dfeec247 | 1342 | } |
0731742a | 1343 | // Don't place `use` before `extern crate`... |
8faf50e0 | 1344 | hir::ItemKind::ExternCrate(_) => {} |
0731742a | 1345 | // ...but do place them before the first other item. |
dfeec247 XL |
1346 | _ => { |
1347 | if self.span.map_or(true, |span| item.span < span) { | |
1348 | if !item.span.from_expansion() { | |
1349 | // Don't insert between attributes and an item. | |
1350 | if item.attrs.is_empty() { | |
1351 | self.span = Some(item.span.shrink_to_lo()); | |
1352 | } else { | |
1353 | // Find the first attribute on the item. | |
1354 | for attr in item.attrs { | |
1355 | if self.span.map_or(true, |span| attr.span < span) { | |
1356 | self.span = Some(attr.span.shrink_to_lo()); | |
1357 | } | |
ff7c6d11 XL |
1358 | } |
1359 | } | |
1360 | } | |
1361 | } | |
dfeec247 | 1362 | } |
ff7c6d11 XL |
1363 | } |
1364 | } | |
1365 | } | |
0731742a | 1366 | |
ba9703b0 | 1367 | type Map = intravisit::ErasedMap<'tcx>; |
dfeec247 | 1368 | |
ba9703b0 | 1369 | fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap<Self::Map> { |
dfeec247 | 1370 | intravisit::NestedVisitorMap::None |
ff7c6d11 XL |
1371 | } |
1372 | } | |
dfeec247 XL |
1373 | |
1374 | fn print_disambiguation_help( | |
f9f354fc | 1375 | item_name: Ident, |
dfeec247 XL |
1376 | args: Option<&'tcx [hir::Expr<'tcx>]>, |
1377 | err: &mut DiagnosticBuilder<'_>, | |
1378 | trait_name: String, | |
1379 | rcvr_ty: Ty<'_>, | |
1380 | kind: ty::AssocKind, | |
ba9703b0 | 1381 | def_id: DefId, |
dfeec247 XL |
1382 | span: Span, |
1383 | candidate: Option<usize>, | |
1384 | source_map: &source_map::SourceMap, | |
1385 | ) { | |
1386 | let mut applicability = Applicability::MachineApplicable; | |
ba9703b0 | 1387 | let sugg_args = if let (ty::AssocKind::Fn, Some(args)) = (kind, args) { |
dfeec247 XL |
1388 | format!( |
1389 | "({}{})", | |
1390 | if rcvr_ty.is_region_ptr() { | |
1391 | if rcvr_ty.is_mutable_ptr() { "&mut " } else { "&" } | |
1392 | } else { | |
1393 | "" | |
1394 | }, | |
1395 | args.iter() | |
1396 | .map(|arg| source_map.span_to_snippet(arg.span).unwrap_or_else(|_| { | |
1397 | applicability = Applicability::HasPlaceholders; | |
1398 | "_".to_owned() | |
1399 | })) | |
1400 | .collect::<Vec<_>>() | |
1401 | .join(", "), | |
1402 | ) | |
1403 | } else { | |
1404 | String::new() | |
1405 | }; | |
1406 | let sugg = format!("{}::{}{}", trait_name, item_name, sugg_args); | |
1407 | err.span_suggestion( | |
1408 | span, | |
1409 | &format!( | |
1410 | "disambiguate the {} for {}", | |
ba9703b0 | 1411 | kind.as_def_kind().descr(def_id), |
dfeec247 XL |
1412 | if let Some(candidate) = candidate { |
1413 | format!("candidate #{}", candidate) | |
1414 | } else { | |
1415 | "the candidate".to_string() | |
1416 | }, | |
1417 | ), | |
1418 | sugg, | |
1419 | applicability, | |
1420 | ); | |
1421 | } |