diff options
Diffstat (limited to '')
-rw-r--r-- | compiler/rustc_hir_analysis/src/collect/lifetimes.rs (renamed from compiler/rustc_resolve/src/late/lifetimes.rs) | 139 | ||||
-rw-r--r-- | compiler/rustc_resolve/Cargo.toml | 1 | ||||
-rw-r--r-- | compiler/rustc_resolve/src/access_levels.rs | 185 | ||||
-rw-r--r-- | compiler/rustc_resolve/src/build_reduced_graph.rs | 7 | ||||
-rw-r--r-- | compiler/rustc_resolve/src/check_unused.rs | 2 | ||||
-rw-r--r-- | compiler/rustc_resolve/src/def_collector.rs | 27 | ||||
-rw-r--r-- | compiler/rustc_resolve/src/diagnostics.rs | 139 | ||||
-rw-r--r-- | compiler/rustc_resolve/src/effective_visibilities.rs | 188 | ||||
-rw-r--r-- | compiler/rustc_resolve/src/ident.rs | 22 | ||||
-rw-r--r-- | compiler/rustc_resolve/src/imports.rs | 32 | ||||
-rw-r--r-- | compiler/rustc_resolve/src/late.rs | 366 | ||||
-rw-r--r-- | compiler/rustc_resolve/src/late/diagnostics.rs | 859 | ||||
-rw-r--r-- | compiler/rustc_resolve/src/lib.rs | 82 | ||||
-rw-r--r-- | compiler/rustc_resolve/src/macros.rs | 25 |
14 files changed, 1206 insertions, 868 deletions
diff --git a/compiler/rustc_resolve/src/late/lifetimes.rs b/compiler/rustc_hir_analysis/src/collect/lifetimes.rs index 4d9704617..3f263a6de 100644 --- a/compiler/rustc_resolve/src/late/lifetimes.rs +++ b/compiler/rustc_hir_analysis/src/collect/lifetimes.rs @@ -32,8 +32,6 @@ trait RegionExt { fn id(&self) -> Option<DefId>; fn shifted(self, amount: u32) -> Region; - - fn shifted_out_to_binder(self, binder: ty::DebruijnIndex) -> Region; } impl RegionExt for Region { @@ -69,15 +67,6 @@ impl RegionExt for Region { _ => self, } } - - fn shifted_out_to_binder(self, binder: ty::DebruijnIndex) -> Region { - match self { - Region::LateBound(debruijn, index, id) => { - Region::LateBound(debruijn.shifted_out_to_binder(binder), index, id) - } - _ => self, - } - } } /// Maps the id of each lifetime reference to the lifetime decl @@ -101,8 +90,8 @@ struct NamedRegionMap { late_bound_vars: HirIdMap<Vec<ty::BoundVariableKind>>, } -pub(crate) struct LifetimeContext<'a, 'tcx> { - pub(crate) tcx: TyCtxt<'tcx>, +struct LifetimeContext<'a, 'tcx> { + tcx: TyCtxt<'tcx>, map: &'a mut NamedRegionMap, scope: ScopeRef<'a>, @@ -234,7 +223,7 @@ type ScopeRef<'a> = &'a Scope<'a>; const ROOT_SCOPE: ScopeRef<'static> = &Scope::Root; -pub fn provide(providers: &mut ty::query::Providers) { +pub(crate) fn provide(providers: &mut ty::query::Providers) { *providers = ty::query::Providers { resolve_lifetimes_trait_definition, resolve_lifetimes, @@ -326,6 +315,7 @@ fn convert_named_region_map(named_region_map: NamedRegionMap) -> ResolveLifetime } debug!(?rl.defs); + debug!(?rl.late_bound_vars); rl } @@ -339,24 +329,25 @@ fn convert_named_region_map(named_region_map: NamedRegionMap) -> ResolveLifetime /// This allows us to avoid cycles. Importantly, if we ask for lifetimes for lifetimes that have an owner /// other than the trait itself (like the trait methods or associated types), then we just use the regular /// `resolve_lifetimes`. -fn resolve_lifetimes_for<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> &'tcx ResolveLifetimes { - let item_id = item_for(tcx, def_id); - if item_id == def_id { - let item = tcx.hir().item(hir::ItemId { def_id: item_id }); +fn resolve_lifetimes_for<'tcx>(tcx: TyCtxt<'tcx>, def_id: hir::OwnerId) -> &'tcx ResolveLifetimes { + let item_id = item_for(tcx, def_id.def_id); + let local_def_id = item_id.owner_id.def_id; + if item_id.owner_id == def_id { + let item = tcx.hir().item(item_id); match item.kind { - hir::ItemKind::Trait(..) => tcx.resolve_lifetimes_trait_definition(item_id), - _ => tcx.resolve_lifetimes(item_id), + hir::ItemKind::Trait(..) => tcx.resolve_lifetimes_trait_definition(local_def_id), + _ => tcx.resolve_lifetimes(local_def_id), } } else { - tcx.resolve_lifetimes(item_id) + tcx.resolve_lifetimes(local_def_id) } } /// Finds the `Item` that contains the given `LocalDefId` -fn item_for(tcx: TyCtxt<'_>, local_def_id: LocalDefId) -> LocalDefId { +fn item_for(tcx: TyCtxt<'_>, local_def_id: LocalDefId) -> hir::ItemId { match tcx.hir().find_by_def_id(local_def_id) { Some(Node::Item(item)) => { - return item.def_id; + return item.item_id(); } _ => {} } @@ -366,7 +357,7 @@ fn item_for(tcx: TyCtxt<'_>, local_def_id: LocalDefId) -> LocalDefId { loop { let node = parent_iter.next().map(|n| n.1); match node { - Some(hir::Node::Item(item)) => break item.def_id, + Some(hir::Node::Item(item)) => break item.item_id(), Some(hir::Node::Crate(_)) | None => bug!("Called `item_for` on an Item."), _ => {} } @@ -506,7 +497,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { }) .unzip(); - self.map.late_bound_vars.insert(e.hir_id, binders); + self.record_late_bound_vars(e.hir_id, binders); let scope = Scope::Binder { hir_id: e.hir_id, lifetimes, @@ -530,7 +521,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { match &item.kind { hir::ItemKind::Impl(hir::Impl { of_trait, .. }) => { if let Some(of_trait) = of_trait { - self.map.late_bound_vars.insert(of_trait.hir_ref_id, Vec::default()); + self.record_late_bound_vars(of_trait.hir_ref_id, Vec::default()); } } _ => {} @@ -566,13 +557,12 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { // their owner, we can keep going until we find the Item that owns that. We then // conservatively add all resolved lifetimes. Otherwise we run into problems in // cases like `type Foo<'a> = impl Bar<As = impl Baz + 'a>`. - for (_hir_id, node) in - self.tcx.hir().parent_iter(self.tcx.hir().local_def_id_to_hir_id(item.def_id)) - { + for (_hir_id, node) in self.tcx.hir().parent_iter(item.owner_id.into()) { match node { hir::Node::Item(parent_item) => { - let resolved_lifetimes: &ResolveLifetimes = - self.tcx.resolve_lifetimes(item_for(self.tcx, parent_item.def_id)); + let resolved_lifetimes: &ResolveLifetimes = self.tcx.resolve_lifetimes( + item_for(self.tcx, parent_item.owner_id.def_id).owner_id.def_id, + ); // We need to add *all* deps, since opaque tys may want them from *us* for (&owner, defs) in resolved_lifetimes.defs.iter() { defs.iter().for_each(|(&local_id, region)| { @@ -583,7 +573,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { resolved_lifetimes.late_bound_vars.iter() { late_bound_vars.iter().for_each(|(&local_id, late_bound_vars)| { - self.map.late_bound_vars.insert( + self.record_late_bound_vars( hir::HirId { owner, local_id }, late_bound_vars.clone(), ); @@ -614,7 +604,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { GenericParamKind::Type { .. } | GenericParamKind::Const { .. } => None, }) .collect(); - self.map.late_bound_vars.insert(item.hir_id(), vec![]); + self.record_late_bound_vars(item.hir_id(), vec![]); let scope = Scope::Binder { hir_id: item.hir_id(), lifetimes, @@ -663,7 +653,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { (pair, r) }) .unzip(); - self.map.late_bound_vars.insert(ty.hir_id, binders); + self.record_late_bound_vars(ty.hir_id, binders); let scope = Scope::Binder { hir_id: ty.hir_id, lifetimes, @@ -817,7 +807,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { GenericParamKind::Type { .. } | GenericParamKind::Const { .. } => {} } } - self.map.late_bound_vars.insert(ty.hir_id, vec![]); + self.record_late_bound_vars(ty.hir_id, vec![]); let scope = Scope::Binder { hir_id: ty.hir_id, @@ -861,7 +851,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { GenericParamKind::Type { .. } | GenericParamKind::Const { .. } => None, }) .collect(); - self.map.late_bound_vars.insert(trait_item.hir_id(), vec![]); + self.record_late_bound_vars(trait_item.hir_id(), vec![]); let scope = Scope::Binder { hir_id: trait_item.hir_id(), lifetimes, @@ -897,7 +887,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { Fn(..) => self.visit_early_late(impl_item.hir_id(), &impl_item.generics, |this| { intravisit::walk_impl_item(this, impl_item) }), - TyAlias(ref ty) => { + Type(ref ty) => { let generics = &impl_item.generics; let lifetimes: FxIndexMap<LocalDefId, Region> = generics .params @@ -909,9 +899,9 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { GenericParamKind::Const { .. } | GenericParamKind::Type { .. } => None, }) .collect(); - self.map.late_bound_vars.insert(ty.hir_id, vec![]); + self.record_late_bound_vars(impl_item.hir_id(), vec![]); let scope = Scope::Binder { - hir_id: ty.hir_id, + hir_id: impl_item.hir_id(), lifetimes, s: self.scope, scope_type: BinderScopeType::Normal, @@ -995,13 +985,14 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { for predicate in generics.predicates { match predicate { &hir::WherePredicate::BoundPredicate(hir::WhereBoundPredicate { + hir_id, ref bounded_ty, bounds, ref bound_generic_params, origin, .. }) => { - let (lifetimes, binders): (FxIndexMap<LocalDefId, Region>, Vec<_>) = + let lifetimes: FxIndexMap<LocalDefId, Region> = bound_generic_params .iter() .filter(|param| { @@ -1009,19 +1000,23 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { }) .enumerate() .map(|(late_bound_idx, param)| { - let pair = - Region::late(late_bound_idx as u32, this.tcx.hir(), param); - let r = late_region_as_bound_region(this.tcx, &pair.1); - (pair, r) + Region::late(late_bound_idx as u32, this.tcx.hir(), param) + }) + .collect(); + let binders: Vec<_> = + lifetimes + .iter() + .map(|(_, region)| { + late_region_as_bound_region(this.tcx, region) }) - .unzip(); - this.map.late_bound_vars.insert(bounded_ty.hir_id, binders.clone()); + .collect(); + this.record_late_bound_vars(hir_id, binders.clone()); // Even if there are no lifetimes defined here, we still wrap it in a binder // scope. If there happens to be a nested poly trait ref (an error), that // will be `Concatenating` anyways, so we don't have to worry about the depth // being wrong. let scope = Scope::Binder { - hir_id: bounded_ty.hir_id, + hir_id, lifetimes, s: this.scope, scope_type: BinderScopeType::Normal, @@ -1089,7 +1084,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { // imagine there's a better way to go about this. let (binders, scope_type) = self.poly_trait_ref_binder_info(); - self.map.late_bound_vars.insert(*hir_id, binders); + self.record_late_bound_vars(*hir_id, binders); let scope = Scope::Binder { hir_id: *hir_id, lifetimes: FxIndexMap::default(), @@ -1127,7 +1122,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { binders.extend(binders_iter); debug!(?binders); - self.map.late_bound_vars.insert(trait_ref.trait_ref.hir_ref_id, binders); + self.record_late_bound_vars(trait_ref.trait_ref.hir_ref_id, binders); // Always introduce a scope here, even if this is in a where clause and // we introduced the binders around the bounded Ty. In that case, we @@ -1211,6 +1206,15 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { } } + fn record_late_bound_vars(&mut self, hir_id: hir::HirId, binder: Vec<ty::BoundVariableKind>) { + if let Some(old) = self.map.late_bound_vars.insert(hir_id, binder) { + bug!( + "overwrote bound vars for {hir_id:?}:\nold={old:?}\nnew={:?}", + self.map.late_bound_vars[&hir_id] + ) + } + } + /// Visits self by adding a scope and handling recursive walk over the contents with `walk`. /// /// Handles visiting fns and methods. These are a bit complicated because we must distinguish @@ -1268,7 +1272,7 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { late_region_as_bound_region(self.tcx, &pair.1) }) .collect(); - self.map.late_bound_vars.insert(hir_id, binders); + self.record_late_bound_vars(hir_id, binders); let scope = Scope::Binder { hir_id, lifetimes, @@ -1315,15 +1319,44 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { // regular fns. if let Some(hir::PredicateOrigin::ImplTrait) = where_bound_origin && let hir::LifetimeName::Param(_, hir::ParamName::Fresh) = lifetime_ref.name - && let hir::IsAsync::NotAsync = self.tcx.asyncness(lifetime_ref.hir_id.owner) + && let hir::IsAsync::NotAsync = self.tcx.asyncness(lifetime_ref.hir_id.owner.def_id) && !self.tcx.features().anonymous_lifetime_in_impl_trait { - rustc_session::parse::feature_err( + let mut diag = rustc_session::parse::feature_err( &self.tcx.sess.parse_sess, sym::anonymous_lifetime_in_impl_trait, lifetime_ref.span, "anonymous lifetimes in `impl Trait` are unstable", - ).emit(); + ); + + match self.tcx.hir().get_generics(lifetime_ref.hir_id.owner.def_id) { + Some(generics) => { + + let new_param_sugg_tuple; + + new_param_sugg_tuple = match generics.span_for_param_suggestion() { + Some(_) => { + Some((self.tcx.sess.source_map().span_through_char(generics.span, '<').shrink_to_hi(), "'a, ".to_owned())) + }, + None => Some((generics.span, "<'a>".to_owned())) + }; + + let mut multi_sugg_vec = vec![(lifetime_ref.span.shrink_to_hi(), "'a ".to_owned())]; + + if let Some(new_tuple) = new_param_sugg_tuple{ + multi_sugg_vec.push(new_tuple); + } + + diag.span_label(lifetime_ref.span, "expected named lifetime parameter"); + diag.multipart_suggestion("consider introducing a named lifetime parameter", + multi_sugg_vec, + rustc_errors::Applicability::MaybeIncorrect); + + }, + None => { } + } + + diag.emit(); return; } scope = s; diff --git a/compiler/rustc_resolve/Cargo.toml b/compiler/rustc_resolve/Cargo.toml index 5d2b606b4..d66db1d7a 100644 --- a/compiler/rustc_resolve/Cargo.toml +++ b/compiler/rustc_resolve/Cargo.toml @@ -4,7 +4,6 @@ version = "0.0.0" edition = "2021" [lib] -doctest = false [dependencies] bitflags = "1.2.1" diff --git a/compiler/rustc_resolve/src/access_levels.rs b/compiler/rustc_resolve/src/access_levels.rs deleted file mode 100644 index 0a3add2e0..000000000 --- a/compiler/rustc_resolve/src/access_levels.rs +++ /dev/null @@ -1,185 +0,0 @@ -use crate::imports::ImportKind; -use crate::NameBinding; -use crate::NameBindingKind; -use crate::Resolver; -use rustc_ast::ast; -use rustc_ast::visit; -use rustc_ast::visit::Visitor; -use rustc_ast::Crate; -use rustc_ast::EnumDef; -use rustc_ast::NodeId; -use rustc_hir::def_id::LocalDefId; -use rustc_hir::def_id::CRATE_DEF_ID; -use rustc_middle::middle::privacy::AccessLevel; -use rustc_middle::ty::DefIdTree; -use rustc_span::sym; - -pub struct AccessLevelsVisitor<'r, 'a> { - r: &'r mut Resolver<'a>, - changed: bool, -} - -impl<'r, 'a> AccessLevelsVisitor<'r, 'a> { - /// Fills the `Resolver::access_levels` 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_access_levels<'c>(r: &'r mut Resolver<'a>, krate: &'c Crate) { - let mut visitor = AccessLevelsVisitor { r, changed: false }; - - visitor.set_access_level_def_id(CRATE_DEF_ID, Some(AccessLevel::Public)); - visitor.set_bindings_access_level(CRATE_DEF_ID); - - while visitor.changed { - visitor.reset(); - visit::walk_crate(&mut visitor, krate); - } - - info!("resolve::access_levels: {:#?}", r.access_levels); - } - - fn reset(&mut self) { - self.changed = false; - } - - /// Update the access level of the bindings in the given module accordingly. The module access - /// level has to be Exported or Public. - /// This will also follow `use` chains (see PrivacyVisitor::set_import_binding_access_level). - fn set_bindings_access_level(&mut self, module_id: LocalDefId) { - assert!(self.r.module_map.contains_key(&&module_id.to_def_id())); - let module_level = self.r.access_levels.map.get(&module_id).copied(); - if !module_level.is_some() { - return; - } - // Set the given binding access level to `AccessLevel::Public` and - // sets the rest of the `use` chain to `AccessLevel::Exported` until - // we hit the actual exported item. - let set_import_binding_access_level = - |this: &mut Self, mut binding: &NameBinding<'a>, mut access_level| { - while let NameBindingKind::Import { binding: nested_binding, import, .. } = - binding.kind - { - this.set_access_level(import.id, access_level); - if let ImportKind::Single { additional_ids, .. } = import.kind { - this.set_access_level(additional_ids.0, access_level); - this.set_access_level(additional_ids.1, access_level); - } - - access_level = Some(AccessLevel::Exported); - binding = nested_binding; - } - }; - - let module = self.r.get_module(module_id.to_def_id()).unwrap(); - let resolutions = self.r.resolutions(module); - - for (.., name_resolution) in resolutions.borrow().iter() { - if let Some(binding) = name_resolution.borrow().binding() && binding.vis.is_public() && !binding.is_ambiguity() { - let access_level = match binding.is_import() { - true => { - set_import_binding_access_level(self, binding, module_level); - Some(AccessLevel::Exported) - }, - false => module_level, - }; - if let Some(def_id) = binding.res().opt_def_id().and_then(|id| id.as_local()) { - self.set_access_level_def_id(def_id, access_level); - } - } - } - } - - /// Sets the access level of the `LocalDefId` corresponding to the given `NodeId`. - /// This function will panic if the `NodeId` does not have a `LocalDefId` - fn set_access_level( - &mut self, - node_id: NodeId, - access_level: Option<AccessLevel>, - ) -> Option<AccessLevel> { - self.set_access_level_def_id(self.r.local_def_id(node_id), access_level) - } - - fn set_access_level_def_id( - &mut self, - def_id: LocalDefId, - access_level: Option<AccessLevel>, - ) -> Option<AccessLevel> { - let old_level = self.r.access_levels.map.get(&def_id).copied(); - if old_level < access_level { - self.r.access_levels.map.insert(def_id, access_level.unwrap()); - self.changed = true; - access_level - } else { - old_level - } - } -} - -impl<'r, 'ast> Visitor<'ast> for AccessLevelsVisitor<'ast, 'r> { - fn visit_item(&mut self, item: &'ast ast::Item) { - let def_id = self.r.local_def_id(item.id); - // Set access level of nested items. - // If it's a mod, also make the visitor walk all of its items - match item.kind { - // Resolved in rustc_privacy when types are available - ast::ItemKind::Impl(..) => return, - - // Should be unreachable at this stage - ast::ItemKind::MacCall(..) => panic!( - "ast::ItemKind::MacCall encountered, this should not anymore appear at this stage" - ), - - // Foreign modules inherit level from parents. - ast::ItemKind::ForeignMod(..) => { - let parent_level = - self.r.access_levels.map.get(&self.r.local_parent(def_id)).copied(); - self.set_access_level(item.id, parent_level); - } - - // Only exported `macro_rules!` items are public, but they always are - ast::ItemKind::MacroDef(ref macro_def) if macro_def.macro_rules => { - if item.attrs.iter().any(|attr| attr.has_name(sym::macro_export)) { - self.set_access_level(item.id, Some(AccessLevel::Public)); - } - } - - ast::ItemKind::Mod(..) => { - self.set_bindings_access_level(def_id); - visit::walk_item(self, item); - } - - ast::ItemKind::Enum(EnumDef { ref variants }, _) => { - self.set_bindings_access_level(def_id); - for variant in variants { - let variant_def_id = self.r.local_def_id(variant.id); - let variant_level = self.r.access_levels.map.get(&variant_def_id).copied(); - for field in variant.data.fields() { - self.set_access_level(field.id, variant_level); - } - } - } - - ast::ItemKind::Struct(ref def, _) | ast::ItemKind::Union(ref def, _) => { - let inherited_level = self.r.access_levels.map.get(&def_id).copied(); - for field in def.fields() { - if field.vis.kind.is_pub() { - self.set_access_level(field.id, inherited_level); - } - } - } - - ast::ItemKind::Trait(..) => { - self.set_bindings_access_level(def_id); - } - - ast::ItemKind::ExternCrate(..) - | ast::ItemKind::Use(..) - | ast::ItemKind::Static(..) - | ast::ItemKind::Const(..) - | ast::ItemKind::GlobalAsm(..) - | ast::ItemKind::TyAlias(..) - | ast::ItemKind::TraitAlias(..) - | ast::ItemKind::MacroDef(..) - | ast::ItemKind::Fn(..) => return, - } - } -} diff --git a/compiler/rustc_resolve/src/build_reduced_graph.rs b/compiler/rustc_resolve/src/build_reduced_graph.rs index 81b67b758..a17793ecd 100644 --- a/compiler/rustc_resolve/src/build_reduced_graph.rs +++ b/compiler/rustc_resolve/src/build_reduced_graph.rs @@ -326,7 +326,7 @@ impl<'a, 'b> BuildReducedGraphVisitor<'a, 'b> { } PathResult::Module(..) => Err(VisResolutionError::ModuleOnly(path.span)), PathResult::NonModule(partial_res) => { - expected_found_error(partial_res.base_res()) + expected_found_error(partial_res.expect_full_res()) } PathResult::Failed { span, label, suggestion, .. } => { Err(VisResolutionError::FailedToResolve(span, label, suggestion)) @@ -1010,7 +1010,8 @@ impl<'a, 'b> BuildReducedGraphVisitor<'a, 'b> { _, ) | Res::Local(..) - | Res::SelfTy { .. } + | Res::SelfTyParam { .. } + | Res::SelfTyAlias { .. } | Res::SelfCtor(..) | Res::Err => bug!("unexpected resolution: {:?}", res), } @@ -1424,7 +1425,7 @@ impl<'a, 'b> Visitor<'b> for BuildReducedGraphVisitor<'a, 'b> { } (DefKind::AssocFn, ValueNS) } - AssocItemKind::TyAlias(..) => (DefKind::AssocTy, TypeNS), + AssocItemKind::Type(..) => (DefKind::AssocTy, TypeNS), AssocItemKind::MacCall(_) => bug!(), // handled above }; diff --git a/compiler/rustc_resolve/src/check_unused.rs b/compiler/rustc_resolve/src/check_unused.rs index 8b58b32b6..01c3801f2 100644 --- a/compiler/rustc_resolve/src/check_unused.rs +++ b/compiler/rustc_resolve/src/check_unused.rs @@ -6,7 +6,7 @@ // `use` items. // // Unused trait imports can't be checked until the method resolution. We save -// candidates here, and do the actual check in librustc_typeck/check_unused.rs. +// candidates here, and do the actual check in rustc_hir_analysis/check_unused.rs. // // Checking for unused imports is split into three steps: // diff --git a/compiler/rustc_resolve/src/def_collector.rs b/compiler/rustc_resolve/src/def_collector.rs index 3f88f44ff..d36e0f61d 100644 --- a/compiler/rustc_resolve/src/def_collector.rs +++ b/compiler/rustc_resolve/src/def_collector.rs @@ -1,6 +1,5 @@ use crate::{ImplTraitContext, Resolver}; use rustc_ast::visit::{self, FnKind}; -use rustc_ast::walk_list; use rustc_ast::*; use rustc_expand::expand::AstFragment; use rustc_hir::def_id::LocalDefId; @@ -148,8 +147,13 @@ impl<'a, 'b> visit::Visitor<'a> for DefCollector<'a, 'b> { self.with_parent(return_impl_trait_id, |this| { this.visit_fn_ret_ty(&sig.decl.output) }); - let closure_def = self.create_def(closure_id, DefPathData::ClosureExpr, span); - self.with_parent(closure_def, |this| walk_list!(this, visit_block, body)); + // 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. + if let Some(body) = body { + let closure_def = self.create_def(closure_id, DefPathData::ClosureExpr, span); + self.with_parent(closure_def, |this| this.visit_block(body)); + } return; } } @@ -235,7 +239,7 @@ impl<'a, 'b> visit::Visitor<'a> for DefCollector<'a, 'b> { fn visit_assoc_item(&mut self, i: &'a AssocItem, ctxt: visit::AssocCtxt) { let def_data = match &i.kind { AssocItemKind::Fn(..) | AssocItemKind::Const(..) => DefPathData::ValueNs(i.ident.name), - AssocItemKind::TyAlias(..) => DefPathData::TypeNs(i.ident.name), + AssocItemKind::Type(..) => DefPathData::TypeNs(i.ident.name), AssocItemKind::MacCall(..) => return self.visit_macro_invoc(i.id), }; @@ -281,21 +285,6 @@ impl<'a, 'b> visit::Visitor<'a> for DefCollector<'a, 'b> { fn visit_ty(&mut self, ty: &'a Ty) { match ty.kind { TyKind::MacCall(..) => self.visit_macro_invoc(ty.id), - TyKind::ImplTrait(node_id, _) => { - let parent_def = match self.impl_trait_context { - ImplTraitContext::Universal(item_def) => self.resolver.create_def( - item_def, - node_id, - DefPathData::ImplTrait, - self.expansion.to_expn_id(), - ty.span, - ), - ImplTraitContext::Existential => { - self.create_def(node_id, DefPathData::ImplTrait, ty.span) - } - }; - self.with_parent(parent_def, |this| visit::walk_ty(this, ty)) - } _ => visit::walk_ty(self, ty), } } diff --git a/compiler/rustc_resolve/src/diagnostics.rs b/compiler/rustc_resolve/src/diagnostics.rs index ab71fa0bc..5d868ebec 100644 --- a/compiler/rustc_resolve/src/diagnostics.rs +++ b/compiler/rustc_resolve/src/diagnostics.rs @@ -24,7 +24,7 @@ use rustc_span::hygiene::MacroKind; 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}; +use rustc_span::{BytePos, Span, SyntaxContext}; use crate::imports::{Import, ImportKind, ImportResolver}; use crate::late::{PatternSource, Rib}; @@ -47,6 +47,7 @@ pub(crate) type Suggestion = (Vec<(Span, String)>, String, Applicability); /// similarly named label and whether or not it is reachable. pub(crate) type LabelSuggestion = (Ident, bool); +#[derive(Debug)] pub(crate) enum SuggestionTarget { /// The target has a similar name as the name used by the programmer (probably a typo) SimilarlyNamed, @@ -54,6 +55,7 @@ pub(crate) enum SuggestionTarget { SingleItem, } +#[derive(Debug)] pub(crate) struct TypoSuggestion { pub candidate: Symbol, pub res: Res, @@ -70,6 +72,7 @@ impl TypoSuggestion { } /// A free importable items suggested in case of resolution failure. +#[derive(Debug, Clone)] pub(crate) struct ImportSuggestion { pub did: Option<DefId>, pub descr: &'static str, @@ -120,7 +123,7 @@ impl<'a> Resolver<'a> { } fn report_with_use_injections(&mut self, krate: &Crate) { - for UseError { mut err, candidates, def_id, instead, suggestion, path } in + for UseError { mut err, candidates, def_id, instead, suggestion, path, is_call } in self.use_injections.drain(..) { let (span, found_use) = if let Some(def_id) = def_id.as_local() { @@ -128,6 +131,7 @@ impl<'a> Resolver<'a> { } else { (None, FoundUse::No) }; + if !candidates.is_empty() { show_candidates( &self.session, @@ -137,13 +141,18 @@ impl<'a> Resolver<'a> { &candidates, if instead { Instead::Yes } else { Instead::No }, found_use, - IsPattern::No, + DiagnosticMode::Normal, path, ); + err.emit(); } else if let Some((span, msg, sugg, appl)) = suggestion { err.span_suggestion(span, msg, sugg, appl); + err.emit(); + } else if let [segment] = path.as_slice() && is_call { + err.stash(segment.ident.span, rustc_errors::StashKey::CallIntoMethod); + } else { + err.emit(); } - err.emit(); } } @@ -475,11 +484,12 @@ impl<'a> Resolver<'a> { module: Module<'a>, names: &mut Vec<TypoSuggestion>, filter_fn: &impl Fn(Res) -> bool, + ctxt: Option<SyntaxContext>, ) { for (key, resolution) in self.resolutions(module).borrow().iter() { if let Some(binding) = resolution.borrow().binding { let res = binding.res(); - if filter_fn(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)); } } @@ -511,24 +521,18 @@ impl<'a> Resolver<'a> { let sm = self.session.source_map(); let def_id = match outer_res { - Res::SelfTy { trait_: maybe_trait_defid, alias_to: maybe_impl_defid } => { - if let Some(impl_span) = - maybe_impl_defid.and_then(|(def_id, _)| self.opt_span(def_id)) - { + Res::SelfTyParam { .. } => { + err.span_label(span, "can't use `Self` here"); + return err; + } + Res::SelfTyAlias { alias_to: def_id, .. } => { + if let Some(impl_span) = self.opt_span(def_id) { err.span_label( reduce_impl_span_to_impl_keyword(sm, impl_span), "`Self` type implicitly declared here, by this `impl`", ); } - match (maybe_trait_defid, maybe_impl_defid) { - (Some(_), None) => { - err.span_label(span, "can't use `Self` here"); - } - (_, Some(_)) => { - err.span_label(span, "use a type here instead"); - } - (None, None) => bug!("`impl` without trait nor type?"), - } + err.span_label(span, "use a type here instead"); return err; } Res::Def(DefKind::TyParam, def_id) => { @@ -545,8 +549,9 @@ impl<'a> Resolver<'a> { } _ => { bug!( - "GenericParamsFromOuterFunction should only be used with Res::SelfTy, \ - DefKind::TyParam or DefKind::ConstParam" + "GenericParamsFromOuterFunction should only be used with \ + Res::SelfTyParam, Res::SelfTyAlias, DefKind::TyParam or \ + DefKind::ConstParam" ); } }; @@ -696,7 +701,7 @@ impl<'a> Resolver<'a> { &import_suggestions, Instead::No, FoundUse::Yes, - IsPattern::Yes, + DiagnosticMode::Pattern, vec![], ); } @@ -1046,6 +1051,19 @@ 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"); @@ -1166,10 +1184,10 @@ impl<'a> Resolver<'a> { Scope::CrateRoot => { let root_ident = Ident::new(kw::PathRoot, ident.span); let root_module = this.resolve_crate_root(root_ident); - this.add_module_candidates(root_module, &mut suggestions, filter_fn); + this.add_module_candidates(root_module, &mut suggestions, filter_fn, None); } Scope::Module(module, _) => { - this.add_module_candidates(module, &mut suggestions, filter_fn); + this.add_module_candidates(module, &mut suggestions, filter_fn, None); } Scope::MacroUsePrelude => { suggestions.extend(this.macro_use_prelude.iter().filter_map( @@ -1206,7 +1224,7 @@ impl<'a> Resolver<'a> { Scope::StdLibPrelude => { if let Some(prelude) = this.prelude { let mut tmp_suggestions = Vec::new(); - this.add_module_candidates(prelude, &mut tmp_suggestions, filter_fn); + this.add_module_candidates(prelude, &mut tmp_suggestions, filter_fn, None); suggestions.extend( tmp_suggestions .into_iter() @@ -1479,7 +1497,7 @@ impl<'a> Resolver<'a> { &import_suggestions, Instead::No, FoundUse::Yes, - IsPattern::No, + DiagnosticMode::Normal, vec![], ); @@ -2440,12 +2458,34 @@ enum FoundUse { No, } -/// Whether a binding is part of a pattern or an expression. Used for diagnostics. -enum IsPattern { +/// Whether a binding is part of a pattern or a use statement. Used for diagnostics. +enum DiagnosticMode { + Normal, /// The binding is part of a pattern - Yes, - /// The binding is part of an expression - No, + Pattern, + /// The binding is part of a use statement + Import, +} + +pub(crate) fn import_candidates( + session: &Session, + source_span: &IndexVec<LocalDefId, Span>, + err: &mut Diagnostic, + // This is `None` if all placement locations are inside expansions + use_placement_span: Option<Span>, + candidates: &[ImportSuggestion], +) { + show_candidates( + session, + source_span, + err, + use_placement_span, + candidates, + Instead::Yes, + FoundUse::Yes, + DiagnosticMode::Import, + vec![], + ); } /// When an entity with a given name is not available in scope, we search for @@ -2460,7 +2500,7 @@ fn show_candidates( candidates: &[ImportSuggestion], instead: Instead, found_use: FoundUse, - is_pattern: IsPattern, + mode: DiagnosticMode, path: Vec<Segment>, ) { if candidates.is_empty() { @@ -2495,7 +2535,7 @@ fn show_candidates( }; let instead = if let Instead::Yes = instead { " instead" } else { "" }; - let mut msg = if let IsPattern::Yes = is_pattern { + let mut msg = if let DiagnosticMode::Pattern = mode { format!( "if you meant to match on {}{}{}, use the full path in the pattern", kind, instead, name @@ -2508,19 +2548,25 @@ fn show_candidates( err.note(note); } - if let (IsPattern::Yes, Some(span)) = (is_pattern, use_placement_span) { - err.span_suggestions( - span, - &msg, - accessible_path_strings.into_iter().map(|a| a.0), - Applicability::MaybeIncorrect, - ); - } else if let Some(span) = use_placement_span { + if let Some(span) = use_placement_span { + let add_use = match mode { + DiagnosticMode::Pattern => { + err.span_suggestions( + span, + &msg, + accessible_path_strings.into_iter().map(|a| a.0), + Applicability::MaybeIncorrect, + ); + return; + } + DiagnosticMode::Import => "", + DiagnosticMode::Normal => "use ", + }; for candidate in &mut accessible_path_strings { // produce an additional newline to separate the new use statement // from the directly following item. let additional_newline = if let FoundUse::Yes = found_use { "" } else { "\n" }; - candidate.0 = format!("use {};\n{}", &candidate.0, additional_newline); + candidate.0 = format!("{}{};\n{}", add_use, &candidate.0, additional_newline); } err.span_suggestions( @@ -2550,11 +2596,14 @@ fn show_candidates( err.note(&msg); } - } else { + } else if !matches!(mode, DiagnosticMode::Import) { assert!(!inaccessible_path_strings.is_empty()); - let prefix = - if let IsPattern::Yes = is_pattern { "you might have meant to match on " } else { "" }; + let prefix = if let DiagnosticMode::Pattern = mode { + "you might have meant to match on " + } else { + "" + }; if inaccessible_path_strings.len() == 1 { let (name, descr, def_id, note) = &inaccessible_path_strings[0]; let msg = format!( @@ -2562,7 +2611,7 @@ fn show_candidates( prefix, descr, name, - if let IsPattern::Yes = is_pattern { ", which" } else { "" } + if let DiagnosticMode::Pattern = mode { ", which" } else { "" } ); if let Some(local_def_id) = def_id.and_then(|did| did.as_local()) { diff --git a/compiler/rustc_resolve/src/effective_visibilities.rs b/compiler/rustc_resolve/src/effective_visibilities.rs new file mode 100644 index 000000000..c40669ac9 --- /dev/null +++ b/compiler/rustc_resolve/src/effective_visibilities.rs @@ -0,0 +1,188 @@ +use crate::{ImportKind, NameBindingKind, Resolver}; +use rustc_ast::ast; +use rustc_ast::visit; +use rustc_ast::visit::Visitor; +use rustc_ast::Crate; +use rustc_ast::EnumDef; +use rustc_hir::def_id::LocalDefId; +use rustc_hir::def_id::CRATE_DEF_ID; +use rustc_middle::middle::privacy::Level; +use rustc_middle::ty::{DefIdTree, Visibility}; + +pub struct EffectiveVisibilitiesVisitor<'r, 'a> { + r: &'r mut Resolver<'a>, + changed: bool, +} + +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); + visitor.set_bindings_effective_visibilities(CRATE_DEF_ID); + + while visitor.changed { + visitor.reset(); + visit::walk_crate(&mut visitor, krate); + } + + 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) { + assert!(self.r.module_map.contains_key(&&module_id.to_def_id())); + let module = self.r.get_module(module_id.to_def_id()).unwrap(); + let resolutions = self.r.resolutions(module); + + for (_, name_resolution) in resolutions.borrow().iter() { + if let Some(mut binding) = name_resolution.borrow().binding() && !binding.is_ambiguity() { + // 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. + + // 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; + } + } + + 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); + } + } + } + } + + 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; + } + let mut effective_visibilities = std::mem::take(&mut self.r.effective_visibilities); + self.changed |= effective_visibilities.update( + def_id, + nominal_vis, + || Visibility::Restricted(module_id), + parent_id, + tag, + &*self.r, + ); + self.r.effective_visibilities = effective_visibilities; + } +} + +impl<'r, 'ast> Visitor<'ast> for EffectiveVisibilitiesVisitor<'ast, 'r> { + fn visit_item(&mut self, item: &'ast ast::Item) { + let def_id = self.r.local_def_id(item.id); + // Update effective visibilities of nested items. + // If it's a mod, also make the visitor walk all of its items + match item.kind { + // Resolved in rustc_privacy when types are available + ast::ItemKind::Impl(..) => return, + + // Should be unreachable at this stage + ast::ItemKind::MacCall(..) => panic!( + "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(..) => { + self.set_bindings_effective_visibilities(def_id); + visit::walk_item(self, item); + } + + ast::ItemKind::Enum(EnumDef { ref variants }, _) => { + self.set_bindings_effective_visibilities(def_id); + 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); + } + } + } + + 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); + } + } + + ast::ItemKind::Trait(..) => { + self.set_bindings_effective_visibilities(def_id); + } + + ast::ItemKind::ExternCrate(..) + | ast::ItemKind::Use(..) + | ast::ItemKind::Static(..) + | ast::ItemKind::Const(..) + | ast::ItemKind::GlobalAsm(..) + | ast::ItemKind::TyAlias(..) + | ast::ItemKind::TraitAlias(..) + | ast::ItemKind::MacroDef(..) + | ast::ItemKind::Fn(..) => return, + } + } +} diff --git a/compiler/rustc_resolve/src/ident.rs b/compiler/rustc_resolve/src/ident.rs index 2287aa1eb..e0542d547 100644 --- a/compiler/rustc_resolve/src/ident.rs +++ b/compiler/rustc_resolve/src/ident.rs @@ -1162,7 +1162,7 @@ impl<'a> Resolver<'a> { return Res::Err; } } - Res::Def(DefKind::TyParam, _) | Res::SelfTy { .. } => { + Res::Def(DefKind::TyParam, _) | Res::SelfTyParam { .. } | Res::SelfTyAlias { .. } => { for rib in ribs { let has_generic_params: HasGenericParams = match rib.kind { NormalRibKind @@ -1182,11 +1182,21 @@ impl<'a> Resolver<'a> { if !(trivial == ConstantHasGenerics::Yes || features.generic_const_exprs) { - // HACK(min_const_generics): If we encounter `Self` in an anonymous constant - // we can't easily tell if it's generic at this stage, so we instead remember - // this and then enforce the self type to be concrete later on. - if let Res::SelfTy { trait_, alias_to: Some((def, _)) } = res { - res = Res::SelfTy { trait_, alias_to: Some((def, true)) } + // HACK(min_const_generics): If we encounter `Self` in an anonymous + // constant we can't easily tell if it's generic at this stage, so + // we instead remember this and then enforce the self type to be + // concrete later on. + if let Res::SelfTyAlias { + alias_to: def, + forbid_generic: _, + is_trait_impl, + } = res + { + res = Res::SelfTyAlias { + alias_to: def, + forbid_generic: true, + is_trait_impl, + } } else { if let Some(span) = finalize { self.report_error( diff --git a/compiler/rustc_resolve/src/imports.rs b/compiler/rustc_resolve/src/imports.rs index c133c272b..f2cc50c19 100644 --- a/compiler/rustc_resolve/src/imports.rs +++ b/compiler/rustc_resolve/src/imports.rs @@ -1,9 +1,9 @@ //! A bunch of methods and structures more or less related to resolving imports. -use crate::diagnostics::Suggestion; +use crate::diagnostics::{import_candidates, Suggestion}; use crate::Determinacy::{self, *}; -use crate::Namespace::{MacroNS, TypeNS}; -use crate::{module_to_string, names_to_string}; +use crate::Namespace::*; +use crate::{module_to_string, names_to_string, ImportSuggestion}; use crate::{AmbiguityKind, BindingKey, ModuleKind, ResolutionError, Resolver, Segment}; use crate::{Finalize, Module, ModuleOrUniformRoot, ParentScope, PerNS, ScopeSet}; use crate::{NameBinding, NameBindingKind, PathResult}; @@ -252,7 +252,7 @@ impl<'a> Resolver<'a> { self.set_binding_parent_module(binding, module); self.update_resolution(module, key, |this, resolution| { if let Some(old_binding) = resolution.binding { - if res == Res::Err { + if res == Res::Err && old_binding.res() != Res::Err { // Do not override real bindings with `Res::Err`s from error recovery. return Ok(()); } @@ -381,6 +381,7 @@ struct UnresolvedImportError { label: Option<String>, note: Option<String>, suggestion: Option<Suggestion>, + candidate: Option<Vec<ImportSuggestion>>, } pub struct ImportResolver<'a, 'b> { @@ -472,6 +473,7 @@ impl<'a, 'b> ImportResolver<'a, 'b> { label: None, note: None, suggestion: None, + candidate: None, }; if path.contains("::") { errors.push((path, err)) @@ -522,6 +524,16 @@ impl<'a, 'b> ImportResolver<'a, 'b> { } diag.multipart_suggestion(&msg, suggestions, applicability); } + + if let Some(candidate) = &err.candidate { + import_candidates( + self.r.session, + &self.r.source_span, + &mut diag, + Some(err.span), + &candidate, + ) + } } diag.emit(); @@ -639,6 +651,7 @@ impl<'a, 'b> ImportResolver<'a, 'b> { Some(finalize), ignore_binding, ); + let no_ambiguity = self.r.ambiguity_errors.len() == prev_ambiguity_errors_len; import.vis.set(orig_vis); let module = match path_res { @@ -681,12 +694,14 @@ impl<'a, 'b> ImportResolver<'a, 'b> { String::from("a similar path exists"), Applicability::MaybeIncorrect, )), + candidate: None, }, None => UnresolvedImportError { span, label: Some(label), note: None, suggestion, + candidate: None, }, }; return Some(err); @@ -729,6 +744,7 @@ impl<'a, 'b> ImportResolver<'a, 'b> { label: Some(String::from("cannot glob-import a module into itself")), note: None, suggestion: None, + candidate: None, }); } } @@ -894,11 +910,19 @@ impl<'a, 'b> ImportResolver<'a, 'b> { } }; + let parent_suggestion = + self.r.lookup_import_candidates(ident, TypeNS, &import.parent_scope, |_| true); + Some(UnresolvedImportError { span: import.span, label: Some(label), note, suggestion, + candidate: if !parent_suggestion.is_empty() { + Some(parent_suggestion) + } else { + None + }, }) } else { // `resolve_ident_in_module` reported a privacy error. diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index d8d82e125..00eb768ad 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -19,7 +19,7 @@ use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap}; use rustc_errors::DiagnosticId; 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}; +use rustc_hir::def_id::{DefId, LocalDefId, CRATE_DEF_ID, LOCAL_CRATE}; use rustc_hir::{BindingAnnotation, PrimTy, TraitCandidate}; use rustc_middle::middle::resolve_lifetime::Set1; use rustc_middle::ty::DefIdTree; @@ -35,7 +35,6 @@ use std::collections::{hash_map::Entry, BTreeSet}; use std::mem::{replace, take}; mod diagnostics; -pub(crate) mod lifetimes; type Res = def::Res<NodeId>; @@ -226,22 +225,14 @@ enum LifetimeUseSet { #[derive(Copy, Clone, Debug)] enum LifetimeRibKind { - /// This rib acts as a barrier to forbid reference to lifetimes of a parent item. - Item, - + // -- Ribs introducing named lifetimes + // /// This rib declares generic parameters. + /// Only for this kind the `LifetimeRib::bindings` field can be non-empty. Generics { binder: NodeId, span: Span, kind: LifetimeBinderKind }, - /// FIXME(const_generics): This patches over an ICE caused by non-'static lifetimes in const - /// generics. We are disallowing this until we can decide on how we want to handle non-'static - /// lifetimes in const generics. See issue #74052 for discussion. - ConstGeneric, - - /// Non-static lifetimes are prohibited in anonymous constants under `min_const_generics`. - /// This function will emit an error if `generic_const_exprs` is not enabled, the body identified by - /// `body_id` is an anonymous constant and `lifetime_ref` is non-static. - AnonConst, - + // -- Ribs introducing unnamed lifetimes + // /// Create a new anonymous lifetime parameter and reference it. /// /// If `report_in_path`, report an error when encountering lifetime elision in a path: @@ -258,16 +249,31 @@ enum LifetimeRibKind { /// ``` AnonymousCreateParameter { binder: NodeId, report_in_path: bool }, + /// Replace all anonymous lifetimes by provided lifetime. + Elided(LifetimeRes), + + // -- Barrier ribs that stop lifetime lookup, or continue it but produce an error later. + // /// Give a hard error when either `&` or `'_` is written. Used to /// rule out things like `where T: Foo<'_>`. Does not imply an /// error on default object bounds (e.g., `Box<dyn Foo>`). AnonymousReportError, - /// Replace all anonymous lifetimes by provided lifetime. - Elided(LifetimeRes), - /// Signal we cannot find which should be the anonymous lifetime. ElisionFailure, + + /// FIXME(const_generics): This patches over an ICE caused by non-'static lifetimes in const + /// generics. We are disallowing this until we can decide on how we want to handle non-'static + /// lifetimes in const generics. See issue #74052 for discussion. + ConstGeneric, + + /// Non-static lifetimes are prohibited in anonymous constants under `min_const_generics`. + /// This function will emit an error if `generic_const_exprs` is not enabled, the body + /// identified by `body_id` is an anonymous constant and `lifetime_ref` is non-static. + AnonConst, + + /// This rib acts as a barrier to forbid reference to lifetimes of a parent item. + Item, } #[derive(Copy, Clone, Debug)] @@ -415,7 +421,8 @@ impl<'a> PathSource<'a> { | DefKind::ForeignTy, _, ) | Res::PrimTy(..) - | Res::SelfTy { .. } + | Res::SelfTyParam { .. } + | Res::SelfTyAlias { .. } ), PathSource::Trait(AliasPossibility::No) => matches!(res, Res::Def(DefKind::Trait, _)), PathSource::Trait(AliasPossibility::Maybe) => { @@ -449,7 +456,8 @@ impl<'a> PathSource<'a> { | DefKind::TyAlias | DefKind::AssocTy, _, - ) | Res::SelfTy { .. } + ) | Res::SelfTyParam { .. } + | Res::SelfTyAlias { .. } ), PathSource::TraitItem(ns) => match res { Res::Def(DefKind::AssocConst | DefKind::AssocFn, _) if ns == ValueNS => true, @@ -517,6 +525,9 @@ struct DiagnosticMetadata<'ast> { /// Used to detect possible `if let` written without `let` and to provide structured suggestion. in_if_condition: Option<&'ast Expr>, + /// Used to detect possible new binding written without `let` and to provide structured suggestion. + in_assignment: Option<&'ast Expr>, + /// If we are currently in a trait object definition. Used to point at the bounds when /// encountering a struct or enum. current_trait_object: Option<&'ast [ast::GenericBound]>, @@ -641,8 +652,7 @@ impl<'a: 'ast, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> { // Check whether we should interpret this as a bare trait object. if qself.is_none() && let Some(partial_res) = self.r.partial_res_map.get(&ty.id) - && partial_res.unresolved_segments() == 0 - && let Res::Def(DefKind::Trait | DefKind::TraitAlias, _) = partial_res.base_res() + && let Some(Res::Def(DefKind::Trait | DefKind::TraitAlias, _)) = partial_res.full_res() { // This path is actually a bare trait object. In case of a bare `Fn`-trait // object with anonymous lifetimes, we need this rib to correctly place the @@ -749,35 +759,31 @@ impl<'a: 'ast, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> { fn visit_foreign_item(&mut self, foreign_item: &'ast ForeignItem) { match foreign_item.kind { ForeignItemKind::TyAlias(box TyAlias { ref generics, .. }) => { - self.with_lifetime_rib(LifetimeRibKind::Item, |this| { - this.with_generic_param_rib( - &generics.params, - ItemRibKind(HasGenericParams::Yes(generics.span)), - LifetimeRibKind::Generics { - binder: foreign_item.id, - kind: LifetimeBinderKind::Item, - span: generics.span, - }, - |this| visit::walk_foreign_item(this, foreign_item), - ) - }); + self.with_generic_param_rib( + &generics.params, + ItemRibKind(HasGenericParams::Yes(generics.span)), + LifetimeRibKind::Generics { + binder: foreign_item.id, + kind: LifetimeBinderKind::Item, + span: generics.span, + }, + |this| visit::walk_foreign_item(this, foreign_item), + ); } ForeignItemKind::Fn(box Fn { ref generics, .. }) => { - self.with_lifetime_rib(LifetimeRibKind::Item, |this| { - this.with_generic_param_rib( - &generics.params, - ItemRibKind(HasGenericParams::Yes(generics.span)), - LifetimeRibKind::Generics { - binder: foreign_item.id, - kind: LifetimeBinderKind::Function, - span: generics.span, - }, - |this| visit::walk_foreign_item(this, foreign_item), - ) - }); + self.with_generic_param_rib( + &generics.params, + ItemRibKind(HasGenericParams::Yes(generics.span)), + LifetimeRibKind::Generics { + binder: foreign_item.id, + kind: LifetimeBinderKind::Function, + span: generics.span, + }, + |this| visit::walk_foreign_item(this, foreign_item), + ); } ForeignItemKind::Static(..) => { - self.with_item_rib(|this| { + self.with_static_rib(|this| { visit::walk_foreign_item(this, foreign_item); }); } @@ -806,7 +812,12 @@ impl<'a: 'ast, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> { sig.decl.has_self(), sig.decl.inputs.iter().map(|Param { ty, .. }| (None, &**ty)), &sig.decl.output, - ) + ); + + this.record_lifetime_params_for_async( + fn_id, + sig.header.asyncness.opt_return_id(), + ); }, ); return; @@ -848,41 +859,7 @@ impl<'a: 'ast, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> { }, ); - // Construct the list of in-scope lifetime parameters for async lowering. - // We include all lifetime parameters, either named or "Fresh". - // The order of those parameters does not matter, as long as it is - // deterministic. - if let Some((async_node_id, _)) = async_node_id { - let mut extra_lifetime_params = this - .r - .extra_lifetime_params_map - .get(&fn_id) - .cloned() - .unwrap_or_default(); - for rib in this.lifetime_ribs.iter().rev() { - extra_lifetime_params.extend( - rib.bindings - .iter() - .map(|(&ident, &(node_id, res))| (ident, node_id, res)), - ); - match rib.kind { - LifetimeRibKind::Item => break, - LifetimeRibKind::AnonymousCreateParameter { - binder, .. - } => { - if let Some(earlier_fresh) = - this.r.extra_lifetime_params_map.get(&binder) - { - extra_lifetime_params.extend(earlier_fresh); - } - } - _ => {} - } - } - this.r - .extra_lifetime_params_map - .insert(async_node_id, extra_lifetime_params); - } + this.record_lifetime_params_for_async(fn_id, async_node_id); if let Some(body) = body { // Ignore errors in function bodies if this is rustdoc @@ -1421,9 +1398,8 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { return self.resolve_anonymous_lifetime(lifetime, false); } - let mut indices = (0..self.lifetime_ribs.len()).rev(); - for i in &mut indices { - let rib = &self.lifetime_ribs[i]; + let mut lifetime_rib_iter = self.lifetime_ribs.iter().rev(); + while let Some(rib) = lifetime_rib_iter.next() { let normalized_ident = ident.normalize_to_macros_2_0(); if let Some(&(_, res)) = rib.bindings.get(&normalized_ident) { self.record_lifetime_res(lifetime.id, res, LifetimeElisionCandidate::Named); @@ -1453,9 +1429,10 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { } else { LifetimeUseSet::Many }), - LifetimeRibKind::Generics { .. } - | LifetimeRibKind::ConstGeneric - | LifetimeRibKind::AnonConst => None, + LifetimeRibKind::Generics { .. } => None, + LifetimeRibKind::ConstGeneric | LifetimeRibKind::AnonConst => { + span_bug!(ident.span, "unexpected rib kind: {:?}", rib.kind) + } }) .unwrap_or(LifetimeUseSet::Many); debug!(?use_ctxt, ?use_set); @@ -1490,13 +1467,16 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { ); return; } - _ => {} + LifetimeRibKind::AnonymousCreateParameter { .. } + | LifetimeRibKind::Elided(_) + | LifetimeRibKind::Generics { .. } + | LifetimeRibKind::ElisionFailure + | LifetimeRibKind::AnonymousReportError => {} } } let mut outer_res = None; - for i in indices { - let rib = &self.lifetime_ribs[i]; + for rib in lifetime_rib_iter { let normalized_ident = ident.normalize_to_macros_2_0(); if let Some((&outer, _)) = rib.bindings.get_key_value(&normalized_ident) { outer_res = Some(outer); @@ -1523,8 +1503,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { count: 1, }; let elision_candidate = LifetimeElisionCandidate::Missing(missing_lifetime); - for i in (0..self.lifetime_ribs.len()).rev() { - let rib = &mut self.lifetime_ribs[i]; + for rib in self.lifetime_ribs.iter().rev() { debug!(?rib.kind); match rib.kind { LifetimeRibKind::AnonymousCreateParameter { binder, .. } => { @@ -1564,9 +1543,11 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { return; } LifetimeRibKind::Item => break, - LifetimeRibKind::Generics { .. } - | LifetimeRibKind::ConstGeneric - | LifetimeRibKind::AnonConst => {} + LifetimeRibKind::Generics { .. } | LifetimeRibKind::ConstGeneric => {} + LifetimeRibKind::AnonConst => { + // There is always an `Elided(LifetimeRes::Static)` inside an `AnonConst`. + span_bug!(lifetime.ident.span, "unexpected rib kind: {:?}", rib.kind) + } } } self.record_lifetime_res(lifetime.id, LifetimeRes::Error, elision_candidate); @@ -1781,9 +1762,11 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { self.report_missing_lifetime_specifiers(vec![missing_lifetime], None); break; } - LifetimeRibKind::Generics { .. } - | LifetimeRibKind::ConstGeneric - | LifetimeRibKind::AnonConst => {} + LifetimeRibKind::Generics { .. } | LifetimeRibKind::ConstGeneric => {} + LifetimeRibKind::AnonConst => { + // There is always an `Elided(LifetimeRes::Static)` inside an `AnonConst`. + span_bug!(elided_lifetime_span, "unexpected rib kind: {:?}", rib.kind) + } } } @@ -1896,9 +1879,11 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { let mut bindings = smallvec![(PatBoundCtx::Product, Default::default())]; for (index, (pat, ty)) in inputs.enumerate() { debug!(?pat, ?ty); - if let Some(pat) = pat { - self.resolve_pattern(pat, PatternSource::FnParam, &mut bindings); - } + self.with_lifetime_rib(LifetimeRibKind::Elided(LifetimeRes::Infer), |this| { + if let Some(pat) = pat { + this.resolve_pattern(pat, PatternSource::FnParam, &mut bindings); + } + }); // Record elision candidates only for this parameter. debug_assert_matches!(self.lifetime_elision_candidates, None); @@ -1992,11 +1977,11 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { match ty.kind { TyKind::ImplicitSelf => true, TyKind::Path(None, _) => { - let path_res = self.r.partial_res_map[&ty.id].base_res(); - if let Res::SelfTy { .. } = path_res { + let path_res = self.r.partial_res_map[&ty.id].full_res(); + if let Some(Res::SelfTyParam { .. } | Res::SelfTyAlias { .. }) = path_res { return true; } - Some(path_res) == self.impl_self + self.impl_self.is_some() && path_res == self.impl_self } _ => false, } @@ -2033,7 +2018,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { None } }) - .map(|res| res.base_res()) + .and_then(|res| res.full_res()) .filter(|res| { // Permit the types that unambiguously always // result in the same type constructor being used @@ -2114,7 +2099,11 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { |this| { let item_def_id = this.r.local_def_id(item.id).to_def_id(); this.with_self_rib( - Res::SelfTy { trait_: None, alias_to: Some((item_def_id, false)) }, + Res::SelfTyAlias { + alias_to: item_def_id, + forbid_generic: false, + is_trait_impl: false, + }, |this| { visit::walk_item(this, item); }, @@ -2228,14 +2217,11 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { }, |this| { let local_def_id = this.r.local_def_id(item.id).to_def_id(); - this.with_self_rib( - Res::SelfTy { trait_: Some(local_def_id), alias_to: None }, - |this| { - this.visit_generics(generics); - walk_list!(this, visit_param_bound, bounds, BoundKind::SuperTraits); - this.resolve_trait_items(items); - }, - ); + this.with_self_rib(Res::SelfTyParam { trait_: local_def_id }, |this| { + this.visit_generics(generics); + walk_list!(this, visit_param_bound, bounds, BoundKind::SuperTraits); + this.resolve_trait_items(items); + }); }, ); } @@ -2252,13 +2238,10 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { }, |this| { let local_def_id = this.r.local_def_id(item.id).to_def_id(); - this.with_self_rib( - Res::SelfTy { trait_: Some(local_def_id), alias_to: None }, - |this| { - this.visit_generics(generics); - walk_list!(this, visit_param_bound, bounds, BoundKind::Bound); - }, - ); + this.with_self_rib(Res::SelfTyParam { trait_: local_def_id }, |this| { + this.visit_generics(generics); + walk_list!(this, visit_param_bound, bounds, BoundKind::Bound); + }); }, ); } @@ -2270,7 +2253,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { } ItemKind::Static(ref ty, _, ref expr) | ItemKind::Const(_, ref ty, ref expr) => { - self.with_item_rib(|this| { + self.with_static_rib(|this| { this.with_lifetime_rib(LifetimeRibKind::Elided(LifetimeRes::Static), |this| { this.visit_ty(ty); }); @@ -2465,11 +2448,9 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { self.label_ribs.pop(); } - fn with_item_rib(&mut self, f: impl FnOnce(&mut Self)) { + fn with_static_rib(&mut self, f: impl FnOnce(&mut Self)) { let kind = ItemRibKind(HasGenericParams::No); - self.with_lifetime_rib(LifetimeRibKind::Item, |this| { - this.with_rib(ValueNS, kind, |this| this.with_rib(TypeNS, kind, f)) - }) + self.with_rib(ValueNS, kind, |this| this.with_rib(TypeNS, kind, f)) } // HACK(min_const_generics,const_evaluatable_unchecked): We @@ -2562,7 +2543,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { AssocItemKind::Fn(box Fn { generics, .. }) => { walk_assoc_item(self, generics, LifetimeBinderKind::Function, item); } - AssocItemKind::TyAlias(box TyAlias { generics, .. }) => self + AssocItemKind::Type(box TyAlias { generics, .. }) => self .with_lifetime_rib(LifetimeRibKind::AnonymousReportError, |this| { walk_assoc_item(this, generics, LifetimeBinderKind::Item, item) }), @@ -2595,7 +2576,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { Finalize::new(trait_ref.ref_id, trait_ref.path.span), ); self.diagnostic_metadata.currently_processing_impl_trait = None; - if let Some(def_id) = res.base_res().opt_def_id() { + if let Some(def_id) = res.expect_full_res().opt_def_id() { new_id = Some(def_id); new_val = Some((self.r.expect_module(def_id), trait_ref.clone())); } @@ -2640,7 +2621,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { }, |this| { // Dummy self type for better errors if `Self` is used in the trait path. - this.with_self_rib(Res::SelfTy { trait_: None, alias_to: None }, |this| { + this.with_self_rib(Res::SelfTyParam { trait_: LOCAL_CRATE.as_def_id() }, |this| { this.with_lifetime_rib( LifetimeRibKind::AnonymousCreateParameter { binder: item_id, @@ -2664,9 +2645,10 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { } let item_def_id = item_def_id.to_def_id(); - let res = Res::SelfTy { - trait_: trait_id, - alias_to: Some((item_def_id, false)), + let res = Res::SelfTyAlias { + alias_to: item_def_id, + forbid_generic: false, + is_trait_impl: trait_id.is_some() }; this.with_self_rib(res, |this| { if let Some(trait_ref) = opt_trait_reference.as_ref() { @@ -2682,8 +2664,9 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { this.with_current_self_type(self_type, |this| { this.with_self_rib_ns(ValueNS, Res::SelfCtor(item_def_id), |this| { debug!("resolve_implementation with_self_rib_ns(ValueNS, ...)"); + let mut seen_trait_items = Default::default(); for item in impl_items { - this.resolve_impl_item(&**item); + this.resolve_impl_item(&**item, &mut seen_trait_items); } }); }); @@ -2697,7 +2680,11 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { ); } - fn resolve_impl_item(&mut self, item: &'ast AssocItem) { + fn resolve_impl_item( + &mut self, + item: &'ast AssocItem, + seen_trait_items: &mut FxHashMap<DefId, Span>, + ) { use crate::ResolutionError::*; match &item.kind { AssocItemKind::Const(_, ty, default) => { @@ -2710,6 +2697,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { &item.kind, ValueNS, item.span, + seen_trait_items, |i, s, c| ConstNotMemberOfTrait(i, s, c), ); @@ -2750,6 +2738,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { &item.kind, ValueNS, item.span, + seen_trait_items, |i, s, c| MethodNotMemberOfTrait(i, s, c), ); @@ -2757,8 +2746,8 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { }, ); } - AssocItemKind::TyAlias(box TyAlias { generics, .. }) => { - debug!("resolve_implementation AssocItemKind::TyAlias"); + AssocItemKind::Type(box TyAlias { generics, .. }) => { + debug!("resolve_implementation AssocItemKind::Type"); // We also need a new scope for the impl item type parameters. self.with_generic_param_rib( &generics.params, @@ -2778,6 +2767,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { &item.kind, TypeNS, item.span, + seen_trait_items, |i, s, c| TypeNotMemberOfTrait(i, s, c), ); @@ -2799,6 +2789,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { kind: &AssocItemKind, ns: Namespace, span: Span, + seen_trait_items: &mut FxHashMap<DefId, Span>, err: F, ) where F: FnOnce(Ident, String, Option<Symbol>) -> ResolutionError<'a>, @@ -2831,9 +2822,27 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { }; let res = binding.res(); - let Res::Def(def_kind, _) = res else { bug!() }; + let Res::Def(def_kind, id_in_trait) = res else { bug!() }; + + match seen_trait_items.entry(id_in_trait) { + Entry::Occupied(entry) => { + self.report_error( + span, + ResolutionError::TraitImplDuplicate { + name: ident.name, + old_span: *entry.get(), + trait_item_span: binding.span, + }, + ); + return; + } + Entry::Vacant(entry) => { + entry.insert(span); + } + }; + match (def_kind, kind) { - (DefKind::AssocTy, AssocItemKind::TyAlias(..)) + (DefKind::AssocTy, AssocItemKind::Type(..)) | (DefKind::AssocFn, AssocItemKind::Fn(..)) | (DefKind::AssocConst, AssocItemKind::Const(..)) => { self.r.record_partial_res(id, PartialRes::new(res)); @@ -2847,7 +2856,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { let (code, kind) = match kind { AssocItemKind::Const(..) => (rustc_errors::error_code!(E0323), "const"), AssocItemKind::Fn(..) => (rustc_errors::error_code!(E0324), "method"), - AssocItemKind::TyAlias(..) => (rustc_errors::error_code!(E0325), "type"), + AssocItemKind::Type(..) => (rustc_errors::error_code!(E0325), "type"), AssocItemKind::MacCall(..) => span_bug!(span, "unexpanded macro"), }; let trait_path = path_names_to_string(path); @@ -2865,10 +2874,13 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { fn resolve_params(&mut self, params: &'ast [Param]) { let mut bindings = smallvec![(PatBoundCtx::Product, Default::default())]; - for Param { pat, ty, .. } in params { - self.resolve_pattern(pat, PatternSource::FnParam, &mut bindings); + self.with_lifetime_rib(LifetimeRibKind::Elided(LifetimeRes::Infer), |this| { + for Param { pat, .. } in params { + this.resolve_pattern(pat, PatternSource::FnParam, &mut bindings); + } + }); + for Param { ty, .. } in params { self.visit_ty(ty); - debug!("(resolving function / closure) recorded parameter"); } } @@ -2923,7 +2935,10 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { } fn is_base_res_local(&self, nid: NodeId) -> bool { - matches!(self.r.partial_res_map.get(&nid).map(|res| res.base_res()), Some(Res::Local(..))) + matches!( + self.r.partial_res_map.get(&nid).map(|res| res.expect_full_res()), + Some(Res::Local(..)) + ) } /// Checks that all of the arms in an or-pattern have exactly the @@ -3326,6 +3341,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { instead, suggestion, path: path.into(), + is_call: source.is_call(), }); } @@ -3390,6 +3406,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { instead: false, suggestion: None, path: path.into(), + is_call: source.is_call(), }); } else { err.cancel(); @@ -3408,12 +3425,11 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { source.defer_to_typeck(), finalize, ) { - Ok(Some(partial_res)) if partial_res.unresolved_segments() == 0 => { - if source.is_expected(partial_res.base_res()) || partial_res.base_res() == Res::Err - { + Ok(Some(partial_res)) if let Some(res) = partial_res.full_res() => { + if source.is_expected(res) || res == Res::Err { partial_res } else { - report_errors(self, Some(partial_res.base_res())) + report_errors(self, Some(res)) } } @@ -3621,20 +3637,21 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { }; if path.len() > 1 - && result.base_res() != Res::Err + && let Some(res) = result.full_res() + && res != Res::Err && path[0].ident.name != kw::PathRoot && path[0].ident.name != kw::DollarCrate { let unqualified_result = { match self.resolve_path(&[*path.last().unwrap()], Some(ns), None) { - PathResult::NonModule(path_res) => path_res.base_res(), + PathResult::NonModule(path_res) => path_res.expect_full_res(), PathResult::Module(ModuleOrUniformRoot::Module(module)) => { module.res().unwrap() } _ => return Ok(Some(result)), } }; - if result.base_res() == unqualified_result { + if res == unqualified_result { let lint = lint::builtin::UNUSED_QUALIFICATIONS; self.r.lint_buffer.buffer_lint( lint, @@ -3828,9 +3845,8 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { ExprKind::Field(ref subexpression, _) => { self.resolve_expr(subexpression, Some(expr)); } - ExprKind::MethodCall(ref segment, ref arguments, _) => { - let mut arguments = arguments.iter(); - self.resolve_expr(arguments.next().unwrap(), Some(expr)); + ExprKind::MethodCall(ref segment, ref receiver, ref arguments, _) => { + self.resolve_expr(receiver, Some(expr)); for argument in arguments { self.resolve_expr(argument, None); } @@ -3927,6 +3943,11 @@ 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; + } _ => { visit::walk_expr(self, expr); } @@ -3962,6 +3983,41 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { Some((ident.name, ns)), ) } + + /// Construct the list of in-scope lifetime parameters for async lowering. + /// We include all lifetime parameters, either named or "Fresh". + /// The order of those parameters does not matter, as long as it is + /// deterministic. + fn record_lifetime_params_for_async( + &mut self, + fn_id: NodeId, + async_node_id: Option<(NodeId, Span)>, + ) { + if let Some((async_node_id, span)) = async_node_id { + let mut extra_lifetime_params = + self.r.extra_lifetime_params_map.get(&fn_id).cloned().unwrap_or_default(); + for rib in self.lifetime_ribs.iter().rev() { + extra_lifetime_params.extend( + rib.bindings.iter().map(|(&ident, &(node_id, res))| (ident, node_id, res)), + ); + match rib.kind { + LifetimeRibKind::Item => break, + LifetimeRibKind::AnonymousCreateParameter { binder, .. } => { + if let Some(earlier_fresh) = self.r.extra_lifetime_params_map.get(&binder) { + extra_lifetime_params.extend(earlier_fresh); + } + } + LifetimeRibKind::Generics { .. } => {} + _ => { + // We are in a function definition. We should only find `Generics` + // and `AnonymousCreateParameter` inside the innermost `Item`. + span_bug!(span, "unexpected rib kind: {:?}", rib.kind) + } + } + } + self.r.extra_lifetime_params_map.insert(async_node_id, extra_lifetime_params); + } + } } struct LifetimeCountVisitor<'a, 'b> { diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs index 3b095eeeb..850f023b1 100644 --- a/compiler/rustc_resolve/src/late/diagnostics.rs +++ b/compiler/rustc_resolve/src/late/diagnostics.rs @@ -38,8 +38,8 @@ type Res = def::Res<ast::NodeId>; /// A field or associated item from self type suggested in case of resolution failure. enum AssocSuggestion { Field, - MethodWithSelf, - AssocFn, + MethodWithSelf { called: bool }, + AssocFn { called: bool }, AssocType, AssocConst, } @@ -48,8 +48,14 @@ impl AssocSuggestion { fn action(&self) -> &'static str { match self { AssocSuggestion::Field => "use the available field", - AssocSuggestion::MethodWithSelf => "call the method with the fully-qualified path", - AssocSuggestion::AssocFn => "call the associated function", + AssocSuggestion::MethodWithSelf { called: true } => { + "call the method with the fully-qualified path" + } + AssocSuggestion::MethodWithSelf { called: false } => { + "refer to the method with the fully-qualified path" + } + AssocSuggestion::AssocFn { called: true } => "call the associated function", + AssocSuggestion::AssocFn { called: false } => "refer to the associated function", AssocSuggestion::AssocConst => "use the associated `const`", AssocSuggestion::AssocType => "use the associated type", } @@ -130,6 +136,33 @@ pub(super) enum LifetimeElisionCandidate { Missing(MissingLifetime), } +/// Only used for diagnostics. +#[derive(Debug)] +struct BaseError { + msg: String, + fallback_label: String, + span: Span, + span_label: Option<(Span, &'static str)>, + could_be_expr: bool, + suggestion: Option<(Span, &'static str, String)>, +} + +#[derive(Debug)] +enum TypoCandidate { + Typo(TypoSuggestion), + Shadowed(Res), + None, +} + +impl TypoCandidate { + fn to_opt_suggestion(self) -> Option<TypoSuggestion> { + match self { + TypoCandidate::Typo(sugg) => Some(sugg), + TypoCandidate::Shadowed(_) | TypoCandidate::None => None, + } + } +} + impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { fn def_span(&self, def_id: DefId) -> Option<Span> { match def_id.krate { @@ -138,35 +171,18 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { } } - /// Handles error reporting for `smart_resolve_path_fragment` function. - /// Creates base error and amends it with one short label and possibly some longer helps/notes. - pub(crate) fn smart_resolve_report_errors( + fn make_base_error( &mut self, path: &[Segment], span: Span, source: PathSource<'_>, res: Option<Res>, - ) -> (DiagnosticBuilder<'a, ErrorGuaranteed>, Vec<ImportSuggestion>) { - let ident_span = path.last().map_or(span, |ident| ident.ident.span); - let ns = source.namespace(); - let is_expected = &|res| source.is_expected(res); - let is_enum_variant = &|res| matches!(res, Res::Def(DefKind::Variant, _)); - - debug!(?res, ?source); - + ) -> BaseError { // Make the base error. - struct BaseError<'a> { - msg: String, - fallback_label: String, - span: Span, - span_label: Option<(Span, &'a str)>, - could_be_expr: bool, - suggestion: Option<(Span, &'a str, String)>, - } let mut expected = source.descr_expected(); let path_str = Segment::names_to_string(path); let item_str = path.last().unwrap().ident; - let base_error = if let Some(res) = res { + if let Some(res) = res { BaseError { msg: format!("expected {}, found {} `{}`", expected, res.descr(), path_str), fallback_label: format!("not a {expected}"), @@ -277,8 +293,20 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { could_be_expr: false, suggestion, } - }; + } + } + /// Handles error reporting for `smart_resolve_path_fragment` function. + /// Creates base error and amends it with one short label and possibly some longer helps/notes. + pub(crate) fn smart_resolve_report_errors( + &mut self, + path: &[Segment], + span: Span, + source: PathSource<'_>, + res: Option<Res>, + ) -> (DiagnosticBuilder<'a, ErrorGuaranteed>, Vec<ImportSuggestion>) { + debug!(?res, ?source); + let base_error = self.make_base_error(path, span, source, res); let code = source.error_code(res.is_some()); let mut err = self.r.session.struct_span_err_with_code(base_error.span, &base_error.msg, code); @@ -289,41 +317,79 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { err.span_label(span, label); } - if let Some(sugg) = base_error.suggestion { - err.span_suggestion_verbose(sugg.0, sugg.1, sugg.2, Applicability::MaybeIncorrect); + if let Some(ref sugg) = base_error.suggestion { + err.span_suggestion_verbose(sugg.0, sugg.1, &sugg.2, Applicability::MaybeIncorrect); } - if let Some(span) = self.diagnostic_metadata.current_block_could_be_bare_struct_literal { - err.multipart_suggestion( - "you might have meant to write a `struct` literal", - vec![ - (span.shrink_to_lo(), "{ SomeStruct ".to_string()), - (span.shrink_to_hi(), "}".to_string()), - ], - Applicability::HasPlaceholders, - ); + self.suggest_bare_struct_literal(&mut err); + self.suggest_pattern_match_with_let(&mut err, source, span); + + self.suggest_self_or_self_ref(&mut err, path, span); + self.detect_assoct_type_constraint_meant_as_path(&mut err, &base_error); + if self.suggest_self_ty(&mut err, source, path, span) + || self.suggest_self_value(&mut err, source, path, span) + { + return (err, Vec::new()); } - match (source, self.diagnostic_metadata.in_if_condition) { - ( - PathSource::Expr(_), - Some(Expr { span: expr_span, kind: ExprKind::Assign(lhs, _, _), .. }), - ) => { - // Icky heuristic so we don't suggest: - // `if (i + 2) = 2` => `if let (i + 2) = 2` (approximately pattern) - // `if 2 = i` => `if let 2 = i` (lhs needs to contain error span) - if lhs.is_approximately_pattern() && lhs.span.contains(span) { - err.span_suggestion_verbose( - expr_span.shrink_to_lo(), - "you might have meant to use pattern matching", - "let ", - Applicability::MaybeIncorrect, - ); + + let (found, candidates) = + self.try_lookup_name_relaxed(&mut err, source, path, span, res, &base_error); + if found { + return (err, candidates); + } + + 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); + 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); + } + } + self.err_code_special_cases(&mut err, source, path, span); + + (err, candidates) + } + + fn detect_assoct_type_constraint_meant_as_path( + &self, + err: &mut Diagnostic, + base_error: &BaseError, + ) { + let Some(ty) = self.diagnostic_metadata.current_type_path else { return; }; + let TyKind::Path(_, path) = &ty.kind else { return; }; + for segment in &path.segments { + let Some(params) = &segment.args else { continue; }; + let ast::GenericArgs::AngleBracketed(ref params) = params.deref() else { continue; }; + for param in ¶ms.args { + let ast::AngleBracketedArg::Constraint(constraint) = param else { continue; }; + let ast::AssocConstraintKind::Bound { bounds } = &constraint.kind else { + continue; + }; + for bound in bounds { + let ast::GenericBound::Trait(trait_ref, ast::TraitBoundModifier::None) + = bound else + { + continue; + }; + if base_error.span == trait_ref.span { + err.span_suggestion_verbose( + constraint.ident.span.between(trait_ref.span), + "you might have meant to write a path instead of an associated type bound", + "::", + Applicability::MachineApplicable, + ); + } } } - _ => {} } + } + fn suggest_self_or_self_ref(&mut self, err: &mut Diagnostic, path: &[Segment], span: Span) { let is_assoc_fn = self.self_type_is_available(); + 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 { err.span_suggestion_short( @@ -358,96 +424,25 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { } } } + } - self.detect_assoct_type_constraint_meant_as_path(base_error.span, &mut err); - - // Emit special messages for unresolved `Self` and `self`. - if is_self_type(path, ns) { - err.code(rustc_errors::error_code!(E0411)); - err.span_label( - span, - "`Self` is only available in impls, traits, and type definitions".to_string(), - ); - 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() - ), - ); - } - return (err, Vec::new()); - } - if is_self_value(path, ns) { - debug!("smart_resolve_path_fragment: E0424, source={:?}", source); - - err.code(rustc_errors::error_code!(E0424)); - err.span_label(span, match source { - PathSource::Pat => "`self` value is a keyword and may not be bound to variables or shadowed", - _ => "`self` value is a keyword only available in methods with a `self` parameter", - }); - if let Some((fn_kind, span)) = &self.diagnostic_metadata.current_function { - // The current function has a `self' parameter, but we were unable to resolve - // a reference to `self`. This can only happen if the `self` identifier we - // are resolving came from a different hygiene context. - if fn_kind.decl().inputs.get(0).map_or(false, |p| p.is_self()) { - err.span_label(*span, "this function has a `self` parameter, but a macro invocation can only access identifiers it receives from parameters"); - } else { - let doesnt = if is_assoc_fn { - let (span, sugg) = fn_kind - .decl() - .inputs - .get(0) - .map(|p| (p.span.shrink_to_lo(), "&self, ")) - .unwrap_or_else(|| { - // Try to look for the "(" after the function name, if possible. - // This avoids placing the suggestion into the visibility specifier. - let span = fn_kind - .ident() - .map_or(*span, |ident| span.with_lo(ident.span.hi())); - ( - self.r - .session - .source_map() - .span_through_char(span, '(') - .shrink_to_hi(), - "&self", - ) - }); - err.span_suggestion_verbose( - span, - "add a `self` receiver parameter to make the associated `fn` a method", - sugg, - Applicability::MaybeIncorrect, - ); - "doesn't" - } else { - "can't" - }; - if let Some(ident) = fn_kind.ident() { - err.span_label( - ident.span, - &format!("this function {} have a `self` parameter", doesnt), - ); - } - } - } else 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() - ), - ); - } - return (err, Vec::new()); - } - + fn try_lookup_name_relaxed( + &mut self, + err: &mut DiagnosticBuilder<'_, ErrorGuaranteed>, + source: PathSource<'_>, + path: &[Segment], + span: Span, + res: Option<Res>, + base_error: &BaseError, + ) -> (bool, Vec<ImportSuggestion>) { // Try to lookup name in more relaxed fashion for better error reporting. let ident = path.last().unwrap().ident; + let is_expected = &|res| source.is_expected(res); + let ns = source.namespace(); + 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) @@ -494,7 +489,7 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { { // Already reported this issue on the lhs of the type ascription. err.delay_as_bug(); - return (err, candidates); + return (true, candidates); } } @@ -522,10 +517,14 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { ); } } + // Try Levenshtein algorithm. - let typo_sugg = self.lookup_typo_candidate(path, ns, is_expected); + let typo_sugg = + self.lookup_typo_candidate(path, source.namespace(), is_expected).to_opt_suggestion(); if path.len() == 1 && self.self_type_is_available() { - if let Some(candidate) = self.lookup_assoc_candidate(ident, ns, is_expected) { + if let Some(candidate) = + self.lookup_assoc_candidate(ident, ns, is_expected, source.is_call()) + { let self_is_available = self.self_value_is_available(path[0].ident.span); match candidate { AssocSuggestion::Field => { @@ -540,16 +539,21 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { err.span_label(span, "a field by this name exists in `Self`"); } } - AssocSuggestion::MethodWithSelf if self_is_available => { + AssocSuggestion::MethodWithSelf { called } if self_is_available => { + let msg = if called { + "you might have meant to call the method" + } else { + "you might have meant to refer to the method" + }; err.span_suggestion( span, - "you might have meant to call the method", + msg, format!("self.{path_str}"), Applicability::MachineApplicable, ); } - AssocSuggestion::MethodWithSelf - | AssocSuggestion::AssocFn + AssocSuggestion::MethodWithSelf { .. } + | AssocSuggestion::AssocFn { .. } | AssocSuggestion::AssocConst | AssocSuggestion::AssocType => { err.span_suggestion( @@ -560,8 +564,8 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { ); } } - self.r.add_typo_suggestion(&mut err, typo_sugg, ident_span); - return (err, candidates); + self.r.add_typo_suggestion(err, typo_sugg, ident_span); + return (true, candidates); } // If the first argument in call is `self` suggest calling a method. @@ -579,14 +583,14 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { format!("self.{path_str}({args_snippet})"), Applicability::MachineApplicable, ); - return (err, candidates); + return (true, candidates); } } // Try context-dependent help if relaxed lookup didn't work. if let Some(res) = res { if self.smart_resolve_context_dependent_help( - &mut err, + err, span, source, res, @@ -594,106 +598,148 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { &base_error.fallback_label, ) { // We do this to avoid losing a secondary span when we override the main error span. - self.r.add_typo_suggestion(&mut err, typo_sugg, ident_span); - return (err, candidates); + self.r.add_typo_suggestion(err, typo_sugg, ident_span); + return (true, candidates); } } + return (false, candidates); + } + fn suggest_trait_and_bounds( + &mut self, + err: &mut DiagnosticBuilder<'_, ErrorGuaranteed>, + source: PathSource<'_>, + res: Option<Res>, + span: Span, + base_error: &BaseError, + ) -> bool { let is_macro = base_error.span.from_expansion() && base_error.span.desugaring_kind().is_none(); - if !self.type_ascription_suggestion(&mut err, base_error.span) { - let mut fallback = false; - if let ( - PathSource::Trait(AliasPossibility::Maybe), - Some(Res::Def(DefKind::Struct | DefKind::Enum | DefKind::Union, _)), - false, - ) = (source, res, is_macro) - { - if let Some(bounds @ [_, .., _]) = self.diagnostic_metadata.current_trait_object { - fallback = true; - let spans: Vec<Span> = bounds - .iter() - .map(|bound| bound.span()) - .filter(|&sp| sp != base_error.span) - .collect(); + let mut fallback = false; - let start_span = bounds[0].span(); - // `end_span` is the end of the poly trait ref (Foo + 'baz + Bar><) - let end_span = bounds.last().unwrap().span(); - // `last_bound_span` is the last bound of the poly trait ref (Foo + >'baz< + Bar) - let last_bound_span = spans.last().cloned().unwrap(); - let mut multi_span: MultiSpan = spans.clone().into(); - for sp in spans { - let msg = if sp == last_bound_span { - format!( - "...because of {these} bound{s}", - these = pluralize!("this", bounds.len() - 1), - s = pluralize!(bounds.len() - 1), - ) - } else { - String::new() - }; - multi_span.push_span_label(sp, msg); - } - multi_span - .push_span_label(base_error.span, "expected this type to be a trait..."); - err.span_help( - multi_span, - "`+` is used to constrain a \"trait object\" type with lifetimes or \ - auto-traits; structs and enums can't be bound in that way", - ); - if bounds.iter().all(|bound| match bound { - ast::GenericBound::Outlives(_) => true, - ast::GenericBound::Trait(tr, _) => tr.span == base_error.span, - }) { - let mut sugg = vec![]; - if base_error.span != start_span { - sugg.push((start_span.until(base_error.span), String::new())); - } - if base_error.span != end_span { - sugg.push((base_error.span.shrink_to_hi().to(end_span), String::new())); - } + if let ( + PathSource::Trait(AliasPossibility::Maybe), + Some(Res::Def(DefKind::Struct | DefKind::Enum | DefKind::Union, _)), + false, + ) = (source, res, is_macro) + { + if let Some(bounds @ [_, .., _]) = self.diagnostic_metadata.current_trait_object { + fallback = true; + let spans: Vec<Span> = bounds + .iter() + .map(|bound| bound.span()) + .filter(|&sp| sp != base_error.span) + .collect(); - err.multipart_suggestion( - "if you meant to use a type and not a trait here, remove the bounds", - sugg, - Applicability::MaybeIncorrect, - ); + let start_span = bounds[0].span(); + // `end_span` is the end of the poly trait ref (Foo + 'baz + Bar><) + let end_span = bounds.last().unwrap().span(); + // `last_bound_span` is the last bound of the poly trait ref (Foo + >'baz< + Bar) + let last_bound_span = spans.last().cloned().unwrap(); + let mut multi_span: MultiSpan = spans.clone().into(); + for sp in spans { + let msg = if sp == last_bound_span { + format!( + "...because of {these} bound{s}", + these = pluralize!("this", bounds.len() - 1), + s = pluralize!(bounds.len() - 1), + ) + } else { + String::new() + }; + multi_span.push_span_label(sp, msg); + } + multi_span.push_span_label(base_error.span, "expected this type to be a trait..."); + err.span_help( + multi_span, + "`+` is used to constrain a \"trait object\" type with lifetimes or \ + auto-traits; structs and enums can't be bound in that way", + ); + if bounds.iter().all(|bound| match bound { + ast::GenericBound::Outlives(_) => true, + ast::GenericBound::Trait(tr, _) => tr.span == base_error.span, + }) { + let mut sugg = vec![]; + if base_error.span != start_span { + sugg.push((start_span.until(base_error.span), String::new())); + } + if base_error.span != end_span { + sugg.push((base_error.span.shrink_to_hi().to(end_span), String::new())); } + + err.multipart_suggestion( + "if you meant to use a type and not a trait here, remove the bounds", + sugg, + Applicability::MaybeIncorrect, + ); } } + } - fallback |= self.restrict_assoc_type_in_where_clause(span, &mut err); + fallback |= self.restrict_assoc_type_in_where_clause(span, err); + fallback + } - if !self.r.add_typo_suggestion(&mut err, typo_sugg, ident_span) { - fallback = true; - match self.diagnostic_metadata.current_let_binding { - Some((pat_sp, Some(ty_sp), None)) - if ty_sp.contains(base_error.span) && base_error.could_be_expr => - { - err.span_suggestion_short( - pat_sp.between(ty_sp), - "use `=` if you meant to assign", - " = ", - Applicability::MaybeIncorrect, - ); - } - _ => {} + fn suggest_typo( + &mut self, + err: &mut DiagnosticBuilder<'_, ErrorGuaranteed>, + source: PathSource<'_>, + path: &[Segment], + span: Span, + base_error: &BaseError, + ) -> bool { + 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) + { + err.span_label( + sugg_span, + format!("you might have meant to refer to this {}", res.descr()), + ); + return true; + } + let mut fallback = false; + let typo_sugg = typo_sugg.to_opt_suggestion(); + if !self.r.add_typo_suggestion(err, typo_sugg, ident_span) { + fallback = true; + match self.diagnostic_metadata.current_let_binding { + Some((pat_sp, Some(ty_sp), None)) + if ty_sp.contains(base_error.span) && base_error.could_be_expr => + { + err.span_suggestion_short( + pat_sp.between(ty_sp), + "use `=` if you meant to assign", + " = ", + Applicability::MaybeIncorrect, + ); } - - // If the trait has a single item (which wasn't matched by Levenshtein), suggest it - let suggestion = self.get_single_associated_item(&path, &source, is_expected); - self.r.add_typo_suggestion(&mut err, suggestion, ident_span); + _ => {} } - if fallback { - // Fallback label. - err.span_label(base_error.span, base_error.fallback_label); + + // If the trait has a single item (which wasn't matched by Levenshtein), suggest it + let suggestion = self.get_single_associated_item(&path, &source, is_expected); + if !self.r.add_typo_suggestion(err, suggestion, ident_span) { + fallback = !self.let_binding_suggestion(err, ident_span); } } + fallback + } + + fn err_code_special_cases( + &mut self, + err: &mut DiagnosticBuilder<'_, ErrorGuaranteed>, + source: PathSource<'_>, + path: &[Segment], + span: Span, + ) { if let Some(err_code) = &err.code { if err_code == &rustc_errors::error_code!(E0425) { for label_rib in &self.label_ribs { for (label_ident, node_id) in &label_rib.bindings { + let ident = path.last().unwrap().ident; if format!("'{}", ident) == label_ident.to_string() { err.span_label(label_ident.span, "a label with a similar name exists"); if let PathSource::Expr(Some(Expr { @@ -724,38 +770,116 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { } } } + } - (err, candidates) + /// Emit special messages for unresolved `Self` and `self`. + fn suggest_self_ty( + &mut self, + err: &mut Diagnostic, + source: PathSource<'_>, + path: &[Segment], + span: Span, + ) -> bool { + if !is_self_type(path, source.namespace()) { + 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(), + ); + 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() + ), + ); + } + true } - fn detect_assoct_type_constraint_meant_as_path(&self, base_span: Span, err: &mut Diagnostic) { - let Some(ty) = self.diagnostic_metadata.current_type_path else { return; }; - let TyKind::Path(_, path) = &ty.kind else { return; }; - for segment in &path.segments { - let Some(params) = &segment.args else { continue; }; - let ast::GenericArgs::AngleBracketed(ref params) = params.deref() else { continue; }; - for param in ¶ms.args { - let ast::AngleBracketedArg::Constraint(constraint) = param else { continue; }; - let ast::AssocConstraintKind::Bound { bounds } = &constraint.kind else { - continue; + fn suggest_self_value( + &mut self, + err: &mut Diagnostic, + source: PathSource<'_>, + path: &[Segment], + span: Span, + ) -> bool { + if !is_self_value(path, source.namespace()) { + return false; + } + + debug!("smart_resolve_path_fragment: E0424, source={:?}", source); + err.code(rustc_errors::error_code!(E0424)); + err.span_label( + span, + match source { + PathSource::Pat => { + "`self` value is a keyword and may not be bound to variables or shadowed" + } + _ => "`self` value is a keyword only available in methods with a `self` parameter", + }, + ); + let is_assoc_fn = self.self_type_is_available(); + if let Some((fn_kind, span)) = &self.diagnostic_metadata.current_function { + // The current function has a `self' parameter, but we were unable to resolve + // a reference to `self`. This can only happen if the `self` identifier we + // are resolving came from a different hygiene context. + if fn_kind.decl().inputs.get(0).map_or(false, |p| p.is_self()) { + err.span_label(*span, "this function has a `self` parameter, but a macro invocation can only access identifiers it receives from parameters"); + } else { + let doesnt = if is_assoc_fn { + let (span, sugg) = fn_kind + .decl() + .inputs + .get(0) + .map(|p| (p.span.shrink_to_lo(), "&self, ")) + .unwrap_or_else(|| { + // Try to look for the "(" after the function name, if possible. + // This avoids placing the suggestion into the visibility specifier. + let span = fn_kind + .ident() + .map_or(*span, |ident| span.with_lo(ident.span.hi())); + ( + self.r + .session + .source_map() + .span_through_char(span, '(') + .shrink_to_hi(), + "&self", + ) + }); + err.span_suggestion_verbose( + span, + "add a `self` receiver parameter to make the associated `fn` a method", + sugg, + Applicability::MaybeIncorrect, + ); + "doesn't" + } else { + "can't" }; - for bound in bounds { - let ast::GenericBound::Trait(trait_ref, ast::TraitBoundModifier::None) - = bound else - { - continue; - }; - if base_span == trait_ref.span { - err.span_suggestion_verbose( - constraint.ident.span.between(trait_ref.span), - "you might have meant to write a path instead of an associated type bound", - "::", - Applicability::MachineApplicable, - ); - } + if let Some(ident) = fn_kind.ident() { + err.span_label( + ident.span, + &format!("this function {} have a `self` parameter", doesnt), + ); } } + } else 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() + ), + ); } + true } fn suggest_swapping_misplaced_self_ty_and_trait( @@ -787,6 +911,45 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { } } + fn suggest_bare_struct_literal(&mut self, err: &mut Diagnostic) { + if let Some(span) = self.diagnostic_metadata.current_block_could_be_bare_struct_literal { + err.multipart_suggestion( + "you might have meant to write a `struct` literal", + vec![ + (span.shrink_to_lo(), "{ SomeStruct ".to_string()), + (span.shrink_to_hi(), "}".to_string()), + ], + Applicability::HasPlaceholders, + ); + } + } + + fn suggest_pattern_match_with_let( + &mut self, + err: &mut Diagnostic, + source: PathSource<'_>, + span: Span, + ) { + if let PathSource::Expr(_) = source && + let Some(Expr { + span: expr_span, + kind: ExprKind::Assign(lhs, _, _), + .. + }) = self.diagnostic_metadata.in_if_condition { + // Icky heuristic so we don't suggest: + // `if (i + 2) = 2` => `if let (i + 2) = 2` (approximately pattern) + // `if 2 = i` => `if let 2 = i` (lhs needs to contain error span) + if lhs.is_approximately_pattern() && lhs.span.contains(span) { + err.span_suggestion_verbose( + expr_span.shrink_to_lo(), + "you might have meant to use pattern matching", + "let ", + Applicability::MaybeIncorrect, + ); + } + } + } + fn get_single_associated_item( &mut self, path: &[Segment], @@ -849,11 +1012,10 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { let Some(partial_res) = self.r.partial_res_map.get(&bounded_ty.id) else { return false; }; - if !(matches!( - partial_res.base_res(), - hir::def::Res::Def(hir::def::DefKind::AssocTy, _) - ) && partial_res.unresolved_segments() == 0) - { + if !matches!( + partial_res.full_res(), + Some(hir::def::Res::Def(hir::def::DefKind::AssocTy, _)) + ) { return false; } (ty, position, path) @@ -867,11 +1029,10 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { let Some(partial_res) = self.r.partial_res_map.get(&peeled_ty.id) else { return false; }; - if !(matches!( - partial_res.base_res(), - hir::def::Res::Def(hir::def::DefKind::TyParam, _) - ) && partial_res.unresolved_segments() == 0) - { + if !matches!( + partial_res.full_res(), + Some(hir::def::Res::Def(hir::def::DefKind::TyParam, _)) + ) { return false; } if let ( @@ -959,41 +1120,14 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { // where a brace being opened means a block is being started. Look // ahead for the next text to see if `span` is followed by a `{`. let sm = self.r.session.source_map(); - let mut sp = span; - loop { - sp = sm.next_point(sp); - match sm.span_to_snippet(sp) { - Ok(ref snippet) => { - if snippet.chars().any(|c| !c.is_whitespace()) { - break; - } - } - _ => break, - } - } + let sp = sm.span_look_ahead(span, None, Some(50)); let followed_by_brace = matches!(sm.span_to_snippet(sp), Ok(ref snippet) if snippet == "{"); // In case this could be a struct literal that needs to be surrounded // by parentheses, find the appropriate span. - let mut i = 0; - let mut closing_brace = None; - loop { - sp = sm.next_point(sp); - match sm.span_to_snippet(sp) { - Ok(ref snippet) => { - if snippet == "}" { - closing_brace = Some(span.to(sp)); - break; - } - } - _ => break, - } - i += 1; - // The bigger the span, the more likely we're incorrect -- - // bound it to 100 chars long. - if i > 100 { - break; - } - } + let closing_span = sm.span_look_ahead(span, Some("}"), Some(50)); + let closing_brace: Option<Span> = sm + .span_to_snippet(closing_span) + .map_or(None, |s| if s == "}" { Some(span.to(closing_span)) } else { None }); (followed_by_brace, closing_brace) } @@ -1017,7 +1151,7 @@ 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(_, args, span) => (args[0].span, *span), + ExprKind::MethodCall(_, receiver, _, span) => (receiver.span, *span), _ => return false, }; @@ -1330,7 +1464,7 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { Applicability::HasPlaceholders, ); } - (Res::SelfTy { .. }, _) if ns == ValueNS => { + (Res::SelfTyParam { .. } | Res::SelfTyAlias { .. }, _) if ns == ValueNS => { err.span_label(span, fallback_label); err.note("can't use `Self` as a constructor, you must use the implemented struct"); } @@ -1363,7 +1497,7 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { .filter(|(_, res)| match (kind, res) { (AssocItemKind::Const(..), Res::Def(DefKind::AssocConst, _)) => true, (AssocItemKind::Fn(_), Res::Def(DefKind::AssocFn, _)) => true, - (AssocItemKind::TyAlias(..), Res::Def(DefKind::AssocTy, _)) => true, + (AssocItemKind::Type(..), Res::Def(DefKind::AssocTy, _)) => true, _ => false, }) .map(|(key, _)| key.ident.name) @@ -1377,6 +1511,7 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { ident: Ident, ns: Namespace, filter_fn: FilterFn, + called: bool, ) -> Option<AssocSuggestion> where FilterFn: Fn(Res) -> bool, @@ -1399,20 +1534,14 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { { // Look for a field with the same name in the current self_type. if let Some(resolution) = self.r.partial_res_map.get(&node_id) { - match resolution.base_res() { - Res::Def(DefKind::Struct | DefKind::Union, did) - if resolution.unresolved_segments() == 0 => - { - if let Some(field_names) = self.r.field_names.get(&did) { - if field_names - .iter() - .any(|&field_name| ident.name == field_name.node) - { - return Some(AssocSuggestion::Field); - } + if let Some(Res::Def(DefKind::Struct | DefKind::Union, did)) = + resolution.full_res() + { + if let Some(field_names) = self.r.field_names.get(&did) { + if field_names.iter().any(|&field_name| ident.name == field_name.node) { + return Some(AssocSuggestion::Field); } } - _ => {} } } } @@ -1424,10 +1553,10 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { return Some(match &assoc_item.kind { ast::AssocItemKind::Const(..) => AssocSuggestion::AssocConst, ast::AssocItemKind::Fn(box ast::Fn { sig, .. }) if sig.decl.has_self() => { - AssocSuggestion::MethodWithSelf + AssocSuggestion::MethodWithSelf { called } } - ast::AssocItemKind::Fn(..) => AssocSuggestion::AssocFn, - ast::AssocItemKind::TyAlias(..) => AssocSuggestion::AssocType, + ast::AssocItemKind::Fn(..) => AssocSuggestion::AssocFn { called }, + ast::AssocItemKind::Type(..) => AssocSuggestion::AssocType, ast::AssocItemKind::MacCall(_) => continue, }); } @@ -1445,10 +1574,12 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { let res = binding.res(); if filter_fn(res) { if self.r.has_self.contains(&res.def_id()) { - return Some(AssocSuggestion::MethodWithSelf); + return Some(AssocSuggestion::MethodWithSelf { called }); } else { match res { - Res::Def(DefKind::AssocFn, _) => return Some(AssocSuggestion::AssocFn), + Res::Def(DefKind::AssocFn, _) => { + return Some(AssocSuggestion::AssocFn { called }); + } Res::Def(DefKind::AssocConst, _) => { return Some(AssocSuggestion::AssocConst); } @@ -1470,22 +1601,38 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { path: &[Segment], ns: Namespace, filter_fn: &impl Fn(Res) -> bool, - ) -> Option<TypoSuggestion> { + ) -> TypoCandidate { let mut names = Vec::new(); if path.len() == 1 { + let mut ctxt = path.last().unwrap().ident.span.ctxt(); + // Search in lexical scope. // Walk backwards up the ribs in scope and collect candidates. for rib in self.ribs[ns].iter().rev() { + let rib_ctxt = if rib.kind.contains_params() { + ctxt.normalize_to_macros_2_0() + } else { + ctxt.normalize_to_macro_rules() + }; + // Locals and type parameters for (ident, &res) in &rib.bindings { - if filter_fn(res) { + if filter_fn(res) && ident.span.ctxt() == rib_ctxt { names.push(TypoSuggestion::typo_from_res(ident.name, res)); } } + + if let RibKind::MacroDefinition(def) = rib.kind && def == self.r.macro_def(ctxt) { + // If an invocation of this macro created `ident`, give up on `ident` + // and switch to `ident`'s source from the macro definition. + ctxt.remove_mark(); + continue; + } + // Items in scope if let RibKind::ModuleRibKind(module) = rib.kind { // Items from this module - self.r.add_module_candidates(module, &mut names, &filter_fn); + self.r.add_module_candidates(module, &mut names, &filter_fn, Some(ctxt)); if let ModuleKind::Block = module.kind { // We can see through blocks @@ -1511,7 +1658,7 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { })); if let Some(prelude) = self.r.prelude { - self.r.add_module_candidates(prelude, &mut names, &filter_fn); + self.r.add_module_candidates(prelude, &mut names, &filter_fn, None); } } break; @@ -1530,7 +1677,7 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { if let PathResult::Module(ModuleOrUniformRoot::Module(module)) = self.resolve_path(mod_path, Some(TypeNS), None) { - self.r.add_module_candidates(module, &mut names, &filter_fn); + self.r.add_module_candidates(module, &mut names, &filter_fn, None); } } @@ -1543,10 +1690,17 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { name, None, ) { - Some(found) if found != name => { - names.into_iter().find(|suggestion| suggestion.candidate == found) + Some(found) => { + let Some(sugg) = names.into_iter().find(|suggestion| suggestion.candidate == found) else { + return TypoCandidate::None; + }; + if found == name { + TypoCandidate::Shadowed(sugg.res) + } else { + TypoCandidate::Typo(sugg) + } } - _ => None, + _ => TypoCandidate::None, } } @@ -1616,26 +1770,16 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { } } if let Ok(base_snippet) = base_snippet { - let mut sp = after_colon_sp; - for _ in 0..100 { - // Try to find an assignment - sp = sm.next_point(sp); - let snippet = sm.span_to_snippet(sp.to(sm.next_point(sp))); - match snippet { - Ok(ref x) if x.as_str() == "=" => { - err.span_suggestion( - base_span, - "maybe you meant to write an assignment here", - format!("let {}", base_snippet), - Applicability::MaybeIncorrect, - ); - show_label = false; - break; - } - Ok(ref x) if x.as_str() == "\n" => break, - Err(_) => break, - Ok(_) => {} - } + // Try to find an assignment + let eq_span = sm.span_look_ahead(after_colon_sp, Some("="), Some(50)); + if let Ok(ref snippet) = sm.span_to_snippet(eq_span) && snippet == "=" { + err.span_suggestion( + base_span, + "maybe you meant to write an assignment here", + format!("let {}", base_snippet), + Applicability::MaybeIncorrect, + ); + show_label = false; } } } @@ -1652,6 +1796,31 @@ 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 && + 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)) + { + 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; + } + } + added_suggestion + } + fn find_module(&mut self, def_id: DefId) -> Option<(Module<'a>, ImportSuggestion)> { let mut result = None; let mut seen_modules = FxHashSet::default(); diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs index 26b9284fe..8aebb7da1 100644 --- a/compiler/rustc_resolve/src/lib.rs +++ b/compiler/rustc_resolve/src/lib.rs @@ -4,7 +4,7 @@ //! Paths in macros, imports, expressions, types, patterns are resolved here. //! Label and lifetime names are resolved here as well. //! -//! Type-relative name resolution (methods, fields, associated items) happens in `rustc_typeck`. +//! Type-relative name resolution (methods, fields, associated items) happens in `rustc_hir_analysis`. #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] #![feature(assert_matches)] @@ -13,7 +13,6 @@ #![feature(if_let_guard)] #![feature(iter_intersperse)] #![feature(let_chains)] -#![cfg_attr(bootstrap, feature(let_else))] #![feature(never_type)] #![recursion_limit = "256"] #![allow(rustdoc::private_intra_doc_links)] @@ -42,12 +41,12 @@ use rustc_hir::TraitCandidate; use rustc_index::vec::IndexVec; use rustc_metadata::creader::{CStore, CrateLoader}; use rustc_middle::metadata::ModChild; -use rustc_middle::middle::privacy::AccessLevels; +use rustc_middle::middle::privacy::EffectiveVisibilities; use rustc_middle::span_bug; -use rustc_middle::ty::query::Providers; -use rustc_middle::ty::{self, DefIdTree, MainDefinition, RegisteredTools, ResolverOutputs}; +use rustc_middle::ty::{self, DefIdTree, MainDefinition, RegisteredTools}; +use rustc_middle::ty::{ResolverGlobalCtxt, ResolverOutputs}; use rustc_query_system::ich::StableHashingContext; -use rustc_session::cstore::{CrateStore, CrateStoreDyn, MetadataLoaderDyn}; +use rustc_session::cstore::{CrateStore, MetadataLoaderDyn}; use rustc_session::lint::LintBuffer; use rustc_session::Session; use rustc_span::hygiene::{ExpnId, LocalExpnId, MacroKind, SyntaxContext, Transparency}; @@ -65,15 +64,15 @@ use imports::{Import, ImportKind, ImportResolver, NameResolution}; use late::{HasGenericParams, PathSource, PatternSource}; use macros::{MacroRulesBinding, MacroRulesScope, MacroRulesScopeRef}; -use crate::access_levels::AccessLevelsVisitor; +use crate::effective_visibilities::EffectiveVisibilitiesVisitor; type Res = def::Res<NodeId>; -mod access_levels; mod build_reduced_graph; mod check_unused; mod def_collector; mod diagnostics; +mod effective_visibilities; mod ident; mod imports; mod late; @@ -238,6 +237,8 @@ enum ResolutionError<'a> { trait_item_span: Span, code: rustc_errors::DiagnosticId, }, + /// Error E0201: multiple impl items for the same trait item. + TraitImplDuplicate { name: Symbol, trait_item_span: Span, old_span: Span }, /// Inline asm `sym` operand must refer to a `fn` or `static`. InvalidAsmSym, } @@ -676,6 +677,8 @@ struct UseError<'a> { /// Path `Segment`s at the place of use that failed. Used for accurate suggestion after telling /// the user to import the item directly. path: Vec<Segment>, + /// Whether the expected source is a call + is_call: bool, } #[derive(Clone, Copy, PartialEq, Debug)] @@ -1028,7 +1031,7 @@ pub struct Resolver<'a> { proc_macros: Vec<NodeId>, confused_type_with_std_module: FxHashMap<Span, Span>, - access_levels: AccessLevels, + effective_visibilities: EffectiveVisibilities, } /// Nothing really interesting here; it just provides memory for the rest of the crate. @@ -1332,7 +1335,7 @@ impl<'a> Resolver<'a> { trait_impls: Default::default(), proc_macros: Default::default(), confused_type_with_std_module: Default::default(), - access_levels: Default::default(), + effective_visibilities: Default::default(), }; let root_parent_scope = ParentScope::module(graph_root, &resolver); @@ -1375,9 +1378,7 @@ impl<'a> Resolver<'a> { Default::default() } - pub fn into_outputs( - self, - ) -> (Definitions, Box<CrateStoreDyn>, ResolverOutputs, ty::ResolverAstLowering) { + pub fn into_outputs(self) -> ResolverOutputs { let proc_macros = self.proc_macros.iter().map(|id| self.local_def_id(*id)).collect(); let definitions = self.definitions; let cstore = Box::new(self.crate_loader.into_cstore()); @@ -1392,13 +1393,14 @@ impl<'a> Resolver<'a> { let glob_map = self.glob_map; let main_def = self.main_def; let confused_type_with_std_module = self.confused_type_with_std_module; - let access_levels = self.access_levels; - let resolutions = ResolverOutputs { + let effective_visibilities = self.effective_visibilities; + let global_ctxt = ResolverGlobalCtxt { + cstore, source_span, expn_that_defined, visibilities, has_pub_restricted, - access_levels, + effective_visibilities, extern_crate_map, reexport_map, glob_map, @@ -1415,7 +1417,7 @@ impl<'a> Resolver<'a> { confused_type_with_std_module, registered_tools: self.registered_tools, }; - let resolutions_lowering = ty::ResolverAstLowering { + let ast_lowering = ty::ResolverAstLowering { legacy_const_generic_args: self.legacy_const_generic_args, partial_res_map: self.partial_res_map, import_res_map: self.import_res_map, @@ -1428,16 +1430,15 @@ impl<'a> Resolver<'a> { trait_map: self.trait_map, builtin_macro_kinds: self.builtin_macro_kinds, }; - (definitions, cstore, resolutions, resolutions_lowering) + ResolverOutputs { definitions, global_ctxt, ast_lowering } } - pub fn clone_outputs( - &self, - ) -> (Definitions, Box<CrateStoreDyn>, ResolverOutputs, ty::ResolverAstLowering) { + pub fn clone_outputs(&self) -> ResolverOutputs { let proc_macros = self.proc_macros.iter().map(|id| self.local_def_id(*id)).collect(); let definitions = self.definitions.clone(); let cstore = Box::new(self.cstore().clone()); - let resolutions = ResolverOutputs { + let global_ctxt = ResolverGlobalCtxt { + cstore, source_span: self.source_span.clone(), expn_that_defined: self.expn_that_defined.clone(), visibilities: self.visibilities.clone(), @@ -1457,9 +1458,9 @@ impl<'a> Resolver<'a> { proc_macros, confused_type_with_std_module: self.confused_type_with_std_module.clone(), registered_tools: self.registered_tools.clone(), - access_levels: self.access_levels.clone(), + effective_visibilities: self.effective_visibilities.clone(), }; - let resolutions_lowering = ty::ResolverAstLowering { + let ast_lowering = ty::ResolverAstLowering { legacy_const_generic_args: self.legacy_const_generic_args.clone(), partial_res_map: self.partial_res_map.clone(), import_res_map: self.import_res_map.clone(), @@ -1472,7 +1473,7 @@ impl<'a> Resolver<'a> { trait_map: self.trait_map.clone(), builtin_macro_kinds: self.builtin_macro_kinds.clone(), }; - (definitions, cstore, resolutions, resolutions_lowering) + ResolverOutputs { definitions, global_ctxt, ast_lowering } } fn create_stable_hashing_context(&self) -> StableHashingContext<'_> { @@ -1520,8 +1521,8 @@ impl<'a> Resolver<'a> { pub fn resolve_crate(&mut self, krate: &Crate) { self.session.time("resolve_crate", || { self.session.time("finalize_imports", || ImportResolver { r: self }.finalize_imports()); - self.session.time("resolve_access_levels", || { - AccessLevelsVisitor::compute_access_levels(self, krate) + self.session.time("compute_effective_visibilities", || { + EffectiveVisibilitiesVisitor::compute_effective_visibilities(self, krate) }); self.session.time("finalize_macro_resolutions", || self.finalize_macro_resolutions()); self.session.time("late_resolve_crate", || self.late_resolve_crate(krate)); @@ -1882,12 +1883,10 @@ impl<'a> Resolver<'a> { match self.maybe_resolve_path(&segments, Some(ns), &parent_scope) { PathResult::Module(ModuleOrUniformRoot::Module(module)) => Some(module.res().unwrap()), - PathResult::NonModule(path_res) if path_res.unresolved_segments() == 0 => { - Some(path_res.base_res()) + PathResult::NonModule(path_res) => path_res.full_res(), + PathResult::Module(ModuleOrUniformRoot::ExternPrelude) | PathResult::Failed { .. } => { + None } - PathResult::Module(ModuleOrUniformRoot::ExternPrelude) - | PathResult::NonModule(..) - | PathResult::Failed { .. } => None, PathResult::Module(..) | PathResult::Indeterminate => unreachable!(), } } @@ -1911,6 +1910,11 @@ impl<'a> Resolver<'a> { } } + /// For rustdoc. + pub fn get_partial_res(&self, node_id: NodeId) -> Option<PartialRes> { + self.partial_res_map.get(&node_id).copied() + } + /// Retrieves the span of the given `DefId` if `DefId` is in the local crate. #[inline] pub fn opt_span(&self, def_id: DefId) -> Option<Span> { @@ -1938,12 +1942,8 @@ impl<'a> Resolver<'a> { return None; } - let partial_res = self.partial_res_map.get(&expr.id)?; - if partial_res.unresolved_segments() != 0 { - return None; - } - - if let Res::Def(def::DefKind::Fn, def_id) = partial_res.base_res() { + let res = self.partial_res_map.get(&expr.id)?.full_res()?; + if let Res::Def(def::DefKind::Fn, def_id) = res { // We only support cross-crate argument rewriting. Uses // within the same crate should be updated to use the new // const generics style. @@ -2065,7 +2065,7 @@ struct Finalize { /// Span of the whole path or some its characteristic fragment. /// E.g. span of `b` in `foo::{a, b, c}`, or full span for regular paths. path_span: Span, - /// Span of the path start, suitable for prepending something to to it. + /// Span of the path start, suitable for prepending something to it. /// E.g. span of `foo` in `foo::{a, b, c}`, or full span for regular paths. root_span: Span, /// Whether to report privacy errors or silently return "no resolution" for them, @@ -2082,7 +2082,3 @@ impl Finalize { Finalize { node_id, path_span, root_span, report_private: true } } } - -pub fn provide(providers: &mut Providers) { - late::lifetimes::provide(providers); -} diff --git a/compiler/rustc_resolve/src/macros.rs b/compiler/rustc_resolve/src/macros.rs index dafa10e9e..9526296f9 100644 --- a/compiler/rustc_resolve/src/macros.rs +++ b/compiler/rustc_resolve/src/macros.rs @@ -12,7 +12,7 @@ use rustc_attr::StabilityLevel; use rustc_data_structures::fx::FxHashSet; use rustc_data_structures::intern::Interned; use rustc_data_structures::sync::Lrc; -use rustc_errors::struct_span_err; +use rustc_errors::{struct_span_err, Applicability}; use rustc_expand::base::{Annotatable, DeriveResolutions, Indeterminate, ResolverExpand}; use rustc_expand::base::{SyntaxExtension, SyntaxExtensionKind}; use rustc_expand::compile_declarative_macro; @@ -590,9 +590,7 @@ impl<'a> Resolver<'a> { let res = if path.len() > 1 { let res = match self.maybe_resolve_path(&path, Some(MacroNS), parent_scope) { - PathResult::NonModule(path_res) if path_res.unresolved_segments() == 0 => { - Ok(path_res.base_res()) - } + PathResult::NonModule(path_res) if let Some(res) = path_res.full_res() => Ok(res), PathResult::Indeterminate if !force => return Err(Determinacy::Undetermined), PathResult::NonModule(..) | PathResult::Indeterminate @@ -692,12 +690,23 @@ impl<'a> Resolver<'a> { Some(Finalize::new(ast::CRATE_NODE_ID, path_span)), None, ) { - PathResult::NonModule(path_res) if path_res.unresolved_segments() == 0 => { - let res = path_res.base_res(); - check_consistency(self, &path, path_span, kind, initial_res, res); + PathResult::NonModule(path_res) if let Some(res) = path_res.full_res() => { + check_consistency(self, &path, path_span, kind, initial_res, res) } path_res @ PathResult::NonModule(..) | path_res @ PathResult::Failed { .. } => { + let mut suggestion = None; let (span, label) = if let PathResult::Failed { span, label, .. } = path_res { + // try to suggest if it's not a macro, maybe a function + if let PathResult::NonModule(partial_res) = self.maybe_resolve_path(&path, Some(ValueNS), &parent_scope) + && partial_res.unresolved_segments() == 0 { + let sm = self.session.source_map(); + let exclamation_span = sm.next_point(span); + suggestion = Some(( + vec![(exclamation_span, "".to_string())], + format!("{} is not a macro, but a {}, try to remove `!`", Segment::names_to_string(&path), partial_res.base_res().descr()), + Applicability::MaybeIncorrect + )); + } (span, label) } else { ( @@ -711,7 +720,7 @@ impl<'a> Resolver<'a> { }; self.report_error( span, - ResolutionError::FailedToResolve { label, suggestion: None }, + ResolutionError::FailedToResolve { label, suggestion }, ); } PathResult::Module(..) | PathResult::Indeterminate => unreachable!(), |