diff --git a/compiler/rustc_resolve/src/diagnostics/impls.rs b/compiler/rustc_resolve/src/diagnostics/impls.rs index ccb6139eae652..52016ca5f8d5c 100644 --- a/compiler/rustc_resolve/src/diagnostics/impls.rs +++ b/compiler/rustc_resolve/src/diagnostics/impls.rs @@ -1987,7 +1987,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { is_expected, &|_| true, ); - self.add_typo_suggestion(err, suggestion, ident.span); + self.add_typo_suggestion(err, suggestion, ident.span, None); self.detect_derive_attribute(err, ident, parent_scope, sugg_span); let import_suggestions = @@ -2271,6 +2271,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { err: &mut Diag<'_>, suggestion: Option, span: Span, + prefix: Option, ) { let suggestion = match suggestion { None => return, @@ -2345,7 +2346,10 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { format!("maybe you meant this {}", suggestion.res.descr()) } }; - (span, msg, suggestion.candidate.to_ident_string()) + let candidate_str = suggestion.candidate.to_ident_string(); + let sugg = + if let Some(p) = prefix { format!("{p}: {candidate_str}") } else { candidate_str }; + (span, msg, sugg) }; err.span_suggestion_verbose(span, msg, sugg, Applicability::MaybeIncorrect); } diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs index e0a3b78fa5e2e..b2061cd9d9d91 100644 --- a/compiler/rustc_resolve/src/late/diagnostics.rs +++ b/compiler/rustc_resolve/src/late/diagnostics.rs @@ -1194,7 +1194,7 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { } } } - self.r.add_typo_suggestion(err, typo_sugg, ident_span); + self.r.add_typo_suggestion(err, typo_sugg, ident_span, None); return (true, suggested_candidates, candidates); } @@ -1244,7 +1244,7 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { &base_error.fallback_label, ) { // We do this to avoid losing a secondary span when we override the main error span. - self.r.add_typo_suggestion(err, typo_sugg, ident_span); + self.r.add_typo_suggestion(err, typo_sugg, ident_span, None); return (true, suggested_candidates, candidates); } } @@ -1429,13 +1429,22 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { return false; } + // Preserve the field name for struct field shorthands to avoid suggesting invalid shorthands. + let mut prefix = None; + if let PathSource::Expr(Some(ast::Expr { kind: ExprKind::Struct(expr), .. })) = source + && let Some(ident) = path.last().map(|seg| seg.ident) + && expr.fields.iter().any(|f| f.ident == ident && f.is_shorthand) + { + prefix = Some(ident); + } + let typo_sugg = self.lookup_typo_candidate(path, following_seg, source.namespace(), is_expected); let mut fallback = true; let typo_sugg = typo_sugg .to_opt_suggestion() .filter(|sugg| !suggested_candidates.contains(sugg.candidate.as_str())); - self.r.add_typo_suggestion(err, typo_sugg, ident_span); + self.r.add_typo_suggestion(err, typo_sugg, ident_span, prefix); match self.diag_metadata.current_let_binding { Some((pat_sp, Some(ty_sp), None)) @@ -1453,7 +1462,7 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { // If the trait has a single item (which wasn't matched by the algorithm), suggest it let suggestion = self.get_single_associated_item(path, &source, is_expected); - self.r.add_typo_suggestion(err, suggestion, ident_span); + self.r.add_typo_suggestion(err, suggestion, ident_span, prefix); if self.let_binding_suggestion(err, ident_span) { fallback = false; diff --git a/compiler/rustc_target/src/spec/targets/wasm32_unknown_emscripten.rs b/compiler/rustc_target/src/spec/targets/wasm32_unknown_emscripten.rs index bda2681605814..a084e71a0c71f 100644 --- a/compiler/rustc_target/src/spec/targets/wasm32_unknown_emscripten.rs +++ b/compiler/rustc_target/src/spec/targets/wasm32_unknown_emscripten.rs @@ -6,8 +6,7 @@ use crate::spec::{ pub(crate) fn target() -> Target { // Reset flags for non-Em flavors back to empty to satisfy sanity checking tests. let pre_link_args = LinkArgs::new(); - let post_link_args = - TargetOptions::link_args(LinkerFlavor::EmCc, &["-sABORTING_MALLOC=0", "-sWASM_BIGINT"]); + let post_link_args = TargetOptions::link_args(LinkerFlavor::EmCc, &["-sABORTING_MALLOC=0"]); let opts = TargetOptions { os: Os::Emscripten, diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/region.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/region.rs index 59ce72eef48fc..e708900aad487 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/infer/region.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/region.rs @@ -1385,6 +1385,15 @@ fn suggest_precise_capturing<'tcx>( hir::GenericBound::Use(args, span) => Some((args, span)), _ => None, }) { + // '_ is an elision marker, not a lifetime name, so it cannot appear twice in a + // capture list. the branch for opaques with no use<..> yet already skips captured + // lifetimes; do the same here to avoid suggesting use<'_, '_>. + if args.iter().any(|arg| { + matches!(arg, hir::PreciseCapturingArg::Lifetime(lt) if lt.ident.name == new_lifetime) + }) { + return; + } + let last_lifetime_span = args.iter().rev().find_map(|arg| match arg { hir::PreciseCapturingArg::Lifetime(lt) => Some(lt.ident.span), _ => None, diff --git a/library/std/src/fs.rs b/library/std/src/fs.rs index 65b8ed634bc05..3f0f276551b45 100644 --- a/library/std/src/fs.rs +++ b/library/std/src/fs.rs @@ -1637,6 +1637,8 @@ impl Dir { /// Queries metadata about the underlying directory. /// + /// This is equivalent to `dir.metadata(".")` but can be more efficient. + /// /// # Examples /// /// ```no_run @@ -1645,13 +1647,13 @@ impl Dir { /// /// fn main() -> std::io::Result<()> { /// let dir = Dir::open("foo")?; - /// let metadata = dir.metadata()?; + /// let metadata = dir.self_metadata()?; /// Ok(()) /// } /// ``` #[unstable(feature = "dirfd", issue = "120426")] - pub fn metadata(&self) -> io::Result { - self.inner.metadata().map(Metadata) + pub fn self_metadata(&self) -> io::Result { + self.inner.self_metadata().map(Metadata) } /// Attempts to open a file in read-only mode relative to this directory. @@ -1891,6 +1893,54 @@ impl Dir { pub fn try_clone(&self) -> io::Result { Ok(Dir { inner: self.inner.duplicate()? }) } + + /// Queries the file system to get information about a file, directory, etc. relative to this + /// directory. + /// + /// This function will traverse symbolic links to query information about the destination file. + /// To query metadata about the path itself without following symbolic links, use + /// [`symlink_metadata`][Self::symlink_metadata]. + /// + /// # Examples + /// + /// ```no_run + /// #![feature(dirfd)] + /// use std::fs::Dir; + /// + /// fn main() -> std::io::Result<()> { + /// let dir = Dir::open("foo")?; + /// let metadata = dir.metadata("subdir/file.txt")?; + /// Ok(()) + /// } + /// ``` + #[unstable(feature = "dirfd", issue = "120426")] + pub fn metadata>(&self, path: P) -> io::Result { + self.inner.metadata(path.as_ref()).map(Metadata) + } + + /// Queries the file system to get information about a file, directory, etc. relative to this + /// directory. + /// + /// This function will return the [`Metadata`] of the exact path without traversing symbolic + /// links to a resolved destination file. Using this function on a path that is a file or + /// directory (not a symbolic link) will behave the same as [`metadata`][Self::metadata]. + /// + /// # Examples + /// + /// ```no_run + /// #![feature(dirfd)] + /// use std::fs::Dir; + /// + /// fn main() -> std::io::Result<()> { + /// let dir = Dir::open("foo")?; + /// let metadata = dir.symlink_metadata("subdir/file.txt")?; + /// Ok(()) + /// } + /// ``` + #[unstable(feature = "dirfd", issue = "120426")] + pub fn symlink_metadata>(&self, path: P) -> io::Result { + self.inner.symlink_metadata(path.as_ref()).map(Metadata) + } } impl AsInner for Dir { diff --git a/library/std/src/fs/tests.rs b/library/std/src/fs/tests.rs index db9a9f3f20b88..2760cd6b13f2a 100644 --- a/library/std/src/fs/tests.rs +++ b/library/std/src/fs/tests.rs @@ -13,7 +13,7 @@ use crate::os::unix::fs::symlink as symlink_file; use crate::os::unix::fs::symlink as junction_point; #[cfg(windows)] use crate::os::windows::fs::{OpenOptionsExt, junction_point, symlink_dir, symlink_file}; -use crate::path::Path; +use crate::path::{Path, PathBuf}; use crate::sync::Arc; use crate::test_helpers::{TempDir, tmpdir}; use crate::time::{Duration, Instant, SystemTime}; @@ -655,11 +655,6 @@ fn set_get_permissions_nofollows() { not(any(target_os = "espidf", target_os = "horizon", target_os = "wasi")) ))] fn set_get_permissions_nofollows_symlink() { - #[cfg(not(windows))] - use crate::os::unix::fs::symlink as symlink_file; - #[cfg(windows)] - use crate::os::windows::fs::symlink_file; - let tmpdir = tmpdir(); let filename = tmpdir.join("set_get_unix_permissions_file"); let symlink_name = tmpdir.join("set_get_unix_permissions"); @@ -3052,10 +3047,10 @@ fn test_dir_clone() { } #[test] -fn test_dir_metadata() { +fn test_dir_self_metadata() { let tmpdir = tmpdir(); let dir = check!(Dir::open(tmpdir.path())); - let metadata = check!(dir.metadata()); + let metadata = check!(dir.self_metadata()); assert!(metadata.is_dir()); } @@ -3143,3 +3138,27 @@ fn test_dir_open_dir() { check!(f.read_exact(&mut buf)); assert_eq!(b"baz", &buf); } + +#[test] +fn test_dir_metadata() { + let tmpdir = tmpdir(); + let dir = check!(Dir::open(tmpdir.path())); + check!(dir.create_dir("subdir")); + // FIXME: `/` does not work as path separator on Windows. + let barpath = PathBuf::from("subdir").join("bar.txt"); + drop(check!(dir.open_file_with(&barpath, &OpenOptions::new().create(true).write(true)))); + check!(symlink_file(&tmpdir.join("subdir/bar.txt"), &tmpdir.join("link"))); + + let metadata = check!(dir.metadata(&barpath)); + assert!(metadata.is_file()); + let metadata = check!(dir.metadata("subdir")); + assert!(metadata.is_dir()); + dir.metadata("does-not-exist").unwrap_err(); + + let metadata = check!(dir.metadata("link")); + assert!(metadata.is_file()); + assert!(!metadata.is_symlink()); + let metadata = check!(dir.symlink_metadata("link")); + assert!(!metadata.is_file()); + assert!(metadata.is_symlink()); +} diff --git a/library/std/src/sys/fs/common.rs b/library/std/src/sys/fs/common.rs index b2472665fe465..1724e6115462e 100644 --- a/library/std/src/sys/fs/common.rs +++ b/library/std/src/sys/fs/common.rs @@ -85,7 +85,7 @@ impl Dir { File::open(&self.path.join(path), opts) } - pub fn metadata(&self) -> io::Result { + pub fn self_metadata(&self) -> io::Result { self.path.metadata().map(|m| m.into_inner()) } @@ -108,6 +108,14 @@ impl Dir { pub fn remove_dir(&self, path: &Path) -> io::Result<()> { remove_dir(self.path.join(path)) } + + pub fn metadata(&self, path: &Path) -> io::Result { + self.path.join(path).metadata().map(|m| m.into_inner()) + } + + pub fn symlink_metadata(&self, path: &Path) -> io::Result { + self.path.join(path).symlink_metadata().map(|m| m.into_inner()) + } } impl fmt::Debug for Dir { diff --git a/library/std/src/sys/fs/unix.rs b/library/std/src/sys/fs/unix.rs index 46d5d33d23ac2..6d1c01be6078f 100644 --- a/library/std/src/sys/fs/unix.rs +++ b/library/std/src/sys/fs/unix.rs @@ -6,6 +6,39 @@ #[cfg(test)] mod tests; +// Import the file operations under consistent names. +cfg_select! { + not(any( + all(target_os = "linux", not(target_env = "musl")), + target_os = "android", + target_os = "hurd", + target_os = "l4re", + )) => { + use libc::{ + dirent as dirent64, fstat as fstat64, ftruncate as ftruncate64, lseek as lseek64, + lstat as lstat64, off_t as off64_t, open as open64, stat as stat64, + }; + } + target_os = "android" => { + // Android's `stat`, `dirent`, and related functions are always 64-bit LFS compatible, and + // `open` already implies `O_LARGEFILE`, so all those don't need to follow Linux. + // However, we still need off64_t, ftruncate64, and lseek64. + use libc::{ + dirent as dirent64, fstat as fstat64, ftruncate64, lseek64, lstat as lstat64, off64_t, + open as open64, stat as stat64, + }; + } + target_os = "l4re" => { + use libc::{ + dirent64, fstat as fstat64, ftruncate as ftruncate64, lseek as lseek64, + lstat as lstat64, off_t as off64_t, open as open64, stat as stat64, + }; + } + _ => { + use libc::{dirent64, fstat64, ftruncate64, lseek64, lstat64, off64_t, open64, stat64}; + } +} + #[cfg(all(target_os = "linux", target_env = "gnu"))] use libc::c_char; #[cfg(any( @@ -17,33 +50,7 @@ use libc::c_char; target_vendor = "apple", ))] use libc::dirfd; -#[cfg(any(target_os = "fuchsia", target_os = "illumos", target_vendor = "apple"))] -use libc::fstatat as fstatat64; -#[cfg(any(all(target_os = "linux", not(target_env = "musl")), target_os = "hurd"))] -use libc::fstatat64; use libc::{c_int, mode_t}; -#[cfg(target_os = "android")] -use libc::{ - dirent as dirent64, fstat as fstat64, fstatat as fstatat64, ftruncate64, lseek64, - lstat as lstat64, off64_t, open as open64, stat as stat64, -}; -#[cfg(not(any( - all(target_os = "linux", not(target_env = "musl")), - target_os = "android", - target_os = "hurd", - target_os = "l4re", -)))] -use libc::{ - dirent as dirent64, fstat as fstat64, ftruncate as ftruncate64, lseek as lseek64, - lstat as lstat64, off_t as off64_t, open as open64, stat as stat64, -}; -#[cfg(target_os = "l4re")] -use libc::{ - dirent64, fstat as fstat64, ftruncate as ftruncate64, lseek as lseek64, lstat as lstat64, - off_t as off64_t, open as open64, stat as stat64, -}; -#[cfg(any(all(target_os = "linux", not(target_env = "musl")), target_os = "hurd"))] -use libc::{dirent64, fstat64, ftruncate64, lseek64, lstat64, off64_t, open64, stat64}; use crate::ffi::{CStr, OsStr, OsString}; use crate::fmt::{self, Write as _}; @@ -1016,89 +1023,61 @@ impl DirEntry { self.file_name_os_str().to_os_string() } - #[cfg(all( - any( - all(target_os = "linux", not(target_env = "musl")), - target_os = "android", - target_os = "fuchsia", - target_os = "hurd", - target_os = "illumos", - target_vendor = "apple", - ), - not(miri) // no dirfd on Miri - ))] pub fn metadata(&self) -> io::Result { - let fd = cvt(unsafe { dirfd(self.dir.dirp.0) })?; - let name = self.name.as_ptr(); - - cfg_has_statx! { - if let Some(ret) = unsafe { try_statx( - fd, - name, - libc::AT_SYMLINK_NOFOLLOW | libc::AT_STATX_SYNC_AS_STAT, - libc::STATX_BASIC_STATS | libc::STATX_BTIME, - ) } { - return ret; - } - } + cfg_select! { + // Use directory handle where possible + all( + any( + all(target_os = "linux", not(target_env = "musl")), + target_os = "android", + target_os = "fuchsia", + target_os = "hurd", + target_os = "illumos", + target_vendor = "apple", + ), + not(miri) // no dirfd on Miri + ) => { + let fd = cvt(unsafe { dirfd(self.dir.dirp.0) })?; - let mut stat: stat64 = unsafe { mem::zeroed() }; - cvt(unsafe { fstatat64(fd, name, &mut stat, libc::AT_SYMLINK_NOFOLLOW) })?; - Ok(FileAttr::from_stat64(stat)) - } + // Make this FD into a directory handle. We don't actually drop it, + // so having an `OwnedFd` is fine. + let dir_handle = + mem::ManuallyDrop::new(dir::Dir(unsafe { OwnedFd::from_raw_fd(fd) })); - #[cfg(any( - not(any( - all(target_os = "linux", not(target_env = "musl")), - target_os = "android", - target_os = "fuchsia", - target_os = "hurd", - target_os = "illumos", - target_vendor = "apple", - )), - miri // no dirfd on Miri - ))] - pub fn metadata(&self) -> io::Result { - run_path_with_cstr(&self.path(), &lstat) - } + dir_handle.metadata_c(&self.name, /* symlink_nofollow */ true) + } - #[cfg(any( - target_os = "solaris", - target_os = "illumos", - target_os = "haiku", - target_os = "vxworks", - target_os = "aix", - target_os = "nto", - target_os = "qnx", - target_os = "vita", - target_os = "l4re", - ))] - pub fn file_type(&self) -> io::Result { - self.metadata().map(|m| m.file_type()) + // Fallback based on path + _ => run_path_with_cstr(&self.path(), &lstat), + } } - #[cfg(not(any( - target_os = "solaris", - target_os = "illumos", - target_os = "haiku", - target_os = "vxworks", - target_os = "aix", - target_os = "nto", - target_os = "qnx", - target_os = "vita", - target_os = "l4re", - )))] pub fn file_type(&self) -> io::Result { + // Use `entry.d_type` if available. + #[cfg(not(any( + target_os = "solaris", + target_os = "illumos", + target_os = "haiku", + target_os = "vxworks", + target_os = "aix", + target_os = "nto", + target_os = "qnx", + target_os = "vita", + target_os = "l4re", + )))] match self.entry.d_type { - libc::DT_CHR => Ok(FileType { mode: libc::S_IFCHR }), - libc::DT_FIFO => Ok(FileType { mode: libc::S_IFIFO }), - libc::DT_LNK => Ok(FileType { mode: libc::S_IFLNK }), - libc::DT_REG => Ok(FileType { mode: libc::S_IFREG }), - libc::DT_SOCK => Ok(FileType { mode: libc::S_IFSOCK }), - libc::DT_DIR => Ok(FileType { mode: libc::S_IFDIR }), - libc::DT_BLK => Ok(FileType { mode: libc::S_IFBLK }), - _ => self.metadata().map(|m| m.file_type()), + libc::DT_CHR => return Ok(FileType { mode: libc::S_IFCHR }), + libc::DT_FIFO => return Ok(FileType { mode: libc::S_IFIFO }), + libc::DT_LNK => return Ok(FileType { mode: libc::S_IFLNK }), + libc::DT_REG => return Ok(FileType { mode: libc::S_IFREG }), + libc::DT_SOCK => return Ok(FileType { mode: libc::S_IFSOCK }), + libc::DT_DIR => return Ok(FileType { mode: libc::S_IFDIR }), + libc::DT_BLK => return Ok(FileType { mode: libc::S_IFBLK }), + _ => {} } + + // Fall back to loading the metadata. + self.metadata().map(|m| m.file_type()) } pub fn ino(&self) -> u64 { diff --git a/library/std/src/sys/fs/unix/dir.rs b/library/std/src/sys/fs/unix/dir.rs index cf0dece265054..dd01db58c600d 100644 --- a/library/std/src/sys/fs/unix/dir.rs +++ b/library/std/src/sys/fs/unix/dir.rs @@ -4,16 +4,16 @@ cfg_select! { not(any( all(target_os = "linux", not(target_env = "musl")), target_os = "l4re", - target_os = "android", target_os = "hurd", )) => { - use libc::{open as open64, openat as openat64}; + use libc::{fstatat as fstatat64, openat as openat64}; } _ => { - use libc::{open64, openat64}; + use libc::{fstatat64, openat64}; } } +use super::{open64, stat64}; use crate::ffi::CStr; use crate::os::fd::{AsFd, BorrowedFd, IntoRawFd, OwnedFd, RawFd}; #[cfg(target_family = "unix")] @@ -26,7 +26,7 @@ use crate::sys::fs::OpenOptions; use crate::sys::fs::unix::{File, FileAttr, debug_path_fd}; use crate::sys::helpers::run_path_with_cstr; use crate::sys::{AsInner, FromInner, IntoInner, cvt, cvt_r}; -use crate::{fmt, fs, io}; +use crate::{fmt, fs, io, mem}; const TRAVERSE_DIRECTORY: i32 = cfg_select! { @@ -36,7 +36,7 @@ const TRAVERSE_DIRECTORY: i32 = _ => libc::O_RDONLY, }; -pub struct Dir(OwnedFd); +pub struct Dir(pub(super) OwnedFd); impl Dir { pub fn open(path: &Path, opts: &OpenOptions) -> io::Result { @@ -57,7 +57,7 @@ impl Dir { .map(File) } - pub fn metadata(&self) -> io::Result { + pub fn self_metadata(&self) -> io::Result { // Reuse the implementation for files, which should work for all FDs. let fd = self.0.as_raw_fd(); let f = core::mem::ManuallyDrop::new(File( @@ -68,7 +68,7 @@ impl Dir { } pub fn remove_file(&self, path: &Path) -> io::Result<()> { - run_path_with_cstr(path, &|path| self.remove_c(path, false)) + run_path_with_cstr(path, &|path| self.remove_c(path, /* remove_dir */ false)) } pub fn rename(&self, from: &Path, to_dir: &Self, to: &Path) -> io::Result<()> { @@ -86,7 +86,17 @@ impl Dir { } pub fn remove_dir(&self, path: &Path) -> io::Result<()> { - run_path_with_cstr(path, &|path| self.remove_c(path, true)) + run_path_with_cstr(path, &|path| self.remove_c(path, /* remove_dir */ true)) + } + + pub fn metadata(&self, path: &Path) -> io::Result { + run_path_with_cstr(path, &|path| { + self.metadata_c(path, /* symlink_nofollow */ false) + }) + } + + pub fn symlink_metadata(&self, path: &Path) -> io::Result { + run_path_with_cstr(path, &|path| self.metadata_c(path, /* symlink_nofollow */ true)) } fn open_with_c(path: &CStr, opts: &OpenOptions) -> io::Result { @@ -143,6 +153,26 @@ impl Dir { fn create_dir_c(&self, path: &CStr) -> io::Result<()> { cvt(unsafe { mkdirat(self.0.as_raw_fd(), path.as_ptr(), 0o777) }).map(|_| ()) } + + pub(super) fn metadata_c(&self, path: &CStr, symlink_nofollow: bool) -> io::Result { + let fd = self.0.as_raw_fd(); + let flag = if symlink_nofollow { libc::AT_SYMLINK_NOFOLLOW } else { 0 }; + + cfg_has_statx! { + if let Some(ret) = unsafe { super::try_statx( + fd, + path.as_ptr(), + flag | libc::AT_STATX_SYNC_AS_STAT, + libc::STATX_BASIC_STATS | libc::STATX_BTIME, + ) } { + return ret; + } + } + + let mut stat: stat64 = unsafe { mem::zeroed() }; + cvt(unsafe { fstatat64(fd, path.as_ptr(), &mut stat, flag) })?; + Ok(FileAttr::from_stat64(stat)) + } } impl fmt::Debug for Dir { diff --git a/library/std/src/sys/fs/windows.rs b/library/std/src/sys/fs/windows.rs index 399c0749bc21e..473d92aface26 100644 --- a/library/std/src/sys/fs/windows.rs +++ b/library/std/src/sys/fs/windows.rs @@ -1454,7 +1454,9 @@ pub fn link(_original: &WCStr, _link: &WCStr) -> io::Result<()> { pub fn stat(path: &WCStr) -> io::Result { match metadata(path, ReparsePoint::Follow) { Err(err) if err.raw_os_error() == Some(c::ERROR_CANT_ACCESS_FILE as i32) => { - if let Ok(attrs) = lstat(path) { + // Fallback to opening reparse points when following fails. Needed for UNIX domain + // sockets. See . + if let Ok(attrs) = metadata(path, ReparsePoint::Open) { if !attrs.file_type().is_symlink() { return Ok(attrs); } diff --git a/library/std/src/sys/fs/windows/dir.rs b/library/std/src/sys/fs/windows/dir.rs index 6f6d59af31628..60f8c2522de26 100644 --- a/library/std/src/sys/fs/windows/dir.rs +++ b/library/std/src/sys/fs/windows/dir.rs @@ -7,7 +7,7 @@ use crate::os::windows::io::{ }; use crate::path::Path; use crate::sys::api::{UnicodeStrRef, WinError}; -use crate::sys::fs::windows::debug_path_handle; +use crate::sys::fs::windows::{ReparsePoint, debug_path_handle}; use crate::sys::fs::{File, FileAttr, OpenOptions}; use crate::sys::handle::Handle; use crate::sys::path::{WCStr, with_native_path}; @@ -165,6 +165,7 @@ impl Dir { fn rename_native(&self, from: &[u16], to_dir: &Self, to: &[u16]) -> io::Result<()> { let mut opts = OpenOptions::new(); opts.access_mode(c::DELETE); + // FIXME: custom_flags is ignored by `open_file_native`! opts.custom_flags(c::FILE_FLAG_OPEN_REPARSE_POINT | c::FILE_FLAG_BACKUP_SEMANTICS); let handle = self.open_file_native(from, &opts, /* create_opt */ 0)?; // Calculate the layout of the `FILE_RENAME_INFORMATION` we pass to `NtSetInformationFile` @@ -222,7 +223,7 @@ impl Dir { .io_result() } - pub fn metadata(&self) -> io::Result { + pub fn self_metadata(&self) -> io::Result { // Reuse the implementation for files, which should work for all handles. let handle = self.handle.as_raw_handle(); let f = core::mem::ManuallyDrop::new(File { @@ -231,6 +232,45 @@ impl Dir { }); f.file_attr() } + + pub fn metadata(&self, path: &Path) -> io::Result { + let path = to_u16s_without_nul(path)?; + // Same as the `stat` logic used for `fs::metadata` + match self.metadata_native(&path, ReparsePoint::Follow) { + Err(err) if err.raw_os_error() == Some(c::ERROR_CANT_ACCESS_FILE as i32) => { + // Fallback to opening reparse points when following fails. Needed for UNIX domain + // sockets. See . + if let Ok(attrs) = self.metadata_native(&path, ReparsePoint::Open) { + if !attrs.file_type().is_symlink() { + return Ok(attrs); + } + } + Err(err) + } + result => result, + } + } + + pub fn symlink_metadata(&self, path: &Path) -> io::Result { + let path = to_u16s_without_nul(path)?; + self.metadata_native(&path, ReparsePoint::Open) + } + + fn metadata_native(&self, path: &[u16], reparse: ReparsePoint) -> io::Result { + let mut opts = OpenOptions::new(); + // the NT functions need at least c::FILE_READ_ATTRIBUTES + opts.access_mode(c::FILE_READ_ATTRIBUTES); + let create_opt = if reparse == ReparsePoint::Open { c::FILE_OPEN_REPARSE_POINT } else { 0 }; + + let name = UnicodeStrRef::from_slice(path); + let object_attributes = c::OBJECT_ATTRIBUTES { + RootDirectory: self.handle.as_raw_handle(), + ObjectName: name.as_ptr().cast_mut(), + ..c::OBJECT_ATTRIBUTES::with_length() + }; + let handle = unsafe { nt_create_file(&opts, &object_attributes, create_opt)? }; + File { handle }.file_attr() + } } impl fmt::Debug for Dir { diff --git a/src/doc/rustc/src/platform-support/wasm32-unknown-emscripten.md b/src/doc/rustc/src/platform-support/wasm32-unknown-emscripten.md index fcf6e42be224b..af95f9bccb94c 100644 --- a/src/doc/rustc/src/platform-support/wasm32-unknown-emscripten.md +++ b/src/doc/rustc/src/platform-support/wasm32-unknown-emscripten.md @@ -43,12 +43,13 @@ APIs, the `wasm32-unknown-emscripten` target may be preferable. ## Requirements This target is cross-compiled. The Emscripten compiler toolchain `emcc` must be -installed to link WASM binaries for this target. You can install `emcc` using: +installed to link WASM binaries for this target. Emscripten 4.0.0 or newer is +required. You can install `emcc` using: ```sh git clone https://github.com/emscripten-core/emsdk.git --depth 1 -./emsdk/emsdk install 3.1.68 -./emsdk/emsdk activate 3.1.68 +./emsdk/emsdk install latest +./emsdk/emsdk activate latest source ./emsdk/emsdk_env.sh ``` diff --git a/src/tools/miri/src/shims/files.rs b/src/tools/miri/src/shims/files.rs index d73ce57f64f19..cd7a7f4f0250b 100644 --- a/src/tools/miri/src/shims/files.rs +++ b/src/tools/miri/src/shims/files.rs @@ -524,7 +524,10 @@ impl FileDescription for DirHandle { fn metadata<'tcx>( &self, ) -> InterpResult<'tcx, Either, &'static str>> { - interp_ok(Either::Left(self.dir.metadata())) + #[cfg(bootstrap)] + return interp_ok(Either::Left(self.dir.metadata())); + #[cfg(not(bootstrap))] + return interp_ok(Either::Left(self.dir.self_metadata())); } } diff --git a/src/tools/miri/src/shims/windows/fs.rs b/src/tools/miri/src/shims/windows/fs.rs index 9aae6677faeb2..bab26d80dee70 100644 --- a/src/tools/miri/src/shims/windows/fs.rs +++ b/src/tools/miri/src/shims/windows/fs.rs @@ -225,7 +225,11 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { return interp_ok(Handle::Invalid); } }; - if !dir.metadata().unwrap().is_dir() { + #[cfg(bootstrap)] + let metadata = dir.metadata(); + #[cfg(not(bootstrap))] + let metadata = dir.self_metadata(); + if !metadata.unwrap().is_dir() { // This changed from a directory to a file. Retry. continue; } diff --git a/tests/ui/impl-trait/precise-capturing/duplicate-anon-lifetime-suggestion-issue-132199.rs b/tests/ui/impl-trait/precise-capturing/duplicate-anon-lifetime-suggestion-issue-132199.rs new file mode 100644 index 0000000000000..1ffe00db8edc3 --- /dev/null +++ b/tests/ui/impl-trait/precise-capturing/duplicate-anon-lifetime-suggestion-issue-132199.rs @@ -0,0 +1,24 @@ +//@ edition: 2024 + +// regression test for https://github.com/rust-lang/rust/issues/132199. +// in edition 2024 impl Trait captures every in-scope lifetime by default, so +// E0700 only fires once an explicit use<..> restricts the set. + +struct T; + +impl T { + // dup: opaque already has use<'_>, needs a second anonymous lifetime. + // before the fix, it suggested use<'_, '_>, which doesnt compile. + fn dup(&self, t: &T) -> impl Sized + use<'_> { (self, t) } + //~^ ERROR hidden type for `impl Sized` captures lifetime that does not appear in bounds + + // single_anon: use<> with one anonymous lifetime still gets use<'_>. + fn single_anon(&self) -> impl Sized + use<> { self } + //~^ ERROR hidden type for `impl Sized` captures lifetime that does not appear in bounds + + // named: a named lifetime still gets suggested by name. + fn named<'a>(&'a self, t: &'a T) -> impl Sized + use<> { (self, t) } + //~^ ERROR hidden type for `impl Sized` captures lifetime that does not appear in bounds +} + +fn main() {} diff --git a/tests/ui/impl-trait/precise-capturing/duplicate-anon-lifetime-suggestion-issue-132199.stderr b/tests/ui/impl-trait/precise-capturing/duplicate-anon-lifetime-suggestion-issue-132199.stderr new file mode 100644 index 0000000000000..2eebc773743b4 --- /dev/null +++ b/tests/ui/impl-trait/precise-capturing/duplicate-anon-lifetime-suggestion-issue-132199.stderr @@ -0,0 +1,40 @@ +error[E0700]: hidden type for `impl Sized` captures lifetime that does not appear in bounds + --> $DIR/duplicate-anon-lifetime-suggestion-issue-132199.rs:12:52 + | +LL | fn dup(&self, t: &T) -> impl Sized + use<'_> { (self, t) } + | -- -------------------- ^^^^^^^^^ + | | | + | | opaque type defined here + | hidden type `(&T, &T)` captures the anonymous lifetime defined here + +error[E0700]: hidden type for `impl Sized` captures lifetime that does not appear in bounds + --> $DIR/duplicate-anon-lifetime-suggestion-issue-132199.rs:16:51 + | +LL | fn single_anon(&self) -> impl Sized + use<> { self } + | ----- ------------------ ^^^^ + | | | + | | opaque type defined here + | hidden type `&T` captures the anonymous lifetime defined here + | +help: add `'_` to the `use<...>` bound to explicitly capture it + | +LL | fn single_anon(&self) -> impl Sized + use<'_> { self } + | ++ + +error[E0700]: hidden type for `impl Sized` captures lifetime that does not appear in bounds + --> $DIR/duplicate-anon-lifetime-suggestion-issue-132199.rs:20:62 + | +LL | fn named<'a>(&'a self, t: &'a T) -> impl Sized + use<> { (self, t) } + | -- ------------------ ^^^^^^^^^ + | | | + | | opaque type defined here + | hidden type `(&'a T, &'a T)` captures the lifetime `'a` as defined here + | +help: add `'a` to the `use<...>` bound to explicitly capture it + | +LL | fn named<'a>(&'a self, t: &'a T) -> impl Sized + use<'a> { (self, t) } + | ++ + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0700`. diff --git a/tests/ui/resolve/struct-shorthand-typo.fixed b/tests/ui/resolve/struct-shorthand-typo.fixed new file mode 100644 index 0000000000000..b06bb357d6838 --- /dev/null +++ b/tests/ui/resolve/struct-shorthand-typo.fixed @@ -0,0 +1,44 @@ +//! Regression test for +//! +//! Tests that typo suggestions for unresolved struct shorthands emit valid `field: value` syntax. + +#![allow(unused)] +#![allow(non_snake_case)] + +//@ run-rustfix + +const A: usize = 0; +struct S { a: usize } + +fn test_basic() { + S { a: A }; //~ ERROR cannot find value `a` in this scope +} + +const B: usize = 1; +struct Multi { a: usize, b: usize } +fn test_multi() { + Multi { a: 0, b: B }; //~ ERROR cannot find value `b` in this scope +} + +const C: usize = 2; +struct Fru { c: usize, d: usize } +fn test_fru() { + let base = Fru { c: 0, d: 0 }; + Fru { c: C, ..base }; //~ ERROR cannot find value `c` in this scope +} + +const D: usize = 3; +enum E { + StructVariant { d: usize }, +} +fn test_enum() { + E::StructVariant { d: D }; //~ ERROR cannot find value `d` in this scope +} + +struct Local { e: usize } +fn test_local() { + let E = 4; + Local { e: E }; //~ ERROR cannot find value `e` in this scope +} + +fn main() {} diff --git a/tests/ui/resolve/struct-shorthand-typo.rs b/tests/ui/resolve/struct-shorthand-typo.rs new file mode 100644 index 0000000000000..e8c1765751d73 --- /dev/null +++ b/tests/ui/resolve/struct-shorthand-typo.rs @@ -0,0 +1,44 @@ +//! Regression test for +//! +//! Tests that typo suggestions for unresolved struct shorthands emit valid `field: value` syntax. + +#![allow(unused)] +#![allow(non_snake_case)] + +//@ run-rustfix + +const A: usize = 0; +struct S { a: usize } + +fn test_basic() { + S { a }; //~ ERROR cannot find value `a` in this scope +} + +const B: usize = 1; +struct Multi { a: usize, b: usize } +fn test_multi() { + Multi { a: 0, b }; //~ ERROR cannot find value `b` in this scope +} + +const C: usize = 2; +struct Fru { c: usize, d: usize } +fn test_fru() { + let base = Fru { c: 0, d: 0 }; + Fru { c, ..base }; //~ ERROR cannot find value `c` in this scope +} + +const D: usize = 3; +enum E { + StructVariant { d: usize }, +} +fn test_enum() { + E::StructVariant { d }; //~ ERROR cannot find value `d` in this scope +} + +struct Local { e: usize } +fn test_local() { + let E = 4; + Local { e }; //~ ERROR cannot find value `e` in this scope +} + +fn main() {} diff --git a/tests/ui/resolve/struct-shorthand-typo.stderr b/tests/ui/resolve/struct-shorthand-typo.stderr new file mode 100644 index 0000000000000..620dbbb1eb51c --- /dev/null +++ b/tests/ui/resolve/struct-shorthand-typo.stderr @@ -0,0 +1,78 @@ +error[E0425]: cannot find value `a` in this scope + --> $DIR/struct-shorthand-typo.rs:14:9 + | +LL | S { a }; + | ^ not found in this scope + | +note: similarly named constant `A` defined here + --> $DIR/struct-shorthand-typo.rs:10:1 + | +LL | const A: usize = 0; + | ^^^^^^^^^^^^^^^^^^^ +help: a constant with a similar name exists + | +LL | S { a: A }; + | +++ + +error[E0425]: cannot find value `b` in this scope + --> $DIR/struct-shorthand-typo.rs:20:19 + | +LL | Multi { a: 0, b }; + | ^ not found in this scope + | +note: similarly named constant `B` defined here + --> $DIR/struct-shorthand-typo.rs:17:1 + | +LL | const B: usize = 1; + | ^^^^^^^^^^^^^^^^^^^ +help: a constant with a similar name exists + | +LL | Multi { a: 0, b: B }; + | +++ + +error[E0425]: cannot find value `c` in this scope + --> $DIR/struct-shorthand-typo.rs:27:11 + | +LL | Fru { c, ..base }; + | ^ not found in this scope + | +note: similarly named constant `C` defined here + --> $DIR/struct-shorthand-typo.rs:23:1 + | +LL | const C: usize = 2; + | ^^^^^^^^^^^^^^^^^^^ +help: a constant with a similar name exists + | +LL | Fru { c: C, ..base }; + | +++ + +error[E0425]: cannot find value `d` in this scope + --> $DIR/struct-shorthand-typo.rs:35:24 + | +LL | E::StructVariant { d }; + | ^ not found in this scope + | +note: similarly named constant `D` defined here + --> $DIR/struct-shorthand-typo.rs:30:1 + | +LL | const D: usize = 3; + | ^^^^^^^^^^^^^^^^^^^ +help: a constant with a similar name exists + | +LL | E::StructVariant { d: D }; + | +++ + +error[E0425]: cannot find value `e` in this scope + --> $DIR/struct-shorthand-typo.rs:41:13 + | +LL | Local { e }; + | ^ not found in this scope + | +help: a local variable with a similar name exists + | +LL | Local { e: E }; + | +++ + +error: aborting due to 5 previous errors + +For more information about this error, try `rustc --explain E0425`. diff --git a/tests/ui/structs/struct-fields-shorthand-unresolved.stderr b/tests/ui/structs/struct-fields-shorthand-unresolved.stderr index 9b4590c76afb1..d3360885fd523 100644 --- a/tests/ui/structs/struct-fields-shorthand-unresolved.stderr +++ b/tests/ui/structs/struct-fields-shorthand-unresolved.stderr @@ -6,9 +6,8 @@ LL | y | help: a local variable with a similar name exists | -LL - y -LL + x - | +LL | y: x + | +++ error: aborting due to 1 previous error diff --git a/tests/ui/typeck/issue-114423-ice-regression-in-suggestion.stderr b/tests/ui/typeck/issue-114423-ice-regression-in-suggestion.stderr index 9510001193d24..4bf3c6a174410 100644 --- a/tests/ui/typeck/issue-114423-ice-regression-in-suggestion.stderr +++ b/tests/ui/typeck/issue-114423-ice-regression-in-suggestion.stderr @@ -18,9 +18,8 @@ LL | let _ = RGB { r, g, b }; | help: a local variable with a similar name exists | -LL - let _ = RGB { r, g, b }; -LL + let _ = RGB { r, b, b }; - | +LL | let _ = RGB { r, g: b, b }; + | +++ error[E0308]: mismatched types --> $DIR/issue-114423-ice-regression-in-suggestion.rs:7:50