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