rust-lang/rust · #164418

Rollup of 7 pull requests

JonathanBrouwer · merged Oct 16, 202638 files · 802 + / 493 −
compiler/rustc_ast_lowering/src/delegation/resolution.rs15 + / 14 −
@@ -484,16 +484,22 @@ impl<'tcx> DelegationResolver<'_, 'tcx> {                 }             } -            mapping.map_return = SelfFinder::contains_self(sig.output());--            let arguments_to_map = sig-                .inputs()-                .iter()-                .enumerate()-                .skip(1) // Already checked above.-                .filter_map(|(idx, &param)| SelfFinder::contains_self(param).then_some(idx));+            // Heuristic: don't do wrapping if there is no target expression.+            if delegation.body.is_some() {+                let arguments_to_map = sig+                    .inputs()+                    .iter()+                    .enumerate()+                    .skip(1) // Already checked above.+                    .filter_map(|(idx, &param)| SelfFinder::contains_self(param).then_some(idx));++                mapping.arguments_to_map.extend(arguments_to_map);+            } -            mapping.arguments_to_map.extend(arguments_to_map);+            // Even if there is no target expression an output should be mapped,+            // this is the case for functions without receiver which return `Self`+            // for example.+            mapping.map_return = SelfFinder::contains_self(sig.output());         }          // We can't yet map more than one argument if there are definitions inside.@@ -513,11 +519,6 @@ impl<'tcx> DelegationResolver<'_, 'tcx> {         parent: LocalDefId,         span: Span,     ) -> bool {-        // Heuristic: don't do wrapping if there is no target expression.-        if delegation.body.is_none() {-            return false;-        }-         let tcx = self.tcx();          // Apply wrapping for delegations inside
compiler/rustc_hir_analysis/src/check/compare_impl_item.rs10 + / 1 −
@@ -2141,7 +2141,16 @@ fn compare_const_generic_param_types<'tcx>(         iter::zip(const_params_of(impl_item.def_id), const_params_of(trait_item.def_id));      for (param_impl, param_trait) in param_iter {-        let param_impl_ty_span = tcx.ty_span(param_impl.def_id.expect_local());+        // If `impl_item` comes from RPITIT:+        // <https://rustc-dev-guide.rust-lang.org/return-position-impl-trait-in-trait.html>+        // then expecting a `LocalDefId` for `param_impl` is fraught: the synthetic+        // associated type reuses the trait's generic parameters, which may be nonlocal.+        // Use the impl item's span instead.+        let param_impl_ty_span = if impl_item.is_impl_trait_in_trait() {+            tcx.def_span(impl_item.def_id)+        } else {+            tcx.ty_span(param_impl.def_id.expect_local())+        };          let cause = ObligationCause::new(             param_impl_ty_span,
library/std/src/io/stdio.rs12 + / 18 −
@@ -594,9 +594,8 @@ impl fmt::Debug for StdinLock<'_> { /// /// ### Note: Windows Portability Considerations ///-/// When operating in a console, the Windows implementation of this stream does not support-/// non-UTF-8 byte sequences. Attempting to write bytes that are not valid UTF-8 will return-/// an error.+/// When operating in a console, the Windows implementation of this stream decodes bytes+/// as UTF-8, replacing malformed sequences with the Unicode replacement character (U+FFFD). /// /// In a process with a detached console, such as one using /// `#![windows_subsystem = "windows"]`, or in a child process spawned from such a process,@@ -627,9 +626,8 @@ pub struct Stdout { /// /// ### Note: Windows Portability Considerations ///-/// When operating in a console, the Windows implementation of this stream does not support-/// non-UTF-8 byte sequences. Attempting to write bytes that are not valid UTF-8 will return-/// an error.+/// When operating in a console, the Windows implementation of this stream decodes bytes+/// as UTF-8, replacing malformed sequences with the Unicode replacement character (U+FFFD). /// /// In a process with a detached console, such as one using /// `#![windows_subsystem = "windows"]`, or in a child process spawned from such a process,@@ -659,9 +657,8 @@ static STDOUT: OnceLock<ReentrantLock<RefCell<LineWriter<StdoutRaw>>>> = OnceLoc /// /// ### Note: Windows Portability Considerations ///-/// When operating in a console, the Windows implementation of this stream does not support-/// non-UTF-8 byte sequences. Attempting to write bytes that are not valid UTF-8 will return-/// an error.+/// When operating in a console, the Windows implementation of this stream decodes bytes+/// as UTF-8, replacing malformed sequences with the Unicode replacement character (U+FFFD). /// /// In a process with a detached console, such as one using /// `#![windows_subsystem = "windows"]`, or in a child process spawned from such a process,@@ -882,9 +879,8 @@ impl fmt::Debug for StdoutLock<'_> { /// /// ### Note: Windows Portability Considerations ///-/// When operating in a console, the Windows implementation of this stream does not support-/// non-UTF-8 byte sequences. Attempting to write bytes that are not valid UTF-8 will return-/// an error.+/// When operating in a console, the Windows implementation of this stream decodes bytes+/// as UTF-8, replacing malformed sequences with the Unicode replacement character (U+FFFD). /// /// In a process with a detached console, such as one using /// `#![windows_subsystem = "windows"]`, or in a child process spawned from such a process,@@ -903,9 +899,8 @@ pub struct Stderr { /// /// ### Note: Windows Portability Considerations ///-/// When operating in a console, the Windows implementation of this stream does not support-/// non-UTF-8 byte sequences. Attempting to write bytes that are not valid UTF-8 will return-/// an error.+/// When operating in a console, the Windows implementation of this stream decodes bytes+/// as UTF-8, replacing malformed sequences with the Unicode replacement character (U+FFFD). /// /// In a process with a detached console, such as one using /// `#![windows_subsystem = "windows"]`, or in a child process spawned from such a process,@@ -924,9 +919,8 @@ pub struct StderrLock<'a> { /// /// ### Note: Windows Portability Considerations ///-/// When operating in a console, the Windows implementation of this stream does not support-/// non-UTF-8 byte sequences. Attempting to write bytes that are not valid UTF-8 will return-/// an error.+/// When operating in a console, the Windows implementation of this stream decodes bytes+/// as UTF-8, replacing malformed sequences with the Unicode replacement character (U+FFFD). /// /// In a process with a detached console, such as one using /// `#![windows_subsystem = "windows"]`, or in a child process spawned from such a process,
library/std/src/sys/io/is_terminal/windows.rs1 + / 1 −
@@ -1,5 +1,4 @@ use crate::ffi::c_void;-use crate::mem::MaybeUninit; use crate::os::windows::io::{AsHandle, AsRawHandle, BorrowedHandle}; use crate::sys::c; @@ -27,6 +26,7 @@ fn handle_is_console(handle: BorrowedHandle<'_>) -> bool {             }         }         _ => unsafe {+            use crate::mem::MaybeUninit;             // `GetConsoleMode` incurs a remote RPC round-trip (= bad), so it's best avoided.             // `NtQueryVolumeInformationFile` lets us test the handle directly.             // The required `FILE_DEVICE_CONSOLE` type was introduced in Windows 8.
library/std/src/sys/pal/windows/c.rs2 + / 6 −
@@ -20,7 +20,9 @@ pub type WCHAR = u16;  pub const INVALID_HANDLE_VALUE: HANDLE = ::core::ptr::without_provenance_mut(-1i32 as _); +#[cfg(not(target_vendor = "win7"))] pub const FILE_DEVICE_NAMED_PIPE: u32 = 0x00000011;+#[cfg(not(target_vendor = "win7"))] pub const FILE_DEVICE_CONSOLE: u32 = 0x00000050;  // https://learn.microsoft.com/en-us/cpp/c-runtime-library/exit-success-exit-failure?view=msvc-170@@ -147,12 +149,6 @@ pub const HANDLE_FLAG_INHERIT: u32 = cu(windows_sys::HANDLE_FLAG_INHERIT); pub const STACK_SIZE_PARAM_IS_A_RESERVATION: u32 =     cu(windows_sys::STACK_SIZE_PARAM_IS_A_RESERVATION); -// dwFlags -- WideCharToMultiByte-pub const WC_ERR_INVALID_CHARS: u32 = cu(windows_sys::WC_ERR_INVALID_CHARS);--// dwFlags -- MultiByteToWideChar-pub const MB_ERR_INVALID_CHARS: u32 = cu(windows_sys::MB_ERR_INVALID_CHARS);- // dwFlags -- CreateWaitableTimerExW pub const CREATE_WAITABLE_TIMER_HIGH_RESOLUTION: u32 =     cu(windows_sys::CREATE_WAITABLE_TIMER_HIGH_RESOLUTION);
library/std/src/sys/pal/windows/c/bindings.txt0 + / 2 −
@@ -2284,7 +2284,6 @@ MAX_PATH MAXIMUM_REPARSE_DATA_BUFFER_SIZE MaximumFileInfoByHandleClass MB_COMPOSITE-MB_ERR_INVALID_CHARS MB_PRECOMPOSED MB_USEGLYPHCHARS MOVEFILE_COPY_ALLOWED@@ -2500,7 +2499,6 @@ WakeAllConditionVariable WakeByAddressAll WakeByAddressSingle WakeConditionVariable-WC_ERR_INVALID_CHARS WideCharToMultiByte WIN32_FIND_DATAW WRITE_DAC
library/std/src/sys/pal/windows/c/windows_sys.rs0 + / 2 −
@@ -2985,7 +2985,6 @@ pub struct M128A { pub const MAXIMUM_REPARSE_DATA_BUFFER_SIZE: i32 = 16384; pub const MAX_PATH: i32 = 260; pub const MB_COMPOSITE: i32 = 2;-pub const MB_ERR_INVALID_CHARS: i32 = 8; pub const MB_PRECOMPOSED: i32 = 1; pub const MB_USEGLYPHCHARS: i32 = 4; pub const MOVEFILE_COPY_ALLOWED: i32 = 2;@@ -3351,7 +3350,6 @@ pub const WAIT_FAILED: u32 = 4294967295; pub const WAIT_IO_COMPLETION: i32 = 192; pub const WAIT_OBJECT_0: i32 = 0; pub const WAIT_TIMEOUT: i32 = 258;-pub const WC_ERR_INVALID_CHARS: i32 = 128; #[repr(C)] #[derive(Clone, Copy)] pub struct WIN32_FIND_DATAW {
library/std/src/sys/stdio/windows.rs167 + / 146 −
@@ -1,14 +1,15 @@ #![unstable(issue = "none", feature = "windows_stdio")] -use core::ffi::c_void; use core::str::utf8_char_width; +#[cfg(not(target_vendor = "win7"))]+use crate::alloc::System; use crate::mem::MaybeUninit; use crate::os::windows::io::{FromRawHandle, IntoRawHandle}; use crate::sys::handle::Handle; use crate::sys::pal::api::{self, WinError}; use crate::sys::{c, cvt};-use crate::{cmp, io, ptr, str};+use crate::{cmp, io, ptr};  #[cfg(test)] mod tests;@@ -52,20 +53,23 @@ impl IncompleteUtf8 {     } } -// Apparently Windows doesn't handle large reads on stdin or writes to stdout/stderr well (see-// #13304 for details).-//-// From MSDN (2011): "The storage for this buffer is allocated from a shared heap for the-// process that is 64 KB in size. The maximum size of the buffer will depend on heap usage."+// The way the console used to work up to Windows 8 is that the "LPC"s carried+// a small scratch space for reads/writes. If the client exceeded it, the server+// allocated a larger buffer from a fixed size section object. Its size was+// `CONSOLE_PORT_MEMORY_SIZE`, aka 64KiB. This meant any IO larger than that failed.+// If the Windows 7 source code ever leaks: The relevant function is `ConsoleAllocateCaptureBuffer`. //-// We choose the cap at 8 KiB because libuv does the same, and it seems to be acceptable so far.+// It remains a good size for a stack allocated buffer, however. const MAX_BUFFER_SIZE: usize = 8192;  // The standard buffer size of BufReader for Stdin should be able to hold 3x more bytes than there // are `u16`'s in MAX_BUFFER_SIZE. This ensures the read data can always be completely decoded from // UTF-16 to UTF-8. pub const STDIN_BUF_SIZE: usize = MAX_BUFFER_SIZE / 2 * 3; +#[cfg(not(target_vendor = "win7"))]+const MAX_WRITE_HEAP_LEN: usize = 64 * 1024; // In u16 -> 128KiB+ pub fn get_handle(handle_id: u32) -> io::Result<c::HANDLE> {     let handle = unsafe { c::GetStdHandle(handle_id) };     if handle == c::INVALID_HANDLE_VALUE {@@ -78,7 +82,6 @@ pub fn get_handle(handle_id: u32) -> io::Result<c::HANDLE> { }  /// Returns true if `handle` refers to a proper console (aka conhost).-#[cfg(not(target_vendor = "win7"))] fn is_console(handle: c::HANDLE) -> bool {     cfg_select! {         target_vendor = "win7" => {@@ -135,141 +138,125 @@ fn write_console_utf16(     incomplete_utf8: &mut IncompleteUtf8,     handle: c::HANDLE, ) -> io::Result<usize> {-    if incomplete_utf8.len > 0 {-        assert!(-            incomplete_utf8.len < 4,-            "Unexpected number of bytes for incomplete UTF-8 codepoint."-        );-        if data[0] >> 6 != 0b10 {-            // not a continuation byte - reject-            incomplete_utf8.len = 0;-            return Err(io::const_error!(-                io::ErrorKind::InvalidData,-                "Windows stdio in console mode does not support writing non-UTF-8 byte sequences",-            ));-        }-        incomplete_utf8.bytes[incomplete_utf8.len as usize] = data[0];-        incomplete_utf8.len += 1;-        let char_width = utf8_char_width(incomplete_utf8.bytes[0]);-        if (incomplete_utf8.len as usize) < char_width {-            // more bytes needed-            return Ok(1);-        }-        let s = str::from_utf8(&incomplete_utf8.bytes[0..incomplete_utf8.len as usize]);-        incomplete_utf8.len = 0;-        match s {-            Ok(s) => {-                assert_eq!(char_width, s.len());-                let written = write_valid_utf8_to_console(handle, s)?;-                assert_eq!(written, s.len()); // guaranteed by write_valid_utf8_to_console() for single codepoint writes-                return Ok(1);-            }-            Err(_) => {-                return Err(io::const_error!(-                    io::ErrorKind::InvalidData,-                    "Windows stdio in console mode does not support writing non-UTF-8 byte sequences",-                ));-            }-        }+    // As stated for `MAX_BUFFER_SIZE`, Windows 7 does not support large writes at all.+    // --> Fall back to the small-writes stack-alloc path.+    #[cfg(not(target_vendor = "win7"))]+    if data.len() > MAX_BUFFER_SIZE+        && let capacity = data.len().min(MAX_WRITE_HEAP_LEN)+        && let Ok(mut utf16) = Box::<[u16], _>::try_new_uninit_slice_in(capacity, System)+    {+        return write_console_with_buffer(data, incomplete_utf8, &mut utf16, handle);     } -    // As the console is meant for presenting text, we assume bytes of `data` are encoded as UTF-8,-    // which needs to be encoded as UTF-16.-    //-    // If the data is not valid UTF-8 we write out as many bytes as are valid.-    // If the first byte is invalid it is either first byte of a multi-byte sequence but the-    // provided byte slice is too short or it is the first byte of an invalid multi-byte sequence.-    let len = cmp::min(data.len(), MAX_BUFFER_SIZE / 2);-    let utf8 = match str::from_utf8(&data[..len]) {-        Ok(s) => s,-        Err(ref e) if e.valid_up_to() == 0 => {-            let first_byte_char_width = utf8_char_width(data[0]);-            if first_byte_char_width > 1 && data.len() < first_byte_char_width {-                incomplete_utf8.bytes[0] = data[0];-                incomplete_utf8.len = 1;-                return Ok(1);-            } else {-                return Err(io::const_error!(-                    io::ErrorKind::InvalidData,-                    "Windows stdio in console mode does not support writing non-UTF-8 byte sequences",-                ));-            }-        }-        Err(e) => str::from_utf8(&data[..e.valid_up_to()]).unwrap(),-    };+    write_console_stack(data, incomplete_utf8, handle)+} -    write_valid_utf8_to_console(handle, utf8)+#[cfg_attr(not(target_vendor = "win7"), inline(never))] // Avoid stack allocations unless called+fn write_console_stack(+    data: &[u8],+    incomplete_utf8: &mut IncompleteUtf8,+    handle: c::HANDLE,+) -> io::Result<usize> {+    let mut utf16 = [const { MaybeUninit::uninit() }; MAX_BUFFER_SIZE];+    write_console_with_buffer(data, incomplete_utf8, &mut utf16, handle) } -fn write_valid_utf8_to_console(handle: c::HANDLE, utf8: &str) -> io::Result<usize> {-    debug_assert!(!utf8.is_empty());--    let mut utf16 = [MaybeUninit::<u16>::uninit(); MAX_BUFFER_SIZE / 2];-    let utf8 = &utf8[..utf8.floor_char_boundary(utf16.len())];--    let utf16: &[u16] = unsafe {-        // Note that this theoretically checks validity twice in the (most common) case-        // where the underlying byte sequence is valid utf-8 (given the check in `write()`).-        let result = c::MultiByteToWideChar(-            c::CP_UTF8,                          // CodePage-            c::MB_ERR_INVALID_CHARS,             // dwFlags-            utf8.as_ptr().cast::<i8>(),          // lpMultiByteStr-            utf8.len() as i32,                   // cbMultiByte-            utf16.as_mut_ptr() as *mut c::WCHAR, // lpWideCharStr-            utf16.len() as i32,                  // cchWideChar-        );-        assert!(result != 0, "Unexpected error in MultiByteToWideChar");--        // Safety: MultiByteToWideChar initializes `result` values.-        utf16[..result as usize].assume_init_ref()-    };+fn write_console_with_buffer(+    data: &[u8],+    incomplete_utf8: &mut IncompleteUtf8,+    utf16: &mut [MaybeUninit<u16>],+    handle: c::HANDLE,+) -> io::Result<usize> {+    let mut written = 0;+    while written < data.len() {+        match write_console_chunk(&data[written..], incomplete_utf8, utf16, handle) {+            Ok(0) => break,+            Ok(consumed) => written += consumed,+            Err(error) if written == 0 => return Err(error),+            Err(_) => break,+        }+    }+    Ok(written)+}++fn write_console_chunk(+    data: &[u8],+    incomplete_utf8: &mut IncompleteUtf8,+    utf16: &mut [MaybeUninit<u16>],+    handle: c::HANDLE,+) -> io::Result<usize> {+    debug_assert!(utf16.len() >= 2, "not enough space for a full codepoint");+    debug_assert!(utf16.len() >= 1024, "unreasonably small buffer"); -    let mut written = write_u16s(handle, utf16)?;+    let mut chunk_len = data.len().min(utf16.len() - incomplete_utf8.len as usize);+    if chunk_len < data.len() {+        chunk_len = incomplete_utf8_suffix_start(&data[..chunk_len]);+    }+    let data = &data[..chunk_len];+    let mut utf16_len = 0;+    let mut remaining = data; -    // Figure out how many bytes of as UTF-8 were written away as UTF-16.-    if written == utf16.len() {-        Ok(utf8.len())-    } else {-        // Make sure we didn't end up writing only half of a surrogate pair (even though the chance-        // is tiny). Because it is not possible for user code to re-slice `data` in such a way that-        // a missing surrogate can be produced (and also because of the UTF-8 validation above),-        // write the missing surrogate out now.-        // Buffering it would mean we have to lie about the number of bytes written.-        let first_code_unit_remaining = utf16[written];-        if matches!(first_code_unit_remaining, 0xDC00..=0xDFFF) {-            // low surrogate-            // We just hope this works, and give up otherwise-            let _ = write_u16s(handle, &utf16[written..written + 1]);-            written += 1;+    // Join the incomplete sequence with the new data.+    if incomplete_utf8.len > 0 {+        let mut len = incomplete_utf8.len as usize;+        let width = utf8_char_width(incomplete_utf8.bytes[0]);++        // Accumulate until we have a complete sequence,+        // or something recognizable as a invalid sequence.+        while len < width+            && let Some((&b, rest)) = remaining.split_first()+            && b & 0xc0 == 0x80+        {+            incomplete_utf8.bytes[len] = b;+            len += 1;+            remaining = rest;         }-        // Calculate the number of bytes of `utf8` that were actually written.-        let mut count = 0;-        for ch in utf16[..written].iter() {-            count += match ch {-                0x0000..=0x007F => 1,-                0x0080..=0x07FF => 2,-                0xDC00..=0xDFFF => 1, // Low surrogate. We already counted 3 bytes for the other.-                _ => 3,-            };++        // If we consumed all of `data` without completing the sequence we're done.+        if len < width && remaining.is_empty() {+            incomplete_utf8.len = len as u8;+            return Ok(data.len());         }-        debug_assert!(String::from_utf16(&utf16[..written]).unwrap() == utf8[..count]);-        Ok(count)++        utf16_len = utf8_to_utf16(&incomplete_utf8.bytes[..len], utf16)?;+    }++    // Strip off any incomplete, trailing UTF8 sequence.+    let (remaining, trailing) = remaining.split_at(incomplete_utf8_suffix_start(remaining));++    // Convert the bulk of the data to UTF16.+    if !remaining.is_empty() {+        utf16_len += utf8_to_utf16(remaining, &mut utf16[utf16_len..])?;     }++    if utf16_len != 0 {+        // NOTE: WriteConsoleW does not result in short writes.+        let mut written = 0;+        cvt(unsafe {+            c::WriteConsoleW(+                handle,+                utf16.as_ptr().cast(),+                utf16_len as u32,+                &mut written,+                ptr::null_mut(),+            )+        })?;+    }++    incomplete_utf8.bytes[..trailing.len()].copy_from_slice(trailing);+    incomplete_utf8.len = trailing.len() as u8;+    Ok(data.len()) } -fn write_u16s(handle: c::HANDLE, data: &[u16]) -> io::Result<usize> {-    debug_assert!(data.len() < u32::MAX as usize);-    let mut written = 0;-    cvt(unsafe {-        c::WriteConsoleW(-            handle,-            data.as_ptr().cast::<c_void>(),-            data.len() as u32,-            &mut written,-            ptr::null_mut(),-        )-    })?;-    Ok(written as usize)+fn incomplete_utf8_suffix_start(bytes: &[u8]) -> usize {+    for length in 1..=3.min(bytes.len()) {+        let start = bytes.len() - length;+        let byte = bytes[start];+        if byte & 0xc0 != 0x80 {+            return if utf8_char_width(byte) > length { start } else { bytes.len() };+        }+    }+    bytes.len() }  impl Stdin {@@ -422,28 +409,62 @@ fn utf16_to_utf8(utf16: &[u16], utf8: &mut [u8]) -> io::Result<usize> {      let result = unsafe {         c::WideCharToMultiByte(-            c::CP_UTF8,              // CodePage-            c::WC_ERR_INVALID_CHARS, // dwFlags-            utf16.as_ptr(),          // lpWideCharStr-            utf16.len() as i32,      // cchWideChar-            utf8.as_mut_ptr(),       // lpMultiByteStr-            utf8.len() as i32,       // cbMultiByte-            ptr::null(),             // lpDefaultChar-            ptr::null_mut(),         // lpUsedDefaultChar+            c::CP_UTF8,         // CodePage+            0,                  // dwFlags+            utf16.as_ptr(),     // lpWideCharStr+            utf16.len() as i32, // cchWideChar+            utf8.as_mut_ptr(),  // lpMultiByteStr+            utf8.len() as i32,  // cbMultiByte+            ptr::null(),        // lpDefaultChar+            ptr::null_mut(),    // lpUsedDefaultChar         )     };     if result == 0 {-        // We can't really do any better than forget all data and return an error.-        Err(io::const_error!(-            io::ErrorKind::InvalidData,-            "Windows stdin in console mode does not support non-UTF-16 input; \-            encountered unpaired surrogate",-        ))+        return Err(io::Error::last_os_error());     } else {         Ok(result as usize)     } } +fn utf8_to_utf16(data: &[u8], utf16: &mut [MaybeUninit<u16>]) -> io::Result<usize> {+    debug_assert!(!data.is_empty());++    if utf16.is_empty() {+        return Ok(0);+    }++    // Fun fact: As is often the case, Windows' algorithm predates Unicode's recommendation.+    // The current behavior was implemented for Windows Vista (2007) and Unicode's recommendation+    // for "Best Practices for Using U+FFFD" came with Unicode 5.2 (2009) [^1]. So, now if a+    // continuation byte violates a boundary limit, it should restore it [^2], but this consumes it.+    // For instance, `ED A0 80` produces two U+FFFD with this API, while it should be three.+    // In practice this difference bears no meaning for console output.+    //+    // NOTE: However, it means we have to be careful about the treatment of `incomplete_utf8`,+    // because the behavior of `MultiByteToWideChar` could theoretically change in the future.+    //+    // `MultiByteToWideChar` is technically a fancy wrapper around `RtlUTF8ToUnicodeN`,+    // whose MSDN is a bit more enlightening about the behavior of the conversion.+    // Despite its name, it's not a syscall, but a simple function inside ntdll.dll.+    //+    // [^1]: https://www.unicode.org/review/pr-121.html+    // [^2]: https://encoding.spec.whatwg.org/#utf-8-decoder+    let len = unsafe {+        c::MultiByteToWideChar(+            c::CP_UTF8,+            0,+            data.as_ptr().cast(),+            data.len() as i32,+            utf16.as_mut_ptr().cast(),+            utf16.len() as i32,+        )+    };+    if len <= 0 {+        return Err(io::Error::last_os_error());+    }+    Ok(len as usize)+}+ impl IncompleteUtf8 {     pub const fn new() -> IncompleteUtf8 {         IncompleteUtf8 { bytes: [0; char::MAX_LEN_UTF8], len: 0 }
src/librustdoc/html/render/sidebar.rs30 + / 9 −
@@ -6,7 +6,6 @@ use askama::Template; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_hir::def::CtorKind; use rustc_hir::def_id::{DefIdMap, DefIdSet};-use rustc_middle::ty::TyCtxt; use tracing::debug;  use super::{Context, ItemSection, impl_trait_key, item_ty_to_section};@@ -463,13 +462,14 @@ fn sidebar_assoc_items<'a>(                     impl_,                     GetMethodsMode::AlsoCollectAssocFns { assoc_fns: &mut assoc_fns },                     used_links_bor,-                    cx.tcx(),+                    cx,                 ));             }             // We want links' order to be reproducible so we don't use unstable sort.             assoc_consts.sort();             assoc_types.sort();-            methods.sort();+            postprocess_method_list(&mut assoc_fns);+            postprocess_method_list(&mut methods);         }          let mut blocks = vec![@@ -580,7 +580,7 @@ fn sidebar_deref_methods<'a>(                         i.inner_impl(),                         GetMethodsMode::Deref { deref_mut, is_deref_target_copy },                         used_links,-                        tcx,+                        cx,                     )                     .collect::<Vec<_>>()                 })@@ -601,8 +601,8 @@ fn sidebar_deref_methods<'a>(                     print_path(impl_.inner_impl().trait_.as_ref().unwrap(), cx),                     print_type(real_target, cx),                 );-                // We want links' order to be reproducible so we don't use unstable sort.-                ret.sort();++                postprocess_method_list(&mut ret);                 out.push(LinkBlock::new(Link::new(id, title), "deref-methods", ret));             }         }@@ -776,7 +776,7 @@ fn get_methods<'a>(     i: &'a clean::Impl,     mut mode: GetMethodsMode<'_, 'a>,     used_links: &mut UsedLinks,-    tcx: TyCtxt<'_>,+    cx: &Context<'_>, ) -> impl Iterator<Item = Link<'a>> {     i.items.iter().filter_map(move |item| {         if let Some(ref name) = item.name@@ -785,12 +785,12 @@ fn get_methods<'a>(             let mut build_link = || {                 Link::new(                     get_next_url(used_links, format!("{typ}.{name}", typ = ItemType::Method)),-                    name.as_str(),+                    format!("{} ({:#})", name.as_str(), print_type(&i.for_, cx)),                 )             };             match &mut mode {                 &mut GetMethodsMode::Deref { deref_mut, is_deref_target_copy } => {-                    if super::should_render_item(item, deref_mut, tcx, is_deref_target_copy) {+                    if super::should_render_item(item, deref_mut, cx.tcx(), is_deref_target_copy) {                         Some(build_link())                     } else {                         None@@ -811,6 +811,27 @@ fn get_methods<'a>(     }) } +fn postprocess_method_list(methods: &mut [Link<'_>]) {+    // We want links' order to be reproducible so we don't use unstable sort.+    methods.sort();+    let trunc_to = |dst: &mut Cow<'_, str>, len: usize| match dst {+        Cow::Borrowed(s) => *s = &s[..len],+        Cow::Owned(s) => s.truncate(len),+    };+    fn get_short_name<'a>(itm: Option<&'a Link<'a>>) -> Option<&'a str> {+        Some(itm?.name.split_once(' ')?.0)+    }+    for i in 0..methods.len() {+        let short_name = get_short_name(methods.get(i));+        if get_short_name(methods.get(i.wrapping_sub(1))) != short_name+            && short_name != get_short_name(methods.get(i + 1))+        {+            let shortened_len = short_name.unwrap().len();+            trunc_to(&mut methods[i].name, shortened_len);+        }+    }+}+ fn get_associated_constants<'a>(     i: &'a clean::Impl,     used_links: &mut UsedLinks,
src/librustdoc/html/static/css/rustdoc.css44 + / 24 −
@@ -1478,6 +1478,13 @@ so that we can apply CSS-filters to change the arrow color in themes */ 	padding: 4px; 	transform: rotate(-45deg); 	top: -5px;+	/* So the arrow doesn't go above the content in the popover. */+	z-index: -1;+}+#settings.popover::before {+	/* Because of the tabs, it renders weird if we pick a different color than the tab's+	   background. */+	background-color: var(--search-tab-button-not-selected-background); }  .setting-line {@@ -1548,9 +1555,22 @@ so that we can apply CSS-filters to change the arrow color in themes */ 	border-color: var(--settings-input-color) !important; } +.settings-menu #settings {+	/* This is to prevent the popover width to change when we switch tabs. I picked the biggest+	   width based on the setting with the longest text. The `- 2px` is to includes the borders+	   width when the window's width is very thin. */+	width: min(456px, calc(100vw - 2px));+} #settings.popover { 	--popover-arrow-offset: 196px; 	top: calc(100% - 16px);+	right: 1px;+}++#settings .settings {+	height: 100%;+	display: flex;+	flex-direction: column; }  /* use larger max-width for help popover, but not for help.html */@@ -2032,13 +2052,15 @@ a.tooltip:hover::after {  #search-tabs { 	margin-top: 0.25rem;+	margin-bottom: 4px;+}++#search-tabs, #settings .tabs { 	display: flex; 	flex-direction: row; 	gap: 1px;-	margin-bottom: 4px; }--#search-tabs button {+#search-tabs button, #settings .tabs button.tab { 	text-align: center; 	font-size: 1.125rem; 	border: 0;@@ -2047,15 +2069,23 @@ a.tooltip:hover::after { 	line-height: 1.5; 	color: inherit; }-#search-tabs button:not(.selected) {+#search-tabs button:not(.selected), #settings .tabs button.tab:not(.selected) { 	background-color: var(--search-tab-button-not-selected-background); 	border-top-color: var(--search-tab-button-not-selected-border-top-color); }-#search-tabs button:hover, #search-tabs button.selected {+#search-tabs button:hover, #search-tabs button.selected,+#settings .tabs button.tab:hover, #settings .tabs button.tab.selected { 	background-color: var(--search-tab-button-selected-background); 	border-top-color: var(--search-tab-button-selected-border-top-color); } +#settings .settings .setting {+	display: none;+}+#settings .settings .setting.selected {+	display: block;+}+ #search-tabs .count { 	font-size: 1rem; 	font-variant-numeric: tabular-nums;@@ -2067,8 +2097,7 @@ a.tooltip:hover::after { 	color: transparent; } -.search-form.loading::after-{+.search-form.loading::after { 	width: 18px; 	height: 18px; 	border-radius: 18px;@@ -2777,8 +2806,11 @@ in src-script.js and main.js 	} 	/* The `overflow-y` property is used on an internal content instead of the top one otherwise the 	   little arrow at the top can't be displayed. */-	#settings.popover .settings, #help.popover .content {+	#settings.popover .settings .setting, #help.popover .content { 		overflow-y: scroll;+	}++	#help.popover .content { 		height: 100%; 	} @@ -3491,10 +3523,10 @@ Original by Dempfi (https://github.com/dempfi/ayu) 	--search-results-alias-color: #c5c5c5; 	--search-results-grey-color: #999; 	--search-tab-title-count-color: #888;-	--search-tab-button-not-selected-border-top-color: none;-	--search-tab-button-not-selected-background: transparent !important;-	--search-tab-button-selected-border-top-color: none;-	--search-tab-button-selected-background: #141920 !important;+	--search-tab-button-not-selected-border-top-color: #1d1f20;+	--search-tab-button-not-selected-background: #1d1f20;+	--search-tab-button-selected-border-top-color: #ffb44c;+	--search-tab-button-selected-background: #141920; 	--settings-menu-filter: invert(70%); 	--settings-menu-hover-filter: invert(100%); 	--stab-background-color: #314559;@@ -3621,18 +3653,6 @@ Original by Dempfi (https://github.com/dempfi/ayu) 	color: #788797; } -:root[data-theme="ayu"] #search-tabs > button.selected {-	border-bottom: 1px solid #ffb44c !important;-	border-top: none;-}-:root[data-theme="ayu"] #search-tabs > button:not(.selected) {-	border: none;-	background-color: transparent !important;-}-:root[data-theme="ayu"] #search-tabs > button:hover {-	border-bottom: 1px solid rgba(242, 151, 24, 0.3);-}- :root[data-theme="ayu"] .settings-menu > a img, :root[data-theme="ayu"] #sidebar-button > a::before { 	filter: invert(100);
src/librustdoc/html/static/js/rustdoc.d.ts9 + / 1 −
@@ -562,7 +562,7 @@ declare namespace rustdoc {     type Dir = [string, rustdoc.Dir[], string[]]      /**-     * Indivitual setting object, used in `settings.js`+     * Individual setting object, used in `settings.js`.      */     interface Setting {         js_name: string,@@ -571,6 +571,14 @@ declare namespace rustdoc {         default: string | boolean,     } +    /**+     * Individual settings tab, used in `settings.js`.+     */+    interface SettingsTab {+        name: string,+        settings: Array<rustdoc.Setting>,+    }+     /**      * Single element in the data-locs field of a scraped example.      * First field is the start and end char index,
src/librustdoc/html/static/js/settings.js145 + / 95 −
@@ -115,52 +115,65 @@     }      /**-     * This function builds the sections inside the "settings page". It takes a `settings` list+     * This function builds the sections inside the "settings page". It takes a `tabs` list      * as argument which describes each setting and how to render it. It returns a string      * representing the raw HTML.      *-     * @param {Array<rustdoc.Setting>} settings+     * @param {Array<rustdoc.SettingsTab>} tabs      *      * @return {string}      */-    function buildSettingsPageSections(settings) {+    function buildSettingsPageSections(tabs) {         let output = ""; -        for (const setting of settings) {-            const js_data_name = setting["js_name"];-            const setting_name = setting["name"];+        output += "<div class=\"tabs\">";+        for (const [index, tab] of tabs.entries()) {+            output += `\+                <button class="tab${index === 0 ? " selected" : ""}" data-name="${tab.name}">\+                    ${tab.name}\+                </button>`;+        }+        output += "</div>";+        for (const [index, tab] of tabs.entries()) {+            output += `\+<div class="setting${index === 0 ? " selected" : ""}" data-name="${tab.name}">`;+            for (const setting of tab.settings) {+                const js_data_name = setting["js_name"];+                const setting_name = setting["name"]; -            if (setting["options"] !== undefined) {-                // This is a select setting.-                output += `\+                if (setting["options"] !== undefined) {+                    // This is a select setting.+                    output += `\ <div class="setting-line" id="${js_data_name}">     <div class="setting-radio-name">${setting_name}</div>     <div class="setting-radio-choices">`;-                onEach(setting["options"], option => {-                    const checked = option === setting["default"] ? " checked" : "";-                    const full = `${js_data_name}-${option.replace(/ /g,"-")}`;+                    onEach(setting["options"], option => {+                        const checked = option === setting["default"] ? " checked" : "";+                        const full = `${js_data_name}-${option.replace(/ /g,"-")}`; -                    output += `\+                        output += `\         <label for="${full}" class="setting-radio">             <input type="radio" name="${js_data_name}"                 id="${full}" value="${option}"${checked}>             <span>${option}</span>         </label>`;-                });-                output += `\+                    });+                    output += `\     </div> </div>`;-            } else {-                // This is a checkbox toggle.-                const checked = setting["default"] === true ? " checked" : "";-                output += `\+                } else {+                    // This is a checkbox toggle.+                    const checked = setting["default"] === true ? " checked" : "";+                    output += `\ <div class="setting-line">\     <label class="setting-check">\         <input type="checkbox" id="${js_data_name}"${checked}>\         <span>${setting_name}</span>\     </label>\ </div>`;+                }             }+            output += "</div>";         }         return output;     }@@ -176,96 +189,133 @@               .split(",").filter(t => t);         theme_names.push("light", "dark", "ayu"); -        const settings = [-            {-                "name": "Theme",-                "js_name": "theme",-                "default": "system preference",-                "options": theme_names.concat("system preference"),-            },-            {-                "name": "Preferred light theme",-                "js_name": "preferred-light-theme",-                "default": "light",-                "options": theme_names,-            },-            {-                "name": "Preferred dark theme",-                "js_name": "preferred-dark-theme",-                "default": "dark",-                "options": theme_names,-            },-            {-                "name": "Auto-hide item contents for large items",-                "js_name": "auto-hide-large-items",-                "default": true,-            },-            {-                "name": "Auto-hide item methods' documentation",-                "js_name": "auto-hide-method-docs",-                "default": false,-            },-            {-                "name": "Auto-hide trait implementation documentation",-                "js_name": "auto-hide-trait-implementations",-                "default": false,-            },+        const tabs = [             {-                "name": "Directly go to item in search if there is only one result",-                "js_name": "go-to-only-result",-                "default": false,+                name: "Appearance",+                settings: [+                    {+                        "name": "Theme",+                        "js_name": "theme",+                        "default": "system preference",+                        "options": theme_names.concat("system preference"),+                    },+                    {+                        "name": "Preferred light theme",+                        "js_name": "preferred-light-theme",+                        "default": "light",+                        "options": theme_names,+                    },+                    {+                        "name": "Preferred dark theme",+                        "js_name": "preferred-dark-theme",+                        "default": "dark",+                        "options": theme_names,+                    },+                    {+                        "name": "Use sans serif fonts",+                        "js_name": "sans-serif-fonts",+                        "default": false,+                    },+                ],             },             {-                "name": "Show line numbers on code examples",-                "js_name": "line-numbers",-                "default": false,+                name: "Interface",+                settings: [+                    {+                        "name": "Hide persistent navigation bar",+                        "js_name": "hide-sidebar",+                        "default": false,+                    },+                    {+                        "name": "Hide table of contents",+                        "js_name": "hide-toc",+                        "default": false,+                    },+                    {+                        "name": "Hide module navigation",+                        "js_name": "hide-modnav",+                        "default": false,+                    },+                    {+                        "name": "Disable keyboard shortcuts",+                        "js_name": "disable-shortcuts",+                        "default": false,+                    },+                    {+                        "name": "Directly go to item in search if there is only one result",+                        "js_name": "go-to-only-result",+                        "default": false,+                    },+                ],             },             {-                "name": "Hide persistent navigation bar",-                "js_name": "hide-sidebar",-                "default": false,-            },-            {-                "name": "Hide table of contents",-                "js_name": "hide-toc",-                "default": false,-            },-            {-                "name": "Hide module navigation",-                "js_name": "hide-modnav",-                "default": false,-            },-            {-                "name": "Disable keyboard shortcuts",-                "js_name": "disable-shortcuts",-                "default": false,-            },-            {-                "name": "Use sans serif fonts",-                "js_name": "sans-serif-fonts",-                "default": false,-            },-            {-                "name": "Word wrap source code",-                "js_name": "word-wrap-source-code",-                "default": false,-            },-            {-                "name": "Hide deprecated items",-                "js_name": "hide-deprecated-items",-                "default": false,+                name: "Content",+                settings: [+                    {+                        "name": "Show line numbers on code examples",+                        "js_name": "line-numbers",+                        "default": false,+                    },+                    {+                        "name": "Word wrap source code",+                        "js_name": "word-wrap-source-code",+                        "default": false,+                    },+                    {+                        "name": "Auto-hide item contents for large items",+                        "js_name": "auto-hide-large-items",+                        "default": true,+                    },+                    {+                        "name": "Auto-hide item methods' documentation",+                        "js_name": "auto-hide-method-docs",+                        "default": false,+                    },+                    {+                        "name": "Auto-hide trait implementation documentation",+                        "js_name": "auto-hide-trait-implementations",+                        "default": false,+                    },+                    {+                        "name": "Hide deprecated items",+                        "js_name": "hide-deprecated-items",+                        "default": false,+                    },+                ],             },         ];          // Then we build the DOM.         const elementKind = isSettingsPage ? "section" : "div";-        const innerHTML = `<div class="settings">${buildSettingsPageSections(settings)}</div>`;+        const innerHTML = `<div class="settings">${buildSettingsPageSections(tabs)}</div>`;         const el = document.createElement(elementKind);         el.id = "settings";         if (!isSettingsPage) {             el.className = "popover";         }         el.innerHTML = innerHTML;+        const selectedClass = "selected";+        onEachLazy(el.getElementsByClassName("tab"), tab => {+            tab.onclick = () => {+                if (tab.classList.contains(selectedClass)) {+                    return;+                }+                const selectedTab = el.querySelector(`.tab.${selectedClass}`);+                if (selectedTab) {+                    selectedTab.classList.remove(selectedClass);+                }+                const name = tab.getAttribute("data-name");+                const selected = el.querySelector(`#settings .setting.${selectedClass}`);+                if (selected) {+                    selected.classList.remove(selectedClass);+                }+                const newSelected = el.querySelector(`#settings .setting[data-name="${name}"]`);+                if (newSelected) {+                    newSelected.classList.add(selectedClass);+                }+                tab.classList.add(selectedClass);+            };+        });          if (isSettingsPage) {             const mainElem = document.getElementById(MAIN_ID);@@ -274,7 +324,7 @@             }         } else {             el.setAttribute("tabindex", "-1");-            onEachLazy(document.querySelectorAll(".settings-menu"), menu => {+            onEachLazy(document.getElementsByClassName("settings-menu"), menu => {                 if (menu.offsetWidth !== 0) {                     menu.appendChild(el);                     return true;@@ -288,7 +338,7 @@      function displaySettings() {         settingsMenu.style.display = "";-        onEachLazy(document.querySelectorAll(".settings-menu"), menu => {+        onEachLazy(document.getElementsByClassName("settings-menu"), menu => {             if (menu.offsetWidth !== 0) {                 if (!menu.contains(settingsMenu) && settingsMenu.parentElement) {                     settingsMenu.parentElement.removeChild(settingsMenu);
tests/pretty/delegation/self-mapping-output.pp2 + / 2 −
@@ -26,7 +26,7 @@     #[attr = Inline(Hint)]     fn method(self: _) -> _ { from(Self { _: Trait::method(self.0) }) }     #[attr = Inline(Hint)]-    fn r#static() -> _ { Trait::r#static() }+    fn r#static() -> _ { from(Self { _: Trait::r#static() }) }     //~^ WARN: function cannot return without recursing [unconditional_recursion]     #[attr = Inline(Hint)]     fn raw_S(self: _) -> _ { Trait::raw_S(self.0) }@@ -36,7 +36,7 @@     #[attr = Inline(Hint)]     fn method(self: _) -> _ { from(Self { _: Trait::method(self.0) }) }     #[attr = Inline(Hint)]-    fn r#static() -> _ { Trait::r#static() }+    fn r#static() -> _ { from(Self { _: Trait::r#static() }) }     #[attr = Inline(Hint)]     fn raw_S(self: _) -> _ { Trait::raw_S(self.0) } }
tests/run-make/remap-path-prefix-doctest-panic/rmake.rsadded55 + / 0 −
@@ -0,0 +1,55 @@+// Checks that the location printed by a panicking doctest is remapped+// according to `--remap-path-scope`.+//+// Only the lines between the start of the stack backtrace and the end are+// are inspected, so timings, temp dirs and backtraces can't leak into the check.++//@ ignore-cross-compile+//@ ignore-windows++use run_make_support::assertion_helpers::{assert_contains, assert_not_contains};+use run_make_support::rustdoc;++/// Runs the failing doctest and returns stdout.+fn panic_file(scope: Option<&str>) -> String {+    let mut cmd = rustdoc();+    cmd.input("src/doctest.rs")+        .arg("--test")+        .arg("--remap-path-prefix=src=/REMAPPED")+        .arg("-Cdebuginfo=1")+        .arg("--edition=2015")+        .env("RUST_BACKTRACE", "1");++    if let Some(scope) = scope {+        cmd.arg(format!("--remap-path-scope={scope}")).arg("-Zunstable-options");+    }++    let out = cmd.run_fail();+    out.assert_stdout_contains("explicit panic");++    let stdout = out.stdout_utf8();+    let section: Vec<&str> = stdout+        .lines()+        .skip_while(|l| l.trim() != "stack backtrace:")+        .skip(1)+        .take_while(|l| l.trim() != "failures:")+        .collect();++    assert!(!section.is_empty(), "no backtrace found in output:\n{stdout}");+    section.join("\n")+}++fn main() {+    // Check that with debuginfo (and related) we get the remapped path+    for scope in [Some("debuginfo"), Some("object"), Some("all"), None] {+        let stdout = panic_file(scope);+        assert_contains(&stdout, "/REMAPPED/doctest.rs");+        assert_not_contains(&stdout, "src/doctest.rs");+    }++    for scope in ["diagnostics", "documentation", "macro"] {+        let stdout = panic_file(Some(scope));+        assert_contains(&stdout, "src/doctest.rs");+        assert_not_contains(&stdout, "/REMAPPED/doctest.rs");+    }+}
tests/run-make/remap-path-prefix-doctest-panic/src/doctest.rsadded4 + / 0 −
@@ -0,0 +1,4 @@+/// ```+/// panic!()+/// ```+pub fn f() {}
tests/rustdoc-gui/copy-code.goml1 + / 1 −
@@ -51,7 +51,7 @@ assert: |copy_height| > 0 && |copy_width| > 0 assert-clipboard: ""  // We make the line numbers appear.-call-function: ("open-settings-menu", {})+call-function: ("change-settings-tab", {'tab_name': 'Content'}) click: "#line-numbers" wait-for-local-storage: {"rustdoc-line-numbers": "true" } 
tests/rustdoc-gui/font-serif-change.goml2 + / 2 −
@@ -9,7 +9,7 @@ assert-css: ("body", {"font-family": |serif_font|}) assert-css: ("p code", {"font-family": |serif_code_font|})  // We now switch to the sans serif font-call-function: ("open-settings-menu", {})+call-function: ("change-settings-tab", {"tab_name": "Appearance"}) click: "#sans-serif-fonts"  store-value: (font, '"Fira Sans", sans-serif')@@ -23,7 +23,7 @@ assert-css: ("body", {"font-family": |font|}) assert-css: ("p code", {"font-family": |code_font|})  // We switch back to the serif font-call-function: ("open-settings-menu", {})+call-function: ("change-settings-tab", {"tab_name": "Appearance"}) click: "#sans-serif-fonts"  assert-css: ("body", {"font-family": |serif_font|})
tests/rustdoc-gui/help-page.goml18 + / 1 −
@@ -1,4 +1,5 @@ // This test ensures that opening the help page in its own tab works.+include: "utils.goml" go-to: "file://" + |DOC_PATH| + "/help.html" set-window-size: (1000, 1000) // Try desktop size first. wait-for: "#help"@@ -55,8 +56,24 @@ wait-for: "#search-tabs" // Waiting for the search.js to load. set-window-size: (1000, 1000) // Only supported on desktop. assert-false: "#help" click: "rustdoc-toolbar .help-menu > a"-assert-css: ("#help", {"display": "block"})+wait-for-css: ("#help", {"display": "block"}) assert-css: ("#help dd", {"font-size": "16px"})++define-function: ("check-arrow-color", [theme], block {+    call-function: ("switch-theme", {"theme": |theme|})+    click: "rustdoc-toolbar .help-menu > a"+    wait-for-css: ("#help", {"display": "block"})+    store-css: ("#help", {"background-color": background})+    assert-css: ("#help::before", {+        "background-color": |background|,+    })+})++// We check that the "top arrow" has the same color as the popover background.+call-function: ("check-arrow-color", {"theme": "ayu"})+call-function: ("check-arrow-color", {"theme": "dark"})+call-function: ("check-arrow-color", {"theme": "light"})+ click: "rustdoc-toolbar .help-menu > a" assert-false: "#help" 
tests/rustdoc-gui/hide-mobile-topbar.goml4 + / 0 −
@@ -1,4 +1,7 @@ // Checks sidebar resizing stays synced with the setting++include: "utils.goml"+ go-to: "file://" + |DOC_PATH| + "/settings.html" set-window-size: (400, 600) @@ -9,6 +12,7 @@ assert-property: ("#hide-sidebar", {"checked": "false"}) assert-css: ("rustdoc-topbar", {"display": "flex"})  // Toggle it+call-function: ("change-settings-tab", {'tab_name': 'Interface'}) click: "#hide-sidebar" assert-property: ("#hide-sidebar", {"checked": "true"}) assert-css: ("rustdoc-topbar", {"display": "none"})
tests/rustdoc-gui/mobile-topbar-menu-popovers.goml5 + / 5 −
@@ -1,23 +1,23 @@ // Ensure that topbar popover menus content can be scrolled on mobile. go-to: "file://" + |DOC_PATH| + "/lib2/index.html"-store-value: (window_height, 500)+store-value: (window_height, 300) set-window-size: (400, |window_height|)  include: "utils.goml"  // We open the settings menu call-function: ("open-settings-menu", {}) // We ensure it's not scrolled down yet.-assert-property: ("#settings .settings", {"scrollTop": 0})+assert-property: ("#settings .settings .setting", {"scrollTop": 0}) // We ensure its height is smaller than the window's, but its content's height is bigger.-store-property: ("#settings .settings", {"offsetHeight": menu_height, "scrollHeight": scroll_height})+store-property: ("#settings .settings .setting", {"offsetHeight": menu_height, "scrollHeight": scroll_height}) assert: |menu_height| < |window_height| && |scroll_height| > |window_height|  // We scroll to the last element of the menu. scroll-to: "#settings .setting-line:last-of-type input" // The item should be visible now, and so the Y scroll value should have changed. // Note: The `scrollTop` value will change if settings are added or removed.-assert-property: ("#settings .settings", {"scrollTop": 335})+assert-property: ("#settings .settings .setting", {"scrollTop": 84})  // Now we open the help menu. click: ".help-menu a"@@ -31,4 +31,4 @@ assert: |menu_height| < |window_height| && |scroll_height| > |window_height| // We scroll to the last element of the menu. scroll-to: "#help .infos > :last-child" // The item should be visible now, and so the Y scroll value should have changed.-assert-property: ("#help .content", {"scrollTop": 339})+assert-property: ("#help .content", {"scrollTop": 439})
tests/rustdoc-gui/search-tab.goml7 + / 7 −
@@ -39,15 +39,15 @@ define-function: (  call-function: ("check-colors", {     "theme": "ayu",-    "background": "transparent",+    "background": "#1d1f20",     "background_selected": "#141920",-    "background_hover": "transparent",+    "background_hover": "#141920",     "border_bottom": "0px none #c5c5c5",-    "border_bottom_selected": "1px solid #ffb44c",-    "border_bottom_hover": "1px solid rgba(242, 151, 24, 0.3)",-    "border_top": "0px none #c5c5c5",-    "border_top_selected": "0px none #c5c5c5",-    "border_top_hover": "0px none #c5c5c5",+    "border_bottom_selected": "0px none #c5c5c5",+    "border_bottom_hover": "0px none #c5c5c5",+    "border_top": "2px solid #1d1f20",+    "border_top_selected": "2px solid #ffb44c",+    "border_top_hover": "2px solid #ffb44c", }) call-function: ("check-colors", {     "theme": "dark",
tests/rustdoc-gui/setting-auto-hide-content-large-items.goml2 + / 0 −
@@ -27,6 +27,7 @@ call-function: ("check-setting", { })  // Now we change its value.+call-function: ("change-settings-tab", {'tab_name': 'Content'}) click: "#auto-hide-large-items" assert-local-storage: {"rustdoc-auto-hide-large-items": "false"} @@ -40,6 +41,7 @@ call-function: ("check-setting", { })  // And now we re-enable the setting.+call-function: ("change-settings-tab", {'tab_name': 'Content'}) click: "#auto-hide-large-items" assert-local-storage: {"rustdoc-auto-hide-large-items": "true"} 
tests/rustdoc-gui/setting-auto-hide-item-methods-docs.goml2 + / 0 −
@@ -24,6 +24,7 @@ call-function: ("check-setting", { })  // Now we change its value.+call-function: ("change-settings-tab", {'tab_name': 'Content'}) click: "#auto-hide-method-docs" assert-local-storage: {"rustdoc-auto-hide-method-docs": "true"} @@ -37,6 +38,7 @@ call-function: ("check-setting", { })  // And now we re-disable the setting.+call-function: ("change-settings-tab", {'tab_name': 'Content'}) click: "#auto-hide-method-docs" assert-local-storage: {"rustdoc-auto-hide-method-docs": "false"} 
tests/rustdoc-gui/setting-auto-hide-trait-implementations.goml2 + / 0 −
@@ -22,6 +22,7 @@ call-function: ("check-setting", {     "toggle_attribute_value": "", }) +call-function: ("change-settings-tab", {'tab_name': 'Content'}) // Now we change its value. click: "#auto-hide-trait-implementations" assert-local-storage: {"rustdoc-auto-hide-trait-implementations": "true"}@@ -35,6 +36,7 @@ call-function: ("check-setting", {     "toggle_attribute_value": null, }) +call-function: ("change-settings-tab", {'tab_name': 'Content'}) // And now we re-disable the setting. click: "#auto-hide-trait-implementations" assert-local-storage: {"rustdoc-auto-hide-trait-implementations": "false"}
tests/rustdoc-gui/setting-go-to-only-result.goml3 + / 2 −
@@ -27,7 +27,7 @@ wait-for: "#search" assert-document-property: ({"URL": "/lib2/index.html"}, CONTAINS)  // Now we change its value.-call-function: ("open-settings-menu", {})+call-function: ("change-settings-tab", {'tab_name': 'Interface'}) click: "#go-to-only-result" assert-local-storage: {"rustdoc-go-to-only-result": "true"} @@ -58,6 +58,7 @@ call-function: ("check-setting", { })  // And now we re-disable the setting.+call-function: ("change-settings-tab", {'tab_name': 'Interface'}) click: "#go-to-only-result" assert-local-storage: {"rustdoc-go-to-only-result": "false"} @@ -105,7 +106,7 @@ write-into: (".search-input", "Whitespace") call-function: ("search-multiple-results", {"location": |location|})  // We now toggle the setting and reload the page.-call-function: ("open-settings-menu", {})+call-function: ("change-settings-tab", {'tab_name': 'Interface'}) click: "#go-to-only-result" assert-local-storage: {"rustdoc-go-to-only-result": "true"} 
tests/rustdoc-gui/setting-hide-deprecated.goml4 + / 4 −
@@ -12,7 +12,7 @@ assert-count: ("dt" + |deprecated_class|, 2) assert-css: ("dt" + |deprecated_class|, {"display": "block"}, ALL)  // We enable the "hide deprecated items" setting.-call-function: ("open-settings-menu", {})+call-function: ("change-settings-tab", {'tab_name': 'Content'}) click: "#hide-deprecated-items" // None of them should be displayed anymore. wait-for-css: ("dt" + |deprecated_class|, {"display": "none"}, ALL)@@ -31,7 +31,7 @@ assert-count: ("details" + |deprecated_class|, 2) assert-css: ("details" + |deprecated_class|, {"display": "block"}, ALL)  // We enable the "hide deprecated items" setting.-call-function: ("open-settings-menu", {})+call-function: ("change-settings-tab", {'tab_name': 'Content'}) click: "#hide-deprecated-items"  // They shouldn't be displayed anymore.@@ -56,7 +56,7 @@ assert: "details:not(" + |deprecated_class| + ") #method\.future_deprecated_fn" assert-css: ("details" + |deprecated_class|, {"display": "block"}, ALL)  // We enable the "hide deprecated items" setting.-call-function: ("open-settings-menu", {})+call-function: ("change-settings-tab", {'tab_name': 'Content'}) click: "#hide-deprecated-items"  // They shouldn't be displayed anymore.@@ -84,7 +84,7 @@ assert-text: ( ) assert-css: ("#results ul.search-results.active .deprecated-count", {"display": "none"}) // We enable the "hide deprecated items" setting.-call-function: ("open-settings-menu", {})+call-function: ("change-settings-tab", {'tab_name': 'Content'}) click: "#hide-deprecated-items" // None of them should be displayed anymore. wait-for-css: (
tests/rustdoc-gui/settings.goml73 + / 2 −
@@ -150,6 +150,7 @@ compare-elements-position-near: (     {"y": 1} ) +call-function: ("change-settings-tab", {'tab_name': 'Content'}) // First we check the "default" display for toggles. assert-css: (     "#auto-hide-large-items",@@ -241,6 +242,7 @@ assert-css: ( )  // We now switch the display.+call-function: ("change-settings-tab", {'tab_name': 'Appearance'}) click: "#theme-system-preference" // Wait for the hidden element to show up. wait-for: "#preferred-dark-theme.setting-line:not(.hidden)"@@ -250,8 +252,77 @@ assert: "#preferred-light-theme.setting-line:not(.hidden)" assert-text: ("#preferred-dark-theme .setting-radio-name", "Preferred dark theme") assert-text: ("#preferred-light-theme .setting-radio-name", "Preferred light theme") +// We check the display of the settings popover tabs.+define-function: (+    "check-tabs",+    [theme, background, background_selected, background_hover, border_bottom,+     border_bottom_selected, border_bottom_hover, border_top, border_top_selected,+     border_top_hover],+    block {+        call-function: ("switch-theme-and-keep-open", {"theme": |theme|})++        assert-css: ("#settings .tabs > button.tab:not(.selected)", {+            "background-color": |background|,+            "border-bottom": |border_bottom|,+            "border-top": |border_top|,+        })+        assert-css: ("#settings .tabs > button.tab.selected", {+            "background-color": |background_selected|,+            "border-bottom": |border_bottom_selected|,+            "border-top": |border_top_selected|,+        })+        // We ensure that the popover "top arrow" has the same color as the tabs background.+        assert-css: ("#settings::before", {+            "background-color": |background|,+        })+        move-cursor-to: "#settings .tabs > button.tab:not(.selected)"+        assert-css: ("#settings .tabs > button.tab:not(.selected):hover", {+            "background-color": |background_hover|,+            "border-bottom": |border_bottom_hover|,+            "border-top": |border_top_hover|,+        })+})++call-function: ("check-tabs", {+    "theme": "ayu",+    "background": "#1d1f20",+    "background_selected": "#141920",+    "background_hover": "#141920",+    "border_bottom": "0px none #c5c5c5",+    "border_bottom_selected": "0px none #c5c5c5",+    "border_bottom_hover": "0px none #c5c5c5",+    "border_top": "2px solid #1d1f20",+    "border_top_selected": "2px solid #ffb44c",+    "border_top_hover": "2px solid #ffb44c",+})+call-function: ("check-tabs", {+    "theme": "dark",+    "background": "#252525",+    "background_selected": "#353535",+    "background_hover": "#353535",+    "border_bottom": "0px none #ddd",+    "border_bottom_selected": "0px none #ddd",+    "border_bottom_hover": "0px none #ddd",+    "border_top": "2px solid #252525",+    "border_top_selected": "2px solid #0089ff",+    "border_top_hover": "2px solid #0089ff",+})+call-function: ("check-tabs", {+    "theme": "light",+    "background": "#e6e6e6",+    "background_selected": "#fff",+    "background_hover": "#fff",+    "border_bottom": "0px none #000",+    "border_bottom_selected": "0px none #000",+    "border_bottom_hover": "0px none #000",+    "border_top": "2px solid #e6e6e6",+    "border_top_selected": "2px solid #0089ff",+    "border_top_hover": "2px solid #0089ff",+})+ // We now check that clicking on the toggles' text is like clicking on the checkbox. // To test it, we use the "Disable keyboard shortcuts".+call-function: ("change-settings-tab", {'tab_name': 'Interface'}) set-local-storage: {"rustdoc-disable-shortcuts": "false"} click: "#disable-shortcuts" assert-local-storage: {"rustdoc-disable-shortcuts": "true"}@@ -312,7 +383,7 @@ assert-css: (".setting-line", {"margin": |setting_line_margin|})  // We will check that the checkboxes size doesn't change either. assert-size: (-    "#settings label > input[type='checkbox']",+    "#settings .selected label > input[type='checkbox']",     {"width": 19, "height": 19},     ALL, )@@ -333,7 +404,7 @@ assert-css: (".setting-radio", {"cursor": "pointer"})  // We ensure that the checkboxes size didn't change. assert-size: (-    "#settings label > input[type='checkbox']",+    "#settings .selected label > input[type='checkbox']",     {"width": 19, "height": 19},     ALL, )
tests/rustdoc-gui/sidebar-resize-setting.goml2 + / 3 −
@@ -5,8 +5,7 @@ assert-property: (".sidebar", {"clientWidth": "199"}) show-text: true  // Verify that the "hide" option is unchecked-call-function: ("open-settings-menu", {})-assert-css: ("#settings", {"display": "block"})+call-function: ("change-settings-tab", {'tab_name': 'Interface'}) assert-property: ("#hide-sidebar", {"checked": "false"}) press-key: "Escape" wait-for-css: ("#settings", {"display": "none"})@@ -28,7 +27,7 @@ go-to: "file://" + |DOC_PATH| + "/test_docs/index.html" set-window-size: (600, 600) focus: "rustdoc-topbar .settings-menu a" press-key: "Enter"-wait-for-css: ("#settings", {"display": "block"})+call-function: ("change-settings-tab", {'tab_name': 'Interface'}) wait-for-css: ("#sidebar-button", {"position": "static"}) assert-property: ("#hide-sidebar", {"checked": "false"}) click: "#hide-sidebar"
tests/rustdoc-gui/source-code-wrapping.goml2 + / 2 −
@@ -14,7 +14,7 @@ define-function: ( )  store-size: (".rust code", {"width": width, "height": height})-call-function: ("open-settings-menu", {})+call-function: ("change-settings-tab", {'tab_name': 'Content'}) call-function: ("click-code-wrapping", {"expected": "true"}) wait-for-size-false: (".rust code", {"width": |width|, "height": |height|}) store-size: (".rust code", {"width": new_width, "height": new_height})@@ -28,7 +28,7 @@ assert-size: (".rust code", {"width": |width|, "height": |height|})  // Now let's check in docs code examples. go-to: "file://" + |DOC_PATH| + "/test_docs/trait_bounds/index.html"-call-function: ("open-settings-menu", {})+call-function: ("change-settings-tab", {'tab_name': 'Content'})  store-property: (".example-wrap .rust code", {"scrollWidth": rust_width, "scrollHeight": rust_height}) store-property: (".example-wrap .language-text code", {"scrollWidth": txt_width, "scrollHeight": txt_height})
tests/rustdoc-gui/utils.goml26 + / 5 −
@@ -44,6 +44,18 @@ define-function: (     } ) +define-function: (+    "change-settings-tab",+    [tab_name],+    block {+        // In case it's not open yet, we open it.+        call-function: ("open-settings-menu", {})+        click: "#settings .tabs button[data-name='" + |tab_name| + "']"+        // We ensure it worked.+        wait-for: "#settings .tabs button[data-name='" + |tab_name| + "'].selected"+    }+)+ define-function: (     "close-settings-menu",     [],@@ -64,19 +76,28 @@ define-function: ( )  define-function: (-    "switch-theme",+    "switch-theme-and-keep-open",     [theme],     block {-        // Set the theme.-        call-function: ("open-settings-menu", {})+        // Open the settings menu.+        call-function: ("change-settings-tab", {'tab_name': 'Appearance'})         // Change the setting.         click: "#theme-"+ |theme|-        call-function: ("close-settings-menu", {})         // Ensure that the local storage was correctly updated.         assert-local-storage: {"rustdoc-theme": |theme|}     }, ) +define-function: (+    "switch-theme",+    [theme],+    block {+        call-function: ("switch-theme-and-keep-open", {'theme': |theme|})+        // Close the settings menu.+        call-function: ("close-settings-menu", {})+    },+)+ define-function: (     "open-search",     [],@@ -134,7 +155,7 @@ define-function: (     "switch-line-numbers-setting",     [expected_status],     block {-        call-function: ("open-settings-menu", {})+        call-function: ("change-settings-tab", {'tab_name': 'Content'})         // We change the line numbers setting.         click: "#line-numbers"         call-function: ("close-settings-menu", {})
tests/rustdoc-html/sidebar/sidebar-dupes.rsadded22 + / 0 −
@@ -0,0 +1,22 @@+#![crate_name = "foo"]++// Test to ensure identically named methods in the sidebar get a disambiguator++//@ has foo/struct.C.html+pub struct A;+pub struct B;+pub struct C<T>(T);++impl C<A> {+    //@ matches - "//a[@href='#method.dupe']" "^dupe \(C<A>\)$"+    pub fn dupe(self) {}+    //@ matches - "//a[@href='#method.uniq1']" "^uniq1$"+    pub fn uniq1(self) {}+}++impl C<B> {+    //@ matches - "//a[@href='#method.dupe-1']" "^dupe \(C<B>\)$"+    pub fn dupe(self) {}+    //@ matches - "//a[@href='#method.uniq2']" "^uniq2$"+    pub fn uniq2(self) {}+}
tests/ui/const-generics/auxiliary/non_local.rs5 + / 0 −
@@ -1,3 +1,8 @@ pub trait NonLocal {     fn method<const N: i32>(); }++use std::future::Future;+pub trait R {+    fn read<const L: usize>(&mut self) -> impl Future<Output = [u8; L]>;+}
tests/ui/const-generics/non-local-method-same-ok.rsadded15 + / 0 −
@@ -0,0 +1,15 @@+// Adapted from; https://github.com/rust-lang/rust/issues/164180#issuecomment-6168887401+//@ check-pass+//@ compile-flags: --crate-type lib+//@ aux-build:non_local.rs+//@ edition: 2024++extern crate non_local;++struct Local;++impl non_local::R for Local {+    async fn read<const L: usize>(&mut self) -> [u8; L] {+        [0; L]+    }+}
tests/ui/const-generics/non-local-method.rs8 + / 0 −
@@ -1,5 +1,6 @@ //@ compile-flags: --crate-type lib //@ aux-build:non_local.rs+//@ edition: 2024  extern crate non_local; @@ -13,3 +14,10 @@ impl NonLocal for Local {         print!("hello\n");     } }++impl non_local::R for Local {+    async fn read<const L: usize>(&mut self) -> [u16; L] {+        //~^ ERROR: expected `impl Future<Output = [u16; L]>` to be a future that resolves to `[u8; L]`, but it resolves to `[u16; L]`+        [0; L]+    }+}
tests/ui/const-generics/non-local-method.stderr19 + / 3 −
@@ -1,9 +1,25 @@ error[E0053]: associated function `method` has an incompatible type for const generic parameter-  --> $DIR/non-local-method.rs:11:24+  --> $DIR/non-local-method.rs:12:24    | LL |     fn method<const N: u16>() {    |                        ^^^ expected `i32`, found `u16` -error: aborting due to 1 previous error+error[E0271]: expected `impl Future<Output = [u16; L]>` to be a future that resolves to `[u8; L]`, but it resolves to `[u16; L]`+  --> $DIR/non-local-method.rs:19:49+   |+LL |     async fn read<const L: usize>(&mut self) -> [u16; L] {+   |                                                 ^^^^^^^^ expected `[u8; L]`, found `[u16; L]`+   |+   = note: expected array `[u8; L]`+              found array `[u16; L]`+note: required by a bound in `R::read::{anon_assoc#0}`+  --> $DIR/auxiliary/non_local.rs:7:55+   |+LL | pub trait R {+LL |     fn read<const L: usize>(&mut self) -> impl Future<Output = [u8; L]>;+   |                                                       ^^^^^^^^^^^^^^^^ required by this bound in `R::read::{anon_assoc#0}`++error: aborting due to 2 previous errors -For more information about this error, try `rustc --explain E0053`.+Some errors have detailed explanations: E0053, E0271.+For more information about an error, try `rustc --explain E0053`.
tests/ui/delegation/self-mapping-output.rs2 + / 7 −
@@ -17,7 +17,6 @@ mod success {     impl Trait for W {         reuse Trait::method { self.0 }         reuse Trait::r#static;-        //~^ WARN: function cannot return without recursing [unconditional_recursion]         reuse Trait::raw_S { self.0 }     } @@ -45,7 +44,6 @@ mod success_non_field {     impl Trait for W {         reuse Trait::method { S }         reuse Trait::r#static;-        //~^ WARN: function cannot return without recursing [unconditional_recursion]         reuse Trait::raw_S { S }     } @@ -81,7 +79,6 @@ mod success_generics {     impl<'a, T, const N: usize> Trait<'a, T, N> for W<'a, 'static, T, (), N, 123> {         reuse Trait::<'a, T, N>::method { self.0 }         reuse Trait::<'a, T, N>::r#static;-        //~^ WARN: function cannot return without recursing [unconditional_recursion]         reuse Trait::<'a, T, N>::raw_S { self.0 }     } @@ -110,7 +107,6 @@ mod no_constructor {         reuse Trait::method { self.0 }         //~^ ERROR: no field `0` on type `&no_constructor::W`         reuse Trait::r#static;-        //~^ WARN: function cannot return without recursing [unconditional_recursion]         reuse Trait::raw_S { self.0 }         //~^ ERROR: no field `0` on type `&no_constructor::W`     }@@ -142,14 +138,15 @@ mod more_than_one_field {         reuse Trait::method { self.0 }         //~^ ERROR: missing fields `0`, `1` and `2` in initializer of `more_than_one_field::W`         reuse Trait::r#static;-        //~^ WARN: function cannot return without recursing [unconditional_recursion]+        //~^ ERROR: missing fields `0`, `1` and `2` in initializer of `more_than_one_field::W`         reuse Trait::raw_S { self.0 }     }      impl W {         reuse Trait::method { self.0 }         //~^ ERROR: missing fields `0`, `1` and `2` in initializer of `more_than_one_field::W`         reuse Trait::r#static;+        //~^ ERROR: missing fields `0`, `1` and `2` in initializer of `more_than_one_field::W`         reuse Trait::raw_S { self.0 }     } }@@ -300,7 +297,6 @@ mod wrong_target_expression {         //~^ ERROR: mismatched types          reuse Trait::r#static;-        //~^ WARN: function cannot return without recursing [unconditional_recursion]         reuse Trait::raw_S { F }     } @@ -335,7 +331,6 @@ mod privacy {         //~^ ERROR: field `0` of struct `private::W` is private          reuse Trait::r#static;-        //~^ WARN: function cannot return without recursing [unconditional_recursion]          reuse Trait::raw_S { self.0 }         //~^ ERROR: field `0` of struct `private::W` is private
tests/ui/delegation/self-mapping-output.stderr56 + / 128 −
@@ -1,5 +1,5 @@ error[E0609]: no field `0` on type `&no_constructor::W`-  --> $DIR/self-mapping-output.rs:110:36+  --> $DIR/self-mapping-output.rs:107:36    | LL |         reuse Trait::method { self.0 }    |                                    ^ unknown field@@ -11,7 +11,7 @@ LL +         reuse Trait::method { self.s }    |  error[E0609]: no field `0` on type `&no_constructor::W`-  --> $DIR/self-mapping-output.rs:114:35+  --> $DIR/self-mapping-output.rs:110:35    | LL |         reuse Trait::raw_S { self.0 }    |                                   ^ unknown field@@ -23,7 +23,7 @@ LL +         reuse Trait::raw_S { self.s }    |  error[E0609]: no field `0` on type `&no_constructor::W`-  --> $DIR/self-mapping-output.rs:119:36+  --> $DIR/self-mapping-output.rs:115:36    | LL |         reuse Trait::method { self.0 }    |                                    ^ unknown field@@ -35,7 +35,7 @@ LL +         reuse Trait::method { self.s }    |  error[E0609]: no field `0` on type `&no_constructor::W`-  --> $DIR/self-mapping-output.rs:122:35+  --> $DIR/self-mapping-output.rs:118:35    | LL |         reuse Trait::raw_S { self.0 }    |                                   ^ unknown field@@ -47,19 +47,31 @@ LL +         reuse Trait::raw_S { self.s }    |  error[E0063]: missing fields `0`, `1` and `2` in initializer of `more_than_one_field::W`-  --> $DIR/self-mapping-output.rs:142:22+  --> $DIR/self-mapping-output.rs:138:22    | LL |         reuse Trait::method { self.0 }    |                      ^^^^^^ missing `0`, `1` and `2`  error[E0063]: missing fields `0`, `1` and `2` in initializer of `more_than_one_field::W`-  --> $DIR/self-mapping-output.rs:150:22+  --> $DIR/self-mapping-output.rs:140:22+   |+LL |         reuse Trait::r#static;+   |                      ^^^^^^^^ missing `0`, `1` and `2`++error[E0063]: missing fields `0`, `1` and `2` in initializer of `more_than_one_field::W`+  --> $DIR/self-mapping-output.rs:146:22    | LL |         reuse Trait::method { self.0 }    |                      ^^^^^^ missing `0`, `1` and `2` +error[E0063]: missing fields `0`, `1` and `2` in initializer of `more_than_one_field::W`+  --> $DIR/self-mapping-output.rs:148:22+   |+LL |         reuse Trait::r#static;+   |                      ^^^^^^^^ missing `0`, `1` and `2`+ error[E0308]: mismatched types-  --> $DIR/self-mapping-output.rs:182:25+  --> $DIR/self-mapping-output.rs:179:25    | LL |         pub fn method(_: impl super::Trait) -> impl super::Trait {    |                                                ----------------- the found opaque type@@ -74,7 +86,7 @@ LL |         reuse to_reuse::method { self.0 }            found opaque type `impl non_trait_path_reuse::Trait`  error[E0308]: mismatched types-  --> $DIR/self-mapping-output.rs:184:25+  --> $DIR/self-mapping-output.rs:181:25    | LL |         pub fn r#static() -> impl super::Trait {    |                              ----------------- the found opaque type@@ -89,13 +101,13 @@ LL |         reuse to_reuse::r#static;            found opaque type `impl non_trait_path_reuse::Trait`  error[E0609]: no field `0` on type `impl super::Trait`-  --> $DIR/self-mapping-output.rs:189:39+  --> $DIR/self-mapping-output.rs:186:39    | LL |         reuse to_reuse::method { self.0 }    |                                       ^ unknown field  error[E0308]: mismatched types-  --> $DIR/self-mapping-output.rs:211:31+  --> $DIR/self-mapping-output.rs:208:31    | LL |         reuse Trait::method { self.0 }    |                      ------   ^^^^^^ expected `&_`, found `()`@@ -105,7 +117,7 @@ LL |         reuse Trait::method { self.0 }    = note: expected reference `&_`               found unit type `()` note: method defined here-  --> $DIR/self-mapping-output.rs:197:12+  --> $DIR/self-mapping-output.rs:194:12    | LL |         fn method(&self) -> ();    |            ^^^^^^  ----@@ -115,14 +127,14 @@ LL |         reuse Trait::method { &self.0 }    |                               +  error[E0283]: type annotations needed-  --> $DIR/self-mapping-output.rs:214:22+  --> $DIR/self-mapping-output.rs:211:22    | LL |         reuse Trait::r#static;    |                      ^^^^^^^^ cannot infer type    |    = note: the type must implement `non_Self_return_type::Trait` help: the following types implement trait `non_Self_return_type::Trait`-  --> $DIR/self-mapping-output.rs:203:5+  --> $DIR/self-mapping-output.rs:200:5    | LL |     impl Trait for S {    |     ^^^^^^^^^^^^^^^^ `non_Self_return_type::S`@@ -131,7 +143,7 @@ LL |     impl Trait for W {    |     ^^^^^^^^^^^^^^^^ `non_Self_return_type::W`  error[E0308]: mismatched types-  --> $DIR/self-mapping-output.rs:217:30+  --> $DIR/self-mapping-output.rs:214:30    | LL |         reuse Trait::raw_S { self.0 }    |                      -----   ^^^^^^ expected `&_`, found `()`@@ -141,7 +153,7 @@ LL |         reuse Trait::raw_S { self.0 }    = note: expected reference `&_`               found unit type `()` note: method defined here-  --> $DIR/self-mapping-output.rs:199:12+  --> $DIR/self-mapping-output.rs:196:12    | LL |         fn raw_S(&self) -> S { S }    |            ^^^^^ -----@@ -151,7 +163,7 @@ LL |         reuse Trait::raw_S { &self.0 }    |                              +  error[E0308]: mismatched types-  --> $DIR/self-mapping-output.rs:222:31+  --> $DIR/self-mapping-output.rs:219:31    | LL |         reuse Trait::method { self.0 }    |                      ------   ^^^^^^ expected `&_`, found `()`@@ -161,7 +173,7 @@ LL |         reuse Trait::method { self.0 }    = note: expected reference `&_`               found unit type `()` note: method defined here-  --> $DIR/self-mapping-output.rs:197:12+  --> $DIR/self-mapping-output.rs:194:12    | LL |         fn method(&self) -> ();    |            ^^^^^^  ----@@ -171,14 +183,14 @@ LL |         reuse Trait::method { &self.0 }    |                               +  error[E0283]: type annotations needed-  --> $DIR/self-mapping-output.rs:225:22+  --> $DIR/self-mapping-output.rs:222:22    | LL |         reuse Trait::r#static;    |                      ^^^^^^^^ cannot infer type    |    = note: the type must implement `non_Self_return_type::Trait` help: the following types implement trait `non_Self_return_type::Trait`-  --> $DIR/self-mapping-output.rs:203:5+  --> $DIR/self-mapping-output.rs:200:5    | LL |     impl Trait for S {    |     ^^^^^^^^^^^^^^^^ `non_Self_return_type::S`@@ -187,7 +199,7 @@ LL |     impl Trait for W {    |     ^^^^^^^^^^^^^^^^ `non_Self_return_type::W`  error[E0308]: mismatched types-  --> $DIR/self-mapping-output.rs:228:30+  --> $DIR/self-mapping-output.rs:225:30    | LL |         reuse Trait::raw_S { self.0 }    |                      -----   ^^^^^^ expected `&_`, found `()`@@ -197,7 +209,7 @@ LL |         reuse Trait::raw_S { self.0 }    = note: expected reference `&_`               found unit type `()` note: method defined here-  --> $DIR/self-mapping-output.rs:199:12+  --> $DIR/self-mapping-output.rs:196:12    | LL |         fn raw_S(&self) -> S { S }    |            ^^^^^ -----@@ -207,7 +219,7 @@ LL |         reuse Trait::raw_S { &self.0 }    |                              +  error[E0308]: mismatched types-  --> $DIR/self-mapping-output.rs:254:38+  --> $DIR/self-mapping-output.rs:251:38    | LL |         reuse <F as Trait>::method { self.0 }    |                             ------   ^^^^^^ expected `&F`, found `&S`@@ -218,28 +230,25 @@ LL |         reuse <F as Trait>::method { self.0 }    = note: expected reference `&wrong_return_type::F`               found reference `&wrong_return_type::S` note: method defined here-  --> $DIR/self-mapping-output.rs:235:12+  --> $DIR/self-mapping-output.rs:232:12    | LL |         fn method(&self) -> Self;    |            ^^^^^^  ----  error[E0308]: mismatched types-  --> $DIR/self-mapping-output.rs:254:29+  --> $DIR/self-mapping-output.rs:251:29    | LL |         reuse <F as Trait>::method { self.0 }    |                             ^^^^^^ expected `S`, found `F`  error[E0308]: mismatched types-  --> $DIR/self-mapping-output.rs:258:29+  --> $DIR/self-mapping-output.rs:255:29    | LL |         reuse <F as Trait>::r#static;-   |                             ^^^^^^^^-   |                             |-   |                             expected `W`, found `F`-   |                             expected `wrong_return_type::W` because of return type+   |                             ^^^^^^^^ expected `S`, found `F`  error[E0308]: mismatched types-  --> $DIR/self-mapping-output.rs:261:37+  --> $DIR/self-mapping-output.rs:258:37    | LL |         reuse <F as Trait>::raw_S { self.0 }    |                             -----   ^^^^^^ expected `&F`, found `&S`@@ -249,13 +258,13 @@ LL |         reuse <F as Trait>::raw_S { self.0 }    = note: expected reference `&wrong_return_type::F`               found reference `&wrong_return_type::S` note: method defined here-  --> $DIR/self-mapping-output.rs:237:12+  --> $DIR/self-mapping-output.rs:234:12    | LL |         fn raw_S(&self) -> S { S }    |            ^^^^^ -----  error[E0308]: mismatched types-  --> $DIR/self-mapping-output.rs:266:38+  --> $DIR/self-mapping-output.rs:263:38    | LL |         reuse <F as Trait>::method { self.0 }    |                             ------   ^^^^^^ expected `&F`, found `&S`@@ -266,28 +275,25 @@ LL |         reuse <F as Trait>::method { self.0 }    = note: expected reference `&wrong_return_type::F`               found reference `&wrong_return_type::S` note: method defined here-  --> $DIR/self-mapping-output.rs:235:12+  --> $DIR/self-mapping-output.rs:232:12    | LL |         fn method(&self) -> Self;    |            ^^^^^^  ----  error[E0308]: mismatched types-  --> $DIR/self-mapping-output.rs:266:29+  --> $DIR/self-mapping-output.rs:263:29    | LL |         reuse <F as Trait>::method { self.0 }    |                             ^^^^^^ expected `S`, found `F`  error[E0308]: mismatched types-  --> $DIR/self-mapping-output.rs:270:29+  --> $DIR/self-mapping-output.rs:267:29    | LL |         reuse <F as Trait>::r#static;-   |                             ^^^^^^^^-   |                             |-   |                             expected `W`, found `F`-   |                             expected `wrong_return_type::W` because of return type+   |                             ^^^^^^^^ expected `S`, found `F`  error[E0308]: mismatched types-  --> $DIR/self-mapping-output.rs:273:37+  --> $DIR/self-mapping-output.rs:270:37    | LL |         reuse <F as Trait>::raw_S { self.0 }    |                             -----   ^^^^^^ expected `&F`, found `&S`@@ -297,13 +303,13 @@ LL |         reuse <F as Trait>::raw_S { self.0 }    = note: expected reference `&wrong_return_type::F`               found reference `&wrong_return_type::S` note: method defined here-  --> $DIR/self-mapping-output.rs:237:12+  --> $DIR/self-mapping-output.rs:234:12    | LL |         fn raw_S(&self) -> S { S }    |            ^^^^^ -----  error[E0308]: mismatched types-  --> $DIR/self-mapping-output.rs:299:31+  --> $DIR/self-mapping-output.rs:296:31    | LL |         reuse Trait::method { F }    |                      ------   ^ expected `&S`, found `&F`@@ -314,13 +320,13 @@ LL |         reuse Trait::method { F }    = note: expected reference `&wrong_target_expression::S`               found reference `&wrong_target_expression::F` note: method defined here-  --> $DIR/self-mapping-output.rs:280:12+  --> $DIR/self-mapping-output.rs:277:12    | LL |         fn method(&self) -> Self;    |            ^^^^^^  ----  error[E0308]: mismatched types-  --> $DIR/self-mapping-output.rs:308:31+  --> $DIR/self-mapping-output.rs:304:31    | LL |         reuse Trait::method { F }    |                      ------   ^ expected `&S`, found `&F`@@ -331,114 +337,36 @@ LL |         reuse Trait::method { F }    = note: expected reference `&wrong_target_expression::S`               found reference `&wrong_target_expression::F` note: method defined here-  --> $DIR/self-mapping-output.rs:280:12+  --> $DIR/self-mapping-output.rs:277:12    | LL |         fn method(&self) -> Self;    |            ^^^^^^  ----  error[E0616]: field `0` of struct `private::W` is private-  --> $DIR/self-mapping-output.rs:334:36+  --> $DIR/self-mapping-output.rs:330:36    | LL |         reuse Trait::method { self.0 }    |                                    ^ private field  error[E0616]: field `0` of struct `private::W` is private-  --> $DIR/self-mapping-output.rs:340:35+  --> $DIR/self-mapping-output.rs:335:35    | LL |         reuse Trait::raw_S { self.0 }    |                                   ^ private field  error[E0616]: field `0` of struct `private::W` is private-  --> $DIR/self-mapping-output.rs:345:36+  --> $DIR/self-mapping-output.rs:340:36    | LL |         reuse Trait::method { self.0 }    |                                    ^ private field  error[E0616]: field `0` of struct `private::W` is private-  --> $DIR/self-mapping-output.rs:350:35+  --> $DIR/self-mapping-output.rs:345:35    | LL |         reuse Trait::raw_S { self.0 }    |                                   ^ private field -warning: function cannot return without recursing-  --> $DIR/self-mapping-output.rs:19:22-   |-LL |         reuse Trait::r#static;-   |                      ^^^^^^^^-   |                      |-   |                      cannot return without recursing-   |                      recursive call site-   |-   = help: a `loop` may express intention better if this is on purpose-   = note: `#[warn(unconditional_recursion)]` on by default--warning: function cannot return without recursing-  --> $DIR/self-mapping-output.rs:47:22-   |-LL |         reuse Trait::r#static;-   |                      ^^^^^^^^-   |                      |-   |                      cannot return without recursing-   |                      recursive call site-   |-   = help: a `loop` may express intention better if this is on purpose--warning: function cannot return without recursing-  --> $DIR/self-mapping-output.rs:83:34-   |-LL |         reuse Trait::<'a, T, N>::r#static;-   |                                  ^^^^^^^^-   |                                  |-   |                                  cannot return without recursing-   |                                  recursive call site-   |-   = help: a `loop` may express intention better if this is on purpose--warning: function cannot return without recursing-  --> $DIR/self-mapping-output.rs:112:22-   |-LL |         reuse Trait::r#static;-   |                      ^^^^^^^^-   |                      |-   |                      cannot return without recursing-   |                      recursive call site-   |-   = help: a `loop` may express intention better if this is on purpose--warning: function cannot return without recursing-  --> $DIR/self-mapping-output.rs:144:22-   |-LL |         reuse Trait::r#static;-   |                      ^^^^^^^^-   |                      |-   |                      cannot return without recursing-   |                      recursive call site-   |-   = help: a `loop` may express intention better if this is on purpose--warning: function cannot return without recursing-  --> $DIR/self-mapping-output.rs:302:22-   |-LL |         reuse Trait::r#static;-   |                      ^^^^^^^^-   |                      |-   |                      cannot return without recursing-   |                      recursive call site-   |-   = help: a `loop` may express intention better if this is on purpose--warning: function cannot return without recursing-  --> $DIR/self-mapping-output.rs:337:22-   |-LL |         reuse Trait::r#static;-   |                      ^^^^^^^^-   |                      |-   |                      cannot return without recursing-   |                      recursive call site-   |-   = help: a `loop` may express intention better if this is on purpose--error: aborting due to 29 previous errors; 7 warnings emitted+error: aborting due to 31 previous errors  Some errors have detailed explanations: E0063, E0283, E0308, E0609, E0616. For more information about an error, try `rustc --explain E0063`.
tests/ui/delegation/self-mapping-static-fn-no-target-expr.rsadded26 + / 0 −
@@ -0,0 +1,26 @@+//@ check-pass++#![feature(fn_delegation)]++pub trait Idx {+    fn new(idx: usize) -> Self;++    fn index(self) -> usize;+}++struct DefIndex(usize);+impl Idx for DefIndex {+    fn new(idx: usize) -> Self { DefIndex(idx) }+    fn index(self) -> usize { self.0 }+}++struct LocalDefId {+    index: DefIndex+}++impl Idx for LocalDefId {+    reuse <DefIndex as Idx>::new;+    reuse Idx::index { self.index }+}++fn main() {}