diff options
| author | Dan Aloni <alonid@gmail.com> | 2020-09-02 10:40:56 +0300 |
|---|---|---|
| committer | Dan Aloni <alonid@gmail.com> | 2020-09-02 22:26:37 +0300 |
| commit | 07e7823c01be1733df2480de19fbbe6b8e9384cf (patch) | |
| tree | 9e4ff1075680201f9c72b58bf780638ef1fcede3 /compiler/rustc_middle/src/ty/print | |
| parent | 7b2deb562822112cc30d23958e7459564e2c6ef9 (diff) | |
| download | rust-07e7823c01be1733df2480de19fbbe6b8e9384cf.tar.gz rust-07e7823c01be1733df2480de19fbbe6b8e9384cf.zip | |
pretty: trim paths of unique symbols
If a symbol name can only be imported from one place for a type, and as long as it was not glob-imported anywhere in the current crate, we can trim its printed path and print only the name. This has wide implications on error messages with types, for example, shortening `std::vec::Vec` to just `Vec`, as long as there is no other `Vec` importable anywhere. This adds a new '-Z trim-diagnostic-paths=false' option to control this feature. On the good path, with no diagnosis printed, we should try to avoid issuing this query, so we need to prevent trimmed_def_paths query on several cases. This change also relies on a previous commit that differentiates between `Debug` and `Display` on various rustc types, where the latter is trimmed and presented to the user and the former is not.
Diffstat (limited to 'compiler/rustc_middle/src/ty/print')
| -rw-r--r-- | compiler/rustc_middle/src/ty/print/pretty.rs | 178 |
1 files changed, 176 insertions, 2 deletions
diff --git a/compiler/rustc_middle/src/ty/print/pretty.rs b/compiler/rustc_middle/src/ty/print/pretty.rs index 999a1d52a26..50ce80cc9fc 100644 --- a/compiler/rustc_middle/src/ty/print/pretty.rs +++ b/compiler/rustc_middle/src/ty/print/pretty.rs @@ -7,10 +7,13 @@ use rustc_apfloat::ieee::{Double, Single}; use rustc_apfloat::Float; use rustc_ast as ast; use rustc_attr::{SignedInt, UnsignedInt}; +use rustc_data_structures::fx::FxHashMap; use rustc_hir as hir; -use rustc_hir::def::{CtorKind, DefKind, Namespace}; -use rustc_hir::def_id::{CrateNum, DefId, CRATE_DEF_INDEX, LOCAL_CRATE}; +use rustc_hir::def::{self, CtorKind, DefKind, Namespace}; +use rustc_hir::def_id::{CrateNum, DefId, DefIdSet, CRATE_DEF_INDEX, LOCAL_CRATE}; use rustc_hir::definitions::{DefPathData, DisambiguatedDefPathData}; +use rustc_hir::ItemKind; +use rustc_session::config::TrimmedDefPaths; use rustc_span::symbol::{kw, Ident, Symbol}; use rustc_target::abi::{Integer, Size}; use rustc_target::spec::abi::Abi; @@ -52,6 +55,7 @@ macro_rules! define_scoped_cx { thread_local! { static FORCE_IMPL_FILENAME_LINE: Cell<bool> = Cell::new(false); static SHOULD_PREFIX_WITH_CRATE: Cell<bool> = Cell::new(false); + static NO_TRIMMED_PATH: Cell<bool> = Cell::new(false); static NO_QUERIES: Cell<bool> = Cell::new(false); } @@ -94,6 +98,18 @@ pub fn with_crate_prefix<F: FnOnce() -> R, R>(f: F) -> R { }) } +/// Prevent path trimming if it is turned on. Path trimming affects `Display` impl +/// of various rustc types, for example `std::vec::Vec` would be trimmed to `Vec`, +/// if no other `Vec` is found. +pub fn with_no_trimmed_paths<F: FnOnce() -> R, R>(f: F) -> R { + NO_TRIMMED_PATH.with(|flag| { + let old = flag.replace(true); + let result = f(); + flag.set(old); + result + }) +} + /// The "region highlights" are used to control region printing during /// specific error messages. When a "region highlight" is enabled, it /// gives an alternate way to print specific regions. For now, we @@ -243,6 +259,28 @@ pub trait PrettyPrinter<'tcx>: self.try_print_visible_def_path_recur(def_id, &mut callers) } + /// Try to see if this path can be trimmed to a unique symbol name. + fn try_print_trimmed_def_path( + mut self, + def_id: DefId, + ) -> Result<(Self::Path, bool), Self::Error> { + if !self.tcx().sess.opts.debugging_opts.trim_diagnostic_paths + || matches!(self.tcx().sess.opts.trimmed_def_paths, TrimmedDefPaths::Never) + || NO_TRIMMED_PATH.with(|flag| flag.get()) + || SHOULD_PREFIX_WITH_CRATE.with(|flag| flag.get()) + { + return Ok((self, false)); + } + + match self.tcx().trimmed_def_paths(LOCAL_CRATE).get(&def_id) { + None => return Ok((self, false)), + Some(symbol) => { + self.write_str(&symbol.as_str())?; + return Ok((self, true)); + } + } + } + /// Does the work of `try_print_visible_def_path`, building the /// full definition path recursively before attempting to /// post-process it into the valid and visible version that @@ -1324,6 +1362,11 @@ impl<F: fmt::Write> Printer<'tcx> for FmtPrinter<'_, 'tcx, F> { define_scoped_cx!(self); if substs.is_empty() { + match self.try_print_trimmed_def_path(def_id)? { + (cx, true) => return Ok(cx), + (cx, false) => self = cx, + } + match self.try_print_visible_def_path(def_id)? { (cx, true) => return Ok(cx), (cx, false) => self = cx, @@ -2064,3 +2107,134 @@ define_print_and_forward_display! { } } } + +fn for_each_def(tcx: TyCtxt<'_>, mut collect_fn: impl for<'b> FnMut(&'b Ident, Namespace, DefId)) { + // Iterate all local crate items no matter where they are defined. + let hir = tcx.hir(); + for item in hir.krate().items.values() { + if item.ident.name.as_str().is_empty() { + continue; + } + + match item.kind { + ItemKind::Use(_, _) => { + continue; + } + _ => {} + } + + if let Some(local_def_id) = hir.definitions().opt_hir_id_to_local_def_id(item.hir_id) { + let def_id = local_def_id.to_def_id(); + let ns = tcx.def_kind(def_id).ns().unwrap_or(Namespace::TypeNS); + collect_fn(&item.ident, ns, def_id); + } + } + + // Now take care of extern crate items. + let queue = &mut Vec::new(); + let mut seen_defs: DefIdSet = Default::default(); + + for &cnum in tcx.crates().iter() { + let def_id = DefId { krate: cnum, index: CRATE_DEF_INDEX }; + + // Ignore crates that are not direct dependencies. + match tcx.extern_crate(def_id) { + None => continue, + Some(extern_crate) => { + if !extern_crate.is_direct() { + continue; + } + } + } + + queue.push(def_id); + } + + // Iterate external crate defs but be mindful about visibility + while let Some(def) = queue.pop() { + for child in tcx.item_children(def).iter() { + if child.vis != ty::Visibility::Public { + continue; + } + + match child.res { + def::Res::Def(DefKind::AssocTy, _) => {} + def::Res::Def(defkind, def_id) => { + if let Some(ns) = defkind.ns() { + collect_fn(&child.ident, ns, def_id); + } + + if seen_defs.insert(def_id) { + queue.push(def_id); + } + } + _ => {} + } + } + } +} + +/// The purpose of this function is to collect public symbols names that are unique across all +/// crates in the build. Later, when printing about types we can use those names instead of the +/// full exported path to them. +/// +/// So essentially, if a symbol name can only be imported from one place for a type, and as +/// long as it was not glob-imported anywhere in the current crate, we can trim its printed +/// path and print only the name. +/// +/// This has wide implications on error messages with types, for example, shortening +/// `std::vec::Vec` to just `Vec`, as long as there is no other `Vec` importable anywhere. +/// +/// The implementation uses similar import discovery logic to that of 'use' suggestions. +fn trimmed_def_paths(tcx: TyCtxt<'_>, crate_num: CrateNum) -> FxHashMap<DefId, Symbol> { + assert_eq!(crate_num, LOCAL_CRATE); + + let mut map = FxHashMap::default(); + + if let TrimmedDefPaths::GoodPath = tcx.sess.opts.trimmed_def_paths { + // For good paths causing this bug, the `rustc_middle::ty::print::with_no_trimmed_paths` + // wrapper can be used to suppress this query, in exchange for full paths being formatted. + tcx.sess.delay_good_path_bug("trimmed_def_paths constructed"); + } + + let unique_symbols_rev: &mut FxHashMap<(Namespace, Symbol), Option<DefId>> = + &mut FxHashMap::default(); + + for symbol_set in tcx.glob_map.values() { + for symbol in symbol_set { + unique_symbols_rev.insert((Namespace::TypeNS, *symbol), None); + unique_symbols_rev.insert((Namespace::ValueNS, *symbol), None); + unique_symbols_rev.insert((Namespace::MacroNS, *symbol), None); + } + } + + for_each_def(tcx, |ident, ns, def_id| { + use std::collections::hash_map::Entry::{Occupied, Vacant}; + + match unique_symbols_rev.entry((ns, ident.name)) { + Occupied(mut v) => match v.get() { + None => {} + Some(existing) => { + if *existing != def_id { + v.insert(None); + } + } + }, + Vacant(v) => { + v.insert(Some(def_id)); + } + } + }); + + for ((_, symbol), opt_def_id) in unique_symbols_rev.drain() { + if let Some(def_id) = opt_def_id { + map.insert(def_id, symbol); + } + } + + map +} + +pub fn provide(providers: &mut ty::query::Providers) { + *providers = ty::query::Providers { trimmed_def_paths, ..*providers }; +} |
