Auto merge of #149276 - matthiaskrgr:rollup-wlrpdrr, r=matthiaskrgr

Rollup of 6 pull requests

Successful merges:

 - rust-lang/rust#148234 (rustdoc: make mergeable crate info more usable)
 - rust-lang/rust#149201 (Add suggest alternatives for Out-of-range \x escapes)
 - rust-lang/rust#149208 ([rustdoc] Make more functions return `fmt::Result` and reduce number of `.unwrap()` calls)
 - rust-lang/rust#149252 (miri: use `tikv-jemalloc-sys` from sysroot)
 - rust-lang/rust#149255 (Use `let...else` consistently in user-facing diagnostics)
 - rust-lang/rust#149275 (Fix missing double-quote in `std::env::consts::OS` values)

r? `@ghost`
`@rustbot` modify labels: rollup
This commit is contained in:
bors 2025-11-24 20:22:07 +00:00
commit c871d09d1c
44 changed files with 365 additions and 142 deletions

View file

@ -2486,7 +2486,6 @@ dependencies = [
"serde_json",
"smallvec",
"tempfile",
"tikv-jemalloc-sys",
"ui_test",
]

View file

@ -99,7 +99,7 @@ fn resolve_block<'tcx>(
for (i, statement) in blk.stmts.iter().enumerate() {
match statement.kind {
hir::StmtKind::Let(LetStmt { els: Some(els), .. }) => {
// Let-else has a special lexical structure for variables.
// let-else has a special lexical structure for variables.
// First we take a checkpoint of the current scope context here.
let mut prev_cx = visitor.cx;

View file

@ -334,7 +334,7 @@ mir_build_suggest_if_let = you might want to use `if let` to ignore the {$count
*[other] variants that aren't
} matched
mir_build_suggest_let_else = you might want to use `let else` to handle the {$count ->
mir_build_suggest_let_else = you might want to use `let...else` to handle the {$count ->
[one] variant that isn't
*[other] variants that aren't
} matched

View file

@ -732,8 +732,6 @@ parse_or_in_let_chain = `||` operators are not supported in let chain conditions
parse_or_pattern_not_allowed_in_fn_parameters = function parameters require top-level or-patterns in parentheses
parse_or_pattern_not_allowed_in_let_binding = `let` bindings require top-level or-patterns in parentheses
parse_out_of_range_hex_escape = out of range hex escape
.label = must be a character in the range [\x00-\x7f]
parse_outer_attr_explanation = outer attributes, like `#[test]`, annotate the item following them

View file

@ -2455,12 +2455,6 @@ pub(crate) enum UnescapeError {
is_hex: bool,
ch: String,
},
#[diag(parse_out_of_range_hex_escape)]
OutOfRangeHexEscape(
#[primary_span]
#[label]
Span,
),
#[diag(parse_leading_underscore_unicode_escape)]
LeadingUnderscoreUnicodeEscape {
#[primary_span]

View file

@ -226,7 +226,24 @@ pub(crate) fn emit_unescape_error(
err.emit()
}
EscapeError::OutOfRangeHexEscape => {
dcx.emit_err(UnescapeError::OutOfRangeHexEscape(err_span))
let mut err = dcx.struct_span_err(err_span, "out of range hex escape");
err.span_label(err_span, "must be a character in the range [\\x00-\\x7f]");
let escape_str = &lit[range];
if lit.len() <= 4
&& escape_str.len() == 4
&& escape_str.starts_with("\\x")
&& let Ok(value) = u8::from_str_radix(&escape_str[2..4], 16)
&& matches!(mode, Mode::Char | Mode::Str)
{
err.help(format!("if you want to write a byte literal, use `b'{}'`", escape_str));
err.help(format!(
"if you want to write a Unicode character, use `'\\u{{{:X}}}'`",
value
));
}
err.emit()
}
EscapeError::LeadingUnderscoreUnicodeEscape => {
let (c, span) = last_char();

View file

@ -867,7 +867,7 @@ impl<'a> Parser<'a> {
if let_else || !if_let {
err.span_suggestion_verbose(
block_span.shrink_to_lo(),
format!("{alternatively}you might have meant to use `let else`"),
format!("{alternatively}you might have meant to use `let...else`"),
"else ".to_string(),
if let_else {
Applicability::MachineApplicable

View file

@ -1097,7 +1097,7 @@ pub mod consts {
/// * `"nto"`
/// * `"redox"`
/// * `"solaris"`
/// * `"solid_asp3`
/// * `"solid_asp3"`
/// * `"vexos"`
/// * `"vita"`
/// * `"vxworks"`

View file

@ -1567,6 +1567,11 @@ tool_rustc_extended!(Miri {
tool_name: "miri",
stable: false,
add_bins_to_sysroot: ["miri"],
add_features: |builder, target, features| {
if builder.config.jemalloc(target) {
features.push("jemalloc".to_string());
}
},
// Always compile also tests when building miri. Otherwise feature unification can cause rebuilds between building and testing miri.
cargo_args: &["--all-targets"],
});

View file

@ -197,6 +197,37 @@ themselves marked as unstable. To use any of these options, pass `-Z unstable-op
the flag in question to Rustdoc on the command-line. To do this from Cargo, you can either use the
`RUSTDOCFLAGS` environment variable or the `cargo rustdoc` command.
### `--merge`, `--parts-out-dir`, and `--include-parts-dir`
These options control how rustdoc handles files that combine data from multiple crates.
By default, they act like `--merge=shared` is set, and `--parts-out-dir` and `--include-parts-dir`
are turned off. The `--merge=shared` mode causes rustdoc to load the existing data in the out-dir,
combine the new crate data into it, and write the result. This is very easy to use in scripts that
manually invoke rustdoc, but it's also slow, because it performs O(crates) work on
every crate, meaning it performs O(crates<sup>2</sup>) work.
```console
$ rustdoc crate1.rs --out-dir=doc
$ cat doc/search.index/crateNames/*
rd_("fcrate1")
$ rustdoc crate2.rs --out-dir=doc
$ cat doc/search.index/crateNames/*
rd_("fcrate1fcrate2")
```
To delay shared-data merging until the end of a build, so that you only have to perform O(crates)
work, use `--merge=none` on every crate except the last one, which will use `--merge=finalize`.
```console
$ rustdoc +nightly crate1.rs --merge=none --parts-out-dir=crate1.d -Zunstable-options
$ cat doc/search.index/crateNames/*
cat: 'doc/search.index/crateNames/*': No such file or directory
$ rustdoc +nightly crate2.rs --merge=finalize --include-parts-dir=crate1.d -Zunstable-options
$ cat doc/search.index/crateNames/*
rd_("fcrate1fcrate2")
```
### `--document-hidden-items`: Show items that are `#[doc(hidden)]`
<span id="document-hidden-items"></span>

View file

@ -978,15 +978,16 @@ fn parse_extern_html_roots(
Ok(externs)
}
/// Path directly to crate-info file.
/// Path directly to crate-info directory.
///
/// For example, `/home/user/project/target/doc.parts/<crate>/crate-info`.
/// For example, `/home/user/project/target/doc.parts`.
/// Each crate has its info stored in a file called `CRATENAME.json`.
#[derive(Clone, Debug)]
pub(crate) struct PathToParts(pub(crate) PathBuf);
impl PathToParts {
fn from_flag(path: String) -> Result<PathToParts, String> {
let mut path = PathBuf::from(path);
let path = PathBuf::from(path);
// check here is for diagnostics
if path.exists() && !path.is_dir() {
Err(format!(
@ -995,20 +996,22 @@ impl PathToParts {
))
} else {
// if it doesn't exist, we'll create it. worry about that in write_shared
path.push("crate-info");
Ok(PathToParts(path))
}
}
}
/// Reports error if --include-parts-dir / crate-info is not a file
/// Reports error if --include-parts-dir is not a directory
fn parse_include_parts_dir(m: &getopts::Matches) -> Result<Vec<PathToParts>, String> {
let mut ret = Vec::new();
for p in m.opt_strs("include-parts-dir") {
let p = PathToParts::from_flag(p)?;
// this is just for diagnostic
if !p.0.is_file() {
return Err(format!("--include-parts-dir expected {} to be a file", p.0.display()));
if !p.0.is_dir() {
return Err(format!(
"--include-parts-dir expected {} to be a directory",
p.0.display()
));
}
ret.push(p);
}

View file

@ -1252,9 +1252,9 @@ struct Indent(usize);
impl Display for Indent {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
(0..self.0).for_each(|_| {
f.write_char(' ').unwrap();
});
for _ in 0..self.0 {
f.write_char(' ')?;
}
Ok(())
}
}

View file

@ -87,7 +87,7 @@ impl HtmlWithLimit {
pub(super) fn close_tag(&mut self) {
if let Some(tag_name) = self.unclosed_tags.pop() {
// Close the most recently opened tag.
write!(self.buf, "</{tag_name}>").unwrap()
write!(self.buf, "</{tag_name}>").expect("infallible string operation");
}
// There are valid cases where `close_tag()` is called without
// there being any tags to close. For example, this occurs when
@ -99,7 +99,7 @@ impl HtmlWithLimit {
/// Write all queued tags and add them to the `unclosed_tags` list.
fn flush_queue(&mut self) {
for tag_name in self.queued_tags.drain(..) {
write!(self.buf, "<{tag_name}>").unwrap();
write!(self.buf, "<{tag_name}>").expect("infallible string operation");
self.unclosed_tags.push(tag_name);
}

View file

@ -916,7 +916,7 @@ fn render_impls(
impls: &[&Impl],
containing_item: &clean::Item,
toggle_open_by_default: bool,
) {
) -> fmt::Result {
let mut rendered_impls = impls
.iter()
.map(|i| {
@ -942,7 +942,7 @@ fn render_impls(
})
.collect::<Vec<_>>();
rendered_impls.sort();
w.write_str(&rendered_impls.join("")).unwrap();
w.write_str(&rendered_impls.join(""))
}
/// Build a (possibly empty) `href` attribute (a key-value pair) for the given associated item.
@ -1037,7 +1037,7 @@ fn assoc_const(
) -> impl fmt::Display {
let tcx = cx.tcx();
fmt::from_fn(move |w| {
render_attributes_in_code(w, it, &" ".repeat(indent), cx);
render_attributes_in_code(w, it, &" ".repeat(indent), cx)?;
write!(
w,
"{indent}{vis}const <a{href} class=\"constant\">{name}</a>{generics}: {ty}",
@ -1145,10 +1145,10 @@ fn assoc_method(
let (indent, indent_str, end_newline) = if parent == ItemType::Trait {
header_len += 4;
let indent_str = " ";
render_attributes_in_code(w, meth, indent_str, cx);
render_attributes_in_code(w, meth, indent_str, cx)?;
(4, indent_str, Ending::NoNewline)
} else {
render_attributes_in_code(w, meth, "", cx);
render_attributes_in_code(w, meth, "", cx)?;
(0, "", Ending::Newline)
};
write!(
@ -1365,10 +1365,10 @@ fn render_all_impls(
concrete: &[&Impl],
synthetic: &[&Impl],
blanket_impl: &[&Impl],
) {
) -> fmt::Result {
let impls = {
let mut buf = String::new();
render_impls(cx, &mut buf, concrete, containing_item, true);
render_impls(cx, &mut buf, concrete, containing_item, true)?;
buf
};
if !impls.is_empty() {
@ -1376,8 +1376,7 @@ fn render_all_impls(
w,
"{}<div id=\"trait-implementations-list\">{impls}</div>",
write_impl_section_heading("Trait Implementations", "trait-implementations")
)
.unwrap();
)?;
}
if !synthetic.is_empty() {
@ -1385,10 +1384,9 @@ fn render_all_impls(
w,
"{}<div id=\"synthetic-implementations-list\">",
write_impl_section_heading("Auto Trait Implementations", "synthetic-implementations",)
)
.unwrap();
render_impls(cx, &mut w, synthetic, containing_item, false);
w.write_str("</div>").unwrap();
)?;
render_impls(cx, &mut w, synthetic, containing_item, false)?;
w.write_str("</div>")?;
}
if !blanket_impl.is_empty() {
@ -1396,11 +1394,11 @@ fn render_all_impls(
w,
"{}<div id=\"blanket-implementations-list\">",
write_impl_section_heading("Blanket Implementations", "blanket-implementations")
)
.unwrap();
render_impls(cx, &mut w, blanket_impl, containing_item, false);
w.write_str("</div>").unwrap();
)?;
render_impls(cx, &mut w, blanket_impl, containing_item, false)?;
w.write_str("</div>")?;
}
Ok(())
}
fn render_assoc_items(
@ -1412,8 +1410,7 @@ fn render_assoc_items(
fmt::from_fn(move |f| {
let mut derefs = DefIdSet::default();
derefs.insert(it);
render_assoc_items_inner(f, cx, containing_item, it, what, &mut derefs);
Ok(())
render_assoc_items_inner(f, cx, containing_item, it, what, &mut derefs)
})
}
@ -1424,10 +1421,10 @@ fn render_assoc_items_inner(
it: DefId,
what: AssocItemRender<'_>,
derefs: &mut DefIdSet,
) {
) -> fmt::Result {
info!("Documenting associated items of {:?}", containing_item.name);
let cache = &cx.shared.cache;
let Some(v) = cache.impls.get(&it) else { return };
let Some(v) = cache.impls.get(&it) else { return Ok(()) };
let (mut non_trait, traits): (Vec<_>, _) =
v.iter().partition(|i| i.inner_impl().trait_.is_none());
if !non_trait.is_empty() {
@ -1511,8 +1508,7 @@ fn render_assoc_items_inner(
matches!(what, AssocItemRender::DerefFor { .. })
.then_some("</details>")
.maybe_display(),
)
.unwrap();
)?;
}
}
@ -1522,13 +1518,13 @@ fn render_assoc_items_inner(
if let Some(impl_) = deref_impl {
let has_deref_mut =
traits.iter().any(|t| t.trait_did() == cx.tcx().lang_items().deref_mut_trait());
render_deref_methods(&mut w, cx, impl_, containing_item, has_deref_mut, derefs);
render_deref_methods(&mut w, cx, impl_, containing_item, has_deref_mut, derefs)?;
}
// If we were already one level into rendering deref methods, we don't want to render
// anything after recursing into any further deref methods above.
if let AssocItemRender::DerefFor { .. } = what {
return;
return Ok(());
}
let (synthetic, concrete): (Vec<&Impl>, Vec<&Impl>) =
@ -1536,8 +1532,9 @@ fn render_assoc_items_inner(
let (blanket_impl, concrete): (Vec<&Impl>, _) =
concrete.into_iter().partition(|t| t.inner_impl().kind.is_blanket());
render_all_impls(w, cx, containing_item, &concrete, &synthetic, &blanket_impl);
render_all_impls(w, cx, containing_item, &concrete, &synthetic, &blanket_impl)?;
}
Ok(())
}
/// `derefs` is the set of all deref targets that have already been handled.
@ -1548,7 +1545,7 @@ fn render_deref_methods(
container_item: &clean::Item,
deref_mut: bool,
derefs: &mut DefIdSet,
) {
) -> fmt::Result {
let cache = cx.cache();
let deref_type = impl_.inner_impl().trait_.as_ref().unwrap();
let (target, real_target) = impl_
@ -1574,15 +1571,16 @@ fn render_deref_methods(
// `impl Deref<Target = S> for S`
if did == type_did || !derefs.insert(did) {
// Avoid infinite cycles
return;
return Ok(());
}
}
render_assoc_items_inner(&mut w, cx, container_item, did, what, derefs);
render_assoc_items_inner(&mut w, cx, container_item, did, what, derefs)?;
} else if let Some(prim) = target.primitive_type()
&& let Some(&did) = cache.primitive_locations.get(&prim)
{
render_assoc_items_inner(&mut w, cx, container_item, did, what, derefs);
render_assoc_items_inner(&mut w, cx, container_item, did, what, derefs)?;
}
Ok(())
}
fn should_render_item(item: &clean::Item, deref_mut_: bool, tcx: TyCtxt<'_>) -> bool {
@ -1805,8 +1803,7 @@ fn render_impl(
// because impls can't have a stability.
if !item.doc_value().is_empty() {
document_item_info(cx, it, Some(parent))
.render_into(&mut info_buffer)
.unwrap();
.render_into(&mut info_buffer)?;
doc_buffer = document_full(item, cx, HeadingOffset::H5).to_string();
short_documented = false;
} else {
@ -1823,9 +1820,7 @@ fn render_impl(
}
}
} else {
document_item_info(cx, item, Some(parent))
.render_into(&mut info_buffer)
.unwrap();
document_item_info(cx, item, Some(parent)).render_into(&mut info_buffer)?;
if rendering_params.show_def_docs {
doc_buffer = document_full(item, cx, HeadingOffset::H5).to_string();
short_documented = false;
@ -2920,7 +2915,7 @@ fn render_attributes_in_code(
item: &clean::Item,
prefix: &str,
cx: &Context<'_>,
) {
) -> fmt::Result {
for attr in &item.attrs.other_attrs {
let hir::Attribute::Parsed(kind) = attr else { continue };
let attr = match kind {
@ -2934,24 +2929,30 @@ fn render_attributes_in_code(
AttributeKind::NonExhaustive(..) => Cow::Borrowed("#[non_exhaustive]"),
_ => continue,
};
render_code_attribute(prefix, attr.as_ref(), w);
render_code_attribute(prefix, attr.as_ref(), w)?;
}
if let Some(def_id) = item.def_id()
&& let Some(repr) = repr_attribute(cx.tcx(), cx.cache(), def_id)
{
render_code_attribute(prefix, &repr, w);
render_code_attribute(prefix, &repr, w)?;
}
Ok(())
}
fn render_repr_attribute_in_code(w: &mut impl fmt::Write, cx: &Context<'_>, def_id: DefId) {
fn render_repr_attribute_in_code(
w: &mut impl fmt::Write,
cx: &Context<'_>,
def_id: DefId,
) -> fmt::Result {
if let Some(repr) = repr_attribute(cx.tcx(), cx.cache(), def_id) {
render_code_attribute("", &repr, w);
render_code_attribute("", &repr, w)?;
}
Ok(())
}
fn render_code_attribute(prefix: &str, attr: &str, w: &mut impl fmt::Write) {
write!(w, "<div class=\"code-attribute\">{prefix}{attr}</div>").unwrap();
fn render_code_attribute(prefix: &str, attr: &str, w: &mut impl fmt::Write) -> fmt::Result {
write!(w, "<div class=\"code-attribute\">{prefix}{attr}</div>")
}
/// Compute the *public* `#[repr]` of the item given by `DefId`.

View file

@ -457,7 +457,7 @@ fn item_module(cx: &Context<'_>, item: &clean::Item, items: &[clean::Item]) -> i
"<dt{id}>\
<code>"
)?;
render_attributes_in_code(w, myitem, "", cx);
render_attributes_in_code(w, myitem, "", cx)?;
write!(
w,
"{vis}{imp}</code>{stab_tags}\
@ -625,7 +625,7 @@ fn item_function(cx: &Context<'_>, it: &clean::Item, f: &clean::Function) -> imp
let notable_traits = notable_traits_button(&f.decl.output, cx).maybe_display();
wrap_item(w, |w| {
render_attributes_in_code(w, it, "", cx);
render_attributes_in_code(w, it, "", cx)?;
write!(
w,
"{vis}{constness}{asyncness}{safety}{abi}fn \
@ -666,7 +666,7 @@ fn item_trait(cx: &Context<'_>, it: &clean::Item, t: &clean::Trait) -> impl fmt:
// Output the trait definition
wrap_item(w, |mut w| {
render_attributes_in_code(&mut w, it, "", cx);
render_attributes_in_code(&mut w, it, "", cx)?;
write!(
w,
"{vis}{safety}{is_auto}trait {name}{generics}{bounds}",
@ -1240,7 +1240,7 @@ fn item_trait_alias(
) -> impl fmt::Display {
fmt::from_fn(|w| {
wrap_item(w, |w| {
render_attributes_in_code(w, it, "", cx);
render_attributes_in_code(w, it, "", cx)?;
write!(
w,
"trait {name}{generics} = {bounds}{where_clause};",
@ -1268,7 +1268,7 @@ fn item_trait_alias(
fn item_type_alias(cx: &Context<'_>, it: &clean::Item, t: &clean::TypeAlias) -> impl fmt::Display {
fmt::from_fn(|w| {
wrap_item(w, |w| {
render_attributes_in_code(w, it, "", cx);
render_attributes_in_code(w, it, "", cx)?;
write!(
w,
"{vis}type {name}{generics}{where_clause} = {type_};",
@ -1464,7 +1464,7 @@ impl<'a, 'cx: 'a> ItemUnion<'a, 'cx> {
fn print_field_attrs(&self, field: &'a clean::Item) -> impl Display {
fmt::from_fn(move |w| {
render_attributes_in_code(w, field, "", self.cx);
render_attributes_in_code(w, field, "", self.cx)?;
Ok(())
})
}
@ -1554,9 +1554,9 @@ impl<'clean> DisplayEnum<'clean> {
wrap_item(w, |w| {
if is_type_alias {
// For now the only attributes we render for type aliases are `repr` attributes.
render_repr_attribute_in_code(w, cx, self.def_id);
render_repr_attribute_in_code(w, cx, self.def_id)?;
} else {
render_attributes_in_code(w, it, "", cx);
render_attributes_in_code(w, it, "", cx)?;
}
write!(
w,
@ -1695,7 +1695,7 @@ fn render_enum_fields(
if v.is_stripped() {
continue;
}
render_attributes_in_code(w, v, TAB, cx);
render_attributes_in_code(w, v, TAB, cx)?;
w.write_str(TAB)?;
match v.kind {
clean::VariantItem(ref var) => match var.kind {
@ -1779,7 +1779,7 @@ fn item_variants(
)
.maybe_display()
)?;
render_attributes_in_code(w, variant, "", cx);
render_attributes_in_code(w, variant, "", cx)?;
if let clean::VariantItem(ref var) = variant.kind
&& let clean::VariantKind::CLike = var.kind
{
@ -1855,7 +1855,7 @@ fn item_variants(
<a href=\"#{id}\" class=\"anchor field\">§</a>\
<code>"
)?;
render_attributes_in_code(w, field, "", cx);
render_attributes_in_code(w, field, "", cx)?;
write!(
w,
"{f}: {t}</code>\
@ -1881,7 +1881,7 @@ fn item_macro(cx: &Context<'_>, it: &clean::Item, t: &clean::Macro) -> impl fmt:
fmt::from_fn(|w| {
wrap_item(w, |w| {
// FIXME: Also print `#[doc(hidden)]` for `macro_rules!` if it `is_doc_hidden`.
render_attributes_in_code(w, it, "", cx);
render_attributes_in_code(w, it, "", cx)?;
if !t.macro_rules {
write!(w, "{}", visibility_print_with_space(it, cx))?;
}
@ -1927,16 +1927,15 @@ fn item_primitive(cx: &Context<'_>, it: &clean::Item) -> impl fmt::Display {
let def_id = it.item_id.expect_def_id();
write!(w, "{}", document(cx, it, None, HeadingOffset::H2))?;
if it.name.map(|n| n.as_str() != "reference").unwrap_or(false) {
write!(w, "{}", render_assoc_items(cx, it, def_id, AssocItemRender::All))?;
write!(w, "{}", render_assoc_items(cx, it, def_id, AssocItemRender::All))
} else {
// We handle the "reference" primitive type on its own because we only want to list
// implementations on generic types.
let (concrete, synthetic, blanket_impl) =
get_filtered_impls_for_reference(&cx.shared, it);
render_all_impls(w, cx, it, &concrete, &synthetic, &blanket_impl);
render_all_impls(w, cx, it, &concrete, &synthetic, &blanket_impl)
}
Ok(())
})
}
@ -1950,7 +1949,7 @@ fn item_constant(
fmt::from_fn(|w| {
wrap_item(w, |w| {
let tcx = cx.tcx();
render_attributes_in_code(w, it, "", cx);
render_attributes_in_code(w, it, "", cx)?;
write!(
w,
@ -2016,9 +2015,9 @@ impl<'a> DisplayStruct<'a> {
wrap_item(w, |w| {
if is_type_alias {
// For now the only attributes we render for type aliases are `repr` attributes.
render_repr_attribute_in_code(w, cx, self.def_id);
render_repr_attribute_in_code(w, cx, self.def_id)?;
} else {
render_attributes_in_code(w, it, "", cx);
render_attributes_in_code(w, it, "", cx)?;
}
write!(
w,
@ -2097,7 +2096,7 @@ fn item_fields(
<code>",
item_type = ItemType::StructField,
)?;
render_attributes_in_code(w, field, "", cx);
render_attributes_in_code(w, field, "", cx)?;
write!(
w,
"{field_name}: {ty}</code>\
@ -2120,7 +2119,7 @@ fn item_static(
) -> impl fmt::Display {
fmt::from_fn(move |w| {
wrap_item(w, |w| {
render_attributes_in_code(w, it, "", cx);
render_attributes_in_code(w, it, "", cx)?;
write!(
w,
"{vis}{safe}static {mutability}{name}: {typ}",
@ -2140,8 +2139,8 @@ fn item_foreign_type(cx: &Context<'_>, it: &clean::Item) -> impl fmt::Display {
fmt::from_fn(|w| {
wrap_item(w, |w| {
w.write_str("extern {\n")?;
render_attributes_in_code(w, it, "", cx);
write!(w, " {}type {};\n}}", visibility_print_with_space(it, cx), it.name.unwrap(),)
render_attributes_in_code(w, it, "", cx)?;
write!(w, " {}type {};\n}}", visibility_print_with_space(it, cx), it.name.unwrap())
})?;
write!(
@ -2292,15 +2291,14 @@ fn print_bounds(
.maybe_display()
}
fn wrap_item<W, F, T>(w: &mut W, f: F) -> T
fn wrap_item<W, F>(w: &mut W, f: F) -> fmt::Result
where
W: fmt::Write,
F: FnOnce(&mut W) -> T,
F: FnOnce(&mut W) -> fmt::Result,
{
write!(w, r#"<pre class="rust item-decl"><code>"#).unwrap();
let res = f(w);
write!(w, "</code></pre>").unwrap();
res
w.write_str(r#"<pre class="rust item-decl"><code>"#)?;
f(w)?;
w.write_str("</code></pre>")
}
#[derive(PartialEq, Eq)]
@ -2370,9 +2368,9 @@ fn render_union(
fmt::from_fn(move |mut f| {
if is_type_alias {
// For now the only attributes we render for type aliases are `repr` attributes.
render_repr_attribute_in_code(f, cx, def_id);
render_repr_attribute_in_code(f, cx, def_id)?;
} else {
render_attributes_in_code(f, it, "", cx);
render_attributes_in_code(f, it, "", cx)?;
}
write!(f, "{}union {}", visibility_print_with_space(it, cx), it.name.unwrap(),)?;
@ -2403,7 +2401,7 @@ fn render_union(
for field in fields {
if let clean::StructFieldItem(ref ty) = field.kind {
render_attributes_in_code(&mut f, field, " ", cx);
render_attributes_in_code(&mut f, field, " ", cx)?;
writeln!(
f,
" {}{}: {},",
@ -2500,7 +2498,7 @@ fn render_struct_fields(
}
for field in fields {
if let clean::StructFieldItem(ref ty) = field.kind {
render_attributes_in_code(w, field, &format!("{tab} "), cx);
render_attributes_in_code(w, field, &format!("{tab} "), cx)?;
writeln!(
w,
"{tab} {vis}{name}: {ty},",

View file

@ -14,7 +14,7 @@
//! or contains "invocation-specific".
use std::cell::RefCell;
use std::ffi::OsString;
use std::ffi::{OsStr, OsString};
use std::fs::File;
use std::io::{self, Write as _};
use std::iter::once;
@ -84,9 +84,11 @@ pub(crate) fn write_shared(
};
if let Some(parts_out_dir) = &opt.parts_out_dir {
create_parents(&parts_out_dir.0)?;
let mut parts_out_file = parts_out_dir.0.clone();
parts_out_file.push(&format!("{crate_name}.json"));
create_parents(&parts_out_file)?;
try_err!(
fs::write(&parts_out_dir.0, serde_json::to_string(&info).unwrap()),
fs::write(&parts_out_file, serde_json::to_string(&info).unwrap()),
&parts_out_dir.0
);
}
@ -238,13 +240,25 @@ impl CrateInfo {
pub(crate) fn read_many(parts_paths: &[PathToParts]) -> Result<Vec<Self>, Error> {
parts_paths
.iter()
.map(|parts_path| {
let path = &parts_path.0;
let parts = try_err!(fs::read(path), &path);
let parts: CrateInfo = try_err!(serde_json::from_slice(&parts), &path);
Ok::<_, Error>(parts)
.fold(Ok(Vec::new()), |acc, parts_path| {
let mut acc = acc?;
let dir = &parts_path.0;
acc.append(&mut try_err!(std::fs::read_dir(dir), dir.as_path())
.filter_map(|file| {
let to_crate_info = |file: Result<std::fs::DirEntry, std::io::Error>| -> Result<Option<CrateInfo>, Error> {
let file = try_err!(file, dir.as_path());
if file.path().extension() != Some(OsStr::new("json")) {
return Ok(None);
}
let parts = try_err!(fs::read(file.path()), file.path());
let parts: CrateInfo = try_err!(serde_json::from_slice(&parts), file.path());
Ok(Some(parts))
};
to_crate_info(file).transpose()
})
.collect::<Result<Vec<CrateInfo>, Error>>()?);
Ok(acc)
})
.collect::<Result<Vec<CrateInfo>, Error>>()
}
}

View file

@ -29,13 +29,6 @@ directories = "6"
bitflags = "2.6"
serde_json = { version = "1.0", optional = true }
# Copied from `compiler/rustc/Cargo.toml`.
# But only for some targets, it fails for others. Rustc configures this in its CI, but we can't
# easily use that since we support of-tree builds.
[target.'cfg(any(target_os = "linux", target_os = "macos"))'.dependencies.tikv-jemalloc-sys]
version = "0.6.1"
features = ['override_allocator_on_supported_platforms']
[target.'cfg(unix)'.dependencies]
libc = "0.2"
# native-lib dependencies
@ -75,6 +68,7 @@ stack-cache = []
expensive-consistency-checks = ["stack-cache"]
tracing = ["serde_json"]
native-lib = ["dep:libffi", "dep:libloading", "dep:capstone", "dep:ipc-channel", "dep:nix", "dep:serde"]
jemalloc = []
[lints.rust.unexpected_cfgs]
level = "warn"

View file

@ -21,9 +21,13 @@ extern crate rustc_session;
extern crate rustc_span;
/// See docs in https://github.com/rust-lang/rust/blob/HEAD/compiler/rustc/src/main.rs
/// and https://github.com/rust-lang/rust/pull/146627 for why we need this `use` statement.
#[cfg(any(target_os = "linux", target_os = "macos"))]
use tikv_jemalloc_sys as _;
/// and https://github.com/rust-lang/rust/pull/146627 for why we need this.
///
/// FIXME(madsmtm): This is loaded from the sysroot that was built with the other `rustc` crates
/// above, instead of via Cargo as you'd normally do. This is currently needed for LTO due to
/// https://github.com/rust-lang/cc-rs/issues/1613.
#[cfg(feature = "jemalloc")]
extern crate tikv_jemalloc_sys as _;
mod log;

View file

@ -0,0 +1,4 @@
//@ hasraw crates.js 'dep1'
//@ hasraw search.index/name/*.js 'Dep1'
//@ has dep1/index.html
pub struct Dep1;

View file

@ -0,0 +1,4 @@
//@ hasraw crates.js 'dep1'
//@ hasraw search.index/name/*.js 'Dep1'
//@ has dep2/index.html
pub struct Dep2;

View file

@ -0,0 +1,4 @@
//@ !hasraw crates.js 'dep_missing'
//@ !hasraw search.index/name/*.js 'DepMissing'
//@ has dep_missing/index.html
pub struct DepMissing;

View file

@ -0,0 +1,46 @@
// Running --merge=finalize without an input crate root should not trigger ICE.
// Issue: https://github.com/rust-lang/rust/issues/146646
//@ needs-target-std
use run_make_support::{htmldocck, path, rustdoc};
fn main() {
let out_dir = path("out");
let merged_dir = path("merged");
let parts_out_dir = path("parts");
rustdoc()
.input("dep1.rs")
.out_dir(&out_dir)
.arg("-Zunstable-options")
.arg(format!("--parts-out-dir={}", parts_out_dir.display()))
.arg("--merge=none")
.run();
assert!(parts_out_dir.join("dep1.json").exists());
rustdoc()
.input("dep2.rs")
.out_dir(&out_dir)
.arg("-Zunstable-options")
.arg(format!("--parts-out-dir={}", parts_out_dir.display()))
.arg("--merge=none")
.run();
assert!(parts_out_dir.join("dep2.json").exists());
// dep_missing is different, because --parts-out-dir is not supplied
rustdoc().input("dep_missing.rs").out_dir(&out_dir).run();
assert!(parts_out_dir.join("dep2.json").exists());
let output = rustdoc()
.arg("-Zunstable-options")
.out_dir(&out_dir)
.arg(format!("--include-parts-dir={}", parts_out_dir.display()))
.arg("--merge=finalize")
.run();
output.assert_stderr_not_contains("error: the compiler unexpectedly panicked. this is a bug.");
htmldocck().arg(&out_dir).arg("dep1.rs").run();
htmldocck().arg(&out_dir).arg("dep2.rs").run();
htmldocck().arg(out_dir).arg("dep_missing.rs").run();
}

View file

@ -16,7 +16,7 @@ fn main() {
.arg(format!("--parts-out-dir={}", parts_out_dir.display()))
.arg("--merge=none")
.run();
assert!(parts_out_dir.join("crate-info").exists());
assert!(parts_out_dir.join("sierra.json").exists());
let output = rustdoc()
.arg("-Zunstable-options")

View file

@ -14,7 +14,7 @@ LL | enum Helper<T, U> {
LL | T(T, [!; 0]),
| - not covered
= note: the matched value is of type `Helper<T, U>`
help: you might want to use `let else` to handle the variant that isn't matched
help: you might want to use `let...else` to handle the variant that isn't matched
|
LL | let Helper::U(u) = Helper::T(t, []) else { todo!() };
| ++++++++++++++++

View file

@ -7,7 +7,7 @@ LL | let Some(y) = x;
= note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant
= note: for more information, visit https://doc.rust-lang.org/book/ch19-02-refutability.html
= note: the matched value is of type `Option<i32>`
help: you might want to use `let else` to handle the variant that isn't matched
help: you might want to use `let...else` to handle the variant that isn't matched
|
LL | let Some(y) = x else { todo!() };
| ++++++++++++++++

View file

@ -7,7 +7,7 @@ LL | let Ok(_x) = &foo();
= note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant
= note: for more information, visit https://doc.rust-lang.org/book/ch19-02-refutability.html
= note: the matched value is of type `&Result<u32, !>`
help: you might want to use `let else` to handle the variant that isn't matched
help: you might want to use `let...else` to handle the variant that isn't matched
|
LL | let Ok(_x) = &foo() else { todo!() };
| ++++++++++++++++

View file

@ -17,7 +17,7 @@ LL | let [a @ 3.., b @ ..3, c @ 4..6, ..] = xs;
= note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant
= note: for more information, visit https://doc.rust-lang.org/book/ch19-02-refutability.html
= note: the matched value is of type `[i32; 8]`
help: you might want to use `let else` to handle the variant that isn't matched
help: you might want to use `let...else` to handle the variant that isn't matched
|
LL | let [a @ 3.., b @ ..3, c @ 4..6, ..] = xs else { todo!() };
| ++++++++++++++++

View file

@ -17,7 +17,7 @@ LL | let [a @ 3.., b @ ..3, c @ 4..6, ..] = xs;
= note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant
= note: for more information, visit https://doc.rust-lang.org/book/ch19-02-refutability.html
= note: the matched value is of type `[i32; 8]`
help: you might want to use `let else` to handle the variant that isn't matched
help: you might want to use `let...else` to handle the variant that isn't matched
|
LL | let [a @ 3.., b @ ..3, c @ 4..6, ..] = xs else { todo!() };
| ++++++++++++++++

View file

@ -3,18 +3,27 @@ error: out of range hex escape
|
LL | let x = "\x80";
| ^^^^ must be a character in the range [\x00-\x7f]
|
= help: if you want to write a byte literal, use `b'\x80'`
= help: if you want to write a Unicode character, use `'\u{80}'`
error: out of range hex escape
--> $DIR/ascii-only-character-escape.rs:3:14
|
LL | let y = "\xff";
| ^^^^ must be a character in the range [\x00-\x7f]
|
= help: if you want to write a byte literal, use `b'\xff'`
= help: if you want to write a Unicode character, use `'\u{FF}'`
error: out of range hex escape
--> $DIR/ascii-only-character-escape.rs:4:14
|
LL | let z = "\xe2";
| ^^^^ must be a character in the range [\x00-\x7f]
|
= help: if you want to write a byte literal, use `b'\xe2'`
= help: if you want to write a Unicode character, use `'\u{E2}'`
error: aborting due to 3 previous errors

View file

@ -0,0 +1,21 @@
fn main() {
let _c = '\xFF'; //~ ERROR out of range hex escape
let _s = "\xFF"; //~ ERROR out of range hex escape
let _c2 = '\xff'; //~ ERROR out of range hex escape
let _s2 = "\xff"; //~ ERROR out of range hex escape
let _c3 = '\x80'; //~ ERROR out of range hex escape
let _s3 = "\x80"; //~ ERROR out of range hex escape
// Byte literals should not get suggestions (they're already valid)
let _b = b'\xFF'; // OK
let _bs = b"\xFF"; // OK
dbg!('\xFF'); //~ ERROR out of range hex escape
// do not suggest for out of range escapes that are too long
dbg!("\xFFFFF"); //~ ERROR out of range hex escape
dbg!("this is some kind of string \xa7"); //~ ERROR out of range hex escape
}

View file

@ -0,0 +1,77 @@
error: out of range hex escape
--> $DIR/out-of-range-hex-escape-suggestions-148917.rs:2:15
|
LL | let _c = '\xFF';
| ^^^^ must be a character in the range [\x00-\x7f]
|
= help: if you want to write a byte literal, use `b'\xFF'`
= help: if you want to write a Unicode character, use `'\u{FF}'`
error: out of range hex escape
--> $DIR/out-of-range-hex-escape-suggestions-148917.rs:3:15
|
LL | let _s = "\xFF";
| ^^^^ must be a character in the range [\x00-\x7f]
|
= help: if you want to write a byte literal, use `b'\xFF'`
= help: if you want to write a Unicode character, use `'\u{FF}'`
error: out of range hex escape
--> $DIR/out-of-range-hex-escape-suggestions-148917.rs:5:16
|
LL | let _c2 = '\xff';
| ^^^^ must be a character in the range [\x00-\x7f]
|
= help: if you want to write a byte literal, use `b'\xff'`
= help: if you want to write a Unicode character, use `'\u{FF}'`
error: out of range hex escape
--> $DIR/out-of-range-hex-escape-suggestions-148917.rs:6:16
|
LL | let _s2 = "\xff";
| ^^^^ must be a character in the range [\x00-\x7f]
|
= help: if you want to write a byte literal, use `b'\xff'`
= help: if you want to write a Unicode character, use `'\u{FF}'`
error: out of range hex escape
--> $DIR/out-of-range-hex-escape-suggestions-148917.rs:8:16
|
LL | let _c3 = '\x80';
| ^^^^ must be a character in the range [\x00-\x7f]
|
= help: if you want to write a byte literal, use `b'\x80'`
= help: if you want to write a Unicode character, use `'\u{80}'`
error: out of range hex escape
--> $DIR/out-of-range-hex-escape-suggestions-148917.rs:9:16
|
LL | let _s3 = "\x80";
| ^^^^ must be a character in the range [\x00-\x7f]
|
= help: if you want to write a byte literal, use `b'\x80'`
= help: if you want to write a Unicode character, use `'\u{80}'`
error: out of range hex escape
--> $DIR/out-of-range-hex-escape-suggestions-148917.rs:15:11
|
LL | dbg!('\xFF');
| ^^^^ must be a character in the range [\x00-\x7f]
|
= help: if you want to write a byte literal, use `b'\xFF'`
= help: if you want to write a Unicode character, use `'\u{FF}'`
error: out of range hex escape
--> $DIR/out-of-range-hex-escape-suggestions-148917.rs:18:11
|
LL | dbg!("\xFFFFF");
| ^^^^ must be a character in the range [\x00-\x7f]
error: out of range hex escape
--> $DIR/out-of-range-hex-escape-suggestions-148917.rs:20:39
|
LL | dbg!("this is some kind of string \xa7");
| ^^^^ must be a character in the range [\x00-\x7f]
error: aborting due to 9 previous errors

View file

@ -25,7 +25,7 @@ LL | let x @ 5 = 6;
= note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant
= note: for more information, visit https://doc.rust-lang.org/book/ch19-02-refutability.html
= note: the matched value is of type `i32`
help: you might want to use `let else` to handle the variants that aren't matched
help: you might want to use `let...else` to handle the variants that aren't matched
|
LL | let x @ 5 = 6 else { todo!() };
| ++++++++++++++++

View file

@ -152,7 +152,7 @@ LL | let Ok(_x) = res_u32_never.as_ref();
= note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant
= note: for more information, visit https://doc.rust-lang.org/book/ch19-02-refutability.html
= note: the matched value is of type `Result<&u32, &!>`
help: you might want to use `let else` to handle the variant that isn't matched
help: you might want to use `let...else` to handle the variant that isn't matched
|
LL | let Ok(_x) = res_u32_never.as_ref() else { todo!() };
| ++++++++++++++++

View file

@ -106,7 +106,7 @@ LL | let Ok(_x) = res_u32_never.as_ref();
= note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant
= note: for more information, visit https://doc.rust-lang.org/book/ch19-02-refutability.html
= note: the matched value is of type `Result<&u32, &!>`
help: you might want to use `let else` to handle the variant that isn't matched
help: you might want to use `let...else` to handle the variant that isn't matched
|
LL | let Ok(_x) = res_u32_never.as_ref() else { todo!() };
| ++++++++++++++++
@ -120,7 +120,7 @@ LL | let Ok(_x) = &res_u32_never;
= note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant
= note: for more information, visit https://doc.rust-lang.org/book/ch19-02-refutability.html
= note: the matched value is of type `&Result<u32, !>`
help: you might want to use `let else` to handle the variant that isn't matched
help: you might want to use `let...else` to handle the variant that isn't matched
|
LL | let Ok(_x) = &res_u32_never else { todo!() };
| ++++++++++++++++

View file

@ -97,7 +97,7 @@ LL | let Ok(_x) = res_u32_never.as_ref();
= note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant
= note: for more information, visit https://doc.rust-lang.org/book/ch19-02-refutability.html
= note: the matched value is of type `Result<&u32, &!>`
help: you might want to use `let else` to handle the variant that isn't matched
help: you might want to use `let...else` to handle the variant that isn't matched
|
LL | let Ok(_x) = res_u32_never.as_ref() else { todo!() };
| ++++++++++++++++
@ -111,7 +111,7 @@ LL | let Ok(_x) = &res_u32_never;
= note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant
= note: for more information, visit https://doc.rust-lang.org/book/ch19-02-refutability.html
= note: the matched value is of type `&Result<u32, !>`
help: you might want to use `let else` to handle the variant that isn't matched
help: you might want to use `let...else` to handle the variant that isn't matched
|
LL | let Ok(_x) = &res_u32_never else { todo!() };
| ++++++++++++++++

View file

@ -17,7 +17,7 @@ LL | Bar,
LL | Baz
| --- not covered
= note: the matched value is of type `Thing`
help: you might want to use `let else` to handle the variants that aren't matched
help: you might want to use `let...else` to handle the variants that aren't matched
|
LL | let Thing::Foo(y) = Thing::Foo(1) else { todo!() };
| ++++++++++++++++

View file

@ -183,7 +183,7 @@ LL | enum Opt {
LL | None,
| ---- not covered
= note: the matched value is of type `Opt`
help: you might want to use `let else` to handle the variant that isn't matched
help: you might want to use `let...else` to handle the variant that isn't matched
|
LL | let Opt::Some(ref _x) = e else { todo!() };
| ++++++++++++++++

View file

@ -7,7 +7,7 @@ LL | let Ok(x) = res;
= note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant
= note: for more information, visit https://doc.rust-lang.org/book/ch19-02-refutability.html
= note: the matched value is of type `Result<u32, &R<'_>>`
help: you might want to use `let else` to handle the variant that isn't matched
help: you might want to use `let...else` to handle the variant that isn't matched
|
LL | let Ok(x) = res else { todo!() };
| ++++++++++++++++

View file

@ -138,7 +138,7 @@ LL | let local_refutable @ NonExhaustiveEnum::Unit = NonExhaustiveEnum::Unit
= note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant
= note: for more information, visit https://doc.rust-lang.org/book/ch19-02-refutability.html
= note: the matched value is of type `NonExhaustiveEnum`
help: you might want to use `let else` to handle the variant that isn't matched
help: you might want to use `let...else` to handle the variant that isn't matched
|
LL | let local_refutable @ NonExhaustiveEnum::Unit = NonExhaustiveEnum::Unit else { todo!() };
| ++++++++++++++++

View file

@ -6,14 +6,14 @@ fn a() {
}
fn b() {
let Some(x) = foo() { //~ ERROR expected one of
//~^ HELP you might have meant to use `let else`
//~^ HELP you might have meant to use `let...else`
return;
}
}
fn c() {
let Some(x) = foo() { //~ ERROR expected one of
//~^ HELP you might have meant to use `if let`
//~| HELP alternatively, you might have meant to use `let else`
//~| HELP alternatively, you might have meant to use `let...else`
// The parser check happens pre-macro-expansion, so we don't know for sure.
println!("{x}");
}

View file

@ -15,7 +15,7 @@ error: expected one of `.`, `;`, `?`, `else`, or an operator, found `{`
LL | let Some(x) = foo() {
| ^ expected one of `.`, `;`, `?`, `else`, or an operator
|
help: you might have meant to use `let else`
help: you might have meant to use `let...else`
|
LL | let Some(x) = foo() else {
| ++++
@ -30,7 +30,7 @@ help: you might have meant to use `if let`
|
LL | if let Some(x) = foo() {
| ++
help: alternatively, you might have meant to use `let else`
help: alternatively, you might have meant to use `let...else`
|
LL | let Some(x) = foo() else {
| ++++

View file

@ -16,7 +16,7 @@ LL | A(foo::SecretlyEmpty),
| - not covered
= note: pattern `Foo::A(_)` is currently uninhabited, but this variant contains private fields which may become inhabited in the future
= note: the matched value is of type `Foo`
help: you might want to use `let else` to handle the variant that isn't matched
help: you might want to use `let...else` to handle the variant that isn't matched
|
LL | let Foo::D(_y, _z) = x else { todo!() };
| ++++++++++++++++

View file

@ -16,7 +16,7 @@ LL | A(foo::SecretlyEmpty),
| - not covered
= note: pattern `Foo::A(_)` is currently uninhabited, but this variant contains private fields which may become inhabited in the future
= note: the matched value is of type `Foo`
help: you might want to use `let else` to handle the variant that isn't matched
help: you might want to use `let...else` to handle the variant that isn't matched
|
LL | let Foo::D(_y, _z) = x else { todo!() };
| ++++++++++++++++

View file

@ -34,5 +34,5 @@ fn main() {
//~| NOTE for more information
//~| NOTE pattern `Foo::A(_)` is currently uninhabited
//~| NOTE the matched value is of type `Foo`
//~| HELP you might want to use `let else`
//~| HELP you might want to use `let...else`
}