1// ignore-tidy-file-filelength2use std::mem;3use std::ops::ControlFlow;45use itertools::Itertools as _;6use rustc_ast::visit::{self, Visitor};7use rustc_ast::{8 self as ast, CRATE_NODE_ID, Crate, DUMMY_NODE_ID, ItemKind, ModKind, NodeId, Path,9 join_path_idents,10};11use rustc_ast_pretty::pprust;12use rustc_attr_parsing::AttributeParser;13use rustc_data_structures::fx::{FxHashMap, FxHashSet};14use rustc_data_structures::unord::{UnordMap, UnordSet};15use rustc_errors::codes::*;16use rustc_errors::{17 Applicability, Diag, DiagCtxtHandle, Diagnostic, ErrorGuaranteed, MultiSpan, SuggestionStyle,18 pluralize, struct_span_code_err,19};20use rustc_feature::BUILTIN_ATTRIBUTES;21use rustc_hir::attrs::diagnostic::{CustomDiagnostic, Directive, FormatArgs};22use rustc_hir::attrs::{AttributeKind, CfgEntry, StrippedCfgItem};23use rustc_hir::def::Namespace::{self, *};24use rustc_hir::def::{CtorKind, CtorOf, DefKind, MacroKinds, NonMacroAttrKind, PerNS};25use rustc_hir::def_id::{CRATE_DEF_ID, DefId};26use rustc_hir::{Attribute, PrimTy, Stability, StabilityLevel, find_attr};27use rustc_lint_defs::builtin::{28 ABSOLUTE_PATHS_NOT_STARTING_WITH_CRATE, AMBIGUOUS_GLOB_IMPORTS, AMBIGUOUS_IMPORT_VISIBILITIES,29 AMBIGUOUS_PANIC_IMPORTS, MACRO_EXPANDED_MACRO_EXPORTS_ACCESSED_BY_ABSOLUTE_PATHS,30};31use rustc_middle::bug;32use rustc_middle::ty::{TyCtxt, Visibility};33use rustc_session::Session;34use rustc_session::utils::was_invoked_from_cargo;35use rustc_span::def_id::ModId;36use rustc_span::edit_distance::find_best_match_for_name;37use rustc_span::edition::Edition;38use rustc_span::hygiene::MacroKind;39use rustc_span::source_map::SourceMap;40use rustc_span::{41 BytePos, Ident, RemapPathScopeComponents, Span, Spanned, Symbol, SyntaxContext, kw, sym,42};43use thin_vec::{ThinVec, thin_vec};44use tracing::{debug, instrument};4546use crate::diagnostics::{47 self, AddedMacroUse, ChangeImportBinding, ChangeImportBindingSuggestion, ConsiderAddingADerive,48 ExplicitUnsafeTraits, MacroDefinedLater, MacroRulesNot, MacroSuggMovePosition,49 MaybeMissingMacroRulesName,50};51use crate::hygiene::Macros20NormalizedSyntaxContext;52use crate::imports::{Import, ImportKind, UnresolvedImportError, import_path_to_string};53use crate::late::{DiagMetadata, PatternSource, Rib};54use crate::{55 AmbiguityError, AmbiguityKind, AmbiguityWarning, BindingError, BindingKey, Decl, DeclKind,56 DelayedVisResolutionError, Finalize, ForwardGenericParamBanReason, HasGenericParams, IdentKey,57 LateDecl, MacroRulesScope, Module, ModuleKind, ModuleOrUniformRoot, ParentScope, PathResult,58 PrivacyError, Res, ResolutionError, Resolver, Scope, ScopeSet, Segment, UseError, Used,59 VisResolutionError, path_names_to_string,60};6162/// A vector of spans and replacements, a message and applicability.63pub(crate) type Suggestion = (Vec<(Span, String)>, String, Applicability);6465/// Potential candidate for an undeclared or out-of-scope label - contains the ident of a66/// similarly named label and whether or not it is reachable.67pub(crate) type LabelSuggestion = (Ident, bool);6869#[derive(Clone)]70pub(crate) struct StructCtor {71 pub res: Res,72 pub vis: Visibility<ModId>,73 pub field_visibilities: Vec<Visibility<ModId>>,74}7576impl StructCtor {77 pub(crate) fn has_private_fields<'ra>(&self, m: Module<'ra>, r: &Resolver<'ra, '_>) -> bool {78 self.field_visibilities.iter().any(|&vis| !r.is_accessible_from(vis, m))79 }80}8182#[derive(Debug)]83pub(crate) enum SuggestionTarget {84 /// The target has a similar name as the name used by the programmer (probably a typo)85 SimilarlyNamed,86 /// The target is the only valid item that can be used in the corresponding context87 SingleItem,88}8990#[derive(Debug)]91pub(crate) struct TypoSuggestion {92 pub candidate: Symbol,93 /// The source location where the name is defined; None if the name is not defined94 /// in source e.g. primitives95 pub span: Option<Span>,96 pub res: Res,97 pub target: SuggestionTarget,98}99100impl TypoSuggestion {101 pub(crate) fn new(candidate: Symbol, span: Span, res: Res) -> TypoSuggestion {102 Self { candidate, span: Some(span), res, target: SuggestionTarget::SimilarlyNamed }103 }104 pub(crate) fn typo_from_name(candidate: Symbol, res: Res) -> TypoSuggestion {105 Self { candidate, span: None, res, target: SuggestionTarget::SimilarlyNamed }106 }107 pub(crate) fn single_item(candidate: Symbol, span: Span, res: Res) -> TypoSuggestion {108 Self { candidate, span: Some(span), res, target: SuggestionTarget::SingleItem }109 }110}111112/// A free importable items suggested in case of resolution failure.113#[derive(Debug)]114pub(crate) struct ImportSuggestion {115 pub did: Option<DefId>,116 pub descr: &'static str,117 pub path: Path,118 pub accessible: bool,119 // false if the path traverses a foreign `#[doc(hidden)]` item.120 pub doc_visible: bool,121 pub via_import: bool,122 /// An extra note that should be issued if this item is suggested123 pub note: Option<String>,124 pub is_stable: bool,125}126127/// Adjust the impl span so that just the `impl` keyword is taken by removing128/// everything after `<` (`"impl<T> Iterator for A<T> {}" -> "impl"`) and129/// everything after the first whitespace (`"impl Iterator for A" -> "impl"`).130///131/// *Attention*: the method used is very fragile since it essentially duplicates the work of the132/// parser. If you need to use this function or something similar, please consider updating the133/// `source_map` functions and this function to something more robust.134fn reduce_impl_span_to_impl_keyword(sm: &SourceMap, impl_span: Span) -> Span {135 let impl_span = sm.span_until_char(impl_span, '<');136 sm.span_until_whitespace(impl_span)137}138139impl<'ra, 'tcx> Resolver<'ra, 'tcx> {140 /// Reports unresolved imports.141 ///142 /// Multiple unresolved import errors within the same use tree are combined into a single143 /// diagnostic.144 pub(crate) fn throw_unresolved_import_error(145 &mut self,146 mut errors: Vec<(Import<'_>, UnresolvedImportError)>,147 glob_error: bool,148 ) {149 errors.retain(|(_import, err)| match err.module {150 // Skip `use` errors for `use foo::Bar;` if `foo.rs` has unrecovered parse errors.151 Some(def_id) if self.mods_with_parse_errors.contains(&def_id) => false,152 // If we've encountered something like `use _;`, we've already emitted an error stating153 // that `_` is not a valid identifier, so we ignore that resolve error.154 _ => err.segment.map(|s| s.name) != Some(kw::Underscore),155 });156 if errors.is_empty() {157 self.tcx.dcx().delayed_bug("expected a parse or \"`_` can't be an identifier\" error");158 return;159 }160161 let span = MultiSpan::from_spans(errors.iter().map(|(_, err)| err.span).collect());162163 let paths = errors164 .iter()165 .map(|(import, err)| {166 let path = import_path_to_string(167 &import.module_path.iter().map(|seg| seg.ident).collect::<Vec<_>>(),168 &import.kind,169 err.span,170 );171 format!("`{path}`")172 })173 .collect::<Vec<_>>();174 let default_message =175 format!("unresolved import{} {}", pluralize!(paths.len()), paths.join(", "),);176177 // Process `import` use of the `#[diagnostic::on_unknown]` attribute.178 //179 // We don't need to check feature gates here; that happens on initialization of the180 // `on_unknown_attr` fields.181 let (mut message, label, mut notes) =182 if let Some(directive) = errors[0].1.on_unknown_attr.as_ref().map(|a| &a.directive) {183 let this = errors184 .iter()185 .map(|(_import, err)| {186 // Is this unwrap_or reachable?187 err.segment.map(|s| s.name).unwrap_or(kw::Underscore)188 })189 .join(", ");190191 let args = FormatArgs { unresolved: this.clone(), this, .. };192193 let CustomDiagnostic { message, label, notes, parent_label: _dead } =194 directive.eval(None, &args);195196 (message, label, notes)197 } else {198 (None, None, Vec::new())199 };200201 // `module` use of the `#[diagnostic::on_unknown]` attribute.202 // We assume that someone who put the attribute on the import has more information than203 // the person who put it on the module, so we choose to prioritize the import attribute.204 let mut mod_diagnostics: Vec<CustomDiagnostic> = errors205 .iter()206 .map(|(import, import_error)| {207 if let Some(ModuleOrUniformRoot::Module(module_data)) = import.imported_module.get()208 && let ModuleKind::Def(DefKind::Mod, def_id, _, name) = module_data.kind209 {210 let Some(directive) = self.on_unknown_data(def_id) else {211 return CustomDiagnostic::default();212 };213214 let this = if let Some(name) = name {215 name.to_string()216 } else if let Some(crate_name) = &self.tcx.sess.opts.crate_name {217 crate_name.to_string()218 } else {219 "<unnamed crate>".to_string()220 };221 let unresolved = import_error.segment.map(|s| s.name).unwrap_or(kw::Underscore);222 let args = FormatArgs { this, unresolved: unresolved.to_string(), .. };223224 directive.eval(None, &args)225 } else {226 CustomDiagnostic::default()227 }228 })229 .collect();230231 // If there is no import attribute with a message,232 // but all mod messages are the same, use that.233 let mod_message =234 mod_diagnostics.iter_mut().flat_map(|d| d.message.take()).all_equal_value();235 if message.is_none()236 && let Ok(mod_msg) = mod_message237 {238 message = Some(mod_msg);239 }240241 let mut diag = if let Some(message) = message {242 struct_span_code_err!(self.dcx(), span, E0432, "{message}").with_note(default_message)243 } else {244 struct_span_code_err!(self.dcx(), span, E0432, "{default_message}")245 };246247 for mod_diag in mod_diagnostics.iter_mut() {248 for mod_note in mod_diag.notes.drain(..) {249 if !notes.contains(&mod_note) {250 notes.push(mod_note);251 }252 }253 }254255 if !notes.is_empty() {256 for note in notes {257 diag.note(note);258 }259 } else if let Some((_, UnresolvedImportError { note: Some(note), .. })) = errors.last() {260 diag.note(note.clone());261 }262263 /// Upper limit on the number of `span_label` messages.264 const MAX_LABEL_COUNT: usize = 10;265 let mod_labels = mod_diagnostics.into_iter().map(|cd| cd.label);266267 for ((import, err), mod_label) in errors.into_iter().zip(mod_labels).take(MAX_LABEL_COUNT) {268 let label_span = match err.segment {269 Some(segment) => segment.span,270 None => err.span,271 };272 if let Some(label) = &label {273 diag.span_label(label_span, label.clone());274 } else if let Some(label) = mod_label {275 diag.span_label(label_span, label);276 } else if let Some(label) = &err.label {277 diag.span_label(label_span, label.clone());278 }279280 if let Some((suggestions, msg, applicability)) = err.suggestion {281 if suggestions.is_empty() {282 diag.help(msg);283 continue;284 }285 diag.multipart_suggestion(msg, suggestions, applicability);286 }287288 if let Some(candidates) = &err.candidates {289 match &import.kind {290 ImportKind::Single { nested: false, source, target, .. } => import_candidates(291 self.tcx,292 &mut diag,293 Some(err.span),294 candidates,295 DiagMode::Import { append: false, unresolved_import: true },296 (source != target)297 .then(|| format!(" as {target}"))298 .as_deref()299 .unwrap_or(""),300 ),301 ImportKind::Single { nested: true, source, target, .. } => {302 import_candidates(303 self.tcx,304 &mut diag,305 None,306 candidates,307 DiagMode::Normal,308 (source != target)309 .then(|| format!(" as {target}"))310 .as_deref()311 .unwrap_or(""),312 );313 }314 _ => {}315 }316 }317318 if matches!(import.kind, ImportKind::Single { .. })319 && let Some(segment) = err.segment320 && let Some(module) = err.module321 {322 self.find_cfg_stripped(&mut diag, &segment.name, module)323 }324 }325326 let guar = diag.emit();327 if glob_error {328 self.glob_error = Some(guar);329 }330 }331332 pub(crate) fn dcx(&self) -> DiagCtxtHandle<'tcx> {333 self.tcx.dcx()334 }335336 pub(crate) fn report_errors(&mut self, krate: &Crate, use_injections: Vec<UseError<'tcx>>) {337 self.report_delayed_vis_resolution_errors();338 self.report_with_use_injections(krate, use_injections);339340 for &(span_use, span_def) in &self.macro_expanded_macro_export_errors {341 self.lint_buffer.buffer_lint(342 MACRO_EXPANDED_MACRO_EXPORTS_ACCESSED_BY_ABSOLUTE_PATHS,343 CRATE_NODE_ID,344 span_use,345 diagnostics::MacroExpandedMacroExportsAccessedByAbsolutePaths {346 definition: span_def,347 },348 );349 }350351 for ambiguity_error in &self.ambiguity_errors {352 let mut diag = self.ambiguity_diagnostic(ambiguity_error);353354 if let Some(ambiguity_warning) = ambiguity_error.warning {355 let node_id = match ambiguity_error.b1.0.kind {356 DeclKind::Import { import, .. } => import.root_id,357 DeclKind::Def(_) => CRATE_NODE_ID,358 };359360 let lint = match ambiguity_warning {361 _ if ambiguity_error.ambig_vis.is_some() => AMBIGUOUS_IMPORT_VISIBILITIES,362 AmbiguityWarning::GlobImport => AMBIGUOUS_GLOB_IMPORTS,363 AmbiguityWarning::PanicImport => AMBIGUOUS_PANIC_IMPORTS,364 };365366 self.lint_buffer.buffer_lint(lint, node_id, diag.ident.span, diag);367 } else {368 diag.is_error = true;369 self.dcx().emit_err(diag);370 }371 }372373 let mut reported_spans = FxHashSet::default();374 for error in mem::take(&mut self.privacy_errors) {375 if reported_spans.insert(error.dedup_span) {376 self.report_privacy_error(&error);377 }378 }379 }380381 fn report_delayed_vis_resolution_errors(&mut self) {382 for DelayedVisResolutionError { vis, parent_scope, error } in383 mem::take(&mut self.delayed_vis_resolution_errors)384 {385 match self.try_resolve_visibility(&parent_scope, &vis, true) {386 Ok(_) => self.report_vis_error(error),387 Err(error) => self.report_vis_error(error),388 };389 }390 }391392 fn report_with_use_injections(&mut self, krate: &Crate, use_injections: Vec<UseError<'tcx>>) {393 for UseError { mut err, candidates, node_id, instead, suggestion, path, is_call } in394 use_injections395 {396 let (span, found_use) = if node_id != DUMMY_NODE_ID {397 UsePlacementFinder::check(krate, node_id)398 } else {399 (None, FoundUse::No)400 };401402 if !candidates.is_empty() {403 show_candidates(404 self.tcx,405 &mut err,406 span,407 &candidates,408 if instead { Instead::Yes } else { Instead::No },409 found_use,410 DiagMode::Normal,411 path,412 "",413 );414 err.emit();415 } else if let Some((span, msg, sugg, appl)) = suggestion {416 err.span_suggestion_verbose(span, msg, sugg, appl);417 err.emit();418 } else if let [segment] = path.as_slice()419 && is_call420 {421 err.stash(segment.ident.span, rustc_errors::StashKey::CallIntoMethod);422 } else {423 err.emit();424 }425 }426 }427428 pub(crate) fn report_conflict(429 &mut self,430 ident: IdentKey,431 ns: Namespace,432 old_binding: Decl<'ra>,433 new_binding: Decl<'ra>,434 ) {435 // Error on the second of two conflicting names436 if old_binding.span.lo() > new_binding.span.lo() {437 return self.report_conflict(ident, ns, new_binding, old_binding);438 }439440 let container = match old_binding.parent_module.unwrap().expect_local().kind {441 // Avoid using TyCtxt::def_kind_descr in the resolver, because it442 // indirectly *calls* the resolver, and would cause a query cycle.443 ModuleKind::Def(kind, def_id, _, _) => kind.descr(def_id),444 ModuleKind::Block => "block",445 };446447 let (name, span) =448 (ident.name, self.tcx.sess.source_map().guess_head_span(new_binding.span));449450 if self.name_already_seen.get(&name) == Some(&span) {451 return;452 }453454 let old_kind = match (ns, old_binding.res()) {455 (ValueNS, _) => "value",456 (MacroNS, _) => "macro",457 (TypeNS, _) if old_binding.is_extern_crate() => "extern crate",458 (TypeNS, Res::Def(DefKind::Mod, _)) => "module",459 (TypeNS, Res::Def(DefKind::Trait, _)) => "trait",460 (TypeNS, _) => "type",461 };462463 let code = match (old_binding.is_extern_crate(), new_binding.is_extern_crate()) {464 (true, true) => E0259,465 (true, _) | (_, true) => match new_binding.is_import() && old_binding.is_import() {466 true => E0254,467 false => E0260,468 },469 _ => match (old_binding.is_import_user_facing(), new_binding.is_import_user_facing()) {470 (false, false) => E0428,471 (true, true) => E0252,472 _ => E0255,473 },474 };475476 let label = match new_binding.is_import_user_facing() {477 true => diagnostics::NameDefinedMultipleTimeLabel::Reimported { span, name },478 false => diagnostics::NameDefinedMultipleTimeLabel::Redefined { span, name },479 };480481 let old_binding_label =482 (!old_binding.span.is_dummy() && old_binding.span != span).then(|| {483 let span = self.tcx.sess.source_map().guess_head_span(old_binding.span);484 match old_binding.is_import_user_facing() {485 true => diagnostics::NameDefinedMultipleTimeOldBindingLabel::Import {486 span,487 old_kind,488 name,489 },490 false => diagnostics::NameDefinedMultipleTimeOldBindingLabel::Definition {491 span,492 old_kind,493 name,494 },495 }496 });497498 let mut err = self499 .dcx()500 .create_err(diagnostics::NameDefinedMultipleTime {501 span,502 name,503 descr: ns.descr(),504 container,505 label,506 old_binding_label,507 })508 .with_code(code);509510 // See https://github.com/rust-lang/rust/issues/32354511 use DeclKind::Import;512 let can_suggest = |binding: Decl<'_>, import: self::Import<'_>| {513 !binding.span.is_dummy()514 && !matches!(import.kind, ImportKind::MacroUse { .. } | ImportKind::MacroExport)515 };516 let import = match (&new_binding.kind, &old_binding.kind) {517 // If there are two imports where one or both have attributes then prefer removing the518 // import without attributes.519 (Import { import: new, .. }, Import { import: old, .. })520 if {521 (new.has_attributes || old.has_attributes)522 && can_suggest(old_binding, *old)523 && can_suggest(new_binding, *new)524 } =>525 {526 if old.has_attributes {527 Some((*new, new_binding.span, true))528 } else {529 Some((*old, old_binding.span, true))530 }531 }532 // Otherwise prioritize the new binding.533 (Import { import, .. }, other) if can_suggest(new_binding, *import) => {534 Some((*import, new_binding.span, other.is_import()))535 }536 (other, Import { import, .. }) if can_suggest(old_binding, *import) => {537 Some((*import, old_binding.span, other.is_import()))538 }539 _ => None,540 };541542 // Check if the target of the use for both bindings is the same.543 let duplicate = new_binding.res().opt_def_id() == old_binding.res().opt_def_id();544 let has_dummy_span = new_binding.span.is_dummy() || old_binding.span.is_dummy();545 let from_item =546 self.extern_prelude.get(&ident).is_none_or(|entry| entry.introduced_by_item());547 // Only suggest removing an import if both bindings are to the same def, if both spans548 // aren't dummy spans. Further, if both bindings are imports, then the ident must have549 // been introduced by an item.550 let should_remove_import = duplicate551 && !has_dummy_span552 && ((new_binding.is_extern_crate() || old_binding.is_extern_crate()) || from_item);553554 match import {555 Some((import, span, true)) if should_remove_import && import.is_nested() => {556 self.add_suggestion_for_duplicate_nested_use(&mut err, import, span);557 }558 Some((import, _, true)) if should_remove_import && !import.is_glob() => {559 // Simple case - remove the entire import. Due to the above match arm, this can560 // only be a single use so just remove it entirely.561 err.subdiagnostic(diagnostics::ToolOnlyRemoveUnnecessaryImport {562 span: import.use_span_with_attributes,563 });564 }565 Some((import, span, _)) => {566 self.add_suggestion_for_rename_of_use(&mut err, name, import, span);567 }568 _ => {}569 }570571 err.emit();572 self.name_already_seen.insert(name, span);573 }574575 /// This function adds a suggestion to change the binding name of a new import that conflicts576 /// with an existing import.577 ///578 /// ```text,ignore (diagnostic)579 /// help: you can use `as` to change the binding name of the import580 /// |581 /// LL | use foo::bar as other_bar;582 /// | ^^^^^^^^^^^^^^^^^^^^^583 /// ```584 fn add_suggestion_for_rename_of_use(585 &self,586 err: &mut Diag<'_>,587 name: Symbol,588 import: Import<'_>,589 binding_span: Span,590 ) {591 let suggested_name = if name.as_str().chars().next().unwrap().is_uppercase() {592 format!("Other{name}")593 } else {594 format!("other_{name}")595 };596597 let mut suggestion = None;598 let mut span = binding_span;599 match import.kind {600 ImportKind::Single { source, .. } => {601 if let Some(pos) = source.span.hi().0.checked_sub(binding_span.lo().0)602 && let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(binding_span)603 && pos as usize <= snippet.len()604 {605 span = binding_span.with_lo(binding_span.lo() + BytePos(pos)).with_hi(606 binding_span.hi() - BytePos(if snippet.ends_with(';') { 1 } else { 0 }),607 );608 suggestion = Some(format!(" as {suggested_name}"));609 }610 }611 ImportKind::ExternCrate { source, target, .. } => {612 suggestion = Some(format!(613 "extern crate {} as {};",614 source.unwrap_or(target.name),615 suggested_name,616 ))617 }618 _ => unreachable!(),619 }620621 if let Some(suggestion) = suggestion {622 err.subdiagnostic(ChangeImportBindingSuggestion { span, suggestion });623 } else {624 err.subdiagnostic(ChangeImportBinding { span });625 }626 }627628 /// This function adds a suggestion to remove an unnecessary binding from an import that is629 /// nested. In the following example, this function will be invoked to remove the `a` binding630 /// in the second use statement:631 ///632 /// ```ignore (diagnostic)633 /// use issue_52891::a;634 /// use issue_52891::{d, a, e};635 /// ```636 ///637 /// The following suggestion will be added:638 ///639 /// ```ignore (diagnostic)640 /// use issue_52891::{d, a, e};641 /// ^-- help: remove unnecessary import642 /// ```643 ///644 /// If the nested use contains only one import then the suggestion will remove the entire645 /// line.646 ///647 /// It is expected that the provided import is nested - this isn't checked by the648 /// function. If this invariant is not upheld, this function's behaviour will be unexpected649 /// as characters expected by span manipulations won't be present.650 fn add_suggestion_for_duplicate_nested_use(651 &self,652 err: &mut Diag<'_>,653 import: Import<'_>,654 binding_span: Span,655 ) {656 assert!(import.is_nested());657658 // Two examples will be used to illustrate the span manipulations we're doing:659 //660 // - Given `use issue_52891::{d, a, e};` where `a` is a duplicate then `binding_span` is661 // `a` and `import.use_span` is `issue_52891::{d, a, e};`.662 // - Given `use issue_52891::{d, e, a};` where `a` is a duplicate then `binding_span` is663 // `a` and `import.use_span` is `issue_52891::{d, e, a};`.664665 let (found_closing_brace, span) =666 find_span_of_binding_until_next_binding(self.tcx.sess, binding_span, import.use_span);667668 // If there was a closing brace then identify the span to remove any trailing commas from669 // previous imports.670 if found_closing_brace {671 if let Some(span) = extend_span_to_previous_binding(self.tcx.sess, span) {672 err.subdiagnostic(diagnostics::ToolOnlyRemoveUnnecessaryImport { span });673 } else {674 // Remove the entire line if we cannot extend the span back, this indicates an675 // `issue_52891::{self}` case.676 err.subdiagnostic(diagnostics::RemoveUnnecessaryImport {677 span: import.use_span_with_attributes,678 });679 }680681 return;682 }683684 err.subdiagnostic(diagnostics::RemoveUnnecessaryImport { span });685 }686687 pub(crate) fn lint_if_path_starts_with_module(688 &mut self,689 finalize: Finalize,690 path: &[Segment],691 second_binding: Option<Decl<'_>>,692 ) {693 let Finalize { node_id, root_span, .. } = finalize;694695 let first_name = match path.get(0) {696 // In the 2018 edition this lint is a hard error, so nothing to do697 Some(seg) if seg.ident.span.is_rust_2015() && self.tcx.sess.is_rust_2015() => {698 seg.ident.name699 }700 _ => return,701 };702703 // We're only interested in `use` paths which should start with704 // `{{root}}` currently.705 if first_name != kw::PathRoot {706 return;707 }708709 match path.get(1) {710 // If this import looks like `crate::...` it's already good711 Some(Segment { ident, .. }) if ident.name == kw::Crate => return,712 // Otherwise go below to see if it's an extern crate713 Some(_) => {}714 // If the path has length one (and it's `PathRoot` most likely)715 // then we don't know whether we're gonna be importing a crate or an716 // item in our crate. Defer this lint to elsewhere717 None => return,718 }719720 // If the first element of our path was actually resolved to an721 // `ExternCrate` (also used for `crate::...`) then no need to issue a722 // warning, this looks all good!723 if let Some(binding) = second_binding724 && let DeclKind::Import { import, .. } = binding.kind725 // Careful: we still want to rewrite paths from renamed extern crates.726 && let ImportKind::ExternCrate { source: None, .. } = import.kind727 {728 return;729 }730731 self.lint_buffer.dyn_buffer_lint_any(732 ABSOLUTE_PATHS_NOT_STARTING_WITH_CRATE,733 node_id,734 root_span,735 move |dcx, level, sess| {736 let (replacement, applicability) = match sess737 .downcast_ref::<Session>()738 .expect("expected a `Session`")739 .source_map()740 .span_to_snippet(root_span)741 {742 Ok(ref s) => {743 // FIXME(Manishearth) ideally the emitting code744 // can tell us whether or not this is global745 let opt_colon = if s.trim_start().starts_with("::") { "" } else { "::" };746747 (format!("crate{opt_colon}{s}"), Applicability::MachineApplicable)748 }749 Err(_) => ("crate::<path>".to_string(), Applicability::HasPlaceholders),750 };751 diagnostics::AbsPathWithModule {752 sugg: diagnostics::AbsPathWithModuleSugg {753 span: root_span,754 applicability,755 replacement,756 },757 }758 .into_diag(dcx, level)759 },760 );761 }762763 pub(crate) fn add_module_candidates(764 &self,765 module: Module<'ra>,766 names: &mut Vec<TypoSuggestion>,767 filter_fn: &impl Fn(Res) -> bool,768 ctxt: Option<SyntaxContext>,769 ) {770 module.for_each_child(self, |_this, ident, orig_ident_span, _ns, binding| {771 let res = binding.res();772 if filter_fn(res) && ctxt.is_none_or(|ctxt| ctxt == *ident.ctxt) {773 names.push(TypoSuggestion::new(ident.name, orig_ident_span, res));774 }775 });776 }777778 /// Combines an error with provided span and emits it.779 ///780 /// This takes the error provided, combines it with the span and any additional spans inside the781 /// error and emits it.782 pub(crate) fn report_error(783 &self,784 span: Span,785 resolution_error: ResolutionError<'ra>,786 ) -> ErrorGuaranteed {787 self.into_struct_error(span, resolution_error).emit()788 }789790 pub(crate) fn into_struct_error(791 &self,792 span: Span,793 resolution_error: ResolutionError<'ra>,794 ) -> Diag<'_> {795 match resolution_error {796 ResolutionError::GenericParamsFromOuterItem {797 outer_res,798 has_generic_params,799 def_kind,800 inner_item,801 current_self_ty,802 } => {803 use diagnostics::GenericParamsFromOuterItemLabel as Label;804 let static_or_const = match def_kind {805 DefKind::Static { .. } => {806 Some(diagnostics::GenericParamsFromOuterItemStaticOrConst::Static)807 }808 DefKind::Const { .. } => {809 Some(diagnostics::GenericParamsFromOuterItemStaticOrConst::Const)810 }811 _ => None,812 };813 let is_self =814 matches!(outer_res, Res::SelfTyParam { .. } | Res::SelfTyAlias { .. });815 let mut err = diagnostics::GenericParamsFromOuterItem {816 span,817 label: None,818 refer_to_type_directly: None,819 use_let: None,820 sugg: None,821 static_or_const,822 is_self,823 item: inner_item.as_ref().map(|(label_span, _, kind)| {824 diagnostics::GenericParamsFromOuterItemInnerItem {825 span: *label_span,826 descr: kind.descr().to_string(),827 is_self,828 }829 }),830 };831832 let sm = self.tcx.sess.source_map();833 // Note: do not early return for missing def_id here,834 // we still want to provide suggestions for `Res::SelfTyParam` and `Res::SelfTyAlias`.835 let def_id = match outer_res {836 Res::SelfTyParam { .. } => {837 err.label = Some(Label::SelfTyParam(span));838 None839 }840 Res::SelfTyAlias { alias_to: def_id, .. } => {841 err.label = Some(Label::SelfTyAlias(reduce_impl_span_to_impl_keyword(842 sm,843 self.def_span(def_id),844 )));845 err.refer_to_type_directly = current_self_ty846 .map(|snippet| diagnostics::UseTypeDirectly { span, snippet });847 None848 }849 Res::Def(DefKind::TyParam, def_id) => {850 err.label = Some(Label::TyParam(self.def_span(def_id)));851 Some(def_id)852 }853 Res::Def(DefKind::ConstParam, def_id) => {854 err.label = Some(Label::ConstParam(self.def_span(def_id)));855 Some(def_id)856 }857 _ => {858 bug!(859 "GenericParamsFromOuterItem should only be used with \860 Res::SelfTyParam, Res::SelfTyAlias, DefKind::TyParam or \861 DefKind::ConstParam"862 );863 }864 };865866 if let Some((_, item_span, ItemKind::Const(_))) = inner_item.as_ref() {867 err.use_let = Some(diagnostics::GenericParamsFromOuterItemUseLet {868 span: sm.span_until_whitespace(*item_span),869 });870 }871872 if let Some(def_id) = def_id873 && let HasGenericParams::Yes(span) = has_generic_params874 && !matches!(inner_item, Some((_, _, ItemKind::Delegation(..))))875 {876 let name = self.tcx.item_name(def_id);877 let (span, snippet) = if span.is_empty() {878 let snippet = format!("<{name}>");879 (span, snippet)880 } else {881 let span = sm.span_through_char(span, '<').shrink_to_hi();882 let snippet = format!("{name}, ");883 (span, snippet)884 };885 err.sugg = Some(diagnostics::GenericParamsFromOuterItemSugg { span, snippet });886 }887888 self.dcx().create_err(err)889 }890 ResolutionError::NameAlreadyUsedInParameterList(name, first_use_span) => {891 self.dcx().create_err(diagnostics::NameAlreadyUsedInParameterList {892 span,893 first_use_span,894 name,895 })896 }897 ResolutionError::MethodNotMemberOfTrait(method, trait_, candidate) => {898 self.dcx().create_err(diagnostics::MethodNotMemberOfTrait {899 span,900 method,901 trait_,902 sub: candidate.map(|c| diagnostics::AssociatedFnWithSimilarNameExists {903 span: method.span,904 candidate: c,905 }),906 })907 }908 ResolutionError::TypeNotMemberOfTrait(type_, trait_, candidate) => {909 self.dcx().create_err(diagnostics::TypeNotMemberOfTrait {910 span,911 type_,912 trait_,913 sub: candidate.map(|c| diagnostics::AssociatedTypeWithSimilarNameExists {914 span: type_.span,915 candidate: c,916 }),917 })918 }919 ResolutionError::ConstNotMemberOfTrait(const_, trait_, candidate) => {920 self.dcx().create_err(diagnostics::ConstNotMemberOfTrait {921 span,922 const_,923 trait_,924 sub: candidate.map(|c| diagnostics::AssociatedConstWithSimilarNameExists {925 span: const_.span,926 candidate: c,927 }),928 })929 }930 ResolutionError::VariableNotBoundInPattern(binding_error, parent_scope) => {931 let BindingError { name, target, origin, could_be_path } = binding_error;932933 let mut target_sp = target.iter().map(|pat| pat.span).collect::<Vec<_>>();934 target_sp.sort();935 target_sp.dedup();936 let mut origin_sp = origin.iter().map(|(span, _)| *span).collect::<Vec<_>>();937 origin_sp.sort();938 origin_sp.dedup();939940 let msp = MultiSpan::from_spans(target_sp.clone());941 let mut err = self.dcx().create_err(diagnostics::VariableIsNotBoundInAllPatterns {942 multispan: msp,943 name,944 });945 for sp in target_sp {946 err.subdiagnostic(diagnostics::PatternDoesntBindName { span: sp, name });947 }948 for sp in &origin_sp {949 err.subdiagnostic(diagnostics::VariableNotInAllPatterns { span: *sp });950 }951 let mut suggested_typo = false;952 if !target.iter().all(|pat| matches!(pat.kind, ast::PatKind::Ident(..)))953 && !origin.iter().all(|(_, pat)| matches!(pat.kind, ast::PatKind::Ident(..)))954 {955 // The check above is so that when we encounter `match foo { (a | b) => {} }`,956 // we don't suggest `(a | a) => {}`, which would never be what the user wants.957 let mut target_visitor = BindingVisitor::default();958 for pat in &target {959 target_visitor.visit_pat(pat);960 }961 target_visitor.identifiers.sort();962 target_visitor.identifiers.dedup();963 let mut origin_visitor = BindingVisitor::default();964 for (_, pat) in &origin {965 origin_visitor.visit_pat(pat);966 }967 origin_visitor.identifiers.sort();968 origin_visitor.identifiers.dedup();969 // Find if the binding could have been a typo970 if let Some(typo) =971 find_best_match_for_name(&target_visitor.identifiers, name.name, None)972 && !origin_visitor.identifiers.contains(&typo)973 {974 err.subdiagnostic(diagnostics::PatternBindingTypo {975 spans: origin_sp,976 typo,977 });978 suggested_typo = true;979 }980 }981 if could_be_path {982 let import_suggestions = self.lookup_import_candidates(983 name,984 Namespace::ValueNS,985 &parent_scope,986 &|res: Res| {987 matches!(988 res,989 Res::Def(990 DefKind::Ctor(CtorOf::Variant, CtorKind::Const)991 | DefKind::Ctor(CtorOf::Struct, CtorKind::Const)992 | DefKind::Const { .. }993 | DefKind::AssocConst { .. },994 _,995 )996 )997 },998 );9991000 if import_suggestions.is_empty() && !suggested_typo {1001 let kind_matches: [fn(DefKind) -> bool; 4] = [1002 |kind| matches!(kind, DefKind::Ctor(CtorOf::Variant, CtorKind::Const)),1003 |kind| matches!(kind, DefKind::Ctor(CtorOf::Struct, CtorKind::Const)),1004 |kind| matches!(kind, DefKind::Const { .. }),1005 |kind| matches!(kind, DefKind::AssocConst { .. }),1006 ];1007 let mut local_names = vec![];1008 self.add_module_candidates(1009 parent_scope.module,1010 &mut local_names,1011 &|res| matches!(res, Res::Def(_, _)),1012 None,1013 );1014 let local_names: FxHashSet<_> = local_names1015 .into_iter()1016 .filter_map(|s| match s.res {1017 Res::Def(_, def_id) => Some(def_id),1018 _ => None,1019 })1020 .collect();10211022 let mut local_suggestions = vec![];1023 let mut suggestions = vec![];1024 for matches_kind in kind_matches {1025 if let Some(suggestion) = self.early_lookup_typo_candidate(1026 ScopeSet::All(Namespace::ValueNS),1027 &parent_scope,1028 name,1029 &|res: Res| match res {1030 Res::Def(k, _) => matches_kind(k),1031 _ => false,1032 },1033 ) && let Res::Def(kind, mut def_id) = suggestion.res1034 {1035 if let DefKind::Ctor(_, _) = kind {1036 def_id = self.tcx.parent(def_id);1037 }1038 let kind = kind.descr(def_id);1039 if local_names.contains(&def_id) {1040 // The item is available in the current scope. Very likely to1041 // be a typo. Don't use the full path.1042 local_suggestions.push((1043 suggestion.candidate,1044 suggestion.candidate.to_string(),1045 kind,1046 ));1047 } else {1048 suggestions.push((1049 suggestion.candidate,1050 self.def_path_str(def_id),1051 kind,1052 ));1053 }1054 }1055 }1056 let suggestions = if !local_suggestions.is_empty() {1057 // There is at least one item available in the current scope that is a1058 // likely typo. We only show those.1059 local_suggestions1060 } else {1061 suggestions1062 };1063 for (name, sugg, kind) in suggestions {1064 err.span_suggestion_verbose(1065 span,1066 format!(1067 "you might have meant to use the similarly named {kind} `{name}`",1068 ),1069 sugg,1070 Applicability::MaybeIncorrect,1071 );1072 suggested_typo = true;1073 }1074 }1075 if import_suggestions.is_empty() && !suggested_typo {1076 let help_msg = format!(1077 "if you meant to match on a unit struct, unit variant or a `const` \1078 item, consider making the path in the pattern qualified: \1079 `path::to::ModOrType::{name}`",1080 );1081 err.span_help(span, help_msg);1082 }1083 show_candidates(1084 self.tcx,1085 &mut err,1086 Some(span),1087 &import_suggestions,1088 Instead::No,1089 FoundUse::Yes,1090 DiagMode::Pattern,1091 vec![],1092 "",1093 );1094 }1095 err1096 }1097 ResolutionError::VariableBoundWithDifferentMode(variable_name, first_binding_span) => {1098 self.dcx().create_err(diagnostics::VariableBoundWithDifferentMode {1099 span,1100 first_binding_span,1101 variable_name,1102 })1103 }1104 ResolutionError::IdentifierBoundMoreThanOnceInParameterList(identifier) => {1105 self.dcx().create_err(diagnostics::IdentifierBoundMoreThanOnceInParameterList {1106 span,1107 identifier,1108 })1109 }1110 ResolutionError::IdentifierBoundMoreThanOnceInSamePattern(identifier) => {1111 self.dcx().create_err(diagnostics::IdentifierBoundMoreThanOnceInSamePattern {1112 span,1113 identifier,1114 })1115 }1116 ResolutionError::UndeclaredLabel { name, suggestion } => {1117 let ((sub_reachable, sub_reachable_suggestion), sub_unreachable) = match suggestion1118 {1119 // A reachable label with a similar name exists.1120 Some((ident, true)) => (1121 (1122 Some(diagnostics::LabelWithSimilarNameReachable(ident.span)),1123 Some(diagnostics::TryUsingSimilarlyNamedLabel {1124 span,1125 ident_name: ident.name,1126 }),1127 ),1128 None,1129 ),1130 // An unreachable label with a similar name exists.1131 Some((ident, false)) => (1132 (None, None),1133 Some(diagnostics::UnreachableLabelWithSimilarNameExists {1134 ident_span: ident.span,1135 }),1136 ),1137 // No similarly-named labels exist.1138 None => ((None, None), None),1139 };1140 self.dcx().create_err(diagnostics::UndeclaredLabel {1141 span,1142 name,1143 sub_reachable,1144 sub_reachable_suggestion,1145 sub_unreachable,1146 })1147 }1148 ResolutionError::FailedToResolve { segment, label, suggestion, module, message } => {1149 let mut err = struct_span_code_err!(self.dcx(), span, E0433, "{message}");1150 err.span_label(span, label);11511152 if let Some((suggestions, msg, applicability)) = suggestion {1153 if suggestions.is_empty() {1154 err.help(msg);1155 return err;1156 }1157 err.multipart_suggestion(msg, suggestions, applicability);1158 }11591160 let module = match module {1161 Some(ModuleOrUniformRoot::Module(m)) if let Some(id) = m.opt_def_id() => id,1162 _ => CRATE_DEF_ID.to_def_id(),1163 };1164 self.find_cfg_stripped(&mut err, &segment, module);11651166 err1167 }1168 ResolutionError::CannotCaptureDynamicEnvironmentInFnItem => {1169 self.dcx().create_err(diagnostics::CannotCaptureDynamicEnvironmentInFnItem { span })1170 }1171 ResolutionError::AttemptToUseNonConstantValueInConstant {1172 ident,1173 suggestion,1174 current,1175 type_span,1176 } => {1177 // let foo =...1178 // ^^^ given this Span1179 // ------- get this Span to have an applicable suggestion11801181 // edit:1182 // only do this if the const and usage of the non-constant value are on the same line1183 // the further the two are apart, the higher the chance of the suggestion being wrong11841185 let sp = self1186 .tcx1187 .sess1188 .source_map()1189 .span_extend_to_prev_str(ident.span, current, true, false);11901191 let (with, with_label, without) = match sp {1192 Some(sp) if !self.tcx.sess.source_map().is_multiline(sp) => {1193 let sp = sp1194 .with_lo(BytePos(sp.lo().0 - (current.len() as u32)))1195 .until(ident.span);11961197 // Only suggest replacing the binding keyword if this is a simple1198 // binding.1199 //1200 // Note: this approach still incorrectly suggests for irrefutable1201 // patterns like `if let x = 1 { const { x } }`, since the text1202 // between `let` and the identifier is just whitespace.1203 // See tests/ui/consts/non-const-value-in-const-irrefutable-pat-binding.rs1204 let is_simple_binding =1205 self.tcx.sess.source_map().span_to_snippet(sp).is_ok_and(|snippet| {1206 let after_keyword = snippet[current.len()..].trim();1207 after_keyword.is_empty() || after_keyword == "mut"1208 });12091210 if is_simple_binding {1211 (1212 Some(diagnostics::AttemptToUseNonConstantValueInConstantWithSuggestion {1213 span: sp,1214 suggestion,1215 current,1216 type_span,1217 }),1218 Some(diagnostics::AttemptToUseNonConstantValueInConstantLabelWithSuggestion { span }),1219 None,1220 )1221 } else {1222 (1223 None,1224 Some(diagnostics::AttemptToUseNonConstantValueInConstantLabelWithSuggestion { span }),1225 None,1226 )1227 }1228 }1229 _ => (1230 None,1231 None,1232 Some(1233 diagnostics::AttemptToUseNonConstantValueInConstantWithoutSuggestion {1234 ident_span: ident.span,1235 suggestion,1236 },1237 ),1238 ),1239 };12401241 self.dcx().create_err(diagnostics::AttemptToUseNonConstantValueInConstant {1242 span,1243 with,1244 with_label,1245 without,1246 })1247 }1248 ResolutionError::BindingShadowsSomethingUnacceptable {1249 shadowing_binding,1250 name,1251 participle,1252 article,1253 shadowed_binding,1254 shadowed_binding_span,1255 } => self.dcx().create_err(diagnostics::BindingShadowsSomethingUnacceptable {1256 span,1257 shadowing_binding,1258 shadowed_binding,1259 article,1260 sub_suggestion: match (shadowing_binding, shadowed_binding) {1261 (1262 PatternSource::Match,1263 Res::Def(DefKind::Ctor(CtorOf::Variant | CtorOf::Struct, CtorKind::Fn), _),1264 ) => Some(diagnostics::BindingShadowsSomethingUnacceptableSuggestion {1265 span,1266 name,1267 }),1268 _ => None,1269 },1270 shadowed_binding_span,1271 participle,1272 name,1273 }),1274 ResolutionError::ForwardDeclaredGenericParam(param, reason) => match reason {1275 ForwardGenericParamBanReason::Default => {1276 self.dcx().create_err(diagnostics::ForwardDeclaredGenericParam { param, span })1277 }1278 ForwardGenericParamBanReason::ConstParamTy => self1279 .dcx()1280 .create_err(diagnostics::ForwardDeclaredGenericInConstParamTy { param, span }),1281 },1282 ResolutionError::ParamInTyOfConstParam { name } => {1283 self.dcx().create_err(diagnostics::ParamInTyOfConstParam { span, name })1284 }1285 ResolutionError::SelfInConstParam => {1286 self.dcx().create_err(diagnostics::SelfInConstGenericTy {1287 span,1288 enable_feature: self.tcx().sess.is_nightly_build(),1289 })1290 }1291 ResolutionError::ParamInNonTrivialAnonConst { is_gca, name, param_kind: is_type } => {1292 self.dcx().create_err(diagnostics::ParamInNonTrivialAnonConst {1293 span,1294 name,1295 param_kind: is_type,1296 help: self.tcx.sess.is_nightly_build()1297 && !self.tcx.features().min_generic_const_args(),1298 is_gca,1299 help_gca: is_gca,1300 help_suggest_gca: self.tcx.sess.is_nightly_build() && !is_gca,1301 })1302 }1303 ResolutionError::ParamInEnumDiscriminant { name, param_kind: is_type } => {1304 self.dcx().create_err(diagnostics::ParamInEnumDiscriminant {1305 span,1306 name,1307 param_kind: is_type,1308 })1309 }1310 ResolutionError::ForwardDeclaredSelf(reason) => match reason {1311 ForwardGenericParamBanReason::Default => {1312 self.dcx().create_err(diagnostics::SelfInGenericParamDefault { span })1313 }1314 ForwardGenericParamBanReason::ConstParamTy => self1315 .dcx()1316 .create_err(diagnostics::SelfInConstGenericTy { span, enable_feature: false }),1317 },1318 ResolutionError::UnreachableLabel { name, definition_span, suggestion } => {1319 let ((sub_suggestion_label, sub_suggestion), sub_unreachable_label) =1320 match suggestion {1321 // A reachable label with a similar name exists.1322 Some((ident, true)) => (1323 (1324 Some(diagnostics::UnreachableLabelSubLabel {1325 ident_span: ident.span,1326 }),1327 Some(diagnostics::UnreachableLabelSubSuggestion {1328 span,1329 // intentionally taking 'ident.name' instead of 'ident' itself, as this1330 // could be used in suggestion context1331 ident_name: ident.name,1332 }),1333 ),1334 None,1335 ),1336 // An unreachable label with a similar name exists.1337 Some((ident, false)) => (1338 (None, None),1339 Some(diagnostics::UnreachableLabelSubLabelUnreachable {1340 ident_span: ident.span,1341 }),1342 ),1343 // No similarly-named labels exist.1344 None => ((None, None), None),1345 };1346 self.dcx().create_err(diagnostics::UnreachableLabel {1347 span,1348 name,1349 definition_span,1350 sub_suggestion,1351 sub_suggestion_label,1352 sub_unreachable_label,1353 })1354 }1355 ResolutionError::TraitImplMismatch {1356 name,1357 kind,1358 code,1359 trait_item_span,1360 trait_path,1361 } => self1362 .dcx()1363 .create_err(diagnostics::TraitImplMismatch {1364 span,1365 name,1366 kind,1367 trait_path,1368 trait_item_span,1369 })1370 .with_code(code),1371 ResolutionError::TraitImplDuplicate { name, trait_item_span, old_span } => {1372 self.dcx().create_err(diagnostics::TraitImplDuplicate {1373 span,1374 name,1375 trait_item_span,1376 old_span,1377 })1378 }1379 ResolutionError::InvalidAsmSym => {1380 self.dcx().create_err(diagnostics::InvalidAsmSym { span })1381 }1382 ResolutionError::LowercaseSelf => {1383 self.dcx().create_err(diagnostics::LowercaseSelf { span })1384 }1385 ResolutionError::BindingInNeverPattern => {1386 self.dcx().create_err(diagnostics::BindingInNeverPattern { span })1387 }1388 }1389 }13901391 pub(crate) fn report_vis_error(1392 &mut self,1393 vis_resolution_error: VisResolutionError,1394 ) -> ErrorGuaranteed {1395 match vis_resolution_error {1396 VisResolutionError::Relative2018(span, path) => {1397 self.dcx().create_err(diagnostics::Relative2018 {1398 span,1399 path_span: path.span,1400 // intentionally converting to String, as the text would also be used as1401 // in suggestion context1402 path_str: pprust::path_to_string(&path),1403 })1404 }1405 VisResolutionError::AncestorOnly(span) => {1406 self.dcx().create_err(diagnostics::AncestorOnly(span))1407 }1408 VisResolutionError::FailedToResolve(span, segment, label, suggestion, message) => self1409 .into_struct_error(1410 span,1411 ResolutionError::FailedToResolve {1412 segment,1413 label,1414 suggestion,1415 module: None,1416 message,1417 },1418 ),1419 VisResolutionError::ExpectedFound(span, path_str, res) => {1420 self.dcx().create_err(diagnostics::ExpectedModuleFound { span, res, path_str })1421 }1422 VisResolutionError::Indeterminate(span) => {1423 self.dcx().create_err(diagnostics::Indeterminate(span))1424 }1425 VisResolutionError::ModuleOnly(span) => {1426 self.dcx().create_err(diagnostics::ModuleOnly(span))1427 }1428 }1429 .emit()1430 }14311432 pub(crate) fn def_path_str(&self, mut def_id: DefId) -> String {1433 // We can't use `def_path_str` in resolve.1434 let mut path = vec![def_id];1435 while let Some(parent) = self.tcx.opt_parent(def_id) {1436 def_id = parent;1437 path.push(def_id);1438 if def_id.is_top_level_module() {1439 break;1440 }1441 }1442 // We will only suggest importing directly if it is accessible through that path.1443 path.into_iter()1444 .rev()1445 .map(|def_id| {1446 self.tcx1447 .opt_item_name(def_id)1448 .map(|name| {1449 match (1450 def_id.is_top_level_module(),1451 def_id.is_local(),1452 self.tcx.sess.edition(),1453 ) {1454 (true, true, Edition::Edition2015) => String::new(),1455 (true, true, _) => kw::Crate.to_string(),1456 (true, false, _) | (false, _, _) => name.to_string(),1457 }1458 })1459 .unwrap_or_else(|| "_".to_string())1460 })1461 .collect::<Vec<String>>()1462 .join("::")1463 }14641465 pub(crate) fn add_scope_set_candidates(1466 &self,1467 suggestions: &mut Vec<TypoSuggestion>,1468 scope_set: ScopeSet<'ra>,1469 ps: &ParentScope<'ra>,1470 sp: Span,1471 filter_fn: &impl Fn(Res) -> bool,1472 ) {1473 let ctxt = Macros20NormalizedSyntaxContext::new(sp.ctxt());1474 self.cm().visit_scopes(scope_set, ps, ctxt, sp, None, |this, scope, use_prelude, _| {1475 match scope {1476 Scope::DeriveHelpers(expn_id) => {1477 let res = Res::NonMacroAttr(NonMacroAttrKind::DeriveHelper);1478 if filter_fn(res) {1479 suggestions.extend(this.helper_attrs.get(&expn_id).into_flat_iter().map(1480 |&(ident, orig_ident_span, _)| {1481 TypoSuggestion::new(ident.name, orig_ident_span, res)1482 },1483 ));1484 }1485 }1486 Scope::DeriveHelpersCompat => {1487 // Never recommend deprecated helper attributes.1488 }1489 Scope::MacroRules(macro_rules_scope) => {1490 if let MacroRulesScope::Def(macro_rules_def) = macro_rules_scope.get() {1491 let res = macro_rules_def.decl.res();1492 if filter_fn(res) {1493 suggestions.push(TypoSuggestion::new(1494 macro_rules_def.ident.name,1495 macro_rules_def.orig_ident_span,1496 res,1497 ))1498 }1499 }1500 }1501 Scope::ModuleNonGlobs(module, _) => {1502 this.add_module_candidates(module, suggestions, filter_fn, None);1503 }1504 Scope::ModuleGlobs(..) => {1505 // Already handled in `ModuleNonGlobs`.1506 }1507 Scope::MacroUsePrelude => {1508 suggestions.extend(this.macro_use_prelude.iter().filter_map(1509 |(name, binding)| {1510 let res = binding.res();1511 filter_fn(res).then_some(TypoSuggestion::typo_from_name(*name, res))1512 },1513 ));1514 }1515 Scope::BuiltinAttrs => {1516 let res = Res::NonMacroAttr(NonMacroAttrKind::Builtin(sym::dummy));1517 if filter_fn(res) {1518 suggestions.extend(1519 BUILTIN_ATTRIBUTES1520 .iter()1521 .map(|attr| TypoSuggestion::typo_from_name(*attr, res)),1522 );1523 }1524 }1525 Scope::ExternPreludeItems => {1526 // Add idents from both item and flag scopes.1527 suggestions.extend(this.extern_prelude.iter().filter_map(|(ident, entry)| {1528 let res = Res::Def(DefKind::Mod, CRATE_DEF_ID.to_def_id());1529 filter_fn(res).then_some(TypoSuggestion::new(ident.name, entry.span(), res))1530 }));1531 }1532 Scope::ExternPreludeFlags => {}1533 Scope::ToolAttributePrelude => {1534 let res = Res::NonMacroAttr(NonMacroAttrKind::Tool);1535 suggestions.extend(1536 this.registered_attr_tools1537 .iter()1538 .map(|ident| TypoSuggestion::new(ident.name, ident.span, res)),1539 );1540 }1541 Scope::StdLibPrelude => {1542 if let Some(prelude) = this.prelude {1543 let mut tmp_suggestions = Vec::new();1544 this.add_module_candidates(prelude, &mut tmp_suggestions, filter_fn, None);1545 suggestions.extend(1546 tmp_suggestions1547 .into_iter()1548 .filter(|s| use_prelude.into() || this.is_builtin_macro(s.res)),1549 );1550 }1551 }1552 Scope::BuiltinTypes => {1553 suggestions.extend(PrimTy::ALL.iter().filter_map(|prim_ty| {1554 let res = Res::PrimTy(*prim_ty);1555 filter_fn(res)1556 .then_some(TypoSuggestion::typo_from_name(prim_ty.name(), res))1557 }))1558 }1559 }15601561 ControlFlow::<()>::Continue(())1562 });1563 }15641565 /// Lookup typo candidate in scope for a macro or import.1566 fn early_lookup_typo_candidate(1567 &self,1568 scope_set: ScopeSet<'ra>,1569 parent_scope: &ParentScope<'ra>,1570 ident: Ident,1571 filter_fn: &impl Fn(Res) -> bool,1572 ) -> Option<TypoSuggestion> {1573 let mut suggestions = Vec::new();1574 self.add_scope_set_candidates(1575 &mut suggestions,1576 scope_set,1577 parent_scope,1578 ident.span,1579 filter_fn,1580 );15811582 // Make sure error reporting is deterministic.1583 suggestions.sort_by(|a, b| a.candidate.as_str().cmp(b.candidate.as_str()));15841585 match find_best_match_for_name(1586 &suggestions.iter().map(|suggestion| suggestion.candidate).collect::<Vec<Symbol>>(),1587 ident.name,1588 None,1589 ) {1590 Some(found) if found != ident.name => {1591 suggestions.into_iter().find(|suggestion| suggestion.candidate == found)1592 }1593 _ => None,1594 }1595 }15961597 fn lookup_import_candidates_from_module<FilterFn>(1598 &self,1599 lookup_ident: Ident,1600 namespace: Namespace,1601 parent_scope: &ParentScope<'ra>,1602 start_module: Module<'ra>,1603 crate_path: ThinVec<ast::PathSegment>,1604 filter_fn: FilterFn,1605 ) -> Vec<ImportSuggestion>1606 where1607 FilterFn: Fn(Res) -> bool,1608 {1609 let mut candidates = Vec::new();1610 let mut seen_modules = FxHashSet::default();1611 let start_did = start_module.def_id();1612 let mut worklist = vec![(1613 start_module,1614 ThinVec::<ast::PathSegment>::new(),1615 true,1616 start_did.is_local() || !self.tcx.is_doc_hidden(start_did),1617 true,1618 )];1619 let mut worklist_via_import = vec![];16201621 while let Some((in_module, path_segments, accessible, doc_visible, is_stable)) =1622 match worklist.pop() {1623 None => worklist_via_import.pop(),1624 Some(x) => Some(x),1625 }1626 {1627 let in_module_is_extern = !in_module.def_id().is_local();1628 in_module.for_each_child(self, |this, ident, orig_ident_span, ns, name_binding| {1629 // Avoid non-importable candidates.1630 if name_binding.is_assoc_item()1631 && !this.features.import_trait_associated_functions()1632 {1633 return;1634 }16351636 if ident.name == kw::Underscore {1637 return;1638 }16391640 let child_accessible =1641 accessible && this.is_accessible_from(name_binding.vis(), parent_scope.module);16421643 // do not venture inside inaccessible items of other crates1644 if in_module_is_extern && !child_accessible {1645 return;1646 }16471648 let via_import = name_binding.is_import() && !name_binding.is_extern_crate();16491650 // There is an assumption elsewhere that paths of variants are in the enum's1651 // declaration and not imported. With this assumption, the variant component is1652 // chopped and the rest of the path is assumed to be the enum's own path. For1653 // errors where a variant is used as the type instead of the enum, this causes1654 // funny looking invalid suggestions, i.e `foo` instead of `foo::MyEnum`.1655 if via_import && name_binding.is_possibly_imported_variant() {1656 return;1657 }16581659 // #90113: Do not count an inaccessible reexported item as a candidate.1660 if let DeclKind::Import { source_decl, .. } = name_binding.kind1661 && this.is_accessible_from(source_decl.vis(), parent_scope.module)1662 && !this.is_accessible_from(name_binding.vis(), parent_scope.module)1663 {1664 return;1665 }16661667 let res = name_binding.res();1668 let did = match res {1669 Res::Def(DefKind::Ctor(..), did) => this.tcx.opt_parent(did),1670 _ => res.opt_def_id(),1671 };1672 let child_doc_visible = doc_visible1673 && did.is_none_or(|did| did.is_local() || !this.tcx.is_doc_hidden(did));16741675 // collect results based on the filter function1676 // avoid suggesting anything from the same module in which we are resolving1677 // avoid suggesting anything with a hygienic name1678 if ident.name == lookup_ident.name1679 && ns == namespace1680 && in_module != parent_scope.module1681 && ident.ctxt.is_root()1682 && filter_fn(res)1683 {1684 // create the path1685 let mut segms = if lookup_ident.span.at_least_rust_2018() {1686 // crate-local absolute paths start with `crate::` in edition 20181687 // FIXME: may also be stabilized for Rust 2015 (Issues #45477, #44660)1688 crate_path.clone()1689 } else {1690 ThinVec::new()1691 };1692 segms.append(&mut path_segments.clone());16931694 segms.push(ast::PathSegment::from_ident(ident.orig(orig_ident_span)));1695 let path = Path { span: name_binding.span, segments: segms };16961697 if child_accessible1698 // Remove invisible match if exists1699 && let Some(idx) = candidates1700 .iter()1701 .position(|v: &ImportSuggestion| v.did == did && !v.accessible)1702 {1703 candidates.remove(idx);1704 }17051706 let is_stable = if is_stable1707 && let Some(did) = did1708 && this.is_stable(did, path.span)1709 {1710 true1711 } else {1712 false1713 };17141715 // Rreplace unstable suggestions if we meet a new stable one,1716 // and do nothing if any other situation. For example, if we1717 // meet `std::ops::Range` after `std::range::legacy::Range`,1718 // we will remove the latter and then insert the former.1719 if is_stable1720 && let Some(idx) = candidates1721 .iter()1722 .position(|v: &ImportSuggestion| v.did == did && !v.is_stable)1723 {1724 candidates.remove(idx);1725 }17261727 if candidates.iter().all(|v: &ImportSuggestion| v.did != did) {1728 // See if we're recommending TryFrom, TryInto, or FromIterator and add1729 // a note about editions1730 let note = if let Some(did) = did {1731 let requires_note = !did.is_local()1732 && find_attr!(1733 this.tcx,1734 did,1735 RustcDiagnosticItem(1736 sym::TryInto | sym::TryFrom | sym::FromIterator1737 )1738 );1739 requires_note.then(|| {1740 format!(1741 "'{}' is included in the prelude starting in Edition 2021",1742 path_names_to_string(&path)1743 )1744 })1745 } else {1746 None1747 };17481749 candidates.push(ImportSuggestion {1750 did,1751 descr: res.descr(),1752 path,1753 accessible: child_accessible,1754 doc_visible: child_doc_visible,1755 note,1756 via_import,1757 is_stable,1758 });1759 }1760 }17611762 // collect submodules to explore1763 if let Some(def_id) = name_binding.res().module_like_def_id() {1764 // form the path1765 let mut path_segments = path_segments.clone();1766 path_segments.push(ast::PathSegment::from_ident(ident.orig(orig_ident_span)));17671768 let alias_import = if let DeclKind::Import { import, .. } = name_binding.kind1769 && let ImportKind::ExternCrate { source: Some(_), .. } = import.kind1770 && import.parent_scope.expansion == parent_scope.expansion1771 {1772 true1773 } else {1774 false1775 };17761777 let is_extern_crate_that_also_appears_in_prelude =1778 name_binding.is_extern_crate() && lookup_ident.span.at_least_rust_2018();17791780 if !is_extern_crate_that_also_appears_in_prelude || alias_import {1781 // add the module to the lookup1782 if seen_modules.insert(def_id) {1783 if via_import { &mut worklist_via_import } else { &mut worklist }.push(1784 (1785 this.expect_module(def_id),1786 path_segments,1787 child_accessible,1788 child_doc_visible,1789 is_stable && this.is_stable(def_id, name_binding.span),1790 ),1791 );1792 }1793 }1794 }1795 })1796 }17971798 candidates1799 }18001801 fn is_stable(&self, did: DefId, span: Span) -> bool {1802 if did.is_local() {1803 return true;1804 }18051806 match self.tcx.lookup_stability(did) {1807 Some(Stability {1808 level: StabilityLevel::Unstable { implied_by, .. }, feature, ..1809 }) => {1810 if span.allows_unstable(feature) {1811 true1812 } else if self.features.enabled(feature) {1813 true1814 } else if let Some(implied_by) = implied_by1815 && self.features.enabled(implied_by)1816 {1817 true1818 } else {1819 false1820 }1821 }1822 Some(_) => true,1823 None => false,1824 }1825 }18261827 /// When name resolution fails, this method can be used to look up candidate1828 /// entities with the expected name. It allows filtering them using the1829 /// supplied predicate (which should be used to only accept the types of1830 /// definitions expected, e.g., traits). The lookup spans across all crates.1831 ///1832 /// N.B., the method does not look into imports, but this is not a problem,1833 /// since we report the definitions (thus, the de-aliased imports).1834 pub(crate) fn lookup_import_candidates<FilterFn>(1835 &self,1836 lookup_ident: Ident,1837 namespace: Namespace,1838 parent_scope: &ParentScope<'ra>,1839 filter_fn: FilterFn,1840 ) -> Vec<ImportSuggestion>1841 where1842 FilterFn: Fn(Res) -> bool,1843 {1844 let crate_path = thin_vec![ast::PathSegment::from_ident(Ident::with_dummy_span(kw::Crate))];1845 let mut suggestions = self.lookup_import_candidates_from_module(1846 lookup_ident,1847 namespace,1848 parent_scope,1849 self.graph_root.to_module(),1850 crate_path,1851 &filter_fn,1852 );18531854 if lookup_ident.span.at_least_rust_2018() {1855 for (ident, entry) in &self.extern_prelude {1856 if entry.span().from_expansion() {1857 // Idents are adjusted to the root context before being1858 // resolved in the extern prelude, so reporting this to the1859 // user is no help. This skips the injected1860 // `extern crate std` in the 2018 edition, which would1861 // otherwise cause duplicate suggestions.1862 continue;1863 }1864 let Some(crate_id) =1865 self.cstore_mut().maybe_process_path_extern(self.tcx, ident.name)1866 else {1867 continue;1868 };18691870 let crate_def_id = crate_id.as_def_id();1871 let crate_root = self.expect_module(crate_def_id);18721873 // Check if there's already an item in scope with the same name as the crate.1874 // If so, we have to disambiguate the potential import suggestions by making1875 // the paths *global* (i.e., by prefixing them with `::`).1876 let needs_disambiguation =1877 self.resolutions(parent_scope.module).iter().any(|(key, name_resolution)| {1878 if key.ns == TypeNS1879 && key.ident == *ident1880 && let Some(decl) = name_resolution.borrow_checked(self).best_decl()1881 {1882 match decl.res() {1883 // No disambiguation needed if the identically named item we1884 // found in scope actually refers to the crate in question.1885 Res::Def(_, def_id) => def_id != crate_def_id,1886 Res::PrimTy(_) => true,1887 _ => false,1888 }1889 } else {1890 false1891 }1892 });1893 let mut crate_path = ThinVec::new();1894 if needs_disambiguation {1895 crate_path.push(ast::PathSegment::path_root(rustc_span::DUMMY_SP));1896 }1897 crate_path.push(ast::PathSegment::from_ident(ident.orig(entry.span())));18981899 suggestions.extend(self.lookup_import_candidates_from_module(1900 lookup_ident,1901 namespace,1902 parent_scope,1903 crate_root,1904 crate_path,1905 &filter_fn,1906 ));1907 }1908 }19091910 suggestions.retain(|suggestion| suggestion.is_stable || self.tcx.sess.is_nightly_build());1911 suggestions1912 }19131914 pub(crate) fn unresolved_macro_suggestions(1915 &mut self,1916 err: &mut Diag<'_>,1917 macro_kind: MacroKind,1918 parent_scope: &ParentScope<'ra>,1919 ident: Ident,1920 krate: &Crate,1921 sugg_span: Option<Span>,1922 ) {1923 // Bring all unused `derive` macros into `macro_map` so we ensure they can be used for1924 // suggestions.1925 self.register_macros_for_all_crates();19261927 let is_expected =1928 &|res: Res| res.macro_kinds().is_some_and(|k| k.contains(macro_kind.into()));1929 let suggestion = self.early_lookup_typo_candidate(1930 ScopeSet::Macro(macro_kind),1931 parent_scope,1932 ident,1933 is_expected,1934 );1935 self.add_typo_suggestion(err, suggestion, ident.span);1936 self.detect_derive_attribute(err, ident, parent_scope, sugg_span);19371938 let import_suggestions =1939 self.lookup_import_candidates(ident, Namespace::MacroNS, parent_scope, is_expected);1940 let (span, found_use) = match parent_scope.module.nearest_parent_mod_node_id() {1941 DUMMY_NODE_ID => (None, FoundUse::No),1942 node_id => UsePlacementFinder::check(krate, node_id),1943 };1944 show_candidates(1945 self.tcx,1946 err,1947 span,1948 &import_suggestions,1949 Instead::No,1950 found_use,1951 DiagMode::Normal,1952 vec![],1953 "",1954 );19551956 if macro_kind == MacroKind::Bang && ident.name == sym::macro_rules {1957 let label_span = ident.span.shrink_to_hi();1958 let mut spans = MultiSpan::from_span(label_span);1959 spans.push_span_label(label_span, "put a macro name here");1960 err.subdiagnostic(MaybeMissingMacroRulesName { spans });1961 return;1962 }19631964 if macro_kind == MacroKind::Derive && (ident.name == sym::Send || ident.name == sym::Sync) {1965 err.subdiagnostic(ExplicitUnsafeTraits { span: ident.span, ident });1966 return;1967 }19681969 let unused_macro = self.unused_macros.iter().find_map(|(def_id, (_, unused_ident))| {1970 if unused_ident.name == ident.name { Some((def_id, unused_ident)) } else { None }1971 });19721973 if let Some((def_id, unused_ident)) = unused_macro {1974 let scope = self.local_macro_def_scopes[&def_id];1975 let parent_nearest = parent_scope.module.nearest_parent_mod();1976 let unused_macro_kinds = self.local_macro_map[def_id].macro_kinds();1977 if !unused_macro_kinds.contains(macro_kind.into()) {1978 match macro_kind {1979 MacroKind::Bang => {1980 err.subdiagnostic(MacroRulesNot::Func { span: unused_ident.span, ident });1981 }1982 MacroKind::Attr => {1983 err.subdiagnostic(MacroRulesNot::Attr { span: unused_ident.span, ident });1984 }1985 MacroKind::Derive => {1986 err.subdiagnostic(MacroRulesNot::Derive { span: unused_ident.span, ident });1987 }1988 }1989 return;1990 }1991 if Some(parent_nearest.to_def_id()) == scope.opt_def_id() {1992 err.subdiagnostic(MacroDefinedLater { span: unused_ident.span });1993 err.subdiagnostic(MacroSuggMovePosition { span: ident.span, ident });1994 return;1995 }1996 }19971998 if ident.name == kw::Default1999 && let ModuleKind::Def(DefKind::Enum, def_id, _, _) = parent_scope.module.kind2000 {
Findings
✓ No findings reported for this file.