From 5e53d929e4638976cac6b9a3069860af8fe814fc Mon Sep 17 00:00:00 2001 From: byd1 <2156864690@qq.com> Date: Sun, 28 Jun 2026 14:19:13 +0800 Subject: [PATCH 01/38] fix --- compiler/rustc_parse/src/parser/stmt.rs | 20 +++++++++++++++++-- .../ui/let-else/detect-invisible-delimiter.rs | 16 +++++++++++++++ 2 files changed, 34 insertions(+), 2 deletions(-) create mode 100644 tests/ui/let-else/detect-invisible-delimiter.rs diff --git a/compiler/rustc_parse/src/parser/stmt.rs b/compiler/rustc_parse/src/parser/stmt.rs index 5bd2ca3139228..2b63a31a139da 100644 --- a/compiler/rustc_parse/src/parser/stmt.rs +++ b/compiler/rustc_parse/src/parser/stmt.rs @@ -5,6 +5,7 @@ use std::ops::Bound; use ast::Label; use rustc_ast as ast; use rustc_ast::token::{self, Delimiter, InvisibleOrigin, MetaVarKind, TokenKind}; +use rustc_ast::tokenstream::TokenTree; use rustc_ast::util::classify::{self, TrailingBrace}; use rustc_ast::visit::{Visitor, walk_expr}; use rustc_ast::{ @@ -353,6 +354,14 @@ impl<'a> Parser<'a> { } else { (None, None, None) }; + + let init_wrapped = self + .tree_look_ahead(2, |tree| match tree { + TokenTree::Token(tok, _) => tok.is_keyword(kw::Else), + TokenTree::Delimited(..) => false, + }) + .unwrap_or(false); + let init = match (self.parse_initializer(err.is_some()), err) { (Ok(init), None) => { // init parsed, ty parsed @@ -390,6 +399,7 @@ impl<'a> Parser<'a> { return Err(err); } }; + let trailing_token = self.prev_token; let kind = match init { None => LocalKind::Decl, Some(init) => { @@ -401,8 +411,14 @@ impl<'a> Parser<'a> { return Err(self.error_block_no_opening_brace_msg(Cow::from(msg))); } let els = self.parse_block()?; - self.check_let_else_init_bool_expr(&init); - self.check_let_else_init_trailing_brace(&init); + // These checks should also respect invisible delimiter + if !init_wrapped { + self.check_let_else_init_bool_expr(&init); + } + if matches!(trailing_token.kind, TokenKind::CloseBrace) { + self.check_let_else_init_trailing_brace(&init); + } + LocalKind::InitElse(init, els) } else { LocalKind::Init(init) diff --git a/tests/ui/let-else/detect-invisible-delimiter.rs b/tests/ui/let-else/detect-invisible-delimiter.rs new file mode 100644 index 0000000000000..76c231bd21e6d --- /dev/null +++ b/tests/ui/let-else/detect-invisible-delimiter.rs @@ -0,0 +1,16 @@ +// The user shouldn't need to wrap the expression in parentheses(#147899) +//@check-pass +#![allow(irrefutable_let_patterns)] +struct Thing {} +macro_rules! foo { + ($e:expr) => { + let _ = $e else { + return; + }; + }; +} + +fn main() { + foo!(true && true); + foo!(Thing {}); +} From a0b341668d9fddfacbcf1dc66520ce5672d5cff9 Mon Sep 17 00:00:00 2001 From: aisr Date: Wed, 1 Apr 2026 18:00:47 +0800 Subject: [PATCH 02/38] add safety section for mem::zeroed --- library/core/src/mem/mod.rs | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/library/core/src/mem/mod.rs b/library/core/src/mem/mod.rs index 63dcf768de073..a603f7f9aaac0 100644 --- a/library/core/src/mem/mod.rs +++ b/library/core/src/mem/mod.rs @@ -706,16 +706,18 @@ pub const fn needs_drop() -> bool { /// This means that, for example, the padding byte in `(u8, u16)` is not /// necessarily zeroed. /// -/// There is no guarantee that an all-zero byte-pattern represents a valid value -/// of some type `T`. For example, the all-zero byte-pattern is not a valid value -/// for reference types (`&T`, `&mut T`) and function pointers. Using `zeroed` -/// on such types causes immediate [undefined behavior][ub] because [the Rust -/// compiler assumes][inv] that there always is a valid value in a variable it -/// considers initialized. -/// /// This has the same effect as [`MaybeUninit::zeroed().assume_init()`][zeroed]. /// It is useful for FFI sometimes, but should generally be avoided. /// +/// +/// # Safety +/// +/// The all-zero byte-pattern must represent a valid value of type `T`. +/// For example, it is not valid for reference types (`&T`, `&mut T`) or function +/// pointers. Using `zeroed` on such types causes immediate [undefined behavior][ub] +/// because [the Rust compiler assumes][inv] that there always is a valid value in a +/// variable it considers initialized. +/// /// [zeroed]: MaybeUninit::zeroed /// [ub]: ../../reference/behavior-considered-undefined.html /// [inv]: MaybeUninit#initialization-invariant From b78c8acf1e22f904cef325d6fab04a2eab00dc56 Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Fri, 21 Aug 2026 06:22:56 +0300 Subject: [PATCH 03/38] Provide a `supertrait_def_ids()` function in rustc_type_ir's interner rust-analyzer has a query for this, so we want to use it there. I don't know if using a query for this will be a perf win for rustc, but rust-analyzer already has this query for other reasons, so it feels a waste to not use it. --- compiler/rustc_middle/src/ty/context/impl_interner.rs | 4 ++++ compiler/rustc_next_trait_solver/src/solve/trait_goals.rs | 3 ++- compiler/rustc_type_ir/src/elaborate.rs | 3 +++ compiler/rustc_type_ir/src/interner.rs | 3 +++ 4 files changed, 12 insertions(+), 1 deletion(-) diff --git a/compiler/rustc_middle/src/ty/context/impl_interner.rs b/compiler/rustc_middle/src/ty/context/impl_interner.rs index 2444f8513b8e4..7f10cb1994ee1 100644 --- a/compiler/rustc_middle/src/ty/context/impl_interner.rs +++ b/compiler/rustc_middle/src/ty/context/impl_interner.rs @@ -430,6 +430,10 @@ impl<'tcx> Interner for TyCtxt<'tcx> { self.impl_super_outlives(impl_def_id) } + fn supertrait_def_ids(self, trait_def_id: DefId) -> impl Iterator { + rustc_type_ir::elaborate::supertrait_def_ids(self, trait_def_id) + } + fn impl_is_const(self, def_id: DefId) -> bool { debug_assert_matches!(self.def_kind(def_id), DefKind::Impl { of_trait: true }); self.is_conditionally_const(def_id) diff --git a/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs b/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs index 335263b1d169d..f9793fb6e417c 100644 --- a/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs +++ b/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs @@ -1109,7 +1109,8 @@ where .auto_traits() .into_iter() .chain(a_data.principal_def_id().into_iter().flat_map(|principal_def_id| { - elaborate::supertrait_def_ids(self.cx(), principal_def_id) + self.cx() + .supertrait_def_ids(principal_def_id) .filter(|def_id| self.cx().trait_is_auto(*def_id)) })) .collect(); diff --git a/compiler/rustc_type_ir/src/elaborate.rs b/compiler/rustc_type_ir/src/elaborate.rs index 912a5ac90f632..2110521eae764 100644 --- a/compiler/rustc_type_ir/src/elaborate.rs +++ b/compiler/rustc_type_ir/src/elaborate.rs @@ -318,6 +318,9 @@ impl> Iterator for Elaborator { /// does not compute the full elaborated super-predicates but just the set of def-ids. It is used /// to identify which traits may define a given associated type to help avoid cycle errors, /// and to make size estimates for vtable layout computation. +/// +/// rust-analyzer has a query for this, so don't use this function there. +#[cfg(feature = "nightly")] pub fn supertrait_def_ids( cx: I, trait_def_id: I::TraitId, diff --git a/compiler/rustc_type_ir/src/interner.rs b/compiler/rustc_type_ir/src/interner.rs index 49899147d5747..5a14b60e1d296 100644 --- a/compiler/rustc_type_ir/src/interner.rs +++ b/compiler/rustc_type_ir/src/interner.rs @@ -359,6 +359,9 @@ pub trait Interner: impl_def_id: Self::ImplId, ) -> ty::EarlyBinder>; + fn supertrait_def_ids(self, trait_def_id: Self::TraitId) + -> impl Iterator; + fn impl_is_const(self, def_id: Self::ImplId) -> bool; fn fn_is_const(self, def_id: Self::FunctionId) -> bool; fn closure_is_const(self, def_id: Self::ClosureId) -> bool; From 940498dad7cb35a753a7cce104ab510b414e84de Mon Sep 17 00:00:00 2001 From: Rachit2323 Date: Mon, 24 Aug 2026 12:25:29 +0530 Subject: [PATCH 04/38] fix const_item_mutation lint to use needs_drop instead of has_dtor --- .../src/check_const_item_mutation.rs | 40 ++++++++++------- tests/ui/lint/lint-const-item-mutation.rs | 10 +++-- tests/ui/lint/lint-const-item-mutation.stderr | 43 +++++++------------ 3 files changed, 47 insertions(+), 46 deletions(-) diff --git a/compiler/rustc_mir_transform/src/check_const_item_mutation.rs b/compiler/rustc_mir_transform/src/check_const_item_mutation.rs index 5b25bdc01117b..e8ff3c3a08b79 100644 --- a/compiler/rustc_mir_transform/src/check_const_item_mutation.rs +++ b/compiler/rustc_mir_transform/src/check_const_item_mutation.rs @@ -2,7 +2,7 @@ use rustc_hir::HirId; use rustc_lint_defs::builtin::CONST_ITEM_MUTATION; use rustc_middle::mir::visit::Visitor; use rustc_middle::mir::*; -use rustc_middle::ty::TyCtxt; +use rustc_middle::ty::{self, TyCtxt, TypeVisitableExt}; use rustc_span::Span; use rustc_span::def_id::DefId; @@ -35,8 +35,9 @@ impl<'tcx> ConstMutationChecker<'_, 'tcx> { fn is_const_item_without_destructor(&self, local: Local) -> Option { let def_id = self.is_const_item(local)?; - // We avoid linting mutation of a const item if the const's type has a - // Drop impl. The Drop logic observes the mutation which was performed. + // We avoid linting mutation of a const item if the const's type needs + // drop. Any drop logic (including that of fields) may observe the + // mutation which was performed. // // pub struct Log { msg: &'static str } // pub const LOG: Log = Log { msg: "" }; @@ -46,21 +47,30 @@ impl<'tcx> ConstMutationChecker<'_, 'tcx> { // // LOG.msg = "wow"; // prints "wow" // + // Likewise, if a field of the const type has its own Drop impl, that + // drop logic may also observe the mutation: + // + // struct Inner { val: u32 } + // impl Drop for Inner { fn drop(&mut self) { println!("{}", self.val); } } + // struct Outer { inner: Inner } + // const O: Outer = Outer { inner: Inner { val: 0 } }; + // + // O.inner.val = 42; // Inner::drop prints "42" + // // FIXME(https://github.com/rust-lang/rust/issues/77425): // Drop this exception once there is a stable attribute to suppress the - // const item mutation lint for a single specific const only. Something - // equivalent to: - // - // #[const_mutation_allowed] - // pub const LOG: Log = Log { msg: "" }; - // FIXME: this should not be checking for `Drop` impls, - // but whether it or any field has a Drop impl (`needs_drop`) - // as fields' Drop impls may make this observable, too. - match self.tcx.type_of(def_id).skip_binder().ty_adt_def().map(|adt| adt.has_dtor(self.tcx)) - { - Some(true) => None, - Some(false) | None => Some(def_id), + // const item mutation lint for a single specific const only. + let ty = self.tcx.type_of(def_id).instantiate_identity().skip_norm_wip(); + // `needs_drop` is overly conservative for types that contain type + // parameters (e.g. `Self` in a trait associated const): it always + // returns `true` because the parameter *might* implement Drop, even + // when the concrete type at the call site does not. In that case we + // cannot suppress the lint, so fall through and warn. + if ty.has_param() { + return Some(def_id); } + let typing_env = ty::TypingEnv::non_body_analysis(self.tcx, def_id); + if ty.needs_drop(self.tcx, typing_env) { None } else { Some(def_id) } } /// If we should lint on this usage, return the [`HirId`], source [`Span`] diff --git a/tests/ui/lint/lint-const-item-mutation.rs b/tests/ui/lint/lint-const-item-mutation.rs index d51d3c394937c..877455e7bb869 100644 --- a/tests/ui/lint/lint-const-item-mutation.rs +++ b/tests/ui/lint/lint-const-item-mutation.rs @@ -18,16 +18,19 @@ impl Drop for Mutable { } } -struct Mutable2 { // this one has drop glue but not a Drop impl +struct Mutable2 { // this one has drop glue but not a direct Drop impl msg: &'static str, other: String, } +struct WithFieldDrop { inner: Mutable } // no Drop on this type, but Mutable has one + const ARRAY: [u8; 1] = [25]; const MY_STRUCT: MyStruct = MyStruct { field: true, inner_array: ['a'], raw_ptr: 2 as *mut u8 }; const RAW_PTR: *mut u8 = 1 as *mut u8; const MUTABLE: Mutable = Mutable { msg: "" }; const MUTABLE2: Mutable2 = Mutable2 { msg: "", other: String::new() }; +const WFD: WithFieldDrop = WithFieldDrop { inner: Mutable { msg: "" } }; const VEC: Vec = Vec::new(); const PTR: *mut () = 1 as *mut _; const PTR_TO_ARRAY: *mut [u32; 4] = 0x12345678 as _; @@ -50,8 +53,9 @@ fn main() { *MY_STRUCT.raw_ptr = 0; } - MUTABLE.msg = "wow"; // no warning, because Drop observes the mutation - MUTABLE2.msg = "wow"; //~ WARN attempting to modify + MUTABLE.msg = "wow"; // no warning — Drop impl observes the mutation + MUTABLE2.msg = "wow"; // no warning — field String has drop glue (needs_drop = true) + WFD.inner.msg = "observed"; // no warning — Mutable's Drop observes the field mutation VEC.push(0); //~ WARN taking a mutable reference to a `const` item // Test that we don't warn when converting a raw pointer diff --git a/tests/ui/lint/lint-const-item-mutation.stderr b/tests/ui/lint/lint-const-item-mutation.stderr index 0e405c306fe46..84f5e78953e60 100644 --- a/tests/ui/lint/lint-const-item-mutation.stderr +++ b/tests/ui/lint/lint-const-item-mutation.stderr @@ -1,45 +1,45 @@ warning: attempting to modify a `const` item - --> $DIR/lint-const-item-mutation.rs:37:5 + --> $DIR/lint-const-item-mutation.rs:40:5 | LL | ARRAY[0] = 5; | ^^^^^^^^^^^^ | = note: each usage of a `const` item creates a new temporary; the original `const` item will not be modified note: `const` item defined here - --> $DIR/lint-const-item-mutation.rs:26:1 + --> $DIR/lint-const-item-mutation.rs:28:1 | LL | const ARRAY: [u8; 1] = [25]; | ^^^^^^^^^^^^^^^^^^^^ = note: `#[warn(const_item_mutation)]` on by default warning: attempting to modify a `const` item - --> $DIR/lint-const-item-mutation.rs:38:5 + --> $DIR/lint-const-item-mutation.rs:41:5 | LL | MY_STRUCT.field = false; | ^^^^^^^^^^^^^^^^^^^^^^^ | = note: each usage of a `const` item creates a new temporary; the original `const` item will not be modified note: `const` item defined here - --> $DIR/lint-const-item-mutation.rs:27:1 + --> $DIR/lint-const-item-mutation.rs:29:1 | LL | const MY_STRUCT: MyStruct = MyStruct { field: true, inner_array: ['a'], raw_ptr: 2 as *mut u8 }; | ^^^^^^^^^^^^^^^^^^^^^^^^^ warning: attempting to modify a `const` item - --> $DIR/lint-const-item-mutation.rs:39:5 + --> $DIR/lint-const-item-mutation.rs:42:5 | LL | MY_STRUCT.inner_array[0] = 'b'; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: each usage of a `const` item creates a new temporary; the original `const` item will not be modified note: `const` item defined here - --> $DIR/lint-const-item-mutation.rs:27:1 + --> $DIR/lint-const-item-mutation.rs:29:1 | LL | const MY_STRUCT: MyStruct = MyStruct { field: true, inner_array: ['a'], raw_ptr: 2 as *mut u8 }; | ^^^^^^^^^^^^^^^^^^^^^^^^^ warning: taking a mutable reference to a `const` item - --> $DIR/lint-const-item-mutation.rs:40:5 + --> $DIR/lint-const-item-mutation.rs:43:5 | LL | MY_STRUCT.use_mut(); | ^^^^^^^^^^^^^^^^^^^ @@ -52,13 +52,13 @@ note: mutable reference created due to call to this method LL | fn use_mut(&mut self) {} | ^^^^^^^^^^^^^^^^^^^^^ note: `const` item defined here - --> $DIR/lint-const-item-mutation.rs:27:1 + --> $DIR/lint-const-item-mutation.rs:29:1 | LL | const MY_STRUCT: MyStruct = MyStruct { field: true, inner_array: ['a'], raw_ptr: 2 as *mut u8 }; | ^^^^^^^^^^^^^^^^^^^^^^^^^ warning: taking a mutable reference to a `const` item - --> $DIR/lint-const-item-mutation.rs:41:5 + --> $DIR/lint-const-item-mutation.rs:44:5 | LL | &mut MY_STRUCT; | ^^^^^^^^^^^^^^ @@ -66,13 +66,13 @@ LL | &mut MY_STRUCT; = note: each usage of a `const` item creates a new temporary = note: the mutable reference will refer to this temporary, not the original `const` item note: `const` item defined here - --> $DIR/lint-const-item-mutation.rs:27:1 + --> $DIR/lint-const-item-mutation.rs:29:1 | LL | const MY_STRUCT: MyStruct = MyStruct { field: true, inner_array: ['a'], raw_ptr: 2 as *mut u8 }; | ^^^^^^^^^^^^^^^^^^^^^^^^^ warning: taking a mutable reference to a `const` item - --> $DIR/lint-const-item-mutation.rs:42:5 + --> $DIR/lint-const-item-mutation.rs:45:5 | LL | (&mut MY_STRUCT).use_mut(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -85,26 +85,13 @@ note: mutable reference created due to call to this method LL | fn use_mut(&mut self) {} | ^^^^^^^^^^^^^^^^^^^^^ note: `const` item defined here - --> $DIR/lint-const-item-mutation.rs:27:1 + --> $DIR/lint-const-item-mutation.rs:29:1 | LL | const MY_STRUCT: MyStruct = MyStruct { field: true, inner_array: ['a'], raw_ptr: 2 as *mut u8 }; | ^^^^^^^^^^^^^^^^^^^^^^^^^ -warning: attempting to modify a `const` item - --> $DIR/lint-const-item-mutation.rs:54:5 - | -LL | MUTABLE2.msg = "wow"; - | ^^^^^^^^^^^^^^^^^^^^ - | - = note: each usage of a `const` item creates a new temporary; the original `const` item will not be modified -note: `const` item defined here - --> $DIR/lint-const-item-mutation.rs:30:1 - | -LL | const MUTABLE2: Mutable2 = Mutable2 { msg: "", other: String::new() }; - | ^^^^^^^^^^^^^^^^^^^^^^^^ - warning: taking a mutable reference to a `const` item - --> $DIR/lint-const-item-mutation.rs:55:5 + --> $DIR/lint-const-item-mutation.rs:59:5 | LL | VEC.push(0); | ^^^^^^^^^^^ @@ -114,10 +101,10 @@ LL | VEC.push(0); note: mutable reference created due to call to this method --> $SRC_DIR/alloc/src/vec/mod.rs:LL:COL note: `const` item defined here - --> $DIR/lint-const-item-mutation.rs:31:1 + --> $DIR/lint-const-item-mutation.rs:34:1 | LL | const VEC: Vec = Vec::new(); | ^^^^^^^^^^^^^^^^^^^ -warning: 8 warnings emitted +warning: 7 warnings emitted From 98a157aec8b08729249aa9ca78f9703aa3a4f279 Mon Sep 17 00:00:00 2001 From: zakrad <49591476+zakrad@users.noreply.github.com> Date: Mon, 31 Aug 2026 13:21:07 +0330 Subject: [PATCH 05/38] Add test for the fn item uniqueness note with late bound lifetimes --- .../fn/fn-item-type-note-late-bound-145558.rs | 19 ++++++++++++++++++ ...fn-item-type-note-late-bound-145558.stderr | 20 +++++++++++++++++++ 2 files changed, 39 insertions(+) create mode 100644 tests/ui/fn/fn-item-type-note-late-bound-145558.rs create mode 100644 tests/ui/fn/fn-item-type-note-late-bound-145558.stderr diff --git a/tests/ui/fn/fn-item-type-note-late-bound-145558.rs b/tests/ui/fn/fn-item-type-note-late-bound-145558.rs new file mode 100644 index 0000000000000..2463e2ba70675 --- /dev/null +++ b/tests/ui/fn/fn-item-type-note-late-bound-145558.rs @@ -0,0 +1,19 @@ +//! Regression test for https://github.com/rust-lang/rust/issues/145558 +//! +//! The note explaining that distinct fn items have distinct types was suppressed when the +//! signatures contained a late-bound lifetime, because the two binders name their bound +//! region differently. + +//@ dont-require-annotations: NOTE + +struct A; + +fn f1<'a>(_: &'a A) {} +fn f2<'a>(_: &'a A) {} + +fn main() { + let mut map = vec![]; + map.push(f1); + map.push(f2); + //~^ ERROR mismatched types +} diff --git a/tests/ui/fn/fn-item-type-note-late-bound-145558.stderr b/tests/ui/fn/fn-item-type-note-late-bound-145558.stderr new file mode 100644 index 0000000000000..31d773c7e4a79 --- /dev/null +++ b/tests/ui/fn/fn-item-type-note-late-bound-145558.stderr @@ -0,0 +1,20 @@ +error[E0308]: mismatched types + --> $DIR/fn-item-type-note-late-bound-145558.rs:17:14 + | +LL | map.push(f1); + | --- -- this argument has type `for<'a> fn(&'a A) {f1}`... + | | + | ... which causes `map` to have type `Vec fn(&'a A) {f1}>` +LL | map.push(f2); + | ---- ^^ expected fn item, found a different fn item + | | + | arguments to this method are incorrect + | + = note: expected fn item `for<'a> fn(&'a A) {f1}` + found fn item `for<'a> fn(&'a A) {f2}` +note: method defined here + --> $SRC_DIR/alloc/src/vec/mod.rs:LL:COL + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0308`. From 21228c2dfa1a8afda35130d0fd59979ed6c34937 Mon Sep 17 00:00:00 2001 From: zakrad <49591476+zakrad@users.noreply.github.com> Date: Mon, 31 Aug 2026 13:23:02 +0330 Subject: [PATCH 06/38] Do not suppress the fn item uniqueness note for late bound lifetimes --- .../src/error_reporting/infer/suggest.rs | 4 +++- tests/ui/fn/fn-item-type-note-late-bound-145558.rs | 1 + tests/ui/fn/fn-item-type-note-late-bound-145558.stderr | 1 + 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/suggest.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/suggest.rs index db852701051cf..577571196a239 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/infer/suggest.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/suggest.rs @@ -521,7 +521,9 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { let found_sig = self.normalize_fn_sig(self.tcx.fn_sig(*did2).instantiate(self.tcx, args2)); - if self.same_type_modulo_infer(expected_sig, found_sig) { + let expected_sig_anon = self.tcx.anonymize_bound_vars(expected_sig); + let found_sig_anon = self.tcx.anonymize_bound_vars(found_sig); + if self.same_type_modulo_infer(expected_sig_anon, found_sig_anon) { diag.subdiagnostic(FnUniqTypes); } diff --git a/tests/ui/fn/fn-item-type-note-late-bound-145558.rs b/tests/ui/fn/fn-item-type-note-late-bound-145558.rs index 2463e2ba70675..3e8fc0e8b14a7 100644 --- a/tests/ui/fn/fn-item-type-note-late-bound-145558.rs +++ b/tests/ui/fn/fn-item-type-note-late-bound-145558.rs @@ -16,4 +16,5 @@ fn main() { map.push(f1); map.push(f2); //~^ ERROR mismatched types + //~| NOTE different fn items have unique types } diff --git a/tests/ui/fn/fn-item-type-note-late-bound-145558.stderr b/tests/ui/fn/fn-item-type-note-late-bound-145558.stderr index 31d773c7e4a79..bf0a1c3316e57 100644 --- a/tests/ui/fn/fn-item-type-note-late-bound-145558.stderr +++ b/tests/ui/fn/fn-item-type-note-late-bound-145558.stderr @@ -12,6 +12,7 @@ LL | map.push(f2); | = note: expected fn item `for<'a> fn(&'a A) {f1}` found fn item `for<'a> fn(&'a A) {f2}` + = note: different fn items have unique types, even if their signatures are the same note: method defined here --> $SRC_DIR/alloc/src/vec/mod.rs:LL:COL From 4c3c97f5aa4a628944df6ff3aa65be6c36c4a639 Mon Sep 17 00:00:00 2001 From: David Carlier Date: Sun, 13 Sep 2026 15:15:33 +0100 Subject: [PATCH 07/38] std: fix unix socket address panic on a full sun_path linux reports an address length one byte past sockaddr_un when the path fills sun_path without a NUL, which made address() slice out of bounds since e96993c68f6. cap the length at the size of sockaddr_un. --- library/std/src/os/unix/net/addr.rs | 2 ++ library/std/src/os/unix/net/tests.rs | 19 +++++++++++++++++++ 2 files changed, 21 insertions(+) diff --git a/library/std/src/os/unix/net/addr.rs b/library/std/src/os/unix/net/addr.rs index 3daddc2d34323..dd6e4a690e326 100644 --- a/library/std/src/os/unix/net/addr.rs +++ b/library/std/src/os/unix/net/addr.rs @@ -123,6 +123,8 @@ impl SocketAddr { .map_or(len, |new_len| (new_len + SUN_PATH_OFFSET) as libc::socklen_t); } + len = len.min(size_of::() as libc::socklen_t); + if len == 0 { // When there is a datagram from unnamed unix socket // linux returns zero bytes of address diff --git a/library/std/src/os/unix/net/tests.rs b/library/std/src/os/unix/net/tests.rs index 9c3119e787b33..4014767461389 100644 --- a/library/std/src/os/unix/net/tests.rs +++ b/library/std/src/os/unix/net/tests.rs @@ -52,6 +52,25 @@ fn sock_addr_without_trailing_nul() { assert_eq!(address.as_pathname(), Some(Path::new("/path/to/socket"))); } +#[test] +#[cfg(any(target_os = "android", target_os = "linux"))] +fn sock_addr_pathname_fills_sun_path() { + use crate::ffi::OsStr; + use crate::os::unix::ffi::OsStrExt; + + let mut addr: libc::sockaddr_un = unsafe { crate::mem::zeroed() }; + addr.sun_family = libc::AF_UNIX as libc::sa_family_t; + let mut path = vec![b'a'; addr.sun_path.len()]; + path[0] = b'/'; + for (dst, &src) in addr.sun_path.iter_mut().zip(&path) { + *dst = src as _; + } + let offset = crate::mem::offset_of!(libc::sockaddr_un, sun_path); + + let address = or_panic!(SocketAddr::from_parts(addr, (offset + path.len() + 1) as _)); + assert_eq!(address.as_pathname(), Some(Path::new(OsStr::from_bytes(&path)))); +} + #[test] #[cfg_attr(target_os = "android", ignore)] // Android SELinux rules prevent creating Unix sockets #[cfg_attr(target_os = "vxworks", ignore = "Unix sockets are not implemented in VxWorks")] From ac0e9fa926596feda688dd289c27759fb0df8d16 Mon Sep 17 00:00:00 2001 From: Jason Gerard DeRose Date: Wed, 16 Sep 2026 10:48:44 -0600 Subject: [PATCH 08/38] First pass at windows::fs::FileExt.seek_read_exact() --- library/std/src/os/windows/fs.rs | 63 ++++++++++++++++++++++++++++++++ 1 file changed, 63 insertions(+) diff --git a/library/std/src/os/windows/fs.rs b/library/std/src/os/windows/fs.rs index 21560638c1d0f..e8a4828ecac1b 100644 --- a/library/std/src/os/windows/fs.rs +++ b/library/std/src/os/windows/fs.rs @@ -50,6 +50,69 @@ pub trait FileExt { #[stable(feature = "file_offset", since = "1.15.0")] fn seek_read(&self, buf: &mut [u8], offset: u64) -> io::Result; + /// Seeks to a given position and reads the exact number of bytes required to fill `buf` + /// + /// The offset is relative to the start of the file and thus independent + /// from the current cursor. The current cursor **is** affected by this + /// function, it is set to the end of the read. + /// + /// Similar to [`io::Read::read_exact`] but uses [`seek_read`] instead of `read`. + /// + /// [`seek_read`]: FileExt::seek_read + /// + /// # Errors + /// + /// If this function encounters an error of the kind + /// [`io::ErrorKind::Interrupted`] then the error is ignored and the operation + /// will continue. + /// + /// If this function encounters an "end of file" before completely filling + /// the buffer, it returns an error of the kind [`io::ErrorKind::UnexpectedEof`]. + /// The contents of `buf` are unspecified in this case. + /// + /// If any other read error is encountered then this function immediately + /// returns. The contents of `buf` are unspecified in this case. + /// + /// If this function returns an error, it is unspecified how many bytes it + /// has read, but it will never read more than would be necessary to + /// completely fill the buffer. + /// + /// # Examples + /// + #[cfg_attr(windows, doc = "```no_run")] + #[cfg_attr(not(windows), doc = "```ignore (needs windows)")] + /// use std::io; + /// use std::fs::File; + /// use std::os::windows::prelude::*; + /// + /// fn main() -> io::Result<()> { + /// let mut buf = [0u8; 8]; + /// let file = File::open("foo.txt")?; + /// + /// // We now read exactly 8 bytes from the offset 10. + /// file.seek_read_exact(&mut buf, 10)?; + /// println!("read {} bytes: {:?}", buf.len(), buf); + /// Ok(()) + /// } + /// ``` + #[unstable(feature = "seek_read_exact", issue = "none")] + fn seek_read_exact(&self, buf: &mut [u8], offset: u64) -> io::Result<()> { + let mut buf = buf; + let mut offset = offset; + while !buf.is_empty() { + match self.seek_read(buf, offset) { + Ok(0) => break, + Ok(n) => { + buf = &mut buf[n..]; + offset += n as u64; + } + Err(ref e) if e.is_interrupted() => {} + Err(e) => return Err(e), + } + } + if !buf.is_empty() { Err(io::Error::READ_EXACT_EOF) } else { Ok(()) } + } + /// Seeks to a given position and reads some bytes into the buffer. /// /// This is equivalent to the [`seek_read`](FileExt::seek_read) method, except that it is passed From 88af4fd6177ba34c23bfa8a18cc5101c484e1af8 Mon Sep 17 00:00:00 2001 From: Jason Gerard DeRose Date: Wed, 16 Sep 2026 12:26:59 -0600 Subject: [PATCH 09/38] First pass at windows::fs::FileExt.seek_write_all() --- library/std/src/os/windows/fs.rs | 53 ++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/library/std/src/os/windows/fs.rs b/library/std/src/os/windows/fs.rs index e8a4828ecac1b..4e51823361943 100644 --- a/library/std/src/os/windows/fs.rs +++ b/library/std/src/os/windows/fs.rs @@ -185,6 +185,59 @@ pub trait FileExt { /// ``` #[stable(feature = "file_offset", since = "1.15.0")] fn seek_write(&self, buf: &[u8], offset: u64) -> io::Result; + + /// Seeks to a given position and attempts to write an entire buffer. + /// + /// The offset is relative to the start of the file and thus independent + /// from the current cursor. The current cursor **is** affected by this + /// function, it is set to the end of the write. + /// + /// This method will continuously call [`seek_write`] until there is no more data + /// to be written or an error of non-[`io::ErrorKind::Interrupted`] kind is + /// returned. This method will not return until the entire buffer has been + /// successfully written or such an error occurs. The first error that is + /// not of [`io::ErrorKind::Interrupted`] kind generated from this method will be + /// returned. + /// + /// # Errors + /// + /// This function will return the first error of + /// non-[`io::ErrorKind::Interrupted`] kind that [`seek_write`] returns. + /// + /// [`seek_write`]: FileExt::seek_write + /// + /// # Examples + /// + #[cfg_attr(windows, doc = "```no_run")] + #[cfg_attr(not(windows), doc = "```ignore (needs windows)")] + /// use std::fs::File; + /// use std::os::windows::prelude::*; + /// + /// fn main() -> std::io::Result<()> { + /// let file = File::open("foo.txt")?; + /// + /// // We now write at the offset 10. + /// file.write_all_at(b"sushi", 10)?; + /// Ok(()) + /// } + /// ``` + #[unstable(feature = "seek_read_exact_seek_write_all", issue = "none")] + fn seek_write_all(&self, mut buf: &[u8], mut offset: u64) -> io::Result<()> { + while !buf.is_empty() { + match self.seek_write(buf, offset) { + Ok(0) => { + return Err(io::Error::WRITE_ALL_EOF); + } + Ok(n) => { + buf = &buf[n..]; + offset += n as u64 + } + Err(ref e) if e.is_interrupted() => {} + Err(e) => return Err(e), + } + } + Ok(()) + } } #[stable(feature = "file_offset", since = "1.15.0")] From 535fa77a1e628344accacc0cb7557a49b16172cd Mon Sep 17 00:00:00 2001 From: Jason Gerard DeRose Date: Wed, 16 Sep 2026 12:30:57 -0600 Subject: [PATCH 10/38] Fix function signature in seek_read_exact(), duh --- library/std/src/os/windows/fs.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/library/std/src/os/windows/fs.rs b/library/std/src/os/windows/fs.rs index 4e51823361943..e0d0a291f8e5e 100644 --- a/library/std/src/os/windows/fs.rs +++ b/library/std/src/os/windows/fs.rs @@ -96,9 +96,7 @@ pub trait FileExt { /// } /// ``` #[unstable(feature = "seek_read_exact", issue = "none")] - fn seek_read_exact(&self, buf: &mut [u8], offset: u64) -> io::Result<()> { - let mut buf = buf; - let mut offset = offset; + fn seek_read_exact(&self, mut buf: &mut [u8], mut offset: u64) -> io::Result<()> { while !buf.is_empty() { match self.seek_read(buf, offset) { Ok(0) => break, From 5c56219fe534dcb1832f1b5fb4b3507c3a0010ea Mon Sep 17 00:00:00 2001 From: Jason Gerard DeRose Date: Wed, 16 Sep 2026 14:01:39 -0600 Subject: [PATCH 11/38] First pass at tests for .seek_read_exact(), seek_write_all() --- library/std/src/fs/tests.rs | 55 +++++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/library/std/src/fs/tests.rs b/library/std/src/fs/tests.rs index 148f1c32b08b9..dfcde4b4f21cd 100644 --- a/library/std/src/fs/tests.rs +++ b/library/std/src/fs/tests.rs @@ -775,6 +775,61 @@ fn file_test_io_seek_read_write() { check!(fs::remove_file(&filename)); } +#[test] +#[cfg(windows)] +fn file_test_io_seek_read_exact_write_all() { + use crate::os::windows::fs::FileExt; + + let tmpdir = tmpdir(); + let filename = tmpdir.join("file_rt_io_file_test_seek_read_exact_write_all.txt"); + let mut buf = [0; 256]; + let write1 = "asdf"; + let write2 = "qwer-"; + let write3 = "-zxcv"; + let content = "qwer-asdf-zxcv"; + { + let oo = OpenOptions::new().create_new(true).write(true).read(true).clone(); + let mut rw = check!(oo.open(&filename)); + check!(rw.seek_write_all(write1.as_bytes(), 5)); + assert_eq!(check!(rw.stream_position()), 9); + check!(rw.seek_read_exact(&mut buf[..write1.len()], 5)); + assert_eq!(str::from_utf8(&buf[..write1.len()]), Ok(write1)); + assert_eq!(check!(rw.stream_position()), 9); + assert_eq!(check!(rw.seek(SeekFrom::Start(0))), 0); + assert_eq!(check!(rw.write(write2.as_bytes())), write2.len()); + assert_eq!(check!(rw.stream_position()), 5); + assert_eq!(check!(rw.read(&mut buf)), write1.len()); + assert_eq!(str::from_utf8(&buf[..write1.len()]), Ok(write1)); + assert_eq!(check!(rw.stream_position()), 9); + check!(rw.seek_read_exact(&mut buf[..write2.len()], 0)); + assert_eq!(str::from_utf8(&buf[..write2.len()]), Ok(write2)); + assert_eq!(check!(rw.stream_position()), 5); + check!(rw.seek_write_all(write3.as_bytes(), 9)); + assert_eq!(check!(rw.stream_position()), 14); + } + { + let mut read = check!(File::open(&filename)); + check!(read.seek_read_exact(&mut buf[..content.len()], 0)); + assert_eq!(str::from_utf8(&buf[..content.len()]), Ok(content)); + assert_eq!(check!(read.stream_position()), 14); + assert_eq!(check!(read.seek(SeekFrom::End(-5))), 9); + check!(read.seek_read_exact(&mut buf[..content.len()], 0)); + assert_eq!(str::from_utf8(&buf[..content.len()]), Ok(content)); + assert_eq!(check!(read.stream_position()), 14); + assert_eq!(check!(read.seek(SeekFrom::End(-5))), 9); + assert_eq!(check!(read.read(&mut buf)), write3.len()); + assert_eq!(str::from_utf8(&buf[..write3.len()]), Ok(write3)); + assert_eq!(check!(read.stream_position()), 14); + check!(read.seek_read_exact(&mut buf[..content.len()], 0)); + assert_eq!(str::from_utf8(&buf[..content.len()]), Ok(content)); + assert_eq!(check!(read.stream_position()), 14); + assert!(read.seek_read_exact(&mut buf, 14).is_err()); + assert!(read.seek_read_exact(&mut buf, 15).is_err()); + } + check!(fs::remove_file(&filename)); +} + + #[test] #[cfg(windows)] fn test_seek_read_buf() { From 6df2dd9ddc76c7271e3a871b06c27d2265234ec8 Mon Sep 17 00:00:00 2001 From: Jason Gerard DeRose Date: Wed, 16 Sep 2026 14:02:56 -0600 Subject: [PATCH 12/38] Whitespace fix --- library/std/src/fs/tests.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/library/std/src/fs/tests.rs b/library/std/src/fs/tests.rs index dfcde4b4f21cd..14cafc4ae109e 100644 --- a/library/std/src/fs/tests.rs +++ b/library/std/src/fs/tests.rs @@ -829,7 +829,6 @@ fn file_test_io_seek_read_exact_write_all() { check!(fs::remove_file(&filename)); } - #[test] #[cfg(windows)] fn test_seek_read_buf() { From 482f884c22e5b1e9a1ae57ec327adfd28f6943cf Mon Sep 17 00:00:00 2001 From: Jason Gerard DeRose Date: Wed, 16 Sep 2026 14:08:31 -0600 Subject: [PATCH 13/38] Use hypothetical seek_read_exact_seek_write_all feature also for .seek_read_exact() --- library/std/src/os/windows/fs.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/std/src/os/windows/fs.rs b/library/std/src/os/windows/fs.rs index e0d0a291f8e5e..d4fef864175de 100644 --- a/library/std/src/os/windows/fs.rs +++ b/library/std/src/os/windows/fs.rs @@ -95,7 +95,7 @@ pub trait FileExt { /// Ok(()) /// } /// ``` - #[unstable(feature = "seek_read_exact", issue = "none")] + #[unstable(feature = "seek_read_exact_seek_write_all", issue = "none")] fn seek_read_exact(&self, mut buf: &mut [u8], mut offset: u64) -> io::Result<()> { while !buf.is_empty() { match self.seek_read(buf, offset) { From 165e7553a366e12093cafbc53ccc537ed40b2096 Mon Sep 17 00:00:00 2001 From: Jason Gerard DeRose Date: Wed, 16 Sep 2026 15:23:03 -0600 Subject: [PATCH 14/38] Tracking issues 162868 --- library/std/src/os/windows/fs.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/library/std/src/os/windows/fs.rs b/library/std/src/os/windows/fs.rs index d4fef864175de..471efbcb4e81b 100644 --- a/library/std/src/os/windows/fs.rs +++ b/library/std/src/os/windows/fs.rs @@ -95,7 +95,7 @@ pub trait FileExt { /// Ok(()) /// } /// ``` - #[unstable(feature = "seek_read_exact_seek_write_all", issue = "none")] + #[unstable(feature = "seek_read_exact_seek_write_all", issue = "162868")] fn seek_read_exact(&self, mut buf: &mut [u8], mut offset: u64) -> io::Result<()> { while !buf.is_empty() { match self.seek_read(buf, offset) { @@ -219,7 +219,7 @@ pub trait FileExt { /// Ok(()) /// } /// ``` - #[unstable(feature = "seek_read_exact_seek_write_all", issue = "none")] + #[unstable(feature = "seek_read_exact_seek_write_all", issue = "162868")] fn seek_write_all(&self, mut buf: &[u8], mut offset: u64) -> io::Result<()> { while !buf.is_empty() { match self.seek_write(buf, offset) { From fafa1eede91d71b1ed467fe13d6e5e2b678cb609 Mon Sep 17 00:00:00 2001 From: Jason Gerard DeRose Date: Wed, 16 Sep 2026 21:49:08 -0600 Subject: [PATCH 15/38] Oops, fix seek_write_all() doc example, was using write_all_at() still --- library/std/src/os/windows/fs.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/std/src/os/windows/fs.rs b/library/std/src/os/windows/fs.rs index 471efbcb4e81b..6568f81f6afe4 100644 --- a/library/std/src/os/windows/fs.rs +++ b/library/std/src/os/windows/fs.rs @@ -215,7 +215,7 @@ pub trait FileExt { /// let file = File::open("foo.txt")?; /// /// // We now write at the offset 10. - /// file.write_all_at(b"sushi", 10)?; + /// file.seek_write_all(b"sushi", 10)?; /// Ok(()) /// } /// ``` From d179246bebe20834fc5f865e84baf60a70dc2483 Mon Sep 17 00:00:00 2001 From: Jason Gerard DeRose Date: Thu, 17 Sep 2026 09:27:55 -0600 Subject: [PATCH 16/38] Add mocked test for windows FileExt trait --- library/std/src/fs/tests.rs | 72 +++++++++++++++++++++++++++++++++++++ 1 file changed, 72 insertions(+) diff --git a/library/std/src/fs/tests.rs b/library/std/src/fs/tests.rs index 14cafc4ae109e..d5f1074910dc8 100644 --- a/library/std/src/fs/tests.rs +++ b/library/std/src/fs/tests.rs @@ -829,6 +829,78 @@ fn file_test_io_seek_read_exact_write_all() { check!(fs::remove_file(&filename)); } +#[test] +#[cfg(windows)] +fn file_test_windows_fileext_trait() { + use crate::io::Result; + use crate::os::windows::fs::FileExt; + + const MSG: &[u8] = + b"The Rust programming language helps you write faster, more reliable software."; + + // Test when seek_read_exact(), seek_write_all() are called with empty bufferes. + // Importantly, no calls to seek_read() or seek_write() should be made, and therefore + // no syscalls should be made. + { + struct MockFile {} + + impl FileExt for MockFile { + fn seek_read(&self, _buf: &mut [u8], _offset: u64) -> Result { + panic!("should not be called"); + } + + fn seek_write(&self, _buf: &[u8], _offset: u64) -> Result { + panic!("should not be called"); + } + } + + let mock_file = MockFile {}; + check!(mock_file.seek_read_exact(&mut [], 0)); + check!(mock_file.seek_read_exact(&mut [], 42)); + check!(mock_file.seek_write_all(&[], 0)); + check!(mock_file.seek_write_all(&[], 42)); + } + + // Test pathological case where seek_read(), seek_write() only do 1 byte per call. + { + struct MockFile { + base_offset: u64, + } + + impl FileExt for MockFile { + fn seek_read(&self, buf: &mut [u8], offset: u64) -> Result { + let offset = (offset - self.base_offset) as usize; + buf[0..1].copy_from_slice(&MSG[offset..offset + 1]); + Ok(1) + } + + fn seek_write(&self, buf: &[u8], offset: u64) -> Result { + let offset = (offset - self.base_offset) as usize; + assert_eq!(buf[0..1], MSG[offset..offset + 1]); + Ok(1) + } + } + + // Offset is 0 + { + let mock_file = MockFile { base_offset: 0 }; + let mut buf = [0; MSG.len()]; + check!(mock_file.seek_read_exact(&mut buf, 0)); + assert_eq!(&buf, MSG); + check!(mock_file.seek_write_all(&buf, 0)); + } + + // Offset is 420 + { + let mock_file = MockFile { base_offset: 420 }; + let mut buf = [0; MSG.len()]; + check!(mock_file.seek_read_exact(&mut buf, 420)); + assert_eq!(&buf, MSG); + check!(mock_file.seek_write_all(&buf, 420)); + } + } +} + #[test] #[cfg(windows)] fn test_seek_read_buf() { From c0785e41033dc5bfccc8c0256fb656a70c57d360 Mon Sep 17 00:00:00 2001 From: Jason Gerard DeRose Date: Thu, 17 Sep 2026 09:51:46 -0600 Subject: [PATCH 17/38] Spelling fixes --- library/std/src/fs/tests.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/library/std/src/fs/tests.rs b/library/std/src/fs/tests.rs index d5f1074910dc8..9f3d2648ec08e 100644 --- a/library/std/src/fs/tests.rs +++ b/library/std/src/fs/tests.rs @@ -838,9 +838,9 @@ fn file_test_windows_fileext_trait() { const MSG: &[u8] = b"The Rust programming language helps you write faster, more reliable software."; - // Test when seek_read_exact(), seek_write_all() are called with empty bufferes. + // Test when seek_read_exact(), seek_write_all() are called with empty buffers. // Importantly, no calls to seek_read() or seek_write() should be made, and therefore - // no syscalls should be made. + // no system calls should be made. { struct MockFile {} From 2adb7485ba72efc9ff321fc6967f4b7f7103f346 Mon Sep 17 00:00:00 2001 From: Jason Gerard DeRose Date: Thu, 17 Sep 2026 12:13:45 -0600 Subject: [PATCH 18/38] Expand test for windows FileExt trait to include almost all scenarios --- library/std/src/fs/tests.rs | 122 ++++++++++++++++++++++++++++++++++-- 1 file changed, 117 insertions(+), 5 deletions(-) diff --git a/library/std/src/fs/tests.rs b/library/std/src/fs/tests.rs index 9f3d2648ec08e..9a60523c59bc5 100644 --- a/library/std/src/fs/tests.rs +++ b/library/std/src/fs/tests.rs @@ -832,7 +832,7 @@ fn file_test_io_seek_read_exact_write_all() { #[test] #[cfg(windows)] fn file_test_windows_fileext_trait() { - use crate::io::Result; + use crate::io; use crate::os::windows::fs::FileExt; const MSG: &[u8] = @@ -845,11 +845,11 @@ fn file_test_windows_fileext_trait() { struct MockFile {} impl FileExt for MockFile { - fn seek_read(&self, _buf: &mut [u8], _offset: u64) -> Result { + fn seek_read(&self, _buf: &mut [u8], _offset: u64) -> io::Result { panic!("should not be called"); } - fn seek_write(&self, _buf: &[u8], _offset: u64) -> Result { + fn seek_write(&self, _buf: &[u8], _offset: u64) -> io::Result { panic!("should not be called"); } } @@ -861,6 +861,48 @@ fn file_test_windows_fileext_trait() { check!(mock_file.seek_write_all(&[], 42)); } + // Test when only one call is made to seek_read() or seek_write() + { + struct MockFile { + expected_offset: u64, + } + + impl FileExt for MockFile { + fn seek_read(&self, buf: &mut [u8], offset: u64) -> io::Result { + assert_eq!(offset, self.expected_offset); + assert_eq!(buf.len(), MSG.len()); + assert_eq!(buf, &[0; MSG.len()]); + buf.copy_from_slice(MSG); + Ok(MSG.len()) + } + + fn seek_write(&self, buf: &[u8], offset: u64) -> io::Result { + assert_eq!(offset, self.expected_offset); + assert_eq!(buf.len(), MSG.len()); + assert_eq!(buf, MSG); + Ok(MSG.len()) + } + } + + // Offset is 0 + { + let mock_file = MockFile { expected_offset: 0 }; + let mut buf = [0; MSG.len()]; + check!(mock_file.seek_read_exact(&mut buf, 0)); + assert_eq!(&buf, MSG); + check!(mock_file.seek_write_all(&buf, 0)); + } + + // Offset is 420 + { + let mock_file = MockFile { expected_offset: 420 }; + let mut buf = [0; MSG.len()]; + check!(mock_file.seek_read_exact(&mut buf, 420)); + assert_eq!(&buf, MSG); + check!(mock_file.seek_write_all(&buf, 420)); + } + } + // Test pathological case where seek_read(), seek_write() only do 1 byte per call. { struct MockFile { @@ -868,13 +910,13 @@ fn file_test_windows_fileext_trait() { } impl FileExt for MockFile { - fn seek_read(&self, buf: &mut [u8], offset: u64) -> Result { + fn seek_read(&self, buf: &mut [u8], offset: u64) -> io::Result { let offset = (offset - self.base_offset) as usize; buf[0..1].copy_from_slice(&MSG[offset..offset + 1]); Ok(1) } - fn seek_write(&self, buf: &[u8], offset: u64) -> Result { + fn seek_write(&self, buf: &[u8], offset: u64) -> io::Result { let offset = (offset - self.base_offset) as usize; assert_eq!(buf[0..1], MSG[offset..offset + 1]); Ok(1) @@ -899,6 +941,76 @@ fn file_test_windows_fileext_trait() { check!(mock_file.seek_write_all(&buf, 420)); } } + + // Test when seek_read(), seek_write() return Ok(0) + { + struct MockFile {} + + impl FileExt for MockFile { + fn seek_read(&self, _buf: &mut [u8], _offset: u64) -> io::Result { + Ok(0) + } + + fn seek_write(&self, _buf: &[u8], _offset: u64) -> io::Result { + Ok(0) + } + } + + let mock_file = MockFile {}; + let mut buf = [0; 256]; + assert_eq!( + mock_file.seek_read(&mut buf, 0).unwrap_err().kind(), + crate::io::ErrorKind::UnexpectedEof + ); + assert_eq!( + mock_file.seek_read(&mut buf, 420).unwrap_err().kind(), + crate::io::ErrorKind::UnexpectedEof + ); + assert_eq!( + mock_file.seek_write(&buf, 0).unwrap_err().kind(), + crate::io::ErrorKind::WriteZero + ); + assert_eq!( + mock_file.seek_write(&buf, 420).unwrap_err().kind(), + crate::io::ErrorKind::WriteZero + ); + } + + // Test that Err other than io::ErrorKind::Interrupted are propagated up. + { + struct MockFile {} + + impl FileExt for MockFile { + fn seek_read(&self, _buf: &mut [u8], _offset: u64) -> io::Result { + Err(io::Error::new(io::ErrorKind::PermissionDenied, "seek_read")) + } + + fn seek_write(&self, _buf: &[u8], _offset: u64) -> io::Result { + Err(io::Error::new(io::ErrorKind::ConnectionRefused, "seek_write")) + } + } + + let mock_file = MockFile {}; + let mut buf = [0; 256]; + assert_eq!( + mock_file.seek_read(&mut buf, 0).unwrap_err().kind(), + crate::io::ErrorKind::PermissionDenied + ); + assert_eq!( + mock_file.seek_read(&mut buf, 420).unwrap_err().kind(), + crate::io::ErrorKind::PermissionDenied + ); + assert_eq!( + mock_file.seek_write(&buf, 0).unwrap_err().kind(), + crate::io::ErrorKind::ConnectionRefused + ); + assert_eq!( + mock_file.seek_write(&buf, 420).unwrap_err().kind(), + crate::io::ErrorKind::ConnectionRefused + ); + } + + // FIXME: Cover io::ErrorKind::Interrupted, but don't infinite loop ;) } #[test] From 1ee847599812a37d107f6a6a96f90e42faa79e0b Mon Sep 17 00:00:00 2001 From: Jason Gerard DeRose Date: Fri, 18 Sep 2026 12:10:02 -0600 Subject: [PATCH 19/38] Split three tests out of file_test_windows_fileext_trait() --- library/std/src/fs/tests.rs | 92 +++++++++++++++++++++++++++++++++++++ 1 file changed, 92 insertions(+) diff --git a/library/std/src/fs/tests.rs b/library/std/src/fs/tests.rs index 9a60523c59bc5..92952c81b9645 100644 --- a/library/std/src/fs/tests.rs +++ b/library/std/src/fs/tests.rs @@ -829,6 +829,98 @@ fn file_test_io_seek_read_exact_write_all() { check!(fs::remove_file(&filename)); } +#[test] +#[cfg(windows)] +fn file_test_windows_fileext_trait_case_1() { + use crate::os::windows::fs::FileExt; + + // Test when seek_read_exact(), seek_write_all() are called with empty buffers. + // Importantly, no calls to seek_read() or seek_write() should be made, and therefore + // no system calls should be made. + struct MockFile {} + + impl FileExt for MockFile { + fn seek_read(&self, _buf: &mut [u8], _offset: u64) -> io::Result { + panic!("should not be called"); + } + + fn seek_write(&self, _buf: &[u8], _offset: u64) -> io::Result { + panic!("should not be called"); + } + } + + let mock_file = MockFile {}; + check!(mock_file.seek_read_exact(&mut [], 0)); + check!(mock_file.seek_read_exact(&mut [], 420)); + check!(mock_file.seek_write_all(&[], 0)); + check!(mock_file.seek_write_all(&[], 420)); +} + +#[test] +#[cfg(windows)] +fn file_test_windows_fileext_trait_case_2() { + use crate::os::windows::fs::FileExt; + + // Test when seek_read(), seek_write() return Ok(0) + struct MockFile {} + + impl FileExt for MockFile { + fn seek_read(&self, _buf: &mut [u8], _offset: u64) -> io::Result { + Ok(0) + } + + fn seek_write(&self, _buf: &[u8], _offset: u64) -> io::Result { + Ok(0) + } + } + + let mock_file = MockFile {}; + let mut buf = [0; 256]; + assert_eq!(mock_file.seek_read(&mut buf, 0).unwrap_err().kind(), io::ErrorKind::UnexpectedEof); + assert_eq!( + mock_file.seek_read(&mut buf, 420).unwrap_err().kind(), + io::ErrorKind::UnexpectedEof + ); + assert_eq!(mock_file.seek_write(&buf, 0).unwrap_err().kind(), io::ErrorKind::WriteZero); + assert_eq!(mock_file.seek_write(&buf, 420).unwrap_err().kind(), io::ErrorKind::WriteZero); +} + +#[test] +#[cfg(windows)] +fn file_test_windows_fileext_trait_case_3() { + use crate::os::windows::fs::FileExt; + + // Test that Err other than io::ErrorKind::Interrupted are propagated up. + struct MockFile {} + + impl FileExt for MockFile { + fn seek_read(&self, _buf: &mut [u8], _offset: u64) -> io::Result { + Err(io::Error::new(io::ErrorKind::PermissionDenied, "seek_read")) + } + + fn seek_write(&self, _buf: &[u8], _offset: u64) -> io::Result { + Err(io::Error::new(io::ErrorKind::ConnectionRefused, "seek_write")) + } + } + + let mock_file = MockFile {}; + let mut buf = [0; 256]; + assert_eq!( + mock_file.seek_read(&mut buf, 0).unwrap_err().kind(), + io::ErrorKind::PermissionDenied + ); + assert_eq!( + mock_file.seek_read(&mut buf, 420).unwrap_err().kind(), + io::ErrorKind::PermissionDenied + ); + assert_eq!(mock_file.seek_write(&buf, 0).unwrap_err().kind(), io::ErrorKind::ConnectionRefused); + assert_eq!( + mock_file.seek_write(&buf, 420).unwrap_err().kind(), + io::ErrorKind::ConnectionRefused + ); + // FIXME: Cover io::ErrorKind::Interrupted, but don't infinite loop ;) +} + #[test] #[cfg(windows)] fn file_test_windows_fileext_trait() { From c9c5a06d77e7ad0fdcb38f3b10b4051bbe1e89aa Mon Sep 17 00:00:00 2001 From: Jason Gerard DeRose Date: Fri, 18 Sep 2026 12:11:04 -0600 Subject: [PATCH 20/38] Remove old versions of those 3 tests --- library/std/src/fs/tests.rs | 93 ------------------------------------- 1 file changed, 93 deletions(-) diff --git a/library/std/src/fs/tests.rs b/library/std/src/fs/tests.rs index 92952c81b9645..f727c224e485e 100644 --- a/library/std/src/fs/tests.rs +++ b/library/std/src/fs/tests.rs @@ -930,29 +930,6 @@ fn file_test_windows_fileext_trait() { const MSG: &[u8] = b"The Rust programming language helps you write faster, more reliable software."; - // Test when seek_read_exact(), seek_write_all() are called with empty buffers. - // Importantly, no calls to seek_read() or seek_write() should be made, and therefore - // no system calls should be made. - { - struct MockFile {} - - impl FileExt for MockFile { - fn seek_read(&self, _buf: &mut [u8], _offset: u64) -> io::Result { - panic!("should not be called"); - } - - fn seek_write(&self, _buf: &[u8], _offset: u64) -> io::Result { - panic!("should not be called"); - } - } - - let mock_file = MockFile {}; - check!(mock_file.seek_read_exact(&mut [], 0)); - check!(mock_file.seek_read_exact(&mut [], 42)); - check!(mock_file.seek_write_all(&[], 0)); - check!(mock_file.seek_write_all(&[], 42)); - } - // Test when only one call is made to seek_read() or seek_write() { struct MockFile { @@ -1033,76 +1010,6 @@ fn file_test_windows_fileext_trait() { check!(mock_file.seek_write_all(&buf, 420)); } } - - // Test when seek_read(), seek_write() return Ok(0) - { - struct MockFile {} - - impl FileExt for MockFile { - fn seek_read(&self, _buf: &mut [u8], _offset: u64) -> io::Result { - Ok(0) - } - - fn seek_write(&self, _buf: &[u8], _offset: u64) -> io::Result { - Ok(0) - } - } - - let mock_file = MockFile {}; - let mut buf = [0; 256]; - assert_eq!( - mock_file.seek_read(&mut buf, 0).unwrap_err().kind(), - crate::io::ErrorKind::UnexpectedEof - ); - assert_eq!( - mock_file.seek_read(&mut buf, 420).unwrap_err().kind(), - crate::io::ErrorKind::UnexpectedEof - ); - assert_eq!( - mock_file.seek_write(&buf, 0).unwrap_err().kind(), - crate::io::ErrorKind::WriteZero - ); - assert_eq!( - mock_file.seek_write(&buf, 420).unwrap_err().kind(), - crate::io::ErrorKind::WriteZero - ); - } - - // Test that Err other than io::ErrorKind::Interrupted are propagated up. - { - struct MockFile {} - - impl FileExt for MockFile { - fn seek_read(&self, _buf: &mut [u8], _offset: u64) -> io::Result { - Err(io::Error::new(io::ErrorKind::PermissionDenied, "seek_read")) - } - - fn seek_write(&self, _buf: &[u8], _offset: u64) -> io::Result { - Err(io::Error::new(io::ErrorKind::ConnectionRefused, "seek_write")) - } - } - - let mock_file = MockFile {}; - let mut buf = [0; 256]; - assert_eq!( - mock_file.seek_read(&mut buf, 0).unwrap_err().kind(), - crate::io::ErrorKind::PermissionDenied - ); - assert_eq!( - mock_file.seek_read(&mut buf, 420).unwrap_err().kind(), - crate::io::ErrorKind::PermissionDenied - ); - assert_eq!( - mock_file.seek_write(&buf, 0).unwrap_err().kind(), - crate::io::ErrorKind::ConnectionRefused - ); - assert_eq!( - mock_file.seek_write(&buf, 420).unwrap_err().kind(), - crate::io::ErrorKind::ConnectionRefused - ); - } - - // FIXME: Cover io::ErrorKind::Interrupted, but don't infinite loop ;) } #[test] From 66a3b724ca7f47d58d4e9b94613e1850235ce859 Mon Sep 17 00:00:00 2001 From: Jason Gerard DeRose Date: Fri, 18 Sep 2026 12:23:45 -0600 Subject: [PATCH 21/38] Split remaining file_test_windows_fileext_trait() into case 4, 5 --- library/std/src/fs/tests.rs | 136 +++++++++++++++++++----------------- 1 file changed, 70 insertions(+), 66 deletions(-) diff --git a/library/std/src/fs/tests.rs b/library/std/src/fs/tests.rs index f727c224e485e..0f2e07507ec8b 100644 --- a/library/std/src/fs/tests.rs +++ b/library/std/src/fs/tests.rs @@ -923,93 +923,97 @@ fn file_test_windows_fileext_trait_case_3() { #[test] #[cfg(windows)] -fn file_test_windows_fileext_trait() { - use crate::io; +fn file_test_windows_fileext_trait_case_4() { use crate::os::windows::fs::FileExt; const MSG: &[u8] = b"The Rust programming language helps you write faster, more reliable software."; // Test when only one call is made to seek_read() or seek_write() - { - struct MockFile { - expected_offset: u64, - } - - impl FileExt for MockFile { - fn seek_read(&self, buf: &mut [u8], offset: u64) -> io::Result { - assert_eq!(offset, self.expected_offset); - assert_eq!(buf.len(), MSG.len()); - assert_eq!(buf, &[0; MSG.len()]); - buf.copy_from_slice(MSG); - Ok(MSG.len()) - } + struct MockFile { + expected_offset: u64, + } - fn seek_write(&self, buf: &[u8], offset: u64) -> io::Result { - assert_eq!(offset, self.expected_offset); - assert_eq!(buf.len(), MSG.len()); - assert_eq!(buf, MSG); - Ok(MSG.len()) - } + impl FileExt for MockFile { + fn seek_read(&self, buf: &mut [u8], offset: u64) -> io::Result { + assert_eq!(offset, self.expected_offset); + assert_eq!(buf.len(), MSG.len()); + assert_eq!(buf, &[0; MSG.len()]); + buf.copy_from_slice(MSG); + Ok(MSG.len()) } - // Offset is 0 - { - let mock_file = MockFile { expected_offset: 0 }; - let mut buf = [0; MSG.len()]; - check!(mock_file.seek_read_exact(&mut buf, 0)); - assert_eq!(&buf, MSG); - check!(mock_file.seek_write_all(&buf, 0)); + fn seek_write(&self, buf: &[u8], offset: u64) -> io::Result { + assert_eq!(offset, self.expected_offset); + assert_eq!(buf.len(), MSG.len()); + assert_eq!(buf, MSG); + Ok(MSG.len()) } + } - // Offset is 420 - { - let mock_file = MockFile { expected_offset: 420 }; - let mut buf = [0; MSG.len()]; - check!(mock_file.seek_read_exact(&mut buf, 420)); - assert_eq!(&buf, MSG); - check!(mock_file.seek_write_all(&buf, 420)); - } + // Offset is 0 + { + let mock_file = MockFile { expected_offset: 0 }; + let mut buf = [0; MSG.len()]; + check!(mock_file.seek_read_exact(&mut buf, 0)); + assert_eq!(&buf, MSG); + check!(mock_file.seek_write_all(&buf, 0)); } - // Test pathological case where seek_read(), seek_write() only do 1 byte per call. + // Offset is 420 { - struct MockFile { - base_offset: u64, - } + let mock_file = MockFile { expected_offset: 420 }; + let mut buf = [0; MSG.len()]; + check!(mock_file.seek_read_exact(&mut buf, 420)); + assert_eq!(&buf, MSG); + check!(mock_file.seek_write_all(&buf, 420)); + } +} - impl FileExt for MockFile { - fn seek_read(&self, buf: &mut [u8], offset: u64) -> io::Result { - let offset = (offset - self.base_offset) as usize; - buf[0..1].copy_from_slice(&MSG[offset..offset + 1]); - Ok(1) - } +#[test] +#[cfg(windows)] +fn file_test_windows_fileext_trait_case_5() { + use crate::os::windows::fs::FileExt; - fn seek_write(&self, buf: &[u8], offset: u64) -> io::Result { - let offset = (offset - self.base_offset) as usize; - assert_eq!(buf[0..1], MSG[offset..offset + 1]); - Ok(1) - } - } + const MSG: &[u8] = + b"Rust is for students and those who are interested in learning about systems concepts."; - // Offset is 0 - { - let mock_file = MockFile { base_offset: 0 }; - let mut buf = [0; MSG.len()]; - check!(mock_file.seek_read_exact(&mut buf, 0)); - assert_eq!(&buf, MSG); - check!(mock_file.seek_write_all(&buf, 0)); + // Test pathological case where seek_read(), seek_write() only do 1 byte per call. + struct MockFile { + base_offset: u64, + } + + impl FileExt for MockFile { + fn seek_read(&self, buf: &mut [u8], offset: u64) -> io::Result { + let offset = (offset - self.base_offset) as usize; + buf[0..1].copy_from_slice(&MSG[offset..offset + 1]); + Ok(1) } - // Offset is 420 - { - let mock_file = MockFile { base_offset: 420 }; - let mut buf = [0; MSG.len()]; - check!(mock_file.seek_read_exact(&mut buf, 420)); - assert_eq!(&buf, MSG); - check!(mock_file.seek_write_all(&buf, 420)); + fn seek_write(&self, buf: &[u8], offset: u64) -> io::Result { + let offset = (offset - self.base_offset) as usize; + assert_eq!(buf[0..1], MSG[offset..offset + 1]); + Ok(1) } } + + // Offset is 0 + { + let mock_file = MockFile { base_offset: 0 }; + let mut buf = [0; MSG.len()]; + check!(mock_file.seek_read_exact(&mut buf, 0)); + assert_eq!(&buf, MSG); + check!(mock_file.seek_write_all(&buf, 0)); + } + + // Offset is 420 + { + let mock_file = MockFile { base_offset: 420 }; + let mut buf = [0; MSG.len()]; + check!(mock_file.seek_read_exact(&mut buf, 420)); + assert_eq!(&buf, MSG); + check!(mock_file.seek_write_all(&buf, 420)); + } } #[test] From 71f10b421541b24303133605a88d74eb33798f32 Mon Sep 17 00:00:00 2001 From: Jason Gerard DeRose Date: Fri, 18 Sep 2026 12:51:37 -0600 Subject: [PATCH 22/38] More test cleanup, always test expected_offset where possible --- library/std/src/fs/tests.rs | 101 ++++++++++++++++++++++-------------- 1 file changed, 62 insertions(+), 39 deletions(-) diff --git a/library/std/src/fs/tests.rs b/library/std/src/fs/tests.rs index 0f2e07507ec8b..e828aa0c2b08b 100644 --- a/library/std/src/fs/tests.rs +++ b/library/std/src/fs/tests.rs @@ -835,8 +835,6 @@ fn file_test_windows_fileext_trait_case_1() { use crate::os::windows::fs::FileExt; // Test when seek_read_exact(), seek_write_all() are called with empty buffers. - // Importantly, no calls to seek_read() or seek_write() should be made, and therefore - // no system calls should be made. struct MockFile {} impl FileExt for MockFile { @@ -851,8 +849,8 @@ fn file_test_windows_fileext_trait_case_1() { let mock_file = MockFile {}; check!(mock_file.seek_read_exact(&mut [], 0)); - check!(mock_file.seek_read_exact(&mut [], 420)); check!(mock_file.seek_write_all(&[], 0)); + check!(mock_file.seek_read_exact(&mut [], 420)); check!(mock_file.seek_write_all(&[], 420)); } @@ -862,27 +860,41 @@ fn file_test_windows_fileext_trait_case_2() { use crate::os::windows::fs::FileExt; // Test when seek_read(), seek_write() return Ok(0) - struct MockFile {} + struct MockFile { + expected_offset: u64, + } impl FileExt for MockFile { - fn seek_read(&self, _buf: &mut [u8], _offset: u64) -> io::Result { + fn seek_read(&self, _buf: &mut [u8], offset: u64) -> io::Result { + assert_eq!(offset, self.expected_offset); Ok(0) } - fn seek_write(&self, _buf: &[u8], _offset: u64) -> io::Result { + fn seek_write(&self, _buf: &[u8], offset: u64) -> io::Result { + assert_eq!(offset, self.expected_offset); Ok(0) } } - let mock_file = MockFile {}; - let mut buf = [0; 256]; - assert_eq!(mock_file.seek_read(&mut buf, 0).unwrap_err().kind(), io::ErrorKind::UnexpectedEof); - assert_eq!( - mock_file.seek_read(&mut buf, 420).unwrap_err().kind(), - io::ErrorKind::UnexpectedEof - ); - assert_eq!(mock_file.seek_write(&buf, 0).unwrap_err().kind(), io::ErrorKind::WriteZero); - assert_eq!(mock_file.seek_write(&buf, 420).unwrap_err().kind(), io::ErrorKind::WriteZero); + { + let mock_file = MockFile { expected_offset: 0 }; + let mut buf = [0; 256]; + assert_eq!( + mock_file.seek_read(&mut buf, 0).unwrap_err().kind(), + io::ErrorKind::UnexpectedEof + ); + assert_eq!(mock_file.seek_write(&buf, 0).unwrap_err().kind(), io::ErrorKind::WriteZero); + } + + { + let mock_file = MockFile { expected_offset: 420 }; + let mut buf = [0; 256]; + assert_eq!(mock_file.seek_write(&buf, 420).unwrap_err().kind(), io::ErrorKind::WriteZero); + assert_eq!( + mock_file.seek_read(&mut buf, 420).unwrap_err().kind(), + io::ErrorKind::UnexpectedEof + ); + } } #[test] @@ -891,33 +903,47 @@ fn file_test_windows_fileext_trait_case_3() { use crate::os::windows::fs::FileExt; // Test that Err other than io::ErrorKind::Interrupted are propagated up. - struct MockFile {} + struct MockFile { + expected_offset: u64, + } impl FileExt for MockFile { - fn seek_read(&self, _buf: &mut [u8], _offset: u64) -> io::Result { + fn seek_read(&self, _buf: &mut [u8], offset: u64) -> io::Result { + assert_eq!(offset, self.expected_offset); Err(io::Error::new(io::ErrorKind::PermissionDenied, "seek_read")) } - fn seek_write(&self, _buf: &[u8], _offset: u64) -> io::Result { + fn seek_write(&self, _buf: &[u8], offset: u64) -> io::Result { + assert_eq!(offset, self.expected_offset); Err(io::Error::new(io::ErrorKind::ConnectionRefused, "seek_write")) } } - let mock_file = MockFile {}; - let mut buf = [0; 256]; - assert_eq!( - mock_file.seek_read(&mut buf, 0).unwrap_err().kind(), - io::ErrorKind::PermissionDenied - ); - assert_eq!( - mock_file.seek_read(&mut buf, 420).unwrap_err().kind(), - io::ErrorKind::PermissionDenied - ); - assert_eq!(mock_file.seek_write(&buf, 0).unwrap_err().kind(), io::ErrorKind::ConnectionRefused); - assert_eq!( - mock_file.seek_write(&buf, 420).unwrap_err().kind(), - io::ErrorKind::ConnectionRefused - ); + { + let mock_file = MockFile { expected_offset: 0 }; + let mut buf = [0; 256]; + assert_eq!( + mock_file.seek_read(&mut buf, 0).unwrap_err().kind(), + io::ErrorKind::PermissionDenied + ); + assert_eq!( + mock_file.seek_write(&buf, 0).unwrap_err().kind(), + io::ErrorKind::ConnectionRefused + ); + } + + { + let mock_file = MockFile { expected_offset: 420 }; + let mut buf = [0; 256]; + assert_eq!( + mock_file.seek_read(&mut buf, 420).unwrap_err().kind(), + io::ErrorKind::PermissionDenied + ); + assert_eq!( + mock_file.seek_write(&buf, 420).unwrap_err().kind(), + io::ErrorKind::ConnectionRefused + ); + } // FIXME: Cover io::ErrorKind::Interrupted, but don't infinite loop ;) } @@ -929,7 +955,8 @@ fn file_test_windows_fileext_trait_case_4() { const MSG: &[u8] = b"The Rust programming language helps you write faster, more reliable software."; - // Test when only one call is made to seek_read() or seek_write() + // Test when the entire read or write is satisfied by only one call to seek_read() or + // seek_write(), respectively. struct MockFile { expected_offset: u64, } @@ -951,7 +978,6 @@ fn file_test_windows_fileext_trait_case_4() { } } - // Offset is 0 { let mock_file = MockFile { expected_offset: 0 }; let mut buf = [0; MSG.len()]; @@ -960,7 +986,6 @@ fn file_test_windows_fileext_trait_case_4() { check!(mock_file.seek_write_all(&buf, 0)); } - // Offset is 420 { let mock_file = MockFile { expected_offset: 420 }; let mut buf = [0; MSG.len()]; @@ -978,7 +1003,7 @@ fn file_test_windows_fileext_trait_case_5() { const MSG: &[u8] = b"Rust is for students and those who are interested in learning about systems concepts."; - // Test pathological case where seek_read(), seek_write() only do 1 byte per call. + // Test pathological case where seek_read(), seek_write() only do 1 byte per call, return Ok(1) struct MockFile { base_offset: u64, } @@ -997,7 +1022,6 @@ fn file_test_windows_fileext_trait_case_5() { } } - // Offset is 0 { let mock_file = MockFile { base_offset: 0 }; let mut buf = [0; MSG.len()]; @@ -1006,7 +1030,6 @@ fn file_test_windows_fileext_trait_case_5() { check!(mock_file.seek_write_all(&buf, 0)); } - // Offset is 420 { let mock_file = MockFile { base_offset: 420 }; let mut buf = [0; MSG.len()]; From e9aaeef7bbfbd61bad035beb28c9f38b0c4eed28 Mon Sep 17 00:00:00 2001 From: Jason Gerard DeRose Date: Fri, 18 Sep 2026 12:58:46 -0600 Subject: [PATCH 23/38] Test read first for consistency --- library/std/src/fs/tests.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/std/src/fs/tests.rs b/library/std/src/fs/tests.rs index e828aa0c2b08b..6607d5bf5771e 100644 --- a/library/std/src/fs/tests.rs +++ b/library/std/src/fs/tests.rs @@ -889,11 +889,11 @@ fn file_test_windows_fileext_trait_case_2() { { let mock_file = MockFile { expected_offset: 420 }; let mut buf = [0; 256]; - assert_eq!(mock_file.seek_write(&buf, 420).unwrap_err().kind(), io::ErrorKind::WriteZero); assert_eq!( mock_file.seek_read(&mut buf, 420).unwrap_err().kind(), io::ErrorKind::UnexpectedEof ); + assert_eq!(mock_file.seek_write(&buf, 420).unwrap_err().kind(), io::ErrorKind::WriteZero); } } From c2218a7324b09531f262e1004cd31d3c0d5de533 Mon Sep 17 00:00:00 2001 From: Jason Gerard DeRose Date: Fri, 18 Sep 2026 13:05:24 -0600 Subject: [PATCH 24/38] Use same doctsring examples as seek_read(), seek_write() --- library/std/src/os/windows/fs.rs | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/library/std/src/os/windows/fs.rs b/library/std/src/os/windows/fs.rs index 6568f81f6afe4..68128c2f49b00 100644 --- a/library/std/src/os/windows/fs.rs +++ b/library/std/src/os/windows/fs.rs @@ -86,12 +86,12 @@ pub trait FileExt { /// use std::os::windows::prelude::*; /// /// fn main() -> io::Result<()> { - /// let mut buf = [0u8; 8]; - /// let file = File::open("foo.txt")?; + /// let mut file = File::open("foo.txt")?; + /// let mut buffer = [0; 10]; /// - /// // We now read exactly 8 bytes from the offset 10. - /// file.seek_read_exact(&mut buf, 10)?; - /// println!("read {} bytes: {:?}", buf.len(), buf); + /// // Read 10 bytes, starting 72 bytes from the + /// // start of the file. + /// file.seek_read_exact(&mut buffer[..], 72)?; /// Ok(()) /// } /// ``` @@ -212,10 +212,11 @@ pub trait FileExt { /// use std::os::windows::prelude::*; /// /// fn main() -> std::io::Result<()> { - /// let file = File::open("foo.txt")?; + /// let mut buffer = File::create("foo.txt")?; /// - /// // We now write at the offset 10. - /// file.seek_write_all(b"sushi", 10)?; + /// // Write a byte string starting 72 bytes from + /// // the start of the file. + /// buffer.seek_write_all(b"some bytes", 72)?; /// Ok(()) /// } /// ``` From 6ac2b720498bf1a53c22253cf8a0ba23f0bb91c6 Mon Sep 17 00:00:00 2001 From: Jason Gerard DeRose Date: Fri, 18 Sep 2026 13:07:42 -0600 Subject: [PATCH 25/38] Missing period --- library/std/src/os/windows/fs.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/std/src/os/windows/fs.rs b/library/std/src/os/windows/fs.rs index 68128c2f49b00..69975e1d4e232 100644 --- a/library/std/src/os/windows/fs.rs +++ b/library/std/src/os/windows/fs.rs @@ -50,7 +50,7 @@ pub trait FileExt { #[stable(feature = "file_offset", since = "1.15.0")] fn seek_read(&self, buf: &mut [u8], offset: u64) -> io::Result; - /// Seeks to a given position and reads the exact number of bytes required to fill `buf` + /// Seeks to a given position and reads the exact number of bytes required to fill `buf`. /// /// The offset is relative to the start of the file and thus independent /// from the current cursor. The current cursor **is** affected by this From ee766dc8d03d3f7b9de904333cea9867f58bf9d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Rakic?= Date: Fri, 18 Sep 2026 23:52:25 +0200 Subject: [PATCH 26/38] post GH comment on types nominations --- triagebot.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/triagebot.toml b/triagebot.toml index fc9c43d2dbcae..36d9ee29b6597 100644 --- a/triagebot.toml +++ b/triagebot.toml @@ -702,6 +702,7 @@ topic = "#{number}: {title}" message_on_add = """\ @*T-types* issue #{number} "{title}" has been nominated for team discussion. """ +github_comment = ":robot: A [dedicated `#t-types/nominated` topic]({zulip_topic_url}) has been opened for humans to discuss this issue :robot:" message_on_remove = "Issue #{number}'s nomination has been removed. Thanks all for participating!" message_on_close = "Issue #{number} has been closed. Thanks for participating!" message_on_reopen = "Issue #{number} has been reopened. Pinging @*T-types*." From 75b444d1ad334617718f331b210fb88a6ff77c4b Mon Sep 17 00:00:00 2001 From: Shun Sakai Date: Sun, 6 Sep 2026 11:39:02 +0900 Subject: [PATCH 27/38] Constify `impl FromStr for NonZero` --- library/core/src/num/nonzero.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/library/core/src/num/nonzero.rs b/library/core/src/num/nonzero.rs index 0563c225f7e0b..d0ae6c31267f3 100644 --- a/library/core/src/num/nonzero.rs +++ b/library/core/src/num/nonzero.rs @@ -1412,7 +1412,8 @@ macro_rules! nonzero_integer { } #[stable(feature = "nonzero_parse", since = "1.35.0")] - impl FromStr for NonZero<$Int> { + #[rustc_const_unstable(feature = "const_convert", issue = "143773")] + const impl FromStr for NonZero<$Int> { type Err = ParseIntError; /// Parses a non-zero integer from a string slice with decimal digits. From c30f2a421f4a835a04476d4c71e16e8f7fe90c34 Mon Sep 17 00:00:00 2001 From: lapla Date: Sat, 19 Sep 2026 15:45:50 +0900 Subject: [PATCH 28/38] Use `end_point` for trailing brace in `let...else` diagnostics --- compiler/rustc_parse/src/parser/stmt.rs | 4 +-- tests/ui/parser/let-else-fullwidth-brace.rs | 7 ++++++ .../ui/parser/let-else-fullwidth-brace.stderr | 25 +++++++++++++++++++ 3 files changed, 34 insertions(+), 2 deletions(-) create mode 100644 tests/ui/parser/let-else-fullwidth-brace.rs create mode 100644 tests/ui/parser/let-else-fullwidth-brace.stderr diff --git a/compiler/rustc_parse/src/parser/stmt.rs b/compiler/rustc_parse/src/parser/stmt.rs index 7c3752cfff187..df34a2864b48a 100644 --- a/compiler/rustc_parse/src/parser/stmt.rs +++ b/compiler/rustc_parse/src/parser/stmt.rs @@ -12,7 +12,7 @@ use rustc_ast::{ LocalKind, MacCall, MacCallStmt, MacStmtStyle, Recovered, Stmt, StmtKind, }; use rustc_errors::{Applicability, Diag, PResult}; -use rustc_span::{BytePos, ErrorGuaranteed, Ident, Span, kw, sym}; +use rustc_span::{ErrorGuaranteed, Ident, Span, kw, sym}; use thin_vec::{ThinVec, thin_vec}; use super::attr::InnerAttrForbiddenReason; @@ -467,7 +467,7 @@ impl<'a> Parser<'a> { ), }; self.dcx().emit_err(diagnostics::InvalidCurlyInLetElse { - span: span.with_lo(span.hi() - BytePos(1)), + span: self.psess.source_map().end_point(span), sugg, }); } diff --git a/tests/ui/parser/let-else-fullwidth-brace.rs b/tests/ui/parser/let-else-fullwidth-brace.rs new file mode 100644 index 0000000000000..98bd65605faa8 --- /dev/null +++ b/tests/ui/parser/let-else-fullwidth-brace.rs @@ -0,0 +1,7 @@ +#![allow(irrefutable_let_patterns)] + +fn main() { + let x = {1} else { return; }; + //~^ ERROR unknown start of token: \u{ff5d} + //~| ERROR right curly brace `}` before `else` in a `let...else` statement not allowed +} diff --git a/tests/ui/parser/let-else-fullwidth-brace.stderr b/tests/ui/parser/let-else-fullwidth-brace.stderr new file mode 100644 index 0000000000000..a5bb4d029dbc5 --- /dev/null +++ b/tests/ui/parser/let-else-fullwidth-brace.stderr @@ -0,0 +1,25 @@ +error: unknown start of token: \u{ff5d} + --> $DIR/let-else-fullwidth-brace.rs:4:15 + | +LL | let x = {1} else { return; }; + | ^^ + | +help: Unicode character '}' (Fullwidth Right Curly Bracket) looks like '}' (Right Curly Brace), but it is not + | +LL - let x = {1} else { return; }; +LL + let x = {1} else { return; }; + | + +error: right curly brace `}` before `else` in a `let...else` statement not allowed + --> $DIR/let-else-fullwidth-brace.rs:4:15 + | +LL | let x = {1} else { return; }; + | ^^ + | +help: wrap the expression in parentheses + | +LL | let x = ({1}) else { return; }; + | + + + +error: aborting due to 2 previous errors + From 1b3f10d6f9be9dbbe571615aea7749ab83a81433 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 19 Sep 2026 11:13:45 +0200 Subject: [PATCH 29/38] add Dir::try_clone --- library/std/src/fs.rs | 22 ++++++++++++++++++++++ library/std/src/fs/tests.rs | 13 +++++++++++++ library/std/src/sys/fs/common.rs | 4 ++++ library/std/src/sys/fs/unix/dir.rs | 4 ++++ library/std/src/sys/fs/windows/dir.rs | 4 ++++ 5 files changed, 47 insertions(+) diff --git a/library/std/src/fs.rs b/library/std/src/fs.rs index 8df809264a6dd..65b8ed634bc05 100644 --- a/library/std/src/fs.rs +++ b/library/std/src/fs.rs @@ -1869,6 +1869,28 @@ impl Dir { pub fn remove_dir>(&self, path: P) -> io::Result<()> { self.inner.remove_dir(path.as_ref()) } + + /// Creates a new `Dir` instance that shares the same underlying directory handle + /// as the existing `Dir` instance. + /// + /// # Examples + /// + /// Creates two handles for a directory named `foo`: + /// + /// ```no_run + /// #![feature(dirfd)] + /// use std::fs::Dir; + /// + /// fn main() -> std::io::Result<()> { + /// let dir = Dir::open("foo")?; + /// let dir_copy = dir.try_clone()?; + /// Ok(()) + /// } + /// ``` + #[unstable(feature = "dirfd", issue = "120426")] + pub fn try_clone(&self) -> io::Result { + Ok(Dir { inner: self.inner.duplicate()? }) + } } impl AsInner for Dir { diff --git a/library/std/src/fs/tests.rs b/library/std/src/fs/tests.rs index 148f1c32b08b9..e949237d3dca2 100644 --- a/library/std/src/fs/tests.rs +++ b/library/std/src/fs/tests.rs @@ -2726,6 +2726,19 @@ fn test_dir_read_file() { assert_eq!("bar", &buf); } +#[test] +fn test_dir_clone() { + let tmpdir = tmpdir(); + let mut f = check!(File::create(tmpdir.join("foo.txt"))); + check!(f.write_all(b"bar")); + drop(f); + + let dir = check!(Dir::open(tmpdir.path())); + let dir2 = check!(dir.try_clone()); + let f = check!(dir2.open_file("foo.txt")); + drop(f); +} + #[test] fn test_dir_metadata() { let tmpdir = tmpdir(); diff --git a/library/std/src/sys/fs/common.rs b/library/std/src/sys/fs/common.rs index 17b98a4506544..96bafb26bb969 100644 --- a/library/std/src/sys/fs/common.rs +++ b/library/std/src/sys/fs/common.rs @@ -77,6 +77,10 @@ impl Dir { Self::open(path, &opts) } + pub fn duplicate(&self) -> io::Result { + Ok(Self { path: self.path.clone() }) + } + pub fn open_file(&self, path: &Path, opts: &OpenOptions) -> io::Result { File::open(&self.path.join(path), opts) } diff --git a/library/std/src/sys/fs/unix/dir.rs b/library/std/src/sys/fs/unix/dir.rs index 3fe952d942927..cf0dece265054 100644 --- a/library/std/src/sys/fs/unix/dir.rs +++ b/library/std/src/sys/fs/unix/dir.rs @@ -47,6 +47,10 @@ impl Dir { run_path_with_cstr(path, &|path| Self::open_traversal_c(path)) } + pub fn duplicate(&self) -> io::Result { + Ok(Self(self.0.try_clone()?)) + } + pub fn open_file(&self, path: &Path, opts: &OpenOptions) -> io::Result { run_path_with_cstr(path.as_ref(), &|path| self.open_file_c(path, opts, 0)) .map(FileDesc::from_inner) diff --git a/library/std/src/sys/fs/windows/dir.rs b/library/std/src/sys/fs/windows/dir.rs index 70bade84f58fd..d4674ad24f87e 100644 --- a/library/std/src/sys/fs/windows/dir.rs +++ b/library/std/src/sys/fs/windows/dir.rs @@ -72,6 +72,10 @@ impl Dir { with_native_path(path, &|path| Self::open_with_native(path, &opts)) } + pub fn duplicate(&self) -> io::Result { + Ok(Self { handle: self.handle.try_clone()? }) + } + pub fn open_file(&self, path: &Path, opts: &OpenOptions) -> io::Result { // NtCreateFile will fail if given an absolute path and a non-null RootDirectory if path.is_absolute() { From bbdd2985aaa77de90eb280f486ebddce35326c77 Mon Sep 17 00:00:00 2001 From: Jason Gerard DeRose Date: Sat, 19 Sep 2026 06:38:36 -0600 Subject: [PATCH 30/38] Oops: actually call _exact(), _all() methods in case 2, 3 --- library/std/src/fs/tests.rs | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/library/std/src/fs/tests.rs b/library/std/src/fs/tests.rs index 6607d5bf5771e..a4c48ed0563ad 100644 --- a/library/std/src/fs/tests.rs +++ b/library/std/src/fs/tests.rs @@ -880,20 +880,23 @@ fn file_test_windows_fileext_trait_case_2() { let mock_file = MockFile { expected_offset: 0 }; let mut buf = [0; 256]; assert_eq!( - mock_file.seek_read(&mut buf, 0).unwrap_err().kind(), + mock_file.seek_read_exact(&mut buf, 0).unwrap_err().kind(), io::ErrorKind::UnexpectedEof ); - assert_eq!(mock_file.seek_write(&buf, 0).unwrap_err().kind(), io::ErrorKind::WriteZero); + assert_eq!(mock_file.seek_write_all(&buf, 0).unwrap_err().kind(), io::ErrorKind::WriteZero); } { let mock_file = MockFile { expected_offset: 420 }; let mut buf = [0; 256]; assert_eq!( - mock_file.seek_read(&mut buf, 420).unwrap_err().kind(), + mock_file.seek_read_exact(&mut buf, 420).unwrap_err().kind(), io::ErrorKind::UnexpectedEof ); - assert_eq!(mock_file.seek_write(&buf, 420).unwrap_err().kind(), io::ErrorKind::WriteZero); + assert_eq!( + mock_file.seek_write_all(&buf, 420).unwrap_err().kind(), + io::ErrorKind::WriteZero + ); } } @@ -923,11 +926,11 @@ fn file_test_windows_fileext_trait_case_3() { let mock_file = MockFile { expected_offset: 0 }; let mut buf = [0; 256]; assert_eq!( - mock_file.seek_read(&mut buf, 0).unwrap_err().kind(), + mock_file.seek_read_exact(&mut buf, 0).unwrap_err().kind(), io::ErrorKind::PermissionDenied ); assert_eq!( - mock_file.seek_write(&buf, 0).unwrap_err().kind(), + mock_file.seek_write_all(&buf, 0).unwrap_err().kind(), io::ErrorKind::ConnectionRefused ); } @@ -936,11 +939,11 @@ fn file_test_windows_fileext_trait_case_3() { let mock_file = MockFile { expected_offset: 420 }; let mut buf = [0; 256]; assert_eq!( - mock_file.seek_read(&mut buf, 420).unwrap_err().kind(), + mock_file.seek_read_exact(&mut buf, 420).unwrap_err().kind(), io::ErrorKind::PermissionDenied ); assert_eq!( - mock_file.seek_write(&buf, 420).unwrap_err().kind(), + mock_file.seek_write_all(&buf, 420).unwrap_err().kind(), io::ErrorKind::ConnectionRefused ); } From 6524ec5bde1bc006773ecceccf80167e9ebd9092 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Le=C3=B3n=20Orell=20Valerian=20Liehr?= Date: Sat, 12 Sep 2026 23:01:57 +0200 Subject: [PATCH 31/38] Remove incorrect parse error recovery code that mistakes `as` casts for the long removed type ascription --- compiler/rustc_parse/src/parser/expr.rs | 32 ------------------------- 1 file changed, 32 deletions(-) diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index 58e98a64b5e41..021ed221f057c 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -576,38 +576,6 @@ impl<'a> Parser<'a> { // `usize < y` as a type with generic arguments. let parser_snapshot_after_type = mem::replace(self, parser_snapshot_before_type); - // Check for typo of `'a: loop { break 'a }` with a missing `'`. - match (&lhs.kind, &self.token.kind) { - ( - // `foo: ` - ExprKind::Path(None, ast::Path { segments, .. }), - token::Ident(kw::For | kw::Loop | kw::While, IdentIsRaw::No), - ) if let [segment] = segments.as_slice() => { - let snapshot = self.create_snapshot_for_diagnostic(); - let label = Label { - ident: Ident::from_str_and_span( - &format!("'{}", segment.ident), - segment.ident.span, - ), - }; - match self.parse_expr_labeled(label, false) { - Ok(expr) => { - type_err.cancel(); - self.dcx().emit_err(crate::diagnostics::MalformedLoopLabel { - span: label.ident.span, - suggestion: label.ident.span.shrink_to_lo(), - }); - return Ok(expr); - } - Err(err) => { - err.cancel(); - self.restore_snapshot(snapshot); - } - } - } - _ => {} - } - match self.parse_path(PathStyle::Expr) { Ok(path) => { let span_after_type = parser_snapshot_after_type.token.span; From c095a72277a472c874db27add811ca9b7e8ad161 Mon Sep 17 00:00:00 2001 From: increasing Date: Fri, 18 Sep 2026 12:24:24 +0200 Subject: [PATCH 32/38] add test --- .../issues/true-false-type-issue-162947.rs | 16 +++++++ .../true-false-type-issue-162947.stderr | 46 +++++++++++++++++++ 2 files changed, 62 insertions(+) create mode 100644 tests/ui/parser/issues/true-false-type-issue-162947.rs create mode 100644 tests/ui/parser/issues/true-false-type-issue-162947.stderr diff --git a/tests/ui/parser/issues/true-false-type-issue-162947.rs b/tests/ui/parser/issues/true-false-type-issue-162947.rs new file mode 100644 index 0000000000000..ba6c9a13a36fa --- /dev/null +++ b/tests/ui/parser/issues/true-false-type-issue-162947.rs @@ -0,0 +1,16 @@ +struct A; + +impl A { + fn _a() -> true { //~ ERROR: expected type, found keyword `true` + false + } + fn b(&self) {} +} + +fn main() { + let a = A; + a.b(); //~ ERROR E0599 + + let _b: true = true; //~ ERROR: expected type, found keyword `true` + //~^ ERROR E0070 +} diff --git a/tests/ui/parser/issues/true-false-type-issue-162947.stderr b/tests/ui/parser/issues/true-false-type-issue-162947.stderr new file mode 100644 index 0000000000000..f471eb2739d81 --- /dev/null +++ b/tests/ui/parser/issues/true-false-type-issue-162947.stderr @@ -0,0 +1,46 @@ +error: expected type, found keyword `true` + --> $DIR/true-false-type-issue-162947.rs:4:15 + | +LL | impl A { + | - while parsing this item list starting here +LL | fn a() -> true { + | ^^^^ expected type +... +LL | } + | - the item list ends here + +error: expected type, found keyword `true` + --> $DIR/true-false-type-issue-162947.rs:14:12 + | +LL | let b: true = true; + | - ^^^^ expected type + | | + | while parsing the type for `b` + | +help: use `=` if you meant to assign + | +LL - let b: true = true; +LL + let b = true = true; + | + +error[E0599]: no method named `b` found for struct `A` in the current scope + --> $DIR/true-false-type-issue-162947.rs:12:7 + | +LL | struct A; + | -------- method `b` not found for this struct +... +LL | a.b(); + | ^ method not found in `A` + +error[E0070]: invalid left-hand side of assignment + --> $DIR/true-false-type-issue-162947.rs:14:17 + | +LL | let b: true = true; + | ---- ^ + | | + | cannot assign to this expression + +error: aborting due to 4 previous errors + +Some errors have detailed explanations: E0070, E0599. +For more information about an error, try `rustc --explain E0070`. From fe96227afdf72caf064b14b5e3a1f2e9f6cf8c85 Mon Sep 17 00:00:00 2001 From: increasing Date: Fri, 18 Sep 2026 18:09:30 +0200 Subject: [PATCH 33/38] recover true and false as bool type --- compiler/rustc_parse/src/parser/ty.rs | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/compiler/rustc_parse/src/parser/ty.rs b/compiler/rustc_parse/src/parser/ty.rs index 88c07c6e2778c..42fb9121076f4 100644 --- a/compiler/rustc_parse/src/parser/ty.rs +++ b/compiler/rustc_parse/src/parser/ty.rs @@ -3,8 +3,8 @@ use rustc_ast::util::case::Case; use rustc_ast::{ self as ast, BoundAsyncness, BoundConstness, BoundPolarity, DUMMY_NODE_ID, FnPtrTy, FnRetTy, GenericBound, GenericBounds, GenericParam, Generics, Lifetime, MacCall, MutTy, Mutability, - Pinnedness, PolyTraitRef, PreciseCapturingArg, TraitBoundModifiers, TraitObjectSyntax, Ty, - TyKind, UnsafeBinderTy, + Path, Pinnedness, PolyTraitRef, PreciseCapturingArg, TraitBoundModifiers, TraitObjectSyntax, + Ty, TyKind, UnsafeBinderTy, }; use rustc_errors::{Applicability, Diag, E0516, PResult}; use rustc_span::{ErrorGuaranteed, Ident, Span, kw, sym}; @@ -406,7 +406,23 @@ impl<'a> Parser<'a> { let msg = format!("expected type, found {}", super::token_descr(&self.token)); let mut err = self.dcx().struct_span_err(lo, msg); err.span_label(lo, "expected type"); - return Err(err); + if self.may_recover() + && (self.eat_keyword_noexpect(kw::True) || self.eat_keyword_noexpect(kw::False)) + { + err.span_suggestion( + self.prev_token.span, + "the type is called", + "bool", + Applicability::MachineApplicable, + ); + err.emit(); + TyKind::Path( + None, + Path::from_ident(Ident { span: self.prev_token.span, name: sym::bool }), + ) + } else { + return Err(err); + } }; let span = lo.to(self.prev_token.span); From 8026b7beef9fb1b1e8f83efc8cdd7b544bff66d9 Mon Sep 17 00:00:00 2001 From: increasing Date: Fri, 18 Sep 2026 19:35:29 +0200 Subject: [PATCH 34/38] bless tests --- .../issues/true-false-type-issue-162947.fixed | 17 ++++++ .../issues/true-false-type-issue-162947.rs | 5 +- .../true-false-type-issue-162947.stderr | 52 +++++-------------- 3 files changed, 33 insertions(+), 41 deletions(-) create mode 100644 tests/ui/parser/issues/true-false-type-issue-162947.fixed diff --git a/tests/ui/parser/issues/true-false-type-issue-162947.fixed b/tests/ui/parser/issues/true-false-type-issue-162947.fixed new file mode 100644 index 0000000000000..8d3037ea0f458 --- /dev/null +++ b/tests/ui/parser/issues/true-false-type-issue-162947.fixed @@ -0,0 +1,17 @@ +//@ run-rustfix + +struct A; + +impl A { + fn _a() -> bool { //~ ERROR: expected type, found keyword `true` + false + } + fn b(&self) {} +} + +fn main() { + let a = A; + a.b(); + + let _b: bool = true; //~ ERROR: expected type, found keyword `true` +} diff --git a/tests/ui/parser/issues/true-false-type-issue-162947.rs b/tests/ui/parser/issues/true-false-type-issue-162947.rs index ba6c9a13a36fa..1c3a35b3f16f1 100644 --- a/tests/ui/parser/issues/true-false-type-issue-162947.rs +++ b/tests/ui/parser/issues/true-false-type-issue-162947.rs @@ -1,3 +1,5 @@ +//@ run-rustfix + struct A; impl A { @@ -9,8 +11,7 @@ impl A { fn main() { let a = A; - a.b(); //~ ERROR E0599 + a.b(); let _b: true = true; //~ ERROR: expected type, found keyword `true` - //~^ ERROR E0070 } diff --git a/tests/ui/parser/issues/true-false-type-issue-162947.stderr b/tests/ui/parser/issues/true-false-type-issue-162947.stderr index f471eb2739d81..558587d8ebe88 100644 --- a/tests/ui/parser/issues/true-false-type-issue-162947.stderr +++ b/tests/ui/parser/issues/true-false-type-issue-162947.stderr @@ -1,46 +1,20 @@ error: expected type, found keyword `true` - --> $DIR/true-false-type-issue-162947.rs:4:15 + --> $DIR/true-false-type-issue-162947.rs:6:16 | -LL | impl A { - | - while parsing this item list starting here -LL | fn a() -> true { - | ^^^^ expected type -... -LL | } - | - the item list ends here +LL | fn _a() -> true { + | ^^^^ + | | + | expected type + | help: the type is called: `bool` error: expected type, found keyword `true` - --> $DIR/true-false-type-issue-162947.rs:14:12 + --> $DIR/true-false-type-issue-162947.rs:16:13 | -LL | let b: true = true; - | - ^^^^ expected type - | | - | while parsing the type for `b` - | -help: use `=` if you meant to assign - | -LL - let b: true = true; -LL + let b = true = true; - | - -error[E0599]: no method named `b` found for struct `A` in the current scope - --> $DIR/true-false-type-issue-162947.rs:12:7 - | -LL | struct A; - | -------- method `b` not found for this struct -... -LL | a.b(); - | ^ method not found in `A` - -error[E0070]: invalid left-hand side of assignment - --> $DIR/true-false-type-issue-162947.rs:14:17 - | -LL | let b: true = true; - | ---- ^ - | | - | cannot assign to this expression +LL | let _b: true = true; + | ^^^^ + | | + | expected type + | help: the type is called: `bool` -error: aborting due to 4 previous errors +error: aborting due to 2 previous errors -Some errors have detailed explanations: E0070, E0599. -For more information about an error, try `rustc --explain E0070`. From e224f8f81ef41b3e7aa64d9a309365768408af36 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Le=C3=B3n=20Orell=20Valerian=20Liehr?= Date: Sat, 12 Sep 2026 23:01:35 +0200 Subject: [PATCH 35/38] Further simplify `parse_assoc_op_cast` --- compiler/rustc_parse/src/parser/expr.rs | 79 ++++++++++--------------- 1 file changed, 32 insertions(+), 47 deletions(-) diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index 021ed221f057c..c8dfebcedfe87 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -210,9 +210,7 @@ impl<'a> Parser<'a> { let (rhs, span) = finish_parsing_bin_op(self)?; self.mk_expr(span, ExprKind::Assign(lhs, rhs, op.span)) } - AssocOp::Cast => { - self.parse_assoc_op_cast(lhs, lhs_span, op.span, ExprKind::Cast)? - } + AssocOp::Cast => self.parse_assoc_op_cast(lhs, lhs_span, op.span)?, AssocOp::Range(limits) => self.parse_expr_range(min_prec, lhs, limits, op.span)?, }; @@ -555,17 +553,17 @@ impl<'a> Parser<'a> { lhs: Box, lhs_span: Span, op_span: Span, - expr_kind: fn(Box, Box) -> ExprKind, ) -> PResult<'a, Box> { - let mk_expr = |this: &mut Self, lhs: Box, rhs: Box| { - this.mk_expr(this.mk_expr_sp(&lhs, lhs_span, op_span, rhs.span), expr_kind(lhs, rhs)) + let mk_expr = |this: &mut Self, rhs: Box| { + let span = this.mk_expr_sp(&lhs, lhs_span, op_span, rhs.span); + this.mk_expr(span, ExprKind::Cast(lhs, rhs)) }; // Save the state of the parser before parsing type normally, in case there is a // LessThan comparison after this cast. let parser_snapshot_before_type = self.clone(); let cast_expr = match self.parse_as_cast_ty() { - Ok(rhs) => mk_expr(self, lhs, rhs), + Ok(rhs) => mk_expr(self, rhs), Err(type_err) => { if !self.may_recover() { return Err(type_err); @@ -579,11 +577,8 @@ impl<'a> Parser<'a> { match self.parse_path(PathStyle::Expr) { Ok(path) => { let span_after_type = parser_snapshot_after_type.token.span; - let expr = mk_expr( - self, - lhs, - self.mk_ty(path.span, TyKind::Path(None, path.clone())), - ); + let expr = + mk_expr(self, self.mk_ty(path.span, TyKind::Path(None, path.clone()))); let args_span = self.look_ahead(1, |t| t.span).to(span_after_type); match self.token.kind { @@ -642,48 +637,38 @@ impl<'a> Parser<'a> { // written `((&x) as T)[0]`. let span = cast_expr.span; - let with_postfix = self.parse_expr_dot_or_call_with(AttrVec::new(), cast_expr, span)?; // Check if an illegal postfix operator has been added after the cast. // If the resulting expression is not a cast, it is an illegal postfix operator. if !matches!(with_postfix.kind, ExprKind::Cast(_, _)) { - let msg = format!( - "cast cannot be followed by {}", - match with_postfix.kind { - ExprKind::Index(..) => "indexing", - ExprKind::Try(_) => "`?`", - ExprKind::Field(_, _) => "a field access", - ExprKind::MethodCall(_) => "a method call", - ExprKind::Call(_, _) => "a function call", - ExprKind::Await(_, _) => "`.await`", - ExprKind::Use(_, _) => "`.use`", - ExprKind::Yield(YieldKind::Postfix(_)) => "`.yield`", - ExprKind::Match(_, _, MatchKind::Postfix) => "a postfix match", - ExprKind::Err(_) => return Ok(with_postfix), - _ => unreachable!( - "did not expect {:?} as an illegal postfix operator following cast", - with_postfix.kind - ), - } - ); - let mut err = self.dcx().struct_span_err(span, msg); - - let suggest_parens = |err: &mut Diag<'_>| { - let suggestions = vec![ - (span.shrink_to_lo(), "(".to_string()), - (span.shrink_to_hi(), ")".to_string()), - ]; - err.multipart_suggestion( + let kind = match with_postfix.kind { + ExprKind::Index(..) => "indexing", + ExprKind::Try(_) => "`?`", + ExprKind::Field(_, _) => "a field access", + ExprKind::MethodCall(_) => "a method call", + ExprKind::Call(_, _) => "a function call", + ExprKind::Await(_, _) => "`.await`", + ExprKind::Use(_, _) => "`.use`", + ExprKind::Yield(YieldKind::Postfix(_)) => "`.yield`", + ExprKind::Match(_, _, MatchKind::Postfix) => "a postfix match", + ExprKind::Err(_) => return Ok(with_postfix), + _ => unreachable!( + "did not expect {:?} as an illegal postfix operator following cast", + with_postfix.kind + ), + }; + self.dcx() + .struct_span_err(span, format!("cast cannot be followed by {kind}")) + .with_multipart_suggestion( "try surrounding the expression in parentheses", - suggestions, + vec![ + (span.shrink_to_lo(), "(".to_string()), + (span.shrink_to_hi(), ")".to_string()), + ], Applicability::MachineApplicable, - ); - }; - - suggest_parens(&mut err); - - err.emit(); + ) + .emit(); }; Ok(with_postfix) } From 8a0a23db23e5d2c9055b980a0448e8a16562e9ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Le=C3=B3n=20Orell=20Valerian=20Liehr?= Date: Sun, 13 Sep 2026 01:58:27 +0200 Subject: [PATCH 36/38] Trigger "C array" parse error recovery in far fewer cases Previously we would trigger on 1. `unsafe { 1, 2, 3 }` and suggest `[ { 1, 2, 3 ]` (sic!) 2. `'label: { 1, 2, 3 }` and suggest `[: { 1, 2, 3 ]` (sic!) 3. `X::<{ 1, 2, 3 }>` and suggest `X::<[ 1, 2, 3]>` (wrong) 4. `|| -> i32 { 1, 2, 3 }` and suggest `|| -> i32 [ 1, 2, 3 ]` (wrong) 5. `await { 1, 2, 3 }` and suggest `await [ 1, 2, 3 ]` (wrong) Moreover, stop looking for identifiers after the `{` as that case can no longer be reached anyway as `maybe_recover_bad_struct_literal_path` will always snatch it first. --- compiler/rustc_parse/src/parser/expr.rs | 42 ++-------------- .../src/parser/expr/diagnostics.rs | 34 ++++++++++++- .../issue-87830-try-brackets-for-arrays.rs | 32 +++++++++++-- ...issue-87830-try-brackets-for-arrays.stderr | 48 ++++++++++++++----- 4 files changed, 100 insertions(+), 56 deletions(-) diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index 58e98a64b5e41..2adfefe2bcb04 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -1401,6 +1401,9 @@ impl<'a> Parser<'a> { if let Some(expr) = this.maybe_recover_bad_struct_literal_path(false)? { return Ok(expr); } + if let Some(arr) = this.recover_from_c_array(lo) { + return Ok(arr); + } this.parse_expr_block(None, lo, BlockCheckMode::Default) } else if this.check(exp!(Or)) || this.check(exp!(OrOr)) { this.parse_expr_closure().map_err(|mut err| { @@ -2227,39 +2230,6 @@ impl<'a> Parser<'a> { } } - fn is_array_like_block(&mut self) -> bool { - self.token.kind == TokenKind::OpenBrace - && self - .look_ahead(1, |t| matches!(t.kind, TokenKind::Ident(..) | TokenKind::Literal(_))) - && self.look_ahead(2, |t| t == &token::Comma) - && self.look_ahead(3, |t| t.can_begin_expr()) - } - - /// Emits a suggestion if it looks like the user meant an array but - /// accidentally used braces, causing the code to be interpreted as a block - /// expression. - fn maybe_suggest_brackets_instead_of_braces(&mut self, lo: Span) -> Option> { - let mut snapshot = self.create_snapshot_for_diagnostic(); - match snapshot.parse_expr_array_or_repeat(exp!(CloseBrace)) { - Ok(arr) => { - let guar = self.dcx().emit_err(crate::diagnostics::ArrayBracketsInsteadOfBraces { - span: arr.span, - sub: crate::diagnostics::ArrayBracketsInsteadOfBracesSugg { - left: lo, - right: snapshot.prev_token.span, - }, - }); - - self.restore_snapshot(snapshot); - Some(self.mk_expr_err(arr.span, guar)) - } - Err(e) => { - e.cancel(); - None - } - } - } - fn suggest_missing_semicolon_before_array( &self, prev_span: Span, @@ -2309,12 +2279,6 @@ impl<'a> Parser<'a> { lo: Span, blk_mode: BlockCheckMode, ) -> PResult<'a, Box> { - if self.may_recover() && self.is_array_like_block() { - if let Some(arr) = self.maybe_suggest_brackets_instead_of_braces(lo) { - return Ok(arr); - } - } - if self.token.is_metavar_block() { self.dcx().emit_err(crate::diagnostics::InvalidBlockMacroSegment { span: self.token.span, diff --git a/compiler/rustc_parse/src/parser/expr/diagnostics.rs b/compiler/rustc_parse/src/parser/expr/diagnostics.rs index 707ae5d34bc75..6e56ea6c616fd 100644 --- a/compiler/rustc_parse/src/parser/expr/diagnostics.rs +++ b/compiler/rustc_parse/src/parser/expr/diagnostics.rs @@ -3,8 +3,8 @@ use rustc_ast::{BinOpKind, Expr, ExprKind, token}; use rustc_errors::{Applicability, Diag, PResult}; use rustc_span::{Span, Spanned, respan, sym}; -use crate::diagnostics; use crate::parser::Parser; +use crate::{diagnostics, exp}; impl<'a> Parser<'a> { /// Recover from alphabetic logic operators `and` and `or` as found in e.g., Python and PHP. @@ -216,6 +216,38 @@ impl<'a> Parser<'a> { } err } + + /// Recover from array expressions as found in C like `{0, 1, 2, 3}`. + pub(super) fn recover_from_c_array(&mut self, lo: Span) -> Option> { + if !self.may_recover() + || self.token.kind != token::OpenBrace + || self.look_ahead(1, |t| !matches!(t.kind, token::Literal(_))) + || self.look_ahead(2, |t| t != &token::Comma) + || self.look_ahead(3, |t| !t.can_begin_expr()) + { + return None; + } + + let mut snapshot = self.create_snapshot_for_diagnostic(); + match snapshot.parse_expr_array_or_repeat(exp!(CloseBrace)) { + Ok(arr) => { + let guar = self.dcx().emit_err(diagnostics::ArrayBracketsInsteadOfBraces { + span: arr.span, + sub: diagnostics::ArrayBracketsInsteadOfBracesSugg { + left: lo, + right: snapshot.prev_token.span, + }, + }); + + self.restore_snapshot(snapshot); + Some(self.mk_expr_err(arr.span, guar)) + } + Err(e) => { + e.cancel(); + None + } + } + } } #[derive(Copy, Clone)] diff --git a/tests/ui/did_you_mean/issue-87830-try-brackets-for-arrays.rs b/tests/ui/did_you_mean/issue-87830-try-brackets-for-arrays.rs index 070ffaa1eff00..99d63929db484 100644 --- a/tests/ui/did_you_mean/issue-87830-try-brackets-for-arrays.rs +++ b/tests/ui/did_you_mean/issue-87830-try-brackets-for-arrays.rs @@ -1,18 +1,42 @@ +// Test that we can recover from very basic C arrays in the parser & provide a good diagnostic. + fn main() {} -const FOO: [u8; 3] = { +const INTS: [u8; 3] = { //~^ ERROR this is a block expression, not an array 1, 2, 3 }; -const BAR: [&str; 3] = {"one", "two", "three"}; +const STRS: [&str; 3] = {"one", "two", "three"}; //~^ ERROR this is a block expression, not an array -fn foo() { +fn expr_stmt() { {1, 2, 3}; //~^ ERROR this is a block expression, not an array } -fn bar() { +// Don't trigger here. +fn unsafe_block() { + unsafe { 1, 2, 3 } //~ ERROR expected one of +} + +// Don't trigger here. +fn labeled_block() { + 'label: { 1, 2, 3 } //~ ERROR expected one of +} + +// Don't trigger here, this is not a block expression, only a block. +fn fn_body_block() { 1, 2, 3 //~ ERROR expected one of } + +// Don't trigger here, this is not a block expression, only a block. +fn closure_body_block() { + || -> i32 { 1, 2, 3 }; //~ ERROR expected one of +} + +// Don't trigger here. +fn const_arg() { + struct Casket; + Casket::<{ 1, 2, 3 }>; //~ ERROR expected one of +} diff --git a/tests/ui/did_you_mean/issue-87830-try-brackets-for-arrays.stderr b/tests/ui/did_you_mean/issue-87830-try-brackets-for-arrays.stderr index 58232e2307d8e..531d54a9e3f1e 100644 --- a/tests/ui/did_you_mean/issue-87830-try-brackets-for-arrays.stderr +++ b/tests/ui/did_you_mean/issue-87830-try-brackets-for-arrays.stderr @@ -1,8 +1,8 @@ error: this is a block expression, not an array - --> $DIR/issue-87830-try-brackets-for-arrays.rs:3:22 + --> $DIR/issue-87830-try-brackets-for-arrays.rs:5:23 | -LL | const FOO: [u8; 3] = { - | ______________________^ +LL | const INTS: [u8; 3] = { + | _______________________^ LL | | LL | | 1, 2, 3 LL | | }; @@ -10,26 +10,26 @@ LL | | }; | help: to make an array, use square brackets instead of curly braces | -LL ~ const FOO: [u8; 3] = [ +LL ~ const INTS: [u8; 3] = [ LL | LL | 1, 2, 3 LL ~ ]; | error: this is a block expression, not an array - --> $DIR/issue-87830-try-brackets-for-arrays.rs:8:24 + --> $DIR/issue-87830-try-brackets-for-arrays.rs:10:25 | -LL | const BAR: [&str; 3] = {"one", "two", "three"}; - | ^^^^^^^^^^^^^^^^^^^^^^^ +LL | const STRS: [&str; 3] = {"one", "two", "three"}; + | ^^^^^^^^^^^^^^^^^^^^^^^ | help: to make an array, use square brackets instead of curly braces | -LL - const BAR: [&str; 3] = {"one", "two", "three"}; -LL + const BAR: [&str; 3] = ["one", "two", "three"]; +LL - const STRS: [&str; 3] = {"one", "two", "three"}; +LL + const STRS: [&str; 3] = ["one", "two", "three"]; | error: this is a block expression, not an array - --> $DIR/issue-87830-try-brackets-for-arrays.rs:12:5 + --> $DIR/issue-87830-try-brackets-for-arrays.rs:14:5 | LL | {1, 2, 3}; | ^^^^^^^^^ @@ -41,10 +41,34 @@ LL + [1, 2, 3]; | error: expected one of `.`, `;`, `?`, `}`, or an operator, found `,` - --> $DIR/issue-87830-try-brackets-for-arrays.rs:17:6 + --> $DIR/issue-87830-try-brackets-for-arrays.rs:20:15 + | +LL | unsafe { 1, 2, 3 } + | ^ expected one of `.`, `;`, `?`, `}`, or an operator + +error: expected one of `.`, `;`, `?`, `}`, or an operator, found `,` + --> $DIR/issue-87830-try-brackets-for-arrays.rs:25:16 + | +LL | 'label: { 1, 2, 3 } + | ^ expected one of `.`, `;`, `?`, `}`, or an operator + +error: expected one of `.`, `;`, `?`, `}`, or an operator, found `,` + --> $DIR/issue-87830-try-brackets-for-arrays.rs:30:6 | LL | 1, 2, 3 | ^ expected one of `.`, `;`, `?`, `}`, or an operator -error: aborting due to 4 previous errors +error: expected one of `.`, `;`, `?`, `}`, or an operator, found `,` + --> $DIR/issue-87830-try-brackets-for-arrays.rs:35:18 + | +LL | || -> i32 { 1, 2, 3 }; + | ^ expected one of `.`, `;`, `?`, `}`, or an operator + +error: expected one of `.`, `;`, `?`, `}`, or an operator, found `,` + --> $DIR/issue-87830-try-brackets-for-arrays.rs:41:17 + | +LL | Casket::<{ 1, 2, 3 }>; + | ^ expected one of `.`, `;`, `?`, `}`, or an operator + +error: aborting due to 8 previous errors From d80ee2c0e10c037ca527dc6728de23fc05953a11 Mon Sep 17 00:00:00 2001 From: Jason Gerard DeRose Date: Sat, 19 Sep 2026 09:57:41 -0600 Subject: [PATCH 37/38] Add missing seek_read_exact_seek_write_all feature flags in doc examples --- library/std/src/os/windows/fs.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/library/std/src/os/windows/fs.rs b/library/std/src/os/windows/fs.rs index 69975e1d4e232..3e6a934f318b2 100644 --- a/library/std/src/os/windows/fs.rs +++ b/library/std/src/os/windows/fs.rs @@ -81,6 +81,8 @@ pub trait FileExt { /// #[cfg_attr(windows, doc = "```no_run")] #[cfg_attr(not(windows), doc = "```ignore (needs windows)")] + /// #![feature(seek_read_exact_seek_write_all)] + /// /// use std::io; /// use std::fs::File; /// use std::os::windows::prelude::*; @@ -208,6 +210,8 @@ pub trait FileExt { /// #[cfg_attr(windows, doc = "```no_run")] #[cfg_attr(not(windows), doc = "```ignore (needs windows)")] + /// #![feature(seek_read_exact_seek_write_all)] + /// /// use std::fs::File; /// use std::os::windows::prelude::*; /// From 2e15f7b0ea873b1057a98a97294d409cff3bf5f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Mon, 27 Jul 2026 20:43:32 +0000 Subject: [PATCH 38/38] Better account for `Self` that might be a typo of `self` When in a method trying to access `Self` on its own, suggest `self`. When in any assoc fn trying to access `Self()`, suggest `Self { fields }` or using an enum variant. When enum has no variants, mention it. --- compiler/rustc_hir_typeck/src/expr.rs | 1 + .../rustc_hir_typeck/src/fn_ctxt/_impl.rs | 56 ++++++-- compiler/rustc_hir_typeck/src/pat.rs | 11 +- .../invalid-self-constructor-56835.stderr | 8 +- .../self-constructor-type-error-56199.rs | 31 +++++ .../self-constructor-type-error-56199.stderr | 129 +++++++++++++++++- 6 files changed, 213 insertions(+), 23 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs index a8898acf3a415..c995cdee10fe9 100644 --- a/compiler/rustc_hir_typeck/src/expr.rs +++ b/compiler/rustc_hir_typeck/src/expr.rs @@ -617,6 +617,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { call_expr_and_args.map_or(expr.span, |(e, _)| e.span), expr.span, expr.hir_id, + call_expr_and_args.is_some(), ) .0 } diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs index e655e0857d858..ea2e3584b2db7 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs @@ -1006,6 +1006,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { span: Span, path_span: Span, hir_id: HirId, + has_args: bool, ) -> (Ty<'tcx>, Res) { let tcx = self.tcx; @@ -1253,17 +1254,50 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { "the `Self` constructor can only be used with tuple or unit structs", ); if let Some(adt_def) = ty.normalized.ty_adt_def() { - match adt_def.adt_kind() { - AdtKind::Enum => { - err.help("did you mean to use one of the enum's variants?"); - } - AdtKind::Struct | AdtKind::Union => { - err.span_suggestion( - span, - "use curly brackets", - "Self { /* fields */ }", - Applicability::HasPlaceholders, - ); + let def_id = self.body_def_id.to_def_id(); + if !has_args + && let Some(assoc) = tcx.opt_associated_item(def_id) + && assoc.is_method() + { + let self_ty = + tcx.fn_sig(def_id).instantiate_identity().skip_binder().inputs()[0]; + let applicability = if let ty::Adt(..) = self_ty.kind() { + // We're within a method that takes ownership of `Self`, likely a + // builder, so this is most likely a typo. + Applicability::MachineApplicable + } else { + // We still might have meant `self` instead of `Self`. + Applicability::MaybeIncorrect + }; + err.span_suggestion_verbose( + span, + format!( + "you might have meant to refer to the `self` binding of type \ + `{self_ty}`", + ), + "self".to_string(), + applicability, + ); + } else { + match adt_def.adt_kind() { + AdtKind::Enum => { + err.span_help( + tcx.def_span(adt_def.did()), + if adt_def.variants().is_empty() { + "the enum is unconstructable because it has no variants" + } else { + "you might have meant to use one of the enum's variants" + }, + ); + } + AdtKind::Struct | AdtKind::Union => { + err.span_suggestion_verbose( + span, + "use curly brackets", + "Self { /* fields */ }", + Applicability::HasPlaceholders, + ); + } } } } diff --git a/compiler/rustc_hir_typeck/src/pat.rs b/compiler/rustc_hir_typeck/src/pat.rs index caffef6a217a8..ec4483b62fe72 100644 --- a/compiler/rustc_hir_typeck/src/pat.rs +++ b/compiler/rustc_hir_typeck/src/pat.rs @@ -911,7 +911,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { rustc_hir::PatExprKind::Path(qpath) => { let (res, opt_ty, segments) = self.resolve_ty_and_res_fully_qualified_call(qpath, lt.hir_id, lt.span); - self.instantiate_value_path(segments, opt_ty, res, lt.span, lt.span, lt.hir_id).0 + self.instantiate_value_path( + segments, opt_ty, res, lt.span, lt.span, lt.hir_id, false, + ) + .0 } }; self.write_ty(lt.hir_id, ty); @@ -1624,7 +1627,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Find the type of the path pattern, for later checking. let (pat_ty, pat_res) = - self.instantiate_value_path(segments, opt_ty, res, span, span, path_id); + self.instantiate_value_path(segments, opt_ty, res, span, span, path_id, false); Ok(ResolvedPat { ty: pat_ty, kind: ResolvedPatKind::Path { res, pat_res, segments } }) } @@ -1784,8 +1787,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } // Type-check the path. - let (pat_ty, res) = - self.instantiate_value_path(segments, opt_ty, res, pat.span, pat.span, pat.hir_id); + let (pat_ty, res) = self + .instantiate_value_path(segments, opt_ty, res, pat.span, pat.span, pat.hir_id, false); if !pat_ty.is_fn() { return report_unexpected_res(res); } diff --git a/tests/ui/structs/invalid-self-constructor-56835.stderr b/tests/ui/structs/invalid-self-constructor-56835.stderr index 045781ec42bd2..9b25348d87d39 100644 --- a/tests/ui/structs/invalid-self-constructor-56835.stderr +++ b/tests/ui/structs/invalid-self-constructor-56835.stderr @@ -2,7 +2,13 @@ error: the `Self` constructor can only be used with tuple or unit structs --> $DIR/invalid-self-constructor-56835.rs:5:12 | LL | fn bar(Self(foo): Self) {} - | ^^^^^^^^^ help: use curly brackets: `Self { /* fields */ }` + | ^^^^^^^^^ + | +help: use curly brackets + | +LL - fn bar(Self(foo): Self) {} +LL + fn bar(Self { /* fields */ }: Self) {} + | error[E0164]: expected tuple struct or tuple variant, found self constructor `Self` --> $DIR/invalid-self-constructor-56835.rs:5:12 diff --git a/tests/ui/typeck/self-constructor-type-error-56199.rs b/tests/ui/typeck/self-constructor-type-error-56199.rs index b08d69189807a..34af8bed25c47 100644 --- a/tests/ui/typeck/self-constructor-type-error-56199.rs +++ b/tests/ui/typeck/self-constructor-type-error-56199.rs @@ -1,5 +1,8 @@ // https://github.com/rust-lang/rust/issues/56199 enum Foo {} +enum Lab { + Qux, +} struct Bar {} impl Foo { @@ -9,6 +12,12 @@ impl Foo { let _ = Self(); //~^ ERROR the `Self` constructor can only be used with tuple or unit structs } + fn foo_method(self) { + let _ = Self; + //~^ ERROR the `Self` constructor can only be used with tuple or unit structs + let _ = Self(); + //~^ ERROR the `Self` constructor can only be used with tuple or unit structs + } } impl Bar { @@ -18,6 +27,28 @@ impl Bar { let _ = Self(); //~^ ERROR the `Self` constructor can only be used with tuple or unit structs } + fn bar_method(self) { + let _ = Self; + //~^ ERROR the `Self` constructor can only be used with tuple or unit structs + let _ = Self(); + //~^ ERROR the `Self` constructor can only be used with tuple or unit structs + } +} + +impl Lab { + fn lab() { + let _ = Self; + //~^ ERROR the `Self` constructor can only be used with tuple or unit structs + let _ = Self(); + //~^ ERROR the `Self` constructor can only be used with tuple or unit structs + } + fn lab_method(self) { + let _ = Self; + //~^ ERROR the `Self` constructor can only be used with tuple or unit structs + let _ = Self(); + //~^ ERROR the `Self` constructor can only be used with tuple or unit structs + } } + fn main() {} diff --git a/tests/ui/typeck/self-constructor-type-error-56199.stderr b/tests/ui/typeck/self-constructor-type-error-56199.stderr index 6e9d0fcd90c05..d0d124c6f3149 100644 --- a/tests/ui/typeck/self-constructor-type-error-56199.stderr +++ b/tests/ui/typeck/self-constructor-type-error-56199.stderr @@ -1,30 +1,145 @@ error: the `Self` constructor can only be used with tuple or unit structs - --> $DIR/self-constructor-type-error-56199.rs:7:17 + --> $DIR/self-constructor-type-error-56199.rs:10:17 | LL | let _ = Self; | ^^^^ | - = help: did you mean to use one of the enum's variants? +help: the enum is unconstructable because it has no variants + --> $DIR/self-constructor-type-error-56199.rs:2:1 + | +LL | enum Foo {} + | ^^^^^^^^ error: the `Self` constructor can only be used with tuple or unit structs - --> $DIR/self-constructor-type-error-56199.rs:9:17 + --> $DIR/self-constructor-type-error-56199.rs:12:17 | LL | let _ = Self(); | ^^^^^^ | - = help: did you mean to use one of the enum's variants? +help: the enum is unconstructable because it has no variants + --> $DIR/self-constructor-type-error-56199.rs:2:1 + | +LL | enum Foo {} + | ^^^^^^^^ error: the `Self` constructor can only be used with tuple or unit structs --> $DIR/self-constructor-type-error-56199.rs:16:17 | LL | let _ = Self; - | ^^^^ help: use curly brackets: `Self { /* fields */ }` + | ^^^^ + | +help: you might have meant to refer to the `self` binding of type `Foo` (notice the capitalization) + | +LL - let _ = Self; +LL + let _ = self; + | error: the `Self` constructor can only be used with tuple or unit structs --> $DIR/self-constructor-type-error-56199.rs:18:17 | LL | let _ = Self(); - | ^^^^^^ help: use curly brackets: `Self { /* fields */ }` + | ^^^^^^ + | +help: the enum is unconstructable because it has no variants + --> $DIR/self-constructor-type-error-56199.rs:2:1 + | +LL | enum Foo {} + | ^^^^^^^^ + +error: the `Self` constructor can only be used with tuple or unit structs + --> $DIR/self-constructor-type-error-56199.rs:25:17 + | +LL | let _ = Self; + | ^^^^ + | +help: use curly brackets + | +LL | let _ = Self { /* fields */ }; + | ++++++++++++++++ + +error: the `Self` constructor can only be used with tuple or unit structs + --> $DIR/self-constructor-type-error-56199.rs:27:17 + | +LL | let _ = Self(); + | ^^^^^^ + | +help: use curly brackets + | +LL - let _ = Self(); +LL + let _ = Self { /* fields */ }; + | + +error: the `Self` constructor can only be used with tuple or unit structs + --> $DIR/self-constructor-type-error-56199.rs:31:17 + | +LL | let _ = Self; + | ^^^^ + | +help: you might have meant to refer to the `self` binding of type `Bar` (notice the capitalization) + | +LL - let _ = Self; +LL + let _ = self; + | + +error: the `Self` constructor can only be used with tuple or unit structs + --> $DIR/self-constructor-type-error-56199.rs:33:17 + | +LL | let _ = Self(); + | ^^^^^^ + | +help: use curly brackets + | +LL - let _ = Self(); +LL + let _ = Self { /* fields */ }; + | + +error: the `Self` constructor can only be used with tuple or unit structs + --> $DIR/self-constructor-type-error-56199.rs:40:17 + | +LL | let _ = Self; + | ^^^^ + | +help: you might have meant to use one of the enum's variants + --> $DIR/self-constructor-type-error-56199.rs:3:1 + | +LL | enum Lab { + | ^^^^^^^^ + +error: the `Self` constructor can only be used with tuple or unit structs + --> $DIR/self-constructor-type-error-56199.rs:42:17 + | +LL | let _ = Self(); + | ^^^^^^ + | +help: you might have meant to use one of the enum's variants + --> $DIR/self-constructor-type-error-56199.rs:3:1 + | +LL | enum Lab { + | ^^^^^^^^ + +error: the `Self` constructor can only be used with tuple or unit structs + --> $DIR/self-constructor-type-error-56199.rs:46:17 + | +LL | let _ = Self; + | ^^^^ + | +help: you might have meant to refer to the `self` binding of type `Lab` (notice the capitalization) + | +LL - let _ = Self; +LL + let _ = self; + | + +error: the `Self` constructor can only be used with tuple or unit structs + --> $DIR/self-constructor-type-error-56199.rs:48:17 + | +LL | let _ = Self(); + | ^^^^^^ + | +help: you might have meant to use one of the enum's variants + --> $DIR/self-constructor-type-error-56199.rs:3:1 + | +LL | enum Lab { + | ^^^^^^^^ -error: aborting due to 4 previous errors +error: aborting due to 12 previous errors