]>
Commit | Line | Data |
---|---|---|
f035d41b | 1 | use crate::diagnostics::{ImportSuggestion, LabelSuggestion, TypoSuggestion}; |
74b04a01 | 2 | use crate::late::lifetimes::{ElisionFailureInfo, LifetimeContext}; |
29967ef6 | 3 | use crate::late::{AliasPossibility, LateResolutionVisitor, RibKind}; |
dfeec247 XL |
4 | use crate::path_names_to_string; |
5 | use crate::{CrateLint, Module, ModuleKind, ModuleOrUniformRoot}; | |
6 | use crate::{PathResult, PathSource, Segment}; | |
416331ca | 7 | |
3dfed10e XL |
8 | use rustc_ast::visit::FnKind; |
9 | use rustc_ast::{self as ast, Expr, ExprKind, Item, ItemKind, NodeId, Path, Ty, TyKind}; | |
29967ef6 | 10 | use rustc_ast_pretty::pprust::path_segment_to_string; |
dfeec247 | 11 | use rustc_data_structures::fx::FxHashSet; |
74b04a01 XL |
12 | use rustc_errors::{pluralize, struct_span_err, Applicability, DiagnosticBuilder}; |
13 | use rustc_hir as hir; | |
dfeec247 | 14 | use rustc_hir::def::Namespace::{self, *}; |
29967ef6 | 15 | use rustc_hir::def::{self, CtorKind, CtorOf, DefKind}; |
3dfed10e | 16 | use rustc_hir::def_id::{DefId, CRATE_DEF_INDEX, LOCAL_CRATE}; |
dfeec247 | 17 | use rustc_hir::PrimTy; |
1b1a35ee | 18 | use rustc_session::parse::feature_err; |
6a06907d | 19 | use rustc_span::edition::Edition; |
dfeec247 | 20 | use rustc_span::hygiene::MacroKind; |
fc512014 | 21 | use rustc_span::lev_distance::find_best_match_for_name; |
3dfed10e | 22 | use rustc_span::symbol::{kw, sym, Ident, Symbol}; |
29967ef6 | 23 | use rustc_span::{BytePos, MultiSpan, Span, DUMMY_SP}; |
416331ca | 24 | |
3dfed10e | 25 | use tracing::debug; |
60c5eb7d | 26 | |
416331ca XL |
27 | type Res = def::Res<ast::NodeId>; |
28 | ||
29 | /// A field or associated item from self type suggested in case of resolution failure. | |
30 | enum AssocSuggestion { | |
31 | Field, | |
32 | MethodWithSelf, | |
29967ef6 XL |
33 | AssocFn, |
34 | AssocType, | |
35 | AssocConst, | |
36 | } | |
37 | ||
38 | impl AssocSuggestion { | |
39 | fn action(&self) -> &'static str { | |
40 | match self { | |
41 | AssocSuggestion::Field => "use the available field", | |
42 | AssocSuggestion::MethodWithSelf => "call the method with the fully-qualified path", | |
43 | AssocSuggestion::AssocFn => "call the associated function", | |
44 | AssocSuggestion::AssocConst => "use the associated `const`", | |
45 | AssocSuggestion::AssocType => "use the associated type", | |
46 | } | |
47 | } | |
416331ca XL |
48 | } |
49 | ||
74b04a01 XL |
50 | crate enum MissingLifetimeSpot<'tcx> { |
51 | Generics(&'tcx hir::Generics<'tcx>), | |
52 | HigherRanked { span: Span, span_type: ForLifetimeSpanType }, | |
3dfed10e | 53 | Static, |
74b04a01 XL |
54 | } |
55 | ||
56 | crate enum ForLifetimeSpanType { | |
57 | BoundEmpty, | |
58 | BoundTail, | |
59 | TypeEmpty, | |
60 | TypeTail, | |
61 | } | |
62 | ||
63 | impl ForLifetimeSpanType { | |
64 | crate fn descr(&self) -> &'static str { | |
65 | match self { | |
66 | Self::BoundEmpty | Self::BoundTail => "bound", | |
67 | Self::TypeEmpty | Self::TypeTail => "type", | |
68 | } | |
69 | } | |
70 | ||
71 | crate fn suggestion(&self, sugg: &str) -> String { | |
72 | match self { | |
73 | Self::BoundEmpty | Self::TypeEmpty => format!("for<{}> ", sugg), | |
74 | Self::BoundTail | Self::TypeTail => format!(", {}", sugg), | |
75 | } | |
76 | } | |
77 | } | |
78 | ||
79 | impl<'tcx> Into<MissingLifetimeSpot<'tcx>> for &'tcx hir::Generics<'tcx> { | |
80 | fn into(self) -> MissingLifetimeSpot<'tcx> { | |
81 | MissingLifetimeSpot::Generics(self) | |
82 | } | |
83 | } | |
84 | ||
416331ca XL |
85 | fn is_self_type(path: &[Segment], namespace: Namespace) -> bool { |
86 | namespace == TypeNS && path.len() == 1 && path[0].ident.name == kw::SelfUpper | |
87 | } | |
88 | ||
89 | fn is_self_value(path: &[Segment], namespace: Namespace) -> bool { | |
90 | namespace == ValueNS && path.len() == 1 && path[0].ident.name == kw::SelfLower | |
91 | } | |
92 | ||
93 | /// Gets the stringified path for an enum from an `ImportSuggestion` for an enum variant. | |
94 | fn import_candidate_to_enum_paths(suggestion: &ImportSuggestion) -> (String, String) { | |
95 | let variant_path = &suggestion.path; | |
96 | let variant_path_string = path_names_to_string(variant_path); | |
97 | ||
98 | let path_len = suggestion.path.segments.len(); | |
99 | let enum_path = ast::Path { | |
100 | span: suggestion.path.span, | |
101 | segments: suggestion.path.segments[0..path_len - 1].to_vec(), | |
1b1a35ee | 102 | tokens: None, |
416331ca XL |
103 | }; |
104 | let enum_path_string = path_names_to_string(&enum_path); | |
105 | ||
106 | (variant_path_string, enum_path_string) | |
107 | } | |
108 | ||
1b1a35ee | 109 | impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { |
3dfed10e XL |
110 | fn def_span(&self, def_id: DefId) -> Option<Span> { |
111 | match def_id.krate { | |
112 | LOCAL_CRATE => self.r.opt_span(def_id), | |
113 | _ => Some( | |
114 | self.r | |
115 | .session | |
116 | .source_map() | |
117 | .guess_head_span(self.r.cstore().get_span_untracked(def_id, self.r.session)), | |
118 | ), | |
119 | } | |
120 | } | |
121 | ||
416331ca XL |
122 | /// Handles error reporting for `smart_resolve_path_fragment` function. |
123 | /// Creates base error and amends it with one short label and possibly some longer helps/notes. | |
124 | pub(crate) fn smart_resolve_report_errors( | |
125 | &mut self, | |
126 | path: &[Segment], | |
127 | span: Span, | |
128 | source: PathSource<'_>, | |
129 | res: Option<Res>, | |
130 | ) -> (DiagnosticBuilder<'a>, Vec<ImportSuggestion>) { | |
131 | let ident_span = path.last().map_or(span, |ident| ident.ident.span); | |
132 | let ns = source.namespace(); | |
133 | let is_expected = &|res| source.is_expected(res); | |
f035d41b | 134 | let is_enum_variant = &|res| matches!(res, Res::Def(DefKind::Variant, _)); |
416331ca XL |
135 | |
136 | // Make the base error. | |
6a06907d | 137 | let mut expected = source.descr_expected(); |
416331ca XL |
138 | let path_str = Segment::names_to_string(path); |
139 | let item_str = path.last().unwrap().ident; | |
e74abb32 | 140 | let (base_msg, fallback_label, base_span, could_be_expr) = if let Some(res) = res { |
dfeec247 XL |
141 | ( |
142 | format!("expected {}, found {} `{}`", expected, res.descr(), path_str), | |
416331ca | 143 | format!("not a {}", expected), |
e74abb32 XL |
144 | span, |
145 | match res { | |
146 | Res::Def(DefKind::Fn, _) => { | |
147 | // Verify whether this is a fn call or an Fn used as a type. | |
dfeec247 XL |
148 | self.r |
149 | .session | |
150 | .source_map() | |
151 | .span_to_snippet(span) | |
152 | .map(|snippet| snippet.ends_with(')')) | |
153 | .unwrap_or(false) | |
e74abb32 | 154 | } |
ba9703b0 XL |
155 | Res::Def( |
156 | DefKind::Ctor(..) | DefKind::AssocFn | DefKind::Const | DefKind::AssocConst, | |
157 | _, | |
158 | ) | |
dfeec247 XL |
159 | | Res::SelfCtor(_) |
160 | | Res::PrimTy(_) | |
161 | | Res::Local(_) => true, | |
e74abb32 | 162 | _ => false, |
dfeec247 XL |
163 | }, |
164 | ) | |
416331ca XL |
165 | } else { |
166 | let item_span = path.last().unwrap().ident.span; | |
167 | let (mod_prefix, mod_str) = if path.len() == 1 { | |
168 | (String::new(), "this scope".to_string()) | |
169 | } else if path.len() == 2 && path[0].ident.name == kw::PathRoot { | |
6a06907d XL |
170 | if self.r.session.edition() > Edition::Edition2015 { |
171 | // In edition 2018 onwards, the `::foo` syntax may only pull from the extern prelude | |
172 | // which overrides all other expectations of item type | |
173 | expected = "crate"; | |
174 | (String::new(), "the list of imported crates".to_string()) | |
175 | } else { | |
176 | (String::new(), "the crate root".to_string()) | |
177 | } | |
178 | } else if path.len() == 2 && path[0].ident.name == kw::Crate { | |
416331ca XL |
179 | (String::new(), "the crate root".to_string()) |
180 | } else { | |
181 | let mod_path = &path[..path.len() - 1]; | |
dfeec247 XL |
182 | let mod_prefix = |
183 | match self.resolve_path(mod_path, Some(TypeNS), false, span, CrateLint::No) { | |
184 | PathResult::Module(ModuleOrUniformRoot::Module(module)) => module.res(), | |
185 | _ => None, | |
186 | } | |
187 | .map_or(String::new(), |res| format!("{} ", res.descr())); | |
416331ca XL |
188 | (mod_prefix, format!("`{}`", Segment::names_to_string(mod_path))) |
189 | }; | |
dfeec247 XL |
190 | ( |
191 | format!("cannot find {} `{}` in {}{}", expected, item_str, mod_prefix, mod_str), | |
f9f354fc | 192 | if path_str == "async" && expected.starts_with("struct") { |
5869c6ff | 193 | "`async` blocks are only allowed in Rust 2018 or later".to_string() |
f9f354fc XL |
194 | } else { |
195 | format!("not found in {}", mod_str) | |
196 | }, | |
e74abb32 | 197 | item_span, |
dfeec247 XL |
198 | false, |
199 | ) | |
416331ca XL |
200 | }; |
201 | ||
dfeec247 | 202 | let code = source.error_code(res.is_some()); |
416331ca XL |
203 | let mut err = self.r.session.struct_span_err_with_code(base_span, &base_msg, code); |
204 | ||
1b1a35ee XL |
205 | match (source, self.diagnostic_metadata.in_if_condition) { |
206 | (PathSource::Expr(_), Some(Expr { span, kind: ExprKind::Assign(..), .. })) => { | |
207 | err.span_suggestion_verbose( | |
208 | span.shrink_to_lo(), | |
209 | "you might have meant to use pattern matching", | |
210 | "let ".to_string(), | |
211 | Applicability::MaybeIncorrect, | |
212 | ); | |
213 | self.r.session.if_let_suggestions.borrow_mut().insert(*span); | |
214 | } | |
215 | _ => {} | |
216 | } | |
217 | ||
3dfed10e | 218 | let is_assoc_fn = self.self_type_is_available(span); |
416331ca | 219 | // Emit help message for fake-self from other languages (e.g., `this` in Javascript). |
3dfed10e | 220 | if ["this", "my"].contains(&&*item_str.as_str()) && is_assoc_fn { |
f035d41b | 221 | err.span_suggestion_short( |
416331ca | 222 | span, |
f035d41b | 223 | "you might have meant to use `self` here instead", |
416331ca XL |
224 | "self".to_string(), |
225 | Applicability::MaybeIncorrect, | |
226 | ); | |
3dfed10e XL |
227 | if !self.self_value_is_available(path[0].ident.span, span) { |
228 | if let Some((FnKind::Fn(_, _, sig, ..), fn_span)) = | |
229 | &self.diagnostic_metadata.current_function | |
230 | { | |
231 | let (span, sugg) = if let Some(param) = sig.decl.inputs.get(0) { | |
232 | (param.span.shrink_to_lo(), "&self, ") | |
233 | } else { | |
234 | ( | |
235 | self.r | |
236 | .session | |
237 | .source_map() | |
238 | .span_through_char(*fn_span, '(') | |
239 | .shrink_to_hi(), | |
240 | "&self", | |
241 | ) | |
242 | }; | |
243 | err.span_suggestion_verbose( | |
244 | span, | |
245 | "if you meant to use `self`, you are also missing a `self` receiver \ | |
246 | argument", | |
247 | sugg.to_string(), | |
248 | Applicability::MaybeIncorrect, | |
249 | ); | |
250 | } | |
251 | } | |
416331ca XL |
252 | } |
253 | ||
254 | // Emit special messages for unresolved `Self` and `self`. | |
255 | if is_self_type(path, ns) { | |
dfeec247 | 256 | err.code(rustc_errors::error_code!(E0411)); |
e74abb32 XL |
257 | err.span_label( |
258 | span, | |
74b04a01 | 259 | "`Self` is only available in impls, traits, and type definitions".to_string(), |
e74abb32 | 260 | ); |
416331ca XL |
261 | return (err, Vec::new()); |
262 | } | |
263 | if is_self_value(path, ns) { | |
264 | debug!("smart_resolve_path_fragment: E0424, source={:?}", source); | |
265 | ||
dfeec247 | 266 | err.code(rustc_errors::error_code!(E0424)); |
416331ca | 267 | err.span_label(span, match source { |
74b04a01 XL |
268 | PathSource::Pat => "`self` value is a keyword and may not be bound to variables or shadowed" |
269 | .to_string(), | |
270 | _ => "`self` value is a keyword only available in methods with a `self` parameter" | |
271 | .to_string(), | |
416331ca | 272 | }); |
f9f354fc XL |
273 | if let Some((fn_kind, span)) = &self.diagnostic_metadata.current_function { |
274 | // The current function has a `self' parameter, but we were unable to resolve | |
275 | // a reference to `self`. This can only happen if the `self` identifier we | |
276 | // are resolving came from a different hygiene context. | |
5869c6ff | 277 | if fn_kind.decl().inputs.get(0).map_or(false, |p| p.is_self()) { |
f9f354fc XL |
278 | err.span_label(*span, "this function has a `self` parameter, but a macro invocation can only access identifiers it receives from parameters"); |
279 | } else { | |
3dfed10e XL |
280 | let doesnt = if is_assoc_fn { |
281 | let (span, sugg) = fn_kind | |
282 | .decl() | |
283 | .inputs | |
284 | .get(0) | |
285 | .map(|p| (p.span.shrink_to_lo(), "&self, ")) | |
286 | .unwrap_or_else(|| { | |
287 | ( | |
288 | self.r | |
289 | .session | |
290 | .source_map() | |
291 | .span_through_char(*span, '(') | |
292 | .shrink_to_hi(), | |
293 | "&self", | |
294 | ) | |
295 | }); | |
296 | err.span_suggestion_verbose( | |
297 | span, | |
298 | "add a `self` receiver parameter to make the associated `fn` a method", | |
299 | sugg.to_string(), | |
300 | Applicability::MaybeIncorrect, | |
301 | ); | |
302 | "doesn't" | |
303 | } else { | |
304 | "can't" | |
305 | }; | |
306 | if let Some(ident) = fn_kind.ident() { | |
307 | err.span_label( | |
308 | ident.span, | |
309 | &format!("this function {} have a `self` parameter", doesnt), | |
310 | ); | |
311 | } | |
f9f354fc | 312 | } |
e74abb32 | 313 | } |
416331ca XL |
314 | return (err, Vec::new()); |
315 | } | |
316 | ||
317 | // Try to lookup name in more relaxed fashion for better error reporting. | |
318 | let ident = path.last().unwrap().ident; | |
dfeec247 XL |
319 | let candidates = self |
320 | .r | |
f035d41b | 321 | .lookup_import_candidates(ident, ns, &self.parent_scope, is_expected) |
416331ca XL |
322 | .drain(..) |
323 | .filter(|ImportSuggestion { did, .. }| { | |
324 | match (did, res.and_then(|res| res.opt_def_id())) { | |
325 | (Some(suggestion_did), Some(actual_did)) => *suggestion_did != actual_did, | |
326 | _ => true, | |
327 | } | |
328 | }) | |
329 | .collect::<Vec<_>>(); | |
330 | let crate_def_id = DefId::local(CRATE_DEF_INDEX); | |
331 | if candidates.is_empty() && is_expected(Res::Def(DefKind::Enum, crate_def_id)) { | |
6a06907d XL |
332 | let mut enum_candidates: Vec<_> = self |
333 | .r | |
334 | .lookup_import_candidates(ident, ns, &self.parent_scope, is_enum_variant) | |
335 | .into_iter() | |
336 | .map(|suggestion| import_candidate_to_enum_paths(&suggestion)) | |
337 | .filter(|(_, enum_ty_path)| !enum_ty_path.starts_with("std::prelude::")) | |
338 | .collect(); | |
416331ca | 339 | if !enum_candidates.is_empty() { |
3dfed10e XL |
340 | if let (PathSource::Type, Some(span)) = |
341 | (source, self.diagnostic_metadata.current_type_ascription.last()) | |
342 | { | |
343 | if self | |
344 | .r | |
345 | .session | |
346 | .parse_sess | |
347 | .type_ascription_path_suggestions | |
348 | .borrow() | |
349 | .contains(span) | |
350 | { | |
351 | // Already reported this issue on the lhs of the type ascription. | |
352 | err.delay_as_bug(); | |
353 | return (err, candidates); | |
354 | } | |
355 | } | |
356 | ||
3dfed10e XL |
357 | enum_candidates.sort(); |
358 | ||
416331ca XL |
359 | // Contextualize for E0412 "cannot find type", but don't belabor the point |
360 | // (that it's a variant) for E0573 "expected type, found variant". | |
361 | let preamble = if res.is_none() { | |
362 | let others = match enum_candidates.len() { | |
363 | 1 => String::new(), | |
364 | 2 => " and 1 other".to_owned(), | |
dfeec247 | 365 | n => format!(" and {} others", n), |
416331ca | 366 | }; |
dfeec247 | 367 | format!("there is an enum variant `{}`{}; ", enum_candidates[0].0, others) |
416331ca XL |
368 | } else { |
369 | String::new() | |
370 | }; | |
371 | let msg = format!("{}try using the variant's enum", preamble); | |
372 | ||
373 | err.span_suggestions( | |
374 | span, | |
375 | &msg, | |
6a06907d | 376 | enum_candidates.into_iter().map(|(_variant_path, enum_ty_path)| enum_ty_path), |
416331ca XL |
377 | Applicability::MachineApplicable, |
378 | ); | |
379 | } | |
380 | } | |
381 | if path.len() == 1 && self.self_type_is_available(span) { | |
382 | if let Some(candidate) = self.lookup_assoc_candidate(ident, ns, is_expected) { | |
383 | let self_is_available = self.self_value_is_available(path[0].ident.span, span); | |
384 | match candidate { | |
385 | AssocSuggestion::Field => { | |
386 | if self_is_available { | |
387 | err.span_suggestion( | |
388 | span, | |
389 | "you might have meant to use the available field", | |
390 | format!("self.{}", path_str), | |
391 | Applicability::MachineApplicable, | |
392 | ); | |
393 | } else { | |
dfeec247 | 394 | err.span_label(span, "a field by this name exists in `Self`"); |
416331ca XL |
395 | } |
396 | } | |
397 | AssocSuggestion::MethodWithSelf if self_is_available => { | |
398 | err.span_suggestion( | |
399 | span, | |
29967ef6 | 400 | "you might have meant to call the method", |
416331ca XL |
401 | format!("self.{}", path_str), |
402 | Applicability::MachineApplicable, | |
403 | ); | |
404 | } | |
29967ef6 XL |
405 | AssocSuggestion::MethodWithSelf |
406 | | AssocSuggestion::AssocFn | |
407 | | AssocSuggestion::AssocConst | |
408 | | AssocSuggestion::AssocType => { | |
416331ca XL |
409 | err.span_suggestion( |
410 | span, | |
29967ef6 | 411 | &format!("you might have meant to {}", candidate.action()), |
416331ca XL |
412 | format!("Self::{}", path_str), |
413 | Applicability::MachineApplicable, | |
414 | ); | |
415 | } | |
416 | } | |
417 | return (err, candidates); | |
418 | } | |
dfeec247 XL |
419 | |
420 | // If the first argument in call is `self` suggest calling a method. | |
421 | if let Some((call_span, args_span)) = self.call_has_self_arg(source) { | |
422 | let mut args_snippet = String::new(); | |
423 | if let Some(args_span) = args_span { | |
424 | if let Ok(snippet) = self.r.session.source_map().span_to_snippet(args_span) { | |
425 | args_snippet = snippet; | |
426 | } | |
427 | } | |
428 | ||
429 | err.span_suggestion( | |
430 | call_span, | |
431 | &format!("try calling `{}` as a method", ident), | |
432 | format!("self.{}({})", path_str, args_snippet), | |
433 | Applicability::MachineApplicable, | |
434 | ); | |
435 | return (err, candidates); | |
436 | } | |
416331ca XL |
437 | } |
438 | ||
439 | // Try Levenshtein algorithm. | |
e74abb32 | 440 | let typo_sugg = self.lookup_typo_candidate(path, ns, is_expected, span); |
416331ca XL |
441 | // Try context-dependent help if relaxed lookup didn't work. |
442 | if let Some(res) = res { | |
e74abb32 XL |
443 | if self.smart_resolve_context_dependent_help( |
444 | &mut err, | |
445 | span, | |
446 | source, | |
447 | res, | |
448 | &path_str, | |
449 | &fallback_label, | |
450 | ) { | |
3dfed10e XL |
451 | // We do this to avoid losing a secondary span when we override the main error span. |
452 | self.r.add_typo_suggestion(&mut err, typo_sugg, ident_span); | |
416331ca XL |
453 | return (err, candidates); |
454 | } | |
455 | } | |
456 | ||
29967ef6 XL |
457 | if !self.type_ascription_suggestion(&mut err, base_span) { |
458 | let mut fallback = false; | |
459 | if let ( | |
460 | PathSource::Trait(AliasPossibility::Maybe), | |
461 | Some(Res::Def(DefKind::Struct | DefKind::Enum | DefKind::Union, _)), | |
462 | ) = (source, res) | |
463 | { | |
464 | if let Some(bounds @ [_, .., _]) = self.diagnostic_metadata.current_trait_object { | |
465 | fallback = true; | |
466 | let spans: Vec<Span> = bounds | |
467 | .iter() | |
468 | .map(|bound| bound.span()) | |
469 | .filter(|&sp| sp != base_span) | |
470 | .collect(); | |
471 | ||
472 | let start_span = bounds.iter().map(|bound| bound.span()).next().unwrap(); | |
473 | // `end_span` is the end of the poly trait ref (Foo + 'baz + Bar><) | |
474 | let end_span = bounds.iter().map(|bound| bound.span()).last().unwrap(); | |
475 | // `last_bound_span` is the last bound of the poly trait ref (Foo + >'baz< + Bar) | |
476 | let last_bound_span = spans.last().cloned().unwrap(); | |
477 | let mut multi_span: MultiSpan = spans.clone().into(); | |
478 | for sp in spans { | |
479 | let msg = if sp == last_bound_span { | |
480 | format!( | |
481 | "...because of {} bound{}", | |
482 | if bounds.len() <= 2 { "this" } else { "these" }, | |
483 | if bounds.len() <= 2 { "" } else { "s" }, | |
484 | ) | |
485 | } else { | |
486 | String::new() | |
487 | }; | |
488 | multi_span.push_span_label(sp, msg); | |
489 | } | |
490 | multi_span.push_span_label( | |
491 | base_span, | |
492 | "expected this type to be a trait...".to_string(), | |
e74abb32 | 493 | ); |
29967ef6 XL |
494 | err.span_help( |
495 | multi_span, | |
496 | "`+` is used to constrain a \"trait object\" type with lifetimes or \ | |
497 | auto-traits; structs and enums can't be bound in that way", | |
498 | ); | |
499 | if bounds.iter().all(|bound| match bound { | |
500 | ast::GenericBound::Outlives(_) => true, | |
501 | ast::GenericBound::Trait(tr, _) => tr.span == base_span, | |
502 | }) { | |
503 | let mut sugg = vec![]; | |
504 | if base_span != start_span { | |
505 | sugg.push((start_span.until(base_span), String::new())); | |
506 | } | |
507 | if base_span != end_span { | |
508 | sugg.push((base_span.shrink_to_hi().to(end_span), String::new())); | |
509 | } | |
510 | ||
511 | err.multipart_suggestion( | |
512 | "if you meant to use a type and not a trait here, remove the bounds", | |
513 | sugg, | |
514 | Applicability::MaybeIncorrect, | |
515 | ); | |
516 | } | |
e74abb32 | 517 | } |
29967ef6 XL |
518 | } |
519 | ||
520 | fallback |= self.restrict_assoc_type_in_where_clause(span, &mut err); | |
521 | ||
522 | if !self.r.add_typo_suggestion(&mut err, typo_sugg, ident_span) { | |
523 | fallback = true; | |
524 | match self.diagnostic_metadata.current_let_binding { | |
525 | Some((pat_sp, Some(ty_sp), None)) | |
526 | if ty_sp.contains(base_span) && could_be_expr => | |
527 | { | |
528 | err.span_suggestion_short( | |
529 | pat_sp.between(ty_sp), | |
530 | "use `=` if you meant to assign", | |
531 | " = ".to_string(), | |
532 | Applicability::MaybeIncorrect, | |
533 | ); | |
534 | } | |
535 | _ => {} | |
536 | } | |
537 | } | |
538 | if fallback { | |
539 | // Fallback label. | |
540 | err.span_label(base_span, fallback_label); | |
e74abb32 | 541 | } |
416331ca | 542 | } |
fc512014 XL |
543 | if let Some(err_code) = &err.code { |
544 | if err_code == &rustc_errors::error_code!(E0425) { | |
545 | for label_rib in &self.label_ribs { | |
5869c6ff | 546 | for (label_ident, node_id) in &label_rib.bindings { |
fc512014 | 547 | if format!("'{}", ident) == label_ident.to_string() { |
5869c6ff XL |
548 | err.span_label(label_ident.span, "a label with a similar name exists"); |
549 | if let PathSource::Expr(Some(Expr { | |
550 | kind: ExprKind::Break(None, Some(_)), | |
551 | .. | |
552 | })) = source | |
553 | { | |
554 | err.span_suggestion( | |
555 | span, | |
556 | "use the similarly named label", | |
557 | label_ident.name.to_string(), | |
558 | Applicability::MaybeIncorrect, | |
559 | ); | |
560 | // Do not lint against unused label when we suggest them. | |
561 | self.diagnostic_metadata.unused_labels.remove(node_id); | |
562 | } | |
fc512014 XL |
563 | } |
564 | } | |
565 | } | |
6a06907d XL |
566 | } else if err_code == &rustc_errors::error_code!(E0412) { |
567 | if let Some(correct) = Self::likely_rust_type(path) { | |
568 | err.span_suggestion( | |
569 | span, | |
570 | "perhaps you intended to use this type", | |
571 | correct.to_string(), | |
572 | Applicability::MaybeIncorrect, | |
573 | ); | |
574 | } | |
fc512014 XL |
575 | } |
576 | } | |
577 | ||
416331ca XL |
578 | (err, candidates) |
579 | } | |
580 | ||
29967ef6 XL |
581 | /// Given `where <T as Bar>::Baz: String`, suggest `where T: Bar<Baz = String>`. |
582 | fn restrict_assoc_type_in_where_clause( | |
583 | &mut self, | |
584 | span: Span, | |
585 | err: &mut DiagnosticBuilder<'_>, | |
586 | ) -> bool { | |
587 | // Detect that we are actually in a `where` predicate. | |
588 | let (bounded_ty, bounds, where_span) = | |
589 | if let Some(ast::WherePredicate::BoundPredicate(ast::WhereBoundPredicate { | |
590 | bounded_ty, | |
591 | bound_generic_params, | |
592 | bounds, | |
593 | span, | |
594 | })) = self.diagnostic_metadata.current_where_predicate | |
595 | { | |
596 | if !bound_generic_params.is_empty() { | |
597 | return false; | |
598 | } | |
599 | (bounded_ty, bounds, span) | |
600 | } else { | |
601 | return false; | |
602 | }; | |
603 | ||
604 | // Confirm that the target is an associated type. | |
605 | let (ty, position, path) = if let ast::TyKind::Path( | |
606 | Some(ast::QSelf { ty, position, .. }), | |
607 | path, | |
608 | ) = &bounded_ty.kind | |
609 | { | |
610 | // use this to verify that ident is a type param. | |
611 | let partial_res = if let Ok(Some(partial_res)) = self.resolve_qpath_anywhere( | |
612 | bounded_ty.id, | |
613 | None, | |
614 | &Segment::from_path(path), | |
615 | Namespace::TypeNS, | |
616 | span, | |
617 | true, | |
618 | CrateLint::No, | |
619 | ) { | |
620 | partial_res | |
621 | } else { | |
622 | return false; | |
623 | }; | |
624 | if !(matches!( | |
625 | partial_res.base_res(), | |
626 | hir::def::Res::Def(hir::def::DefKind::AssocTy, _) | |
627 | ) && partial_res.unresolved_segments() == 0) | |
628 | { | |
629 | return false; | |
630 | } | |
631 | (ty, position, path) | |
632 | } else { | |
633 | return false; | |
634 | }; | |
635 | ||
636 | if let ast::TyKind::Path(None, type_param_path) = &ty.peel_refs().kind { | |
637 | // Confirm that the `SelfTy` is a type parameter. | |
638 | let partial_res = if let Ok(Some(partial_res)) = self.resolve_qpath_anywhere( | |
639 | bounded_ty.id, | |
640 | None, | |
641 | &Segment::from_path(type_param_path), | |
642 | Namespace::TypeNS, | |
643 | span, | |
644 | true, | |
645 | CrateLint::No, | |
646 | ) { | |
647 | partial_res | |
648 | } else { | |
649 | return false; | |
650 | }; | |
651 | if !(matches!( | |
652 | partial_res.base_res(), | |
653 | hir::def::Res::Def(hir::def::DefKind::TyParam, _) | |
654 | ) && partial_res.unresolved_segments() == 0) | |
655 | { | |
656 | return false; | |
657 | } | |
658 | if let ( | |
659 | [ast::PathSegment { ident: constrain_ident, args: None, .. }], | |
660 | [ast::GenericBound::Trait(poly_trait_ref, ast::TraitBoundModifier::None)], | |
661 | ) = (&type_param_path.segments[..], &bounds[..]) | |
662 | { | |
663 | if let [ast::PathSegment { ident, args: None, .. }] = | |
664 | &poly_trait_ref.trait_ref.path.segments[..] | |
665 | { | |
666 | if ident.span == span { | |
667 | err.span_suggestion_verbose( | |
668 | *where_span, | |
669 | &format!("constrain the associated type to `{}`", ident), | |
670 | format!( | |
671 | "{}: {}<{} = {}>", | |
672 | self.r | |
673 | .session | |
674 | .source_map() | |
675 | .span_to_snippet(ty.span) // Account for `<&'a T as Foo>::Bar`. | |
676 | .unwrap_or_else(|_| constrain_ident.to_string()), | |
677 | path.segments[..*position] | |
678 | .iter() | |
679 | .map(|segment| path_segment_to_string(segment)) | |
680 | .collect::<Vec<_>>() | |
681 | .join("::"), | |
682 | path.segments[*position..] | |
683 | .iter() | |
684 | .map(|segment| path_segment_to_string(segment)) | |
685 | .collect::<Vec<_>>() | |
686 | .join("::"), | |
687 | ident, | |
688 | ), | |
689 | Applicability::MaybeIncorrect, | |
690 | ); | |
691 | } | |
692 | return true; | |
693 | } | |
694 | } | |
695 | } | |
696 | false | |
697 | } | |
698 | ||
dfeec247 XL |
699 | /// Check if the source is call expression and the first argument is `self`. If true, |
700 | /// return the span of whole call and the span for all arguments expect the first one (`self`). | |
701 | fn call_has_self_arg(&self, source: PathSource<'_>) -> Option<(Span, Option<Span>)> { | |
702 | let mut has_self_arg = None; | |
3dfed10e XL |
703 | if let PathSource::Expr(Some(parent)) = source { |
704 | match &parent.kind { | |
74b04a01 | 705 | ExprKind::Call(_, args) if !args.is_empty() => { |
dfeec247 XL |
706 | let mut expr_kind = &args[0].kind; |
707 | loop { | |
708 | match expr_kind { | |
709 | ExprKind::Path(_, arg_name) if arg_name.segments.len() == 1 => { | |
710 | if arg_name.segments[0].ident.name == kw::SelfLower { | |
3dfed10e | 711 | let call_span = parent.span; |
dfeec247 XL |
712 | let tail_args_span = if args.len() > 1 { |
713 | Some(Span::new( | |
714 | args[1].span.lo(), | |
715 | args.last().unwrap().span.hi(), | |
716 | call_span.ctxt(), | |
717 | )) | |
718 | } else { | |
719 | None | |
720 | }; | |
721 | has_self_arg = Some((call_span, tail_args_span)); | |
722 | } | |
723 | break; | |
724 | } | |
725 | ExprKind::AddrOf(_, _, expr) => expr_kind = &expr.kind, | |
726 | _ => break, | |
727 | } | |
728 | } | |
729 | } | |
730 | _ => (), | |
731 | } | |
732 | }; | |
ba9703b0 | 733 | has_self_arg |
dfeec247 XL |
734 | } |
735 | ||
f9f354fc | 736 | fn followed_by_brace(&self, span: Span) -> (bool, Option<Span>) { |
416331ca XL |
737 | // HACK(estebank): find a better way to figure out that this was a |
738 | // parser issue where a struct literal is being used on an expression | |
739 | // where a brace being opened means a block is being started. Look | |
740 | // ahead for the next text to see if `span` is followed by a `{`. | |
741 | let sm = self.r.session.source_map(); | |
742 | let mut sp = span; | |
743 | loop { | |
744 | sp = sm.next_point(sp); | |
745 | match sm.span_to_snippet(sp) { | |
746 | Ok(ref snippet) => { | |
dfeec247 | 747 | if snippet.chars().any(|c| !c.is_whitespace()) { |
416331ca XL |
748 | break; |
749 | } | |
750 | } | |
751 | _ => break, | |
752 | } | |
753 | } | |
29967ef6 | 754 | let followed_by_brace = matches!(sm.span_to_snippet(sp), Ok(ref snippet) if snippet == "{"); |
416331ca | 755 | // In case this could be a struct literal that needs to be surrounded |
f9f354fc | 756 | // by parentheses, find the appropriate span. |
416331ca XL |
757 | let mut i = 0; |
758 | let mut closing_brace = None; | |
759 | loop { | |
760 | sp = sm.next_point(sp); | |
761 | match sm.span_to_snippet(sp) { | |
762 | Ok(ref snippet) => { | |
763 | if snippet == "}" { | |
f9f354fc | 764 | closing_brace = Some(span.to(sp)); |
416331ca XL |
765 | break; |
766 | } | |
767 | } | |
768 | _ => break, | |
769 | } | |
770 | i += 1; | |
771 | // The bigger the span, the more likely we're incorrect -- | |
772 | // bound it to 100 chars long. | |
773 | if i > 100 { | |
774 | break; | |
775 | } | |
776 | } | |
ba9703b0 | 777 | (followed_by_brace, closing_brace) |
416331ca XL |
778 | } |
779 | ||
780 | /// Provides context-dependent help for errors reported by the `smart_resolve_path_fragment` | |
781 | /// function. | |
782 | /// Returns `true` if able to provide context-dependent help. | |
783 | fn smart_resolve_context_dependent_help( | |
784 | &mut self, | |
785 | err: &mut DiagnosticBuilder<'a>, | |
786 | span: Span, | |
787 | source: PathSource<'_>, | |
788 | res: Res, | |
789 | path_str: &str, | |
790 | fallback_label: &str, | |
791 | ) -> bool { | |
792 | let ns = source.namespace(); | |
793 | let is_expected = &|res| source.is_expected(res); | |
794 | ||
e74abb32 | 795 | let path_sep = |err: &mut DiagnosticBuilder<'_>, expr: &Expr| match expr.kind { |
416331ca XL |
796 | ExprKind::Field(_, ident) => { |
797 | err.span_suggestion( | |
798 | expr.span, | |
799 | "use the path separator to refer to an item", | |
800 | format!("{}::{}", path_str, ident), | |
801 | Applicability::MaybeIncorrect, | |
802 | ); | |
803 | true | |
804 | } | |
805 | ExprKind::MethodCall(ref segment, ..) => { | |
806 | let span = expr.span.with_hi(segment.ident.span.hi()); | |
807 | err.span_suggestion( | |
808 | span, | |
809 | "use the path separator to refer to an item", | |
810 | format!("{}::{}", path_str, segment.ident), | |
811 | Applicability::MaybeIncorrect, | |
812 | ); | |
813 | true | |
814 | } | |
815 | _ => false, | |
816 | }; | |
817 | ||
e74abb32 | 818 | let mut bad_struct_syntax_suggestion = |def_id: DefId| { |
416331ca | 819 | let (followed_by_brace, closing_brace) = self.followed_by_brace(span); |
3dfed10e | 820 | |
416331ca | 821 | match source { |
3dfed10e XL |
822 | PathSource::Expr(Some( |
823 | parent @ Expr { kind: ExprKind::Field(..) | ExprKind::MethodCall(..), .. }, | |
824 | )) if path_sep(err, &parent) => {} | |
825 | PathSource::Expr( | |
826 | None | |
827 | | Some(Expr { | |
828 | kind: | |
829 | ExprKind::Path(..) | |
830 | | ExprKind::Binary(..) | |
831 | | ExprKind::Unary(..) | |
832 | | ExprKind::If(..) | |
833 | | ExprKind::While(..) | |
834 | | ExprKind::ForLoop(..) | |
835 | | ExprKind::Match(..), | |
836 | .. | |
837 | }), | |
838 | ) if followed_by_brace => { | |
f9f354fc | 839 | if let Some(sp) = closing_brace { |
3dfed10e | 840 | err.span_label(span, fallback_label); |
f9f354fc XL |
841 | err.multipart_suggestion( |
842 | "surround the struct literal with parentheses", | |
843 | vec![ | |
844 | (sp.shrink_to_lo(), "(".to_string()), | |
845 | (sp.shrink_to_hi(), ")".to_string()), | |
846 | ], | |
416331ca XL |
847 | Applicability::MaybeIncorrect, |
848 | ); | |
849 | } else { | |
850 | err.span_label( | |
f9f354fc XL |
851 | span, // Note the parentheses surrounding the suggestion below |
852 | format!( | |
853 | "you might want to surround a struct literal with parentheses: \ | |
854 | `({} {{ /* fields */ }})`?", | |
855 | path_str | |
856 | ), | |
416331ca XL |
857 | ); |
858 | } | |
dfeec247 | 859 | } |
1b1a35ee | 860 | PathSource::Expr(_) | PathSource::TupleStruct(..) | PathSource::Pat => { |
3dfed10e XL |
861 | let span = match &source { |
862 | PathSource::Expr(Some(Expr { | |
863 | span, kind: ExprKind::Call(_, _), .. | |
864 | })) | |
1b1a35ee | 865 | | PathSource::TupleStruct(span, _) => { |
3dfed10e XL |
866 | // We want the main underline to cover the suggested code as well for |
867 | // cleaner output. | |
868 | err.set_span(*span); | |
869 | *span | |
870 | } | |
871 | _ => span, | |
872 | }; | |
873 | if let Some(span) = self.def_span(def_id) { | |
874 | err.span_label(span, &format!("`{}` defined here", path_str)); | |
875 | } | |
876 | let (tail, descr, applicability) = match source { | |
1b1a35ee | 877 | PathSource::Pat | PathSource::TupleStruct(..) => { |
3dfed10e XL |
878 | ("", "pattern", Applicability::MachineApplicable) |
879 | } | |
880 | _ => (": val", "literal", Applicability::HasPlaceholders), | |
881 | }; | |
882 | let (fields, applicability) = match self.r.field_names.get(&def_id) { | |
883 | Some(fields) => ( | |
884 | fields | |
885 | .iter() | |
886 | .map(|f| format!("{}{}", f.node, tail)) | |
887 | .collect::<Vec<String>>() | |
888 | .join(", "), | |
889 | applicability, | |
890 | ), | |
891 | None => ("/* fields */".to_string(), Applicability::HasPlaceholders), | |
892 | }; | |
893 | let pad = match self.r.field_names.get(&def_id) { | |
894 | Some(fields) if fields.is_empty() => "", | |
895 | _ => " ", | |
896 | }; | |
897 | err.span_suggestion( | |
898 | span, | |
899 | &format!("use struct {} syntax instead", descr), | |
29967ef6 | 900 | format!("{path_str} {{{pad}{fields}{pad}}}"), |
3dfed10e XL |
901 | applicability, |
902 | ); | |
903 | } | |
904 | _ => { | |
905 | err.span_label(span, fallback_label); | |
e74abb32 | 906 | } |
416331ca XL |
907 | } |
908 | }; | |
909 | ||
910 | match (res, source) { | |
911 | (Res::Def(DefKind::Macro(MacroKind::Bang), _), _) => { | |
3dfed10e | 912 | err.span_label(span, fallback_label); |
ba9703b0 XL |
913 | err.span_suggestion_verbose( |
914 | span.shrink_to_hi(), | |
416331ca | 915 | "use `!` to invoke the macro", |
ba9703b0 | 916 | "!".to_string(), |
416331ca XL |
917 | Applicability::MaybeIncorrect, |
918 | ); | |
919 | if path_str == "try" && span.rust_2015() { | |
5869c6ff | 920 | err.note("if you want the `try` keyword, you need Rust 2018 or later"); |
416331ca XL |
921 | } |
922 | } | |
f9f354fc | 923 | (Res::Def(DefKind::TyAlias, def_id), PathSource::Trait(_)) => { |
416331ca | 924 | err.span_label(span, "type aliases cannot be used as traits"); |
fc512014 | 925 | if self.r.session.is_nightly_build() { |
f9f354fc XL |
926 | let msg = "you might have meant to use `#![feature(trait_alias)]` instead of a \ |
927 | `type` alias"; | |
3dfed10e | 928 | if let Some(span) = self.def_span(def_id) { |
f9f354fc XL |
929 | err.span_help(span, msg); |
930 | } else { | |
931 | err.help(msg); | |
932 | } | |
416331ca XL |
933 | } |
934 | } | |
935 | (Res::Def(DefKind::Mod, _), PathSource::Expr(Some(parent))) => { | |
936 | if !path_sep(err, &parent) { | |
937 | return false; | |
938 | } | |
939 | } | |
3dfed10e XL |
940 | ( |
941 | Res::Def(DefKind::Enum, def_id), | |
1b1a35ee | 942 | PathSource::TupleStruct(..) | PathSource::Expr(..), |
3dfed10e XL |
943 | ) => { |
944 | if self | |
945 | .diagnostic_metadata | |
946 | .current_type_ascription | |
947 | .last() | |
948 | .map(|sp| { | |
949 | self.r | |
950 | .session | |
951 | .parse_sess | |
952 | .type_ascription_path_suggestions | |
953 | .borrow() | |
954 | .contains(&sp) | |
955 | }) | |
956 | .unwrap_or(false) | |
957 | { | |
958 | err.delay_as_bug(); | |
959 | // We already suggested changing `:` into `::` during parsing. | |
960 | return false; | |
961 | } | |
416331ca | 962 | |
29967ef6 | 963 | self.suggest_using_enum_variant(err, source, def_id, span); |
e1599b0c | 964 | } |
416331ca | 965 | (Res::Def(DefKind::Struct, def_id), _) if ns == ValueNS => { |
29967ef6 XL |
966 | let (ctor_def, ctor_vis, fields) = |
967 | if let Some(struct_ctor) = self.r.struct_constructors.get(&def_id).cloned() { | |
968 | struct_ctor | |
969 | } else { | |
970 | bad_struct_syntax_suggestion(def_id); | |
971 | return true; | |
972 | }; | |
1b1a35ee | 973 | |
29967ef6 XL |
974 | let is_accessible = self.r.is_accessible_from(ctor_vis, self.parent_scope.module); |
975 | if !is_expected(ctor_def) || is_accessible { | |
976 | return true; | |
977 | } | |
978 | ||
979 | let field_spans = match source { | |
980 | // e.g. `if let Enum::TupleVariant(field1, field2) = _` | |
981 | PathSource::TupleStruct(_, pattern_spans) => { | |
982 | err.set_primary_message( | |
983 | "cannot match against a tuple struct which contains private fields", | |
984 | ); | |
985 | ||
986 | // Use spans of the tuple struct pattern. | |
987 | Some(Vec::from(pattern_spans)) | |
416331ca | 988 | } |
29967ef6 XL |
989 | // e.g. `let _ = Enum::TupleVariant(field1, field2);` |
990 | _ if source.is_call() => { | |
991 | err.set_primary_message( | |
992 | "cannot initialize a tuple struct which contains private fields", | |
993 | ); | |
994 | ||
995 | // Use spans of the tuple struct definition. | |
996 | self.r | |
997 | .field_names | |
998 | .get(&def_id) | |
999 | .map(|fields| fields.iter().map(|f| f.span).collect::<Vec<_>>()) | |
1000 | } | |
1001 | _ => None, | |
1002 | }; | |
1003 | ||
1004 | if let Some(spans) = | |
1005 | field_spans.filter(|spans| spans.len() > 0 && fields.len() == spans.len()) | |
1006 | { | |
1007 | let non_visible_spans: Vec<Span> = fields | |
1008 | .iter() | |
1009 | .zip(spans.iter()) | |
1010 | .filter(|(vis, _)| { | |
1011 | !self.r.is_accessible_from(**vis, self.parent_scope.module) | |
1012 | }) | |
1013 | .map(|(_, span)| *span) | |
1014 | .collect(); | |
1015 | ||
1016 | if non_visible_spans.len() > 0 { | |
1017 | let mut m: rustc_span::MultiSpan = non_visible_spans.clone().into(); | |
1018 | non_visible_spans | |
1019 | .into_iter() | |
1020 | .for_each(|s| m.push_span_label(s, "private field".to_string())); | |
1021 | err.span_note(m, "constructor is not visible here due to private fields"); | |
1022 | } | |
1023 | ||
1024 | return true; | |
416331ca | 1025 | } |
29967ef6 XL |
1026 | |
1027 | err.span_label( | |
1028 | span, | |
1029 | "constructor is not visible here due to private fields".to_string(), | |
1030 | ); | |
416331ca | 1031 | } |
ba9703b0 XL |
1032 | ( |
1033 | Res::Def( | |
1034 | DefKind::Union | DefKind::Variant | DefKind::Ctor(_, CtorKind::Fictive), | |
1035 | def_id, | |
1036 | ), | |
1037 | _, | |
1038 | ) if ns == ValueNS => { | |
e74abb32 | 1039 | bad_struct_syntax_suggestion(def_id); |
416331ca | 1040 | } |
e74abb32 | 1041 | (Res::Def(DefKind::Ctor(_, CtorKind::Fn), def_id), _) if ns == ValueNS => { |
3dfed10e | 1042 | if let Some(span) = self.def_span(def_id) { |
e74abb32 XL |
1043 | err.span_label(span, &format!("`{}` defined here", path_str)); |
1044 | } | |
3dfed10e XL |
1045 | let fields = |
1046 | self.r.field_names.get(&def_id).map_or("/* fields */".to_string(), |fields| { | |
1047 | vec!["_"; fields.len()].join(", ") | |
1048 | }); | |
1049 | err.span_suggestion( | |
1050 | span, | |
1051 | "use the tuple variant pattern syntax instead", | |
1052 | format!("{}({})", path_str, fields), | |
1053 | Applicability::HasPlaceholders, | |
1054 | ); | |
e1599b0c | 1055 | } |
416331ca XL |
1056 | (Res::SelfTy(..), _) if ns == ValueNS => { |
1057 | err.span_label(span, fallback_label); | |
1058 | err.note("can't use `Self` as a constructor, you must use the implemented struct"); | |
1059 | } | |
ba9703b0 | 1060 | (Res::Def(DefKind::TyAlias | DefKind::AssocTy, _), _) if ns == ValueNS => { |
416331ca XL |
1061 | err.note("can't use a type alias as a constructor"); |
1062 | } | |
1063 | _ => return false, | |
1064 | } | |
1065 | true | |
1066 | } | |
1067 | ||
dfeec247 XL |
1068 | fn lookup_assoc_candidate<FilterFn>( |
1069 | &mut self, | |
1070 | ident: Ident, | |
1071 | ns: Namespace, | |
1072 | filter_fn: FilterFn, | |
1073 | ) -> Option<AssocSuggestion> | |
1074 | where | |
1075 | FilterFn: Fn(Res) -> bool, | |
416331ca XL |
1076 | { |
1077 | fn extract_node_id(t: &Ty) -> Option<NodeId> { | |
e74abb32 | 1078 | match t.kind { |
416331ca XL |
1079 | TyKind::Path(None, _) => Some(t.id), |
1080 | TyKind::Rptr(_, ref mut_ty) => extract_node_id(&mut_ty.ty), | |
1081 | // This doesn't handle the remaining `Ty` variants as they are not | |
1082 | // that commonly the self_type, it might be interesting to provide | |
1083 | // support for those in future. | |
1084 | _ => None, | |
1085 | } | |
1086 | } | |
1087 | ||
1088 | // Fields are generally expected in the same contexts as locals. | |
1089 | if filter_fn(Res::Local(ast::DUMMY_NODE_ID)) { | |
dfeec247 XL |
1090 | if let Some(node_id) = |
1091 | self.diagnostic_metadata.current_self_type.as_ref().and_then(extract_node_id) | |
e74abb32 | 1092 | { |
416331ca XL |
1093 | // Look for a field with the same name in the current self_type. |
1094 | if let Some(resolution) = self.r.partial_res_map.get(&node_id) { | |
1095 | match resolution.base_res() { | |
ba9703b0 | 1096 | Res::Def(DefKind::Struct | DefKind::Union, did) |
dfeec247 XL |
1097 | if resolution.unresolved_segments() == 0 => |
1098 | { | |
416331ca | 1099 | if let Some(field_names) = self.r.field_names.get(&did) { |
dfeec247 XL |
1100 | if field_names |
1101 | .iter() | |
1102 | .any(|&field_name| ident.name == field_name.node) | |
1103 | { | |
416331ca XL |
1104 | return Some(AssocSuggestion::Field); |
1105 | } | |
1106 | } | |
1107 | } | |
1108 | _ => {} | |
1109 | } | |
1110 | } | |
1111 | } | |
1112 | } | |
1113 | ||
29967ef6 | 1114 | if let Some(items) = self.diagnostic_metadata.current_trait_assoc_items { |
6a06907d | 1115 | for assoc_item in items { |
29967ef6 XL |
1116 | if assoc_item.ident == ident { |
1117 | return Some(match &assoc_item.kind { | |
1118 | ast::AssocItemKind::Const(..) => AssocSuggestion::AssocConst, | |
5869c6ff XL |
1119 | ast::AssocItemKind::Fn(box ast::FnKind(_, sig, ..)) |
1120 | if sig.decl.has_self() => | |
1121 | { | |
29967ef6 XL |
1122 | AssocSuggestion::MethodWithSelf |
1123 | } | |
1124 | ast::AssocItemKind::Fn(..) => AssocSuggestion::AssocFn, | |
1125 | ast::AssocItemKind::TyAlias(..) => AssocSuggestion::AssocType, | |
1126 | ast::AssocItemKind::MacCall(_) => continue, | |
1127 | }); | |
1128 | } | |
416331ca XL |
1129 | } |
1130 | } | |
1131 | ||
1132 | // Look for associated items in the current trait. | |
1133 | if let Some((module, _)) = self.current_trait_ref { | |
1134 | if let Ok(binding) = self.r.resolve_ident_in_module( | |
dfeec247 XL |
1135 | ModuleOrUniformRoot::Module(module), |
1136 | ident, | |
1137 | ns, | |
1138 | &self.parent_scope, | |
1139 | false, | |
1140 | module.span, | |
1141 | ) { | |
416331ca XL |
1142 | let res = binding.res(); |
1143 | if filter_fn(res) { | |
29967ef6 XL |
1144 | if self.r.has_self.contains(&res.def_id()) { |
1145 | return Some(AssocSuggestion::MethodWithSelf); | |
416331ca | 1146 | } else { |
29967ef6 XL |
1147 | match res { |
1148 | Res::Def(DefKind::AssocFn, _) => return Some(AssocSuggestion::AssocFn), | |
1149 | Res::Def(DefKind::AssocConst, _) => { | |
1150 | return Some(AssocSuggestion::AssocConst); | |
1151 | } | |
1152 | Res::Def(DefKind::AssocTy, _) => { | |
1153 | return Some(AssocSuggestion::AssocType); | |
1154 | } | |
1155 | _ => {} | |
1156 | } | |
1157 | } | |
416331ca XL |
1158 | } |
1159 | } | |
1160 | } | |
1161 | ||
1162 | None | |
1163 | } | |
1164 | ||
1165 | fn lookup_typo_candidate( | |
1166 | &mut self, | |
1167 | path: &[Segment], | |
1168 | ns: Namespace, | |
1169 | filter_fn: &impl Fn(Res) -> bool, | |
1170 | span: Span, | |
1171 | ) -> Option<TypoSuggestion> { | |
1172 | let mut names = Vec::new(); | |
1173 | if path.len() == 1 { | |
1174 | // Search in lexical scope. | |
1175 | // Walk backwards up the ribs in scope and collect candidates. | |
1176 | for rib in self.ribs[ns].iter().rev() { | |
1177 | // Locals and type parameters | |
1178 | for (ident, &res) in &rib.bindings { | |
1179 | if filter_fn(res) { | |
1180 | names.push(TypoSuggestion::from_res(ident.name, res)); | |
1181 | } | |
1182 | } | |
1183 | // Items in scope | |
1184 | if let RibKind::ModuleRibKind(module) = rib.kind { | |
1185 | // Items from this module | |
e1599b0c | 1186 | self.r.add_module_candidates(module, &mut names, &filter_fn); |
416331ca XL |
1187 | |
1188 | if let ModuleKind::Block(..) = module.kind { | |
1189 | // We can see through blocks | |
1190 | } else { | |
1191 | // Items from the prelude | |
1192 | if !module.no_implicit_prelude { | |
1193 | let extern_prelude = self.r.extern_prelude.clone(); | |
1194 | names.extend(extern_prelude.iter().flat_map(|(ident, _)| { | |
3dfed10e XL |
1195 | self.r.crate_loader.maybe_process_path_extern(ident.name).and_then( |
1196 | |crate_id| { | |
416331ca XL |
1197 | let crate_mod = Res::Def( |
1198 | DefKind::Mod, | |
dfeec247 | 1199 | DefId { krate: crate_id, index: CRATE_DEF_INDEX }, |
416331ca XL |
1200 | ); |
1201 | ||
1202 | if filter_fn(crate_mod) { | |
1203 | Some(TypoSuggestion::from_res(ident.name, crate_mod)) | |
1204 | } else { | |
1205 | None | |
1206 | } | |
3dfed10e XL |
1207 | }, |
1208 | ) | |
416331ca XL |
1209 | })); |
1210 | ||
1211 | if let Some(prelude) = self.r.prelude { | |
e1599b0c | 1212 | self.r.add_module_candidates(prelude, &mut names, &filter_fn); |
416331ca XL |
1213 | } |
1214 | } | |
1215 | break; | |
1216 | } | |
1217 | } | |
1218 | } | |
1219 | // Add primitive types to the mix | |
1220 | if filter_fn(Res::PrimTy(PrimTy::Bool)) { | |
1221 | names.extend( | |
6a06907d XL |
1222 | PrimTy::ALL.iter().map(|prim_ty| { |
1223 | TypoSuggestion::from_res(prim_ty.name(), Res::PrimTy(*prim_ty)) | |
dfeec247 | 1224 | }), |
416331ca XL |
1225 | ) |
1226 | } | |
1227 | } else { | |
1228 | // Search in module. | |
1229 | let mod_path = &path[..path.len() - 1]; | |
dfeec247 XL |
1230 | if let PathResult::Module(module) = |
1231 | self.resolve_path(mod_path, Some(TypeNS), false, span, CrateLint::No) | |
1232 | { | |
416331ca | 1233 | if let ModuleOrUniformRoot::Module(module) = module { |
e1599b0c | 1234 | self.r.add_module_candidates(module, &mut names, &filter_fn); |
416331ca XL |
1235 | } |
1236 | } | |
1237 | } | |
1238 | ||
1239 | let name = path[path.len() - 1].ident.name; | |
1240 | // Make sure error reporting is deterministic. | |
1241 | names.sort_by_cached_key(|suggestion| suggestion.candidate.as_str()); | |
1242 | ||
1243 | match find_best_match_for_name( | |
fc512014 | 1244 | &names.iter().map(|suggestion| suggestion.candidate).collect::<Vec<Symbol>>(), |
3dfed10e | 1245 | name, |
416331ca XL |
1246 | None, |
1247 | ) { | |
dfeec247 XL |
1248 | Some(found) if found != name => { |
1249 | names.into_iter().find(|suggestion| suggestion.candidate == found) | |
1250 | } | |
416331ca XL |
1251 | _ => None, |
1252 | } | |
1253 | } | |
1254 | ||
6a06907d XL |
1255 | // Returns the name of the Rust type approximately corresponding to |
1256 | // a type name in another programming language. | |
1257 | fn likely_rust_type(path: &[Segment]) -> Option<Symbol> { | |
1258 | let name = path[path.len() - 1].ident.as_str(); | |
1259 | // Common Java types | |
1260 | Some(match &*name { | |
1261 | "byte" => sym::u8, // In Java, bytes are signed, but in practice one almost always wants unsigned bytes. | |
1262 | "short" => sym::i16, | |
1263 | "boolean" => sym::bool, | |
1264 | "int" => sym::i32, | |
1265 | "long" => sym::i64, | |
1266 | "float" => sym::f32, | |
1267 | "double" => sym::f64, | |
1268 | _ => return None, | |
1269 | }) | |
1270 | } | |
1271 | ||
416331ca XL |
1272 | /// Only used in a specific case of type ascription suggestions |
1273 | fn get_colon_suggestion_span(&self, start: Span) -> Span { | |
74b04a01 XL |
1274 | let sm = self.r.session.source_map(); |
1275 | start.to(sm.next_point(start)) | |
416331ca XL |
1276 | } |
1277 | ||
3dfed10e | 1278 | fn type_ascription_suggestion(&self, err: &mut DiagnosticBuilder<'_>, base_span: Span) -> bool { |
74b04a01 XL |
1279 | let sm = self.r.session.source_map(); |
1280 | let base_snippet = sm.span_to_snippet(base_span); | |
3dfed10e XL |
1281 | if let Some(&sp) = self.diagnostic_metadata.current_type_ascription.last() { |
1282 | if let Ok(snippet) = sm.span_to_snippet(sp) { | |
1283 | let len = snippet.trim_end().len() as u32; | |
1284 | if snippet.trim() == ":" { | |
1285 | let colon_sp = | |
1286 | sp.with_lo(sp.lo() + BytePos(len - 1)).with_hi(sp.lo() + BytePos(len)); | |
1287 | let mut show_label = true; | |
1288 | if sm.is_multiline(sp) { | |
1289 | err.span_suggestion_short( | |
1290 | colon_sp, | |
1291 | "maybe you meant to write `;` here", | |
1292 | ";".to_string(), | |
1293 | Applicability::MaybeIncorrect, | |
1294 | ); | |
1295 | } else { | |
1296 | let after_colon_sp = | |
1297 | self.get_colon_suggestion_span(colon_sp.shrink_to_hi()); | |
1298 | if snippet.len() == 1 { | |
1299 | // `foo:bar` | |
1300 | err.span_suggestion( | |
1301 | colon_sp, | |
1302 | "maybe you meant to write a path separator here", | |
1303 | "::".to_string(), | |
416331ca XL |
1304 | Applicability::MaybeIncorrect, |
1305 | ); | |
3dfed10e XL |
1306 | show_label = false; |
1307 | if !self | |
1308 | .r | |
1309 | .session | |
1310 | .parse_sess | |
1311 | .type_ascription_path_suggestions | |
1312 | .borrow_mut() | |
1313 | .insert(colon_sp) | |
416331ca | 1314 | { |
3dfed10e | 1315 | err.delay_as_bug(); |
416331ca | 1316 | } |
3dfed10e XL |
1317 | } |
1318 | if let Ok(base_snippet) = base_snippet { | |
1319 | let mut sp = after_colon_sp; | |
1320 | for _ in 0..100 { | |
1321 | // Try to find an assignment | |
1322 | sp = sm.next_point(sp); | |
1323 | let snippet = sm.span_to_snippet(sp.to(sm.next_point(sp))); | |
1324 | match snippet { | |
1325 | Ok(ref x) if x.as_str() == "=" => { | |
1326 | err.span_suggestion( | |
1327 | base_span, | |
1328 | "maybe you meant to write an assignment here", | |
1329 | format!("let {}", base_snippet), | |
1330 | Applicability::MaybeIncorrect, | |
1331 | ); | |
1332 | show_label = false; | |
1333 | break; | |
416331ca | 1334 | } |
3dfed10e XL |
1335 | Ok(ref x) if x.as_str() == "\n" => break, |
1336 | Err(_) => break, | |
1337 | Ok(_) => {} | |
416331ca XL |
1338 | } |
1339 | } | |
1340 | } | |
416331ca | 1341 | } |
3dfed10e XL |
1342 | if show_label { |
1343 | err.span_label( | |
1344 | base_span, | |
1345 | "expecting a type here because of type ascription", | |
1346 | ); | |
1347 | } | |
1348 | return show_label; | |
416331ca XL |
1349 | } |
1350 | } | |
1351 | } | |
3dfed10e | 1352 | false |
416331ca XL |
1353 | } |
1354 | ||
1355 | fn find_module(&mut self, def_id: DefId) -> Option<(Module<'a>, ImportSuggestion)> { | |
1356 | let mut result = None; | |
1357 | let mut seen_modules = FxHashSet::default(); | |
1358 | let mut worklist = vec![(self.r.graph_root, Vec::new())]; | |
1359 | ||
1360 | while let Some((in_module, path_segments)) = worklist.pop() { | |
1361 | // abort if the module is already found | |
dfeec247 XL |
1362 | if result.is_some() { |
1363 | break; | |
1364 | } | |
416331ca | 1365 | |
e74abb32 | 1366 | in_module.for_each_child(self.r, |_, ident, _, name_binding| { |
416331ca XL |
1367 | // abort if the module is already found or if name_binding is private external |
1368 | if result.is_some() || !name_binding.vis.is_visible_locally() { | |
dfeec247 | 1369 | return; |
416331ca XL |
1370 | } |
1371 | if let Some(module) = name_binding.module() { | |
1372 | // form the path | |
1373 | let mut path_segments = path_segments.clone(); | |
1374 | path_segments.push(ast::PathSegment::from_ident(ident)); | |
1375 | let module_def_id = module.def_id().unwrap(); | |
1376 | if module_def_id == def_id { | |
1b1a35ee XL |
1377 | let path = |
1378 | Path { span: name_binding.span, segments: path_segments, tokens: None }; | |
f9f354fc XL |
1379 | result = Some(( |
1380 | module, | |
f035d41b XL |
1381 | ImportSuggestion { |
1382 | did: Some(def_id), | |
1383 | descr: "module", | |
1384 | path, | |
1385 | accessible: true, | |
1386 | }, | |
f9f354fc | 1387 | )); |
416331ca XL |
1388 | } else { |
1389 | // add the module to the lookup | |
1390 | if seen_modules.insert(module_def_id) { | |
1391 | worklist.push((module, path_segments)); | |
1392 | } | |
1393 | } | |
1394 | } | |
1395 | }); | |
1396 | } | |
1397 | ||
1398 | result | |
1399 | } | |
1400 | ||
29967ef6 | 1401 | fn collect_enum_ctors(&mut self, def_id: DefId) -> Option<Vec<(Path, DefId, CtorKind)>> { |
416331ca | 1402 | self.find_module(def_id).map(|(enum_module, enum_import_suggestion)| { |
416331ca | 1403 | let mut variants = Vec::new(); |
e74abb32 | 1404 | enum_module.for_each_child(self.r, |_, ident, _, name_binding| { |
29967ef6 | 1405 | if let Res::Def(DefKind::Ctor(CtorOf::Variant, kind), def_id) = name_binding.res() { |
416331ca XL |
1406 | let mut segms = enum_import_suggestion.path.segments.clone(); |
1407 | segms.push(ast::PathSegment::from_ident(ident)); | |
29967ef6 XL |
1408 | let path = Path { span: name_binding.span, segments: segms, tokens: None }; |
1409 | variants.push((path, def_id, kind)); | |
416331ca XL |
1410 | } |
1411 | }); | |
1412 | variants | |
1413 | }) | |
1414 | } | |
dfeec247 | 1415 | |
29967ef6 XL |
1416 | /// Adds a suggestion for using an enum's variant when an enum is used instead. |
1417 | fn suggest_using_enum_variant( | |
1418 | &mut self, | |
1419 | err: &mut DiagnosticBuilder<'a>, | |
1420 | source: PathSource<'_>, | |
1421 | def_id: DefId, | |
1422 | span: Span, | |
1423 | ) { | |
1424 | let variants = match self.collect_enum_ctors(def_id) { | |
1425 | Some(variants) => variants, | |
1426 | None => { | |
1427 | err.note("you might have meant to use one of the enum's variants"); | |
1428 | return; | |
1429 | } | |
1430 | }; | |
1431 | ||
1432 | let suggest_only_tuple_variants = | |
1433 | matches!(source, PathSource::TupleStruct(..)) || source.is_call(); | |
1434 | if suggest_only_tuple_variants { | |
1435 | // Suggest only tuple variants regardless of whether they have fields and do not | |
1436 | // suggest path with added parenthesis. | |
1437 | let mut suggestable_variants = variants | |
1438 | .iter() | |
1439 | .filter(|(.., kind)| *kind == CtorKind::Fn) | |
1440 | .map(|(variant, ..)| path_names_to_string(variant)) | |
1441 | .collect::<Vec<_>>(); | |
1442 | ||
1443 | let non_suggestable_variant_count = variants.len() - suggestable_variants.len(); | |
1444 | ||
1445 | let source_msg = if source.is_call() { | |
1446 | "to construct" | |
1447 | } else if matches!(source, PathSource::TupleStruct(..)) { | |
1448 | "to match against" | |
1449 | } else { | |
1450 | unreachable!() | |
1451 | }; | |
1452 | ||
1453 | if !suggestable_variants.is_empty() { | |
1454 | let msg = if non_suggestable_variant_count == 0 && suggestable_variants.len() == 1 { | |
1455 | format!("try {} the enum's variant", source_msg) | |
1456 | } else { | |
1457 | format!("try {} one of the enum's variants", source_msg) | |
1458 | }; | |
1459 | ||
1460 | err.span_suggestions( | |
1461 | span, | |
1462 | &msg, | |
1463 | suggestable_variants.drain(..), | |
1464 | Applicability::MaybeIncorrect, | |
1465 | ); | |
1466 | } | |
1467 | ||
1468 | // If the enum has no tuple variants.. | |
1469 | if non_suggestable_variant_count == variants.len() { | |
1470 | err.help(&format!("the enum has no tuple variants {}", source_msg)); | |
1471 | } | |
1472 | ||
1473 | // If there are also non-tuple variants.. | |
1474 | if non_suggestable_variant_count == 1 { | |
1475 | err.help(&format!( | |
1476 | "you might have meant {} the enum's non-tuple variant", | |
1477 | source_msg | |
1478 | )); | |
1479 | } else if non_suggestable_variant_count >= 1 { | |
1480 | err.help(&format!( | |
1481 | "you might have meant {} one of the enum's non-tuple variants", | |
1482 | source_msg | |
1483 | )); | |
1484 | } | |
1485 | } else { | |
1486 | let needs_placeholder = |def_id: DefId, kind: CtorKind| { | |
5869c6ff | 1487 | let has_no_fields = self.r.field_names.get(&def_id).map_or(false, |f| f.is_empty()); |
29967ef6 XL |
1488 | match kind { |
1489 | CtorKind::Const => false, | |
1490 | CtorKind::Fn | CtorKind::Fictive if has_no_fields => false, | |
1491 | _ => true, | |
1492 | } | |
1493 | }; | |
1494 | ||
1495 | let mut suggestable_variants = variants | |
1496 | .iter() | |
1497 | .filter(|(_, def_id, kind)| !needs_placeholder(*def_id, *kind)) | |
1498 | .map(|(variant, _, kind)| (path_names_to_string(variant), kind)) | |
1499 | .map(|(variant, kind)| match kind { | |
1500 | CtorKind::Const => variant, | |
1501 | CtorKind::Fn => format!("({}())", variant), | |
1502 | CtorKind::Fictive => format!("({} {{}})", variant), | |
1503 | }) | |
1504 | .collect::<Vec<_>>(); | |
1505 | ||
1506 | if !suggestable_variants.is_empty() { | |
1507 | let msg = if suggestable_variants.len() == 1 { | |
1508 | "you might have meant to use the following enum variant" | |
1509 | } else { | |
1510 | "you might have meant to use one of the following enum variants" | |
1511 | }; | |
1512 | ||
1513 | err.span_suggestions( | |
1514 | span, | |
1515 | msg, | |
1516 | suggestable_variants.drain(..), | |
1517 | Applicability::MaybeIncorrect, | |
1518 | ); | |
1519 | } | |
1520 | ||
1521 | let mut suggestable_variants_with_placeholders = variants | |
1522 | .iter() | |
1523 | .filter(|(_, def_id, kind)| needs_placeholder(*def_id, *kind)) | |
1524 | .map(|(variant, _, kind)| (path_names_to_string(variant), kind)) | |
1525 | .filter_map(|(variant, kind)| match kind { | |
1526 | CtorKind::Fn => Some(format!("({}(/* fields */))", variant)), | |
1527 | CtorKind::Fictive => Some(format!("({} {{ /* fields */ }})", variant)), | |
1528 | _ => None, | |
1529 | }) | |
1530 | .collect::<Vec<_>>(); | |
1531 | ||
1532 | if !suggestable_variants_with_placeholders.is_empty() { | |
1533 | let msg = match ( | |
1534 | suggestable_variants.is_empty(), | |
1535 | suggestable_variants_with_placeholders.len(), | |
1536 | ) { | |
1537 | (true, 1) => "the following enum variant is available", | |
1538 | (true, _) => "the following enum variants are available", | |
1539 | (false, 1) => "alternatively, the following enum variant is available", | |
1540 | (false, _) => "alternatively, the following enum variants are also available", | |
1541 | }; | |
1542 | ||
1543 | err.span_suggestions( | |
1544 | span, | |
1545 | msg, | |
1546 | suggestable_variants_with_placeholders.drain(..), | |
1547 | Applicability::HasPlaceholders, | |
1548 | ); | |
1549 | } | |
1550 | }; | |
1551 | ||
1552 | if def_id.is_local() { | |
1553 | if let Some(span) = self.def_span(def_id) { | |
1554 | err.span_note(span, "the enum is defined here"); | |
1555 | } | |
1556 | } | |
1557 | } | |
1558 | ||
dfeec247 XL |
1559 | crate fn report_missing_type_error( |
1560 | &self, | |
1561 | path: &[Segment], | |
1562 | ) -> Option<(Span, &'static str, String, Applicability)> { | |
f035d41b XL |
1563 | let (ident, span) = match path { |
1564 | [segment] if !segment.has_generic_args => { | |
1565 | (segment.ident.to_string(), segment.ident.span) | |
1566 | } | |
dfeec247 XL |
1567 | _ => return None, |
1568 | }; | |
f035d41b XL |
1569 | let mut iter = ident.chars().map(|c| c.is_uppercase()); |
1570 | let single_uppercase_char = | |
1571 | matches!(iter.next(), Some(true)) && matches!(iter.next(), None); | |
1572 | if !self.diagnostic_metadata.currently_processing_generics && !single_uppercase_char { | |
1573 | return None; | |
1574 | } | |
1575 | match (self.diagnostic_metadata.current_item, single_uppercase_char) { | |
1576 | (Some(Item { kind: ItemKind::Fn(..), ident, .. }), _) if ident.name == sym::main => { | |
dfeec247 XL |
1577 | // Ignore `fn main()` as we don't want to suggest `fn main<T>()` |
1578 | } | |
f035d41b XL |
1579 | ( |
1580 | Some(Item { | |
1581 | kind: | |
1582 | kind @ ItemKind::Fn(..) | |
1583 | | kind @ ItemKind::Enum(..) | |
1584 | | kind @ ItemKind::Struct(..) | |
1585 | | kind @ ItemKind::Union(..), | |
1586 | .. | |
1587 | }), | |
1588 | true, | |
1589 | ) | |
1590 | | (Some(Item { kind, .. }), false) => { | |
dfeec247 XL |
1591 | // Likely missing type parameter. |
1592 | if let Some(generics) = kind.generics() { | |
f035d41b XL |
1593 | if span.overlaps(generics.span) { |
1594 | // Avoid the following: | |
1595 | // error[E0405]: cannot find trait `A` in this scope | |
1596 | // --> $DIR/typo-suggestion-named-underscore.rs:CC:LL | |
1597 | // | | |
1598 | // L | fn foo<T: A>(x: T) {} // Shouldn't suggest underscore | |
1599 | // | ^- help: you might be missing a type parameter: `, A` | |
1600 | // | | | |
1601 | // | not found in this scope | |
1602 | return None; | |
1603 | } | |
dfeec247 XL |
1604 | let msg = "you might be missing a type parameter"; |
1605 | let (span, sugg) = if let [.., param] = &generics.params[..] { | |
1606 | let span = if let [.., bound] = ¶m.bounds[..] { | |
1607 | bound.span() | |
1608 | } else { | |
1609 | param.ident.span | |
1610 | }; | |
1611 | (span, format!(", {}", ident)) | |
1612 | } else { | |
1613 | (generics.span, format!("<{}>", ident)) | |
1614 | }; | |
1615 | // Do not suggest if this is coming from macro expansion. | |
1616 | if !span.from_expansion() { | |
1617 | return Some(( | |
1618 | span.shrink_to_hi(), | |
1619 | msg, | |
1620 | sugg, | |
1621 | Applicability::MaybeIncorrect, | |
1622 | )); | |
1623 | } | |
1624 | } | |
1625 | } | |
1626 | _ => {} | |
1627 | } | |
1628 | None | |
1629 | } | |
f035d41b XL |
1630 | |
1631 | /// Given the target `label`, search the `rib_index`th label rib for similarly named labels, | |
1632 | /// optionally returning the closest match and whether it is reachable. | |
1633 | crate fn suggestion_for_label_in_rib( | |
1634 | &self, | |
1635 | rib_index: usize, | |
1636 | label: Ident, | |
1637 | ) -> Option<LabelSuggestion> { | |
1638 | // Are ribs from this `rib_index` within scope? | |
1639 | let within_scope = self.is_label_valid_from_rib(rib_index); | |
1640 | ||
1641 | let rib = &self.label_ribs[rib_index]; | |
1642 | let names = rib | |
1643 | .bindings | |
1644 | .iter() | |
1645 | .filter(|(id, _)| id.span.ctxt() == label.span.ctxt()) | |
fc512014 XL |
1646 | .map(|(id, _)| id.name) |
1647 | .collect::<Vec<Symbol>>(); | |
f035d41b | 1648 | |
fc512014 | 1649 | find_best_match_for_name(&names, label.name, None).map(|symbol| { |
f035d41b XL |
1650 | // Upon finding a similar name, get the ident that it was from - the span |
1651 | // contained within helps make a useful diagnostic. In addition, determine | |
1652 | // whether this candidate is within scope. | |
1653 | let (ident, _) = rib.bindings.iter().find(|(ident, _)| ident.name == symbol).unwrap(); | |
1654 | (*ident, within_scope) | |
1655 | }) | |
1656 | } | |
416331ca | 1657 | } |
74b04a01 XL |
1658 | |
1659 | impl<'tcx> LifetimeContext<'_, 'tcx> { | |
1660 | crate fn report_missing_lifetime_specifiers( | |
1661 | &self, | |
1662 | span: Span, | |
1663 | count: usize, | |
1664 | ) -> DiagnosticBuilder<'tcx> { | |
1665 | struct_span_err!( | |
1666 | self.tcx.sess, | |
1667 | span, | |
1668 | E0106, | |
1669 | "missing lifetime specifier{}", | |
1670 | pluralize!(count) | |
1671 | ) | |
1672 | } | |
1673 | ||
1674 | crate fn emit_undeclared_lifetime_error(&self, lifetime_ref: &hir::Lifetime) { | |
1675 | let mut err = struct_span_err!( | |
1676 | self.tcx.sess, | |
1677 | lifetime_ref.span, | |
1678 | E0261, | |
1679 | "use of undeclared lifetime name `{}`", | |
1680 | lifetime_ref | |
1681 | ); | |
1682 | err.span_label(lifetime_ref.span, "undeclared lifetime"); | |
f035d41b | 1683 | let mut suggests_in_band = false; |
6a06907d | 1684 | let mut suggest_note = true; |
74b04a01 XL |
1685 | for missing in &self.missing_named_lifetime_spots { |
1686 | match missing { | |
1687 | MissingLifetimeSpot::Generics(generics) => { | |
5869c6ff XL |
1688 | let (span, sugg) = if let Some(param) = generics.params.iter().find(|p| { |
1689 | !matches!( | |
1690 | p.kind, | |
74b04a01 XL |
1691 | hir::GenericParamKind::Type { |
1692 | synthetic: Some(hir::SyntheticTyParamKind::ImplTrait), | |
1693 | .. | |
5869c6ff | 1694 | } | hir::GenericParamKind::Lifetime { |
6c58768f | 1695 | kind: hir::LifetimeParamKind::Elided, |
5869c6ff XL |
1696 | } |
1697 | ) | |
1698 | }) { | |
74b04a01 XL |
1699 | (param.span.shrink_to_lo(), format!("{}, ", lifetime_ref)) |
1700 | } else { | |
f035d41b | 1701 | suggests_in_band = true; |
74b04a01 XL |
1702 | (generics.span, format!("<{}>", lifetime_ref)) |
1703 | }; | |
6a06907d XL |
1704 | if !span.from_expansion() { |
1705 | err.span_suggestion( | |
1706 | span, | |
1707 | &format!("consider introducing lifetime `{}` here", lifetime_ref), | |
1708 | sugg, | |
1709 | Applicability::MaybeIncorrect, | |
1710 | ); | |
1711 | } else if suggest_note { | |
1712 | suggest_note = false; // Avoid displaying the same help multiple times. | |
1713 | err.span_label( | |
1714 | span, | |
1715 | &format!( | |
1716 | "lifetime `{}` is missing in item created through this procedural \ | |
1717 | macro", | |
1718 | lifetime_ref, | |
1719 | ), | |
1720 | ); | |
1721 | } | |
74b04a01 XL |
1722 | } |
1723 | MissingLifetimeSpot::HigherRanked { span, span_type } => { | |
1724 | err.span_suggestion( | |
1725 | *span, | |
1726 | &format!( | |
1727 | "consider making the {} lifetime-generic with a new `{}` lifetime", | |
1728 | span_type.descr(), | |
1729 | lifetime_ref | |
1730 | ), | |
1731 | span_type.suggestion(&lifetime_ref.to_string()), | |
1732 | Applicability::MaybeIncorrect, | |
1733 | ); | |
1734 | err.note( | |
1735 | "for more information on higher-ranked polymorphism, visit \ | |
6a06907d | 1736 | https://doc.rust-lang.org/nomicon/hrtb.html", |
74b04a01 XL |
1737 | ); |
1738 | } | |
3dfed10e | 1739 | _ => {} |
74b04a01 XL |
1740 | } |
1741 | } | |
fc512014 | 1742 | if self.tcx.sess.is_nightly_build() |
f035d41b XL |
1743 | && !self.tcx.features().in_band_lifetimes |
1744 | && suggests_in_band | |
1745 | { | |
1746 | err.help( | |
1747 | "if you want to experiment with in-band lifetime bindings, \ | |
6a06907d | 1748 | add `#![feature(in_band_lifetimes)]` to the crate attributes", |
f035d41b XL |
1749 | ); |
1750 | } | |
74b04a01 XL |
1751 | err.emit(); |
1752 | } | |
1753 | ||
3dfed10e XL |
1754 | // FIXME(const_generics): This patches over a ICE caused by non-'static lifetimes in const |
1755 | // generics. We are disallowing this until we can decide on how we want to handle non-'static | |
1756 | // lifetimes in const generics. See issue #74052 for discussion. | |
1757 | crate fn emit_non_static_lt_in_const_generic_error(&self, lifetime_ref: &hir::Lifetime) { | |
1758 | let mut err = struct_span_err!( | |
1759 | self.tcx.sess, | |
1760 | lifetime_ref.span, | |
1761 | E0771, | |
1762 | "use of non-static lifetime `{}` in const generic", | |
1763 | lifetime_ref | |
1764 | ); | |
1765 | err.note( | |
1766 | "for more information, see issue #74052 \ | |
1767 | <https://github.com/rust-lang/rust/issues/74052>", | |
1768 | ); | |
1769 | err.emit(); | |
1770 | } | |
1771 | ||
74b04a01 XL |
1772 | crate fn is_trait_ref_fn_scope(&mut self, trait_ref: &'tcx hir::PolyTraitRef<'tcx>) -> bool { |
1773 | if let def::Res::Def(_, did) = trait_ref.trait_ref.path.res { | |
1774 | if [ | |
1775 | self.tcx.lang_items().fn_once_trait(), | |
1776 | self.tcx.lang_items().fn_trait(), | |
1777 | self.tcx.lang_items().fn_mut_trait(), | |
1778 | ] | |
1779 | .contains(&Some(did)) | |
1780 | { | |
1781 | let (span, span_type) = match &trait_ref.bound_generic_params { | |
1782 | [] => (trait_ref.span.shrink_to_lo(), ForLifetimeSpanType::BoundEmpty), | |
1783 | [.., bound] => (bound.span.shrink_to_hi(), ForLifetimeSpanType::BoundTail), | |
1784 | }; | |
1785 | self.missing_named_lifetime_spots | |
1786 | .push(MissingLifetimeSpot::HigherRanked { span, span_type }); | |
1787 | return true; | |
1788 | } | |
1789 | }; | |
1790 | false | |
1791 | } | |
1792 | ||
1793 | crate fn add_missing_lifetime_specifiers_label( | |
1794 | &self, | |
1795 | err: &mut DiagnosticBuilder<'_>, | |
1796 | span: Span, | |
1797 | count: usize, | |
3dfed10e XL |
1798 | lifetime_names: &FxHashSet<Symbol>, |
1799 | lifetime_spans: Vec<Span>, | |
74b04a01 XL |
1800 | params: &[ElisionFailureInfo], |
1801 | ) { | |
f9f354fc | 1802 | let snippet = self.tcx.sess.source_map().span_to_snippet(span).ok(); |
74b04a01 | 1803 | |
f9f354fc XL |
1804 | err.span_label( |
1805 | span, | |
1806 | &format!( | |
1807 | "expected {} lifetime parameter{}", | |
1808 | if count == 1 { "named".to_string() } else { count.to_string() }, | |
1809 | pluralize!(count) | |
1810 | ), | |
1811 | ); | |
1812 | ||
3dfed10e XL |
1813 | let suggest_existing = |err: &mut DiagnosticBuilder<'_>, |
1814 | name: &str, | |
1815 | formatter: &dyn Fn(&str) -> String| { | |
1816 | if let Some(MissingLifetimeSpot::HigherRanked { span: for_span, span_type }) = | |
1817 | self.missing_named_lifetime_spots.iter().rev().next() | |
1818 | { | |
1819 | // When we have `struct S<'a>(&'a dyn Fn(&X) -> &X);` we want to not only suggest | |
1820 | // using `'a`, but also introduce the concept of HRLTs by suggesting | |
1821 | // `struct S<'a>(&'a dyn for<'b> Fn(&X) -> &'b X);`. (#72404) | |
1822 | let mut introduce_suggestion = vec![]; | |
1823 | ||
1824 | let a_to_z_repeat_n = |n| { | |
1825 | (b'a'..=b'z').map(move |c| { | |
1826 | let mut s = '\''.to_string(); | |
1827 | s.extend(std::iter::repeat(char::from(c)).take(n)); | |
1828 | s | |
1829 | }) | |
1830 | }; | |
1831 | ||
1832 | // If all single char lifetime names are present, we wrap around and double the chars. | |
1833 | let lt_name = (1..) | |
1834 | .flat_map(a_to_z_repeat_n) | |
1835 | .find(|lt| !lifetime_names.contains(&Symbol::intern(<))) | |
1836 | .unwrap(); | |
1837 | let msg = format!( | |
1838 | "consider making the {} lifetime-generic with a new `{}` lifetime", | |
1839 | span_type.descr(), | |
1840 | lt_name, | |
1841 | ); | |
1842 | err.note( | |
1843 | "for more information on higher-ranked polymorphism, visit \ | |
1844 | https://doc.rust-lang.org/nomicon/hrtb.html", | |
1845 | ); | |
1846 | let for_sugg = span_type.suggestion(<_name); | |
1847 | for param in params { | |
1848 | if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(param.span) { | |
1849 | if snippet.starts_with('&') && !snippet.starts_with("&'") { | |
1850 | introduce_suggestion | |
1851 | .push((param.span, format!("&{} {}", lt_name, &snippet[1..]))); | |
1b1a35ee | 1852 | } else if let Some(stripped) = snippet.strip_prefix("&'_ ") { |
3dfed10e | 1853 | introduce_suggestion |
1b1a35ee | 1854 | .push((param.span, format!("&{} {}", lt_name, stripped))); |
3dfed10e XL |
1855 | } |
1856 | } | |
1857 | } | |
1b1a35ee | 1858 | introduce_suggestion.push((*for_span, for_sugg)); |
3dfed10e XL |
1859 | introduce_suggestion.push((span, formatter(<_name))); |
1860 | err.multipart_suggestion(&msg, introduce_suggestion, Applicability::MaybeIncorrect); | |
1861 | } | |
1862 | ||
f9f354fc XL |
1863 | err.span_suggestion_verbose( |
1864 | span, | |
1865 | &format!("consider using the `{}` lifetime", lifetime_names.iter().next().unwrap()), | |
3dfed10e | 1866 | formatter(name), |
f9f354fc XL |
1867 | Applicability::MaybeIncorrect, |
1868 | ); | |
1869 | }; | |
1870 | let suggest_new = |err: &mut DiagnosticBuilder<'_>, sugg: &str| { | |
1871 | for missing in self.missing_named_lifetime_spots.iter().rev() { | |
1872 | let mut introduce_suggestion = vec![]; | |
1873 | let msg; | |
1874 | let should_break; | |
1875 | introduce_suggestion.push(match missing { | |
1876 | MissingLifetimeSpot::Generics(generics) => { | |
3dfed10e XL |
1877 | if generics.span == DUMMY_SP { |
1878 | // Account for malformed generics in the HIR. This shouldn't happen, | |
1879 | // but if we make a mistake elsewhere, mainly by keeping something in | |
1880 | // `missing_named_lifetime_spots` that we shouldn't, like associated | |
1881 | // `const`s or making a mistake in the AST lowering we would provide | |
1882 | // non-sensical suggestions. Guard against that by skipping these. | |
1883 | // (#74264) | |
1884 | continue; | |
1885 | } | |
f9f354fc XL |
1886 | msg = "consider introducing a named lifetime parameter".to_string(); |
1887 | should_break = true; | |
29967ef6 | 1888 | if let Some(param) = generics.params.iter().find(|p| { |
5869c6ff XL |
1889 | !matches!( |
1890 | p.kind, | |
1891 | hir::GenericParamKind::Type { | |
1892 | synthetic: Some(hir::SyntheticTyParamKind::ImplTrait), | |
1893 | .. | |
1894 | } | |
1895 | ) | |
f9f354fc XL |
1896 | }) { |
1897 | (param.span.shrink_to_lo(), "'a, ".to_string()) | |
1898 | } else { | |
1899 | (generics.span, "<'a>".to_string()) | |
74b04a01 XL |
1900 | } |
1901 | } | |
f9f354fc XL |
1902 | MissingLifetimeSpot::HigherRanked { span, span_type } => { |
1903 | msg = format!( | |
1904 | "consider making the {} lifetime-generic with a new `'a` lifetime", | |
1905 | span_type.descr(), | |
1906 | ); | |
1907 | should_break = false; | |
1908 | err.note( | |
1909 | "for more information on higher-ranked polymorphism, visit \ | |
1910 | https://doc.rust-lang.org/nomicon/hrtb.html", | |
1911 | ); | |
1912 | (*span, span_type.suggestion("'a")) | |
1913 | } | |
3dfed10e XL |
1914 | MissingLifetimeSpot::Static => { |
1915 | let (span, sugg) = match snippet.as_deref() { | |
1916 | Some("&") => (span.shrink_to_hi(), "'static ".to_owned()), | |
1917 | Some("'_") => (span, "'static".to_owned()), | |
1918 | Some(snippet) if !snippet.ends_with('>') => { | |
1919 | if snippet == "" { | |
1920 | ( | |
1921 | span, | |
1922 | std::iter::repeat("'static") | |
1923 | .take(count) | |
1924 | .collect::<Vec<_>>() | |
1925 | .join(", "), | |
1926 | ) | |
1927 | } else { | |
1928 | ( | |
1929 | span.shrink_to_hi(), | |
1930 | format!( | |
1931 | "<{}>", | |
1932 | std::iter::repeat("'static") | |
1933 | .take(count) | |
1934 | .collect::<Vec<_>>() | |
1935 | .join(", ") | |
1936 | ), | |
1937 | ) | |
1938 | } | |
1939 | } | |
1940 | _ => continue, | |
1941 | }; | |
1942 | err.span_suggestion_verbose( | |
1943 | span, | |
1944 | "consider using the `'static` lifetime", | |
1945 | sugg.to_string(), | |
1946 | Applicability::MaybeIncorrect, | |
1947 | ); | |
1948 | continue; | |
1949 | } | |
f9f354fc XL |
1950 | }); |
1951 | for param in params { | |
1952 | if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(param.span) { | |
1953 | if snippet.starts_with('&') && !snippet.starts_with("&'") { | |
1954 | introduce_suggestion | |
1955 | .push((param.span, format!("&'a {}", &snippet[1..]))); | |
29967ef6 XL |
1956 | } else if let Some(stripped) = snippet.strip_prefix("&'_ ") { |
1957 | introduce_suggestion.push((param.span, format!("&'a {}", &stripped))); | |
f9f354fc | 1958 | } |
74b04a01 XL |
1959 | } |
1960 | } | |
f9f354fc XL |
1961 | introduce_suggestion.push((span, sugg.to_string())); |
1962 | err.multipart_suggestion(&msg, introduce_suggestion, Applicability::MaybeIncorrect); | |
1963 | if should_break { | |
1964 | break; | |
74b04a01 | 1965 | } |
f9f354fc XL |
1966 | } |
1967 | }; | |
1968 | ||
1b1a35ee | 1969 | let lifetime_names: Vec<_> = lifetime_names.iter().collect(); |
3dfed10e XL |
1970 | match (&lifetime_names[..], snippet.as_deref()) { |
1971 | ([name], Some("&")) => { | |
1972 | suggest_existing(err, &name.as_str()[..], &|name| format!("&{} ", name)); | |
f9f354fc | 1973 | } |
3dfed10e XL |
1974 | ([name], Some("'_")) => { |
1975 | suggest_existing(err, &name.as_str()[..], &|n| n.to_string()); | |
f9f354fc | 1976 | } |
3dfed10e XL |
1977 | ([name], Some("")) => { |
1978 | suggest_existing(err, &name.as_str()[..], &|n| format!("{}, ", n).repeat(count)); | |
f9f354fc | 1979 | } |
3dfed10e XL |
1980 | ([name], Some(snippet)) if !snippet.ends_with('>') => { |
1981 | let f = |name: &str| { | |
f9f354fc XL |
1982 | format!( |
1983 | "{}<{}>", | |
1984 | snippet, | |
1985 | std::iter::repeat(name.to_string()) | |
1986 | .take(count) | |
1987 | .collect::<Vec<_>>() | |
1988 | .join(", ") | |
3dfed10e XL |
1989 | ) |
1990 | }; | |
1991 | suggest_existing(err, &name.as_str()[..], &f); | |
f9f354fc | 1992 | } |
3dfed10e | 1993 | ([], Some("&")) if count == 1 => { |
f9f354fc XL |
1994 | suggest_new(err, "&'a "); |
1995 | } | |
3dfed10e | 1996 | ([], Some("'_")) if count == 1 => { |
f9f354fc XL |
1997 | suggest_new(err, "'a"); |
1998 | } | |
3dfed10e XL |
1999 | ([], Some(snippet)) if !snippet.ends_with('>') => { |
2000 | if snippet == "" { | |
2001 | // This happens when we have `type Bar<'a> = Foo<T>` where we point at the space | |
2002 | // before `T`. We will suggest `type Bar<'a> = Foo<'a, T>`. | |
2003 | suggest_new( | |
2004 | err, | |
2005 | &std::iter::repeat("'a, ").take(count).collect::<Vec<_>>().join(""), | |
2006 | ); | |
2007 | } else { | |
2008 | suggest_new( | |
2009 | err, | |
2010 | &format!( | |
2011 | "{}<{}>", | |
2012 | snippet, | |
2013 | std::iter::repeat("'a").take(count).collect::<Vec<_>>().join(", ") | |
2014 | ), | |
2015 | ); | |
2016 | } | |
f9f354fc | 2017 | } |
3dfed10e XL |
2018 | (lts, ..) if lts.len() > 1 => { |
2019 | err.span_note(lifetime_spans, "these named lifetimes are available to use"); | |
f9f354fc XL |
2020 | if Some("") == snippet.as_deref() { |
2021 | // This happens when we have `Foo<T>` where we point at the space before `T`, | |
2022 | // but this can be confusing so we give a suggestion with placeholders. | |
2023 | err.span_suggestion_verbose( | |
2024 | span, | |
2025 | "consider using one of the available lifetimes here", | |
2026 | "'lifetime, ".repeat(count), | |
2027 | Applicability::HasPlaceholders, | |
2028 | ); | |
74b04a01 XL |
2029 | } |
2030 | } | |
f9f354fc | 2031 | _ => {} |
74b04a01 XL |
2032 | } |
2033 | } | |
1b1a35ee | 2034 | |
5869c6ff XL |
2035 | /// Non-static lifetimes are prohibited in anonymous constants under `min_const_generics`. |
2036 | /// This function will emit an error if `const_generics` is not enabled, the body identified by | |
1b1a35ee XL |
2037 | /// `body_id` is an anonymous constant and `lifetime_ref` is non-static. |
2038 | crate fn maybe_emit_forbidden_non_static_lifetime_error( | |
2039 | &self, | |
2040 | body_id: hir::BodyId, | |
2041 | lifetime_ref: &'tcx hir::Lifetime, | |
2042 | ) { | |
2043 | let is_anon_const = matches!( | |
2044 | self.tcx.def_kind(self.tcx.hir().body_owner_def_id(body_id)), | |
2045 | hir::def::DefKind::AnonConst | |
2046 | ); | |
2047 | let is_allowed_lifetime = matches!( | |
2048 | lifetime_ref.name, | |
2049 | hir::LifetimeName::Implicit | hir::LifetimeName::Static | hir::LifetimeName::Underscore | |
2050 | ); | |
2051 | ||
5869c6ff | 2052 | if !self.tcx.lazy_normalization() && is_anon_const && !is_allowed_lifetime { |
1b1a35ee XL |
2053 | feature_err( |
2054 | &self.tcx.sess.parse_sess, | |
2055 | sym::const_generics, | |
2056 | lifetime_ref.span, | |
2057 | "a non-static lifetime is not allowed in a `const`", | |
2058 | ) | |
2059 | .emit(); | |
2060 | } | |
2061 | } | |
74b04a01 | 2062 | } |