From 4983fdaada0c78d48e57f177453e5b954d5caad3 Mon Sep 17 00:00:00 2001 From: наб Date: Sun, 23 Feb 2025 19:40:57 +0100 Subject: libstd: init(): dup() subsequent /dev/nulls instead of opening them again This will be faster, and also it deduplicates the code so win/win The dup() is actually infallible here. But whatever. Before: poll([{fd=0, events=0}, {fd=1, events=0}, {fd=2, events=0}], 3, 0) = 1 ([{fd=2, revents=POLLNVAL}]) openat(AT_FDCWD, "/dev/null", O_RDWR) = 2 rt_sigaction(SIGPIPE, {sa_handler=SIG_IGN, sa_mask=[PIPE], sa_flags=SA_RESTORER|SA_RESTART, sa_restorer=0x7f5749313050}, {sa_handler=SIG_DFL, sa_mask=[], sa_flags=0}, 8) = 0 poll([{fd=0, events=0}, {fd=1, events=0}, {fd=2, events=0}], 3, 0) = 2 ([{fd=0, revents=POLLNVAL}, {fd=2, revents=POLLNVAL}]) openat(AT_FDCWD, "/dev/null", O_RDWR) = 0 openat(AT_FDCWD, "/dev/null", O_RDWR) = 2 rt_sigaction(SIGPIPE, {sa_handler=SIG_IGN, sa_mask=[PIPE], sa_flags=SA_RESTORER|SA_RESTART, sa_restorer=0x7efe12006050}, {sa_handler=SIG_DFL, sa_mask=[], sa_flags=0}, 8) = 0 poll([{fd=0, events=0}, {fd=1, events=0}, {fd=2, events=0}], 3, 0) = 3 ([{fd=0, revents=POLLNVAL}, {fd=1, revents=POLLNVAL}, {fd=2, revents=POLLNVAL}]) openat(AT_FDCWD, "/dev/null", O_RDWR) = 0 openat(AT_FDCWD, "/dev/null", O_RDWR) = 1 openat(AT_FDCWD, "/dev/null", O_RDWR) = 2 rt_sigaction(SIGPIPE, {sa_handler=SIG_IGN, sa_mask=[PIPE], sa_flags=SA_RESTORER|SA_RESTART, sa_restorer=0x7fc2dc7ca050}, {sa_handler=SIG_DFL, sa_mask=[], sa_flags=0}, 8) = 0 After: poll([{fd=0, events=0}, {fd=1, events=0}, {fd=2, events=0}], 3, 0) = 1 ([{fd=1, revents=POLLNVAL}]) openat(AT_FDCWD, "/dev/null", O_RDWR) = 1 rt_sigaction(SIGPIPE, {sa_handler=SIG_IGN, sa_mask=[PIPE], sa_flags=SA_RESTORER|SA_RESTART, sa_restorer=0x7f488a3fb050}, {sa_handler=SIG_DFL, sa_mask=[], sa_flags=0}, 8) = 0 poll([{fd=0, events=0}, {fd=1, events=0}, {fd=2, events=0}], 3, 0) = 2 ([{fd=1, revents=POLLNVAL}, {fd=2, revents=POLLNVAL}]) openat(AT_FDCWD, "/dev/null", O_RDWR) = 1 dup(1) = 2 rt_sigaction(SIGPIPE, {sa_handler=SIG_IGN, sa_mask=[PIPE], sa_flags=SA_RESTORER|SA_RESTART, sa_restorer=0x7f1a8943c050}, {sa_handler=SIG_DFL, sa_mask=[], sa_flags=0}, 8) = 0 poll([{fd=0, events=0}, {fd=1, events=0}, {fd=2, events=0}], 3, 0) = 3 ([{fd=0, revents=POLLNVAL}, {fd=1, revents=POLLNVAL}, {fd=2, revents=POLLNVAL}]) openat(AT_FDCWD, "/dev/null", O_RDWR) = 0 dup(0) = 1 dup(0) = 2 rt_sigaction(SIGPIPE, {sa_handler=SIG_IGN, sa_mask=[PIPE], sa_flags=SA_RESTORER|SA_RESTART, sa_restorer=0x7f4e3a4c7050}, {sa_handler=SIG_DFL, sa_mask=[], sa_flags=0}, 8) = 0 --- library/std/src/sys/pal/unix/mod.rs | 48 ++++++++++++++++++------------------- 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/library/std/src/sys/pal/unix/mod.rs b/library/std/src/sys/pal/unix/mod.rs index c0b56d8d2b2..ec61fcad7e8 100644 --- a/library/std/src/sys/pal/unix/mod.rs +++ b/library/std/src/sys/pal/unix/mod.rs @@ -61,6 +61,28 @@ pub unsafe fn init(argc: isize, argv: *const *const u8, sigpipe: u8) { } unsafe fn sanitize_standard_fds() { + let mut opened_devnull = -1; + let mut open_devnull = || { + #[cfg(not(all(target_os = "linux", target_env = "gnu")))] + use libc::open as open64; + #[cfg(all(target_os = "linux", target_env = "gnu"))] + use libc::open64; + + if opened_devnull != -1 { + if libc::dup(opened_devnull) != -1 { + return; + } + } + opened_devnull = open64(c"/dev/null".as_ptr(), libc::O_RDWR, 0); + if opened_devnull == -1 { + // If the stream is closed but we failed to reopen it, abort the + // process. Otherwise we wouldn't preserve the safety of + // operations on the corresponding Rust object Stdin, Stdout, or + // Stderr. + libc::abort(); + } + }; + // fast path with a single syscall for systems with poll() #[cfg(not(any( miri, @@ -76,11 +98,6 @@ pub unsafe fn init(argc: isize, argv: *const *const u8, sigpipe: u8) { target_vendor = "apple", )))] 'poll: { - #[cfg(not(all(target_os = "linux", target_env = "gnu")))] - use libc::open as open64; - #[cfg(all(target_os = "linux", target_env = "gnu"))] - use libc::open64; - use crate::sys::os::errno; let pfds: &mut [_] = &mut [ libc::pollfd { fd: 0, events: 0, revents: 0 }, @@ -108,13 +125,7 @@ pub unsafe fn init(argc: isize, argv: *const *const u8, sigpipe: u8) { if pfd.revents & libc::POLLNVAL == 0 { continue; } - if open64(c"/dev/null".as_ptr(), libc::O_RDWR, 0) == -1 { - // If the stream is closed but we failed to reopen it, abort the - // process. Otherwise we wouldn't preserve the safety of - // operations on the corresponding Rust object Stdin, Stdout, or - // Stderr. - libc::abort(); - } + open_devnull(); } return; } @@ -131,21 +142,10 @@ pub unsafe fn init(argc: isize, argv: *const *const u8, sigpipe: u8) { target_os = "vita", )))] { - #[cfg(not(all(target_os = "linux", target_env = "gnu")))] - use libc::open as open64; - #[cfg(all(target_os = "linux", target_env = "gnu"))] - use libc::open64; - use crate::sys::os::errno; for fd in 0..3 { if libc::fcntl(fd, libc::F_GETFD) == -1 && errno() == libc::EBADF { - if open64(c"/dev/null".as_ptr(), libc::O_RDWR, 0) == -1 { - // If the stream is closed but we failed to reopen it, abort the - // process. Otherwise we wouldn't preserve the safety of - // operations on the corresponding Rust object Stdin, Stdout, or - // Stderr. - libc::abort(); - } + open_devnull(); } } } -- cgit 1.4.1-3-g733a5 From c73cfb83b7b7855f19603420990775ebb3d32d95 Mon Sep 17 00:00:00 2001 From: наб Date: Wed, 26 Mar 2025 02:08:15 +0100 Subject: #[allow(dead_code)] --- library/std/src/sys/pal/unix/mod.rs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/library/std/src/sys/pal/unix/mod.rs b/library/std/src/sys/pal/unix/mod.rs index ec61fcad7e8..20b670fdd19 100644 --- a/library/std/src/sys/pal/unix/mod.rs +++ b/library/std/src/sys/pal/unix/mod.rs @@ -61,19 +61,21 @@ pub unsafe fn init(argc: isize, argv: *const *const u8, sigpipe: u8) { } unsafe fn sanitize_standard_fds() { + #[allow(dead_code)] let mut opened_devnull = -1; + #[allow(dead_code)] let mut open_devnull = || { #[cfg(not(all(target_os = "linux", target_env = "gnu")))] - use libc::open as open64; + use libc::open; #[cfg(all(target_os = "linux", target_env = "gnu"))] - use libc::open64; + use libc::open64 as open; if opened_devnull != -1 { if libc::dup(opened_devnull) != -1 { return; } } - opened_devnull = open64(c"/dev/null".as_ptr(), libc::O_RDWR, 0); + opened_devnull = open(c"/dev/null".as_ptr(), libc::O_RDWR, 0); if opened_devnull == -1 { // If the stream is closed but we failed to reopen it, abort the // process. Otherwise we wouldn't preserve the safety of -- cgit 1.4.1-3-g733a5 From ac761f28d15764887a0a1df4fa9067dcc7e0e94f Mon Sep 17 00:00:00 2001 From: наб Date: Sat, 12 Apr 2025 23:20:51 +0200 Subject: https://github.com/rust-lang/rust/pull/139717#issuecomment-2799036117 🥴 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- library/std/src/sys/pal/unix/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/library/std/src/sys/pal/unix/mod.rs b/library/std/src/sys/pal/unix/mod.rs index 20b670fdd19..ebfc92ebfee 100644 --- a/library/std/src/sys/pal/unix/mod.rs +++ b/library/std/src/sys/pal/unix/mod.rs @@ -61,9 +61,9 @@ pub unsafe fn init(argc: isize, argv: *const *const u8, sigpipe: u8) { } unsafe fn sanitize_standard_fds() { - #[allow(dead_code)] + #[allow(dead_code, unused_variables, unused_mut)] let mut opened_devnull = -1; - #[allow(dead_code)] + #[allow(dead_code, unused_variables, unused_mut)] let mut open_devnull = || { #[cfg(not(all(target_os = "linux", target_env = "gnu")))] use libc::open; -- cgit 1.4.1-3-g733a5 From 1ddb4d0062fbe55b88e55cffc00fe8b898512536 Mon Sep 17 00:00:00 2001 From: Michael Rieder Date: Fri, 4 Apr 2025 10:01:37 +0200 Subject: Fix parameter order for `_by()` variants of `min` / `max`/ `minmax` in `std::cmp` --- library/core/src/cmp.rs | 29 ++++++++++++++++++++++++++--- 1 file changed, 26 insertions(+), 3 deletions(-) diff --git a/library/core/src/cmp.rs b/library/core/src/cmp.rs index c315131f413..b407dc5f6ef 100644 --- a/library/core/src/cmp.rs +++ b/library/core/src/cmp.rs @@ -1552,6 +1552,9 @@ pub fn min(v1: T, v2: T) -> T { /// /// Returns the first argument if the comparison determines them to be equal. /// +/// The parameter order is preserved when calling the `compare` function, i.e. `v1` is +/// always passed as the first argument and `v2` as the second. +/// /// # Examples /// /// ``` @@ -1567,12 +1570,17 @@ pub fn min(v1: T, v2: T) -> T { /// /// let result = cmp::min_by(1, -1, abs_cmp); /// assert_eq!(result, 1); +/// +/// let rhs_abs_cmp = |x: &i32, y: &i32| x.cmp(&y.abs()); +/// +/// let result = cmp::min_by(-2, 1, rhs_abs_cmp); +/// assert_eq!(result, -2); /// ``` #[inline] #[must_use] #[stable(feature = "cmp_min_max_by", since = "1.53.0")] pub fn min_by Ordering>(v1: T, v2: T, compare: F) -> T { - if compare(&v2, &v1).is_lt() { v2 } else { v1 } + if compare(&v1, &v2).is_le() { v1 } else { v2 } } /// Returns the element that gives the minimum value from the specified function. @@ -1644,6 +1652,9 @@ pub fn max(v1: T, v2: T) -> T { /// /// Returns the second argument if the comparison determines them to be equal. /// +/// The parameter order is preserved when calling the `compare` function, i.e. `v1` is +/// always passed as the first argument and `v2` as the second. +/// /// # Examples /// /// ``` @@ -1659,12 +1670,17 @@ pub fn max(v1: T, v2: T) -> T { /// /// let result = cmp::max_by(1, -1, abs_cmp); /// assert_eq!(result, -1); +/// +/// let rhs_abs_cmp = |x: &i32, y: &i32| x.cmp(&y.abs()); +/// +/// let result = cmp::max_by(-2, 1, rhs_abs_cmp); +/// assert_eq!(result, 1); /// ``` #[inline] #[must_use] #[stable(feature = "cmp_min_max_by", since = "1.53.0")] pub fn max_by Ordering>(v1: T, v2: T, compare: F) -> T { - if compare(&v2, &v1).is_lt() { v1 } else { v2 } + if compare(&v1, &v2).is_gt() { v1 } else { v2 } } /// Returns the element that gives the maximum value from the specified function. @@ -1743,6 +1759,9 @@ where /// /// Returns `[v1, v2]` if the comparison determines them to be equal. /// +/// The parameter order is preserved when calling the `compare` function, i.e. `v1` is +/// always passed as the first argument and `v2` as the second. +/// /// # Examples /// /// ``` @@ -1755,6 +1774,10 @@ where /// assert_eq!(cmp::minmax_by(-1, 2, abs_cmp), [-1, 2]); /// assert_eq!(cmp::minmax_by(-2, 2, abs_cmp), [-2, 2]); /// +/// let rhs_abs_cmp = |x: &i32, y: &i32| x.cmp(&y.abs()); +/// +/// assert_eq!(cmp::minmax_by(-2, 1, rhs_abs_cmp), [-2, 1]); +/// /// // You can destructure the result using array patterns /// let [min, max] = cmp::minmax_by(-42, 17, abs_cmp); /// assert_eq!(min, 17); @@ -1767,7 +1790,7 @@ pub fn minmax_by(v1: T, v2: T, compare: F) -> [T; 2] where F: FnOnce(&T, &T) -> Ordering, { - if compare(&v2, &v1).is_lt() { [v2, v1] } else { [v1, v2] } + if compare(&v1, &v2).is_le() { [v1, v2] } else { [v2, v1] } } /// Returns minimum and maximum values with respect to the specified key function. -- cgit 1.4.1-3-g733a5 From aab1563d42ed7cf54b017ad414f95fa45526f566 Mon Sep 17 00:00:00 2001 From: Martin Habovstiak Date: Sun, 18 Dec 2022 19:36:39 +0100 Subject: `impl PartialEq<{str,String}> for {Path,PathBuf}` Comparison of paths and strings is expected to be possible and needed e.g. in tests. This change adds the impls os `PartialEq` between strings and paths, both owned and unsized, in both directions. ACP: https://github.com/rust-lang/libs-team/issues/151 --- library/std/src/path.rs | 65 ++++++++++++++++++++++ tests/ui/inference/issue-72616.stderr | 12 ++-- .../partialeq_suggest_swap_on_e0277.stderr | 2 + 3 files changed, 71 insertions(+), 8 deletions(-) diff --git a/library/std/src/path.rs b/library/std/src/path.rs index 1a4a7aa7448..30b12a0e796 100644 --- a/library/std/src/path.rs +++ b/library/std/src/path.rs @@ -2068,6 +2068,38 @@ impl PartialEq for PathBuf { } } +#[stable(feature = "eq_str_for_path", since = "CURRENT_RUSTC_VERSION")] +impl cmp::PartialEq for PathBuf { + #[inline] + fn eq(&self, other: &str) -> bool { + &*self == other + } +} + +#[stable(feature = "eq_str_for_path", since = "CURRENT_RUSTC_VERSION")] +impl cmp::PartialEq for str { + #[inline] + fn eq(&self, other: &PathBuf) -> bool { + other == self + } +} + +#[stable(feature = "eq_str_for_path", since = "CURRENT_RUSTC_VERSION")] +impl cmp::PartialEq for PathBuf { + #[inline] + fn eq(&self, other: &String) -> bool { + **self == **other + } +} + +#[stable(feature = "eq_str_for_path", since = "CURRENT_RUSTC_VERSION")] +impl cmp::PartialEq for String { + #[inline] + fn eq(&self, other: &PathBuf) -> bool { + other == self + } +} + #[stable(feature = "rust1", since = "1.0.0")] impl Hash for PathBuf { fn hash(&self, h: &mut H) { @@ -3242,6 +3274,39 @@ impl PartialEq for Path { } } +#[stable(feature = "eq_str_for_path", since = "CURRENT_RUSTC_VERSION")] +impl cmp::PartialEq for Path { + #[inline] + fn eq(&self, other: &str) -> bool { + let other: &OsStr = other.as_ref(); + self == other + } +} + +#[stable(feature = "eq_str_for_path", since = "CURRENT_RUSTC_VERSION")] +impl cmp::PartialEq for str { + #[inline] + fn eq(&self, other: &Path) -> bool { + other == self + } +} + +#[stable(feature = "eq_str_for_path", since = "CURRENT_RUSTC_VERSION")] +impl cmp::PartialEq for Path { + #[inline] + fn eq(&self, other: &String) -> bool { + self == &*other + } +} + +#[stable(feature = "eq_str_for_path", since = "CURRENT_RUSTC_VERSION")] +impl cmp::PartialEq for String { + #[inline] + fn eq(&self, other: &Path) -> bool { + other == self + } +} + #[stable(feature = "rust1", since = "1.0.0")] impl Hash for Path { fn hash(&self, h: &mut H) { diff --git a/tests/ui/inference/issue-72616.stderr b/tests/ui/inference/issue-72616.stderr index 31a0586301d..a271639996f 100644 --- a/tests/ui/inference/issue-72616.stderr +++ b/tests/ui/inference/issue-72616.stderr @@ -6,14 +6,10 @@ LL | if String::from("a") == "a".try_into().unwrap() {} | | | type must be known at this point | - = note: cannot satisfy `String: PartialEq<_>` - = help: the following types implement trait `PartialEq`: - `String` implements `PartialEq<&str>` - `String` implements `PartialEq` - `String` implements `PartialEq` - `String` implements `PartialEq>` - `String` implements `PartialEq` - `String` implements `PartialEq` + = note: multiple `impl`s satisfying `String: PartialEq<_>` found in the following crates: `alloc`, `std`: + - impl PartialEq for String; + - impl PartialEq for String; + - impl PartialEq for String; help: try using a fully qualified path to specify the expected types | LL - if String::from("a") == "a".try_into().unwrap() {} diff --git a/tests/ui/suggestions/partialeq_suggest_swap_on_e0277.stderr b/tests/ui/suggestions/partialeq_suggest_swap_on_e0277.stderr index ebe103ef19a..c5984f53f68 100644 --- a/tests/ui/suggestions/partialeq_suggest_swap_on_e0277.stderr +++ b/tests/ui/suggestions/partialeq_suggest_swap_on_e0277.stderr @@ -10,6 +10,8 @@ LL | String::from("Girls Band Cry") == T(String::from("Girls Band Cry")); `String` implements `PartialEq` `String` implements `PartialEq` `String` implements `PartialEq>` + `String` implements `PartialEq` + `String` implements `PartialEq` `String` implements `PartialEq` `String` implements `PartialEq` = note: `T` implements `PartialEq` -- cgit 1.4.1-3-g733a5 From 3688b33cb34f7fbee69a285cf4d76f45a227ef40 Mon Sep 17 00:00:00 2001 From: Samuel Tardieu Date: Tue, 11 Mar 2025 17:11:58 +0100 Subject: Move `adjust_derefs_manually_drop` into `clippy_utils` --- clippy_lints/src/dereference.rs | 11 +---------- clippy_utils/src/ty/mod.rs | 12 ++++++++++-- 2 files changed, 11 insertions(+), 12 deletions(-) diff --git a/clippy_lints/src/dereference.rs b/clippy_lints/src/dereference.rs index a22a2ee66d2..30f9a637410 100644 --- a/clippy_lints/src/dereference.rs +++ b/clippy_lints/src/dereference.rs @@ -1,12 +1,11 @@ use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_hir_and_then}; use clippy_utils::source::{snippet_with_applicability, snippet_with_context}; use clippy_utils::sugg::has_enclosing_paren; -use clippy_utils::ty::{implements_trait, is_manually_drop}; +use clippy_utils::ty::{adjust_derefs_manually_drop, implements_trait, is_manually_drop}; use clippy_utils::{ DefinedTy, ExprUseNode, expr_use_ctxt, get_parent_expr, is_block_like, is_lint_allowed, path_to_local, peel_middle_ty_refs, }; -use core::mem; use rustc_ast::util::parser::ExprPrecedence; use rustc_data_structures::fx::FxIndexMap; use rustc_errors::Applicability; @@ -707,14 +706,6 @@ fn try_parse_ref_op<'tcx>( )) } -// Checks if the adjustments contains a deref of `ManuallyDrop<_>` -fn adjust_derefs_manually_drop<'tcx>(adjustments: &'tcx [Adjustment<'tcx>], mut ty: Ty<'tcx>) -> bool { - adjustments.iter().any(|a| { - let ty = mem::replace(&mut ty, a.target); - matches!(a.kind, Adjust::Deref(Some(ref op)) if op.mutbl == Mutability::Mut) && is_manually_drop(ty) - }) -} - // Checks whether the type for a deref call actually changed the type, not just the mutability of // the reference. fn deref_method_same_type<'tcx>(result_ty: Ty<'tcx>, arg_ty: Ty<'tcx>) -> bool { diff --git a/clippy_utils/src/ty/mod.rs b/clippy_utils/src/ty/mod.rs index c50ad17bfad..f0402e2aff2 100644 --- a/clippy_utils/src/ty/mod.rs +++ b/clippy_utils/src/ty/mod.rs @@ -17,6 +17,7 @@ use rustc_lint::LateContext; use rustc_middle::mir::ConstValue; use rustc_middle::mir::interpret::Scalar; use rustc_middle::traits::EvaluationResult; +use rustc_middle::ty::adjustment::{Adjust, Adjustment}; use rustc_middle::ty::layout::ValidityRequirement; use rustc_middle::ty::{ self, AdtDef, AliasTy, AssocItem, AssocTag, Binder, BoundRegion, FnSig, GenericArg, GenericArgKind, GenericArgsRef, @@ -30,7 +31,7 @@ use rustc_trait_selection::traits::query::normalize::QueryNormalizeExt; use rustc_trait_selection::traits::{Obligation, ObligationCause}; use std::assert_matches::debug_assert_matches; use std::collections::hash_map::Entry; -use std::iter; +use std::{iter, mem}; use crate::path_res; use crate::paths::{PathNS, lookup_path_str}; @@ -1362,7 +1363,6 @@ pub fn is_slice_like<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { || matches!(ty.kind(), ty::Adt(adt_def, _) if cx.tcx.is_diagnostic_item(sym::Vec, adt_def.did())) } -/// Gets the index of a field by name. pub fn get_field_idx_by_name(ty: Ty<'_>, name: Symbol) -> Option { match *ty.kind() { ty::Adt(def, _) if def.is_union() || def.is_struct() => { @@ -1372,3 +1372,11 @@ pub fn get_field_idx_by_name(ty: Ty<'_>, name: Symbol) -> Option { _ => None, } } + +/// Checks if the adjustments contain a mutable dereference of a `ManuallyDrop<_>`. +pub fn adjust_derefs_manually_drop<'tcx>(adjustments: &'tcx [Adjustment<'tcx>], mut ty: Ty<'tcx>) -> bool { + adjustments.iter().any(|a| { + let ty = mem::replace(&mut ty, a.target); + matches!(a.kind, Adjust::Deref(Some(op)) if op.mutbl == Mutability::Mut) && is_manually_drop(ty) + }) +} -- cgit 1.4.1-3-g733a5 From 2cfbb05d6a9293aa2ef93bca98c2ec3d373cea6d Mon Sep 17 00:00:00 2001 From: Samuel Tardieu Date: Mon, 10 Mar 2025 18:30:12 +0100 Subject: Convert `deref_addrof` lint to late lint This is necessary in order to use type analysis in later commits. --- clippy_lints/src/lib.rs | 2 +- clippy_lints/src/reference.rs | 21 +++++++-------------- 2 files changed, 8 insertions(+), 15 deletions(-) diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 006145cc623..ceb423b02ee 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -717,7 +717,7 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) { store.register_late_pass(move |_| Box::new(trait_bounds::TraitBounds::new(conf))); store.register_late_pass(|_| Box::new(comparison_chain::ComparisonChain)); store.register_late_pass(move |tcx| Box::new(mut_key::MutableKeyType::new(tcx, conf))); - store.register_early_pass(|| Box::new(reference::DerefAddrOf)); + store.register_late_pass(|_| Box::new(reference::DerefAddrOf)); store.register_early_pass(|| Box::new(double_parens::DoubleParens)); let format_args = format_args_storage.clone(); store.register_late_pass(move |_| Box::new(format_impl::FormatImpl::new(format_args.clone()))); diff --git a/clippy_lints/src/reference.rs b/clippy_lints/src/reference.rs index 4bff37216ed..cb414d79119 100644 --- a/clippy_lints/src/reference.rs +++ b/clippy_lints/src/reference.rs @@ -1,8 +1,8 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::{SpanRangeExt, snippet_with_applicability}; -use rustc_ast::ast::{Expr, ExprKind, Mutability, UnOp}; use rustc_errors::Applicability; -use rustc_lint::{EarlyContext, EarlyLintPass}; +use rustc_hir::{Expr, ExprKind, Mutability, UnOp}; +use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; use rustc_span::{BytePos, Span}; @@ -37,17 +37,10 @@ declare_clippy_lint! { declare_lint_pass!(DerefAddrOf => [DEREF_ADDROF]); -fn without_parens(mut e: &Expr) -> &Expr { - while let ExprKind::Paren(ref child_e) = e.kind { - e = child_e; - } - e -} - -impl EarlyLintPass for DerefAddrOf { - fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &Expr) { - if let ExprKind::Unary(UnOp::Deref, ref deref_target) = e.kind - && let ExprKind::AddrOf(_, ref mutability, ref addrof_target) = without_parens(deref_target).kind +impl LateLintPass<'_> for DerefAddrOf { + fn check_expr(&mut self, cx: &LateContext<'_>, e: &Expr<'_>) { + if let ExprKind::Unary(UnOp::Deref, deref_target) = e.kind + && let ExprKind::AddrOf(_, mutability, addrof_target) = deref_target.kind // NOTE(tesuji): `*&` forces rustc to const-promote the array to `.rodata` section. // See #12854 for details. && !matches!(addrof_target.kind, ExprKind::Array(_)) @@ -79,7 +72,7 @@ impl EarlyLintPass for DerefAddrOf { }) }; - if *mutability == Mutability::Mut { + if mutability == Mutability::Mut { generate_snippet("mut") } else { generate_snippet("&") -- cgit 1.4.1-3-g733a5 From d78e7628d1376d1fe1feaff4079f590ba305aac9 Mon Sep 17 00:00:00 2001 From: binarycat Date: Fri, 25 Apr 2025 15:26:52 -0500 Subject: scrape-examples.js: give each function a signature --- src/librustdoc/html/static/js/scrape-examples.js | 33 +++++++++++++++++------- 1 file changed, 24 insertions(+), 9 deletions(-) diff --git a/src/librustdoc/html/static/js/scrape-examples.js b/src/librustdoc/html/static/js/scrape-examples.js index d641405c875..7ffa5d5b42a 100644 --- a/src/librustdoc/html/static/js/scrape-examples.js +++ b/src/librustdoc/html/static/js/scrape-examples.js @@ -1,7 +1,4 @@ -/* global addClass, hasClass, removeClass, onEachLazy */ - -// Eventually fix this. -// @ts-nocheck +/* global addClass, hasClass, removeClass, onEachLazy, nonnull */ "use strict"; @@ -14,7 +11,12 @@ const DEFAULT_MAX_LINES = 5; const HIDDEN_MAX_LINES = 10; - // Scroll code block to the given code location + /** + * Scroll code block to the given code location + * @param {HTMLElement} elt + * @param {[number, number]} loc + * @param {boolean} isHidden + */ function scrollToLoc(elt, loc, isHidden) { const lines = elt.querySelectorAll("[data-nosnippet]"); let scrollOffset; @@ -35,10 +37,15 @@ scrollOffset = offsetMid - halfHeight; } - lines[0].parentElement.scrollTo(0, scrollOffset); - elt.querySelector(".rust").scrollTo(0, scrollOffset); + nonnull(lines[0].parentElement).scrollTo(0, scrollOffset); + nonnull(elt.querySelector(".rust")).scrollTo(0, scrollOffset); } + /** + * @param {HTMLElement} parent + * @param {string} className + * @param {string} content + */ function createScrapeButton(parent, className, content) { const button = document.createElement("button"); button.className = className; @@ -50,14 +57,15 @@ window.updateScrapedExample = (example, buttonHolder) => { let locIndex = 0; const highlights = Array.prototype.slice.call(example.querySelectorAll(".highlight")); - const link = example.querySelector(".scraped-example-title a"); + const link = nonnull(example.querySelector(".scraped-example-title a")); let expandButton = null; if (!example.classList.contains("expanded")) { expandButton = createScrapeButton(buttonHolder, "expand", "Show all"); } - const isHidden = example.parentElement.classList.contains("more-scraped-examples"); + const isHidden = nonnull(example.parentElement).classList.contains("more-scraped-examples"); + // @ts-expect-error const locs = example.locs; if (locs.length > 1) { const next = createScrapeButton(buttonHolder, "next", "Next usage"); @@ -106,7 +114,14 @@ } }; + /** + * Intitialize the `locs` field + * + * @param {HTMLElement} example + * @param {boolean} isHidden + */ function setupLoc(example, isHidden) { + // @ts-expect-error example.locs = JSON.parse(example.attributes.getNamedItem("data-locs").textContent); // Start with the first example in view scrollToLoc(example, example.locs[0][0], isHidden); -- cgit 1.4.1-3-g733a5 From b4c77e12408a3ae05ef6779eff0d8af3613716e4 Mon Sep 17 00:00:00 2001 From: binarycat Date: Fri, 25 Apr 2025 16:16:18 -0500 Subject: rustdoc js: add ScrapedLoc type --- src/librustdoc/html/static/js/rustdoc.d.ts | 7 +++++++ src/librustdoc/html/static/js/scrape-examples.js | 13 +++++++++---- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/src/librustdoc/html/static/js/rustdoc.d.ts b/src/librustdoc/html/static/js/rustdoc.d.ts index 0d2e19e019f..a10f7241492 100644 --- a/src/librustdoc/html/static/js/rustdoc.d.ts +++ b/src/librustdoc/html/static/js/rustdoc.d.ts @@ -491,4 +491,11 @@ declare namespace rustdoc { options?: string[], default: string | boolean, } + + /** + * Single element in the data-locs field of a scraped example. + * First field is the start and end char index, + * other fields seem to be unused. + */ + type ScrapedLoc = [[number, number], string, string] } diff --git a/src/librustdoc/html/static/js/scrape-examples.js b/src/librustdoc/html/static/js/scrape-examples.js index 7ffa5d5b42a..32642799d88 100644 --- a/src/librustdoc/html/static/js/scrape-examples.js +++ b/src/librustdoc/html/static/js/scrape-examples.js @@ -18,6 +18,9 @@ * @param {boolean} isHidden */ function scrollToLoc(elt, loc, isHidden) { + /** @type {HTMLElement[]} */ + // blocked on https://github.com/microsoft/TypeScript/issues/29037 + // @ts-expect-error const lines = elt.querySelectorAll("[data-nosnippet]"); let scrollOffset; @@ -57,6 +60,8 @@ window.updateScrapedExample = (example, buttonHolder) => { let locIndex = 0; const highlights = Array.prototype.slice.call(example.querySelectorAll(".highlight")); + + /** @type {HTMLAnchorElement} */ const link = nonnull(example.querySelector(".scraped-example-title a")); let expandButton = null; @@ -72,6 +77,7 @@ const prev = createScrapeButton(buttonHolder, "prev", "Previous usage"); // Toggle through list of examples in a given file + /** @type {function(function(): void): void} */ const onChangeLoc = changeIndex => { removeClass(highlights[locIndex], "focus"); changeIndex(); @@ -117,14 +123,13 @@ /** * Intitialize the `locs` field * - * @param {HTMLElement} example + * @param {HTMLElement & {locs?: rustdoc.ScrapedLoc[]}} example * @param {boolean} isHidden */ function setupLoc(example, isHidden) { - // @ts-expect-error - example.locs = JSON.parse(example.attributes.getNamedItem("data-locs").textContent); + const locs = example.locs = JSON.parse(nonnull(nonnull(example.attributes.getNamedItem("data-locs")).textContent)); // Start with the first example in view - scrollToLoc(example, example.locs[0][0], isHidden); + scrollToLoc(example, locs[0][0], isHidden); } const firstExamples = document.querySelectorAll(".scraped-example-list > .scraped-example"); -- cgit 1.4.1-3-g733a5 From 35ba7dd217f3a1af420189927205d89cc2c02718 Mon Sep 17 00:00:00 2001 From: binarycat Date: Fri, 25 Apr 2025 16:28:50 -0500 Subject: rustdoc js: add rustdoc.ScrapedLoc type --- src/librustdoc/html/static/js/rustdoc.d.ts | 2 ++ src/librustdoc/html/static/js/scrape-examples.js | 4 +++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/librustdoc/html/static/js/rustdoc.d.ts b/src/librustdoc/html/static/js/rustdoc.d.ts index a10f7241492..2c0d47447c0 100644 --- a/src/librustdoc/html/static/js/rustdoc.d.ts +++ b/src/librustdoc/html/static/js/rustdoc.d.ts @@ -496,6 +496,8 @@ declare namespace rustdoc { * Single element in the data-locs field of a scraped example. * First field is the start and end char index, * other fields seem to be unused. + * + * generated by `render_call_locations` in `render/mod.rs`. */ type ScrapedLoc = [[number, number], string, string] } diff --git a/src/librustdoc/html/static/js/scrape-examples.js b/src/librustdoc/html/static/js/scrape-examples.js index 32642799d88..5dab4cc0156 100644 --- a/src/librustdoc/html/static/js/scrape-examples.js +++ b/src/librustdoc/html/static/js/scrape-examples.js @@ -127,7 +127,9 @@ * @param {boolean} isHidden */ function setupLoc(example, isHidden) { - const locs = example.locs = JSON.parse(nonnull(nonnull(example.attributes.getNamedItem("data-locs")).textContent)); + const locs_str = example.attributes.getNamedItem("data-locs")).textContent; + const locs = example.locs = + JSON.parse(nonnull(nonnull(locs_str)); // Start with the first example in view scrollToLoc(example, locs[0][0], isHidden); } -- cgit 1.4.1-3-g733a5 From 8799d3dd97e38af3e31a2e76369d647e01b8a423 Mon Sep 17 00:00:00 2001 From: binarycat Date: Mon, 28 Apr 2025 18:44:46 -0500 Subject: fix typo --- src/librustdoc/html/static/js/scrape-examples.js | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/librustdoc/html/static/js/scrape-examples.js b/src/librustdoc/html/static/js/scrape-examples.js index 5dab4cc0156..7a231dc3b77 100644 --- a/src/librustdoc/html/static/js/scrape-examples.js +++ b/src/librustdoc/html/static/js/scrape-examples.js @@ -1,4 +1,4 @@ -/* global addClass, hasClass, removeClass, onEachLazy, nonnull */ + /* global addClass, hasClass, removeClass, onEachLazy, nonnull */ "use strict"; @@ -127,9 +127,10 @@ * @param {boolean} isHidden */ function setupLoc(example, isHidden) { - const locs_str = example.attributes.getNamedItem("data-locs")).textContent; - const locs = example.locs = - JSON.parse(nonnull(nonnull(locs_str)); + const locs_str = example.attributes.getNamedItem("data-locs").textContent; + const locs = + JSON.parse(nonnull(nonnull(locs_str))); + example.locs = locs; // Start with the first example in view scrollToLoc(example, locs[0][0], isHidden); } -- cgit 1.4.1-3-g733a5 From b06113dc5cd6fbb2cc63918f9e861929537f41fd Mon Sep 17 00:00:00 2001 From: binarycat Date: Thu, 22 May 2025 16:14:15 -0500 Subject: scrape-examples.js: add another nonnull() invokation --- src/librustdoc/html/static/js/scrape-examples.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/librustdoc/html/static/js/scrape-examples.js b/src/librustdoc/html/static/js/scrape-examples.js index 7a231dc3b77..ec472666ce1 100644 --- a/src/librustdoc/html/static/js/scrape-examples.js +++ b/src/librustdoc/html/static/js/scrape-examples.js @@ -127,7 +127,7 @@ * @param {boolean} isHidden */ function setupLoc(example, isHidden) { - const locs_str = example.attributes.getNamedItem("data-locs").textContent; + const locs_str = nonnull(example.attributes.getNamedItem("data-locs")).textContent; const locs = JSON.parse(nonnull(nonnull(locs_str))); example.locs = locs; -- cgit 1.4.1-3-g733a5 From 30721b0e909d7451a37bbb4ae371e1c909a0cbaf Mon Sep 17 00:00:00 2001 From: jyn Date: Mon, 26 May 2025 20:10:17 -0400 Subject: Add stubs for environment variables; document some of the important ones This uses a very hacky regex that will probably miss some variables. But having some docs seems better than none at all. This uses a very hacky regex that will probably miss some variables. But having some docs seems better than none at all. In particular, this generates stubs for the following env vars: - COLORTERM - QNX_TARGET - RUST_BACKTRACE - RUSTC_BLESS - RUSTC_BOOTSTRAP - RUSTC_BREAK_ON_ICE - RUSTC_CTFE_BACKTRACE - RUSTC_FORCE_RUSTC_VERSION - RUSTC_GRAPHVIZ_FONT - RUSTC_ICE - RUSTC_LOG - RUSTC_OVERRIDE_VERSION_STRING - RUSTC_RETRY_LINKER_ON_SEGFAULT - RUSTC_TRANSLATION_NO_DEBUG_ASSERT - RUST_DEP_GRAPH_FILTER - RUST_DEP_GRAPH - RUST_FORBID_DEP_GRAPH_EDGE - RUST_MIN_STACK - RUST_TARGET_PATH - SDKROOT - TERM - UNSTABLE_RUSTDOC_TEST_LINE - UNSTABLE_RUSTDOC_TEST_PATH [rendered](![screenshot of unstable-book running locally, with 14 environment variables shown in the sidebar](https://github.com/user-attachments/assets/8238d094-fb7a-456f-ad43-7c07aa2c44dd)) --- .../compiler-environment-variables/COLORTERM.md | 5 ++++ .../compiler-environment-variables/QNX_TARGET.md | 11 +++++++ .../src/compiler-environment-variables/SDKROOT.md | 6 ++++ .../src/compiler-environment-variables/TERM.md | 5 ++++ .../src/compiler-flags/terminal-urls.md | 13 ++++++++ src/tools/tidy/src/features.rs | 35 ++++++++++++++++++++++ src/tools/tidy/src/unstable_book.rs | 2 ++ src/tools/unstable-book-gen/src/main.rs | 29 ++++++++++++++---- src/tools/unstable-book-gen/src/stub-env-var.md | 9 ++++++ 9 files changed, 109 insertions(+), 6 deletions(-) create mode 100644 src/doc/unstable-book/src/compiler-environment-variables/COLORTERM.md create mode 100644 src/doc/unstable-book/src/compiler-environment-variables/QNX_TARGET.md create mode 100644 src/doc/unstable-book/src/compiler-environment-variables/SDKROOT.md create mode 100644 src/doc/unstable-book/src/compiler-environment-variables/TERM.md create mode 100644 src/doc/unstable-book/src/compiler-flags/terminal-urls.md create mode 100644 src/tools/unstable-book-gen/src/stub-env-var.md diff --git a/src/doc/unstable-book/src/compiler-environment-variables/COLORTERM.md b/src/doc/unstable-book/src/compiler-environment-variables/COLORTERM.md new file mode 100644 index 00000000000..f5be63b796c --- /dev/null +++ b/src/doc/unstable-book/src/compiler-environment-variables/COLORTERM.md @@ -0,0 +1,5 @@ +# `COLORTERM` + +This environment variable is used by [`-Zterminal-urls`] to detect if URLs are supported by the terminal emulator. + +[`-Zterminal-urls`]: ../compiler-flags/terminal-urls.html diff --git a/src/doc/unstable-book/src/compiler-environment-variables/QNX_TARGET.md b/src/doc/unstable-book/src/compiler-environment-variables/QNX_TARGET.md new file mode 100644 index 00000000000..cc98fc3b3b2 --- /dev/null +++ b/src/doc/unstable-book/src/compiler-environment-variables/QNX_TARGET.md @@ -0,0 +1,11 @@ +# `QNX_TARGET` + +---- + +This environment variable is mandatory when linking on `nto-qnx*_iosock` platforms. It is used to determine an `-L` path to pass to the QNX linker. + +You should [set this variable] by running `source qnxsdp-env.sh`. +See [the QNX docs] for more background information. + +[set this variable]: https://www.qnx.com/developers/docs/qsc/com.qnx.doc.qsc.inst_larg_org/topic/build_server_developer_steps.html +[the QNX docs]: https://www.qnx.com/developers/docs/7.1/index.html#com.qnx.doc.neutrino.io_sock/topic/migrate_app.html. diff --git a/src/doc/unstable-book/src/compiler-environment-variables/SDKROOT.md b/src/doc/unstable-book/src/compiler-environment-variables/SDKROOT.md new file mode 100644 index 00000000000..b00c2f773ad --- /dev/null +++ b/src/doc/unstable-book/src/compiler-environment-variables/SDKROOT.md @@ -0,0 +1,6 @@ +# `SDKROOT` + +This environment variable is used on MacOS targets. +It is passed through to the linker (either as `-isysroot` or `-syslibroot`) without changes. + +Note that this variable is not always respected. When the SDKROOT is clearly wrong (e.g. when the platform of the SDK does not match the `--target` used by rustc), this is ignored and rustc does its own detection. diff --git a/src/doc/unstable-book/src/compiler-environment-variables/TERM.md b/src/doc/unstable-book/src/compiler-environment-variables/TERM.md new file mode 100644 index 00000000000..9208fd378a3 --- /dev/null +++ b/src/doc/unstable-book/src/compiler-environment-variables/TERM.md @@ -0,0 +1,5 @@ +# `TERM` + +This environment variable is used by [`-Zterminal-urls`] to detect if URLs are supported by the terminal emulator. + +[`-Zterminal-urls`]: ../compiler-flags/terminal-urls.html diff --git a/src/doc/unstable-book/src/compiler-flags/terminal-urls.md b/src/doc/unstable-book/src/compiler-flags/terminal-urls.md new file mode 100644 index 00000000000..a5427978f25 --- /dev/null +++ b/src/doc/unstable-book/src/compiler-flags/terminal-urls.md @@ -0,0 +1,13 @@ +# `-Z terminal-urls` + +The tracking feature for this issue is [#125586] + +[#125586]: https://github.com/rust-lang/rust/issues/125586 + +--- + +This flag takes either a boolean or the string "auto". + +When enabled, use the OSC 8 hyperlink terminal specification to print hyperlinks in the compiler output. +Use "auto" to try and autodetect whether the terminal emulator supports hyperlinks. +Currently, "auto" only enables hyperlinks if `COLORTERM=truecolor` and `TERM=xterm-256color`. diff --git a/src/tools/tidy/src/features.rs b/src/tools/tidy/src/features.rs index fcd7943e6e0..cbb7e18a8db 100644 --- a/src/tools/tidy/src/features.rs +++ b/src/tools/tidy/src/features.rs @@ -9,6 +9,7 @@ //! * All unstable lang features have tests to ensure they are actually unstable. //! * Language features in a group are sorted by feature name. +use std::collections::BTreeSet; use std::collections::hash_map::{Entry, HashMap}; use std::ffi::OsStr; use std::num::NonZeroU32; @@ -21,6 +22,7 @@ use crate::walk::{filter_dirs, filter_not_rust, walk, walk_many}; mod tests; mod version; +use regex::Regex; use version::Version; const FEATURE_GROUP_START_PREFIX: &str = "// feature-group-start"; @@ -610,3 +612,36 @@ fn map_lib_features( }, ); } + +fn should_document(var: &str) -> bool { + if var.starts_with("RUSTC_") || var.starts_with("RUST_") || var.starts_with("UNSTABLE_RUSTDOC_") + { + return true; + } + ["SDKROOT", "QNX_TARGET", "COLORTERM", "TERM"].contains(&var) +} + +pub fn collect_env_vars(compiler: &Path) -> BTreeSet { + let env_var_regex: Regex = Regex::new(r#"env::var(_os)?\("([^"]+)"#).unwrap(); + + let mut vars = BTreeSet::new(); + walk( + compiler, + // skip build scripts, tests, and non-rust files + |path, _is_dir| { + filter_dirs(path) + || filter_not_rust(path) + || path.ends_with("build.rs") + || path.ends_with("tests.rs") + }, + &mut |_entry, contents| { + for env_var in env_var_regex.captures_iter(contents).map(|c| c.get(2).unwrap().as_str()) + { + if should_document(env_var) { + vars.insert(env_var.to_owned()); + } + } + }, + ); + vars +} diff --git a/src/tools/tidy/src/unstable_book.rs b/src/tools/tidy/src/unstable_book.rs index a2453a6c960..5b0bf2201c1 100644 --- a/src/tools/tidy/src/unstable_book.rs +++ b/src/tools/tidy/src/unstable_book.rs @@ -6,6 +6,8 @@ use crate::features::{CollectedFeatures, Features, Status}; pub const PATH_STR: &str = "doc/unstable-book"; +pub const ENV_VARS_DIR: &str = "src/compiler-environment-variables"; + pub const COMPILER_FLAGS_DIR: &str = "src/compiler-flags"; pub const LANG_FEATURES_DIR: &str = "src/language-features"; diff --git a/src/tools/unstable-book-gen/src/main.rs b/src/tools/unstable-book-gen/src/main.rs index b9a2d313182..2a3388590cd 100644 --- a/src/tools/unstable-book-gen/src/main.rs +++ b/src/tools/unstable-book-gen/src/main.rs @@ -5,11 +5,11 @@ use std::env; use std::fs::{self, write}; use std::path::Path; -use tidy::features::{Features, collect_lang_features, collect_lib_features}; +use tidy::features::{Features, collect_env_vars, collect_lang_features, collect_lib_features}; use tidy::t; use tidy::unstable_book::{ - LANG_FEATURES_DIR, LIB_FEATURES_DIR, PATH_STR, collect_unstable_book_section_file_names, - collect_unstable_feature_names, + ENV_VARS_DIR, LANG_FEATURES_DIR, LIB_FEATURES_DIR, PATH_STR, + collect_unstable_book_section_file_names, collect_unstable_feature_names, }; fn generate_stub_issue(path: &Path, name: &str, issue: u32) { @@ -22,6 +22,11 @@ fn generate_stub_no_issue(path: &Path, name: &str) { t!(write(path, content), path); } +fn generate_stub_env_var(path: &Path, name: &str) { + let content = format!(include_str!("stub-env-var.md"), name = name); + t!(write(path, content), path); +} + fn set_to_summary_str(set: &BTreeSet, dir: &str) -> String { set.iter() .map(|ref n| format!(" - [{}]({}/{}.md)", n.replace('-', "_"), dir, n)) @@ -54,7 +59,7 @@ fn generate_summary(path: &Path, lang_features: &Features, lib_features: &Featur t!(write(&summary_path, content), summary_path); } -fn generate_unstable_book_files(src: &Path, out: &Path, features: &Features) { +fn generate_feature_files(src: &Path, out: &Path, features: &Features) { let unstable_features = collect_unstable_feature_names(features); let unstable_section_file_names = collect_unstable_book_section_file_names(src); t!(fs::create_dir_all(&out)); @@ -72,6 +77,16 @@ fn generate_unstable_book_files(src: &Path, out: &Path, features: &Features) { } } +fn generate_env_files(src: &Path, out: &Path, env_vars: &BTreeSet) { + let env_var_file_names = collect_unstable_book_section_file_names(src); + t!(fs::create_dir_all(&out)); + for env_var in env_vars - &env_var_file_names { + let file_name = format!("{env_var}.md"); + let out_file_path = out.join(&file_name); + generate_stub_env_var(&out_file_path, &env_var); + } +} + fn copy_recursive(from: &Path, to: &Path) { for entry in t!(fs::read_dir(from)) { let e = t!(entry); @@ -101,21 +116,23 @@ fn main() { .into_iter() .filter(|&(ref name, _)| !lang_features.contains_key(name)) .collect(); + let env_vars = collect_env_vars(compiler_path); let doc_src_path = src_path.join(PATH_STR); t!(fs::create_dir_all(&dest_path)); - generate_unstable_book_files( + generate_feature_files( &doc_src_path.join(LANG_FEATURES_DIR), &dest_path.join(LANG_FEATURES_DIR), &lang_features, ); - generate_unstable_book_files( + generate_feature_files( &doc_src_path.join(LIB_FEATURES_DIR), &dest_path.join(LIB_FEATURES_DIR), &lib_features, ); + generate_env_files(&doc_src_path.join(ENV_VARS_DIR), &dest_path.join(ENV_VARS_DIR), &env_vars); copy_recursive(&doc_src_path, &dest_path); diff --git a/src/tools/unstable-book-gen/src/stub-env-var.md b/src/tools/unstable-book-gen/src/stub-env-var.md new file mode 100644 index 00000000000..e8a7ddb855a --- /dev/null +++ b/src/tools/unstable-book-gen/src/stub-env-var.md @@ -0,0 +1,9 @@ +# `{name}` + +Environment variables have no tracking issue. This environment variable has no documentation, and therefore is likely internal to the compiler and not meant for general use. + +See [the code][github search] for more information. + +[github search]: https://github.com/search?q=repo%3Arust-lang%2Frust+%22{name}%22+path%3Acompiler&type=code + +------------------------ -- cgit 1.4.1-3-g733a5 From c021e7a9d2818e7d9f71bd38124d4e2c212007d6 Mon Sep 17 00:00:00 2001 From: lolbinarycat Date: Tue, 27 May 2025 12:50:14 -0500 Subject: scrape-examples.js: fix typos Co-authored-by: Guillaume Gomez --- src/librustdoc/html/static/js/rustdoc.d.ts | 2 +- src/librustdoc/html/static/js/scrape-examples.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/librustdoc/html/static/js/rustdoc.d.ts b/src/librustdoc/html/static/js/rustdoc.d.ts index 2c0d47447c0..686d77abde5 100644 --- a/src/librustdoc/html/static/js/rustdoc.d.ts +++ b/src/librustdoc/html/static/js/rustdoc.d.ts @@ -497,7 +497,7 @@ declare namespace rustdoc { * First field is the start and end char index, * other fields seem to be unused. * - * generated by `render_call_locations` in `render/mod.rs`. + * Generated by `render_call_locations` in `render/mod.rs`. */ type ScrapedLoc = [[number, number], string, string] } diff --git a/src/librustdoc/html/static/js/scrape-examples.js b/src/librustdoc/html/static/js/scrape-examples.js index ec472666ce1..eeab591bcd8 100644 --- a/src/librustdoc/html/static/js/scrape-examples.js +++ b/src/librustdoc/html/static/js/scrape-examples.js @@ -121,7 +121,7 @@ }; /** - * Intitialize the `locs` field + * Initialize the `locs` field * * @param {HTMLElement & {locs?: rustdoc.ScrapedLoc[]}} example * @param {boolean} isHidden -- cgit 1.4.1-3-g733a5 From 964eb82b63f7c99f951e5b5f262dc8800f7bd6ec Mon Sep 17 00:00:00 2001 From: Pavel Grigorenko Date: Thu, 29 May 2025 22:10:40 +0300 Subject: Stabilize `ip_from` --- library/core/src/net/ip_addr.rs | 12 ++++++------ library/coretests/tests/lib.rs | 1 - 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/library/core/src/net/ip_addr.rs b/library/core/src/net/ip_addr.rs index aaa68e8d7d1..1d1d184bb21 100644 --- a/library/core/src/net/ip_addr.rs +++ b/library/core/src/net/ip_addr.rs @@ -627,13 +627,13 @@ impl Ipv4Addr { /// # Examples /// /// ``` - /// #![feature(ip_from)] /// use std::net::Ipv4Addr; /// /// let addr = Ipv4Addr::from_octets([13u8, 12u8, 11u8, 10u8]); /// assert_eq!(Ipv4Addr::new(13, 12, 11, 10), addr); /// ``` - #[unstable(feature = "ip_from", issue = "131360")] + #[stable(feature = "ip_from", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "ip_from", since = "CURRENT_RUSTC_VERSION")] #[must_use] #[inline] pub const fn from_octets(octets: [u8; 4]) -> Ipv4Addr { @@ -1460,7 +1460,6 @@ impl Ipv6Addr { /// # Examples /// /// ``` - /// #![feature(ip_from)] /// use std::net::Ipv6Addr; /// /// let addr = Ipv6Addr::from_segments([ @@ -1475,7 +1474,8 @@ impl Ipv6Addr { /// addr /// ); /// ``` - #[unstable(feature = "ip_from", issue = "131360")] + #[stable(feature = "ip_from", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "ip_from", since = "CURRENT_RUSTC_VERSION")] #[must_use] #[inline] pub const fn from_segments(segments: [u16; 8]) -> Ipv6Addr { @@ -2025,7 +2025,6 @@ impl Ipv6Addr { /// # Examples /// /// ``` - /// #![feature(ip_from)] /// use std::net::Ipv6Addr; /// /// let addr = Ipv6Addr::from_octets([ @@ -2040,7 +2039,8 @@ impl Ipv6Addr { /// addr /// ); /// ``` - #[unstable(feature = "ip_from", issue = "131360")] + #[stable(feature = "ip_from", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "ip_from", since = "CURRENT_RUSTC_VERSION")] #[must_use] #[inline] pub const fn from_octets(octets: [u8; 16]) -> Ipv6Addr { diff --git a/library/coretests/tests/lib.rs b/library/coretests/tests/lib.rs index 693b14ef762..b9c642461ea 100644 --- a/library/coretests/tests/lib.rs +++ b/library/coretests/tests/lib.rs @@ -47,7 +47,6 @@ #![feature(hashmap_internals)] #![feature(int_roundings)] #![feature(ip)] -#![feature(ip_from)] #![feature(is_ascii_octdigit)] #![feature(isolate_most_least_significant_one)] #![feature(iter_advance_by)] -- cgit 1.4.1-3-g733a5 From 1140e90074b0cbcfdea8535e4b51877e2838227e Mon Sep 17 00:00:00 2001 From: binarycat Date: Tue, 27 May 2025 12:42:25 -0500 Subject: rustdoc search: prefer stable items in search results fixes https://github.com/rust-lang/rust/issues/138067 --- src/librustdoc/formats/cache.rs | 1 + src/librustdoc/html/render/mod.rs | 9 ++++++++- src/librustdoc/html/render/search_index.rs | 6 ++++++ src/librustdoc/html/static/js/rustdoc.d.ts | 5 ++++- src/librustdoc/html/static/js/search.js | 21 ++++++++++++++++++++- tests/rustdoc-js-std/core-transmute.js | 2 +- tests/rustdoc-js-std/transmute-fail.js | 2 +- tests/rustdoc-js-std/transmute.js | 2 +- tests/rustdoc-js/sort-stability.js | 9 +++++++++ tests/rustdoc-js/sort-stability.rs | 16 ++++++++++++++++ 10 files changed, 67 insertions(+), 6 deletions(-) create mode 100644 tests/rustdoc-js/sort-stability.js create mode 100644 tests/rustdoc-js/sort-stability.rs diff --git a/src/librustdoc/formats/cache.rs b/src/librustdoc/formats/cache.rs index 4989bd718c9..c3251ca7806 100644 --- a/src/librustdoc/formats/cache.rs +++ b/src/librustdoc/formats/cache.rs @@ -586,6 +586,7 @@ fn add_item_to_search_index(tcx: TyCtxt<'_>, cache: &mut Cache, item: &clean::It search_type, aliases, deprecation, + stability: item.stability(tcx), }; cache.search_index.push(index_item); } diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs index 66d5aafa3c1..29917bb0fca 100644 --- a/src/librustdoc/html/render/mod.rs +++ b/src/librustdoc/html/render/mod.rs @@ -50,7 +50,8 @@ use std::{fs, str}; use askama::Template; use itertools::Either; use rustc_attr_data_structures::{ - ConstStability, DeprecatedSince, Deprecation, RustcVersion, StabilityLevel, StableSince, + ConstStability, DeprecatedSince, Deprecation, RustcVersion, Stability, StabilityLevel, + StableSince, }; use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet}; use rustc_hir::Mutability; @@ -140,6 +141,12 @@ pub(crate) struct IndexItem { pub(crate) search_type: Option, pub(crate) aliases: Box<[Symbol]>, pub(crate) deprecation: Option, + pub(crate) stability: Option, +} +impl IndexItem { + fn is_unstable(&self) -> bool { + matches!(&self.stability, Some(Stability { level: StabilityLevel::Unstable { .. }, .. })) + } } /// A type used for the search index. diff --git a/src/librustdoc/html/render/search_index.rs b/src/librustdoc/html/render/search_index.rs index aff8684ee3a..6635aa02e97 100644 --- a/src/librustdoc/html/render/search_index.rs +++ b/src/librustdoc/html/render/search_index.rs @@ -93,6 +93,7 @@ pub(crate) fn build_index( ), aliases: item.attrs.get_doc_aliases(), deprecation: item.deprecation(tcx), + stability: item.stability(tcx), }); } } @@ -642,6 +643,7 @@ pub(crate) fn build_index( let mut parents_backref_queue = VecDeque::new(); let mut functions = String::with_capacity(self.items.len()); let mut deprecated = Vec::with_capacity(self.items.len()); + let mut unstable = Vec::with_capacity(self.items.len()); let mut type_backref_queue = VecDeque::new(); @@ -698,6 +700,9 @@ pub(crate) fn build_index( // bitmasks always use 1-indexing for items, with 0 as the crate itself deprecated.push(u32::try_from(index + 1).unwrap()); } + if item.is_unstable() { + unstable.push(u32::try_from(index + 1).unwrap()); + } } for (index, path) in &revert_extra_paths { @@ -736,6 +741,7 @@ pub(crate) fn build_index( crate_data.serialize_field("r", &re_exports)?; crate_data.serialize_field("b", &self.associated_item_disambiguators)?; crate_data.serialize_field("c", &bitmap_to_string(&deprecated))?; + crate_data.serialize_field("u", &bitmap_to_string(&unstable))?; crate_data.serialize_field("e", &bitmap_to_string(&self.empty_desc))?; crate_data.serialize_field("P", ¶m_names)?; if has_aliases { diff --git a/src/librustdoc/html/static/js/rustdoc.d.ts b/src/librustdoc/html/static/js/rustdoc.d.ts index 0d2e19e019f..8110f1f5a5e 100644 --- a/src/librustdoc/html/static/js/rustdoc.d.ts +++ b/src/librustdoc/html/static/js/rustdoc.d.ts @@ -129,7 +129,7 @@ declare namespace rustdoc { /** * A single parsed "atom" in a search query. For example, - * + * * std::fmt::Formatter, Write -> Result<()> * ┏━━━━━━━━━━━━━━━━━━ ┌──── ┏━━━━━┅┅┅┅┄┄┄┄┄┄┄┄┄┄┄┄┄┄┐ * ┃ │ ┗ QueryElement { ┊ @@ -442,6 +442,8 @@ declare namespace rustdoc { * of `p`) but is used for modules items like free functions. * * `c` is an array of item indices that are deprecated. + * + * `u` is an array of item indices that are unstable. */ type RawSearchIndexCrate = { doc: string, @@ -456,6 +458,7 @@ declare namespace rustdoc { p: Array<[number, string] | [number, string, number] | [number, string, number, number] | [number, string, number, number, string]>, b: Array<[number, String]>, c: string, + u: string, r: Array<[number, number]>, P: Array<[number, string]>, }; diff --git a/src/librustdoc/html/static/js/search.js b/src/librustdoc/html/static/js/search.js index dce5fddb317..da15433622a 100644 --- a/src/librustdoc/html/static/js/search.js +++ b/src/librustdoc/html/static/js/search.js @@ -1463,6 +1463,11 @@ class DocSearch { * @type {Map} */ this.searchIndexEmptyDesc = new Map(); + /** + * @type {Map} + */ + this.searchIndexUnstable = new Map(); + /** * @type {Uint32Array} */ @@ -2048,9 +2053,10 @@ class DocSearch { }; const descShardList = [descShard]; - // Deprecated items and items with no description + // Deprecated and unstable items and items with no description this.searchIndexDeprecated.set(crate, new RoaringBitmap(crateCorpus.c)); this.searchIndexEmptyDesc.set(crate, new RoaringBitmap(crateCorpus.e)); + this.searchIndexUnstable.set(crate, new RoaringBitmap(crateCorpus.u)); let descIndex = 0; /** @@ -3284,6 +3290,19 @@ class DocSearch { return a - b; } + // sort unstable items later + a = Number( + // @ts-expect-error + this.searchIndexUnstable.get(aaa.item.crate).contains(aaa.item.bitIndex), + ); + b = Number( + // @ts-expect-error + this.searchIndexUnstable.get(bbb.item.crate).contains(bbb.item.bitIndex), + ); + if (a !== b) { + return a - b; + } + // sort by crate (current crate comes first) a = Number(aaa.item.crate !== preferredCrate); b = Number(bbb.item.crate !== preferredCrate); diff --git a/tests/rustdoc-js-std/core-transmute.js b/tests/rustdoc-js-std/core-transmute.js index 8c9910a32d7..b15f398902c 100644 --- a/tests/rustdoc-js-std/core-transmute.js +++ b/tests/rustdoc-js-std/core-transmute.js @@ -3,9 +3,9 @@ const EXPECTED = [ { 'query': 'generic:T -> generic:U', 'others': [ + { 'path': 'core::mem', 'name': 'transmute' }, { 'path': 'core::intrinsics::simd', 'name': 'simd_as' }, { 'path': 'core::intrinsics::simd', 'name': 'simd_cast' }, - { 'path': 'core::mem', 'name': 'transmute' }, ], }, ]; diff --git a/tests/rustdoc-js-std/transmute-fail.js b/tests/rustdoc-js-std/transmute-fail.js index ddfb2761948..459e8dc3f00 100644 --- a/tests/rustdoc-js-std/transmute-fail.js +++ b/tests/rustdoc-js-std/transmute-fail.js @@ -6,9 +6,9 @@ const EXPECTED = [ // should-fail tag and the search query below: 'query': 'generic:T -> generic:T', 'others': [ + { 'path': 'std::mem', 'name': 'transmute' }, { 'path': 'std::intrinsics::simd', 'name': 'simd_as' }, { 'path': 'std::intrinsics::simd', 'name': 'simd_cast' }, - { 'path': 'std::mem', 'name': 'transmute' }, ], }, ]; diff --git a/tests/rustdoc-js-std/transmute.js b/tests/rustdoc-js-std/transmute.js index f52e0ab1436..a85b02e2994 100644 --- a/tests/rustdoc-js-std/transmute.js +++ b/tests/rustdoc-js-std/transmute.js @@ -5,9 +5,9 @@ const EXPECTED = [ // should-fail tag and the search query below: 'query': 'generic:T -> generic:U', 'others': [ + { 'path': 'std::mem', 'name': 'transmute' }, { 'path': 'std::intrinsics::simd', 'name': 'simd_as' }, { 'path': 'std::intrinsics::simd', 'name': 'simd_cast' }, - { 'path': 'std::mem', 'name': 'transmute' }, ], }, ]; diff --git a/tests/rustdoc-js/sort-stability.js b/tests/rustdoc-js/sort-stability.js new file mode 100644 index 00000000000..8c095619a08 --- /dev/null +++ b/tests/rustdoc-js/sort-stability.js @@ -0,0 +1,9 @@ +const EXPECTED = [ + { + 'query': 'foo', + 'others': [ + {"path": "sort_stability::old", "name": "foo"}, + {"path": "sort_stability::new", "name": "foo"}, + ], + }, +]; diff --git a/tests/rustdoc-js/sort-stability.rs b/tests/rustdoc-js/sort-stability.rs new file mode 100644 index 00000000000..68662bb3aab --- /dev/null +++ b/tests/rustdoc-js/sort-stability.rs @@ -0,0 +1,16 @@ +#![feature(staged_api)] +#![stable(feature = "foo_lib", since = "1.0.0")] + +#[stable(feature = "old_foo", since = "1.0.1")] +pub mod old { + /// Old, stable foo + #[stable(feature = "old_foo", since = "1.0.1")] + pub fn foo() {} +} + +#[unstable(feature = "new_foo", issue = "none")] +pub mod new { + /// New, unstable foo + #[unstable(feature = "new_foo", issue = "none")] + pub fn foo() {} +} -- cgit 1.4.1-3-g733a5 From eb2913b01b3e423b2bcd73cc62406ffecbe59ea3 Mon Sep 17 00:00:00 2001 From: Benoît du Garreau Date: Thu, 19 Jun 2025 21:55:55 +0200 Subject: Fix unsoundness in some tests --- library/coretests/tests/io/borrowed_buf.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/library/coretests/tests/io/borrowed_buf.rs b/library/coretests/tests/io/borrowed_buf.rs index fbd3864dcac..8fbf9c3e8a2 100644 --- a/library/coretests/tests/io/borrowed_buf.rs +++ b/library/coretests/tests/io/borrowed_buf.rs @@ -66,7 +66,7 @@ fn clear() { #[test] fn set_init() { - let buf: &mut [_] = &mut [MaybeUninit::uninit(); 16]; + let buf: &mut [_] = &mut [MaybeUninit::zeroed(); 16]; let mut rbuf: BorrowedBuf<'_> = buf.into(); unsafe { @@ -134,7 +134,7 @@ fn reborrow_written() { #[test] fn cursor_set_init() { - let buf: &mut [_] = &mut [MaybeUninit::uninit(); 16]; + let buf: &mut [_] = &mut [MaybeUninit::zeroed(); 16]; let mut rbuf: BorrowedBuf<'_> = buf.into(); unsafe { -- cgit 1.4.1-3-g733a5 From 06ae1bee205c9f85cc89047568737a87c791a032 Mon Sep 17 00:00:00 2001 From: yukang Date: Sun, 22 Jun 2025 20:43:31 +0800 Subject: Make doc for transpose api better --- library/core/src/option.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/library/core/src/option.rs b/library/core/src/option.rs index f2a1e901188..2132376b3c5 100644 --- a/library/core/src/option.rs +++ b/library/core/src/option.rs @@ -2037,9 +2037,9 @@ impl Option<&mut T> { impl Option> { /// Transposes an `Option` of a [`Result`] into a [`Result`] of an `Option`. /// - /// [`None`] will be mapped to [Ok]\([None]). - /// [Some]\([Ok]\(\_)) and [Some]\([Err]\(\_)) will be mapped to - /// [Ok]\([Some]\(\_)) and [Err]\(\_). + /// [Some]\([Ok]\(\_)) is mapped to [Ok]\([Some]\(\_)), + /// [Some]\([Err]\(\_)) is mapped to [Err]\(\_), + /// and [`None`] will be mapped to [Ok]\([None]). /// /// # Examples /// @@ -2047,9 +2047,9 @@ impl Option> { /// #[derive(Debug, Eq, PartialEq)] /// struct SomeErr; /// - /// let x: Result, SomeErr> = Ok(Some(5)); - /// let y: Option> = Some(Ok(5)); - /// assert_eq!(x, y.transpose()); + /// let x: Option> = Some(Ok(5)); + /// let y: Result, SomeErr> = Ok(Some(5)); + /// assert_eq!(x.transpose(), y); /// ``` #[inline] #[stable(feature = "transpose_result", since = "1.33.0")] -- cgit 1.4.1-3-g733a5 From 1045b70304ab6a875fc9fbf10cb7e7c010edd3ab Mon Sep 17 00:00:00 2001 From: Jubilee Young Date: Thu, 26 Jun 2025 18:03:00 -0700 Subject: tests: add test for invalid interrupt signatures --- .../ui/abi/interrupt-invalid-signature.avr.stderr | 28 ++++++ .../ui/abi/interrupt-invalid-signature.i686.stderr | 15 +++ .../abi/interrupt-invalid-signature.msp430.stderr | 28 ++++++ .../abi/interrupt-invalid-signature.riscv32.stderr | 54 ++++++++++ .../abi/interrupt-invalid-signature.riscv64.stderr | 54 ++++++++++ tests/ui/abi/interrupt-invalid-signature.rs | 110 +++++++++++++++++++++ .../ui/abi/interrupt-invalid-signature.x64.stderr | 15 +++ 7 files changed, 304 insertions(+) create mode 100644 tests/ui/abi/interrupt-invalid-signature.avr.stderr create mode 100644 tests/ui/abi/interrupt-invalid-signature.i686.stderr create mode 100644 tests/ui/abi/interrupt-invalid-signature.msp430.stderr create mode 100644 tests/ui/abi/interrupt-invalid-signature.riscv32.stderr create mode 100644 tests/ui/abi/interrupt-invalid-signature.riscv64.stderr create mode 100644 tests/ui/abi/interrupt-invalid-signature.rs create mode 100644 tests/ui/abi/interrupt-invalid-signature.x64.stderr diff --git a/tests/ui/abi/interrupt-invalid-signature.avr.stderr b/tests/ui/abi/interrupt-invalid-signature.avr.stderr new file mode 100644 index 00000000000..91f5ca2022e --- /dev/null +++ b/tests/ui/abi/interrupt-invalid-signature.avr.stderr @@ -0,0 +1,28 @@ +error: invalid signature for `extern "avr-interrupt"` function + --> $DIR/interrupt-invalid-signature.rs:44:35 + | +LL | extern "avr-interrupt" fn avr_arg(_byte: u8) {} + | ^^^^^^^^^ + | + = note: functions with the "avr-interrupt" ABI cannot have any parameters or return type +help: remove the parameters and return type + | +LL - extern "avr-interrupt" fn avr_arg(_byte: u8) {} +LL + extern "avr-interrupt" fn avr_arg() {} + | + +error: invalid signature for `extern "avr-interrupt"` function + --> $DIR/interrupt-invalid-signature.rs:59:40 + | +LL | extern "avr-interrupt" fn avr_ret() -> u8 { + | ^^ + | + = note: functions with the "avr-interrupt" ABI cannot have any parameters or return type +help: remove the parameters and return type + | +LL - extern "avr-interrupt" fn avr_ret() -> u8 { +LL + extern "avr-interrupt" fn avr_ret() { + | + +error: aborting due to 2 previous errors + diff --git a/tests/ui/abi/interrupt-invalid-signature.i686.stderr b/tests/ui/abi/interrupt-invalid-signature.i686.stderr new file mode 100644 index 00000000000..06fd513df19 --- /dev/null +++ b/tests/ui/abi/interrupt-invalid-signature.i686.stderr @@ -0,0 +1,15 @@ +error: invalid signature for `extern "x86-interrupt"` function + --> $DIR/interrupt-invalid-signature.rs:83:40 + | +LL | extern "x86-interrupt" fn x86_ret() -> u8 { + | ^^ + | + = note: functions with the "custom" ABI cannot have a return type +help: remove the return type + --> $DIR/interrupt-invalid-signature.rs:83:40 + | +LL | extern "x86-interrupt" fn x86_ret() -> u8 { + | ^^ + +error: aborting due to 1 previous error + diff --git a/tests/ui/abi/interrupt-invalid-signature.msp430.stderr b/tests/ui/abi/interrupt-invalid-signature.msp430.stderr new file mode 100644 index 00000000000..38479f93de5 --- /dev/null +++ b/tests/ui/abi/interrupt-invalid-signature.msp430.stderr @@ -0,0 +1,28 @@ +error: invalid signature for `extern "msp430-interrupt"` function + --> $DIR/interrupt-invalid-signature.rs:40:41 + | +LL | extern "msp430-interrupt" fn msp430_arg(_byte: u8) {} + | ^^^^^^^^^ + | + = note: functions with the "msp430-interrupt" ABI cannot have any parameters or return type +help: remove the parameters and return type + | +LL - extern "msp430-interrupt" fn msp430_arg(_byte: u8) {} +LL + extern "msp430-interrupt" fn msp430_arg() {} + | + +error: invalid signature for `extern "msp430-interrupt"` function + --> $DIR/interrupt-invalid-signature.rs:65:46 + | +LL | extern "msp430-interrupt" fn msp430_ret() -> u8 { + | ^^ + | + = note: functions with the "msp430-interrupt" ABI cannot have any parameters or return type +help: remove the parameters and return type + | +LL - extern "msp430-interrupt" fn msp430_ret() -> u8 { +LL + extern "msp430-interrupt" fn msp430_ret() { + | + +error: aborting due to 2 previous errors + diff --git a/tests/ui/abi/interrupt-invalid-signature.riscv32.stderr b/tests/ui/abi/interrupt-invalid-signature.riscv32.stderr new file mode 100644 index 00000000000..d632b17ab83 --- /dev/null +++ b/tests/ui/abi/interrupt-invalid-signature.riscv32.stderr @@ -0,0 +1,54 @@ +error: invalid signature for `extern "riscv-interrupt-m"` function + --> $DIR/interrupt-invalid-signature.rs:48:43 + | +LL | extern "riscv-interrupt-m" fn riscv_m_arg(_byte: u8) {} + | ^^^^^^^^^ + | + = note: functions with the "riscv-interrupt-m" ABI cannot have any parameters or return type +help: remove the parameters and return type + | +LL - extern "riscv-interrupt-m" fn riscv_m_arg(_byte: u8) {} +LL + extern "riscv-interrupt-m" fn riscv_m_arg() {} + | + +error: invalid signature for `extern "riscv-interrupt-s"` function + --> $DIR/interrupt-invalid-signature.rs:52:43 + | +LL | extern "riscv-interrupt-s" fn riscv_s_arg(_byte: u8) {} + | ^^^^^^^^^ + | + = note: functions with the "riscv-interrupt-s" ABI cannot have any parameters or return type +help: remove the parameters and return type + | +LL - extern "riscv-interrupt-s" fn riscv_s_arg(_byte: u8) {} +LL + extern "riscv-interrupt-s" fn riscv_s_arg() {} + | + +error: invalid signature for `extern "riscv-interrupt-m"` function + --> $DIR/interrupt-invalid-signature.rs:71:48 + | +LL | extern "riscv-interrupt-m" fn riscv_m_ret() -> u8 { + | ^^ + | + = note: functions with the "riscv-interrupt-m" ABI cannot have any parameters or return type +help: remove the parameters and return type + | +LL - extern "riscv-interrupt-m" fn riscv_m_ret() -> u8 { +LL + extern "riscv-interrupt-m" fn riscv_m_ret() { + | + +error: invalid signature for `extern "riscv-interrupt-s"` function + --> $DIR/interrupt-invalid-signature.rs:77:48 + | +LL | extern "riscv-interrupt-s" fn riscv_s_ret() -> u8 { + | ^^ + | + = note: functions with the "riscv-interrupt-s" ABI cannot have any parameters or return type +help: remove the parameters and return type + | +LL - extern "riscv-interrupt-s" fn riscv_s_ret() -> u8 { +LL + extern "riscv-interrupt-s" fn riscv_s_ret() { + | + +error: aborting due to 4 previous errors + diff --git a/tests/ui/abi/interrupt-invalid-signature.riscv64.stderr b/tests/ui/abi/interrupt-invalid-signature.riscv64.stderr new file mode 100644 index 00000000000..d632b17ab83 --- /dev/null +++ b/tests/ui/abi/interrupt-invalid-signature.riscv64.stderr @@ -0,0 +1,54 @@ +error: invalid signature for `extern "riscv-interrupt-m"` function + --> $DIR/interrupt-invalid-signature.rs:48:43 + | +LL | extern "riscv-interrupt-m" fn riscv_m_arg(_byte: u8) {} + | ^^^^^^^^^ + | + = note: functions with the "riscv-interrupt-m" ABI cannot have any parameters or return type +help: remove the parameters and return type + | +LL - extern "riscv-interrupt-m" fn riscv_m_arg(_byte: u8) {} +LL + extern "riscv-interrupt-m" fn riscv_m_arg() {} + | + +error: invalid signature for `extern "riscv-interrupt-s"` function + --> $DIR/interrupt-invalid-signature.rs:52:43 + | +LL | extern "riscv-interrupt-s" fn riscv_s_arg(_byte: u8) {} + | ^^^^^^^^^ + | + = note: functions with the "riscv-interrupt-s" ABI cannot have any parameters or return type +help: remove the parameters and return type + | +LL - extern "riscv-interrupt-s" fn riscv_s_arg(_byte: u8) {} +LL + extern "riscv-interrupt-s" fn riscv_s_arg() {} + | + +error: invalid signature for `extern "riscv-interrupt-m"` function + --> $DIR/interrupt-invalid-signature.rs:71:48 + | +LL | extern "riscv-interrupt-m" fn riscv_m_ret() -> u8 { + | ^^ + | + = note: functions with the "riscv-interrupt-m" ABI cannot have any parameters or return type +help: remove the parameters and return type + | +LL - extern "riscv-interrupt-m" fn riscv_m_ret() -> u8 { +LL + extern "riscv-interrupt-m" fn riscv_m_ret() { + | + +error: invalid signature for `extern "riscv-interrupt-s"` function + --> $DIR/interrupt-invalid-signature.rs:77:48 + | +LL | extern "riscv-interrupt-s" fn riscv_s_ret() -> u8 { + | ^^ + | + = note: functions with the "riscv-interrupt-s" ABI cannot have any parameters or return type +help: remove the parameters and return type + | +LL - extern "riscv-interrupt-s" fn riscv_s_ret() -> u8 { +LL + extern "riscv-interrupt-s" fn riscv_s_ret() { + | + +error: aborting due to 4 previous errors + diff --git a/tests/ui/abi/interrupt-invalid-signature.rs b/tests/ui/abi/interrupt-invalid-signature.rs new file mode 100644 index 00000000000..e389285b069 --- /dev/null +++ b/tests/ui/abi/interrupt-invalid-signature.rs @@ -0,0 +1,110 @@ +/*! Tests interrupt ABIs have a constricted signature + +Most interrupt ABIs share a similar restriction in terms of not allowing most signatures. +Specifically, they generally cannot have arguments or return types. +So we test that they error in essentially all of the same places. +A notable and interesting exception is x86. + +This test uses `cfg` because it is not testing whether these ABIs work on the platform. +*/ +//@ add-core-stubs +//@ revisions: x64 i686 riscv32 riscv64 avr msp430 +// +//@ [x64] needs-llvm-components: x86 +//@ [x64] compile-flags: --target=x86_64-unknown-linux-gnu --crate-type=rlib +//@ [i686] needs-llvm-components: x86 +//@ [i686] compile-flags: --target=i686-unknown-linux-gnu --crate-type=rlib +//@ [riscv32] needs-llvm-components: riscv +//@ [riscv32] compile-flags: --target=riscv32i-unknown-none-elf --crate-type=rlib +//@ [riscv64] needs-llvm-components: riscv +//@ [riscv64] compile-flags: --target=riscv64gc-unknown-none-elf --crate-type=rlib +//@ [avr] needs-llvm-components: avr +//@ [avr] compile-flags: --target=avr-none -C target-cpu=atmega328p --crate-type=rlib +//@ [msp430] needs-llvm-components: msp430 +//@ [msp430] compile-flags: --target=msp430-none-elf --crate-type=rlib +#![no_core] +#![feature( + no_core, + abi_msp430_interrupt, + abi_avr_interrupt, + abi_x86_interrupt, + abi_riscv_interrupt +)] + +extern crate minicore; +use minicore::*; + +/* most extern "interrupt" definitions should not accept args */ + +#[cfg(msp430)] +extern "msp430-interrupt" fn msp430_arg(_byte: u8) {} +//[msp430]~^ ERROR invalid signature + +#[cfg(avr)] +extern "avr-interrupt" fn avr_arg(_byte: u8) {} +//[avr]~^ ERROR invalid signature + +#[cfg(any(riscv32,riscv64))] +extern "riscv-interrupt-m" fn riscv_m_arg(_byte: u8) {} +//[riscv32,riscv64]~^ ERROR invalid signature + +#[cfg(any(riscv32,riscv64))] +extern "riscv-interrupt-s" fn riscv_s_arg(_byte: u8) {} +//[riscv32,riscv64]~^ ERROR invalid signature + + +/* all extern "interrupt" definitions should not return non-1ZST values */ + +#[cfg(avr)] +extern "avr-interrupt" fn avr_ret() -> u8 { + //[avr]~^ ERROR invalid signature + 1 +} + +#[cfg(msp430)] +extern "msp430-interrupt" fn msp430_ret() -> u8 { + //[msp430]~^ ERROR invalid signature + 1 +} + +#[cfg(any(riscv32,riscv64))] +extern "riscv-interrupt-m" fn riscv_m_ret() -> u8 { + //[riscv32,riscv64]~^ ERROR invalid signature + 1 +} + +#[cfg(any(riscv32,riscv64))] +extern "riscv-interrupt-s" fn riscv_s_ret() -> u8 { + //[riscv32,riscv64]~^ ERROR invalid signature + 1 +} + +#[cfg(any(x64,i686))] +extern "x86-interrupt" fn x86_ret() -> u8 { + //[x64,i686]~^ ERROR invalid signature + 1 +} + + + +/* extern "interrupt" fnptrs with invalid signatures */ + +#[cfg(avr)] +fn avr_ptr(_f: extern "avr-interrupt" fn(u8) -> u8) { +} + +#[cfg(msp430)] +fn msp430_ptr(_f: extern "msp430-interrupt" fn(u8) -> u8) { +} + +#[cfg(any(riscv32,riscv64))] +fn riscv_m_ptr(_f: extern "riscv-interrupt-m" fn(u8) -> u8) { +} + +#[cfg(any(riscv32,riscv64))] +fn riscv_s_ptr(_f: extern "riscv-interrupt-s" fn(u8) -> u8) { +} + +#[cfg(any(x64,i686))] +fn x86_ptr(_f: extern "x86-interrupt" fn() -> u8) { +} diff --git a/tests/ui/abi/interrupt-invalid-signature.x64.stderr b/tests/ui/abi/interrupt-invalid-signature.x64.stderr new file mode 100644 index 00000000000..06fd513df19 --- /dev/null +++ b/tests/ui/abi/interrupt-invalid-signature.x64.stderr @@ -0,0 +1,15 @@ +error: invalid signature for `extern "x86-interrupt"` function + --> $DIR/interrupt-invalid-signature.rs:83:40 + | +LL | extern "x86-interrupt" fn x86_ret() -> u8 { + | ^^ + | + = note: functions with the "custom" ABI cannot have a return type +help: remove the return type + --> $DIR/interrupt-invalid-signature.rs:83:40 + | +LL | extern "x86-interrupt" fn x86_ret() -> u8 { + | ^^ + +error: aborting due to 1 previous error + -- cgit 1.4.1-3-g733a5 From db33e98540fca8f3268cafc6276dbefd55681d55 Mon Sep 17 00:00:00 2001 From: Jubilee Young Date: Thu, 26 Jun 2025 18:02:54 -0700 Subject: compiler: fixup error message for x86-interrupt invalid returns --- compiler/rustc_ast_passes/messages.ftl | 2 +- tests/ui/abi/interrupt-invalid-signature.i686.stderr | 2 +- tests/ui/abi/interrupt-invalid-signature.x64.stderr | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/compiler/rustc_ast_passes/messages.ftl b/compiler/rustc_ast_passes/messages.ftl index 6eddce7b590..869c3a58ae3 100644 --- a/compiler/rustc_ast_passes/messages.ftl +++ b/compiler/rustc_ast_passes/messages.ftl @@ -17,7 +17,7 @@ ast_passes_abi_must_not_have_parameters_or_return_type= ast_passes_abi_must_not_have_return_type= invalid signature for `extern {$abi}` function - .note = functions with the "custom" ABI cannot have a return type + .note = functions with the {$abi} ABI cannot have a return type .help = remove the return type ast_passes_assoc_const_without_body = diff --git a/tests/ui/abi/interrupt-invalid-signature.i686.stderr b/tests/ui/abi/interrupt-invalid-signature.i686.stderr index 06fd513df19..86f2e097c37 100644 --- a/tests/ui/abi/interrupt-invalid-signature.i686.stderr +++ b/tests/ui/abi/interrupt-invalid-signature.i686.stderr @@ -4,7 +4,7 @@ error: invalid signature for `extern "x86-interrupt"` function LL | extern "x86-interrupt" fn x86_ret() -> u8 { | ^^ | - = note: functions with the "custom" ABI cannot have a return type + = note: functions with the "x86-interrupt" ABI cannot have a return type help: remove the return type --> $DIR/interrupt-invalid-signature.rs:83:40 | diff --git a/tests/ui/abi/interrupt-invalid-signature.x64.stderr b/tests/ui/abi/interrupt-invalid-signature.x64.stderr index 06fd513df19..86f2e097c37 100644 --- a/tests/ui/abi/interrupt-invalid-signature.x64.stderr +++ b/tests/ui/abi/interrupt-invalid-signature.x64.stderr @@ -4,7 +4,7 @@ error: invalid signature for `extern "x86-interrupt"` function LL | extern "x86-interrupt" fn x86_ret() -> u8 { | ^^ | - = note: functions with the "custom" ABI cannot have a return type + = note: functions with the "x86-interrupt" ABI cannot have a return type help: remove the return type --> $DIR/interrupt-invalid-signature.rs:83:40 | -- cgit 1.4.1-3-g733a5 From d6b3314fb29cc688a968811b6a78dc8a5309cb7d Mon Sep 17 00:00:00 2001 From: Jubilee Young Date: Thu, 26 Jun 2025 19:35:28 -0700 Subject: compiler: allow interrupts to return () or ! --- compiler/rustc_ast_passes/src/ast_validation.rs | 16 +++- tests/ui/abi/interrupt-returns-never-or-unit.rs | 110 ++++++++++++++++++++++++ 2 files changed, 124 insertions(+), 2 deletions(-) create mode 100644 tests/ui/abi/interrupt-returns-never-or-unit.rs diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs index da248251203..478cec23fd5 100644 --- a/compiler/rustc_ast_passes/src/ast_validation.rs +++ b/compiler/rustc_ast_passes/src/ast_validation.rs @@ -396,7 +396,13 @@ impl<'a> AstValidator<'a> { if let InterruptKind::X86 = interrupt_kind { // "x86-interrupt" is special because it does have arguments. // FIXME(workingjubilee): properly lint on acceptable input types. - if let FnRetTy::Ty(ref ret_ty) = sig.decl.output { + if let FnRetTy::Ty(ref ret_ty) = sig.decl.output + && match &ret_ty.kind { + TyKind::Never => false, + TyKind::Tup(tup) if tup.is_empty() => false, + _ => true, + } + { self.dcx().emit_err(errors::AbiMustNotHaveReturnType { span: ret_ty.span, abi, @@ -455,7 +461,13 @@ impl<'a> AstValidator<'a> { fn reject_params_or_return(&self, abi: ExternAbi, ident: &Ident, sig: &FnSig) { let mut spans: Vec<_> = sig.decl.inputs.iter().map(|p| p.span).collect(); - if let FnRetTy::Ty(ref ret_ty) = sig.decl.output { + if let FnRetTy::Ty(ref ret_ty) = sig.decl.output + && match &ret_ty.kind { + TyKind::Never => false, + TyKind::Tup(tup) if tup.is_empty() => false, + _ => true, + } + { spans.push(ret_ty.span); } diff --git a/tests/ui/abi/interrupt-returns-never-or-unit.rs b/tests/ui/abi/interrupt-returns-never-or-unit.rs new file mode 100644 index 00000000000..34d509c8328 --- /dev/null +++ b/tests/ui/abi/interrupt-returns-never-or-unit.rs @@ -0,0 +1,110 @@ +/*! Tests interrupt ABIs can return ! + +Most interrupt ABIs share a similar restriction in terms of not allowing most signatures, +but it makes sense to allow them to return ! because they could indeed be divergent. + +This test uses `cfg` because it is not testing whether these ABIs work on the platform. +*/ +//@ add-core-stubs +//@ revisions: x64 i686 riscv32 riscv64 avr msp430 +//@ build-pass +// +//@ [x64] needs-llvm-components: x86 +//@ [x64] compile-flags: --target=x86_64-unknown-linux-gnu --crate-type=rlib +//@ [i686] needs-llvm-components: x86 +//@ [i686] compile-flags: --target=i686-unknown-linux-gnu --crate-type=rlib +//@ [riscv32] needs-llvm-components: riscv +//@ [riscv32] compile-flags: --target=riscv32i-unknown-none-elf --crate-type=rlib +//@ [riscv64] needs-llvm-components: riscv +//@ [riscv64] compile-flags: --target=riscv64gc-unknown-none-elf --crate-type=rlib +//@ [avr] needs-llvm-components: avr +//@ [avr] compile-flags: --target=avr-none -C target-cpu=atmega328p --crate-type=rlib +//@ [msp430] needs-llvm-components: msp430 +//@ [msp430] compile-flags: --target=msp430-none-elf --crate-type=rlib +#![no_core] +#![feature( + no_core, + abi_msp430_interrupt, + abi_avr_interrupt, + abi_x86_interrupt, + abi_riscv_interrupt +)] + +extern crate minicore; +use minicore::*; + +/* interrupts can return never */ + +#[cfg(avr)] +extern "avr-interrupt" fn avr_ret_never() -> ! { + unsafe { hint::unreachable_unchecked() } +} + +#[cfg(msp430)] +extern "msp430-interrupt" fn msp430_ret_never() -> ! { + unsafe { hint::unreachable_unchecked() } +} + +#[cfg(any(riscv32,riscv64))] +extern "riscv-interrupt-m" fn riscv_m_ret_never() -> ! { + unsafe { hint::unreachable_unchecked() } +} + +#[cfg(any(riscv32,riscv64))] +extern "riscv-interrupt-s" fn riscv_s_ret_never() -> ! { + unsafe { hint::unreachable_unchecked() } +} + +#[cfg(any(x64,i686))] +extern "x86-interrupt" fn x86_ret_never() -> ! { + unsafe { hint::unreachable_unchecked() } +} + +/* interrupts can return explicit () */ + +#[cfg(avr)] +extern "avr-interrupt" fn avr_ret_unit() -> () { + unsafe { hint::unreachable_unchecked() } +} + +#[cfg(msp430)] +extern "msp430-interrupt" fn msp430_ret_unit() -> () { + unsafe { hint::unreachable_unchecked() } +} + +#[cfg(any(riscv32,riscv64))] +extern "riscv-interrupt-m" fn riscv_m_ret_unit() -> () { + unsafe { hint::unreachable_unchecked() } +} + +#[cfg(any(riscv32,riscv64))] +extern "riscv-interrupt-s" fn riscv_s_ret_unit() -> () { + unsafe { hint::unreachable_unchecked() } +} + +#[cfg(any(x64,i686))] +extern "x86-interrupt" fn x86_ret_unit() -> () { + unsafe { hint::unreachable_unchecked() } +} + +/* extern "interrupt" fnptrs can return ! too */ + +#[cfg(avr)] +fn avr_ptr(_f: extern "avr-interrupt" fn() -> !) { +} + +#[cfg(msp430)] +fn msp430_ptr(_f: extern "msp430-interrupt" fn() -> !) { +} + +#[cfg(any(riscv32,riscv64))] +fn riscv_m_ptr(_f: extern "riscv-interrupt-m" fn() -> !) { +} + +#[cfg(any(riscv32,riscv64))] +fn riscv_s_ptr(_f: extern "riscv-interrupt-s" fn() -> !) { +} + +#[cfg(any(x64,i686))] +fn x86_ptr(_f: extern "x86-interrupt" fn() -> !) { +} -- cgit 1.4.1-3-g733a5 From b5ab966626e006172027e658fb1e43cf79f1c21a Mon Sep 17 00:00:00 2001 From: Jubilee Young Date: Fri, 27 Jun 2025 11:03:23 -0700 Subject: remember how to write never returns --- tests/ui/abi/interrupt-returns-never-or-unit.rs | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/tests/ui/abi/interrupt-returns-never-or-unit.rs b/tests/ui/abi/interrupt-returns-never-or-unit.rs index 34d509c8328..8e224229a0b 100644 --- a/tests/ui/abi/interrupt-returns-never-or-unit.rs +++ b/tests/ui/abi/interrupt-returns-never-or-unit.rs @@ -37,54 +37,54 @@ use minicore::*; #[cfg(avr)] extern "avr-interrupt" fn avr_ret_never() -> ! { - unsafe { hint::unreachable_unchecked() } + loop {} } #[cfg(msp430)] extern "msp430-interrupt" fn msp430_ret_never() -> ! { - unsafe { hint::unreachable_unchecked() } + loop {} } #[cfg(any(riscv32,riscv64))] extern "riscv-interrupt-m" fn riscv_m_ret_never() -> ! { - unsafe { hint::unreachable_unchecked() } + loop {} } #[cfg(any(riscv32,riscv64))] extern "riscv-interrupt-s" fn riscv_s_ret_never() -> ! { - unsafe { hint::unreachable_unchecked() } + loop {} } #[cfg(any(x64,i686))] extern "x86-interrupt" fn x86_ret_never() -> ! { - unsafe { hint::unreachable_unchecked() } + loop {} } /* interrupts can return explicit () */ #[cfg(avr)] extern "avr-interrupt" fn avr_ret_unit() -> () { - unsafe { hint::unreachable_unchecked() } + () } #[cfg(msp430)] extern "msp430-interrupt" fn msp430_ret_unit() -> () { - unsafe { hint::unreachable_unchecked() } + () } #[cfg(any(riscv32,riscv64))] extern "riscv-interrupt-m" fn riscv_m_ret_unit() -> () { - unsafe { hint::unreachable_unchecked() } + () } #[cfg(any(riscv32,riscv64))] extern "riscv-interrupt-s" fn riscv_s_ret_unit() -> () { - unsafe { hint::unreachable_unchecked() } + () } #[cfg(any(x64,i686))] extern "x86-interrupt" fn x86_ret_unit() -> () { - unsafe { hint::unreachable_unchecked() } + () } /* extern "interrupt" fnptrs can return ! too */ -- cgit 1.4.1-3-g733a5 From 7804d949dfcc32003dfd5412b44066d3546e4299 Mon Sep 17 00:00:00 2001 From: Jayden Qi Date: Sat, 22 Feb 2025 13:03:50 -0800 Subject: fix: wasm-bare targets compiling x86 builtins Added sanity check to bootstrap to hard error on wasm builds without clang, and changed distribution image `dist-various-2` to use clang to build for official targets. --- src/bootstrap/src/core/sanity.rs | 11 +++++++++++ src/ci/docker/host-x86_64/dist-various-2/Dockerfile | 4 ++++ src/ci/docker/host-x86_64/test-various/Dockerfile | 7 ++++--- 3 files changed, 19 insertions(+), 3 deletions(-) diff --git a/src/bootstrap/src/core/sanity.rs b/src/bootstrap/src/core/sanity.rs index 958058d982b..fa6987d4bf5 100644 --- a/src/bootstrap/src/core/sanity.rs +++ b/src/bootstrap/src/core/sanity.rs @@ -328,6 +328,17 @@ than building it. .entry(*target) .or_insert_with(|| Target::from_triple(&target.triple)); + // compiler-rt c fallbacks for wasm cannot be built with gcc + if target.contains("wasm") // bare metal targets without wasi sdk + && (build.config.optimized_compiler_builtins(*target) + || build.config.rust_std_features.contains("compiler-builtins-c")) + { + let is_clang = build.cc_tool(*target).is_like_clang(); + if !is_clang { + panic!("only clang supports building c code for wasm targets"); + } + } + if (target.contains("-none-") || target.contains("nvptx")) && build.no_std(*target) == Some(false) { diff --git a/src/ci/docker/host-x86_64/dist-various-2/Dockerfile b/src/ci/docker/host-x86_64/dist-various-2/Dockerfile index e1d83d36087..931cc688402 100644 --- a/src/ci/docker/host-x86_64/dist-various-2/Dockerfile +++ b/src/ci/docker/host-x86_64/dist-various-2/Dockerfile @@ -59,6 +59,10 @@ ENV \ CXX_i686_unknown_uefi=clang++-11 \ CC_x86_64_unknown_uefi=clang-11 \ CXX_x86_64_unknown_uefi=clang++-11 \ + CC_wasm32_unknown_unknown=clang-11 \ + CXX_wasm32_unknown_unknown=clang++-11 \ + CC_wasm32v1_none=clang-11 \ + CXX_wasm32v1_none=clang++-11 \ CC=gcc-9 \ CXX=g++-9 diff --git a/src/ci/docker/host-x86_64/test-various/Dockerfile b/src/ci/docker/host-x86_64/test-various/Dockerfile index 8d2e45ae497..6c3e62267e1 100644 --- a/src/ci/docker/host-x86_64/test-various/Dockerfile +++ b/src/ci/docker/host-x86_64/test-various/Dockerfile @@ -4,6 +4,7 @@ ARG DEBIAN_FRONTEND=noninteractive RUN apt-get update && apt-get install -y --no-install-recommends \ clang-11 \ llvm-11 \ + gcc-multilib \ g++ \ make \ ninja-build \ @@ -59,8 +60,8 @@ RUN curl -L https://github.com/bytecodealliance/wasmtime/releases/download/v19.0 tar -xJ ENV PATH "$PATH:/wasmtime-v19.0.0-x86_64-linux" -ENV WASM_TARGETS=wasm32-wasip1 -ENV WASM_SCRIPT python3 /checkout/x.py --stage 2 test --host='' --target $WASM_TARGETS \ +ENV WASM_WASIP_TARGET=wasm32-wasip1 +ENV WASM_WASIP_SCRIPT python3 /checkout/x.py --stage 2 test --host='' --target $WASM_WASIP_TARGET \ tests/run-make \ tests/ui \ tests/mir-opt \ @@ -90,4 +91,4 @@ ENV UEFI_TARGETS=aarch64-unknown-uefi,i686-unknown-uefi,x86_64-unknown-uefi \ ENV UEFI_SCRIPT python3 /checkout/x.py --stage 2 build --host='' --target $UEFI_TARGETS && \ python3 -u /uefi_qemu_test/run.py -ENV SCRIPT $WASM_SCRIPT && $NVPTX_SCRIPT && $MUSL_SCRIPT && $UEFI_SCRIPT +ENV SCRIPT $WASM_WASIP_SCRIPT && $NVPTX_SCRIPT && $MUSL_SCRIPT && $UEFI_SCRIPT -- cgit 1.4.1-3-g733a5 From e2d2926e6840294fe2e0b1a72d162e8d9facf8ea Mon Sep 17 00:00:00 2001 From: Jayden Qi Date: Fri, 14 Mar 2025 18:40:11 -0700 Subject: fix: install correct cc for wasm32-unknown-emscripten Also fixed a typo in the sanity check for bootstrap, as we are checking for clang-likeness in every wasm target. --- src/bootstrap/src/core/sanity.rs | 2 +- src/ci/docker/host-x86_64/dist-various-1/Dockerfile | 9 +++++++++ .../docker/host-x86_64/dist-various-1/install-emscripten.sh | 12 ++++++++++++ 3 files changed, 22 insertions(+), 1 deletion(-) create mode 100755 src/ci/docker/host-x86_64/dist-various-1/install-emscripten.sh diff --git a/src/bootstrap/src/core/sanity.rs b/src/bootstrap/src/core/sanity.rs index fa6987d4bf5..37b2d782931 100644 --- a/src/bootstrap/src/core/sanity.rs +++ b/src/bootstrap/src/core/sanity.rs @@ -329,7 +329,7 @@ than building it. .or_insert_with(|| Target::from_triple(&target.triple)); // compiler-rt c fallbacks for wasm cannot be built with gcc - if target.contains("wasm") // bare metal targets without wasi sdk + if target.contains("wasm") && (build.config.optimized_compiler_builtins(*target) || build.config.rust_std_features.contains("compiler-builtins-c")) { diff --git a/src/ci/docker/host-x86_64/dist-various-1/Dockerfile b/src/ci/docker/host-x86_64/dist-various-1/Dockerfile index 5c459e5cd18..1e45ef45c9b 100644 --- a/src/ci/docker/host-x86_64/dist-various-1/Dockerfile +++ b/src/ci/docker/host-x86_64/dist-various-1/Dockerfile @@ -55,6 +55,15 @@ RUN ./install-riscv64-none-elf.sh COPY host-x86_64/dist-various-1/install-riscv32-none-elf.sh /build RUN ./install-riscv32-none-elf.sh +COPY host-x86_64/dist-various-1/install-llvm-mingw.sh /build +RUN ./install-llvm-mingw.sh + +COPY host-x86_64/dist-various-1/install-emscripten.sh /build +RUN ./install-emscripten.sh + +# Add Emscripten to PATH +ENV PATH="/build/emsdk:/build/emsdk/upstream/emscripten:/build/emsdk/node/current/bin:${PATH}" + # Suppress some warnings in the openwrt toolchains we downloaded ENV STAGING_DIR=/tmp diff --git a/src/ci/docker/host-x86_64/dist-various-1/install-emscripten.sh b/src/ci/docker/host-x86_64/dist-various-1/install-emscripten.sh new file mode 100755 index 00000000000..eeb54ca67f7 --- /dev/null +++ b/src/ci/docker/host-x86_64/dist-various-1/install-emscripten.sh @@ -0,0 +1,12 @@ +#!/usr/bin/env bash +set -ex + +apt-get update +apt-get install -y --no-install-recommends \ + nodejs \ + default-jre + +git clone https://github.com/emscripten-core/emsdk.git +cd emsdk +./emsdk install latest +./emsdk activate latest -- cgit 1.4.1-3-g733a5 From 2a0fdef9260a72ded605ae1d980cad70644f25cb Mon Sep 17 00:00:00 2001 From: Jayden Qi Date: Tue, 1 Jul 2025 14:12:01 -0700 Subject: fix: error message --- src/bootstrap/src/core/sanity.rs | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/bootstrap/src/core/sanity.rs b/src/bootstrap/src/core/sanity.rs index 37b2d782931..295faa25e1c 100644 --- a/src/bootstrap/src/core/sanity.rs +++ b/src/bootstrap/src/core/sanity.rs @@ -333,9 +333,14 @@ than building it. && (build.config.optimized_compiler_builtins(*target) || build.config.rust_std_features.contains("compiler-builtins-c")) { - let is_clang = build.cc_tool(*target).is_like_clang(); - if !is_clang { - panic!("only clang supports building c code for wasm targets"); + let cc_tool = build.cc_tool(*target); + if !cc_tool.is_like_clang() { + panic!( + "Clang is required to build C code for Wasm targets, got `{}` instead\n\ + this is because compiler-builtins is configured to build C source. Either \ + ensure Clang is used, or adjust this configuration.", + cc_tool.path().display() + ); } } -- cgit 1.4.1-3-g733a5 From 8b1dbac4e508c4b27f0461ad2631b6df07f1af73 Mon Sep 17 00:00:00 2001 From: Jayden Qi Date: Tue, 1 Jul 2025 14:12:21 -0700 Subject: fix: remove unneeded(?) install script --- src/ci/docker/host-x86_64/dist-various-1/Dockerfile | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/ci/docker/host-x86_64/dist-various-1/Dockerfile b/src/ci/docker/host-x86_64/dist-various-1/Dockerfile index 1e45ef45c9b..4d5980027ca 100644 --- a/src/ci/docker/host-x86_64/dist-various-1/Dockerfile +++ b/src/ci/docker/host-x86_64/dist-various-1/Dockerfile @@ -55,9 +55,6 @@ RUN ./install-riscv64-none-elf.sh COPY host-x86_64/dist-various-1/install-riscv32-none-elf.sh /build RUN ./install-riscv32-none-elf.sh -COPY host-x86_64/dist-various-1/install-llvm-mingw.sh /build -RUN ./install-llvm-mingw.sh - COPY host-x86_64/dist-various-1/install-emscripten.sh /build RUN ./install-emscripten.sh -- cgit 1.4.1-3-g733a5 From 3ddd2ef8c518efe10c811a229f056e96864d83c1 Mon Sep 17 00:00:00 2001 From: Manish Goregaokar Date: Wed, 2 Jul 2025 13:15:07 -0700 Subject: Mention that casting to *const () is a way to roundtrip with from_raw_parts --- library/core/src/ptr/metadata.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/library/core/src/ptr/metadata.rs b/library/core/src/ptr/metadata.rs index 0deac3621e8..dc3ec3fd199 100644 --- a/library/core/src/ptr/metadata.rs +++ b/library/core/src/ptr/metadata.rs @@ -106,6 +106,9 @@ pub const fn metadata(ptr: *const T) -> ::Metadat /// For slices, see the documentation of [`slice::from_raw_parts`] for safety requirements. /// For trait objects, the metadata must come from a pointer to the same underlying erased type. /// +/// If you are attempting to deconstruct a DST in a generic context to be reconstructed later, +/// a thin pointer can always be obtained by casting `*const T` to `*const ()`. +/// /// [`slice::from_raw_parts`]: crate::slice::from_raw_parts #[unstable(feature = "ptr_metadata", issue = "81513")] #[inline] -- cgit 1.4.1-3-g733a5 From 937044097b8fc23b387ab58fc233109551e8536f Mon Sep 17 00:00:00 2001 From: Deadbeef Date: Thu, 3 Jul 2025 23:43:02 +0800 Subject: stabilize `const_array_each_ref` --- library/core/src/array/mod.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/library/core/src/array/mod.rs b/library/core/src/array/mod.rs index 5a46c04527b..1739da105d1 100644 --- a/library/core/src/array/mod.rs +++ b/library/core/src/array/mod.rs @@ -618,11 +618,11 @@ impl [T; N] { /// assert_eq!(strings.len(), 3); /// ``` #[stable(feature = "array_methods", since = "1.77.0")] - #[rustc_const_unstable(feature = "const_array_each_ref", issue = "133289")] + #[rustc_const_stable(feature = "const_array_each_ref", since = "CURRENT_RUSTC_VERSION")] pub const fn each_ref(&self) -> [&T; N] { let mut buf = [null::(); N]; - // FIXME(const-hack): We would like to simply use iterators for this (as in the original implementation), but this is not allowed in constant expressions. + // FIXME(const_trait_impl): We would like to simply use iterators for this (as in the original implementation), but this is not allowed in constant expressions. let mut i = 0; while i < N { buf[i] = &raw const self[i]; @@ -649,11 +649,11 @@ impl [T; N] { /// assert_eq!(floats, [0.0, 2.7, -1.0]); /// ``` #[stable(feature = "array_methods", since = "1.77.0")] - #[rustc_const_unstable(feature = "const_array_each_ref", issue = "133289")] + #[rustc_const_stable(feature = "const_array_each_ref", since = "CURRENT_RUSTC_VERSION")] pub const fn each_mut(&mut self) -> [&mut T; N] { let mut buf = [null_mut::(); N]; - // FIXME(const-hack): We would like to simply use iterators for this (as in the original implementation), but this is not allowed in constant expressions. + // FIXME(const_trait_impl): We would like to simply use iterators for this (as in the original implementation), but this is not allowed in constant expressions. let mut i = 0; while i < N { buf[i] = &raw mut self[i]; -- cgit 1.4.1-3-g733a5 From dee1b192cb4343cbe228dcf1e553b6f380595b53 Mon Sep 17 00:00:00 2001 From: Jayden Qi Date: Thu, 3 Jul 2025 15:17:36 -0700 Subject: fix: allow emcc --- src/bootstrap/src/core/sanity.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/bootstrap/src/core/sanity.rs b/src/bootstrap/src/core/sanity.rs index 295faa25e1c..d26d4ce1ba8 100644 --- a/src/bootstrap/src/core/sanity.rs +++ b/src/bootstrap/src/core/sanity.rs @@ -334,7 +334,8 @@ than building it. || build.config.rust_std_features.contains("compiler-builtins-c")) { let cc_tool = build.cc_tool(*target); - if !cc_tool.is_like_clang() { + if !cc_tool.is_like_clang() && !cc_tool.path().ends_with("emcc") { + // emcc works as well panic!( "Clang is required to build C code for Wasm targets, got `{}` instead\n\ this is because compiler-builtins is configured to build C source. Either \ -- cgit 1.4.1-3-g733a5 From 6030997800b681cee91acf6ef8aba199d03041a6 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Fri, 23 May 2025 08:52:52 -0400 Subject: Remove `verify_inside_clippy_dir` since we always set the current directory. --- clippy_dev/src/setup/git_hook.rs | 6 ------ clippy_dev/src/setup/mod.rs | 20 -------------------- clippy_dev/src/setup/toolchain.rs | 6 ------ clippy_dev/src/setup/vscode.rs | 6 ------ 4 files changed, 38 deletions(-) diff --git a/clippy_dev/src/setup/git_hook.rs b/clippy_dev/src/setup/git_hook.rs index c7c53bc69d0..c5a1e8264c7 100644 --- a/clippy_dev/src/setup/git_hook.rs +++ b/clippy_dev/src/setup/git_hook.rs @@ -1,8 +1,6 @@ use std::fs; use std::path::Path; -use super::verify_inside_clippy_dir; - /// Rusts setup uses `git rev-parse --git-common-dir` to get the root directory of the repo. /// I've decided against this for the sake of simplicity and to make sure that it doesn't install /// the hook if `clippy_dev` would be used in the rust tree. The hook also references this tool @@ -35,10 +33,6 @@ pub fn install_hook(force_override: bool) { } fn check_precondition(force_override: bool) -> bool { - if !verify_inside_clippy_dir() { - return false; - } - // Make sure that we can find the git repository let git_path = Path::new(REPO_GIT_DIR); if !git_path.exists() || !git_path.is_dir() { diff --git a/clippy_dev/src/setup/mod.rs b/clippy_dev/src/setup/mod.rs index b0d31814639..5e938fff126 100644 --- a/clippy_dev/src/setup/mod.rs +++ b/clippy_dev/src/setup/mod.rs @@ -2,23 +2,3 @@ pub mod git_hook; pub mod intellij; pub mod toolchain; pub mod vscode; - -use std::path::Path; - -const CLIPPY_DEV_DIR: &str = "clippy_dev"; - -/// This function verifies that the tool is being executed in the clippy directory. -/// This is useful to ensure that setups only modify Clippy's resources. The verification -/// is done by checking that `clippy_dev` is a sub directory of the current directory. -/// -/// It will print an error message and return `false` if the directory could not be -/// verified. -fn verify_inside_clippy_dir() -> bool { - let path = Path::new(CLIPPY_DEV_DIR); - if path.exists() && path.is_dir() { - true - } else { - eprintln!("error: unable to verify that the working directory is clippy's directory"); - false - } -} diff --git a/clippy_dev/src/setup/toolchain.rs b/clippy_dev/src/setup/toolchain.rs index ecd80215f7e..c70b56461a7 100644 --- a/clippy_dev/src/setup/toolchain.rs +++ b/clippy_dev/src/setup/toolchain.rs @@ -8,13 +8,7 @@ use walkdir::WalkDir; use crate::utils::exit_if_err; -use super::verify_inside_clippy_dir; - pub fn create(standalone: bool, force: bool, release: bool, name: &str) { - if !verify_inside_clippy_dir() { - return; - } - let rustup_home = std::env::var("RUSTUP_HOME").unwrap(); let toolchain = std::env::var("RUSTUP_TOOLCHAIN").unwrap(); diff --git a/clippy_dev/src/setup/vscode.rs b/clippy_dev/src/setup/vscode.rs index a37c873eed4..a24aef65991 100644 --- a/clippy_dev/src/setup/vscode.rs +++ b/clippy_dev/src/setup/vscode.rs @@ -1,8 +1,6 @@ use std::fs; use std::path::Path; -use super::verify_inside_clippy_dir; - const VSCODE_DIR: &str = ".vscode"; const TASK_SOURCE_FILE: &str = "util/etc/vscode-tasks.json"; const TASK_TARGET_FILE: &str = ".vscode/tasks.json"; @@ -22,10 +20,6 @@ pub fn install_tasks(force_override: bool) { } fn check_install_precondition(force_override: bool) -> bool { - if !verify_inside_clippy_dir() { - return false; - } - let vs_dir_path = Path::new(VSCODE_DIR); if vs_dir_path.exists() { // verify the target will be valid -- cgit 1.4.1-3-g733a5 From fc8bf97095d6578be5464c4f10e1f11971738d80 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Fri, 23 May 2025 13:19:51 -0400 Subject: Rename `exit_if_err` to `run_exit_on_err` --- clippy_dev/src/dogfood.rs | 48 +++++++++++++++++---------------------- clippy_dev/src/lint.rs | 36 ++++++++++++++--------------- clippy_dev/src/setup/toolchain.rs | 12 ++++------ clippy_dev/src/utils.rs | 26 ++++++++++----------- 4 files changed, 55 insertions(+), 67 deletions(-) diff --git a/clippy_dev/src/dogfood.rs b/clippy_dev/src/dogfood.rs index 7e9d92458d0..c1ee3633d3e 100644 --- a/clippy_dev/src/dogfood.rs +++ b/clippy_dev/src/dogfood.rs @@ -1,4 +1,5 @@ -use crate::utils::exit_if_err; +use crate::utils::run_exit_on_err; +use itertools::Itertools; use std::process::Command; /// # Panics @@ -6,30 +7,23 @@ use std::process::Command; /// Panics if unable to run the dogfood test #[allow(clippy::fn_params_excessive_bools)] pub fn dogfood(fix: bool, allow_dirty: bool, allow_staged: bool, allow_no_vcs: bool) { - let mut cmd = Command::new("cargo"); - - cmd.args(["test", "--test", "dogfood"]) - .args(["--features", "internal"]) - .args(["--", "dogfood_clippy", "--nocapture"]); - - let mut dogfood_args = Vec::new(); - if fix { - dogfood_args.push("--fix"); - } - - if allow_dirty { - dogfood_args.push("--allow-dirty"); - } - - if allow_staged { - dogfood_args.push("--allow-staged"); - } - - if allow_no_vcs { - dogfood_args.push("--allow-no-vcs"); - } - - cmd.env("__CLIPPY_DOGFOOD_ARGS", dogfood_args.join(" ")); - - exit_if_err(cmd.status()); + run_exit_on_err( + "cargo test", + Command::new("cargo") + .args(["test", "--test", "dogfood"]) + .args(["--features", "internal"]) + .args(["--", "dogfood_clippy", "--nocapture"]) + .env( + "__CLIPPY_DOGFOOD_ARGS", + [ + if fix { "--fix" } else { "" }, + if allow_dirty { "--allow-dirty" } else { "" }, + if allow_staged { "--allow-staged" } else { "" }, + if allow_no_vcs { "--allow-no-vcs" } else { "" }, + ] + .iter() + .filter(|x| !x.is_empty()) + .join(" "), + ), + ); } diff --git a/clippy_dev/src/lint.rs b/clippy_dev/src/lint.rs index 0d66f167a38..f450bf5d8a2 100644 --- a/clippy_dev/src/lint.rs +++ b/clippy_dev/src/lint.rs @@ -1,4 +1,4 @@ -use crate::utils::{cargo_clippy_path, exit_if_err}; +use crate::utils::{cargo_clippy_path, run_exit_on_err}; use std::process::{self, Command}; use std::{env, fs}; @@ -12,8 +12,9 @@ pub fn run<'a>(path: &str, edition: &str, args: impl Iterator }; if is_file { - exit_if_err( - Command::new(env::var("CARGO").unwrap_or_else(|_| "cargo".into())) + run_exit_on_err( + "cargo run", + Command::new(env::var("CARGO").unwrap_or("cargo".into())) .args(["run", "--bin", "clippy-driver", "--"]) .args(["-L", "./target/debug"]) .args(["-Z", "no-codegen"]) @@ -21,24 +22,21 @@ pub fn run<'a>(path: &str, edition: &str, args: impl Iterator .arg(path) .args(args) // Prevent rustc from creating `rustc-ice-*` files the console output is enough. - .env("RUSTC_ICE", "0") - .status(), + .env("RUSTC_ICE", "0"), ); } else { - exit_if_err( - Command::new(env::var("CARGO").unwrap_or_else(|_| "cargo".into())) - .arg("build") - .status(), + run_exit_on_err( + "cargo build", + Command::new(env::var("CARGO").unwrap_or_else(|_| "cargo".into())).arg("build"), + ); + run_exit_on_err( + "cargo clippy", + Command::new(cargo_clippy_path()) + .arg("clippy") + .args(args) + // Prevent rustc from creating `rustc-ice-*` files the console output is enough. + .env("RUSTC_ICE", "0") + .current_dir(path), ); - - let status = Command::new(cargo_clippy_path()) - .arg("clippy") - .args(args) - // Prevent rustc from creating `rustc-ice-*` files the console output is enough. - .env("RUSTC_ICE", "0") - .current_dir(path) - .status(); - - exit_if_err(status); } } diff --git a/clippy_dev/src/setup/toolchain.rs b/clippy_dev/src/setup/toolchain.rs index c70b56461a7..4c1db4e8a67 100644 --- a/clippy_dev/src/setup/toolchain.rs +++ b/clippy_dev/src/setup/toolchain.rs @@ -1,3 +1,4 @@ +use crate::utils::run_exit_on_err; use std::env::consts::EXE_SUFFIX; use std::env::current_dir; use std::ffi::OsStr; @@ -6,8 +7,6 @@ use std::path::{Path, PathBuf}; use std::process::Command; use walkdir::WalkDir; -use crate::utils::exit_if_err; - pub fn create(standalone: bool, force: bool, release: bool, name: &str) { let rustup_home = std::env::var("RUSTUP_HOME").unwrap(); let toolchain = std::env::var("RUSTUP_TOOLCHAIN").unwrap(); @@ -45,11 +44,10 @@ pub fn create(standalone: bool, force: bool, release: bool, name: &str) { } } - let status = Command::new("cargo") - .arg("build") - .args(release.then_some("--release")) - .status(); - exit_if_err(status); + run_exit_on_err( + "cargo build", + Command::new("cargo").arg("build").args(release.then_some("--release")), + ); install_bin("cargo-clippy", &dest, standalone, release); install_bin("clippy-driver", &dest, standalone, release); diff --git a/clippy_dev/src/utils.rs b/clippy_dev/src/utils.rs index 89962a11034..f518460f582 100644 --- a/clippy_dev/src/utils.rs +++ b/clippy_dev/src/utils.rs @@ -8,7 +8,7 @@ use std::ffi::OsStr; use std::fs::{self, OpenOptions}; use std::io::{self, Read as _, Seek as _, SeekFrom, Write}; use std::path::{Path, PathBuf}; -use std::process::{self, Command, ExitStatus, Stdio}; +use std::process::{self, Command, Stdio}; use std::{env, thread}; use walkdir::WalkDir; @@ -288,19 +288,6 @@ impl ClippyInfo { } } -/// # Panics -/// Panics if given command result was failed. -pub fn exit_if_err(status: io::Result) { - match status.expect("failed to run command").code() { - Some(0) => {}, - Some(n) => process::exit(n), - None => { - eprintln!("Killed by signal"); - process::exit(1); - }, - } -} - #[derive(Clone, Copy)] pub enum UpdateStatus { Unchanged, @@ -653,6 +640,17 @@ pub fn write_file(path: &Path, contents: &str) { expect_action(fs::write(path, contents), ErrAction::Write, path); } +pub fn run_exit_on_err(path: &(impl AsRef + ?Sized), cmd: &mut Command) { + match expect_action(cmd.status(), ErrAction::Run, path.as_ref()).code() { + Some(0) => {}, + Some(n) => process::exit(n), + None => { + eprintln!("{} killed by signal", path.as_ref().display()); + process::exit(1); + }, + } +} + #[must_use] pub fn run_with_output(path: &(impl AsRef + ?Sized), cmd: &mut Command) -> Vec { fn f(path: &Path, cmd: &mut Command) -> Vec { -- cgit 1.4.1-3-g733a5 From ed50c8a0e987511d10ec11936f9e88fb297b18b4 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Fri, 23 May 2025 14:22:45 -0400 Subject: Make `dev serve` actually detect all updates --- clippy_dev/src/serve.rs | 109 +++++++++++++++++++++++++++++++----------------- 1 file changed, 71 insertions(+), 38 deletions(-) diff --git a/clippy_dev/src/serve.rs b/clippy_dev/src/serve.rs index 498ffeba9d6..73d9a1e8237 100644 --- a/clippy_dev/src/serve.rs +++ b/clippy_dev/src/serve.rs @@ -1,7 +1,11 @@ +use crate::utils::{ErrAction, expect_action}; +use core::fmt::Display; +use core::mem; use std::path::Path; use std::process::Command; use std::time::{Duration, SystemTime}; -use std::{env, thread}; +use std::{env, fs, thread}; +use walkdir::WalkDir; #[cfg(windows)] const PYTHON: &str = "python"; @@ -18,56 +22,85 @@ pub fn run(port: u16, lint: Option) -> ! { Some(lint) => format!("http://localhost:{port}/#{lint}"), }); + let mut last_update = mtime("util/gh-pages/index.html"); loop { - let index_time = mtime("util/gh-pages/index.html"); - let times = [ - "clippy_lints/src", - "util/gh-pages/index_template.html", - "tests/compile-test.rs", - ] - .map(mtime); - - if times.iter().any(|&time| index_time < time) { - Command::new(env::var("CARGO").unwrap_or_else(|_| "cargo".into())) - .arg("collect-metadata") - .spawn() - .unwrap() - .wait() - .unwrap(); + if is_metadata_outdated(mem::replace(&mut last_update, SystemTime::now())) { + // Ignore the command result; we'll fall back to displaying the old metadata. + let _ = expect_action( + Command::new(env::var("CARGO").unwrap_or_else(|_| "cargo".into())) + .arg("collect-metadata") + .status(), + ErrAction::Run, + "cargo collect-metadata", + ); + last_update = SystemTime::now(); } + + // Only start the web server the first time around. if let Some(url) = url.take() { thread::spawn(move || { - let mut child = Command::new(PYTHON) - .arg("-m") - .arg("http.server") - .arg(port.to_string()) - .current_dir("util/gh-pages") - .spawn() - .unwrap(); + let mut child = expect_action( + Command::new(PYTHON) + .args(["-m", "http.server", port.to_string().as_str()]) + .current_dir("util/gh-pages") + .spawn(), + ErrAction::Run, + "python -m http.server", + ); // Give some time for python to start thread::sleep(Duration::from_millis(500)); // Launch browser after first export.py has completed and http.server is up let _result = opener::open(url); - child.wait().unwrap(); + expect_action(child.wait(), ErrAction::Run, "python -m http.server"); }); } + + // Delay to avoid updating the metadata too aggressively. thread::sleep(Duration::from_millis(1000)); } } -fn mtime(path: impl AsRef) -> SystemTime { - let path = path.as_ref(); - if path.is_dir() { - path.read_dir() - .into_iter() - .flatten() - .flatten() - .map(|entry| mtime(entry.path())) - .max() - .unwrap_or(SystemTime::UNIX_EPOCH) - } else { - path.metadata() - .and_then(|metadata| metadata.modified()) - .unwrap_or(SystemTime::UNIX_EPOCH) +fn log_err_and_continue(res: Result, path: &Path) -> Option { + match res { + Ok(x) => Some(x), + Err(ref e) => { + eprintln!("error reading `{}`: {e}", path.display()); + None + }, } } + +fn mtime(path: &str) -> SystemTime { + log_err_and_continue(fs::metadata(path), path.as_ref()) + .and_then(|metadata| log_err_and_continue(metadata.modified(), path.as_ref())) + .unwrap_or(SystemTime::UNIX_EPOCH) +} + +fn is_metadata_outdated(time: SystemTime) -> bool { + // Ignore all IO errors here. We don't want to stop them from hosting the server. + if time < mtime("util/gh-pages/index_template.html") || time < mtime("tests/compile-test.rs") { + return true; + } + let Some(dir) = log_err_and_continue(fs::read_dir("."), ".".as_ref()) else { + return false; + }; + dir.map_while(|e| log_err_and_continue(e, ".".as_ref())).any(|e| { + let name = e.file_name(); + let name_bytes = name.as_encoded_bytes(); + if (name_bytes.starts_with(b"clippy_lints") && name_bytes != b"clippy_lints_internal") + || name_bytes == b"clippy_config" + { + WalkDir::new(&name) + .into_iter() + .map_while(|e| log_err_and_continue(e, name.as_ref())) + .filter(|e| e.file_type().is_file()) + .filter_map(|e| { + log_err_and_continue(e.metadata(), e.path()) + .and_then(|m| log_err_and_continue(m.modified(), e.path())) + }) + .any(|ftime| time < ftime) + } else { + false + } + }) +} -- cgit 1.4.1-3-g733a5 From 46ff0a85657fcc08f36fec0a06455536c3296121 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Fri, 23 May 2025 14:32:10 -0400 Subject: Add helper function for creating cargo commands. --- clippy_dev/src/dogfood.rs | 5 ++--- clippy_dev/src/lint.rs | 11 ++++------- clippy_dev/src/serve.rs | 8 +++----- clippy_dev/src/setup/toolchain.rs | 5 ++--- clippy_dev/src/utils.rs | 10 ++++++++++ 5 files changed, 21 insertions(+), 18 deletions(-) diff --git a/clippy_dev/src/dogfood.rs b/clippy_dev/src/dogfood.rs index c1ee3633d3e..d0fca952b93 100644 --- a/clippy_dev/src/dogfood.rs +++ b/clippy_dev/src/dogfood.rs @@ -1,6 +1,5 @@ -use crate::utils::run_exit_on_err; +use crate::utils::{cargo_cmd, run_exit_on_err}; use itertools::Itertools; -use std::process::Command; /// # Panics /// @@ -9,7 +8,7 @@ use std::process::Command; pub fn dogfood(fix: bool, allow_dirty: bool, allow_staged: bool, allow_no_vcs: bool) { run_exit_on_err( "cargo test", - Command::new("cargo") + cargo_cmd() .args(["test", "--test", "dogfood"]) .args(["--features", "internal"]) .args(["--", "dogfood_clippy", "--nocapture"]) diff --git a/clippy_dev/src/lint.rs b/clippy_dev/src/lint.rs index f450bf5d8a2..d8d3e39116b 100644 --- a/clippy_dev/src/lint.rs +++ b/clippy_dev/src/lint.rs @@ -1,6 +1,6 @@ -use crate::utils::{cargo_clippy_path, run_exit_on_err}; +use crate::utils::{cargo_clippy_path, cargo_cmd, run_exit_on_err}; +use std::fs; use std::process::{self, Command}; -use std::{env, fs}; pub fn run<'a>(path: &str, edition: &str, args: impl Iterator) { let is_file = match fs::metadata(path) { @@ -14,7 +14,7 @@ pub fn run<'a>(path: &str, edition: &str, args: impl Iterator if is_file { run_exit_on_err( "cargo run", - Command::new(env::var("CARGO").unwrap_or("cargo".into())) + cargo_cmd() .args(["run", "--bin", "clippy-driver", "--"]) .args(["-L", "./target/debug"]) .args(["-Z", "no-codegen"]) @@ -25,10 +25,7 @@ pub fn run<'a>(path: &str, edition: &str, args: impl Iterator .env("RUSTC_ICE", "0"), ); } else { - run_exit_on_err( - "cargo build", - Command::new(env::var("CARGO").unwrap_or_else(|_| "cargo".into())).arg("build"), - ); + run_exit_on_err("cargo build", cargo_cmd().arg("build")); run_exit_on_err( "cargo clippy", Command::new(cargo_clippy_path()) diff --git a/clippy_dev/src/serve.rs b/clippy_dev/src/serve.rs index 73d9a1e8237..d9e01813381 100644 --- a/clippy_dev/src/serve.rs +++ b/clippy_dev/src/serve.rs @@ -1,10 +1,10 @@ -use crate::utils::{ErrAction, expect_action}; +use crate::utils::{ErrAction, cargo_cmd, expect_action}; use core::fmt::Display; use core::mem; use std::path::Path; use std::process::Command; use std::time::{Duration, SystemTime}; -use std::{env, fs, thread}; +use std::{fs, thread}; use walkdir::WalkDir; #[cfg(windows)] @@ -27,9 +27,7 @@ pub fn run(port: u16, lint: Option) -> ! { if is_metadata_outdated(mem::replace(&mut last_update, SystemTime::now())) { // Ignore the command result; we'll fall back to displaying the old metadata. let _ = expect_action( - Command::new(env::var("CARGO").unwrap_or_else(|_| "cargo".into())) - .arg("collect-metadata") - .status(), + cargo_cmd().arg("collect-metadata").status(), ErrAction::Run, "cargo collect-metadata", ); diff --git a/clippy_dev/src/setup/toolchain.rs b/clippy_dev/src/setup/toolchain.rs index 4c1db4e8a67..c64ae4ef3c3 100644 --- a/clippy_dev/src/setup/toolchain.rs +++ b/clippy_dev/src/setup/toolchain.rs @@ -1,10 +1,9 @@ -use crate::utils::run_exit_on_err; +use crate::utils::{cargo_cmd, run_exit_on_err}; use std::env::consts::EXE_SUFFIX; use std::env::current_dir; use std::ffi::OsStr; use std::fs; use std::path::{Path, PathBuf}; -use std::process::Command; use walkdir::WalkDir; pub fn create(standalone: bool, force: bool, release: bool, name: &str) { @@ -46,7 +45,7 @@ pub fn create(standalone: bool, force: bool, release: bool, name: &str) { run_exit_on_err( "cargo build", - Command::new("cargo").arg("build").args(release.then_some("--release")), + cargo_cmd().arg("build").args(release.then_some("--release")), ); install_bin("cargo-clippy", &dest, standalone, release); diff --git a/clippy_dev/src/utils.rs b/clippy_dev/src/utils.rs index f518460f582..4b77a49a9f6 100644 --- a/clippy_dev/src/utils.rs +++ b/clippy_dev/src/utils.rs @@ -130,6 +130,16 @@ pub fn cargo_clippy_path() -> PathBuf { path } +/// Creates a `Command` for running cargo. +#[must_use] +pub fn cargo_cmd() -> Command { + if let Some(path) = env::var_os("CARGO") { + Command::new(path) + } else { + Command::new("cargo") + } +} + #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] pub struct Version { pub major: u16, -- cgit 1.4.1-3-g733a5 From ed1088eec4235e7bb86ff9ea4c231c5b5472f7b7 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Fri, 23 May 2025 14:37:52 -0400 Subject: Use `track_caller` more for better error spans in `clippy_dev` --- clippy_dev/src/utils.rs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/clippy_dev/src/utils.rs b/clippy_dev/src/utils.rs index 4b77a49a9f6..0c5020c6982 100644 --- a/clippy_dev/src/utils.rs +++ b/clippy_dev/src/utils.rs @@ -338,6 +338,7 @@ pub struct FileUpdater { dst_buf: String, } impl FileUpdater { + #[track_caller] fn update_file_checked_inner( &mut self, tool: &str, @@ -361,6 +362,7 @@ impl FileUpdater { } } + #[track_caller] fn update_file_inner(&mut self, path: &Path, update: &mut dyn FnMut(&Path, &str, &mut String) -> UpdateStatus) { let mut file = File::open(path, OpenOptions::new().read(true).write(true)); file.read_to_cleared_string(&mut self.src_buf); @@ -370,6 +372,7 @@ impl FileUpdater { } } + #[track_caller] pub fn update_file_checked( &mut self, tool: &str, @@ -380,6 +383,7 @@ impl FileUpdater { self.update_file_checked_inner(tool, mode, path.as_ref(), update); } + #[track_caller] pub fn update_file( &mut self, path: impl AsRef, @@ -598,6 +602,7 @@ impl<'txt> RustSearcher<'txt> { } } +#[track_caller] #[expect(clippy::must_use_candidate)] pub fn try_rename_file(old_name: &Path, new_name: &Path) -> bool { match OpenOptions::new().create_new(true).write(true).open(new_name) { @@ -620,6 +625,7 @@ pub fn try_rename_file(old_name: &Path, new_name: &Path) -> bool { } } +#[track_caller] #[expect(clippy::must_use_candidate)] pub fn try_rename_dir(old_name: &Path, new_name: &Path) -> bool { match fs::create_dir(new_name) { @@ -646,10 +652,12 @@ pub fn try_rename_dir(old_name: &Path, new_name: &Path) -> bool { } } +#[track_caller] pub fn write_file(path: &Path, contents: &str) { expect_action(fs::write(path, contents), ErrAction::Write, path); } +#[track_caller] pub fn run_exit_on_err(path: &(impl AsRef + ?Sized), cmd: &mut Command) { match expect_action(cmd.status(), ErrAction::Run, path.as_ref()).code() { Some(0) => {}, @@ -661,6 +669,7 @@ pub fn run_exit_on_err(path: &(impl AsRef + ?Sized), cmd: &mut Command) { } } +#[track_caller] #[must_use] pub fn run_with_output(path: &(impl AsRef + ?Sized), cmd: &mut Command) -> Vec { fn f(path: &Path, cmd: &mut Command) -> Vec { @@ -746,6 +755,7 @@ pub fn split_args_for_threads( } } +#[track_caller] #[expect(clippy::must_use_candidate)] pub fn delete_file_if_exists(path: &Path) -> bool { match fs::remove_file(path) { @@ -755,6 +765,7 @@ pub fn delete_file_if_exists(path: &Path) -> bool { } } +#[track_caller] pub fn delete_dir_if_exists(path: &Path) { match fs::remove_dir_all(path) { Ok(()) => {}, -- cgit 1.4.1-3-g733a5 From e624b7759282d5a76e585ff198e997e7b4ccd072 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Sat, 24 May 2025 14:17:33 -0400 Subject: Inline `cargo_clippy_path` and add a note about removing it when possible --- clippy_dev/src/lint.rs | 29 +++++++++++++++++------------ clippy_dev/src/utils.rs | 17 ----------------- 2 files changed, 17 insertions(+), 29 deletions(-) diff --git a/clippy_dev/src/lint.rs b/clippy_dev/src/lint.rs index d8d3e39116b..2d9f563cdae 100644 --- a/clippy_dev/src/lint.rs +++ b/clippy_dev/src/lint.rs @@ -1,16 +1,14 @@ -use crate::utils::{cargo_clippy_path, cargo_cmd, run_exit_on_err}; -use std::fs; -use std::process::{self, Command}; +use crate::utils::{ErrAction, cargo_cmd, expect_action, run_exit_on_err}; +use std::process::Command; +use std::{env, fs}; -pub fn run<'a>(path: &str, edition: &str, args: impl Iterator) { - let is_file = match fs::metadata(path) { - Ok(metadata) => metadata.is_file(), - Err(e) => { - eprintln!("Failed to read {path}: {e:?}"); - process::exit(1); - }, - }; +#[cfg(not(windows))] +static CARGO_CLIPPY_EXE: &str = "cargo-clippy"; +#[cfg(windows)] +static CARGO_CLIPPY_EXE: &str = "cargo-clippy.exe"; +pub fn run<'a>(path: &str, edition: &str, args: impl Iterator) { + let is_file = expect_action(fs::metadata(path), ErrAction::Read, path).is_file(); if is_file { run_exit_on_err( "cargo run", @@ -25,10 +23,17 @@ pub fn run<'a>(path: &str, edition: &str, args: impl Iterator .env("RUSTC_ICE", "0"), ); } else { + // Ideally this would just be `cargo run`, but the working directory needs to be + // set to clippy's directory when building, and the target project's directory + // when running clippy. `cargo` can only set a single working directory for both + // when using `run`. run_exit_on_err("cargo build", cargo_cmd().arg("build")); + + let mut exe = env::current_exe().expect("failed to get current executable name"); + exe.set_file_name(CARGO_CLIPPY_EXE); run_exit_on_err( "cargo clippy", - Command::new(cargo_clippy_path()) + Command::new(exe) .arg("clippy") .args(args) // Prevent rustc from creating `rustc-ice-*` files the console output is enough. diff --git a/clippy_dev/src/utils.rs b/clippy_dev/src/utils.rs index 0c5020c6982..dfa2d186097 100644 --- a/clippy_dev/src/utils.rs +++ b/clippy_dev/src/utils.rs @@ -12,11 +12,6 @@ use std::process::{self, Command, Stdio}; use std::{env, thread}; use walkdir::WalkDir; -#[cfg(not(windows))] -static CARGO_CLIPPY_EXE: &str = "cargo-clippy"; -#[cfg(windows)] -static CARGO_CLIPPY_EXE: &str = "cargo-clippy.exe"; - #[derive(Clone, Copy)] pub enum ErrAction { Open, @@ -118,18 +113,6 @@ impl<'a> File<'a> { } } -/// Returns the path to the `cargo-clippy` binary -/// -/// # Panics -/// -/// Panics if the path of current executable could not be retrieved. -#[must_use] -pub fn cargo_clippy_path() -> PathBuf { - let mut path = env::current_exe().expect("failed to get current executable name"); - path.set_file_name(CARGO_CLIPPY_EXE); - path -} - /// Creates a `Command` for running cargo. #[must_use] pub fn cargo_cmd() -> Command { -- cgit 1.4.1-3-g733a5 From 913681464e21e8c7d27ea25b8504710d3fd847d9 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Sat, 24 May 2025 14:18:06 -0400 Subject: Make `utils` module private. --- clippy_dev/src/lib.rs | 7 ++++--- clippy_dev/src/main.rs | 11 ++++++----- clippy_dev/src/utils.rs | 10 ---------- 3 files changed, 10 insertions(+), 18 deletions(-) diff --git a/clippy_dev/src/lib.rs b/clippy_dev/src/lib.rs index 3361443196a..eaf9589c8fe 100644 --- a/clippy_dev/src/lib.rs +++ b/clippy_dev/src/lib.rs @@ -16,8 +16,7 @@ )] #![allow(clippy::missing_panics_doc)] -// The `rustc_driver` crate seems to be required in order to use the `rust_lexer` crate. -#[allow(unused_extern_crates)] +#[expect(unused_extern_crates, reason = "required to link to rustc crates")] extern crate rustc_driver; extern crate rustc_lexer; extern crate rustc_literal_escaper; @@ -33,4 +32,6 @@ pub mod serve; pub mod setup; pub mod sync; pub mod update_lints; -pub mod utils; + +mod utils; +pub use utils::{ClippyInfo, UpdateMode}; diff --git a/clippy_dev/src/main.rs b/clippy_dev/src/main.rs index 26aa269fb63..5fef231f6ca 100644 --- a/clippy_dev/src/main.rs +++ b/clippy_dev/src/main.rs @@ -4,14 +4,15 @@ use clap::{Args, Parser, Subcommand}; use clippy_dev::{ - deprecate_lint, dogfood, fmt, lint, new_lint, release, rename_lint, serve, setup, sync, update_lints, utils, + ClippyInfo, UpdateMode, deprecate_lint, dogfood, fmt, lint, new_lint, release, rename_lint, serve, setup, sync, + update_lints, }; use std::convert::Infallible; use std::env; fn main() { let dev = Dev::parse(); - let clippy = utils::ClippyInfo::search_for_manifest(); + let clippy = ClippyInfo::search_for_manifest(); if let Err(e) = env::set_current_dir(&clippy.path) { panic!("error setting current directory to `{}`: {e}", clippy.path.display()); } @@ -26,8 +27,8 @@ fn main() { allow_staged, allow_no_vcs, } => dogfood::dogfood(fix, allow_dirty, allow_staged, allow_no_vcs), - DevCommand::Fmt { check } => fmt::run(utils::UpdateMode::from_check(check)), - DevCommand::UpdateLints { check } => update_lints::update(utils::UpdateMode::from_check(check)), + DevCommand::Fmt { check } => fmt::run(UpdateMode::from_check(check)), + DevCommand::UpdateLints { check } => update_lints::update(UpdateMode::from_check(check)), DevCommand::NewLint { pass, name, @@ -35,7 +36,7 @@ fn main() { r#type, msrv, } => match new_lint::create(clippy.version, pass, &name, &category, r#type.as_deref(), msrv) { - Ok(()) => update_lints::update(utils::UpdateMode::Change), + Ok(()) => update_lints::update(UpdateMode::Change), Err(e) => eprintln!("Unable to create lint: {e}"), }, DevCommand::Setup(SetupCommand { subcommand }) => match subcommand { diff --git a/clippy_dev/src/utils.rs b/clippy_dev/src/utils.rs index dfa2d186097..057951d0e33 100644 --- a/clippy_dev/src/utils.rs +++ b/clippy_dev/src/utils.rs @@ -434,7 +434,6 @@ pub enum Token<'a> { OpenParen, Pound, Semi, - Slash, } pub struct RustSearcher<'txt> { @@ -512,7 +511,6 @@ impl<'txt> RustSearcher<'txt> { | (Token::OpenParen, lexer::TokenKind::OpenParen) | (Token::Pound, lexer::TokenKind::Pound) | (Token::Semi, lexer::TokenKind::Semi) - | (Token::Slash, lexer::TokenKind::Slash) | ( Token::LitStr, lexer::TokenKind::Literal { @@ -586,7 +584,6 @@ impl<'txt> RustSearcher<'txt> { } #[track_caller] -#[expect(clippy::must_use_candidate)] pub fn try_rename_file(old_name: &Path, new_name: &Path) -> bool { match OpenOptions::new().create_new(true).write(true).open(new_name) { Ok(file) => drop(file), @@ -609,7 +606,6 @@ pub fn try_rename_file(old_name: &Path, new_name: &Path) -> bool { } #[track_caller] -#[expect(clippy::must_use_candidate)] pub fn try_rename_dir(old_name: &Path, new_name: &Path) -> bool { match fs::create_dir(new_name) { Ok(()) => {}, @@ -635,11 +631,6 @@ pub fn try_rename_dir(old_name: &Path, new_name: &Path) -> bool { } } -#[track_caller] -pub fn write_file(path: &Path, contents: &str) { - expect_action(fs::write(path, contents), ErrAction::Write, path); -} - #[track_caller] pub fn run_exit_on_err(path: &(impl AsRef + ?Sized), cmd: &mut Command) { match expect_action(cmd.status(), ErrAction::Run, path.as_ref()).code() { @@ -739,7 +730,6 @@ pub fn split_args_for_threads( } #[track_caller] -#[expect(clippy::must_use_candidate)] pub fn delete_file_if_exists(path: &Path) -> bool { match fs::remove_file(path) { Ok(()) => true, -- cgit 1.4.1-3-g733a5 From f6bdffdcba7498fd3fe7b18bca966b23da6c463f Mon Sep 17 00:00:00 2001 From: Pavel Grigorenko Date: Mon, 20 Nov 2023 02:18:52 +0300 Subject: Add Ref/RefMut try_map method --- library/core/src/cell.rs | 93 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 93 insertions(+) diff --git a/library/core/src/cell.rs b/library/core/src/cell.rs index c53a2b2beb4..2f1a74fa241 100644 --- a/library/core/src/cell.rs +++ b/library/core/src/cell.rs @@ -1573,6 +1573,47 @@ impl<'b, T: ?Sized> Ref<'b, T> { } } + /// Tries to makes a new `Ref` for a component of the borrowed data. + /// On failure, the original guard is returned alongside with the error + /// returned by the closure. + /// + /// The `RefCell` is already immutably borrowed, so this cannot fail. + /// + /// This is an associated function that needs to be used as + /// `Ref::try_map(...)`. A method would interfere with methods of the same + /// name on the contents of a `RefCell` used through `Deref`. + /// + /// # Examples + /// + /// ``` + /// #![feature(refcell_try_map)] + /// use std::cell::{RefCell, Ref}; + /// use std::str::{from_utf8, Utf8Error}; + /// + /// let c = RefCell::new(vec![0xF0, 0x9F, 0xA6 ,0x80]); + /// let b1: Ref<'_, Vec> = c.borrow(); + /// let b2: Result, _> = Ref::try_map(b1, |v| from_utf8(v)); + /// assert_eq!(&*b2.unwrap(), "🦀"); + /// + /// let c = RefCell::new(vec![0xF0, 0x9F, 0xA6]); + /// let b1: Ref<'_, Vec> = c.borrow(); + /// let b2: Result<_, (Ref<'_, Vec>, Utf8Error)> = Ref::try_map(b1, |v| from_utf8(v)); + /// let (b3, e) = b2.unwrap_err(); + /// assert_eq!(*b3, vec![0xF0, 0x9F, 0xA6]); + /// assert_eq!(e.valid_up_to(), 0); + /// ``` + #[unstable(feature = "refcell_try_map", issue = "143801")] + #[inline] + pub fn try_map( + orig: Ref<'b, T>, + f: impl FnOnce(&T) -> Result<&U, E>, + ) -> Result, (Self, E)> { + match f(&*orig) { + Ok(value) => Ok(Ref { value: NonNull::from(value), borrow: orig.borrow }), + Err(e) => Err((orig, e)), + } + } + /// Splits a `Ref` into multiple `Ref`s for different components of the /// borrowed data. /// @@ -1734,6 +1775,58 @@ impl<'b, T: ?Sized> RefMut<'b, T> { } } + /// Tries to makes a new `RefMut` for a component of the borrowed data. + /// On failure, the original guard is returned alongside with the error + /// returned by the closure. + /// + /// The `RefCell` is already mutably borrowed, so this cannot fail. + /// + /// This is an associated function that needs to be used as + /// `RefMut::try_map(...)`. A method would interfere with methods of the same + /// name on the contents of a `RefCell` used through `Deref`. + /// + /// # Examples + /// + /// ``` + /// #![feature(refcell_try_map)] + /// use std::cell::{RefCell, RefMut}; + /// use std::str::{from_utf8_mut, Utf8Error}; + /// + /// let c = RefCell::new(vec![0x68, 0x65, 0x6C, 0x6C, 0x6F]); + /// { + /// let b1: RefMut<'_, Vec> = c.borrow_mut(); + /// let b2: Result, _> = RefMut::try_map(b1, |v| from_utf8_mut(v)); + /// let mut b2 = b2.unwrap(); + /// assert_eq!(&*b2, "hello"); + /// b2.make_ascii_uppercase(); + /// } + /// assert_eq!(*c.borrow(), "HELLO".as_bytes()); + /// + /// let c = RefCell::new(vec![0xFF]); + /// let b1: RefMut<'_, Vec> = c.borrow_mut(); + /// let b2: Result<_, (RefMut<'_, Vec>, Utf8Error)> = RefMut::try_map(b1, |v| from_utf8_mut(v)); + /// let (b3, e) = b2.unwrap_err(); + /// assert_eq!(*b3, vec![0xFF]); + /// assert_eq!(e.valid_up_to(), 0); + /// ``` + #[unstable(feature = "refcell_try_map", issue = "143801")] + #[inline] + pub fn try_map( + mut orig: RefMut<'b, T>, + f: impl FnOnce(&mut T) -> Result<&mut U, E>, + ) -> Result, (Self, E)> { + // SAFETY: function holds onto an exclusive reference for the duration + // of its call through `orig`, and the pointer is only de-referenced + // inside of the function call never allowing the exclusive reference to + // escape. + match f(&mut *orig) { + Ok(value) => { + Ok(RefMut { value: NonNull::from(value), borrow: orig.borrow, marker: PhantomData }) + } + Err(e) => Err((orig, e)), + } + } + /// Splits a `RefMut` into multiple `RefMut`s for different components of the /// borrowed data. /// -- cgit 1.4.1-3-g733a5 From 87dd4695fe5fe2e76999d5040deb20a018e21942 Mon Sep 17 00:00:00 2001 From: Jules Bertholet Date: Tue, 15 Jul 2025 17:41:31 -0400 Subject: Add `Default` impls for `Pin`ned `Box`, `Rc`, `Arc` --- library/alloc/src/boxed.rs | 16 +++++++++++++++- library/alloc/src/rc.rs | 19 ++++++++++++++++--- library/alloc/src/sync.rs | 13 +++++++++++++ 3 files changed, 44 insertions(+), 4 deletions(-) diff --git a/library/alloc/src/boxed.rs b/library/alloc/src/boxed.rs index 3db37f1d16f..fa12d379c8c 100644 --- a/library/alloc/src/boxed.rs +++ b/library/alloc/src/boxed.rs @@ -1672,7 +1672,7 @@ unsafe impl<#[may_dangle] T: ?Sized, A: Allocator> Drop for Box { #[cfg(not(no_global_oom_handling))] #[stable(feature = "rust1", since = "1.0.0")] impl Default for Box { - /// Creates a `Box`, with the `Default` value for T. + /// Creates a `Box`, with the `Default` value for `T`. #[inline] fn default() -> Self { let mut x: Box> = Box::new_uninit(); @@ -1695,6 +1695,7 @@ impl Default for Box { #[cfg(not(no_global_oom_handling))] #[stable(feature = "rust1", since = "1.0.0")] impl Default for Box<[T]> { + /// Creates an empty `[T]` inside a `Box`. #[inline] fn default() -> Self { let ptr: Unique<[T]> = Unique::<[T; 0]>::dangling(); @@ -1716,6 +1717,19 @@ impl Default for Box { } } +#[cfg(not(no_global_oom_handling))] +#[stable(feature = "pin_default_impls", since = "CURRENT_RUSTC_VERSION")] +impl Default for Pin> +where + T: ?Sized, + Box: Default, +{ + #[inline] + fn default() -> Self { + Box::into_pin(Box::::default()) + } +} + #[cfg(not(no_global_oom_handling))] #[stable(feature = "rust1", since = "1.0.0")] impl Clone for Box { diff --git a/library/alloc/src/rc.rs b/library/alloc/src/rc.rs index 5018ff4ad71..529b583cdd2 100644 --- a/library/alloc/src/rc.rs +++ b/library/alloc/src/rc.rs @@ -2357,7 +2357,7 @@ impl Default for Rc { /// assert_eq!(*x, 0); /// ``` #[inline] - fn default() -> Rc { + fn default() -> Self { unsafe { Self::from_inner( Box::leak(Box::write( @@ -2373,7 +2373,7 @@ impl Default for Rc { #[cfg(not(no_global_oom_handling))] #[stable(feature = "more_rc_default_impls", since = "1.80.0")] impl Default for Rc { - /// Creates an empty str inside an Rc + /// Creates an empty `str` inside an `Rc`. /// /// This may or may not share an allocation with other Rcs on the same thread. #[inline] @@ -2387,7 +2387,7 @@ impl Default for Rc { #[cfg(not(no_global_oom_handling))] #[stable(feature = "more_rc_default_impls", since = "1.80.0")] impl Default for Rc<[T]> { - /// Creates an empty `[T]` inside an Rc + /// Creates an empty `[T]` inside an `Rc`. /// /// This may or may not share an allocation with other Rcs on the same thread. #[inline] @@ -2397,6 +2397,19 @@ impl Default for Rc<[T]> { } } +#[cfg(not(no_global_oom_handling))] +#[stable(feature = "pin_default_impls", since = "CURRENT_RUSTC_VERSION")] +impl Default for Pin> +where + T: ?Sized, + Rc: Default, +{ + #[inline] + fn default() -> Self { + unsafe { Pin::new_unchecked(Rc::::default()) } + } +} + #[stable(feature = "rust1", since = "1.0.0")] trait RcEqIdent { fn eq(&self, other: &Rc) -> bool; diff --git a/library/alloc/src/sync.rs b/library/alloc/src/sync.rs index b8925f4544f..29caa7bc539 100644 --- a/library/alloc/src/sync.rs +++ b/library/alloc/src/sync.rs @@ -3654,6 +3654,19 @@ impl Default for Arc<[T]> { } } +#[cfg(not(no_global_oom_handling))] +#[stable(feature = "pin_default_impls", since = "CURRENT_RUSTC_VERSION")] +impl Default for Pin> +where + T: ?Sized, + Arc: Default, +{ + #[inline] + fn default() -> Self { + unsafe { Pin::new_unchecked(Arc::::default()) } + } +} + #[stable(feature = "rust1", since = "1.0.0")] impl Hash for Arc { fn hash(&self, state: &mut H) { -- cgit 1.4.1-3-g733a5 From 14c6e50d4740e13bf36b6c272485804ba9ad9117 Mon Sep 17 00:00:00 2001 From: Josh Simmons Date: Thu, 17 Jul 2025 09:11:44 +0200 Subject: Stabilize as_array_of_cells --- library/core/src/cell.rs | 4 ++-- tests/ui/rfcs/rfc-1789-as-cell/from-mut.rs | 2 -- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/library/core/src/cell.rs b/library/core/src/cell.rs index d6d1bf2effa..583a3340c26 100644 --- a/library/core/src/cell.rs +++ b/library/core/src/cell.rs @@ -698,14 +698,14 @@ impl Cell<[T; N]> { /// # Examples /// /// ``` - /// #![feature(as_array_of_cells)] /// use std::cell::Cell; /// /// let mut array: [i32; 3] = [1, 2, 3]; /// let cell_array: &Cell<[i32; 3]> = Cell::from_mut(&mut array); /// let array_cell: &[Cell; 3] = cell_array.as_array_of_cells(); /// ``` - #[unstable(feature = "as_array_of_cells", issue = "88248")] + #[stable(feature = "as_array_of_cells", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "as_array_of_cells", since = "CURRENT_RUSTC_VERSION")] pub const fn as_array_of_cells(&self) -> &[Cell; N] { // SAFETY: `Cell` has the same memory layout as `T`. unsafe { &*(self as *const Cell<[T; N]> as *const [Cell; N]) } diff --git a/tests/ui/rfcs/rfc-1789-as-cell/from-mut.rs b/tests/ui/rfcs/rfc-1789-as-cell/from-mut.rs index d3b441fbe88..700f875a4f6 100644 --- a/tests/ui/rfcs/rfc-1789-as-cell/from-mut.rs +++ b/tests/ui/rfcs/rfc-1789-as-cell/from-mut.rs @@ -1,7 +1,5 @@ //@ run-pass -#![feature(as_array_of_cells)] - use std::cell::Cell; fn main() { -- cgit 1.4.1-3-g733a5 From 6e002317cec5fa0c55a88307f839f2b5fb31922c Mon Sep 17 00:00:00 2001 From: Maksim Bondarenkov Date: Sat, 19 Jul 2025 14:19:54 +0300 Subject: opt-dist: rebuild rustc when doing static LLVM builds when building LLVM it's obvious that in case of shared build rustc doesn't need to be recompiled, but with static builds it would be better to compile rustc again to ensure we linked proper library. maybe I didn't understand the pipeline correctly, but it was strange for me to see that in Stage 5 LLVM is built while rustc is not --- src/tools/opt-dist/src/exec.rs | 6 ++++++ src/tools/opt-dist/src/main.rs | 10 ++++++++-- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/src/tools/opt-dist/src/exec.rs b/src/tools/opt-dist/src/exec.rs index 56eff2ca2a7..e5a037dc4e9 100644 --- a/src/tools/opt-dist/src/exec.rs +++ b/src/tools/opt-dist/src/exec.rs @@ -189,6 +189,12 @@ impl Bootstrap { self } + /// Rebuild rustc in case of statically linked LLVM + pub fn rustc_rebuild(mut self) -> Self { + self.cmd = self.cmd.arg("--keep-stage").arg("0"); + self + } + pub fn run(self, timer: &mut TimerSection) -> anyhow::Result<()> { self.cmd.run()?; let metrics = load_metrics(&self.metrics_path)?; diff --git a/src/tools/opt-dist/src/main.rs b/src/tools/opt-dist/src/main.rs index 7857f196626..8dc77883612 100644 --- a/src/tools/opt-dist/src/main.rs +++ b/src/tools/opt-dist/src/main.rs @@ -363,8 +363,14 @@ fn execute_pipeline( let mut dist = Bootstrap::dist(env, &dist_args) .llvm_pgo_optimize(llvm_pgo_profile.as_ref()) - .rustc_pgo_optimize(&rustc_pgo_profile) - .avoid_rustc_rebuild(); + .rustc_pgo_optimize(&rustc_pgo_profile); + + // if LLVM is not built we'll have PGO optimized rustc + dist = if env.supports_shared_llvm() || !env.build_llvm() { + dist.avoid_rustc_rebuild() + } else { + dist.rustc_rebuild() + }; for bolt_profile in bolt_profiles { dist = dist.with_bolt_profile(bolt_profile); -- cgit 1.4.1-3-g733a5 From 788fb08bdb375eb811d682b4f24566d0aad1baca Mon Sep 17 00:00:00 2001 From: León Orell Valerian Liehr Date: Fri, 18 Jul 2025 12:51:44 +0200 Subject: Reject relaxed bounds inside associated type bounds --- compiler/rustc_ast_lowering/src/lib.rs | 12 ++++++++---- .../rustc_hir_analysis/src/hir_ty_lowering/bounds.rs | 8 +++----- tests/ui/trait-bounds/more_maybe_bounds.rs | 8 ++++++-- tests/ui/trait-bounds/more_maybe_bounds.stderr | 6 +++--- tests/ui/unsized/relaxed-bounds-invalid-places.rs | 4 ++++ tests/ui/unsized/relaxed-bounds-invalid-places.stderr | 18 +++++++++++++----- 6 files changed, 37 insertions(+), 19 deletions(-) diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index 533ee9bff54..d3e8f62618f 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -296,6 +296,7 @@ enum RelaxedBoundPolicy<'a> { enum RelaxedBoundForbiddenReason { TraitObjectTy, SuperTrait, + AssocTyBounds, LateBoundVarsInScope, } @@ -1102,9 +1103,11 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { &*self.arena.alloc(self.ty(constraint.span, hir::TyKind::Err(guar))); hir::AssocItemConstraintKind::Equality { term: err_ty.into() } } else { - // FIXME(#135229): These should be forbidden! - let bounds = - self.lower_param_bounds(bounds, RelaxedBoundPolicy::Allowed, itctx); + let bounds = self.lower_param_bounds( + bounds, + RelaxedBoundPolicy::Forbidden(RelaxedBoundForbiddenReason::AssocTyBounds), + itctx, + ); hir::AssocItemConstraintKind::Bound { bounds } } } @@ -2117,7 +2120,8 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { diag.emit(); return; } - RelaxedBoundForbiddenReason::LateBoundVarsInScope => {} + RelaxedBoundForbiddenReason::AssocTyBounds + | RelaxedBoundForbiddenReason::LateBoundVarsInScope => {} }; } } diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs index d7a827c649d..bd0d0d7c106 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs @@ -199,12 +199,10 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { // However, this can easily get out of sync! Ideally, we would perform this step // where we are guaranteed to catch *all* bounds like in // `Self::lower_poly_trait_ref`. List of concrete issues: - // FIXME(more_maybe_bounds): We don't call this for e.g., trait object tys or - // supertrait bounds! + // FIXME(more_maybe_bounds): We don't call this for trait object tys, supertrait + // bounds or associated type bounds (ATB)! // FIXME(trait_alias, #143122): We don't call it for the RHS. Arguably however, - // AST lowering should reject them outright. - // FIXME(associated_type_bounds): We don't call this for them. However, AST - // lowering should reject them outright (#135229). + // AST lowering should reject them outright. let bounds = collect_relaxed_bounds(hir_bounds, self_ty_where_predicates); self.check_and_report_invalid_relaxed_bounds(bounds); } diff --git a/tests/ui/trait-bounds/more_maybe_bounds.rs b/tests/ui/trait-bounds/more_maybe_bounds.rs index 47348b0a0dd..d367dd5b299 100644 --- a/tests/ui/trait-bounds/more_maybe_bounds.rs +++ b/tests/ui/trait-bounds/more_maybe_bounds.rs @@ -1,7 +1,7 @@ // FIXME(more_maybe_bounds): Even under `more_maybe_bounds` / `-Zexperimental-default-bounds`, // trying to relax non-default bounds should still be an error in all contexts! As you can see -// there are places like supertrait bounds and trait object types where we currently don't perform -// this check. +// there are places like supertrait bounds, trait object types or associated type bounds (ATB) +// where we currently don't perform this check. #![feature(auto_traits, more_maybe_bounds, negative_impls)] trait Trait1 {} @@ -13,11 +13,15 @@ trait Trait4 where Self: Trait1 {} // FIXME: `?Trait2` should be rejected, `Trait2` isn't marked `#[lang = "default_traitN"]`. fn foo(_: Box<(dyn Trait3 + ?Trait2)>) {} + fn bar(_: &T) {} //~^ ERROR bound modifier `?` can only be applied to default traits like `Sized` //~| ERROR bound modifier `?` can only be applied to default traits like `Sized` //~| ERROR bound modifier `?` can only be applied to default traits like `Sized` +// FIXME: `?Trait1` should be rejected, `Trait1` isn't marked `#[lang = "default_traitN"]`. +fn baz() where T: Iterator {} + struct S; impl !Trait2 for S {} impl Trait1 for S {} diff --git a/tests/ui/trait-bounds/more_maybe_bounds.stderr b/tests/ui/trait-bounds/more_maybe_bounds.stderr index 09c9fc31165..8dd83fc7728 100644 --- a/tests/ui/trait-bounds/more_maybe_bounds.stderr +++ b/tests/ui/trait-bounds/more_maybe_bounds.stderr @@ -1,17 +1,17 @@ error: bound modifier `?` can only be applied to default traits like `Sized` - --> $DIR/more_maybe_bounds.rs:16:20 + --> $DIR/more_maybe_bounds.rs:17:20 | LL | fn bar(_: &T) {} | ^^^^^^^ error: bound modifier `?` can only be applied to default traits like `Sized` - --> $DIR/more_maybe_bounds.rs:16:30 + --> $DIR/more_maybe_bounds.rs:17:30 | LL | fn bar(_: &T) {} | ^^^^^^^ error: bound modifier `?` can only be applied to default traits like `Sized` - --> $DIR/more_maybe_bounds.rs:16:40 + --> $DIR/more_maybe_bounds.rs:17:40 | LL | fn bar(_: &T) {} | ^^^^^^^ diff --git a/tests/ui/unsized/relaxed-bounds-invalid-places.rs b/tests/ui/unsized/relaxed-bounds-invalid-places.rs index b8eda1e7786..4c1f242a01c 100644 --- a/tests/ui/unsized/relaxed-bounds-invalid-places.rs +++ b/tests/ui/unsized/relaxed-bounds-invalid-places.rs @@ -22,6 +22,10 @@ impl S1 { fn f() where T: ?Sized {} //~ ERROR this relaxed bound is not permitted here } +// Test associated type bounds (ATB). +// issue: +struct S6(T) where T: Iterator; //~ ERROR this relaxed bound is not permitted here + trait Tr: ?Sized {} //~ ERROR relaxed bounds are not permitted in supertrait bounds // Test that relaxed `Sized` bounds are rejected in trait object types: diff --git a/tests/ui/unsized/relaxed-bounds-invalid-places.stderr b/tests/ui/unsized/relaxed-bounds-invalid-places.stderr index 30285d62693..d3f0535e2f0 100644 --- a/tests/ui/unsized/relaxed-bounds-invalid-places.stderr +++ b/tests/ui/unsized/relaxed-bounds-invalid-places.stderr @@ -38,8 +38,16 @@ LL | fn f() where T: ?Sized {} | = note: in this context, relaxed bounds are only allowed on type parameters defined by the closest item +error: this relaxed bound is not permitted here + --> $DIR/relaxed-bounds-invalid-places.rs:27:41 + | +LL | struct S6(T) where T: Iterator; + | ^^^^^^ + | + = note: in this context, relaxed bounds are only allowed on type parameters defined by the closest item + error: relaxed bounds are not permitted in supertrait bounds - --> $DIR/relaxed-bounds-invalid-places.rs:25:11 + --> $DIR/relaxed-bounds-invalid-places.rs:29:11 | LL | trait Tr: ?Sized {} | ^^^^^^ @@ -47,19 +55,19 @@ LL | trait Tr: ?Sized {} = note: traits are `?Sized` by default error: relaxed bounds are not permitted in trait object types - --> $DIR/relaxed-bounds-invalid-places.rs:29:20 + --> $DIR/relaxed-bounds-invalid-places.rs:33:20 | LL | type O1 = dyn Tr + ?Sized; | ^^^^^^ error: relaxed bounds are not permitted in trait object types - --> $DIR/relaxed-bounds-invalid-places.rs:30:15 + --> $DIR/relaxed-bounds-invalid-places.rs:34:15 | LL | type O2 = dyn ?Sized + ?Sized + Tr; | ^^^^^^ error: relaxed bounds are not permitted in trait object types - --> $DIR/relaxed-bounds-invalid-places.rs:30:24 + --> $DIR/relaxed-bounds-invalid-places.rs:34:24 | LL | type O2 = dyn ?Sized + ?Sized + Tr; | ^^^^^^ @@ -76,5 +84,5 @@ error: bound modifier `?` can only be applied to `Sized` LL | struct S5(*const T) where T: ?Trait<'static> + ?Sized; | ^^^^^^^^^^^^^^^ -error: aborting due to 11 previous errors +error: aborting due to 12 previous errors -- cgit 1.4.1-3-g733a5 From 30033b45bb2fcbc0f267acaf9fab22411422e166 Mon Sep 17 00:00:00 2001 From: gewitternacht <60887951+gewitternacht@users.noreply.github.com> Date: Tue, 22 Jul 2025 23:16:49 +0200 Subject: document assumptions about `Clone` and `Eq` traits --- library/core/src/clone.rs | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/library/core/src/clone.rs b/library/core/src/clone.rs index a34d1b4a064..7afdf50ce51 100644 --- a/library/core/src/clone.rs +++ b/library/core/src/clone.rs @@ -139,6 +139,30 @@ mod uninit; /// // Note: With the manual implementations the above line will compile. /// ``` /// +/// ## `Clone` and `PartialEq`/`Eq` +/// `Clone` is intended for the duplication of objects. Consequently, when implementing +/// both `Clone` and [`PartialEq`], the following property is expected to hold: +/// ```text +/// x == x -> x.clone() == x +/// ``` +/// In other words, if an object compares equal to itself, +/// its clone must also compare equal to the original. +/// +/// For types that also implement [`Eq`] – for which `x == x` always holds – +/// this implies that `x.clone() == x` must always be true. +/// Standard library collections such as +/// [`HashMap`], [`HashSet`], [`BTreeMap`], [`BTreeSet`] and [`BinaryHeap`] +/// rely on their keys respecting this property for correct behavior. +/// +/// This property is automatically satisfied when deriving both `Clone` and [`PartialEq`] +/// using `#[derive(Clone, PartialEq)]` or when additionally deriving [`Eq`] +/// using `#[derive(Clone, PartialEq, Eq)]`. +/// +/// Violating this property is a logic error. The behavior resulting from a logic error is not +/// specified, but users of the trait must ensure that such logic errors do *not* result in +/// undefined behavior. This means that `unsafe` code **must not** rely on the correctness of these +/// methods. +/// /// ## Additional implementors /// /// In addition to the [implementors listed below][impls], -- cgit 1.4.1-3-g733a5 From cdbcd72f341c949e4fa11f58d76a094bd435d18f Mon Sep 17 00:00:00 2001 From: gewitternacht <60887951+gewitternacht@users.noreply.github.com> Date: Wed, 23 Jul 2025 00:41:48 +0200 Subject: remove trailing whitespace --- library/core/src/clone.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/core/src/clone.rs b/library/core/src/clone.rs index 7afdf50ce51..64916f49f3a 100644 --- a/library/core/src/clone.rs +++ b/library/core/src/clone.rs @@ -155,7 +155,7 @@ mod uninit; /// rely on their keys respecting this property for correct behavior. /// /// This property is automatically satisfied when deriving both `Clone` and [`PartialEq`] -/// using `#[derive(Clone, PartialEq)]` or when additionally deriving [`Eq`] +/// using `#[derive(Clone, PartialEq)]` or when additionally deriving [`Eq`] /// using `#[derive(Clone, PartialEq, Eq)]`. /// /// Violating this property is a logic error. The behavior resulting from a logic error is not -- cgit 1.4.1-3-g733a5 From 623317eb5e2bf73b53a5cd52d693ac032cf5aa32 Mon Sep 17 00:00:00 2001 From: gewitternacht <60887951+gewitternacht@users.noreply.github.com> Date: Wed, 23 Jul 2025 01:44:18 +0200 Subject: add links to collections --- library/core/src/clone.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/library/core/src/clone.rs b/library/core/src/clone.rs index 64916f49f3a..c4778edf0fc 100644 --- a/library/core/src/clone.rs +++ b/library/core/src/clone.rs @@ -176,6 +176,11 @@ mod uninit; /// (even if the referent doesn't), /// while variables captured by mutable reference never implement `Clone`. /// +/// [`HashMap`]: ../../std/collections/struct.HashMap.html +/// [`HashSet`]: ../../std/collections/struct.HashSet.html +/// [`BTreeMap`]: ../../std/collections/struct.BTreeMap.html +/// [`BTreeSet`]: ../../std/collections/struct.BTreeSet.html +/// [`BinaryHeap`]: ../../std/collections/struct.BinaryHeap.html /// [impls]: #implementors #[stable(feature = "rust1", since = "1.0.0")] #[lang = "clone"] -- cgit 1.4.1-3-g733a5 From 84f834b87f1f0b2eb56e94652eb3a4b440402a93 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 19 Jul 2025 23:23:40 +0200 Subject: atomicrmw on pointers: move integer-pointer cast hacks into backend --- src/builder.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/builder.rs b/src/builder.rs index a4ec4bf8dea..032f5d0a622 100644 --- a/src/builder.rs +++ b/src/builder.rs @@ -1656,6 +1656,7 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> { dst: RValue<'gcc>, src: RValue<'gcc>, order: AtomicOrdering, + ret_ptr: bool, ) -> RValue<'gcc> { let size = get_maybe_pointer_size(src); let name = match op { @@ -1683,6 +1684,9 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> { let atomic_function = self.context.get_builtin_function(name); let order = self.context.new_rvalue_from_int(self.i32_type, order.to_gcc()); + // FIXME: If `ret_ptr` is true and `src` is an integer, we should really tell GCC + // that this is a pointer operation that needs to preserve provenance -- but like LLVM, + // GCC does not currently seems to support that. let void_ptr_type = self.context.new_type::<*mut ()>(); let volatile_void_ptr_type = void_ptr_type.make_volatile(); let dst = self.context.new_cast(self.location, dst, volatile_void_ptr_type); @@ -1690,7 +1694,8 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> { let new_src_type = atomic_function.get_param(1).to_rvalue().get_type(); let src = self.context.new_bitcast(self.location, src, new_src_type); let res = self.context.new_call(self.location, atomic_function, &[dst, src, order]); - self.context.new_cast(self.location, res, src.get_type()) + let res_type = if ret_ptr { void_ptr_type } else { src.get_type() }; + self.context.new_cast(self.location, res, res_type) } fn atomic_fence(&mut self, order: AtomicOrdering, scope: SynchronizationScope) { -- cgit 1.4.1-3-g733a5 From a383fb0c738967ea8c6af200d8f4a37c751427c1 Mon Sep 17 00:00:00 2001 From: WANG Rui Date: Thu, 9 Jan 2025 20:35:49 +0800 Subject: asm: Stabilize loongarch32 --- compiler/rustc_ast_lowering/src/asm.rs | 1 + .../src/language-features/asm-experimental-arch.md | 5 -- tests/assembly-llvm/asm/loongarch-type.rs | 51 +++++++++++------- .../loongarch/bad-reg.loongarch32_ilp32d.stderr | 38 +++++++++++++ .../loongarch/bad-reg.loongarch32_ilp32s.stderr | 62 ++++++++++++++++++++++ .../asm/loongarch/bad-reg.loongarch64_lp64d.stderr | 12 ++--- .../asm/loongarch/bad-reg.loongarch64_lp64s.stderr | 20 +++---- tests/ui/asm/loongarch/bad-reg.rs | 15 ++++-- 8 files changed, 158 insertions(+), 46 deletions(-) create mode 100644 tests/ui/asm/loongarch/bad-reg.loongarch32_ilp32d.stderr create mode 100644 tests/ui/asm/loongarch/bad-reg.loongarch32_ilp32s.stderr diff --git a/compiler/rustc_ast_lowering/src/asm.rs b/compiler/rustc_ast_lowering/src/asm.rs index af279e07acc..d44faad017e 100644 --- a/compiler/rustc_ast_lowering/src/asm.rs +++ b/compiler/rustc_ast_lowering/src/asm.rs @@ -48,6 +48,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { | asm::InlineAsmArch::Arm64EC | asm::InlineAsmArch::RiscV32 | asm::InlineAsmArch::RiscV64 + | asm::InlineAsmArch::LoongArch32 | asm::InlineAsmArch::LoongArch64 | asm::InlineAsmArch::S390x ); diff --git a/src/doc/unstable-book/src/language-features/asm-experimental-arch.md b/src/doc/unstable-book/src/language-features/asm-experimental-arch.md index 121f9493435..d9566c9f55c 100644 --- a/src/doc/unstable-book/src/language-features/asm-experimental-arch.md +++ b/src/doc/unstable-book/src/language-features/asm-experimental-arch.md @@ -19,7 +19,6 @@ This feature tracks `asm!` and `global_asm!` support for the following architect - M68k - CSKY - SPARC -- LoongArch32 ## Register classes @@ -54,8 +53,6 @@ This feature tracks `asm!` and `global_asm!` support for the following architect | CSKY | `freg` | `f[0-31]` | `f` | | SPARC | `reg` | `r[2-29]` | `r` | | SPARC | `yreg` | `y` | Only clobbers | -| LoongArch32 | `reg` | `$r1`, `$r[4-20]`, `$r[23,30]` | `r` | -| LoongArch32 | `freg` | `$f[0-31]` | `f` | > **Notes**: > - NVPTX doesn't have a fixed register set, so named registers are not supported. @@ -94,8 +91,6 @@ This feature tracks `asm!` and `global_asm!` support for the following architect | CSKY | `freg` | None | `f32`, | | SPARC | `reg` | None | `i8`, `i16`, `i32`, `i64` (SPARC64 only) | | SPARC | `yreg` | N/A | Only clobbers | -| LoongArch32 | `reg` | None | `i8`, `i16`, `i32`, `f32` | -| LoongArch32 | `freg` | None | `f32`, `f64` | ## Register aliases diff --git a/tests/assembly-llvm/asm/loongarch-type.rs b/tests/assembly-llvm/asm/loongarch-type.rs index c782be19f1d..95d811a6bd0 100644 --- a/tests/assembly-llvm/asm/loongarch-type.rs +++ b/tests/assembly-llvm/asm/loongarch-type.rs @@ -1,8 +1,15 @@ //@ add-core-stubs +//@ revisions: loongarch32 loongarch64 + //@ assembly-output: emit-asm -//@ compile-flags: --target loongarch64-unknown-linux-gnu + +//@[loongarch32] compile-flags: --target loongarch32-unknown-none +//@[loongarch32] needs-llvm-components: loongarch + +//@[loongarch64] compile-flags: --target loongarch64-unknown-none +//@[loongarch64] needs-llvm-components: loongarch + //@ compile-flags: -Zmerge-functions=disabled -//@ needs-llvm-components: loongarch #![feature(no_core, f16)] #![crate_type = "rlib"] @@ -22,7 +29,7 @@ extern "C" { // CHECK-LABEL: sym_fn: // CHECK: #APP // CHECK: pcalau12i $t0, %got_pc_hi20(extern_func) -// CHECK: ld.d $t0, $t0, %got_pc_lo12(extern_func) +// CHECK: ld.{{[wd]}} $t0, $t0, %got_pc_lo12(extern_func) // CHECK: #NO_APP #[no_mangle] pub unsafe fn sym_fn() { @@ -32,7 +39,7 @@ pub unsafe fn sym_fn() { // CHECK-LABEL: sym_static: // CHECK: #APP // CHECK: pcalau12i $t0, %got_pc_hi20(extern_static) -// CHECK: ld.d $t0, $t0, %got_pc_lo12(extern_static) +// CHECK: ld.{{[wd]}} $t0, $t0, %got_pc_lo12(extern_static) // CHECK: #NO_APP #[no_mangle] pub unsafe fn sym_static() { @@ -87,16 +94,18 @@ check!(reg_i32, i32, reg, "move"); // CHECK: #NO_APP check!(reg_f32, f32, reg, "move"); -// CHECK-LABEL: reg_i64: -// CHECK: #APP -// CHECK: move ${{[a-z0-9]+}}, ${{[a-z0-9]+}} -// CHECK: #NO_APP +// loongarch64-LABEL: reg_i64: +// loongarch64: #APP +// loongarch64: move ${{[a-z0-9]+}}, ${{[a-z0-9]+}} +// loongarch64: #NO_APP +#[cfg(loongarch64)] check!(reg_i64, i64, reg, "move"); -// CHECK-LABEL: reg_f64: -// CHECK: #APP -// CHECK: move ${{[a-z0-9]+}}, ${{[a-z0-9]+}} -// CHECK: #NO_APP +// loongarch64-LABEL: reg_f64: +// loongarch64: #APP +// loongarch64: move ${{[a-z0-9]+}}, ${{[a-z0-9]+}} +// loongarch64: #NO_APP +#[cfg(loongarch64)] check!(reg_f64, f64, reg, "move"); // CHECK-LABEL: reg_ptr: @@ -153,16 +162,18 @@ check_reg!(r4_i32, i32, "$r4", "move"); // CHECK: #NO_APP check_reg!(r4_f32, f32, "$r4", "move"); -// CHECK-LABEL: r4_i64: -// CHECK: #APP -// CHECK: move $a0, $a0 -// CHECK: #NO_APP +// loongarch64-LABEL: r4_i64: +// loongarch64: #APP +// loongarch64: move $a0, $a0 +// loongarch64: #NO_APP +#[cfg(loongarch64)] check_reg!(r4_i64, i64, "$r4", "move"); -// CHECK-LABEL: r4_f64: -// CHECK: #APP -// CHECK: move $a0, $a0 -// CHECK: #NO_APP +// loongarch64-LABEL: r4_f64: +// loongarch64: #APP +// loongarch64: move $a0, $a0 +// loongarch64: #NO_APP +#[cfg(loongarch64)] check_reg!(r4_f64, f64, "$r4", "move"); // CHECK-LABEL: r4_ptr: diff --git a/tests/ui/asm/loongarch/bad-reg.loongarch32_ilp32d.stderr b/tests/ui/asm/loongarch/bad-reg.loongarch32_ilp32d.stderr new file mode 100644 index 00000000000..8742d4bd82c --- /dev/null +++ b/tests/ui/asm/loongarch/bad-reg.loongarch32_ilp32d.stderr @@ -0,0 +1,38 @@ +error: invalid register `$r0`: constant zero cannot be used as an operand for inline asm + --> $DIR/bad-reg.rs:27:18 + | +LL | asm!("", out("$r0") _); + | ^^^^^^^^^^^^ + +error: invalid register `$tp`: reserved for TLS + --> $DIR/bad-reg.rs:29:18 + | +LL | asm!("", out("$tp") _); + | ^^^^^^^^^^^^ + +error: invalid register `$sp`: the stack pointer cannot be used as an operand for inline asm + --> $DIR/bad-reg.rs:31:18 + | +LL | asm!("", out("$sp") _); + | ^^^^^^^^^^^^ + +error: invalid register `$r21`: reserved by the ABI + --> $DIR/bad-reg.rs:33:18 + | +LL | asm!("", out("$r21") _); + | ^^^^^^^^^^^^^ + +error: invalid register `$fp`: the frame pointer cannot be used as an operand for inline asm + --> $DIR/bad-reg.rs:35:18 + | +LL | asm!("", out("$fp") _); + | ^^^^^^^^^^^^ + +error: invalid register `$r31`: $r31 is used internally by LLVM and cannot be used as an operand for inline asm + --> $DIR/bad-reg.rs:37:18 + | +LL | asm!("", out("$r31") _); + | ^^^^^^^^^^^^^ + +error: aborting due to 6 previous errors + diff --git a/tests/ui/asm/loongarch/bad-reg.loongarch32_ilp32s.stderr b/tests/ui/asm/loongarch/bad-reg.loongarch32_ilp32s.stderr new file mode 100644 index 00000000000..e6cb6e40c70 --- /dev/null +++ b/tests/ui/asm/loongarch/bad-reg.loongarch32_ilp32s.stderr @@ -0,0 +1,62 @@ +error: invalid register `$r0`: constant zero cannot be used as an operand for inline asm + --> $DIR/bad-reg.rs:27:18 + | +LL | asm!("", out("$r0") _); + | ^^^^^^^^^^^^ + +error: invalid register `$tp`: reserved for TLS + --> $DIR/bad-reg.rs:29:18 + | +LL | asm!("", out("$tp") _); + | ^^^^^^^^^^^^ + +error: invalid register `$sp`: the stack pointer cannot be used as an operand for inline asm + --> $DIR/bad-reg.rs:31:18 + | +LL | asm!("", out("$sp") _); + | ^^^^^^^^^^^^ + +error: invalid register `$r21`: reserved by the ABI + --> $DIR/bad-reg.rs:33:18 + | +LL | asm!("", out("$r21") _); + | ^^^^^^^^^^^^^ + +error: invalid register `$fp`: the frame pointer cannot be used as an operand for inline asm + --> $DIR/bad-reg.rs:35:18 + | +LL | asm!("", out("$fp") _); + | ^^^^^^^^^^^^ + +error: invalid register `$r31`: $r31 is used internally by LLVM and cannot be used as an operand for inline asm + --> $DIR/bad-reg.rs:37:18 + | +LL | asm!("", out("$r31") _); + | ^^^^^^^^^^^^^ + +error: register class `freg` requires at least one of the following target features: d, f + --> $DIR/bad-reg.rs:41:26 + | +LL | asm!("/* {} */", in(freg) f); + | ^^^^^^^^^^ + +error: register class `freg` requires at least one of the following target features: d, f + --> $DIR/bad-reg.rs:43:26 + | +LL | asm!("/* {} */", out(freg) _); + | ^^^^^^^^^^^ + +error: register class `freg` requires at least one of the following target features: d, f + --> $DIR/bad-reg.rs:45:26 + | +LL | asm!("/* {} */", in(freg) d); + | ^^^^^^^^^^ + +error: register class `freg` requires at least one of the following target features: d, f + --> $DIR/bad-reg.rs:47:26 + | +LL | asm!("/* {} */", out(freg) d); + | ^^^^^^^^^^^ + +error: aborting due to 10 previous errors + diff --git a/tests/ui/asm/loongarch/bad-reg.loongarch64_lp64d.stderr b/tests/ui/asm/loongarch/bad-reg.loongarch64_lp64d.stderr index 0e544119650..8742d4bd82c 100644 --- a/tests/ui/asm/loongarch/bad-reg.loongarch64_lp64d.stderr +++ b/tests/ui/asm/loongarch/bad-reg.loongarch64_lp64d.stderr @@ -1,35 +1,35 @@ error: invalid register `$r0`: constant zero cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:22:18 + --> $DIR/bad-reg.rs:27:18 | LL | asm!("", out("$r0") _); | ^^^^^^^^^^^^ error: invalid register `$tp`: reserved for TLS - --> $DIR/bad-reg.rs:24:18 + --> $DIR/bad-reg.rs:29:18 | LL | asm!("", out("$tp") _); | ^^^^^^^^^^^^ error: invalid register `$sp`: the stack pointer cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:26:18 + --> $DIR/bad-reg.rs:31:18 | LL | asm!("", out("$sp") _); | ^^^^^^^^^^^^ error: invalid register `$r21`: reserved by the ABI - --> $DIR/bad-reg.rs:28:18 + --> $DIR/bad-reg.rs:33:18 | LL | asm!("", out("$r21") _); | ^^^^^^^^^^^^^ error: invalid register `$fp`: the frame pointer cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:30:18 + --> $DIR/bad-reg.rs:35:18 | LL | asm!("", out("$fp") _); | ^^^^^^^^^^^^ error: invalid register `$r31`: $r31 is used internally by LLVM and cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:32:18 + --> $DIR/bad-reg.rs:37:18 | LL | asm!("", out("$r31") _); | ^^^^^^^^^^^^^ diff --git a/tests/ui/asm/loongarch/bad-reg.loongarch64_lp64s.stderr b/tests/ui/asm/loongarch/bad-reg.loongarch64_lp64s.stderr index 6d0410dc6a1..e6cb6e40c70 100644 --- a/tests/ui/asm/loongarch/bad-reg.loongarch64_lp64s.stderr +++ b/tests/ui/asm/loongarch/bad-reg.loongarch64_lp64s.stderr @@ -1,59 +1,59 @@ error: invalid register `$r0`: constant zero cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:22:18 + --> $DIR/bad-reg.rs:27:18 | LL | asm!("", out("$r0") _); | ^^^^^^^^^^^^ error: invalid register `$tp`: reserved for TLS - --> $DIR/bad-reg.rs:24:18 + --> $DIR/bad-reg.rs:29:18 | LL | asm!("", out("$tp") _); | ^^^^^^^^^^^^ error: invalid register `$sp`: the stack pointer cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:26:18 + --> $DIR/bad-reg.rs:31:18 | LL | asm!("", out("$sp") _); | ^^^^^^^^^^^^ error: invalid register `$r21`: reserved by the ABI - --> $DIR/bad-reg.rs:28:18 + --> $DIR/bad-reg.rs:33:18 | LL | asm!("", out("$r21") _); | ^^^^^^^^^^^^^ error: invalid register `$fp`: the frame pointer cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:30:18 + --> $DIR/bad-reg.rs:35:18 | LL | asm!("", out("$fp") _); | ^^^^^^^^^^^^ error: invalid register `$r31`: $r31 is used internally by LLVM and cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:32:18 + --> $DIR/bad-reg.rs:37:18 | LL | asm!("", out("$r31") _); | ^^^^^^^^^^^^^ error: register class `freg` requires at least one of the following target features: d, f - --> $DIR/bad-reg.rs:36:26 + --> $DIR/bad-reg.rs:41:26 | LL | asm!("/* {} */", in(freg) f); | ^^^^^^^^^^ error: register class `freg` requires at least one of the following target features: d, f - --> $DIR/bad-reg.rs:38:26 + --> $DIR/bad-reg.rs:43:26 | LL | asm!("/* {} */", out(freg) _); | ^^^^^^^^^^^ error: register class `freg` requires at least one of the following target features: d, f - --> $DIR/bad-reg.rs:40:26 + --> $DIR/bad-reg.rs:45:26 | LL | asm!("/* {} */", in(freg) d); | ^^^^^^^^^^ error: register class `freg` requires at least one of the following target features: d, f - --> $DIR/bad-reg.rs:42:26 + --> $DIR/bad-reg.rs:47:26 | LL | asm!("/* {} */", out(freg) d); | ^^^^^^^^^^^ diff --git a/tests/ui/asm/loongarch/bad-reg.rs b/tests/ui/asm/loongarch/bad-reg.rs index 685b460bc92..0d3eba07f14 100644 --- a/tests/ui/asm/loongarch/bad-reg.rs +++ b/tests/ui/asm/loongarch/bad-reg.rs @@ -1,6 +1,11 @@ //@ add-core-stubs //@ needs-asm-support -//@ revisions: loongarch64_lp64d loongarch64_lp64s +//@ revisions: loongarch32_ilp32d loongarch32_ilp32s loongarch64_lp64d loongarch64_lp64s +//@ min-llvm-version: 20 +//@[loongarch32_ilp32d] compile-flags: --target loongarch32-unknown-none +//@[loongarch32_ilp32d] needs-llvm-components: loongarch +//@[loongarch32_ilp32s] compile-flags: --target loongarch32-unknown-none-softfloat +//@[loongarch32_ilp32s] needs-llvm-components: loongarch //@[loongarch64_lp64d] compile-flags: --target loongarch64-unknown-linux-gnu //@[loongarch64_lp64d] needs-llvm-components: loongarch //@[loongarch64_lp64s] compile-flags: --target loongarch64-unknown-none-softfloat @@ -34,12 +39,12 @@ fn f() { asm!("", out("$f0") _); // ok asm!("/* {} */", in(freg) f); - //[loongarch64_lp64s]~^ ERROR register class `freg` requires at least one of the following target features: d, f + //[loongarch32_ilp32s,loongarch64_lp64s]~^ ERROR register class `freg` requires at least one of the following target features: d, f asm!("/* {} */", out(freg) _); - //[loongarch64_lp64s]~^ ERROR register class `freg` requires at least one of the following target features: d, f + //[loongarch32_ilp32s,loongarch64_lp64s]~^ ERROR register class `freg` requires at least one of the following target features: d, f asm!("/* {} */", in(freg) d); - //[loongarch64_lp64s]~^ ERROR register class `freg` requires at least one of the following target features: d, f + //[loongarch32_ilp32s,loongarch64_lp64s]~^ ERROR register class `freg` requires at least one of the following target features: d, f asm!("/* {} */", out(freg) d); - //[loongarch64_lp64s]~^ ERROR register class `freg` requires at least one of the following target features: d, f + //[loongarch32_ilp32s,loongarch64_lp64s]~^ ERROR register class `freg` requires at least one of the following target features: d, f } } -- cgit 1.4.1-3-g733a5 From 90bb5cacb5c1a5fe20ba821d28e7eb7a21e35d09 Mon Sep 17 00:00:00 2001 From: Kivooeo Date: Thu, 24 Jul 2025 18:29:09 +0500 Subject: moved 34 tests to organized locations --- tests/ui/closures/unused-closure-warning-16256.rs | 6 ++ .../array-slice-coercion-mismatch-15783.rs | 16 ++++ .../derive-partial-ord-discriminant-64bit.rs | 39 ++++++++++ .../ui/derives/derive-partial-ord-discriminant.rs | 42 +++++++++++ tests/ui/drop/early-return-drop-order.rs | 88 ++++++++++++++++++++++ tests/ui/drop/enum-drop-impl-15063.rs | 12 +++ tests/ui/drop/generic-drop-trait-bound-15858.rs | 35 +++++++++ tests/ui/drop/struct-field-drop-order.rs | 67 ++++++++++++++++ tests/ui/drop/vec-replace-skip-while-drop.rs | 32 ++++++++ tests/ui/extern/empty-struct-extern-fn-16441.rs | 11 +++ tests/ui/fn/fn-traits-call-once-signature.rs | 27 +++++++ tests/ui/imports/enum-variant-import-path-15774.rs | 25 ++++++ .../inference/return-block-type-inference-15965.rs | 7 ++ tests/ui/issues/issue-15034.rs | 22 ------ tests/ui/issues/issue-15043.rs | 13 ---- tests/ui/issues/issue-15063.rs | 12 --- tests/ui/issues/issue-15094.rs | 27 ------- tests/ui/issues/issue-15104.rs | 13 ---- tests/ui/issues/issue-15129-rpass.rs | 25 ------ tests/ui/issues/issue-15167.rs | 26 ------- tests/ui/issues/issue-15189.rs | 10 --- tests/ui/issues/issue-15207.rs | 6 -- tests/ui/issues/issue-15260.rs | 25 ------ tests/ui/issues/issue-15381.rs | 10 --- tests/ui/issues/issue-15444.rs | 22 ------ tests/ui/issues/issue-15523-big.rs | 39 ---------- tests/ui/issues/issue-15523.rs | 42 ----------- tests/ui/issues/issue-15571.rs | 57 -------------- tests/ui/issues/issue-15673.rs | 9 --- tests/ui/issues/issue-15734.rs | 59 --------------- tests/ui/issues/issue-15735.rs | 17 ----- tests/ui/issues/issue-15756.rs | 14 ---- tests/ui/issues/issue-15763.rs | 88 ---------------------- tests/ui/issues/issue-15774.rs | 25 ------ tests/ui/issues/issue-15783.rs | 16 ---- tests/ui/issues/issue-15793.rs | 27 ------- tests/ui/issues/issue-15858.rs | 35 --------- tests/ui/issues/issue-15896.rs | 15 ---- tests/ui/issues/issue-15965.rs | 7 -- tests/ui/issues/issue-16048.rs | 30 -------- tests/ui/issues/issue-16149.rs | 11 --- tests/ui/issues/issue-16151.rs | 32 -------- tests/ui/issues/issue-16256.rs | 6 -- tests/ui/issues/issue-16401.rs | 15 ---- tests/ui/issues/issue-16441.rs | 11 --- tests/ui/issues/issue-16452.rs | 9 --- tests/ui/issues/issue-16492.rs | 67 ---------------- tests/ui/iterators/chunk-iterator-mut-pattern.rs | 14 ++++ tests/ui/iterators/iterator-sum-array-15673.rs | 9 +++ .../lifetimes/explicit-lifetime-required-15034.rs | 22 ++++++ .../lifetimes/struct-lifetime-inference-15735.rs | 17 +++++ tests/ui/macros/macro-hygiene-scope-15167.rs | 26 +++++++ tests/ui/macros/macro-variable-capture-15189.rs | 10 +++ tests/ui/match/nested-enum-match-optimization.rs | 27 +++++++ tests/ui/moves/match-move-same-binding-15571.rs | 57 ++++++++++++++ tests/ui/never/never-type-method-call-15207.rs | 6 ++ tests/ui/pattern/enum-struct-pattern-mismatch.rs | 15 ++++ .../ui/pattern/refutable-pattern-for-loop-15381.rs | 10 +++ tests/ui/pattern/slice-pattern-recursion-15104.rs | 13 ++++ tests/ui/pattern/static-binding-shadow-16149.rs | 11 +++ tests/ui/pattern/struct-field-duplicate-binding.rs | 25 ++++++ tests/ui/pattern/tuple-enum-match-15129.rs | 25 ++++++ .../pattern/unit-type-struct-pattern-mismatch.rs | 15 ++++ tests/ui/statics/conditional-static-declaration.rs | 9 +++ tests/ui/structs/static-struct-init-15043.rs | 13 ++++ tests/ui/traits/fn-type-trait-impl-15444.rs | 22 ++++++ tests/ui/traits/index-trait-multiple-impls.rs | 59 +++++++++++++++ tests/ui/traits/lifetime-mismatch-trait-impl.rs | 30 ++++++++ 68 files changed, 842 insertions(+), 842 deletions(-) create mode 100644 tests/ui/closures/unused-closure-warning-16256.rs create mode 100644 tests/ui/coercion/array-slice-coercion-mismatch-15783.rs create mode 100644 tests/ui/derives/derive-partial-ord-discriminant-64bit.rs create mode 100644 tests/ui/derives/derive-partial-ord-discriminant.rs create mode 100644 tests/ui/drop/early-return-drop-order.rs create mode 100644 tests/ui/drop/enum-drop-impl-15063.rs create mode 100644 tests/ui/drop/generic-drop-trait-bound-15858.rs create mode 100644 tests/ui/drop/struct-field-drop-order.rs create mode 100644 tests/ui/drop/vec-replace-skip-while-drop.rs create mode 100644 tests/ui/extern/empty-struct-extern-fn-16441.rs create mode 100644 tests/ui/fn/fn-traits-call-once-signature.rs create mode 100644 tests/ui/imports/enum-variant-import-path-15774.rs create mode 100644 tests/ui/inference/return-block-type-inference-15965.rs delete mode 100644 tests/ui/issues/issue-15034.rs delete mode 100644 tests/ui/issues/issue-15043.rs delete mode 100644 tests/ui/issues/issue-15063.rs delete mode 100644 tests/ui/issues/issue-15094.rs delete mode 100644 tests/ui/issues/issue-15104.rs delete mode 100644 tests/ui/issues/issue-15129-rpass.rs delete mode 100644 tests/ui/issues/issue-15167.rs delete mode 100644 tests/ui/issues/issue-15189.rs delete mode 100644 tests/ui/issues/issue-15207.rs delete mode 100644 tests/ui/issues/issue-15260.rs delete mode 100644 tests/ui/issues/issue-15381.rs delete mode 100644 tests/ui/issues/issue-15444.rs delete mode 100644 tests/ui/issues/issue-15523-big.rs delete mode 100644 tests/ui/issues/issue-15523.rs delete mode 100644 tests/ui/issues/issue-15571.rs delete mode 100644 tests/ui/issues/issue-15673.rs delete mode 100644 tests/ui/issues/issue-15734.rs delete mode 100644 tests/ui/issues/issue-15735.rs delete mode 100644 tests/ui/issues/issue-15756.rs delete mode 100644 tests/ui/issues/issue-15763.rs delete mode 100644 tests/ui/issues/issue-15774.rs delete mode 100644 tests/ui/issues/issue-15783.rs delete mode 100644 tests/ui/issues/issue-15793.rs delete mode 100644 tests/ui/issues/issue-15858.rs delete mode 100644 tests/ui/issues/issue-15896.rs delete mode 100644 tests/ui/issues/issue-15965.rs delete mode 100644 tests/ui/issues/issue-16048.rs delete mode 100644 tests/ui/issues/issue-16149.rs delete mode 100644 tests/ui/issues/issue-16151.rs delete mode 100644 tests/ui/issues/issue-16256.rs delete mode 100644 tests/ui/issues/issue-16401.rs delete mode 100644 tests/ui/issues/issue-16441.rs delete mode 100644 tests/ui/issues/issue-16452.rs delete mode 100644 tests/ui/issues/issue-16492.rs create mode 100644 tests/ui/iterators/chunk-iterator-mut-pattern.rs create mode 100644 tests/ui/iterators/iterator-sum-array-15673.rs create mode 100644 tests/ui/lifetimes/explicit-lifetime-required-15034.rs create mode 100644 tests/ui/lifetimes/struct-lifetime-inference-15735.rs create mode 100644 tests/ui/macros/macro-hygiene-scope-15167.rs create mode 100644 tests/ui/macros/macro-variable-capture-15189.rs create mode 100644 tests/ui/match/nested-enum-match-optimization.rs create mode 100644 tests/ui/moves/match-move-same-binding-15571.rs create mode 100644 tests/ui/never/never-type-method-call-15207.rs create mode 100644 tests/ui/pattern/enum-struct-pattern-mismatch.rs create mode 100644 tests/ui/pattern/refutable-pattern-for-loop-15381.rs create mode 100644 tests/ui/pattern/slice-pattern-recursion-15104.rs create mode 100644 tests/ui/pattern/static-binding-shadow-16149.rs create mode 100644 tests/ui/pattern/struct-field-duplicate-binding.rs create mode 100644 tests/ui/pattern/tuple-enum-match-15129.rs create mode 100644 tests/ui/pattern/unit-type-struct-pattern-mismatch.rs create mode 100644 tests/ui/statics/conditional-static-declaration.rs create mode 100644 tests/ui/structs/static-struct-init-15043.rs create mode 100644 tests/ui/traits/fn-type-trait-impl-15444.rs create mode 100644 tests/ui/traits/index-trait-multiple-impls.rs create mode 100644 tests/ui/traits/lifetime-mismatch-trait-impl.rs diff --git a/tests/ui/closures/unused-closure-warning-16256.rs b/tests/ui/closures/unused-closure-warning-16256.rs new file mode 100644 index 00000000000..1024e4511d6 --- /dev/null +++ b/tests/ui/closures/unused-closure-warning-16256.rs @@ -0,0 +1,6 @@ +//@ run-pass + +fn main() { + let mut buf = Vec::new(); + |c: u8| buf.push(c); //~ WARN unused closure that must be used +} diff --git a/tests/ui/coercion/array-slice-coercion-mismatch-15783.rs b/tests/ui/coercion/array-slice-coercion-mismatch-15783.rs new file mode 100644 index 00000000000..ef948a1a88c --- /dev/null +++ b/tests/ui/coercion/array-slice-coercion-mismatch-15783.rs @@ -0,0 +1,16 @@ +//@ dont-require-annotations: NOTE + +pub fn foo(params: Option<&[&str]>) -> usize { + params.unwrap().first().unwrap().len() +} + +fn main() { + let name = "Foo"; + let x = Some(&[name]); + let msg = foo(x); + //~^ ERROR mismatched types + //~| NOTE expected enum `Option<&[&str]>` + //~| NOTE found enum `Option<&[&str; 1]>` + //~| NOTE expected `Option<&[&str]>`, found `Option<&[&str; 1]>` + assert_eq!(msg, 3); +} diff --git a/tests/ui/derives/derive-partial-ord-discriminant-64bit.rs b/tests/ui/derives/derive-partial-ord-discriminant-64bit.rs new file mode 100644 index 00000000000..7214a4fb1d5 --- /dev/null +++ b/tests/ui/derives/derive-partial-ord-discriminant-64bit.rs @@ -0,0 +1,39 @@ +//@ run-pass +// Issue 15523: derive(PartialOrd) should use the provided +// discriminant values for the derived ordering. +// +// This test is checking corner cases that arise when you have +// 64-bit values in the variants. + +#[derive(PartialEq, PartialOrd)] +#[repr(u64)] +enum Eu64 { + Pos2 = 2, + PosMax = !0, + Pos1 = 1, +} + +#[derive(PartialEq, PartialOrd)] +#[repr(i64)] +enum Ei64 { + Pos2 = 2, + Neg1 = -1, + NegMin = 1 << 63, + PosMax = !(1 << 63), + Pos1 = 1, +} + +fn main() { + assert!(Eu64::Pos2 > Eu64::Pos1); + assert!(Eu64::Pos2 < Eu64::PosMax); + assert!(Eu64::Pos1 < Eu64::PosMax); + + + assert!(Ei64::Pos2 > Ei64::Pos1); + assert!(Ei64::Pos2 > Ei64::Neg1); + assert!(Ei64::Pos1 > Ei64::Neg1); + assert!(Ei64::Pos2 > Ei64::NegMin); + assert!(Ei64::Pos1 > Ei64::NegMin); + assert!(Ei64::Pos2 < Ei64::PosMax); + assert!(Ei64::Pos1 < Ei64::PosMax); +} diff --git a/tests/ui/derives/derive-partial-ord-discriminant.rs b/tests/ui/derives/derive-partial-ord-discriminant.rs new file mode 100644 index 00000000000..9fc2e51c6ab --- /dev/null +++ b/tests/ui/derives/derive-partial-ord-discriminant.rs @@ -0,0 +1,42 @@ +//@ run-pass +// Issue 15523: derive(PartialOrd) should use the provided +// discriminant values for the derived ordering. +// +// This is checking the basic functionality. + +#[derive(PartialEq, PartialOrd)] +enum E1 { + Pos2 = 2, + Neg1 = -1, + Pos1 = 1, +} + +#[derive(PartialEq, PartialOrd)] +#[repr(u8)] +enum E2 { + Pos2 = 2, + PosMax = !0 as u8, + Pos1 = 1, +} + +#[derive(PartialEq, PartialOrd)] +#[repr(i8)] +enum E3 { + Pos2 = 2, + Neg1 = -1_i8, + Pos1 = 1, +} + +fn main() { + assert!(E1::Pos2 > E1::Pos1); + assert!(E1::Pos1 > E1::Neg1); + assert!(E1::Pos2 > E1::Neg1); + + assert!(E2::Pos2 > E2::Pos1); + assert!(E2::Pos1 < E2::PosMax); + assert!(E2::Pos2 < E2::PosMax); + + assert!(E3::Pos2 > E3::Pos1); + assert!(E3::Pos1 > E3::Neg1); + assert!(E3::Pos2 > E3::Neg1); +} diff --git a/tests/ui/drop/early-return-drop-order.rs b/tests/ui/drop/early-return-drop-order.rs new file mode 100644 index 00000000000..0ebadd80541 --- /dev/null +++ b/tests/ui/drop/early-return-drop-order.rs @@ -0,0 +1,88 @@ +//@ run-pass +#![allow(unreachable_code)] + +#[derive(PartialEq, Debug)] +struct Bar { + x: isize +} +impl Drop for Bar { + fn drop(&mut self) { + assert_eq!(self.x, 22); + } +} + +#[derive(PartialEq, Debug)] +struct Foo { + x: Bar, + a: isize +} + +fn foo() -> Result { + return Ok(Foo { + x: Bar { x: 22 }, + a: return Err(32) + }); +} + +fn baz() -> Result { + Ok(Foo { + x: Bar { x: 22 }, + a: return Err(32) + }) +} + +// explicit immediate return +fn aa() -> isize { + return 3; +} + +// implicit immediate return +fn bb() -> isize { + 3 +} + +// implicit outptr return +fn cc() -> Result { + Ok(3) +} + +// explicit outptr return +fn dd() -> Result { + return Ok(3); +} + +trait A { + fn aaa(&self) -> isize { + 3 + } + fn bbb(&self) -> isize { + return 3; + } + fn ccc(&self) -> Result { + Ok(3) + } + fn ddd(&self) -> Result { + return Ok(3); + } +} + +impl A for isize {} + +fn main() { + assert_eq!(foo(), Err(32)); + assert_eq!(baz(), Err(32)); + + assert_eq!(aa(), 3); + assert_eq!(bb(), 3); + assert_eq!(cc().unwrap(), 3); + assert_eq!(dd().unwrap(), 3); + + let i = Box::new(32isize) as Box; + assert_eq!(i.aaa(), 3); + let i = Box::new(32isize) as Box; + assert_eq!(i.bbb(), 3); + let i = Box::new(32isize) as Box; + assert_eq!(i.ccc().unwrap(), 3); + let i = Box::new(32isize) as Box; + assert_eq!(i.ddd().unwrap(), 3); +} diff --git a/tests/ui/drop/enum-drop-impl-15063.rs b/tests/ui/drop/enum-drop-impl-15063.rs new file mode 100644 index 00000000000..969dbe5fad2 --- /dev/null +++ b/tests/ui/drop/enum-drop-impl-15063.rs @@ -0,0 +1,12 @@ +//@ run-pass +#![allow(dead_code)] +#![allow(unused_variables)] +enum Two { A, B} +impl Drop for Two { + fn drop(&mut self) { + println!("Dropping!"); + } +} +fn main() { + let k = Two::A; +} diff --git a/tests/ui/drop/generic-drop-trait-bound-15858.rs b/tests/ui/drop/generic-drop-trait-bound-15858.rs new file mode 100644 index 00000000000..27887d49e3e --- /dev/null +++ b/tests/ui/drop/generic-drop-trait-bound-15858.rs @@ -0,0 +1,35 @@ +//@ run-pass +// FIXME(static_mut_refs): this could use an atomic +#![allow(static_mut_refs)] +static mut DROP_RAN: bool = false; + +trait Bar { + fn do_something(&mut self); //~ WARN method `do_something` is never used +} + +struct BarImpl; + +impl Bar for BarImpl { + fn do_something(&mut self) {} +} + + +struct Foo(#[allow(dead_code)] B); + +impl Drop for Foo { + fn drop(&mut self) { + unsafe { + DROP_RAN = true; + } + } +} + + +fn main() { + { + let _x: Foo = Foo(BarImpl); + } + unsafe { + assert_eq!(DROP_RAN, true); + } +} diff --git a/tests/ui/drop/struct-field-drop-order.rs b/tests/ui/drop/struct-field-drop-order.rs new file mode 100644 index 00000000000..cfdba5fda35 --- /dev/null +++ b/tests/ui/drop/struct-field-drop-order.rs @@ -0,0 +1,67 @@ +//@ run-pass +#![allow(non_snake_case)] + +use std::rc::Rc; +use std::cell::Cell; + +struct Field { + number: usize, + state: Rc> +} + +impl Field { + fn new(number: usize, state: Rc>) -> Field { + Field { + number: number, + state: state + } + } +} + +impl Drop for Field { + fn drop(&mut self) { + println!("Dropping field {}", self.number); + assert_eq!(self.state.get(), self.number); + self.state.set(self.state.get()+1); + } +} + +struct NoDropImpl { + _one: Field, + _two: Field, + _three: Field +} + +struct HasDropImpl { + _one: Field, + _two: Field, + _three: Field +} + +impl Drop for HasDropImpl { + fn drop(&mut self) { + println!("HasDropImpl.drop()"); + assert_eq!(self._one.state.get(), 0); + self._one.state.set(1); + } +} + +pub fn main() { + let state = Rc::new(Cell::new(1)); + let noImpl = NoDropImpl { + _one: Field::new(1, state.clone()), + _two: Field::new(2, state.clone()), + _three: Field::new(3, state.clone()) + }; + drop(noImpl); + assert_eq!(state.get(), 4); + + state.set(0); + let hasImpl = HasDropImpl { + _one: Field::new(1, state.clone()), + _two: Field::new(2, state.clone()), + _three: Field::new(3, state.clone()) + }; + drop(hasImpl); + assert_eq!(state.get(), 4); +} diff --git a/tests/ui/drop/vec-replace-skip-while-drop.rs b/tests/ui/drop/vec-replace-skip-while-drop.rs new file mode 100644 index 00000000000..b18108e0a8a --- /dev/null +++ b/tests/ui/drop/vec-replace-skip-while-drop.rs @@ -0,0 +1,32 @@ +//@ run-pass + +// FIXME(static_mut_refs): Do not allow `static_mut_refs` lint +#![allow(static_mut_refs)] + +use std::mem; + +static mut DROP_COUNT: usize = 0; + +struct Fragment; + +impl Drop for Fragment { + fn drop(&mut self) { + unsafe { + DROP_COUNT += 1; + } + } +} + +fn main() { + { + let mut fragments = vec![Fragment, Fragment, Fragment]; + let _new_fragments: Vec = mem::replace(&mut fragments, vec![]) + .into_iter() + .skip_while(|_fragment| { + true + }).collect(); + } + unsafe { + assert_eq!(DROP_COUNT, 3); + } +} diff --git a/tests/ui/extern/empty-struct-extern-fn-16441.rs b/tests/ui/extern/empty-struct-extern-fn-16441.rs new file mode 100644 index 00000000000..58cfb389297 --- /dev/null +++ b/tests/ui/extern/empty-struct-extern-fn-16441.rs @@ -0,0 +1,11 @@ +//@ run-pass +#![allow(dead_code)] + +struct Empty; + +// This used to cause an ICE +#[allow(improper_ctypes_definitions)] +extern "C" fn ice(_a: Empty) {} + +fn main() { +} diff --git a/tests/ui/fn/fn-traits-call-once-signature.rs b/tests/ui/fn/fn-traits-call-once-signature.rs new file mode 100644 index 00000000000..408ab82eb8c --- /dev/null +++ b/tests/ui/fn/fn-traits-call-once-signature.rs @@ -0,0 +1,27 @@ +#![feature(fn_traits, unboxed_closures)] + +use std::{fmt, ops}; + +struct Debuger { + x: T +} + +impl ops::FnOnce<(),> for Debuger { + type Output = (); + fn call_once(self, _args: ()) { + //~^ ERROR `call_once` has an incompatible type for trait + //~| NOTE expected signature `extern "rust-call" fn + //~| NOTE found signature `fn + //~| NOTE expected "rust-call" fn, found "Rust" fn + println!("{:?}", self.x); + } +} + +fn make_shower(x: T) -> Debuger { + Debuger { x: x } +} + +pub fn main() { + let show3 = make_shower(3); + show3(); +} diff --git a/tests/ui/imports/enum-variant-import-path-15774.rs b/tests/ui/imports/enum-variant-import-path-15774.rs new file mode 100644 index 00000000000..dadd59cc077 --- /dev/null +++ b/tests/ui/imports/enum-variant-import-path-15774.rs @@ -0,0 +1,25 @@ +//@ edition: 2015 +//@ run-pass + +#![deny(warnings)] +#![allow(unused_imports)] + +pub enum Foo { A } +mod bar { + pub fn normal(x: ::Foo) { + use Foo::A; + match x { + A => {} + } + } + pub fn wrong(x: ::Foo) { + match x { + ::Foo::A => {} + } + } +} + +pub fn main() { + bar::normal(Foo::A); + bar::wrong(Foo::A); +} diff --git a/tests/ui/inference/return-block-type-inference-15965.rs b/tests/ui/inference/return-block-type-inference-15965.rs new file mode 100644 index 00000000000..eef4900d432 --- /dev/null +++ b/tests/ui/inference/return-block-type-inference-15965.rs @@ -0,0 +1,7 @@ +fn main() { + return + { return () } +//~^ ERROR type annotations needed [E0282] + () + ; +} diff --git a/tests/ui/issues/issue-15034.rs b/tests/ui/issues/issue-15034.rs deleted file mode 100644 index 9ea6ed89ca2..00000000000 --- a/tests/ui/issues/issue-15034.rs +++ /dev/null @@ -1,22 +0,0 @@ -pub struct Lexer<'a> { - input: &'a str, -} - -impl<'a> Lexer<'a> { - pub fn new(input: &'a str) -> Lexer<'a> { - Lexer { input: input } - } -} - -struct Parser<'a> { - lexer: &'a mut Lexer<'a>, -} - -impl<'a> Parser<'a> { - pub fn new(lexer: &'a mut Lexer) -> Parser<'a> { - Parser { lexer: lexer } - //~^ ERROR explicit lifetime required in the type of `lexer` [E0621] - } -} - -fn main() {} diff --git a/tests/ui/issues/issue-15043.rs b/tests/ui/issues/issue-15043.rs deleted file mode 100644 index a9bb46b649b..00000000000 --- a/tests/ui/issues/issue-15043.rs +++ /dev/null @@ -1,13 +0,0 @@ -//@ run-pass - -#![allow(warnings)] - -struct S(T); - -static s1: S>=S(S(0)); -static s2: S=S(0); - -fn main() { - let foo: S>=S(S(0)); - let foo: S=S(0); -} diff --git a/tests/ui/issues/issue-15063.rs b/tests/ui/issues/issue-15063.rs deleted file mode 100644 index 969dbe5fad2..00000000000 --- a/tests/ui/issues/issue-15063.rs +++ /dev/null @@ -1,12 +0,0 @@ -//@ run-pass -#![allow(dead_code)] -#![allow(unused_variables)] -enum Two { A, B} -impl Drop for Two { - fn drop(&mut self) { - println!("Dropping!"); - } -} -fn main() { - let k = Two::A; -} diff --git a/tests/ui/issues/issue-15094.rs b/tests/ui/issues/issue-15094.rs deleted file mode 100644 index 408ab82eb8c..00000000000 --- a/tests/ui/issues/issue-15094.rs +++ /dev/null @@ -1,27 +0,0 @@ -#![feature(fn_traits, unboxed_closures)] - -use std::{fmt, ops}; - -struct Debuger { - x: T -} - -impl ops::FnOnce<(),> for Debuger { - type Output = (); - fn call_once(self, _args: ()) { - //~^ ERROR `call_once` has an incompatible type for trait - //~| NOTE expected signature `extern "rust-call" fn - //~| NOTE found signature `fn - //~| NOTE expected "rust-call" fn, found "Rust" fn - println!("{:?}", self.x); - } -} - -fn make_shower(x: T) -> Debuger { - Debuger { x: x } -} - -pub fn main() { - let show3 = make_shower(3); - show3(); -} diff --git a/tests/ui/issues/issue-15104.rs b/tests/ui/issues/issue-15104.rs deleted file mode 100644 index e68c94c370e..00000000000 --- a/tests/ui/issues/issue-15104.rs +++ /dev/null @@ -1,13 +0,0 @@ -//@ run-pass - -fn main() { - assert_eq!(count_members(&[1, 2, 3, 4]), 4); -} - -fn count_members(v: &[usize]) -> usize { - match *v { - [] => 0, - [_] => 1, - [_, ref xs @ ..] => 1 + count_members(xs) - } -} diff --git a/tests/ui/issues/issue-15129-rpass.rs b/tests/ui/issues/issue-15129-rpass.rs deleted file mode 100644 index e2ddb989072..00000000000 --- a/tests/ui/issues/issue-15129-rpass.rs +++ /dev/null @@ -1,25 +0,0 @@ -//@ run-pass - -pub enum T { - T1(()), - T2(()) -} - -pub enum V { - V1(isize), - V2(bool) -} - -fn foo(x: (T, V)) -> String { - match x { - (T::T1(()), V::V1(i)) => format!("T1(()), V1({})", i), - (T::T2(()), V::V2(b)) => format!("T2(()), V2({})", b), - _ => String::new() - } -} - - -fn main() { - assert_eq!(foo((T::T1(()), V::V1(99))), "T1(()), V1(99)".to_string()); - assert_eq!(foo((T::T2(()), V::V2(true))), "T2(()), V2(true)".to_string()); -} diff --git a/tests/ui/issues/issue-15167.rs b/tests/ui/issues/issue-15167.rs deleted file mode 100644 index a2653c10ea4..00000000000 --- a/tests/ui/issues/issue-15167.rs +++ /dev/null @@ -1,26 +0,0 @@ -// macro f should not be able to inject a reference to 'n'. - -macro_rules! f { () => (n) } -//~^ ERROR cannot find value `n` in this scope -//~| ERROR cannot find value `n` in this scope -//~| ERROR cannot find value `n` in this scope -//~| ERROR cannot find value `n` in this scope - -fn main() -> (){ - for n in 0..1 { - println!("{}", f!()); - } - - if let Some(n) = None { - println!("{}", f!()); - } - - if false { - } else if let Some(n) = None { - println!("{}", f!()); - } - - while let Some(n) = None { - println!("{}", f!()); - } -} diff --git a/tests/ui/issues/issue-15189.rs b/tests/ui/issues/issue-15189.rs deleted file mode 100644 index 4dbe2179dd9..00000000000 --- a/tests/ui/issues/issue-15189.rs +++ /dev/null @@ -1,10 +0,0 @@ -//@ run-pass -macro_rules! third { - ($e:expr) => ({let x = 2; $e[x]}) -} - -fn main() { - let x = vec![10_usize,11_usize,12_usize,13_usize]; - let t = third!(x); - assert_eq!(t,12_usize); -} diff --git a/tests/ui/issues/issue-15207.rs b/tests/ui/issues/issue-15207.rs deleted file mode 100644 index 356e55ac912..00000000000 --- a/tests/ui/issues/issue-15207.rs +++ /dev/null @@ -1,6 +0,0 @@ -fn main() { - loop { - break.push(1) //~ ERROR no method named `push` found for type `!` - ; - } -} diff --git a/tests/ui/issues/issue-15260.rs b/tests/ui/issues/issue-15260.rs deleted file mode 100644 index 64fc3df3d23..00000000000 --- a/tests/ui/issues/issue-15260.rs +++ /dev/null @@ -1,25 +0,0 @@ -struct Foo { - a: usize, -} - -fn main() { - let Foo { - a: _, - a: _ - //~^ ERROR field `a` bound multiple times in the pattern - } = Foo { a: 29 }; - - let Foo { - a, - a: _ - //~^ ERROR field `a` bound multiple times in the pattern - } = Foo { a: 29 }; - - let Foo { - a, - a: _, - //~^ ERROR field `a` bound multiple times in the pattern - a: x - //~^ ERROR field `a` bound multiple times in the pattern - } = Foo { a: 29 }; -} diff --git a/tests/ui/issues/issue-15381.rs b/tests/ui/issues/issue-15381.rs deleted file mode 100644 index bd5f62ddc67..00000000000 --- a/tests/ui/issues/issue-15381.rs +++ /dev/null @@ -1,10 +0,0 @@ -fn main() { - let values: Vec = vec![1,2,3,4,5,6,7,8]; - - for &[x,y,z] in values.chunks(3).filter(|&xs| xs.len() == 3) { - //~^ ERROR refutable pattern in `for` loop binding - //~| NOTE patterns `&[]`, `&[_]`, `&[_, _]` and 1 more not covered - //~| NOTE the matched value is of type `&[u8]` - println!("y={}", y); - } -} diff --git a/tests/ui/issues/issue-15444.rs b/tests/ui/issues/issue-15444.rs deleted file mode 100644 index 14708c7733c..00000000000 --- a/tests/ui/issues/issue-15444.rs +++ /dev/null @@ -1,22 +0,0 @@ -//@ run-pass - -trait MyTrait { - fn foo(&self); -} - -impl MyTrait for fn(A, B) -> C { - fn foo(&self) {} -} - -fn bar(t: &T) { - t.foo() -} - -fn thing(a: isize, b: isize) -> isize { - a + b -} - -fn main() { - let thing: fn(isize, isize) -> isize = thing; // coerce to fn type - bar(&thing); -} diff --git a/tests/ui/issues/issue-15523-big.rs b/tests/ui/issues/issue-15523-big.rs deleted file mode 100644 index 7214a4fb1d5..00000000000 --- a/tests/ui/issues/issue-15523-big.rs +++ /dev/null @@ -1,39 +0,0 @@ -//@ run-pass -// Issue 15523: derive(PartialOrd) should use the provided -// discriminant values for the derived ordering. -// -// This test is checking corner cases that arise when you have -// 64-bit values in the variants. - -#[derive(PartialEq, PartialOrd)] -#[repr(u64)] -enum Eu64 { - Pos2 = 2, - PosMax = !0, - Pos1 = 1, -} - -#[derive(PartialEq, PartialOrd)] -#[repr(i64)] -enum Ei64 { - Pos2 = 2, - Neg1 = -1, - NegMin = 1 << 63, - PosMax = !(1 << 63), - Pos1 = 1, -} - -fn main() { - assert!(Eu64::Pos2 > Eu64::Pos1); - assert!(Eu64::Pos2 < Eu64::PosMax); - assert!(Eu64::Pos1 < Eu64::PosMax); - - - assert!(Ei64::Pos2 > Ei64::Pos1); - assert!(Ei64::Pos2 > Ei64::Neg1); - assert!(Ei64::Pos1 > Ei64::Neg1); - assert!(Ei64::Pos2 > Ei64::NegMin); - assert!(Ei64::Pos1 > Ei64::NegMin); - assert!(Ei64::Pos2 < Ei64::PosMax); - assert!(Ei64::Pos1 < Ei64::PosMax); -} diff --git a/tests/ui/issues/issue-15523.rs b/tests/ui/issues/issue-15523.rs deleted file mode 100644 index 9fc2e51c6ab..00000000000 --- a/tests/ui/issues/issue-15523.rs +++ /dev/null @@ -1,42 +0,0 @@ -//@ run-pass -// Issue 15523: derive(PartialOrd) should use the provided -// discriminant values for the derived ordering. -// -// This is checking the basic functionality. - -#[derive(PartialEq, PartialOrd)] -enum E1 { - Pos2 = 2, - Neg1 = -1, - Pos1 = 1, -} - -#[derive(PartialEq, PartialOrd)] -#[repr(u8)] -enum E2 { - Pos2 = 2, - PosMax = !0 as u8, - Pos1 = 1, -} - -#[derive(PartialEq, PartialOrd)] -#[repr(i8)] -enum E3 { - Pos2 = 2, - Neg1 = -1_i8, - Pos1 = 1, -} - -fn main() { - assert!(E1::Pos2 > E1::Pos1); - assert!(E1::Pos1 > E1::Neg1); - assert!(E1::Pos2 > E1::Neg1); - - assert!(E2::Pos2 > E2::Pos1); - assert!(E2::Pos1 < E2::PosMax); - assert!(E2::Pos2 < E2::PosMax); - - assert!(E3::Pos2 > E3::Pos1); - assert!(E3::Pos1 > E3::Neg1); - assert!(E3::Pos2 > E3::Neg1); -} diff --git a/tests/ui/issues/issue-15571.rs b/tests/ui/issues/issue-15571.rs deleted file mode 100644 index cf17113a282..00000000000 --- a/tests/ui/issues/issue-15571.rs +++ /dev/null @@ -1,57 +0,0 @@ -//@ run-pass - -fn match_on_local() { - let mut foo: Option> = Some(Box::new(5)); - match foo { - None => {}, - Some(x) => { - foo = Some(x); - } - } - println!("'{}'", foo.unwrap()); -} - -fn match_on_arg(mut foo: Option>) { - match foo { - None => {} - Some(x) => { - foo = Some(x); - } - } - println!("'{}'", foo.unwrap()); -} - -fn match_on_binding() { - match Some(Box::new(7)) { - mut foo => { - match foo { - None => {}, - Some(x) => { - foo = Some(x); - } - } - println!("'{}'", foo.unwrap()); - } - } -} - -fn match_on_upvar() { - let mut foo: Option> = Some(Box::new(8)); - let f = move|| { - match foo { - None => {}, - Some(x) => { - foo = Some(x); - } - } - println!("'{}'", foo.unwrap()); - }; - f(); -} - -fn main() { - match_on_local(); - match_on_arg(Some(Box::new(6))); - match_on_binding(); - match_on_upvar(); -} diff --git a/tests/ui/issues/issue-15673.rs b/tests/ui/issues/issue-15673.rs deleted file mode 100644 index bb61c246276..00000000000 --- a/tests/ui/issues/issue-15673.rs +++ /dev/null @@ -1,9 +0,0 @@ -//@ run-pass -#![allow(stable_features)] - -#![feature(iter_arith)] - -fn main() { - let x: [u64; 3] = [1, 2, 3]; - assert_eq!(6, (0..3).map(|i| x[i]).sum::()); -} diff --git a/tests/ui/issues/issue-15734.rs b/tests/ui/issues/issue-15734.rs deleted file mode 100644 index 26fb7061664..00000000000 --- a/tests/ui/issues/issue-15734.rs +++ /dev/null @@ -1,59 +0,0 @@ -//@ run-pass -//@ revisions: current next -//@ ignore-compare-mode-next-solver (explicit revisions) -//@[next] compile-flags: -Znext-solver - -use std::ops::Index; - -struct Mat { data: Vec, cols: usize, } - -impl Mat { - fn new(data: Vec, cols: usize) -> Mat { - Mat { data: data, cols: cols } - } - fn row<'a>(&'a self, row: usize) -> Row<&'a Mat> { - Row { mat: self, row: row, } - } -} - -impl Index<(usize, usize)> for Mat { - type Output = T; - - fn index<'a>(&'a self, (row, col): (usize, usize)) -> &'a T { - &self.data[row * self.cols + col] - } -} - -impl<'a, T> Index<(usize, usize)> for &'a Mat { - type Output = T; - - fn index<'b>(&'b self, index: (usize, usize)) -> &'b T { - (*self).index(index) - } -} - -struct Row { mat: M, row: usize, } - -impl> Index for Row { - type Output = T; - - fn index<'a>(&'a self, col: usize) -> &'a T { - &self.mat[(self.row, col)] - } -} - -fn main() { - let m = Mat::new(vec![1, 2, 3, 4, 5, 6], 3); - let r = m.row(1); - - assert_eq!(r.index(2), &6); - assert_eq!(r[2], 6); - assert_eq!(r[2], 6); - assert_eq!(6, r[2]); - - let e = r[2]; - assert_eq!(e, 6); - - let e: usize = r[2]; - assert_eq!(e, 6); -} diff --git a/tests/ui/issues/issue-15735.rs b/tests/ui/issues/issue-15735.rs deleted file mode 100644 index f5b3803f155..00000000000 --- a/tests/ui/issues/issue-15735.rs +++ /dev/null @@ -1,17 +0,0 @@ -//@ check-pass -#![allow(dead_code)] -struct A<'a> { - a: &'a i32, - b: &'a i32, -} - -impl <'a> A<'a> { - fn foo<'b>(&'b self) { - A { - a: self.a, - b: self.b, - }; - } -} - -fn main() { } diff --git a/tests/ui/issues/issue-15756.rs b/tests/ui/issues/issue-15756.rs deleted file mode 100644 index e0861dee61e..00000000000 --- a/tests/ui/issues/issue-15756.rs +++ /dev/null @@ -1,14 +0,0 @@ -use std::slice::Chunks; -use std::slice::ChunksMut; - -fn dft_iter<'a, T>(arg1: Chunks<'a,T>, arg2: ChunksMut<'a,T>) -{ - for - &mut something - //~^ ERROR the size for values of type - in arg2 - { - } -} - -fn main() {} diff --git a/tests/ui/issues/issue-15763.rs b/tests/ui/issues/issue-15763.rs deleted file mode 100644 index 0ebadd80541..00000000000 --- a/tests/ui/issues/issue-15763.rs +++ /dev/null @@ -1,88 +0,0 @@ -//@ run-pass -#![allow(unreachable_code)] - -#[derive(PartialEq, Debug)] -struct Bar { - x: isize -} -impl Drop for Bar { - fn drop(&mut self) { - assert_eq!(self.x, 22); - } -} - -#[derive(PartialEq, Debug)] -struct Foo { - x: Bar, - a: isize -} - -fn foo() -> Result { - return Ok(Foo { - x: Bar { x: 22 }, - a: return Err(32) - }); -} - -fn baz() -> Result { - Ok(Foo { - x: Bar { x: 22 }, - a: return Err(32) - }) -} - -// explicit immediate return -fn aa() -> isize { - return 3; -} - -// implicit immediate return -fn bb() -> isize { - 3 -} - -// implicit outptr return -fn cc() -> Result { - Ok(3) -} - -// explicit outptr return -fn dd() -> Result { - return Ok(3); -} - -trait A { - fn aaa(&self) -> isize { - 3 - } - fn bbb(&self) -> isize { - return 3; - } - fn ccc(&self) -> Result { - Ok(3) - } - fn ddd(&self) -> Result { - return Ok(3); - } -} - -impl A for isize {} - -fn main() { - assert_eq!(foo(), Err(32)); - assert_eq!(baz(), Err(32)); - - assert_eq!(aa(), 3); - assert_eq!(bb(), 3); - assert_eq!(cc().unwrap(), 3); - assert_eq!(dd().unwrap(), 3); - - let i = Box::new(32isize) as Box; - assert_eq!(i.aaa(), 3); - let i = Box::new(32isize) as Box; - assert_eq!(i.bbb(), 3); - let i = Box::new(32isize) as Box; - assert_eq!(i.ccc().unwrap(), 3); - let i = Box::new(32isize) as Box; - assert_eq!(i.ddd().unwrap(), 3); -} diff --git a/tests/ui/issues/issue-15774.rs b/tests/ui/issues/issue-15774.rs deleted file mode 100644 index dadd59cc077..00000000000 --- a/tests/ui/issues/issue-15774.rs +++ /dev/null @@ -1,25 +0,0 @@ -//@ edition: 2015 -//@ run-pass - -#![deny(warnings)] -#![allow(unused_imports)] - -pub enum Foo { A } -mod bar { - pub fn normal(x: ::Foo) { - use Foo::A; - match x { - A => {} - } - } - pub fn wrong(x: ::Foo) { - match x { - ::Foo::A => {} - } - } -} - -pub fn main() { - bar::normal(Foo::A); - bar::wrong(Foo::A); -} diff --git a/tests/ui/issues/issue-15783.rs b/tests/ui/issues/issue-15783.rs deleted file mode 100644 index ef948a1a88c..00000000000 --- a/tests/ui/issues/issue-15783.rs +++ /dev/null @@ -1,16 +0,0 @@ -//@ dont-require-annotations: NOTE - -pub fn foo(params: Option<&[&str]>) -> usize { - params.unwrap().first().unwrap().len() -} - -fn main() { - let name = "Foo"; - let x = Some(&[name]); - let msg = foo(x); - //~^ ERROR mismatched types - //~| NOTE expected enum `Option<&[&str]>` - //~| NOTE found enum `Option<&[&str; 1]>` - //~| NOTE expected `Option<&[&str]>`, found `Option<&[&str; 1]>` - assert_eq!(msg, 3); -} diff --git a/tests/ui/issues/issue-15793.rs b/tests/ui/issues/issue-15793.rs deleted file mode 100644 index af92e9dfa4c..00000000000 --- a/tests/ui/issues/issue-15793.rs +++ /dev/null @@ -1,27 +0,0 @@ -//@ run-pass -#![allow(dead_code)] - -enum NestedEnum { - First, - Second, - Third -} -enum Enum { - Variant1(bool), - Variant2(NestedEnum) -} - -#[inline(never)] -fn foo(x: Enum) -> isize { - match x { - Enum::Variant1(true) => 1, - Enum::Variant1(false) => 2, - Enum::Variant2(NestedEnum::Second) => 3, - Enum::Variant2(NestedEnum::Third) => 4, - Enum::Variant2(NestedEnum::First) => 5 - } -} - -fn main() { - assert_eq!(foo(Enum::Variant2(NestedEnum::Third)), 4); -} diff --git a/tests/ui/issues/issue-15858.rs b/tests/ui/issues/issue-15858.rs deleted file mode 100644 index 27887d49e3e..00000000000 --- a/tests/ui/issues/issue-15858.rs +++ /dev/null @@ -1,35 +0,0 @@ -//@ run-pass -// FIXME(static_mut_refs): this could use an atomic -#![allow(static_mut_refs)] -static mut DROP_RAN: bool = false; - -trait Bar { - fn do_something(&mut self); //~ WARN method `do_something` is never used -} - -struct BarImpl; - -impl Bar for BarImpl { - fn do_something(&mut self) {} -} - - -struct Foo(#[allow(dead_code)] B); - -impl Drop for Foo { - fn drop(&mut self) { - unsafe { - DROP_RAN = true; - } - } -} - - -fn main() { - { - let _x: Foo = Foo(BarImpl); - } - unsafe { - assert_eq!(DROP_RAN, true); - } -} diff --git a/tests/ui/issues/issue-15896.rs b/tests/ui/issues/issue-15896.rs deleted file mode 100644 index d3153b516e6..00000000000 --- a/tests/ui/issues/issue-15896.rs +++ /dev/null @@ -1,15 +0,0 @@ -// Regression test for #15896. It used to ICE rustc. - -fn main() { - enum R { REB(()) } - struct Tau { t: usize } - enum E { B(R, Tau) } - - let e = E::B(R::REB(()), Tau { t: 3 }); - let u = match e { - E::B( - Tau{t: x}, - //~^ ERROR mismatched types - _) => x, - }; -} diff --git a/tests/ui/issues/issue-15965.rs b/tests/ui/issues/issue-15965.rs deleted file mode 100644 index eef4900d432..00000000000 --- a/tests/ui/issues/issue-15965.rs +++ /dev/null @@ -1,7 +0,0 @@ -fn main() { - return - { return () } -//~^ ERROR type annotations needed [E0282] - () - ; -} diff --git a/tests/ui/issues/issue-16048.rs b/tests/ui/issues/issue-16048.rs deleted file mode 100644 index eaf6acff26b..00000000000 --- a/tests/ui/issues/issue-16048.rs +++ /dev/null @@ -1,30 +0,0 @@ -trait NoLifetime { - fn get<'p, T : Test<'p>>(&self) -> T; - //~^ NOTE lifetimes in impl do not match this method in trait -} - -trait Test<'p> { - fn new(buf: &'p mut [u8]) -> Self; -} - -struct Foo<'a> { - buf: &'a mut [u8], -} - -impl<'a> Test<'a> for Foo<'a> { - fn new(buf: &'a mut [u8]) -> Foo<'a> { - Foo { buf: buf } - } -} - -impl<'a> NoLifetime for Foo<'a> { - fn get<'p, T: Test<'a> + From>>(&self) -> T { - //~^ ERROR E0195 - //~| NOTE lifetimes do not match method in trait - return *self as T; - //~^ ERROR non-primitive cast: `Foo<'a>` as `T` - //~| NOTE an `as` expression can only be used to convert between primitive types - } -} - -fn main() {} diff --git a/tests/ui/issues/issue-16149.rs b/tests/ui/issues/issue-16149.rs deleted file mode 100644 index 51b60725c5a..00000000000 --- a/tests/ui/issues/issue-16149.rs +++ /dev/null @@ -1,11 +0,0 @@ -extern "C" { - static externalValue: isize; -} - -fn main() { - let boolValue = match 42 { - externalValue => true, - //~^ ERROR match bindings cannot shadow statics - _ => false, - }; -} diff --git a/tests/ui/issues/issue-16151.rs b/tests/ui/issues/issue-16151.rs deleted file mode 100644 index b18108e0a8a..00000000000 --- a/tests/ui/issues/issue-16151.rs +++ /dev/null @@ -1,32 +0,0 @@ -//@ run-pass - -// FIXME(static_mut_refs): Do not allow `static_mut_refs` lint -#![allow(static_mut_refs)] - -use std::mem; - -static mut DROP_COUNT: usize = 0; - -struct Fragment; - -impl Drop for Fragment { - fn drop(&mut self) { - unsafe { - DROP_COUNT += 1; - } - } -} - -fn main() { - { - let mut fragments = vec![Fragment, Fragment, Fragment]; - let _new_fragments: Vec = mem::replace(&mut fragments, vec![]) - .into_iter() - .skip_while(|_fragment| { - true - }).collect(); - } - unsafe { - assert_eq!(DROP_COUNT, 3); - } -} diff --git a/tests/ui/issues/issue-16256.rs b/tests/ui/issues/issue-16256.rs deleted file mode 100644 index 1024e4511d6..00000000000 --- a/tests/ui/issues/issue-16256.rs +++ /dev/null @@ -1,6 +0,0 @@ -//@ run-pass - -fn main() { - let mut buf = Vec::new(); - |c: u8| buf.push(c); //~ WARN unused closure that must be used -} diff --git a/tests/ui/issues/issue-16401.rs b/tests/ui/issues/issue-16401.rs deleted file mode 100644 index 0985a6806d2..00000000000 --- a/tests/ui/issues/issue-16401.rs +++ /dev/null @@ -1,15 +0,0 @@ -struct Slice { - data: *const T, - len: usize, -} - -fn main() { - match () { //~ NOTE this expression has type `()` - Slice { data: data, len: len } => (), - //~^ ERROR mismatched types - //~| NOTE expected unit type `()` - //~| NOTE found struct `Slice<_>` - //~| NOTE expected `()`, found `Slice<_>` - _ => unreachable!() - } -} diff --git a/tests/ui/issues/issue-16441.rs b/tests/ui/issues/issue-16441.rs deleted file mode 100644 index 58cfb389297..00000000000 --- a/tests/ui/issues/issue-16441.rs +++ /dev/null @@ -1,11 +0,0 @@ -//@ run-pass -#![allow(dead_code)] - -struct Empty; - -// This used to cause an ICE -#[allow(improper_ctypes_definitions)] -extern "C" fn ice(_a: Empty) {} - -fn main() { -} diff --git a/tests/ui/issues/issue-16452.rs b/tests/ui/issues/issue-16452.rs deleted file mode 100644 index 4ab74f09059..00000000000 --- a/tests/ui/issues/issue-16452.rs +++ /dev/null @@ -1,9 +0,0 @@ -//@ run-pass -#![allow(dead_code)] - -fn main() { - if true { return } - match () { - () => { static MAGIC: usize = 0; } - } -} diff --git a/tests/ui/issues/issue-16492.rs b/tests/ui/issues/issue-16492.rs deleted file mode 100644 index cfdba5fda35..00000000000 --- a/tests/ui/issues/issue-16492.rs +++ /dev/null @@ -1,67 +0,0 @@ -//@ run-pass -#![allow(non_snake_case)] - -use std::rc::Rc; -use std::cell::Cell; - -struct Field { - number: usize, - state: Rc> -} - -impl Field { - fn new(number: usize, state: Rc>) -> Field { - Field { - number: number, - state: state - } - } -} - -impl Drop for Field { - fn drop(&mut self) { - println!("Dropping field {}", self.number); - assert_eq!(self.state.get(), self.number); - self.state.set(self.state.get()+1); - } -} - -struct NoDropImpl { - _one: Field, - _two: Field, - _three: Field -} - -struct HasDropImpl { - _one: Field, - _two: Field, - _three: Field -} - -impl Drop for HasDropImpl { - fn drop(&mut self) { - println!("HasDropImpl.drop()"); - assert_eq!(self._one.state.get(), 0); - self._one.state.set(1); - } -} - -pub fn main() { - let state = Rc::new(Cell::new(1)); - let noImpl = NoDropImpl { - _one: Field::new(1, state.clone()), - _two: Field::new(2, state.clone()), - _three: Field::new(3, state.clone()) - }; - drop(noImpl); - assert_eq!(state.get(), 4); - - state.set(0); - let hasImpl = HasDropImpl { - _one: Field::new(1, state.clone()), - _two: Field::new(2, state.clone()), - _three: Field::new(3, state.clone()) - }; - drop(hasImpl); - assert_eq!(state.get(), 4); -} diff --git a/tests/ui/iterators/chunk-iterator-mut-pattern.rs b/tests/ui/iterators/chunk-iterator-mut-pattern.rs new file mode 100644 index 00000000000..e0861dee61e --- /dev/null +++ b/tests/ui/iterators/chunk-iterator-mut-pattern.rs @@ -0,0 +1,14 @@ +use std::slice::Chunks; +use std::slice::ChunksMut; + +fn dft_iter<'a, T>(arg1: Chunks<'a,T>, arg2: ChunksMut<'a,T>) +{ + for + &mut something + //~^ ERROR the size for values of type + in arg2 + { + } +} + +fn main() {} diff --git a/tests/ui/iterators/iterator-sum-array-15673.rs b/tests/ui/iterators/iterator-sum-array-15673.rs new file mode 100644 index 00000000000..bb61c246276 --- /dev/null +++ b/tests/ui/iterators/iterator-sum-array-15673.rs @@ -0,0 +1,9 @@ +//@ run-pass +#![allow(stable_features)] + +#![feature(iter_arith)] + +fn main() { + let x: [u64; 3] = [1, 2, 3]; + assert_eq!(6, (0..3).map(|i| x[i]).sum::()); +} diff --git a/tests/ui/lifetimes/explicit-lifetime-required-15034.rs b/tests/ui/lifetimes/explicit-lifetime-required-15034.rs new file mode 100644 index 00000000000..9ea6ed89ca2 --- /dev/null +++ b/tests/ui/lifetimes/explicit-lifetime-required-15034.rs @@ -0,0 +1,22 @@ +pub struct Lexer<'a> { + input: &'a str, +} + +impl<'a> Lexer<'a> { + pub fn new(input: &'a str) -> Lexer<'a> { + Lexer { input: input } + } +} + +struct Parser<'a> { + lexer: &'a mut Lexer<'a>, +} + +impl<'a> Parser<'a> { + pub fn new(lexer: &'a mut Lexer) -> Parser<'a> { + Parser { lexer: lexer } + //~^ ERROR explicit lifetime required in the type of `lexer` [E0621] + } +} + +fn main() {} diff --git a/tests/ui/lifetimes/struct-lifetime-inference-15735.rs b/tests/ui/lifetimes/struct-lifetime-inference-15735.rs new file mode 100644 index 00000000000..f5b3803f155 --- /dev/null +++ b/tests/ui/lifetimes/struct-lifetime-inference-15735.rs @@ -0,0 +1,17 @@ +//@ check-pass +#![allow(dead_code)] +struct A<'a> { + a: &'a i32, + b: &'a i32, +} + +impl <'a> A<'a> { + fn foo<'b>(&'b self) { + A { + a: self.a, + b: self.b, + }; + } +} + +fn main() { } diff --git a/tests/ui/macros/macro-hygiene-scope-15167.rs b/tests/ui/macros/macro-hygiene-scope-15167.rs new file mode 100644 index 00000000000..a2653c10ea4 --- /dev/null +++ b/tests/ui/macros/macro-hygiene-scope-15167.rs @@ -0,0 +1,26 @@ +// macro f should not be able to inject a reference to 'n'. + +macro_rules! f { () => (n) } +//~^ ERROR cannot find value `n` in this scope +//~| ERROR cannot find value `n` in this scope +//~| ERROR cannot find value `n` in this scope +//~| ERROR cannot find value `n` in this scope + +fn main() -> (){ + for n in 0..1 { + println!("{}", f!()); + } + + if let Some(n) = None { + println!("{}", f!()); + } + + if false { + } else if let Some(n) = None { + println!("{}", f!()); + } + + while let Some(n) = None { + println!("{}", f!()); + } +} diff --git a/tests/ui/macros/macro-variable-capture-15189.rs b/tests/ui/macros/macro-variable-capture-15189.rs new file mode 100644 index 00000000000..4dbe2179dd9 --- /dev/null +++ b/tests/ui/macros/macro-variable-capture-15189.rs @@ -0,0 +1,10 @@ +//@ run-pass +macro_rules! third { + ($e:expr) => ({let x = 2; $e[x]}) +} + +fn main() { + let x = vec![10_usize,11_usize,12_usize,13_usize]; + let t = third!(x); + assert_eq!(t,12_usize); +} diff --git a/tests/ui/match/nested-enum-match-optimization.rs b/tests/ui/match/nested-enum-match-optimization.rs new file mode 100644 index 00000000000..af92e9dfa4c --- /dev/null +++ b/tests/ui/match/nested-enum-match-optimization.rs @@ -0,0 +1,27 @@ +//@ run-pass +#![allow(dead_code)] + +enum NestedEnum { + First, + Second, + Third +} +enum Enum { + Variant1(bool), + Variant2(NestedEnum) +} + +#[inline(never)] +fn foo(x: Enum) -> isize { + match x { + Enum::Variant1(true) => 1, + Enum::Variant1(false) => 2, + Enum::Variant2(NestedEnum::Second) => 3, + Enum::Variant2(NestedEnum::Third) => 4, + Enum::Variant2(NestedEnum::First) => 5 + } +} + +fn main() { + assert_eq!(foo(Enum::Variant2(NestedEnum::Third)), 4); +} diff --git a/tests/ui/moves/match-move-same-binding-15571.rs b/tests/ui/moves/match-move-same-binding-15571.rs new file mode 100644 index 00000000000..cf17113a282 --- /dev/null +++ b/tests/ui/moves/match-move-same-binding-15571.rs @@ -0,0 +1,57 @@ +//@ run-pass + +fn match_on_local() { + let mut foo: Option> = Some(Box::new(5)); + match foo { + None => {}, + Some(x) => { + foo = Some(x); + } + } + println!("'{}'", foo.unwrap()); +} + +fn match_on_arg(mut foo: Option>) { + match foo { + None => {} + Some(x) => { + foo = Some(x); + } + } + println!("'{}'", foo.unwrap()); +} + +fn match_on_binding() { + match Some(Box::new(7)) { + mut foo => { + match foo { + None => {}, + Some(x) => { + foo = Some(x); + } + } + println!("'{}'", foo.unwrap()); + } + } +} + +fn match_on_upvar() { + let mut foo: Option> = Some(Box::new(8)); + let f = move|| { + match foo { + None => {}, + Some(x) => { + foo = Some(x); + } + } + println!("'{}'", foo.unwrap()); + }; + f(); +} + +fn main() { + match_on_local(); + match_on_arg(Some(Box::new(6))); + match_on_binding(); + match_on_upvar(); +} diff --git a/tests/ui/never/never-type-method-call-15207.rs b/tests/ui/never/never-type-method-call-15207.rs new file mode 100644 index 00000000000..356e55ac912 --- /dev/null +++ b/tests/ui/never/never-type-method-call-15207.rs @@ -0,0 +1,6 @@ +fn main() { + loop { + break.push(1) //~ ERROR no method named `push` found for type `!` + ; + } +} diff --git a/tests/ui/pattern/enum-struct-pattern-mismatch.rs b/tests/ui/pattern/enum-struct-pattern-mismatch.rs new file mode 100644 index 00000000000..d3153b516e6 --- /dev/null +++ b/tests/ui/pattern/enum-struct-pattern-mismatch.rs @@ -0,0 +1,15 @@ +// Regression test for #15896. It used to ICE rustc. + +fn main() { + enum R { REB(()) } + struct Tau { t: usize } + enum E { B(R, Tau) } + + let e = E::B(R::REB(()), Tau { t: 3 }); + let u = match e { + E::B( + Tau{t: x}, + //~^ ERROR mismatched types + _) => x, + }; +} diff --git a/tests/ui/pattern/refutable-pattern-for-loop-15381.rs b/tests/ui/pattern/refutable-pattern-for-loop-15381.rs new file mode 100644 index 00000000000..bd5f62ddc67 --- /dev/null +++ b/tests/ui/pattern/refutable-pattern-for-loop-15381.rs @@ -0,0 +1,10 @@ +fn main() { + let values: Vec = vec![1,2,3,4,5,6,7,8]; + + for &[x,y,z] in values.chunks(3).filter(|&xs| xs.len() == 3) { + //~^ ERROR refutable pattern in `for` loop binding + //~| NOTE patterns `&[]`, `&[_]`, `&[_, _]` and 1 more not covered + //~| NOTE the matched value is of type `&[u8]` + println!("y={}", y); + } +} diff --git a/tests/ui/pattern/slice-pattern-recursion-15104.rs b/tests/ui/pattern/slice-pattern-recursion-15104.rs new file mode 100644 index 00000000000..e68c94c370e --- /dev/null +++ b/tests/ui/pattern/slice-pattern-recursion-15104.rs @@ -0,0 +1,13 @@ +//@ run-pass + +fn main() { + assert_eq!(count_members(&[1, 2, 3, 4]), 4); +} + +fn count_members(v: &[usize]) -> usize { + match *v { + [] => 0, + [_] => 1, + [_, ref xs @ ..] => 1 + count_members(xs) + } +} diff --git a/tests/ui/pattern/static-binding-shadow-16149.rs b/tests/ui/pattern/static-binding-shadow-16149.rs new file mode 100644 index 00000000000..51b60725c5a --- /dev/null +++ b/tests/ui/pattern/static-binding-shadow-16149.rs @@ -0,0 +1,11 @@ +extern "C" { + static externalValue: isize; +} + +fn main() { + let boolValue = match 42 { + externalValue => true, + //~^ ERROR match bindings cannot shadow statics + _ => false, + }; +} diff --git a/tests/ui/pattern/struct-field-duplicate-binding.rs b/tests/ui/pattern/struct-field-duplicate-binding.rs new file mode 100644 index 00000000000..64fc3df3d23 --- /dev/null +++ b/tests/ui/pattern/struct-field-duplicate-binding.rs @@ -0,0 +1,25 @@ +struct Foo { + a: usize, +} + +fn main() { + let Foo { + a: _, + a: _ + //~^ ERROR field `a` bound multiple times in the pattern + } = Foo { a: 29 }; + + let Foo { + a, + a: _ + //~^ ERROR field `a` bound multiple times in the pattern + } = Foo { a: 29 }; + + let Foo { + a, + a: _, + //~^ ERROR field `a` bound multiple times in the pattern + a: x + //~^ ERROR field `a` bound multiple times in the pattern + } = Foo { a: 29 }; +} diff --git a/tests/ui/pattern/tuple-enum-match-15129.rs b/tests/ui/pattern/tuple-enum-match-15129.rs new file mode 100644 index 00000000000..e2ddb989072 --- /dev/null +++ b/tests/ui/pattern/tuple-enum-match-15129.rs @@ -0,0 +1,25 @@ +//@ run-pass + +pub enum T { + T1(()), + T2(()) +} + +pub enum V { + V1(isize), + V2(bool) +} + +fn foo(x: (T, V)) -> String { + match x { + (T::T1(()), V::V1(i)) => format!("T1(()), V1({})", i), + (T::T2(()), V::V2(b)) => format!("T2(()), V2({})", b), + _ => String::new() + } +} + + +fn main() { + assert_eq!(foo((T::T1(()), V::V1(99))), "T1(()), V1(99)".to_string()); + assert_eq!(foo((T::T2(()), V::V2(true))), "T2(()), V2(true)".to_string()); +} diff --git a/tests/ui/pattern/unit-type-struct-pattern-mismatch.rs b/tests/ui/pattern/unit-type-struct-pattern-mismatch.rs new file mode 100644 index 00000000000..0985a6806d2 --- /dev/null +++ b/tests/ui/pattern/unit-type-struct-pattern-mismatch.rs @@ -0,0 +1,15 @@ +struct Slice { + data: *const T, + len: usize, +} + +fn main() { + match () { //~ NOTE this expression has type `()` + Slice { data: data, len: len } => (), + //~^ ERROR mismatched types + //~| NOTE expected unit type `()` + //~| NOTE found struct `Slice<_>` + //~| NOTE expected `()`, found `Slice<_>` + _ => unreachable!() + } +} diff --git a/tests/ui/statics/conditional-static-declaration.rs b/tests/ui/statics/conditional-static-declaration.rs new file mode 100644 index 00000000000..4ab74f09059 --- /dev/null +++ b/tests/ui/statics/conditional-static-declaration.rs @@ -0,0 +1,9 @@ +//@ run-pass +#![allow(dead_code)] + +fn main() { + if true { return } + match () { + () => { static MAGIC: usize = 0; } + } +} diff --git a/tests/ui/structs/static-struct-init-15043.rs b/tests/ui/structs/static-struct-init-15043.rs new file mode 100644 index 00000000000..a9bb46b649b --- /dev/null +++ b/tests/ui/structs/static-struct-init-15043.rs @@ -0,0 +1,13 @@ +//@ run-pass + +#![allow(warnings)] + +struct S(T); + +static s1: S>=S(S(0)); +static s2: S=S(0); + +fn main() { + let foo: S>=S(S(0)); + let foo: S=S(0); +} diff --git a/tests/ui/traits/fn-type-trait-impl-15444.rs b/tests/ui/traits/fn-type-trait-impl-15444.rs new file mode 100644 index 00000000000..14708c7733c --- /dev/null +++ b/tests/ui/traits/fn-type-trait-impl-15444.rs @@ -0,0 +1,22 @@ +//@ run-pass + +trait MyTrait { + fn foo(&self); +} + +impl MyTrait for fn(A, B) -> C { + fn foo(&self) {} +} + +fn bar(t: &T) { + t.foo() +} + +fn thing(a: isize, b: isize) -> isize { + a + b +} + +fn main() { + let thing: fn(isize, isize) -> isize = thing; // coerce to fn type + bar(&thing); +} diff --git a/tests/ui/traits/index-trait-multiple-impls.rs b/tests/ui/traits/index-trait-multiple-impls.rs new file mode 100644 index 00000000000..26fb7061664 --- /dev/null +++ b/tests/ui/traits/index-trait-multiple-impls.rs @@ -0,0 +1,59 @@ +//@ run-pass +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@[next] compile-flags: -Znext-solver + +use std::ops::Index; + +struct Mat { data: Vec, cols: usize, } + +impl Mat { + fn new(data: Vec, cols: usize) -> Mat { + Mat { data: data, cols: cols } + } + fn row<'a>(&'a self, row: usize) -> Row<&'a Mat> { + Row { mat: self, row: row, } + } +} + +impl Index<(usize, usize)> for Mat { + type Output = T; + + fn index<'a>(&'a self, (row, col): (usize, usize)) -> &'a T { + &self.data[row * self.cols + col] + } +} + +impl<'a, T> Index<(usize, usize)> for &'a Mat { + type Output = T; + + fn index<'b>(&'b self, index: (usize, usize)) -> &'b T { + (*self).index(index) + } +} + +struct Row { mat: M, row: usize, } + +impl> Index for Row { + type Output = T; + + fn index<'a>(&'a self, col: usize) -> &'a T { + &self.mat[(self.row, col)] + } +} + +fn main() { + let m = Mat::new(vec![1, 2, 3, 4, 5, 6], 3); + let r = m.row(1); + + assert_eq!(r.index(2), &6); + assert_eq!(r[2], 6); + assert_eq!(r[2], 6); + assert_eq!(6, r[2]); + + let e = r[2]; + assert_eq!(e, 6); + + let e: usize = r[2]; + assert_eq!(e, 6); +} diff --git a/tests/ui/traits/lifetime-mismatch-trait-impl.rs b/tests/ui/traits/lifetime-mismatch-trait-impl.rs new file mode 100644 index 00000000000..eaf6acff26b --- /dev/null +++ b/tests/ui/traits/lifetime-mismatch-trait-impl.rs @@ -0,0 +1,30 @@ +trait NoLifetime { + fn get<'p, T : Test<'p>>(&self) -> T; + //~^ NOTE lifetimes in impl do not match this method in trait +} + +trait Test<'p> { + fn new(buf: &'p mut [u8]) -> Self; +} + +struct Foo<'a> { + buf: &'a mut [u8], +} + +impl<'a> Test<'a> for Foo<'a> { + fn new(buf: &'a mut [u8]) -> Foo<'a> { + Foo { buf: buf } + } +} + +impl<'a> NoLifetime for Foo<'a> { + fn get<'p, T: Test<'a> + From>>(&self) -> T { + //~^ ERROR E0195 + //~| NOTE lifetimes do not match method in trait + return *self as T; + //~^ ERROR non-primitive cast: `Foo<'a>` as `T` + //~| NOTE an `as` expression can only be used to convert between primitive types + } +} + +fn main() {} -- cgit 1.4.1-3-g733a5 From c6ac515a2bcf6dbb4e27b11111da80f5ff36a88d Mon Sep 17 00:00:00 2001 From: "Pascal S. de Kloe" Date: Thu, 26 Jun 2025 14:52:39 +0200 Subject: fmt of non-decimals is always non-negative due to the two's-complement output --- library/core/src/fmt/num.rs | 88 ++++++++++++++++++++++++--------------------- 1 file changed, 48 insertions(+), 40 deletions(-) diff --git a/library/core/src/fmt/num.rs b/library/core/src/fmt/num.rs index 7d41ae45093..a52749d4325 100644 --- a/library/core/src/fmt/num.rs +++ b/library/core/src/fmt/num.rs @@ -54,44 +54,31 @@ unsafe trait GenericRadix: Sized { /// Converts an integer to corresponding radix digit. fn digit(x: u8) -> u8; - /// Format an integer using the radix using a formatter. + /// Format an unsigned integer using the radix using a formatter. fn fmt_int(&self, mut x: T, f: &mut fmt::Formatter<'_>) -> fmt::Result { // The radix can be as low as 2, so we need a buffer of at least 128 // characters for a base 2 number. let zero = T::zero(); - let is_nonnegative = x >= zero; let mut buf = [MaybeUninit::::uninit(); 128]; let mut offset = buf.len(); let base = T::from_u8(Self::BASE); - if is_nonnegative { - // Accumulate each digit of the number from the least significant - // to the most significant figure. - loop { - let n = x % base; // Get the current place value. - x = x / base; // Deaccumulate the number. - offset -= 1; - buf[offset].write(Self::digit(n.to_u8())); // Store the digit in the buffer. - if x == zero { - // No more digits left to accumulate. - break; - }; - } - } else { - // Do the same as above, but accounting for two's complement. - loop { - let n = zero - (x % base); // Get the current place value. - x = x / base; // Deaccumulate the number. - offset -= 1; - buf[offset].write(Self::digit(n.to_u8())); // Store the digit in the buffer. - if x == zero { - // No more digits left to accumulate. - break; - }; - } + + // Accumulate each digit of the number from the least significant + // to the most significant figure. + loop { + let n = x % base; // Get the current place value. + x = x / base; // Deaccumulate the number. + offset -= 1; + buf[offset].write(Self::digit(n.to_u8())); // Store the digit in the buffer. + if x == zero { + // No more digits left to accumulate. + break; + }; } + // SAFETY: Starting from `offset`, all elements of the slice have been set. - let buf_slice = unsafe { slice_buffer_to_str(&buf, offset) }; - f.pad_integral(is_nonnegative, Self::PREFIX, buf_slice) + let digits = unsafe { slice_buffer_to_str(&buf, offset) }; + f.pad_integral(true, Self::PREFIX, digits) } } @@ -132,11 +119,11 @@ radix! { LowerHex, 16, "0x", x @ 0 ..= 9 => b'0' + x, x @ 10 ..= 15 => b'a' + radix! { UpperHex, 16, "0x", x @ 0 ..= 9 => b'0' + x, x @ 10 ..= 15 => b'A' + (x - 10) } macro_rules! int_base { - (fmt::$Trait:ident for $T:ident as $U:ident -> $Radix:ident) => { + (fmt::$Trait:ident for $T:ident -> $Radix:ident) => { #[stable(feature = "rust1", since = "1.0.0")] impl fmt::$Trait for $T { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - $Radix.fmt_int(*self as $U, f) + $Radix.fmt_int(*self, f) } } }; @@ -144,15 +131,36 @@ macro_rules! int_base { macro_rules! integer { ($Int:ident, $Uint:ident) => { - int_base! { fmt::Binary for $Int as $Uint -> Binary } - int_base! { fmt::Octal for $Int as $Uint -> Octal } - int_base! { fmt::LowerHex for $Int as $Uint -> LowerHex } - int_base! { fmt::UpperHex for $Int as $Uint -> UpperHex } - - int_base! { fmt::Binary for $Uint as $Uint -> Binary } - int_base! { fmt::Octal for $Uint as $Uint -> Octal } - int_base! { fmt::LowerHex for $Uint as $Uint -> LowerHex } - int_base! { fmt::UpperHex for $Uint as $Uint -> UpperHex } + int_base! { fmt::Binary for $Uint -> Binary } + int_base! { fmt::Octal for $Uint -> Octal } + int_base! { fmt::LowerHex for $Uint -> LowerHex } + int_base! { fmt::UpperHex for $Uint -> UpperHex } + + // Format signed integers as unsigned (two’s complement representation). + #[stable(feature = "rust1", since = "1.0.0")] + impl fmt::Binary for $Int { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Binary::fmt(&self.cast_unsigned(), f) + } + } + #[stable(feature = "rust1", since = "1.0.0")] + impl fmt::Octal for $Int { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Octal::fmt(&self.cast_unsigned(), f) + } + } + #[stable(feature = "rust1", since = "1.0.0")] + impl fmt::LowerHex for $Int { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::LowerHex::fmt(&self.cast_unsigned(), f) + } + } + #[stable(feature = "rust1", since = "1.0.0")] + impl fmt::UpperHex for $Int { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::UpperHex::fmt(&self.cast_unsigned(), f) + } + } }; } integer! { isize, usize } -- cgit 1.4.1-3-g733a5 From 7704a3968523e872dbda05e449cd6375e4520bad Mon Sep 17 00:00:00 2001 From: "Pascal S. de Kloe" Date: Sun, 29 Jun 2025 17:08:40 +0200 Subject: fmt benchmarks for binary, octal and hex --- library/coretests/benches/fmt.rs | 180 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 180 insertions(+) diff --git a/library/coretests/benches/fmt.rs b/library/coretests/benches/fmt.rs index ee8e981b46b..f45b921b939 100644 --- a/library/coretests/benches/fmt.rs +++ b/library/coretests/benches/fmt.rs @@ -162,3 +162,183 @@ fn write_u8_min(bh: &mut Bencher) { black_box(format!("{}", black_box(u8::MIN))); }); } + +#[bench] +fn write_i8_bin(bh: &mut Bencher) { + let mut buf = String::with_capacity(256); + bh.iter(|| { + write!(black_box(&mut buf), "{:b}", black_box(0_i8)).unwrap(); + write!(black_box(&mut buf), "{:b}", black_box(100_i8)).unwrap(); + write!(black_box(&mut buf), "{:b}", black_box(-100_i8)).unwrap(); + write!(black_box(&mut buf), "{:b}", black_box(1_i8 << 4)).unwrap(); + black_box(&mut buf).clear(); + }); +} + +#[bench] +fn write_i16_bin(bh: &mut Bencher) { + let mut buf = String::with_capacity(256); + bh.iter(|| { + write!(black_box(&mut buf), "{:b}", black_box(0_i16)).unwrap(); + write!(black_box(&mut buf), "{:b}", black_box(100_i16)).unwrap(); + write!(black_box(&mut buf), "{:b}", black_box(-100_i16)).unwrap(); + write!(black_box(&mut buf), "{:b}", black_box(1_i16 << 8)).unwrap(); + black_box(&mut buf).clear(); + }); +} + +#[bench] +fn write_i32_bin(bh: &mut Bencher) { + let mut buf = String::with_capacity(256); + bh.iter(|| { + write!(black_box(&mut buf), "{:b}", black_box(0_i32)).unwrap(); + write!(black_box(&mut buf), "{:b}", black_box(100_i32)).unwrap(); + write!(black_box(&mut buf), "{:b}", black_box(-100_i32)).unwrap(); + write!(black_box(&mut buf), "{:b}", black_box(1_i32 << 16)).unwrap(); + black_box(&mut buf).clear(); + }); +} + +#[bench] +fn write_i64_bin(bh: &mut Bencher) { + let mut buf = String::with_capacity(256); + bh.iter(|| { + write!(black_box(&mut buf), "{:b}", black_box(0_i64)).unwrap(); + write!(black_box(&mut buf), "{:b}", black_box(100_i64)).unwrap(); + write!(black_box(&mut buf), "{:b}", black_box(-100_i64)).unwrap(); + write!(black_box(&mut buf), "{:b}", black_box(1_i64 << 32)).unwrap(); + black_box(&mut buf).clear(); + }); +} + +#[bench] +fn write_i128_bin(bh: &mut Bencher) { + let mut buf = String::with_capacity(256); + bh.iter(|| { + write!(black_box(&mut buf), "{:b}", black_box(0_i128)).unwrap(); + write!(black_box(&mut buf), "{:b}", black_box(100_i128)).unwrap(); + write!(black_box(&mut buf), "{:b}", black_box(-100_i128)).unwrap(); + write!(black_box(&mut buf), "{:b}", black_box(1_i128 << 64)).unwrap(); + black_box(&mut buf).clear(); + }); +} + +#[bench] +fn write_i8_oct(bh: &mut Bencher) { + let mut buf = String::with_capacity(256); + bh.iter(|| { + write!(black_box(&mut buf), "{:o}", black_box(0_i8)).unwrap(); + write!(black_box(&mut buf), "{:o}", black_box(100_i8)).unwrap(); + write!(black_box(&mut buf), "{:o}", black_box(-100_i8)).unwrap(); + write!(black_box(&mut buf), "{:o}", black_box(1_i8 << 4)).unwrap(); + black_box(&mut buf).clear(); + }); +} + +#[bench] +fn write_i16_oct(bh: &mut Bencher) { + let mut buf = String::with_capacity(256); + bh.iter(|| { + write!(black_box(&mut buf), "{:o}", black_box(0_i16)).unwrap(); + write!(black_box(&mut buf), "{:o}", black_box(100_i16)).unwrap(); + write!(black_box(&mut buf), "{:o}", black_box(-100_i16)).unwrap(); + write!(black_box(&mut buf), "{:o}", black_box(1_i16 << 8)).unwrap(); + black_box(&mut buf).clear(); + }); +} + +#[bench] +fn write_i32_oct(bh: &mut Bencher) { + let mut buf = String::with_capacity(256); + bh.iter(|| { + write!(black_box(&mut buf), "{:o}", black_box(0_i32)).unwrap(); + write!(black_box(&mut buf), "{:o}", black_box(100_i32)).unwrap(); + write!(black_box(&mut buf), "{:o}", black_box(-100_i32)).unwrap(); + write!(black_box(&mut buf), "{:o}", black_box(1_i32 << 16)).unwrap(); + black_box(&mut buf).clear(); + }); +} + +#[bench] +fn write_i64_oct(bh: &mut Bencher) { + let mut buf = String::with_capacity(256); + bh.iter(|| { + write!(black_box(&mut buf), "{:o}", black_box(0_i64)).unwrap(); + write!(black_box(&mut buf), "{:o}", black_box(100_i64)).unwrap(); + write!(black_box(&mut buf), "{:o}", black_box(-100_i64)).unwrap(); + write!(black_box(&mut buf), "{:o}", black_box(1_i64 << 32)).unwrap(); + black_box(&mut buf).clear(); + }); +} + +#[bench] +fn write_i128_oct(bh: &mut Bencher) { + let mut buf = String::with_capacity(256); + bh.iter(|| { + write!(black_box(&mut buf), "{:o}", black_box(0_i128)).unwrap(); + write!(black_box(&mut buf), "{:o}", black_box(100_i128)).unwrap(); + write!(black_box(&mut buf), "{:o}", black_box(-100_i128)).unwrap(); + write!(black_box(&mut buf), "{:o}", black_box(1_i128 << 64)).unwrap(); + black_box(&mut buf).clear(); + }); +} + +#[bench] +fn write_i8_hex(bh: &mut Bencher) { + let mut buf = String::with_capacity(256); + bh.iter(|| { + write!(black_box(&mut buf), "{:x}", black_box(0_i8)).unwrap(); + write!(black_box(&mut buf), "{:x}", black_box(100_i8)).unwrap(); + write!(black_box(&mut buf), "{:x}", black_box(-100_i8)).unwrap(); + write!(black_box(&mut buf), "{:x}", black_box(1_i8 << 4)).unwrap(); + black_box(&mut buf).clear(); + }); +} + +#[bench] +fn write_i16_hex(bh: &mut Bencher) { + let mut buf = String::with_capacity(256); + bh.iter(|| { + write!(black_box(&mut buf), "{:x}", black_box(0_i16)).unwrap(); + write!(black_box(&mut buf), "{:x}", black_box(100_i16)).unwrap(); + write!(black_box(&mut buf), "{:x}", black_box(-100_i16)).unwrap(); + write!(black_box(&mut buf), "{:x}", black_box(1_i16 << 8)).unwrap(); + black_box(&mut buf).clear(); + }); +} + +#[bench] +fn write_i32_hex(bh: &mut Bencher) { + let mut buf = String::with_capacity(256); + bh.iter(|| { + write!(black_box(&mut buf), "{:x}", black_box(0_i32)).unwrap(); + write!(black_box(&mut buf), "{:x}", black_box(100_i32)).unwrap(); + write!(black_box(&mut buf), "{:x}", black_box(-100_i32)).unwrap(); + write!(black_box(&mut buf), "{:x}", black_box(1_i32 << 16)).unwrap(); + black_box(&mut buf).clear(); + }); +} + +#[bench] +fn write_i64_hex(bh: &mut Bencher) { + let mut buf = String::with_capacity(256); + bh.iter(|| { + write!(black_box(&mut buf), "{:x}", black_box(0_i64)).unwrap(); + write!(black_box(&mut buf), "{:x}", black_box(100_i64)).unwrap(); + write!(black_box(&mut buf), "{:x}", black_box(-100_i64)).unwrap(); + write!(black_box(&mut buf), "{:x}", black_box(1_i64 << 32)).unwrap(); + black_box(&mut buf).clear(); + }); +} + +#[bench] +fn write_i128_hex(bh: &mut Bencher) { + let mut buf = String::with_capacity(256); + bh.iter(|| { + write!(black_box(&mut buf), "{:x}", black_box(0_i128)).unwrap(); + write!(black_box(&mut buf), "{:x}", black_box(100_i128)).unwrap(); + write!(black_box(&mut buf), "{:x}", black_box(-100_i128)).unwrap(); + write!(black_box(&mut buf), "{:x}", black_box(1_i128 << 64)).unwrap(); + black_box(&mut buf).clear(); + }); +} -- cgit 1.4.1-3-g733a5 From a6f266612a42737ee46b69645244975e75406b60 Mon Sep 17 00:00:00 2001 From: morinmorin Date: Fri, 25 Jul 2025 23:22:36 +0900 Subject: std/sys/fd: remove `- 1` from `READ_LIMIT` on Darwin Darwin's `read`/`write` syscalls emit `EINVAL` only when `nbyte > INT_MAX`. The case `nbyte == INT_MAX` is valid, so the subtraction can be removed. --- library/std/src/sys/fd/unix.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/library/std/src/sys/fd/unix.rs b/library/std/src/sys/fd/unix.rs index cdca73cdca1..a12f692e754 100644 --- a/library/std/src/sys/fd/unix.rs +++ b/library/std/src/sys/fd/unix.rs @@ -37,10 +37,10 @@ pub struct FileDesc(OwnedFd); // // On Apple targets however, apparently the 64-bit libc is either buggy or // intentionally showing odd behavior by rejecting any read with a size -// larger than or equal to INT_MAX. To handle both of these the read -// size is capped on both platforms. +// larger than INT_MAX. To handle both of these the read size is capped on +// both platforms. const READ_LIMIT: usize = if cfg!(target_vendor = "apple") { - libc::c_int::MAX as usize - 1 + libc::c_int::MAX as usize } else { libc::ssize_t::MAX as usize }; -- cgit 1.4.1-3-g733a5 From 7ed763b2f9e7fd3cba54ab935906afce8b1b7404 Mon Sep 17 00:00:00 2001 From: Samuel Tardieu Date: Sat, 26 Jul 2025 19:36:01 +0200 Subject: Do not replace `match` by `if` if any arm contains a binding --- clippy_lints/src/matches/match_bool.rs | 6 +++++- tests/ui/match_bool.fixed | 13 +++++++++++++ tests/ui/match_bool.rs | 13 +++++++++++++ 3 files changed, 31 insertions(+), 1 deletion(-) diff --git a/clippy_lints/src/matches/match_bool.rs b/clippy_lints/src/matches/match_bool.rs index b90cf6357c5..a2c8741f4f7 100644 --- a/clippy_lints/src/matches/match_bool.rs +++ b/clippy_lints/src/matches/match_bool.rs @@ -12,7 +12,11 @@ use super::MATCH_BOOL; pub(crate) fn check(cx: &LateContext<'_>, scrutinee: &Expr<'_>, arms: &[Arm<'_>], expr: &Expr<'_>) { // Type of expression is `bool`. - if *cx.typeck_results().expr_ty(scrutinee).kind() == ty::Bool { + if *cx.typeck_results().expr_ty(scrutinee).kind() == ty::Bool + && arms + .iter() + .all(|arm| arm.pat.walk_short(|p| !matches!(p.kind, PatKind::Binding(..)))) + { span_lint_and_then( cx, MATCH_BOOL, diff --git a/tests/ui/match_bool.fixed b/tests/ui/match_bool.fixed index 1dfb82db120..876ae935afd 100644 --- a/tests/ui/match_bool.fixed +++ b/tests/ui/match_bool.fixed @@ -61,4 +61,17 @@ fn issue14099() { } } } +fn issue15351() { + let mut d = false; + match d { + false => println!("foo"), + ref mut d => *d = false, + } + + match d { + false => println!("foo"), + e => println!("{e}"), + } +} + fn main() {} diff --git a/tests/ui/match_bool.rs b/tests/ui/match_bool.rs index 719b4e51eb6..a134ad8346e 100644 --- a/tests/ui/match_bool.rs +++ b/tests/ui/match_bool.rs @@ -113,4 +113,17 @@ fn issue14099() { } } +fn issue15351() { + let mut d = false; + match d { + false => println!("foo"), + ref mut d => *d = false, + } + + match d { + false => println!("foo"), + e => println!("{e}"), + } +} + fn main() {} -- cgit 1.4.1-3-g733a5 From 7ead5764a88d91803db898e679dd49f2520cdae6 Mon Sep 17 00:00:00 2001 From: Scott McMurray Date: Sat, 26 Jul 2025 13:22:24 -0700 Subject: Implement `ptr_cast_array` --- library/core/src/ptr/const_ptr.rs | 9 +++++++++ library/core/src/ptr/mut_ptr.rs | 9 +++++++++ library/core/src/ptr/non_null.rs | 7 +++++++ 3 files changed, 25 insertions(+) diff --git a/library/core/src/ptr/const_ptr.rs b/library/core/src/ptr/const_ptr.rs index 2ad520b7ead..90d08f60a04 100644 --- a/library/core/src/ptr/const_ptr.rs +++ b/library/core/src/ptr/const_ptr.rs @@ -1547,6 +1547,15 @@ impl *const [T] { } } +impl *const T { + /// Casts from a pointer-to-`T` to a pointer-to-`[T; N]`. + #[inline] + #[unstable(feature = "ptr_cast_array", issue = "144514")] + pub const fn cast_array(self) -> *const [T; N] { + self.cast() + } +} + impl *const [T; N] { /// Returns a raw pointer to the array's buffer. /// diff --git a/library/core/src/ptr/mut_ptr.rs b/library/core/src/ptr/mut_ptr.rs index 579e2461103..dec212d04c9 100644 --- a/library/core/src/ptr/mut_ptr.rs +++ b/library/core/src/ptr/mut_ptr.rs @@ -1965,6 +1965,15 @@ impl *mut [T] { } } +impl *mut T { + /// Casts from a pointer-to-`T` to a pointer-to-`[T; N]`. + #[inline] + #[unstable(feature = "ptr_cast_array", issue = "144514")] + pub const fn cast_array(self) -> *mut [T; N] { + self.cast() + } +} + impl *mut [T; N] { /// Returns a raw pointer to the array's buffer. /// diff --git a/library/core/src/ptr/non_null.rs b/library/core/src/ptr/non_null.rs index 62da6567cca..245a0a08321 100644 --- a/library/core/src/ptr/non_null.rs +++ b/library/core/src/ptr/non_null.rs @@ -193,6 +193,13 @@ impl NonNull { // requirements for a reference. unsafe { &mut *self.cast().as_ptr() } } + + /// Casts from a pointer-to-`T` to a pointer-to-`[T; N]`. + #[inline] + #[unstable(feature = "ptr_cast_array", issue = "144514")] + pub const fn cast_array(self) -> NonNull<[T; N]> { + self.cast() + } } impl NonNull { -- cgit 1.4.1-3-g733a5 From 51b0416c19f436393b3c070cc037a8f062bcbd83 Mon Sep 17 00:00:00 2001 From: Scott McMurray Date: Sat, 26 Jul 2025 13:26:53 -0700 Subject: Use `cast_array` in core --- library/core/src/slice/mod.rs | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/library/core/src/slice/mod.rs b/library/core/src/slice/mod.rs index 6fe5affc48b..13e182b5f87 100644 --- a/library/core/src/slice/mod.rs +++ b/library/core/src/slice/mod.rs @@ -330,7 +330,7 @@ impl [T] { } else { // SAFETY: We explicitly check for the correct number of elements, // and do not let the reference outlive the slice. - Some(unsafe { &*(self.as_ptr().cast::<[T; N]>()) }) + Some(unsafe { &*(self.as_ptr().cast_array()) }) } } @@ -361,7 +361,7 @@ impl [T] { // SAFETY: We explicitly check for the correct number of elements, // do not let the reference outlive the slice, // and require exclusive access to the entire slice to mutate the chunk. - Some(unsafe { &mut *(self.as_mut_ptr().cast::<[T; N]>()) }) + Some(unsafe { &mut *(self.as_mut_ptr().cast_array()) }) } } @@ -389,7 +389,7 @@ impl [T] { // SAFETY: We explicitly check for the correct number of elements, // and do not let the references outlive the slice. - Some((unsafe { &*(first.as_ptr().cast::<[T; N]>()) }, tail)) + Some((unsafe { &*(first.as_ptr().cast_array()) }, tail)) } /// Returns a mutable array reference to the first `N` items in the slice and the remaining @@ -422,7 +422,7 @@ impl [T] { // SAFETY: We explicitly check for the correct number of elements, // do not let the reference outlive the slice, // and enforce exclusive mutability of the chunk by the split. - Some((unsafe { &mut *(first.as_mut_ptr().cast::<[T; N]>()) }, tail)) + Some((unsafe { &mut *(first.as_mut_ptr().cast_array()) }, tail)) } /// Returns an array reference to the last `N` items in the slice and the remaining slice. @@ -450,7 +450,7 @@ impl [T] { // SAFETY: We explicitly check for the correct number of elements, // and do not let the references outlive the slice. - Some((init, unsafe { &*(last.as_ptr().cast::<[T; N]>()) })) + Some((init, unsafe { &*(last.as_ptr().cast_array()) })) } /// Returns a mutable array reference to the last `N` items in the slice and the remaining @@ -484,7 +484,7 @@ impl [T] { // SAFETY: We explicitly check for the correct number of elements, // do not let the reference outlive the slice, // and enforce exclusive mutability of the chunk by the split. - Some((init, unsafe { &mut *(last.as_mut_ptr().cast::<[T; N]>()) })) + Some((init, unsafe { &mut *(last.as_mut_ptr().cast_array()) })) } /// Returns an array reference to the last `N` items in the slice. @@ -513,7 +513,7 @@ impl [T] { // SAFETY: We explicitly check for the correct number of elements, // and do not let the references outlive the slice. - Some(unsafe { &*(last.as_ptr().cast::<[T; N]>()) }) + Some(unsafe { &*(last.as_ptr().cast_array()) }) } /// Returns a mutable array reference to the last `N` items in the slice. @@ -544,7 +544,7 @@ impl [T] { // SAFETY: We explicitly check for the correct number of elements, // do not let the reference outlive the slice, // and require exclusive access to the entire slice to mutate the chunk. - Some(unsafe { &mut *(last.as_mut_ptr().cast::<[T; N]>()) }) + Some(unsafe { &mut *(last.as_mut_ptr().cast_array()) }) } /// Returns a reference to an element or subslice depending on the type of @@ -848,7 +848,7 @@ impl [T] { #[must_use] pub const fn as_array(&self) -> Option<&[T; N]> { if self.len() == N { - let ptr = self.as_ptr() as *const [T; N]; + let ptr = self.as_ptr().cast_array(); // SAFETY: The underlying array of a slice can be reinterpreted as an actual array `[T; N]` if `N` is not greater than the slice's length. let me = unsafe { &*ptr }; @@ -866,7 +866,7 @@ impl [T] { #[must_use] pub const fn as_mut_array(&mut self) -> Option<&mut [T; N]> { if self.len() == N { - let ptr = self.as_mut_ptr() as *mut [T; N]; + let ptr = self.as_mut_ptr().cast_array(); // SAFETY: The underlying array of a slice can be reinterpreted as an actual array `[T; N]` if `N` is not greater than the slice's length. let me = unsafe { &mut *ptr }; -- cgit 1.4.1-3-g733a5 From ca58013caab2ce7855468177df579bb99ac49fdb Mon Sep 17 00:00:00 2001 From: Philip Woolford Date: Tue, 29 Jul 2025 09:48:59 +0930 Subject: Enable features that are always available in a live system. While some neon and crypto features may not be supported on the switch at boot (e.g. on the a53 cores), the features will _always_ be available if running as a sysmodule or homebrew application under Horizon/Atmosphere. --- .../src/spec/targets/aarch64_nintendo_switch_freestanding.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/rustc_target/src/spec/targets/aarch64_nintendo_switch_freestanding.rs b/compiler/rustc_target/src/spec/targets/aarch64_nintendo_switch_freestanding.rs index 9b81362b27d..8d5390f5771 100644 --- a/compiler/rustc_target/src/spec/targets/aarch64_nintendo_switch_freestanding.rs +++ b/compiler/rustc_target/src/spec/targets/aarch64_nintendo_switch_freestanding.rs @@ -19,7 +19,7 @@ pub(crate) fn target() -> Target { data_layout: "e-m:e-p270:32:32-p271:32:32-p272:64:64-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128-Fn32".into(), arch: "aarch64".into(), options: TargetOptions { - features: "+v8a".into(), + features: "+v8a,+neon,+crypto".into(), linker_flavor: LinkerFlavor::Gnu(Cc::No, Lld::Yes), linker: Some("rust-lld".into()), link_script: Some(LINKER_SCRIPT.into()), -- cgit 1.4.1-3-g733a5 From 05cd6e854b09ce803b72443f94f3e58e71c24db6 Mon Sep 17 00:00:00 2001 From: Philip Woolford Date: Tue, 29 Jul 2025 11:01:22 +0930 Subject: Add the CRC instructions. --- .../src/spec/targets/aarch64_nintendo_switch_freestanding.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/rustc_target/src/spec/targets/aarch64_nintendo_switch_freestanding.rs b/compiler/rustc_target/src/spec/targets/aarch64_nintendo_switch_freestanding.rs index 8d5390f5771..61e4cad3fa2 100644 --- a/compiler/rustc_target/src/spec/targets/aarch64_nintendo_switch_freestanding.rs +++ b/compiler/rustc_target/src/spec/targets/aarch64_nintendo_switch_freestanding.rs @@ -19,7 +19,7 @@ pub(crate) fn target() -> Target { data_layout: "e-m:e-p270:32:32-p271:32:32-p272:64:64-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128-Fn32".into(), arch: "aarch64".into(), options: TargetOptions { - features: "+v8a,+neon,+crypto".into(), + features: "+v8a,+neon,+crypto,+crc".into(), linker_flavor: LinkerFlavor::Gnu(Cc::No, Lld::Yes), linker: Some("rust-lld".into()), link_script: Some(LINKER_SCRIPT.into()), -- cgit 1.4.1-3-g733a5 From c72bb70ffd284538519242663fa9443037f4d41e Mon Sep 17 00:00:00 2001 From: gewitternacht <60887951+gewitternacht@users.noreply.github.com> Date: Tue, 29 Jul 2025 08:39:28 +0200 Subject: clearer wording for `unsafe` code --- library/core/src/clone.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/library/core/src/clone.rs b/library/core/src/clone.rs index c4778edf0fc..6fcba298a43 100644 --- a/library/core/src/clone.rs +++ b/library/core/src/clone.rs @@ -160,8 +160,8 @@ mod uninit; /// /// Violating this property is a logic error. The behavior resulting from a logic error is not /// specified, but users of the trait must ensure that such logic errors do *not* result in -/// undefined behavior. This means that `unsafe` code **must not** rely on the correctness of these -/// methods. +/// undefined behavior. This means that `unsafe` code **must not** rely on this property +/// being satisfied. /// /// ## Additional implementors /// -- cgit 1.4.1-3-g733a5 From 0ed3ef4ea48c7587a164572a7bda3290beb3a81f Mon Sep 17 00:00:00 2001 From: Jakub Beránek Date: Tue, 29 Jul 2025 10:16:28 +0200 Subject: Use GH app for authenticating pull PRs --- src/tools/miri/.github/workflows/ci.yml | 7 ++++++- src/tools/miri/triagebot.toml | 3 --- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/tools/miri/.github/workflows/ci.yml b/src/tools/miri/.github/workflows/ci.yml index c47f9695624..668caef8f7f 100644 --- a/src/tools/miri/.github/workflows/ci.yml +++ b/src/tools/miri/.github/workflows/ci.yml @@ -169,6 +169,11 @@ jobs: run: rustup toolchain install nightly --profile minimal - name: Install rustup-toolchain-install-master run: cargo install -f rustup-toolchain-install-master + - uses: actions/create-github-app-token@v2 + id: app-token + with: + app-id: ${{ vars.APP_CLIENT_ID }} + private-key: ${{ secrets.APP_PRIVATE_KEY }} - name: Push changes to a branch and create PR run: | # Make it easier to see what happens. @@ -200,7 +205,7 @@ jobs: git push -u origin $BRANCH gh pr create -B master --title 'Automatic Rustup' --body 'Please close and re-open this PR to trigger CI, then enable auto-merge.' env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GITHUB_TOKEN: ${{ steps.app-token.outputs.token }} cron-fail-notify: name: cronjob failure notification diff --git a/src/tools/miri/triagebot.toml b/src/tools/miri/triagebot.toml index a0ce9f80024..5d6b596ea54 100644 --- a/src/tools/miri/triagebot.toml +++ b/src/tools/miri/triagebot.toml @@ -50,9 +50,6 @@ new_pr = true [autolabel."S-waiting-on-author"] new_draft = true -# Automatically close and reopen PRs made by bots to run CI on them -[bot-pull-requests] - # Canonicalize issue numbers to avoid closing the wrong issue when upstreaming this subtree [canonicalize-issue-links] -- cgit 1.4.1-3-g733a5 From 1fd183ea920bbe20718a4c45315fe2b30bbf4b2d Mon Sep 17 00:00:00 2001 From: Jakub Beránek Date: Tue, 29 Jul 2025 12:28:46 +0200 Subject: Apply suggestions from code review Co-authored-by: Ralf Jung --- src/tools/miri/.github/workflows/ci.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/tools/miri/.github/workflows/ci.yml b/src/tools/miri/.github/workflows/ci.yml index 668caef8f7f..ba496d76712 100644 --- a/src/tools/miri/.github/workflows/ci.yml +++ b/src/tools/miri/.github/workflows/ci.yml @@ -169,12 +169,13 @@ jobs: run: rustup toolchain install nightly --profile minimal - name: Install rustup-toolchain-install-master run: cargo install -f rustup-toolchain-install-master + # Create a token for the next step so it can create a PR that actually runs CI. - uses: actions/create-github-app-token@v2 id: app-token with: app-id: ${{ vars.APP_CLIENT_ID }} private-key: ${{ secrets.APP_PRIVATE_KEY }} - - name: Push changes to a branch and create PR + - name: pull changes from rustc and create PR run: | # Make it easier to see what happens. set -x -- cgit 1.4.1-3-g733a5 From ba5b6b9ec472dc32bdaa8b18c22d30bd6abf7ebc Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Thu, 17 Jul 2025 20:00:19 +0200 Subject: const-eval: full support for pointer fragments --- compiler/rustc_const_eval/messages.ftl | 11 +- .../src/const_eval/eval_queries.rs | 7 + compiler/rustc_const_eval/src/errors.rs | 16 ++- compiler/rustc_const_eval/src/interpret/intern.rs | 97 ++++++++------ compiler/rustc_const_eval/src/interpret/memory.rs | 44 ++----- compiler/rustc_const_eval/src/interpret/place.rs | 8 +- .../rustc_const_eval/src/interpret/validity.rs | 4 +- .../rustc_middle/src/mir/interpret/allocation.rs | 84 +++++++----- .../src/mir/interpret/allocation/provenance_map.rs | 142 ++++++++++----------- compiler/rustc_middle/src/mir/interpret/error.rs | 3 - compiler/rustc_middle/src/mir/interpret/pointer.rs | 12 +- compiler/rustc_middle/src/mir/pretty.rs | 4 +- library/core/src/ptr/mod.rs | 38 +----- library/coretests/tests/ptr.rs | 12 +- src/tools/miri/src/machine.rs | 10 +- src/tools/miri/src/shims/native_lib/mod.rs | 2 +- src/tools/miri/tests/fail/provenance/mix-ptrs1.rs | 21 +++ .../miri/tests/fail/provenance/mix-ptrs1.stderr | 16 +++ src/tools/miri/tests/fail/provenance/mix-ptrs2.rs | 37 ++++++ .../miri/tests/fail/provenance/mix-ptrs2.stderr | 16 +++ .../uninit_alloc_diagnostic_with_provenance.rs | 11 +- .../uninit_alloc_diagnostic_with_provenance.stderr | 2 +- .../native-lib/fail/tracing/partial_init.stderr | 2 +- src/tools/miri/tests/pass/provenance.rs | 35 ----- src/tools/miri/tests/pass/transmute_ptr.rs | 21 +-- .../const-eval/heap/ptr_not_made_global.stderr | 4 +- .../const-eval/heap/ptr_not_made_global_mut.stderr | 2 +- .../ui/consts/const-eval/partial_ptr_overwrite.rs | 12 -- .../consts/const-eval/partial_ptr_overwrite.stderr | 12 -- tests/ui/consts/const-eval/ptr_fragments.rs | 63 +++++++++ .../ui/consts/const-eval/ptr_fragments_in_final.rs | 25 ++++ .../const-eval/ptr_fragments_in_final.stderr | 10 ++ tests/ui/consts/const-eval/read_partial_ptr.rs | 49 +++++++ tests/ui/consts/const-eval/read_partial_ptr.stderr | 39 ++++++ tests/ui/consts/missing_span_in_backtrace.rs | 13 +- tests/ui/consts/missing_span_in_backtrace.stderr | 16 +-- 36 files changed, 542 insertions(+), 358 deletions(-) create mode 100644 src/tools/miri/tests/fail/provenance/mix-ptrs1.rs create mode 100644 src/tools/miri/tests/fail/provenance/mix-ptrs1.stderr create mode 100644 src/tools/miri/tests/fail/provenance/mix-ptrs2.rs create mode 100644 src/tools/miri/tests/fail/provenance/mix-ptrs2.stderr delete mode 100644 tests/ui/consts/const-eval/partial_ptr_overwrite.rs delete mode 100644 tests/ui/consts/const-eval/partial_ptr_overwrite.stderr create mode 100644 tests/ui/consts/const-eval/ptr_fragments.rs create mode 100644 tests/ui/consts/const-eval/ptr_fragments_in_final.rs create mode 100644 tests/ui/consts/const-eval/ptr_fragments_in_final.stderr create mode 100644 tests/ui/consts/const-eval/read_partial_ptr.rs create mode 100644 tests/ui/consts/const-eval/read_partial_ptr.stderr diff --git a/compiler/rustc_const_eval/messages.ftl b/compiler/rustc_const_eval/messages.ftl index aa0bc42d448..10cdc372ddd 100644 --- a/compiler/rustc_const_eval/messages.ftl +++ b/compiler/rustc_const_eval/messages.ftl @@ -57,7 +57,7 @@ const_eval_const_context = {$kind -> } const_eval_const_heap_ptr_in_final = encountered `const_allocate` pointer in final value that was not made global - .note = use `const_make_global` to make allocated pointers immutable before returning + .note = use `const_make_global` to turn allocated pointers into immutable globals before returning const_eval_const_make_global_ptr_already_made_global = attempting to call `const_make_global` twice on the same allocation {$alloc} @@ -231,6 +231,9 @@ const_eval_mutable_borrow_escaping = const_eval_mutable_ptr_in_final = encountered mutable pointer in final value of {const_eval_intern_kind} +const_eval_partial_pointer_in_final = encountered partial pointer in final value of {const_eval_intern_kind} + .note = while pointers can be broken apart into individual bytes during const-evaluation, only complete pointers (with all their bytes in the right order) are supported in the final value + const_eval_nested_static_in_thread_local = #[thread_local] does not support implicit nested statics, please create explicit static items and refer to them instead const_eval_non_const_await = @@ -299,10 +302,8 @@ const_eval_panic = evaluation panicked: {$msg} const_eval_panic_non_str = argument to `panic!()` in a const context must have type `&str` -const_eval_partial_pointer_copy = - unable to copy parts of a pointer from memory at {$ptr} -const_eval_partial_pointer_overwrite = - unable to overwrite parts of a pointer in memory at {$ptr} +const_eval_partial_pointer_read = + unable to read parts of a pointer from memory at {$ptr} const_eval_pointer_arithmetic_overflow = overflowing pointer arithmetic: the total offset in bytes does not fit in an `isize` diff --git a/compiler/rustc_const_eval/src/const_eval/eval_queries.rs b/compiler/rustc_const_eval/src/const_eval/eval_queries.rs index 5835660e1c3..4da2663319c 100644 --- a/compiler/rustc_const_eval/src/const_eval/eval_queries.rs +++ b/compiler/rustc_const_eval/src/const_eval/eval_queries.rs @@ -117,6 +117,13 @@ fn eval_body_using_ecx<'tcx, R: InterpretationResult<'tcx>>( ecx.tcx.dcx().emit_err(errors::ConstHeapPtrInFinal { span: ecx.tcx.span }), ))); } + Err(InternError::PartialPointer) => { + throw_inval!(AlreadyReported(ReportedErrorInfo::non_const_eval_error( + ecx.tcx + .dcx() + .emit_err(errors::PartialPtrInFinal { span: ecx.tcx.span, kind: intern_kind }), + ))); + } } interp_ok(R::make_result(ret, ecx)) diff --git a/compiler/rustc_const_eval/src/errors.rs b/compiler/rustc_const_eval/src/errors.rs index b6a64035261..322fd8e9363 100644 --- a/compiler/rustc_const_eval/src/errors.rs +++ b/compiler/rustc_const_eval/src/errors.rs @@ -51,6 +51,15 @@ pub(crate) struct ConstHeapPtrInFinal { pub span: Span, } +#[derive(Diagnostic)] +#[diag(const_eval_partial_pointer_in_final)] +#[note] +pub(crate) struct PartialPtrInFinal { + #[primary_span] + pub span: Span, + pub kind: InternKind, +} + #[derive(Diagnostic)] #[diag(const_eval_unstable_in_stable_exposed)] pub(crate) struct UnstableInStableExposed { @@ -832,8 +841,7 @@ impl ReportErrorExt for UnsupportedOpInfo { UnsupportedOpInfo::Unsupported(s) => s.clone().into(), UnsupportedOpInfo::ExternTypeField => const_eval_extern_type_field, UnsupportedOpInfo::UnsizedLocal => const_eval_unsized_local, - UnsupportedOpInfo::OverwritePartialPointer(_) => const_eval_partial_pointer_overwrite, - UnsupportedOpInfo::ReadPartialPointer(_) => const_eval_partial_pointer_copy, + UnsupportedOpInfo::ReadPartialPointer(_) => const_eval_partial_pointer_read, UnsupportedOpInfo::ReadPointerAsInt(_) => const_eval_read_pointer_as_int, UnsupportedOpInfo::ThreadLocalStatic(_) => const_eval_thread_local_static, UnsupportedOpInfo::ExternStatic(_) => const_eval_extern_static, @@ -844,7 +852,7 @@ impl ReportErrorExt for UnsupportedOpInfo { use UnsupportedOpInfo::*; use crate::fluent_generated::*; - if let ReadPointerAsInt(_) | OverwritePartialPointer(_) | ReadPartialPointer(_) = self { + if let ReadPointerAsInt(_) | ReadPartialPointer(_) = self { diag.help(const_eval_ptr_as_bytes_1); diag.help(const_eval_ptr_as_bytes_2); } @@ -856,7 +864,7 @@ impl ReportErrorExt for UnsupportedOpInfo { | UnsupportedOpInfo::ExternTypeField | Unsupported(_) | ReadPointerAsInt(_) => {} - OverwritePartialPointer(ptr) | ReadPartialPointer(ptr) => { + ReadPartialPointer(ptr) => { diag.arg("ptr", ptr); } ThreadLocalStatic(did) | ExternStatic(did) => rustc_middle::ty::tls::with(|tcx| { diff --git a/compiler/rustc_const_eval/src/interpret/intern.rs b/compiler/rustc_const_eval/src/interpret/intern.rs index bb59b9f5418..5d510a98352 100644 --- a/compiler/rustc_const_eval/src/interpret/intern.rs +++ b/compiler/rustc_const_eval/src/interpret/intern.rs @@ -19,9 +19,12 @@ use rustc_data_structures::fx::{FxHashSet, FxIndexMap}; use rustc_hir as hir; use rustc_hir::definitions::{DefPathData, DisambiguatorState}; use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrs; -use rustc_middle::mir::interpret::{ConstAllocation, CtfeProvenance, InterpResult}; +use rustc_middle::mir::interpret::{ + AllocBytes, ConstAllocation, CtfeProvenance, InterpResult, Provenance, +}; use rustc_middle::query::TyCtxtAt; use rustc_middle::span_bug; +use rustc_middle::ty::TyCtxt; use rustc_middle::ty::layout::TyAndLayout; use rustc_span::def_id::LocalDefId; use tracing::{instrument, trace}; @@ -52,39 +55,30 @@ impl HasStaticRootDefId for const_eval::CompileTimeMachine<'_> { } } -/// Intern an allocation. Returns `Err` if the allocation does not exist in the local memory. -/// -/// `mutability` can be used to force immutable interning: if it is `Mutability::Not`, the -/// allocation is interned immutably; if it is `Mutability::Mut`, then the allocation *must be* -/// already mutable (as a sanity check). -/// -/// Returns an iterator over all relocations referred to by this allocation. -fn intern_shallow<'tcx, M: CompileTimeMachine<'tcx>>( - ecx: &mut InterpCx<'tcx, M>, - alloc_id: AllocId, +fn prepare_alloc<'tcx, Prov: Provenance, Extra, Bytes: AllocBytes>( + tcx: TyCtxt<'tcx>, + kind: MemoryKind, + alloc: &mut Allocation, mutability: Mutability, - disambiguator: Option<&mut DisambiguatorState>, -) -> Result + 'tcx, InternError> { - trace!("intern_shallow {:?}", alloc_id); - // remove allocation - // FIXME(#120456) - is `swap_remove` correct? - let Some((kind, mut alloc)) = ecx.memory.alloc_map.swap_remove(&alloc_id) else { - return Err(InternError::DanglingPointer); - }; - +) -> Result<(), InternError> { match kind { MemoryKind::Machine(const_eval::MemoryKind::Heap { was_made_global }) => { if !was_made_global { // Attempting to intern a `const_allocate`d pointer that was not made global via - // `const_make_global`. We want to error here, but we have to first put the - // allocation back into the `alloc_map` to keep things in a consistent state. - ecx.memory.alloc_map.insert(alloc_id, (kind, alloc)); + // `const_make_global`. + tcx.dcx().delayed_bug("non-global heap allocation in const value"); return Err(InternError::ConstAllocNotGlobal); } } MemoryKind::Stack | MemoryKind::CallerLocation => {} } + if !alloc.provenance_merge_bytes(&tcx) { + // Per-byte provenance is not supported by backends, so we cannot accept it here. + tcx.dcx().delayed_bug("partial pointer in const value"); + return Err(InternError::PartialPointer); + } + // Set allocation mutability as appropriate. This is used by LLVM to put things into // read-only memory, and also by Miri when evaluating other globals that // access this one. @@ -97,6 +91,36 @@ fn intern_shallow<'tcx, M: CompileTimeMachine<'tcx>>( assert_eq!(alloc.mutability, Mutability::Mut); } } + Ok(()) +} + +/// Intern an allocation. Returns `Err` if the allocation does not exist in the local memory. +/// +/// `mutability` can be used to force immutable interning: if it is `Mutability::Not`, the +/// allocation is interned immutably; if it is `Mutability::Mut`, then the allocation *must be* +/// already mutable (as a sanity check). +/// +/// Returns an iterator over all relocations referred to by this allocation. +fn intern_shallow<'tcx, M: CompileTimeMachine<'tcx>>( + ecx: &mut InterpCx<'tcx, M>, + alloc_id: AllocId, + mutability: Mutability, + disambiguator: Option<&mut DisambiguatorState>, +) -> Result + 'tcx, InternError> { + trace!("intern_shallow {:?}", alloc_id); + // remove allocation + // FIXME(#120456) - is `swap_remove` correct? + let Some((kind, mut alloc)) = ecx.memory.alloc_map.swap_remove(&alloc_id) else { + return Err(InternError::DanglingPointer); + }; + + if let Err(err) = prepare_alloc(*ecx.tcx, kind, &mut alloc, mutability) { + // We want to error here, but we have to first put the + // allocation back into the `alloc_map` to keep things in a consistent state. + ecx.memory.alloc_map.insert(alloc_id, (kind, alloc)); + return Err(err); + } + // link the alloc id to the actual allocation let alloc = ecx.tcx.mk_const_alloc(alloc); if let Some(static_id) = ecx.machine.static_def_id() { @@ -166,6 +190,7 @@ pub enum InternError { BadMutablePointer, DanglingPointer, ConstAllocNotGlobal, + PartialPointer, } /// Intern `ret` and everything it references. @@ -221,13 +246,11 @@ pub fn intern_const_alloc_recursive<'tcx, M: CompileTimeMachine<'tcx>>( let mut todo: Vec<_> = if is_static { // Do not steal the root allocation, we need it later to create the return value of `eval_static_initializer`. // But still change its mutability to match the requested one. - let alloc = ecx.memory.alloc_map.get_mut(&base_alloc_id).unwrap(); - alloc.1.mutability = base_mutability; - alloc.1.provenance().ptrs().iter().map(|&(_, prov)| prov).collect() + let (kind, alloc) = ecx.memory.alloc_map.get_mut(&base_alloc_id).unwrap(); + prepare_alloc(*ecx.tcx, *kind, alloc, base_mutability)?; + alloc.provenance().ptrs().iter().map(|&(_, prov)| prov).collect() } else { - intern_shallow(ecx, base_alloc_id, base_mutability, Some(&mut disambiguator)) - .unwrap() - .collect() + intern_shallow(ecx, base_alloc_id, base_mutability, Some(&mut disambiguator))?.collect() }; // We need to distinguish "has just been interned" from "was already in `tcx`", // so we track this in a separate set. @@ -235,7 +258,6 @@ pub fn intern_const_alloc_recursive<'tcx, M: CompileTimeMachine<'tcx>>( // Whether we encountered a bad mutable pointer. // We want to first report "dangling" and then "mutable", so we need to delay reporting these // errors. - let mut result = Ok(()); let mut found_bad_mutable_ptr = false; // Keep interning as long as there are things to intern. @@ -310,20 +332,15 @@ pub fn intern_const_alloc_recursive<'tcx, M: CompileTimeMachine<'tcx>>( // okay with losing some potential for immutability here. This can anyway only affect // `static mut`. just_interned.insert(alloc_id); - match intern_shallow(ecx, alloc_id, inner_mutability, Some(&mut disambiguator)) { - Ok(nested) => todo.extend(nested), - Err(err) => { - ecx.tcx.dcx().delayed_bug("error during const interning"); - result = Err(err); - } - } + let next = intern_shallow(ecx, alloc_id, inner_mutability, Some(&mut disambiguator))?; + todo.extend(next); } - if found_bad_mutable_ptr && result.is_ok() { + if found_bad_mutable_ptr { // We found a mutable pointer inside a const where inner allocations should be immutable, // and there was no other error. This should usually never happen! However, this can happen // in unleash-miri mode, so report it as a normal error then. if ecx.tcx.sess.opts.unstable_opts.unleash_the_miri_inside_of_you { - result = Err(InternError::BadMutablePointer); + return Err(InternError::BadMutablePointer); } else { span_bug!( ecx.tcx.span, @@ -331,7 +348,7 @@ pub fn intern_const_alloc_recursive<'tcx, M: CompileTimeMachine<'tcx>>( ); } } - result + Ok(()) } /// Intern `ret`. This function assumes that `ret` references no other allocation. diff --git a/compiler/rustc_const_eval/src/interpret/memory.rs b/compiler/rustc_const_eval/src/interpret/memory.rs index 47bebf5371a..b5703e789ff 100644 --- a/compiler/rustc_const_eval/src/interpret/memory.rs +++ b/compiler/rustc_const_eval/src/interpret/memory.rs @@ -1310,29 +1310,20 @@ impl<'a, 'tcx, Prov: Provenance, Extra, Bytes: AllocBytes> } /// Mark the given sub-range (relative to this allocation reference) as uninitialized. - pub fn write_uninit(&mut self, range: AllocRange) -> InterpResult<'tcx> { + pub fn write_uninit(&mut self, range: AllocRange) { let range = self.range.subrange(range); - self.alloc - .write_uninit(&self.tcx, range) - .map_err(|e| e.to_interp_error(self.alloc_id)) - .into() + self.alloc.write_uninit(&self.tcx, range); } /// Mark the entire referenced range as uninitialized - pub fn write_uninit_full(&mut self) -> InterpResult<'tcx> { - self.alloc - .write_uninit(&self.tcx, self.range) - .map_err(|e| e.to_interp_error(self.alloc_id)) - .into() + pub fn write_uninit_full(&mut self) { + self.alloc.write_uninit(&self.tcx, self.range); } /// Remove all provenance in the reference range. - pub fn clear_provenance(&mut self) -> InterpResult<'tcx> { - self.alloc - .clear_provenance(&self.tcx, self.range) - .map_err(|e| e.to_interp_error(self.alloc_id)) - .into() + pub fn clear_provenance(&mut self) { + self.alloc.clear_provenance(&self.tcx, self.range); } } @@ -1423,11 +1414,8 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { // Side-step AllocRef and directly access the underlying bytes more efficiently. // (We are staying inside the bounds here and all bytes do get overwritten so all is good.) - let alloc_id = alloc_ref.alloc_id; - let bytes = alloc_ref - .alloc - .get_bytes_unchecked_for_overwrite(&alloc_ref.tcx, alloc_ref.range) - .map_err(move |e| e.to_interp_error(alloc_id))?; + let bytes = + alloc_ref.alloc.get_bytes_unchecked_for_overwrite(&alloc_ref.tcx, alloc_ref.range); // `zip` would stop when the first iterator ends; we want to definitely // cover all of `bytes`. for dest in bytes { @@ -1509,10 +1497,8 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { // `get_bytes_mut` will clear the provenance, which is correct, // since we don't want to keep any provenance at the target. // This will also error if copying partial provenance is not supported. - let provenance = src_alloc - .provenance() - .prepare_copy(src_range, dest_offset, num_copies, self) - .map_err(|e| e.to_interp_error(src_alloc_id))?; + let provenance = + src_alloc.provenance().prepare_copy(src_range, dest_offset, num_copies, self); // Prepare a copy of the initialization mask. let init = src_alloc.init_mask().prepare_copy(src_range); @@ -1530,10 +1516,8 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { dest_range, )?; // Yes we do overwrite all bytes in `dest_bytes`. - let dest_bytes = dest_alloc - .get_bytes_unchecked_for_overwrite_ptr(&tcx, dest_range) - .map_err(|e| e.to_interp_error(dest_alloc_id))? - .as_mut_ptr(); + let dest_bytes = + dest_alloc.get_bytes_unchecked_for_overwrite_ptr(&tcx, dest_range).as_mut_ptr(); if init.no_bytes_init() { // Fast path: If all bytes are `uninit` then there is nothing to copy. The target range @@ -1542,9 +1526,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { // This also avoids writing to the target bytes so that the backing allocation is never // touched if the bytes stay uninitialized for the whole interpreter execution. On contemporary // operating system this can avoid physically allocating the page. - dest_alloc - .write_uninit(&tcx, dest_range) - .map_err(|e| e.to_interp_error(dest_alloc_id))?; + dest_alloc.write_uninit(&tcx, dest_range); // `write_uninit` also resets the provenance, so we are done. return interp_ok(()); } diff --git a/compiler/rustc_const_eval/src/interpret/place.rs b/compiler/rustc_const_eval/src/interpret/place.rs index e2284729efd..afd96a7c0c9 100644 --- a/compiler/rustc_const_eval/src/interpret/place.rs +++ b/compiler/rustc_const_eval/src/interpret/place.rs @@ -700,7 +700,7 @@ where match value { Immediate::Scalar(scalar) => { - alloc.write_scalar(alloc_range(Size::ZERO, scalar.size()), scalar) + alloc.write_scalar(alloc_range(Size::ZERO, scalar.size()), scalar)?; } Immediate::ScalarPair(a_val, b_val) => { let BackendRepr::ScalarPair(a, b) = layout.backend_repr else { @@ -720,10 +720,10 @@ where alloc.write_scalar(alloc_range(Size::ZERO, a_val.size()), a_val)?; alloc.write_scalar(alloc_range(b_offset, b_val.size()), b_val)?; // We don't have to reset padding here, `write_immediate` will anyway do a validation run. - interp_ok(()) } Immediate::Uninit => alloc.write_uninit_full(), } + interp_ok(()) } pub fn write_uninit( @@ -743,7 +743,7 @@ where // Zero-sized access return interp_ok(()); }; - alloc.write_uninit_full()?; + alloc.write_uninit_full(); } } interp_ok(()) @@ -767,7 +767,7 @@ where // Zero-sized access return interp_ok(()); }; - alloc.clear_provenance()?; + alloc.clear_provenance(); } } interp_ok(()) diff --git a/compiler/rustc_const_eval/src/interpret/validity.rs b/compiler/rustc_const_eval/src/interpret/validity.rs index 693b3782960..f8d9ebcafa7 100644 --- a/compiler/rustc_const_eval/src/interpret/validity.rs +++ b/compiler/rustc_const_eval/src/interpret/validity.rs @@ -949,7 +949,7 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValidityVisitor<'rt, 'tcx, M> { let padding_size = offset - padding_cleared_until; let range = alloc_range(padding_start, padding_size); trace!("reset_padding on {}: resetting padding range {range:?}", mplace.layout.ty); - alloc.write_uninit(range)?; + alloc.write_uninit(range); } padding_cleared_until = offset + size; } @@ -1239,7 +1239,7 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValueVisitor<'tcx, M> for ValidityVisitor<'rt, if self.reset_provenance_and_padding { // We can't share this with above as above, we might be looking at read-only memory. let mut alloc = self.ecx.get_ptr_alloc_mut(mplace.ptr(), size)?.expect("we already excluded size 0"); - alloc.clear_provenance()?; + alloc.clear_provenance(); // Also, mark this as containing data, not padding. self.add_data_range(mplace.ptr(), size); } diff --git a/compiler/rustc_middle/src/mir/interpret/allocation.rs b/compiler/rustc_middle/src/mir/interpret/allocation.rs index 27ead514531..2ea92a39d48 100644 --- a/compiler/rustc_middle/src/mir/interpret/allocation.rs +++ b/compiler/rustc_middle/src/mir/interpret/allocation.rs @@ -306,8 +306,6 @@ pub enum AllocError { ScalarSizeMismatch(ScalarSizeMismatch), /// Encountered a pointer where we needed raw bytes. ReadPointerAsInt(Option), - /// Partially overwriting a pointer. - OverwritePartialPointer(Size), /// Partially copying a pointer. ReadPartialPointer(Size), /// Using uninitialized data where it is not allowed. @@ -331,9 +329,6 @@ impl AllocError { ReadPointerAsInt(info) => InterpErrorKind::Unsupported( UnsupportedOpInfo::ReadPointerAsInt(info.map(|b| (alloc_id, b))), ), - OverwritePartialPointer(offset) => InterpErrorKind::Unsupported( - UnsupportedOpInfo::OverwritePartialPointer(Pointer::new(alloc_id, offset)), - ), ReadPartialPointer(offset) => InterpErrorKind::Unsupported( UnsupportedOpInfo::ReadPartialPointer(Pointer::new(alloc_id, offset)), ), @@ -633,11 +628,11 @@ impl Allocation &mut self, cx: &impl HasDataLayout, range: AllocRange, - ) -> AllocResult<&mut [u8]> { + ) -> &mut [u8] { self.mark_init(range, true); - self.provenance.clear(range, cx)?; + self.provenance.clear(range, cx); - Ok(&mut self.bytes[range.start.bytes_usize()..range.end().bytes_usize()]) + &mut self.bytes[range.start.bytes_usize()..range.end().bytes_usize()] } /// A raw pointer variant of `get_bytes_unchecked_for_overwrite` that avoids invalidating existing immutable aliases @@ -646,15 +641,15 @@ impl Allocation &mut self, cx: &impl HasDataLayout, range: AllocRange, - ) -> AllocResult<*mut [u8]> { + ) -> *mut [u8] { self.mark_init(range, true); - self.provenance.clear(range, cx)?; + self.provenance.clear(range, cx); assert!(range.end().bytes_usize() <= self.bytes.len()); // need to do our own bounds-check // Crucially, we go via `AllocBytes::as_mut_ptr`, not `AllocBytes::deref_mut`. let begin_ptr = self.bytes.as_mut_ptr().wrapping_add(range.start.bytes_usize()); let len = range.end().bytes_usize() - range.start.bytes_usize(); - Ok(ptr::slice_from_raw_parts_mut(begin_ptr, len)) + ptr::slice_from_raw_parts_mut(begin_ptr, len) } /// This gives direct mutable access to the entire buffer, just exposing their internal state @@ -723,26 +718,45 @@ impl Allocation let ptr = Pointer::new(prov, Size::from_bytes(bits)); return Ok(Scalar::from_pointer(ptr, cx)); } - - // If we can work on pointers byte-wise, join the byte-wise provenances. - if Prov::OFFSET_IS_ADDR { - let mut prov = self.provenance.get(range.start, cx); + // The other easy case is total absence of provenance. + if self.provenance.range_empty(range, cx) { + return Ok(Scalar::from_uint(bits, range.size)); + } + // If we get here, we have to check per-byte provenance, and join them together. + let prov = 'prov: { + // Initialize with first fragment. Must have index 0. + let Some((mut joint_prov, 0)) = self.provenance.get_byte(range.start, cx) else { + break 'prov None; + }; + // Update with the remaining fragments. for offset in Size::from_bytes(1)..range.size { - let this_prov = self.provenance.get(range.start + offset, cx); - prov = Prov::join(prov, this_prov); + // Ensure there is provenance here and it has the right index. + let Some((frag_prov, frag_idx)) = + self.provenance.get_byte(range.start + offset, cx) + else { + break 'prov None; + }; + // Wildcard provenance is allowed to come with any index (this is needed + // for Miri's native-lib mode to work). + if u64::from(frag_idx) != offset.bytes() && Some(frag_prov) != Prov::WILDCARD { + break 'prov None; + } + // Merge this byte's provenance with the previous ones. + joint_prov = match Prov::join(joint_prov, frag_prov) { + Some(prov) => prov, + None => break 'prov None, + }; } - // Now use this provenance. - let ptr = Pointer::new(prov, Size::from_bytes(bits)); - return Ok(Scalar::from_maybe_pointer(ptr, cx)); - } else { - // Without OFFSET_IS_ADDR, the only remaining case we can handle is total absence of - // provenance. - if self.provenance.range_empty(range, cx) { - return Ok(Scalar::from_uint(bits, range.size)); - } - // Else we have mixed provenance, that doesn't work. + break 'prov Some(joint_prov); + }; + if prov.is_none() && !Prov::OFFSET_IS_ADDR { + // There are some bytes with provenance here but overall the provenance does not add up. + // We need `OFFSET_IS_ADDR` to fall back to no-provenance here; without that option, we must error. return Err(AllocError::ReadPartialPointer(range.start)); } + // We can use this provenance. + let ptr = Pointer::new(prov, Size::from_bytes(bits)); + return Ok(Scalar::from_maybe_pointer(ptr, cx)); } else { // We are *not* reading a pointer. // If we can just ignore provenance or there is none, that's easy. @@ -782,7 +796,7 @@ impl Allocation let endian = cx.data_layout().endian; // Yes we do overwrite all the bytes in `dst`. - let dst = self.get_bytes_unchecked_for_overwrite(cx, range)?; + let dst = self.get_bytes_unchecked_for_overwrite(cx, range); write_target_uint(endian, dst, bytes).unwrap(); // See if we have to also store some provenance. @@ -795,10 +809,9 @@ impl Allocation } /// Write "uninit" to the given memory range. - pub fn write_uninit(&mut self, cx: &impl HasDataLayout, range: AllocRange) -> AllocResult { + pub fn write_uninit(&mut self, cx: &impl HasDataLayout, range: AllocRange) { self.mark_init(range, false); - self.provenance.clear(range, cx)?; - Ok(()) + self.provenance.clear(range, cx); } /// Mark all bytes in the given range as initialised and reset the provenance @@ -817,9 +830,12 @@ impl Allocation } /// Remove all provenance in the given memory range. - pub fn clear_provenance(&mut self, cx: &impl HasDataLayout, range: AllocRange) -> AllocResult { - self.provenance.clear(range, cx)?; - return Ok(()); + pub fn clear_provenance(&mut self, cx: &impl HasDataLayout, range: AllocRange) { + self.provenance.clear(range, cx); + } + + pub fn provenance_merge_bytes(&mut self, cx: &impl HasDataLayout) -> bool { + self.provenance.merge_bytes(cx) } /// Applies a previously prepared provenance copy. diff --git a/compiler/rustc_middle/src/mir/interpret/allocation/provenance_map.rs b/compiler/rustc_middle/src/mir/interpret/allocation/provenance_map.rs index 119d4be64e6..ea8596ea286 100644 --- a/compiler/rustc_middle/src/mir/interpret/allocation/provenance_map.rs +++ b/compiler/rustc_middle/src/mir/interpret/allocation/provenance_map.rs @@ -10,7 +10,7 @@ use rustc_macros::HashStable; use rustc_serialize::{Decodable, Decoder, Encodable, Encoder}; use tracing::trace; -use super::{AllocError, AllocRange, AllocResult, CtfeProvenance, Provenance, alloc_range}; +use super::{AllocRange, CtfeProvenance, Provenance, alloc_range}; /// Stores the provenance information of pointers stored in memory. #[derive(Clone, PartialEq, Eq, Hash, Debug)] @@ -19,25 +19,25 @@ pub struct ProvenanceMap { /// `Provenance` in this map applies from the given offset for an entire pointer-size worth of /// bytes. Two entries in this map are always at least a pointer size apart. ptrs: SortedMap, - /// Provenance in this map only applies to the given single byte. - /// This map is disjoint from the previous. It will always be empty when - /// `Prov::OFFSET_IS_ADDR` is false. - bytes: Option>>, + /// This stores byte-sized provenance fragments. + /// The `u8` indicates the position of this byte inside its original pointer. + /// If the bytes are re-assembled in their original order, the pointer can be used again. + /// Wildcard provenance is allowed to have index 0 everywhere. + bytes: Option>>, } // These impls are generic over `Prov` since `CtfeProvenance` is only decodable/encodable // for some particular `D`/`S`. impl> Decodable for ProvenanceMap { fn decode(d: &mut D) -> Self { - assert!(!Prov::OFFSET_IS_ADDR); // only `CtfeProvenance` is ever serialized + // `bytes` is not in the serialized format Self { ptrs: Decodable::decode(d), bytes: None } } } impl> Encodable for ProvenanceMap { fn encode(&self, s: &mut S) { let Self { ptrs, bytes } = self; - assert!(!Prov::OFFSET_IS_ADDR); // only `CtfeProvenance` is ever serialized - debug_assert!(bytes.is_none()); // without `OFFSET_IS_ADDR`, this is always empty + assert!(bytes.is_none()); // interning refuses allocations with pointer fragments ptrs.encode(s) } } @@ -58,10 +58,10 @@ impl ProvenanceMap { /// Give access to the ptr-sized provenances (which can also be thought of as relocations, and /// indeed that is how codegen treats them). /// - /// Only exposed with `CtfeProvenance` provenance, since it panics if there is bytewise provenance. + /// Only use on interned allocations, as other allocations may have per-byte provenance! #[inline] pub fn ptrs(&self) -> &SortedMap { - debug_assert!(self.bytes.is_none()); // `CtfeProvenance::OFFSET_IS_ADDR` is false so this cannot fail + assert!(self.bytes.is_none(), "`ptrs()` called on non-interned allocation"); &self.ptrs } } @@ -88,12 +88,12 @@ impl ProvenanceMap { } /// `pm.range_ptrs_is_empty(r, cx)` == `pm.range_ptrs_get(r, cx).is_empty()`, but is faster. - pub(super) fn range_ptrs_is_empty(&self, range: AllocRange, cx: &impl HasDataLayout) -> bool { + fn range_ptrs_is_empty(&self, range: AllocRange, cx: &impl HasDataLayout) -> bool { self.ptrs.range_is_empty(Self::adjusted_range_ptrs(range, cx)) } /// Returns all byte-wise provenance in the given range. - fn range_bytes_get(&self, range: AllocRange) -> &[(Size, Prov)] { + fn range_bytes_get(&self, range: AllocRange) -> &[(Size, (Prov, u8))] { if let Some(bytes) = self.bytes.as_ref() { bytes.range(range.start..range.end()) } else { @@ -107,19 +107,47 @@ impl ProvenanceMap { } /// Get the provenance of a single byte. - pub fn get(&self, offset: Size, cx: &impl HasDataLayout) -> Option { + pub fn get_byte(&self, offset: Size, cx: &impl HasDataLayout) -> Option<(Prov, u8)> { let prov = self.range_ptrs_get(alloc_range(offset, Size::from_bytes(1)), cx); debug_assert!(prov.len() <= 1); if let Some(entry) = prov.first() { // If it overlaps with this byte, it is on this byte. debug_assert!(self.bytes.as_ref().is_none_or(|b| !b.contains_key(&offset))); - Some(entry.1) + Some((entry.1, (offset - entry.0).bytes() as u8)) } else { // Look up per-byte provenance. self.bytes.as_ref().and_then(|b| b.get(&offset).copied()) } } + /// Attempt to merge per-byte provenance back into ptr chunks, if the right fragments + /// sit next to each other. Return `false` is that is not possible due to partial pointers. + pub fn merge_bytes(&mut self, cx: &impl HasDataLayout) -> bool { + let Some(bytes) = self.bytes.as_deref_mut() else { + return true; + }; + let ptr_size = cx.data_layout().pointer_size(); + while let Some((offset, (prov, _))) = bytes.iter().next().copied() { + // Check if this fragment starts a pointer. + let range = offset..offset + ptr_size; + let frags = bytes.range(range.clone()); + if frags.len() != ptr_size.bytes_usize() { + return false; + } + for (idx, (_offset, (frag_prov, frag_idx))) in frags.iter().copied().enumerate() { + if frag_prov != prov || frag_idx != idx as u8 { + return false; + } + } + // Looks like a pointer! Move it over to the ptr provenance map. + bytes.remove_range(range); + self.ptrs.insert(offset, prov); + } + // We managed to convert everything into whole pointers. + self.bytes = None; + true + } + /// Check if there is ptr-sized provenance at the given index. /// Does not mean anything for bytewise provenance! But can be useful as an optimization. pub fn get_ptr(&self, offset: Size) -> Option { @@ -137,7 +165,7 @@ impl ProvenanceMap { /// Yields all the provenances stored in this map. pub fn provenances(&self) -> impl Iterator { - let bytes = self.bytes.iter().flat_map(|b| b.values()); + let bytes = self.bytes.iter().flat_map(|b| b.values().map(|(p, _i)| p)); self.ptrs.values().chain(bytes).copied() } @@ -148,16 +176,12 @@ impl ProvenanceMap { /// Removes all provenance inside the given range. /// If there is provenance overlapping with the edges, might result in an error. - pub fn clear(&mut self, range: AllocRange, cx: &impl HasDataLayout) -> AllocResult { + pub fn clear(&mut self, range: AllocRange, cx: &impl HasDataLayout) { let start = range.start; let end = range.end(); // Clear the bytewise part -- this is easy. - if Prov::OFFSET_IS_ADDR { - if let Some(bytes) = self.bytes.as_mut() { - bytes.remove_range(start..end); - } - } else { - debug_assert!(self.bytes.is_none()); + if let Some(bytes) = self.bytes.as_mut() { + bytes.remove_range(start..end); } let pointer_size = cx.data_layout().pointer_size(); @@ -168,7 +192,7 @@ impl ProvenanceMap { // Find all provenance overlapping the given range. if self.range_ptrs_is_empty(range, cx) { // No provenance in this range, we are done. This is the common case. - return Ok(()); + return; } // This redoes some of the work of `range_get_ptrs_is_empty`, but this path is much @@ -179,28 +203,20 @@ impl ProvenanceMap { // We need to handle clearing the provenance from parts of a pointer. if first < start { - if !Prov::OFFSET_IS_ADDR { - // We can't split up the provenance into less than a pointer. - return Err(AllocError::OverwritePartialPointer(first)); - } // Insert the remaining part in the bytewise provenance. let prov = self.ptrs[&first]; let bytes = self.bytes.get_or_insert_with(Box::default); for offset in first..start { - bytes.insert(offset, prov); + bytes.insert(offset, (prov, (offset - first).bytes() as u8)); } } if last > end { let begin_of_last = last - pointer_size; - if !Prov::OFFSET_IS_ADDR { - // We can't split up the provenance into less than a pointer. - return Err(AllocError::OverwritePartialPointer(begin_of_last)); - } // Insert the remaining part in the bytewise provenance. let prov = self.ptrs[&begin_of_last]; let bytes = self.bytes.get_or_insert_with(Box::default); for offset in end..last { - bytes.insert(offset, prov); + bytes.insert(offset, (prov, (offset - begin_of_last).bytes() as u8)); } } @@ -208,8 +224,6 @@ impl ProvenanceMap { // Since provenance do not overlap, we know that removing until `last` (exclusive) is fine, // i.e., this will not remove any other provenance just after the ones we care about. self.ptrs.remove_range(first..last); - - Ok(()) } /// Overwrites all provenance in the given range with wildcard provenance. @@ -218,10 +232,6 @@ impl ProvenanceMap { /// /// Provided for usage in Miri and panics otherwise. pub fn write_wildcards(&mut self, cx: &impl HasDataLayout, range: AllocRange) { - assert!( - Prov::OFFSET_IS_ADDR, - "writing wildcard provenance is not supported when `OFFSET_IS_ADDR` is false" - ); let wildcard = Prov::WILDCARD.unwrap(); let bytes = self.bytes.get_or_insert_with(Box::default); @@ -229,21 +239,22 @@ impl ProvenanceMap { // Remove pointer provenances that overlap with the range, then readd the edge ones bytewise. let ptr_range = Self::adjusted_range_ptrs(range, cx); let ptrs = self.ptrs.range(ptr_range.clone()); - if let Some((offset, prov)) = ptrs.first() { - for byte_ofs in *offset..range.start { - bytes.insert(byte_ofs, *prov); + if let Some((offset, prov)) = ptrs.first().copied() { + for byte_ofs in offset..range.start { + bytes.insert(byte_ofs, (prov, (byte_ofs - offset).bytes() as u8)); } } - if let Some((offset, prov)) = ptrs.last() { - for byte_ofs in range.end()..*offset + cx.data_layout().pointer_size() { - bytes.insert(byte_ofs, *prov); + if let Some((offset, prov)) = ptrs.last().copied() { + for byte_ofs in range.end()..offset + cx.data_layout().pointer_size() { + bytes.insert(byte_ofs, (prov, (byte_ofs - offset).bytes() as u8)); } } self.ptrs.remove_range(ptr_range); // Overwrite bytewise provenance. for offset in range.start..range.end() { - bytes.insert(offset, wildcard); + // The fragment index does not matter for wildcard provenance. + bytes.insert(offset, (wildcard, 0)); } } } @@ -253,7 +264,7 @@ impl ProvenanceMap { /// Offsets are already adjusted to the destination allocation. pub struct ProvenanceCopy { dest_ptrs: Option>, - dest_bytes: Option>, + dest_bytes: Option>, } impl ProvenanceMap { @@ -263,7 +274,7 @@ impl ProvenanceMap { dest: Size, count: u64, cx: &impl HasDataLayout, - ) -> AllocResult> { + ) -> ProvenanceCopy { let shift_offset = move |idx, offset| { // compute offset for current repetition let dest_offset = dest + src.size * idx; // `Size` operations @@ -301,24 +312,16 @@ impl ProvenanceMap { let mut dest_bytes_box = None; let begin_overlap = self.range_ptrs_get(alloc_range(src.start, Size::ZERO), cx).first(); let end_overlap = self.range_ptrs_get(alloc_range(src.end(), Size::ZERO), cx).first(); - if !Prov::OFFSET_IS_ADDR { - // There can't be any bytewise provenance, and we cannot split up the begin/end overlap. - if let Some(entry) = begin_overlap { - return Err(AllocError::ReadPartialPointer(entry.0)); - } - if let Some(entry) = end_overlap { - return Err(AllocError::ReadPartialPointer(entry.0)); - } - debug_assert!(self.bytes.is_none()); - } else { - let mut bytes = Vec::new(); + // We only need to go here if there is some overlap or some bytewise provenance. + if begin_overlap.is_some() || end_overlap.is_some() || self.bytes.is_some() { + let mut bytes: Vec<(Size, (Prov, u8))> = Vec::new(); // First, if there is a part of a pointer at the start, add that. if let Some(entry) = begin_overlap { trace!("start overlapping entry: {entry:?}"); // For really small copies, make sure we don't run off the end of the `src` range. let entry_end = cmp::min(entry.0 + ptr_size, src.end()); for offset in src.start..entry_end { - bytes.push((offset, entry.1)); + bytes.push((offset, (entry.1, (offset - entry.0).bytes() as u8))); } } else { trace!("no start overlapping entry"); @@ -334,8 +337,9 @@ impl ProvenanceMap { let entry_start = cmp::max(entry.0, src.start); for offset in entry_start..src.end() { if bytes.last().is_none_or(|bytes_entry| bytes_entry.0 < offset) { - // The last entry, if it exists, has a lower offset than us. - bytes.push((offset, entry.1)); + // The last entry, if it exists, has a lower offset than us, so we + // can add it at the end and remain sorted. + bytes.push((offset, (entry.1, (offset - entry.0).bytes() as u8))); } else { // There already is an entry for this offset in there! This can happen when the // start and end range checks actually end up hitting the same pointer, so we @@ -358,7 +362,7 @@ impl ProvenanceMap { dest_bytes_box = Some(dest_bytes.into_boxed_slice()); } - Ok(ProvenanceCopy { dest_ptrs: dest_ptrs_box, dest_bytes: dest_bytes_box }) + ProvenanceCopy { dest_ptrs: dest_ptrs_box, dest_bytes: dest_bytes_box } } /// Applies a provenance copy. @@ -368,14 +372,10 @@ impl ProvenanceMap { if let Some(dest_ptrs) = copy.dest_ptrs { self.ptrs.insert_presorted(dest_ptrs.into()); } - if Prov::OFFSET_IS_ADDR { - if let Some(dest_bytes) = copy.dest_bytes - && !dest_bytes.is_empty() - { - self.bytes.get_or_insert_with(Box::default).insert_presorted(dest_bytes.into()); - } - } else { - debug_assert!(copy.dest_bytes.is_none()); + if let Some(dest_bytes) = copy.dest_bytes + && !dest_bytes.is_empty() + { + self.bytes.get_or_insert_with(Box::default).insert_presorted(dest_bytes.into()); } } } diff --git a/compiler/rustc_middle/src/mir/interpret/error.rs b/compiler/rustc_middle/src/mir/interpret/error.rs index 2b0cfb86564..d8168697b4a 100644 --- a/compiler/rustc_middle/src/mir/interpret/error.rs +++ b/compiler/rustc_middle/src/mir/interpret/error.rs @@ -577,9 +577,6 @@ pub enum UnsupportedOpInfo { // // The variants below are only reachable from CTFE/const prop, miri will never emit them. // - /// Overwriting parts of a pointer; without knowing absolute addresses, the resulting state - /// cannot be represented by the CTFE interpreter. - OverwritePartialPointer(Pointer), /// Attempting to read or copy parts of a pointer to somewhere else; without knowing absolute /// addresses, the resulting state cannot be represented by the CTFE interpreter. ReadPartialPointer(Pointer), diff --git a/compiler/rustc_middle/src/mir/interpret/pointer.rs b/compiler/rustc_middle/src/mir/interpret/pointer.rs index e25ff7651f6..c581b2ebf09 100644 --- a/compiler/rustc_middle/src/mir/interpret/pointer.rs +++ b/compiler/rustc_middle/src/mir/interpret/pointer.rs @@ -56,7 +56,7 @@ impl PointerArithmetic for T {} /// mostly opaque; the `Machine` trait extends it with some more operations that also have access to /// some global state. /// The `Debug` rendering is used to display bare provenance, and for the default impl of `fmt`. -pub trait Provenance: Copy + fmt::Debug + 'static { +pub trait Provenance: Copy + PartialEq + fmt::Debug + 'static { /// Says whether the `offset` field of `Pointer`s with this provenance is the actual physical address. /// - If `false`, the offset *must* be relative. This means the bytes representing a pointer are /// different from what the Abstract Machine prescribes, so the interpreter must prevent any @@ -79,7 +79,7 @@ pub trait Provenance: Copy + fmt::Debug + 'static { fn get_alloc_id(self) -> Option; /// Defines the 'join' of provenance: what happens when doing a pointer load and different bytes have different provenance. - fn join(left: Option, right: Option) -> Option; + fn join(left: Self, right: Self) -> Option; } /// The type of provenance in the compile-time interpreter. @@ -192,8 +192,8 @@ impl Provenance for CtfeProvenance { Some(self.alloc_id()) } - fn join(_left: Option, _right: Option) -> Option { - panic!("merging provenance is not supported when `OFFSET_IS_ADDR` is false") + fn join(left: Self, right: Self) -> Option { + if left == right { Some(left) } else { None } } } @@ -224,8 +224,8 @@ impl Provenance for AllocId { Some(self) } - fn join(_left: Option, _right: Option) -> Option { - panic!("merging provenance is not supported when `OFFSET_IS_ADDR` is false") + fn join(_left: Self, _right: Self) -> Option { + unreachable!() } } diff --git a/compiler/rustc_middle/src/mir/pretty.rs b/compiler/rustc_middle/src/mir/pretty.rs index 809cdb329f7..c0dd9a58f85 100644 --- a/compiler/rustc_middle/src/mir/pretty.rs +++ b/compiler/rustc_middle/src/mir/pretty.rs @@ -1826,7 +1826,7 @@ pub fn write_allocation_bytes<'tcx, Prov: Provenance, Extra, Bytes: AllocBytes>( ascii.push('╼'); i += ptr_size; } - } else if let Some(prov) = alloc.provenance().get(i, &tcx) { + } else if let Some((prov, idx)) = alloc.provenance().get_byte(i, &tcx) { // Memory with provenance must be defined assert!( alloc.init_mask().is_range_initialized(alloc_range(i, Size::from_bytes(1))).is_ok() @@ -1836,7 +1836,7 @@ pub fn write_allocation_bytes<'tcx, Prov: Provenance, Extra, Bytes: AllocBytes>( // Format is similar to "oversized" above. let j = i.bytes_usize(); let c = alloc.inspect_with_uninit_and_ptr_outside_interpreter(j..j + 1)[0]; - write!(w, "╾{c:02x}{prov:#?} (1 ptr byte)╼")?; + write!(w, "╾{c:02x}{prov:#?} (ptr fragment {idx})╼")?; i += Size::from_bytes(1); } else if alloc .init_mask() diff --git a/library/core/src/ptr/mod.rs b/library/core/src/ptr/mod.rs index dbe3999b4a4..7339ccd4048 100644 --- a/library/core/src/ptr/mod.rs +++ b/library/core/src/ptr/mod.rs @@ -1345,40 +1345,6 @@ pub const unsafe fn swap(x: *mut T, y: *mut T) { /// assert_eq!(x, [7, 8, 3, 4]); /// assert_eq!(y, [1, 2, 9]); /// ``` -/// -/// # Const evaluation limitations -/// -/// If this function is invoked during const-evaluation, the current implementation has a small (and -/// rarely relevant) limitation: if `count` is at least 2 and the data pointed to by `x` or `y` -/// contains a pointer that crosses the boundary of two `T`-sized chunks of memory, the function may -/// fail to evaluate (similar to a panic during const-evaluation). This behavior may change in the -/// future. -/// -/// The limitation is illustrated by the following example: -/// -/// ``` -/// use std::mem::size_of; -/// use std::ptr; -/// -/// const { unsafe { -/// const PTR_SIZE: usize = size_of::<*const i32>(); -/// let mut data1 = [0u8; PTR_SIZE]; -/// let mut data2 = [0u8; PTR_SIZE]; -/// // Store a pointer in `data1`. -/// data1.as_mut_ptr().cast::<*const i32>().write_unaligned(&42); -/// // Swap the contents of `data1` and `data2` by swapping `PTR_SIZE` many `u8`-sized chunks. -/// // This call will fail, because the pointer in `data1` crosses the boundary -/// // between several of the 1-byte chunks that are being swapped here. -/// //ptr::swap_nonoverlapping(data1.as_mut_ptr(), data2.as_mut_ptr(), PTR_SIZE); -/// // Swap the contents of `data1` and `data2` by swapping a single chunk of size -/// // `[u8; PTR_SIZE]`. That works, as there is no pointer crossing the boundary between -/// // two chunks. -/// ptr::swap_nonoverlapping(&mut data1, &mut data2, 1); -/// // Read the pointer from `data2` and dereference it. -/// let ptr = data2.as_ptr().cast::<*const i32>().read_unaligned(); -/// assert!(*ptr == 42); -/// } } -/// ``` #[inline] #[stable(feature = "swap_nonoverlapping", since = "1.27.0")] #[rustc_const_stable(feature = "const_swap_nonoverlapping", since = "1.88.0")] @@ -1407,9 +1373,7 @@ pub const unsafe fn swap_nonoverlapping(x: *mut T, y: *mut T, count: usize) { const_eval_select!( @capture[T] { x: *mut T, y: *mut T, count: usize }: if const { - // At compile-time we want to always copy this in chunks of `T`, to ensure that if there - // are pointers inside `T` we will copy them in one go rather than trying to copy a part - // of a pointer (which would not work). + // At compile-time we don't need all the special code below. // SAFETY: Same preconditions as this function unsafe { swap_nonoverlapping_const(x, y, count) } } else { diff --git a/library/coretests/tests/ptr.rs b/library/coretests/tests/ptr.rs index 197a14423b5..c13fb96a67f 100644 --- a/library/coretests/tests/ptr.rs +++ b/library/coretests/tests/ptr.rs @@ -936,22 +936,18 @@ fn test_const_swap_ptr() { assert!(*s1.0.ptr == 666); assert!(*s2.0.ptr == 1); - // Swap them back, again as an array. + // Swap them back, byte-for-byte unsafe { ptr::swap_nonoverlapping( - ptr::from_mut(&mut s1).cast::(), - ptr::from_mut(&mut s2).cast::(), - 1, + ptr::from_mut(&mut s1).cast::(), + ptr::from_mut(&mut s2).cast::(), + size_of::(), ); } // Make sure they still work. assert!(*s1.0.ptr == 1); assert!(*s2.0.ptr == 666); - - // This is where we'd swap again using a `u8` type and a `count` of `size_of::()` if it - // were not for the limitation of `swap_nonoverlapping` around pointers crossing multiple - // elements. }; } diff --git a/src/tools/miri/src/machine.rs b/src/tools/miri/src/machine.rs index 7271d3f619c..b8a187fed7c 100644 --- a/src/tools/miri/src/machine.rs +++ b/src/tools/miri/src/machine.rs @@ -285,16 +285,16 @@ impl interpret::Provenance for Provenance { Ok(()) } - fn join(left: Option, right: Option) -> Option { + fn join(left: Self, right: Self) -> Option { match (left, right) { // If both are the *same* concrete tag, that is the result. ( - Some(Provenance::Concrete { alloc_id: left_alloc, tag: left_tag }), - Some(Provenance::Concrete { alloc_id: right_alloc, tag: right_tag }), - ) if left_alloc == right_alloc && left_tag == right_tag => left, + Provenance::Concrete { alloc_id: left_alloc, tag: left_tag }, + Provenance::Concrete { alloc_id: right_alloc, tag: right_tag }, + ) if left_alloc == right_alloc && left_tag == right_tag => Some(left), // If one side is a wildcard, the best possible outcome is that it is equal to the other // one, and we use that. - (Some(Provenance::Wildcard), o) | (o, Some(Provenance::Wildcard)) => o, + (Provenance::Wildcard, o) | (o, Provenance::Wildcard) => Some(o), // Otherwise, fall back to `None`. _ => None, } diff --git a/src/tools/miri/src/shims/native_lib/mod.rs b/src/tools/miri/src/shims/native_lib/mod.rs index 2827ed997a7..36c18379cb8 100644 --- a/src/tools/miri/src/shims/native_lib/mod.rs +++ b/src/tools/miri/src/shims/native_lib/mod.rs @@ -246,7 +246,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { let p_map = alloc.provenance(); for idx in overlap { // If a provenance was read by the foreign code, expose it. - if let Some(prov) = p_map.get(Size::from_bytes(idx), this) { + if let Some((prov, _idx)) = p_map.get_byte(Size::from_bytes(idx), this) { this.expose_provenance(prov)?; } } diff --git a/src/tools/miri/tests/fail/provenance/mix-ptrs1.rs b/src/tools/miri/tests/fail/provenance/mix-ptrs1.rs new file mode 100644 index 00000000000..0b7266a4da6 --- /dev/null +++ b/src/tools/miri/tests/fail/provenance/mix-ptrs1.rs @@ -0,0 +1,21 @@ +use std::{mem, ptr}; + +const PTR_SIZE: usize = mem::size_of::<&i32>(); + +fn main() { + unsafe { + let ptr = &0 as *const i32; + let arr = [ptr; 2]; + // We want to do a scalar read of a pointer at offset PTR_SIZE/2 into this array. But we + // cannot use a packed struct or `read_unaligned`, as those use the memcpy code path in + // Miri. So instead we shift the entire array by a bit and then the actual read we want to + // do is perfectly aligned. + let mut target_arr = [ptr::null::(); 3]; + let target = target_arr.as_mut_ptr().cast::(); + target.add(PTR_SIZE / 2).cast::<[*const i32; 2]>().write_unaligned(arr); + // Now target_arr[1] is a mix of the two `ptr` we had stored in `arr`. + // They all have the same provenance, but not in the right order, so we reject this. + let strange_ptr = target_arr[1]; + assert_eq!(*strange_ptr.with_addr(ptr.addr()), 0); //~ERROR: no provenance + } +} diff --git a/src/tools/miri/tests/fail/provenance/mix-ptrs1.stderr b/src/tools/miri/tests/fail/provenance/mix-ptrs1.stderr new file mode 100644 index 00000000000..88d75135a76 --- /dev/null +++ b/src/tools/miri/tests/fail/provenance/mix-ptrs1.stderr @@ -0,0 +1,16 @@ +error: Undefined Behavior: pointer not dereferenceable: pointer must be dereferenceable for 4 bytes, but got $HEX[noalloc] which is a dangling pointer (it has no provenance) + --> tests/fail/provenance/mix-ptrs1.rs:LL:CC + | +LL | assert_eq!(*strange_ptr.with_addr(ptr.addr()), 0); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Undefined Behavior occurred here + | + = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior + = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information + = note: BACKTRACE: + = note: inside `main` at RUSTLIB/core/src/macros/mod.rs:LL:CC + = note: this error originates in the macro `assert_eq` (in Nightly builds, run with -Z macro-backtrace for more info) + +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + +error: aborting due to 1 previous error + diff --git a/src/tools/miri/tests/fail/provenance/mix-ptrs2.rs b/src/tools/miri/tests/fail/provenance/mix-ptrs2.rs new file mode 100644 index 00000000000..9ef40c194ab --- /dev/null +++ b/src/tools/miri/tests/fail/provenance/mix-ptrs2.rs @@ -0,0 +1,37 @@ +use std::mem; + +const PTR_SIZE: usize = mem::size_of::<&i32>(); + +/// Overwrite one byte of a pointer, then restore it. +fn main() { + unsafe fn ptr_bytes<'x>(ptr: &'x mut *const i32) -> &'x mut [mem::MaybeUninit; PTR_SIZE] { + mem::transmute(ptr) + } + + // Returns a value with the same provenance as `x` but 0 for the integer value. + // `x` must be initialized. + unsafe fn zero_with_provenance(x: mem::MaybeUninit) -> mem::MaybeUninit { + let ptr = [x; PTR_SIZE]; + let ptr: *const i32 = mem::transmute(ptr); + let mut ptr = ptr.with_addr(0); + ptr_bytes(&mut ptr)[0] + } + + unsafe { + let ptr = &42; + let mut ptr = ptr as *const i32; + // Get a bytewise view of the pointer. + let ptr_bytes = ptr_bytes(&mut ptr); + + // The highest bytes must be 0 for this to work. + let hi = if cfg!(target_endian = "little") { ptr_bytes.len() - 1 } else { 0 }; + assert_eq!(*ptr_bytes[hi].as_ptr().cast::(), 0); + // Overwrite provenance on the last byte. + ptr_bytes[hi] = mem::MaybeUninit::new(0); + // Restore it from the another byte. + ptr_bytes[hi] = zero_with_provenance(ptr_bytes[1]); + + // Now ptr is almost good, except the provenance fragment indices do not work out... + assert_eq!(*ptr, 42); //~ERROR: no provenance + } +} diff --git a/src/tools/miri/tests/fail/provenance/mix-ptrs2.stderr b/src/tools/miri/tests/fail/provenance/mix-ptrs2.stderr new file mode 100644 index 00000000000..636378952ba --- /dev/null +++ b/src/tools/miri/tests/fail/provenance/mix-ptrs2.stderr @@ -0,0 +1,16 @@ +error: Undefined Behavior: pointer not dereferenceable: pointer must be dereferenceable for 4 bytes, but got $HEX[noalloc] which is a dangling pointer (it has no provenance) + --> tests/fail/provenance/mix-ptrs2.rs:LL:CC + | +LL | assert_eq!(*ptr, 42); + | ^^^^^^^^^^^^^^^^^^^^ Undefined Behavior occurred here + | + = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior + = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information + = note: BACKTRACE: + = note: inside `main` at RUSTLIB/core/src/macros/mod.rs:LL:CC + = note: this error originates in the macro `assert_eq` (in Nightly builds, run with -Z macro-backtrace for more info) + +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + +error: aborting due to 1 previous error + diff --git a/src/tools/miri/tests/fail/uninit/uninit_alloc_diagnostic_with_provenance.rs b/src/tools/miri/tests/fail/uninit/uninit_alloc_diagnostic_with_provenance.rs index 1cf70d9d9a3..78a5098d21a 100644 --- a/src/tools/miri/tests/fail/uninit/uninit_alloc_diagnostic_with_provenance.rs +++ b/src/tools/miri/tests/fail/uninit/uninit_alloc_diagnostic_with_provenance.rs @@ -9,11 +9,10 @@ use std::alloc::{Layout, alloc, dealloc}; use std::mem::{self, MaybeUninit}; use std::slice::from_raw_parts; -fn byte_with_provenance(val: u8, prov: *const T) -> MaybeUninit { - let ptr = prov.with_addr(val as usize); +fn byte_with_provenance(val: u8, prov: *const T, frag_idx: usize) -> MaybeUninit { + let ptr = prov.with_addr(usize::from_ne_bytes([val; _])); let bytes: [MaybeUninit; mem::size_of::<*const ()>()] = unsafe { mem::transmute(ptr) }; - let lsb = if cfg!(target_endian = "little") { 0 } else { bytes.len() - 1 }; - bytes[lsb] + bytes[frag_idx] } fn main() { @@ -21,10 +20,10 @@ fn main() { unsafe { let ptr = alloc(layout); let ptr_raw = ptr.cast::>(); - *ptr_raw.add(0) = byte_with_provenance(0x42, &42u8); + *ptr_raw.add(0) = byte_with_provenance(0x42, &42u8, 0); *ptr.add(1) = 0x12; *ptr.add(2) = 0x13; - *ptr_raw.add(3) = byte_with_provenance(0x43, &0u8); + *ptr_raw.add(3) = byte_with_provenance(0x43, &0u8, 1); let slice1 = from_raw_parts(ptr, 8); let slice2 = from_raw_parts(ptr.add(8), 8); drop(slice1.cmp(slice2)); diff --git a/src/tools/miri/tests/fail/uninit/uninit_alloc_diagnostic_with_provenance.stderr b/src/tools/miri/tests/fail/uninit/uninit_alloc_diagnostic_with_provenance.stderr index d3253317fae..305d8d4fbed 100644 --- a/src/tools/miri/tests/fail/uninit/uninit_alloc_diagnostic_with_provenance.stderr +++ b/src/tools/miri/tests/fail/uninit/uninit_alloc_diagnostic_with_provenance.stderr @@ -17,7 +17,7 @@ LL | drop(slice1.cmp(slice2)); Uninitialized memory occurred at ALLOC[0x4..0x8], in this allocation: ALLOC (Rust heap, size: 16, align: 8) { - ╾42[ALLOC] (1 ptr byte)╼ 12 13 ╾43[ALLOC] (1 ptr byte)╼ __ __ __ __ __ __ __ __ __ __ __ __ │ ━..━░░░░░░░░░░░░ + ╾42[ALLOC] (ptr fragment 0)╼ 12 13 ╾43[ALLOC] (ptr fragment 1)╼ __ __ __ __ __ __ __ __ __ __ __ __ │ ━..━░░░░░░░░░░░░ } ALLOC (global (static or const), size: 1, align: 1) { 2a │ * diff --git a/src/tools/miri/tests/native-lib/fail/tracing/partial_init.stderr b/src/tools/miri/tests/native-lib/fail/tracing/partial_init.stderr index 74a599ede5c..1c0ad2f0dc9 100644 --- a/src/tools/miri/tests/native-lib/fail/tracing/partial_init.stderr +++ b/src/tools/miri/tests/native-lib/fail/tracing/partial_init.stderr @@ -35,7 +35,7 @@ LL | partial_init(); Uninitialized memory occurred at ALLOC[0x2..0x3], in this allocation: ALLOC (stack variable, size: 3, align: 1) { - ╾00[wildcard] (1 ptr byte)╼ ╾00[wildcard] (1 ptr byte)╼ __ │ ━━░ + ╾00[wildcard] (ptr fragment 0)╼ ╾00[wildcard] (ptr fragment 0)╼ __ │ ━━░ } note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace diff --git a/src/tools/miri/tests/pass/provenance.rs b/src/tools/miri/tests/pass/provenance.rs index 8c52b703190..46f5bd73d55 100644 --- a/src/tools/miri/tests/pass/provenance.rs +++ b/src/tools/miri/tests/pass/provenance.rs @@ -6,7 +6,6 @@ const PTR_SIZE: usize = mem::size_of::<&i32>(); fn main() { basic(); - partial_overwrite_then_restore(); bytewise_ptr_methods(); bytewise_custom_memcpy(); bytewise_custom_memcpy_chunked(); @@ -29,40 +28,6 @@ fn basic() { assert_eq!(unsafe { *ptr_back }, 42); } -/// Overwrite one byte of a pointer, then restore it. -fn partial_overwrite_then_restore() { - unsafe fn ptr_bytes<'x>(ptr: &'x mut *const i32) -> &'x mut [mem::MaybeUninit; PTR_SIZE] { - mem::transmute(ptr) - } - - // Returns a value with the same provenance as `x` but 0 for the integer value. - // `x` must be initialized. - unsafe fn zero_with_provenance(x: mem::MaybeUninit) -> mem::MaybeUninit { - let ptr = [x; PTR_SIZE]; - let ptr: *const i32 = mem::transmute(ptr); - let mut ptr = ptr.with_addr(0); - ptr_bytes(&mut ptr)[0] - } - - unsafe { - let ptr = &42; - let mut ptr = ptr as *const i32; - // Get a bytewise view of the pointer. - let ptr_bytes = ptr_bytes(&mut ptr); - - // The highest bytes must be 0 for this to work. - let hi = if cfg!(target_endian = "little") { ptr_bytes.len() - 1 } else { 0 }; - assert_eq!(*ptr_bytes[hi].as_ptr().cast::(), 0); - // Overwrite provenance on the last byte. - ptr_bytes[hi] = mem::MaybeUninit::new(0); - // Restore it from the another byte. - ptr_bytes[hi] = zero_with_provenance(ptr_bytes[1]); - - // Now ptr should be good again. - assert_eq!(*ptr, 42); - } -} - fn bytewise_ptr_methods() { let mut ptr1 = &1; let mut ptr2 = &2; diff --git a/src/tools/miri/tests/pass/transmute_ptr.rs b/src/tools/miri/tests/pass/transmute_ptr.rs index 0944781c6de..0a53b778294 100644 --- a/src/tools/miri/tests/pass/transmute_ptr.rs +++ b/src/tools/miri/tests/pass/transmute_ptr.rs @@ -1,6 +1,6 @@ //@revisions: stack tree //@[tree]compile-flags: -Zmiri-tree-borrows -use std::{mem, ptr}; +use std::mem; fn t1() { // If we are careful, we can exploit data layout... @@ -27,27 +27,8 @@ fn ptr_integer_array() { let _x: [u8; PTR_SIZE] = unsafe { mem::transmute(&0) }; } -fn ptr_in_two_halves() { - unsafe { - let ptr = &0 as *const i32; - let arr = [ptr; 2]; - // We want to do a scalar read of a pointer at offset PTR_SIZE/2 into this array. But we - // cannot use a packed struct or `read_unaligned`, as those use the memcpy code path in - // Miri. So instead we shift the entire array by a bit and then the actual read we want to - // do is perfectly aligned. - let mut target_arr = [ptr::null::(); 3]; - let target = target_arr.as_mut_ptr().cast::(); - target.add(PTR_SIZE / 2).cast::<[*const i32; 2]>().write_unaligned(arr); - // Now target_arr[1] is a mix of the two `ptr` we had stored in `arr`. - let strange_ptr = target_arr[1]; - // Check that the provenance works out. - assert_eq!(*strange_ptr.with_addr(ptr.addr()), 0); - } -} - fn main() { t1(); t2(); ptr_integer_array(); - ptr_in_two_halves(); } diff --git a/tests/ui/consts/const-eval/heap/ptr_not_made_global.stderr b/tests/ui/consts/const-eval/heap/ptr_not_made_global.stderr index cb2bb1e8cd8..e5b108df298 100644 --- a/tests/ui/consts/const-eval/heap/ptr_not_made_global.stderr +++ b/tests/ui/consts/const-eval/heap/ptr_not_made_global.stderr @@ -4,7 +4,7 @@ error: encountered `const_allocate` pointer in final value that was not made glo LL | const FOO: &i32 = foo(); | ^^^^^^^^^^^^^^^ | - = note: use `const_make_global` to make allocated pointers immutable before returning + = note: use `const_make_global` to turn allocated pointers into immutable globals before returning error: encountered `const_allocate` pointer in final value that was not made global --> $DIR/ptr_not_made_global.rs:12:1 @@ -12,7 +12,7 @@ error: encountered `const_allocate` pointer in final value that was not made glo LL | const FOO_RAW: *const i32 = foo(); | ^^^^^^^^^^^^^^^^^^^^^^^^^ | - = note: use `const_make_global` to make allocated pointers immutable before returning + = note: use `const_make_global` to turn allocated pointers into immutable globals before returning error: aborting due to 2 previous errors diff --git a/tests/ui/consts/const-eval/heap/ptr_not_made_global_mut.stderr b/tests/ui/consts/const-eval/heap/ptr_not_made_global_mut.stderr index 2445ce633d6..2d1993f96d3 100644 --- a/tests/ui/consts/const-eval/heap/ptr_not_made_global_mut.stderr +++ b/tests/ui/consts/const-eval/heap/ptr_not_made_global_mut.stderr @@ -4,7 +4,7 @@ error: encountered `const_allocate` pointer in final value that was not made glo LL | const BAR: *mut i32 = unsafe { intrinsics::const_allocate(4, 4) as *mut i32 }; | ^^^^^^^^^^^^^^^^^^^ | - = note: use `const_make_global` to make allocated pointers immutable before returning + = note: use `const_make_global` to turn allocated pointers into immutable globals before returning error: aborting due to 1 previous error diff --git a/tests/ui/consts/const-eval/partial_ptr_overwrite.rs b/tests/ui/consts/const-eval/partial_ptr_overwrite.rs deleted file mode 100644 index bd97bec0f71..00000000000 --- a/tests/ui/consts/const-eval/partial_ptr_overwrite.rs +++ /dev/null @@ -1,12 +0,0 @@ -// Test for the behavior described in . - -const PARTIAL_OVERWRITE: () = { - let mut p = &42; - unsafe { - let ptr: *mut _ = &mut p; - *(ptr as *mut u8) = 123; //~ ERROR unable to overwrite parts of a pointer - } - let x = *p; -}; - -fn main() {} diff --git a/tests/ui/consts/const-eval/partial_ptr_overwrite.stderr b/tests/ui/consts/const-eval/partial_ptr_overwrite.stderr deleted file mode 100644 index 6ef1cfd35c8..00000000000 --- a/tests/ui/consts/const-eval/partial_ptr_overwrite.stderr +++ /dev/null @@ -1,12 +0,0 @@ -error[E0080]: unable to overwrite parts of a pointer in memory at ALLOC0 - --> $DIR/partial_ptr_overwrite.rs:7:9 - | -LL | *(ptr as *mut u8) = 123; - | ^^^^^^^^^^^^^^^^^^^^^^^ evaluation of `PARTIAL_OVERWRITE` failed here - | - = help: this code performed an operation that depends on the underlying bytes representing a pointer - = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0080`. diff --git a/tests/ui/consts/const-eval/ptr_fragments.rs b/tests/ui/consts/const-eval/ptr_fragments.rs new file mode 100644 index 00000000000..04dcbe55590 --- /dev/null +++ b/tests/ui/consts/const-eval/ptr_fragments.rs @@ -0,0 +1,63 @@ +//! Test that various operations involving pointer fragments work as expected. +//@ run-pass + +use std::mem::{self, MaybeUninit, transmute}; +use std::ptr; + +type Byte = MaybeUninit; + +const unsafe fn memcpy(dst: *mut Byte, src: *const Byte, n: usize) { + let mut i = 0; + while i < n { + *dst.add(i) = *src.add(i); + i += 1; + } +} + +const _MEMCPY: () = unsafe { + let ptr = &42; + let mut ptr2 = ptr::null::(); + memcpy(&mut ptr2 as *mut _ as *mut _, &ptr as *const _ as *const _, mem::size_of::<&i32>()); + assert!(*ptr2 == 42); +}; +const _MEMCPY_OFFSET: () = unsafe { + // Same as above, but the pointer has a non-zero offset so not all the data bytes are the same. + let ptr = &(42, 18); + let ptr = &ptr.1; + let mut ptr2 = ptr::null::(); + memcpy(&mut ptr2 as *mut _ as *mut _, &ptr as *const _ as *const _, mem::size_of::<&i32>()); + assert!(*ptr2 == 18); +}; + +const MEMCPY_RET: MaybeUninit<*const i32> = unsafe { + let ptr = &42; + let mut ptr2 = MaybeUninit::new(ptr::null::()); + memcpy(&mut ptr2 as *mut _ as *mut _, &ptr as *const _ as *const _, mem::size_of::<&i32>()); + // Return in a MaybeUninit so it does not get treated as a scalar. + ptr2 +}; + +#[allow(dead_code)] +fn reassemble_ptr_fragments_in_static() { + static DATA: i32 = 1i32; + + #[cfg(target_pointer_width = "64")] + struct Thing { + x: MaybeUninit, + y: MaybeUninit, + } + #[cfg(target_pointer_width = "32")] + struct Thing { + x: MaybeUninit, + y: MaybeUninit, + } + + static X: Thing = unsafe { + let Thing { x, y } = transmute(&raw const DATA); + Thing { x, y } + }; +} + +fn main() { + assert_eq!(unsafe { MEMCPY_RET.assume_init().read() }, 42); +} diff --git a/tests/ui/consts/const-eval/ptr_fragments_in_final.rs b/tests/ui/consts/const-eval/ptr_fragments_in_final.rs new file mode 100644 index 00000000000..e2f3f51b086 --- /dev/null +++ b/tests/ui/consts/const-eval/ptr_fragments_in_final.rs @@ -0,0 +1,25 @@ +//! Test that we properly error when there is a pointer fragment in the final value. + +use std::{mem::{self, MaybeUninit}, ptr}; + +type Byte = MaybeUninit; + +const unsafe fn memcpy(dst: *mut Byte, src: *const Byte, n: usize) { + let mut i = 0; + while i < n { + dst.add(i).write(src.add(i).read()); + i += 1; + } +} + +const MEMCPY_RET: MaybeUninit<*const i32> = unsafe { //~ERROR: partial pointer in final value + let ptr = &42; + let mut ptr2 = MaybeUninit::new(ptr::null::()); + memcpy(&mut ptr2 as *mut _ as *mut _, &ptr as *const _ as *const _, mem::size_of::<&i32>() / 2); + // Return in a MaybeUninit so it does not get treated as a scalar. + ptr2 +}; + +fn main() { + assert_eq!(unsafe { MEMCPY_RET.assume_init().read() }, 42); +} diff --git a/tests/ui/consts/const-eval/ptr_fragments_in_final.stderr b/tests/ui/consts/const-eval/ptr_fragments_in_final.stderr new file mode 100644 index 00000000000..628bf2566e5 --- /dev/null +++ b/tests/ui/consts/const-eval/ptr_fragments_in_final.stderr @@ -0,0 +1,10 @@ +error: encountered partial pointer in final value of constant + --> $DIR/ptr_fragments_in_final.rs:15:1 + | +LL | const MEMCPY_RET: MaybeUninit<*const i32> = unsafe { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: while pointers can be broken apart into individual bytes during const-evaluation, only complete pointers (with all their bytes in the right order) are supported in the final value + +error: aborting due to 1 previous error + diff --git a/tests/ui/consts/const-eval/read_partial_ptr.rs b/tests/ui/consts/const-eval/read_partial_ptr.rs new file mode 100644 index 00000000000..bccef9c0bc6 --- /dev/null +++ b/tests/ui/consts/const-eval/read_partial_ptr.rs @@ -0,0 +1,49 @@ +//! Ensure we error when trying to load from a pointer whose provenance has been messed with. + +const PARTIAL_OVERWRITE: () = { + let mut p = &42; + // Overwrite one byte with a no-provenance value. + unsafe { + let ptr: *mut _ = &mut p; + *(ptr as *mut u8) = 123; + } + let x = *p; //~ ERROR: unable to read parts of a pointer +}; + +const PTR_BYTES_SWAP: () = { + let mut p = &42; + // Swap the first two bytes. + unsafe { + let ptr = &mut p as *mut _ as *mut std::mem::MaybeUninit; + let byte0 = ptr.read(); + let byte1 = ptr.add(1).read(); + ptr.write(byte1); + ptr.add(1).write(byte0); + } + let x = *p; //~ ERROR: unable to read parts of a pointer +}; + +const PTR_BYTES_REPEAT: () = { + let mut p = &42; + // Duplicate the first byte over the second. + unsafe { + let ptr = &mut p as *mut _ as *mut std::mem::MaybeUninit; + let byte0 = ptr.read(); + ptr.add(1).write(byte0); + } + let x = *p; //~ ERROR: unable to read parts of a pointer +}; + +const PTR_BYTES_MIX: () = { + let mut p = &42; + let q = &43; + // Overwrite the first byte of p with the first byte of q. + unsafe { + let ptr = &mut p as *mut _ as *mut std::mem::MaybeUninit; + let qtr = &q as *const _ as *const std::mem::MaybeUninit; + ptr.write(qtr.read()); + } + let x = *p; //~ ERROR: unable to read parts of a pointer +}; + +fn main() {} diff --git a/tests/ui/consts/const-eval/read_partial_ptr.stderr b/tests/ui/consts/const-eval/read_partial_ptr.stderr new file mode 100644 index 00000000000..196606c77a3 --- /dev/null +++ b/tests/ui/consts/const-eval/read_partial_ptr.stderr @@ -0,0 +1,39 @@ +error[E0080]: unable to read parts of a pointer from memory at ALLOC0 + --> $DIR/read_partial_ptr.rs:10:13 + | +LL | let x = *p; + | ^^ evaluation of `PARTIAL_OVERWRITE` failed here + | + = help: this code performed an operation that depends on the underlying bytes representing a pointer + = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported + +error[E0080]: unable to read parts of a pointer from memory at ALLOC1 + --> $DIR/read_partial_ptr.rs:23:13 + | +LL | let x = *p; + | ^^ evaluation of `PTR_BYTES_SWAP` failed here + | + = help: this code performed an operation that depends on the underlying bytes representing a pointer + = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported + +error[E0080]: unable to read parts of a pointer from memory at ALLOC2 + --> $DIR/read_partial_ptr.rs:34:13 + | +LL | let x = *p; + | ^^ evaluation of `PTR_BYTES_REPEAT` failed here + | + = help: this code performed an operation that depends on the underlying bytes representing a pointer + = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported + +error[E0080]: unable to read parts of a pointer from memory at ALLOC3 + --> $DIR/read_partial_ptr.rs:46:13 + | +LL | let x = *p; + | ^^ evaluation of `PTR_BYTES_MIX` failed here + | + = help: this code performed an operation that depends on the underlying bytes representing a pointer + = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported + +error: aborting due to 4 previous errors + +For more information about this error, try `rustc --explain E0080`. diff --git a/tests/ui/consts/missing_span_in_backtrace.rs b/tests/ui/consts/missing_span_in_backtrace.rs index 4f3f9aa6ada..09a11c75fd7 100644 --- a/tests/ui/consts/missing_span_in_backtrace.rs +++ b/tests/ui/consts/missing_span_in_backtrace.rs @@ -1,3 +1,4 @@ +//! Check what happens when the error occurs inside a std function that we can't print the span of. //@ ignore-backends: gcc //@ compile-flags: -Z ui-testing=no @@ -7,15 +8,15 @@ use std::{ }; const X: () = { - let mut ptr1 = &1; - let mut ptr2 = &2; + let mut x1 = 1; + let mut x2 = 2; // Swap them, bytewise. unsafe { - ptr::swap_nonoverlapping( //~ ERROR unable to copy parts of a pointer - &mut ptr1 as *mut _ as *mut MaybeUninit, - &mut ptr2 as *mut _ as *mut MaybeUninit, - mem::size_of::<&i32>(), + ptr::swap_nonoverlapping( //~ ERROR beyond the end of the allocation + &mut x1 as *mut _ as *mut MaybeUninit, + &mut x2 as *mut _ as *mut MaybeUninit, + 10, ); } }; diff --git a/tests/ui/consts/missing_span_in_backtrace.stderr b/tests/ui/consts/missing_span_in_backtrace.stderr index 0ac1e281107..55dde635828 100644 --- a/tests/ui/consts/missing_span_in_backtrace.stderr +++ b/tests/ui/consts/missing_span_in_backtrace.stderr @@ -1,15 +1,13 @@ -error[E0080]: unable to copy parts of a pointer from memory at ALLOC0 - --> $DIR/missing_span_in_backtrace.rs:15:9 +error[E0080]: memory access failed: attempting to access 1 byte, but got ALLOC0+0x4 which is at or beyond the end of the allocation of size 4 bytes + --> $DIR/missing_span_in_backtrace.rs:16:9 | -15 | / ptr::swap_nonoverlapping( -16 | | &mut ptr1 as *mut _ as *mut MaybeUninit, -17 | | &mut ptr2 as *mut _ as *mut MaybeUninit, -18 | | mem::size_of::<&i32>(), -19 | | ); +16 | / ptr::swap_nonoverlapping( +17 | | &mut x1 as *mut _ as *mut MaybeUninit, +18 | | &mut x2 as *mut _ as *mut MaybeUninit, +19 | | 10, +20 | | ); | |_________^ evaluation of `X` failed inside this call | - = help: this code performed an operation that depends on the underlying bytes representing a pointer - = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported note: inside `swap_nonoverlapping::compiletime::>` --> $SRC_DIR/core/src/ptr/mod.rs:LL:COL note: inside `std::ptr::swap_nonoverlapping_const::>` -- cgit 1.4.1-3-g733a5 From 02ac116e53f525a929d251bf9dc4f2ea1cacd3c8 Mon Sep 17 00:00:00 2001 From: Eduard Stefes Date: Tue, 29 Jul 2025 11:27:07 +0200 Subject: Fix tests for big-endian The tests fail on s390x and presumably other big-endian systems, due to print of raw values and padding bytes. To fix the tests remove the raw output values in the error note with `normalize-stderr`. --- tests/ui/consts/const-eval/union-const-eval-field.rs | 1 + tests/ui/consts/const-eval/union-const-eval-field.stderr | 8 ++++---- tests/ui/intrinsics/intrinsic-raw_eq-const-bad.rs | 1 + tests/ui/intrinsics/intrinsic-raw_eq-const-bad.stderr | 8 ++++---- 4 files changed, 10 insertions(+), 8 deletions(-) diff --git a/tests/ui/consts/const-eval/union-const-eval-field.rs b/tests/ui/consts/const-eval/union-const-eval-field.rs index 719e59b007c..2c9061a7a50 100644 --- a/tests/ui/consts/const-eval/union-const-eval-field.rs +++ b/tests/ui/consts/const-eval/union-const-eval-field.rs @@ -1,5 +1,6 @@ //@ dont-require-annotations: NOTE //@ normalize-stderr: "(the raw bytes of the constant) \(size: [0-9]*, align: [0-9]*\)" -> "$1 (size: $$SIZE, align: $$ALIGN)" +//@ normalize-stderr: "([[:xdigit:]]{2}\s){4}(__\s){4}\s+│\s+([?|\.]){4}\W{4}" -> "HEX_DUMP" type Field1 = i32; type Field2 = f32; diff --git a/tests/ui/consts/const-eval/union-const-eval-field.stderr b/tests/ui/consts/const-eval/union-const-eval-field.stderr index 1843ce273ac..3b7e5508d56 100644 --- a/tests/ui/consts/const-eval/union-const-eval-field.stderr +++ b/tests/ui/consts/const-eval/union-const-eval-field.stderr @@ -1,21 +1,21 @@ error[E0080]: reading memory at ALLOC0[0x0..0x8], but memory is uninitialized at [0x4..0x8], and this operation requires initialized memory - --> $DIR/union-const-eval-field.rs:29:37 + --> $DIR/union-const-eval-field.rs:30:37 | LL | const FIELD3: Field3 = unsafe { UNION.field3 }; | ^^^^^^^^^^^^ evaluation of `read_field3::FIELD3` failed here | = note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) { - 00 00 80 3f __ __ __ __ │ ...?░░░░ + HEX_DUMP } note: erroneous constant encountered - --> $DIR/union-const-eval-field.rs:31:5 + --> $DIR/union-const-eval-field.rs:32:5 | LL | FIELD3 | ^^^^^^ note: erroneous constant encountered - --> $DIR/union-const-eval-field.rs:31:5 + --> $DIR/union-const-eval-field.rs:32:5 | LL | FIELD3 | ^^^^^^ diff --git a/tests/ui/intrinsics/intrinsic-raw_eq-const-bad.rs b/tests/ui/intrinsics/intrinsic-raw_eq-const-bad.rs index 15f4a9a778e..ed15f5bba96 100644 --- a/tests/ui/intrinsics/intrinsic-raw_eq-const-bad.rs +++ b/tests/ui/intrinsics/intrinsic-raw_eq-const-bad.rs @@ -1,3 +1,4 @@ +//@ normalize-stderr: "[[:xdigit:]]{2} __ ([[:xdigit:]]{2}\s){2}" -> "HEX_DUMP" #![feature(core_intrinsics)] const RAW_EQ_PADDING: bool = unsafe { diff --git a/tests/ui/intrinsics/intrinsic-raw_eq-const-bad.stderr b/tests/ui/intrinsics/intrinsic-raw_eq-const-bad.stderr index 5f4ef14d586..329da35297e 100644 --- a/tests/ui/intrinsics/intrinsic-raw_eq-const-bad.stderr +++ b/tests/ui/intrinsics/intrinsic-raw_eq-const-bad.stderr @@ -1,15 +1,15 @@ error[E0080]: reading memory at ALLOC0[0x0..0x4], but memory is uninitialized at [0x1..0x2], and this operation requires initialized memory - --> $DIR/intrinsic-raw_eq-const-bad.rs:4:5 + --> $DIR/intrinsic-raw_eq-const-bad.rs:5:5 | LL | std::intrinsics::raw_eq(&(1_u8, 2_u16), &(1_u8, 2_u16)) | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ evaluation of `RAW_EQ_PADDING` failed here | = note: the raw bytes of the constant (size: 4, align: 2) { - 01 __ 02 00 │ .░.. + HEX_DUMP │ .░.. } error[E0080]: unable to turn pointer into integer - --> $DIR/intrinsic-raw_eq-const-bad.rs:9:5 + --> $DIR/intrinsic-raw_eq-const-bad.rs:10:5 | LL | std::intrinsics::raw_eq(&(&0), &(&1)) | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ evaluation of `RAW_EQ_PTR` failed here @@ -18,7 +18,7 @@ LL | std::intrinsics::raw_eq(&(&0), &(&1)) = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported error[E0080]: accessing memory with alignment 1, but alignment 4 is required - --> $DIR/intrinsic-raw_eq-const-bad.rs:16:5 + --> $DIR/intrinsic-raw_eq-const-bad.rs:17:5 | LL | std::intrinsics::raw_eq(aref, aref) | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ evaluation of `RAW_EQ_NOT_ALIGNED` failed here -- cgit 1.4.1-3-g733a5 From 64ed2b5f3d0d13cbb3206c02cd28a7a527657071 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Thu, 31 Jul 2025 07:58:56 +0200 Subject: rustup --- src/tools/miri/rust-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/miri/rust-version b/src/tools/miri/rust-version index 2178caf6396..1ced6098acf 100644 --- a/src/tools/miri/rust-version +++ b/src/tools/miri/rust-version @@ -1 +1 @@ -733dab558992d902d6d17576de1da768094e2cf3 +32e7a4b92b109c24e9822c862a7c74436b50e564 -- cgit 1.4.1-3-g733a5 From 76a213bb18497b17e2e753c6bfb4e08c9fc36d9a Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Thu, 31 Jul 2025 08:12:43 +0200 Subject: rely on preinstalled rustup on windows-arm --- src/tools/miri/.github/workflows/ci.yml | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/tools/miri/.github/workflows/ci.yml b/src/tools/miri/.github/workflows/ci.yml index ba496d76712..4e55200cddf 100644 --- a/src/tools/miri/.github/workflows/ci.yml +++ b/src/tools/miri/.github/workflows/ci.yml @@ -69,12 +69,6 @@ jobs: sudo apt update # Install needed packages sudo apt install $(echo "libatomic1: zlib1g-dev:" | sed 's/:/:${{ matrix.multiarch }}/g') - - name: Install rustup on Windows ARM - if: ${{ matrix.os == 'windows-11-arm' }} - run: | - curl -LOs https://static.rust-lang.org/rustup/dist/aarch64-pc-windows-msvc/rustup-init.exe - ./rustup-init.exe -y --no-modify-path - echo "$USERPROFILE/.cargo/bin" >> "$GITHUB_PATH" - uses: ./.github/workflows/setup with: toolchain_flags: "--host ${{ matrix.host_target }}" -- cgit 1.4.1-3-g733a5 From 431fc2a7ea59ec16c5ffc6464b31b33745b4c32e Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Thu, 31 Jul 2025 08:06:10 +0200 Subject: bless cargo miri doctest execution --- src/tools/miri/test-cargo-miri/run-test.py | 2 +- src/tools/miri/test-cargo-miri/test.default.stdout.ref | 1 + src/tools/miri/test-cargo-miri/test.filter.stdout.ref | 1 + src/tools/miri/test-cargo-miri/test.multiple_targets.stdout.ref | 2 ++ 4 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/tools/miri/test-cargo-miri/run-test.py b/src/tools/miri/test-cargo-miri/run-test.py index 40bfe7f845f..6b3b6343b82 100755 --- a/src/tools/miri/test-cargo-miri/run-test.py +++ b/src/tools/miri/test-cargo-miri/run-test.py @@ -37,7 +37,7 @@ def cargo_miri(cmd, quiet = True, targets = None): def normalize_stdout(str): str = str.replace("src\\", "src/") # normalize paths across platforms - str = re.sub("finished in \\d+\\.\\d\\ds", "finished in $TIME", str) # the time keeps changing, obviously + str = re.sub("\\b\\d+\\.\\d+s\\b", "$TIME", str) # the time keeps changing, obviously return str def check_output(actual, path, name): diff --git a/src/tools/miri/test-cargo-miri/test.default.stdout.ref b/src/tools/miri/test-cargo-miri/test.default.stdout.ref index 2d74d82f769..ef092ef703b 100644 --- a/src/tools/miri/test-cargo-miri/test.default.stdout.ref +++ b/src/tools/miri/test-cargo-miri/test.default.stdout.ref @@ -14,3 +14,4 @@ running 5 tests ..... test result: ok. 5 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME +all doctests ran in $TIME; merged doctests compilation took $TIME diff --git a/src/tools/miri/test-cargo-miri/test.filter.stdout.ref b/src/tools/miri/test-cargo-miri/test.filter.stdout.ref index b68bc983276..071aa5691c1 100644 --- a/src/tools/miri/test-cargo-miri/test.filter.stdout.ref +++ b/src/tools/miri/test-cargo-miri/test.filter.stdout.ref @@ -15,3 +15,4 @@ running 0 tests test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 5 filtered out; finished in $TIME +all doctests ran in $TIME; merged doctests compilation took $TIME diff --git a/src/tools/miri/test-cargo-miri/test.multiple_targets.stdout.ref b/src/tools/miri/test-cargo-miri/test.multiple_targets.stdout.ref index a376530a8cf..20139e9ffe6 100644 --- a/src/tools/miri/test-cargo-miri/test.multiple_targets.stdout.ref +++ b/src/tools/miri/test-cargo-miri/test.multiple_targets.stdout.ref @@ -25,8 +25,10 @@ running 5 tests ..... test result: ok. 5 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME +all doctests ran in $TIME; merged doctests compilation took $TIME running 5 tests ..... test result: ok. 5 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME +all doctests ran in $TIME; merged doctests compilation took $TIME -- cgit 1.4.1-3-g733a5 From 27478bef40046250b85d2796eb61ab185cd0d7ce Mon Sep 17 00:00:00 2001 From: Samuel Tardieu Date: Mon, 10 Mar 2025 18:39:52 +0100 Subject: Do not use `DerefMut` on `ManuallyDrop<_>` reached through unions --- clippy_lints/src/reference.rs | 32 ++++++++++++++++++++++++- tests/ui/deref_addrof.fixed | 54 ++++++++++++++++++++++++++++++++++++++++++- tests/ui/deref_addrof.rs | 54 ++++++++++++++++++++++++++++++++++++++++++- tests/ui/deref_addrof.stderr | 48 ++++++++++++++++++++++++++++---------- 4 files changed, 173 insertions(+), 15 deletions(-) diff --git a/clippy_lints/src/reference.rs b/clippy_lints/src/reference.rs index cb414d79119..42d1dcaee36 100644 --- a/clippy_lints/src/reference.rs +++ b/clippy_lints/src/reference.rs @@ -1,7 +1,8 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::{SpanRangeExt, snippet_with_applicability}; +use clippy_utils::ty::adjust_derefs_manually_drop; use rustc_errors::Applicability; -use rustc_hir::{Expr, ExprKind, Mutability, UnOp}; +use rustc_hir::{Expr, ExprKind, Mutability, Node, UnOp}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; use rustc_span::{BytePos, Span}; @@ -44,6 +45,7 @@ impl LateLintPass<'_> for DerefAddrOf { // NOTE(tesuji): `*&` forces rustc to const-promote the array to `.rodata` section. // See #12854 for details. && !matches!(addrof_target.kind, ExprKind::Array(_)) + && !is_manually_drop_through_union(cx, addrof_target) && deref_target.span.eq_ctxt(e.span) && !addrof_target.span.from_expansion() { @@ -102,3 +104,31 @@ impl LateLintPass<'_> for DerefAddrOf { } } } + +/// Check if `expr` is part of an access to a `ManuallyDrop` entity reached through a union +fn is_manually_drop_through_union(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { + if is_reached_through_union(cx, expr) { + let typeck = cx.typeck_results(); + for (_, node) in cx.tcx.hir_parent_iter(expr.hir_id) { + if let Node::Expr(expr) = node { + if adjust_derefs_manually_drop(typeck.expr_adjustments(expr), typeck.expr_ty(expr)) { + return true; + } + } else { + break; + } + } + } + false +} + +/// Checks whether `expr` denotes an object reached through a union +fn is_reached_through_union(cx: &LateContext<'_>, mut expr: &Expr<'_>) -> bool { + while let ExprKind::Field(parent, _) | ExprKind::Index(parent, _, _) = expr.kind { + if cx.typeck_results().expr_ty_adjusted(parent).is_union() { + return true; + } + expr = parent; + } + false +} diff --git a/tests/ui/deref_addrof.fixed b/tests/ui/deref_addrof.fixed index 35dbd790e89..6b200edc38b 100644 --- a/tests/ui/deref_addrof.fixed +++ b/tests/ui/deref_addrof.fixed @@ -1,6 +1,11 @@ //@aux-build:proc_macros.rs -#![allow(clippy::return_self_not_must_use, clippy::useless_vec)] +#![allow( + dangerous_implicit_autorefs, + clippy::explicit_auto_deref, + clippy::return_self_not_must_use, + clippy::useless_vec +)] #![warn(clippy::deref_addrof)] extern crate proc_macros; @@ -72,3 +77,50 @@ impl S { //~^ deref_addrof } } + +fn issue14386() { + use std::mem::ManuallyDrop; + + #[derive(Copy, Clone)] + struct Data { + num: u64, + } + + #[derive(Clone, Copy)] + struct M { + md: ManuallyDrop<[u8; 4]>, + } + + union DataWithPadding<'lt> { + data: ManuallyDrop, + prim: ManuallyDrop, + padding: [u8; size_of::()], + tup: (ManuallyDrop, ()), + indirect: M, + indirect_arr: [M; 2], + indirect_ref: &'lt mut M, + } + + let mut a = DataWithPadding { + padding: [0; size_of::()], + }; + unsafe { + a.padding = [1; size_of::()]; + //~^ deref_addrof + a.tup.1 = (); + //~^ deref_addrof + *a.prim = 0; + //~^ deref_addrof + + (*&mut a.data).num = 42; + (*&mut a.tup).0.num = 42; + (*&mut a.indirect.md)[3] = 1; + (*&mut a.indirect_arr[1].md)[3] = 1; + (*&mut a.indirect_ref.md)[3] = 1; + + // Check that raw pointers are properly considered as well + *a.prim = 0; + //~^ deref_addrof + (*&raw mut a.data).num = 42; + } +} diff --git a/tests/ui/deref_addrof.rs b/tests/ui/deref_addrof.rs index 96d1b92ef7b..abc9e819ade 100644 --- a/tests/ui/deref_addrof.rs +++ b/tests/ui/deref_addrof.rs @@ -1,6 +1,11 @@ //@aux-build:proc_macros.rs -#![allow(clippy::return_self_not_must_use, clippy::useless_vec)] +#![allow( + dangerous_implicit_autorefs, + clippy::explicit_auto_deref, + clippy::return_self_not_must_use, + clippy::useless_vec +)] #![warn(clippy::deref_addrof)] extern crate proc_macros; @@ -72,3 +77,50 @@ impl S { //~^ deref_addrof } } + +fn issue14386() { + use std::mem::ManuallyDrop; + + #[derive(Copy, Clone)] + struct Data { + num: u64, + } + + #[derive(Clone, Copy)] + struct M { + md: ManuallyDrop<[u8; 4]>, + } + + union DataWithPadding<'lt> { + data: ManuallyDrop, + prim: ManuallyDrop, + padding: [u8; size_of::()], + tup: (ManuallyDrop, ()), + indirect: M, + indirect_arr: [M; 2], + indirect_ref: &'lt mut M, + } + + let mut a = DataWithPadding { + padding: [0; size_of::()], + }; + unsafe { + (*&mut a.padding) = [1; size_of::()]; + //~^ deref_addrof + (*&mut a.tup).1 = (); + //~^ deref_addrof + **&mut a.prim = 0; + //~^ deref_addrof + + (*&mut a.data).num = 42; + (*&mut a.tup).0.num = 42; + (*&mut a.indirect.md)[3] = 1; + (*&mut a.indirect_arr[1].md)[3] = 1; + (*&mut a.indirect_ref.md)[3] = 1; + + // Check that raw pointers are properly considered as well + **&raw mut a.prim = 0; + //~^ deref_addrof + (*&raw mut a.data).num = 42; + } +} diff --git a/tests/ui/deref_addrof.stderr b/tests/ui/deref_addrof.stderr index 81414b625b2..3c422a4040b 100644 --- a/tests/ui/deref_addrof.stderr +++ b/tests/ui/deref_addrof.stderr @@ -1,5 +1,5 @@ error: immediately dereferencing a reference - --> tests/ui/deref_addrof.rs:23:13 + --> tests/ui/deref_addrof.rs:28:13 | LL | let b = *&a; | ^^^ help: try: `a` @@ -8,55 +8,55 @@ LL | let b = *&a; = help: to override `-D warnings` add `#[allow(clippy::deref_addrof)]` error: immediately dereferencing a reference - --> tests/ui/deref_addrof.rs:26:13 + --> tests/ui/deref_addrof.rs:31:13 | LL | let b = *&get_number(); | ^^^^^^^^^^^^^^ help: try: `get_number()` error: immediately dereferencing a reference - --> tests/ui/deref_addrof.rs:32:13 + --> tests/ui/deref_addrof.rs:37:13 | LL | let b = *&bytes[1..2][0]; | ^^^^^^^^^^^^^^^^ help: try: `bytes[1..2][0]` error: immediately dereferencing a reference - --> tests/ui/deref_addrof.rs:37:13 + --> tests/ui/deref_addrof.rs:42:13 | LL | let b = *&(a); | ^^^^^ help: try: `(a)` error: immediately dereferencing a reference - --> tests/ui/deref_addrof.rs:40:13 + --> tests/ui/deref_addrof.rs:45:13 | LL | let b = *(&a); | ^^^^^ help: try: `a` error: immediately dereferencing a reference - --> tests/ui/deref_addrof.rs:44:13 + --> tests/ui/deref_addrof.rs:49:13 | LL | let b = *((&a)); | ^^^^^^^ help: try: `a` error: immediately dereferencing a reference - --> tests/ui/deref_addrof.rs:47:13 + --> tests/ui/deref_addrof.rs:52:13 | LL | let b = *&&a; | ^^^^ help: try: `&a` error: immediately dereferencing a reference - --> tests/ui/deref_addrof.rs:50:14 + --> tests/ui/deref_addrof.rs:55:14 | LL | let b = **&aref; | ^^^^^^ help: try: `aref` error: immediately dereferencing a reference - --> tests/ui/deref_addrof.rs:55:19 + --> tests/ui/deref_addrof.rs:60:19 | LL | let _repeat = *&[0; 64]; | ^^^^^^^^^ help: try: `[0; 64]` error: immediately dereferencing a reference - --> tests/ui/deref_addrof.rs:66:17 + --> tests/ui/deref_addrof.rs:71:17 | LL | inline!(*& $(@expr self)) | ^^^^^^^^^^^^^^^^ help: try: `$(@expr self)` @@ -64,12 +64,36 @@ LL | inline!(*& $(@expr self)) = note: this error originates in the macro `__inline_mac_impl` (in Nightly builds, run with -Z macro-backtrace for more info) error: immediately dereferencing a reference - --> tests/ui/deref_addrof.rs:71:17 + --> tests/ui/deref_addrof.rs:76:17 | LL | inline!(*&mut $(@expr self)) | ^^^^^^^^^^^^^^^^^^^ help: try: `$(@expr self)` | = note: this error originates in the macro `__inline_mac_impl` (in Nightly builds, run with -Z macro-backtrace for more info) -error: aborting due to 11 previous errors +error: immediately dereferencing a reference + --> tests/ui/deref_addrof.rs:108:9 + | +LL | (*&mut a.padding) = [1; size_of::()]; + | ^^^^^^^^^^^^^^^^^ help: try: `a.padding` + +error: immediately dereferencing a reference + --> tests/ui/deref_addrof.rs:110:9 + | +LL | (*&mut a.tup).1 = (); + | ^^^^^^^^^^^^^ help: try: `a.tup` + +error: immediately dereferencing a reference + --> tests/ui/deref_addrof.rs:112:10 + | +LL | **&mut a.prim = 0; + | ^^^^^^^^^^^^ help: try: `a.prim` + +error: immediately dereferencing a reference + --> tests/ui/deref_addrof.rs:122:10 + | +LL | **&raw mut a.prim = 0; + | ^^^^^^^^^^^^^^^^ help: try: `a.prim` + +error: aborting due to 15 previous errors -- cgit 1.4.1-3-g733a5 From ff496ad34fc823bc063293edbf48f14261c6e28b Mon Sep 17 00:00:00 2001 From: Samuel Tardieu Date: Wed, 7 May 2025 16:28:26 +0200 Subject: Lint more cases when the explicit dereference can be kept in place --- clippy_lints/src/reference.rs | 54 +++++++++++++++++++++++++++++++++++-------- tests/ui/deref_addrof.fixed | 20 +++++++++++----- tests/ui/deref_addrof.rs | 10 +++++++- tests/ui/deref_addrof.stderr | 34 +++++++++++++++++++++++++-- 4 files changed, 99 insertions(+), 19 deletions(-) diff --git a/clippy_lints/src/reference.rs b/clippy_lints/src/reference.rs index 42d1dcaee36..0b8993ee1e1 100644 --- a/clippy_lints/src/reference.rs +++ b/clippy_lints/src/reference.rs @@ -2,7 +2,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::{SpanRangeExt, snippet_with_applicability}; use clippy_utils::ty::adjust_derefs_manually_drop; use rustc_errors::Applicability; -use rustc_hir::{Expr, ExprKind, Mutability, Node, UnOp}; +use rustc_hir::{Expr, ExprKind, HirId, Mutability, Node, UnOp}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; use rustc_span::{BytePos, Span}; @@ -45,10 +45,18 @@ impl LateLintPass<'_> for DerefAddrOf { // NOTE(tesuji): `*&` forces rustc to const-promote the array to `.rodata` section. // See #12854 for details. && !matches!(addrof_target.kind, ExprKind::Array(_)) - && !is_manually_drop_through_union(cx, addrof_target) && deref_target.span.eq_ctxt(e.span) && !addrof_target.span.from_expansion() { + // If this expression is an explicit `DerefMut` of a `ManuallyDrop` reached through a + // union, we may remove the reference if we are at the point where the implicit + // dereference would take place. Otherwise, we should not lint. + let keep_deref = match is_manually_drop_through_union(cx, e.hir_id, addrof_target) { + ManuallyDropThroughUnion::Directly => true, + ManuallyDropThroughUnion::Indirect => return, + ManuallyDropThroughUnion::No => false, + }; + let mut applicability = Applicability::MachineApplicable; let sugg = if e.span.from_expansion() { if let Some(macro_source) = e.span.get_source_text(cx) { @@ -97,7 +105,11 @@ impl LateLintPass<'_> for DerefAddrOf { e.span, "immediately dereferencing a reference", "try", - sugg.to_string(), + if keep_deref { + format!("(*{sugg})") + } else { + sugg.to_string() + }, applicability, ); } @@ -105,21 +117,43 @@ impl LateLintPass<'_> for DerefAddrOf { } } -/// Check if `expr` is part of an access to a `ManuallyDrop` entity reached through a union -fn is_manually_drop_through_union(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { - if is_reached_through_union(cx, expr) { +/// Is this a `ManuallyDrop` reached through a union, and when is `DerefMut` called on it? +enum ManuallyDropThroughUnion { + /// `ManuallyDrop` reached through a union and immediately explicitely dereferenced + Directly, + /// `ManuallyDrop` reached through a union, and dereferenced later on + Indirect, + /// Any other situation + No, +} + +/// Check if `addrof_target` is part of an access to a `ManuallyDrop` entity reached through a +/// union, and when it is dereferenced using `DerefMut` starting from `expr_id` and going up. +fn is_manually_drop_through_union( + cx: &LateContext<'_>, + expr_id: HirId, + addrof_target: &Expr<'_>, +) -> ManuallyDropThroughUnion { + if is_reached_through_union(cx, addrof_target) { let typeck = cx.typeck_results(); - for (_, node) in cx.tcx.hir_parent_iter(expr.hir_id) { - if let Node::Expr(expr) = node { + for (idx, id) in std::iter::once(expr_id) + .chain(cx.tcx.hir_parent_id_iter(expr_id)) + .enumerate() + { + if let Node::Expr(expr) = cx.tcx.hir_node(id) { if adjust_derefs_manually_drop(typeck.expr_adjustments(expr), typeck.expr_ty(expr)) { - return true; + return if idx == 0 { + ManuallyDropThroughUnion::Directly + } else { + ManuallyDropThroughUnion::Indirect + }; } } else { break; } } } - false + ManuallyDropThroughUnion::No } /// Checks whether `expr` denotes an object reached through a union diff --git a/tests/ui/deref_addrof.fixed b/tests/ui/deref_addrof.fixed index 6b200edc38b..ae8ed0dc114 100644 --- a/tests/ui/deref_addrof.fixed +++ b/tests/ui/deref_addrof.fixed @@ -112,15 +112,23 @@ fn issue14386() { *a.prim = 0; //~^ deref_addrof - (*&mut a.data).num = 42; - (*&mut a.tup).0.num = 42; - (*&mut a.indirect.md)[3] = 1; - (*&mut a.indirect_arr[1].md)[3] = 1; - (*&mut a.indirect_ref.md)[3] = 1; + (*a.data).num = 42; + //~^ deref_addrof + (*a.indirect.md)[3] = 1; + //~^ deref_addrof + (*a.indirect_arr[1].md)[3] = 1; + //~^ deref_addrof + (*a.indirect_ref.md)[3] = 1; + //~^ deref_addrof // Check that raw pointers are properly considered as well *a.prim = 0; //~^ deref_addrof - (*&raw mut a.data).num = 42; + (*a.data).num = 42; + //~^ deref_addrof + + // Do not lint, as the dereference happens later, we cannot + // just remove `&mut` + (*&mut a.tup).0.num = 42; } } diff --git a/tests/ui/deref_addrof.rs b/tests/ui/deref_addrof.rs index abc9e819ade..4ff01405916 100644 --- a/tests/ui/deref_addrof.rs +++ b/tests/ui/deref_addrof.rs @@ -113,14 +113,22 @@ fn issue14386() { //~^ deref_addrof (*&mut a.data).num = 42; - (*&mut a.tup).0.num = 42; + //~^ deref_addrof (*&mut a.indirect.md)[3] = 1; + //~^ deref_addrof (*&mut a.indirect_arr[1].md)[3] = 1; + //~^ deref_addrof (*&mut a.indirect_ref.md)[3] = 1; + //~^ deref_addrof // Check that raw pointers are properly considered as well **&raw mut a.prim = 0; //~^ deref_addrof (*&raw mut a.data).num = 42; + //~^ deref_addrof + + // Do not lint, as the dereference happens later, we cannot + // just remove `&mut` + (*&mut a.tup).0.num = 42; } } diff --git a/tests/ui/deref_addrof.stderr b/tests/ui/deref_addrof.stderr index 3c422a4040b..adfa542765c 100644 --- a/tests/ui/deref_addrof.stderr +++ b/tests/ui/deref_addrof.stderr @@ -90,10 +90,40 @@ LL | **&mut a.prim = 0; | ^^^^^^^^^^^^ help: try: `a.prim` error: immediately dereferencing a reference - --> tests/ui/deref_addrof.rs:122:10 + --> tests/ui/deref_addrof.rs:115:9 + | +LL | (*&mut a.data).num = 42; + | ^^^^^^^^^^^^^^ help: try: `(*a.data)` + +error: immediately dereferencing a reference + --> tests/ui/deref_addrof.rs:117:9 + | +LL | (*&mut a.indirect.md)[3] = 1; + | ^^^^^^^^^^^^^^^^^^^^^ help: try: `(*a.indirect.md)` + +error: immediately dereferencing a reference + --> tests/ui/deref_addrof.rs:119:9 + | +LL | (*&mut a.indirect_arr[1].md)[3] = 1; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `(*a.indirect_arr[1].md)` + +error: immediately dereferencing a reference + --> tests/ui/deref_addrof.rs:121:9 + | +LL | (*&mut a.indirect_ref.md)[3] = 1; + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `(*a.indirect_ref.md)` + +error: immediately dereferencing a reference + --> tests/ui/deref_addrof.rs:125:10 | LL | **&raw mut a.prim = 0; | ^^^^^^^^^^^^^^^^ help: try: `a.prim` -error: aborting due to 15 previous errors +error: immediately dereferencing a reference + --> tests/ui/deref_addrof.rs:127:9 + | +LL | (*&raw mut a.data).num = 42; + | ^^^^^^^^^^^^^^^^^^ help: try: `(*a.data)` + +error: aborting due to 20 previous errors -- cgit 1.4.1-3-g733a5 From 7224dffc65e7082d2366aa2ca3f7620f93f5439b Mon Sep 17 00:00:00 2001 From: Samuel Tardieu Date: Wed, 7 May 2025 17:39:58 +0200 Subject: Do not lint data coming from macro Suggesting to remove `*&` or `*&mut` in a macro may be incorrect, as it would require tracking all possible macro usages. In some cases, it is not possible to remove the dereference or the reference. For example, in the following code, the `drmut!()` macro will be linted against while called as `drmut!(d)`, and the suggestion would be to remove the `*&mut`. That would make `drmut!(u.data).num = 1` invalid, as a `ManuallyDrop` object coming through a union cannot be implicitely dereferenced through `DerefMut`. ```rust use std::mem::ManuallyDrop; #[derive(Copy, Clone)] struct Data { num: u64, } union Union { data: ManuallyDrop, } macro_rules! drmut { ($e:expr) => { *&mut $e }; } fn f(mut u: Union, mut d: Data) { unsafe { drmut!(u.data).num = 1; } drmut!(d).num = 1; } ``` --- clippy_lints/src/reference.rs | 88 +++++++++++++------------------------------ tests/ui/deref_addrof.fixed | 32 +++++++--------- tests/ui/deref_addrof.rs | 32 +++++++--------- tests/ui/deref_addrof.stderr | 54 ++++++++++---------------- 4 files changed, 73 insertions(+), 133 deletions(-) diff --git a/clippy_lints/src/reference.rs b/clippy_lints/src/reference.rs index 0b8993ee1e1..3bbcad12a31 100644 --- a/clippy_lints/src/reference.rs +++ b/clippy_lints/src/reference.rs @@ -1,11 +1,11 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::source::{SpanRangeExt, snippet_with_applicability}; +use clippy_utils::source::snippet; +use clippy_utils::sugg::{Sugg, has_enclosing_paren}; use clippy_utils::ty::adjust_derefs_manually_drop; use rustc_errors::Applicability; -use rustc_hir::{Expr, ExprKind, HirId, Mutability, Node, UnOp}; +use rustc_hir::{Expr, ExprKind, HirId, Node, UnOp}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; -use rustc_span::{BytePos, Span}; declare_clippy_lint! { /// ### What it does @@ -40,79 +40,43 @@ declare_lint_pass!(DerefAddrOf => [DEREF_ADDROF]); impl LateLintPass<'_> for DerefAddrOf { fn check_expr(&mut self, cx: &LateContext<'_>, e: &Expr<'_>) { - if let ExprKind::Unary(UnOp::Deref, deref_target) = e.kind - && let ExprKind::AddrOf(_, mutability, addrof_target) = deref_target.kind + if !e.span.from_expansion() + && let ExprKind::Unary(UnOp::Deref, deref_target) = e.kind + && !deref_target.span.from_expansion() + && let ExprKind::AddrOf(_, _, addrof_target) = deref_target.kind // NOTE(tesuji): `*&` forces rustc to const-promote the array to `.rodata` section. // See #12854 for details. && !matches!(addrof_target.kind, ExprKind::Array(_)) && deref_target.span.eq_ctxt(e.span) && !addrof_target.span.from_expansion() { + let mut applicability = Applicability::MachineApplicable; + let mut sugg = || Sugg::hir_with_applicability(cx, addrof_target, "_", &mut applicability); + // If this expression is an explicit `DerefMut` of a `ManuallyDrop` reached through a // union, we may remove the reference if we are at the point where the implicit // dereference would take place. Otherwise, we should not lint. - let keep_deref = match is_manually_drop_through_union(cx, e.hir_id, addrof_target) { - ManuallyDropThroughUnion::Directly => true, + let sugg = match is_manually_drop_through_union(cx, e.hir_id, addrof_target) { + ManuallyDropThroughUnion::Directly => sugg().deref(), ManuallyDropThroughUnion::Indirect => return, - ManuallyDropThroughUnion::No => false, + ManuallyDropThroughUnion::No => sugg(), }; - let mut applicability = Applicability::MachineApplicable; - let sugg = if e.span.from_expansion() { - if let Some(macro_source) = e.span.get_source_text(cx) { - // Remove leading whitespace from the given span - // e.g: ` $visitor` turns into `$visitor` - let trim_leading_whitespaces = |span: Span| { - span.get_source_text(cx) - .and_then(|snip| { - #[expect(clippy::cast_possible_truncation)] - snip.find(|c: char| !c.is_whitespace()) - .map(|pos| span.lo() + BytePos(pos as u32)) - }) - .map_or(span, |start_no_whitespace| e.span.with_lo(start_no_whitespace)) - }; - - let mut generate_snippet = |pattern: &str| { - #[expect(clippy::cast_possible_truncation)] - macro_source.rfind(pattern).map(|pattern_pos| { - let rpos = pattern_pos + pattern.len(); - let span_after_ref = e.span.with_lo(BytePos(e.span.lo().0 + rpos as u32)); - let span = trim_leading_whitespaces(span_after_ref); - snippet_with_applicability(cx, span, "_", &mut applicability) - }) - }; - - if mutability == Mutability::Mut { - generate_snippet("mut") - } else { - generate_snippet("&") - } - } else { - Some(snippet_with_applicability(cx, e.span, "_", &mut applicability)) - } + let sugg = if has_enclosing_paren(snippet(cx, e.span, "")) { + sugg.maybe_paren() } else { - Some(snippet_with_applicability( - cx, - addrof_target.span, - "_", - &mut applicability, - )) + sugg }; - if let Some(sugg) = sugg { - span_lint_and_sugg( - cx, - DEREF_ADDROF, - e.span, - "immediately dereferencing a reference", - "try", - if keep_deref { - format!("(*{sugg})") - } else { - sugg.to_string() - }, - applicability, - ); - } + + span_lint_and_sugg( + cx, + DEREF_ADDROF, + e.span, + "immediately dereferencing a reference", + "try", + sugg.to_string(), + applicability, + ); } } } diff --git a/tests/ui/deref_addrof.fixed b/tests/ui/deref_addrof.fixed index ae8ed0dc114..ffe7f7d1440 100644 --- a/tests/ui/deref_addrof.fixed +++ b/tests/ui/deref_addrof.fixed @@ -1,5 +1,3 @@ -//@aux-build:proc_macros.rs - #![allow( dangerous_implicit_autorefs, clippy::explicit_auto_deref, @@ -8,9 +6,6 @@ )] #![warn(clippy::deref_addrof)] -extern crate proc_macros; -use proc_macros::inline_macros; - fn get_number() -> usize { 10 } @@ -61,21 +56,22 @@ fn main() { //~^ deref_addrof // do NOT lint for array as semantic differences with/out `*&`. let _arr = *&[0, 1, 2, 3, 4]; -} -#[derive(Copy, Clone)] -pub struct S; -#[inline_macros] -impl S { - pub fn f(&self) -> &Self { - inline!($(@expr self)) - //~^ deref_addrof - } - #[allow(unused_mut)] // mut will be unused, once the macro is fixed - pub fn f_mut(mut self) -> Self { - inline!($(@expr self)) - //~^ deref_addrof + // Do not lint when text comes from macro + macro_rules! mac { + (dr) => { + *&0 + }; + (dr $e:expr) => { + *&$e + }; + (r $e:expr) => { + &$e + }; } + let b = mac!(dr); + let b = mac!(dr a); + let b = *mac!(r a); } fn issue14386() { diff --git a/tests/ui/deref_addrof.rs b/tests/ui/deref_addrof.rs index 4ff01405916..bc253716aff 100644 --- a/tests/ui/deref_addrof.rs +++ b/tests/ui/deref_addrof.rs @@ -1,5 +1,3 @@ -//@aux-build:proc_macros.rs - #![allow( dangerous_implicit_autorefs, clippy::explicit_auto_deref, @@ -8,9 +6,6 @@ )] #![warn(clippy::deref_addrof)] -extern crate proc_macros; -use proc_macros::inline_macros; - fn get_number() -> usize { 10 } @@ -61,21 +56,22 @@ fn main() { //~^ deref_addrof // do NOT lint for array as semantic differences with/out `*&`. let _arr = *&[0, 1, 2, 3, 4]; -} -#[derive(Copy, Clone)] -pub struct S; -#[inline_macros] -impl S { - pub fn f(&self) -> &Self { - inline!(*& $(@expr self)) - //~^ deref_addrof - } - #[allow(unused_mut)] // mut will be unused, once the macro is fixed - pub fn f_mut(mut self) -> Self { - inline!(*&mut $(@expr self)) - //~^ deref_addrof + // Do not lint when text comes from macro + macro_rules! mac { + (dr) => { + *&0 + }; + (dr $e:expr) => { + *&$e + }; + (r $e:expr) => { + &$e + }; } + let b = mac!(dr); + let b = mac!(dr a); + let b = *mac!(r a); } fn issue14386() { diff --git a/tests/ui/deref_addrof.stderr b/tests/ui/deref_addrof.stderr index adfa542765c..65dd904a8f7 100644 --- a/tests/ui/deref_addrof.stderr +++ b/tests/ui/deref_addrof.stderr @@ -1,5 +1,5 @@ error: immediately dereferencing a reference - --> tests/ui/deref_addrof.rs:28:13 + --> tests/ui/deref_addrof.rs:23:13 | LL | let b = *&a; | ^^^ help: try: `a` @@ -8,122 +8,106 @@ LL | let b = *&a; = help: to override `-D warnings` add `#[allow(clippy::deref_addrof)]` error: immediately dereferencing a reference - --> tests/ui/deref_addrof.rs:31:13 + --> tests/ui/deref_addrof.rs:26:13 | LL | let b = *&get_number(); | ^^^^^^^^^^^^^^ help: try: `get_number()` error: immediately dereferencing a reference - --> tests/ui/deref_addrof.rs:37:13 + --> tests/ui/deref_addrof.rs:32:13 | LL | let b = *&bytes[1..2][0]; | ^^^^^^^^^^^^^^^^ help: try: `bytes[1..2][0]` error: immediately dereferencing a reference - --> tests/ui/deref_addrof.rs:42:13 + --> tests/ui/deref_addrof.rs:37:13 | LL | let b = *&(a); | ^^^^^ help: try: `(a)` error: immediately dereferencing a reference - --> tests/ui/deref_addrof.rs:45:13 + --> tests/ui/deref_addrof.rs:40:13 | LL | let b = *(&a); | ^^^^^ help: try: `a` error: immediately dereferencing a reference - --> tests/ui/deref_addrof.rs:49:13 + --> tests/ui/deref_addrof.rs:44:13 | LL | let b = *((&a)); | ^^^^^^^ help: try: `a` error: immediately dereferencing a reference - --> tests/ui/deref_addrof.rs:52:13 + --> tests/ui/deref_addrof.rs:47:13 | LL | let b = *&&a; | ^^^^ help: try: `&a` error: immediately dereferencing a reference - --> tests/ui/deref_addrof.rs:55:14 + --> tests/ui/deref_addrof.rs:50:14 | LL | let b = **&aref; | ^^^^^^ help: try: `aref` error: immediately dereferencing a reference - --> tests/ui/deref_addrof.rs:60:19 + --> tests/ui/deref_addrof.rs:55:19 | LL | let _repeat = *&[0; 64]; | ^^^^^^^^^ help: try: `[0; 64]` error: immediately dereferencing a reference - --> tests/ui/deref_addrof.rs:71:17 - | -LL | inline!(*& $(@expr self)) - | ^^^^^^^^^^^^^^^^ help: try: `$(@expr self)` - | - = note: this error originates in the macro `__inline_mac_impl` (in Nightly builds, run with -Z macro-backtrace for more info) - -error: immediately dereferencing a reference - --> tests/ui/deref_addrof.rs:76:17 - | -LL | inline!(*&mut $(@expr self)) - | ^^^^^^^^^^^^^^^^^^^ help: try: `$(@expr self)` - | - = note: this error originates in the macro `__inline_mac_impl` (in Nightly builds, run with -Z macro-backtrace for more info) - -error: immediately dereferencing a reference - --> tests/ui/deref_addrof.rs:108:9 + --> tests/ui/deref_addrof.rs:104:9 | LL | (*&mut a.padding) = [1; size_of::()]; | ^^^^^^^^^^^^^^^^^ help: try: `a.padding` error: immediately dereferencing a reference - --> tests/ui/deref_addrof.rs:110:9 + --> tests/ui/deref_addrof.rs:106:9 | LL | (*&mut a.tup).1 = (); | ^^^^^^^^^^^^^ help: try: `a.tup` error: immediately dereferencing a reference - --> tests/ui/deref_addrof.rs:112:10 + --> tests/ui/deref_addrof.rs:108:10 | LL | **&mut a.prim = 0; | ^^^^^^^^^^^^ help: try: `a.prim` error: immediately dereferencing a reference - --> tests/ui/deref_addrof.rs:115:9 + --> tests/ui/deref_addrof.rs:111:9 | LL | (*&mut a.data).num = 42; | ^^^^^^^^^^^^^^ help: try: `(*a.data)` error: immediately dereferencing a reference - --> tests/ui/deref_addrof.rs:117:9 + --> tests/ui/deref_addrof.rs:113:9 | LL | (*&mut a.indirect.md)[3] = 1; | ^^^^^^^^^^^^^^^^^^^^^ help: try: `(*a.indirect.md)` error: immediately dereferencing a reference - --> tests/ui/deref_addrof.rs:119:9 + --> tests/ui/deref_addrof.rs:115:9 | LL | (*&mut a.indirect_arr[1].md)[3] = 1; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `(*a.indirect_arr[1].md)` error: immediately dereferencing a reference - --> tests/ui/deref_addrof.rs:121:9 + --> tests/ui/deref_addrof.rs:117:9 | LL | (*&mut a.indirect_ref.md)[3] = 1; | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `(*a.indirect_ref.md)` error: immediately dereferencing a reference - --> tests/ui/deref_addrof.rs:125:10 + --> tests/ui/deref_addrof.rs:121:10 | LL | **&raw mut a.prim = 0; | ^^^^^^^^^^^^^^^^ help: try: `a.prim` error: immediately dereferencing a reference - --> tests/ui/deref_addrof.rs:127:9 + --> tests/ui/deref_addrof.rs:123:9 | LL | (*&raw mut a.data).num = 42; | ^^^^^^^^^^^^^^^^^^ help: try: `(*a.data)` -error: aborting due to 20 previous errors +error: aborting due to 18 previous errors -- cgit 1.4.1-3-g733a5 From f4b3415271faa86e0d93cebfad9703b590a55983 Mon Sep 17 00:00:00 2001 From: Bruno Roy Date: Thu, 31 Jul 2025 18:52:33 -0400 Subject: [Doc] Add links to the various collections Add a few links to the collections mentioned in the module doc for Collections. --- library/std/src/collections/mod.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/library/std/src/collections/mod.rs b/library/std/src/collections/mod.rs index 889ed3c5380..6104a02c739 100644 --- a/library/std/src/collections/mod.rs +++ b/library/std/src/collections/mod.rs @@ -26,7 +26,7 @@ //! should be considered. Detailed discussions of strengths and weaknesses of //! individual collections can be found on their own documentation pages. //! -//! ### Use a `Vec` when: +//! ### Use a [`Vec`] when: //! * You want to collect items up to be processed or sent elsewhere later, and //! don't care about any properties of the actual values being stored. //! * You want a sequence of elements in a particular order, and will only be @@ -35,25 +35,25 @@ //! * You want a resizable array. //! * You want a heap-allocated array. //! -//! ### Use a `VecDeque` when: +//! ### Use a [`VecDeque`] when: //! * You want a [`Vec`] that supports efficient insertion at both ends of the //! sequence. //! * You want a queue. //! * You want a double-ended queue (deque). //! -//! ### Use a `LinkedList` when: +//! ### Use a [`LinkedList`] when: //! * You want a [`Vec`] or [`VecDeque`] of unknown size, and can't tolerate //! amortization. //! * You want to efficiently split and append lists. //! * You are *absolutely* certain you *really*, *truly*, want a doubly linked //! list. //! -//! ### Use a `HashMap` when: +//! ### Use a [`HashMap`] when: //! * You want to associate arbitrary keys with an arbitrary value. //! * You want a cache. //! * You want a map, with no extra functionality. //! -//! ### Use a `BTreeMap` when: +//! ### Use a [`BTreeMap`] when: //! * You want a map sorted by its keys. //! * You want to be able to get a range of entries on-demand. //! * You're interested in what the smallest or largest key-value pair is. @@ -65,7 +65,7 @@ //! * There is no meaningful value to associate with your keys. //! * You just want a set. //! -//! ### Use a `BinaryHeap` when: +//! ### Use a [`BinaryHeap`] when: //! //! * You want to store a bunch of elements, but only ever want to process the //! "biggest" or "most important" one at any given time. -- cgit 1.4.1-3-g733a5 From 24c770ba1c53c4c04acdba945cd9f59d687f4d5b Mon Sep 17 00:00:00 2001 From: Trevor Gross Date: Fri, 1 Aug 2025 00:32:56 +0000 Subject: aarch64: Make `outline-atomics` a known target feature This is a feature used by LLVM that is enabled for our `aarch64-linux` targets, which we would like to configure on in `std`. Thus, mark `outline-atomics` a known feature. It is left unstable for now. --- compiler/rustc_target/src/target_features.rs | 4 ++++ tests/assembly-llvm/asm/aarch64-outline-atomics.rs | 4 ++++ tests/ui/check-cfg/target_feature.stderr | 1 + 3 files changed, 9 insertions(+) diff --git a/compiler/rustc_target/src/target_features.rs b/compiler/rustc_target/src/target_features.rs index b2af99228fe..7551d5a66fc 100644 --- a/compiler/rustc_target/src/target_features.rs +++ b/compiler/rustc_target/src/target_features.rs @@ -248,6 +248,10 @@ static AARCH64_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ ("mte", Stable, &[]), // FEAT_AdvSimd & FEAT_FP ("neon", Stable, &[]), + // Backend option to turn atomic operations into an intrinsic call when `lse` is not known to be + // available, so the intrinsic can do runtime LSE feature detection rather than unconditionally + // using slower non-LSE operations. Unstable since it doesn't need to user-togglable. + ("outline-atomics", Unstable(sym::aarch64_unstable_target_feature), &[]), // FEAT_PAUTH (address authentication) ("paca", Stable, &[]), // FEAT_PAUTH (generic authentication) diff --git a/tests/assembly-llvm/asm/aarch64-outline-atomics.rs b/tests/assembly-llvm/asm/aarch64-outline-atomics.rs index 22599c18dcf..1177c1e68ed 100644 --- a/tests/assembly-llvm/asm/aarch64-outline-atomics.rs +++ b/tests/assembly-llvm/asm/aarch64-outline-atomics.rs @@ -8,6 +8,10 @@ use std::sync::atomic::AtomicI32; use std::sync::atomic::Ordering::*; +// Verify config on outline-atomics works (it is always enabled on aarch64-linux). +#[cfg(not(target_feature = "outline-atomics"))] +compile_error!("outline-atomics is not enabled"); + pub fn compare_exchange(a: &AtomicI32) { // On AArch64 LLVM should outline atomic operations. // CHECK: __aarch64_cas4_relax diff --git a/tests/ui/check-cfg/target_feature.stderr b/tests/ui/check-cfg/target_feature.stderr index f422919983b..e730492fd9b 100644 --- a/tests/ui/check-cfg/target_feature.stderr +++ b/tests/ui/check-cfg/target_feature.stderr @@ -183,6 +183,7 @@ LL | cfg!(target_feature = "_UNEXPECTED_VALUE"); `nnp-assist` `nontrapping-fptoint` `nvic` +`outline-atomics` `paca` `pacg` `pan` -- cgit 1.4.1-3-g733a5 From 613080b5f1d58a2496d9f8dc8c314d5432fe3d6f Mon Sep 17 00:00:00 2001 From: Jeremy Smart Date: Thu, 31 Jul 2025 23:46:04 -0400 Subject: add Option::reduce --- library/core/src/option.rs | 36 ++++++++++++++++++++++++++++++++++++ library/coretests/tests/lib.rs | 1 + 2 files changed, 37 insertions(+) diff --git a/library/core/src/option.rs b/library/core/src/option.rs index 0e481519f76..f3ddc05bc1a 100644 --- a/library/core/src/option.rs +++ b/library/core/src/option.rs @@ -1961,6 +1961,42 @@ impl Option { _ => None, } } + + /// Reduces two options into one, using the provided function if both are `Some`. + /// + /// If `self` is `Some(s)` and `other` is `Some(o)`, this method returns `Some(f(s, o))`. + /// Otherwise, if only one of `self` and `other` is `Some`, that one is returned. + /// If both `self` and `other` are `None`, `None` is returned. + /// + /// # Examples + /// + /// ``` + /// #![feature(option_reduce)] + /// + /// let s12 = Some(12); + /// let s17 = Some(17); + /// let n = None; + /// let f = |a, b| a + b; + /// + /// assert_eq!(s12.reduce(s17, f), Some(29)); + /// assert_eq!(s12.reduce(n, f), Some(12)); + /// assert_eq!(n.reduce(s17, f), Some(17)); + /// assert_eq!(n.reduce(n, f), None); + /// ``` + #[unstable(feature = "option_reduce", issue = "144273")] + pub fn reduce(self, other: Option, f: F) -> Option + where + T: Into, + U: Into, + F: FnOnce(T, U) -> R, + { + match (self, other) { + (Some(a), Some(b)) => Some(f(a, b)), + (Some(a), _) => Some(a.into()), + (_, Some(b)) => Some(b.into()), + _ => None, + } + } } impl Option<(T, U)> { diff --git a/library/coretests/tests/lib.rs b/library/coretests/tests/lib.rs index 4cfac9ecc2a..3f817ab128b 100644 --- a/library/coretests/tests/lib.rs +++ b/library/coretests/tests/lib.rs @@ -77,6 +77,7 @@ #![feature(never_type)] #![feature(next_index)] #![feature(numfmt)] +#![feature(option_reduce)] #![feature(pattern)] #![feature(pointer_is_aligned_to)] #![feature(portable_simd)] -- cgit 1.4.1-3-g733a5 From ad61b3113c7ce43c60134bf5441397b82898a78c Mon Sep 17 00:00:00 2001 From: Trevor Gross Date: Thu, 31 Jul 2025 22:53:27 -0500 Subject: Correct some grammar in integer documentation Update "between" to "among" (more than two items), connect the "which" dependent clause to the independent part, and remove the redundant "here". --- library/core/src/num/int_macros.rs | 9 +++------ library/core/src/num/saturating.rs | 4 ++-- library/core/src/num/uint_macros.rs | 13 +++++-------- library/core/src/num/wrapping.rs | 4 ++-- 4 files changed, 12 insertions(+), 18 deletions(-) diff --git a/library/core/src/num/int_macros.rs b/library/core/src/num/int_macros.rs index 5683d5ec92d..7fa2a3e45f3 100644 --- a/library/core/src/num/int_macros.rs +++ b/library/core/src/num/int_macros.rs @@ -2512,8 +2512,7 @@ macro_rules! int_impl { /// /// # Examples /// - /// Please note that this example is shared between integer types. - /// Which explains why `i32` is used here. + /// Please note that this example is shared among integer types, which is why `i32` is used. /// /// ``` /// #![feature(bigint_helper_methods)] @@ -2543,8 +2542,7 @@ macro_rules! int_impl { /// /// # Examples /// - /// Please note that this example is shared between integer types. - /// Which explains why `i32` is used here. + /// Please note that this example is shared among integer types, which is why `i32` is used. /// /// ``` /// #![feature(bigint_helper_methods)] @@ -2581,8 +2579,7 @@ macro_rules! int_impl { /// /// # Examples /// - /// Please note that this example is shared between integer types. - /// Which explains why `i32` is used here. + /// Please note that this example is shared among integer types, which is why `i32` is used. /// /// ``` /// #![feature(bigint_helper_methods)] diff --git a/library/core/src/num/saturating.rs b/library/core/src/num/saturating.rs index 4460e430aec..e3a79dca764 100644 --- a/library/core/src/num/saturating.rs +++ b/library/core/src/num/saturating.rs @@ -660,8 +660,8 @@ macro_rules! saturating_int_impl { /// /// # Examples /// - /// Please note that this example is shared between integer types. - /// Which explains why `i16` is used here. + /// Please note that this example is shared among integer types, which is why `i16` + /// is used. /// /// ``` /// use std::num::Saturating; diff --git a/library/core/src/num/uint_macros.rs b/library/core/src/num/uint_macros.rs index 584cd60fbe5..84d8de665b2 100644 --- a/library/core/src/num/uint_macros.rs +++ b/library/core/src/num/uint_macros.rs @@ -2136,8 +2136,7 @@ macro_rules! uint_impl { /// /// # Examples /// - /// Please note that this example is shared between integer types. - /// Which explains why `u8` is used here. + /// Please note that this example is shared among integer types, which is why `u8` is used. /// /// ``` /// assert_eq!(10u8.wrapping_mul(12), 120); @@ -2627,8 +2626,8 @@ macro_rules! uint_impl { /// /// # Examples /// - /// Please note that this example is shared between integer types. - /// Which explains why `u32` is used here. + /// Please note that this example is shared among integer types, which is why why `u32` + /// is used. /// /// ``` /// assert_eq!(5u32.overflowing_mul(2), (10, false)); @@ -2654,8 +2653,7 @@ macro_rules! uint_impl { /// /// # Examples /// - /// Please note that this example is shared between integer types. - /// Which explains why `u32` is used here. + /// Please note that this example is shared among integer types, which is why `u32` is used. /// /// ``` /// #![feature(bigint_helper_methods)] @@ -2685,8 +2683,7 @@ macro_rules! uint_impl { /// /// # Examples /// - /// Please note that this example is shared between integer types. - /// Which explains why `u32` is used here. + /// Please note that this example is shared among integer types, which is why `u32` is used. /// /// ``` /// #![feature(bigint_helper_methods)] diff --git a/library/core/src/num/wrapping.rs b/library/core/src/num/wrapping.rs index c460f38bd2e..e91280e056d 100644 --- a/library/core/src/num/wrapping.rs +++ b/library/core/src/num/wrapping.rs @@ -677,8 +677,8 @@ macro_rules! wrapping_int_impl { /// /// # Examples /// - /// Please note that this example is shared between integer types. - /// Which explains why `i16` is used here. + /// Please note that this example is shared among integer types, which is why `i16` + /// is used. /// /// Basic usage: /// -- cgit 1.4.1-3-g733a5 From 67cce09bfb87c896ec83117d0d61f847cdb8de60 Mon Sep 17 00:00:00 2001 From: The Miri Cronjob Bot Date: Fri, 1 Aug 2025 05:06:37 +0000 Subject: Prepare for merging from rust-lang/rust This updates the rust-version file to adcb3d3b4cd3b7c4cde642f3ed537037f293738e. --- src/tools/miri/rust-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/miri/rust-version b/src/tools/miri/rust-version index 1ced6098acf..bf69accaf06 100644 --- a/src/tools/miri/rust-version +++ b/src/tools/miri/rust-version @@ -1 +1 @@ -32e7a4b92b109c24e9822c862a7c74436b50e564 +adcb3d3b4cd3b7c4cde642f3ed537037f293738e -- cgit 1.4.1-3-g733a5 From 0ff984894b263ba3efaee10a32ece57e50b2320a Mon Sep 17 00:00:00 2001 From: The Miri Cronjob Bot Date: Fri, 1 Aug 2025 05:14:44 +0000 Subject: fmt --- src/tools/miri/src/machine.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/miri/src/machine.rs b/src/tools/miri/src/machine.rs index 00c3373bb0f..757244c11a6 100644 --- a/src/tools/miri/src/machine.rs +++ b/src/tools/miri/src/machine.rs @@ -12,10 +12,10 @@ use rand::rngs::StdRng; use rand::{Rng, SeedableRng}; use rustc_abi::{Align, ExternAbi, Size}; use rustc_apfloat::{Float, FloatConvert}; -use rustc_hir::attrs::InlineAttr; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; #[allow(unused)] use rustc_data_structures::static_assert_size; +use rustc_hir::attrs::InlineAttr; use rustc_middle::mir; use rustc_middle::query::TyCtxtAt; use rustc_middle::ty::layout::{ -- cgit 1.4.1-3-g733a5 From 6ab38e446a1e4053f515922d4d1456d3a95c1a36 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Fri, 1 Aug 2025 08:57:31 +0200 Subject: update rustup PR message --- src/tools/miri/.github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/miri/.github/workflows/ci.yml b/src/tools/miri/.github/workflows/ci.yml index 4e55200cddf..7d79c384f85 100644 --- a/src/tools/miri/.github/workflows/ci.yml +++ b/src/tools/miri/.github/workflows/ci.yml @@ -198,7 +198,7 @@ jobs: BRANCH="rustup-$(date -u +%Y-%m-%d)" git switch -c $BRANCH git push -u origin $BRANCH - gh pr create -B master --title 'Automatic Rustup' --body 'Please close and re-open this PR to trigger CI, then enable auto-merge.' + gh pr create -B master --title 'Automatic Rustup' --body "Update \`rustc\` to https://github.com/rust-lang/rust/commit/$(cat rust-version)." env: GITHUB_TOKEN: ${{ steps.app-token.outputs.token }} -- cgit 1.4.1-3-g733a5 From a067c6a3c6ac4db7652e1722acf2648ef65b0229 Mon Sep 17 00:00:00 2001 From: Waffle Lapkin Date: Sun, 1 Jun 2025 12:51:17 +0200 Subject: refactor `unreachable/expr_cast.rs` test --- tests/ui/reachable/expr_cast.rs | 15 +++++++-------- tests/ui/reachable/expr_cast.stderr | 26 ++++++++++++++------------ 2 files changed, 21 insertions(+), 20 deletions(-) diff --git a/tests/ui/reachable/expr_cast.rs b/tests/ui/reachable/expr_cast.rs index e8e477ea4f6..58ebe2a7b9f 100644 --- a/tests/ui/reachable/expr_cast.rs +++ b/tests/ui/reachable/expr_cast.rs @@ -1,13 +1,12 @@ -#![allow(unused_variables)] -#![allow(unused_assignments)] -#![allow(dead_code)] +//@ edition: 2024 #![deny(unreachable_code)] -#![feature(never_type, type_ascription)] fn a() { - // the cast is unreachable: - let x = {return} as !; //~ ERROR unreachable - //~| ERROR non-primitive cast + _ = {return} as u32; //~ error: unreachable } -fn main() { } +fn b() { + (return) as u32; //~ error: unreachable +} + +fn main() {} diff --git a/tests/ui/reachable/expr_cast.stderr b/tests/ui/reachable/expr_cast.stderr index 6643f1784a1..cf711d4436e 100644 --- a/tests/ui/reachable/expr_cast.stderr +++ b/tests/ui/reachable/expr_cast.stderr @@ -1,24 +1,26 @@ error: unreachable expression - --> $DIR/expr_cast.rs:9:13 + --> $DIR/expr_cast.rs:5:9 | -LL | let x = {return} as !; - | ^------^^^^^^ - | || - | |any code following this expression is unreachable - | unreachable expression +LL | _ = {return} as u32; + | ^------^^^^^^^^ + | || + | |any code following this expression is unreachable + | unreachable expression | note: the lint level is defined here - --> $DIR/expr_cast.rs:4:9 + --> $DIR/expr_cast.rs:2:9 | LL | #![deny(unreachable_code)] | ^^^^^^^^^^^^^^^^ -error[E0605]: non-primitive cast: `()` as `!` - --> $DIR/expr_cast.rs:9:13 +error: unreachable expression + --> $DIR/expr_cast.rs:9:5 | -LL | let x = {return} as !; - | ^^^^^^^^^^^^^ an `as` expression can only be used to convert between primitive types or to coerce to a specific trait object +LL | (return) as u32; + | --------^^^^^^^ + | | + | unreachable expression + | any code following this expression is unreachable error: aborting due to 2 previous errors -For more information about this error, try `rustc --explain E0605`. -- cgit 1.4.1-3-g733a5 From d2e133d96a2a7b0063cfd514a35304e4908dcb6f Mon Sep 17 00:00:00 2001 From: Waffle Lapkin Date: Sun, 1 Jun 2025 12:51:17 +0200 Subject: don't warn on explicit casts of never to any --- compiler/rustc_hir_typeck/src/expr.rs | 3 +++ tests/ui/reachable/expr_cast.rs | 13 ++++++++++-- tests/ui/reachable/expr_cast.stderr | 26 ----------------------- tests/ui/reachable/unreachable-try-pattern.rs | 2 +- tests/ui/reachable/unreachable-try-pattern.stderr | 11 +++++----- 5 files changed, 20 insertions(+), 35 deletions(-) delete mode 100644 tests/ui/reachable/expr_cast.stderr diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs index 454ec7ddcaf..74136b54628 100644 --- a/compiler/rustc_hir_typeck/src/expr.rs +++ b/compiler/rustc_hir_typeck/src/expr.rs @@ -290,6 +290,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { | ExprKind::Let(..) | ExprKind::Loop(..) | ExprKind::Match(..) => {} + // Do not warn on `as` casts from never to any, + // they are sometimes required to appeal typeck. + ExprKind::Cast(_, _) => {} // If `expr` is a result of desugaring the try block and is an ok-wrapped // diverging expression (e.g. it arose from desugaring of `try { return }`), // we skip issuing a warning because it is autogenerated code. diff --git a/tests/ui/reachable/expr_cast.rs b/tests/ui/reachable/expr_cast.rs index 58ebe2a7b9f..aa412c99b2e 100644 --- a/tests/ui/reachable/expr_cast.rs +++ b/tests/ui/reachable/expr_cast.rs @@ -1,12 +1,21 @@ +//@ check-pass //@ edition: 2024 +// +// Check that we don't warn on `as` casts of never to any as unreachable. +// While they *are* unreachable, sometimes they are required to appeal typeck. #![deny(unreachable_code)] fn a() { - _ = {return} as u32; //~ error: unreachable + _ = {return} as u32; } fn b() { - (return) as u32; //~ error: unreachable + (return) as u32; +} + +// example that needs an explicit never-to-any `as` cast +fn example() -> impl Iterator { + todo!() as std::iter::Empty<_> } fn main() {} diff --git a/tests/ui/reachable/expr_cast.stderr b/tests/ui/reachable/expr_cast.stderr deleted file mode 100644 index cf711d4436e..00000000000 --- a/tests/ui/reachable/expr_cast.stderr +++ /dev/null @@ -1,26 +0,0 @@ -error: unreachable expression - --> $DIR/expr_cast.rs:5:9 - | -LL | _ = {return} as u32; - | ^------^^^^^^^^ - | || - | |any code following this expression is unreachable - | unreachable expression - | -note: the lint level is defined here - --> $DIR/expr_cast.rs:2:9 - | -LL | #![deny(unreachable_code)] - | ^^^^^^^^^^^^^^^^ - -error: unreachable expression - --> $DIR/expr_cast.rs:9:5 - | -LL | (return) as u32; - | --------^^^^^^^ - | | - | unreachable expression - | any code following this expression is unreachable - -error: aborting due to 2 previous errors - diff --git a/tests/ui/reachable/unreachable-try-pattern.rs b/tests/ui/reachable/unreachable-try-pattern.rs index 22cbfb95af0..1358722e229 100644 --- a/tests/ui/reachable/unreachable-try-pattern.rs +++ b/tests/ui/reachable/unreachable-try-pattern.rs @@ -18,7 +18,7 @@ fn bar(x: Result) -> Result { fn foo(x: Result) -> Result { let y = (match x { Ok(n) => Ok(n as u32), Err(e) => Err(e) })?; //~^ WARN unreachable pattern - //~| WARN unreachable expression + //~| WARN unreachable call Ok(y) } diff --git a/tests/ui/reachable/unreachable-try-pattern.stderr b/tests/ui/reachable/unreachable-try-pattern.stderr index 40b11613105..468af427249 100644 --- a/tests/ui/reachable/unreachable-try-pattern.stderr +++ b/tests/ui/reachable/unreachable-try-pattern.stderr @@ -1,11 +1,10 @@ -warning: unreachable expression - --> $DIR/unreachable-try-pattern.rs:19:36 +warning: unreachable call + --> $DIR/unreachable-try-pattern.rs:19:33 | LL | let y = (match x { Ok(n) => Ok(n as u32), Err(e) => Err(e) })?; - | -^^^^^^^ - | | - | unreachable expression - | any code following this expression is unreachable + | ^^ - any code following this expression is unreachable + | | + | unreachable call | note: the lint level is defined here --> $DIR/unreachable-try-pattern.rs:3:9 -- cgit 1.4.1-3-g733a5 From 4ad8606d79a7cb9693f315fdd979fdb03d72318e Mon Sep 17 00:00:00 2001 From: "Pascal S. de Kloe" Date: Tue, 8 Jul 2025 17:56:25 +0200 Subject: fmt with table lookup for binary, octal and hex * correct buffer size * no trait abstraction * similar to decimal --- library/core/src/fmt/num.rs | 177 ++++++++++++++------------------------------ 1 file changed, 55 insertions(+), 122 deletions(-) diff --git a/library/core/src/fmt/num.rs b/library/core/src/fmt/num.rs index a52749d4325..cdabaeb7265 100644 --- a/library/core/src/fmt/num.rs +++ b/library/core/src/fmt/num.rs @@ -10,9 +10,6 @@ use crate::{fmt, ptr, slice, str}; trait DisplayInt: PartialEq + PartialOrd + Div + Rem + Sub + Copy { - fn zero() -> Self; - fn from_u8(u: u8) -> Self; - fn to_u8(&self) -> u8; #[cfg(not(any(target_pointer_width = "64", target_arch = "wasm32")))] fn to_u32(&self) -> u32; fn to_u64(&self) -> u64; @@ -22,9 +19,6 @@ trait DisplayInt: macro_rules! impl_int { ($($t:ident)*) => ( $(impl DisplayInt for $t { - fn zero() -> Self { 0 } - fn from_u8(u: u8) -> Self { u as Self } - fn to_u8(&self) -> u8 { *self as u8 } #[cfg(not(any(target_pointer_width = "64", target_arch = "wasm32")))] fn to_u32(&self) -> u32 { *self as u32 } fn to_u64(&self) -> u64 { *self as u64 } @@ -38,137 +32,76 @@ impl_int! { u8 u16 u32 u64 u128 usize } -/// A type that represents a specific radix -/// -/// # Safety -/// -/// `digit` must return an ASCII character. -#[doc(hidden)] -unsafe trait GenericRadix: Sized { - /// The number of digits. - const BASE: u8; - - /// A radix-specific prefix string. - const PREFIX: &'static str; - - /// Converts an integer to corresponding radix digit. - fn digit(x: u8) -> u8; - - /// Format an unsigned integer using the radix using a formatter. - fn fmt_int(&self, mut x: T, f: &mut fmt::Formatter<'_>) -> fmt::Result { - // The radix can be as low as 2, so we need a buffer of at least 128 - // characters for a base 2 number. - let zero = T::zero(); - let mut buf = [MaybeUninit::::uninit(); 128]; - let mut offset = buf.len(); - let base = T::from_u8(Self::BASE); - - // Accumulate each digit of the number from the least significant - // to the most significant figure. - loop { - let n = x % base; // Get the current place value. - x = x / base; // Deaccumulate the number. - offset -= 1; - buf[offset].write(Self::digit(n.to_u8())); // Store the digit in the buffer. - if x == zero { - // No more digits left to accumulate. - break; - }; - } +/// Formatting of integers with a non-decimal radix. +macro_rules! radix_integer { + (fmt::$Trait:ident for $Signed:ident and $Unsigned:ident, $prefix:literal, $dig_tab:literal) => { + #[stable(feature = "rust1", since = "1.0.0")] + impl fmt::$Trait for $Unsigned { + /// Format unsigned integers in the radix. + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + // Check macro arguments at compile time. + const { + assert!($Unsigned::MIN == 0, "need unsigned"); + assert!($dig_tab.is_ascii(), "need single-byte entries"); + } - // SAFETY: Starting from `offset`, all elements of the slice have been set. - let digits = unsafe { slice_buffer_to_str(&buf, offset) }; - f.pad_integral(true, Self::PREFIX, digits) - } -} + // ASCII digits in ascending order are used as a lookup table. + const DIG_TAB: &[u8] = $dig_tab; + const BASE: $Unsigned = DIG_TAB.len() as $Unsigned; + const MAX_DIG_N: usize = $Unsigned::MAX.ilog(BASE) as usize + 1; + + // Buffer digits of self with right alignment. + let mut buf = [MaybeUninit::::uninit(); MAX_DIG_N]; + // Count the number of bytes in buf that are not initialized. + let mut offset = buf.len(); -/// A binary (base 2) radix -#[derive(Clone, PartialEq)] -struct Binary; - -/// An octal (base 8) radix -#[derive(Clone, PartialEq)] -struct Octal; - -/// A hexadecimal (base 16) radix, formatted with lower-case characters -#[derive(Clone, PartialEq)] -struct LowerHex; - -/// A hexadecimal (base 16) radix, formatted with upper-case characters -#[derive(Clone, PartialEq)] -struct UpperHex; - -macro_rules! radix { - ($T:ident, $base:expr, $prefix:expr, $($x:pat => $conv:expr),+) => { - unsafe impl GenericRadix for $T { - const BASE: u8 = $base; - const PREFIX: &'static str = $prefix; - fn digit(x: u8) -> u8 { - match x { - $($x => $conv,)+ - x => panic!("number not in the range 0..={}: {}", Self::BASE - 1, x), + // Accumulate each digit of the number from the least + // significant to the most significant figure. + let mut remain = *self; + loop { + let digit = remain % BASE; + remain /= BASE; + + offset -= 1; + // SAFETY: `remain` will reach 0 and we will break before `offset` wraps + unsafe { core::hint::assert_unchecked(offset < buf.len()) } + buf[offset].write(DIG_TAB[digit as usize]); + if remain == 0 { + break; + } } + + // SAFETY: Starting from `offset`, all elements of the slice have been set. + let digits = unsafe { slice_buffer_to_str(&buf, offset) }; + f.pad_integral(true, $prefix, digits) } } - } -} - -radix! { Binary, 2, "0b", x @ 0 ..= 1 => b'0' + x } -radix! { Octal, 8, "0o", x @ 0 ..= 7 => b'0' + x } -radix! { LowerHex, 16, "0x", x @ 0 ..= 9 => b'0' + x, x @ 10 ..= 15 => b'a' + (x - 10) } -radix! { UpperHex, 16, "0x", x @ 0 ..= 9 => b'0' + x, x @ 10 ..= 15 => b'A' + (x - 10) } -macro_rules! int_base { - (fmt::$Trait:ident for $T:ident -> $Radix:ident) => { #[stable(feature = "rust1", since = "1.0.0")] - impl fmt::$Trait for $T { + impl fmt::$Trait for $Signed { + /// Format signed integers in the two’s-complement form. fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - $Radix.fmt_int(*self, f) + fmt::$Trait::fmt(&self.cast_unsigned(), f) } } }; } -macro_rules! integer { - ($Int:ident, $Uint:ident) => { - int_base! { fmt::Binary for $Uint -> Binary } - int_base! { fmt::Octal for $Uint -> Octal } - int_base! { fmt::LowerHex for $Uint -> LowerHex } - int_base! { fmt::UpperHex for $Uint -> UpperHex } - - // Format signed integers as unsigned (two’s complement representation). - #[stable(feature = "rust1", since = "1.0.0")] - impl fmt::Binary for $Int { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Binary::fmt(&self.cast_unsigned(), f) - } - } - #[stable(feature = "rust1", since = "1.0.0")] - impl fmt::Octal for $Int { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Octal::fmt(&self.cast_unsigned(), f) - } - } - #[stable(feature = "rust1", since = "1.0.0")] - impl fmt::LowerHex for $Int { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::LowerHex::fmt(&self.cast_unsigned(), f) - } - } - #[stable(feature = "rust1", since = "1.0.0")] - impl fmt::UpperHex for $Int { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::UpperHex::fmt(&self.cast_unsigned(), f) - } - } +/// Formatting of integers with a non-decimal radix. +macro_rules! radix_integers { + ($Signed:ident, $Unsigned:ident) => { + radix_integer! { fmt::Binary for $Signed and $Unsigned, "0b", b"01" } + radix_integer! { fmt::Octal for $Signed and $Unsigned, "0o", b"01234567" } + radix_integer! { fmt::LowerHex for $Signed and $Unsigned, "0x", b"0123456789abcdef" } + radix_integer! { fmt::UpperHex for $Signed and $Unsigned, "0x", b"0123456789ABCDEF" } }; } -integer! { isize, usize } -integer! { i8, u8 } -integer! { i16, u16 } -integer! { i32, u32 } -integer! { i64, u64 } -integer! { i128, u128 } +radix_integers! { isize, usize } +radix_integers! { i8, u8 } +radix_integers! { i16, u16 } +radix_integers! { i32, u32 } +radix_integers! { i64, u64 } +radix_integers! { i128, u128 } macro_rules! impl_Debug { ($($T:ident)*) => { -- cgit 1.4.1-3-g733a5 From 74c6fdf171e04e368e6e2179158396a4ead6c634 Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Sun, 27 Jul 2025 18:01:40 +0200 Subject: c-variadic: multiple ABIs in the same program for arm --- .../compiletest/src/directives/directive_names.rs | 1 + .../c-variadic/same-program-multiple-abis-arm.rs | 77 ++++++++++++++ .../same-program-multiple-abis-x86_64.rs | 112 +++++++++++++++++++++ tests/ui/c-variadic/same-program-multiple-abis.rs | 112 --------------------- 4 files changed, 190 insertions(+), 112 deletions(-) create mode 100644 tests/ui/c-variadic/same-program-multiple-abis-arm.rs create mode 100644 tests/ui/c-variadic/same-program-multiple-abis-x86_64.rs delete mode 100644 tests/ui/c-variadic/same-program-multiple-abis.rs diff --git a/src/tools/compiletest/src/directives/directive_names.rs b/src/tools/compiletest/src/directives/directive_names.rs index 7fc76a42e0c..cc4e81c357a 100644 --- a/src/tools/compiletest/src/directives/directive_names.rs +++ b/src/tools/compiletest/src/directives/directive_names.rs @@ -193,6 +193,7 @@ pub(crate) const KNOWN_DIRECTIVE_NAMES: &[&str] = &[ "only-bpf", "only-cdb", "only-dist", + "only-eabihf", "only-elf", "only-emscripten", "only-gnu", diff --git a/tests/ui/c-variadic/same-program-multiple-abis-arm.rs b/tests/ui/c-variadic/same-program-multiple-abis-arm.rs new file mode 100644 index 00000000000..1b0bdecabfb --- /dev/null +++ b/tests/ui/c-variadic/same-program-multiple-abis-arm.rs @@ -0,0 +1,77 @@ +#![feature(extended_varargs_abi_support)] +//@ run-pass +//@ only-arm +//@ ignore-thumb (this test uses arm assembly) +//@ only-eabihf (the assembly below requires float hardware support) + +// Check that multiple c-variadic calling conventions can be used in the same program. +// +// Clang and gcc reject defining functions with a non-default calling convention and a variable +// argument list, so C programs that use multiple c-variadic calling conventions are unlikely +// to come up. Here we validate that our codegen backends do in fact generate correct code. + +extern "C" { + fn variadic_c(_: f64, _: ...) -> f64; +} + +extern "aapcs" { + fn variadic_aapcs(_: f64, _: ...) -> f64; +} + +fn main() { + unsafe { + assert_eq!(variadic_c(1.0, 2.0, 3.0), 1.0 + 2.0 + 3.0); + assert_eq!(variadic_aapcs(1.0, 2.0, 3.0), 1.0 + 2.0 + 3.0); + } +} + +// This assembly was generated using https://godbolt.org/z/xcW6a1Tj5, and corresponds to the +// following code compiled for the `armv7-unknown-linux-gnueabihf` target: +// +// ```rust +// #![feature(c_variadic)] +// +// #[unsafe(no_mangle)] +// unsafe extern "C" fn variadic(a: f64, mut args: ...) -> f64 { +// let b = args.arg::(); +// let c = args.arg::(); +// +// a + b + c +// } +// ``` +// +// This function uses floats (and passes one normal float argument) because the aapcs and C calling +// conventions differ in how floats are passed, e.g. https://godbolt.org/z/sz799f51x. However, for +// c-variadic functions, both ABIs actually behave the same, based on: +// +// https://github.com/ARM-software/abi-aa/blob/main/aapcs32/aapcs32.rst#65parameter-passing +// +// > A variadic function is always marshaled as for the base standard. +// +// https://github.com/ARM-software/abi-aa/blob/main/aapcs32/aapcs32.rst#7the-standard-variants +// +// > This section applies only to non-variadic functions. For a variadic function the base standard +// > is always used both for argument passing and result return. +core::arch::global_asm!( + r#" +{variadic_c}: +{variadic_aapcs}: + sub sp, sp, #12 + stmib sp, {{r2, r3}} + vmov d0, r0, r1 + add r0, sp, #4 + vldr d1, [sp, #4] + add r0, r0, #15 + bic r0, r0, #7 + vadd.f64 d0, d0, d1 + add r1, r0, #8 + str r1, [sp] + vldr d1, [r0] + vadd.f64 d0, d0, d1 + vmov r0, r1, d0 + add sp, sp, #12 + bx lr + "#, + variadic_c = sym variadic_c, + variadic_aapcs = sym variadic_aapcs, +); diff --git a/tests/ui/c-variadic/same-program-multiple-abis-x86_64.rs b/tests/ui/c-variadic/same-program-multiple-abis-x86_64.rs new file mode 100644 index 00000000000..b21accb999e --- /dev/null +++ b/tests/ui/c-variadic/same-program-multiple-abis-x86_64.rs @@ -0,0 +1,112 @@ +#![feature(extended_varargs_abi_support)] +//@ run-pass +//@ only-x86_64 + +// Check that multiple c-variadic calling conventions can be used in the same program. +// +// Clang and gcc reject defining functions with a non-default calling convention and a variable +// argument list, so C programs that use multiple c-variadic calling conventions are unlikely +// to come up. Here we validate that our codegen backends do in fact generate correct code. + +extern "sysv64" { + fn variadic_sysv64(_: u32, _: ...) -> u32; +} + +extern "win64" { + fn variadic_win64(_: u32, _: ...) -> u32; +} + +fn main() { + unsafe { + assert_eq!(variadic_win64(1, 2, 3), 1 + 2 + 3); + assert_eq!(variadic_sysv64(1, 2, 3), 1 + 2 + 3); + } +} + +// This assembly was generated using https://godbolt.org/z/dbTGanoh6, and corresponds to the +// following code compiled for the `x86_64-unknown-linux-gnu` and `x86_64-pc-windows-gnu` +// targets, respectively: +// +// ```rust +// #![feature(c_variadic)] +// +// #[unsafe(no_mangle)] +// unsafe extern "C" fn variadic(a: u32, mut args: ...) -> u32 { +// let b = args.arg::(); +// let c = args.arg::(); +// +// a + b + c +// } +// ``` +core::arch::global_asm!( + r#" +{variadic_sysv64}: + sub rsp, 88 + test al, al + je .LBB0_7 + movaps xmmword ptr [rsp - 48], xmm0 + movaps xmmword ptr [rsp - 32], xmm1 + movaps xmmword ptr [rsp - 16], xmm2 + movaps xmmword ptr [rsp], xmm3 + movaps xmmword ptr [rsp + 16], xmm4 + movaps xmmword ptr [rsp + 32], xmm5 + movaps xmmword ptr [rsp + 48], xmm6 + movaps xmmword ptr [rsp + 64], xmm7 +.LBB0_7: + mov qword ptr [rsp - 88], rsi + mov qword ptr [rsp - 80], rdx + mov qword ptr [rsp - 72], rcx + mov qword ptr [rsp - 64], r8 + mov qword ptr [rsp - 56], r9 + movabs rax, 206158430216 + mov qword ptr [rsp - 120], rax + lea rax, [rsp + 96] + mov qword ptr [rsp - 112], rax + lea rax, [rsp - 96] + mov qword ptr [rsp - 104], rax + mov edx, 8 + cmp rdx, 41 + jae .LBB0_1 + mov rax, qword ptr [rsp - 104] + mov ecx, 8 + add rcx, 8 + mov dword ptr [rsp - 120], ecx + mov eax, dword ptr [rax + rdx] + cmp edx, 32 + ja .LBB0_2 + add rcx, qword ptr [rsp - 104] + add edx, 16 + mov dword ptr [rsp - 120], edx + add eax, edi + add eax, dword ptr [rcx] + add rsp, 88 + ret +.LBB0_1: + mov rax, qword ptr [rsp - 112] + lea rcx, [rax + 8] + mov qword ptr [rsp - 112], rcx + mov eax, dword ptr [rax] +.LBB0_2: + mov rcx, qword ptr [rsp - 112] + lea rdx, [rcx + 8] + mov qword ptr [rsp - 112], rdx + add eax, edi + add eax, dword ptr [rcx] + add rsp, 88 + ret + +{variadic_win64}: + push rax + mov qword ptr [rsp + 40], r9 + mov qword ptr [rsp + 24], rdx + mov qword ptr [rsp + 32], r8 + lea rax, [rsp + 40] + mov qword ptr [rsp], rax + lea eax, [rdx + rcx] + add eax, r8d + pop rcx + ret + "#, + variadic_win64 = sym variadic_win64, + variadic_sysv64 = sym variadic_sysv64, +); diff --git a/tests/ui/c-variadic/same-program-multiple-abis.rs b/tests/ui/c-variadic/same-program-multiple-abis.rs deleted file mode 100644 index b21accb999e..00000000000 --- a/tests/ui/c-variadic/same-program-multiple-abis.rs +++ /dev/null @@ -1,112 +0,0 @@ -#![feature(extended_varargs_abi_support)] -//@ run-pass -//@ only-x86_64 - -// Check that multiple c-variadic calling conventions can be used in the same program. -// -// Clang and gcc reject defining functions with a non-default calling convention and a variable -// argument list, so C programs that use multiple c-variadic calling conventions are unlikely -// to come up. Here we validate that our codegen backends do in fact generate correct code. - -extern "sysv64" { - fn variadic_sysv64(_: u32, _: ...) -> u32; -} - -extern "win64" { - fn variadic_win64(_: u32, _: ...) -> u32; -} - -fn main() { - unsafe { - assert_eq!(variadic_win64(1, 2, 3), 1 + 2 + 3); - assert_eq!(variadic_sysv64(1, 2, 3), 1 + 2 + 3); - } -} - -// This assembly was generated using https://godbolt.org/z/dbTGanoh6, and corresponds to the -// following code compiled for the `x86_64-unknown-linux-gnu` and `x86_64-pc-windows-gnu` -// targets, respectively: -// -// ```rust -// #![feature(c_variadic)] -// -// #[unsafe(no_mangle)] -// unsafe extern "C" fn variadic(a: u32, mut args: ...) -> u32 { -// let b = args.arg::(); -// let c = args.arg::(); -// -// a + b + c -// } -// ``` -core::arch::global_asm!( - r#" -{variadic_sysv64}: - sub rsp, 88 - test al, al - je .LBB0_7 - movaps xmmword ptr [rsp - 48], xmm0 - movaps xmmword ptr [rsp - 32], xmm1 - movaps xmmword ptr [rsp - 16], xmm2 - movaps xmmword ptr [rsp], xmm3 - movaps xmmword ptr [rsp + 16], xmm4 - movaps xmmword ptr [rsp + 32], xmm5 - movaps xmmword ptr [rsp + 48], xmm6 - movaps xmmword ptr [rsp + 64], xmm7 -.LBB0_7: - mov qword ptr [rsp - 88], rsi - mov qword ptr [rsp - 80], rdx - mov qword ptr [rsp - 72], rcx - mov qword ptr [rsp - 64], r8 - mov qword ptr [rsp - 56], r9 - movabs rax, 206158430216 - mov qword ptr [rsp - 120], rax - lea rax, [rsp + 96] - mov qword ptr [rsp - 112], rax - lea rax, [rsp - 96] - mov qword ptr [rsp - 104], rax - mov edx, 8 - cmp rdx, 41 - jae .LBB0_1 - mov rax, qword ptr [rsp - 104] - mov ecx, 8 - add rcx, 8 - mov dword ptr [rsp - 120], ecx - mov eax, dword ptr [rax + rdx] - cmp edx, 32 - ja .LBB0_2 - add rcx, qword ptr [rsp - 104] - add edx, 16 - mov dword ptr [rsp - 120], edx - add eax, edi - add eax, dword ptr [rcx] - add rsp, 88 - ret -.LBB0_1: - mov rax, qword ptr [rsp - 112] - lea rcx, [rax + 8] - mov qword ptr [rsp - 112], rcx - mov eax, dword ptr [rax] -.LBB0_2: - mov rcx, qword ptr [rsp - 112] - lea rdx, [rcx + 8] - mov qword ptr [rsp - 112], rdx - add eax, edi - add eax, dword ptr [rcx] - add rsp, 88 - ret - -{variadic_win64}: - push rax - mov qword ptr [rsp + 40], r9 - mov qword ptr [rsp + 24], rdx - mov qword ptr [rsp + 32], r8 - lea rax, [rsp + 40] - mov qword ptr [rsp], rax - lea eax, [rdx + rcx] - add eax, r8d - pop rcx - ret - "#, - variadic_win64 = sym variadic_win64, - variadic_sysv64 = sym variadic_sysv64, -); -- cgit 1.4.1-3-g733a5 From c0c48984b05e244fc93144d0c1d48db318639b3b Mon Sep 17 00:00:00 2001 From: Camille GILLOT Date: Sat, 2 Aug 2025 17:28:17 +0000 Subject: Method probe: check doc aliases after edit distance. --- compiler/rustc_hir_typeck/src/method/probe.rs | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/method/probe.rs b/compiler/rustc_hir_typeck/src/method/probe.rs index 1f3969bd93c..f5584e0596a 100644 --- a/compiler/rustc_hir_typeck/src/method/probe.rs +++ b/compiler/rustc_hir_typeck/src/method/probe.rs @@ -2373,17 +2373,14 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { if !self.is_relevant_kind_for_mode(x.kind) { return false; } - if self.matches_by_doc_alias(x.def_id) { - return true; - } - match edit_distance_with_substrings( + if let Some(d) = edit_distance_with_substrings( name.as_str(), x.name().as_str(), max_dist, ) { - Some(d) => d > 0, - None => false, + return d > 0; } + self.matches_by_doc_alias(x.def_id) }) .copied() .collect() -- cgit 1.4.1-3-g733a5 From e772f7ab3ee7d5f217c60b640b95f8bfc84d9536 Mon Sep 17 00:00:00 2001 From: Isaac Chen Date: Wed, 23 Oct 2024 20:13:56 -0400 Subject: corrected lifetime in core::panic::Location::file return type --- library/core/src/panic/location.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/core/src/panic/location.rs b/library/core/src/panic/location.rs index 6ef7d5a22a3..e81dc5eb530 100644 --- a/library/core/src/panic/location.rs +++ b/library/core/src/panic/location.rs @@ -183,7 +183,7 @@ impl<'a> Location<'a> { #[must_use] #[stable(feature = "panic_hooks", since = "1.10.0")] #[rustc_const_stable(feature = "const_location_fields", since = "1.79.0")] - pub const fn file(&self) -> &str { + pub const fn file(&self) -> &'a str { // SAFETY: The filename is valid. unsafe { self.filename.as_ref() } } -- cgit 1.4.1-3-g733a5 From 9c4a35e0e705dbe93dcfbba9fbde9d3dbfc13e11 Mon Sep 17 00:00:00 2001 From: Isaac Chen Date: Wed, 23 Oct 2024 23:49:23 -0400 Subject: added regression test for `core::panic::Location::file`'s lifetime --- library/core/tests/panic/location.rs | 39 ++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 library/core/tests/panic/location.rs diff --git a/library/core/tests/panic/location.rs b/library/core/tests/panic/location.rs new file mode 100644 index 00000000000..35017612f00 --- /dev/null +++ b/library/core/tests/panic/location.rs @@ -0,0 +1,39 @@ +use core::panic::Location; + +// Note: Some of the following tests depend on the source location, +// so please be careful when editing this file. + +#[test] +fn location_const_caller() { + const _CALLER_REFERENCE: &Location<'static> = Location::caller(); + const _CALLER: Location<'static> = *Location::caller(); +} + +#[test] +fn location_const_file() { + const CALLER: &Location<'static> = Location::caller(); + const FILE: &str = CALLER.file(); + assert_eq!(FILE, file!()); +} + +#[test] +fn location_const_line() { + const CALLER: &Location<'static> = Location::caller(); + const LINE: u32 = CALLER.line(); + assert_eq!(LINE, 21); +} + +#[test] +fn location_const_column() { + const CALLER: &Location<'static> = Location::caller(); + const COLUMN: u32 = CALLER.column(); + assert_eq!(COLUMN, 40); +} + +#[test] +fn location_file_lifetime<'x>() { + // Verify that the returned `&str`s lifetime is derived from the generic + // lifetime 'a, not the lifetime of `&self`, when calling `Location::file`. + // Test failure is indicated by a compile failure, not a runtime panic. + let _: for<'a> fn(&'a Location<'x>) -> &'x str = Location::file; +} -- cgit 1.4.1-3-g733a5 From 557737062d1a65b25f9bbb257f07755078e5cdae Mon Sep 17 00:00:00 2001 From: Isaac Chen Date: Wed, 23 Oct 2024 20:13:56 -0400 Subject: corrected lifetime in core::panic::Location::file return type --- library/core/src/panic/location.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/core/src/panic/location.rs b/library/core/src/panic/location.rs index e81dc5eb530..cafdcfa2c2e 100644 --- a/library/core/src/panic/location.rs +++ b/library/core/src/panic/location.rs @@ -195,7 +195,7 @@ impl<'a> Location<'a> { #[must_use] #[unstable(feature = "file_with_nul", issue = "141727")] #[inline] - pub const fn file_with_nul(&self) -> &CStr { + pub const fn file_with_nul(&self) -> &'a CStr { let filename = self.filename.as_ptr(); // SAFETY: The filename is valid for `filename_len+1` bytes, so this addition can't -- cgit 1.4.1-3-g733a5 From 8a0438fee86b7dc566d0758b3aee050b84edc842 Mon Sep 17 00:00:00 2001 From: Isaac Chen Date: Sat, 2 Aug 2025 16:53:06 -0400 Subject: moved new test to updated test location --- library/core/tests/panic/location.rs | 39 ------------------------------- library/coretests/tests/panic/location.rs | 8 +++++++ 2 files changed, 8 insertions(+), 39 deletions(-) delete mode 100644 library/core/tests/panic/location.rs diff --git a/library/core/tests/panic/location.rs b/library/core/tests/panic/location.rs deleted file mode 100644 index 35017612f00..00000000000 --- a/library/core/tests/panic/location.rs +++ /dev/null @@ -1,39 +0,0 @@ -use core::panic::Location; - -// Note: Some of the following tests depend on the source location, -// so please be careful when editing this file. - -#[test] -fn location_const_caller() { - const _CALLER_REFERENCE: &Location<'static> = Location::caller(); - const _CALLER: Location<'static> = *Location::caller(); -} - -#[test] -fn location_const_file() { - const CALLER: &Location<'static> = Location::caller(); - const FILE: &str = CALLER.file(); - assert_eq!(FILE, file!()); -} - -#[test] -fn location_const_line() { - const CALLER: &Location<'static> = Location::caller(); - const LINE: u32 = CALLER.line(); - assert_eq!(LINE, 21); -} - -#[test] -fn location_const_column() { - const CALLER: &Location<'static> = Location::caller(); - const COLUMN: u32 = CALLER.column(); - assert_eq!(COLUMN, 40); -} - -#[test] -fn location_file_lifetime<'x>() { - // Verify that the returned `&str`s lifetime is derived from the generic - // lifetime 'a, not the lifetime of `&self`, when calling `Location::file`. - // Test failure is indicated by a compile failure, not a runtime panic. - let _: for<'a> fn(&'a Location<'x>) -> &'x str = Location::file; -} diff --git a/library/coretests/tests/panic/location.rs b/library/coretests/tests/panic/location.rs index 910001bcc1c..2174ac854e9 100644 --- a/library/coretests/tests/panic/location.rs +++ b/library/coretests/tests/panic/location.rs @@ -47,6 +47,14 @@ fn location_const_column() { assert_eq!(COLUMN, 40); } +#[test] +fn location_file_lifetime<'x>() { + // Verify that the returned `&str`s lifetime is derived from the generic + // lifetime 'a, not the lifetime of `&self`, when calling `Location::file`. + // Test failure is indicated by a compile failure, not a runtime panic. + let _: for<'a> fn(&'a Location<'x>) -> &'x str = Location::file; +} + #[test] fn location_debug() { let f = format!("{:?}", Location::caller()); -- cgit 1.4.1-3-g733a5 From 792ec3bdd42800bfbf16ee1d570efc398bf30f30 Mon Sep 17 00:00:00 2001 From: Isaac Chen Date: Sat, 2 Aug 2025 17:34:59 -0400 Subject: updated line number in test --- library/coretests/tests/panic/location.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/coretests/tests/panic/location.rs b/library/coretests/tests/panic/location.rs index 2174ac854e9..a7db05a15c6 100644 --- a/library/coretests/tests/panic/location.rs +++ b/library/coretests/tests/panic/location.rs @@ -59,7 +59,7 @@ fn location_file_lifetime<'x>() { fn location_debug() { let f = format!("{:?}", Location::caller()); assert!(f.contains(&format!("{:?}", file!()))); - assert!(f.contains("52")); + assert!(f.contains("60")); assert!(f.contains("29")); } -- cgit 1.4.1-3-g733a5 From bf644e9d14d4e088303041d4136daefaa8ae6f86 Mon Sep 17 00:00:00 2001 From: Camille GILLOT Date: Sat, 2 Aug 2025 19:03:46 +0000 Subject: Use codegen_fn_attrs to check if function is naked. --- compiler/rustc_hir_typeck/src/lib.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/lib.rs b/compiler/rustc_hir_typeck/src/lib.rs index aae870f7ee3..72fd8db300b 100644 --- a/compiler/rustc_hir_typeck/src/lib.rs +++ b/compiler/rustc_hir_typeck/src/lib.rs @@ -46,12 +46,12 @@ use rustc_data_structures::unord::UnordSet; use rustc_errors::codes::*; use rustc_errors::{Applicability, ErrorGuaranteed, pluralize, struct_span_code_err}; use rustc_hir as hir; -use rustc_hir::attrs::AttributeKind; use rustc_hir::def::{DefKind, Res}; -use rustc_hir::{HirId, HirIdMap, Node, find_attr}; +use rustc_hir::{HirId, HirIdMap, Node}; use rustc_hir_analysis::check::{check_abi, check_custom_abi}; use rustc_hir_analysis::hir_ty_lowering::HirTyLowerer; use rustc_infer::traits::{ObligationCauseCode, ObligationInspector, WellFormedLoc}; +use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags; use rustc_middle::query::Providers; use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt}; use rustc_middle::{bug, span_bug}; @@ -174,7 +174,7 @@ fn typeck_with_inspect<'tcx>( .map(|(idx, ty)| fcx.normalize(arg_span(idx), ty)), ); - if find_attr!(tcx.get_all_attrs(def_id), AttributeKind::Naked(..)) { + if tcx.codegen_fn_attrs(def_id).flags.contains(CodegenFnAttrFlags::NAKED) { naked_functions::typeck_naked_fn(tcx, def_id, body); } -- cgit 1.4.1-3-g733a5 From 4c4b8b23c6935e719ca01b614922024abb694656 Mon Sep 17 00:00:00 2001 From: Kivooeo Date: Mon, 4 Aug 2025 01:06:04 +0500 Subject: remove gate --- library/std/src/path.rs | 3 +-- library/std/tests/path.rs | 8 +------- src/tools/clippy/tests/missing-test-files.rs | 2 +- 3 files changed, 3 insertions(+), 10 deletions(-) diff --git a/library/std/src/path.rs b/library/std/src/path.rs index 055e7f81480..5dd52a23e97 100644 --- a/library/std/src/path.rs +++ b/library/std/src/path.rs @@ -2678,7 +2678,6 @@ impl Path { /// # Examples /// /// ``` - /// # #![feature(path_file_prefix)] /// use std::path::Path; /// /// assert_eq!("foo", Path::new("foo.rs").file_prefix().unwrap()); @@ -2691,7 +2690,7 @@ impl Path { /// /// [`Path::file_stem`]: Path::file_stem /// - #[unstable(feature = "path_file_prefix", issue = "86319")] + #[stable(feature = "path_file_prefix", since = "CURRENT_RUSTC_VERSION")] #[must_use] pub fn file_prefix(&self) -> Option<&OsStr> { self.file_name().map(split_file_at_dot).and_then(|(before, _after)| Some(before)) diff --git a/library/std/tests/path.rs b/library/std/tests/path.rs index 901d2770f20..e1576a0d423 100644 --- a/library/std/tests/path.rs +++ b/library/std/tests/path.rs @@ -1,10 +1,4 @@ -#![feature( - clone_to_uninit, - path_add_extension, - path_file_prefix, - maybe_uninit_slice, - normalize_lexically -)] +#![feature(clone_to_uninit, path_add_extension, maybe_uninit_slice, normalize_lexically)] use std::clone::CloneToUninit; use std::ffi::OsStr; diff --git a/src/tools/clippy/tests/missing-test-files.rs b/src/tools/clippy/tests/missing-test-files.rs index 565dcd73f58..63f960c92fa 100644 --- a/src/tools/clippy/tests/missing-test-files.rs +++ b/src/tools/clippy/tests/missing-test-files.rs @@ -1,6 +1,6 @@ #![warn(rust_2018_idioms, unused_lifetimes)] #![allow(clippy::assertions_on_constants)] -#![feature(path_file_prefix)] +#![cfg_attr(bootstrap, feature(path_file_prefix))] use std::cmp::Ordering; use std::ffi::OsStr; -- cgit 1.4.1-3-g733a5 From 849f71f02564f1c726848efa4c70673128cd64d2 Mon Sep 17 00:00:00 2001 From: Kivooeo Date: Mon, 4 Aug 2025 01:06:04 +0500 Subject: remove gate --- tests/missing-test-files.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/missing-test-files.rs b/tests/missing-test-files.rs index 565dcd73f58..63f960c92fa 100644 --- a/tests/missing-test-files.rs +++ b/tests/missing-test-files.rs @@ -1,6 +1,6 @@ #![warn(rust_2018_idioms, unused_lifetimes)] #![allow(clippy::assertions_on_constants)] -#![feature(path_file_prefix)] +#![cfg_attr(bootstrap, feature(path_file_prefix))] use std::cmp::Ordering; use std::ffi::OsStr; -- cgit 1.4.1-3-g733a5 From 36f2045a930a3367963881db4f423d7de33b46a5 Mon Sep 17 00:00:00 2001 From: Camille GILLOT Date: Sat, 12 Jul 2025 13:47:45 +0000 Subject: Implement `stability_implications` without a visitor. --- compiler/rustc_hir/src/def.rs | 41 ++++++++++++ compiler/rustc_passes/src/stability.rs | 111 +++++++++------------------------ 2 files changed, 72 insertions(+), 80 deletions(-) diff --git a/compiler/rustc_hir/src/def.rs b/compiler/rustc_hir/src/def.rs index 3fee9af01b3..8774fb4d585 100644 --- a/compiler/rustc_hir/src/def.rs +++ b/compiler/rustc_hir/src/def.rs @@ -305,6 +305,11 @@ impl DefKind { matches!(self, DefKind::Mod | DefKind::Enum | DefKind::Trait) } + #[inline] + pub fn is_adt(self) -> bool { + matches!(self, DefKind::Struct | DefKind::Union | DefKind::Enum) + } + #[inline] pub fn is_fn_like(self) -> bool { matches!( @@ -313,6 +318,42 @@ impl DefKind { ) } + pub fn has_generics(self) -> bool { + match self { + DefKind::AnonConst + | DefKind::AssocConst + | DefKind::AssocFn + | DefKind::AssocTy + | DefKind::Closure + | DefKind::Const + | DefKind::Ctor(..) + | DefKind::Enum + | DefKind::Field + | DefKind::Fn + | DefKind::ForeignTy + | DefKind::Impl { .. } + | DefKind::InlineConst + | DefKind::OpaqueTy + | DefKind::Static { .. } + | DefKind::Struct + | DefKind::SyntheticCoroutineBody + | DefKind::Trait + | DefKind::TraitAlias + | DefKind::TyAlias + | DefKind::Union + | DefKind::Variant => true, + DefKind::ConstParam + | DefKind::ExternCrate + | DefKind::ForeignMod + | DefKind::GlobalAsm + | DefKind::LifetimeParam + | DefKind::Macro(_) + | DefKind::Mod + | DefKind::TyParam + | DefKind::Use => false, + } + } + /// Whether `query get_codegen_attrs` should be used with this definition. pub fn has_codegen_attrs(self) -> bool { match self { diff --git a/compiler/rustc_passes/src/stability.rs b/compiler/rustc_passes/src/stability.rs index 9ed7293873f..abc1821e76d 100644 --- a/compiler/rustc_passes/src/stability.rs +++ b/compiler/rustc_passes/src/stability.rs @@ -267,93 +267,51 @@ fn lookup_const_stability(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Option { - tcx: TyCtxt<'tcx>, - implications: UnordMap, -} - -impl<'tcx> Annotator<'tcx> { - /// Determine the stability for a node based on its attributes and inherited stability. The - /// stability is recorded in the index and used as the parent. If the node is a function, - /// `fn_sig` is its signature. - #[instrument(level = "trace", skip(self))] - fn annotate(&mut self, def_id: LocalDefId) { - if !self.tcx.features().staged_api() { - return; - } +fn stability_implications(tcx: TyCtxt<'_>, LocalCrate: LocalCrate) -> UnordMap { + let mut implications = UnordMap::default(); - if let Some(stability) = self.tcx.lookup_stability(def_id) + let mut register_implication = |def_id| { + if let Some(stability) = tcx.lookup_stability(def_id) && let StabilityLevel::Unstable { implied_by: Some(implied_by), .. } = stability.level { - self.implications.insert(implied_by, stability.feature); + implications.insert(implied_by, stability.feature); } - if let Some(stability) = self.tcx.lookup_const_stability(def_id) + if let Some(stability) = tcx.lookup_const_stability(def_id) && let StabilityLevel::Unstable { implied_by: Some(implied_by), .. } = stability.level { - self.implications.insert(implied_by, stability.feature); + implications.insert(implied_by, stability.feature); } - } -} - -impl<'tcx> Visitor<'tcx> for Annotator<'tcx> { - /// Because stability levels are scoped lexically, we want to walk - /// nested items in the context of the outer item, so enable - /// deep-walking. - type NestedFilter = nested_filter::All; - - fn maybe_tcx(&mut self) -> Self::MaybeTyCtxt { - self.tcx - } + }; - fn visit_item(&mut self, i: &'tcx Item<'tcx>) { - match i.kind { - hir::ItemKind::Struct(_, _, ref sd) => { - if let Some(ctor_def_id) = sd.ctor_def_id() { - self.annotate(ctor_def_id); + if tcx.features().staged_api() { + register_implication(CRATE_DEF_ID); + for def_id in tcx.hir_crate_items(()).definitions() { + register_implication(def_id); + let def_kind = tcx.def_kind(def_id); + if def_kind.is_adt() { + let adt = tcx.adt_def(def_id); + for variant in adt.variants() { + if variant.def_id != def_id.to_def_id() { + register_implication(variant.def_id.expect_local()); + } + for field in &variant.fields { + register_implication(field.did.expect_local()); + } + if let Some(ctor_def_id) = variant.ctor_def_id() { + register_implication(ctor_def_id.expect_local()) + } + } + } + if def_kind.has_generics() { + for param in tcx.generics_of(def_id).own_params.iter() { + register_implication(param.def_id.expect_local()) } } - _ => {} - } - - self.annotate(i.owner_id.def_id); - intravisit::walk_item(self, i) - } - - fn visit_trait_item(&mut self, ti: &'tcx hir::TraitItem<'tcx>) { - self.annotate(ti.owner_id.def_id); - intravisit::walk_trait_item(self, ti); - } - - fn visit_impl_item(&mut self, ii: &'tcx hir::ImplItem<'tcx>) { - self.annotate(ii.owner_id.def_id); - intravisit::walk_impl_item(self, ii); - } - - fn visit_variant(&mut self, var: &'tcx Variant<'tcx>) { - self.annotate(var.def_id); - if let Some(ctor_def_id) = var.data.ctor_def_id() { - self.annotate(ctor_def_id); } - - intravisit::walk_variant(self, var) } - fn visit_field_def(&mut self, s: &'tcx FieldDef<'tcx>) { - self.annotate(s.def_id); - intravisit::walk_field_def(self, s); - } - - fn visit_foreign_item(&mut self, i: &'tcx hir::ForeignItem<'tcx>) { - self.annotate(i.owner_id.def_id); - intravisit::walk_foreign_item(self, i); - } - - fn visit_generic_param(&mut self, p: &'tcx hir::GenericParam<'tcx>) { - self.annotate(p.def_id); - intravisit::walk_generic_param(self, p); - } + implications } struct MissingStabilityAnnotations<'tcx> { @@ -565,13 +523,6 @@ impl<'tcx> Visitor<'tcx> for MissingStabilityAnnotations<'tcx> { } } -fn stability_implications(tcx: TyCtxt<'_>, LocalCrate: LocalCrate) -> UnordMap { - let mut annotator = Annotator { tcx, implications: Default::default() }; - annotator.annotate(CRATE_DEF_ID); - tcx.hir_walk_toplevel_module(&mut annotator); - annotator.implications -} - /// Cross-references the feature names of unstable APIs with enabled /// features and possibly prints errors. fn check_mod_unstable_api_usage(tcx: TyCtxt<'_>, module_def_id: LocalModDefId) { -- cgit 1.4.1-3-g733a5 From 607ecca1fbe884a7d032ef1ec1d98853ce284a3d Mon Sep 17 00:00:00 2001 From: The Miri Cronjob Bot Date: Mon, 4 Aug 2025 05:09:06 +0000 Subject: Prepare for merging from rust-lang/rust This updates the rust-version file to 07b7dc90ee4df5815dbb91ef8e98cb93571230f5. --- src/tools/miri/rust-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/miri/rust-version b/src/tools/miri/rust-version index bf69accaf06..8cf5873eab4 100644 --- a/src/tools/miri/rust-version +++ b/src/tools/miri/rust-version @@ -1 +1 @@ -adcb3d3b4cd3b7c4cde642f3ed537037f293738e +07b7dc90ee4df5815dbb91ef8e98cb93571230f5 -- cgit 1.4.1-3-g733a5 From 9b107bed9f2d2098dbbaade0ddbc363c651cd38b Mon Sep 17 00:00:00 2001 From: Scott McMurray Date: Sun, 27 Jul 2025 21:37:17 -0700 Subject: Add a mir-opt test for an unneeded drop_in_place --- tests/mir-opt/remove_unneeded_drop_in_place.rs | 13 +++++++++++++ ..._in_place.slice_in_place.RemoveUnneededDrops.diff | 20 ++++++++++++++++++++ 2 files changed, 33 insertions(+) create mode 100644 tests/mir-opt/remove_unneeded_drop_in_place.rs create mode 100644 tests/mir-opt/remove_unneeded_drop_in_place.slice_in_place.RemoveUnneededDrops.diff diff --git a/tests/mir-opt/remove_unneeded_drop_in_place.rs b/tests/mir-opt/remove_unneeded_drop_in_place.rs new file mode 100644 index 00000000000..7fb7b1e78de --- /dev/null +++ b/tests/mir-opt/remove_unneeded_drop_in_place.rs @@ -0,0 +1,13 @@ +//@ test-mir-pass: RemoveUnneededDrops +//@ needs-unwind + +// EMIT_MIR remove_unneeded_drop_in_place.slice_in_place.RemoveUnneededDrops.diff +unsafe fn slice_in_place(ptr: *mut [char]) { + std::ptr::drop_in_place(ptr) +} + +fn main() { + // CHECK-LABEL: fn main( + let mut a = ['o', 'k']; + unsafe { slice_in_place(&raw mut a) }; +} diff --git a/tests/mir-opt/remove_unneeded_drop_in_place.slice_in_place.RemoveUnneededDrops.diff b/tests/mir-opt/remove_unneeded_drop_in_place.slice_in_place.RemoveUnneededDrops.diff new file mode 100644 index 00000000000..4068e7940bc --- /dev/null +++ b/tests/mir-opt/remove_unneeded_drop_in_place.slice_in_place.RemoveUnneededDrops.diff @@ -0,0 +1,20 @@ +- // MIR for `slice_in_place` before RemoveUnneededDrops ++ // MIR for `slice_in_place` after RemoveUnneededDrops + + fn slice_in_place(_1: *mut [char]) -> () { + debug ptr => _1; + let mut _0: (); + let mut _2: *mut [char]; + + bb0: { + StorageLive(_2); + _2 = copy _1; + _0 = drop_in_place::<[char]>(move _2) -> [return: bb1, unwind continue]; + } + + bb1: { + StorageDead(_2); + return; + } + } + -- cgit 1.4.1-3-g733a5 From c4b91bd9f87391c1dda3784205c0dd10b54ec2e8 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Mon, 4 Aug 2025 10:49:43 +0200 Subject: Merge commit '482e8540a1b757ed7bccc2041c5400f051fdb01e' into subtree-update_cg_gcc_2025-08-04 --- build_system/src/abi_test.rs | 2 +- build_system/src/fuzz.rs | 8 +-- .../0001-Add-stdarch-Cargo.toml-for-testing.patch | 23 +++--- rust-toolchain | 2 +- src/allocator.rs | 14 ++-- src/builder.rs | 26 +++++-- src/int.rs | 76 +++++++++++++++++--- src/intrinsic/archs.rs | 60 ++++++++++++++++ src/intrinsic/mod.rs | 9 ++- src/intrinsic/simd.rs | 81 ++++++++++++++++++++++ src/lib.rs | 5 +- tests/failing-lto-tests.txt | 2 +- tests/failing-run-make-tests.txt | 1 - tests/failing-ui-tests.txt | 8 ++- tools/cspell_dicts/rustc_codegen_gcc.txt | 3 + 15 files changed, 275 insertions(+), 45 deletions(-) diff --git a/build_system/src/abi_test.rs b/build_system/src/abi_test.rs index 3c1531be27a..a85886d87f3 100644 --- a/build_system/src/abi_test.rs +++ b/build_system/src/abi_test.rs @@ -31,7 +31,7 @@ pub fn run() -> Result<(), String> { Some("clones/abi-cafe".as_ref()), true, ) - .map_err(|err| (format!("Git clone failed with message: {err:?}!")))?; + .map_err(|err| format!("Git clone failed with message: {err:?}!"))?; // Configure abi-cafe to use the exact same rustc version we use - this is crucial. // Otherwise, the concept of ABI compatibility becomes meanignless. std::fs::copy("rust-toolchain", "clones/abi-cafe/rust-toolchain") diff --git a/build_system/src/fuzz.rs b/build_system/src/fuzz.rs index 453211366b3..9714ce29af9 100644 --- a/build_system/src/fuzz.rs +++ b/build_system/src/fuzz.rs @@ -43,18 +43,18 @@ pub fn run() -> Result<(), String> { "--start" => { start = str::parse(&args.next().ok_or_else(|| "Fuzz start not provided!".to_string())?) - .map_err(|err| (format!("Fuzz start not a number {err:?}!")))?; + .map_err(|err| format!("Fuzz start not a number {err:?}!"))?; } "--count" => { count = str::parse(&args.next().ok_or_else(|| "Fuzz count not provided!".to_string())?) - .map_err(|err| (format!("Fuzz count not a number {err:?}!")))?; + .map_err(|err| format!("Fuzz count not a number {err:?}!"))?; } "-j" | "--jobs" => { threads = str::parse( &args.next().ok_or_else(|| "Fuzz thread count not provided!".to_string())?, ) - .map_err(|err| (format!("Fuzz thread count not a number {err:?}!")))?; + .map_err(|err| format!("Fuzz thread count not a number {err:?}!"))?; } _ => return Err(format!("Unknown option {arg}")), } @@ -66,7 +66,7 @@ pub fn run() -> Result<(), String> { Some("clones/rustlantis".as_ref()), true, ) - .map_err(|err| (format!("Git clone failed with message: {err:?}!")))?; + .map_err(|err| format!("Git clone failed with message: {err:?}!"))?; // Ensure that we are on the newest rustlantis commit. let cmd: &[&dyn AsRef] = &[&"git", &"pull", &"origin"]; diff --git a/patches/0001-Add-stdarch-Cargo.toml-for-testing.patch b/patches/0001-Add-stdarch-Cargo.toml-for-testing.patch index 9cc377850b9..3a8c37a8b8d 100644 --- a/patches/0001-Add-stdarch-Cargo.toml-for-testing.patch +++ b/patches/0001-Add-stdarch-Cargo.toml-for-testing.patch @@ -1,29 +1,28 @@ -From b8f3eed3053c9333b5dfbeaeb2a6a65a4b3156df Mon Sep 17 00:00:00 2001 -From: Antoni Boucher -Date: Tue, 29 Aug 2023 13:06:34 -0400 +From 190e26c9274b3c93a9ee3516b395590e6bd9213b Mon Sep 17 00:00:00 2001 +From: None +Date: Sun, 3 Aug 2025 19:54:56 -0400 Subject: [PATCH] Patch 0001-Add-stdarch-Cargo.toml-for-testing.patch --- - library/stdarch/Cargo.toml | 23 +++++++++++++++++++++++ - 1 file changed, 23 insertions(+) + library/stdarch/Cargo.toml | 20 ++++++++++++++++++++ + 1 file changed, 20 insertions(+) create mode 100644 library/stdarch/Cargo.toml diff --git a/library/stdarch/Cargo.toml b/library/stdarch/Cargo.toml new file mode 100644 -index 0000000..4c63700 +index 0000000..bd6725c --- /dev/null +++ b/library/stdarch/Cargo.toml -@@ -0,0 +1,21 @@ +@@ -0,0 +1,20 @@ +[workspace] +resolver = "1" +members = [ -+ "crates/core_arch", -+ "crates/std_detect", -+ "crates/stdarch-gen-arm", ++ "crates/*", + #"examples/" +] +exclude = [ -+ "crates/wasm-assert-instr-tests" ++ "crates/wasm-assert-instr-tests", ++ "rust_programs", +] + +[profile.release] @@ -36,5 +35,5 @@ index 0000000..4c63700 +opt-level = 3 +incremental = true -- -2.42.0 +2.50.1 diff --git a/rust-toolchain b/rust-toolchain index 2fe8ec4647f..058e734be5c 100644 --- a/rust-toolchain +++ b/rust-toolchain @@ -1,3 +1,3 @@ [toolchain] -channel = "nightly-2025-07-04" +channel = "nightly-2025-08-03" components = ["rust-src", "rustc-dev", "llvm-tools-preview"] diff --git a/src/allocator.rs b/src/allocator.rs index 0d8dc93274f..2a95a7368aa 100644 --- a/src/allocator.rs +++ b/src/allocator.rs @@ -99,11 +99,15 @@ fn create_const_value_function( let func = context.new_function(None, FunctionType::Exported, output, &[], name, false); #[cfg(feature = "master")] - func.add_attribute(FnAttribute::Visibility(symbol_visibility_to_gcc( - tcx.sess.default_visibility(), - ))); - - func.add_attribute(FnAttribute::AlwaysInline); + { + func.add_attribute(FnAttribute::Visibility(symbol_visibility_to_gcc( + tcx.sess.default_visibility(), + ))); + + // FIXME(antoyo): cg_llvm sets AlwaysInline, but AlwaysInline is different in GCC and using + // it here will causes linking errors when using LTO. + func.add_attribute(FnAttribute::Inline); + } if tcx.sess.must_emit_unwind_tables() { // TODO(antoyo): emit unwind tables. diff --git a/src/builder.rs b/src/builder.rs index 4aee211e2ef..34ade3d025f 100644 --- a/src/builder.rs +++ b/src/builder.rs @@ -540,9 +540,15 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> { fn ret(&mut self, mut value: RValue<'gcc>) { let expected_return_type = self.current_func().get_return_type(); - if !expected_return_type.is_compatible_with(value.get_type()) { - // NOTE: due to opaque pointers now being used, we need to cast here. - value = self.context.new_cast(self.location, value, expected_return_type); + let value_type = value.get_type(); + if !expected_return_type.is_compatible_with(value_type) { + // NOTE: due to opaque pointers now being used, we need to (bit)cast here. + if self.is_native_int_type(value_type) && self.is_native_int_type(expected_return_type) + { + value = self.context.new_cast(self.location, value, expected_return_type); + } else { + value = self.context.new_bitcast(self.location, value, expected_return_type); + } } self.llbb().end_with_return(self.location, value); } @@ -1279,11 +1285,19 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> { fn intcast( &mut self, - value: RValue<'gcc>, + mut value: RValue<'gcc>, dest_typ: Type<'gcc>, - _is_signed: bool, + is_signed: bool, ) -> RValue<'gcc> { - // NOTE: is_signed is for value, not dest_typ. + let value_type = value.get_type(); + if is_signed && !value_type.is_signed(self.cx) { + let signed_type = value_type.to_signed(self.cx); + value = self.gcc_int_cast(value, signed_type); + } else if !is_signed && value_type.is_signed(self.cx) { + let unsigned_type = value_type.to_unsigned(self.cx); + value = self.gcc_int_cast(value, unsigned_type); + } + self.gcc_int_cast(value, dest_typ) } diff --git a/src/int.rs b/src/int.rs index 6f21ce9352b..9fb7f6bad68 100644 --- a/src/int.rs +++ b/src/int.rs @@ -4,12 +4,15 @@ // cSpell:words cmpti divti modti mulodi muloti udivti umodti -use gccjit::{BinaryOp, ComparisonOp, FunctionType, Location, RValue, ToRValue, Type, UnaryOp}; +use gccjit::{ + BinaryOp, CType, ComparisonOp, FunctionType, Location, RValue, ToRValue, Type, UnaryOp, +}; use rustc_abi::{CanonAbi, Endian, ExternAbi}; use rustc_codegen_ssa::common::{IntPredicate, TypeKind}; use rustc_codegen_ssa::traits::{BackendTypes, BaseTypeCodegenMethods, BuilderMethods, OverflowOp}; use rustc_middle::ty::{self, Ty}; use rustc_target::callconv::{ArgAbi, ArgAttributes, FnAbi, PassMode}; +use rustc_type_ir::{Interner, TyKind}; use crate::builder::{Builder, ToGccComp}; use crate::common::{SignType, TypeReflection}; @@ -167,9 +170,9 @@ impl<'a, 'gcc, 'tcx> Builder<'a, 'gcc, 'tcx> { if a_type.is_vector() { // Vector types need to be bitcast. // TODO(antoyo): perhaps use __builtin_convertvector for vector casting. - b = self.context.new_bitcast(self.location, b, a.get_type()); + b = self.context.new_bitcast(self.location, b, a_type); } else { - b = self.context.new_cast(self.location, b, a.get_type()); + b = self.context.new_cast(self.location, b, a_type); } } self.context.new_binary_op(self.location, operation, a_type, a, b) @@ -216,13 +219,22 @@ impl<'a, 'gcc, 'tcx> Builder<'a, 'gcc, 'tcx> { operation_name: &str, signed: bool, a: RValue<'gcc>, - b: RValue<'gcc>, + mut b: RValue<'gcc>, ) -> RValue<'gcc> { let a_type = a.get_type(); let b_type = b.get_type(); if (self.is_native_int_type_or_bool(a_type) && self.is_native_int_type_or_bool(b_type)) || (a_type.is_vector() && b_type.is_vector()) { + if !a_type.is_compatible_with(b_type) { + if a_type.is_vector() { + // Vector types need to be bitcast. + // TODO(antoyo): perhaps use __builtin_convertvector for vector casting. + b = self.context.new_bitcast(self.location, b, a_type); + } else { + b = self.context.new_cast(self.location, b, a_type); + } + } self.context.new_binary_op(self.location, operation, a_type, a, b) } else { debug_assert!(a_type.dyncast_array().is_some()); @@ -351,6 +363,11 @@ impl<'a, 'gcc, 'tcx> Builder<'a, 'gcc, 'tcx> { // TODO(antoyo): is it correct to use rhs type instead of the parameter typ? .new_local(self.location, rhs.get_type(), "binopResult") .get_address(self.location); + let new_type = type_kind_to_gcc_type(new_kind); + let new_type = self.context.new_c_type(new_type); + let lhs = self.context.new_cast(self.location, lhs, new_type); + let rhs = self.context.new_cast(self.location, rhs, new_type); + let res = self.context.new_cast(self.location, res, new_type.make_pointer()); let overflow = self.overflow_call(intrinsic, &[lhs, rhs, res], None); (res.dereference(self.location).to_rvalue(), overflow) } @@ -477,11 +494,27 @@ impl<'a, 'gcc, 'tcx> Builder<'a, 'gcc, 'tcx> { let lhs_low = self.context.new_cast(self.location, self.low(lhs), unsigned_type); let rhs_low = self.context.new_cast(self.location, self.low(rhs), unsigned_type); + let mut lhs_high = self.high(lhs); + let mut rhs_high = self.high(rhs); + + match op { + IntPredicate::IntUGT + | IntPredicate::IntUGE + | IntPredicate::IntULT + | IntPredicate::IntULE => { + lhs_high = self.context.new_cast(self.location, lhs_high, unsigned_type); + rhs_high = self.context.new_cast(self.location, rhs_high, unsigned_type); + } + // TODO(antoyo): we probably need to handle signed comparison for unsigned + // integers. + _ => (), + } + let condition = self.context.new_comparison( self.location, ComparisonOp::LessThan, - self.high(lhs), - self.high(rhs), + lhs_high, + rhs_high, ); self.llbb().end_with_conditional(self.location, condition, block1, block2); @@ -495,8 +528,8 @@ impl<'a, 'gcc, 'tcx> Builder<'a, 'gcc, 'tcx> { let condition = self.context.new_comparison( self.location, ComparisonOp::GreaterThan, - self.high(lhs), - self.high(rhs), + lhs_high, + rhs_high, ); block2.end_with_conditional(self.location, condition, block3, block4); @@ -620,7 +653,7 @@ impl<'a, 'gcc, 'tcx> Builder<'a, 'gcc, 'tcx> { } } - pub fn gcc_xor(&self, a: RValue<'gcc>, b: RValue<'gcc>) -> RValue<'gcc> { + pub fn gcc_xor(&self, a: RValue<'gcc>, mut b: RValue<'gcc>) -> RValue<'gcc> { let a_type = a.get_type(); let b_type = b.get_type(); if a_type.is_vector() && b_type.is_vector() { @@ -628,6 +661,9 @@ impl<'a, 'gcc, 'tcx> Builder<'a, 'gcc, 'tcx> { a ^ b } else if self.is_native_int_type_or_bool(a_type) && self.is_native_int_type_or_bool(b_type) { + if !a_type.is_compatible_with(b_type) { + b = self.context.new_cast(self.location, b, a_type); + } a ^ b } else { self.concat_low_high_rvalues( @@ -1042,3 +1078,25 @@ impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> { self.context.new_array_constructor(None, typ, &values) } } + +fn type_kind_to_gcc_type(kind: TyKind) -> CType { + use rustc_middle::ty::IntTy::*; + use rustc_middle::ty::UintTy::*; + use rustc_middle::ty::{Int, Uint}; + + match kind { + Int(I8) => CType::Int8t, + Int(I16) => CType::Int16t, + Int(I32) => CType::Int32t, + Int(I64) => CType::Int64t, + Int(I128) => CType::Int128t, + + Uint(U8) => CType::UInt8t, + Uint(U16) => CType::UInt16t, + Uint(U32) => CType::UInt32t, + Uint(U64) => CType::UInt64t, + Uint(U128) => CType::UInt128t, + + _ => unimplemented!("Kind: {:?}", kind), + } +} diff --git a/src/intrinsic/archs.rs b/src/intrinsic/archs.rs index 915ed875e32..d1b2a93243d 100644 --- a/src/intrinsic/archs.rs +++ b/src/intrinsic/archs.rs @@ -95,8 +95,11 @@ fn map_arch_intrinsic(full_name: &str) -> &'static str { "cubema" => "__builtin_amdgcn_cubema", "cubesc" => "__builtin_amdgcn_cubesc", "cubetc" => "__builtin_amdgcn_cubetc", + "cvt.f16.bf8" => "__builtin_amdgcn_cvt_f16_bf8", + "cvt.f16.fp8" => "__builtin_amdgcn_cvt_f16_fp8", "cvt.f32.bf8" => "__builtin_amdgcn_cvt_f32_bf8", "cvt.f32.fp8" => "__builtin_amdgcn_cvt_f32_fp8", + "cvt.f32.fp8.e5m3" => "__builtin_amdgcn_cvt_f32_fp8_e5m3", "cvt.off.f32.i4" => "__builtin_amdgcn_cvt_off_f32_i4", "cvt.pk.bf8.f32" => "__builtin_amdgcn_cvt_pk_bf8_f32", "cvt.pk.f16.bf8" => "__builtin_amdgcn_cvt_pk_f16_bf8", @@ -181,6 +184,12 @@ fn map_arch_intrinsic(full_name: &str) -> &'static str { "dot4.f32.fp8.bf8" => "__builtin_amdgcn_dot4_f32_fp8_bf8", "dot4.f32.fp8.fp8" => "__builtin_amdgcn_dot4_f32_fp8_fp8", "ds.add.gs.reg.rtn" => "__builtin_amdgcn_ds_add_gs_reg_rtn", + "ds.atomic.async.barrier.arrive.b64" => { + "__builtin_amdgcn_ds_atomic_async_barrier_arrive_b64" + } + "ds.atomic.barrier.arrive.rtn.b64" => { + "__builtin_amdgcn_ds_atomic_barrier_arrive_rtn_b64" + } "ds.bpermute" => "__builtin_amdgcn_ds_bpermute", "ds.bpermute.fi.b32" => "__builtin_amdgcn_ds_bpermute_fi_b32", "ds.gws.barrier" => "__builtin_amdgcn_ds_gws_barrier", @@ -198,8 +207,32 @@ fn map_arch_intrinsic(full_name: &str) -> &'static str { "fdot2.f16.f16" => "__builtin_amdgcn_fdot2_f16_f16", "fdot2.f32.bf16" => "__builtin_amdgcn_fdot2_f32_bf16", "fdot2c.f32.bf16" => "__builtin_amdgcn_fdot2c_f32_bf16", + "flat.prefetch" => "__builtin_amdgcn_flat_prefetch", "fmul.legacy" => "__builtin_amdgcn_fmul_legacy", + "global.load.async.to.lds.b128" => { + "__builtin_amdgcn_global_load_async_to_lds_b128" + } + "global.load.async.to.lds.b32" => { + "__builtin_amdgcn_global_load_async_to_lds_b32" + } + "global.load.async.to.lds.b64" => { + "__builtin_amdgcn_global_load_async_to_lds_b64" + } + "global.load.async.to.lds.b8" => "__builtin_amdgcn_global_load_async_to_lds_b8", "global.load.lds" => "__builtin_amdgcn_global_load_lds", + "global.prefetch" => "__builtin_amdgcn_global_prefetch", + "global.store.async.from.lds.b128" => { + "__builtin_amdgcn_global_store_async_from_lds_b128" + } + "global.store.async.from.lds.b32" => { + "__builtin_amdgcn_global_store_async_from_lds_b32" + } + "global.store.async.from.lds.b64" => { + "__builtin_amdgcn_global_store_async_from_lds_b64" + } + "global.store.async.from.lds.b8" => { + "__builtin_amdgcn_global_store_async_from_lds_b8" + } "groupstaticsize" => "__builtin_amdgcn_groupstaticsize", "iglp.opt" => "__builtin_amdgcn_iglp_opt", "implicit.buffer.ptr" => "__builtin_amdgcn_implicit_buffer_ptr", @@ -291,6 +324,7 @@ fn map_arch_intrinsic(full_name: &str) -> &'static str { "s.incperflevel" => "__builtin_amdgcn_s_incperflevel", "s.memrealtime" => "__builtin_amdgcn_s_memrealtime", "s.memtime" => "__builtin_amdgcn_s_memtime", + "s.monitor.sleep" => "__builtin_amdgcn_s_monitor_sleep", "s.sendmsg" => "__builtin_amdgcn_s_sendmsg", "s.sendmsghalt" => "__builtin_amdgcn_s_sendmsghalt", "s.setprio" => "__builtin_amdgcn_s_setprio", @@ -300,11 +334,15 @@ fn map_arch_intrinsic(full_name: &str) -> &'static str { "s.sleep.var" => "__builtin_amdgcn_s_sleep_var", "s.ttracedata" => "__builtin_amdgcn_s_ttracedata", "s.ttracedata.imm" => "__builtin_amdgcn_s_ttracedata_imm", + "s.wait.asynccnt" => "__builtin_amdgcn_s_wait_asynccnt", "s.wait.event.export.ready" => "__builtin_amdgcn_s_wait_event_export_ready", + "s.wait.tensorcnt" => "__builtin_amdgcn_s_wait_tensorcnt", "s.waitcnt" => "__builtin_amdgcn_s_waitcnt", "sad.hi.u8" => "__builtin_amdgcn_sad_hi_u8", "sad.u16" => "__builtin_amdgcn_sad_u16", "sad.u8" => "__builtin_amdgcn_sad_u8", + "sat.pk4.i4.i8" => "__builtin_amdgcn_sat_pk4_i4_i8", + "sat.pk4.u4.u8" => "__builtin_amdgcn_sat_pk4_u4_u8", "sched.barrier" => "__builtin_amdgcn_sched_barrier", "sched.group.barrier" => "__builtin_amdgcn_sched_group_barrier", "sdot2" => "__builtin_amdgcn_sdot2", @@ -346,8 +384,13 @@ fn map_arch_intrinsic(full_name: &str) -> &'static str { "smfmac.i32.16x16x64.i8" => "__builtin_amdgcn_smfmac_i32_16x16x64_i8", "smfmac.i32.32x32x32.i8" => "__builtin_amdgcn_smfmac_i32_32x32x32_i8", "smfmac.i32.32x32x64.i8" => "__builtin_amdgcn_smfmac_i32_32x32x64_i8", + "struct.ptr.buffer.load.lds" => "__builtin_amdgcn_struct_ptr_buffer_load_lds", "sudot4" => "__builtin_amdgcn_sudot4", "sudot8" => "__builtin_amdgcn_sudot8", + "tensor.load.to.lds" => "__builtin_amdgcn_tensor_load_to_lds", + "tensor.load.to.lds.d2" => "__builtin_amdgcn_tensor_load_to_lds_d2", + "tensor.store.from.lds" => "__builtin_amdgcn_tensor_store_from_lds", + "tensor.store.from.lds.d2" => "__builtin_amdgcn_tensor_store_from_lds_d2", "udot2" => "__builtin_amdgcn_udot2", "udot4" => "__builtin_amdgcn_udot4", "udot8" => "__builtin_amdgcn_udot8", @@ -6326,6 +6369,23 @@ fn map_arch_intrinsic(full_name: &str) -> &'static str { } s390(name, full_name) } + "spv" => { + #[allow(non_snake_case)] + fn spv(name: &str, full_name: &str) -> &'static str { + match name { + // spv + "num.subgroups" => "__builtin_spirv_num_subgroups", + "subgroup.id" => "__builtin_spirv_subgroup_id", + "subgroup.local.invocation.id" => { + "__builtin_spirv_subgroup_local_invocation_id" + } + "subgroup.max.size" => "__builtin_spirv_subgroup_max_size", + "subgroup.size" => "__builtin_spirv_subgroup_size", + _ => unimplemented!("***** unsupported LLVM intrinsic {full_name}"), + } + } + spv(name, full_name) + } "ve" => { #[allow(non_snake_case)] fn ve(name: &str, full_name: &str) -> &'static str { diff --git a/src/intrinsic/mod.rs b/src/intrinsic/mod.rs index 0753ac1aeb8..eb0a5336a1f 100644 --- a/src/intrinsic/mod.rs +++ b/src/intrinsic/mod.rs @@ -925,10 +925,17 @@ impl<'a, 'gcc, 'tcx> Builder<'a, 'gcc, 'tcx> { // TODO(antoyo): use width? let arg_type = arg.get_type(); let result_type = self.u32_type; + let arg = if arg_type.is_signed(self.cx) { + let new_type = arg_type.to_unsigned(self.cx); + self.gcc_int_cast(arg, new_type) + } else { + arg + }; + let arg_type = arg.get_type(); let count_leading_zeroes = // TODO(antoyo): write a new function Type::is_compatible_with(&Type) and use it here // instead of using is_uint(). - if arg_type.is_uint(self.cx) { + if arg_type.is_uchar(self.cx) || arg_type.is_ushort(self.cx) || arg_type.is_uint(self.cx) { "__builtin_clz" } else if arg_type.is_ulong(self.cx) { diff --git a/src/intrinsic/simd.rs b/src/intrinsic/simd.rs index 350915a277e..fdc15d580ef 100644 --- a/src/intrinsic/simd.rs +++ b/src/intrinsic/simd.rs @@ -206,6 +206,28 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>( ); } + #[cfg(feature = "master")] + if name == sym::simd_funnel_shl { + return Ok(simd_funnel_shift( + bx, + args[0].immediate(), + args[1].immediate(), + args[2].immediate(), + true, + )); + } + + #[cfg(feature = "master")] + if name == sym::simd_funnel_shr { + return Ok(simd_funnel_shift( + bx, + args[0].immediate(), + args[1].immediate(), + args[2].immediate(), + false, + )); + } + if name == sym::simd_bswap { return Ok(simd_bswap(bx, args[0].immediate())); } @@ -1434,3 +1456,62 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>( unimplemented!("simd {}", name); } + +#[cfg(feature = "master")] +fn simd_funnel_shift<'a, 'gcc, 'tcx>( + bx: &mut Builder<'a, 'gcc, 'tcx>, + a: RValue<'gcc>, + b: RValue<'gcc>, + shift: RValue<'gcc>, + shift_left: bool, +) -> RValue<'gcc> { + use crate::common::SignType; + + let a_type = a.get_type(); + let vector_type = a_type.unqualified().dyncast_vector().expect("vector type"); + let num_units = vector_type.get_num_units(); + let elem_type = vector_type.get_element_type(); + + let (new_int_type, int_shift_val, int_mask) = if elem_type.is_compatible_with(bx.u8_type) + || elem_type.is_compatible_with(bx.i8_type) + { + (bx.u16_type, 8, u8::MAX as u64) + } else if elem_type.is_compatible_with(bx.u16_type) || elem_type.is_compatible_with(bx.i16_type) + { + (bx.u32_type, 16, u16::MAX as u64) + } else if elem_type.is_compatible_with(bx.u32_type) || elem_type.is_compatible_with(bx.i32_type) + { + (bx.u64_type, 32, u32::MAX as u64) + } else if elem_type.is_compatible_with(bx.u64_type) || elem_type.is_compatible_with(bx.i64_type) + { + (bx.u128_type, 64, u64::MAX) + } else { + unimplemented!("funnel shift on {:?}", elem_type); + }; + + let int_mask = bx.context.new_rvalue_from_long(new_int_type, int_mask as i64); + let int_shift_val = bx.context.new_rvalue_from_int(new_int_type, int_shift_val); + let mut elements = vec![]; + let unsigned_type = elem_type.to_unsigned(bx); + for i in 0..num_units { + let index = bx.context.new_rvalue_from_int(bx.int_type, i as i32); + let a_val = bx.context.new_vector_access(None, a, index).to_rvalue(); + let a_val = bx.context.new_bitcast(None, a_val, unsigned_type); + // TODO: we probably need to use gcc_int_cast instead. + let a_val = bx.gcc_int_cast(a_val, new_int_type); + let b_val = bx.context.new_vector_access(None, b, index).to_rvalue(); + let b_val = bx.context.new_bitcast(None, b_val, unsigned_type); + let b_val = bx.gcc_int_cast(b_val, new_int_type); + let shift_val = bx.context.new_vector_access(None, shift, index).to_rvalue(); + let shift_val = bx.gcc_int_cast(shift_val, new_int_type); + let mut val = a_val << int_shift_val | b_val; + if shift_left { + val = (val << shift_val) >> int_shift_val; + } else { + val = (val >> shift_val) & int_mask; + } + let val = bx.gcc_int_cast(val, elem_type); + elements.push(val); + } + bx.context.new_rvalue_from_vector(None, a_type, &elements) +} diff --git a/src/lib.rs b/src/lib.rs index 613315f77a6..b11f11d38e3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -50,6 +50,7 @@ extern crate rustc_session; extern crate rustc_span; extern crate rustc_symbol_mangling; extern crate rustc_target; +extern crate rustc_type_ir; // This prevents duplicating functions and statics that are already part of the host rustc process. #[allow(unused_extern_crates)] @@ -362,9 +363,9 @@ impl WriteBackendMethods for GccCodegenBackend { _exported_symbols_for_lto: &[String], each_linked_rlib_for_lto: &[PathBuf], modules: Vec>, - diff_fncs: Vec, + diff_functions: Vec, ) -> Result, FatalError> { - if !diff_fncs.is_empty() { + if !diff_functions.is_empty() { unimplemented!(); } diff --git a/tests/failing-lto-tests.txt b/tests/failing-lto-tests.txt index b9126fb73a7..bf0633f7320 100644 --- a/tests/failing-lto-tests.txt +++ b/tests/failing-lto-tests.txt @@ -28,6 +28,6 @@ tests/ui/macros/macro-comma-behavior-rpass.rs tests/ui/macros/rfc-2011-nicer-assert-messages/assert-with-custom-errors-does-not-create-unnecessary-code.rs tests/ui/macros/rfc-2011-nicer-assert-messages/feature-gate-generic_assert.rs tests/ui/macros/stringify.rs -tests/ui/reexport-test-harness-main.rs tests/ui/rfcs/rfc-1937-termination-trait/termination-trait-in-test.rs tests/ui/binding/fn-arg-incomplete-pattern-drop-order.rs +tests/ui/lto/debuginfo-lto-alloc.rs diff --git a/tests/failing-run-make-tests.txt b/tests/failing-run-make-tests.txt index 842533cd3c6..29032b321fa 100644 --- a/tests/failing-run-make-tests.txt +++ b/tests/failing-run-make-tests.txt @@ -6,7 +6,6 @@ tests/run-make/doctests-keep-binaries/ tests/run-make/doctests-runtool/ tests/run-make/emit-shared-files/ tests/run-make/exit-code/ -tests/run-make/issue-22131/ tests/run-make/issue-64153/ tests/run-make/llvm-ident/ tests/run-make/native-link-modifier-bundle/ diff --git a/tests/failing-ui-tests.txt b/tests/failing-ui-tests.txt index 6979c04d534..41fb4729c07 100644 --- a/tests/failing-ui-tests.txt +++ b/tests/failing-ui-tests.txt @@ -10,11 +10,10 @@ tests/ui/iterators/iter-sum-overflow-overflow-checks.rs tests/ui/mir/mir_drop_order.rs tests/ui/mir/mir_let_chains_drop_order.rs tests/ui/mir/mir_match_guard_let_chains_drop_order.rs -tests/ui/oom_unwind.rs +tests/ui/panics/oom-panic-unwind.rs tests/ui/panic-runtime/abort-link-to-unwinding-crates.rs tests/ui/panic-runtime/abort.rs tests/ui/panic-runtime/link-to-abort.rs -tests/ui/unwind-no-uwtable.rs tests/ui/parser/unclosed-delimiter-in-dep.rs tests/ui/consts/missing_span_in_backtrace.rs tests/ui/drop/dynamic-drop.rs @@ -82,3 +81,8 @@ tests/ui/coroutine/panic-drops.rs tests/ui/coroutine/panic-safe.rs tests/ui/process/nofile-limit.rs tests/ui/simd/intrinsic/generic-arithmetic-pass.rs +tests/ui/linking/no-gc-encapsulation-symbols.rs +tests/ui/panics/unwind-force-no-unwind-tables.rs +tests/ui/attributes/fn-align-dyn.rs +tests/ui/linkage-attr/raw-dylib/elf/glibc-x86_64.rs +tests/ui/explicit-tail-calls/recursion-etc.rs diff --git a/tools/cspell_dicts/rustc_codegen_gcc.txt b/tools/cspell_dicts/rustc_codegen_gcc.txt index 31023e50ffa..4fb018b3ecd 100644 --- a/tools/cspell_dicts/rustc_codegen_gcc.txt +++ b/tools/cspell_dicts/rustc_codegen_gcc.txt @@ -8,6 +8,7 @@ clzll cmse codegened csky +ctfe ctlz ctpop cttz @@ -25,6 +26,7 @@ fwrapv gimple hrtb immediates +interner liblto llbb llcx @@ -47,6 +49,7 @@ mavx mcmodel minimumf minnumf +miri monomorphization monomorphizations monomorphized -- cgit 1.4.1-3-g733a5 From 1c793602ae4183d4b114e62affb4c7035cb130ad Mon Sep 17 00:00:00 2001 From: Antoni Boucher Date: Mon, 4 Aug 2025 09:47:33 -0400 Subject: Reenable m68k CI --- .github/workflows/m68k.yml | 38 ++++++++++++++++---------------------- 1 file changed, 16 insertions(+), 22 deletions(-) diff --git a/.github/workflows/m68k.yml b/.github/workflows/m68k.yml index 759d0d59e26..176ee8628b0 100644 --- a/.github/workflows/m68k.yml +++ b/.github/workflows/m68k.yml @@ -91,11 +91,7 @@ jobs: ./y.sh prepare --only-libcore --cross ./y.sh build --sysroot --features compiler-builtins-no-f16-f128 --target-triple m68k-unknown-linux-gnu ./y.sh test --mini-tests --target-triple m68k-unknown-linux-gnu - # FIXME: since https://github.com/rust-lang/rust/pull/140809, we cannot run programs for architectures not - # supported by the object crate, since this adds a dependency on symbols.o for the panic runtime. - # And as such, a wrong order of the object files in the linker command now fails with an undefined reference - # to some symbols like __rustc::rust_panic. - #CG_GCC_TEST_TARGET=m68k-unknown-linux-gnu ./y.sh test --cargo-tests --target-triple m68k-unknown-linux-gnu + CG_GCC_TEST_TARGET=m68k-unknown-linux-gnu ./y.sh test --cargo-tests --target-triple m68k-unknown-linux-gnu ./y.sh clean all - name: Prepare dependencies @@ -104,23 +100,21 @@ jobs: git config --global user.name "User" ./y.sh prepare --cross - # FIXME: We cannot run programs for architectures not supported by the object crate. See comment above. - #- name: Run tests - #run: | - #./y.sh test --target-triple m68k-unknown-linux-gnu --release --clean --build-sysroot --sysroot-features compiler-builtins-no-f16-f128 ${{ matrix.commands }} - - # FIXME: We cannot run programs for architectures not supported by the object crate. See comment above. - #- name: Run Hello World! - #run: | - #./y.sh build --target-triple m68k-unknown-linux-gnu - - #vm_dir=$(pwd)/vm - #cd tests/hello-world - #CG_RUSTFLAGS="-Clinker=m68k-unknown-linux-gnu-gcc" ../../y.sh cargo build --target m68k-unknown-linux-gnu - #sudo cp target/m68k-unknown-linux-gnu/debug/hello_world $vm_dir/home/ - #sudo chroot $vm_dir qemu-m68k-static /home/hello_world > hello_world_stdout - #expected_output="40" - #test $(cat hello_world_stdout) == $expected_output || (echo "Output differs. Actual output: $(cat hello_world_stdout)"; exit 1) + - name: Run tests + run: | + ./y.sh test --target-triple m68k-unknown-linux-gnu --release --clean --build-sysroot --sysroot-features compiler-builtins-no-f16-f128 ${{ matrix.commands }} + + - name: Run Hello World! + run: | + ./y.sh build --target-triple m68k-unknown-linux-gnu + + vm_dir=$(pwd)/vm + cd tests/hello-world + CG_RUSTFLAGS="-Clinker=m68k-unknown-linux-gnu-gcc" ../../y.sh cargo build --target m68k-unknown-linux-gnu + sudo cp target/m68k-unknown-linux-gnu/debug/hello_world $vm_dir/home/ + sudo chroot $vm_dir qemu-m68k-static /home/hello_world > hello_world_stdout + expected_output="40" + test $(cat hello_world_stdout) == $expected_output || (echo "Output differs. Actual output: $(cat hello_world_stdout)"; exit 1) # Summary job for the merge queue. # ALL THE PREVIOUS JOBS NEED TO BE ADDED TO THE `needs` SECTION OF THIS JOB! -- cgit 1.4.1-3-g733a5 From aa51e18ae6c86e7c44f3d77ea40a2a9c4e967235 Mon Sep 17 00:00:00 2001 From: Antoni Boucher Date: Mon, 4 Aug 2025 10:06:57 -0400 Subject: Remove outdated comment --- src/intrinsic/simd.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/intrinsic/simd.rs b/src/intrinsic/simd.rs index fdc15d580ef..41363d6313d 100644 --- a/src/intrinsic/simd.rs +++ b/src/intrinsic/simd.rs @@ -1497,7 +1497,6 @@ fn simd_funnel_shift<'a, 'gcc, 'tcx>( let index = bx.context.new_rvalue_from_int(bx.int_type, i as i32); let a_val = bx.context.new_vector_access(None, a, index).to_rvalue(); let a_val = bx.context.new_bitcast(None, a_val, unsigned_type); - // TODO: we probably need to use gcc_int_cast instead. let a_val = bx.gcc_int_cast(a_val, new_int_type); let b_val = bx.context.new_vector_access(None, b, index).to_rvalue(); let b_val = bx.context.new_bitcast(None, b_val, unsigned_type); -- cgit 1.4.1-3-g733a5 From d85a64531673c42ef8a7e214305751f27da53bac Mon Sep 17 00:00:00 2001 From: Jakub Beránek Date: Mon, 4 Aug 2025 16:22:05 +0200 Subject: Require approval from t-infra instead of t-release on tier bumps --- src/doc/rustc/src/target-tier-policy.md | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/doc/rustc/src/target-tier-policy.md b/src/doc/rustc/src/target-tier-policy.md index a0acfdf0e4a..28d3dc32a63 100644 --- a/src/doc/rustc/src/target-tier-policy.md +++ b/src/doc/rustc/src/target-tier-policy.md @@ -534,10 +534,10 @@ tests, and will reject patches that fail to build or pass the testsuite on a target. We hold tier 1 targets to our highest standard of requirements. A proposed new tier 1 target must be reviewed and approved by the compiler team -based on these requirements. In addition, the release team must approve the -viability and value of supporting the target. For a tier 1 target, this will +based on these requirements. In addition, the infra team must approve the +viability of supporting the target. For a tier 1 target, this will typically take place via a full RFC proposing the target, to be jointly -reviewed and approved by the compiler team and release team. +reviewed and approved by the compiler team and infra team. In addition, the infrastructure team must approve the integration of the target into Continuous Integration (CI), and the tier 1 CI-related requirements. This @@ -617,7 +617,7 @@ including the infrastructure team in the RFC proposing the target. A tier 1 target may be demoted if it no longer meets these requirements but still meets the requirements for a lower tier. Any proposal for demotion of a tier 1 target requires a full RFC process, with approval by the compiler and -release teams. Any such proposal will be communicated widely to the Rust +infra teams. Any such proposal will be communicated widely to the Rust community, both when initially proposed and before being dropped from a stable release. A tier 1 target is highly unlikely to be directly removed without first being demoted to tier 2 or tier 3. (The amount of time between such @@ -628,7 +628,7 @@ planned and scheduled action.) Raising the baseline expectations of a tier 1 target (such as the minimum CPU features or OS version required) requires the approval of the compiler and -release teams, and should be widely communicated as well, but does not +infra teams, and should be widely communicated as well, but does not necessarily require a full RFC. ### Tier 1 with host tools @@ -638,11 +638,11 @@ host (such as `rustc` and `cargo`). This allows the target to be used as a development platform, not just a compilation target. A proposed new tier 1 target with host tools must be reviewed and approved by -the compiler team based on these requirements. In addition, the release team -must approve the viability and value of supporting host tools for the target. +the compiler team based on these requirements. In addition, the infra team +must approve the viability of supporting host tools for the target. For a tier 1 target, this will typically take place via a full RFC proposing the target, to be jointly reviewed and approved by the compiler team and -release team. +infra team. In addition, the infrastructure team must approve the integration of the target's host tools into Continuous Integration (CI), and the CI-related @@ -697,7 +697,7 @@ target with host tools may be demoted (including having its host tools dropped, or being demoted to tier 2 with host tools) if it no longer meets these requirements but still meets the requirements for a lower tier. Any proposal for demotion of a tier 1 target (with or without host tools) requires a full -RFC process, with approval by the compiler and release teams. Any such proposal +RFC process, with approval by the compiler and infra teams. Any such proposal will be communicated widely to the Rust community, both when initially proposed and before being dropped from a stable release. -- cgit 1.4.1-3-g733a5 From 6e75abd0aa4f22f6291f1a25ff81e0c6b959ad26 Mon Sep 17 00:00:00 2001 From: Ayush Singh Date: Sun, 20 Jul 2025 22:35:43 +0530 Subject: std: sys: io: io_slice: Add UEFI types UEFI networking APIs do support vectored read/write. While the types for UDP4, UDP6, TCP4 and TCP6 are defined separately, they are essentially the same C struct. So we can map IoSlice and IoSliceMut to have the same binary representation. Since all UEFI networking types for read/write are DSTs, `IoSlice` and `IoSliceMut` will need to be copied to the end of the transmit/receive structures. So having the same binary representation just allows us to do a single memcpy instead of having to loop and set the DST. Signed-off-by: Ayush Singh --- library/std/src/sys/io/io_slice/uefi.rs | 74 ++++++++++++++++++++ library/std/src/sys/io/mod.rs | 3 + library/std/src/sys/pal/uefi/tests.rs | 119 ++++++++++++++++++++++++++++++++ 3 files changed, 196 insertions(+) create mode 100644 library/std/src/sys/io/io_slice/uefi.rs diff --git a/library/std/src/sys/io/io_slice/uefi.rs b/library/std/src/sys/io/io_slice/uefi.rs new file mode 100644 index 00000000000..909cfbea0b7 --- /dev/null +++ b/library/std/src/sys/io/io_slice/uefi.rs @@ -0,0 +1,74 @@ +//! A buffer type used with `Write::write_vectored` for UEFI Networking APIs. Vectored writing to +//! File is not supported as of UEFI Spec 2.11. + +use crate::marker::PhantomData; +use crate::slice; + +#[derive(Copy, Clone)] +#[repr(C)] +pub struct IoSlice<'a> { + len: u32, + data: *const u8, + _p: PhantomData<&'a [u8]>, +} + +impl<'a> IoSlice<'a> { + #[inline] + pub fn new(buf: &'a [u8]) -> IoSlice<'a> { + let len = buf.len().try_into().unwrap(); + Self { len, data: buf.as_ptr(), _p: PhantomData } + } + + #[inline] + pub fn advance(&mut self, n: usize) { + self.len = u32::try_from(n) + .ok() + .and_then(|n| self.len.checked_sub(n)) + .expect("advancing IoSlice beyond its length"); + unsafe { self.data = self.data.add(n) }; + } + + #[inline] + pub const fn as_slice(&self) -> &'a [u8] { + unsafe { slice::from_raw_parts(self.data, self.len as usize) } + } +} + +#[repr(C)] +pub struct IoSliceMut<'a> { + len: u32, + data: *mut u8, + _p: PhantomData<&'a mut [u8]>, +} + +impl<'a> IoSliceMut<'a> { + #[inline] + pub fn new(buf: &'a mut [u8]) -> IoSliceMut<'a> { + let len = buf.len().try_into().unwrap(); + Self { len, data: buf.as_mut_ptr(), _p: PhantomData } + } + + #[inline] + pub fn advance(&mut self, n: usize) { + self.len = u32::try_from(n) + .ok() + .and_then(|n| self.len.checked_sub(n)) + .expect("advancing IoSlice beyond its length"); + unsafe { self.data = self.data.add(n) }; + } + + #[inline] + pub fn as_slice(&self) -> &[u8] { + unsafe { slice::from_raw_parts(self.data, self.len as usize) } + } + + #[inline] + pub const fn into_slice(self) -> &'a mut [u8] { + unsafe { slice::from_raw_parts_mut(self.data, self.len as usize) } + } + + #[inline] + pub fn as_mut_slice(&mut self) -> &mut [u8] { + unsafe { slice::from_raw_parts_mut(self.data, self.len as usize) } + } +} diff --git a/library/std/src/sys/io/mod.rs b/library/std/src/sys/io/mod.rs index 4d0365d42fd..ae75f4d97b4 100644 --- a/library/std/src/sys/io/mod.rs +++ b/library/std/src/sys/io/mod.rs @@ -11,6 +11,9 @@ mod io_slice { } else if #[cfg(target_os = "wasi")] { mod wasi; pub use wasi::*; + } else if #[cfg(target_os = "uefi")] { + mod uefi; + pub use uefi::*; } else { mod unsupported; pub use unsupported::*; diff --git a/library/std/src/sys/pal/uefi/tests.rs b/library/std/src/sys/pal/uefi/tests.rs index 38658cc4e9a..49e75a1a70d 100644 --- a/library/std/src/sys/pal/uefi/tests.rs +++ b/library/std/src/sys/pal/uefi/tests.rs @@ -1,5 +1,6 @@ use super::alloc::*; use super::time::*; +use crate::io::{IoSlice, IoSliceMut}; use crate::time::Duration; #[test] @@ -39,3 +40,121 @@ fn epoch() { }; assert_eq!(system_time_internal::uefi_time_to_duration(t), Duration::new(0, 0)); } + +// UEFI IoSlice and IoSliceMut Tests +// +// Strictly speaking, vectored read/write types for UDP4, UDP6, TCP4, TCP6 are defined +// separately in the UEFI Spec. However, they have the same signature. These tests just ensure +// that `IoSlice` and `IoSliceMut` are compatible with the vectored types for all the +// networking protocols. + +unsafe fn to_slice(val: &T) -> &[u8] { + let len = size_of_val(val); + unsafe { crate::slice::from_raw_parts(crate::ptr::from_ref(val).cast(), len) } +} + +#[test] +fn io_slice_single() { + let mut data = [0, 1, 2, 3, 4]; + + let tcp4_frag = r_efi::protocols::tcp4::FragmentData { + fragment_length: data.len().try_into().unwrap(), + fragment_buffer: data.as_mut_ptr().cast(), + }; + let tcp6_frag = r_efi::protocols::tcp6::FragmentData { + fragment_length: data.len().try_into().unwrap(), + fragment_buffer: data.as_mut_ptr().cast(), + }; + let udp4_frag = r_efi::protocols::udp4::FragmentData { + fragment_length: data.len().try_into().unwrap(), + fragment_buffer: data.as_mut_ptr().cast(), + }; + let udp6_frag = r_efi::protocols::udp6::FragmentData { + fragment_length: data.len().try_into().unwrap(), + fragment_buffer: data.as_mut_ptr().cast(), + }; + let io_slice = IoSlice::new(&data); + + unsafe { + assert_eq!(to_slice(&io_slice), to_slice(&tcp4_frag)); + assert_eq!(to_slice(&io_slice), to_slice(&tcp6_frag)); + assert_eq!(to_slice(&io_slice), to_slice(&udp4_frag)); + assert_eq!(to_slice(&io_slice), to_slice(&udp6_frag)); + } +} + +#[test] +fn io_slice_mut_single() { + let mut data = [0, 1, 2, 3, 4]; + + let tcp4_frag = r_efi::protocols::tcp4::FragmentData { + fragment_length: data.len().try_into().unwrap(), + fragment_buffer: data.as_mut_ptr().cast(), + }; + let tcp6_frag = r_efi::protocols::tcp6::FragmentData { + fragment_length: data.len().try_into().unwrap(), + fragment_buffer: data.as_mut_ptr().cast(), + }; + let udp4_frag = r_efi::protocols::udp4::FragmentData { + fragment_length: data.len().try_into().unwrap(), + fragment_buffer: data.as_mut_ptr().cast(), + }; + let udp6_frag = r_efi::protocols::udp6::FragmentData { + fragment_length: data.len().try_into().unwrap(), + fragment_buffer: data.as_mut_ptr().cast(), + }; + let io_slice_mut = IoSliceMut::new(&mut data); + + unsafe { + assert_eq!(to_slice(&io_slice_mut), to_slice(&tcp4_frag)); + assert_eq!(to_slice(&io_slice_mut), to_slice(&tcp6_frag)); + assert_eq!(to_slice(&io_slice_mut), to_slice(&udp4_frag)); + assert_eq!(to_slice(&io_slice_mut), to_slice(&udp6_frag)); + } +} + +#[test] +fn io_slice_multi() { + let mut data = [0, 1, 2, 3, 4]; + + let tcp4_frag = r_efi::protocols::tcp4::FragmentData { + fragment_length: data.len().try_into().unwrap(), + fragment_buffer: data.as_mut_ptr().cast(), + }; + let rhs = + [tcp4_frag.clone(), tcp4_frag.clone(), tcp4_frag.clone(), tcp4_frag.clone(), tcp4_frag]; + let lhs = [ + IoSlice::new(&data), + IoSlice::new(&data), + IoSlice::new(&data), + IoSlice::new(&data), + IoSlice::new(&data), + ]; + + unsafe { + assert_eq!(to_slice(&lhs), to_slice(&rhs)); + } +} + +#[test] +fn io_slice_basic() { + let data = [0, 1, 2, 3, 4]; + let mut io_slice = IoSlice::new(&data); + + assert_eq!(data, io_slice.as_slice()); + io_slice.advance(2); + assert_eq!(&data[2..], io_slice.as_slice()); +} + +#[test] +fn io_slice_mut_basic() { + let data = [0, 1, 2, 3, 4]; + let mut data_clone = [0, 1, 2, 3, 4]; + let mut io_slice_mut = IoSliceMut::new(&mut data_clone); + + assert_eq!(data, io_slice_mut.as_slice()); + assert_eq!(data, io_slice_mut.as_mut_slice()); + + io_slice_mut.advance(2); + assert_eq!(&data[2..], io_slice_mut.into_slice()); +} -- cgit 1.4.1-3-g733a5 From 2db126d651b6803c9b37fd021376ce6e5dd5a09e Mon Sep 17 00:00:00 2001 From: Esteban Küber Date: Sat, 1 Mar 2025 22:27:16 +0000 Subject: Include whitespace in "remove `|`" suggestion and make it hidden --- compiler/rustc_errors/src/diagnostic.rs | 2 +- compiler/rustc_parse/messages.ftl | 4 +- compiler/rustc_parse/src/errors.rs | 17 ++++- compiler/rustc_parse/src/parser/pat.rs | 18 +++-- .../issue-64879-trailing-before-guard.fixed | 18 +++++ .../issue-64879-trailing-before-guard.rs | 5 +- .../issue-64879-trailing-before-guard.stderr | 22 +++--- tests/ui/or-patterns/remove-leading-vert.fixed | 26 +++---- tests/ui/or-patterns/remove-leading-vert.rs | 2 +- tests/ui/or-patterns/remove-leading-vert.stderr | 85 +--------------------- ...ICE-130779-never-arm-no-oatherwise-block.stderr | 6 -- 11 files changed, 76 insertions(+), 129 deletions(-) create mode 100644 tests/ui/or-patterns/issue-64879-trailing-before-guard.fixed diff --git a/compiler/rustc_errors/src/diagnostic.rs b/compiler/rustc_errors/src/diagnostic.rs index 96c7ba6ed27..0034a30be08 100644 --- a/compiler/rustc_errors/src/diagnostic.rs +++ b/compiler/rustc_errors/src/diagnostic.rs @@ -1112,7 +1112,7 @@ impl<'a, G: EmissionGuarantee> Diag<'a, G> { .map(|snippet| { debug_assert!( !(sp.is_empty() && snippet.is_empty()), - "Span must not be empty and have no suggestion" + "Span `{sp:?}` must not be empty and have no suggestion" ); Substitution { parts: vec![SubstitutionPart { snippet, span: sp }] } }) diff --git a/compiler/rustc_parse/messages.ftl b/compiler/rustc_parse/messages.ftl index 859118a4ade..83c5ac449c2 100644 --- a/compiler/rustc_parse/messages.ftl +++ b/compiler/rustc_parse/messages.ftl @@ -851,8 +851,8 @@ parse_too_many_hashes = too many `#` symbols: raw strings may be delimited by up parse_too_short_hex_escape = numeric character escape is too short -parse_trailing_vert_not_allowed = a trailing `|` is not allowed in an or-pattern - .suggestion = remove the `{$token}` +parse_trailing_vert_not_allowed = a trailing `{$token}` is not allowed in an or-pattern +parse_trailing_vert_not_allowed_suggestion = remove the `{$token}` parse_trait_alias_cannot_be_auto = trait aliases cannot be `auto` parse_trait_alias_cannot_be_const = trait aliases cannot be `const` diff --git a/compiler/rustc_parse/src/errors.rs b/compiler/rustc_parse/src/errors.rs index 4aaaba01fae..871d4a53b5c 100644 --- a/compiler/rustc_parse/src/errors.rs +++ b/compiler/rustc_parse/src/errors.rs @@ -2620,7 +2620,7 @@ pub(crate) enum TopLevelOrPatternNotAllowedSugg { parse_sugg_remove_leading_vert_in_pattern, code = "", applicability = "machine-applicable", - style = "verbose" + style = "tool-only" )] RemoveLeadingVert { #[primary_span] @@ -2653,12 +2653,25 @@ pub(crate) struct UnexpectedVertVertInPattern { pub start: Option, } +#[derive(Subdiagnostic)] +#[suggestion( + parse_trailing_vert_not_allowed, + code = "", + applicability = "machine-applicable", + style = "tool-only" +)] +pub(crate) struct TrailingVertSuggestion { + #[primary_span] + pub span: Span, +} + #[derive(Diagnostic)] #[diag(parse_trailing_vert_not_allowed)] pub(crate) struct TrailingVertNotAllowed { #[primary_span] - #[suggestion(code = "", applicability = "machine-applicable", style = "verbose")] pub span: Span, + #[subdiagnostic] + pub suggestion: TrailingVertSuggestion, #[label(parse_label_while_parsing_or_pattern_here)] pub start: Option, pub token: Token, diff --git a/compiler/rustc_parse/src/parser/pat.rs b/compiler/rustc_parse/src/parser/pat.rs index 64653ee2a04..a5d16ee4231 100644 --- a/compiler/rustc_parse/src/parser/pat.rs +++ b/compiler/rustc_parse/src/parser/pat.rs @@ -25,10 +25,10 @@ use crate::errors::{ GenericArgsInPatRequireTurbofishSyntax, InclusiveRangeExtraEquals, InclusiveRangeMatchArrow, InclusiveRangeNoEnd, InvalidMutInPattern, ParenRangeSuggestion, PatternOnWrongSideOfAt, RemoveLet, RepeatedMutInPattern, SwitchRefBoxOrder, TopLevelOrPatternNotAllowed, - TopLevelOrPatternNotAllowedSugg, TrailingVertNotAllowed, UnexpectedExpressionInPattern, - UnexpectedExpressionInPatternSugg, UnexpectedLifetimeInPattern, UnexpectedParenInRangePat, - UnexpectedParenInRangePatSugg, UnexpectedVertVertBeforeFunctionParam, - UnexpectedVertVertInPattern, WrapInParens, + TopLevelOrPatternNotAllowedSugg, TrailingVertNotAllowed, TrailingVertSuggestion, + UnexpectedExpressionInPattern, UnexpectedExpressionInPatternSugg, UnexpectedLifetimeInPattern, + UnexpectedParenInRangePat, UnexpectedParenInRangePatSugg, + UnexpectedVertVertBeforeFunctionParam, UnexpectedVertVertInPattern, WrapInParens, }; use crate::parser::expr::{DestructuredFloat, could_be_unclosed_char_literal}; use crate::{exp, maybe_recover_from_interpolated_ty_qpath}; @@ -268,10 +268,9 @@ impl<'a> Parser<'a> { if let PatKind::Or(pats) = &pat.kind { let span = pat.span; - let sub = if pats.len() == 1 { - Some(TopLevelOrPatternNotAllowedSugg::RemoveLeadingVert { - span: span.with_hi(span.lo() + BytePos(1)), - }) + let sub = if let [_] = &pats[..] { + let span = span.with_hi(span.lo() + BytePos(1)); + Some(TopLevelOrPatternNotAllowedSugg::RemoveLeadingVert { span }) } else { Some(TopLevelOrPatternNotAllowedSugg::WrapInParens { span, @@ -363,6 +362,9 @@ impl<'a> Parser<'a> { self.dcx().emit_err(TrailingVertNotAllowed { span: self.token.span, start: lo, + suggestion: TrailingVertSuggestion { + span: self.prev_token.span.shrink_to_hi().with_hi(self.token.span.hi()), + }, token: self.token, note_double_vert: self.token.kind == token::OrOr, }); diff --git a/tests/ui/or-patterns/issue-64879-trailing-before-guard.fixed b/tests/ui/or-patterns/issue-64879-trailing-before-guard.fixed new file mode 100644 index 00000000000..0c65f709d66 --- /dev/null +++ b/tests/ui/or-patterns/issue-64879-trailing-before-guard.fixed @@ -0,0 +1,18 @@ +// In this regression test we check that a trailing `|` in an or-pattern just +// before the `if` token of a `match` guard will receive parser recovery with +// an appropriate error message. +//@ run-rustfix +#![allow(dead_code)] + +enum E { A, B } + +fn main() { + match E::A { + E::A | + E::B //~ ERROR a trailing `|` is not allowed in an or-pattern + if true => { + let _recovery_witness: i32 = 0i32; //~ ERROR mismatched types + } + _ => {} + } +} diff --git a/tests/ui/or-patterns/issue-64879-trailing-before-guard.rs b/tests/ui/or-patterns/issue-64879-trailing-before-guard.rs index 181c770096a..d7da564c2e1 100644 --- a/tests/ui/or-patterns/issue-64879-trailing-before-guard.rs +++ b/tests/ui/or-patterns/issue-64879-trailing-before-guard.rs @@ -1,6 +1,8 @@ // In this regression test we check that a trailing `|` in an or-pattern just // before the `if` token of a `match` guard will receive parser recovery with // an appropriate error message. +//@ run-rustfix +#![allow(dead_code)] enum E { A, B } @@ -9,7 +11,8 @@ fn main() { E::A | E::B | //~ ERROR a trailing `|` is not allowed in an or-pattern if true => { - let recovery_witness: bool = 0; //~ ERROR mismatched types + let _recovery_witness: i32 = 0u32; //~ ERROR mismatched types } + _ => {} } } diff --git a/tests/ui/or-patterns/issue-64879-trailing-before-guard.stderr b/tests/ui/or-patterns/issue-64879-trailing-before-guard.stderr index 91db3d049f6..238c76080dc 100644 --- a/tests/ui/or-patterns/issue-64879-trailing-before-guard.stderr +++ b/tests/ui/or-patterns/issue-64879-trailing-before-guard.stderr @@ -1,24 +1,24 @@ error: a trailing `|` is not allowed in an or-pattern - --> $DIR/issue-64879-trailing-before-guard.rs:10:14 + --> $DIR/issue-64879-trailing-before-guard.rs:12:14 | LL | E::A | | ---- while parsing this or-pattern starting here LL | E::B | | ^ + +error[E0308]: mismatched types + --> $DIR/issue-64879-trailing-before-guard.rs:14:42 | -help: remove the `|` +LL | let _recovery_witness: i32 = 0u32; + | --- ^^^^ expected `i32`, found `u32` + | | + | expected due to this | -LL - E::B | -LL + E::B +help: change the type of the numeric literal from `u32` to `i32` | - -error[E0308]: mismatched types - --> $DIR/issue-64879-trailing-before-guard.rs:12:42 +LL - let _recovery_witness: i32 = 0u32; +LL + let _recovery_witness: i32 = 0i32; | -LL | let recovery_witness: bool = 0; - | ---- ^ expected `bool`, found integer - | | - | expected due to this error: aborting due to 2 previous errors diff --git a/tests/ui/or-patterns/remove-leading-vert.fixed b/tests/ui/or-patterns/remove-leading-vert.fixed index 2851b8f18c5..aa7975dc508 100644 --- a/tests/ui/or-patterns/remove-leading-vert.fixed +++ b/tests/ui/or-patterns/remove-leading-vert.fixed @@ -23,26 +23,26 @@ fn leading() { #[cfg(false)] fn trailing() { - let ( A ): E; //~ ERROR a trailing `|` is not allowed in an or-pattern - let (a ,): (E,); //~ ERROR a trailing `|` is not allowed in an or-pattern - let ( A | B ): E; //~ ERROR a trailing `|` is not allowed in an or-pattern - let [ A | B ]: [E; 1]; //~ ERROR a trailing `|` is not allowed in an or-pattern - let S { f: B }; //~ ERROR a trailing `|` is not allowed in an or-pattern - let ( A | B ): E; //~ ERROR unexpected token `||` in pattern + let ( A ): E; //~ ERROR a trailing `|` is not allowed in an or-pattern + let (a,): (E,); //~ ERROR a trailing `|` is not allowed in an or-pattern + let ( A | B ): E; //~ ERROR a trailing `|` is not allowed in an or-pattern + let [ A | B ]: [E; 1]; //~ ERROR a trailing `|` is not allowed in an or-pattern + let S { f: B }; //~ ERROR a trailing `|` is not allowed in an or-pattern + let ( A | B ): E; //~ ERROR unexpected token `||` in pattern //~^ ERROR a trailing `|` is not allowed in an or-pattern match A { - A => {} //~ ERROR a trailing `|` is not allowed in an or-pattern - A => {} //~ ERROR a trailing `|` is not allowed in an or-pattern - A | B => {} //~ ERROR unexpected token `||` in pattern + A => {} //~ ERROR a trailing `|` is not allowed in an or-pattern + A => {} //~ ERROR a trailing `||` is not allowed in an or-pattern + A | B => {} //~ ERROR unexpected token `||` in pattern //~^ ERROR a trailing `|` is not allowed in an or-pattern - | A | B => {} + | A | B => {} //~^ ERROR a trailing `|` is not allowed in an or-pattern } // These test trailing-vert in `let` bindings, but they also test that we don't emit a // duplicate suggestion that would confuse rustfix. - let a : u8 = 0; //~ ERROR a trailing `|` is not allowed in an or-pattern - let a = 0; //~ ERROR a trailing `|` is not allowed in an or-pattern - let a ; //~ ERROR a trailing `|` is not allowed in an or-pattern + let a : u8 = 0; //~ ERROR a trailing `|` is not allowed in an or-pattern + let a = 0; //~ ERROR a trailing `|` is not allowed in an or-pattern + let a ; //~ ERROR a trailing `|` is not allowed in an or-pattern } diff --git a/tests/ui/or-patterns/remove-leading-vert.rs b/tests/ui/or-patterns/remove-leading-vert.rs index 1e1dbfbc6e6..1b4eb669fbb 100644 --- a/tests/ui/or-patterns/remove-leading-vert.rs +++ b/tests/ui/or-patterns/remove-leading-vert.rs @@ -32,7 +32,7 @@ fn trailing() { //~^ ERROR a trailing `|` is not allowed in an or-pattern match A { A | => {} //~ ERROR a trailing `|` is not allowed in an or-pattern - A || => {} //~ ERROR a trailing `|` is not allowed in an or-pattern + A || => {} //~ ERROR a trailing `||` is not allowed in an or-pattern A || B | => {} //~ ERROR unexpected token `||` in pattern //~^ ERROR a trailing `|` is not allowed in an or-pattern | A | B | => {} diff --git a/tests/ui/or-patterns/remove-leading-vert.stderr b/tests/ui/or-patterns/remove-leading-vert.stderr index 0323c64f042..29450153ba4 100644 --- a/tests/ui/or-patterns/remove-leading-vert.stderr +++ b/tests/ui/or-patterns/remove-leading-vert.stderr @@ -3,12 +3,6 @@ error: function parameters require top-level or-patterns in parentheses | LL | fn fun1( | A: E) {} | ^^^ - | -help: remove the `|` - | -LL - fn fun1( | A: E) {} -LL + fn fun1( A: E) {} - | error: unexpected `||` before function parameter --> $DIR/remove-leading-vert.rs:12:14 @@ -78,12 +72,6 @@ LL | let ( A | ): E; | - ^ | | | while parsing this or-pattern starting here - | -help: remove the `|` - | -LL - let ( A | ): E; -LL + let ( A ): E; - | error: a trailing `|` is not allowed in an or-pattern --> $DIR/remove-leading-vert.rs:27:12 @@ -92,12 +80,6 @@ LL | let (a |,): (E,); | - ^ | | | while parsing this or-pattern starting here - | -help: remove the `|` - | -LL - let (a |,): (E,); -LL + let (a ,): (E,); - | error: a trailing `|` is not allowed in an or-pattern --> $DIR/remove-leading-vert.rs:28:17 @@ -106,12 +88,6 @@ LL | let ( A | B | ): E; | - ^ | | | while parsing this or-pattern starting here - | -help: remove the `|` - | -LL - let ( A | B | ): E; -LL + let ( A | B ): E; - | error: a trailing `|` is not allowed in an or-pattern --> $DIR/remove-leading-vert.rs:29:17 @@ -120,12 +96,6 @@ LL | let [ A | B | ]: [E; 1]; | - ^ | | | while parsing this or-pattern starting here - | -help: remove the `|` - | -LL - let [ A | B | ]: [E; 1]; -LL + let [ A | B ]: [E; 1]; - | error: a trailing `|` is not allowed in an or-pattern --> $DIR/remove-leading-vert.rs:30:18 @@ -134,12 +104,6 @@ LL | let S { f: B | }; | - ^ | | | while parsing this or-pattern starting here - | -help: remove the `|` - | -LL - let S { f: B | }; -LL + let S { f: B }; - | error: unexpected token `||` in pattern --> $DIR/remove-leading-vert.rs:31:13 @@ -162,12 +126,6 @@ LL | let ( A || B | ): E; | - ^ | | | while parsing this or-pattern starting here - | -help: remove the `|` - | -LL - let ( A || B | ): E; -LL + let ( A || B ): E; - | error: a trailing `|` is not allowed in an or-pattern --> $DIR/remove-leading-vert.rs:34:11 @@ -176,14 +134,8 @@ LL | A | => {} | - ^ | | | while parsing this or-pattern starting here - | -help: remove the `|` - | -LL - A | => {} -LL + A => {} - | -error: a trailing `|` is not allowed in an or-pattern +error: a trailing `||` is not allowed in an or-pattern --> $DIR/remove-leading-vert.rs:35:11 | LL | A || => {} @@ -192,11 +144,6 @@ LL | A || => {} | while parsing this or-pattern starting here | = note: alternatives in or-patterns are separated with `|`, not `||` -help: remove the `||` - | -LL - A || => {} -LL + A => {} - | error: unexpected token `||` in pattern --> $DIR/remove-leading-vert.rs:36:11 @@ -219,12 +166,6 @@ LL | A || B | => {} | - ^ | | | while parsing this or-pattern starting here - | -help: remove the `|` - | -LL - A || B | => {} -LL + A || B => {} - | error: a trailing `|` is not allowed in an or-pattern --> $DIR/remove-leading-vert.rs:38:17 @@ -233,12 +174,6 @@ LL | | A | B | => {} | - ^ | | | while parsing this or-pattern starting here - | -help: remove the `|` - | -LL - | A | B | => {} -LL + | A | B => {} - | error: a trailing `|` is not allowed in an or-pattern --> $DIR/remove-leading-vert.rs:45:11 @@ -247,12 +182,6 @@ LL | let a | : u8 = 0; | - ^ | | | while parsing this or-pattern starting here - | -help: remove the `|` - | -LL - let a | : u8 = 0; -LL + let a : u8 = 0; - | error: a trailing `|` is not allowed in an or-pattern --> $DIR/remove-leading-vert.rs:46:11 @@ -261,12 +190,6 @@ LL | let a | = 0; | - ^ | | | while parsing this or-pattern starting here - | -help: remove the `|` - | -LL - let a | = 0; -LL + let a = 0; - | error: a trailing `|` is not allowed in an or-pattern --> $DIR/remove-leading-vert.rs:47:11 @@ -275,12 +198,6 @@ LL | let a | ; | - ^ | | | while parsing this or-pattern starting here - | -help: remove the `|` - | -LL - let a | ; -LL + let a ; - | error: aborting due to 21 previous errors diff --git a/tests/ui/rfcs/rfc-0000-never_patterns/ICE-130779-never-arm-no-oatherwise-block.stderr b/tests/ui/rfcs/rfc-0000-never_patterns/ICE-130779-never-arm-no-oatherwise-block.stderr index 26731e29ffc..5f4a5f31e34 100644 --- a/tests/ui/rfcs/rfc-0000-never_patterns/ICE-130779-never-arm-no-oatherwise-block.stderr +++ b/tests/ui/rfcs/rfc-0000-never_patterns/ICE-130779-never-arm-no-oatherwise-block.stderr @@ -5,12 +5,6 @@ LL | ! | | - ^ | | | while parsing this or-pattern starting here - | -help: remove the `|` - | -LL - ! | -LL + ! - | error: a never pattern is always unreachable --> $DIR/ICE-130779-never-arm-no-oatherwise-block.rs:10:20 -- cgit 1.4.1-3-g733a5 From 970ac40300bc75de9f5ef2edf77e93be5bd0769d Mon Sep 17 00:00:00 2001 From: Kivooeo Date: Sat, 2 Aug 2025 23:10:46 +0500 Subject: updated doc comment --- src/librustdoc/clean/types.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs index 89d5acb985b..cc3faa3d612 100644 --- a/src/librustdoc/clean/types.rs +++ b/src/librustdoc/clean/types.rs @@ -1534,10 +1534,10 @@ impl Type { matches!(self, Type::Path { path: Path { res: Res::Def(DefKind::TyAlias, _), .. } }) } - /// Check if two types are "the same" for documentation purposes. + /// Check if this type is a subtype of another type for documentation purposes. /// /// This is different from `Eq`, because it knows that things like - /// `Placeholder` are possible matches for everything. + /// `Infer` and generics have special subtyping rules. /// /// This relation is not commutative when generics are involved: /// @@ -1548,8 +1548,8 @@ impl Type { /// let cache = Cache::new(false); /// let generic = Type::Generic(rustc_span::symbol::sym::Any); /// let unit = Type::Primitive(PrimitiveType::Unit); - /// assert!(!generic.is_same(&unit, &cache)); - /// assert!(unit.is_same(&generic, &cache)); + /// assert!(!generic.is_doc_subtype_of(&unit, &cache)); + /// assert!(unit.is_doc_subtype_of(&generic, &cache)); /// ``` /// /// An owned type is also the same as its borrowed variants (this is commutative), -- cgit 1.4.1-3-g733a5 From d5f2260bd37966b76ef6ee9c22d0cdebdcbe97a9 Mon Sep 17 00:00:00 2001 From: Camille GILLOT Date: Mon, 4 Aug 2025 23:07:15 +0000 Subject: Add doc-comment. --- compiler/rustc_hir/src/def.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/compiler/rustc_hir/src/def.rs b/compiler/rustc_hir/src/def.rs index 8774fb4d585..f2567f73d99 100644 --- a/compiler/rustc_hir/src/def.rs +++ b/compiler/rustc_hir/src/def.rs @@ -318,6 +318,7 @@ impl DefKind { ) } + /// Whether the corresponding item has generic parameters, ie. the `generics_of` query works. pub fn has_generics(self) -> bool { match self { DefKind::AnonConst -- cgit 1.4.1-3-g733a5 From dccc075d05f0c28fab5775f7efa5550f24382ec6 Mon Sep 17 00:00:00 2001 From: Camille GILLOT Date: Mon, 4 Aug 2025 23:47:40 +0000 Subject: Check for rustc_has_incoherent_inherent_impls in incoherent_impls query. --- compiler/rustc_hir_typeck/src/method/probe.rs | 21 +++------------------ compiler/rustc_middle/src/ty/trait_def.rs | 7 +++++++ 2 files changed, 10 insertions(+), 18 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/method/probe.rs b/compiler/rustc_hir_typeck/src/method/probe.rs index f5584e0596a..51de661a2bb 100644 --- a/compiler/rustc_hir_typeck/src/method/probe.rs +++ b/compiler/rustc_hir_typeck/src/method/probe.rs @@ -777,31 +777,16 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { self.assemble_inherent_candidates_from_object(generalized_self_ty); self.assemble_inherent_impl_candidates_for_type(p.def_id(), receiver_steps); - if self.tcx.has_attr(p.def_id(), sym::rustc_has_incoherent_inherent_impls) { - self.assemble_inherent_candidates_for_incoherent_ty( - raw_self_ty, - receiver_steps, - ); - } + self.assemble_inherent_candidates_for_incoherent_ty(raw_self_ty, receiver_steps); } ty::Adt(def, _) => { let def_id = def.did(); self.assemble_inherent_impl_candidates_for_type(def_id, receiver_steps); - if self.tcx.has_attr(def_id, sym::rustc_has_incoherent_inherent_impls) { - self.assemble_inherent_candidates_for_incoherent_ty( - raw_self_ty, - receiver_steps, - ); - } + self.assemble_inherent_candidates_for_incoherent_ty(raw_self_ty, receiver_steps); } ty::Foreign(did) => { self.assemble_inherent_impl_candidates_for_type(did, receiver_steps); - if self.tcx.has_attr(did, sym::rustc_has_incoherent_inherent_impls) { - self.assemble_inherent_candidates_for_incoherent_ty( - raw_self_ty, - receiver_steps, - ); - } + self.assemble_inherent_candidates_for_incoherent_ty(raw_self_ty, receiver_steps); } ty::Param(_) => { self.assemble_inherent_candidates_from_param(raw_self_ty); diff --git a/compiler/rustc_middle/src/ty/trait_def.rs b/compiler/rustc_middle/src/ty/trait_def.rs index 59e2b2a034d..4e38d969192 100644 --- a/compiler/rustc_middle/src/ty/trait_def.rs +++ b/compiler/rustc_middle/src/ty/trait_def.rs @@ -6,6 +6,7 @@ use rustc_hir as hir; use rustc_hir::def::DefKind; use rustc_hir::def_id::{DefId, LOCAL_CRATE}; use rustc_macros::{Decodable, Encodable, HashStable}; +use rustc_span::symbol::sym; use tracing::debug; use crate::query::LocalCrate; @@ -239,6 +240,12 @@ pub(super) fn trait_impls_of_provider(tcx: TyCtxt<'_>, trait_id: DefId) -> Trait /// Query provider for `incoherent_impls`. pub(super) fn incoherent_impls_provider(tcx: TyCtxt<'_>, simp: SimplifiedType) -> &[DefId] { + if let Some(def_id) = simp.def() + && !tcx.has_attr(def_id, sym::rustc_has_incoherent_inherent_impls) + { + return &[]; + } + let mut impls = Vec::new(); for cnum in iter::once(LOCAL_CRATE).chain(tcx.crates(()).iter().copied()) { for &impl_def_id in tcx.crate_incoherent_impls((cnum, simp)) { -- cgit 1.4.1-3-g733a5 From 5073886181cb95bfe899a26404c57d2944f2a4d4 Mon Sep 17 00:00:00 2001 From: Camille GILLOT Date: Sat, 2 Aug 2025 18:21:16 +0000 Subject: Gate checking rustc attrs on the feature. --- compiler/rustc_hir_typeck/src/callee.rs | 6 ++++-- compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs | 16 ++++++++++++---- compiler/rustc_hir_typeck/src/upvar.rs | 2 +- compiler/rustc_hir_typeck/src/writeback.rs | 3 ++- 4 files changed, 19 insertions(+), 8 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/callee.rs b/compiler/rustc_hir_typeck/src/callee.rs index 48bb45de53e..e19dd83f81d 100644 --- a/compiler/rustc_hir_typeck/src/callee.rs +++ b/compiler/rustc_hir_typeck/src/callee.rs @@ -511,7 +511,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Untranslatable diagnostics are okay for rustc internals #[allow(rustc::untranslatable_diagnostic)] #[allow(rustc::diagnostic_outside_of_impl)] - if self.tcx.has_attr(def_id, sym::rustc_evaluate_where_clauses) { + if self.has_rustc_attrs + && self.tcx.has_attr(def_id, sym::rustc_evaluate_where_clauses) + { let predicates = self.tcx.predicates_of(def_id); let predicates = predicates.instantiate(self.tcx, args); for (predicate, predicate_span) in predicates { @@ -894,7 +896,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } // If we have `rustc_do_not_const_check`, do not check `[const]` bounds. - if self.tcx.has_attr(self.body_id, sym::rustc_do_not_const_check) { + if self.has_rustc_attrs && self.tcx.has_attr(self.body_id, sym::rustc_do_not_const_check) { return; } diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs index 0c6226ce71e..74f27e85cba 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs @@ -126,6 +126,10 @@ pub(crate) struct FnCtxt<'a, 'tcx> { /// These are stored here so we may collect them when canonicalizing user /// type ascriptions later. pub(super) trait_ascriptions: RefCell>>>, + + /// Whether the current crate enables the `rustc_attrs` feature. + /// This allows to skip processing attributes in many places. + pub(super) has_rustc_attrs: bool, } impl<'a, 'tcx> FnCtxt<'a, 'tcx> { @@ -154,6 +158,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { diverging_fallback_behavior, diverging_block_behavior, trait_ascriptions: Default::default(), + has_rustc_attrs: root_ctxt.tcx.features().rustc_attrs(), } } @@ -525,10 +530,13 @@ fn parse_never_type_options_attr( let mut fallback = None; let mut block = None; - let items = tcx - .get_attr(CRATE_DEF_ID, sym::rustc_never_type_options) - .map(|attr| attr.meta_item_list().unwrap()) - .unwrap_or_default(); + let items = if tcx.features().rustc_attrs() { + tcx.get_attr(CRATE_DEF_ID, sym::rustc_never_type_options) + .map(|attr| attr.meta_item_list().unwrap()) + } else { + None + }; + let items = items.unwrap_or_default(); for item in items { if item.has_name(sym::fallback) && fallback.is_none() { diff --git a/compiler/rustc_hir_typeck/src/upvar.rs b/compiler/rustc_hir_typeck/src/upvar.rs index df38c3a1214..8c0ec78e0af 100644 --- a/compiler/rustc_hir_typeck/src/upvar.rs +++ b/compiler/rustc_hir_typeck/src/upvar.rs @@ -1747,7 +1747,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } fn should_log_capture_analysis(&self, closure_def_id: LocalDefId) -> bool { - self.tcx.has_attr(closure_def_id, sym::rustc_capture_analysis) + self.has_rustc_attrs && self.tcx.has_attr(closure_def_id, sym::rustc_capture_analysis) } fn log_capture_analysis_first_pass( diff --git a/compiler/rustc_hir_typeck/src/writeback.rs b/compiler/rustc_hir_typeck/src/writeback.rs index 093de950d63..574406ecc8a 100644 --- a/compiler/rustc_hir_typeck/src/writeback.rs +++ b/compiler/rustc_hir_typeck/src/writeback.rs @@ -45,7 +45,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // This attribute causes us to dump some writeback information // in the form of errors, which is used for unit tests. - let rustc_dump_user_args = self.tcx.has_attr(item_def_id, sym::rustc_dump_user_args); + let rustc_dump_user_args = + self.has_rustc_attrs && self.tcx.has_attr(item_def_id, sym::rustc_dump_user_args); let mut wbcx = WritebackCx::new(self, body, rustc_dump_user_args); for param in body.params { -- cgit 1.4.1-3-g733a5 From 6b7f8fbcc584f4dc46081b5c690aeca7def88019 Mon Sep 17 00:00:00 2001 From: Camille GILLOT Date: Tue, 5 Aug 2025 01:55:20 +0000 Subject: Promote crash test. --- tests/crashes/124352.rs | 4 ---- tests/ui/attributes/malformed-never-type-options.rs | 8 ++++++++ .../ui/attributes/malformed-never-type-options.stderr | 19 +++++++++++++++++++ 3 files changed, 27 insertions(+), 4 deletions(-) delete mode 100644 tests/crashes/124352.rs create mode 100644 tests/ui/attributes/malformed-never-type-options.rs create mode 100644 tests/ui/attributes/malformed-never-type-options.stderr diff --git a/tests/crashes/124352.rs b/tests/crashes/124352.rs deleted file mode 100644 index e9eb4419e6a..00000000000 --- a/tests/crashes/124352.rs +++ /dev/null @@ -1,4 +0,0 @@ -//@ known-bug: #124352 -#![rustc_never_type_options(: Unsize = "hi")] - -fn main() {} diff --git a/tests/ui/attributes/malformed-never-type-options.rs b/tests/ui/attributes/malformed-never-type-options.rs new file mode 100644 index 00000000000..0c384be0e22 --- /dev/null +++ b/tests/ui/attributes/malformed-never-type-options.rs @@ -0,0 +1,8 @@ +//! Regression test for #124352 +//! The `rustc_*` attribute is malformed, but ICEing without a `feature(rustc_attrs)` is still bad. + +#![rustc_never_type_options(: Unsize = "hi")] +//~^ ERROR expected unsuffixed literal, found `:` +//~| ERROR use of an internal attribute + +fn main() {} diff --git a/tests/ui/attributes/malformed-never-type-options.stderr b/tests/ui/attributes/malformed-never-type-options.stderr new file mode 100644 index 00000000000..0d2ff4881f2 --- /dev/null +++ b/tests/ui/attributes/malformed-never-type-options.stderr @@ -0,0 +1,19 @@ +error: expected unsuffixed literal, found `:` + --> $DIR/malformed-never-type-options.rs:4:29 + | +LL | #![rustc_never_type_options(: Unsize = "hi")] + | ^ + +error[E0658]: use of an internal attribute + --> $DIR/malformed-never-type-options.rs:4:1 + | +LL | #![rustc_never_type_options(: Unsize = "hi")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: add `#![feature(rustc_attrs)]` to the crate attributes to enable + = note: the `#[rustc_never_type_options]` attribute is an internal implementation detail that will never be stable + = note: `rustc_never_type_options` is used to experiment with never type fallback and work on never type stabilization + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0658`. -- cgit 1.4.1-3-g733a5 From 4e81ecaf3a45a4bb5a8a78ac407d1ef6a7367e72 Mon Sep 17 00:00:00 2001 From: Scott McMurray Date: Sun, 27 Jul 2025 21:39:56 -0700 Subject: Let `RemoveUnneededDrops` also remove `drop_in_place` --- .../src/remove_unneeded_drops.rs | 31 +++++++++++++++++----- tests/mir-opt/remove_unneeded_drop_in_place.rs | 6 ++++- ...n_place.slice_in_place.RemoveUnneededDrops.diff | 8 +++--- 3 files changed, 33 insertions(+), 12 deletions(-) diff --git a/compiler/rustc_mir_transform/src/remove_unneeded_drops.rs b/compiler/rustc_mir_transform/src/remove_unneeded_drops.rs index 43f80508e4a..6c2dfc59da2 100644 --- a/compiler/rustc_mir_transform/src/remove_unneeded_drops.rs +++ b/compiler/rustc_mir_transform/src/remove_unneeded_drops.rs @@ -4,7 +4,13 @@ //! useful because (unlike MIR building) it runs after type checking, so it can make use of //! `TypingMode::PostAnalysis` to provide more precise type information, especially about opaque //! types. +//! +//! When we're optimizing, we also remove calls to `drop_in_place` when `T` isn't `needs_drop`, +//! as those are essentially equivalent to `Drop` terminators. While the compiler doesn't insert +//! them automatically, preferring the built-in instead, they're common in generic code (such as +//! `Vec::truncate`) so removing them from things like inlined `Vec` is helpful. +use rustc_hir::LangItem; use rustc_middle::mir::*; use rustc_middle::ty::TyCtxt; use tracing::{debug, trace}; @@ -21,15 +27,26 @@ impl<'tcx> crate::MirPass<'tcx> for RemoveUnneededDrops { let mut should_simplify = false; for block in body.basic_blocks.as_mut() { let terminator = block.terminator_mut(); - if let TerminatorKind::Drop { place, target, .. } = terminator.kind { - let ty = place.ty(&body.local_decls, tcx); - if ty.ty.needs_drop(tcx, typing_env) { - continue; + let (ty, target) = match terminator.kind { + TerminatorKind::Drop { place, target, .. } => { + (place.ty(&body.local_decls, tcx).ty, target) + } + TerminatorKind::Call { ref func, target: Some(target), .. } + if tcx.sess.mir_opt_level() > 0 + && let Some((def_id, generics)) = func.const_fn_def() + && tcx.is_lang_item(def_id, LangItem::DropInPlace) => + { + (generics.type_at(0), target) } - debug!("SUCCESS: replacing `drop` with goto({:?})", target); - terminator.kind = TerminatorKind::Goto { target }; - should_simplify = true; + _ => continue, + }; + + if ty.needs_drop(tcx, typing_env) { + continue; } + debug!("SUCCESS: replacing `drop` with goto({:?})", target); + terminator.kind = TerminatorKind::Goto { target }; + should_simplify = true; } // if we applied optimizations, we potentially have some cfg to cleanup to diff --git a/tests/mir-opt/remove_unneeded_drop_in_place.rs b/tests/mir-opt/remove_unneeded_drop_in_place.rs index 7fb7b1e78de..470c6499d16 100644 --- a/tests/mir-opt/remove_unneeded_drop_in_place.rs +++ b/tests/mir-opt/remove_unneeded_drop_in_place.rs @@ -1,13 +1,17 @@ //@ test-mir-pass: RemoveUnneededDrops //@ needs-unwind +//@ compile-flags: -Z mir-opt-level=1 // EMIT_MIR remove_unneeded_drop_in_place.slice_in_place.RemoveUnneededDrops.diff unsafe fn slice_in_place(ptr: *mut [char]) { + // CHECK-LABEL: fn slice_in_place(_1: *mut [char]) + // CHECK: bb0: { + // CHECK-NEXT: return; + // CHECK-NEXT: } std::ptr::drop_in_place(ptr) } fn main() { - // CHECK-LABEL: fn main( let mut a = ['o', 'k']; unsafe { slice_in_place(&raw mut a) }; } diff --git a/tests/mir-opt/remove_unneeded_drop_in_place.slice_in_place.RemoveUnneededDrops.diff b/tests/mir-opt/remove_unneeded_drop_in_place.slice_in_place.RemoveUnneededDrops.diff index 4068e7940bc..4d70e7151c3 100644 --- a/tests/mir-opt/remove_unneeded_drop_in_place.slice_in_place.RemoveUnneededDrops.diff +++ b/tests/mir-opt/remove_unneeded_drop_in_place.slice_in_place.RemoveUnneededDrops.diff @@ -9,10 +9,10 @@ bb0: { StorageLive(_2); _2 = copy _1; - _0 = drop_in_place::<[char]>(move _2) -> [return: bb1, unwind continue]; - } - - bb1: { +- _0 = drop_in_place::<[char]>(move _2) -> [return: bb1, unwind continue]; +- } +- +- bb1: { StorageDead(_2); return; } -- cgit 1.4.1-3-g733a5 From bdaabc17b664952ce0b9666b9b57188a6126935b Mon Sep 17 00:00:00 2001 From: Sasha Pourcelot Date: Mon, 4 Aug 2025 20:47:43 +0200 Subject: Add test for "missing function argument" on multiline call --- tests/ui/fn/fn-arg-count-mismatch-diagnostics.rs | 12 ++++++++ .../ui/fn/fn-arg-count-mismatch-diagnostics.stderr | 32 ++++++++++++++++++++-- 2 files changed, 41 insertions(+), 3 deletions(-) diff --git a/tests/ui/fn/fn-arg-count-mismatch-diagnostics.rs b/tests/ui/fn/fn-arg-count-mismatch-diagnostics.rs index b2f80ba1bf6..3b12ea1a736 100644 --- a/tests/ui/fn/fn-arg-count-mismatch-diagnostics.rs +++ b/tests/ui/fn/fn-arg-count-mismatch-diagnostics.rs @@ -46,9 +46,21 @@ impl Bar { } } +fn function_with_lots_of_arguments(a: i32, b: char, c: i32, d: i32, e: i32, f: i32) {} + fn main() { foo(1, 2, 3); //~^ ERROR function takes 4 arguments but 3 bar(1, 2, 3); //~^ ERROR function takes 6 arguments but 3 + + let variable_name = 42; + function_with_lots_of_arguments( + variable_name, + variable_name, + variable_name, + variable_name, + variable_name, + ); + //~^^^^^^^ ERROR this function takes 6 arguments but 5 arguments were supplied [E0061] } diff --git a/tests/ui/fn/fn-arg-count-mismatch-diagnostics.stderr b/tests/ui/fn/fn-arg-count-mismatch-diagnostics.stderr index 6af7671af03..494d9bf60a6 100644 --- a/tests/ui/fn/fn-arg-count-mismatch-diagnostics.stderr +++ b/tests/ui/fn/fn-arg-count-mismatch-diagnostics.stderr @@ -52,7 +52,7 @@ LL | <$from>::$method(8, /* u8 */) | ++++++++++ error[E0061]: this function takes 4 arguments but 3 arguments were supplied - --> $DIR/fn-arg-count-mismatch-diagnostics.rs:50:5 + --> $DIR/fn-arg-count-mismatch-diagnostics.rs:52:5 | LL | foo(1, 2, 3); | ^^^--------- argument #4 of type `isize` is missing @@ -68,7 +68,7 @@ LL | foo(1, 2, 3, /* isize */); | +++++++++++++ error[E0061]: this function takes 6 arguments but 3 arguments were supplied - --> $DIR/fn-arg-count-mismatch-diagnostics.rs:52:5 + --> $DIR/fn-arg-count-mismatch-diagnostics.rs:54:5 | LL | bar(1, 2, 3); | ^^^--------- three arguments of type `i32`, `i32`, and `i32` are missing @@ -83,6 +83,32 @@ help: provide the arguments LL | bar(1, 2, 3, /* i32 */, /* i32 */, /* i32 */); | +++++++++++++++++++++++++++++++++ -error: aborting due to 5 previous errors +error[E0061]: this function takes 6 arguments but 5 arguments were supplied + --> $DIR/fn-arg-count-mismatch-diagnostics.rs:58:5 + | +LL | function_with_lots_of_arguments( + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | variable_name, +LL | variable_name, + | ------------- argument #2 of type `char` is missing + | +note: function defined here + --> $DIR/fn-arg-count-mismatch-diagnostics.rs:49:4 + | +LL | fn function_with_lots_of_arguments(a: i32, b: char, c: i32, d: i32, e: i32, f: i32) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ------- +help: provide the argument + | +LL - function_with_lots_of_arguments( +LL - variable_name, +LL - variable_name, +LL - variable_name, +LL - variable_name, +LL - variable_name, +LL - ); +LL + function_with_lots_of_arguments(variable_name, /* char */, variable_name, variable_name, variable_name, variable_name); + | + +error: aborting due to 6 previous errors For more information about this error, try `rustc --explain E0061`. -- cgit 1.4.1-3-g733a5 From 2136c500cdbdd08fe7c25927b5d3ce790d75f68b Mon Sep 17 00:00:00 2001 From: Sebastian Poeplau Date: Fri, 1 Aug 2025 12:39:50 +0200 Subject: Preserve the .debug_gdb_scripts section Make sure that compiler and linker don't optimize the section's contents away by adding the global holding the data to "llvm.used". The volatile load in the main shim is retained because "llvm.used", which translates to SHF_GNU_RETAIN on ELF targets, requires a reasonably recent linker; emitting the volatile load ensures compatibility with older linkers, at least when libstd is used. Pretty printers in dylib dependencies are now emitted by the main crate instead of the dylib; apart from matching how rlibs are handled, this approach has the advantage that `omit_gdb_pretty_printer_section` keeps working with dylib dependencies. --- src/debuginfo.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/debuginfo.rs b/src/debuginfo.rs index 4c8585192a1..4c0b6439523 100644 --- a/src/debuginfo.rs +++ b/src/debuginfo.rs @@ -254,7 +254,8 @@ impl<'gcc, 'tcx> DebugInfoCodegenMethods<'tcx> for CodegenCx<'gcc, 'tcx> { // TODO(antoyo): implement. } - fn debuginfo_finalize(&self) { + fn debuginfo_finalize(&mut self) { + // TODO: emit section `.debug_gdb_scripts`. self.context.set_debug_info(true) } -- cgit 1.4.1-3-g733a5 From 566cdab16d74f29837b0978f25159c9ee7c51440 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Fri, 1 Aug 2025 21:04:53 +1000 Subject: Clarify `value_path_str_with_args`. The use of `print_value_path` means the value namespace is always used and the `guess_def_namespace` call is unnecessary. This commit removes the `guess_def_namespace` call and hard-codes `ValueNS`. It also changes the `print_value_path` to `print_def_path` for consistency with `def_path_str_with_args`. --- compiler/rustc_middle/src/ty/print/pretty.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/compiler/rustc_middle/src/ty/print/pretty.rs b/compiler/rustc_middle/src/ty/print/pretty.rs index b381d62be47..c150aa65b9b 100644 --- a/compiler/rustc_middle/src/ty/print/pretty.rs +++ b/compiler/rustc_middle/src/ty/print/pretty.rs @@ -2131,8 +2131,6 @@ impl<'a, 'tcx> FmtPrinter<'a, 'tcx> { } } -// HACK(eddyb) get rid of `def_path_str` and/or pass `Namespace` explicitly always -// (but also some things just print a `DefId` generally so maybe we need this?) fn guess_def_namespace(tcx: TyCtxt<'_>, def_id: DefId) -> Namespace { match tcx.def_key(def_id).disambiguated_data.data { DefPathData::TypeNs(..) | DefPathData::CrateRoot | DefPathData::OpaqueTy => { @@ -2157,6 +2155,7 @@ impl<'t> TyCtxt<'t> { self.def_path_str_with_args(def_id, &[]) } + /// For this one we determine the appropriate namespace for the `def_id`. pub fn def_path_str_with_args( self, def_id: impl IntoQueryParam, @@ -2169,16 +2168,17 @@ impl<'t> TyCtxt<'t> { FmtPrinter::print_string(self, ns, |p| p.print_def_path(def_id, args)).unwrap() } + /// For this one we always use value namespace. pub fn value_path_str_with_args( self, def_id: impl IntoQueryParam, args: &'t [GenericArg<'t>], ) -> String { let def_id = def_id.into_query_param(); - let ns = guess_def_namespace(self, def_id); + let ns = Namespace::ValueNS; debug!("value_path_str: def_id={:?}, ns={:?}", def_id, ns); - FmtPrinter::print_string(self, ns, |p| p.print_value_path(def_id, args)).unwrap() + FmtPrinter::print_string(self, ns, |p| p.print_def_path(def_id, args)).unwrap() } } -- cgit 1.4.1-3-g733a5 From b0c36dd4c7519d295239b69127ff84f51f14acc3 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Fri, 1 Aug 2025 23:58:46 +1000 Subject: Rename most of the printers. Three of them are named `AbsolutePathPrinter`, which is confusing, so give those names that better indicate how they are used. And then there is `SymbolPrinter` and `SymbolMangler`, which are renamed as `LegacySymbolMangler` and `V0SymbolMangler`, better indicating their similarity. --- compiler/rustc_const_eval/src/util/type_name.rs | 10 +++++----- compiler/rustc_lint/src/context.rs | 6 +++--- compiler/rustc_symbol_mangling/src/legacy.rs | 10 +++++----- compiler/rustc_symbol_mangling/src/v0.rs | 12 ++++++------ .../rustc_trait_selection/src/error_reporting/infer/mod.rs | 6 +++--- 5 files changed, 22 insertions(+), 22 deletions(-) diff --git a/compiler/rustc_const_eval/src/util/type_name.rs b/compiler/rustc_const_eval/src/util/type_name.rs index e6b9759819f..573c734e931 100644 --- a/compiler/rustc_const_eval/src/util/type_name.rs +++ b/compiler/rustc_const_eval/src/util/type_name.rs @@ -7,12 +7,12 @@ use rustc_middle::bug; use rustc_middle::ty::print::{PrettyPrinter, PrintError, Printer}; use rustc_middle::ty::{self, GenericArg, GenericArgKind, Ty, TyCtxt}; -struct AbsolutePathPrinter<'tcx> { +struct TypeNamePrinter<'tcx> { tcx: TyCtxt<'tcx>, path: String, } -impl<'tcx> Printer<'tcx> for AbsolutePathPrinter<'tcx> { +impl<'tcx> Printer<'tcx> for TypeNamePrinter<'tcx> { fn tcx(&self) -> TyCtxt<'tcx> { self.tcx } @@ -133,7 +133,7 @@ impl<'tcx> Printer<'tcx> for AbsolutePathPrinter<'tcx> { } } -impl<'tcx> PrettyPrinter<'tcx> for AbsolutePathPrinter<'tcx> { +impl<'tcx> PrettyPrinter<'tcx> for TypeNamePrinter<'tcx> { fn should_print_region(&self, _region: ty::Region<'_>) -> bool { false } @@ -157,7 +157,7 @@ impl<'tcx> PrettyPrinter<'tcx> for AbsolutePathPrinter<'tcx> { } } -impl Write for AbsolutePathPrinter<'_> { +impl Write for TypeNamePrinter<'_> { fn write_str(&mut self, s: &str) -> std::fmt::Result { self.path.push_str(s); Ok(()) @@ -165,7 +165,7 @@ impl Write for AbsolutePathPrinter<'_> { } pub fn type_name<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> String { - let mut p = AbsolutePathPrinter { tcx, path: String::new() }; + let mut p = TypeNamePrinter { tcx, path: String::new() }; p.print_type(ty).unwrap(); p.path } diff --git a/compiler/rustc_lint/src/context.rs b/compiler/rustc_lint/src/context.rs index 11181d10af5..0dd50af2efc 100644 --- a/compiler/rustc_lint/src/context.rs +++ b/compiler/rustc_lint/src/context.rs @@ -745,12 +745,12 @@ impl<'tcx> LateContext<'tcx> { /// } /// ``` pub fn get_def_path(&self, def_id: DefId) -> Vec { - struct AbsolutePathPrinter<'tcx> { + struct LintPathPrinter<'tcx> { tcx: TyCtxt<'tcx>, path: Vec, } - impl<'tcx> Printer<'tcx> for AbsolutePathPrinter<'tcx> { + impl<'tcx> Printer<'tcx> for LintPathPrinter<'tcx> { fn tcx(&self) -> TyCtxt<'tcx> { self.tcx } @@ -853,7 +853,7 @@ impl<'tcx> LateContext<'tcx> { } } - let mut p = AbsolutePathPrinter { tcx: self.tcx, path: vec![] }; + let mut p = LintPathPrinter { tcx: self.tcx, path: vec![] }; p.print_def_path(def_id, &[]).unwrap(); p.path } diff --git a/compiler/rustc_symbol_mangling/src/legacy.rs b/compiler/rustc_symbol_mangling/src/legacy.rs index aa8292c0504..f8bab0f5d0c 100644 --- a/compiler/rustc_symbol_mangling/src/legacy.rs +++ b/compiler/rustc_symbol_mangling/src/legacy.rs @@ -58,7 +58,7 @@ pub(super) fn mangle<'tcx>( let hash = get_symbol_hash(tcx, instance, instance_ty, instantiating_crate); - let mut p = SymbolPrinter { tcx, path: SymbolPath::new(), keep_within_component: false }; + let mut p = LegacySymbolMangler { tcx, path: SymbolPath::new(), keep_within_component: false }; p.print_def_path( def_id, if let ty::InstanceKind::DropGlue(_, _) @@ -213,7 +213,7 @@ impl SymbolPath { } } -struct SymbolPrinter<'tcx> { +struct LegacySymbolMangler<'tcx> { tcx: TyCtxt<'tcx>, path: SymbolPath, @@ -228,7 +228,7 @@ struct SymbolPrinter<'tcx> { // `PrettyPrinter` aka pretty printing of e.g. types in paths, // symbol names should have their own printing machinery. -impl<'tcx> Printer<'tcx> for SymbolPrinter<'tcx> { +impl<'tcx> Printer<'tcx> for LegacySymbolMangler<'tcx> { fn tcx(&self) -> TyCtxt<'tcx> { self.tcx } @@ -453,7 +453,7 @@ impl<'tcx> Printer<'tcx> for SymbolPrinter<'tcx> { } } -impl<'tcx> PrettyPrinter<'tcx> for SymbolPrinter<'tcx> { +impl<'tcx> PrettyPrinter<'tcx> for LegacySymbolMangler<'tcx> { fn should_print_region(&self, _region: ty::Region<'_>) -> bool { false } @@ -489,7 +489,7 @@ impl<'tcx> PrettyPrinter<'tcx> for SymbolPrinter<'tcx> { } } -impl fmt::Write for SymbolPrinter<'_> { +impl fmt::Write for LegacySymbolMangler<'_> { fn write_str(&mut self, s: &str) -> fmt::Result { // Name sanitation. LLVM will happily accept identifiers with weird names, but // gas doesn't! diff --git a/compiler/rustc_symbol_mangling/src/v0.rs b/compiler/rustc_symbol_mangling/src/v0.rs index c2458ae814b..2aad5f315f3 100644 --- a/compiler/rustc_symbol_mangling/src/v0.rs +++ b/compiler/rustc_symbol_mangling/src/v0.rs @@ -33,7 +33,7 @@ pub(super) fn mangle<'tcx>( let args = tcx.normalize_erasing_regions(ty::TypingEnv::fully_monomorphized(), instance.args); let prefix = "_R"; - let mut p: SymbolMangler<'_> = SymbolMangler { + let mut p: V0SymbolMangler<'_> = V0SymbolMangler { tcx, start_offset: prefix.len(), is_exportable, @@ -88,7 +88,7 @@ pub fn mangle_internal_symbol<'tcx>(tcx: TyCtxt<'tcx>, item_name: &str) -> Strin } let prefix = "_R"; - let mut p: SymbolMangler<'_> = SymbolMangler { + let mut p: V0SymbolMangler<'_> = V0SymbolMangler { tcx, start_offset: prefix.len(), is_exportable: false, @@ -131,7 +131,7 @@ pub(super) fn mangle_typeid_for_trait_ref<'tcx>( trait_ref: ty::ExistentialTraitRef<'tcx>, ) -> String { // FIXME(flip1995): See comment in `mangle_typeid_for_fnabi`. - let mut p = SymbolMangler { + let mut p = V0SymbolMangler { tcx, start_offset: 0, is_exportable: false, @@ -159,7 +159,7 @@ struct BinderLevel { lifetime_depths: Range, } -struct SymbolMangler<'tcx> { +struct V0SymbolMangler<'tcx> { tcx: TyCtxt<'tcx>, binders: Vec, out: String, @@ -173,7 +173,7 @@ struct SymbolMangler<'tcx> { consts: FxHashMap, usize>, } -impl<'tcx> SymbolMangler<'tcx> { +impl<'tcx> V0SymbolMangler<'tcx> { fn push(&mut self, s: &str) { self.out.push_str(s); } @@ -272,7 +272,7 @@ impl<'tcx> SymbolMangler<'tcx> { } } -impl<'tcx> Printer<'tcx> for SymbolMangler<'tcx> { +impl<'tcx> Printer<'tcx> for V0SymbolMangler<'tcx> { fn tcx(&self) -> TyCtxt<'tcx> { self.tcx } diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs index ed8229154a9..0aefb4580f8 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs @@ -224,12 +224,12 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { use ty::GenericArg; use ty::print::Printer; - struct AbsolutePathPrinter<'tcx> { + struct ConflictingPathPrinter<'tcx> { tcx: TyCtxt<'tcx>, segments: Vec, } - impl<'tcx> Printer<'tcx> for AbsolutePathPrinter<'tcx> { + impl<'tcx> Printer<'tcx> for ConflictingPathPrinter<'tcx> { fn tcx<'a>(&'a self) -> TyCtxt<'tcx> { self.tcx } @@ -300,7 +300,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { // let _ = [{struct Foo; Foo}, {struct Foo; Foo}]; if did1.krate != did2.krate { let abs_path = |def_id| { - let mut p = AbsolutePathPrinter { tcx: self.tcx, segments: vec![] }; + let mut p = ConflictingPathPrinter { tcx: self.tcx, segments: vec![] }; p.print_def_path(def_id, &[]).map(|_| p.segments) }; -- cgit 1.4.1-3-g733a5 From d76eef481e2deaf3635da967e8afc5ac63c4c13e Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Sat, 2 Aug 2025 00:03:28 +1000 Subject: Simplify multiple things. - `same_path` can just be a `bool`. - `expected` and `found` are only needed inside the block. - Neaten a comment. --- .../src/error_reporting/infer/mod.rs | 27 +++++++++++----------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs index 0aefb4580f8..fd204412564 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs @@ -304,24 +304,23 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { p.print_def_path(def_id, &[]).map(|_| p.segments) }; - // We compare strings because DefPath can be different - // for imported and non-imported crates + // We compare strings because DefPath can be different for imported and + // non-imported crates. let expected_str = self.tcx.def_path_str(did1); let found_str = self.tcx.def_path_str(did2); let Ok(expected_abs) = abs_path(did1) else { return false }; let Ok(found_abs) = abs_path(did2) else { return false }; - let same_path = || -> Result<_, PrintError> { - Ok(expected_str == found_str || expected_abs == found_abs) - }; - // We want to use as unique a type path as possible. If both types are "locally - // known" by the same name, we use the "absolute path" which uses the original - // crate name instead. - let (expected, found) = if expected_str == found_str { - (join_path_syms(&expected_abs), join_path_syms(&found_abs)) - } else { - (expected_str.clone(), found_str.clone()) - }; - if same_path().unwrap_or(false) { + let same_path = expected_str == found_str || expected_abs == found_abs; + if same_path { + // We want to use as unique a type path as possible. If both types are "locally + // known" by the same name, we use the "absolute path" which uses the original + // crate name instead. + let (expected, found) = if expected_str == found_str { + (join_path_syms(&expected_abs), join_path_syms(&found_abs)) + } else { + (expected_str.clone(), found_str.clone()) + }; + // We've displayed "expected `a::b`, found `a::b`". We add context to // differentiate the different cases where that might happen. let expected_crate_name = self.tcx.crate_name(did1.krate); -- cgit 1.4.1-3-g733a5 From 3a993a611c9f32e34e341794ce19026ad3df2c0f Mon Sep 17 00:00:00 2001 From: Raoul Strackx Date: Tue, 5 Aug 2025 14:51:02 +0200 Subject: Ignore sleep_until test on SGX --- library/std/tests/thread.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/library/std/tests/thread.rs b/library/std/tests/thread.rs index 32561dd6ab6..29f220d8a70 100644 --- a/library/std/tests/thread.rs +++ b/library/std/tests/thread.rs @@ -19,6 +19,7 @@ fn sleep_very_long() { } #[test] +#[cfg_attr(target_env = "sgx", ignore = "Time within SGX enclave cannot be trusted")] fn sleep_until() { let now = Instant::now(); let period = Duration::from_millis(100); -- cgit 1.4.1-3-g733a5 From f7e46f9d603010be83b3302500c65130a5186782 Mon Sep 17 00:00:00 2001 From: Marco Cavenati Date: Tue, 5 Aug 2025 15:35:59 +0200 Subject: fix(unicode-table-generator): fix duplicated unique indices unicode-table-generator panicked while populating distinct_indices because of duplicated indices. This was introduced by swapping the order of canonical_words.push(...) and canonical_words.len(). --- src/tools/unicode-table-generator/src/raw_emitter.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/unicode-table-generator/src/raw_emitter.rs b/src/tools/unicode-table-generator/src/raw_emitter.rs index e9e0efc4594..03ed9499e26 100644 --- a/src/tools/unicode-table-generator/src/raw_emitter.rs +++ b/src/tools/unicode-table-generator/src/raw_emitter.rs @@ -341,7 +341,7 @@ impl Canonicalized { for &w in unique_words { unique_mapping.entry(w).or_insert_with(|| { canonical_words.push(w); - UniqueMapping::Canonical(canonical_words.len()) + UniqueMapping::Canonical(canonical_words.len() - 1) }); } assert_eq!(canonicalized_words.len() + canonical_words.len(), unique_words.len()); -- cgit 1.4.1-3-g733a5 From 7a113811fab70d9d1932ad32ea392b9f100186ef Mon Sep 17 00:00:00 2001 From: Zihan Date: Mon, 4 Aug 2025 21:44:16 -0400 Subject: fix &str type check in `from_str_radix_10` changelog: none Signed-off-by: Zihan --- clippy_lints/src/from_str_radix_10.rs | 4 ++-- tests/ui/from_str_radix_10.fixed | 10 ++++++++++ tests/ui/from_str_radix_10.rs | 10 ++++++++++ tests/ui/from_str_radix_10.stderr | 14 +++++++++++++- 4 files changed, 35 insertions(+), 3 deletions(-) diff --git a/clippy_lints/src/from_str_radix_10.rs b/clippy_lints/src/from_str_radix_10.rs index b816963cc82..d5873b3f85a 100644 --- a/clippy_lints/src/from_str_radix_10.rs +++ b/clippy_lints/src/from_str_radix_10.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::sugg::Sugg; -use clippy_utils::ty::{is_type_diagnostic_item, is_type_lang_item}; +use clippy_utils::ty::is_type_lang_item; use clippy_utils::{is_in_const_context, is_integer_literal, sym}; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, LangItem, PrimTy, QPath, TyKind, def}; @@ -89,5 +89,5 @@ impl<'tcx> LateLintPass<'tcx> for FromStrRadix10 { /// Checks if a Ty is `String` or `&str` fn is_ty_stringish(cx: &LateContext<'_>, ty: Ty<'_>) -> bool { - is_type_lang_item(cx, ty, LangItem::String) || is_type_diagnostic_item(cx, ty, sym::str) + is_type_lang_item(cx, ty, LangItem::String) || ty.peel_refs().is_str() } diff --git a/tests/ui/from_str_radix_10.fixed b/tests/ui/from_str_radix_10.fixed index 4b8fd778685..47d24167e56 100644 --- a/tests/ui/from_str_radix_10.fixed +++ b/tests/ui/from_str_radix_10.fixed @@ -74,3 +74,13 @@ fn issue_12731() { let _ = u32::from_str_radix("123", 10); } } + +fn fix_str_ref_check() { + #![allow(clippy::needless_borrow)] + let s = "1"; + let _ = s.parse::().unwrap(); + //~^ from_str_radix_10 + let s_ref = &s; + let _ = s_ref.parse::().unwrap(); + //~^ from_str_radix_10 +} diff --git a/tests/ui/from_str_radix_10.rs b/tests/ui/from_str_radix_10.rs index 89002b11a99..952e19b57a0 100644 --- a/tests/ui/from_str_radix_10.rs +++ b/tests/ui/from_str_radix_10.rs @@ -74,3 +74,13 @@ fn issue_12731() { let _ = u32::from_str_radix("123", 10); } } + +fn fix_str_ref_check() { + #![allow(clippy::needless_borrow)] + let s = "1"; + let _ = u32::from_str_radix(&s, 10).unwrap(); + //~^ from_str_radix_10 + let s_ref = &s; + let _ = u32::from_str_radix(&s_ref, 10).unwrap(); + //~^ from_str_radix_10 +} diff --git a/tests/ui/from_str_radix_10.stderr b/tests/ui/from_str_radix_10.stderr index c693e8f50ff..d4e6c3657f2 100644 --- a/tests/ui/from_str_radix_10.stderr +++ b/tests/ui/from_str_radix_10.stderr @@ -49,5 +49,17 @@ error: this call to `from_str_radix` can be replaced with a call to `str::parse` LL | i32::from_str_radix(&stringier, 10)?; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `stringier.parse::()` -error: aborting due to 8 previous errors +error: this call to `from_str_radix` can be replaced with a call to `str::parse` + --> tests/ui/from_str_radix_10.rs:81:13 + | +LL | let _ = u32::from_str_radix(&s, 10).unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `s.parse::()` + +error: this call to `from_str_radix` can be replaced with a call to `str::parse` + --> tests/ui/from_str_radix_10.rs:84:13 + | +LL | let _ = u32::from_str_radix(&s_ref, 10).unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `s_ref.parse::()` + +error: aborting due to 10 previous errors -- cgit 1.4.1-3-g733a5 From b038197b161e61a04fcf77e28f5284853640631c Mon Sep 17 00:00:00 2001 From: Ross MacArthur Date: Tue, 5 Aug 2025 17:05:23 +0200 Subject: Stabilize `core::iter::chain` --- library/core/src/iter/adapters/chain.rs | 4 +--- library/core/src/iter/adapters/mod.rs | 2 +- library/core/src/iter/mod.rs | 2 +- library/coretests/tests/lib.rs | 1 - 4 files changed, 3 insertions(+), 6 deletions(-) diff --git a/library/core/src/iter/adapters/chain.rs b/library/core/src/iter/adapters/chain.rs index dad3d79acb1..943b88e2330 100644 --- a/library/core/src/iter/adapters/chain.rs +++ b/library/core/src/iter/adapters/chain.rs @@ -45,8 +45,6 @@ impl Chain { /// # Examples /// /// ``` -/// #![feature(iter_chain)] -/// /// use std::iter::chain; /// /// let a = [1, 2, 3]; @@ -62,7 +60,7 @@ impl Chain { /// assert_eq!(iter.next(), Some(6)); /// assert_eq!(iter.next(), None); /// ``` -#[unstable(feature = "iter_chain", reason = "recently added", issue = "125964")] +#[stable(feature = "iter_chain", since = "CURRENT_RUSTC_VERSION")] pub fn chain(a: A, b: B) -> Chain where A: IntoIterator, diff --git a/library/core/src/iter/adapters/mod.rs b/library/core/src/iter/adapters/mod.rs index 2a0ef0189d1..6c6de0a4e5c 100644 --- a/library/core/src/iter/adapters/mod.rs +++ b/library/core/src/iter/adapters/mod.rs @@ -32,7 +32,7 @@ mod zip; pub use self::array_chunks::ArrayChunks; #[unstable(feature = "std_internals", issue = "none")] pub use self::by_ref_sized::ByRefSized; -#[unstable(feature = "iter_chain", reason = "recently added", issue = "125964")] +#[stable(feature = "iter_chain", since = "CURRENT_RUSTC_VERSION")] pub use self::chain::chain; #[stable(feature = "iter_cloned", since = "1.1.0")] pub use self::cloned::Cloned; diff --git a/library/core/src/iter/mod.rs b/library/core/src/iter/mod.rs index 56ca1305b60..bc07324f520 100644 --- a/library/core/src/iter/mod.rs +++ b/library/core/src/iter/mod.rs @@ -404,7 +404,7 @@ pub use self::adapters::StepBy; pub use self::adapters::TrustedRandomAccess; #[unstable(feature = "trusted_random_access", issue = "none")] pub use self::adapters::TrustedRandomAccessNoCoerce; -#[unstable(feature = "iter_chain", reason = "recently added", issue = "125964")] +#[stable(feature = "iter_chain", since = "CURRENT_RUSTC_VERSION")] pub use self::adapters::chain; pub(crate) use self::adapters::try_process; #[stable(feature = "iter_zip", since = "1.59.0")] diff --git a/library/coretests/tests/lib.rs b/library/coretests/tests/lib.rs index 0a9c0c61c95..b15ee88b8d7 100644 --- a/library/coretests/tests/lib.rs +++ b/library/coretests/tests/lib.rs @@ -59,7 +59,6 @@ #![feature(isolate_most_least_significant_one)] #![feature(iter_advance_by)] #![feature(iter_array_chunks)] -#![feature(iter_chain)] #![feature(iter_collect_into)] #![feature(iter_intersperse)] #![feature(iter_is_partitioned)] -- cgit 1.4.1-3-g733a5 From 9c0cfd262f4c1197d9993a78ad0fbfc04a9c774c Mon Sep 17 00:00:00 2001 From: Michael Tautschnig Date: Tue, 5 Aug 2025 08:59:08 +0000 Subject: Fix description of unsigned `checked_exact_div` Like its signed counterpart, this function does not panic. Also, fix the examples to document how it returns Some/None. --- library/core/src/num/uint_macros.rs | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/library/core/src/num/uint_macros.rs b/library/core/src/num/uint_macros.rs index 584cd60fbe5..ac5cbcec0b1 100644 --- a/library/core/src/num/uint_macros.rs +++ b/library/core/src/num/uint_macros.rs @@ -1103,23 +1103,17 @@ macro_rules! uint_impl { self / rhs } - /// Checked integer division without remainder. Computes `self / rhs`. - /// - /// # Panics - /// - /// This function will panic if `rhs == 0` or `self % rhs != 0`. + /// Checked integer division without remainder. Computes `self / rhs`, + /// returning `None` if `rhs == 0` or if `self % rhs != 0`. /// /// # Examples /// /// ``` /// #![feature(exact_div)] - #[doc = concat!("assert_eq!(64", stringify!($SelfT), ".exact_div(2), 32);")] - #[doc = concat!("assert_eq!(64", stringify!($SelfT), ".exact_div(32), 2);")] - /// ``` - /// - /// ```should_panic - /// #![feature(exact_div)] - #[doc = concat!("let _ = 65", stringify!($SelfT), ".exact_div(2);")] + #[doc = concat!("assert_eq!(64", stringify!($SelfT), ".checked_exact_div(2), Some(32));")] + #[doc = concat!("assert_eq!(64", stringify!($SelfT), ".checked_exact_div(32), Some(2));")] + #[doc = concat!("assert_eq!(64", stringify!($SelfT), ".checked_exact_div(0), None);")] + #[doc = concat!("assert_eq!(65", stringify!($SelfT), ".checked_exact_div(2), None);")] /// ``` #[unstable( feature = "exact_div", -- cgit 1.4.1-3-g733a5 From 9a1d38047fe367c32685b07a9ac41da4675b892d Mon Sep 17 00:00:00 2001 From: "Chai T. Rex" Date: Thu, 3 Jul 2025 23:38:17 -0400 Subject: Add `u8`-as-ASCII methods to `core::ascii::Char` --- library/core/src/ascii/ascii_char.rs | 604 ++++++++++++++++++++++++++++++++++- 1 file changed, 603 insertions(+), 1 deletion(-) diff --git a/library/core/src/ascii/ascii_char.rs b/library/core/src/ascii/ascii_char.rs index 054ddf84470..bde4b92c271 100644 --- a/library/core/src/ascii/ascii_char.rs +++ b/library/core/src/ascii/ascii_char.rs @@ -445,7 +445,7 @@ pub enum AsciiChar { } impl AsciiChar { - /// Creates an ascii character from the byte `b`, + /// Creates an ASCII character from the byte `b`, /// or returns `None` if it's too large. #[unstable(feature = "ascii_char", issue = "110998")] #[inline] @@ -540,6 +540,608 @@ impl AsciiChar { pub const fn as_str(&self) -> &str { crate::slice::from_ref(self).as_str() } + + /// Makes a copy of the value in its upper case equivalent. + /// + /// Letters 'a' to 'z' are mapped to 'A' to 'Z'. + /// + /// To uppercase the value in-place, use [`make_uppercase`]. + /// + /// # Examples + /// + /// ``` + /// #![feature(ascii_char, ascii_char_variants)] + /// use std::ascii; + /// + /// let lowercase_a = ascii::Char::SmallA; + /// + /// assert_eq!( + /// ascii::Char::CapitalA, + /// lowercase_a.to_uppercase(), + /// ); + /// ``` + /// + /// [`make_uppercase`]: Self::make_uppercase + #[must_use = "to uppercase the value in-place, use `make_uppercase()`"] + #[unstable(feature = "ascii_char", issue = "110998")] + #[inline] + pub const fn to_uppercase(self) -> Self { + let uppercase_byte = self.to_u8().to_ascii_uppercase(); + // SAFETY: Toggling the 6th bit won't convert ASCII to non-ASCII. + unsafe { Self::from_u8_unchecked(uppercase_byte) } + } + + /// Makes a copy of the value in its lower case equivalent. + /// + /// Letters 'A' to 'Z' are mapped to 'a' to 'z'. + /// + /// To lowercase the value in-place, use [`make_lowercase`]. + /// + /// # Examples + /// + /// ``` + /// #![feature(ascii_char, ascii_char_variants)] + /// use std::ascii; + /// + /// let uppercase_a = ascii::Char::CapitalA; + /// + /// assert_eq!( + /// ascii::Char::SmallA, + /// uppercase_a.to_lowercase(), + /// ); + /// ``` + /// + /// [`make_lowercase`]: Self::make_lowercase + #[must_use = "to lowercase the value in-place, use `make_lowercase()`"] + #[unstable(feature = "ascii_char", issue = "110998")] + #[inline] + pub const fn to_lowercase(self) -> Self { + let lowercase_byte = self.to_u8().to_ascii_lowercase(); + // SAFETY: Setting the 6th bit won't convert ASCII to non-ASCII. + unsafe { Self::from_u8_unchecked(lowercase_byte) } + } + + /// Checks that two values are a case-insensitive match. + /// + /// This is equivalent to `to_lowercase(a) == to_lowercase(b)`. + /// + /// # Examples + /// + /// ``` + /// #![feature(ascii_char, ascii_char_variants)] + /// use std::ascii; + /// + /// let lowercase_a = ascii::Char::SmallA; + /// let uppercase_a = ascii::Char::CapitalA; + /// + /// assert!(lowercase_a.eq_ignore_case(uppercase_a)); + /// ``` + #[unstable(feature = "ascii_char", issue = "110998")] + #[inline] + pub const fn eq_ignore_case(self, other: Self) -> bool { + // FIXME(const-hack) `arg.to_u8().to_ascii_lowercase()` -> `arg.to_lowercase()` + // once `PartialEq` is const for `Self`. + self.to_u8().to_ascii_lowercase() == other.to_u8().to_ascii_lowercase() + } + + /// Converts this value to its upper case equivalent in-place. + /// + /// Letters 'a' to 'z' are mapped to 'A' to 'Z'. + /// + /// To return a new uppercased value without modifying the existing one, use + /// [`to_uppercase`]. + /// + /// # Examples + /// + /// ``` + /// #![feature(ascii_char, ascii_char_variants)] + /// use std::ascii; + /// + /// let mut letter_a = ascii::Char::SmallA; + /// + /// letter_a.make_uppercase(); + /// + /// assert_eq!(ascii::Char::CapitalA, letter_a); + /// ``` + /// + /// [`to_uppercase`]: Self::to_uppercase + #[unstable(feature = "ascii_char", issue = "110998")] + #[inline] + pub const fn make_uppercase(&mut self) { + *self = self.to_uppercase(); + } + + /// Converts this value to its lower case equivalent in-place. + /// + /// Letters 'A' to 'Z' are mapped to 'a' to 'z'. + /// + /// To return a new lowercased value without modifying the existing one, use + /// [`to_lowercase`]. + /// + /// # Examples + /// + /// ``` + /// #![feature(ascii_char, ascii_char_variants)] + /// use std::ascii; + /// + /// let mut letter_a = ascii::Char::CapitalA; + /// + /// letter_a.make_lowercase(); + /// + /// assert_eq!(ascii::Char::SmallA, letter_a); + /// ``` + /// + /// [`to_lowercase`]: Self::to_lowercase + #[unstable(feature = "ascii_char", issue = "110998")] + #[inline] + pub const fn make_lowercase(&mut self) { + *self = self.to_lowercase(); + } + + /// Checks if the value is an alphabetic character: + /// + /// - 0x41 'A' ..= 0x5A 'Z', or + /// - 0x61 'a' ..= 0x7A 'z'. + /// + /// # Examples + /// + /// ``` + /// #![feature(ascii_char, ascii_char_variants)] + /// use std::ascii; + /// + /// let uppercase_a = ascii::Char::CapitalA; + /// let uppercase_g = ascii::Char::CapitalG; + /// let a = ascii::Char::SmallA; + /// let g = ascii::Char::SmallG; + /// let zero = ascii::Char::Digit0; + /// let percent = ascii::Char::PercentSign; + /// let space = ascii::Char::Space; + /// let lf = ascii::Char::LineFeed; + /// let esc = ascii::Char::Escape; + /// + /// assert!(uppercase_a.is_alphabetic()); + /// assert!(uppercase_g.is_alphabetic()); + /// assert!(a.is_alphabetic()); + /// assert!(g.is_alphabetic()); + /// assert!(!zero.is_alphabetic()); + /// assert!(!percent.is_alphabetic()); + /// assert!(!space.is_alphabetic()); + /// assert!(!lf.is_alphabetic()); + /// assert!(!esc.is_alphabetic()); + /// ``` + #[must_use] + #[unstable(feature = "ascii_char", issue = "110998")] + #[inline] + pub const fn is_alphabetic(self) -> bool { + self.to_u8().is_ascii_alphabetic() + } + + /// Checks if the value is an uppercase character: + /// 0x41 'A' ..= 0x5A 'Z'. + /// + /// # Examples + /// + /// ``` + /// #![feature(ascii_char, ascii_char_variants)] + /// use std::ascii; + /// + /// let uppercase_a = ascii::Char::CapitalA; + /// let uppercase_g = ascii::Char::CapitalG; + /// let a = ascii::Char::SmallA; + /// let g = ascii::Char::SmallG; + /// let zero = ascii::Char::Digit0; + /// let percent = ascii::Char::PercentSign; + /// let space = ascii::Char::Space; + /// let lf = ascii::Char::LineFeed; + /// let esc = ascii::Char::Escape; + /// + /// assert!(uppercase_a.is_uppercase()); + /// assert!(uppercase_g.is_uppercase()); + /// assert!(!a.is_uppercase()); + /// assert!(!g.is_uppercase()); + /// assert!(!zero.is_uppercase()); + /// assert!(!percent.is_uppercase()); + /// assert!(!space.is_uppercase()); + /// assert!(!lf.is_uppercase()); + /// assert!(!esc.is_uppercase()); + /// ``` + #[must_use] + #[unstable(feature = "ascii_char", issue = "110998")] + #[inline] + pub const fn is_uppercase(self) -> bool { + self.to_u8().is_ascii_uppercase() + } + + /// Checks if the value is a lowercase character: + /// 0x61 'a' ..= 0x7A 'z'. + /// + /// # Examples + /// + /// ``` + /// #![feature(ascii_char, ascii_char_variants)] + /// use std::ascii; + /// + /// let uppercase_a = ascii::Char::CapitalA; + /// let uppercase_g = ascii::Char::CapitalG; + /// let a = ascii::Char::SmallA; + /// let g = ascii::Char::SmallG; + /// let zero = ascii::Char::Digit0; + /// let percent = ascii::Char::PercentSign; + /// let space = ascii::Char::Space; + /// let lf = ascii::Char::LineFeed; + /// let esc = ascii::Char::Escape; + /// + /// assert!(!uppercase_a.is_lowercase()); + /// assert!(!uppercase_g.is_lowercase()); + /// assert!(a.is_lowercase()); + /// assert!(g.is_lowercase()); + /// assert!(!zero.is_lowercase()); + /// assert!(!percent.is_lowercase()); + /// assert!(!space.is_lowercase()); + /// assert!(!lf.is_lowercase()); + /// assert!(!esc.is_lowercase()); + /// ``` + #[must_use] + #[unstable(feature = "ascii_char", issue = "110998")] + #[inline] + pub const fn is_lowercase(self) -> bool { + self.to_u8().is_ascii_lowercase() + } + + /// Checks if the value is an alphanumeric character: + /// + /// - 0x41 'A' ..= 0x5A 'Z', or + /// - 0x61 'a' ..= 0x7A 'z', or + /// - 0x30 '0' ..= 0x39 '9'. + /// + /// # Examples + /// + /// ``` + /// #![feature(ascii_char, ascii_char_variants)] + /// use std::ascii; + /// + /// let uppercase_a = ascii::Char::CapitalA; + /// let uppercase_g = ascii::Char::CapitalG; + /// let a = ascii::Char::SmallA; + /// let g = ascii::Char::SmallG; + /// let zero = ascii::Char::Digit0; + /// let percent = ascii::Char::PercentSign; + /// let space = ascii::Char::Space; + /// let lf = ascii::Char::LineFeed; + /// let esc = ascii::Char::Escape; + /// + /// assert!(uppercase_a.is_alphanumeric()); + /// assert!(uppercase_g.is_alphanumeric()); + /// assert!(a.is_alphanumeric()); + /// assert!(g.is_alphanumeric()); + /// assert!(zero.is_alphanumeric()); + /// assert!(!percent.is_alphanumeric()); + /// assert!(!space.is_alphanumeric()); + /// assert!(!lf.is_alphanumeric()); + /// assert!(!esc.is_alphanumeric()); + /// ``` + #[must_use] + #[unstable(feature = "ascii_char", issue = "110998")] + #[inline] + pub const fn is_alphanumeric(self) -> bool { + self.to_u8().is_ascii_alphanumeric() + } + + /// Checks if the value is a decimal digit: + /// 0x30 '0' ..= 0x39 '9'. + /// + /// # Examples + /// + /// ``` + /// #![feature(ascii_char, ascii_char_variants)] + /// use std::ascii; + /// + /// let uppercase_a = ascii::Char::CapitalA; + /// let uppercase_g = ascii::Char::CapitalG; + /// let a = ascii::Char::SmallA; + /// let g = ascii::Char::SmallG; + /// let zero = ascii::Char::Digit0; + /// let percent = ascii::Char::PercentSign; + /// let space = ascii::Char::Space; + /// let lf = ascii::Char::LineFeed; + /// let esc = ascii::Char::Escape; + /// + /// assert!(!uppercase_a.is_digit()); + /// assert!(!uppercase_g.is_digit()); + /// assert!(!a.is_digit()); + /// assert!(!g.is_digit()); + /// assert!(zero.is_digit()); + /// assert!(!percent.is_digit()); + /// assert!(!space.is_digit()); + /// assert!(!lf.is_digit()); + /// assert!(!esc.is_digit()); + /// ``` + #[must_use] + #[unstable(feature = "ascii_char", issue = "110998")] + #[inline] + pub const fn is_digit(self) -> bool { + self.to_u8().is_ascii_digit() + } + + /// Checks if the value is an octal digit: + /// 0x30 '0' ..= 0x37 '7'. + /// + /// # Examples + /// + /// ``` + /// #![feature(ascii_char, ascii_char_variants, is_ascii_octdigit)] + /// + /// use std::ascii; + /// + /// let uppercase_a = ascii::Char::CapitalA; + /// let a = ascii::Char::SmallA; + /// let zero = ascii::Char::Digit0; + /// let seven = ascii::Char::Digit7; + /// let eight = ascii::Char::Digit8; + /// let percent = ascii::Char::PercentSign; + /// let lf = ascii::Char::LineFeed; + /// let esc = ascii::Char::Escape; + /// + /// assert!(!uppercase_a.is_octdigit()); + /// assert!(!a.is_octdigit()); + /// assert!(zero.is_octdigit()); + /// assert!(seven.is_octdigit()); + /// assert!(!eight.is_octdigit()); + /// assert!(!percent.is_octdigit()); + /// assert!(!lf.is_octdigit()); + /// assert!(!esc.is_octdigit()); + /// ``` + #[must_use] + // This is blocked on two unstable features. Please ensure both are + // stabilized before marking this method as stable. + #[unstable(feature = "ascii_char", issue = "110998")] + // #[unstable(feature = "is_ascii_octdigit", issue = "101288")] + #[inline] + pub const fn is_octdigit(self) -> bool { + self.to_u8().is_ascii_octdigit() + } + + /// Checks if the value is a hexadecimal digit: + /// + /// - 0x30 '0' ..= 0x39 '9', or + /// - 0x41 'A' ..= 0x46 'F', or + /// - 0x61 'a' ..= 0x66 'f'. + /// + /// # Examples + /// + /// ``` + /// #![feature(ascii_char, ascii_char_variants)] + /// use std::ascii; + /// + /// let uppercase_a = ascii::Char::CapitalA; + /// let uppercase_g = ascii::Char::CapitalG; + /// let a = ascii::Char::SmallA; + /// let g = ascii::Char::SmallG; + /// let zero = ascii::Char::Digit0; + /// let percent = ascii::Char::PercentSign; + /// let space = ascii::Char::Space; + /// let lf = ascii::Char::LineFeed; + /// let esc = ascii::Char::Escape; + /// + /// assert!(uppercase_a.is_hexdigit()); + /// assert!(!uppercase_g.is_hexdigit()); + /// assert!(a.is_hexdigit()); + /// assert!(!g.is_hexdigit()); + /// assert!(zero.is_hexdigit()); + /// assert!(!percent.is_hexdigit()); + /// assert!(!space.is_hexdigit()); + /// assert!(!lf.is_hexdigit()); + /// assert!(!esc.is_hexdigit()); + /// ``` + #[must_use] + #[unstable(feature = "ascii_char", issue = "110998")] + #[inline] + pub const fn is_hexdigit(self) -> bool { + self.to_u8().is_ascii_hexdigit() + } + + /// Checks if the value is a punctuation character: + /// + /// - 0x21 ..= 0x2F `! " # $ % & ' ( ) * + , - . /`, or + /// - 0x3A ..= 0x40 `: ; < = > ? @`, or + /// - 0x5B ..= 0x60 `` [ \ ] ^ _ ` ``, or + /// - 0x7B ..= 0x7E `{ | } ~` + /// + /// # Examples + /// + /// ``` + /// #![feature(ascii_char, ascii_char_variants)] + /// use std::ascii; + /// + /// let uppercase_a = ascii::Char::CapitalA; + /// let uppercase_g = ascii::Char::CapitalG; + /// let a = ascii::Char::SmallA; + /// let g = ascii::Char::SmallG; + /// let zero = ascii::Char::Digit0; + /// let percent = ascii::Char::PercentSign; + /// let space = ascii::Char::Space; + /// let lf = ascii::Char::LineFeed; + /// let esc = ascii::Char::Escape; + /// + /// assert!(!uppercase_a.is_punctuation()); + /// assert!(!uppercase_g.is_punctuation()); + /// assert!(!a.is_punctuation()); + /// assert!(!g.is_punctuation()); + /// assert!(!zero.is_punctuation()); + /// assert!(percent.is_punctuation()); + /// assert!(!space.is_punctuation()); + /// assert!(!lf.is_punctuation()); + /// assert!(!esc.is_punctuation()); + /// ``` + #[must_use] + #[unstable(feature = "ascii_char", issue = "110998")] + #[inline] + pub const fn is_punctuation(self) -> bool { + self.to_u8().is_ascii_punctuation() + } + + /// Checks if the value is a graphic character: + /// 0x21 '!' ..= 0x7E '~'. + /// + /// # Examples + /// + /// ``` + /// #![feature(ascii_char, ascii_char_variants)] + /// use std::ascii; + /// + /// let uppercase_a = ascii::Char::CapitalA; + /// let uppercase_g = ascii::Char::CapitalG; + /// let a = ascii::Char::SmallA; + /// let g = ascii::Char::SmallG; + /// let zero = ascii::Char::Digit0; + /// let percent = ascii::Char::PercentSign; + /// let space = ascii::Char::Space; + /// let lf = ascii::Char::LineFeed; + /// let esc = ascii::Char::Escape; + /// + /// assert!(uppercase_a.is_graphic()); + /// assert!(uppercase_g.is_graphic()); + /// assert!(a.is_graphic()); + /// assert!(g.is_graphic()); + /// assert!(zero.is_graphic()); + /// assert!(percent.is_graphic()); + /// assert!(!space.is_graphic()); + /// assert!(!lf.is_graphic()); + /// assert!(!esc.is_graphic()); + /// ``` + #[must_use] + #[unstable(feature = "ascii_char", issue = "110998")] + #[inline] + pub const fn is_graphic(self) -> bool { + self.to_u8().is_ascii_graphic() + } + + /// Checks if the value is a whitespace character: + /// 0x20 SPACE, 0x09 HORIZONTAL TAB, 0x0A LINE FEED, + /// 0x0C FORM FEED, or 0x0D CARRIAGE RETURN. + /// + /// Rust uses the WhatWG Infra Standard's [definition of ASCII + /// whitespace][infra-aw]. There are several other definitions in + /// wide use. For instance, [the POSIX locale][pct] includes + /// 0x0B VERTICAL TAB as well as all the above characters, + /// but—from the very same specification—[the default rule for + /// "field splitting" in the Bourne shell][bfs] considers *only* + /// SPACE, HORIZONTAL TAB, and LINE FEED as whitespace. + /// + /// If you are writing a program that will process an existing + /// file format, check what that format's definition of whitespace is + /// before using this function. + /// + /// [infra-aw]: https://infra.spec.whatwg.org/#ascii-whitespace + /// [pct]: https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap07.html#tag_07_03_01 + /// [bfs]: https://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_06_05 + /// + /// # Examples + /// + /// ``` + /// #![feature(ascii_char, ascii_char_variants)] + /// use std::ascii; + /// + /// let uppercase_a = ascii::Char::CapitalA; + /// let uppercase_g = ascii::Char::CapitalG; + /// let a = ascii::Char::SmallA; + /// let g = ascii::Char::SmallG; + /// let zero = ascii::Char::Digit0; + /// let percent = ascii::Char::PercentSign; + /// let space = ascii::Char::Space; + /// let lf = ascii::Char::LineFeed; + /// let esc = ascii::Char::Escape; + /// + /// assert!(!uppercase_a.is_whitespace()); + /// assert!(!uppercase_g.is_whitespace()); + /// assert!(!a.is_whitespace()); + /// assert!(!g.is_whitespace()); + /// assert!(!zero.is_whitespace()); + /// assert!(!percent.is_whitespace()); + /// assert!(space.is_whitespace()); + /// assert!(lf.is_whitespace()); + /// assert!(!esc.is_whitespace()); + /// ``` + #[must_use] + #[unstable(feature = "ascii_char", issue = "110998")] + #[inline] + pub const fn is_whitespace(self) -> bool { + self.to_u8().is_ascii_whitespace() + } + + /// Checks if the value is a control character: + /// 0x00 NUL ..= 0x1F UNIT SEPARATOR, or 0x7F DELETE. + /// Note that most whitespace characters are control + /// characters, but SPACE is not. + /// + /// # Examples + /// + /// ``` + /// #![feature(ascii_char, ascii_char_variants)] + /// use std::ascii; + /// + /// let uppercase_a = ascii::Char::CapitalA; + /// let uppercase_g = ascii::Char::CapitalG; + /// let a = ascii::Char::SmallA; + /// let g = ascii::Char::SmallG; + /// let zero = ascii::Char::Digit0; + /// let percent = ascii::Char::PercentSign; + /// let space = ascii::Char::Space; + /// let lf = ascii::Char::LineFeed; + /// let esc = ascii::Char::Escape; + /// + /// assert!(!uppercase_a.is_control()); + /// assert!(!uppercase_g.is_control()); + /// assert!(!a.is_control()); + /// assert!(!g.is_control()); + /// assert!(!zero.is_control()); + /// assert!(!percent.is_control()); + /// assert!(!space.is_control()); + /// assert!(lf.is_control()); + /// assert!(esc.is_control()); + /// ``` + #[must_use] + #[unstable(feature = "ascii_char", issue = "110998")] + #[inline] + pub const fn is_control(self) -> bool { + self.to_u8().is_ascii_control() + } + + /// Returns an iterator that produces an escaped version of a + /// character. + /// + /// The behavior is identical to + /// [`ascii::escape_default`](crate::ascii::escape_default). + /// + /// # Examples + /// + /// ``` + /// #![feature(ascii_char, ascii_char_variants)] + /// use std::ascii; + /// + /// let zero = ascii::Char::Digit0; + /// let tab = ascii::Char::CharacterTabulation; + /// let cr = ascii::Char::CarriageReturn; + /// let lf = ascii::Char::LineFeed; + /// let apostrophe = ascii::Char::Apostrophe; + /// let double_quote = ascii::Char::QuotationMark; + /// let backslash = ascii::Char::ReverseSolidus; + /// + /// assert_eq!("0", zero.escape_ascii().to_string()); + /// assert_eq!("\\t", tab.escape_ascii().to_string()); + /// assert_eq!("\\r", cr.escape_ascii().to_string()); + /// assert_eq!("\\n", lf.escape_ascii().to_string()); + /// assert_eq!("\\'", apostrophe.escape_ascii().to_string()); + /// assert_eq!("\\\"", double_quote.escape_ascii().to_string()); + /// assert_eq!("\\\\", backslash.escape_ascii().to_string()); + /// ``` + #[must_use = "this returns the escaped character as an iterator, \ + without modifying the original"] + #[unstable(feature = "ascii_char", issue = "110998")] + #[inline] + pub fn escape_ascii(self) -> super::EscapeDefault { + super::escape_default(self.to_u8()) + } } macro_rules! into_int_impl { -- cgit 1.4.1-3-g733a5 From d811581ba7b8f577b537d33446856eb6c6ad41fa Mon Sep 17 00:00:00 2001 From: "Chai T. Rex" Date: Fri, 4 Jul 2025 23:32:58 -0400 Subject: Add `std::ascii::Char` `MIN` and `MAX` constants --- library/core/src/ascii/ascii_char.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/library/core/src/ascii/ascii_char.rs b/library/core/src/ascii/ascii_char.rs index bde4b92c271..419e4694594 100644 --- a/library/core/src/ascii/ascii_char.rs +++ b/library/core/src/ascii/ascii_char.rs @@ -445,6 +445,14 @@ pub enum AsciiChar { } impl AsciiChar { + /// The character with the lowest ASCII code. + #[unstable(feature = "ascii_char", issue = "110998")] + pub const MIN: Self = Self::Null; + + /// The character with the highest ASCII code. + #[unstable(feature = "ascii_char", issue = "110998")] + pub const MAX: Self = Self::Delete; + /// Creates an ASCII character from the byte `b`, /// or returns `None` if it's too large. #[unstable(feature = "ascii_char", issue = "110998")] -- cgit 1.4.1-3-g733a5 From 070425e54d36fd20d363d6994d8ffdfa44ec160d Mon Sep 17 00:00:00 2001 From: Jens Reidel Date: Tue, 5 Aug 2025 16:26:42 +0200 Subject: Add aarch64_be-unknown-none-softfloat target Signed-off-by: Jens Reidel --- compiler/rustc_target/src/spec/mod.rs | 1 + .../targets/aarch64_be_unknown_none_softfloat.rs | 43 +++++++++++++ src/doc/rustc/src/SUMMARY.md | 1 + src/doc/rustc/src/platform-support.md | 1 + .../aarch64_be-unknown-none-softfloat.md | 74 ++++++++++++++++++++++ tests/assembly-llvm/targets/targets-elf.rs | 3 + 6 files changed, 123 insertions(+) create mode 100644 compiler/rustc_target/src/spec/targets/aarch64_be_unknown_none_softfloat.rs create mode 100644 src/doc/rustc/src/platform-support/aarch64_be-unknown-none-softfloat.md diff --git a/compiler/rustc_target/src/spec/mod.rs b/compiler/rustc_target/src/spec/mod.rs index 033590e01a6..126d17b9758 100644 --- a/compiler/rustc_target/src/spec/mod.rs +++ b/compiler/rustc_target/src/spec/mod.rs @@ -2146,6 +2146,7 @@ supported_targets! { ("aarch64-unknown-none", aarch64_unknown_none), ("aarch64-unknown-none-softfloat", aarch64_unknown_none_softfloat), + ("aarch64_be-unknown-none-softfloat", aarch64_be_unknown_none_softfloat), ("aarch64-unknown-nuttx", aarch64_unknown_nuttx), ("x86_64-fortanix-unknown-sgx", x86_64_fortanix_unknown_sgx), diff --git a/compiler/rustc_target/src/spec/targets/aarch64_be_unknown_none_softfloat.rs b/compiler/rustc_target/src/spec/targets/aarch64_be_unknown_none_softfloat.rs new file mode 100644 index 00000000000..7f918e85080 --- /dev/null +++ b/compiler/rustc_target/src/spec/targets/aarch64_be_unknown_none_softfloat.rs @@ -0,0 +1,43 @@ +// Generic big-endian AArch64 target for bare-metal code - Floating point disabled +// +// Can be used in conjunction with the `target-feature` and +// `target-cpu` compiler flags to opt-in more hardware-specific +// features. +// +// For example, `-C target-cpu=cortex-a53`. +use rustc_abi::Endian; + +use crate::spec::{ + Cc, LinkerFlavor, Lld, PanicStrategy, RelocModel, SanitizerSet, StackProbeType, Target, + TargetMetadata, TargetOptions, +}; + +pub(crate) fn target() -> Target { + let opts = TargetOptions { + abi: "softfloat".into(), + linker_flavor: LinkerFlavor::Gnu(Cc::No, Lld::Yes), + linker: Some("rust-lld".into()), + features: "+v8a,+strict-align,-neon,-fp-armv8".into(), + relocation_model: RelocModel::Static, + disable_redzone: true, + max_atomic_width: Some(128), + supported_sanitizers: SanitizerSet::KCFI | SanitizerSet::KERNELADDRESS, + stack_probes: StackProbeType::Inline, + panic_strategy: PanicStrategy::Abort, + endian: Endian::Big, + ..Default::default() + }; + Target { + llvm_target: "aarch64_be-unknown-none".into(), + metadata: TargetMetadata { + description: Some("Bare ARM64 (big-endian), softfloat".into()), + tier: Some(3), + host_tools: Some(false), + std: Some(false), + }, + pointer_width: 64, + data_layout: "E-m:e-p270:32:32-p271:32:32-p272:64:64-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128-Fn32".into(), + arch: "aarch64".into(), + options: opts, + } +} diff --git a/src/doc/rustc/src/SUMMARY.md b/src/doc/rustc/src/SUMMARY.md index 7c688e32bc0..83e51ca6369 100644 --- a/src/doc/rustc/src/SUMMARY.md +++ b/src/doc/rustc/src/SUMMARY.md @@ -47,6 +47,7 @@ - [\*-apple-visionos](platform-support/apple-visionos.md) - [aarch64-nintendo-switch-freestanding](platform-support/aarch64-nintendo-switch-freestanding.md) - [aarch64-unknown-linux-musl](platform-support/aarch64-unknown-linux-musl.md) + - [aarch64_be-unknown-none-softfloat](platform-support/aarch64_be-unknown-none-softfloat.md) - [amdgcn-amd-amdhsa](platform-support/amdgcn-amd-amdhsa.md) - [armeb-unknown-linux-gnueabi](platform-support/armeb-unknown-linux-gnueabi.md) - [arm-none-eabi](platform-support/arm-none-eabi.md) diff --git a/src/doc/rustc/src/platform-support.md b/src/doc/rustc/src/platform-support.md index 65b70630153..005be9c4d66 100644 --- a/src/doc/rustc/src/platform-support.md +++ b/src/doc/rustc/src/platform-support.md @@ -273,6 +273,7 @@ target | std | host | notes `aarch64_be-unknown-linux-gnu` | ✓ | ✓ | ARM64 Linux (big-endian) `aarch64_be-unknown-linux-gnu_ilp32` | ✓ | ✓ | ARM64 Linux (big-endian, ILP32 ABI) [`aarch64_be-unknown-netbsd`](platform-support/netbsd.md) | ✓ | ✓ | ARM64 NetBSD (big-endian) +[`aarch64_be-unknown-none-softfloat`](platform-support/aarch64_be-unknown-none-softfloat.md) | * | | Bare big-endian ARM64, softfloat [`amdgcn-amd-amdhsa`](platform-support/amdgcn-amd-amdhsa.md) | * | | `-Ctarget-cpu=gfx...` to specify [the AMD GPU] to compile for [`arm64_32-apple-watchos`](platform-support/apple-watchos.md) | ✓ | | Arm Apple WatchOS 64-bit with 32-bit pointers [`arm64e-apple-darwin`](platform-support/arm64e-apple-darwin.md) | ✓ | ✓ | ARM64e Apple Darwin diff --git a/src/doc/rustc/src/platform-support/aarch64_be-unknown-none-softfloat.md b/src/doc/rustc/src/platform-support/aarch64_be-unknown-none-softfloat.md new file mode 100644 index 00000000000..a28ddcdf7f2 --- /dev/null +++ b/src/doc/rustc/src/platform-support/aarch64_be-unknown-none-softfloat.md @@ -0,0 +1,74 @@ +# aarch64_be-unknown-none-softfloat + +**Tier: 3** + +Target for freestanding/bare-metal big-endian ARM64 binaries in ELF format: +firmware, kernels, etc. + +## Target maintainers + +[@Gelbpunkt](https://github.com/Gelbpunkt) + +## Requirements + +This target is cross-compiled. There is no support for `std`. There is no +default allocator, but it's possible to use `alloc` by supplying an allocator. + +The target does not assume existence of a FPU and does not make use of any +non-GPR register. This allows the generated code to run in environments, such +as kernels, which may need to avoid the use of such registers or which may have +special considerations about the use of such registers (e.g. saving and +restoring them to avoid breaking userspace code using the same registers). You +can change code generation to use additional CPU features via the +`-C target-feature=` codegen options to rustc, or via the `#[target_feature]` +mechanism within Rust code. + +By default, code generated with the soft-float target should run on any +big-endian ARM64 hardware, enabling additional target features may raise this +baseline. + +`extern "C"` uses the [architecture's standard calling convention][aapcs64]. + +[aapcs64]: https://github.com/ARM-software/abi-aa/blob/main/aapcs64/aapcs64.rst + +The targets generate binaries in the ELF format. Any alternate formats or +special considerations for binary layout will require linker options or linker +scripts. + +## Building the target + +You can build Rust with support for the target by adding it to the `target` +list in `bootstrap.toml`: + +```toml +[build] +target = ["aarch64_be-unknown-none-softfloat"] +``` + +## Building Rust programs + +Rust does not yet ship pre-compiled artifacts for this target. To compile for +this target, you will first need to build Rust with the target enabled (see +"Building the target" above). + +## Cross-compilation + +For cross builds, you will need an appropriate ARM64 C/C++ toolchain for +linking, or if you want to compile C code along with Rust (such as for Rust +crates with C dependencies). + +Rust *may* be able to use an `aarch64_be-unknown-linux-{gnu,musl}-` toolchain +with appropriate standalone flags to build for this target (depending on the +assumptions of that toolchain, see below), or you may wish to use a separate +`aarch64_be-unknown-none-softfloat` toolchain. + +On some ARM64 hosts that use ELF binaries, you *may* be able to use the host C +toolchain, if it does not introduce assumptions about the host environment that +don't match the expectations of a standalone environment. Otherwise, you may +need a separate toolchain for standalone/freestanding development, just as when +cross-compiling from a non-ARM64 platform. + +## Testing + +As the target supports a variety of different environments and does not support +`std`, it does not support running the Rust test suite. diff --git a/tests/assembly-llvm/targets/targets-elf.rs b/tests/assembly-llvm/targets/targets-elf.rs index edf16548e7d..11c7567cfa1 100644 --- a/tests/assembly-llvm/targets/targets-elf.rs +++ b/tests/assembly-llvm/targets/targets-elf.rs @@ -10,6 +10,9 @@ //@ revisions: aarch64_be_unknown_netbsd //@ [aarch64_be_unknown_netbsd] compile-flags: --target aarch64_be-unknown-netbsd //@ [aarch64_be_unknown_netbsd] needs-llvm-components: aarch64 +//@ revisions: aarch64_be_unknown_none_softfloat +//@ [aarch64_be_unknown_none_softfloat] compile-flags: --target aarch64_be-unknown-none-softfloat +//@ [aarch64_be_unknown_none_softfloat] needs-llvm-components: aarch64 //@ revisions: aarch64_kmc_solid_asp3 //@ [aarch64_kmc_solid_asp3] compile-flags: --target aarch64-kmc-solid_asp3 //@ [aarch64_kmc_solid_asp3] needs-llvm-components: aarch64 -- cgit 1.4.1-3-g733a5 From dfd38f0d36d04c1d080578481546029e4ebbebca Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Sat, 2 Aug 2025 02:18:47 +1000 Subject: Add comments to `Printer`. These details took me some time to work out. --- compiler/rustc_middle/src/ty/print/mod.rs | 56 +++++++++++++++++++++---------- 1 file changed, 39 insertions(+), 17 deletions(-) diff --git a/compiler/rustc_middle/src/ty/print/mod.rs b/compiler/rustc_middle/src/ty/print/mod.rs index 8a125c7fe28..e404206d19e 100644 --- a/compiler/rustc_middle/src/ty/print/mod.rs +++ b/compiler/rustc_middle/src/ty/print/mod.rs @@ -21,18 +21,16 @@ pub trait Print<'tcx, P> { fn print(&self, p: &mut P) -> Result<(), PrintError>; } -/// Interface for outputting user-facing "type-system entities" -/// (paths, types, lifetimes, constants, etc.) as a side-effect -/// (e.g. formatting, like `PrettyPrinter` implementors do) or by -/// constructing some alternative representation (e.g. an AST), -/// which the associated types allow passing through the methods. -/// -/// For pretty-printing/formatting in particular, see `PrettyPrinter`. -// -// FIXME(eddyb) find a better name; this is more general than "printing". +/// A trait that "prints" user-facing type system entities: paths, types, lifetimes, constants, +/// etc. "Printing" here means building up a representation of the entity's path, usually as a +/// `String` (e.g. "std::io::Read") or a `Vec` (e.g. `[sym::std, sym::io, sym::Read]`). The +/// representation is built up by appending one or more pieces. The specific details included in +/// the built-up representation depend on the purpose of the printer. The more advanced printers +/// also rely on the `PrettyPrinter` sub-trait. pub trait Printer<'tcx>: Sized { fn tcx<'a>(&'a self) -> TyCtxt<'tcx>; + /// Appends a representation of an entity with a normal path, e.g. "std::io::Read". fn print_def_path( &mut self, def_id: DefId, @@ -41,6 +39,7 @@ pub trait Printer<'tcx>: Sized { self.default_print_def_path(def_id, args) } + /// Like `print_def_path`, but for `DefPathData::Impl`. fn print_impl_path( &mut self, impl_def_id: DefId, @@ -66,25 +65,40 @@ pub trait Printer<'tcx>: Sized { self.default_print_impl_path(impl_def_id, self_ty, impl_trait_ref) } + /// Appends a representation of a region. fn print_region(&mut self, region: ty::Region<'tcx>) -> Result<(), PrintError>; + /// Appends a representation of a type. fn print_type(&mut self, ty: Ty<'tcx>) -> Result<(), PrintError>; + /// Appends a representation of a list of `PolyExistentialPredicate`s. fn print_dyn_existential( &mut self, predicates: &'tcx ty::List>, ) -> Result<(), PrintError>; + /// Appends a representation of a const. fn print_const(&mut self, ct: ty::Const<'tcx>) -> Result<(), PrintError>; + /// Appends a representation of a crate name, e.g. `std`, or even ``. fn path_crate(&mut self, cnum: CrateNum) -> Result<(), PrintError>; - fn path_qualified( + /// Appends a representation of a (full or partial) simple path, in two parts. `print_prefix`, + /// when called, appends the representation of the leading segments. The rest of the method + /// appends the representation of the final segment, the details of which are in + /// `disambiguated_data`. + /// + /// E.g. `std::io` + `Read` -> `std::io::Read`. + fn path_append( &mut self, - self_ty: Ty<'tcx>, - trait_ref: Option>, + print_prefix: impl FnOnce(&mut Self) -> Result<(), PrintError>, + disambiguated_data: &DisambiguatedDefPathData, ) -> Result<(), PrintError>; + /// Similar to `path_append`, but the final segment is an `impl` segment. + /// + /// E.g. `slice` + `` -> `slice::`, which may then be further appended to, + /// giving a longer path representation such as `slice::::to_vec_in::ConvertVec`. fn path_append_impl( &mut self, print_prefix: impl FnOnce(&mut Self) -> Result<(), PrintError>, @@ -92,16 +106,24 @@ pub trait Printer<'tcx>: Sized { trait_ref: Option>, ) -> Result<(), PrintError>; - fn path_append( + /// Appends a representation of a path ending in generic args, in two parts. `print_prefix`, + /// when called, appends the leading segments. The rest of the method appends the + /// representation of the generic args. (Some printers choose to skip appending the generic + /// args.) + /// + /// E.g. `ImplementsTraitForUsize` + `` -> `ImplementsTraitForUsize`. + fn path_generic_args( &mut self, print_prefix: impl FnOnce(&mut Self) -> Result<(), PrintError>, - disambiguated_data: &DisambiguatedDefPathData, + args: &[GenericArg<'tcx>], ) -> Result<(), PrintError>; - fn path_generic_args( + /// Appends a representation of a qualified path segment, e.g. `>`. + /// If `trait_ref` is `None`, it may fall back to simpler forms, e.g. `>` or just `Foo`. + fn path_qualified( &mut self, - print_prefix: impl FnOnce(&mut Self) -> Result<(), PrintError>, - args: &[GenericArg<'tcx>], + self_ty: Ty<'tcx>, + trait_ref: Option>, ) -> Result<(), PrintError>; fn should_truncate(&mut self) -> bool { -- cgit 1.4.1-3-g733a5 From 69bcd79ab78fb02798406424ab64d209bfed1880 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Sun, 3 Aug 2025 20:38:11 +1000 Subject: Move `should_truncate` from trait `Printer` to sub-trait `PrettyPrinter`. It's not used in `Printer`. --- compiler/rustc_middle/src/ty/print/mod.rs | 4 ---- compiler/rustc_middle/src/ty/print/pretty.rs | 12 ++++++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/compiler/rustc_middle/src/ty/print/mod.rs b/compiler/rustc_middle/src/ty/print/mod.rs index e404206d19e..c0ad1ba3846 100644 --- a/compiler/rustc_middle/src/ty/print/mod.rs +++ b/compiler/rustc_middle/src/ty/print/mod.rs @@ -126,10 +126,6 @@ pub trait Printer<'tcx>: Sized { trait_ref: Option>, ) -> Result<(), PrintError>; - fn should_truncate(&mut self) -> bool { - false - } - // Defaults (should not be overridden): #[instrument(skip(self), level = "debug")] diff --git a/compiler/rustc_middle/src/ty/print/pretty.rs b/compiler/rustc_middle/src/ty/print/pretty.rs index c150aa65b9b..394b94999bf 100644 --- a/compiler/rustc_middle/src/ty/print/pretty.rs +++ b/compiler/rustc_middle/src/ty/print/pretty.rs @@ -333,6 +333,10 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { f: impl FnOnce(&mut Self) -> Result<(), PrintError>, ) -> Result<(), PrintError>; + fn should_truncate(&mut self) -> bool { + false + } + /// Returns `true` if the region should be printed in /// optional positions, e.g., `&'a T` or `dyn Tr + 'b`. /// This is typically the case for all non-`'_` regions. @@ -2295,10 +2299,6 @@ impl<'tcx> Printer<'tcx> for FmtPrinter<'_, 'tcx> { } } - fn should_truncate(&mut self) -> bool { - !self.type_length_limit.value_within_limit(self.printed_type_count) - } - fn print_dyn_existential( &mut self, predicates: &'tcx ty::List>, @@ -2487,6 +2487,10 @@ impl<'tcx> PrettyPrinter<'tcx> for FmtPrinter<'_, 'tcx> { Ok(()) } + fn should_truncate(&mut self) -> bool { + !self.type_length_limit.value_within_limit(self.printed_type_count) + } + fn should_print_region(&self, region: ty::Region<'tcx>) -> bool { let highlight = self.region_highlight_mode; if highlight.region_highlighted(region).is_some() { -- cgit 1.4.1-3-g733a5 From b8adda6194dfe3296fff73812dcaa88c58a4f0f1 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Sun, 3 Aug 2025 21:02:28 +1000 Subject: Rename some `Printer` methods. I find these name clearer, and starting them all with `print_` makes things more consistent. --- compiler/rustc_const_eval/src/util/type_name.rs | 10 +++--- compiler/rustc_lint/src/context.rs | 10 +++--- compiler/rustc_middle/src/ty/print/mod.rs | 30 ++++++++++-------- compiler/rustc_middle/src/ty/print/pretty.rs | 37 ++++++++++++---------- compiler/rustc_symbol_mangling/src/legacy.rs | 17 ++++++---- compiler/rustc_symbol_mangling/src/v0.rs | 16 +++++----- .../src/error_reporting/infer/mod.rs | 10 +++--- .../src/error_reporting/infer/note_and_explain.rs | 2 +- 8 files changed, 71 insertions(+), 61 deletions(-) diff --git a/compiler/rustc_const_eval/src/util/type_name.rs b/compiler/rustc_const_eval/src/util/type_name.rs index 573c734e931..2f74f998fd0 100644 --- a/compiler/rustc_const_eval/src/util/type_name.rs +++ b/compiler/rustc_const_eval/src/util/type_name.rs @@ -73,12 +73,12 @@ impl<'tcx> Printer<'tcx> for TypeNamePrinter<'tcx> { self.pretty_print_dyn_existential(predicates) } - fn path_crate(&mut self, cnum: CrateNum) -> Result<(), PrintError> { + fn print_crate_name(&mut self, cnum: CrateNum) -> Result<(), PrintError> { self.path.push_str(self.tcx.crate_name(cnum).as_str()); Ok(()) } - fn path_qualified( + fn print_path_with_qualified( &mut self, self_ty: Ty<'tcx>, trait_ref: Option>, @@ -86,7 +86,7 @@ impl<'tcx> Printer<'tcx> for TypeNamePrinter<'tcx> { self.pretty_path_qualified(self_ty, trait_ref) } - fn path_append_impl( + fn print_path_with_impl( &mut self, print_prefix: impl FnOnce(&mut Self) -> Result<(), PrintError>, self_ty: Ty<'tcx>, @@ -105,7 +105,7 @@ impl<'tcx> Printer<'tcx> for TypeNamePrinter<'tcx> { ) } - fn path_append( + fn print_path_with_simple( &mut self, print_prefix: impl FnOnce(&mut Self) -> Result<(), PrintError>, disambiguated_data: &DisambiguatedDefPathData, @@ -117,7 +117,7 @@ impl<'tcx> Printer<'tcx> for TypeNamePrinter<'tcx> { Ok(()) } - fn path_generic_args( + fn print_path_with_generic_args( &mut self, print_prefix: impl FnOnce(&mut Self) -> Result<(), PrintError>, args: &[GenericArg<'tcx>], diff --git a/compiler/rustc_lint/src/context.rs b/compiler/rustc_lint/src/context.rs index 0dd50af2efc..d9163d94710 100644 --- a/compiler/rustc_lint/src/context.rs +++ b/compiler/rustc_lint/src/context.rs @@ -774,12 +774,12 @@ impl<'tcx> LateContext<'tcx> { unreachable!(); // because `path_generic_args` ignores the `GenericArgs` } - fn path_crate(&mut self, cnum: CrateNum) -> Result<(), PrintError> { + fn print_crate_name(&mut self, cnum: CrateNum) -> Result<(), PrintError> { self.path = vec![self.tcx.crate_name(cnum)]; Ok(()) } - fn path_qualified( + fn print_path_with_qualified( &mut self, self_ty: Ty<'tcx>, trait_ref: Option>, @@ -800,7 +800,7 @@ impl<'tcx> LateContext<'tcx> { }) } - fn path_append_impl( + fn print_path_with_impl( &mut self, print_prefix: impl FnOnce(&mut Self) -> Result<(), PrintError>, self_ty: Ty<'tcx>, @@ -825,7 +825,7 @@ impl<'tcx> LateContext<'tcx> { Ok(()) } - fn path_append( + fn print_path_with_simple( &mut self, print_prefix: impl FnOnce(&mut Self) -> Result<(), PrintError>, disambiguated_data: &DisambiguatedDefPathData, @@ -844,7 +844,7 @@ impl<'tcx> LateContext<'tcx> { Ok(()) } - fn path_generic_args( + fn print_path_with_generic_args( &mut self, print_prefix: impl FnOnce(&mut Self) -> Result<(), PrintError>, _args: &[GenericArg<'tcx>], diff --git a/compiler/rustc_middle/src/ty/print/mod.rs b/compiler/rustc_middle/src/ty/print/mod.rs index c0ad1ba3846..4ca483eafe3 100644 --- a/compiler/rustc_middle/src/ty/print/mod.rs +++ b/compiler/rustc_middle/src/ty/print/mod.rs @@ -81,7 +81,7 @@ pub trait Printer<'tcx>: Sized { fn print_const(&mut self, ct: ty::Const<'tcx>) -> Result<(), PrintError>; /// Appends a representation of a crate name, e.g. `std`, or even ``. - fn path_crate(&mut self, cnum: CrateNum) -> Result<(), PrintError>; + fn print_crate_name(&mut self, cnum: CrateNum) -> Result<(), PrintError>; /// Appends a representation of a (full or partial) simple path, in two parts. `print_prefix`, /// when called, appends the representation of the leading segments. The rest of the method @@ -89,17 +89,17 @@ pub trait Printer<'tcx>: Sized { /// `disambiguated_data`. /// /// E.g. `std::io` + `Read` -> `std::io::Read`. - fn path_append( + fn print_path_with_simple( &mut self, print_prefix: impl FnOnce(&mut Self) -> Result<(), PrintError>, disambiguated_data: &DisambiguatedDefPathData, ) -> Result<(), PrintError>; - /// Similar to `path_append`, but the final segment is an `impl` segment. + /// Similar to `print_path_with_simple`, but the final segment is an `impl` segment. /// /// E.g. `slice` + `` -> `slice::`, which may then be further appended to, /// giving a longer path representation such as `slice::::to_vec_in::ConvertVec`. - fn path_append_impl( + fn print_path_with_impl( &mut self, print_prefix: impl FnOnce(&mut Self) -> Result<(), PrintError>, self_ty: Ty<'tcx>, @@ -112,7 +112,7 @@ pub trait Printer<'tcx>: Sized { /// args.) /// /// E.g. `ImplementsTraitForUsize` + `` -> `ImplementsTraitForUsize`. - fn path_generic_args( + fn print_path_with_generic_args( &mut self, print_prefix: impl FnOnce(&mut Self) -> Result<(), PrintError>, args: &[GenericArg<'tcx>], @@ -120,7 +120,7 @@ pub trait Printer<'tcx>: Sized { /// Appends a representation of a qualified path segment, e.g. `>`. /// If `trait_ref` is `None`, it may fall back to simpler forms, e.g. `>` or just `Foo`. - fn path_qualified( + fn print_path_with_qualified( &mut self, self_ty: Ty<'tcx>, trait_ref: Option>, @@ -140,7 +140,7 @@ pub trait Printer<'tcx>: Sized { match key.disambiguated_data.data { DefPathData::CrateRoot => { assert!(key.parent.is_none()); - self.path_crate(def_id.krate) + self.print_crate_name(def_id.krate) } DefPathData::Impl => self.print_impl_path(def_id, args), @@ -164,7 +164,7 @@ pub trait Printer<'tcx>: Sized { )) = self.tcx().coroutine_kind(def_id) && args.len() > parent_args.len() { - return self.path_generic_args( + return self.print_path_with_generic_args( |p| p.print_def_path(def_id, parent_args), &args[..parent_args.len() + 1][..1], ); @@ -186,7 +186,7 @@ pub trait Printer<'tcx>: Sized { _ => { if !generics.is_own_empty() && args.len() >= generics.count() { let args = generics.own_args_no_defaults(self.tcx(), args); - return self.path_generic_args( + return self.print_path_with_generic_args( |p| p.print_def_path(def_id, parent_args), args, ); @@ -202,7 +202,7 @@ pub trait Printer<'tcx>: Sized { && self.tcx().generics_of(parent_def_id).parent_count == 0; } - self.path_append( + self.print_path_with_simple( |p: &mut Self| { if trait_qualify_parent { let trait_ref = ty::TraitRef::new( @@ -210,7 +210,7 @@ pub trait Printer<'tcx>: Sized { parent_def_id, parent_args.iter().copied(), ); - p.path_qualified(trait_ref.self_ty(), Some(trait_ref)) + p.print_path_with_qualified(trait_ref.self_ty(), Some(trait_ref)) } else { p.print_def_path(parent_def_id, parent_args) } @@ -253,11 +253,15 @@ pub trait Printer<'tcx>: Sized { // If the impl is not co-located with either self-type or // trait-type, then fallback to a format that identifies // the module more clearly. - self.path_append_impl(|p| p.print_def_path(parent_def_id, &[]), self_ty, impl_trait_ref) + self.print_path_with_impl( + |p| p.print_def_path(parent_def_id, &[]), + self_ty, + impl_trait_ref, + ) } else { // Otherwise, try to give a good form that would be valid language // syntax. Preferably using associated item notation. - self.path_qualified(self_ty, impl_trait_ref) + self.print_path_with_qualified(self_ty, impl_trait_ref) } } } diff --git a/compiler/rustc_middle/src/ty/print/pretty.rs b/compiler/rustc_middle/src/ty/print/pretty.rs index 394b94999bf..a7ff1b51833 100644 --- a/compiler/rustc_middle/src/ty/print/pretty.rs +++ b/compiler/rustc_middle/src/ty/print/pretty.rs @@ -474,7 +474,7 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { // path to the crate followed by the path to the item within the crate. if let Some(cnum) = def_id.as_crate_root() { if cnum == LOCAL_CRATE { - self.path_crate(cnum)?; + self.print_crate_name(cnum)?; return Ok(true); } @@ -498,7 +498,7 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { // or avoid ending up with `ExternCrateSource::Extern`, // for the injected `std`/`core`. if span.is_dummy() { - self.path_crate(cnum)?; + self.print_crate_name(cnum)?; return Ok(true); } @@ -512,13 +512,13 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { return Ok(true); } (ExternCrateSource::Path, LOCAL_CRATE) => { - self.path_crate(cnum)?; + self.print_crate_name(cnum)?; return Ok(true); } _ => {} }, None => { - self.path_crate(cnum)?; + self.print_crate_name(cnum)?; return Ok(true); } } @@ -628,7 +628,7 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { return Ok(false); } callers.push(visible_parent); - // HACK(eddyb) this bypasses `path_append`'s prefix printing to avoid + // HACK(eddyb) this bypasses `print_path_with_simple`'s prefix printing to avoid // knowing ahead of time whether the entire path will succeed or not. // To support printers that do not implement `PrettyPrinter`, a `Vec` or // linked list on the stack would need to be built, before any printing. @@ -637,7 +637,10 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { true => {} } callers.pop(); - self.path_append(|_| Ok(()), &DisambiguatedDefPathData { data, disambiguator: 0 })?; + self.print_path_with_simple( + |_| Ok(()), + &DisambiguatedDefPathData { data, disambiguator: 0 }, + )?; Ok(true) } @@ -1312,10 +1315,10 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { alias_ty: ty::AliasTerm<'tcx>, ) -> Result<(), PrintError> { let def_key = self.tcx().def_key(alias_ty.def_id); - self.path_generic_args( + self.print_path_with_generic_args( |p| { - p.path_append( - |p| p.path_qualified(alias_ty.self_ty(), None), + p.print_path_with_simple( + |p| p.print_path_with_qualified(alias_ty.self_ty(), None), &def_key.disambiguated_data, ) }, @@ -1400,7 +1403,7 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { } } - // HACK(eddyb) this duplicates `FmtPrinter`'s `path_generic_args`, + // HACK(eddyb) this duplicates `FmtPrinter`'s `print_path_with_generic_args`, // in order to place the projections inside the `<...>`. if !resugared { let principal_with_self = @@ -2234,7 +2237,7 @@ impl<'tcx> Printer<'tcx> for FmtPrinter<'_, 'tcx> { self.print_def_path(parent_def_id, &[])?; - // HACK(eddyb) copy of `path_append` to avoid + // HACK(eddyb) copy of `print_path_with_simple` to avoid // constructing a `DisambiguatedDefPathData`. if !self.empty_path { write!(self, "::")?; @@ -2310,7 +2313,7 @@ impl<'tcx> Printer<'tcx> for FmtPrinter<'_, 'tcx> { self.pretty_print_const(ct, false) } - fn path_crate(&mut self, cnum: CrateNum) -> Result<(), PrintError> { + fn print_crate_name(&mut self, cnum: CrateNum) -> Result<(), PrintError> { self.empty_path = true; if cnum == LOCAL_CRATE { if self.tcx.sess.at_least_rust_2018() { @@ -2327,7 +2330,7 @@ impl<'tcx> Printer<'tcx> for FmtPrinter<'_, 'tcx> { Ok(()) } - fn path_qualified( + fn print_path_with_qualified( &mut self, self_ty: Ty<'tcx>, trait_ref: Option>, @@ -2337,7 +2340,7 @@ impl<'tcx> Printer<'tcx> for FmtPrinter<'_, 'tcx> { Ok(()) } - fn path_append_impl( + fn print_path_with_impl( &mut self, print_prefix: impl FnOnce(&mut Self) -> Result<(), PrintError>, self_ty: Ty<'tcx>, @@ -2359,7 +2362,7 @@ impl<'tcx> Printer<'tcx> for FmtPrinter<'_, 'tcx> { Ok(()) } - fn path_append( + fn print_path_with_simple( &mut self, print_prefix: impl FnOnce(&mut Self) -> Result<(), PrintError>, disambiguated_data: &DisambiguatedDefPathData, @@ -2390,7 +2393,7 @@ impl<'tcx> Printer<'tcx> for FmtPrinter<'_, 'tcx> { Ok(()) } - fn path_generic_args( + fn print_path_with_generic_args( &mut self, print_prefix: impl FnOnce(&mut Self) -> Result<(), PrintError>, args: &[GenericArg<'tcx>], @@ -3229,7 +3232,7 @@ define_print! { // The args don't contain the self ty (as it has been erased) but the corresp. // generics do as the trait always has a self ty param. We need to offset. let args = &self.args[p.tcx().generics_of(self.def_id).parent_count - 1..]; - p.path_generic_args(|p| write!(p, "{name}"), args)?; + p.print_path_with_generic_args(|p| write!(p, "{name}"), args)?; write!(p, " = ")?; self.term.print(p)?; } diff --git a/compiler/rustc_symbol_mangling/src/legacy.rs b/compiler/rustc_symbol_mangling/src/legacy.rs index f8bab0f5d0c..350546d4b66 100644 --- a/compiler/rustc_symbol_mangling/src/legacy.rs +++ b/compiler/rustc_symbol_mangling/src/legacy.rs @@ -218,8 +218,8 @@ struct LegacySymbolMangler<'tcx> { path: SymbolPath, // When `true`, `finalize_pending_component` isn't used. - // This is needed when recursing into `path_qualified`, - // or `path_generic_args`, as any nested paths are + // This is needed when recursing into `print_path_with_qualified`, + // or `print_path_with_generic_args`, as any nested paths are // logically within one component. keep_within_component: bool, } @@ -303,11 +303,12 @@ impl<'tcx> Printer<'tcx> for LegacySymbolMangler<'tcx> { Ok(()) } - fn path_crate(&mut self, cnum: CrateNum) -> Result<(), PrintError> { + fn print_crate_name(&mut self, cnum: CrateNum) -> Result<(), PrintError> { self.write_str(self.tcx.crate_name(cnum).as_str())?; Ok(()) } - fn path_qualified( + + fn print_path_with_qualified( &mut self, self_ty: Ty<'tcx>, trait_ref: Option>, @@ -329,7 +330,7 @@ impl<'tcx> Printer<'tcx> for LegacySymbolMangler<'tcx> { } } - fn path_append_impl( + fn print_path_with_impl( &mut self, print_prefix: impl FnOnce(&mut Self) -> Result<(), PrintError>, self_ty: Ty<'tcx>, @@ -352,7 +353,8 @@ impl<'tcx> Printer<'tcx> for LegacySymbolMangler<'tcx> { trait_ref, ) } - fn path_append( + + fn print_path_with_simple( &mut self, print_prefix: impl FnOnce(&mut Self) -> Result<(), PrintError>, disambiguated_data: &DisambiguatedDefPathData, @@ -375,7 +377,8 @@ impl<'tcx> Printer<'tcx> for LegacySymbolMangler<'tcx> { Ok(()) } - fn path_generic_args( + + fn print_path_with_generic_args( &mut self, print_prefix: impl FnOnce(&mut Self) -> Result<(), PrintError>, args: &[GenericArg<'tcx>], diff --git a/compiler/rustc_symbol_mangling/src/v0.rs b/compiler/rustc_symbol_mangling/src/v0.rs index 2aad5f315f3..0cbd48ba08c 100644 --- a/compiler/rustc_symbol_mangling/src/v0.rs +++ b/compiler/rustc_symbol_mangling/src/v0.rs @@ -365,7 +365,7 @@ impl<'tcx> Printer<'tcx> for V0SymbolMangler<'tcx> { // Encode impl generic params if the generic parameters contain non-region parameters // and this isn't an inherent impl. if impl_trait_ref.is_some() && args.iter().any(|a| a.has_non_region_param()) { - self.path_generic_args( + self.print_path_with_generic_args( |this| { this.path_append_ns( |p| p.print_def_path(parent_def_id, &[]), @@ -786,7 +786,7 @@ impl<'tcx> Printer<'tcx> for V0SymbolMangler<'tcx> { None => { self.push("S"); for (field_def, field) in iter::zip(&variant_def.fields, fields) { - // HACK(eddyb) this mimics `path_append`, + // HACK(eddyb) this mimics `print_path_with_simple`, // instead of simply using `field_def.ident`, // just to be able to handle disambiguators. let disambiguated_field = @@ -819,7 +819,7 @@ impl<'tcx> Printer<'tcx> for V0SymbolMangler<'tcx> { Ok(()) } - fn path_crate(&mut self, cnum: CrateNum) -> Result<(), PrintError> { + fn print_crate_name(&mut self, cnum: CrateNum) -> Result<(), PrintError> { self.push("C"); if !self.is_exportable { let stable_crate_id = self.tcx.def_path_hash(cnum.as_def_id()).stable_crate_id(); @@ -830,7 +830,7 @@ impl<'tcx> Printer<'tcx> for V0SymbolMangler<'tcx> { Ok(()) } - fn path_qualified( + fn print_path_with_qualified( &mut self, self_ty: Ty<'tcx>, trait_ref: Option>, @@ -843,7 +843,7 @@ impl<'tcx> Printer<'tcx> for V0SymbolMangler<'tcx> { self.print_def_path(trait_ref.def_id, trait_ref.args) } - fn path_append_impl( + fn print_path_with_impl( &mut self, _: impl FnOnce(&mut Self) -> Result<(), PrintError>, _: Ty<'tcx>, @@ -853,7 +853,7 @@ impl<'tcx> Printer<'tcx> for V0SymbolMangler<'tcx> { unreachable!() } - fn path_append( + fn print_path_with_simple( &mut self, print_prefix: impl FnOnce(&mut Self) -> Result<(), PrintError>, disambiguated_data: &DisambiguatedDefPathData, @@ -873,7 +873,7 @@ impl<'tcx> Printer<'tcx> for V0SymbolMangler<'tcx> { DefPathData::SyntheticCoroutineBody => 's', DefPathData::NestedStatic => 'n', - // These should never show up as `path_append` arguments. + // These should never show up as `print_path_with_simple` arguments. DefPathData::CrateRoot | DefPathData::Use | DefPathData::GlobalAsm @@ -896,7 +896,7 @@ impl<'tcx> Printer<'tcx> for V0SymbolMangler<'tcx> { ) } - fn path_generic_args( + fn print_path_with_generic_args( &mut self, print_prefix: impl FnOnce(&mut Self) -> Result<(), PrintError>, args: &[GenericArg<'tcx>], diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs index fd204412564..839a8007bf2 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs @@ -253,12 +253,12 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { unreachable!(); // because `path_generic_args` ignores the `GenericArgs` } - fn path_crate(&mut self, cnum: CrateNum) -> Result<(), PrintError> { + fn print_crate_name(&mut self, cnum: CrateNum) -> Result<(), PrintError> { self.segments = vec![self.tcx.crate_name(cnum)]; Ok(()) } - fn path_qualified( + fn print_path_with_qualified( &mut self, _self_ty: Ty<'tcx>, _trait_ref: Option>, @@ -266,7 +266,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { Err(fmt::Error) } - fn path_append_impl( + fn print_path_with_impl( &mut self, _print_prefix: impl FnOnce(&mut Self) -> Result<(), PrintError>, _self_ty: Ty<'tcx>, @@ -275,7 +275,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { Err(fmt::Error) } - fn path_append( + fn print_path_with_simple( &mut self, print_prefix: impl FnOnce(&mut Self) -> Result<(), PrintError>, disambiguated_data: &DisambiguatedDefPathData, @@ -285,7 +285,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { Ok(()) } - fn path_generic_args( + fn print_path_with_generic_args( &mut self, print_prefix: impl FnOnce(&mut Self) -> Result<(), PrintError>, _args: &[GenericArg<'tcx>], diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/note_and_explain.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/note_and_explain.rs index 129d0963a75..b9284c07d83 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/infer/note_and_explain.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/note_and_explain.rs @@ -947,7 +947,7 @@ fn foo(&self) -> Self::T { String::new() } pub fn format_generic_args(&self, args: &[ty::GenericArg<'tcx>]) -> String { FmtPrinter::print_string(self.tcx, hir::def::Namespace::TypeNS, |p| { - p.path_generic_args(|_| Ok(()), args) + p.print_path_with_generic_args(|_| Ok(()), args) }) .expect("could not write to `String`.") } -- cgit 1.4.1-3-g733a5 From 42a1042f9b915b48d1e7176802cb130d029b3b48 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Sun, 3 Aug 2025 21:14:01 +1000 Subject: Rename some `PrettyPrinter` methods. More consistency. --- compiler/rustc_const_eval/src/util/type_name.rs | 4 +- compiler/rustc_middle/src/mir/pretty.rs | 4 +- compiler/rustc_middle/src/ty/print/pretty.rs | 52 ++++++++++++------------- compiler/rustc_symbol_mangling/src/legacy.rs | 6 +-- 4 files changed, 33 insertions(+), 33 deletions(-) diff --git a/compiler/rustc_const_eval/src/util/type_name.rs b/compiler/rustc_const_eval/src/util/type_name.rs index 2f74f998fd0..d50507f65eb 100644 --- a/compiler/rustc_const_eval/src/util/type_name.rs +++ b/compiler/rustc_const_eval/src/util/type_name.rs @@ -83,7 +83,7 @@ impl<'tcx> Printer<'tcx> for TypeNamePrinter<'tcx> { self_ty: Ty<'tcx>, trait_ref: Option>, ) -> Result<(), PrintError> { - self.pretty_path_qualified(self_ty, trait_ref) + self.pretty_print_path_with_qualified(self_ty, trait_ref) } fn print_path_with_impl( @@ -92,7 +92,7 @@ impl<'tcx> Printer<'tcx> for TypeNamePrinter<'tcx> { self_ty: Ty<'tcx>, trait_ref: Option>, ) -> Result<(), PrintError> { - self.pretty_path_append_impl( + self.pretty_print_path_with_impl( |cx| { print_prefix(cx)?; diff --git a/compiler/rustc_middle/src/mir/pretty.rs b/compiler/rustc_middle/src/mir/pretty.rs index ed067d49127..a3949308eb4 100644 --- a/compiler/rustc_middle/src/mir/pretty.rs +++ b/compiler/rustc_middle/src/mir/pretty.rs @@ -1969,7 +1969,7 @@ fn pretty_print_const_value_tcx<'tcx>( let args = tcx.lift(args).unwrap(); let mut p = FmtPrinter::new(tcx, Namespace::ValueNS); p.print_alloc_ids = true; - p.print_value_path(variant_def.def_id, args)?; + p.pretty_print_value_path(variant_def.def_id, args)?; fmt.write_str(&p.into_buffer())?; match variant_def.ctor_kind() { @@ -2011,7 +2011,7 @@ fn pretty_print_const_value_tcx<'tcx>( (ConstValue::ZeroSized, ty::FnDef(d, s)) => { let mut p = FmtPrinter::new(tcx, Namespace::ValueNS); p.print_alloc_ids = true; - p.print_value_path(*d, s)?; + p.pretty_print_value_path(*d, s)?; fmt.write_str(&p.into_buffer())?; return Ok(()); } diff --git a/compiler/rustc_middle/src/ty/print/pretty.rs b/compiler/rustc_middle/src/ty/print/pretty.rs index a7ff1b51833..46876cef1d0 100644 --- a/compiler/rustc_middle/src/ty/print/pretty.rs +++ b/compiler/rustc_middle/src/ty/print/pretty.rs @@ -245,7 +245,7 @@ impl<'tcx> RegionHighlightMode<'tcx> { /// Trait for printers that pretty-print using `fmt::Write` to the printer. pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { /// Like `print_def_path` but for value paths. - fn print_value_path( + fn pretty_print_value_path( &mut self, def_id: DefId, args: &'tcx [GenericArg<'tcx>], @@ -253,7 +253,7 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { self.print_def_path(def_id, args) } - fn print_in_binder(&mut self, value: &ty::Binder<'tcx, T>) -> Result<(), PrintError> + fn pretty_print_in_binder(&mut self, value: &ty::Binder<'tcx, T>) -> Result<(), PrintError> where T: Print<'tcx, Self> + TypeFoldable>, { @@ -644,7 +644,7 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { Ok(true) } - fn pretty_path_qualified( + fn pretty_print_path_with_qualified( &mut self, self_ty: Ty<'tcx>, trait_ref: Option>, @@ -679,7 +679,7 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { }) } - fn pretty_path_append_impl( + fn pretty_print_path_with_impl( &mut self, print_prefix: impl FnOnce(&mut Self) -> Result<(), PrintError>, self_ty: Ty<'tcx>, @@ -746,7 +746,7 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { } sig.print(self)?; write!(self, " {{")?; - self.print_value_path(def_id, args)?; + self.pretty_print_value_path(def_id, args)?; write!(self, "}}")?; } } @@ -1393,7 +1393,7 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { if let ty::Tuple(tys) = principal.args.type_at(0).kind() { let mut projections = predicates.projection_bounds(); if let (Some(proj), None) = (projections.next(), projections.next()) { - p.pretty_fn_sig( + p.pretty_print_fn_sig( tys, false, proj.skip_binder().term.as_type().expect("Return type was a const"), @@ -1495,7 +1495,7 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { Ok(()) } - fn pretty_fn_sig( + fn pretty_print_fn_sig( &mut self, inputs: &[Ty<'tcx>], c_variadic: bool, @@ -1532,7 +1532,7 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { ty::ConstKind::Unevaluated(ty::UnevaluatedConst { def, args }) => { match self.tcx().def_kind(def) { DefKind::Const | DefKind::AssocConst => { - self.print_value_path(def, args)?; + self.pretty_print_value_path(def, args)?; } DefKind::AnonConst => { if def.is_local() @@ -1541,13 +1541,13 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { { write!(self, "{snip}")?; } else { - // Do not call `print_value_path` as if a parent of this anon const is - // an impl it will attempt to print out the impl trait ref i.e. `::{constant#0}`. This would cause printing to enter an - // infinite recursion if the anon const is in the self type i.e. - // `impl Default for [T; 32 - 1 - 1 - 1] {` where we would - // try to print - // `<[T; /* print constant#0 again */] as // Default>::{constant#0}`. + // Do not call `pretty_print_value_path` as if a parent of this anon + // const is an impl it will attempt to print out the impl trait ref + // i.e. `::{constant#0}`. This would cause printing to + // enter an infinite recursion if the anon const is in the self type + // i.e. `impl Default for [T; 32 - 1 - 1 - 1] {` where we + // would try to print `<[T; /* print constant#0 again */] as // + // Default>::{constant#0}`. write!( self, "{}::{}", @@ -1749,7 +1749,7 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { self.tcx().try_get_global_alloc(prov.alloc_id()) { self.typed_value( - |this| this.print_value_path(instance.def_id(), instance.args), + |this| this.pretty_print_value_path(instance.def_id(), instance.args), |this| this.print_type(ty), " as ", )?; @@ -1943,7 +1943,7 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { let variant_idx = contents.variant.expect("destructed const of adt without variant idx"); let variant_def = &def.variant(variant_idx); - self.print_value_path(variant_def.def_id, args)?; + self.pretty_print_value_path(variant_def.def_id, args)?; match variant_def.ctor_kind() { Some(CtorKind::Const) => {} Some(CtorKind::Fn) => { @@ -1979,7 +1979,7 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { } (_, ty::FnDef(def_id, args)) => { // Never allowed today, but we still encounter them in invalid const args. - self.print_value_path(def_id, args)?; + self.pretty_print_value_path(def_id, args)?; return Ok(()); } // FIXME(oli-obk): also pretty print arrays and other aggregate constants by reading @@ -2000,7 +2000,7 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { Ok(()) } - fn pretty_closure_as_impl( + fn pretty_print_closure_as_impl( &mut self, closure: ty::ClosureArgs>, ) -> Result<(), PrintError> { @@ -2335,7 +2335,7 @@ impl<'tcx> Printer<'tcx> for FmtPrinter<'_, 'tcx> { self_ty: Ty<'tcx>, trait_ref: Option>, ) -> Result<(), PrintError> { - self.pretty_path_qualified(self_ty, trait_ref)?; + self.pretty_print_path_with_qualified(self_ty, trait_ref)?; self.empty_path = false; Ok(()) } @@ -2346,7 +2346,7 @@ impl<'tcx> Printer<'tcx> for FmtPrinter<'_, 'tcx> { self_ty: Ty<'tcx>, trait_ref: Option>, ) -> Result<(), PrintError> { - self.pretty_path_append_impl( + self.pretty_print_path_with_impl( |p| { print_prefix(p)?; if !p.empty_path { @@ -2424,7 +2424,7 @@ impl<'tcx> PrettyPrinter<'tcx> for FmtPrinter<'_, 'tcx> { self.0.const_infer_name_resolver.as_ref().and_then(|func| func(id)) } - fn print_value_path( + fn pretty_print_value_path( &mut self, def_id: DefId, args: &'tcx [GenericArg<'tcx>], @@ -2436,7 +2436,7 @@ impl<'tcx> PrettyPrinter<'tcx> for FmtPrinter<'_, 'tcx> { Ok(()) } - fn print_in_binder(&mut self, value: &ty::Binder<'tcx, T>) -> Result<(), PrintError> + fn pretty_print_in_binder(&mut self, value: &ty::Binder<'tcx, T>) -> Result<(), PrintError> where T: Print<'tcx, Self> + TypeFoldable>, { @@ -2899,7 +2899,7 @@ where T: Print<'tcx, P> + TypeFoldable>, { fn print(&self, p: &mut P) -> Result<(), PrintError> { - p.print_in_binder(self) + p.pretty_print_in_binder(self) } } @@ -3097,7 +3097,7 @@ define_print! { } write!(p, "fn")?; - p.pretty_fn_sig(self.inputs(), self.c_variadic, self.output())?; + p.pretty_print_fn_sig(self.inputs(), self.c_variadic, self.output())?; } ty::TraitRef<'tcx> { @@ -3321,7 +3321,7 @@ define_print_and_forward_display! { } PrintClosureAsImpl<'tcx> { - p.pretty_closure_as_impl(self.closure)?; + p.pretty_print_closure_as_impl(self.closure)?; } ty::ParamTy { diff --git a/compiler/rustc_symbol_mangling/src/legacy.rs b/compiler/rustc_symbol_mangling/src/legacy.rs index 350546d4b66..923318ef303 100644 --- a/compiler/rustc_symbol_mangling/src/legacy.rs +++ b/compiler/rustc_symbol_mangling/src/legacy.rs @@ -313,7 +313,7 @@ impl<'tcx> Printer<'tcx> for LegacySymbolMangler<'tcx> { self_ty: Ty<'tcx>, trait_ref: Option>, ) -> Result<(), PrintError> { - // Similar to `pretty_path_qualified`, but for the other + // Similar to `pretty_print_path_with_qualified`, but for the other // types that are printed as paths (see `print_type` above). match self_ty.kind() { ty::FnDef(..) @@ -326,7 +326,7 @@ impl<'tcx> Printer<'tcx> for LegacySymbolMangler<'tcx> { self.print_type(self_ty) } - _ => self.pretty_path_qualified(self_ty, trait_ref), + _ => self.pretty_print_path_with_qualified(self_ty, trait_ref), } } @@ -336,7 +336,7 @@ impl<'tcx> Printer<'tcx> for LegacySymbolMangler<'tcx> { self_ty: Ty<'tcx>, trait_ref: Option>, ) -> Result<(), PrintError> { - self.pretty_path_append_impl( + self.pretty_print_path_with_impl( |cx| { print_prefix(cx)?; -- cgit 1.4.1-3-g733a5 From bcefc2ee977e38a4a5ecd0956e19b0968d33052b Mon Sep 17 00:00:00 2001 From: Amanda Stjerna Date: Wed, 6 Aug 2025 11:12:51 +0200 Subject: Add annotations to the graphviz region graph on region origins This adds - (ex) for regions whose origin is existential, - (p) for regoins whose origin is a placeholder, and - (p for ) if the originating placeholder is named. This has helped _my_ debugging and it doesn't create too bad clutter, I feel. The change is ridiculously small, but I turned it into a separate PR so we can bikeshed the format. --- compiler/rustc_borrowck/src/region_infer/graphviz.rs | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/compiler/rustc_borrowck/src/region_infer/graphviz.rs b/compiler/rustc_borrowck/src/region_infer/graphviz.rs index a3e29982e90..899dde9ecd4 100644 --- a/compiler/rustc_borrowck/src/region_infer/graphviz.rs +++ b/compiler/rustc_borrowck/src/region_infer/graphviz.rs @@ -41,7 +41,21 @@ fn render_region_vid<'tcx>( "".to_string() }; - format!("{:?}{universe_str}{external_name_str}", rvid) + let extra_info = match regioncx.region_definition(rvid).origin { + NllRegionVariableOrigin::FreeRegion => "".to_string(), + NllRegionVariableOrigin::Placeholder(p) => match p.bound.kind { + ty::BoundRegionKind::Named(def_id) => { + format!("(p for {})", tcx.item_name(def_id)) + } + ty::BoundRegionKind::ClosureEnv | ty::BoundRegionKind::Anon => "(p)".to_string(), + ty::BoundRegionKind::NamedAnon(_) => { + bug!("only used for pretty printing") + } + }, + NllRegionVariableOrigin::Existential { .. } => "(ex)".to_string(), + }; + + format!("{:?}{universe_str}{external_name_str}{extra_info}", rvid) } impl<'tcx> RegionInferenceContext<'tcx> { -- cgit 1.4.1-3-g733a5 From 2472d19625fa39ed635a211be397319d2b62cb24 Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Wed, 6 Aug 2025 13:38:26 +0000 Subject: Include allocator module in LLVM_passes timer --- compiler/rustc_codegen_ssa/src/back/write.rs | 7 +--- compiler/rustc_codegen_ssa/src/base.rs | 56 ++++++++++++++-------------- 2 files changed, 30 insertions(+), 33 deletions(-) diff --git a/compiler/rustc_codegen_ssa/src/back/write.rs b/compiler/rustc_codegen_ssa/src/back/write.rs index aa29afb7f5b..a2ae03a7611 100644 --- a/compiler/rustc_codegen_ssa/src/back/write.rs +++ b/compiler/rustc_codegen_ssa/src/back/write.rs @@ -110,7 +110,6 @@ pub struct ModuleConfig { pub lint_llvm_ir: bool, pub no_prepopulate_passes: bool, pub no_builtins: bool, - pub time_module: bool, pub vectorize_loop: bool, pub vectorize_slp: bool, pub merge_functions: bool, @@ -228,10 +227,6 @@ impl ModuleConfig { no_prepopulate_passes: sess.opts.cg.no_prepopulate_passes, no_builtins: no_builtins || sess.target.no_builtins, - // Exclude metadata and allocator modules from time_passes output, - // since they throw off the "LLVM passes" measurement. - time_module: if_regular!(true, false), - // Copy what clang does by turning on loop vectorization at O2 and // slp vectorization at O3. vectorize_loop: !sess.opts.cg.no_vectorize_loops @@ -1740,7 +1735,7 @@ fn spawn_work<'a, B: ExtraBackendMethods>( llvm_start_time: &mut Option>, work: WorkItem, ) { - if cgcx.config(work.module_kind()).time_module && llvm_start_time.is_none() { + if llvm_start_time.is_none() { *llvm_start_time = Some(cgcx.prof.verbose_generic_activity("LLVM_passes")); } diff --git a/compiler/rustc_codegen_ssa/src/base.rs b/compiler/rustc_codegen_ssa/src/base.rs index b4556ced0b3..10a25bb3caa 100644 --- a/compiler/rustc_codegen_ssa/src/base.rs +++ b/compiler/rustc_codegen_ssa/src/base.rs @@ -681,33 +681,6 @@ pub fn codegen_crate( let ongoing_codegen = start_async_codegen(backend.clone(), tcx, target_cpu, autodiff_items); - // Codegen an allocator shim, if necessary. - if let Some(kind) = allocator_kind_for_codegen(tcx) { - let llmod_id = - cgu_name_builder.build_cgu_name(LOCAL_CRATE, &["crate"], Some("allocator")).to_string(); - let module_llvm = tcx.sess.time("write_allocator_module", || { - backend.codegen_allocator( - tcx, - &llmod_id, - kind, - // If allocator_kind is Some then alloc_error_handler_kind must - // also be Some. - tcx.alloc_error_handler_kind(()).unwrap(), - ) - }); - - ongoing_codegen.wait_for_signal_to_codegen_item(); - ongoing_codegen.check_for_errors(tcx.sess); - - // These modules are generally cheap and won't throw off scheduling. - let cost = 0; - submit_codegened_module_to_llvm( - &ongoing_codegen.coordinator, - ModuleCodegen::new_allocator(llmod_id, module_llvm), - cost, - ); - } - // For better throughput during parallel processing by LLVM, we used to sort // CGUs largest to smallest. This would lead to better thread utilization // by, for example, preventing a large CGU from being processed last and @@ -823,6 +796,35 @@ pub fn codegen_crate( } } + // Codegen an allocator shim, if necessary. + // Do this last to ensure the LLVM_passes timer doesn't start while no CGUs have been codegened + // yet for the backend to optimize. + if let Some(kind) = allocator_kind_for_codegen(tcx) { + let llmod_id = + cgu_name_builder.build_cgu_name(LOCAL_CRATE, &["crate"], Some("allocator")).to_string(); + let module_llvm = tcx.sess.time("write_allocator_module", || { + backend.codegen_allocator( + tcx, + &llmod_id, + kind, + // If allocator_kind is Some then alloc_error_handler_kind must + // also be Some. + tcx.alloc_error_handler_kind(()).unwrap(), + ) + }); + + ongoing_codegen.wait_for_signal_to_codegen_item(); + ongoing_codegen.check_for_errors(tcx.sess); + + // These modules are generally cheap and won't throw off scheduling. + let cost = 0; + submit_codegened_module_to_llvm( + &ongoing_codegen.coordinator, + ModuleCodegen::new_allocator(llmod_id, module_llvm), + cost, + ); + } + ongoing_codegen.codegen_finished(tcx); // Since the main thread is sometimes blocked during codegen, we keep track -- cgit 1.4.1-3-g733a5 From ee38bc06ce68d3f426f191a012ab529eceecb700 Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Wed, 6 Aug 2025 14:03:35 +0000 Subject: Remove unused field from ModuleConfig --- compiler/rustc_codegen_ssa/src/back/write.rs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/compiler/rustc_codegen_ssa/src/back/write.rs b/compiler/rustc_codegen_ssa/src/back/write.rs index a2ae03a7611..17dcca75398 100644 --- a/compiler/rustc_codegen_ssa/src/back/write.rs +++ b/compiler/rustc_codegen_ssa/src/back/write.rs @@ -76,12 +76,9 @@ pub struct ModuleConfig { /// Names of additional optimization passes to run. pub passes: Vec, /// Some(level) to optimize at a certain level, or None to run - /// absolutely no optimizations (used for the metadata module). + /// absolutely no optimizations (used for the allocator module). pub opt_level: Option, - /// Some(level) to optimize binary size, or None to not affect program size. - pub opt_size: Option, - pub pgo_gen: SwitchWithOptPath, pub pgo_use: Option, pub pgo_sample_use: Option, @@ -170,7 +167,6 @@ impl ModuleConfig { passes: if_regular!(sess.opts.cg.passes.clone(), vec![]), opt_level: opt_level_and_size, - opt_size: opt_level_and_size, pgo_gen: if_regular!( sess.opts.cg.profile_generate.clone(), -- cgit 1.4.1-3-g733a5 From 61e8869ed9aa8bf50a0ea2447a43bf856479feaa Mon Sep 17 00:00:00 2001 From: Amanda Stjerna Date: Wed, 6 Aug 2025 16:35:20 +0200 Subject: Proposed format --- compiler/rustc_borrowck/src/region_infer/graphviz.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/compiler/rustc_borrowck/src/region_infer/graphviz.rs b/compiler/rustc_borrowck/src/region_infer/graphviz.rs index 899dde9ecd4..ae4efddea4a 100644 --- a/compiler/rustc_borrowck/src/region_infer/graphviz.rs +++ b/compiler/rustc_borrowck/src/region_infer/graphviz.rs @@ -45,14 +45,14 @@ fn render_region_vid<'tcx>( NllRegionVariableOrigin::FreeRegion => "".to_string(), NllRegionVariableOrigin::Placeholder(p) => match p.bound.kind { ty::BoundRegionKind::Named(def_id) => { - format!("(p for {})", tcx.item_name(def_id)) + format!(" (for<{}>)", tcx.item_name(def_id)) } - ty::BoundRegionKind::ClosureEnv | ty::BoundRegionKind::Anon => "(p)".to_string(), + ty::BoundRegionKind::ClosureEnv | ty::BoundRegionKind::Anon => " (for<'_>)".to_string(), ty::BoundRegionKind::NamedAnon(_) => { bug!("only used for pretty printing") } }, - NllRegionVariableOrigin::Existential { .. } => "(ex)".to_string(), + NllRegionVariableOrigin::Existential { .. } => " (ex<'?>)".to_string(), }; format!("{:?}{universe_str}{external_name_str}{extra_info}", rvid) -- cgit 1.4.1-3-g733a5 From a7e8fe73111a96871feadda1eefa2b3f616cdb8e Mon Sep 17 00:00:00 2001 From: ginnyTheCat Date: Wed, 6 Aug 2025 16:52:43 +0200 Subject: Clarify EOF handling for `BufRead::skip_until` --- library/std/src/io/mod.rs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/library/std/src/io/mod.rs b/library/std/src/io/mod.rs index d351ee5e739..ff0e29e04c2 100644 --- a/library/std/src/io/mod.rs +++ b/library/std/src/io/mod.rs @@ -2461,7 +2461,7 @@ pub trait BufRead: Read { /// delimiter or EOF is found. /// /// If successful, this function will return the total number of bytes read, - /// including the delimiter byte. + /// including the delimiter byte if found. /// /// This is useful for efficiently skipping data such as NUL-terminated strings /// in binary file formats without buffering. @@ -2489,7 +2489,7 @@ pub trait BufRead: Read { /// ``` /// use std::io::{self, BufRead}; /// - /// let mut cursor = io::Cursor::new(b"Ferris\0Likes long walks on the beach\0Crustacean\0"); + /// let mut cursor = io::Cursor::new(b"Ferris\0Likes long walks on the beach\0Crustacean\0!"); /// /// // read name /// let mut name = Vec::new(); @@ -2509,6 +2509,11 @@ pub trait BufRead: Read { /// .expect("reading from cursor won't fail"); /// assert_eq!(num_bytes, 11); /// assert_eq!(animal, b"Crustacean\0"); + /// + /// // reach EOF + /// let num_bytes = cursor.skip_until(b'\0') + /// .expect("reading from cursor won't fail"); + /// assert_eq!(num_bytes, 1); /// ``` #[stable(feature = "bufread_skip_until", since = "1.83.0")] fn skip_until(&mut self, byte: u8) -> Result { -- cgit 1.4.1-3-g733a5 From 648e3fc3938b4b5127e7591187ce523e660ade44 Mon Sep 17 00:00:00 2001 From: Amanda Stjerna Date: Wed, 6 Aug 2025 16:58:42 +0200 Subject: Track names of existentials --- compiler/rustc_borrowck/src/handle_placeholders.rs | 2 +- compiler/rustc_borrowck/src/region_infer/graphviz.rs | 3 ++- compiler/rustc_borrowck/src/region_infer/mod.rs | 4 ++-- compiler/rustc_borrowck/src/renumber.rs | 2 +- compiler/rustc_borrowck/src/type_check/relate_tys.rs | 2 +- compiler/rustc_infer/src/infer/mod.rs | 1 + 6 files changed, 8 insertions(+), 6 deletions(-) diff --git a/compiler/rustc_borrowck/src/handle_placeholders.rs b/compiler/rustc_borrowck/src/handle_placeholders.rs index aaaf2f45c86..5eec121b8a7 100644 --- a/compiler/rustc_borrowck/src/handle_placeholders.rs +++ b/compiler/rustc_borrowck/src/handle_placeholders.rs @@ -157,7 +157,7 @@ fn region_definitions<'tcx>( for info in var_infos.iter() { let origin = match info.origin { RegionVariableOrigin::Nll(origin) => origin, - _ => NllRegionVariableOrigin::Existential { from_forall: false }, + _ => NllRegionVariableOrigin::Existential { from_forall: false, name: None }, }; let definition = RegionDefinition { origin, universe: info.universe, external_name: None }; diff --git a/compiler/rustc_borrowck/src/region_infer/graphviz.rs b/compiler/rustc_borrowck/src/region_infer/graphviz.rs index ae4efddea4a..b2f67c43125 100644 --- a/compiler/rustc_borrowck/src/region_infer/graphviz.rs +++ b/compiler/rustc_borrowck/src/region_infer/graphviz.rs @@ -52,7 +52,8 @@ fn render_region_vid<'tcx>( bug!("only used for pretty printing") } }, - NllRegionVariableOrigin::Existential { .. } => " (ex<'?>)".to_string(), + NllRegionVariableOrigin::Existential { name: Some(name), .. } => format!(" (ex<{name}>)"), + NllRegionVariableOrigin::Existential { .. } => format!(" (ex<'_>)"), }; format!("{:?}{universe_str}{external_name_str}{extra_info}", rvid) diff --git a/compiler/rustc_borrowck/src/region_infer/mod.rs b/compiler/rustc_borrowck/src/region_infer/mod.rs index f1320048533..6ac908fd04c 100644 --- a/compiler/rustc_borrowck/src/region_infer/mod.rs +++ b/compiler/rustc_borrowck/src/region_infer/mod.rs @@ -1940,9 +1940,9 @@ impl<'tcx> RegionInferenceContext<'tcx> { // and here we prefer to blame the source (the y = x statement). let blame_source = match from_region_origin { NllRegionVariableOrigin::FreeRegion - | NllRegionVariableOrigin::Existential { from_forall: false } => true, + | NllRegionVariableOrigin::Existential { from_forall: false, name: _ } => true, NllRegionVariableOrigin::Placeholder(_) - | NllRegionVariableOrigin::Existential { from_forall: true } => false, + | NllRegionVariableOrigin::Existential { from_forall: true, name: _ } => false, }; // To pick a constraint to blame, we organize constraints by how interesting we expect them diff --git a/compiler/rustc_borrowck/src/renumber.rs b/compiler/rustc_borrowck/src/renumber.rs index ff92b4168a8..100d30704b9 100644 --- a/compiler/rustc_borrowck/src/renumber.rs +++ b/compiler/rustc_borrowck/src/renumber.rs @@ -66,7 +66,7 @@ impl<'a, 'tcx> RegionRenumberer<'a, 'tcx> { T: TypeFoldable>, F: Fn() -> RegionCtxt, { - let origin = NllRegionVariableOrigin::Existential { from_forall: false }; + let origin = NllRegionVariableOrigin::Existential { from_forall: false, name: None }; fold_regions(self.infcx.tcx, value, |_region, _depth| { self.infcx.next_nll_region_var(origin, || region_ctxt_fn()) }) diff --git a/compiler/rustc_borrowck/src/type_check/relate_tys.rs b/compiler/rustc_borrowck/src/type_check/relate_tys.rs index bb72d1d52f3..393adafea0b 100644 --- a/compiler/rustc_borrowck/src/type_check/relate_tys.rs +++ b/compiler/rustc_borrowck/src/type_check/relate_tys.rs @@ -249,7 +249,7 @@ impl<'a, 'b, 'tcx> NllTypeRelating<'a, 'b, 'tcx> { from_forall: bool, name: Option, ) -> ty::Region<'tcx> { - let origin = NllRegionVariableOrigin::Existential { from_forall }; + let origin = NllRegionVariableOrigin::Existential { name, from_forall }; let reg_var = self.type_checker.infcx.next_nll_region_var(origin, || RegionCtxt::Existential(name)); diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs index 7379df7c7c7..a2afdc45fa8 100644 --- a/compiler/rustc_infer/src/infer/mod.rs +++ b/compiler/rustc_infer/src/infer/mod.rs @@ -484,6 +484,7 @@ pub enum NllRegionVariableOrigin { Placeholder(ty::PlaceholderRegion), Existential { + name: Option, /// If this is true, then this variable was created to represent a lifetime /// bound in a `for` binder. For example, it might have been created to /// represent the lifetime `'a` in a type like `for<'a> fn(&'a u32)`. -- cgit 1.4.1-3-g733a5 From b0619327ef6afb89492b2fe0bd86f987c39f6c25 Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Fri, 18 Jul 2025 14:16:08 +0000 Subject: Change adjust_for_rust_scalar into arg_attrs_for_rust_scalar Directly creating the ArgAttributes rather than adjusting one is a bit clearer. --- compiler/rustc_ty_utils/src/abi.rs | 25 +++++++++---------------- 1 file changed, 9 insertions(+), 16 deletions(-) diff --git a/compiler/rustc_ty_utils/src/abi.rs b/compiler/rustc_ty_utils/src/abi.rs index af2e000e340..a0b33371835 100644 --- a/compiler/rustc_ty_utils/src/abi.rs +++ b/compiler/rustc_ty_utils/src/abi.rs @@ -268,20 +268,21 @@ fn fn_abi_of_instance<'tcx>( } // Handle safe Rust thin and wide pointers. -fn adjust_for_rust_scalar<'tcx>( +fn arg_attrs_for_rust_scalar<'tcx>( cx: LayoutCx<'tcx>, - attrs: &mut ArgAttributes, scalar: Scalar, layout: TyAndLayout<'tcx>, offset: Size, is_return: bool, drop_target_pointee: Option>, -) { +) -> ArgAttributes { + let mut attrs = ArgAttributes::new(); + // Booleans are always a noundef i1 that needs to be zero-extended. if scalar.is_bool() { attrs.ext(ArgExtension::Zext); attrs.set(ArgAttribute::NoUndef); - return; + return attrs; } if !scalar.is_uninit_valid() { @@ -289,7 +290,7 @@ fn adjust_for_rust_scalar<'tcx>( } // Only pointer types handled below. - let Scalar::Initialized { value: Pointer(_), valid_range } = scalar else { return }; + let Scalar::Initialized { value: Pointer(_), valid_range } = scalar else { return attrs }; // Set `nonnull` if the validity range excludes zero, or for the argument to `drop_in_place`, // which must be nonnull per its documented safety requirements. @@ -358,6 +359,8 @@ fn adjust_for_rust_scalar<'tcx>( } } } + + attrs } /// Ensure that the ABI makes basic sense. @@ -530,17 +533,7 @@ fn fn_abi_new_uncached<'tcx>( }; let mut arg = ArgAbi::new(cx, layout, |layout, scalar, offset| { - let mut attrs = ArgAttributes::new(); - adjust_for_rust_scalar( - *cx, - &mut attrs, - scalar, - *layout, - offset, - is_return, - drop_target_pointee, - ); - attrs + arg_attrs_for_rust_scalar(*cx, scalar, *layout, offset, is_return, drop_target_pointee) }); if arg.layout.is_zst() { -- cgit 1.4.1-3-g733a5 From e2acc6c5f4474b3610ce9f1cde84ecb854a354a7 Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Fri, 18 Jul 2025 14:25:56 +0000 Subject: Avoid using unadjusted ABI for the thread-local shim This restricts the uses of the unadjusted ABI to LLVM intrinsics. The Rust ABI works fine for the thread-local shim as it always returns pointers directly like the backend expects. --- compiler/rustc_ty_utils/src/abi.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/rustc_ty_utils/src/abi.rs b/compiler/rustc_ty_utils/src/abi.rs index a0b33371835..439d18a69d4 100644 --- a/compiler/rustc_ty_utils/src/abi.rs +++ b/compiler/rustc_ty_utils/src/abi.rs @@ -39,7 +39,7 @@ fn fn_sig_for_fn_abi<'tcx>( tcx.thread_local_ptr_ty(instance.def_id()), false, hir::Safety::Safe, - rustc_abi::ExternAbi::Unadjusted, + rustc_abi::ExternAbi::Rust, ); } -- cgit 1.4.1-3-g733a5 From 1e271d6ed13756db08160daaa327b99b7fe6099e Mon Sep 17 00:00:00 2001 From: Sasha Pourcelot Date: Tue, 5 Aug 2025 00:11:01 +0200 Subject: Allow function argument mismatch suggestion to be multiline --- compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs | 50 +++++++++++++++++++--- tests/ui/argument-suggestions/issue-100478.stderr | 16 ++++--- .../ui/fn/fn-arg-count-mismatch-diagnostics.stderr | 12 ++---- 3 files changed, 58 insertions(+), 20 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs index 36abd7c8555..b80a2af3100 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs @@ -1589,26 +1589,64 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // e.g. `reuse HasSelf::method;` should suggest `reuse HasSelf::method($args);`. full_call_span.shrink_to_hi() }; + + // Controls how the arguments should be listed in the suggestion. + enum ArgumentsFormatting { + SingleLine, + Multiline { fallback_indent: String, brace_indent: String }, + } + let arguments_formatting = { + let mut provided_inputs = matched_inputs.iter().filter_map(|a| *a); + if let Some(brace_indent) = source_map.indentation_before(suggestion_span) + && let Some(first_idx) = provided_inputs.by_ref().next() + && let Some(last_idx) = provided_inputs.by_ref().next() + && let (_, first_span) = provided_arg_tys[first_idx] + && let (_, last_span) = provided_arg_tys[last_idx] + && source_map.is_multiline(first_span.to(last_span)) + && let Some(fallback_indent) = source_map.indentation_before(first_span) + { + ArgumentsFormatting::Multiline { fallback_indent, brace_indent } + } else { + ArgumentsFormatting::SingleLine + } + }; + let mut suggestion = "(".to_owned(); let mut needs_comma = false; for (expected_idx, provided_idx) in matched_inputs.iter_enumerated() { if needs_comma { - suggestion += ", "; - } else { - needs_comma = true; + suggestion += ","; + } + match &arguments_formatting { + ArgumentsFormatting::SingleLine if needs_comma => suggestion += " ", + ArgumentsFormatting::SingleLine => {} + ArgumentsFormatting::Multiline { .. } => suggestion += "\n", } - let suggestion_text = if let Some(provided_idx) = provided_idx + needs_comma = true; + let (suggestion_span, suggestion_text) = if let Some(provided_idx) = provided_idx && let (_, provided_span) = provided_arg_tys[*provided_idx] && let Ok(arg_text) = source_map.span_to_snippet(provided_span) { - arg_text + (Some(provided_span), arg_text) } else { // Propose a placeholder of the correct type let (_, expected_ty) = formal_and_expected_inputs[expected_idx]; - ty_to_snippet(expected_ty, expected_idx) + (None, ty_to_snippet(expected_ty, expected_idx)) }; + if let ArgumentsFormatting::Multiline { fallback_indent, .. } = + &arguments_formatting + { + let indent = suggestion_span + .and_then(|span| source_map.indentation_before(span)) + .unwrap_or_else(|| fallback_indent.clone()); + suggestion += &indent; + } suggestion += &suggestion_text; } + if let ArgumentsFormatting::Multiline { brace_indent, .. } = arguments_formatting { + suggestion += ",\n"; + suggestion += &brace_indent; + } suggestion += ")"; err.span_suggestion_verbose( suggestion_span, diff --git a/tests/ui/argument-suggestions/issue-100478.stderr b/tests/ui/argument-suggestions/issue-100478.stderr index 8889a0ab5df..81dbff2f000 100644 --- a/tests/ui/argument-suggestions/issue-100478.stderr +++ b/tests/ui/argument-suggestions/issue-100478.stderr @@ -75,12 +75,16 @@ LL | fn foo(p1: T1, p2: Arc, p3: T3, p4: Arc, p5: T5, p6: T6, p7: T7, p8 | ^^^ ----------- help: provide the argument | -LL - foo( -LL - -LL - p1, //p2, -LL - p3, p4, p5, p6, p7, p8, -LL - ); -LL + foo(p1, /* Arc */, p3, p4, p5, p6, p7, p8); +LL ~ foo( +LL + p1, +LL + /* Arc */, +LL + p3, +LL + p4, +LL + p5, +LL + p6, +LL + p7, +LL + p8, +LL ~ ); | error: aborting due to 4 previous errors diff --git a/tests/ui/fn/fn-arg-count-mismatch-diagnostics.stderr b/tests/ui/fn/fn-arg-count-mismatch-diagnostics.stderr index 494d9bf60a6..dda9b398a83 100644 --- a/tests/ui/fn/fn-arg-count-mismatch-diagnostics.stderr +++ b/tests/ui/fn/fn-arg-count-mismatch-diagnostics.stderr @@ -99,14 +99,10 @@ LL | fn function_with_lots_of_arguments(a: i32, b: char, c: i32, d: i32, e: i32, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ------- help: provide the argument | -LL - function_with_lots_of_arguments( -LL - variable_name, -LL - variable_name, -LL - variable_name, -LL - variable_name, -LL - variable_name, -LL - ); -LL + function_with_lots_of_arguments(variable_name, /* char */, variable_name, variable_name, variable_name, variable_name); +LL | function_with_lots_of_arguments( +LL | variable_name, +LL ~ /* char */, +LL ~ variable_name, | error: aborting due to 6 previous errors -- cgit 1.4.1-3-g733a5 From 82aac02c5267a427fdc5e8e1abbba98fc11c832c Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Wed, 6 Aug 2025 18:01:07 +0000 Subject: Revert "Preserve the .debug_gdb_scripts section" This reverts commit 868bdde25b030e0b71a29a5dbc04a891036e702e. --- src/debuginfo.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/debuginfo.rs b/src/debuginfo.rs index 4c0b6439523..4c8585192a1 100644 --- a/src/debuginfo.rs +++ b/src/debuginfo.rs @@ -254,8 +254,7 @@ impl<'gcc, 'tcx> DebugInfoCodegenMethods<'tcx> for CodegenCx<'gcc, 'tcx> { // TODO(antoyo): implement. } - fn debuginfo_finalize(&mut self) { - // TODO: emit section `.debug_gdb_scripts`. + fn debuginfo_finalize(&self) { self.context.set_debug_info(true) } -- cgit 1.4.1-3-g733a5 From 7d88f657e993a3ec394f0b29dea0e5008633a6ba Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Wed, 6 Aug 2025 18:31:58 +0000 Subject: Fix ICE --- compiler/rustc_ty_utils/src/abi.rs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/compiler/rustc_ty_utils/src/abi.rs b/compiler/rustc_ty_utils/src/abi.rs index 439d18a69d4..89d1dd8cf23 100644 --- a/compiler/rustc_ty_utils/src/abi.rs +++ b/compiler/rustc_ty_utils/src/abi.rs @@ -474,9 +474,10 @@ fn fn_abi_new_uncached<'tcx>( let (caller_location, determined_fn_def_id, is_virtual_call) = if let Some(instance) = instance { let is_virtual_call = matches!(instance.def, ty::InstanceKind::Virtual(..)); + let is_tls_shim_call = matches!(instance.def, ty::InstanceKind::ThreadLocalShim(_)); ( instance.def.requires_caller_location(tcx).then(|| tcx.caller_location_ty()), - if is_virtual_call { None } else { Some(instance.def_id()) }, + if is_virtual_call || is_tls_shim_call { None } else { Some(instance.def_id()) }, is_virtual_call, ) } else { @@ -556,6 +557,7 @@ fn fn_abi_new_uncached<'tcx>( c_variadic: sig.c_variadic, fixed_count: inputs.len() as u32, conv, + // FIXME return false for tls shim can_unwind: fn_can_unwind( tcx, // Since `#[rustc_nounwind]` can change unwinding, we cannot infer unwinding by `fn_def_id` for a virtual call. @@ -568,8 +570,9 @@ fn fn_abi_new_uncached<'tcx>( &mut fn_abi, sig.abi, // If this is a virtual call, we cannot pass the `fn_def_id`, as it might call other - // functions from vtable. Internally, `deduced_param_attrs` attempts to infer attributes by - // visit the function body. + // functions from vtable. And for a tls shim, passing the `fn_def_id` would refer to + // the underlying static. Internally, `deduced_param_attrs` attempts to infer attributes + // by visit the function body. determined_fn_def_id, ); debug!("fn_abi_new_uncached = {:?}", fn_abi); -- cgit 1.4.1-3-g733a5 From 0bba9bd55c60a108c89cb32971ca2ebbf7386ba7 Mon Sep 17 00:00:00 2001 From: Paul Murphy Date: Wed, 6 Aug 2025 13:37:27 -0500 Subject: Explicitly disable vector feature on s390x baseline of bad-reg test If the baseline s390x cpu is changed to a newer variant, such as z13, the vector feature may be enabled by default. When rust is packaged on fedora 38 and newer, it is set to z13. Explicitly disable vector support on the baseline test for consistent results across s390x cpus. --- tests/ui/asm/s390x/bad-reg.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/ui/asm/s390x/bad-reg.rs b/tests/ui/asm/s390x/bad-reg.rs index 56b2d709372..eb9138755e7 100644 --- a/tests/ui/asm/s390x/bad-reg.rs +++ b/tests/ui/asm/s390x/bad-reg.rs @@ -1,7 +1,7 @@ //@ add-core-stubs //@ needs-asm-support //@ revisions: s390x s390x_vector s390x_vector_stable -//@[s390x] compile-flags: --target s390x-unknown-linux-gnu +//@[s390x] compile-flags: --target s390x-unknown-linux-gnu -C target-feature=-vector //@[s390x] needs-llvm-components: systemz //@[s390x_vector] compile-flags: --target s390x-unknown-linux-gnu -C target-feature=+vector //@[s390x_vector] needs-llvm-components: systemz -- cgit 1.4.1-3-g733a5 From bf5020937f17fd4c2f8ae5e7703fd9b8d15f0502 Mon Sep 17 00:00:00 2001 From: Karl Meakin Date: Thu, 7 Aug 2025 00:05:22 +0100 Subject: Optimize `char::is_alphanumeric` Avoid an unnecessary call to `unicode::Alphabetic` when `self` is an ASCII digit (ie `0..=9`). --- library/core/src/char/methods.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/library/core/src/char/methods.rs b/library/core/src/char/methods.rs index b0752a85faf..7ee0962721f 100644 --- a/library/core/src/char/methods.rs +++ b/library/core/src/char/methods.rs @@ -920,7 +920,11 @@ impl char { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn is_alphanumeric(self) -> bool { - self.is_alphabetic() || self.is_numeric() + if self.is_ascii() { + self.is_ascii_alphanumeric() + } else { + unicode::Alphabetic(self) || unicode::N(self) + } } /// Returns `true` if this `char` has the general category for control codes. -- cgit 1.4.1-3-g733a5 From cece1b95defd25f29f17da2483b5b2b79b829105 Mon Sep 17 00:00:00 2001 From: Samuel Tardieu Date: Wed, 6 Aug 2025 19:54:58 +0200 Subject: Reuse previous `Vec` allocation in loop --- clippy_lints/src/doc/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/doc/mod.rs b/clippy_lints/src/doc/mod.rs index d27d68d3866..eca3bc390d7 100644 --- a/clippy_lints/src/doc/mod.rs +++ b/clippy_lints/src/doc/mod.rs @@ -1139,12 +1139,12 @@ fn check_doc<'a, Events: Iterator, Range in_footnote_definition = true, End(TagEnd::FootnoteDefinition) => in_footnote_definition = false, -- cgit 1.4.1-3-g733a5 From 186ea6e1e6cbd0edf35ae6aca4abf441856f168f Mon Sep 17 00:00:00 2001 From: Jakub Beránek Date: Thu, 7 Aug 2025 10:41:43 +0200 Subject: Only run the pull workflow once per week --- src/doc/rustc-dev-guide/.github/workflows/rustc-pull.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/doc/rustc-dev-guide/.github/workflows/rustc-pull.yml b/src/doc/rustc-dev-guide/.github/workflows/rustc-pull.yml index 04d6469aeaa..5ff3118960d 100644 --- a/src/doc/rustc-dev-guide/.github/workflows/rustc-pull.yml +++ b/src/doc/rustc-dev-guide/.github/workflows/rustc-pull.yml @@ -3,8 +3,8 @@ name: rustc-pull on: workflow_dispatch: schedule: - # Run at 04:00 UTC every Monday and Thursday - - cron: '0 4 * * 1,4' + # Run at 04:00 UTC every Monday + - cron: '0 4 * * 1' jobs: pull: -- cgit 1.4.1-3-g733a5 From 064d5886544de3f28b0804fb15654ea49eb6b9c7 Mon Sep 17 00:00:00 2001 From: lcnr Date: Thu, 7 Aug 2025 14:20:19 +0200 Subject: move member-constraints tests --- .../min-choice-reject-ambiguous.rs | 43 ++++++++++++++ .../min-choice-reject-ambiguous.stderr | 48 ++++++++++++++++ .../ui/impl-trait/member-constraints/min-choice.rs | 34 +++++++++++ .../member-constraints/nested-impl-trait-fail.rs | 33 +++++++++++ .../nested-impl-trait-fail.stderr | 67 ++++++++++++++++++++++ .../member-constraints/nested-impl-trait-pass.rs | 29 ++++++++++ .../min-choice-reject-ambiguous.rs | 43 -------------- .../min-choice-reject-ambiguous.stderr | 48 ---------------- tests/ui/nll/member-constraints/min-choice.rs | 34 ----------- .../member-constraints/nested-impl-trait-fail.rs | 33 ----------- .../nested-impl-trait-fail.stderr | 67 ---------------------- .../member-constraints/nested-impl-trait-pass.rs | 29 ---------- 12 files changed, 254 insertions(+), 254 deletions(-) create mode 100644 tests/ui/impl-trait/member-constraints/min-choice-reject-ambiguous.rs create mode 100644 tests/ui/impl-trait/member-constraints/min-choice-reject-ambiguous.stderr create mode 100644 tests/ui/impl-trait/member-constraints/min-choice.rs create mode 100644 tests/ui/impl-trait/member-constraints/nested-impl-trait-fail.rs create mode 100644 tests/ui/impl-trait/member-constraints/nested-impl-trait-fail.stderr create mode 100644 tests/ui/impl-trait/member-constraints/nested-impl-trait-pass.rs delete mode 100644 tests/ui/nll/member-constraints/min-choice-reject-ambiguous.rs delete mode 100644 tests/ui/nll/member-constraints/min-choice-reject-ambiguous.stderr delete mode 100644 tests/ui/nll/member-constraints/min-choice.rs delete mode 100644 tests/ui/nll/member-constraints/nested-impl-trait-fail.rs delete mode 100644 tests/ui/nll/member-constraints/nested-impl-trait-fail.stderr delete mode 100644 tests/ui/nll/member-constraints/nested-impl-trait-pass.rs diff --git a/tests/ui/impl-trait/member-constraints/min-choice-reject-ambiguous.rs b/tests/ui/impl-trait/member-constraints/min-choice-reject-ambiguous.rs new file mode 100644 index 00000000000..09138095523 --- /dev/null +++ b/tests/ui/impl-trait/member-constraints/min-choice-reject-ambiguous.rs @@ -0,0 +1,43 @@ +// ... continued from ./min-choice.rs + +//@ check-fail + +trait Cap<'a> {} +impl Cap<'_> for T {} + +fn type_test<'a, T: 'a>() -> &'a u8 { &0 } + +// Make sure we don't pick `'b`. +fn test_b<'a, 'b, 'c, T>() -> impl Cap<'a> + Cap<'b> + Cap<'c> +where + 'a: 'b, + 'a: 'c, + T: 'b, +{ + type_test::<'_, T>() // This should pass if we pick 'b. + //~^ ERROR the parameter type `T` may not live long enough +} + +// Make sure we don't pick `'c`. +fn test_c<'a, 'b, 'c, T>() -> impl Cap<'a> + Cap<'b> + Cap<'c> +where + 'a: 'b, + 'a: 'c, + T: 'c, +{ + type_test::<'_, T>() // This should pass if we pick 'c. + //~^ ERROR the parameter type `T` may not live long enough +} + +// We need to pick min_choice from `['b, 'c]`, but it's ambiguous which one to pick because +// they're incomparable. +fn test_ambiguous<'a, 'b, 'c>(s: &'a u8) -> impl Cap<'b> + Cap<'c> +where + 'a: 'b, + 'a: 'c, +{ + s + //~^ ERROR captures lifetime that does not appear in bounds +} + +fn main() {} diff --git a/tests/ui/impl-trait/member-constraints/min-choice-reject-ambiguous.stderr b/tests/ui/impl-trait/member-constraints/min-choice-reject-ambiguous.stderr new file mode 100644 index 00000000000..911ddd3dc80 --- /dev/null +++ b/tests/ui/impl-trait/member-constraints/min-choice-reject-ambiguous.stderr @@ -0,0 +1,48 @@ +error[E0309]: the parameter type `T` may not live long enough + --> $DIR/min-choice-reject-ambiguous.rs:17:5 + | +LL | fn test_b<'a, 'b, 'c, T>() -> impl Cap<'a> + Cap<'b> + Cap<'c> + | -- the parameter type `T` must be valid for the lifetime `'a` as defined here... +... +LL | type_test::<'_, T>() // This should pass if we pick 'b. + | ^^^^^^^^^^^^^^^^^^ ...so that the type `T` will meet its required lifetime bounds + | +help: consider adding an explicit lifetime bound + | +LL | T: 'b + 'a, + | ++++ + +error[E0309]: the parameter type `T` may not live long enough + --> $DIR/min-choice-reject-ambiguous.rs:28:5 + | +LL | fn test_c<'a, 'b, 'c, T>() -> impl Cap<'a> + Cap<'b> + Cap<'c> + | -- the parameter type `T` must be valid for the lifetime `'a` as defined here... +... +LL | type_test::<'_, T>() // This should pass if we pick 'c. + | ^^^^^^^^^^^^^^^^^^ ...so that the type `T` will meet its required lifetime bounds + | +help: consider adding an explicit lifetime bound + | +LL | T: 'c + 'a, + | ++++ + +error[E0700]: hidden type for `impl Cap<'b> + Cap<'c>` captures lifetime that does not appear in bounds + --> $DIR/min-choice-reject-ambiguous.rs:39:5 + | +LL | fn test_ambiguous<'a, 'b, 'c>(s: &'a u8) -> impl Cap<'b> + Cap<'c> + | -- ---------------------- opaque type defined here + | | + | hidden type `&'a u8` captures the lifetime `'a` as defined here +... +LL | s + | ^ + | +help: add a `use<...>` bound to explicitly capture `'a` + | +LL | fn test_ambiguous<'a, 'b, 'c>(s: &'a u8) -> impl Cap<'b> + Cap<'c> + use<'b, 'c, 'a> + | +++++++++++++++++ + +error: aborting due to 3 previous errors + +Some errors have detailed explanations: E0309, E0700. +For more information about an error, try `rustc --explain E0309`. diff --git a/tests/ui/impl-trait/member-constraints/min-choice.rs b/tests/ui/impl-trait/member-constraints/min-choice.rs new file mode 100644 index 00000000000..4fbffeb4b2a --- /dev/null +++ b/tests/ui/impl-trait/member-constraints/min-choice.rs @@ -0,0 +1,34 @@ +// Assuming that the hidden type in these tests is `&'?15 u8`, +// we have a member constraint: `'?15 member ['static, 'a, 'b, 'c]`. +// +// Make sure we pick up the minimum non-ambiguous region among them. +// We will have to exclude `['b, 'c]` because they're incomparable, +// and then we should pick `'a` because we know `'static: 'a`. + +//@ check-pass + +trait Cap<'a> {} +impl Cap<'_> for T {} + +fn type_test<'a, T: 'a>() -> &'a u8 { &0 } + +// Basic test: make sure we don't bail out because 'b and 'c are incomparable. +fn basic<'a, 'b, 'c>() -> impl Cap<'a> + Cap<'b> + Cap<'c> +where + 'a: 'b, + 'a: 'c, +{ + &0 +} + +// Make sure we don't pick `'static`. +fn test_static<'a, 'b, 'c, T>() -> impl Cap<'a> + Cap<'b> + Cap<'c> +where + 'a: 'b, + 'a: 'c, + T: 'a, +{ + type_test::<'_, T>() // This will fail if we pick 'static +} + +fn main() {} diff --git a/tests/ui/impl-trait/member-constraints/nested-impl-trait-fail.rs b/tests/ui/impl-trait/member-constraints/nested-impl-trait-fail.rs new file mode 100644 index 00000000000..0bf32a2624f --- /dev/null +++ b/tests/ui/impl-trait/member-constraints/nested-impl-trait-fail.rs @@ -0,0 +1,33 @@ +// Nested impl-traits can impose different member constraints on the same region variable. + +//@ check-fail + +trait Cap<'a> {} +impl Cap<'_> for T {} + +// Assuming the hidden type is `[&'?15 u8; 1]`, we have two distinct member constraints: +// - '?15 member ['static, 'a, 'b] // from outer impl-trait +// - '?15 member ['static, 'a, 'b] // from inner impl-trait +// To satisfy both we can choose 'a or 'b, so it's a failure due to ambiguity. +fn fail_early_bound<'s, 'a, 'b>(a: &'s u8) -> impl IntoIterator + Cap<'b>> +where + 's: 'a, + 's: 'b, +{ + [a] + //~^ ERROR E0700 + //~| ERROR E0700 +} + +// Same as the above but with late-bound regions. +fn fail_late_bound<'s, 'a, 'b>( + a: &'s u8, + _: &'a &'s u8, + _: &'b &'s u8, +) -> impl IntoIterator + Cap<'b>> { + [a] + //~^ ERROR E0700 + //~| ERROR E0700 +} + +fn main() {} diff --git a/tests/ui/impl-trait/member-constraints/nested-impl-trait-fail.stderr b/tests/ui/impl-trait/member-constraints/nested-impl-trait-fail.stderr new file mode 100644 index 00000000000..1a0611e715e --- /dev/null +++ b/tests/ui/impl-trait/member-constraints/nested-impl-trait-fail.stderr @@ -0,0 +1,67 @@ +error[E0700]: hidden type for `impl IntoIterator + Cap<'b>>` captures lifetime that does not appear in bounds + --> $DIR/nested-impl-trait-fail.rs:17:5 + | +LL | fn fail_early_bound<'s, 'a, 'b>(a: &'s u8) -> impl IntoIterator + Cap<'b>> + | -- ------------------------------------------------ opaque type defined here + | | + | hidden type `[&'s u8; 1]` captures the lifetime `'s` as defined here +... +LL | [a] + | ^^^ + | +help: add a `use<...>` bound to explicitly capture `'s` + | +LL | fn fail_early_bound<'s, 'a, 'b>(a: &'s u8) -> impl IntoIterator + Cap<'b>> + use<'a, 'b, 's> + | +++++++++++++++++ + +error[E0700]: hidden type for `impl Cap<'a> + Cap<'b>` captures lifetime that does not appear in bounds + --> $DIR/nested-impl-trait-fail.rs:17:5 + | +LL | fn fail_early_bound<'s, 'a, 'b>(a: &'s u8) -> impl IntoIterator + Cap<'b>> + | -- ---------------------- opaque type defined here + | | + | hidden type `&'s u8` captures the lifetime `'s` as defined here +... +LL | [a] + | ^^^ + | +help: add a `use<...>` bound to explicitly capture `'s` + | +LL | fn fail_early_bound<'s, 'a, 'b>(a: &'s u8) -> impl IntoIterator + Cap<'b> + use<'a, 'b, 's>> + | +++++++++++++++++ + +error[E0700]: hidden type for `impl IntoIterator + Cap<'b>>` captures lifetime that does not appear in bounds + --> $DIR/nested-impl-trait-fail.rs:28:5 + | +LL | fn fail_late_bound<'s, 'a, 'b>( + | -- hidden type `[&'s u8; 1]` captures the lifetime `'s` as defined here +... +LL | ) -> impl IntoIterator + Cap<'b>> { + | ------------------------------------------------ opaque type defined here +LL | [a] + | ^^^ + | +help: add a `use<...>` bound to explicitly capture `'s` + | +LL | ) -> impl IntoIterator + Cap<'b>> + use<'a, 'b, 's> { + | +++++++++++++++++ + +error[E0700]: hidden type for `impl Cap<'a> + Cap<'b>` captures lifetime that does not appear in bounds + --> $DIR/nested-impl-trait-fail.rs:28:5 + | +LL | fn fail_late_bound<'s, 'a, 'b>( + | -- hidden type `&'s u8` captures the lifetime `'s` as defined here +... +LL | ) -> impl IntoIterator + Cap<'b>> { + | ---------------------- opaque type defined here +LL | [a] + | ^^^ + | +help: add a `use<...>` bound to explicitly capture `'s` + | +LL | ) -> impl IntoIterator + Cap<'b> + use<'a, 'b, 's>> { + | +++++++++++++++++ + +error: aborting due to 4 previous errors + +For more information about this error, try `rustc --explain E0700`. diff --git a/tests/ui/impl-trait/member-constraints/nested-impl-trait-pass.rs b/tests/ui/impl-trait/member-constraints/nested-impl-trait-pass.rs new file mode 100644 index 00000000000..4633ad68230 --- /dev/null +++ b/tests/ui/impl-trait/member-constraints/nested-impl-trait-pass.rs @@ -0,0 +1,29 @@ +// Nested impl-traits can impose different member constraints on the same region variable. + +//@ check-pass + +trait Cap<'a> {} +impl Cap<'_> for T {} + +// Assuming the hidden type is `[&'?15 u8; 1]`, we have two distinct member constraints: +// - '?15 member ['static, 'a, 'b] // from outer impl-trait +// - '?15 member ['static, 'a] // from inner impl-trait +// To satisfy both we can only choose 'a. +fn pass_early_bound<'s, 'a, 'b>(a: &'s u8) -> impl IntoIterator> + Cap<'b> +where + 's: 'a, + 's: 'b, +{ + [a] +} + +// Same as the above but with late-bound regions. +fn pass_late_bound<'s, 'a, 'b>( + a: &'s u8, + _: &'a &'s u8, + _: &'b &'s u8, +) -> impl IntoIterator> + Cap<'b> { + [a] +} + +fn main() {} diff --git a/tests/ui/nll/member-constraints/min-choice-reject-ambiguous.rs b/tests/ui/nll/member-constraints/min-choice-reject-ambiguous.rs deleted file mode 100644 index 09138095523..00000000000 --- a/tests/ui/nll/member-constraints/min-choice-reject-ambiguous.rs +++ /dev/null @@ -1,43 +0,0 @@ -// ... continued from ./min-choice.rs - -//@ check-fail - -trait Cap<'a> {} -impl Cap<'_> for T {} - -fn type_test<'a, T: 'a>() -> &'a u8 { &0 } - -// Make sure we don't pick `'b`. -fn test_b<'a, 'b, 'c, T>() -> impl Cap<'a> + Cap<'b> + Cap<'c> -where - 'a: 'b, - 'a: 'c, - T: 'b, -{ - type_test::<'_, T>() // This should pass if we pick 'b. - //~^ ERROR the parameter type `T` may not live long enough -} - -// Make sure we don't pick `'c`. -fn test_c<'a, 'b, 'c, T>() -> impl Cap<'a> + Cap<'b> + Cap<'c> -where - 'a: 'b, - 'a: 'c, - T: 'c, -{ - type_test::<'_, T>() // This should pass if we pick 'c. - //~^ ERROR the parameter type `T` may not live long enough -} - -// We need to pick min_choice from `['b, 'c]`, but it's ambiguous which one to pick because -// they're incomparable. -fn test_ambiguous<'a, 'b, 'c>(s: &'a u8) -> impl Cap<'b> + Cap<'c> -where - 'a: 'b, - 'a: 'c, -{ - s - //~^ ERROR captures lifetime that does not appear in bounds -} - -fn main() {} diff --git a/tests/ui/nll/member-constraints/min-choice-reject-ambiguous.stderr b/tests/ui/nll/member-constraints/min-choice-reject-ambiguous.stderr deleted file mode 100644 index 911ddd3dc80..00000000000 --- a/tests/ui/nll/member-constraints/min-choice-reject-ambiguous.stderr +++ /dev/null @@ -1,48 +0,0 @@ -error[E0309]: the parameter type `T` may not live long enough - --> $DIR/min-choice-reject-ambiguous.rs:17:5 - | -LL | fn test_b<'a, 'b, 'c, T>() -> impl Cap<'a> + Cap<'b> + Cap<'c> - | -- the parameter type `T` must be valid for the lifetime `'a` as defined here... -... -LL | type_test::<'_, T>() // This should pass if we pick 'b. - | ^^^^^^^^^^^^^^^^^^ ...so that the type `T` will meet its required lifetime bounds - | -help: consider adding an explicit lifetime bound - | -LL | T: 'b + 'a, - | ++++ - -error[E0309]: the parameter type `T` may not live long enough - --> $DIR/min-choice-reject-ambiguous.rs:28:5 - | -LL | fn test_c<'a, 'b, 'c, T>() -> impl Cap<'a> + Cap<'b> + Cap<'c> - | -- the parameter type `T` must be valid for the lifetime `'a` as defined here... -... -LL | type_test::<'_, T>() // This should pass if we pick 'c. - | ^^^^^^^^^^^^^^^^^^ ...so that the type `T` will meet its required lifetime bounds - | -help: consider adding an explicit lifetime bound - | -LL | T: 'c + 'a, - | ++++ - -error[E0700]: hidden type for `impl Cap<'b> + Cap<'c>` captures lifetime that does not appear in bounds - --> $DIR/min-choice-reject-ambiguous.rs:39:5 - | -LL | fn test_ambiguous<'a, 'b, 'c>(s: &'a u8) -> impl Cap<'b> + Cap<'c> - | -- ---------------------- opaque type defined here - | | - | hidden type `&'a u8` captures the lifetime `'a` as defined here -... -LL | s - | ^ - | -help: add a `use<...>` bound to explicitly capture `'a` - | -LL | fn test_ambiguous<'a, 'b, 'c>(s: &'a u8) -> impl Cap<'b> + Cap<'c> + use<'b, 'c, 'a> - | +++++++++++++++++ - -error: aborting due to 3 previous errors - -Some errors have detailed explanations: E0309, E0700. -For more information about an error, try `rustc --explain E0309`. diff --git a/tests/ui/nll/member-constraints/min-choice.rs b/tests/ui/nll/member-constraints/min-choice.rs deleted file mode 100644 index 4fbffeb4b2a..00000000000 --- a/tests/ui/nll/member-constraints/min-choice.rs +++ /dev/null @@ -1,34 +0,0 @@ -// Assuming that the hidden type in these tests is `&'?15 u8`, -// we have a member constraint: `'?15 member ['static, 'a, 'b, 'c]`. -// -// Make sure we pick up the minimum non-ambiguous region among them. -// We will have to exclude `['b, 'c]` because they're incomparable, -// and then we should pick `'a` because we know `'static: 'a`. - -//@ check-pass - -trait Cap<'a> {} -impl Cap<'_> for T {} - -fn type_test<'a, T: 'a>() -> &'a u8 { &0 } - -// Basic test: make sure we don't bail out because 'b and 'c are incomparable. -fn basic<'a, 'b, 'c>() -> impl Cap<'a> + Cap<'b> + Cap<'c> -where - 'a: 'b, - 'a: 'c, -{ - &0 -} - -// Make sure we don't pick `'static`. -fn test_static<'a, 'b, 'c, T>() -> impl Cap<'a> + Cap<'b> + Cap<'c> -where - 'a: 'b, - 'a: 'c, - T: 'a, -{ - type_test::<'_, T>() // This will fail if we pick 'static -} - -fn main() {} diff --git a/tests/ui/nll/member-constraints/nested-impl-trait-fail.rs b/tests/ui/nll/member-constraints/nested-impl-trait-fail.rs deleted file mode 100644 index 0bf32a2624f..00000000000 --- a/tests/ui/nll/member-constraints/nested-impl-trait-fail.rs +++ /dev/null @@ -1,33 +0,0 @@ -// Nested impl-traits can impose different member constraints on the same region variable. - -//@ check-fail - -trait Cap<'a> {} -impl Cap<'_> for T {} - -// Assuming the hidden type is `[&'?15 u8; 1]`, we have two distinct member constraints: -// - '?15 member ['static, 'a, 'b] // from outer impl-trait -// - '?15 member ['static, 'a, 'b] // from inner impl-trait -// To satisfy both we can choose 'a or 'b, so it's a failure due to ambiguity. -fn fail_early_bound<'s, 'a, 'b>(a: &'s u8) -> impl IntoIterator + Cap<'b>> -where - 's: 'a, - 's: 'b, -{ - [a] - //~^ ERROR E0700 - //~| ERROR E0700 -} - -// Same as the above but with late-bound regions. -fn fail_late_bound<'s, 'a, 'b>( - a: &'s u8, - _: &'a &'s u8, - _: &'b &'s u8, -) -> impl IntoIterator + Cap<'b>> { - [a] - //~^ ERROR E0700 - //~| ERROR E0700 -} - -fn main() {} diff --git a/tests/ui/nll/member-constraints/nested-impl-trait-fail.stderr b/tests/ui/nll/member-constraints/nested-impl-trait-fail.stderr deleted file mode 100644 index 1a0611e715e..00000000000 --- a/tests/ui/nll/member-constraints/nested-impl-trait-fail.stderr +++ /dev/null @@ -1,67 +0,0 @@ -error[E0700]: hidden type for `impl IntoIterator + Cap<'b>>` captures lifetime that does not appear in bounds - --> $DIR/nested-impl-trait-fail.rs:17:5 - | -LL | fn fail_early_bound<'s, 'a, 'b>(a: &'s u8) -> impl IntoIterator + Cap<'b>> - | -- ------------------------------------------------ opaque type defined here - | | - | hidden type `[&'s u8; 1]` captures the lifetime `'s` as defined here -... -LL | [a] - | ^^^ - | -help: add a `use<...>` bound to explicitly capture `'s` - | -LL | fn fail_early_bound<'s, 'a, 'b>(a: &'s u8) -> impl IntoIterator + Cap<'b>> + use<'a, 'b, 's> - | +++++++++++++++++ - -error[E0700]: hidden type for `impl Cap<'a> + Cap<'b>` captures lifetime that does not appear in bounds - --> $DIR/nested-impl-trait-fail.rs:17:5 - | -LL | fn fail_early_bound<'s, 'a, 'b>(a: &'s u8) -> impl IntoIterator + Cap<'b>> - | -- ---------------------- opaque type defined here - | | - | hidden type `&'s u8` captures the lifetime `'s` as defined here -... -LL | [a] - | ^^^ - | -help: add a `use<...>` bound to explicitly capture `'s` - | -LL | fn fail_early_bound<'s, 'a, 'b>(a: &'s u8) -> impl IntoIterator + Cap<'b> + use<'a, 'b, 's>> - | +++++++++++++++++ - -error[E0700]: hidden type for `impl IntoIterator + Cap<'b>>` captures lifetime that does not appear in bounds - --> $DIR/nested-impl-trait-fail.rs:28:5 - | -LL | fn fail_late_bound<'s, 'a, 'b>( - | -- hidden type `[&'s u8; 1]` captures the lifetime `'s` as defined here -... -LL | ) -> impl IntoIterator + Cap<'b>> { - | ------------------------------------------------ opaque type defined here -LL | [a] - | ^^^ - | -help: add a `use<...>` bound to explicitly capture `'s` - | -LL | ) -> impl IntoIterator + Cap<'b>> + use<'a, 'b, 's> { - | +++++++++++++++++ - -error[E0700]: hidden type for `impl Cap<'a> + Cap<'b>` captures lifetime that does not appear in bounds - --> $DIR/nested-impl-trait-fail.rs:28:5 - | -LL | fn fail_late_bound<'s, 'a, 'b>( - | -- hidden type `&'s u8` captures the lifetime `'s` as defined here -... -LL | ) -> impl IntoIterator + Cap<'b>> { - | ---------------------- opaque type defined here -LL | [a] - | ^^^ - | -help: add a `use<...>` bound to explicitly capture `'s` - | -LL | ) -> impl IntoIterator + Cap<'b> + use<'a, 'b, 's>> { - | +++++++++++++++++ - -error: aborting due to 4 previous errors - -For more information about this error, try `rustc --explain E0700`. diff --git a/tests/ui/nll/member-constraints/nested-impl-trait-pass.rs b/tests/ui/nll/member-constraints/nested-impl-trait-pass.rs deleted file mode 100644 index 4633ad68230..00000000000 --- a/tests/ui/nll/member-constraints/nested-impl-trait-pass.rs +++ /dev/null @@ -1,29 +0,0 @@ -// Nested impl-traits can impose different member constraints on the same region variable. - -//@ check-pass - -trait Cap<'a> {} -impl Cap<'_> for T {} - -// Assuming the hidden type is `[&'?15 u8; 1]`, we have two distinct member constraints: -// - '?15 member ['static, 'a, 'b] // from outer impl-trait -// - '?15 member ['static, 'a] // from inner impl-trait -// To satisfy both we can only choose 'a. -fn pass_early_bound<'s, 'a, 'b>(a: &'s u8) -> impl IntoIterator> + Cap<'b> -where - 's: 'a, - 's: 'b, -{ - [a] -} - -// Same as the above but with late-bound regions. -fn pass_late_bound<'s, 'a, 'b>( - a: &'s u8, - _: &'a &'s u8, - _: &'b &'s u8, -) -> impl IntoIterator> + Cap<'b> { - [a] -} - -fn main() {} -- cgit 1.4.1-3-g733a5 From 8441f95ec70a2ee5cde4d1b8c81203860e4f5bb9 Mon Sep 17 00:00:00 2001 From: lcnr Date: Thu, 7 Aug 2025 14:29:11 +0200 Subject: add opaque type member constraint tests --- .../apply_member_constraint-no-req-eq.rs | 24 +++++++++++++++ .../member-constraints/incomplete-constraint.rs | 21 +++++++++++++ .../member-constraints/nested-impl-trait-pass.rs | 5 +++- .../reject-choice-due-to-prev-constraint.rs | 34 ++++++++++++++++++++++ tests/ui/impl-trait/no-anonymize-regions.rs | 18 ++++++++++++ 5 files changed, 101 insertions(+), 1 deletion(-) create mode 100644 tests/ui/impl-trait/member-constraints/apply_member_constraint-no-req-eq.rs create mode 100644 tests/ui/impl-trait/member-constraints/incomplete-constraint.rs create mode 100644 tests/ui/impl-trait/member-constraints/reject-choice-due-to-prev-constraint.rs create mode 100644 tests/ui/impl-trait/no-anonymize-regions.rs diff --git a/tests/ui/impl-trait/member-constraints/apply_member_constraint-no-req-eq.rs b/tests/ui/impl-trait/member-constraints/apply_member_constraint-no-req-eq.rs new file mode 100644 index 00000000000..3aa52fe2644 --- /dev/null +++ b/tests/ui/impl-trait/member-constraints/apply_member_constraint-no-req-eq.rs @@ -0,0 +1,24 @@ +//@ check-pass +// FIXME(-Znext-solver): enable this test + +trait Id { + type This; +} +impl Id for T { + type This = T; +} + +// We have two member constraints here: +// +// - 'unconstrained member ['a, 'static] +// - 'unconstrained member ['static] +// +// Applying the first constraint results in `'unconstrained: 'a` +// while the second then adds `'unconstrained: 'static`. If applying +// member constraints were to require the member region equal to the +// choice region, applying the first constraint first and then the +// second would result in a `'a: 'static` requirement. +fn test<'a>() -> impl Id> + use<'a> { + &() +} +fn main() {} diff --git a/tests/ui/impl-trait/member-constraints/incomplete-constraint.rs b/tests/ui/impl-trait/member-constraints/incomplete-constraint.rs new file mode 100644 index 00000000000..92fd31516d7 --- /dev/null +++ b/tests/ui/impl-trait/member-constraints/incomplete-constraint.rs @@ -0,0 +1,21 @@ +//@ check-pass +// FIXME(-Znext-solver): enable this test + +// These functions currently does not normalize the opaque type but will do +// so in the future. At this point we've got a new use of the opaque will fully +// universal arguments but for which lifetimes in the hidden type are unconstrained. +// +// Applying the member constraints would then incompletely infer `'unconstrained` to `'static`. +fn new_defining_use R, T, R>(_: F) {} + +fn rpit1<'a, 'b: 'b>(x: &'b ()) -> impl Sized + use<'a, 'b> { + new_defining_use(rpit1::<'a, 'b>); + x +} + +struct Inv<'a, 'b>(*mut (&'a (), &'b ())); +fn rpit2<'a>(_: ()) -> impl Sized + use<'a> { + new_defining_use(rpit2::<'a>); + Inv::<'a, 'static>(std::ptr::null_mut()) +} +fn main() {} diff --git a/tests/ui/impl-trait/member-constraints/nested-impl-trait-pass.rs b/tests/ui/impl-trait/member-constraints/nested-impl-trait-pass.rs index 4633ad68230..6860fc5e6a5 100644 --- a/tests/ui/impl-trait/member-constraints/nested-impl-trait-pass.rs +++ b/tests/ui/impl-trait/member-constraints/nested-impl-trait-pass.rs @@ -1,6 +1,7 @@ // Nested impl-traits can impose different member constraints on the same region variable. //@ check-pass +// FIXME(-Znext-solver): enable this test trait Cap<'a> {} impl Cap<'_> for T {} @@ -8,7 +9,9 @@ impl Cap<'_> for T {} // Assuming the hidden type is `[&'?15 u8; 1]`, we have two distinct member constraints: // - '?15 member ['static, 'a, 'b] // from outer impl-trait // - '?15 member ['static, 'a] // from inner impl-trait -// To satisfy both we can only choose 'a. +// To satisfy both we can only choose 'a. Concretely, first member constraint requires ?15 +// to outlive at least 'b while the second requires ?15 to outlive 'a. As 'a outlives 'b we +// end up with 'a as the final member region. fn pass_early_bound<'s, 'a, 'b>(a: &'s u8) -> impl IntoIterator> + Cap<'b> where 's: 'a, diff --git a/tests/ui/impl-trait/member-constraints/reject-choice-due-to-prev-constraint.rs b/tests/ui/impl-trait/member-constraints/reject-choice-due-to-prev-constraint.rs new file mode 100644 index 00000000000..4018256b2c8 --- /dev/null +++ b/tests/ui/impl-trait/member-constraints/reject-choice-due-to-prev-constraint.rs @@ -0,0 +1,34 @@ +//@ revisions: current next +//@[next] compile-flags: -Znext-solver +//@ ignore-compare-mode-next-solver (explicit revisions) +//@ check-pass + +// We've got `'0 member ['a, 'b, 'static]` and `'1 member ['a, 'b, 'static]`. +// +// As '0 gets outlived by 'a - its "upper bound" - the only applicable choice +// region is 'a. +// +// '1 has to outlive 'b so the only applicabel choice regions are 'b and 'static. +// Considering this member constraint by itself would choose 'b as it is the +// smaller of the two regions. +// +// However, this is only the case when ignoring the member constraint on '0. +// After applying this constraint and requiring '0 to outlive 'a. As '1 outlives +// '0, the region 'b is no longer an applicable choice region for '1 as 'b does +// does not outlive 'a. We would therefore choose 'static. +// +// This means applying member constraints is order dependent. We handle this by +// first applying member constraints for regions 'x and then consider the resulting +// constraints when applying member constraints for regions 'y with 'y: 'x. +fn with_constraints<'r0, 'r1, 'a, 'b>() -> *mut (&'r0 (), &'r1 ()) +where + 'r1: 'r0, + 'a: 'r0, + 'r1: 'b, +{ + loop {} +} +fn foo<'a, 'b>() -> impl Sized + use<'a, 'b> { + with_constraints::<'_, '_, 'a, 'b>() +} +fn main() {} diff --git a/tests/ui/impl-trait/no-anonymize-regions.rs b/tests/ui/impl-trait/no-anonymize-regions.rs new file mode 100644 index 00000000000..4f7f7c0641c --- /dev/null +++ b/tests/ui/impl-trait/no-anonymize-regions.rs @@ -0,0 +1,18 @@ +//@ check-pass +// FIXME(-Znext-solver): enable this test + +// A regression test for an error in `redis` while working on #139587. +// +// We check for structural equality when adding defining uses of opaques. +// In this test one defining use had anonymized regions while the other +// one did not, causing an error. +struct W(T); +fn constrain R, T, R>(f: F) -> R { + loop {} +} +fn foo<'a>(x: for<'b> fn(&'b ())) -> impl Sized + use<'a> { + let mut r = constrain(foo::<'_>); + r = W(x); + r +} +fn main() {} -- cgit 1.4.1-3-g733a5 From 098d06b0300e7ec3d4174904c2d6a9d4444a30a2 Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Thu, 7 Aug 2025 13:13:04 +0000 Subject: Prevent name collisions with internal implementation details The implementation of the linkage attribute inside extern blocks defines symbols starting with _rust_extern_with_linkage_. If someone tries to also define this symbol you will get a symbol conflict or even an ICE. By adding an unpredictable component to the symbol name, this becomes less of an issue. --- src/consts.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/consts.rs b/src/consts.rs index c04c75e1b11..873f1f1951c 100644 --- a/src/consts.rs +++ b/src/consts.rs @@ -6,6 +6,7 @@ use rustc_codegen_ssa::traits::{ BaseTypeCodegenMethods, ConstCodegenMethods, StaticCodegenMethods, }; use rustc_hir::def::DefKind; +use rustc_hir::def_id::LOCAL_CRATE; use rustc_middle::middle::codegen_fn_attrs::{CodegenFnAttrFlags, CodegenFnAttrs}; use rustc_middle::mir::interpret::{ self, ConstAllocation, ErrorHandled, Scalar as InterpScalar, read_target_uint, @@ -384,8 +385,8 @@ fn check_and_apply_linkage<'gcc, 'tcx>( // linkage and there are no definitions), then // `extern_with_linkage_foo` will instead be initialized to // zero. - let mut real_name = "_rust_extern_with_linkage_".to_string(); - real_name.push_str(sym); + let real_name = + format!("_rust_extern_with_linkage_{:016x}_{sym}", cx.tcx.stable_crate_id(LOCAL_CRATE)); let global2 = cx.define_global(&real_name, gcc_type, is_tls, attrs.link_section); // TODO(antoyo): set linkage. let value = cx.const_ptrcast(global1.get_address(None), gcc_type); -- cgit 1.4.1-3-g733a5 From eb15cf0a30eec3166088d6a0c5a8fc6854571371 Mon Sep 17 00:00:00 2001 From: Philipp Krones Date: Thu, 7 Aug 2025 17:05:15 +0200 Subject: Merge commit '334fb906aef13d20050987b13448f37391bb97a2' into clippy-subtree-update --- .github/ISSUE_TEMPLATE/new_lint.yml | 21 ++ .github/driver.sh | 6 +- CHANGELOG.md | 101 +++++- CONTRIBUTING.md | 44 ++- Cargo.toml | 2 +- book/src/lint_configuration.md | 2 +- clippy_config/Cargo.toml | 2 +- clippy_config/src/conf.rs | 2 +- clippy_lints/Cargo.toml | 2 +- clippy_lints/src/approx_const.rs | 3 +- clippy_lints/src/attrs/duplicated_attributes.rs | 44 ++- clippy_lints/src/attrs/inline_always.rs | 3 +- clippy_lints/src/attrs/repr_attributes.rs | 3 +- clippy_lints/src/blocks_in_conditions.rs | 3 +- clippy_lints/src/booleans.rs | 3 +- clippy_lints/src/casts/cast_lossless.rs | 6 + clippy_lints/src/casts/mod.rs | 2 +- clippy_lints/src/cloned_ref_to_slice_refs.rs | 2 +- clippy_lints/src/coerce_container_to_any.rs | 2 +- clippy_lints/src/cognitive_complexity.rs | 2 +- clippy_lints/src/collapsible_if.rs | 53 ++- clippy_lints/src/copies.rs | 6 +- clippy_lints/src/declared_lints.rs | 2 +- clippy_lints/src/default_union_representation.rs | 3 +- clippy_lints/src/deprecated_lints.rs | 2 + clippy_lints/src/doc/broken_link.rs | 76 ++--- clippy_lints/src/doc/mod.rs | 2 +- clippy_lints/src/doc/suspicious_doc_comments.rs | 2 +- .../src/doc/too_long_first_doc_paragraph.rs | 2 +- clippy_lints/src/empty_with_brackets.rs | 4 +- clippy_lints/src/eta_reduction.rs | 5 +- clippy_lints/src/exhaustive_items.rs | 5 +- clippy_lints/src/floating_point_arithmetic.rs | 2 +- clippy_lints/src/format_args.rs | 7 +- clippy_lints/src/formatting.rs | 28 +- clippy_lints/src/four_forward_slashes.rs | 15 + clippy_lints/src/functions/must_use.rs | 2 +- clippy_lints/src/if_not_else.rs | 2 +- clippy_lints/src/incompatible_msrv.rs | 26 +- clippy_lints/src/index_refutable_slice.rs | 4 +- clippy_lints/src/infallible_try_from.rs | 6 +- clippy_lints/src/inline_fn_without_body.rs | 5 +- clippy_lints/src/let_with_type_underscore.rs | 11 +- clippy_lints/src/lib.rs | 6 +- clippy_lints/src/loops/never_loop.rs | 2 +- clippy_lints/src/macro_use.rs | 5 +- clippy_lints/src/manual_assert.rs | 16 +- clippy_lints/src/manual_async_fn.rs | 2 +- clippy_lints/src/manual_non_exhaustive.rs | 5 +- clippy_lints/src/matches/match_single_binding.rs | 246 +++++++++++--- clippy_lints/src/matches/single_match.rs | 8 +- clippy_lints/src/methods/implicit_clone.rs | 4 +- clippy_lints/src/methods/ip_constant.rs | 25 +- .../methods/iter_on_single_or_empty_collections.rs | 69 ++-- clippy_lints/src/methods/mod.rs | 2 +- clippy_lints/src/methods/unnecessary_map_or.rs | 14 +- clippy_lints/src/methods/unnecessary_sort_by.rs | 78 +++-- clippy_lints/src/methods/unnecessary_to_owned.rs | 4 +- clippy_lints/src/min_ident_chars.rs | 79 ++++- clippy_lints/src/missing_inline.rs | 5 +- clippy_lints/src/needless_bool.rs | 21 +- clippy_lints/src/needless_pass_by_value.rs | 4 +- clippy_lints/src/no_mangle_with_rust_abi.rs | 2 +- clippy_lints/src/non_std_lazy_statics.rs | 2 +- clippy_lints/src/option_if_let_else.rs | 9 +- clippy_lints/src/pass_by_ref_or_value.rs | 5 +- clippy_lints/src/redundant_else.rs | 2 +- clippy_lints/src/return_self_not_must_use.rs | 5 +- clippy_lints/src/semicolon_block.rs | 36 +- clippy_lints/src/std_instead_of_core.rs | 3 +- clippy_lints/src/strings.rs | 121 ------- clippy_lints/src/unit_types/let_unit_value.rs | 109 ++++-- clippy_lints/src/unit_types/mod.rs | 17 +- clippy_lints/src/utils/author.rs | 6 +- clippy_lints/src/wildcard_imports.rs | 2 +- .../src/derive_deserialize_allowing_unknown.rs | 5 +- clippy_test_deps/Cargo.lock | 7 - clippy_test_deps/Cargo.toml | 1 - clippy_utils/Cargo.toml | 2 +- clippy_utils/README.md | 2 +- clippy_utils/src/attrs.rs | 4 +- clippy_utils/src/lib.rs | 116 ++++--- clippy_utils/src/msrvs.rs | 2 +- clippy_utils/src/qualify_min_const_fn.rs | 2 +- clippy_utils/src/source.rs | 7 +- clippy_utils/src/sugg.rs | 127 ++++--- clippy_utils/src/sym.rs | 1 - clippy_utils/src/ty/mod.rs | 7 +- declare_clippy_lint/Cargo.toml | 2 +- lintcheck/src/input.rs | 2 +- lintcheck/src/json.rs | 2 +- lintcheck/src/output.rs | 2 +- rust-toolchain.toml | 2 +- tests/compile-test.rs | 11 +- tests/symbols-used.rs | 2 +- .../cargo_rust_version/fail_file_attr/Cargo.stderr | 2 +- tests/ui-cargo/duplicate_mod/fail/Cargo.stderr | 10 +- .../lint_groups_priority/fail/Cargo.stderr | 6 +- .../wildcard_imports/wildcard_imports.fixed | 11 + tests/ui-toml/wildcard_imports/wildcard_imports.rs | 11 + .../wildcard_imports/wildcard_imports.stderr | 6 +- tests/ui/cast_lossless_integer_unfixable.rs | 17 + tests/ui/collapsible_else_if.fixed | 22 ++ tests/ui/collapsible_else_if.rs | 24 ++ tests/ui/collapsible_else_if.stderr | 11 +- tests/ui/collapsible_if.fixed | 18 + tests/ui/collapsible_if.rs | 19 ++ tests/ui/collapsible_if.stderr | 20 +- tests/ui/deprecated.rs | 1 + tests/ui/deprecated.stderr | 18 +- tests/ui/doc/doc-fixable.fixed | 2 +- tests/ui/doc/doc-fixable.rs | 2 +- tests/ui/duplicated_attributes.rs | 2 +- tests/ui/duplicated_attributes.stderr | 23 +- tests/ui/empty_structs_with_brackets.fixed | 14 + tests/ui/empty_structs_with_brackets.rs | 14 + tests/ui/empty_structs_with_brackets.stderr | 10 +- tests/ui/four_forward_slashes_bare_cr.rs | 6 + tests/ui/four_forward_slashes_bare_cr.stderr | 14 + tests/ui/implicit_clone.fixed | 6 + tests/ui/implicit_clone.rs | 6 + tests/ui/implicit_clone.stderr | 14 +- .../slice_indexing_in_macro.fixed | 12 +- .../slice_indexing_in_macro.rs | 12 +- .../slice_indexing_in_macro.stderr | 11 +- tests/ui/indexing_slicing_slice.rs | 1 - tests/ui/indexing_slicing_slice.stderr | 38 +-- tests/ui/infallible_try_from.stderr | 4 +- tests/ui/ip_constant.fixed | 14 + tests/ui/ip_constant.rs | 14 + tests/ui/ip_constant.stderr | 80 ++++- tests/ui/iter_on_single_items.fixed | 24 ++ tests/ui/iter_on_single_items.rs | 24 ++ tests/ui/let_unit.fixed | 11 + tests/ui/let_unit.rs | 10 + tests/ui/let_unit.stderr | 16 +- tests/ui/let_with_type_underscore.fixed | 12 + tests/ui/let_with_type_underscore.rs | 12 + tests/ui/let_with_type_underscore.stderr | 50 ++- tests/ui/manual_assert.edition2018.stderr | 4 +- tests/ui/manual_assert.edition2021.stderr | 4 +- tests/ui/manual_strip.rs | 2 +- tests/ui/manual_unwrap_or_default.fixed | 2 +- tests/ui/manual_unwrap_or_default.rs | 2 +- tests/ui/map_identity.fixed | 64 +++- tests/ui/map_identity.rs | 64 +++- tests/ui/map_identity.stderr | 38 ++- tests/ui/match_single_binding.fixed | 62 ++++ tests/ui/match_single_binding.rs | 76 +++++ tests/ui/match_single_binding.stderr | 116 ++++++- tests/ui/min_ident_chars.rs | 49 +++ tests/ui/min_ident_chars.stderr | 80 ++++- tests/ui/needless_collect_indirect.rs | 3 +- tests/ui/needless_collect_indirect.stderr | 32 +- tests/ui/nonminimal_bool.rs | 18 + tests/ui/nonminimal_bool.stderr | 18 +- tests/ui/option_if_let_else.fixed | 12 + tests/ui/option_if_let_else.rs | 16 + tests/ui/option_if_let_else.stderr | 19 +- tests/ui/search_is_some.rs | 15 + tests/ui/search_is_some.stderr | 17 +- tests/ui/search_is_some_fixable_some.fixed | 7 + tests/ui/search_is_some_fixable_some.rs | 7 + tests/ui/search_is_some_fixable_some.stderr | 8 +- tests/ui/string_to_string.rs | 21 -- tests/ui/string_to_string.stderr | 28 -- tests/ui/string_to_string_in_map.fixed | 20 -- tests/ui/string_to_string_in_map.rs | 20 -- tests/ui/string_to_string_in_map.stderr | 38 --- tests/ui/suspicious_else_formatting.rs | 10 +- tests/ui/suspicious_else_formatting.stderr | 6 +- tests/ui/unnecessary_clippy_cfg.rs | 3 +- tests/ui/unnecessary_clippy_cfg.stderr | 46 +-- tests/ui/unnecessary_map_or.fixed | 20 ++ tests/ui/unnecessary_map_or.rs | 20 ++ tests/ui/unnecessary_map_or.stderr | 30 +- tests/ui/unnecessary_sort_by.stderr | 12 +- triagebot.toml | 1 - util/gh-pages/index_template.html | 37 +-- util/gh-pages/script.js | 13 +- util/gh-pages/style.css | 369 ++++++++++++++++++--- 181 files changed, 2802 insertions(+), 1026 deletions(-) create mode 100644 tests/ui/cast_lossless_integer_unfixable.rs create mode 100644 tests/ui/four_forward_slashes_bare_cr.rs create mode 100644 tests/ui/four_forward_slashes_bare_cr.stderr delete mode 100644 tests/ui/string_to_string.rs delete mode 100644 tests/ui/string_to_string.stderr delete mode 100644 tests/ui/string_to_string_in_map.fixed delete mode 100644 tests/ui/string_to_string_in_map.rs delete mode 100644 tests/ui/string_to_string_in_map.stderr diff --git a/.github/ISSUE_TEMPLATE/new_lint.yml b/.github/ISSUE_TEMPLATE/new_lint.yml index 464740640e0..a8202f6378f 100644 --- a/.github/ISSUE_TEMPLATE/new_lint.yml +++ b/.github/ISSUE_TEMPLATE/new_lint.yml @@ -48,3 +48,24 @@ body: ``` validations: required: true + - type: textarea + id: comparison + attributes: + label: Comparison with existing lints + description: | + What makes this lint different from any existing lints that are similar, and how are those differences useful? + + You can [use this playground template to see what existing lints are triggered by the bad code][playground] + (make sure to use "Tools > Clippy" and not "Build"). + You can also look through the list of [rustc's allowed-by-default lints][allowed-by-default], + as those won't show up in the playground above. + + [allowed-by-default]: https://doc.rust-lang.org/rustc/lints/listing/allowed-by-default.html + + [playground]: https://play.rust-lang.org/?version=nightly&mode=debug&edition=2024&code=%23%21%5Bwarn%28clippy%3A%3Apedantic%29%5D%0A%23%21%5Bwarn%28clippy%3A%3Anursery%29%5D%0A%23%21%5Bwarn%28clippy%3A%3Arestriction%29%5D%0A%23%21%5Bwarn%28clippy%3A%3Aall%29%5D%0A%23%21%5Ballow%28clippy%3A%3Ablanket_clippy_restriction_lints%2C+reason+%3D+%22testing+to+see+if+any+restriction+lints+match+given+code%22%29%5D%0A%0A%2F%2F%21+Template+that+can+be+used+to+see+what+clippy+lints+a+given+piece+of+code+would+trigger + placeholder: Unlike `clippy::...`, the proposed lint would... + - type: textarea + id: context + attributes: + label: Additional Context + description: Any additional context that you believe may be relevant. diff --git a/.github/driver.sh b/.github/driver.sh index 5a81b411291..2874aaf2110 100755 --- a/.github/driver.sh +++ b/.github/driver.sh @@ -47,9 +47,9 @@ unset CARGO_MANIFEST_DIR # Run a lint and make sure it produces the expected output. It's also expected to exit with code 1 # FIXME: How to match the clippy invocation in compile-test.rs? -./target/debug/clippy-driver -Dwarnings -Aunused -Zui-testing --emit metadata --crate-type bin tests/ui/string_to_string.rs 2>string_to_string.stderr && exit 1 -sed -e "/= help: for/d" string_to_string.stderr > normalized.stderr -diff -u normalized.stderr tests/ui/string_to_string.stderr +./target/debug/clippy-driver -Dwarnings -Aunused -Zui-testing --emit metadata --crate-type bin tests/ui/char_lit_as_u8.rs 2>char_lit_as_u8.stderr && exit 1 +sed -e "/= help: for/d" char_lit_as_u8.stderr > normalized.stderr +diff -u normalized.stderr tests/ui/char_lit_as_u8.stderr # make sure "clippy-driver --rustc --arg" and "rustc --arg" behave the same SYSROOT=$(rustc --print sysroot) diff --git a/CHANGELOG.md b/CHANGELOG.md index a92fbdc767b..bc60b1c57f7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,7 +6,105 @@ document. ## Unreleased / Beta / In Rust Nightly -[03a5b6b9...master](https://github.com/rust-lang/rust-clippy/compare/03a5b6b9...master) +[4ef75291...master](https://github.com/rust-lang/rust-clippy/compare/4ef75291...master) + +## Rust 1.89 + +Current stable, released 2025-08-07 + +[View all 137 merged pull requests](https://github.com/rust-lang/rust-clippy/pulls?q=merged%3A2025-05-01T16%3A52%3A57Z..2025-06-13T08%3A33%3A27Z+base%3Amaster) + +### New Lints + +* Added [`coerce_container_to_any`] to `nursery` [#14812](https://github.com/rust-lang/rust-clippy/pull/14812) +* Added [`ip_constant`] to `pedantic` [#14878](https://github.com/rust-lang/rust-clippy/pull/14878) +* Added [`infallible_try_from`] to `suspicious` [#14813](https://github.com/rust-lang/rust-clippy/pull/14813) +* Added [`doc_suspicious_footnotes`] to `suspicious` [#14708](https://github.com/rust-lang/rust-clippy/pull/14708) +* Added [`pointer_format`] to `restriction` [#14792](https://github.com/rust-lang/rust-clippy/pull/14792) +* Added [`useless_concat`] to `complexity` [#13829](https://github.com/rust-lang/rust-clippy/pull/13829) +* Added [`cloned_ref_to_slice_refs`] to `perf` [#14284](https://github.com/rust-lang/rust-clippy/pull/14284) +* Added [`confusing_method_to_numeric_cast`] to `suspicious` [#13979](https://github.com/rust-lang/rust-clippy/pull/13979) + +### Moves and Deprecations + +* Removed superseded lints: `transmute_float_to_int`, `transmute_int_to_char`, + `transmute_int_to_float`, `transmute_num_to_bytes` (now in rustc) + [#14703](https://github.com/rust-lang/rust-clippy/pull/14703) + +### Enhancements + +* [`module_name_repetitions`] added `allow_exact_repetitions` configuration option + [#14261](https://github.com/rust-lang/rust-clippy/pull/14261) +* [`missing_docs_in_private_items`] added `allow_unused` config for underscored fields + [#14453](https://github.com/rust-lang/rust-clippy/pull/14453) +* [`unnecessary_unwrap`] fixed being emitted twice in closure + [#14763](https://github.com/rust-lang/rust-clippy/pull/14763) +* [`needless_return`] lint span no longer wraps to previous line + [#14790](https://github.com/rust-lang/rust-clippy/pull/14790) +* [`trivial-copy-size-limit`] now defaults to `target_pointer_width` + [#13319](https://github.com/rust-lang/rust-clippy/pull/13319) +* [`integer_division`] fixed false negative for NonZero denominators + [#14664](https://github.com/rust-lang/rust-clippy/pull/14664) +* [`arbitrary_source_item_ordering`] no longer lints inside items with `#[repr]` attribute + [#14610](https://github.com/rust-lang/rust-clippy/pull/14610) +* [`excessive_precision`] no longer triggers on exponent with leading zeros + [#14824](https://github.com/rust-lang/rust-clippy/pull/14824) +* [`to_digit_is_some`] no longer lints in const contexts when MSRV is below 1.87 + [#14771](https://github.com/rust-lang/rust-clippy/pull/14771) + +### False Positive Fixes + +* [`std_instead_of_core`] fixed FP when part of the `use` cannot be replaced + [#15016](https://github.com/rust-lang/rust-clippy/pull/15016) +* [`unused_unit`] fixed FP for `Fn` bounds + [#14962](https://github.com/rust-lang/rust-clippy/pull/14962) +* [`unnecessary_debug_formatting`] fixed FP inside `Debug` impl + [#14955](https://github.com/rust-lang/rust-clippy/pull/14955) +* [`assign_op_pattern`] fixed FP on unstable const trait + [#14886](https://github.com/rust-lang/rust-clippy/pull/14886) +* [`useless_conversion`] fixed FP when using `.into_iter().any()` + [#14800](https://github.com/rust-lang/rust-clippy/pull/14800) +* [`collapsible_if`] fixed FP on block stmt before expr + [#14730](https://github.com/rust-lang/rust-clippy/pull/14730) +* [`manual_unwrap_or_default`] fixed FP on ref binding + [#14731](https://github.com/rust-lang/rust-clippy/pull/14731) +* [`unused_async`] fixed FP on default impl + [#14720](https://github.com/rust-lang/rust-clippy/pull/14720) +* [`manual_slice_fill`] fixed FP on `IndexMut` overload + [#14719](https://github.com/rust-lang/rust-clippy/pull/14719) +* [`unnecessary_to_owned`] fixed FP when map key is a reference + [#14834](https://github.com/rust-lang/rust-clippy/pull/14834) + +### ICE Fixes + +* [`mutable_key_type`] fixed ICE when infinitely associated generic types are used + [#14965](https://github.com/rust-lang/rust-clippy/pull/14965) +* [`zero_sized_map_values`] fixed ICE while computing type layout + [#14837](https://github.com/rust-lang/rust-clippy/pull/14837) +* [`useless_asref`] fixed ICE on trait method + [#14830](https://github.com/rust-lang/rust-clippy/pull/14830) +* [`manual_slice_size_calculation`] fixed ICE in suggestion and triggers in `const` context + [#14804](https://github.com/rust-lang/rust-clippy/pull/14804) +* [`missing_const_for_fn`]: fix ICE with some compilation options + [#14776](https://github.com/rust-lang/rust-clippy/pull/14776) + +### Documentation Improvements + +* [`manual_contains`] improved documentation wording + [#14917](https://github.com/rust-lang/rust-clippy/pull/14917) + +### Performance improvements + +* [`strlen_on_c_strings`] optimized by 99.75% (31M → 76k instructions) + [#15043](https://github.com/rust-lang/rust-clippy/pull/15043) +* Reduced documentation lints execution time by 85% (7.5% → 1% of total runtime) + [#14870](https://github.com/rust-lang/rust-clippy/pull/14870) +* [`unit_return_expecting_ord`] optimized to reduce binder instantiation from 95k to 10k calls + [#14905](https://github.com/rust-lang/rust-clippy/pull/14905) +* [`doc_markdown`] optimized by 50% + [#14693](https://github.com/rust-lang/rust-clippy/pull/14693) +* Refactor and speed up `cargo dev fmt` + [#14638](https://github.com/rust-lang/rust-clippy/pull/14638) ## Rust 1.88 @@ -6260,6 +6358,7 @@ Released 2018-09-13 [`pointers_in_nomem_asm_block`]: https://rust-lang.github.io/rust-clippy/master/index.html#pointers_in_nomem_asm_block [`positional_named_format_parameters`]: https://rust-lang.github.io/rust-clippy/master/index.html#positional_named_format_parameters [`possible_missing_comma`]: https://rust-lang.github.io/rust-clippy/master/index.html#possible_missing_comma +[`possible_missing_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#possible_missing_else [`precedence`]: https://rust-lang.github.io/rust-clippy/master/index.html#precedence [`precedence_bits`]: https://rust-lang.github.io/rust-clippy/master/index.html#precedence_bits [`print_in_format_impl`]: https://rust-lang.github.io/rust-clippy/master/index.html#print_in_format_impl diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 72ab08792d3..42ed624ec21 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -199,26 +199,34 @@ currently. Between writing new lints, fixing issues, reviewing pull requests and responding to issues there may not always be enough time to stay on top of it all. -Our highest priority is fixing [ICEs][I-ICE] and [bugs][C-bug], for example -an ICE in a popular crate that many other crates depend on. We don't -want Clippy to crash on your code and we want it to be as reliable as the -suggestions from Rust compiler errors. - -We have prioritization labels and a sync-blocker label, which are described below. -- [P-low][p-low]: Requires attention (fix/response/evaluation) by a team member but isn't urgent. -- [P-medium][p-medium]: Should be addressed by a team member until the next sync. -- [P-high][p-high]: Should be immediately addressed and will require an out-of-cycle sync or a backport. -- [L-sync-blocker][l-sync-blocker]: An issue that "blocks" a sync. -Or rather: before the sync this should be addressed, -e.g. by removing a lint again, so it doesn't hit beta/stable. +To find things to fix, go to the [tracking issue][tracking_issue], find an issue that you like, +and claim it with `@rustbot claim`. + +As a general metric and always taking into account your skill and knowledge level, you can use this guide: + +- 🟥 [ICEs][search_ice], these are compiler errors that causes Clippy to panic and crash. Usually involves high-level +debugging, sometimes interacting directly with the upstream compiler. Difficult to fix but a great challenge that +improves a lot developer workflows! + +- 🟧 [Suggestion causes bug][sugg_causes_bug], Clippy suggested code that changed logic in some silent way. +Unacceptable, as this may have disastrous consequences. Easier to fix than ICEs + +- 🟨 [Suggestion causes error][sugg_causes_error], Clippy suggested code snippet that caused a compiler error +when applied. We need to make sure that Clippy doesn't suggest using a variable twice at the same time or similar +easy-to-happen occurrences. + +- 🟩 [False positives][false_positive], a lint should not have fired, the easiest of them all, as this is "just" +identifying the root of a false positive and making an exception for those cases. + +Note that false negatives do not have priority unless the case is very clear, as they are a feature-request in a +trench coat. [triage]: https://forge.rust-lang.org/release/triage-procedure.html -[I-ICE]: https://github.com/rust-lang/rust-clippy/labels/I-ICE -[C-bug]: https://github.com/rust-lang/rust-clippy/labels/C-bug -[p-low]: https://github.com/rust-lang/rust-clippy/labels/P-low -[p-medium]: https://github.com/rust-lang/rust-clippy/labels/P-medium -[p-high]: https://github.com/rust-lang/rust-clippy/labels/P-high -[l-sync-blocker]: https://github.com/rust-lang/rust-clippy/labels/L-sync-blocker +[search_ice]: https://github.com/rust-lang/rust-clippy/issues?q=sort%3Aupdated-desc+state%3Aopen+label%3A%22I-ICE%22 +[sugg_causes_bug]: https://github.com/rust-lang/rust-clippy/issues?q=sort%3Aupdated-desc%20state%3Aopen%20label%3AI-suggestion-causes-bug +[sugg_causes_error]: https://github.com/rust-lang/rust-clippy/issues?q=sort%3Aupdated-desc%20state%3Aopen%20label%3AI-suggestion-causes-error%20 +[false_positive]: https://github.com/rust-lang/rust-clippy/issues?q=sort%3Aupdated-desc%20state%3Aopen%20label%3AI-false-positive +[tracking_issue]: https://github.com/rust-lang/rust-clippy/issues/15086 ## Contributions diff --git a/Cargo.toml b/Cargo.toml index f25f9ab54dd..daf1c98cdc9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "clippy" -version = "0.1.90" +version = "0.1.91" description = "A bunch of helpful lints to avoid common pitfalls in Rust" repository = "https://github.com/rust-lang/rust-clippy" readme = "README.md" diff --git a/book/src/lint_configuration.md b/book/src/lint_configuration.md index 992ed2c6aaa..7f16f3a9810 100644 --- a/book/src/lint_configuration.md +++ b/book/src/lint_configuration.md @@ -555,7 +555,7 @@ default configuration of Clippy. By default, any configuration will replace the * `doc-valid-idents = ["ClipPy"]` would replace the default list with `["ClipPy"]`. * `doc-valid-idents = ["ClipPy", ".."]` would append `ClipPy` to the default list. -**Default Value:** `["KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "MHz", "GHz", "THz", "AccessKit", "CoAP", "CoreFoundation", "CoreGraphics", "CoreText", "DevOps", "Direct2D", "Direct3D", "DirectWrite", "DirectX", "ECMAScript", "GPLv2", "GPLv3", "GitHub", "GitLab", "IPv4", "IPv6", "ClojureScript", "CoffeeScript", "JavaScript", "PostScript", "PureScript", "TypeScript", "WebAssembly", "NaN", "NaNs", "OAuth", "GraphQL", "OCaml", "OpenAL", "OpenDNS", "OpenGL", "OpenMP", "OpenSSH", "OpenSSL", "OpenStreetMap", "OpenTelemetry", "OpenType", "WebGL", "WebGL2", "WebGPU", "WebRTC", "WebSocket", "WebTransport", "WebP", "OpenExr", "YCbCr", "sRGB", "TensorFlow", "TrueType", "iOS", "macOS", "FreeBSD", "NetBSD", "OpenBSD", "TeX", "LaTeX", "BibTeX", "BibLaTeX", "MinGW", "CamelCase"]` +**Default Value:** `["KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "MHz", "GHz", "THz", "AccessKit", "CoAP", "CoreFoundation", "CoreGraphics", "CoreText", "DevOps", "Direct2D", "Direct3D", "DirectWrite", "DirectX", "ECMAScript", "GPLv2", "GPLv3", "GitHub", "GitLab", "IPv4", "IPv6", "ClojureScript", "CoffeeScript", "JavaScript", "PostScript", "PureScript", "TypeScript", "WebAssembly", "NaN", "NaNs", "OAuth", "GraphQL", "OCaml", "OpenAL", "OpenDNS", "OpenGL", "OpenMP", "OpenSSH", "OpenSSL", "OpenStreetMap", "OpenTelemetry", "OpenType", "WebGL", "WebGL2", "WebGPU", "WebRTC", "WebSocket", "WebTransport", "WebP", "OpenExr", "YCbCr", "sRGB", "TensorFlow", "TrueType", "iOS", "macOS", "FreeBSD", "NetBSD", "OpenBSD", "NixOS", "TeX", "LaTeX", "BibTeX", "BibLaTeX", "MinGW", "CamelCase"]` --- **Affected lints:** diff --git a/clippy_config/Cargo.toml b/clippy_config/Cargo.toml index 858366c8a5c..6ad2cf0d0b1 100644 --- a/clippy_config/Cargo.toml +++ b/clippy_config/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "clippy_config" -version = "0.1.90" +version = "0.1.91" edition = "2024" publish = false diff --git a/clippy_config/src/conf.rs b/clippy_config/src/conf.rs index 555f54bcfb8..8167d75583e 100644 --- a/clippy_config/src/conf.rs +++ b/clippy_config/src/conf.rs @@ -44,7 +44,7 @@ const DEFAULT_DOC_VALID_IDENTS: &[&str] = &[ "WebP", "OpenExr", "YCbCr", "sRGB", "TensorFlow", "TrueType", - "iOS", "macOS", "FreeBSD", "NetBSD", "OpenBSD", + "iOS", "macOS", "FreeBSD", "NetBSD", "OpenBSD", "NixOS", "TeX", "LaTeX", "BibTeX", "BibLaTeX", "MinGW", "CamelCase", diff --git a/clippy_lints/Cargo.toml b/clippy_lints/Cargo.toml index c03cc99b581..b9e6de79cc0 100644 --- a/clippy_lints/Cargo.toml +++ b/clippy_lints/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "clippy_lints" -version = "0.1.90" +version = "0.1.91" description = "A bunch of helpful lints to avoid common pitfalls in Rust" repository = "https://github.com/rust-lang/rust-clippy" readme = "README.md" diff --git a/clippy_lints/src/approx_const.rs b/clippy_lints/src/approx_const.rs index ab47e309752..a3710ca5165 100644 --- a/clippy_lints/src/approx_const.rs +++ b/clippy_lints/src/approx_const.rs @@ -2,8 +2,7 @@ use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::msrvs::{self, Msrv}; use rustc_ast::ast::{FloatTy, LitFloatType, LitKind}; -use rustc_hir::RustcVersion; -use rustc_hir::{HirId, Lit}; +use rustc_hir::{HirId, Lit, RustcVersion}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::impl_lint_pass; use rustc_span::{Span, symbol}; diff --git a/clippy_lints/src/attrs/duplicated_attributes.rs b/clippy_lints/src/attrs/duplicated_attributes.rs index c2406bcfb64..c956738edf0 100644 --- a/clippy_lints/src/attrs/duplicated_attributes.rs +++ b/clippy_lints/src/attrs/duplicated_attributes.rs @@ -2,6 +2,7 @@ use super::DUPLICATED_ATTRIBUTES; use clippy_utils::diagnostics::span_lint_and_then; use itertools::Itertools; use rustc_ast::{Attribute, MetaItem}; +use rustc_ast_pretty::pprust::path_to_string; use rustc_data_structures::fx::FxHashMap; use rustc_lint::EarlyContext; use rustc_span::{Span, Symbol, sym}; @@ -35,31 +36,38 @@ fn check_duplicated_attr( if attr.span.from_expansion() { return; } - let Some(ident) = attr.ident() else { return }; - let name = ident.name; - if name == sym::doc || name == sym::cfg_attr_trace || name == sym::rustc_on_unimplemented || name == sym::reason { - // FIXME: Would be nice to handle `cfg_attr` as well. Only problem is to check that cfg - // conditions are the same. - // `#[rustc_on_unimplemented]` contains duplicated subattributes, that's expected. - return; - } - if let Some(direct_parent) = parent.last() - && *direct_parent == sym::cfg_trace - && [sym::all, sym::not, sym::any].contains(&name) - { - // FIXME: We don't correctly check `cfg`s for now, so if it's more complex than just a one - // level `cfg`, we leave. - return; + let attr_path = if let Some(ident) = attr.ident() { + ident.name + } else { + Symbol::intern(&path_to_string(&attr.path)) + }; + if let Some(ident) = attr.ident() { + let name = ident.name; + if name == sym::doc || name == sym::cfg_attr_trace || name == sym::rustc_on_unimplemented || name == sym::reason + { + // FIXME: Would be nice to handle `cfg_attr` as well. Only problem is to check that cfg + // conditions are the same. + // `#[rustc_on_unimplemented]` contains duplicated subattributes, that's expected. + return; + } + if let Some(direct_parent) = parent.last() + && *direct_parent == sym::cfg_trace + && [sym::all, sym::not, sym::any].contains(&name) + { + // FIXME: We don't correctly check `cfg`s for now, so if it's more complex than just a one + // level `cfg`, we leave. + return; + } } if let Some(value) = attr.value_str() { emit_if_duplicated( cx, attr, attr_paths, - format!("{}:{name}={value}", parent.iter().join(":")), + format!("{}:{attr_path}={value}", parent.iter().join(":")), ); } else if let Some(sub_attrs) = attr.meta_item_list() { - parent.push(name); + parent.push(attr_path); for sub_attr in sub_attrs { if let Some(meta) = sub_attr.meta_item() { check_duplicated_attr(cx, meta, attr_paths, parent); @@ -67,7 +75,7 @@ fn check_duplicated_attr( } parent.pop(); } else { - emit_if_duplicated(cx, attr, attr_paths, format!("{}:{name}", parent.iter().join(":"))); + emit_if_duplicated(cx, attr, attr_paths, format!("{}:{attr_path}", parent.iter().join(":"))); } } diff --git a/clippy_lints/src/attrs/inline_always.rs b/clippy_lints/src/attrs/inline_always.rs index 409bb698665..fb86ae8da9d 100644 --- a/clippy_lints/src/attrs/inline_always.rs +++ b/clippy_lints/src/attrs/inline_always.rs @@ -1,8 +1,7 @@ use super::INLINE_ALWAYS; use clippy_utils::diagnostics::span_lint; use rustc_hir::attrs::{AttributeKind, InlineAttr}; -use rustc_hir::find_attr; -use rustc_hir::Attribute; +use rustc_hir::{Attribute, find_attr}; use rustc_lint::LateContext; use rustc_span::Span; use rustc_span::symbol::Symbol; diff --git a/clippy_lints/src/attrs/repr_attributes.rs b/clippy_lints/src/attrs/repr_attributes.rs index 4ece3ed44fd..8a530e8cff2 100644 --- a/clippy_lints/src/attrs/repr_attributes.rs +++ b/clippy_lints/src/attrs/repr_attributes.rs @@ -1,6 +1,5 @@ use rustc_hir::attrs::{AttributeKind, ReprAttr}; -use rustc_hir::find_attr; -use rustc_hir::Attribute; +use rustc_hir::{Attribute, find_attr}; use rustc_lint::LateContext; use rustc_span::Span; diff --git a/clippy_lints/src/blocks_in_conditions.rs b/clippy_lints/src/blocks_in_conditions.rs index 011962846cb..10fe1a4174f 100644 --- a/clippy_lints/src/blocks_in_conditions.rs +++ b/clippy_lints/src/blocks_in_conditions.rs @@ -100,8 +100,7 @@ impl<'tcx> LateLintPass<'tcx> for BlocksInConditions { cond.span, BRACED_EXPR_MESSAGE, "try", - snippet_block_with_applicability(cx, ex.span, "..", Some(expr.span), &mut applicability) - .to_string(), + snippet_block_with_applicability(cx, ex.span, "..", Some(expr.span), &mut applicability), applicability, ); } diff --git a/clippy_lints/src/booleans.rs b/clippy_lints/src/booleans.rs index ba1135d745a..64aeb27df69 100644 --- a/clippy_lints/src/booleans.rs +++ b/clippy_lints/src/booleans.rs @@ -7,10 +7,9 @@ use clippy_utils::sugg::Sugg; use clippy_utils::ty::{implements_trait, is_type_diagnostic_item}; use clippy_utils::{eq_expr_value, sym}; use rustc_ast::ast::LitKind; -use rustc_hir::RustcVersion; use rustc_errors::Applicability; use rustc_hir::intravisit::{FnKind, Visitor, walk_expr}; -use rustc_hir::{BinOpKind, Body, Expr, ExprKind, FnDecl, UnOp}; +use rustc_hir::{BinOpKind, Body, Expr, ExprKind, FnDecl, RustcVersion, UnOp}; use rustc_lint::{LateContext, LateLintPass, Level}; use rustc_session::impl_lint_pass; use rustc_span::def_id::LocalDefId; diff --git a/clippy_lints/src/casts/cast_lossless.rs b/clippy_lints/src/casts/cast_lossless.rs index c1d6cec1b62..c924fba6b5c 100644 --- a/clippy_lints/src/casts/cast_lossless.rs +++ b/clippy_lints/src/casts/cast_lossless.rs @@ -25,6 +25,12 @@ pub(super) fn check( return; } + // If the `as` is from a macro and the casting type is from macro input, whether it is lossless is + // dependent on the input + if expr.span.from_expansion() && !cast_to_hir.span.eq_ctxt(expr.span) { + return; + } + span_lint_and_then( cx, CAST_LOSSLESS, diff --git a/clippy_lints/src/casts/mod.rs b/clippy_lints/src/casts/mod.rs index 37accff5eaa..dcc439a272c 100644 --- a/clippy_lints/src/casts/mod.rs +++ b/clippy_lints/src/casts/mod.rs @@ -807,7 +807,7 @@ declare_clippy_lint! { /// ```no_run /// let _ = u16::MAX as usize; /// ``` - #[clippy::version = "1.86.0"] + #[clippy::version = "1.89.0"] pub CONFUSING_METHOD_TO_NUMERIC_CAST, suspicious, "casting a primitive method pointer to any integer type" diff --git a/clippy_lints/src/cloned_ref_to_slice_refs.rs b/clippy_lints/src/cloned_ref_to_slice_refs.rs index e33a8e0fb74..72ab292ee3c 100644 --- a/clippy_lints/src/cloned_ref_to_slice_refs.rs +++ b/clippy_lints/src/cloned_ref_to_slice_refs.rs @@ -37,7 +37,7 @@ declare_clippy_lint! { /// let data_ref = &data; /// take_slice(slice::from_ref(data_ref)); /// ``` - #[clippy::version = "1.87.0"] + #[clippy::version = "1.89.0"] pub CLONED_REF_TO_SLICE_REFS, perf, "cloning a reference for slice references" diff --git a/clippy_lints/src/coerce_container_to_any.rs b/clippy_lints/src/coerce_container_to_any.rs index 6217fc4c897..2e3acb7748e 100644 --- a/clippy_lints/src/coerce_container_to_any.rs +++ b/clippy_lints/src/coerce_container_to_any.rs @@ -41,7 +41,7 @@ declare_clippy_lint! { /// // Succeeds since we have a &dyn Any to the inner u32! /// assert_eq!(dyn_any_of_u32.downcast_ref::(), Some(&0u32)); /// ``` - #[clippy::version = "1.88.0"] + #[clippy::version = "1.89.0"] pub COERCE_CONTAINER_TO_ANY, nursery, "coercing to `&dyn Any` when dereferencing could produce a `dyn Any` without coercion is usually not intended" diff --git a/clippy_lints/src/cognitive_complexity.rs b/clippy_lints/src/cognitive_complexity.rs index 518535e8c8b..8f1c0296524 100644 --- a/clippy_lints/src/cognitive_complexity.rs +++ b/clippy_lints/src/cognitive_complexity.rs @@ -35,7 +35,7 @@ declare_clippy_lint! { /// * [`too_many_lines`](https://rust-lang.github.io/rust-clippy/master/index.html#too_many_lines) #[clippy::version = "1.35.0"] pub COGNITIVE_COMPLEXITY, - nursery, + restriction, "functions that should be split up into multiple functions", @eval_always = true } diff --git a/clippy_lints/src/collapsible_if.rs b/clippy_lints/src/collapsible_if.rs index 1854d86c53b..e3103e2d301 100644 --- a/clippy_lints/src/collapsible_if.rs +++ b/clippy_lints/src/collapsible_if.rs @@ -136,6 +136,9 @@ impl CollapsibleIf { return; } + // Peel off any parentheses. + let (_, else_block_span, _) = peel_parens(cx.tcx.sess.source_map(), else_.span); + // Prevent "elseif" // Check that the "else" is followed by whitespace let requires_space = if let Some(c) = snippet(cx, up_to_else, "..").chars().last() { @@ -152,7 +155,7 @@ impl CollapsibleIf { if requires_space { " " } else { "" }, snippet_block_with_applicability( cx, - else_.span, + else_block_span, "..", Some(else_block.span), &mut applicability @@ -187,7 +190,8 @@ impl CollapsibleIf { .with_leading_whitespace(cx) .into_span() }; - let inner_if = inner.span.split_at(2).0; + let (paren_start, inner_if_span, paren_end) = peel_parens(cx.tcx.sess.source_map(), inner.span); + let inner_if = inner_if_span.split_at(2).0; let mut sugg = vec![ // Remove the outer then block `{` (then_open_bracket, String::new()), @@ -196,6 +200,17 @@ impl CollapsibleIf { // Replace inner `if` by `&&` (inner_if, String::from("&&")), ]; + + if !paren_start.is_empty() { + // Remove any leading parentheses '(' + sugg.push((paren_start, String::new())); + } + + if !paren_end.is_empty() { + // Remove any trailing parentheses ')' + sugg.push((paren_end, String::new())); + } + sugg.extend(parens_around(check)); sugg.extend(parens_around(check_inner)); @@ -285,3 +300,37 @@ fn span_extract_keyword(sm: &SourceMap, span: Span, keyword: &str) -> Option (Span, Span, Span) { + use crate::rustc_span::Pos; + + let start = span.shrink_to_lo(); + let end = span.shrink_to_hi(); + + let snippet = sm.span_to_snippet(span).unwrap(); + if let Some((trim_start, _, trim_end)) = peel_parens_str(&snippet) { + let mut data = span.data(); + data.lo = data.lo + BytePos::from_usize(trim_start); + data.hi = data.hi - BytePos::from_usize(trim_end); + span = data.span(); + } + + (start.with_hi(span.lo()), span, end.with_lo(span.hi())) +} + +fn peel_parens_str(snippet: &str) -> Option<(usize, &str, usize)> { + let trimmed = snippet.trim(); + if !(trimmed.starts_with('(') && trimmed.ends_with(')')) { + return None; + } + + let trim_start = (snippet.len() - snippet.trim_start().len()) + 1; + let trim_end = (snippet.len() - snippet.trim_end().len()) + 1; + + let inner = snippet.get(trim_start..snippet.len() - trim_end)?; + Some(match peel_parens_str(inner) { + None => (trim_start, inner, trim_end), + Some((start, inner, end)) => (trim_start + start, inner, trim_end + end), + }) +} diff --git a/clippy_lints/src/copies.rs b/clippy_lints/src/copies.rs index 4bd34527d21..4fdb497950f 100644 --- a/clippy_lints/src/copies.rs +++ b/clippy_lints/src/copies.rs @@ -235,9 +235,9 @@ fn lint_branches_sharing_code<'tcx>( let cond_snippet = reindent_multiline(&snippet(cx, cond_span, "_"), false, None); let cond_indent = indent_of(cx, cond_span); let moved_snippet = reindent_multiline(&snippet(cx, span, "_"), true, None); - let suggestion = moved_snippet.to_string() + "\n" + &cond_snippet + "{"; + let suggestion = moved_snippet + "\n" + &cond_snippet + "{"; let suggestion = reindent_multiline(&suggestion, true, cond_indent); - (replace_span, suggestion.to_string()) + (replace_span, suggestion) }); let end_suggestion = res.end_span(last_block, sm).map(|span| { let moved_snipped = reindent_multiline(&snippet(cx, span, "_"), true, None); @@ -253,7 +253,7 @@ fn lint_branches_sharing_code<'tcx>( .then_some(range.start - 4..range.end) }) .map_or(span, |range| range.with_ctxt(span.ctxt())); - (span, suggestion.to_string()) + (span, suggestion.clone()) }); let (span, msg, end_span) = match (&start_suggestion, &end_suggestion) { diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs index c3f8e02b4c0..e1cb08e361c 100644 --- a/clippy_lints/src/declared_lints.rs +++ b/clippy_lints/src/declared_lints.rs @@ -178,6 +178,7 @@ pub static LINTS: &[&::declare_clippy_lint::LintInfo] = &[ crate::format_impl::RECURSIVE_FORMAT_IMPL_INFO, crate::format_push_string::FORMAT_PUSH_STRING_INFO, crate::formatting::POSSIBLE_MISSING_COMMA_INFO, + crate::formatting::POSSIBLE_MISSING_ELSE_INFO, crate::formatting::SUSPICIOUS_ASSIGNMENT_FORMATTING_INFO, crate::formatting::SUSPICIOUS_ELSE_FORMATTING_INFO, crate::formatting::SUSPICIOUS_UNARY_OP_FORMATTING_INFO, @@ -690,7 +691,6 @@ pub static LINTS: &[&::declare_clippy_lint::LintInfo] = &[ crate::strings::STRING_FROM_UTF8_AS_BYTES_INFO, crate::strings::STRING_LIT_AS_BYTES_INFO, crate::strings::STRING_SLICE_INFO, - crate::strings::STRING_TO_STRING_INFO, crate::strings::STR_TO_STRING_INFO, crate::strings::TRIM_SPLIT_WHITESPACE_INFO, crate::strlen_on_c_strings::STRLEN_ON_C_STRINGS_INFO, diff --git a/clippy_lints/src/default_union_representation.rs b/clippy_lints/src/default_union_representation.rs index f41255e54db..df6525ce040 100644 --- a/clippy_lints/src/default_union_representation.rs +++ b/clippy_lints/src/default_union_representation.rs @@ -1,7 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; use rustc_hir::attrs::{AttributeKind, ReprAttr}; -use rustc_hir::find_attr; -use rustc_hir::{HirId, Item, ItemKind}; +use rustc_hir::{HirId, Item, ItemKind, find_attr}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::layout::LayoutOf; use rustc_middle::ty::{self, FieldDef}; diff --git a/clippy_lints/src/deprecated_lints.rs b/clippy_lints/src/deprecated_lints.rs index 5204f73ea0a..88aebc3e6a1 100644 --- a/clippy_lints/src/deprecated_lints.rs +++ b/clippy_lints/src/deprecated_lints.rs @@ -34,6 +34,8 @@ declare_with_version! { DEPRECATED(DEPRECATED_VERSION) = [ ("clippy::replace_consts", "`min_value` and `max_value` are now deprecated"), #[clippy::version = "pre 1.29.0"] ("clippy::should_assert_eq", "`assert!(a == b)` can now print the values the same way `assert_eq!(a, b) can"), + #[clippy::version = "1.90.0"] + ("clippy::string_to_string", "`clippy:implicit_clone` covers those cases"), #[clippy::version = "pre 1.29.0"] ("clippy::unsafe_vector_initialization", "the suggested alternative could be substantially slower"), #[clippy::version = "pre 1.29.0"] diff --git a/clippy_lints/src/doc/broken_link.rs b/clippy_lints/src/doc/broken_link.rs index 4af10510023..8878fa9180f 100644 --- a/clippy_lints/src/doc/broken_link.rs +++ b/clippy_lints/src/doc/broken_link.rs @@ -19,52 +19,52 @@ pub fn check(cx: &LateContext<'_>, bl: &PullDownBrokenLink<'_>, doc: &str, fragm } fn warn_if_broken_link(cx: &LateContext<'_>, bl: &PullDownBrokenLink<'_>, doc: &str, fragments: &[DocFragment]) { - if let Some((span, _)) = source_span_for_markdown_range(cx.tcx, doc, &bl.span, fragments) { - let mut len = 0; + let mut len = 0; - // grab raw link data - let (_, raw_link) = doc.split_at(bl.span.start); + // grab raw link data + let (_, raw_link) = doc.split_at(bl.span.start); - // strip off link text part - let raw_link = match raw_link.split_once(']') { - None => return, - Some((prefix, suffix)) => { - len += prefix.len() + 1; - suffix - }, - }; + // strip off link text part + let raw_link = match raw_link.split_once(']') { + None => return, + Some((prefix, suffix)) => { + len += prefix.len() + 1; + suffix + }, + }; - let raw_link = match raw_link.split_once('(') { - None => return, - Some((prefix, suffix)) => { - if !prefix.is_empty() { - // there is text between ']' and '(' chars, so it is not a valid link - return; - } - len += prefix.len() + 1; - suffix - }, - }; - - if raw_link.starts_with("(http") { - // reduce chances of false positive reports - // by limiting this checking only to http/https links. - return; - } - - for c in raw_link.chars() { - if c == ')' { - // it is a valid link + let raw_link = match raw_link.split_once('(') { + None => return, + Some((prefix, suffix)) => { + if !prefix.is_empty() { + // there is text between ']' and '(' chars, so it is not a valid link return; } + len += prefix.len() + 1; + suffix + }, + }; - if c == '\n' { - report_broken_link(cx, span, len); - break; - } + if raw_link.starts_with("(http") { + // reduce chances of false positive reports + // by limiting this checking only to http/https links. + return; + } - len += 1; + for c in raw_link.chars() { + if c == ')' { + // it is a valid link + return; } + + if c == '\n' + && let Some((span, _)) = source_span_for_markdown_range(cx.tcx, doc, &bl.span, fragments) + { + report_broken_link(cx, span, len); + break; + } + + len += 1; } } diff --git a/clippy_lints/src/doc/mod.rs b/clippy_lints/src/doc/mod.rs index ea0da0d2467..d27d68d3866 100644 --- a/clippy_lints/src/doc/mod.rs +++ b/clippy_lints/src/doc/mod.rs @@ -662,7 +662,7 @@ declare_clippy_lint! { /// /// [^1]: defined here /// fn my_fn() {} /// ``` - #[clippy::version = "1.88.0"] + #[clippy::version = "1.89.0"] pub DOC_SUSPICIOUS_FOOTNOTES, suspicious, "looks like a link or footnote ref, but with no definition" diff --git a/clippy_lints/src/doc/suspicious_doc_comments.rs b/clippy_lints/src/doc/suspicious_doc_comments.rs index 1e7d1f92fa3..47d91b80e7e 100644 --- a/clippy_lints/src/doc/suspicious_doc_comments.rs +++ b/clippy_lints/src/doc/suspicious_doc_comments.rs @@ -1,9 +1,9 @@ use clippy_utils::diagnostics::span_lint_and_then; use rustc_ast::AttrStyle; use rustc_ast::token::CommentKind; -use rustc_hir::attrs::AttributeKind; use rustc_errors::Applicability; use rustc_hir::Attribute; +use rustc_hir::attrs::AttributeKind; use rustc_lint::LateContext; use rustc_span::Span; diff --git a/clippy_lints/src/doc/too_long_first_doc_paragraph.rs b/clippy_lints/src/doc/too_long_first_doc_paragraph.rs index 32ba696b3ec..674690e7e31 100644 --- a/clippy_lints/src/doc/too_long_first_doc_paragraph.rs +++ b/clippy_lints/src/doc/too_long_first_doc_paragraph.rs @@ -1,5 +1,5 @@ -use rustc_hir::attrs::AttributeKind; use rustc_errors::Applicability; +use rustc_hir::attrs::AttributeKind; use rustc_hir::{Attribute, Item, ItemKind}; use rustc_lint::LateContext; diff --git a/clippy_lints/src/empty_with_brackets.rs b/clippy_lints/src/empty_with_brackets.rs index f2757407ba5..e7230ebf8cb 100644 --- a/clippy_lints/src/empty_with_brackets.rs +++ b/clippy_lints/src/empty_with_brackets.rs @@ -93,11 +93,11 @@ impl_lint_pass!(EmptyWithBrackets => [EMPTY_STRUCTS_WITH_BRACKETS, EMPTY_ENUM_VA impl LateLintPass<'_> for EmptyWithBrackets { fn check_item(&mut self, cx: &LateContext<'_>, item: &Item<'_>) { // FIXME: handle `struct $name {}` - if let ItemKind::Struct(ident, _, var_data) = &item.kind + if let ItemKind::Struct(ident, generics, var_data) = &item.kind && !item.span.from_expansion() && !ident.span.from_expansion() && has_brackets(var_data) - && let span_after_ident = item.span.with_lo(ident.span.hi()) + && let span_after_ident = item.span.with_lo(generics.span.hi()) && has_no_fields(cx, var_data, span_after_ident) { span_lint_and_then( diff --git a/clippy_lints/src/eta_reduction.rs b/clippy_lints/src/eta_reduction.rs index ba539d05b6b..e467246741c 100644 --- a/clippy_lints/src/eta_reduction.rs +++ b/clippy_lints/src/eta_reduction.rs @@ -7,10 +7,9 @@ use clippy_utils::{ get_path_from_caller_to_method_type, is_adjusted, is_no_std_crate, path_to_local, path_to_local_id, }; use rustc_abi::ExternAbi; -use rustc_hir::attrs::{AttributeKind}; -use rustc_hir::find_attr; use rustc_errors::Applicability; -use rustc_hir::{BindingMode, Expr, ExprKind, FnRetTy, GenericArgs, Param, PatKind, QPath, Safety, TyKind}; +use rustc_hir::attrs::AttributeKind; +use rustc_hir::{BindingMode, Expr, ExprKind, FnRetTy, GenericArgs, Param, PatKind, QPath, Safety, TyKind, find_attr}; use rustc_infer::infer::TyCtxtInferExt; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::{ diff --git a/clippy_lints/src/exhaustive_items.rs b/clippy_lints/src/exhaustive_items.rs index 5f40e576443..ac61ce705eb 100644 --- a/clippy_lints/src/exhaustive_items.rs +++ b/clippy_lints/src/exhaustive_items.rs @@ -1,9 +1,8 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::indent_of; -use rustc_hir::attrs::{AttributeKind}; -use rustc_hir::find_attr; use rustc_errors::Applicability; -use rustc_hir::{Item, ItemKind}; +use rustc_hir::attrs::AttributeKind; +use rustc_hir::{Item, ItemKind, find_attr}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; diff --git a/clippy_lints/src/floating_point_arithmetic.rs b/clippy_lints/src/floating_point_arithmetic.rs index d5abaa547e8..84d39dd81c9 100644 --- a/clippy_lints/src/floating_point_arithmetic.rs +++ b/clippy_lints/src/floating_point_arithmetic.rs @@ -148,7 +148,7 @@ fn prepare_receiver_sugg<'a>(cx: &LateContext<'_>, mut expr: &'a Expr<'a>) -> Su .into(); suggestion = match suggestion { - Sugg::MaybeParen(_) => Sugg::MaybeParen(op), + Sugg::MaybeParen(_) | Sugg::UnOp(UnOp::Neg, _) => Sugg::MaybeParen(op), _ => Sugg::NonParen(op), }; } diff --git a/clippy_lints/src/format_args.rs b/clippy_lints/src/format_args.rs index af4202422e4..3359aa60323 100644 --- a/clippy_lints/src/format_args.rs +++ b/clippy_lints/src/format_args.rs @@ -17,12 +17,11 @@ use rustc_ast::{ FormatArgPosition, FormatArgPositionKind, FormatArgsPiece, FormatArgumentKind, FormatCount, FormatOptions, FormatPlaceholder, FormatTrait, }; -use rustc_hir::attrs::{AttributeKind}; -use rustc_hir::{find_attr,RustcVersion}; use rustc_data_structures::fx::FxHashMap; use rustc_errors::Applicability; use rustc_errors::SuggestionStyle::{CompletelyHidden, ShowCode}; -use rustc_hir::{Expr, ExprKind, LangItem}; +use rustc_hir::attrs::AttributeKind; +use rustc_hir::{Expr, ExprKind, LangItem, RustcVersion, find_attr}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::ty::adjustment::{Adjust, Adjustment}; use rustc_middle::ty::{self, GenericArg, List, TraitRef, Ty, TyCtxt, Upcast}; @@ -223,7 +222,7 @@ declare_clippy_lint! { /// ``` /// /// [pointer format]: https://doc.rust-lang.org/std/fmt/index.html#formatting-traits - #[clippy::version = "1.88.0"] + #[clippy::version = "1.89.0"] pub POINTER_FORMAT, restriction, "formatting a pointer" diff --git a/clippy_lints/src/formatting.rs b/clippy_lints/src/formatting.rs index 4b482f7b233..1c751643bec 100644 --- a/clippy_lints/src/formatting.rs +++ b/clippy_lints/src/formatting.rs @@ -91,6 +91,31 @@ declare_clippy_lint! { "suspicious formatting of `else`" } +declare_clippy_lint! { + /// ### What it does + /// Checks for an `if` expression followed by either a block or another `if` that + /// looks like it should have an `else` between them. + /// + /// ### Why is this bad? + /// This is probably some refactoring remnant, even if the code is correct, it + /// might look confusing. + /// + /// ### Example + /// ```rust,ignore + /// if foo { + /// } { // looks like an `else` is missing here + /// } + /// + /// if foo { + /// } if bar { // looks like an `else` is missing here + /// } + /// ``` + #[clippy::version = "1.90.0"] + pub POSSIBLE_MISSING_ELSE, + suspicious, + "possibly missing `else`" +} + declare_clippy_lint! { /// ### What it does /// Checks for possible missing comma in an array. It lints if @@ -116,6 +141,7 @@ declare_lint_pass!(Formatting => [ SUSPICIOUS_ASSIGNMENT_FORMATTING, SUSPICIOUS_UNARY_OP_FORMATTING, SUSPICIOUS_ELSE_FORMATTING, + POSSIBLE_MISSING_ELSE, POSSIBLE_MISSING_COMMA ]); @@ -307,7 +333,7 @@ fn check_missing_else(cx: &EarlyContext<'_>, first: &Expr, second: &Expr) { span_lint_and_note( cx, - SUSPICIOUS_ELSE_FORMATTING, + POSSIBLE_MISSING_ELSE, else_span, format!("this looks like {looks_like} but the `else` is missing"), None, diff --git a/clippy_lints/src/four_forward_slashes.rs b/clippy_lints/src/four_forward_slashes.rs index 8822b87f92f..a7b0edeb799 100644 --- a/clippy_lints/src/four_forward_slashes.rs +++ b/clippy_lints/src/four_forward_slashes.rs @@ -1,4 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::source::SpanRangeExt as _; +use itertools::Itertools; use rustc_errors::Applicability; use rustc_hir::Item; use rustc_lint::{LateContext, LateLintPass, LintContext}; @@ -81,6 +83,14 @@ impl<'tcx> LateLintPass<'tcx> for FourForwardSlashes { "turn these into doc comments by removing one `/`" }; + // If the comment contains a bare CR (not followed by a LF), do not propose an auto-fix + // as bare CR are not allowed in doc comments. + if span.check_source_text(cx, contains_bare_cr) { + diag.help(msg) + .note("bare CR characters are not allowed in doc comments"); + return; + } + diag.multipart_suggestion( msg, bad_comments @@ -97,3 +107,8 @@ impl<'tcx> LateLintPass<'tcx> for FourForwardSlashes { } } } + +/// Checks if `text` contains any CR not followed by a LF +fn contains_bare_cr(text: &str) -> bool { + text.bytes().tuple_windows().any(|(a, b)| a == b'\r' && b != b'\n') +} diff --git a/clippy_lints/src/functions/must_use.rs b/clippy_lints/src/functions/must_use.rs index 55ca0d9ecb7..8de68bfcb51 100644 --- a/clippy_lints/src/functions/must_use.rs +++ b/clippy_lints/src/functions/must_use.rs @@ -14,7 +14,7 @@ use clippy_utils::source::snippet_indent; use clippy_utils::ty::is_must_use_ty; use clippy_utils::visitors::for_each_expr_without_closures; use clippy_utils::{return_ty, trait_ref_of_method}; -use rustc_hir::attrs::{AttributeKind}; +use rustc_hir::attrs::AttributeKind; use rustc_hir::find_attr; use rustc_span::Symbol; use rustc_trait_selection::error_reporting::InferCtxtErrorExt; diff --git a/clippy_lints/src/if_not_else.rs b/clippy_lints/src/if_not_else.rs index 25fed0d4dd1..e8afa69b537 100644 --- a/clippy_lints/src/if_not_else.rs +++ b/clippy_lints/src/if_not_else.rs @@ -81,7 +81,7 @@ impl LateLintPass<'_> for IfNotElse { e.span, msg, "try", - make_sugg(cx, &cond.kind, cond_inner.span, els.span, "..", Some(e.span)).to_string(), + make_sugg(cx, &cond.kind, cond_inner.span, els.span, "..", Some(e.span)), Applicability::MachineApplicable, ), _ => span_lint_and_help(cx, IF_NOT_ELSE, e.span, msg, None, help), diff --git a/clippy_lints/src/incompatible_msrv.rs b/clippy_lints/src/incompatible_msrv.rs index 85ebc830d3b..89988be5875 100644 --- a/clippy_lints/src/incompatible_msrv.rs +++ b/clippy_lints/src/incompatible_msrv.rs @@ -2,14 +2,13 @@ use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::msrvs::Msrv; use clippy_utils::{is_in_const_context, is_in_test}; -use rustc_hir::{RustcVersion, StabilityLevel, StableSince}; use rustc_data_structures::fx::FxHashMap; use rustc_hir::def::DefKind; -use rustc_hir::{self as hir, AmbigArg, Expr, ExprKind, HirId, QPath}; +use rustc_hir::{self as hir, AmbigArg, Expr, ExprKind, HirId, QPath, RustcVersion, StabilityLevel, StableSince}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::TyCtxt; use rustc_session::impl_lint_pass; -use rustc_span::def_id::DefId; +use rustc_span::def_id::{CrateNum, DefId}; use rustc_span::{ExpnKind, Span, sym}; declare_clippy_lint! { @@ -83,16 +82,22 @@ pub struct IncompatibleMsrv { msrv: Msrv, availability_cache: FxHashMap<(DefId, bool), Availability>, check_in_tests: bool, + core_crate: Option, } impl_lint_pass!(IncompatibleMsrv => [INCOMPATIBLE_MSRV]); impl IncompatibleMsrv { - pub fn new(conf: &'static Conf) -> Self { + pub fn new(tcx: TyCtxt<'_>, conf: &'static Conf) -> Self { Self { msrv: conf.msrv, availability_cache: FxHashMap::default(), check_in_tests: conf.check_incompatible_msrv_in_tests, + core_crate: tcx + .crates(()) + .iter() + .find(|krate| tcx.crate_name(**krate) == sym::core) + .copied(), } } @@ -140,23 +145,16 @@ impl IncompatibleMsrv { // We don't check local items since their MSRV is supposed to always be valid. return; } - if let ExpnKind::AstPass(_) | ExpnKind::Desugaring(_) = span.ctxt().outer_expn_data().kind { + let expn_data = span.ctxt().outer_expn_data(); + if let ExpnKind::AstPass(_) | ExpnKind::Desugaring(_) = expn_data.kind { // Desugared expressions get to cheat and stability is ignored. // Intentionally not using `.from_expansion()`, since we do still care about macro expansions return; } - // Functions coming from `core` while expanding a macro such as `assert*!()` get to cheat too: the // macros may have existed prior to the checked MSRV, but their expansion with a recent compiler // might use recent functions or methods. Compiling with an older compiler would not use those. - if span.from_expansion() - && cx.tcx.crate_name(def_id.krate) == sym::core - && span - .ctxt() - .outer_expn_data() - .macro_def_id - .is_some_and(|def_id| cx.tcx.crate_name(def_id.krate) == sym::core) - { + if Some(def_id.krate) == self.core_crate && expn_data.macro_def_id.map(|did| did.krate) == self.core_crate { return; } diff --git a/clippy_lints/src/index_refutable_slice.rs b/clippy_lints/src/index_refutable_slice.rs index 3d131a7825a..8f9f71a1476 100644 --- a/clippy_lints/src/index_refutable_slice.rs +++ b/clippy_lints/src/index_refutable_slice.rs @@ -4,7 +4,7 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::higher::IfLet; use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::ty::is_copy; -use clippy_utils::{is_expn_of, is_lint_allowed, path_to_local, sym}; +use clippy_utils::{is_lint_allowed, path_to_local}; use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet}; use rustc_errors::Applicability; use rustc_hir as hir; @@ -71,7 +71,7 @@ impl_lint_pass!(IndexRefutableSlice => [INDEX_REFUTABLE_SLICE]); impl<'tcx> LateLintPass<'tcx> for IndexRefutableSlice { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { if let Some(IfLet { let_pat, if_then, .. }) = IfLet::hir(cx, expr) - && (!expr.span.from_expansion() || is_expn_of(expr.span, sym::if_chain).is_some()) + && !expr.span.from_expansion() && !is_lint_allowed(cx, INDEX_REFUTABLE_SLICE, expr.hir_id) && let found_slices = find_slice_values(cx, let_pat) && !found_slices.is_empty() diff --git a/clippy_lints/src/infallible_try_from.rs b/clippy_lints/src/infallible_try_from.rs index f7cdf05359a..589c294a678 100644 --- a/clippy_lints/src/infallible_try_from.rs +++ b/clippy_lints/src/infallible_try_from.rs @@ -13,7 +13,7 @@ declare_clippy_lint! { /// /// ### Why is this bad? /// - /// Infalliable conversions should be implemented via `From` with the blanket conversion. + /// Infallible conversions should be implemented via `From` with the blanket conversion. /// /// ### Example /// ```no_run @@ -35,7 +35,7 @@ declare_clippy_lint! { /// } /// } /// ``` - #[clippy::version = "1.88.0"] + #[clippy::version = "1.89.0"] pub INFALLIBLE_TRY_FROM, suspicious, "TryFrom with infallible Error type" @@ -71,7 +71,7 @@ impl<'tcx> LateLintPass<'tcx> for InfallibleTryFrom { cx, INFALLIBLE_TRY_FROM, span, - "infallible TryFrom impl; consider implementing From, instead", + "infallible TryFrom impl; consider implementing From instead", ); } } diff --git a/clippy_lints/src/inline_fn_without_body.rs b/clippy_lints/src/inline_fn_without_body.rs index ee59a4cc8cb..e416ac079d6 100644 --- a/clippy_lints/src/inline_fn_without_body.rs +++ b/clippy_lints/src/inline_fn_without_body.rs @@ -1,9 +1,8 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::sugg::DiagExt; -use rustc_hir::attrs::{AttributeKind}; -use rustc_hir::find_attr; use rustc_errors::Applicability; -use rustc_hir::{TraitFn, TraitItem, TraitItemKind}; +use rustc_hir::attrs::AttributeKind; +use rustc_hir::{TraitFn, TraitItem, TraitItemKind, find_attr}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; diff --git a/clippy_lints/src/let_with_type_underscore.rs b/clippy_lints/src/let_with_type_underscore.rs index 1917ca24a05..5b0f95ffc37 100644 --- a/clippy_lints/src/let_with_type_underscore.rs +++ b/clippy_lints/src/let_with_type_underscore.rs @@ -1,5 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::is_from_proc_macro; +use clippy_utils::source::{IntoSpan, SpanRangeExt}; use rustc_errors::Applicability; use rustc_hir::{LetStmt, TyKind}; use rustc_lint::{LateContext, LateLintPass}; @@ -30,9 +31,15 @@ impl<'tcx> LateLintPass<'tcx> for UnderscoreTyped { if let Some(ty) = local.ty // Ensure that it has a type defined && let TyKind::Infer(()) = &ty.kind // that type is '_' && local.span.eq_ctxt(ty.span) - && !local.span.in_external_macro(cx.tcx.sess.source_map()) + && let sm = cx.tcx.sess.source_map() + && !local.span.in_external_macro(sm) && !is_from_proc_macro(cx, ty) { + let span_to_remove = sm + .span_extend_to_prev_char_before(ty.span, ':', true) + .with_leading_whitespace(cx) + .into_span(); + span_lint_and_then( cx, LET_WITH_TYPE_UNDERSCORE, @@ -40,7 +47,7 @@ impl<'tcx> LateLintPass<'tcx> for UnderscoreTyped { "variable declared with type underscore", |diag| { diag.span_suggestion_verbose( - ty.span.with_lo(local.pat.span.hi()), + span_to_remove, "remove the explicit type `_` declaration", "", Applicability::MachineApplicable, diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 914aa6b9b80..844bc1b0e39 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -522,7 +522,8 @@ pub fn register_lint_passes(store: &mut rustc_lint::LintStore, conf: &'static Co store.register_late_pass(|_| Box::new(same_name_method::SameNameMethod)); store.register_late_pass(move |_| Box::new(index_refutable_slice::IndexRefutableSlice::new(conf))); store.register_late_pass(|_| Box::::default()); - store.register_late_pass(|_| Box::new(unit_types::UnitTypes)); + let format_args = format_args_storage.clone(); + store.register_late_pass(move |_| Box::new(unit_types::UnitTypes::new(format_args.clone()))); store.register_late_pass(move |_| Box::new(loops::Loops::new(conf))); store.register_late_pass(|_| Box::::default()); store.register_late_pass(move |_| Box::new(lifetimes::Lifetimes::new(conf))); @@ -663,7 +664,6 @@ pub fn register_lint_passes(store: &mut rustc_lint::LintStore, conf: &'static Co store.register_early_pass(|| Box::new(asm_syntax::InlineAsmX86IntelSyntax)); store.register_late_pass(|_| Box::new(empty_drop::EmptyDrop)); store.register_late_pass(|_| Box::new(strings::StrToString)); - store.register_late_pass(|_| Box::new(strings::StringToString)); store.register_late_pass(|_| Box::new(zero_sized_map_values::ZeroSizedMapValues)); store.register_late_pass(|_| Box::::default()); store.register_late_pass(|_| Box::new(redundant_slicing::RedundantSlicing)); @@ -796,7 +796,7 @@ pub fn register_lint_passes(store: &mut rustc_lint::LintStore, conf: &'static Co store.register_late_pass(|_| Box::::default()); store.register_late_pass(move |_| Box::new(pub_underscore_fields::PubUnderscoreFields::new(conf))); store.register_late_pass(move |_| Box::new(missing_const_for_thread_local::MissingConstForThreadLocal::new(conf))); - store.register_late_pass(move |_| Box::new(incompatible_msrv::IncompatibleMsrv::new(conf))); + store.register_late_pass(move |tcx| Box::new(incompatible_msrv::IncompatibleMsrv::new(tcx, conf))); store.register_late_pass(|_| Box::new(to_string_trait_impl::ToStringTraitImpl)); store.register_early_pass(|| Box::new(multiple_bound_locations::MultipleBoundLocations)); store.register_late_pass(move |_| Box::new(assigning_clones::AssigningClones::new(conf))); diff --git a/clippy_lints/src/loops/never_loop.rs b/clippy_lints/src/loops/never_loop.rs index 8a253ae5810..2ccff768097 100644 --- a/clippy_lints/src/loops/never_loop.rs +++ b/clippy_lints/src/loops/never_loop.rs @@ -202,7 +202,7 @@ fn all_spans_after_expr(cx: &LateContext<'_>, expr: &Expr<'_>) -> Vec { .iter() .skip_while(|inner| inner.hir_id != stmt.hir_id) .map(stmt_source_span) - .chain(if let Some(e) = block.expr { vec![e.span] } else { vec![] }) + .chain(block.expr.map(|e| e.span)) .collect(); } diff --git a/clippy_lints/src/macro_use.rs b/clippy_lints/src/macro_use.rs index bf89556fbb6..8989793625a 100644 --- a/clippy_lints/src/macro_use.rs +++ b/clippy_lints/src/macro_use.rs @@ -1,11 +1,10 @@ use clippy_utils::diagnostics::span_lint_hir_and_then; use clippy_utils::source::snippet; use hir::def::{DefKind, Res}; -use rustc_hir::attrs::{AttributeKind}; -use rustc_hir::find_attr; use rustc_data_structures::fx::FxHashSet; use rustc_errors::Applicability; -use rustc_hir::{self as hir, AmbigArg}; +use rustc_hir::attrs::AttributeKind; +use rustc_hir::{self as hir, AmbigArg, find_attr}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_session::impl_lint_pass; use rustc_span::Span; diff --git a/clippy_lints/src/manual_assert.rs b/clippy_lints/src/manual_assert.rs index ea6b01a053a..76cb2286477 100644 --- a/clippy_lints/src/manual_assert.rs +++ b/clippy_lints/src/manual_assert.rs @@ -1,8 +1,8 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::macros::{is_panic, root_macro_call}; -use clippy_utils::{is_else_clause, is_parent_stmt, peel_blocks_with_stmt, span_extract_comment, sugg}; +use clippy_utils::{higher, is_else_clause, is_parent_stmt, peel_blocks_with_stmt, span_extract_comment, sugg}; use rustc_errors::Applicability; -use rustc_hir::{Expr, ExprKind, UnOp}; +use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_session::declare_lint_pass; @@ -35,7 +35,7 @@ declare_lint_pass!(ManualAssert => [MANUAL_ASSERT]); impl<'tcx> LateLintPass<'tcx> for ManualAssert { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'tcx>) { - if let ExprKind::If(cond, then, None) = expr.kind + if let Some(higher::If { cond, then, r#else: None }) = higher::If::hir(expr) && !matches!(cond.kind, ExprKind::Let(_)) && !expr.span.from_expansion() && let then = peel_blocks_with_stmt(then) @@ -51,19 +51,13 @@ impl<'tcx> LateLintPass<'tcx> for ManualAssert { && !is_else_clause(cx.tcx, expr) { let mut applicability = Applicability::MachineApplicable; - let cond = cond.peel_drop_temps(); let mut comments = span_extract_comment(cx.sess().source_map(), expr.span); if !comments.is_empty() { comments += "\n"; } - let (cond, not) = match cond.kind { - ExprKind::Unary(UnOp::Not, e) => (e, ""), - _ => (cond, "!"), - }; - let cond_sugg = - sugg::Sugg::hir_with_context(cx, cond, expr.span.ctxt(), "..", &mut applicability).maybe_paren(); + let cond_sugg = !sugg::Sugg::hir_with_context(cx, cond, expr.span.ctxt(), "..", &mut applicability); let semicolon = if is_parent_stmt(cx, expr.hir_id) { ";" } else { "" }; - let sugg = format!("assert!({not}{cond_sugg}, {format_args_snip}){semicolon}"); + let sugg = format!("assert!({cond_sugg}, {format_args_snip}){semicolon}"); // we show to the user the suggestion without the comments, but when applying the fix, include the // comments in the block span_lint_and_then( diff --git a/clippy_lints/src/manual_async_fn.rs b/clippy_lints/src/manual_async_fn.rs index abd1ac954cd..ba1ad599e11 100644 --- a/clippy_lints/src/manual_async_fn.rs +++ b/clippy_lints/src/manual_async_fn.rs @@ -83,7 +83,7 @@ impl<'tcx> LateLintPass<'tcx> for ManualAsyncFn { format!("{} async {}", vis_snip, &header_snip[vis_snip.len() + 1..ret_pos]) }; - let body_snip = snippet_block(cx, closure_body.value.span, "..", Some(block.span)).to_string(); + let body_snip = snippet_block(cx, closure_body.value.span, "..", Some(block.span)); diag.multipart_suggestion( "make the function `async` and return the output of the future directly", diff --git a/clippy_lints/src/manual_non_exhaustive.rs b/clippy_lints/src/manual_non_exhaustive.rs index 6b0f7446849..0d783fde331 100644 --- a/clippy_lints/src/manual_non_exhaustive.rs +++ b/clippy_lints/src/manual_non_exhaustive.rs @@ -4,12 +4,11 @@ use clippy_utils::is_doc_hidden; use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::snippet_indent; use itertools::Itertools; -use rustc_hir::attrs::{AttributeKind}; -use rustc_hir::find_attr; use rustc_data_structures::fx::FxHashSet; use rustc_errors::Applicability; +use rustc_hir::attrs::AttributeKind; use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res}; -use rustc_hir::{Expr, ExprKind, Item, ItemKind, QPath, TyKind, VariantData}; +use rustc_hir::{Expr, ExprKind, Item, ItemKind, QPath, TyKind, VariantData, find_attr}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::impl_lint_pass; use rustc_span::Span; diff --git a/clippy_lints/src/matches/match_single_binding.rs b/clippy_lints/src/matches/match_single_binding.rs index 6a76c6cedd0..82d5310663e 100644 --- a/clippy_lints/src/matches/match_single_binding.rs +++ b/clippy_lints/src/matches/match_single_binding.rs @@ -1,11 +1,16 @@ +use std::ops::ControlFlow; + use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::macros::HirNode; -use clippy_utils::source::{indent_of, snippet, snippet_block_with_context, snippet_with_context}; -use clippy_utils::{is_refutable, peel_blocks}; +use clippy_utils::source::{indent_of, reindent_multiline, snippet, snippet_block_with_context, snippet_with_context}; +use clippy_utils::{is_expr_identity_of_pat, is_refutable, peel_blocks}; +use rustc_data_structures::fx::FxHashSet; use rustc_errors::Applicability; -use rustc_hir::{Arm, Expr, ExprKind, Node, PatKind, StmtKind}; +use rustc_hir::def::Res; +use rustc_hir::intravisit::{Visitor, walk_block, walk_expr, walk_path, walk_stmt}; +use rustc_hir::{Arm, Block, Expr, ExprKind, HirId, Node, PatKind, Path, Stmt, StmtKind}; use rustc_lint::LateContext; -use rustc_span::Span; +use rustc_span::{Span, Symbol}; use super::MATCH_SINGLE_BINDING; @@ -26,9 +31,7 @@ pub(crate) fn check<'a>(cx: &LateContext<'a>, ex: &Expr<'a>, arms: &[Arm<'_>], e let match_body = peel_blocks(arms[0].body); let mut app = Applicability::MaybeIncorrect; let ctxt = expr.span.ctxt(); - let mut snippet_body = snippet_block_with_context(cx, match_body.span, ctxt, "..", Some(expr.span), &mut app) - .0 - .to_string(); + let mut snippet_body = snippet_block_with_context(cx, match_body.span, ctxt, "..", Some(expr.span), &mut app).0; // Do we need to add ';' to suggestion ? if let Node::Stmt(stmt) = cx.tcx.parent_hir_node(expr.hir_id) @@ -50,10 +53,11 @@ pub(crate) fn check<'a>(cx: &LateContext<'a>, ex: &Expr<'a>, arms: &[Arm<'_>], e cx, (ex, expr), (bind_names, matched_vars), - &snippet_body, + snippet_body, &mut app, Some(span), true, + is_var_binding_used_later(cx, expr, &arms[0]), ); span_lint_and_sugg( @@ -78,15 +82,28 @@ pub(crate) fn check<'a>(cx: &LateContext<'a>, ex: &Expr<'a>, arms: &[Arm<'_>], e snippet_with_context(cx, pat_span, ctxt, "..", &mut app).0 ), ), + None if is_expr_identity_of_pat(cx, arms[0].pat, ex, false) => { + span_lint_and_sugg( + cx, + MATCH_SINGLE_BINDING, + expr.span, + "this match could be replaced by its body itself", + "consider using the match body instead", + snippet_body, + Applicability::MachineApplicable, + ); + return; + }, None => { let sugg = sugg_with_curlies( cx, (ex, expr), (bind_names, matched_vars), - &snippet_body, + snippet_body, &mut app, None, true, + is_var_binding_used_later(cx, expr, &arms[0]), ); (expr.span, sugg) }, @@ -108,10 +125,11 @@ pub(crate) fn check<'a>(cx: &LateContext<'a>, ex: &Expr<'a>, arms: &[Arm<'_>], e cx, (ex, expr), (bind_names, matched_vars), - &snippet_body, + snippet_body, &mut app, None, false, + true, ); span_lint_and_sugg( @@ -139,6 +157,125 @@ pub(crate) fn check<'a>(cx: &LateContext<'a>, ex: &Expr<'a>, arms: &[Arm<'_>], e } } +struct VarBindingVisitor<'a, 'tcx> { + cx: &'a LateContext<'tcx>, + identifiers: FxHashSet, +} + +impl<'tcx> Visitor<'tcx> for VarBindingVisitor<'_, 'tcx> { + type Result = ControlFlow<()>; + + fn visit_path(&mut self, path: &Path<'tcx>, _: HirId) -> Self::Result { + if let Res::Local(_) = path.res + && let [segment] = path.segments + && self.identifiers.contains(&segment.ident.name) + { + return ControlFlow::Break(()); + } + + walk_path(self, path) + } + + fn visit_block(&mut self, block: &'tcx Block<'tcx>) -> Self::Result { + let before = self.identifiers.clone(); + walk_block(self, block)?; + self.identifiers = before; + ControlFlow::Continue(()) + } + + fn visit_stmt(&mut self, stmt: &'tcx Stmt<'tcx>) -> Self::Result { + if let StmtKind::Let(let_stmt) = stmt.kind { + if let Some(init) = let_stmt.init { + self.visit_expr(init)?; + } + + let_stmt.pat.each_binding(|_, _, _, ident| { + self.identifiers.remove(&ident.name); + }); + } + walk_stmt(self, stmt) + } + + fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) -> Self::Result { + match expr.kind { + ExprKind::If( + Expr { + kind: ExprKind::Let(let_expr), + .. + }, + then, + else_, + ) => { + self.visit_expr(let_expr.init)?; + let before = self.identifiers.clone(); + let_expr.pat.each_binding(|_, _, _, ident| { + self.identifiers.remove(&ident.name); + }); + + self.visit_expr(then)?; + self.identifiers = before; + if let Some(else_) = else_ { + self.visit_expr(else_)?; + } + ControlFlow::Continue(()) + }, + ExprKind::Closure(closure) => { + let body = self.cx.tcx.hir_body(closure.body); + let before = self.identifiers.clone(); + for param in body.params { + param.pat.each_binding(|_, _, _, ident| { + self.identifiers.remove(&ident.name); + }); + } + self.visit_expr(body.value)?; + self.identifiers = before; + ControlFlow::Continue(()) + }, + ExprKind::Match(expr, arms, _) => { + self.visit_expr(expr)?; + for arm in arms { + let before = self.identifiers.clone(); + arm.pat.each_binding(|_, _, _, ident| { + self.identifiers.remove(&ident.name); + }); + if let Some(guard) = arm.guard { + self.visit_expr(guard)?; + } + self.visit_expr(arm.body)?; + self.identifiers = before; + } + ControlFlow::Continue(()) + }, + _ => walk_expr(self, expr), + } + } +} + +fn is_var_binding_used_later(cx: &LateContext<'_>, expr: &Expr<'_>, arm: &Arm<'_>) -> bool { + let Node::Stmt(stmt) = cx.tcx.parent_hir_node(expr.hir_id) else { + return false; + }; + let Node::Block(block) = cx.tcx.parent_hir_node(stmt.hir_id) else { + return false; + }; + + let mut identifiers = FxHashSet::default(); + arm.pat.each_binding(|_, _, _, ident| { + identifiers.insert(ident.name); + }); + + let mut visitor = VarBindingVisitor { cx, identifiers }; + block + .stmts + .iter() + .skip_while(|s| s.hir_id != stmt.hir_id) + .skip(1) + .any(|stmt| matches!(visitor.visit_stmt(stmt), ControlFlow::Break(()))) + || block + .expr + .is_some_and(|expr| matches!(visitor.visit_expr(expr), ControlFlow::Break(()))) +} + /// Returns true if the `ex` match expression is in a local (`let`) or assign expression fn opt_parent_assign_span<'a>(cx: &LateContext<'a>, ex: &Expr<'a>) -> Option { if let Node::Expr(parent_arm_expr) = cx.tcx.parent_hir_node(ex.hir_id) { @@ -161,47 +298,66 @@ fn opt_parent_assign_span<'a>(cx: &LateContext<'a>, ex: &Expr<'a>) -> Option(cx: &LateContext<'a>, match_expr: &Expr<'a>) -> bool { +fn expr_in_nested_block(cx: &LateContext<'_>, match_expr: &Expr<'_>) -> bool { + if let Node::Block(block) = cx.tcx.parent_hir_node(match_expr.hir_id) { + return block + .expr + .map_or_else(|| matches!(block.stmts, [_]), |_| block.stmts.is_empty()); + } + false +} + +fn expr_must_have_curlies(cx: &LateContext<'_>, match_expr: &Expr<'_>) -> bool { let parent = cx.tcx.parent_hir_node(match_expr.hir_id); - matches!( - parent, - Node::Expr(Expr { - kind: ExprKind::Closure { .. }, - .. - }) | Node::AnonConst(..) + if let Node::Expr(Expr { + kind: ExprKind::Closure(..) | ExprKind::Binary(..), + .. + }) + | Node::AnonConst(..) = parent + { + return true; + } + + if let Node::Arm(arm) = &cx.tcx.parent_hir_node(match_expr.hir_id) + && let ExprKind::Match(..) = arm.body.kind + { + return true; + } + + false +} + +fn indent_of_nth_line(snippet: &str, nth: usize) -> Option { + snippet + .lines() + .nth(nth) + .and_then(|s| s.find(|c: char| !c.is_whitespace())) +} + +fn reindent_snippet_if_in_block(snippet_body: &str, has_assignment: bool) -> String { + if has_assignment || !snippet_body.starts_with('{') { + return reindent_multiline(snippet_body, true, indent_of_nth_line(snippet_body, 1)); + } + + let snippet_body = snippet_body.trim_start_matches('{').trim_end_matches('}').trim(); + reindent_multiline( + snippet_body, + false, + indent_of_nth_line(snippet_body, 0).map(|indent| indent.saturating_sub(4)), ) } +#[expect(clippy::too_many_arguments)] fn sugg_with_curlies<'a>( cx: &LateContext<'a>, (ex, match_expr): (&Expr<'a>, &Expr<'a>), (bind_names, matched_vars): (Span, Span), - snippet_body: &str, + mut snippet_body: String, applicability: &mut Applicability, assignment: Option, needs_var_binding: bool, + is_var_binding_used_later: bool, ) -> String { - let mut indent = " ".repeat(indent_of(cx, ex.span).unwrap_or(0)); - - let (mut cbrace_start, mut cbrace_end) = (String::new(), String::new()); - if expr_parent_requires_curlies(cx, match_expr) { - cbrace_end = format!("\n{indent}}}"); - // Fix body indent due to the closure - indent = " ".repeat(indent_of(cx, bind_names).unwrap_or(0)); - cbrace_start = format!("{{\n{indent}"); - } - - // If the parent is already an arm, and the body is another match statement, - // we need curly braces around suggestion - if let Node::Arm(arm) = &cx.tcx.parent_hir_node(match_expr.hir_id) - && let ExprKind::Match(..) = arm.body.kind - { - cbrace_end = format!("\n{indent}}}"); - // Fix body indent due to the match - indent = " ".repeat(indent_of(cx, bind_names).unwrap_or(0)); - cbrace_start = format!("{{\n{indent}"); - } - let assignment_str = assignment.map_or_else(String::new, |span| { let mut s = snippet(cx, span, "..").to_string(); s.push_str(" = "); @@ -221,5 +377,17 @@ fn sugg_with_curlies<'a>( .to_string() }; + let mut indent = " ".repeat(indent_of(cx, ex.span).unwrap_or(0)); + let (mut cbrace_start, mut cbrace_end) = (String::new(), String::new()); + if !expr_in_nested_block(cx, match_expr) + && ((needs_var_binding && is_var_binding_used_later) || expr_must_have_curlies(cx, match_expr)) + { + cbrace_end = format!("\n{indent}}}"); + // Fix body indent due to the closure + indent = " ".repeat(indent_of(cx, bind_names).unwrap_or(0)); + cbrace_start = format!("{{\n{indent}"); + snippet_body = reindent_snippet_if_in_block(&snippet_body, !assignment_str.is_empty()); + } + format!("{cbrace_start}{scrutinee};\n{indent}{assignment_str}{snippet_body}{cbrace_end}") } diff --git a/clippy_lints/src/matches/single_match.rs b/clippy_lints/src/matches/single_match.rs index 7e530e98ac4..bcf079b7007 100644 --- a/clippy_lints/src/matches/single_match.rs +++ b/clippy_lints/src/matches/single_match.rs @@ -112,9 +112,7 @@ fn report_single_pattern( let (sugg, help) = if is_unit_expr(arm.body) { (String::new(), "`match` expression can be removed") } else { - let mut sugg = snippet_block_with_context(cx, arm.body.span, ctxt, "..", Some(expr.span), &mut app) - .0 - .to_string(); + let mut sugg = snippet_block_with_context(cx, arm.body.span, ctxt, "..", Some(expr.span), &mut app).0; if let Node::Stmt(stmt) = cx.tcx.parent_hir_node(expr.hir_id) && let StmtKind::Expr(_) = stmt.kind && match arm.body.kind { @@ -127,7 +125,7 @@ fn report_single_pattern( (sugg, "try") }; span_lint_and_then(cx, lint, expr.span, msg, |diag| { - diag.span_suggestion(expr.span, help, sugg.to_string(), app); + diag.span_suggestion(expr.span, help, sugg, app); note(diag); }); return; @@ -188,7 +186,7 @@ fn report_single_pattern( }; span_lint_and_then(cx, lint, expr.span, msg, |diag| { - diag.span_suggestion(expr.span, "try", sugg.to_string(), app); + diag.span_suggestion(expr.span, "try", sugg, app); note(diag); }); } diff --git a/clippy_lints/src/methods/implicit_clone.rs b/clippy_lints/src/methods/implicit_clone.rs index efa8cee58df..8a976d1b4dc 100644 --- a/clippy_lints/src/methods/implicit_clone.rs +++ b/clippy_lints/src/methods/implicit_clone.rs @@ -40,14 +40,12 @@ pub fn check(cx: &LateContext<'_>, method_name: Symbol, expr: &hir::Expr<'_>, re } /// Returns true if the named method can be used to clone the receiver. -/// Note that `to_string` is not flagged by `implicit_clone`. So other lints that call -/// `is_clone_like` and that do flag `to_string` must handle it separately. See, e.g., -/// `is_to_owned_like` in `unnecessary_to_owned.rs`. pub fn is_clone_like(cx: &LateContext<'_>, method_name: Symbol, method_def_id: hir::def_id::DefId) -> bool { match method_name { sym::to_os_string => is_diag_item_method(cx, method_def_id, sym::OsStr), sym::to_owned => is_diag_trait_item(cx, method_def_id, sym::ToOwned), sym::to_path_buf => is_diag_item_method(cx, method_def_id, sym::Path), + sym::to_string => is_diag_trait_item(cx, method_def_id, sym::ToString), sym::to_vec => cx .tcx .impl_of_assoc(method_def_id) diff --git a/clippy_lints/src/methods/ip_constant.rs b/clippy_lints/src/methods/ip_constant.rs index 83803fba6a1..a2ac4e54334 100644 --- a/clippy_lints/src/methods/ip_constant.rs +++ b/clippy_lints/src/methods/ip_constant.rs @@ -1,7 +1,7 @@ use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::span_lint_and_then; use rustc_errors::Applicability; -use rustc_hir::{Expr, ExprKind, QPath, Ty, TyKind}; +use rustc_hir::{Expr, ExprKind, QPath, TyKind}; use rustc_lint::LateContext; use rustc_span::sym; use smallvec::SmallVec; @@ -9,13 +9,8 @@ use smallvec::SmallVec; use super::IP_CONSTANT; pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, func: &Expr<'_>, args: &[Expr<'_>]) { - if let ExprKind::Path(QPath::TypeRelative( - Ty { - kind: TyKind::Path(QPath::Resolved(_, func_path)), - .. - }, - p, - )) = func.kind + if let ExprKind::Path(QPath::TypeRelative(ty, p)) = func.kind + && let TyKind::Path(QPath::Resolved(_, func_path)) = ty.kind && p.ident.name == sym::new && let Some(func_def_id) = func_path.res.opt_def_id() && matches!( @@ -40,13 +35,15 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, func: &Expr<'_>, args _ => return, }; + let mut sugg = vec![(expr.span.with_lo(p.ident.span.lo()), constant_name.to_owned())]; + let before_span = expr.span.shrink_to_lo().until(ty.span); + if !before_span.is_empty() { + // Remove everything before the type name + sugg.push((before_span, String::new())); + } + span_lint_and_then(cx, IP_CONSTANT, expr.span, "hand-coded well-known IP address", |diag| { - diag.span_suggestion_verbose( - expr.span.with_lo(p.ident.span.lo()), - "use", - constant_name, - Applicability::MachineApplicable, - ); + diag.multipart_suggestion_verbose("use", sugg, Applicability::MachineApplicable); }); } } diff --git a/clippy_lints/src/methods/iter_on_single_or_empty_collections.rs b/clippy_lints/src/methods/iter_on_single_or_empty_collections.rs index c0366765234..83e565562af 100644 --- a/clippy_lints/src/methods/iter_on_single_or_empty_collections.rs +++ b/clippy_lints/src/methods/iter_on_single_or_empty_collections.rs @@ -2,14 +2,15 @@ use std::iter::once; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet; +use clippy_utils::ty::{ExprFnSig, expr_sig, ty_sig}; use clippy_utils::{get_expr_use_or_unification_node, is_res_lang_ctor, path_res, std_or_core, sym}; use rustc_errors::Applicability; use rustc_hir::LangItem::{OptionNone, OptionSome}; -use rustc_hir::def_id::DefId; use rustc_hir::hir_id::HirId; use rustc_hir::{Expr, ExprKind, Node}; use rustc_lint::LateContext; +use rustc_middle::ty::Binder; use rustc_span::Symbol; use super::{ITER_ON_EMPTY_COLLECTIONS, ITER_ON_SINGLE_ITEMS}; @@ -32,24 +33,34 @@ impl IterType { fn is_arg_ty_unified_in_fn<'tcx>( cx: &LateContext<'tcx>, - fn_id: DefId, + fn_sig: ExprFnSig<'tcx>, arg_id: HirId, args: impl IntoIterator>, + is_method: bool, ) -> bool { - let fn_sig = cx.tcx.fn_sig(fn_id).instantiate_identity(); let arg_id_in_args = args.into_iter().position(|e| e.hir_id == arg_id).unwrap(); - let arg_ty_in_args = fn_sig.input(arg_id_in_args).skip_binder(); + let Some(arg_ty_in_args) = fn_sig.input(arg_id_in_args).map(Binder::skip_binder) else { + return false; + }; - cx.tcx.predicates_of(fn_id).predicates.iter().any(|(clause, _)| { - clause - .as_projection_clause() - .and_then(|p| p.map_bound(|p| p.term.as_type()).transpose()) - .is_some_and(|ty| ty.skip_binder() == arg_ty_in_args) - }) || fn_sig - .inputs() - .iter() - .enumerate() - .any(|(i, ty)| i != arg_id_in_args && ty.skip_binder().walk().any(|arg| arg.as_type() == Some(arg_ty_in_args))) + fn_sig + .predicates_id() + .map(|def_id| cx.tcx.predicates_of(def_id)) + .is_some_and(|generics| { + generics.predicates.iter().any(|(clause, _)| { + clause + .as_projection_clause() + .and_then(|p| p.map_bound(|p| p.term.as_type()).transpose()) + .is_some_and(|ty| ty.skip_binder() == arg_ty_in_args) + }) + }) + || (!is_method + && fn_sig.input(arg_id_in_args).is_some_and(|binder| { + binder + .skip_binder() + .walk() + .any(|arg| arg.as_type() == Some(arg_ty_in_args)) + })) } pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, method_name: Symbol, recv: &'tcx Expr<'tcx>) { @@ -70,25 +81,16 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, method let is_unified = match get_expr_use_or_unification_node(cx.tcx, expr) { Some((Node::Expr(parent), child_id)) => match parent.kind { ExprKind::If(e, _, _) | ExprKind::Match(e, _, _) if e.hir_id == child_id => false, - ExprKind::Call( - Expr { - kind: ExprKind::Path(path), - hir_id, - .. - }, - args, - ) => cx + ExprKind::Call(recv, args) => { + expr_sig(cx, recv).is_some_and(|fn_sig| is_arg_ty_unified_in_fn(cx, fn_sig, child_id, args, false)) + }, + ExprKind::MethodCall(_name, recv, args, _span) => cx .typeck_results() - .qpath_res(path, *hir_id) - .opt_def_id() - .filter(|fn_id| cx.tcx.def_kind(fn_id).is_fn_like()) - .is_some_and(|fn_id| is_arg_ty_unified_in_fn(cx, fn_id, child_id, args)), - ExprKind::MethodCall(_name, recv, args, _span) => is_arg_ty_unified_in_fn( - cx, - cx.typeck_results().type_dependent_def_id(parent.hir_id).unwrap(), - child_id, - once(recv).chain(args.iter()), - ), + .type_dependent_def_id(parent.hir_id) + .and_then(|def_id| ty_sig(cx, cx.tcx.type_of(def_id).instantiate_identity())) + .is_some_and(|fn_sig| { + is_arg_ty_unified_in_fn(cx, fn_sig, child_id, once(recv).chain(args.iter()), true) + }), ExprKind::If(_, _, _) | ExprKind::Match(_, _, _) | ExprKind::Closure(_) @@ -96,7 +98,8 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, method | ExprKind::Break(_, _) => true, _ => false, }, - Some((Node::Stmt(_) | Node::LetStmt(_), _)) => false, + Some((Node::LetStmt(let_stmt), _)) => let_stmt.ty.is_some(), + Some((Node::Stmt(_), _)) => false, _ => true, }; diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index bcd54557331..49ca81dafc2 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -5450,7 +5450,7 @@ impl Methods { implicit_clone::check(cx, name, expr, recv); } }, - (sym::to_os_string | sym::to_path_buf | sym::to_vec, []) => { + (sym::to_os_string | sym::to_path_buf | sym::to_string | sym::to_vec, []) => { implicit_clone::check(cx, name, expr, recv); }, (sym::type_id, []) => { diff --git a/clippy_lints/src/methods/unnecessary_map_or.rs b/clippy_lints/src/methods/unnecessary_map_or.rs index 4a9007c607c..1f5e3de6e7a 100644 --- a/clippy_lints/src/methods/unnecessary_map_or.rs +++ b/clippy_lints/src/methods/unnecessary_map_or.rs @@ -109,10 +109,16 @@ pub(super) fn check<'a>( ); let sugg = if let Some(parent_expr) = get_parent_expr(cx, expr) { - match parent_expr.kind { - ExprKind::Binary(..) | ExprKind::Unary(..) | ExprKind::Cast(..) => binop.maybe_paren(), - ExprKind::MethodCall(_, receiver, _, _) if receiver.hir_id == expr.hir_id => binop.maybe_paren(), - _ => binop, + if parent_expr.span.eq_ctxt(expr.span) { + match parent_expr.kind { + ExprKind::Binary(..) | ExprKind::Unary(..) | ExprKind::Cast(..) => binop.maybe_paren(), + ExprKind::MethodCall(_, receiver, _, _) if receiver.hir_id == expr.hir_id => binop.maybe_paren(), + _ => binop, + } + } else { + // if our parent expr is created by a macro, then it should be the one taking care of + // parenthesising us if necessary + binop } } else { binop diff --git a/clippy_lints/src/methods/unnecessary_sort_by.rs b/clippy_lints/src/methods/unnecessary_sort_by.rs index 1de9f6ab497..fa9d5332ff4 100644 --- a/clippy_lints/src/methods/unnecessary_sort_by.rs +++ b/clippy_lints/src/methods/unnecessary_sort_by.rs @@ -199,44 +199,50 @@ pub(super) fn check<'tcx>( is_unstable: bool, ) { match detect_lint(cx, expr, recv, arg) { - Some(LintTrigger::SortByKey(trigger)) => span_lint_and_sugg( - cx, - UNNECESSARY_SORT_BY, - expr.span, - "consider using `sort_by_key`", - "try", - format!( - "{}.sort{}_by_key(|{}| {})", - trigger.vec_name, - if is_unstable { "_unstable" } else { "" }, - trigger.closure_arg, - if let Some(std_or_core) = std_or_core(cx) - && trigger.reverse - { - format!("{}::cmp::Reverse({})", std_or_core, trigger.closure_body) + Some(LintTrigger::SortByKey(trigger)) => { + let method = if is_unstable { + "sort_unstable_by_key" + } else { + "sort_by_key" + }; + span_lint_and_sugg( + cx, + UNNECESSARY_SORT_BY, + expr.span, + format!("consider using `{method}`"), + "try", + format!( + "{}.{}(|{}| {})", + trigger.vec_name, + method, + trigger.closure_arg, + if let Some(std_or_core) = std_or_core(cx) + && trigger.reverse + { + format!("{}::cmp::Reverse({})", std_or_core, trigger.closure_body) + } else { + trigger.closure_body + }, + ), + if trigger.reverse { + Applicability::MaybeIncorrect } else { - trigger.closure_body.to_string() + Applicability::MachineApplicable }, - ), - if trigger.reverse { - Applicability::MaybeIncorrect - } else { - Applicability::MachineApplicable - }, - ), - Some(LintTrigger::Sort(trigger)) => span_lint_and_sugg( - cx, - UNNECESSARY_SORT_BY, - expr.span, - "consider using `sort`", - "try", - format!( - "{}.sort{}()", - trigger.vec_name, - if is_unstable { "_unstable" } else { "" }, - ), - Applicability::MachineApplicable, - ), + ); + }, + Some(LintTrigger::Sort(trigger)) => { + let method = if is_unstable { "sort_unstable" } else { "sort" }; + span_lint_and_sugg( + cx, + UNNECESSARY_SORT_BY, + expr.span, + format!("consider using `{method}`"), + "try", + format!("{}.{}()", trigger.vec_name, method), + Applicability::MachineApplicable, + ); + }, None => {}, } } diff --git a/clippy_lints/src/methods/unnecessary_to_owned.rs b/clippy_lints/src/methods/unnecessary_to_owned.rs index 54f45263275..c1f4904af7c 100644 --- a/clippy_lints/src/methods/unnecessary_to_owned.rs +++ b/clippy_lints/src/methods/unnecessary_to_owned.rs @@ -621,8 +621,8 @@ fn is_cloned_or_copied(cx: &LateContext<'_>, method_name: Symbol, method_def_id: /// Returns true if the named method can be used to convert the receiver to its "owned" /// representation. fn is_to_owned_like<'a>(cx: &LateContext<'a>, call_expr: &Expr<'a>, method_name: Symbol, method_def_id: DefId) -> bool { - is_clone_like(cx, method_name, method_def_id) - || is_cow_into_owned(cx, method_name, method_def_id) + is_cow_into_owned(cx, method_name, method_def_id) + || (method_name != sym::to_string && is_clone_like(cx, method_name, method_def_id)) || is_to_string_on_string_like(cx, call_expr, method_name, method_def_id) } diff --git a/clippy_lints/src/min_ident_chars.rs b/clippy_lints/src/min_ident_chars.rs index 99f01c8001a..dbce29a8631 100644 --- a/clippy_lints/src/min_ident_chars.rs +++ b/clippy_lints/src/min_ident_chars.rs @@ -4,10 +4,14 @@ use clippy_utils::is_from_proc_macro; use rustc_data_structures::fx::FxHashSet; use rustc_hir::def::{DefKind, Res}; use rustc_hir::intravisit::{Visitor, walk_item, walk_trait_item}; -use rustc_hir::{GenericParamKind, HirId, Item, ItemKind, ItemLocalId, Node, Pat, PatKind, TraitItem, UsePath}; +use rustc_hir::{ + GenericParamKind, HirId, Impl, ImplItem, ImplItemKind, Item, ItemKind, ItemLocalId, Node, Pat, PatKind, TraitItem, + UsePath, +}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_session::impl_lint_pass; -use rustc_span::Span; +use rustc_span::symbol::Ident; +use rustc_span::{Span, Symbol}; use std::borrow::Cow; declare_clippy_lint! { @@ -32,6 +36,10 @@ declare_clippy_lint! { /// let title = movie.title; /// } /// ``` + /// + /// ### Limitations + /// Trait implementations which use the same function or parameter name as the trait declaration will + /// not be warned about, even if the name is below the configured limit. #[clippy::version = "1.72.0"] pub MIN_IDENT_CHARS, restriction, @@ -76,6 +84,18 @@ impl LateLintPass<'_> for MinIdentChars { return; } + // If the function is declared but not defined in a trait, check_pat isn't called so we need to + // check this explicitly + if matches!(&item.kind, rustc_hir::TraitItemKind::Fn(_, _)) { + let param_names = cx.tcx.fn_arg_idents(item.owner_id.to_def_id()); + for ident in param_names.iter().flatten() { + let str = ident.as_str(); + if self.is_ident_too_short(cx, str, ident.span) { + emit_min_ident_chars(self, cx, str, ident.span); + } + } + } + walk_trait_item(&mut IdentVisitor { conf: self, cx }, item); } @@ -84,6 +104,7 @@ impl LateLintPass<'_> for MinIdentChars { if let PatKind::Binding(_, _, ident, ..) = pat.kind && let str = ident.as_str() && self.is_ident_too_short(cx, str, ident.span) + && is_not_in_trait_impl(cx, pat, ident) { emit_min_ident_chars(self, cx, str, ident.span); } @@ -118,6 +139,11 @@ impl Visitor<'_> for IdentVisitor<'_, '_> { let str = ident.as_str(); if conf.is_ident_too_short(cx, str, ident.span) { + // Check whether the node is part of a `impl` for a trait. + if matches!(cx.tcx.parent_hir_node(hir_id), Node::TraitRef(_)) { + return; + } + // Check whether the node is part of a `use` statement. We don't want to emit a warning if the user // has no control over the type. let usenode = opt_as_use_node(node).or_else(|| { @@ -201,3 +227,52 @@ fn opt_as_use_node(node: Node<'_>) -> Option<&'_ UsePath<'_>> { None } } + +/// Check if a pattern is a function param in an impl block for a trait and that the param name is +/// the same than in the trait definition. +fn is_not_in_trait_impl(cx: &LateContext<'_>, pat: &Pat<'_>, ident: Ident) -> bool { + let parent_node = cx.tcx.parent_hir_node(pat.hir_id); + if !matches!(parent_node, Node::Param(_)) { + return true; + } + + for (_, parent_node) in cx.tcx.hir_parent_iter(pat.hir_id) { + if let Node::ImplItem(impl_item) = parent_node + && matches!(impl_item.kind, ImplItemKind::Fn(_, _)) + { + let impl_parent_node = cx.tcx.parent_hir_node(impl_item.hir_id()); + if let Node::Item(parent_item) = impl_parent_node + && let ItemKind::Impl(Impl { of_trait: Some(_), .. }) = &parent_item.kind + && let Some(name) = get_param_name(impl_item, cx, ident) + { + return name != ident.name; + } + + return true; + } + } + + true +} + +fn get_param_name(impl_item: &ImplItem<'_>, cx: &LateContext<'_>, ident: Ident) -> Option { + if let Some(trait_item_def_id) = impl_item.trait_item_def_id { + let trait_param_names = cx.tcx.fn_arg_idents(trait_item_def_id); + + let ImplItemKind::Fn(_, body_id) = impl_item.kind else { + return None; + }; + + if let Some(param_index) = cx + .tcx + .hir_body_param_idents(body_id) + .position(|param_ident| param_ident.is_some_and(|param_ident| param_ident.span == ident.span)) + && let Some(trait_param_name) = trait_param_names.get(param_index) + && let Some(trait_param_ident) = trait_param_name + { + return Some(trait_param_ident.name); + } + } + + None +} diff --git a/clippy_lints/src/missing_inline.rs b/clippy_lints/src/missing_inline.rs index c637fb247ff..d02952eb487 100644 --- a/clippy_lints/src/missing_inline.rs +++ b/clippy_lints/src/missing_inline.rs @@ -1,8 +1,7 @@ use clippy_utils::diagnostics::span_lint; -use rustc_hir::attrs::{AttributeKind}; -use rustc_hir::find_attr; +use rustc_hir::attrs::AttributeKind; use rustc_hir::def_id::DefId; -use rustc_hir::{self as hir, Attribute}; +use rustc_hir::{self as hir, Attribute, find_attr}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::ty::AssocItemContainer; use rustc_session::declare_lint_pass; diff --git a/clippy_lints/src/needless_bool.rs b/clippy_lints/src/needless_bool.rs index b3aa1a7286a..fa5afcc0087 100644 --- a/clippy_lints/src/needless_bool.rs +++ b/clippy_lints/src/needless_bool.rs @@ -3,7 +3,7 @@ use clippy_utils::source::snippet_with_applicability; use clippy_utils::sugg::Sugg; use clippy_utils::{ SpanlessEq, get_parent_expr, higher, is_block_like, is_else_clause, is_expn_of, is_parent_stmt, - is_receiver_of_method_call, peel_blocks, peel_blocks_with_stmt, span_extract_comment, sym, + is_receiver_of_method_call, peel_blocks, peel_blocks_with_stmt, span_contains_comment, sym, }; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; @@ -128,14 +128,13 @@ fn condition_needs_parentheses(e: &Expr<'_>) -> bool { impl<'tcx> LateLintPass<'tcx> for NeedlessBool { fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) { use self::Expression::{Bool, RetBool}; - if e.span.from_expansion() || !span_extract_comment(cx.tcx.sess.source_map(), e.span).is_empty() { - return; - } - if let Some(higher::If { - cond, - then, - r#else: Some(r#else), - }) = higher::If::hir(e) + if !e.span.from_expansion() + && let Some(higher::If { + cond, + then, + r#else: Some(else_expr), + }) = higher::If::hir(e) + && !span_contains_comment(cx.tcx.sess.source_map(), e.span) { let reduce = |ret, not| { let mut applicability = Applicability::MachineApplicable; @@ -167,7 +166,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessBool { applicability, ); }; - if let Some((a, b)) = fetch_bool_block(then).and_then(|a| Some((a, fetch_bool_block(r#else)?))) { + if let Some((a, b)) = fetch_bool_block(then).and_then(|a| Some((a, fetch_bool_block(else_expr)?))) { match (a, b) { (RetBool(true), RetBool(true)) | (Bool(true), Bool(true)) => { span_lint( @@ -193,7 +192,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessBool { } } if let Some((lhs_a, a)) = fetch_assign(then) - && let Some((lhs_b, b)) = fetch_assign(r#else) + && let Some((lhs_b, b)) = fetch_assign(else_expr) && SpanlessEq::new(cx).eq_expr(lhs_a, lhs_b) { let mut applicability = Applicability::MachineApplicable; diff --git a/clippy_lints/src/needless_pass_by_value.rs b/clippy_lints/src/needless_pass_by_value.rs index 7b057998063..32ded96c123 100644 --- a/clippy_lints/src/needless_pass_by_value.rs +++ b/clippy_lints/src/needless_pass_by_value.rs @@ -311,9 +311,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByValue { /// Functions marked with these attributes must have the exact signature. pub(crate) fn requires_exact_signature(attrs: &[Attribute]) -> bool { - attrs.iter().any(|attr| { - attr.is_proc_macro_attr() - }) + attrs.iter().any(Attribute::is_proc_macro_attr) } #[derive(Default)] diff --git a/clippy_lints/src/no_mangle_with_rust_abi.rs b/clippy_lints/src/no_mangle_with_rust_abi.rs index 791bbbe30a8..daa2c287794 100644 --- a/clippy_lints/src/no_mangle_with_rust_abi.rs +++ b/clippy_lints/src/no_mangle_with_rust_abi.rs @@ -1,8 +1,8 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::{snippet, snippet_with_applicability}; use rustc_abi::ExternAbi; -use rustc_hir::attrs::AttributeKind; use rustc_errors::Applicability; +use rustc_hir::attrs::AttributeKind; use rustc_hir::{Attribute, Item, ItemKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; diff --git a/clippy_lints/src/non_std_lazy_statics.rs b/clippy_lints/src/non_std_lazy_statics.rs index abee3c44c5a..7ecde40aee0 100644 --- a/clippy_lints/src/non_std_lazy_statics.rs +++ b/clippy_lints/src/non_std_lazy_statics.rs @@ -49,7 +49,7 @@ declare_clippy_lint! { /// Some functions could be replaced as well if we have replaced `Lazy` to `LazyLock`, /// therefore after suggesting replace the type, we need to make sure the function calls can be /// replaced, otherwise the suggestions cannot be applied thus the applicability should be -/// `Unspecified` or `MaybeIncorret`. +/// [`Applicability::Unspecified`] or [`Applicability::MaybeIncorrect`]. static FUNCTION_REPLACEMENTS: &[(&str, Option<&str>)] = &[ ("once_cell::sync::Lazy::force", Some("std::sync::LazyLock::force")), ("once_cell::sync::Lazy::get", None), // `std::sync::LazyLock::get` is experimental diff --git a/clippy_lints/src/option_if_let_else.rs b/clippy_lints/src/option_if_let_else.rs index 9487cec87ef..3483f3081a5 100644 --- a/clippy_lints/src/option_if_let_else.rs +++ b/clippy_lints/src/option_if_let_else.rs @@ -127,7 +127,8 @@ fn try_get_option_occurrence<'tcx>( if_else: &'tcx Expr<'_>, ) -> Option { let cond_expr = match expr.kind { - ExprKind::Unary(UnOp::Deref, inner_expr) | ExprKind::AddrOf(_, _, inner_expr) => inner_expr, + ExprKind::AddrOf(_, _, inner_expr) => inner_expr, + ExprKind::Unary(UnOp::Deref, inner_expr) if !cx.typeck_results().expr_ty(inner_expr).is_raw_ptr() => inner_expr, _ => expr, }; let (inner_pat, is_result) = try_get_inner_pat_and_is_result(cx, pat)?; @@ -223,8 +224,8 @@ fn try_get_option_occurrence<'tcx>( let mut app = Applicability::Unspecified; - let (none_body, is_argless_call) = match none_body.kind { - ExprKind::Call(call_expr, []) if !none_body.span.from_expansion() => (call_expr, true), + let (none_body, can_omit_arg) = match none_body.kind { + ExprKind::Call(call_expr, []) if !none_body.span.from_expansion() && !is_result => (call_expr, true), _ => (none_body, false), }; @@ -241,7 +242,7 @@ fn try_get_option_occurrence<'tcx>( ), none_expr: format!( "{}{}", - if method_sugg == "map_or" || is_argless_call { + if method_sugg == "map_or" || can_omit_arg { "" } else if is_result { "|_| " diff --git a/clippy_lints/src/pass_by_ref_or_value.rs b/clippy_lints/src/pass_by_ref_or_value.rs index 303c5dfed89..d7b4a03aa53 100644 --- a/clippy_lints/src/pass_by_ref_or_value.rs +++ b/clippy_lints/src/pass_by_ref_or_value.rs @@ -5,13 +5,12 @@ use clippy_utils::ty::{for_each_top_level_late_bound_region, is_copy}; use clippy_utils::{is_self, is_self_ty}; use core::ops::ControlFlow; use rustc_abi::ExternAbi; -use rustc_hir::attrs::{AttributeKind, InlineAttr}; -use rustc_hir::find_attr; use rustc_data_structures::fx::FxHashSet; use rustc_errors::Applicability; use rustc_hir as hir; +use rustc_hir::attrs::{AttributeKind, InlineAttr}; use rustc_hir::intravisit::FnKind; -use rustc_hir::{BindingMode, Body, FnDecl, Impl, ItemKind, MutTy, Mutability, Node, PatKind}; +use rustc_hir::{BindingMode, Body, FnDecl, Impl, ItemKind, MutTy, Mutability, Node, PatKind, find_attr}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::adjustment::{Adjust, PointerCoercion}; use rustc_middle::ty::layout::LayoutOf; diff --git a/clippy_lints/src/redundant_else.rs b/clippy_lints/src/redundant_else.rs index a3be16ed858..79353dc9247 100644 --- a/clippy_lints/src/redundant_else.rs +++ b/clippy_lints/src/redundant_else.rs @@ -97,7 +97,7 @@ impl EarlyLintPass for RedundantElse { els.span.with_lo(then.span.hi()), "redundant else block", "remove the `else` block and move the contents out", - make_sugg(cx, els.span, "..", Some(expr.span)).to_string(), + make_sugg(cx, els.span, "..", Some(expr.span)), app, ); } diff --git a/clippy_lints/src/return_self_not_must_use.rs b/clippy_lints/src/return_self_not_must_use.rs index 2cdb8ef3a65..b057396034c 100644 --- a/clippy_lints/src/return_self_not_must_use.rs +++ b/clippy_lints/src/return_self_not_must_use.rs @@ -1,11 +1,10 @@ use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::ty::is_must_use_ty; use clippy_utils::{nth_arg, return_ty}; -use rustc_hir::attrs::{AttributeKind}; -use rustc_hir::find_attr; +use rustc_hir::attrs::AttributeKind; use rustc_hir::def_id::LocalDefId; use rustc_hir::intravisit::FnKind; -use rustc_hir::{Body, FnDecl, OwnerId, TraitItem, TraitItemKind}; +use rustc_hir::{Body, FnDecl, OwnerId, TraitItem, TraitItemKind, find_attr}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_session::declare_lint_pass; use rustc_span::Span; diff --git a/clippy_lints/src/semicolon_block.rs b/clippy_lints/src/semicolon_block.rs index f6c128d4c52..db91c57b181 100644 --- a/clippy_lints/src/semicolon_block.rs +++ b/clippy_lints/src/semicolon_block.rs @@ -102,7 +102,7 @@ impl SemicolonBlock { } fn semicolon_outside_block(&self, cx: &LateContext<'_>, block: &Block<'_>, tail_stmt_expr: &Expr<'_>) { - let insert_span = block.span.with_lo(block.span.hi()); + let insert_span = block.span.shrink_to_hi(); // For macro call semicolon statements (`mac!();`), the statement's span does not actually // include the semicolon itself, so use `mac_call_stmt_semi_span`, which finds the semicolon @@ -144,28 +144,20 @@ impl LateLintPass<'_> for SemicolonBlock { kind: ExprKind::Block(block, _), .. }) if !block.span.from_expansion() && stmt.span.contains(block.span) => { - let Block { - expr: None, - stmts: [.., stmt], - .. - } = block - else { - return; - }; - let &Stmt { - kind: StmtKind::Semi(expr), - .. - } = stmt - else { - return; - }; - self.semicolon_outside_block(cx, block, expr); + if block.expr.is_none() + && let [.., stmt] = block.stmts + && let StmtKind::Semi(expr) = stmt.kind + { + self.semicolon_outside_block(cx, block, expr); + } }, StmtKind::Semi(Expr { - kind: ExprKind::Block(block @ Block { expr: Some(tail), .. }, _), + kind: ExprKind::Block(block, _), .. }) if !block.span.from_expansion() => { - self.semicolon_inside_block(cx, block, tail, stmt.span); + if let Some(tail) = block.expr { + self.semicolon_inside_block(cx, block, tail, stmt.span); + } }, _ => (), } @@ -173,9 +165,5 @@ impl LateLintPass<'_> for SemicolonBlock { } fn get_line(cx: &LateContext<'_>, span: Span) -> Option { - if let Ok(line) = cx.sess().source_map().lookup_line(span.lo()) { - return Some(line.line); - } - - None + cx.sess().source_map().lookup_line(span.lo()).ok().map(|line| line.line) } diff --git a/clippy_lints/src/std_instead_of_core.rs b/clippy_lints/src/std_instead_of_core.rs index 50c44a8e75c..e9534bc63a6 100644 --- a/clippy_lints/src/std_instead_of_core.rs +++ b/clippy_lints/src/std_instead_of_core.rs @@ -2,11 +2,10 @@ use clippy_config::Conf; use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg}; use clippy_utils::is_from_proc_macro; use clippy_utils::msrvs::Msrv; -use rustc_hir::{StabilityLevel, StableSince}; use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::DefId; -use rustc_hir::{Block, Body, HirId, Path, PathSegment}; +use rustc_hir::{Block, Body, HirId, Path, PathSegment, StabilityLevel, StableSince}; use rustc_lint::{LateContext, LateLintPass, Lint, LintContext}; use rustc_session::impl_lint_pass; use rustc_span::symbol::kw; diff --git a/clippy_lints/src/strings.rs b/clippy_lints/src/strings.rs index 1cda6f596f4..490e6c974ae 100644 --- a/clippy_lints/src/strings.rs +++ b/clippy_lints/src/strings.rs @@ -13,8 +13,6 @@ use rustc_middle::ty; use rustc_session::declare_lint_pass; use rustc_span::source_map::Spanned; -use std::ops::ControlFlow; - declare_clippy_lint! { /// ### What it does /// Checks for string appends of the form `x = x + y` (without @@ -411,125 +409,6 @@ impl<'tcx> LateLintPass<'tcx> for StrToString { } } -declare_clippy_lint! { - /// ### What it does - /// This lint checks for `.to_string()` method calls on values of type `String`. - /// - /// ### Why restrict this? - /// The `to_string` method is also used on other types to convert them to a string. - /// When called on a `String` it only clones the `String`, which can be more specifically - /// expressed with `.clone()`. - /// - /// ### Example - /// ```no_run - /// let msg = String::from("Hello World"); - /// let _ = msg.to_string(); - /// ``` - /// Use instead: - /// ```no_run - /// let msg = String::from("Hello World"); - /// let _ = msg.clone(); - /// ``` - #[clippy::version = "pre 1.29.0"] - pub STRING_TO_STRING, - restriction, - "using `to_string()` on a `String`, which should be `clone()`" -} - -declare_lint_pass!(StringToString => [STRING_TO_STRING]); - -fn is_parent_map_like(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option { - if let Some(parent_expr) = get_parent_expr(cx, expr) - && let ExprKind::MethodCall(name, _, _, parent_span) = parent_expr.kind - && name.ident.name == sym::map - && let Some(caller_def_id) = cx.typeck_results().type_dependent_def_id(parent_expr.hir_id) - && (clippy_utils::is_diag_item_method(cx, caller_def_id, sym::Result) - || clippy_utils::is_diag_item_method(cx, caller_def_id, sym::Option) - || clippy_utils::is_diag_trait_item(cx, caller_def_id, sym::Iterator)) - { - Some(parent_span) - } else { - None - } -} - -fn is_called_from_map_like(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option { - // Look for a closure as parent of `expr`, discarding simple blocks - let parent_closure = cx - .tcx - .hir_parent_iter(expr.hir_id) - .try_fold(expr.hir_id, |child_hir_id, (_, node)| match node { - // Check that the child expression is the only expression in the block - Node::Block(block) if block.stmts.is_empty() && block.expr.map(|e| e.hir_id) == Some(child_hir_id) => { - ControlFlow::Continue(block.hir_id) - }, - Node::Expr(expr) if matches!(expr.kind, ExprKind::Block(..)) => ControlFlow::Continue(expr.hir_id), - Node::Expr(expr) if matches!(expr.kind, ExprKind::Closure(_)) => ControlFlow::Break(Some(expr)), - _ => ControlFlow::Break(None), - }) - .break_value()?; - is_parent_map_like(cx, parent_closure?) -} - -fn suggest_cloned_string_to_string(cx: &LateContext<'_>, span: rustc_span::Span) { - span_lint_and_sugg( - cx, - STRING_TO_STRING, - span, - "`to_string()` called on a `String`", - "try", - "cloned()".to_string(), - Applicability::MachineApplicable, - ); -} - -impl<'tcx> LateLintPass<'tcx> for StringToString { - fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'_>) { - if expr.span.from_expansion() { - return; - } - - match &expr.kind { - ExprKind::MethodCall(path, self_arg, [], _) => { - if path.ident.name == sym::to_string - && let ty = cx.typeck_results().expr_ty(self_arg) - && is_type_lang_item(cx, ty.peel_refs(), LangItem::String) - { - if let Some(parent_span) = is_called_from_map_like(cx, expr) { - suggest_cloned_string_to_string(cx, parent_span); - } else { - #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")] - span_lint_and_then( - cx, - STRING_TO_STRING, - expr.span, - "`to_string()` called on a `String`", - |diag| { - diag.help("consider using `.clone()`"); - }, - ); - } - } - }, - ExprKind::Path(QPath::TypeRelative(ty, segment)) => { - if segment.ident.name == sym::to_string - && let rustc_hir::TyKind::Path(QPath::Resolved(_, path)) = ty.peel_refs().kind - && let rustc_hir::def::Res::Def(_, def_id) = path.res - && cx - .tcx - .lang_items() - .get(LangItem::String) - .is_some_and(|lang_id| lang_id == def_id) - && let Some(parent_span) = is_parent_map_like(cx, expr) - { - suggest_cloned_string_to_string(cx, parent_span); - } - }, - _ => {}, - } - } -} - declare_clippy_lint! { /// ### What it does /// Warns about calling `str::trim` (or variants) before `str::split_whitespace`. diff --git a/clippy_lints/src/unit_types/let_unit_value.rs b/clippy_lints/src/unit_types/let_unit_value.rs index 87f184e13ce..d5b6c175854 100644 --- a/clippy_lints/src/unit_types/let_unit_value.rs +++ b/clippy_lints/src/unit_types/let_unit_value.rs @@ -1,17 +1,20 @@ use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::source::snippet_with_context; +use clippy_utils::macros::{FormatArgsStorage, find_format_arg_expr, is_format_macro, root_macro_call_first_node}; +use clippy_utils::source::{indent_of, reindent_multiline, snippet_with_context}; use clippy_utils::visitors::{for_each_local_assignment, for_each_value_source}; use core::ops::ControlFlow; +use rustc_ast::{FormatArgs, FormatArgumentKind}; use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; -use rustc_hir::intravisit::{Visitor, walk_body}; +use rustc_hir::intravisit::{Visitor, walk_body, walk_expr}; use rustc_hir::{Expr, ExprKind, HirId, HirIdSet, LetStmt, MatchSource, Node, PatKind, QPath, TyKind}; use rustc_lint::{LateContext, LintContext}; use rustc_middle::ty; +use rustc_span::Span; use super::LET_UNIT_VALUE; -pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, local: &'tcx LetStmt<'_>) { +pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, format_args: &FormatArgsStorage, local: &'tcx LetStmt<'_>) { // skip `let () = { ... }` if let PatKind::Tuple(fields, ..) = local.pat.kind && fields.is_empty() @@ -73,11 +76,8 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, local: &'tcx LetStmt<'_>) { let mut suggestions = Vec::new(); // Suggest omitting the `let` binding - if let Some(expr) = &local.init { - let mut app = Applicability::MachineApplicable; - let snip = snippet_with_context(cx, expr.span, local.span.ctxt(), "()", &mut app).0; - suggestions.push((local.span, format!("{snip};"))); - } + let mut app = Applicability::MachineApplicable; + let snip = snippet_with_context(cx, init.span, local.span.ctxt(), "()", &mut app).0; // If this is a binding pattern, we need to add suggestions to remove any usages // of the variable @@ -85,53 +85,102 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, local: &'tcx LetStmt<'_>) { && let Some(body_id) = cx.enclosing_body.as_ref() { let body = cx.tcx.hir_body(*body_id); - - // Collect variable usages - let mut visitor = UnitVariableCollector::new(binding_hir_id); + let mut visitor = UnitVariableCollector::new(cx, format_args, binding_hir_id); walk_body(&mut visitor, body); - // Add suggestions for replacing variable usages - suggestions.extend(visitor.spans.into_iter().map(|span| (span, "()".to_string()))); - } + let mut has_in_format_capture = false; + suggestions.extend(visitor.spans.iter().filter_map(|span| match span { + MaybeInFormatCapture::Yes => { + has_in_format_capture = true; + None + }, + MaybeInFormatCapture::No(span) => Some((*span, "()".to_string())), + })); - // Emit appropriate diagnostic based on whether there are usages of the let binding - if !suggestions.is_empty() { - let message = if suggestions.len() == 1 { - "omit the `let` binding" - } else { - "omit the `let` binding and replace variable usages with `()`" - }; - diag.multipart_suggestion(message, suggestions, Applicability::MachineApplicable); + if has_in_format_capture { + suggestions.push(( + init.span, + format!("();\n{}", reindent_multiline(&snip, false, indent_of(cx, local.span))), + )); + diag.multipart_suggestion( + "replace variable usages with `()`", + suggestions, + Applicability::MachineApplicable, + ); + return; + } } + + suggestions.push((local.span, format!("{snip};"))); + let message = if suggestions.len() == 1 { + "omit the `let` binding" + } else { + "omit the `let` binding and replace variable usages with `()`" + }; + diag.multipart_suggestion(message, suggestions, Applicability::MachineApplicable); }, ); } } } -struct UnitVariableCollector { +struct UnitVariableCollector<'a, 'tcx> { + cx: &'a LateContext<'tcx>, + format_args: &'a FormatArgsStorage, id: HirId, - spans: Vec, + spans: Vec, + macro_call: Option<&'a FormatArgs>, } -impl UnitVariableCollector { - fn new(id: HirId) -> Self { - Self { id, spans: vec![] } +enum MaybeInFormatCapture { + Yes, + No(Span), +} + +impl<'a, 'tcx> UnitVariableCollector<'a, 'tcx> { + fn new(cx: &'a LateContext<'tcx>, format_args: &'a FormatArgsStorage, id: HirId) -> Self { + Self { + cx, + format_args, + id, + spans: Vec::new(), + macro_call: None, + } } } /** * Collect all instances where a variable is used based on its `HirId`. */ -impl<'tcx> Visitor<'tcx> for UnitVariableCollector { +impl<'tcx> Visitor<'tcx> for UnitVariableCollector<'_, 'tcx> { fn visit_expr(&mut self, ex: &'tcx Expr<'tcx>) -> Self::Result { + if let Some(macro_call) = root_macro_call_first_node(self.cx, ex) + && is_format_macro(self.cx, macro_call.def_id) + && let Some(format_args) = self.format_args.get(self.cx, ex, macro_call.expn) + { + let parent_macro_call = self.macro_call; + self.macro_call = Some(format_args); + walk_expr(self, ex); + self.macro_call = parent_macro_call; + return; + } + if let ExprKind::Path(QPath::Resolved(None, path)) = ex.kind && let Res::Local(id) = path.res && id == self.id { - self.spans.push(path.span); + if let Some(macro_call) = self.macro_call + && macro_call.arguments.all_args().iter().any(|arg| { + matches!(arg.kind, FormatArgumentKind::Captured(_)) && find_format_arg_expr(ex, arg).is_some() + }) + { + self.spans.push(MaybeInFormatCapture::Yes); + } else { + self.spans.push(MaybeInFormatCapture::No(path.span)); + } } - rustc_hir::intravisit::walk_expr(self, ex); + + walk_expr(self, ex); } } diff --git a/clippy_lints/src/unit_types/mod.rs b/clippy_lints/src/unit_types/mod.rs index e016bd3434b..4ffcc247acf 100644 --- a/clippy_lints/src/unit_types/mod.rs +++ b/clippy_lints/src/unit_types/mod.rs @@ -3,9 +3,10 @@ mod unit_arg; mod unit_cmp; mod utils; +use clippy_utils::macros::FormatArgsStorage; use rustc_hir::{Expr, LetStmt}; use rustc_lint::{LateContext, LateLintPass}; -use rustc_session::declare_lint_pass; +use rustc_session::impl_lint_pass; declare_clippy_lint! { /// ### What it does @@ -96,11 +97,21 @@ declare_clippy_lint! { "passing unit to a function" } -declare_lint_pass!(UnitTypes => [LET_UNIT_VALUE, UNIT_CMP, UNIT_ARG]); +pub struct UnitTypes { + format_args: FormatArgsStorage, +} + +impl_lint_pass!(UnitTypes => [LET_UNIT_VALUE, UNIT_CMP, UNIT_ARG]); + +impl UnitTypes { + pub fn new(format_args: FormatArgsStorage) -> Self { + Self { format_args } + } +} impl<'tcx> LateLintPass<'tcx> for UnitTypes { fn check_local(&mut self, cx: &LateContext<'tcx>, local: &'tcx LetStmt<'tcx>) { - let_unit_value::check(cx, local); + let_unit_value::check(cx, &self.format_args, local); } fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { diff --git a/clippy_lints/src/utils/author.rs b/clippy_lints/src/utils/author.rs index 29931738412..2113cb92137 100644 --- a/clippy_lints/src/utils/author.rs +++ b/clippy_lints/src/utils/author.rs @@ -348,7 +348,7 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { IntTy::I128 => "I128", }; format!("LitIntType::Signed(IntTy::{t})") - } + }, LitIntType::Unsigned(uint_ty) => { let t = match uint_ty { UintTy::Usize => "Usize", @@ -359,7 +359,7 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { UintTy::U128 => "U128", }; format!("LitIntType::Unsigned(UintTy::{t})") - } + }, LitIntType::Unsuffixed => String::from("LitIntType::Unsuffixed"), }; kind!("Int({i}, {int_ty})"); @@ -374,7 +374,7 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { FloatTy::F128 => "F128", }; format!("LitFloatType::Suffixed(FloatTy::{t})") - } + }, LitFloatType::Unsuffixed => String::from("LitFloatType::Unsuffixed"), }; kind!("Float(_, {float_ty})"); diff --git a/clippy_lints/src/wildcard_imports.rs b/clippy_lints/src/wildcard_imports.rs index 22fd15d153a..a2523b5fb07 100644 --- a/clippy_lints/src/wildcard_imports.rs +++ b/clippy_lints/src/wildcard_imports.rs @@ -118,7 +118,7 @@ impl_lint_pass!(WildcardImports => [ENUM_GLOB_USE, WILDCARD_IMPORTS]); impl LateLintPass<'_> for WildcardImports { fn check_item(&mut self, cx: &LateContext<'_>, item: &Item<'_>) { - if cx.sess().is_test_crate() { + if cx.sess().is_test_crate() || item.span.in_external_macro(cx.sess().source_map()) { return; } diff --git a/clippy_lints_internal/src/derive_deserialize_allowing_unknown.rs b/clippy_lints_internal/src/derive_deserialize_allowing_unknown.rs index e0ae0c11cc2..5e6a40ac2eb 100644 --- a/clippy_lints_internal/src/derive_deserialize_allowing_unknown.rs +++ b/clippy_lints_internal/src/derive_deserialize_allowing_unknown.rs @@ -2,12 +2,11 @@ use clippy_utils::diagnostics::span_lint; use clippy_utils::paths; use rustc_ast::tokenstream::{TokenStream, TokenTree}; use rustc_ast::{AttrStyle, DelimArgs}; -use rustc_hir::attrs::{AttributeKind}; -use rustc_hir::find_attr; +use rustc_hir::attrs::AttributeKind; use rustc_hir::def::Res; use rustc_hir::def_id::LocalDefId; use rustc_hir::{ - AttrArgs, AttrItem, AttrPath, Attribute, HirId, Impl, Item, ItemKind, Path, QPath, TraitRef, Ty, TyKind, + AttrArgs, AttrItem, AttrPath, Attribute, HirId, Impl, Item, ItemKind, Path, QPath, TraitRef, Ty, TyKind, find_attr, }; use rustc_lint::{LateContext, LateLintPass}; use rustc_lint_defs::declare_tool_lint; diff --git a/clippy_test_deps/Cargo.lock b/clippy_test_deps/Cargo.lock index 5be404f24e6..2f987c0137c 100644 --- a/clippy_test_deps/Cargo.lock +++ b/clippy_test_deps/Cargo.lock @@ -70,7 +70,6 @@ name = "clippy_test_deps" version = "0.1.0" dependencies = [ "futures", - "if_chain", "itertools", "libc", "parking_lot", @@ -182,12 +181,6 @@ version = "0.31.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" -[[package]] -name = "if_chain" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb56e1aa765b4b4f3aadfab769793b7087bb03a4ea4920644a6d238e2df5b9ed" - [[package]] name = "io-uring" version = "0.7.8" diff --git a/clippy_test_deps/Cargo.toml b/clippy_test_deps/Cargo.toml index fcedc5d4843..e449b48bc46 100644 --- a/clippy_test_deps/Cargo.toml +++ b/clippy_test_deps/Cargo.toml @@ -9,7 +9,6 @@ edition = "2021" libc = "0.2" regex = "1.5.5" serde = { version = "1.0.145", features = ["derive"] } -if_chain = "1.0" quote = "1.0.25" syn = { version = "2.0", features = ["full"] } futures = "0.3" diff --git a/clippy_utils/Cargo.toml b/clippy_utils/Cargo.toml index 73291aa8cdf..bdf7431f29f 100644 --- a/clippy_utils/Cargo.toml +++ b/clippy_utils/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "clippy_utils" -version = "0.1.90" +version = "0.1.91" edition = "2024" description = "Helpful tools for writing lints, provided as they are used in Clippy" repository = "https://github.com/rust-lang/rust-clippy" diff --git a/clippy_utils/README.md b/clippy_utils/README.md index 19e71f6af1d..6d8dd92d55d 100644 --- a/clippy_utils/README.md +++ b/clippy_utils/README.md @@ -8,7 +8,7 @@ This crate is only guaranteed to build with this `nightly` toolchain: ``` -nightly-2025-07-25 +nightly-2025-08-07 ``` diff --git a/clippy_utils/src/attrs.rs b/clippy_utils/src/attrs.rs index 4ccd9c5300b..2d42e76dcbc 100644 --- a/clippy_utils/src/attrs.rs +++ b/clippy_utils/src/attrs.rs @@ -2,9 +2,9 @@ use crate::source::SpanRangeExt; use crate::{sym, tokenize_with_text}; use rustc_ast::attr; use rustc_ast::attr::AttributeExt; -use rustc_hir::attrs::{AttributeKind}; -use rustc_hir::find_attr; use rustc_errors::Applicability; +use rustc_hir::attrs::AttributeKind; +use rustc_hir::find_attr; use rustc_lexer::TokenKind; use rustc_lint::LateContext; use rustc_middle::ty::{AdtDef, TyCtxt}; diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 5b9b0ef3001..fc716d86fc6 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -83,19 +83,18 @@ pub use self::hir_utils::{ use core::mem; use core::ops::ControlFlow; use std::collections::hash_map::Entry; -use std::iter::{once, repeat_n}; +use std::iter::{once, repeat_n, zip}; use std::sync::{Mutex, MutexGuard, OnceLock}; use itertools::Itertools; use rustc_abi::Integer; use rustc_ast::ast::{self, LitKind, RangeLimits}; use rustc_ast::join_path_syms; -use rustc_hir::attrs::{AttributeKind}; -use rustc_hir::find_attr; use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::packed::Pu128; use rustc_data_structures::unhash::UnindexMap; use rustc_hir::LangItem::{OptionNone, OptionSome, ResultErr, ResultOk}; +use rustc_hir::attrs::AttributeKind; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::{DefId, LocalDefId, LocalModDefId}; use rustc_hir::definitions::{DefPath, DefPathData}; @@ -106,7 +105,7 @@ use rustc_hir::{ CoroutineKind, Destination, Expr, ExprField, ExprKind, FnDecl, FnRetTy, GenericArg, GenericArgs, HirId, Impl, ImplItem, ImplItemKind, Item, ItemKind, LangItem, LetStmt, MatchSource, Mutability, Node, OwnerId, OwnerNode, Param, Pat, PatExpr, PatExprKind, PatKind, Path, PathSegment, QPath, Stmt, StmtKind, TraitFn, TraitItem, - TraitItemKind, TraitRef, TyKind, UnOp, def, + TraitItemKind, TraitRef, TyKind, UnOp, def, find_attr, }; use rustc_lexer::{FrontmatterAllowed, TokenKind, tokenize}; use rustc_lint::{LateContext, Level, Lint, LintContext}; @@ -582,7 +581,7 @@ pub fn can_mut_borrow_both(cx: &LateContext<'_>, e1: &Expr<'_>, e2: &Expr<'_>) - return false; } - for (x1, x2) in s1.iter().zip(s2.iter()) { + for (x1, x2) in zip(&s1, &s2) { if expr_custom_deref_adjustment(cx, x1).is_some() || expr_custom_deref_adjustment(cx, x2).is_some() { return false; } @@ -1898,42 +1897,11 @@ pub fn is_must_use_func_call(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { /// * `|x| { return x; }` /// * `|(x, y)| (x, y)` /// * `|[x, y]| [x, y]` +/// * `|Foo(bar, baz)| Foo(bar, baz)` +/// * `|Foo { bar, baz }| Foo { bar, baz }` /// /// Consider calling [`is_expr_untyped_identity_function`] or [`is_expr_identity_function`] instead. fn is_body_identity_function(cx: &LateContext<'_>, func: &Body<'_>) -> bool { - fn check_pat(cx: &LateContext<'_>, pat: &Pat<'_>, expr: &Expr<'_>) -> bool { - if cx - .typeck_results() - .pat_binding_modes() - .get(pat.hir_id) - .is_some_and(|mode| matches!(mode.0, ByRef::Yes(_))) - { - // If the parameter is `(x, y)` of type `&(T, T)`, or `[x, y]` of type `&[T; 2]`, then - // due to match ergonomics, the inner patterns become references. Don't consider this - // the identity function as that changes types. - return false; - } - - match (pat.kind, expr.kind) { - (PatKind::Binding(_, id, _, _), _) => { - path_to_local_id(expr, id) && cx.typeck_results().expr_adjustments(expr).is_empty() - }, - (PatKind::Tuple(pats, dotdot), ExprKind::Tup(tup)) - if dotdot.as_opt_usize().is_none() && pats.len() == tup.len() => - { - pats.iter().zip(tup).all(|(pat, expr)| check_pat(cx, pat, expr)) - }, - (PatKind::Slice(before, slice, after), ExprKind::Array(arr)) - if slice.is_none() && before.len() + after.len() == arr.len() => - { - (before.iter().chain(after)) - .zip(arr) - .all(|(pat, expr)| check_pat(cx, pat, expr)) - }, - _ => false, - } - } - let [param] = func.params else { return false; }; @@ -1966,11 +1934,81 @@ fn is_body_identity_function(cx: &LateContext<'_>, func: &Body<'_>) -> bool { return false; } }, - _ => return check_pat(cx, param.pat, expr), + _ => return is_expr_identity_of_pat(cx, param.pat, expr, true), } } } +/// Checks if the given expression is an identity representation of the given pattern: +/// * `x` is the identity representation of `x` +/// * `(x, y)` is the identity representation of `(x, y)` +/// * `[x, y]` is the identity representation of `[x, y]` +/// * `Foo(bar, baz)` is the identity representation of `Foo(bar, baz)` +/// * `Foo { bar, baz }` is the identity representation of `Foo { bar, baz }` +/// +/// Note that `by_hir` is used to determine bindings are checked by their `HirId` or by their name. +/// This can be useful when checking patterns in `let` bindings or `match` arms. +pub fn is_expr_identity_of_pat(cx: &LateContext<'_>, pat: &Pat<'_>, expr: &Expr<'_>, by_hir: bool) -> bool { + if cx + .typeck_results() + .pat_binding_modes() + .get(pat.hir_id) + .is_some_and(|mode| matches!(mode.0, ByRef::Yes(_))) + { + // If the parameter is `(x, y)` of type `&(T, T)`, or `[x, y]` of type `&[T; 2]`, then + // due to match ergonomics, the inner patterns become references. Don't consider this + // the identity function as that changes types. + return false; + } + + // NOTE: we're inside a (function) body, so this won't ICE + let qpath_res = |qpath, hir| cx.typeck_results().qpath_res(qpath, hir); + + match (pat.kind, expr.kind) { + (PatKind::Binding(_, id, _, _), _) if by_hir => { + path_to_local_id(expr, id) && cx.typeck_results().expr_adjustments(expr).is_empty() + }, + (PatKind::Binding(_, _, ident, _), ExprKind::Path(QPath::Resolved(_, path))) => { + matches!(path.segments, [ segment] if segment.ident.name == ident.name) + }, + (PatKind::Tuple(pats, dotdot), ExprKind::Tup(tup)) + if dotdot.as_opt_usize().is_none() && pats.len() == tup.len() => + { + zip(pats, tup).all(|(pat, expr)| is_expr_identity_of_pat(cx, pat, expr, by_hir)) + }, + (PatKind::Slice(before, None, after), ExprKind::Array(arr)) if before.len() + after.len() == arr.len() => { + zip(before.iter().chain(after), arr).all(|(pat, expr)| is_expr_identity_of_pat(cx, pat, expr, by_hir)) + }, + (PatKind::TupleStruct(pat_ident, field_pats, dotdot), ExprKind::Call(ident, fields)) + if dotdot.as_opt_usize().is_none() && field_pats.len() == fields.len() => + { + // check ident + if let ExprKind::Path(ident) = &ident.kind + && qpath_res(&pat_ident, pat.hir_id) == qpath_res(ident, expr.hir_id) + // check fields + && zip(field_pats, fields).all(|(pat, expr)| is_expr_identity_of_pat(cx, pat, expr,by_hir)) + { + true + } else { + false + } + }, + (PatKind::Struct(pat_ident, field_pats, false), ExprKind::Struct(ident, fields, hir::StructTailExpr::None)) + if field_pats.len() == fields.len() => + { + // check ident + qpath_res(&pat_ident, pat.hir_id) == qpath_res(ident, expr.hir_id) + // check fields + && field_pats.iter().all(|field_pat| { + fields.iter().any(|field| { + field_pat.ident == field.ident && is_expr_identity_of_pat(cx, field_pat.pat, field.expr, by_hir) + }) + }) + }, + _ => false, + } +} + /// This is the same as [`is_expr_identity_function`], but does not consider closures /// with type annotations for its bindings (or similar) as identity functions: /// * `|x: u8| x` diff --git a/clippy_utils/src/msrvs.rs b/clippy_utils/src/msrvs.rs index 480e0687756..89a83e2c48f 100644 --- a/clippy_utils/src/msrvs.rs +++ b/clippy_utils/src/msrvs.rs @@ -1,8 +1,8 @@ use crate::sym; use rustc_ast::Attribute; use rustc_ast::attr::AttributeExt; -use rustc_hir::RustcVersion; use rustc_attr_parsing::parse_version; +use rustc_hir::RustcVersion; use rustc_lint::LateContext; use rustc_session::Session; use rustc_span::Symbol; diff --git a/clippy_utils/src/qualify_min_const_fn.rs b/clippy_utils/src/qualify_min_const_fn.rs index 79116eba971..68f0b5ea255 100644 --- a/clippy_utils/src/qualify_min_const_fn.rs +++ b/clippy_utils/src/qualify_min_const_fn.rs @@ -5,10 +5,10 @@ use crate::msrvs::{self, Msrv}; use hir::LangItem; -use rustc_hir::{RustcVersion, StableSince}; use rustc_const_eval::check_consts::ConstCx; use rustc_hir as hir; use rustc_hir::def_id::DefId; +use rustc_hir::{RustcVersion, StableSince}; use rustc_infer::infer::TyCtxtInferExt; use rustc_infer::traits::Obligation; use rustc_lint::LateContext; diff --git a/clippy_utils/src/source.rs b/clippy_utils/src/source.rs index 7d21336be1c..e675291b6f3 100644 --- a/clippy_utils/src/source.rs +++ b/clippy_utils/src/source.rs @@ -342,11 +342,8 @@ impl SourceFileRange { /// Attempts to get the text from the source file. This can fail if the source text isn't /// loaded. pub fn as_str(&self) -> Option<&str> { - self.sf - .src - .as_ref() - .map(|src| src.as_str()) - .or_else(|| self.sf.external_src.get().and_then(|src| src.get_source())) + (self.sf.src.as_ref().map(|src| src.as_str())) + .or_else(|| self.sf.external_src.get()?.get_source()) .and_then(|x| x.get(self.range.clone())) } } diff --git a/clippy_utils/src/sugg.rs b/clippy_utils/src/sugg.rs index 7a24d07fa1d..a63333c9b48 100644 --- a/clippy_utils/src/sugg.rs +++ b/clippy_utils/src/sugg.rs @@ -4,11 +4,11 @@ use crate::source::{snippet, snippet_opt, snippet_with_applicability, snippet_with_context}; use crate::ty::expr_sig; use crate::{get_parent_expr_for_hir, higher}; -use rustc_ast::ast; use rustc_ast::util::parser::AssocOp; +use rustc_ast::{UnOp, ast}; +use rustc_data_structures::fx::FxHashSet; use rustc_errors::Applicability; -use rustc_hir as hir; -use rustc_hir::{Closure, ExprKind, HirId, MutTy, TyKind}; +use rustc_hir::{self as hir, Closure, ExprKind, HirId, MutTy, Node, TyKind}; use rustc_hir_typeck::expr_use_visitor::{Delegate, ExprUseVisitor, PlaceBase, PlaceWithHirId}; use rustc_lint::{EarlyContext, LateContext, LintContext}; use rustc_middle::hir::place::ProjectionKind; @@ -29,6 +29,11 @@ pub enum Sugg<'a> { /// A binary operator expression, including `as`-casts and explicit type /// coercion. BinOp(AssocOp, Cow<'a, str>, Cow<'a, str>), + /// A unary operator expression. This is used to sometimes represent `!` + /// or `-`, but only if the type with and without the operator is kept identical. + /// It means that doubling the operator can be used to remove it instead, in + /// order to provide better suggestions. + UnOp(UnOp, Box>), } /// Literal constant `0`, for convenience. @@ -40,9 +45,10 @@ pub const EMPTY: Sugg<'static> = Sugg::NonParen(Cow::Borrowed("")); impl Display for Sugg<'_> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { - match *self { - Sugg::NonParen(ref s) | Sugg::MaybeParen(ref s) => s.fmt(f), - Sugg::BinOp(op, ref lhs, ref rhs) => binop_to_string(op, lhs, rhs).fmt(f), + match self { + Sugg::NonParen(s) | Sugg::MaybeParen(s) => s.fmt(f), + Sugg::BinOp(op, lhs, rhs) => binop_to_string(*op, lhs, rhs).fmt(f), + Sugg::UnOp(op, inner) => write!(f, "{}{}", op.as_str(), inner.clone().maybe_inner_paren()), } } } @@ -100,9 +106,19 @@ impl<'a> Sugg<'a> { applicability: &mut Applicability, ) -> Self { if expr.span.ctxt() == ctxt { - Self::hir_from_snippet(expr, |span| { - snippet_with_context(cx, span, ctxt, default, applicability).0 - }) + if let ExprKind::Unary(op, inner) = expr.kind + && matches!(op, UnOp::Neg | UnOp::Not) + && cx.typeck_results().expr_ty(expr) == cx.typeck_results().expr_ty(inner) + { + Sugg::UnOp( + op, + Box::new(Self::hir_with_context(cx, inner, ctxt, default, applicability)), + ) + } else { + Self::hir_from_snippet(expr, |span| { + snippet_with_context(cx, span, ctxt, default, applicability).0 + }) + } } else { let (snip, _) = snippet_with_context(cx, expr.span, ctxt, default, applicability); Sugg::NonParen(snip) @@ -341,6 +357,7 @@ impl<'a> Sugg<'a> { let sugg = binop_to_string(op, &lhs, &rhs); Sugg::NonParen(format!("({sugg})").into()) }, + Sugg::UnOp(op, inner) => Sugg::NonParen(format!("({}{})", op.as_str(), inner.maybe_inner_paren()).into()), } } @@ -348,6 +365,26 @@ impl<'a> Sugg<'a> { match self { Sugg::NonParen(p) | Sugg::MaybeParen(p) => p.into_owned(), Sugg::BinOp(b, l, r) => binop_to_string(b, &l, &r), + Sugg::UnOp(op, inner) => format!("{}{}", op.as_str(), inner.maybe_inner_paren()), + } + } + + /// Checks if `self` starts with a unary operator. + fn starts_with_unary_op(&self) -> bool { + match self { + Sugg::UnOp(..) => true, + Sugg::BinOp(..) => false, + Sugg::MaybeParen(s) | Sugg::NonParen(s) => s.starts_with(['*', '!', '-', '&']), + } + } + + /// Call `maybe_paren` on `self` if it doesn't start with a unary operator, + /// don't touch it otherwise. + fn maybe_inner_paren(self) -> Self { + if self.starts_with_unary_op() { + self + } else { + self.maybe_paren() } } } @@ -430,10 +467,11 @@ impl Sub for &Sugg<'_> { forward_binop_impls_to_ref!(impl Add, add for Sugg<'_>, type Output = Sugg<'static>); forward_binop_impls_to_ref!(impl Sub, sub for Sugg<'_>, type Output = Sugg<'static>); -impl Neg for Sugg<'_> { - type Output = Sugg<'static>; - fn neg(self) -> Sugg<'static> { - match &self { +impl<'a> Neg for Sugg<'a> { + type Output = Sugg<'a>; + fn neg(self) -> Self::Output { + match self { + Self::UnOp(UnOp::Neg, sugg) => *sugg, Self::BinOp(AssocOp::Cast, ..) => Sugg::MaybeParen(format!("-({self})").into()), _ => make_unop("-", self), } @@ -446,19 +484,21 @@ impl<'a> Not for Sugg<'a> { use AssocOp::Binary; use ast::BinOpKind::{Eq, Ge, Gt, Le, Lt, Ne}; - if let Sugg::BinOp(op, lhs, rhs) = self { - let to_op = match op { - Binary(Eq) => Binary(Ne), - Binary(Ne) => Binary(Eq), - Binary(Lt) => Binary(Ge), - Binary(Ge) => Binary(Lt), - Binary(Gt) => Binary(Le), - Binary(Le) => Binary(Gt), - _ => return make_unop("!", Sugg::BinOp(op, lhs, rhs)), - }; - Sugg::BinOp(to_op, lhs, rhs) - } else { - make_unop("!", self) + match self { + Sugg::BinOp(op, lhs, rhs) => { + let to_op = match op { + Binary(Eq) => Binary(Ne), + Binary(Ne) => Binary(Eq), + Binary(Lt) => Binary(Ge), + Binary(Ge) => Binary(Lt), + Binary(Gt) => Binary(Le), + Binary(Le) => Binary(Gt), + _ => return make_unop("!", Sugg::BinOp(op, lhs, rhs)), + }; + Sugg::BinOp(to_op, lhs, rhs) + }, + Sugg::UnOp(UnOp::Not, expr) => *expr, + _ => make_unop("!", self), } } } @@ -491,20 +531,11 @@ impl Display for ParenHelper { /// Builds the string for `` adding parenthesis when necessary. /// /// For convenience, the operator is taken as a string because all unary -/// operators have the same -/// precedence. +/// operators have the same precedence. pub fn make_unop(op: &str, expr: Sugg<'_>) -> Sugg<'static> { - // If the `expr` starts with `op` already, do not add wrap it in + // If the `expr` starts with a unary operator already, do not wrap it in // parentheses. - let expr = if let Sugg::MaybeParen(ref sugg) = expr - && !has_enclosing_paren(sugg) - && sugg.starts_with(op) - { - expr - } else { - expr.maybe_paren() - }; - Sugg::MaybeParen(format!("{op}{expr}").into()) + Sugg::MaybeParen(format!("{op}{}", expr.maybe_inner_paren()).into()) } /// Builds the string for ` ` adding parenthesis when necessary. @@ -753,8 +784,10 @@ pub fn deref_closure_args(cx: &LateContext<'_>, closure: &hir::Expr<'_>) -> Opti let mut visitor = DerefDelegate { cx, closure_span: closure.span, + closure_arg_id: closure_body.params[0].pat.hir_id, closure_arg_is_type_annotated_double_ref, next_pos: closure.span.lo(), + checked_borrows: FxHashSet::default(), suggestion_start: String::new(), applicability: Applicability::MachineApplicable, }; @@ -780,10 +813,15 @@ struct DerefDelegate<'a, 'tcx> { cx: &'a LateContext<'tcx>, /// The span of the input closure to adapt closure_span: Span, + /// The `hir_id` of the closure argument being checked + closure_arg_id: HirId, /// Indicates if the arg of the closure is a type annotated double reference closure_arg_is_type_annotated_double_ref: bool, /// last position of the span to gradually build the suggestion next_pos: BytePos, + /// `hir_id`s that has been checked. This is used to avoid checking the same `hir_id` multiple + /// times when inside macro expansions. + checked_borrows: FxHashSet, /// starting part of the gradually built suggestion suggestion_start: String, /// confidence on the built suggestion @@ -847,9 +885,15 @@ impl<'tcx> Delegate<'tcx> for DerefDelegate<'_, 'tcx> { fn use_cloned(&mut self, _: &PlaceWithHirId<'tcx>, _: HirId) {} + #[expect(clippy::too_many_lines)] fn borrow(&mut self, cmt: &PlaceWithHirId<'tcx>, _: HirId, _: ty::BorrowKind) { if let PlaceBase::Local(id) = cmt.place.base { let span = self.cx.tcx.hir_span(cmt.hir_id); + if !self.checked_borrows.insert(cmt.hir_id) { + // already checked this span and hir_id, skip + return; + } + let start_span = Span::new(self.next_pos, span.lo(), span.ctxt(), None); let mut start_snip = snippet_with_applicability(self.cx, start_span, "..", &mut self.applicability); @@ -858,7 +902,12 @@ impl<'tcx> Delegate<'tcx> for DerefDelegate<'_, 'tcx> { // full identifier that includes projection (i.e.: `fp.field`) let ident_str_with_proj = snippet(self.cx, span, "..").to_string(); - if cmt.place.projections.is_empty() { + // Make sure to get in all projections if we're on a `matches!` + if let Node::Pat(pat) = self.cx.tcx.hir_node(id) + && pat.hir_id != self.closure_arg_id + { + let _ = write!(self.suggestion_start, "{start_snip}{ident_str_with_proj}"); + } else if cmt.place.projections.is_empty() { // handle item without any projection, that needs an explicit borrowing // i.e.: suggest `&x` instead of `x` let _: fmt::Result = write!(self.suggestion_start, "{start_snip}&{ident_str}"); diff --git a/clippy_utils/src/sym.rs b/clippy_utils/src/sym.rs index 934be97d94e..ce7cc9348fb 100644 --- a/clippy_utils/src/sym.rs +++ b/clippy_utils/src/sym.rs @@ -171,7 +171,6 @@ generate! { has_significant_drop, hidden_glob_reexports, hygiene, - if_chain, insert, inspect, int_roundings, diff --git a/clippy_utils/src/ty/mod.rs b/clippy_utils/src/ty/mod.rs index 02a8eda5893..d79773f8321 100644 --- a/clippy_utils/src/ty/mod.rs +++ b/clippy_utils/src/ty/mod.rs @@ -6,13 +6,12 @@ use core::ops::ControlFlow; use itertools::Itertools; use rustc_abi::VariantIdx; use rustc_ast::ast::Mutability; -use rustc_hir::attrs::{AttributeKind}; -use rustc_hir::find_attr; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_hir as hir; +use rustc_hir::attrs::AttributeKind; use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res}; use rustc_hir::def_id::DefId; -use rustc_hir::{Expr, FnDecl, LangItem, TyKind}; +use rustc_hir::{Expr, FnDecl, LangItem, TyKind, find_attr}; use rustc_hir_analysis::lower_ty; use rustc_infer::infer::TyCtxtInferExt; use rustc_lint::LateContext; @@ -583,7 +582,7 @@ pub fn all_predicates_of(tcx: TyCtxt<'_>, id: DefId) -> impl Iterator { Sig(Binder<'tcx, FnSig<'tcx>>, Option), Closure(Option<&'tcx FnDecl<'tcx>>, Binder<'tcx, FnSig<'tcx>>), diff --git a/declare_clippy_lint/Cargo.toml b/declare_clippy_lint/Cargo.toml index bd6b4dfdee4..ec0e59e7054 100644 --- a/declare_clippy_lint/Cargo.toml +++ b/declare_clippy_lint/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "declare_clippy_lint" -version = "0.1.90" +version = "0.1.91" edition = "2024" repository = "https://github.com/rust-lang/rust-clippy" license = "MIT OR Apache-2.0" diff --git a/lintcheck/src/input.rs b/lintcheck/src/input.rs index 408a2e087af..1ed059d2fb1 100644 --- a/lintcheck/src/input.rs +++ b/lintcheck/src/input.rs @@ -117,7 +117,7 @@ pub fn read_crates(toml_path: &Path) -> (Vec, RecursiveOptions) crate_sources.push(CrateWithSource { name: tk.name.clone(), source: CrateSource::CratesIo { - version: version.to_string(), + version: version.clone(), }, file_link: tk.file_link(DEFAULT_DOCS_LINK), options: tk.options.clone(), diff --git a/lintcheck/src/json.rs b/lintcheck/src/json.rs index 808997ff022..79c1255c5ff 100644 --- a/lintcheck/src/json.rs +++ b/lintcheck/src/json.rs @@ -66,7 +66,7 @@ impl fmt::Display for Summary { } in &self.0 { let html_id = to_html_id(name); - writeln!(f, "| [`{name}`](#{html_id}) | {added} | {changed} | {removed} |")?; + writeln!(f, "| [`{name}`](#{html_id}) | {added} | {removed} | {changed} |")?; } Ok(()) diff --git a/lintcheck/src/output.rs b/lintcheck/src/output.rs index d7fe0915121..1ecc3f7c249 100644 --- a/lintcheck/src/output.rs +++ b/lintcheck/src/output.rs @@ -220,7 +220,7 @@ fn print_stats(old_stats: HashMap, new_stats: HashMap<&String, us let same_in_both_hashmaps = old_stats .iter() .filter(|(old_key, old_val)| new_stats.get::<&String>(old_key) == Some(old_val)) - .map(|(k, v)| (k.to_string(), *v)) + .map(|(k, v)| (k.clone(), *v)) .collect::>(); let mut old_stats_deduped = old_stats; diff --git a/rust-toolchain.toml b/rust-toolchain.toml index 0edb80edd04..ac51ec2d61b 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,6 +1,6 @@ [toolchain] # begin autogenerated nightly -channel = "nightly-2025-07-25" +channel = "nightly-2025-08-07" # end autogenerated nightly components = ["cargo", "llvm-tools", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"] profile = "minimal" diff --git a/tests/compile-test.rs b/tests/compile-test.rs index 464efc45c6b..6b6dfd7b81e 100644 --- a/tests/compile-test.rs +++ b/tests/compile-test.rs @@ -73,8 +73,7 @@ fn internal_extern_flags() -> Vec { && INTERNAL_TEST_DEPENDENCIES.contains(&name) { // A dependency may be listed twice if it is available in sysroot, - // and the sysroot dependencies are listed first. As of the writing, - // this only seems to apply to if_chain. + // and the sysroot dependencies are listed first. crates.insert(name, path); } } @@ -434,6 +433,7 @@ fn ui_cargo_toml_metadata() { #[derive(Template)] #[template(path = "index_template.html")] struct Renderer<'a> { + count: usize, lints: &'a Vec, } @@ -513,7 +513,12 @@ impl DiagnosticCollector { fs::write( "util/gh-pages/index.html", - Renderer { lints: &metadata }.render().unwrap(), + Renderer { + count: LINTS.len(), + lints: &metadata, + } + .render() + .unwrap(), ) .unwrap(); }); diff --git a/tests/symbols-used.rs b/tests/symbols-used.rs index bc0456711fb..a1049ba64d5 100644 --- a/tests/symbols-used.rs +++ b/tests/symbols-used.rs @@ -75,7 +75,7 @@ fn all_symbols_are_used() -> Result<()> { for sym in extra { eprintln!(" - {sym}"); } - Err(format!("extra symbols found — remove them {SYM_FILE}"))?; + Err(format!("extra symbols found — remove them from {SYM_FILE}"))?; } Ok(()) } diff --git a/tests/ui-cargo/cargo_rust_version/fail_file_attr/Cargo.stderr b/tests/ui-cargo/cargo_rust_version/fail_file_attr/Cargo.stderr index 14a6b5047b1..666b842bf80 100644 --- a/tests/ui-cargo/cargo_rust_version/fail_file_attr/Cargo.stderr +++ b/tests/ui-cargo/cargo_rust_version/fail_file_attr/Cargo.stderr @@ -7,7 +7,7 @@ error: unnecessary structure name repetition note: the lint level is defined here --> src/main.rs:6:9 | -6 | #![deny(clippy::use_self)] + 6 | #![deny(clippy::use_self)] | ^^^^^^^^^^^^^^^^ error: unnecessary structure name repetition diff --git a/tests/ui-cargo/duplicate_mod/fail/Cargo.stderr b/tests/ui-cargo/duplicate_mod/fail/Cargo.stderr index b626551e35b..8db52c47ad5 100644 --- a/tests/ui-cargo/duplicate_mod/fail/Cargo.stderr +++ b/tests/ui-cargo/duplicate_mod/fail/Cargo.stderr @@ -14,10 +14,10 @@ error: file is loaded as a module multiple times: `src/b.rs` error: file is loaded as a module multiple times: `src/c.rs` --> src/main.rs:7:1 | -7 | mod c; + 7 | mod c; | ^^^^^^ first loaded here -8 | / #[path = "c.rs"] -9 | | mod c2; + 8 | / #[path = "c.rs"] + 9 | | mod c2; | |_______^ loaded again here 10 | / #[path = "c.rs"] 11 | | mod c3; @@ -44,8 +44,8 @@ error: file is loaded as a module multiple times: `src/from_other_module.rs` | ::: src/other_module/mod.rs:1:1 | -1 | / #[path = "../from_other_module.rs"] -2 | | mod m; + 1 | / #[path = "../from_other_module.rs"] + 2 | | mod m; | |______^ loaded again here | = help: replace all but one `mod` item with `use` items diff --git a/tests/ui-cargo/lint_groups_priority/fail/Cargo.stderr b/tests/ui-cargo/lint_groups_priority/fail/Cargo.stderr index 059427d8ee0..a87339a9a30 100644 --- a/tests/ui-cargo/lint_groups_priority/fail/Cargo.stderr +++ b/tests/ui-cargo/lint_groups_priority/fail/Cargo.stderr @@ -1,7 +1,7 @@ error: lint group `rust_2018_idioms` has the same priority (0) as a lint --> Cargo.toml:7:1 | -7 | rust_2018_idioms = "warn" + 7 | rust_2018_idioms = "warn" | ^^^^^^^^^^^^^^^^ ------ has an implicit priority of 0 ... 12 | unused_attributes = { level = "allow" } @@ -11,8 +11,8 @@ error: lint group `rust_2018_idioms` has the same priority (0) as a lint = note: `#[deny(clippy::lint_groups_priority)]` on by default help: to have lints override the group set `rust_2018_idioms` to a lower priority | -7 - rust_2018_idioms = "warn" -7 + rust_2018_idioms = { level = "warn", priority = -1 } + 7 - rust_2018_idioms = "warn" + 7 + rust_2018_idioms = { level = "warn", priority = -1 } | error: lint group `unused` has the same priority (0) as a lint diff --git a/tests/ui-toml/wildcard_imports/wildcard_imports.fixed b/tests/ui-toml/wildcard_imports/wildcard_imports.fixed index 20511cbed16..ec9fbcfd4a3 100644 --- a/tests/ui-toml/wildcard_imports/wildcard_imports.fixed +++ b/tests/ui-toml/wildcard_imports/wildcard_imports.fixed @@ -1,3 +1,4 @@ +//@aux-build:../../ui/auxiliary/proc_macros.rs #![warn(clippy::wildcard_imports)] mod prelude { @@ -13,6 +14,10 @@ mod my_crate { pub mod utils { pub fn my_util_fn() {} } + + pub mod utils2 { + pub const SOME_CONST: u32 = 1; + } } pub use utils::{BAR, print}; @@ -22,6 +27,12 @@ use my_crate::utils::my_util_fn; use prelude::FOO; //~^ ERROR: usage of wildcard import +proc_macros::external! { + use my_crate::utils2::*; + + static SOME_STATIC: u32 = SOME_CONST; +} + fn main() { let _ = FOO; let _ = BAR; diff --git a/tests/ui-toml/wildcard_imports/wildcard_imports.rs b/tests/ui-toml/wildcard_imports/wildcard_imports.rs index 8d05910f471..233ee19f89b 100644 --- a/tests/ui-toml/wildcard_imports/wildcard_imports.rs +++ b/tests/ui-toml/wildcard_imports/wildcard_imports.rs @@ -1,3 +1,4 @@ +//@aux-build:../../ui/auxiliary/proc_macros.rs #![warn(clippy::wildcard_imports)] mod prelude { @@ -13,6 +14,10 @@ mod my_crate { pub mod utils { pub fn my_util_fn() {} } + + pub mod utils2 { + pub const SOME_CONST: u32 = 1; + } } pub use utils::*; @@ -22,6 +27,12 @@ use my_crate::utils::*; use prelude::*; //~^ ERROR: usage of wildcard import +proc_macros::external! { + use my_crate::utils2::*; + + static SOME_STATIC: u32 = SOME_CONST; +} + fn main() { let _ = FOO; let _ = BAR; diff --git a/tests/ui-toml/wildcard_imports/wildcard_imports.stderr b/tests/ui-toml/wildcard_imports/wildcard_imports.stderr index 5e624dd6c3c..5d37cb705f5 100644 --- a/tests/ui-toml/wildcard_imports/wildcard_imports.stderr +++ b/tests/ui-toml/wildcard_imports/wildcard_imports.stderr @@ -1,5 +1,5 @@ error: usage of wildcard import - --> tests/ui-toml/wildcard_imports/wildcard_imports.rs:18:9 + --> tests/ui-toml/wildcard_imports/wildcard_imports.rs:23:9 | LL | pub use utils::*; | ^^^^^^^^ help: try: `utils::{BAR, print}` @@ -8,13 +8,13 @@ LL | pub use utils::*; = help: to override `-D warnings` add `#[allow(clippy::wildcard_imports)]` error: usage of wildcard import - --> tests/ui-toml/wildcard_imports/wildcard_imports.rs:20:5 + --> tests/ui-toml/wildcard_imports/wildcard_imports.rs:25:5 | LL | use my_crate::utils::*; | ^^^^^^^^^^^^^^^^^^ help: try: `my_crate::utils::my_util_fn` error: usage of wildcard import - --> tests/ui-toml/wildcard_imports/wildcard_imports.rs:22:5 + --> tests/ui-toml/wildcard_imports/wildcard_imports.rs:27:5 | LL | use prelude::*; | ^^^^^^^^^^ help: try: `prelude::FOO` diff --git a/tests/ui/cast_lossless_integer_unfixable.rs b/tests/ui/cast_lossless_integer_unfixable.rs new file mode 100644 index 00000000000..db9cbbb5b66 --- /dev/null +++ b/tests/ui/cast_lossless_integer_unfixable.rs @@ -0,0 +1,17 @@ +//@check-pass +#![warn(clippy::cast_lossless)] + +fn issue15348() { + macro_rules! zero { + ($int:ty) => {{ + let data: [u8; 3] = [0, 0, 0]; + data[0] as $int + }}; + } + + let _ = zero!(u8); + let _ = zero!(u16); + let _ = zero!(u32); + let _ = zero!(u64); + let _ = zero!(u128); +} diff --git a/tests/ui/collapsible_else_if.fixed b/tests/ui/collapsible_else_if.fixed index fed75244c6f..3d709fe9b8e 100644 --- a/tests/ui/collapsible_else_if.fixed +++ b/tests/ui/collapsible_else_if.fixed @@ -104,3 +104,25 @@ fn issue14799() { } } } + +fn in_parens() { + let x = "hello"; + let y = "world"; + + if x == "hello" { + print!("Hello "); + } else if y == "world" { println!("world") } else { println!("!") } + //~^^^ collapsible_else_if +} + +fn in_brackets() { + let x = "hello"; + let y = "world"; + + // There is no lint when the inner `if` is in a block. + if x == "hello" { + print!("Hello "); + } else { + { if y == "world" { println!("world") } else { println!("!") } } + } +} diff --git a/tests/ui/collapsible_else_if.rs b/tests/ui/collapsible_else_if.rs index e50e781fb69..51868e03908 100644 --- a/tests/ui/collapsible_else_if.rs +++ b/tests/ui/collapsible_else_if.rs @@ -120,3 +120,27 @@ fn issue14799() { } } } + +fn in_parens() { + let x = "hello"; + let y = "world"; + + if x == "hello" { + print!("Hello "); + } else { + (if y == "world" { println!("world") } else { println!("!") }) + } + //~^^^ collapsible_else_if +} + +fn in_brackets() { + let x = "hello"; + let y = "world"; + + // There is no lint when the inner `if` is in a block. + if x == "hello" { + print!("Hello "); + } else { + { if y == "world" { println!("world") } else { println!("!") } } + } +} diff --git a/tests/ui/collapsible_else_if.stderr b/tests/ui/collapsible_else_if.stderr index 7d80894cadb..1a7bcec7fd5 100644 --- a/tests/ui/collapsible_else_if.stderr +++ b/tests/ui/collapsible_else_if.stderr @@ -150,5 +150,14 @@ LL | | if false {} LL | | } | |_____^ help: collapse nested if block: `if false {}` -error: aborting due to 8 previous errors +error: this `else { if .. }` block can be collapsed + --> tests/ui/collapsible_else_if.rs:130:12 + | +LL | } else { + | ____________^ +LL | | (if y == "world" { println!("world") } else { println!("!") }) +LL | | } + | |_____^ help: collapse nested if block: `if y == "world" { println!("world") } else { println!("!") }` + +error: aborting due to 9 previous errors diff --git a/tests/ui/collapsible_if.fixed b/tests/ui/collapsible_if.fixed index 77bc791ea8e..78354c2d7cf 100644 --- a/tests/ui/collapsible_if.fixed +++ b/tests/ui/collapsible_if.fixed @@ -163,3 +163,21 @@ fn issue14799() { if true {} }; } + +fn in_parens() { + if true + && true { + println!("In parens, linted"); + } + //~^^^^^ collapsible_if +} + +fn in_brackets() { + if true { + { + if true { + println!("In brackets, not linted"); + } + } + } +} diff --git a/tests/ui/collapsible_if.rs b/tests/ui/collapsible_if.rs index d30df157d5e..5d9afa10956 100644 --- a/tests/ui/collapsible_if.rs +++ b/tests/ui/collapsible_if.rs @@ -173,3 +173,22 @@ fn issue14799() { if true {} }; } + +fn in_parens() { + if true { + (if true { + println!("In parens, linted"); + }) + } + //~^^^^^ collapsible_if +} + +fn in_brackets() { + if true { + { + if true { + println!("In brackets, not linted"); + } + } + } +} diff --git a/tests/ui/collapsible_if.stderr b/tests/ui/collapsible_if.stderr index 32c6b019403..a685cc2e929 100644 --- a/tests/ui/collapsible_if.stderr +++ b/tests/ui/collapsible_if.stderr @@ -190,5 +190,23 @@ LL | // This is a comment, do not collapse code to it LL ~ ; 3 | -error: aborting due to 11 previous errors +error: this `if` statement can be collapsed + --> tests/ui/collapsible_if.rs:178:5 + | +LL | / if true { +LL | | (if true { +LL | | println!("In parens, linted"); +LL | | }) +LL | | } + | |_____^ + | +help: collapse nested if block + | +LL ~ if true +LL ~ && true { +LL | println!("In parens, linted"); +LL ~ } + | + +error: aborting due to 12 previous errors diff --git a/tests/ui/deprecated.rs b/tests/ui/deprecated.rs index 6b69bdd29ce..9743a83fb93 100644 --- a/tests/ui/deprecated.rs +++ b/tests/ui/deprecated.rs @@ -12,6 +12,7 @@ #![warn(clippy::regex_macro)] //~ ERROR: lint `clippy::regex_macro` #![warn(clippy::replace_consts)] //~ ERROR: lint `clippy::replace_consts` #![warn(clippy::should_assert_eq)] //~ ERROR: lint `clippy::should_assert_eq` +#![warn(clippy::string_to_string)] //~ ERROR: lint `clippy::string_to_string` #![warn(clippy::unsafe_vector_initialization)] //~ ERROR: lint `clippy::unsafe_vector_initialization` #![warn(clippy::unstable_as_mut_slice)] //~ ERROR: lint `clippy::unstable_as_mut_slice` #![warn(clippy::unstable_as_slice)] //~ ERROR: lint `clippy::unstable_as_slice` diff --git a/tests/ui/deprecated.stderr b/tests/ui/deprecated.stderr index 07e59d33d60..cd225da611c 100644 --- a/tests/ui/deprecated.stderr +++ b/tests/ui/deprecated.stderr @@ -61,35 +61,41 @@ error: lint `clippy::should_assert_eq` has been removed: `assert!(a == b)` can n LL | #![warn(clippy::should_assert_eq)] | ^^^^^^^^^^^^^^^^^^^^^^^^ -error: lint `clippy::unsafe_vector_initialization` has been removed: the suggested alternative could be substantially slower +error: lint `clippy::string_to_string` has been removed: `clippy:implicit_clone` covers those cases --> tests/ui/deprecated.rs:15:9 | +LL | #![warn(clippy::string_to_string)] + | ^^^^^^^^^^^^^^^^^^^^^^^^ + +error: lint `clippy::unsafe_vector_initialization` has been removed: the suggested alternative could be substantially slower + --> tests/ui/deprecated.rs:16:9 + | LL | #![warn(clippy::unsafe_vector_initialization)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: lint `clippy::unstable_as_mut_slice` has been removed: `Vec::as_mut_slice` is now stable - --> tests/ui/deprecated.rs:16:9 + --> tests/ui/deprecated.rs:17:9 | LL | #![warn(clippy::unstable_as_mut_slice)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: lint `clippy::unstable_as_slice` has been removed: `Vec::as_slice` is now stable - --> tests/ui/deprecated.rs:17:9 + --> tests/ui/deprecated.rs:18:9 | LL | #![warn(clippy::unstable_as_slice)] | ^^^^^^^^^^^^^^^^^^^^^^^^^ error: lint `clippy::unused_collect` has been removed: `Iterator::collect` is now marked as `#[must_use]` - --> tests/ui/deprecated.rs:18:9 + --> tests/ui/deprecated.rs:19:9 | LL | #![warn(clippy::unused_collect)] | ^^^^^^^^^^^^^^^^^^^^^^ error: lint `clippy::wrong_pub_self_convention` has been removed: `clippy::wrong_self_convention` now covers this case via the `avoid-breaking-exported-api` config - --> tests/ui/deprecated.rs:19:9 + --> tests/ui/deprecated.rs:20:9 | LL | #![warn(clippy::wrong_pub_self_convention)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 15 previous errors +error: aborting due to 16 previous errors diff --git a/tests/ui/doc/doc-fixable.fixed b/tests/ui/doc/doc-fixable.fixed index 8cf20d8b1a1..bbbd5973036 100644 --- a/tests/ui/doc/doc-fixable.fixed +++ b/tests/ui/doc/doc-fixable.fixed @@ -83,7 +83,7 @@ fn test_units() { /// WebGL WebGL2 WebGPU WebRTC WebSocket WebTransport /// TensorFlow /// TrueType -/// iOS macOS FreeBSD NetBSD OpenBSD +/// iOS macOS FreeBSD NetBSD OpenBSD NixOS /// TeX LaTeX BibTeX BibLaTeX /// MinGW /// CamelCase (see also #2395) diff --git a/tests/ui/doc/doc-fixable.rs b/tests/ui/doc/doc-fixable.rs index 5b6f2bd8330..1077d3580d3 100644 --- a/tests/ui/doc/doc-fixable.rs +++ b/tests/ui/doc/doc-fixable.rs @@ -83,7 +83,7 @@ fn test_units() { /// WebGL WebGL2 WebGPU WebRTC WebSocket WebTransport /// TensorFlow /// TrueType -/// iOS macOS FreeBSD NetBSD OpenBSD +/// iOS macOS FreeBSD NetBSD OpenBSD NixOS /// TeX LaTeX BibTeX BibLaTeX /// MinGW /// CamelCase (see also #2395) diff --git a/tests/ui/duplicated_attributes.rs b/tests/ui/duplicated_attributes.rs index 3ca91d6f182..9a671499505 100644 --- a/tests/ui/duplicated_attributes.rs +++ b/tests/ui/duplicated_attributes.rs @@ -1,6 +1,6 @@ //@aux-build:proc_macro_attr.rs +#![warn(clippy::duplicated_attributes, clippy::duplicated_attributes)] //~ ERROR: duplicated attribute #![feature(rustc_attrs)] -#![warn(clippy::duplicated_attributes)] #![cfg(any(unix, windows))] #![allow(dead_code)] #![allow(dead_code)] //~ ERROR: duplicated attribute diff --git a/tests/ui/duplicated_attributes.stderr b/tests/ui/duplicated_attributes.stderr index 0903617a8d1..922939d60dd 100644 --- a/tests/ui/duplicated_attributes.stderr +++ b/tests/ui/duplicated_attributes.stderr @@ -1,3 +1,22 @@ +error: duplicated attribute + --> tests/ui/duplicated_attributes.rs:2:40 + | +LL | #![warn(clippy::duplicated_attributes, clippy::duplicated_attributes)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: first defined here + --> tests/ui/duplicated_attributes.rs:2:9 + | +LL | #![warn(clippy::duplicated_attributes, clippy::duplicated_attributes)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +help: remove this attribute + --> tests/ui/duplicated_attributes.rs:2:40 + | +LL | #![warn(clippy::duplicated_attributes, clippy::duplicated_attributes)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = note: `-D clippy::duplicated-attributes` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::duplicated_attributes)]` + error: duplicated attribute --> tests/ui/duplicated_attributes.rs:6:10 | @@ -14,8 +33,6 @@ help: remove this attribute | LL | #![allow(dead_code)] | ^^^^^^^^^ - = note: `-D clippy::duplicated-attributes` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::duplicated_attributes)]` error: duplicated attribute --> tests/ui/duplicated_attributes.rs:14:9 @@ -34,5 +51,5 @@ help: remove this attribute LL | #[allow(dead_code)] | ^^^^^^^^^ -error: aborting due to 2 previous errors +error: aborting due to 3 previous errors diff --git a/tests/ui/empty_structs_with_brackets.fixed b/tests/ui/empty_structs_with_brackets.fixed index 419cf2354f8..22386245ffe 100644 --- a/tests/ui/empty_structs_with_brackets.fixed +++ b/tests/ui/empty_structs_with_brackets.fixed @@ -32,3 +32,17 @@ macro_rules! empty_struct { empty_struct!(FromMacro); fn main() {} + +mod issue15349 { + trait Bar {} + impl Bar for [u8; 7] {} + + struct Foo; + //~^ empty_structs_with_brackets + impl Foo + where + [u8; N]: Bar<[(); N]>, + { + fn foo() {} + } +} diff --git a/tests/ui/empty_structs_with_brackets.rs b/tests/ui/empty_structs_with_brackets.rs index 90c415c1220..5cb54b66134 100644 --- a/tests/ui/empty_structs_with_brackets.rs +++ b/tests/ui/empty_structs_with_brackets.rs @@ -32,3 +32,17 @@ macro_rules! empty_struct { empty_struct!(FromMacro); fn main() {} + +mod issue15349 { + trait Bar {} + impl Bar for [u8; 7] {} + + struct Foo {} + //~^ empty_structs_with_brackets + impl Foo + where + [u8; N]: Bar<[(); N]>, + { + fn foo() {} + } +} diff --git a/tests/ui/empty_structs_with_brackets.stderr b/tests/ui/empty_structs_with_brackets.stderr index 86ef43aa960..f662bb9423e 100644 --- a/tests/ui/empty_structs_with_brackets.stderr +++ b/tests/ui/empty_structs_with_brackets.stderr @@ -16,5 +16,13 @@ LL | struct MyEmptyTupleStruct(); // should trigger lint | = help: remove the brackets -error: aborting due to 2 previous errors +error: found empty brackets on struct declaration + --> tests/ui/empty_structs_with_brackets.rs:40:31 + | +LL | struct Foo {} + | ^^^ + | + = help: remove the brackets + +error: aborting due to 3 previous errors diff --git a/tests/ui/four_forward_slashes_bare_cr.rs b/tests/ui/four_forward_slashes_bare_cr.rs new file mode 100644 index 00000000000..19123cd206e --- /dev/null +++ b/tests/ui/four_forward_slashes_bare_cr.rs @@ -0,0 +1,6 @@ +//@no-rustfix +#![warn(clippy::four_forward_slashes)] + +//~v four_forward_slashes +//// nondoc comment with bare CR: ' ' +fn main() {} diff --git a/tests/ui/four_forward_slashes_bare_cr.stderr b/tests/ui/four_forward_slashes_bare_cr.stderr new file mode 100644 index 00000000000..64e70b97db9 --- /dev/null +++ b/tests/ui/four_forward_slashes_bare_cr.stderr @@ -0,0 +1,14 @@ +error: this item has comments with 4 forward slashes (`////`). These look like doc comments, but they aren't + --> tests/ui/four_forward_slashes_bare_cr.rs:5:1 + | +LL | / //// nondoc comment with bare CR: '␍' +LL | | fn main() {} + | |_^ + | + = help: make this a doc comment by removing one `/` + = note: bare CR characters are not allowed in doc comments + = note: `-D clippy::four-forward-slashes` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::four_forward_slashes)]` + +error: aborting due to 1 previous error + diff --git a/tests/ui/implicit_clone.fixed b/tests/ui/implicit_clone.fixed index d60d1cb0ec0..267514c5f3d 100644 --- a/tests/ui/implicit_clone.fixed +++ b/tests/ui/implicit_clone.fixed @@ -135,4 +135,10 @@ fn main() { } let no_clone = &NoClone; let _ = no_clone.to_owned(); + + let s = String::from("foo"); + let _ = s.clone(); + //~^ implicit_clone + let _ = s.clone(); + //~^ implicit_clone } diff --git a/tests/ui/implicit_clone.rs b/tests/ui/implicit_clone.rs index b96828f28c8..fba954026e7 100644 --- a/tests/ui/implicit_clone.rs +++ b/tests/ui/implicit_clone.rs @@ -135,4 +135,10 @@ fn main() { } let no_clone = &NoClone; let _ = no_clone.to_owned(); + + let s = String::from("foo"); + let _ = s.to_owned(); + //~^ implicit_clone + let _ = s.to_string(); + //~^ implicit_clone } diff --git a/tests/ui/implicit_clone.stderr b/tests/ui/implicit_clone.stderr index 1eb6ff1fe42..4cca9b0d0c0 100644 --- a/tests/ui/implicit_clone.stderr +++ b/tests/ui/implicit_clone.stderr @@ -67,5 +67,17 @@ error: implicitly cloning a `PathBuf` by calling `to_path_buf` on its dereferenc LL | let _ = pathbuf_ref.to_path_buf(); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(**pathbuf_ref).clone()` -error: aborting due to 11 previous errors +error: implicitly cloning a `String` by calling `to_owned` on its dereferenced type + --> tests/ui/implicit_clone.rs:140:13 + | +LL | let _ = s.to_owned(); + | ^^^^^^^^^^^^ help: consider using: `s.clone()` + +error: implicitly cloning a `String` by calling `to_string` on its dereferenced type + --> tests/ui/implicit_clone.rs:142:13 + | +LL | let _ = s.to_string(); + | ^^^^^^^^^^^^^ help: consider using: `s.clone()` + +error: aborting due to 13 previous errors diff --git a/tests/ui/index_refutable_slice/slice_indexing_in_macro.fixed b/tests/ui/index_refutable_slice/slice_indexing_in_macro.fixed index 72edc539f04..edf6f014d3d 100644 --- a/tests/ui/index_refutable_slice/slice_indexing_in_macro.fixed +++ b/tests/ui/index_refutable_slice/slice_indexing_in_macro.fixed @@ -1,8 +1,5 @@ #![deny(clippy::index_refutable_slice)] -extern crate if_chain; -use if_chain::if_chain; - macro_rules! if_let_slice_macro { () => { // This would normally be linted @@ -18,12 +15,9 @@ fn main() { if_let_slice_macro!(); // Do lint this - if_chain! { - let slice: Option<&[u32]> = Some(&[1, 2, 3]); - if let Some([slice_0, ..]) = slice; + let slice: Option<&[u32]> = Some(&[1, 2, 3]); + if let Some([slice_0, ..]) = slice { //~^ ERROR: this binding can be a slice pattern to avoid indexing - then { - println!("{}", slice_0); - } + println!("{}", slice_0); } } diff --git a/tests/ui/index_refutable_slice/slice_indexing_in_macro.rs b/tests/ui/index_refutable_slice/slice_indexing_in_macro.rs index 7b474ba423b..76d4a2350f5 100644 --- a/tests/ui/index_refutable_slice/slice_indexing_in_macro.rs +++ b/tests/ui/index_refutable_slice/slice_indexing_in_macro.rs @@ -1,8 +1,5 @@ #![deny(clippy::index_refutable_slice)] -extern crate if_chain; -use if_chain::if_chain; - macro_rules! if_let_slice_macro { () => { // This would normally be linted @@ -18,12 +15,9 @@ fn main() { if_let_slice_macro!(); // Do lint this - if_chain! { - let slice: Option<&[u32]> = Some(&[1, 2, 3]); - if let Some(slice) = slice; + let slice: Option<&[u32]> = Some(&[1, 2, 3]); + if let Some(slice) = slice { //~^ ERROR: this binding can be a slice pattern to avoid indexing - then { - println!("{}", slice[0]); - } + println!("{}", slice[0]); } } diff --git a/tests/ui/index_refutable_slice/slice_indexing_in_macro.stderr b/tests/ui/index_refutable_slice/slice_indexing_in_macro.stderr index 64741abb911..635e6d19aef 100644 --- a/tests/ui/index_refutable_slice/slice_indexing_in_macro.stderr +++ b/tests/ui/index_refutable_slice/slice_indexing_in_macro.stderr @@ -1,8 +1,8 @@ error: this binding can be a slice pattern to avoid indexing - --> tests/ui/index_refutable_slice/slice_indexing_in_macro.rs:23:21 + --> tests/ui/index_refutable_slice/slice_indexing_in_macro.rs:19:17 | -LL | if let Some(slice) = slice; - | ^^^^^ +LL | if let Some(slice) = slice { + | ^^^^^ | note: the lint level is defined here --> tests/ui/index_refutable_slice/slice_indexing_in_macro.rs:1:9 @@ -11,10 +11,9 @@ LL | #![deny(clippy::index_refutable_slice)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace the binding and indexed access with a slice pattern | -LL ~ if let Some([slice_0, ..]) = slice; +LL ~ if let Some([slice_0, ..]) = slice { LL | -LL | then { -LL ~ println!("{}", slice_0); +LL ~ println!("{}", slice_0); | error: aborting due to 1 previous error diff --git a/tests/ui/indexing_slicing_slice.rs b/tests/ui/indexing_slicing_slice.rs index cad77f56d03..dea31530a0b 100644 --- a/tests/ui/indexing_slicing_slice.rs +++ b/tests/ui/indexing_slicing_slice.rs @@ -1,6 +1,5 @@ //@aux-build: proc_macros.rs -#![warn(clippy::indexing_slicing)] // We also check the out_of_bounds_indexing lint here, because it lints similar things and // we want to avoid false positives. #![warn(clippy::out_of_bounds_indexing)] diff --git a/tests/ui/indexing_slicing_slice.stderr b/tests/ui/indexing_slicing_slice.stderr index e3ef89823e3..e3d6086544d 100644 --- a/tests/ui/indexing_slicing_slice.stderr +++ b/tests/ui/indexing_slicing_slice.stderr @@ -1,5 +1,5 @@ error: slicing may panic - --> tests/ui/indexing_slicing_slice.rs:115:6 + --> tests/ui/indexing_slicing_slice.rs:114:6 | LL | &x[index..]; | ^^^^^^^^^^ @@ -9,7 +9,7 @@ LL | &x[index..]; = help: to override `-D warnings` add `#[allow(clippy::indexing_slicing)]` error: slicing may panic - --> tests/ui/indexing_slicing_slice.rs:117:6 + --> tests/ui/indexing_slicing_slice.rs:116:6 | LL | &x[..index]; | ^^^^^^^^^^ @@ -17,7 +17,7 @@ LL | &x[..index]; = help: consider using `.get(..n)`or `.get_mut(..n)` instead error: slicing may panic - --> tests/ui/indexing_slicing_slice.rs:119:6 + --> tests/ui/indexing_slicing_slice.rs:118:6 | LL | &x[index_from..index_to]; | ^^^^^^^^^^^^^^^^^^^^^^^ @@ -25,7 +25,7 @@ LL | &x[index_from..index_to]; = help: consider using `.get(n..m)` or `.get_mut(n..m)` instead error: slicing may panic - --> tests/ui/indexing_slicing_slice.rs:121:6 + --> tests/ui/indexing_slicing_slice.rs:120:6 | LL | &x[index_from..][..index_to]; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -33,7 +33,7 @@ LL | &x[index_from..][..index_to]; = help: consider using `.get(..n)`or `.get_mut(..n)` instead error: slicing may panic - --> tests/ui/indexing_slicing_slice.rs:121:6 + --> tests/ui/indexing_slicing_slice.rs:120:6 | LL | &x[index_from..][..index_to]; | ^^^^^^^^^^^^^^^ @@ -41,7 +41,7 @@ LL | &x[index_from..][..index_to]; = help: consider using `.get(n..)` or .get_mut(n..)` instead error: slicing may panic - --> tests/ui/indexing_slicing_slice.rs:124:6 + --> tests/ui/indexing_slicing_slice.rs:123:6 | LL | &x[5..][..10]; | ^^^^^^^^^^^^ @@ -49,7 +49,7 @@ LL | &x[5..][..10]; = help: consider using `.get(..n)`or `.get_mut(..n)` instead error: range is out of bounds - --> tests/ui/indexing_slicing_slice.rs:124:8 + --> tests/ui/indexing_slicing_slice.rs:123:8 | LL | &x[5..][..10]; | ^ @@ -58,7 +58,7 @@ LL | &x[5..][..10]; = help: to override `-D warnings` add `#[allow(clippy::out_of_bounds_indexing)]` error: slicing may panic - --> tests/ui/indexing_slicing_slice.rs:127:6 + --> tests/ui/indexing_slicing_slice.rs:126:6 | LL | &x[0..][..3]; | ^^^^^^^^^^^ @@ -66,7 +66,7 @@ LL | &x[0..][..3]; = help: consider using `.get(..n)`or `.get_mut(..n)` instead error: slicing may panic - --> tests/ui/indexing_slicing_slice.rs:129:6 + --> tests/ui/indexing_slicing_slice.rs:128:6 | LL | &x[1..][..5]; | ^^^^^^^^^^^ @@ -74,19 +74,19 @@ LL | &x[1..][..5]; = help: consider using `.get(..n)`or `.get_mut(..n)` instead error: range is out of bounds - --> tests/ui/indexing_slicing_slice.rs:137:12 + --> tests/ui/indexing_slicing_slice.rs:136:12 | LL | &y[0..=4]; | ^ error: range is out of bounds - --> tests/ui/indexing_slicing_slice.rs:139:11 + --> tests/ui/indexing_slicing_slice.rs:138:11 | LL | &y[..=4]; | ^ error: slicing may panic - --> tests/ui/indexing_slicing_slice.rs:145:6 + --> tests/ui/indexing_slicing_slice.rs:144:6 | LL | &v[10..100]; | ^^^^^^^^^^ @@ -94,7 +94,7 @@ LL | &v[10..100]; = help: consider using `.get(n..m)` or `.get_mut(n..m)` instead error: slicing may panic - --> tests/ui/indexing_slicing_slice.rs:147:6 + --> tests/ui/indexing_slicing_slice.rs:146:6 | LL | &x[10..][..100]; | ^^^^^^^^^^^^^^ @@ -102,13 +102,13 @@ LL | &x[10..][..100]; = help: consider using `.get(..n)`or `.get_mut(..n)` instead error: range is out of bounds - --> tests/ui/indexing_slicing_slice.rs:147:8 + --> tests/ui/indexing_slicing_slice.rs:146:8 | LL | &x[10..][..100]; | ^^ error: slicing may panic - --> tests/ui/indexing_slicing_slice.rs:150:6 + --> tests/ui/indexing_slicing_slice.rs:149:6 | LL | &v[10..]; | ^^^^^^^ @@ -116,7 +116,7 @@ LL | &v[10..]; = help: consider using `.get(n..)` or .get_mut(n..)` instead error: slicing may panic - --> tests/ui/indexing_slicing_slice.rs:152:6 + --> tests/ui/indexing_slicing_slice.rs:151:6 | LL | &v[..100]; | ^^^^^^^^ @@ -124,7 +124,7 @@ LL | &v[..100]; = help: consider using `.get(..n)`or `.get_mut(..n)` instead error: indexing may panic - --> tests/ui/indexing_slicing_slice.rs:170:5 + --> tests/ui/indexing_slicing_slice.rs:169:5 | LL | map_with_get[true]; | ^^^^^^^^^^^^^^^^^^ @@ -132,7 +132,7 @@ LL | map_with_get[true]; = help: consider using `.get(n)` or `.get_mut(n)` instead error: indexing may panic - --> tests/ui/indexing_slicing_slice.rs:174:5 + --> tests/ui/indexing_slicing_slice.rs:173:5 | LL | s[0]; | ^^^^ @@ -140,7 +140,7 @@ LL | s[0]; = help: consider using `.get(n)` or `.get_mut(n)` instead error: indexing may panic - --> tests/ui/indexing_slicing_slice.rs:178:5 + --> tests/ui/indexing_slicing_slice.rs:177:5 | LL | y[0]; | ^^^^ diff --git a/tests/ui/infallible_try_from.stderr b/tests/ui/infallible_try_from.stderr index 705b1188489..d1e0d9e7d3b 100644 --- a/tests/ui/infallible_try_from.stderr +++ b/tests/ui/infallible_try_from.stderr @@ -1,4 +1,4 @@ -error: infallible TryFrom impl; consider implementing From, instead +error: infallible TryFrom impl; consider implementing From instead --> tests/ui/infallible_try_from.rs:8:1 | LL | impl TryFrom for MyStruct { @@ -10,7 +10,7 @@ LL | type Error = !; = note: `-D clippy::infallible-try-from` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::infallible_try_from)]` -error: infallible TryFrom impl; consider implementing From, instead +error: infallible TryFrom impl; consider implementing From instead --> tests/ui/infallible_try_from.rs:16:1 | LL | impl TryFrom for MyStruct { diff --git a/tests/ui/ip_constant.fixed b/tests/ui/ip_constant.fixed index 2e3389c1193..c9479682139 100644 --- a/tests/ui/ip_constant.fixed +++ b/tests/ui/ip_constant.fixed @@ -48,6 +48,20 @@ fn literal_test3() { //~^ ip_constant } +fn wrapped_in_parens() { + let _ = std::net::Ipv4Addr::LOCALHOST; + //~^ ip_constant + let _ = std::net::Ipv4Addr::BROADCAST; + //~^ ip_constant + let _ = std::net::Ipv4Addr::UNSPECIFIED; + //~^ ip_constant + + let _ = std::net::Ipv6Addr::LOCALHOST; + //~^ ip_constant + let _ = std::net::Ipv6Addr::UNSPECIFIED; + //~^ ip_constant +} + const CONST_U8_0: u8 = 0; const CONST_U8_1: u8 = 1; const CONST_U8_127: u8 = 127; diff --git a/tests/ui/ip_constant.rs b/tests/ui/ip_constant.rs index 15e0b0551ba..69a5c3b4e92 100644 --- a/tests/ui/ip_constant.rs +++ b/tests/ui/ip_constant.rs @@ -48,6 +48,20 @@ fn literal_test3() { //~^ ip_constant } +fn wrapped_in_parens() { + let _ = (std::net::Ipv4Addr::new(127, 0, 0, 1)); + //~^ ip_constant + let _ = (std::net::Ipv4Addr::new(255, 255, 255, 255)); + //~^ ip_constant + let _ = (std::net::Ipv4Addr::new(0, 0, 0, 0)); + //~^ ip_constant + + let _ = (std::net::Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1)); + //~^ ip_constant + let _ = (std::net::Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0)); + //~^ ip_constant +} + const CONST_U8_0: u8 = 0; const CONST_U8_1: u8 = 1; const CONST_U8_127: u8 = 127; diff --git a/tests/ui/ip_constant.stderr b/tests/ui/ip_constant.stderr index 3e984c6cb3b..07d912b18a5 100644 --- a/tests/ui/ip_constant.stderr +++ b/tests/ui/ip_constant.stderr @@ -180,9 +180,69 @@ LL - let _ = std::net::Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0); LL + let _ = std::net::Ipv6Addr::UNSPECIFIED; | +error: hand-coded well-known IP address + --> tests/ui/ip_constant.rs:52:13 + | +LL | let _ = (std::net::Ipv4Addr::new(127, 0, 0, 1)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use + | +LL - let _ = (std::net::Ipv4Addr::new(127, 0, 0, 1)); +LL + let _ = std::net::Ipv4Addr::LOCALHOST; + | + +error: hand-coded well-known IP address + --> tests/ui/ip_constant.rs:54:13 + | +LL | let _ = (std::net::Ipv4Addr::new(255, 255, 255, 255)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use + | +LL - let _ = (std::net::Ipv4Addr::new(255, 255, 255, 255)); +LL + let _ = std::net::Ipv4Addr::BROADCAST; + | + +error: hand-coded well-known IP address + --> tests/ui/ip_constant.rs:56:13 + | +LL | let _ = (std::net::Ipv4Addr::new(0, 0, 0, 0)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use + | +LL - let _ = (std::net::Ipv4Addr::new(0, 0, 0, 0)); +LL + let _ = std::net::Ipv4Addr::UNSPECIFIED; + | + +error: hand-coded well-known IP address + --> tests/ui/ip_constant.rs:59:13 + | +LL | let _ = (std::net::Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use + | +LL - let _ = (std::net::Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1)); +LL + let _ = std::net::Ipv6Addr::LOCALHOST; + | + error: hand-coded well-known IP address --> tests/ui/ip_constant.rs:61:13 | +LL | let _ = (std::net::Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use + | +LL - let _ = (std::net::Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0)); +LL + let _ = std::net::Ipv6Addr::UNSPECIFIED; + | + +error: hand-coded well-known IP address + --> tests/ui/ip_constant.rs:75:13 + | LL | let _ = Ipv4Addr::new(CONST_U8_127, CONST_U8_0, CONST_U8_0, CONST_U8_1); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | @@ -193,7 +253,7 @@ LL + let _ = Ipv4Addr::LOCALHOST; | error: hand-coded well-known IP address - --> tests/ui/ip_constant.rs:63:13 + --> tests/ui/ip_constant.rs:77:13 | LL | let _ = Ipv4Addr::new(CONST_U8_255, CONST_U8_255, CONST_U8_255, CONST_U8_255); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -205,7 +265,7 @@ LL + let _ = Ipv4Addr::BROADCAST; | error: hand-coded well-known IP address - --> tests/ui/ip_constant.rs:65:13 + --> tests/ui/ip_constant.rs:79:13 | LL | let _ = Ipv4Addr::new(CONST_U8_0, CONST_U8_0, CONST_U8_0, CONST_U8_0); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -217,7 +277,7 @@ LL + let _ = Ipv4Addr::UNSPECIFIED; | error: hand-coded well-known IP address - --> tests/ui/ip_constant.rs:69:13 + --> tests/ui/ip_constant.rs:83:13 | LL | let _ = Ipv6Addr::new( | _____________^ @@ -246,7 +306,7 @@ LL + let _ = Ipv6Addr::LOCALHOST; | error: hand-coded well-known IP address - --> tests/ui/ip_constant.rs:81:13 + --> tests/ui/ip_constant.rs:95:13 | LL | let _ = Ipv6Addr::new( | _____________^ @@ -275,7 +335,7 @@ LL + let _ = Ipv6Addr::UNSPECIFIED; | error: hand-coded well-known IP address - --> tests/ui/ip_constant.rs:96:13 + --> tests/ui/ip_constant.rs:110:13 | LL | let _ = Ipv4Addr::new(126 + 1, 0, 0, 1); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -287,7 +347,7 @@ LL + let _ = Ipv4Addr::LOCALHOST; | error: hand-coded well-known IP address - --> tests/ui/ip_constant.rs:98:13 + --> tests/ui/ip_constant.rs:112:13 | LL | let _ = Ipv4Addr::new(254 + CONST_U8_1, 255, { 255 - CONST_U8_0 }, CONST_U8_255); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -299,7 +359,7 @@ LL + let _ = Ipv4Addr::BROADCAST; | error: hand-coded well-known IP address - --> tests/ui/ip_constant.rs:100:13 + --> tests/ui/ip_constant.rs:114:13 | LL | let _ = Ipv4Addr::new(0, CONST_U8_255 - 255, 0, { 1 + 0 - 1 }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -311,7 +371,7 @@ LL + let _ = Ipv4Addr::UNSPECIFIED; | error: hand-coded well-known IP address - --> tests/ui/ip_constant.rs:104:13 + --> tests/ui/ip_constant.rs:118:13 | LL | let _ = Ipv6Addr::new(0 + CONST_U16_0, 0, 0, 0, 0, 0, 0, 1); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -323,7 +383,7 @@ LL + let _ = Ipv6Addr::LOCALHOST; | error: hand-coded well-known IP address - --> tests/ui/ip_constant.rs:106:13 + --> tests/ui/ip_constant.rs:120:13 | LL | let _ = Ipv6Addr::new(0 + 0, 0, 0, 0, 0, { 2 - 1 - CONST_U16_1 }, 0, 1); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -334,5 +394,5 @@ LL - let _ = Ipv6Addr::new(0 + 0, 0, 0, 0, 0, { 2 - 1 - CONST_U16_1 }, 0, 1) LL + let _ = Ipv6Addr::LOCALHOST; | -error: aborting due to 25 previous errors +error: aborting due to 30 previous errors diff --git a/tests/ui/iter_on_single_items.fixed b/tests/ui/iter_on_single_items.fixed index b43fad6449c..044037aac2e 100644 --- a/tests/ui/iter_on_single_items.fixed +++ b/tests/ui/iter_on_single_items.fixed @@ -66,3 +66,27 @@ fn main() { custom_option::custom_option(); in_macros!(); } + +mod issue14981 { + use std::option::IntoIter; + fn takes_into_iter(_: impl IntoIterator) {} + + fn let_stmt() { + macro_rules! x { + ($e:expr) => { + let _: IntoIter = $e; + }; + } + x!(Some(5).into_iter()); + } + + fn fn_ptr() { + fn some_func(_: IntoIter) -> IntoIter { + todo!() + } + some_func(Some(5).into_iter()); + + const C: fn(IntoIter) -> IntoIter = as IntoIterator>::into_iter; + C(Some(5).into_iter()); + } +} diff --git a/tests/ui/iter_on_single_items.rs b/tests/ui/iter_on_single_items.rs index 625c96d3ef1..c925d0e480f 100644 --- a/tests/ui/iter_on_single_items.rs +++ b/tests/ui/iter_on_single_items.rs @@ -66,3 +66,27 @@ fn main() { custom_option::custom_option(); in_macros!(); } + +mod issue14981 { + use std::option::IntoIter; + fn takes_into_iter(_: impl IntoIterator) {} + + fn let_stmt() { + macro_rules! x { + ($e:expr) => { + let _: IntoIter = $e; + }; + } + x!(Some(5).into_iter()); + } + + fn fn_ptr() { + fn some_func(_: IntoIter) -> IntoIter { + todo!() + } + some_func(Some(5).into_iter()); + + const C: fn(IntoIter) -> IntoIter = as IntoIterator>::into_iter; + C(Some(5).into_iter()); + } +} diff --git a/tests/ui/let_unit.fixed b/tests/ui/let_unit.fixed index 304eacecd94..381d4cac462 100644 --- a/tests/ui/let_unit.fixed +++ b/tests/ui/let_unit.fixed @@ -198,3 +198,14 @@ pub fn issue12594() { returns_result(res).unwrap(); } } + +fn issue15061() { + fn return_unit() {} + fn do_something(x: ()) {} + + let res = (); + return_unit(); + //~^ let_unit_value + do_something(()); + println!("{res:?}"); +} diff --git a/tests/ui/let_unit.rs b/tests/ui/let_unit.rs index a02cb346ff9..cdfc74991c4 100644 --- a/tests/ui/let_unit.rs +++ b/tests/ui/let_unit.rs @@ -198,3 +198,13 @@ pub fn issue12594() { returns_result(res).unwrap(); } } + +fn issue15061() { + fn return_unit() {} + fn do_something(x: ()) {} + + let res = return_unit(); + //~^ let_unit_value + do_something(res); + println!("{res:?}"); +} diff --git a/tests/ui/let_unit.stderr b/tests/ui/let_unit.stderr index d743110c99d..637c9ff686b 100644 --- a/tests/ui/let_unit.stderr +++ b/tests/ui/let_unit.stderr @@ -68,5 +68,19 @@ LL ~ returns_result(()).unwrap(); LL ~ returns_result(()).unwrap(); | -error: aborting due to 4 previous errors +error: this let-binding has unit value + --> tests/ui/let_unit.rs:206:5 + | +LL | let res = return_unit(); + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: replace variable usages with `()` + | +LL ~ let res = (); +LL ~ return_unit(); +LL | +LL ~ do_something(()); + | + +error: aborting due to 5 previous errors diff --git a/tests/ui/let_with_type_underscore.fixed b/tests/ui/let_with_type_underscore.fixed index 7a4af4e3d1e..6339fe595c2 100644 --- a/tests/ui/let_with_type_underscore.fixed +++ b/tests/ui/let_with_type_underscore.fixed @@ -45,3 +45,15 @@ fn main() { x = (); }; } + +fn issue15377() { + let (a) = 0; + //~^ let_with_type_underscore + let ((a)) = 0; + //~^ let_with_type_underscore + let ((a,)) = (0,); + //~^ let_with_type_underscore + #[rustfmt::skip] + let ( (a ) ) = 0; + //~^ let_with_type_underscore +} diff --git a/tests/ui/let_with_type_underscore.rs b/tests/ui/let_with_type_underscore.rs index a7c2f598b56..bd85346cf01 100644 --- a/tests/ui/let_with_type_underscore.rs +++ b/tests/ui/let_with_type_underscore.rs @@ -45,3 +45,15 @@ fn main() { x = (); }; } + +fn issue15377() { + let (a): _ = 0; + //~^ let_with_type_underscore + let ((a)): _ = 0; + //~^ let_with_type_underscore + let ((a,)): _ = (0,); + //~^ let_with_type_underscore + #[rustfmt::skip] + let ( (a ) ): _ = 0; + //~^ let_with_type_underscore +} diff --git a/tests/ui/let_with_type_underscore.stderr b/tests/ui/let_with_type_underscore.stderr index 9179f992207..c3f6c82397b 100644 --- a/tests/ui/let_with_type_underscore.stderr +++ b/tests/ui/let_with_type_underscore.stderr @@ -60,5 +60,53 @@ LL - let x : _ = 1; LL + let x = 1; | -error: aborting due to 5 previous errors +error: variable declared with type underscore + --> tests/ui/let_with_type_underscore.rs:50:5 + | +LL | let (a): _ = 0; + | ^^^^^^^^^^^^^^^ + | +help: remove the explicit type `_` declaration + | +LL - let (a): _ = 0; +LL + let (a) = 0; + | + +error: variable declared with type underscore + --> tests/ui/let_with_type_underscore.rs:52:5 + | +LL | let ((a)): _ = 0; + | ^^^^^^^^^^^^^^^^^ + | +help: remove the explicit type `_` declaration + | +LL - let ((a)): _ = 0; +LL + let ((a)) = 0; + | + +error: variable declared with type underscore + --> tests/ui/let_with_type_underscore.rs:54:5 + | +LL | let ((a,)): _ = (0,); + | ^^^^^^^^^^^^^^^^^^^^^ + | +help: remove the explicit type `_` declaration + | +LL - let ((a,)): _ = (0,); +LL + let ((a,)) = (0,); + | + +error: variable declared with type underscore + --> tests/ui/let_with_type_underscore.rs:57:5 + | +LL | let ( (a ) ): _ = 0; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: remove the explicit type `_` declaration + | +LL - let ( (a ) ): _ = 0; +LL + let ( (a ) ) = 0; + | + +error: aborting due to 9 previous errors diff --git a/tests/ui/manual_assert.edition2018.stderr b/tests/ui/manual_assert.edition2018.stderr index 221cddf069d..2e9c9045caa 100644 --- a/tests/ui/manual_assert.edition2018.stderr +++ b/tests/ui/manual_assert.edition2018.stderr @@ -167,7 +167,7 @@ LL - comment */ LL - /// Doc comment LL - panic!("panic with comment") // comment after `panic!` LL - } -LL + assert!(!(a > 2), "panic with comment"); +LL + assert!(a <= 2, "panic with comment"); | error: only a `panic!` in `if`-then statement @@ -186,7 +186,7 @@ LL - const BAR: () = if N == 0 { LL - LL - panic!() LL - }; -LL + const BAR: () = assert!(!(N == 0), ); +LL + const BAR: () = assert!(N != 0, ); | error: only a `panic!` in `if`-then statement diff --git a/tests/ui/manual_assert.edition2021.stderr b/tests/ui/manual_assert.edition2021.stderr index 221cddf069d..2e9c9045caa 100644 --- a/tests/ui/manual_assert.edition2021.stderr +++ b/tests/ui/manual_assert.edition2021.stderr @@ -167,7 +167,7 @@ LL - comment */ LL - /// Doc comment LL - panic!("panic with comment") // comment after `panic!` LL - } -LL + assert!(!(a > 2), "panic with comment"); +LL + assert!(a <= 2, "panic with comment"); | error: only a `panic!` in `if`-then statement @@ -186,7 +186,7 @@ LL - const BAR: () = if N == 0 { LL - LL - panic!() LL - }; -LL + const BAR: () = assert!(!(N == 0), ); +LL + const BAR: () = assert!(N != 0, ); | error: only a `panic!` in `if`-then statement diff --git a/tests/ui/manual_strip.rs b/tests/ui/manual_strip.rs index 086b75a3987..0fdaa1c045e 100644 --- a/tests/ui/manual_strip.rs +++ b/tests/ui/manual_strip.rs @@ -75,7 +75,7 @@ fn main() { s4[2..].to_string(); } - // Don't propose to reuse the `stripped` identifier as it is overriden + // Don't propose to reuse the `stripped` identifier as it is overridden if s.starts_with("ab") { let stripped = &s["ab".len()..]; //~^ ERROR: stripping a prefix manually diff --git a/tests/ui/manual_unwrap_or_default.fixed b/tests/ui/manual_unwrap_or_default.fixed index 41ca44ceef4..189fe876aa5 100644 --- a/tests/ui/manual_unwrap_or_default.fixed +++ b/tests/ui/manual_unwrap_or_default.fixed @@ -102,7 +102,7 @@ fn issue_12928() { let y = if let Some(Y(a, ..)) = x { a } else { 0 }; } -// For symetry with `manual_unwrap_or` test +// For symmetry with `manual_unwrap_or` test fn allowed_manual_unwrap_or_zero() -> u32 { Some(42).unwrap_or_default() } diff --git a/tests/ui/manual_unwrap_or_default.rs b/tests/ui/manual_unwrap_or_default.rs index 343fbc4879c..ca87926763c 100644 --- a/tests/ui/manual_unwrap_or_default.rs +++ b/tests/ui/manual_unwrap_or_default.rs @@ -138,7 +138,7 @@ fn issue_12928() { let y = if let Some(Y(a, ..)) = x { a } else { 0 }; } -// For symetry with `manual_unwrap_or` test +// For symmetry with `manual_unwrap_or` test fn allowed_manual_unwrap_or_zero() -> u32 { if let Some(x) = Some(42) { //~^ manual_unwrap_or_default diff --git a/tests/ui/map_identity.fixed b/tests/ui/map_identity.fixed index b82d3e6d956..6c971ba6338 100644 --- a/tests/ui/map_identity.fixed +++ b/tests/ui/map_identity.fixed @@ -1,5 +1,5 @@ #![warn(clippy::map_identity)] -#![allow(clippy::needless_return)] +#![allow(clippy::needless_return, clippy::disallowed_names)] fn main() { let x: [u16; 3] = [1, 2, 3]; @@ -99,3 +99,65 @@ fn issue15198() { let _ = x.iter().copied(); //~^ map_identity } + +mod foo { + #[derive(Clone, Copy)] + pub struct Foo { + pub foo: u8, + pub bar: u8, + } + + #[derive(Clone, Copy)] + pub struct Foo2(pub u8, pub u8); +} +use foo::{Foo, Foo2}; + +struct Bar { + foo: u8, + bar: u8, +} + +struct Bar2(u8, u8); + +fn structs() { + let x = [Foo { foo: 0, bar: 0 }]; + + let _ = x.into_iter(); + //~^ map_identity + + // still lint when different paths are used for the same struct + let _ = x.into_iter(); + //~^ map_identity + + // don't lint: same fields but different structs + let _ = x.into_iter().map(|Foo { foo, bar }| Bar { foo, bar }); + + // still lint with redundant field names + #[allow(clippy::redundant_field_names)] + let _ = x.into_iter(); + //~^ map_identity + + // still lint with field order change + let _ = x.into_iter(); + //~^ map_identity + + // don't lint: switched field assignment + let _ = x.into_iter().map(|Foo { foo, bar }| Foo { foo: bar, bar: foo }); +} + +fn tuple_structs() { + let x = [Foo2(0, 0)]; + + let _ = x.into_iter(); + //~^ map_identity + + // still lint when different paths are used for the same struct + let _ = x.into_iter(); + //~^ map_identity + + // don't lint: same fields but different structs + let _ = x.into_iter().map(|Foo2(foo, bar)| Bar2(foo, bar)); + + // don't lint: switched field assignment + let _ = x.into_iter().map(|Foo2(foo, bar)| Foo2(bar, foo)); +} diff --git a/tests/ui/map_identity.rs b/tests/ui/map_identity.rs index c295bf87270..59dcfcda3b6 100644 --- a/tests/ui/map_identity.rs +++ b/tests/ui/map_identity.rs @@ -1,5 +1,5 @@ #![warn(clippy::map_identity)] -#![allow(clippy::needless_return)] +#![allow(clippy::needless_return, clippy::disallowed_names)] fn main() { let x: [u16; 3] = [1, 2, 3]; @@ -105,3 +105,65 @@ fn issue15198() { let _ = x.iter().copied().map(|[x, y]| [x, y]); //~^ map_identity } + +mod foo { + #[derive(Clone, Copy)] + pub struct Foo { + pub foo: u8, + pub bar: u8, + } + + #[derive(Clone, Copy)] + pub struct Foo2(pub u8, pub u8); +} +use foo::{Foo, Foo2}; + +struct Bar { + foo: u8, + bar: u8, +} + +struct Bar2(u8, u8); + +fn structs() { + let x = [Foo { foo: 0, bar: 0 }]; + + let _ = x.into_iter().map(|Foo { foo, bar }| Foo { foo, bar }); + //~^ map_identity + + // still lint when different paths are used for the same struct + let _ = x.into_iter().map(|Foo { foo, bar }| foo::Foo { foo, bar }); + //~^ map_identity + + // don't lint: same fields but different structs + let _ = x.into_iter().map(|Foo { foo, bar }| Bar { foo, bar }); + + // still lint with redundant field names + #[allow(clippy::redundant_field_names)] + let _ = x.into_iter().map(|Foo { foo, bar }| Foo { foo: foo, bar: bar }); + //~^ map_identity + + // still lint with field order change + let _ = x.into_iter().map(|Foo { foo, bar }| Foo { bar, foo }); + //~^ map_identity + + // don't lint: switched field assignment + let _ = x.into_iter().map(|Foo { foo, bar }| Foo { foo: bar, bar: foo }); +} + +fn tuple_structs() { + let x = [Foo2(0, 0)]; + + let _ = x.into_iter().map(|Foo2(foo, bar)| Foo2(foo, bar)); + //~^ map_identity + + // still lint when different paths are used for the same struct + let _ = x.into_iter().map(|Foo2(foo, bar)| foo::Foo2(foo, bar)); + //~^ map_identity + + // don't lint: same fields but different structs + let _ = x.into_iter().map(|Foo2(foo, bar)| Bar2(foo, bar)); + + // don't lint: switched field assignment + let _ = x.into_iter().map(|Foo2(foo, bar)| Foo2(bar, foo)); +} diff --git a/tests/ui/map_identity.stderr b/tests/ui/map_identity.stderr index 9b624a0dc75..a50c0d6b87b 100644 --- a/tests/ui/map_identity.stderr +++ b/tests/ui/map_identity.stderr @@ -93,5 +93,41 @@ error: unnecessary map of the identity function LL | let _ = x.iter().copied().map(|[x, y]| [x, y]); | ^^^^^^^^^^^^^^^^^^^^^ help: remove the call to `map` -error: aborting due to 14 previous errors +error: unnecessary map of the identity function + --> tests/ui/map_identity.rs:131:26 + | +LL | let _ = x.into_iter().map(|Foo { foo, bar }| Foo { foo, bar }); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove the call to `map` + +error: unnecessary map of the identity function + --> tests/ui/map_identity.rs:135:26 + | +LL | let _ = x.into_iter().map(|Foo { foo, bar }| foo::Foo { foo, bar }); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove the call to `map` + +error: unnecessary map of the identity function + --> tests/ui/map_identity.rs:143:26 + | +LL | let _ = x.into_iter().map(|Foo { foo, bar }| Foo { foo: foo, bar: bar }); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove the call to `map` + +error: unnecessary map of the identity function + --> tests/ui/map_identity.rs:147:26 + | +LL | let _ = x.into_iter().map(|Foo { foo, bar }| Foo { bar, foo }); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove the call to `map` + +error: unnecessary map of the identity function + --> tests/ui/map_identity.rs:157:26 + | +LL | let _ = x.into_iter().map(|Foo2(foo, bar)| Foo2(foo, bar)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove the call to `map` + +error: unnecessary map of the identity function + --> tests/ui/map_identity.rs:161:26 + | +LL | let _ = x.into_iter().map(|Foo2(foo, bar)| foo::Foo2(foo, bar)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove the call to `map` + +error: aborting due to 20 previous errors diff --git a/tests/ui/match_single_binding.fixed b/tests/ui/match_single_binding.fixed index e11dea35204..e29fb87dbc3 100644 --- a/tests/ui/match_single_binding.fixed +++ b/tests/ui/match_single_binding.fixed @@ -204,3 +204,65 @@ mod issue14991 { }], } } + +mod issue15018 { + fn used_later(a: i32, b: i32, c: i32) { + let x = 1; + { + let (x, y, z) = (a, b, c); + println!("{} {} {}", x, y, z); + } + println!("x = {x}"); + } + + fn not_used_later(a: i32, b: i32, c: i32) { + let (x, y, z) = (a, b, c); + println!("{} {} {}", x, y, z) + } + + #[allow(irrefutable_let_patterns)] + fn not_used_later_but_shadowed(a: i32, b: i32, c: i32) { + let (x, y, z) = (a, b, c); + println!("{} {} {}", x, y, z); + let x = 1; + println!("x = {x}"); + } + + #[allow(irrefutable_let_patterns)] + fn not_used_later_but_shadowed_nested(a: i32, b: i32, c: i32) { + let (x, y, z) = (a, b, c); + println!("{} {} {}", x, y, z); + if let (x, y, z) = (a, b, c) { + println!("{} {} {}", x, y, z) + } + + { + let x: i32 = 1; + { + let (x, y, z) = (a, b, c); + println!("{} {} {}", x, y, z); + } + if let (x, y, z) = (a, x, c) { + println!("{} {} {}", x, y, z) + } + } + + { + let (x, y, z) = (a, b, c); + println!("{} {} {}", x, y, z); + let fn_ = |y| { + println!("{} {} {}", a, b, y); + }; + fn_(c); + } + } +} + +#[allow(clippy::short_circuit_statement)] +fn issue15269(a: usize, b: usize, c: usize) -> bool { + a < b + && b < c; + + a < b + && b < c +} diff --git a/tests/ui/match_single_binding.rs b/tests/ui/match_single_binding.rs index d498da30fc8..ede1ab32beb 100644 --- a/tests/ui/match_single_binding.rs +++ b/tests/ui/match_single_binding.rs @@ -267,3 +267,79 @@ mod issue14991 { }], } } + +mod issue15018 { + fn used_later(a: i32, b: i32, c: i32) { + let x = 1; + match (a, b, c) { + //~^ match_single_binding + (x, y, z) => println!("{} {} {}", x, y, z), + } + println!("x = {x}"); + } + + fn not_used_later(a: i32, b: i32, c: i32) { + match (a, b, c) { + //~^ match_single_binding + (x, y, z) => println!("{} {} {}", x, y, z), + } + } + + #[allow(irrefutable_let_patterns)] + fn not_used_later_but_shadowed(a: i32, b: i32, c: i32) { + match (a, b, c) { + //~^ match_single_binding + (x, y, z) => println!("{} {} {}", x, y, z), + } + let x = 1; + println!("x = {x}"); + } + + #[allow(irrefutable_let_patterns)] + fn not_used_later_but_shadowed_nested(a: i32, b: i32, c: i32) { + match (a, b, c) { + //~^ match_single_binding + (x, y, z) => println!("{} {} {}", x, y, z), + } + if let (x, y, z) = (a, b, c) { + println!("{} {} {}", x, y, z) + } + + { + let x: i32 = 1; + match (a, b, c) { + //~^ match_single_binding + (x, y, z) => println!("{} {} {}", x, y, z), + } + if let (x, y, z) = (a, x, c) { + println!("{} {} {}", x, y, z) + } + } + + { + match (a, b, c) { + //~^ match_single_binding + (x, y, z) => println!("{} {} {}", x, y, z), + } + let fn_ = |y| { + println!("{} {} {}", a, b, y); + }; + fn_(c); + } + } +} + +#[allow(clippy::short_circuit_statement)] +fn issue15269(a: usize, b: usize, c: usize) -> bool { + a < b + && match b { + //~^ match_single_binding + b => b < c, + }; + + a < b + && match (a, b) { + //~^ match_single_binding + (a, b) => b < c, + } +} diff --git a/tests/ui/match_single_binding.stderr b/tests/ui/match_single_binding.stderr index f274f80c81d..eea71777890 100644 --- a/tests/ui/match_single_binding.stderr +++ b/tests/ui/match_single_binding.stderr @@ -411,5 +411,119 @@ LL ~ let _n = 1; LL + 42 | -error: aborting due to 29 previous errors +error: this match could be written as a `let` statement + --> tests/ui/match_single_binding.rs:274:9 + | +LL | / match (a, b, c) { +LL | | +LL | | (x, y, z) => println!("{} {} {}", x, y, z), +LL | | } + | |_________^ + | +help: consider using a `let` statement + | +LL ~ { +LL + let (x, y, z) = (a, b, c); +LL + println!("{} {} {}", x, y, z); +LL + } + | + +error: this match could be written as a `let` statement + --> tests/ui/match_single_binding.rs:282:9 + | +LL | / match (a, b, c) { +LL | | +LL | | (x, y, z) => println!("{} {} {}", x, y, z), +LL | | } + | |_________^ + | +help: consider using a `let` statement + | +LL ~ let (x, y, z) = (a, b, c); +LL + println!("{} {} {}", x, y, z) + | + +error: this match could be written as a `let` statement + --> tests/ui/match_single_binding.rs:290:9 + | +LL | / match (a, b, c) { +LL | | +LL | | (x, y, z) => println!("{} {} {}", x, y, z), +LL | | } + | |_________^ + | +help: consider using a `let` statement + | +LL ~ let (x, y, z) = (a, b, c); +LL + println!("{} {} {}", x, y, z); + | + +error: this match could be written as a `let` statement + --> tests/ui/match_single_binding.rs:300:9 + | +LL | / match (a, b, c) { +LL | | +LL | | (x, y, z) => println!("{} {} {}", x, y, z), +LL | | } + | |_________^ + | +help: consider using a `let` statement + | +LL ~ let (x, y, z) = (a, b, c); +LL + println!("{} {} {}", x, y, z); + | + +error: this match could be written as a `let` statement + --> tests/ui/match_single_binding.rs:310:13 + | +LL | / match (a, b, c) { +LL | | +LL | | (x, y, z) => println!("{} {} {}", x, y, z), +LL | | } + | |_____________^ + | +help: consider using a `let` statement + | +LL ~ { +LL + let (x, y, z) = (a, b, c); +LL + println!("{} {} {}", x, y, z); +LL + } + | + +error: this match could be written as a `let` statement + --> tests/ui/match_single_binding.rs:320:13 + | +LL | / match (a, b, c) { +LL | | +LL | | (x, y, z) => println!("{} {} {}", x, y, z), +LL | | } + | |_____________^ + | +help: consider using a `let` statement + | +LL ~ let (x, y, z) = (a, b, c); +LL + println!("{} {} {}", x, y, z); + | + +error: this match could be replaced by its body itself + --> tests/ui/match_single_binding.rs:335:12 + | +LL | && match b { + | ____________^ +LL | | +LL | | b => b < c, +LL | | }; + | |_________^ help: consider using the match body instead: `b < c` + +error: this match could be replaced by its body itself + --> tests/ui/match_single_binding.rs:341:12 + | +LL | && match (a, b) { + | ____________^ +LL | | +LL | | (a, b) => b < c, +LL | | } + | |_________^ help: consider using the match body instead: `b < c` + +error: aborting due to 37 previous errors diff --git a/tests/ui/min_ident_chars.rs b/tests/ui/min_ident_chars.rs index f473ac848a8..e2f82e2a182 100644 --- a/tests/ui/min_ident_chars.rs +++ b/tests/ui/min_ident_chars.rs @@ -124,3 +124,52 @@ fn wrong_pythagoras(a: f32, b: f32) -> f32 { mod issue_11163 { struct Array([T; N]); } + +struct Issue13396; + +impl core::fmt::Display for Issue13396 { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!(f, "Issue13396") + } +} + +impl core::fmt::Debug for Issue13396 { + fn fmt(&self, g: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + //~^ min_ident_chars + write!(g, "Issue13396") + } +} + +fn issue13396() { + let a = |f: i8| f; + //~^ min_ident_chars + //~| min_ident_chars +} + +trait D { + //~^ min_ident_chars + fn f(g: i32); + //~^ min_ident_chars + //~| min_ident_chars + fn long(long: i32); + + fn g(arg: i8) { + //~^ min_ident_chars + fn c(d: u8) {} + //~^ min_ident_chars + //~| min_ident_chars + } +} + +impl D for Issue13396 { + fn f(g: i32) { + fn h() {} + //~^ min_ident_chars + fn inner(a: i32) {} + //~^ min_ident_chars + let a = |f: String| f; + //~^ min_ident_chars + //~| min_ident_chars + } + fn long(long: i32) {} +} diff --git a/tests/ui/min_ident_chars.stderr b/tests/ui/min_ident_chars.stderr index bd6c45cf648..36bf89e79f0 100644 --- a/tests/ui/min_ident_chars.stderr +++ b/tests/ui/min_ident_chars.stderr @@ -193,5 +193,83 @@ error: this ident consists of a single char LL | fn wrong_pythagoras(a: f32, b: f32) -> f32 { | ^ -error: aborting due to 32 previous errors +error: this ident consists of a single char + --> tests/ui/min_ident_chars.rs:137:19 + | +LL | fn fmt(&self, g: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + | ^ + +error: this ident consists of a single char + --> tests/ui/min_ident_chars.rs:144:14 + | +LL | let a = |f: i8| f; + | ^ + +error: this ident consists of a single char + --> tests/ui/min_ident_chars.rs:144:9 + | +LL | let a = |f: i8| f; + | ^ + +error: this ident consists of a single char + --> tests/ui/min_ident_chars.rs:149:7 + | +LL | trait D { + | ^ + +error: this ident consists of a single char + --> tests/ui/min_ident_chars.rs:151:10 + | +LL | fn f(g: i32); + | ^ + +error: this ident consists of a single char + --> tests/ui/min_ident_chars.rs:151:8 + | +LL | fn f(g: i32); + | ^ + +error: this ident consists of a single char + --> tests/ui/min_ident_chars.rs:156:8 + | +LL | fn g(arg: i8) { + | ^ + +error: this ident consists of a single char + --> tests/ui/min_ident_chars.rs:158:12 + | +LL | fn c(d: u8) {} + | ^ + +error: this ident consists of a single char + --> tests/ui/min_ident_chars.rs:158:14 + | +LL | fn c(d: u8) {} + | ^ + +error: this ident consists of a single char + --> tests/ui/min_ident_chars.rs:166:12 + | +LL | fn h() {} + | ^ + +error: this ident consists of a single char + --> tests/ui/min_ident_chars.rs:168:18 + | +LL | fn inner(a: i32) {} + | ^ + +error: this ident consists of a single char + --> tests/ui/min_ident_chars.rs:170:18 + | +LL | let a = |f: String| f; + | ^ + +error: this ident consists of a single char + --> tests/ui/min_ident_chars.rs:170:13 + | +LL | let a = |f: String| f; + | ^ + +error: aborting due to 45 previous errors diff --git a/tests/ui/needless_collect_indirect.rs b/tests/ui/needless_collect_indirect.rs index 57d0f2b9948..fff6d2f34b8 100644 --- a/tests/ui/needless_collect_indirect.rs +++ b/tests/ui/needless_collect_indirect.rs @@ -1,5 +1,4 @@ -#![allow(clippy::uninlined_format_args, clippy::useless_vec)] -#![allow(clippy::needless_if, clippy::uninlined_format_args)] +#![allow(clippy::uninlined_format_args, clippy::useless_vec, clippy::needless_if)] #![warn(clippy::needless_collect)] //@no-rustfix use std::collections::{BinaryHeap, HashMap, HashSet, LinkedList, VecDeque}; diff --git a/tests/ui/needless_collect_indirect.stderr b/tests/ui/needless_collect_indirect.stderr index c7bf1b14df8..24523c9f97b 100644 --- a/tests/ui/needless_collect_indirect.stderr +++ b/tests/ui/needless_collect_indirect.stderr @@ -1,5 +1,5 @@ error: avoid using `collect()` when not needed - --> tests/ui/needless_collect_indirect.rs:9:39 + --> tests/ui/needless_collect_indirect.rs:8:39 | LL | let indirect_iter = sample.iter().collect::>(); | ^^^^^^^ @@ -18,7 +18,7 @@ LL ~ sample.iter().map(|x| (x, x + 1)).collect::>(); | error: avoid using `collect()` when not needed - --> tests/ui/needless_collect_indirect.rs:13:38 + --> tests/ui/needless_collect_indirect.rs:12:38 | LL | let indirect_len = sample.iter().collect::>(); | ^^^^^^^ @@ -35,7 +35,7 @@ LL ~ sample.iter().count(); | error: avoid using `collect()` when not needed - --> tests/ui/needless_collect_indirect.rs:17:40 + --> tests/ui/needless_collect_indirect.rs:16:40 | LL | let indirect_empty = sample.iter().collect::>(); | ^^^^^^^ @@ -52,7 +52,7 @@ LL ~ sample.iter().next().is_none(); | error: avoid using `collect()` when not needed - --> tests/ui/needless_collect_indirect.rs:21:43 + --> tests/ui/needless_collect_indirect.rs:20:43 | LL | let indirect_contains = sample.iter().collect::>(); | ^^^^^^^ @@ -69,7 +69,7 @@ LL ~ sample.iter().any(|x| x == &5); | error: avoid using `collect()` when not needed - --> tests/ui/needless_collect_indirect.rs:35:48 + --> tests/ui/needless_collect_indirect.rs:34:48 | LL | let non_copy_contains = sample.into_iter().collect::>(); | ^^^^^^^ @@ -86,7 +86,7 @@ LL ~ sample.into_iter().any(|x| x == a); | error: avoid using `collect()` when not needed - --> tests/ui/needless_collect_indirect.rs:66:51 + --> tests/ui/needless_collect_indirect.rs:65:51 | LL | let buffer: Vec<&str> = string.split('/').collect(); | ^^^^^^^ @@ -103,7 +103,7 @@ LL ~ string.split('/').count() | error: avoid using `collect()` when not needed - --> tests/ui/needless_collect_indirect.rs:73:55 + --> tests/ui/needless_collect_indirect.rs:72:55 | LL | let indirect_len: VecDeque<_> = sample.iter().collect(); | ^^^^^^^ @@ -120,7 +120,7 @@ LL ~ sample.iter().count() | error: avoid using `collect()` when not needed - --> tests/ui/needless_collect_indirect.rs:80:57 + --> tests/ui/needless_collect_indirect.rs:79:57 | LL | let indirect_len: LinkedList<_> = sample.iter().collect(); | ^^^^^^^ @@ -137,7 +137,7 @@ LL ~ sample.iter().count() | error: avoid using `collect()` when not needed - --> tests/ui/needless_collect_indirect.rs:87:57 + --> tests/ui/needless_collect_indirect.rs:86:57 | LL | let indirect_len: BinaryHeap<_> = sample.iter().collect(); | ^^^^^^^ @@ -154,7 +154,7 @@ LL ~ sample.iter().count() | error: avoid using `collect()` when not needed - --> tests/ui/needless_collect_indirect.rs:149:59 + --> tests/ui/needless_collect_indirect.rs:148:59 | LL | let y: Vec = vec.iter().map(|k| k * k).collect(); | ^^^^^^^ @@ -172,7 +172,7 @@ LL ~ vec.iter().map(|k| k * k).any(|x| x == i); | error: avoid using `collect()` when not needed - --> tests/ui/needless_collect_indirect.rs:176:59 + --> tests/ui/needless_collect_indirect.rs:175:59 | LL | let y: Vec = vec.iter().map(|k| k * k).collect(); | ^^^^^^^ @@ -190,7 +190,7 @@ LL ~ vec.iter().map(|k| k * k).any(|x| x == n); | error: avoid using `collect()` when not needed - --> tests/ui/needless_collect_indirect.rs:207:63 + --> tests/ui/needless_collect_indirect.rs:206:63 | LL | let y: Vec = vec.iter().map(|k| k * k).collect(); | ^^^^^^^ @@ -208,7 +208,7 @@ LL ~ vec.iter().map(|k| k * k).any(|x| x == n); | error: avoid using `collect()` when not needed - --> tests/ui/needless_collect_indirect.rs:245:59 + --> tests/ui/needless_collect_indirect.rs:244:59 | LL | let y: Vec = vec.iter().map(|k| k * k).collect(); | ^^^^^^^ @@ -226,7 +226,7 @@ LL ~ vec.iter().map(|k| k * k).any(|x| x == n); | error: avoid using `collect()` when not needed - --> tests/ui/needless_collect_indirect.rs:272:26 + --> tests/ui/needless_collect_indirect.rs:271:26 | LL | let w = v.iter().collect::>(); | ^^^^^^^ @@ -244,7 +244,7 @@ LL ~ for _ in 0..v.iter().count() { | error: avoid using `collect()` when not needed - --> tests/ui/needless_collect_indirect.rs:296:30 + --> tests/ui/needless_collect_indirect.rs:295:30 | LL | let mut w = v.iter().collect::>(); | ^^^^^^^ @@ -262,7 +262,7 @@ LL ~ while 1 == v.iter().count() { | error: avoid using `collect()` when not needed - --> tests/ui/needless_collect_indirect.rs:320:30 + --> tests/ui/needless_collect_indirect.rs:319:30 | LL | let mut w = v.iter().collect::>(); | ^^^^^^^ diff --git a/tests/ui/nonminimal_bool.rs b/tests/ui/nonminimal_bool.rs index 1eecc3dee3d..cacce9a7d1c 100644 --- a/tests/ui/nonminimal_bool.rs +++ b/tests/ui/nonminimal_bool.rs @@ -236,3 +236,21 @@ mod issue14404 { } } } + +fn dont_simplify_double_not_if_types_differ() { + struct S; + + impl std::ops::Not for S { + type Output = bool; + fn not(self) -> bool { + true + } + } + + // The lint must propose `if !!S`, not `if S`. + // FIXME: `bool_comparison` will propose to use `S == true` + // which is invalid. + if !S != true {} + //~^ nonminimal_bool + //~| bool_comparison +} diff --git a/tests/ui/nonminimal_bool.stderr b/tests/ui/nonminimal_bool.stderr index ecb82a23da0..c20412974b2 100644 --- a/tests/ui/nonminimal_bool.stderr +++ b/tests/ui/nonminimal_bool.stderr @@ -179,7 +179,7 @@ error: inequality checks against true can be replaced by a negation --> tests/ui/nonminimal_bool.rs:186:8 | LL | if !b != true {} - | ^^^^^^^^^^ help: try simplifying it as shown: `!!b` + | ^^^^^^^^^^ help: try simplifying it as shown: `b` error: this boolean expression can be simplified --> tests/ui/nonminimal_bool.rs:189:8 @@ -209,7 +209,7 @@ error: inequality checks against true can be replaced by a negation --> tests/ui/nonminimal_bool.rs:193:8 | LL | if true != !b {} - | ^^^^^^^^^^ help: try simplifying it as shown: `!!b` + | ^^^^^^^^^^ help: try simplifying it as shown: `b` error: this boolean expression can be simplified --> tests/ui/nonminimal_bool.rs:196:8 @@ -235,5 +235,17 @@ error: this boolean expression can be simplified LL | if !(matches!(ty, TyKind::Ref(_, _, _)) && !is_mutable(&expr)) { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `!matches!(ty, TyKind::Ref(_, _, _)) || is_mutable(&expr)` -error: aborting due to 31 previous errors +error: this boolean expression can be simplified + --> tests/ui/nonminimal_bool.rs:253:8 + | +LL | if !S != true {} + | ^^^^^^^^^^ help: try: `S == true` + +error: inequality checks against true can be replaced by a negation + --> tests/ui/nonminimal_bool.rs:253:8 + | +LL | if !S != true {} + | ^^^^^^^^^^ help: try simplifying it as shown: `!!S` + +error: aborting due to 33 previous errors diff --git a/tests/ui/option_if_let_else.fixed b/tests/ui/option_if_let_else.fixed index fe3ac9e8f92..0f86de5646c 100644 --- a/tests/ui/option_if_let_else.fixed +++ b/tests/ui/option_if_let_else.fixed @@ -302,3 +302,15 @@ mod issue11059 { if let Some(o) = o { o } else { &S } } } + +fn issue15379() { + let opt = Some(0usize); + let opt_raw_ptr = &opt as *const Option; + let _ = unsafe { (*opt_raw_ptr).map_or(1, |o| o) }; + //~^ option_if_let_else +} + +fn issue15002() { + let res: Result = Ok("_".to_string()); + let _ = res.map_or_else(|_| String::new(), |s| s.clone()); +} diff --git a/tests/ui/option_if_let_else.rs b/tests/ui/option_if_let_else.rs index 5b7498bc8e2..7aabd778f87 100644 --- a/tests/ui/option_if_let_else.rs +++ b/tests/ui/option_if_let_else.rs @@ -365,3 +365,19 @@ mod issue11059 { if let Some(o) = o { o } else { &S } } } + +fn issue15379() { + let opt = Some(0usize); + let opt_raw_ptr = &opt as *const Option; + let _ = unsafe { if let Some(o) = *opt_raw_ptr { o } else { 1 } }; + //~^ option_if_let_else +} + +fn issue15002() { + let res: Result = Ok("_".to_string()); + let _ = match res { + //~^ option_if_let_else + Ok(s) => s.clone(), + Err(_) => String::new(), + }; +} diff --git a/tests/ui/option_if_let_else.stderr b/tests/ui/option_if_let_else.stderr index 9eb41f81a53..2e2fe6f2049 100644 --- a/tests/ui/option_if_let_else.stderr +++ b/tests/ui/option_if_let_else.stderr @@ -334,5 +334,22 @@ error: use Option::map_or_else instead of an if let/else LL | let mut _hm = if let Some(hm) = &opt { hm.clone() } else { new_map!() }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `opt.as_ref().map_or_else(|| new_map!(), |hm| hm.clone())` -error: aborting due to 25 previous errors +error: use Option::map_or instead of an if let/else + --> tests/ui/option_if_let_else.rs:372:22 + | +LL | let _ = unsafe { if let Some(o) = *opt_raw_ptr { o } else { 1 } }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `(*opt_raw_ptr).map_or(1, |o| o)` + +error: use Option::map_or_else instead of an if let/else + --> tests/ui/option_if_let_else.rs:378:13 + | +LL | let _ = match res { + | _____________^ +LL | | +LL | | Ok(s) => s.clone(), +LL | | Err(_) => String::new(), +LL | | }; + | |_____^ help: try: `res.map_or_else(|_| String::new(), |s| s.clone())` + +error: aborting due to 27 previous errors diff --git a/tests/ui/search_is_some.rs b/tests/ui/search_is_some.rs index 4143b8bfba5..802d27449ab 100644 --- a/tests/ui/search_is_some.rs +++ b/tests/ui/search_is_some.rs @@ -87,3 +87,18 @@ fn is_none() { let _ = (0..1).find(some_closure).is_none(); //~^ search_is_some } + +#[allow(clippy::match_like_matches_macro)] +fn issue15102() { + let values = [None, Some(3)]; + let has_even = values + //~^ search_is_some + .iter() + .find(|v| match v { + Some(x) if x % 2 == 0 => true, + _ => false, + }) + .is_some(); + + println!("{has_even}"); +} diff --git a/tests/ui/search_is_some.stderr b/tests/ui/search_is_some.stderr index d9a43c8915e..d5412f90111 100644 --- a/tests/ui/search_is_some.stderr +++ b/tests/ui/search_is_some.stderr @@ -90,5 +90,20 @@ error: called `is_none()` after searching an `Iterator` with `find` LL | let _ = (0..1).find(some_closure).is_none(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `!(0..1).any(some_closure)` -error: aborting due to 8 previous errors +error: called `is_some()` after searching an `Iterator` with `find` + --> tests/ui/search_is_some.rs:94:20 + | +LL | let has_even = values + | ____________________^ +LL | | +LL | | .iter() +LL | | .find(|v| match v { +... | +LL | | }) +LL | | .is_some(); + | |__________________^ + | + = help: this is more succinctly expressed by calling `any()` + +error: aborting due to 9 previous errors diff --git a/tests/ui/search_is_some_fixable_some.fixed b/tests/ui/search_is_some_fixable_some.fixed index 42b39b33b57..c7a4422f373 100644 --- a/tests/ui/search_is_some_fixable_some.fixed +++ b/tests/ui/search_is_some_fixable_some.fixed @@ -289,3 +289,10 @@ mod issue9120 { //~^ search_is_some } } + +fn issue15102() { + let values = [None, Some(3)]; + let has_even = values.iter().any(|v| matches!(&v, Some(x) if x % 2 == 0)); + //~^ search_is_some + println!("{has_even}"); +} diff --git a/tests/ui/search_is_some_fixable_some.rs b/tests/ui/search_is_some_fixable_some.rs index ca4f4d941cb..d6b1c67c971 100644 --- a/tests/ui/search_is_some_fixable_some.rs +++ b/tests/ui/search_is_some_fixable_some.rs @@ -297,3 +297,10 @@ mod issue9120 { //~^ search_is_some } } + +fn issue15102() { + let values = [None, Some(3)]; + let has_even = values.iter().find(|v| matches!(v, Some(x) if x % 2 == 0)).is_some(); + //~^ search_is_some + println!("{has_even}"); +} diff --git a/tests/ui/search_is_some_fixable_some.stderr b/tests/ui/search_is_some_fixable_some.stderr index 8291f48d43c..551a670d937 100644 --- a/tests/ui/search_is_some_fixable_some.stderr +++ b/tests/ui/search_is_some_fixable_some.stderr @@ -289,5 +289,11 @@ error: called `is_some()` after searching an `Iterator` with `find` LL | let _ = v.iter().find(|x: &&u32| (*arg_no_deref_dyn)(x)).is_some(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `any(|x: &u32| (*arg_no_deref_dyn)(&x))` -error: aborting due to 46 previous errors +error: called `is_some()` after searching an `Iterator` with `find` + --> tests/ui/search_is_some_fixable_some.rs:303:34 + | +LL | let has_even = values.iter().find(|v| matches!(v, Some(x) if x % 2 == 0)).is_some(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `any(|v| matches!(&v, Some(x) if x % 2 == 0))` + +error: aborting due to 47 previous errors diff --git a/tests/ui/string_to_string.rs b/tests/ui/string_to_string.rs deleted file mode 100644 index 7c5bd8a897b..00000000000 --- a/tests/ui/string_to_string.rs +++ /dev/null @@ -1,21 +0,0 @@ -#![warn(clippy::string_to_string)] -#![allow(clippy::redundant_clone, clippy::unnecessary_literal_unwrap)] - -fn main() { - let mut message = String::from("Hello"); - let mut v = message.to_string(); - //~^ string_to_string - - let variable1 = String::new(); - let v = &variable1; - let variable2 = Some(v); - let _ = variable2.map(|x| { - println!(); - x.to_string() - }); - //~^^ string_to_string - - let x = Some(String::new()); - let _ = x.unwrap_or_else(|| v.to_string()); - //~^ string_to_string -} diff --git a/tests/ui/string_to_string.stderr b/tests/ui/string_to_string.stderr deleted file mode 100644 index 99eea06f18e..00000000000 --- a/tests/ui/string_to_string.stderr +++ /dev/null @@ -1,28 +0,0 @@ -error: `to_string()` called on a `String` - --> tests/ui/string_to_string.rs:6:17 - | -LL | let mut v = message.to_string(); - | ^^^^^^^^^^^^^^^^^^^ - | - = help: consider using `.clone()` - = note: `-D clippy::string-to-string` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::string_to_string)]` - -error: `to_string()` called on a `String` - --> tests/ui/string_to_string.rs:14:9 - | -LL | x.to_string() - | ^^^^^^^^^^^^^ - | - = help: consider using `.clone()` - -error: `to_string()` called on a `String` - --> tests/ui/string_to_string.rs:19:33 - | -LL | let _ = x.unwrap_or_else(|| v.to_string()); - | ^^^^^^^^^^^^^ - | - = help: consider using `.clone()` - -error: aborting due to 3 previous errors - diff --git a/tests/ui/string_to_string_in_map.fixed b/tests/ui/string_to_string_in_map.fixed deleted file mode 100644 index efc085539f1..00000000000 --- a/tests/ui/string_to_string_in_map.fixed +++ /dev/null @@ -1,20 +0,0 @@ -#![deny(clippy::string_to_string)] -#![allow(clippy::unnecessary_literal_unwrap, clippy::useless_vec, clippy::iter_cloned_collect)] - -fn main() { - let variable1 = String::new(); - let v = &variable1; - let variable2 = Some(v); - let _ = variable2.cloned(); - //~^ string_to_string - let _ = variable2.cloned(); - //~^ string_to_string - #[rustfmt::skip] - let _ = variable2.cloned(); - //~^ string_to_string - - let _ = vec![String::new()].iter().cloned().collect::>(); - //~^ string_to_string - let _ = vec![String::new()].iter().cloned().collect::>(); - //~^ string_to_string -} diff --git a/tests/ui/string_to_string_in_map.rs b/tests/ui/string_to_string_in_map.rs deleted file mode 100644 index 5bf1d7ba5a2..00000000000 --- a/tests/ui/string_to_string_in_map.rs +++ /dev/null @@ -1,20 +0,0 @@ -#![deny(clippy::string_to_string)] -#![allow(clippy::unnecessary_literal_unwrap, clippy::useless_vec, clippy::iter_cloned_collect)] - -fn main() { - let variable1 = String::new(); - let v = &variable1; - let variable2 = Some(v); - let _ = variable2.map(String::to_string); - //~^ string_to_string - let _ = variable2.map(|x| x.to_string()); - //~^ string_to_string - #[rustfmt::skip] - let _ = variable2.map(|x| { x.to_string() }); - //~^ string_to_string - - let _ = vec![String::new()].iter().map(String::to_string).collect::>(); - //~^ string_to_string - let _ = vec![String::new()].iter().map(|x| x.to_string()).collect::>(); - //~^ string_to_string -} diff --git a/tests/ui/string_to_string_in_map.stderr b/tests/ui/string_to_string_in_map.stderr deleted file mode 100644 index 35aeed656ee..00000000000 --- a/tests/ui/string_to_string_in_map.stderr +++ /dev/null @@ -1,38 +0,0 @@ -error: `to_string()` called on a `String` - --> tests/ui/string_to_string_in_map.rs:8:23 - | -LL | let _ = variable2.map(String::to_string); - | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `cloned()` - | -note: the lint level is defined here - --> tests/ui/string_to_string_in_map.rs:1:9 - | -LL | #![deny(clippy::string_to_string)] - | ^^^^^^^^^^^^^^^^^^^^^^^^ - -error: `to_string()` called on a `String` - --> tests/ui/string_to_string_in_map.rs:10:23 - | -LL | let _ = variable2.map(|x| x.to_string()); - | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `cloned()` - -error: `to_string()` called on a `String` - --> tests/ui/string_to_string_in_map.rs:13:23 - | -LL | let _ = variable2.map(|x| { x.to_string() }); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `cloned()` - -error: `to_string()` called on a `String` - --> tests/ui/string_to_string_in_map.rs:16:40 - | -LL | let _ = vec![String::new()].iter().map(String::to_string).collect::>(); - | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `cloned()` - -error: `to_string()` called on a `String` - --> tests/ui/string_to_string_in_map.rs:18:40 - | -LL | let _ = vec![String::new()].iter().map(|x| x.to_string()).collect::>(); - | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `cloned()` - -error: aborting due to 5 previous errors - diff --git a/tests/ui/suspicious_else_formatting.rs b/tests/ui/suspicious_else_formatting.rs index 28a3b551116..072e7b27b0d 100644 --- a/tests/ui/suspicious_else_formatting.rs +++ b/tests/ui/suspicious_else_formatting.rs @@ -1,6 +1,6 @@ //@aux-build:proc_macro_suspicious_else_formatting.rs -#![warn(clippy::suspicious_else_formatting)] +#![warn(clippy::suspicious_else_formatting, clippy::possible_missing_else)] #![allow( clippy::if_same_then_else, clippy::let_unit_value, @@ -20,12 +20,12 @@ fn main() { // weird `else` formatting: if foo() { } { - //~^ suspicious_else_formatting + //~^ possible_missing_else } if foo() { } if foo() { - //~^ suspicious_else_formatting + //~^ possible_missing_else } let _ = { // if as the last expression @@ -33,7 +33,7 @@ fn main() { if foo() { } if foo() { - //~^ suspicious_else_formatting + //~^ possible_missing_else } else { } @@ -42,7 +42,7 @@ fn main() { let _ = { // if in the middle of a block if foo() { } if foo() { - //~^ suspicious_else_formatting + //~^ possible_missing_else } else { } diff --git a/tests/ui/suspicious_else_formatting.stderr b/tests/ui/suspicious_else_formatting.stderr index affd20b22d9..04555c6edbd 100644 --- a/tests/ui/suspicious_else_formatting.stderr +++ b/tests/ui/suspicious_else_formatting.stderr @@ -5,8 +5,8 @@ LL | } { | ^ | = note: to remove this lint, add the missing `else` or add a new line before the next block - = note: `-D clippy::suspicious-else-formatting` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::suspicious_else_formatting)]` + = note: `-D clippy::possible-missing-else` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::possible_missing_else)]` error: this looks like an `else if` but the `else` is missing --> tests/ui/suspicious_else_formatting.rs:27:6 @@ -41,6 +41,8 @@ LL | | { | |____^ | = note: to remove this lint, remove the `else` or remove the new line between `else` and `{..}` + = note: `-D clippy::suspicious-else-formatting` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::suspicious_else_formatting)]` error: this is an `else if` but the formatting might hide it --> tests/ui/suspicious_else_formatting.rs:67:6 diff --git a/tests/ui/unnecessary_clippy_cfg.rs b/tests/ui/unnecessary_clippy_cfg.rs index e7e01248dfb..65f67df7913 100644 --- a/tests/ui/unnecessary_clippy_cfg.rs +++ b/tests/ui/unnecessary_clippy_cfg.rs @@ -1,5 +1,6 @@ //@no-rustfix +#![allow(clippy::duplicated_attributes)] #![warn(clippy::unnecessary_clippy_cfg)] #![cfg_attr(clippy, deny(clippy::non_minimal_cfg))] //~^ unnecessary_clippy_cfg @@ -7,7 +8,6 @@ //~^ unnecessary_clippy_cfg #![cfg_attr(clippy, deny(dead_code, clippy::non_minimal_cfg))] //~^ unnecessary_clippy_cfg -//~| duplicated_attributes #![cfg_attr(clippy, deny(clippy::non_minimal_cfg))] //~^ unnecessary_clippy_cfg @@ -17,7 +17,6 @@ //~^ unnecessary_clippy_cfg #[cfg_attr(clippy, deny(dead_code, clippy::non_minimal_cfg))] //~^ unnecessary_clippy_cfg -//~| duplicated_attributes #[cfg_attr(clippy, deny(clippy::non_minimal_cfg))] //~^ unnecessary_clippy_cfg diff --git a/tests/ui/unnecessary_clippy_cfg.stderr b/tests/ui/unnecessary_clippy_cfg.stderr index f66c6894954..4f638d5c513 100644 --- a/tests/ui/unnecessary_clippy_cfg.stderr +++ b/tests/ui/unnecessary_clippy_cfg.stderr @@ -1,5 +1,5 @@ error: no need to put clippy lints behind a `clippy` cfg - --> tests/ui/unnecessary_clippy_cfg.rs:4:1 + --> tests/ui/unnecessary_clippy_cfg.rs:5:1 | LL | #![cfg_attr(clippy, deny(clippy::non_minimal_cfg))] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `#![deny(clippy::non_minimal_cfg)]` @@ -8,7 +8,7 @@ LL | #![cfg_attr(clippy, deny(clippy::non_minimal_cfg))] = help: to override `-D warnings` add `#[allow(clippy::unnecessary_clippy_cfg)]` error: no need to put clippy lints behind a `clippy` cfg - --> tests/ui/unnecessary_clippy_cfg.rs:6:37 + --> tests/ui/unnecessary_clippy_cfg.rs:7:37 | LL | #![cfg_attr(clippy, deny(dead_code, clippy::non_minimal_cfg))] | ^^^^^^^^^^^^^^^^^^^^^^^ @@ -16,7 +16,7 @@ LL | #![cfg_attr(clippy, deny(dead_code, clippy::non_minimal_cfg))] = note: write instead: `#![deny(clippy::non_minimal_cfg)]` error: no need to put clippy lints behind a `clippy` cfg - --> tests/ui/unnecessary_clippy_cfg.rs:8:37 + --> tests/ui/unnecessary_clippy_cfg.rs:9:37 | LL | #![cfg_attr(clippy, deny(dead_code, clippy::non_minimal_cfg))] | ^^^^^^^^^^^^^^^^^^^^^^^ @@ -52,46 +52,10 @@ LL | #[cfg_attr(clippy, deny(dead_code, clippy::non_minimal_cfg))] = note: write instead: `#[deny(clippy::non_minimal_cfg)]` error: no need to put clippy lints behind a `clippy` cfg - --> tests/ui/unnecessary_clippy_cfg.rs:21:1 + --> tests/ui/unnecessary_clippy_cfg.rs:20:1 | LL | #[cfg_attr(clippy, deny(clippy::non_minimal_cfg))] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `#[deny(clippy::non_minimal_cfg)]` -error: duplicated attribute - --> tests/ui/unnecessary_clippy_cfg.rs:8:26 - | -LL | #![cfg_attr(clippy, deny(dead_code, clippy::non_minimal_cfg))] - | ^^^^^^^^^ - | -note: first defined here - --> tests/ui/unnecessary_clippy_cfg.rs:6:26 - | -LL | #![cfg_attr(clippy, deny(dead_code, clippy::non_minimal_cfg))] - | ^^^^^^^^^ -help: remove this attribute - --> tests/ui/unnecessary_clippy_cfg.rs:8:26 - | -LL | #![cfg_attr(clippy, deny(dead_code, clippy::non_minimal_cfg))] - | ^^^^^^^^^ - = note: `-D clippy::duplicated-attributes` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::duplicated_attributes)]` - -error: duplicated attribute - --> tests/ui/unnecessary_clippy_cfg.rs:18:25 - | -LL | #[cfg_attr(clippy, deny(dead_code, clippy::non_minimal_cfg))] - | ^^^^^^^^^ - | -note: first defined here - --> tests/ui/unnecessary_clippy_cfg.rs:16:25 - | -LL | #[cfg_attr(clippy, deny(dead_code, clippy::non_minimal_cfg))] - | ^^^^^^^^^ -help: remove this attribute - --> tests/ui/unnecessary_clippy_cfg.rs:18:25 - | -LL | #[cfg_attr(clippy, deny(dead_code, clippy::non_minimal_cfg))] - | ^^^^^^^^^ - -error: aborting due to 10 previous errors +error: aborting due to 8 previous errors diff --git a/tests/ui/unnecessary_map_or.fixed b/tests/ui/unnecessary_map_or.fixed index 3109c4af8e2..10552431d65 100644 --- a/tests/ui/unnecessary_map_or.fixed +++ b/tests/ui/unnecessary_map_or.fixed @@ -131,6 +131,26 @@ fn issue14201(a: Option, b: Option, s: &String) -> bool { x && y } +fn issue14714() { + assert!(Some("test") == Some("test")); + //~^ unnecessary_map_or + + // even though we're in a macro context, we still need to parenthesise because of the `then` + assert!((Some("test") == Some("test")).then(|| 1).is_some()); + //~^ unnecessary_map_or + + // method lints don't fire on macros + macro_rules! m { + ($x:expr) => { + // should become !($x == Some(1)) + let _ = !$x.map_or(false, |v| v == 1); + // should become $x == Some(1) + let _ = $x.map_or(false, |v| v == 1); + }; + } + m!(Some(5)); +} + fn issue15180() { let s = std::sync::Mutex::new(Some("foo")); _ = s.lock().unwrap().is_some_and(|s| s == "foo"); diff --git a/tests/ui/unnecessary_map_or.rs b/tests/ui/unnecessary_map_or.rs index 52a55f9fc9e..4b406ec2998 100644 --- a/tests/ui/unnecessary_map_or.rs +++ b/tests/ui/unnecessary_map_or.rs @@ -135,6 +135,26 @@ fn issue14201(a: Option, b: Option, s: &String) -> bool { x && y } +fn issue14714() { + assert!(Some("test").map_or(false, |x| x == "test")); + //~^ unnecessary_map_or + + // even though we're in a macro context, we still need to parenthesise because of the `then` + assert!(Some("test").map_or(false, |x| x == "test").then(|| 1).is_some()); + //~^ unnecessary_map_or + + // method lints don't fire on macros + macro_rules! m { + ($x:expr) => { + // should become !($x == Some(1)) + let _ = !$x.map_or(false, |v| v == 1); + // should become $x == Some(1) + let _ = $x.map_or(false, |v| v == 1); + }; + } + m!(Some(5)); +} + fn issue15180() { let s = std::sync::Mutex::new(Some("foo")); _ = s.lock().unwrap().map_or(false, |s| s == "foo"); diff --git a/tests/ui/unnecessary_map_or.stderr b/tests/ui/unnecessary_map_or.stderr index 99e17e8b34b..b8a22346c37 100644 --- a/tests/ui/unnecessary_map_or.stderr +++ b/tests/ui/unnecessary_map_or.stderr @@ -327,7 +327,31 @@ LL + let y = b.is_none_or(|b| b == *s); | error: this `map_or` can be simplified - --> tests/ui/unnecessary_map_or.rs:140:9 + --> tests/ui/unnecessary_map_or.rs:139:13 + | +LL | assert!(Some("test").map_or(false, |x| x == "test")); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use a standard comparison instead + | +LL - assert!(Some("test").map_or(false, |x| x == "test")); +LL + assert!(Some("test") == Some("test")); + | + +error: this `map_or` can be simplified + --> tests/ui/unnecessary_map_or.rs:143:13 + | +LL | assert!(Some("test").map_or(false, |x| x == "test").then(|| 1).is_some()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use a standard comparison instead + | +LL - assert!(Some("test").map_or(false, |x| x == "test").then(|| 1).is_some()); +LL + assert!((Some("test") == Some("test")).then(|| 1).is_some()); + | + +error: this `map_or` can be simplified + --> tests/ui/unnecessary_map_or.rs:160:9 | LL | _ = s.lock().unwrap().map_or(false, |s| s == "foo"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -339,7 +363,7 @@ LL + _ = s.lock().unwrap().is_some_and(|s| s == "foo"); | error: this `map_or` can be simplified - --> tests/ui/unnecessary_map_or.rs:144:9 + --> tests/ui/unnecessary_map_or.rs:164:9 | LL | _ = s.map_or(false, |s| s == "foo"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -350,5 +374,5 @@ LL - _ = s.map_or(false, |s| s == "foo"); LL + _ = s.is_some_and(|s| s == "foo"); | -error: aborting due to 28 previous errors +error: aborting due to 30 previous errors diff --git a/tests/ui/unnecessary_sort_by.stderr b/tests/ui/unnecessary_sort_by.stderr index 9a38d3746da..a066554037f 100644 --- a/tests/ui/unnecessary_sort_by.stderr +++ b/tests/ui/unnecessary_sort_by.stderr @@ -7,7 +7,7 @@ LL | vec.sort_by(|a, b| a.cmp(b)); = note: `-D clippy::unnecessary-sort-by` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::unnecessary_sort_by)]` -error: consider using `sort` +error: consider using `sort_unstable` --> tests/ui/unnecessary_sort_by.rs:14:5 | LL | vec.sort_unstable_by(|a, b| a.cmp(b)); @@ -19,7 +19,7 @@ error: consider using `sort_by_key` LL | vec.sort_by(|a, b| (a + 5).abs().cmp(&(b + 5).abs())); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort_by_key(|a| (a + 5).abs())` -error: consider using `sort_by_key` +error: consider using `sort_unstable_by_key` --> tests/ui/unnecessary_sort_by.rs:18:5 | LL | vec.sort_unstable_by(|a, b| id(-a).cmp(&id(-b))); @@ -31,7 +31,7 @@ error: consider using `sort_by_key` LL | vec.sort_by(|a, b| (b + 5).abs().cmp(&(a + 5).abs())); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort_by_key(|b| std::cmp::Reverse((b + 5).abs()))` -error: consider using `sort_by_key` +error: consider using `sort_unstable_by_key` --> tests/ui/unnecessary_sort_by.rs:24:5 | LL | vec.sort_unstable_by(|a, b| id(-b).cmp(&id(-a))); @@ -43,7 +43,7 @@ error: consider using `sort_by_key` LL | vec.sort_by(|a, b| (***a).abs().cmp(&(***b).abs())); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort_by_key(|a| (***a).abs())` -error: consider using `sort_by_key` +error: consider using `sort_unstable_by_key` --> tests/ui/unnecessary_sort_by.rs:37:5 | LL | vec.sort_unstable_by(|a, b| (***a).abs().cmp(&(***b).abs())); @@ -55,7 +55,7 @@ error: consider using `sort_by_key` LL | args.sort_by(|a, b| a.name().cmp(&b.name())); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `args.sort_by_key(|a| a.name())` -error: consider using `sort_by_key` +error: consider using `sort_unstable_by_key` --> tests/ui/unnecessary_sort_by.rs:99:9 | LL | args.sort_unstable_by(|a, b| a.name().cmp(&b.name())); @@ -67,7 +67,7 @@ error: consider using `sort_by_key` LL | args.sort_by(|a, b| b.name().cmp(&a.name())); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `args.sort_by_key(|b| std::cmp::Reverse(b.name()))` -error: consider using `sort_by_key` +error: consider using `sort_unstable_by_key` --> tests/ui/unnecessary_sort_by.rs:104:9 | LL | args.sort_unstable_by(|a, b| b.name().cmp(&a.name())); diff --git a/triagebot.toml b/triagebot.toml index a62b6269a3b..805baf2af6d 100644 --- a/triagebot.toml +++ b/triagebot.toml @@ -54,7 +54,6 @@ contributing_url = "https://github.com/rust-lang/rust-clippy/blob/master/CONTRIB users_on_vacation = [ "matthiaskrgr", "Manishearth", - "samueltardieu", ] [assign.owners] diff --git a/util/gh-pages/index_template.html b/util/gh-pages/index_template.html index 5d65ea585df..d34ff0a5973 100644 --- a/util/gh-pages/index_template.html +++ b/util/gh-pages/index_template.html @@ -14,7 +14,6 @@ Otherwise, have a great day =^.^= Clippy Lints {# #} - {# #} {# #} {# #} @@ -49,7 +48,7 @@ Otherwise, have a great day =^.^= {# #}
{# #} -

Clippy Lints

{# #} +

Clippy Lints Total number: {{+ count }}

{# #}