Auto merge of #139881 - matthiaskrgr:rollup-7x6zcrc, r=matthiaskrgr

Rollup of 7 pull requests

Successful merges:

 - #138455 (`librustdoc`: more `impl fmt::Display`)
 - #139818 (Normalize ADT field in `find_tails_for_unsizing`)
 - #139819 (Use `rust-cache` to speed-up `citool` compilation)
 - #139824 (Remove safe remove)
 - #139848 ( Reduce kw::Empty usage, part 5)
 - #139859 (CI: rename MacOS runner)
 - #139877 (Add warning comment to `Take::get_ref` and `Chain::get_ref`)

r? `@ghost`
`@rustbot` modify labels: rollup
This commit is contained in:
bors 2025-04-15 19:21:06 +00:00
commit 38c560ae68
17 changed files with 330 additions and 356 deletions

View File

@ -53,6 +53,13 @@ jobs:
steps:
- name: Checkout the source code
uses: actions/checkout@v4
# Cache citool to make its build faster, as it's in the critical path.
# The rust-cache doesn't bleed into the main `job`, so it should not affect any other
# Rust compilation.
- name: Cache citool
uses: Swatinem/rust-cache@9d47c6ad4b02e050fd481d890b2ea34778fd09d6 # v2.7.8
with:
workspaces: src/ci/citool
- name: Calculate the CI job matrix
env:
COMMIT_MESSAGE: ${{ github.event.head_commit.message }}

View File

@ -1765,7 +1765,6 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
ident: Ident,
is_anon_in_path: IsAnonInPath,
) -> &'hir hir::Lifetime {
debug_assert_ne!(ident.name, kw::Empty);
let res = self.resolver.get_lifetime_res(id).unwrap_or(LifetimeRes::Error);
let res = match res {
LifetimeRes::Param { param, .. } => hir::LifetimeName::Param(param),

View File

@ -1600,11 +1600,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
Ok(method)
}
Err(error) => {
if segment.ident.name == kw::Empty {
span_bug!(rcvr.span, "empty method name")
} else {
Err(self.report_method_error(expr.hir_id, rcvr_t, error, expected, false))
}
Err(self.report_method_error(expr.hir_id, rcvr_t, error, expected, false))
}
};
@ -2941,9 +2937,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
return Ty::new_error(self.tcx(), guar);
}
let guar = if field.name == kw::Empty {
self.dcx().span_bug(field.span, "field name with no name")
} else if self.method_exists_for_diagnostic(
let guar = if self.method_exists_for_diagnostic(
field,
base_ty,
expr.hir_id,

View File

@ -27,9 +27,9 @@ use rustc_middle::ty::{
};
use rustc_middle::{bug, span_bug};
use rustc_session::lint;
use rustc_span::Span;
use rustc_span::def_id::LocalDefId;
use rustc_span::hygiene::DesugaringKind;
use rustc_span::{Span, kw};
use rustc_trait_selection::error_reporting::infer::need_type_info::TypeAnnotationNeeded;
use rustc_trait_selection::traits::{
self, NormalizeExt, ObligationCauseCode, StructurallyNormalizeExt,
@ -833,7 +833,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
let trait_missing_method =
matches!(error, method::MethodError::NoMatch(_)) && ty.normalized.is_trait();
assert_ne!(item_name.name, kw::Empty);
self.report_method_error(
hir_id,
ty.normalized,

View File

@ -290,7 +290,7 @@ pub(crate) fn prepare_session_directory(sess: &Session, crate_name: Symbol) {
// Try to remove the session directory we just allocated. We don't
// know if there's any garbage in it from the failed copy action.
if let Err(err) = safe_remove_dir_all(&session_dir) {
if let Err(err) = std_fs::remove_dir_all(&session_dir) {
sess.dcx().emit_warn(errors::DeletePartial { path: &session_dir, err });
}
@ -324,7 +324,7 @@ pub fn finalize_session_directory(sess: &Session, svh: Option<Svh>) {
incr_comp_session_dir.display()
);
if let Err(err) = safe_remove_dir_all(&*incr_comp_session_dir) {
if let Err(err) = std_fs::remove_dir_all(&*incr_comp_session_dir) {
sess.dcx().emit_warn(errors::DeleteFull { path: &incr_comp_session_dir, err });
}
@ -715,7 +715,7 @@ pub(crate) fn garbage_collect_session_directories(sess: &Session) -> io::Result<
for directory_name in session_directories {
if !lock_file_to_session_dir.items().any(|(_, dir)| *dir == directory_name) {
let path = crate_directory.join(directory_name);
if let Err(err) = safe_remove_dir_all(&path) {
if let Err(err) = std_fs::remove_dir_all(&path) {
sess.dcx().emit_warn(errors::InvalidGcFailed { path: &path, err });
}
}
@ -821,7 +821,7 @@ pub(crate) fn garbage_collect_session_directories(sess: &Session) -> io::Result<
all_except_most_recent(deletion_candidates).into_items().all(|(path, lock)| {
debug!("garbage_collect_session_directories() - deleting `{}`", path.display());
if let Err(err) = safe_remove_dir_all(&path) {
if let Err(err) = std_fs::remove_dir_all(&path) {
sess.dcx().emit_warn(errors::FinalizedGcFailed { path: &path, err });
} else {
delete_session_dir_lock_file(sess, &lock_file_path(&path));
@ -839,7 +839,7 @@ pub(crate) fn garbage_collect_session_directories(sess: &Session) -> io::Result<
fn delete_old(sess: &Session, path: &Path) {
debug!("garbage_collect_session_directories() - deleting `{}`", path.display());
if let Err(err) = safe_remove_dir_all(path) {
if let Err(err) = std_fs::remove_dir_all(path) {
sess.dcx().emit_warn(errors::SessionGcFailed { path, err });
} else {
delete_session_dir_lock_file(sess, &lock_file_path(path));
@ -862,30 +862,8 @@ fn all_except_most_recent(
}
}
/// Since paths of artifacts within session directories can get quite long, we
/// need to support deleting files with very long paths. The regular
/// WinApi functions only support paths up to 260 characters, however. In order
/// to circumvent this limitation, we canonicalize the path of the directory
/// before passing it to std::fs::remove_dir_all(). This will convert the path
/// into the '\\?\' format, which supports much longer paths.
fn safe_remove_dir_all(p: &Path) -> io::Result<()> {
let canonicalized = match try_canonicalize(p) {
Ok(canonicalized) => canonicalized,
Err(err) if err.kind() == io::ErrorKind::NotFound => return Ok(()),
Err(err) => return Err(err),
};
std_fs::remove_dir_all(canonicalized)
}
fn safe_remove_file(p: &Path) -> io::Result<()> {
let canonicalized = match try_canonicalize(p) {
Ok(canonicalized) => canonicalized,
Err(err) if err.kind() == io::ErrorKind::NotFound => return Ok(()),
Err(err) => return Err(err),
};
match std_fs::remove_file(canonicalized) {
match std_fs::remove_file(p) {
Err(err) if err.kind() == io::ErrorKind::NotFound => Ok(()),
result => result,
}

View File

@ -688,7 +688,7 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirUsedCollector<'a, 'tcx> {
let target_ty = self.monomorphize(target_ty);
let source_ty = self.monomorphize(source_ty);
let (source_ty, target_ty) =
find_vtable_types_for_unsizing(self.tcx.at(span), source_ty, target_ty);
find_tails_for_unsizing(self.tcx.at(span), source_ty, target_ty);
// This could also be a different Unsize instruction, like
// from a fixed sized array to a slice. But we are only
// interested in things that produce a vtable.
@ -1037,36 +1037,35 @@ fn should_codegen_locally<'tcx>(tcx: TyCtxt<'tcx>, instance: Instance<'tcx>) ->
///
/// Finally, there is also the case of custom unsizing coercions, e.g., for
/// smart pointers such as `Rc` and `Arc`.
fn find_vtable_types_for_unsizing<'tcx>(
fn find_tails_for_unsizing<'tcx>(
tcx: TyCtxtAt<'tcx>,
source_ty: Ty<'tcx>,
target_ty: Ty<'tcx>,
) -> (Ty<'tcx>, Ty<'tcx>) {
let ptr_vtable = |inner_source: Ty<'tcx>, inner_target: Ty<'tcx>| {
let typing_env = ty::TypingEnv::fully_monomorphized();
if tcx.type_has_metadata(inner_source, typing_env) {
(inner_source, inner_target)
} else {
tcx.struct_lockstep_tails_for_codegen(inner_source, inner_target, typing_env)
}
};
let typing_env = ty::TypingEnv::fully_monomorphized();
debug_assert!(!source_ty.has_param(), "{source_ty} should be fully monomorphic");
debug_assert!(!target_ty.has_param(), "{target_ty} should be fully monomorphic");
match (source_ty.kind(), target_ty.kind()) {
(&ty::Ref(_, a, _), &ty::Ref(_, b, _) | &ty::RawPtr(b, _))
| (&ty::RawPtr(a, _), &ty::RawPtr(b, _)) => ptr_vtable(a, b),
(
&ty::Ref(_, source_pointee, _),
&ty::Ref(_, target_pointee, _) | &ty::RawPtr(target_pointee, _),
)
| (&ty::RawPtr(source_pointee, _), &ty::RawPtr(target_pointee, _)) => {
tcx.struct_lockstep_tails_for_codegen(source_pointee, target_pointee, typing_env)
}
// `Box<T>` could go through the ADT code below, b/c it'll unpeel to `Unique<T>`,
// and eventually bottom out in a raw ref, but we can micro-optimize it here.
(_, _)
if let Some(source_boxed) = source_ty.boxed_ty()
&& let Some(target_boxed) = target_ty.boxed_ty() =>
{
ptr_vtable(source_boxed, target_boxed)
tcx.struct_lockstep_tails_for_codegen(source_boxed, target_boxed, typing_env)
}
// T as dyn* Trait
(_, &ty::Dynamic(_, _, ty::DynStar)) => ptr_vtable(source_ty, target_ty),
(&ty::Adt(source_adt_def, source_args), &ty::Adt(target_adt_def, target_args)) => {
assert_eq!(source_adt_def, target_adt_def);
let CustomCoerceUnsized::Struct(coerce_index) =
match crate::custom_coerce_unsize_info(tcx, source_ty, target_ty) {
Ok(ccu) => ccu,
@ -1075,21 +1074,23 @@ fn find_vtable_types_for_unsizing<'tcx>(
return (e, e);
}
};
let source_fields = &source_adt_def.non_enum_variant().fields;
let target_fields = &target_adt_def.non_enum_variant().fields;
assert!(
coerce_index.index() < source_fields.len()
&& source_fields.len() == target_fields.len()
);
find_vtable_types_for_unsizing(
tcx,
source_fields[coerce_index].ty(*tcx, source_args),
target_fields[coerce_index].ty(*tcx, target_args),
)
let coerce_field = &source_adt_def.non_enum_variant().fields[coerce_index];
// We're getting a possibly unnormalized type, so normalize it.
let source_field =
tcx.normalize_erasing_regions(typing_env, coerce_field.ty(*tcx, source_args));
let target_field =
tcx.normalize_erasing_regions(typing_env, coerce_field.ty(*tcx, target_args));
find_tails_for_unsizing(tcx, source_field, target_field)
}
// `T` as `dyn* Trait` unsizes *directly*.
//
// FIXME(dyn_star): This case is a bit awkward, b/c we're not really computing
// a tail here. We probably should handle this separately in the *caller* of
// this function, rather than returning something that is semantically different
// than what we return above.
(_, &ty::Dynamic(_, _, ty::DynStar)) => (source_ty, target_ty),
_ => bug!(
"find_vtable_types_for_unsizing: invalid coercion {:?} -> {:?}",
source_ty,
@ -1308,7 +1309,7 @@ fn visit_mentioned_item<'tcx>(
}
MentionedItem::UnsizeCast { source_ty, target_ty } => {
let (source_ty, target_ty) =
find_vtable_types_for_unsizing(tcx.at(span), source_ty, target_ty);
find_tails_for_unsizing(tcx.at(span), source_ty, target_ty);
// This could also be a different Unsize instruction, like
// from a fixed sized array to a slice. But we are only
// interested in things that produce a vtable.

View File

@ -1012,7 +1012,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
// HACK(eddyb) `lint_if_path_starts_with_module` needs at least
// 2 segments, so the `resolve_path` above won't trigger it.
let mut full_path = import.module_path.clone();
full_path.push(Segment::from_ident(Ident::empty()));
full_path.push(Segment::from_ident(Ident::dummy()));
self.lint_if_path_starts_with_module(Some(finalize), &full_path, None);
}

View File

@ -776,7 +776,7 @@ impl<'tcx> Printer<'tcx> for SymbolMangler<'tcx> {
self.push_disambiguator(
disambiguated_field.disambiguator as u64,
);
self.push_ident(field_name.unwrap_or(kw::Empty).as_str());
self.push_ident(field_name.unwrap().as_str());
field.print(self)?;
}

View File

@ -2658,6 +2658,10 @@ impl<T, U> Chain<T, U> {
/// Gets references to the underlying readers in this `Chain`.
///
/// Care should be taken to avoid modifying the internal I/O state of the
/// underlying readers as doing so may corrupt the internal state of this
/// `Chain`.
///
/// # Examples
///
/// ```no_run
@ -2915,6 +2919,10 @@ impl<T> Take<T> {
/// Gets a reference to the underlying reader.
///
/// Care should be taken to avoid modifying the internal I/O state of the
/// underlying reader as doing so may corrupt the internal limit of this
/// `Take`.
///
/// # Examples
///
/// ```no_run

View File

@ -23,8 +23,8 @@ runners:
os: ubuntu-24.04-16core-64gb
<<: *base-job
- &job-macos-xl
os: macos-13 # We use the standard runner for now
- &job-macos
os: macos-13
<<: *base-job
- &job-macos-m1
@ -380,7 +380,7 @@ auto:
NO_OVERFLOW_CHECKS: 1
DIST_REQUIRE_ALL_TOOLS: 1
CODEGEN_BACKENDS: llvm,cranelift
<<: *job-macos-xl
<<: *job-macos
- name: dist-apple-various
env:
@ -397,18 +397,18 @@ auto:
NO_LLVM_ASSERTIONS: 1
NO_DEBUG_ASSERTIONS: 1
NO_OVERFLOW_CHECKS: 1
<<: *job-macos-xl
<<: *job-macos
- name: x86_64-apple-1
env:
<<: *env-x86_64-apple-tests
<<: *job-macos-xl
<<: *job-macos
- name: x86_64-apple-2
env:
SCRIPT: ./x.py --stage 2 test tests/ui tests/rustdoc
<<: *env-x86_64-apple-tests
<<: *job-macos-xl
<<: *job-macos
- name: dist-aarch64-apple
env:

View File

@ -517,7 +517,7 @@ impl Item {
Some(RenderedLink {
original_text: s.clone(),
new_text: link_text.clone(),
tooltip: link_tooltip(*id, fragment, cx),
tooltip: link_tooltip(*id, fragment, cx).to_string(),
href,
})
} else {

View File

@ -11,6 +11,7 @@ use std::borrow::Cow;
use std::cmp::Ordering;
use std::fmt::{self, Display, Write};
use std::iter::{self, once};
use std::slice;
use itertools::Either;
use rustc_abi::ExternAbi;
@ -650,33 +651,35 @@ pub(crate) fn href_relative_parts<'fqp>(
}
}
pub(crate) fn link_tooltip(did: DefId, fragment: &Option<UrlFragment>, cx: &Context<'_>) -> String {
let cache = cx.cache();
let Some((fqp, shortty)) = cache.paths.get(&did).or_else(|| cache.external_paths.get(&did))
else {
return String::new();
};
let mut buf = String::new();
let fqp = if *shortty == ItemType::Primitive {
// primitives are documented in a crate, but not actually part of it
&fqp[fqp.len() - 1..]
} else {
fqp
};
if let &Some(UrlFragment::Item(id)) = fragment {
write_str(&mut buf, format_args!("{} ", cx.tcx().def_descr(id)));
for component in fqp {
write_str(&mut buf, format_args!("{component}::"));
pub(crate) fn link_tooltip(
did: DefId,
fragment: &Option<UrlFragment>,
cx: &Context<'_>,
) -> impl fmt::Display {
fmt::from_fn(move |f| {
let cache = cx.cache();
let Some((fqp, shortty)) = cache.paths.get(&did).or_else(|| cache.external_paths.get(&did))
else {
return Ok(());
};
let fqp = if *shortty == ItemType::Primitive {
// primitives are documented in a crate, but not actually part of it
slice::from_ref(fqp.last().unwrap())
} else {
fqp
};
if let &Some(UrlFragment::Item(id)) = fragment {
write!(f, "{} ", cx.tcx().def_descr(id))?;
for component in fqp {
write!(f, "{component}::")?;
}
write!(f, "{}", cx.tcx().item_name(id))?;
} else if !fqp.is_empty() {
write!(f, "{shortty} ")?;
fqp.iter().joined("::", f)?;
}
write_str(&mut buf, format_args!("{}", cx.tcx().item_name(id)));
} else if !fqp.is_empty() {
let mut fqp_it = fqp.iter();
write_str(&mut buf, format_args!("{shortty} {}", fqp_it.next().unwrap()));
for component in fqp_it {
write_str(&mut buf, format_args!("::{component}"));
}
}
buf
Ok(())
})
}
/// Used to render a [`clean::Path`].

View File

@ -650,15 +650,7 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> {
bar.render_into(&mut sidebar).unwrap();
let v = layout::render(
&shared.layout,
&page,
sidebar,
BufDisplay(|buf: &mut String| {
all.print(buf);
}),
&shared.style_files,
);
let v = layout::render(&shared.layout, &page, sidebar, all.print(), &shared.style_files);
shared.fs.write(final_file, v)?;
// if to avoid writing help, settings files to doc root unless we're on the final invocation

View File

@ -40,6 +40,7 @@ mod span_map;
mod type_layout;
mod write_shared;
use std::borrow::Cow;
use std::collections::VecDeque;
use std::fmt::{self, Display as _, Write};
use std::iter::Peekable;
@ -47,6 +48,7 @@ use std::path::PathBuf;
use std::{fs, str};
use askama::Template;
use itertools::Either;
use rustc_attr_parsing::{
ConstStability, DeprecatedSince, Deprecation, RustcVersion, StabilityLevel, StableSince,
};
@ -98,6 +100,19 @@ enum AssocItemRender<'a> {
DerefFor { trait_: &'a clean::Path, type_: &'a clean::Type, deref_mut_: bool },
}
impl AssocItemRender<'_> {
fn render_mode(&self) -> RenderMode {
match self {
Self::All => RenderMode::Normal,
&Self::DerefFor { deref_mut_, .. } => RenderMode::ForDeref { mut_: deref_mut_ },
}
}
fn class(&self) -> Option<&'static str> {
if let Self::DerefFor { .. } = self { Some("impl-items") } else { None }
}
}
/// For different handling of associated items from the Deref target of a type rather than the type
/// itself.
#[derive(Copy, Clone, PartialEq)]
@ -439,44 +454,49 @@ impl AllTypes {
sections
}
fn print(&self, f: &mut String) {
fn print_entries(f: &mut String, e: &FxIndexSet<ItemEntry>, kind: ItemSection) {
if !e.is_empty() {
let mut e: Vec<&ItemEntry> = e.iter().collect();
e.sort();
write_str(
f,
format_args!(
"<h3 id=\"{id}\">{title}</h3><ul class=\"all-items\">",
id = kind.id(),
title = kind.name(),
),
);
for s in e.iter() {
write_str(f, format_args!("<li>{}</li>", s.print()));
fn print(&self) -> impl fmt::Display {
fn print_entries(e: &FxIndexSet<ItemEntry>, kind: ItemSection) -> impl fmt::Display {
fmt::from_fn(move |f| {
if e.is_empty() {
return Ok(());
}
f.push_str("</ul>");
}
let mut e: Vec<&ItemEntry> = e.iter().collect();
e.sort();
write!(
f,
"<h3 id=\"{id}\">{title}</h3><ul class=\"all-items\">",
id = kind.id(),
title = kind.name(),
)?;
for s in e.iter() {
write!(f, "<li>{}</li>", s.print())?;
}
f.write_str("</ul>")
})
}
f.push_str("<h1>List of all items</h1>");
// Note: print_entries does not escape the title, because we know the current set of titles
// doesn't require escaping.
print_entries(f, &self.structs, ItemSection::Structs);
print_entries(f, &self.enums, ItemSection::Enums);
print_entries(f, &self.unions, ItemSection::Unions);
print_entries(f, &self.primitives, ItemSection::PrimitiveTypes);
print_entries(f, &self.traits, ItemSection::Traits);
print_entries(f, &self.macros, ItemSection::Macros);
print_entries(f, &self.attribute_macros, ItemSection::AttributeMacros);
print_entries(f, &self.derive_macros, ItemSection::DeriveMacros);
print_entries(f, &self.functions, ItemSection::Functions);
print_entries(f, &self.type_aliases, ItemSection::TypeAliases);
print_entries(f, &self.trait_aliases, ItemSection::TraitAliases);
print_entries(f, &self.statics, ItemSection::Statics);
print_entries(f, &self.constants, ItemSection::Constants);
fmt::from_fn(|f| {
f.write_str("<h1>List of all items</h1>")?;
// Note: print_entries does not escape the title, because we know the current set of titles
// doesn't require escaping.
print_entries(&self.structs, ItemSection::Structs).fmt(f)?;
print_entries(&self.enums, ItemSection::Enums).fmt(f)?;
print_entries(&self.unions, ItemSection::Unions).fmt(f)?;
print_entries(&self.primitives, ItemSection::PrimitiveTypes).fmt(f)?;
print_entries(&self.traits, ItemSection::Traits).fmt(f)?;
print_entries(&self.macros, ItemSection::Macros).fmt(f)?;
print_entries(&self.attribute_macros, ItemSection::AttributeMacros).fmt(f)?;
print_entries(&self.derive_macros, ItemSection::DeriveMacros).fmt(f)?;
print_entries(&self.functions, ItemSection::Functions).fmt(f)?;
print_entries(&self.type_aliases, ItemSection::TypeAliases).fmt(f)?;
print_entries(&self.trait_aliases, ItemSection::TraitAliases).fmt(f)?;
print_entries(&self.statics, ItemSection::Statics).fmt(f)?;
print_entries(&self.constants, ItemSection::Constants).fmt(f)?;
Ok(())
})
}
}
@ -1205,7 +1225,7 @@ impl<'a> AssocItemLink<'a> {
}
fn write_section_heading(
title: &str,
title: impl fmt::Display,
id: &str,
extra_class: Option<&str>,
extra: impl fmt::Display,
@ -1225,7 +1245,7 @@ fn write_section_heading(
})
}
fn write_impl_section_heading(title: &str, id: &str) -> impl fmt::Display {
fn write_impl_section_heading(title: impl fmt::Display, id: &str) -> impl fmt::Display {
write_section_heading(title, id, None, "")
}
@ -1302,20 +1322,17 @@ fn render_assoc_items_inner(
let (mut non_trait, traits): (Vec<_>, _) =
v.iter().partition(|i| i.inner_impl().trait_.is_none());
if !non_trait.is_empty() {
let mut close_tags = <Vec<&str>>::with_capacity(1);
let mut tmp_buf = String::new();
let (render_mode, id, class_html) = match what {
AssocItemRender::All => {
write_str(
&mut tmp_buf,
format_args!(
"{}",
write_impl_section_heading("Implementations", "implementations")
),
);
(RenderMode::Normal, "implementations-list".to_owned(), "")
}
AssocItemRender::DerefFor { trait_, type_, deref_mut_ } => {
let render_mode = what.render_mode();
let class_html = what
.class()
.map(|class| fmt::from_fn(move |f| write!(f, r#" class="{class}""#)))
.maybe_display();
let (section_heading, id) = match what {
AssocItemRender::All => (
Either::Left(write_impl_section_heading("Implementations", "implementations")),
Cow::Borrowed("implementations-list"),
),
AssocItemRender::DerefFor { trait_, type_, .. } => {
let id =
cx.derive_id(small_url_encode(format!("deref-methods-{:#}", type_.print(cx))));
// the `impls.get` above only looks at the outermost type,
@ -1329,25 +1346,27 @@ fn render_assoc_items_inner(
type_.is_doc_subtype_of(&impl_.inner_impl().for_, &cx.shared.cache)
});
let derived_id = cx.derive_id(&id);
close_tags.push("</details>");
write_str(
&mut tmp_buf,
format_args!(
"<details class=\"toggle big-toggle\" open><summary>{}</summary>",
write_impl_section_heading(
&format!(
"<span>Methods from {trait_}&lt;Target = {type_}&gt;</span>",
trait_ = trait_.print(cx),
type_ = type_.print(cx),
),
&id,
)
),
);
if let Some(def_id) = type_.def_id(cx.cache()) {
cx.deref_id_map.borrow_mut().insert(def_id, id);
cx.deref_id_map.borrow_mut().insert(def_id, id.clone());
}
(RenderMode::ForDeref { mut_: deref_mut_ }, derived_id, r#" class="impl-items""#)
(
Either::Right(fmt::from_fn(move |f| {
write!(
f,
"<details class=\"toggle big-toggle\" open><summary>{}</summary>",
write_impl_section_heading(
fmt::from_fn(|f| write!(
f,
"<span>Methods from {trait_}&lt;Target = {type_}&gt;</span>",
trait_ = trait_.print(cx),
type_ = type_.print(cx),
)),
&id,
)
)
})),
Cow::Owned(derived_id),
)
}
};
let mut impls_buf = String::new();
@ -1375,10 +1394,14 @@ fn render_assoc_items_inner(
);
}
if !impls_buf.is_empty() {
write!(w, "{tmp_buf}<div id=\"{id}\"{class_html}>{impls_buf}</div>").unwrap();
for tag in close_tags.into_iter().rev() {
w.write_str(tag).unwrap();
}
write!(
w,
"{section_heading}<div id=\"{id}\"{class_html}>{impls_buf}</div>{}",
matches!(what, AssocItemRender::DerefFor { .. })
.then_some("</details>")
.maybe_display(),
)
.unwrap();
}
}
@ -1639,8 +1662,8 @@ fn render_impl(
// `containing_item` is used for rendering stability info. If the parent is a trait impl,
// `containing_item` will the grandparent, since trait impls can't have stability attached.
fn doc_impl_item(
boring: &mut String,
interesting: &mut String,
boring: impl fmt::Write,
interesting: impl fmt::Write,
cx: &Context<'_>,
item: &clean::Item,
parent: &clean::Item,
@ -1649,7 +1672,7 @@ fn render_impl(
is_default_item: bool,
trait_: Option<&clean::Trait>,
rendering_params: ImplRenderingParameters,
) {
) -> fmt::Result {
let item_type = item.type_();
let name = item.name.as_ref().unwrap();
@ -1724,15 +1747,16 @@ fn render_impl(
);
}
}
let w = if short_documented && trait_.is_some() { interesting } else { boring };
let mut w = if short_documented && trait_.is_some() {
Either::Left(interesting)
} else {
Either::Right(boring)
};
let toggled = !doc_buffer.is_empty();
if toggled {
let method_toggle_class = if item_type.is_method() { " method-toggle" } else { "" };
write_str(
w,
format_args!("<details class=\"toggle{method_toggle_class}\" open><summary>"),
);
write!(w, "<details class=\"toggle{method_toggle_class}\" open><summary>")?;
}
match &item.kind {
clean::MethodItem(..) | clean::RequiredMethodItem(_) => {
@ -1747,172 +1771,151 @@ fn render_impl(
.find(|item| item.name.map(|n| n == *name).unwrap_or(false))
})
.map(|item| format!("{}.{name}", item.type_()));
write_str(
write!(
w,
format_args!(
"<section id=\"{id}\" class=\"{item_type}{in_trait_class}\">\
"<section id=\"{id}\" class=\"{item_type}{in_trait_class}\">\
{}",
render_rightside(cx, item, render_mode)
),
);
render_rightside(cx, item, render_mode)
)?;
if trait_.is_some() {
// Anchors are only used on trait impls.
write_str(w, format_args!("<a href=\"#{id}\" class=\"anchor\">§</a>"));
write!(w, "<a href=\"#{id}\" class=\"anchor\">§</a>")?;
}
write_str(
write!(
w,
format_args!(
"<h4 class=\"code-header\">{}</h4></section>",
render_assoc_item(
item,
link.anchor(source_id.as_ref().unwrap_or(&id)),
ItemType::Impl,
cx,
render_mode,
),
"<h4 class=\"code-header\">{}</h4></section>",
render_assoc_item(
item,
link.anchor(source_id.as_ref().unwrap_or(&id)),
ItemType::Impl,
cx,
render_mode,
),
);
)?;
}
}
clean::RequiredAssocConstItem(generics, ty) => {
let source_id = format!("{item_type}.{name}");
let id = cx.derive_id(&source_id);
write_str(
write!(
w,
format_args!(
"<section id=\"{id}\" class=\"{item_type}{in_trait_class}\">\
"<section id=\"{id}\" class=\"{item_type}{in_trait_class}\">\
{}",
render_rightside(cx, item, render_mode)
),
);
render_rightside(cx, item, render_mode)
)?;
if trait_.is_some() {
// Anchors are only used on trait impls.
write_str(w, format_args!("<a href=\"#{id}\" class=\"anchor\">§</a>"));
write!(w, "<a href=\"#{id}\" class=\"anchor\">§</a>")?;
}
write_str(
write!(
w,
format_args!(
"<h4 class=\"code-header\">{}</h4></section>",
assoc_const(
item,
generics,
ty,
AssocConstValue::None,
link.anchor(if trait_.is_some() { &source_id } else { &id }),
0,
cx,
)
"<h4 class=\"code-header\">{}</h4></section>",
assoc_const(
item,
generics,
ty,
AssocConstValue::None,
link.anchor(if trait_.is_some() { &source_id } else { &id }),
0,
cx,
),
);
)?;
}
clean::ProvidedAssocConstItem(ci) | clean::ImplAssocConstItem(ci) => {
let source_id = format!("{item_type}.{name}");
let id = cx.derive_id(&source_id);
write_str(
write!(
w,
format_args!(
"<section id=\"{id}\" class=\"{item_type}{in_trait_class}\">\
"<section id=\"{id}\" class=\"{item_type}{in_trait_class}\">\
{}",
render_rightside(cx, item, render_mode)
),
);
render_rightside(cx, item, render_mode),
)?;
if trait_.is_some() {
// Anchors are only used on trait impls.
write_str(w, format_args!("<a href=\"#{id}\" class=\"anchor\">§</a>"));
write!(w, "<a href=\"#{id}\" class=\"anchor\">§</a>")?;
}
write_str(
write!(
w,
format_args!(
"<h4 class=\"code-header\">{}</h4></section>",
assoc_const(
item,
&ci.generics,
&ci.type_,
match item.kind {
clean::ProvidedAssocConstItem(_) =>
AssocConstValue::TraitDefault(&ci.kind),
clean::ImplAssocConstItem(_) => AssocConstValue::Impl(&ci.kind),
_ => unreachable!(),
},
link.anchor(if trait_.is_some() { &source_id } else { &id }),
0,
cx,
)
"<h4 class=\"code-header\">{}</h4></section>",
assoc_const(
item,
&ci.generics,
&ci.type_,
match item.kind {
clean::ProvidedAssocConstItem(_) =>
AssocConstValue::TraitDefault(&ci.kind),
clean::ImplAssocConstItem(_) => AssocConstValue::Impl(&ci.kind),
_ => unreachable!(),
},
link.anchor(if trait_.is_some() { &source_id } else { &id }),
0,
cx,
),
);
)?;
}
clean::RequiredAssocTypeItem(generics, bounds) => {
let source_id = format!("{item_type}.{name}");
let id = cx.derive_id(&source_id);
write_str(
write!(
w,
format_args!(
"<section id=\"{id}\" class=\"{item_type}{in_trait_class}\">\
"<section id=\"{id}\" class=\"{item_type}{in_trait_class}\">\
{}",
render_rightside(cx, item, render_mode)
),
);
render_rightside(cx, item, render_mode),
)?;
if trait_.is_some() {
// Anchors are only used on trait impls.
write_str(w, format_args!("<a href=\"#{id}\" class=\"anchor\">§</a>"));
write!(w, "<a href=\"#{id}\" class=\"anchor\">§</a>")?;
}
write_str(
write!(
w,
format_args!(
"<h4 class=\"code-header\">{}</h4></section>",
assoc_type(
item,
generics,
bounds,
None,
link.anchor(if trait_.is_some() { &source_id } else { &id }),
0,
cx,
)
"<h4 class=\"code-header\">{}</h4></section>",
assoc_type(
item,
generics,
bounds,
None,
link.anchor(if trait_.is_some() { &source_id } else { &id }),
0,
cx,
),
);
)?;
}
clean::AssocTypeItem(tydef, _bounds) => {
let source_id = format!("{item_type}.{name}");
let id = cx.derive_id(&source_id);
write_str(
write!(
w,
format_args!(
"<section id=\"{id}\" class=\"{item_type}{in_trait_class}\">\
"<section id=\"{id}\" class=\"{item_type}{in_trait_class}\">\
{}",
render_rightside(cx, item, render_mode)
),
);
render_rightside(cx, item, render_mode),
)?;
if trait_.is_some() {
// Anchors are only used on trait impls.
write_str(w, format_args!("<a href=\"#{id}\" class=\"anchor\">§</a>"));
write!(w, "<a href=\"#{id}\" class=\"anchor\">§</a>")?;
}
write_str(
write!(
w,
format_args!(
"<h4 class=\"code-header\">{}</h4></section>",
assoc_type(
item,
&tydef.generics,
&[], // intentionally leaving out bounds
Some(tydef.item_type.as_ref().unwrap_or(&tydef.type_)),
link.anchor(if trait_.is_some() { &source_id } else { &id }),
0,
cx,
)
"<h4 class=\"code-header\">{}</h4></section>",
assoc_type(
item,
&tydef.generics,
&[], // intentionally leaving out bounds
Some(tydef.item_type.as_ref().unwrap_or(&tydef.type_)),
link.anchor(if trait_.is_some() { &source_id } else { &id }),
0,
cx,
),
);
)?;
}
clean::StrippedItem(..) => return,
clean::StrippedItem(..) => return Ok(()),
_ => panic!("can't make docs for trait item with name {:?}", item.name),
}
w.push_str(&info_buffer);
w.write_str(&info_buffer)?;
if toggled {
w.push_str("</summary>");
w.push_str(&doc_buffer);
w.push_str("</details>");
write!(w, "</summary>{doc_buffer}</details>")?;
}
Ok(())
}
let mut impl_items = String::new();
@ -1955,7 +1958,7 @@ fn render_impl(
false,
trait_,
rendering_params,
);
)?;
}
_ => {}
}
@ -1973,7 +1976,7 @@ fn render_impl(
false,
trait_,
rendering_params,
);
)?;
}
for method in methods {
doc_impl_item(
@ -1987,20 +1990,20 @@ fn render_impl(
false,
trait_,
rendering_params,
);
)?;
}
}
fn render_default_items(
boring: &mut String,
interesting: &mut String,
mut boring: impl fmt::Write,
mut interesting: impl fmt::Write,
cx: &Context<'_>,
t: &clean::Trait,
i: &clean::Impl,
parent: &clean::Item,
render_mode: RenderMode,
rendering_params: ImplRenderingParameters,
) {
) -> fmt::Result {
for trait_item in &t.items {
// Skip over any default trait items that are impossible to reference
// (e.g. if it has a `Self: Sized` bound on an unsized type).
@ -2020,8 +2023,8 @@ fn render_impl(
let assoc_link = AssocItemLink::GotoSource(did.into(), &provided_methods);
doc_impl_item(
boring,
interesting,
&mut boring,
&mut interesting,
cx,
trait_item,
parent,
@ -2030,8 +2033,9 @@ fn render_impl(
true,
Some(t),
rendering_params,
);
)?;
}
Ok(())
}
// If we've implemented a trait, then also emit documentation for all
@ -2051,7 +2055,7 @@ fn render_impl(
&i.impl_item,
render_mode,
rendering_params,
);
)?;
}
}
if render_mode == RenderMode::Normal {

View File

@ -47,8 +47,7 @@ fn test_all_types_prints_header_once() {
// Regression test for #82477
let all_types = AllTypes::new();
let mut buffer = String::new();
all_types.print(&mut buffer);
let buffer = all_types.print().to_string();
assert_eq!(1, buffer.matches("List of all items").count());
}

View File

@ -1,42 +0,0 @@
//@ known-bug: #74451
//@ compile-flags: -Copt-level=0
#![feature(specialization)]
#![feature(unsize, coerce_unsized)]
#![allow(incomplete_features)]
#![crate_type = "lib"]
use std::ops::CoerceUnsized;
pub struct SmartassPtr<A: Smartass+?Sized>(A::Data);
pub trait Smartass {
type Data;
type Data2: CoerceUnsized<*const [u8]>;
}
pub trait MaybeObjectSafe {}
impl MaybeObjectSafe for () {}
impl<T> Smartass for T {
type Data = <Self as Smartass>::Data2;
default type Data2 = *const [u8; 0];
}
impl Smartass for () {
type Data2 = *const [u8; 1];
}
impl Smartass for dyn MaybeObjectSafe {
type Data = *const [u8];
type Data2 = *const [u8; 0];
}
impl<U: Smartass+?Sized, T: Smartass+?Sized> CoerceUnsized<SmartassPtr<T>> for SmartassPtr<U>
where <U as Smartass>::Data: std::ops::CoerceUnsized<<T as Smartass>::Data>
{}
pub fn conv(s: SmartassPtr<()>) -> SmartassPtr<dyn MaybeObjectSafe> {
s // This shouldn't coerce
}

View File

@ -0,0 +1,32 @@
//@ build-pass
// Regression test for <https://github.com/rust-lang/rust/issues/139812>.
// Make sure that the unsize coercion we collect in mono for `Signal<i32> -> Signal<dyn Any>`
// doesn't choke on the fact that the inner unsized field of `Signal<T>` is a (trivial) alias.
// This exercises a normalize call that is necessary since we're getting a type from the type
// system, which isn't guaranteed to be normalized after substitution.
#![feature(coerce_unsized)]
use std::ops::CoerceUnsized;
trait Mirror {
type Assoc: ?Sized;
}
impl<T: ?Sized> Mirror for T {
type Assoc = T;
}
trait Any {}
impl<T> Any for T {}
struct Signal<'a, T: ?Sized>(<&'a T as Mirror>::Assoc);
// This `CoerceUnsized` impl isn't special; it's a bit more restricted than we'd see in the wild,
// but this ICE also reproduces if we were to make it general over `Signal<T> -> Signal<U>`.
impl<'a> CoerceUnsized<Signal<'a, dyn Any>> for Signal<'a, i32> {}
fn main() {
Signal(&1i32) as Signal<dyn Any>;
}