Auto merge of #73996 - da-x:short-unique-paths, r=petrochenkov
diagnostics: shorten paths of unique symbols This is a step towards implementing a fix for #50310, and continuation of the discussion in [Pre-RFC: Nicer Types In Diagnostics - compiler - Rust Internals](https://internals.rust-lang.org/t/pre-rfc-nicer-types-in-diagnostics/11139). Impressed upon me from previous discussion in #21934 that an RFC for this is not needed, and I should just come up with code. The recent improvements to `use` suggestions that I've contributed have given rise to this implementation. Contrary to previous suggestions, it's rather simple logic, and I believe it only reduces the amount of cognitive load that a developer would need when reading type errors. ----- If a symbol name can only be imported from one place, and as long as it was not glob-imported anywhere in the current crate, we can trim its printed path to the last component. 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 from anywhere.
This commit is contained in:
commit
af3c6e733a
1316 changed files with 4820 additions and 4497 deletions
|
@ -13,6 +13,7 @@ use rustc_hir as hir;
|
|||
use rustc_hir::def::DefKind;
|
||||
use rustc_hir::def_id::{CrateNum, DefId, CRATE_DEF_INDEX};
|
||||
use rustc_hir::{self, HirId};
|
||||
use rustc_middle::ty::print::with_no_trimmed_paths;
|
||||
use rustc_session::lint::builtin::{DEPRECATED, DEPRECATED_IN_FUTURE, SOFT_UNSTABLE};
|
||||
use rustc_session::lint::{BuiltinLintDiagnostics, Lint, LintBuffer};
|
||||
use rustc_session::parse::feature_err_issue;
|
||||
|
@ -308,7 +309,7 @@ impl<'tcx> TyCtxt<'tcx> {
|
|||
// #[rustc_deprecated] however wants to emit down the whole
|
||||
// hierarchy.
|
||||
if !skip || depr_entry.attr.is_since_rustc_version {
|
||||
let path = &self.def_path_str(def_id);
|
||||
let path = &with_no_trimmed_paths(|| self.def_path_str(def_id));
|
||||
let kind = self.def_kind(def_id).descr(def_id);
|
||||
let (message, lint) = deprecation_message(&depr_entry.attr, kind, path);
|
||||
late_report_deprecation(
|
||||
|
|
|
@ -108,6 +108,7 @@ use rustc_data_structures::sync::{HashMapExt, Lock};
|
|||
use rustc_data_structures::tiny_list::TinyList;
|
||||
use rustc_hir::def_id::DefId;
|
||||
use rustc_macros::HashStable;
|
||||
use rustc_middle::ty::print::with_no_trimmed_paths;
|
||||
use rustc_serialize::{Decodable, Encodable};
|
||||
use rustc_target::abi::{Endian, Size};
|
||||
|
||||
|
@ -145,7 +146,7 @@ pub struct GlobalId<'tcx> {
|
|||
|
||||
impl GlobalId<'tcx> {
|
||||
pub fn display(self, tcx: TyCtxt<'tcx>) -> String {
|
||||
let instance_name = tcx.def_path_str(self.instance.def.def_id());
|
||||
let instance_name = with_no_trimmed_paths(|| tcx.def_path_str(self.instance.def.def_id()));
|
||||
if let Some(promoted) = self.promoted {
|
||||
format!("{}::{:?}", instance_name, promoted)
|
||||
} else {
|
||||
|
|
|
@ -1255,6 +1255,11 @@ rustc_queries! {
|
|||
storage(ArenaCacheSelector<'tcx>)
|
||||
desc { "calculating the visible parent map" }
|
||||
}
|
||||
query trimmed_def_paths(_: CrateNum)
|
||||
-> FxHashMap<DefId, Symbol> {
|
||||
storage(ArenaCacheSelector<'tcx>)
|
||||
desc { "calculating trimmed def paths" }
|
||||
}
|
||||
query missing_extern_crate_item(_: CrateNum) -> bool {
|
||||
eval_always
|
||||
desc { "seeing if we're missing an `extern crate` item for this crate" }
|
||||
|
|
|
@ -944,7 +944,7 @@ pub struct GlobalCtxt<'tcx> {
|
|||
maybe_unused_extern_crates: Vec<(LocalDefId, Span)>,
|
||||
/// A map of glob use to a set of names it actually imports. Currently only
|
||||
/// used in save-analysis.
|
||||
glob_map: FxHashMap<LocalDefId, FxHashSet<Symbol>>,
|
||||
pub(crate) glob_map: FxHashMap<LocalDefId, FxHashSet<Symbol>>,
|
||||
/// Extern prelude entries. The value is `true` if the entry was introduced
|
||||
/// via `extern crate` item and not `--extern` option or compiler built-in.
|
||||
pub extern_prelude: FxHashMap<Symbol, bool>,
|
||||
|
|
|
@ -260,10 +260,11 @@ impl<'tcx> fmt::Display for Instance<'tcx> {
|
|||
InstanceDef::ReifyShim(_) => write!(f, " - shim(reify)"),
|
||||
InstanceDef::Intrinsic(_) => write!(f, " - intrinsic"),
|
||||
InstanceDef::Virtual(_, num) => write!(f, " - virtual#{}", num),
|
||||
InstanceDef::FnPtrShim(_, ty) => write!(f, " - shim({:?})", ty),
|
||||
InstanceDef::FnPtrShim(_, ty) => write!(f, " - shim({})", ty),
|
||||
InstanceDef::ClosureOnceShim { .. } => write!(f, " - shim"),
|
||||
InstanceDef::DropGlue(_, ty) => write!(f, " - shim({:?})", ty),
|
||||
InstanceDef::CloneShim(_, ty) => write!(f, " - shim({:?})", ty),
|
||||
InstanceDef::DropGlue(_, None) => write!(f, " - shim(None)"),
|
||||
InstanceDef::DropGlue(_, Some(ty)) => write!(f, " - shim(Some({}))", ty),
|
||||
InstanceDef::CloneShim(_, ty) => write!(f, " - shim({})", ty),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -174,9 +174,9 @@ pub enum LayoutError<'tcx> {
|
|||
impl<'tcx> fmt::Display for LayoutError<'tcx> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match *self {
|
||||
LayoutError::Unknown(ty) => write!(f, "the type `{:?}` has an unknown layout", ty),
|
||||
LayoutError::Unknown(ty) => write!(f, "the type `{}` has an unknown layout", ty),
|
||||
LayoutError::SizeOverflow(ty) => {
|
||||
write!(f, "the type `{:?}` is too big for the current architecture", ty)
|
||||
write!(f, "the type `{}` is too big for the current architecture", ty)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3101,6 +3101,7 @@ pub fn provide(providers: &mut ty::query::Providers) {
|
|||
erase_regions::provide(providers);
|
||||
layout::provide(providers);
|
||||
util::provide(providers);
|
||||
print::provide(providers);
|
||||
super::util::bug::provide(providers);
|
||||
*providers = ty::query::Providers {
|
||||
trait_impls_of: trait_def::trait_impls_of_provider,
|
||||
|
|
|
@ -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 };
|
||||
}
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
use crate::mir::interpret;
|
||||
use crate::mir::ProjectionKind;
|
||||
use crate::ty::fold::{TypeFoldable, TypeFolder, TypeVisitor};
|
||||
use crate::ty::print::{FmtPrinter, Printer};
|
||||
use crate::ty::print::{with_no_trimmed_paths, FmtPrinter, Printer};
|
||||
use crate::ty::{self, InferConst, Lift, Ty, TyCtxt};
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::def::Namespace;
|
||||
|
@ -20,7 +20,9 @@ use std::sync::Arc;
|
|||
impl fmt::Debug for ty::TraitDef {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
ty::tls::with(|tcx| {
|
||||
FmtPrinter::new(tcx, f, Namespace::TypeNS).print_def_path(self.def_id, &[])?;
|
||||
with_no_trimmed_paths(|| {
|
||||
FmtPrinter::new(tcx, f, Namespace::TypeNS).print_def_path(self.def_id, &[])
|
||||
})?;
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
|
@ -29,7 +31,9 @@ impl fmt::Debug for ty::TraitDef {
|
|||
impl fmt::Debug for ty::AdtDef {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
ty::tls::with(|tcx| {
|
||||
FmtPrinter::new(tcx, f, Namespace::TypeNS).print_def_path(self.did, &[])?;
|
||||
with_no_trimmed_paths(|| {
|
||||
FmtPrinter::new(tcx, f, Namespace::TypeNS).print_def_path(self.did, &[])
|
||||
})?;
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
|
@ -50,7 +54,7 @@ impl fmt::Debug for ty::UpvarBorrow<'tcx> {
|
|||
|
||||
impl fmt::Debug for ty::ExistentialTraitRef<'tcx> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
fmt::Display::fmt(self, f)
|
||||
with_no_trimmed_paths(|| fmt::Display::fmt(self, f))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -183,13 +187,13 @@ impl fmt::Debug for ty::FloatVarValue {
|
|||
|
||||
impl fmt::Debug for ty::TraitRef<'tcx> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
fmt::Display::fmt(self, f)
|
||||
with_no_trimmed_paths(|| fmt::Display::fmt(self, f))
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for Ty<'tcx> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
fmt::Display::fmt(self, f)
|
||||
with_no_trimmed_paths(|| fmt::Display::fmt(self, f))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue