diff --git a/compiler/rustc_ast_lowering/src/index.rs b/compiler/rustc_ast_lowering/src/index.rs index 95fcde08b60bc..33c33685cbfa1 100644 --- a/compiler/rustc_ast_lowering/src/index.rs +++ b/compiler/rustc_ast_lowering/src/index.rs @@ -152,6 +152,13 @@ impl<'a, 'hir> Visitor<'hir> for NodeCollector<'a, 'hir> { self.visit_body(body); } + fn visit_use(&mut self, tree: &'hir UseTree<'hir>, hir_id: HirId) { + if !hir_id.is_owner() { + self.insert(tree.prefix.span, hir_id, Node::NestedUseTree(tree)); + } + intravisit::walk_use(self, tree, hir_id); + } + fn visit_param(&mut self, param: &'hir Param<'hir>) { let node = Node::Param(param); self.insert(param.pat.span, param.hir_id, node); diff --git a/compiler/rustc_ast_lowering/src/item.rs b/compiler/rustc_ast_lowering/src/item.rs index b5e28d21a2613..722bc54cd2d7e 100644 --- a/compiler/rustc_ast_lowering/src/item.rs +++ b/compiler/rustc_ast_lowering/src/item.rs @@ -3,13 +3,12 @@ use rustc_ast::visit::AssocCtxt; use rustc_ast::*; use rustc_errors::{E0570, ErrorGuaranteed, struct_span_code_err}; use rustc_hir::attrs::{AttributeKind, EiiImplResolution}; -use rustc_hir::def::{DefKind, PerNS, Res}; +use rustc_hir::def::{DefKind, Res}; use rustc_hir::{ self as hir, CRATE_OWNER_ID, HirId, ImplItemImplKind, LifetimeSource, PredicateOrigin, Target, find_attr, }; use rustc_middle::middle::resolve::ResolverAstLowering; -use rustc_middle::span_bug; use rustc_middle::ty::TyCtxt; use rustc_middle::ty::data_structures::IndexMap; use rustc_span::def_id::{DefId, LocalDefId}; @@ -244,11 +243,7 @@ impl<'hir> LoweringContext<'_, 'hir> { hir::ItemKind::ExternCrate(*orig_name, ident) } ItemKind::Use(use_tree) => { - // Start with an empty prefix. - let prefix = - Path { segments: ThinVec::new(), span: use_tree.prefix.span.shrink_to_lo() }; - - self.lower_use_tree(use_tree, &prefix, id, vis_span, attrs) + hir::ItemKind::Use(self.lower_use_tree(use_tree, id, vis_span, attrs)) } ItemKind::Static(ast::StaticItem { ident, @@ -599,13 +594,12 @@ impl<'hir> LoweringContext<'_, 'hir> { fn lower_use_tree( &mut self, tree: &UseTree, - prefix: &Path, id: NodeId, vis_span: Span, attrs: &'hir [hir::Attribute], - ) -> hir::ItemKind<'hir> { + ) -> hir::UseTree<'hir> { let path = &tree.prefix; - let segments = prefix.segments.iter().chain(path.segments.iter()).cloned().collect(); + let segments = path.segments.iter().cloned().collect(); match tree.kind { UseTreeKind::Simple(rename) => { @@ -627,104 +621,35 @@ impl<'hir> LoweringContext<'_, 'hir> { let res = self.lower_import_res(id, path.span); let path = self.lower_use_path(res, &path, ParamMode::Explicit); let ident = self.lower_ident(ident); - hir::ItemKind::Use(path, hir::UseKind::Single(ident)) + hir::UseTree { prefix: path, kind: hir::UseKind::Single(ident) } } UseTreeKind::Glob(_) => { let res = self.expect_full_res(id); let res = self.lower_res(res); // Put the result in the appropriate namespace. - let res = match res { - Res::Def(DefKind::Mod | DefKind::Trait, _) => { - PerNS { type_ns: Some(res), value_ns: None, macro_ns: None } - } - Res::Def(DefKind::Enum, _) => { - PerNS { type_ns: None, value_ns: Some(res), macro_ns: None } - } - Res::Err => { - // Propagate the error to all namespaces, just to be sure. - let err = Some(Res::Err); - PerNS { type_ns: err, value_ns: err, macro_ns: err } - } - _ => span_bug!(path.span, "bad glob res {:?}", res), - }; + let res = res.in_namespace(); let path = Path { segments, span: path.span }; let path = self.lower_use_path(res, &path, ParamMode::Explicit); - hir::ItemKind::Use(path, hir::UseKind::Glob) + hir::UseTree { prefix: path, kind: hir::UseKind::Glob } } UseTreeKind::Nested { items: ref trees, .. } => { - // Nested imports are desugared into simple imports. - // So, if we start with - // - // ``` - // pub(x) use foo::{a, b}; - // ``` - // - // we will create three items: - // - // ``` - // pub(x) use foo::a; - // pub(x) use foo::b; - // pub(x) use foo::{}; // <-- this is called the `ListStem` - // ``` - // - // The first two are produced by recursively invoking - // `lower_use_tree` (and indeed there may be things - // like `use foo::{a::{b, c}}` and so forth). They - // wind up being directly added to - // `self.items`. However, the structure of this - // function also requires us to return one item, and - // for that we return the `{}` import (called the - // `ListStem`). - - let span = prefix.span.to(path.span); - let prefix = Path { segments, span }; + let res = self.expect_full_res(id); + let res = self.lower_res(res); + // Put the result in the appropriate namespace. + let res = res.in_namespace(); + let prefix = self.lower_use_path(res, &path, ParamMode::Explicit); // Add all the nested `PathListItem`s to the HIR. - for &(ref use_tree, id) in trees { - let owner_id = self.owner_id(id); - - // Each `use` import is an item and thus are owners of the - // names in the path. Up to this point the nested import is - // the current owner, since we want each desugared import to - // own its own names, we have to adjust the owner before - // lowering the rest of the import. - self.with_hir_id_owner(id, |this| { - // `prefix` is lowered multiple times, but in different HIR owners. - // So each segment gets renewed `HirId` with the same - // `ItemLocalId` and the new owner. (See `lower_node_id`) - let kind = this.lower_use_tree(use_tree, &prefix, id, vis_span, attrs); - if !attrs.is_empty() { - this.curr_owner.attrs.insert(hir::ItemLocalId::ZERO, attrs); - } - - let item = hir::Item { - owner_id, - kind, - vis_span, - span: this.lower_span(use_tree.span()), - eii: find_attr!(attrs, EiiImpl(..) | EiiDeclaration(..)), - }; - hir::OwnerNode::Item(this.arena.alloc(item)) - }); - } + let items = self.arena.alloc_from_iter(trees.iter().map(|&(ref use_tree, id)| { + let hir_id = self.lower_node_id(id); + let def_id = self.curr_owner.owner.node_id_to_def_id[&id]; + if !attrs.is_empty() { + self.curr_owner.attrs.insert(hir_id.local_id, attrs); + } + (self.lower_use_tree(use_tree, id, vis_span, attrs), hir_id, def_id) + })); - // Condition should match `build_reduced_graph_for_use_tree`. - let path = if trees.is_empty() - && !(prefix.segments.is_empty() - || prefix.segments.len() == 1 - && prefix.segments[0].ident.name == kw::PathRoot) - { - // For empty lists we need to lower the prefix so it is checked for things - // like stability later. - let res = self.lower_import_res(id, span); - self.lower_use_path(res, &prefix, ParamMode::Explicit) - } else { - // For non-empty lists we can just drop all the data, the prefix is already - // present in HIR as a part of nested imports. - let span = self.lower_span(span); - self.arena.alloc(hir::UsePath { res: PerNS::default(), segments: &[], span }) - }; - hir::ItemKind::Use(path, hir::UseKind::ListStem) + hir::UseTree { prefix, kind: hir::UseKind::Nested { items } } } } } diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index a27dc47bf27c3..1ebcf206df4a1 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -51,7 +51,6 @@ use rustc_data_structures::sorted_map::SortedMap; use rustc_data_structures::stable_hash::{StableHash, StableHasher}; use rustc_data_structures::steal::Steal; use rustc_data_structures::tagged_ptr::TaggedRef; -use rustc_data_structures::unord::ExtendUnord; use rustc_errors::codes::*; use rustc_errors::{DiagArgFromDisplay, DiagCtxtHandle, ErrorGuaranteed}; use rustc_hir::attrs::lang_items::LangItem; @@ -663,27 +662,6 @@ fn index_ast<'tcx>( let item = mem::replace(item, *dummy); self.insert(item.id, node(Box::new(item))); } - - #[tracing::instrument(level = "trace", skip(self))] - fn visit_item_id_use_tree( - &mut self, - tree: &UseTree, - parent: LocalDefId, - items: &mut SmallVec<[Box; 1]>, - ) { - match tree.kind { - UseTreeKind::Glob(_) | UseTreeKind::Simple(_) => {} - UseTreeKind::Nested { items: ref nested_vec, span } => { - for &(ref nested, id) in nested_vec { - self.insert(id, AstOwner::NestedUseTree(parent)); - items.push(self.make_dummy(id, span, ItemKind::MacCall)); - - let def_id = self.owners[&id].def_id; - self.visit_item_id_use_tree(nested, def_id, items); - } - } - } - } } impl MutVisitor for Indexer<'_, '_> { @@ -693,37 +671,13 @@ fn index_ast<'tcx>( } fn flat_map_item(&mut self, mut item: Box) -> SmallVec<[Box; 1]> { - let def_id = self.owners[&item.id].def_id; mut_visit::walk_item(self, &mut *item); let dummy = self.make_dummy(item.id, item.span, ItemKind::MacCall); - let mut items = smallvec![dummy]; - if let ItemKind::Use(ref use_tree) = item.kind { - self.visit_item_id_use_tree(use_tree, def_id, &mut items); - } + let items = smallvec![dummy]; self.insert(item.id, AstOwner::Item(item)); items } - fn flat_map_stmt(&mut self, stmt: Stmt) -> SmallVec<[Stmt; 1]> { - let Stmt { id, span, kind } = stmt; - let mut id = Some(id); - mut_visit::walk_flat_map_stmt_kind(self, kind) - .into_iter() - .map(|kind| { - // Expanding the current statement is a nested `use` item, - // it is expanded into several flat `use` items. - // Create new NodeIds for the corresponding statements - // as two statements cannot have the same. - let id = id.take().unwrap_or_else(|| { - let next = self.next_node_id; - self.next_node_id.increment_by(1); - next - }); - Stmt { id, kind, span } - }) - .collect() - } - fn visit_assoc_item(&mut self, item: &mut AssocItem, ctxt: visit::AssocCtxt) { mut_visit::walk_assoc_item(self, item, ctxt); match ctxt { @@ -750,12 +704,12 @@ fn lower_to_hir(tcx: TyCtxt<'_>, def_id: LocalDefId) -> hir::MaybeOwner<'_> { let ast_index = tcx.index_ast(()); let resolver_and_node = ast_index.get(def_id).map(Steal::steal); - let fallback_to_ancestor = |parent_id| { + let fallback_to_ancestor = || { // The item did not exist in the AST, it was created while lowering another item. - // `parent_id` may be different from the direct parent of `def_id`, - // for instance use-trees are lowered by the first sibling. + + let parent_id = tcx.local_parent(def_id); let mut parent_info = tcx.lower_to_hir(parent_id); - if let hir::MaybeOwner::NonOwner(hir_id) = parent_info { + while let hir::MaybeOwner::NonOwner(hir_id) = parent_info { // `parent_id` could also not be a owner either. // For instance if `def_id` is an enum variant field, // the direct parent is the enum variant. @@ -766,7 +720,8 @@ fn lower_to_hir(tcx: TyCtxt<'_>, def_id: LocalDefId) -> hir::MaybeOwner<'_> { let parent_info = parent_info.unwrap(); *parent_info.children.get(&def_id).unwrap_or_else(|| { - panic!( + span_bug!( + tcx.source_span(def_id), "{:?} does not appear in children of {:?}", def_id, parent_info.nodes.node().def_id() @@ -778,7 +733,7 @@ fn lower_to_hir(tcx: TyCtxt<'_>, def_id: LocalDefId) -> hir::MaybeOwner<'_> { // `ast_index` does not contain all definitions, only up-to the highest // `LocalDefId` which has a non-trivial `AstOwner`. Gracefully handle // other definitions, in particular those nested inside this highest definition. - return fallback_to_ancestor(tcx.local_parent(def_id)); + return fallback_to_ancestor(); }; let mut item_lowerer = item::ItemLowerer { tcx, resolver: &*resolver }; @@ -790,10 +745,9 @@ fn lower_to_hir(tcx: TyCtxt<'_>, def_id: LocalDefId) -> hir::MaybeOwner<'_> { AstOwner::TraitItem(item) => item_lowerer.lower_trait_item(&item), AstOwner::ImplItem(item) => item_lowerer.lower_impl_item(&item), AstOwner::ForeignItem(item) => item_lowerer.lower_foreign_item(&item), - AstOwner::NestedUseTree(owner_id) => fallback_to_ancestor(*owner_id), // The item existed in the AST, but is not a HIR owner. // Fetch the correct information from its parent. - AstOwner::NonOwner => fallback_to_ancestor(tcx.local_parent(def_id)), + AstOwner::NonOwner => fallback_to_ancestor(), }; tcx.sess.time("drop_ast", || mem::drop(node)); @@ -902,43 +856,6 @@ impl<'hir> LoweringContext<'_, 'hir> { hir::OwnerId { def_id: self.resolver.owners[&node].def_id } } - /// Freshen the `LoweringContext` and ready it to lower a nested item. - /// The lowered item is registered into `self.curr_owner.children`. - /// - /// This function sets up `HirId` lowering infrastructure, - /// and stashes the per-owner state to avoid pollution by the closure. - #[instrument(level = "debug", skip(self, f))] - fn with_hir_id_owner( - &mut self, - owner: NodeId, - f: impl FnOnce(&mut Self) -> hir::OwnerNode<'hir>, - ) { - let child_owner = PerOwnerLoweringState::new(self.resolver, owner); - let parent_owner = mem::replace(&mut self.curr_owner, child_owner); - - // Do not reset `next_node_id` and `node_id_to_def_id`: - // we want `f` to be able to refer to the `LocalDefId`s that the caller created. - // and the caller to refer to some of the subdefinitions' nodes' `LocalDefId`s. - - // Always allocate the first `HirId` for the owner itself. - #[cfg(debug_assertions)] - self.curr_owner - .relowering_checker - .assert_node_is_not_relowered(owner, hir::ItemLocalId::ZERO); - - let item = f(self); - let completed_child_owner = mem::replace(&mut self.curr_owner, parent_owner); - let owner_id = completed_child_owner.owner_id; - let info = completed_child_owner.into_owner_info(self.tcx, item); - - self.curr_owner - .children - .extend_unord(info.children.items().map(|(&def_id, &info)| (def_id, info))); - - debug_assert!(!self.curr_owner.children.contains_key(&owner_id.def_id)); - self.curr_owner.children.insert(owner_id.def_id, hir::MaybeOwner::Owner(info)); - } - /// This method allocates a new `HirId` for the given `NodeId`. /// Take care not to call this method if the resulting `HirId` is then not /// actually used in the HIR, as that would trigger an assertion in the @@ -1002,8 +919,22 @@ impl<'hir> LoweringContext<'_, 'hir> { } fn lower_import_res(&mut self, id: NodeId, span: Span) -> PerNS> { - debug_assert_eq!(id, self.curr_owner.owner.id); - let per_ns = self.curr_owner.owner.import_res.map(|res| res.map(|res| self.lower_res(res))); + let per_ns = self + .curr_owner + .owner + .import_res + .get(&id) + .unwrap_or_else(|| { + let sp = self.tcx.source_span(self.curr_owner.owner.def_id); + self.tcx.dcx().span_delayed_bug( + sp, + "no import_res entry for import, \ + this should only happen if it already errored in resolve", + ); + &PerNS { value_ns: None, type_ns: None, macro_ns: None } + }) + .map(|res| res.map(|res| self.lower_res(res))); + if per_ns.is_empty() { // Propagate the error to all namespaces, just to be sure. self.dcx().span_delayed_bug(span, "no resolution for an import"); diff --git a/compiler/rustc_hir/src/def.rs b/compiler/rustc_hir/src/def.rs index f1047e6c0bab4..64967866d3af3 100644 --- a/compiler/rustc_hir/src/def.rs +++ b/compiler/rustc_hir/src/def.rs @@ -580,6 +580,25 @@ pub enum Res { Err, } +impl Res { + pub fn in_namespace(self) -> PerNS> { + match self { + Res::Def(DefKind::Mod | DefKind::Trait, _) => { + PerNS { type_ns: Some(self), value_ns: None, macro_ns: None } + } + Res::Def(DefKind::Enum, _) => { + PerNS { type_ns: None, value_ns: Some(self), macro_ns: None } + } + Res::Err => { + // Propagate the error to all namespaces, just to be sure. + let err = Some(Res::Err); + PerNS { type_ns: err, value_ns: err, macro_ns: err } + } + _ => panic!("bad path segment res {self:?}"), + } + } +} + impl IntoDiagArg for Res { fn into_diag_arg(self, _: &mut Option) -> DiagArgValue { DiagArgValue::Str(Cow::Borrowed(self.descr())) diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index ee79680d7d1e9..eb24a9733d1e4 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -4072,8 +4072,32 @@ pub struct Variant<'hir> { pub span: Span, } -#[derive(Copy, Clone, PartialEq, Debug, StableHash)] -pub enum UseKind { +#[derive(Copy, Clone, Debug, StableHash)] +pub struct UseTree<'hir> { + pub prefix: &'hir UsePath<'hir>, + pub kind: UseKind<'hir>, +} + +impl UseTree<'_> { + pub fn resolutions(&self) -> impl Iterator>> { + Box::new(std::iter::iter!(|| { + match self.kind { + UseKind::Glob => yield self.prefix.res, + UseKind::Single(_) => yield self.prefix.res, + UseKind::Nested { items } => { + for (item, _, _) in items { + for res in item.resolutions() { + yield res; + } + } + } + } + })()) + } +} + +#[derive(Copy, Clone, Debug, StableHash)] +pub enum UseKind<'hir> { /// One import, e.g., `use foo::bar` or `use foo::bar as baz`. /// Also produced for each element of a list `use`, e.g. /// `use foo::{a, b}` lowers to `use foo::a; use foo::b;`. @@ -4085,10 +4109,8 @@ pub enum UseKind { /// Glob import, e.g., `use foo::*`. Glob, - /// Degenerate list import, e.g., `use foo::{a, b}` produces - /// an additional `use foo::{}` for performing checks such as - /// unstable feature gating. May be removed in the future. - ListStem, + /// `use prefix::{...}` + Nested { items: &'hir [(UseTree<'hir>, HirId, LocalDefId)] }, } /// References to traits in impls. @@ -4267,7 +4289,7 @@ impl<'hir> Item<'hir> { expect_extern_crate, (Option, Ident), ItemKind::ExternCrate(s, ident), (*s, *ident); - expect_use, (&'hir UsePath<'hir>, UseKind), ItemKind::Use(p, uk), (p, *uk); + expect_use, UseTree<'hir>, ItemKind::Use(ut), *ut; expect_static, (Mutability, Ident, &'hir Ty<'hir>, BodyId), ItemKind::Static(mutbl, ident, ty, body), (*mutbl, *ident, ty, *body); @@ -4520,7 +4542,7 @@ pub enum ItemKind<'hir> { /// or just /// /// `use foo::bar::baz;` (with `as baz` implicitly on the right). - Use(&'hir UsePath<'hir>, UseKind), + Use(UseTree<'hir>), /// A `static` item. Static(Mutability, Ident, &'hir Ty<'hir>, BodyId), @@ -4615,7 +4637,7 @@ impl ItemKind<'_> { pub fn ident(&self) -> Option { match *self { ItemKind::ExternCrate(_, ident) - | ItemKind::Use(_, UseKind::Single(ident)) + | ItemKind::Use(UseTree { kind: UseKind::Single(ident), .. }) | ItemKind::Static(_, ident, ..) | ItemKind::Const(ident, ..) | ItemKind::Fn { ident, .. } @@ -4628,7 +4650,7 @@ impl ItemKind<'_> { | ItemKind::Trait { ident, .. } | ItemKind::TraitAlias(_, ident, ..) => Some(ident), - ItemKind::Use(_, UseKind::Glob | UseKind::ListStem) + ItemKind::Use(UseTree { kind: UseKind::Glob | UseKind::Nested { .. }, .. }) | ItemKind::ForeignMod { .. } | ItemKind::GlobalAsm { .. } | ItemKind::Impl(_) @@ -4885,6 +4907,7 @@ impl<'hir> From> for Node<'hir> { pub enum Node<'hir> { Param(&'hir Param<'hir>), Item(&'hir Item<'hir>), + NestedUseTree(&'hir UseTree<'hir>), ForeignItem(&'hir ForeignItem<'hir>), TraitItem(&'hir TraitItem<'hir>), ImplItem(&'hir ImplItem<'hir>), @@ -4950,6 +4973,7 @@ impl<'hir> Node<'hir> { Node::TraitItem(TraitItem { ident, .. }) | Node::ImplItem(ImplItem { ident, .. }) | Node::ForeignItem(ForeignItem { ident, .. }) + | Node::NestedUseTree(UseTree { kind: UseKind::Single(ident), .. }) | Node::Field(FieldDef { ident, .. }) | Node::Variant(Variant { ident, .. }) | Node::PathSegment(PathSegment { ident, .. }) => Some(*ident), @@ -4977,6 +5001,7 @@ impl<'hir> Node<'hir> { | Node::Ty(..) | Node::TraitRef(..) | Node::OpaqueTy(..) + | Node::NestedUseTree(_) | Node::Infer(..) | Node::WherePredicate(..) | Node::TestBinderForall(..) diff --git a/compiler/rustc_hir/src/intravisit.rs b/compiler/rustc_hir/src/intravisit.rs index 9cd4b5d7d001f..263f6cc36532c 100644 --- a/compiler/rustc_hir/src/intravisit.rs +++ b/compiler/rustc_hir/src/intravisit.rs @@ -421,8 +421,8 @@ pub trait Visitor<'v>: Sized { ) -> Self::Result { walk_fn(self, fk, fd, b, id) } - fn visit_use(&mut self, path: &'v UsePath<'v>, hir_id: HirId) -> Self::Result { - walk_use(self, path, hir_id) + fn visit_use(&mut self, tree: &'v UseTree<'v>, hir_id: HirId) -> Self::Result { + walk_use(self, tree, hir_id) } fn visit_trait_item(&mut self, ti: &'v TraitItem<'v>) -> Self::Result { walk_trait_item(self, ti) @@ -550,12 +550,8 @@ pub fn walk_item<'v, V: Visitor<'v>>(visitor: &mut V, item: &'v Item<'v>) -> V:: visit_opt!(visitor, visit_name, orig_name); try_visit!(visitor.visit_ident(ident)); } - ItemKind::Use(ref path, kind) => { - try_visit!(visitor.visit_use(path, item.hir_id())); - match kind { - UseKind::Single(ident) => try_visit!(visitor.visit_ident(ident)), - UseKind::Glob | UseKind::ListStem => {} - } + ItemKind::Use(ref tree) => { + try_visit!(visitor.visit_use(tree, item.hir_id())); } ItemKind::Static(_, ident, ref typ, body) => { try_visit!(visitor.visit_ident(ident)); @@ -1264,13 +1260,25 @@ pub fn walk_fn_kind<'v, V: Visitor<'v>>(visitor: &mut V, function_kind: FnKind<' pub fn walk_use<'v, V: Visitor<'v>>( visitor: &mut V, - path: &'v UsePath<'v>, + tree: &'v UseTree<'v>, hir_id: HirId, ) -> V::Result { - let UsePath { segments, ref res, span } = *path; + visitor.visit_id(hir_id); + let UseTree { prefix, kind } = *tree; + let UsePath { segments, ref res, span } = *prefix; for res in res.present_items() { try_visit!(visitor.visit_path(&Path { segments, res, span }, hir_id)); } + + match kind { + UseKind::Single(ident) => try_visit!(visitor.visit_ident(ident)), + UseKind::Glob => {} + UseKind::Nested { items } => { + for (tree, id, _) in items { + try_visit!(visitor.visit_use(tree, *id)); + } + } + } V::Result::output() } diff --git a/compiler/rustc_hir/src/lib.rs b/compiler/rustc_hir/src/lib.rs index 0f31ed196c3df..dd44caef5ca73 100644 --- a/compiler/rustc_hir/src/lib.rs +++ b/compiler/rustc_hir/src/lib.rs @@ -12,6 +12,8 @@ #![feature(derive_const)] #![feature(exhaustive_patterns)] #![feature(final_associated_functions)] +#![feature(iter_macro)] +#![feature(yield_expr)] #![recursion_limit = "256"] // tidy-alphabetical-end diff --git a/compiler/rustc_hir_analysis/src/check_unused.rs b/compiler/rustc_hir_analysis/src/check_unused.rs index 3c8bd2af9b4c1..5da1814dedced 100644 --- a/compiler/rustc_hir_analysis/src/check_unused.rs +++ b/compiler/rustc_hir_analysis/src/check_unused.rs @@ -44,16 +44,15 @@ pub(super) fn check_unused_traits(tcx: TyCtxt<'_>, (): ()) { if used_trait_imports.contains(&id) { continue; } - let item = tcx.hir_expect_item(id); - if item.span.is_dummy() { + let span = tcx.def_span(id); + if span.is_dummy() { continue; } - let (path, _) = item.expect_use(); tcx.emit_node_span_lint( UNUSED_IMPORTS, - item.hir_id(), - path.span, - UnusedImport { tcx, span: path.span }, + tcx.local_def_id_to_hir_id(id), + span, + UnusedImport { tcx, span }, ); } } diff --git a/compiler/rustc_hir_pretty/src/lib.rs b/compiler/rustc_hir_pretty/src/lib.rs index 6d1ae563a9fa2..46137cec81db3 100644 --- a/compiler/rustc_hir_pretty/src/lib.rs +++ b/compiler/rustc_hir_pretty/src/lib.rs @@ -216,6 +216,7 @@ impl<'a> State<'a> { Node::LetStmt(a) => self.print_local_decl(a), Node::Crate(..) => panic!("cannot print Crate"), Node::WherePredicate(pred) => self.print_where_predicate(pred), + Node::NestedUseTree(tree) => self.print_use_tree(tree), Node::TestBinderForall(_) => panic!("cannot print Node::TestBinderForall"), Node::TestBinderExists(_) => panic!("cannot print Node::TestBinderExists"), Node::TestBinderBoundTypeConstraint(_) => { @@ -609,22 +610,10 @@ impl<'a> State<'a> { self.end(ib); self.end(cb); } - hir::ItemKind::Use(path, kind) => { + hir::ItemKind::Use(ref tree) => { let (cb, ib) = self.head("use"); - self.print_path(path, false); - match kind { - hir::UseKind::Single(ident) => { - if path.segments.last().unwrap().ident != ident { - self.space(); - self.word_space("as"); - self.print_ident(ident); - } - self.word(";"); - } - hir::UseKind::Glob => self.word("::*;"), - hir::UseKind::ListStem => self.word("::{};"), - } + self.print_use_tree(tree); self.end(ib); self.end(cb); } @@ -819,6 +808,29 @@ impl<'a> State<'a> { self.ann.post(self, AnnNode::Item(item)) } + fn print_use_tree(&mut self, tree: &hir::UseTree<'_>) { + let hir::UseTree { prefix, kind } = *tree; + self.print_path(prefix, false); + match kind { + hir::UseKind::Single(ident) => { + if tree.prefix.segments.last().unwrap().ident != ident { + self.space(); + self.word_space("as"); + self.print_ident(ident); + } + self.word(";"); + } + hir::UseKind::Glob => self.word("::*;"), + hir::UseKind::Nested { items } => { + self.word("::{"); + for (item, _, _) in items { + self.print_use_tree(item) + } + self.word("};"); + } + } + } + fn print_trait_ref(&mut self, t: &hir::TraitRef<'_>) { self.print_path(t.path, false); } diff --git a/compiler/rustc_hir_typeck/src/method/prelude_edition_lints.rs b/compiler/rustc_hir_typeck/src/method/prelude_edition_lints.rs index 986cefcea77f3..d13ecfe4844e5 100644 --- a/compiler/rustc_hir_typeck/src/method/prelude_edition_lints.rs +++ b/compiler/rustc_hir_typeck/src/method/prelude_edition_lints.rs @@ -442,23 +442,22 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Find an identifier with which this trait was imported (note that `_` doesn't count). for item in import_items.iter() { - let (_, kind) = item.expect_use(); - match kind { + match item.expect_use().kind { hir::UseKind::Single(ident) => { if ident.name != kw::Underscore { return Some(format!("{}", ident.name)); } } hir::UseKind::Glob => return None, // Glob import, so just use its name. - hir::UseKind::ListStem => unreachable!(), + hir::UseKind::Nested { .. } => unreachable!(), } } // All that is left is `_`! We need to use the full path. It doesn't matter which one we // pick, so just take the first one. match import_items[0].kind { - ItemKind::Use(path, _) => { - Some(join_path_idents(path.segments.iter().map(|seg| seg.ident))) + ItemKind::Use(tree) => { + Some(join_path_idents(tree.prefix.segments.iter().map(|seg| seg.ident))) } _ => { span_bug!(span, "unexpected item kind, expected a use: {:?}", import_items[0].kind); diff --git a/compiler/rustc_lint/src/builtin.rs b/compiler/rustc_lint/src/builtin.rs index f85a14852d6cd..09e9d24c0a095 100644 --- a/compiler/rustc_lint/src/builtin.rs +++ b/compiler/rustc_lint/src/builtin.rs @@ -1151,49 +1151,46 @@ impl UnreachablePub { exportable: bool, ) { let mut applicability = Applicability::MachineApplicable; - if cx.tcx.visibility(def_id).is_public() && !cx.effective_visibilities.is_reachable(def_id) + if !cx.tcx.visibility(def_id).is_public() || cx.effective_visibilities.is_reachable(def_id) { - // prefer suggesting `pub(super)` instead of `pub(crate)` when possible, - // except when `pub(super) == pub(crate)` - let new_vis = if let Some(ty::Visibility::Restricted(restricted_did)) = - cx.effective_visibilities.effective_vis(def_id).map(|effective_vis| { - effective_vis.at_level(rustc_middle::middle::privacy::Level::Reachable) - }) - && let parent_parent = cx - .tcx - .parent_module_from_def_id(cx.tcx.parent_module_from_def_id(def_id).into()) - && *restricted_did == parent_parent - && !restricted_did.to_def_id().is_crate_root() - { - "pub(super)" - } else { - "pub(crate)" - }; + return; + } - if vis_span.from_expansion() { - applicability = Applicability::MaybeIncorrect; - } - let def_span = cx.tcx.def_span(def_id); - cx.emit_span_lint( - UNREACHABLE_PUB, - def_span, - BuiltinUnreachablePub { - what, - new_vis, - suggestion: (vis_span, applicability), - help: exportable, - }, - ); + // prefer suggesting `pub(super)` instead of `pub(crate)` when possible, + // except when `pub(super) == pub(crate)` + let new_vis = if let Some(ty::Visibility::Restricted(restricted_did)) = + cx.effective_visibilities.effective_vis(def_id).map(|effective_vis| { + effective_vis.at_level(rustc_middle::middle::privacy::Level::Reachable) + }) + && let parent_parent = + cx.tcx.parent_module_from_def_id(cx.tcx.parent_module_from_def_id(def_id).into()) + && *restricted_did == parent_parent + && !restricted_did.to_def_id().is_crate_root() + { + "pub(super)" + } else { + "pub(crate)" + }; + + if vis_span.from_expansion() { + applicability = Applicability::MaybeIncorrect; } + let def_span = cx.tcx.def_span(def_id); + cx.emit_span_lint( + UNREACHABLE_PUB, + def_span, + BuiltinUnreachablePub { + what, + new_vis, + suggestion: (vis_span, applicability), + help: exportable, + }, + ); } } impl<'tcx> LateLintPass<'tcx> for UnreachablePub { fn check_item(&mut self, cx: &LateContext<'_>, item: &hir::Item<'_>) { - // Do not warn for fake `use` statements. - if let hir::ItemKind::Use(_, hir::UseKind::ListStem) = &item.kind { - return; - } self.perform_lint(cx, "item", item.owner_id.def_id, item.vis_span, true); } diff --git a/compiler/rustc_lint/src/internal.rs b/compiler/rustc_lint/src/internal.rs index 9746b52dda41e..b73cd69236573 100644 --- a/compiler/rustc_lint/src/internal.rs +++ b/compiler/rustc_lint/src/internal.rs @@ -407,7 +407,9 @@ impl<'tcx> LateLintPass<'tcx> for TypeIr { } fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'tcx>) { - let rustc_hir::ItemKind::Use(path, kind) = item.kind else { return }; + let rustc_hir::ItemKind::Use(hir::UseTree { prefix: path, kind }) = item.kind else { + return; + }; let is_mod_inherent = |res: Res| { res.opt_def_id() diff --git a/compiler/rustc_lint/src/unqualified_local_imports.rs b/compiler/rustc_lint/src/unqualified_local_imports.rs index 0bfca85b59b34..73fecba6d02f2 100644 --- a/compiler/rustc_lint/src/unqualified_local_imports.rs +++ b/compiler/rustc_lint/src/unqualified_local_imports.rs @@ -45,7 +45,7 @@ declare_lint_pass!(UnqualifiedLocalImports => [UNQUALIFIED_LOCAL_IMPORTS]); impl<'tcx> LateLintPass<'tcx> for UnqualifiedLocalImports { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'tcx>) { - let hir::ItemKind::Use(path, _kind) = item.kind else { return }; + let hir::ItemKind::Use(hir::UseTree { prefix: path, .. }) = item.kind else { return }; // Check the type and value namespace resolutions for a local crate. let is_local_import = matches!( path.res.type_ns, diff --git a/compiler/rustc_metadata/src/rmeta/decoder.rs b/compiler/rustc_metadata/src/rmeta/decoder.rs index 8a565369d7610..74946947a7dd5 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder.rs @@ -1449,7 +1449,6 @@ impl CrateMetadata { // Structure and variant constructors don't have any attributes encoded for them, // but we assume that someone passing a constructor ID actually wants to look at // the attributes on the corresponding struct or variant. - assert_eq!(def_key.disambiguated_data.data, DefPathData::Ctor); let parent_id = def_key.parent.expect("no parent for a constructor"); self.root .tables diff --git a/compiler/rustc_middle/src/hir/map.rs b/compiler/rustc_middle/src/hir/map.rs index 15188f68ccf52..7b02ff0f866db 100644 --- a/compiler/rustc_middle/src/hir/map.rs +++ b/compiler/rustc_middle/src/hir/map.rs @@ -795,6 +795,7 @@ impl<'tcx> TyCtxt<'tcx> { } Node::Crate(..) => String::from("(root_crate)"), Node::WherePredicate(_) => node_str("where predicate"), + Node::NestedUseTree(_) => node_str("use"), Node::PreciseCapturingNonLifetimeArg(_param) => node_str("parameter"), Node::TestBinderForall(_) => node_str("forall"), Node::TestBinderExists(_) => node_str("exists"), @@ -1002,10 +1003,10 @@ impl<'tcx> TyCtxt<'tcx> { } // Other cases. Node::Item(item) => match &item.kind { - ItemKind::Use(path, _) => { + ItemKind::Use(use_tree) => { // Ensure that the returned span has the item's SyntaxContext, and not the // SyntaxContext of the path. - path.span.find_ancestor_in_same_ctxt(item.span).unwrap_or(item.span) + use_tree.prefix.span.find_ancestor_in_same_ctxt(item.span).unwrap_or(item.span) } _ => { if let Some(ident) = item.kind.ident() { @@ -1074,6 +1075,7 @@ impl<'tcx> TyCtxt<'tcx> { Node::Crate(item) => item.spans.inner_span, Node::WherePredicate(pred) => pred.span, Node::PreciseCapturingNonLifetimeArg(param) => param.ident.span, + Node::NestedUseTree(tree) => tree.prefix.span, Node::TestBinderForall(forall) => forall.span, Node::TestBinderExists(exists) => exists.span, Node::TestBinderBoundTypeConstraint(bound_type) => bound_type.span, diff --git a/compiler/rustc_middle/src/hir/mod.rs b/compiler/rustc_middle/src/hir/mod.rs index 15a24ffea6700..82ea3b5a77999 100644 --- a/compiler/rustc_middle/src/hir/mod.rs +++ b/compiler/rustc_middle/src/hir/mod.rs @@ -343,6 +343,7 @@ impl<'tcx> TyCtxt<'tcx> { | Node::Synthetic | Node::Err(_) | Node::Ctor(_) + | Node::NestedUseTree(_) | Node::Lifetime(_) | Node::GenericParam(_) | Node::Crate(_) diff --git a/compiler/rustc_middle/src/middle/privacy.rs b/compiler/rustc_middle/src/middle/privacy.rs index 5bf4bbe79a9a0..816ce8933350a 100644 --- a/compiler/rustc_middle/src/middle/privacy.rs +++ b/compiler/rustc_middle/src/middle/privacy.rs @@ -8,7 +8,7 @@ use std::hash::Hash; use rustc_data_structures::fx::{FxIndexMap, IndexEntry}; use rustc_data_structures::stable_hash::{StableHash, StableHashCtxt, StableHasher}; use rustc_hir::def::DefKind; -use rustc_hir::{ItemKind, Node, UseKind}; +use rustc_hir::{ItemKind, Node, UseKind, UseTree}; use rustc_macros::StableHash; use rustc_span::def_id::{CRATE_DEF_ID, LocalDefId}; @@ -188,7 +188,7 @@ impl EffectiveVisibilities { let nominal_vis = tcx.visibility(def_id); if ev.reachable.greater_than(nominal_vis, tcx) { if let Node::Item(item) = tcx.hir_node_by_def_id(def_id) - && let ItemKind::Use(_, UseKind::Glob) = item.kind + && let ItemKind::Use(UseTree { kind: UseKind::Glob, .. }) = item.kind { // Glob import visibilities can be increased by other // more public glob imports in cases of ambiguity. diff --git a/compiler/rustc_middle/src/middle/resolve.rs b/compiler/rustc_middle/src/middle/resolve.rs index 8267cde89ad27..14a2743a47671 100644 --- a/compiler/rustc_middle/src/middle/resolve.rs +++ b/compiler/rustc_middle/src/middle/resolve.rs @@ -207,8 +207,8 @@ pub struct PerOwnerResolverData<'tcx> { pub trait_map: NodeMap<&'tcx [TraitCandidate<'tcx>]> = Default::default(), - /// Resolution for import nodes, which have multiple resolutions in different namespaces. - pub import_res: PerNS>> = Default::default(), + /// Resolutions for import nodes, which have multiple resolutions in different namespaces. + pub import_res: NodeMap>>> = Default::default(), /// Lifetime parameters that lowering will have to introduce. pub extra_lifetime_params_map: NodeMap> = Default::default(), @@ -330,9 +330,6 @@ pub type DocLinkResMap = FxIndexMap<(Symbol, Namespace), Option>>; pub enum AstOwner { /// This definition does not correspond to a HIR owner. NonOwner, - /// This definition corresponds to a nested `use` tree. - /// The `LocalDefId` points to its HIR owner. - NestedUseTree(LocalDefId), Crate(Box), Item(Box), TraitItem(Box), diff --git a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs index d11eef067c51a..40ef8de2bfedb 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs @@ -1070,7 +1070,7 @@ fn find_fallback_pattern_typo<'tcx>( if let DefKind::Use = cx.tcx.def_kind(item.owner_id) { // Look for consts being re-exported. let item = cx.tcx.hir_expect_item(item.owner_id.def_id); - let hir::ItemKind::Use(path, _) = item.kind else { + let hir::ItemKind::Use(hir::UseTree { prefix: path, .. }) = item.kind else { continue; }; if let Some(value_ns) = path.res.value_ns diff --git a/compiler/rustc_passes/src/check_export.rs b/compiler/rustc_passes/src/check_export.rs index bd2cf272faa35..3fa314c6f6b4f 100644 --- a/compiler/rustc_passes/src/check_export.rs +++ b/compiler/rustc_passes/src/check_export.rs @@ -132,13 +132,15 @@ impl<'tcx> Visitor<'tcx> for ExportableItemCollector<'tcx> { | hir::ItemKind::TyAlias(..) => { self.add_exportable(def_id); } - hir::ItemKind::Use(path, _) => { - for res in path.res.present_items() { - // Only local items are exportable. - if let Some(res_id) = res.opt_def_id() - && let Some(res_id) = res_id.as_local() - { - self.add_exportable(res_id); + hir::ItemKind::Use(tree) => { + for res in tree.resolutions() { + for res in res.present_items() { + // Only local items are exportable. + if let Some(res_id) = res.opt_def_id() + && let Some(res_id) = res_id.as_local() + { + self.add_exportable(res_id); + } } } } diff --git a/compiler/rustc_passes/src/input_stats.rs b/compiler/rustc_passes/src/input_stats.rs index f2a7cb6e46fca..20eb4b534cd42 100644 --- a/compiler/rustc_passes/src/input_stats.rs +++ b/compiler/rustc_passes/src/input_stats.rs @@ -448,16 +448,24 @@ impl<'v> hir_visit::Visitor<'v> for StatCollector<'v> { hir_visit::walk_fn(self, fk, fd, b, id) } - fn visit_use(&mut self, p: &'v hir::UsePath<'v>, _hir_id: HirId) { + fn visit_use(&mut self, tree: &'v hir::UseTree<'v>, _hir_id: HirId) { // This is `visit_use`, but the type is `Path` so record it that way. - self.record("Path", None, p); + self.record("Path", None, tree); // Don't call `hir_visit::walk_use(self, p, hir_id)`: it calls // `visit_path` up to three times, once for each namespace result in // `p.res`, by building temporary `Path`s that are not part of the real // HIR, which causes `p` to be double- or triple-counted. Instead just // walk the path internals (i.e. the segments) directly. - let hir::Path { span: _, res: _, segments } = *p; + let hir::Path { span: _, res: _, segments } = *tree.prefix; ast_visit::walk_list!(self, visit_path_segment, segments); + match tree.kind { + hir::UseKind::Single(_) | hir::UseKind::Glob => {} + hir::UseKind::Nested { items } => { + for (tree, id, _) in items { + self.visit_use(tree, *id); + } + } + } } fn visit_trait_item(&mut self, ti: &'v hir::TraitItem<'v>) { diff --git a/compiler/rustc_passes/src/reachable.rs b/compiler/rustc_passes/src/reachable.rs index de0d0a4f8a4f2..10be00e14139b 100644 --- a/compiler/rustc_passes/src/reachable.rs +++ b/compiler/rustc_passes/src/reachable.rs @@ -301,6 +301,7 @@ impl<'tcx> ReachableContext<'tcx> { | Node::Field(_) | Node::Ty(_) | Node::Crate(_) + | Node::NestedUseTree(_) | Node::Synthetic | Node::OpaqueTy(..) => {} _ => { diff --git a/compiler/rustc_passes/src/stability.rs b/compiler/rustc_passes/src/stability.rs index 64d969d808fef..a4fe99f346e91 100644 --- a/compiler/rustc_passes/src/stability.rs +++ b/compiler/rustc_passes/src/stability.rs @@ -743,36 +743,72 @@ impl<'tcx> Visitor<'tcx> for Checker<'tcx> { intravisit::walk_poly_trait_ref(self, t); } - fn visit_use(&mut self, path: &'tcx UsePath<'tcx>, hir_id: HirId) { - let res = path.res; - - // A use item can import something from two namespaces at the same time. - // For deprecation/stability we don't want to warn twice. - // This specifically happens with constructors for unit/tuple structs. - if let Some(ty_ns_res) = res.type_ns - && let Some(value_ns_res) = res.value_ns - && let Some(type_ns_did) = ty_ns_res.opt_def_id() - && let Some(value_ns_did) = value_ns_res.opt_def_id() - && let DefKind::Ctor(.., _) = self.tcx.def_kind(value_ns_did) - && self.tcx.parent(value_ns_did) == type_ns_did - { - // Only visit the value namespace path when we've detected a duplicate, - // not the type namespace path. - let UsePath { segments, res: _, span } = *path; - self.visit_path(&Path { segments, res: value_ns_res, span }, hir_id); - - // Though, visit the macro namespace if it exists, - // regardless of the checks above relating to constructors. - if let Some(res) = res.macro_ns { - self.visit_path(&Path { segments, res, span }, hir_id); + fn visit_use(&mut self, tree: &'tcx hir::UseTree<'tcx>, hir_id: HirId) { + let mut v = vec![]; + + #[instrument(skip(visitor))] + fn recurse<'tcx>( + visitor: &mut Checker<'tcx>, + tree: &'tcx hir::UseTree<'tcx>, + hir_id: HirId, + stack: &mut Vec<&'tcx [hir::PathSegment<'tcx>]>, + ) { + let UsePath { segments, res, span } = *tree.prefix; + + match tree.kind { + hir::UseKind::Single(_) | hir::UseKind::Glob => { + // A use item can import something from two namespaces at the same time. + // For deprecation/stability we don't want to warn twice. + // This specifically happens with constructors for unit/tuple structs. + if let Some(res) = res.value_ns.or(res.type_ns) { + visitor.check_path(&Path { segments, res, span }, hir_id, stack); + } + + // Though, visit the macro namespace if it exists, + // regardless of the checks above relating to constructors. + if let Some(res) = res.macro_ns { + visitor.check_path(&Path { segments, res, span }, hir_id, stack); + } + } + hir::UseKind::Nested { items } => { + stack.push(tree.prefix.segments); + if items.is_empty() { + // need to handle `use foo::bar::{};` + visitor.check_path( + &Path { + segments, + res: segments.last().map_or(Res::Err, |seg| seg.res), + span, + }, + hir_id, + stack, + ); + } else { + for (tree, id, _) in items { + recurse(visitor, tree, *id, stack); + } + } + stack.pop(); + } } - } else { - // if there's no duplicate, just walk as normal - intravisit::walk_use(self, path, hir_id) } + recurse(self, tree, hir_id, &mut v); } fn visit_path(&mut self, path: &hir::Path<'tcx>, id: hir::HirId) { + self.check_path(path, id, &[]); + + intravisit::walk_path(self, path) + } +} + +impl<'tcx> Checker<'tcx> { + fn check_path( + &mut self, + path: &hir::Path<'tcx>, + id: hir::HirId, + prefix: &[&[hir::PathSegment<'tcx>]], + ) { if let Some(def_id) = path.res.opt_def_id() { let method_span = path.segments.last().map(|s| s.ident.span); let item_is_allowed = self.tcx.check_stability_allow_unstable( @@ -796,83 +832,104 @@ impl<'tcx> Visitor<'tcx> for Checker<'tcx> { } _ => None, }); + let segments = prefix + .into_iter() + .flat_map(|i| i.into_iter()) + .chain(path.segments.iter().rev().skip(1).rev()); + let intrinsics_module = segments.clone().last(); + for segment in segments { + self.check_path_segments( + path.span, + segment, + intrinsics_module, + id, + method_span, + is_allowed_through_unstable_modules, + ); + } + } + } + } + /// Check parent modules stability as well if the item the path refers to is itself + /// stable. We only emit errors for unstable path segments if the item is stable + /// or allowed because stability is often inherited, so the most common case is that + /// both the segments and the item are unstable behind the same feature flag. + /// + /// We check here rather than in `visit_path_segment` to prevent visiting the last + /// path segment twice + /// + /// We include special cases via #[rustc_allowed_through_unstable_modules] for items + /// that were accidentally stabilized through unstable paths before this check was + /// added, such as `core::intrinsics::transmute` + fn check_path_segments( + &mut self, + span: Span, + path_segment: &hir::PathSegment<'_>, + intrinsics_module: Option<&hir::PathSegment<'_>>, + id: HirId, + method_span: Option, + is_allowed_through_unstable_modules: Option<(Symbol, Symbol)>, + ) { + // The item itself is allowed; check whether the path there is also allowed. - // Check parent modules stability as well if the item the path refers to is itself - // stable. We only emit errors for unstable path segments if the item is stable - // or allowed because stability is often inherited, so the most common case is that - // both the segments and the item are unstable behind the same feature flag. - // - // We check here rather than in `visit_path_segment` to prevent visiting the last - // path segment twice - // - // We include special cases via #[rustc_allowed_through_unstable_modules] for items - // that were accidentally stabilized through unstable paths before this check was - // added, such as `core::intrinsics::transmute` - let parents = path.segments.iter().rev().skip(1); - for path_segment in parents { - if let Some(def_id) = path_segment.res.opt_def_id() { - match is_allowed_through_unstable_modules { - None => { - // Emit a hard stability error if this path is not stable. - - // use `None` for id to prevent deprecation check - self.tcx.check_stability_allow_unstable( - def_id, - None, - path_segment.ident.span, - None, - if is_unstable_reexport(self.tcx, id) { - AllowUnstable::Yes - } else { - AllowUnstable::No - }, - ); - } - Some((message, suggestion)) => { - // Call the stability check directly so that we can control which - // diagnostic is emitted. - let eval_result = self.tcx.eval_stability_allow_unstable( - def_id, - None, - path.span, - None, - if is_unstable_reexport(self.tcx, id) { - AllowUnstable::Yes - } else { - AllowUnstable::No - }, - ); - let is_allowed = matches!(eval_result, EvalResult::Allow); - if !is_allowed { - // Show a deprecation message. - let [.., intrinsics_module, _intrinsic] = path.segments else { - span_bug!( - path.span, - "no module for `is_allowed_through_unstable_modules` intrinsic {path:?}" - ) - }; - let diag = diagnostics::RustcAtumSuggestion { - message, - import_span: path.span, - unstable_mod_span: { intrinsics_module.ident.span }, - module: intrinsics_module.ident, - suggestion, - }; - self.tcx.emit_node_span_lint( - DEPRECATED, - id, - method_span.unwrap_or(path.span), - diag, - ); - } - } - } + if let Some(def_id) = path_segment.res.opt_def_id() { + match is_allowed_through_unstable_modules { + None => { + // Emit a hard stability error if this path is not stable. + + // use `None` for id to prevent deprecation check + self.tcx.check_stability_allow_unstable( + def_id, + None, + path_segment.ident.span, + None, + if is_unstable_reexport(self.tcx, id) { + AllowUnstable::Yes + } else { + AllowUnstable::No + }, + ); + } + Some((message, suggestion)) => { + // Call the stability check directly so that we can control which + // diagnostic is emitted. + let eval_result = self.tcx.eval_stability_allow_unstable( + def_id, + None, + span, + None, + if is_unstable_reexport(self.tcx, id) { + AllowUnstable::Yes + } else { + AllowUnstable::No + }, + ); + let is_allowed = matches!(eval_result, EvalResult::Allow); + if !is_allowed { + // Show a deprecation message. + let intrinsics_module = intrinsics_module.unwrap_or_else(|| { + span_bug!( + span, + "no module for `is_allowed_through_unstable_modules` intrinsic {path_segment:?}" + ) + }); + let diag = diagnostics::RustcAtumSuggestion { + message, + import_span: span, + unstable_mod_span: { intrinsics_module.ident.span }, + module: intrinsics_module.ident, + suggestion, + }; + self.tcx.emit_node_span_lint( + DEPRECATED, + id, + method_span.unwrap_or(span), + diag, + ); } } } } - - intravisit::walk_path(self, path) } } @@ -881,10 +938,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'tcx> { /// See issue #94972 for details on why this is a special case fn is_unstable_reexport(tcx: TyCtxt<'_>, id: hir::HirId) -> bool { // Get the LocalDefId so we can lookup the item to check the kind. - let Some(owner) = id.as_owner() else { - return false; - }; - let def_id = owner.def_id; + let def_id = id.owner.def_id; let Some(stab) = tcx.lookup_stability(def_id) else { return false; @@ -896,7 +950,10 @@ fn is_unstable_reexport(tcx: TyCtxt<'_>, id: hir::HirId) -> bool { } // If this is a path that isn't a use, we don't need to do anything special - if !matches!(tcx.hir_expect_item(def_id).kind, ItemKind::Use(..)) { + if !matches!( + tcx.hir_node(id), + hir::Node::Item(hir::Item { kind: ItemKind::Use(..), .. }) | hir::Node::NestedUseTree(_) + ) { return false; } diff --git a/compiler/rustc_resolve/src/build_reduced_graph.rs b/compiler/rustc_resolve/src/build_reduced_graph.rs index 88f057c3a6d6d..074322bb140d9 100644 --- a/compiler/rustc_resolve/src/build_reduced_graph.rs +++ b/compiler/rustc_resolve/src/build_reduced_graph.rs @@ -754,13 +754,12 @@ impl<'a, 'ra, 'tcx> DefCollector<'a, 'ra, 'tcx> { } ast::UseTreeKind::Nested { ref items, .. } => { for &(ref tree, id) in items { - self.with_owner(id, None, DefKind::Use, use_tree.span(), |this, feed| { - this.build_reduced_graph_for_use_tree( - // This particular use tree - tree, id, &prefix, true, false, // The whole `use` item - item, vis, root_span, feed, - ) - }); + let feed = self.create_def(id, None, DefKind::Use, use_tree.span()); + self.build_reduced_graph_for_use_tree( + // This particular use tree + tree, id, &prefix, true, false, // The whole `use` item + item, vis, root_span, feed, + ); } // Empty groups `a::b::{}` are turned into synthetic `self` imports diff --git a/compiler/rustc_resolve/src/check_unused.rs b/compiler/rustc_resolve/src/check_unused.rs index 8337b2848edc3..7955f04297628 100644 --- a/compiler/rustc_resolve/src/check_unused.rs +++ b/compiler/rustc_resolve/src/check_unused.rs @@ -38,13 +38,14 @@ use rustc_lint_defs::builtin::{ use rustc_span::{DUMMY_SP, Ident, Span, kw}; use crate::imports::{Import, ImportKind}; -use crate::{DeclKind, IdentKey, LateDecl, Resolver, diagnostics, module_to_string}; +use crate::{DeclKind, IdentKey, LateDecl, Resolver, diagnostics, module_to_string, with_owner}; struct UnusedImport { use_tree: ast::UseTree, use_tree_id: ast::NodeId, item_span: Span, unused: UnordSet, + use_tree_def_id: LocalDefId, } impl UnusedImport { @@ -59,7 +60,6 @@ struct UnusedImportCheckVisitor<'a, 'ra, 'tcx> { unused_imports: FxIndexMap, extern_crate_items: Vec, base_use_tree: Option<&'a ast::UseTree>, - base_id: ast::NodeId, item_span: Span, } @@ -89,20 +89,19 @@ impl<'a, 'ra, 'tcx> UnusedImportCheckVisitor<'a, 'ra, 'tcx> { // Check later. return; } - self.unused_import(self.base_id).add(id); + self.unused_import().add(id); } else { // This trait import is definitely used, in a way other than // method resolution. // FIXME(#120456) - is `swap_remove` correct? self.r.maybe_unused_trait_imports.swap_remove(&def_id); - if let Some(i) = self.unused_imports.get_mut(&self.base_id) { + if let Some(i) = self.unused_imports.get_mut(&self.r.current_owner.id) { i.unused.remove(&id); } } } - fn check_use_tree(&mut self, use_tree: &'a ast::UseTree, id: ast::NodeId) { - let def_id = self.r.owner_def_id(id); + fn check_use_tree(&mut self, use_tree: &'a ast::UseTree, id: ast::NodeId, def_id: LocalDefId) { if self.r.effective_visibilities.is_exported(def_id) { self.check_import_as_underscore(use_tree, id); self.r.maybe_unused_trait_imports.swap_remove(&def_id); @@ -111,23 +110,24 @@ impl<'a, 'ra, 'tcx> UnusedImportCheckVisitor<'a, 'ra, 'tcx> { if let ast::UseTreeKind::Nested { ref items, .. } = use_tree.kind { if items.is_empty() { - self.unused_import(self.base_id).add(id); + self.unused_import().add(id); } } else { self.check_import(id, def_id); } } - fn unused_import(&mut self, id: ast::NodeId) -> &mut UnusedImport { - let use_tree_id = self.base_id; + fn unused_import(&mut self) -> &mut UnusedImport { + let use_tree_id = self.r.current_owner.id; let use_tree = self.base_use_tree.unwrap().clone(); let item_span = self.item_span; - self.unused_imports.entry(id).or_insert_with(|| UnusedImport { + self.unused_imports.entry(use_tree_id).or_insert_with(|| UnusedImport { use_tree, use_tree_id, item_span, unused: Default::default(), + use_tree_def_id: self.r.current_owner.def_id, }) } @@ -136,11 +136,11 @@ impl<'a, 'ra, 'tcx> UnusedImportCheckVisitor<'a, 'ra, 'tcx> { ast::UseTreeKind::Simple(Some(ident)) => { if ident.name == kw::Underscore && !matches!( - self.r.owners[&id].import_res.type_ns, + self.r.current_owner.import_res[&id].type_ns, Some(Res::Def(DefKind::Trait | DefKind::TraitAlias, _)) ) { - self.unused_import(self.base_id).add(id); + self.unused_import().add(id); } } ast::UseTreeKind::Nested { ref items, .. } => self.check_imports_as_underscore(items), @@ -243,6 +243,12 @@ impl<'a, 'ra, 'tcx> UnusedImportCheckVisitor<'a, 'ra, 'tcx> { } } +impl<'a, 'ra, 'tcx> AsMut> for UnusedImportCheckVisitor<'a, 'ra, 'tcx> { + fn as_mut(&mut self) -> &mut Resolver<'ra, 'tcx> { + self.r + } +} + impl<'a, 'ra, 'tcx> Visitor<'a> for UnusedImportCheckVisitor<'a, 'ra, 'tcx> { fn visit_item(&mut self, item: &'a ast::Item) { self.item_span = item.span_with_attributes(); @@ -255,9 +261,8 @@ impl<'a, 'ra, 'tcx> Visitor<'a> for UnusedImportCheckVisitor<'a, 'ra, 'tcx> { // Use the base UseTree's NodeId as the item id // This allows the grouping of all the lints in the same item ast::ItemKind::Use(use_tree) => { - self.base_id = item.id; self.base_use_tree = Some(use_tree); - self.check_use_tree(use_tree, item.id); + self.check_use_tree(use_tree, item.id, self.r.current_owner.def_id); } &ast::ItemKind::ExternCrate(orig_name, ident) => { self.extern_crate_items.push(ExternCrateToLint { @@ -277,7 +282,7 @@ impl<'a, 'ra, 'tcx> Visitor<'a> for UnusedImportCheckVisitor<'a, 'ra, 'tcx> { } fn visit_nested_use_tree(&mut self, use_tree: &'a ast::UseTree, id: ast::NodeId) { - self.check_use_tree(use_tree, id); + self.check_use_tree(use_tree, id, self.r.local_def_id(id)); visit::walk_use_tree(self, use_tree); } } @@ -462,12 +467,11 @@ impl Resolver<'_, '_> { unused_imports: Default::default(), extern_crate_items: Default::default(), base_use_tree: None, - base_id: ast::DUMMY_NODE_ID, item_span: DUMMY_SP, }; // `use_items` is in crate DFS order, so diagnostics and side effects are unchanged. for item in use_items { - visitor.visit_item(item); + with_owner(&mut visitor, item.id, |visitor| visitor.visit_item(item)) } visitor.report_unused_extern_crate_items(maybe_unused_extern_crates); @@ -502,9 +506,8 @@ impl Resolver<'_, '_> { let test_module_span = if tcx.sess.is_test_crate() { None } else { - let parent_module = visitor.r.get_nearest_non_block_module( - visitor.r.owner_def_id(unused.use_tree_id).to_def_id(), - ); + let parent_module = + visitor.r.get_nearest_non_block_module(unused.use_tree_def_id.to_def_id()); match module_to_string(parent_module) { Some(module) if module == "test" diff --git a/compiler/rustc_resolve/src/def_collector.rs b/compiler/rustc_resolve/src/def_collector.rs index 4c8000c28f065..54c87dd98e0e4 100644 --- a/compiler/rustc_resolve/src/def_collector.rs +++ b/compiler/rustc_resolve/src/def_collector.rs @@ -204,7 +204,9 @@ impl<'a, 'ra, 'tcx> visit::Visitor<'a> for DefCollector<'a, 'ra, 'tcx> { ItemKind::GlobalAsm(..) => DefKind::GlobalAsm, ItemKind::Use(_) => { return self.with_owner(i.id, None, DefKind::Use, i.span, |this, feed| { - this.brg_visit_item(i, feed); + this.with_parent(feed.def_id(), |this| { + this.brg_visit_item(i, feed); + }) }); } ItemKind::MacCall(..) => { diff --git a/compiler/rustc_resolve/src/effective_visibilities.rs b/compiler/rustc_resolve/src/effective_visibilities.rs index 335abe38d9954..3fb75e1261da8 100644 --- a/compiler/rustc_resolve/src/effective_visibilities.rs +++ b/compiler/rustc_resolve/src/effective_visibilities.rs @@ -109,7 +109,12 @@ impl<'a, 'ra, 'tcx> EffectiveVisibilitiesVisitor<'a, 'ra, 'tcx> { for (decl, eff_vis) in visitor.import_effective_visibilities.iter() { let DeclKind::Import { import, .. } = decl.kind else { unreachable!() }; if let Some(def_id) = import.def_id() { - r.effective_visibilities.update_eff_vis(def_id, eff_vis, r.tcx) + r.effective_visibilities.update_eff_vis(def_id, eff_vis, r.tcx); + let root = r.owners[&import.root_id].def_id; + // The `unreachable_pub` lint also needs to know whether any of the nested entries are + // exported by this use statement. + // FIXME: We could compute this lazily in `unreachable_pub` directly, but this is less invasive. + r.effective_visibilities.update_eff_vis(root, eff_vis, r.tcx); } if decl.ambiguity.get().is_some() && eff_vis.is_public_at_level(Level::Reexported) { exported_ambiguities.insert(*decl); diff --git a/compiler/rustc_resolve/src/imports.rs b/compiler/rustc_resolve/src/imports.rs index 1cda9b9139028..82b178e78b91e 100644 --- a/compiler/rustc_resolve/src/imports.rs +++ b/compiler/rustc_resolve/src/imports.rs @@ -1650,7 +1650,12 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { // purposes it's good enough to just favor one over the other. self.per_ns_mut(|this, ns| { if let Some(binding) = bindings[ns].get().decl().map(|b| b.import_source()) { - this.owners.get_mut(&import_id).unwrap().import_res[ns] = Some(binding.res()); + this.owners + .get_mut(&import.root_id) + .unwrap() + .import_res + .entry(import_id) + .or_default()[ns] = Some(binding.res()); } }); diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index 9b879d9a443fa..3386c2f708ca3 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -850,6 +850,9 @@ struct LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { /// `use` injections are delayed for better placement and deduplication. use_injections: Vec>, + + /// All `use` and `extern crate` items, in the order in which they are visited. + use_items: Vec<&'ast Item>, } impl<'ra, 'tcx> AsRef> for LateResolutionVisitor<'_, '_, 'ra, 'tcx> { @@ -1575,6 +1578,7 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { in_func_body: false, lifetime_uses: Default::default(), use_injections: Vec::new(), + use_items: Vec::new(), } } @@ -3065,6 +3069,7 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { ), ItemKind::Use(use_tree) => { + self.use_items.push(item); let maybe_exported = match use_tree.kind { UseTreeKind::Simple(_) | UseTreeKind::Glob(_) => MaybeExported::Ok(item.id), UseTreeKind::Nested { .. } => MaybeExported::NestedUse(&item.vis), @@ -3110,7 +3115,7 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { ); } - ItemKind::ExternCrate(..) => {} + ItemKind::ExternCrate(..) => self.use_items.push(item), ItemKind::MacCall(_) | ItemKind::DelegationMac(..) => { panic!("unexpanded macro in resolve!") @@ -5720,13 +5725,11 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { /// Walks the whole crate in DFS order, visiting each item, counting the declared number of /// lifetime generic parameters and function parameters. Also collects all `use` and /// `extern crate` items so that `check_unused` doesn't need to walk the crate again. -struct ItemInfoCollector<'a, 'ast, 'ra, 'tcx> { +struct ItemInfoCollector<'a, 'ra, 'tcx> { r: &'a mut Resolver<'ra, 'tcx>, - /// All `use` and `extern crate` items, in the order in which they are visited. - use_items: Vec<&'ast Item>, } -impl ItemInfoCollector<'_, '_, '_, '_> { +impl ItemInfoCollector<'_, '_, '_> { fn collect_fn_info(&mut self, decl: &FnDecl, id: NodeId) { self.r .delegation_fn_sigs @@ -5760,8 +5763,8 @@ fn required_generic_args_suggestion(generics: &ast::Generics) -> Option if required.is_empty() { None } else { Some(format!("<{}>", required.join(", "))) } } -impl<'ast> Visitor<'ast> for ItemInfoCollector<'_, 'ast, '_, '_> { - fn visit_item(&mut self, item: &'ast Item) { +impl Visitor<'_> for ItemInfoCollector<'_, '_, '_> { + fn visit_item(&mut self, item: &Item) { if let Some(generics) = item.opt_generics() { let def_id = self.r.owner_def_id(item.id); let count = generics @@ -5785,16 +5788,14 @@ impl<'ast> Visitor<'ast> for ItemInfoCollector<'_, 'ast, '_, '_> { } } - ItemKind::Use(..) | ItemKind::ExternCrate(..) => { - self.use_items.push(item); - } - ItemKind::Mod(..) | ItemKind::Static(..) | ItemKind::ConstBlock(..) | ItemKind::MacroDef(..) | ItemKind::GlobalAsm(..) | ItemKind::MacCall(..) + | ItemKind::Use(..) + | ItemKind::ExternCrate(..) | ItemKind::DelegationMac(..) | ItemKind::TyAlias(..) | ItemKind::Const(..) @@ -5815,7 +5816,7 @@ impl<'ast> Visitor<'ast> for ItemInfoCollector<'_, 'ast, '_, '_> { visit::walk_item(self, item) } - fn visit_assoc_item(&mut self, item: &'ast AssocItem, ctxt: AssocCtxt) { + fn visit_assoc_item(&mut self, item: &AssocItem, ctxt: AssocCtxt) { if let AssocItemKind::Fn(Fn { sig, .. }) = &item.kind { self.collect_fn_info(&sig.decl, item.id); } @@ -5837,14 +5838,13 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { krate: &'ast Crate, ) -> (Vec<&'ast Item>, Vec>) { with_owner(self, CRATE_NODE_ID, |this| { - let mut info_collector = ItemInfoCollector { r: this, use_items: Vec::new() }; + let mut info_collector = ItemInfoCollector { r: this }; visit::walk_crate(&mut info_collector, krate); - let use_items = info_collector.use_items; let mut late_resolution_visitor = LateResolutionVisitor::new(this); late_resolution_visitor .resolve_doc_links(&krate.attrs, MaybeExported::Ok(CRATE_NODE_ID)); visit::walk_crate(&mut late_resolution_visitor, krate); - let LateResolutionVisitor { use_injections, diag_metadata, .. } = + let LateResolutionVisitor { use_injections, diag_metadata, use_items, .. } = late_resolution_visitor; for (id, span) in diag_metadata.unused_labels.iter() { this.lint_buffer.buffer_lint( diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs index baadbc644582b..adaee56749909 100644 --- a/compiler/rustc_resolve/src/lib.rs +++ b/compiler/rustc_resolve/src/lib.rs @@ -1663,7 +1663,12 @@ impl<'tcx> Resolver<'_, 'tcx> { /// Get the `DefId` of a child of the current owner fn local_def_id(&self, node: NodeId) -> LocalDefId { - self.opt_local_def_id(node).unwrap_or_else(|| panic!("no entry for node id: `{node:?}`")) + self.opt_local_def_id(node).unwrap_or_else(|| { + panic!( + "no entry for node id `{node:?}` in owner {:?}, available: {:#?}", + self.current_owner.def_id, self.current_owner.node_id_to_def_id + ) + }) } /// Adds a definition with a parent definition. diff --git a/src/librustdoc/clean/inline.rs b/src/librustdoc/clean/inline.rs index d7fbe64c30771..aff93addd8441 100644 --- a/src/librustdoc/clean/inline.rs +++ b/src/librustdoc/clean/inline.rs @@ -7,7 +7,7 @@ use rustc_data_structures::fx::FxHashSet; use rustc_data_structures::thin_vec::{ThinVec, thin_vec}; use rustc_hir::def::{DefKind, MacroKinds, Res}; use rustc_hir::def_id::{DefId, DefIdSet, LocalDefId, LocalModId}; -use rustc_hir::{self as hir, Mutability, find_attr}; +use rustc_hir::{self as hir, HirId, Mutability, find_attr}; use rustc_metadata::creader::{CStore, LoadedMacro}; use rustc_middle::ty::fast_reject::SimplifiedType; use rustc_middle::ty::{self, TyCtxt}; @@ -184,7 +184,8 @@ pub(crate) fn try_inline_glob( current_mod: LocalModId, visited: &mut DefIdSet, inlined_names: &mut FxHashSet<(ItemType, Symbol)>, - import: &hir::Item<'_>, + import_id: LocalDefId, + import_hir_id: HirId, ) -> Option> { let did = res.opt_def_id()?; if did.is_local() { @@ -203,7 +204,7 @@ pub(crate) fn try_inline_glob( .filter_map(|child| child.res.opt_def_id()) .filter(|&def_id| !cx.tcx.is_doc_hidden(def_id)) .collect(); - let attrs = cx.tcx.hir_attrs(import.hir_id()); + let attrs = cx.tcx.hir_attrs(import_hir_id); let mut items = build_module_items( cx, did, @@ -211,7 +212,7 @@ pub(crate) fn try_inline_glob( visited, inlined_names, Some(&reexports), - Some((attrs, Some(import.owner_id.def_id))), + Some((attrs, Some(import_id))), ); items.retain(|item| { if let Some(name) = item.name { diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index 2b1a37cbcda30..f91114c2f2502 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -38,12 +38,11 @@ use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap, FxIndexSet, In use rustc_data_structures::thin_vec::ThinVec; use rustc_errors::codes::*; use rustc_errors::{FatalError, struct_span_code_err}; -use rustc_hir as hir; use rustc_hir::attrs::lang_items::LangItem; use rustc_hir::attrs::{AttributeKind, DocAttribute, DocInline}; -use rustc_hir::def::{CtorKind, DefKind, MacroKinds, Res}; +use rustc_hir::def::{CtorKind, DefKind, MacroKinds, PerNS, Res}; use rustc_hir::def_id::{DefId, DefIdMap, DefIdSet, LOCAL_CRATE, LocalDefId}; -use rustc_hir::{PredicateOrigin, find_attr}; +use rustc_hir::{self as hir, HirId, PredicateOrigin, find_attr}; use rustc_hir_analysis::{lower_const_arg_for_rustdoc, lower_ty}; use rustc_middle::middle::resolve::Reexport; use rustc_middle::middle::resolve_bound_vars as rbv; @@ -65,6 +64,14 @@ use crate::core::DocContext; use crate::formats::item_type::ItemType; use crate::visit_ast; +#[derive(Copy, Clone, Debug)] +enum ImportLowerMode { + Everything, + GlobsOnly, + NoGlobs, +} + +#[instrument(level = "trace", skip(cx))] pub(crate) fn clean_doc_module<'tcx>( doc: &visit_ast::Module<'tcx>, cx: &mut DocContext<'tcx>, @@ -102,9 +109,6 @@ pub(crate) fn clean_doc_module<'tcx>( items.extend(doc.items.values().flat_map( |visit_ast::ItemEntry { item, renamed, import_ids }| { // First, lower everything other than glob imports. - if matches!(item.kind, hir::ItemKind::Use(_, hir::UseKind::Glob)) { - return Vec::new(); - } let v = clean_maybe_renamed_item(cx, item, *renamed, import_ids); for item in &v { if let Some(name) = item.name @@ -120,18 +124,26 @@ pub(crate) fn clean_doc_module<'tcx>( |((_, renamed), visit_ast::InlinedForeign { res, import_id })| { let Some(def_id) = res.opt_def_id() else { return Vec::new() }; let name = renamed.unwrap_or_else(|| cx.tcx.item_name(def_id)); - let import = cx.tcx.hir_expect_item(*import_id); - match import.kind { - hir::ItemKind::Use(path, kind) => { - let hir::UsePath { segments, span, .. } = *path; - let path = hir::Path { segments, res: *res, span }; - clean_use_statement_inner( - import, + let import = cx.tcx.hir_node_by_def_id(*import_id); + match import { + hir::Node::Item(hir::Item { kind: hir::ItemKind::Use(tree), .. }) + | hir::Node::NestedUseTree(tree) => { + let hir::UsePath { segments, span, .. } = *tree.prefix; + let path = hir::UsePath { + segments, + res: PerNS { value_ns: Some(*res), type_ns: None, macro_ns: None }, + span, + }; + clean_use_statement( + *import_id, + cx.tcx.local_def_id_to_hir_id(*import_id), + tree.prefix.span, Some(name), &path, - kind, + tree.kind, cx, &mut Default::default(), + ImportLowerMode::Everything, ) } _ => unreachable!(), @@ -141,8 +153,18 @@ pub(crate) fn clean_doc_module<'tcx>( items.extend(doc.items.values().flat_map( |visit_ast::ItemEntry { item, renamed, import_ids: _ }| { // Now we actually lower the imports, skipping everything else. - if let hir::ItemKind::Use(path, hir::UseKind::Glob) = item.kind { - clean_use_statement(item, *renamed, path, hir::UseKind::Glob, cx, &mut inserted) + if let hir::ItemKind::Use(tree) = item.kind { + clean_use_statement( + item.owner_id.def_id, + item.hir_id(), + item.span, + *renamed, + tree.prefix, + tree.kind, + cx, + &mut inserted, + ImportLowerMode::GlobsOnly, + ) } else { // skip everything else Vec::new() @@ -179,10 +201,10 @@ pub(crate) fn clean_doc_module<'tcx>( } fn is_glob_import(tcx: TyCtxt<'_>, import_id: LocalDefId) -> bool { - if let hir::Node::Item(item) = tcx.hir_node_by_def_id(import_id) - && let hir::ItemKind::Use(_, use_kind) = item.kind + if let hir::Node::Item(hir::Item { kind: hir::ItemKind::Use(tree), .. }) + | hir::Node::NestedUseTree(tree) = tcx.hir_node_by_def_id(import_id) { - use_kind == hir::UseKind::Glob + matches!(tree.kind, hir::UseKind::Glob) } else { false } @@ -1680,10 +1702,32 @@ fn first_non_private<'tcx>( 'reexps: for reexp in child.reexport_chain.iter() { if let Some(use_def_id) = reexp.id() && let Some(local_use_def_id) = use_def_id.as_local() - && let hir::Node::Item(item) = cx.tcx.hir_node_by_def_id(local_use_def_id) - && let hir::ItemKind::Use(path, hir::UseKind::Single(_)) = item.kind + && let hir::Node::Item(hir::Item { kind: hir::ItemKind::Use(tree), .. }) + | hir::Node::NestedUseTree(tree) = + cx.tcx.hir_node_by_def_id(local_use_def_id) + && let hir::UseKind::Single(_) = tree.kind { - for res in path.res.present_items() { + let mut segments = tree.prefix.segments.to_vec(); + let mut span = tree.prefix.span; + let mut parent = cx.tcx.local_parent(local_use_def_id); + loop { + match cx.tcx.hir_node_by_def_id(parent) { + hir::Node::Item(hir::Item { + kind: hir::ItemKind::Use(tree), .. + }) => { + span = tree.prefix.span.to(span); + segments.splice(0..0, tree.prefix.segments.iter().copied()); + break; + } + hir::Node::NestedUseTree(tree) => { + span = tree.prefix.span.to(span); + segments.splice(0..0, tree.prefix.segments.iter().copied()); + parent = cx.tcx.local_parent(local_use_def_id); + } + _ => break, + } + } + for res in tree.prefix.res.present_items() { if let Res::Def(DefKind::Ctor(..), _) | Res::SelfCtor(..) = res { continue; } @@ -1696,7 +1740,7 @@ fn first_non_private<'tcx>( { break 'reexps; } - last_path_res = Some((path, res)); + last_path_res = Some((segments, span, res)); continue 'reexps; } } @@ -1707,13 +1751,8 @@ fn first_non_private<'tcx>( // // 1. We found a public reexport. // 2. We didn't find a public reexport so it's the "end type" path. - if let Some((new_path, _)) = last_path_res { - return Some(first_non_private_clean_path( - cx, - path, - new_path.segments, - new_path.span, - )); + if let Some((segments, span, _)) = last_path_res { + return Some(first_non_private_clean_path(cx, path, &segments, span)); } // If `last_path_res` is `None`, it can mean two things: // @@ -2888,14 +2927,17 @@ fn clean_maybe_renamed_item<'tcx>( // generate an impl placeholder and not a "real" impl item. return clean_impl(impl_, item.owner_id.def_id, cx, renamed.is_some()); } - ItemKind::Use(path, kind) => { + ItemKind::Use(tree) => { return clean_use_statement( - item, + item.owner_id.def_id, + item.hir_id(), + item.span, get_name(cx.tcx, item, renamed), - path, - kind, + tree.prefix, + tree.kind, cx, &mut FxHashSet::default(), + ImportLowerMode::NoGlobs, ); } _ => {} @@ -3143,27 +3185,73 @@ fn clean_extern_crate<'tcx>( } fn clean_use_statement<'tcx>( - import: &hir::Item<'tcx>, + import_def_id: LocalDefId, + import_hir_id: HirId, + import_span: rustc_span::Span, name: Option, - path: &hir::UsePath<'tcx>, - kind: hir::UseKind, + path: &hir::UsePath<'_>, + kind: hir::UseKind<'tcx>, cx: &mut DocContext<'tcx>, inlined_names: &mut FxHashSet<(ItemType, Symbol)>, + mode: ImportLowerMode, ) -> Vec { - let mut items = Vec::new(); - let hir::UsePath { segments, ref res, span } = *path; - for res in res.present_items() { - let path = hir::Path { segments, res, span }; - items.append(&mut clean_use_statement_inner(import, name, &path, kind, cx, inlined_names)); - } - items + let name = match (kind, mode) { + (hir::UseKind::Single(n), ImportLowerMode::Everything | ImportLowerMode::NoGlobs) => { + name.or(Some(n.name)) + } + (hir::UseKind::Glob, ImportLowerMode::NoGlobs) + | (hir::UseKind::Single(_), ImportLowerMode::GlobsOnly) => return vec![], + (hir::UseKind::Glob, ImportLowerMode::Everything | ImportLowerMode::GlobsOnly) => name, + (hir::UseKind::Nested { items }, _) => { + let mut all = vec![]; + for (tree, hir_id, def_id) in items { + let mut segments = path.segments.to_vec(); + segments.extend(tree.prefix.segments.iter()); + let path = hir::UsePath { + segments: &segments, + res: tree.prefix.res, + span: path.span.to(tree.prefix.span), + }; + all.append(&mut clean_use_statement( + *def_id, + *hir_id, + tree.prefix.span, + None, + &path, + tree.kind, + cx, + inlined_names, + mode, + )); + } + return all; + } + }; + path.res + .present_items() + .flat_map(|res| { + let path = hir::Path { span: path.span, res, segments: path.segments }; + clean_use_statement_leaf( + import_def_id, + import_hir_id, + import_span, + name, + &path, + kind, + cx, + inlined_names, + ) + }) + .collect() } -fn clean_use_statement_inner<'tcx>( - import: &hir::Item<'tcx>, +fn clean_use_statement_leaf<'tcx>( + import_id: LocalDefId, + import_hir_id: HirId, + import_span: rustc_span::Span, name: Option, path: &hir::Path<'_>, - kind: hir::UseKind, + kind: hir::UseKind<'tcx>, cx: &mut DocContext<'tcx>, inlined_names: &mut FxHashSet<(ItemType, Symbol)>, ) -> Vec { @@ -3173,20 +3261,20 @@ fn clean_use_statement_inner<'tcx>( // We need this comparison because some imports (for std types for example) // are "inserted" as well but directly by the compiler and they should not be // taken into account. - if import.span.ctxt().outer_expn_data().kind == ExpnKind::AstPass(AstPass::StdImports) { + if import_span.ctxt().outer_expn_data().kind == ExpnKind::AstPass(AstPass::StdImports) { return Vec::new(); } - let visibility = cx.tcx.visibility(import.owner_id); - let attrs = cx.tcx.hir_attrs(import.hir_id()); + let visibility = cx.tcx.visibility(import_id); + let attrs = cx.tcx.hir_attrs(import_hir_id); let inline_attr = find_attr!( attrs, Doc(d) if d.inline.first().is_some_and(|(i, _)| *i == DocInline::Inline) => d ) .and_then(|d| d.inline.first()); let pub_underscore = visibility.is_public() && name == Some(kw::Underscore); - let current_mod = cx.tcx.parent_module_from_def_id(import.owner_id.def_id); - let import_def_id = import.owner_id.def_id; + let current_mod = cx.tcx.parent_module_from_def_id(import_id); + let import_def_id = import_id; // The parent of the module in which this import resides. This // is the same as `current_mod` if that's already the top @@ -3208,7 +3296,7 @@ fn clean_use_statement_inner<'tcx>( E0780, "anonymous imports cannot be inlined" ) - .with_span_label(import.span, "anonymous import") + .with_span_label(import_span, "anonymous import") .emit(); } @@ -3228,7 +3316,7 @@ fn clean_use_statement_inner<'tcx>( // Also check whether imports were asked to be inlined, in case we're trying to re-export a // crate in Rust 2018+ let path = clean_path(path, cx); - let inner = if kind == hir::UseKind::Glob { + let inner = if matches!(kind, hir::UseKind::Glob) { if !denied { let mut visited = DefIdSet::default(); if let Some(items) = inline::try_inline_glob( @@ -3237,7 +3325,8 @@ fn clean_use_statement_inner<'tcx>( current_mod, &mut visited, inlined_names, - import, + import_id, + import_hir_id, ) { return items; } diff --git a/src/librustdoc/passes/propagate_stability.rs b/src/librustdoc/passes/propagate_stability.rs index 9afde1e6195e7..cff1e7fa46dbb 100644 --- a/src/librustdoc/passes/propagate_stability.rs +++ b/src/librustdoc/passes/propagate_stability.rs @@ -37,9 +37,9 @@ impl DocFolder for StabilityPropagator<'_, '_> { matches!( self.cx.tcx.hir_node(hir_id), rustc_hir::Node::Item(rustc_hir::Item { - kind: rustc_hir::ItemKind::Use(_, rustc_hir::UseKind::Glob), + kind: rustc_hir::ItemKind::Use(tree), .. - }) + }) | rustc_hir::Node::NestedUseTree(tree) if matches!(tree.kind, rustc_hir::UseKind::Glob) ) }); let own_stability = if let Some(item_stab) = item_stability diff --git a/src/librustdoc/visit_ast.rs b/src/librustdoc/visit_ast.rs index ae5a76545eefb..ab9fb79074489 100644 --- a/src/librustdoc/visit_ast.rs +++ b/src/librustdoc/visit_ast.rs @@ -5,12 +5,11 @@ use std::mem; use rustc_ast::attr::AttributeExt; use rustc_data_structures::fx::{FxHashSet, FxIndexMap}; -use rustc_hir as hir; use rustc_hir::attrs::DocInline; use rustc_hir::def::{DefKind, MacroKinds, Res}; use rustc_hir::def_id::{DefId, DefIdMap, LocalDefId, LocalDefIdSet}; use rustc_hir::intravisit::{Visitor, walk_body, walk_item}; -use rustc_hir::{Node, find_attr}; +use rustc_hir::{self as hir, HirId, Node, find_attr}; use rustc_middle::hir::nested_filter; use rustc_middle::ty::TyCtxt; use rustc_span::Span; @@ -119,6 +118,16 @@ fn def_id_to_path(tcx: TyCtxt<'_>, did: DefId) -> Vec { std::iter::once(crate_name).chain(relative).collect() } +#[derive(Copy, Clone)] +enum GlobMode { + // Globs and everything else + Everything, + // Skip all globs + NoGlob, + // Skip all items except for globs + Only, +} + pub(crate) struct RustdocVisitor<'a, 'tcx> { cx: &'a mut core::DocContext<'tcx>, view_item_stack: LocalDefIdSet, @@ -129,6 +138,7 @@ pub(crate) struct RustdocVisitor<'a, 'tcx> { modules: Vec>, is_importable_from_parent: bool, inside_body: bool, + glob_mode: GlobMode, } impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> { @@ -153,6 +163,7 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> { modules: vec![om], is_importable_from_parent: true, inside_body: false, + glob_mode: GlobMode::Everything, } } @@ -210,21 +221,22 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> { // Reimplementation of `walk_mod` because we need to do it in two passes (explanations in // the second loop): + let old_glob_mode = mem::replace(&mut self.glob_mode, GlobMode::NoGlob); for &i in m.item_ids { let item = self.cx.tcx.hir_item(i); - if !matches!(item.kind, hir::ItemKind::Use(_, hir::UseKind::Glob)) { - self.visit_item(item); - } + self.visit_item(item); } + self.glob_mode = GlobMode::Only; for &i in m.item_ids { let item = self.cx.tcx.hir_item(i); // To match the way import precedence works, visit glob imports last. // Later passes in rustdoc will de-duplicate by name and kind, so if glob- // imported items appear last, then they'll be the ones that get discarded. - if matches!(item.kind, hir::ItemKind::Use(_, hir::UseKind::Glob)) { - self.visit_item(item); + if matches!(item.kind, hir::ItemKind::Use(..)) { + self.visit_item_inner(item, None, None); } } + self.glob_mode = old_glob_mode; self.inside_public_path = orig_inside_public_path; debug!("Leaving module {m:?}"); } @@ -425,21 +437,14 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> { renamed = None; } let key = (item.owner_id.def_id, renamed); - if let Some(import_id) = import_id { - self.modules - .last_mut() - .unwrap() - .items - .entry(key) - .and_modify(|v| v.import_ids.push(import_id)) - .or_insert_with(|| ItemEntry { item, renamed, import_ids: vec![import_id] }); - } else { - self.modules - .last_mut() - .unwrap() - .items - .insert(key, ItemEntry { item, renamed, import_ids: Vec::new() }); - } + self.modules + .last_mut() + .unwrap() + .items + .entry(key) + .or_insert_with(|| ItemEntry { item, renamed, import_ids: vec![] }) + .import_ids + .extend(import_id); } } @@ -486,53 +491,16 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> { // If we're inlining, skip private items. _ if self.inlining && !is_pub => {} hir::ItemKind::GlobalAsm { .. } => {} - hir::ItemKind::Use(_, hir::UseKind::ListStem) => {} - hir::ItemKind::Use(path, kind) => { - for res in path.res.present_items() { - // Struct and variant constructors and proc macro stubs always show up alongside - // their definitions, we've already processed them so just discard these. - if should_ignore_res(res) { - continue; - } - - let attrs = tcx.hir_attrs(tcx.local_def_id_to_hir_id(item.owner_id.def_id)); - - // If there was a private module in the current path then don't bother inlining - // anything as it will probably be stripped anyway. - if is_pub && self.inside_public_path { - let please_inline = if let Some(res_did) = res.opt_def_id() - && matches!(tcx.def_kind(res_did), DefKind::Macro(MacroKinds::BANG)) - { - crate::clean::macro_reexport_is_inline( - tcx, - item.owner_id.def_id, - res_did, - ) - } else { - find_attr!( - attrs, - Doc(d) - if d.inline.first().is_some_and(|(inline, _)| *inline == DocInline::Inline) - ) - }; - let ident = match kind { - hir::UseKind::Single(ident) => Some(ident.name), - hir::UseKind::Glob => None, - hir::UseKind::ListStem => unreachable!(), - }; - if self.maybe_inline_local( - item.owner_id.def_id, - res, - ident, - please_inline, - import_id, - ) { - debug!("Inlining {:?}", item.owner_id.def_id); - continue; - } - } - self.add_to_current_mod(item, renamed, import_id); - } + hir::ItemKind::Use(ref tree) => { + self.visit_use_inner( + tree, + item.owner_id.def_id, + item.hir_id(), + is_pub, + import_id, + item, + renamed, + ); } hir::ItemKind::Macro(_, macro_def, _) => { // `#[macro_export] macro_rules!` items are handled separately in `visit()`, @@ -616,6 +584,65 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> { let last = self.modules.pop().unwrap(); self.modules.last_mut().unwrap().mods.push(last); } + + fn visit_use_inner( + &mut self, + tree: &hir::UseTree<'tcx>, + def_id: LocalDefId, + hir_id: HirId, + is_pub: bool, + import_id: Option, + item: &'tcx hir::Item<'tcx>, + renamed: Option, + ) { + let tcx = self.cx.tcx; + for res in tree.prefix.res.present_items() { + // Struct and variant constructors and proc macro stubs always show up alongside + // their definitions, we've already processed them so just discard these. + if should_ignore_res(res) { + continue; + } + + let attrs = tcx.hir_attrs(hir_id); + + // If there was a private module in the current path then don't bother inlining + // anything as it will probably be stripped anyway. + if is_pub && self.inside_public_path { + let please_inline = if let Some(res_did) = res.opt_def_id() + && matches!(tcx.def_kind(res_did), DefKind::Macro(MacroKinds::BANG)) + { + crate::clean::macro_reexport_is_inline(tcx, def_id, res_did) + } else { + find_attr!( + attrs, + Doc(d) + if d.inline.first().is_some_and(|(inline, _)| *inline == DocInline::Inline) + ) + }; + let ident = match (tree.kind, self.glob_mode) { + (hir::UseKind::Single(ident), GlobMode::NoGlob | GlobMode::Everything) => { + Some(ident.name) + } + (hir::UseKind::Glob, GlobMode::Only | GlobMode::Everything) => None, + (hir::UseKind::Single(_), GlobMode::Only) + | (hir::UseKind::Glob, GlobMode::NoGlob) => continue, + (hir::UseKind::Nested { items }, _) => { + for (tree, hir_id, def_id) in items { + self.visit_use_inner( + tree, *def_id, *hir_id, is_pub, import_id, item, renamed, + ); + } + continue; + } + }; + if self.maybe_inline_local(def_id, res, ident, please_inline, import_id) { + debug!("Inlining {:?}", def_id); + continue; + } + } + self.add_to_current_mod(item, renamed, import_id); + } + } } // We need to implement this visitor so it'll go everywhere and retrieve items we're interested in @@ -646,7 +673,7 @@ impl<'tcx> Visitor<'tcx> for RustdocVisitor<'_, 'tcx> { // Handled in `visit_item_inner` } - fn visit_use(&mut self, _: &hir::UsePath<'tcx>, _: hir::HirId) { + fn visit_use(&mut self, _: &hir::UseTree<'tcx>, _: hir::HirId) { // Handled in `visit_item_inner` } diff --git a/src/tools/clippy/clippy_lints/src/disallowed_types.rs b/src/tools/clippy/clippy_lints/src/disallowed_types.rs index 2236e613b1472..fb2e70844865d 100644 --- a/src/tools/clippy/clippy_lints/src/disallowed_types.rs +++ b/src/tools/clippy/clippy_lints/src/disallowed_types.rs @@ -5,7 +5,7 @@ use clippy_utils::paths::PathNS; use rustc_data_structures::fx::FxHashMap; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::DefIdMap; -use rustc_hir::{AmbigArg, Item, ItemKind, PolyTraitRef, PrimTy, Ty, TyKind, UseKind}; +use rustc_hir::{AmbigArg, Item, ItemKind, PolyTraitRef, PrimTy, Ty, TyKind, UseKind, UseTree}; use rustc_lint::{LateContext, LateLintPass, impl_lint_pass}; use rustc_middle::ty::TyCtxt; use rustc_span::Span; @@ -107,8 +107,11 @@ pub fn def_kind_predicate(def_kind: DefKind) -> bool { impl<'tcx> LateLintPass<'tcx> for DisallowedTypes { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) { - if let ItemKind::Use(path, UseKind::Single(_)) = &item.kind - && let Some(res) = path.res.type_ns + if let ItemKind::Use(UseTree { + prefix, + kind: UseKind::Single(_), + }) = &item.kind + && let Some(res) = prefix.res.type_ns { self.check_res_emit(cx, &res, item.span); } diff --git a/src/tools/clippy/clippy_lints/src/ifs/branches_sharing_code.rs b/src/tools/clippy/clippy_lints/src/ifs/branches_sharing_code.rs index ff4683349ad88..de361e0d88d3c 100644 --- a/src/tools/clippy/clippy_lints/src/ifs/branches_sharing_code.rs +++ b/src/tools/clippy/clippy_lints/src/ifs/branches_sharing_code.rs @@ -11,7 +11,7 @@ use core::iter; use core::ops::ControlFlow; use rustc_errors::Applicability; use rustc_hir::{ - Arm, Block, Expr, ExprKind, HirId, HirIdSet, ItemKind, LetStmt, Node, Stmt, StmtKind, UseKind, intravisit, + Arm, Block, Expr, ExprKind, HirId, HirIdSet, ItemKind, LetStmt, Node, Stmt, StmtKind, UseKind, UseTree, intravisit, }; use rustc_lint::LateContext; use rustc_span::hygiene::walk_chain; @@ -259,7 +259,10 @@ fn eq_binding_names(cx: &LateContext<'_>, s: &Stmt<'_>, names: &[(HirId, Symbol) | ItemKind::Const(ident, ..) | ItemKind::Fn { ident, .. } | ItemKind::TyAlias(ident, ..) - | ItemKind::Use(_, UseKind::Single(ident)) + | ItemKind::Use(UseTree { + kind: UseKind::Single(ident), + .. + }) | ItemKind::Mod(ident, _) = item.kind => { *name == ident.name diff --git a/src/tools/clippy/clippy_lints/src/item_name_repetitions.rs b/src/tools/clippy/clippy_lints/src/item_name_repetitions.rs index 8c604aadc811e..67f9a51cd1c1f 100644 --- a/src/tools/clippy/clippy_lints/src/item_name_repetitions.rs +++ b/src/tools/clippy/clippy_lints/src/item_name_repetitions.rs @@ -3,7 +3,7 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_help, span_lint_hir}; use clippy_utils::str_utils::{camel_case_split, count_match_end, count_match_start, to_camel_case, to_snake_case}; use clippy_utils::{is_bool, is_from_proc_macro}; use rustc_data_structures::fx::FxHashSet; -use rustc_hir::{Body, EnumDef, FieldDef, Item, ItemKind, QPath, TyKind, UseKind, Variant, VariantData}; +use rustc_hir::{Body, EnumDef, FieldDef, Item, ItemKind, QPath, TyKind, UseKind, UseTree, Variant, VariantData}; use rustc_lint::{LateContext, LateLintPass, impl_lint_pass}; use rustc_span::symbol::Symbol; @@ -531,7 +531,10 @@ impl LateLintPass<'_> for ItemNameRepetitions { | ItemKind::TraitAlias(_, ident, ..) | ItemKind::TyAlias(ident, ..) | ItemKind::Union(ident, ..) - | ItemKind::Use(_, UseKind::Single(ident)) => ident, + | ItemKind::Use(UseTree { + kind: UseKind::Single(ident), + .. + }) => ident, ItemKind::ForeignMod { .. } | ItemKind::GlobalAsm { .. } diff --git a/src/tools/clippy/clippy_lints/src/legacy_numeric_constants.rs b/src/tools/clippy/clippy_lints/src/legacy_numeric_constants.rs index 7f83149a222c0..31959089610ed 100644 --- a/src/tools/clippy/clippy_lints/src/legacy_numeric_constants.rs +++ b/src/tools/clippy/clippy_lints/src/legacy_numeric_constants.rs @@ -5,8 +5,7 @@ use clippy_utils::source::SpanExt as _; use clippy_utils::{is_from_proc_macro, sym}; use hir::def_id::DefId; use rustc_errors::Applicability; -use rustc_hir as hir; -use rustc_hir::{ExprKind, Item, ItemKind, QPath, UseKind}; +use rustc_hir::{self as hir, ExprKind, Item, ItemKind, QPath, UseKind, UseTree}; use rustc_lint::{LateContext, LateLintPass, LintContext as _, impl_lint_pass}; use rustc_span::Symbol; use rustc_span::symbol::kw; @@ -44,18 +43,26 @@ impl LegacyNumericConstants { pub fn new(conf: &'static Conf) -> Self { Self { msrv: conf.msrv.into() } } -} -impl<'tcx> LateLintPass<'tcx> for LegacyNumericConstants { - fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) { + fn check_use_tree(&mut self, cx: &LateContext<'_>, tree: &UseTree<'_>) { + match tree.kind { + UseKind::Single(_) | UseKind::Glob => {}, + UseKind::Nested { items } => { + for (tree, ..) in items { + self.check_use_tree(cx, tree); + } + return; + }, + } + + let prefix = tree.prefix; // Integer modules are "TBD" deprecated, and the contents are too, // so lint on the `use` statement directly. - if let ItemKind::Use(path, kind @ (UseKind::Single(_) | UseKind::Glob)) = item.kind - && !item.span.in_external_macro(cx.sess().source_map()) - // use `present_items` because it could be in either type_ns or value_ns - && let Some(res) = path.res.present_items().next() - && let Some(def_id) = res.opt_def_id() - && self.msrv.meets(cx, msrvs::NUMERIC_ASSOCIATED_CONSTANTS) + if !tree.prefix.span.in_external_macro(cx.sess().source_map()) + // use `present_items` because it could be in either type_ns or value_ns + && let Some(res) = prefix.res.present_items().next() + && let Some(def_id) = res.opt_def_id() + && self.msrv.meets(cx, msrvs::NUMERIC_ASSOCIATED_CONSTANTS) { let module = if is_integer_module(cx, def_id) { true @@ -68,14 +75,14 @@ impl<'tcx> LateLintPass<'tcx> for LegacyNumericConstants { span_lint_and_then( cx, LEGACY_NUMERIC_CONSTANTS, - path.span, + prefix.span, if module { "importing legacy numeric constants" } else { "importing a legacy numeric constant" }, |diag| { - if let UseKind::Single(ident) = kind + if let UseKind::Single(ident) = tree.kind && ident.name == kw::Underscore { diag.help("remove this import"); @@ -85,7 +92,7 @@ impl<'tcx> LateLintPass<'tcx> for LegacyNumericConstants { let def_path = cx.get_def_path(def_id); if module && let [.., module_name] = &*def_path { - if kind == UseKind::Glob { + if matches!(tree.kind, UseKind::Glob) { diag.help(format!("remove this import and use associated constants `{module_name}::` from the primitive type instead")); } else { diag.help("remove this import").note(format!( @@ -94,13 +101,21 @@ impl<'tcx> LateLintPass<'tcx> for LegacyNumericConstants { } } else if let [.., module_name, name] = &*def_path { diag.help( - format!("remove this import and use the associated constant `{module_name}::{name}` from the primitive type instead") - ); + format!("remove this import and use the associated constant `{module_name}::{name}` from the primitive type instead") + ); } }, ); } } +} + +impl<'tcx> LateLintPass<'tcx> for LegacyNumericConstants { + fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) { + if let ItemKind::Use(tree) = &item.kind { + self.check_use_tree(cx, tree); + } + } fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx rustc_hir::Expr<'tcx>) { // `std::::` check diff --git a/src/tools/clippy/clippy_lints/src/macro_use.rs b/src/tools/clippy/clippy_lints/src/macro_use.rs index af33d1f7a8aa5..0644337ed5247 100644 --- a/src/tools/clippy/clippy_lints/src/macro_use.rs +++ b/src/tools/clippy/clippy_lints/src/macro_use.rs @@ -3,7 +3,7 @@ use clippy_utils::source::snippet; use rustc_data_structures::fx::FxHashSet; use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; -use rustc_hir::{self as hir, AmbigArg, find_attr}; +use rustc_hir::{self as hir, AmbigArg, UseTree, find_attr}; use rustc_lint::{LateContext, LateLintPass, LintContext as _, impl_lint_pass}; use rustc_span::Span; use rustc_span::edition::Edition; @@ -95,11 +95,11 @@ impl MacroUseImports { impl LateLintPass<'_> for MacroUseImports { fn check_item(&mut self, cx: &LateContext<'_>, item: &hir::Item<'_>) { if cx.sess().opts.edition >= Edition::Edition2018 - && let hir::ItemKind::Use(path, _kind) = &item.kind + && let hir::ItemKind::Use(UseTree { prefix, .. }) = &item.kind && let hir_id = item.hir_id() && let attrs = cx.tcx.hir_attrs(hir_id) && let Some(mac_attr_span) = find_attr!(attrs, MacroUse {span, ..} => *span) - && let Some(Res::Def(DefKind::Mod, id)) = path.res.type_ns + && let Some(Res::Def(DefKind::Mod, id)) = prefix.res.type_ns && !id.is_local() { for kid in cx.tcx.module_children(id) { diff --git a/src/tools/clippy/clippy_lints/src/min_ident_chars.rs b/src/tools/clippy/clippy_lints/src/min_ident_chars.rs index e5a292806259d..ee546528020ac 100644 --- a/src/tools/clippy/clippy_lints/src/min_ident_chars.rs +++ b/src/tools/clippy/clippy_lints/src/min_ident_chars.rs @@ -7,7 +7,7 @@ use rustc_data_structures::fx::FxHashSet; use rustc_errors::pluralize; use rustc_hir::{ FieldDef, HirId, ImplItem, ImplItemImplKind, ImplItemKind, Item, ItemKind, Node, Pat, PatKind, TraitFn, TraitItem, - TraitItemKind, UseKind, Variant, + TraitItemKind, UseKind, UseTree, Variant, }; use rustc_lint::{LateContext, LateLintPass, impl_lint_pass}; use rustc_span::{Ident, Symbol}; @@ -112,6 +112,24 @@ impl MinIdentChars { }); } } + + fn check_tree(&self, cx: &LateContext<'_>, tree: &UseTree<'_>) { + match tree.kind { + UseKind::Single(ident) => { + if tree.prefix.segments.last().is_some_and(|p| p.ident.span != ident.span) + && let Some(missing) = self.check_sym(ident.name) + { + self.emit(cx, ident, missing); + } + }, + UseKind::Glob => {}, + UseKind::Nested { items } => { + for (tree, _, _) in items { + self.check_tree(cx, tree); + } + }, + } + } } impl LateLintPass<'_> for MinIdentChars { @@ -132,17 +150,15 @@ impl LateLintPass<'_> for MinIdentChars { | ItemKind::TraitAlias(_, ident, ..) | ItemKind::TyAlias(ident, ..) | ItemKind::Union(ident, ..) => ident, - ItemKind::Use(path, UseKind::Single(ident)) - if path.segments.last().is_some_and(|p| p.ident.span != ident.span) => - { - ident + ItemKind::Use(ref tree) => { + self.check_tree(cx, tree); + return; }, ItemKind::ExternCrate(..) | ItemKind::ForeignMod { .. } | ItemKind::GlobalAsm { .. } | ItemKind::Impl(_) - | ItemKind::Use(..) | ItemKind::TestBinderConstraints { .. } => return, }; if let Some(missing) = self.check_sym(ident.name) diff --git a/src/tools/clippy/clippy_lints/src/missing_enforced_import_rename.rs b/src/tools/clippy/clippy_lints/src/missing_enforced_import_rename.rs index bb40f50bef098..aa4892280b007 100644 --- a/src/tools/clippy/clippy_lints/src/missing_enforced_import_rename.rs +++ b/src/tools/clippy/clippy_lints/src/missing_enforced_import_rename.rs @@ -5,7 +5,7 @@ use clippy_utils::source::SpanExt as _; use rustc_errors::Applicability; use rustc_hir::def::Res; use rustc_hir::def_id::DefIdMap; -use rustc_hir::{Item, ItemKind, UseKind}; +use rustc_hir::{Item, ItemKind, UseKind, UseTree}; use rustc_lint::{LateContext, LateLintPass, LintContext as _, impl_lint_pass}; use rustc_middle::ty::TyCtxt; use rustc_span::Symbol; @@ -66,41 +66,55 @@ impl ImportRename { .collect(), } } + + fn check_use_tree(&mut self, cx: &LateContext<'_>, tree: &UseTree<'_>) { + let hi = match tree.kind { + UseKind::Single(ident) => ident.span.hi(), + UseKind::Glob => return, + UseKind::Nested { items } => { + for (tree, ..) in items { + self.check_use_tree(cx, tree); + } + return; + }, + }; + // use `present_items` because it could be in any of type_ns, value_ns, macro_ns + for res in tree.prefix.res.present_items() { + if let Res::Def(_, id) = res + && let Some(name) = self.renames.get(&id) + // Remove semicolon since it is not present for nested imports + && let span_without_semi = cx.sess().source_map().span_until_char(tree.prefix.span.with_hi(hi), ';') + && let Some(snip) = span_without_semi.get_text(cx) + && let Some(import) = match snip.split_once(" as ") { + None => Some(snip.as_str()), + Some((import, rename)) => { + let trimmed_rename = rename.trim(); + if trimmed_rename == "_" || trimmed_rename == name.as_str() { + None + } else { + Some(import.trim()) + } + }, + } + { + span_lint_and_sugg( + cx, + MISSING_ENFORCED_IMPORT_RENAMES, + span_without_semi, + "this import should be renamed", + "try", + format!("{import} as {name}"), + Applicability::MachineApplicable, + ); + } + } + } } impl LateLintPass<'_> for ImportRename { fn check_item(&mut self, cx: &LateContext<'_>, item: &Item<'_>) { - if let ItemKind::Use(path, UseKind::Single(_)) = &item.kind { - // use `present_items` because it could be in any of type_ns, value_ns, macro_ns - for res in path.res.present_items() { - if let Res::Def(_, id) = res - && let Some(name) = self.renames.get(&id) - // Remove semicolon since it is not present for nested imports - && let span_without_semi = cx.sess().source_map().span_until_char(item.span, ';') - && let Some(snip) = span_without_semi.get_text(cx) - && let Some(import) = match snip.split_once(" as ") { - None => Some(snip.as_str()), - Some((import, rename)) => { - let trimmed_rename = rename.trim(); - if trimmed_rename == "_" || trimmed_rename == name.as_str() { - None - } else { - Some(import.trim()) - } - }, - } - { - span_lint_and_sugg( - cx, - MISSING_ENFORCED_IMPORT_RENAMES, - span_without_semi, - "this import should be renamed", - "try", - format!("{import} as {name}"), - Applicability::MachineApplicable, - ); - } - } + if let ItemKind::Use(tree) = &item.kind { + self.check_use_tree(cx, tree) } } } diff --git a/src/tools/clippy/clippy_lints/src/redundant_pub_crate.rs b/src/tools/clippy/clippy_lints/src/redundant_pub_crate.rs index 5bc73d0a5d2d0..e1ee75521b4c0 100644 --- a/src/tools/clippy/clippy_lints/src/redundant_pub_crate.rs +++ b/src/tools/clippy/clippy_lints/src/redundant_pub_crate.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_then; use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; -use rustc_hir::{Item, ItemKind, UseKind}; +use rustc_hir::{Item, ItemKind}; use rustc_lint::{LateContext, LateLintPass, impl_lint_pass}; use rustc_middle::ty; use rustc_span::def_id::CRATE_MOD_ID; @@ -83,10 +83,12 @@ impl<'tcx> LateLintPass<'tcx> for RedundantPubCrate { } } -// We ignore macro exports. And `ListStem` uses, which aren't interesting. +// We ignore macro exports. fn is_ignorable_export<'tcx>(item: &'tcx Item<'tcx>) -> bool { - if let ItemKind::Use(path, kind) = item.kind { - let ignore = matches!(path.res.macro_ns, Some(Res::Def(DefKind::Macro(_), _))) || kind == UseKind::ListStem; + if let ItemKind::Use(tree) = item.kind { + let ignore = tree + .resolutions() + .any(|res| matches!(res.macro_ns, Some(Res::Def(DefKind::Macro(_), _)))); if ignore { return true; } diff --git a/src/tools/clippy/clippy_lints/src/std_instead_of_core.rs b/src/tools/clippy/clippy_lints/src/std_instead_of_core.rs index 1c7a6e407cc05..85d1c8d0775c6 100644 --- a/src/tools/clippy/clippy_lints/src/std_instead_of_core.rs +++ b/src/tools/clippy/clippy_lints/src/std_instead_of_core.rs @@ -5,10 +5,10 @@ use clippy_utils::msrvs::Msrv; use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::DefId; -use rustc_hir::{Block, Body, HirId, Path, PathSegment, StabilityLevel, StableSince}; +use rustc_hir::{Block, Body, HirId, Item, ItemKind, Path, PathSegment, StabilityLevel, StableSince}; use rustc_lint::{LateContext, LateLintPass, Lint, LintContext as _, impl_lint_pass}; use rustc_span::symbol::kw; -use rustc_span::{Span, sym}; +use rustc_span::{Ident, Span, sym}; declare_clippy_lint! { /// ### What it does @@ -94,6 +94,7 @@ impl_lint_pass!(StdReexports => [ pub struct StdReexports { lint_points: Option<(Span, Vec)>, msrv: Msrv, + tree_start: Option<(Res, Ident)>, } impl StdReexports { @@ -101,6 +102,7 @@ impl StdReexports { Self { lint_points: Option::default(), msrv: conf.msrv.into(), + tree_start: None, } } @@ -121,36 +123,46 @@ enum LintPoint { } impl<'tcx> LateLintPass<'tcx> for StdReexports { + fn check_item(&mut self, _: &LateContext<'_>, item: &Item<'_>) { + if let ItemKind::Use(tree) = item.kind { + self.tree_start = get_first_segment(tree.prefix.segments); + } + } + + fn check_item_post(&mut self, _: &LateContext<'_>, _: &Item<'_>) { + self.tree_start = None; + } + fn check_path(&mut self, cx: &LateContext<'tcx>, path: &Path<'tcx>, _: HirId) { if let Res::Def(def_kind, def_id) = path.res && !matches!(def_kind, DefKind::Macro(_)) - && let Some(first_segment) = get_first_segment(path) - && let Res::Def(DefKind::Mod, crate_def_id) = first_segment.res + && let Some((res, ident)) = self.tree_start.or(get_first_segment(path.segments)) + && let Res::Def(DefKind::Mod, crate_def_id) = res && crate_def_id.is_crate_root() && is_stable(cx, def_id, self.msrv) && !path.span.in_external_macro(cx.sess().source_map()) - && !is_from_proc_macro(cx, &first_segment.ident) + && !is_from_proc_macro(cx, &ident) && let Some(last_segment) = path.segments.last() { - let (lint, used_mod, replace_with) = match first_segment.ident.name { + let (lint, used_mod, replace_with) = match ident.name { sym::std => match cx.tcx.crate_name(def_id.krate) { sym::core => (STD_INSTEAD_OF_CORE, "std", "core"), sym::alloc => (STD_INSTEAD_OF_ALLOC, "std", "alloc"), _ => { - self.lint_if_finish(cx, first_segment.ident.span, LintPoint::Conflict); + self.lint_if_finish(cx, ident.span, LintPoint::Conflict); return; }, }, sym::alloc if cx.tcx.crate_name(def_id.krate) == sym::core => (ALLOC_INSTEAD_OF_CORE, "alloc", "core"), _ => { - self.lint_if_finish(cx, first_segment.ident.span, LintPoint::Conflict); + self.lint_if_finish(cx, ident.span, LintPoint::Conflict); return; }, }; self.lint_if_finish( cx, - first_segment.ident.span, + ident.span, LintPoint::Available(last_segment.ident.span, lint, used_mod, replace_with), ); } @@ -222,11 +234,11 @@ fn emit_lints(cx: &LateContext<'_>, lint_points: Option<(Span, Vec)>) /// /// If this is a global path (such as `::std::fmt::Debug`), then the segment after [`kw::PathRoot`] /// is returned. -fn get_first_segment<'tcx>(path: &Path<'tcx>) -> Option<&'tcx PathSegment<'tcx>> { - match path.segments { +fn get_first_segment<'tcx>(segments: &'tcx [PathSegment<'tcx>]) -> Option<(Res, Ident)> { + match segments { // A global path will have PathRoot as the first segment. In this case, return the segment after. - [x, y, ..] if x.ident.name == kw::PathRoot => Some(y), - [x, ..] => Some(x), + [x, y, ..] if x.ident.name == kw::PathRoot => Some((y.res, y.ident)), + [x, ..] => Some((x.res, x.ident)), _ => None, } } diff --git a/src/tools/clippy/clippy_lints/src/unused_trait_names.rs b/src/tools/clippy/clippy_lints/src/unused_trait_names.rs index ce40f6785c7f5..eea7ae72ab16f 100644 --- a/src/tools/clippy/clippy_lints/src/unused_trait_names.rs +++ b/src/tools/clippy/clippy_lints/src/unused_trait_names.rs @@ -4,7 +4,8 @@ use clippy_utils::is_from_proc_macro; use clippy_utils::msrvs::{self, Msrv}; use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; -use rustc_hir::{Item, ItemKind, UseKind}; +use rustc_hir::def_id::LocalDefId; +use rustc_hir::{Item, ItemKind, UseKind, UseTree}; use rustc_lint::{LateContext, LateLintPass, impl_lint_pass}; use rustc_middle::ty::Visibility; use rustc_span::symbol::kw; @@ -53,21 +54,29 @@ impl UnusedTraitNames { pub fn new(conf: &'static Conf) -> Self { Self { msrv: conf.msrv.into() } } -} -impl<'tcx> LateLintPass<'tcx> for UnusedTraitNames { - fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) { - if !item.span.from_expansion() - && let ItemKind::Use(path, UseKind::Single(ident)) = item.kind - // Ignore imports that already use Underscore - && ident.name != kw::Underscore + fn check_use_tree(&mut self, cx: &LateContext<'_>, tree: &UseTree<'_>, def_id: LocalDefId) { + let ident = match tree.kind { + UseKind::Single(ident) => ident, + UseKind::Glob => return, + UseKind::Nested { items } => { + for (tree, _, def_id) in items { + self.check_use_tree(cx, tree, *def_id); + } + return; + }, + }; + let prefix = tree.prefix; + if + // Ignore imports that already use Underscore + ident.name != kw::Underscore // Only check traits - && let Some(Res::Def(DefKind::Trait, _)) = path.res.type_ns - && cx.tcx.resolutions(()).maybe_unused_trait_imports.contains(&item.owner_id.def_id) + && let Some(Res::Def(DefKind::Trait, _)) = prefix.res.type_ns + && cx.tcx.resolutions(()).maybe_unused_trait_imports.contains(&def_id) // Only check this import if it is visible to its module only (no pub, pub(crate), ...) - && let module = cx.tcx.parent_module_from_def_id(item.owner_id.def_id) - && cx.tcx.local_visibility(item.owner_id.def_id) == Visibility::Restricted(module) - && let Some(last_segment) = path.segments.last() + && let module = cx.tcx.parent_module_from_def_id(def_id) + && cx.tcx.local_visibility(def_id) == Visibility::Restricted(module) + && let Some(last_segment) = prefix.segments.last() && self.msrv.meets(cx, msrvs::UNDERSCORE_IMPORTS) && !is_from_proc_macro(cx, &last_segment.ident) { @@ -99,3 +108,13 @@ impl<'tcx> LateLintPass<'tcx> for UnusedTraitNames { } } } + +impl<'tcx> LateLintPass<'tcx> for UnusedTraitNames { + fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) { + if !item.span.from_expansion() + && let ItemKind::Use(tree) = &item.kind + { + self.check_use_tree(cx, tree, item.owner_id.def_id); + } + } +} diff --git a/src/tools/clippy/clippy_lints/src/wildcard_imports.rs b/src/tools/clippy/clippy_lints/src/wildcard_imports.rs index 16721fbe77a4e..e724e1f25615b 100644 --- a/src/tools/clippy/clippy_lints/src/wildcard_imports.rs +++ b/src/tools/clippy/clippy_lints/src/wildcard_imports.rs @@ -5,11 +5,12 @@ use clippy_utils::source::{snippet, snippet_with_applicability}; use rustc_data_structures::fx::FxHashSet; use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; -use rustc_hir::{Item, ItemKind, PathSegment, UseKind}; +use rustc_hir::def_id::LocalDefId; +use rustc_hir::{HirId, Item, ItemKind, PathSegment, UseKind, UseTree}; use rustc_lint::{LateContext, LateLintPass, LintContext as _, impl_lint_pass}; use rustc_middle::ty; -use rustc_span::BytePos; use rustc_span::symbol::kw; +use rustc_span::{BytePos, Span}; declare_clippy_lint! { /// ### What it does @@ -125,9 +126,27 @@ impl LateLintPass<'_> for WildcardImports { if cx.tcx.local_visibility(item.owner_id.def_id) != ty::Visibility::Restricted(module) && !self.warn_on_all { return; } - if let ItemKind::Use(use_path, UseKind::Glob) = &item.kind - && (self.warn_on_all || !self.check_exceptions(cx, item, use_path.segments)) - && let Some(used_imports) = cx.tcx.resolutions(()).glob_map.get(&item.owner_id.def_id) + if let ItemKind::Use(tree) = &item.kind { + self.check_use_tree(cx, tree, item.hir_id(), item.owner_id.def_id); + } + } +} + +impl WildcardImports { + fn check_use_tree(&mut self, cx: &LateContext<'_>, tree: &UseTree<'_>, hir_id: HirId, def_id: LocalDefId) { + match tree.kind { + UseKind::Single(_) => return, + UseKind::Glob => {}, + UseKind::Nested { items } => { + for (tree, id, def_id) in items { + self.check_use_tree(cx, tree, *id, *def_id); + } + return; + }, + } + let use_path = tree.prefix; + if (self.warn_on_all || !self.check_exceptions(cx, use_path.span, hir_id, use_path.segments)) + && let Some(used_imports) = cx.tcx.resolutions(()).glob_map.get(&def_id) && !used_imports.is_empty() // Already handled by `unused_imports` && !used_imports.contains(&kw::Underscore) { @@ -144,10 +163,14 @@ impl LateLintPass<'_> for WildcardImports { // formatting like `use _ :: *;`, we extend it up to, but not including the // `;`. In nested imports, like `use _::{inner::*, _}` there is no `;` and we // can just use the end of the item span - let mut span = use_path.span.with_hi(item.span.hi()); + let mut span = use_path.span; if snippet(cx, span, "").ends_with(';') { - span = use_path.span.with_hi(item.span.hi() - BytePos(1)); + span = use_path.span.with_hi(span.hi() - BytePos(1)); } + while !snippet(cx, span, "").ends_with('*') { + span = use_path.span.with_hi(span.hi() + BytePos(1)); + } + (span, false) }; @@ -179,11 +202,11 @@ impl LateLintPass<'_> for WildcardImports { } impl WildcardImports { - fn check_exceptions(&self, cx: &LateContext<'_>, item: &Item<'_>, segments: &[PathSegment<'_>]) -> bool { - item.span.from_expansion() + fn check_exceptions(&self, cx: &LateContext<'_>, span: Span, hir_id: HirId, segments: &[PathSegment<'_>]) -> bool { + span.from_expansion() || is_prelude_import(segments) || is_allowed_via_config(segments, self.allowed_segments) - || (is_super_only_import(segments) && is_in_test(cx.tcx, item.hir_id())) + || (is_super_only_import(segments) && is_in_test(cx.tcx, hir_id)) } } diff --git a/src/tools/clippy/clippy_utils/src/hir_utils.rs b/src/tools/clippy/clippy_utils/src/hir_utils.rs index a229de847b92e..9c5f134bb73fd 100644 --- a/src/tools/clippy/clippy_utils/src/hir_utils.rs +++ b/src/tools/clippy/clippy_utils/src/hir_utils.rs @@ -15,7 +15,7 @@ use rustc_hir::{ GenericParam, GenericParamKind, GenericParamSource, Generics, HirId, HirIdMap, InlineAsmOperand, ItemId, ItemKind, LetExpr, Lifetime, LifetimeKind, LifetimeParamKind, Node, ParamName, Pat, PatExpr, PatExprKind, PatField, PatKind, Path, PathSegment, PreciseCapturingArgKind, PrimTy, QPath, Stmt, StmtKind, StructTailExpr, TraitBoundModifiers, Ty, - TyFieldPath, TyKind, TyPat, TyPatKind, UseKind, WherePredicate, WherePredicateKind, + TyFieldPath, TyKind, TyPat, TyPatKind, UseKind, UseTree, WherePredicate, WherePredicateKind, }; use rustc_lexer::{FrontmatterAllowed, TokenKind, tokenize}; use rustc_lint::LateContext; @@ -245,14 +245,7 @@ impl HirEqInterExpr<'_, '_, '_> { (ItemKind::TyAlias(l_ident, l_generics, l_ty), ItemKind::TyAlias(r_ident, r_generics, r_ty)) => { l_ident.name == r_ident.name && self.eq_generics(l_generics, r_generics) && self.eq_ty(l_ty, r_ty) }, - (ItemKind::Use(l_path, l_kind), ItemKind::Use(r_path, r_kind)) => { - self.eq_path_segments(l_path.segments, r_path.segments) - && match (l_kind, r_kind) { - (UseKind::Single(l_ident), UseKind::Single(r_ident)) => l_ident.name == r_ident.name, - (UseKind::Glob, UseKind::Glob) | (UseKind::ListStem, UseKind::ListStem) => true, - _ => false, - } - }, + (ItemKind::Use(ref l_tree), ItemKind::Use(ref r_tree)) => self.eq_use_tree(l_tree, r_tree), (ItemKind::Mod(l_ident, l_mod), ItemKind::Mod(r_ident, r_mod)) => { l_ident.name == r_ident.name && over(l_mod.item_ids, r_mod.item_ids, |l, r| self.eq_item(*l, *r)) }, @@ -264,6 +257,18 @@ impl HirEqInterExpr<'_, '_, '_> { eq } + fn eq_use_tree(&mut self, l_tree: &UseTree<'_>, r_tree: &UseTree<'_>) -> bool { + self.eq_path_segments(l_tree.prefix.segments, r_tree.prefix.segments) + && match (l_tree.kind, r_tree.kind) { + (UseKind::Single(l_ident), UseKind::Single(r_ident)) => l_ident.name == r_ident.name, + (UseKind::Glob, UseKind::Glob) => true, + (UseKind::Nested { items: l_items }, UseKind::Nested { items: r_items }) => { + over(l_items, r_items, |((l, _, _), (r, _, _))| self.eq_use_tree(l, r)) + }, + _ => false, + } + } + fn eq_fn_sig(&mut self, left: &FnSig<'_>, right: &FnSig<'_>) -> bool { left.header.safety == right.header.safety && left.header.constness == right.header.constness diff --git a/src/tools/clippy/clippy_utils/src/lib.rs b/src/tools/clippy/clippy_utils/src/lib.rs index 5ba7c0496e906..7257826d2a59c 100644 --- a/src/tools/clippy/clippy_utils/src/lib.rs +++ b/src/tools/clippy/clippy_utils/src/lib.rs @@ -2789,6 +2789,7 @@ pub fn expr_use_sites<'tcx>( | Node::TraitRef(_) | Node::Ty(_) | Node::TyPat(_) + | Node::NestedUseTree(_) | Node::WherePredicate(_) | Node::TestBinderForall(_) | Node::TestBinderExists(_) diff --git a/src/tools/clippy/clippy_utils/src/paths.rs b/src/tools/clippy/clippy_utils/src/paths.rs index a97295d952949..090d9c2962b50 100644 --- a/src/tools/clippy/clippy_utils/src/paths.rs +++ b/src/tools/clippy/clippy_utils/src/paths.rs @@ -11,7 +11,7 @@ use rustc_data_structures::fx::FxHashMap; use rustc_hir::def::Namespace::{MacroNS, TypeNS, ValueNS}; use rustc_hir::def::{DefKind, Namespace, Res}; use rustc_hir::def_id::{DefId, LOCAL_CRATE, LocalDefId}; -use rustc_hir::{ItemKind, Node, UseKind}; +use rustc_hir::{ItemKind, Node, UseKind, UseTree}; use rustc_lint::LateContext; use rustc_middle::ty::fast_reject::SimplifiedType; use rustc_middle::ty::layout::HasTyCtxt; @@ -308,13 +308,17 @@ fn local_item_child_by_name(tcx: TyCtxt<'_>, local_id: LocalDefId, ns: PathNS, n match item_kind { ItemKind::Mod(_, r#mod) => r#mod.item_ids.iter().find_map(|&item_id| { let item = tcx.hir_item(item_id); - if let ItemKind::Use(path, UseKind::Single(ident)) = item.kind { + if let ItemKind::Use(UseTree { + prefix, + kind: UseKind::Single(ident), + }) = item.kind + { if ident.name == name { let opt_def_id = |ns: Option| ns.and_then(|res| res.opt_def_id()); match ns { - PathNS::Type => opt_def_id(path.res.type_ns), - PathNS::Value => opt_def_id(path.res.value_ns), - PathNS::Macro => opt_def_id(path.res.macro_ns), + PathNS::Type => opt_def_id(prefix.res.type_ns), + PathNS::Value => opt_def_id(prefix.res.value_ns), + PathNS::Macro => opt_def_id(prefix.res.macro_ns), PathNS::Field => None, PathNS::Arbitrary => unreachable!(), } diff --git a/src/tools/clippy/tests/ui-toml/missing_enforced_import_rename/conf_missing_enforced_import_rename.stderr b/src/tools/clippy/tests/ui-toml/missing_enforced_import_rename/conf_missing_enforced_import_rename.stderr index 139331d176198..ff6b1b8822a7b 100644 --- a/src/tools/clippy/tests/ui-toml/missing_enforced_import_rename/conf_missing_enforced_import_rename.stderr +++ b/src/tools/clippy/tests/ui-toml/missing_enforced_import_rename/conf_missing_enforced_import_rename.stderr @@ -8,10 +8,10 @@ LL | use std::process::{Child as Kid, exit as wrong_exit}; = help: to override `-D warnings` add `#[allow(clippy::missing_enforced_import_renames)]` error: this import should be renamed - --> tests/ui-toml/missing_enforced_import_rename/conf_missing_enforced_import_rename.rs:7:1 + --> tests/ui-toml/missing_enforced_import_rename/conf_missing_enforced_import_rename.rs:7:5 | LL | use std::thread::sleep; - | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `use std::thread::sleep as thread_sleep` + | ^^^^^^^^^^^^^^^^^^ help: try: `std::thread::sleep as thread_sleep` error: this import should be renamed --> tests/ui-toml/missing_enforced_import_rename/conf_missing_enforced_import_rename.rs:11:11 @@ -32,10 +32,10 @@ LL | sync :: Mutex, | ^^^^^^^^^^^^^ help: try: `sync :: Mutex as StdMutie` error: this import should be renamed - --> tests/ui-toml/missing_enforced_import_rename/conf_missing_enforced_import_rename.rs:21:5 + --> tests/ui-toml/missing_enforced_import_rename/conf_missing_enforced_import_rename.rs:21:9 | LL | use std::collections::BTreeMap as OopsWrongRename; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `use std::collections::BTreeMap as Map` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `std::collections::BTreeMap as Map` error: aborting due to 6 previous errors diff --git a/src/tools/clippy/tests/ui/redundant_pub_crate.stderr b/src/tools/clippy/tests/ui/redundant_pub_crate.stderr index b6542e1db0902..37edb6a51ac98 100644 --- a/src/tools/clippy/tests/ui/redundant_pub_crate.stderr +++ b/src/tools/clippy/tests/ui/redundant_pub_crate.stderr @@ -138,10 +138,10 @@ LL | pub(crate) use m5_1::*; | help: consider using: `pub` error: pub(crate) import inside private module - --> tests/ui/redundant_pub_crate.rs:138:27 + --> tests/ui/redundant_pub_crate.rs:138:5 | LL | pub(crate) use m5_1::{*}; - | ---------- ^ + | ----------^^^^^^^^^^^^^^^ | | | help: consider using: `pub` diff --git a/src/tools/clippy/tests/ui/wildcard_imports.fixed b/src/tools/clippy/tests/ui/wildcard_imports.fixed index 27d01e6573b85..a2854d3f1b0fe 100644 --- a/src/tools/clippy/tests/ui/wildcard_imports.fixed +++ b/src/tools/clippy/tests/ui/wildcard_imports.fixed @@ -200,7 +200,7 @@ fn test_reexported() { #[rustfmt::skip] fn test_weird_formatting() { - use crate:: in_fn_test::exported; + use crate:: in_fn_test::exported ; //~^ wildcard_imports use crate:: fn_mod::foo; diff --git a/src/tools/clippy/tests/ui/wildcard_imports.stderr b/src/tools/clippy/tests/ui/wildcard_imports.stderr index 26434656a509e..1ec063fa14f5b 100644 --- a/src/tools/clippy/tests/ui/wildcard_imports.stderr +++ b/src/tools/clippy/tests/ui/wildcard_imports.stderr @@ -89,7 +89,7 @@ error: usage of wildcard import --> tests/ui/wildcard_imports.rs:203:9 | LL | use crate:: in_fn_test:: * ; - | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `crate:: in_fn_test::exported` + | ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `crate:: in_fn_test::exported` error: usage of wildcard import --> tests/ui/wildcard_imports.rs:205:9 diff --git a/src/tools/clippy/tests/ui/wildcard_imports_2021.edition2018.fixed b/src/tools/clippy/tests/ui/wildcard_imports_2021.edition2018.fixed index 46abaa91a1c2f..e4a1a32ca2271 100644 --- a/src/tools/clippy/tests/ui/wildcard_imports_2021.edition2018.fixed +++ b/src/tools/clippy/tests/ui/wildcard_imports_2021.edition2018.fixed @@ -194,7 +194,7 @@ fn test_reexported() { #[rustfmt::skip] fn test_weird_formatting() { - use crate:: in_fn_test::exported; + use crate:: in_fn_test::exported ; //~^ wildcard_imports use crate:: fn_mod::foo; diff --git a/src/tools/clippy/tests/ui/wildcard_imports_2021.edition2018.stderr b/src/tools/clippy/tests/ui/wildcard_imports_2021.edition2018.stderr index 873ce41b04f49..84f26820c63f6 100644 --- a/src/tools/clippy/tests/ui/wildcard_imports_2021.edition2018.stderr +++ b/src/tools/clippy/tests/ui/wildcard_imports_2021.edition2018.stderr @@ -89,7 +89,7 @@ error: usage of wildcard import --> tests/ui/wildcard_imports_2021.rs:197:9 | LL | use crate:: in_fn_test:: * ; - | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `crate:: in_fn_test::exported` + | ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `crate:: in_fn_test::exported` error: usage of wildcard import --> tests/ui/wildcard_imports_2021.rs:199:9 diff --git a/src/tools/clippy/tests/ui/wildcard_imports_2021.edition2021.fixed b/src/tools/clippy/tests/ui/wildcard_imports_2021.edition2021.fixed index 46abaa91a1c2f..e4a1a32ca2271 100644 --- a/src/tools/clippy/tests/ui/wildcard_imports_2021.edition2021.fixed +++ b/src/tools/clippy/tests/ui/wildcard_imports_2021.edition2021.fixed @@ -194,7 +194,7 @@ fn test_reexported() { #[rustfmt::skip] fn test_weird_formatting() { - use crate:: in_fn_test::exported; + use crate:: in_fn_test::exported ; //~^ wildcard_imports use crate:: fn_mod::foo; diff --git a/src/tools/clippy/tests/ui/wildcard_imports_2021.edition2021.stderr b/src/tools/clippy/tests/ui/wildcard_imports_2021.edition2021.stderr index 873ce41b04f49..84f26820c63f6 100644 --- a/src/tools/clippy/tests/ui/wildcard_imports_2021.edition2021.stderr +++ b/src/tools/clippy/tests/ui/wildcard_imports_2021.edition2021.stderr @@ -89,7 +89,7 @@ error: usage of wildcard import --> tests/ui/wildcard_imports_2021.rs:197:9 | LL | use crate:: in_fn_test:: * ; - | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `crate:: in_fn_test::exported` + | ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `crate:: in_fn_test::exported` error: usage of wildcard import --> tests/ui/wildcard_imports_2021.rs:199:9 diff --git a/tests/ui/lint/unreachable_pub.stderr b/tests/ui/lint/unreachable_pub.stderr index 5173ff1f0264d..30a77f589b38e 100644 --- a/tests/ui/lint/unreachable_pub.stderr +++ b/tests/ui/lint/unreachable_pub.stderr @@ -14,10 +14,10 @@ LL | #![warn(unreachable_pub)] | ^^^^^^^^^^^^^^^ warning: unreachable `pub` item - --> $DIR/unreachable_pub.rs:11:24 + --> $DIR/unreachable_pub.rs:11:13 | LL | pub use std::env::{Args}; // braced-use has different item spans than unbraced - | --- ^^^^ + | --- ^^^^^^^^ | | | help: consider restricting its visibility: `pub(crate)` | diff --git a/tests/ui/unpretty/exhaustive.hir.stdout b/tests/ui/unpretty/exhaustive.hir.stdout index 1b18e00c951c8..4756c08504448 100644 --- a/tests/ui/unpretty/exhaustive.hir.stdout +++ b/tests/ui/unpretty/exhaustive.hir.stdout @@ -403,9 +403,7 @@ mod items { } /// ItemKind::Use mod item_use { - use ::{}; - use crate::expressions; - use crate::items::item_use; + use crate::{expressions;items::item_use;}; use core::*; } /// ItemKind::Static