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