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