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