From 1f259ae6799e7e0a08e5b84fc5686e404b17eef0 Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Fri, 25 Nov 2022 17:39:38 +0300 Subject: [PATCH] rustc_hir: Change representation of import paths to support multiple resolutions --- compiler/rustc_ast_lowering/src/item.rs | 21 +++---- compiler/rustc_ast_lowering/src/path.rs | 21 ++----- .../src/stable_hasher.rs | 2 +- compiler/rustc_hir/src/arena.rs | 1 + compiler/rustc_hir/src/hir.rs | 9 ++- compiler/rustc_hir/src/intravisit.rs | 9 ++- compiler/rustc_hir_pretty/src/lib.rs | 2 +- compiler/rustc_passes/src/hir_stats.rs | 2 +- .../rustc_save_analysis/src/dump_visitor.rs | 4 +- compiler/rustc_save_analysis/src/lib.rs | 4 +- src/librustdoc/clean/mod.rs | 20 ++++++ src/librustdoc/clean/types.rs | 11 ++-- src/librustdoc/html/render/span_map.rs | 8 --- src/librustdoc/visit_ast.rs | 63 ++++++++++--------- .../clippy_lints/src/disallowed_types.rs | 4 +- .../clippy/clippy_lints/src/macro_use.rs | 5 +- .../src/missing_enforced_import_rename.rs | 59 ++++++++--------- .../clippy_lints/src/redundant_pub_crate.rs | 2 +- .../clippy_lints/src/wildcard_imports.rs | 3 +- 19 files changed, 136 insertions(+), 114 deletions(-) diff --git a/compiler/rustc_ast_lowering/src/item.rs b/compiler/rustc_ast_lowering/src/item.rs index 2b47e908912..b26b37b0466 100644 --- a/compiler/rustc_ast_lowering/src/item.rs +++ b/compiler/rustc_ast_lowering/src/item.rs @@ -508,7 +508,7 @@ impl<'hir> LoweringContext<'_, 'hir> { let mut resolutions = self.expect_full_res_from_use(id).fuse(); // We want to return *something* from this function, so hold onto the first item // for later. - let ret_res = self.lower_res(resolutions.next().unwrap_or(Res::Err)); + let ret_res = smallvec![self.lower_res(resolutions.next().unwrap_or(Res::Err))]; // Here, we are looping over namespaces, if they exist for the definition // being imported. We only handle type and value namespaces because we @@ -538,8 +538,8 @@ impl<'hir> LoweringContext<'_, 'hir> { let span = path.span; self.with_hir_id_owner(new_node_id, |this| { - let res = this.lower_res(res); - let path = this.lower_path_extra(res, &path, ParamMode::Explicit); + let res = smallvec![this.lower_res(res)]; + let path = this.lower_use_path(res, &path, ParamMode::Explicit); let kind = hir::ItemKind::Use(path, hir::UseKind::Single); if let Some(attrs) = attrs { this.attrs.insert(hir::ItemLocalId::new(0), attrs); @@ -556,15 +556,14 @@ impl<'hir> LoweringContext<'_, 'hir> { }); } - let path = self.lower_path_extra(ret_res, &path, ParamMode::Explicit); + let path = self.lower_use_path(ret_res, &path, ParamMode::Explicit); hir::ItemKind::Use(path, hir::UseKind::Single) } UseTreeKind::Glob => { - let path = self.lower_path( - id, - &Path { segments, span: path.span, tokens: None }, - ParamMode::Explicit, - ); + let res = self.expect_full_res(id); + let res = smallvec![self.lower_res(res)]; + let path = Path { segments, span: path.span, tokens: None }; + let path = self.lower_use_path(res, &path, ParamMode::Explicit); hir::ItemKind::Use(path, hir::UseKind::Glob) } UseTreeKind::Nested(ref trees) => { @@ -635,8 +634,8 @@ impl<'hir> LoweringContext<'_, 'hir> { } let res = self.expect_full_res_from_use(id).next().unwrap_or(Res::Err); - let res = self.lower_res(res); - let path = self.lower_path_extra(res, &prefix, ParamMode::Explicit); + let res = smallvec![self.lower_res(res)]; + let path = self.lower_use_path(res, &prefix, ParamMode::Explicit); hir::ItemKind::Use(path, hir::UseKind::ListStem) } } diff --git a/compiler/rustc_ast_lowering/src/path.rs b/compiler/rustc_ast_lowering/src/path.rs index dc85b5e95ea..8d23c26e603 100644 --- a/compiler/rustc_ast_lowering/src/path.rs +++ b/compiler/rustc_ast_lowering/src/path.rs @@ -12,7 +12,7 @@ use rustc_hir::GenericArg; use rustc_span::symbol::{kw, Ident}; use rustc_span::{BytePos, Span, DUMMY_SP}; -use smallvec::smallvec; +use smallvec::{smallvec, SmallVec}; impl<'a, 'hir> LoweringContext<'a, 'hir> { #[instrument(level = "trace", skip(self))] @@ -144,13 +144,13 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { ); } - pub(crate) fn lower_path_extra( + pub(crate) fn lower_use_path( &mut self, - res: Res, + res: SmallVec<[Res; 3]>, p: &Path, param_mode: ParamMode, - ) -> &'hir hir::Path<'hir> { - self.arena.alloc(hir::Path { + ) -> &'hir hir::UsePath<'hir> { + self.arena.alloc(hir::UsePath { res, segments: self.arena.alloc_from_iter(p.segments.iter().map(|segment| { self.lower_path_segment( @@ -165,17 +165,6 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { }) } - pub(crate) fn lower_path( - &mut self, - id: NodeId, - p: &Path, - param_mode: ParamMode, - ) -> &'hir hir::Path<'hir> { - let res = self.expect_full_res(id); - let res = self.lower_res(res); - self.lower_path_extra(res, p, param_mode) - } - pub(crate) fn lower_path_segment( &mut self, path_span: Span, diff --git a/compiler/rustc_data_structures/src/stable_hasher.rs b/compiler/rustc_data_structures/src/stable_hasher.rs index ce859173418..e2c33e7e062 100644 --- a/compiler/rustc_data_structures/src/stable_hasher.rs +++ b/compiler/rustc_data_structures/src/stable_hasher.rs @@ -399,7 +399,7 @@ where } } -impl HashStable for SmallVec<[A; 1]> +impl HashStable for SmallVec<[A; N]> where A: HashStable, { diff --git a/compiler/rustc_hir/src/arena.rs b/compiler/rustc_hir/src/arena.rs index 44335b7f42e..c89e7eb75f8 100644 --- a/compiler/rustc_hir/src/arena.rs +++ b/compiler/rustc_hir/src/arena.rs @@ -39,6 +39,7 @@ macro_rules! arena_types { [] param: rustc_hir::Param<'tcx>, [] pat: rustc_hir::Pat<'tcx>, [] path: rustc_hir::Path<'tcx>, + [] use_path: rustc_hir::UsePath<'tcx>, [] path_segment: rustc_hir::PathSegment<'tcx>, [] poly_trait_ref: rustc_hir::PolyTraitRef<'tcx>, [] qpath: rustc_hir::QPath<'tcx>, diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index 81aedcce877..118eafe2910 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -183,14 +183,17 @@ impl Lifetime { /// `std::cmp::PartialEq`. It's represented as a sequence of identifiers, /// along with a bunch of supporting information. #[derive(Debug, HashStable_Generic)] -pub struct Path<'hir> { +pub struct Path<'hir, R = Res> { pub span: Span, /// The resolution for the path. - pub res: Res, + pub res: R, /// The segments in the path: the things separated by `::`. pub segments: &'hir [PathSegment<'hir>], } +/// Up to three resolutions for type, value and macro namespaces. +pub type UsePath<'hir> = Path<'hir, SmallVec<[Res; 3]>>; + impl Path<'_> { pub fn is_global(&self) -> bool { !self.segments.is_empty() && self.segments[0].ident.name == kw::PathRoot @@ -3068,7 +3071,7 @@ pub enum ItemKind<'hir> { /// or just /// /// `use foo::bar::baz;` (with `as baz` implicitly on the right). - Use(&'hir Path<'hir>, UseKind), + Use(&'hir UsePath<'hir>, UseKind), /// A `static` item. Static(&'hir Ty<'hir>, Mutability, BodyId), diff --git a/compiler/rustc_hir/src/intravisit.rs b/compiler/rustc_hir/src/intravisit.rs index 9eeb04c8e01..cbb530424ca 100644 --- a/compiler/rustc_hir/src/intravisit.rs +++ b/compiler/rustc_hir/src/intravisit.rs @@ -367,7 +367,7 @@ pub trait Visitor<'v>: Sized { fn visit_fn(&mut self, fk: FnKind<'v>, fd: &'v FnDecl<'v>, b: BodyId, _: Span, id: HirId) { walk_fn(self, fk, fd, b, id) } - fn visit_use(&mut self, path: &'v Path<'v>, hir_id: HirId) { + fn visit_use(&mut self, path: &'v UsePath<'v>, hir_id: HirId) { walk_use(self, path, hir_id) } fn visit_trait_item(&mut self, ti: &'v TraitItem<'v>) { @@ -938,9 +938,12 @@ 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 Path<'v>, hir_id: HirId) { +pub fn walk_use<'v, V: Visitor<'v>>(visitor: &mut V, path: &'v UsePath<'v>, hir_id: HirId) { visitor.visit_id(hir_id); - visitor.visit_path(path, hir_id); + let UsePath { segments, ref res, span } = *path; + for &res in res { + visitor.visit_path(&Path { segments, res, span }, hir_id); + } } pub fn walk_trait_item<'v, V: Visitor<'v>>(visitor: &mut V, trait_item: &'v TraitItem<'v>) { diff --git a/compiler/rustc_hir_pretty/src/lib.rs b/compiler/rustc_hir_pretty/src/lib.rs index 95729822677..2460a23bb3f 100644 --- a/compiler/rustc_hir_pretty/src/lib.rs +++ b/compiler/rustc_hir_pretty/src/lib.rs @@ -1591,7 +1591,7 @@ impl<'a> State<'a> { self.print_ident(Ident::with_dummy_span(name)) } - pub fn print_path(&mut self, path: &hir::Path<'_>, colons_before_params: bool) { + pub fn print_path(&mut self, path: &hir::Path<'_, R>, colons_before_params: bool) { self.maybe_print_comment(path.span.lo()); for (i, segment) in path.segments.iter().enumerate() { diff --git a/compiler/rustc_passes/src/hir_stats.rs b/compiler/rustc_passes/src/hir_stats.rs index 20951878e00..a7854cd4998 100644 --- a/compiler/rustc_passes/src/hir_stats.rs +++ b/compiler/rustc_passes/src/hir_stats.rs @@ -369,7 +369,7 @@ 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::Path<'v>, hir_id: hir::HirId) { + fn visit_use(&mut self, p: &'v hir::UsePath<'v>, hir_id: hir::HirId) { // This is `visit_use`, but the type is `Path` so record it that way. self.record("Path", Id::None, p); hir_visit::walk_use(self, p, hir_id) diff --git a/compiler/rustc_save_analysis/src/dump_visitor.rs b/compiler/rustc_save_analysis/src/dump_visitor.rs index fae20c2ba5f..b4528853825 100644 --- a/compiler/rustc_save_analysis/src/dump_visitor.rs +++ b/compiler/rustc_save_analysis/src/dump_visitor.rs @@ -185,13 +185,13 @@ impl<'tcx> DumpVisitor<'tcx> { } } - fn write_sub_paths(&mut self, path: &'tcx hir::Path<'tcx>) { + fn write_sub_paths(&mut self, path: &'tcx hir::Path<'tcx, R>) { self.write_segments(path.segments) } // As write_sub_paths, but does not process the last ident in the path (assuming it // will be processed elsewhere). See note on write_sub_paths about global. - fn write_sub_paths_truncated(&mut self, path: &'tcx hir::Path<'tcx>) { + fn write_sub_paths_truncated(&mut self, path: &'tcx hir::Path<'tcx, R>) { if let [segments @ .., _] = path.segments { self.write_segments(segments) } diff --git a/compiler/rustc_save_analysis/src/lib.rs b/compiler/rustc_save_analysis/src/lib.rs index 4fa0c14715e..f05eb2b7432 100644 --- a/compiler/rustc_save_analysis/src/lib.rs +++ b/compiler/rustc_save_analysis/src/lib.rs @@ -594,7 +594,9 @@ impl<'tcx> SaveContext<'tcx> { match self.tcx.hir().get(hir_id) { Node::TraitRef(tr) => tr.path.res, - Node::Item(&hir::Item { kind: hir::ItemKind::Use(path, _), .. }) => path.res, + Node::Item(&hir::Item { kind: hir::ItemKind::Use(path, _), .. }) => { + path.res.get(0).copied().unwrap_or(Res::Err) + } Node::PathSegment(seg) => { if seg.res != Res::Err { seg.res diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index b18788a033f..80909919ba2 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -2231,6 +2231,26 @@ fn clean_extern_crate<'tcx>( } fn clean_use_statement<'tcx>( + import: &hir::Item<'tcx>, + name: Symbol, + path: &hir::UsePath<'tcx>, + kind: hir::UseKind, + cx: &mut DocContext<'tcx>, + inlined_names: &mut FxHashSet<(ItemType, Symbol)>, +) -> Vec { + let mut items = Vec::new(); + let hir::UsePath { segments, ref res, span } = *path; + for &res in res { + if let Res::Def(DefKind::Ctor(..), _) | Res::SelfCtor(..) = res { + continue; + } + let path = hir::Path { segments, res, span }; + items.append(&mut clean_use_statement_inner(import, name, &path, kind, cx, inlined_names)); + } + items +} + +fn clean_use_statement_inner<'tcx>( import: &hir::Item<'tcx>, name: Symbol, path: &hir::Path<'tcx>, diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs index ed4e9508f43..2590bb0df3f 100644 --- a/src/librustdoc/clean/types.rs +++ b/src/librustdoc/clean/types.rs @@ -242,7 +242,9 @@ impl ExternalCrate { hir::ItemKind::Use(path, hir::UseKind::Single) if tcx.visibility(id.owner_id).is_public() => { - as_keyword(path.res.expect_non_local()) + path.res + .iter() + .find_map(|res| as_keyword(res.expect_non_local())) .map(|(_, prim)| (id.owner_id.to_def_id(), prim)) } _ => None, @@ -310,10 +312,11 @@ impl ExternalCrate { hir::ItemKind::Use(path, hir::UseKind::Single) if tcx.visibility(id.owner_id).is_public() => { - as_primitive(path.res.expect_non_local()).map(|(_, prim)| { + path.res + .iter() + .find_map(|res| as_primitive(res.expect_non_local())) // Pretend the primitive is local. - (id.owner_id.to_def_id(), prim) - }) + .map(|(_, prim)| (id.owner_id.to_def_id(), prim)) } _ => None, } diff --git a/src/librustdoc/html/render/span_map.rs b/src/librustdoc/html/render/span_map.rs index b898db24246..4514894cabe 100644 --- a/src/librustdoc/html/render/span_map.rs +++ b/src/librustdoc/html/render/span_map.rs @@ -190,12 +190,4 @@ impl<'tcx> Visitor<'tcx> for SpanMapVisitor<'tcx> { } intravisit::walk_expr(self, expr); } - - fn visit_use(&mut self, path: &'tcx rustc_hir::Path<'tcx>, id: HirId) { - if self.handle_macro(path.span) { - return; - } - self.handle_path(path); - intravisit::walk_use(self, path, id); - } } diff --git a/src/librustdoc/visit_ast.rs b/src/librustdoc/visit_ast.rs index c788b9f4093..22068ebe041 100644 --- a/src/librustdoc/visit_ast.rs +++ b/src/librustdoc/visit_ast.rs @@ -301,39 +301,40 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> { hir::ItemKind::GlobalAsm(..) => {} hir::ItemKind::Use(_, hir::UseKind::ListStem) => {} hir::ItemKind::Use(path, kind) => { - let is_glob = kind == hir::UseKind::Glob; - - // Struct and variant constructors and proc macro stubs always show up alongside - // their definitions, we've already processed them so just discard these. - if let Res::Def(DefKind::Ctor(..), _) | Res::SelfCtor(..) = path.res { - return; - } - - let attrs = self.cx.tcx.hir().attrs(item.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 = attrs.iter().any(|item| match item.meta_item_list() { - Some(ref list) if item.has_name(sym::doc) => { - list.iter().any(|i| i.has_name(sym::inline)) - } - _ => false, - }); - let ident = if is_glob { None } else { Some(name) }; - if self.maybe_inline_local( - item.hir_id(), - path.res, - ident, - is_glob, - om, - please_inline, - ) { - return; + for &res in &path.res { + // Struct and variant constructors and proc macro stubs always show up alongside + // their definitions, we've already processed them so just discard these. + if let Res::Def(DefKind::Ctor(..), _) | Res::SelfCtor(..) = res { + continue; } - } - om.items.push((item, renamed, parent_id)) + let attrs = self.cx.tcx.hir().attrs(item.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 = attrs.iter().any(|item| match item.meta_item_list() { + Some(ref list) if item.has_name(sym::doc) => { + list.iter().any(|i| i.has_name(sym::inline)) + } + _ => false, + }); + let is_glob = kind == hir::UseKind::Glob; + let ident = if is_glob { None } else { Some(name) }; + if self.maybe_inline_local( + item.hir_id(), + res, + ident, + is_glob, + om, + please_inline, + ) { + continue; + } + } + + om.items.push((item, renamed, parent_id)) + } } hir::ItemKind::Macro(ref macro_def, _) => { // `#[macro_export] macro_rules!` items are handled separately in `visit()`, diff --git a/src/tools/clippy/clippy_lints/src/disallowed_types.rs b/src/tools/clippy/clippy_lints/src/disallowed_types.rs index aee3d8c4f08..1f56d0118a4 100644 --- a/src/tools/clippy/clippy_lints/src/disallowed_types.rs +++ b/src/tools/clippy/clippy_lints/src/disallowed_types.rs @@ -106,7 +106,9 @@ 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 { - self.check_res_emit(cx, &path.res, item.span); + for res in &path.res { + self.check_res_emit(cx, res, item.span); + } } } diff --git a/src/tools/clippy/clippy_lints/src/macro_use.rs b/src/tools/clippy/clippy_lints/src/macro_use.rs index 594f6af76b3..e2e6a87a301 100644 --- a/src/tools/clippy/clippy_lints/src/macro_use.rs +++ b/src/tools/clippy/clippy_lints/src/macro_use.rs @@ -94,7 +94,10 @@ impl<'tcx> LateLintPass<'tcx> for MacroUseImports { let hir_id = item.hir_id(); let attrs = cx.tcx.hir().attrs(hir_id); if let Some(mac_attr) = attrs.iter().find(|attr| attr.has_name(sym::macro_use)); - if let Res::Def(DefKind::Mod, id) = path.res; + if let Some(id) = path.res.iter().find_map(|res| match res { + Res::Def(DefKind::Mod, id) => Some(id), + _ => None, + }); if !id.is_local(); then { for kid in cx.tcx.module_children(id).iter() { 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 4712846939e..773174679db 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 @@ -66,35 +66,38 @@ impl LateLintPass<'_> for ImportRename { } fn check_item(&mut self, cx: &LateContext<'_>, item: &Item<'_>) { - if_chain! { - if let ItemKind::Use(path, UseKind::Single) = &item.kind; - if let Res::Def(_, id) = path.res; - if 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, ';'); - if let Some(snip) = snippet_opt(cx, span_without_semi); - if let Some(import) = match snip.split_once(" as ") { - None => Some(snip.as_str()), - Some((import, rename)) => { - if rename.trim() == name.as_str() { - None - } else { - Some(import.trim()) + if let ItemKind::Use(path, UseKind::Single) = &item.kind { + for &res in &path.res { + if_chain! { + if let Res::Def(_, id) = res; + if 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, ';'); + if let Some(snip) = snippet_opt(cx, span_without_semi); + if let Some(import) = match snip.split_once(" as ") { + None => Some(snip.as_str()), + Some((import, rename)) => { + if rename.trim() == name.as_str() { + None + } else { + Some(import.trim()) + } + }, + }; + then { + span_lint_and_sugg( + cx, + MISSING_ENFORCED_IMPORT_RENAMES, + span_without_semi, + "this import should be renamed", + "try", + format!( + "{import} as {name}", + ), + Applicability::MachineApplicable, + ); } - }, - }; - then { - span_lint_and_sugg( - cx, - MISSING_ENFORCED_IMPORT_RENAMES, - span_without_semi, - "this import should be renamed", - "try", - format!( - "{import} as {name}", - ), - Applicability::MachineApplicable, - ); + } } } } 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 833dc4913b4..d612d249c2f 100644 --- a/src/tools/clippy/clippy_lints/src/redundant_pub_crate.rs +++ b/src/tools/clippy/clippy_lints/src/redundant_pub_crate.rs @@ -84,7 +84,7 @@ impl<'tcx> LateLintPass<'tcx> for RedundantPubCrate { fn is_not_macro_export<'tcx>(item: &'tcx Item<'tcx>) -> bool { if let ItemKind::Use(path, _) = item.kind { - if let Res::Def(DefKind::Macro(MacroKind::Bang), _) = path.res { + if path.res.iter().all(|res| matches!(res, Res::Def(DefKind::Macro(MacroKind::Bang), _))) { return false; } } else if let ItemKind::Macro(..) = item.kind { diff --git a/src/tools/clippy/clippy_lints/src/wildcard_imports.rs b/src/tools/clippy/clippy_lints/src/wildcard_imports.rs index be98344470b..e4d1ee195c4 100644 --- a/src/tools/clippy/clippy_lints/src/wildcard_imports.rs +++ b/src/tools/clippy/clippy_lints/src/wildcard_imports.rs @@ -176,7 +176,8 @@ impl LateLintPass<'_> for WildcardImports { format!("{import_source_snippet}::{imports_string}") }; - let (lint, message) = if let Res::Def(DefKind::Enum, _) = use_path.res { + // Glob imports always have a single resolution. + let (lint, message) = if let Res::Def(DefKind::Enum, _) = use_path.res[0] { (ENUM_GLOB_USE, "usage of wildcard import for enum variants") } else { (WILDCARD_IMPORTS, "usage of wildcard import")