From e79fef40866bfbf2a4ae33b0030205ce51a7adf9 Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Thu, 1 Jan 2026 22:21:57 +0800 Subject: [PATCH 001/103] Fix incorrect Self path expand for inline_call Example --- ```rust trait Trait { fn f() -> Self; } struct Foo<'a>(&'a ()); impl<'a> Trait for Foo<'a> { fn f() -> Self { Self(&()) } } impl Foo<'_> { fn new() -> Self { Self::$0f() } } ``` **Before this PR** ```rust trait Trait { fn f() -> Self; } struct Foo<'a>(&'a ()); impl<'a> Trait for Foo<'a> { fn f() -> Self { Self(&()) } } impl Foo<'_> { fn new() -> Self { Foo<'a>(&()) } } ``` **After this PR** ```rust trait Trait { fn f() -> Self; } struct Foo<'a>(&'a ()); impl<'a> Trait for Foo<'a> { fn f() -> Self { Self(&()) } } impl Foo<'_> { fn new() -> Self { Foo(&()) } } ``` --- .../ide-assists/src/handlers/inline_call.rs | 52 +++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_call.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_call.rs index fa4f2a78c8b7f..21f2249a19c9d 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_call.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_call.rs @@ -403,6 +403,12 @@ fn inline( .find(|tok| tok.kind() == SyntaxKind::SELF_TYPE_KW) { let replace_with = t.clone_subtree().syntax().clone_for_update(); + if !is_in_type_path(&self_tok) + && let Some(ty) = ast::Type::cast(replace_with.clone()) + && let Some(generic_arg_list) = ty.generic_arg_list() + { + ted::remove(generic_arg_list.syntax()); + } ted::replace(self_tok, replace_with); } } @@ -588,6 +594,17 @@ fn inline( } } +fn is_in_type_path(self_tok: &syntax::SyntaxToken) -> bool { + self_tok + .parent_ancestors() + .skip_while(|it| !ast::Path::can_cast(it.kind())) + .map_while(ast::Path::cast) + .last() + .and_then(|it| it.syntax().parent()) + .and_then(ast::PathType::cast) + .is_some() +} + fn path_expr_as_record_field(usage: &PathExpr) -> Option { let path = usage.path()?; let name_ref = path.as_single_name_ref()?; @@ -1694,6 +1711,41 @@ fn main() { ) } + #[test] + fn inline_trait_method_call_with_lifetimes() { + check_assist( + inline_call, + r#" +trait Trait { + fn f() -> Self; +} +struct Foo<'a>(&'a ()); +impl<'a> Trait for Foo<'a> { + fn f() -> Self { Self(&()) } +} +impl Foo<'_> { + fn new() -> Self { + Self::$0f() + } +} +"#, + r#" +trait Trait { + fn f() -> Self; +} +struct Foo<'a>(&'a ()); +impl<'a> Trait for Foo<'a> { + fn f() -> Self { Self(&()) } +} +impl Foo<'_> { + fn new() -> Self { + Foo(&()) + } +} +"#, + ) + } + #[test] fn method_by_reborrow() { check_assist( From f091a8acad5c52378be2f1b6f29cc2d21ff7bf5f Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Sun, 4 Jan 2026 20:54:19 +0800 Subject: [PATCH 002/103] Fix complete semicolon in array expression Example --- ```rust fn foo() {} fn bar() { let _ = [fo$0]; } ``` **Before this PR** ```rust fn foo() {} fn bar() { let _ = [foo();$0]; } ``` **After this PR** ```rust fn foo() {} fn bar() { let _ = [foo()$0]; } ``` --- .../crates/ide-completion/src/context.rs | 5 ++++- .../ide-completion/src/render/function.rs | 19 +++++++++++++++++++ 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/context.rs b/src/tools/rust-analyzer/crates/ide-completion/src/context.rs index d116f665adbdd..7f3e69c8ab30b 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/context.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/context.rs @@ -821,7 +821,10 @@ impl<'db> CompletionContext<'db> { CompleteSemicolon::DoNotComplete } else if let Some(term_node) = sema.token_ancestors_with_macros(token.clone()).find(|node| { - matches!(node.kind(), BLOCK_EXPR | MATCH_ARM | CLOSURE_EXPR | ARG_LIST | PAREN_EXPR) + matches!( + node.kind(), + BLOCK_EXPR | MATCH_ARM | CLOSURE_EXPR | ARG_LIST | PAREN_EXPR | ARRAY_EXPR + ) }) { let next_token = iter::successors(token.next_token(), |it| it.next_token()) diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render/function.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render/function.rs index 4713b1f1afa77..475e00dfcf29b 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/render/function.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/render/function.rs @@ -905,6 +905,25 @@ fn baz(_: impl FnOnce()) {} fn bar() { baz(foo()$0); } +"#, + ); + } + + #[test] + fn no_semicolon_in_array() { + check_edit( + r#"foo"#, + r#" +fn foo() {} +fn bar() { + let _ = [fo$0]; +} +"#, + r#" +fn foo() {} +fn bar() { + let _ = [foo()$0]; +} "#, ); } From d6362e6d0fa95ec0d6d5bdcd2b81516bf9e3a4b6 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Wed, 17 Dec 2025 14:00:59 +0100 Subject: [PATCH 003/103] document guidelines for which shims have a place in Miri --- src/tools/miri/CONTRIBUTING.md | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/tools/miri/CONTRIBUTING.md b/src/tools/miri/CONTRIBUTING.md index 1995300c5bcbb..f9cb60c66d916 100644 --- a/src/tools/miri/CONTRIBUTING.md +++ b/src/tools/miri/CONTRIBUTING.md @@ -66,6 +66,23 @@ process for such contributions: This process is largely informal, and its primary goal is to more clearly communicate expectations. Please get in touch with us if you have any questions! +## Scope of Miri shims + +Miri has "shims" to implement functionality that is usually implemented in C libraries which are +invoked from Rust code, such as opening files or spawning threads, as well as for +CPU-vendor-provided SIMD intrinsics. However, the set of C functions that Rust code invokes this way +is enormous, and for obvious reasons we have no intention of implementing every C API ever written +in Miri. + +At the moment, the general guideline for "could this function have a shim in Miri" is: we will +generally only add shims for functions that can be implemented in a portable way using just what is +provided by the Rust standard library. The function should also be reasonably widely-used in Rust +code to justify the review and maintenance effort (i.e. the easier the function is to implement, the +lower the barrier). Other than that, we might make exceptions for certain cases if (a) there is a +good case for why Miri should support those APIs, and (b) robust and widely-used portable libraries +exist in the Rust ecosystem. We will generally not add shims to Miri that would require Miri to +directly interact with platform-specific APIs (such as `libc` or `windows-sys`). + ## Preparing the build environment Miri heavily relies on internal and unstable rustc interfaces to execute MIR, From 0e6d6033b5893cb3d38f96915eb35a4e1b211e7f Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Thu, 22 Jan 2026 19:04:36 +0530 Subject: [PATCH 004/103] migrate introduce_named_lifetime to SyntaxEditor --- .../src/handlers/introduce_named_lifetime.rs | 210 ++++++++++++------ 1 file changed, 141 insertions(+), 69 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/introduce_named_lifetime.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/introduce_named_lifetime.rs index 264e3767a2324..76d47b9276b1a 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/introduce_named_lifetime.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/introduce_named_lifetime.rs @@ -1,11 +1,11 @@ -use ide_db::FxHashSet; +use ide_db::{FileId, FxHashSet}; use syntax::{ - AstNode, TextRange, - ast::{self, HasGenericParams, edit_in_place::GenericParamsOwnerEdit, make}, - ted::{self, Position}, + AstNode, T, TextRange, + ast::{self, HasGenericParams, HasName, syntax_factory::SyntaxFactory}, + syntax_editor::{Element, Position, SyntaxEditor}, }; -use crate::{AssistContext, AssistId, Assists, assist_context::SourceChangeBuilder}; +use crate::{AssistContext, AssistId, Assists}; static ASSIST_NAME: &str = "introduce_named_lifetime"; static ASSIST_LABEL: &str = "Introduce named lifetime"; @@ -38,100 +38,110 @@ pub(crate) fn introduce_named_lifetime(acc: &mut Assists, ctx: &AssistContext<'_ // FIXME: should also add support for the case fun(f: &Foo) -> &$0Foo let lifetime = ctx.find_node_at_offset::().filter(|lifetime| lifetime.text() == "'_")?; + let file_id = ctx.vfs_file_id(); let lifetime_loc = lifetime.lifetime_ident_token()?.text_range(); if let Some(fn_def) = lifetime.syntax().ancestors().find_map(ast::Fn::cast) { - generate_fn_def_assist(acc, fn_def, lifetime_loc, lifetime) + generate_fn_def_assist(acc, fn_def, lifetime_loc, lifetime, file_id) } else if let Some(impl_def) = lifetime.syntax().ancestors().find_map(ast::Impl::cast) { - generate_impl_def_assist(acc, impl_def, lifetime_loc, lifetime) + generate_impl_def_assist(acc, impl_def, lifetime_loc, lifetime, file_id) } else { None } } -/// Generate the assist for the fn def case +/// Given a type parameter list, generate a unique lifetime parameter name +/// which is not in the list +fn generate_unique_lifetime_param_name( + existing_type_param_list: Option, +) -> Option { + match existing_type_param_list { + Some(type_params) => { + let used_lifetime_params: FxHashSet<_> = + type_params.lifetime_params().map(|p| p.syntax().text().to_string()).collect(); + ('a'..='z').map(|it| format!("'{it}")).find(|it| !used_lifetime_params.contains(it)) + } + None => Some("'a".to_owned()), + } +} + fn generate_fn_def_assist( acc: &mut Assists, fn_def: ast::Fn, lifetime_loc: TextRange, lifetime: ast::Lifetime, + file_id: FileId, ) -> Option<()> { - let param_list: ast::ParamList = fn_def.param_list()?; - let new_lifetime_param = generate_unique_lifetime_param_name(fn_def.generic_param_list())?; + let param_list = fn_def.param_list()?; + let new_lifetime_name = generate_unique_lifetime_param_name(fn_def.generic_param_list())?; let self_param = - // use the self if it's a reference and has no explicit lifetime param_list.self_param().filter(|p| p.lifetime().is_none() && p.amp_token().is_some()); - // compute the location which implicitly has the same lifetime as the anonymous lifetime + let loc_needing_lifetime = if let Some(self_param) = self_param { - // if we have a self reference, use that Some(NeedsLifetime::SelfParam(self_param)) } else { - // otherwise, if there's a single reference parameter without a named lifetime, use that - let fn_params_without_lifetime: Vec<_> = param_list + let unnamed_refs: Vec<_> = param_list .params() .filter_map(|param| match param.ty() { - Some(ast::Type::RefType(ascribed_type)) if ascribed_type.lifetime().is_none() => { - Some(NeedsLifetime::RefType(ascribed_type)) + Some(ast::Type::RefType(ref_type)) if ref_type.lifetime().is_none() => { + Some(NeedsLifetime::RefType(ref_type)) } _ => None, }) .collect(); - match fn_params_without_lifetime.len() { - 1 => Some(fn_params_without_lifetime.into_iter().next()?), + + match unnamed_refs.len() { + 1 => Some(unnamed_refs.into_iter().next()?), 0 => None, - // multiple unnamed is invalid. assist is not applicable _ => return None, } }; - acc.add(AssistId::refactor(ASSIST_NAME), ASSIST_LABEL, lifetime_loc, |builder| { - let fn_def = builder.make_mut(fn_def); - let lifetime = builder.make_mut(lifetime); - let loc_needing_lifetime = - loc_needing_lifetime.and_then(|it| it.make_mut(builder).to_position()); - - fn_def.get_or_create_generic_param_list().add_generic_param( - make::lifetime_param(new_lifetime_param.clone()).clone_for_update().into(), - ); - ted::replace(lifetime.syntax(), new_lifetime_param.clone_for_update().syntax()); - if let Some(position) = loc_needing_lifetime { - ted::insert(position, new_lifetime_param.clone_for_update().syntax()); + + acc.add(AssistId::refactor(ASSIST_NAME), ASSIST_LABEL, lifetime_loc, |edit| { + let root = fn_def.syntax().ancestors().last().unwrap().clone(); + let mut editor = SyntaxEditor::new(root); + let factory = SyntaxFactory::with_mappings(); + + if let Some(generic_list) = fn_def.generic_param_list() { + insert_lifetime_param(&mut editor, &factory, &generic_list, &new_lifetime_name); + } else { + insert_new_generic_param_list_fn(&mut editor, &factory, &fn_def, &new_lifetime_name); } + + editor.replace(lifetime.syntax(), factory.lifetime(&new_lifetime_name).syntax()); + + if let Some(pos) = loc_needing_lifetime.and_then(|l| l.to_position()) { + editor.insert_all( + pos, + vec![ + factory.lifetime(&new_lifetime_name).syntax().clone().into(), + factory.whitespace(" ").into(), + ], + ); + } + + edit.add_file_edits(file_id, editor); }) } -/// Generate the assist for the impl def case -fn generate_impl_def_assist( - acc: &mut Assists, - impl_def: ast::Impl, - lifetime_loc: TextRange, - lifetime: ast::Lifetime, +fn insert_new_generic_param_list_fn( + editor: &mut SyntaxEditor, + factory: &SyntaxFactory, + fn_def: &ast::Fn, + lifetime_name: &str, ) -> Option<()> { - let new_lifetime_param = generate_unique_lifetime_param_name(impl_def.generic_param_list())?; - acc.add(AssistId::refactor(ASSIST_NAME), ASSIST_LABEL, lifetime_loc, |builder| { - let impl_def = builder.make_mut(impl_def); - let lifetime = builder.make_mut(lifetime); + let name = fn_def.name()?; - impl_def.get_or_create_generic_param_list().add_generic_param( - make::lifetime_param(new_lifetime_param.clone()).clone_for_update().into(), - ); - ted::replace(lifetime.syntax(), new_lifetime_param.clone_for_update().syntax()); - }) -} + editor.insert_all( + Position::after(name.syntax()), + vec![ + factory.token(T![<]).syntax_element(), + factory.lifetime(lifetime_name).syntax().syntax_element(), + factory.token(T![>]).syntax_element(), + ], + ); -/// Given a type parameter list, generate a unique lifetime parameter name -/// which is not in the list -fn generate_unique_lifetime_param_name( - existing_type_param_list: Option, -) -> Option { - match existing_type_param_list { - Some(type_params) => { - let used_lifetime_params: FxHashSet<_> = - type_params.lifetime_params().map(|p| p.syntax().text().to_string()).collect(); - ('a'..='z').map(|it| format!("'{it}")).find(|it| !used_lifetime_params.contains(it)) - } - None => Some("'a".to_owned()), - } - .map(|it| make::lifetime(&it)) + Some(()) } enum NeedsLifetime { @@ -140,13 +150,6 @@ enum NeedsLifetime { } impl NeedsLifetime { - fn make_mut(self, builder: &mut SourceChangeBuilder) -> Self { - match self { - Self::SelfParam(it) => Self::SelfParam(builder.make_mut(it)), - Self::RefType(it) => Self::RefType(builder.make_mut(it)), - } - } - fn to_position(self) -> Option { match self { Self::SelfParam(it) => Some(Position::after(it.amp_token()?)), @@ -155,6 +158,75 @@ impl NeedsLifetime { } } +fn generate_impl_def_assist( + acc: &mut Assists, + impl_def: ast::Impl, + lifetime_loc: TextRange, + lifetime: ast::Lifetime, + file_id: FileId, +) -> Option<()> { + let new_lifetime_name = generate_unique_lifetime_param_name(impl_def.generic_param_list())?; + + acc.add(AssistId::refactor(ASSIST_NAME), ASSIST_LABEL, lifetime_loc, |edit| { + let root = impl_def.syntax().ancestors().last().unwrap().clone(); + let mut editor = SyntaxEditor::new(root); + let factory = SyntaxFactory::without_mappings(); + + if let Some(generic_list) = impl_def.generic_param_list() { + insert_lifetime_param(&mut editor, &factory, &generic_list, &new_lifetime_name); + } else { + insert_new_generic_param_list_imp(&mut editor, &factory, &impl_def, &new_lifetime_name); + } + + editor.replace(lifetime.syntax(), factory.lifetime(&new_lifetime_name).syntax()); + + edit.add_file_edits(file_id, editor); + }) +} + +fn insert_new_generic_param_list_imp( + editor: &mut SyntaxEditor, + factory: &SyntaxFactory, + impl_: &ast::Impl, + lifetime_name: &str, +) -> Option<()> { + let impl_kw = impl_.impl_token()?; + + editor.insert_all( + Position::after(impl_kw), + vec![ + factory.token(T![<]).syntax_element(), + factory.lifetime(lifetime_name).syntax().syntax_element(), + factory.token(T![>]).syntax_element(), + ], + ); + + Some(()) +} + +fn insert_lifetime_param( + editor: &mut SyntaxEditor, + factory: &SyntaxFactory, + generic_list: &ast::GenericParamList, + lifetime_name: &str, +) -> Option<()> { + let r_angle = generic_list.r_angle_token()?; + let needs_comma = generic_list.generic_params().next().is_some(); + + let mut elements = Vec::new(); + + if needs_comma { + elements.push(factory.token(T![,]).syntax_element()); + elements.push(factory.whitespace(" ").syntax_element()); + } + + let lifetime = factory.lifetime(lifetime_name); + elements.push(lifetime.syntax().clone().into()); + + editor.insert_all(Position::before(r_angle), elements); + Some(()) +} + #[cfg(test)] mod tests { use super::*; From ed446599737fa64c2d0a16ceb49fc06fe4e46ec8 Mon Sep 17 00:00:00 2001 From: The Miri Cronjob Bot Date: Sun, 25 Jan 2026 05:05:35 +0000 Subject: [PATCH 005/103] Prepare for merging from rust-lang/rust This updates the rust-version file to 5a07626f4b8802d2baa260b76b2e1d0714674efe. --- src/tools/miri/rust-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/miri/rust-version b/src/tools/miri/rust-version index 90ba52120ee87..fa15ee59b2413 100644 --- a/src/tools/miri/rust-version +++ b/src/tools/miri/rust-version @@ -1 +1 @@ -d10ac47c20152feb5e99b1c35a2e6830f77c66dc +5a07626f4b8802d2baa260b76b2e1d0714674efe From 20c31e0d00acf22635692fbd9d048925ae1bf635 Mon Sep 17 00:00:00 2001 From: The Miri Cronjob Bot Date: Mon, 26 Jan 2026 05:07:09 +0000 Subject: [PATCH 006/103] Prepare for merging from rust-lang/rust This updates the rust-version file to 873d4682c7d285540b8f28bfe637006cef8918a6. --- src/tools/miri/rust-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/miri/rust-version b/src/tools/miri/rust-version index fa15ee59b2413..ccc0b55d4dc55 100644 --- a/src/tools/miri/rust-version +++ b/src/tools/miri/rust-version @@ -1 +1 @@ -5a07626f4b8802d2baa260b76b2e1d0714674efe +873d4682c7d285540b8f28bfe637006cef8918a6 From db8f27619aa5b70e658c8b7906c8688f771ed59e Mon Sep 17 00:00:00 2001 From: The Miri Cronjob Bot Date: Wed, 28 Jan 2026 05:03:07 +0000 Subject: [PATCH 007/103] Prepare for merging from rust-lang/rust This updates the rust-version file to e96bb7e44fbcc23c1e6009e8d0ee8ab208668fb4. --- src/tools/miri/rust-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/miri/rust-version b/src/tools/miri/rust-version index ccc0b55d4dc55..cc1ff32815cd5 100644 --- a/src/tools/miri/rust-version +++ b/src/tools/miri/rust-version @@ -1 +1 @@ -873d4682c7d285540b8f28bfe637006cef8918a6 +e96bb7e44fbcc23c1e6009e8d0ee8ab208668fb4 From 71294125eaafd54f3bf28ec643ac8f232e665e3f Mon Sep 17 00:00:00 2001 From: joboet Date: Wed, 28 Jan 2026 10:39:10 +0100 Subject: [PATCH 008/103] add a bug to the trophy shelf --- src/tools/miri/README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/src/tools/miri/README.md b/src/tools/miri/README.md index 925b85f58766c..5153520bd43db 100644 --- a/src/tools/miri/README.md +++ b/src/tools/miri/README.md @@ -626,6 +626,7 @@ Definite bugs found: * [`ReentrantLock` not correctly dealing with reuse of addresses for TLS storage of different threads](https://github.com/rust-lang/rust/pull/141248) * [Rare Deadlock in the thread (un)parking example code](https://github.com/rust-lang/rust/issues/145816) * [`winit` registering a global constructor with the wrong ABI on Windows](https://github.com/rust-windowing/winit/issues/4435) +* [`VecDeque::splice` confusing physical and logical indices](https://github.com/rust-lang/rust/issues/151758) Violations of [Stacked Borrows] found that are likely bugs (but Stacked Borrows is currently just an experiment): From f67f5d910f5517a1ffa104136b56d43387c0653d Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Wed, 28 Jan 2026 11:23:55 +0100 Subject: [PATCH 009/103] bless android tests --- .../tests/fail-dep/libc/prctl-get-name-buffer-too-small.stderr | 1 - 1 file changed, 1 deletion(-) diff --git a/src/tools/miri/tests/fail-dep/libc/prctl-get-name-buffer-too-small.stderr b/src/tools/miri/tests/fail-dep/libc/prctl-get-name-buffer-too-small.stderr index cc50564a43f5a..09f1a3682da5d 100644 --- a/src/tools/miri/tests/fail-dep/libc/prctl-get-name-buffer-too-small.stderr +++ b/src/tools/miri/tests/fail-dep/libc/prctl-get-name-buffer-too-small.stderr @@ -11,7 +11,6 @@ help: ALLOC was allocated here: | LL | let mut buf = vec![0u8; 15]; | ^^^^^^^^^^^^^ - = note: this error originates in the macro `vec` (in Nightly builds, run with -Z macro-backtrace for more info) note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace From ce7c03d96de4eb0ff4ef4f7227f79ac52bd84112 Mon Sep 17 00:00:00 2001 From: The Miri Cronjob Bot Date: Fri, 30 Jan 2026 05:13:08 +0000 Subject: [PATCH 010/103] Prepare for merging from rust-lang/rust This updates the rust-version file to 35a31ba763976907cd38ba88743a3aefbaf8ffff. --- src/tools/miri/rust-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/miri/rust-version b/src/tools/miri/rust-version index cc1ff32815cd5..3e85143462188 100644 --- a/src/tools/miri/rust-version +++ b/src/tools/miri/rust-version @@ -1 +1 @@ -e96bb7e44fbcc23c1e6009e8d0ee8ab208668fb4 +35a31ba763976907cd38ba88743a3aefbaf8ffff From 455172fd0e3446127152506fc8cb8969e3b18a7f Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 31 Jan 2026 11:06:30 +0100 Subject: [PATCH 011/103] trophy case: oneshot data race --- src/tools/miri/README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/src/tools/miri/README.md b/src/tools/miri/README.md index 5153520bd43db..f254eb357a454 100644 --- a/src/tools/miri/README.md +++ b/src/tools/miri/README.md @@ -627,6 +627,7 @@ Definite bugs found: * [Rare Deadlock in the thread (un)parking example code](https://github.com/rust-lang/rust/issues/145816) * [`winit` registering a global constructor with the wrong ABI on Windows](https://github.com/rust-windowing/winit/issues/4435) * [`VecDeque::splice` confusing physical and logical indices](https://github.com/rust-lang/rust/issues/151758) +* [Data race in `oneshot` channel](https://github.com/faern/oneshot/issues/69) Violations of [Stacked Borrows] found that are likely bugs (but Stacked Borrows is currently just an experiment): From 1af122276e123b4f0ac747ddfe478ef667f9de05 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sun, 1 Feb 2026 12:44:13 +0100 Subject: [PATCH 012/103] Prepare for merging from rust-lang/rust This updates the rust-version file to 878374e07f3bf038c96e94e5bc917471878e4bf6. --- src/tools/miri/rust-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/miri/rust-version b/src/tools/miri/rust-version index 3e85143462188..d7d94190c98b4 100644 --- a/src/tools/miri/rust-version +++ b/src/tools/miri/rust-version @@ -1 +1 @@ -35a31ba763976907cd38ba88743a3aefbaf8ffff +878374e07f3bf038c96e94e5bc917471878e4bf6 From 070102bb1bc628f1b380db80f3e92c382259fb94 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sun, 1 Feb 2026 12:56:35 +0100 Subject: [PATCH 013/103] fix building sysroot for JSON targets --- src/tools/miri/cargo-miri/src/setup.rs | 2 ++ src/tools/miri/ci/ci.sh | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/tools/miri/cargo-miri/src/setup.rs b/src/tools/miri/cargo-miri/src/setup.rs index c7682093663ed..47c99f939254e 100644 --- a/src/tools/miri/cargo-miri/src/setup.rs +++ b/src/tools/miri/cargo-miri/src/setup.rs @@ -88,6 +88,8 @@ pub fn setup( }; let cargo_cmd = { let mut command = cargo(); + // Allow JSON targets since users do not have a good way to set this flag otherwise. + command.arg("-Zjson-target-spec"); // Use Miri as rustc to build a libstd compatible with us (and use the right flags). // We set ourselves (`cargo-miri`) instead of Miri directly to be able to patch the flags // for `libpanic_abort` (usually this is done by bootstrap but we have to do it ourselves). diff --git a/src/tools/miri/ci/ci.sh b/src/tools/miri/ci/ci.sh index c8e359cf23851..0d3e70a163be4 100755 --- a/src/tools/miri/ci/ci.sh +++ b/src/tools/miri/ci/ci.sh @@ -129,7 +129,7 @@ function run_tests_minimal { time ./miri test $TARGET_FLAG "$@" # Ensure that a small smoke test of cargo-miri works. - time cargo miri run --manifest-path test-cargo-miri/no-std-smoke/Cargo.toml $TARGET_FLAG + time cargo miri run --manifest-path test-cargo-miri/no-std-smoke/Cargo.toml -Zjson-target-spec $TARGET_FLAG endgroup } From c89a88ed1bb18ce0df23782c6484be3ab83cdf91 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Mon, 2 Feb 2026 19:31:40 +0100 Subject: [PATCH 014/103] do not run miri for the dependency build and reset MIRIFLAGS as well just to be safe --- src/tools/miri/cargo-miri/src/phases.rs | 3 +++ src/tools/miri/tests/deps/src/main.rs | 4 +++- src/tools/miri/tests/ui.rs | 13 +++++++++---- 3 files changed, 15 insertions(+), 5 deletions(-) diff --git a/src/tools/miri/cargo-miri/src/phases.rs b/src/tools/miri/cargo-miri/src/phases.rs index 0f04397b72d22..0babccb40d33c 100644 --- a/src/tools/miri/cargo-miri/src/phases.rs +++ b/src/tools/miri/cargo-miri/src/phases.rs @@ -63,6 +63,9 @@ pub fn phase_cargo_miri(mut args: impl Iterator) { "setup" => MiriCommand::Setup, "test" | "t" | "run" | "r" | "nextest" => MiriCommand::Forward(subcommand), "clean" => MiriCommand::Clean, + // For use by the `./miri test` dependency builder. + "build" if env::var_os("MIRI_BUILD_TEST_DEPS").is_some() => + MiriCommand::Forward("build".into()), _ => { // Check for version and help flags. if has_arg_flag("--help") || has_arg_flag("-h") { diff --git a/src/tools/miri/tests/deps/src/main.rs b/src/tools/miri/tests/deps/src/main.rs index f328e4d9d04c3..363c9744fa486 100644 --- a/src/tools/miri/tests/deps/src/main.rs +++ b/src/tools/miri/tests/deps/src/main.rs @@ -1 +1,3 @@ -fn main() {} +fn main() { + unreachable!() +} diff --git a/src/tools/miri/tests/ui.rs b/src/tools/miri/tests/ui.rs index 70739eef28838..ebc1277987877 100644 --- a/src/tools/miri/tests/ui.rs +++ b/src/tools/miri/tests/ui.rs @@ -132,14 +132,19 @@ fn miri_config( // (It's a separate crate, so we don't get an env var from cargo.) program: miri_path() .with_file_name(format!("cargo-miri{}", env::consts::EXE_SUFFIX)), - // There is no `cargo miri build` so we just use `cargo miri run`. // Add `-Zbinary-dep-depinfo` since it is needed for bootstrap builds (and doesn't harm otherwise). - args: ["miri", "run", "--quiet", "-Zbinary-dep-depinfo"] + args: ["miri", "build", "-Zbinary-dep-depinfo"] .into_iter() .map(Into::into) .collect(), - // Reset `RUSTFLAGS` to work around . - envs: vec![("RUSTFLAGS".into(), None)], + envs: vec![ + // Reset `RUSTFLAGS` to work around . + ("RUSTFLAGS".into(), None), + // Reset `MIRIFLAGS` because it caused trouble in the past and should not be needed. + ("MIRIFLAGS".into(), None), + // Allow `cargo miri build`. + ("MIRI_BUILD_TEST_DEPS".into(), Some("1".into())), + ], ..CommandBuilder::cargo() }, crate_manifest_path: Path::new("tests/deps").join("Cargo.toml"), From 1044241d38ad9b21e90cf1f6d5ca5c4fce2777eb Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Mon, 2 Feb 2026 20:50:09 +0100 Subject: [PATCH 015/103] do not forward run flags to dependency build --- src/tools/miri/tests/ui.rs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/tools/miri/tests/ui.rs b/src/tools/miri/tests/ui.rs index ebc1277987877..047cdeb357c20 100644 --- a/src/tools/miri/tests/ui.rs +++ b/src/tools/miri/tests/ui.rs @@ -366,15 +366,16 @@ fn run_dep_mode(target: String, args: impl Iterator) -> Result< miri_config(&target, "", Mode::RunDep, Some(WithDependencies { bless: false })); config.comment_defaults.base().custom.remove("edition"); // `./miri` adds an `--edition` in `args`, so don't set it twice config.fill_host_and_target()?; + let dep_builder = BuildManager::one_off(config.clone()); + // Only set these for the actual run, not the dep builder, so invalid flags do not fail + // the dependency build. config.program.args = args.collect(); + let test_config = TestConfig::one_off_runner(config, PathBuf::new()); - let test_config = TestConfig::one_off_runner(config.clone(), PathBuf::new()); - - let build_manager = BuildManager::one_off(config); let mut cmd = test_config.config.program.build(&test_config.config.out_dir); cmd.arg("--target").arg(test_config.config.target.as_ref().unwrap()); // Build dependencies - test_config.apply_custom(&mut cmd, &build_manager).unwrap(); + test_config.apply_custom(&mut cmd, &dep_builder).expect("failed to build dependencies"); if cmd.spawn()?.wait()?.success() { Ok(()) } else { std::process::exit(1) } } From ab29d8285edf4ecb091676ba2f41dca2487b1e99 Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Tue, 3 Feb 2026 03:18:56 +0200 Subject: [PATCH 016/103] When autoimporting a segment followed by other segments, only consider items that will resolve with the after segments --- src/tools/rust-analyzer/crates/hir/src/lib.rs | 6 +- .../ide-assists/src/handlers/auto_import.rs | 33 ++++++- .../ide-db/src/imports/import_assets.rs | 88 +++++++++++++++++-- 3 files changed, 116 insertions(+), 11 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir/src/lib.rs b/src/tools/rust-analyzer/crates/hir/src/lib.rs index 252d71fb80a46..f2948ee83c572 100644 --- a/src/tools/rust-analyzer/crates/hir/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir/src/lib.rs @@ -6064,11 +6064,7 @@ impl<'db> Type<'db> { match name { Some(name) => { - match ctx.probe_for_name( - method_resolution::Mode::MethodCall, - name.clone(), - self_ty, - ) { + match ctx.probe_for_name(method_resolution::Mode::Path, name.clone(), self_ty) { Ok(candidate) | Err(method_resolution::MethodError::PrivateMatch(candidate)) => { let id = candidate.item.into(); diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/auto_import.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/auto_import.rs index cc2bf8174941f..f4b84f9adb395 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/auto_import.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/auto_import.rs @@ -351,7 +351,7 @@ mod tests { let config = TEST_CONFIG; let ctx = AssistContext::new(sema, &config, frange); let mut acc = Assists::new(&ctx, AssistResolveStrategy::All); - auto_import(&mut acc, &ctx); + hir::attach_db(&db, || auto_import(&mut acc, &ctx)); let assists = acc.finish(); let labels = assists.iter().map(|assist| assist.label.to_string()).collect::>(); @@ -1896,4 +1896,35 @@ fn foo(_: S) {} "#, ); } + + #[test] + fn with_after_segments() { + let before = r#" +mod foo { + pub mod wanted { + pub fn abc() {} + } +} + +mod bar { + pub mod wanted {} +} + +mod baz { + pub fn wanted() {} +} + +mod quux { + pub struct wanted; +} +impl quux::wanted { + fn abc() {} +} + +fn f() { + wanted$0::abc; +} + "#; + check_auto_import_order(before, &["Import `foo::wanted`", "Import `quux::wanted`"]); + } } diff --git a/src/tools/rust-analyzer/crates/ide-db/src/imports/import_assets.rs b/src/tools/rust-analyzer/crates/ide-db/src/imports/import_assets.rs index 35579eb2590dc..1c485270272e3 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/imports/import_assets.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/imports/import_assets.rs @@ -9,7 +9,7 @@ use hir::{ }; use itertools::Itertools; use rustc_hash::{FxHashMap, FxHashSet}; -use smallvec::SmallVec; +use smallvec::{SmallVec, smallvec}; use syntax::{ AstNode, SyntaxNode, ast::{self, HasName, make}, @@ -68,6 +68,8 @@ pub struct PathImportCandidate { pub qualifier: Vec, /// The name the item (struct, trait, enum, etc.) should have. pub name: NameToImport, + /// Potentially more segments that should resolve in the candidate. + pub after: Vec, } /// A name that will be used during item lookups. @@ -376,7 +378,7 @@ fn path_applicable_imports( ) -> FxIndexSet { let _p = tracing::info_span!("ImportAssets::path_applicable_imports").entered(); - match &*path_candidate.qualifier { + let mut result = match &*path_candidate.qualifier { [] => { items_locator::items_with_name( db, @@ -433,6 +435,75 @@ fn path_applicable_imports( }) .take(DEFAULT_QUERY_SEARCH_LIMIT) .collect(), + }; + + filter_candidates_by_after_path(db, scope, path_candidate, &mut result); + + result +} + +fn filter_candidates_by_after_path( + db: &RootDatabase, + scope: &SemanticsScope<'_>, + path_candidate: &PathImportCandidate, + imports: &mut FxIndexSet, +) { + if imports.len() <= 1 { + // Short-circuit, as even if it doesn't match fully we want it. + return; + } + + let Some((last_after, after_except_last)) = path_candidate.after.split_last() else { + return; + }; + + let original_imports = imports.clone(); + + let traits_in_scope = scope.visible_traits(); + imports.retain(|import| { + let items = if after_except_last.is_empty() { + smallvec![import.original_item] + } else { + let ItemInNs::Types(ModuleDef::Module(item)) = import.original_item else { + return false; + }; + // FIXME: This doesn't consider visibilities. + item.resolve_mod_path(db, after_except_last.iter().cloned()) + .into_iter() + .flatten() + .collect::>() + }; + items.into_iter().any(|item| { + let has_last_method = |ty: hir::Type<'_>| { + ty.iterate_path_candidates(db, scope, &traits_in_scope, Some(last_after), |_| { + Some(()) + }) + .is_some() + }; + // FIXME: A trait can have an assoc type that has a function/const, that's two segments before last. + match item { + // A module? Can we resolve one more segment? + ItemInNs::Types(ModuleDef::Module(module)) => module + .resolve_mod_path(db, [last_after.clone()]) + .is_some_and(|mut it| it.any(|_| true)), + // And ADT/Type Alias? That might be a method. + ItemInNs::Types(ModuleDef::Adt(it)) => has_last_method(it.ty(db)), + ItemInNs::Types(ModuleDef::BuiltinType(it)) => has_last_method(it.ty(db)), + ItemInNs::Types(ModuleDef::TypeAlias(it)) => has_last_method(it.ty(db)), + // A trait? Might have an associated item. + ItemInNs::Types(ModuleDef::Trait(it)) => it + .items(db) + .into_iter() + .any(|assoc_item| assoc_item.name(db) == Some(last_after.clone())), + // Other items? can't resolve one more segment. + _ => false, + } + }) + }); + + if imports.is_empty() { + // Better one half-match than zero full matches. + *imports = original_imports; } } @@ -759,10 +830,14 @@ impl<'db> ImportCandidate<'db> { if sema.resolve_path(path).is_some() { return None; } + let after = std::iter::successors(path.parent_path(), |it| it.parent_path()) + .map(|seg| seg.segment()?.name_ref().map(|name| Name::new_root(&name.text()))) + .collect::>()?; path_import_candidate( sema, path.qualifier(), NameToImport::exact_case_sensitive(path.segment()?.name_ref()?.to_string()), + after, ) } @@ -777,6 +852,7 @@ impl<'db> ImportCandidate<'db> { Some(ImportCandidate::Path(PathImportCandidate { qualifier: vec![], name: NameToImport::exact_case_sensitive(name.to_string()), + after: vec![], })) } @@ -785,7 +861,8 @@ impl<'db> ImportCandidate<'db> { fuzzy_name: String, sema: &Semantics<'db, RootDatabase>, ) -> Option { - path_import_candidate(sema, qualifier, NameToImport::fuzzy(fuzzy_name)) + // Assume a fuzzy match does not want the segments after. Because... I guess why not? + path_import_candidate(sema, qualifier, NameToImport::fuzzy(fuzzy_name), Vec::new()) } } @@ -793,6 +870,7 @@ fn path_import_candidate<'db>( sema: &Semantics<'db, RootDatabase>, qualifier: Option, name: NameToImport, + after: Vec, ) -> Option> { Some(match qualifier { Some(qualifier) => match sema.resolve_path(&qualifier) { @@ -802,7 +880,7 @@ fn path_import_candidate<'db>( .segments() .map(|seg| seg.name_ref().map(|name| Name::new_root(&name.text()))) .collect::>>()?; - ImportCandidate::Path(PathImportCandidate { qualifier, name }) + ImportCandidate::Path(PathImportCandidate { qualifier, name, after }) } else { return None; } @@ -826,7 +904,7 @@ fn path_import_candidate<'db>( } Some(_) => return None, }, - None => ImportCandidate::Path(PathImportCandidate { qualifier: vec![], name }), + None => ImportCandidate::Path(PathImportCandidate { qualifier: vec![], name, after }), }) } From 3eabab29a2685cd81f861b8a983883e8f49faea7 Mon Sep 17 00:00:00 2001 From: hsqStephenZhang Date: Mon, 2 Feb 2026 19:20:25 +0100 Subject: [PATCH 017/103] chore: configure capstone for x86 and clean up arm64 stub --- src/tools/miri/Cargo.toml | 2 +- src/tools/miri/src/shims/native_lib/trace/parent.rs | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/src/tools/miri/Cargo.toml b/src/tools/miri/Cargo.toml index e7c90e45eba59..62387848868d5 100644 --- a/src/tools/miri/Cargo.toml +++ b/src/tools/miri/Cargo.toml @@ -39,7 +39,7 @@ serde = { version = "1.0.219", features = ["derive"], optional = true } [target.'cfg(target_os = "linux")'.dependencies] nix = { version = "0.30.1", features = ["mman", "ptrace", "signal"], optional = true } ipc-channel = { version = "0.20.0", optional = true } -capstone = { version = "0.14", optional = true } +capstone = { version = "0.14", features = ["arch_x86", "full"], default-features = false, optional = true} [target.'cfg(all(target_os = "linux", target_pointer_width = "64", target_endian = "little"))'.dependencies] genmc-sys = { path = "./genmc-sys/", version = "0.1.0", optional = true } diff --git a/src/tools/miri/src/shims/native_lib/trace/parent.rs b/src/tools/miri/src/shims/native_lib/trace/parent.rs index 5476cccc02e3b..f73b1359cef74 100644 --- a/src/tools/miri/src/shims/native_lib/trace/parent.rs +++ b/src/tools/miri/src/shims/native_lib/trace/parent.rs @@ -395,8 +395,6 @@ fn capstone_find_events( _ => (), } } - // FIXME: arm64 - _ => unimplemented!(), } false From dcb608d8180b5313119071be2d9f973488e62178 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Tue, 3 Feb 2026 10:42:22 +0100 Subject: [PATCH 018/103] re-balance CI --- src/tools/miri/ci/ci.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/tools/miri/ci/ci.sh b/src/tools/miri/ci/ci.sh index 0d3e70a163be4..53a7b3c047fa9 100755 --- a/src/tools/miri/ci/ci.sh +++ b/src/tools/miri/ci/ci.sh @@ -174,6 +174,8 @@ case $HOST_TARGET in MIR_OPT=1 MANY_SEEDS=64 TEST_BENCH=1 CARGO_MIRI_ENV=1 run_tests # Custom target JSON file TEST_TARGET=tests/x86_64-unknown-kernel.json MIRI_NO_STD=1 run_tests_minimal no_std + # Not officially supported tier 2 + MANY_SEEDS=16 TEST_TARGET=x86_64-pc-solaris run_tests ;; aarch64-apple-darwin) # Host @@ -184,7 +186,6 @@ case $HOST_TARGET in # Not officially supported tier 2 MANY_SEEDS=16 TEST_TARGET=mips-unknown-linux-gnu run_tests # a 32bit big-endian target, and also a target without 64bit atomics MANY_SEEDS=16 TEST_TARGET=x86_64-unknown-illumos run_tests - MANY_SEEDS=16 TEST_TARGET=x86_64-pc-solaris run_tests ;; i686-pc-windows-msvc) # Host From 2159eaf5789c8c367e4c7f77c62d79262ea0252e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maja=20K=C4=85dzio=C5=82ka?= Date: Sun, 4 Jan 2026 23:37:32 +0100 Subject: [PATCH 019/103] Add a validity testcase for uninhabited variants --- .../tests/fail/validity/uninhabited_variant.rs | 17 +++++++++++++++++ .../fail/validity/uninhabited_variant.stderr | 13 +++++++++++++ 2 files changed, 30 insertions(+) create mode 100644 src/tools/miri/tests/fail/validity/uninhabited_variant.rs create mode 100644 src/tools/miri/tests/fail/validity/uninhabited_variant.stderr diff --git a/src/tools/miri/tests/fail/validity/uninhabited_variant.rs b/src/tools/miri/tests/fail/validity/uninhabited_variant.rs new file mode 100644 index 0000000000000..303584423fdcf --- /dev/null +++ b/src/tools/miri/tests/fail/validity/uninhabited_variant.rs @@ -0,0 +1,17 @@ +// NOTE: this is essentially a smoke-test, with more comprehensive tests living in the rustc +// repository at tests/ui/consts/const-eval/ub-enum.rs +#![feature(never_type)] + +#[repr(C)] +#[allow(dead_code)] +enum E { + V1, // discriminant: 0 + V2(!), // 1 +} + +fn main() { + unsafe { + std::mem::transmute::(1); + //~^ ERROR: encountered an uninhabited enum variant + } +} diff --git a/src/tools/miri/tests/fail/validity/uninhabited_variant.stderr b/src/tools/miri/tests/fail/validity/uninhabited_variant.stderr new file mode 100644 index 0000000000000..76ee25009b6ec --- /dev/null +++ b/src/tools/miri/tests/fail/validity/uninhabited_variant.stderr @@ -0,0 +1,13 @@ +error: Undefined Behavior: constructing invalid value at .: encountered an uninhabited enum variant + --> tests/fail/validity/uninhabited_variant.rs:LL:CC + | +LL | std::mem::transmute::(1); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Undefined Behavior occurred here + | + = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior + = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information + +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + +error: aborting due to 1 previous error + From 510af14aa03cee1005f1e1c94103c84e72376d65 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 3 Feb 2026 19:31:36 +0000 Subject: [PATCH 020/103] Bump bytes from 1.10.1 to 1.11.1 in /tests/deps Bumps [bytes](https://github.com/tokio-rs/bytes) from 1.10.1 to 1.11.1. - [Release notes](https://github.com/tokio-rs/bytes/releases) - [Changelog](https://github.com/tokio-rs/bytes/blob/master/CHANGELOG.md) - [Commits](https://github.com/tokio-rs/bytes/compare/v1.10.1...v1.11.1) --- updated-dependencies: - dependency-name: bytes dependency-version: 1.11.1 dependency-type: indirect ... Signed-off-by: dependabot[bot] --- src/tools/miri/tests/deps/Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/tools/miri/tests/deps/Cargo.lock b/src/tools/miri/tests/deps/Cargo.lock index 2549396251672..f54628e810425 100644 --- a/src/tools/miri/tests/deps/Cargo.lock +++ b/src/tools/miri/tests/deps/Cargo.lock @@ -46,9 +46,9 @@ checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43" [[package]] name = "bytes" -version = "1.10.1" +version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" +checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33" [[package]] name = "cfg-if" From 9a84e66021c1e206e7b1b64ff6de81447ab122c2 Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Wed, 4 Feb 2026 12:22:42 +0800 Subject: [PATCH 021/103] fix: Fix loses associated bounds for replace_derive_with_manual_impl Example --- ```rust #[derive(Clo$0ne)] struct Foo(T::Target); ``` **Before this PR** ```rust struct Foo(T::Target); impl Clone for Foo { $0fn clone(&self) -> Self { Self(self.0.clone()) } } ``` **After this PR** ```rust struct Foo(T::Target); impl Clone for Foo where T::Target: Clone { $0fn clone(&self) -> Self { Self(self.0.clone()) } } ``` --- .../replace_derive_with_manual_impl.rs | 23 ++++++++ .../crates/ide-assists/src/utils.rs | 53 ++++++++++++++++++- 2 files changed, 75 insertions(+), 1 deletion(-) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_derive_with_manual_impl.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_derive_with_manual_impl.rs index 11b3fd22faa37..7c024263b4a80 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_derive_with_manual_impl.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_derive_with_manual_impl.rs @@ -1307,6 +1307,29 @@ impl Clone for Foo { ) } + #[test] + fn add_custom_impl_clone_generic_tuple_struct_with_associated() { + check_assist( + replace_derive_with_manual_impl, + r#" +//- minicore: clone, derive, deref +#[derive(Clo$0ne)] +struct Foo(T::Target); +"#, + r#" +struct Foo(T::Target); + +impl Clone for Foo +where T::Target: Clone +{ + $0fn clone(&self) -> Self { + Self(self.0.clone()) + } +} +"#, + ) + } + #[test] fn test_ignore_derive_macro_without_input() { check_assist_not_applicable( diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs b/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs index 4b8c193057934..1732d8c2018e2 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs @@ -15,6 +15,7 @@ use ide_db::{ path_transform::PathTransform, syntax_helpers::{node_ext::preorder_expr, prettify_macro_expansion}, }; +use itertools::Itertools; use stdx::format_to; use syntax::{ AstNode, AstToken, Direction, NodeOrToken, SourceFile, @@ -766,6 +767,11 @@ fn generate_impl_inner( }); let generic_args = generic_params.as_ref().map(|params| params.to_generic_args().clone_for_update()); + let trait_where_clause = trait_ + .as_ref() + .zip(generic_params.as_ref()) + .and_then(|(trait_, params)| generic_param_associated_bounds(adt, trait_, params)); + let ty = make::ty_path(make::ext::ident_path(&adt.name().unwrap().text())); let cfg_attrs = @@ -781,7 +787,7 @@ fn generate_impl_inner( false, trait_, ty, - None, + trait_where_clause, adt.where_clause(), body, ), @@ -790,6 +796,51 @@ fn generate_impl_inner( .clone_for_update() } +fn generic_param_associated_bounds( + adt: &ast::Adt, + trait_: &ast::Type, + generic_params: &ast::GenericParamList, +) -> Option { + let in_type_params = |name: &ast::NameRef| { + generic_params + .generic_params() + .filter_map(|param| match param { + ast::GenericParam::TypeParam(type_param) => type_param.name(), + _ => None, + }) + .any(|param| param.text() == name.text()) + }; + let adt_body = match adt { + ast::Adt::Enum(e) => e.variant_list().map(|it| it.syntax().clone()), + ast::Adt::Struct(s) => s.field_list().map(|it| it.syntax().clone()), + ast::Adt::Union(u) => u.record_field_list().map(|it| it.syntax().clone()), + }; + let mut trait_where_clause = adt_body + .into_iter() + .flat_map(|it| it.descendants()) + .filter_map(ast::Path::cast) + .filter_map(|path| { + let qualifier = path.qualifier()?.as_single_segment()?; + let qualifier = qualifier + .name_ref() + .or_else(|| match qualifier.type_anchor()?.ty()? { + ast::Type::PathType(path_type) => path_type.path()?.as_single_name_ref(), + _ => None, + }) + .filter(in_type_params)?; + Some((qualifier, path.segment()?.name_ref()?)) + }) + .map(|(qualifier, assoc_name)| { + let segments = [qualifier, assoc_name].map(make::path_segment); + let path = make::path_from_segments(segments, false); + let bounds = Some(make::type_bound(trait_.clone())); + make::where_pred(either::Either::Right(make::ty_path(path)), bounds) + }) + .unique_by(|it| it.syntax().to_string()) + .peekable(); + trait_where_clause.peek().is_some().then(|| make::where_clause(trait_where_clause)) +} + pub(crate) fn add_method_to_adt( builder: &mut SourceChangeBuilder, adt: &ast::Adt, From a3d848be47c49e841e5a5a88706970f646ce55d2 Mon Sep 17 00:00:00 2001 From: The Miri Cronjob Bot Date: Wed, 4 Feb 2026 05:13:19 +0000 Subject: [PATCH 022/103] Prepare for merging from rust-lang/rust This updates the rust-version file to 1d05e3c131d7181eb3df1a8c261f43135c99200d. --- src/tools/miri/rust-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/miri/rust-version b/src/tools/miri/rust-version index d7d94190c98b4..4aad364eb6f6a 100644 --- a/src/tools/miri/rust-version +++ b/src/tools/miri/rust-version @@ -1 +1 @@ -878374e07f3bf038c96e94e5bc917471878e4bf6 +1d05e3c131d7181eb3df1a8c261f43135c99200d From 5009ac30945e3a88acbafb3a692abade4fcf9f01 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 4 Feb 2026 20:44:47 +0000 Subject: [PATCH 023/103] Bump git2 from 0.20.2 to 0.20.4 Bumps [git2](https://github.com/rust-lang/git2-rs) from 0.20.2 to 0.20.4. - [Changelog](https://github.com/rust-lang/git2-rs/blob/git2-0.20.4/CHANGELOG.md) - [Commits](https://github.com/rust-lang/git2-rs/compare/git2-0.20.2...git2-0.20.4) --- updated-dependencies: - dependency-name: git2 dependency-version: 0.20.4 dependency-type: indirect ... Signed-off-by: dependabot[bot] --- src/tools/miri/Cargo.lock | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/tools/miri/Cargo.lock b/src/tools/miri/Cargo.lock index b314aaafbdf0c..2d332ae98ddaa 100644 --- a/src/tools/miri/Cargo.lock +++ b/src/tools/miri/Cargo.lock @@ -562,9 +562,9 @@ checksum = "e629b9b98ef3dd8afe6ca2bd0f89306cec16d43d907889945bc5d6687f2f13c7" [[package]] name = "git2" -version = "0.20.2" +version = "0.20.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2deb07a133b1520dc1a5690e9bd08950108873d7ed5de38dcc74d3b5ebffa110" +checksum = "7b88256088d75a56f8ecfa070513a775dd9107f6530ef14919dac831af9cfe2b" dependencies = [ "bitflags", "libc", @@ -804,9 +804,9 @@ dependencies = [ [[package]] name = "libgit2-sys" -version = "0.18.2+1.9.1" +version = "0.18.3+1.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c42fe03df2bd3c53a3a9c7317ad91d80c81cd1fb0caec8d7cc4cd2bfa10c222" +checksum = "c9b3acc4b91781bb0b3386669d325163746af5f6e4f73e6d2d630e09a35f3487" dependencies = [ "cc", "libc", From 4b68f80a4e8330558cc3d0db1198cdb6b356287c Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Thu, 5 Feb 2026 14:01:35 +0800 Subject: [PATCH 024/103] feat: Improve hover too long parameter list Example --- ```rust fn fn_$0( attrs: impl IntoIterator, visibility: Option, fn_name: ast::Name, type_params: Option, where_clause: Option, params: ast::ParamList, body: ast::BlockExpr, ret_type: Option, is_async: bool, is_const: bool, is_unsafe: bool, is_gen: bool, ) -> ast::Fn {} ``` **Before this PR** ```rust fn fn_(attrs: impl IntoIterator, visibility: Option, fn_name: ast::Name, type_params: Option, where_clause: Option, params: ast::ParamList, body: ast::BlockExpr, ret_type: Option, is_async: bool, is_const: bool, is_unsafe: bool, is_gen: bool) -> ast::Fn ``` **After this PR** ```rust fn fn_( attrs: impl IntoIterator, visibility: Option, fn_name: ast::Name, type_params: Option, where_clause: Option, params: ast::ParamList, body: ast::BlockExpr, ret_type: Option, is_async: bool, is_const: bool, is_unsafe: bool, is_gen: bool ) -> ast::Fn ``` --- .../rust-analyzer/crates/hir/src/display.rs | 13 ++- .../crates/ide/src/hover/tests.rs | 93 +++++++++++++++++++ 2 files changed, 104 insertions(+), 2 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir/src/display.rs b/src/tools/rust-analyzer/crates/hir/src/display.rs index 1f9af564c3599..30b7a51d59f6f 100644 --- a/src/tools/rust-analyzer/crates/hir/src/display.rs +++ b/src/tools/rust-analyzer/crates/hir/src/display.rs @@ -172,8 +172,13 @@ fn write_function<'db>(f: &mut HirFormatter<'_, 'db>, func_id: FunctionId) -> Re write_generic_params(GenericDefId::FunctionId(func_id), f)?; + let too_long_param = data.params.len() > 4; f.write_char('(')?; + if too_long_param { + f.write_str("\n ")?; + } + let mut first = true; let mut skip_self = 0; if let Some(self_param) = func.self_param(db) { @@ -182,11 +187,12 @@ fn write_function<'db>(f: &mut HirFormatter<'_, 'db>, func_id: FunctionId) -> Re skip_self = 1; } + let comma = if too_long_param { ",\n " } else { ", " }; // FIXME: Use resolved `param.ty` once we no longer discard lifetimes let body = db.body(func_id.into()); for (type_ref, param) in data.params.iter().zip(func.assoc_fn_params(db)).skip(skip_self) { if !first { - f.write_str(", ")?; + f.write_str(comma)?; } else { first = false; } @@ -201,11 +207,14 @@ fn write_function<'db>(f: &mut HirFormatter<'_, 'db>, func_id: FunctionId) -> Re if data.is_varargs() { if !first { - f.write_str(", ")?; + f.write_str(comma)?; } f.write_str("...")?; } + if too_long_param { + f.write_char('\n')?; + } f.write_char(')')?; // `FunctionData::ret_type` will be `::core::future::Future` for async fns. diff --git a/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs b/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs index 7900a0dc9991c..7fbbc576dd36a 100644 --- a/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs +++ b/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs @@ -9719,6 +9719,99 @@ fn test_hover_function_with_pat_param() { ); } +#[test] +fn test_hover_function_with_too_long_param() { + check( + r#" +fn fn_$0( + attrs: impl IntoIterator, + visibility: Option, + fn_name: ast::Name, + type_params: Option, + where_clause: Option, + params: ast::ParamList, + body: ast::BlockExpr, + ret_type: Option, + is_async: bool, + is_const: bool, + is_unsafe: bool, + is_gen: bool, +) -> ast::Fn {} + "#, + expect![[r#" + *fn_* + + ```rust + ra_test_fixture + ``` + + ```rust + fn fn_( + attrs: impl IntoIterator, + visibility: Option, + fn_name: ast::Name, + type_params: Option, + where_clause: Option, + params: ast::ParamList, + body: ast::BlockExpr, + ret_type: Option, + is_async: bool, + is_const: bool, + is_unsafe: bool, + is_gen: bool + ) -> ast::Fn + ``` + "#]], + ); + + check( + r#" +fn fn_$0( + &self, + attrs: impl IntoIterator, + visibility: Option, + fn_name: ast::Name, + type_params: Option, + where_clause: Option, + params: ast::ParamList, + body: ast::BlockExpr, + ret_type: Option, + is_async: bool, + is_const: bool, + is_unsafe: bool, + is_gen: bool, + ... +) -> ast::Fn {} + "#, + expect![[r#" + *fn_* + + ```rust + ra_test_fixture + ``` + + ```rust + fn fn_( + &self, + attrs: impl IntoIterator, + visibility: Option, + fn_name: ast::Name, + type_params: Option, + where_clause: Option, + params: ast::ParamList, + body: ast::BlockExpr, + ret_type: Option, + is_async: bool, + is_const: bool, + is_unsafe: bool, + is_gen: bool, + ... + ) -> ast::Fn + ``` + "#]], + ); +} + #[test] fn hover_path_inside_block_scope() { check( From 94fa522956116707738df340136259005a325423 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Thu, 5 Feb 2026 08:52:39 +0100 Subject: [PATCH 025/103] Prepare for merging from rust-lang/rust This updates the rust-version file to 9f4b56a5aed81e8c36cc26b3c1b4666ead6b71fc. --- src/tools/miri/rust-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/miri/rust-version b/src/tools/miri/rust-version index 4aad364eb6f6a..c122f58ec34ac 100644 --- a/src/tools/miri/rust-version +++ b/src/tools/miri/rust-version @@ -1 +1 @@ -1d05e3c131d7181eb3df1a8c261f43135c99200d +9f4b56a5aed81e8c36cc26b3c1b4666ead6b71fc From 40857fcd00c7982cb7ca5f32674f42ce6e1e9688 Mon Sep 17 00:00:00 2001 From: hsqStephenZhang Date: Thu, 5 Feb 2026 12:48:55 +0100 Subject: [PATCH 026/103] chore: fix typos suggested by typos-cli --- src/tools/miri/src/alloc/isolated_alloc.rs | 4 ++-- src/tools/miri/src/bin/log/tracing_chrome_instant.rs | 4 ++-- .../miri/src/borrow_tracker/tree_borrows/perms.rs | 2 +- src/tools/miri/src/borrow_tracker/tree_borrows/tree.rs | 2 +- .../miri/src/borrow_tracker/tree_borrows/tree/tests.rs | 2 +- .../src/borrow_tracker/tree_borrows/tree_visitor.rs | 2 +- src/tools/miri/src/concurrency/data_race.rs | 2 +- .../miri/src/concurrency/genmc/global_allocations.rs | 2 +- src/tools/miri/src/concurrency/genmc/mod.rs | 10 +++++----- src/tools/miri/src/concurrency/genmc/run.rs | 2 +- src/tools/miri/src/concurrency/weak_memory.rs | 2 +- src/tools/miri/src/eval.rs | 2 +- src/tools/miri/src/helpers.rs | 4 ++-- src/tools/miri/src/machine.rs | 4 ++-- src/tools/miri/src/shims/native_lib/trace/child.rs | 2 +- src/tools/miri/src/shims/os_str.rs | 2 +- src/tools/miri/src/shims/time.rs | 4 ++-- src/tools/miri/src/shims/unix/foreign_items.rs | 2 +- src/tools/miri/src/shims/unix/freebsd/sync.rs | 2 +- src/tools/miri/src/shims/unix/linux/foreign_items.rs | 4 ++-- src/tools/miri/src/shims/unix/linux_like/epoll.rs | 2 +- src/tools/miri/src/shims/unix/macos/sync.rs | 2 +- src/tools/miri/src/shims/windows/foreign_items.rs | 2 +- src/tools/miri/src/shims/windows/sync.rs | 2 +- src/tools/miri/src/shims/x86/avx2.rs | 2 +- src/tools/miri/src/shims/x86/bmi.rs | 2 +- src/tools/miri/src/shims/x86/sse.rs | 4 ++-- src/tools/miri/src/shims/x86/sse2.rs | 6 +++--- src/tools/miri/src/shims/x86/sse41.rs | 2 +- src/tools/miri/src/shims/x86/sse42.rs | 6 +++--- src/tools/miri/src/shims/x86/ssse3.rs | 2 +- 31 files changed, 46 insertions(+), 46 deletions(-) diff --git a/src/tools/miri/src/alloc/isolated_alloc.rs b/src/tools/miri/src/alloc/isolated_alloc.rs index 1745727b16b40..5c4a7230cf585 100644 --- a/src/tools/miri/src/alloc/isolated_alloc.rs +++ b/src/tools/miri/src/alloc/isolated_alloc.rs @@ -66,10 +66,10 @@ impl IsolatedAlloc { // And make sure the align is at least one page let align = std::cmp::max(layout.align(), self.page_size); // pg_count gives us the # of pages needed to satisfy the size. For - // align > page_size where align = n * page_size, a sufficently-aligned + // align > page_size where align = n * page_size, a sufficiently-aligned // address must exist somewhere in the range of // some_page_aligned_address..some_page_aligned_address + (n-1) * page_size - // (since if some_page_aligned_address + n * page_size is sufficently aligned, + // (since if some_page_aligned_address + n * page_size is sufficiently aligned, // then so is some_page_aligned_address itself per the definition of n, so we // can avoid using that 1 extra page). // Thus we allocate n-1 extra pages diff --git a/src/tools/miri/src/bin/log/tracing_chrome_instant.rs b/src/tools/miri/src/bin/log/tracing_chrome_instant.rs index b5f00852b82ff..04705b8846d9c 100644 --- a/src/tools/miri/src/bin/log/tracing_chrome_instant.rs +++ b/src/tools/miri/src/bin/log/tracing_chrome_instant.rs @@ -2,7 +2,7 @@ //! . //! A useful resource is also //! , -//! although this file does not implement TSC synchronization but insteads pins threads to CPUs, +//! although this file does not implement TSC synchronization but instead pins threads to CPUs, //! since the former is not reliable (i.e. it might lead to non-monotonic time measurements). //! Another useful resource for future improvements might be measureme's time measurement utils: //! . @@ -11,7 +11,7 @@ #![cfg(feature = "tracing")] /// This alternative `TracingChromeInstant` implementation was made entirely to suit the needs of -/// [crate::log::tracing_chrome], and shouldn't be used for anything else. It featues two functions: +/// [crate::log::tracing_chrome], and shouldn't be used for anything else. It features two functions: /// - [TracingChromeInstant::setup_for_thread_and_start], which sets up the current thread to do /// proper time tracking and returns a point in time to use as "t=0", and /// - [TracingChromeInstant::with_elapsed_micros_subtracting_tracing], which allows diff --git a/src/tools/miri/src/borrow_tracker/tree_borrows/perms.rs b/src/tools/miri/src/borrow_tracker/tree_borrows/perms.rs index 064c1cc5b95f7..b62c5f242c374 100644 --- a/src/tools/miri/src/borrow_tracker/tree_borrows/perms.rs +++ b/src/tools/miri/src/borrow_tracker/tree_borrows/perms.rs @@ -578,7 +578,7 @@ pub mod diagnostics { // - created as Reserved { conflicted: false }, // then Unique -> Disabled is forbidden // A potential `Reserved { conflicted: false } - // -> Reserved { conflicted: true }` is inexistant or irrelevant, + // -> Reserved { conflicted: true }` is inexistent or irrelevant, // and so is the `Reserved { conflicted: false } -> Unique` (Unique, Frozen) => false, (ReservedFrz { conflicted: true }, _) => false, diff --git a/src/tools/miri/src/borrow_tracker/tree_borrows/tree.rs b/src/tools/miri/src/borrow_tracker/tree_borrows/tree.rs index 2c6be522837cb..22e6389fefc48 100644 --- a/src/tools/miri/src/borrow_tracker/tree_borrows/tree.rs +++ b/src/tools/miri/src/borrow_tracker/tree_borrows/tree.rs @@ -226,7 +226,7 @@ impl LocationState { /// Records a new access, so that future access can potentially be skipped /// by `skip_if_known_noop`. This must be called on child accesses, and otherwise - /// shoud be called on foreign accesses for increased performance. It should not be called + /// should be called on foreign accesses for increased performance. It should not be called /// when `skip_if_known_noop` indicated skipping, since it then is a no-op. /// See `foreign_access_skipping.rs` fn record_new_access(&mut self, access_kind: AccessKind, rel_pos: AccessRelatedness) { diff --git a/src/tools/miri/src/borrow_tracker/tree_borrows/tree/tests.rs b/src/tools/miri/src/borrow_tracker/tree_borrows/tree/tests.rs index 0b83de2cedc09..52fe1c08a3092 100644 --- a/src/tools/miri/src/borrow_tracker/tree_borrows/tree/tests.rs +++ b/src/tools/miri/src/borrow_tracker/tree_borrows/tree/tests.rs @@ -741,7 +741,7 @@ mod spurious_read { ); eprintln!(" (arbitrary code instanciated with '{opaque}')"); err += 1; - // We found an instanciation of the opaque code that makes this Pattern + // We found an instantiation of the opaque code that makes this Pattern // fail, we don't really need to check the rest. break; } diff --git a/src/tools/miri/src/borrow_tracker/tree_borrows/tree_visitor.rs b/src/tools/miri/src/borrow_tracker/tree_borrows/tree_visitor.rs index b1ceeecf577de..ab3a19ad9e17c 100644 --- a/src/tools/miri/src/borrow_tracker/tree_borrows/tree_visitor.rs +++ b/src/tools/miri/src/borrow_tracker/tree_borrows/tree_visitor.rs @@ -99,7 +99,7 @@ where assert!(self.stack.is_empty()); // First, handle accessed node. A bunch of things need to // be handled differently here compared to the further parents - // of `accesssed_node`. + // of `accessesed_node`. { self.propagate_at(this, accessed_node, AccessRelatedness::LocalAccess)?; if matches!(visit_children, ChildrenVisitMode::VisitChildrenOfAccessed) { diff --git a/src/tools/miri/src/concurrency/data_race.rs b/src/tools/miri/src/concurrency/data_race.rs index c18b780998606..336abff6c52d6 100644 --- a/src/tools/miri/src/concurrency/data_race.rs +++ b/src/tools/miri/src/concurrency/data_race.rs @@ -371,7 +371,7 @@ impl AccessType { if let Some(size) = size { if size == Size::ZERO { - // In this case there were multiple read accesss with different sizes and then a write. + // In this case there were multiple read accesses with different sizes and then a write. // We will be reporting *one* of the other reads, but we don't have enough information // to determine which one had which size. assert!(self == AccessType::AtomicLoad); diff --git a/src/tools/miri/src/concurrency/genmc/global_allocations.rs b/src/tools/miri/src/concurrency/genmc/global_allocations.rs index 7f34c60dcdaff..76be8c3a4c9a8 100644 --- a/src/tools/miri/src/concurrency/genmc/global_allocations.rs +++ b/src/tools/miri/src/concurrency/genmc/global_allocations.rs @@ -62,7 +62,7 @@ impl GlobalStateInner { let entry = match self.base_addr.entry(alloc_id) { Entry::Occupied(occupied_entry) => { // Looks like some other thread allocated this for us - // between when we released the read lock and aquired the write lock, + // between when we released the read lock and acquired the write lock, // so we just return that value. return interp_ok(*occupied_entry.get()); } diff --git a/src/tools/miri/src/concurrency/genmc/mod.rs b/src/tools/miri/src/concurrency/genmc/mod.rs index 740553ab85d64..092fc7294d15d 100644 --- a/src/tools/miri/src/concurrency/genmc/mod.rs +++ b/src/tools/miri/src/concurrency/genmc/mod.rs @@ -252,7 +252,7 @@ impl GenmcCtx { /// Inform GenMC about an atomic load. /// Returns that value that the load should read. /// - /// `old_value` is the value that a non-atomic load would read here, or `None` if the memory is uninitalized. + /// `old_value` is the value that a non-atomic load would read here, or `None` if the memory is uninitialized. pub(crate) fn atomic_load<'tcx>( &self, ecx: &InterpCx<'tcx, MiriMachine<'tcx>>, @@ -275,7 +275,7 @@ impl GenmcCtx { /// Inform GenMC about an atomic store. /// Returns `true` if the stored value should be reflected in Miri's memory. /// - /// `old_value` is the value that a non-atomic load would read here, or `None` if the memory is uninitalized. + /// `old_value` is the value that a non-atomic load would read here, or `None` if the memory is uninitialized. pub(crate) fn atomic_store<'tcx>( &self, ecx: &InterpCx<'tcx, MiriMachine<'tcx>>, @@ -320,7 +320,7 @@ impl GenmcCtx { /// /// Returns `(old_val, Option)`. `new_val` might not be the latest write in coherence order, which is indicated by `None`. /// - /// `old_value` is the value that a non-atomic load would read here, or `None` if the memory is uninitalized. + /// `old_value` is the value that a non-atomic load would read here, or `None` if the memory is uninitialized. pub(crate) fn atomic_rmw_op<'tcx>( &self, ecx: &InterpCx<'tcx, MiriMachine<'tcx>>, @@ -345,7 +345,7 @@ impl GenmcCtx { /// Returns `(old_val, Option)`. `new_val` might not be the latest write in coherence order, which is indicated by `None`. /// - /// `old_value` is the value that a non-atomic load would read here, or `None` if the memory is uninitalized. + /// `old_value` is the value that a non-atomic load would read here, or `None` if the memory is uninitialized. pub(crate) fn atomic_exchange<'tcx>( &self, ecx: &InterpCx<'tcx, MiriMachine<'tcx>>, @@ -370,7 +370,7 @@ impl GenmcCtx { /// /// Returns the old value read by the compare exchange, optionally the value that Miri should write back to its memory, and whether the compare-exchange was a success or not. /// - /// `old_value` is the value that a non-atomic load would read here, or `None` if the memory is uninitalized. + /// `old_value` is the value that a non-atomic load would read here, or `None` if the memory is uninitialized. pub(crate) fn atomic_compare_exchange<'tcx>( &self, ecx: &InterpCx<'tcx, MiriMachine<'tcx>>, diff --git a/src/tools/miri/src/concurrency/genmc/run.rs b/src/tools/miri/src/concurrency/genmc/run.rs index 6ff8e0656f36e..de6d87373ec02 100644 --- a/src/tools/miri/src/concurrency/genmc/run.rs +++ b/src/tools/miri/src/concurrency/genmc/run.rs @@ -30,7 +30,7 @@ pub fn run_genmc_mode<'tcx>( config: &MiriConfig, eval_entry: impl Fn(Rc) -> Result<(), NonZeroI32>, ) -> Result<(), NonZeroI32> { - // Check for supported target: endianess and pointer size must match the host. + // Check for supported target: endianness and pointer size must match the host. if tcx.data_layout.endian != Endian::Little || tcx.data_layout.pointer_size().bits() != 64 { tcx.dcx().fatal("GenMC only supports 64bit little-endian targets"); } diff --git a/src/tools/miri/src/concurrency/weak_memory.rs b/src/tools/miri/src/concurrency/weak_memory.rs index 6fe73fec0f57f..2869152bc39d7 100644 --- a/src/tools/miri/src/concurrency/weak_memory.rs +++ b/src/tools/miri/src/concurrency/weak_memory.rs @@ -389,7 +389,7 @@ impl<'tcx> StoreBuffer { }) .filter(|&store_elem| { if is_seqcst && store_elem.is_seqcst { - // An SC load needs to ignore all but last store maked SC (stores not marked SC are not + // An SC load needs to ignore all but last store made SC (stores not marked SC are not // affected) let include = !found_sc; found_sc = true; diff --git a/src/tools/miri/src/eval.rs b/src/tools/miri/src/eval.rs index 0423b0ea5abdf..1e75df7d278fb 100644 --- a/src/tools/miri/src/eval.rs +++ b/src/tools/miri/src/eval.rs @@ -113,7 +113,7 @@ pub struct MiriConfig { pub float_nondet: bool, /// Whether floating-point operations can have a non-deterministic rounding error. pub float_rounding_error: FloatRoundingErrorMode, - /// Whether Miri artifically introduces short reads/writes on file descriptors. + /// Whether Miri artificially introduces short reads/writes on file descriptors. pub short_fd_operations: bool, /// A list of crates that are considered user-relevant. pub user_relevant_crates: Vec, diff --git a/src/tools/miri/src/helpers.rs b/src/tools/miri/src/helpers.rs index f4fc478481a7a..5dcd2d9ec2084 100644 --- a/src/tools/miri/src/helpers.rs +++ b/src/tools/miri/src/helpers.rs @@ -138,7 +138,7 @@ pub fn iter_exported_symbols<'tcx>( } // Next, all our dependencies. - // `dependency_formats` includes all the transitive informations needed to link a crate, + // `dependency_formats` includes all the transitive information needed to link a crate, // which is what we need here since we need to dig out `exported_symbols` from all transitive // dependencies. let dependency_formats = tcx.dependency_formats(()); @@ -1148,7 +1148,7 @@ impl ToUsize for u32 { } /// Similarly, a maximum address size of `u64` is assumed widely here, so let's have ergonomic -/// converion from `usize` to `u64`. +/// conversion from `usize` to `u64`. pub trait ToU64 { fn to_u64(self) -> u64; } diff --git a/src/tools/miri/src/machine.rs b/src/tools/miri/src/machine.rs index f17bd5ac4319c..d50475c748747 100644 --- a/src/tools/miri/src/machine.rs +++ b/src/tools/miri/src/machine.rs @@ -654,7 +654,7 @@ pub struct MiriMachine<'tcx> { /// Whether floating-point operations can have a non-deterministic rounding error. pub float_rounding_error: FloatRoundingErrorMode, - /// Whether Miri artifically introduces short reads/writes on file descriptors. + /// Whether Miri artificially introduces short reads/writes on file descriptors. pub short_fd_operations: bool, } @@ -1802,7 +1802,7 @@ impl<'tcx> Machine<'tcx> for MiriMachine<'tcx> { // We have to skip the frame that is just being popped. ecx.active_thread_mut().recompute_top_user_relevant_frame(/* skip */ 1); } - // tracing-tree can autoamtically annotate scope changes, but it gets very confused by our + // tracing-tree can automatically annotate scope changes, but it gets very confused by our // concurrency and what it prints is just plain wrong. So we print our own information // instead. (Cc https://github.com/rust-lang/miri/issues/2266) info!("Leaving {}", ecx.frame().instance()); diff --git a/src/tools/miri/src/shims/native_lib/trace/child.rs b/src/tools/miri/src/shims/native_lib/trace/child.rs index 021ec2e9aeb3b..d7d9a591911a0 100644 --- a/src/tools/miri/src/shims/native_lib/trace/child.rs +++ b/src/tools/miri/src/shims/native_lib/trace/child.rs @@ -31,7 +31,7 @@ pub struct Supervisor { /// Used for synchronisation, allowing us to receive confirmation that the /// parent process has handled the request from `message_tx`. confirm_rx: ipc::IpcReceiver, - /// Receiver for memory acceses that ocurred during the FFI call. + /// Receiver for memory accesses that occurred during the FFI call. event_rx: ipc::IpcReceiver, } diff --git a/src/tools/miri/src/shims/os_str.rs b/src/tools/miri/src/shims/os_str.rs index 28b03ffb88c61..db9cb3a7a32bd 100644 --- a/src/tools/miri/src/shims/os_str.rs +++ b/src/tools/miri/src/shims/os_str.rs @@ -316,7 +316,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // The new path is still absolute on Windows. path.remove(0); } - // If this starts withs a `\` but not a `\\`, then this was absolute on Unix but is + // If this starts with a `\` but not a `\\`, then this was absolute on Unix but is // relative on Windows (relative to "the root of the current directory", e.g. the // drive letter). else if path.first() == Some(&sep) && path.get(1) != Some(&sep) { diff --git a/src/tools/miri/src/shims/time.rs b/src/tools/miri/src/shims/time.rs index 865a80251e314..2c2b029a1232f 100644 --- a/src/tools/miri/src/shims/time.rs +++ b/src/tools/miri/src/shims/time.rs @@ -401,11 +401,11 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { }; let timeout_anchor = if flags == 0 { - // No flags set, the timespec should be interperted as a duration + // No flags set, the timespec should be interpreted as a duration // to sleep for TimeoutAnchor::Relative } else if flags == this.eval_libc_i32("TIMER_ABSTIME") { - // Only flag TIMER_ABSTIME set, the timespec should be interperted as + // Only flag TIMER_ABSTIME set, the timespec should be interpreted as // an absolute time. TimeoutAnchor::Absolute } else { diff --git a/src/tools/miri/src/shims/unix/foreign_items.rs b/src/tools/miri/src/shims/unix/foreign_items.rs index 87a307c989484..48e2ebd0f13ea 100644 --- a/src/tools/miri/src/shims/unix/foreign_items.rs +++ b/src/tools/miri/src/shims/unix/foreign_items.rs @@ -1045,7 +1045,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { &[Os::Linux, Os::FreeBsd, Os::Illumos, Os::Solaris, Os::Android, Os::MacOs], link_name, )?; - // This function looks and behaves excatly like miri_start_unwind. + // This function looks and behaves exactly like miri_start_unwind. let [payload] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; this.handle_miri_start_unwind(payload)?; return interp_ok(EmulateItemResult::NeedsUnwind); diff --git a/src/tools/miri/src/shims/unix/freebsd/sync.rs b/src/tools/miri/src/shims/unix/freebsd/sync.rs index ae8a167080b90..8cf4464389631 100644 --- a/src/tools/miri/src/shims/unix/freebsd/sync.rs +++ b/src/tools/miri/src/shims/unix/freebsd/sync.rs @@ -169,7 +169,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let Some(futex_ref) = this.get_sync_or_init(obj, |_| FreeBsdFutex { futex: Default::default() }) else { - // From Linux implemenation: + // From Linux implementation: // No AllocId, or no live allocation at that AllocId. // Return an error code. (That seems nicer than silently doing something non-intuitive.) // This means that if an address gets reused by a new allocation, diff --git a/src/tools/miri/src/shims/unix/linux/foreign_items.rs b/src/tools/miri/src/shims/unix/linux/foreign_items.rs index 12cee0d162a0c..b92732de73ca0 100644 --- a/src/tools/miri/src/shims/unix/linux/foreign_items.rs +++ b/src/tools/miri/src/shims/unix/linux/foreign_items.rs @@ -165,7 +165,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { )? { ThreadNameResult::Ok => Scalar::from_u32(0), ThreadNameResult::NameTooLong => this.eval_libc("ERANGE"), - // Act like we faild to open `/proc/self/task/$tid/comm`. + // Act like we failed to open `/proc/self/task/$tid/comm`. ThreadNameResult::ThreadNotFound => this.eval_libc("ENOENT"), }; this.write_scalar(res, dest)?; @@ -186,7 +186,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { )? { ThreadNameResult::Ok => Scalar::from_u32(0), ThreadNameResult::NameTooLong => unreachable!(), - // Act like we faild to open `/proc/self/task/$tid/comm`. + // Act like we failed to open `/proc/self/task/$tid/comm`. ThreadNameResult::ThreadNotFound => this.eval_libc("ENOENT"), } } else { diff --git a/src/tools/miri/src/shims/unix/linux_like/epoll.rs b/src/tools/miri/src/shims/unix/linux_like/epoll.rs index ff5367ea87ab9..7480db00d6ed3 100644 --- a/src/tools/miri/src/shims/unix/linux_like/epoll.rs +++ b/src/tools/miri/src/shims/unix/linux_like/epoll.rs @@ -176,7 +176,7 @@ impl EpollInterestTable { if let Some(epolls) = self.0.remove(&id) { for epoll in epolls.iter().filter_map(|(_id, epoll)| epoll.upgrade()) { // This is a still-live epoll with interest in this FD. Remove all - // relevent interests (including from the ready set). + // relevant interests (including from the ready set). epoll .interest_list .borrow_mut() diff --git a/src/tools/miri/src/shims/unix/macos/sync.rs b/src/tools/miri/src/shims/unix/macos/sync.rs index be32ca9abd597..6ee9ffaf37762 100644 --- a/src/tools/miri/src/shims/unix/macos/sync.rs +++ b/src/tools/miri/src/shims/unix/macos/sync.rs @@ -169,7 +169,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let is_shared = flags == shared; let timeout = clock_timeout.map(|(_, anchor, timeout)| { - // The only clock that is currenlty supported is the monotonic clock. + // The only clock that is currently supported is the monotonic clock. // While the deadline argument of `os_sync_wait_on_address_with_deadline` // is actually not in nanoseconds but in the units of `mach_current_time`, // the two are equivalent in miri. diff --git a/src/tools/miri/src/shims/windows/foreign_items.rs b/src/tools/miri/src/shims/windows/foreign_items.rs index 0bdf6bb785056..2d1a153d9262e 100644 --- a/src/tools/miri/src/shims/windows/foreign_items.rs +++ b/src/tools/miri/src/shims/windows/foreign_items.rs @@ -1199,7 +1199,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { "`_Unwind_RaiseException` is not supported on non-MinGW Windows", ); } - // This function looks and behaves excatly like miri_start_unwind. + // This function looks and behaves exactly like miri_start_unwind. let [payload] = this.check_shim_sig( shim_sig!(extern "C" fn(*mut _) -> unwind::libunwind::_Unwind_Reason_Code), link_name, diff --git a/src/tools/miri/src/shims/windows/sync.rs b/src/tools/miri/src/shims/windows/sync.rs index db1860bdfd309..14562450e6e07 100644 --- a/src/tools/miri/src/shims/windows/sync.rs +++ b/src/tools/miri/src/shims/windows/sync.rs @@ -67,7 +67,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { ) } - /// Returns `true` if we were succssful, `false` if we would block. + /// Returns `true` if we were successful, `false` if we would block. fn init_once_try_begin( &mut self, init_once_ref: &InitOnceRef, diff --git a/src/tools/miri/src/shims/x86/avx2.rs b/src/tools/miri/src/shims/x86/avx2.rs index 7d8e52db73d64..bddb9d47457ca 100644 --- a/src/tools/miri/src/shims/x86/avx2.rs +++ b/src/tools/miri/src/shims/x86/avx2.rs @@ -194,7 +194,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Used to implement the _mm256_sign_epi{8,16,32} functions. // Negates elements from `left` when the corresponding element in // `right` is negative. If an element from `right` is zero, zero - // is writen to the corresponding output element. + // is written to the corresponding output element. // Basically, we multiply `left` with `right.signum()`. "psign.b" | "psign.w" | "psign.d" => { let [left, right] = diff --git a/src/tools/miri/src/shims/x86/bmi.rs b/src/tools/miri/src/shims/x86/bmi.rs index 814823d2acb17..877ecf319ca40 100644 --- a/src/tools/miri/src/shims/x86/bmi.rs +++ b/src/tools/miri/src/shims/x86/bmi.rs @@ -44,7 +44,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let right = if is_64_bit { right.to_u64()? } else { u64::from(right.to_u32()?) }; let result = match unprefixed_name { - // Extract a contigous range of bits from an unsigned integer. + // Extract a contiguous range of bits from an unsigned integer. // https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_bextr_u32 "bextr" => { let start = u32::try_from(right & 0xff).unwrap(); diff --git a/src/tools/miri/src/shims/x86/sse.rs b/src/tools/miri/src/shims/x86/sse.rs index 309fbb61de5a7..7da7a0b57c907 100644 --- a/src/tools/miri/src/shims/x86/sse.rs +++ b/src/tools/miri/src/shims/x86/sse.rs @@ -24,8 +24,8 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Prefix should have already been checked. let unprefixed_name = link_name.as_str().strip_prefix("llvm.x86.sse.").unwrap(); // All these intrinsics operate on 128-bit (f32x4) SIMD vectors unless stated otherwise. - // Many intrinsic names are sufixed with "ps" (packed single) or "ss" (scalar single), - // where single means single precision floating point (f32). "ps" means thet the operation + // Many intrinsic names are suffixed with "ps" (packed single) or "ss" (scalar single), + // where single means single precision floating point (f32). "ps" means that the operation // is performed on each element of the vector, while "ss" means that the operation is // performed only on the first element, copying the remaining elements from the input // vector (for binary operations, from the left-hand side). diff --git a/src/tools/miri/src/shims/x86/sse2.rs b/src/tools/miri/src/shims/x86/sse2.rs index f712814a5eda8..1a33f4c70fd28 100644 --- a/src/tools/miri/src/shims/x86/sse2.rs +++ b/src/tools/miri/src/shims/x86/sse2.rs @@ -26,14 +26,14 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // These intrinsics operate on 128-bit (f32x4, f64x2, i8x16, i16x8, i32x4, i64x2) SIMD // vectors unless stated otherwise. - // Many intrinsic names are sufixed with "ps" (packed single), "ss" (scalar signle), + // Many intrinsic names are suffixed with "ps" (packed single), "ss" (scalar single), // "pd" (packed double) or "sd" (scalar double), where single means single precision // floating point (f32) and double means double precision floating point (f64). "ps" - // and "pd" means thet the operation is performed on each element of the vector, while + // and "pd" means that the operation is performed on each element of the vector, while // "ss" and "sd" means that the operation is performed only on the first element, copying // the remaining elements from the input vector (for binary operations, from the left-hand // side). - // Intrinsincs sufixed with "epiX" or "epuX" operate with X-bit signed or unsigned + // Intrinsics suffixed with "epiX" or "epuX" operate with X-bit signed or unsigned // vectors. match unprefixed_name { // Used to implement the _mm_sad_epu8 function. diff --git a/src/tools/miri/src/shims/x86/sse41.rs b/src/tools/miri/src/shims/x86/sse41.rs index 1e8b0f34428d1..c5a4a98ba881b 100644 --- a/src/tools/miri/src/shims/x86/sse41.rs +++ b/src/tools/miri/src/shims/x86/sse41.rs @@ -115,7 +115,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { round_all::(this, op, rounding, dest)?; } // Used to implement the _mm_minpos_epu16 function. - // Find the minimum unsinged 16-bit integer in `op` and + // Find the minimum unsigned 16-bit integer in `op` and // returns its value and position. "phminposuw" => { let [op] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; diff --git a/src/tools/miri/src/shims/x86/sse42.rs b/src/tools/miri/src/shims/x86/sse42.rs index aa8aea3558832..7c0f9c570e2ef 100644 --- a/src/tools/miri/src/shims/x86/sse42.rs +++ b/src/tools/miri/src/shims/x86/sse42.rs @@ -213,7 +213,7 @@ fn deconstruct_args<'tcx>( }; // The fourth letter of each string comparison intrinsic is either 'e' for "explicit" or 'i' for "implicit". - // The distinction will correspond to the intrinsics type signature. In this constext, "explicit" and "implicit" + // The distinction will correspond to the intrinsics type signature. In this context, "explicit" and "implicit" // refer to the way the string length is determined. The length is either passed explicitly in the "explicit" // case or determined by a null terminator in the "implicit" case. let is_explicit = match unprefixed_name.as_bytes().get(4) { @@ -297,7 +297,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { deconstruct_args(unprefixed_name, this, link_name, abi, args)?; let mask = compare_strings(this, &str1, &str2, len, imm)?; - // The sixth bit inside the immediate byte distiguishes + // The sixth bit inside the immediate byte distinguishes // between a bit mask or a byte mask when generating a mask. if imm & 0b100_0000 != 0 { let (array_layout, size) = if imm & USE_WORDS != 0 { @@ -347,7 +347,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let mask = compare_strings(this, &str1, &str2, len, imm)?; let len = default_len::(imm); - // The sixth bit inside the immediate byte distiguishes between the least + // The sixth bit inside the immediate byte distinguishes between the least // significant bit and the most significant bit when generating an index. let result = if imm & 0b100_0000 != 0 { // most significant bit diff --git a/src/tools/miri/src/shims/x86/ssse3.rs b/src/tools/miri/src/shims/x86/ssse3.rs index b01a8795b4d13..7c1d15ff1265d 100644 --- a/src/tools/miri/src/shims/x86/ssse3.rs +++ b/src/tools/miri/src/shims/x86/ssse3.rs @@ -68,7 +68,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Used to implement the _mm_sign_epi{8,16,32} functions. // Negates elements from `left` when the corresponding element in // `right` is negative. If an element from `right` is zero, zero - // is writen to the corresponding output element. + // is written to the corresponding output element. // Basically, we multiply `left` with `right.signum()`. "psign.b.128" | "psign.w.128" | "psign.d.128" => { let [left, right] = From ba33e03f9cd684adad8966c79fd73f9aa6c475b5 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Thu, 5 Feb 2026 08:58:36 +0100 Subject: [PATCH 027/103] pass -Zunstable-options to tests --- src/tools/miri/cargo-miri/src/phases.rs | 1 + src/tools/miri/ci/ci.sh | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/tools/miri/cargo-miri/src/phases.rs b/src/tools/miri/cargo-miri/src/phases.rs index 0babccb40d33c..567d51c0b31e7 100644 --- a/src/tools/miri/cargo-miri/src/phases.rs +++ b/src/tools/miri/cargo-miri/src/phases.rs @@ -312,6 +312,7 @@ pub fn phase_rustc(args: impl Iterator, phase: RustcPhase) { // Ask rustc for the filename (since that is target-dependent). let mut rustc = miri_for_host(); // sysroot doesn't matter for this so we just use the host rustc.arg("--print").arg("file-names"); + rustc.arg("-Zunstable-options"); // needed for JSON targets for flag in ["--crate-name", "--crate-type", "--target"] { for val in get_arg_flag_values(flag) { rustc.arg(flag).arg(val); diff --git a/src/tools/miri/ci/ci.sh b/src/tools/miri/ci/ci.sh index 53a7b3c047fa9..6c0bceac7731f 100755 --- a/src/tools/miri/ci/ci.sh +++ b/src/tools/miri/ci/ci.sh @@ -173,7 +173,7 @@ case $HOST_TARGET in # Host MIR_OPT=1 MANY_SEEDS=64 TEST_BENCH=1 CARGO_MIRI_ENV=1 run_tests # Custom target JSON file - TEST_TARGET=tests/x86_64-unknown-kernel.json MIRI_NO_STD=1 run_tests_minimal no_std + TEST_TARGET=tests/x86_64-unknown-kernel.json MIRI_NO_STD=1 MIRIFLAGS="-Zunstable-options" run_tests_minimal no_std # Not officially supported tier 2 MANY_SEEDS=16 TEST_TARGET=x86_64-pc-solaris run_tests ;; From 2a6b9e6befac091246ae2a73d0d5f775f8cb04ca Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Tue, 3 Feb 2026 23:28:15 +0200 Subject: [PATCH 028/103] inline the binding --- compiler/rustc_expand/src/config.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/compiler/rustc_expand/src/config.rs b/compiler/rustc_expand/src/config.rs index c9d0319a97c5d..c94a45ec92486 100644 --- a/compiler/rustc_expand/src/config.rs +++ b/compiler/rustc_expand/src/config.rs @@ -86,8 +86,7 @@ pub fn features(sess: &Session, krate_attrs: &[Attribute], crate_name: Symbol) - if let Some(f) = REMOVED_LANG_FEATURES.iter().find(|f| name == f.feature.name) { let pull_note = if let Some(pull) = f.pull { format!( - "; see for more information", - pull + "; see for more information", ) } else { "".to_owned() From 193bc70101fc8c34e81bfdb5711c6a6aa1c35729 Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Tue, 3 Feb 2026 23:28:59 +0200 Subject: [PATCH 029/103] missing word --- compiler/rustc_expand/src/config.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/rustc_expand/src/config.rs b/compiler/rustc_expand/src/config.rs index c94a45ec92486..e4d734086a54f 100644 --- a/compiler/rustc_expand/src/config.rs +++ b/compiler/rustc_expand/src/config.rs @@ -122,7 +122,7 @@ pub fn features(sess: &Session, krate_attrs: &[Attribute], crate_name: Symbol) - // If the enabled feature is unstable, record it. if UNSTABLE_LANG_FEATURES.iter().find(|f| name == f.name).is_some() { - // When the ICE comes a standard library crate, there's a chance that the person + // When the ICE comes from a standard library crate, there's a chance that the person // hitting the ICE may be using -Zbuild-std or similar with an untested target. // The bug is probably in the standard library and not the compiler in that case, // but that doesn't really matter - we want a bug report. From 85f4694b7d2497fb944bb993fdb7aa5a922a0e6e Mon Sep 17 00:00:00 2001 From: The Miri Cronjob Bot Date: Fri, 6 Feb 2026 05:15:37 +0000 Subject: [PATCH 030/103] Prepare for merging from rust-lang/rust This updates the rust-version file to f889772d6500faebcac5bb70fa44b5e6581c38cd. --- src/tools/miri/rust-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/miri/rust-version b/src/tools/miri/rust-version index c122f58ec34ac..e51d39de3c1b3 100644 --- a/src/tools/miri/rust-version +++ b/src/tools/miri/rust-version @@ -1 +1 @@ -9f4b56a5aed81e8c36cc26b3c1b4666ead6b71fc +f889772d6500faebcac5bb70fa44b5e6581c38cd From 5b6813879702bedea495119c4b132671f0790aca Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Tue, 3 Feb 2026 23:31:43 +0200 Subject: [PATCH 031/103] add trailing commas --- rust-bors.toml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/rust-bors.toml b/rust-bors.toml index 9b632481c7c3f..0efbb5e8fc61c 100644 --- a/rust-bors.toml +++ b/rust-bors.toml @@ -25,7 +25,7 @@ labels_blocking_approval = [ "S-waiting-on-t-rustdoc-frontend", "S-waiting-on-t-clippy", # PR manually set to blocked - "S-blocked" + "S-blocked", ] # If CI runs quicker than this duration, consider it to be a failure @@ -41,7 +41,7 @@ approved = [ "-S-waiting-on-author", "-S-waiting-on-crater", "-S-waiting-on-review", - "-S-waiting-on-team" + "-S-waiting-on-team", ] unapproved = [ "+S-waiting-on-author", @@ -49,16 +49,16 @@ unapproved = [ "-S-waiting-on-bors", "-S-waiting-on-crater", "-S-waiting-on-review", - "-S-waiting-on-team" + "-S-waiting-on-team", ] try_failed = [ "+S-waiting-on-author", "-S-waiting-on-review", - "-S-waiting-on-crater" + "-S-waiting-on-crater", ] auto_build_succeeded = [ "+merged-by-bors", - "-S-waiting-on-bors" + "-S-waiting-on-bors", ] auto_build_failed = [ "+S-waiting-on-review", @@ -66,5 +66,5 @@ auto_build_failed = [ "-S-waiting-on-bors", "-S-waiting-on-author", "-S-waiting-on-crater", - "-S-waiting-on-team" + "-S-waiting-on-team", ] From 6bb8cc619e908f32f451f685d8ee928840eaa6a8 Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Tue, 3 Feb 2026 23:33:56 +0200 Subject: [PATCH 032/103] obsolete comment `since` is now a field of one of the parameters --- compiler/rustc_feature/src/unstable.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs index c99af9658cdef..a0cb99f9ea4a3 100644 --- a/compiler/rustc_feature/src/unstable.rs +++ b/compiler/rustc_feature/src/unstable.rs @@ -64,8 +64,6 @@ pub struct EnabledLibFeature { } impl Features { - /// `since` should be set for stable features that are nevertheless enabled with a `#[feature]` - /// attribute, indicating since when they are stable. pub fn set_enabled_lang_feature(&mut self, lang_feat: EnabledLangFeature) { self.enabled_lang_features.push(lang_feat); self.enabled_features.insert(lang_feat.gate_name); From 638d23ce4203c0064c8849ce6f265a74cb68bc7d Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Thu, 5 Feb 2026 14:03:10 +0200 Subject: [PATCH 033/103] E0570: improve text --- compiler/rustc_error_codes/src/error_codes/E0570.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_error_codes/src/error_codes/E0570.md b/compiler/rustc_error_codes/src/error_codes/E0570.md index 355e71ffb4329..e8a7200c1d052 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0570.md +++ b/compiler/rustc_error_codes/src/error_codes/E0570.md @@ -1,7 +1,7 @@ The requested ABI is unsupported by the current target. -The rust compiler maintains for each target a list of unsupported ABIs on -that target. If an ABI is present in such a list this usually means that the +The Rust compiler maintains a list of unsupported ABIs for each target. +If an ABI is present in such a list, this usually means that the target / ABI combination is currently unsupported by llvm. If necessary, you can circumvent this check using custom target specifications. From 84741ed8ce900c5fab9984fe8d152cbff2af1809 Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Thu, 5 Feb 2026 14:03:49 +0200 Subject: [PATCH 034/103] add reading pauses in doc comment --- compiler/rustc_lint_defs/src/builtin.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs index f4e6e93356c75..d792e936ef341 100644 --- a/compiler/rustc_lint_defs/src/builtin.rs +++ b/compiler/rustc_lint_defs/src/builtin.rs @@ -3629,10 +3629,10 @@ declare_lint! { /// `stdcall`, `fastcall`, and `cdecl` calling conventions (or their unwind /// variants) on targets that cannot meaningfully be supported for the requested target. /// - /// For example `stdcall` does not make much sense for a x86_64 or, more apparently, powerpc + /// For example, `stdcall` does not make much sense for a x86_64 or, more apparently, powerpc /// code, because this calling convention was never specified for those targets. /// - /// Historically MSVC toolchains have fallen back to the regular C calling convention for + /// Historically, MSVC toolchains have fallen back to the regular C calling convention for /// targets other than x86, but Rust doesn't really see a similar need to introduce a similar /// hack across many more targets. /// @@ -3659,7 +3659,7 @@ declare_lint! { /// /// ### Explanation /// - /// On most of the targets the behaviour of `stdcall` and similar calling conventions is not + /// On most of the targets, the behaviour of `stdcall` and similar calling conventions is not /// defined at all, but was previously accepted due to a bug in the implementation of the /// compiler. pub UNSUPPORTED_CALLING_CONVENTIONS, From 3be2843107838f3198820cf0c33f9c1d2216196d Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Fri, 6 Feb 2026 05:26:43 +0200 Subject: [PATCH 035/103] make docs render better by separating intro from rest of paragraph --- compiler/rustc_feature/src/unstable.rs | 5 ++-- compiler/rustc_type_ir/src/predicate.rs | 13 ++++++++-- compiler/rustc_type_ir/src/ty_kind.rs | 34 ++++++++++++++++++------- 3 files changed, 39 insertions(+), 13 deletions(-) diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs index a0cb99f9ea4a3..2c7c6dbdb9e8d 100644 --- a/compiler/rustc_feature/src/unstable.rs +++ b/compiler/rustc_feature/src/unstable.rs @@ -769,8 +769,9 @@ impl Features { } } -/// Some features are not allowed to be used together at the same time, if -/// the two are present, produce an error. +/// Some features are not allowed to be used together at the same time. +/// +/// If the two are present, produce an error. pub const INCOMPATIBLE_FEATURES: &[(Symbol, Symbol)] = &[ // Experimental match ergonomics rulesets are incompatible with each other, to simplify the // boolean logic required to tell which typing rules to use. diff --git a/compiler/rustc_type_ir/src/predicate.rs b/compiler/rustc_type_ir/src/predicate.rs index 42d204a8b0941..ab4677fa0a5fb 100644 --- a/compiler/rustc_type_ir/src/predicate.rs +++ b/compiler/rustc_type_ir/src/predicate.rs @@ -42,7 +42,9 @@ where } } -/// A complete reference to a trait. These take numerous guises in syntax, +/// A complete reference to a trait. +/// +/// These take numerous guises in syntax, /// but perhaps the most recognizable form is in a where-clause: /// ```ignore (illustrative) /// T: Foo @@ -241,7 +243,9 @@ impl ImplPolarity { } } -/// Polarity for a trait predicate. May either be negative or positive. +/// Polarity for a trait predicate. +/// +/// May either be negative or positive. /// Distinguished from [`ImplPolarity`] since we never compute goals with /// "reservation" level. #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] @@ -327,6 +331,7 @@ impl ty::Binder> { } /// An existential reference to a trait, where `Self` is erased. +/// /// For example, the trait object `Trait<'a, 'b, X, Y>` is: /// ```ignore (illustrative) /// exists T. T: Trait<'a, 'b, X, Y> @@ -442,6 +447,7 @@ impl ExistentialProjection { } /// Extracts the underlying existential trait reference from this projection. + /// /// For example, if this is a projection of `exists T. ::Item == X`, /// then this function would return an `exists T. T: Iterator` existential trait /// reference. @@ -493,14 +499,17 @@ impl ty::Binder> { #[cfg_attr(feature = "nightly", derive(Encodable, Decodable, HashStable_NoContext))] pub enum AliasTermKind { /// A projection `::AssocType`. + /// /// Can get normalized away if monomorphic enough. ProjectionTy, /// An associated type in an inherent `impl` InherentTy, /// An opaque type (usually from `impl Trait` in type aliases or function return types) + /// /// Can only be normalized away in PostAnalysis mode or its defining scope. OpaqueTy, /// A free type alias that actually checks its trait bounds. + /// /// Currently only used if the type alias references opaque types. /// Can always be normalized away. FreeTy, diff --git a/compiler/rustc_type_ir/src/ty_kind.rs b/compiler/rustc_type_ir/src/ty_kind.rs index 7cb71387e8680..983d8f0820b6b 100644 --- a/compiler/rustc_type_ir/src/ty_kind.rs +++ b/compiler/rustc_type_ir/src/ty_kind.rs @@ -29,14 +29,17 @@ mod closure; )] pub enum AliasTyKind { /// A projection `::AssocType`. + /// /// Can get normalized away if monomorphic enough. Projection, /// An associated type in an inherent `impl` Inherent, /// An opaque type (usually from `impl Trait` in type aliases or function return types) + /// /// Can only be normalized away in PostAnalysis mode or its defining scope. Opaque, /// A type alias that actually checks its trait bounds. + /// /// Currently only used if the type alias references opaque types. /// Can always be normalized away. Free, @@ -99,7 +102,9 @@ pub enum TyKind { /// An array with the given length. Written as `[T; N]`. Array(I::Ty, I::Const), - /// A pattern newtype. Takes any type and restricts its valid values to its pattern. + /// A pattern newtype. + /// + /// Takes any type and restricts its valid values to its pattern. /// This will also change the layout to take advantage of this restriction. /// Only `Copy` and `Clone` will automatically get implemented for pattern types. /// Auto-traits treat this as if it were an aggregate with a single nested type. @@ -116,8 +121,9 @@ pub enum TyKind { /// `&'a mut T` or `&'a T`. Ref(I::Region, I::Ty, Mutability), - /// The anonymous type of a function declaration/definition. Each - /// function has a unique type. + /// The anonymous type of a function declaration/definition. + /// + /// Each function has a unique type. /// /// For the function `fn foo() -> i32 { 3 }` this type would be /// shown to the user as `fn() -> i32 {foo}`. @@ -129,7 +135,9 @@ pub enum TyKind { /// ``` FnDef(I::FunctionId, I::GenericArgs), - /// A pointer to a function. Written as `fn() -> i32`. + /// A pointer to a function. + /// + /// Written as `fn() -> i32`. /// /// Note that both functions and closures start out as either /// [FnDef] or [Closure] which can be then be coerced to this variant. @@ -179,6 +187,7 @@ pub enum TyKind { Coroutine(I::CoroutineId, I::GenericArgs), /// A type representing the types stored inside a coroutine. + /// /// This should only appear as part of the `CoroutineArgs`. /// /// Unlike upvars, the witness can reference lifetimes from @@ -210,6 +219,7 @@ pub enum TyKind { Tuple(I::Tys), /// A projection, opaque type, free type alias, or inherent associated type. + /// /// All of these types are represented as pairs of def-id and args, and can /// be normalized, so they are grouped conceptually. Alias(AliasTyKind, AliasTy), @@ -253,8 +263,9 @@ pub enum TyKind { /// inside of the type. Infer(InferTy), - /// A placeholder for a type which could not be computed; this is - /// propagated to avoid useless error messages. + /// A placeholder for a type which could not be computed. + /// + /// This is propagated to avoid useless error messages. Error(I::ErrorGuaranteed), } @@ -282,7 +293,9 @@ impl TyKind { } /// Returns `true` when the outermost type cannot be further normalized, - /// resolved, or instantiated. This includes all primitive types, but also + /// resolved, or instantiated. + /// + /// This includes all primitive types, but also /// things like ADTs and trait objects, since even if their arguments or /// nested types may be further simplified, the outermost [`ty::TyKind`] or /// type constructor remains the same. @@ -481,6 +494,7 @@ impl AliasTy { } /// Extracts the underlying trait reference and own args from this projection. + /// /// For example, if this is a projection of `::Item<'a>`, /// then this function would return a `T: StreamingIterator` trait reference and /// `['a]` as the own args. @@ -490,6 +504,7 @@ impl AliasTy { } /// Extracts the underlying trait reference from this projection. + /// /// For example, if this is a projection of `::Item`, /// then this function would return a `T: Iterator` trait reference. /// @@ -593,8 +608,9 @@ pub enum InferTy { FloatVar(FloatVid), /// A [`FreshTy`][Self::FreshTy] is one that is generated as a replacement - /// for an unbound type variable. This is convenient for caching etc. See - /// `TypeFreshener` for more details. + /// for an unbound type variable. + /// + /// This is convenient for caching etc. See `TypeFreshener` for more details. /// /// Compare with [`TyVar`][Self::TyVar]. FreshTy(u32), From f605c0ab7c66506172600ad37a05be3603a20dd2 Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Sun, 8 Feb 2026 22:14:06 +0530 Subject: [PATCH 036/103] self_param and impl_ implementation in syntax_factory --- .../src/ast/syntax_factory/constructors.rs | 59 +++++++++++++++++++ 1 file changed, 59 insertions(+) diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/syntax_factory/constructors.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/syntax_factory/constructors.rs index 5fe419ad4eb79..ad9b9054a8f52 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/ast/syntax_factory/constructors.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/ast/syntax_factory/constructors.rs @@ -1590,6 +1590,65 @@ impl SyntaxFactory { ast } + pub fn self_param(&self) -> ast::SelfParam { + let ast = make::self_param().clone_for_update(); + + if let Some(mut mapping) = self.mappings() { + let builder = SyntaxMappingBuilder::new(ast.syntax().clone()); + builder.finish(&mut mapping); + } + + ast + } + + pub fn impl_( + &self, + attrs: impl IntoIterator, + generic_params: Option, + generic_args: Option, + path_type: ast::Type, + where_clause: Option, + body: Option, + ) -> ast::Impl { + let (attrs, attrs_input) = iterator_input(attrs); + let ast = make::impl_( + attrs, + generic_params.clone(), + generic_args.clone(), + path_type.clone(), + where_clause.clone(), + body.clone(), + ) + .clone_for_update(); + + if let Some(mut mapping) = self.mappings() { + let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); + builder.map_children(attrs_input, ast.attrs().map(|attr| attr.syntax().clone())); + if let Some(generic_params) = generic_params { + builder.map_node( + generic_params.syntax().clone(), + ast.generic_param_list().unwrap().syntax().clone(), + ); + } + builder.map_node(path_type.syntax().clone(), ast.self_ty().unwrap().syntax().clone()); + if let Some(where_clause) = where_clause { + builder.map_node( + where_clause.syntax().clone(), + ast.where_clause().unwrap().syntax().clone(), + ); + } + if let Some(body) = body { + builder.map_node( + body.syntax().clone(), + ast.assoc_item_list().unwrap().syntax().clone(), + ); + } + builder.finish(&mut mapping); + } + + ast + } + pub fn ret_type(&self, ty: ast::Type) -> ast::RetType { let ast = make::ret_type(ty.clone()).clone_for_update(); From fa31e1759a65a3689a6a9ed23eff65da9cc8ce76 Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Sun, 8 Feb 2026 22:14:30 +0530 Subject: [PATCH 037/103] migrate generate_getter_or_setter to syntaxeditor api --- .../src/handlers/generate_getter_or_setter.rs | 161 +++++++++++------- 1 file changed, 100 insertions(+), 61 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_getter_or_setter.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_getter_or_setter.rs index 73e93a3fbf526..2db5e46bb093e 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_getter_or_setter.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_getter_or_setter.rs @@ -2,13 +2,16 @@ use ide_db::{famous_defs::FamousDefs, source_change::SourceChangeBuilder}; use stdx::{format_to, to_lower_snake_case}; use syntax::{ TextRange, - ast::{self, AstNode, HasName, HasVisibility, edit_in_place::Indent, make}, - ted, + ast::{ + self, AstNode, HasGenericParams, HasName, HasVisibility, edit_in_place::Indent, + syntax_factory::SyntaxFactory, + }, + syntax_editor::Position, }; use crate::{ AssistContext, AssistId, Assists, GroupLabel, - utils::{convert_reference_type, find_struct_impl, generate_impl}, + utils::{convert_reference_type, find_struct_impl}, }; // Assist: generate_setter @@ -215,12 +218,14 @@ fn generate_getter_from_info( ctx: &AssistContext<'_>, info: &AssistInfo, record_field_info: &RecordFieldInfo, + syntax_factory: &SyntaxFactory, ) -> ast::Fn { let (ty, body) = if matches!(info.assist_type, AssistType::MutGet) { + let self_expr = syntax_factory.expr_path(syntax_factory.ident_path("self")); ( - make::ty_ref(record_field_info.field_ty.clone(), true), - make::expr_ref( - make::expr_field(make::ext::expr_self(), &record_field_info.field_name.text()), + syntax_factory.ty_ref(record_field_info.field_ty.clone(), true), + syntax_factory.expr_ref( + syntax_factory.expr_field(self_expr, &record_field_info.field_name.text()).into(), true, ), ) @@ -241,9 +246,14 @@ fn generate_getter_from_info( })() .unwrap_or_else(|| { ( - make::ty_ref(record_field_info.field_ty.clone(), false), - make::expr_ref( - make::expr_field(make::ext::expr_self(), &record_field_info.field_name.text()), + syntax_factory.ty_ref(record_field_info.field_ty.clone(), false), + syntax_factory.expr_ref( + syntax_factory + .expr_field( + syntax_factory.expr_path(syntax_factory.ident_path("self")), + &record_field_info.field_name.text(), + ) + .into(), false, ), ) @@ -251,18 +261,18 @@ fn generate_getter_from_info( }; let self_param = if matches!(info.assist_type, AssistType::MutGet) { - make::mut_self_param() + syntax_factory.mut_self_param() } else { - make::self_param() + syntax_factory.self_param() }; let strukt = &info.strukt; - let fn_name = make::name(&record_field_info.fn_name); - let params = make::param_list(Some(self_param), []); - let ret_type = Some(make::ret_type(ty)); - let body = make::block_expr([], Some(body)); + let fn_name = syntax_factory.name(&record_field_info.fn_name); + let params = syntax_factory.param_list(Some(self_param), []); + let ret_type = Some(syntax_factory.ret_type(ty)); + let body = syntax_factory.block_expr([], Some(body)); - make::fn_( + syntax_factory.fn_( None, strukt.visibility(), fn_name, @@ -278,28 +288,35 @@ fn generate_getter_from_info( ) } -fn generate_setter_from_info(info: &AssistInfo, record_field_info: &RecordFieldInfo) -> ast::Fn { +fn generate_setter_from_info( + info: &AssistInfo, + record_field_info: &RecordFieldInfo, + syntax_factory: &SyntaxFactory, +) -> ast::Fn { let strukt = &info.strukt; let field_name = &record_field_info.fn_name; - let fn_name = make::name(&format!("set_{field_name}")); + let fn_name = syntax_factory.name(&format!("set_{field_name}")); let field_ty = &record_field_info.field_ty; // Make the param list // `(&mut self, $field_name: $field_ty)` - let field_param = - make::param(make::ident_pat(false, false, make::name(field_name)).into(), field_ty.clone()); - let params = make::param_list(Some(make::mut_self_param()), [field_param]); + let field_param = syntax_factory.param( + syntax_factory.ident_pat(false, false, syntax_factory.name(field_name)).into(), + field_ty.clone(), + ); + let params = syntax_factory.param_list(Some(syntax_factory.mut_self_param()), [field_param]); // Make the assignment body // `self.$field_name = $field_name` - let self_expr = make::ext::expr_self(); - let lhs = make::expr_field(self_expr, field_name); - let rhs = make::expr_path(make::ext::ident_path(field_name)); - let assign_stmt = make::expr_stmt(make::expr_assignment(lhs, rhs).into()); - let body = make::block_expr([assign_stmt.into()], None); + let self_expr = syntax_factory.expr_path(syntax_factory.ident_path("self")); + let lhs = syntax_factory.expr_field(self_expr, field_name); + let rhs = syntax_factory.expr_path(syntax_factory.ident_path(field_name)); + let assign_stmt = + syntax_factory.expr_stmt(syntax_factory.expr_assignment(lhs.into(), rhs).into()); + let body = syntax_factory.block_expr([assign_stmt.into()], None); // Make the setter fn - make::fn_( + syntax_factory.fn_( None, strukt.visibility(), fn_name, @@ -403,47 +420,69 @@ fn build_source_change( info_of_record_fields: Vec, assist_info: AssistInfo, ) { - let record_fields_count = info_of_record_fields.len(); - - let impl_def = if let Some(impl_def) = &assist_info.impl_def { - // We have an existing impl to add to - builder.make_mut(impl_def.clone()) - } else { - // Generate a new impl to add the methods to - let impl_def = generate_impl(&ast::Adt::Struct(assist_info.strukt.clone())); - - // Insert it after the adt - let strukt = builder.make_mut(assist_info.strukt.clone()); - - ted::insert_all_raw( - ted::Position::after(strukt.syntax()), - vec![make::tokens::blank_line().into(), impl_def.syntax().clone().into()], - ); - - impl_def - }; + let syntax_factory = SyntaxFactory::without_mappings(); - let assoc_item_list = impl_def.get_or_create_assoc_item_list(); + let items: Vec = info_of_record_fields + .iter() + .map(|record_field_info| { + let method = match assist_info.assist_type { + AssistType::Set => { + generate_setter_from_info(&assist_info, record_field_info, &syntax_factory) + } + _ => { + generate_getter_from_info(ctx, &assist_info, record_field_info, &syntax_factory) + } + }; + let new_fn = method.clone_for_update(); + new_fn.indent(1.into()); + new_fn.into() + }) + .collect(); - for (i, record_field_info) in info_of_record_fields.iter().enumerate() { - // Make the new getter or setter fn - let new_fn = match assist_info.assist_type { - AssistType::Set => generate_setter_from_info(&assist_info, record_field_info), - _ => generate_getter_from_info(ctx, &assist_info, record_field_info), - } - .clone_for_update(); - new_fn.indent(1.into()); + if let Some(impl_def) = &assist_info.impl_def { + // We have an existing impl to add to + let mut editor = builder.make_editor(impl_def.syntax()); + impl_def.assoc_item_list().unwrap().add_items(&mut editor, items.clone()); - // Insert a tabstop only for last method we generate - if i == record_fields_count - 1 - && let Some(cap) = ctx.config.snippet_cap - && let Some(name) = new_fn.name() + if let Some(cap) = ctx.config.snippet_cap + && let Some(ast::AssocItem::Fn(fn_)) = items.last() + && let Some(name) = fn_.name() { - builder.add_tabstop_before(cap, name); + let tabstop = builder.make_tabstop_before(cap); + editor.add_annotation(name.syntax(), tabstop); } - assoc_item_list.add_item(new_fn.clone().into()); + builder.add_file_edits(ctx.vfs_file_id(), editor); + return; } + let ty_params = assist_info.strukt.generic_param_list(); + let ty_args = ty_params.as_ref().map(|it| it.to_generic_args()); + let impl_def = syntax_factory.impl_( + None, + ty_params, + ty_args, + syntax_factory + .ty_path(syntax_factory.ident_path(&assist_info.strukt.name().unwrap().to_string())) + .into(), + None, + Some(syntax_factory.assoc_item_list(items)), + ); + let mut editor = builder.make_editor(assist_info.strukt.syntax()); + editor.insert_all( + Position::after(assist_info.strukt.syntax()), + vec![syntax_factory.whitespace("\n\n").into(), impl_def.syntax().clone().into()], + ); + + if let Some(cap) = ctx.config.snippet_cap + && let Some(assoc_list) = impl_def.assoc_item_list() + && let Some(ast::AssocItem::Fn(fn_)) = assoc_list.assoc_items().last() + && let Some(name) = fn_.name() + { + let tabstop = builder.make_tabstop_before(cap); + editor.add_annotation(name.syntax().clone(), tabstop); + } + + builder.add_file_edits(ctx.vfs_file_id(), editor); } #[cfg(test)] From 59111f7a73fda12e0401cb35ccab7fbcd80ad272 Mon Sep 17 00:00:00 2001 From: Vincent Esche Date: Wed, 4 Feb 2026 09:01:24 +0100 Subject: [PATCH 038/103] Add `expression_types()`, `pattern_types()`, `binding_types()` to `DefWithBody` --- src/tools/rust-analyzer/crates/hir/src/lib.rs | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/src/tools/rust-analyzer/crates/hir/src/lib.rs b/src/tools/rust-analyzer/crates/hir/src/lib.rs index 4b615665167c0..0167ccaa1002a 100644 --- a/src/tools/rust-analyzer/crates/hir/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir/src/lib.rs @@ -2244,6 +2244,39 @@ impl DefWithBody { acc.push(diag.into()) } } + + /// Returns an iterator over the inferred types of all expressions in this body. + pub fn expression_types<'db>( + self, + db: &'db dyn HirDatabase, + ) -> impl Iterator> { + self.id().into_iter().flat_map(move |def_id| { + let infer = InferenceResult::for_body(db, def_id); + let resolver = def_id.resolver(db); + + infer.expression_types().map(move |(_, ty)| Type::new_with_resolver(db, &resolver, ty)) + }) + } + + /// Returns an iterator over the inferred types of all patterns in this body. + pub fn pattern_types<'db>(self, db: &'db dyn HirDatabase) -> impl Iterator> { + self.id().into_iter().flat_map(move |def_id| { + let infer = InferenceResult::for_body(db, def_id); + let resolver = def_id.resolver(db); + + infer.pattern_types().map(move |(_, ty)| Type::new_with_resolver(db, &resolver, ty)) + }) + } + + /// Returns an iterator over the inferred types of all bindings in this body. + pub fn binding_types<'db>(self, db: &'db dyn HirDatabase) -> impl Iterator> { + self.id().into_iter().flat_map(move |def_id| { + let infer = InferenceResult::for_body(db, def_id); + let resolver = def_id.resolver(db); + + infer.binding_types().map(move |(_, ty)| Type::new_with_resolver(db, &resolver, ty)) + }) + } } fn expr_store_diagnostics<'db>( From ae24fdaccc71600ec540ff018208c5d592ad016a Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Mon, 9 Feb 2026 12:45:01 +0100 Subject: [PATCH 039/103] fix: Fix `set_top_subtree_delimiter_span` using wrong index for close span --- src/tools/rust-analyzer/crates/tt/src/storage.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/rust-analyzer/crates/tt/src/storage.rs b/src/tools/rust-analyzer/crates/tt/src/storage.rs index 4dd02d875a29e..50a1106175ab3 100644 --- a/src/tools/rust-analyzer/crates/tt/src/storage.rs +++ b/src/tools/rust-analyzer/crates/tt/src/storage.rs @@ -488,7 +488,7 @@ impl TopSubtree { unreachable!() }; *open_span = S::new(span.open.range, 0); - *close_span = S::new(span.close.range, 0); + *close_span = S::new(span.close.range, 1); } dispatch! { match &mut self.repr => tt => do_it(tt, span) From e7855f5bfaf5d0d568f1fe4d9c07aec1144f405d Mon Sep 17 00:00:00 2001 From: Wilfred Hughes Date: Tue, 3 Feb 2026 15:59:46 +0000 Subject: [PATCH 040/103] internal: Run clippy as a separate CI step Currently clippy is run in CI as part of the macOS build. This is a little confusing, because clippy failures just show as "Rust (macos-latest)" which make it look like a macOS build failure. Instead, treat clippy as a separate build step, like miri and rustfmt. This should also make CI a little faster, because it reduces macOS runner usage (which tend to be slower than Linux on GitHub actions), and it reduces the number of steps where we need to install clippy. --- .../rust-analyzer/.github/workflows/ci.yaml | 30 +++++++++++++++---- .../crates/hir-ty/src/mir/eval.rs | 2 +- 2 files changed, 25 insertions(+), 7 deletions(-) diff --git a/src/tools/rust-analyzer/.github/workflows/ci.yaml b/src/tools/rust-analyzer/.github/workflows/ci.yaml index 1a0deee564aeb..c3a5017d72d73 100644 --- a/src/tools/rust-analyzer/.github/workflows/ci.yaml +++ b/src/tools/rust-analyzer/.github/workflows/ci.yaml @@ -96,7 +96,7 @@ jobs: run: | rustup update --no-self-update stable rustup default stable - rustup component add --toolchain stable rust-src clippy rustfmt + rustup component add --toolchain stable rust-src rustfmt # We also install a nightly rustfmt, because we use `--file-lines` in # a test. rustup toolchain install nightly --profile minimal --component rustfmt @@ -128,10 +128,6 @@ jobs: - name: Run cargo-machete run: cargo machete - - name: Run Clippy - if: matrix.os == 'macos-latest' - run: cargo clippy --all-targets -- -D clippy::disallowed_macros -D clippy::dbg_macro -D clippy::todo -D clippy::print_stdout -D clippy::print_stderr - analysis-stats: if: github.repository == 'rust-lang/rust-analyzer' runs-on: ubuntu-latest @@ -178,6 +174,28 @@ jobs: - run: cargo fmt -- --check + clippy: + if: github.repository == 'rust-lang/rust-analyzer' + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + # Note that clippy output is currently dependent on whether rust-src is installed, + # https://github.com/rust-lang/rust-clippy/issues/14625 + - name: Install Rust toolchain + run: | + rustup update --no-self-update stable + rustup default stable + rustup component add --toolchain stable rust-src clippy + + # https://github.com/actions-rust-lang/setup-rust-toolchain/blob/main/rust.json + - name: Install Rust Problem Matcher + run: echo "::add-matcher::.github/rust.json" + + - run: cargo clippy --all-targets -- -D clippy::disallowed_macros -D clippy::dbg_macro -D clippy::todo -D clippy::print_stdout -D clippy::print_stderr + miri: if: github.repository == 'rust-lang/rust-analyzer' runs-on: ubuntu-latest @@ -309,7 +327,7 @@ jobs: run: typos conclusion: - needs: [rust, rust-cross, typescript, typo-check, proc-macro-srv, miri, rustfmt, analysis-stats] + needs: [rust, rust-cross, typescript, typo-check, proc-macro-srv, miri, rustfmt, clippy, analysis-stats] # We need to ensure this job does *not* get skipped if its dependencies fail, # because a skipped job is considered a success by GitHub. So we have to # overwrite `if:`. We use `!cancelled()` to ensure the job does still not get run diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs index 5de08313f418d..ec0723c3f8c3f 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs @@ -1625,7 +1625,7 @@ impl<'db> Evaluator<'db> { }; match target_ty { rustc_type_ir::FloatTy::F32 => Owned((value as f32).to_le_bytes().to_vec()), - rustc_type_ir::FloatTy::F64 => Owned((value as f64).to_le_bytes().to_vec()), + rustc_type_ir::FloatTy::F64 => Owned(value.to_le_bytes().to_vec()), rustc_type_ir::FloatTy::F16 | rustc_type_ir::FloatTy::F128 => { not_supported!("unstable floating point type f16 and f128"); } From 6494dabc33a27dafd61b98dca55915b5cb7f60de Mon Sep 17 00:00:00 2001 From: Shoyu Vanilla Date: Tue, 10 Feb 2026 00:57:41 +0900 Subject: [PATCH 041/103] fix: Sync `allow_normalize` to rustc --- .../hir-ty/src/next_solver/predicate.rs | 7 +- .../crates/hir-ty/src/tests/regression.rs | 70 +++++++++++++++++++ .../crates/test-utils/src/minicore.rs | 24 ++++++- 3 files changed, 97 insertions(+), 4 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/predicate.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/predicate.rs index 6f4fae7073178..8658d03a9e3e8 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/predicate.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/predicate.rs @@ -714,9 +714,9 @@ impl<'db> rustc_type_ir::inherent::Predicate> for Predicate<'db> fn allow_normalization(self) -> bool { // TODO: this should probably live in rustc_type_ir match self.inner().as_ref().skip_binder() { - PredicateKind::Clause(ClauseKind::WellFormed(_)) - | PredicateKind::AliasRelate(..) - | PredicateKind::NormalizesTo(..) => false, + PredicateKind::Clause(ClauseKind::WellFormed(_)) | PredicateKind::AliasRelate(..) => { + false + } PredicateKind::Clause(ClauseKind::Trait(_)) | PredicateKind::Clause(ClauseKind::RegionOutlives(_)) | PredicateKind::Clause(ClauseKind::TypeOutlives(_)) @@ -729,6 +729,7 @@ impl<'db> rustc_type_ir::inherent::Predicate> for Predicate<'db> | PredicateKind::Coerce(_) | PredicateKind::Clause(ClauseKind::ConstEvaluatable(_)) | PredicateKind::ConstEquate(_, _) + | PredicateKind::NormalizesTo(..) | PredicateKind::Ambiguous => true, } } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs index 3b5b4e4fa5404..fba582f880fde 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs @@ -2688,3 +2688,73 @@ pub trait FilterT = Self> { "#, ); } + +#[test] +fn regression_21605() { + check_infer( + r#" +//- minicore: fn, coerce_unsized, dispatch_from_dyn, iterator, iterators +pub struct Filter<'a, 'b, T> +where + T: 'b, + 'a: 'b, +{ + filter_fn: dyn Fn(&'a T) -> bool, + t: Option, + b: &'b (), +} + +impl<'a, 'b, T> Filter<'a, 'b, T> +where + T: 'b, + 'a: 'b, +{ + pub fn new(filter_fn: dyn Fn(&T) -> bool) -> Self { + Self { + filter_fn: filter_fn, + t: None, + b: &(), + } + } +} + +pub trait FilterExt { + type Output; + fn filter(&self, filter: &Filter) -> Self::Output; +} + +impl FilterExt for [T; N] +where + T: IntoIterator, +{ + type Output = T; + fn filter(&self, filter: &Filter) -> Self::Output { + let _ = self.into_iter().filter(filter.filter_fn); + loop {} + } +} +"#, + expect![[r#" + 214..223 'filter_fn': dyn Fn(&'? T) -> bool + 'static + 253..360 '{ ... }': Filter<'a, 'b, T> + 263..354 'Self {... }': Filter<'a, 'b, T> + 293..302 'filter_fn': dyn Fn(&'? T) -> bool + 'static + 319..323 'None': Option + 340..343 '&()': &'? () + 341..343 '()': () + 421..425 'self': &'? Self + 427..433 'filter': &'? Filter<'?, '?, T> + 580..584 'self': &'? [T; N] + 586..592 'filter': &'? Filter<'?, '?, T> + 622..704 '{ ... }': T + 636..637 '_': Filter, dyn Fn(&'? T) -> bool + '?> + 640..644 'self': &'? [T; N] + 640..656 'self.i...iter()': Iter<'?, T> + 640..681 'self.i...er_fn)': Filter, dyn Fn(&'? T) -> bool + '?> + 664..670 'filter': &'? Filter<'?, '?, T> + 664..680 'filter...ter_fn': dyn Fn(&'? T) -> bool + 'static + 691..698 'loop {}': ! + 696..698 '{}': () + "#]], + ); +} diff --git a/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs b/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs index 7d95043867ee6..c34475bbdf012 100644 --- a/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs +++ b/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs @@ -1689,6 +1689,21 @@ pub mod iter { } } + pub struct Filter { + iter: I, + predicate: P, + } + impl Iterator for Filter + where + P: FnMut(&I::Item) -> bool, + { + type Item = I::Item; + + fn next(&mut self) -> Option { + loop {} + } + } + pub struct FilterMap { iter: I, f: F, @@ -1705,7 +1720,7 @@ pub mod iter { } } } - pub use self::adapters::{FilterMap, Take}; + pub use self::adapters::{Filter, FilterMap, Take}; mod sources { mod repeat { @@ -1756,6 +1771,13 @@ pub mod iter { { loop {} } + fn filter

(self, predicate: P) -> crate::iter::Filter + where + Self: Sized, + P: FnMut(&Self::Item) -> bool, + { + loop {} + } fn filter_map(self, _f: F) -> crate::iter::FilterMap where Self: Sized, From 5a721a117ff8c94dd5083b28027dfa6b83a3aa72 Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Tue, 10 Feb 2026 04:13:05 +0800 Subject: [PATCH 042/103] Improve variable name --- src/tools/rust-analyzer/crates/ide-assists/src/utils.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs b/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs index 1732d8c2018e2..dd4bbd145537a 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs @@ -767,7 +767,7 @@ fn generate_impl_inner( }); let generic_args = generic_params.as_ref().map(|params| params.to_generic_args().clone_for_update()); - let trait_where_clause = trait_ + let adt_assoc_bounds = trait_ .as_ref() .zip(generic_params.as_ref()) .and_then(|(trait_, params)| generic_param_associated_bounds(adt, trait_, params)); @@ -787,7 +787,7 @@ fn generate_impl_inner( false, trait_, ty, - trait_where_clause, + adt_assoc_bounds, adt.where_clause(), body, ), From 78c8ab35e8713f77191c0af3d3e6c12b0a304b2c Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Wed, 11 Feb 2026 13:39:13 +0530 Subject: [PATCH 043/103] migrate destructure tuple binding to new syntaxEditor --- .../src/handlers/destructure_tuple_binding.rs | 85 +++++---- .../ide-assists/src/utils/ref_field_expr.rs | 18 +- .../crates/syntax/src/ast/edit_in_place.rs | 173 ++++++++++++------ 3 files changed, 181 insertions(+), 95 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/destructure_tuple_binding.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/destructure_tuple_binding.rs index e2afc0bf130ee..b8dc59f87dee7 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/destructure_tuple_binding.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/destructure_tuple_binding.rs @@ -8,8 +8,8 @@ use ide_db::{ use itertools::Itertools; use syntax::{ T, - ast::{self, AstNode, FieldExpr, HasName, IdentPat, make}, - ted, + ast::{self, AstNode, FieldExpr, HasName, IdentPat, syntax_factory::SyntaxFactory}, + syntax_editor::{Position, SyntaxEditor}, }; use crate::{ @@ -89,13 +89,20 @@ fn destructure_tuple_edit_impl( data: &TupleData, in_sub_pattern: bool, ) { - let assignment_edit = edit_tuple_assignment(ctx, edit, data, in_sub_pattern); - let current_file_usages_edit = edit_tuple_usages(data, edit, ctx, in_sub_pattern); + let mut syntax_editor = edit.make_editor(data.ident_pat.syntax()); + let syntax_factory = SyntaxFactory::with_mappings(); - assignment_edit.apply(); + let assignment_edit = + edit_tuple_assignment(ctx, edit, &mut syntax_editor, &syntax_factory, data, in_sub_pattern); + let current_file_usages_edit = edit_tuple_usages(data, ctx, &syntax_factory, in_sub_pattern); + + assignment_edit.apply(&mut syntax_editor, &syntax_factory); if let Some(usages_edit) = current_file_usages_edit { - usages_edit.into_iter().for_each(|usage_edit| usage_edit.apply(edit)) + usages_edit.into_iter().for_each(|usage_edit| usage_edit.apply(edit, &mut syntax_editor)) } + + syntax_editor.add_mappings(syntax_factory.finish_with_mappings()); + edit.add_file_edits(ctx.vfs_file_id(), syntax_editor); } fn collect_data(ident_pat: IdentPat, ctx: &AssistContext<'_>) -> Option { @@ -165,11 +172,11 @@ struct TupleData { fn edit_tuple_assignment( ctx: &AssistContext<'_>, edit: &mut SourceChangeBuilder, + editor: &mut SyntaxEditor, + make: &SyntaxFactory, data: &TupleData, in_sub_pattern: bool, ) -> AssignmentEdit { - let ident_pat = edit.make_mut(data.ident_pat.clone()); - let tuple_pat = { let original = &data.ident_pat; let is_ref = original.ref_token().is_some(); @@ -177,10 +184,11 @@ fn edit_tuple_assignment( let fields = data .field_names .iter() - .map(|name| ast::Pat::from(make::ident_pat(is_ref, is_mut, make::name(name)))); - make::tuple_pat(fields).clone_for_update() + .map(|name| ast::Pat::from(make.ident_pat(is_ref, is_mut, make.name(name)))); + make.tuple_pat(fields) }; - let is_shorthand_field = ident_pat + let is_shorthand_field = data + .ident_pat .name() .as_ref() .and_then(ast::RecordPatField::for_field_name) @@ -189,14 +197,20 @@ fn edit_tuple_assignment( if let Some(cap) = ctx.config.snippet_cap { // place cursor on first tuple name if let Some(ast::Pat::IdentPat(first_pat)) = tuple_pat.fields().next() { - edit.add_tabstop_before( - cap, - first_pat.name().expect("first ident pattern should have a name"), - ) + let annotation = edit.make_tabstop_before(cap); + editor.add_annotation( + first_pat.name().expect("first ident pattern should have a name").syntax(), + annotation, + ); } } - AssignmentEdit { ident_pat, tuple_pat, in_sub_pattern, is_shorthand_field } + AssignmentEdit { + ident_pat: data.ident_pat.clone(), + tuple_pat, + in_sub_pattern, + is_shorthand_field, + } } struct AssignmentEdit { ident_pat: ast::IdentPat, @@ -206,23 +220,30 @@ struct AssignmentEdit { } impl AssignmentEdit { - fn apply(self) { + fn apply(self, syntax_editor: &mut SyntaxEditor, syntax_mapping: &SyntaxFactory) { // with sub_pattern: keep original tuple and add subpattern: `tup @ (_0, _1)` if self.in_sub_pattern { - self.ident_pat.set_pat(Some(self.tuple_pat.into())) + self.ident_pat.set_pat_with_editor( + Some(self.tuple_pat.into()), + syntax_editor, + syntax_mapping, + ) } else if self.is_shorthand_field { - ted::insert(ted::Position::after(self.ident_pat.syntax()), self.tuple_pat.syntax()); - ted::insert_raw(ted::Position::after(self.ident_pat.syntax()), make::token(T![:])); + syntax_editor.insert(Position::after(self.ident_pat.syntax()), self.tuple_pat.syntax()); + syntax_editor + .insert(Position::after(self.ident_pat.syntax()), syntax_mapping.whitespace(" ")); + syntax_editor + .insert(Position::after(self.ident_pat.syntax()), syntax_mapping.token(T![:])); } else { - ted::replace(self.ident_pat.syntax(), self.tuple_pat.syntax()) + syntax_editor.replace(self.ident_pat.syntax(), self.tuple_pat.syntax()) } } } fn edit_tuple_usages( data: &TupleData, - edit: &mut SourceChangeBuilder, ctx: &AssistContext<'_>, + make: &SyntaxFactory, in_sub_pattern: bool, ) -> Option> { // We need to collect edits first before actually applying them @@ -238,20 +259,20 @@ fn edit_tuple_usages( .as_ref()? .as_slice() .iter() - .filter_map(|r| edit_tuple_usage(ctx, edit, r, data, in_sub_pattern)) + .filter_map(|r| edit_tuple_usage(ctx, make, r, data, in_sub_pattern)) .collect_vec(); Some(edits) } fn edit_tuple_usage( ctx: &AssistContext<'_>, - builder: &mut SourceChangeBuilder, + make: &SyntaxFactory, usage: &FileReference, data: &TupleData, in_sub_pattern: bool, ) -> Option { match detect_tuple_index(usage, data) { - Some(index) => Some(edit_tuple_field_usage(ctx, builder, data, index)), + Some(index) => Some(edit_tuple_field_usage(ctx, make, data, index)), None if in_sub_pattern => { cov_mark::hit!(destructure_tuple_call_with_subpattern); None @@ -262,20 +283,18 @@ fn edit_tuple_usage( fn edit_tuple_field_usage( ctx: &AssistContext<'_>, - builder: &mut SourceChangeBuilder, + make: &SyntaxFactory, data: &TupleData, index: TupleIndex, ) -> EditTupleUsage { let field_name = &data.field_names[index.index]; - let field_name = make::expr_path(make::ext::ident_path(field_name)); + let field_name = make.expr_path(make.ident_path(field_name)); if data.ref_type.is_some() { let (replace_expr, ref_data) = determine_ref_and_parens(ctx, &index.field_expr); - let replace_expr = builder.make_mut(replace_expr); - EditTupleUsage::ReplaceExpr(replace_expr, ref_data.wrap_expr(field_name)) + EditTupleUsage::ReplaceExpr(replace_expr, ref_data.wrap_expr_with_factory(field_name, make)) } else { - let field_expr = builder.make_mut(index.field_expr); - EditTupleUsage::ReplaceExpr(field_expr.into(), field_name) + EditTupleUsage::ReplaceExpr(index.field_expr.into(), field_name) } } enum EditTupleUsage { @@ -291,14 +310,14 @@ enum EditTupleUsage { } impl EditTupleUsage { - fn apply(self, edit: &mut SourceChangeBuilder) { + fn apply(self, edit: &mut SourceChangeBuilder, syntax_editor: &mut SyntaxEditor) { match self { EditTupleUsage::NoIndex(range) => { edit.insert(range.start(), "/*"); edit.insert(range.end(), "*/"); } EditTupleUsage::ReplaceExpr(target_expr, replace_with) => { - ted::replace(target_expr.syntax(), replace_with.clone_for_update().syntax()) + syntax_editor.replace(target_expr.syntax(), replace_with.syntax()) } } } diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/utils/ref_field_expr.rs b/src/tools/rust-analyzer/crates/ide-assists/src/utils/ref_field_expr.rs index 840b26a7ad58b..df8ad4111234e 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/utils/ref_field_expr.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/utils/ref_field_expr.rs @@ -5,7 +5,7 @@ //! based on the parent of the existing expression. use syntax::{ AstNode, T, - ast::{self, FieldExpr, MethodCallExpr, make}, + ast::{self, FieldExpr, MethodCallExpr, make, syntax_factory::SyntaxFactory}, }; use crate::AssistContext; @@ -130,4 +130,20 @@ impl RefData { expr } + + pub(crate) fn wrap_expr_with_factory( + &self, + mut expr: ast::Expr, + syntax_factory: &SyntaxFactory, + ) -> ast::Expr { + if self.needs_deref { + expr = syntax_factory.expr_prefix(T![*], expr).into(); + } + + if self.needs_parentheses { + expr = syntax_factory.expr_paren(expr).into(); + } + + expr + } } diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/edit_in_place.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/edit_in_place.rs index 1cd8146f68630..2b7dc5cd76ab5 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/ast/edit_in_place.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/ast/edit_in_place.rs @@ -9,8 +9,9 @@ use crate::{ SyntaxKind::{ATTR, COMMENT, WHITESPACE}, SyntaxNode, SyntaxToken, algo::{self, neighbor}, - ast::{self, HasGenericParams, edit::IndentLevel, make}, - ted::{self, Position}, + ast::{self, HasGenericParams, edit::IndentLevel, make, syntax_factory::SyntaxFactory}, + syntax_editor::{Position, SyntaxEditor}, + ted, }; use super::{GenericParam, HasName}; @@ -26,13 +27,13 @@ impl GenericParamsOwnerEdit for ast::Fn { Some(it) => it, None => { let position = if let Some(name) = self.name() { - Position::after(name.syntax) + ted::Position::after(name.syntax) } else if let Some(fn_token) = self.fn_token() { - Position::after(fn_token) + ted::Position::after(fn_token) } else if let Some(param_list) = self.param_list() { - Position::before(param_list.syntax) + ted::Position::before(param_list.syntax) } else { - Position::last_child_of(self.syntax()) + ted::Position::last_child_of(self.syntax()) }; create_generic_param_list(position) } @@ -42,11 +43,11 @@ impl GenericParamsOwnerEdit for ast::Fn { fn get_or_create_where_clause(&self) -> ast::WhereClause { if self.where_clause().is_none() { let position = if let Some(ty) = self.ret_type() { - Position::after(ty.syntax()) + ted::Position::after(ty.syntax()) } else if let Some(param_list) = self.param_list() { - Position::after(param_list.syntax()) + ted::Position::after(param_list.syntax()) } else { - Position::last_child_of(self.syntax()) + ted::Position::last_child_of(self.syntax()) }; create_where_clause(position); } @@ -60,8 +61,8 @@ impl GenericParamsOwnerEdit for ast::Impl { Some(it) => it, None => { let position = match self.impl_token() { - Some(imp_token) => Position::after(imp_token), - None => Position::last_child_of(self.syntax()), + Some(imp_token) => ted::Position::after(imp_token), + None => ted::Position::last_child_of(self.syntax()), }; create_generic_param_list(position) } @@ -71,8 +72,8 @@ impl GenericParamsOwnerEdit for ast::Impl { fn get_or_create_where_clause(&self) -> ast::WhereClause { if self.where_clause().is_none() { let position = match self.assoc_item_list() { - Some(items) => Position::before(items.syntax()), - None => Position::last_child_of(self.syntax()), + Some(items) => ted::Position::before(items.syntax()), + None => ted::Position::last_child_of(self.syntax()), }; create_where_clause(position); } @@ -86,11 +87,11 @@ impl GenericParamsOwnerEdit for ast::Trait { Some(it) => it, None => { let position = if let Some(name) = self.name() { - Position::after(name.syntax) + ted::Position::after(name.syntax) } else if let Some(trait_token) = self.trait_token() { - Position::after(trait_token) + ted::Position::after(trait_token) } else { - Position::last_child_of(self.syntax()) + ted::Position::last_child_of(self.syntax()) }; create_generic_param_list(position) } @@ -100,9 +101,9 @@ impl GenericParamsOwnerEdit for ast::Trait { fn get_or_create_where_clause(&self) -> ast::WhereClause { if self.where_clause().is_none() { let position = match (self.assoc_item_list(), self.semicolon_token()) { - (Some(items), _) => Position::before(items.syntax()), - (_, Some(tok)) => Position::before(tok), - (None, None) => Position::last_child_of(self.syntax()), + (Some(items), _) => ted::Position::before(items.syntax()), + (_, Some(tok)) => ted::Position::before(tok), + (None, None) => ted::Position::last_child_of(self.syntax()), }; create_where_clause(position); } @@ -116,11 +117,11 @@ impl GenericParamsOwnerEdit for ast::TypeAlias { Some(it) => it, None => { let position = if let Some(name) = self.name() { - Position::after(name.syntax) + ted::Position::after(name.syntax) } else if let Some(trait_token) = self.type_token() { - Position::after(trait_token) + ted::Position::after(trait_token) } else { - Position::last_child_of(self.syntax()) + ted::Position::last_child_of(self.syntax()) }; create_generic_param_list(position) } @@ -130,10 +131,10 @@ impl GenericParamsOwnerEdit for ast::TypeAlias { fn get_or_create_where_clause(&self) -> ast::WhereClause { if self.where_clause().is_none() { let position = match self.eq_token() { - Some(tok) => Position::before(tok), + Some(tok) => ted::Position::before(tok), None => match self.semicolon_token() { - Some(tok) => Position::before(tok), - None => Position::last_child_of(self.syntax()), + Some(tok) => ted::Position::before(tok), + None => ted::Position::last_child_of(self.syntax()), }, }; create_where_clause(position); @@ -148,11 +149,11 @@ impl GenericParamsOwnerEdit for ast::Struct { Some(it) => it, None => { let position = if let Some(name) = self.name() { - Position::after(name.syntax) + ted::Position::after(name.syntax) } else if let Some(struct_token) = self.struct_token() { - Position::after(struct_token) + ted::Position::after(struct_token) } else { - Position::last_child_of(self.syntax()) + ted::Position::last_child_of(self.syntax()) }; create_generic_param_list(position) } @@ -166,13 +167,13 @@ impl GenericParamsOwnerEdit for ast::Struct { ast::FieldList::TupleFieldList(it) => Some(it), }); let position = if let Some(tfl) = tfl { - Position::after(tfl.syntax()) + ted::Position::after(tfl.syntax()) } else if let Some(gpl) = self.generic_param_list() { - Position::after(gpl.syntax()) + ted::Position::after(gpl.syntax()) } else if let Some(name) = self.name() { - Position::after(name.syntax()) + ted::Position::after(name.syntax()) } else { - Position::last_child_of(self.syntax()) + ted::Position::last_child_of(self.syntax()) }; create_where_clause(position); } @@ -186,11 +187,11 @@ impl GenericParamsOwnerEdit for ast::Enum { Some(it) => it, None => { let position = if let Some(name) = self.name() { - Position::after(name.syntax) + ted::Position::after(name.syntax) } else if let Some(enum_token) = self.enum_token() { - Position::after(enum_token) + ted::Position::after(enum_token) } else { - Position::last_child_of(self.syntax()) + ted::Position::last_child_of(self.syntax()) }; create_generic_param_list(position) } @@ -200,11 +201,11 @@ impl GenericParamsOwnerEdit for ast::Enum { fn get_or_create_where_clause(&self) -> ast::WhereClause { if self.where_clause().is_none() { let position = if let Some(gpl) = self.generic_param_list() { - Position::after(gpl.syntax()) + ted::Position::after(gpl.syntax()) } else if let Some(name) = self.name() { - Position::after(name.syntax()) + ted::Position::after(name.syntax()) } else { - Position::last_child_of(self.syntax()) + ted::Position::last_child_of(self.syntax()) }; create_where_clause(position); } @@ -212,12 +213,12 @@ impl GenericParamsOwnerEdit for ast::Enum { } } -fn create_where_clause(position: Position) { +fn create_where_clause(position: ted::Position) { let where_clause = make::where_clause(empty()).clone_for_update(); ted::insert(position, where_clause.syntax()); } -fn create_generic_param_list(position: Position) -> ast::GenericParamList { +fn create_generic_param_list(position: ted::Position) -> ast::GenericParamList { let gpl = make::generic_param_list(empty()).clone_for_update(); ted::insert_raw(position, gpl.syntax()); gpl @@ -253,7 +254,7 @@ impl ast::GenericParamList { pub fn add_generic_param(&self, generic_param: ast::GenericParam) { match self.generic_params().last() { Some(last_param) => { - let position = Position::after(last_param.syntax()); + let position = ted::Position::after(last_param.syntax()); let elements = vec![ make::token(T![,]).into(), make::tokens::single_space().into(), @@ -262,7 +263,7 @@ impl ast::GenericParamList { ted::insert_all(position, elements); } None => { - let after_l_angle = Position::after(self.l_angle_token().unwrap()); + let after_l_angle = ted::Position::after(self.l_angle_token().unwrap()); ted::insert(after_l_angle, generic_param.syntax()); } } @@ -412,7 +413,7 @@ impl ast::UseTree { match self.use_tree_list() { Some(it) => it, None => { - let position = Position::last_child_of(self.syntax()); + let position = ted::Position::last_child_of(self.syntax()); let use_tree_list = make::use_tree_list(empty()).clone_for_update(); let mut elements = Vec::with_capacity(2); if self.coloncolon_token().is_none() { @@ -458,7 +459,7 @@ impl ast::UseTree { // Next, transform 'suffix' use tree into 'prefix::{suffix}' let subtree = self.clone_subtree().clone_for_update(); ted::remove_all_iter(self.syntax().children_with_tokens()); - ted::insert(Position::first_child_of(self.syntax()), prefix.syntax()); + ted::insert(ted::Position::first_child_of(self.syntax()), prefix.syntax()); self.get_or_create_use_tree_list().add_use_tree(subtree); fn split_path_prefix(prefix: &ast::Path) -> Option<()> { @@ -507,7 +508,7 @@ impl ast::UseTreeList { pub fn add_use_tree(&self, use_tree: ast::UseTree) { let (position, elements) = match self.use_trees().last() { Some(last_tree) => ( - Position::after(last_tree.syntax()), + ted::Position::after(last_tree.syntax()), vec![ make::token(T![,]).into(), make::tokens::single_space().into(), @@ -516,8 +517,8 @@ impl ast::UseTreeList { ), None => { let position = match self.l_curly_token() { - Some(l_curly) => Position::after(l_curly), - None => Position::last_child_of(self.syntax()), + Some(l_curly) => ted::Position::after(l_curly), + None => ted::Position::last_child_of(self.syntax()), }; (position, vec![use_tree.syntax.into()]) } @@ -582,15 +583,15 @@ impl ast::AssocItemList { let (indent, position, whitespace) = match self.assoc_items().last() { Some(last_item) => ( IndentLevel::from_node(last_item.syntax()), - Position::after(last_item.syntax()), + ted::Position::after(last_item.syntax()), "\n\n", ), None => match self.l_curly_token() { Some(l_curly) => { normalize_ws_between_braces(self.syntax()); - (IndentLevel::from_token(&l_curly) + 1, Position::after(&l_curly), "\n") + (IndentLevel::from_token(&l_curly) + 1, ted::Position::after(&l_curly), "\n") } - None => (IndentLevel::single(), Position::last_child_of(self.syntax()), "\n"), + None => (IndentLevel::single(), ted::Position::last_child_of(self.syntax()), "\n"), }, }; let elements: Vec = vec![ @@ -618,17 +619,17 @@ impl ast::RecordExprFieldList { let position = match self.fields().last() { Some(last_field) => { let comma = get_or_insert_comma_after(last_field.syntax()); - Position::after(comma) + ted::Position::after(comma) } None => match self.l_curly_token() { - Some(it) => Position::after(it), - None => Position::last_child_of(self.syntax()), + Some(it) => ted::Position::after(it), + None => ted::Position::last_child_of(self.syntax()), }, }; ted::insert_all(position, vec![whitespace.into(), field.syntax().clone().into()]); if is_multiline { - ted::insert(Position::after(field.syntax()), ast::make::token(T![,])); + ted::insert(ted::Position::after(field.syntax()), ast::make::token(T![,])); } } } @@ -656,7 +657,7 @@ impl ast::RecordExprField { ast::make::tokens::single_space().into(), expr.syntax().clone().into(), ]; - ted::insert_all_raw(Position::last_child_of(self.syntax()), children); + ted::insert_all_raw(ted::Position::last_child_of(self.syntax()), children); } } } @@ -679,17 +680,17 @@ impl ast::RecordPatFieldList { Some(last_field) => { let syntax = last_field.syntax(); let comma = get_or_insert_comma_after(syntax); - Position::after(comma) + ted::Position::after(comma) } None => match self.l_curly_token() { - Some(it) => Position::after(it), - None => Position::last_child_of(self.syntax()), + Some(it) => ted::Position::after(it), + None => ted::Position::last_child_of(self.syntax()), }, }; ted::insert_all(position, vec![whitespace.into(), field.syntax().clone().into()]); if is_multiline { - ted::insert(Position::after(field.syntax()), ast::make::token(T![,])); + ted::insert(ted::Position::after(field.syntax()), ast::make::token(T![,])); } } } @@ -703,7 +704,7 @@ fn get_or_insert_comma_after(syntax: &SyntaxNode) -> SyntaxToken { Some(it) => it, None => { let comma = ast::make::token(T![,]); - ted::insert(Position::after(syntax), &comma); + ted::insert(ted::Position::after(syntax), &comma); comma } } @@ -728,7 +729,7 @@ fn normalize_ws_between_braces(node: &SyntaxNode) -> Option<()> { } } Some(ws) if ws.kind() == T!['}'] => { - ted::insert(Position::after(l), make::tokens::whitespace(&format!("\n{indent}"))); + ted::insert(ted::Position::after(l), make::tokens::whitespace(&format!("\n{indent}"))); } _ => (), } @@ -780,6 +781,56 @@ impl ast::IdentPat { } } } + + pub fn set_pat_with_editor( + &self, + pat: Option, + syntax_editor: &mut SyntaxEditor, + syntax_factory: &SyntaxFactory, + ) { + match pat { + None => { + if let Some(at_token) = self.at_token() { + // Remove `@ Pat` + let start = at_token.clone().into(); + let end = self + .pat() + .map(|it| it.syntax().clone().into()) + .unwrap_or_else(|| at_token.into()); + syntax_editor.delete_all(start..=end); + + // Remove any trailing ws + if let Some(last) = + self.syntax().last_token().filter(|it| it.kind() == WHITESPACE) + { + last.detach(); + } + } + } + Some(pat) => { + if let Some(old_pat) = self.pat() { + // Replace existing pattern + syntax_editor.replace(old_pat.syntax(), pat.syntax()) + } else if let Some(at_token) = self.at_token() { + // Have an `@` token but not a pattern yet + syntax_editor.insert(Position::after(at_token), pat.syntax()); + } else { + // Don't have an `@`, should have a name + let name = self.name().unwrap(); + + syntax_editor.insert_all( + Position::after(name.syntax()), + vec![ + syntax_factory.whitespace(" ").into(), + syntax_factory.token(T![@]).into(), + syntax_factory.whitespace(" ").into(), + pat.syntax().clone().into(), + ], + ) + } + } + } + } } pub trait HasVisibilityEdit: ast::HasVisibility { From 9c7cc624069425e0acedc7bef2714bb174ccdc2f Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Tue, 10 Feb 2026 17:13:01 +0530 Subject: [PATCH 044/103] make generate_unique_lifetime_param_name return Option --- .../src/handlers/introduce_named_lifetime.rs | 21 +++++++++---------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/introduce_named_lifetime.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/introduce_named_lifetime.rs index 76d47b9276b1a..854e9561d29a4 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/introduce_named_lifetime.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/introduce_named_lifetime.rs @@ -1,7 +1,8 @@ use ide_db::{FileId, FxHashSet}; use syntax::{ - AstNode, T, TextRange, + AstNode, SmolStr, T, TextRange, ToSmolStr, ast::{self, HasGenericParams, HasName, syntax_factory::SyntaxFactory}, + format_smolstr, syntax_editor::{Element, Position, SyntaxEditor}, }; @@ -53,16 +54,14 @@ pub(crate) fn introduce_named_lifetime(acc: &mut Assists, ctx: &AssistContext<'_ /// Given a type parameter list, generate a unique lifetime parameter name /// which is not in the list fn generate_unique_lifetime_param_name( - existing_type_param_list: Option, -) -> Option { - match existing_type_param_list { - Some(type_params) => { - let used_lifetime_params: FxHashSet<_> = - type_params.lifetime_params().map(|p| p.syntax().text().to_string()).collect(); - ('a'..='z').map(|it| format!("'{it}")).find(|it| !used_lifetime_params.contains(it)) - } - None => Some("'a".to_owned()), - } + existing_params: Option, +) -> Option { + let used_lifetime_param: FxHashSet = existing_params + .iter() + .flat_map(|params| params.lifetime_params()) + .map(|p| p.syntax().text().to_smolstr()) + .collect(); + ('a'..='z').map(|c| format_smolstr!("'{c}")).find(|lt| !used_lifetime_param.contains(lt)) } fn generate_fn_def_assist( From fcb881244ba9b1b807a22999dc33b423ade01674 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Wed, 11 Feb 2026 22:43:14 +0100 Subject: [PATCH 045/103] align_strange_enum_discriminant_offset: fix accidentally unused variable --- .../tests/pass/align_strange_enum_discriminant_offset.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/tools/miri/tests/pass/align_strange_enum_discriminant_offset.rs b/src/tools/miri/tests/pass/align_strange_enum_discriminant_offset.rs index e0d05e0b65abf..d8d28bc9a768d 100644 --- a/src/tools/miri/tests/pass/align_strange_enum_discriminant_offset.rs +++ b/src/tools/miri/tests/pass/align_strange_enum_discriminant_offset.rs @@ -1,5 +1,4 @@ -#![allow(unused)] - +#[allow(unused)] #[repr(u16)] enum DeviceKind { Nil = 0, @@ -19,5 +18,5 @@ fn main() { let x = None::<(DeviceInfo, u8)>; let y = None::<(DeviceInfo, u16)>; let z = None::<(DeviceInfo, u64)>; - format!("{} {} {}", x.is_some(), y.is_some(), y.is_some()); + let _out = format!("{} {} {}", x.is_some(), y.is_some(), z.is_some()); } From ecc243b3789c6af15f54bd791d8d3068119e6ed9 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Wed, 11 Feb 2026 22:49:03 +0100 Subject: [PATCH 046/103] remove an unused allow(unused) --- src/tools/miri/tests/pass/async-closure.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/tools/miri/tests/pass/async-closure.rs b/src/tools/miri/tests/pass/async-closure.rs index 1b38f06eb7cd9..5067f1d2d8e78 100644 --- a/src/tools/miri/tests/pass/async-closure.rs +++ b/src/tools/miri/tests/pass/async-closure.rs @@ -1,5 +1,4 @@ #![feature(async_fn_traits)] -#![allow(unused)] use std::future::Future; use std::ops::{AsyncFn, AsyncFnMut, AsyncFnOnce}; From 7077797f520b3a588b05d2aa99a2f41c3814e8c3 Mon Sep 17 00:00:00 2001 From: mu001999 Date: Wed, 28 Jan 2026 21:19:09 +0800 Subject: [PATCH 047/103] Parse and lower final for methods Co-authored-by: Michael Goulet --- compiler/rustc_ast/src/ast.rs | 12 +++++-- compiler/rustc_ast_lowering/src/item.rs | 21 ++++++----- compiler/rustc_ast_passes/src/feature_gate.rs | 1 + .../rustc_ast_pretty/src/pprust/state/item.rs | 4 +-- .../src/alloc_error_handler.rs | 2 +- compiler/rustc_builtin_macros/src/autodiff.rs | 2 +- .../src/deriving/coerce_pointee.rs | 4 +-- .../src/deriving/generic/mod.rs | 6 ++-- .../src/global_allocator.rs | 2 +- compiler/rustc_builtin_macros/src/test.rs | 2 +- .../rustc_builtin_macros/src/test_harness.rs | 2 +- compiler/rustc_expand/src/build.rs | 2 +- compiler/rustc_feature/src/unstable.rs | 2 ++ compiler/rustc_parse/src/errors.rs | 11 ++++++ compiler/rustc_parse/src/parser/item.rs | 35 +++++++++++++------ compiler/rustc_parse/src/parser/token_type.rs | 4 +++ compiler/rustc_span/src/symbol.rs | 1 + tests/ui/coroutine/gen_fn.none.stderr | 4 +-- tests/ui/coroutine/gen_fn.rs | 2 +- ...feature-gate-final-associated-functions.rs | 6 ++++ ...ure-gate-final-associated-functions.stderr | 13 +++++++ tests/ui/parser/duplicate-visibility.rs | 4 +-- tests/ui/parser/duplicate-visibility.stderr | 4 +-- .../parser/misspelled-keywords/pub-fn.stderr | 4 +-- tests/ui/traits/final/final-kw.gated.stderr | 13 +++++++ tests/ui/traits/final/final-kw.rs | 9 +++++ tests/ui/traits/final/final-kw.ungated.stderr | 13 +++++++ 27 files changed, 142 insertions(+), 43 deletions(-) create mode 100644 tests/ui/feature-gates/feature-gate-final-associated-functions.rs create mode 100644 tests/ui/feature-gates/feature-gate-final-associated-functions.stderr create mode 100644 tests/ui/traits/final/final-kw.gated.stderr create mode 100644 tests/ui/traits/final/final-kw.rs create mode 100644 tests/ui/traits/final/final-kw.ungated.stderr diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index fa323b7cf581a..738435891f16f 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -3131,8 +3131,16 @@ pub enum Const { /// For details see the [RFC #2532](https://github.com/rust-lang/rfcs/pull/2532). #[derive(Copy, Clone, PartialEq, Encodable, Decodable, Debug, HashStable_Generic, Walkable)] pub enum Defaultness { + /// Item is unmarked. Implicitly determined based off of position. + /// For impls, this is `final`; for traits, this is `default`. + /// + /// If you're expanding an item in a built-in macro or parsing an item + /// by hand, you probably want to use this. + Implicit, + /// `default` Default(Span), - Final, + /// `final`; per RFC 3678, only trait items may be *explicitly* marked final. + Final(Span), } #[derive(Copy, Clone, PartialEq, Encodable, Decodable, HashStable_Generic, Walkable)] @@ -4140,7 +4148,7 @@ impl AssocItemKind { | Self::Fn(box Fn { defaultness, .. }) | Self::Type(box TyAlias { defaultness, .. }) => defaultness, Self::MacCall(..) | Self::Delegation(..) | Self::DelegationMac(..) => { - Defaultness::Final + Defaultness::Implicit } } } diff --git a/compiler/rustc_ast_lowering/src/item.rs b/compiler/rustc_ast_lowering/src/item.rs index 9922ed8a5c588..8ae117f62786f 100644 --- a/compiler/rustc_ast_lowering/src/item.rs +++ b/compiler/rustc_ast_lowering/src/item.rs @@ -939,7 +939,7 @@ impl<'hir> LoweringContext<'_, 'hir> { ); let trait_item_def_id = hir_id.expect_owner(); - let (ident, generics, kind, has_default) = match &i.kind { + let (ident, generics, kind, has_value) = match &i.kind { AssocItemKind::Const(box ConstItem { ident, generics, @@ -1088,13 +1088,17 @@ impl<'hir> LoweringContext<'_, 'hir> { } }; + let (defaultness, _) = self.lower_defaultness(i.kind.defaultness(), has_value, || { + hir::Defaultness::Default { has_value } + }); + let item = hir::TraitItem { owner_id: trait_item_def_id, ident: self.lower_ident(ident), generics, kind, span: self.lower_span(i.span), - defaultness: hir::Defaultness::Default { has_value: has_default }, + defaultness, has_delayed_lints: !self.delayed_lints.is_empty(), }; self.arena.alloc(item) @@ -1122,7 +1126,8 @@ impl<'hir> LoweringContext<'_, 'hir> { // `defaultness.has_value()` is never called for an `impl`, always `true` in order // to not cause an assertion failure inside the `lower_defaultness` function. let has_val = true; - let (defaultness, defaultness_span) = self.lower_defaultness(defaultness, has_val); + let (defaultness, defaultness_span) = + self.lower_defaultness(defaultness, has_val, || hir::Defaultness::Final); let modifiers = TraitBoundModifiers { constness: BoundConstness::Never, asyncness: BoundAsyncness::Normal, @@ -1151,7 +1156,8 @@ impl<'hir> LoweringContext<'_, 'hir> { ) -> &'hir hir::ImplItem<'hir> { // Since `default impl` is not yet implemented, this is always true in impls. let has_value = true; - let (defaultness, _) = self.lower_defaultness(i.kind.defaultness(), has_value); + let (defaultness, _) = + self.lower_defaultness(i.kind.defaultness(), has_value, || hir::Defaultness::Final); let hir_id = hir::HirId::make_owner(self.current_hir_id_owner.def_id); let attrs = self.lower_attrs( hir_id, @@ -1304,15 +1310,14 @@ impl<'hir> LoweringContext<'_, 'hir> { &self, d: Defaultness, has_value: bool, + implicit: impl FnOnce() -> hir::Defaultness, ) -> (hir::Defaultness, Option) { match d { + Defaultness::Implicit => (implicit(), None), Defaultness::Default(sp) => { (hir::Defaultness::Default { has_value }, Some(self.lower_span(sp))) } - Defaultness::Final => { - assert!(has_value); - (hir::Defaultness::Final, None) - } + Defaultness::Final(sp) => (hir::Defaultness::Final, Some(self.lower_span(sp))), } } diff --git a/compiler/rustc_ast_passes/src/feature_gate.rs b/compiler/rustc_ast_passes/src/feature_gate.rs index eac7f03d8450c..c902550ae1a61 100644 --- a/compiler/rustc_ast_passes/src/feature_gate.rs +++ b/compiler/rustc_ast_passes/src/feature_gate.rs @@ -583,6 +583,7 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session, features: &Features) { gate_all!(frontmatter, "frontmatters are experimental"); gate_all!(coroutines, "coroutine syntax is experimental"); gate_all!(const_block_items, "const block items are experimental"); + gate_all!(final_associated_functions, "`final` on trait functions is experimental"); if !visitor.features.never_patterns() { if let Some(spans) = spans.get(&sym::never_patterns) { diff --git a/compiler/rustc_ast_pretty/src/pprust/state/item.rs b/compiler/rustc_ast_pretty/src/pprust/state/item.rs index c7f110a2e0034..57e105010343c 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state/item.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state/item.rs @@ -51,7 +51,7 @@ impl<'a> State<'a> { expr.as_deref(), vis, *safety, - ast::Defaultness::Final, + ast::Defaultness::Implicit, define_opaque.as_deref(), ), ast::ForeignItemKind::TyAlias(box ast::TyAlias { @@ -201,7 +201,7 @@ impl<'a> State<'a> { body.as_deref(), &item.vis, ast::Safety::Default, - ast::Defaultness::Final, + ast::Defaultness::Implicit, define_opaque.as_deref(), ); } diff --git a/compiler/rustc_builtin_macros/src/alloc_error_handler.rs b/compiler/rustc_builtin_macros/src/alloc_error_handler.rs index c87f1e41043d2..5f78758513e17 100644 --- a/compiler/rustc_builtin_macros/src/alloc_error_handler.rs +++ b/compiler/rustc_builtin_macros/src/alloc_error_handler.rs @@ -83,7 +83,7 @@ fn generate_handler(cx: &ExtCtxt<'_>, handler: Ident, span: Span, sig_span: Span let body = Some(cx.block_expr(call)); let kind = ItemKind::Fn(Box::new(Fn { - defaultness: ast::Defaultness::Final, + defaultness: ast::Defaultness::Implicit, sig, ident: Ident::from_str_and_span(&global_fn_name(ALLOC_ERROR_HANDLER), span), generics: Generics::default(), diff --git a/compiler/rustc_builtin_macros/src/autodiff.rs b/compiler/rustc_builtin_macros/src/autodiff.rs index 95191b82ff3ff..264f797a78250 100644 --- a/compiler/rustc_builtin_macros/src/autodiff.rs +++ b/compiler/rustc_builtin_macros/src/autodiff.rs @@ -334,7 +334,7 @@ mod llvm_enzyme { // The first element of it is the name of the function to be generated let d_fn = Box::new(ast::Fn { - defaultness: ast::Defaultness::Final, + defaultness: ast::Defaultness::Implicit, sig: d_sig, ident: first_ident(&meta_item_vec[0]), generics, diff --git a/compiler/rustc_builtin_macros/src/deriving/coerce_pointee.rs b/compiler/rustc_builtin_macros/src/deriving/coerce_pointee.rs index 8984e51c08447..1d9551f93a14c 100644 --- a/compiler/rustc_builtin_macros/src/deriving/coerce_pointee.rs +++ b/compiler/rustc_builtin_macros/src/deriving/coerce_pointee.rs @@ -136,7 +136,7 @@ pub(crate) fn expand_deriving_coerce_pointee( of_trait: Some(Box::new(ast::TraitImplHeader { safety: ast::Safety::Default, polarity: ast::ImplPolarity::Positive, - defaultness: ast::Defaultness::Final, + defaultness: ast::Defaultness::Implicit, trait_ref, })), constness: ast::Const::No, @@ -159,7 +159,7 @@ pub(crate) fn expand_deriving_coerce_pointee( of_trait: Some(Box::new(ast::TraitImplHeader { safety: ast::Safety::Default, polarity: ast::ImplPolarity::Positive, - defaultness: ast::Defaultness::Final, + defaultness: ast::Defaultness::Implicit, trait_ref, })), constness: ast::Const::No, diff --git a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs index 3fe5e89ef06db..5362bcde1aad8 100644 --- a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs +++ b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs @@ -614,7 +614,7 @@ impl<'a> TraitDef<'a> { }, attrs: ast::AttrVec::new(), kind: ast::AssocItemKind::Type(Box::new(ast::TyAlias { - defaultness: ast::Defaultness::Final, + defaultness: ast::Defaultness::Implicit, ident, generics: Generics::default(), after_where_clause: ast::WhereClause::default(), @@ -851,7 +851,7 @@ impl<'a> TraitDef<'a> { of_trait: Some(Box::new(ast::TraitImplHeader { safety: self.safety, polarity: ast::ImplPolarity::Positive, - defaultness: ast::Defaultness::Final, + defaultness: ast::Defaultness::Implicit, trait_ref, })), constness: if self.is_const { ast::Const::Yes(DUMMY_SP) } else { ast::Const::No }, @@ -1073,7 +1073,7 @@ impl<'a> MethodDef<'a> { let trait_lo_sp = span.shrink_to_lo(); let sig = ast::FnSig { header: ast::FnHeader::default(), decl: fn_decl, span }; - let defaultness = ast::Defaultness::Final; + let defaultness = ast::Defaultness::Implicit; // Create the method. Box::new(ast::AssocItem { diff --git a/compiler/rustc_builtin_macros/src/global_allocator.rs b/compiler/rustc_builtin_macros/src/global_allocator.rs index 9e22d408c125a..74c13f0fc2fbe 100644 --- a/compiler/rustc_builtin_macros/src/global_allocator.rs +++ b/compiler/rustc_builtin_macros/src/global_allocator.rs @@ -77,7 +77,7 @@ impl AllocFnFactory<'_, '_> { let sig = FnSig { decl, header, span: self.span }; let body = Some(self.cx.block_expr(result)); let kind = ItemKind::Fn(Box::new(Fn { - defaultness: ast::Defaultness::Final, + defaultness: ast::Defaultness::Implicit, sig, ident: Ident::from_str_and_span(&global_fn_name(method.name), self.span), generics: Generics::default(), diff --git a/compiler/rustc_builtin_macros/src/test.rs b/compiler/rustc_builtin_macros/src/test.rs index a9718d53ac491..45d5daf913275 100644 --- a/compiler/rustc_builtin_macros/src/test.rs +++ b/compiler/rustc_builtin_macros/src/test.rs @@ -283,7 +283,7 @@ pub(crate) fn expand_test_or_bench( // const $ident: test::TestDescAndFn = ast::ItemKind::Const( ast::ConstItem { - defaultness: ast::Defaultness::Final, + defaultness: ast::Defaultness::Implicit, ident: Ident::new(fn_.ident.name, sp), generics: ast::Generics::default(), ty: cx.ty(sp, ast::TyKind::Path(None, test_path("TestDescAndFn"))), diff --git a/compiler/rustc_builtin_macros/src/test_harness.rs b/compiler/rustc_builtin_macros/src/test_harness.rs index 8d6969b0ca125..8b89d06c9ec61 100644 --- a/compiler/rustc_builtin_macros/src/test_harness.rs +++ b/compiler/rustc_builtin_macros/src/test_harness.rs @@ -329,7 +329,7 @@ fn mk_main(cx: &mut TestCtxt<'_>) -> Box { let decl = ecx.fn_decl(ThinVec::new(), ast::FnRetTy::Ty(main_ret_ty)); let sig = ast::FnSig { decl, header: ast::FnHeader::default(), span: sp }; - let defaultness = ast::Defaultness::Final; + let defaultness = ast::Defaultness::Implicit; // Honor the reexport_test_harness_main attribute let main_ident = match cx.reexport_test_harness_main { diff --git a/compiler/rustc_expand/src/build.rs b/compiler/rustc_expand/src/build.rs index 7320ee12abe3b..19a2d65762e86 100644 --- a/compiler/rustc_expand/src/build.rs +++ b/compiler/rustc_expand/src/build.rs @@ -729,7 +729,7 @@ impl<'a> ExtCtxt<'a> { ty: Box, rhs_kind: ast::ConstItemRhsKind, ) -> Box { - let defaultness = ast::Defaultness::Final; + let defaultness = ast::Defaultness::Implicit; self.item( span, AttrVec::new(), diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs index add09c3ea58b0..9262b08f05e0d 100644 --- a/compiler/rustc_feature/src/unstable.rs +++ b/compiler/rustc_feature/src/unstable.rs @@ -494,6 +494,8 @@ declare_features! ( (unstable, ffi_const, "1.45.0", Some(58328)), /// Allows the use of `#[ffi_pure]` on foreign functions. (unstable, ffi_pure, "1.45.0", Some(58329)), + /// Allows marking trait functions as `final` to prevent overriding impls + (unstable, final_associated_functions, "CURRENT_RUSTC_VERSION", Some(1)), /// Controlling the behavior of fmt::Debug (unstable, fmt_debug, "1.82.0", Some(129709)), /// Allows using `#[align(...)]` on function items diff --git a/compiler/rustc_parse/src/errors.rs b/compiler/rustc_parse/src/errors.rs index 6499f31bb935b..05f6f7ba833f8 100644 --- a/compiler/rustc_parse/src/errors.rs +++ b/compiler/rustc_parse/src/errors.rs @@ -3795,6 +3795,17 @@ pub(crate) struct RecoverImportAsUse { pub token_name: String, } +#[derive(Diagnostic)] +#[diag("{$article} {$descr} cannot be `final`")] +#[note("only associated functions in traits can be `final`")] +pub(crate) struct InappropriateFinal { + #[primary_span] + #[label("`final` because of this")] + pub span: Span, + pub article: &'static str, + pub descr: &'static str, +} + #[derive(Diagnostic)] #[diag("expected `::`, found `:`")] #[note("import paths are delimited using `::`")] diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs index 5b81acb0f91f0..ae25b82db7614 100644 --- a/compiler/rustc_parse/src/parser/item.rs +++ b/compiler/rustc_parse/src/parser/item.rs @@ -206,14 +206,24 @@ impl<'a> Parser<'a> { }) } - /// Error in-case `default` was parsed in an in-appropriate context. + /// Error in-case `default`/`final` was parsed in an in-appropriate context. fn error_on_unconsumed_default(&self, def: Defaultness, kind: &ItemKind) { - if let Defaultness::Default(span) = def { - self.dcx().emit_err(errors::InappropriateDefault { - span, - article: kind.article(), - descr: kind.descr(), - }); + match def { + Defaultness::Default(span) => { + self.dcx().emit_err(errors::InappropriateDefault { + span, + article: kind.article(), + descr: kind.descr(), + }); + } + Defaultness::Final(span) => { + self.dcx().emit_err(errors::InappropriateFinal { + span, + article: kind.article(), + descr: kind.descr(), + }); + } + Defaultness::Implicit => (), } } @@ -229,8 +239,8 @@ impl<'a> Parser<'a> { fn_parse_mode: FnParseMode, case: Case, ) -> PResult<'a, Option> { - let check_pub = def == &Defaultness::Final; - let mut def_ = || mem::replace(def, Defaultness::Final); + let check_pub = def == &Defaultness::Implicit; + let mut def_ = || mem::replace(def, Defaultness::Implicit); let info = if !self.is_use_closure() && self.eat_keyword_case(exp!(Use), case) { self.parse_use_item()? @@ -1005,8 +1015,11 @@ impl<'a> Parser<'a> { { self.bump(); // `default` Defaultness::Default(self.prev_token_uninterpolated_span()) + } else if self.eat_keyword(exp!(Final)) { + self.psess.gated_spans.gate(sym::final_associated_functions, self.prev_token.span); + Defaultness::Final(self.prev_token_uninterpolated_span()) } else { - Defaultness::Final + Defaultness::Implicit } } @@ -1130,7 +1143,7 @@ impl<'a> Parser<'a> { }) => { self.dcx().emit_err(errors::AssociatedStaticItemNotAllowed { span }); AssocItemKind::Const(Box::new(ConstItem { - defaultness: Defaultness::Final, + defaultness: Defaultness::Implicit, ident, generics: Generics::default(), ty, diff --git a/compiler/rustc_parse/src/parser/token_type.rs b/compiler/rustc_parse/src/parser/token_type.rs index 567b1be5e5d98..2d1db430ac27c 100644 --- a/compiler/rustc_parse/src/parser/token_type.rs +++ b/compiler/rustc_parse/src/parser/token_type.rs @@ -91,6 +91,7 @@ pub enum TokenType { KwElse, KwEnum, KwExtern, + KwFinal, KwFn, KwFor, KwGen, @@ -233,6 +234,7 @@ impl TokenType { KwExtern, KwFn, KwFor, + KwFinal, KwGen, KwIf, KwImpl, @@ -309,6 +311,7 @@ impl TokenType { TokenType::KwExtern => Some(kw::Extern), TokenType::KwFn => Some(kw::Fn), TokenType::KwFor => Some(kw::For), + TokenType::KwFinal => Some(kw::Final), TokenType::KwGen => Some(kw::Gen), TokenType::KwIf => Some(kw::If), TokenType::KwImpl => Some(kw::Impl), @@ -524,6 +527,7 @@ macro_rules! exp { (Extern) => { exp!(@kw, Extern, KwExtern) }; (Fn) => { exp!(@kw, Fn, KwFn) }; (For) => { exp!(@kw, For, KwFor) }; + (Final) => { exp!(@kw, Final, KwFinal) }; (Gen) => { exp!(@kw, Gen, KwGen) }; (If) => { exp!(@kw, If, KwIf) }; (Impl) => { exp!(@kw, Impl, KwImpl) }; diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index b9a9d8029d286..2cbaf64ff90b3 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -1094,6 +1094,7 @@ symbols! { fields, file, file_options, + final_associated_functions, flags, float, float_to_int_unchecked, diff --git a/tests/ui/coroutine/gen_fn.none.stderr b/tests/ui/coroutine/gen_fn.none.stderr index 590210641aed4..8a5f865413168 100644 --- a/tests/ui/coroutine/gen_fn.none.stderr +++ b/tests/ui/coroutine/gen_fn.none.stderr @@ -1,8 +1,8 @@ -error: expected one of `#`, `async`, `const`, `default`, `extern`, `fn`, `pub`, `safe`, `unsafe`, or `use`, found `gen` +error: expected one of `#`, `async`, `const`, `default`, `extern`, `final`, `fn`, `pub`, `safe`, `unsafe`, or `use`, found `gen` --> $DIR/gen_fn.rs:4:1 | LL | gen fn foo() {} - | ^^^ expected one of 10 possible tokens + | ^^^ expected one of 11 possible tokens error: aborting due to 1 previous error diff --git a/tests/ui/coroutine/gen_fn.rs b/tests/ui/coroutine/gen_fn.rs index 2f50d5db9acf6..78301cd2832c7 100644 --- a/tests/ui/coroutine/gen_fn.rs +++ b/tests/ui/coroutine/gen_fn.rs @@ -2,7 +2,7 @@ //@[e2024] edition: 2024 gen fn foo() {} -//[none]~^ ERROR: expected one of `#`, `async`, `const`, `default`, `extern`, `fn`, `pub`, `safe`, `unsafe`, or `use`, found `gen` +//[none]~^ ERROR: expected one of `#`, `async`, `const`, `default`, `extern`, `final`, `fn`, `pub`, `safe`, `unsafe`, or `use`, found `gen` //[e2024]~^^ ERROR: gen blocks are experimental fn main() {} diff --git a/tests/ui/feature-gates/feature-gate-final-associated-functions.rs b/tests/ui/feature-gates/feature-gate-final-associated-functions.rs new file mode 100644 index 0000000000000..bdac2710d58e1 --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-final-associated-functions.rs @@ -0,0 +1,6 @@ +trait Foo { + final fn bar() {} + //~^ ERROR `final` on trait functions is experimental +} + +fn main() {} diff --git a/tests/ui/feature-gates/feature-gate-final-associated-functions.stderr b/tests/ui/feature-gates/feature-gate-final-associated-functions.stderr new file mode 100644 index 0000000000000..b3731acd1d905 --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-final-associated-functions.stderr @@ -0,0 +1,13 @@ +error[E0658]: `final` on trait functions is experimental + --> $DIR/feature-gate-final-associated-functions.rs:2:5 + | +LL | final fn bar() {} + | ^^^^^ + | + = note: see issue #1 for more information + = help: add `#![feature(final_associated_functions)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/parser/duplicate-visibility.rs b/tests/ui/parser/duplicate-visibility.rs index f0ee60873da01..d35624981c744 100644 --- a/tests/ui/parser/duplicate-visibility.rs +++ b/tests/ui/parser/duplicate-visibility.rs @@ -2,8 +2,8 @@ fn main() {} extern "C" { //~ NOTE while parsing this item list starting here pub pub fn foo(); - //~^ ERROR expected one of `(`, `async`, `const`, `default`, `extern`, `fn`, `safe`, `unsafe`, or `use`, found keyword `pub` - //~| NOTE expected one of 9 possible tokens + //~^ ERROR expected one of `(`, `async`, `const`, `default`, `extern`, `final`, `fn`, `safe`, `unsafe`, or `use`, found keyword `pub` + //~| NOTE expected one of 10 possible tokens //~| HELP there is already a visibility modifier, remove one //~| NOTE explicit visibility first seen here } //~ NOTE the item list ends here diff --git a/tests/ui/parser/duplicate-visibility.stderr b/tests/ui/parser/duplicate-visibility.stderr index 0d1421ee7f4e4..e00ebe6a8cf6d 100644 --- a/tests/ui/parser/duplicate-visibility.stderr +++ b/tests/ui/parser/duplicate-visibility.stderr @@ -1,4 +1,4 @@ -error: expected one of `(`, `async`, `const`, `default`, `extern`, `fn`, `safe`, `unsafe`, or `use`, found keyword `pub` +error: expected one of `(`, `async`, `const`, `default`, `extern`, `final`, `fn`, `safe`, `unsafe`, or `use`, found keyword `pub` --> $DIR/duplicate-visibility.rs:4:9 | LL | extern "C" { @@ -6,7 +6,7 @@ LL | extern "C" { LL | pub pub fn foo(); | ^^^ | | - | expected one of 9 possible tokens + | expected one of 10 possible tokens | help: there is already a visibility modifier, remove one ... LL | } diff --git a/tests/ui/parser/misspelled-keywords/pub-fn.stderr b/tests/ui/parser/misspelled-keywords/pub-fn.stderr index 1123c652c0ee7..9b895f32c91e1 100644 --- a/tests/ui/parser/misspelled-keywords/pub-fn.stderr +++ b/tests/ui/parser/misspelled-keywords/pub-fn.stderr @@ -1,8 +1,8 @@ -error: expected one of `#`, `async`, `auto`, `const`, `default`, `enum`, `extern`, `fn`, `gen`, `impl`, `macro_rules`, `macro`, `mod`, `pub`, `safe`, `static`, `struct`, `trait`, `type`, `unsafe`, or `use`, found `puB` +error: expected one of `#`, `async`, `auto`, `const`, `default`, `enum`, `extern`, `final`, `fn`, `gen`, `impl`, `macro_rules`, `macro`, `mod`, `pub`, `safe`, `static`, `struct`, `trait`, `type`, `unsafe`, or `use`, found `puB` --> $DIR/pub-fn.rs:1:1 | LL | puB fn code() {} - | ^^^ expected one of 21 possible tokens + | ^^^ expected one of 22 possible tokens | help: write keyword `pub` in lowercase | diff --git a/tests/ui/traits/final/final-kw.gated.stderr b/tests/ui/traits/final/final-kw.gated.stderr new file mode 100644 index 0000000000000..a7967ebf08e2a --- /dev/null +++ b/tests/ui/traits/final/final-kw.gated.stderr @@ -0,0 +1,13 @@ +error[E0658]: `final` on trait functions is experimental + --> $DIR/final-kw.rs:5:5 + | +LL | final fn foo() {} + | ^^^^^ + | + = note: see issue #1 for more information + = help: add `#![feature(final_associated_functions)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/traits/final/final-kw.rs b/tests/ui/traits/final/final-kw.rs new file mode 100644 index 0000000000000..ed675dea6608d --- /dev/null +++ b/tests/ui/traits/final/final-kw.rs @@ -0,0 +1,9 @@ +//@ revisions: ungated gated + +#[cfg(ungated)] +trait Trait { + final fn foo() {} + //~^ ERROR `final` on trait functions is experimental +} + +fn main() {} diff --git a/tests/ui/traits/final/final-kw.ungated.stderr b/tests/ui/traits/final/final-kw.ungated.stderr new file mode 100644 index 0000000000000..a7967ebf08e2a --- /dev/null +++ b/tests/ui/traits/final/final-kw.ungated.stderr @@ -0,0 +1,13 @@ +error[E0658]: `final` on trait functions is experimental + --> $DIR/final-kw.rs:5:5 + | +LL | final fn foo() {} + | ^^^^^ + | + = note: see issue #1 for more information + = help: add `#![feature(final_associated_functions)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0658`. From 460cda8c9583ad1a165c771b311afa66cd393882 Mon Sep 17 00:00:00 2001 From: mu001999 Date: Wed, 28 Jan 2026 21:45:38 +0800 Subject: [PATCH 048/103] Validate final usages in AST Co-authored-by: Michael Goulet --- .../rustc_ast_passes/src/ast_validation.rs | 78 +++++++-- compiler/rustc_ast_passes/src/errors.rs | 18 +++ tests/ui/traits/final/final-must-have-body.rs | 8 + .../traits/final/final-must-have-body.stderr | 10 ++ tests/ui/traits/final/positions.rs | 69 ++++++++ tests/ui/traits/final/positions.stderr | 151 ++++++++++++++++++ 6 files changed, 322 insertions(+), 12 deletions(-) create mode 100644 tests/ui/traits/final/final-must-have-body.rs create mode 100644 tests/ui/traits/final/final-must-have-body.stderr create mode 100644 tests/ui/traits/final/positions.rs create mode 100644 tests/ui/traits/final/positions.stderr diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs index b9fb20b68971d..8a93bd0466bc7 100644 --- a/compiler/rustc_ast_passes/src/ast_validation.rs +++ b/compiler/rustc_ast_passes/src/ast_validation.rs @@ -65,6 +65,28 @@ impl TraitOrImpl { } } +enum AllowDefault { + Yes, + No, +} + +impl AllowDefault { + fn when(b: bool) -> Self { + if b { Self::Yes } else { Self::No } + } +} + +enum AllowFinal { + Yes, + No, +} + +impl AllowFinal { + fn when(b: bool) -> Self { + if b { Self::Yes } else { Self::No } + } +} + struct AstValidator<'a> { sess: &'a Session, features: &'a Features, @@ -563,10 +585,32 @@ impl<'a> AstValidator<'a> { } } - fn check_defaultness(&self, span: Span, defaultness: Defaultness) { - if let Defaultness::Default(def_span) = defaultness { - let span = self.sess.source_map().guess_head_span(span); - self.dcx().emit_err(errors::ForbiddenDefault { span, def_span }); + fn check_defaultness( + &self, + span: Span, + defaultness: Defaultness, + allow_default: AllowDefault, + allow_final: AllowFinal, + ) { + match defaultness { + Defaultness::Default(def_span) if matches!(allow_default, AllowDefault::No) => { + let span = self.sess.source_map().guess_head_span(span); + self.dcx().emit_err(errors::ForbiddenDefault { span, def_span }); + } + Defaultness::Final(def_span) if matches!(allow_final, AllowFinal::No) => { + let span = self.sess.source_map().guess_head_span(span); + self.dcx().emit_err(errors::ForbiddenFinal { span, def_span }); + } + _ => (), + } + } + + fn check_final_has_body(&self, item: &Item, defaultness: Defaultness) { + if let AssocItemKind::Fn(box Fn { body: None, .. }) = &item.kind + && let Defaultness::Final(def_span) = defaultness + { + let span = self.sess.source_map().guess_head_span(item.span); + self.dcx().emit_err(errors::ForbiddenFinalWithoutBody { span, def_span }); } } @@ -1192,7 +1236,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> { }, ) => { self.visit_attrs_vis_ident(&item.attrs, &item.vis, ident); - self.check_defaultness(item.span, *defaultness); + self.check_defaultness(item.span, *defaultness, AllowDefault::No, AllowFinal::No); for EiiImpl { eii_macro_path, .. } in eii_impls { self.visit_path(eii_macro_path); @@ -1362,7 +1406,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> { }); } ItemKind::Const(box ConstItem { defaultness, ident, rhs_kind, .. }) => { - self.check_defaultness(item.span, *defaultness); + self.check_defaultness(item.span, *defaultness, AllowDefault::No, AllowFinal::No); if !rhs_kind.has_expr() { self.dcx().emit_err(errors::ConstWithoutBody { span: item.span, @@ -1400,7 +1444,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> { ItemKind::TyAlias( ty_alias @ box TyAlias { defaultness, bounds, after_where_clause, ty, .. }, ) => { - self.check_defaultness(item.span, *defaultness); + self.check_defaultness(item.span, *defaultness, AllowDefault::No, AllowFinal::No); if ty.is_none() { self.dcx().emit_err(errors::TyAliasWithoutBody { span: item.span, @@ -1430,7 +1474,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> { fn visit_foreign_item(&mut self, fi: &'a ForeignItem) { match &fi.kind { ForeignItemKind::Fn(box Fn { defaultness, ident, sig, body, .. }) => { - self.check_defaultness(fi.span, *defaultness); + self.check_defaultness(fi.span, *defaultness, AllowDefault::No, AllowFinal::No); self.check_foreign_fn_bodyless(*ident, body.as_deref()); self.check_foreign_fn_headerless(sig.header); self.check_foreign_item_ascii_only(*ident); @@ -1450,7 +1494,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> { ty, .. }) => { - self.check_defaultness(fi.span, *defaultness); + self.check_defaultness(fi.span, *defaultness, AllowDefault::No, AllowFinal::No); self.check_foreign_kind_bodyless(*ident, "type", ty.as_ref().map(|b| b.span)); self.check_type_no_bounds(bounds, "`extern` blocks"); self.check_foreign_ty_genericless(generics, after_where_clause); @@ -1709,9 +1753,19 @@ impl<'a> Visitor<'a> for AstValidator<'a> { self.check_nomangle_item_asciionly(ident, item.span); } - if ctxt == AssocCtxt::Trait || self.outer_trait_or_trait_impl.is_none() { - self.check_defaultness(item.span, item.kind.defaultness()); - } + let defaultness = item.kind.defaultness(); + self.check_defaultness( + item.span, + defaultness, + // `default` is allowed on all associated items in impls. + AllowDefault::when(matches!(ctxt, AssocCtxt::Impl { .. })), + // `final` is allowed on all associated *functions* in traits. + AllowFinal::when( + ctxt == AssocCtxt::Trait && matches!(item.kind, AssocItemKind::Fn(..)), + ), + ); + + self.check_final_has_body(item, defaultness); if let AssocCtxt::Impl { .. } = ctxt { match &item.kind { diff --git a/compiler/rustc_ast_passes/src/errors.rs b/compiler/rustc_ast_passes/src/errors.rs index bda04129e5795..ee5b1b6f30931 100644 --- a/compiler/rustc_ast_passes/src/errors.rs +++ b/compiler/rustc_ast_passes/src/errors.rs @@ -159,6 +159,24 @@ pub(crate) struct ForbiddenDefault { pub def_span: Span, } +#[derive(Diagnostic)] +#[diag("`final` is only allowed on associated functions in traits")] +pub(crate) struct ForbiddenFinal { + #[primary_span] + pub span: Span, + #[label("`final` because of this")] + pub def_span: Span, +} + +#[derive(Diagnostic)] +#[diag("`final` is only allowed on associated functions if they have a body")] +pub(crate) struct ForbiddenFinalWithoutBody { + #[primary_span] + pub span: Span, + #[label("`final` because of this")] + pub def_span: Span, +} + #[derive(Diagnostic)] #[diag("associated constant in `impl` without body")] pub(crate) struct AssocConstWithoutBody { diff --git a/tests/ui/traits/final/final-must-have-body.rs b/tests/ui/traits/final/final-must-have-body.rs new file mode 100644 index 0000000000000..57ed26c3e78db --- /dev/null +++ b/tests/ui/traits/final/final-must-have-body.rs @@ -0,0 +1,8 @@ +#![feature(final_associated_functions)] + +trait Foo { + final fn method(); + //~^ ERROR `final` is only allowed on associated functions if they have a body +} + +fn main() {} diff --git a/tests/ui/traits/final/final-must-have-body.stderr b/tests/ui/traits/final/final-must-have-body.stderr new file mode 100644 index 0000000000000..e4f1ffb701e86 --- /dev/null +++ b/tests/ui/traits/final/final-must-have-body.stderr @@ -0,0 +1,10 @@ +error: `final` is only allowed on associated functions if they have a body + --> $DIR/final-must-have-body.rs:4:5 + | +LL | final fn method(); + | -----^^^^^^^^^^^^^ + | | + | `final` because of this + +error: aborting due to 1 previous error + diff --git a/tests/ui/traits/final/positions.rs b/tests/ui/traits/final/positions.rs new file mode 100644 index 0000000000000..01ce6ea4750fe --- /dev/null +++ b/tests/ui/traits/final/positions.rs @@ -0,0 +1,69 @@ +#![feature(final_associated_functions)] + +// Just for exercising the syntax positions +#![feature(associated_type_defaults, extern_types, inherent_associated_types)] +#![allow(incomplete_features)] + +final struct Foo {} +//~^ ERROR a struct cannot be `final` + +final trait Trait { +//~^ ERROR a trait cannot be `final` + + final fn method() {} + // OK! + + final type Foo = (); + //~^ ERROR `final` is only allowed on associated functions in traits + + final const FOO: usize = 1; + //~^ ERROR `final` is only allowed on associated functions in traits +} + +final impl Foo { + final fn method() {} + //~^ ERROR `final` is only allowed on associated functions in traits + + final type Foo = (); + //~^ ERROR `final` is only allowed on associated functions in traits + + final const FOO: usize = 1; + //~^ ERROR `final` is only allowed on associated functions in traits +} + +final impl Trait for Foo { + final fn method() {} + //~^ ERROR `final` is only allowed on associated functions in traits + + final type Foo = (); + //~^ ERROR `final` is only allowed on associated functions in traits + + final const FOO: usize = 1; + //~^ ERROR `final` is only allowed on associated functions in traits +} + + +final fn foo() {} +//~^ ERROR `final` is only allowed on associated functions in traits + +final type FooTy = (); +//~^ ERROR `final` is only allowed on associated functions in traits + +final const FOO: usize = 0; +//~^ ERROR `final` is only allowed on associated functions in traits + +final unsafe extern "C" { +//~^ ERROR an extern block cannot be `final` + + final fn foo_extern(); + //~^ ERROR `final` is only allowed on associated functions in traits + + final type FooExtern; + //~^ ERROR `final` is only allowed on associated functions in traits + + final static FOO_EXTERN: usize = 0; + //~^ ERROR a static item cannot be `final` + //~| ERROR incorrect `static` inside `extern` block +} + +fn main() {} diff --git a/tests/ui/traits/final/positions.stderr b/tests/ui/traits/final/positions.stderr new file mode 100644 index 0000000000000..0514f5e9f4729 --- /dev/null +++ b/tests/ui/traits/final/positions.stderr @@ -0,0 +1,151 @@ +error: a struct cannot be `final` + --> $DIR/positions.rs:7:1 + | +LL | final struct Foo {} + | ^^^^^ `final` because of this + | + = note: only associated functions in traits can be `final` + +error: a trait cannot be `final` + --> $DIR/positions.rs:10:1 + | +LL | final trait Trait { + | ^^^^^ `final` because of this + | + = note: only associated functions in traits can be `final` + +error: a static item cannot be `final` + --> $DIR/positions.rs:64:5 + | +LL | final static FOO_EXTERN: usize = 0; + | ^^^^^ `final` because of this + | + = note: only associated functions in traits can be `final` + +error: an extern block cannot be `final` + --> $DIR/positions.rs:55:1 + | +LL | final unsafe extern "C" { + | ^^^^^ `final` because of this + | + = note: only associated functions in traits can be `final` + +error: `final` is only allowed on associated functions in traits + --> $DIR/positions.rs:16:5 + | +LL | final type Foo = (); + | -----^^^^^^^^^^^^^^^ + | | + | `final` because of this + +error: `final` is only allowed on associated functions in traits + --> $DIR/positions.rs:19:5 + | +LL | final const FOO: usize = 1; + | -----^^^^^^^^^^^^^^^^^^^^^^ + | | + | `final` because of this + +error: `final` is only allowed on associated functions in traits + --> $DIR/positions.rs:24:5 + | +LL | final fn method() {} + | -----^^^^^^^^^^^^ + | | + | `final` because of this + +error: `final` is only allowed on associated functions in traits + --> $DIR/positions.rs:27:5 + | +LL | final type Foo = (); + | -----^^^^^^^^^^^^^^^ + | | + | `final` because of this + +error: `final` is only allowed on associated functions in traits + --> $DIR/positions.rs:30:5 + | +LL | final const FOO: usize = 1; + | -----^^^^^^^^^^^^^^^^^^^^^^ + | | + | `final` because of this + +error: `final` is only allowed on associated functions in traits + --> $DIR/positions.rs:35:5 + | +LL | final fn method() {} + | -----^^^^^^^^^^^^ + | | + | `final` because of this + +error: `final` is only allowed on associated functions in traits + --> $DIR/positions.rs:38:5 + | +LL | final type Foo = (); + | -----^^^^^^^^^^^^^^^ + | | + | `final` because of this + +error: `final` is only allowed on associated functions in traits + --> $DIR/positions.rs:41:5 + | +LL | final const FOO: usize = 1; + | -----^^^^^^^^^^^^^^^^^^^^^^ + | | + | `final` because of this + +error: `final` is only allowed on associated functions in traits + --> $DIR/positions.rs:46:1 + | +LL | final fn foo() {} + | -----^^^^^^^^^ + | | + | `final` because of this + +error: `final` is only allowed on associated functions in traits + --> $DIR/positions.rs:49:1 + | +LL | final type FooTy = (); + | -----^^^^^^^^^^^^^^^^^ + | | + | `final` because of this + +error: `final` is only allowed on associated functions in traits + --> $DIR/positions.rs:52:1 + | +LL | final const FOO: usize = 0; + | -----^^^^^^^^^^^^^^^^^^^^^^ + | | + | `final` because of this + +error: `final` is only allowed on associated functions in traits + --> $DIR/positions.rs:58:5 + | +LL | final fn foo_extern(); + | -----^^^^^^^^^^^^^^^^^ + | | + | `final` because of this + +error: `final` is only allowed on associated functions in traits + --> $DIR/positions.rs:61:5 + | +LL | final type FooExtern; + | -----^^^^^^^^^^^^^^^^ + | | + | `final` because of this + +error: incorrect `static` inside `extern` block + --> $DIR/positions.rs:64:18 + | +LL | final unsafe extern "C" { + | ----------------------- `extern` blocks define existing foreign statics and statics inside of them cannot have a body +... +LL | final static FOO_EXTERN: usize = 0; + | ^^^^^^^^^^ - the invalid body + | | + | cannot have a body + | + = note: for more information, visit https://doc.rust-lang.org/std/keyword.extern.html + +error: aborting due to 18 previous errors + From 3572d482a0ddd0b5bd91deb18e618ff254dee3cb Mon Sep 17 00:00:00 2001 From: mu001999 Date: Wed, 28 Jan 2026 21:59:42 +0800 Subject: [PATCH 049/103] Validate no override impl definitions Co-authored-by: Michael Goulet --- .../rustc_hir_analysis/src/check/check.rs | 26 ++++++++- compiler/rustc_hir_analysis/src/check/mod.rs | 12 ---- compiler/rustc_hir_analysis/src/errors.rs | 10 ++++ tests/ui/traits/final/overriding.rs | 12 ++++ tests/ui/traits/final/overriding.stderr | 14 +++++ tests/ui/traits/final/positions.rs | 3 + tests/ui/traits/final/positions.stderr | 58 +++++++++++++++---- tests/ui/traits/final/works.rs | 13 +++++ 8 files changed, 124 insertions(+), 24 deletions(-) create mode 100644 tests/ui/traits/final/overriding.rs create mode 100644 tests/ui/traits/final/overriding.stderr create mode 100644 tests/ui/traits/final/works.rs diff --git a/compiler/rustc_hir_analysis/src/check/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs index 1bee7a72229a1..5f22dce29dc62 100644 --- a/compiler/rustc_hir_analysis/src/check/check.rs +++ b/compiler/rustc_hir_analysis/src/check/check.rs @@ -1175,13 +1175,35 @@ pub(super) fn check_specialization_validity<'tcx>( if let Err(parent_impl) = result { if !tcx.is_impl_trait_in_trait(impl_item) { - report_forbidden_specialization(tcx, impl_item, parent_impl); + let span = tcx.def_span(impl_item); + let ident = tcx.item_ident(impl_item); + + let err = match tcx.span_of_impl(parent_impl) { + Ok(sp) => errors::ImplNotMarkedDefault::Ok { span, ident, ok_label: sp }, + Err(cname) => errors::ImplNotMarkedDefault::Err { span, ident, cname }, + }; + + tcx.dcx().emit_err(err); } else { tcx.dcx().delayed_bug(format!("parent item: {parent_impl:?} not marked as default")); } } } +fn check_overriding_final_trait_item<'tcx>( + tcx: TyCtxt<'tcx>, + trait_item: ty::AssocItem, + impl_item: ty::AssocItem, +) { + if trait_item.defaultness(tcx).is_final() { + tcx.dcx().emit_err(errors::OverridingFinalTraitFunction { + impl_span: tcx.def_span(impl_item.def_id), + trait_span: tcx.def_span(trait_item.def_id), + ident: tcx.item_ident(impl_item.def_id), + }); + } +} + fn check_impl_items_against_trait<'tcx>( tcx: TyCtxt<'tcx>, impl_id: LocalDefId, @@ -1259,6 +1281,8 @@ fn check_impl_items_against_trait<'tcx>( impl_id.to_def_id(), impl_item, ); + + check_overriding_final_trait_item(tcx, ty_trait_item, ty_impl_item); } if let Ok(ancestors) = trait_def.ancestors(tcx, impl_id.to_def_id()) { diff --git a/compiler/rustc_hir_analysis/src/check/mod.rs b/compiler/rustc_hir_analysis/src/check/mod.rs index d6ae14a7acfef..074501b8ebe47 100644 --- a/compiler/rustc_hir_analysis/src/check/mod.rs +++ b/compiler/rustc_hir_analysis/src/check/mod.rs @@ -197,18 +197,6 @@ pub(super) fn maybe_check_static_with_link_section(tcx: TyCtxt<'_>, id: LocalDef } } -fn report_forbidden_specialization(tcx: TyCtxt<'_>, impl_item: DefId, parent_impl: DefId) { - let span = tcx.def_span(impl_item); - let ident = tcx.item_ident(impl_item); - - let err = match tcx.span_of_impl(parent_impl) { - Ok(sp) => errors::ImplNotMarkedDefault::Ok { span, ident, ok_label: sp }, - Err(cname) => errors::ImplNotMarkedDefault::Err { span, ident, cname }, - }; - - tcx.dcx().emit_err(err); -} - fn missing_items_err( tcx: TyCtxt<'_>, impl_def_id: LocalDefId, diff --git a/compiler/rustc_hir_analysis/src/errors.rs b/compiler/rustc_hir_analysis/src/errors.rs index 6a23b42ae0981..4144f3eb95e7d 100644 --- a/compiler/rustc_hir_analysis/src/errors.rs +++ b/compiler/rustc_hir_analysis/src/errors.rs @@ -892,6 +892,16 @@ pub(crate) enum ImplNotMarkedDefault { #[diag("this item cannot be used as its where bounds are not satisfied for the `Self` type")] pub(crate) struct UselessImplItem; +#[derive(Diagnostic)] +#[diag("cannot override `{$ident}` because it already has a `final` definition in the trait")] +pub(crate) struct OverridingFinalTraitFunction { + #[primary_span] + pub impl_span: Span, + #[note("`{$ident}` is marked final here")] + pub trait_span: Span, + pub ident: Ident, +} + #[derive(Diagnostic)] #[diag("not all trait items implemented, missing: `{$missing_items_msg}`", code = E0046)] pub(crate) struct MissingTraitItem { diff --git a/tests/ui/traits/final/overriding.rs b/tests/ui/traits/final/overriding.rs new file mode 100644 index 0000000000000..f91451852ff06 --- /dev/null +++ b/tests/ui/traits/final/overriding.rs @@ -0,0 +1,12 @@ +#![feature(final_associated_functions)] + +trait Foo { + final fn method() {} +} + +impl Foo for () { + fn method() {} + //~^ ERROR cannot override `method` because it already has a `final` definition in the trait +} + +fn main() {} diff --git a/tests/ui/traits/final/overriding.stderr b/tests/ui/traits/final/overriding.stderr new file mode 100644 index 0000000000000..b5598565072fc --- /dev/null +++ b/tests/ui/traits/final/overriding.stderr @@ -0,0 +1,14 @@ +error: cannot override `method` because it already has a `final` definition in the trait + --> $DIR/overriding.rs:8:5 + | +LL | fn method() {} + | ^^^^^^^^^^^ + | +note: `method` is marked final here + --> $DIR/overriding.rs:4:5 + | +LL | final fn method() {} + | ^^^^^^^^^^^^^^^^^ + +error: aborting due to 1 previous error + diff --git a/tests/ui/traits/final/positions.rs b/tests/ui/traits/final/positions.rs index 01ce6ea4750fe..9bf659e12431e 100644 --- a/tests/ui/traits/final/positions.rs +++ b/tests/ui/traits/final/positions.rs @@ -34,12 +34,15 @@ final impl Foo { final impl Trait for Foo { final fn method() {} //~^ ERROR `final` is only allowed on associated functions in traits + //~^^ ERROR cannot override `method` because it already has a `final` definition in the trait final type Foo = (); //~^ ERROR `final` is only allowed on associated functions in traits + //~^^ ERROR cannot override `Foo` because it already has a `final` definition in the trait final const FOO: usize = 1; //~^ ERROR `final` is only allowed on associated functions in traits + //~^^ ERROR cannot override `FOO` because it already has a `final` definition in the trait } diff --git a/tests/ui/traits/final/positions.stderr b/tests/ui/traits/final/positions.stderr index 0514f5e9f4729..bcb300e49d1a9 100644 --- a/tests/ui/traits/final/positions.stderr +++ b/tests/ui/traits/final/positions.stderr @@ -15,7 +15,7 @@ LL | final trait Trait { = note: only associated functions in traits can be `final` error: a static item cannot be `final` - --> $DIR/positions.rs:64:5 + --> $DIR/positions.rs:67:5 | LL | final static FOO_EXTERN: usize = 0; | ^^^^^ `final` because of this @@ -23,7 +23,7 @@ LL | final static FOO_EXTERN: usize = 0; = note: only associated functions in traits can be `final` error: an extern block cannot be `final` - --> $DIR/positions.rs:55:1 + --> $DIR/positions.rs:58:1 | LL | final unsafe extern "C" { | ^^^^^ `final` because of this @@ -79,7 +79,7 @@ LL | final fn method() {} | `final` because of this error: `final` is only allowed on associated functions in traits - --> $DIR/positions.rs:38:5 + --> $DIR/positions.rs:39:5 | LL | final type Foo = (); | -----^^^^^^^^^^^^^^^ @@ -87,7 +87,7 @@ LL | final type Foo = (); | `final` because of this error: `final` is only allowed on associated functions in traits - --> $DIR/positions.rs:41:5 + --> $DIR/positions.rs:43:5 | LL | final const FOO: usize = 1; | -----^^^^^^^^^^^^^^^^^^^^^^ @@ -95,7 +95,7 @@ LL | final const FOO: usize = 1; | `final` because of this error: `final` is only allowed on associated functions in traits - --> $DIR/positions.rs:46:1 + --> $DIR/positions.rs:49:1 | LL | final fn foo() {} | -----^^^^^^^^^ @@ -103,7 +103,7 @@ LL | final fn foo() {} | `final` because of this error: `final` is only allowed on associated functions in traits - --> $DIR/positions.rs:49:1 + --> $DIR/positions.rs:52:1 | LL | final type FooTy = (); | -----^^^^^^^^^^^^^^^^^ @@ -111,7 +111,7 @@ LL | final type FooTy = (); | `final` because of this error: `final` is only allowed on associated functions in traits - --> $DIR/positions.rs:52:1 + --> $DIR/positions.rs:55:1 | LL | final const FOO: usize = 0; | -----^^^^^^^^^^^^^^^^^^^^^^ @@ -119,7 +119,7 @@ LL | final const FOO: usize = 0; | `final` because of this error: `final` is only allowed on associated functions in traits - --> $DIR/positions.rs:58:5 + --> $DIR/positions.rs:61:5 | LL | final fn foo_extern(); | -----^^^^^^^^^^^^^^^^^ @@ -127,7 +127,7 @@ LL | final fn foo_extern(); | `final` because of this error: `final` is only allowed on associated functions in traits - --> $DIR/positions.rs:61:5 + --> $DIR/positions.rs:64:5 | LL | final type FooExtern; | -----^^^^^^^^^^^^^^^^ @@ -135,7 +135,7 @@ LL | final type FooExtern; | `final` because of this error: incorrect `static` inside `extern` block - --> $DIR/positions.rs:64:18 + --> $DIR/positions.rs:67:18 | LL | final unsafe extern "C" { | ----------------------- `extern` blocks define existing foreign statics and statics inside of them cannot have a body @@ -147,5 +147,41 @@ LL | final static FOO_EXTERN: usize = 0; | = note: for more information, visit https://doc.rust-lang.org/std/keyword.extern.html -error: aborting due to 18 previous errors +error: cannot override `method` because it already has a `final` definition in the trait + --> $DIR/positions.rs:35:5 + | +LL | final fn method() {} + | ^^^^^^^^^^^^^^^^^ + | +note: `method` is marked final here + --> $DIR/positions.rs:13:5 + | +LL | final fn method() {} + | ^^^^^^^^^^^^^^^^^ + +error: cannot override `Foo` because it already has a `final` definition in the trait + --> $DIR/positions.rs:39:5 + | +LL | final type Foo = (); + | ^^^^^^^^^^^^^^ + | +note: `Foo` is marked final here + --> $DIR/positions.rs:16:5 + | +LL | final type Foo = (); + | ^^^^^^^^^^^^^^ + +error: cannot override `FOO` because it already has a `final` definition in the trait + --> $DIR/positions.rs:43:5 + | +LL | final const FOO: usize = 1; + | ^^^^^^^^^^^^^^^^^^^^^^ + | +note: `FOO` is marked final here + --> $DIR/positions.rs:19:5 + | +LL | final const FOO: usize = 1; + | ^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 21 previous errors diff --git a/tests/ui/traits/final/works.rs b/tests/ui/traits/final/works.rs new file mode 100644 index 0000000000000..e756521701a45 --- /dev/null +++ b/tests/ui/traits/final/works.rs @@ -0,0 +1,13 @@ +//@ check-pass + +#![feature(final_associated_functions)] + +trait Foo { + final fn bar(&self) {} +} + +impl Foo for () {} + +fn main() { + ().bar(); +} From f0e1c8f416f45225db02355fb4969c6e3ade9ddb Mon Sep 17 00:00:00 2001 From: mu001999 Date: Wed, 28 Jan 2026 22:12:13 +0800 Subject: [PATCH 050/103] Fix rustfmt Co-authored-by: Michael Goulet --- src/tools/rustfmt/src/items.rs | 12 ++++++------ src/tools/rustfmt/src/utils.rs | 3 ++- src/tools/rustfmt/src/visitor.rs | 20 ++++++++++++++++++-- src/tools/rustfmt/tests/target/final-kw.rs | 5 +++++ 4 files changed, 31 insertions(+), 9 deletions(-) create mode 100644 src/tools/rustfmt/tests/target/final-kw.rs diff --git a/src/tools/rustfmt/src/items.rs b/src/tools/rustfmt/src/items.rs index 1aa85ce2ce550..90466b0b11106 100644 --- a/src/tools/rustfmt/src/items.rs +++ b/src/tools/rustfmt/src/items.rs @@ -319,12 +319,13 @@ impl<'a> FnSig<'a> { method_sig: &'a ast::FnSig, generics: &'a ast::Generics, visibility: &'a ast::Visibility, + defaultness: ast::Defaultness, ) -> FnSig<'a> { FnSig { safety: method_sig.header.safety, coroutine_kind: Cow::Borrowed(&method_sig.header.coroutine_kind), constness: method_sig.header.constness, - defaultness: ast::Defaultness::Final, + defaultness, ext: method_sig.header.ext, decl: &*method_sig.decl, generics, @@ -339,9 +340,7 @@ impl<'a> FnSig<'a> { ) -> FnSig<'a> { match *fn_kind { visit::FnKind::Fn(visit::FnCtxt::Assoc(..), vis, ast::Fn { sig, generics, .. }) => { - let mut fn_sig = FnSig::from_method_sig(sig, generics, vis); - fn_sig.defaultness = defaultness; - fn_sig + FnSig::from_method_sig(sig, generics, vis, defaultness) } visit::FnKind::Fn(_, vis, ast::Fn { sig, generics, .. }) => FnSig { decl, @@ -459,6 +458,7 @@ impl<'a> FmtVisitor<'a> { sig: &ast::FnSig, vis: &ast::Visibility, generics: &ast::Generics, + defaultness: ast::Defaultness, span: Span, ) -> RewriteResult { // Drop semicolon or it will be interpreted as comment. @@ -469,7 +469,7 @@ impl<'a> FmtVisitor<'a> { &context, indent, ident, - &FnSig::from_method_sig(sig, generics, vis), + &FnSig::from_method_sig(sig, generics, vis, defaultness), span, FnBraceStyle::None, )?; @@ -3495,7 +3495,7 @@ impl Rewrite for ast::ForeignItem { context, shape.indent, ident, - &FnSig::from_method_sig(sig, generics, &self.vis), + &FnSig::from_method_sig(sig, generics, &self.vis, defaultness), span, FnBraceStyle::None, ) diff --git a/src/tools/rustfmt/src/utils.rs b/src/tools/rustfmt/src/utils.rs index 3a2975024a33b..b676803379f7c 100644 --- a/src/tools/rustfmt/src/utils.rs +++ b/src/tools/rustfmt/src/utils.rs @@ -102,8 +102,9 @@ pub(crate) fn format_constness_right(constness: ast::Const) -> &'static str { #[inline] pub(crate) fn format_defaultness(defaultness: ast::Defaultness) -> &'static str { match defaultness { + ast::Defaultness::Implicit => "", ast::Defaultness::Default(..) => "default ", - ast::Defaultness::Final => "", + ast::Defaultness::Final(..) => "final ", } } diff --git a/src/tools/rustfmt/src/visitor.rs b/src/tools/rustfmt/src/visitor.rs index 8abcba121e58b..742caefc7cc55 100644 --- a/src/tools/rustfmt/src/visitor.rs +++ b/src/tools/rustfmt/src/visitor.rs @@ -583,7 +583,15 @@ impl<'b, 'a: 'b> FmtVisitor<'a> { } else { let indent = self.block_indent; let rewrite = self - .rewrite_required_fn(indent, ident, sig, &item.vis, generics, item.span) + .rewrite_required_fn( + indent, + ident, + sig, + &item.vis, + generics, + defaultness, + item.span, + ) .ok(); self.push_rewrite(item.span, rewrite); } @@ -686,7 +694,15 @@ impl<'b, 'a: 'b> FmtVisitor<'a> { } else { let indent = self.block_indent; let rewrite = self - .rewrite_required_fn(indent, fn_kind.ident, sig, &ai.vis, generics, ai.span) + .rewrite_required_fn( + indent, + fn_kind.ident, + sig, + &ai.vis, + generics, + defaultness, + ai.span, + ) .ok(); self.push_rewrite(ai.span, rewrite); } diff --git a/src/tools/rustfmt/tests/target/final-kw.rs b/src/tools/rustfmt/tests/target/final-kw.rs new file mode 100644 index 0000000000000..d68b6908d76a7 --- /dev/null +++ b/src/tools/rustfmt/tests/target/final-kw.rs @@ -0,0 +1,5 @@ +trait Foo { + final fn final_() {} + + fn not_final() {} +} From f0a019bf9036fa9f14e7cee662d2c01dcbc6f6d2 Mon Sep 17 00:00:00 2001 From: mu001999 Date: Wed, 28 Jan 2026 22:44:01 +0800 Subject: [PATCH 051/103] Render final associated functions correctly in rustdoc Co-authored-by: Michael Goulet --- src/librustdoc/clean/mod.rs | 24 ++++++------ src/librustdoc/clean/types.rs | 43 +++++++++++++++++----- src/librustdoc/fold.rs | 4 +- src/librustdoc/html/format.rs | 4 -- src/librustdoc/html/render/mod.rs | 22 +++++++---- src/librustdoc/html/render/search_index.rs | 2 +- src/librustdoc/json/conversions.rs | 2 +- src/librustdoc/visit.rs | 4 +- tests/rustdoc-html/final-trait-method.rs | 22 +++++++++++ 9 files changed, 88 insertions(+), 39 deletions(-) create mode 100644 tests/rustdoc-html/final-trait-method.rs diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index c09e17d3787e1..18f991ad43aa6 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -1222,11 +1222,11 @@ fn clean_trait_item<'tcx>(trait_item: &hir::TraitItem<'tcx>, cx: &mut DocContext } hir::TraitItemKind::Fn(ref sig, hir::TraitFn::Provided(body)) => { let m = clean_function(cx, sig, trait_item.generics, ParamsSrc::Body(body)); - MethodItem(m, None) + MethodItem(m, Defaultness::from_trait_item(trait_item.defaultness)) } hir::TraitItemKind::Fn(ref sig, hir::TraitFn::Required(idents)) => { let m = clean_function(cx, sig, trait_item.generics, ParamsSrc::Idents(idents)); - RequiredMethodItem(m) + RequiredMethodItem(m, Defaultness::from_trait_item(trait_item.defaultness)) } hir::TraitItemKind::Type(bounds, Some(default)) => { let generics = enter_impl_trait(cx, |cx| clean_generics(trait_item.generics, cx)); @@ -1271,7 +1271,7 @@ pub(crate) fn clean_impl_item<'tcx>( hir::ImplItemImplKind::Inherent { .. } => hir::Defaultness::Final, hir::ImplItemImplKind::Trait { defaultness, .. } => defaultness, }; - MethodItem(m, Some(defaultness)) + MethodItem(m, Defaultness::from_impl_item(defaultness)) } hir::ImplItemKind::Type(hir_ty) => { let type_ = clean_ty(hir_ty, cx); @@ -1353,18 +1353,20 @@ pub(crate) fn clean_middle_assoc_item(assoc_item: &ty::AssocItem, cx: &mut DocCo } } - let provided = match assoc_item.container { - ty::AssocContainer::InherentImpl | ty::AssocContainer::TraitImpl(_) => true, - ty::AssocContainer::Trait => assoc_item.defaultness(tcx).has_value(), + let defaultness = assoc_item.defaultness(tcx); + let (provided, defaultness) = match assoc_item.container { + ty::AssocContainer::Trait => { + (defaultness.has_value(), Defaultness::from_trait_item(defaultness)) + } + ty::AssocContainer::InherentImpl | ty::AssocContainer::TraitImpl(_) => { + (true, Defaultness::from_impl_item(defaultness)) + } }; + if provided { - let defaultness = match assoc_item.container { - ty::AssocContainer::TraitImpl(_) => Some(assoc_item.defaultness(tcx)), - ty::AssocContainer::InherentImpl | ty::AssocContainer::Trait => None, - }; MethodItem(item, defaultness) } else { - RequiredMethodItem(item) + RequiredMethodItem(item, defaultness) } } ty::AssocKind::Type { .. } => { diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs index c145929534d97..47a1199e7fe46 100644 --- a/src/librustdoc/clean/types.rs +++ b/src/librustdoc/clean/types.rs @@ -61,6 +61,29 @@ pub(crate) enum ItemId { Blanket { impl_id: DefId, for_: DefId }, } +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub(crate) enum Defaultness { + Implicit, + Default, + Final, +} + +impl Defaultness { + pub(crate) fn from_trait_item(defaultness: hir::Defaultness) -> Self { + match defaultness { + hir::Defaultness::Default { .. } => Self::Implicit, + hir::Defaultness::Final => Self::Final, + } + } + + pub(crate) fn from_impl_item(defaultness: hir::Defaultness) -> Self { + match defaultness { + hir::Defaultness::Default { .. } => Self::Default, + hir::Defaultness::Final => Self::Implicit, + } + } +} + impl ItemId { #[inline] pub(crate) fn is_local(self) -> bool { @@ -703,12 +726,12 @@ impl Item { ItemType::from(self) } - pub(crate) fn is_default(&self) -> bool { + pub(crate) fn defaultness(&self) -> Option { match self.kind { - ItemKind::MethodItem(_, Some(defaultness)) => { - defaultness.has_value() && !defaultness.is_final() + ItemKind::MethodItem(_, defaultness) | ItemKind::RequiredMethodItem(_, defaultness) => { + Some(defaultness) } - _ => false, + _ => None, } } @@ -770,8 +793,8 @@ impl Item { } } ItemKind::FunctionItem(_) - | ItemKind::MethodItem(_, _) - | ItemKind::RequiredMethodItem(_) => { + | ItemKind::MethodItem(..) + | ItemKind::RequiredMethodItem(..) => { let def_id = self.def_id().unwrap(); build_fn_header(def_id, tcx, tcx.asyncness(def_id)) } @@ -855,11 +878,11 @@ pub(crate) enum ItemKind { TraitAliasItem(TraitAlias), ImplItem(Box), /// A required method in a trait declaration meaning it's only a function signature. - RequiredMethodItem(Box), + RequiredMethodItem(Box, Defaultness), /// A method in a trait impl or a provided method in a trait declaration. /// /// Compared to [RequiredMethodItem], it also contains a method body. - MethodItem(Box, Option), + MethodItem(Box, Defaultness), StructFieldItem(Type), VariantItem(Variant), /// `fn`s from an extern block @@ -917,8 +940,8 @@ impl ItemKind { | StaticItem(_) | ConstantItem(_) | TraitAliasItem(_) - | RequiredMethodItem(_) - | MethodItem(_, _) + | RequiredMethodItem(..) + | MethodItem(..) | StructFieldItem(_) | ForeignFunctionItem(_, _) | ForeignStaticItem(_, _) diff --git a/src/librustdoc/fold.rs b/src/librustdoc/fold.rs index ee5f260615db5..c970fdbbc93af 100644 --- a/src/librustdoc/fold.rs +++ b/src/librustdoc/fold.rs @@ -82,8 +82,8 @@ pub(crate) trait DocFolder: Sized { | StaticItem(_) | ConstantItem(..) | TraitAliasItem(_) - | RequiredMethodItem(_) - | MethodItem(_, _) + | RequiredMethodItem(..) + | MethodItem(..) | StructFieldItem(_) | ForeignFunctionItem(..) | ForeignStaticItem(..) diff --git a/src/librustdoc/html/format.rs b/src/librustdoc/html/format.rs index f38a21bd1ff36..7646375cbf7a3 100644 --- a/src/librustdoc/html/format.rs +++ b/src/librustdoc/html/format.rs @@ -1566,10 +1566,6 @@ pub(crate) fn print_abi_with_space(abi: ExternAbi) -> impl Display { }) } -pub(crate) fn print_default_space(v: bool) -> &'static str { - if v { "default " } else { "" } -} - fn print_generic_arg(generic_arg: &clean::GenericArg, cx: &Context<'_>) -> impl Display { fmt::from_fn(move |f| match generic_arg { clean::GenericArg::Lifetime(lt) => f.write_str(print_lifetime(lt)), diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs index 86e8167dc3cda..c03a3e8980e11 100644 --- a/src/librustdoc/html/render/mod.rs +++ b/src/librustdoc/html/render/mod.rs @@ -66,7 +66,7 @@ use tracing::{debug, info}; pub(crate) use self::context::*; pub(crate) use self::span_map::{LinkFromSrc, collect_spans_and_sources}; pub(crate) use self::write_shared::*; -use crate::clean::{self, ItemId, RenderedLink}; +use crate::clean::{self, Defaultness, ItemId, RenderedLink}; use crate::display::{Joined as _, MaybeDisplay as _}; use crate::error::Error; use crate::formats::Impl; @@ -75,8 +75,8 @@ use crate::formats::item_type::ItemType; use crate::html::escape::Escape; use crate::html::format::{ Ending, HrefError, HrefInfo, PrintWithSpace, full_print_fn_decl, href, print_abi_with_space, - print_constness_with_space, print_default_space, print_generic_bounds, print_generics, - print_impl, print_path, print_type, print_where_clause, visibility_print_with_space, + print_constness_with_space, print_generic_bounds, print_generics, print_impl, print_path, + print_type, print_where_clause, visibility_print_with_space, }; use crate::html::markdown::{ HeadingOffset, IdMap, Markdown, MarkdownItemInfo, MarkdownSummaryLine, @@ -1109,7 +1109,11 @@ fn assoc_method( let header = meth.fn_header(tcx).expect("Trying to get header from a non-function item"); let name = meth.name.as_ref().unwrap(); let vis = visibility_print_with_space(meth, cx).to_string(); - let defaultness = print_default_space(meth.is_default()); + let defaultness = match meth.defaultness().expect("Expected assoc method to have defaultness") { + Defaultness::Implicit => "", + Defaultness::Final => "final ", + Defaultness::Default => "default ", + }; // FIXME: Once https://github.com/rust-lang/rust/issues/143874 is implemented, we can remove // this condition. let constness = match render_mode { @@ -1260,7 +1264,7 @@ fn render_assoc_item( ) -> impl fmt::Display { fmt::from_fn(move |f| match &item.kind { clean::StrippedItem(..) => Ok(()), - clean::RequiredMethodItem(m) | clean::MethodItem(m, _) => { + clean::RequiredMethodItem(m, _) | clean::MethodItem(m, _) => { assoc_method(item, &m.generics, &m.decl, link, parent, cx, render_mode).fmt(f) } clean::RequiredAssocConstItem(generics, ty) => assoc_const( @@ -1585,7 +1589,7 @@ fn render_deref_methods( fn should_render_item(item: &clean::Item, deref_mut_: bool, tcx: TyCtxt<'_>) -> bool { let self_type_opt = match item.kind { clean::MethodItem(ref method, _) => method.decl.receiver_type(), - clean::RequiredMethodItem(ref method) => method.decl.receiver_type(), + clean::RequiredMethodItem(ref method, _) => method.decl.receiver_type(), _ => None, }; @@ -1855,7 +1859,7 @@ fn render_impl( deprecation_class = ""; } match &item.kind { - clean::MethodItem(..) | clean::RequiredMethodItem(_) => { + clean::MethodItem(..) | clean::RequiredMethodItem(..) => { // Only render when the method is not static or we allow static methods if render_method_item { let id = cx.derive_id(format!("{item_type}.{name}")); @@ -2033,7 +2037,9 @@ fn render_impl( if !impl_.is_negative_trait_impl() { for impl_item in &impl_.items { match impl_item.kind { - clean::MethodItem(..) | clean::RequiredMethodItem(_) => methods.push(impl_item), + clean::MethodItem(..) | clean::RequiredMethodItem(..) => { + methods.push(impl_item) + } clean::RequiredAssocTypeItem(..) | clean::AssocTypeItem(..) => { assoc_types.push(impl_item) } diff --git a/src/librustdoc/html/render/search_index.rs b/src/librustdoc/html/render/search_index.rs index 30b534003da17..766109b65beaa 100644 --- a/src/librustdoc/html/render/search_index.rs +++ b/src/librustdoc/html/render/search_index.rs @@ -1968,7 +1968,7 @@ pub(crate) fn get_function_type_for_search( clean::ForeignFunctionItem(ref f, _) | clean::FunctionItem(ref f) | clean::MethodItem(ref f, _) - | clean::RequiredMethodItem(ref f) => { + | clean::RequiredMethodItem(ref f, _) => { get_fn_inputs_and_outputs(f, tcx, impl_or_trait_generics, cache) } clean::ConstantItem(ref c) => make_nullary_fn(&c.type_), diff --git a/src/librustdoc/json/conversions.rs b/src/librustdoc/json/conversions.rs index 2edf7891be400..599d7b10005d9 100644 --- a/src/librustdoc/json/conversions.rs +++ b/src/librustdoc/json/conversions.rs @@ -296,7 +296,7 @@ fn from_clean_item(item: &clean::Item, renderer: &JsonRenderer<'_>) -> ItemEnum MethodItem(m, _) => { ItemEnum::Function(from_clean_function(m, true, header.unwrap(), renderer)) } - RequiredMethodItem(m) => { + RequiredMethodItem(m, _) => { ItemEnum::Function(from_clean_function(m, false, header.unwrap(), renderer)) } ImplItem(i) => ItemEnum::Impl(i.into_json(renderer)), diff --git a/src/librustdoc/visit.rs b/src/librustdoc/visit.rs index 4d31409afe825..9f6bf009a0540 100644 --- a/src/librustdoc/visit.rs +++ b/src/librustdoc/visit.rs @@ -35,8 +35,8 @@ pub(crate) trait DocVisitor<'a>: Sized { | StaticItem(_) | ConstantItem(..) | TraitAliasItem(_) - | RequiredMethodItem(_) - | MethodItem(_, _) + | RequiredMethodItem(..) + | MethodItem(..) | StructFieldItem(_) | ForeignFunctionItem(..) | ForeignStaticItem(..) diff --git a/tests/rustdoc-html/final-trait-method.rs b/tests/rustdoc-html/final-trait-method.rs new file mode 100644 index 0000000000000..016578a8b0e74 --- /dev/null +++ b/tests/rustdoc-html/final-trait-method.rs @@ -0,0 +1,22 @@ +#![feature(final_associated_functions)] + +//@ has final_trait_method/trait.Item.html +pub trait Item { + //@ has - '//*[@id="method.foo"]' 'final fn foo()' + //@ !has - '//*[@id="method.foo"]' 'default fn foo()' + final fn foo() {} + + //@ has - '//*[@id="method.bar"]' 'fn bar()' + //@ !has - '//*[@id="method.bar"]' 'default fn bar()' + //@ !has - '//*[@id="method.bar"]' 'final fn bar()' + fn bar() {} +} + +//@ has final_trait_method/struct.Foo.html +pub struct Foo; +impl Item for Foo { + //@ has - '//*[@id="method.bar"]' 'fn bar()' + //@ !has - '//*[@id="method.bar"]' 'final fn bar()' + //@ !has - '//*[@id="method.bar"]' 'default fn bar()' + fn bar() {} +} From 2e0ff1fb1eaf7405ff19c544bf296a8e59c595ae Mon Sep 17 00:00:00 2001 From: mu001999 Date: Wed, 28 Jan 2026 22:45:25 +0800 Subject: [PATCH 052/103] Fix clippy ast utils Co-authored-by: Michael Goulet --- src/tools/clippy/clippy_utils/src/ast_utils/mod.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/tools/clippy/clippy_utils/src/ast_utils/mod.rs b/src/tools/clippy/clippy_utils/src/ast_utils/mod.rs index 3b043f7565ef9..15f877632ef11 100644 --- a/src/tools/clippy/clippy_utils/src/ast_utils/mod.rs +++ b/src/tools/clippy/clippy_utils/src/ast_utils/mod.rs @@ -819,7 +819,9 @@ pub fn eq_use_tree_kind(l: &UseTreeKind, r: &UseTreeKind) -> bool { pub fn eq_defaultness(l: Defaultness, r: Defaultness) -> bool { matches!( (l, r), - (Defaultness::Final, Defaultness::Final) | (Defaultness::Default(_), Defaultness::Default(_)) + (Defaultness::Implicit, Defaultness::Implicit) + | (Defaultness::Default(_), Defaultness::Default(_)) + | (Defaultness::Final(_), Defaultness::Final(_)) ) } From 8c77b6c02512e5186dd4a3b7575872c207c399f9 Mon Sep 17 00:00:00 2001 From: mu001999 Date: Thu, 29 Jan 2026 20:34:14 +0800 Subject: [PATCH 053/103] Exclude final methods from dyn-compat check and vtable --- .../src/traits/dyn_compatibility.rs | 5 +++ .../src/traits/vtable.rs | 5 +++ compiler/rustc_ty_utils/src/instance.rs | 6 +++ tests/ui/traits/final/dyn-compat.rs | 40 +++++++++++++++++++ 4 files changed, 56 insertions(+) create mode 100644 tests/ui/traits/final/dyn-compat.rs diff --git a/compiler/rustc_trait_selection/src/traits/dyn_compatibility.rs b/compiler/rustc_trait_selection/src/traits/dyn_compatibility.rs index 512973e452870..489b2d160a51c 100644 --- a/compiler/rustc_trait_selection/src/traits/dyn_compatibility.rs +++ b/compiler/rustc_trait_selection/src/traits/dyn_compatibility.rs @@ -314,6 +314,11 @@ pub fn dyn_compatibility_violations_for_assoc_item( trait_def_id: DefId, item: ty::AssocItem, ) -> Vec { + // `final` assoc functions don't prevent a trait from being dyn-compatible + if tcx.defaultness(item.def_id).is_final() { + return Vec::new(); + } + // Any item that has a `Self: Sized` requisite is otherwise exempt from the regulations. if tcx.generics_require_sized_self(item.def_id) { return Vec::new(); diff --git a/compiler/rustc_trait_selection/src/traits/vtable.rs b/compiler/rustc_trait_selection/src/traits/vtable.rs index bd48068eb579f..b919f158789e2 100644 --- a/compiler/rustc_trait_selection/src/traits/vtable.rs +++ b/compiler/rustc_trait_selection/src/traits/vtable.rs @@ -210,6 +210,11 @@ fn own_existential_vtable_entries_iter( debug!("own_existential_vtable_entry: trait_method={:?}", trait_method); let def_id = trait_method.def_id; + // Final methods should not be included in the vtable. + if trait_method.defaultness(tcx).is_final() { + return None; + } + // Some methods cannot be called on an object; skip those. if !is_vtable_safe_method(tcx, trait_def_id, trait_method) { debug!("own_existential_vtable_entry: not vtable safe"); diff --git a/compiler/rustc_ty_utils/src/instance.rs b/compiler/rustc_ty_utils/src/instance.rs index cbe15eb54787f..a014098e7ed55 100644 --- a/compiler/rustc_ty_utils/src/instance.rs +++ b/compiler/rustc_ty_utils/src/instance.rs @@ -237,6 +237,12 @@ fn resolve_associated_item<'tcx>( } traits::ImplSource::Builtin(BuiltinImplSource::Object(_), _) => { let trait_ref = ty::TraitRef::from_assoc(tcx, trait_id, rcvr_args); + + // `final` methods should be called directly. + if tcx.defaultness(trait_item_id).is_final() { + return Ok(Some(ty::Instance::new_raw(trait_item_id, rcvr_args))); + } + if trait_ref.has_non_region_infer() || trait_ref.has_non_region_param() { // We only resolve totally substituted vtable entries. None diff --git a/tests/ui/traits/final/dyn-compat.rs b/tests/ui/traits/final/dyn-compat.rs new file mode 100644 index 0000000000000..d600058820c20 --- /dev/null +++ b/tests/ui/traits/final/dyn-compat.rs @@ -0,0 +1,40 @@ +//@ run-pass + +#![feature(final_associated_functions)] + +trait FinalNoReceiver { + final fn no_receiver() {} +} + +trait FinalGeneric { + final fn generic(&self, _value: T) {} +} + +trait FinalSelfParam { + final fn self_param(&self, _other: &Self) {} +} + +trait FinalSelfReturn { + final fn self_return(&self) -> &Self { + self + } +} + +struct S; + +impl FinalNoReceiver for S {} +impl FinalGeneric for S {} +impl FinalSelfParam for S {} +impl FinalSelfReturn for S {} + +fn main() { + let s = S; + ::no_receiver(); + let obj_generic: &dyn FinalGeneric = &s; + let obj_param: &dyn FinalSelfParam = &s; + let obj_return: &dyn FinalSelfReturn = &s; + obj_generic.generic(1u8); + obj_param.self_param(obj_param); + let _ = obj_return.self_return(); + let _: &dyn FinalNoReceiver = &s; +} From 11ce0a56552051a3735268a5d239d2b2f0005b1e Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Thu, 12 Feb 2026 14:01:45 +0530 Subject: [PATCH 054/103] pin nightly for miri workflow --- src/tools/rust-analyzer/.github/workflows/ci.yaml | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/tools/rust-analyzer/.github/workflows/ci.yaml b/src/tools/rust-analyzer/.github/workflows/ci.yaml index c3a5017d72d73..ca7d3058d8f0b 100644 --- a/src/tools/rust-analyzer/.github/workflows/ci.yaml +++ b/src/tools/rust-analyzer/.github/workflows/ci.yaml @@ -206,8 +206,11 @@ jobs: - name: Install Rust toolchain run: | - rustup update --no-self-update nightly - rustup default nightly + # FIXME: Pin nightly due to a regression in miri on nightly-2026-02-12. + # See https://github.com/rust-lang/miri/issues/4855. + # Revert to plain `nightly` once this is fixed upstream. + rustup toolchain install nightly-2026-02-10 + rustup default nightly-2026-02-10 rustup component add miri # - name: Cache Dependencies From 8b27b45bbd3969d9e31f983d41a5d1f7f94d5a21 Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Thu, 12 Feb 2026 06:41:03 +0200 Subject: [PATCH 055/103] Revert "feat: Implement fine grained client side request cancellation support" --- src/tools/rust-analyzer/Cargo.lock | 12 ++--- src/tools/rust-analyzer/Cargo.toml | 4 +- .../crates/base-db/src/editioned_file_id.rs | 2 +- .../crates/hir-ty/src/tests/regression.rs | 1 - src/tools/rust-analyzer/crates/ide/src/lib.rs | 6 +-- .../crates/rust-analyzer/src/global_state.rs | 8 +-- .../rust-analyzer/src/handlers/dispatch.rs | 17 +------ .../rust-analyzer/crates/span/src/hygiene.rs | 51 ++++++++++--------- 8 files changed, 38 insertions(+), 63 deletions(-) diff --git a/src/tools/rust-analyzer/Cargo.lock b/src/tools/rust-analyzer/Cargo.lock index 755ae55eea462..11d41a17b1652 100644 --- a/src/tools/rust-analyzer/Cargo.lock +++ b/src/tools/rust-analyzer/Cargo.lock @@ -2453,9 +2453,9 @@ checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" [[package]] name = "salsa" -version = "0.26.0" +version = "0.25.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f77debccd43ba198e9cee23efd7f10330ff445e46a98a2b107fed9094a1ee676" +checksum = "e2e2aa2fca57727371eeafc975acc8e6f4c52f8166a78035543f6ee1c74c2dcc" dependencies = [ "boxcar", "crossbeam-queue", @@ -2478,15 +2478,15 @@ dependencies = [ [[package]] name = "salsa-macro-rules" -version = "0.26.0" +version = "0.25.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea07adbf42d91cc076b7daf3b38bc8168c19eb362c665964118a89bc55ef19a5" +checksum = "1bfc2a1e7bf06964105515451d728f2422dedc3a112383324a00b191a5c397a3" [[package]] name = "salsa-macros" -version = "0.26.0" +version = "0.25.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d16d4d8b66451b9c75ddf740b7fc8399bc7b8ba33e854a5d7526d18708f67b05" +checksum = "3d844c1aa34946da46af683b5c27ec1088a3d9d84a2b837a108223fd830220e1" dependencies = [ "proc-macro2", "quote", diff --git a/src/tools/rust-analyzer/Cargo.toml b/src/tools/rust-analyzer/Cargo.toml index 04559f15eda41..9f31e1903af59 100644 --- a/src/tools/rust-analyzer/Cargo.toml +++ b/src/tools/rust-analyzer/Cargo.toml @@ -135,13 +135,13 @@ rayon = "1.10.0" rowan = "=0.15.17" # Ideally we'd not enable the macros feature but unfortunately the `tracked` attribute does not work # on impls without it -salsa = { version = "0.26", default-features = false, features = [ +salsa = { version = "0.25.2", default-features = false, features = [ "rayon", "salsa_unstable", "macros", "inventory", ] } -salsa-macros = "0.26" +salsa-macros = "0.25.2" semver = "1.0.26" serde = { version = "1.0.219" } serde_derive = { version = "1.0.219" } diff --git a/src/tools/rust-analyzer/crates/base-db/src/editioned_file_id.rs b/src/tools/rust-analyzer/crates/base-db/src/editioned_file_id.rs index dd419f48fc7ec..13fb05d565479 100644 --- a/src/tools/rust-analyzer/crates/base-db/src/editioned_file_id.rs +++ b/src/tools/rust-analyzer/crates/base-db/src/editioned_file_id.rs @@ -60,7 +60,7 @@ const _: () = { } } - impl zalsa_::HashEqLike for EditionedFileIdData { + impl zalsa_struct_::HashEqLike for EditionedFileIdData { #[inline] fn hash(&self, state: &mut H) { Hash::hash(self, state); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs index fba582f880fde..3b73550ad40d2 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs @@ -2363,7 +2363,6 @@ fn test() { } "#, expect![[r#" - 46..49 'Foo': Foo 93..97 'self': Foo 108..125 '{ ... }': usize 118..119 'N': usize diff --git a/src/tools/rust-analyzer/crates/ide/src/lib.rs b/src/tools/rust-analyzer/crates/ide/src/lib.rs index 2e618550f92fb..930eaf2262d93 100644 --- a/src/tools/rust-analyzer/crates/ide/src/lib.rs +++ b/src/tools/rust-analyzer/crates/ide/src/lib.rs @@ -67,7 +67,7 @@ use ide_db::{ FxHashMap, FxIndexSet, LineIndexDatabase, base_db::{ CrateOrigin, CrateWorkspaceData, Env, FileSet, RootQueryDb, SourceDatabase, VfsPath, - salsa::{CancellationToken, Cancelled, Database}, + salsa::{Cancelled, Database}, }, prime_caches, symbol_index, }; @@ -947,10 +947,6 @@ impl Analysis { // We use `attach_db_allow_change()` and not `attach_db()` because fixture injection can change the database. hir::attach_db_allow_change(&self.db, || Cancelled::catch(|| f(&self.db))) } - - pub fn cancellation_token(&self) -> CancellationToken { - self.db.cancellation_token() - } } #[test] diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs index 1462727df4e18..afd4162de6227 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs @@ -14,7 +14,7 @@ use hir::ChangeWithProcMacros; use ide::{Analysis, AnalysisHost, Cancellable, FileId, SourceRootId}; use ide_db::{ MiniCore, - base_db::{Crate, ProcMacroPaths, SourceDatabase, salsa::CancellationToken, salsa::Revision}, + base_db::{Crate, ProcMacroPaths, SourceDatabase, salsa::Revision}, }; use itertools::Itertools; use load_cargo::SourceRootConfig; @@ -88,7 +88,6 @@ pub(crate) struct GlobalState { pub(crate) task_pool: Handle, Receiver>, pub(crate) fmt_pool: Handle, Receiver>, pub(crate) cancellation_pool: thread::Pool, - pub(crate) cancellation_tokens: FxHashMap, pub(crate) config: Arc, pub(crate) config_errors: Option, @@ -266,7 +265,6 @@ impl GlobalState { task_pool, fmt_pool, cancellation_pool, - cancellation_tokens: Default::default(), loader, config: Arc::new(config.clone()), analysis_host, @@ -619,7 +617,6 @@ impl GlobalState { } pub(crate) fn respond(&mut self, response: lsp_server::Response) { - self.cancellation_tokens.remove(&response.id); if let Some((method, start)) = self.req_queue.incoming.complete(&response.id) { if let Some(err) = &response.error && err.message.starts_with("server panicked") @@ -634,9 +631,6 @@ impl GlobalState { } pub(crate) fn cancel(&mut self, request_id: lsp_server::RequestId) { - if let Some(token) = self.cancellation_tokens.remove(&request_id) { - token.cancel(); - } if let Some(response) = self.req_queue.incoming.cancel(request_id) { self.send(response.into()); } diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/dispatch.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/dispatch.rs index 63b4e6430c2d9..90deae2d902e5 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/dispatch.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/dispatch.rs @@ -253,9 +253,6 @@ impl RequestDispatcher<'_> { tracing::debug!(?params); let world = self.global_state.snapshot(); - self.global_state - .cancellation_tokens - .insert(req.id.clone(), world.analysis.cancellation_token()); if RUSTFMT { &mut self.global_state.fmt_pool.handle } else { @@ -268,19 +265,7 @@ impl RequestDispatcher<'_> { }); match thread_result_to_response::(req.id.clone(), result) { Ok(response) => Task::Response(response), - Err(HandlerCancelledError::Inner( - Cancelled::PendingWrite | Cancelled::PropagatedPanic, - )) if ALLOW_RETRYING => Task::Retry(req), - // Note: Technically the return value here does not matter as we have already responded to the client with this error. - Err(HandlerCancelledError::Inner(Cancelled::Local)) => Task::Response(Response { - id: req.id, - result: None, - error: Some(ResponseError { - code: lsp_server::ErrorCode::RequestCanceled as i32, - message: "canceled by client".to_owned(), - data: None, - }), - }), + Err(_cancelled) if ALLOW_RETRYING => Task::Retry(req), Err(_cancelled) => { let error = on_cancelled(); Task::Response(Response { id: req.id, result: None, error: Some(error) }) diff --git a/src/tools/rust-analyzer/crates/span/src/hygiene.rs b/src/tools/rust-analyzer/crates/span/src/hygiene.rs index 0a81cef52ec5a..fe05ef9465181 100644 --- a/src/tools/rust-analyzer/crates/span/src/hygiene.rs +++ b/src/tools/rust-analyzer/crates/span/src/hygiene.rs @@ -81,24 +81,25 @@ const _: () = { #[derive(Hash)] struct StructKey<'db, T0, T1, T2, T3>(T0, T1, T2, T3, std::marker::PhantomData<&'db ()>); - impl<'db, T0, T1, T2, T3> zalsa_::HashEqLike> for SyntaxContextData + impl<'db, T0, T1, T2, T3> zalsa_::interned::HashEqLike> + for SyntaxContextData where - Option: zalsa_::HashEqLike, - Transparency: zalsa_::HashEqLike, - Edition: zalsa_::HashEqLike, - SyntaxContext: zalsa_::HashEqLike, + Option: zalsa_::interned::HashEqLike, + Transparency: zalsa_::interned::HashEqLike, + Edition: zalsa_::interned::HashEqLike, + SyntaxContext: zalsa_::interned::HashEqLike, { fn hash(&self, h: &mut H) { - zalsa_::HashEqLike::::hash(&self.outer_expn, &mut *h); - zalsa_::HashEqLike::::hash(&self.outer_transparency, &mut *h); - zalsa_::HashEqLike::::hash(&self.edition, &mut *h); - zalsa_::HashEqLike::::hash(&self.parent, &mut *h); + zalsa_::interned::HashEqLike::::hash(&self.outer_expn, &mut *h); + zalsa_::interned::HashEqLike::::hash(&self.outer_transparency, &mut *h); + zalsa_::interned::HashEqLike::::hash(&self.edition, &mut *h); + zalsa_::interned::HashEqLike::::hash(&self.parent, &mut *h); } fn eq(&self, data: &StructKey<'db, T0, T1, T2, T3>) -> bool { - zalsa_::HashEqLike::::eq(&self.outer_expn, &data.0) - && zalsa_::HashEqLike::::eq(&self.outer_transparency, &data.1) - && zalsa_::HashEqLike::::eq(&self.edition, &data.2) - && zalsa_::HashEqLike::::eq(&self.parent, &data.3) + zalsa_::interned::HashEqLike::::eq(&self.outer_expn, &data.0) + && zalsa_::interned::HashEqLike::::eq(&self.outer_transparency, &data.1) + && zalsa_::interned::HashEqLike::::eq(&self.edition, &data.2) + && zalsa_::interned::HashEqLike::::eq(&self.parent, &data.3) } } impl zalsa_struct_::Configuration for SyntaxContext { @@ -202,10 +203,10 @@ const _: () = { impl<'db> SyntaxContext { pub fn new< Db, - T0: zalsa_::Lookup> + std::hash::Hash, - T1: zalsa_::Lookup + std::hash::Hash, - T2: zalsa_::Lookup + std::hash::Hash, - T3: zalsa_::Lookup + std::hash::Hash, + T0: zalsa_::interned::Lookup> + std::hash::Hash, + T1: zalsa_::interned::Lookup + std::hash::Hash, + T2: zalsa_::interned::Lookup + std::hash::Hash, + T3: zalsa_::interned::Lookup + std::hash::Hash, >( db: &'db Db, outer_expn: T0, @@ -217,10 +218,10 @@ const _: () = { ) -> Self where Db: ?Sized + salsa::Database, - Option: zalsa_::HashEqLike, - Transparency: zalsa_::HashEqLike, - Edition: zalsa_::HashEqLike, - SyntaxContext: zalsa_::HashEqLike, + Option: zalsa_::interned::HashEqLike, + Transparency: zalsa_::interned::HashEqLike, + Edition: zalsa_::interned::HashEqLike, + SyntaxContext: zalsa_::interned::HashEqLike, { let (zalsa, zalsa_local) = db.zalsas(); @@ -235,10 +236,10 @@ const _: () = { std::marker::PhantomData, ), |id, data| SyntaxContextData { - outer_expn: zalsa_::Lookup::into_owned(data.0), - outer_transparency: zalsa_::Lookup::into_owned(data.1), - edition: zalsa_::Lookup::into_owned(data.2), - parent: zalsa_::Lookup::into_owned(data.3), + outer_expn: zalsa_::interned::Lookup::into_owned(data.0), + outer_transparency: zalsa_::interned::Lookup::into_owned(data.1), + edition: zalsa_::interned::Lookup::into_owned(data.2), + parent: zalsa_::interned::Lookup::into_owned(data.3), opaque: opaque(zalsa_::FromId::from_id(id)), opaque_and_semiopaque: opaque_and_semiopaque(zalsa_::FromId::from_id(id)), }, From 87feb37bc7c5aa6f90ba337de8cbe512d5027ba4 Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Wed, 11 Feb 2026 13:37:08 +0530 Subject: [PATCH 056/103] migrate covert_tuple_return_type to struct to syntax editor --- .../convert_tuple_return_type_to_struct.rs | 121 +++++++---- .../crates/ide-db/src/imports/insert_use.rs | 200 +++++++++++++++++- .../src/ast/syntax_factory/constructors.rs | 18 ++ 3 files changed, 294 insertions(+), 45 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_tuple_return_type_to_struct.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_tuple_return_type_to_struct.rs index 0e5e6185d0540..ad983df8a57ab 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_tuple_return_type_to_struct.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_tuple_return_type_to_struct.rs @@ -5,15 +5,16 @@ use ide_db::{ assists::AssistId, defs::Definition, helpers::mod_path_to_ast, - imports::insert_use::{ImportScope, insert_use}, + imports::insert_use::{ImportScope, insert_use_with_editor}, search::{FileReference, UsageSearchResult}, source_change::SourceChangeBuilder, syntax_helpers::node_ext::{for_each_tail_expr, walk_expr}, }; use syntax::{ AstNode, SyntaxNode, - ast::{self, HasName, edit::IndentLevel, edit_in_place::Indent, make}, - match_ast, ted, + ast::{self, HasName, edit::IndentLevel, edit_in_place::Indent, syntax_factory::SyntaxFactory}, + match_ast, + syntax_editor::SyntaxEditor, }; use crate::assist_context::{AssistContext, Assists}; @@ -67,14 +68,15 @@ pub(crate) fn convert_tuple_return_type_to_struct( "Convert tuple return type to tuple struct", target, move |edit| { - let ret_type = edit.make_mut(ret_type); - let fn_ = edit.make_mut(fn_); + let mut syntax_editor = edit.make_editor(ret_type.syntax()); + let syntax_factory = SyntaxFactory::with_mappings(); let usages = Definition::Function(fn_def).usages(&ctx.sema).all(); let struct_name = format!("{}Result", stdx::to_camel_case(&fn_name.to_string())); let parent = fn_.syntax().ancestors().find_map(>::cast); add_tuple_struct_def( edit, + &syntax_factory, ctx, &usages, parent.as_ref().map(|it| it.syntax()).unwrap_or(fn_.syntax()), @@ -83,15 +85,23 @@ pub(crate) fn convert_tuple_return_type_to_struct( &target_module, ); - ted::replace( + syntax_editor.replace( ret_type.syntax(), - make::ret_type(make::ty(&struct_name)).syntax().clone_for_update(), + syntax_factory.ret_type(syntax_factory.ty(&struct_name)).syntax(), ); if let Some(fn_body) = fn_.body() { - replace_body_return_values(ast::Expr::BlockExpr(fn_body), &struct_name); + replace_body_return_values( + &mut syntax_editor, + &syntax_factory, + ast::Expr::BlockExpr(fn_body), + &struct_name, + ); } + syntax_editor.add_mappings(syntax_factory.finish_with_mappings()); + edit.add_file_edits(ctx.vfs_file_id(), syntax_editor); + replace_usages(edit, ctx, &usages, &struct_name, &target_module); }, ) @@ -106,24 +116,37 @@ fn replace_usages( target_module: &hir::Module, ) { for (file_id, references) in usages.iter() { - edit.edit_file(file_id.file_id(ctx.db())); + let Some(first_ref) = references.first() else { continue }; + + let mut editor = edit.make_editor(first_ref.name.syntax().as_node().unwrap()); + let syntax_factory = SyntaxFactory::with_mappings(); - let refs_with_imports = - augment_references_with_imports(edit, ctx, references, struct_name, target_module); + let refs_with_imports = augment_references_with_imports( + &syntax_factory, + ctx, + references, + struct_name, + target_module, + ); refs_with_imports.into_iter().rev().for_each(|(name, import_data)| { if let Some(fn_) = name.syntax().parent().and_then(ast::Fn::cast) { cov_mark::hit!(replace_trait_impl_fns); if let Some(ret_type) = fn_.ret_type() { - ted::replace( + editor.replace( ret_type.syntax(), - make::ret_type(make::ty(struct_name)).syntax().clone_for_update(), + syntax_factory.ret_type(syntax_factory.ty(struct_name)).syntax(), ); } if let Some(fn_body) = fn_.body() { - replace_body_return_values(ast::Expr::BlockExpr(fn_body), struct_name); + replace_body_return_values( + &mut editor, + &syntax_factory, + ast::Expr::BlockExpr(fn_body), + struct_name, + ); } } else { // replace tuple patterns @@ -143,22 +166,30 @@ fn replace_usages( _ => None, }); for tuple_pat in tuple_pats { - ted::replace( + editor.replace( tuple_pat.syntax(), - make::tuple_struct_pat( - make::path_from_text(struct_name), - tuple_pat.fields(), - ) - .clone_for_update() - .syntax(), + syntax_factory + .tuple_struct_pat( + syntax_factory.path_from_text(struct_name), + tuple_pat.fields(), + ) + .syntax(), ); } } - // add imports across modules where needed if let Some((import_scope, path)) = import_data { - insert_use(&import_scope, path, &ctx.config.insert_use); + insert_use_with_editor( + &import_scope, + path, + &ctx.config.insert_use, + &mut editor, + &syntax_factory, + ); } - }) + }); + + editor.add_mappings(syntax_factory.finish_with_mappings()); + edit.add_file_edits(file_id.file_id(ctx.db()), editor); } } @@ -176,7 +207,7 @@ fn node_to_pats(node: SyntaxNode) -> Option> { } fn augment_references_with_imports( - edit: &mut SourceChangeBuilder, + syntax_factory: &SyntaxFactory, ctx: &AssistContext<'_>, references: &[FileReference], struct_name: &str, @@ -191,8 +222,6 @@ fn augment_references_with_imports( ctx.sema.scope(name.syntax()).map(|scope| (name, scope.module())) }) .map(|(name, ref_module)| { - let new_name = edit.make_mut(name); - // if the referenced module is not the same as the target one and has not been seen before, add an import let import_data = if ref_module.nearest_non_block_module(ctx.db()) != *target_module && !visited_modules.contains(&ref_module) @@ -201,8 +230,7 @@ fn augment_references_with_imports( let cfg = ctx.config.find_path_config(ctx.sema.is_nightly(ref_module.krate(ctx.sema.db))); - let import_scope = - ImportScope::find_insert_use_container(new_name.syntax(), &ctx.sema); + let import_scope = ImportScope::find_insert_use_container(name.syntax(), &ctx.sema); let path = ref_module .find_use_path( ctx.sema.db, @@ -211,12 +239,12 @@ fn augment_references_with_imports( cfg, ) .map(|mod_path| { - make::path_concat( + syntax_factory.path_concat( mod_path_to_ast( &mod_path, target_module.krate(ctx.db()).edition(ctx.db()), ), - make::path_from_text(struct_name), + syntax_factory.path_from_text(struct_name), ) }); @@ -225,7 +253,7 @@ fn augment_references_with_imports( None }; - (new_name, import_data) + (name, import_data) }) .collect() } @@ -233,6 +261,7 @@ fn augment_references_with_imports( // Adds the definition of the tuple struct before the parent function. fn add_tuple_struct_def( edit: &mut SourceChangeBuilder, + syntax_factory: &SyntaxFactory, ctx: &AssistContext<'_>, usages: &UsageSearchResult, parent: &SyntaxNode, @@ -248,13 +277,13 @@ fn add_tuple_struct_def( ctx.sema.scope(name.syntax()).map(|scope| scope.module()) }) .any(|module| module.nearest_non_block_module(ctx.db()) != *target_module); - let visibility = if make_struct_pub { Some(make::visibility_pub()) } else { None }; + let visibility = if make_struct_pub { Some(syntax_factory.visibility_pub()) } else { None }; - let field_list = ast::FieldList::TupleFieldList(make::tuple_field_list( - tuple_ty.fields().map(|ty| make::tuple_field(visibility.clone(), ty)), + let field_list = ast::FieldList::TupleFieldList(syntax_factory.tuple_field_list( + tuple_ty.fields().map(|ty| syntax_factory.tuple_field(visibility.clone(), ty)), )); - let struct_name = make::name(struct_name); - let struct_def = make::struct_(visibility, struct_name, None, field_list).clone_for_update(); + let struct_name = syntax_factory.name(struct_name); + let struct_def = syntax_factory.struct_(visibility, struct_name, None, field_list); let indent = IndentLevel::from_node(parent); struct_def.reindent_to(indent); @@ -263,7 +292,12 @@ fn add_tuple_struct_def( } /// Replaces each returned tuple in `body` with the constructor of the tuple struct named `struct_name`. -fn replace_body_return_values(body: ast::Expr, struct_name: &str) { +fn replace_body_return_values( + syntax_editor: &mut SyntaxEditor, + syntax_factory: &SyntaxFactory, + body: ast::Expr, + struct_name: &str, +) { let mut exprs_to_wrap = Vec::new(); let tail_cb = &mut |e: &_| tail_cb_impl(&mut exprs_to_wrap, e); @@ -278,12 +312,11 @@ fn replace_body_return_values(body: ast::Expr, struct_name: &str) { for ret_expr in exprs_to_wrap { if let ast::Expr::TupleExpr(tuple_expr) = &ret_expr { - let struct_constructor = make::expr_call( - make::expr_path(make::ext::ident_path(struct_name)), - make::arg_list(tuple_expr.fields()), - ) - .clone_for_update(); - ted::replace(ret_expr.syntax(), struct_constructor.syntax()); + let struct_constructor = syntax_factory.expr_call( + syntax_factory.expr_path(syntax_factory.ident_path(struct_name)), + syntax_factory.arg_list(tuple_expr.fields()), + ); + syntax_editor.replace(ret_expr.syntax(), struct_constructor.syntax()); } } } diff --git a/src/tools/rust-analyzer/crates/ide-db/src/imports/insert_use.rs b/src/tools/rust-analyzer/crates/ide-db/src/imports/insert_use.rs index db1d599d550d7..f26952fa1535d 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/imports/insert_use.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/imports/insert_use.rs @@ -9,8 +9,9 @@ use syntax::{ Direction, NodeOrToken, SyntaxKind, SyntaxNode, algo, ast::{ self, AstNode, HasAttrs, HasModuleItem, HasVisibility, PathSegmentKind, - edit_in_place::Removable, make, + edit_in_place::Removable, make, syntax_factory::SyntaxFactory, }, + syntax_editor::{Position, SyntaxEditor}, ted, }; @@ -146,6 +147,17 @@ pub fn insert_use(scope: &ImportScope, path: ast::Path, cfg: &InsertUseConfig) { insert_use_with_alias_option(scope, path, cfg, None); } +/// Insert an import path into the given file/node. A `merge` value of none indicates that no import merging is allowed to occur. +pub fn insert_use_with_editor( + scope: &ImportScope, + path: ast::Path, + cfg: &InsertUseConfig, + syntax_editor: &mut SyntaxEditor, + syntax_factory: &SyntaxFactory, +) { + insert_use_with_alias_option_with_editor(scope, path, cfg, None, syntax_editor, syntax_factory); +} + pub fn insert_use_as_alias( scope: &ImportScope, path: ast::Path, @@ -229,6 +241,71 @@ fn insert_use_with_alias_option( insert_use_(scope, use_item, cfg.group); } +fn insert_use_with_alias_option_with_editor( + scope: &ImportScope, + path: ast::Path, + cfg: &InsertUseConfig, + alias: Option, + syntax_editor: &mut SyntaxEditor, + syntax_factory: &SyntaxFactory, +) { + let _p = tracing::info_span!("insert_use_with_alias_option").entered(); + let mut mb = match cfg.granularity { + ImportGranularity::Crate => Some(MergeBehavior::Crate), + ImportGranularity::Module => Some(MergeBehavior::Module), + ImportGranularity::One => Some(MergeBehavior::One), + ImportGranularity::Item => None, + }; + if !cfg.enforce_granularity { + let file_granularity = guess_granularity_from_scope(scope); + mb = match file_granularity { + ImportGranularityGuess::Unknown => mb, + ImportGranularityGuess::Item => None, + ImportGranularityGuess::Module => Some(MergeBehavior::Module), + // We use the user's setting to infer if this is module or item. + ImportGranularityGuess::ModuleOrItem => match mb { + Some(MergeBehavior::Module) | None => mb, + // There isn't really a way to decide between module or item here, so we just pick one. + // FIXME: Maybe it is possible to infer based on semantic analysis? + Some(MergeBehavior::One | MergeBehavior::Crate) => Some(MergeBehavior::Module), + }, + ImportGranularityGuess::Crate => Some(MergeBehavior::Crate), + ImportGranularityGuess::CrateOrModule => match mb { + Some(MergeBehavior::Crate | MergeBehavior::Module) => mb, + Some(MergeBehavior::One) | None => Some(MergeBehavior::Crate), + }, + ImportGranularityGuess::One => Some(MergeBehavior::One), + }; + } + + let use_tree = syntax_factory.use_tree(path, None, alias, false); + if mb == Some(MergeBehavior::One) && use_tree.path().is_some() { + use_tree.wrap_in_tree_list(); + } + let use_item = make::use_(None, None, use_tree).clone_for_update(); + for attr in + scope.required_cfgs.iter().map(|attr| attr.syntax().clone_subtree().clone_for_update()) + { + syntax_editor.insert(Position::first_child_of(use_item.syntax()), attr); + } + + // merge into existing imports if possible + if let Some(mb) = mb { + let filter = |it: &_| !(cfg.skip_glob_imports && ast::Use::is_simple_glob(it)); + for existing_use in + scope.as_syntax_node().children().filter_map(ast::Use::cast).filter(filter) + { + if let Some(merged) = try_merge_imports(&existing_use, &use_item, mb) { + syntax_editor.replace(existing_use.syntax(), merged.syntax()); + return; + } + } + } + // either we weren't allowed to merge or there is no import that fits the merge conditions + // so look for the place we have to insert to + insert_use_with_editor_(scope, use_item, cfg.group, syntax_editor, syntax_factory); +} + pub fn ast_to_remove_for_path_in_use_stmt(path: &ast::Path) -> Option> { // FIXME: improve this if path.parent_path().is_some() { @@ -500,6 +577,127 @@ fn insert_use_(scope: &ImportScope, use_item: ast::Use, group_imports: bool) { } } +fn insert_use_with_editor_( + scope: &ImportScope, + use_item: ast::Use, + group_imports: bool, + syntax_editor: &mut SyntaxEditor, + syntax_factory: &SyntaxFactory, +) { + let scope_syntax = scope.as_syntax_node(); + let insert_use_tree = + use_item.use_tree().expect("`use_item` should have a use tree for `insert_path`"); + let group = ImportGroup::new(&insert_use_tree); + let path_node_iter = scope_syntax + .children() + .filter_map(|node| ast::Use::cast(node.clone()).zip(Some(node))) + .flat_map(|(use_, node)| { + let tree = use_.use_tree()?; + Some((tree, node)) + }); + + if group_imports { + // Iterator that discards anything that's not in the required grouping + // This implementation allows the user to rearrange their import groups as this only takes the first group that fits + let group_iter = path_node_iter + .clone() + .skip_while(|(use_tree, ..)| ImportGroup::new(use_tree) != group) + .take_while(|(use_tree, ..)| ImportGroup::new(use_tree) == group); + + // track the last element we iterated over, if this is still None after the iteration then that means we never iterated in the first place + let mut last = None; + // find the element that would come directly after our new import + let post_insert: Option<(_, SyntaxNode)> = group_iter + .inspect(|(.., node)| last = Some(node.clone())) + .find(|(use_tree, _)| use_tree_cmp(&insert_use_tree, use_tree) != Ordering::Greater); + + if let Some((.., node)) = post_insert { + cov_mark::hit!(insert_group); + // insert our import before that element + return syntax_editor.insert(Position::before(node), use_item.syntax()); + } + if let Some(node) = last { + cov_mark::hit!(insert_group_last); + // there is no element after our new import, so append it to the end of the group + return syntax_editor.insert(Position::after(node), use_item.syntax()); + } + + // the group we were looking for actually doesn't exist, so insert + + let mut last = None; + // find the group that comes after where we want to insert + let post_group = path_node_iter + .inspect(|(.., node)| last = Some(node.clone())) + .find(|(use_tree, ..)| ImportGroup::new(use_tree) > group); + if let Some((.., node)) = post_group { + cov_mark::hit!(insert_group_new_group); + syntax_editor.insert(Position::before(&node), use_item.syntax()); + if let Some(node) = algo::non_trivia_sibling(node.into(), Direction::Prev) { + syntax_editor.insert(Position::after(node), syntax_factory.whitespace("\n")); + } + return; + } + // there is no such group, so append after the last one + if let Some(node) = last { + cov_mark::hit!(insert_group_no_group); + syntax_editor.insert(Position::after(&node), use_item.syntax()); + syntax_editor.insert(Position::after(node), syntax_factory.whitespace("\n")); + return; + } + } else { + // There exists a group, so append to the end of it + if let Some((_, node)) = path_node_iter.last() { + cov_mark::hit!(insert_no_grouping_last); + syntax_editor.insert(Position::after(node), use_item.syntax()); + return; + } + } + + let l_curly = match &scope.kind { + ImportScopeKind::File(_) => None, + // don't insert the imports before the item list/block expr's opening curly brace + ImportScopeKind::Module(item_list) => item_list.l_curly_token(), + // don't insert the imports before the item list's opening curly brace + ImportScopeKind::Block(block) => block.l_curly_token(), + }; + // there are no imports in this file at all + // so put the import after all inner module attributes and possible license header comments + if let Some(last_inner_element) = scope_syntax + .children_with_tokens() + // skip the curly brace + .skip(l_curly.is_some() as usize) + .take_while(|child| match child { + NodeOrToken::Node(node) => is_inner_attribute(node.clone()), + NodeOrToken::Token(token) => { + [SyntaxKind::WHITESPACE, SyntaxKind::COMMENT, SyntaxKind::SHEBANG] + .contains(&token.kind()) + } + }) + .filter(|child| child.as_token().is_none_or(|t| t.kind() != SyntaxKind::WHITESPACE)) + .last() + { + cov_mark::hit!(insert_empty_inner_attr); + syntax_editor.insert(Position::after(&last_inner_element), use_item.syntax()); + syntax_editor.insert(Position::after(last_inner_element), syntax_factory.whitespace("\n")); + } else { + match l_curly { + Some(b) => { + cov_mark::hit!(insert_empty_module); + syntax_editor.insert(Position::after(&b), syntax_factory.whitespace("\n")); + syntax_editor.insert(Position::after(&b), use_item.syntax()); + } + None => { + cov_mark::hit!(insert_empty_file); + syntax_editor.insert( + Position::first_child_of(scope_syntax), + syntax_factory.whitespace("\n\n"), + ); + syntax_editor.insert(Position::first_child_of(scope_syntax), use_item.syntax()); + } + } + } +} + fn is_inner_attribute(node: SyntaxNode) -> bool { ast::Attr::cast(node).map(|attr| attr.kind()) == Some(ast::AttrKind::Inner) } diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/syntax_factory/constructors.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/syntax_factory/constructors.rs index ad9b9054a8f52..6e17d262a79dd 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/ast/syntax_factory/constructors.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/ast/syntax_factory/constructors.rs @@ -75,6 +75,24 @@ impl SyntaxFactory { make::path_from_text(text).clone_for_update() } + pub fn path_concat(&self, first: ast::Path, second: ast::Path) -> ast::Path { + make::path_concat(first, second).clone_for_update() + } + + pub fn visibility_pub(&self) -> ast::Visibility { + make::visibility_pub() + } + + pub fn struct_( + &self, + visibility: Option, + strukt_name: ast::Name, + generic_param_list: Option, + field_list: ast::FieldList, + ) -> ast::Struct { + make::struct_(visibility, strukt_name, generic_param_list, field_list).clone_for_update() + } + pub fn expr_field(&self, receiver: ast::Expr, field: &str) -> ast::FieldExpr { let ast::Expr::FieldExpr(ast) = make::expr_field(receiver.clone(), field).clone_for_update() From 9c468be3630acc6767bac520a6f3d9f9d91a2875 Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Thu, 12 Feb 2026 10:38:36 +0530 Subject: [PATCH 057/103] move to edit::AstNodeEdit from edit_in_place::Indent --- .../src/handlers/convert_tuple_return_type_to_struct.rs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_tuple_return_type_to_struct.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_tuple_return_type_to_struct.rs index ad983df8a57ab..1740cd024a89c 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_tuple_return_type_to_struct.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_tuple_return_type_to_struct.rs @@ -12,7 +12,11 @@ use ide_db::{ }; use syntax::{ AstNode, SyntaxNode, - ast::{self, HasName, edit::IndentLevel, edit_in_place::Indent, syntax_factory::SyntaxFactory}, + ast::{ + self, HasName, + edit::{AstNodeEdit, IndentLevel}, + syntax_factory::SyntaxFactory, + }, match_ast, syntax_editor::SyntaxEditor, }; @@ -286,7 +290,7 @@ fn add_tuple_struct_def( let struct_def = syntax_factory.struct_(visibility, struct_name, None, field_list); let indent = IndentLevel::from_node(parent); - struct_def.reindent_to(indent); + let struct_def = struct_def.indent(indent); edit.insert(parent.text_range().start(), format!("{struct_def}\n\n{indent}")); } From 61df37fafa98736f2ee5de1f117935efb0598f02 Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Thu, 12 Feb 2026 23:17:55 +0200 Subject: [PATCH 058/103] Don't assume `extern fn`s parameters are patterns Unlike normal fns, they should be bare identifiers. --- .../crates/hir-def/src/expr_store/lower.rs | 42 +++++++++++++++++-- .../crates/hir-ty/src/tests/regression.rs | 13 ++++++ 2 files changed, 52 insertions(+), 3 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower.rs b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower.rs index 701586c258386..1cecd1976b409 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower.rs @@ -32,8 +32,8 @@ use triomphe::Arc; use tt::TextRange; use crate::{ - AdtId, BlockId, BlockLoc, DefWithBodyId, FunctionId, GenericDefId, ImplId, MacroId, - ModuleDefId, ModuleId, TraitId, TypeAliasId, UnresolvedMacro, + AdtId, BlockId, BlockLoc, DefWithBodyId, FunctionId, GenericDefId, ImplId, ItemContainerId, + MacroId, ModuleDefId, ModuleId, TraitId, TypeAliasId, UnresolvedMacro, attrs::AttrFlags, db::DefDatabase, expr_store::{ @@ -141,9 +141,19 @@ pub(super) fn lower_body( source_map_self_param = Some(collector.expander.in_file(AstPtr::new(&self_param_syn))); } + let is_extern = matches!( + owner, + DefWithBodyId::FunctionId(id) + if matches!(id.loc(db).container, ItemContainerId::ExternBlockId(_)), + ); + for param in param_list.params() { if collector.check_cfg(¶m) { - let param_pat = collector.collect_pat_top(param.pat()); + let param_pat = if is_extern { + collector.collect_extern_fn_param(param.pat()) + } else { + collector.collect_pat_top(param.pat()) + }; params.push(param_pat); } } @@ -2248,6 +2258,32 @@ impl<'db> ExprCollector<'db> { } } + fn collect_extern_fn_param(&mut self, pat: Option) -> PatId { + // `extern` functions cannot have pattern-matched parameters, and furthermore, the identifiers + // in their parameters are always interpreted as bindings, even if in a normal function they + // won't be, because they would refer to a path pattern. + let Some(pat) = pat else { return self.missing_pat() }; + + match &pat { + ast::Pat::IdentPat(bp) => { + // FIXME: Emit an error if `!bp.is_simple_ident()`. + + let name = bp.name().map(|nr| nr.as_name()).unwrap_or_else(Name::missing); + let hygiene = bp + .name() + .map(|name| self.hygiene_id_for(name.syntax().text_range())) + .unwrap_or(HygieneId::ROOT); + let binding = self.alloc_binding(name, BindingAnnotation::Unannotated, hygiene); + let pat = + self.alloc_pat(Pat::Bind { id: binding, subpat: None }, AstPtr::new(&pat)); + self.add_definition_to_binding(binding, pat); + pat + } + // FIXME: Emit an error. + _ => self.missing_pat(), + } + } + // region: patterns fn collect_pat_top(&mut self, pat: Option) -> PatId { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs index 3b73550ad40d2..5291bf80e8219 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs @@ -2757,3 +2757,16 @@ where "#]], ); } + +#[test] +fn extern_fns_cannot_have_param_patterns() { + check_no_mismatches( + r#" +pub(crate) struct Builder<'a>(&'a ()); + +unsafe extern "C" { + pub(crate) fn foo<'a>(Builder: &Builder<'a>); +} + "#, + ); +} From 4fc780d97cc7c0d9244be42164286a8927855bb2 Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Fri, 13 Feb 2026 00:26:49 +0200 Subject: [PATCH 059/103] Do not resolve proc macros in value ns (as functions), only in macro ns, outside their defining crate The main impact is that syntax highlighting will show it as a macro and not a function. --- .../crates/hir-def/src/item_scope.rs | 5 +++ .../crates/hir-def/src/nameres.rs | 8 +++++ .../crates/hir-def/src/nameres/collector.rs | 12 +++++-- .../hir-def/src/nameres/tests/macros.rs | 6 ++-- .../crates/hir-def/src/resolver.rs | 11 ++++-- .../crates/hir-ty/src/tests/simple.rs | 35 +++++++++++++++++++ .../crates/ide/src/references.rs | 2 ++ .../test_data/highlight_macros.html | 3 +- .../ide/src/syntax_highlighting/tests.rs | 8 ++++- 9 files changed, 79 insertions(+), 11 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-def/src/item_scope.rs b/src/tools/rust-analyzer/crates/hir-def/src/item_scope.rs index 1303773b59d43..b11a8bcd9097d 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/item_scope.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/item_scope.rs @@ -483,6 +483,11 @@ impl ItemScope { self.declarations.push(def) } + pub(crate) fn remove_from_value_ns(&mut self, name: &Name, def: ModuleDefId) { + let entry = self.values.shift_remove(name); + assert!(entry.is_some_and(|entry| entry.def == def)) + } + pub(crate) fn get_legacy_macro(&self, name: &Name) -> Option<&[MacroId]> { self.legacy_macros.get(name).map(|it| &**it) } diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres.rs index 1e3ea50c5a0f3..5fda1beab4130 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/nameres.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres.rs @@ -211,6 +211,7 @@ struct DefMapCrateData { /// Side table for resolving derive helpers. exported_derives: FxHashMap>, fn_proc_macro_mapping: FxHashMap, + fn_proc_macro_mapping_back: FxHashMap, /// Custom tool modules registered with `#![register_tool]`. registered_tools: Vec, @@ -230,6 +231,7 @@ impl DefMapCrateData { Self { exported_derives: FxHashMap::default(), fn_proc_macro_mapping: FxHashMap::default(), + fn_proc_macro_mapping_back: FxHashMap::default(), registered_tools: PREDEFINED_TOOLS.iter().map(|it| Symbol::intern(it)).collect(), unstable_features: FxHashSet::default(), rustc_coherence_is_core: false, @@ -244,6 +246,7 @@ impl DefMapCrateData { let Self { exported_derives, fn_proc_macro_mapping, + fn_proc_macro_mapping_back, registered_tools, unstable_features, rustc_coherence_is_core: _, @@ -254,6 +257,7 @@ impl DefMapCrateData { } = self; exported_derives.shrink_to_fit(); fn_proc_macro_mapping.shrink_to_fit(); + fn_proc_macro_mapping_back.shrink_to_fit(); registered_tools.shrink_to_fit(); unstable_features.shrink_to_fit(); } @@ -570,6 +574,10 @@ impl DefMap { self.data.fn_proc_macro_mapping.get(&id).copied() } + pub fn proc_macro_as_fn(&self, id: ProcMacroId) -> Option { + self.data.fn_proc_macro_mapping_back.get(&id).copied() + } + pub fn krate(&self) -> Crate { self.krate } diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs index f51524c1b5511..e672e83f0194f 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs @@ -634,6 +634,7 @@ impl<'db> DefCollector<'db> { crate_data.exported_derives.insert(proc_macro_id.into(), helpers); } crate_data.fn_proc_macro_mapping.insert(fn_id, proc_macro_id); + crate_data.fn_proc_macro_mapping_back.insert(proc_macro_id, fn_id); } /// Define a macro with `macro_rules`. @@ -2095,6 +2096,8 @@ impl ModCollector<'_, '_> { let vis = resolve_vis(def_map, local_def_map, &self.item_tree[it.visibility]); + update_def(self.def_collector, fn_id.into(), &it.name, vis, false); + if self.def_collector.def_map.block.is_none() && self.def_collector.is_proc_macro && self.module_id == self.def_collector.def_map.root @@ -2105,9 +2108,14 @@ impl ModCollector<'_, '_> { InFile::new(self.file_id(), id), fn_id, ); - } - update_def(self.def_collector, fn_id.into(), &it.name, vis, false); + // A proc macro is implemented as a function, but it's treated as a macro, not a function. + // You cannot call it like a function, for example, except in its defining crate. + // So we keep the function definition, but remove it from the scope, leaving only the macro. + self.def_collector.def_map[module_id] + .scope + .remove_from_value_ns(&it.name, fn_id.into()); + } } ModItemId::Struct(id) => { let it = &self.item_tree[id]; diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/macros.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/macros.rs index a943f6f0ac01e..a013f8b2bc1a4 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/macros.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/macros.rs @@ -1068,10 +1068,8 @@ pub fn derive_macro_2(_item: TokenStream) -> TokenStream { - AnotherTrait : macro# - DummyTrait : macro# - TokenStream : type value - - attribute_macro : value macro# - - derive_macro : value - - derive_macro_2 : value - - function_like_macro : value macro! + - attribute_macro : macro# + - function_like_macro : macro! "#]], ); } diff --git a/src/tools/rust-analyzer/crates/hir-def/src/resolver.rs b/src/tools/rust-analyzer/crates/hir-def/src/resolver.rs index 2ac0f90fb2090..c10a0ac3eb518 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/resolver.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/resolver.rs @@ -1153,7 +1153,7 @@ impl<'db> ModuleItemMap<'db> { ); match unresolved_idx { None => { - let (value, import) = to_value_ns(module_def)?; + let (value, import) = to_value_ns(module_def, self.def_map)?; Some((ResolveValueResult::ValueNs(value, import), prefix_info)) } Some(unresolved_idx) => { @@ -1194,8 +1194,13 @@ impl<'db> ModuleItemMap<'db> { } } -fn to_value_ns(per_ns: PerNs) -> Option<(ValueNs, Option)> { - let (def, import) = per_ns.take_values_import()?; +fn to_value_ns(per_ns: PerNs, def_map: &DefMap) -> Option<(ValueNs, Option)> { + let (def, import) = per_ns.take_values_import().or_else(|| { + let Some(MacroId::ProcMacroId(proc_macro)) = per_ns.take_macros() else { return None }; + // If we cannot resolve to value ns, but we can resolve to a proc macro, and this is the crate + // defining this proc macro - inside this crate, we should treat the macro as a function. + def_map.proc_macro_as_fn(proc_macro).map(|it| (ModuleDefId::FunctionId(it), None)) + })?; let res = match def { ModuleDefId::FunctionId(it) => ValueNs::FunctionId(it), ModuleDefId::AdtId(AdtId::StructId(it)) => ValueNs::StructId(it), diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs index 7d4f04268af9f..dab8bdb54b691 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs @@ -4074,3 +4074,38 @@ static S: &[u8; 158] = include_bytes!("/foo/bar/baz.txt"); "#, ); } + +#[test] +fn proc_macros_are_functions_inside_defining_crate_and_macros_outside() { + check_types( + r#" +//- /pm.rs crate:pm +#![crate_type = "proc-macro"] + +#[proc_macro_attribute] +pub fn proc_macro() {} + +fn foo() { + proc_macro; + // ^^^^^^^^^^ fn proc_macro() +} + +mod bar { + use super::proc_macro; + + fn baz() { + super::proc_macro; + // ^^^^^^^^^^^^^^^^^ fn proc_macro() + proc_macro; + // ^^^^^^^^^^ fn proc_macro() + } +} + +//- /lib.rs crate:lib deps:pm +fn foo() { + pm::proc_macro; + // ^^^^^^^^^^^^^^ {unknown} +} + "#, + ); +} diff --git a/src/tools/rust-analyzer/crates/ide/src/references.rs b/src/tools/rust-analyzer/crates/ide/src/references.rs index 5443021988d45..38ee09703398b 100644 --- a/src/tools/rust-analyzer/crates/ide/src/references.rs +++ b/src/tools/rust-analyzer/crates/ide/src/references.rs @@ -2073,6 +2073,7 @@ fn func() {} expect![[r#" identity Attribute FileId(1) 1..107 32..40 + FileId(0) 17..25 import FileId(0) 43..51 "#]], ); @@ -2103,6 +2104,7 @@ mirror$0! {} expect![[r#" mirror ProcMacro FileId(1) 1..77 22..28 + FileId(0) 17..23 import FileId(0) 26..32 "#]], ) diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_macros.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_macros.html index 59612634fda38..740a6272a79a7 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_macros.html +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_macros.html @@ -41,7 +41,8 @@ .invalid_escape_sequence { color: #FC5555; text-decoration: wavy underline; } .unresolved_reference { color: #FC5555; text-decoration: wavy underline; } -

use proc_macros::{mirror, identity, DeriveIdentity};
+
use proc_macros::{mirror, identity, DeriveIdentity};
+use pm::proc_macro;
 
 mirror! {
     {
diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tests.rs b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tests.rs
index 8b529cf10f7f9..c6aebd0b0cd11 100644
--- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tests.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tests.rs
@@ -55,8 +55,9 @@ fn macros() {
         r#"
 //- proc_macros: mirror, identity, derive_identity
 //- minicore: fmt, include, concat
-//- /lib.rs crate:lib
+//- /lib.rs crate:lib deps:pm
 use proc_macros::{mirror, identity, DeriveIdentity};
+use pm::proc_macro;
 
 mirror! {
     {
@@ -126,6 +127,11 @@ fn main() {
 //- /foo/foo.rs crate:foo
 mod foo {}
 use self::foo as bar;
+//- /pm.rs crate:pm
+#![crate_type = "proc-macro"]
+
+#[proc_macro_attribute]
+pub fn proc_macro() {}
 "#,
         expect_file!["./test_data/highlight_macros.html"],
         false,

From b76642de1572e29aba4c2f423c64d014a19fc5e0 Mon Sep 17 00:00:00 2001
From: Chayim Refael Friedman 
Date: Fri, 13 Feb 2026 00:31:51 +0200
Subject: [PATCH 060/103] Cleanup unnecessary code

---
 .../crates/hir-def/src/resolver.rs            | 70 +++++++++----------
 .../hir-ty/src/diagnostics/unsafe_check.rs    |  2 +-
 .../rust-analyzer/crates/hir-ty/src/infer.rs  |  4 +-
 .../crates/hir-ty/src/infer/path.rs           |  4 +-
 .../crates/hir-ty/src/lower/path.rs           | 10 ++-
 .../crates/hir-ty/src/mir/lower.rs            |  4 +-
 .../hir-ty/src/mir/lower/pattern_matching.rs  |  4 +-
 7 files changed, 45 insertions(+), 53 deletions(-)

diff --git a/src/tools/rust-analyzer/crates/hir-def/src/resolver.rs b/src/tools/rust-analyzer/crates/hir-def/src/resolver.rs
index c10a0ac3eb518..d32e53fc6bee2 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/resolver.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/resolver.rs
@@ -32,7 +32,7 @@ use crate::{
         BindingId, ExprId, LabelId,
         generics::{GenericParams, TypeOrConstParamData},
     },
-    item_scope::{BUILTIN_SCOPE, BuiltinShadowMode, ImportOrExternCrate, ImportOrGlob, ItemScope},
+    item_scope::{BUILTIN_SCOPE, BuiltinShadowMode, ImportOrExternCrate, ItemScope},
     lang_item::LangItemTarget,
     nameres::{DefMap, LocalDefMap, MacroSubNs, ResolvePathResultPrefixInfo, block_def_map},
     per_ns::PerNs,
@@ -111,8 +111,8 @@ pub enum TypeNs {
 
 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
 pub enum ResolveValueResult {
-    ValueNs(ValueNs, Option),
-    Partial(TypeNs, usize, Option),
+    ValueNs(ValueNs),
+    Partial(TypeNs, usize),
 }
 
 #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
@@ -332,20 +332,17 @@ impl<'db> Resolver<'db> {
             Path::Normal(it) => &it.mod_path,
             Path::LangItem(l, None) => {
                 return Some((
-                    ResolveValueResult::ValueNs(
-                        match *l {
-                            LangItemTarget::FunctionId(it) => ValueNs::FunctionId(it),
-                            LangItemTarget::StaticId(it) => ValueNs::StaticId(it),
-                            LangItemTarget::StructId(it) => ValueNs::StructId(it),
-                            LangItemTarget::EnumVariantId(it) => ValueNs::EnumVariantId(it),
-                            LangItemTarget::UnionId(_)
-                            | LangItemTarget::ImplId(_)
-                            | LangItemTarget::TypeAliasId(_)
-                            | LangItemTarget::TraitId(_)
-                            | LangItemTarget::EnumId(_) => return None,
-                        },
-                        None,
-                    ),
+                    ResolveValueResult::ValueNs(match *l {
+                        LangItemTarget::FunctionId(it) => ValueNs::FunctionId(it),
+                        LangItemTarget::StaticId(it) => ValueNs::StaticId(it),
+                        LangItemTarget::StructId(it) => ValueNs::StructId(it),
+                        LangItemTarget::EnumVariantId(it) => ValueNs::EnumVariantId(it),
+                        LangItemTarget::UnionId(_)
+                        | LangItemTarget::ImplId(_)
+                        | LangItemTarget::TypeAliasId(_)
+                        | LangItemTarget::TraitId(_)
+                        | LangItemTarget::EnumId(_) => return None,
+                    }),
                     ResolvePathResultPrefixInfo::default(),
                 ));
             }
@@ -363,7 +360,7 @@ impl<'db> Resolver<'db> {
                 };
                 // Remaining segments start from 0 because lang paths have no segments other than the remaining.
                 return Some((
-                    ResolveValueResult::Partial(type_ns, 0, None),
+                    ResolveValueResult::Partial(type_ns, 0),
                     ResolvePathResultPrefixInfo::default(),
                 ));
             }
@@ -388,10 +385,7 @@ impl<'db> Resolver<'db> {
 
                         if let Some(e) = entry {
                             return Some((
-                                ResolveValueResult::ValueNs(
-                                    ValueNs::LocalBinding(e.binding()),
-                                    None,
-                                ),
+                                ResolveValueResult::ValueNs(ValueNs::LocalBinding(e.binding())),
                                 ResolvePathResultPrefixInfo::default(),
                             ));
                         }
@@ -404,14 +398,14 @@ impl<'db> Resolver<'db> {
                             && *first_name == sym::Self_
                         {
                             return Some((
-                                ResolveValueResult::ValueNs(ValueNs::ImplSelf(impl_), None),
+                                ResolveValueResult::ValueNs(ValueNs::ImplSelf(impl_)),
                                 ResolvePathResultPrefixInfo::default(),
                             ));
                         }
                         if let Some(id) = params.find_const_by_name(first_name, *def) {
                             let val = ValueNs::GenericParam(id);
                             return Some((
-                                ResolveValueResult::ValueNs(val, None),
+                                ResolveValueResult::ValueNs(val),
                                 ResolvePathResultPrefixInfo::default(),
                             ));
                         }
@@ -431,7 +425,7 @@ impl<'db> Resolver<'db> {
                         if let &GenericDefId::ImplId(impl_) = def {
                             if *first_name == sym::Self_ {
                                 return Some((
-                                    ResolveValueResult::Partial(TypeNs::SelfType(impl_), 1, None),
+                                    ResolveValueResult::Partial(TypeNs::SelfType(impl_), 1),
                                     ResolvePathResultPrefixInfo::default(),
                                 ));
                             }
@@ -440,14 +434,14 @@ impl<'db> Resolver<'db> {
                         {
                             let ty = TypeNs::AdtSelfType(adt);
                             return Some((
-                                ResolveValueResult::Partial(ty, 1, None),
+                                ResolveValueResult::Partial(ty, 1),
                                 ResolvePathResultPrefixInfo::default(),
                             ));
                         }
                         if let Some(id) = params.find_type_by_name(first_name, *def) {
                             let ty = TypeNs::GenericParam(id);
                             return Some((
-                                ResolveValueResult::Partial(ty, 1, None),
+                                ResolveValueResult::Partial(ty, 1),
                                 ResolvePathResultPrefixInfo::default(),
                             ));
                         }
@@ -473,7 +467,7 @@ impl<'db> Resolver<'db> {
             && let Some(builtin) = BuiltinType::by_name(first_name)
         {
             return Some((
-                ResolveValueResult::Partial(TypeNs::BuiltinType(builtin), 1, None),
+                ResolveValueResult::Partial(TypeNs::BuiltinType(builtin), 1),
                 ResolvePathResultPrefixInfo::default(),
             ));
         }
@@ -488,7 +482,7 @@ impl<'db> Resolver<'db> {
         hygiene: HygieneId,
     ) -> Option {
         match self.resolve_path_in_value_ns(db, path, hygiene)? {
-            ResolveValueResult::ValueNs(it, _) => Some(it),
+            ResolveValueResult::ValueNs(it) => Some(it),
             ResolveValueResult::Partial(..) => None,
         }
     }
@@ -1153,12 +1147,12 @@ impl<'db> ModuleItemMap<'db> {
         );
         match unresolved_idx {
             None => {
-                let (value, import) = to_value_ns(module_def, self.def_map)?;
-                Some((ResolveValueResult::ValueNs(value, import), prefix_info))
+                let value = to_value_ns(module_def, self.def_map)?;
+                Some((ResolveValueResult::ValueNs(value), prefix_info))
             }
             Some(unresolved_idx) => {
-                let def = module_def.take_types_full()?;
-                let ty = match def.def {
+                let def = module_def.take_types()?;
+                let ty = match def {
                     ModuleDefId::AdtId(it) => TypeNs::AdtId(it),
                     ModuleDefId::TraitId(it) => TypeNs::TraitId(it),
                     ModuleDefId::TypeAliasId(it) => TypeNs::TypeAliasId(it),
@@ -1171,7 +1165,7 @@ impl<'db> ModuleItemMap<'db> {
                     | ModuleDefId::MacroId(_)
                     | ModuleDefId::StaticId(_) => return None,
                 };
-                Some((ResolveValueResult::Partial(ty, unresolved_idx, def.import), prefix_info))
+                Some((ResolveValueResult::Partial(ty, unresolved_idx), prefix_info))
             }
         }
     }
@@ -1194,12 +1188,12 @@ impl<'db> ModuleItemMap<'db> {
     }
 }
 
-fn to_value_ns(per_ns: PerNs, def_map: &DefMap) -> Option<(ValueNs, Option)> {
-    let (def, import) = per_ns.take_values_import().or_else(|| {
+fn to_value_ns(per_ns: PerNs, def_map: &DefMap) -> Option {
+    let def = per_ns.take_values().or_else(|| {
         let Some(MacroId::ProcMacroId(proc_macro)) = per_ns.take_macros() else { return None };
         // If we cannot resolve to value ns, but we can resolve to a proc macro, and this is the crate
         // defining this proc macro - inside this crate, we should treat the macro as a function.
-        def_map.proc_macro_as_fn(proc_macro).map(|it| (ModuleDefId::FunctionId(it), None))
+        def_map.proc_macro_as_fn(proc_macro).map(ModuleDefId::FunctionId)
     })?;
     let res = match def {
         ModuleDefId::FunctionId(it) => ValueNs::FunctionId(it),
@@ -1215,7 +1209,7 @@ fn to_value_ns(per_ns: PerNs, def_map: &DefMap) -> Option<(ValueNs, Option return None,
     };
-    Some((res, import))
+    Some(res)
 }
 
 fn to_type_ns(per_ns: PerNs) -> Option<(TypeNs, Option)> {
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/unsafe_check.rs b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/unsafe_check.rs
index 50d4517d01254..21f263723bb17 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/unsafe_check.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/unsafe_check.rs
@@ -430,7 +430,7 @@ impl<'db> UnsafeVisitor<'db> {
     fn mark_unsafe_path(&mut self, node: ExprOrPatId, path: &Path) {
         let hygiene = self.body.expr_or_pat_path_hygiene(node);
         let value_or_partial = self.resolver.resolve_path_in_value_ns(self.db, path, hygiene);
-        if let Some(ResolveValueResult::ValueNs(ValueNs::StaticId(id), _)) = value_or_partial {
+        if let Some(ResolveValueResult::ValueNs(ValueNs::StaticId(id))) = value_or_partial {
             let static_data = self.db.static_signature(id);
             if static_data.flags.contains(StaticFlags::MUTABLE) {
                 self.on_unsafe_op(node, UnsafetyReason::MutableStatic);
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs
index 35d744e7d16b4..991acda14bc70 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs
@@ -1584,7 +1584,7 @@ impl<'body, 'db> InferenceContext<'body, 'db> {
                 return (self.err_ty(), None);
             };
             match res {
-                ResolveValueResult::ValueNs(value, _) => match value {
+                ResolveValueResult::ValueNs(value) => match value {
                     ValueNs::EnumVariantId(var) => {
                         let args = path_ctx.substs_from_path(var.into(), true, false);
                         drop(ctx);
@@ -1608,7 +1608,7 @@ impl<'body, 'db> InferenceContext<'body, 'db> {
                         return (self.err_ty(), None);
                     }
                 },
-                ResolveValueResult::Partial(typens, unresolved, _) => (typens, Some(unresolved)),
+                ResolveValueResult::Partial(typens, unresolved) => (typens, Some(unresolved)),
             }
         } else {
             match path_ctx.resolve_path_in_type_ns() {
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs
index ef1a610a323d8..40c6fdf3cc880 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs
@@ -164,11 +164,11 @@ impl<'db> InferenceContext<'_, 'db> {
             let value_or_partial = path_ctx.resolve_path_in_value_ns(hygiene)?;
 
             match value_or_partial {
-                ResolveValueResult::ValueNs(it, _) => {
+                ResolveValueResult::ValueNs(it) => {
                     drop_ctx(ctx, no_diagnostics);
                     (it, None)
                 }
-                ResolveValueResult::Partial(def, remaining_index, _) => {
+                ResolveValueResult::Partial(def, remaining_index) => {
                     // there may be more intermediate segments between the resolved one and
                     // the end. Only the last segment needs to be resolved to a value; from
                     // the segments before that, we need to get either a type or a trait ref.
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lower/path.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lower/path.rs
index f3d0de12275ed..517f67b828a07 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/lower/path.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/lower/path.rs
@@ -396,12 +396,10 @@ impl<'a, 'b, 'db> PathLoweringContext<'a, 'b, 'db> {
         }
 
         let (mod_segments, enum_segment, resolved_segment_idx) = match res {
-            ResolveValueResult::Partial(_, unresolved_segment, _) => {
+            ResolveValueResult::Partial(_, unresolved_segment) => {
                 (segments.take(unresolved_segment - 1), None, unresolved_segment - 1)
             }
-            ResolveValueResult::ValueNs(ValueNs::EnumVariantId(_), _)
-                if prefix_info.enum_variant =>
-            {
+            ResolveValueResult::ValueNs(ValueNs::EnumVariantId(_)) if prefix_info.enum_variant => {
                 (segments.strip_last_two(), segments.len().checked_sub(2), segments.len() - 1)
             }
             ResolveValueResult::ValueNs(..) => (segments.strip_last(), None, segments.len() - 1),
@@ -431,7 +429,7 @@ impl<'a, 'b, 'db> PathLoweringContext<'a, 'b, 'db> {
         }
 
         match &res {
-            ResolveValueResult::ValueNs(resolution, _) => {
+            ResolveValueResult::ValueNs(resolution) => {
                 let resolved_segment_idx = self.current_segment_u32();
                 let resolved_segment = self.current_or_prev_segment;
 
@@ -469,7 +467,7 @@ impl<'a, 'b, 'db> PathLoweringContext<'a, 'b, 'db> {
                     | ValueNs::ConstId(_) => {}
                 }
             }
-            ResolveValueResult::Partial(resolution, _, _) => {
+            ResolveValueResult::Partial(resolution, _) => {
                 if !self.handle_type_ns_resolution(resolution) {
                     return None;
                 }
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs
index 199db7a3e7187..8d5e5c2e6e74b 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs
@@ -1429,7 +1429,7 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> {
                     .resolve_path_in_value_ns(self.db, c, HygieneId::ROOT)
                     .ok_or_else(unresolved_name)?;
                 match pr {
-                    ResolveValueResult::ValueNs(v, _) => {
+                    ResolveValueResult::ValueNs(v) => {
                         if let ValueNs::ConstId(c) = v {
                             self.lower_const_to_operand(
                                 GenericArgs::empty(self.interner()),
@@ -1439,7 +1439,7 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> {
                             not_supported!("bad path in range pattern");
                         }
                     }
-                    ResolveValueResult::Partial(_, _, _) => {
+                    ResolveValueResult::Partial(_, _) => {
                         not_supported!("associated constants in range pattern")
                     }
                 }
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/pattern_matching.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/pattern_matching.rs
index a8aacbff16fa8..83139821e3b8b 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/pattern_matching.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/pattern_matching.rs
@@ -373,7 +373,7 @@ impl<'db> MirLowerCtx<'_, 'db> {
 
                     if let (
                         MatchingMode::Assign,
-                        ResolveValueResult::ValueNs(ValueNs::LocalBinding(binding), _),
+                        ResolveValueResult::ValueNs(ValueNs::LocalBinding(binding)),
                     ) = (mode, &pr)
                     {
                         let local = self.binding_local(*binding)?;
@@ -398,7 +398,7 @@ impl<'db> MirLowerCtx<'_, 'db> {
                         {
                             break 'b (c, x.1);
                         }
-                        if let ResolveValueResult::ValueNs(ValueNs::ConstId(c), _) = pr {
+                        if let ResolveValueResult::ValueNs(ValueNs::ConstId(c)) = pr {
                             break 'b (c, GenericArgs::empty(self.interner()));
                         }
                         not_supported!("path in pattern position that is not const or variant")

From 1911d670fb9ef837d7554309c645e16c47bab2ae Mon Sep 17 00:00:00 2001
From: The Miri Cronjob Bot 
Date: Fri, 13 Feb 2026 05:18:33 +0000
Subject: [PATCH 061/103] Prepare for merging from rust-lang/rust

This updates the rust-version file to 47611e16044c68ef27bac31c35fda2ba1dc20b73.
---
 src/tools/miri/rust-version | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/tools/miri/rust-version b/src/tools/miri/rust-version
index e51d39de3c1b3..a79511cea30a6 100644
--- a/src/tools/miri/rust-version
+++ b/src/tools/miri/rust-version
@@ -1 +1 @@
-f889772d6500faebcac5bb70fa44b5e6581c38cd
+47611e16044c68ef27bac31c35fda2ba1dc20b73

From 3a202c47362cd1b5066e734425aad02589c0c389 Mon Sep 17 00:00:00 2001
From: bit-aloo 
Date: Fri, 13 Feb 2026 12:07:01 +0530
Subject: [PATCH 062/103] remove edit_in_place from replace_let_with_if_let
 assist with edit::AstNodeEdit

---
 .../ide-assists/src/handlers/replace_let_with_if_let.rs       | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_let_with_if_let.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_let_with_if_let.rs
index b95e9b52b0538..98c1cd9b8a379 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_let_with_if_let.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_let_with_if_let.rs
@@ -1,7 +1,7 @@
 use ide_db::ty_filter::TryEnum;
 use syntax::{
     AstNode, T,
-    ast::{self, edit::IndentLevel, edit_in_place::Indent, syntax_factory::SyntaxFactory},
+    ast::{self, edit::{AstNodeEdit, IndentLevel}, syntax_factory::SyntaxFactory},
 };
 
 use crate::{AssistContext, AssistId, Assists};
@@ -64,7 +64,7 @@ pub(crate) fn replace_let_with_if_let(acc: &mut Assists, ctx: &AssistContext<'_>
                 if let_expr_needs_paren(&init) { make.expr_paren(init).into() } else { init };
 
             let block = make.block_expr([], None);
-            block.indent(IndentLevel::from_node(let_stmt.syntax()));
+            let block = block.indent(IndentLevel::from_node(let_stmt.syntax()));
             let if_expr = make.expr_if(
                 make.expr_let(pat, init_expr).into(),
                 block,

From 28bf2dbb130e175113255ee3223953b7b04158a6 Mon Sep 17 00:00:00 2001
From: bit-aloo 
Date: Fri, 13 Feb 2026 12:11:19 +0530
Subject: [PATCH 063/103] remove edit_in_place from move_const_to_impl assist
 with edit::AstNodeEdit

---
 .../crates/ide-assists/src/handlers/move_const_to_impl.rs    | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_const_to_impl.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_const_to_impl.rs
index 102d7e6d532cb..f78b9e5931d67 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_const_to_impl.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_const_to_impl.rs
@@ -2,7 +2,7 @@ use hir::{AsAssocItem, AssocItemContainer, FileRange, HasSource};
 use ide_db::{assists::AssistId, defs::Definition, search::SearchScope};
 use syntax::{
     SyntaxKind,
-    ast::{self, AstNode, edit::IndentLevel, edit_in_place::Indent},
+    ast::{self, AstNode, edit::{AstNodeEdit, IndentLevel}},
 };
 
 use crate::assist_context::{AssistContext, Assists};
@@ -136,7 +136,8 @@ pub(crate) fn move_const_to_impl(acc: &mut Assists, ctx: &AssistContext<'_>) ->
             let indent = IndentLevel::from_node(parent_fn.syntax());
 
             let const_ = const_.clone_for_update();
-            const_.reindent_to(indent);
+            let const_ = const_.reset_indent();
+            let const_ = const_.indent(indent);
             builder.insert(insert_offset, format!("\n{indent}{const_}{fixup}"));
         },
     )

From 6221b39591b2db8d1472cded851e378d32668579 Mon Sep 17 00:00:00 2001
From: bit-aloo 
Date: Fri, 13 Feb 2026 12:13:35 +0530
Subject: [PATCH 064/103] remove edit_in_place from bind_unused_param assist
 with edit::AstNodeEdit

---
 .../crates/ide-assists/src/handlers/bind_unused_param.rs        | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/bind_unused_param.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/bind_unused_param.rs
index 771e80bb92312..0e85a77822bed 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/bind_unused_param.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/bind_unused_param.rs
@@ -2,7 +2,7 @@ use crate::assist_context::{AssistContext, Assists};
 use ide_db::{LineIndexDatabase, assists::AssistId, defs::Definition};
 use syntax::{
     AstNode,
-    ast::{self, HasName, edit_in_place::Indent},
+    ast::{self, HasName, edit::AstNodeEdit},
 };
 
 // Assist: bind_unused_param

From 5de304e2a4f52ef06318d7e59140a8a7e34377a1 Mon Sep 17 00:00:00 2001
From: bit-aloo 
Date: Fri, 13 Feb 2026 12:15:47 +0530
Subject: [PATCH 065/103] remove edit_in_place from generate_mut_trait_impl
 assist with edit::AstNodeEdit

---
 .../ide-assists/src/handlers/generate_mut_trait_impl.rs     | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_mut_trait_impl.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_mut_trait_impl.rs
index 53f6f4883f4dc..3a62a8853e3af 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_mut_trait_impl.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_mut_trait_impl.rs
@@ -1,7 +1,7 @@
 use ide_db::{famous_defs::FamousDefs, traits::resolve_target_trait};
 use syntax::{
     AstNode, SyntaxElement, SyntaxNode, T,
-    ast::{self, edit::AstNodeEdit, edit_in_place::Indent, syntax_factory::SyntaxFactory},
+    ast::{self, edit::AstNodeEdit, syntax_factory::SyntaxFactory},
     syntax_editor::{Element, Position, SyntaxEditor},
 };
 
@@ -46,7 +46,7 @@ use crate::{AssistContext, AssistId, Assists};
 // ```
 pub(crate) fn generate_mut_trait_impl(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
     let impl_def = ctx.find_node_at_offset::()?;
-    let indent = Indent::indent_level(&impl_def);
+    let indent = impl_def.indent_level();
 
     let ast::Type::PathType(path) = impl_def.trait_()? else {
         return None;
@@ -78,7 +78,7 @@ pub(crate) fn generate_mut_trait_impl(acc: &mut Assists, ctx: &AssistContext<'_>
 
             let new_impl = ast::Impl::cast(new_root.clone()).unwrap();
 
-            Indent::indent(&new_impl, indent);
+            let new_impl = new_impl.indent(indent);
 
             let mut editor = edit.make_editor(impl_def.syntax());
             editor.insert_all(

From 34eee0c30a657269bf95bbd3207eba75d53c1bf9 Mon Sep 17 00:00:00 2001
From: bit-aloo 
Date: Fri, 13 Feb 2026 12:16:50 +0530
Subject: [PATCH 066/103] remove edit_in_place from generate_getter_or_setter
 assist with edit::AstNodeEdit

---
 .../ide-assists/src/handlers/generate_getter_or_setter.rs    | 5 ++---
 1 file changed, 2 insertions(+), 3 deletions(-)

diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_getter_or_setter.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_getter_or_setter.rs
index 2db5e46bb093e..84a6f9e8b28c4 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_getter_or_setter.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_getter_or_setter.rs
@@ -3,8 +3,7 @@ use stdx::{format_to, to_lower_snake_case};
 use syntax::{
     TextRange,
     ast::{
-        self, AstNode, HasGenericParams, HasName, HasVisibility, edit_in_place::Indent,
-        syntax_factory::SyntaxFactory,
+        self, AstNode, HasGenericParams, HasName, HasVisibility, edit::AstNodeEdit, syntax_factory::SyntaxFactory
     },
     syntax_editor::Position,
 };
@@ -434,7 +433,7 @@ fn build_source_change(
                 }
             };
             let new_fn = method.clone_for_update();
-            new_fn.indent(1.into());
+            let new_fn = new_fn.indent(1.into());
             new_fn.into()
         })
         .collect();

From 96d6a3e0773ffcc1f3aeea3ee7dab9968a88071a Mon Sep 17 00:00:00 2001
From: bit-aloo 
Date: Fri, 13 Feb 2026 12:20:42 +0530
Subject: [PATCH 067/103] remove edit_in_place from convert_let_else_to_match
 assist with edit::AstNodeEdit

---
 .../ide-assists/src/handlers/convert_let_else_to_match.rs   | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_let_else_to_match.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_let_else_to_match.rs
index ebfed9f9ca991..d2336a4a5d8d0 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_let_else_to_match.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_let_else_to_match.rs
@@ -1,7 +1,6 @@
 use syntax::T;
 use syntax::ast::RangeItem;
-use syntax::ast::edit::IndentLevel;
-use syntax::ast::edit_in_place::Indent;
+use syntax::ast::edit::AstNodeEdit;
 use syntax::ast::syntax_factory::SyntaxFactory;
 use syntax::ast::{self, AstNode, HasName, LetStmt, Pat};
 
@@ -93,7 +92,8 @@ pub(crate) fn convert_let_else_to_match(acc: &mut Assists, ctx: &AssistContext<'
             );
             let else_arm = make.match_arm(make.wildcard_pat().into(), None, else_expr);
             let match_ = make.expr_match(init, make.match_arm_list([binding_arm, else_arm]));
-            match_.reindent_to(IndentLevel::from_node(let_stmt.syntax()));
+            let match_ = match_.reset_indent();
+            let match_ = match_.indent(let_stmt.indent_level());
 
             if bindings.is_empty() {
                 editor.replace(let_stmt.syntax(), match_.syntax());

From c8e284bf017db659ee1b7f5b6df1d967868fd2bf Mon Sep 17 00:00:00 2001
From: bit-aloo 
Date: Fri, 13 Feb 2026 13:01:50 +0530
Subject: [PATCH 068/103] remove edit_in_place from replace_if_let_with_match
 assist with edit::AstNodeEdit

---
 .../src/handlers/generate_getter_or_setter.rs |  3 +-
 .../src/handlers/move_const_to_impl.rs        |  5 +-
 .../src/handlers/replace_if_let_with_match.rs | 62 ++++++++++---------
 .../src/handlers/replace_let_with_if_let.rs   |  6 +-
 4 files changed, 43 insertions(+), 33 deletions(-)

diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_getter_or_setter.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_getter_or_setter.rs
index 84a6f9e8b28c4..51b967437b568 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_getter_or_setter.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_getter_or_setter.rs
@@ -3,7 +3,8 @@ use stdx::{format_to, to_lower_snake_case};
 use syntax::{
     TextRange,
     ast::{
-        self, AstNode, HasGenericParams, HasName, HasVisibility, edit::AstNodeEdit, syntax_factory::SyntaxFactory
+        self, AstNode, HasGenericParams, HasName, HasVisibility, edit::AstNodeEdit,
+        syntax_factory::SyntaxFactory,
     },
     syntax_editor::Position,
 };
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_const_to_impl.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_const_to_impl.rs
index f78b9e5931d67..b3e79e4663e9d 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_const_to_impl.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_const_to_impl.rs
@@ -2,7 +2,10 @@ use hir::{AsAssocItem, AssocItemContainer, FileRange, HasSource};
 use ide_db::{assists::AssistId, defs::Definition, search::SearchScope};
 use syntax::{
     SyntaxKind,
-    ast::{self, AstNode, edit::{AstNodeEdit, IndentLevel}},
+    ast::{
+        self, AstNode,
+        edit::{AstNodeEdit, IndentLevel},
+    },
 };
 
 use crate::assist_context::{AssistContext, Assists};
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_if_let_with_match.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_if_let_with_match.rs
index b7e5344712eb4..915dd3ffcaf0c 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_if_let_with_match.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_if_let_with_match.rs
@@ -3,7 +3,11 @@ use std::iter::successors;
 use ide_db::{RootDatabase, defs::NameClass, ty_filter::TryEnum};
 use syntax::{
     AstNode, Edition, SyntaxKind, T, TextRange,
-    ast::{self, HasName, edit::IndentLevel, edit_in_place::Indent, syntax_factory::SyntaxFactory},
+    ast::{
+        self, HasName,
+        edit::{AstNodeEdit, IndentLevel},
+        syntax_factory::SyntaxFactory,
+    },
     syntax_editor::SyntaxEditor,
 };
 
@@ -54,8 +58,7 @@ pub(crate) fn replace_if_let_with_match(acc: &mut Assists, ctx: &AssistContext<'
         ast::ElseBranch::IfExpr(expr) => Some(expr),
         ast::ElseBranch::Block(block) => {
             let block = unwrap_trivial_block(block).clone_for_update();
-            block.reindent_to(IndentLevel(1));
-            else_block = Some(block);
+            else_block = Some(block.reset_indent().indent(IndentLevel(1)));
             None
         }
     });
@@ -82,12 +85,13 @@ pub(crate) fn replace_if_let_with_match(acc: &mut Assists, ctx: &AssistContext<'
                 (Some(pat), guard)
             }
         };
-        if let Some(guard) = &guard {
-            guard.dedent(indent);
-            guard.indent(IndentLevel(1));
-        }
-        let body = if_expr.then_branch()?.clone_for_update();
-        body.indent(IndentLevel(1));
+        let guard = if let Some(guard) = &guard {
+            Some(guard.dedent(indent).indent(IndentLevel(1)))
+        } else {
+            guard
+        };
+
+        let body = if_expr.then_branch()?.clone_for_update().indent(IndentLevel(1));
         cond_bodies.push((cond, guard, body));
     }
 
@@ -109,7 +113,8 @@ pub(crate) fn replace_if_let_with_match(acc: &mut Assists, ctx: &AssistContext<'
                 let else_arm = make_else_arm(ctx, &make, else_block, &cond_bodies);
                 let make_match_arm =
                     |(pat, guard, body): (_, Option, ast::BlockExpr)| {
-                        body.reindent_to(IndentLevel::single());
+                        // Dedent from original position, then indent for match arm
+                        let body = body.dedent(indent).indent(IndentLevel::single());
                         let body = unwrap_trivial_block(body);
                         match (pat, guard.map(|it| make.match_guard(it))) {
                             (Some(pat), guard) => make.match_arm(pat, guard, body),
@@ -122,8 +127,8 @@ pub(crate) fn replace_if_let_with_match(acc: &mut Assists, ctx: &AssistContext<'
                         }
                     };
                 let arms = cond_bodies.into_iter().map(make_match_arm).chain([else_arm]);
-                let match_expr = make.expr_match(scrutinee_to_be_expr, make.match_arm_list(arms));
-                match_expr.indent(indent);
+                let match_expr =
+                    make.expr_match(scrutinee_to_be_expr, make.match_arm_list(arms)).indent(indent);
                 match_expr.into()
             };
 
@@ -131,10 +136,9 @@ pub(crate) fn replace_if_let_with_match(acc: &mut Assists, ctx: &AssistContext<'
                 if_expr.syntax().parent().is_some_and(|it| ast::IfExpr::can_cast(it.kind()));
             let expr = if has_preceding_if_expr {
                 // make sure we replace the `else if let ...` with a block so we don't end up with `else expr`
-                match_expr.dedent(indent);
-                match_expr.indent(IndentLevel(1));
-                let block_expr = make.block_expr([], Some(match_expr));
-                block_expr.indent(indent);
+                let block_expr = make
+                    .block_expr([], Some(match_expr.dedent(indent).indent(IndentLevel(1))))
+                    .indent(indent);
                 block_expr.into()
             } else {
                 match_expr
@@ -267,10 +271,7 @@ pub(crate) fn replace_match_with_if_let(acc: &mut Assists, ctx: &AssistContext<'
                 // wrap them in another BlockExpr.
                 match expr {
                     ast::Expr::BlockExpr(block) if block.modifier().is_none() => block,
-                    expr => {
-                        expr.indent(IndentLevel(1));
-                        make.block_expr([], Some(expr))
-                    }
+                    expr => make.block_expr([], Some(expr.indent(IndentLevel(1)))),
                 }
             };
 
@@ -292,18 +293,19 @@ pub(crate) fn replace_match_with_if_let(acc: &mut Assists, ctx: &AssistContext<'
             } else {
                 condition
             };
-            let then_expr = then_expr.clone_for_update();
-            let else_expr = else_expr.clone_for_update();
-            then_expr.reindent_to(IndentLevel::single());
-            else_expr.reindent_to(IndentLevel::single());
+            let then_expr =
+                then_expr.clone_for_update().reset_indent().indent(IndentLevel::single());
+            let else_expr =
+                else_expr.clone_for_update().reset_indent().indent(IndentLevel::single());
             let then_block = make_block_expr(then_expr);
             let else_expr = if is_empty_expr(&else_expr) { None } else { Some(else_expr) };
-            let if_let_expr = make.expr_if(
-                condition,
-                then_block,
-                else_expr.map(make_block_expr).map(ast::ElseBranch::Block),
-            );
-            if_let_expr.indent(IndentLevel::from_node(match_expr.syntax()));
+            let if_let_expr = make
+                .expr_if(
+                    condition,
+                    then_block,
+                    else_expr.map(make_block_expr).map(ast::ElseBranch::Block),
+                )
+                .indent(IndentLevel::from_node(match_expr.syntax()));
 
             let mut editor = builder.make_editor(match_expr.syntax());
             editor.replace(match_expr.syntax(), if_let_expr.syntax());
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_let_with_if_let.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_let_with_if_let.rs
index 98c1cd9b8a379..15977c420e642 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_let_with_if_let.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_let_with_if_let.rs
@@ -1,7 +1,11 @@
 use ide_db::ty_filter::TryEnum;
 use syntax::{
     AstNode, T,
-    ast::{self, edit::{AstNodeEdit, IndentLevel}, syntax_factory::SyntaxFactory},
+    ast::{
+        self,
+        edit::{AstNodeEdit, IndentLevel},
+        syntax_factory::SyntaxFactory,
+    },
 };
 
 use crate::{AssistContext, AssistId, Assists};

From 140a039fb7332154f52ccabb06d9b4f015433da9 Mon Sep 17 00:00:00 2001
From: bit-aloo 
Date: Fri, 13 Feb 2026 13:09:27 +0530
Subject: [PATCH 069/103] remove edit_in_place from generate_trait_from_impl
 assist with edit::AstNodeEdit

---
 .../crates/ide-assists/src/handlers/generate_trait_from_impl.rs | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_trait_from_impl.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_trait_from_impl.rs
index 56500cf068024..8bc4d50cf6af2 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_trait_from_impl.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_trait_from_impl.rs
@@ -2,7 +2,7 @@ use crate::assist_context::{AssistContext, Assists};
 use ide_db::assists::AssistId;
 use syntax::{
     AstNode, SyntaxKind, T,
-    ast::{self, HasGenericParams, HasName, HasVisibility, edit_in_place::Indent, make},
+    ast::{self, HasGenericParams, HasName, HasVisibility, edit::AstNodeEdit, make},
     syntax_editor::{Position, SyntaxEditor},
 };
 

From 85e0c3209e1c627365ca99ec354e1d7f432f36ab Mon Sep 17 00:00:00 2001
From: bit-aloo 
Date: Fri, 13 Feb 2026 13:11:46 +0530
Subject: [PATCH 070/103] remove edit_in_place from convert_bool_to_enum assist
 with edit::AstNodeEdit

---
 .../crates/ide-assists/src/handlers/convert_bool_to_enum.rs | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_bool_to_enum.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_bool_to_enum.rs
index 1ae5f6491744a..434fbbae05c43 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_bool_to_enum.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_bool_to_enum.rs
@@ -11,9 +11,10 @@ use ide_db::{
     source_change::SourceChangeBuilder,
 };
 use itertools::Itertools;
+use syntax::ast::edit::AstNodeEdit;
 use syntax::{
     AstNode, NodeOrToken, SyntaxKind, SyntaxNode, T,
-    ast::{self, HasName, edit::IndentLevel, edit_in_place::Indent, make},
+    ast::{self, HasName, edit::IndentLevel, make},
 };
 
 use crate::{
@@ -479,10 +480,9 @@ fn add_enum_def(
             ctx.sema.scope(name.syntax()).map(|scope| scope.module())
         })
         .any(|module| module.nearest_non_block_module(ctx.db()) != *target_module);
-    let enum_def = make_bool_enum(make_enum_pub);
 
     let indent = IndentLevel::from_node(&insert_before);
-    enum_def.reindent_to(indent);
+    let enum_def = make_bool_enum(make_enum_pub).reset_indent().indent(indent);
 
     edit.insert(
         insert_before.text_range().start(),

From c6b1f8ad9a525ccf50886eb0f0ce60d81195f006 Mon Sep 17 00:00:00 2001
From: bit-aloo 
Date: Fri, 13 Feb 2026 13:14:06 +0530
Subject: [PATCH 071/103] remove edit_in_place from generate_new assist with
 edit::AstNodeEdit

---
 .../crates/ide-assists/src/handlers/generate_new.rs | 13 ++++++-------
 1 file changed, 6 insertions(+), 7 deletions(-)

diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_new.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_new.rs
index 4b923ab556b8c..793211a27b478 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_new.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_new.rs
@@ -3,7 +3,7 @@ use ide_db::{
     use_trivial_constructor::use_trivial_constructor,
 };
 use syntax::{
-    ast::{self, AstNode, HasName, HasVisibility, StructKind, edit_in_place::Indent, make},
+    ast::{self, AstNode, HasName, HasVisibility, StructKind, edit::AstNodeEdit, make},
     syntax_editor::Position,
 };
 
@@ -150,14 +150,14 @@ pub(crate) fn generate_new(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option
             false,
             false,
         )
-        .clone_for_update();
-        fn_.indent(1.into());
+        .clone_for_update()
+        .indent(1.into());
 
         let mut editor = builder.make_editor(strukt.syntax());
 
         // Get the node for set annotation
         let contain_fn = if let Some(impl_def) = impl_def {
-            fn_.indent(impl_def.indent_level());
+            let fn_ = fn_.indent(impl_def.indent_level());
 
             if let Some(l_curly) = impl_def.assoc_item_list().and_then(|list| list.l_curly_token())
             {
@@ -182,9 +182,8 @@ pub(crate) fn generate_new(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option
             let indent_level = strukt.indent_level();
             let body = vec![ast::AssocItem::Fn(fn_)];
             let list = make::assoc_item_list(Some(body));
-            let impl_def = generate_impl_with_item(&ast::Adt::Struct(strukt.clone()), Some(list));
-
-            impl_def.indent(strukt.indent_level());
+            let impl_def = generate_impl_with_item(&ast::Adt::Struct(strukt.clone()), Some(list))
+                .indent(strukt.indent_level());
 
             // Insert it after the adt
             editor.insert_all(

From b132beb7e0484d116dfe29530ce2c4a0aa716669 Mon Sep 17 00:00:00 2001
From: Lukas Wirth 
Date: Sat, 7 Feb 2026 14:52:48 +0100
Subject: [PATCH 072/103] internal: Use rayon for proc-macro loading

---
 src/tools/rust-analyzer/Cargo.lock            |  1 +
 .../crates/load-cargo/src/lib.rs              |  7 ++---
 .../crates/proc-macro-api/Cargo.toml          |  1 +
 .../crates/proc-macro-api/src/lib.rs          |  8 ++----
 .../crates/proc-macro-api/src/pool.rs         | 28 +++++++++----------
 .../crates/proc-macro-api/src/process.rs      | 16 ++++++++---
 .../rust-analyzer/crates/span/src/ast_id.rs   |  2 --
 7 files changed, 31 insertions(+), 32 deletions(-)

diff --git a/src/tools/rust-analyzer/Cargo.lock b/src/tools/rust-analyzer/Cargo.lock
index 755ae55eea462..5c2443e26e0fc 100644
--- a/src/tools/rust-analyzer/Cargo.lock
+++ b/src/tools/rust-analyzer/Cargo.lock
@@ -1845,6 +1845,7 @@ dependencies = [
  "paths",
  "postcard",
  "proc-macro-srv",
+ "rayon",
  "rustc-hash 2.1.1",
  "semver",
  "serde",
diff --git a/src/tools/rust-analyzer/crates/load-cargo/src/lib.rs b/src/tools/rust-analyzer/crates/load-cargo/src/lib.rs
index c2935d94a8a71..70a00cf825162 100644
--- a/src/tools/rust-analyzer/crates/load-cargo/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/load-cargo/src/lib.rs
@@ -26,10 +26,7 @@ use ide_db::{
 use itertools::Itertools;
 use proc_macro_api::{
     MacroDylib, ProcMacroClient,
-    bidirectional_protocol::{
-        msg::{SubRequest, SubResponse},
-        reject_subrequests,
-    },
+    bidirectional_protocol::msg::{SubRequest, SubResponse},
 };
 use project_model::{CargoConfig, PackageRoot, ProjectManifest, ProjectWorkspace};
 use span::{Span, SpanAnchor, SyntaxContext};
@@ -446,7 +443,7 @@ pub fn load_proc_macro(
 ) -> ProcMacroLoadResult {
     let res: Result, _> = (|| {
         let dylib = MacroDylib::new(path.to_path_buf());
-        let vec = server.load_dylib(dylib, Some(&reject_subrequests)).map_err(|e| {
+        let vec = server.load_dylib(dylib).map_err(|e| {
             ProcMacroLoadingError::ProcMacroSrvError(format!("{e}").into_boxed_str())
         })?;
         if vec.is_empty() {
diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/Cargo.toml b/src/tools/rust-analyzer/crates/proc-macro-api/Cargo.toml
index 4de1a3e5dd7d5..a135a469e87e4 100644
--- a/src/tools/rust-analyzer/crates/proc-macro-api/Cargo.toml
+++ b/src/tools/rust-analyzer/crates/proc-macro-api/Cargo.toml
@@ -31,6 +31,7 @@ span = { path = "../span", version = "0.0.0", default-features = false}
 intern.workspace = true
 postcard.workspace = true
 semver.workspace = true
+rayon.workspace = true
 
 [features]
 sysroot-abi = ["proc-macro-srv", "proc-macro-srv/sysroot-abi"]
diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/src/lib.rs b/src/tools/rust-analyzer/crates/proc-macro-api/src/lib.rs
index e4b121b033d34..68b3afc3e8414 100644
--- a/src/tools/rust-analyzer/crates/proc-macro-api/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/proc-macro-api/src/lib.rs
@@ -198,12 +198,8 @@ impl ProcMacroClient {
     }
 
     /// Loads a proc-macro dylib into the server process returning a list of `ProcMacro`s loaded.
-    pub fn load_dylib(
-        &self,
-        dylib: MacroDylib,
-        callback: Option>,
-    ) -> Result, ServerError> {
-        self.pool.load_dylib(&dylib, callback)
+    pub fn load_dylib(&self, dylib: MacroDylib) -> Result, ServerError> {
+        self.pool.load_dylib(&dylib)
     }
 
     /// Checks if the proc-macro server has exited.
diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/src/pool.rs b/src/tools/rust-analyzer/crates/proc-macro-api/src/pool.rs
index a637bc0e480a4..e6541823da583 100644
--- a/src/tools/rust-analyzer/crates/proc-macro-api/src/pool.rs
+++ b/src/tools/rust-analyzer/crates/proc-macro-api/src/pool.rs
@@ -1,10 +1,9 @@
 //! A pool of proc-macro server processes
 use std::sync::Arc;
 
-use crate::{
-    MacroDylib, ProcMacro, ServerError, bidirectional_protocol::SubCallback,
-    process::ProcMacroServerProcess,
-};
+use rayon::iter::{IntoParallelIterator, ParallelIterator};
+
+use crate::{MacroDylib, ProcMacro, ServerError, process::ProcMacroServerProcess};
 
 #[derive(Debug, Clone)]
 pub(crate) struct ProcMacroServerPool {
@@ -50,11 +49,7 @@ impl ProcMacroServerPool {
         })
     }
 
-    pub(crate) fn load_dylib(
-        &self,
-        dylib: &MacroDylib,
-        callback: Option>,
-    ) -> Result, ServerError> {
+    pub(crate) fn load_dylib(&self, dylib: &MacroDylib) -> Result, ServerError> {
         let _span = tracing::info_span!("ProcMacroServer::load_dylib").entered();
 
         let dylib_path = Arc::new(dylib.path.clone());
@@ -64,14 +59,17 @@ impl ProcMacroServerPool {
         let (first, rest) = self.workers.split_first().expect("worker pool must not be empty");
 
         let macros = first
-            .find_proc_macros(&dylib.path, callback)?
+            .find_proc_macros(&dylib.path)?
             .map_err(|e| ServerError { message: e, io: None })?;
 
-        for worker in rest {
-            worker
-                .find_proc_macros(&dylib.path, callback)?
-                .map_err(|e| ServerError { message: e, io: None })?;
-        }
+        rest.into_par_iter()
+            .map(|worker| {
+                worker
+                    .find_proc_macros(&dylib.path)?
+                    .map(|_| ())
+                    .map_err(|e| ServerError { message: e, io: None })
+            })
+            .collect::>()?;
 
         Ok(macros
             .into_iter()
diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/src/process.rs b/src/tools/rust-analyzer/crates/proc-macro-api/src/process.rs
index 9f80880965b83..80e4ed05c36d8 100644
--- a/src/tools/rust-analyzer/crates/proc-macro-api/src/process.rs
+++ b/src/tools/rust-analyzer/crates/proc-macro-api/src/process.rs
@@ -18,7 +18,11 @@ use stdx::JodChild;
 
 use crate::{
     ProcMacro, ProcMacroKind, ProtocolFormat, ServerError,
-    bidirectional_protocol::{self, SubCallback, msg::BidirectionalMessage, reject_subrequests},
+    bidirectional_protocol::{
+        self, SubCallback,
+        msg::{BidirectionalMessage, SubResponse},
+        reject_subrequests,
+    },
     legacy_protocol::{self, SpanMode},
     version,
 };
@@ -207,14 +211,18 @@ impl ProcMacroServerProcess {
     pub(crate) fn find_proc_macros(
         &self,
         dylib_path: &AbsPath,
-        callback: Option>,
     ) -> Result, String>, ServerError> {
         match self.protocol {
             Protocol::LegacyJson { .. } => legacy_protocol::find_proc_macros(self, dylib_path),
 
             Protocol::BidirectionalPostcardPrototype { .. } => {
-                let cb = callback.expect("callback required for bidirectional protocol");
-                bidirectional_protocol::find_proc_macros(self, dylib_path, cb)
+                bidirectional_protocol::find_proc_macros(self, dylib_path, &|_| {
+                    Ok(SubResponse::Cancel {
+                        reason: String::from(
+                            "Server should not do a sub request when loading proc-macros",
+                        ),
+                    })
+                })
             }
         }
     }
diff --git a/src/tools/rust-analyzer/crates/span/src/ast_id.rs b/src/tools/rust-analyzer/crates/span/src/ast_id.rs
index 599b3c7175228..f52604e139177 100644
--- a/src/tools/rust-analyzer/crates/span/src/ast_id.rs
+++ b/src/tools/rust-analyzer/crates/span/src/ast_id.rs
@@ -88,7 +88,6 @@ impl fmt::Debug for ErasedFileAstId {
             Module,
             Static,
             Trait,
-            TraitAlias,
             Variant,
             Const,
             Fn,
@@ -129,7 +128,6 @@ enum ErasedFileAstIdKind {
     Module,
     Static,
     Trait,
-    TraitAlias,
     // Until here associated with `ErasedHasNameFileAstId`.
     // The following are associated with `ErasedAssocItemFileAstId`.
     Variant,

From c2c7d5d7efbce617577835fc8a02678ccf0847c6 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Fri, 13 Feb 2026 11:43:39 +0000
Subject: [PATCH 073/103] build(deps): bump qs from 6.14.1 to 6.14.2 in
 /editors/code

Bumps [qs](https://github.com/ljharb/qs) from 6.14.1 to 6.14.2.
- [Changelog](https://github.com/ljharb/qs/blob/main/CHANGELOG.md)
- [Commits](https://github.com/ljharb/qs/compare/v6.14.1...v6.14.2)

---
updated-dependencies:
- dependency-name: qs
  dependency-version: 6.14.2
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] 
---
 .../rust-analyzer/editors/code/package-lock.json     | 12 +++---------
 1 file changed, 3 insertions(+), 9 deletions(-)

diff --git a/src/tools/rust-analyzer/editors/code/package-lock.json b/src/tools/rust-analyzer/editors/code/package-lock.json
index 57f6bf69beb08..84be0a666f9ca 100644
--- a/src/tools/rust-analyzer/editors/code/package-lock.json
+++ b/src/tools/rust-analyzer/editors/code/package-lock.json
@@ -1486,7 +1486,6 @@
             "integrity": "sha512-4gbs64bnbSzu4FpgMiQ1A+D+urxkoJk/kqlDJ2W//5SygaEiAP2B4GoS7TEdxgwol2el03gckFV9lJ4QOMiiHg==",
             "dev": true,
             "license": "MIT",
-            "peer": true,
             "dependencies": {
                 "@typescript-eslint/scope-manager": "8.25.0",
                 "@typescript-eslint/types": "8.25.0",
@@ -1870,7 +1869,6 @@
             "integrity": "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==",
             "dev": true,
             "license": "MIT",
-            "peer": true,
             "bin": {
                 "acorn": "bin/acorn"
             },
@@ -2840,7 +2838,6 @@
             "resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-3.0.0.tgz",
             "integrity": "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==",
             "license": "ISC",
-            "peer": true,
             "engines": {
                 "node": ">=12"
             }
@@ -3322,7 +3319,6 @@
             "integrity": "sha512-KjeihdFqTPhOMXTt7StsDxriV4n66ueuF/jfPNC3j/lduHwr/ijDwJMsF+wyMJethgiKi5wniIE243vi07d3pg==",
             "dev": true,
             "license": "MIT",
-            "peer": true,
             "dependencies": {
                 "@eslint-community/eslint-utils": "^4.2.0",
                 "@eslint-community/regexpp": "^4.12.1",
@@ -4410,7 +4406,6 @@
             "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.4.2.tgz",
             "integrity": "sha512-rg9zJN+G4n2nfJl5MW3BMygZX56zKPNVEYYqq7adpmMh4Jn2QNEwhvQlFy6jPVdcod7txZtKHWnyZiA3a0zP7A==",
             "license": "MIT",
-            "peer": true,
             "bin": {
                 "jiti": "lib/jiti-cli.mjs"
             }
@@ -5584,9 +5579,9 @@
             }
         },
         "node_modules/qs": {
-            "version": "6.14.1",
-            "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.1.tgz",
-            "integrity": "sha512-4EK3+xJl8Ts67nLYNwqw/dsFVnCf+qR7RgXSK9jEEm9unao3njwMDdmsdvoKBKHzxd7tCYz5e5M+SnMjdtXGQQ==",
+            "version": "6.14.2",
+            "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.2.tgz",
+            "integrity": "sha512-V/yCWTTF7VJ9hIh18Ugr2zhJMP01MY7c5kh4J870L7imm6/DIzBsNLTXzMwUA3yZ5b/KBqLx8Kp3uRvd7xSe3Q==",
             "dev": true,
             "license": "BSD-3-Clause",
             "dependencies": {
@@ -6678,7 +6673,6 @@
             "integrity": "sha512-84MVSjMEHP+FQRPy3pX9sTVV/INIex71s9TL2Gm5FG/WG1SqXeKyZ0k7/blY/4FdOzI12CBy1vGc4og/eus0fw==",
             "dev": true,
             "license": "Apache-2.0",
-            "peer": true,
             "bin": {
                 "tsc": "bin/tsc",
                 "tsserver": "bin/tsserver"

From da9ff14a13d072835afc9e70740547d5b0aacc87 Mon Sep 17 00:00:00 2001
From: Lukas Wirth 
Date: Fri, 13 Feb 2026 16:23:59 +0100
Subject: [PATCH 074/103] Remove incorrect warning log

---
 .../rust-analyzer/crates/rust-analyzer/src/flycheck.rs     | 7 -------
 1 file changed, 7 deletions(-)

diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/flycheck.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/flycheck.rs
index c74f4550fd890..797b95994073f 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/flycheck.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/flycheck.rs
@@ -757,13 +757,6 @@ impl FlycheckActor {
                                         DiagnosticsReceived::AtLeastOneAndClearedWorkspace;
                                 }
 
-                                if let Some(package_id) = package_id {
-                                    tracing::warn!(
-                                        "Ignoring package label {:?} and applying diagnostics to the whole workspace",
-                                        package_id
-                                    );
-                                }
-
                                 self.send(FlycheckMessage::AddDiagnostic {
                                     id: self.id,
                                     generation: self.generation,

From 7ce98394cce86a54dbf4c79fc71a0d7ecbccb7e4 Mon Sep 17 00:00:00 2001
From: bit-aloo 
Date: Fri, 13 Feb 2026 22:58:11 +0530
Subject: [PATCH 075/103] migrate generate_impl to use AstNodeEdit

---
 .../ide-assists/src/handlers/generate_impl.rs | 21 +++++++++++++------
 1 file changed, 15 insertions(+), 6 deletions(-)

diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_impl.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_impl.rs
index 77eb8efc6f6af..bbd42481ef0fa 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_impl.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_impl.rs
@@ -1,5 +1,5 @@
 use syntax::{
-    ast::{self, AstNode, HasGenericParams, HasName, edit_in_place::Indent, make},
+    ast::{self, AstNode, HasGenericParams, HasName, edit::AstNodeEdit, make},
     syntax_editor::{Position, SyntaxEditor},
 };
 
@@ -8,10 +8,14 @@ use crate::{
     utils::{self, DefaultMethods, IgnoreAssocItems},
 };
 
-fn insert_impl(editor: &mut SyntaxEditor, impl_: &ast::Impl, nominal: &impl Indent) {
+fn insert_impl(
+    editor: &mut SyntaxEditor,
+    impl_: &ast::Impl,
+    nominal: &impl AstNodeEdit,
+) -> ast::Impl {
     let indent = nominal.indent_level();
 
-    impl_.indent(indent);
+    let impl_ = impl_.indent(indent);
     editor.insert_all(
         Position::after(nominal.syntax()),
         vec![
@@ -20,6 +24,8 @@ fn insert_impl(editor: &mut SyntaxEditor, impl_: &ast::Impl, nominal: &impl Inde
             impl_.syntax().clone().into(),
         ],
     );
+
+    impl_
 }
 
 // Assist: generate_impl
@@ -57,6 +63,8 @@ pub(crate) fn generate_impl(acc: &mut Assists, ctx: &AssistContext<'_>) -> Optio
             let impl_ = utils::generate_impl(&nominal);
 
             let mut editor = edit.make_editor(nominal.syntax());
+
+            let impl_ = insert_impl(&mut editor, &impl_, &nominal);
             // Add a tabstop after the left curly brace
             if let Some(cap) = ctx.config.snippet_cap
                 && let Some(l_curly) = impl_.assoc_item_list().and_then(|it| it.l_curly_token())
@@ -65,7 +73,6 @@ pub(crate) fn generate_impl(acc: &mut Assists, ctx: &AssistContext<'_>) -> Optio
                 editor.add_annotation(l_curly, tabstop);
             }
 
-            insert_impl(&mut editor, &impl_, &nominal);
             edit.add_file_edits(ctx.vfs_file_id(), editor);
         },
     )
@@ -106,6 +113,8 @@ pub(crate) fn generate_trait_impl(acc: &mut Assists, ctx: &AssistContext<'_>) ->
             let impl_ = utils::generate_trait_impl_intransitive(&nominal, make::ty_placeholder());
 
             let mut editor = edit.make_editor(nominal.syntax());
+
+            let impl_ = insert_impl(&mut editor, &impl_, &nominal);
             // Make the trait type a placeholder snippet
             if let Some(cap) = ctx.config.snippet_cap {
                 if let Some(trait_) = impl_.trait_() {
@@ -119,7 +128,6 @@ pub(crate) fn generate_trait_impl(acc: &mut Assists, ctx: &AssistContext<'_>) ->
                 }
             }
 
-            insert_impl(&mut editor, &impl_, &nominal);
             edit.add_file_edits(ctx.vfs_file_id(), editor);
         },
     )
@@ -206,6 +214,8 @@ pub(crate) fn generate_impl_trait(acc: &mut Assists, ctx: &AssistContext<'_>) ->
                 make_impl_(Some(assoc_item_list))
             };
 
+            let impl_ = insert_impl(&mut editor, &impl_, &trait_);
+
             if let Some(cap) = ctx.config.snippet_cap {
                 if let Some(generics) = impl_.trait_().and_then(|it| it.generic_arg_list()) {
                     for generic in generics.generic_args() {
@@ -232,7 +242,6 @@ pub(crate) fn generate_impl_trait(acc: &mut Assists, ctx: &AssistContext<'_>) ->
                 }
             }
 
-            insert_impl(&mut editor, &impl_, &trait_);
             edit.add_file_edits(ctx.vfs_file_id(), editor);
         },
     )

From a9cc42e5763b55e95aed9adf48435a03ac4abf1f Mon Sep 17 00:00:00 2001
From: Lukas Wirth 
Date: Fri, 13 Feb 2026 19:19:07 +0100
Subject: [PATCH 076/103] Revert "fix: Stale diagnostics with rust-project.json
 and rustc JSON"

This reverts commit 2cefe47e6d55c186b68687bf677bf0d5eb65a922.
---
 .../crates/rust-analyzer/src/flycheck.rs      | 81 +++++++------------
 1 file changed, 30 insertions(+), 51 deletions(-)

diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/flycheck.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/flycheck.rs
index 797b95994073f..47f7a57f72eca 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/flycheck.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/flycheck.rs
@@ -741,63 +741,42 @@ impl FlycheckActor {
                             flycheck_id = self.id,
                             message = diagnostic.message,
                             package_id = package_id.as_ref().map(|it| it.as_str()),
-                            scope = ?self.scope,
                             "diagnostic received"
                         );
-
-                        match &self.scope {
-                            FlycheckScope::Workspace => {
-                                if self.diagnostics_received == DiagnosticsReceived::NotYet {
-                                    self.send(FlycheckMessage::ClearDiagnostics {
-                                        id: self.id,
-                                        kind: ClearDiagnosticsKind::All(ClearScope::Workspace),
-                                    });
-
-                                    self.diagnostics_received =
-                                        DiagnosticsReceived::AtLeastOneAndClearedWorkspace;
-                                }
-
-                                self.send(FlycheckMessage::AddDiagnostic {
-                                    id: self.id,
-                                    generation: self.generation,
-                                    package_id: None,
-                                    workspace_root: self.root.clone(),
-                                    diagnostic,
-                                });
-                            }
-                            FlycheckScope::Package { package: flycheck_package, .. } => {
-                                if self.diagnostics_received == DiagnosticsReceived::NotYet {
-                                    self.diagnostics_received = DiagnosticsReceived::AtLeastOne;
-                                }
-
-                                // If the package has been set in the diagnostic JSON, respect that. Otherwise, use the
-                                // package that the current flycheck is scoped to. This is useful when a project is
-                                // directly using rustc for its checks (e.g. custom check commands in rust-project.json).
-                                let package_id = package_id.unwrap_or(flycheck_package.clone());
-
-                                if self.diagnostics_cleared_for.insert(package_id.clone()) {
-                                    tracing::trace!(
-                                        flycheck_id = self.id,
-                                        package_id = package_id.as_str(),
-                                        "clearing diagnostics"
-                                    );
-                                    self.send(FlycheckMessage::ClearDiagnostics {
-                                        id: self.id,
-                                        kind: ClearDiagnosticsKind::All(ClearScope::Package(
-                                            package_id.clone(),
-                                        )),
-                                    });
-                                }
-
-                                self.send(FlycheckMessage::AddDiagnostic {
+                        if self.diagnostics_received == DiagnosticsReceived::NotYet {
+                            self.diagnostics_received = DiagnosticsReceived::AtLeastOne;
+                        }
+                        if let Some(package_id) = &package_id {
+                            if self.diagnostics_cleared_for.insert(package_id.clone()) {
+                                tracing::trace!(
+                                    flycheck_id = self.id,
+                                    package_id = package_id.as_str(),
+                                    "clearing diagnostics"
+                                );
+                                self.send(FlycheckMessage::ClearDiagnostics {
                                     id: self.id,
-                                    generation: self.generation,
-                                    package_id: Some(package_id),
-                                    workspace_root: self.root.clone(),
-                                    diagnostic,
+                                    kind: ClearDiagnosticsKind::All(ClearScope::Package(
+                                        package_id.clone(),
+                                    )),
                                 });
                             }
+                        } else if self.diagnostics_received
+                            != DiagnosticsReceived::AtLeastOneAndClearedWorkspace
+                        {
+                            self.diagnostics_received =
+                                DiagnosticsReceived::AtLeastOneAndClearedWorkspace;
+                            self.send(FlycheckMessage::ClearDiagnostics {
+                                id: self.id,
+                                kind: ClearDiagnosticsKind::All(ClearScope::Workspace),
+                            });
                         }
+                        self.send(FlycheckMessage::AddDiagnostic {
+                            id: self.id,
+                            generation: self.generation,
+                            package_id,
+                            workspace_root: self.root.clone(),
+                            diagnostic,
+                        });
                     }
                 },
             }

From 42479673c56606e04a474b981384e805f71644a2 Mon Sep 17 00:00:00 2001
From: Roy Ammerschuber 
Date: Thu, 5 Feb 2026 15:05:51 +0100
Subject: [PATCH 077/103] simplify wildcard datastructure

---
 .../src/borrow_tracker/tree_borrows/tree.rs   |  79 +--
 .../borrow_tracker/tree_borrows/wildcard.rs   | 591 +++++-------------
 2 files changed, 190 insertions(+), 480 deletions(-)

diff --git a/src/tools/miri/src/borrow_tracker/tree_borrows/tree.rs b/src/tools/miri/src/borrow_tracker/tree_borrows/tree.rs
index 2c6be522837cb..fc8271454796f 100644
--- a/src/tools/miri/src/borrow_tracker/tree_borrows/tree.rs
+++ b/src/tools/miri/src/borrow_tracker/tree_borrows/tree.rs
@@ -26,7 +26,7 @@ use super::foreign_access_skipping::IdempotentForeignAccess;
 use super::perms::{PermTransition, Permission};
 use super::tree_visitor::{ChildrenVisitMode, ContinueTraversal, NodeAppArgs, TreeVisitor};
 use super::unimap::{UniIndex, UniKeyMap, UniValMap};
-use super::wildcard::WildcardState;
+use super::wildcard::ExposedCache;
 use crate::borrow_tracker::{AccessKind, GlobalState, ProtectorKind};
 use crate::*;
 
@@ -89,7 +89,7 @@ impl LocationState {
         &mut self,
         idx: UniIndex,
         nodes: &mut UniValMap,
-        wildcard_accesses: &mut UniValMap,
+        exposed_cache: &mut ExposedCache,
         access_kind: AccessKind,
         relatedness: AccessRelatedness,
         protected: bool,
@@ -99,7 +99,7 @@ impl LocationState {
         // ensures it is only called when `skip_if_known_noop` returns
         // `Recurse`, due to the contract of `traverse_this_parents_children_other`.
         self.record_new_access(access_kind, relatedness);
-
+        let old_access_level = self.permission.strongest_allowed_local_access(protected);
         let transition = self.perform_access(access_kind, relatedness, protected)?;
         if !transition.is_noop() {
             let node = nodes.get_mut(idx).unwrap();
@@ -111,8 +111,8 @@ impl LocationState {
             // We need to update the wildcard state, if the permission
             // of an exposed pointer changes.
             if node.is_exposed {
-                let access_type = self.permission.strongest_allowed_local_access(protected);
-                WildcardState::update_exposure(idx, access_type, nodes, wildcard_accesses);
+                let access_level = self.permission.strongest_allowed_local_access(protected);
+                exposed_cache.update_exposure(nodes, idx, old_access_level, access_level);
             }
         }
         Ok(())
@@ -261,14 +261,8 @@ pub struct LocationTree {
     ///
     /// We do uphold the fact that `keys(perms)` is a subset of `keys(nodes)`
     pub perms: UniValMap,
-    /// Maps a tag and a location to its wildcard access tracking information,
-    /// with possible lazy initialization.
-    ///
-    /// If this allocation doesn't have any exposed nodes, then this map doesn't get
-    /// initialized. This way we only need to allocate the map if we need it.
-    ///
-    /// NOTE: same guarantees on entry initialization as for `perms`.
-    pub wildcard_accesses: UniValMap,
+    /// Caches information about the relatedness of nodes for a wildcard access.
+    pub exposed_cache: ExposedCache,
 }
 /// Tree structure with both parents and children since we want to be
 /// able to traverse the tree efficiently in both directions.
@@ -276,7 +270,7 @@ pub struct LocationTree {
 pub struct Tree {
     /// Mapping from tags to keys. The key obtained can then be used in
     /// any of the `UniValMap` relative to this allocation, i.e.
-    /// `nodes`, `LocationTree::perms` and `LocationTree::wildcard_accesses`
+    /// `nodes`, `LocationTree::perms` and `LocationTree::exposed_cache`
     /// of the same `Tree`.
     /// The parent-child relationship in `Node` is encoded in terms of these same
     /// keys, so traversing the entire tree needs exactly one access to
@@ -372,8 +366,8 @@ impl Tree {
                     IdempotentForeignAccess::None,
                 ),
             );
-            let wildcard_accesses = UniValMap::default();
-            DedupRangeMap::new(size, LocationTree { perms, wildcard_accesses })
+            let exposed_cache = ExposedCache::default();
+            DedupRangeMap::new(size, LocationTree { perms, exposed_cache })
         };
         Self { roots: SmallVec::from_slice(&[root_idx]), nodes, locations, tag_mapping }
     }
@@ -451,19 +445,9 @@ impl<'tcx> Tree {
             }
         }
 
-        // We need to ensure the consistency of the wildcard access tracking data structure.
-        // For this, we insert the correct entry for this tag based on its parent, if it exists.
-        // If we are inserting a new wildcard root (with Wildcard as parent_prov) then we insert
-        // the special wildcard root initial state instead.
-        for (_range, loc) in self.locations.iter_mut_all() {
-            if let Some(parent_idx) = parent_idx {
-                if let Some(parent_access) = loc.wildcard_accesses.get(parent_idx) {
-                    loc.wildcard_accesses.insert(idx, parent_access.for_new_child());
-                }
-            } else {
-                loc.wildcard_accesses.insert(idx, WildcardState::for_wildcard_root());
-            }
-        }
+        // We don't have to update `exposed_cache` as the new node is not exposed and
+        // has no children so the default counts of 0 are correct.
+
         // If the parent is a wildcard pointer, then it doesn't track SIFA and doesn't need to be updated.
         if let Some(parent_idx) = parent_idx {
             // Inserting the new perms might have broken the SIFA invariant (see
@@ -807,7 +791,7 @@ impl Tree {
         let node = self.nodes.remove(this).unwrap();
         for (_range, loc) in self.locations.iter_mut_all() {
             loc.perms.remove(this);
-            loc.wildcard_accesses.remove(this);
+            loc.exposed_cache.remove(this);
         }
         self.tag_mapping.remove(&node.tag);
     }
@@ -943,7 +927,7 @@ impl<'tcx> LocationTree {
         };
 
         let accessed_root_tag = accessed_root.map(|idx| nodes.get(idx).unwrap().tag);
-        for root in roots {
+        for (i, root) in roots.enumerate() {
             let tag = nodes.get(root).unwrap().tag;
             // On a protector release access we have to skip the children of the accessed tag.
             // However, if the tag has exposed children then some of the wildcard subtrees could
@@ -981,6 +965,7 @@ impl<'tcx> LocationTree {
                 access_kind,
                 global,
                 diagnostics,
+                /*is_wildcard_tree*/ i != 0,
             )?;
         }
         interp_ok(())
@@ -1029,7 +1014,7 @@ impl<'tcx> LocationTree {
                 .perform_transition(
                     args.idx,
                     args.nodes,
-                    &mut args.data.wildcard_accesses,
+                    &mut args.data.exposed_cache,
                     access_kind,
                     args.rel_pos,
                     protected,
@@ -1074,12 +1059,18 @@ impl<'tcx> LocationTree {
         access_kind: AccessKind,
         global: &GlobalState,
         diagnostics: &DiagnosticInfo,
+        is_wildcard_tree: bool,
     ) -> InterpResult<'tcx> {
         let get_relatedness = |idx: UniIndex, node: &Node, loc: &LocationTree| {
-            let wildcard_state = loc.wildcard_accesses.get(idx).cloned().unwrap_or_default();
             // If the tag is larger than `max_local_tag` then the access can only be foreign.
             let only_foreign = max_local_tag.is_some_and(|max_local_tag| max_local_tag < node.tag);
-            wildcard_state.access_relatedness(access_kind, only_foreign)
+            loc.exposed_cache.access_relatedness(
+                root,
+                idx,
+                access_kind,
+                is_wildcard_tree,
+                only_foreign,
+            )
         };
 
         // Whether there is an exposed node in this tree that allows this access.
@@ -1156,7 +1147,7 @@ impl<'tcx> LocationTree {
                 perm.perform_transition(
                     args.idx,
                     args.nodes,
-                    &mut args.data.wildcard_accesses,
+                    &mut args.data.exposed_cache,
                     access_kind,
                     relatedness,
                     protected,
@@ -1175,19 +1166,11 @@ impl<'tcx> LocationTree {
                 })
             },
         )?;
-        // If there is no exposed node in this tree that allows this access, then the
-        // access *must* be foreign. So we check if the root of this tree would allow this
-        // as a foreign access, and if not, then we can error.
-        // In practice, all wildcard trees accept foreign accesses, but the main tree does
-        // not, so this catches UB when none of the nodes in the main tree allows this access.
-        if !has_valid_exposed
-            && self
-                .wildcard_accesses
-                .get(root)
-                .unwrap()
-                .access_relatedness(access_kind, /* only_foreign */ true)
-                .is_none()
-        {
+        // If there is no exposed node in this tree that allows this access, then the access *must*
+        // be foreign to the entire subtree. Foreign accesses are only possible on wildcard subtrees
+        // as there are no ancestors to the main root. So if we do not find a valid exposed node in
+        // the main tree then this access is UB.
+        if !has_valid_exposed && !is_wildcard_tree {
             return Err(no_valid_exposed_references_error(diagnostics)).into();
         }
         interp_ok(())
diff --git a/src/tools/miri/src/borrow_tracker/tree_borrows/wildcard.rs b/src/tools/miri/src/borrow_tracker/tree_borrows/wildcard.rs
index b5ae0ee4c7d31..b03635de70ae7 100644
--- a/src/tools/miri/src/borrow_tracker/tree_borrows/wildcard.rs
+++ b/src/tools/miri/src/borrow_tracker/tree_borrows/wildcard.rs
@@ -1,4 +1,3 @@
-use std::cmp::max;
 use std::fmt::Debug;
 
 use super::Tree;
@@ -51,374 +50,142 @@ impl WildcardAccessRelatedness {
     }
 }
 
+/// Caches information about where in the tree exposed nodes with permission to do reads/ rites are
+/// located. [`ExposedCache`] stores this information a single location (or rather, a range of
+/// homogeneous locations) for all nodes in an allocation.
+///
+/// Nodes not in this map have a default [`ExposedCacheNode`], i.e. they have no exposed children.
+/// In particular, this map remains empty (and thus consumes no memory) until the first
+/// node in the tree gets exposed.
+#[derive(Clone, Debug, Default, PartialEq, Eq)]
+pub struct ExposedCache(UniValMap);
+
 /// State per location per node keeping track of where relative to this
 /// node exposed nodes are and what access permissions they have.
-///
-/// Designed to be completely determined by its parent, siblings and
-/// direct children's max_local_access/max_foreign_access.
-#[derive(Clone, Default, PartialEq, Eq)]
-pub struct WildcardState {
-    /// How many of this node's direct children have `max_local_access()==Write`.
-    child_writes: u16,
-    /// How many of this node's direct children have `max_local_access()>=Read`.
-    child_reads: u16,
-    /// The maximum access level that could happen from an exposed node
-    /// that is foreign to this node.
-    ///
-    /// This is calculated as the `max()` of the parent's `max_foreign_access`,
-    /// `exposed_as` and the siblings' `max_local_access()`.
-    max_foreign_access: WildcardAccessLevel,
-    /// At what access level this node itself is exposed.
-    exposed_as: WildcardAccessLevel,
-}
-impl Debug for WildcardState {
-    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
-        f.debug_struct("WildcardState")
-            .field("child_r/w", &(self.child_reads, self.child_writes))
-            .field("foreign", &self.max_foreign_access)
-            .field("exposed_as", &self.exposed_as)
-            .finish()
-    }
+#[derive(Clone, Default, Debug, PartialEq, Eq)]
+struct ExposedCacheNode {
+    /// How many local nodes (in this subtree) are exposed with write permissions.
+    local_writes: u16,
+    /// How many local nodes (in this subtree) are exposed with read permissions.
+    local_reads: u16,
 }
-impl WildcardState {
-    /// The maximum access level that could happen from an exposed
-    /// node that is local to this node.
-    fn max_local_access(&self) -> WildcardAccessLevel {
-        use WildcardAccessLevel::*;
-        max(
-            self.exposed_as,
-            if self.child_writes > 0 {
-                Write
-            } else if self.child_reads > 0 {
-                Read
-            } else {
-                None
-            },
-        )
-    }
 
-    /// From where relative to the node with this wildcard info a read or write access could happen.
-    /// If `only_foreign` is true then we treat `LocalAccess` as impossible. This means we return
-    /// `None` if only a `LocalAccess` is possible, and we treat `EitherAccess` as a
-    /// `ForeignAccess`.
+impl ExposedCache {
+    /// Returns the relatedness of a wildcard access to a node.
+    ///
+    /// This function only considers a single subtree. If the current subtree does not contain
+    /// any valid exposed nodes then the function return `None`.
+    ///
+    /// * `root`: The root of the subtree the node belongs to.
+    /// * `id`: The id of the node.
+    /// * `kind`: The kind of the wildcard access.
+    /// * `is_wildcard_tree`: This nodes belongs to a wildcard subtree.
+    ///   This means we always treat foreign accesses as possible.
+    /// * `only_foreign`: Assume the access cannot come from a local node.
     pub fn access_relatedness(
         &self,
+        root: UniIndex,
+        id: UniIndex,
         kind: AccessKind,
+        is_wildcard_tree: bool,
         only_foreign: bool,
     ) -> Option {
-        let rel = match kind {
-            AccessKind::Read => self.read_access_relatedness(),
-            AccessKind::Write => self.write_access_relatedness(),
+        // All nodes in the tree are local to the root, so we can use the root to get the total
+        // number of valid exposed nodes in the tree.
+        let root = self.0.get(root).cloned().unwrap_or_default();
+        let node = self.0.get(id).cloned().unwrap_or_default();
+
+        let (total_num, local_num) = match kind {
+            AccessKind::Read => (root.local_reads, node.local_reads),
+            AccessKind::Write => (root.local_writes, node.local_writes),
         };
-        if only_foreign {
-            use WildcardAccessRelatedness as E;
-            match rel {
-                Some(E::EitherAccess | E::ForeignAccess) => Some(E::ForeignAccess),
-                Some(E::LocalAccess) | None => None,
-            }
-        } else {
-            rel
-        }
-    }
 
-    /// From where relative to the node with this wildcard info a read access could happen.
-    fn read_access_relatedness(&self) -> Option {
-        let has_foreign = self.max_foreign_access >= WildcardAccessLevel::Read;
-        let has_local = self.max_local_access() >= WildcardAccessLevel::Read;
-        use WildcardAccessRelatedness as E;
-        match (has_foreign, has_local) {
-            (true, true) => Some(E::EitherAccess),
-            (true, false) => Some(E::ForeignAccess),
-            (false, true) => Some(E::LocalAccess),
-            (false, false) => None,
-        }
-    }
-
-    /// From where relative to the node with this wildcard info a write access could happen.
-    fn write_access_relatedness(&self) -> Option {
-        let has_foreign = self.max_foreign_access == WildcardAccessLevel::Write;
-        let has_local = self.max_local_access() == WildcardAccessLevel::Write;
-        use WildcardAccessRelatedness as E;
-        match (has_foreign, has_local) {
-            (true, true) => Some(E::EitherAccess),
-            (true, false) => Some(E::ForeignAccess),
-            (false, true) => Some(E::LocalAccess),
-            (false, false) => None,
-        }
-    }
-
-    /// Gets the access tracking information for a new child node of a parent with this
-    /// wildcard info.
-    /// The new node doesn't have any child reads/writes, but calculates `max_foreign_access`
-    /// from its parent.
-    pub fn for_new_child(&self) -> Self {
-        Self {
-            max_foreign_access: max(self.max_foreign_access, self.max_local_access()),
-            ..Default::default()
-        }
-    }
-    /// Crates the initial `WildcardState` for a wildcard root.
-    /// This has `max_foreign_access==Write` as it actually is the child of *some* exposed node
-    /// through which we can receive foreign accesses.
-    ///
-    /// This is different from the main root which has `max_foreign_access==None`, since there
-    /// cannot be a foreign access to the root of the allocation.
-    pub fn for_wildcard_root() -> Self {
-        Self { max_foreign_access: WildcardAccessLevel::Write, ..Default::default() }
-    }
-
-    /// Pushes the nodes of `children` onto the stack who's `max_foreign_access`
-    /// needs to be updated.
-    ///
-    /// * `children`: A list of nodes with the same parent. `children` doesn't
-    ///   necessarily have to contain all children of parent, but can just be
-    ///   a subset.
-    ///
-    /// * `child_reads`, `child_writes`: How many of `children` have `max_local_access()`
-    ///   of at least `read`/`write`
-    ///
-    /// * `new_foreign_access`, `old_foreign_access`:
-    ///   The max possible access level that is foreign to all `children`
-    ///   (i.e., it is not local to *any* of them).
-    ///   This can be calculated as the max of the parent's `exposed_as()`, `max_foreign_access`
-    ///   and of all `max_local_access()` of any nodes with the same parent that are
-    ///   not listed in `children`.
-    ///
-    ///   This access level changed from `old` to `new`, which is why we need to
-    ///   update `children`.
-    fn push_relevant_children(
-        stack: &mut Vec<(UniIndex, WildcardAccessLevel)>,
-        new_foreign_access: WildcardAccessLevel,
-        old_foreign_access: WildcardAccessLevel,
-        child_reads: u16,
-        child_writes: u16,
-        children: impl Iterator,
-
-        wildcard_accesses: &UniValMap,
-    ) {
-        use WildcardAccessLevel::*;
-
-        // Nothing changed so we don't need to update anything.
-        if new_foreign_access == old_foreign_access {
-            return;
-        }
-
-        // We need to consider that the children's `max_local_access()` affect each
-        // other's `max_foreign_access`, but do not affect their own `max_foreign_access`.
-
-        // The new `max_foreign_acces` for children with `max_local_access()==Write`.
-        let write_foreign_access = max(
-            new_foreign_access,
-            if child_writes > 1 {
-                // There exists at least one more child with exposed write access.
-                // This means that a foreign write through that node is possible.
-                Write
-            } else if child_reads > 1 {
-                // There exists at least one more child with exposed read access,
-                // but no other with write access.
-                // This means that a foreign read but no write through that node
-                // is possible.
-                Read
-            } else {
-                // There are no other nodes with read or write access.
-                // This means no foreign writes through other children are possible.
-                None
-            },
-        );
-
-        // The new `max_foreign_acces` for children with `max_local_access()==Read`.
-        let read_foreign_access = max(
-            new_foreign_access,
-            if child_writes > 0 {
-                // There exists at least one child with write access (and it's not this one).
-                Write
-            } else if child_reads > 1 {
-                // There exists at least one more child with exposed read access,
-                // but no other with write access.
-                Read
-            } else {
-                // There are no other nodes with read or write access,
-                None
-            },
-        );
-
-        // The new `max_foreign_acces` for children with `max_local_access()==None`.
-        let none_foreign_access = max(
-            new_foreign_access,
-            if child_writes > 0 {
-                // There exists at least one child with write access (and it's not this one).
-                Write
-            } else if child_reads > 0 {
-                // There exists at least one child with read access (and it's not this one),
-                // but none with write access.
-                Read
+        // If this is a wildcard tree then an access can always be foreign as
+        // it could come from another tree.
+        // We can represent this by adding 1 to the total which means there
+        // always exists a foreign exposed node.
+        // (We cannot bake this into the root's count as then if `node == root` it would
+        // affect both `total` and `local`.)
+        let total_num = total_num + u16::from(is_wildcard_tree);
+
+        use WildcardAccessRelatedness::*;
+        let relatedness = if total_num == 0 {
+            // we return None if the tree does not contain any valid exposed nodes.
+            None
+        } else {
+            Some(if total_num == local_num {
+                // If all valid exposed nodes are local to this node then the access is local.
+                LocalAccess
+            } else if local_num == 0 {
+                // If the node does not have any exposed nodes as children then the access is foreign.
+                ForeignAccess
             } else {
-                // No children are exposed as read or write.
-                None
-            },
-        );
-
-        stack.extend(children.filter_map(|child| {
-            let state = wildcard_accesses.get(child).cloned().unwrap_or_default();
-
-            let new_foreign_access = match state.max_local_access() {
-                Write => write_foreign_access,
-                Read => read_foreign_access,
-                None => none_foreign_access,
-            };
+                // If some but not all of the valid exposed nodes are local then we cannot determine the correct relatedness.
+                EitherAccess
+            })
+        };
 
-            if new_foreign_access != state.max_foreign_access {
-                Some((child, new_foreign_access))
-            } else {
-                Option::None
+        if only_foreign {
+            // This is definitely not a local access; clamp the result accordingly.
+            match relatedness {
+                Some(LocalAccess) => None,
+                Some(ForeignAccess) => Some(ForeignAccess),
+                Some(EitherAccess) => Some(ForeignAccess),
+                None => None,
             }
-        }));
+        } else {
+            relatedness
+        }
     }
-
     /// Update the tracking information of a tree, to reflect that the node specified by `id` is
-    /// now exposed with `new_exposed_as`.
+    /// now exposed with `new_exposed_as` permission.
     ///
     /// Propagates the Willard access information over the tree. This needs to be called every
     /// time the access level of an exposed node changes, to keep the state in sync with
     /// the rest of the tree.
+    ///
+    /// * `from`: The previous access level of the exposed node.
+    ///   Set to `None` if the node was not exposed before.
+    /// * `to`: The new access level.
     pub fn update_exposure(
-        id: UniIndex,
-        new_exposed_as: WildcardAccessLevel,
+        &mut self,
         nodes: &UniValMap,
-        wildcard_accesses: &mut UniValMap,
+        id: UniIndex,
+        from: WildcardAccessLevel,
+        to: WildcardAccessLevel,
     ) {
-        let mut entry = wildcard_accesses.entry(id);
-        let src_state = entry.or_insert(Default::default());
-        let old_exposed_as = src_state.exposed_as;
-
         // If the exposure doesn't change, then we don't need to update anything.
-        if old_exposed_as == new_exposed_as {
+        if from == to {
             return;
         }
 
-        let src_old_local_access = src_state.max_local_access();
-
-        src_state.exposed_as = new_exposed_as;
-
-        let src_new_local_access = src_state.max_local_access();
-
-        // Stack of nodes for which the max_foreign_access field needs to be updated.
-        // Will be filled with the children of this node and its parents children before
-        // we begin downwards traversal.
-        let mut stack: Vec<(UniIndex, WildcardAccessLevel)> = Vec::new();
-
-        // Add the direct children of this node to the stack.
-        {
+        // Update the counts of this node and all its ancestors.
+        let mut next_id = Some(id);
+        while let Some(id) = next_id {
             let node = nodes.get(id).unwrap();
-            Self::push_relevant_children(
-                &mut stack,
-                // new_foreign_access
-                max(src_state.max_foreign_access, new_exposed_as),
-                // old_foreign_access
-                max(src_state.max_foreign_access, old_exposed_as),
-                // Consider all children.
-                src_state.child_reads,
-                src_state.child_writes,
-                node.children.iter().copied(),
-                wildcard_accesses,
-            );
-        }
-        // We need to propagate the tracking info up the tree, for this we traverse
-        // up the parents.
-        // We can skip propagating info to the parent and siblings of a node if its
-        // access didn't change.
-        {
-            // The child from which we came.
-            let mut child = id;
-            // This is the `max_local_access()` of the child we came from, before
-            // this update...
-            let mut old_child_access = src_old_local_access;
-            // and after this update.
-            let mut new_child_access = src_new_local_access;
-            while let Some(parent_id) = nodes.get(child).unwrap().parent {
-                let parent_node = nodes.get(parent_id).unwrap();
-                let mut entry = wildcard_accesses.entry(parent_id);
-                let parent_state = entry.or_insert(Default::default());
-
-                let old_parent_local_access = parent_state.max_local_access();
-                use WildcardAccessLevel::*;
-                // Updating this node's tracking state for its children.
-                match (old_child_access, new_child_access) {
-                    (None | Read, Write) => parent_state.child_writes += 1,
-                    (Write, None | Read) => parent_state.child_writes -= 1,
-                    _ => {}
-                }
-                match (old_child_access, new_child_access) {
-                    (None, Read | Write) => parent_state.child_reads += 1,
-                    (Read | Write, None) => parent_state.child_reads -= 1,
-                    _ => {}
-                }
-
-                let new_parent_local_access = parent_state.max_local_access();
-
-                {
-                    // We need to update the `max_foreign_access` of `child`'s
-                    // siblings. For this we can reuse the `push_relevant_children`
-                    // function.
-                    //
-                    // We pass it just the siblings without child itself. Since
-                    // `child`'s `max_local_access()` is foreign to all of its
-                    // siblings we can pass it as part of the foreign access.
-
-                    let parent_access =
-                        max(parent_state.exposed_as, parent_state.max_foreign_access);
-                    // This is how many of `child`'s siblings have read/write local access.
-                    // If `child` itself has access, then we need to subtract its access from the count.
-                    let sibling_reads =
-                        parent_state.child_reads - if new_child_access >= Read { 1 } else { 0 };
-                    let sibling_writes =
-                        parent_state.child_writes - if new_child_access >= Write { 1 } else { 0 };
-                    Self::push_relevant_children(
-                        &mut stack,
-                        // new_foreign_access
-                        max(parent_access, new_child_access),
-                        // old_foreign_access
-                        max(parent_access, old_child_access),
-                        // Consider only siblings of child.
-                        sibling_reads,
-                        sibling_writes,
-                        parent_node.children.iter().copied().filter(|id| child != *id),
-                        wildcard_accesses,
-                    );
-                }
-                if old_parent_local_access == new_parent_local_access {
-                    // We didn't change `max_local_access()` for parent, so we don't need to propagate further upwards.
-                    break;
-                }
-
-                old_child_access = old_parent_local_access;
-                new_child_access = new_parent_local_access;
-                child = parent_id;
+            let mut state = self.0.entry(id);
+            let state = state.or_insert(Default::default());
+
+            use WildcardAccessLevel::*;
+            match (from, to) {
+                (None | Read, Write) => state.local_writes += 1,
+                (Write, None | Read) => state.local_writes -= 1,
+                _ => {}
             }
-        }
-        // Traverses down the tree to update max_foreign_access fields of children and cousins who need to be updated.
-        while let Some((id, new_access)) = stack.pop() {
-            let node = nodes.get(id).unwrap();
-            let mut entry = wildcard_accesses.entry(id);
-            let state = entry.or_insert(Default::default());
-
-            let old_access = state.max_foreign_access;
-            state.max_foreign_access = new_access;
-
-            Self::push_relevant_children(
-                &mut stack,
-                // new_foreign_access
-                max(state.exposed_as, new_access),
-                // old_foreign_access
-                max(state.exposed_as, old_access),
-                // Consider all children.
-                state.child_reads,
-                state.child_writes,
-                node.children.iter().copied(),
-                wildcard_accesses,
-            );
+            match (from, to) {
+                (None, Read | Write) => state.local_reads += 1,
+                (Read | Write, None) => state.local_reads -= 1,
+                _ => {}
+            }
+            next_id = node.parent;
         }
     }
+    /// Removes a node from the datastructure.
+    ///
+    /// The caller needs to ensure that the node does not have any children.
+    pub fn remove(&mut self, idx: UniIndex) {
+        self.0.remove(idx);
+    }
 }
 
 impl Tree {
@@ -428,25 +195,28 @@ impl Tree {
     pub fn expose_tag(&mut self, tag: BorTag, protected: bool) {
         let id = self.tag_mapping.get(&tag).unwrap();
         let node = self.nodes.get_mut(id).unwrap();
-        node.is_exposed = true;
-        let node = self.nodes.get(id).unwrap();
-
-        // When the first tag gets exposed then we initialize the
-        // wildcard state for every node and location in the tree.
-        for (_, loc) in self.locations.iter_mut_all() {
-            let perm = loc
-                .perms
-                .get(id)
-                .map(|p| p.permission())
-                .unwrap_or_else(|| node.default_location_state().permission());
-
-            let access_type = perm.strongest_allowed_local_access(protected);
-            WildcardState::update_exposure(
-                id,
-                access_type,
-                &self.nodes,
-                &mut loc.wildcard_accesses,
-            );
+        if !node.is_exposed {
+            node.is_exposed = true;
+            let node = self.nodes.get(id).unwrap();
+
+            for (_, loc) in self.locations.iter_mut_all() {
+                let perm = loc
+                    .perms
+                    .get(id)
+                    .map(|p| p.permission())
+                    .unwrap_or_else(|| node.default_location_state().permission());
+
+                let access_level = perm.strongest_allowed_local_access(protected);
+                // An unexposed node gets treated as access level `None`. Therefore,
+                // the initial exposure transitions from `None` to the node's actual
+                // `access_level`.
+                loc.exposed_cache.update_exposure(
+                    &self.nodes,
+                    id,
+                    WildcardAccessLevel::None,
+                    access_level,
+                );
+            }
         }
     }
 
@@ -457,10 +227,19 @@ impl Tree {
 
         // We check if the node is already exposed, as we don't want to expose any
         // nodes which aren't already exposed.
-
-        if self.nodes.get(idx).unwrap().is_exposed {
-            // Updates the exposure to the new permission on every location.
-            self.expose_tag(tag, /* protected */ false);
+        let node = self.nodes.get(idx).unwrap();
+        if node.is_exposed {
+            for (_, loc) in self.locations.iter_mut_all() {
+                let perm = loc
+                    .perms
+                    .get(idx)
+                    .map(|p| p.permission())
+                    .unwrap_or_else(|| node.default_location_state().permission());
+                // We are transitioning from protected to unprotected.
+                let old_access_type = perm.strongest_allowed_local_access(/*protected*/ true);
+                let access_type = perm.strongest_allowed_local_access(/*protected*/ false);
+                loc.exposed_cache.update_exposure(&self.nodes, idx, old_access_type, access_type);
+            }
         }
     }
 }
@@ -472,20 +251,15 @@ impl Tree {
     pub fn verify_wildcard_consistency(&self, global: &GlobalState) {
         // We rely on the fact that `roots` is ordered according to tag from low to high.
         assert!(self.roots.is_sorted_by_key(|idx| self.nodes.get(*idx).unwrap().tag));
-        let main_root_idx = self.roots[0];
 
         let protected_tags = &global.borrow().protected_tags;
         for (_, loc) in self.locations.iter_all() {
-            let wildcard_accesses = &loc.wildcard_accesses;
+            let exposed_cache = &loc.exposed_cache;
             let perms = &loc.perms;
-            // Checks if accesses is empty.
-            if wildcard_accesses.is_empty() {
-                return;
-            }
             for (id, node) in self.nodes.iter() {
-                let state = wildcard_accesses.get(id).unwrap();
+                let state = exposed_cache.0.get(id).cloned().unwrap_or_default();
 
-                let expected_exposed_as = if node.is_exposed {
+                let exposed_as = if node.is_exposed {
                     let perm =
                         perms.get(id).copied().unwrap_or_else(|| node.default_location_state());
 
@@ -495,72 +269,25 @@ impl Tree {
                     WildcardAccessLevel::None
                 };
 
-                // The foreign wildcard accesses possible at a node are determined by which
-                // accesses can originate from their siblings, their parent, and from above
-                // their parent.
-                let expected_max_foreign_access = if let Some(parent) = node.parent {
-                    let parent_node = self.nodes.get(parent).unwrap();
-                    let parent_state = wildcard_accesses.get(parent).unwrap();
-
-                    let max_sibling_access = parent_node
-                        .children
-                        .iter()
-                        .copied()
-                        .filter(|child| *child != id)
-                        .map(|child| {
-                            let state = wildcard_accesses.get(child).unwrap();
-                            state.max_local_access()
-                        })
-                        .fold(WildcardAccessLevel::None, max);
-
-                    max_sibling_access
-                        .max(parent_state.max_foreign_access)
-                        .max(parent_state.exposed_as)
-                } else {
-                    if main_root_idx == id {
-                        // There can never be a foreign access to the root of the allocation.
-                        // So its foreign access level is always `None`.
-                        WildcardAccessLevel::None
-                    } else {
-                        // For wildcard roots any access on a different subtree can be foreign
-                        // to it. So a wildcard root has the maximum possible foreign access
-                        // level.
-                        WildcardAccessLevel::Write
-                    }
-                };
-
-                // Count how many children can be the source of wildcard reads or writes
-                // (either directly, or via their children).
-                let child_accesses = node.children.iter().copied().map(|child| {
-                    let state = wildcard_accesses.get(child).unwrap();
-                    state.max_local_access()
-                });
-                let expected_child_reads =
-                    child_accesses.clone().filter(|a| *a >= WildcardAccessLevel::Read).count();
-                let expected_child_writes =
-                    child_accesses.filter(|a| *a >= WildcardAccessLevel::Write).count();
-
-                assert_eq!(
-                    expected_exposed_as, state.exposed_as,
-                    "tag {:?} (id:{id:?}) should be exposed as {expected_exposed_as:?} but is exposed as {:?}",
-                    node.tag, state.exposed_as
-                );
-                assert_eq!(
-                    expected_max_foreign_access, state.max_foreign_access,
-                    "expected {:?}'s (id:{id:?}) max_foreign_access to be {:?} instead of {:?}",
-                    node.tag, expected_max_foreign_access, state.max_foreign_access
-                );
-                let child_reads: usize = state.child_reads.into();
+                let (child_reads, child_writes) = node
+                    .children
+                    .iter()
+                    .copied()
+                    .map(|id| exposed_cache.0.get(id).cloned().unwrap_or_default())
+                    .fold((0, 0), |acc, wc| (acc.0 + wc.local_reads, acc.1 + wc.local_writes));
+                let expected_reads =
+                    child_reads + u16::from(exposed_as >= WildcardAccessLevel::Read);
+                let expected_writes =
+                    child_writes + u16::from(exposed_as >= WildcardAccessLevel::Write);
                 assert_eq!(
-                    expected_child_reads, child_reads,
-                    "expected {:?}'s (id:{id:?}) child_reads to be {} instead of {}",
-                    node.tag, expected_child_reads, child_reads
+                    state.local_reads, expected_reads,
+                    "expected {:?}'s (id:{id:?}) local_reads to be {expected_reads:?} instead of {:?} (child_reads: {child_reads:?}, exposed_as: {exposed_as:?})",
+                    node.tag, state.local_reads
                 );
-                let child_writes: usize = state.child_writes.into();
                 assert_eq!(
-                    expected_child_writes, child_writes,
-                    "expected {:?}'s (id:{id:?}) child_writes to be {} instead of {}",
-                    node.tag, expected_child_writes, child_writes
+                    state.local_writes, expected_writes,
+                    "expected {:?}'s (id:{id:?}) local_writes to be {expected_writes:?} instead of {:?} (child_writes: {child_writes:?}, exposed_as: {exposed_as:?})",
+                    node.tag, state.local_writes
                 );
             }
         }

From 921667859d16c4aa66cadbcfa1320a8b26ffecf7 Mon Sep 17 00:00:00 2001
From: hsqStephenZhang 
Date: Wed, 4 Feb 2026 12:58:48 +0100
Subject: [PATCH 078/103] feat: vtbl1_u8 intrinsic on aarch64

needed for chacha20
---
 src/tools/miri/src/shims/aarch64.rs           | 32 ++++++++++++++++++-
 .../shims/aarch64/intrinsics-aarch64-neon.rs  | 26 +++++++++++++++
 2 files changed, 57 insertions(+), 1 deletion(-)

diff --git a/src/tools/miri/src/shims/aarch64.rs b/src/tools/miri/src/shims/aarch64.rs
index 595a6595b531d..2aca71819e5e9 100644
--- a/src/tools/miri/src/shims/aarch64.rs
+++ b/src/tools/miri/src/shims/aarch64.rs
@@ -1,4 +1,4 @@
-use rustc_abi::CanonAbi;
+use rustc_abi::{CanonAbi, Size};
 use rustc_middle::mir::BinOp;
 use rustc_middle::ty::Ty;
 use rustc_span::Symbol;
@@ -58,7 +58,37 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
                     this.write_immediate(*res_lane, &dest)?;
                 }
             }
+            // Vector table lookup: each index selects a byte from the 16-byte table, out-of-range -> 0.
+            // Used to implement vtbl1_u8 function.
+            // LLVM does not have a portable shuffle that takes non-const indices.
+            // So we need to implement this ourselves
+            // https://developer.arm.com/architectures/instruction-sets/intrinsics/vtbl1_u8
+            "neon.tbl1.v16i8" => {
+                let [table, indices] =
+                    this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
+
+                let (table, table_len) = this.project_to_simd(table)?;
+                let (indices, idx_len) = this.project_to_simd(indices)?;
+                let (dest, dest_len) = this.project_to_simd(dest)?;
+                assert_eq!(table_len, 16);
+                assert_eq!(idx_len, dest_len);
 
+                let table_len = u128::from(table_len);
+                let elem_size = Size::from_bytes(1);
+                for i in 0..dest_len {
+                    let idx = this.read_immediate(&this.project_index(&indices, i)?)?;
+                    let idx_u = idx.to_scalar().to_uint(elem_size)?;
+                    let val = if idx_u < table_len {
+                        let t = this.read_immediate(
+                            &this.project_index(&table, idx_u.try_into().unwrap())?,
+                        )?;
+                        t.to_scalar()
+                    } else {
+                        Scalar::from_u8(0)
+                    };
+                    this.write_scalar(val, &this.project_index(&dest, i)?)?;
+                }
+            }
             _ => return interp_ok(EmulateItemResult::NotSupported),
         }
         interp_ok(EmulateItemResult::NeedsReturn)
diff --git a/src/tools/miri/tests/pass/shims/aarch64/intrinsics-aarch64-neon.rs b/src/tools/miri/tests/pass/shims/aarch64/intrinsics-aarch64-neon.rs
index 84485dbad8c9e..b1132fcaabcdb 100644
--- a/src/tools/miri/tests/pass/shims/aarch64/intrinsics-aarch64-neon.rs
+++ b/src/tools/miri/tests/pass/shims/aarch64/intrinsics-aarch64-neon.rs
@@ -4,12 +4,14 @@
 
 use std::arch::aarch64::*;
 use std::arch::is_aarch64_feature_detected;
+use std::mem::transmute;
 
 fn main() {
     assert!(is_aarch64_feature_detected!("neon"));
 
     unsafe {
         test_neon();
+        tbl1_v16i8_basic();
     }
 }
 
@@ -38,3 +40,27 @@ unsafe fn test_neon() {
     }
     test_vpmaxq_u8_is_unsigned();
 }
+
+#[target_feature(enable = "neon")]
+fn tbl1_v16i8_basic() {
+    unsafe {
+        // table = 0..15
+        let table: uint8x16_t =
+            transmute::<[u8; 16], _>([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]);
+        // indices: include in-range, 15 (last), 16 and 255 (out-of-range → 0)
+        let idx: uint8x16_t =
+            transmute::<[u8; 16], _>([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]);
+        let got = vqtbl1q_u8(table, idx);
+        let got_arr: [u8; 16] = transmute(got);
+        assert_eq!(got_arr, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]);
+
+        let idx2: uint8x16_t =
+            transmute::<[u8; 16], _>([15, 16, 255, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]);
+        let got2 = vqtbl1q_u8(table, idx2);
+        let got2_arr: [u8; 16] = transmute(got2);
+        assert_eq!(got2_arr[0], 15);
+        assert_eq!(got2_arr[1], 0); // out-of-range
+        assert_eq!(got2_arr[2], 0); // out-of-range
+        assert_eq!(&got2_arr[3..16], &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12][..]);
+    }
+}

From 59329154b71669ede57c579e23251962ca7df771 Mon Sep 17 00:00:00 2001
From: Ralf Jung 
Date: Sat, 14 Feb 2026 13:33:03 +0100
Subject: [PATCH 079/103] minor tweaks

---
 src/tools/miri/src/shims/aarch64.rs              | 16 ++++++----------
 .../shims/aarch64/intrinsics-aarch64-neon.rs     | 11 ++++++-----
 2 files changed, 12 insertions(+), 15 deletions(-)

diff --git a/src/tools/miri/src/shims/aarch64.rs b/src/tools/miri/src/shims/aarch64.rs
index 2aca71819e5e9..d06b02a41334f 100644
--- a/src/tools/miri/src/shims/aarch64.rs
+++ b/src/tools/miri/src/shims/aarch64.rs
@@ -1,4 +1,4 @@
-use rustc_abi::{CanonAbi, Size};
+use rustc_abi::CanonAbi;
 use rustc_middle::mir::BinOp;
 use rustc_middle::ty::Ty;
 use rustc_span::Symbol;
@@ -60,8 +60,8 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
             }
             // Vector table lookup: each index selects a byte from the 16-byte table, out-of-range -> 0.
             // Used to implement vtbl1_u8 function.
-            // LLVM does not have a portable shuffle that takes non-const indices.
-            // So we need to implement this ourselves
+            // LLVM does not have a portable shuffle that takes non-const indices
+            // so we need to implement this ourselves.
             // https://developer.arm.com/architectures/instruction-sets/intrinsics/vtbl1_u8
             "neon.tbl1.v16i8" => {
                 let [table, indices] =
@@ -73,15 +73,11 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
                 assert_eq!(table_len, 16);
                 assert_eq!(idx_len, dest_len);
 
-                let table_len = u128::from(table_len);
-                let elem_size = Size::from_bytes(1);
                 for i in 0..dest_len {
                     let idx = this.read_immediate(&this.project_index(&indices, i)?)?;
-                    let idx_u = idx.to_scalar().to_uint(elem_size)?;
-                    let val = if idx_u < table_len {
-                        let t = this.read_immediate(
-                            &this.project_index(&table, idx_u.try_into().unwrap())?,
-                        )?;
+                    let idx_u = idx.to_scalar().to_u8()?;
+                    let val = if u64::from(idx_u) < table_len {
+                        let t = this.read_immediate(&this.project_index(&table, idx_u.into())?)?;
                         t.to_scalar()
                     } else {
                         Scalar::from_u8(0)
diff --git a/src/tools/miri/tests/pass/shims/aarch64/intrinsics-aarch64-neon.rs b/src/tools/miri/tests/pass/shims/aarch64/intrinsics-aarch64-neon.rs
index b1132fcaabcdb..6d3f153e194f3 100644
--- a/src/tools/miri/tests/pass/shims/aarch64/intrinsics-aarch64-neon.rs
+++ b/src/tools/miri/tests/pass/shims/aarch64/intrinsics-aarch64-neon.rs
@@ -10,13 +10,13 @@ fn main() {
     assert!(is_aarch64_feature_detected!("neon"));
 
     unsafe {
-        test_neon();
-        tbl1_v16i8_basic();
+        test_vpmaxq_u8();
+        test_tbl1_v16i8_basic();
     }
 }
 
 #[target_feature(enable = "neon")]
-unsafe fn test_neon() {
+unsafe fn test_vpmaxq_u8() {
     // Adapted from library/stdarch/crates/core_arch/src/aarch64/neon/mod.rs
     unsafe fn test_vpmaxq_u8() {
         let a = vld1q_u8([1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 6, 7, 8].as_ptr());
@@ -42,18 +42,19 @@ unsafe fn test_neon() {
 }
 
 #[target_feature(enable = "neon")]
-fn tbl1_v16i8_basic() {
+fn test_tbl1_v16i8_basic() {
     unsafe {
         // table = 0..15
         let table: uint8x16_t =
             transmute::<[u8; 16], _>([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]);
-        // indices: include in-range, 15 (last), 16 and 255 (out-of-range → 0)
+        // indices
         let idx: uint8x16_t =
             transmute::<[u8; 16], _>([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]);
         let got = vqtbl1q_u8(table, idx);
         let got_arr: [u8; 16] = transmute(got);
         assert_eq!(got_arr, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]);
 
+        // Also try different order and out-of-range indices (16, 255).
         let idx2: uint8x16_t =
             transmute::<[u8; 16], _>([15, 16, 255, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]);
         let got2 = vqtbl1q_u8(table, idx2);

From d15d7149b17e8b399cf4030580649cc18b59ed6a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Alexander=20Kj=C3=A4ll?= 
Date: Sat, 14 Feb 2026 21:02:46 +0100
Subject: [PATCH 080/103] fix smol_str compilation error the command 'cargo
 build --no-default-features --features borsh' failed with: error[E0599]: no
 function or associated item named 'other' found for struct 'borsh::io::Error'
 in the current scope    --> lib/smol_str/src/borsh.rs:33:39     |  33 |      
           .ok_or_else(|| Error::other("u8::vec_from_reader unexpectedly
 returned None"))?;     |                                       ^^^^^ function
 or associated item not found in 'borsh::io::Error'     |

---
 src/tools/rust-analyzer/lib/smol_str/src/borsh.rs | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/src/tools/rust-analyzer/lib/smol_str/src/borsh.rs b/src/tools/rust-analyzer/lib/smol_str/src/borsh.rs
index 527ce85a1746a..b684a4910c963 100644
--- a/src/tools/rust-analyzer/lib/smol_str/src/borsh.rs
+++ b/src/tools/rust-analyzer/lib/smol_str/src/borsh.rs
@@ -29,8 +29,9 @@ impl BorshDeserialize for SmolStr {
             }))
         } else {
             // u8::vec_from_reader always returns Some on success in current implementation
-            let vec = u8::vec_from_reader(len, reader)?
-                .ok_or_else(|| Error::other("u8::vec_from_reader unexpectedly returned None"))?;
+            let vec = u8::vec_from_reader(len, reader)?.ok_or_else(|| {
+                Error::new(ErrorKind::Other, "u8::vec_from_reader unexpectedly returned None")
+            })?;
             Ok(SmolStr::from(String::from_utf8(vec).map_err(|err| {
                 let msg = err.to_string();
                 Error::new(ErrorKind::InvalidData, msg)

From 646418838fc494822f4814e6fd58a59f08d3575a Mon Sep 17 00:00:00 2001
From: Albab-Hasan 
Date: Sat, 14 Feb 2026 22:19:26 +0600
Subject: [PATCH 081/103] fix: handle `ref mut` bindings in
 `contains_explicit_ref_binding`

the standalone `contains_explicit_ref_binding` function only checked for
`BindingAnnotation::Ref`, missing `BindingAnnotation::RefMut`. this caused
`let ref mut x = expr` to incorrectly take the coercion path instead of
preserving the exact type of the rhs expression. the method version used
for match arms already handles both `Ref` and `RefMut` correctly.
---
 .../crates/hir-ty/src/infer/pat.rs            |  2 +-
 .../crates/hir-ty/src/tests/never_type.rs     | 26 +++++++++++++++++++
 2 files changed, 27 insertions(+), 1 deletion(-)

diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs
index 1b8ce5ceaf860..87fd0dace38f9 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs
@@ -673,7 +673,7 @@ impl<'db> InferenceContext<'_, 'db> {
 pub(super) fn contains_explicit_ref_binding(body: &Body, pat_id: PatId) -> bool {
     let mut res = false;
     body.walk_pats(pat_id, &mut |pat| {
-        res |= matches!(body[pat], Pat::Bind { id, .. } if body[id].mode == BindingAnnotation::Ref);
+        res |= matches!(body[pat], Pat::Bind { id, .. } if matches!(body[id].mode, BindingAnnotation::Ref | BindingAnnotation::RefMut));
     });
     res
 }
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/never_type.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/never_type.rs
index 4d68179a88b82..a89d97984dfe3 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/never_type.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/never_type.rs
@@ -760,6 +760,32 @@ fn coerce_ref_binding() -> ! {
     )
 }
 
+#[test]
+fn diverging_place_match_ref_mut() {
+    check_infer_with_mismatches(
+        r#"
+//- minicore: sized
+fn coerce_ref_mut_binding() -> ! {
+    unsafe {
+        let x: *mut ! = 0 as _;
+        let ref mut _x: () = *x;
+    }
+}
+"#,
+        expect![[r#"
+            33..120 '{     ...   } }': !
+            39..118 'unsafe...     }': !
+            60..61 'x': *mut !
+            72..73 '0': i32
+            72..78 '0 as _': *mut !
+            92..102 'ref mut _x': &'? mut ()
+            109..111 '*x': !
+            110..111 'x': *mut !
+            109..111: expected (), got !
+        "#]],
+    )
+}
+
 #[test]
 fn never_place_isnt_diverging() {
     check_infer_with_mismatches(

From 1c21e6096440b6b5c273ff8a1755e9d534389701 Mon Sep 17 00:00:00 2001
From: Ben Kimock 
Date: Sat, 14 Feb 2026 17:54:44 -0500
Subject: [PATCH 082/103] made -> marked

---
 src/tools/miri/src/concurrency/weak_memory.rs | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/tools/miri/src/concurrency/weak_memory.rs b/src/tools/miri/src/concurrency/weak_memory.rs
index 2869152bc39d7..3aded9a1454a7 100644
--- a/src/tools/miri/src/concurrency/weak_memory.rs
+++ b/src/tools/miri/src/concurrency/weak_memory.rs
@@ -389,7 +389,7 @@ impl<'tcx> StoreBuffer {
             })
             .filter(|&store_elem| {
                 if is_seqcst && store_elem.is_seqcst {
-                    // An SC load needs to ignore all but last store made SC (stores not marked SC are not
+                    // An SC load needs to ignore all but last store marked SC (stores not marked SC are not
                     // affected)
                     let include = !found_sc;
                     found_sc = true;

From d888b1c1082217d7b084aa02cab2c59783edffd1 Mon Sep 17 00:00:00 2001
From: The Miri Cronjob Bot 
Date: Sun, 15 Feb 2026 05:18:09 +0000
Subject: [PATCH 083/103] Prepare for merging from rust-lang/rust

This updates the rust-version file to 7bee525095c0872e87c038c412c781b9bbb3f5dc.
---
 src/tools/miri/rust-version | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/tools/miri/rust-version b/src/tools/miri/rust-version
index a79511cea30a6..28c3e88535f61 100644
--- a/src/tools/miri/rust-version
+++ b/src/tools/miri/rust-version
@@ -1 +1 @@
-47611e16044c68ef27bac31c35fda2ba1dc20b73
+7bee525095c0872e87c038c412c781b9bbb3f5dc

From 8d3b9f4a81f7763e7901287a477bc4e17e16f30b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Alexander=20Kj=C3=A4ll?= 
Date: Sun, 15 Feb 2026 10:28:47 +0100
Subject: [PATCH 084/103] gate borsh tests on std also, as they depend on both

---
 src/tools/rust-analyzer/lib/smol_str/tests/test.rs | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/tools/rust-analyzer/lib/smol_str/tests/test.rs b/src/tools/rust-analyzer/lib/smol_str/tests/test.rs
index 640e7df681c9d..00fab2ee1c7fd 100644
--- a/src/tools/rust-analyzer/lib/smol_str/tests/test.rs
+++ b/src/tools/rust-analyzer/lib/smol_str/tests/test.rs
@@ -393,7 +393,7 @@ mod test_str_ext {
     }
 }
 
-#[cfg(feature = "borsh")]
+#[cfg(all(feature = "borsh", feature = "std"))]
 mod borsh_tests {
     use borsh::BorshDeserialize;
     use smol_str::{SmolStr, ToSmolStr};

From 7733f65ff9fbd5b033e5ad012250f0fc4bee44ad Mon Sep 17 00:00:00 2001
From: Albab-Hasan 
Date: Sun, 15 Feb 2026 17:37:51 +0600
Subject: [PATCH 085/103] fix: use `ExprIsRead::Yes` for rhs of ordinary
 assignments

the rhs of an ordinary assignment `x = *never_ptr` was inferred with
`ExprIsRead::No`, which prevented `NeverToAny` coercion for place
expressions of type `!`. this caused false type mismatches and missing
divergence detection. the destructuring assignment path and let binding
path both correctly use `ExprIsRead::Yes` for the rhs value, since the
value is always consumed (read). this makes the ordinary assignment path
consistent with both.
---
 .../crates/hir-ty/src/infer/expr.rs              |  2 +-
 .../crates/hir-ty/src/tests/never_type.rs        | 16 ++++++++++++++++
 2 files changed, 17 insertions(+), 1 deletion(-)

diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs
index 9f2d9d25b9572..16e7d51e8734f 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs
@@ -751,7 +751,7 @@ impl<'db> InferenceContext<'_, 'db> {
 
                 if let Some(lhs_ty) = lhs_ty {
                     self.write_pat_ty(target, lhs_ty);
-                    self.infer_expr_coerce(value, &Expectation::has_type(lhs_ty), ExprIsRead::No);
+                    self.infer_expr_coerce(value, &Expectation::has_type(lhs_ty), ExprIsRead::Yes);
                 } else {
                     let rhs_ty = self.infer_expr(value, &Expectation::none(), ExprIsRead::Yes);
                     let resolver_guard =
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/never_type.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/never_type.rs
index a89d97984dfe3..e4e7b4cc38859 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/never_type.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/never_type.rs
@@ -786,6 +786,22 @@ fn coerce_ref_mut_binding() -> ! {
     )
 }
 
+#[test]
+fn assign_never_place_no_mismatch() {
+    check_no_mismatches(
+        r#"
+//- minicore: sized
+fn foo() {
+    unsafe {
+        let p: *mut ! = 0 as _;
+        let mut x: () = ();
+        x = *p;
+    }
+}
+"#,
+    );
+}
+
 #[test]
 fn never_place_isnt_diverging() {
     check_infer_with_mismatches(

From b55673bd76a1054c91988376bfd303c546b9b6d7 Mon Sep 17 00:00:00 2001
From: THARUN 
Date: Sun, 15 Feb 2026 19:02:44 +0530
Subject: [PATCH 086/103] Avoid ICE in From/TryFrom diagnostic under
 -Znext-solver

---
 .../traits/fulfillment_errors.rs              | 34 ++++++++++++-------
 tests/ui/traits/next-solver-ice.rs            |  9 +++++
 tests/ui/traits/next-solver-ice.stderr        | 21 ++++++++++++
 3 files changed, 51 insertions(+), 13 deletions(-)
 create mode 100644 tests/ui/traits/next-solver-ice.rs
 create mode 100644 tests/ui/traits/next-solver-ice.stderr

diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs
index d9c6d339328aa..d0dabd9824764 100644
--- a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs
+++ b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs
@@ -282,23 +282,31 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
                         if self.tcx.is_diagnostic_item(sym::From, trait_def_id)
                             || self.tcx.is_diagnostic_item(sym::TryFrom, trait_def_id)
                         {
-                            let found_ty = leaf_trait_predicate.skip_binder().trait_ref.args.type_at(1);
-                            let ty = main_trait_predicate.skip_binder().self_ty();
-                            if let Some(cast_ty) = self.find_explicit_cast_type(
-                                obligation.param_env,
-                                found_ty,
-                                ty,
-                            ) {
-                                let found_ty_str = self.tcx.short_string(found_ty, &mut long_ty_file);
-                                let cast_ty_str = self.tcx.short_string(cast_ty, &mut long_ty_file);
-                                err.help(
-                                    format!(
+                            let trait_ref = leaf_trait_predicate.skip_binder().trait_ref;
+
+                            // Defensive: next-solver may produce fewer args than expected.
+                            if trait_ref.args.len() > 1 {
+                                let found_ty = trait_ref.args.type_at(1);
+                                let ty = main_trait_predicate.skip_binder().self_ty();
+
+                                if let Some(cast_ty) = self.find_explicit_cast_type(
+                                    obligation.param_env,
+                                    found_ty,
+                                    ty,
+                                ) {
+                                    let found_ty_str =
+                                        self.tcx.short_string(found_ty, &mut long_ty_file);
+                                    let cast_ty_str =
+                                        self.tcx.short_string(cast_ty, &mut long_ty_file);
+
+                                    err.help(format!(
                                         "consider casting the `{found_ty_str}` value to `{cast_ty_str}`",
-                                    ),
-                                );
+                                    ));
+                                }
                             }
                         }
 
+
                         *err.long_ty_path() = long_ty_file;
 
                         let mut suggested = false;
diff --git a/tests/ui/traits/next-solver-ice.rs b/tests/ui/traits/next-solver-ice.rs
new file mode 100644
index 0000000000000..889d1094a1030
--- /dev/null
+++ b/tests/ui/traits/next-solver-ice.rs
@@ -0,0 +1,9 @@
+//@compile-flags: -Znext-solver=globally
+//@check-fail
+
+fn check() {
+    ::Item>>::from;
+    //~^ ERROR the trait bound `f32: From<::Item>` is not satisfied
+}
+
+fn main() {}
diff --git a/tests/ui/traits/next-solver-ice.stderr b/tests/ui/traits/next-solver-ice.stderr
new file mode 100644
index 0000000000000..d6b022d70175b
--- /dev/null
+++ b/tests/ui/traits/next-solver-ice.stderr
@@ -0,0 +1,21 @@
+error[E0277]: the trait bound `f32: From<::Item>` is not satisfied
+  --> $DIR/next-solver-ice.rs:5:6
+   |
+LL |     ::Item>>::from;
+   |      ^^^ the nightly-only, unstable trait `ZeroablePrimitive` is not implemented for `f32`
+   |
+   = help: the following other types implement trait `ZeroablePrimitive`:
+             i128
+             i16
+             i32
+             i64
+             i8
+             isize
+             u128
+             u16
+           and 4 others
+   = note: required for `f32` to implement `From<::Item>`
+
+error: aborting due to 1 previous error
+
+For more information about this error, try `rustc --explain E0277`.

From 40a3ca1189940ad1805c8deb3ba60744fa16dc57 Mon Sep 17 00:00:00 2001
From: Embers-of-the-Fire 
Date: Sun, 15 Feb 2026 22:01:01 +0800
Subject: [PATCH 087/103] fix: remove unused source span

---
 src/librustdoc/html/render/mod.rs | 46 ++++---------------------------
 src/librustdoc/html/sources.rs    | 12 ++++++--
 2 files changed, 15 insertions(+), 43 deletions(-)

diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs
index 57428b6f481e8..a3860e1ec2274 100644
--- a/src/librustdoc/html/render/mod.rs
+++ b/src/librustdoc/html/render/mod.rs
@@ -2779,46 +2779,12 @@ fn render_call_locations(
         let needs_expansion = line_max - line_min > NUM_VISIBLE_LINES;
         let locations_encoded = serde_json::to_string(&line_ranges).unwrap();
 
-        let source_map = tcx.sess.source_map();
-        let files = source_map.files();
-        let local = tcx.sess.local_crate_source_file().unwrap();
-
-        let get_file_start_pos = || {
-            let crate_src = local.clone().into_local_path()?;
-            let abs_crate_src = crate_src.canonicalize().ok()?;
-            let crate_root = abs_crate_src.parent()?.parent()?;
-            let rel_path = path.strip_prefix(crate_root).ok()?;
-            files
-                .iter()
-                .find(|file| match &file.name {
-                    FileName::Real(real) => real.local_path().map_or(false, |p| p == rel_path),
-                    _ => false,
-                })
-                .map(|file| file.start_pos)
-        };
-
-        // Look for the example file in the source map if it exists, otherwise
-        // return a span to the local crate's source file
-        let Some(file_span) = get_file_start_pos()
-            .or_else(|| {
-                files
-                    .iter()
-                    .find(|file| match &file.name {
-                        FileName::Real(file_name) => file_name == &local,
-                        _ => false,
-                    })
-                    .map(|file| file.start_pos)
-            })
-            .map(|start_pos| {
-                rustc_span::Span::with_root_ctxt(
-                    start_pos + BytePos(byte_min),
-                    start_pos + BytePos(byte_max),
-                )
-            })
-        else {
-            // if the fallback span can't be built, don't render the code for this example
-            return false;
-        };
+        // For scraped examples, we don't need a real span from the SourceMap.
+        // The URL is already provided in ScrapedInfo, and sources::print_src
+        // will use that directly. We use DUMMY_SP as a placeholder.
+        // Note: DUMMY_SP is safe here because href_from_span won't be called
+        // for scraped examples.
+        let file_span = rustc_span::DUMMY_SP;
 
         let mut decoration_info = FxIndexMap::default();
         decoration_info.insert("highlight focus", vec![byte_ranges.remove(0)]);
diff --git a/src/librustdoc/html/sources.rs b/src/librustdoc/html/sources.rs
index 89fd78979839b..dda9b7c55351c 100644
--- a/src/librustdoc/html/sources.rs
+++ b/src/librustdoc/html/sources.rs
@@ -344,9 +344,15 @@ pub(crate) fn print_src(
         lines += line_info.start_line as usize;
     }
     let code = fmt::from_fn(move |fmt| {
-        let current_href = context
-            .href_from_span(clean::Span::new(file_span), false)
-            .expect("only local crates should have sources emitted");
+        // For scraped examples, use the URL from ScrapedInfo directly.
+        // For regular sources, derive it from the span.
+        let current_href = if let SourceContext::Embedded(info) = source_context {
+            info.url.to_string()
+        } else {
+            context
+                .href_from_span(clean::Span::new(file_span), false)
+                .expect("only local crates should have sources emitted")
+        };
         highlight::write_code(
             fmt,
             s,

From a8bbacd2702cfbc260b52f9c19fc5c01c510a7bb Mon Sep 17 00:00:00 2001
From: Embers-of-the-Fire 
Date: Sun, 15 Feb 2026 22:26:54 +0800
Subject: [PATCH 088/103] fix: remove unused import

---
 src/librustdoc/html/render/mod.rs | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs
index a3860e1ec2274..53df362a34f7b 100644
--- a/src/librustdoc/html/render/mod.rs
+++ b/src/librustdoc/html/render/mod.rs
@@ -60,7 +60,7 @@ use rustc_hir::{ConstStability, Mutability, RustcVersion, StabilityLevel, Stable
 use rustc_middle::ty::print::PrintTraitRefExt;
 use rustc_middle::ty::{self, TyCtxt};
 use rustc_span::symbol::{Symbol, sym};
-use rustc_span::{BytePos, DUMMY_SP, FileName};
+use rustc_span::DUMMY_SP;
 use tracing::{debug, info};
 
 pub(crate) use self::context::*;

From d13828b2c7cedb6147999222118cd87bf4fe28cd Mon Sep 17 00:00:00 2001
From: Embers-of-the-Fire 
Date: Sun, 15 Feb 2026 22:34:53 +0800
Subject: [PATCH 089/103] fix: re-format the changes

---
 src/librustdoc/html/render/mod.rs | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs
index 53df362a34f7b..c0f0167aec799 100644
--- a/src/librustdoc/html/render/mod.rs
+++ b/src/librustdoc/html/render/mod.rs
@@ -59,8 +59,8 @@ use rustc_hir::def_id::{DefId, DefIdSet};
 use rustc_hir::{ConstStability, Mutability, RustcVersion, StabilityLevel, StableSince};
 use rustc_middle::ty::print::PrintTraitRefExt;
 use rustc_middle::ty::{self, TyCtxt};
-use rustc_span::symbol::{Symbol, sym};
 use rustc_span::DUMMY_SP;
+use rustc_span::symbol::{Symbol, sym};
 use tracing::{debug, info};
 
 pub(crate) use self::context::*;

From 702793191fd51ec36803f620553fee675ed93157 Mon Sep 17 00:00:00 2001
From: The Miri Cronjob Bot 
Date: Sun, 15 Feb 2026 05:27:15 +0000
Subject: [PATCH 090/103] fmt

---
 .../miri/tests/fail/match/all_variants_uninhabited.rs      | 2 +-
 .../miri/tests/fail/match/closures/uninhabited-variant2.rs | 3 ++-
 src/tools/miri/tests/fail/match/only_inhabited_variant.rs  | 7 ++++---
 src/tools/miri/tests/fail/match/single_variant.rs          | 7 ++++---
 src/tools/miri/tests/fail/match/single_variant_uninit.rs   | 3 ++-
 5 files changed, 13 insertions(+), 9 deletions(-)

diff --git a/src/tools/miri/tests/fail/match/all_variants_uninhabited.rs b/src/tools/miri/tests/fail/match/all_variants_uninhabited.rs
index e7ca45579d84d..b073d339f10cd 100644
--- a/src/tools/miri/tests/fail/match/all_variants_uninhabited.rs
+++ b/src/tools/miri/tests/fail/match/all_variants_uninhabited.rs
@@ -5,7 +5,7 @@ enum Never {}
 fn main() {
     unsafe {
         match *std::ptr::null::>() {
-        //~^ ERROR: read discriminant of an uninhabited enum variant
+            //~^ ERROR: read discriminant of an uninhabited enum variant
             Ok(_) => {
                 lol();
             }
diff --git a/src/tools/miri/tests/fail/match/closures/uninhabited-variant2.rs b/src/tools/miri/tests/fail/match/closures/uninhabited-variant2.rs
index ed68e357fbd59..050e83884d1e2 100644
--- a/src/tools/miri/tests/fail/match/closures/uninhabited-variant2.rs
+++ b/src/tools/miri/tests/fail/match/closures/uninhabited-variant2.rs
@@ -22,7 +22,8 @@ fn main() {
         // After rust-lang/rust#138961, constructing the closure performs a reborrow of r.
         // Nevertheless, the discriminant is only actually inspected when the closure
         // is called.
-        match r { //~ ERROR: read discriminant of an uninhabited enum variant
+        match r {
+            //~^ ERROR: read discriminant of an uninhabited enum variant
             E::V0 => {}
             E::V1(_) => {}
         }
diff --git a/src/tools/miri/tests/fail/match/only_inhabited_variant.rs b/src/tools/miri/tests/fail/match/only_inhabited_variant.rs
index 30a7350d2b667..2be5e8083aa7f 100644
--- a/src/tools/miri/tests/fail/match/only_inhabited_variant.rs
+++ b/src/tools/miri/tests/fail/match/only_inhabited_variant.rs
@@ -4,8 +4,8 @@
 #[repr(C)]
 #[allow(dead_code)]
 enum E {
-  V0, // discriminant: 0
-  V1(!), // 1
+    V0,    // discriminant: 0
+    V1(!), // 1
 }
 
 fn main() {
@@ -14,7 +14,8 @@ fn main() {
     let val = 1u32;
     let ptr = (&raw const val).cast::();
     let r = unsafe { &*ptr };
-    match r { //~ ERROR: read discriminant of an uninhabited enum variant
+    match r {
+        //~^ ERROR: read discriminant of an uninhabited enum variant
         E::V0 => {}
         E::V1(_) => {}
     }
diff --git a/src/tools/miri/tests/fail/match/single_variant.rs b/src/tools/miri/tests/fail/match/single_variant.rs
index dcef6d461a2c3..35bb63620ea23 100644
--- a/src/tools/miri/tests/fail/match/single_variant.rs
+++ b/src/tools/miri/tests/fail/match/single_variant.rs
@@ -20,12 +20,13 @@ fn main() {
         let x: &[u8; 2] = &[21, 37];
         let y: &Exhaustive = std::mem::transmute(x);
         match y {
-            Exhaustive::A(_) => {},
+            Exhaustive::A(_) => {}
         }
 
         let y: &NonExhaustive = std::mem::transmute(x);
-        match y { //~ ERROR: enum value has invalid tag
-            NonExhaustive::A(_) => {},
+        match y {
+            //~^ ERROR: enum value has invalid tag
+            NonExhaustive::A(_) => {}
         }
     }
 }
diff --git a/src/tools/miri/tests/fail/match/single_variant_uninit.rs b/src/tools/miri/tests/fail/match/single_variant_uninit.rs
index 51e8bc57c837a..e04947f996288 100644
--- a/src/tools/miri/tests/fail/match/single_variant_uninit.rs
+++ b/src/tools/miri/tests/fail/match/single_variant_uninit.rs
@@ -28,7 +28,8 @@ fn main() {
             _ => {}
         }
 
-        match *nexh { //~ ERROR: memory is uninitialized
+        match *nexh {
+            //~^ ERROR: memory is uninitialized
             NonExhaustive::A(ref _val) => {}
             _ => {}
         }

From 2ef5156751c949377e1fee6c71a3257dcc2ef0d6 Mon Sep 17 00:00:00 2001
From: The rustc-josh-sync Cronjob Bot 
Date: Mon, 16 Feb 2026 04:49:19 +0000
Subject: [PATCH 091/103] Prepare for merging from rust-lang/rust

This updates the rust-version file to 139651428df86cf88443295542c12ea617cbb587.
---
 src/tools/rust-analyzer/rust-version | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/tools/rust-analyzer/rust-version b/src/tools/rust-analyzer/rust-version
index a1011c4a0acf0..b22c6c3869c62 100644
--- a/src/tools/rust-analyzer/rust-version
+++ b/src/tools/rust-analyzer/rust-version
@@ -1 +1 @@
-ba284f468cd2cda48420251efc991758ec13d450
+139651428df86cf88443295542c12ea617cbb587

From 0f5c2215ad3a6e5475c87ddd6416b9a13437aaa8 Mon Sep 17 00:00:00 2001
From: Zalathar 
Date: Mon, 16 Feb 2026 17:13:47 +1100
Subject: [PATCH 092/103] Regression test for "unstable" traits in
 force-unstable builds

---
 tests/ui/traits/auxiliary/force_unstable.rs   |  7 ++++
 .../traits/nightly-only-unstable.force.stderr | 36 +++++++++++++++++++
 .../nightly-only-unstable.normal.stderr       | 36 +++++++++++++++++++
 tests/ui/traits/nightly-only-unstable.rs      | 36 +++++++++++++++++++
 4 files changed, 115 insertions(+)
 create mode 100644 tests/ui/traits/auxiliary/force_unstable.rs
 create mode 100644 tests/ui/traits/nightly-only-unstable.force.stderr
 create mode 100644 tests/ui/traits/nightly-only-unstable.normal.stderr
 create mode 100644 tests/ui/traits/nightly-only-unstable.rs

diff --git a/tests/ui/traits/auxiliary/force_unstable.rs b/tests/ui/traits/auxiliary/force_unstable.rs
new file mode 100644
index 0000000000000..ce71e1241f9cb
--- /dev/null
+++ b/tests/ui/traits/auxiliary/force_unstable.rs
@@ -0,0 +1,7 @@
+//@ edition: 2024
+//@ compile-flags: -Zforce-unstable-if-unmarked
+
+// Auxiliary crate that uses `-Zforce-unstable-if-unmarked` to export an
+// "unstable" trait.
+
+pub trait ForeignTrait {}
diff --git a/tests/ui/traits/nightly-only-unstable.force.stderr b/tests/ui/traits/nightly-only-unstable.force.stderr
new file mode 100644
index 0000000000000..b24e2867a304d
--- /dev/null
+++ b/tests/ui/traits/nightly-only-unstable.force.stderr
@@ -0,0 +1,36 @@
+error[E0277]: the trait bound `(): LocalTrait` is not satisfied
+  --> $DIR/nightly-only-unstable.rs:25:21
+   |
+LL |     use_local_trait(());
+   |     --------------- ^^ the nightly-only, unstable trait `LocalTrait` is not implemented for `()`
+   |     |
+   |     required by a bound introduced by this call
+   |
+help: this trait has no implementations, consider adding one
+  --> $DIR/nightly-only-unstable.rs:14:1
+   |
+LL | trait LocalTrait {}
+   | ^^^^^^^^^^^^^^^^
+note: required by a bound in `use_local_trait`
+  --> $DIR/nightly-only-unstable.rs:16:28
+   |
+LL | fn use_local_trait(_: impl LocalTrait) {}
+   |                            ^^^^^^^^^^ required by this bound in `use_local_trait`
+
+error[E0277]: the trait bound `(): ForeignTrait` is not satisfied
+  --> $DIR/nightly-only-unstable.rs:31:23
+   |
+LL |     use_foreign_trait(());
+   |     ----------------- ^^ the nightly-only, unstable trait `ForeignTrait` is not implemented for `()`
+   |     |
+   |     required by a bound introduced by this call
+   |
+note: required by a bound in `use_foreign_trait`
+  --> $DIR/nightly-only-unstable.rs:20:30
+   |
+LL | fn use_foreign_trait(_: impl force_unstable::ForeignTrait) {}
+   |                              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `use_foreign_trait`
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0277`.
diff --git a/tests/ui/traits/nightly-only-unstable.normal.stderr b/tests/ui/traits/nightly-only-unstable.normal.stderr
new file mode 100644
index 0000000000000..51b896cfefdf2
--- /dev/null
+++ b/tests/ui/traits/nightly-only-unstable.normal.stderr
@@ -0,0 +1,36 @@
+error[E0277]: the trait bound `(): LocalTrait` is not satisfied
+  --> $DIR/nightly-only-unstable.rs:25:21
+   |
+LL |     use_local_trait(());
+   |     --------------- ^^ the trait `LocalTrait` is not implemented for `()`
+   |     |
+   |     required by a bound introduced by this call
+   |
+help: this trait has no implementations, consider adding one
+  --> $DIR/nightly-only-unstable.rs:14:1
+   |
+LL | trait LocalTrait {}
+   | ^^^^^^^^^^^^^^^^
+note: required by a bound in `use_local_trait`
+  --> $DIR/nightly-only-unstable.rs:16:28
+   |
+LL | fn use_local_trait(_: impl LocalTrait) {}
+   |                            ^^^^^^^^^^ required by this bound in `use_local_trait`
+
+error[E0277]: the trait bound `(): ForeignTrait` is not satisfied
+  --> $DIR/nightly-only-unstable.rs:31:23
+   |
+LL |     use_foreign_trait(());
+   |     ----------------- ^^ the nightly-only, unstable trait `ForeignTrait` is not implemented for `()`
+   |     |
+   |     required by a bound introduced by this call
+   |
+note: required by a bound in `use_foreign_trait`
+  --> $DIR/nightly-only-unstable.rs:20:30
+   |
+LL | fn use_foreign_trait(_: impl force_unstable::ForeignTrait) {}
+   |                              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `use_foreign_trait`
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0277`.
diff --git a/tests/ui/traits/nightly-only-unstable.rs b/tests/ui/traits/nightly-only-unstable.rs
new file mode 100644
index 0000000000000..c3fa5df944b64
--- /dev/null
+++ b/tests/ui/traits/nightly-only-unstable.rs
@@ -0,0 +1,36 @@
+//@ revisions: normal force
+//@ edition: 2024
+//@ aux-crate: force_unstable=force_unstable.rs
+//@[force] compile-flags: -Zforce-unstable-if-unmarked
+
+#![feature(rustc_private)]
+
+// Regression test for .
+//
+// When building a crate with `-Zforce-unstable-if-unmarked` (e.g. the compiler or stdlib),
+// it's unhelpful to mention that a not-implemented trait is unstable, because that will
+// be true of every local and foreign trait that isn't explicitly marked stable.
+
+trait LocalTrait {}
+
+fn use_local_trait(_: impl LocalTrait) {}
+//~^ NOTE required by a bound in `use_local_trait`
+//~| NOTE required by this bound in `use_local_trait`
+
+fn use_foreign_trait(_: impl force_unstable::ForeignTrait) {}
+//~^ NOTE required by a bound in `use_foreign_trait`
+//~| NOTE required by this bound in `use_foreign_trait`
+
+fn main() {
+    use_local_trait(());
+    //~^ ERROR the trait bound `(): LocalTrait` is not satisfied
+    //[normal]~| NOTE the trait `LocalTrait` is not implemented for `()`
+    //[force]~| NOTE the nightly-only, unstable trait `LocalTrait` is not implemented for `()`
+    //~| NOTE required by a bound introduced by this call
+
+    use_foreign_trait(());
+    //~^ ERROR the trait bound `(): ForeignTrait` is not satisfied
+    //[normal]~| NOTE the nightly-only, unstable trait `ForeignTrait` is not implemented for `()`
+    //[force]~| NOTE the nightly-only, unstable trait `ForeignTrait` is not implemented for `()`
+    //~| NOTE required by a bound introduced by this call
+}

From 125e69e862b79a2943a68c7dd03bea224073cdf1 Mon Sep 17 00:00:00 2001
From: Zalathar 
Date: Mon, 16 Feb 2026 17:05:13 +1100
Subject: [PATCH 093/103] Suppress unstable-trait notes under
 `-Zforce-unstable-if-unmarked`

---
 .../src/error_reporting/traits/suggestions.rs  | 18 ++++++++++--------
 .../traits/nightly-only-unstable.force.stderr  |  4 ++--
 tests/ui/traits/nightly-only-unstable.rs       |  4 ++--
 3 files changed, 14 insertions(+), 12 deletions(-)

diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs
index 63fd61cb257b2..434c8caae7dc0 100644
--- a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs
+++ b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs
@@ -5608,15 +5608,17 @@ pub(super) fn get_explanation_based_on_obligation<'tcx>(
             None => String::new(),
         };
         if let ty::PredicatePolarity::Positive = trait_predicate.polarity() {
+            // If the trait in question is unstable, mention that fact in the diagnostic.
+            // But if we're building with `-Zforce-unstable-if-unmarked` then _any_ trait
+            // not explicitly marked stable is considered unstable, so the extra text is
+            // unhelpful noise. See .
+            let mention_unstable = !tcx.sess.opts.unstable_opts.force_unstable_if_unmarked
+                && try { tcx.lookup_stability(trait_predicate.def_id())?.level.is_stable() }
+                    == Some(false);
+            let unstable = if mention_unstable { "nightly-only, unstable " } else { "" };
+
             format!(
-                "{pre_message}the {}trait `{}` is not implemented for{desc} `{}`",
-                if tcx.lookup_stability(trait_predicate.def_id()).map(|s| s.level.is_stable())
-                    == Some(false)
-                {
-                    "nightly-only, unstable "
-                } else {
-                    ""
-                },
+                "{pre_message}the {unstable}trait `{}` is not implemented for{desc} `{}`",
                 trait_predicate.print_modifiers_and_trait_path(),
                 tcx.short_string(trait_predicate.self_ty().skip_binder(), long_ty_path),
             )
diff --git a/tests/ui/traits/nightly-only-unstable.force.stderr b/tests/ui/traits/nightly-only-unstable.force.stderr
index b24e2867a304d..c1b5a45dc827e 100644
--- a/tests/ui/traits/nightly-only-unstable.force.stderr
+++ b/tests/ui/traits/nightly-only-unstable.force.stderr
@@ -2,7 +2,7 @@ error[E0277]: the trait bound `(): LocalTrait` is not satisfied
   --> $DIR/nightly-only-unstable.rs:25:21
    |
 LL |     use_local_trait(());
-   |     --------------- ^^ the nightly-only, unstable trait `LocalTrait` is not implemented for `()`
+   |     --------------- ^^ the trait `LocalTrait` is not implemented for `()`
    |     |
    |     required by a bound introduced by this call
    |
@@ -21,7 +21,7 @@ error[E0277]: the trait bound `(): ForeignTrait` is not satisfied
   --> $DIR/nightly-only-unstable.rs:31:23
    |
 LL |     use_foreign_trait(());
-   |     ----------------- ^^ the nightly-only, unstable trait `ForeignTrait` is not implemented for `()`
+   |     ----------------- ^^ the trait `ForeignTrait` is not implemented for `()`
    |     |
    |     required by a bound introduced by this call
    |
diff --git a/tests/ui/traits/nightly-only-unstable.rs b/tests/ui/traits/nightly-only-unstable.rs
index c3fa5df944b64..94f3000743900 100644
--- a/tests/ui/traits/nightly-only-unstable.rs
+++ b/tests/ui/traits/nightly-only-unstable.rs
@@ -25,12 +25,12 @@ fn main() {
     use_local_trait(());
     //~^ ERROR the trait bound `(): LocalTrait` is not satisfied
     //[normal]~| NOTE the trait `LocalTrait` is not implemented for `()`
-    //[force]~| NOTE the nightly-only, unstable trait `LocalTrait` is not implemented for `()`
+    //[force]~| NOTE the trait `LocalTrait` is not implemented for `()`
     //~| NOTE required by a bound introduced by this call
 
     use_foreign_trait(());
     //~^ ERROR the trait bound `(): ForeignTrait` is not satisfied
     //[normal]~| NOTE the nightly-only, unstable trait `ForeignTrait` is not implemented for `()`
-    //[force]~| NOTE the nightly-only, unstable trait `ForeignTrait` is not implemented for `()`
+    //[force]~| NOTE the trait `ForeignTrait` is not implemented for `()`
     //~| NOTE required by a bound introduced by this call
 }

From 72c7d8640f98f5ac8a59eb25eee0126ad9570b79 Mon Sep 17 00:00:00 2001
From: Nicholas Nethercote 
Date: Fri, 13 Feb 2026 15:53:06 +1100
Subject: [PATCH 094/103] Remove `rustc_query_system` README file.

It's identical to the one in `rustc_query_impl`.
---
 compiler/rustc_query_system/src/query/README.md | 3 ---
 1 file changed, 3 deletions(-)
 delete mode 100644 compiler/rustc_query_system/src/query/README.md

diff --git a/compiler/rustc_query_system/src/query/README.md b/compiler/rustc_query_system/src/query/README.md
deleted file mode 100644
index 8ec07b9fdeb78..0000000000000
--- a/compiler/rustc_query_system/src/query/README.md
+++ /dev/null
@@ -1,3 +0,0 @@
-For more information about how the query system works, see the [rustc dev guide].
-
-[rustc dev guide]: https://rustc-dev-guide.rust-lang.org/query.html

From 8d56cfe4c3dfec0a258b8371931c608fce4510bb Mon Sep 17 00:00:00 2001
From: Nicholas Nethercote 
Date: Fri, 13 Feb 2026 15:58:57 +1100
Subject: [PATCH 095/103] Move `QuerySideEffect`.

From `rustc_query_system` to `rustc_middle.` I put it in `graph.rs`,
it's one of two files that uses `QuerySideEffect` and seemed as good as
anywhere else.
---
 Cargo.lock                                    |  3 ---
 compiler/rustc_middle/src/dep_graph/graph.rs  | 18 ++++++++++++++-
 compiler/rustc_middle/src/dep_graph/mod.rs    |  3 ++-
 .../rustc_middle/src/query/on_disk_cache.rs   |  3 +--
 compiler/rustc_query_system/Cargo.toml        |  3 ---
 compiler/rustc_query_system/src/lib.rs        |  1 -
 compiler/rustc_query_system/src/query/mod.rs  | 22 -------------------
 7 files changed, 20 insertions(+), 33 deletions(-)
 delete mode 100644 compiler/rustc_query_system/src/query/mod.rs

diff --git a/Cargo.lock b/Cargo.lock
index 53cac09b7a7ab..0afdced031e66 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -4514,11 +4514,8 @@ dependencies = [
  "rustc_abi",
  "rustc_ast",
  "rustc_data_structures",
- "rustc_errors",
  "rustc_feature",
  "rustc_hir",
- "rustc_macros",
- "rustc_serialize",
  "rustc_session",
  "rustc_span",
  "smallvec",
diff --git a/compiler/rustc_middle/src/dep_graph/graph.rs b/compiler/rustc_middle/src/dep_graph/graph.rs
index 6b9f18a6bfe1d..a243bbb343346 100644
--- a/compiler/rustc_middle/src/dep_graph/graph.rs
+++ b/compiler/rustc_middle/src/dep_graph/graph.rs
@@ -16,7 +16,6 @@ use rustc_errors::DiagInner;
 use rustc_index::IndexVec;
 use rustc_macros::{Decodable, Encodable};
 use rustc_query_system::ich::StableHashingContext;
-use rustc_query_system::query::QuerySideEffect;
 use rustc_serialize::opaque::{FileEncodeResult, FileEncoder};
 use rustc_session::Session;
 use tracing::{debug, instrument};
@@ -30,6 +29,23 @@ use crate::dep_graph::edges::EdgesVec;
 use crate::ty::TyCtxt;
 use crate::verify_ich::incremental_verify_ich;
 
+/// Tracks 'side effects' for a particular query.
+/// This struct is saved to disk along with the query result,
+/// and loaded from disk if we mark the query as green.
+/// This allows us to 'replay' changes to global state
+/// that would otherwise only occur if we actually
+/// executed the query method.
+///
+/// Each side effect gets an unique dep node index which is added
+/// as a dependency of the query which had the effect.
+#[derive(Debug, Encodable, Decodable)]
+pub enum QuerySideEffect {
+    /// Stores a diagnostic emitted during query execution.
+    /// This diagnostic will be re-emitted if we mark
+    /// the query as green, as that query will have the side
+    /// effect dep node as a dependency.
+    Diagnostic(DiagInner),
+}
 #[derive(Clone)]
 pub struct DepGraph {
     data: Option>,
diff --git a/compiler/rustc_middle/src/dep_graph/mod.rs b/compiler/rustc_middle/src/dep_graph/mod.rs
index b10f341c71a6d..e8b2f86e5718c 100644
--- a/compiler/rustc_middle/src/dep_graph/mod.rs
+++ b/compiler/rustc_middle/src/dep_graph/mod.rs
@@ -7,7 +7,8 @@ pub use self::dep_node::{
     label_strs,
 };
 pub use self::graph::{
-    DepGraph, DepGraphData, DepNodeIndex, TaskDepsRef, WorkProduct, WorkProductMap, hash_result,
+    DepGraph, DepGraphData, DepNodeIndex, QuerySideEffect, TaskDepsRef, WorkProduct,
+    WorkProductMap, hash_result,
 };
 use self::graph::{MarkFrame, print_markframe_trace};
 pub use self::query::DepGraphQuery;
diff --git a/compiler/rustc_middle/src/query/on_disk_cache.rs b/compiler/rustc_middle/src/query/on_disk_cache.rs
index 5ef0c2500e7a9..e874e7e22b5c9 100644
--- a/compiler/rustc_middle/src/query/on_disk_cache.rs
+++ b/compiler/rustc_middle/src/query/on_disk_cache.rs
@@ -11,7 +11,6 @@ use rustc_hir::def_id::{CrateNum, DefId, DefIndex, LOCAL_CRATE, LocalDefId, Stab
 use rustc_hir::definitions::DefPathHash;
 use rustc_index::{Idx, IndexVec};
 use rustc_macros::{Decodable, Encodable};
-use rustc_query_system::query::QuerySideEffect;
 use rustc_serialize::opaque::{FileEncodeResult, FileEncoder, IntEncodedWithFixedSize, MemDecoder};
 use rustc_serialize::{Decodable, Decoder, Encodable, Encoder};
 use rustc_session::Session;
@@ -24,7 +23,7 @@ use rustc_span::{
     SourceFile, Span, SpanDecoder, SpanEncoder, StableSourceFileId, Symbol,
 };
 
-use crate::dep_graph::{DepNodeIndex, SerializedDepNodeIndex};
+use crate::dep_graph::{DepNodeIndex, QuerySideEffect, SerializedDepNodeIndex};
 use crate::mir::interpret::{AllocDecodingSession, AllocDecodingState};
 use crate::mir::mono::MonoItem;
 use crate::mir::{self, interpret};
diff --git a/compiler/rustc_query_system/Cargo.toml b/compiler/rustc_query_system/Cargo.toml
index bd12dcbfe0d18..8ea5b6c5487b9 100644
--- a/compiler/rustc_query_system/Cargo.toml
+++ b/compiler/rustc_query_system/Cargo.toml
@@ -8,11 +8,8 @@ edition = "2024"
 rustc_abi = { path = "../rustc_abi" }
 rustc_ast = { path = "../rustc_ast" }
 rustc_data_structures = { path = "../rustc_data_structures" }
-rustc_errors = { path = "../rustc_errors" }
 rustc_feature = { path = "../rustc_feature" }
 rustc_hir = { path = "../rustc_hir" }
-rustc_macros = { path = "../rustc_macros" }
-rustc_serialize = { path = "../rustc_serialize" }
 rustc_session = { path = "../rustc_session" }
 rustc_span = { path = "../rustc_span" }
 smallvec = { version = "1.8.1", features = ["union", "may_dangle"] }
diff --git a/compiler/rustc_query_system/src/lib.rs b/compiler/rustc_query_system/src/lib.rs
index bb077d02422b9..21c8650ea56a8 100644
--- a/compiler/rustc_query_system/src/lib.rs
+++ b/compiler/rustc_query_system/src/lib.rs
@@ -5,4 +5,3 @@
 // tidy-alphabetical-end
 
 pub mod ich;
-pub mod query;
diff --git a/compiler/rustc_query_system/src/query/mod.rs b/compiler/rustc_query_system/src/query/mod.rs
deleted file mode 100644
index 87be4358fb8ba..0000000000000
--- a/compiler/rustc_query_system/src/query/mod.rs
+++ /dev/null
@@ -1,22 +0,0 @@
-use std::fmt::Debug;
-
-use rustc_errors::DiagInner;
-use rustc_macros::{Decodable, Encodable};
-
-/// Tracks 'side effects' for a particular query.
-/// This struct is saved to disk along with the query result,
-/// and loaded from disk if we mark the query as green.
-/// This allows us to 'replay' changes to global state
-/// that would otherwise only occur if we actually
-/// executed the query method.
-///
-/// Each side effect gets an unique dep node index which is added
-/// as a dependency of the query which had the effect.
-#[derive(Debug, Encodable, Decodable)]
-pub enum QuerySideEffect {
-    /// Stores a diagnostic emitted during query execution.
-    /// This diagnostic will be re-emitted if we mark
-    /// the query as green, as that query will have the side
-    /// effect dep node as a dependency.
-    Diagnostic(DiagInner),
-}

From 6a3a3d4f98da0f9f6cba62735ced5ea69505e3a4 Mon Sep 17 00:00:00 2001
From: Ralf Jung 
Date: Mon, 16 Feb 2026 11:08:06 +0100
Subject: [PATCH 096/103] try to make cargo-miri work with bootstrap cargo

---
 src/tools/miri/cargo-miri/src/setup.rs | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/src/tools/miri/cargo-miri/src/setup.rs b/src/tools/miri/cargo-miri/src/setup.rs
index 47c99f939254e..76e9e62f52001 100644
--- a/src/tools/miri/cargo-miri/src/setup.rs
+++ b/src/tools/miri/cargo-miri/src/setup.rs
@@ -89,7 +89,10 @@ pub fn setup(
     let cargo_cmd = {
         let mut command = cargo();
         // Allow JSON targets since users do not have a good way to set this flag otherwise.
-        command.arg("-Zjson-target-spec");
+        if env::var("RUSTC_STAGE").is_err() {
+            // ^ is a HACK for bootstrap cargo. FIXME(cfg(bootstrap)) remove the hack.
+            command.arg("-Zjson-target-spec");
+        }
         // Use Miri as rustc to build a libstd compatible with us (and use the right flags).
         // We set ourselves (`cargo-miri`) instead of Miri directly to be able to patch the flags
         // for `libpanic_abort` (usually this is done by bootstrap but we have to do it ourselves).

From 74dd36a934caddbfabe3a44694958e4d3b2c4af6 Mon Sep 17 00:00:00 2001
From: Nicholas Nethercote 
Date: Mon, 16 Feb 2026 21:04:57 +1100
Subject: [PATCH 097/103] Remove `DEP_KIND_DEBUG`.

It's plumbing to work around lack of access to `rustc_middle`, which is
no longer a problem.
---
 compiler/rustc_interface/src/callbacks.rs       | 17 +----------------
 compiler/rustc_middle/src/dep_graph/dep_node.rs | 17 ++++++++---------
 2 files changed, 9 insertions(+), 25 deletions(-)

diff --git a/compiler/rustc_interface/src/callbacks.rs b/compiler/rustc_interface/src/callbacks.rs
index 05ddfd1f107a5..114d73220d379 100644
--- a/compiler/rustc_interface/src/callbacks.rs
+++ b/compiler/rustc_interface/src/callbacks.rs
@@ -12,8 +12,7 @@
 use std::fmt;
 
 use rustc_errors::{DiagInner, TRACK_DIAGNOSTIC};
-use rustc_middle::dep_graph::dep_node::default_dep_kind_debug;
-use rustc_middle::dep_graph::{DepKind, DepNode, TaskDepsRef};
+use rustc_middle::dep_graph::{DepNode, TaskDepsRef};
 use rustc_middle::ty::tls;
 
 fn track_span_parent(def_id: rustc_span::def_id::LocalDefId) {
@@ -65,18 +64,6 @@ fn def_id_debug(def_id: rustc_hir::def_id::DefId, f: &mut fmt::Formatter<'_>) ->
     write!(f, ")")
 }
 
-/// This is a callback from `rustc_query_system` as it cannot access the implicit state
-/// in `rustc_middle` otherwise.
-pub fn dep_kind_debug(kind: DepKind, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
-    tls::with_opt(|opt_tcx| {
-        if let Some(tcx) = opt_tcx {
-            write!(f, "{}", tcx.dep_kind_vtable(kind).name)
-        } else {
-            default_dep_kind_debug(kind, f)
-        }
-    })
-}
-
 /// This is a callback from `rustc_query_system` as it cannot access the implicit state
 /// in `rustc_middle` otherwise.
 pub fn dep_node_debug(node: DepNode, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
@@ -105,8 +92,6 @@ pub fn dep_node_debug(node: DepNode, f: &mut std::fmt::Formatter<'_>) -> std::fm
 pub fn setup_callbacks() {
     rustc_span::SPAN_TRACK.swap(&(track_span_parent as fn(_)));
     rustc_hir::def_id::DEF_ID_DEBUG.swap(&(def_id_debug as fn(_, &mut fmt::Formatter<'_>) -> _));
-    rustc_middle::dep_graph::dep_node::DEP_KIND_DEBUG
-        .swap(&(dep_kind_debug as fn(_, &mut fmt::Formatter<'_>) -> _));
     rustc_middle::dep_graph::dep_node::DEP_NODE_DEBUG
         .swap(&(dep_node_debug as fn(_, &mut fmt::Formatter<'_>) -> _));
     TRACK_DIAGNOSTIC.swap(&(track_diagnostic as _));
diff --git a/compiler/rustc_middle/src/dep_graph/dep_node.rs b/compiler/rustc_middle/src/dep_graph/dep_node.rs
index 909638b85906c..cec2d0e4bbed7 100644
--- a/compiler/rustc_middle/src/dep_graph/dep_node.rs
+++ b/compiler/rustc_middle/src/dep_graph/dep_node.rs
@@ -69,7 +69,7 @@ use rustc_span::Symbol;
 
 use super::{FingerprintStyle, SerializedDepNodeIndex};
 use crate::mir::mono::MonoItem;
-use crate::ty::TyCtxt;
+use crate::ty::{TyCtxt, tls};
 
 /// This serves as an index into arrays built by `make_dep_kind_array`.
 #[derive(Clone, Copy, PartialEq, Eq, Hash)]
@@ -114,16 +114,15 @@ impl DepKind {
     pub(crate) const MAX: u16 = DEP_KIND_VARIANTS - 1;
 }
 
-pub fn default_dep_kind_debug(kind: DepKind, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-    f.debug_struct("DepKind").field("variant", &kind.variant).finish()
-}
-
-pub static DEP_KIND_DEBUG: AtomicRef) -> fmt::Result> =
-    AtomicRef::new(&(default_dep_kind_debug as fn(_, &mut fmt::Formatter<'_>) -> _));
-
 impl fmt::Debug for DepKind {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        (*DEP_KIND_DEBUG)(*self, f)
+        tls::with_opt(|opt_tcx| {
+            if let Some(tcx) = opt_tcx {
+                write!(f, "{}", tcx.dep_kind_vtable(*self).name)
+            } else {
+                f.debug_struct("DepKind").field("variant", &self.variant).finish()
+            }
+        })
     }
 }
 

From 47ed4526d9aa208e7a5c9249586d42784ff660da Mon Sep 17 00:00:00 2001
From: Nicholas Nethercote 
Date: Mon, 16 Feb 2026 21:13:40 +1100
Subject: [PATCH 098/103] Remove `DEP_NODE_DEBUG`.

Like the previous commit, it's no longer needed.
---
 compiler/rustc_interface/src/callbacks.rs     | 31 ++-----------------
 .../rustc_middle/src/dep_graph/dep_node.rs    | 27 ++++++++++------
 2 files changed, 21 insertions(+), 37 deletions(-)

diff --git a/compiler/rustc_interface/src/callbacks.rs b/compiler/rustc_interface/src/callbacks.rs
index 114d73220d379..4a1da0f50cc23 100644
--- a/compiler/rustc_interface/src/callbacks.rs
+++ b/compiler/rustc_interface/src/callbacks.rs
@@ -11,8 +11,8 @@
 
 use std::fmt;
 
-use rustc_errors::{DiagInner, TRACK_DIAGNOSTIC};
-use rustc_middle::dep_graph::{DepNode, TaskDepsRef};
+use rustc_errors::DiagInner;
+use rustc_middle::dep_graph::TaskDepsRef;
 use rustc_middle::ty::tls;
 
 fn track_span_parent(def_id: rustc_span::def_id::LocalDefId) {
@@ -64,35 +64,10 @@ fn def_id_debug(def_id: rustc_hir::def_id::DefId, f: &mut fmt::Formatter<'_>) ->
     write!(f, ")")
 }
 
-/// This is a callback from `rustc_query_system` as it cannot access the implicit state
-/// in `rustc_middle` otherwise.
-pub fn dep_node_debug(node: DepNode, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
-    write!(f, "{:?}(", node.kind)?;
-
-    tls::with_opt(|opt_tcx| {
-        if let Some(tcx) = opt_tcx {
-            if let Some(def_id) = node.extract_def_id(tcx) {
-                write!(f, "{}", tcx.def_path_debug_str(def_id))?;
-            } else if let Some(ref s) = tcx.dep_graph.dep_node_debug_str(node) {
-                write!(f, "{s}")?;
-            } else {
-                write!(f, "{}", node.hash)?;
-            }
-        } else {
-            write!(f, "{}", node.hash)?;
-        }
-        Ok(())
-    })?;
-
-    write!(f, ")")
-}
-
 /// Sets up the callbacks in prior crates which we want to refer to the
 /// TyCtxt in.
 pub fn setup_callbacks() {
     rustc_span::SPAN_TRACK.swap(&(track_span_parent as fn(_)));
     rustc_hir::def_id::DEF_ID_DEBUG.swap(&(def_id_debug as fn(_, &mut fmt::Formatter<'_>) -> _));
-    rustc_middle::dep_graph::dep_node::DEP_NODE_DEBUG
-        .swap(&(dep_node_debug as fn(_, &mut fmt::Formatter<'_>) -> _));
-    TRACK_DIAGNOSTIC.swap(&(track_diagnostic as _));
+    rustc_errors::TRACK_DIAGNOSTIC.swap(&(track_diagnostic as _));
 }
diff --git a/compiler/rustc_middle/src/dep_graph/dep_node.rs b/compiler/rustc_middle/src/dep_graph/dep_node.rs
index cec2d0e4bbed7..099a4613edf67 100644
--- a/compiler/rustc_middle/src/dep_graph/dep_node.rs
+++ b/compiler/rustc_middle/src/dep_graph/dep_node.rs
@@ -58,7 +58,6 @@
 use std::fmt;
 use std::hash::Hash;
 
-use rustc_data_structures::AtomicRef;
 use rustc_data_structures::fingerprint::{Fingerprint, PackedFingerprint};
 use rustc_data_structures::stable_hasher::{HashStable, StableHasher, StableOrd, ToStableHashKey};
 use rustc_hir::def_id::DefId;
@@ -174,16 +173,26 @@ impl DepNode {
     }
 }
 
-pub fn default_dep_node_debug(node: DepNode, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-    f.debug_struct("DepNode").field("kind", &node.kind).field("hash", &node.hash).finish()
-}
-
-pub static DEP_NODE_DEBUG: AtomicRef) -> fmt::Result> =
-    AtomicRef::new(&(default_dep_node_debug as fn(_, &mut fmt::Formatter<'_>) -> _));
-
 impl fmt::Debug for DepNode {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        (*DEP_NODE_DEBUG)(*self, f)
+        write!(f, "{:?}(", self.kind)?;
+
+        tls::with_opt(|opt_tcx| {
+            if let Some(tcx) = opt_tcx {
+                if let Some(def_id) = self.extract_def_id(tcx) {
+                    write!(f, "{}", tcx.def_path_debug_str(def_id))?;
+                } else if let Some(ref s) = tcx.dep_graph.dep_node_debug_str(*self) {
+                    write!(f, "{s}")?;
+                } else {
+                    write!(f, "{}", self.hash)?;
+                }
+            } else {
+                write!(f, "{}", self.hash)?;
+            }
+            Ok(())
+        })?;
+
+        write!(f, ")")
     }
 }
 

From e9288e7e908228fd33e6e4407f36291be2b66f9c Mon Sep 17 00:00:00 2001
From: Nicholas Nethercote 
Date: Fri, 13 Feb 2026 16:02:59 +1100
Subject: [PATCH 099/103] Remove last remnants of `rustc_query_system`.

At this point module `ich` is the only thing left.
---
 Cargo.lock                                       | 16 ----------------
 compiler/rustc_codegen_llvm/Cargo.toml           |  1 -
 compiler/rustc_macros/src/hash_stable.rs         |  2 +-
 compiler/rustc_middle/Cargo.toml                 |  1 -
 compiler/rustc_middle/src/dep_graph/dep_node.rs  |  2 +-
 compiler/rustc_middle/src/dep_graph/graph.rs     |  2 +-
 .../src/ich/hcx.rs                               |  0
 .../src/ich/impls_syntax.rs                      |  2 +-
 .../src/ich/mod.rs                               |  0
 compiler/rustc_middle/src/lib.rs                 |  1 +
 compiler/rustc_middle/src/middle/privacy.rs      |  2 +-
 compiler/rustc_middle/src/mir/mod.rs             |  3 ++-
 compiler/rustc_middle/src/mir/mono.rs            |  2 +-
 compiler/rustc_middle/src/query/caches.rs        |  2 +-
 compiler/rustc_middle/src/query/plumbing.rs      |  5 +----
 compiler/rustc_middle/src/ty/adt.rs              |  2 +-
 compiler/rustc_middle/src/ty/context.rs          |  4 ++--
 compiler/rustc_middle/src/ty/impls_ty.rs         |  2 +-
 compiler/rustc_middle/src/ty/mod.rs              |  2 +-
 compiler/rustc_middle/src/verify_ich.rs          |  2 +-
 compiler/rustc_query_system/Cargo.toml           | 16 ----------------
 compiler/rustc_query_system/src/lib.rs           |  7 -------
 .../builder/cli_paths/snapshots/x_bench.snap     |  1 -
 .../cli_paths/snapshots/x_build_compiler.snap    |  1 -
 .../builder/cli_paths/snapshots/x_check.snap     |  1 -
 .../cli_paths/snapshots/x_check_compiler.snap    |  1 -
 ..._check_compiletest_include_default_paths.snap |  1 -
 .../builder/cli_paths/snapshots/x_clippy.snap    |  1 -
 .../core/builder/cli_paths/snapshots/x_fix.snap  |  1 -
 .../core/builder/cli_paths/snapshots/x_test.snap |  1 -
 .../snapshots/x_test_skip_coverage.snap          |  1 -
 .../cli_paths/snapshots/x_test_skip_tests.snap   |  1 -
 .../snapshots/x_test_skip_tests_etc.snap         |  1 -
 src/bootstrap/src/core/builder/tests.rs          | 14 +++++++-------
 .../queries/incremental-compilation-in-detail.md |  4 ++--
 tests/run-make/short-ice/rmake.rs                |  6 +++---
 tests/ui-fulldeps/hash-stable-is-unstable.rs     |  2 +-
 tests/ui-fulldeps/hash-stable-is-unstable.stderr |  4 ++--
 triagebot.toml                                   |  5 ++---
 39 files changed, 34 insertions(+), 88 deletions(-)
 rename compiler/{rustc_query_system => rustc_middle}/src/ich/hcx.rs (100%)
 rename compiler/{rustc_query_system => rustc_middle}/src/ich/impls_syntax.rs (99%)
 rename compiler/{rustc_query_system => rustc_middle}/src/ich/mod.rs (100%)
 delete mode 100644 compiler/rustc_query_system/Cargo.toml
 delete mode 100644 compiler/rustc_query_system/src/lib.rs

diff --git a/Cargo.lock b/Cargo.lock
index 0afdced031e66..d7d19be42c580 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -3640,7 +3640,6 @@ dependencies = [
  "rustc_macros",
  "rustc_metadata",
  "rustc_middle",
- "rustc_query_system",
  "rustc_sanitizers",
  "rustc_session",
  "rustc_span",
@@ -4243,7 +4242,6 @@ dependencies = [
  "rustc_index",
  "rustc_lint_defs",
  "rustc_macros",
- "rustc_query_system",
  "rustc_serialize",
  "rustc_session",
  "rustc_span",
@@ -4507,20 +4505,6 @@ dependencies = [
  "tracing",
 ]
 
-[[package]]
-name = "rustc_query_system"
-version = "0.0.0"
-dependencies = [
- "rustc_abi",
- "rustc_ast",
- "rustc_data_structures",
- "rustc_feature",
- "rustc_hir",
- "rustc_session",
- "rustc_span",
- "smallvec",
-]
-
 [[package]]
 name = "rustc_resolve"
 version = "0.0.0"
diff --git a/compiler/rustc_codegen_llvm/Cargo.toml b/compiler/rustc_codegen_llvm/Cargo.toml
index 90c87494c3c50..0ffff2d331b1d 100644
--- a/compiler/rustc_codegen_llvm/Cargo.toml
+++ b/compiler/rustc_codegen_llvm/Cargo.toml
@@ -31,7 +31,6 @@ rustc_llvm = { path = "../rustc_llvm" }
 rustc_macros = { path = "../rustc_macros" }
 rustc_metadata = { path = "../rustc_metadata" }
 rustc_middle = { path = "../rustc_middle" }
-rustc_query_system = { path = "../rustc_query_system" }
 rustc_sanitizers = { path = "../rustc_sanitizers" }
 rustc_session = { path = "../rustc_session" }
 rustc_span = { path = "../rustc_span" }
diff --git a/compiler/rustc_macros/src/hash_stable.rs b/compiler/rustc_macros/src/hash_stable.rs
index a6396ba687d11..fa67adb406ed2 100644
--- a/compiler/rustc_macros/src/hash_stable.rs
+++ b/compiler/rustc_macros/src/hash_stable.rs
@@ -96,7 +96,7 @@ fn hash_stable_derive_with_mode(
 
     let context: syn::Type = match mode {
         HashStableMode::Normal => {
-            parse_quote!(::rustc_query_system::ich::StableHashingContext<'__ctx>)
+            parse_quote!(::rustc_middle::ich::StableHashingContext<'__ctx>)
         }
         HashStableMode::Generic | HashStableMode::NoContext => parse_quote!(__CTX),
     };
diff --git a/compiler/rustc_middle/Cargo.toml b/compiler/rustc_middle/Cargo.toml
index 121e77614725f..8bad0e291bf8b 100644
--- a/compiler/rustc_middle/Cargo.toml
+++ b/compiler/rustc_middle/Cargo.toml
@@ -26,7 +26,6 @@ rustc_hir_pretty = { path = "../rustc_hir_pretty" }
 rustc_index = { path = "../rustc_index" }
 rustc_lint_defs = { path = "../rustc_lint_defs" }
 rustc_macros = { path = "../rustc_macros" }
-rustc_query_system = { path = "../rustc_query_system" }
 rustc_serialize = { path = "../rustc_serialize" }
 rustc_session = { path = "../rustc_session" }
 rustc_span = { path = "../rustc_span" }
diff --git a/compiler/rustc_middle/src/dep_graph/dep_node.rs b/compiler/rustc_middle/src/dep_graph/dep_node.rs
index 099a4613edf67..578277bce593a 100644
--- a/compiler/rustc_middle/src/dep_graph/dep_node.rs
+++ b/compiler/rustc_middle/src/dep_graph/dep_node.rs
@@ -63,10 +63,10 @@ use rustc_data_structures::stable_hasher::{HashStable, StableHasher, StableOrd,
 use rustc_hir::def_id::DefId;
 use rustc_hir::definitions::DefPathHash;
 use rustc_macros::{Decodable, Encodable};
-use rustc_query_system::ich::StableHashingContext;
 use rustc_span::Symbol;
 
 use super::{FingerprintStyle, SerializedDepNodeIndex};
+use crate::ich::StableHashingContext;
 use crate::mir::mono::MonoItem;
 use crate::ty::{TyCtxt, tls};
 
diff --git a/compiler/rustc_middle/src/dep_graph/graph.rs b/compiler/rustc_middle/src/dep_graph/graph.rs
index a243bbb343346..6a19c02bb81b1 100644
--- a/compiler/rustc_middle/src/dep_graph/graph.rs
+++ b/compiler/rustc_middle/src/dep_graph/graph.rs
@@ -15,7 +15,6 @@ use rustc_data_structures::{assert_matches, outline};
 use rustc_errors::DiagInner;
 use rustc_index::IndexVec;
 use rustc_macros::{Decodable, Encodable};
-use rustc_query_system::ich::StableHashingContext;
 use rustc_serialize::opaque::{FileEncodeResult, FileEncoder};
 use rustc_session::Session;
 use tracing::{debug, instrument};
@@ -26,6 +25,7 @@ use super::query::DepGraphQuery;
 use super::serialized::{GraphEncoder, SerializedDepGraph, SerializedDepNodeIndex};
 use super::{DepKind, DepNode, HasDepContext, WorkProductId, read_deps, with_deps};
 use crate::dep_graph::edges::EdgesVec;
+use crate::ich::StableHashingContext;
 use crate::ty::TyCtxt;
 use crate::verify_ich::incremental_verify_ich;
 
diff --git a/compiler/rustc_query_system/src/ich/hcx.rs b/compiler/rustc_middle/src/ich/hcx.rs
similarity index 100%
rename from compiler/rustc_query_system/src/ich/hcx.rs
rename to compiler/rustc_middle/src/ich/hcx.rs
diff --git a/compiler/rustc_query_system/src/ich/impls_syntax.rs b/compiler/rustc_middle/src/ich/impls_syntax.rs
similarity index 99%
rename from compiler/rustc_query_system/src/ich/impls_syntax.rs
rename to compiler/rustc_middle/src/ich/impls_syntax.rs
index 5592f65539716..be4e5333c64b3 100644
--- a/compiler/rustc_query_system/src/ich/impls_syntax.rs
+++ b/compiler/rustc_middle/src/ich/impls_syntax.rs
@@ -6,7 +6,7 @@ use rustc_span::{SourceFile, Symbol, sym};
 use smallvec::SmallVec;
 use {rustc_ast as ast, rustc_hir as hir};
 
-use crate::ich::StableHashingContext;
+use super::StableHashingContext;
 
 impl<'a> HashStable> for ast::NodeId {
     #[inline]
diff --git a/compiler/rustc_query_system/src/ich/mod.rs b/compiler/rustc_middle/src/ich/mod.rs
similarity index 100%
rename from compiler/rustc_query_system/src/ich/mod.rs
rename to compiler/rustc_middle/src/ich/mod.rs
diff --git a/compiler/rustc_middle/src/lib.rs b/compiler/rustc_middle/src/lib.rs
index 1c4c987aee920..5fa6f10865b54 100644
--- a/compiler/rustc_middle/src/lib.rs
+++ b/compiler/rustc_middle/src/lib.rs
@@ -72,6 +72,7 @@ pub mod arena;
 pub mod error;
 pub mod hir;
 pub mod hooks;
+pub mod ich;
 pub mod infer;
 pub mod lint;
 pub mod metadata;
diff --git a/compiler/rustc_middle/src/middle/privacy.rs b/compiler/rustc_middle/src/middle/privacy.rs
index e3e04c9d1800b..0be4c8243d632 100644
--- a/compiler/rustc_middle/src/middle/privacy.rs
+++ b/compiler/rustc_middle/src/middle/privacy.rs
@@ -8,9 +8,9 @@ use rustc_data_structures::fx::{FxIndexMap, IndexEntry};
 use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
 use rustc_hir::def::DefKind;
 use rustc_macros::HashStable;
-use rustc_query_system::ich::StableHashingContext;
 use rustc_span::def_id::{CRATE_DEF_ID, LocalDefId};
 
+use crate::ich::StableHashingContext;
 use crate::ty::{TyCtxt, Visibility};
 
 /// Represents the levels of effective visibility an item can have.
diff --git a/compiler/rustc_middle/src/mir/mod.rs b/compiler/rustc_middle/src/mir/mod.rs
index 418cdea01660b..9d0e4b5e6dfa6 100644
--- a/compiler/rustc_middle/src/mir/mod.rs
+++ b/compiler/rustc_middle/src/mir/mod.rs
@@ -911,7 +911,8 @@ pub struct VarBindingIntroduction {
 
 mod binding_form_impl {
     use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
-    use rustc_query_system::ich::StableHashingContext;
+
+    use crate::ich::StableHashingContext;
 
     impl<'a, 'tcx> HashStable> for super::BindingForm<'tcx> {
         fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) {
diff --git a/compiler/rustc_middle/src/mir/mono.rs b/compiler/rustc_middle/src/mir/mono.rs
index 577d226fc9d7a..acebf91b1cbf5 100644
--- a/compiler/rustc_middle/src/mir/mono.rs
+++ b/compiler/rustc_middle/src/mir/mono.rs
@@ -12,7 +12,6 @@ use rustc_hir::ItemId;
 use rustc_hir::attrs::{InlineAttr, Linkage};
 use rustc_hir::def_id::{CrateNum, DefId, DefIdSet, LOCAL_CRATE};
 use rustc_macros::{HashStable, TyDecodable, TyEncodable};
-use rustc_query_system::ich::StableHashingContext;
 use rustc_session::config::OptLevel;
 use rustc_span::{Span, Symbol};
 use rustc_target::spec::SymbolVisibility;
@@ -20,6 +19,7 @@ use tracing::debug;
 
 use crate::dep_graph::dep_node::{make_compile_codegen_unit, make_compile_mono_item};
 use crate::dep_graph::{DepNode, WorkProduct, WorkProductId};
+use crate::ich::StableHashingContext;
 use crate::middle::codegen_fn_attrs::CodegenFnAttrFlags;
 use crate::ty::{self, GenericArgs, Instance, InstanceKind, SymbolName, Ty, TyCtxt};
 
diff --git a/compiler/rustc_middle/src/query/caches.rs b/compiler/rustc_middle/src/query/caches.rs
index 7424492ddc1f3..c1f5e5b670856 100644
--- a/compiler/rustc_middle/src/query/caches.rs
+++ b/compiler/rustc_middle/src/query/caches.rs
@@ -7,10 +7,10 @@ use rustc_data_structures::stable_hasher::HashStable;
 pub use rustc_data_structures::vec_cache::VecCache;
 use rustc_hir::def_id::LOCAL_CRATE;
 use rustc_index::Idx;
-use rustc_query_system::ich::StableHashingContext;
 use rustc_span::def_id::{DefId, DefIndex};
 
 use crate::dep_graph::DepNodeIndex;
+use crate::ich::StableHashingContext;
 
 /// Traits that all query keys must satisfy.
 pub trait QueryCacheKey = Hash + Eq + Copy + Debug + for<'a> HashStable>;
diff --git a/compiler/rustc_middle/src/query/plumbing.rs b/compiler/rustc_middle/src/query/plumbing.rs
index 727e931482510..47b6aea077d17 100644
--- a/compiler/rustc_middle/src/query/plumbing.rs
+++ b/compiler/rustc_middle/src/query/plumbing.rs
@@ -8,12 +8,12 @@ use rustc_data_structures::sync::{AtomicU64, WorkerLocal};
 use rustc_hir::def_id::{DefId, LocalDefId};
 use rustc_hir::hir_id::OwnerId;
 use rustc_macros::HashStable;
-use rustc_query_system::ich::StableHashingContext;
 use rustc_span::{ErrorGuaranteed, Span};
 pub use sealed::IntoQueryParam;
 
 use crate::dep_graph;
 use crate::dep_graph::{DepKind, DepNodeIndex, SerializedDepNodeIndex};
+use crate::ich::StableHashingContext;
 use crate::queries::{
     ExternProviders, PerQueryVTables, Providers, QueryArenas, QueryCaches, QueryEngine, QueryStates,
 };
@@ -102,9 +102,6 @@ pub enum QueryMode {
 }
 
 /// Stores function pointers and other metadata for a particular query.
-///
-/// Used indirectly by query plumbing in `rustc_query_system` via a trait,
-/// and also used directly by query plumbing in `rustc_query_impl`.
 pub struct QueryVTable<'tcx, C: QueryCache> {
     pub name: &'static str,
     pub eval_always: bool,
diff --git a/compiler/rustc_middle/src/ty/adt.rs b/compiler/rustc_middle/src/ty/adt.rs
index 510c546f82a4e..242d3742abad0 100644
--- a/compiler/rustc_middle/src/ty/adt.rs
+++ b/compiler/rustc_middle/src/ty/adt.rs
@@ -15,7 +15,6 @@ use rustc_hir::def_id::DefId;
 use rustc_hir::{self as hir, LangItem, find_attr};
 use rustc_index::{IndexSlice, IndexVec};
 use rustc_macros::{HashStable, TyDecodable, TyEncodable};
-use rustc_query_system::ich::StableHashingContext;
 use rustc_session::DataTypeKind;
 use rustc_type_ir::solve::AdtDestructorKind;
 use tracing::{debug, info, trace};
@@ -23,6 +22,7 @@ use tracing::{debug, info, trace};
 use super::{
     AsyncDestructor, Destructor, FieldDef, GenericPredicates, Ty, TyCtxt, VariantDef, VariantDiscr,
 };
+use crate::ich::StableHashingContext;
 use crate::mir::interpret::ErrorHandled;
 use crate::ty;
 use crate::ty::util::{Discr, IntTypeExt};
diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs
index 430890d5a42d8..94a77ce13c14a 100644
--- a/compiler/rustc_middle/src/ty/context.rs
+++ b/compiler/rustc_middle/src/ty/context.rs
@@ -39,7 +39,6 @@ use rustc_hir::lang_items::LangItem;
 use rustc_hir::limit::Limit;
 use rustc_hir::{self as hir, HirId, Node, TraitCandidate, find_attr};
 use rustc_index::IndexVec;
-use rustc_query_system::ich::StableHashingContext;
 use rustc_serialize::opaque::{FileEncodeResult, FileEncoder};
 use rustc_session::Session;
 use rustc_session::config::CrateType;
@@ -55,6 +54,7 @@ use tracing::{debug, instrument};
 use crate::arena::Arena;
 use crate::dep_graph::dep_node::make_metadata;
 use crate::dep_graph::{DepGraph, DepKindVTable, DepNodeIndex};
+use crate::ich::StableHashingContext;
 use crate::infer::canonical::{CanonicalParamEnvCache, CanonicalVarKind};
 use crate::lint::lint_level;
 use crate::metadata::ModChild;
@@ -1220,7 +1220,7 @@ impl<'tcx> TyCtxt<'tcx> {
     pub fn needs_crate_hash(self) -> bool {
         // Why is the crate hash needed for these configurations?
         // - debug_assertions: for the "fingerprint the result" check in
-        //   `rustc_query_system::query::plumbing::execute_job`.
+        //   `rustc_query_impl::execution::execute_job`.
         // - incremental: for query lookups.
         // - needs_metadata: for putting into crate metadata.
         // - instrument_coverage: for putting into coverage data (see
diff --git a/compiler/rustc_middle/src/ty/impls_ty.rs b/compiler/rustc_middle/src/ty/impls_ty.rs
index 95a1a1bf5bce5..f06ce7324d4c0 100644
--- a/compiler/rustc_middle/src/ty/impls_ty.rs
+++ b/compiler/rustc_middle/src/ty/impls_ty.rs
@@ -9,9 +9,9 @@ use rustc_data_structures::fx::FxHashMap;
 use rustc_data_structures::stable_hasher::{
     HashStable, HashingControls, StableHasher, ToStableHashKey,
 };
-use rustc_query_system::ich::StableHashingContext;
 use tracing::trace;
 
+use crate::ich::StableHashingContext;
 use crate::middle::region;
 use crate::{mir, ty};
 
diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs
index 0363977099254..2c7244187f4fd 100644
--- a/compiler/rustc_middle/src/ty/mod.rs
+++ b/compiler/rustc_middle/src/ty/mod.rs
@@ -47,7 +47,6 @@ use rustc_macros::{
     BlobDecodable, Decodable, Encodable, HashStable, TyDecodable, TyEncodable, TypeFoldable,
     TypeVisitable, extension,
 };
-use rustc_query_system::ich::StableHashingContext;
 use rustc_serialize::{Decodable, Encodable};
 pub use rustc_session::lint::RegisteredTools;
 use rustc_span::hygiene::MacroKind;
@@ -112,6 +111,7 @@ pub use self::typeck_results::{
     Rust2024IncompatiblePatInfo, TypeckResults, UserType, UserTypeAnnotationIndex, UserTypeKind,
 };
 use crate::error::{OpaqueHiddenTypeMismatch, TypeMismatchReason};
+use crate::ich::StableHashingContext;
 use crate::metadata::{AmbigModChild, ModChild};
 use crate::middle::privacy::EffectiveVisibilities;
 use crate::mir::{Body, CoroutineLayout, CoroutineSavedLocal, SourceInfo};
diff --git a/compiler/rustc_middle/src/verify_ich.rs b/compiler/rustc_middle/src/verify_ich.rs
index 290786d439d4e..ff3c5bb5a9d6b 100644
--- a/compiler/rustc_middle/src/verify_ich.rs
+++ b/compiler/rustc_middle/src/verify_ich.rs
@@ -1,10 +1,10 @@
 use std::cell::Cell;
 
 use rustc_data_structures::fingerprint::Fingerprint;
-use rustc_query_system::ich::StableHashingContext;
 use tracing::instrument;
 
 use crate::dep_graph::{DepGraphData, SerializedDepNodeIndex};
+use crate::ich::StableHashingContext;
 use crate::ty::TyCtxt;
 
 #[inline]
diff --git a/compiler/rustc_query_system/Cargo.toml b/compiler/rustc_query_system/Cargo.toml
deleted file mode 100644
index 8ea5b6c5487b9..0000000000000
--- a/compiler/rustc_query_system/Cargo.toml
+++ /dev/null
@@ -1,16 +0,0 @@
-[package]
-name = "rustc_query_system"
-version = "0.0.0"
-edition = "2024"
-
-[dependencies]
-# tidy-alphabetical-start
-rustc_abi = { path = "../rustc_abi" }
-rustc_ast = { path = "../rustc_ast" }
-rustc_data_structures = { path = "../rustc_data_structures" }
-rustc_feature = { path = "../rustc_feature" }
-rustc_hir = { path = "../rustc_hir" }
-rustc_session = { path = "../rustc_session" }
-rustc_span = { path = "../rustc_span" }
-smallvec = { version = "1.8.1", features = ["union", "may_dangle"] }
-# tidy-alphabetical-end
diff --git a/compiler/rustc_query_system/src/lib.rs b/compiler/rustc_query_system/src/lib.rs
deleted file mode 100644
index 21c8650ea56a8..0000000000000
--- a/compiler/rustc_query_system/src/lib.rs
+++ /dev/null
@@ -1,7 +0,0 @@
-// tidy-alphabetical-start
-#![allow(internal_features)]
-#![cfg_attr(bootstrap, feature(assert_matches))]
-#![feature(min_specialization)]
-// tidy-alphabetical-end
-
-pub mod ich;
diff --git a/src/bootstrap/src/core/builder/cli_paths/snapshots/x_bench.snap b/src/bootstrap/src/core/builder/cli_paths/snapshots/x_bench.snap
index 3adf952d66e07..294623f073864 100644
--- a/src/bootstrap/src/core/builder/cli_paths/snapshots/x_bench.snap
+++ b/src/bootstrap/src/core/builder/cli_paths/snapshots/x_bench.snap
@@ -79,7 +79,6 @@ expression: bench
     - Set({bench::compiler/rustc_public})
     - Set({bench::compiler/rustc_public_bridge})
     - Set({bench::compiler/rustc_query_impl})
-    - Set({bench::compiler/rustc_query_system})
     - Set({bench::compiler/rustc_resolve})
     - Set({bench::compiler/rustc_sanitizers})
     - Set({bench::compiler/rustc_serialize})
diff --git a/src/bootstrap/src/core/builder/cli_paths/snapshots/x_build_compiler.snap b/src/bootstrap/src/core/builder/cli_paths/snapshots/x_build_compiler.snap
index 1d6e63696b062..d5da908c8a443 100644
--- a/src/bootstrap/src/core/builder/cli_paths/snapshots/x_build_compiler.snap
+++ b/src/bootstrap/src/core/builder/cli_paths/snapshots/x_build_compiler.snap
@@ -61,7 +61,6 @@ expression: build compiler
     - Set({build::compiler/rustc_public})
     - Set({build::compiler/rustc_public_bridge})
     - Set({build::compiler/rustc_query_impl})
-    - Set({build::compiler/rustc_query_system})
     - Set({build::compiler/rustc_resolve})
     - Set({build::compiler/rustc_sanitizers})
     - Set({build::compiler/rustc_serialize})
diff --git a/src/bootstrap/src/core/builder/cli_paths/snapshots/x_check.snap b/src/bootstrap/src/core/builder/cli_paths/snapshots/x_check.snap
index 6fc2e190290e4..242a2272b4d16 100644
--- a/src/bootstrap/src/core/builder/cli_paths/snapshots/x_check.snap
+++ b/src/bootstrap/src/core/builder/cli_paths/snapshots/x_check.snap
@@ -63,7 +63,6 @@ expression: check
     - Set({check::compiler/rustc_public})
     - Set({check::compiler/rustc_public_bridge})
     - Set({check::compiler/rustc_query_impl})
-    - Set({check::compiler/rustc_query_system})
     - Set({check::compiler/rustc_resolve})
     - Set({check::compiler/rustc_sanitizers})
     - Set({check::compiler/rustc_serialize})
diff --git a/src/bootstrap/src/core/builder/cli_paths/snapshots/x_check_compiler.snap b/src/bootstrap/src/core/builder/cli_paths/snapshots/x_check_compiler.snap
index c0456f7f84d33..dab86b792127f 100644
--- a/src/bootstrap/src/core/builder/cli_paths/snapshots/x_check_compiler.snap
+++ b/src/bootstrap/src/core/builder/cli_paths/snapshots/x_check_compiler.snap
@@ -63,7 +63,6 @@ expression: check compiler
     - Set({check::compiler/rustc_public})
     - Set({check::compiler/rustc_public_bridge})
     - Set({check::compiler/rustc_query_impl})
-    - Set({check::compiler/rustc_query_system})
     - Set({check::compiler/rustc_resolve})
     - Set({check::compiler/rustc_sanitizers})
     - Set({check::compiler/rustc_serialize})
diff --git a/src/bootstrap/src/core/builder/cli_paths/snapshots/x_check_compiletest_include_default_paths.snap b/src/bootstrap/src/core/builder/cli_paths/snapshots/x_check_compiletest_include_default_paths.snap
index 10f36ffa67482..e43d5380a398d 100644
--- a/src/bootstrap/src/core/builder/cli_paths/snapshots/x_check_compiletest_include_default_paths.snap
+++ b/src/bootstrap/src/core/builder/cli_paths/snapshots/x_check_compiletest_include_default_paths.snap
@@ -63,7 +63,6 @@ expression: check compiletest --include-default-paths
     - Set({check::compiler/rustc_public})
     - Set({check::compiler/rustc_public_bridge})
     - Set({check::compiler/rustc_query_impl})
-    - Set({check::compiler/rustc_query_system})
     - Set({check::compiler/rustc_resolve})
     - Set({check::compiler/rustc_sanitizers})
     - Set({check::compiler/rustc_serialize})
diff --git a/src/bootstrap/src/core/builder/cli_paths/snapshots/x_clippy.snap b/src/bootstrap/src/core/builder/cli_paths/snapshots/x_clippy.snap
index 492a10d3862aa..827f2f8b60acb 100644
--- a/src/bootstrap/src/core/builder/cli_paths/snapshots/x_clippy.snap
+++ b/src/bootstrap/src/core/builder/cli_paths/snapshots/x_clippy.snap
@@ -78,7 +78,6 @@ expression: clippy
     - Set({clippy::compiler/rustc_public})
     - Set({clippy::compiler/rustc_public_bridge})
     - Set({clippy::compiler/rustc_query_impl})
-    - Set({clippy::compiler/rustc_query_system})
     - Set({clippy::compiler/rustc_resolve})
     - Set({clippy::compiler/rustc_sanitizers})
     - Set({clippy::compiler/rustc_serialize})
diff --git a/src/bootstrap/src/core/builder/cli_paths/snapshots/x_fix.snap b/src/bootstrap/src/core/builder/cli_paths/snapshots/x_fix.snap
index 41889cd124801..d380cb416acf8 100644
--- a/src/bootstrap/src/core/builder/cli_paths/snapshots/x_fix.snap
+++ b/src/bootstrap/src/core/builder/cli_paths/snapshots/x_fix.snap
@@ -63,7 +63,6 @@ expression: fix
     - Set({fix::compiler/rustc_public})
     - Set({fix::compiler/rustc_public_bridge})
     - Set({fix::compiler/rustc_query_impl})
-    - Set({fix::compiler/rustc_query_system})
     - Set({fix::compiler/rustc_resolve})
     - Set({fix::compiler/rustc_sanitizers})
     - Set({fix::compiler/rustc_serialize})
diff --git a/src/bootstrap/src/core/builder/cli_paths/snapshots/x_test.snap b/src/bootstrap/src/core/builder/cli_paths/snapshots/x_test.snap
index 51e2c270e3ba6..ac2f315d39d96 100644
--- a/src/bootstrap/src/core/builder/cli_paths/snapshots/x_test.snap
+++ b/src/bootstrap/src/core/builder/cli_paths/snapshots/x_test.snap
@@ -129,7 +129,6 @@ expression: test
     - Set({test::compiler/rustc_public})
     - Set({test::compiler/rustc_public_bridge})
     - Set({test::compiler/rustc_query_impl})
-    - Set({test::compiler/rustc_query_system})
     - Set({test::compiler/rustc_resolve})
     - Set({test::compiler/rustc_sanitizers})
     - Set({test::compiler/rustc_serialize})
diff --git a/src/bootstrap/src/core/builder/cli_paths/snapshots/x_test_skip_coverage.snap b/src/bootstrap/src/core/builder/cli_paths/snapshots/x_test_skip_coverage.snap
index bc828c162bb0e..09adbb0041ae6 100644
--- a/src/bootstrap/src/core/builder/cli_paths/snapshots/x_test_skip_coverage.snap
+++ b/src/bootstrap/src/core/builder/cli_paths/snapshots/x_test_skip_coverage.snap
@@ -128,7 +128,6 @@ expression: test --skip=coverage
     - Set({test::compiler/rustc_public})
     - Set({test::compiler/rustc_public_bridge})
     - Set({test::compiler/rustc_query_impl})
-    - Set({test::compiler/rustc_query_system})
     - Set({test::compiler/rustc_resolve})
     - Set({test::compiler/rustc_sanitizers})
     - Set({test::compiler/rustc_serialize})
diff --git a/src/bootstrap/src/core/builder/cli_paths/snapshots/x_test_skip_tests.snap b/src/bootstrap/src/core/builder/cli_paths/snapshots/x_test_skip_tests.snap
index ceb910e4cb36e..b5fccfcb966bb 100644
--- a/src/bootstrap/src/core/builder/cli_paths/snapshots/x_test_skip_tests.snap
+++ b/src/bootstrap/src/core/builder/cli_paths/snapshots/x_test_skip_tests.snap
@@ -92,7 +92,6 @@ expression: test --skip=tests
     - Set({test::compiler/rustc_public})
     - Set({test::compiler/rustc_public_bridge})
     - Set({test::compiler/rustc_query_impl})
-    - Set({test::compiler/rustc_query_system})
     - Set({test::compiler/rustc_resolve})
     - Set({test::compiler/rustc_sanitizers})
     - Set({test::compiler/rustc_serialize})
diff --git a/src/bootstrap/src/core/builder/cli_paths/snapshots/x_test_skip_tests_etc.snap b/src/bootstrap/src/core/builder/cli_paths/snapshots/x_test_skip_tests_etc.snap
index f0e8f1aee2c7f..9ad8914f58e30 100644
--- a/src/bootstrap/src/core/builder/cli_paths/snapshots/x_test_skip_tests_etc.snap
+++ b/src/bootstrap/src/core/builder/cli_paths/snapshots/x_test_skip_tests_etc.snap
@@ -72,7 +72,6 @@ expression: test --skip=tests --skip=coverage-map --skip=coverage-run --skip=lib
     - Set({test::compiler/rustc_public})
     - Set({test::compiler/rustc_public_bridge})
     - Set({test::compiler/rustc_query_impl})
-    - Set({test::compiler/rustc_query_system})
     - Set({test::compiler/rustc_resolve})
     - Set({test::compiler/rustc_sanitizers})
     - Set({test::compiler/rustc_serialize})
diff --git a/src/bootstrap/src/core/builder/tests.rs b/src/bootstrap/src/core/builder/tests.rs
index 61db494c8c188..002fb32dcf0c6 100644
--- a/src/bootstrap/src/core/builder/tests.rs
+++ b/src/bootstrap/src/core/builder/tests.rs
@@ -1815,7 +1815,7 @@ mod snapshot {
         insta::assert_snapshot!(
             ctx.config("check")
                 .path("compiler")
-                .render_steps(), @"[check] rustc 0  -> rustc 1  (74 crates)");
+                .render_steps(), @"[check] rustc 0  -> rustc 1  (73 crates)");
     }
 
     #[test]
@@ -1841,7 +1841,7 @@ mod snapshot {
             ctx.config("check")
                 .path("compiler")
                 .stage(1)
-                .render_steps(), @"[check] rustc 0  -> rustc 1  (74 crates)");
+                .render_steps(), @"[check] rustc 0  -> rustc 1  (73 crates)");
     }
 
     #[test]
@@ -1851,11 +1851,11 @@ mod snapshot {
             ctx.config("check")
                 .path("compiler")
                 .stage(2)
-                .render_steps(), @r"
+                .render_steps(), @"
         [build] llvm 
         [build] rustc 0  -> rustc 1 
         [build] rustc 1  -> std 1 
-        [check] rustc 1  -> rustc 2  (74 crates)
+        [check] rustc 1  -> rustc 2  (73 crates)
         ");
     }
 
@@ -1866,12 +1866,12 @@ mod snapshot {
             ctx.config("check")
                 .targets(&[TEST_TRIPLE_1])
                 .hosts(&[TEST_TRIPLE_1])
-                .render_steps(), @r"
+                .render_steps(), @"
         [build] llvm 
         [build] rustc 0  -> rustc 1 
         [build] rustc 1  -> std 1 
         [check] rustc 1  -> std 1 
-        [check] rustc 1  -> rustc 2  (74 crates)
+        [check] rustc 1  -> rustc 2  (73 crates)
         [check] rustc 1  -> rustc 2 
         [check] rustc 1  -> Rustdoc 2 
         [check] rustc 1  -> rustc_codegen_cranelift 2 
@@ -1967,7 +1967,7 @@ mod snapshot {
             ctx.config("check")
                 .paths(&["library", "compiler"])
                 .args(&args)
-                .render_steps(), @"[check] rustc 0  -> rustc 1  (74 crates)");
+                .render_steps(), @"[check] rustc 0  -> rustc 1  (73 crates)");
     }
 
     #[test]
diff --git a/src/doc/rustc-dev-guide/src/queries/incremental-compilation-in-detail.md b/src/doc/rustc-dev-guide/src/queries/incremental-compilation-in-detail.md
index 46e38832e64d2..cab9f6871f7c5 100644
--- a/src/doc/rustc-dev-guide/src/queries/incremental-compilation-in-detail.md
+++ b/src/doc/rustc-dev-guide/src/queries/incremental-compilation-in-detail.md
@@ -178,7 +178,7 @@ fn try_mark_green(tcx, current_node) -> bool {
 
 > NOTE:
 > The actual implementation can be found in
-> [`compiler/rustc_query_system/src/dep_graph/graph.rs`][try_mark_green]
+> [`compiler/rustc_middle/src/dep_graph/graph.rs`][try_mark_green]
 
 By using red-green marking we can avoid the devastating cumulative effect of
 having false positives during change detection. Whenever a query is executed
@@ -534,4 +534,4 @@ information.
 
 
 [query-model]: ./query-evaluation-model-in-detail.html
-[try_mark_green]: https://doc.rust-lang.org/nightly/nightly-rustc/src/rustc_query_system/dep_graph/graph.rs.html
+[try_mark_green]: https://doc.rust-lang.org/nightly/nightly-rustc/src/rustc_middle/dep_graph/graph.rs.html
diff --git a/tests/run-make/short-ice/rmake.rs b/tests/run-make/short-ice/rmake.rs
index 4fc183a8c74ef..043f880abcf7e 100644
--- a/tests/run-make/short-ice/rmake.rs
+++ b/tests/run-make/short-ice/rmake.rs
@@ -31,10 +31,10 @@ fn main() {
     let output_bt_full = &concat_stderr_stdout(&rustc_bt_full);
 
     // Count how many lines of output mention symbols or paths in
-    // `rustc_query_system` or `rustc_query_impl`, which are the kinds of
+    // `rustc_query_impl`, which are the kinds of
     // stack frames we want to be omitting in short backtraces.
-    let rustc_query_count_short = count_lines_with(output_bt_short, "rustc_query_");
-    let rustc_query_count_full = count_lines_with(output_bt_full, "rustc_query_");
+    let rustc_query_count_short = count_lines_with(output_bt_short, "rustc_query_impl");
+    let rustc_query_count_full = count_lines_with(output_bt_full, "rustc_query_impl");
 
     // Dump both outputs in full to make debugging easier, especially on CI.
     // Use `--no-capture --force-rerun` to view output even when the test is passing.
diff --git a/tests/ui-fulldeps/hash-stable-is-unstable.rs b/tests/ui-fulldeps/hash-stable-is-unstable.rs
index 7f62b60441040..a3dfd7e85ba60 100644
--- a/tests/ui-fulldeps/hash-stable-is-unstable.rs
+++ b/tests/ui-fulldeps/hash-stable-is-unstable.rs
@@ -7,7 +7,7 @@ extern crate rustc_macros;
 //~^ ERROR use of unstable library feature `rustc_private`
 //~| NOTE: see issue #27812  for more information
 //~| NOTE: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
-extern crate rustc_query_system;
+extern crate rustc_middle;
 //~^ ERROR use of unstable library feature `rustc_private`
 //~| NOTE: see issue #27812  for more information
 //~| NOTE: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
diff --git a/tests/ui-fulldeps/hash-stable-is-unstable.stderr b/tests/ui-fulldeps/hash-stable-is-unstable.stderr
index e7740d744b4f8..7c69e8f5e6317 100644
--- a/tests/ui-fulldeps/hash-stable-is-unstable.stderr
+++ b/tests/ui-fulldeps/hash-stable-is-unstable.stderr
@@ -21,8 +21,8 @@ LL | extern crate rustc_macros;
 error[E0658]: use of unstable library feature `rustc_private`: this crate is being loaded from the sysroot, an unstable location; did you mean to load this crate from crates.io via `Cargo.toml` instead?
   --> $DIR/hash-stable-is-unstable.rs:10:1
    |
-LL | extern crate rustc_query_system;
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+LL | extern crate rustc_middle;
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
    = note: see issue #27812  for more information
    = help: add `#![feature(rustc_private)]` to the crate attributes to enable
diff --git a/triagebot.toml b/triagebot.toml
index c9d6c1a730b7f..6c6daac9cf3b1 100644
--- a/triagebot.toml
+++ b/triagebot.toml
@@ -540,7 +540,6 @@ trigger_files = [
 
 [autolabel."A-query-system"]
 trigger_files = [
-    "compiler/rustc_query_system",
     "compiler/rustc_query_impl",
     "compiler/rustc_macros/src/query.rs"
 ]
@@ -1557,8 +1556,10 @@ dep-bumps = [
 "/compiler/rustc_codegen_llvm/src/debuginfo" =           ["compiler", "debuginfo"]
 "/compiler/rustc_codegen_ssa" =                          ["compiler", "codegen"]
 "/compiler/rustc_middle/src/dep_graph" =                 ["compiler", "incremental", "query-system"]
+"/compiler/rustc_middle/src/ich" =                       ["compiler", "incremental", "query-system"]
 "/compiler/rustc_middle/src/mir" =                       ["compiler", "mir"]
 "/compiler/rustc_middle/src/traits" =                    ["compiler", "types"]
+"/compiler/rustc_middle/src/query" =                     ["compiler", "query-system"]
 "/compiler/rustc_middle/src/ty" =                        ["compiler", "types"]
 "/compiler/rustc_const_eval/src/interpret" =             ["compiler", "mir"]
 "/compiler/rustc_mir_build/src/builder" =                ["compiler", "mir"]
@@ -1567,8 +1568,6 @@ dep-bumps = [
 "/compiler/rustc_parse" =                                ["compiler", "parser"]
 "/compiler/rustc_parse/src/lexer" =                      ["compiler", "lexer"]
 "/compiler/rustc_query_impl" =                           ["compiler", "query-system"]
-"/compiler/rustc_query_system" =                         ["compiler", "query-system"]
-"/compiler/rustc_query_system/src/ich" =                 ["compiler", "incremental", "query-system"]
 "/compiler/rustc_trait_selection" =                      ["compiler", "types"]
 "/compiler/rustc_traits" =                               ["compiler", "types"]
 "/compiler/rustc_type_ir" =                              ["compiler", "types"]

From 7e14260fd1f4a9bea9d6078428cd18b58c7b93d9 Mon Sep 17 00:00:00 2001
From: lcnr 
Date: Mon, 16 Feb 2026 19:35:43 +0000
Subject: [PATCH 100/103] `probe_op` silence ambiguity errors if tainted

see the `proc-macro/quote/not-repeatable.rs` test for a case where this is useful
---
 compiler/rustc_hir_typeck/src/method/probe.rs |  1 +
 .../ui/methods/call_method_unknown_pointee.rs | 33 +++++++++++++------
 .../call_method_unknown_pointee.stderr        | 20 +++++------
 .../methods/call_method_unknown_referent.rs   | 16 +++++----
 .../call_method_unknown_referent.stderr       |  4 +--
 tests/ui/proc-macro/quote/not-repeatable.rs   |  1 -
 .../ui/proc-macro/quote/not-repeatable.stderr | 11 ++-----
 tests/ui/typeck/issue-13853.rs                |  2 +-
 tests/ui/typeck/issue-13853.stderr            | 12 ++-----
 9 files changed, 52 insertions(+), 48 deletions(-)

diff --git a/compiler/rustc_hir_typeck/src/method/probe.rs b/compiler/rustc_hir_typeck/src/method/probe.rs
index 2a92a4b48edcc..e9802a99c1c2e 100644
--- a/compiler/rustc_hir_typeck/src/method/probe.rs
+++ b/compiler/rustc_hir_typeck/src/method/probe.rs
@@ -490,6 +490,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                     .unwrap_or_else(|_| span_bug!(span, "instantiating {:?} failed?", ty));
                 let ty = self.resolve_vars_if_possible(ty.value);
                 let guar = match *ty.kind() {
+                    _ if let Some(guar) = self.tainted_by_errors() => guar,
                     ty::Infer(ty::TyVar(_)) => {
                         // We want to get the variable name that the method
                         // is being called on. If it is a method call.
diff --git a/tests/ui/methods/call_method_unknown_pointee.rs b/tests/ui/methods/call_method_unknown_pointee.rs
index a144e855ae3cb..8927576239a80 100644
--- a/tests/ui/methods/call_method_unknown_pointee.rs
+++ b/tests/ui/methods/call_method_unknown_pointee.rs
@@ -3,26 +3,39 @@
 // tests that the pointee type of a raw pointer must be known to call methods on it
 // see also: `tests/ui/editions/edition-raw-pointer-method-2018.rs`
 
-fn main() {
-    let val = 1_u32;
-    let ptr = &val as *const u32;
+fn a() {
+    let ptr = &1u32 as *const u32;
     unsafe {
         let _a: i32 = (ptr as *const _).read();
         //~^ ERROR type annotations needed
+    }
+}
+
+fn b() {
+    let ptr = &1u32 as *const u32;
+    unsafe {
         let b = ptr as *const _;
         //~^ ERROR type annotations needed
         let _b: u8 = b.read();
-        let _c = (ptr as *const u8).read(); // we know the type here
     }
+}
+
 
-    let mut val = 2_u32;
-    let ptr = &mut val as *mut u32;
+fn c() {
+    let ptr = &mut 2u32 as *mut u32;
     unsafe {
-        let _a: i32 = (ptr as *mut _).read();
+        let _c: i32 = (ptr as *mut _).read();
         //~^ ERROR type annotations needed
-        let b = ptr as *mut _;
+    }
+}
+
+fn d() {
+    let ptr = &mut 2u32 as *mut u32;
+    unsafe {
+        let d = ptr as *mut _;
         //~^ ERROR type annotations needed
-        b.write(10);
-        (ptr as *mut i32).write(1000); // we know the type here
+        let _d: u8 = d.read();
     }
 }
+
+fn main() {}
diff --git a/tests/ui/methods/call_method_unknown_pointee.stderr b/tests/ui/methods/call_method_unknown_pointee.stderr
index e20c6f8e8a17c..c123533b51bc1 100644
--- a/tests/ui/methods/call_method_unknown_pointee.stderr
+++ b/tests/ui/methods/call_method_unknown_pointee.stderr
@@ -1,5 +1,5 @@
 error[E0282]: type annotations needed
-  --> $DIR/call_method_unknown_pointee.rs:10:23
+  --> $DIR/call_method_unknown_pointee.rs:9:23
    |
 LL |         let _a: i32 = (ptr as *const _).read();
    |                       ^^^^^^^^^^^^^^^^^ ---- cannot call a method on a raw pointer with an unknown pointee type
@@ -7,7 +7,7 @@ LL |         let _a: i32 = (ptr as *const _).read();
    |                       cannot infer type
 
 error[E0282]: type annotations needed for `*const _`
-  --> $DIR/call_method_unknown_pointee.rs:12:13
+  --> $DIR/call_method_unknown_pointee.rs:17:13
    |
 LL |         let b = ptr as *const _;
    |             ^
@@ -21,25 +21,25 @@ LL |         let b: *const _ = ptr as *const _;
    |              ++++++++++
 
 error[E0282]: type annotations needed
-  --> $DIR/call_method_unknown_pointee.rs:21:23
+  --> $DIR/call_method_unknown_pointee.rs:27:23
    |
-LL |         let _a: i32 = (ptr as *mut _).read();
+LL |         let _c: i32 = (ptr as *mut _).read();
    |                       ^^^^^^^^^^^^^^^ ---- cannot call a method on a raw pointer with an unknown pointee type
    |                       |
    |                       cannot infer type
 
 error[E0282]: type annotations needed for `*mut _`
-  --> $DIR/call_method_unknown_pointee.rs:23:13
+  --> $DIR/call_method_unknown_pointee.rs:35:13
    |
-LL |         let b = ptr as *mut _;
+LL |         let d = ptr as *mut _;
    |             ^
 LL |
-LL |         b.write(10);
-   |           ----- cannot call a method on a raw pointer with an unknown pointee type
+LL |         let _d: u8 = d.read();
+   |                        ---- cannot call a method on a raw pointer with an unknown pointee type
    |
-help: consider giving `b` an explicit type, where the placeholders `_` are specified
+help: consider giving `d` an explicit type, where the placeholders `_` are specified
    |
-LL |         let b: *mut _ = ptr as *mut _;
+LL |         let d: *mut _ = ptr as *mut _;
    |              ++++++++
 
 error: aborting due to 4 previous errors
diff --git a/tests/ui/methods/call_method_unknown_referent.rs b/tests/ui/methods/call_method_unknown_referent.rs
index b26ecc74175b6..54b8653a2109d 100644
--- a/tests/ui/methods/call_method_unknown_referent.rs
+++ b/tests/ui/methods/call_method_unknown_referent.rs
@@ -14,20 +14,22 @@ impl SmartPtr {
     fn foo(&self) {}
 }
 
-fn main() {
-    let val = 1_u32;
-    let ptr = &val;
+fn a() {
+    let ptr = &1u32;
     let _a: i32 = (ptr as &_).read();
     //~^ ERROR type annotations needed
+}
 
+fn b() {
     // Same again, but with a smart pointer type
-    let val2 = 1_u32;
-    let rc = std::rc::Rc::new(val2);
+    let rc = std::rc::Rc::new(1u32);
     let _b = (rc as std::rc::Rc<_>).read();
     //~^ ERROR type annotations needed
+}
 
+fn c() {
     // Same again, but with a smart pointer type
-    let ptr = SmartPtr(val);
+    let ptr = SmartPtr(1u32);
 
     // We can call unambiguous outer-type methods on this
     (ptr as SmartPtr<_>).foo();
@@ -46,3 +48,5 @@ fn main() {
     let _c = (ptr as SmartPtr<_>).read();
     //~^ ERROR no method named `read` found for struct `SmartPtr`
 }
+
+fn main() {}
diff --git a/tests/ui/methods/call_method_unknown_referent.stderr b/tests/ui/methods/call_method_unknown_referent.stderr
index 35c7d9caf3efa..92fb32b987dfc 100644
--- a/tests/ui/methods/call_method_unknown_referent.stderr
+++ b/tests/ui/methods/call_method_unknown_referent.stderr
@@ -1,5 +1,5 @@
 error[E0282]: type annotations needed
-  --> $DIR/call_method_unknown_referent.rs:20:19
+  --> $DIR/call_method_unknown_referent.rs:19:19
    |
 LL |     let _a: i32 = (ptr as &_).read();
    |                   ^^^^^^^^^^^ cannot infer type
@@ -11,7 +11,7 @@ LL |     let _b = (rc as std::rc::Rc<_>).read();
    |              ^^^^^^^^^^^^^^^^^^^^^^ cannot infer type
 
 error[E0599]: no method named `read` found for struct `SmartPtr` in the current scope
-  --> $DIR/call_method_unknown_referent.rs:46:35
+  --> $DIR/call_method_unknown_referent.rs:48:35
    |
 LL | struct SmartPtr(T);
    | ------------------ method `read` not found for this struct
diff --git a/tests/ui/proc-macro/quote/not-repeatable.rs b/tests/ui/proc-macro/quote/not-repeatable.rs
index 373f0e74dbdac..55ba1669f1b1f 100644
--- a/tests/ui/proc-macro/quote/not-repeatable.rs
+++ b/tests/ui/proc-macro/quote/not-repeatable.rs
@@ -10,5 +10,4 @@ fn main() {
     let ip = Ipv4Addr;
     let _ = quote! { $($ip)* };
     //~^ ERROR the method `quote_into_iter` exists for struct `Ipv4Addr`, but its trait bounds were not satisfied
-    //~| ERROR type annotations needed
 }
diff --git a/tests/ui/proc-macro/quote/not-repeatable.stderr b/tests/ui/proc-macro/quote/not-repeatable.stderr
index 6a867350a3b36..611da37f3a1f9 100644
--- a/tests/ui/proc-macro/quote/not-repeatable.stderr
+++ b/tests/ui/proc-macro/quote/not-repeatable.stderr
@@ -20,13 +20,6 @@ note: the traits `Iterator` and `ToTokens` must be implemented
   --> $SRC_DIR/proc_macro/src/to_tokens.rs:LL:COL
   --> $SRC_DIR/core/src/iter/traits/iterator.rs:LL:COL
 
-error[E0282]: type annotations needed
-  --> $DIR/not-repeatable.rs:11:25
-   |
-LL |     let _ = quote! { $($ip)* };
-   |                         ^^ cannot infer type
-
-error: aborting due to 2 previous errors
+error: aborting due to 1 previous error
 
-Some errors have detailed explanations: E0282, E0599.
-For more information about an error, try `rustc --explain E0282`.
+For more information about this error, try `rustc --explain E0599`.
diff --git a/tests/ui/typeck/issue-13853.rs b/tests/ui/typeck/issue-13853.rs
index ed44d5062614f..ac9886d2e7249 100644
--- a/tests/ui/typeck/issue-13853.rs
+++ b/tests/ui/typeck/issue-13853.rs
@@ -25,7 +25,7 @@ impl Node for Stuff {
 
 fn iterate>(graph: &G) {
     for node in graph.iter() { //~ ERROR no method named `iter` found
-        node.zomg(); //~ ERROR type annotations needed
+        node.zomg();
     }
 }
 
diff --git a/tests/ui/typeck/issue-13853.stderr b/tests/ui/typeck/issue-13853.stderr
index 4a39b404770d0..45363c87d29df 100644
--- a/tests/ui/typeck/issue-13853.stderr
+++ b/tests/ui/typeck/issue-13853.stderr
@@ -17,12 +17,6 @@ error[E0599]: no method named `iter` found for reference `&G` in the current sco
 LL |     for node in graph.iter() {
    |                       ^^^^ method not found in `&G`
 
-error[E0282]: type annotations needed
-  --> $DIR/issue-13853.rs:28:9
-   |
-LL |         node.zomg();
-   |         ^^^^ cannot infer type
-
 error[E0308]: mismatched types
   --> $DIR/issue-13853.rs:37:13
    |
@@ -43,7 +37,7 @@ help: consider borrowing here
 LL |     iterate(&graph);
    |             +
 
-error: aborting due to 4 previous errors
+error: aborting due to 3 previous errors
 
-Some errors have detailed explanations: E0282, E0308, E0599.
-For more information about an error, try `rustc --explain E0282`.
+Some errors have detailed explanations: E0308, E0599.
+For more information about an error, try `rustc --explain E0308`.

From 8081e86c94119a99c8ed2462edee58f412e661b9 Mon Sep 17 00:00:00 2001
From: Oscar Bray 
Date: Mon, 16 Feb 2026 19:42:12 +0000
Subject: [PATCH 101/103] Port #![default_lib_allocator] to the new attribute
 parser

---
 .../rustc_attr_parsing/src/attributes/crate_level.rs     | 9 +++++++++
 compiler/rustc_attr_parsing/src/context.rs               | 1 +
 compiler/rustc_hir/src/attrs/data_structures.rs          | 3 +++
 compiler/rustc_hir/src/attrs/encode_cross_crate.rs       | 1 +
 compiler/rustc_metadata/src/rmeta/encoder.rs             | 5 +----
 compiler/rustc_passes/src/check_attr.rs                  | 2 +-
 6 files changed, 16 insertions(+), 5 deletions(-)

diff --git a/compiler/rustc_attr_parsing/src/attributes/crate_level.rs b/compiler/rustc_attr_parsing/src/attributes/crate_level.rs
index bdfe7bfb8f1fb..3b3bbf4f0c8af 100644
--- a/compiler/rustc_attr_parsing/src/attributes/crate_level.rs
+++ b/compiler/rustc_attr_parsing/src/attributes/crate_level.rs
@@ -292,3 +292,12 @@ impl NoArgsAttributeParser for RustcNoImplicitBoundsParser {
     const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Crate)]);
     const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcNoImplicitBounds;
 }
+
+pub(crate) struct DefaultLibAllocatorParser;
+
+impl NoArgsAttributeParser for DefaultLibAllocatorParser {
+    const PATH: &[Symbol] = &[sym::default_lib_allocator];
+    const ON_DUPLICATE: OnDuplicate = OnDuplicate::Warn;
+    const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Crate)]);
+    const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::DefaultLibAllocator;
+}
diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs
index 1bb6c8aa1866c..a0c672778ac12 100644
--- a/compiler/rustc_attr_parsing/src/context.rs
+++ b/compiler/rustc_attr_parsing/src/context.rs
@@ -235,6 +235,7 @@ attribute_parsers!(
         Single>,
         Single>,
         Single>,
+        Single>,
         Single>,
         Single>,
         Single>,
diff --git a/compiler/rustc_hir/src/attrs/data_structures.rs b/compiler/rustc_hir/src/attrs/data_structures.rs
index 41970fb7d35a1..b8f3c898ed07f 100644
--- a/compiler/rustc_hir/src/attrs/data_structures.rs
+++ b/compiler/rustc_hir/src/attrs/data_structures.rs
@@ -897,6 +897,9 @@ pub enum AttributeKind {
     /// Represents `#[debugger_visualizer]`.
     DebuggerVisualizer(ThinVec),
 
+    /// Represents `#![default_lib_allocator]`
+    DefaultLibAllocator,
+
     /// Represents [`#[deprecated]`](https://doc.rust-lang.org/stable/reference/attributes/diagnostics.html#the-deprecated-attribute).
     Deprecation { deprecation: Deprecation, span: Span },
 
diff --git a/compiler/rustc_hir/src/attrs/encode_cross_crate.rs b/compiler/rustc_hir/src/attrs/encode_cross_crate.rs
index c50a5c649ee4b..86eafb984121d 100644
--- a/compiler/rustc_hir/src/attrs/encode_cross_crate.rs
+++ b/compiler/rustc_hir/src/attrs/encode_cross_crate.rs
@@ -35,6 +35,7 @@ impl AttributeKind {
             CrateType(_) => No,
             CustomMir(_, _, _) => Yes,
             DebuggerVisualizer(..) => No,
+            DefaultLibAllocator => No,
             Deprecation { .. } => Yes,
             DoNotRecommend { .. } => Yes,
             Doc(_) => Yes,
diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs
index 3a85b0a050526..a3d8b07fb1d9a 100644
--- a/compiler/rustc_metadata/src/rmeta/encoder.rs
+++ b/compiler/rustc_metadata/src/rmeta/encoder.rs
@@ -734,10 +734,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
                 has_global_allocator: tcx.has_global_allocator(LOCAL_CRATE),
                 has_alloc_error_handler: tcx.has_alloc_error_handler(LOCAL_CRATE),
                 has_panic_handler: tcx.has_panic_handler(LOCAL_CRATE),
-                has_default_lib_allocator: ast::attr::contains_name(
-                    attrs,
-                    sym::default_lib_allocator,
-                ),
+                has_default_lib_allocator: find_attr!(attrs, AttributeKind::DefaultLibAllocator),
                 externally_implementable_items,
                 proc_macro_data,
                 debugger_visualizers,
diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs
index ec0306371205b..11c9ee9d61aee 100644
--- a/compiler/rustc_passes/src/check_attr.rs
+++ b/compiler/rustc_passes/src/check_attr.rs
@@ -246,6 +246,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
                     | AttributeKind::CrateName { .. }
                     | AttributeKind::CrateType(..)
                     | AttributeKind::DebuggerVisualizer(..)
+                    | AttributeKind::DefaultLibAllocator
                     // `#[doc]` is actually a lot more than just doc comments, so is checked below
                     | AttributeKind::DocComment {..}
                     | AttributeKind::EiiDeclaration { .. }
@@ -399,7 +400,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
                             | sym::deny
                             | sym::forbid
                             // internal
-                            | sym::default_lib_allocator
                             | sym::rustc_inherit_overflow_checks
                             | sym::rustc_on_unimplemented
                             | sym::rustc_doc_primitive

From 67799c97393c82a6fd378e460cfd3996fc461020 Mon Sep 17 00:00:00 2001
From: okaneco <47607823+okaneco@users.noreply.github.com>
Date: Wed, 11 Feb 2026 21:27:51 -0500
Subject: [PATCH 102/103] core: Implement feature
 `float_exact_integer_constants`

Implement accepted ACP for `MAX_EXACT_INTEGER` and `MIN_EXACT_INTEGER`
on `f16`, `f32`, `f64`, and `f128`
Add tests to `coretests/tests/floats/mod.rs`
Disable doc tests for i586 since float<->int casts return incorrect
results
---
 library/core/src/num/f128.rs          | 64 ++++++++++++++++++
 library/core/src/num/f16.rs           | 64 ++++++++++++++++++
 library/core/src/num/f32.rs           | 58 +++++++++++++++++
 library/core/src/num/f64.rs           | 58 +++++++++++++++++
 library/coretests/tests/floats/mod.rs | 93 +++++++++++++++++++++++++++
 library/coretests/tests/lib.rs        |  1 +
 6 files changed, 338 insertions(+)

diff --git a/library/core/src/num/f128.rs b/library/core/src/num/f128.rs
index d114b821655bf..03bc5f20d7e94 100644
--- a/library/core/src/num/f128.rs
+++ b/library/core/src/num/f128.rs
@@ -275,6 +275,70 @@ impl f128 {
     #[unstable(feature = "f128", issue = "116909")]
     pub const NEG_INFINITY: f128 = -1.0_f128 / 0.0_f128;
 
+    /// Maximum integer that can be represented exactly in an [`f128`] value,
+    /// with no other integer converting to the same floating point value.
+    ///
+    /// For an integer `x` which satisfies `MIN_EXACT_INTEGER <= x <= MAX_EXACT_INTEGER`,
+    /// there is a "one-to-one" mapping between [`i128`] and [`f128`] values.
+    /// `MAX_EXACT_INTEGER + 1` also converts losslessly to [`f128`] and back to
+    /// [`i128`], but `MAX_EXACT_INTEGER + 2` converts to the same [`f128`] value
+    /// (and back to `MAX_EXACT_INTEGER + 1` as an integer) so there is not a
+    /// "one-to-one" mapping.
+    ///
+    /// [`MAX_EXACT_INTEGER`]: f128::MAX_EXACT_INTEGER
+    /// [`MIN_EXACT_INTEGER`]: f128::MIN_EXACT_INTEGER
+    /// ```
+    /// #![feature(f128)]
+    /// #![feature(float_exact_integer_constants)]
+    /// # // FIXME(#152635): Float rounding on `i586` does not adhere to IEEE 754
+    /// # #[cfg(not(all(target_arch = "x86", not(target_feature = "sse"))))] {
+    /// # #[cfg(target_has_reliable_f128)] {
+    /// let max_exact_int = f128::MAX_EXACT_INTEGER;
+    /// assert_eq!(max_exact_int, max_exact_int as f128 as i128);
+    /// assert_eq!(max_exact_int + 1, (max_exact_int + 1) as f128 as i128);
+    /// assert_ne!(max_exact_int + 2, (max_exact_int + 2) as f128 as i128);
+    ///
+    /// // Beyond `f128::MAX_EXACT_INTEGER`, multiple integers can map to one float value
+    /// assert_eq!((max_exact_int + 1) as f128, (max_exact_int + 2) as f128);
+    /// # }}
+    /// ```
+    // #[unstable(feature = "f128", issue = "116909")]
+    #[unstable(feature = "float_exact_integer_constants", issue = "152466")]
+    pub const MAX_EXACT_INTEGER: i128 = (1 << Self::MANTISSA_DIGITS) - 1;
+
+    /// Minimum integer that can be represented exactly in an [`f128`] value,
+    /// with no other integer converting to the same floating point value.
+    ///
+    /// For an integer `x` which satisfies `MIN_EXACT_INTEGER <= x <= MAX_EXACT_INTEGER`,
+    /// there is a "one-to-one" mapping between [`i128`] and [`f128`] values.
+    /// `MAX_EXACT_INTEGER + 1` also converts losslessly to [`f128`] and back to
+    /// [`i128`], but `MAX_EXACT_INTEGER + 2` converts to the same [`f128`] value
+    /// (and back to `MAX_EXACT_INTEGER + 1` as an integer) so there is not a
+    /// "one-to-one" mapping.
+    ///
+    /// This constant is equivalent to `-MAX_EXACT_INTEGER`.
+    ///
+    /// [`MAX_EXACT_INTEGER`]: f128::MAX_EXACT_INTEGER
+    /// [`MIN_EXACT_INTEGER`]: f128::MIN_EXACT_INTEGER
+    /// ```
+    /// #![feature(f128)]
+    /// #![feature(float_exact_integer_constants)]
+    /// # // FIXME(#152635): Float rounding on `i586` does not adhere to IEEE 754
+    /// # #[cfg(not(all(target_arch = "x86", not(target_feature = "sse"))))] {
+    /// # #[cfg(target_has_reliable_f128)] {
+    /// let min_exact_int = f128::MIN_EXACT_INTEGER;
+    /// assert_eq!(min_exact_int, min_exact_int as f128 as i128);
+    /// assert_eq!(min_exact_int - 1, (min_exact_int - 1) as f128 as i128);
+    /// assert_ne!(min_exact_int - 2, (min_exact_int - 2) as f128 as i128);
+    ///
+    /// // Below `f128::MIN_EXACT_INTEGER`, multiple integers can map to one float value
+    /// assert_eq!((min_exact_int - 1) as f128, (min_exact_int - 2) as f128);
+    /// # }}
+    /// ```
+    // #[unstable(feature = "f128", issue = "116909")]
+    #[unstable(feature = "float_exact_integer_constants", issue = "152466")]
+    pub const MIN_EXACT_INTEGER: i128 = -Self::MAX_EXACT_INTEGER;
+
     /// Sign bit
     pub(crate) const SIGN_MASK: u128 = 0x8000_0000_0000_0000_0000_0000_0000_0000;
 
diff --git a/library/core/src/num/f16.rs b/library/core/src/num/f16.rs
index 373225c5806c1..ef937fccb47f3 100644
--- a/library/core/src/num/f16.rs
+++ b/library/core/src/num/f16.rs
@@ -269,6 +269,70 @@ impl f16 {
     #[unstable(feature = "f16", issue = "116909")]
     pub const NEG_INFINITY: f16 = -1.0_f16 / 0.0_f16;
 
+    /// Maximum integer that can be represented exactly in an [`f16`] value,
+    /// with no other integer converting to the same floating point value.
+    ///
+    /// For an integer `x` which satisfies `MIN_EXACT_INTEGER <= x <= MAX_EXACT_INTEGER`,
+    /// there is a "one-to-one" mapping between [`i16`] and [`f16`] values.
+    /// `MAX_EXACT_INTEGER + 1` also converts losslessly to [`f16`] and back to
+    /// [`i16`], but `MAX_EXACT_INTEGER + 2` converts to the same [`f16`] value
+    /// (and back to `MAX_EXACT_INTEGER + 1` as an integer) so there is not a
+    /// "one-to-one" mapping.
+    ///
+    /// [`MAX_EXACT_INTEGER`]: f16::MAX_EXACT_INTEGER
+    /// [`MIN_EXACT_INTEGER`]: f16::MIN_EXACT_INTEGER
+    /// ```
+    /// #![feature(f16)]
+    /// #![feature(float_exact_integer_constants)]
+    /// # // FIXME(#152635): Float rounding on `i586` does not adhere to IEEE 754
+    /// # #[cfg(not(all(target_arch = "x86", not(target_feature = "sse"))))] {
+    /// # #[cfg(target_has_reliable_f16)] {
+    /// let max_exact_int = f16::MAX_EXACT_INTEGER;
+    /// assert_eq!(max_exact_int, max_exact_int as f16 as i16);
+    /// assert_eq!(max_exact_int + 1, (max_exact_int + 1) as f16 as i16);
+    /// assert_ne!(max_exact_int + 2, (max_exact_int + 2) as f16 as i16);
+    ///
+    /// // Beyond `f16::MAX_EXACT_INTEGER`, multiple integers can map to one float value
+    /// assert_eq!((max_exact_int + 1) as f16, (max_exact_int + 2) as f16);
+    /// # }}
+    /// ```
+    // #[unstable(feature = "f16", issue = "116909")]
+    #[unstable(feature = "float_exact_integer_constants", issue = "152466")]
+    pub const MAX_EXACT_INTEGER: i16 = (1 << Self::MANTISSA_DIGITS) - 1;
+
+    /// Minimum integer that can be represented exactly in an [`f16`] value,
+    /// with no other integer converting to the same floating point value.
+    ///
+    /// For an integer `x` which satisfies `MIN_EXACT_INTEGER <= x <= MAX_EXACT_INTEGER`,
+    /// there is a "one-to-one" mapping between [`i16`] and [`f16`] values.
+    /// `MAX_EXACT_INTEGER + 1` also converts losslessly to [`f16`] and back to
+    /// [`i16`], but `MAX_EXACT_INTEGER + 2` converts to the same [`f16`] value
+    /// (and back to `MAX_EXACT_INTEGER + 1` as an integer) so there is not a
+    /// "one-to-one" mapping.
+    ///
+    /// This constant is equivalent to `-MAX_EXACT_INTEGER`.
+    ///
+    /// [`MAX_EXACT_INTEGER`]: f16::MAX_EXACT_INTEGER
+    /// [`MIN_EXACT_INTEGER`]: f16::MIN_EXACT_INTEGER
+    /// ```
+    /// #![feature(f16)]
+    /// #![feature(float_exact_integer_constants)]
+    /// # // FIXME(#152635): Float rounding on `i586` does not adhere to IEEE 754
+    /// # #[cfg(not(all(target_arch = "x86", not(target_feature = "sse"))))] {
+    /// # #[cfg(target_has_reliable_f16)] {
+    /// let min_exact_int = f16::MIN_EXACT_INTEGER;
+    /// assert_eq!(min_exact_int, min_exact_int as f16 as i16);
+    /// assert_eq!(min_exact_int - 1, (min_exact_int - 1) as f16 as i16);
+    /// assert_ne!(min_exact_int - 2, (min_exact_int - 2) as f16 as i16);
+    ///
+    /// // Below `f16::MIN_EXACT_INTEGER`, multiple integers can map to one float value
+    /// assert_eq!((min_exact_int - 1) as f16, (min_exact_int - 2) as f16);
+    /// # }}
+    /// ```
+    // #[unstable(feature = "f16", issue = "116909")]
+    #[unstable(feature = "float_exact_integer_constants", issue = "152466")]
+    pub const MIN_EXACT_INTEGER: i16 = -Self::MAX_EXACT_INTEGER;
+
     /// Sign bit
     pub(crate) const SIGN_MASK: u16 = 0x8000;
 
diff --git a/library/core/src/num/f32.rs b/library/core/src/num/f32.rs
index f3c7961931a1d..aac81d48c1b45 100644
--- a/library/core/src/num/f32.rs
+++ b/library/core/src/num/f32.rs
@@ -513,6 +513,64 @@ impl f32 {
     #[stable(feature = "assoc_int_consts", since = "1.43.0")]
     pub const NEG_INFINITY: f32 = -1.0_f32 / 0.0_f32;
 
+    /// Maximum integer that can be represented exactly in an [`f32`] value,
+    /// with no other integer converting to the same floating point value.
+    ///
+    /// For an integer `x` which satisfies `MIN_EXACT_INTEGER <= x <= MAX_EXACT_INTEGER`,
+    /// there is a "one-to-one" mapping between [`i32`] and [`f32`] values.
+    /// `MAX_EXACT_INTEGER + 1` also converts losslessly to [`f32`] and back to
+    /// [`i32`], but `MAX_EXACT_INTEGER + 2` converts to the same [`f32`] value
+    /// (and back to `MAX_EXACT_INTEGER + 1` as an integer) so there is not a
+    /// "one-to-one" mapping.
+    ///
+    /// [`MAX_EXACT_INTEGER`]: f32::MAX_EXACT_INTEGER
+    /// [`MIN_EXACT_INTEGER`]: f32::MIN_EXACT_INTEGER
+    /// ```
+    /// #![feature(float_exact_integer_constants)]
+    /// # // FIXME(#152635): Float rounding on `i586` does not adhere to IEEE 754
+    /// # #[cfg(not(all(target_arch = "x86", not(target_feature = "sse"))))] {
+    /// let max_exact_int = f32::MAX_EXACT_INTEGER;
+    /// assert_eq!(max_exact_int, max_exact_int as f32 as i32);
+    /// assert_eq!(max_exact_int + 1, (max_exact_int + 1) as f32 as i32);
+    /// assert_ne!(max_exact_int + 2, (max_exact_int + 2) as f32 as i32);
+    ///
+    /// // Beyond `f32::MAX_EXACT_INTEGER`, multiple integers can map to one float value
+    /// assert_eq!((max_exact_int + 1) as f32, (max_exact_int + 2) as f32);
+    /// # }
+    /// ```
+    #[unstable(feature = "float_exact_integer_constants", issue = "152466")]
+    pub const MAX_EXACT_INTEGER: i32 = (1 << Self::MANTISSA_DIGITS) - 1;
+
+    /// Minimum integer that can be represented exactly in an [`f32`] value,
+    /// with no other integer converting to the same floating point value.
+    ///
+    /// For an integer `x` which satisfies `MIN_EXACT_INTEGER <= x <= MAX_EXACT_INTEGER`,
+    /// there is a "one-to-one" mapping between [`i32`] and [`f32`] values.
+    /// `MAX_EXACT_INTEGER + 1` also converts losslessly to [`f32`] and back to
+    /// [`i32`], but `MAX_EXACT_INTEGER + 2` converts to the same [`f32`] value
+    /// (and back to `MAX_EXACT_INTEGER + 1` as an integer) so there is not a
+    /// "one-to-one" mapping.
+    ///
+    /// This constant is equivalent to `-MAX_EXACT_INTEGER`.
+    ///
+    /// [`MAX_EXACT_INTEGER`]: f32::MAX_EXACT_INTEGER
+    /// [`MIN_EXACT_INTEGER`]: f32::MIN_EXACT_INTEGER
+    /// ```
+    /// #![feature(float_exact_integer_constants)]
+    /// # // FIXME(#152635): Float rounding on `i586` does not adhere to IEEE 754
+    /// # #[cfg(not(all(target_arch = "x86", not(target_feature = "sse"))))] {
+    /// let min_exact_int = f32::MIN_EXACT_INTEGER;
+    /// assert_eq!(min_exact_int, min_exact_int as f32 as i32);
+    /// assert_eq!(min_exact_int - 1, (min_exact_int - 1) as f32 as i32);
+    /// assert_ne!(min_exact_int - 2, (min_exact_int - 2) as f32 as i32);
+    ///
+    /// // Below `f32::MIN_EXACT_INTEGER`, multiple integers can map to one float value
+    /// assert_eq!((min_exact_int - 1) as f32, (min_exact_int - 2) as f32);
+    /// # }
+    /// ```
+    #[unstable(feature = "float_exact_integer_constants", issue = "152466")]
+    pub const MIN_EXACT_INTEGER: i32 = -Self::MAX_EXACT_INTEGER;
+
     /// Sign bit
     pub(crate) const SIGN_MASK: u32 = 0x8000_0000;
 
diff --git a/library/core/src/num/f64.rs b/library/core/src/num/f64.rs
index a6fd3b1cb5d07..bacf429e77fab 100644
--- a/library/core/src/num/f64.rs
+++ b/library/core/src/num/f64.rs
@@ -512,6 +512,64 @@ impl f64 {
     #[stable(feature = "assoc_int_consts", since = "1.43.0")]
     pub const NEG_INFINITY: f64 = -1.0_f64 / 0.0_f64;
 
+    /// Maximum integer that can be represented exactly in an [`f64`] value,
+    /// with no other integer converting to the same floating point value.
+    ///
+    /// For an integer `x` which satisfies `MIN_EXACT_INTEGER <= x <= MAX_EXACT_INTEGER`,
+    /// there is a "one-to-one" mapping between [`i64`] and [`f64`] values.
+    /// `MAX_EXACT_INTEGER + 1` also converts losslessly to [`f64`] and back to
+    /// [`i64`], but `MAX_EXACT_INTEGER + 2` converts to the same [`f64`] value
+    /// (and back to `MAX_EXACT_INTEGER + 1` as an integer) so there is not a
+    /// "one-to-one" mapping.
+    ///
+    /// [`MAX_EXACT_INTEGER`]: f64::MAX_EXACT_INTEGER
+    /// [`MIN_EXACT_INTEGER`]: f64::MIN_EXACT_INTEGER
+    /// ```
+    /// #![feature(float_exact_integer_constants)]
+    /// # // FIXME(#152635): Float rounding on `i586` does not adhere to IEEE 754
+    /// # #[cfg(not(all(target_arch = "x86", not(target_feature = "sse"))))] {
+    /// let max_exact_int = f64::MAX_EXACT_INTEGER;
+    /// assert_eq!(max_exact_int, max_exact_int as f64 as i64);
+    /// assert_eq!(max_exact_int + 1, (max_exact_int + 1) as f64 as i64);
+    /// assert_ne!(max_exact_int + 2, (max_exact_int + 2) as f64 as i64);
+    ///
+    /// // Beyond `f64::MAX_EXACT_INTEGER`, multiple integers can map to one float value
+    /// assert_eq!((max_exact_int + 1) as f64, (max_exact_int + 2) as f64);
+    /// # }
+    /// ```
+    #[unstable(feature = "float_exact_integer_constants", issue = "152466")]
+    pub const MAX_EXACT_INTEGER: i64 = (1 << Self::MANTISSA_DIGITS) - 1;
+
+    /// Minimum integer that can be represented exactly in an [`f64`] value,
+    /// with no other integer converting to the same floating point value.
+    ///
+    /// For an integer `x` which satisfies `MIN_EXACT_INTEGER <= x <= MAX_EXACT_INTEGER`,
+    /// there is a "one-to-one" mapping between [`i64`] and [`f64`] values.
+    /// `MAX_EXACT_INTEGER + 1` also converts losslessly to [`f64`] and back to
+    /// [`i64`], but `MAX_EXACT_INTEGER + 2` converts to the same [`f64`] value
+    /// (and back to `MAX_EXACT_INTEGER + 1` as an integer) so there is not a
+    /// "one-to-one" mapping.
+    ///
+    /// This constant is equivalent to `-MAX_EXACT_INTEGER`.
+    ///
+    /// [`MAX_EXACT_INTEGER`]: f64::MAX_EXACT_INTEGER
+    /// [`MIN_EXACT_INTEGER`]: f64::MIN_EXACT_INTEGER
+    /// ```
+    /// #![feature(float_exact_integer_constants)]
+    /// # // FIXME(#152635): Float rounding on `i586` does not adhere to IEEE 754
+    /// # #[cfg(not(all(target_arch = "x86", not(target_feature = "sse"))))] {
+    /// let min_exact_int = f64::MIN_EXACT_INTEGER;
+    /// assert_eq!(min_exact_int, min_exact_int as f64 as i64);
+    /// assert_eq!(min_exact_int - 1, (min_exact_int - 1) as f64 as i64);
+    /// assert_ne!(min_exact_int - 2, (min_exact_int - 2) as f64 as i64);
+    ///
+    /// // Below `f64::MIN_EXACT_INTEGER`, multiple integers can map to one float value
+    /// assert_eq!((min_exact_int - 1) as f64, (min_exact_int - 2) as f64);
+    /// # }
+    /// ```
+    #[unstable(feature = "float_exact_integer_constants", issue = "152466")]
+    pub const MIN_EXACT_INTEGER: i64 = -Self::MAX_EXACT_INTEGER;
+
     /// Sign bit
     pub(crate) const SIGN_MASK: u64 = 0x8000_0000_0000_0000;
 
diff --git a/library/coretests/tests/floats/mod.rs b/library/coretests/tests/floats/mod.rs
index c61961f8584e7..b729cdf8458d7 100644
--- a/library/coretests/tests/floats/mod.rs
+++ b/library/coretests/tests/floats/mod.rs
@@ -5,6 +5,8 @@ trait TestableFloat: Sized {
     const BITS: u32;
     /// Unsigned int with the same size, for converting to/from bits.
     type Int;
+    /// Signed int with the same size.
+    type SInt;
     /// Set the default tolerance for float comparison based on the type.
     const APPROX: Self;
     /// Allow looser tolerance for f32 on miri
@@ -61,6 +63,7 @@ trait TestableFloat: Sized {
 impl TestableFloat for f16 {
     const BITS: u32 = 16;
     type Int = u16;
+    type SInt = i16;
     const APPROX: Self = 1e-3;
     const POWF_APPROX: Self = 5e-1;
     const _180_TO_RADIANS_APPROX: Self = 1e-2;
@@ -101,6 +104,7 @@ impl TestableFloat for f16 {
 impl TestableFloat for f32 {
     const BITS: u32 = 32;
     type Int = u32;
+    type SInt = i32;
     const APPROX: Self = 1e-6;
     /// Miri adds some extra errors to float functions; make sure the tests still pass.
     /// These values are purely used as a canary to test against and are thus not a stable guarantee Rust provides.
@@ -143,6 +147,7 @@ impl TestableFloat for f32 {
 impl TestableFloat for f64 {
     const BITS: u32 = 64;
     type Int = u64;
+    type SInt = i64;
     const APPROX: Self = 1e-6;
     const GAMMA_APPROX_LOOSE: Self = 1e-4;
     const LNGAMMA_APPROX_LOOSE: Self = 1e-4;
@@ -170,6 +175,7 @@ impl TestableFloat for f64 {
 impl TestableFloat for f128 {
     const BITS: u32 = 128;
     type Int = u128;
+    type SInt = i128;
     const APPROX: Self = 1e-9;
     const EXP_APPROX: Self = 1e-12;
     const LN_APPROX: Self = 1e-12;
@@ -2003,6 +2009,93 @@ float_test! {
     }
 }
 
+// Test the `float_exact_integer_constants` feature
+float_test! {
+    name: max_exact_integer_constant,
+    attrs: {
+        f16: #[cfg(any(miri, target_has_reliable_f16))],
+        f128: #[cfg(any(miri, target_has_reliable_f128))],
+    },
+    test {
+        // The maximum integer that converts to a unique floating point
+        // value.
+        const MAX_EXACT_INTEGER: ::SInt = Float::MAX_EXACT_INTEGER;
+
+        let max_minus_one = (MAX_EXACT_INTEGER - 1) as Float as ::SInt;
+        let max_plus_one = (MAX_EXACT_INTEGER + 1) as Float as ::SInt;
+        let max_plus_two = (MAX_EXACT_INTEGER + 2) as Float as ::SInt;
+
+        // This does an extra round trip back to float for the second operand in
+        // order to print the results if there is a mismatch
+        assert_biteq!((MAX_EXACT_INTEGER - 1) as Float, max_minus_one as Float);
+        assert_biteq!(MAX_EXACT_INTEGER as Float, MAX_EXACT_INTEGER as Float as ::SInt as Float);
+        assert_biteq!((MAX_EXACT_INTEGER + 1) as Float, max_plus_one as Float);
+        // The first non-unique conversion, where `max_plus_two` roundtrips to
+        // `max_plus_one`
+        assert_biteq!((MAX_EXACT_INTEGER + 1) as Float, (MAX_EXACT_INTEGER + 2) as Float);
+        assert_biteq!((MAX_EXACT_INTEGER + 2) as Float, max_plus_one as Float);
+        assert_biteq!((MAX_EXACT_INTEGER + 2) as Float, max_plus_two as Float);
+
+        // Lossless roundtrips, for integers
+        assert!(MAX_EXACT_INTEGER - 1 == max_minus_one);
+        assert!(MAX_EXACT_INTEGER == MAX_EXACT_INTEGER as Float as ::SInt);
+        assert!(MAX_EXACT_INTEGER + 1 == max_plus_one);
+        // The first non-unique conversion, where `max_plus_two` roundtrips to
+        // one less than the starting value
+        assert!(MAX_EXACT_INTEGER + 2 != max_plus_two);
+
+        // max-1 | max+0 | max+1 | max+2
+        // After roundtripping, +1 and +2 will equal each other
+        assert!(max_minus_one != MAX_EXACT_INTEGER);
+        assert!(MAX_EXACT_INTEGER != max_plus_one);
+        assert!(max_plus_one == max_plus_two);
+    }
+}
+
+float_test! {
+    name: min_exact_integer_constant,
+    attrs: {
+        f16: #[cfg(any(miri, target_has_reliable_f16))],
+        f128: #[cfg(any(miri, target_has_reliable_f128))],
+    },
+    test {
+        // The minimum integer that converts to a unique floating point
+        // value.
+        const MIN_EXACT_INTEGER: ::SInt = Float::MIN_EXACT_INTEGER;
+
+        // Same logic as the `max` test, but we work our way leftward
+        // across the number line from (min_exact + 1) to (min_exact - 2).
+        let min_plus_one = (MIN_EXACT_INTEGER + 1) as Float as ::SInt;
+        let min_minus_one = (MIN_EXACT_INTEGER - 1) as Float as ::SInt;
+        let min_minus_two = (MIN_EXACT_INTEGER - 2) as Float as ::SInt;
+
+        // This does an extra round trip back to float for the second operand in
+        // order to print the results if there is a mismatch
+        assert_biteq!((MIN_EXACT_INTEGER + 1) as Float, min_plus_one as Float);
+        assert_biteq!(MIN_EXACT_INTEGER as Float, MIN_EXACT_INTEGER as Float as ::SInt as Float);
+        assert_biteq!((MIN_EXACT_INTEGER - 1) as Float, min_minus_one as Float);
+        // The first non-unique conversion, which roundtrips to one
+        // greater than the starting value.
+        assert_biteq!((MIN_EXACT_INTEGER - 1) as Float, (MIN_EXACT_INTEGER - 2) as Float);
+        assert_biteq!((MIN_EXACT_INTEGER - 2) as Float, min_minus_one as Float);
+        assert_biteq!((MIN_EXACT_INTEGER - 2) as Float, min_minus_two as Float);
+
+        // Lossless roundtrips, for integers
+        assert!(MIN_EXACT_INTEGER + 1 == min_plus_one);
+        assert!(MIN_EXACT_INTEGER == MIN_EXACT_INTEGER as Float as ::SInt);
+        assert!(MIN_EXACT_INTEGER - 1 == min_minus_one);
+        // The first non-unique conversion, which roundtrips to one
+        // greater than the starting value.
+        assert!(MIN_EXACT_INTEGER - 2 != min_minus_two);
+
+        // min-2 | min-1 | min | min+1
+        // After roundtripping, -2 and -1 will equal each other.
+        assert!(min_plus_one != MIN_EXACT_INTEGER);
+        assert!(MIN_EXACT_INTEGER != min_minus_one);
+        assert!(min_minus_one == min_minus_two);
+    }
+}
+
 // FIXME(f128): Uncomment and adapt these tests once the From<{u64,i64}> impls are added.
 // float_test! {
 //     name: from_u64_i64,
diff --git a/library/coretests/tests/lib.rs b/library/coretests/tests/lib.rs
index 34732741a21c0..85ee7cff68266 100644
--- a/library/coretests/tests/lib.rs
+++ b/library/coretests/tests/lib.rs
@@ -53,6 +53,7 @@
 #![feature(f128)]
 #![feature(float_algebraic)]
 #![feature(float_bits_const)]
+#![feature(float_exact_integer_constants)]
 #![feature(float_gamma)]
 #![feature(float_minimum_maximum)]
 #![feature(flt2dec)]

From 7be024fc06538914615971c2c5b2c964dde5551f Mon Sep 17 00:00:00 2001
From: okaneco <47607823+okaneco@users.noreply.github.com>
Date: Thu, 12 Feb 2026 15:38:13 -0500
Subject: [PATCH 103/103] [cg_clif]: Fix codegen of f128 to i128 casts

Correct name for intrinsic that converts f128 to u128
Use `to_signed` instead of `from_signed` to ensure proper intrinsic
selected for u128/i128
---
 compiler/rustc_codegen_cranelift/src/codegen_f16_f128.rs | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/compiler/rustc_codegen_cranelift/src/codegen_f16_f128.rs b/compiler/rustc_codegen_cranelift/src/codegen_f16_f128.rs
index 86bff32dc623c..d8977657e305d 100644
--- a/compiler/rustc_codegen_cranelift/src/codegen_f16_f128.rs
+++ b/compiler/rustc_codegen_cranelift/src/codegen_f16_f128.rs
@@ -208,7 +208,7 @@ pub(crate) fn codegen_cast(
         let ret_ty = if to_ty.bits() < 32 { types::I32 } else { to_ty };
         let name = format!(
             "__fix{sign}tf{size}i",
-            sign = if from_signed { "" } else { "un" },
+            sign = if to_signed { "" } else { "uns" },
             size = match ret_ty {
                 types::I32 => 's',
                 types::I64 => 'd',