diff --git a/src/librustdoc/html/ambiguity.rs b/src/librustdoc/html/ambiguity.rs new file mode 100644 index 0000000000000..4a4dc5928473b --- /dev/null +++ b/src/librustdoc/html/ambiguity.rs @@ -0,0 +1,202 @@ +use rustc_data_structures::fx::FxHashMap; +use rustc_hir::def_id::DefId; +use rustc_span::Symbol; + +use crate::clean::{self, FnDecl, Function}; + +#[derive(Debug, Clone)] +pub struct PathLike<'a> { + path: &'a clean::Path, + suffix_len: usize, +} + +impl<'a> PathLike<'a> { + pub fn make_longer(self) -> Self { + let Self { path, suffix_len } = self; + assert!(suffix_len < path.segments.len(), "{self:?}"); + Self { path, suffix_len: suffix_len + 1 } + } + + pub fn iter_from_end(&self) -> impl DoubleEndedIterator { + self.path.segments.iter().map(|seg| &seg.name).rev().take(self.suffix_len) + } +} + +impl<'a> Eq for PathLike<'a> {} +impl<'a> PartialEq for PathLike<'a> { + fn eq(&self, other: &Self) -> bool { + if self.suffix_len != other.suffix_len { + return false; + } + let self_suffix = self.iter_from_end(); + let other_suffix = other.iter_from_end(); + self_suffix.zip(other_suffix).all(|(s, o)| s.eq(o)) + } +} + +impl<'a> std::hash::Hash for PathLike<'a> { + fn hash(&self, state: &mut H) { + for sym in self.iter_from_end() { + sym.hash(state) + } + } +} + +#[derive(Debug, Clone)] +pub struct AmbiguityTable<'a> { + inner: FxHashMap>, +} + +impl<'a> AmbiguityTable<'a> { + pub fn empty() -> Self { + Self { inner: FxHashMap::default() } + } + + // pub fn is_empty(&self) -> bool { + // self.inner.is_empty() + // } + + pub fn build() -> AmbiguityTableBuilder<'a> { + AmbiguityTableBuilder { inner: FxHashMap::default() } + } + + #[allow(rustc::pass_by_value)] + pub fn get(&self, x: &DefId) -> Option<&PathLike<'a>> { + self.inner.get(x) + } + + pub fn build_fn_decl(decl: &'a FnDecl) -> Self { + let mut builder = Self::build(); + builder.add_fndecl(decl); + builder.finnish() + } + + pub fn build_fn(f: &'a Function) -> Self { + let mut builder = Self::build(); + builder.add_fn(f); + builder.finnish() + } +} + +enum AmbiguityTableBuilderEntry { + MapsToOne(DefId), + IsAmbiguous, +} + +pub struct AmbiguityTableBuilder<'a> { + inner: FxHashMap, AmbiguityTableBuilderEntry>, +} + +impl<'a> AmbiguityTableBuilder<'a> { + // Invariant: must start with length 1 path view + fn add_path_view(&mut self, p: PathLike<'a>, did: DefId) { + use std::collections::hash_map::Entry::*; + match self.inner.entry(p.clone()) { + Occupied(entry) => { + match entry.get() { + AmbiguityTableBuilderEntry::MapsToOne(other_did) if other_did == &did => return, + _ => (), + } + let (other_p, v) = entry.replace_entry(AmbiguityTableBuilderEntry::IsAmbiguous); + // dbg!(&other_p, &p); + match v { + AmbiguityTableBuilderEntry::MapsToOne(other_did) => { + self.add_path_view(other_p.make_longer(), other_did) + } + AmbiguityTableBuilderEntry::IsAmbiguous => (), + } + self.add_path_view(p.make_longer(), did) + } + Vacant(entry) => { + entry.insert(AmbiguityTableBuilderEntry::MapsToOne(did)); + } + } + } + + fn add_path(&mut self, path: &'a clean::Path) { + let pv = PathLike { path, suffix_len: 1 }; + self.add_path_view(pv, path.def_id()) + } + + fn add_generic_bound(&mut self, generic_bound: &'a clean::GenericBound) { + match generic_bound { + clean::GenericBound::TraitBound(poly_trait, _) => self.add_poly_trait(poly_trait), + clean::GenericBound::Outlives(_) => (), + } + } + + fn add_poly_trait(&mut self, poly_trait: &'a clean::PolyTrait) { + self.add_path(&poly_trait.trait_); + for gen_param in &poly_trait.generic_params { + use clean::GenericParamDefKind::*; + match &gen_param.kind { + Type { bounds, .. } => { + for bnd in bounds { + self.add_generic_bound(bnd) + } + } + Lifetime { .. } | Const { .. } => (), + } + } + } + + fn add_type(&mut self, ty: &'a clean::Type) { + match ty { + clean::Type::Path { path } => self.add_path(path), + clean::Type::Tuple(tys) => { + for ty in tys { + self.add_type(ty) + } + } + clean::Type::RawPointer(_, ty) + | clean::Type::Slice(ty) + | clean::Type::BorrowedRef { type_: ty, .. } + | clean::Type::Array(ty, _) => self.add_type(ty), + + clean::Type::DynTrait(poly_trait, _) => { + for trai in poly_trait { + self.add_poly_trait(trai) + } + } + + clean::Type::BareFunction(bare_func_decl) => { + let clean::FnDecl { output, inputs, .. } = &bare_func_decl.decl; + self.add_type(output); + for inpt in &inputs.values { + self.add_type(&inpt.type_) + } + } + clean::Type::ImplTrait(bnds) => { + for bnd in bnds { + self.add_generic_bound(bnd) + } + } + clean::Type::Infer + | clean::Type::QPath(_) + | clean::Type::Primitive(_) + | clean::Type::Generic(_) => (), + } + } + + fn add_fndecl(&mut self, decl: &'a FnDecl) { + for arg in &decl.inputs.values { + self.add_type(&arg.type_); + } + self.add_type(&decl.output); + } + + pub fn add_fn(&mut self, f: &'a Function) { + self.add_fndecl(&f.decl); + } + + pub fn finnish(self) -> AmbiguityTable<'a> { + let mut inner = FxHashMap::default(); + for (path_view, did) in self.inner { + if let AmbiguityTableBuilderEntry::MapsToOne(did) = did { + let hopefully_none = inner.insert(did, path_view); + assert!(hopefully_none.is_none()); + } + } + AmbiguityTable { inner } + } +} diff --git a/src/librustdoc/html/format.rs b/src/librustdoc/html/format.rs index 312765d3e6d03..e2850a29fbdaa 100644 --- a/src/librustdoc/html/format.rs +++ b/src/librustdoc/html/format.rs @@ -37,6 +37,7 @@ use crate::html::escape::Escape; use crate::html::render::Context; use crate::passes::collect_intra_doc_links::UrlFragment; +use super::ambiguity::AmbiguityTable; use super::url_parts_builder::estimate_item_path_byte_length; use super::url_parts_builder::UrlPartsBuilder; @@ -167,6 +168,7 @@ pub(crate) fn comma_sep( pub(crate) fn print_generic_bounds<'a, 'tcx: 'a>( bounds: &'a [clean::GenericBound], cx: &'a Context<'tcx>, + at: &'a AmbiguityTable<'_>, ) -> impl Display + 'a + Captures<'tcx> { display_fn(move |f| { let mut bounds_dup = FxHashSet::default(); @@ -175,7 +177,7 @@ pub(crate) fn print_generic_bounds<'a, 'tcx: 'a>( if i > 0 { f.write_str(" + ")?; } - bound.print(cx).fmt(f)?; + bound.print(cx, at).fmt(f)?; } Ok(()) }) @@ -185,6 +187,7 @@ impl clean::GenericParamDef { pub(crate) fn print<'a, 'tcx: 'a>( &'a self, cx: &'a Context<'tcx>, + at: &'a AmbiguityTable<'_>, ) -> impl Display + 'a + Captures<'tcx> { display_fn(move |f| match &self.kind { clean::GenericParamDefKind::Lifetime { outlives } => { @@ -207,19 +210,19 @@ impl clean::GenericParamDef { if !bounds.is_empty() { f.write_str(": ")?; - print_generic_bounds(bounds, cx).fmt(f)?; + print_generic_bounds(bounds, cx, at).fmt(f)?; } if let Some(ref ty) = default { f.write_str(" = ")?; - ty.print(cx).fmt(f)?; + ty.print(cx, at).fmt(f)?; } Ok(()) } clean::GenericParamDefKind::Const { ty, default, .. } => { write!(f, "const {}: ", self.name)?; - ty.print(cx).fmt(f)?; + ty.print(cx, at).fmt(f)?; if let Some(default) = default { f.write_str(" = ")?; @@ -240,6 +243,7 @@ impl clean::Generics { pub(crate) fn print<'a, 'tcx: 'a>( &'a self, cx: &'a Context<'tcx>, + at: &'a AmbiguityTable<'_>, ) -> impl Display + 'a + Captures<'tcx> { display_fn(move |f| { let mut real_params = self.params.iter().filter(|p| !p.is_synthetic_param()).peekable(); @@ -248,9 +252,9 @@ impl clean::Generics { } if f.alternate() { - write!(f, "<{:#}>", comma_sep(real_params.map(|g| g.print(cx)), true)) + write!(f, "<{:#}>", comma_sep(real_params.map(|g| g.print(cx, at)), true)) } else { - write!(f, "<{}>", comma_sep(real_params.map(|g| g.print(cx)), true)) + write!(f, "<{}>", comma_sep(real_params.map(|g| g.print(cx, at)), true)) } }) } @@ -268,6 +272,7 @@ pub(crate) enum Ending { pub(crate) fn print_where_clause<'a, 'tcx: 'a>( gens: &'a clean::Generics, cx: &'a Context<'tcx>, + at: &'a AmbiguityTable<'_>, indent: usize, ending: Ending, ) -> impl Display + 'a + Captures<'tcx> { @@ -285,12 +290,12 @@ pub(crate) fn print_where_clause<'a, 'tcx: 'a>( match pred { clean::WherePredicate::BoundPredicate { ty, bounds, bound_params } => { - print_higher_ranked_params_with_space(bound_params, cx).fmt(f)?; - ty.print(cx).fmt(f)?; + print_higher_ranked_params_with_space(bound_params, cx, at).fmt(f)?; + ty.print(cx, at).fmt(f)?; f.write_str(":")?; if !bounds.is_empty() { f.write_str(" ")?; - print_generic_bounds(bounds, cx).fmt(f)?; + print_generic_bounds(bounds, cx, at).fmt(f)?; } Ok(()) } @@ -299,15 +304,15 @@ pub(crate) fn print_where_clause<'a, 'tcx: 'a>( // the lifetime nor the bounds contain any characters which need escaping. write!(f, "{}:", lifetime.print())?; if !bounds.is_empty() { - write!(f, " {}", print_generic_bounds(bounds, cx))?; + write!(f, " {}", print_generic_bounds(bounds, cx, at))?; } Ok(()) } clean::WherePredicate::EqPredicate { lhs, rhs } => { if f.alternate() { - write!(f, "{:#} == {:#}", lhs.print(cx), rhs.print(cx)) + write!(f, "{:#} == {:#}", lhs.print(cx, at), rhs.print(cx, at)) } else { - write!(f, "{} == {}", lhs.print(cx), rhs.print(cx)) + write!(f, "{} == {}", lhs.print(cx, at), rhs.print(cx, at)) } } } @@ -387,10 +392,14 @@ impl clean::Constant { } impl clean::PolyTrait { - fn print<'a, 'tcx: 'a>(&'a self, cx: &'a Context<'tcx>) -> impl Display + 'a + Captures<'tcx> { + fn print<'a, 'tcx: 'a>( + &'a self, + cx: &'a Context<'tcx>, + at: &'a AmbiguityTable<'_>, + ) -> impl Display + 'a + Captures<'tcx> { display_fn(move |f| { - print_higher_ranked_params_with_space(&self.generic_params, cx).fmt(f)?; - self.trait_.print(cx).fmt(f) + print_higher_ranked_params_with_space(&self.generic_params, cx, at).fmt(f)?; + self.trait_.print(cx, at).fmt(f) }) } } @@ -399,6 +408,7 @@ impl clean::GenericBound { pub(crate) fn print<'a, 'tcx: 'a>( &'a self, cx: &'a Context<'tcx>, + at: &'a AmbiguityTable<'_>, ) -> impl Display + 'a + Captures<'tcx> { display_fn(move |f| match self { clean::GenericBound::Outlives(lt) => write!(f, "{}", lt.print()), @@ -410,14 +420,18 @@ impl clean::GenericBound { // `const` and `~const` trait bounds are experimental; don't render them. hir::TraitBoundModifier::Const | hir::TraitBoundModifier::MaybeConst => "", })?; - ty.print(cx).fmt(f) + ty.print(cx, at).fmt(f) } }) } } impl clean::GenericArgs { - fn print<'a, 'tcx: 'a>(&'a self, cx: &'a Context<'tcx>) -> impl Display + 'a + Captures<'tcx> { + fn print<'a, 'tcx: 'a>( + &'a self, + cx: &'a Context<'tcx>, + at: &'a AmbiguityTable<'_>, + ) -> impl Display + 'a + Captures<'tcx> { display_fn(move |f| { match self { clean::GenericArgs::AngleBracketed { args, bindings } => { @@ -434,9 +448,9 @@ impl clean::GenericArgs { } comma = true; if f.alternate() { - write!(f, "{:#}", arg.print(cx))?; + write!(f, "{:#}", arg.print(cx, at))?; } else { - write!(f, "{}", arg.print(cx))?; + write!(f, "{}", arg.print(cx, at))?; } } for binding in bindings.iter() { @@ -445,9 +459,9 @@ impl clean::GenericArgs { } comma = true; if f.alternate() { - write!(f, "{:#}", binding.print(cx))?; + write!(f, "{:#}", binding.print(cx, at))?; } else { - write!(f, "{}", binding.print(cx))?; + write!(f, "{}", binding.print(cx, at))?; } } if f.alternate() { @@ -465,14 +479,14 @@ impl clean::GenericArgs { f.write_str(", ")?; } comma = true; - ty.print(cx).fmt(f)?; + ty.print(cx, at).fmt(f)?; } f.write_str(")")?; if let Some(ref ty) = *output { if f.alternate() { - write!(f, " -> {:#}", ty.print(cx))?; + write!(f, " -> {:#}", ty.print(cx, at))?; } else { - write!(f, " -> {}", ty.print(cx))?; + write!(f, " -> {}", ty.print(cx, at))?; } } } @@ -827,6 +841,7 @@ fn resolved_path<'cx>( print_all: bool, use_absolute: bool, cx: &'cx Context<'_>, + at: &AmbiguityTable<'_>, ) -> fmt::Result { let last = path.segments.last().unwrap(); @@ -836,7 +851,7 @@ fn resolved_path<'cx>( } } if w.alternate() { - write!(w, "{}{:#}", &last.name, last.args.print(cx))?; + write!(w, "{}{:#}", &last.name, last.args.print(cx, at))?; } else { let path = if use_absolute { if let Ok((_, _, fqp)) = href(did, cx) { @@ -849,9 +864,19 @@ fn resolved_path<'cx>( last.name.to_string() } } else { - anchor(did, last.name, cx).to_string() + match at.get(&did) { + Some(path_view) => { + let mut iter = path_view.iter_from_end(); + let end = iter.next().unwrap(); + for seg in iter.rev() { + write!(w, "{}::", seg)?; + } + anchor(did, *end, cx).to_string() + } + None => anchor(did, last.name, cx).to_string(), + } }; - write!(w, "{path}{args}", args = last.args.print(cx))?; + write!(w, "{path}{args}", args = last.args.print(cx, at))?; } Ok(()) } @@ -935,13 +960,14 @@ fn tybounds<'a, 'tcx: 'a>( bounds: &'a [clean::PolyTrait], lt: &'a Option, cx: &'a Context<'tcx>, + at: &'a AmbiguityTable<'_>, ) -> impl Display + 'a + Captures<'tcx> { display_fn(move |f| { for (i, bound) in bounds.iter().enumerate() { if i > 0 { write!(f, " + ")?; } - bound.print(cx).fmt(f)?; + bound.print(cx, at).fmt(f)?; } if let Some(lt) = lt { // We don't need to check `alternate` since we can be certain that @@ -955,11 +981,12 @@ fn tybounds<'a, 'tcx: 'a>( fn print_higher_ranked_params_with_space<'a, 'tcx: 'a>( params: &'a [clean::GenericParamDef], cx: &'a Context<'tcx>, + at: &'a AmbiguityTable<'_>, ) -> impl Display + 'a + Captures<'tcx> { display_fn(move |f| { if !params.is_empty() { f.write_str(if f.alternate() { "for<" } else { "for<" })?; - comma_sep(params.iter().map(|lt| lt.print(cx)), true).fmt(f)?; + comma_sep(params.iter().map(|lt| lt.print(cx, at)), true).fmt(f)?; f.write_str(if f.alternate() { "> " } else { "> " })?; } Ok(()) @@ -990,6 +1017,7 @@ fn fmt_type<'cx>( f: &mut fmt::Formatter<'_>, use_absolute: bool, cx: &'cx Context<'_>, + at: &AmbiguityTable<'_>, ) -> fmt::Result { trace!("fmt_type(t = {t:?})"); @@ -998,11 +1026,11 @@ fn fmt_type<'cx>( clean::Type::Path { ref path } => { // Paths like `T::Output` and `Self::Output` should be rendered with all segments. let did = path.def_id(); - resolved_path(f, did, path, path.is_assoc_ty(), use_absolute, cx) + resolved_path(f, did, path, path.is_assoc_ty(), use_absolute, cx, at) } clean::DynTrait(ref bounds, ref lt) => { f.write_str("dyn ")?; - tybounds(bounds, lt, cx).fmt(f) + tybounds(bounds, lt, cx, at).fmt(f) } clean::Infer => write!(f, "_"), clean::Primitive(clean::PrimitiveType::Never) => { @@ -1012,7 +1040,7 @@ fn fmt_type<'cx>( primitive_link(f, prim, format_args!("{}", prim.as_sym().as_str()), cx) } clean::BareFunction(ref decl) => { - print_higher_ranked_params_with_space(&decl.generic_params, cx).fmt(f)?; + print_higher_ranked_params_with_space(&decl.generic_params, cx, at).fmt(f)?; decl.unsafety.print_with_space().fmt(f)?; print_abi_with_space(decl.abi).fmt(f)?; if f.alternate() { @@ -1020,7 +1048,7 @@ fn fmt_type<'cx>( } else { primitive_link(f, PrimitiveType::Fn, format_args!("fn"), cx)?; } - decl.decl.print(cx).fmt(f) + decl.decl.print(cx, at).fmt(f) } clean::Tuple(ref typs) => match &typs[..] { &[] => primitive_link(f, PrimitiveType::Unit, format_args!("()"), cx), @@ -1029,7 +1057,7 @@ fn fmt_type<'cx>( primitive_link(f, PrimitiveType::Tuple, format_args!("({name},)"), cx) } else { write!(f, "(")?; - one.print(cx).fmt(f)?; + one.print(cx, at).fmt(f)?; write!(f, ",)") } } @@ -1055,7 +1083,7 @@ fn fmt_type<'cx>( if i != 0 { write!(f, ", ")?; } - item.print(cx).fmt(f)?; + item.print(cx, at).fmt(f)?; } write!(f, ")") } @@ -1067,7 +1095,7 @@ fn fmt_type<'cx>( } _ => { write!(f, "[")?; - t.print(cx).fmt(f)?; + t.print(cx, at).fmt(f)?; write!(f, "]") } }, @@ -1080,7 +1108,7 @@ fn fmt_type<'cx>( ), _ => { write!(f, "[")?; - t.print(cx).fmt(f)?; + t.print(cx, at).fmt(f)?; if f.alternate() { write!(f, "; {n}")?; } else { @@ -1102,7 +1130,7 @@ fn fmt_type<'cx>( }; if matches!(**t, clean::Generic(_)) || t.is_assoc_ty() { - let ty = t.print(cx); + let ty = t.print(cx, at); if f.alternate() { primitive_link( f, @@ -1120,7 +1148,7 @@ fn fmt_type<'cx>( } } else { primitive_link(f, clean::PrimitiveType::RawPointer, format_args!("*{m} "), cx)?; - t.print(cx).fmt(f) + t.print(cx, at).fmt(f) } } clean::BorrowedRef { lifetime: ref l, mutability, type_: ref ty } => { @@ -1154,7 +1182,7 @@ fn fmt_type<'cx>( if needs_parens { f.write_str("(")?; } - fmt_type(ty, f, use_absolute, cx)?; + fmt_type(ty, f, use_absolute, cx, at)?; if needs_parens { f.write_str(")")?; } @@ -1162,7 +1190,7 @@ fn fmt_type<'cx>( } clean::ImplTrait(ref bounds) => { f.write_str("impl ")?; - print_generic_bounds(bounds, cx).fmt(f) + print_generic_bounds(bounds, cx, at).fmt(f) } clean::QPath(box clean::QPathData { ref assoc, @@ -1177,17 +1205,17 @@ fn fmt_type<'cx>( if let Some(trait_) = trait_ && should_show_cast { - write!(f, "<{:#} as {:#}>::", self_type.print(cx), trait_.print(cx))? + write!(f, "<{:#} as {:#}>::", self_type.print(cx, at), trait_.print(cx, at))? } else { - write!(f, "{:#}::", self_type.print(cx))? + write!(f, "{:#}::", self_type.print(cx, at))? } } else { if let Some(trait_) = trait_ && should_show_cast { - write!(f, "<{} as {}>::", self_type.print(cx), trait_.print(cx))? + write!(f, "<{} as {}>::", self_type.print(cx, at), trait_.print(cx, at))? } else { - write!(f, "{}::", self_type.print(cx))? + write!(f, "{}::", self_type.print(cx, at))? } }; // It's pretty unsightly to look at `::C` in output, and @@ -1234,7 +1262,7 @@ fn fmt_type<'cx>( write!(f, "{}", assoc.name) }?; - assoc.args.print(cx).fmt(f) + assoc.args.print(cx, at).fmt(f) } } } @@ -1243,8 +1271,9 @@ impl clean::Type { pub(crate) fn print<'b, 'a: 'b, 'tcx: 'a>( &'a self, cx: &'a Context<'tcx>, + at: &'a AmbiguityTable<'_>, ) -> impl Display + 'b + Captures<'tcx> { - display_fn(move |f| fmt_type(self, f, false, cx)) + display_fn(move |f| fmt_type(self, f, false, cx, at)) } } @@ -1252,8 +1281,9 @@ impl clean::Path { pub(crate) fn print<'b, 'a: 'b, 'tcx: 'a>( &'a self, cx: &'a Context<'tcx>, + at: &'b AmbiguityTable<'_>, ) -> impl Display + 'b + Captures<'tcx> { - display_fn(move |f| resolved_path(f, self.def_id(), self, false, false, cx)) + display_fn(move |f| resolved_path(f, self.def_id(), self, false, false, cx, at)) } } @@ -1262,10 +1292,11 @@ impl clean::Impl { &'a self, use_absolute: bool, cx: &'a Context<'tcx>, + at: &'a AmbiguityTable<'_>, ) -> impl Display + 'a + Captures<'tcx> { display_fn(move |f| { f.write_str("impl")?; - self.generics.print(cx).fmt(f)?; + self.generics.print(cx, at).fmt(f)?; f.write_str(" ")?; if let Some(ref ty) = self.trait_ { @@ -1273,7 +1304,7 @@ impl clean::Impl { ty::ImplPolarity::Positive | ty::ImplPolarity::Reservation => {} ty::ImplPolarity::Negative => write!(f, "!")?, } - ty.print(cx).fmt(f)?; + ty.print(cx, at).fmt(f)?; write!(f, " for ")?; } @@ -1298,7 +1329,7 @@ impl clean::Impl { // Hardcoded anchor library/core/src/primitive_docs.rs // Link should match `# Trait implementations` - print_higher_ranked_params_with_space(&bare_fn.generic_params, cx).fmt(f)?; + print_higher_ranked_params_with_space(&bare_fn.generic_params, cx, at).fmt(f)?; bare_fn.unsafety.print_with_space().fmt(f)?; print_abi_with_space(bare_fn.abi).fmt(f)?; let ellipsis = if bare_fn.decl.c_variadic { ", ..." } else { "" }; @@ -1312,15 +1343,15 @@ impl clean::Impl { // Write output. if !bare_fn.decl.output.is_unit() { write!(f, " -> ")?; - fmt_type(&bare_fn.decl.output, f, use_absolute, cx)?; + fmt_type(&bare_fn.decl.output, f, use_absolute, cx, at)?; } } else if let Some(ty) = self.kind.as_blanket_ty() { - fmt_type(ty, f, use_absolute, cx)?; + fmt_type(ty, f, use_absolute, cx, at)?; } else { - fmt_type(&self.for_, f, use_absolute, cx)?; + fmt_type(&self.for_, f, use_absolute, cx, at)?; } - print_where_clause(&self.generics, cx, 0, Ending::Newline).fmt(f) + print_where_clause(&self.generics, cx, at, 0, Ending::Newline).fmt(f) }) } } @@ -1329,11 +1360,12 @@ impl clean::Arguments { pub(crate) fn print<'a, 'tcx: 'a>( &'a self, cx: &'a Context<'tcx>, + at: &'a AmbiguityTable<'_>, ) -> impl Display + 'a + Captures<'tcx> { display_fn(move |f| { for (i, input) in self.values.iter().enumerate() { write!(f, "{}: ", input.name)?; - input.type_.print(cx).fmt(f)?; + input.type_.print(cx, at).fmt(f)?; if i + 1 < self.values.len() { write!(f, ", ")?; } @@ -1369,6 +1401,7 @@ impl clean::FnDecl { pub(crate) fn print<'b, 'a: 'b, 'tcx: 'a>( &'a self, cx: &'a Context<'tcx>, + at: &'b AmbiguityTable<'_>, ) -> impl Display + 'b + Captures<'tcx> { display_fn(move |f| { let ellipsis = if self.c_variadic { ", ..." } else { "" }; @@ -1376,17 +1409,17 @@ impl clean::FnDecl { write!( f, "({args:#}{ellipsis}){arrow:#}", - args = self.inputs.print(cx), + args = self.inputs.print(cx, at), ellipsis = ellipsis, - arrow = self.print_output(cx) + arrow = self.print_output(cx, at) ) } else { write!( f, "({args}{ellipsis}){arrow}", - args = self.inputs.print(cx), + args = self.inputs.print(cx, at), ellipsis = ellipsis, - arrow = self.print_output(cx) + arrow = self.print_output(cx, at) ) } }) @@ -1403,21 +1436,73 @@ impl clean::FnDecl { header_len: usize, indent: usize, cx: &'a Context<'tcx>, + at: &'a AmbiguityTable<'_>, ) -> impl Display + 'a + Captures<'tcx> { display_fn(move |f| { // First, generate the text form of the declaration, with no line wrapping, and count the bytes. let mut counter = WriteCounter(0); - write!(&mut counter, "{:#}", display_fn(|f| { self.inner_full_print(None, f, cx) })) - .unwrap(); + write!( + &mut counter, + "{:#}", + display_fn(|f| { self.inner_full_print(None, f, cx, at) }) + ) + .unwrap(); // If the text form was over 80 characters wide, we will line-wrap our output. let line_wrapping_indent = if header_len + counter.0 > 80 { Some(indent) } else { None }; // Generate the final output. This happens to accept `{:#}` formatting to get textual // output but in practice it is only formatted with `{}` to get HTML output. - self.inner_full_print(line_wrapping_indent, f, cx) + self.inner_full_print(line_wrapping_indent, f, cx, at) }) } + // fn ambiguities<'a>( + // types: impl Iterator, + // ) -> FxHashMap>> { + // fn inner( + // ty: &clean::Type, + // res_map: &mut FxHashMap>>, + // conflict_map: &mut FxHashMap>>, + // ) { + // match ty { + // clean::Type::Path { path } => { + // res_map.entry(path.def_id()).or_insert_with(|| { + // let last = path.last(); + // conflict_map + // .entry(last) + // .and_modify(|b| { + // b.replace(true); + // }) + // .or_insert_with(|| Rc::new(Cell::new(false))) + // .clone() + // }); + // } + // clean::Type::Tuple(types) => { + // for ty in types { + // inner(ty, res_map, conflict_map) + // } + // } + // clean::Type::Slice(ty) + // | clean::Type::Array(ty, _) + // | clean::Type::RawPointer(_, ty) + // | clean::Type::BorrowedRef { type_: ty, .. } => inner(ty, res_map, conflict_map), + // clean::Type::QPath(_) + // | clean::Type::Infer + // | clean::Type::ImplTrait(_) + // | clean::Type::BareFunction(_) + // | clean::Type::Primitive(_) + // | clean::Type::Generic(_) + // | clean::Type::DynTrait(_, _) => (), + // } + // } + // let mut res_map = FxHashMap::default(); + // let mut conflict_map = FxHashMap::default(); + // for ty in types { + // inner(ty, &mut res_map, &mut conflict_map) + // } + // res_map + // } + fn inner_full_print( &self, // For None, the declaration will not be line-wrapped. For Some(n), @@ -1425,6 +1510,7 @@ impl clean::FnDecl { line_wrapping_indent: Option, f: &mut fmt::Formatter<'_>, cx: &Context<'_>, + at: &AmbiguityTable<'_>, ) -> fmt::Result { let amp = if f.alternate() { "&" } else { "&" }; @@ -1463,7 +1549,7 @@ impl clean::FnDecl { } clean::SelfExplicit(ref typ) => { write!(f, "self: ")?; - typ.print(cx).fmt(f)?; + typ.print(&cx, at).fmt(f)?; } } } else { @@ -1471,7 +1557,8 @@ impl clean::FnDecl { write!(f, "const ")?; } write!(f, "{}: ", input.name)?; - input.type_.print(cx).fmt(f)?; + + fmt_type(&input.type_, f, false, &cx, at)?; } } @@ -1487,19 +1574,20 @@ impl clean::FnDecl { Some(n) => write!(f, "\n{})", Indent(n))?, }; - self.print_output(cx).fmt(f) + self.print_output(&cx, at).fmt(f) } fn print_output<'a, 'tcx: 'a>( &'a self, cx: &'a Context<'tcx>, + at: &'a AmbiguityTable<'_>, ) -> impl Display + 'a + Captures<'tcx> { display_fn(move |f| match &self.output { clean::Tuple(tys) if tys.is_empty() => Ok(()), ty if f.alternate() => { - write!(f, " -> {:#}", ty.print(cx)) + write!(f, " -> {:#}", ty.print(cx, at)) } - ty => write!(f, " -> {}", ty.print(cx)), + ty => write!(f, " -> {}", ty.print(cx, at)), }) } } @@ -1647,20 +1735,21 @@ impl clean::Import { pub(crate) fn print<'a, 'tcx: 'a>( &'a self, cx: &'a Context<'tcx>, + at: &'a AmbiguityTable<'_>, ) -> impl Display + 'a + Captures<'tcx> { display_fn(move |f| match self.kind { clean::ImportKind::Simple(name) => { if name == self.source.path.last() { - write!(f, "use {};", self.source.print(cx)) + write!(f, "use {};", self.source.print(cx, at)) } else { - write!(f, "use {source} as {name};", source = self.source.print(cx)) + write!(f, "use {source} as {name};", source = self.source.print(cx, at)) } } clean::ImportKind::Glob => { if self.source.path.segments.is_empty() { write!(f, "use *;") } else { - write!(f, "use {}::*;", self.source.print(cx)) + write!(f, "use {}::*;", self.source.print(cx, at)) } } }) @@ -1671,9 +1760,10 @@ impl clean::ImportSource { pub(crate) fn print<'a, 'tcx: 'a>( &'a self, cx: &'a Context<'tcx>, + at: &'a AmbiguityTable<'_>, ) -> impl Display + 'a + Captures<'tcx> { display_fn(move |f| match self.did { - Some(did) => resolved_path(f, did, &self.path, true, false, cx), + Some(did) => resolved_path(f, did, &self.path, true, false, cx, at), _ => { for seg in &self.path.segments[..self.path.segments.len() - 1] { write!(f, "{}::", seg.name)?; @@ -1699,19 +1789,20 @@ impl clean::TypeBinding { pub(crate) fn print<'a, 'tcx: 'a>( &'a self, cx: &'a Context<'tcx>, + at: &'a AmbiguityTable<'_>, ) -> impl Display + 'a + Captures<'tcx> { display_fn(move |f| { f.write_str(self.assoc.name.as_str())?; - self.assoc.args.print(cx).fmt(f)?; + self.assoc.args.print(cx, at).fmt(f)?; match self.kind { clean::TypeBindingKind::Equality { ref term } => { f.write_str(" = ")?; - term.print(cx).fmt(f)?; + term.print(cx, at).fmt(f)?; } clean::TypeBindingKind::Constraint { ref bounds } => { if !bounds.is_empty() { f.write_str(": ")?; - print_generic_bounds(bounds, cx).fmt(f)?; + print_generic_bounds(bounds, cx, at).fmt(f)?; } } } @@ -1738,10 +1829,11 @@ impl clean::GenericArg { pub(crate) fn print<'a, 'tcx: 'a>( &'a self, cx: &'a Context<'tcx>, + at: &'a AmbiguityTable<'_>, ) -> impl Display + 'a + Captures<'tcx> { display_fn(move |f| match self { clean::GenericArg::Lifetime(lt) => lt.print().fmt(f), - clean::GenericArg::Type(ty) => ty.print(cx).fmt(f), + clean::GenericArg::Type(ty) => ty.print(cx, at).fmt(f), clean::GenericArg::Const(ct) => ct.print(cx.tcx()).fmt(f), clean::GenericArg::Infer => Display::fmt("_", f), }) @@ -1752,9 +1844,10 @@ impl clean::Term { pub(crate) fn print<'a, 'tcx: 'a>( &'a self, cx: &'a Context<'tcx>, + at: &'a AmbiguityTable<'_>, ) -> impl Display + 'a + Captures<'tcx> { display_fn(move |f| match self { - clean::Term::Type(ty) => ty.print(cx).fmt(f), + clean::Term::Type(ty) => ty.print(cx, at).fmt(f), clean::Term::Constant(ct) => ct.print(cx.tcx()).fmt(f), }) } diff --git a/src/librustdoc/html/mod.rs b/src/librustdoc/html/mod.rs index 481ed16c05f7e..2a7835c13cfe5 100644 --- a/src/librustdoc/html/mod.rs +++ b/src/librustdoc/html/mod.rs @@ -1,3 +1,4 @@ +mod ambiguity; pub(crate) mod escape; pub(crate) mod format; pub(crate) mod highlight; diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs index f1887684797a6..6634fa8376f35 100644 --- a/src/librustdoc/html/render/mod.rs +++ b/src/librustdoc/html/render/mod.rs @@ -82,6 +82,8 @@ use crate::scrape_examples::{CallData, CallLocation}; use crate::try_none; use crate::DOC_RUST_LANG_ORG_CHANNEL; +use super::ambiguity::AmbiguityTable; + pub(crate) fn ensure_trailing_slash(v: &str) -> impl fmt::Display + '_ { crate::html::format::display_fn(move |f| { if !v.ends_with('/') && !v.is_empty() { write!(f, "{v}/") } else { f.write_str(v) } @@ -878,6 +880,7 @@ fn assoc_const( indent: usize, cx: &Context<'_>, ) { + let at = AmbiguityTable::empty(); let tcx = cx.tcx(); write!( w, @@ -886,8 +889,8 @@ fn assoc_const( vis = visibility_print_with_space(it, cx), href = assoc_href_attr(it, link, cx), name = it.name.as_ref().unwrap(), - generics = generics.print(cx), - ty = ty.print(cx), + generics = generics.print(cx, &at), + ty = ty.print(cx, &at), ); if let Some(default) = default { w.write_str(" = "); @@ -899,7 +902,7 @@ fn assoc_const( // Find a way to print constants here without all that jazz. write!(w, "{}", Escape(&default.value(tcx).unwrap_or_else(|| default.expr(tcx)))); } - write!(w, "{}", print_where_clause(generics, cx, indent, Ending::NoNewline)); + write!(w, "{}", print_where_clause(generics, cx, &at, indent, Ending::NoNewline)); } fn assoc_type( @@ -912,6 +915,7 @@ fn assoc_type( indent: usize, cx: &Context<'_>, ) { + let at = AmbiguityTable::empty(); write!( w, "{indent}{vis}type {name}{generics}", @@ -919,16 +923,16 @@ fn assoc_type( vis = visibility_print_with_space(it, cx), href = assoc_href_attr(it, link, cx), name = it.name.as_ref().unwrap(), - generics = generics.print(cx), + generics = generics.print(cx, &at), ); if !bounds.is_empty() { - write!(w, ": {}", print_generic_bounds(bounds, cx)) + write!(w, ": {}", print_generic_bounds(bounds, cx, &at)) } // Render the default before the where-clause which aligns with the new recommended style. See #89122. if let Some(default) = default { - write!(w, " = {}", default.print(cx)) + write!(w, " = {}", default.print(cx, &at)) } - write!(w, "{}", print_where_clause(generics, cx, indent, Ending::NoNewline)); + write!(w, "{}", print_where_clause(generics, cx, &at, indent, Ending::NoNewline)); } fn assoc_method( @@ -946,6 +950,7 @@ fn assoc_method( let name = meth.name.as_ref().unwrap(); let vis = visibility_print_with_space(meth, cx).to_string(); let defaultness = print_default_space(meth.is_default()); + let at = AmbiguityTable::build_fn_decl(d); // FIXME: Once https://github.com/rust-lang/rust/issues/67792 is implemented, we can remove // this condition. let constness = match render_mode { @@ -960,7 +965,7 @@ fn assoc_method( let href = assoc_href_attr(meth, link, cx); // NOTE: `{:#}` does not print HTML formatting, `{}` does. So `g.print` can't be reused between the length calculation and `write!`. - let generics_len = format!("{:#}", g.print(cx)).len(); + let generics_len = format!("{:#}", g.print(cx, &at)).len(); let mut header_len = "fn ".len() + vis.len() + defaultness.len() @@ -996,10 +1001,10 @@ fn assoc_method( abi = abi, href = href, name = name, - generics = g.print(cx), - decl = d.full_print(header_len, indent, cx), + generics = g.print(cx, &at), + decl = d.full_print(header_len, indent, cx, &at), notable_traits = notable_traits.unwrap_or_default(), - where_clause = print_where_clause(g, cx, indent, end_newline), + where_clause = print_where_clause(g, cx, &at, indent, end_newline), ); } @@ -1288,6 +1293,7 @@ fn render_assoc_items_inner( what: AssocItemRender<'_>, derefs: &mut DefIdSet, ) { + let at = AmbiguityTable::empty(); info!("Documenting associated items of {:?}", containing_item.name); let shared = Rc::clone(&cx.shared); let cache = &shared.cache; @@ -1301,15 +1307,17 @@ fn render_assoc_items_inner( (RenderMode::Normal, "implementations-list".to_owned(), "") } AssocItemRender::DerefFor { trait_, type_, deref_mut_ } => { - let id = - cx.derive_id(small_url_encode(format!("deref-methods-{:#}", type_.print(cx)))); + let id = cx.derive_id(small_url_encode(format!( + "deref-methods-{:#}", + type_.print(cx, &at) + ))); let derived_id = cx.derive_id(&id); write_impl_section_heading( &mut tmp_buf, &format!( "Methods from {trait_}<Target = {type_}>", - trait_ = trait_.print(cx), - type_ = type_.print(cx), + trait_ = trait_.print(cx, &at), + type_ = type_.print(cx, &at), ), &id, ); @@ -1480,12 +1488,12 @@ pub(crate) fn notable_traits_button(ty: &clean::Type, cx: &mut Context<'_>) -> O } } } - if has_notable_trait { + let at = AmbiguityTable::empty(); cx.types_with_notable_traits.insert(ty.clone()); Some(format!( " ", - ty = Escape(&format!("{:#}", ty.print(cx))), + ty = Escape(&format!("{:#}", ty.print(cx, &at))), )) } else { None @@ -1498,7 +1506,7 @@ fn notable_traits_decl(ty: &clean::Type, cx: &Context<'_>) -> (String, String) { let did = ty.def_id(cx.cache()).expect("notable_traits_button already checked this"); let impls = cx.cache().impls.get(&did).expect("notable_traits_button already checked this"); - + let at = AmbiguityTable::empty(); for i in impls { let impl_ = i.inner_impl(); if !ty.is_doc_subtype_of(&impl_.for_, cx.cache()) { @@ -1515,11 +1523,11 @@ fn notable_traits_decl(ty: &clean::Type, cx: &Context<'_>) -> (String, String) { &mut out, "

Notable traits for {}

\
",
-                        impl_.for_.print(cx)
+                        impl_.for_.print(cx, &at)
                     );
                 }
 
-                write!(&mut out, "
{}
", impl_.print(false, cx)); + write!(&mut out, "
{}
", impl_.print(false, cx, &at)); for it in &impl_.items { if let clean::AssocTypeItem(ref tydef, ref _bounds) = *it.kind { out.push_str("
"); @@ -1545,7 +1553,7 @@ fn notable_traits_decl(ty: &clean::Type, cx: &Context<'_>) -> (String, String) { out.write_str("
"); } - (format!("{:#}", ty.print(cx)), out.into_inner()) + (format!("{:#}", ty.print(cx, &at)), out.into_inner()) } pub(crate) fn notable_traits_json<'a>( @@ -2016,9 +2024,9 @@ pub(crate) fn render_impl_summary( "§\

" ); - + let at = AmbiguityTable::empty(); if let Some(use_absolute) = use_absolute { - write!(w, "{}", inner_impl.print(use_absolute, cx)); + write!(w, "{}", inner_impl.print(use_absolute, cx, &at)); if show_def_docs { for it in &inner_impl.items { if let clean::AssocTypeItem(ref tydef, ref _bounds) = *it.kind { @@ -2038,7 +2046,7 @@ pub(crate) fn render_impl_summary( } } } else { - write!(w, "{}", inner_impl.print(false, cx)); + write!(w, "{}", inner_impl.print(false, cx, &at)); } w.write_str("

"); @@ -2144,9 +2152,10 @@ fn get_id_for_impl<'tcx>(tcx: TyCtxt<'tcx>, impl_id: ItemId) -> String { fn extract_for_impl_name(item: &clean::Item, cx: &Context<'_>) -> Option<(String, String)> { match *item.kind { clean::ItemKind::ImplItem(ref i) if i.trait_.is_some() => { + let at = AmbiguityTable::empty(); // Alternative format produces no URLs, // so this parameter does nothing. - Some((format!("{:#}", i.for_.print(cx)), get_id_for_impl(cx.tcx(), item.item_id))) + Some((format!("{:#}", i.for_.print(cx, &at)), get_id_for_impl(cx.tcx(), item.item_id))) } _ => None, } diff --git a/src/librustdoc/html/render/print_item.rs b/src/librustdoc/html/render/print_item.rs index 5d4f1acc4b188..b8ab61627f371 100644 --- a/src/librustdoc/html/render/print_item.rs +++ b/src/librustdoc/html/render/print_item.rs @@ -26,6 +26,7 @@ use crate::clean; use crate::config::ModuleSorting; use crate::formats::item_type::ItemType; use crate::formats::Impl; +use crate::html::ambiguity::AmbiguityTable; use crate::html::escape::Escape; use crate::html::format::{ display_fn, join_with_double_colon, print_abi_with_space, print_constness_with_space, @@ -172,9 +173,10 @@ fn print_where_clause_and_check<'a, 'tcx: 'a>( buffer: &mut Buffer, gens: &'a clean::Generics, cx: &'a Context<'tcx>, + at: &'a AmbiguityTable<'_>, ) -> bool { let len_before = buffer.len(); - write!(buffer, "{}", print_where_clause(gens, cx, 0, Ending::Newline)); + write!(buffer, "{}", print_where_clause(gens, cx, at, 0, Ending::Newline)); len_before != buffer.len() } @@ -461,6 +463,7 @@ fn item_module(w: &mut Buffer, cx: &mut Context<'_>, item: &clean::Item, items: } clean::ImportItem(ref import) => { + let at = AmbiguityTable::empty(); let stab_tags = if let Some(import_def_id) = import.source.did { // Just need an item with the correct def_id and attrs let import_item = @@ -492,7 +495,7 @@ fn item_module(w: &mut Buffer, cx: &mut Context<'_>, item: &clean::Item, items: \ {stab_tags_before}{stab_tags}{stab_tags_after}", vis = visibility_print_with_space(myitem, cx), - imp = import.print(cx), + imp = import.print(cx, &at), ); w.write_str(ITEM_TABLE_ROW_CLOSE); } @@ -633,8 +636,9 @@ fn item_function(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, f: &cle let asyncness = header.asyncness.print_with_space(); let visibility = visibility_print_with_space(it, cx).to_string(); let name = it.name.unwrap(); + let at = AmbiguityTable::build_fn(f); - let generics_len = format!("{:#}", f.generics.print(cx)).len(); + let generics_len = format!("{:#}", f.generics.print(cx, &at)).len(); let header_len = "fn ".len() + visibility.len() + constness.len() @@ -645,7 +649,6 @@ fn item_function(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, f: &cle + generics_len; let notable_traits = notable_traits_button(&f.decl.output, cx); - wrap_item(w, |w| { w.reserve(header_len); write!( @@ -659,9 +662,9 @@ fn item_function(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, f: &cle unsafety = unsafety, abi = abi, name = name, - generics = f.generics.print(cx), - where_clause = print_where_clause(&f.generics, cx, 0, Ending::Newline), - decl = f.decl.full_print(header_len, 0, cx), + generics = f.generics.print(cx, &at), + where_clause = print_where_clause(&f.generics, cx, &at, 0, Ending::Newline), + decl = f.decl.full_print(header_len, 0, cx, &at), notable_traits = notable_traits.unwrap_or_default(), ); }); @@ -682,6 +685,7 @@ fn item_trait(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean: let count_methods = required_methods.len() + provided_methods.len(); let must_implement_one_of_functions = tcx.trait_def(t.def_id).must_implement_one_of.clone(); + let at = AmbiguityTable::empty(); // Output the trait definition wrap_item(w, |mut w| { write!( @@ -692,11 +696,11 @@ fn item_trait(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean: unsafety = t.unsafety(tcx).print_with_space(), is_auto = if t.is_auto(tcx) { "auto " } else { "" }, name = it.name.unwrap(), - generics = t.generics.print(cx), + generics = t.generics.print(cx, &at), ); if !t.generics.where_predicates.is_empty() { - write!(w, "{}", print_where_clause(&t.generics, cx, 0, Ending::Newline)); + write!(w, "{}", print_where_clause(&t.generics, cx, &at, 0, Ending::Newline)); } else { w.write_str(" "); } @@ -1185,14 +1189,15 @@ fn item_trait_alias( it: &clean::Item, t: &clean::TraitAlias, ) { + let at = AmbiguityTable::empty(); wrap_item(w, |w| { write!( w, "{attrs}trait {name}{generics}{where_b} = {bounds};", attrs = render_attributes_in_pre(it, "", cx), name = it.name.unwrap(), - generics = t.generics.print(cx), - where_b = print_where_clause(&t.generics, cx, 0, Ending::Newline), + generics = t.generics.print(cx, &at), + where_b = print_where_clause(&t.generics, cx, &at, 0, Ending::Newline), bounds = bounds(&t.bounds, true, cx), ) .unwrap(); @@ -1213,14 +1218,15 @@ fn item_opaque_ty( it: &clean::Item, t: &clean::OpaqueTy, ) { + let at = AmbiguityTable::empty(); wrap_item(w, |w| { write!( w, "{attrs}type {name}{generics}{where_clause} = impl {bounds};", attrs = render_attributes_in_pre(it, "", cx), name = it.name.unwrap(), - generics = t.generics.print(cx), - where_clause = print_where_clause(&t.generics, cx, 0, Ending::Newline), + generics = t.generics.print(cx, &at), + where_clause = print_where_clause(&t.generics, cx, &at, 0, Ending::Newline), bounds = bounds(&t.bounds, false, cx), ) .unwrap(); @@ -1239,15 +1245,16 @@ fn item_opaque_ty( fn item_type_alias(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean::TypeAlias) { fn write_content(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, t: &clean::TypeAlias) { wrap_item(w, |w| { + let at = AmbiguityTable::empty(); write!( w, "{attrs}{vis}type {name}{generics}{where_clause} = {type_};", attrs = render_attributes_in_pre(it, "", cx), vis = visibility_print_with_space(it, cx), name = it.name.unwrap(), - generics = t.generics.print(cx), - where_clause = print_where_clause(&t.generics, cx, 0, Ending::Newline), - type_ = t.type_.print(cx), + generics = t.generics.print(cx, &at), + where_clause = print_where_clause(&t.generics, cx, &at, 0, Ending::Newline), + type_ = t.type_.print(cx, &at), ); }); } @@ -1269,8 +1276,8 @@ fn item_type_alias(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &c let variants_len = variants.len(); let variants_count = variants_iter().count(); let has_stripped_entries = variants_len != variants_count; - - write!(w, "enum {}{}", it.name.unwrap(), t.generics.print(cx)); + let at = AmbiguityTable::empty(); + write!(w, "enum {}{}", it.name.unwrap(), t.generics.print(cx, &at)); render_enum_fields( w, cx, @@ -1286,10 +1293,11 @@ fn item_type_alias(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &c } clean::TypeAliasInnerType::Union { fields } => { wrap_item(w, |w| { + let at = AmbiguityTable::empty(); let fields_count = fields.iter().filter(|i| !i.is_stripped()).count(); let has_stripped_fields = fields.len() != fields_count; - write!(w, "union {}{}", it.name.unwrap(), t.generics.print(cx)); + write!(w, "union {}{}", it.name.unwrap(), t.generics.print(cx, &at)); render_struct_fields( w, Some(&t.generics), @@ -1305,10 +1313,11 @@ fn item_type_alias(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &c } clean::TypeAliasInnerType::Struct { ctor_kind, fields } => { wrap_item(w, |w| { + let at = AmbiguityTable::empty(); let fields_count = fields.iter().filter(|i| !i.is_stripped()).count(); let has_stripped_fields = fields.len() != fields_count; - write!(w, "struct {}{}", it.name.unwrap(), t.generics.print(cx)); + write!(w, "struct {}{}", it.name.unwrap(), t.generics.print(cx, &at)); render_struct_fields( w, Some(&t.generics), @@ -1471,8 +1480,9 @@ fn item_union(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, s: &clean: ty: &'a clean::Type, ) -> impl fmt::Display + Captures<'a> + 'b + Captures<'cx> { display_fn(move |f| { + let at = AmbiguityTable::empty(); let cx = self.cx.borrow(); - let v = ty.print(*cx); + let v = ty.print(*cx, &at); write!(f, "{v}") }) } @@ -1513,7 +1523,10 @@ fn print_tuple_struct_fields<'a, 'cx: 'a>( } match *ty.kind { clean::StrippedItem(box clean::StructFieldItem(_)) => f.write_str("_")?, - clean::StructFieldItem(ref ty) => write!(f, "{}", ty.print(cx))?, + clean::StructFieldItem(ref ty) => { + let at = AmbiguityTable::empty(); + write!(f, "{}", ty.print(cx, &at))?; + } _ => unreachable!(), } } @@ -1525,12 +1538,13 @@ fn item_enum(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, e: &clean:: let count_variants = e.variants().count(); wrap_item(w, |w| { render_attributes_in_code(w, it, cx); + let at = AmbiguityTable::empty(); write!( w, "{}enum {}{}", visibility_print_with_space(it, cx), it.name.unwrap(), - e.generics.print(cx), + e.generics.print(cx, &at), ); render_enum_fields( @@ -1616,7 +1630,8 @@ fn render_enum_fields( enum_def_id: DefId, ) { let should_show_enum_discriminant = should_show_enum_discriminant(cx, enum_def_id, variants); - if !g.is_some_and(|g| print_where_clause_and_check(w, g, cx)) { + let at = AmbiguityTable::empty(); + if !g.is_some_and(|g| print_where_clause_and_check(w, g, cx, &at)) { // If there wasn't a `where` clause, we add a whitespace. w.write_str(" "); } @@ -1759,6 +1774,7 @@ fn item_variants( {}", document_non_exhaustive(variant) ); + let at = AmbiguityTable::empty(); for field in fields { match *field.kind { clean::StrippedItem(box clean::StructFieldItem(_)) => {} @@ -1776,7 +1792,7 @@ fn item_variants( {f}: {t}\ ", f = field.name.unwrap(), - t = ty.print(cx), + t = ty.print(cx, &at), ); write!( w, @@ -1855,15 +1871,15 @@ fn item_constant(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, c: &cle wrap_item(w, |w| { let tcx = cx.tcx(); render_attributes_in_code(w, it, cx); - + let at = AmbiguityTable::empty(); write!( w, "{vis}const {name}{generics}: {typ}{where_clause}", vis = visibility_print_with_space(it, cx), name = it.name.unwrap(), - generics = c.generics.print(cx), - typ = c.type_.print(cx), - where_clause = print_where_clause(&c.generics, cx, 0, Ending::NoNewline), + generics = c.generics.print(cx, &at), + typ = c.type_.print(cx, &at), + where_clause = print_where_clause(&c.generics, cx, &at, 0, Ending::NoNewline), ); // FIXME: The code below now prints @@ -1938,6 +1954,7 @@ fn item_fields( document_non_exhaustive_header(it), ); write_section_heading(w, &title, "fields", Some("fields"), document_non_exhaustive(it)); + let at = AmbiguityTable::empty(); for (index, (field, ty)) in fields.enumerate() { let field_name = field.name.map_or_else(|| index.to_string(), |sym| sym.as_str().to_string()); @@ -1949,7 +1966,7 @@ fn item_fields( {field_name}: {ty}\ ", item_type = ItemType::StructField, - ty = ty.print(cx) + ty = ty.print(cx, &at) ); write!(w, "{}", document(cx, field, Some(it), HeadingOffset::H3)); } @@ -1960,13 +1977,14 @@ fn item_fields( fn item_static(w: &mut impl fmt::Write, cx: &mut Context<'_>, it: &clean::Item, s: &clean::Static) { wrap_item(w, |buffer| { render_attributes_in_code(buffer, it, cx); + let at = AmbiguityTable::empty(); write!( buffer, "{vis}static {mutability}{name}: {typ}", vis = visibility_print_with_space(it, cx), mutability = s.mutability.print_with_space(), name = it.name.unwrap(), - typ = s.type_.print(cx) + typ = s.type_.print(cx, &at) ) .unwrap(); }); @@ -2049,6 +2067,7 @@ pub(super) fn item_path(ty: ItemType, name: &str) -> String { fn bounds(t_bounds: &[clean::GenericBound], trait_alias: bool, cx: &Context<'_>) -> String { let mut bounds = String::new(); + let at = AmbiguityTable::empty(); if !t_bounds.is_empty() { if !trait_alias { bounds.push_str(": "); @@ -2057,7 +2076,7 @@ fn bounds(t_bounds: &[clean::GenericBound], trait_alias: bool, cx: &Context<'_>) if i > 0 { bounds.push_str(" + "); } - bounds.push_str(&p.print(cx).to_string()); + bounds.push_str(&p.print(cx, &at).to_string()); } } bounds @@ -2078,7 +2097,8 @@ struct ImplString(String); impl ImplString { fn new(i: &Impl, cx: &Context<'_>) -> ImplString { - ImplString(format!("{}", i.inner_impl().print(false, cx))) + let at = AmbiguityTable::empty(); + ImplString(format!("{}", i.inner_impl().print(false, cx, &at))) } } @@ -2139,12 +2159,12 @@ fn render_union<'a, 'cx: 'a>( ) -> impl fmt::Display + 'a + Captures<'cx> { display_fn(move |mut f| { write!(f, "{}union {}", visibility_print_with_space(it, cx), it.name.unwrap(),)?; - + let at = AmbiguityTable::empty(); let where_displayed = g .map(|g| { let mut buf = Buffer::html(); - write!(buf, "{}", g.print(cx)); - let where_displayed = print_where_clause_and_check(&mut buf, g, cx); + write!(buf, "{}", g.print(cx, &at)); + let where_displayed = print_where_clause_and_check(&mut buf, g, cx, &at); write!(f, "{buf}", buf = buf.into_inner()).unwrap(); where_displayed }) @@ -2170,7 +2190,7 @@ fn render_union<'a, 'cx: 'a>( " {}{}: {},\n", visibility_print_with_space(field, cx), field.name.unwrap(), - ty.print(cx) + ty.print(cx, &at) )?; } } @@ -2204,7 +2224,8 @@ fn render_struct( it.name.unwrap() ); if let Some(g) = g { - write!(w, "{}", g.print(cx)) + let at = AmbiguityTable::empty(); + write!(w, "{}", g.print(cx, &at)); } render_struct_fields( w, @@ -2228,10 +2249,11 @@ fn render_struct_fields( has_stripped_entries: bool, cx: &Context<'_>, ) { + let at = AmbiguityTable::empty(); match ty { None => { let where_displayed = - g.map(|g| print_where_clause_and_check(w, g, cx)).unwrap_or(false); + g.map(|g| print_where_clause_and_check(w, g, cx, &at)).unwrap_or(false); // If there wasn't a `where` clause, we add a whitespace. if !where_displayed { @@ -2253,7 +2275,7 @@ fn render_struct_fields( "\n{tab} {vis}{name}: {ty},", vis = visibility_print_with_space(field, cx), name = field.name.unwrap(), - ty = ty.print(cx), + ty = ty.print(cx, &at), ); } } @@ -2287,7 +2309,12 @@ fn render_struct_fields( match *field.kind { clean::StrippedItem(box clean::StructFieldItem(..)) => write!(w, "_"), clean::StructFieldItem(ref ty) => { - write!(w, "{}{}", visibility_print_with_space(field, cx), ty.print(cx),) + write!( + w, + "{}{}", + visibility_print_with_space(field, cx), + ty.print(cx, &at), + ) } _ => unreachable!(), } @@ -2295,7 +2322,7 @@ fn render_struct_fields( } w.write_str(")"); if let Some(g) = g { - write!(w, "{}", print_where_clause(g, cx, 0, Ending::NoNewline)); + write!(w, "{}", print_where_clause(g, cx, &at, 0, Ending::NoNewline)); } // We only want a ";" when we are displaying a tuple struct, not a variant tuple struct. if structhead { @@ -2305,7 +2332,7 @@ fn render_struct_fields( Some(CtorKind::Const) => { // Needed for PhantomData. if let Some(g) = g { - write!(w, "{}", print_where_clause(g, cx, 0, Ending::NoNewline)); + write!(w, "{}", print_where_clause(g, cx, &at, 0, Ending::NoNewline)); } w.write_str(";"); } diff --git a/src/librustdoc/html/render/sidebar.rs b/src/librustdoc/html/render/sidebar.rs index 3d28937eb99e1..f45addfc37da0 100644 --- a/src/librustdoc/html/render/sidebar.rs +++ b/src/librustdoc/html/render/sidebar.rs @@ -8,7 +8,7 @@ use rustc_middle::ty::{self, TyCtxt}; use crate::{ clean, formats::{item_type::ItemType, Impl}, - html::{format::Buffer, markdown::IdMap}, + html::{ambiguity::AmbiguityTable, format::Buffer, markdown::IdMap}, }; use super::{item_ty_to_section, Context, ItemSection}; @@ -423,10 +423,11 @@ fn sidebar_deref_methods<'a>( } else { Cow::Borrowed("deref-methods") }; + let at = AmbiguityTable::empty(); let title = format!( "Methods from {:#}", - impl_.inner_impl().trait_.as_ref().unwrap().print(cx), - real_target.print(cx), + impl_.inner_impl().trait_.as_ref().unwrap().print(cx, &at), + real_target.print(cx, &at), ); // We want links' order to be reproducible so we don't use unstable sort. ret.sort(); @@ -530,7 +531,8 @@ fn sidebar_render_assoc_items( ty::ImplPolarity::Positive | ty::ImplPolarity::Reservation => "", ty::ImplPolarity::Negative => "!", }; - let generated = Link::new(encoded, format!("{prefix}{:#}", trait_.print(cx))); + let at = AmbiguityTable::empty(); + let generated = Link::new(encoded, format!("{prefix}{:#}", trait_.print(cx, &at))); if links.insert(generated.clone()) { Some(generated) } else { None } }) .collect::>>(); diff --git a/src/librustdoc/html/render/write_shared.rs b/src/librustdoc/html/render/write_shared.rs index fbd45b2b48ef9..82b0316735b4e 100644 --- a/src/librustdoc/html/render/write_shared.rs +++ b/src/librustdoc/html/render/write_shared.rs @@ -23,6 +23,7 @@ use crate::error::Error; use crate::formats::cache::Cache; use crate::formats::item_type::ItemType; use crate::formats::Impl; +use crate::html::ambiguity::AmbiguityTable; use crate::html::format::Buffer; use crate::html::render::{AssocItemLink, ImplRenderingParameters}; use crate::html::{layout, static_files}; @@ -534,11 +535,12 @@ else if (window.initSearch) window.initSearch(searchIndex); .values() .flat_map(|AliasedTypeImpl { impl_, type_aliases }| { let mut ret = Vec::new(); + let at = AmbiguityTable::empty(); let trait_ = impl_ .inner_impl() .trait_ .as_ref() - .map(|trait_| format!("{:#}", trait_.print(cx))); + .map(|trait_| format!("{:#}", trait_.print(cx, &at))); // render_impl will filter out "impossible-to-call" methods // to make that functionality work here, it needs to be called with // each type alias, and if it gives a different result, split the impl @@ -692,8 +694,10 @@ else if (window.initSearch) window.initSearch(searchIndex); if imp.impl_item.item_id.krate() == did.krate || !imp.impl_item.item_id.is_local() { None } else { + let at = AmbiguityTable::empty(); + let text = imp.inner_impl().print(false, cx, &at).to_string(); Some(Implementor { - text: imp.inner_impl().print(false, cx).to_string(), + text, synthetic: imp.inner_impl().kind.is_auto(), types: collect_paths_for_type(imp.inner_impl().for_.clone(), cache), }) diff --git a/src/librustdoc/lib.rs b/src/librustdoc/lib.rs index 18651875130fc..b270dc0f42a99 100644 --- a/src/librustdoc/lib.rs +++ b/src/librustdoc/lib.rs @@ -10,6 +10,7 @@ #![feature(iter_intersperse)] #![feature(lazy_cell)] #![feature(let_chains)] +#![feature(map_entry_replace)] #![feature(never_type)] #![feature(round_char_boundary)] #![feature(test)] diff --git a/tests/rustdoc/fn_param_ambiguities.rs b/tests/rustdoc/fn_param_ambiguities.rs new file mode 100644 index 0000000000000..ba35b6471cc12 --- /dev/null +++ b/tests/rustdoc/fn_param_ambiguities.rs @@ -0,0 +1,13 @@ +pub mod X { + pub enum A {} + pub enum B {} + pub enum C {} +} + +pub mod Y { + pub enum A {} + pub enum B {} +} + +// @has fn_param_ambiguities/fn.f.html //pre 'pub fn f(xa: X::A, ya: Y::A, yb: B, xc: C)' +pub fn f(xa: X::A, ya: Y::A, yb : Y::B, xc: X::C) {}