diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-17 12:18:25 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-17 12:18:25 +0000 |
commit | 5363f350887b1e5b5dd21a86f88c8af9d7fea6da (patch) | |
tree | 35ca005eb6e0e9a1ba3bb5dbc033209ad445dc17 /compiler/rustc_resolve | |
parent | Adding debian version 1.66.0+dfsg1-1. (diff) | |
download | rustc-5363f350887b1e5b5dd21a86f88c8af9d7fea6da.tar.xz rustc-5363f350887b1e5b5dd21a86f88c8af9d7fea6da.zip |
Merging upstream version 1.67.1+dfsg1.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r-- | compiler/rustc_resolve/Cargo.toml | 8 | ||||
-rw-r--r-- | compiler/rustc_resolve/src/build_reduced_graph.rs | 125 | ||||
-rw-r--r-- | compiler/rustc_resolve/src/check_unused.rs | 8 | ||||
-rw-r--r-- | compiler/rustc_resolve/src/def_collector.rs | 29 | ||||
-rw-r--r-- | compiler/rustc_resolve/src/diagnostics.rs | 649 | ||||
-rw-r--r-- | compiler/rustc_resolve/src/effective_visibilities.rs | 227 | ||||
-rw-r--r-- | compiler/rustc_resolve/src/errors.rs | 474 | ||||
-rw-r--r-- | compiler/rustc_resolve/src/ident.rs | 8 | ||||
-rw-r--r-- | compiler/rustc_resolve/src/imports.rs | 220 | ||||
-rw-r--r-- | compiler/rustc_resolve/src/late.rs | 163 | ||||
-rw-r--r-- | compiler/rustc_resolve/src/late/diagnostics.rs | 186 | ||||
-rw-r--r-- | compiler/rustc_resolve/src/lib.rs | 72 | ||||
-rw-r--r-- | compiler/rustc_resolve/src/macros.rs | 2 |
13 files changed, 1320 insertions, 851 deletions
diff --git a/compiler/rustc_resolve/Cargo.toml b/compiler/rustc_resolve/Cargo.toml index d66db1d7a..7c3a0f8f2 100644 --- a/compiler/rustc_resolve/Cargo.toml +++ b/compiler/rustc_resolve/Cargo.toml @@ -7,10 +7,8 @@ edition = "2021" [dependencies] bitflags = "1.2.1" -tracing = "0.1" -rustc_ast = { path = "../rustc_ast" } rustc_arena = { path = "../rustc_arena" } -rustc_middle = { path = "../rustc_middle" } +rustc_ast = { path = "../rustc_ast" } rustc_ast_pretty = { path = "../rustc_ast_pretty" } rustc_attr = { path = "../rustc_attr" } rustc_data_structures = { path = "../rustc_data_structures" } @@ -19,8 +17,12 @@ rustc_expand = { path = "../rustc_expand" } rustc_feature = { path = "../rustc_feature" } rustc_hir = { path = "../rustc_hir" } rustc_index = { path = "../rustc_index" } +rustc_macros = { path = "../rustc_macros" } rustc_metadata = { path = "../rustc_metadata" } +rustc_middle = { path = "../rustc_middle" } rustc_query_system = { path = "../rustc_query_system" } rustc_session = { path = "../rustc_session" } rustc_span = { path = "../rustc_span" } smallvec = { version = "1.8.1", features = ["union", "may_dangle"] } +thin-vec = "0.2.8" +tracing = "0.1" diff --git a/compiler/rustc_resolve/src/build_reduced_graph.rs b/compiler/rustc_resolve/src/build_reduced_graph.rs index a17793ecd..9c90d67aa 100644 --- a/compiler/rustc_resolve/src/build_reduced_graph.rs +++ b/compiler/rustc_resolve/src/build_reduced_graph.rs @@ -30,7 +30,7 @@ use rustc_middle::metadata::ModChild; use rustc_middle::ty::{self, DefIdTree}; use rustc_session::cstore::CrateStore; use rustc_span::hygiene::{ExpnId, LocalExpnId, MacroKind}; -use rustc_span::source_map::{respan, Spanned}; +use rustc_span::source_map::respan; use rustc_span::symbol::{kw, sym, Ident, Symbol}; use rustc_span::Span; @@ -56,21 +56,7 @@ impl<'a, Id: Into<DefId>> ToNameBinding<'a> impl<'a, Id: Into<DefId>> ToNameBinding<'a> for (Res, ty::Visibility<Id>, Span, LocalExpnId) { fn to_name_binding(self, arenas: &'a ResolverArenas<'a>) -> &'a NameBinding<'a> { arenas.alloc_name_binding(NameBinding { - kind: NameBindingKind::Res(self.0, false), - ambiguity: None, - vis: self.1.to_def_id(), - span: self.2, - expansion: self.3, - }) - } -} - -struct IsMacroExport; - -impl<'a> ToNameBinding<'a> for (Res, ty::Visibility, Span, LocalExpnId, IsMacroExport) { - fn to_name_binding(self, arenas: &'a ResolverArenas<'a>) -> &'a NameBinding<'a> { - arenas.alloc_name_binding(NameBinding { - kind: NameBindingKind::Res(self.0, true), + kind: NameBindingKind::Res(self.0), ambiguity: None, vis: self.1.to_def_id(), span: self.2, @@ -218,7 +204,9 @@ impl<'a> Resolver<'a> { } pub(crate) fn build_reduced_graph_external(&mut self, module: Module<'a>) { - for child in self.cstore().module_children_untracked(module.def_id(), self.session) { + for child in + Vec::from_iter(self.cstore().module_children_untracked(module.def_id(), self.session)) + { let parent_scope = ParentScope::module(module, self); BuildReducedGraphVisitor { r: self, parent_scope } .build_reduced_graph_for_external_crate_res(child); @@ -343,10 +331,12 @@ impl<'a, 'b> BuildReducedGraphVisitor<'a, 'b> { .iter() .map(|field| respan(field.span, field.ident.map_or(kw::Empty, |ident| ident.name))) .collect(); - self.insert_field_names(def_id, field_names); + self.r.field_names.insert(def_id, field_names); } - fn insert_field_names(&mut self, def_id: DefId, field_names: Vec<Spanned<Symbol>>) { + fn insert_field_names_extern(&mut self, def_id: DefId) { + let field_names = + self.r.cstore().struct_field_names_untracked(def_id, self.r.session).collect(); self.r.field_names.insert(def_id, field_names); } @@ -364,7 +354,6 @@ impl<'a, 'b> BuildReducedGraphVisitor<'a, 'b> { module_path: Vec<Segment>, kind: ImportKind<'a>, span: Span, - id: NodeId, item: &ast::Item, root_span: Span, root_id: NodeId, @@ -377,7 +366,6 @@ impl<'a, 'b> BuildReducedGraphVisitor<'a, 'b> { module_path, imported_module: Cell::new(None), span, - id, use_span: item.span, use_span_with_attributes: item.span_with_attributes(), has_attributes: !item.attrs.is_empty(), @@ -457,19 +445,13 @@ impl<'a, 'b> BuildReducedGraphVisitor<'a, 'b> { prefix.is_empty() || prefix.len() == 1 && prefix[0].ident.name == kw::PathRoot }; match use_tree.kind { - ast::UseTreeKind::Simple(rename, id1, id2) => { + ast::UseTreeKind::Simple(rename) => { let mut ident = use_tree.ident(); let mut module_path = prefix; let mut source = module_path.pop().unwrap(); let mut type_ns_only = false; self.r.visibilities.insert(self.r.local_def_id(id), vis); - if id1 != ast::DUMMY_NODE_ID { - self.r.visibilities.insert(self.r.local_def_id(id1), vis); - } - if id2 != ast::DUMMY_NODE_ID { - self.r.visibilities.insert(self.r.local_def_id(id2), vis); - } if nested { // Correctly handle `self` @@ -485,9 +467,11 @@ impl<'a, 'b> BuildReducedGraphVisitor<'a, 'b> { } // Replace `use foo::{ self };` with `use foo;` + let self_span = source.ident.span; source = module_path.pop().unwrap(); if rename.is_none() { - ident = source.ident; + // Keep the span of `self`, but the name of `foo` + ident = Ident { name: source.ident.name, span: self_span }; } } } else { @@ -574,27 +558,19 @@ impl<'a, 'b> BuildReducedGraphVisitor<'a, 'b> { }, type_ns_only, nested, - additional_ids: (id1, id2), + id, }; - self.add_import( - module_path, - kind, - use_tree.span, - id, - item, - root_span, - item.id, - vis, - ); + self.add_import(module_path, kind, use_tree.span, item, root_span, item.id, vis); } ast::UseTreeKind::Glob => { let kind = ImportKind::Glob { is_prelude: self.r.session.contains_name(&item.attrs, sym::prelude_import), max_vis: Cell::new(None), + id, }; self.r.visibilities.insert(self.r.local_def_id(id), vis); - self.add_import(prefix, kind, use_tree.span, id, item, root_span, item.id, vis); + self.add_import(prefix, kind, use_tree.span, item, root_span, item.id, vis); } ast::UseTreeKind::Nested(ref items) => { // Ensure there is at most one `self` in the list @@ -638,11 +614,7 @@ impl<'a, 'b> BuildReducedGraphVisitor<'a, 'b> { let new_span = prefix[prefix.len() - 1].ident.span; let tree = ast::UseTree { prefix: ast::Path::from_ident(Ident::new(kw::SelfLower, new_span)), - kind: ast::UseTreeKind::Simple( - Some(Ident::new(kw::Underscore, new_span)), - ast::DUMMY_NODE_ID, - ast::DUMMY_NODE_ID, - ), + kind: ast::UseTreeKind::Simple(Some(Ident::new(kw::Underscore, new_span))), span: use_tree.span, }; self.build_reduced_graph_for_use_tree( @@ -768,7 +740,7 @@ impl<'a, 'b> BuildReducedGraphVisitor<'a, 'b> { // If this is a tuple or unit struct, define a name // in the value namespace as well. - if let Some(ctor_node_id) = vdata.ctor_id() { + if let Some((ctor_kind, ctor_node_id)) = CtorKind::from_ast(vdata) { // If the structure is marked as non_exhaustive then lower the visibility // to within the crate. let mut ctor_vis = if vis.is_public() @@ -794,10 +766,8 @@ impl<'a, 'b> BuildReducedGraphVisitor<'a, 'b> { ret_fields.push(field_vis.to_def_id()); } let ctor_def_id = self.r.local_def_id(ctor_node_id); - let ctor_res = Res::Def( - DefKind::Ctor(CtorOf::Struct, CtorKind::from_ast(vdata)), - ctor_def_id.to_def_id(), - ); + let ctor_res = + Res::Def(DefKind::Ctor(CtorOf::Struct, ctor_kind), ctor_def_id.to_def_id()); self.r.define(parent, ident, ValueNS, (ctor_res, ctor_vis, sp, expansion)); self.r.visibilities.insert(ctor_def_id, ctor_vis); @@ -881,9 +851,8 @@ impl<'a, 'b> BuildReducedGraphVisitor<'a, 'b> { }) .unwrap_or((true, None, self.r.dummy_binding)); let import = self.r.arenas.alloc_import(Import { - kind: ImportKind::ExternCrate { source: orig_name, target: ident }, + kind: ImportKind::ExternCrate { source: orig_name, target: ident, id: item.id }, root_id: item.id, - id: item.id, parent_scope: self.parent_scope, imported_module: Cell::new(module), has_attributes: !item.attrs.is_empty(), @@ -1019,10 +988,7 @@ impl<'a, 'b> BuildReducedGraphVisitor<'a, 'b> { let cstore = self.r.cstore(); match res { Res::Def(DefKind::Struct, def_id) => { - let field_names = - cstore.struct_field_names_untracked(def_id, self.r.session).collect(); - let ctor = cstore.ctor_def_id_and_kind_untracked(def_id); - if let Some((ctor_def_id, ctor_kind)) = ctor { + if let Some((ctor_kind, ctor_def_id)) = cstore.ctor_untracked(def_id) { let ctor_res = Res::Def(DefKind::Ctor(CtorOf::Struct, ctor_kind), ctor_def_id); let ctor_vis = cstore.visibility_untracked(ctor_def_id); let field_visibilities = @@ -1031,13 +997,9 @@ impl<'a, 'b> BuildReducedGraphVisitor<'a, 'b> { .struct_constructors .insert(def_id, (ctor_res, ctor_vis, field_visibilities)); } - self.insert_field_names(def_id, field_names); - } - Res::Def(DefKind::Union, def_id) => { - let field_names = - cstore.struct_field_names_untracked(def_id, self.r.session).collect(); - self.insert_field_names(def_id, field_names); + self.insert_field_names_extern(def_id) } + Res::Def(DefKind::Union, def_id) => self.insert_field_names_extern(def_id), Res::Def(DefKind::AssocFn, def_id) => { if cstore.fn_has_self_parameter_untracked(def_id, self.r.session) { self.r.has_self.insert(def_id); @@ -1118,7 +1080,6 @@ impl<'a, 'b> BuildReducedGraphVisitor<'a, 'b> { this.r.arenas.alloc_import(Import { kind: ImportKind::MacroUse, root_id: item.id, - id: item.id, parent_scope: this.parent_scope, imported_module: Cell::new(Some(ModuleOrUniformRoot::Module(module))), use_span_with_attributes: item.span_with_attributes(), @@ -1278,8 +1239,22 @@ impl<'a, 'b> BuildReducedGraphVisitor<'a, 'b> { let binding = (res, vis, span, expansion).to_name_binding(self.r.arenas); self.r.set_binding_parent_module(binding, parent_scope.module); if is_macro_export { - let module = self.r.graph_root; - self.r.define(module, ident, MacroNS, (res, vis, span, expansion, IsMacroExport)); + let import = self.r.arenas.alloc_import(Import { + kind: ImportKind::MacroExport, + root_id: item.id, + parent_scope: self.parent_scope, + imported_module: Cell::new(None), + has_attributes: false, + use_span_with_attributes: span, + use_span: span, + root_span: span, + span: span, + module_path: Vec::new(), + vis: Cell::new(Some(vis)), + used: Cell::new(true), + }); + let import_binding = self.r.import(binding, import); + self.r.define(self.r.graph_root, ident, MacroNS, import_binding); } else { self.r.check_reserved_macro_name(ident, res); self.insert_unused_macro(ident, def_id, item.id, &rule_spans); @@ -1526,20 +1501,16 @@ impl<'a, 'b> Visitor<'b> for BuildReducedGraphVisitor<'a, 'b> { }; // Define a constructor name in the value namespace. - // Braced variants, unlike structs, generate unusable names in - // value namespace, they are reserved for possible future use. - // It's ok to use the variant's id as a ctor id since an - // error will be reported on any use of such resolution anyway. - let ctor_node_id = variant.data.ctor_id().unwrap_or(variant.id); - let ctor_def_id = self.r.local_def_id(ctor_node_id); - let ctor_kind = CtorKind::from_ast(&variant.data); - let ctor_res = Res::Def(DefKind::Ctor(CtorOf::Variant, ctor_kind), ctor_def_id.to_def_id()); - self.r.define(parent, ident, ValueNS, (ctor_res, ctor_vis, variant.span, expn_id)); - if ctor_def_id != def_id { + if let Some((ctor_kind, ctor_node_id)) = CtorKind::from_ast(&variant.data) { + let ctor_def_id = self.r.local_def_id(ctor_node_id); + let ctor_res = + Res::Def(DefKind::Ctor(CtorOf::Variant, ctor_kind), ctor_def_id.to_def_id()); + self.r.define(parent, ident, ValueNS, (ctor_res, ctor_vis, variant.span, expn_id)); self.r.visibilities.insert(ctor_def_id, ctor_vis); } + // Record field names for error reporting. - self.insert_field_names_local(ctor_def_id.to_def_id(), &variant.data); + self.insert_field_names_local(def_id.to_def_id(), &variant.data); visit::walk_variant(self, variant); } diff --git a/compiler/rustc_resolve/src/check_unused.rs b/compiler/rustc_resolve/src/check_unused.rs index 01c3801f2..32fb5e182 100644 --- a/compiler/rustc_resolve/src/check_unused.rs +++ b/compiler/rustc_resolve/src/check_unused.rs @@ -234,7 +234,7 @@ impl Resolver<'_> { if !import.span.is_dummy() { self.lint_buffer.buffer_lint( MACRO_USE_EXTERN_CRATE, - import.id, + import.root_id, import.span, "deprecated `#[macro_use]` attribute used to \ import macros should be replaced at use sites \ @@ -244,13 +244,13 @@ impl Resolver<'_> { } } } - ImportKind::ExternCrate { .. } => { - let def_id = self.local_def_id(import.id); + ImportKind::ExternCrate { id, .. } => { + let def_id = self.local_def_id(id); self.maybe_unused_extern_crates.push((def_id, import.span)); } ImportKind::MacroUse => { let msg = "unused `#[macro_use]` import"; - self.lint_buffer.buffer_lint(UNUSED_IMPORTS, import.id, import.span, msg); + self.lint_buffer.buffer_lint(UNUSED_IMPORTS, import.root_id, import.span, msg); } _ => {} } diff --git a/compiler/rustc_resolve/src/def_collector.rs b/compiler/rustc_resolve/src/def_collector.rs index d36e0f61d..2764a6c28 100644 --- a/compiler/rustc_resolve/src/def_collector.rs +++ b/compiler/rustc_resolve/src/def_collector.rs @@ -118,8 +118,8 @@ impl<'a, 'b> visit::Visitor<'a> for DefCollector<'a, 'b> { match i.kind { ItemKind::Struct(ref struct_def, _) | ItemKind::Union(ref struct_def, _) => { // If this is a unit or tuple-like struct, register the constructor. - if let Some(ctor_hir_id) = struct_def.ctor_id() { - this.create_def(ctor_hir_id, DefPathData::Ctor, i.span); + if let Some(ctor_node_id) = struct_def.ctor_node_id() { + this.create_def(ctor_node_id, DefPathData::Ctor, i.span); } } _ => {} @@ -131,12 +131,9 @@ impl<'a, 'b> visit::Visitor<'a> for DefCollector<'a, 'b> { fn visit_fn(&mut self, fn_kind: FnKind<'a>, span: Span, _: NodeId) { if let FnKind::Fn(_, _, sig, _, generics, body) = fn_kind { - if let Async::Yes { closure_id, return_impl_trait_id, .. } = sig.header.asyncness { + if let Async::Yes { closure_id, .. } = sig.header.asyncness { self.visit_generics(generics); - let return_impl_trait_id = - self.create_def(return_impl_trait_id, DefPathData::ImplTrait, span); - // For async functions, we need to create their inner defs inside of a // closure to match their desugared representation. Besides that, // we must mirror everything that `visit::walk_fn` below does. @@ -144,9 +141,7 @@ impl<'a, 'b> visit::Visitor<'a> for DefCollector<'a, 'b> { for param in &sig.decl.inputs { self.visit_param(param); } - self.with_parent(return_impl_trait_id, |this| { - this.visit_fn_ret_ty(&sig.decl.output) - }); + self.visit_fn_ret_ty(&sig.decl.output); // If this async fn has no body (i.e. it's an async fn signature in a trait) // then the closure_def will never be used, and we should avoid generating a // def-id for it. @@ -163,14 +158,6 @@ impl<'a, 'b> visit::Visitor<'a> for DefCollector<'a, 'b> { fn visit_use_tree(&mut self, use_tree: &'a UseTree, id: NodeId, _nested: bool) { self.create_def(id, DefPathData::Use, use_tree.span); - match use_tree.kind { - UseTreeKind::Simple(_, id1, id2) => { - self.create_def(id1, DefPathData::Use, use_tree.prefix.span); - self.create_def(id2, DefPathData::Use, use_tree.prefix.span); - } - UseTreeKind::Glob => (), - UseTreeKind::Nested(..) => {} - } visit::walk_use_tree(self, use_tree, id); } @@ -196,8 +183,8 @@ impl<'a, 'b> visit::Visitor<'a> for DefCollector<'a, 'b> { } let def = self.create_def(v.id, DefPathData::TypeNs(v.ident.name), v.span); self.with_parent(def, |this| { - if let Some(ctor_hir_id) = v.data.ctor_id() { - this.create_def(ctor_hir_id, DefPathData::Ctor, v.span); + if let Some(ctor_node_id) = v.data.ctor_node_id() { + this.create_def(ctor_node_id, DefPathData::Ctor, v.span); } visit::walk_variant(this, v) }); @@ -262,11 +249,11 @@ impl<'a, 'b> visit::Visitor<'a> for DefCollector<'a, 'b> { fn visit_expr(&mut self, expr: &'a Expr) { let parent_def = match expr.kind { ExprKind::MacCall(..) => return self.visit_macro_invoc(expr.id), - ExprKind::Closure(_, _, asyncness, ..) => { + ExprKind::Closure(ref closure) => { // Async closures desugar to closures inside of closures, so // we must create two defs. let closure_def = self.create_def(expr.id, DefPathData::ClosureExpr, expr.span); - match asyncness { + match closure.asyncness { Async::Yes { closure_id, .. } => { self.create_def(closure_id, DefPathData::ClosureExpr, expr.span) } diff --git a/compiler/rustc_resolve/src/diagnostics.rs b/compiler/rustc_resolve/src/diagnostics.rs index 5d868ebec..e392df6c5 100644 --- a/compiler/rustc_resolve/src/diagnostics.rs +++ b/compiler/rustc_resolve/src/diagnostics.rs @@ -25,7 +25,9 @@ use rustc_span::lev_distance::find_best_match_for_name; use rustc_span::source_map::SourceMap; use rustc_span::symbol::{kw, sym, Ident, Symbol}; use rustc_span::{BytePos, Span, SyntaxContext}; +use thin_vec::ThinVec; +use crate::errors as errs; use crate::imports::{Import, ImportKind, ImportResolver}; use crate::late::{PatternSource, Rib}; use crate::path_names_to_string; @@ -58,16 +60,32 @@ pub(crate) enum SuggestionTarget { #[derive(Debug)] pub(crate) struct TypoSuggestion { pub candidate: Symbol, + /// The source location where the name is defined; None if the name is not defined + /// in source e.g. primitives + pub span: Option<Span>, pub res: Res, pub target: SuggestionTarget, } impl TypoSuggestion { - pub(crate) fn typo_from_res(candidate: Symbol, res: Res) -> TypoSuggestion { - Self { candidate, res, target: SuggestionTarget::SimilarlyNamed } + pub(crate) fn typo_from_ident(ident: Ident, res: Res) -> TypoSuggestion { + Self { + candidate: ident.name, + span: Some(ident.span), + res, + target: SuggestionTarget::SimilarlyNamed, + } + } + pub(crate) fn typo_from_name(candidate: Symbol, res: Res) -> TypoSuggestion { + Self { candidate, span: None, res, target: SuggestionTarget::SimilarlyNamed } } - pub(crate) fn single_item_from_res(candidate: Symbol, res: Res) -> TypoSuggestion { - Self { candidate, res, target: SuggestionTarget::SingleItem } + pub(crate) fn single_item_from_ident(ident: Ident, res: Res) -> TypoSuggestion { + Self { + candidate: ident.name, + span: Some(ident.span), + res, + target: SuggestionTarget::SingleItem, + } } } @@ -174,12 +192,12 @@ impl<'a> Resolver<'a> { ModuleKind::Block => "block", }; - let old_noun = match old_binding.is_import() { + let old_noun = match old_binding.is_import_user_facing() { true => "import", false => "definition", }; - let new_participle = match new_binding.is_import() { + let new_participle = match new_binding.is_import_user_facing() { true => "imported", false => "defined", }; @@ -210,7 +228,7 @@ impl<'a> Resolver<'a> { true => struct_span_err!(self.session, span, E0254, "{}", msg), false => struct_span_err!(self.session, span, E0260, "{}", msg), }, - _ => match (old_binding.is_import(), new_binding.is_import()) { + _ => match (old_binding.is_import_user_facing(), new_binding.is_import_user_facing()) { (false, false) => struct_span_err!(self.session, span, E0428, "{}", msg), (true, true) => struct_span_err!(self.session, span, E0252, "{}", msg), _ => struct_span_err!(self.session, span, E0255, "{}", msg), @@ -225,21 +243,27 @@ impl<'a> Resolver<'a> { )); err.span_label(span, format!("`{}` re{} here", name, new_participle)); - err.span_label( - self.session.source_map().guess_head_span(old_binding.span), - format!("previous {} of the {} `{}` here", old_noun, old_kind, name), - ); + if !old_binding.span.is_dummy() && old_binding.span != span { + err.span_label( + self.session.source_map().guess_head_span(old_binding.span), + format!("previous {} of the {} `{}` here", old_noun, old_kind, name), + ); + } // See https://github.com/rust-lang/rust/issues/32354 use NameBindingKind::Import; + let can_suggest = |binding: &NameBinding<'_>, import: &self::Import<'_>| { + !binding.span.is_dummy() + && !matches!(import.kind, ImportKind::MacroUse | ImportKind::MacroExport) + }; let import = match (&new_binding.kind, &old_binding.kind) { // If there are two imports where one or both have attributes then prefer removing the // import without attributes. (Import { import: new, .. }, Import { import: old, .. }) if { - !new_binding.span.is_dummy() - && !old_binding.span.is_dummy() - && (new.has_attributes || old.has_attributes) + (new.has_attributes || old.has_attributes) + && can_suggest(old_binding, old) + && can_suggest(new_binding, new) } => { if old.has_attributes { @@ -249,10 +273,10 @@ impl<'a> Resolver<'a> { } } // Otherwise prioritize the new binding. - (Import { import, .. }, other) if !new_binding.span.is_dummy() => { + (Import { import, .. }, other) if can_suggest(new_binding, import) => { Some((import, new_binding.span, other.is_import())) } - (other, Import { import, .. }) if !old_binding.span.is_dummy() => { + (other, Import { import, .. }) if can_suggest(old_binding, import) => { Some((import, old_binding.span, other.is_import())) } _ => None, @@ -337,7 +361,7 @@ impl<'a> Resolver<'a> { } } } - ImportKind::ExternCrate { source, target } => { + ImportKind::ExternCrate { source, target, .. } => { suggestion = Some(format!( "extern crate {} as {};", source.unwrap_or(target.name), @@ -490,7 +514,7 @@ impl<'a> Resolver<'a> { if let Some(binding) = resolution.borrow().binding { let res = binding.res(); if filter_fn(res) && ctxt.map_or(true, |ctxt| ctxt == key.ident.span.ctxt()) { - names.push(TypoSuggestion::typo_from_res(key.ident.name, res)); + names.push(TypoSuggestion::typo_from_ident(key.ident, res)); } } } @@ -575,78 +599,41 @@ impl<'a> Resolver<'a> { err } - ResolutionError::NameAlreadyUsedInParameterList(name, first_use_span) => { - let mut err = struct_span_err!( - self.session, - span, - E0403, - "the name `{}` is already used for a generic \ - parameter in this item's generic parameters", - name, - ); - err.span_label(span, "already used"); - err.span_label(first_use_span, format!("first use of `{}`", name)); - err - } + ResolutionError::NameAlreadyUsedInParameterList(name, first_use_span) => self + .session + .create_err(errs::NameAlreadyUsedInParameterList { span, first_use_span, name }), ResolutionError::MethodNotMemberOfTrait(method, trait_, candidate) => { - let mut err = struct_span_err!( - self.session, + self.session.create_err(errs::MethodNotMemberOfTrait { span, - E0407, - "method `{}` is not a member of trait `{}`", method, - trait_ - ); - err.span_label(span, format!("not a member of trait `{}`", trait_)); - if let Some(candidate) = candidate { - err.span_suggestion( - method.span, - "there is an associated function with a similar name", - candidate.to_ident_string(), - Applicability::MaybeIncorrect, - ); - } - err + trait_, + sub: candidate.map(|c| errs::AssociatedFnWithSimilarNameExists { + span: method.span, + candidate: c, + }), + }) } ResolutionError::TypeNotMemberOfTrait(type_, trait_, candidate) => { - let mut err = struct_span_err!( - self.session, + self.session.create_err(errs::TypeNotMemberOfTrait { span, - E0437, - "type `{}` is not a member of trait `{}`", type_, - trait_ - ); - err.span_label(span, format!("not a member of trait `{}`", trait_)); - if let Some(candidate) = candidate { - err.span_suggestion( - type_.span, - "there is an associated type with a similar name", - candidate.to_ident_string(), - Applicability::MaybeIncorrect, - ); - } - err + trait_, + sub: candidate.map(|c| errs::AssociatedTypeWithSimilarNameExists { + span: type_.span, + candidate: c, + }), + }) } ResolutionError::ConstNotMemberOfTrait(const_, trait_, candidate) => { - let mut err = struct_span_err!( - self.session, + self.session.create_err(errs::ConstNotMemberOfTrait { span, - E0438, - "const `{}` is not a member of trait `{}`", const_, - trait_ - ); - err.span_label(span, format!("not a member of trait `{}`", trait_)); - if let Some(candidate) = candidate { - err.span_suggestion( - const_.span, - "there is an associated constant with a similar name", - candidate.to_ident_string(), - Applicability::MaybeIncorrect, - ); - } - err + trait_, + sub: candidate.map(|c| errs::AssociatedConstWithSimilarNameExists { + span: const_.span, + candidate: c, + }), + }) } ResolutionError::VariableNotBoundInPattern(binding_error, parent_scope) => { let BindingError { name, target, origin, could_be_path } = binding_error; @@ -708,128 +695,78 @@ impl<'a> Resolver<'a> { err } ResolutionError::VariableBoundWithDifferentMode(variable_name, first_binding_span) => { - let mut err = struct_span_err!( - self.session, - span, - E0409, - "variable `{}` is bound inconsistently across alternatives separated by `|`", - variable_name - ); - err.span_label(span, "bound in different ways"); - err.span_label(first_binding_span, "first binding"); - err - } - ResolutionError::IdentifierBoundMoreThanOnceInParameterList(identifier) => { - let mut err = struct_span_err!( - self.session, - span, - E0415, - "identifier `{}` is bound more than once in this parameter list", - identifier - ); - err.span_label(span, "used as parameter more than once"); - err - } - ResolutionError::IdentifierBoundMoreThanOnceInSamePattern(identifier) => { - let mut err = struct_span_err!( - self.session, + self.session.create_err(errs::VariableBoundWithDifferentMode { span, - E0416, - "identifier `{}` is bound more than once in the same pattern", - identifier - ); - err.span_label(span, "used in a pattern more than once"); - err - } + first_binding_span, + variable_name, + }) + } + ResolutionError::IdentifierBoundMoreThanOnceInParameterList(identifier) => self + .session + .create_err(errs::IdentifierBoundMoreThanOnceInParameterList { span, identifier }), + ResolutionError::IdentifierBoundMoreThanOnceInSamePattern(identifier) => self + .session + .create_err(errs::IdentifierBoundMoreThanOnceInSamePattern { span, identifier }), ResolutionError::UndeclaredLabel { name, suggestion } => { - let mut err = struct_span_err!( - self.session, - span, - E0426, - "use of undeclared label `{}`", - name - ); - - err.span_label(span, format!("undeclared label `{}`", name)); - - match suggestion { + let ((sub_reachable, sub_reachable_suggestion), sub_unreachable) = match suggestion + { // A reachable label with a similar name exists. - Some((ident, true)) => { - err.span_label(ident.span, "a label with a similar name is reachable"); - err.span_suggestion( - span, - "try using similarly named label", - ident.name, - Applicability::MaybeIncorrect, - ); - } + Some((ident, true)) => ( + ( + Some(errs::LabelWithSimilarNameReachable(ident.span)), + Some(errs::TryUsingSimilarlyNamedLabel { + span, + ident_name: ident.name, + }), + ), + None, + ), // An unreachable label with a similar name exists. - Some((ident, false)) => { - err.span_label( - ident.span, - "a label with a similar name exists but is unreachable", - ); - } + Some((ident, false)) => ( + (None, None), + Some(errs::UnreachableLabelWithSimilarNameExists { + ident_span: ident.span, + }), + ), // No similarly-named labels exist. - None => (), - } - - err + None => ((None, None), None), + }; + self.session.create_err(errs::UndeclaredLabel { + span, + name, + sub_reachable, + sub_reachable_suggestion, + sub_unreachable, + }) } ResolutionError::SelfImportsOnlyAllowedWithin { root, span_with_rename } => { - let mut err = struct_span_err!( - self.session, - span, - E0429, - "{}", - "`self` imports are only allowed within a { } list" - ); - // None of the suggestions below would help with a case like `use self`. - if !root { + let (suggestion, mpart_suggestion) = if root { + (None, None) + } else { // use foo::bar::self -> foo::bar // use foo::bar::self as abc -> foo::bar as abc - err.span_suggestion( - span, - "consider importing the module directly", - "", - Applicability::MachineApplicable, - ); + let suggestion = errs::SelfImportsOnlyAllowedWithinSuggestion { span }; // use foo::bar::self -> foo::bar::{self} // use foo::bar::self as abc -> foo::bar::{self as abc} - let braces = vec![ - (span_with_rename.shrink_to_lo(), "{".to_string()), - (span_with_rename.shrink_to_hi(), "}".to_string()), - ]; - err.multipart_suggestion( - "alternatively, use the multi-path `use` syntax to import `self`", - braces, - Applicability::MachineApplicable, - ); - } - err + let mpart_suggestion = errs::SelfImportsOnlyAllowedWithinMultipartSuggestion { + multipart_start: span_with_rename.shrink_to_lo(), + multipart_end: span_with_rename.shrink_to_hi(), + }; + (Some(suggestion), Some(mpart_suggestion)) + }; + self.session.create_err(errs::SelfImportsOnlyAllowedWithin { + span, + suggestion, + mpart_suggestion, + }) } ResolutionError::SelfImportCanOnlyAppearOnceInTheList => { - let mut err = struct_span_err!( - self.session, - span, - E0430, - "`self` import can only appear once in an import list" - ); - err.span_label(span, "can only appear once in an import list"); - err + self.session.create_err(errs::SelfImportCanOnlyAppearOnceInTheList { span }) } ResolutionError::SelfImportOnlyInImportListWithNonEmptyPrefix => { - let mut err = struct_span_err!( - self.session, - span, - E0431, - "`self` import can only appear in an import list with \ - a non-empty prefix" - ); - err.span_label(span, "can only appear in an import list with a non-empty prefix"); - err + self.session.create_err(errs::SelfImportOnlyInImportListWithNonEmptyPrefix { span }) } ResolutionError::FailedToResolve { label, suggestion } => { let mut err = @@ -847,23 +784,9 @@ impl<'a> Resolver<'a> { err } ResolutionError::CannotCaptureDynamicEnvironmentInFnItem => { - let mut err = struct_span_err!( - self.session, - span, - E0434, - "{}", - "can't capture dynamic environment in a fn item" - ); - err.help("use the `|| { ... }` closure form instead"); - err + self.session.create_err(errs::CannotCaptureDynamicEnvironmentInFnItem { span }) } - ResolutionError::AttemptToUseNonConstantValueInConstant(ident, sugg, current) => { - let mut err = struct_span_err!( - self.session, - span, - E0435, - "attempt to use a non-constant value in a constant" - ); + ResolutionError::AttemptToUseNonConstantValueInConstant(ident, suggestion, current) => { // let foo =... // ^^^ given this Span // ------- get this Span to have an applicable suggestion @@ -877,23 +800,34 @@ impl<'a> Resolver<'a> { .source_map() .span_extend_to_prev_str(ident.span, current, true, false); - match sp { + let ((with, with_label), without) = match sp { Some(sp) if !self.session.source_map().is_multiline(sp) => { let sp = sp.with_lo(BytePos(sp.lo().0 - (current.len() as u32))); - err.span_suggestion( - sp, - &format!("consider using `{}` instead of `{}`", sugg, current), - format!("{} {}", sugg, ident), - Applicability::MaybeIncorrect, - ); - err.span_label(span, "non-constant value"); - } - _ => { - err.span_label(ident.span, &format!("this would need to be a `{}`", sugg)); + ( + (Some(errs::AttemptToUseNonConstantValueInConstantWithSuggestion { + span: sp, + ident, + suggestion, + current, + }), Some(errs::AttemptToUseNonConstantValueInConstantLabelWithSuggestion {span})), + None, + ) } - } + _ => ( + (None, None), + Some(errs::AttemptToUseNonConstantValueInConstantWithoutSuggestion { + ident_span: ident.span, + suggestion, + }), + ), + }; - err + self.session.create_err(errs::AttemptToUseNonConstantValueInConstant { + span, + with, + with_label, + without, + }) } ResolutionError::BindingShadowsSomethingUnacceptable { shadowing_binding, @@ -902,135 +836,80 @@ impl<'a> Resolver<'a> { article, shadowed_binding, shadowed_binding_span, - } => { - let shadowed_binding_descr = shadowed_binding.descr(); - let mut err = struct_span_err!( - self.session, - span, - E0530, - "{}s cannot shadow {}s", - shadowing_binding.descr(), - shadowed_binding_descr, - ); - err.span_label( - span, - format!("cannot be named the same as {} {}", article, shadowed_binding_descr), - ); - match (shadowing_binding, shadowed_binding) { + } => self.session.create_err(errs::BindingShadowsSomethingUnacceptable { + span, + shadowing_binding, + shadowed_binding, + article, + sub_suggestion: match (shadowing_binding, shadowed_binding) { ( PatternSource::Match, Res::Def(DefKind::Ctor(CtorOf::Variant | CtorOf::Struct, CtorKind::Fn), _), - ) => { - err.span_suggestion( - span, - "try specify the pattern arguments", - format!("{}(..)", name), - Applicability::Unspecified, - ); - } - _ => (), - } - let msg = - format!("the {} `{}` is {} here", shadowed_binding_descr, name, participle); - err.span_label(shadowed_binding_span, msg); - err - } + ) => Some(errs::BindingShadowsSomethingUnacceptableSuggestion { span, name }), + _ => None, + }, + shadowed_binding_span, + participle, + name, + }), ResolutionError::ForwardDeclaredGenericParam => { - let mut err = struct_span_err!( - self.session, - span, - E0128, - "generic parameters with a default cannot use \ - forward declared identifiers" - ); - err.span_label(span, "defaulted generic parameters cannot be forward declared"); - err + self.session.create_err(errs::ForwardDeclaredGenericParam { span }) } ResolutionError::ParamInTyOfConstParam(name) => { - let mut err = struct_span_err!( - self.session, - span, - E0770, - "the type of const parameters must not depend on other generic parameters" - ); - err.span_label( - span, - format!("the type must not depend on the parameter `{}`", name), - ); - err + self.session.create_err(errs::ParamInTyOfConstParam { span, name }) } ResolutionError::ParamInNonTrivialAnonConst { name, is_type } => { - let mut err = self.session.struct_span_err( + self.session.create_err(errs::ParamInNonTrivialAnonConst { span, - "generic parameters may not be used in const operations", - ); - err.span_label(span, &format!("cannot perform const operation using `{}`", name)); - - if is_type { - err.note("type parameters may not be used in const expressions"); - } else { - err.help(&format!( - "const parameters may only be used as standalone arguments, i.e. `{}`", - name - )); - } - - if self.session.is_nightly_build() { - err.help( - "use `#![feature(generic_const_exprs)]` to allow generic const expressions", - ); - } - - err + name, + sub_is_type: if is_type { + errs::ParamInNonTrivialAnonConstIsType::AType + } else { + errs::ParamInNonTrivialAnonConstIsType::NotAType { name } + }, + help: self + .session + .is_nightly_build() + .then_some(errs::ParamInNonTrivialAnonConstHelp), + }) } ResolutionError::SelfInGenericParamDefault => { - let mut err = struct_span_err!( - self.session, - span, - E0735, - "generic parameters cannot use `Self` in their defaults" - ); - err.span_label(span, "`Self` in generic parameter default"); - err + self.session.create_err(errs::SelfInGenericParamDefault { span }) } ResolutionError::UnreachableLabel { name, definition_span, suggestion } => { - let mut err = struct_span_err!( - self.session, + let ((sub_suggestion_label, sub_suggestion), sub_unreachable_label) = + match suggestion { + // A reachable label with a similar name exists. + Some((ident, true)) => ( + ( + Some(errs::UnreachableLabelSubLabel { ident_span: ident.span }), + Some(errs::UnreachableLabelSubSuggestion { + span, + // intentionally taking 'ident.name' instead of 'ident' itself, as this + // could be used in suggestion context + ident_name: ident.name, + }), + ), + None, + ), + // An unreachable label with a similar name exists. + Some((ident, false)) => ( + (None, None), + Some(errs::UnreachableLabelSubLabelUnreachable { + ident_span: ident.span, + }), + ), + // No similarly-named labels exist. + None => ((None, None), None), + }; + self.session.create_err(errs::UnreachableLabel { span, - E0767, - "use of unreachable label `{}`", name, - ); - - err.span_label(definition_span, "unreachable label defined here"); - err.span_label(span, format!("unreachable label `{}`", name)); - err.note( - "labels are unreachable through functions, closures, async blocks and modules", - ); - - match suggestion { - // A reachable label with a similar name exists. - Some((ident, true)) => { - err.span_label(ident.span, "a label with a similar name is reachable"); - err.span_suggestion( - span, - "try using similarly named label", - ident.name, - Applicability::MaybeIncorrect, - ); - } - // An unreachable label with a similar name exists. - Some((ident, false)) => { - err.span_label( - ident.span, - "a label with a similar name exists but is also unreachable", - ); - } - // No similarly-named labels exist. - None => (), - } - - err + definition_span, + sub_suggestion, + sub_suggestion_label, + sub_unreachable_label, + }) } ResolutionError::TraitImplMismatch { name, @@ -1051,25 +930,10 @@ impl<'a> Resolver<'a> { err.span_label(trait_item_span, "item in trait"); err } - ResolutionError::TraitImplDuplicate { name, trait_item_span, old_span } => { - let mut err = struct_span_err!( - self.session, - span, - E0201, - "duplicate definitions with name `{}`:", - name, - ); - err.span_label(old_span, "previous definition here"); - err.span_label(trait_item_span, "item in trait"); - err.span_label(span, "duplicate definition"); - err - } - ResolutionError::InvalidAsmSym => { - let mut err = self.session.struct_span_err(span, "invalid `sym` operand"); - err.span_label(span, "is a local variable"); - err.help("`sym` operands must refer to either a function or a static"); - err - } + ResolutionError::TraitImplDuplicate { name, trait_item_span, old_span } => self + .session + .create_err(errs::TraitImplDuplicate { span, name, trait_item_span, old_span }), + ResolutionError::InvalidAsmSym => self.session.create_err(errs::InvalidAsmSym { span }), } } @@ -1079,48 +943,27 @@ impl<'a> Resolver<'a> { ) -> ErrorGuaranteed { match vis_resolution_error { VisResolutionError::Relative2018(span, path) => { - let mut err = self.session.struct_span_err( + self.session.create_err(errs::Relative2018 { span, - "relative paths are not supported in visibilities in 2018 edition or later", - ); - err.span_suggestion( - path.span, - "try", - format!("crate::{}", pprust::path_to_string(&path)), - Applicability::MaybeIncorrect, - ); - err + path_span: path.span, + // intentionally converting to String, as the text would also be used as + // in suggestion context + path_str: pprust::path_to_string(&path), + }) + } + VisResolutionError::AncestorOnly(span) => { + self.session.create_err(errs::AncestorOnly(span)) } - VisResolutionError::AncestorOnly(span) => struct_span_err!( - self.session, - span, - E0742, - "visibilities can only be restricted to ancestor modules" - ), VisResolutionError::FailedToResolve(span, label, suggestion) => { self.into_struct_error(span, ResolutionError::FailedToResolve { label, suggestion }) } VisResolutionError::ExpectedFound(span, path_str, res) => { - let mut err = struct_span_err!( - self.session, - span, - E0577, - "expected module, found {} `{}`", - res.descr(), - path_str - ); - err.span_label(span, "not a module"); - err + self.session.create_err(errs::ExpectedFound { span, res, path_str }) } - VisResolutionError::Indeterminate(span) => struct_span_err!( - self.session, - span, - E0578, - "cannot determine resolution for the visibility" - ), - VisResolutionError::ModuleOnly(span) => { - self.session.struct_span_err(span, "visibility must resolve to a module") + VisResolutionError::Indeterminate(span) => { + self.session.create_err(errs::Indeterminate(span)) } + VisResolutionError::ModuleOnly(span) => self.session.create_err(errs::ModuleOnly(span)), } .emit() } @@ -1145,7 +988,7 @@ impl<'a> Resolver<'a> { .get(&expn_id) .into_iter() .flatten() - .map(|ident| TypoSuggestion::typo_from_res(ident.name, res)), + .map(|ident| TypoSuggestion::typo_from_ident(*ident, res)), ); } } @@ -1164,7 +1007,7 @@ impl<'a> Resolver<'a> { suggestions.extend( ext.helper_attrs .iter() - .map(|name| TypoSuggestion::typo_from_res(*name, res)), + .map(|name| TypoSuggestion::typo_from_name(*name, res)), ); } } @@ -1174,8 +1017,8 @@ impl<'a> Resolver<'a> { if let MacroRulesScope::Binding(macro_rules_binding) = macro_rules_scope.get() { let res = macro_rules_binding.binding.res(); if filter_fn(res) { - suggestions.push(TypoSuggestion::typo_from_res( - macro_rules_binding.ident.name, + suggestions.push(TypoSuggestion::typo_from_ident( + macro_rules_binding.ident, res, )) } @@ -1193,7 +1036,7 @@ impl<'a> Resolver<'a> { suggestions.extend(this.macro_use_prelude.iter().filter_map( |(name, binding)| { let res = binding.res(); - filter_fn(res).then_some(TypoSuggestion::typo_from_res(*name, res)) + filter_fn(res).then_some(TypoSuggestion::typo_from_name(*name, res)) }, )); } @@ -1203,14 +1046,14 @@ impl<'a> Resolver<'a> { suggestions.extend( BUILTIN_ATTRIBUTES .iter() - .map(|attr| TypoSuggestion::typo_from_res(attr.name, res)), + .map(|attr| TypoSuggestion::typo_from_name(attr.name, res)), ); } } Scope::ExternPrelude => { suggestions.extend(this.extern_prelude.iter().filter_map(|(ident, _)| { let res = Res::Def(DefKind::Mod, CRATE_DEF_ID.to_def_id()); - filter_fn(res).then_some(TypoSuggestion::typo_from_res(ident.name, res)) + filter_fn(res).then_some(TypoSuggestion::typo_from_ident(*ident, res)) })); } Scope::ToolPrelude => { @@ -1218,7 +1061,7 @@ impl<'a> Resolver<'a> { suggestions.extend( this.registered_tools .iter() - .map(|ident| TypoSuggestion::typo_from_res(ident.name, res)), + .map(|ident| TypoSuggestion::typo_from_ident(*ident, res)), ); } Scope::StdLibPrelude => { @@ -1235,7 +1078,8 @@ impl<'a> Resolver<'a> { Scope::BuiltinTypes => { suggestions.extend(PrimTy::ALL.iter().filter_map(|prim_ty| { let res = Res::PrimTy(*prim_ty); - filter_fn(res).then_some(TypoSuggestion::typo_from_res(prim_ty.name(), res)) + filter_fn(res) + .then_some(TypoSuggestion::typo_from_name(prim_ty.name(), res)) })) } } @@ -1272,7 +1116,7 @@ impl<'a> Resolver<'a> { { let mut candidates = Vec::new(); let mut seen_modules = FxHashSet::default(); - let mut worklist = vec![(start_module, Vec::<ast::PathSegment>::new(), true)]; + let mut worklist = vec![(start_module, ThinVec::<ast::PathSegment>::new(), true)]; let mut worklist_via_import = vec![]; while let Some((in_module, path_segments, accessible)) = match worklist.pop() { @@ -1666,7 +1510,7 @@ impl<'a> Resolver<'a> { let a = if built_in.is_empty() { res.article() } else { "a" }; format!("{a}{built_in} {thing}{from}", thing = res.descr()) } else { - let introduced = if b.is_import() { "imported" } else { "defined" }; + let introduced = if b.is_import_user_facing() { "imported" } else { "defined" }; format!("the {thing} {introduced} here", thing = res.descr()) } } @@ -1725,10 +1569,10 @@ impl<'a> Resolver<'a> { /// If the binding refers to a tuple struct constructor with fields, /// returns the span of its fields. fn ctor_fields_span(&self, binding: &NameBinding<'_>) -> Option<Span> { - if let NameBindingKind::Res( - Res::Def(DefKind::Ctor(CtorOf::Struct, CtorKind::Fn), ctor_def_id), - _, - ) = binding.kind + if let NameBindingKind::Res(Res::Def( + DefKind::Ctor(CtorOf::Struct, CtorKind::Fn), + ctor_def_id, + )) = binding.kind { let def_id = self.parent(ctor_def_id); let fields = self.field_names.get(&def_id)?; @@ -1772,7 +1616,9 @@ impl<'a> Resolver<'a> { next_ident = source; Some(binding) } - ImportKind::Glob { .. } | ImportKind::MacroUse => Some(binding), + ImportKind::Glob { .. } | ImportKind::MacroUse | ImportKind::MacroExport => { + Some(binding) + } ImportKind::ExternCrate { .. } => None, }, _ => None, @@ -1994,13 +1840,16 @@ impl<'a> Resolver<'a> { (format!("use of undeclared type `{}`", ident), suggestion) } else { - let suggestion = if ident.name == sym::alloc { - Some(( + let mut suggestion = None; + if ident.name == sym::alloc { + suggestion = Some(( vec![], String::from("add `extern crate alloc` to use the `alloc` crate"), Applicability::MaybeIncorrect, )) - } else { + } + + suggestion = suggestion.or_else(|| { self.find_similarly_named_module_or_crate(ident.name, &parent_scope.module).map( |sugg| { ( @@ -2010,7 +1859,7 @@ impl<'a> Resolver<'a> { ) }, ) - }; + }); (format!("use of undeclared crate or module `{}`", ident), suggestion) } } diff --git a/compiler/rustc_resolve/src/effective_visibilities.rs b/compiler/rustc_resolve/src/effective_visibilities.rs index c40669ac9..85399385d 100644 --- a/compiler/rustc_resolve/src/effective_visibilities.rs +++ b/compiler/rustc_resolve/src/effective_visibilities.rs @@ -1,41 +1,120 @@ -use crate::{ImportKind, NameBindingKind, Resolver}; +use crate::{NameBinding, NameBindingKind, Resolver, ResolverTree}; use rustc_ast::ast; use rustc_ast::visit; use rustc_ast::visit::Visitor; use rustc_ast::Crate; use rustc_ast::EnumDef; +use rustc_data_structures::intern::Interned; use rustc_hir::def_id::LocalDefId; use rustc_hir::def_id::CRATE_DEF_ID; -use rustc_middle::middle::privacy::Level; +use rustc_middle::middle::privacy::{EffectiveVisibilities, EffectiveVisibility}; +use rustc_middle::middle::privacy::{IntoDefIdTree, Level}; use rustc_middle::ty::{DefIdTree, Visibility}; +use std::mem; + +type ImportId<'a> = Interned<'a, NameBinding<'a>>; + +#[derive(Clone, Copy)] +enum ParentId<'a> { + Def(LocalDefId), + Import(ImportId<'a>), +} + +impl ParentId<'_> { + fn level(self) -> Level { + match self { + ParentId::Def(_) => Level::Direct, + ParentId::Import(_) => Level::Reexported, + } + } +} pub struct EffectiveVisibilitiesVisitor<'r, 'a> { r: &'r mut Resolver<'a>, + def_effective_visibilities: EffectiveVisibilities, + /// While walking import chains we need to track effective visibilities per-binding, and def id + /// keys in `Resolver::effective_visibilities` are not enough for that, because multiple + /// bindings can correspond to a single def id in imports. So we keep a separate table. + import_effective_visibilities: EffectiveVisibilities<ImportId<'a>>, + // It's possible to recalculate this at any point, but it's relatively expensive. + current_private_vis: Visibility, changed: bool, } +impl Resolver<'_> { + fn nearest_normal_mod(&mut self, def_id: LocalDefId) -> LocalDefId { + self.get_nearest_non_block_module(def_id.to_def_id()).nearest_parent_mod().expect_local() + } + + fn private_vis_import(&mut self, binding: ImportId<'_>) -> Visibility { + let NameBindingKind::Import { import, .. } = binding.kind else { unreachable!() }; + Visibility::Restricted( + import + .id() + .map(|id| self.nearest_normal_mod(self.local_def_id(id))) + .unwrap_or(CRATE_DEF_ID), + ) + } + + fn private_vis_def(&mut self, def_id: LocalDefId) -> Visibility { + // For mod items `nearest_normal_mod` returns its argument, but we actually need its parent. + let normal_mod_id = self.nearest_normal_mod(def_id); + if normal_mod_id == def_id { + self.opt_local_parent(def_id).map_or(Visibility::Public, Visibility::Restricted) + } else { + Visibility::Restricted(normal_mod_id) + } + } +} + +impl<'a, 'b> IntoDefIdTree for &'b mut Resolver<'a> { + type Tree = &'b Resolver<'a>; + fn tree(self) -> Self::Tree { + self + } +} + impl<'r, 'a> EffectiveVisibilitiesVisitor<'r, 'a> { /// Fills the `Resolver::effective_visibilities` table with public & exported items /// For now, this doesn't resolve macros (FIXME) and cannot resolve Impl, as we /// need access to a TyCtxt for that. pub fn compute_effective_visibilities<'c>(r: &'r mut Resolver<'a>, krate: &'c Crate) { - let mut visitor = EffectiveVisibilitiesVisitor { r, changed: false }; - - visitor.update(CRATE_DEF_ID, Visibility::Public, CRATE_DEF_ID, Level::Direct); + let mut visitor = EffectiveVisibilitiesVisitor { + r, + def_effective_visibilities: Default::default(), + import_effective_visibilities: Default::default(), + current_private_vis: Visibility::Public, + changed: false, + }; + + visitor.update(CRATE_DEF_ID, CRATE_DEF_ID); + visitor.current_private_vis = Visibility::Restricted(CRATE_DEF_ID); visitor.set_bindings_effective_visibilities(CRATE_DEF_ID); while visitor.changed { - visitor.reset(); + visitor.changed = false; visit::walk_crate(&mut visitor, krate); } + visitor.r.effective_visibilities = visitor.def_effective_visibilities; + + // Update visibilities for import def ids. These are not used during the + // `EffectiveVisibilitiesVisitor` pass, because we have more detailed binding-based + // information, but are used by later passes. Effective visibility of an import def id + // is the maximum value among visibilities of bindings corresponding to that def id. + for (binding, eff_vis) in visitor.import_effective_visibilities.iter() { + let NameBindingKind::Import { import, .. } = binding.kind else { unreachable!() }; + if let Some(node_id) = import.id() { + r.effective_visibilities.update_eff_vis( + r.local_def_id(node_id), + eff_vis, + ResolverTree(&r.definitions, &r.crate_loader), + ) + } + } info!("resolve::effective_visibilities: {:#?}", r.effective_visibilities); } - fn reset(&mut self) { - self.changed = false; - } - /// Update effective visibilities of bindings in the given module, /// including their whole reexport chains. fn set_bindings_effective_visibilities(&mut self, module_id: LocalDefId) { @@ -48,73 +127,68 @@ impl<'r, 'a> EffectiveVisibilitiesVisitor<'r, 'a> { // Set the given effective visibility level to `Level::Direct` and // sets the rest of the `use` chain to `Level::Reexported` until // we hit the actual exported item. + let mut parent_id = ParentId::Def(module_id); + while let NameBindingKind::Import { binding: nested_binding, .. } = binding.kind { + let binding_id = ImportId::new_unchecked(binding); + self.update_import(binding_id, parent_id); - // FIXME: tag and is_public() condition should be removed, but assertions occur. - let tag = if binding.is_import() { Level::Reexported } else { Level::Direct }; - if binding.vis.is_public() { - let mut prev_parent_id = module_id; - let mut level = Level::Direct; - while let NameBindingKind::Import { binding: nested_binding, import, .. } = - binding.kind - { - let mut update = |node_id| self.update( - self.r.local_def_id(node_id), - binding.vis.expect_local(), - prev_parent_id, - level, - ); - // In theory all the import IDs have individual visibilities and effective - // visibilities, but in practice these IDs go straigth to HIR where all - // their few uses assume that their (effective) visibility applies to the - // whole syntactic `use` item. So we update them all to the maximum value - // among the potential individual effective visibilities. Maybe HIR for - // imports shouldn't use three IDs at all. - update(import.id); - if let ImportKind::Single { additional_ids, .. } = import.kind { - update(additional_ids.0); - update(additional_ids.1); - } - - level = Level::Reexported; - prev_parent_id = self.r.local_def_id(import.id); - binding = nested_binding; - } + parent_id = ParentId::Import(binding_id); + binding = nested_binding; } if let Some(def_id) = binding.res().opt_def_id().and_then(|id| id.as_local()) { - self.update(def_id, binding.vis.expect_local(), module_id, tag); + self.update_def(def_id, binding.vis.expect_local(), parent_id); } } } } - fn update( - &mut self, - def_id: LocalDefId, - nominal_vis: Visibility, - parent_id: LocalDefId, - tag: Level, - ) { - let module_id = self - .r - .get_nearest_non_block_module(def_id.to_def_id()) - .nearest_parent_mod() - .expect_local(); - if nominal_vis == Visibility::Restricted(module_id) - || self.r.visibilities[&parent_id] == Visibility::Restricted(module_id) - { - return; + fn cheap_private_vis(&self, parent_id: ParentId<'_>) -> Option<Visibility> { + matches!(parent_id, ParentId::Def(_)).then_some(self.current_private_vis) + } + + fn effective_vis_or_private(&mut self, parent_id: ParentId<'a>) -> EffectiveVisibility { + // Private nodes are only added to the table for caching, they could be added or removed at + // any moment without consequences, so we don't set `changed` to true when adding them. + *match parent_id { + ParentId::Def(def_id) => self + .def_effective_visibilities + .effective_vis_or_private(def_id, || self.r.private_vis_def(def_id)), + ParentId::Import(binding) => self + .import_effective_visibilities + .effective_vis_or_private(binding, || self.r.private_vis_import(binding)), } - let mut effective_visibilities = std::mem::take(&mut self.r.effective_visibilities); - self.changed |= effective_visibilities.update( + } + + fn update_import(&mut self, binding: ImportId<'a>, parent_id: ParentId<'a>) { + let nominal_vis = binding.vis.expect_local(); + let private_vis = self.cheap_private_vis(parent_id); + let inherited_eff_vis = self.effective_vis_or_private(parent_id); + self.changed |= self.import_effective_visibilities.update( + binding, + nominal_vis, + |r| (private_vis.unwrap_or_else(|| r.private_vis_import(binding)), r), + inherited_eff_vis, + parent_id.level(), + &mut *self.r, + ); + } + + fn update_def(&mut self, def_id: LocalDefId, nominal_vis: Visibility, parent_id: ParentId<'a>) { + let private_vis = self.cheap_private_vis(parent_id); + let inherited_eff_vis = self.effective_vis_or_private(parent_id); + self.changed |= self.def_effective_visibilities.update( def_id, nominal_vis, - || Visibility::Restricted(module_id), - parent_id, - tag, - &*self.r, + |r| (private_vis.unwrap_or_else(|| r.private_vis_def(def_id)), r), + inherited_eff_vis, + parent_id.level(), + &mut *self.r, ); - self.r.effective_visibilities = effective_visibilities; + } + + fn update(&mut self, def_id: LocalDefId, parent_id: LocalDefId) { + self.update_def(def_id, self.r.visibilities[&def_id], ParentId::Def(parent_id)); } } @@ -132,22 +206,12 @@ impl<'r, 'ast> Visitor<'ast> for EffectiveVisibilitiesVisitor<'ast, 'r> { "ast::ItemKind::MacCall encountered, this should not anymore appear at this stage" ), - // Foreign modules inherit level from parents. - ast::ItemKind::ForeignMod(..) => { - let parent_id = self.r.local_parent(def_id); - self.update(def_id, Visibility::Public, parent_id, Level::Direct); - } - - // Only exported `macro_rules!` items are public, but they always are - ast::ItemKind::MacroDef(ref macro_def) if macro_def.macro_rules => { - let parent_id = self.r.local_parent(def_id); - let vis = self.r.visibilities[&def_id]; - self.update(def_id, vis, parent_id, Level::Direct); - } - ast::ItemKind::Mod(..) => { + let prev_private_vis = + mem::replace(&mut self.current_private_vis, Visibility::Restricted(def_id)); self.set_bindings_effective_visibilities(def_id); visit::walk_item(self, item); + self.current_private_vis = prev_private_vis; } ast::ItemKind::Enum(EnumDef { ref variants }, _) => { @@ -155,18 +219,14 @@ impl<'r, 'ast> Visitor<'ast> for EffectiveVisibilitiesVisitor<'ast, 'r> { for variant in variants { let variant_def_id = self.r.local_def_id(variant.id); for field in variant.data.fields() { - let field_def_id = self.r.local_def_id(field.id); - let vis = self.r.visibilities[&field_def_id]; - self.update(field_def_id, vis, variant_def_id, Level::Direct); + self.update(self.r.local_def_id(field.id), variant_def_id); } } } ast::ItemKind::Struct(ref def, _) | ast::ItemKind::Union(ref def, _) => { for field in def.fields() { - let field_def_id = self.r.local_def_id(field.id); - let vis = self.r.visibilities[&field_def_id]; - self.update(field_def_id, vis, def_id, Level::Direct); + self.update(self.r.local_def_id(field.id), def_id); } } @@ -182,6 +242,7 @@ impl<'r, 'ast> Visitor<'ast> for EffectiveVisibilitiesVisitor<'ast, 'r> { | ast::ItemKind::TyAlias(..) | ast::ItemKind::TraitAlias(..) | ast::ItemKind::MacroDef(..) + | ast::ItemKind::ForeignMod(..) | ast::ItemKind::Fn(..) => return, } } diff --git a/compiler/rustc_resolve/src/errors.rs b/compiler/rustc_resolve/src/errors.rs new file mode 100644 index 000000000..2c4427746 --- /dev/null +++ b/compiler/rustc_resolve/src/errors.rs @@ -0,0 +1,474 @@ +use rustc_macros::{Diagnostic, Subdiagnostic}; +use rustc_span::{ + symbol::{Ident, Symbol}, + Span, +}; + +use crate::{late::PatternSource, Res}; + +#[derive(Diagnostic)] +#[diag(resolve_parent_module_reset_for_binding, code = "E0637")] +pub(crate) struct ParentModuleResetForBinding; + +#[derive(Diagnostic)] +#[diag(resolve_ampersand_used_without_explicit_lifetime_name, code = "E0637")] +#[note] +pub(crate) struct AmpersandUsedWithoutExplicitLifetimeName(#[primary_span] pub(crate) Span); + +#[derive(Diagnostic)] +#[diag(resolve_underscore_lifetime_name_cannot_be_used_here, code = "E0637")] +#[note] +pub(crate) struct UnderscoreLifetimeNameCannotBeUsedHere(#[primary_span] pub(crate) Span); + +#[derive(Diagnostic)] +#[diag(resolve_crate_may_not_be_imported)] +pub(crate) struct CrateMayNotBeImprted(#[primary_span] pub(crate) Span); + +#[derive(Diagnostic)] +#[diag(resolve_crate_root_imports_must_be_named_explicitly)] +pub(crate) struct CrateRootNamesMustBeNamedExplicitly(#[primary_span] pub(crate) Span); + +#[derive(Diagnostic)] +#[diag(resolve_crate_root_imports_must_be_named_explicitly)] +pub(crate) struct ResolutionError(#[primary_span] pub(crate) Span); + +#[derive(Diagnostic)] +#[diag(resolve_name_is_already_used_as_generic_parameter, code = "E0403")] +pub(crate) struct NameAlreadyUsedInParameterList { + #[primary_span] + #[label] + pub(crate) span: Span, + #[label(first_use_of_name)] + pub(crate) first_use_span: Span, + pub(crate) name: Symbol, +} + +#[derive(Diagnostic)] +#[diag(resolve_method_not_member_of_trait, code = "E0407")] +pub(crate) struct MethodNotMemberOfTrait { + #[primary_span] + #[label] + pub(crate) span: Span, + pub(crate) method: Ident, + pub(crate) trait_: String, + #[subdiagnostic] + pub(crate) sub: Option<AssociatedFnWithSimilarNameExists>, +} + +#[derive(Subdiagnostic)] +#[suggestion( + resolve_associated_fn_with_similar_name_exists, + code = "{candidate}", + applicability = "maybe-incorrect" +)] +pub(crate) struct AssociatedFnWithSimilarNameExists { + #[primary_span] + pub(crate) span: Span, + pub(crate) candidate: Symbol, +} + +#[derive(Diagnostic)] +#[diag(resolve_type_not_member_of_trait, code = "E0437")] +pub(crate) struct TypeNotMemberOfTrait { + #[primary_span] + #[label] + pub(crate) span: Span, + pub(crate) type_: Ident, + pub(crate) trait_: String, + #[subdiagnostic] + pub(crate) sub: Option<AssociatedTypeWithSimilarNameExists>, +} + +#[derive(Subdiagnostic)] +#[suggestion( + resolve_associated_type_with_similar_name_exists, + code = "{candidate}", + applicability = "maybe-incorrect" +)] +pub(crate) struct AssociatedTypeWithSimilarNameExists { + #[primary_span] + pub(crate) span: Span, + pub(crate) candidate: Symbol, +} + +#[derive(Diagnostic)] +#[diag(resolve_const_not_member_of_trait, code = "E0438")] +pub(crate) struct ConstNotMemberOfTrait { + #[primary_span] + #[label] + pub(crate) span: Span, + pub(crate) const_: Ident, + pub(crate) trait_: String, + #[subdiagnostic] + pub(crate) sub: Option<AssociatedConstWithSimilarNameExists>, +} + +#[derive(Subdiagnostic)] +#[suggestion( + resolve_associated_const_with_similar_name_exists, + code = "{candidate}", + applicability = "maybe-incorrect" +)] +pub(crate) struct AssociatedConstWithSimilarNameExists { + #[primary_span] + pub(crate) span: Span, + pub(crate) candidate: Symbol, +} + +#[derive(Diagnostic)] +#[diag(resolve_variable_bound_with_different_mode, code = "E0409")] +pub(crate) struct VariableBoundWithDifferentMode { + #[primary_span] + #[label] + pub(crate) span: Span, + #[label(first_binding_span)] + pub(crate) first_binding_span: Span, + pub(crate) variable_name: Symbol, +} + +#[derive(Diagnostic)] +#[diag(resolve_ident_bound_more_than_once_in_parameter_list, code = "E0415")] +pub(crate) struct IdentifierBoundMoreThanOnceInParameterList { + #[primary_span] + #[label] + pub(crate) span: Span, + pub(crate) identifier: Symbol, +} + +#[derive(Diagnostic)] +#[diag(resolve_ident_bound_more_than_once_in_same_pattern, code = "E0416")] +pub(crate) struct IdentifierBoundMoreThanOnceInSamePattern { + #[primary_span] + #[label] + pub(crate) span: Span, + pub(crate) identifier: Symbol, +} + +#[derive(Diagnostic)] +#[diag(resolve_undeclared_label, code = "E0426")] +pub(crate) struct UndeclaredLabel { + #[primary_span] + #[label] + pub(crate) span: Span, + pub(crate) name: Symbol, + #[subdiagnostic] + pub(crate) sub_reachable: Option<LabelWithSimilarNameReachable>, + #[subdiagnostic] + pub(crate) sub_reachable_suggestion: Option<TryUsingSimilarlyNamedLabel>, + #[subdiagnostic] + pub(crate) sub_unreachable: Option<UnreachableLabelWithSimilarNameExists>, +} + +#[derive(Subdiagnostic)] +#[label(resolve_label_with_similar_name_reachable)] +pub(crate) struct LabelWithSimilarNameReachable(#[primary_span] pub(crate) Span); + +#[derive(Subdiagnostic)] +#[suggestion( + resolve_try_using_similarly_named_label, + code = "{ident_name}", + applicability = "maybe-incorrect" +)] +pub(crate) struct TryUsingSimilarlyNamedLabel { + #[primary_span] + pub(crate) span: Span, + pub(crate) ident_name: Symbol, +} + +#[derive(Subdiagnostic)] +#[label(resolve_unreachable_label_with_similar_name_exists)] +pub(crate) struct UnreachableLabelWithSimilarNameExists { + #[primary_span] + pub(crate) ident_span: Span, +} + +#[derive(Diagnostic)] +#[diag(resolve_self_import_can_only_appear_once_in_the_list, code = "E0430")] +pub(crate) struct SelfImportCanOnlyAppearOnceInTheList { + #[primary_span] + #[label] + pub(crate) span: Span, +} + +#[derive(Diagnostic)] +#[diag(resolve_self_import_only_in_import_list_with_non_empty_prefix, code = "E0431")] +pub(crate) struct SelfImportOnlyInImportListWithNonEmptyPrefix { + #[primary_span] + #[label] + pub(crate) span: Span, +} + +#[derive(Diagnostic)] +#[diag(resolve_cannot_capture_dynamic_environment_in_fn_item, code = "E0434")] +#[help] +pub(crate) struct CannotCaptureDynamicEnvironmentInFnItem { + #[primary_span] + pub(crate) span: Span, +} + +#[derive(Diagnostic)] +#[diag(resolve_attempt_to_use_non_constant_value_in_constant, code = "E0435")] +pub(crate) struct AttemptToUseNonConstantValueInConstant<'a> { + #[primary_span] + pub(crate) span: Span, + #[subdiagnostic] + pub(crate) with: Option<AttemptToUseNonConstantValueInConstantWithSuggestion<'a>>, + #[subdiagnostic] + pub(crate) with_label: Option<AttemptToUseNonConstantValueInConstantLabelWithSuggestion>, + #[subdiagnostic] + pub(crate) without: Option<AttemptToUseNonConstantValueInConstantWithoutSuggestion<'a>>, +} + +#[derive(Subdiagnostic)] +#[suggestion( + resolve_attempt_to_use_non_constant_value_in_constant_with_suggestion, + code = "{suggestion} {ident}", + applicability = "maybe-incorrect" +)] +pub(crate) struct AttemptToUseNonConstantValueInConstantWithSuggestion<'a> { + #[primary_span] + pub(crate) span: Span, + pub(crate) ident: Ident, + pub(crate) suggestion: &'a str, + pub(crate) current: &'a str, +} + +#[derive(Subdiagnostic)] +#[label(resolve_attempt_to_use_non_constant_value_in_constant_label_with_suggestion)] +pub(crate) struct AttemptToUseNonConstantValueInConstantLabelWithSuggestion { + #[primary_span] + pub(crate) span: Span, +} + +#[derive(Subdiagnostic)] +#[label(resolve_attempt_to_use_non_constant_value_in_constant_without_suggestion)] +pub(crate) struct AttemptToUseNonConstantValueInConstantWithoutSuggestion<'a> { + #[primary_span] + pub(crate) ident_span: Span, + pub(crate) suggestion: &'a str, +} + +#[derive(Diagnostic)] +#[diag(resolve_self_imports_only_allowed_within, code = "E0429")] +pub(crate) struct SelfImportsOnlyAllowedWithin { + #[primary_span] + pub(crate) span: Span, + #[subdiagnostic] + pub(crate) suggestion: Option<SelfImportsOnlyAllowedWithinSuggestion>, + #[subdiagnostic] + pub(crate) mpart_suggestion: Option<SelfImportsOnlyAllowedWithinMultipartSuggestion>, +} + +#[derive(Subdiagnostic)] +#[suggestion( + resolve_self_imports_only_allowed_within_suggestion, + code = "", + applicability = "machine-applicable" +)] +pub(crate) struct SelfImportsOnlyAllowedWithinSuggestion { + #[primary_span] + pub(crate) span: Span, +} + +#[derive(Subdiagnostic)] +#[multipart_suggestion( + resolve_self_imports_only_allowed_within_multipart_suggestion, + applicability = "machine-applicable" +)] +pub(crate) struct SelfImportsOnlyAllowedWithinMultipartSuggestion { + #[suggestion_part(code = "{{")] + pub(crate) multipart_start: Span, + #[suggestion_part(code = "}}")] + pub(crate) multipart_end: Span, +} + +#[derive(Diagnostic)] +#[diag(resolve_binding_shadows_something_unacceptable, code = "E0530")] +pub(crate) struct BindingShadowsSomethingUnacceptable<'a> { + #[primary_span] + #[label] + pub(crate) span: Span, + pub(crate) shadowing_binding: PatternSource, + pub(crate) shadowed_binding: Res, + pub(crate) article: &'a str, + #[subdiagnostic] + pub(crate) sub_suggestion: Option<BindingShadowsSomethingUnacceptableSuggestion>, + #[label(label_shadowed_binding)] + pub(crate) shadowed_binding_span: Span, + pub(crate) participle: &'a str, + pub(crate) name: Symbol, +} + +#[derive(Subdiagnostic)] +#[suggestion( + resolve_binding_shadows_something_unacceptable_suggestion, + code = "{name}(..)", + applicability = "unspecified" +)] +pub(crate) struct BindingShadowsSomethingUnacceptableSuggestion { + #[primary_span] + pub(crate) span: Span, + pub(crate) name: Symbol, +} + +#[derive(Diagnostic)] +#[diag(resolve_forward_declared_generic_param, code = "E0128")] +pub(crate) struct ForwardDeclaredGenericParam { + #[primary_span] + #[label] + pub(crate) span: Span, +} + +#[derive(Diagnostic)] +#[diag(resolve_param_in_ty_of_const_param, code = "E0770")] +pub(crate) struct ParamInTyOfConstParam { + #[primary_span] + #[label] + pub(crate) span: Span, + pub(crate) name: Symbol, +} + +#[derive(Diagnostic)] +#[diag(resolve_self_in_generic_param_default, code = "E0735")] +pub(crate) struct SelfInGenericParamDefault { + #[primary_span] + #[label] + pub(crate) span: Span, +} + +#[derive(Diagnostic)] +#[diag(resolve_param_in_non_trivial_anon_const)] +pub(crate) struct ParamInNonTrivialAnonConst { + #[primary_span] + #[label] + pub(crate) span: Span, + pub(crate) name: Symbol, + #[subdiagnostic] + pub(crate) sub_is_type: ParamInNonTrivialAnonConstIsType, + #[subdiagnostic] + pub(crate) help: Option<ParamInNonTrivialAnonConstHelp>, +} + +#[derive(Subdiagnostic)] +#[help(resolve_param_in_non_trivial_anon_const_help)] +pub(crate) struct ParamInNonTrivialAnonConstHelp; + +#[derive(Subdiagnostic)] +pub(crate) enum ParamInNonTrivialAnonConstIsType { + #[note(resolve_param_in_non_trivial_anon_const_sub_type)] + AType, + #[help(resolve_param_in_non_trivial_anon_const_sub_non_type)] + NotAType { name: Symbol }, +} + +#[derive(Diagnostic)] +#[diag(resolve_unreachable_label, code = "E0767")] +#[note] +pub(crate) struct UnreachableLabel { + #[primary_span] + #[label] + pub(crate) span: Span, + pub(crate) name: Symbol, + #[label(label_definition_span)] + pub(crate) definition_span: Span, + #[subdiagnostic] + pub(crate) sub_suggestion: Option<UnreachableLabelSubSuggestion>, + #[subdiagnostic] + pub(crate) sub_suggestion_label: Option<UnreachableLabelSubLabel>, + #[subdiagnostic] + pub(crate) sub_unreachable_label: Option<UnreachableLabelSubLabelUnreachable>, +} + +#[derive(Subdiagnostic)] +#[suggestion( + resolve_unreachable_label_suggestion_use_similarly_named, + code = "{ident_name}", + applicability = "maybe-incorrect" +)] +pub(crate) struct UnreachableLabelSubSuggestion { + #[primary_span] + pub(crate) span: Span, + pub(crate) ident_name: Symbol, +} + +#[derive(Subdiagnostic)] +#[label(resolve_unreachable_label_similar_name_reachable)] +pub(crate) struct UnreachableLabelSubLabel { + #[primary_span] + pub(crate) ident_span: Span, +} + +#[derive(Subdiagnostic)] +#[label(resolve_unreachable_label_similar_name_unreachable)] +pub(crate) struct UnreachableLabelSubLabelUnreachable { + #[primary_span] + pub(crate) ident_span: Span, +} + +#[derive(Diagnostic)] +#[diag(resolve_trait_impl_mismatch, code = "{code}")] +pub(crate) struct TraitImplMismatch { + #[primary_span] + #[label] + pub(crate) span: Span, + pub(crate) name: Symbol, + pub(crate) kind: String, + #[label(label_trait_item)] + pub(crate) trait_item_span: Span, + pub(crate) trait_path: String, + pub(crate) code: String, +} + +#[derive(Diagnostic)] +#[diag(resolve_invalid_asm_sym)] +#[help] +pub(crate) struct InvalidAsmSym { + #[primary_span] + #[label] + pub(crate) span: Span, +} + +#[derive(Diagnostic)] +#[diag(resolve_trait_impl_duplicate, code = "E0201")] +pub(crate) struct TraitImplDuplicate { + #[primary_span] + #[label] + pub(crate) span: Span, + #[label(old_span_label)] + pub(crate) old_span: Span, + #[label(trait_item_span)] + pub(crate) trait_item_span: Span, + pub(crate) name: Symbol, +} + +#[derive(Diagnostic)] +#[diag(resolve_relative_2018)] +pub(crate) struct Relative2018 { + #[primary_span] + pub(crate) span: Span, + #[suggestion(code = "crate::{path_str}", applicability = "maybe-incorrect")] + pub(crate) path_span: Span, + pub(crate) path_str: String, +} + +#[derive(Diagnostic)] +#[diag(resolve_ancestor_only, code = "E0742")] +pub(crate) struct AncestorOnly(#[primary_span] pub(crate) Span); + +#[derive(Diagnostic)] +#[diag(resolve_expected_found, code = "E0577")] +pub(crate) struct ExpectedFound { + #[primary_span] + #[label] + pub(crate) span: Span, + pub(crate) res: Res, + pub(crate) path_str: String, +} + +#[derive(Diagnostic)] +#[diag(resolve_indeterminate, code = "E0578")] +pub(crate) struct Indeterminate(#[primary_span] pub(crate) Span); + +#[derive(Diagnostic)] +#[diag(resolve_module_only)] +pub(crate) struct ModuleOnly(#[primary_span] pub(crate) Span); diff --git a/compiler/rustc_resolve/src/ident.rs b/compiler/rustc_resolve/src/ident.rs index e0542d547..759818856 100644 --- a/compiler/rustc_resolve/src/ident.rs +++ b/compiler/rustc_resolve/src/ident.rs @@ -19,7 +19,7 @@ use crate::late::{ }; use crate::macros::{sub_namespace_match, MacroRulesScope}; use crate::{AmbiguityError, AmbiguityErrorMisc, AmbiguityKind, Determinacy, Finalize}; -use crate::{ImportKind, LexicalScopeBinding, Module, ModuleKind, ModuleOrUniformRoot}; +use crate::{Import, ImportKind, LexicalScopeBinding, Module, ModuleKind, ModuleOrUniformRoot}; use crate::{NameBinding, NameBindingKind, ParentScope, PathResult, PrivacyError, Res}; use crate::{ResolutionError, Resolver, Scope, ScopeSet, Segment, ToNameBinding, Weak}; @@ -915,7 +915,11 @@ impl<'a> Resolver<'a> { } if !restricted_shadowing && binding.expansion != LocalExpnId::ROOT { - if let NameBindingKind::Res(_, true) = binding.kind { + if let NameBindingKind::Import { + import: Import { kind: ImportKind::MacroExport, .. }, + .. + } = binding.kind + { self.macro_expanded_macro_export_errors.insert((path_span, binding.span)); } } diff --git a/compiler/rustc_resolve/src/imports.rs b/compiler/rustc_resolve/src/imports.rs index f2cc50c19..b100a8c17 100644 --- a/compiler/rustc_resolve/src/imports.rs +++ b/compiler/rustc_resolve/src/imports.rs @@ -4,7 +4,10 @@ use crate::diagnostics::{import_candidates, Suggestion}; use crate::Determinacy::{self, *}; use crate::Namespace::*; use crate::{module_to_string, names_to_string, ImportSuggestion}; -use crate::{AmbiguityKind, BindingKey, ModuleKind, ResolutionError, Resolver, Segment}; +use crate::{ + AmbiguityError, AmbiguityErrorMisc, AmbiguityKind, BindingKey, ModuleKind, ResolutionError, + Resolver, Segment, +}; use crate::{Finalize, Module, ModuleOrUniformRoot, ParentScope, PerNS, ScopeSet}; use crate::{NameBinding, NameBindingKind, PathResult}; @@ -44,20 +47,33 @@ pub enum ImportKind<'a> { type_ns_only: bool, /// Did this import result from a nested import? ie. `use foo::{bar, baz};` nested: bool, - /// Additional `NodeId`s allocated to a `ast::UseTree` for automatically generated `use` statement - /// (eg. implicit struct constructors) - additional_ids: (NodeId, NodeId), + /// The ID of the `UseTree` that imported this `Import`. + /// + /// In the case where the `Import` was expanded from a "nested" use tree, + /// this id is the ID of the leaf tree. For example: + /// + /// ```ignore (pacify the merciless tidy) + /// use foo::bar::{a, b} + /// ``` + /// + /// If this is the import for `foo::bar::a`, we would have the ID of the `UseTree` + /// for `a` in this field. + id: NodeId, }, Glob { is_prelude: bool, - max_vis: Cell<Option<ty::Visibility>>, // The visibility of the greatest re-export. - // n.b. `max_vis` is only used in `finalize_import` to check for re-export errors. + // The visibility of the greatest re-export. + // n.b. `max_vis` is only used in `finalize_import` to check for re-export errors. + max_vis: Cell<Option<ty::Visibility>>, + id: NodeId, }, ExternCrate { source: Option<Symbol>, target: Ident, + id: NodeId, }, MacroUse, + MacroExport, } /// Manually implement `Debug` for `ImportKind` because the `source/target_bindings` @@ -71,7 +87,7 @@ impl<'a> std::fmt::Debug for ImportKind<'a> { ref target, ref type_ns_only, ref nested, - ref additional_ids, + ref id, // Ignore the following to avoid an infinite loop while printing. source_bindings: _, target_bindings: _, @@ -81,19 +97,22 @@ impl<'a> std::fmt::Debug for ImportKind<'a> { .field("target", target) .field("type_ns_only", type_ns_only) .field("nested", nested) - .field("additional_ids", additional_ids) + .field("id", id) .finish_non_exhaustive(), - Glob { ref is_prelude, ref max_vis } => f + Glob { ref is_prelude, ref max_vis, ref id } => f .debug_struct("Glob") .field("is_prelude", is_prelude) .field("max_vis", max_vis) + .field("id", id) .finish(), - ExternCrate { ref source, ref target } => f + ExternCrate { ref source, ref target, ref id } => f .debug_struct("ExternCrate") .field("source", source) .field("target", target) + .field("id", id) .finish(), MacroUse => f.debug_struct("MacroUse").finish(), + MacroExport => f.debug_struct("MacroExport").finish(), } } } @@ -103,24 +122,15 @@ impl<'a> std::fmt::Debug for ImportKind<'a> { pub(crate) struct Import<'a> { pub kind: ImportKind<'a>, - /// The ID of the `extern crate`, `UseTree` etc that imported this `Import`. - /// - /// In the case where the `Import` was expanded from a "nested" use tree, - /// this id is the ID of the leaf tree. For example: - /// - /// ```ignore (pacify the merciless tidy) + /// Node ID of the "root" use item -- this is always the same as `ImportKind`'s `id` + /// (if it exists) except in the case of "nested" use trees, in which case + /// it will be the ID of the root use tree. e.g., in the example + /// ```ignore (incomplete code) /// use foo::bar::{a, b} /// ``` - /// - /// If this is the import for `foo::bar::a`, we would have the ID of the `UseTree` - /// for `a` in this field. - pub id: NodeId, - - /// The `id` of the "root" use-kind -- this is always the same as - /// `id` except in the case of "nested" use trees, in which case - /// it will be the `id` of the root use tree. e.g., in the example - /// from `id`, this would be the ID of the `use foo::bar` - /// `UseTree` node. + /// this would be the ID of the `use foo::bar` `UseTree` node. + /// In case of imports without their own node ID it's the closest node that can be used, + /// for example, for reporting lints. pub root_id: NodeId, /// Span of the entire use statement. @@ -161,6 +171,15 @@ impl<'a> Import<'a> { pub(crate) fn expect_vis(&self) -> ty::Visibility { self.vis.get().expect("encountered cleared import visibility") } + + pub(crate) fn id(&self) -> Option<NodeId> { + match self.kind { + ImportKind::Single { id, .. } + | ImportKind::Glob { id, .. } + | ImportKind::ExternCrate { id, .. } => Some(id), + ImportKind::MacroUse | ImportKind::MacroExport => None, + } + } } /// Records information about the resolution of a name in a namespace of a module. @@ -175,7 +194,7 @@ pub(crate) struct NameResolution<'a> { } impl<'a> NameResolution<'a> { - // Returns the binding for the name if it is known or None if it not known. + /// Returns the binding for the name if it is known or None if it not known. pub(crate) fn binding(&self) -> Option<&'a NameBinding<'a>> { self.binding.and_then(|binding| { if !binding.is_glob_import() || self.single_imports.is_empty() { @@ -207,8 +226,8 @@ fn pub_use_of_private_extern_crate_hack(import: &Import<'_>, binding: &NameBindi } impl<'a> Resolver<'a> { - // Given a binding and an import that resolves to it, - // return the corresponding binding defined by the import. + /// Given a binding and an import that resolves to it, + /// return the corresponding binding defined by the import. pub(crate) fn import( &self, binding: &'a NameBinding<'a>, @@ -240,7 +259,7 @@ impl<'a> Resolver<'a> { }) } - // Define the name or return the existing binding if there is a collision. + /// Define the name or return the existing binding if there is a collision. pub(crate) fn try_define( &mut self, module: Module<'a>, @@ -368,7 +387,9 @@ impl<'a> Resolver<'a> { self.record_use(target, dummy_binding, false); } else if import.imported_module.get().is_none() { import.used.set(true); - self.used_imports.insert(import.id); + if let Some(id) = import.id() { + self.used_imports.insert(id); + } } } } @@ -450,7 +471,7 @@ impl<'a, 'b> ImportResolver<'a, 'b> { { // In the case of a new import line, throw a diagnostic message // for the previous line. - self.throw_unresolved_import_error(errors, None); + self.throw_unresolved_import_error(errors); errors = vec![]; } if seen_spans.insert(err.span) { @@ -482,29 +503,21 @@ impl<'a, 'b> ImportResolver<'a, 'b> { } if !errors.is_empty() { - self.throw_unresolved_import_error(errors, None); + self.throw_unresolved_import_error(errors); } } - fn throw_unresolved_import_error( - &self, - errors: Vec<(String, UnresolvedImportError)>, - span: Option<MultiSpan>, - ) { + fn throw_unresolved_import_error(&self, errors: Vec<(String, UnresolvedImportError)>) { + if errors.is_empty() { + return; + } + /// Upper limit on the number of `span_label` messages. const MAX_LABEL_COUNT: usize = 10; - let (span, msg) = if errors.is_empty() { - (span.unwrap(), "unresolved import".to_string()) - } else { - let span = MultiSpan::from_spans(errors.iter().map(|(_, err)| err.span).collect()); - - let paths = errors.iter().map(|(path, _)| format!("`{}`", path)).collect::<Vec<_>>(); - - let msg = format!("unresolved import{} {}", pluralize!(paths.len()), paths.join(", "),); - - (span, msg) - }; + let span = MultiSpan::from_spans(errors.iter().map(|(_, err)| err.span).collect()); + let paths = errors.iter().map(|(path, _)| format!("`{}`", path)).collect::<Vec<_>>(); + let msg = format!("unresolved import{} {}", pluralize!(paths.len()), paths.join(", "),); let mut diag = struct_span_err!(self.r.session, span, E0432, "{}", &msg); @@ -718,47 +731,51 @@ impl<'a, 'b> ImportResolver<'a, 'b> { PathResult::Indeterminate => unreachable!(), }; - let (ident, target, source_bindings, target_bindings, type_ns_only) = match import.kind { - ImportKind::Single { - source, - target, - ref source_bindings, - ref target_bindings, - type_ns_only, - .. - } => (source, target, source_bindings, target_bindings, type_ns_only), - ImportKind::Glob { is_prelude, ref max_vis } => { - if import.module_path.len() <= 1 { - // HACK(eddyb) `lint_if_path_starts_with_module` needs at least - // 2 segments, so the `resolve_path` above won't trigger it. - let mut full_path = import.module_path.clone(); - full_path.push(Segment::from_ident(Ident::empty())); - self.r.lint_if_path_starts_with_module(Some(finalize), &full_path, None); - } + let (ident, target, source_bindings, target_bindings, type_ns_only, import_id) = + match import.kind { + ImportKind::Single { + source, + target, + ref source_bindings, + ref target_bindings, + type_ns_only, + id, + .. + } => (source, target, source_bindings, target_bindings, type_ns_only, id), + ImportKind::Glob { is_prelude, ref max_vis, id } => { + if import.module_path.len() <= 1 { + // HACK(eddyb) `lint_if_path_starts_with_module` needs at least + // 2 segments, so the `resolve_path` above won't trigger it. + let mut full_path = import.module_path.clone(); + full_path.push(Segment::from_ident(Ident::empty())); + self.r.lint_if_path_starts_with_module(Some(finalize), &full_path, None); + } - if let ModuleOrUniformRoot::Module(module) = module { - if ptr::eq(module, import.parent_scope.module) { - // Importing a module into itself is not allowed. - return Some(UnresolvedImportError { - span: import.span, - label: Some(String::from("cannot glob-import a module into itself")), - note: None, - suggestion: None, - candidate: None, - }); + if let ModuleOrUniformRoot::Module(module) = module { + if ptr::eq(module, import.parent_scope.module) { + // Importing a module into itself is not allowed. + return Some(UnresolvedImportError { + span: import.span, + label: Some(String::from( + "cannot glob-import a module into itself", + )), + note: None, + suggestion: None, + candidate: None, + }); + } } - } - if !is_prelude + if !is_prelude && let Some(max_vis) = max_vis.get() && !max_vis.is_at_least(import.expect_vis(), &*self.r) { let msg = "glob import doesn't reexport anything because no candidate is public enough"; - self.r.lint_buffer.buffer_lint(UNUSED_IMPORTS, import.id, import.span, msg); + self.r.lint_buffer.buffer_lint(UNUSED_IMPORTS, id, import.span, msg); } - return None; - } - _ => unreachable!(), - }; + return None; + } + _ => unreachable!(), + }; let mut all_ns_err = true; self.r.per_ns(|this, ns| { @@ -777,7 +794,7 @@ impl<'a, 'b> ImportResolver<'a, 'b> { match binding { Ok(binding) => { // Consistency checks, analogous to `finalize_macro_resolutions`. - let initial_res = source_bindings[ns].get().map(|initial_binding| { + let initial_binding = source_bindings[ns].get().map(|initial_binding| { all_ns_err = false; if let Some(target_binding) = target_bindings[ns].get() { if target.name == kw::Underscore @@ -791,12 +808,20 @@ impl<'a, 'b> ImportResolver<'a, 'b> { ); } } - initial_binding.res() + initial_binding }); let res = binding.res(); - if let Ok(initial_res) = initial_res { + if let Ok(initial_binding) = initial_binding { + let initial_res = initial_binding.res(); if res != initial_res && this.ambiguity_errors.is_empty() { - span_bug!(import.span, "inconsistent resolution for an import"); + this.ambiguity_errors.push(AmbiguityError { + kind: AmbiguityKind::Import, + ident, + b1: initial_binding, + b2: binding, + misc1: AmbiguityErrorMisc::None, + misc2: AmbiguityErrorMisc::None, + }); } } else if res != Res::Err && this.ambiguity_errors.is_empty() @@ -858,7 +883,7 @@ impl<'a, 'b> ImportResolver<'a, 'b> { match binding.kind { // Never suggest the name that has binding error // i.e., the name that cannot be previously resolved - NameBindingKind::Res(Res::Err, _) => None, + NameBindingKind::Res(Res::Err) => None, _ => Some(i.name), } } @@ -960,7 +985,7 @@ impl<'a, 'b> ImportResolver<'a, 'b> { ); self.r.lint_buffer.buffer_lint( PUB_USE_OF_PRIVATE_EXTERN_CRATE, - import.id, + import_id, import.span, &msg, ); @@ -989,7 +1014,7 @@ impl<'a, 'b> ImportResolver<'a, 'b> { let mut err = struct_span_err!(self.r.session, import.span, E0364, "{error_msg}"); match binding.kind { - NameBindingKind::Res(Res::Def(DefKind::Macro(_), def_id), _) + NameBindingKind::Res(Res::Def(DefKind::Macro(_), def_id)) // exclude decl_macro if self.r.get_macro_by_def_id(def_id).macro_rules => { @@ -1029,7 +1054,7 @@ impl<'a, 'b> ImportResolver<'a, 'b> { // purposes it's good enough to just favor one over the other. self.r.per_ns(|this, ns| { if let Ok(binding) = source_bindings[ns].get() { - this.import_res_map.entry(import.id).or_default()[ns] = Some(binding.res()); + this.import_res_map.entry(import_id).or_default()[ns] = Some(binding.res()); } }); @@ -1047,6 +1072,9 @@ impl<'a, 'b> ImportResolver<'a, 'b> { target_bindings: &PerNS<Cell<Option<&'b NameBinding<'b>>>>, target: Ident, ) { + // This function is only called for single imports. + let ImportKind::Single { id, .. } = import.kind else { unreachable!() }; + // Skip if the import was produced by a macro. if import.parent_scope.expansion != LocalExpnId::ROOT { return; @@ -1094,7 +1122,7 @@ impl<'a, 'b> ImportResolver<'a, 'b> { redundant_spans.dedup(); self.r.lint_buffer.buffer_lint_with_diagnostic( UNUSED_IMPORTS, - import.id, + id, import.span, &format!("the item `{}` is imported redundantly", ident), BuiltinLintDiagnostics::RedundantImport(redundant_spans, ident), @@ -1103,6 +1131,9 @@ impl<'a, 'b> ImportResolver<'a, 'b> { } fn resolve_glob_import(&mut self, import: &'b Import<'b>) { + // This function is only called for glob imports. + let ImportKind::Glob { id, is_prelude, .. } = import.kind else { unreachable!() }; + let ModuleOrUniformRoot::Module(module) = import.imported_module.get().unwrap() else { self.r.session.span_err(import.span, "cannot glob-import all possible crates"); return; @@ -1113,7 +1144,7 @@ impl<'a, 'b> ImportResolver<'a, 'b> { return; } else if ptr::eq(module, import.parent_scope.module) { return; - } else if let ImportKind::Glob { is_prelude: true, .. } = import.kind { + } else if is_prelude { self.r.prelude = Some(module); return; } @@ -1145,7 +1176,7 @@ impl<'a, 'b> ImportResolver<'a, 'b> { } // Record the destination of this import - self.r.record_partial_res(import.id, PartialRes::new(module.res().unwrap())); + self.r.record_partial_res(id, PartialRes::new(module.res().unwrap())); } // Miscellaneous post-processing, including recording re-exports, @@ -1204,5 +1235,6 @@ fn import_kind_to_string(import_kind: &ImportKind<'_>) -> String { ImportKind::Glob { .. } => "*".to_string(), ImportKind::ExternCrate { .. } => "<extern crate>".to_string(), ImportKind::MacroUse => "#[macro_use]".to_string(), + ImportKind::MacroExport => "#[macro_export]".to_string(), } } diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index 00eb768ad..cf3e59460 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -16,7 +16,7 @@ use rustc_ast::ptr::P; use rustc_ast::visit::{self, AssocCtxt, BoundKind, FnCtxt, FnKind, Visitor}; use rustc_ast::*; use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap}; -use rustc_errors::DiagnosticId; +use rustc_errors::{DiagnosticArgValue, DiagnosticId, IntoDiagnosticArg}; use rustc_hir::def::Namespace::{self, *}; use rustc_hir::def::{self, CtorKind, DefKind, LifetimeRes, PartialRes, PerNS}; use rustc_hir::def_id::{DefId, LocalDefId, CRATE_DEF_ID, LOCAL_CRATE}; @@ -31,8 +31,9 @@ use smallvec::{smallvec, SmallVec}; use rustc_span::source_map::{respan, Spanned}; use std::assert_matches::debug_assert_matches; +use std::borrow::Cow; use std::collections::{hash_map::Entry, BTreeSet}; -use std::mem::{replace, take}; +use std::mem::{replace, swap, take}; mod diagnostics; @@ -78,6 +79,12 @@ impl PatternSource { } } +impl IntoDiagnosticArg for PatternSource { + fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> { + DiagnosticArgValue::Str(Cow::Borrowed(self.descr())) + } +} + /// Denotes whether the context for the set of already bound bindings is a `Product` /// or `Or` context. This is used in e.g., `fresh_binding` and `resolve_pattern_inner`. /// See those functions for more information. @@ -527,6 +534,7 @@ struct DiagnosticMetadata<'ast> { /// Used to detect possible new binding written without `let` and to provide structured suggestion. in_assignment: Option<&'ast Expr>, + is_assign_rhs: bool, /// If we are currently in a trait object definition. Used to point at the bounds when /// encountering a struct or enum. @@ -647,7 +655,7 @@ impl<'a: 'ast, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> { } TyKind::Path(ref qself, ref path) => { self.diagnostic_metadata.current_type_path = Some(ty); - self.smart_resolve_path(ty.id, qself.as_ref(), path, PathSource::Type); + self.smart_resolve_path(ty.id, &qself, path, PathSource::Type); // Check whether we should interpret this as a bare trait object. if qself.is_none() @@ -748,7 +756,7 @@ impl<'a: 'ast, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> { this.visit_generic_params(&tref.bound_generic_params, false); this.smart_resolve_path( tref.trait_ref.ref_id, - None, + &None, &tref.trait_ref.path, PathSource::Trait(AliasPossibility::Maybe), ); @@ -977,7 +985,7 @@ impl<'a: 'ast, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> { |this| { this.smart_resolve_path( ty.id, - qself.as_ref(), + qself, path, PathSource::Expr(None), ); @@ -1137,12 +1145,7 @@ impl<'a: 'ast, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> { self.with_rib(ValueNS, InlineAsmSymRibKind, |this| { this.with_rib(TypeNS, InlineAsmSymRibKind, |this| { this.with_label_rib(InlineAsmSymRibKind, |this| { - this.smart_resolve_path( - sym.id, - sym.qself.as_ref(), - &sym.path, - PathSource::Expr(None), - ); + this.smart_resolve_path(sym.id, &sym.qself, &sym.path, PathSource::Expr(None)); visit::walk_inline_asm_sym(this, sym); }); }) @@ -1835,6 +1838,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { let outer_failures = take(&mut self.diagnostic_metadata.current_elision_failures); let output_rib = if let Ok(res) = elision_lifetime.as_ref() { + self.r.lifetime_elision_allowed.insert(fn_id); LifetimeRibKind::Elided(*res) } else { LifetimeRibKind::ElisionFailure @@ -1923,7 +1927,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { // We have a single lifetime => success. elision_lifetime = Elision::Param(res) } else { - // We have have multiple lifetimes => error. + // We have multiple lifetimes => error. elision_lifetime = Elision::Err; } } @@ -2356,8 +2360,8 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { if let GenericParamKind::Lifetime = param.kind { // Record lifetime res, so lowering knows there is something fishy. self.record_lifetime_param(param.id, LifetimeRes::Error); - continue; } + continue; } Entry::Vacant(entry) => { entry.insert(param.ident.span); @@ -2570,7 +2574,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { self.diagnostic_metadata.currently_processing_impl_trait = Some((trait_ref.clone(), self_type.clone())); let res = self.smart_resolve_path_fragment( - None, + &None, &path, PathSource::Trait(AliasPossibility::No), Finalize::new(trait_ref.ref_id, trait_ref.path.span), @@ -3093,7 +3097,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { PatKind::TupleStruct(ref qself, ref path, ref sub_patterns) => { self.smart_resolve_path( pat.id, - qself.as_ref(), + qself, path, PathSource::TupleStruct( pat.span, @@ -3102,10 +3106,10 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { ); } PatKind::Path(ref qself, ref path) => { - self.smart_resolve_path(pat.id, qself.as_ref(), path, PathSource::Pat); + self.smart_resolve_path(pat.id, qself, path, PathSource::Pat); } PatKind::Struct(ref qself, ref path, ..) => { - self.smart_resolve_path(pat.id, qself.as_ref(), path, PathSource::Struct); + self.smart_resolve_path(pat.id, qself, path, PathSource::Struct); } PatKind::Or(ref ps) => { // Add a new set of bindings to the stack. `Or` here records that when a @@ -3298,7 +3302,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { fn smart_resolve_path( &mut self, id: NodeId, - qself: Option<&QSelf>, + qself: &Option<P<QSelf>>, path: &Path, source: PathSource<'ast>, ) { @@ -3312,7 +3316,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { fn smart_resolve_path_fragment( &mut self, - qself: Option<&QSelf>, + qself: &Option<P<QSelf>>, path: &[Segment], source: PathSource<'ast>, finalize: Finalize, @@ -3361,18 +3365,13 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { // Before we start looking for candidates, we have to get our hands // on the type user is trying to perform invocation on; basically: // we're transforming `HashMap::new` into just `HashMap`. - let path = match path.split_last() { + let prefix_path = match path.split_last() { Some((_, path)) if !path.is_empty() => path, _ => return Some(parent_err), }; let (mut err, candidates) = - this.smart_resolve_report_errors(path, path_span, PathSource::Type, None); - - if candidates.is_empty() { - err.cancel(); - return Some(parent_err); - } + this.smart_resolve_report_errors(prefix_path, path_span, PathSource::Type, None); // There are two different error messages user might receive at // this point: @@ -3383,37 +3382,74 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { // latter one - for paths in expression-position. // // Thus (since we're in expression-position at this point), not to - // confuse the user, we want to keep the *message* from E0432 (so + // confuse the user, we want to keep the *message* from E0433 (so // `parent_err`), but we want *hints* from E0412 (so `err`). // // And that's what happens below - we're just mixing both messages // into a single one. let mut parent_err = this.r.into_struct_error(parent_err.span, parent_err.node); + // overwrite all properties with the parent's error message err.message = take(&mut parent_err.message); err.code = take(&mut parent_err.code); + swap(&mut err.span, &mut parent_err.span); err.children = take(&mut parent_err.children); + err.sort_span = parent_err.sort_span; + err.is_lint = parent_err.is_lint; + + // merge the parent's suggestions with the typo suggestions + fn append_result<T, E>(res1: &mut Result<Vec<T>, E>, res2: Result<Vec<T>, E>) { + match res1 { + Ok(vec1) => match res2 { + Ok(mut vec2) => vec1.append(&mut vec2), + Err(e) => *res1 = Err(e), + }, + Err(_) => (), + }; + } + append_result(&mut err.suggestions, parent_err.suggestions.clone()); parent_err.cancel(); let def_id = this.parent_scope.module.nearest_parent_mod(); if this.should_report_errs() { - this.r.use_injections.push(UseError { - err, - candidates, - def_id, - instead: false, - suggestion: None, - path: path.into(), - is_call: source.is_call(), - }); + if candidates.is_empty() { + if path.len() == 2 && prefix_path.len() == 1 { + // Delay to check whether methond name is an associated function or not + // ``` + // let foo = Foo {}; + // foo::bar(); // possibly suggest to foo.bar(); + //``` + err.stash( + prefix_path[0].ident.span, + rustc_errors::StashKey::CallAssocMethod, + ); + } else { + // When there is no suggested imports, we can just emit the error + // and suggestions immediately. Note that we bypass the usually error + // reporting routine (ie via `self.r.report_error`) because we need + // to post-process the `ResolutionError` above. + err.emit(); + } + } else { + // If there are suggested imports, the error reporting is delayed + this.r.use_injections.push(UseError { + err, + candidates, + def_id, + instead: false, + suggestion: None, + path: prefix_path.into(), + is_call: source.is_call(), + }); + } } else { err.cancel(); } // We don't return `Some(parent_err)` here, because the error will - // be already printed as part of the `use` injections + // be already printed either immediately or as part of the `use` injections None }; @@ -3513,7 +3549,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { // Resolve in alternative namespaces if resolution in the primary namespace fails. fn resolve_qpath_anywhere( &mut self, - qself: Option<&QSelf>, + qself: &Option<P<QSelf>>, path: &[Segment], primary_ns: Namespace, span: Span, @@ -3557,7 +3593,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { /// Handles paths that may refer to associated items. fn resolve_qpath( &mut self, - qself: Option<&QSelf>, + qself: &Option<P<QSelf>>, path: &[Segment], ns: Namespace, finalize: Finalize, @@ -3587,7 +3623,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { // but with `qself` set to `None`. let ns = if qself.position + 1 == path.len() { ns } else { TypeNS }; let partial_res = self.smart_resolve_path_fragment( - None, + &None, &path[..=qself.position], PathSource::TraitItem(ns), Finalize::with_root_span(finalize.node_id, finalize.path_span, qself.path_span), @@ -3770,12 +3806,12 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { // Next, resolve the node. match expr.kind { ExprKind::Path(ref qself, ref path) => { - self.smart_resolve_path(expr.id, qself.as_ref(), path, PathSource::Expr(parent)); + self.smart_resolve_path(expr.id, qself, path, PathSource::Expr(parent)); visit::walk_expr(self, expr); } ExprKind::Struct(ref se) => { - self.smart_resolve_path(expr.id, se.qself.as_ref(), &se.path, PathSource::Struct); + self.smart_resolve_path(expr.id, &se.qself, &se.path, PathSource::Struct); visit::walk_expr(self, expr); } @@ -3818,7 +3854,9 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { } } - ExprKind::Loop(ref block, label) => self.resolve_labeled_block(label, expr.id, &block), + ExprKind::Loop(ref block, label, _) => { + self.resolve_labeled_block(label, expr.id, &block) + } ExprKind::While(ref cond, ref block, label) => { self.with_resolved_label(label, expr.id, |this| { @@ -3845,12 +3883,12 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { ExprKind::Field(ref subexpression, _) => { self.resolve_expr(subexpression, Some(expr)); } - ExprKind::MethodCall(ref segment, ref receiver, ref arguments, _) => { + ExprKind::MethodCall(box MethodCall { ref seg, ref receiver, ref args, .. }) => { self.resolve_expr(receiver, Some(expr)); - for argument in arguments { - self.resolve_expr(argument, None); + for arg in args { + self.resolve_expr(arg, None); } - self.visit_path_segment(segment); + self.visit_path_segment(seg); } ExprKind::Call(ref callee, ref arguments) => { @@ -3889,10 +3927,15 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { visit::walk_expr(self, expr); self.diagnostic_metadata.current_type_ascription.pop(); } - // `async |x| ...` gets desugared to `|x| future_from_generator(|| ...)`, so we need to + // `async |x| ...` gets desugared to `|x| async {...}`, so we need to // resolve the arguments within the proper scopes so that usages of them inside the // closure are detected as upvars rather than normal closure arg usages. - ExprKind::Closure(_, _, Async::Yes { .. }, _, ref fn_decl, ref body, _span) => { + ExprKind::Closure(box ast::Closure { + asyncness: Async::Yes { .. }, + ref fn_decl, + ref body, + .. + }) => { self.with_rib(ValueNS, NormalRibKind, |this| { this.with_label_rib(ClosureOrAsyncRibKind, |this| { // Resolve arguments: @@ -3912,7 +3955,10 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { }); } // For closures, ClosureOrAsyncRibKind is added in visit_fn - ExprKind::Closure(ClosureBinder::For { ref generic_params, span }, ..) => { + ExprKind::Closure(box ast::Closure { + binder: ClosureBinder::For { ref generic_params, span }, + .. + }) => { self.with_generic_param_rib( &generic_params, NormalRibKind, @@ -3943,10 +3989,15 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { self.resolve_expr(elem, Some(expr)); self.visit_expr(idx); } - ExprKind::Assign(..) => { - let old = self.diagnostic_metadata.in_assignment.replace(expr); - visit::walk_expr(self, expr); - self.diagnostic_metadata.in_assignment = old; + ExprKind::Assign(ref lhs, ref rhs, _) => { + if !self.diagnostic_metadata.is_assign_rhs { + self.diagnostic_metadata.in_assignment = Some(expr); + } + self.visit_expr(lhs); + self.diagnostic_metadata.is_assign_rhs = true; + self.diagnostic_metadata.in_assignment = None; + self.visit_expr(rhs); + self.diagnostic_metadata.is_assign_rhs = false; } _ => { visit::walk_expr(self, expr); @@ -3964,9 +4015,9 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { let traits = self.traits_in_scope(ident, ValueNS); self.r.trait_map.insert(expr.id, traits); } - ExprKind::MethodCall(ref segment, ..) => { + ExprKind::MethodCall(ref call) => { debug!("(recording candidate traits for expr) recording traits for {}", expr.id); - let traits = self.traits_in_scope(segment.ident, ValueNS); + let traits = self.traits_in_scope(call.seg.ident, ValueNS); self.r.trait_map.insert(expr.id, traits); } _ => { diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs index 850f023b1..df59a350e 100644 --- a/compiler/rustc_resolve/src/late/diagnostics.rs +++ b/compiler/rustc_resolve/src/late/diagnostics.rs @@ -8,7 +8,7 @@ use crate::{PathResult, PathSource, Segment}; use rustc_ast::visit::{FnCtxt, FnKind, LifetimeCtxt}; use rustc_ast::{ self as ast, AssocItemKind, Expr, ExprKind, GenericParam, GenericParamKind, Item, ItemKind, - NodeId, Path, Ty, TyKind, DUMMY_NODE_ID, + MethodCall, NodeId, Path, Ty, TyKind, DUMMY_NODE_ID, }; use rustc_ast_pretty::pprust::path_segment_to_string; use rustc_data_structures::fx::FxHashSet; @@ -21,6 +21,7 @@ use rustc_hir::def::Namespace::{self, *}; use rustc_hir::def::{self, CtorKind, CtorOf, DefKind}; use rustc_hir::def_id::{DefId, CRATE_DEF_ID, LOCAL_CRATE}; use rustc_hir::PrimTy; +use rustc_middle::ty::DefIdTree; use rustc_session::lint; use rustc_session::parse::feature_err; use rustc_session::Session; @@ -33,6 +34,8 @@ use rustc_span::{BytePos, Span}; use std::iter; use std::ops::Deref; +use thin_vec::ThinVec; + type Res = def::Res<ast::NodeId>; /// A field or associated item from self type suggested in case of resolution failure. @@ -78,7 +81,7 @@ fn import_candidate_to_enum_paths(suggestion: &ImportSuggestion) -> (String, Str let path_len = suggestion.path.segments.len(); let enum_path = ast::Path { span: suggestion.path.span, - segments: suggestion.path.segments[0..path_len - 1].to_vec(), + segments: suggestion.path.segments[0..path_len - 1].iter().cloned().collect(), tokens: None, }; let enum_path_string = path_names_to_string(&enum_path); @@ -150,7 +153,7 @@ struct BaseError { #[derive(Debug)] enum TypoCandidate { Typo(TypoSuggestion), - Shadowed(Res), + Shadowed(Res, Option<Span>), None, } @@ -158,7 +161,7 @@ impl TypoCandidate { fn to_opt_suggestion(self) -> Option<TypoSuggestion> { match self { TypoCandidate::Typo(sugg) => Some(sugg), - TypoCandidate::Shadowed(_) | TypoCandidate::None => None, + TypoCandidate::Shadowed(_, _) | TypoCandidate::None => None, } } } @@ -219,26 +222,26 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { let (mod_prefix, mod_str, suggestion) = if path.len() == 1 { debug!(?self.diagnostic_metadata.current_impl_items); debug!(?self.diagnostic_metadata.current_function); - let suggestion = if let Some(items) = self.diagnostic_metadata.current_impl_items + let suggestion = if self.current_trait_ref.is_none() && let Some((fn_kind, _)) = self.diagnostic_metadata.current_function - && self.current_trait_ref.is_none() && let Some(FnCtxt::Assoc(_)) = fn_kind.ctxt() + && let Some(items) = self.diagnostic_metadata.current_impl_items && let Some(item) = items.iter().find(|i| { - if let AssocItemKind::Fn(fn_) = &i.kind - && !fn_.sig.decl.has_self() - && i.ident.name == item_str.name + if let AssocItemKind::Fn(_) = &i.kind && i.ident.name == item_str.name { debug!(?item_str.name); - debug!(?fn_.sig.decl.inputs); return true } false }) + && let AssocItemKind::Fn(fn_) = &item.kind { + debug!(?fn_); + let self_sugg = if fn_.sig.decl.has_self() { "self." } else { "Self::" }; Some(( - item_span, + item_span.shrink_to_lo(), "consider using the associated function", - format!("Self::{}", item.ident) + self_sugg.to_string() )) } else { None @@ -279,6 +282,14 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { "you may want to use a bool value instead", format!("{}", item_typo), )) + // FIXME(vincenzopalazzo): make the check smarter, + // and maybe expand with levenshtein distance checks + } else if item_str.as_str() == "printf" { + Some(( + item_span, + "you may have meant to use the `print` macro", + "print!".to_owned(), + )) } else { suggestion }; @@ -322,7 +333,12 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { } self.suggest_bare_struct_literal(&mut err); - self.suggest_pattern_match_with_let(&mut err, source, span); + + if self.suggest_pattern_match_with_let(&mut err, source, span) { + // Fallback label. + err.span_label(base_error.span, &base_error.fallback_label); + return (err, Vec::new()); + } self.suggest_self_or_self_ref(&mut err, path, span); self.detect_assoct_type_constraint_meant_as_path(&mut err, &base_error); @@ -341,7 +357,11 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { if !self.type_ascription_suggestion(&mut err, base_error.span) { let mut fallback = self.suggest_trait_and_bounds(&mut err, source, res, span, &base_error); + + // if we have suggested using pattern matching, then don't add needless suggestions + // for typos. fallback |= self.suggest_typo(&mut err, source, path, span, &base_error); + if fallback { // Fallback label. err.span_label(base_error.span, &base_error.fallback_label); @@ -387,11 +407,13 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { } fn suggest_self_or_self_ref(&mut self, err: &mut Diagnostic, path: &[Segment], span: Span) { - let is_assoc_fn = self.self_type_is_available(); + if !self.self_type_is_available() { + return; + } let Some(path_last_segment) = path.last() else { return }; let item_str = path_last_segment.ident; // Emit help message for fake-self from other languages (e.g., `this` in Javascript). - if ["this", "my"].contains(&item_str.as_str()) && is_assoc_fn { + if ["this", "my"].contains(&item_str.as_str()) { err.span_suggestion_short( span, "you might have meant to use `self` here instead", @@ -428,7 +450,7 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { fn try_lookup_name_relaxed( &mut self, - err: &mut DiagnosticBuilder<'_, ErrorGuaranteed>, + err: &mut Diagnostic, source: PathSource<'_>, path: &[Segment], span: Span, @@ -442,7 +464,6 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { let is_enum_variant = &|res| matches!(res, Res::Def(DefKind::Variant, _)); let path_str = Segment::names_to_string(path); let ident_span = path.last().map_or(span, |ident| ident.ident.span); - let mut candidates = self .r .lookup_import_candidates(ident, ns, &self.parent_scope, is_expected) @@ -488,7 +509,7 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { .contains(span) { // Already reported this issue on the lhs of the type ascription. - err.delay_as_bug(); + err.downgrade_to_delayed_bug(); return (true, candidates); } } @@ -607,7 +628,7 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { fn suggest_trait_and_bounds( &mut self, - err: &mut DiagnosticBuilder<'_, ErrorGuaranteed>, + err: &mut Diagnostic, source: PathSource<'_>, res: Option<Res>, span: Span, @@ -682,7 +703,7 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { fn suggest_typo( &mut self, - err: &mut DiagnosticBuilder<'_, ErrorGuaranteed>, + err: &mut Diagnostic, source: PathSource<'_>, path: &[Segment], span: Span, @@ -691,9 +712,20 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { let is_expected = &|res| source.is_expected(res); let ident_span = path.last().map_or(span, |ident| ident.ident.span); let typo_sugg = self.lookup_typo_candidate(path, source.namespace(), is_expected); - if let TypoCandidate::Shadowed(res) = typo_sugg - && let Some(id) = res.opt_def_id() - && let Some(sugg_span) = self.r.opt_span(id) + let is_in_same_file = &|sp1, sp2| { + let source_map = self.r.session.source_map(); + let file1 = source_map.span_to_filename(sp1); + let file2 = source_map.span_to_filename(sp2); + file1 == file2 + }; + // print 'you might have meant' if the candidate is (1) is a shadowed name with + // accessible definition and (2) either defined in the same crate as the typo + // (could be in a different file) or introduced in the same file as the typo + // (could belong to a different crate) + if let TypoCandidate::Shadowed(res, Some(sugg_span)) = typo_sugg + && res + .opt_def_id() + .map_or(false, |id| id.is_local() || is_in_same_file(span, sugg_span)) { err.span_label( sugg_span, @@ -730,7 +762,7 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { fn err_code_special_cases( &mut self, - err: &mut DiagnosticBuilder<'_, ErrorGuaranteed>, + err: &mut Diagnostic, source: PathSource<'_>, path: &[Segment], span: Span, @@ -784,19 +816,18 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { return false; } err.code(rustc_errors::error_code!(E0411)); - err.span_label( - span, - "`Self` is only available in impls, traits, and type definitions".to_string(), - ); + err.span_label(span, "`Self` is only available in impls, traits, and type definitions"); if let Some(item_kind) = self.diagnostic_metadata.current_item { - err.span_label( - item_kind.ident.span, - format!( - "`Self` not allowed in {} {}", - item_kind.kind.article(), - item_kind.kind.descr() - ), - ); + if !item_kind.ident.span.is_dummy() { + err.span_label( + item_kind.ident.span, + format!( + "`Self` not allowed in {} {}", + item_kind.kind.article(), + item_kind.kind.descr() + ), + ); + } } true } @@ -929,7 +960,7 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { err: &mut Diagnostic, source: PathSource<'_>, span: Span, - ) { + ) -> bool { if let PathSource::Expr(_) = source && let Some(Expr { span: expr_span, @@ -946,8 +977,10 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { "let ", Applicability::MaybeIncorrect, ); + return true; } } + false } fn get_single_associated_item( @@ -973,10 +1006,7 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { .collect(); if targets.len() == 1 { let target = targets[0]; - return Some(TypoSuggestion::single_item_from_res( - target.0.ident.name, - target.1, - )); + return Some(TypoSuggestion::single_item_from_ident(target.0.ident, target.1)); } } } @@ -1003,11 +1033,7 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { }; // Confirm that the target is an associated type. - let (ty, position, path) = if let ast::TyKind::Path( - Some(ast::QSelf { ty, position, .. }), - path, - ) = &bounded_ty.kind - { + let (ty, position, path) = if let ast::TyKind::Path(Some(qself), path) = &bounded_ty.kind { // use this to verify that ident is a type param. let Some(partial_res) = self.r.partial_res_map.get(&bounded_ty.id) else { return false; @@ -1018,7 +1044,7 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { ) { return false; } - (ty, position, path) + (&qself.ty, qself.position, path) } else { return false; }; @@ -1054,12 +1080,12 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { .source_map() .span_to_snippet(ty.span) // Account for `<&'a T as Foo>::Bar`. .unwrap_or_else(|_| constrain_ident.to_string()), - path.segments[..*position] + path.segments[..position] .iter() .map(|segment| path_segment_to_string(segment)) .collect::<Vec<_>>() .join("::"), - path.segments[*position..] + path.segments[position..] .iter() .map(|segment| path_segment_to_string(segment)) .collect::<Vec<_>>() @@ -1151,7 +1177,9 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { let (lhs_span, rhs_span) = match &expr.kind { ExprKind::Field(base, ident) => (base.span, ident.span), - ExprKind::MethodCall(_, receiver, _, span) => (receiver.span, *span), + ExprKind::MethodCall(box MethodCall { receiver, span, .. }) => { + (receiver.span, *span) + } _ => return false, }; @@ -1423,13 +1451,7 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { err.span_label(span, "constructor is not visible here due to private fields"); } - ( - Res::Def( - DefKind::Union | DefKind::Variant | DefKind::Ctor(_, CtorKind::Fictive), - def_id, - ), - _, - ) if ns == ValueNS => { + (Res::Def(DefKind::Union | DefKind::Variant, def_id), _) if ns == ValueNS => { bad_struct_syntax_suggestion(def_id); } (Res::Def(DefKind::Ctor(_, CtorKind::Const), def_id), _) if ns == ValueNS => { @@ -1449,7 +1471,8 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { _ => return false, } } - (Res::Def(DefKind::Ctor(_, CtorKind::Fn), def_id), _) if ns == ValueNS => { + (Res::Def(DefKind::Ctor(_, CtorKind::Fn), ctor_def_id), _) if ns == ValueNS => { + let def_id = self.r.parent(ctor_def_id); if let Some(span) = self.def_span(def_id) { err.span_label(span, &format!("`{}` defined here", path_str)); } @@ -1526,7 +1549,6 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { _ => None, } } - // Fields are generally expected in the same contexts as locals. if filter_fn(Res::Local(ast::DUMMY_NODE_ID)) { if let Some(node_id) = @@ -1618,7 +1640,7 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { // Locals and type parameters for (ident, &res) in &rib.bindings { if filter_fn(res) && ident.span.ctxt() == rib_ctxt { - names.push(TypoSuggestion::typo_from_res(ident.name, res)); + names.push(TypoSuggestion::typo_from_ident(*ident, res)); } } @@ -1647,9 +1669,7 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { Res::Def(DefKind::Mod, crate_id.as_def_id()); if filter_fn(crate_mod) { - Some(TypoSuggestion::typo_from_res( - ident.name, crate_mod, - )) + Some(TypoSuggestion::typo_from_ident(*ident, crate_mod)) } else { None } @@ -1668,7 +1688,7 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { // Add primitive types to the mix if filter_fn(Res::PrimTy(PrimTy::Bool)) { names.extend(PrimTy::ALL.iter().map(|prim_ty| { - TypoSuggestion::typo_from_res(prim_ty.name(), Res::PrimTy(*prim_ty)) + TypoSuggestion::typo_from_name(prim_ty.name(), Res::PrimTy(*prim_ty)) })) } } else { @@ -1695,7 +1715,7 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { return TypoCandidate::None; }; if found == name { - TypoCandidate::Shadowed(sugg.res) + TypoCandidate::Shadowed(sugg.res, sugg.span) } else { TypoCandidate::Typo(sugg) } @@ -1796,35 +1816,28 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { false } - fn let_binding_suggestion(&self, err: &mut Diagnostic, ident_span: Span) -> bool { - // try to give a suggestion for this pattern: `name = 1`, which is common in other languages - let mut added_suggestion = false; - if let Some(Expr { kind: ExprKind::Assign(lhs, _rhs, _), .. }) = self.diagnostic_metadata.in_assignment && + // try to give a suggestion for this pattern: `name = blah`, which is common in other languages + // suggest `let name = blah` to introduce a new binding + fn let_binding_suggestion(&mut self, err: &mut Diagnostic, ident_span: Span) -> bool { + if let Some(Expr { kind: ExprKind::Assign(lhs, .. ), .. }) = self.diagnostic_metadata.in_assignment && let ast::ExprKind::Path(None, _) = lhs.kind { - let sm = self.r.session.source_map(); - let line_span = sm.span_extend_to_line(ident_span); - let ident_name = sm.span_to_snippet(ident_span).unwrap(); - // HACK(chenyukang): make sure ident_name is at the starting of the line to protect against macros - if sm - .span_to_snippet(line_span) - .map_or(false, |s| s.trim().starts_with(&ident_name)) - { + if !ident_span.from_expansion() { err.span_suggestion_verbose( ident_span.shrink_to_lo(), "you might have meant to introduce a new binding", "let ".to_string(), Applicability::MaybeIncorrect, ); - added_suggestion = true; + return true; } } - added_suggestion + false } fn find_module(&mut self, def_id: DefId) -> Option<(Module<'a>, ImportSuggestion)> { let mut result = None; let mut seen_modules = FxHashSet::default(); - let mut worklist = vec![(self.r.graph_root, Vec::new())]; + let mut worklist = vec![(self.r.graph_root, ThinVec::new())]; while let Some((in_module, path_segments)) = worklist.pop() { // abort if the module is already found @@ -1927,7 +1940,7 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { err.span_suggestions( span, &msg, - suggestable_variants.into_iter(), + suggestable_variants, Applicability::MaybeIncorrect, ); } @@ -1950,11 +1963,12 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { )); } } else { - let needs_placeholder = |def_id: DefId, kind: CtorKind| { + let needs_placeholder = |ctor_def_id: DefId, kind: CtorKind| { + let def_id = self.r.parent(ctor_def_id); let has_no_fields = self.r.field_names.get(&def_id).map_or(false, |f| f.is_empty()); match kind { CtorKind::Const => false, - CtorKind::Fn | CtorKind::Fictive if has_no_fields => false, + CtorKind::Fn if has_no_fields => false, _ => true, } }; @@ -1966,7 +1980,6 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { .map(|(variant, kind)| match kind { CtorKind::Const => variant, CtorKind::Fn => format!("({}())", variant), - CtorKind::Fictive => format!("({} {{}})", variant), }) .collect::<Vec<_>>(); let no_suggestable_variant = suggestable_variants.is_empty(); @@ -1981,7 +1994,7 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { err.span_suggestions( span, msg, - suggestable_variants.into_iter(), + suggestable_variants, Applicability::MaybeIncorrect, ); } @@ -1992,7 +2005,6 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { .map(|(variant, _, kind)| (path_names_to_string(variant), kind)) .filter_map(|(variant, kind)| match kind { CtorKind::Fn => Some(format!("({}(/* fields */))", variant)), - CtorKind::Fictive => Some(format!("({} {{ /* fields */ }})", variant)), _ => None, }) .collect::<Vec<_>>(); @@ -2011,7 +2023,7 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { err.span_suggestions( span, msg, - suggestable_variants_with_placeholders.into_iter(), + suggestable_variants_with_placeholders, Applicability::HasPlaceholders, ); } diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs index 8aebb7da1..a0fa61c45 100644 --- a/compiler/rustc_resolve/src/lib.rs +++ b/compiler/rustc_resolve/src/lib.rs @@ -73,6 +73,7 @@ mod check_unused; mod def_collector; mod diagnostics; mod effective_visibilities; +mod errors; mod ident; mod imports; mod late; @@ -646,7 +647,7 @@ impl<'a> ToNameBinding<'a> for &'a NameBinding<'a> { #[derive(Clone, Debug)] enum NameBindingKind<'a> { - Res(Res, /* is_macro_export */ bool), + Res(Res), Module(Module<'a>), Import { binding: &'a NameBinding<'a>, import: &'a Import<'a>, used: Cell<bool> }, } @@ -745,7 +746,7 @@ impl<'a> NameBinding<'a> { fn res(&self) -> Res { match self.kind { - NameBindingKind::Res(res, _) => res, + NameBindingKind::Res(res) => res, NameBindingKind::Module(module) => module.res().unwrap(), NameBindingKind::Import { binding, .. } => binding.res(), } @@ -762,10 +763,10 @@ impl<'a> NameBinding<'a> { fn is_possibly_imported_variant(&self) -> bool { match self.kind { NameBindingKind::Import { binding, .. } => binding.is_possibly_imported_variant(), - NameBindingKind::Res( - Res::Def(DefKind::Variant | DefKind::Ctor(CtorOf::Variant, ..), _), + NameBindingKind::Res(Res::Def( + DefKind::Variant | DefKind::Ctor(CtorOf::Variant, ..), _, - ) => true, + )) => true, NameBindingKind::Res(..) | NameBindingKind::Module(..) => false, } } @@ -788,6 +789,13 @@ impl<'a> NameBinding<'a> { matches!(self.kind, NameBindingKind::Import { .. }) } + /// The binding introduced by `#[macro_export] macro_rules` is a public import, but it might + /// not be perceived as such by users, so treat it as a non-import in some diagnostics. + fn is_import_user_facing(&self) -> bool { + matches!(self.kind, NameBindingKind::Import { import, .. } + if !matches!(import.kind, ImportKind::MacroExport)) + } + fn is_glob_import(&self) -> bool { match self.kind { NameBindingKind::Import { import, .. } => import.is_glob(), @@ -1030,6 +1038,8 @@ pub struct Resolver<'a> { /// they are declared in the static array generated by proc_macro_harness. proc_macros: Vec<NodeId>, confused_type_with_std_module: FxHashMap<Span, Span>, + /// Whether lifetime elision was successful. + lifetime_elision_allowed: FxHashSet<NodeId>, effective_visibilities: EffectiveVisibilities, } @@ -1101,17 +1111,30 @@ impl<'a> AsMut<Resolver<'a>> for Resolver<'a> { } } -impl<'a, 'b> DefIdTree for &'a Resolver<'b> { +/// A minimal subset of resolver that can implemenent `DefIdTree`, sometimes +/// required to satisfy borrow checker by avoiding borrowing the whole resolver. +#[derive(Clone, Copy)] +struct ResolverTree<'a, 'b>(&'a Definitions, &'a CrateLoader<'b>); + +impl DefIdTree for ResolverTree<'_, '_> { #[inline] fn opt_parent(self, id: DefId) -> Option<DefId> { + let ResolverTree(definitions, crate_loader) = self; match id.as_local() { - Some(id) => self.definitions.def_key(id).parent, - None => self.cstore().def_key(id).parent, + Some(id) => definitions.def_key(id).parent, + None => crate_loader.cstore().def_key(id).parent, } .map(|index| DefId { index, ..id }) } } +impl<'a, 'b> DefIdTree for &'a Resolver<'b> { + #[inline] + fn opt_parent(self, id: DefId) -> Option<DefId> { + ResolverTree(&self.definitions, &self.crate_loader).opt_parent(id) + } +} + impl Resolver<'_> { fn opt_local_def_id(&self, node: NodeId) -> Option<LocalDefId> { self.node_id_to_def_id.get(&node).copied() @@ -1175,7 +1198,7 @@ impl<'a> Resolver<'a> { pub fn new( session: &'a Session, krate: &Crate, - crate_name: &str, + crate_name: Symbol, metadata_loader: Box<MetadataLoaderDyn>, arenas: &'a ResolverArenas<'a>, ) -> Resolver<'a> { @@ -1283,7 +1306,7 @@ impl<'a> Resolver<'a> { arenas, dummy_binding: arenas.alloc_name_binding(NameBinding { - kind: NameBindingKind::Res(Res::Err, false), + kind: NameBindingKind::Res(Res::Err), ambiguity: None, expansion: LocalExpnId::ROOT, span: DUMMY_SP, @@ -1335,6 +1358,7 @@ impl<'a> Resolver<'a> { trait_impls: Default::default(), proc_macros: Default::default(), confused_type_with_std_module: Default::default(), + lifetime_elision_allowed: Default::default(), effective_visibilities: Default::default(), }; @@ -1429,6 +1453,7 @@ impl<'a> Resolver<'a> { def_id_to_node_id: self.def_id_to_node_id, trait_map: self.trait_map, builtin_macro_kinds: self.builtin_macro_kinds, + lifetime_elision_allowed: self.lifetime_elision_allowed, }; ResolverOutputs { definitions, global_ctxt, ast_lowering } } @@ -1472,6 +1497,7 @@ impl<'a> Resolver<'a> { def_id_to_node_id: self.def_id_to_node_id.clone(), trait_map: self.trait_map.clone(), builtin_macro_kinds: self.builtin_macro_kinds.clone(), + lifetime_elision_allowed: self.lifetime_elision_allowed.clone(), }; ResolverOutputs { definitions, global_ctxt, ast_lowering } } @@ -1613,10 +1639,12 @@ impl<'a> Resolver<'a> { ) -> SmallVec<[LocalDefId; 1]> { let mut import_ids = smallvec![]; while let NameBindingKind::Import { import, binding, .. } = kind { - let id = self.local_def_id(import.id); - self.maybe_unused_trait_imports.insert(id); + if let Some(node_id) = import.id() { + let def_id = self.local_def_id(node_id); + self.maybe_unused_trait_imports.insert(def_id); + import_ids.push(def_id); + } self.add_to_glob_map(&import, trait_name); - import_ids.push(id); kind = &binding.kind; } import_ids @@ -1683,7 +1711,9 @@ impl<'a> Resolver<'a> { } used.set(true); import.used.set(true); - self.used_imports.insert(import.id); + if let Some(id) = import.id() { + self.used_imports.insert(id); + } self.add_to_glob_map(&import, ident); self.record_use(ident, binding, false); } @@ -1691,8 +1721,8 @@ impl<'a> Resolver<'a> { #[inline] fn add_to_glob_map(&mut self, import: &Import<'_>, ident: Ident) { - if import.is_glob() { - let def_id = self.local_def_id(import.id); + if let ImportKind::Glob { id, .. } = import.kind { + let def_id = self.local_def_id(id); self.glob_map.entry(def_id).or_default().insert(ident.name); } } @@ -1897,7 +1927,7 @@ impl<'a> Resolver<'a> { if let Some(def_id) = def_id.as_local() { self.reexport_map.get(&def_id).cloned().unwrap_or_default() } else { - self.cstore().module_children_untracked(def_id, self.session) + self.cstore().module_children_untracked(def_id, self.session).collect() } } @@ -1961,7 +1991,7 @@ impl<'a> Resolver<'a> { .find(|a| a.has_name(sym::rustc_legacy_const_generics))?; let mut ret = Vec::new(); for meta in attr.meta_item_list()? { - match meta.literal()?.kind { + match meta.lit()?.kind { LitKind::Int(a, _) => ret.push(a as usize), _ => panic!("invalid arg index"), } @@ -1999,11 +2029,7 @@ impl<'a> Resolver<'a> { // Items that go to reexport table encoded to metadata and visible through it to other crates. fn is_reexport(&self, binding: &NameBinding<'a>) -> Option<def::Res<!>> { - // FIXME: Consider changing the binding inserted by `#[macro_export] macro_rules` - // into the crate root to actual `NameBindingKind::Import`. - if binding.is_import() - || matches!(binding.kind, NameBindingKind::Res(_, _is_macro_export @ true)) - { + if binding.is_import() { let res = binding.res().expect_non_local(); // Ambiguous imports are treated as errors at this point and are // not exposed to other crates (see #36837 for more details). diff --git a/compiler/rustc_resolve/src/macros.rs b/compiler/rustc_resolve/src/macros.rs index 9526296f9..8c7972f8e 100644 --- a/compiler/rustc_resolve/src/macros.rs +++ b/compiler/rustc_resolve/src/macros.rs @@ -356,7 +356,7 @@ impl<'a> ResolverExpand for Resolver<'a> { has_derive_copy: false, }); let parent_scope = self.invocation_parent_scopes[&expn_id]; - for (i, (path, _, opt_ext)) in entry.resolutions.iter_mut().enumerate() { + for (i, (path, _, opt_ext, _)) in entry.resolutions.iter_mut().enumerate() { if opt_ext.is_none() { *opt_ext = Some( match self.resolve_macro_path( |