Merge from rustc

This commit is contained in:
Ralf Jung 2025-02-05 09:36:46 +01:00
commit 7f95fa4cee
378 changed files with 4214 additions and 3006 deletions

View file

@ -455,6 +455,7 @@ tool_check_step!(Rls { path: "src/tools/rls" });
tool_check_step!(Rustfmt { path: "src/tools/rustfmt" });
tool_check_step!(MiroptTestTools { path: "src/tools/miropt-test-tools" });
tool_check_step!(TestFloatParse { path: "src/etc/test-float-parse" });
tool_check_step!(FeaturesStatusDump { path: "src/tools/features-status-dump" });
tool_check_step!(Bootstrap { path: "src/bootstrap", default: false });

View file

@ -339,7 +339,7 @@ fn copy_self_contained_objects(
// to using gcc from a glibc-targeting toolchain for linking.
// To do that we have to distribute musl startup objects as a part of Rust toolchain
// and link with them manually in the self-contained mode.
if target.contains("musl") && !target.contains("unikraft") {
if target.needs_crt_begin_end() {
let srcdir = builder.musl_libdir(target).unwrap_or_else(|| {
panic!("Target {:?} does not have a \"musl-libdir\" key", target.triple)
});

View file

@ -1295,7 +1295,9 @@ impl Step for CrtBeginEnd {
}
fn make_run(run: RunConfig<'_>) {
run.builder.ensure(CrtBeginEnd { target: run.target });
if run.target.needs_crt_begin_end() {
run.builder.ensure(CrtBeginEnd { target: run.target });
}
}
/// Build crtbegin.o/crtend.o for musl target.

View file

@ -311,3 +311,34 @@ impl Step for UnicodeTableGenerator {
cmd.run(builder);
}
}
#[derive(Debug, PartialOrd, Ord, Clone, Hash, PartialEq, Eq)]
pub struct FeaturesStatusDump;
impl Step for FeaturesStatusDump {
type Output = ();
const ONLY_HOSTS: bool = true;
fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
run.path("src/tools/features-status-dump")
}
fn make_run(run: RunConfig<'_>) {
run.builder.ensure(FeaturesStatusDump);
}
fn run(self, builder: &Builder<'_>) {
let mut cmd = builder.tool_cmd(Tool::FeaturesStatusDump);
cmd.arg("--library-path");
cmd.arg(builder.src.join("library"));
cmd.arg("--compiler-path");
cmd.arg(builder.src.join("compiler"));
cmd.arg("--output-path");
cmd.arg(builder.out.join("features-status-dump.json"));
cmd.run(builder);
}
}

View file

@ -365,6 +365,7 @@ bootstrap_tool!(
RustcPerfWrapper, "src/tools/rustc-perf-wrapper", "rustc-perf-wrapper";
WasmComponentLd, "src/tools/wasm-component-ld", "wasm-component-ld", is_unstable_tool = true, allow_features = "min_specialization";
UnicodeTableGenerator, "src/tools/unicode-table-generator", "unicode-table-generator";
FeaturesStatusDump, "src/tools/features-status-dump", "features-status-dump";
);
/// These are the submodules that are required for rustbook to work due to

View file

@ -659,7 +659,10 @@ impl Builder<'_> {
// Build proc macros both for the host and the target unless proc-macros are not
// supported by the target.
if target != compiler.host && cmd_kind != Kind::Check {
let error = command(self.rustc(compiler))
let mut rustc_cmd = command(self.rustc(compiler));
self.add_rustc_lib_path(compiler, &mut rustc_cmd);
let error = rustc_cmd
.arg("--target")
.arg(target.rustc_target_arg())
.arg("--print=file-names")
@ -667,6 +670,7 @@ impl Builder<'_> {
.arg("-")
.run_capture(self)
.stderr();
let not_supported = error
.lines()
.any(|line| line.contains("unsupported crate type `proc-macro`"));

View file

@ -938,6 +938,7 @@ impl<'a> Builder<'a> {
check::Bootstrap,
check::RunMakeSupport,
check::Compiletest,
check::FeaturesStatusDump,
),
Kind::Test => describe!(
crate::core::build_steps::toolstate::ToolStateCheck,
@ -1089,6 +1090,7 @@ impl<'a> Builder<'a> {
run::GenerateWindowsSys,
run::GenerateCompletions,
run::UnicodeTableGenerator,
run::FeaturesStatusDump,
),
Kind::Setup => {
describe!(setup::Profile, setup::Hook, setup::Link, setup::Editor)

View file

@ -575,6 +575,10 @@ impl TargetSelection {
env::var("OSTYPE").is_ok_and(|v| v.to_lowercase().contains("cygwin"))
}
pub fn needs_crt_begin_end(&self) -> bool {
self.contains("musl") && !self.contains("unikraft")
}
/// Path to the file defining the custom target, if any.
pub fn filepath(&self) -> Option<&Path> {
self.file.as_ref().map(Path::new)

View file

@ -0,0 +1,9 @@
# `new_range`
The tracking issue for this feature is: [#123741]
[#123741]: https://github.com/rust-lang/rust/issues/123741
---
Switch the syntaxes `a..`, `a..b`, and `a..=b` to resolve the new range types.

View file

@ -14,6 +14,7 @@ use rustc_span::Span;
use rustc_span::symbol::{Symbol, sym};
use crate::html::escape::Escape;
use crate::joined::Joined as _;
#[cfg(test)]
mod tests;
@ -396,6 +397,8 @@ impl Display<'_> {
sub_cfgs: &[Cfg],
separator: &str,
) -> fmt::Result {
use fmt::Display as _;
let short_longhand = self.1.is_long() && {
let all_crate_features =
sub_cfgs.iter().all(|sub_cfg| matches!(sub_cfg, Cfg::Cfg(sym::feature, Some(_))));
@ -414,20 +417,29 @@ impl Display<'_> {
}
};
for (i, sub_cfg) in sub_cfgs.iter().enumerate() {
if i != 0 {
fmt.write_str(separator)?;
}
if let (true, Cfg::Cfg(_, Some(feat))) = (short_longhand, sub_cfg) {
if self.1.is_html() {
write!(fmt, "<code>{feat}</code>")?;
} else {
write!(fmt, "`{feat}`")?;
}
} else {
write_with_opt_paren(fmt, !sub_cfg.is_all(), Display(sub_cfg, self.1))?;
}
}
fmt::from_fn(|f| {
sub_cfgs
.iter()
.map(|sub_cfg| {
fmt::from_fn(move |fmt| {
if let Cfg::Cfg(_, Some(feat)) = sub_cfg
&& short_longhand
{
if self.1.is_html() {
write!(fmt, "<code>{feat}</code>")?;
} else {
write!(fmt, "`{feat}`")?;
}
} else {
write_with_opt_paren(fmt, !sub_cfg.is_all(), Display(sub_cfg, self.1))?;
}
Ok(())
})
})
.joined(separator, f)
})
.fmt(fmt)?;
Ok(())
}
}
@ -439,11 +451,20 @@ impl fmt::Display for Display<'_> {
Cfg::Any(ref sub_cfgs) => {
let separator =
if sub_cfgs.iter().all(Cfg::is_simple) { " nor " } else { ", nor " };
for (i, sub_cfg) in sub_cfgs.iter().enumerate() {
fmt.write_str(if i == 0 { "neither " } else { separator })?;
write_with_opt_paren(fmt, !sub_cfg.is_all(), Display(sub_cfg, self.1))?;
}
Ok(())
fmt.write_str("neither ")?;
sub_cfgs
.iter()
.map(|sub_cfg| {
fmt::from_fn(|fmt| {
write_with_opt_paren(
fmt,
!sub_cfg.is_all(),
Display(sub_cfg, self.1),
)
})
})
.joined(separator, fmt)
}
ref simple @ Cfg::Cfg(..) => write!(fmt, "non-{}", Display(simple, self.1)),
ref c => write!(fmt, "not ({})", Display(c, self.1)),

View file

@ -1946,7 +1946,7 @@ fn can_elide_trait_object_lifetime_bound<'tcx>(
preds: &'tcx ty::List<ty::PolyExistentialPredicate<'tcx>>,
tcx: TyCtxt<'tcx>,
) -> bool {
// Below we quote extracts from https://doc.rust-lang.org/reference/lifetime-elision.html#default-trait-object-lifetimes
// Below we quote extracts from https://doc.rust-lang.org/stable/reference/lifetime-elision.html#default-trait-object-lifetimes
// > If the trait object is used as a type argument of a generic type then the containing type is
// > first used to try to infer a bound.

View file

@ -588,9 +588,9 @@ pub(crate) fn attrs_have_doc_flag<'a>(
/// so that the channel is consistent.
///
/// Set by `bootstrap::Builder::doc_rust_lang_org_channel` in order to keep tests passing on beta/stable.
pub(crate) const DOC_RUST_LANG_ORG_CHANNEL: &str = env!("DOC_RUST_LANG_ORG_CHANNEL");
pub(crate) static DOC_CHANNEL: Lazy<&'static str> =
Lazy::new(|| DOC_RUST_LANG_ORG_CHANNEL.rsplit('/').find(|c| !c.is_empty()).unwrap());
pub(crate) const DOC_RUST_LANG_ORG_VERSION: &str = env!("DOC_RUST_LANG_ORG_CHANNEL");
pub(crate) static RUSTDOC_VERSION: Lazy<&'static str> =
Lazy::new(|| DOC_RUST_LANG_ORG_VERSION.rsplit('/').find(|c| !c.is_empty()).unwrap());
/// Render a sequence of macro arms in a format suitable for displaying to the user
/// as part of an item declaration.

View file

@ -387,7 +387,7 @@ pub(crate) fn run_global_ctxt(
let help = format!(
"The following guide may be of use:\n\
{}/rustdoc/how-to-write-documentation.html",
crate::DOC_RUST_LANG_ORG_CHANNEL
crate::DOC_RUST_LANG_ORG_VERSION
);
tcx.node_lint(
crate::lint::MISSING_CRATE_LEVEL_DOCS,

View file

@ -8,12 +8,11 @@
//! not be used external to this module.
use std::borrow::Cow;
use std::cell::Cell;
use std::cmp::Ordering;
use std::fmt::{self, Display, Write};
use std::iter::{self, once};
use itertools::Itertools;
use itertools::Either;
use rustc_abi::ExternAbi;
use rustc_attr_parsing::{ConstStability, StabilityLevel, StableSince};
use rustc_data_structures::captures::Captures;
@ -35,6 +34,7 @@ use crate::formats::cache::Cache;
use crate::formats::item_type::ItemType;
use crate::html::escape::{Escape, EscapeBodyText};
use crate::html::render::Context;
use crate::joined::Joined as _;
use crate::passes::collect_intra_doc_links::UrlFragment;
pub(crate) trait Print {
@ -146,22 +146,6 @@ impl Buffer {
}
}
pub(crate) fn comma_sep<T: Display>(
items: impl Iterator<Item = T>,
space_after_comma: bool,
) -> impl Display {
let items = Cell::new(Some(items));
fmt::from_fn(move |f| {
for (i, item) in items.take().unwrap().enumerate() {
if i != 0 {
write!(f, ",{}", if space_after_comma { " " } else { "" })?;
}
item.fmt(f)?;
}
Ok(())
})
}
pub(crate) fn print_generic_bounds<'a, 'tcx: 'a>(
bounds: &'a [clean::GenericBound],
cx: &'a Context<'tcx>,
@ -169,13 +153,11 @@ pub(crate) fn print_generic_bounds<'a, 'tcx: 'a>(
fmt::from_fn(move |f| {
let mut bounds_dup = FxHashSet::default();
for (i, bound) in bounds.iter().filter(|b| bounds_dup.insert(*b)).enumerate() {
if i > 0 {
f.write_str(" + ")?;
}
bound.print(cx).fmt(f)?;
}
Ok(())
bounds
.iter()
.filter(move |b| bounds_dup.insert(*b))
.map(|bound| bound.print(cx))
.joined(" + ", f)
})
}
@ -190,12 +172,7 @@ impl clean::GenericParamDef {
if !outlives.is_empty() {
f.write_str(": ")?;
for (i, lt) in outlives.iter().enumerate() {
if i != 0 {
f.write_str(" + ")?;
}
write!(f, "{}", lt.print())?;
}
outlives.iter().map(|lt| lt.print()).joined(" + ", f)?;
}
Ok(())
@ -245,10 +222,12 @@ impl clean::Generics {
return Ok(());
}
let real_params =
fmt::from_fn(|f| real_params.clone().map(|g| g.print(cx)).joined(", ", f));
if f.alternate() {
write!(f, "<{:#}>", comma_sep(real_params.map(|g| g.print(cx)), true))
write!(f, "<{:#}>", real_params)
} else {
write!(f, "&lt;{}&gt;", comma_sep(real_params.map(|g| g.print(cx)), true))
write!(f, "&lt;{}&gt;", real_params)
}
})
}
@ -260,6 +239,42 @@ pub(crate) enum Ending {
NoNewline,
}
fn print_where_predicate<'a, 'tcx: 'a>(
predicate: &'a clean::WherePredicate,
cx: &'a Context<'tcx>,
) -> impl Display + 'a + Captures<'tcx> {
fmt::from_fn(move |f| {
match predicate {
clean::WherePredicate::BoundPredicate { ty, bounds, bound_params } => {
print_higher_ranked_params_with_space(bound_params, cx, "for").fmt(f)?;
ty.print(cx).fmt(f)?;
f.write_str(":")?;
if !bounds.is_empty() {
f.write_str(" ")?;
print_generic_bounds(bounds, cx).fmt(f)?;
}
Ok(())
}
clean::WherePredicate::RegionPredicate { lifetime, bounds } => {
// We don't need to check `alternate` since we can be certain that neither
// 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))?;
}
Ok(())
}
clean::WherePredicate::EqPredicate { lhs, rhs } => {
if f.alternate() {
write!(f, "{:#} == {:#}", lhs.print(cx), rhs.print(cx))
} else {
write!(f, "{} == {}", lhs.print(cx), rhs.print(cx))
}
}
}
})
}
/// * The Generics from which to emit a where-clause.
/// * The number of spaces to indent each line with.
/// * Whether the where-clause needs to add a comma and newline after the last bound.
@ -270,55 +285,26 @@ pub(crate) fn print_where_clause<'a, 'tcx: 'a>(
ending: Ending,
) -> impl Display + 'a + Captures<'tcx> {
fmt::from_fn(move |f| {
let mut where_predicates = gens
.where_predicates
.iter()
.map(|pred| {
fmt::from_fn(move |f| {
if f.alternate() {
f.write_str(" ")?;
} else {
f.write_str("\n")?;
}
match pred {
clean::WherePredicate::BoundPredicate { ty, bounds, bound_params } => {
print_higher_ranked_params_with_space(bound_params, cx, "for")
.fmt(f)?;
ty.print(cx).fmt(f)?;
f.write_str(":")?;
if !bounds.is_empty() {
f.write_str(" ")?;
print_generic_bounds(bounds, cx).fmt(f)?;
}
Ok(())
}
clean::WherePredicate::RegionPredicate { lifetime, bounds } => {
// We don't need to check `alternate` since we can be certain that neither
// 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))?;
}
Ok(())
}
clean::WherePredicate::EqPredicate { lhs, rhs } => {
if f.alternate() {
write!(f, "{:#} == {:#}", lhs.print(cx), rhs.print(cx))
} else {
write!(f, "{} == {}", lhs.print(cx), rhs.print(cx))
}
}
}
})
})
.peekable();
if where_predicates.peek().is_none() {
if gens.where_predicates.is_empty() {
return Ok(());
}
let where_preds = comma_sep(where_predicates, false);
let where_preds = fmt::from_fn(|f| {
gens.where_predicates
.iter()
.map(|predicate| {
fmt::from_fn(|f| {
if f.alternate() {
f.write_str(" ")?;
} else {
f.write_str("\n")?;
}
print_where_predicate(predicate, cx).fmt(f)
})
})
.joined(",", f)
});
let clause = if f.alternate() {
if ending == Ending::Newline {
format!(" where{where_preds},")
@ -415,12 +401,7 @@ impl clean::GenericBound {
} else {
f.write_str("use&lt;")?;
}
for (i, arg) in args.iter().enumerate() {
if i > 0 {
write!(f, ", ")?;
}
arg.fmt(f)?;
}
args.iter().joined(", ", f)?;
if f.alternate() { f.write_str(">") } else { f.write_str("&gt;") }
}
})
@ -438,29 +419,18 @@ impl clean::GenericArgs {
} else {
f.write_str("&lt;")?;
}
let mut comma = false;
for arg in args.iter() {
if comma {
f.write_str(", ")?;
}
comma = true;
if f.alternate() {
write!(f, "{:#}", arg.print(cx))?;
} else {
write!(f, "{}", arg.print(cx))?;
}
}
for constraint in constraints.iter() {
if comma {
f.write_str(", ")?;
}
comma = true;
if f.alternate() {
write!(f, "{:#}", constraint.print(cx))?;
} else {
write!(f, "{}", constraint.print(cx))?;
}
}
[Either::Left(args), Either::Right(constraints)]
.into_iter()
.flat_map(Either::factor_into_iter)
.map(|either| {
either.map_either(
|arg| arg.print(cx),
|constraint| constraint.print(cx),
)
})
.joined(", ", f)?;
if f.alternate() {
f.write_str(">")?;
} else {
@ -470,14 +440,7 @@ impl clean::GenericArgs {
}
clean::GenericArgs::Parenthesized { inputs, output } => {
f.write_str("(")?;
let mut comma = false;
for ty in inputs.iter() {
if comma {
f.write_str(", ")?;
}
comma = true;
ty.print(cx).fmt(f)?;
}
inputs.iter().map(|ty| ty.print(cx)).joined(", ", f)?;
f.write_str(")")?;
if let Some(ref ty) = *output {
if f.alternate() {
@ -524,6 +487,7 @@ pub(crate) enum HrefError {
// Panics if `syms` is empty.
pub(crate) fn join_with_double_colon(syms: &[Symbol]) -> String {
let mut s = String::with_capacity(estimate_item_path_byte_length(syms.len()));
// NOTE: using `Joined::joined` here causes a noticeable perf regression
s.push_str(syms[0].as_str());
for sym in &syms[1..] {
s.push_str("::");
@ -572,20 +536,20 @@ fn generate_macro_def_id_path(
}
if let Some(last) = path.last_mut() {
*last = Symbol::intern(&format!("macro.{}.html", last.as_str()));
*last = Symbol::intern(&format!("macro.{last}.html"));
}
let url = match cache.extern_locations[&def_id.krate] {
ExternalLocation::Remote(ref s) => {
// `ExternalLocation::Remote` always end with a `/`.
format!("{s}{path}", path = path.iter().map(|p| p.as_str()).join("/"))
format!("{s}{path}", path = fmt::from_fn(|f| path.iter().joined("/", f)))
}
ExternalLocation::Local => {
// `root_path` always end with a `/`.
format!(
"{root_path}{path}",
root_path = root_path.unwrap_or(""),
path = path.iter().map(|p| p.as_str()).join("/")
path = fmt::from_fn(|f| path.iter().joined("/", f))
)
}
ExternalLocation::Unknown => {
@ -682,9 +646,8 @@ fn make_href(
url_parts.push("index.html");
}
_ => {
let prefix = shortty.as_str();
let last = fqp.last().unwrap();
url_parts.push_fmt(format_args!("{prefix}.{last}.html"));
url_parts.push_fmt(format_args!("{shortty}.{last}.html"));
}
}
Ok((url_parts.finish(), shortty, fqp.to_vec()))
@ -950,12 +913,7 @@ fn tybounds<'a, 'tcx: 'a>(
cx: &'a Context<'tcx>,
) -> impl Display + 'a + Captures<'tcx> {
fmt::from_fn(move |f| {
for (i, bound) in bounds.iter().enumerate() {
if i > 0 {
write!(f, " + ")?;
}
bound.print(cx).fmt(f)?;
}
bounds.iter().map(|bound| bound.print(cx)).joined(" + ", f)?;
if let Some(lt) = lt {
// We don't need to check `alternate` since we can be certain that
// the lifetime doesn't contain any characters which need escaping.
@ -974,7 +932,7 @@ fn print_higher_ranked_params_with_space<'a, 'tcx: 'a>(
if !params.is_empty() {
f.write_str(keyword)?;
f.write_str(if f.alternate() { "<" } else { "&lt;" })?;
comma_sep(params.iter().map(|lt| lt.print(cx)), true).fmt(f)?;
params.iter().map(|lt| lt.print(cx)).joined(", ", f)?;
f.write_str(if f.alternate() { "> " } else { "&gt; " })?;
}
Ok(())
@ -1025,9 +983,7 @@ fn fmt_type(
clean::Primitive(clean::PrimitiveType::Never) => {
primitive_link(f, PrimitiveType::Never, format_args!("!"), cx)
}
clean::Primitive(prim) => {
primitive_link(f, prim, format_args!("{}", prim.as_sym().as_str()), cx)
}
clean::Primitive(prim) => primitive_link(f, prim, format_args!("{}", prim.as_sym()), cx),
clean::BareFunction(ref decl) => {
print_higher_ranked_params_with_space(&decl.generic_params, cx, "for").fmt(f)?;
decl.safety.print_with_space().fmt(f)?;
@ -1067,18 +1023,16 @@ fn fmt_type(
primitive_link(
f,
PrimitiveType::Tuple,
format_args!("({})", generic_names.iter().map(|s| s.as_str()).join(", ")),
format_args!(
"({})",
fmt::from_fn(|f| generic_names.iter().joined(", ", f))
),
cx,
)
} else {
write!(f, "(")?;
for (i, item) in many.iter().enumerate() {
if i != 0 {
write!(f, ", ")?;
}
item.print(cx).fmt(f)?;
}
write!(f, ")")
f.write_str("(")?;
many.iter().map(|item| item.print(cx)).joined(", ", f)?;
f.write_str(")")
}
}
},
@ -1407,16 +1361,17 @@ impl clean::Arguments {
cx: &'a Context<'tcx>,
) -> impl Display + 'a + Captures<'tcx> {
fmt::from_fn(move |f| {
for (i, input) in self.values.iter().enumerate() {
if !input.name.is_empty() {
write!(f, "{}: ", input.name)?;
}
input.type_.print(cx).fmt(f)?;
if i + 1 < self.values.len() {
write!(f, ", ")?;
}
}
Ok(())
self.values
.iter()
.map(|input| {
fmt::from_fn(|f| {
if !input.name.is_empty() {
write!(f, "{}: ", input.name)?;
}
input.type_.print(cx).fmt(f)
})
})
.joined(", ", f)
})
}
}
@ -1725,12 +1680,7 @@ impl clean::ImportSource {
}
let name = self.path.last();
if let hir::def::Res::PrimTy(p) = self.path.res {
primitive_link(
f,
PrimitiveType::from(p),
format_args!("{}", name.as_str()),
cx,
)?;
primitive_link(f, PrimitiveType::from(p), format_args!("{name}"), cx)?;
} else {
f.write_str(name.as_str())?;
}

View file

@ -112,7 +112,7 @@ pub(crate) fn render<T: Print, S: Print>(
display_krate_with_trailing_slash,
display_krate_version_number,
display_krate_version_extra,
rust_channel: *crate::clean::utils::DOC_CHANNEL,
rust_channel: *crate::clean::utils::RUSTDOC_VERSION,
rustdoc_version,
}
.render()

View file

@ -32,7 +32,7 @@ use crate::html::render::write_shared::write_shared;
use crate::html::url_parts_builder::UrlPartsBuilder;
use crate::html::{layout, sources, static_files};
use crate::scrape_examples::AllCallLocations;
use crate::try_err;
use crate::{DOC_RUST_LANG_ORG_VERSION, try_err};
/// Major driving force in all rustdoc rendering. This contains information
/// about where in the tree-like hierarchy rendering is occurring and controls
@ -730,7 +730,7 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> {
<noscript>\
<section>\
<p>You need to enable JavaScript to use keyboard commands or search.</p>\
<p>For more information, browse the <a href=\"https://doc.rust-lang.org/rustdoc/\">rustdoc handbook</a>.</p>\
<p>For more information, browse the <a href=\"{DOC_RUST_LANG_ORG_VERSION}/rustdoc/\">rustdoc handbook</a>.</p>\
</section>\
</noscript>",
)

View file

@ -79,7 +79,7 @@ use crate::html::markdown::{
use crate::html::static_files::SCRAPE_EXAMPLES_HELP_MD;
use crate::html::{highlight, sources};
use crate::scrape_examples::{CallData, CallLocation};
use crate::{DOC_RUST_LANG_ORG_CHANNEL, try_none};
use crate::{DOC_RUST_LANG_ORG_VERSION, try_none};
pub(crate) fn ensure_trailing_slash(v: &str) -> impl fmt::Display + '_ {
fmt::from_fn(move |f| {
@ -480,7 +480,7 @@ fn scrape_examples_help(shared: &SharedContext<'_>) -> String {
content.push_str(&format!(
"## More information\n\n\
If you want more information about this feature, please read the [corresponding chapter in \
the Rustdoc book]({DOC_RUST_LANG_ORG_CHANNEL}/rustdoc/scraped-examples.html)."
the Rustdoc book]({DOC_RUST_LANG_ORG_VERSION}/rustdoc/scraped-examples.html)."
));
let mut ids = IdMap::default();

View file

@ -1,5 +1,6 @@
use std::cmp::Ordering;
use std::fmt;
use std::fmt::{Display, Write};
use itertools::Itertools;
use rinja::Template;
@ -36,6 +37,7 @@ use crate::html::format::{
use crate::html::markdown::{HeadingOffset, MarkdownSummaryLine};
use crate::html::render::{document_full, document_item_info};
use crate::html::url_parts_builder::UrlPartsBuilder;
use crate::joined::Joined as _;
/// Generates a Rinja template struct for rendering items with common methods.
///
@ -290,7 +292,7 @@ fn should_hide_fields(n_fields: usize) -> bool {
n_fields > 12
}
fn toggle_open(mut w: impl fmt::Write, text: impl fmt::Display) {
fn toggle_open(mut w: impl fmt::Write, text: impl Display) {
write!(
w,
"<details class=\"toggle type-contents-toggle\">\
@ -305,7 +307,7 @@ fn toggle_close(mut w: impl fmt::Write) {
w.write_str("</details>").unwrap();
}
trait ItemTemplate<'a, 'cx: 'a>: rinja::Template + fmt::Display {
trait ItemTemplate<'a, 'cx: 'a>: rinja::Template + Display {
fn item_and_cx(&self) -> (&'a clean::Item, &'a Context<'cx>);
}
@ -519,13 +521,9 @@ fn extra_info_tags<'a, 'tcx: 'a>(
item: &'a clean::Item,
parent: &'a clean::Item,
import_def_id: Option<DefId>,
) -> impl fmt::Display + 'a + Captures<'tcx> {
) -> impl Display + 'a + Captures<'tcx> {
fmt::from_fn(move |f| {
fn tag_html<'a>(
class: &'a str,
title: &'a str,
contents: &'a str,
) -> impl fmt::Display + 'a {
fn tag_html<'a>(class: &'a str, title: &'a str, contents: &'a str) -> impl Display + 'a {
fmt::from_fn(move |f| {
write!(
f,
@ -924,7 +922,7 @@ fn item_trait(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, t: &clean::Tra
<a href=\"{base}/reference/items/traits.html#dyn-compatibility\">dyn compatible</a>.</p>\
<p><i>In older versions of Rust, dyn compatibility was called \"object safety\", \
so this trait is not object safe.</i></p></div>",
base = crate::clean::utils::DOC_RUST_LANG_ORG_CHANNEL
base = crate::clean::utils::DOC_RUST_LANG_ORG_VERSION
),
);
}
@ -1374,7 +1372,7 @@ fn item_union(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, s: &clean::Uni
);
impl<'a, 'cx: 'a> ItemUnion<'a, 'cx> {
fn render_union<'b>(&'b self) -> impl fmt::Display + Captures<'a> + 'b + Captures<'cx> {
fn render_union<'b>(&'b self) -> impl Display + Captures<'a> + 'b + Captures<'cx> {
fmt::from_fn(move |f| {
let v = render_union(self.it, Some(&self.s.generics), &self.s.fields, self.cx);
write!(f, "{v}")
@ -1384,7 +1382,7 @@ fn item_union(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, s: &clean::Uni
fn document_field<'b>(
&'b self,
field: &'a clean::Item,
) -> impl fmt::Display + Captures<'a> + 'b + Captures<'cx> {
) -> impl Display + Captures<'a> + 'b + Captures<'cx> {
fmt::from_fn(move |f| {
let v = document(self.cx, field, Some(self.it), HeadingOffset::H3);
write!(f, "{v}")
@ -1398,7 +1396,7 @@ fn item_union(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, s: &clean::Uni
fn print_ty<'b>(
&'b self,
ty: &'a clean::Type,
) -> impl fmt::Display + Captures<'a> + 'b + Captures<'cx> {
) -> impl Display + Captures<'a> + 'b + Captures<'cx> {
fmt::from_fn(move |f| {
let v = ty.print(self.cx);
write!(f, "{v}")
@ -1425,7 +1423,7 @@ fn item_union(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, s: &clean::Uni
fn print_tuple_struct_fields<'a, 'cx: 'a>(
cx: &'a Context<'cx>,
s: &'a [clean::Item],
) -> impl fmt::Display + 'a + Captures<'cx> {
) -> impl Display + 'a + Captures<'cx> {
fmt::from_fn(|f| {
if !s.is_empty()
&& s.iter().all(|field| {
@ -1435,17 +1433,15 @@ fn print_tuple_struct_fields<'a, 'cx: 'a>(
return f.write_str("<span class=\"comment\">/* private fields */</span>");
}
for (i, ty) in s.iter().enumerate() {
if i > 0 {
f.write_str(", ")?;
}
match ty.kind {
clean::StrippedItem(box clean::StructFieldItem(_)) => f.write_str("_")?,
clean::StructFieldItem(ref ty) => write!(f, "{}", ty.print(cx))?,
_ => unreachable!(),
}
}
Ok(())
s.iter()
.map(|ty| {
fmt::from_fn(|f| match ty.kind {
clean::StrippedItem(box clean::StructFieldItem(_)) => f.write_str("_"),
clean::StructFieldItem(ref ty) => write!(f, "{}", ty.print(cx)),
_ => unreachable!(),
})
})
.joined(", ", f)
})
}
@ -2068,12 +2064,12 @@ fn bounds(t_bounds: &[clean::GenericBound], trait_alias: bool, cx: &Context<'_>)
bounds.push_str(": ");
}
}
for (i, p) in t_bounds.iter().enumerate() {
if i > 0 {
bounds.push_str(inter_str);
}
bounds.push_str(&p.print(cx).to_string());
}
write!(
bounds,
"{}",
fmt::from_fn(|f| t_bounds.iter().map(|p| p.print(cx)).joined(inter_str, f))
)
.unwrap();
bounds
}
@ -2150,7 +2146,7 @@ fn render_union<'a, 'cx: 'a>(
g: Option<&'a clean::Generics>,
fields: &'a [clean::Item],
cx: &'a Context<'cx>,
) -> impl fmt::Display + 'a + Captures<'cx> {
) -> impl Display + 'a + Captures<'cx> {
fmt::from_fn(move |mut f| {
write!(f, "{}union {}", visibility_print_with_space(it, cx), it.name.unwrap(),)?;
@ -2330,7 +2326,7 @@ fn document_non_exhaustive_header(item: &clean::Item) -> &str {
if item.is_non_exhaustive() { " (Non-exhaustive)" } else { "" }
}
fn document_non_exhaustive(item: &clean::Item) -> impl fmt::Display + '_ {
fn document_non_exhaustive(item: &clean::Item) -> impl Display + '_ {
fmt::from_fn(|f| {
if item.is_non_exhaustive() {
write!(

View file

@ -1534,10 +1534,10 @@ function preLoadCss(cssUrl) {
function buildHelpMenu() {
const book_info = document.createElement("span");
const channel = getVar("channel");
const drloChannel = `https://doc.rust-lang.org/${getVar("channel")}`;
book_info.className = "top";
book_info.innerHTML = `You can find more information in \
<a href="https://doc.rust-lang.org/${channel}/rustdoc/">the rustdoc book</a>.`;
<a href="${drloChannel}/rustdoc/">the rustdoc book</a>.`;
const shortcuts = [
["?", "Show this help dialog"],
@ -1557,8 +1557,8 @@ function preLoadCss(cssUrl) {
div_shortcuts.innerHTML = "<h2>Keyboard Shortcuts</h2><dl>" + shortcuts + "</dl></div>";
const infos = [
`For a full list of all search features, take a look <a \
href="https://doc.rust-lang.org/${channel}/rustdoc/read-documentation/search.html">here</a>.`,
`For a full list of all search features, take a look \
<a href="${drloChannel}/rustdoc/read-documentation/search.html">here</a>.`,
"Prefix searches with a type followed by a colon (e.g., <code>fn:</code>) to \
restrict the search to a given item kind.",
"Accepted kinds are: <code>fn</code>, <code>mod</code>, <code>struct</code>, \
@ -1568,10 +1568,10 @@ href="https://doc.rust-lang.org/${channel}/rustdoc/read-documentation/search.htm
<code>-&gt; vec</code> or <code>String, enum:Cow -&gt; bool</code>)",
"You can look for items with an exact name by putting double quotes around \
your request: <code>\"string\"</code>",
"Look for functions that accept or return \
<a href=\"https://doc.rust-lang.org/std/primitive.slice.html\">slices</a> and \
<a href=\"https://doc.rust-lang.org/std/primitive.array.html\">arrays</a> by writing \
square brackets (e.g., <code>-&gt; [u8]</code> or <code>[] -&gt; Option</code>)",
`Look for functions that accept or return \
<a href="${drloChannel}/std/primitive.slice.html">slices</a> and \
<a href="${drloChannel}/std/primitive.array.html">arrays</a> by writing square \
brackets (e.g., <code>-&gt; [u8]</code> or <code>[] -&gt; Option</code>)`,
"Look for items inside another one by searching for a path: <code>vec::Vec</code>",
].map(x => "<p>" + x + "</p>").join("");
const div_infos = document.createElement("div");

View file

@ -1,5 +1,5 @@
// ignore-tidy-filelength
/* global addClass, getNakedUrl, getSettingValue */
/* global addClass, getNakedUrl, getSettingValue, getVar */
/* global onEachLazy, removeClass, searchState, browserSupportsHistoryApi, exports */
"use strict";
@ -4923,17 +4923,18 @@ ${item.displayPath}<span class="${type}">${name}</span>\
}
});
} else if (query.error === null) {
const dlroChannel = `https://doc.rust-lang.org/${getVar("channel")}`;
output.className = "search-failed" + extraClass;
output.innerHTML = "No results :(<br/>" +
"Try on <a href=\"https://duckduckgo.com/?q=" +
encodeURIComponent("rust " + query.userQuery) +
"\">DuckDuckGo</a>?<br/><br/>" +
"Or try looking in one of these:<ul><li>The <a " +
"href=\"https://doc.rust-lang.org/reference/index.html\">Rust Reference</a> " +
`href="${dlroChannel}/reference/index.html">Rust Reference</a> ` +
" for technical details about the language.</li><li><a " +
"href=\"https://doc.rust-lang.org/rust-by-example/index.html\">Rust By " +
`href="${dlroChannel}/rust-by-example/index.html">Rust By ` +
"Example</a> for expository code examples.</a></li><li>The <a " +
"href=\"https://doc.rust-lang.org/book/index.html\">Rust Book</a> for " +
`href="${dlroChannel}/book/index.html">Rust Book</a> for ` +
"introductions to language features and the language itself.</li><li><a " +
"href=\"https://docs.rs\">Docs.rs</a> for documentation of crates released on" +
" <a href=\"https://crates.io/\">crates.io</a>.</li></ul>";

View file

@ -10,7 +10,7 @@
unstable</strong> and may even differ between compilations. {#+ #}
The only exception is types with certain <code>repr(...)</code> {#+ #}
attributes. Please see the Rust Reference's {#+ #}
<a href="https://doc.rust-lang.org/reference/type-layout.html">“Type Layout”</a> {#+ #}
<a href="{{ crate::DOC_RUST_LANG_ORG_VERSION }}/reference/type-layout.html">“Type Layout”</a> {#+ #}
chapter for details on type layout guarantees. {# #}
</p> {# #}
</div> {# #}

View file

@ -7,6 +7,6 @@
{{ size +}} bytes
{% endif %}
{% if is_uninhabited %}
{# +#} (<a href="https://doc.rust-lang.org/stable/reference/glossary.html#uninhabited">uninhabited</a>)
{# +#} (<a href="{{ crate::DOC_RUST_LANG_ORG_VERSION }}/reference/glossary.html#uninhabited">uninhabited</a>)
{% endif %}
{% endif %}

29
src/librustdoc/joined.rs Normal file
View file

@ -0,0 +1,29 @@
use std::fmt::{self, Display, Formatter};
pub(crate) trait Joined: IntoIterator {
/// Takes an iterator over elements that implement [`Display`], and format them into `f`, separated by `sep`.
///
/// This is similar to [`Itertools::format`](itertools::Itertools::format), but instead of returning an implementation of `Display`,
/// it formats directly into a [`Formatter`].
///
/// The performance of `joined` is slightly better than `format`, since it doesn't need to use a `Cell` to keep track of whether [`fmt`](Display::fmt)
/// was already called (`joined`'s API doesn't allow it be called more than once).
fn joined(self, sep: &str, f: &mut Formatter<'_>) -> fmt::Result;
}
impl<I, T> Joined for I
where
I: IntoIterator<Item = T>,
T: Display,
{
fn joined(self, sep: &str, f: &mut Formatter<'_>) -> fmt::Result {
let mut iter = self.into_iter();
let Some(first) = iter.next() else { return Ok(()) };
first.fmt(f)?;
while let Some(item) = iter.next() {
f.write_str(sep)?;
item.fmt(f)?;
}
Ok(())
}
}

View file

@ -82,7 +82,7 @@ use rustc_session::config::{ErrorOutputType, RustcOptGroup, make_crate_type_opti
use rustc_session::{EarlyDiagCtxt, getopts};
use tracing::info;
use crate::clean::utils::DOC_RUST_LANG_ORG_CHANNEL;
use crate::clean::utils::DOC_RUST_LANG_ORG_VERSION;
/// A macro to create a FxHashMap.
///
@ -113,6 +113,7 @@ mod fold;
mod formats;
// used by the error-index generator, so it needs to be public
pub mod html;
mod joined;
mod json;
pub(crate) mod lint;
mod markdown;
@ -709,7 +710,7 @@ fn usage(argv0: &str) {
println!("{}", options.usage(&format!("{argv0} [options] <input>")));
println!(" @path Read newline separated options from `path`\n");
println!(
"More information available at {DOC_RUST_LANG_ORG_CHANNEL}/rustdoc/what-is-rustdoc.html",
"More information available at {DOC_RUST_LANG_ORG_VERSION}/rustdoc/what-is-rustdoc.html",
);
}

View file

@ -2168,7 +2168,7 @@ fn disambiguator_error(
report_diagnostic(cx.tcx, BROKEN_INTRA_DOC_LINKS, msg, &diag_info, |diag, _sp, _link_range| {
let msg = format!(
"see {}/rustdoc/write-documentation/linking-to-items-by-name.html#namespaces-and-disambiguators for more info about disambiguators",
crate::DOC_RUST_LANG_ORG_CHANNEL
crate::DOC_RUST_LANG_ORG_VERSION
);
diag.note(msg);
});

View file

@ -10,7 +10,7 @@ use rustc_hir::{
AssocItemConstraint, BinOpKind, BindingMode, Block, BodyId, Closure, ConstArg, ConstArgKind, Expr, ExprField,
ExprKind, FnRetTy, GenericArg, GenericArgs, HirId, HirIdMap, InlineAsmOperand, LetExpr, Lifetime, LifetimeName,
Pat, PatExpr, PatExprKind, PatField, PatKind, Path, PathSegment, PrimTy, QPath, Stmt, StmtKind, StructTailExpr,
TraitBoundModifiers, Ty, TyKind,
TraitBoundModifiers, Ty, TyKind, TyPat, TyPatKind,
};
use rustc_lexer::{TokenKind, tokenize};
use rustc_lint::LateContext;
@ -1102,6 +1102,22 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> {
}
}
pub fn hash_ty_pat(&mut self, pat: &TyPat<'_>) {
std::mem::discriminant(&pat.kind).hash(&mut self.s);
match pat.kind {
TyPatKind::Range(s, e, i) => {
if let Some(s) = s {
self.hash_const_arg(s);
}
if let Some(e) = e {
self.hash_const_arg(e);
}
std::mem::discriminant(&i).hash(&mut self.s);
},
TyPatKind::Err(_) => {},
}
}
pub fn hash_pat(&mut self, pat: &Pat<'_>) {
std::mem::discriminant(&pat.kind).hash(&mut self.s);
match pat.kind {
@ -1247,7 +1263,7 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> {
},
TyKind::Pat(ty, pat) => {
self.hash_ty(ty);
self.hash_pat(pat);
self.hash_ty_pat(pat);
},
TyKind::Ptr(mut_ty) => {
self.hash_ty(mut_ty.ty);

View file

@ -0,0 +1,12 @@
[package]
name = "features-status-dump"
version = "0.1.0"
license = "MIT OR Apache-2.0"
edition = "2021"
[dependencies]
anyhow = { version = "1", features = ["backtrace"] }
clap = { version = "4", features = ["derive"] }
serde = { version = "1.0.125", features = [ "derive" ] }
serde_json = "1.0.59"
tidy = { path = "../tidy", features = ["build-metrics"] }

View file

@ -0,0 +1,53 @@
use std::collections::HashMap;
use std::fs::File;
use std::io::BufWriter;
use std::path::PathBuf;
use anyhow::{Context, Result};
use clap::Parser;
use tidy::features::{Feature, collect_lang_features, collect_lib_features};
#[derive(Debug, Parser)]
struct Cli {
/// Path to `library/` directory.
#[arg(long)]
library_path: PathBuf,
/// Path to `compiler/` directory.
#[arg(long)]
compiler_path: PathBuf,
/// Path to `output/` directory.
#[arg(long)]
output_path: PathBuf,
}
#[derive(Debug, serde::Serialize)]
struct FeaturesStatus {
lang_features_status: HashMap<String, Feature>,
lib_features_status: HashMap<String, Feature>,
}
fn main() -> Result<()> {
let Cli { compiler_path, library_path, output_path } = Cli::parse();
let lang_features_status = collect_lang_features(&compiler_path, &mut false);
let lib_features_status = collect_lib_features(&library_path)
.into_iter()
.filter(|&(ref name, _)| !lang_features_status.contains_key(name))
.collect();
let features_status = FeaturesStatus { lang_features_status, lib_features_status };
let output_dir = output_path.parent().with_context(|| {
format!("failed to get parent dir of output path `{}`", output_path.display())
})?;
std::fs::create_dir_all(output_dir).with_context(|| {
format!("failed to create output directory at `{}`", output_dir.display())
})?;
let output_file = File::create(&output_path).with_context(|| {
format!("failed to create file at given output path `{}`", output_path.display())
})?;
let writer = BufWriter::new(output_file);
serde_json::to_writer_pretty(writer, &features_status)
.context("failed to write json output")?;
Ok(())
}

View file

@ -0,0 +1,5 @@
#![feature(core_intrinsics)]
fn main() {
// one bit in common
unsafe { std::intrinsics::disjoint_bitor(0b01101001_u8, 0b10001110) }; //~ ERROR: Undefined Behavior
}

View file

@ -0,0 +1,15 @@
error: Undefined Behavior: `assume` called with `false`
--> tests/fail/intrinsics/disjoint_bitor.rs:LL:CC
|
LL | unsafe { std::intrinsics::disjoint_bitor(0b01101001_u8, 0b10001110) };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `assume` called with `false`
|
= help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
= help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
= note: BACKTRACE:
= note: inside `main` at tests/fail/intrinsics/disjoint_bitor.rs:LL:CC
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
error: aborting due to 1 previous error

View file

@ -12,11 +12,15 @@ miropt-test-tools = { path = "../miropt-test-tools" }
walkdir = "2"
ignore = "0.4.18"
semver = "1.0"
serde = { version = "1.0.125", features = ["derive"], optional = true }
termcolor = "1.1.3"
rustc-hash = "2.0.0"
fluent-syntax = "0.11.1"
similar = "2.5.0"
[features]
build-metrics = ["dep:serde"]
[[bin]]
name = "rust-tidy"
path = "src/main.rs"

View file

@ -27,6 +27,7 @@ const FEATURE_GROUP_START_PREFIX: &str = "// feature-group-start";
const FEATURE_GROUP_END_PREFIX: &str = "// feature-group-end";
#[derive(Debug, PartialEq, Clone)]
#[cfg_attr(feature = "build-metrics", derive(serde::Serialize))]
pub enum Status {
Accepted,
Removed,
@ -45,6 +46,7 @@ impl fmt::Display for Status {
}
#[derive(Debug, Clone)]
#[cfg_attr(feature = "build-metrics", derive(serde::Serialize))]
pub struct Feature {
pub level: Status,
pub since: Option<Version>,

View file

@ -8,6 +8,7 @@ mod tests;
pub const VERSION_PLACEHOLDER: &str = "CURRENT_RUSTC_VERSION";
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
#[cfg_attr(feature = "build-metrics", derive(serde::Serialize))]
pub enum Version {
Explicit { parts: [u32; 3] },
CurrentPlaceholder,

View file

@ -48,8 +48,9 @@ const EXCEPTION_PATHS: &[&str] = &[
// we must use `#[cfg(windows)]` to conditionally compile the
// correct `VaList` structure for windows.
"library/core/src/ffi/va_list.rs",
// We placed a linkage against Windows libraries here
// core::ffi contains platform-specific type and linkage configuration
"library/core/src/ffi/mod.rs",
"library/core/src/ffi/primitives.rs",
"library/std/src/sys", // Platform-specific code for std lives here.
"library/std/src/os", // Platform-specific public interfaces
// Temporary `std` exceptions