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