rust-lang/rust · #164418
Rollup of 7 pull requests
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, ¶m)| 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, ¶m)| 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 insidecompiler/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_DAClibrary/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 privatetests/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() {}