From e0b75776c3265fdb9dead021e565c2fda906ed88 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Rakic?= Date: Wed, 26 Feb 2025 15:45:36 +0000 Subject: [PATCH 01/16] linux x64: default to `-znostart-stop-gc` This will help stabilization of lld. --- compiler/rustc_codegen_ssa/src/back/link.rs | 29 +++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs index a170b2e3b6a56..3407117a06e3b 100644 --- a/compiler/rustc_codegen_ssa/src/back/link.rs +++ b/compiler/rustc_codegen_ssa/src/back/link.rs @@ -3382,6 +3382,35 @@ fn add_lld_args( // this, `wasm-component-ld`, which is overridden if this option is passed. if !sess.target.is_like_wasm { cmd.cc_arg("-fuse-ld=lld"); + + // On ELF platforms like at least x64 linux, GNU ld and LLD have opposite defaults on some + // section garbage-collection features. For example, the somewhat popular `linkme` crate and + // its dependents rely in practice on this difference: when using lld, they need `-z + // nostart-stop-gc` to prevent encapsulation symbols and sections from being + // garbage-collected. + // + // More information about all this can be found in: + // - https://maskray.me/blog/2021-01-31-metadata-sections-comdat-and-shf-link-order + // - https://lld.llvm.org/ELF/start-stop-gc + // + // So when using lld, we restore, for now, the traditional behavior to help migration, but + // will remove it in the future. + // Since this only disables an optimization, it shouldn't create issues, but is in theory + // slightly suboptimal. However, it: + // - doesn't have any visible impact on our benchmarks + // - reduces the need to disable lld for the crates that depend on this + // + // Note that lld can detect some cases where this difference is relied on, and emits a + // dedicated error to add this link arg. We could make use of this error to emit an FCW. As + // of writing this, we don't do it, because lld is already enabled by default on nightly + // without this mitigation: no working project would see the FCW, so we do this to help + // stabilization. + // + // FIXME: emit an FCW if linking fails due its absence, and then remove this link-arg in the + // future. + if sess.target.llvm_target == "x86_64-unknown-linux-gnu" { + cmd.link_arg("-znostart-stop-gc"); + } } if !flavor.is_gnu() { From 0dfe2ae3fb72c50ea369286131c73daede13d7e5 Mon Sep 17 00:00:00 2001 From: Chris Denton Date: Mon, 24 Feb 2025 03:48:12 +0000 Subject: [PATCH 02/16] Windows: Use MoveFileEx by default in `fs:rename` --- library/std/src/sys/pal/windows/fs.rs | 179 ++++++++------------------ 1 file changed, 56 insertions(+), 123 deletions(-) diff --git a/library/std/src/sys/pal/windows/fs.rs b/library/std/src/sys/pal/windows/fs.rs index 17dc3e5c257d4..34108e44d58bd 100644 --- a/library/std/src/sys/pal/windows/fs.rs +++ b/library/std/src/sys/pal/windows/fs.rs @@ -1,10 +1,10 @@ use super::api::{self, WinError, set_file_information_by_handle}; use super::{IoResult, to_u16s}; -use crate::alloc::{alloc, handle_alloc_error}; +use crate::alloc::{Layout, alloc, dealloc, handle_alloc_error}; use crate::borrow::Cow; use crate::ffi::{OsStr, OsString, c_void}; use crate::io::{self, BorrowedCursor, Error, IoSlice, IoSliceMut, SeekFrom}; -use crate::mem::{self, MaybeUninit}; +use crate::mem::{self, MaybeUninit, offset_of}; use crate::os::windows::io::{AsHandle, BorrowedHandle}; use crate::os::windows::prelude::*; use crate::path::{Path, PathBuf}; @@ -1241,139 +1241,72 @@ pub fn rename(old: &Path, new: &Path) -> io::Result<()> { let old = maybe_verbatim(old)?; let new = maybe_verbatim(new)?; - let new_len_without_nul_in_bytes = (new.len() - 1).try_into().unwrap(); - - // The last field of FILE_RENAME_INFO, the file name, is unsized, - // and FILE_RENAME_INFO has two padding bytes. - // Therefore we need to make sure to not allocate less than - // size_of::() bytes, which would be the case with - // 0 or 1 character paths + a null byte. - let struct_size = size_of::() - .max(mem::offset_of!(c::FILE_RENAME_INFO, FileName) + new.len() * size_of::()); - - let struct_size: u32 = struct_size.try_into().unwrap(); - - let create_file = |extra_access, extra_flags| { - let handle = unsafe { - HandleOrInvalid::from_raw_handle(c::CreateFileW( - old.as_ptr(), - c::SYNCHRONIZE | c::DELETE | extra_access, - c::FILE_SHARE_READ | c::FILE_SHARE_WRITE | c::FILE_SHARE_DELETE, - ptr::null(), - c::OPEN_EXISTING, - c::FILE_ATTRIBUTE_NORMAL | c::FILE_FLAG_BACKUP_SEMANTICS | extra_flags, - ptr::null_mut(), - )) - }; - - OwnedHandle::try_from(handle).map_err(|_| io::Error::last_os_error()) - }; - - // The following code replicates `MoveFileEx`'s behavior as reverse-engineered from its disassembly. - // If `old` refers to a mount point, we move it instead of the target. - let handle = match create_file(c::FILE_READ_ATTRIBUTES, c::FILE_FLAG_OPEN_REPARSE_POINT) { - Ok(handle) => { - let mut file_attribute_tag_info: MaybeUninit = - MaybeUninit::uninit(); - - let result = unsafe { - cvt(c::GetFileInformationByHandleEx( - handle.as_raw_handle(), - c::FileAttributeTagInfo, - file_attribute_tag_info.as_mut_ptr().cast(), - size_of::().try_into().unwrap(), - )) + if unsafe { c::MoveFileExW(old.as_ptr(), new.as_ptr(), c::MOVEFILE_REPLACE_EXISTING) } == 0 { + let err = api::get_last_error(); + // if `MoveFileExW` fails with ERROR_ACCESS_DENIED then try to move + // the file while ignoring the readonly attribute. + // This is accomplished by calling `SetFileInformationByHandle` with `FileRenameInfoEx`. + if err == WinError::ACCESS_DENIED { + let mut opts = OpenOptions::new(); + opts.access_mode(c::DELETE); + opts.custom_flags(c::FILE_FLAG_OPEN_REPARSE_POINT | c::FILE_FLAG_BACKUP_SEMANTICS); + let Ok(f) = File::open_native(&old, &opts) else { return Err(err).io_result() }; + + // Calculate the layout of the `FILE_RENAME_INFO` we pass to `SetFileInformation` + // This is a dynamically sized struct so we need to get the position of the last field to calculate the actual size. + let Ok(new_len_without_nul_in_bytes): Result = ((new.len() - 1) * 2).try_into() + else { + return Err(err).io_result(); }; - - if let Err(err) = result { - if err.raw_os_error() == Some(c::ERROR_INVALID_PARAMETER as _) - || err.raw_os_error() == Some(c::ERROR_INVALID_FUNCTION as _) - { - // `GetFileInformationByHandleEx` documents that not all underlying drivers support all file information classes. - // Since we know we passed the correct arguments, this means the underlying driver didn't understand our request; - // `MoveFileEx` proceeds by reopening the file without inhibiting reparse point behavior. - None - } else { - Some(Err(err)) + let offset: u32 = offset_of!(c::FILE_RENAME_INFO, FileName).try_into().unwrap(); + let struct_size = offset + new_len_without_nul_in_bytes + 2; + let layout = + Layout::from_size_align(struct_size as usize, align_of::()) + .unwrap(); + + // SAFETY: We allocate enough memory for a full FILE_RENAME_INFO struct and a filename. + let file_rename_info; + unsafe { + file_rename_info = alloc(layout).cast::(); + if file_rename_info.is_null() { + handle_alloc_error(layout); } - } else { - // SAFETY: The struct has been initialized by GetFileInformationByHandleEx - let file_attribute_tag_info = unsafe { file_attribute_tag_info.assume_init() }; - let file_type = FileType::new( - file_attribute_tag_info.FileAttributes, - file_attribute_tag_info.ReparseTag, - ); - if file_type.is_symlink() { - // The file is a mount point, junction point or symlink so - // don't reopen the file so that the link gets renamed. - Some(Ok(handle)) - } else { - // Otherwise reopen the file without inhibiting reparse point behavior. - None - } - } - } - // The underlying driver may not support `FILE_FLAG_OPEN_REPARSE_POINT`: Retry without it. - Err(err) if err.raw_os_error() == Some(c::ERROR_INVALID_PARAMETER as _) => None, - Err(err) => Some(Err(err)), - } - .unwrap_or_else(|| create_file(0, 0))?; - - let layout = - core::alloc::Layout::from_size_align(struct_size as _, align_of::()) - .unwrap(); - - let file_rename_info = unsafe { alloc(layout) } as *mut c::FILE_RENAME_INFO; - - if file_rename_info.is_null() { - handle_alloc_error(layout); - } - - // SAFETY: file_rename_info is a non-null pointer pointing to memory allocated by the global allocator. - let mut file_rename_info = unsafe { Box::from_raw(file_rename_info) }; - - // SAFETY: We have allocated enough memory for a full FILE_RENAME_INFO struct and a filename. - unsafe { - (&raw mut (*file_rename_info).Anonymous).write(c::FILE_RENAME_INFO_0 { - Flags: c::FILE_RENAME_FLAG_REPLACE_IF_EXISTS | c::FILE_RENAME_FLAG_POSIX_SEMANTICS, - }); + (&raw mut (*file_rename_info).Anonymous).write(c::FILE_RENAME_INFO_0 { + Flags: c::FILE_RENAME_FLAG_REPLACE_IF_EXISTS + | c::FILE_RENAME_FLAG_POSIX_SEMANTICS, + }); - (&raw mut (*file_rename_info).RootDirectory).write(ptr::null_mut()); - (&raw mut (*file_rename_info).FileNameLength).write(new_len_without_nul_in_bytes); - - new.as_ptr() - .copy_to_nonoverlapping((&raw mut (*file_rename_info).FileName) as *mut u16, new.len()); - } - - // We don't use `set_file_information_by_handle` here as `FILE_RENAME_INFO` is used for both `FileRenameInfo` and `FileRenameInfoEx`. - let result = unsafe { - cvt(c::SetFileInformationByHandle( - handle.as_raw_handle(), - c::FileRenameInfoEx, - (&raw const *file_rename_info).cast::(), - struct_size, - )) - }; + (&raw mut (*file_rename_info).RootDirectory).write(ptr::null_mut()); + // Don't include the NULL in the size + (&raw mut (*file_rename_info).FileNameLength).write(new_len_without_nul_in_bytes); - if let Err(err) = result { - if err.raw_os_error() == Some(c::ERROR_INVALID_PARAMETER as _) { - // FileRenameInfoEx and FILE_RENAME_FLAG_POSIX_SEMANTICS were added in Windows 10 1607; retry with FileRenameInfo. - file_rename_info.Anonymous.ReplaceIfExists = true; + new.as_ptr().copy_to_nonoverlapping( + (&raw mut (*file_rename_info).FileName).cast::(), + new.len(), + ); + } - cvt(unsafe { + let result = unsafe { c::SetFileInformationByHandle( - handle.as_raw_handle(), - c::FileRenameInfo, - (&raw const *file_rename_info).cast::(), + f.as_raw_handle(), + c::FileRenameInfoEx, + file_rename_info.cast::(), struct_size, ) - })?; + }; + unsafe { dealloc(file_rename_info.cast::(), layout) }; + if result == 0 { + if api::get_last_error() == WinError::DIR_NOT_EMPTY { + return Err(WinError::DIR_NOT_EMPTY).io_result(); + } else { + return Err(err).io_result(); + } + } } else { - return Err(err); + return Err(err).io_result(); } } - Ok(()) } From 3cb53df1feaba73b84344c8c0e3dc4120ad8c95b Mon Sep 17 00:00:00 2001 From: Chris Denton Date: Thu, 6 Mar 2025 14:18:18 +0000 Subject: [PATCH 03/16] Return OutOfMemoryError and update docs --- library/std/src/fs.rs | 2 +- library/std/src/sys/pal/windows/fs.rs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/library/std/src/fs.rs b/library/std/src/fs.rs index 4314c8a0b1825..46b5860123fc1 100644 --- a/library/std/src/fs.rs +++ b/library/std/src/fs.rs @@ -2446,7 +2446,7 @@ pub fn symlink_metadata>(path: P) -> io::Result { /// # Platform-specific behavior /// /// This function currently corresponds to the `rename` function on Unix -/// and the `SetFileInformationByHandle` function on Windows. +/// and the `MoveFileExW` or `SetFileInformationByHandle` function on Windows. /// /// Because of this, the behavior when both `from` and `to` exist differs. On /// Unix, if `from` is a directory, `to` must also be an (empty) directory. If diff --git a/library/std/src/sys/pal/windows/fs.rs b/library/std/src/sys/pal/windows/fs.rs index 34108e44d58bd..8fce0496d80c9 100644 --- a/library/std/src/sys/pal/windows/fs.rs +++ b/library/std/src/sys/pal/windows/fs.rs @@ -1,6 +1,6 @@ use super::api::{self, WinError, set_file_information_by_handle}; use super::{IoResult, to_u16s}; -use crate::alloc::{Layout, alloc, dealloc, handle_alloc_error}; +use crate::alloc::{Layout, alloc, dealloc}; use crate::borrow::Cow; use crate::ffi::{OsStr, OsString, c_void}; use crate::io::{self, BorrowedCursor, Error, IoSlice, IoSliceMut, SeekFrom}; @@ -1269,7 +1269,7 @@ pub fn rename(old: &Path, new: &Path) -> io::Result<()> { unsafe { file_rename_info = alloc(layout).cast::(); if file_rename_info.is_null() { - handle_alloc_error(layout); + return Err(io::ErrorKind::OutOfMemory.into()); } (&raw mut (*file_rename_info).Anonymous).write(c::FILE_RENAME_INFO_0 { From d975bd3a67e4f18c02f57d8d66a9d32daa295a47 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Thu, 27 Feb 2025 21:09:45 +0000 Subject: [PATCH 04/16] Remove highlighting of spans on `-Zteach` `-Zteach` is perma-unstable, barely used, the highlighting logic buggy and the flag being passed around is tech-debt. We should likely remove `-Zteach` in its entirely. --- compiler/rustc_errors/src/emitter.rs | 11 ----------- compiler/rustc_session/src/session.rs | 1 - src/librustdoc/core.rs | 1 - 3 files changed, 13 deletions(-) diff --git a/compiler/rustc_errors/src/emitter.rs b/compiler/rustc_errors/src/emitter.rs index f7f842393084b..18846a6dbe103 100644 --- a/compiler/rustc_errors/src/emitter.rs +++ b/compiler/rustc_errors/src/emitter.rs @@ -616,7 +616,6 @@ pub struct HumanEmitter { #[setters(skip)] fallback_bundle: LazyFallbackBundle, short_message: bool, - teach: bool, ui_testing: bool, ignored_directories_in_source_blocks: Vec, diagnostic_width: Option, @@ -642,7 +641,6 @@ impl HumanEmitter { fluent_bundle: None, fallback_bundle, short_message: false, - teach: false, ui_testing: false, ignored_directories_in_source_blocks: Vec::new(), diagnostic_width: None, @@ -1044,15 +1042,6 @@ impl HumanEmitter { underline.style, ); } - _ if self.teach => { - buffer.set_style_range( - line_offset, - (code_offset + annotation.start_col.display).saturating_sub(left), - (code_offset + annotation.end_col.display).saturating_sub(left), - underline.style, - annotation.is_primary, - ); - } _ => {} } } diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs index aa1e9762f397b..bcd9a73d9d30a 100644 --- a/compiler/rustc_session/src/session.rs +++ b/compiler/rustc_session/src/session.rs @@ -930,7 +930,6 @@ fn default_emitter( .fluent_bundle(bundle) .sm(source_map) .short_message(short) - .teach(sopts.unstable_opts.teach) .diagnostic_width(sopts.diagnostic_width) .macro_backtrace(macro_backtrace) .track_diagnostics(track_diagnostics) diff --git a/src/librustdoc/core.rs b/src/librustdoc/core.rs index f95ae380fa8da..719f1f978feca 100644 --- a/src/librustdoc/core.rs +++ b/src/librustdoc/core.rs @@ -160,7 +160,6 @@ pub(crate) fn new_dcx( HumanEmitter::new(stderr_destination(color_config), fallback_bundle) .sm(source_map.map(|sm| sm as _)) .short_message(short) - .teach(unstable_opts.teach) .diagnostic_width(diagnostic_width) .track_diagnostics(unstable_opts.track_diagnostics) .theme(if let HumanReadableErrorType::Unicode = kind { From 72326bfe4033fe51c5cb0f31614bbf6e66ec77f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Thu, 27 Feb 2025 21:11:22 +0000 Subject: [PATCH 05/16] On long spans, trim the middle of them to make them fit in the terminal width When encountering a single line span that is wider than the terminal, we keep context at the start and end of the span but otherwise remove the code from the middle. This is somewhat independent from whether the left and right margins of the output have been trimmed as well. ``` error[E0308]: mismatched types --> $DIR/long-span.rs:6:15 | LL | ... = [0, 0, 0, 0, ..., 0, 0]; | ^^^^^^^^^^^^^...^^^^^^^ expected `u8`, found `[{integer}; 1681]` ``` Address part of #137680 (missing handling of the long suggestion). Fix #125581. --- compiler/rustc_errors/src/emitter.rs | 27 +++++++++++++++++++ compiler/rustc_errors/src/styled_buffer.rs | 10 +++++++ .../ui/diagnostic-width/long-span.long.stderr | 9 +++++++ .../diagnostic-width/long-span.longest.stderr | 9 +++++++ tests/ui/diagnostic-width/long-span.rs | 9 +++++++ .../diagnostic-width/long-span.short.stderr | 9 +++++++ .../long-span.shortest.stderr | 9 +++++++ tests/ui/parser/raw/too-many-hash.stderr | 4 +-- .../rust-2024/reserved-guarded-strings.stderr | 4 +-- 9 files changed, 86 insertions(+), 4 deletions(-) create mode 100644 tests/ui/diagnostic-width/long-span.long.stderr create mode 100644 tests/ui/diagnostic-width/long-span.longest.stderr create mode 100644 tests/ui/diagnostic-width/long-span.rs create mode 100644 tests/ui/diagnostic-width/long-span.short.stderr create mode 100644 tests/ui/diagnostic-width/long-span.shortest.stderr diff --git a/compiler/rustc_errors/src/emitter.rs b/compiler/rustc_errors/src/emitter.rs index 18846a6dbe103..9277bae94ecde 100644 --- a/compiler/rustc_errors/src/emitter.rs +++ b/compiler/rustc_errors/src/emitter.rs @@ -1238,6 +1238,33 @@ impl HumanEmitter { ); } } + + // We look for individual *long* spans, and we trim the *middle*, so that we render + // LL | ...= [0, 0, 0, ..., 0, 0]; + // | ^^^^^^^^^^...^^^^^^^ expected `&[u8]`, found `[{integer}; 1680]` + for &(pos, annotation) in &annotations_position { + let AnnotationType::Singleline = annotation.annotation_type else { continue }; + let width = annotation.end_col.display - annotation.start_col.display; + if pos == 0 && width > margin.column_width && width > 10 { + // If the terminal is *too* small, we keep at least a tiny bit of the span for + // display. + let pad = max(margin.column_width / 2, 5); + // Code line + buffer.replace( + line_offset, + annotation.start_col.file + pad, + annotation.end_col.file - pad, + self.margin(), + ); + // Underline line + buffer.replace( + line_offset + 1, + annotation.start_col.file + pad, + annotation.end_col.file - pad, + self.margin(), + ); + } + } annotations_position .iter() .filter_map(|&(_, annotation)| match annotation.annotation_type { diff --git a/compiler/rustc_errors/src/styled_buffer.rs b/compiler/rustc_errors/src/styled_buffer.rs index 5ca9e9b18f341..b0f4ec84a8995 100644 --- a/compiler/rustc_errors/src/styled_buffer.rs +++ b/compiler/rustc_errors/src/styled_buffer.rs @@ -89,6 +89,16 @@ impl StyledBuffer { } } + pub(crate) fn replace(&mut self, line: usize, start: usize, end: usize, string: &str) { + if start == end { + return; + } + let _ = self.lines[line].drain(start..(end - string.chars().count())); + for (i, c) in string.chars().enumerate() { + self.lines[line][start + i] = StyledChar::new(c, Style::LineNumber); + } + } + /// For given `line` inserts `string` with `style` before old content of that line, /// adding lines if needed pub(crate) fn prepend(&mut self, line: usize, string: &str, style: Style) { diff --git a/tests/ui/diagnostic-width/long-span.long.stderr b/tests/ui/diagnostic-width/long-span.long.stderr new file mode 100644 index 0000000000000..81edde85b33ac --- /dev/null +++ b/tests/ui/diagnostic-width/long-span.long.stderr @@ -0,0 +1,9 @@ +error[E0308]: mismatched types + ╭▸ $DIR/long-span.rs:7:15 + │ +LL │ …u8 = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, …, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]… + ╰╴ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━…━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ expected `u8`, found `[{integer}; 1680]` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/diagnostic-width/long-span.longest.stderr b/tests/ui/diagnostic-width/long-span.longest.stderr new file mode 100644 index 0000000000000..77aafc5f4267c --- /dev/null +++ b/tests/ui/diagnostic-width/long-span.longest.stderr @@ -0,0 +1,9 @@ +error[E0308]: mismatched types + --> $DIR/long-span.rs:7:15 + | +LL | ... = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,... 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ... + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^...^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `u8`, found `[{integer}; 1680]` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/diagnostic-width/long-span.rs b/tests/ui/diagnostic-width/long-span.rs new file mode 100644 index 0000000000000..2feacdd96047a --- /dev/null +++ b/tests/ui/diagnostic-width/long-span.rs @@ -0,0 +1,9 @@ +//@ revisions: shortest short long longest +//@[shortest] compile-flags: --diagnostic-width=4 +//@[short] compile-flags: --diagnostic-width=12 -Zunstable-options --json=diagnostic-unicode +//@[long] compile-flags: --diagnostic-width=80 -Zunstable-options --json=diagnostic-unicode +//@[longest] compile-flags: --diagnostic-width=120 +// ignore-tidy-linelength +const C: u8 = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; +//~^ ERROR E0308 +fn main() {} diff --git a/tests/ui/diagnostic-width/long-span.short.stderr b/tests/ui/diagnostic-width/long-span.short.stderr new file mode 100644 index 0000000000000..8e62acc936ce0 --- /dev/null +++ b/tests/ui/diagnostic-width/long-span.short.stderr @@ -0,0 +1,9 @@ +error[E0308]: mismatched types + ╭▸ $DIR/long-span.rs:7:15 + │ +LL │ …u8 = [0, 0, 0…0]… + ╰╴ ━━━━━━━━…━━ expected `u8`, found `[{integer}; 1680]` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/diagnostic-width/long-span.shortest.stderr b/tests/ui/diagnostic-width/long-span.shortest.stderr new file mode 100644 index 0000000000000..d9cec96ad8fdb --- /dev/null +++ b/tests/ui/diagnostic-width/long-span.shortest.stderr @@ -0,0 +1,9 @@ +error[E0308]: mismatched types + --> $DIR/long-span.rs:7:15 + | +LL | ... = [0, 0, 0...... + | ^^^^^^^^...^^ expected `u8`, found `[{integer}; 1680]` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/parser/raw/too-many-hash.stderr b/tests/ui/parser/raw/too-many-hash.stderr index 1c46b5385cd9c..61fcbcee15952 100644 --- a/tests/ui/parser/raw/too-many-hash.stderr +++ b/tests/ui/parser/raw/too-many-hash.stderr @@ -1,8 +1,8 @@ error: too many `#` symbols: raw strings may be delimited by up to 255 `#` symbols, but found 256 --> $DIR/too-many-hash.rs:4:19 | -LL | ... = r################################################################################################################################################################################################################################################################"very raw"##############################################################################################################################################################################################################################################################... - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | ... = r############################################################################...#############################################################... + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^...^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: aborting due to 1 previous error diff --git a/tests/ui/rust-2024/reserved-guarded-strings.stderr b/tests/ui/rust-2024/reserved-guarded-strings.stderr index 0f3b06147c4fd..5c0f50645467e 100644 --- a/tests/ui/rust-2024/reserved-guarded-strings.stderr +++ b/tests/ui/rust-2024/reserved-guarded-strings.stderr @@ -241,8 +241,8 @@ LL | demo2!(#"foo"## #); error: invalid string literal --> $DIR/reserved-guarded-strings.rs:71:12 | -LL | ...n!(####################################################################################################################################################################################################################################################################"foo... - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | ...n!(######################################################################...#################################################################"foo... + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^...^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: unprefixed guarded string literals are reserved for future use since Rust 2024 help: consider inserting whitespace here From f1c751bc1a08e3439a9d0c482cbb0ea0fc8f644f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Thu, 27 Feb 2025 21:12:58 +0000 Subject: [PATCH 06/16] Refactor `emitter` to better account for unicode chars when trimming Change the way that underline positions are calculated by delaying using the "visual" column position until the last possible moment, instead using the "file"/byte position in the file, and then calculating visual positioning as late as possible. This should make the underlines more resilient to non-1-width unicode chars. Unfortunately, as part of this change (which fixes some visual bugs) comes with the loss of some eager tab codepoint handling, but the output remains legible despite some minor regression on the "margin trimming" logic. --- compiler/rustc_errors/src/emitter.rs | 177 +++---- tests/ui/codemap_tests/tab_2.stderr | 2 +- .../ui/diagnostic-width/long-span.long.stderr | 2 +- .../diagnostic-width/long-span.longest.stderr | 2 +- .../diagnostic-width/long-span.short.stderr | 2 +- .../long-span.shortest.stderr | 2 +- ...width-unicode-multiline-label.ascii.stderr | 59 ++- .../non-1-width-unicode-multiline-label.rs | 6 + ...dth-unicode-multiline-label.unicode.stderr | 59 ++- .../non-whitespace-trimming-unicode.stderr | 8 +- .../ui/diagnostic-width/tabs-trimming.stderr | 20 +- .../multiline-removal-suggestion.svg | 436 +++++++++--------- tests/ui/issues/issue-44078.stderr | 2 +- .../lexer/unterminated-nested-comment.stderr | 2 +- tests/ui/macros/not-utf8.stderr | 2 +- tests/ui/macros/same-sequence-span.stderr | 2 +- tests/ui/parser/byte-string-literals.stderr | 2 +- tests/ui/parser/raw/too-many-hash.stderr | 2 +- tests/ui/parser/unbalanced-doublequote.stderr | 2 +- .../rust-2024/reserved-guarded-strings.stderr | 2 +- 20 files changed, 462 insertions(+), 329 deletions(-) diff --git a/compiler/rustc_errors/src/emitter.rs b/compiler/rustc_errors/src/emitter.rs index 9277bae94ecde..9a3c96776b97c 100644 --- a/compiler/rustc_errors/src/emitter.rs +++ b/compiler/rustc_errors/src/emitter.rs @@ -113,24 +113,11 @@ impl Margin { self.computed_left > 0 } - fn was_cut_right(&self, line_len: usize) -> bool { - let right = - if self.computed_right == self.span_right || self.computed_right == self.label_right { - // FIXME: This comment refers to the only callsite of this method. - // Rephrase it or refactor it, so it can stand on its own. - // Account for the "..." padding given above. Otherwise we end up with code lines - // that do fit but end in "..." as if they were trimmed. - // FIXME: Don't hard-code this offset. Is this meant to represent - // `2 * str_width(self.margin())`? - self.computed_right - 6 - } else { - self.computed_right - }; - right < line_len && self.computed_left + self.column_width < line_len - } - fn compute(&mut self, max_line_len: usize) { // When there's a lot of whitespace (>20), we want to trim it as it is useless. + // FIXME: this doesn't account for '\t', but to do so correctly we need to perform that + // calculation later, right before printing in order to be accurate with both unicode + // handling and trimming of long lines. self.computed_left = if self.whitespace_left > 20 { self.whitespace_left - 16 // We want some padding. } else { @@ -668,43 +655,43 @@ impl HumanEmitter { width_offset: usize, code_offset: usize, margin: Margin, - ) { - // Tabs are assumed to have been replaced by spaces in calling code. - debug_assert!(!source_string.contains('\t')); + ) -> usize { let line_len = source_string.len(); // Create the source line we will highlight. let left = margin.left(line_len); let right = margin.right(line_len); // FIXME: The following code looks fishy. See #132860. // On long lines, we strip the source line, accounting for unicode. - let mut taken = 0; let code: String = source_string .chars() - .skip(left) - .take_while(|ch| { - // Make sure that the trimming on the right will fall within the terminal width. - let next = char_width(*ch); - if taken + next > right - left { - return false; - } - taken += next; - true - }) + .enumerate() + .skip_while(|(i, _)| *i < left) + .take_while(|(i, _)| *i < right) + .map(|(_, c)| c) .collect(); + let code = normalize_whitespace(&code); + let was_cut_right = + source_string.chars().enumerate().skip_while(|(i, _)| *i < right).next().is_some(); buffer.puts(line_offset, code_offset, &code, Style::Quotation); let placeholder = self.margin(); if margin.was_cut_left() { // We have stripped some code/whitespace from the beginning, make it clear. buffer.puts(line_offset, code_offset, placeholder, Style::LineNumber); } - if margin.was_cut_right(line_len) { + if was_cut_right { let padding = str_width(placeholder); // We have stripped some code after the rightmost span end, make it clear we did so. - buffer.puts(line_offset, code_offset + taken - padding, placeholder, Style::LineNumber); + buffer.puts( + line_offset, + code_offset + str_width(&code) - padding, + placeholder, + Style::LineNumber, + ); } buffer.puts(line_offset, 0, &self.maybe_anonymized(line_index), Style::LineNumber); self.draw_col_separator_no_space(buffer, line_offset, width_offset - 2); + left } #[instrument(level = "trace", skip(self), ret)] @@ -736,22 +723,16 @@ impl HumanEmitter { return Vec::new(); } - let source_string = match file.get_line(line.line_index - 1) { - Some(s) => normalize_whitespace(&s), - None => return Vec::new(), + let Some(source_string) = file.get_line(line.line_index - 1) else { + return Vec::new(); }; trace!(?source_string); let line_offset = buffer.num_lines(); - // Left trim - let left = margin.left(source_string.len()); - + // Left trim. // FIXME: This looks fishy. See #132860. - // Account for unicode characters of width !=0 that were removed. - let left = source_string.chars().take(left).map(|ch| char_width(ch)).sum(); - - self.draw_line( + let left = self.draw_line( buffer, &source_string, line.line_index, @@ -1033,12 +1014,18 @@ impl HumanEmitter { let pos = pos + 1; match annotation.annotation_type { AnnotationType::MultilineStart(depth) | AnnotationType::MultilineEnd(depth) => { + let pre: usize = source_string + .chars() + .take(annotation.start_col.file) + .skip(left) + .map(|c| char_width(c)) + .sum(); self.draw_range( buffer, underline.multiline_horizontal, line_offset + pos, width_offset + depth, - (code_offset + annotation.start_col.display).saturating_sub(left), + code_offset + pre, underline.style, ); } @@ -1061,11 +1048,18 @@ impl HumanEmitter { let underline = self.underline(annotation.is_primary); let pos = pos + 1; + let code_offset = code_offset + + source_string + .chars() + .take(annotation.start_col.file) + .skip(left) + .map(|c| char_width(c)) + .sum::(); if pos > 1 && (annotation.has_label() || annotation.takes_space()) { for p in line_offset + 1..=line_offset + pos { buffer.putc( p, - (code_offset + annotation.start_col.display).saturating_sub(left), + code_offset, match annotation.annotation_type { AnnotationType::MultilineLine(_) => underline.multiline_vertical, _ => underline.vertical_text_line, @@ -1076,7 +1070,7 @@ impl HumanEmitter { if let AnnotationType::MultilineStart(_) = annotation.annotation_type { buffer.putc( line_offset + pos, - (code_offset + annotation.start_col.display).saturating_sub(left), + code_offset, underline.bottom_right, underline.style, ); @@ -1086,7 +1080,7 @@ impl HumanEmitter { { buffer.putc( line_offset + pos, - (code_offset + annotation.start_col.display).saturating_sub(left), + code_offset, underline.multiline_bottom_right_with_text, underline.style, ); @@ -1144,13 +1138,30 @@ impl HumanEmitter { let style = if annotation.is_primary { Style::LabelPrimary } else { Style::LabelSecondary }; let (pos, col) = if pos == 0 { - if annotation.end_col.display == 0 { - (pos + 1, (annotation.end_col.display + 2).saturating_sub(left)) + let pre: usize = source_string + .chars() + .take(annotation.end_col.file) + .skip(left) + .map(|c| char_width(c)) + .sum(); + if annotation.end_col.file == 0 { + (pos + 1, (pre + 2)) } else { - (pos + 1, (annotation.end_col.display + 1).saturating_sub(left)) + let pad = if annotation.end_col.file - annotation.start_col.file == 0 { + 2 + } else { + 1 + }; + (pos + 1, (pre + pad)) } } else { - (pos + 2, annotation.start_col.display.saturating_sub(left)) + let pre: usize = source_string + .chars() + .take(annotation.start_col.file) + .skip(left) + .map(|c| char_width(c)) + .sum(); + (pos + 2, pre) }; if let Some(ref label) = annotation.label { buffer.puts(line_offset + pos, code_offset + col, label, style); @@ -1183,14 +1194,35 @@ impl HumanEmitter { // | _^ test for &(pos, annotation) in &annotations_position { let uline = self.underline(annotation.is_primary); - for p in annotation.start_col.display..annotation.end_col.display { + let width = annotation.end_col.file - annotation.start_col.file; + let previous: String = + source_string.chars().take(annotation.start_col.file).skip(left).collect(); + let underlined: String = + source_string.chars().skip(annotation.start_col.file).take(width).collect(); + debug!(?previous, ?underlined); + let code_offset = code_offset + + source_string + .chars() + .take(annotation.start_col.file) + .skip(left) + .map(|c| char_width(c)) + .sum::(); + let ann_width: usize = source_string + .chars() + .skip(annotation.start_col.file) + .take(width) + .map(|c| char_width(c)) + .sum(); + let ann_width = if ann_width == 0 + && matches!(annotation.annotation_type, AnnotationType::Singleline) + { + 1 + } else { + ann_width + }; + for p in 0..ann_width { // The default span label underline. - buffer.putc( - line_offset + 1, - (code_offset + p).saturating_sub(left), - uline.underline, - uline.style, - ); + buffer.putc(line_offset + 1, code_offset + p, uline.underline, uline.style); } if pos == 0 @@ -1202,7 +1234,7 @@ impl HumanEmitter { // The beginning of a multiline span with its leftward moving line on the same line. buffer.putc( line_offset + 1, - (code_offset + annotation.start_col.display).saturating_sub(left), + code_offset, match annotation.annotation_type { AnnotationType::MultilineStart(_) => uline.top_right_flat, AnnotationType::MultilineEnd(_) => uline.multiline_end_same_line, @@ -1220,7 +1252,7 @@ impl HumanEmitter { // so we start going down first. buffer.putc( line_offset + 1, - (code_offset + annotation.start_col.display).saturating_sub(left), + code_offset, match annotation.annotation_type { AnnotationType::MultilineStart(_) => uline.multiline_start_down, AnnotationType::MultilineEnd(_) => uline.multiline_end_up, @@ -1230,12 +1262,7 @@ impl HumanEmitter { ); } else if pos != 0 && annotation.has_label() { // The beginning of a span label with an actual label, we'll point down. - buffer.putc( - line_offset + 1, - (code_offset + annotation.start_col.display).saturating_sub(left), - uline.label_start, - uline.style, - ); + buffer.putc(line_offset + 1, code_offset, uline.label_start, uline.style); } } @@ -1718,17 +1745,11 @@ impl HumanEmitter { // non-rustc_lexer::is_whitespace() chars are reported as an // error (ex. no-break-spaces \u{a0}), and thus can't be considered // for removal during error reporting. + // FIXME: doesn't account for '\t' properly. let leading_whitespace = source_string .chars() .take_while(|c| rustc_lexer::is_whitespace(*c)) - .map(|c| { - match c { - // Tabs are displayed as 4 spaces - '\t' => 4, - _ => 1, - } - }) - .sum(); + .count(); if source_string.chars().any(|c| !rustc_lexer::is_whitespace(c)) { whitespace_margin = min(whitespace_margin, leading_whitespace); } @@ -1742,8 +1763,8 @@ impl HumanEmitter { let mut span_left_margin = usize::MAX; for line in &annotated_file.lines { for ann in &line.annotations { - span_left_margin = min(span_left_margin, ann.start_col.display); - span_left_margin = min(span_left_margin, ann.end_col.display); + span_left_margin = min(span_left_margin, ann.start_col.file); + span_left_margin = min(span_left_margin, ann.end_col.file); } } if span_left_margin == usize::MAX { @@ -1763,12 +1784,12 @@ impl HumanEmitter { .map_or(0, |s| s.len()), ); for ann in &line.annotations { - span_right_margin = max(span_right_margin, ann.start_col.display); - span_right_margin = max(span_right_margin, ann.end_col.display); + span_right_margin = max(span_right_margin, ann.start_col.file); + span_right_margin = max(span_right_margin, ann.end_col.file); // FIXME: account for labels not in the same line let label_right = ann.label.as_ref().map_or(0, |l| l.len() + 1); label_right_margin = - max(label_right_margin, ann.end_col.display + label_right); + max(label_right_margin, ann.end_col.file + label_right); } } diff --git a/tests/ui/codemap_tests/tab_2.stderr b/tests/ui/codemap_tests/tab_2.stderr index b22c7b4266517..4f9a937155dde 100644 --- a/tests/ui/codemap_tests/tab_2.stderr +++ b/tests/ui/codemap_tests/tab_2.stderr @@ -4,7 +4,7 @@ error[E0765]: unterminated double quote string LL | """; | ___________________^ LL | | } - | |_^ + | |__^ error: aborting due to 1 previous error diff --git a/tests/ui/diagnostic-width/long-span.long.stderr b/tests/ui/diagnostic-width/long-span.long.stderr index 81edde85b33ac..e39f4000d3cdc 100644 --- a/tests/ui/diagnostic-width/long-span.long.stderr +++ b/tests/ui/diagnostic-width/long-span.long.stderr @@ -1,7 +1,7 @@ error[E0308]: mismatched types ╭▸ $DIR/long-span.rs:7:15 │ -LL │ …u8 = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, …, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]… +LL │ …u8 = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, …, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; ╰╴ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━…━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ expected `u8`, found `[{integer}; 1680]` error: aborting due to 1 previous error diff --git a/tests/ui/diagnostic-width/long-span.longest.stderr b/tests/ui/diagnostic-width/long-span.longest.stderr index 77aafc5f4267c..8e2bad93692de 100644 --- a/tests/ui/diagnostic-width/long-span.longest.stderr +++ b/tests/ui/diagnostic-width/long-span.longest.stderr @@ -1,7 +1,7 @@ error[E0308]: mismatched types --> $DIR/long-span.rs:7:15 | -LL | ... = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,... 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ... +LL | ... = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,... 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^...^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `u8`, found `[{integer}; 1680]` error: aborting due to 1 previous error diff --git a/tests/ui/diagnostic-width/long-span.short.stderr b/tests/ui/diagnostic-width/long-span.short.stderr index 8e62acc936ce0..73ee895a89b63 100644 --- a/tests/ui/diagnostic-width/long-span.short.stderr +++ b/tests/ui/diagnostic-width/long-span.short.stderr @@ -1,7 +1,7 @@ error[E0308]: mismatched types ╭▸ $DIR/long-span.rs:7:15 │ -LL │ …u8 = [0, 0, 0…0]… +LL │ …u8 = [0, 0, 0…0]; ╰╴ ━━━━━━━━…━━ expected `u8`, found `[{integer}; 1680]` error: aborting due to 1 previous error diff --git a/tests/ui/diagnostic-width/long-span.shortest.stderr b/tests/ui/diagnostic-width/long-span.shortest.stderr index d9cec96ad8fdb..2b485e7554135 100644 --- a/tests/ui/diagnostic-width/long-span.shortest.stderr +++ b/tests/ui/diagnostic-width/long-span.shortest.stderr @@ -1,7 +1,7 @@ error[E0308]: mismatched types --> $DIR/long-span.rs:7:15 | -LL | ... = [0, 0, 0...... +LL | ... = [0, 0, 0...0]; | ^^^^^^^^...^^ expected `u8`, found `[{integer}; 1680]` error: aborting due to 1 previous error diff --git a/tests/ui/diagnostic-width/non-1-width-unicode-multiline-label.ascii.stderr b/tests/ui/diagnostic-width/non-1-width-unicode-multiline-label.ascii.stderr index 4d8afb6f3ad9b..60ce0d9a14839 100644 --- a/tests/ui/diagnostic-width/non-1-width-unicode-multiline-label.ascii.stderr +++ b/tests/ui/diagnostic-width/non-1-width-unicode-multiline-label.ascii.stderr @@ -1,11 +1,41 @@ error[E0369]: cannot add `&str` to `&str` - --> $DIR/non-1-width-unicode-multiline-label.rs:7:260 + --> $DIR/non-1-width-unicode-multiline-label.rs:7:237 | -LL | ...ཽཾཿ྄ཱྀྀྂྃ྅྆྇ྈྉྊྋྌྍྎྏྐྑྒྒྷྔྕྖྗ྘ྙྚྛྜྜྷྞྟྠྡྡྷྣྤྥྦྦྷྨྩྪྫྫྷྭྮྯྰྱྲླྴྵྶྷྸྐྵྺྻྼ྽྾྿࿀࿁࿂࿃࿄࿅࿆࿇...࿋࿌࿍࿎࿏࿐࿑࿒࿓࿔࿕࿖࿗࿘࿙࿚"; let _a = unicode_is_fun + " really fun!"; - | -------------- ^ -------------- &str - | | | - | | `+` cannot be used to concatenate two `&str` strings - | &str +LL | ...👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦"; let _a = unicode_is_fun + " really fun!"; + | -------------- ^ -------------- &str + | | | + | | `+` cannot be used to concatenate two `&str` strings + | &str + | + = note: string concatenation requires an owned `String` on the left +help: create an owned `String` from a string reference + | +LL | let _ = "👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦"; let _a = unicode_is_fun.to_owned() + " really fun!"; + | +++++++++++ + +error[E0369]: cannot add `&str` to `&str` + --> $DIR/non-1-width-unicode-multiline-label.rs:9:384 + | +LL | ...👧👦👨👩👧👦👨👩👧👦"; let _a = unicode_is_fun + " really fun!"; + | -------------- ^ -------------- &str + | | | + | | `+` cannot be used to concatenate two `&str` strings + | &str + | + = note: string concatenation requires an owned `String` on the left +help: create an owned `String` from a string reference + | +LL | let _ = "👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦"; let _a = unicode_is_fun.to_owned() + " really fun!"; + | +++++++++++ + +error[E0369]: cannot add `&str` to `&str` + --> $DIR/non-1-width-unicode-multiline-label.rs:11:260 + | +LL | ...࿇࿈࿉࿊࿋࿌࿍࿎࿏࿐࿑࿒࿓࿔࿕࿖࿗࿘࿙࿚"; let _a = unicode_is_fun + " really fun!"; + | -------------- ^ -------------- &str + | | | + | | `+` cannot be used to concatenate two `&str` strings + | &str | = note: string concatenation requires an owned `String` on the left help: create an owned `String` from a string reference @@ -13,6 +43,21 @@ help: create an owned `String` from a string reference LL | let _ = "ༀ༁༂༃༄༅༆༇༈༉༊་༌།༎༏༐༑༒༓༔༕༖༗༘༙༚༛༜༝༞༟༠༡༢༣༤༥༦༧༨༩༪༫༬༭༮༯༰༱༲༳༴༵༶༷༸༹༺༻༼༽༾༿ཀཁགགྷངཅཆཇ཈ཉཊཋཌཌྷཎཏཐདདྷནཔཕབབྷམཙཚཛཛྷཝཞཟའཡརལཤཥསཧཨཀྵཪཫཬ཭཮཯཰ཱཱཱིིུུྲྀཷླྀཹེཻོཽཾཿ྄ཱྀྀྂྃ྅྆྇ྈྉྊྋྌྍྎྏྐྑྒྒྷྔྕྖྗ྘ྙྚྛྜྜྷྞྟྠྡྡྷྣྤྥྦྦྷྨྩྪྫྫྷྭྮྯྰྱྲླྴྵྶྷྸྐྵྺྻྼ྽྾྿࿀࿁࿂࿃࿄࿅࿆࿇࿈࿉࿊࿋࿌࿍࿎࿏࿐࿑࿒࿓࿔࿕࿖࿗࿘࿙࿚"; let _a = unicode_is_fun.to_owned() + " really fun!"; | +++++++++++ -error: aborting due to 1 previous error +error[E0369]: cannot add `&str` to `&str` + --> $DIR/non-1-width-unicode-multiline-label.rs:13:219 + | +LL | ...xxxxxxxxxxxxxxxxxxxx"; let _a = unicode_is_fun + " really fun!"; + | -------------- ^ -------------- &str + | | | + | | `+` cannot be used to concatenate two `&str` strings + | &str + | + = note: string concatenation requires an owned `String` on the left +help: create an owned `String` from a string reference + | +LL | let _ = "xxxxxxx👨👩👧👦xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx👨xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"; let _a = unicode_is_fun.to_owned() + " really fun!"; + | +++++++++++ + +error: aborting due to 4 previous errors For more information about this error, try `rustc --explain E0369`. diff --git a/tests/ui/diagnostic-width/non-1-width-unicode-multiline-label.rs b/tests/ui/diagnostic-width/non-1-width-unicode-multiline-label.rs index e630db8ba42a2..6b9b27f6297fc 100644 --- a/tests/ui/diagnostic-width/non-1-width-unicode-multiline-label.rs +++ b/tests/ui/diagnostic-width/non-1-width-unicode-multiline-label.rs @@ -4,6 +4,12 @@ fn main() { let unicode_is_fun = "؁‱ஹ௸௵꧄.ဪ꧅⸻𒈙𒐫﷽𒌄𒈟𒍼𒁎𒀱𒌧𒅃 𒈓𒍙𒊎𒄡𒅌𒁏𒀰𒐪𒐩𒈙𒐫𪚥"; + let _ = "👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦"; let _a = unicode_is_fun + " really fun!"; + //[ascii]~^ ERROR cannot add `&str` to `&str` + let _ = "👨‍👩‍👧‍👦👨‍👩‍👧‍👦👨‍👩‍👧‍👦👨‍👩‍👧‍👦👨‍👩‍👧‍👦👨‍👩‍👧‍👦👨‍👩‍👧‍👦👨‍👩‍👧‍👦👨‍👩‍👧‍👦👨‍👩‍👧‍👦👨‍👩‍👧‍👦👨‍👩‍👧‍👦👨‍👩‍👧‍👦👨‍👩‍👧‍👦👨‍👩‍👧‍👦👨‍👩‍👧‍👦👨‍👩‍👧‍👦👨‍👩‍👧‍👦👨‍👩‍👧‍👦👨‍👩‍👧‍👦👨‍👩‍👧‍👦👨‍👩‍👧‍👦👨‍👩‍👧‍👦👨‍👩‍👧‍👦👨‍👩‍👧‍👦👨‍👩‍👧‍👦👨‍👩‍👧‍👦👨‍👩‍👧‍👦👨‍👩‍👧‍👦👨‍👩‍👧‍👦👨‍👩‍👧‍👦👨‍👩‍👧‍👦👨‍👩‍👧‍👦👨‍👩‍👧‍👦👨‍👩‍👧‍👦👨‍👩‍👧‍👦👨‍👩‍👧‍👦👨‍👩‍👧‍👦👨‍👩‍👧‍👦👨‍👩‍👧‍👦👨‍👩‍👧‍👦👨‍👩‍👧‍👦👨‍👩‍👧‍👦👨‍👩‍👧‍👦👨‍👩‍👧‍👦👨‍👩‍👧‍👦👨‍👩‍👧‍👦👨‍👩‍👧‍👦👨‍👩‍👧‍👦"; let _a = unicode_is_fun + " really fun!"; + //[ascii]~^ ERROR cannot add `&str` to `&str` let _ = "ༀ༁༂༃༄༅༆༇༈༉༊་༌།༎༏༐༑༒༓༔༕༖༗༘༙༚༛༜༝༞༟༠༡༢༣༤༥༦༧༨༩༪༫༬༭༮༯༰༱༲༳༴༵༶༷༸༹༺༻༼༽༾༿ཀཁགགྷངཅཆཇ཈ཉཊཋཌཌྷཎཏཐདདྷནཔཕབབྷམཙཚཛཛྷཝཞཟའཡརལཤཥསཧཨཀྵཪཫཬ཭཮཯཰ཱཱཱིིུུྲྀཷླྀཹེཻོཽཾཿ྄ཱྀྀྂྃ྅྆྇ྈྉྊྋྌྍྎྏྐྑྒྒྷྔྕྖྗ྘ྙྚྛྜྜྷྞྟྠྡྡྷྣྤྥྦྦྷྨྩྪྫྫྷྭྮྯྰྱྲླྴྵྶྷྸྐྵྺྻྼ྽྾྿࿀࿁࿂࿃࿄࿅࿆࿇࿈࿉࿊࿋࿌࿍࿎࿏࿐࿑࿒࿓࿔࿕࿖࿗࿘࿙࿚"; let _a = unicode_is_fun + " really fun!"; //[ascii]~^ ERROR cannot add `&str` to `&str` + let _ = "xxxxxxx👨‍👩‍👧‍👦xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx👨xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"; let _a = unicode_is_fun + " really fun!"; + //[ascii]~^ ERROR cannot add `&str` to `&str` } diff --git a/tests/ui/diagnostic-width/non-1-width-unicode-multiline-label.unicode.stderr b/tests/ui/diagnostic-width/non-1-width-unicode-multiline-label.unicode.stderr index ed8ce770bb7ea..15b5dd9d7e2d7 100644 --- a/tests/ui/diagnostic-width/non-1-width-unicode-multiline-label.unicode.stderr +++ b/tests/ui/diagnostic-width/non-1-width-unicode-multiline-label.unicode.stderr @@ -1,11 +1,41 @@ error[E0369]: cannot add `&str` to `&str` - ╭▸ $DIR/non-1-width-unicode-multiline-label.rs:7:260 + ╭▸ $DIR/non-1-width-unicode-multiline-label.rs:7:237 │ -LL │ …ཻོཽཾཿ྄ཱྀྀྂྃ྅྆྇ྈྉྊྋྌྍྎྏྐྑྒྒྷྔྕྖྗ྘ྙྚྛྜྜྷྞྟྠྡྡྷྣྤྥྦྦྷྨྩྪྫྫྷྭྮྯྰྱྲླྴྵྶྷྸྐྵྺྻྼ྽྾྿࿀࿁࿂࿃࿄࿅࿆࿇࿈࿉…࿋࿌࿍࿎࿏࿐࿑࿒࿓࿔࿕࿖࿗࿘࿙࿚"; let _a = unicode_is_fun + " really fun!"; - │ ┬───────────── ┯ ────────────── &str - │ │ │ - │ │ `+` cannot be used to concatenate two `&str` strings - │ &str +LL │ …👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦"; let _a = unicode_is_fun + " really fun!"; + │ ┬───────────── ┯ ────────────── &str + │ │ │ + │ │ `+` cannot be used to concatenate two `&str` strings + │ &str + │ + ╰ note: string concatenation requires an owned `String` on the left +help: create an owned `String` from a string reference + ╭╴ +LL │ let _ = "👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦"; let _a = unicode_is_fun.to_owned() + " really fun!"; + ╰╴ +++++++++++ + +error[E0369]: cannot add `&str` to `&str` + ╭▸ $DIR/non-1-width-unicode-multiline-label.rs:9:384 + │ +LL │ …👨👩👧👦👨👩👧👦👨👩👧👦"; let _a = unicode_is_fun + " really fun!"; + │ ┬───────────── ┯ ────────────── &str + │ │ │ + │ │ `+` cannot be used to concatenate two `&str` strings + │ &str + │ + ╰ note: string concatenation requires an owned `String` on the left +help: create an owned `String` from a string reference + ╭╴ +LL │ let _ = "👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦👨👩👧👦"; let _a = unicode_is_fun.to_owned() + " really fun!"; + ╰╴ +++++++++++ + +error[E0369]: cannot add `&str` to `&str` + ╭▸ $DIR/non-1-width-unicode-multiline-label.rs:11:260 + │ +LL │ …࿅࿆࿇࿈࿉࿊࿋࿌࿍࿎࿏࿐࿑࿒࿓࿔࿕࿖࿗࿘࿙࿚"; let _a = unicode_is_fun + " really fun!"; + │ ┬───────────── ┯ ────────────── &str + │ │ │ + │ │ `+` cannot be used to concatenate two `&str` strings + │ &str │ ╰ note: string concatenation requires an owned `String` on the left help: create an owned `String` from a string reference @@ -13,6 +43,21 @@ help: create an owned `String` from a string reference LL │ let _ = "ༀ༁༂༃༄༅༆༇༈༉༊་༌།༎༏༐༑༒༓༔༕༖༗༘༙༚༛༜༝༞༟༠༡༢༣༤༥༦༧༨༩༪༫༬༭༮༯༰༱༲༳༴༵༶༷༸༹༺༻༼༽༾༿ཀཁགགྷངཅཆཇ཈ཉཊཋཌཌྷཎཏཐདདྷནཔཕབབྷམཙཚཛཛྷཝཞཟའཡརལཤཥསཧཨཀྵཪཫཬ཭཮཯཰ཱཱཱིིུུྲྀཷླྀཹེཻོཽཾཿ྄ཱྀྀྂྃ྅྆྇ྈྉྊྋྌྍྎྏྐྑྒྒྷྔྕྖྗ྘ྙྚྛྜྜྷྞྟྠྡྡྷྣྤྥྦྦྷྨྩྪྫྫྷྭྮྯྰྱྲླྴྵྶྷྸྐྵྺྻྼ྽྾྿࿀࿁࿂࿃࿄࿅࿆࿇࿈࿉࿊࿋࿌࿍࿎࿏࿐࿑࿒࿓࿔࿕࿖࿗࿘࿙࿚"; let _a = unicode_is_fun.to_owned() + " really fun!"; ╰╴ +++++++++++ -error: aborting due to 1 previous error +error[E0369]: cannot add `&str` to `&str` + ╭▸ $DIR/non-1-width-unicode-multiline-label.rs:13:219 + │ +LL │ …xxxxxxxxxxxxxxxxxxxxxx"; let _a = unicode_is_fun + " really fun!"; + │ ┬───────────── ┯ ────────────── &str + │ │ │ + │ │ `+` cannot be used to concatenate two `&str` strings + │ &str + │ + ╰ note: string concatenation requires an owned `String` on the left +help: create an owned `String` from a string reference + ╭╴ +LL │ let _ = "xxxxxxx👨👩👧👦xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx👨xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"; let _a = unicode_is_fun.to_owned() + " really fun!"; + ╰╴ +++++++++++ + +error: aborting due to 4 previous errors For more information about this error, try `rustc --explain E0369`. diff --git a/tests/ui/diagnostic-width/non-whitespace-trimming-unicode.stderr b/tests/ui/diagnostic-width/non-whitespace-trimming-unicode.stderr index da3d8d318922e..5408825d8cd6d 100644 --- a/tests/ui/diagnostic-width/non-whitespace-trimming-unicode.stderr +++ b/tests/ui/diagnostic-width/non-whitespace-trimming-unicode.stderr @@ -1,10 +1,10 @@ error[E0308]: mismatched types --> $DIR/non-whitespace-trimming-unicode.rs:4:415 | -LL | ...♭♮♯♰♱♲♳♴♵♶♷♸♹♺♻♼♽♾♿⚀⚁⚂⚃⚄⚅⚆⚈⚉4"; let _: () = 42; let _: &str = "🦀☀☁☂☃☄★☆☇☈☉☊☋☌☍☎☏☐☑☒☓ ☖☗☘☙☚☛☜☝☞☟☠☡☢☣☤☥☦☧☨☩☪☫☬☭☮☯☰☱☲☳☴☵☶☷☸☹☺☻☼☽☾☿♀♁♂♃♄... - | -- ^^ expected `()`, found integer - | | - | expected due to this +LL | ...♣♤♥♦♧♨♩♪♫♬♭♮♯♰♱♲♳♴♵♶♷♸♹♺♻♼♽♾♿⚀⚁⚂⚃⚄⚅⚆⚈⚉4"; let _: () = 42; let _: &str = "🦀☀☁☂☃☄★☆☇☈☉☊☋☌☍☎☏☐☑☒☓ ☖☗☘☙☚☛☜☝☞☟☠☡☢☣☤☥☦☧☨☩☪☫☬☭☮☯☰☱☲☳☴☵☶☷☸☹☺☻☼... + | -- ^^ expected `()`, found integer + | | + | expected due to this error: aborting due to 1 previous error diff --git a/tests/ui/diagnostic-width/tabs-trimming.stderr b/tests/ui/diagnostic-width/tabs-trimming.stderr index 85103fbf6f591..a896345bd7021 100644 --- a/tests/ui/diagnostic-width/tabs-trimming.stderr +++ b/tests/ui/diagnostic-width/tabs-trimming.stderr @@ -1,20 +1,20 @@ error[E0408]: variable `v` is not bound in all patterns --> $DIR/tabs-trimming.rs:9:16 | -LL | ... v @ 1 | 2 | 3 => panic!("You gave me too little money {}", v), // Long text here: TTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTT... - | - ^ ^ pattern doesn't bind `v` - | | | - | | pattern doesn't bind `v` - | variable not in all patterns +LL | ... v @ 1 | 2 | 3 => panic!("You gave me too little money {}", v), // Long text here: TTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTT... + | - ^ ^ pattern doesn't bind `v` + | | | + | | pattern doesn't bind `v` + | variable not in all patterns error[E0381]: used binding `v` is possibly-uninitialized --> $DIR/tabs-trimming.rs:9:67 | -LL | ... v @ 1 | 2 | 3 => panic!("You gave me too little money {}", v), // Long text here: TTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTT... - | - ^ `v` used here but it is possibly-uninitialized - | | - | binding initialized here in some conditions - | binding declared here but left uninitialized +LL | ... v @ 1 | 2 | 3 => panic!("You gave me too little money {}", v), // Long text here: TTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTT... + | - ^ `v` used here but it is possibly-uninitialized + | | + | binding initialized here in some conditions + | binding declared here but left uninitialized | = note: this error originates in the macro `$crate::const_format_args` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/ui/error-emitter/multiline-removal-suggestion.svg b/tests/ui/error-emitter/multiline-removal-suggestion.svg index 95c7740f6995d..0820baaff09ec 100644 --- a/tests/ui/error-emitter/multiline-removal-suggestion.svg +++ b/tests/ui/error-emitter/multiline-removal-suggestion.svg @@ -1,4 +1,4 @@ - + AsVecIntoIter for IntoIter { type Item = I; diff --git a/library/alloc/src/collections/mod.rs b/library/alloc/src/collections/mod.rs index 020cf4d736510..1414f797e8a45 100644 --- a/library/alloc/src/collections/mod.rs +++ b/library/alloc/src/collections/mod.rs @@ -24,41 +24,54 @@ pub mod btree_map { pub mod btree_set { //! An ordered set based on a B-Tree. #[stable(feature = "rust1", since = "1.0.0")] + #[cfg(not(test))] pub use super::btree::set::*; } +#[cfg(not(test))] use core::fmt::Display; #[cfg(not(no_global_oom_handling))] #[stable(feature = "rust1", since = "1.0.0")] #[doc(no_inline)] +#[cfg(not(test))] pub use binary_heap::BinaryHeap; #[cfg(not(no_global_oom_handling))] #[stable(feature = "rust1", since = "1.0.0")] #[doc(no_inline)] +#[cfg(not(test))] pub use btree_map::BTreeMap; #[cfg(not(no_global_oom_handling))] #[stable(feature = "rust1", since = "1.0.0")] #[doc(no_inline)] +#[cfg(not(test))] pub use btree_set::BTreeSet; #[cfg(not(no_global_oom_handling))] #[stable(feature = "rust1", since = "1.0.0")] #[doc(no_inline)] +#[cfg(not(test))] pub use linked_list::LinkedList; #[cfg(not(no_global_oom_handling))] #[stable(feature = "rust1", since = "1.0.0")] #[doc(no_inline)] +#[cfg(not(test))] pub use vec_deque::VecDeque; +#[cfg(not(test))] use crate::alloc::{Layout, LayoutError}; /// The error type for `try_reserve` methods. #[derive(Clone, PartialEq, Eq, Debug)] #[stable(feature = "try_reserve", since = "1.57.0")] +#[cfg(not(test))] pub struct TryReserveError { kind: TryReserveErrorKind, } +#[cfg(test)] +pub use realalloc::collections::TryReserveError; + +#[cfg(not(test))] impl TryReserveError { /// Details about the allocation that caused the error #[inline] @@ -80,6 +93,7 @@ impl TryReserveError { reason = "Uncertain how much info should be exposed", issue = "48043" )] +#[cfg(not(test))] pub enum TryReserveErrorKind { /// Error due to the computed capacity exceeding the collection's maximum /// (usually `isize::MAX` bytes). @@ -103,11 +117,15 @@ pub enum TryReserveErrorKind { }, } +#[cfg(test)] +pub use realalloc::collections::TryReserveErrorKind; + #[unstable( feature = "try_reserve_kind", reason = "Uncertain how much info should be exposed", issue = "48043" )] +#[cfg(not(test))] impl From for TryReserveError { #[inline] fn from(kind: TryReserveErrorKind) -> Self { @@ -116,6 +134,7 @@ impl From for TryReserveError { } #[unstable(feature = "try_reserve_kind", reason = "new API", issue = "48043")] +#[cfg(not(test))] impl From for TryReserveErrorKind { /// Always evaluates to [`TryReserveErrorKind::CapacityOverflow`]. #[inline] @@ -125,6 +144,7 @@ impl From for TryReserveErrorKind { } #[stable(feature = "try_reserve", since = "1.57.0")] +#[cfg(not(test))] impl Display for TryReserveError { fn fmt( &self, @@ -152,4 +172,5 @@ trait SpecExtend { } #[stable(feature = "try_reserve", since = "1.57.0")] +#[cfg(not(test))] impl core::error::Error for TryReserveError {} diff --git a/library/alloc/src/collections/vec_deque/mod.rs b/library/alloc/src/collections/vec_deque/mod.rs index 299c8b8679e3d..f8844e2d3a5cb 100644 --- a/library/alloc/src/collections/vec_deque/mod.rs +++ b/library/alloc/src/collections/vec_deque/mod.rs @@ -645,6 +645,7 @@ impl VecDeque { /// initialized rather than only supporting `0..len`. Requires that /// `initialized.start` ≤ `initialized.end` ≤ `capacity`. #[inline] + #[cfg(not(test))] pub(crate) unsafe fn from_contiguous_raw_parts_in( ptr: *mut T, initialized: Range, diff --git a/library/alloc/src/collections/vec_deque/spec_extend.rs b/library/alloc/src/collections/vec_deque/spec_extend.rs index d246385ca8419..7c7072c4c3a1b 100644 --- a/library/alloc/src/collections/vec_deque/spec_extend.rs +++ b/library/alloc/src/collections/vec_deque/spec_extend.rs @@ -3,6 +3,7 @@ use core::slice; use super::VecDeque; use crate::alloc::Allocator; +#[cfg(not(test))] use crate::vec; // Specialization trait used for VecDeque::extend @@ -78,6 +79,7 @@ where } } +#[cfg(not(test))] impl SpecExtend> for VecDeque { #[track_caller] fn spec_extend(&mut self, mut iterator: vec::IntoIter) { diff --git a/library/alloc/src/collections/vec_deque/spec_from_iter.rs b/library/alloc/src/collections/vec_deque/spec_from_iter.rs index 1efe84d6d7d7d..c80a30c2103dd 100644 --- a/library/alloc/src/collections/vec_deque/spec_from_iter.rs +++ b/library/alloc/src/collections/vec_deque/spec_from_iter.rs @@ -19,6 +19,7 @@ where } } +#[cfg(not(test))] impl SpecFromIter> for VecDeque { #[inline] fn spec_from_iter(iterator: crate::vec::IntoIter) -> Self { diff --git a/library/alloc/src/ffi/c_str.rs b/library/alloc/src/ffi/c_str.rs index fd93045a5ac4d..f6743c6571095 100644 --- a/library/alloc/src/ffi/c_str.rs +++ b/library/alloc/src/ffi/c_str.rs @@ -10,7 +10,6 @@ use core::{fmt, mem, ops, ptr, slice}; use crate::borrow::{Cow, ToOwned}; use crate::boxed::Box; use crate::rc::Rc; -use crate::slice::hack::into_vec; use crate::string::String; #[cfg(target_has_atomic = "ptr")] use crate::sync::Arc; @@ -103,7 +102,7 @@ use crate::vec::Vec; /// of `CString` instances can lead to invalid memory accesses, memory leaks, /// and other memory errors. #[derive(PartialEq, PartialOrd, Eq, Ord, Hash, Clone)] -#[cfg_attr(not(test), rustc_diagnostic_item = "cstring_type")] +#[rustc_diagnostic_item = "cstring_type"] #[stable(feature = "alloc_c_string", since = "1.64.0")] pub struct CString { // Invariant 1: the slice ends with a zero byte and has a length of at least one. @@ -491,7 +490,7 @@ impl CString { #[must_use = "`self` will be dropped if the result is not used"] #[stable(feature = "cstring_into", since = "1.7.0")] pub fn into_bytes(self) -> Vec { - let mut vec = into_vec(self.into_inner()); + let mut vec = self.into_inner().into_vec(); let _nul = vec.pop(); debug_assert_eq!(_nul, Some(0u8)); vec @@ -512,7 +511,7 @@ impl CString { #[must_use = "`self` will be dropped if the result is not used"] #[stable(feature = "cstring_into", since = "1.7.0")] pub fn into_bytes_with_nul(self) -> Vec { - into_vec(self.into_inner()) + self.into_inner().into_vec() } /// Returns the contents of this `CString` as a slice of bytes. @@ -573,7 +572,7 @@ impl CString { #[inline] #[must_use] #[stable(feature = "as_c_str", since = "1.20.0")] - #[cfg_attr(not(test), rustc_diagnostic_item = "cstring_as_c_str")] + #[rustc_diagnostic_item = "cstring_as_c_str"] pub fn as_c_str(&self) -> &CStr { &*self } @@ -755,7 +754,6 @@ impl<'a> From> for CString { } } -#[cfg(not(test))] #[stable(feature = "box_from_c_str", since = "1.17.0")] impl From<&CStr> for Box { /// Converts a `&CStr` into a `Box`, @@ -766,7 +764,6 @@ impl From<&CStr> for Box { } } -#[cfg(not(test))] #[stable(feature = "box_from_mut_slice", since = "1.84.0")] impl From<&mut CStr> for Box { /// Converts a `&mut CStr` into a `Box`, @@ -845,7 +842,6 @@ impl TryFrom for String { } } -#[cfg(not(test))] #[stable(feature = "more_box_slice_clone", since = "1.29.0")] impl Clone for Box { #[inline] @@ -971,7 +967,6 @@ impl Default for Rc { } } -#[cfg(not(test))] #[stable(feature = "default_box_extra", since = "1.17.0")] impl Default for Box { fn default() -> Box { @@ -1080,7 +1075,7 @@ impl ToOwned for CStr { } fn clone_into(&self, target: &mut CString) { - let mut b = into_vec(mem::take(&mut target.inner)); + let mut b = mem::take(&mut target.inner).into_vec(); self.to_bytes_with_nul().clone_into(&mut b); target.inner = b.into_boxed_slice(); } @@ -1113,7 +1108,6 @@ impl AsRef for CString { } } -#[cfg(not(test))] impl CStr { /// Converts a `CStr` into a [Cow]<[str]>. /// diff --git a/library/alloc/src/lib.rs b/library/alloc/src/lib.rs index cb93100f56cf0..84bbc5a297394 100644 --- a/library/alloc/src/lib.rs +++ b/library/alloc/src/lib.rs @@ -92,7 +92,6 @@ // // Library features: // tidy-alphabetical-start -#![cfg_attr(test, feature(str_as_str))] #![feature(alloc_layout_extra)] #![feature(allocator_api)] #![feature(array_chunks)] @@ -159,13 +158,11 @@ // // Language features: // tidy-alphabetical-start -#![cfg_attr(not(test), feature(coroutine_trait))] -#![cfg_attr(test, feature(panic_update_hook))] -#![cfg_attr(test, feature(test))] #![feature(allocator_internals)] #![feature(allow_internal_unstable)] #![feature(cfg_sanitize)] #![feature(const_precise_live_drops)] +#![feature(coroutine_trait)] #![feature(decl_macro)] #![feature(dropck_eyepatch)] #![feature(fundamental)] @@ -198,15 +195,6 @@ // from other crates, but since this can only appear for lang items, it doesn't seem worth fixing. #![feature(intra_doc_pointers)] -// Allow testing this library -#[cfg(test)] -#[macro_use] -extern crate std; -#[cfg(test)] -extern crate test; -#[cfg(test)] -mod testing; - // Module with internal macros used by other modules (needs to be included before other modules). #[macro_use] mod macros; @@ -214,7 +202,6 @@ mod macros; mod raw_vec; // Heaps provided for low-level allocation strategies - pub mod alloc; // Primitive types using the heaps above @@ -222,13 +209,8 @@ pub mod alloc; // Need to conditionally define the mod from `boxed.rs` to avoid // duplicating the lang-items when building in test cfg; but also need // to allow code to have `use boxed::Box;` declarations. -#[cfg(not(test))] -pub mod boxed; -#[cfg(test)] -mod boxed { - pub(crate) use std::boxed::Box; -} pub mod borrow; +pub mod boxed; #[unstable(feature = "bstr", issue = "134915")] pub mod bstr; pub mod collections; @@ -252,20 +234,3 @@ pub mod __export { pub use core::format_args; pub use core::hint::must_use; } - -#[cfg(test)] -#[allow(dead_code)] // Not used in all configurations -pub(crate) mod test_helpers { - /// Copied from `std::test_helpers::test_rng`, since these tests rely on the - /// seed not being the same for every RNG invocation too. - pub(crate) fn test_rng() -> rand_xorshift::XorShiftRng { - use std::hash::{BuildHasher, Hash, Hasher}; - let mut hasher = std::hash::RandomState::new().build_hasher(); - std::panic::Location::caller().hash(&mut hasher); - let hc64 = hasher.finish(); - let seed_vec = - hc64.to_le_bytes().into_iter().chain(0u8..8).collect::>(); - let seed: [u8; 16] = seed_vec.as_slice().try_into().unwrap(); - rand::SeedableRng::from_seed(seed) - } -} diff --git a/library/alloc/src/macros.rs b/library/alloc/src/macros.rs index c000fd6f4efa7..214192b8c9a9b 100644 --- a/library/alloc/src/macros.rs +++ b/library/alloc/src/macros.rs @@ -34,7 +34,7 @@ /// be mindful of side effects. /// /// [`Vec`]: crate::vec::Vec -#[cfg(all(not(no_global_oom_handling), not(test)))] +#[cfg(not(no_global_oom_handling))] #[macro_export] #[stable(feature = "rust1", since = "1.0.0")] #[rustc_diagnostic_item = "vec_macro"] @@ -55,25 +55,6 @@ macro_rules! vec { ); } -// HACK(japaric): with cfg(test) the inherent `[T]::into_vec` method, which is -// required for this macro definition, is not available. Instead use the -// `slice::into_vec` function which is only available with cfg(test) -// NB see the slice::hack module in slice.rs for more information -#[cfg(all(not(no_global_oom_handling), test))] -#[allow(unused_macro_rules)] -macro_rules! vec { - () => ( - $crate::vec::Vec::new() - ); - ($elem:expr; $n:expr) => ( - $crate::vec::from_elem($elem, $n) - ); - ($($x:expr),*) => ( - $crate::slice::into_vec($crate::boxed::Box::new([$($x),*])) - ); - ($($x:expr,)*) => (vec![$($x),*]) -} - /// Creates a `String` using interpolation of runtime expressions. /// /// The first argument `format!` receives is a format string. This must be a string @@ -120,7 +101,7 @@ macro_rules! vec { #[macro_export] #[stable(feature = "rust1", since = "1.0.0")] #[allow_internal_unstable(hint_must_use, liballoc_internals)] -#[cfg_attr(not(test), rustc_diagnostic_item = "format_macro")] +#[rustc_diagnostic_item = "format_macro"] macro_rules! format { ($($arg:tt)*) => { $crate::__export::must_use({ diff --git a/library/alloc/src/raw_vec/mod.rs b/library/alloc/src/raw_vec/mod.rs index 70f32fbaab427..f1f5ffefb9b71 100644 --- a/library/alloc/src/raw_vec/mod.rs +++ b/library/alloc/src/raw_vec/mod.rs @@ -1,4 +1,5 @@ #![unstable(feature = "raw_vec_internals", reason = "unstable const warnings", issue = "none")] +#![cfg_attr(test, allow(dead_code))] use core::marker::PhantomData; use core::mem::{ManuallyDrop, MaybeUninit, SizedTypeProperties}; diff --git a/library/alloc/src/rc.rs b/library/alloc/src/rc.rs index 2a4d4f264444c..fe0b25f31b923 100644 --- a/library/alloc/src/rc.rs +++ b/library/alloc/src/rc.rs @@ -262,14 +262,11 @@ use core::ptr::{self, NonNull, drop_in_place}; #[cfg(not(no_global_oom_handling))] use core::slice::from_raw_parts_mut; use core::{borrow, fmt, hint}; -#[cfg(test)] -use std::boxed::Box; #[cfg(not(no_global_oom_handling))] use crate::alloc::handle_alloc_error; use crate::alloc::{AllocError, Allocator, Global, Layout}; use crate::borrow::{Cow, ToOwned}; -#[cfg(not(test))] use crate::boxed::Box; #[cfg(not(no_global_oom_handling))] use crate::string::String; @@ -306,7 +303,7 @@ fn rc_inner_layout_for_value_layout(layout: Layout) -> Layout { /// /// [get_mut]: Rc::get_mut #[doc(search_unbox)] -#[cfg_attr(not(test), rustc_diagnostic_item = "Rc")] +#[rustc_diagnostic_item = "Rc"] #[stable(feature = "rust1", since = "1.0.0")] #[rustc_insignificant_dtor] pub struct Rc< @@ -2981,7 +2978,7 @@ impl> ToRcSlice for I { /// /// [`upgrade`]: Weak::upgrade #[stable(feature = "rc_weak", since = "1.4.0")] -#[cfg_attr(not(test), rustc_diagnostic_item = "RcWeak")] +#[rustc_diagnostic_item = "RcWeak"] pub struct Weak< T: ?Sized, #[unstable(feature = "allocator_api", issue = "32838")] A: Allocator = Global, diff --git a/library/alloc/src/slice.rs b/library/alloc/src/slice.rs index 8baf96850626d..7c5d22e1ee9be 100644 --- a/library/alloc/src/slice.rs +++ b/library/alloc/src/slice.rs @@ -8,9 +8,6 @@ //! A few functions are provided to create a slice from a value reference //! or from a raw pointer. #![stable(feature = "rust1", since = "1.0.0")] -// Many of the usings in this module are only used in the test configuration. -// It's cleaner to just turn off the unused_imports warning than to fix them. -#![cfg_attr(test, allow(unused_imports, dead_code))] use core::borrow::{Borrow, BorrowMut}; #[cfg(not(no_global_oom_handling))] @@ -63,16 +60,6 @@ pub use core::slice::{range, try_range}; //////////////////////////////////////////////////////////////////////////////// // Basic slice extension methods //////////////////////////////////////////////////////////////////////////////// - -// HACK(japaric) needed for the implementation of `vec!` macro during testing -// N.B., see the `hack` module in this file for more details. -#[cfg(test)] -pub use hack::into_vec; -// HACK(japaric) needed for the implementation of `Vec::clone` during testing -// N.B., see the `hack` module in this file for more details. -#[cfg(test)] -pub use hack::to_vec; - use crate::alloc::Allocator; #[cfg(not(no_global_oom_handling))] use crate::alloc::Global; @@ -81,98 +68,6 @@ use crate::borrow::ToOwned; use crate::boxed::Box; use crate::vec::Vec; -// HACK(japaric): With cfg(test) `impl [T]` is not available, these three -// functions are actually methods that are in `impl [T]` but not in -// `core::slice::SliceExt` - we need to supply these functions for the -// `test_permutations` test -#[allow(unreachable_pub)] // cfg(test) pub above -pub(crate) mod hack { - use core::alloc::Allocator; - - use crate::boxed::Box; - use crate::vec::Vec; - - // We shouldn't add inline attribute to this since this is used in - // `vec!` macro mostly and causes perf regression. See #71204 for - // discussion and perf results. - #[allow(missing_docs)] - pub fn into_vec(b: Box<[T], A>) -> Vec { - unsafe { - let len = b.len(); - let (b, alloc) = Box::into_raw_with_allocator(b); - Vec::from_raw_parts_in(b as *mut T, len, len, alloc) - } - } - - #[cfg(not(no_global_oom_handling))] - #[allow(missing_docs)] - #[inline] - pub fn to_vec(s: &[T], alloc: A) -> Vec { - T::to_vec(s, alloc) - } - - #[cfg(not(no_global_oom_handling))] - pub trait ConvertVec { - fn to_vec(s: &[Self], alloc: A) -> Vec - where - Self: Sized; - } - - #[cfg(not(no_global_oom_handling))] - impl ConvertVec for T { - #[inline] - default fn to_vec(s: &[Self], alloc: A) -> Vec { - struct DropGuard<'a, T, A: Allocator> { - vec: &'a mut Vec, - num_init: usize, - } - impl<'a, T, A: Allocator> Drop for DropGuard<'a, T, A> { - #[inline] - fn drop(&mut self) { - // SAFETY: - // items were marked initialized in the loop below - unsafe { - self.vec.set_len(self.num_init); - } - } - } - let mut vec = Vec::with_capacity_in(s.len(), alloc); - let mut guard = DropGuard { vec: &mut vec, num_init: 0 }; - let slots = guard.vec.spare_capacity_mut(); - // .take(slots.len()) is necessary for LLVM to remove bounds checks - // and has better codegen than zip. - for (i, b) in s.iter().enumerate().take(slots.len()) { - guard.num_init = i; - slots[i].write(b.clone()); - } - core::mem::forget(guard); - // SAFETY: - // the vec was allocated and initialized above to at least this length. - unsafe { - vec.set_len(s.len()); - } - vec - } - } - - #[cfg(not(no_global_oom_handling))] - impl ConvertVec for T { - #[inline] - fn to_vec(s: &[Self], alloc: A) -> Vec { - let mut v = Vec::with_capacity_in(s.len(), alloc); - // SAFETY: - // allocated above with the capacity of `s`, and initialize to `s.len()` in - // ptr::copy_to_non_overlapping below. - unsafe { - s.as_ptr().copy_to_nonoverlapping(v.as_mut_ptr(), s.len()); - v.set_len(s.len()); - } - v - } - } -} - -#[cfg(not(test))] impl [T] { /// Sorts the slice, preserving initial order of equal elements. /// @@ -501,8 +396,64 @@ impl [T] { where T: Clone, { - // N.B., see the `hack` module in this file for more details. - hack::to_vec(self, alloc) + return T::to_vec(self, alloc); + + trait ConvertVec { + fn to_vec(s: &[Self], alloc: A) -> Vec + where + Self: Sized; + } + + impl ConvertVec for T { + #[inline] + default fn to_vec(s: &[Self], alloc: A) -> Vec { + struct DropGuard<'a, T, A: Allocator> { + vec: &'a mut Vec, + num_init: usize, + } + impl<'a, T, A: Allocator> Drop for DropGuard<'a, T, A> { + #[inline] + fn drop(&mut self) { + // SAFETY: + // items were marked initialized in the loop below + unsafe { + self.vec.set_len(self.num_init); + } + } + } + let mut vec = Vec::with_capacity_in(s.len(), alloc); + let mut guard = DropGuard { vec: &mut vec, num_init: 0 }; + let slots = guard.vec.spare_capacity_mut(); + // .take(slots.len()) is necessary for LLVM to remove bounds checks + // and has better codegen than zip. + for (i, b) in s.iter().enumerate().take(slots.len()) { + guard.num_init = i; + slots[i].write(b.clone()); + } + core::mem::forget(guard); + // SAFETY: + // the vec was allocated and initialized above to at least this length. + unsafe { + vec.set_len(s.len()); + } + vec + } + } + + impl ConvertVec for T { + #[inline] + fn to_vec(s: &[Self], alloc: A) -> Vec { + let mut v = Vec::with_capacity_in(s.len(), alloc); + // SAFETY: + // allocated above with the capacity of `s`, and initialize to `s.len()` in + // ptr::copy_to_non_overlapping below. + unsafe { + s.as_ptr().copy_to_nonoverlapping(v.as_mut_ptr(), s.len()); + v.set_len(s.len()); + } + v + } + } } /// Converts `self` into a vector without clones or allocation. @@ -522,10 +473,13 @@ impl [T] { #[rustc_allow_incoherent_impl] #[stable(feature = "rust1", since = "1.0.0")] #[inline] - #[cfg_attr(not(test), rustc_diagnostic_item = "slice_into_vec")] + #[rustc_diagnostic_item = "slice_into_vec"] pub fn into_vec(self: Box) -> Vec { - // N.B., see the `hack` module in this file for more details. - hack::into_vec(self) + unsafe { + let len = self.len(); + let (b, alloc) = Box::into_raw_with_allocator(self); + Vec::from_raw_parts_in(b as *mut T, len, len, alloc) + } } /// Creates a vector by copying a slice `n` times. @@ -666,7 +620,6 @@ impl [T] { } } -#[cfg(not(test))] impl [u8] { /// Returns a vector containing a copy of this slice where each byte /// is mapped to its ASCII upper case equivalent. @@ -883,14 +836,9 @@ impl SpecCloneIntoVec for [T] { #[stable(feature = "rust1", since = "1.0.0")] impl ToOwned for [T] { type Owned = Vec; - #[cfg(not(test))] - fn to_owned(&self) -> Vec { - self.to_vec() - } - #[cfg(test)] fn to_owned(&self) -> Vec { - hack::to_vec(self, Global) + self.to_vec() } fn clone_into(&self, target: &mut Vec) { diff --git a/library/alloc/src/str.rs b/library/alloc/src/str.rs index 6fee8d3fe3346..0664f2c3cf2c1 100644 --- a/library/alloc/src/str.rs +++ b/library/alloc/src/str.rs @@ -219,7 +219,6 @@ impl ToOwned for str { } /// Methods for string slices. -#[cfg(not(test))] impl str { /// Converts a `Box` into a `Box<[u8]>` without copying or allocating. /// @@ -631,7 +630,6 @@ pub unsafe fn from_boxed_utf8_unchecked(v: Box<[u8]>) -> Box { #[unstable(feature = "str_internals", issue = "none")] #[doc(hidden)] #[inline] -#[cfg(not(test))] #[cfg(not(no_global_oom_handling))] pub fn convert_while_ascii(s: &str, convert: fn(&u8) -> u8) -> (String, &str) { // Process the input in chunks of 16 bytes to enable auto-vectorization. @@ -704,7 +702,6 @@ pub fn convert_while_ascii(s: &str, convert: fn(&u8) -> u8) -> (String, &str) { } } #[inline] -#[cfg(not(test))] #[cfg(not(no_global_oom_handling))] #[allow(dead_code)] /// Faster implementation of string replacement for ASCII to ASCII cases. diff --git a/library/alloc/src/string.rs b/library/alloc/src/string.rs index 2c03478654983..2ba797ab2add4 100644 --- a/library/alloc/src/string.rs +++ b/library/alloc/src/string.rs @@ -356,7 +356,7 @@ use crate::vec::{self, Vec}; /// [`as_str()`]: String::as_str #[derive(PartialEq, PartialOrd, Eq, Ord)] #[stable(feature = "rust1", since = "1.0.0")] -#[cfg_attr(not(test), lang = "String")] +#[lang = "String"] pub struct String { vec: Vec, } @@ -438,7 +438,7 @@ impl String { /// ``` #[inline] #[rustc_const_stable(feature = "const_string_new", since = "1.39.0")] - #[cfg_attr(not(test), rustc_diagnostic_item = "string_new")] + #[rustc_diagnostic_item = "string_new"] #[stable(feature = "rust1", since = "1.0.0")] #[must_use] pub const fn new() -> String { @@ -501,17 +501,6 @@ impl String { Ok(String { vec: Vec::try_with_capacity(capacity)? }) } - // HACK(japaric): with cfg(test) the inherent `[T]::to_vec` method, which is - // required for this method definition, is not available. Since we don't - // require this method for testing purposes, I'll just stub it - // NB see the slice::hack module in slice.rs for more information - #[inline] - #[cfg(test)] - #[allow(missing_docs)] - pub fn from_str(_: &str) -> String { - panic!("not available with cfg(test)"); - } - /// Converts a vector of bytes to a `String`. /// /// A string ([`String`]) is made of bytes ([`u8`]), and a vector of bytes @@ -570,7 +559,7 @@ impl String { /// [`into_bytes`]: String::into_bytes #[inline] #[stable(feature = "rust1", since = "1.0.0")] - #[cfg_attr(not(test), rustc_diagnostic_item = "string_from_utf8")] + #[rustc_diagnostic_item = "string_from_utf8"] pub fn from_utf8(vec: Vec) -> Result { match str::from_utf8(&vec) { Ok(..) => Ok(String { vec }), @@ -1071,7 +1060,7 @@ impl String { #[inline] #[must_use] #[stable(feature = "string_as_str", since = "1.7.0")] - #[cfg_attr(not(test), rustc_diagnostic_item = "string_as_str")] + #[rustc_diagnostic_item = "string_as_str"] #[rustc_const_unstable(feature = "const_vec_string_slice", issue = "129041")] pub const fn as_str(&self) -> &str { // SAFETY: String contents are stipulated to be valid UTF-8, invalid contents are an error @@ -1094,7 +1083,7 @@ impl String { #[inline] #[must_use] #[stable(feature = "string_as_str", since = "1.7.0")] - #[cfg_attr(not(test), rustc_diagnostic_item = "string_as_mut_str")] + #[rustc_diagnostic_item = "string_as_mut_str"] #[rustc_const_unstable(feature = "const_vec_string_slice", issue = "129041")] pub const fn as_mut_str(&mut self) -> &mut str { // SAFETY: String contents are stipulated to be valid UTF-8, invalid contents are an error @@ -1117,7 +1106,7 @@ impl String { #[inline] #[stable(feature = "rust1", since = "1.0.0")] #[rustc_confusables("append", "push")] - #[cfg_attr(not(test), rustc_diagnostic_item = "string_push_str")] + #[rustc_diagnostic_item = "string_push_str"] pub fn push_str(&mut self, string: &str) { self.vec.extend_from_slice(string.as_bytes()) } @@ -1755,7 +1744,7 @@ impl String { #[cfg(not(no_global_oom_handling))] #[inline] #[stable(feature = "insert_str", since = "1.16.0")] - #[cfg_attr(not(test), rustc_diagnostic_item = "string_insert_str")] + #[rustc_diagnostic_item = "string_insert_str"] pub fn insert_str(&mut self, idx: usize, string: &str) { assert!(self.is_char_boundary(idx)); @@ -2724,7 +2713,7 @@ impl FromStr for String { /// implementation for free. /// /// [`Display`]: fmt::Display -#[cfg_attr(not(test), rustc_diagnostic_item = "ToString")] +#[rustc_diagnostic_item = "ToString"] #[stable(feature = "rust1", since = "1.0.0")] pub trait ToString { /// Converts the given value to a `String`. @@ -2739,7 +2728,7 @@ pub trait ToString { /// ``` #[rustc_conversion_suggestion] #[stable(feature = "rust1", since = "1.0.0")] - #[cfg_attr(not(test), rustc_diagnostic_item = "to_string_method")] + #[rustc_diagnostic_item = "to_string_method"] fn to_string(&self) -> String; } @@ -2979,7 +2968,6 @@ impl From<&String> for String { } // note: test pulls in std, which causes errors here -#[cfg(not(test))] #[stable(feature = "string_from_box", since = "1.18.0")] impl From> for String { /// Converts the given boxed `str` slice to a [`String`]. diff --git a/library/alloc/src/sync.rs b/library/alloc/src/sync.rs index 1956dda538816..faee15ab0c666 100644 --- a/library/alloc/src/sync.rs +++ b/library/alloc/src/sync.rs @@ -234,7 +234,7 @@ macro_rules! acquire { /// /// [rc_examples]: crate::rc#examples #[doc(search_unbox)] -#[cfg_attr(not(test), rustc_diagnostic_item = "Arc")] +#[rustc_diagnostic_item = "Arc"] #[stable(feature = "rust1", since = "1.0.0")] #[rustc_insignificant_dtor] pub struct Arc< @@ -311,7 +311,7 @@ impl Arc { /// /// [`upgrade`]: Weak::upgrade #[stable(feature = "arc_weak", since = "1.4.0")] -#[cfg_attr(not(test), rustc_diagnostic_item = "ArcWeak")] +#[rustc_diagnostic_item = "ArcWeak"] pub struct Weak< T: ?Sized, #[unstable(feature = "allocator_api", issue = "32838")] A: Allocator = Global, diff --git a/library/alloc/src/vec/into_iter.rs b/library/alloc/src/vec/into_iter.rs index 52597e41c1cf8..3eee988b6c9d1 100644 --- a/library/alloc/src/vec/into_iter.rs +++ b/library/alloc/src/vec/into_iter.rs @@ -472,14 +472,9 @@ where #[cfg(not(no_global_oom_handling))] #[stable(feature = "vec_into_iter_clone", since = "1.8.0")] impl Clone for IntoIter { - #[cfg(not(test))] fn clone(&self) -> Self { self.as_slice().to_vec_in(self.alloc.deref().clone()).into_iter() } - #[cfg(test)] - fn clone(&self) -> Self { - crate::slice::to_vec(self.as_slice(), self.alloc.deref().clone()).into_iter() - } } #[stable(feature = "rust1", since = "1.0.0")] diff --git a/library/alloc/src/vec/mod.rs b/library/alloc/src/vec/mod.rs index 49878f2b6fa5d..ce6685405981e 100644 --- a/library/alloc/src/vec/mod.rs +++ b/library/alloc/src/vec/mod.rs @@ -404,7 +404,7 @@ mod spec_extend; /// [owned slice]: Box /// [`into_boxed_slice`]: Vec::into_boxed_slice #[stable(feature = "rust1", since = "1.0.0")] -#[cfg_attr(not(test), rustc_diagnostic_item = "Vec")] +#[rustc_diagnostic_item = "Vec"] #[rustc_insignificant_dtor] pub struct Vec { buf: RawVec, @@ -428,7 +428,7 @@ impl Vec { /// ``` #[inline] #[rustc_const_stable(feature = "const_vec_new", since = "1.39.0")] - #[cfg_attr(not(test), rustc_diagnostic_item = "vec_new")] + #[rustc_diagnostic_item = "vec_new"] #[stable(feature = "rust1", since = "1.0.0")] #[must_use] pub const fn new() -> Self { @@ -489,7 +489,7 @@ impl Vec { #[inline] #[stable(feature = "rust1", since = "1.0.0")] #[must_use] - #[cfg_attr(not(test), rustc_diagnostic_item = "vec_with_capacity")] + #[rustc_diagnostic_item = "vec_with_capacity"] #[track_caller] pub fn with_capacity(capacity: usize) -> Self { Self::with_capacity_in(capacity, Global) @@ -1279,7 +1279,7 @@ impl Vec { #[cfg(not(no_global_oom_handling))] #[stable(feature = "rust1", since = "1.0.0")] #[track_caller] - #[cfg_attr(not(test), rustc_diagnostic_item = "vec_reserve")] + #[rustc_diagnostic_item = "vec_reserve"] pub fn reserve(&mut self, additional: usize) { self.buf.reserve(self.len, additional); } @@ -1568,7 +1568,7 @@ impl Vec { /// ``` #[inline] #[stable(feature = "vec_as_slice", since = "1.7.0")] - #[cfg_attr(not(test), rustc_diagnostic_item = "vec_as_slice")] + #[rustc_diagnostic_item = "vec_as_slice"] #[rustc_const_unstable(feature = "const_vec_string_slice", issue = "129041")] pub const fn as_slice(&self) -> &[T] { // SAFETY: `slice::from_raw_parts` requires pointee is a contiguous, aligned buffer of size @@ -1600,7 +1600,7 @@ impl Vec { /// ``` #[inline] #[stable(feature = "vec_as_slice", since = "1.7.0")] - #[cfg_attr(not(test), rustc_diagnostic_item = "vec_as_mut_slice")] + #[rustc_diagnostic_item = "vec_as_mut_slice"] #[rustc_const_unstable(feature = "const_vec_string_slice", issue = "129041")] pub const fn as_mut_slice(&mut self) -> &mut [T] { // SAFETY: `slice::from_raw_parts_mut` requires pointee is a contiguous, aligned buffer of @@ -2511,7 +2511,7 @@ impl Vec { /// Takes *O*(1) time. #[inline] #[stable(feature = "rust1", since = "1.0.0")] - #[cfg_attr(not(test), rustc_diagnostic_item = "vec_pop")] + #[rustc_diagnostic_item = "vec_pop"] pub fn pop(&mut self) -> Option { if self.len == 0 { None @@ -2712,7 +2712,7 @@ impl Vec { /// assert!(!v.is_empty()); /// ``` #[stable(feature = "rust1", since = "1.0.0")] - #[cfg_attr(not(test), rustc_diagnostic_item = "vec_is_empty")] + #[rustc_diagnostic_item = "vec_is_empty"] #[rustc_const_unstable(feature = "const_vec_string_slice", issue = "129041")] pub const fn is_empty(&self) -> bool { self.len() == 0 @@ -3193,7 +3193,7 @@ impl Vec { #[doc(hidden)] #[cfg(not(no_global_oom_handling))] #[stable(feature = "rust1", since = "1.0.0")] -#[cfg_attr(not(test), rustc_diagnostic_item = "vec_from_elem")] +#[rustc_diagnostic_item = "vec_from_elem"] #[track_caller] pub fn from_elem(elem: T, n: usize) -> Vec { ::from_elem(elem, n, Global) @@ -3293,23 +3293,12 @@ unsafe impl ops::DerefPure for Vec {} #[cfg(not(no_global_oom_handling))] #[stable(feature = "rust1", since = "1.0.0")] impl Clone for Vec { - #[cfg(not(test))] #[track_caller] fn clone(&self) -> Self { let alloc = self.allocator().clone(); <[T]>::to_vec_in(&**self, alloc) } - // HACK(japaric): with cfg(test) the inherent `[T]::to_vec` method, which is - // required for this method definition, is not available. Instead use the - // `slice::to_vec` function which is only available with cfg(test) - // NB see the slice::hack module in slice.rs for more information - #[cfg(test)] - fn clone(&self) -> Self { - let alloc = self.allocator().clone(); - crate::slice::to_vec(&**self, alloc) - } - /// Overwrites the contents of `self` with a clone of the contents of `source`. /// /// This method is preferred over simply assigning `source.clone()` to `self`, @@ -3854,15 +3843,10 @@ impl From<&[T]> for Vec { /// ``` /// assert_eq!(Vec::from(&[1, 2, 3][..]), vec![1, 2, 3]); /// ``` - #[cfg(not(test))] #[track_caller] fn from(s: &[T]) -> Vec { s.to_vec() } - #[cfg(test)] - fn from(s: &[T]) -> Vec { - crate::slice::to_vec(s, Global) - } } #[cfg(not(no_global_oom_handling))] @@ -3875,15 +3859,10 @@ impl From<&mut [T]> for Vec { /// ``` /// assert_eq!(Vec::from(&mut [1, 2, 3][..]), vec![1, 2, 3]); /// ``` - #[cfg(not(test))] #[track_caller] fn from(s: &mut [T]) -> Vec { s.to_vec() } - #[cfg(test)] - fn from(s: &mut [T]) -> Vec { - crate::slice::to_vec(s, Global) - } } #[cfg(not(no_global_oom_handling))] @@ -3928,16 +3907,10 @@ impl From<[T; N]> for Vec { /// ``` /// assert_eq!(Vec::from([1, 2, 3]), vec![1, 2, 3]); /// ``` - #[cfg(not(test))] #[track_caller] fn from(s: [T; N]) -> Vec { <[T]>::into_vec(Box::new(s)) } - - #[cfg(test)] - fn from(s: [T; N]) -> Vec { - crate::slice::into_vec(Box::new(s)) - } } #[stable(feature = "vec_from_cow_slice", since = "1.14.0")] @@ -3966,7 +3939,6 @@ where } // note: test pulls in std, which causes errors here -#[cfg(not(test))] #[stable(feature = "vec_from_box", since = "1.18.0")] impl From> for Vec { /// Converts a boxed slice into a vector by transferring ownership of @@ -3985,7 +3957,6 @@ impl From> for Vec { // note: test pulls in std, which causes errors here #[cfg(not(no_global_oom_handling))] -#[cfg(not(test))] #[stable(feature = "box_from_vec", since = "1.20.0")] impl From> for Box<[T], A> { /// Converts a vector into a boxed slice. diff --git a/library/alloctests/Cargo.toml b/library/alloctests/Cargo.toml index f1a783b1e224d..306375f5f01cc 100644 --- a/library/alloctests/Cargo.toml +++ b/library/alloctests/Cargo.toml @@ -10,8 +10,9 @@ edition = "2021" [lib] path = "lib.rs" -test = false -bench = false +test = true +bench = true +doc = false [dev-dependencies] rand = { version = "0.9.0", default-features = false, features = ["alloc"] } diff --git a/library/alloctests/lib.rs b/library/alloctests/lib.rs index b49208cd4eb3a..0b5aa2c575d9a 100644 --- a/library/alloctests/lib.rs +++ b/library/alloctests/lib.rs @@ -1 +1,90 @@ -// Intentionally left empty. +#![cfg(test)] +#![allow(unused_attributes)] +#![unstable(feature = "alloctests", issue = "none")] +#![no_std] +// Lints: +#![deny(unsafe_op_in_unsafe_fn)] +#![warn(deprecated_in_future)] +#![warn(missing_debug_implementations)] +#![allow(explicit_outlives_requirements)] +#![allow(internal_features)] +#![allow(rustdoc::redundant_explicit_links)] +#![warn(rustdoc::unescaped_backticks)] +#![deny(ffi_unwind_calls)] +// +// Library features: +// tidy-alphabetical-start +#![feature(alloc_layout_extra)] +#![feature(allocator_api)] +#![feature(array_into_iter_constructors)] +#![feature(assert_matches)] +#![feature(core_intrinsics)] +#![feature(exact_size_is_empty)] +#![feature(extend_one)] +#![feature(extend_one_unchecked)] +#![feature(hasher_prefixfree_extras)] +#![feature(inplace_iteration)] +#![feature(iter_advance_by)] +#![feature(iter_next_chunk)] +#![feature(maybe_uninit_slice)] +#![feature(maybe_uninit_uninit_array_transpose)] +#![feature(ptr_internals)] +#![feature(sized_type_properties)] +#![feature(slice_iter_mut_as_mut_slice)] +#![feature(slice_ptr_get)] +#![feature(slice_range)] +#![feature(std_internals)] +#![feature(temporary_niche_types)] +#![feature(trusted_fused)] +#![feature(trusted_len)] +#![feature(trusted_random_access)] +#![feature(try_reserve_kind)] +#![feature(try_trait_v2)] +// tidy-alphabetical-end +// +// Language features: +// tidy-alphabetical-start +#![feature(cfg_sanitize)] +#![feature(dropck_eyepatch)] +#![feature(lang_items)] +#![feature(min_specialization)] +#![feature(negative_impls)] +#![feature(never_type)] +#![feature(optimize_attribute)] +#![feature(rustc_allow_const_fn_unstable)] +#![feature(rustc_attrs)] +#![feature(staged_api)] +#![feature(test)] +#![rustc_preserve_ub_checks] +// tidy-alphabetical-end + +// Allow testing this library +extern crate alloc as realalloc; +#[macro_use] +extern crate std; +#[cfg(test)] +extern crate test; +mod testing; +use realalloc::*; + +#[path = "../alloc/src/collections/mod.rs"] +mod collections; + +#[path = "../alloc/src/raw_vec/mod.rs"] +mod raw_vec; + +#[allow(dead_code)] // Not used in all configurations +pub(crate) mod test_helpers { + /// Copied from `std::test_helpers::test_rng`, since these tests rely on the + /// seed not being the same for every RNG invocation too. + pub(crate) fn test_rng() -> rand_xorshift::XorShiftRng { + use std::hash::{BuildHasher, Hash, Hasher}; + let mut hasher = std::hash::RandomState::new().build_hasher(); + std::panic::Location::caller().hash(&mut hasher); + let hc64 = hasher.finish(); + let seed_vec = + hc64.to_le_bytes().into_iter().chain(0u8..8).collect::>(); + let seed: [u8; 16] = seed_vec.as_slice().try_into().unwrap(); + rand::SeedableRng::from_seed(seed) + } +} diff --git a/library/alloc/src/testing/crash_test.rs b/library/alloctests/testing/crash_test.rs similarity index 100% rename from library/alloc/src/testing/crash_test.rs rename to library/alloctests/testing/crash_test.rs diff --git a/library/alloc/src/testing/mod.rs b/library/alloctests/testing/mod.rs similarity index 100% rename from library/alloc/src/testing/mod.rs rename to library/alloctests/testing/mod.rs diff --git a/library/alloc/src/testing/ord_chaos.rs b/library/alloctests/testing/ord_chaos.rs similarity index 100% rename from library/alloc/src/testing/ord_chaos.rs rename to library/alloctests/testing/ord_chaos.rs diff --git a/library/alloc/src/testing/rng.rs b/library/alloctests/testing/rng.rs similarity index 100% rename from library/alloc/src/testing/rng.rs rename to library/alloctests/testing/rng.rs From 22d0440993d6eab6e9faf35f729e2d52ba6d72a6 Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Thu, 13 Feb 2025 15:38:07 +0000 Subject: [PATCH 15/16] Add comments --- library/alloc/src/collections/mod.rs | 3 +++ library/alloc/src/raw_vec/mod.rs | 3 +++ library/alloctests/lib.rs | 3 +++ 3 files changed, 9 insertions(+) diff --git a/library/alloc/src/collections/mod.rs b/library/alloc/src/collections/mod.rs index 1414f797e8a45..fac4d1a65abcb 100644 --- a/library/alloc/src/collections/mod.rs +++ b/library/alloc/src/collections/mod.rs @@ -1,5 +1,8 @@ //! Collection types. +// Note: This module is also included in the alloctests crate using #[path] to +// run the tests. See the comment there for an explanation why this is the case. + #![stable(feature = "rust1", since = "1.0.0")] #[cfg(not(no_global_oom_handling))] diff --git a/library/alloc/src/raw_vec/mod.rs b/library/alloc/src/raw_vec/mod.rs index f1f5ffefb9b71..99ebc5c4bfca8 100644 --- a/library/alloc/src/raw_vec/mod.rs +++ b/library/alloc/src/raw_vec/mod.rs @@ -1,6 +1,9 @@ #![unstable(feature = "raw_vec_internals", reason = "unstable const warnings", issue = "none")] #![cfg_attr(test, allow(dead_code))] +// Note: This module is also included in the alloctests crate using #[path] to +// run the tests. See the comment there for an explanation why this is the case. + use core::marker::PhantomData; use core::mem::{ManuallyDrop, MaybeUninit, SizedTypeProperties}; use core::ptr::{self, NonNull, Unique}; diff --git a/library/alloctests/lib.rs b/library/alloctests/lib.rs index 0b5aa2c575d9a..6ce8a6d9ca174 100644 --- a/library/alloctests/lib.rs +++ b/library/alloctests/lib.rs @@ -67,6 +67,9 @@ extern crate test; mod testing; use realalloc::*; +// We are directly including collections and raw_vec here as both use non-public +// methods and fields in tests and as such need to have the types to test in the +// same crate as the tests themself. #[path = "../alloc/src/collections/mod.rs"] mod collections; From 17dd2b179c4fab87975bb1c23ea2b869ca516ea5 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Fri, 7 Mar 2025 22:00:36 +0100 Subject: [PATCH 16/16] Mention `env` and `option_env` macros in `std::env::var` docs --- library/std/src/env.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/library/std/src/env.rs b/library/std/src/env.rs index e62aeb2ede0ad..6961fa8ea947f 100644 --- a/library/std/src/env.rs +++ b/library/std/src/env.rs @@ -202,6 +202,9 @@ impl fmt::Debug for VarsOs { /// Returns [`VarError::NotUnicode`] if the variable's value is not valid /// Unicode. If this is not desired, consider using [`var_os`]. /// +/// Use [`env!`] or [`option_env!`] instead if you want to check environment +/// variables at compile time. +/// /// # Examples /// /// ```