Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
9e0664a
tests/assembly-llvm: pin frame pointer in issue-141649 aarch64 test
DeepeshWR Jul 15, 2026
352fa24
Windows: add context when opening NUL for child stdio fails
Joel-Wwalker Jul 17, 2026
b43da85
Detect when trait bound requires closure to return itself
estebank Jul 17, 2026
d61c706
Fix string indexing in diagnostic format strings
mejrs Jul 17, 2026
5de65af
Move `std::io::read_to_string` to `alloc::io`
bushrat011899 May 12, 2026
01c4242
Move compiletest CLI parsing to `cli.rs`
Kobzol Jul 17, 2026
630793c
Add explicit `Iterator::count` impl for `str::EncodeUtf16`
SimonSapin Jul 17, 2026
d9ac1a5
Print unknown stdio ids instead of assuming stderr in the NUL error
Joel-Wwalker Jul 18, 2026
7cc650f
Move rustdoc run-make tests into new `tests/run-make/rustdoc/`
fmease Jun 13, 2026
291a31c
Rollup merge of #159425 - Joel-Wwalker:157049-nul-error-context, r=Da…
JonathanBrouwer Jul 18, 2026
ae70887
Rollup merge of #159467 - SimonSapin:encode_utf16-count, r=joboet
JonathanBrouwer Jul 18, 2026
0bf6c18
Rollup merge of #157860 - fmease:mkdir-tests-run-make-rustdoc, r=notr…
JonathanBrouwer Jul 18, 2026
caeca00
Rollup merge of #158545 - bushrat011899:alloc_io_read_to_string, r=cl…
JonathanBrouwer Jul 18, 2026
823b135
Rollup merge of #159328 - DeepeshWR:fix/issue-141649-relax-aarch64-st…
JonathanBrouwer Jul 18, 2026
7cdce76
Rollup merge of #159459 - estebank:closure-ret-self, r=mu001999
JonathanBrouwer Jul 18, 2026
d711a09
Rollup merge of #159470 - mejrs:fix_diagnostic_ice, r=jieyouxu
JonathanBrouwer Jul 18, 2026
2fa71af
Rollup merge of #159500 - Kobzol:compiletest-move-cli, r=jieyouxu
JonathanBrouwer Jul 18, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion compiler/rustc_parse_format/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -759,7 +759,7 @@ impl<'input> Parser<'input> {
return spec;
};

spec.ty = self.string(self.input_vec_index);
spec.ty = self.string(self.input_vec_index2pos(self.input_vec_index));
spec.ty_span = {
let end = self.input_vec_index2range(self.input_vec_index).start;
Some(start..end)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1683,27 +1683,61 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
});
let mut diag = struct_span_code_err!(self.dcx(), span, E0271, "{msg}");
*diag.long_ty_path() = file;
let mut mention_bounds = true;
if let Some(span) = closure_span {
// Mark the closure decl so that it is seen even if we are pointing at the return
// type or expression.
//
// error[E0271]: expected `{closure@foo.rs:41:16}` to be a closure that returns
// `Unit3`, but it returns `Unit4`
// --> $DIR/foo.rs:43:17
// |
// LL | let v = Unit2.m(
// | - required by a bound introduced by this call
// ...
// LL | f: |x| {
// | --- /* this span */
// LL | drop(x);
// LL | Unit4
// | ^^^^^ expected `Unit3`, found `Unit4`
// |
diag.span_label(span, "this closure");
if !span.overlaps(obligation.cause.span) {
// Point at the binding corresponding to the closure where it is used.
diag.span_label(obligation.cause.span, "closure used here");
if let Some((_, _, expected_ty)) = values
&& let Some(expected_ty) = expected_ty.as_type()
&& let ty::Closure(def_id, _) = expected_ty.kind()
&& self.tcx.def_span(*def_id).overlaps(span)
&& let ObligationCauseCode::FunctionArg { parent_code, arg_hir_id, .. } =
obligation.cause.code()
&& let ObligationCauseCode::WhereClauseInExpr(def_id, span, _, _)
| ObligationCauseCode::WhereClause(def_id, span) = &**parent_code
{
// We have a trait bound for a closure to return itself, like
// `T: FnOnce() -> T`. This is nonsensical, but as far as the type system is
// concerned, valid. This is quite an edge case, but lets produce a reasonable
// diagnostic even in the face of an unreasonable user :)
let mut multispan: MultiSpan = (*span).into();
multispan.push_span_label(*span, "this requires the closure to return itself");
if let Node::Expr(arg) = self.tcx.hir_node(*arg_hir_id) {
multispan
.push_span_label(arg.span, "this closure would have to return itself");
}
let in_the_item = match self.tcx.opt_item_name(*def_id) {
Some(name) => format!("in `{name}`"),
None => String::new(),
};
diag.span_note(
multispan,
format!(
"a bound {in_the_item} requires that a closure return itself, which is \
not possible",
),
);
mention_bounds = false;
} else {
// Mark the closure decl so that it is seen even if we are pointing at the
// return type or expression.
//
// error[E0271]: expected `{closure@foo.rs:41:16}` to be a closure that returns
// `Unit3`, but it returns `Unit4`
// --> $DIR/foo.rs:43:17
// |
// LL | let v = Unit2.m(
// | - required by a bound introduced by this call
// ...
// LL | f: |x| {
// | --- /* this span */
// LL | drop(x);
// LL | Unit4
// | ^^^^^ expected `Unit3`, found `Unit4`
// |
diag.span_label(span, "this closure");
if !span.overlaps(obligation.cause.span) {
// Point at the binding corresponding to the closure where it is used.
diag.span_label(obligation.cause.span, "closure used here");
}
}
}

Expand Down Expand Up @@ -1775,7 +1809,9 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
false,
Some(span),
);
self.note_obligation_cause(&mut diag, obligation);
if mention_bounds {
self.note_obligation_cause(&mut diag, obligation);
}
diag.emit()
})
}
Expand Down
7 changes: 5 additions & 2 deletions library/alloc/src/io/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,6 @@ pub use core::io::{
stream_len_default, take,
};

#[unstable(feature = "alloc_io", issue = "154046")]
pub use self::{read::Read, util::Bytes};
#[doc(hidden)]
#[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")]
pub use self::{
Expand All @@ -38,3 +36,8 @@ pub use self::{
},
util::{SpecReadByte, bytes, uninlined_slow_read_byte},
};
#[unstable(feature = "alloc_io", issue = "154046")]
pub use self::{
read::{Read, read_to_string},
util::Bytes,
};
65 changes: 65 additions & 0 deletions library/alloc/src/io/read.rs
Original file line number Diff line number Diff line change
Expand Up @@ -700,6 +700,71 @@ pub trait Read {
}
}

/// Reads all bytes from a [reader][Read] into a new [`String`].
///
/// This is a convenience function for [`Read::read_to_string`]. Using this
/// function avoids having to create a variable first and provides more type
/// safety since you can only get the buffer out if there were no errors. (If you
/// use [`Read::read_to_string`] you have to remember to check whether the read
/// succeeded because otherwise your buffer will be empty or only partially full.)
///
/// # Performance
///
/// The downside of this function's increased ease of use and type safety is
/// that it gives you less control over performance. For example, you can't
/// pre-allocate memory like you can using [`String::with_capacity`] and
/// [`Read::read_to_string`]. Also, you can't re-use the buffer if an error
/// occurs while reading.
///
/// In many cases, this function's performance will be adequate and the ease of use
/// and type safety tradeoffs will be worth it. However, there are cases where you
/// need more control over performance, and in those cases you should definitely use
/// [`Read::read_to_string`] directly.
///
/// Note that in some special cases, such as when reading files, this function will
/// pre-allocate memory based on the size of the input it is reading. In those
/// cases, the performance should be as good as if you had used
/// [`Read::read_to_string`] with a manually pre-allocated buffer.
///
/// # Errors
///
/// This function forces you to handle errors because the output (the `String`)
/// is wrapped in a [`Result`]. See [`Read::read_to_string`] for the errors
/// that can occur. If any error occurs, you will get an [`Err`], so you
/// don't have to worry about your buffer being empty or partially full.
///
/// # Examples
///
/// ```no_run
/// # use std::io;
/// fn main() -> io::Result<()> {
/// let stdin = io::read_to_string(io::stdin())?;
/// println!("Stdin was:");
/// println!("{stdin}");
/// Ok(())
/// }
/// ```
///
/// # Usage Notes
///
/// `read_to_string` attempts to read a source until EOF, but many sources are continuous streams
/// that do not send EOF. In these cases, `read_to_string` will block indefinitely. Standard input
/// is one such stream which may be finite if piped, but is typically continuous. For example,
/// `cat file | my-rust-program` will correctly terminate with an `EOF` upon closure of cat.
/// Reading user input or running programs that remain open indefinitely will never terminate
/// the stream with `EOF` (e.g. `yes | my-rust-program`).
///
/// Using `.lines()` with a `BufReader` or using [`read`] can provide a better solution
///
/// [`read`]: Read::read
///
#[stable(feature = "io_read_to_string", since = "1.65.0")]
pub fn read_to_string<R: Read>(mut reader: R) -> Result<String> {
let mut buf = String::new();
reader.read_to_string(&mut buf)?;
Ok(buf)
}

/// Bare metal platforms usually have very small amounts of RAM
/// (in the order of hundreds of KB)
#[doc(hidden)]
Expand Down
15 changes: 15 additions & 0 deletions library/alloctests/tests/str.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1785,6 +1785,21 @@ fn test_utf16_size_hint() {
assert_eq!(hint_vec("\u{101234}a"), [(2, Some(5)), (2, Some(2)), (1, Some(1)), (0, Some(0))]);
}

#[test]
fn test_utf16_count() {
assert_eq!("".encode_utf16().count(), 0);
assert_eq!("a".encode_utf16().count(), 1);
assert_eq!("é".encode_utf16().count(), 1);
assert_eq!("字".encode_utf16().count(), 1);
assert_eq!("\u{1F4A9}".encode_utf16().count(), 2);
let mut iter = "\u{1F4A9}字éa".encode_utf16();
assert_eq!(iter.clone().count(), 5);
iter.next();
assert_eq!(iter.clone().count(), 4); // counting half of the surrogate pair
iter.next();
assert_eq!(iter.count(), 3);
}

#[test]
fn starts_with_in_unicode() {
assert!(!"├── Cargo.toml".starts_with("# "));
Expand Down
37 changes: 37 additions & 0 deletions library/core/src/str/iter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1533,6 +1533,43 @@ impl<'a> Iterator for EncodeUtf16<'a> {
(len.div_ceil(3) + 1, Some(len + 1))
}
}

#[inline]
fn count(self) -> usize {
let extra = if self.extra == 0 { 0 } else { 1 };
// Find UTF-8 non-continuation bytes and map:
//
// len_utf8() | len_utf16()
// -----------|------------
// 1 | 1
// 2 | 1
// 3 | 1
// 4 | 2
//
// Because we never map to larger values the result is never larger than
// `self.chars.as_str().len()` so the sum can never overflow
self.chars.as_str().as_bytes().iter().fold(extra, |acc, &byte| {
acc.wrapping_add({
if byte < 0b1000_0000 {
// 0b0xxx_xxxx: ASCII-compatible U+0000 to U+007F
1
} else if byte < 0b1100_0000 {
// 0b10xx_xxxx: UTF-8 continuation byte, skip
0
} else if byte < 0b1111_0000 {
// 0b110x_xxxx: start of a 2-byte UTF-8 sequence for U+0080 to U+07FF
// 0b1110_xxxx: start of a 3-byte UTF-8 sequence for U+0800 to U+FFFF
1
} else {
// 0b1111_0xxx: start of a 4-byte UTF-8 sequence for U+010000 to U+10FFFF
// This is exactly the range encoded as a surrogate pair in UTF-16
//
// 0b1111_1xxx: would fall here but never occurs in valid UTF-8
2
}
})
})
}
}

#[stable(feature = "fused", since = "1.26.0")]
Expand Down
67 changes: 2 additions & 65 deletions library/std/src/io/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -306,6 +306,8 @@ pub use alloc_crate::io::RawOsError;
pub use alloc_crate::io::SimpleMessage;
#[unstable(feature = "io_const_error", issue = "133448")]
pub use alloc_crate::io::const_error;
#[stable(feature = "io_read_to_string", since = "1.65.0")]
pub use alloc_crate::io::read_to_string;
#[unstable(feature = "read_buf", issue = "78485")]
pub use alloc_crate::io::{BorrowedBuf, BorrowedCursor};
#[stable(feature = "rust1", since = "1.0.0")]
Expand Down Expand Up @@ -359,71 +361,6 @@ mod util;

pub(crate) use stdio::cleanup;

/// Reads all bytes from a [reader][Read] into a new [`String`].
///
/// This is a convenience function for [`Read::read_to_string`]. Using this
/// function avoids having to create a variable first and provides more type
/// safety since you can only get the buffer out if there were no errors. (If you
/// use [`Read::read_to_string`] you have to remember to check whether the read
/// succeeded because otherwise your buffer will be empty or only partially full.)
///
/// # Performance
///
/// The downside of this function's increased ease of use and type safety is
/// that it gives you less control over performance. For example, you can't
/// pre-allocate memory like you can using [`String::with_capacity`] and
/// [`Read::read_to_string`]. Also, you can't re-use the buffer if an error
/// occurs while reading.
///
/// In many cases, this function's performance will be adequate and the ease of use
/// and type safety tradeoffs will be worth it. However, there are cases where you
/// need more control over performance, and in those cases you should definitely use
/// [`Read::read_to_string`] directly.
///
/// Note that in some special cases, such as when reading files, this function will
/// pre-allocate memory based on the size of the input it is reading. In those
/// cases, the performance should be as good as if you had used
/// [`Read::read_to_string`] with a manually pre-allocated buffer.
///
/// # Errors
///
/// This function forces you to handle errors because the output (the `String`)
/// is wrapped in a [`Result`]. See [`Read::read_to_string`] for the errors
/// that can occur. If any error occurs, you will get an [`Err`], so you
/// don't have to worry about your buffer being empty or partially full.
///
/// # Examples
///
/// ```no_run
/// # use std::io;
/// fn main() -> io::Result<()> {
/// let stdin = io::read_to_string(io::stdin())?;
/// println!("Stdin was:");
/// println!("{stdin}");
/// Ok(())
/// }
/// ```
///
/// # Usage Notes
///
/// `read_to_string` attempts to read a source until EOF, but many sources are continuous streams
/// that do not send EOF. In these cases, `read_to_string` will block indefinitely. Standard input
/// is one such stream which may be finite if piped, but is typically continuous. For example,
/// `cat file | my-rust-program` will correctly terminate with an `EOF` upon closure of cat.
/// Reading user input or running programs that remain open indefinitely will never terminate
/// the stream with `EOF` (e.g. `yes | my-rust-program`).
///
/// Using `.lines()` with a [`BufReader`] or using [`read`] can provide a better solution
///
///[`read`]: Read::read
///
#[stable(feature = "io_read_to_string", since = "1.65.0")]
pub fn read_to_string<R: Read>(mut reader: R) -> Result<String> {
let mut buf = String::new();
reader.read_to_string(&mut buf)?;
Ok(buf)
}

fn read_until<R: BufRead + ?Sized>(r: &mut R, delim: u8, buf: &mut Vec<u8>) -> Result<usize> {
let mut read = 0;
loop {
Expand Down
19 changes: 18 additions & 1 deletion library/std/src/sys/process/windows.rs
Original file line number Diff line number Diff line change
Expand Up @@ -640,7 +640,24 @@ impl Stdio {
opts.read(stdio_id == c::STD_INPUT_HANDLE);
opts.write(stdio_id != c::STD_INPUT_HANDLE);
opts.inherit_handle(true);
File::open(Path::new(r"\\.\NUL"), &opts).map(|file| file.into_inner())
File::open(Path::new(r"\\.\NUL"), &opts).map(|file| file.into_inner()).map_err(
|e| {
// A raw `NotFound` here is easily mistaken for the program
// being missing, so say what actually failed to open.
// `spawn` only passes the three standard ids, but print
// anything else as a number rather than mislabeling it.
let stream = match stdio_id {
c::STD_INPUT_HANDLE => "stdin".to_string(),
c::STD_OUTPUT_HANDLE => "stdout".to_string(),
c::STD_ERROR_HANDLE => "stderr".to_string(),
id => format!("stdio handle {id}"),
};
Error::new(
e.kind(),
format!("failed to open NUL device for child {stream}: {e}"),
)
},
)
}
}
}
Expand Down
5 changes: 3 additions & 2 deletions src/doc/rustc-dev-guide/src/tests/compiletest.md
Original file line number Diff line number Diff line change
Expand Up @@ -96,10 +96,11 @@ The following test suites are available, with links for more information:
| [`rustdoc-json`][rustdoc-json-tests] | Check JSON output of `rustdoc` |
| `rustdoc-ui` | Check terminal output of `rustdoc` ([see also](ui.md)) |

Some rustdoc-specific tests can also be found in `ui/rustdoc/`.
`ui/rustdoc/` also contains some rustdoc-specific tests.
These tests ensure that certain lints that are emitted as part of executing rustdoc
are also run when executing rustc.
Run-make tests pertaining to rustdoc are typically named `run-make/rustdoc-*/`.

`run-make/rustdoc/` contains run-make tests pertaining to rustdoc.

[rustdoc-html-tests]: ../rustdoc-internals/rustdoc-html-test-suite.md
[rustdoc-gui-tests]: ../rustdoc-internals/rustdoc-gui-test-suite.md
Expand Down
Loading
Loading